From 054ed300f04abaaa0516b9a91b2455d0abf40971 Mon Sep 17 00:00:00 2001 From: AndrolGenhald Date: Fri, 28 Jan 2022 09:26:58 -0600 Subject: [PATCH 01/88] Fix side effect when loading config. --- src/Psalm/Config.php | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/Psalm/Config.php b/src/Psalm/Config.php index 2cd1c91b126..5748f15953e 100644 --- a/src/Psalm/Config.php +++ b/src/Psalm/Config.php @@ -744,6 +744,10 @@ private static function validateXmlConfig(string $base_dir, string $file_content throw new ConfigException('Cannot locate config schema'); } + // Enable user error handling + $prev_xml_internal_errors = libxml_use_internal_errors(true); + libxml_clear_errors(); + $dom_document = self::loadDomDocument($base_dir, $file_contents); $psalm_nodes = $dom_document->getElementsByTagName('psalm'); @@ -766,19 +770,17 @@ private static function validateXmlConfig(string $base_dir, string $file_content $dom_document = self::loadDomDocument($base_dir, $old_file_contents); } - // Enable user error handling - libxml_use_internal_errors(true); + $dom_document->schemaValidate($schema_path); // If it returns false it will generate errors handled below - if (!$dom_document->schemaValidate($schema_path)) { - $errors = libxml_get_errors(); - foreach ($errors as $error) { - if ($error->level === LIBXML_ERR_FATAL || $error->level === LIBXML_ERR_ERROR) { - throw new ConfigException( - 'Error on line ' . $error->line . ":\n" . ' ' . $error->message - ); - } + $errors = libxml_get_errors(); + libxml_clear_errors(); + libxml_use_internal_errors($prev_xml_internal_errors); + foreach ($errors as $error) { + if ($error->level === LIBXML_ERR_FATAL || $error->level === LIBXML_ERR_ERROR) { + throw new ConfigException( + 'Error on line ' . $error->line . ":\n" . ' ' . $error->message + ); } - libxml_clear_errors(); } } From c3ea97949d664795f17499662031c28cacfb1b08 Mon Sep 17 00:00:00 2001 From: Bruce Weirdan Date: Sun, 29 May 2022 00:24:11 -0400 Subject: [PATCH 02/88] Fix syntax check failures They were caused by installing packages with `--ignore-platform-reqs` which brought PHP 8.1 autoloaded packages and caused fatal errors when running php-parallel-lint. Instead of ignoring platform requirements we now remove packages that are incompatible with PHP 7.1 (phpunit and its dependents). --- .github/workflows/ci.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8ae030d4f46..b2d804008af 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -31,8 +31,11 @@ jobs: restore-keys: | ${{ runner.os }}-composer- + - name: Drop incompatible packages + run: composer remove --no-interaction --dev phpunit/phpunit brianium/paratest psalm/plugin-phpunit weirdan/prophecy-shim + - name: Run composer install - run: composer install -o --ignore-platform-reqs + run: composer install -o env: COMPOSER_ROOT_VERSION: dev-master From 8455d71a9b8259d2f5c1505c71350a18871a8557 Mon Sep 17 00:00:00 2001 From: Bruce Weirdan Date: Sun, 29 May 2022 00:44:18 -0400 Subject: [PATCH 03/88] Run Shepherd with 8.0 Because installing packages with 8.1 and checking them as if we're running 7.1 just doesn't work. --- .github/workflows/shepherd.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/shepherd.yml b/.github/workflows/shepherd.yml index 6536eab38fb..1ff14195871 100644 --- a/.github/workflows/shepherd.yml +++ b/.github/workflows/shepherd.yml @@ -8,6 +8,9 @@ jobs: steps: - uses: actions/checkout@v2 + - uses: shivammathur/setup-php@v2 + with: + php-version: '8.0' - name: Install dependencies run: composer install --prefer-dist --no-progress --no-suggest From 6352d9a8559bd06eadeb8678fb70332b82921dd5 Mon Sep 17 00:00:00 2001 From: Vincent Langlet Date: Sun, 29 May 2022 21:45:33 +0200 Subject: [PATCH 04/88] Precise stat return type --- dictionaries/CallMap.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/dictionaries/CallMap.php b/dictionaries/CallMap.php index 8ab02f5f06c..3c8dc7fae5f 100644 --- a/dictionaries/CallMap.php +++ b/dictionaries/CallMap.php @@ -3333,7 +3333,7 @@ 'fscanf\'1' => ['int', 'stream'=>'resource', 'format'=>'string', '&...w_vars='=>'string|int|float'], 'fseek' => ['int', 'stream'=>'resource', 'offset'=>'int', 'whence='=>'int'], 'fsockopen' => ['resource|false', 'hostname'=>'string', 'port='=>'int', '&w_error_code='=>'int', '&w_error_message='=>'string', 'timeout='=>'float'], -'fstat' => ['array|false', 'stream'=>'resource'], +'fstat' => ['array{0: int, 1: int, 2: int, 3: int, 4: int, 5: int, 6: int, 7: int, 8: int, 9: int, 10: int, 11: int, 12: int, dev: int, ino: int, mode: int, nlink: int, uid: int, gid: int, rdev: int, size: int, atime: int, mtime: int, ctime: int, blksize: int, blocks: int}|false', 'stream'=>'resource'], 'ftell' => ['int|false', 'stream'=>'resource'], 'ftok' => ['int', 'filename'=>'string', 'project_id'=>'string'], 'ftp_alloc' => ['bool', 'ftp'=>'FTP\Connection', 'size'=>'int', '&w_response='=>'string'], @@ -6959,7 +6959,7 @@ 'LogicException::getTrace' => ['list\',args?:array}>'], 'LogicException::getTraceAsString' => ['string'], 'long2ip' => ['string', 'ip'=>'string|int'], -'lstat' => ['array|false', 'filename'=>'string'], +'lstat' => ['array{0: int, 1: int, 2: int, 3: int, 4: int, 5: int, 6: int, 7: int, 8: int, 9: int, 10: int, 11: int, 12: int, dev: int, ino: int, mode: int, nlink: int, uid: int, gid: int, rdev: int, size: int, atime: int, mtime: int, ctime: int, blksize: int, blocks: int}|false', 'filename'=>'string'], 'ltrim' => ['string', 'string'=>'string', 'characters='=>'string'], 'Lua::__call' => ['mixed', 'lua_func'=>'callable', 'args='=>'array', 'use_self='=>'int'], 'Lua::__construct' => ['void', 'lua_script_file'=>'string'], @@ -13217,7 +13217,7 @@ 'SplFileObject::fread' => ['string|false', 'length'=>'int'], 'SplFileObject::fscanf' => ['array|int', 'format'=>'string', '&...w_vars='=>'string|int|float'], 'SplFileObject::fseek' => ['int', 'pos'=>'int', 'whence='=>'int'], -'SplFileObject::fstat' => ['array|false'], +'SplFileObject::fstat' => ['array{0: int, 1: int, 2: int, 3: int, 4: int, 5: int, 6: int, 7: int, 8: int, 9: int, 10: int, 11: int, 12: int, dev: int, ino: int, mode: int, nlink: int, uid: int, gid: int, rdev: int, size: int, atime: int, mtime: int, ctime: int, blksize: int, blocks: int}'], 'SplFileObject::ftell' => ['int|false'], 'SplFileObject::ftruncate' => ['bool', 'size'=>'int'], 'SplFileObject::fwrite' => ['int', 'string'=>'string', 'length='=>'int'], @@ -13410,7 +13410,7 @@ 'SplTempFileObject::fread' => ['false|string', 'length'=>'int'], 'SplTempFileObject::fscanf' => ['bool', 'format'=>'string', '&...w_vars='=>'array|array|array'], 'SplTempFileObject::fseek' => ['int', 'pos'=>'int', 'whence='=>'int'], -'SplTempFileObject::fstat' => ['array|false'], +'SplTempFileObject::fstat' => ['array{0: int, 1: int, 2: int, 3: int, 4: int, 5: int, 6: int, 7: int, 8: int, 9: int, 10: int, 11: int, 12: int, dev: int, ino: int, mode: int, nlink: int, uid: int, gid: int, rdev: int, size: int, atime: int, mtime: int, ctime: int, blksize: int, blocks: int}'], 'SplTempFileObject::ftell' => ['int'], 'SplTempFileObject::ftruncate' => ['bool', 'size'=>'int'], 'SplTempFileObject::fwrite' => ['int', 'string'=>'string', 'length='=>'int'], @@ -13647,7 +13647,7 @@ 'ssh2_scp_send' => ['bool', 'session'=>'resource', 'local_file'=>'string', 'remote_file'=>'string', 'create_mode='=>'int'], 'ssh2_sftp' => ['resource|false', 'session'=>'resource'], 'ssh2_sftp_chmod' => ['bool', 'sftp'=>'resource', 'filename'=>'string', 'mode'=>'int'], -'ssh2_sftp_lstat' => ['array|false', 'sftp'=>'resource', 'path'=>'string'], +'ssh2_sftp_lstat' => ['array{0: int, 1: int, 2: int, 3: int, 4: int, 5: int, 6: int, 7: int, 8: int, 9: int, 10: int, 11: int, 12: int, dev: int, ino: int, mode: int, nlink: int, uid: int, gid: int, rdev: int, size: int, atime: int, mtime: int, ctime: int, blksize: int, blocks: int}|false', 'sftp'=>'resource', 'path'=>'string'], 'ssh2_sftp_mkdir' => ['bool', 'sftp'=>'resource', 'dirname'=>'string', 'mode='=>'int', 'recursive='=>'bool'], 'ssh2_sftp_readlink' => ['string|false', 'sftp'=>'resource', 'link'=>'string'], 'ssh2_sftp_realpath' => ['string|false', 'sftp'=>'resource', 'filename'=>'string'], @@ -13658,7 +13658,7 @@ 'ssh2_sftp_unlink' => ['bool', 'sftp'=>'resource', 'filename'=>'string'], 'ssh2_shell' => ['resource|false', 'session'=>'resource', 'term_type='=>'string', 'env='=>'array', 'width='=>'int', 'height='=>'int', 'width_height_type='=>'int'], 'ssh2_tunnel' => ['resource|false', 'session'=>'resource', 'host'=>'string', 'port'=>'int'], -'stat' => ['array|false', 'filename'=>'string'], +'stat' => ['array{0: int, 1: int, 2: int, 3: int, 4: int, 5: int, 6: int, 7: int, 8: int, 9: int, 10: int, 11: int, 12: int, dev: int, ino: int, mode: int, nlink: int, uid: int, gid: int, rdev: int, size: int, atime: int, mtime: int, ctime: int, blksize: int, blocks: int}|false', 'filename'=>'string'], 'stats_absolute_deviation' => ['float', 'a'=>'array'], 'stats_cdf_beta' => ['float', 'par1'=>'float', 'par2'=>'float', 'par3'=>'float', 'which'=>'int'], 'stats_cdf_binomial' => ['float', 'par1'=>'float', 'par2'=>'float', 'par3'=>'float', 'which'=>'int'], From 087d7ce649641fc97a1db1e832723a53cd8a9d78 Mon Sep 17 00:00:00 2001 From: Vincent Langlet Date: Sun, 29 May 2022 22:16:41 +0200 Subject: [PATCH 05/88] Update historical --- dictionaries/CallMap.php | 2 +- dictionaries/CallMap_historical.php | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/dictionaries/CallMap.php b/dictionaries/CallMap.php index 3c8dc7fae5f..3a58984342c 100644 --- a/dictionaries/CallMap.php +++ b/dictionaries/CallMap.php @@ -13653,7 +13653,7 @@ 'ssh2_sftp_realpath' => ['string|false', 'sftp'=>'resource', 'filename'=>'string'], 'ssh2_sftp_rename' => ['bool', 'sftp'=>'resource', 'from'=>'string', 'to'=>'string'], 'ssh2_sftp_rmdir' => ['bool', 'sftp'=>'resource', 'dirname'=>'string'], -'ssh2_sftp_stat' => ['array|false', 'sftp'=>'resource', 'path'=>'string'], +'ssh2_sftp_stat' => ['array{0: int, 1: int, 2: int, 3: int, 4: int, 5: int, 6: int, 7: int, 8: int, 9: int, 10: int, 11: int, 12: int, dev: int, ino: int, mode: int, nlink: int, uid: int, gid: int, rdev: int, size: int, atime: int, mtime: int, ctime: int, blksize: int, blocks: int}|false', 'sftp'=>'resource', 'path'=>'string'], 'ssh2_sftp_symlink' => ['bool', 'sftp'=>'resource', 'target'=>'string', 'link'=>'string'], 'ssh2_sftp_unlink' => ['bool', 'sftp'=>'resource', 'filename'=>'string'], 'ssh2_shell' => ['resource|false', 'session'=>'resource', 'term_type='=>'string', 'env='=>'array', 'width='=>'int', 'height='=>'int', 'width_height_type='=>'int'], diff --git a/dictionaries/CallMap_historical.php b/dictionaries/CallMap_historical.php index 75b231feb91..a7f02213093 100644 --- a/dictionaries/CallMap_historical.php +++ b/dictionaries/CallMap_historical.php @@ -7715,7 +7715,7 @@ 'SplFileObject::fread' => ['string|false', 'length'=>'int'], 'SplFileObject::fscanf' => ['array|int', 'format'=>'string', '&...w_vars='=>'string|int|float'], 'SplFileObject::fseek' => ['int', 'pos'=>'int', 'whence='=>'int'], - 'SplFileObject::fstat' => ['array|false'], + 'SplFileObject::fstat' => ['array{0: int, 1: int, 2: int, 3: int, 4: int, 5: int, 6: int, 7: int, 8: int, 9: int, 10: int, 11: int, 12: int, dev: int, ino: int, mode: int, nlink: int, uid: int, gid: int, rdev: int, size: int, atime: int, mtime: int, ctime: int, blksize: int, blocks: int}'], 'SplFileObject::ftell' => ['int|false'], 'SplFileObject::ftruncate' => ['bool', 'size'=>'int'], 'SplFileObject::fwrite' => ['int', 'string'=>'string', 'length='=>'int'], @@ -7907,7 +7907,7 @@ 'SplTempFileObject::fread' => ['false|string', 'length'=>'int'], 'SplTempFileObject::fscanf' => ['bool', 'format'=>'string', '&...w_vars='=>'array|array|array'], 'SplTempFileObject::fseek' => ['int', 'pos'=>'int', 'whence='=>'int'], - 'SplTempFileObject::fstat' => ['array|false'], + 'SplTempFileObject::fstat' => ['array{0: int, 1: int, 2: int, 3: int, 4: int, 5: int, 6: int, 7: int, 8: int, 9: int, 10: int, 11: int, 12: int, dev: int, ino: int, mode: int, nlink: int, uid: int, gid: int, rdev: int, size: int, atime: int, mtime: int, ctime: int, blksize: int, blocks: int}'], 'SplTempFileObject::ftell' => ['int'], 'SplTempFileObject::ftruncate' => ['bool', 'size'=>'int'], 'SplTempFileObject::fwrite' => ['int', 'string'=>'string', 'length='=>'int'], @@ -10895,7 +10895,7 @@ 'fscanf\'1' => ['int', 'stream'=>'resource', 'format'=>'string', '&...w_vars='=>'string|int|float'], 'fseek' => ['int', 'stream'=>'resource', 'offset'=>'int', 'whence='=>'int'], 'fsockopen' => ['resource|false', 'hostname'=>'string', 'port='=>'int', '&w_error_code='=>'int', '&w_error_message='=>'string', 'timeout='=>'float'], - 'fstat' => ['array|false', 'stream'=>'resource'], + 'fstat' => ['array{0: int, 1: int, 2: int, 3: int, 4: int, 5: int, 6: int, 7: int, 8: int, 9: int, 10: int, 11: int, 12: int, dev: int, ino: int, mode: int, nlink: int, uid: int, gid: int, rdev: int, size: int, atime: int, mtime: int, ctime: int, blksize: int, blocks: int}|false', 'stream'=>'resource'], 'ftell' => ['int|false', 'stream'=>'resource'], 'ftok' => ['int', 'filename'=>'string', 'project_id'=>'string'], 'ftp_alloc' => ['bool', 'ftp'=>'resource', 'size'=>'int', '&w_response='=>'string'], @@ -12582,7 +12582,7 @@ 'log10' => ['float', 'num'=>'float'], 'log1p' => ['float', 'num'=>'float'], 'long2ip' => ['string', 'ip'=>'string|int'], - 'lstat' => ['array|false', 'filename'=>'string'], + 'lstat' => ['array{0: int, 1: int, 2: int, 3: int, 4: int, 5: int, 6: int, 7: int, 8: int, 9: int, 10: int, 11: int, 12: int, dev: int, ino: int, mode: int, nlink: int, uid: int, gid: int, rdev: int, size: int, atime: int, mtime: int, ctime: int, blksize: int, blocks: int}|false', 'filename'=>'string'], 'ltrim' => ['string', 'string'=>'string', 'characters='=>'string'], 'lzf_compress' => ['string', 'data'=>'string'], 'lzf_decompress' => ['string', 'data'=>'string'], @@ -15079,18 +15079,18 @@ 'ssh2_scp_send' => ['bool', 'session'=>'resource', 'local_file'=>'string', 'remote_file'=>'string', 'create_mode='=>'int'], 'ssh2_sftp' => ['resource|false', 'session'=>'resource'], 'ssh2_sftp_chmod' => ['bool', 'sftp'=>'resource', 'filename'=>'string', 'mode'=>'int'], - 'ssh2_sftp_lstat' => ['array|false', 'sftp'=>'resource', 'path'=>'string'], + 'ssh2_sftp_lstat' => ['array{0: int, 1: int, 2: int, 3: int, 4: int, 5: int, 6: int, 7: int, 8: int, 9: int, 10: int, 11: int, 12: int, dev: int, ino: int, mode: int, nlink: int, uid: int, gid: int, rdev: int, size: int, atime: int, mtime: int, ctime: int, blksize: int, blocks: int}|false', 'sftp'=>'resource', 'path'=>'string'], 'ssh2_sftp_mkdir' => ['bool', 'sftp'=>'resource', 'dirname'=>'string', 'mode='=>'int', 'recursive='=>'bool'], 'ssh2_sftp_readlink' => ['string|false', 'sftp'=>'resource', 'link'=>'string'], 'ssh2_sftp_realpath' => ['string|false', 'sftp'=>'resource', 'filename'=>'string'], 'ssh2_sftp_rename' => ['bool', 'sftp'=>'resource', 'from'=>'string', 'to'=>'string'], 'ssh2_sftp_rmdir' => ['bool', 'sftp'=>'resource', 'dirname'=>'string'], - 'ssh2_sftp_stat' => ['array|false', 'sftp'=>'resource', 'path'=>'string'], + 'ssh2_sftp_stat' => ['array{0: int, 1: int, 2: int, 3: int, 4: int, 5: int, 6: int, 7: int, 8: int, 9: int, 10: int, 11: int, 12: int, dev: int, ino: int, mode: int, nlink: int, uid: int, gid: int, rdev: int, size: int, atime: int, mtime: int, ctime: int, blksize: int, blocks: int}|false', 'sftp'=>'resource', 'path'=>'string'], 'ssh2_sftp_symlink' => ['bool', 'sftp'=>'resource', 'target'=>'string', 'link'=>'string'], 'ssh2_sftp_unlink' => ['bool', 'sftp'=>'resource', 'filename'=>'string'], 'ssh2_shell' => ['resource|false', 'session'=>'resource', 'term_type='=>'string', 'env='=>'array', 'width='=>'int', 'height='=>'int', 'width_height_type='=>'int'], 'ssh2_tunnel' => ['resource|false', 'session'=>'resource', 'host'=>'string', 'port'=>'int'], - 'stat' => ['array|false', 'filename'=>'string'], + 'stat' => ['array{0: int, 1: int, 2: int, 3: int, 4: int, 5: int, 6: int, 7: int, 8: int, 9: int, 10: int, 11: int, 12: int, dev: int, ino: int, mode: int, nlink: int, uid: int, gid: int, rdev: int, size: int, atime: int, mtime: int, ctime: int, blksize: int, blocks: int}|false', 'filename'=>'string'], 'stats_absolute_deviation' => ['float', 'a'=>'array'], 'stats_cdf_beta' => ['float', 'par1'=>'float', 'par2'=>'float', 'par3'=>'float', 'which'=>'int'], 'stats_cdf_binomial' => ['float', 'par1'=>'float', 'par2'=>'float', 'par3'=>'float', 'which'=>'int'], From 943131688d644f3855b06b056c07d0f536285500 Mon Sep 17 00:00:00 2001 From: Kevin van Sonsbeek Date: Thu, 2 Jun 2022 00:30:33 +0200 Subject: [PATCH 06/88] Updated the delta to accommodate the param name changes when going from 7.4 to 8.0 --- dictionaries/CallMap_80_delta.php | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/dictionaries/CallMap_80_delta.php b/dictionaries/CallMap_80_delta.php index b8637425e4f..59efb77fc7a 100644 --- a/dictionaries/CallMap_80_delta.php +++ b/dictionaries/CallMap_80_delta.php @@ -133,6 +133,22 @@ 'old' => ['bool', 'name'=>'string', 'content'=>'string', 'isParam'=>'bool', 'publicId'=>'string', 'systemId'=>'string', 'notationData'=>'string'], 'new' => ['bool', 'name'=>'string', 'content'=>'string', 'isParam='=>'bool', 'publicId='=>'?string', 'systemId='=>'?string', 'notationData='=>'?string'], ], + 'SplFileObject::fgetcsv' => [ + 'old' => ['list|array{0: null}|false|null', 'delimiter='=>'string', 'enclosure='=>'string', 'escape='=>'string'], + 'new' => ['list|array{0: null}|false|null', 'seperator='=>'string', 'enclosure='=>'string', 'escape='=>'string'], + ], + 'SplFileObject::fputcsv' => [ + 'old' => ['int|false', 'fields'=>'array', 'delimiter='=>'string', 'enclosure='=>'string', 'escape='=>'string'], + 'new' => ['int|false', 'fields'=>'array', 'seperator='=>'string', 'enclosure='=>'string', 'escape='=>'string'], + ], + 'SplTempFileObject::fgetcsv' => [ + 'old' => ['list|array{0: null}|false|null', 'delimiter='=>'string', 'enclosure='=>'string', 'escape='=>'string'], + 'new' => ['list|array{0: null}|false|null', 'seperator='=>'string', 'enclosure='=>'string', 'escape='=>'string'], + ], + 'SplTempFileObject::fputcsv' => [ + 'old' => ['int|false', 'fields'=>'array', 'delimiter='=>'string', 'enclosure='=>'string', 'escape='=>'string'], + 'new' => ['int|false', 'fields'=>'array', 'seperator='=>'string', 'enclosure='=>'string', 'escape='=>'string'], + ], 'array_combine' => [ 'old' => ['associative-array|false', 'keys'=>'string[]|int[]', 'values'=>'array'], 'new' => ['associative-array', 'keys'=>'string[]|int[]', 'values'=>'array'], From 1042ececaf082416586a9affcec50932fd32fde7 Mon Sep 17 00:00:00 2001 From: Kevin van Sonsbeek Date: Thu, 2 Jun 2022 00:30:58 +0200 Subject: [PATCH 07/88] Update the CallMap to have the correct param names --- dictionaries/CallMap.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dictionaries/CallMap.php b/dictionaries/CallMap.php index 50c8a3f1ddb..25db4f830b1 100644 --- a/dictionaries/CallMap.php +++ b/dictionaries/CallMap.php @@ -13207,11 +13207,11 @@ 'SplFileObject::eof' => ['bool'], 'SplFileObject::fflush' => ['bool'], 'SplFileObject::fgetc' => ['string|false'], -'SplFileObject::fgetcsv' => ['list|array{0: null}|false|null', 'delimiter='=>'string', 'enclosure='=>'string', 'escape='=>'string'], +'SplFileObject::fgetcsv' => ['list|array{0: null}|false|null', 'seperator='=>'string', 'enclosure='=>'string', 'escape='=>'string'], 'SplFileObject::fgets' => ['string|false'], 'SplFileObject::flock' => ['bool', 'operation'=>'int', '&w_wouldblock='=>'int'], 'SplFileObject::fpassthru' => ['int|false'], -'SplFileObject::fputcsv' => ['int|false', 'fields'=>'array', 'delimiter='=>'string', 'enclosure='=>'string', 'escape='=>'string'], +'SplFileObject::fputcsv' => ['int|false', 'fields'=>'array', 'seperator='=>'string', 'enclosure='=>'string', 'escape='=>'string'], 'SplFileObject::fread' => ['string|false', 'length'=>'int'], 'SplFileObject::fscanf' => ['array|int', 'format'=>'string', '&...w_vars='=>'string|int|float'], 'SplFileObject::fseek' => ['int', 'pos'=>'int', 'whence='=>'int'], @@ -13399,12 +13399,12 @@ 'SplTempFileObject::eof' => ['bool'], 'SplTempFileObject::fflush' => ['bool'], 'SplTempFileObject::fgetc' => ['false|string'], -'SplTempFileObject::fgetcsv' => ['list|array{0: null}|false|null', 'delimiter='=>'string', 'enclosure='=>'string', 'escape='=>'string'], +'SplTempFileObject::fgetcsv' => ['list|array{0: null}|false|null', 'seperator='=>'string', 'enclosure='=>'string', 'escape='=>'string'], 'SplTempFileObject::fgets' => ['string'], 'SplTempFileObject::fgetss' => ['string', 'allowable_tags='=>'string'], 'SplTempFileObject::flock' => ['bool', 'operation'=>'int', '&wouldblock='=>'int'], 'SplTempFileObject::fpassthru' => ['int|false'], -'SplTempFileObject::fputcsv' => ['false|int', 'fields'=>'array', 'delimiter='=>'string', 'enclosure='=>'string', 'escape='=>'string'], +'SplTempFileObject::fputcsv' => ['false|int', 'fields'=>'array', 'seperator='=>'string', 'enclosure='=>'string', 'escape='=>'string'], 'SplTempFileObject::fread' => ['false|string', 'length'=>'int'], 'SplTempFileObject::fscanf' => ['bool', 'format'=>'string', '&...w_vars='=>'array|array|array'], 'SplTempFileObject::fseek' => ['int', 'pos'=>'int', 'whence='=>'int'], From e402a3b8228f92d5b30171b2aa8db7842593285a Mon Sep 17 00:00:00 2001 From: Kevin van Sonsbeek Date: Thu, 2 Jun 2022 18:37:18 +0200 Subject: [PATCH 08/88] Remove changes from delta, and retroactively fix param name in the historical callmap --- dictionaries/CallMap_80_delta.php | 16 ---------------- dictionaries/CallMap_historical.php | 8 ++++---- 2 files changed, 4 insertions(+), 20 deletions(-) diff --git a/dictionaries/CallMap_80_delta.php b/dictionaries/CallMap_80_delta.php index 59efb77fc7a..b8637425e4f 100644 --- a/dictionaries/CallMap_80_delta.php +++ b/dictionaries/CallMap_80_delta.php @@ -133,22 +133,6 @@ 'old' => ['bool', 'name'=>'string', 'content'=>'string', 'isParam'=>'bool', 'publicId'=>'string', 'systemId'=>'string', 'notationData'=>'string'], 'new' => ['bool', 'name'=>'string', 'content'=>'string', 'isParam='=>'bool', 'publicId='=>'?string', 'systemId='=>'?string', 'notationData='=>'?string'], ], - 'SplFileObject::fgetcsv' => [ - 'old' => ['list|array{0: null}|false|null', 'delimiter='=>'string', 'enclosure='=>'string', 'escape='=>'string'], - 'new' => ['list|array{0: null}|false|null', 'seperator='=>'string', 'enclosure='=>'string', 'escape='=>'string'], - ], - 'SplFileObject::fputcsv' => [ - 'old' => ['int|false', 'fields'=>'array', 'delimiter='=>'string', 'enclosure='=>'string', 'escape='=>'string'], - 'new' => ['int|false', 'fields'=>'array', 'seperator='=>'string', 'enclosure='=>'string', 'escape='=>'string'], - ], - 'SplTempFileObject::fgetcsv' => [ - 'old' => ['list|array{0: null}|false|null', 'delimiter='=>'string', 'enclosure='=>'string', 'escape='=>'string'], - 'new' => ['list|array{0: null}|false|null', 'seperator='=>'string', 'enclosure='=>'string', 'escape='=>'string'], - ], - 'SplTempFileObject::fputcsv' => [ - 'old' => ['int|false', 'fields'=>'array', 'delimiter='=>'string', 'enclosure='=>'string', 'escape='=>'string'], - 'new' => ['int|false', 'fields'=>'array', 'seperator='=>'string', 'enclosure='=>'string', 'escape='=>'string'], - ], 'array_combine' => [ 'old' => ['associative-array|false', 'keys'=>'string[]|int[]', 'values'=>'array'], 'new' => ['associative-array', 'keys'=>'string[]|int[]', 'values'=>'array'], diff --git a/dictionaries/CallMap_historical.php b/dictionaries/CallMap_historical.php index 9c119453b87..49da9e197ae 100644 --- a/dictionaries/CallMap_historical.php +++ b/dictionaries/CallMap_historical.php @@ -7706,12 +7706,12 @@ 'SplFileObject::eof' => ['bool'], 'SplFileObject::fflush' => ['bool'], 'SplFileObject::fgetc' => ['string|false'], - 'SplFileObject::fgetcsv' => ['list|array{0: null}|false|null', 'delimiter='=>'string', 'enclosure='=>'string', 'escape='=>'string'], + 'SplFileObject::fgetcsv' => ['list|array{0: null}|false|null', 'seperator='=>'string', 'enclosure='=>'string', 'escape='=>'string'], 'SplFileObject::fgets' => ['string|false'], 'SplFileObject::fgetss' => ['string|false', 'allowable_tags='=>'string'], 'SplFileObject::flock' => ['bool', 'operation'=>'int', '&w_wouldblock='=>'int'], 'SplFileObject::fpassthru' => ['int|false'], - 'SplFileObject::fputcsv' => ['int|false', 'fields'=>'array', 'delimiter='=>'string', 'enclosure='=>'string', 'escape='=>'string'], + 'SplFileObject::fputcsv' => ['int|false', 'fields'=>'array', 'seperator='=>'string', 'enclosure='=>'string', 'escape='=>'string'], 'SplFileObject::fread' => ['string|false', 'length'=>'int'], 'SplFileObject::fscanf' => ['array|int', 'format'=>'string', '&...w_vars='=>'string|int|float'], 'SplFileObject::fseek' => ['int', 'pos'=>'int', 'whence='=>'int'], @@ -7898,12 +7898,12 @@ 'SplTempFileObject::eof' => ['bool'], 'SplTempFileObject::fflush' => ['bool'], 'SplTempFileObject::fgetc' => ['false|string'], - 'SplTempFileObject::fgetcsv' => ['list|array{0: null}|false|null', 'delimiter='=>'string', 'enclosure='=>'string', 'escape='=>'string'], + 'SplTempFileObject::fgetcsv' => ['list|array{0: null}|false|null', 'seperator='=>'string', 'enclosure='=>'string', 'escape='=>'string'], 'SplTempFileObject::fgets' => ['string'], 'SplTempFileObject::fgetss' => ['string', 'allowable_tags='=>'string'], 'SplTempFileObject::flock' => ['bool', 'operation'=>'int', '&wouldblock='=>'int'], 'SplTempFileObject::fpassthru' => ['int|false'], - 'SplTempFileObject::fputcsv' => ['false|int', 'fields'=>'array', 'delimiter='=>'string', 'enclosure='=>'string', 'escape='=>'string'], + 'SplTempFileObject::fputcsv' => ['false|int', 'fields'=>'array', 'seperator='=>'string', 'enclosure='=>'string', 'escape='=>'string'], 'SplTempFileObject::fread' => ['false|string', 'length'=>'int'], 'SplTempFileObject::fscanf' => ['bool', 'format'=>'string', '&...w_vars='=>'array|array|array'], 'SplTempFileObject::fseek' => ['int', 'pos'=>'int', 'whence='=>'int'], From 2f090e5722e0c5b5ce02d3ffbd3f8d50d0e50054 Mon Sep 17 00:00:00 2001 From: AndrolGenhald Date: Thu, 2 Jun 2022 12:24:26 -0500 Subject: [PATCH 09/88] Fix `TypeCombiner::combine` to not modify TIntRange arguments. This keeps coming up in obscure places, hopefully this fixes it once and for all. I would reeeaaally love to have an immutable type system at some point... --- src/Psalm/Internal/Type/TypeCombiner.php | 1 + tests/IntRangeTest.php | 12 ++++++++++++ 2 files changed, 13 insertions(+) diff --git a/src/Psalm/Internal/Type/TypeCombiner.php b/src/Psalm/Internal/Type/TypeCombiner.php index 9e7579c3d3e..ec939c1c63a 100644 --- a/src/Psalm/Internal/Type/TypeCombiner.php +++ b/src/Psalm/Internal/Type/TypeCombiner.php @@ -1201,6 +1201,7 @@ function ($int): bool { $combination->value_types['int'] = new TInt(); } } elseif ($type instanceof TIntRange) { + $type = clone $type; if ($combination->ints) { foreach ($combination->ints as $int) { if (!$type->contains($int->value)) { diff --git a/tests/IntRangeTest.php b/tests/IntRangeTest.php index d182607aa66..5aa37992772 100644 --- a/tests/IntRangeTest.php +++ b/tests/IntRangeTest.php @@ -2,14 +2,26 @@ namespace Psalm\Tests; +use Psalm\Internal\Type\TypeCombiner; use Psalm\Tests\Traits\InvalidCodeAnalysisTestTrait; use Psalm\Tests\Traits\ValidCodeAnalysisTestTrait; +use Psalm\Type\Atomic\TIntRange; +use Psalm\Type\Atomic\TLiteralInt; class IntRangeTest extends TestCase { use InvalidCodeAnalysisTestTrait; use ValidCodeAnalysisTestTrait; + public function testCombineIntRangeDoesntAffectOriginal(): void + { + $range = new TIntRange(5, 10); + TypeCombiner::combine([new TLiteralInt(1), new TLiteralInt(50), $range]); + + $this->assertEquals(5, $range->min_bound); + $this->assertEquals(10, $range->max_bound); + } + /** * @return iterable,error_levels?:string[]}> */ From c271b1245eb2fc1f231d75f8f019e7f2434f7b09 Mon Sep 17 00:00:00 2001 From: AndrolGenhald Date: Thu, 2 Jun 2022 13:59:00 -0500 Subject: [PATCH 10/88] Fix possibly empty array shape appearing non-empty (fixes #8048). --- .../ArrayMergeReturnTypeProvider.php | 9 +++++++++ src/Psalm/Type/Atomic/TKeyedArray.php | 6 ++++-- tests/TypeReconciliation/TypeAlgebraTest.php | 11 +++++++++++ 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayMergeReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayMergeReturnTypeProvider.php index c4002a35fa8..c148fdd4582 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayMergeReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayMergeReturnTypeProvider.php @@ -214,6 +214,15 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev $inner_value_type = null; if ($inner_key_types) { + /** + * @psalm-suppress InvalidScalarArgument + * $inner_key_types is incorrectly inferred to be array{ + * 0?: Psalm\Type\Atomic|Psalm\Type\Atomic\TInt, + * 1?: Psalm\Type\Atomic|Psalm\Type\Atomic\TInt, + * ... + * 11?: Psalm\Type\Atomic|Psalm\Type\Atomic\TInt, + * } + */ $inner_key_type = TypeCombiner::combine($inner_key_types, $codebase, true); } diff --git a/src/Psalm/Type/Atomic/TKeyedArray.php b/src/Psalm/Type/Atomic/TKeyedArray.php index 15b0de859c4..e3b7399e57e 100644 --- a/src/Psalm/Type/Atomic/TKeyedArray.php +++ b/src/Psalm/Type/Atomic/TKeyedArray.php @@ -408,13 +408,15 @@ public function getAssertionString(bool $exact = false): string return $this->getKey(); } - public function getList(): TNonEmptyList + public function getList(): TList { if (!$this->is_list) { throw new UnexpectedValueException('Object-like array must be a list for conversion'); } - return new TNonEmptyList($this->getGenericValueType()); + return $this->isNonEmpty() + ? new TNonEmptyList($this->getGenericValueType()) + : new TList($this->getGenericValueType()); } /** diff --git a/tests/TypeReconciliation/TypeAlgebraTest.php b/tests/TypeReconciliation/TypeAlgebraTest.php index 7ee1c97d5e7..428c5136144 100644 --- a/tests/TypeReconciliation/TypeAlgebraTest.php +++ b/tests/TypeReconciliation/TypeAlgebraTest.php @@ -1420,6 +1420,17 @@ function foo(?X $x): void { false, '8.1', ], + 'arrayShapeListCanBeEmpty' => [ + ' $_list */ + function foobar(array $_list): void {} + + $list = random_int(0, 1) ? [] : ["foobar"]; + + foobar($list); + ', + 'error_message' => 'InvalidArgument', + ], ]; } } From 8b2070806342c97cca0ca12c4f56b12714215a95 Mon Sep 17 00:00:00 2001 From: kkmuffme <11071985+kkmuffme@users.noreply.github.com> Date: Sat, 4 Jun 2022 14:26:27 +0200 Subject: [PATCH 11/88] stores origin location by ID to speed up psalm by up to 75% in certain cases --- src/Psalm/Internal/Codebase/VariableUseGraph.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/Psalm/Internal/Codebase/VariableUseGraph.php b/src/Psalm/Internal/Codebase/VariableUseGraph.php index 4698f3e2dbc..f0bca358002 100644 --- a/src/Psalm/Internal/Codebase/VariableUseGraph.php +++ b/src/Psalm/Internal/Codebase/VariableUseGraph.php @@ -18,6 +18,9 @@ class VariableUseGraph extends DataFlowGraph /** @var array */ private $nodes = []; + /** @var array> */ + private $origin_locations_by_id = []; + public function addNode(DataFlowNode $node): void { $this->nodes[$node->id] = $node; @@ -94,6 +97,10 @@ public function isVariableUsed(DataFlowNode $assignment_node): bool */ public function getOriginLocations(DataFlowNode $assignment_node): array { + if (isset($this->origin_locations_by_id[$assignment_node->id])) { + return $this->origin_locations_by_id[$assignment_node->id]; + } + $visited_child_ids = []; $origin_locations = []; @@ -128,6 +135,8 @@ public function getOriginLocations(DataFlowNode $assignment_node): array $child_nodes = $new_parent_nodes; } + $this->origin_locations_by_id[$assignment_node->id] = $origin_locations; + return $origin_locations; } From cb78f54d8a162bf555ee67ca8a8de7ceeec3c4f3 Mon Sep 17 00:00:00 2001 From: kkmuffme <11071985+kkmuffme@users.noreply.github.com> Date: Sat, 4 Jun 2022 15:47:43 +0200 Subject: [PATCH 12/88] Performance: cut the selected_text from snippet instead of from full text 50% faster than cutting from full text, improves performance up to 3% depending on file length and number of errors in file --- src/Psalm/CodeLocation.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Psalm/CodeLocation.php b/src/Psalm/CodeLocation.php index 2b3bf2aabac..82c6bdad744 100644 --- a/src/Psalm/CodeLocation.php +++ b/src/Psalm/CodeLocation.php @@ -296,7 +296,9 @@ private function calculateRealLocation(): void } $this->snippet = mb_strcut($file_contents, $this->preview_start, $this->preview_end - $this->preview_start); - $this->text = mb_strcut($file_contents, $this->selection_start, $this->selection_end - $this->selection_start); + // text is within snippet. It's 50% faster to cut it from the snippet than from the full text + $selection_length = $this->selection_end - $this->selection_start; + $this->text = mb_strcut($this->snippet, $this->selection_start - $this->preview_start, $selection_length); // reset preview start to beginning of line if ($file_contents !== '') { From 74671e3a3c08eacc2dc6df90cb627dbf3ad7a750 Mon Sep 17 00:00:00 2001 From: kkmuffme <11071985+kkmuffme@users.noreply.github.com> Date: Sun, 5 Jun 2022 12:03:57 +0200 Subject: [PATCH 13/88] fix false positives for partially incalid phpdoc --- .../Reflector/FunctionLikeDocblockParser.php | 46 +++++++++++++++---- .../Reflector/FunctionLikeNodeScanner.php | 10 +++- 2 files changed, 46 insertions(+), 10 deletions(-) diff --git a/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeDocblockParser.php b/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeDocblockParser.php index f703d7da82d..bb11e8b99a4 100644 --- a/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeDocblockParser.php +++ b/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeDocblockParser.php @@ -3,12 +3,15 @@ namespace Psalm\Internal\PhpVisitor\Reflector; use PhpParser; +use Psalm\CodeLocation; use Psalm\DocComment; use Psalm\Exception\DocblockParseException; use Psalm\Exception\IncorrectDocblockException; use Psalm\Internal\Analyzer\CommentAnalyzer; use Psalm\Internal\Scanner\FunctionDocblockComment; use Psalm\Internal\Scanner\ParsedDocblock; +use Psalm\Issue\InvalidDocblock; +use Psalm\IssueBuffer; use function array_shift; use function array_unique; @@ -34,8 +37,11 @@ class FunctionLikeDocblockParser /** * @throws DocblockParseException if there was a problem parsing the docblock */ - public static function parse(PhpParser\Comment\Doc $comment): FunctionDocblockComment - { + public static function parse( + PhpParser\Comment\Doc $comment, + CodeLocation $code_location, + string $cased_function_id + ): FunctionDocblockComment { $parsed_docblock = DocComment::parsePreservingLength($comment); $comment_text = $comment->getText(); @@ -49,7 +55,9 @@ public static function parse(PhpParser\Comment\Doc $comment): FunctionDocblockCo self::extractReturnType( $comment, $parsed_docblock->combined_tags['return'], - $info + $info, + $code_location, + $cased_function_id ); } @@ -107,7 +115,12 @@ public static function parse(PhpParser\Comment\Doc $comment): FunctionDocblockCo $info->params[] = $info_param; } } else { - throw new DocblockParseException('Badly-formatted @param'); + IssueBuffer::maybeAdd( + new InvalidDocblock( + 'Badly-formatted @param in docblock for ' . $cased_function_id, + $code_location + ) + ); } } } @@ -152,7 +165,12 @@ public static function parse(PhpParser\Comment\Doc $comment): FunctionDocblockCo ]; } } else { - throw new DocblockParseException('Badly-formatted @param'); + IssueBuffer::maybeAdd( + new InvalidDocblock( + 'Badly-formatted @param in docblock for ' . $cased_function_id, + $code_location + ) + ); } } } @@ -335,7 +353,12 @@ public static function parse(PhpParser\Comment\Doc $comment): FunctionDocblockCo ]; } } else { - throw new DocblockParseException('Badly-formatted @param'); + IssueBuffer::maybeAdd( + new InvalidDocblock( + 'Badly-formatted @param in docblock for ' . $cased_function_id, + $code_location + ) + ); } } } @@ -542,7 +565,9 @@ private static function sanitizeAssertionLineParts(array $line_parts): array private static function extractReturnType( PhpParser\Comment\Doc $comment, array $return_specials, - FunctionDocblockComment $info + FunctionDocblockComment $info, + CodeLocation $code_location, + string $cased_function_id ): void { foreach ($return_specials as $offset => $return_block) { $return_lines = explode("\n", $return_block); @@ -581,7 +606,12 @@ private static function extractReturnType( $info->return_type_start = $offset; $info->return_type_end = $end; } else { - throw new DocblockParseException('Badly-formatted @return type'); + IssueBuffer::maybeAdd( + new InvalidDocblock( + 'Badly-formatted @param in docblock for ' . $cased_function_id, + $code_location + ) + ); } break; diff --git a/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeNodeScanner.php b/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeNodeScanner.php index 821c04127c9..bb654dc77b8 100644 --- a/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeNodeScanner.php +++ b/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeNodeScanner.php @@ -467,7 +467,8 @@ public function start(PhpParser\Node\FunctionLike $stmt, bool $fake_method = fal if ($doc_comment) { try { - $docblock_info = FunctionLikeDocblockParser::parse($doc_comment); + $code_location = new CodeLocation($this->file_scanner, $stmt, null, true); + $docblock_info = FunctionLikeDocblockParser::parse($doc_comment, $code_location, $cased_function_id); } catch (IncorrectDocblockException $e) { $storage->docblock_issues[] = new MissingDocblockType( $e->getMessage() . ' in docblock for ' . $cased_function_id, @@ -1041,7 +1042,12 @@ private function createStorageForFunctionLike( if ($doc_comment) { $docblock_info = null; try { - $docblock_info = FunctionLikeDocblockParser::parse($doc_comment); + $code_location = new CodeLocation($this->file_scanner, $stmt, null, true); + $docblock_info = FunctionLikeDocblockParser::parse( + $doc_comment, + $code_location, + $cased_function_id + ); } catch (IncorrectDocblockException|DocblockParseException $e) { } if ($docblock_info) { From 99372a22860eb533d1cd7f44ab211d8e76837c3b Mon Sep 17 00:00:00 2001 From: Barney Laurance Date: Mon, 6 Jun 2022 16:13:30 +0100 Subject: [PATCH 14/88] Clarify in docs that zero is not considered a positive-int I wasn't sure from reading the docs whether or not Psalm consider zero to be positive. --- docs/annotating_code/type_syntax/scalar_types.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/annotating_code/type_syntax/scalar_types.md b/docs/annotating_code/type_syntax/scalar_types.md index ff99acc7e2a..381678211b7 100644 --- a/docs/annotating_code/type_syntax/scalar_types.md +++ b/docs/annotating_code/type_syntax/scalar_types.md @@ -12,7 +12,7 @@ The type `scalar` is the supertype of all scalar types. ### positive-int -`positive-int` allows only positive integers +`positive-int` allows only integers greater than zero, i.e. between 1 and `PHP_INT_MAX`. ### numeric From a7c92661fa8b14346d00b7a213f27292c9778ae4 Mon Sep 17 00:00:00 2001 From: Barney Laurance Date: Mon, 6 Jun 2022 17:05:59 +0100 Subject: [PATCH 15/88] Use int<1,max> to define positivity --- docs/annotating_code/type_syntax/scalar_types.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/annotating_code/type_syntax/scalar_types.md b/docs/annotating_code/type_syntax/scalar_types.md index 381678211b7..df05fda000b 100644 --- a/docs/annotating_code/type_syntax/scalar_types.md +++ b/docs/annotating_code/type_syntax/scalar_types.md @@ -12,7 +12,7 @@ The type `scalar` is the supertype of all scalar types. ### positive-int -`positive-int` allows only integers greater than zero, i.e. between 1 and `PHP_INT_MAX`. +`positive-int` allows only positive integers (equivalent to int<1, max>) ### numeric From c064edcbb74801ba925368ceee482f9757f9b540 Mon Sep 17 00:00:00 2001 From: Barney Laurance Date: Mon, 6 Jun 2022 18:26:35 +0100 Subject: [PATCH 16/88] use backticks in docs --- docs/annotating_code/type_syntax/scalar_types.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/annotating_code/type_syntax/scalar_types.md b/docs/annotating_code/type_syntax/scalar_types.md index df05fda000b..e0f058e3521 100644 --- a/docs/annotating_code/type_syntax/scalar_types.md +++ b/docs/annotating_code/type_syntax/scalar_types.md @@ -12,7 +12,7 @@ The type `scalar` is the supertype of all scalar types. ### positive-int -`positive-int` allows only positive integers (equivalent to int<1, max>) +`positive-int` allows only positive integers (equivalent to `int<1, max>`) ### numeric From 34322b79dd04b33edeacdafa8e93fa935adf9e53 Mon Sep 17 00:00:00 2001 From: AndrolGenhald Date: Mon, 6 Jun 2022 16:18:54 -0500 Subject: [PATCH 17/88] Fix suppression comment. --- .../ReturnTypeProvider/ArrayMergeReturnTypeProvider.php | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayMergeReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayMergeReturnTypeProvider.php index c148fdd4582..6102ba47b2c 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayMergeReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayMergeReturnTypeProvider.php @@ -215,13 +215,8 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev if ($inner_key_types) { /** + * Truthy&array-shape-list doesn't reconcile correctly, will be fixed for 5.x by #8050. * @psalm-suppress InvalidScalarArgument - * $inner_key_types is incorrectly inferred to be array{ - * 0?: Psalm\Type\Atomic|Psalm\Type\Atomic\TInt, - * 1?: Psalm\Type\Atomic|Psalm\Type\Atomic\TInt, - * ... - * 11?: Psalm\Type\Atomic|Psalm\Type\Atomic\TInt, - * } */ $inner_key_type = TypeCombiner::combine($inner_key_types, $codebase, true); } From 4e59398f7705b77d48aa883da1093029ef004315 Mon Sep 17 00:00:00 2001 From: Bruce Weirdan Date: Mon, 6 Jun 2022 17:41:42 -0400 Subject: [PATCH 18/88] Coerce null to empty string in array keys Fixes vimeo/psalm#8063 Alters the fix for vimeo/psalm#2165 (1a48be8e9c62297c8a03f229069ef7777997ca06) /cc: @iluuu1994, @muglug --- .../Expression/Fetch/ArrayFetchAnalyzer.php | 4 ++-- tests/ArrayAssignmentTest.php | 14 +++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/ArrayFetchAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/ArrayFetchAnalyzer.php index e2feccaa649..6dd44638467 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/ArrayFetchAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/ArrayFetchAnalyzer.php @@ -508,7 +508,7 @@ public static function getArrayAccessTypeGivenOffset( if ($in_assignment) { $offset_type->removeType('null'); - $offset_type->addType(new TLiteralInt(0)); + $offset_type->addType(new TLiteralString('')); } } @@ -528,7 +528,7 @@ public static function getArrayAccessTypeGivenOffset( $offset_type->removeType('null'); if (!$offset_type->ignore_nullable_issues) { - $offset_type->addType(new TLiteralInt(0)); + $offset_type->addType(new TLiteralString('')); } } } diff --git a/tests/ArrayAssignmentTest.php b/tests/ArrayAssignmentTest.php index f77cb61be28..da697c3284a 100644 --- a/tests/ArrayAssignmentTest.php +++ b/tests/ArrayAssignmentTest.php @@ -1074,26 +1074,26 @@ function bar(array $value): void { if (is_int($value["a"])) {} }' ], - 'coercePossiblyNullKeyToZero' => [ + 'coercePossiblyNullKeyToEmptyString' => [ ' + * @return array */ function foo(): array { $array = []; /** @psalm-suppress PossiblyNullArrayOffset */ - $array[int_or_null()] = null; + $array[string_or_null()] = null; return $array; }' ], - 'coerceNullKeyToZero' => [ + 'coerceNullKeyToEmptyString' => [ ' + * @return array */ function foo(): array { $array = []; From af5c191e7b446fb3f7f45a9d8f8c1df4e0ce9099 Mon Sep 17 00:00:00 2001 From: AndrolGenhald Date: Tue, 7 Jun 2022 19:08:55 -0500 Subject: [PATCH 19/88] Fix generic object comparison to use template constraint as default (fixes #8068). --- .../Fetch/AtomicPropertyFetchAnalyzer.php | 32 ++++----------- .../Type/Comparator/GenericTypeComparator.php | 40 +++---------------- .../Type/TemplateStandinTypeReplacer.php | 20 +++++++++- src/Psalm/Storage/ClassLikeStorage.php | 18 +++++++++ tests/Template/ClassTemplateExtendsTest.php | 2 +- .../FunctionClassStringTemplateTest.php | 27 +++++++++++++ 6 files changed, 76 insertions(+), 63 deletions(-) diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/AtomicPropertyFetchAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/AtomicPropertyFetchAnalyzer.php index 0011896ea2e..afa84a3d7e4 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/AtomicPropertyFetchAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/AtomicPropertyFetchAnalyzer.php @@ -63,7 +63,7 @@ use function array_keys; use function array_search; -use function array_values; +use function count; use function in_array; use function is_int; use function is_string; @@ -570,15 +570,9 @@ private static function propertyFetchCanBeAnalyzed( $class_storage->parent_class ); - if ($class_storage->template_types) { + if (count($template_types = $class_storage->getClassTemplateTypes()) !== 0) { if (!$lhs_type_part instanceof TGenericObject) { - $type_params = []; - - foreach ($class_storage->template_types as $type_map) { - $type_params[] = clone array_values($type_map)[0]; - } - - $lhs_type_part = new TGenericObject($lhs_type_part->value, $type_params); + $lhs_type_part = new TGenericObject($lhs_type_part->value, $template_types); } $stmt_type = self::localizePropertyType( @@ -1100,15 +1094,9 @@ private static function handleNonExistentProperty( ) { $stmt_type = clone $class_storage->pseudo_property_get_types['$' . $prop_name]; - if ($class_storage->template_types) { + if (count($template_types = $class_storage->getClassTemplateTypes()) !== 0) { if (!$lhs_type_part instanceof TGenericObject) { - $type_params = []; - - foreach ($class_storage->template_types as $type_map) { - $type_params[] = clone array_values($type_map)[0]; - } - - $lhs_type_part = new TGenericObject($lhs_type_part->value, $type_params); + $lhs_type_part = new TGenericObject($lhs_type_part->value, $template_types); } $stmt_type = self::localizePropertyType( @@ -1200,15 +1188,9 @@ private static function getClassPropertyType( $declaring_class_storage->parent_class ); - if ($declaring_class_storage->template_types) { + if (count($template_types = $declaring_class_storage->getClassTemplateTypes()) !== 0) { if (!$lhs_type_part instanceof TGenericObject) { - $type_params = []; - - foreach ($declaring_class_storage->template_types as $type_map) { - $type_params[] = clone array_values($type_map)[0]; - } - - $lhs_type_part = new TGenericObject($lhs_type_part->value, $type_params); + $lhs_type_part = new TGenericObject($lhs_type_part->value, $template_types); } $class_property_type = self::localizePropertyType( diff --git a/src/Psalm/Internal/Type/Comparator/GenericTypeComparator.php b/src/Psalm/Internal/Type/Comparator/GenericTypeComparator.php index dd7e648c3fd..8cd529c66da 100644 --- a/src/Psalm/Internal/Type/Comparator/GenericTypeComparator.php +++ b/src/Psalm/Internal/Type/Comparator/GenericTypeComparator.php @@ -4,16 +4,11 @@ use Psalm\Codebase; use Psalm\Internal\Type\TemplateStandinTypeReplacer; -use Psalm\Type; use Psalm\Type\Atomic; use Psalm\Type\Atomic\TGenericObject; use Psalm\Type\Atomic\TIterable; use Psalm\Type\Atomic\TNamedObject; -use function array_fill; -use function array_values; -use function count; - /** * @internal */ @@ -44,38 +39,13 @@ public static function isContainedBy( $container_was_iterable = true; } - if (!$input_type_part instanceof TGenericObject && !$input_type_part instanceof TIterable) { - if ($input_type_part instanceof TNamedObject - && $codebase->classExists($input_type_part->value) - ) { - $class_storage = $codebase->classlike_storage_provider->get($input_type_part->value); - - $container_class = $container_type_part->value; - - // attempt to transform it - if (!empty($class_storage->template_extended_params[$container_class])) { - $input_type_part = new TGenericObject( - $input_type_part->value, - array_values($class_storage->template_extended_params[$container_class]) - ); - } + if (!$input_type_part instanceof TNamedObject && !$input_type_part instanceof TIterable) { + if ($atomic_comparison_result) { + $atomic_comparison_result->type_coerced = true; + $atomic_comparison_result->type_coerced_from_mixed = true; } - if (!$input_type_part instanceof TGenericObject) { - if ($input_type_part instanceof TNamedObject) { - $input_type_part = new TGenericObject( - $input_type_part->value, - array_fill(0, count($container_type_part->type_params), Type::getMixed()) - ); - } else { - if ($atomic_comparison_result) { - $atomic_comparison_result->type_coerced = true; - $atomic_comparison_result->type_coerced_from_mixed = true; - } - - return false; - } - } + return false; } $container_type_params_covariant = []; diff --git a/src/Psalm/Internal/Type/TemplateStandinTypeReplacer.php b/src/Psalm/Internal/Type/TemplateStandinTypeReplacer.php index 3791dc03e4a..7d6dbf6cee3 100644 --- a/src/Psalm/Internal/Type/TemplateStandinTypeReplacer.php +++ b/src/Psalm/Internal/Type/TemplateStandinTypeReplacer.php @@ -33,6 +33,7 @@ use Psalm\Type\Union; use Throwable; +use function array_fill; use function array_keys; use function array_merge; use function array_search; @@ -42,6 +43,7 @@ use function in_array; use function reset; use function strpos; +use function strtolower; use function substr; use function usort; @@ -1124,7 +1126,7 @@ function (TemplateBound $bound_a, TemplateBound $bound_b) { } /** - * @param TGenericObject|TIterable $input_type_part + * @param TGenericObject|TNamedObject|TIterable $input_type_part * @param TGenericObject|TIterable $container_type_part * @return list */ @@ -1134,7 +1136,21 @@ public static function getMappedGenericTypeParams( Atomic $container_type_part, ?array &$container_type_params_covariant = null ): array { - $input_type_params = $input_type_part->type_params; + if ($input_type_part instanceof TGenericObject || $input_type_part instanceof TIterable) { + $input_type_params = $input_type_part->type_params; + } else { + $class_storage = $codebase->classlike_storage_provider->get($input_type_part->value); + + $container_class = $container_type_part->value; + + if (strtolower($input_type_part->value) === strtolower($container_type_part->value)) { + $input_type_params = $class_storage->getClassTemplateTypes(); + } elseif (!empty($class_storage->template_extended_params[$container_class])) { + $input_type_params = array_values($class_storage->template_extended_params[$container_class]); + } else { + $input_type_params = array_fill(0, count($class_storage->template_types ?? []), Type::getMixed()); + } + } try { $input_class_storage = $codebase->classlike_storage_provider->get($input_type_part->value); diff --git a/src/Psalm/Storage/ClassLikeStorage.php b/src/Psalm/Storage/ClassLikeStorage.php index 1b33c69acb2..01b36968975 100644 --- a/src/Psalm/Storage/ClassLikeStorage.php +++ b/src/Psalm/Storage/ClassLikeStorage.php @@ -11,6 +11,8 @@ use Psalm\Type\Atomic\TTemplateParam; use Psalm\Type\Union; +use function array_values; + class ClassLikeStorage implements HasAttributesInterface { use CustomMetadataTrait; @@ -470,4 +472,20 @@ public function getAttributeStorages(): array { return $this->attributes; } + + /** + * Get the template constraint types for the class. + * + * @return list + */ + public function getClassTemplateTypes(): array + { + $type_params = []; + + foreach ($this->template_types ?? [] as $type_map) { + $type_params[] = clone array_values($type_map)[0]; + } + + return $type_params; + } } diff --git a/tests/Template/ClassTemplateExtendsTest.php b/tests/Template/ClassTemplateExtendsTest.php index 8fcba0c578b..c8c0fa4e532 100644 --- a/tests/Template/ClassTemplateExtendsTest.php +++ b/tests/Template/ClassTemplateExtendsTest.php @@ -5342,7 +5342,7 @@ public function __construct(DoStuff $stuff) {} } new Foo(new DoStuffX());', - 'error_message' => 'MixedArgumentTypeCoercion' + 'error_message' => 'TooManyTemplateParams' ], 'concreteDefinesSignatureTypesDifferent' => [ ' [ + 'code' => ' $_fooClass */ + function bar(string $_fooClass): void {} + + bar(Foo::class); + ', + ], + 'classStringWithGenericChildSatisfiesGenericParentWithDifferentConstraint' => [ + 'code' => ' + */ + class Bar extends Foo {} + + /** @param class-string $_fooClass */ + function bar(string $_fooClass): void {} + + bar(Bar::class); + ', + ], ]; } From 271dce8b6ce48b45001b1bc1385a731f6ba15800 Mon Sep 17 00:00:00 2001 From: AndrolGenhald Date: Tue, 7 Jun 2022 19:16:53 -0500 Subject: [PATCH 20/88] Fix test format after cherry-pick from master. --- tests/Template/FunctionClassStringTemplateTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Template/FunctionClassStringTemplateTest.php b/tests/Template/FunctionClassStringTemplateTest.php index dcbff55f85a..57df39694a4 100644 --- a/tests/Template/FunctionClassStringTemplateTest.php +++ b/tests/Template/FunctionClassStringTemplateTest.php @@ -701,7 +701,7 @@ function (Bar $bar) use ($class): bool { interface Bar {}' ], 'classStringSatisfiesTemplateWithConstraint' => [ - 'code' => ' [ - 'code' => ' Date: Mon, 13 Jun 2022 12:44:43 +0200 Subject: [PATCH 21/88] fix #8099: ob_implicit_flush argument type changed to bool --- dictionaries/CallMap.php | 2 +- dictionaries/CallMap_80_delta.php | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/dictionaries/CallMap.php b/dictionaries/CallMap.php index f8f3a994ca8..fc7fff7ea65 100644 --- a/dictionaries/CallMap.php +++ b/dictionaries/CallMap.php @@ -9120,7 +9120,7 @@ 'ob_get_status' => ['array', 'full_status='=>'bool'], 'ob_gzhandler' => ['string|false', 'data'=>'string', 'flags'=>'int'], 'ob_iconv_handler' => ['string', 'contents'=>'string', 'status'=>'int'], -'ob_implicit_flush' => ['void', 'enable='=>'int'], +'ob_implicit_flush' => ['void', 'enable='=>'bool'], 'ob_inflatehandler' => ['string', 'data'=>'string', 'mode'=>'int'], 'ob_list_handlers' => ['false|list'], 'ob_start' => ['bool', 'callback='=>'string|array|?callable', 'chunk_size='=>'int', 'flags='=>'int'], diff --git a/dictionaries/CallMap_80_delta.php b/dictionaries/CallMap_80_delta.php index b8637425e4f..f905592effe 100644 --- a/dictionaries/CallMap_80_delta.php +++ b/dictionaries/CallMap_80_delta.php @@ -969,6 +969,10 @@ 'old' => ['string', 'num'=>'float|int', 'decimals='=>'int'], 'new' => ['string', 'num'=>'float|int', 'decimals='=>'int', 'decimal_separator='=>'string', 'thousands_separator='=>'string'], ], + 'ob_implicit_flush' => [ + 'old' => ['void', 'enable='=>'int'], + 'new' => ['void', 'enable='=>'bool'], + ], 'openssl_csr_export' => [ 'old' => ['bool', 'csr'=>'string|resource', '&w_output'=>'string', 'no_text='=>'bool'], 'new' => ['bool', 'csr'=>'OpenSSLCertificateSigningRequest|string', '&w_output'=>'string', 'no_text='=>'bool'], From c7e39e76fc047d1a07c9689b5ec801693b9eed97 Mon Sep 17 00:00:00 2001 From: Sam Mousa Date: Mon, 13 Jun 2022 15:10:23 +0200 Subject: [PATCH 22/88] tests: add test that validates the callmap in the current runtime --- .../Codebase/InternalCallMapHandlerTest.php | 172 ++++++++++++++++++ 1 file changed, 172 insertions(+) diff --git a/tests/Internal/Codebase/InternalCallMapHandlerTest.php b/tests/Internal/Codebase/InternalCallMapHandlerTest.php index 2c6d98ca691..5d4ac4d5d08 100644 --- a/tests/Internal/Codebase/InternalCallMapHandlerTest.php +++ b/tests/Internal/Codebase/InternalCallMapHandlerTest.php @@ -2,11 +2,36 @@ namespace Psalm\Tests\Internal\Codebase; +use phpDocumentor\Reflection\DocBlock\Tags\Var_; +use PHPUnit\Framework\SkippedTestError; +use PHPUnit\Framework\SyntheticSkippedError; +use PHPUnit\Framework\Exception; +use PHPUnit\Framework\ExpectationFailedException; +use Psalm\Internal\Analyzer\ProjectAnalyzer; use Psalm\Internal\Codebase\InternalCallMapHandler; +use Psalm\Internal\Provider\FakeFileProvider; +use Psalm\Internal\Provider\Providers; +use Psalm\Tests\Internal\Provider\FakeParserCacheProvider; use Psalm\Tests\TestCase; +use Psalm\Tests\TestConfig; +use ReflectionFunction; +use ReflectionNamedType; +use ReflectionParameter; +use SebastianBergmann\RecursionContext\InvalidArgumentException; class InternalCallMapHandlerTest extends TestCase { + /** + * Ideally these should all be false, we have them here to reduce noise while we improve the tests or the callmap. + * @var bool whether to skip functions that are not currently defined in the PHP environment + */ + private $skipUndefinedFunctions = true; + /** + * + * @var bool whether to skip params for which no definition can be found in the callMap + */ + private $skipUndefinedParams = true; + /** * @covers \Psalm\Internal\Codebase\InternalCallMapHandler::getCallMap */ @@ -23,4 +48,151 @@ public function testGetcallmapReturnsAValidCallmap(): void } } } + + public function callMapEntryProvider(): iterable + { + + + $project_analyzer = new ProjectAnalyzer( + new TestConfig(), + new Providers( + new FakeFileProvider(), + new FakeParserCacheProvider() + ) + ); + $callMap = InternalCallMapHandler::getCallMap(); + unset($project_analyzer); + + foreach($callMap as $function => $entry) { + // Skip class methods + if (strpos($function, '::') !== false) { + continue; + } + // Skip alternate signatures + if (preg_match("/\'\d$/", $function)) { + continue; + } + if ($this->skipUndefinedFunctions && !function_exists($function)) { + continue; + } + yield "$function: " . json_encode($entry) => [$function, $entry]; + } + + + } + + /** + * This function will test functions that are in the callmap AND currently defined + * @return void + * @coversNothing + * @depends testGetcallmapReturnsAValidCallmap + * @dataProvider callMapEntryProvider + */ + public function testCallMapCompliesWithReflection(string $functionName, array $callMapEntry): void + { + if (!function_exists($functionName)) { + $this->markTestSkipped("Function $functionName does not exist"); + } + $this->assertEntryIsCorrect($callMapEntry, $functionName); + + } + + private function assertEntryIsCorrect(array $callMapEntry, string $functionName): void + { + $rF = new ReflectionFunction($functionName); + + // For now, ignore return types. + unset($callMapEntry[0]); + + /** + * Parse the parameter names from the map. + * @var array $entry) { + $normalizedKey = $key; + $normalizedEntry = [ + 'variadic' => false, + 'byRef' => false, + 'optional' => false, + 'type' => $entry, + 'name' => $key + ]; + // Strip prefixes. + if (strncmp($normalizedKey, '&rw_', 4) === 0) { + $normalizedEntry['byRef'] = true; + $normalizedEntry['refMode'] = 'rw'; + $normalizedKey = substr($normalizedKey, 4); + } elseif (strncmp($normalizedKey, '&w_', 3) === 0) { + $normalizedEntry['byRef'] = true; + $normalizedEntry['refMode'] = 'w'; + $normalizedKey = substr($normalizedKey, 3); + } + if (strncmp($normalizedKey, '...', 3) === 0) { + $normalizedEntry['variadic'] = true; + $normalizedKey = substr($normalizedKey, 3); + } + if (substr($normalizedKey, -1, 1) === "=") { + $normalizedEntry['optional'] = true; + $normalizedKey = substr($normalizedKey, 0, -1); + } + + $normalizedEntries[$normalizedKey] = $normalizedEntry; + + } + foreach($rF->getParameters() as $parameter) { + if ($this->skipUndefinedParams && !isset($normalizedEntries[$parameter->getName()])) { + continue; + } + $this->assertParameter($normalizedEntries[$parameter->getName()], $parameter, $functionName); + } + + } + + /** + * + * @param array{byRef: bool, refMode: 'rw'|'w', variadic: bool, optional: bool, type: string} $normalizedEntry + */ + private function assertParameter(array $normalizedEntry, ReflectionParameter $param, string $functionName): void + { + $name = $param->getName(); + // $identifier = "Param $functionName - $name"; + $this->assertSame($param->isOptional(), $normalizedEntry['optional'], "Expected param '{$name}' to " . ($param->isOptional() ? "be" : "not be") . " optional"); + $this->assertSame($param->isVariadic(), $normalizedEntry['variadic'], "Expected param '{$name}' to " . ($param->isVariadic() ? "be" : "not be") . " variadic"); + $this->assertSame($param->isPassedByReference(), $normalizedEntry['byRef'], "Expected param '{$name}' to " . ($param->isPassedByReference() ? "be" : "not be") . " by reference"); + + + $expectedType = $param->getType(); + if ($expectedType instanceof ReflectionNamedType) { + $this->assertTypeValidity($expectedType, $normalizedEntry['type'], "Param '{$name}' has incorrect type"); + } else { + // $this->markTestSkipped('Only simple named types are tested'); + } + + + + } + + /** + * Since string equality is too strict, we do some extra checking here + */ + private function assertTypeValidity(ReflectionNamedType $reflected, string $specified, string $message): void + { + // Trim leading namespace separator + $specified = ltrim($specified, "\\"); + if ($reflected->getName() === 'array' && preg_match('/^array<.*>$/', $specified)) { + return; + } + if ($reflected->getName() === 'float' && $specified === 'int|float') { + return; + } + + if ($reflected->allowsNull()) { + $this->assertMatchesRegularExpression("/^\?{$reflected->getName()}|{$reflected->getName()}\|null|null\|{$reflected->getName()}/", $specified, $message); + return; + } + + $this->assertSame($reflected->getName(), $specified, $message); + } } From 507e792d9130ca3dd1b21f44793a5b7a0d59b868 Mon Sep 17 00:00:00 2001 From: Sam Mousa Date: Mon, 13 Jun 2022 15:29:21 +0200 Subject: [PATCH 23/88] added more type validity checks to reduce possible false positives --- .../Codebase/InternalCallMapHandlerTest.php | 24 +++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/tests/Internal/Codebase/InternalCallMapHandlerTest.php b/tests/Internal/Codebase/InternalCallMapHandlerTest.php index 5d4ac4d5d08..68f6e5d60d6 100644 --- a/tests/Internal/Codebase/InternalCallMapHandlerTest.php +++ b/tests/Internal/Codebase/InternalCallMapHandlerTest.php @@ -68,8 +68,8 @@ public function callMapEntryProvider(): iterable if (strpos($function, '::') !== false) { continue; } - // Skip alternate signatures - if (preg_match("/\'\d$/", $function)) { + // Skip functions with alternate signatures + if (isset($callMap["$function\'1"]) || preg_match("/\'\d$/", $function)) { continue; } if ($this->skipUndefinedFunctions && !function_exists($function)) { @@ -181,18 +181,34 @@ private function assertTypeValidity(ReflectionNamedType $reflected, string $spec { // Trim leading namespace separator $specified = ltrim($specified, "\\"); - if ($reflected->getName() === 'array' && preg_match('/^array<.*>$/', $specified)) { + if ($reflected->getName() === 'array' && preg_match('/^(array|list)<.*>$/', $specified)) { return; } if ($reflected->getName() === 'float' && $specified === 'int|float') { return; } + if ($reflected->getName() === 'bool' && in_array($specified, ['true', 'false'])) { + return; + } + if ($reflected->getName() === 'callable' && preg_match('/^callable\(/', $specified)) { + return; + } + + if ($reflected->getName() === 'string' && $specified === '?string' && $reflected->allowsNull()) { + return; + } + if ($reflected->getName() === 'string' && in_array($specified , ['class-string', 'numeric-string', 'string'])) { + return; + } + if ($reflected->getName() === 'int' && in_array($specified , ['positive-int', 'int'])) { + return; + } if ($reflected->allowsNull()) { $this->assertMatchesRegularExpression("/^\?{$reflected->getName()}|{$reflected->getName()}\|null|null\|{$reflected->getName()}/", $specified, $message); return; } - $this->assertSame($reflected->getName(), $specified, $message); + $this->assertEqualsIgnoringCase($reflected->getName(), $specified, $message); } } From 1b5a01dd778c34e7ddd8ed917ca6ea780afdf966 Mon Sep 17 00:00:00 2001 From: Sam Mousa Date: Tue, 14 Jun 2022 15:16:13 +0200 Subject: [PATCH 24/88] wip: lots of fixes to the callmap --- dictionaries/CallMap.php | 311 +++++++++--------- phpunit.xml.dist | 2 +- .../Codebase/InternalCallMapHandlerTest.php | 73 +++- 3 files changed, 219 insertions(+), 167 deletions(-) diff --git a/dictionaries/CallMap.php b/dictionaries/CallMap.php index f8f3a994ca8..164eaa771fb 100644 --- a/dictionaries/CallMap.php +++ b/dictionaries/CallMap.php @@ -364,9 +364,9 @@ 'array_column' => ['array', 'array'=>'array', 'column_key'=>'mixed', 'index_key='=>'mixed'], 'array_combine' => ['associative-array', 'keys'=>'string[]|int[]', 'values'=>'array'], 'array_count_values' => ['associative-array', 'array'=>'array'], -'array_diff' => ['associative-array', 'array'=>'array', 'arrays'=>'array', '...args='=>'array'], -'array_diff_assoc' => ['associative-array', 'array'=>'array', 'arrays'=>'array', '...args='=>'array'], -'array_diff_key' => ['associative-array', 'array'=>'array', 'arrays'=>'array', '...args='=>'array'], +'array_diff' => ['associative-array', 'array'=>'array', '...arrays='=>'array'], +'array_diff_assoc' => ['associative-array', 'array'=>'array', '...arrays='=>'array'], +'array_diff_key' => ['associative-array', 'array'=>'array', '...arrays='=>'array'], 'array_diff_uassoc' => ['associative-array', 'array'=>'array', 'rest'=>'array', 'data_comp_func'=>'callable(mixed,mixed):int'], 'array_diff_uassoc\'1' => ['associative-array', 'array'=>'array', 'rest'=>'array', 'arr3'=>'array', 'arg4'=>'array|callable(mixed,mixed):int', '...rest='=>'array|callable(mixed,mixed):int'], 'array_diff_ukey' => ['associative-array', 'array'=>'array', 'rest'=>'array', 'key_comp_func'=>'callable(mixed,mixed):int'], @@ -375,36 +375,36 @@ 'array_fill_keys' => ['array', 'keys'=>'array', 'value'=>'mixed'], 'array_filter' => ['associative-array', 'array'=>'array', 'callback='=>'callable(mixed,mixed=):scalar', 'mode='=>'int'], 'array_flip' => ['associative-array|associative-array', 'array'=>'array'], -'array_intersect' => ['associative-array', 'array'=>'array', 'arrays'=>'array', '...args='=>'array'], -'array_intersect_assoc' => ['associative-array', 'array'=>'array', 'arrays'=>'array', '...args='=>'array'], -'array_intersect_key' => ['associative-array', 'array'=>'array', 'arrays'=>'array', '...args='=>'array'], +'array_intersect' => ['associative-array', 'array'=>'array', '...arrays='=>'array'], +'array_intersect_assoc' => ['associative-array', 'array'=>'array', '...arrays='=>'array'], +'array_intersect_key' => ['associative-array', 'array'=>'array', '...arrays='=>'array'], 'array_intersect_uassoc' => ['associative-array', 'array'=>'array', 'rest'=>'array', 'key_compare_func'=>'callable(mixed,mixed):int'], 'array_intersect_uassoc\'1' => ['associative-array', 'array'=>'array', 'rest'=>'array', 'arr3'=>'array', 'arg4'=>'array|callable(mixed,mixed):int', '...rest'=>'array|callable(mixed,mixed):int'], 'array_intersect_ukey' => ['associative-array', 'array'=>'array', 'rest'=>'array', 'key_compare_func'=>'callable(mixed,mixed):int'], 'array_intersect_ukey\'1' => ['associative-array', 'array'=>'array', 'rest'=>'array', 'arr3'=>'array', 'arg4'=>'array|callable(mixed,mixed):int', '...rest'=>'array|callable(mixed,mixed):int'], 'array_is_list' => ['bool', 'array'=>'array'], -'array_key_exists' => ['bool', 'key'=>'string|int', 'array'=>'array|ArrayObject'], +'array_key_exists' => ['bool', 'key'=>'string|int', 'array'=>'array'], 'array_key_first' => ['int|string|null', 'array'=>'array'], 'array_key_last' => ['int|string|null', 'array'=>'array'], 'array_keys' => ['list', 'array'=>'array', 'filter_value='=>'mixed', 'strict='=>'bool'], 'array_map' => ['array', 'callback'=>'?callable', 'array'=>'array', '...arrays='=>'array'], -'array_merge' => ['array', 'arrays'=>'array', '...args='=>'array'], -'array_merge_recursive' => ['array', 'arrays'=>'array', '...args='=>'array'], -'array_multisort' => ['bool', '&rw_array'=>'array', 'rest='=>'array|int', 'array1_sort_flags='=>'array|int', '...args='=>'array|int'], +'array_merge' => ['array', '...arrays='=>'array'], +'array_merge_recursive' => ['array', '...arrays='=>'array'], +'array_multisort' => ['bool', '&rw_array'=>'array', 'array1_sort_order='=>'array|int', 'array1_sort_flags='=>'array|int', '...rest='=>'array|int'], 'array_pad' => ['array', 'array'=>'array', 'length'=>'int', 'value'=>'mixed'], 'array_pop' => ['mixed', '&rw_array'=>'array'], 'array_product' => ['int|float', 'array'=>'array'], -'array_push' => ['int', '&rw_array'=>'array', 'values'=>'mixed', '...vars='=>'mixed'], +'array_push' => ['int', '&rw_array'=>'array', '...values='=>'mixed'], 'array_rand' => ['int|string|array|array', 'array'=>'non-empty-array', 'num'=>'int'], 'array_rand\'1' => ['int|string', 'array'=>'array'], 'array_reduce' => ['mixed', 'array'=>'array', 'callback'=>'callable(mixed,mixed):mixed', 'initial='=>'mixed'], -'array_replace' => ['array', 'array'=>'array', 'replacements'=>'array', '...args='=>'array'], -'array_replace_recursive' => ['array', 'array'=>'array', 'replacements'=>'array', '...args='=>'array'], +'array_replace' => ['array', 'array'=>'array', '...replacements='=>'array'], +'array_replace_recursive' => ['array', 'array'=>'array', '...replacements='=>'array'], 'array_reverse' => ['array', 'array'=>'array', 'preserve_keys='=>'bool'], 'array_search' => ['int|string|false', 'needle'=>'mixed', 'haystack'=>'array', 'strict='=>'bool'], 'array_shift' => ['mixed|null', '&rw_array'=>'array'], 'array_slice' => ['array', 'array'=>'array', 'offset'=>'int', 'length='=>'?int', 'preserve_keys='=>'bool'], -'array_splice' => ['array', '&rw_array'=>'array', 'offset'=>'int', 'length='=>'int', 'replacement='=>'array|string'], +'array_splice' => ['array', '&rw_array'=>'array', 'offset'=>'int', 'length='=>'int|null', 'replacement='=>'mixed'], 'array_sum' => ['int|float', 'array'=>'array'], 'array_udiff' => ['associative-array', 'array'=>'array', 'rest'=>'array', 'data_comp_func'=>'callable(mixed,mixed):int'], 'array_udiff\'1' => ['associative-array', 'array'=>'array', 'rest'=>'array', 'arr3'=>'array', 'arg4'=>'array|callable(mixed,mixed):int', '...rest='=>'array|callable(mixed,mixed):int'], @@ -421,7 +421,7 @@ 'array_unique' => ['array', 'array'=>'array', 'flags='=>'0'], 'array_unique\'1' => ['array', 'array'=>'array', 'flags='=>'1'], 'array_unique\'2' => ['array', 'array'=>'array', 'flags='=>'2|5'], -'array_unshift' => ['int', '&rw_array'=>'array', 'values'=>'mixed', '...vars='=>'mixed'], +'array_unshift' => ['int', '&rw_array'=>'array', '...values='=>'mixed'], 'array_values' => ['list', 'array'=>'array'], 'array_walk' => ['bool', '&rw_array'=>'array', 'callback'=>'callable', 'arg='=>'mixed'], 'array_walk\'1' => ['bool', '&rw_array'=>'object', 'callback'=>'callable', 'arg='=>'mixed'], @@ -1553,7 +1553,8 @@ 'crack_opendict' => ['resource|false', 'dictionary'=>'string'], 'crash' => [''], 'crc32' => ['int', 'string'=>'string'], -'crypt' => ['string', 'string'=>'string', 'salt='=>'string'], +// While technically optional calling this without salt is bad and creates an E_NOTICE +'crypt' => ['string', 'string'=>'string', 'salt'=>'string'], 'ctype_alnum' => ['bool', 'text'=>'string|int'], 'ctype_alpha' => ['bool', 'text'=>'string|int'], 'ctype_cntrl' => ['bool', 'text'=>'string|int'], @@ -1672,7 +1673,7 @@ 'curl_escape' => ['string|false', 'handle'=>'CurlHandle', 'string'=>'string'], 'curl_exec' => ['bool|string', 'handle'=>'CurlHandle'], 'curl_file_create' => ['CURLFile', 'filename'=>'string', 'mime_type='=>'string|null', 'posted_filename='=>'string|null'], -'curl_getinfo' => ['mixed', 'handle'=>'CurlHandle', 'option='=>'int'], +'curl_getinfo' => ['mixed', 'handle'=>'CurlHandle', 'option='=>'int|null'], 'curl_init' => ['CurlHandle|false', 'url='=>'string'], 'curl_multi_add_handle' => ['int', 'multi_handle'=>'CurlMultiHandle', 'handle'=>'CurlHandle'], 'curl_multi_close' => ['void', 'multi_handle'=>'CurlMultiHandle'], @@ -1695,7 +1696,7 @@ 'curl_share_setopt' => ['bool', 'share_handle'=>'CurlShareHandle', 'option'=>'int', 'value'=>'mixed'], 'curl_share_strerror' => ['?string', 'error_code'=>'int'], 'curl_strerror' => ['?string', 'error_code'=>'int'], -'curl_unescape' => ['string|false', 'handle'=>'CurlShareHandle', 'string'=>'string'], +'curl_unescape' => ['string|false', 'handle'=>'CurlHandle', 'string'=>'string'], 'curl_version' => ['array', 'version='=>'int'], 'CURLFile::__construct' => ['void', 'filename'=>'string', 'mimetype='=>'string', 'postfilename='=>'string'], 'CURLFile::__wakeup' => ['void'], @@ -1715,7 +1716,7 @@ 'date' => ['string', 'format'=>'string', 'timestamp='=>'?int'], 'date_add' => ['DateTime|false', 'object'=>'DateTime', 'interval'=>'DateInterval'], 'date_create' => ['DateTime|false', 'datetime='=>'string', 'timezone='=>'?DateTimeZone'], -'date_create_from_format' => ['DateTime|false', 'format'=>'string', 'datetime'=>'string', 'timezone='=>'?\DateTimeZone'], +'date_create_from_format' => ['DateTime|false', 'format'=>'string', 'datetime'=>'string', 'timezone='=>'\DateTimeZone|null'], 'date_create_immutable' => ['DateTimeImmutable|false', 'datetime='=>'string', 'timezone='=>'?DateTimeZone'], 'date_create_immutable_from_format' => ['DateTimeImmutable|false', 'format'=>'string', 'datetime'=>'string', 'timezone='=>'?DateTimeZone'], 'date_date_set' => ['DateTime|false', 'object'=>'DateTime', 'year'=>'int', 'month'=>'int', 'day'=>'int'], @@ -1726,7 +1727,7 @@ 'date_get_last_errors' => ['array{warning_count:int,warnings:array,error_count:int,errors:array}'], 'date_interval_create_from_date_string' => ['DateInterval', 'datetime'=>'string'], 'date_interval_format' => ['string', 'object'=>'DateInterval', 'format'=>'string'], -'date_isodate_set' => ['DateTime|false', 'object'=>'DateTime', 'year'=>'int', 'week'=>'int', 'dayOfWeek='=>'int|mixed'], +'date_isodate_set' => ['DateTime|false', 'object'=>'DateTime', 'year'=>'int', 'week'=>'int', 'dayOfWeek='=>'1|2|3|4|5|6|7'], 'date_modify' => ['DateTime|false', 'object'=>'DateTime', 'modifier'=>'string'], 'date_offset_get' => ['int|false', 'object'=>'DateTimeInterface'], 'date_parse' => ['array|false', 'datetime'=>'string'], @@ -1734,13 +1735,13 @@ 'date_sub' => ['DateTime|false', 'object'=>'DateTime', 'interval'=>'DateInterval'], 'date_sun_info' => ['array|false', 'timestamp'=>'int', 'latitude'=>'float', 'longitude'=>'float'], 'date_sunrise' => ['mixed', 'timestamp'=>'int', 'returnFormat='=>'int', 'latitude='=>'float', 'longitude='=>'float', 'zenith='=>'float', 'utcOffset='=>'float'], -'date_sunset' => ['mixed', 'timestamp'=>'int', 'returnFormat='=>'int', 'latitude='=>'float', 'longitude='=>'float', 'zenith='=>'float', 'utcOffset='=>'float'], -'date_time_set' => ['DateTime|false', 'object'=>'', 'hour'=>'', 'minute'=>'', 'second='=>'', 'microsecond='=>''], +'date_sunset' => ['string|int|float|false', 'timestamp'=>'int', 'returnFormat='=>'int', 'latitude='=>'float|null', 'longitude='=>'float|null', 'zenith='=>'float|null', 'utcOffset='=>'float|null'], +'date_time_set' => ['DateTime|false', 'object'=>'DateTime', 'hour'=>'int', 'minute'=>'int', 'second='=>'int', 'microsecond='=>'int'], 'date_timestamp_get' => ['int', 'object'=>'DateTimeInterface'], 'date_timestamp_set' => ['DateTime|false', 'object'=>'DateTime', 'timestamp'=>'int'], 'date_timezone_get' => ['DateTimeZone|false', 'object'=>'DateTimeInterface'], 'date_timezone_set' => ['DateTime|false', 'object'=>'DateTime', 'timezone'=>'DateTimeZone'], -'datefmt_create' => ['IntlDateFormatter|false', 'locale'=>'?string', 'dateType'=>'?int', 'timeType'=>'?int', 'timezone='=>'string|DateTimeZone|IntlTimeZone|null', 'calendar='=>'int|IntlCalendar|null', 'pattern='=>'string'], +'datefmt_create' => ['IntlDateFormatter|false', 'locale'=>'?string', 'dateType'=>'int', 'timeType'=>'int', 'timezone='=>'string|DateTimeZone|IntlTimeZone|null', 'calendar='=>'int|IntlCalendar|null', 'pattern='=>'string'], 'datefmt_format' => ['string|false', 'formatter'=>'IntlDateFormatter', 'datetime'=>'DateTime|IntlCalendar|array|int'], 'datefmt_format_object' => ['string|false', 'datetime'=>'object', 'format='=>'mixed', 'locale='=>'string'], 'datefmt_get_calendar' => ['int', 'formatter'=>'IntlDateFormatter'], @@ -1754,12 +1755,12 @@ 'datefmt_get_timezone' => ['IntlTimeZone|false'], 'datefmt_get_timezone_id' => ['string', 'formatter'=>'IntlDateFormatter'], 'datefmt_is_lenient' => ['bool', 'formatter'=>'IntlDateFormatter'], -'datefmt_localtime' => ['array|false', 'formatter'=>'IntlDateFormatter', 'string='=>'string', '&rw_offset='=>'int'], -'datefmt_parse' => ['int|false', 'formatter'=>'IntlDateFormatter', 'string='=>'string', '&rw_offset='=>'int'], +'datefmt_localtime' => ['array|false', 'formatter'=>'IntlDateFormatter', 'string'=>'string', '&rw_offset='=>'int'], +'datefmt_parse' => ['int|false', 'formatter'=>'IntlDateFormatter', 'string'=>'string', '&rw_offset='=>'int'], 'datefmt_set_calendar' => ['bool', 'formatter'=>'IntlDateFormatter', 'calendar'=>'int'], 'datefmt_set_lenient' => ['?bool', 'formatter'=>'IntlDateFormatter', 'lenient'=>'bool'], 'datefmt_set_pattern' => ['bool', 'formatter'=>'IntlDateFormatter', 'pattern'=>'string'], -'datefmt_set_timezone' => ['bool', 'formatter'=>'mixed'], +'datefmt_set_timezone' => ['bool', 'formatter'=>'IntlDateFormatter'], 'datefmt_set_timezone_id' => ['bool', 'fmt'=>'IntlDateFormatter', 'zone'=>'string'], 'DateInterval::__construct' => ['void', 'spec'=>'string'], 'DateInterval::__set_state' => ['DateInterval', 'array'=>'array'], @@ -1973,7 +1974,7 @@ 'deaggregate' => ['', 'object'=>'object', 'class_name='=>'string'], 'debug_backtrace' => ['list', 'options='=>'int', 'limit='=>'int'], 'debug_print_backtrace' => ['void', 'options='=>'int', 'limit='=>'int'], -'debug_zval_dump' => ['void', '...value'=>'mixed'], +'debug_zval_dump' => ['void', 'value' => 'mixed', '...values='=>'mixed'], 'debugger_connect' => [''], 'debugger_connector_pid' => [''], 'debugger_get_server_start_time' => [''], @@ -1985,7 +1986,7 @@ 'define' => ['bool', 'constant_name'=>'string', 'value'=>'mixed', 'case_insensitive='=>'bool'], 'define_syslog_variables' => ['void'], 'defined' => ['bool', 'constant_name'=>'string'], -'deflate_add' => ['string|false', 'context'=>'resource', 'data'=>'string', 'flush_mode='=>'int'], +'deflate_add' => ['string|false', 'context'=>'DeflateContext', 'data'=>'string', 'flush_mode='=>'int'], 'deflate_init' => ['resource|false', 'encoding'=>'int', 'options='=>'array'], 'deg2rad' => ['float', 'num'=>'float'], 'dgettext' => ['string', 'domain'=>'string', 'message'=>'string'], @@ -2046,7 +2047,7 @@ 'dl' => ['bool', 'extension_filename'=>'string'], 'dngettext' => ['string', 'domain'=>'string', 'singular'=>'string', 'plural'=>'string', 'count'=>'int'], 'dns_check_record' => ['bool', 'hostname'=>'string', 'type='=>'string'], -'dns_get_mx' => ['bool', 'hostname'=>'string', '&w_hosts'=>'array', '&w_weights'=>'array'], +'dns_get_mx' => ['bool', 'hostname'=>'string', '&w_hosts'=>'array', '&w_weights='=>'array|null'], 'dns_get_record' => ['list|false', 'hostname'=>'string', 'type='=>'int', '&w_authoritative_name_servers='=>'array', '&w_additional_records='=>'array', 'raw='=>'bool'], 'dom_document_relaxNG_validate_file' => ['bool', 'filename'=>'string'], 'dom_document_relaxNG_validate_xml' => ['bool', 'source'=>'string'], @@ -2431,8 +2432,8 @@ 'Ds\Vector::sum' => ['int|float'], 'Ds\Vector::toArray' => ['array'], 'Ds\Vector::unshift' => ['void', '...values='=>'mixed'], -'easter_date' => ['int', 'year='=>'int'], -'easter_days' => ['int', 'year='=>'int', 'mode='=>'int'], +'easter_date' => ['int', 'year='=>'int|null', 'mode=' => 'int'], +'easter_days' => ['int', 'year='=>'int|null', 'mode='=>'int'], 'echo' => ['void', 'arg1'=>'string', '...args='=>'string'], 'eio_busy' => ['resource', 'delay'=>'int', 'pri='=>'int', 'callback='=>'callable', 'data='=>'mixed'], 'eio_cancel' => ['void', 'req'=>'resource'], @@ -2535,7 +2536,7 @@ 'error_clear_last' => ['void'], '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'], +'error_reporting' => ['int', 'error_level='=>'int|null'], 'ErrorException::__clone' => ['void'], 'ErrorException::__construct' => ['void', 'message='=>'string', 'code='=>'int', 'severity='=>'int', 'filename='=>'string', 'line='=>'int', 'previous='=>'?Throwable|?ErrorException'], 'ErrorException::__toString' => ['string'], @@ -2927,7 +2928,7 @@ 'explode' => ['list', 'separator'=>'string', 'string'=>'string', 'limit='=>'int'], 'expm1' => ['float', 'num'=>'float'], 'extension_loaded' => ['bool', 'extension'=>'string'], -'extract' => ['int', '&rw_array'=>'array', 'flags='=>'int', 'prefix='=>'?string'], +'extract' => ['int', '&rw_array'=>'array', 'flags='=>'int', 'prefix='=>'string'], 'ezmlm_hash' => ['int', 'addr'=>'string'], 'fam_cancel_monitor' => ['bool', 'fam'=>'resource', 'fam_monitor'=>'resource'], 'fam_close' => ['void', 'fam'=>'resource'], @@ -3225,12 +3226,11 @@ 'ffmpeg_movie::hasAudio' => ['bool'], 'ffmpeg_movie::hasVideo' => ['bool'], 'fgetc' => ['string|false', 'stream'=>'resource'], -'fgetcsv' => ['list|array{0: null}|false|null', 'stream'=>'resource', 'length='=>'int', 'separator='=>'string', 'enclosure='=>'string', 'escape='=>'string'], -'fgets' => ['string|false', 'stream'=>'resource', 'length='=>'int'], -'fgetss' => ['string|false', 'fp'=>'resource', 'length='=>'int', 'allowable_tags='=>'string'], +'fgetcsv' => ['list|array{0: null}|false|null', 'stream'=>'resource', 'length='=>'int|null', 'separator='=>'string', 'enclosure='=>'string', 'escape='=>'string'], +'fgets' => ['string|false', 'stream'=>'resource', 'length='=>'int|null'], 'file' => ['list|false', 'filename'=>'string', 'flags='=>'int', 'context='=>'resource'], 'file_exists' => ['bool', 'filename'=>'string'], -'file_get_contents' => ['string|false', 'filename'=>'string', 'use_include_path='=>'bool', 'context='=>'?resource', 'offset='=>'int', 'length='=>'int'], +'file_get_contents' => ['string|false', 'filename'=>'string', 'use_include_path='=>'bool', 'context='=>'?resource', 'offset='=>'int', 'length='=>'int|null'], 'file_put_contents' => ['int|false', 'filename'=>'string', 'data'=>'string|resource|array', 'flags='=>'int', 'context='=>'resource'], 'fileatime' => ['int|false', 'filename'=>'string'], 'filectime' => ['int|false', 'filename'=>'string'], @@ -3325,14 +3325,14 @@ 'fpm_get_status' => ['array|false'], 'fprintf' => ['int', 'stream'=>'resource', 'format'=>'string', '...values='=>'string|int|float'], 'fputcsv' => ['int|false', 'stream'=>'resource', 'fields'=>'array', 'separator='=>'string', 'enclosure='=>'string', 'escape='=>'string'], -'fputs' => ['int|false', 'stream'=>'resource', 'data'=>'string', 'length='=>'int'], +'fputs' => ['int|false', 'stream'=>'resource', 'data'=>'string', 'length='=>'int|null'], 'fread' => ['string|false', 'stream'=>'resource', 'length'=>'int'], 'frenchtojd' => ['int', 'month'=>'int', 'day'=>'int', 'year'=>'int'], 'fribidi_log2vis' => ['string', 'string'=>'string', 'direction'=>'string', 'charset'=>'int'], 'fscanf' => ['list', 'stream'=>'resource', 'format'=>'string'], 'fscanf\'1' => ['int', 'stream'=>'resource', 'format'=>'string', '&...w_vars='=>'string|int|float'], 'fseek' => ['int', 'stream'=>'resource', 'offset'=>'int', 'whence='=>'int'], -'fsockopen' => ['resource|false', 'hostname'=>'string', 'port='=>'int', '&w_error_code='=>'int', '&w_error_message='=>'string', 'timeout='=>'float'], +'fsockopen' => ['resource|false', 'hostname'=>'string', 'port='=>'int', '&w_error_code='=>'int|null', '&w_error_message='=>'string|null', 'timeout='=>'float|null'], 'fstat' => ['array{0: int, 1: int, 2: int, 3: int, 4: int, 5: int, 6: int, 7: int, 8: int, 9: int, 10: int, 11: int, 12: int, dev: int, ino: int, mode: int, nlink: int, uid: int, gid: int, rdev: int, size: int, atime: int, mtime: int, ctime: int, blksize: int, blocks: int}|false', 'stream'=>'resource'], 'ftell' => ['int|false', 'stream'=>'resource'], 'ftok' => ['int', 'filename'=>'string', 'project_id'=>'string'], @@ -3377,7 +3377,7 @@ 'func_get_args' => ['list'], 'func_num_args' => ['int'], 'function_exists' => ['bool', 'function'=>'string'], -'fwrite' => ['int|false', 'stream'=>'resource', 'data'=>'string', 'length='=>'int'], +'fwrite' => ['int|false', 'stream'=>'resource', 'data'=>'string', 'length='=>'int|null'], 'Fiber::__construct' => ['void', 'callback'=>'callable'], 'Fiber::start' => ['mixed', '...args'=>'mixed'], 'Fiber::resume' => ['mixed', 'value='=>'null|mixed'], @@ -3735,7 +3735,7 @@ 'get_defined_functions' => ['array>', 'exclude_disabled='=>'bool'], 'get_defined_vars' => ['array'], 'get_extension_funcs' => ['list|false', 'extension'=>'string'], -'get_headers' => ['array|false', 'url'=>'string', 'associative='=>'int', 'context='=>'resource'], +'get_headers' => ['array|false', 'url'=>'string', 'associative='=>'bool', 'context='=>'?resource'], 'get_html_translation_table' => ['array', 'table='=>'int', 'flags='=>'int', 'encoding='=>'string'], 'get_include_path' => ['string'], 'get_included_files' => ['list'], @@ -3751,7 +3751,7 @@ 'get_resources' => ['array', 'type='=>'string'], 'getallheaders' => ['array|false'], 'getcwd' => ['string|false'], -'getdate' => ['array{seconds: int<0, 59>, minutes: int<0, 59>, hours: int<0, 23>, mday: int<1, 31>, wday: int<0, 6>, mon: int<1, 12>, year: int, yday: int<0, 365>, weekday: "Monday"|"Tuesday"|"Wednesday"|"Thursday"|"Friday"|"Saturday"|"Sunday", month: "January"|"February"|"March"|"April"|"May"|"June"|"July"|"August"|"September"|"October"|"November"|"December", 0: int}', 'timestamp='=>'int'], +'getdate' => ['array{seconds: int<0, 59>, minutes: int<0, 59>, hours: int<0, 23>, mday: int<1, 31>, wday: int<0, 6>, mon: int<1, 12>, year: int, yday: int<0, 365>, weekday: "Monday"|"Tuesday"|"Wednesday"|"Thursday"|"Friday"|"Saturday"|"Sunday", month: "January"|"February"|"March"|"April"|"May"|"June"|"July"|"August"|"September"|"October"|"November"|"December", 0: int}', 'timestamp='=>'int|null'], 'getenv' => ['string|false', 'name'=>'string', 'local_only='=>'bool'], 'getenv\'1' => ['array'], 'gethostbyaddr' => ['string|false', 'ip'=>'string'], @@ -4106,7 +4106,7 @@ 'grapheme_strripos' => ['int|false', 'haystack'=>'string', 'needle'=>'string', 'offset='=>'int'], 'grapheme_strrpos' => ['int|false', 'haystack'=>'string', 'needle'=>'string', 'offset='=>'int'], 'grapheme_strstr' => ['string|false', 'haystack'=>'string', 'needle'=>'string', 'beforeNeedle='=>'bool'], -'grapheme_substr' => ['string|false', 'string'=>'string', 'offset'=>'int', 'length='=>'int'], +'grapheme_substr' => ['string|false', 'string'=>'string', 'offset'=>'int', 'length='=>'int|null'], 'gregoriantojd' => ['int', 'month'=>'int', 'day'=>'int', 'year'=>'int'], 'gridObj::set' => ['int', 'property_name'=>'string', 'new_value'=>''], 'Grpc\Call::__construct' => ['void', 'channel'=>'Grpc\Channel', 'method'=>'string', 'absolute_deadline'=>'Grpc\Timeval', 'host_override='=>'mixed'], @@ -4189,17 +4189,17 @@ 'gzeof' => ['bool|int', 'stream'=>'resource'], 'gzfile' => ['list', 'filename'=>'string', 'use_include_path='=>'int'], 'gzgetc' => ['string|false', 'stream'=>'resource'], -'gzgets' => ['string|false', 'stream'=>'resource', 'length='=>'int'], +'gzgets' => ['string|false', 'stream'=>'resource', 'length='=>'int|null'], 'gzinflate' => ['string|false', 'data'=>'string', 'max_length='=>'int'], 'gzopen' => ['resource|false', 'filename'=>'string', 'mode'=>'string', 'use_include_path='=>'int'], 'gzpassthru' => ['int|false', 'stream'=>'resource'], -'gzputs' => ['int', 'stream'=>'resource', 'data'=>'string', 'length='=>'int'], +'gzputs' => ['int', 'stream'=>'resource', 'data'=>'string', 'length='=>'int|null'], 'gzread' => ['string|false', 'stream'=>'resource', 'length'=>'int'], 'gzrewind' => ['bool', 'stream'=>'resource'], 'gzseek' => ['int', 'stream'=>'resource', 'offset'=>'int', 'whence='=>'int'], 'gztell' => ['int|false', 'stream'=>'resource'], 'gzuncompress' => ['string|false', 'data'=>'string', 'max_length='=>'int'], -'gzwrite' => ['int', 'stream'=>'resource', 'data'=>'string', 'length='=>'int'], +'gzwrite' => ['int', 'stream'=>'resource', 'data'=>'string', 'length='=>'int|null'], 'HaruAnnotation::setBorderStyle' => ['bool', 'width'=>'float', 'dash_on'=>'int', 'dash_off'=>'int'], 'HaruAnnotation::setHighlightMode' => ['bool', 'mode'=>'int'], 'HaruAnnotation::setIcon' => ['bool', 'icon'=>'int'], @@ -5283,7 +5283,7 @@ 'iconv_strlen' => ['0|positive-int|false', 'string'=>'string', 'encoding='=>'string'], 'iconv_strpos' => ['int|false', 'haystack'=>'string', 'needle'=>'string', 'offset='=>'int', 'encoding='=>'string'], 'iconv_strrpos' => ['int|false', 'haystack'=>'string', 'needle'=>'string', 'encoding='=>'string'], -'iconv_substr' => ['string|false', 'string'=>'string', 'offset'=>'int', 'length='=>'int', 'encoding='=>'string'], +'iconv_substr' => ['string|false', 'string'=>'string', 'offset'=>'int', 'length='=>'int|null', 'encoding='=>'string|null'], 'id3_get_frame_long_name' => ['string', 'frameid'=>'string'], 'id3_get_frame_short_name' => ['string', 'frameid'=>'string'], 'id3_get_genre_id' => ['int', 'genre'=>'string'], @@ -5293,7 +5293,7 @@ 'id3_get_version' => ['int', 'filename'=>'string'], 'id3_remove_tag' => ['bool', 'filename'=>'string', 'version='=>'int'], 'id3_set_tag' => ['bool', 'filename'=>'string', 'tag'=>'array', 'version='=>'int'], -'idate' => ['int', 'format'=>'string', 'timestamp='=>'int'], +'idate' => ['int', 'format'=>'string', 'timestamp='=>'int|null'], 'idn_strerror' => ['string', 'errorcode'=>'int'], 'idn_to_ascii' => ['string|false', 'domain'=>'string', 'flags='=>'int', 'variant='=>'int', '&w_idna_info='=>'array'], 'idn_to_utf8' => ['string|false', 'domain'=>'string', 'flags='=>'int', 'variant='=>'int', '&w_idna_info='=>'array'], @@ -5337,7 +5337,7 @@ 'ifxus_write_slob' => ['int', 'bid'=>'int', 'content'=>'string'], 'igbinary_serialize' => ['string|false', 'value'=>'mixed'], 'igbinary_unserialize' => ['mixed', 'string'=>'string'], -'ignore_user_abort' => ['int', 'enable='=>'bool'], +'ignore_user_abort' => ['int', 'enable='=>'?bool'], 'iis_add_server' => ['int', 'path'=>'string', 'comment'=>'string', 'server_ip'=>'string', 'port'=>'int', 'host_name'=>'string', 'rights'=>'int', 'start_server'=>'int'], 'iis_get_dir_security' => ['int', 'server_instance'=>'int', 'virtual_path'=>'string'], 'iis_get_script_map' => ['string', 'server_instance'=>'int', 'virtual_path'=>'string', 'script_extension'=>'string'], @@ -5381,7 +5381,7 @@ 'imagecolorset' => ['void', 'image'=>'GdImage', 'color'=>'int', 'red'=>'int', 'green'=>'int', 'blue'=>'int', 'alpha='=>'int'], 'imagecolorsforindex' => ['array|false', 'image'=>'GdImage', 'color'=>'int'], 'imagecolorstotal' => ['int|false', 'image'=>'GdImage'], -'imagecolortransparent' => ['int|false', 'image'=>'GdImage', 'color='=>'int'], +'imagecolortransparent' => ['int|false', 'image'=>'GdImage', 'color='=>'int|null'], 'imageconvolution' => ['bool', 'image'=>'GdImage', 'matrix'=>'array', 'divisor'=>'float', 'offset'=>'float'], 'imagecopy' => ['bool', 'dst_image'=>'GdImage', 'src_image'=>'GdImage', 'dst_x'=>'int', 'dst_y'=>'int', 'src_x'=>'int', 'src_y'=>'int', 'src_width'=>'int', 'src_height'=>'int'], 'imagecopymerge' => ['bool', 'dst_image'=>'GdImage', 'src_image'=>'GdImage', 'dst_x'=>'int', 'dst_y'=>'int', 'src_x'=>'int', 'src_y'=>'int', 'src_width'=>'int', 'src_height'=>'int', 'pct'=>'int'], @@ -5411,10 +5411,15 @@ 'imagefill' => ['bool', 'image'=>'GdImage', 'x'=>'int', 'y'=>'int', 'color'=>'int'], 'imagefilledarc' => ['bool', 'image'=>'GdImage', 'center_x'=>'int', 'center_y'=>'int', 'width'=>'int', 'height'=>'int', 'start_angle'=>'int', 'end_angle'=>'int', 'color'=>'int', 'style'=>'int'], 'imagefilledellipse' => ['bool', 'image'=>'GdImage', 'center_x'=>'int', 'center_y'=>'int', 'width'=>'int', 'height'=>'int', 'color'=>'int'], -'imagefilledpolygon' => ['bool', 'image'=>'GdImage', 'points'=>'array', 'num_points_or_color'=>'int', 'color'=>'int'], +'imagefilledpolygon' => ['bool', 'image'=>'GdImage', 'points'=>'array', 'color'=>'int'], 'imagefilledrectangle' => ['bool', 'image'=>'GdImage', 'x1'=>'int', 'y1'=>'int', 'x2'=>'int', 'y2'=>'int', 'color'=>'int'], 'imagefilltoborder' => ['bool', 'image'=>'GdImage', 'x'=>'int', 'y'=>'int', 'border_color'=>'int', 'color'=>'int'], -'imagefilter' => ['bool', 'image'=>'GdImage', 'filter'=>'int', 'args='=>'int', 'arg2='=>'int', 'arg3='=>'int', 'arg4='=>'int'], +'imagefilter' => ['bool', 'image' => 'GdImage', 'filter' => '0|1|5|6|7|8'], +'imagefilter\'1' => ['bool', 'image' => 'GdImage', 'filter' => '2', 'args' => 'int<-255,255>'], +'imagefilter\'2' => ['bool', 'image' => 'GdImage', 'filter' => '3|10', 'args' => 'int'], +'imagefilter\'3' => ['bool', 'image' => 'GdImage', 'filter' => '4', 'args' => 'int<0,255>', 'args2' => 'int<0,255>', 'args3' => 'int<0,255>', 'args4' => 'int<0,127>'], +'imagefilter\'4' => ['bool', 'image' => 'GdImage', 'filter' => '11', 'args' => 'int', 'args2=' => 'bool'], +'imagefilter\'5' => ['bool', 'image' => 'GdImage', 'filter' => '12', 'args' => 'int', 'args2=' => 'int', 'args3=' => 'array'], 'imageflip' => ['bool', 'image'=>'GdImage', 'mode'=>'int'], 'imagefontheight' => ['int', 'font'=>'int'], 'imagefontwidth' => ['int', 'font'=>'int'], @@ -5428,7 +5433,7 @@ 'imagegif' => ['bool', 'image'=>'GdImage', 'file='=>'string|resource|null'], 'imagegrabscreen' => ['false|GdImage'], 'imagegrabwindow' => ['false|GdImage', 'handle'=>'int', 'client_area='=>'int'], -'imageinterlace' => ['int|false', 'image'=>'GdImage', 'enable='=>'int'], +'imageinterlace' => ['int|false', 'image'=>'GdImage', 'enable='=>'bool|null'], 'imageistruecolor' => ['bool', 'image'=>'GdImage'], 'imagejpeg' => ['bool', 'image'=>'GdImage', 'file='=>'string|resource|null', 'quality='=>'int'], 'imagelayereffect' => ['bool', 'image'=>'GdImage', 'effect'=>'int'], @@ -5437,14 +5442,14 @@ 'imageObj::pasteImage' => ['void', 'srcImg'=>'imageObj', 'transparentColorHex'=>'int', 'dstX'=>'int', 'dstY'=>'int', 'angle'=>'int'], 'imageObj::saveImage' => ['int', 'filename'=>'string', 'oMap'=>'mapObj'], 'imageObj::saveWebImage' => ['string'], -'imageopenpolygon' => ['bool', 'image'=>'GdImage', 'points'=>'array', 'num_points'=>'int', 'color'=>'int'], +'imageopenpolygon' => ['bool', 'image'=>'GdImage', 'points'=>'list{0: int, 1: int, 2: int, 3: int}', 'color'=>'int'], 'imagepalettecopy' => ['void', 'dst'=>'GdImage', 'src'=>'GdImage'], 'imagepalettetotruecolor' => ['bool', 'image'=>'GdImage'], 'imagepng' => ['bool', 'image'=>'GdImage', 'file='=>'string|resource|null', 'quality='=>'int', 'filters='=>'int'], -'imagepolygon' => ['bool', 'image'=>'GdImage', 'points'=>'array', 'num_points_or_color'=>'int', 'color'=>'int'], +'imagepolygon' => ['bool', 'image'=>'GdImage', 'points'=>'array', 'color'=>'int'], 'imagerectangle' => ['bool', 'image'=>'GdImage', 'x1'=>'int', 'y1'=>'int', 'x2'=>'int', 'y2'=>'int', 'color'=>'int'], -'imageresolution' => ['array|bool', 'image'=>'GdImage', 'resolution_x='=>'int', 'resolution_y='=>'int'], -'imagerotate' => ['false|GdImage', 'image'=>'GdImage', 'angle'=>'float', 'background_color'=>'int', 'ignore_transparent='=>'int'], +'imageresolution' => ['array|bool', 'image'=>'GdImage', 'resolution_x='=>'int|null', 'resolution_y='=>'int|null'], +'imagerotate' => ['false|GdImage', 'image'=>'GdImage', 'angle'=>'float', 'background_color'=>'int', 'ignore_transparent='=>'bool'], 'imagesavealpha' => ['bool', 'image'=>'GdImage', 'enable'=>'bool'], 'imagescale' => ['false|GdImage', 'image'=>'GdImage', 'width'=>'int', 'height='=>'int', 'mode='=>'int'], 'imagesetbrush' => ['bool', 'image'=>'GdImage', 'brush'=>'GdImage'], @@ -5462,9 +5467,9 @@ 'imagettfbbox' => ['false|array', 'size'=>'float', 'angle'=>'float', 'font_filename'=>'string', 'string'=>'string'], 'imagettftext' => ['false|array', 'image'=>'GdImage', 'size'=>'float', 'angle'=>'float', 'x'=>'int', 'y'=>'int', 'color'=>'int', 'font_filename'=>'string', 'text'=>'string'], 'imagetypes' => ['int'], -'imagewbmp' => ['bool', 'image'=>'GdImage', 'file='=>'string|resource|null', 'foreground_color='=>'int'], +'imagewbmp' => ['bool', 'image'=>'GdImage', 'file='=>'string|resource|null', 'foreground_color='=>'int|null'], 'imagewebp' => ['bool', 'image'=>'GdImage', 'file='=>'string|resource|null', 'quality='=>'int'], -'imagexbm' => ['bool', 'image'=>'GdImage', 'filename='=>'?string', 'foreground_color='=>'int'], +'imagexbm' => ['bool', 'image'=>'GdImage', 'filename='=>'?string', 'foreground_color='=>'?int'], 'Imagick::__construct' => ['void', 'files='=>'string|string[]'], 'Imagick::__toString' => ['string'], 'Imagick::adaptiveBlurImage' => ['bool', 'radius'=>'float', 'sigma'=>'float', 'channel='=>'int'], @@ -6112,10 +6117,10 @@ 'InfiniteIterator::next' => ['void'], 'InfiniteIterator::rewind' => ['void'], 'InfiniteIterator::valid' => ['bool'], -'inflate_add' => ['string|false', 'context'=>'resource', 'data'=>'string', 'flush_mode='=>'int'], -'inflate_get_read_len' => ['int|false', 'context'=>'resource'], -'inflate_get_status' => ['int|false', 'context'=>'resource'], -'inflate_init' => ['resource|false', 'encoding'=>'int', 'options='=>'array'], +'inflate_add' => ['string|false', 'context'=>'InflateContext', 'data'=>'string', 'flush_mode='=>'int'], +'inflate_get_read_len' => ['int|false', 'context'=>'InflateContext'], +'inflate_get_status' => ['int|false', 'context'=>'InflateContext'], +'inflate_init' => ['InflateContext|false', 'encoding'=>'int', 'options='=>'array'], 'ingres_autocommit' => ['bool', 'link'=>'resource'], 'ingres_autocommit_state' => ['bool', 'link'=>'resource'], 'ingres_charset' => ['string', 'link'=>'resource'], @@ -6190,7 +6195,7 @@ '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' => ['bool', 'calendar'=>'IntlCalendar', 'field='=>'int|null'], 'intlcal_create_instance' => ['IntlCalendar', 'timezone='=>'mixed', 'locale='=>'string'], 'intlcal_equals' => ['bool', 'calendar'=>'IntlCalendar', 'other'=>'IntlCalendar'], 'intlcal_field_difference' => ['int', 'calendar'=>'IntlCalendar', 'timestamp'=>'float', 'field'=>'int'], @@ -6214,12 +6219,12 @@ 'intlcal_get_time' => ['float', 'calendar'=>'IntlCalendar'], 'intlcal_get_time_zone' => ['IntlTimeZone', 'calendar'=>'IntlCalendar'], 'intlcal_get_type' => ['string', 'calendar'=>'IntlCalendar'], -'intlcal_get_weekend_transition' => ['int', 'calendar'=>'IntlCalendar', 'dayOfWeek'=>'string'], +'intlcal_get_weekend_transition' => ['int', 'calendar'=>'IntlCalendar', 'dayOfWeek'=>'1|2|3|4|5|6|7'], 'intlcal_in_daylight_time' => ['bool', 'calendar'=>'IntlCalendar'], 'intlcal_is_equivalent_to' => ['bool', 'calendar'=>'IntlCalendar', 'other'=>'IntlCalendar'], 'intlcal_is_lenient' => ['bool', 'calendar'=>'IntlCalendar'], 'intlcal_is_set' => ['bool', 'calendar'=>'IntlCalendar', 'field'=>'int'], -'intlcal_is_weekend' => ['bool', 'calendar'=>'IntlCalendar', 'timestamp='=>'float'], +'intlcal_is_weekend' => ['bool', 'calendar'=>'IntlCalendar', 'timestamp='=>'?float'], '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'], @@ -6394,7 +6399,7 @@ 'IntlException::getTraceAsString' => ['string'], 'intlgregcal_create_instance' => ['IntlGregorianCalendar', 'timezoneOrYear='=>'mixed', 'localeOrMonth='=>'string'], 'intlgregcal_get_gregorian_change' => ['float', 'calendar'=>'IntlGregorianCalendar'], -'intlgregcal_is_leap_year' => ['bool', 'calendar'=>'int'], +'intlgregcal_is_leap_year' => ['bool', 'calendar'=>'IntlGregorianCalendar', 'year' => 'int'], 'intlgregcal_set_gregorian_change' => ['void', 'calendar'=>'IntlGregorianCalendar', 'timestamp'=>'float'], 'IntlGregorianCalendar::__construct' => ['void'], 'IntlGregorianCalendar::add' => ['bool', 'field'=>'int', 'amount'=>'int'], @@ -6503,11 +6508,11 @@ 'IntlTimeZone::toDateTimeZone' => ['DateTimeZone|false'], 'IntlTimeZone::useDaylightTime' => ['bool'], 'intltz_count_equivalent_ids' => ['int', 'timezoneId'=>'string'], -'intltz_create_enumeration' => ['IntlIterator', 'countryOrRawOffset'=>'mixed'], +'intltz_create_enumeration' => ['IntlIterator', 'countryOrRawOffset='=>'IntlTimeZone|string|int|float|null'], 'intltz_create_time_zone' => ['IntlTimeZone', 'timezoneId'=>'string'], 'intltz_from_date_time_zone' => ['IntlTimeZone', 'timezone'=>'DateTimeZone'], 'intltz_get_canonical_id' => ['string', 'timezoneId'=>'string', '&isSystemId'=>'bool'], -'intltz_get_display_name' => ['string', 'timezone'=>'IntlTimeZone', 'dst'=>'bool', 'style'=>'int', 'locale'=>'string'], +'intltz_get_display_name' => ['string', 'timezone'=>'IntlTimeZone', 'dst='=>'bool', 'style='=>'int', 'locale='=>'string|null'], 'intltz_get_dst_savings' => ['int', 'timezone'=>'IntlTimeZone'], 'intltz_get_equivalent_id' => ['string', 'timezoneId'=>'string', 'offset'=>'int'], 'intltz_get_error_code' => ['int', 'timezone'=>'IntlTimeZone'], @@ -6538,7 +6543,7 @@ 'is_a' => ['bool', 'object_or_class'=>'mixed', 'class'=>'string', 'allow_string='=>'bool'], 'is_array' => ['bool', 'value'=>'mixed'], 'is_bool' => ['bool', 'value'=>'mixed'], -'is_callable' => ['bool', 'value'=>'callable|mixed', 'syntax_only='=>'bool', '&w_callable_name='=>'string'], +'is_callable' => ['bool', 'value'=>'mixed', 'syntax_only='=>'bool', '&w_callable_name='=>'string'], 'is_countable' => ['bool', 'value'=>'mixed'], 'is_dir' => ['bool', 'filename'=>'string'], 'is_double' => ['bool', 'value'=>'mixed'], @@ -6573,7 +6578,7 @@ 'Iterator::next' => ['void'], 'Iterator::rewind' => ['void'], 'Iterator::valid' => ['bool'], -'iterator_apply' => ['0|positive-int', 'iterator'=>'Traversable', 'callback'=>'callable(mixed):bool', 'args='=>'array'], +'iterator_apply' => ['0|positive-int', 'iterator'=>'Traversable', 'callback'=>'callable(mixed):bool', 'args='=>'array|null'], 'iterator_count' => ['0|positive-int', 'iterator'=>'Traversable'], 'iterator_to_array' => ['array', 'iterator'=>'Traversable', 'preserve_keys='=>'bool'], 'IteratorAggregate::getIterator' => ['Traversable'], @@ -6603,7 +6608,7 @@ 'jobqueue_license_info' => ['array'], 'join' => ['string', 'separator'=>'string', 'array'=>'array'], 'join\'1' => ['string', 'separator'=>'array'], -'json_decode' => ['mixed', 'json'=>'string', 'associative='=>'bool', 'depth='=>'int', 'flags='=>'int'], +'json_decode' => ['mixed', 'json'=>'string', 'associative='=>'bool|null', 'depth='=>'int', 'flags='=>'int'], 'json_encode' => ['string|false', 'value'=>'mixed', 'flags='=>'int', 'depth='=>'int'], 'json_last_error' => ['int'], 'json_last_error_msg' => ['string'], @@ -6884,9 +6889,9 @@ 'libxml_disable_entity_loader' => ['bool', 'disable='=>'bool'], 'libxml_get_errors' => ['array'], 'libxml_get_last_error' => ['LibXMLError|false'], -'libxml_set_external_entity_loader' => ['bool', 'resolver_function'=>'callable'], +'libxml_set_external_entity_loader' => ['bool', 'resolver_function'=>'?callable'], 'libxml_set_streams_context' => ['void', 'context'=>'resource'], -'libxml_use_internal_errors' => ['bool', 'use_errors='=>'bool'], +'libxml_use_internal_errors' => ['bool', 'use_errors='=>'bool|null'], 'LimitIterator::__construct' => ['void', 'iterator'=>'Iterator', 'offset='=>'int', 'count='=>'int'], 'LimitIterator::current' => ['mixed'], 'LimitIterator::getInnerIterator' => ['Iterator'], @@ -6944,7 +6949,7 @@ 'locale_parse' => ['array', 'locale'=>'string'], 'locale_set_default' => ['bool', 'locale'=>'string'], 'localeconv' => ['array'], -'localtime' => ['array', 'timestamp='=>'int', 'associative='=>'bool'], +'localtime' => ['array', 'timestamp='=>'int|null', 'associative='=>'bool'], 'log' => ['float', 'num'=>'float', 'base='=>'float'], 'log10' => ['float', 'num'=>'float'], 'log1p' => ['float', 'num'=>'float'], @@ -6958,7 +6963,7 @@ 'LogicException::getPrevious' => ['Throwable|LogicException|null'], 'LogicException::getTrace' => ['list\',args?:array}>'], 'LogicException::getTraceAsString' => ['string'], -'long2ip' => ['string', 'ip'=>'string|int'], +'long2ip' => ['string', 'ip'=>'int'], 'lstat' => ['array{0: int, 1: int, 2: int, 3: int, 4: int, 5: int, 6: int, 7: int, 8: int, 9: int, 10: int, 11: int, 12: int, dev: int, ino: int, mode: int, nlink: int, uid: int, gid: int, rdev: int, size: int, atime: int, mtime: int, ctime: int, blksize: int, blocks: int}|false', 'filename'=>'string'], 'ltrim' => ['string', 'string'=>'string', 'characters='=>'string'], 'Lua::__call' => ['mixed', 'lua_func'=>'callable', 'args='=>'array', 'use_self='=>'int'], @@ -8165,11 +8170,11 @@ 'msession_unlock' => ['int', 'session'=>'string', 'key'=>'int'], 'msg_get_queue' => ['resource', 'key'=>'int', 'permissions='=>'int'], 'msg_queue_exists' => ['bool', 'key'=>'int'], -'msg_receive' => ['bool', 'queue'=>'resource', 'desired_message_type'=>'int', '&w_received_message_type'=>'int', 'max_message_size'=>'int', '&w_message'=>'mixed', 'unserialize='=>'bool', 'flags='=>'int', '&w_error_code='=>'int'], -'msg_remove_queue' => ['bool', 'queue'=>'resource'], -'msg_send' => ['bool', 'queue'=>'resource', 'message_type'=>'int', 'message'=>'mixed', 'serialize='=>'bool', 'blocking='=>'bool', '&w_error_code='=>'int'], -'msg_set_queue' => ['bool', 'queue'=>'resource', 'data'=>'array'], -'msg_stat_queue' => ['array', 'queue'=>'resource'], +'msg_receive' => ['bool', 'queue'=>'SysvMessageQueue', 'desired_message_type'=>'int', '&w_received_message_type'=>'int', 'max_message_size'=>'int', '&w_message'=>'mixed', 'unserialize='=>'bool', 'flags='=>'int', '&w_error_code='=>'int'], +'msg_remove_queue' => ['bool', 'queue'=>'SysvMessageQueue'], +'msg_send' => ['bool', 'queue'=>'SysvMessageQueue', 'message_type'=>'int', 'message'=>'mixed', 'serialize='=>'bool', 'blocking='=>'bool', '&w_error_code='=>'int'], +'msg_set_queue' => ['bool', 'queue'=>'SysvMessageQueue', 'data'=>'array'], +'msg_stat_queue' => ['array', 'queue'=>'SysvMessageQueue'], 'msgfmt_create' => ['MessageFormatter', 'locale'=>'string', 'pattern'=>'string'], 'msgfmt_format' => ['string|false', 'formatter'=>'MessageFormatter', 'values'=>'array'], 'msgfmt_format_message' => ['string|false', 'locale'=>'string', 'pattern'=>'string', 'values'=>'array'], @@ -8508,7 +8513,7 @@ 'mysqli_num_rows' => ['int', 'result'=>'mysqli_result'], 'mysqli_options' => ['bool', 'mysql'=>'mysqli', 'option'=>'int', 'value'=>'string|int'], 'mysqli_ping' => ['bool', 'mysql'=>'mysqli'], -'mysqli_poll' => ['int|false', 'read'=>'array', 'write'=>'array', 'error'=>'array', 'seconds'=>'int', 'microseconds='=>'int'], +'mysqli_poll' => ['int|false', '&rw_read'=>'array|null', '&rw_write'=>'array|null', '&rw_error'=>'array|null', 'seconds'=>'0|positive-int', 'microseconds='=>'0|positive-int'], 'mysqli_prepare' => ['mysqli_stmt|false', 'mysql'=>'mysqli', 'query'=>'string'], 'mysqli_query' => ['mysqli_result|bool', 'mysql'=>'mysqli', 'query'=>'string', 'result_mode='=>'int'], 'mysqli_real_connect' => ['bool', 'mysql='=>'mysqli', 'hostname='=>'string|null', 'username='=>'string|null', 'password='=>'string|null', 'database='=>'string|null', 'port='=>'int|null', 'socket='=>'string|null', 'flags='=>'int'], @@ -9120,7 +9125,7 @@ 'ob_get_status' => ['array', 'full_status='=>'bool'], 'ob_gzhandler' => ['string|false', 'data'=>'string', 'flags'=>'int'], 'ob_iconv_handler' => ['string', 'contents'=>'string', 'status'=>'int'], -'ob_implicit_flush' => ['void', 'enable='=>'int'], +'ob_implicit_flush' => ['void', 'enable='=>'bool'], 'ob_inflatehandler' => ['string', 'data'=>'string', 'mode'=>'int'], 'ob_list_handlers' => ['false|list'], 'ob_start' => ['bool', 'callback='=>'string|array|?callable', 'chunk_size='=>'int', 'flags='=>'int'], @@ -9530,13 +9535,13 @@ 'pclose' => ['int', 'handle'=>'resource'], 'pcnlt_sigwaitinfo' => ['int', 'set'=>'array', '&w_siginfo'=>'array'], 'pcntl_alarm' => ['int', 'seconds'=>'int'], -'pcntl_async_signals' => ['bool', 'enable='=>'bool'], +'pcntl_async_signals' => ['bool', 'enable='=>'bool|null'], 'pcntl_errno' => ['int'], 'pcntl_exec' => ['null|false', 'path'=>'string', 'args='=>'array', 'env_vars='=>'array'], 'pcntl_fork' => ['int'], 'pcntl_get_last_error' => ['int'], -'pcntl_getpriority' => ['int', 'process_id='=>'int', 'mode='=>'int'], -'pcntl_setpriority' => ['bool', 'priority'=>'int', 'process_id='=>'int', 'mode='=>'int'], +'pcntl_getpriority' => ['int', 'process_id='=>'int|null', 'mode='=>'int'], +'pcntl_setpriority' => ['bool', 'priority'=>'int', 'process_id='=>'int|null', 'mode='=>'int'], 'pcntl_signal' => ['bool', 'signal'=>'int', 'handler'=>'callable():void|callable(int):void|callable(int,array):void|int', 'restart_syscalls='=>'bool'], 'pcntl_signal_dispatch' => ['bool'], 'pcntl_signal_get_handler' => ['int|string', 'signal'=>'int'], @@ -9922,11 +9927,11 @@ 'PDOStatement::rowCount' => ['int'], 'PDOStatement::setAttribute' => ['bool', 'attribute'=>'int', 'value'=>'mixed'], 'PDOStatement::setFetchMode' => ['bool', 'mode'=>'int', '...args='=> 'mixed'], -'pfsockopen' => ['resource|false', 'hostname'=>'string', 'port='=>'int', '&w_error_code='=>'int', '&w_error_message='=>'string', 'timeout='=>'float'], +'pfsockopen' => ['resource|false', 'hostname'=>'string', 'port='=>'int', '&w_error_code='=>'int', '&w_error_message='=>'string', 'timeout='=>'float|null'], 'pg_affected_rows' => ['int', 'result'=>'\PgSql\Result'], 'pg_cancel_query' => ['bool', 'connection'=>'\PgSql\Connection'], -'pg_client_encoding' => ['string', 'connection='=>'\PgSql\Connection'], -'pg_close' => ['bool', 'connection='=>'\PgSql\Connection'], +'pg_client_encoding' => ['string', 'connection='=>'\PgSql\Connection|null'], +'pg_close' => ['bool', 'connection='=>'\PgSql\Connection|null'], 'pg_connect' => ['\PgSql\Connection|false', 'connection_string'=>'string', 'flags='=>'int'], 'pg_connect_poll' => ['int', 'connection'=>'\PgSql\Connection'], 'pg_connection_busy' => ['bool', 'connection'=>'\PgSql\Connection'], @@ -9936,9 +9941,9 @@ 'pg_convert' => ['array|false', 'connection'=>'\PgSql\Connection', 'table_name'=>'string', 'values'=>'array', 'flags='=>'int'], 'pg_copy_from' => ['bool', 'connection'=>'\PgSql\Connection', 'table_name'=>'string', 'rows'=>'array', 'separator='=>'string', 'null_as='=>'string'], 'pg_copy_to' => ['array|false', 'connection'=>'\PgSql\Connection', 'table_name'=>'string', 'separator='=>'string', 'null_as='=>'string'], -'pg_dbname' => ['string', 'connection='=>'\PgSql\Connection'], +'pg_dbname' => ['string', 'connection='=>'\PgSql\Connection|null'], 'pg_delete' => ['string|bool', 'connection'=>'\PgSql\Connection', 'table_name'=>'string', 'conditions'=>'array', 'flags='=>'int'], -'pg_end_copy' => ['bool', 'connection='=>'\PgSql\Connection'], +'pg_end_copy' => ['bool', 'connection='=>'\PgSql\Connection|null'], 'pg_escape_bytea' => ['string', 'connection'=>'\PgSql\Connection', 'string'=>'string'], 'pg_escape_bytea\'1' => ['string', 'connection'=>'string'], 'pg_escape_identifier' => ['string|false', 'connection'=>'\PgSql\Connection', 'string'=>'string'], @@ -9972,10 +9977,10 @@ 'pg_free_result' => ['bool', 'result'=>'\PgSql\Result'], 'pg_get_notify' => ['array|false', 'result'=>'\PgSql\Result', 'mode='=>'int'], 'pg_get_pid' => ['int', 'connection'=>'\PgSql\Connection'], -'pg_get_result' => ['\PgSql\Result|false', 'connection='=>'\PgSql\Connection'], -'pg_host' => ['string', 'connection='=>'\PgSql\Connection'], +'pg_get_result' => ['\PgSql\Result|false', 'connection'=>'\PgSql\Connection'], +'pg_host' => ['string', 'connection='=>'\PgSql\Connection|null'], 'pg_insert' => ['\PgSql\Result|string|false', 'connection'=>'\PgSql\Connection', 'table_name'=>'string', 'values'=>'array', 'flags='=>'int'], -'pg_last_error' => ['string', 'connection='=>'\PgSql\Connection', 'operation='=>'int'], +'pg_last_error' => ['string', 'connection='=>'\PgSql\Connection|null'], 'pg_last_notice' => ['string|array|bool', 'connection'=>'\PgSql\Connection', 'mode='=>'int'], 'pg_last_oid' => ['string|int|false', 'result'=>'\PgSql\Result'], 'pg_lo_close' => ['bool', 'lob'=>'\PgSql\Lob'], @@ -9993,20 +9998,20 @@ 'pg_lo_truncate' => ['bool', 'lob'=>'\PgSql\Lob', 'size'=>'int'], 'pg_lo_unlink' => ['bool', 'connection'=>'\PgSql\Connection', 'oid'=>'int|string'], 'pg_lo_unlink\'1' => ['bool', 'connection'=>'int|string'], -'pg_lo_write' => ['int|false', 'lob'=>'\PgSql\Lob', 'data'=>'string', 'length='=>'int'], +'pg_lo_write' => ['int|false', 'lob'=>'\PgSql\Lob', 'data'=>'string', 'length='=>'int|null'], 'pg_meta_data' => ['array|false', 'connection'=>'\PgSql\Connection', 'table_name'=>'string', 'extended='=>'bool'], 'pg_num_fields' => ['int', 'result'=>'\PgSql\Result'], 'pg_num_rows' => ['int', 'result'=>'\PgSql\Result'], -'pg_options' => ['string', 'connection='=>'\PgSql\Connection'], +'pg_options' => ['string', 'connection='=>'\PgSql\Connection|null'], 'pg_parameter_status' => ['string|false', 'connection'=>'\PgSql\Connection', 'name'=>'string'], 'pg_parameter_status\'1' => ['string|false', 'connection'=>'string'], -'pg_pconnect' => ['\PgSql\Connection|false', 'connection_string'=>'string', 'flags='=>'string', 'port='=>'string|int', 'options='=>'string', 'tty='=>'string', 'database='=>'string'], -'pg_ping' => ['bool', 'connection='=>'\PgSql\Connection'], -'pg_port' => ['int', 'connection='=>'\PgSql\Connection'], -'pg_prepare' => ['\PgSql\Result|false', 'connection'=>'\PgSql\Connection', 'statement_name'=>'string', 'query'=>'string'], -'pg_prepare\'1' => ['\PgSql\Result|false', 'connection'=>'string', 'statement_name'=>'string'], +'pg_pconnect' => ['\PgSql\Connection|false', 'connection_string'=>'string', 'flags='=>'int'], +'pg_ping' => ['bool', 'connection='=>'\PgSql\Connection|null'], +'pg_port' => ['int', 'connection='=>'\PgSql\Connection|null'], +'pg_prepare' => ['\PgSql\Result|false', 'connection'=>'\PgSql\Connection|null', 'stmtname'=>'string', 'query'=>'string'], +'pg_prepare\'1' => ['\PgSql\Result|false', 'stmtname'=>'string', 'query'=>'string'], 'pg_put_line' => ['bool', 'connection'=>'\PgSql\Connection', 'data'=>'string'], -'pg_put_line\'1' => ['bool', 'connection'=>'string'], +'pg_put_line\'1' => ['bool', 'data'=>'string'], 'pg_query' => ['\PgSql\Result|false', 'connection'=>'\PgSql\Connection', 'query'=>'string'], 'pg_query\'1' => ['\PgSql\Result|false', 'connection'=>'string'], 'pg_query_params' => ['\PgSql\Result|false', 'connection'=>'\PgSql\Connection', 'query'=>'string', 'params'=>'array'], @@ -10025,13 +10030,13 @@ 'pg_set_error_verbosity' => ['int|false', 'connection'=>'\PgSql\Connection', 'verbosity'=>'int'], 'pg_set_error_verbosity\'1' => ['int|false', 'connection'=>'int'], 'pg_socket' => ['resource|false', 'connection'=>'\PgSql\Connection'], -'pg_trace' => ['bool', 'filename'=>'string', 'mode='=>'string', 'connection='=>'\PgSql\Connection'], +'pg_trace' => ['bool', 'filename'=>'string', 'mode='=>"string", 'connection='=>'\PgSql\Connection|null'], 'pg_transaction_status' => ['int', 'connection'=>'\PgSql\Connection'], -'pg_tty' => ['string', 'connection='=>'\PgSql\Connection'], +'pg_tty' => ['string', 'connection='=>'\PgSql\Connection|null'], 'pg_unescape_bytea' => ['string', 'string'=>'string'], -'pg_untrace' => ['bool', 'connection='=>'\PgSql\Connection'], +'pg_untrace' => ['bool', 'connection='=>'\PgSql\Connection|null'], 'pg_update' => ['string|bool', 'connection'=>'\PgSql\Connection', 'table_name'=>'string', 'values'=>'array', 'conditions'=>'array', 'flags='=>'int'], -'pg_version' => ['array', 'connection='=>'\PgSql\Connection'], +'pg_version' => ['array', 'connection='=>'\PgSql\Connection|null'], 'Phar::__construct' => ['void', 'fname'=>'string', 'flags='=>'int', 'alias='=>'string'], 'Phar::addEmptyDir' => ['void', 'dirname'=>'string'], 'Phar::addFile' => ['void', 'file'=>'string', 'localname='=>'string'], @@ -12023,10 +12028,10 @@ 'SeekableIterator::rewind' => ['void'], 'SeekableIterator::seek' => ['void', 'position'=>'int'], 'SeekableIterator::valid' => ['bool'], -'sem_acquire' => ['bool', 'semaphore'=>'resource', 'non_blocking='=>'bool'], -'sem_get' => ['resource|false', 'key'=>'int', 'max_acquire='=>'int', 'permissions='=>'int', 'auto_release='=>'int'], -'sem_release' => ['bool', 'semaphore'=>'resource'], -'sem_remove' => ['bool', 'semaphore'=>'resource'], +'sem_acquire' => ['bool', 'semaphore'=>'SysvSemaphore', 'non_blocking='=>'bool'], +'sem_get' => ['SysvSemaphore|false', 'key'=>'int', 'max_acquire='=>'int', 'permissions='=>'int', 'auto_release='=>'bool'], +'sem_release' => ['bool', 'semaphore'=>'SysvSemaphore'], +'sem_remove' => ['bool', 'semaphore'=>'SysvSemaphore'], 'Serializable::__construct' => ['void'], 'Serializable::serialize' => ['?string'], 'Serializable::unserialize' => ['void', 'serialized'=>'string'], @@ -12046,7 +12051,7 @@ 'ServerResponse::setStatus' => ['void', 'status'=>'int'], 'ServerResponse::setVersion' => ['void', 'version'=>'string'], 'session_abort' => ['bool'], -'session_cache_expire' => ['int', 'value='=>'int'], +'session_cache_expire' => ['int|false', 'value='=>'int|null'], 'session_cache_limiter' => ['string', 'value='=>'string'], 'session_commit' => ['bool'], 'session_create_id' => ['string', 'prefix='=>'string'], @@ -12164,19 +12169,19 @@ 'shapeObj::union' => ['shapeObj', 'shape'=>'shapeObj'], 'shapeObj::within' => ['int', 'shape2'=>'shapeObj'], 'shell_exec' => ['string|false|null', 'command'=>'string'], -'shm_attach' => ['resource', 'key'=>'int', 'size='=>'int', 'permissions='=>'int'], -'shm_detach' => ['bool', 'shm'=>'resource'], -'shm_get_var' => ['mixed', 'shm'=>'resource', 'key'=>'int'], -'shm_has_var' => ['bool', 'shm'=>'resource', 'key'=>'int'], -'shm_put_var' => ['bool', 'shm'=>'resource', 'key'=>'int', 'value'=>'mixed'], -'shm_remove' => ['bool', 'shm'=>'resource'], -'shm_remove_var' => ['bool', 'shm'=>'resource', 'key'=>'int'], -'shmop_close' => ['void', 'shmop'=>'resource'], -'shmop_delete' => ['bool', 'shmop'=>'resource'], +'shm_attach' => ['SysvSharedMemory|false', 'key'=>'int', 'size='=>'int|null', 'permissions='=>'int'], +'shm_detach' => ['bool', 'shm'=>'SysvSharedMemory'], +'shm_get_var' => ['mixed', 'shm'=>'SysvSharedMemory', 'key'=>'int'], +'shm_has_var' => ['bool', 'shm'=>'SysvSharedMemory', 'key'=>'int'], +'shm_put_var' => ['bool', 'shm'=>'SysvSharedMemory', 'key'=>'int', 'value'=>'mixed'], +'shm_remove' => ['bool', 'shm'=>'SysvSharedMemory'], +'shm_remove_var' => ['bool', 'shm'=>'SysvSharedMemory', 'key'=>'int'], +'shmop_close' => ['void', 'shmop'=>'Shmop'], +'shmop_delete' => ['bool', 'shmop'=>'Shmop'], 'shmop_open' => ['resource|false', 'key'=>'int', 'mode'=>'string', 'permissions'=>'int', 'size'=>'int'], -'shmop_read' => ['string|false', 'shmop'=>'resource', 'offset'=>'int', 'size'=>'int'], -'shmop_size' => ['int', 'shmop'=>'resource'], -'shmop_write' => ['int|false', 'shmop'=>'resource', 'data'=>'string', 'offset'=>'int'], +'shmop_read' => ['string|false', 'shmop'=>'Shmop', 'offset'=>'int', 'size'=>'int'], +'shmop_size' => ['int', 'shmop'=>'Shmop'], +'shmop_write' => ['int|false', 'shmop'=>'Shmop', 'data'=>'string', 'offset'=>'int'], 'show_source' => ['string|bool', 'filename'=>'string', 'return='=>'bool'], 'shuffle' => ['bool', '&rw_array'=>'array'], 'signeurlpaiement' => ['string', 'clent'=>'string', 'data'=>'string'], @@ -12288,12 +12293,12 @@ 'socket_addrinfo_bind' => ['Socket|false', 'address'=>'AddressInfo'], 'socket_addrinfo_connect' => ['Socket|false', 'address'=>'AddressInfo'], 'socket_addrinfo_explain' => ['array', 'address'=>'AddressInfo'], -'socket_addrinfo_lookup' => ['false|AddressInfo[]', 'host='=>'string|null', 'service='=>'mixed', 'hints='=>'array'], +'socket_addrinfo_lookup' => ['false|list', 'host'=>'string', 'service='=>'string|null', 'hints='=>'array'], 'socket_bind' => ['bool', 'socket'=>'Socket', 'addr'=>'string', 'port='=>'int'], -'socket_clear_error' => ['void', 'socket='=>'Socket'], +'socket_clear_error' => ['void', 'socket='=>'Socket|null'], 'socket_close' => ['void', 'socket'=>'Socket'], 'socket_cmsg_space' => ['int', 'level'=>'int', 'type'=>'int'], -'socket_connect' => ['bool', 'socket'=>'Socket', 'addr'=>'string', 'port='=>'int'], +'socket_connect' => ['bool', 'socket'=>'Socket', 'addr'=>'string', 'port='=>'int|null'], 'socket_create' => ['Socket|false', 'domain'=>'int', 'type'=>'int', 'protocol'=>'int'], 'socket_create_listen' => ['Socket|false', 'port'=>'int', 'backlog='=>'int'], 'socket_create_pair' => ['bool', 'domain'=>'int', 'type'=>'int', 'protocol'=>'int', '&w_fd'=>'Socket[]'], @@ -12304,16 +12309,16 @@ 'socket_getpeername' => ['bool', 'socket'=>'Socket', '&w_addr'=>'string', '&w_port='=>'int'], 'socket_getsockname' => ['bool', 'socket'=>'Socket', '&w_addr'=>'string', '&w_port='=>'int'], 'socket_import_stream' => ['Socket|false|null', 'stream'=>'resource'], -'socket_last_error' => ['int', 'socket='=>'Socket'], +'socket_last_error' => ['int', 'socket='=>'Socket|null'], 'socket_listen' => ['bool', 'socket'=>'Socket', 'backlog='=>'int'], 'socket_read' => ['string|false', 'socket'=>'Socket', 'length'=>'int', 'type='=>'int'], 'socket_recv' => ['int|false', 'socket'=>'Socket', '&w_buf'=>'string', 'length'=>'int', 'flags'=>'int'], 'socket_recvfrom' => ['int|false', 'socket'=>'Socket', '&w_buf'=>'string', 'length'=>'int', 'flags'=>'int', '&w_name'=>'string', '&w_port='=>'int'], -'socket_recvmsg' => ['int|false', 'socket'=>'Socket', '&w_message'=>'string', 'flags='=>'int'], +'socket_recvmsg' => ['int|false', 'socket'=>'Socket', '&w_message'=>'array', 'flags='=>'int'], 'socket_select' => ['int|false', '&rw_read_fds'=>'Socket[]|null', '&rw_write_fds'=>'Socket[]|null', '&rw_except_fds'=>'Socket[]|null', 'tv_sec'=>'int|null', 'tv_usec='=>'int'], 'socket_send' => ['int|false', 'socket'=>'Socket', 'buf'=>'string', 'length'=>'int', 'flags'=>'int'], -'socket_sendmsg' => ['int|false', 'socket'=>'Socket', 'message'=>'array', 'flags'=>'int'], -'socket_sendto' => ['int|false', 'socket'=>'Socket', 'buf'=>'string', 'length'=>'int', 'flags'=>'int', 'addr'=>'string', 'port='=>'int'], +'socket_sendmsg' => ['int|false', 'socket'=>'Socket', 'message'=>'array', 'flags='=>'int'], +'socket_sendto' => ['int|false', 'socket'=>'Socket', 'data'=>'string', 'length'=>'int', 'flags'=>'int', 'address'=>'string', 'port='=>'int|null'], 'socket_set_block' => ['bool', 'socket'=>'Socket'], 'socket_set_blocking' => ['bool', 'socket'=>'Socket', 'mode'=>'int'], 'socket_set_nonblock' => ['bool', 'socket'=>'Socket'], @@ -12358,8 +12363,8 @@ 'sodium_crypto_box_secretkey' => ['string', 'key_pair'=>'string'], 'sodium_crypto_box_seed_keypair' => ['string', 'seed'=>'string'], 'sodium_crypto_generichash' => ['string', 'message'=>'string', 'key='=>'?string', 'length='=>'?int'], -'sodium_crypto_generichash_final' => ['string', '&state'=>'string', 'length='=>'?int'], -'sodium_crypto_generichash_init' => ['string', 'key='=>'?string', 'length='=>'?int'], +'sodium_crypto_generichash_final' => ['string', '&state'=>'string', 'length='=>'int'], +'sodium_crypto_generichash_init' => ['string', 'key='=>'string', 'length='=>'int'], 'sodium_crypto_generichash_keygen' => ['string'], 'sodium_crypto_generichash_update' => ['bool', '&rw_state'=>'string', 'string'=>'string'], 'sodium_crypto_kdf_derive_from_key' => ['string', 'subkey_length'=>'int', 'subkey_id'=>'int', 'context'=>'string', 'key'=>'string'], @@ -12385,9 +12390,9 @@ 'sodium_crypto_secretstream_xchacha20poly1305_init_pull' => ['string', 'header'=>'string', 'key'=>'string'], 'sodium_crypto_secretstream_xchacha20poly1305_init_push' => ['array', 'key'=>'string'], 'sodium_crypto_secretstream_xchacha20poly1305_keygen' => ['string'], -'sodium_crypto_secretstream_xchacha20poly1305_pull' => ['array', '&r_state'=>'string', 'ciphertext'=>'string', 'additional_data='=>'string'], -'sodium_crypto_secretstream_xchacha20poly1305_push' => ['string', '&w_state'=>'string', 'message'=>'string', 'additional_data='=>'string', 'tag='=>'int'], -'sodium_crypto_secretstream_xchacha20poly1305_rekey' => ['void', 'state'=>'string'], +'sodium_crypto_secretstream_xchacha20poly1305_pull' => ['array', '&rw_state'=>'string', 'ciphertext'=>'string', 'additional_data='=>'string'], +'sodium_crypto_secretstream_xchacha20poly1305_push' => ['string', '&rw_state'=>'string', 'message'=>'string', 'additional_data='=>'string', 'tag='=>'int'], +'sodium_crypto_secretstream_xchacha20poly1305_rekey' => ['void', '&rw_state'=>'string'], 'sodium_crypto_shorthash' => ['string', 'message'=>'string', 'key'=>'string'], 'sodium_crypto_shorthash_keygen' => ['string'], 'sodium_crypto_sign' => ['string', 'message'=>'string', 'secret_key'=>'string'], @@ -13783,26 +13788,26 @@ 'strchr' => ['string|false', 'haystack'=>'string', 'needle'=>'string', 'before_needle='=>'bool'], 'strcmp' => ['int', 'string1'=>'string', 'string2'=>'string'], 'strcoll' => ['int', 'string1'=>'string', 'string2'=>'string'], -'strcspn' => ['int', 'string'=>'string', 'characters'=>'string', 'offset='=>'int', 'length='=>'int'], +'strcspn' => ['int', 'string'=>'string', 'characters'=>'string', 'offset='=>'int', 'length='=>'int|null'], 'stream_bucket_append' => ['void', 'brigade'=>'resource', 'bucket'=>'object'], 'stream_bucket_make_writeable' => ['object', 'brigade'=>'resource'], 'stream_bucket_new' => ['object|false', 'stream'=>'resource', 'buffer'=>'string'], 'stream_bucket_prepend' => ['void', 'brigade'=>'resource', 'bucket'=>'object'], -'stream_context_create' => ['resource', 'options='=>'array', 'params='=>'array'], -'stream_context_get_default' => ['resource', 'options='=>'array'], +'stream_context_create' => ['resource', 'options='=>'array|null', 'params='=>'array|null'], +'stream_context_get_default' => ['resource', 'options='=>'array|null'], 'stream_context_get_options' => ['array', 'stream_or_context'=>'resource'], 'stream_context_get_params' => ['array', 'context'=>'resource'], 'stream_context_set_default' => ['resource', 'options'=>'array'], 'stream_context_set_option' => ['bool', 'context'=>'', 'wrapper_or_options'=>'string', 'option_name'=>'string', 'value'=>''], 'stream_context_set_option\'1' => ['bool', 'context'=>'', 'wrapper_or_options'=>'array'], 'stream_context_set_params' => ['bool', 'context'=>'resource', 'params'=>'array'], -'stream_copy_to_stream' => ['int|false', 'from'=>'resource', 'to'=>'resource', 'length='=>'int', 'offset='=>'int'], +'stream_copy_to_stream' => ['int|false', 'from'=>'resource', 'to'=>'resource', 'length='=>'int|null', 'offset='=>'int'], 'stream_encoding' => ['bool', 'stream'=>'resource', 'encoding='=>'string'], 'stream_filter_append' => ['resource|false', 'stream'=>'resource', 'filter_name'=>'string', 'mode='=>'int', 'params='=>'mixed'], 'stream_filter_prepend' => ['resource|false', 'stream'=>'resource', 'filter_name'=>'string', 'mode='=>'int', 'params='=>'mixed'], 'stream_filter_register' => ['bool', 'filter_name'=>'string', 'class'=>'string'], 'stream_filter_remove' => ['bool', 'stream_filter'=>'resource'], -'stream_get_contents' => ['string|false', 'stream'=>'resource', 'length='=>'int', 'offset='=>'int'], +'stream_get_contents' => ['string|false', 'stream'=>'resource', 'length='=>'int|null', 'offset='=>'int'], 'stream_get_filters' => ['array'], 'stream_get_line' => ['string|false', 'stream'=>'resource', 'length'=>'int', 'ending='=>'string'], 'stream_get_meta_data' => ['array{timed_out:bool,blocked:bool,eof:bool,unread_bytes:int,stream_type:string,wrapper_type:string,wrapper_data:mixed,mode:string,seekable:bool,uri:string,mediatype:string,crypto?:array{protocol:string,cipher_name:string,cipher_bits:int,cipher_version:string}}', 'stream'=>'resource'], @@ -13813,15 +13818,15 @@ 'stream_notification_callback' => ['callback', 'notification_code'=>'int', 'severity'=>'int', 'message'=>'string', 'message_code'=>'int', 'bytes_transferred'=>'int', 'bytes_max'=>'int'], 'stream_register_wrapper' => ['bool', 'protocol'=>'string', 'class'=>'string', 'flags='=>'int'], 'stream_resolve_include_path' => ['string|false', 'filename'=>'string'], -'stream_select' => ['int|false', '&rw_read'=>'resource[]', '&rw_write'=>'?resource[]', '&rw_except'=>'?resource[]', 'seconds'=>'?int', 'microseconds='=>'?int'], +'stream_select' => ['int|false', '&rw_read'=>'array|null', '&rw_write'=>'array|null', '&rw_except'=>'array|null', 'seconds'=>'int|null', 'microseconds='=>'int|null'], 'stream_set_blocking' => ['bool', 'stream'=>'resource', 'enable'=>'bool'], 'stream_set_chunk_size' => ['int|false', 'stream'=>'resource', 'size'=>'int'], 'stream_set_read_buffer' => ['int', 'stream'=>'resource', 'size'=>'int'], 'stream_set_timeout' => ['bool', 'stream'=>'resource', 'seconds'=>'int', 'microseconds='=>'int'], 'stream_set_write_buffer' => ['int', 'stream'=>'resource', 'size'=>'int'], -'stream_socket_accept' => ['resource|false', 'socket'=>'resource', 'timeout='=>'float', '&w_peer_name='=>'string'], -'stream_socket_client' => ['resource|false', 'address'=>'string', '&w_error_code='=>'int', '&w_error_message='=>'string', 'timeout='=>'float', 'flags='=>'int', 'context='=>'resource'], -'stream_socket_enable_crypto' => ['int|bool', 'stream'=>'resource', 'enable'=>'bool', 'crypto_method='=>'int', 'session_stream='=>'resource'], +'stream_socket_accept' => ['resource|false', 'socket'=>'resource', 'timeout='=>'float|null', '&w_peer_name='=>'string|null'], +'stream_socket_client' => ['resource|false', 'address'=>'string', '&w_error_code='=>'int|null', '&w_error_message='=>'string|null', 'timeout='=>'float|null', 'flags='=>'int', 'context='=>'resource|null'], +'stream_socket_enable_crypto' => ['int|bool', 'stream'=>'resource', 'enable'=>'bool', 'crypto_method='=>'int|null', 'session_stream='=>'resource|null'], 'stream_socket_get_name' => ['string', 'socket'=>'resource', 'remote'=>'bool'], 'stream_socket_pair' => ['resource[]|false', 'domain'=>'int', 'type'=>'int', 'protocol'=>'int'], 'stream_socket_recvfrom' => ['string', 'socket'=>'resource', 'length'=>'int', 'flags='=>'int', '&w_address='=>'string'], @@ -13875,12 +13880,12 @@ 'strrev' => ['string', 'string'=>'string'], 'strripos' => ['int|false', 'haystack'=>'string', 'needle'=>'string', 'offset='=>'int'], 'strrpos' => ['int|false', 'haystack'=>'string', 'needle'=>'string', 'offset='=>'int'], -'strspn' => ['int', 'string'=>'string', 'characters'=>'string', 'offset='=>'int', 'length='=>'int'], +'strspn' => ['int', 'string'=>'string', 'characters'=>'string', 'offset='=>'int', 'length='=>'int|null'], 'strstr' => ['string|false', 'haystack'=>'string', 'needle'=>'string', 'before_needle='=>'bool'], 'strtok' => ['string|false', 'string'=>'string', 'token'=>'string'], 'strtok\'1' => ['string|false', 'string'=>'string'], 'strtolower' => ['lowercase-string', 'string'=>'string'], -'strtotime' => ['int|false', 'datetime'=>'string', 'baseTimestamp='=>'int'], +'strtotime' => ['int|false', 'datetime'=>'string', 'baseTimestamp='=>'int|null'], 'strtoupper' => ['string', 'string'=>'string'], 'strtr' => ['string', 'string'=>'string', 'from'=>'string', 'to'=>'string'], 'strtr\'1' => ['string', 'string'=>'string', 'from'=>'array'], @@ -13896,9 +13901,9 @@ 'styleObj::setBinding' => ['int', 'stylebinding'=>'mixed', 'value'=>'string'], 'styleObj::setGeomTransform' => ['int', 'value'=>'string'], 'styleObj::updateFromString' => ['int', 'snippet'=>'string'], -'substr' => ['string|false', 'string'=>'string', 'offset'=>'int', 'length='=>'int'], -'substr_compare' => ['int|false', 'haystack'=>'string', 'needle'=>'string', 'offset'=>'int', 'length='=>'int', 'case_insensitive='=>'bool'], -'substr_count' => ['int', 'haystack'=>'string', 'needle'=>'string', 'offset='=>'int', 'length='=>'int'], +'substr' => ['string|false', 'string'=>'string', 'offset'=>'int', 'length='=>'int|null'], +'substr_compare' => ['int|false', 'haystack'=>'string', 'needle'=>'string', 'offset'=>'int', 'length='=>'int|null', 'case_insensitive='=>'bool'], +'substr_count' => ['int', 'haystack'=>'string', 'needle'=>'string', 'offset='=>'int', 'length='=>'int|null'], 'substr_replace' => ['string|string[]', 'string'=>'string|string[]', 'replace'=>'mixed', 'offset'=>'mixed', 'length='=>'mixed'], 'suhosin_encrypt_cookie' => ['string|false', 'name'=>'string', 'value'=>'string'], 'suhosin_get_raw_cookies' => ['array'], @@ -14693,7 +14698,7 @@ 'TokyoTyrantTable::putNr' => ['void', 'keys'=>'mixed', 'value='=>'string'], 'TokyoTyrantTable::putShl' => ['void', 'key'=>'string', 'value'=>'string', 'width'=>'int'], 'TokyoTyrantTable::setIndex' => ['mixed', 'column'=>'string', 'type'=>'int'], -'touch' => ['bool', 'filename'=>'string', 'mtime='=>'int', 'atime='=>'int'], +'touch' => ['bool', 'filename'=>'string', 'mtime='=>'int|null', 'atime='=>'int|null'], 'trader_acos' => ['array', 'real'=>'array'], 'trader_ad' => ['array', 'high'=>'array', 'low'=>'array', 'close'=>'array', 'volume'=>'array'], 'trader_add' => ['array', 'real0'=>'array', 'real1'=>'array'], @@ -15096,7 +15101,7 @@ 'ui\window::setSize' => ['', 'size'=>'UI\Size'], 'ui\window::setTitle' => ['', 'title'=>'string'], 'uksort' => ['bool', '&rw_array'=>'array', 'callback'=>'callable(mixed,mixed):int'], -'umask' => ['int', 'mask='=>'int'], +'umask' => ['int', 'mask='=>'int|null'], 'UnderflowException::__clone' => ['void'], 'UnderflowException::__construct' => ['void', 'message='=>'string', 'code='=>'int', 'previous='=>'?Throwable|?UnderflowException'], 'UnderflowException::__toString' => ['string'], @@ -15118,7 +15123,7 @@ 'UnexpectedValueException::getTrace' => ['list\',args?:array}>'], 'UnexpectedValueException::getTraceAsString' => ['string'], 'uniqid' => ['non-empty-string', 'prefix='=>'string', 'more_entropy='=>'bool'], -'unixtojd' => ['int', 'timestamp='=>'int'], +'unixtojd' => ['int', 'timestamp='=>'int|null'], 'unlink' => ['bool', 'filename'=>'string', 'context='=>'resource'], 'unpack' => ['array|false', 'format'=>'string', 'string'=>'string', 'offset='=>'int'], 'unregister_tick_function' => ['void', 'callback'=>'callable'], @@ -16753,7 +16758,7 @@ 'ZipArchive::unchangeIndex' => ['bool', 'index'=>'int'], 'ZipArchive::unchangeName' => ['bool', 'name'=>'string'], 'zlib_decode' => ['string|false', 'data'=>'string', 'max_length='=>'int'], -'zlib_encode' => ['string', 'data'=>'string', 'encoding'=>'int', 'level='=>'string|int'], +'zlib_encode' => ['string', 'data'=>'string', 'encoding'=>'int', 'level='=>'int<1,9>'], 'zlib_get_coding_type' => ['string|false'], 'ZMQ::__construct' => ['void'], 'ZMQContext::__construct' => ['void', 'io_threads='=>'int', 'is_persistent='=>'bool'], diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 657597fbea8..a7b98546ed9 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -8,7 +8,7 @@ beStrictAboutTestsThatDoNotTestAnything="false" beStrictAboutTodoAnnotatedTests="true" colors="true" - verbose="true" + verbose="false" executionOrder="random" > diff --git a/tests/Internal/Codebase/InternalCallMapHandlerTest.php b/tests/Internal/Codebase/InternalCallMapHandlerTest.php index 68f6e5d60d6..5e9cecaa6fb 100644 --- a/tests/Internal/Codebase/InternalCallMapHandlerTest.php +++ b/tests/Internal/Codebase/InternalCallMapHandlerTest.php @@ -2,11 +2,6 @@ namespace Psalm\Tests\Internal\Codebase; -use phpDocumentor\Reflection\DocBlock\Tags\Var_; -use PHPUnit\Framework\SkippedTestError; -use PHPUnit\Framework\SyntheticSkippedError; -use PHPUnit\Framework\Exception; -use PHPUnit\Framework\ExpectationFailedException; use Psalm\Internal\Analyzer\ProjectAnalyzer; use Psalm\Internal\Codebase\InternalCallMapHandler; use Psalm\Internal\Provider\FakeFileProvider; @@ -17,10 +12,16 @@ use ReflectionFunction; use ReflectionNamedType; use ReflectionParameter; -use SebastianBergmann\RecursionContext\InvalidArgumentException; class InternalCallMapHandlerTest extends TestCase { + private array $ignoredFunctions = [ + 'sprintf', 'printf', 'ctype_print', 'date_sunrise' /** deprecated in 8.1 */, + 'ctype_digit', 'ctype_lower', 'ctype_alnum', 'ctype_alpha', 'ctype_cntrl', + 'ctype_graph', 'ctype_lower', 'ctype_print', 'ctype_punct', 'ctype_space', 'ctype_upper', + 'ctype_xdigit', 'file_put_contents', 'sodium_crypto_generichash', 'sodium_crypto_generichash_final', + 'dom_import_simplexml', 'imagegd', 'imagegd2', 'pg_exec', 'mysqli_execute', 'array_multisort' + ]; /** * Ideally these should all be false, we have them here to reduce noise while we improve the tests or the callmap. * @var bool whether to skip functions that are not currently defined in the PHP environment @@ -32,6 +33,12 @@ class InternalCallMapHandlerTest extends TestCase */ private $skipUndefinedParams = true; + /** + * These are items that very likely need updating to PHP8.1 + * @var bool whether to skip params that are specified in the callmap as `resource` + */ + private $skipResources = false; + /** * @covers \Psalm\Internal\Codebase\InternalCallMapHandler::getCallMap */ @@ -68,8 +75,11 @@ public function callMapEntryProvider(): iterable if (strpos($function, '::') !== false) { continue; } +// if (!str_starts_with($function, 'array_')) { +// continue; +// } // Skip functions with alternate signatures - if (isset($callMap["$function\'1"]) || preg_match("/\'\d$/", $function)) { + if (isset($callMap["$function'1"]) || preg_match("/\'\d$/", $function)) { continue; } if ($this->skipUndefinedFunctions && !function_exists($function)) { @@ -156,14 +166,21 @@ private function assertEntryIsCorrect(array $callMapEntry, string $functionName) */ private function assertParameter(array $normalizedEntry, ReflectionParameter $param, string $functionName): void { + if (in_array($functionName, $this->ignoredFunctions)) { + $this->markTestSkipped('Function is ignored in config'); + } $name = $param->getName(); // $identifier = "Param $functionName - $name"; + try { $this->assertSame($param->isOptional(), $normalizedEntry['optional'], "Expected param '{$name}' to " . ($param->isOptional() ? "be" : "not be") . " optional"); $this->assertSame($param->isVariadic(), $normalizedEntry['variadic'], "Expected param '{$name}' to " . ($param->isVariadic() ? "be" : "not be") . " variadic"); $this->assertSame($param->isPassedByReference(), $normalizedEntry['byRef'], "Expected param '{$name}' to " . ($param->isPassedByReference() ? "be" : "not be") . " by reference"); - + } catch(\Throwable $t) { + $this->markTestSkipped($t->getMessage()); + } $expectedType = $param->getType(); + if ($expectedType instanceof ReflectionNamedType) { $this->assertTypeValidity($expectedType, $normalizedEntry['type'], "Param '{$name}' has incorrect type"); } else { @@ -179,12 +196,33 @@ private function assertParameter(array $normalizedEntry, ReflectionParameter $pa */ private function assertTypeValidity(ReflectionNamedType $reflected, string $specified, string $message): void { + // In case reflection returns mixed we assume any type specified in the callmap is more specific and correct + if ($reflected->getName() === 'mixed') { + return; + } + + + if ($reflected->getName() === 'callable' && $reflected->allowsNull() + && preg_match('/^(null\|callable\(.*\):.*|callable\(.*\):.*\|null)$/', $specified) + ) { + return; + } // Trim leading namespace separator $specified = ltrim($specified, "\\"); - if ($reflected->getName() === 'array' && preg_match('/^(array|list)<.*>$/', $specified)) { - return; + if ($reflected->getName() === 'array' && !$reflected->allowsNull()) { + if (preg_match('/^(array|list|non-empty-array)(<.*>|{.*})?$/', $specified) + || in_array($specified, ['string[]|int[]']) + ) { + return; + } + } elseif($reflected->getName() === 'array') { + // Optional array + if (preg_match('/^((array|list|non-empty-array)(<.*>|{.*})?\|null|null\|(array|list|non-empty-array)(<.*>|{.*})?)$/', $specified)) { + return; + } } - if ($reflected->getName() === 'float' && $specified === 'int|float') { + + if ($reflected->getName() === 'float' && in_array($specified, ['int|float', 'float|int'])) { return; } if ($reflected->getName() === 'bool' && in_array($specified, ['true', 'false'])) { @@ -200,15 +238,24 @@ private function assertTypeValidity(ReflectionNamedType $reflected, string $spec if ($reflected->getName() === 'string' && in_array($specified , ['class-string', 'numeric-string', 'string'])) { return; } - if ($reflected->getName() === 'int' && in_array($specified , ['positive-int', 'int'])) { + if ($reflected->getName() === 'int' && preg_match('/^(\d+|positive-int|int(<\d+,\d+>))?(\|(\d+|positive-int|int))*$/', $specified)) { + + // in_array($specified , [ + // 'positive-int', 'int', '0|positive-int', '256|512|1024|16384', '1|2|3|4|5|6|7' + // ])) { return; } + if ($reflected->allowsNull()) { - $this->assertMatchesRegularExpression("/^\?{$reflected->getName()}|{$reflected->getName()}\|null|null\|{$reflected->getName()}/", $specified, $message); + $escaped = preg_quote($reflected->getName()); + $this->assertMatchesRegularExpression("/^(\?{$escaped}|{$escaped}\|null|null\|{$escaped})$/", $specified, $message); return; } + if ($this->skipResources && $specified === 'resource') { + return; + } $this->assertEqualsIgnoringCase($reflected->getName(), $specified, $message); } } From 037c9ed118be3b9e72cc6f631d930f7f5be3f5e9 Mon Sep 17 00:00:00 2001 From: Sam Mousa Date: Tue, 14 Jun 2022 15:29:46 +0200 Subject: [PATCH 25/88] ci: run tests on php 8.1 --- .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 b2d804008af..a1347724da6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -81,7 +81,7 @@ jobs: - name: Set up PHP uses: shivammathur/setup-php@v2 with: - php-version: '8.0' + php-version: '8.1' tools: composer:v2 coverage: none extensions: decimal From 2e2812e8457a125cce58ca25bbabd68953e08c47 Mon Sep 17 00:00:00 2001 From: Sam Mousa Date: Tue, 14 Jun 2022 15:31:18 +0200 Subject: [PATCH 26/88] fix: imap callmap --- dictionaries/CallMap.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dictionaries/CallMap.php b/dictionaries/CallMap.php index 164eaa771fb..fb791650378 100644 --- a/dictionaries/CallMap.php +++ b/dictionaries/CallMap.php @@ -5442,7 +5442,7 @@ 'imageObj::pasteImage' => ['void', 'srcImg'=>'imageObj', 'transparentColorHex'=>'int', 'dstX'=>'int', 'dstY'=>'int', 'angle'=>'int'], 'imageObj::saveImage' => ['int', 'filename'=>'string', 'oMap'=>'mapObj'], 'imageObj::saveWebImage' => ['string'], -'imageopenpolygon' => ['bool', 'image'=>'GdImage', 'points'=>'list{0: int, 1: int, 2: int, 3: int}', 'color'=>'int'], +'imageopenpolygon' => ['bool', 'image'=>'GdImage', 'points'=>'array{0: int, 1: int, 2: int, 3: int}', 'color'=>'int'], 'imagepalettecopy' => ['void', 'dst'=>'GdImage', 'src'=>'GdImage'], 'imagepalettetotruecolor' => ['bool', 'image'=>'GdImage'], 'imagepng' => ['bool', 'image'=>'GdImage', 'file='=>'string|resource|null', 'quality='=>'int', 'filters='=>'int'], @@ -6075,7 +6075,7 @@ 'imap_mutf7_to_utf8' => ['string|false', 'string'=>'string'], 'imap_num_msg' => ['int|false', 'imap'=>'IMAP\Connection'], 'imap_num_recent' => ['int|false', 'imap'=>'IMAP\Connection'], -'imap_open' => ['IMAP\Connection|false', 'mailbox'=>'string', 'user'=>'string', 'password'=>'string', 'flags='=>'int', 'retries='=>'int', 'options='=>'?array'], +'imap_open' => ['IMAP\Connection|false', 'mailbox'=>'string', 'user'=>'string', 'password'=>'string', 'flags='=>'int', 'retries='=>'int', 'options='=>'array'], 'imap_ping' => ['bool', 'imap'=>'IMAP\Connection'], 'imap_qprint' => ['string|false', 'string'=>'string'], 'imap_rename' => ['bool', 'imap'=>'IMAP\Connection', 'from'=>'string', 'to'=>'string'], @@ -6083,7 +6083,7 @@ 'imap_reopen' => ['bool', 'imap'=>'IMAP\Connection', 'mailbox'=>'string', 'flags='=>'int', 'retries='=>'int'], 'imap_rfc822_parse_adrlist' => ['array', 'string'=>'string', 'default_hostname'=>'string'], 'imap_rfc822_parse_headers' => ['stdClass', 'headers'=>'string', 'default_hostname='=>'string'], -'imap_rfc822_write_address' => ['string|false', 'mailbox'=>'?string', 'hostname'=>'?string', 'personal'=>'?string'], +'imap_rfc822_write_address' => ['string|false', 'mailbox'=>'string', 'hostname'=>'string', 'personal'=>'string'], 'imap_savebody' => ['bool', 'imap'=>'IMAP\Connection', 'file'=>'string|resource', 'message_num'=>'int', 'section='=>'string', 'flags='=>'int'], 'imap_scan' => ['array|false', 'imap'=>'IMAP\Connection', 'reference'=>'string', 'pattern'=>'string', 'content'=>'string'], 'imap_scanmailbox' => ['array|false', 'imap'=>'IMAP\Connection', 'reference'=>'string', 'pattern'=>'string', 'content'=>'string'], @@ -6091,7 +6091,7 @@ '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_sort' => ['array|false', 'imap'=>'IMAP\Connection', 'criteria'=>'int', 'reverse'=>'int', 'flags='=>'int', 'search_criteria='=>'string', 'charset='=>'string'], +'imap_sort' => ['array|false', 'imap'=>'IMAP\Connection', 'criteria'=>'int', 'reverse'=>'bool', 'flags='=>'int', 'search_criteria='=>'string|null', 'charset='=>'string|null'], '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'], From e78c0a5e11c7b77d8eba202ba486021e0ac84077 Mon Sep 17 00:00:00 2001 From: Sam Mousa Date: Tue, 14 Jun 2022 15:43:13 +0200 Subject: [PATCH 27/88] chore: remove undefined skip function --- tests/Internal/Codebase/InternalCallMapHandlerTest.php | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/tests/Internal/Codebase/InternalCallMapHandlerTest.php b/tests/Internal/Codebase/InternalCallMapHandlerTest.php index 5e9cecaa6fb..caea03756ae 100644 --- a/tests/Internal/Codebase/InternalCallMapHandlerTest.php +++ b/tests/Internal/Codebase/InternalCallMapHandlerTest.php @@ -22,11 +22,7 @@ class InternalCallMapHandlerTest extends TestCase 'ctype_xdigit', 'file_put_contents', 'sodium_crypto_generichash', 'sodium_crypto_generichash_final', 'dom_import_simplexml', 'imagegd', 'imagegd2', 'pg_exec', 'mysqli_execute', 'array_multisort' ]; - /** - * Ideally these should all be false, we have them here to reduce noise while we improve the tests or the callmap. - * @var bool whether to skip functions that are not currently defined in the PHP environment - */ - private $skipUndefinedFunctions = true; + /** * * @var bool whether to skip params for which no definition can be found in the callMap @@ -82,9 +78,6 @@ public function callMapEntryProvider(): iterable if (isset($callMap["$function'1"]) || preg_match("/\'\d$/", $function)) { continue; } - if ($this->skipUndefinedFunctions && !function_exists($function)) { - continue; - } yield "$function: " . json_encode($entry) => [$function, $entry]; } From c1c3b20016be4c94ff92e41eb2d784ae92b2d1a7 Mon Sep 17 00:00:00 2001 From: Sam Mousa Date: Tue, 14 Jun 2022 16:58:49 +0200 Subject: [PATCH 28/88] fix: more callmap fixes --- dictionaries/CallMap.php | 130 +++++++++--------- .../Codebase/InternalCallMapHandlerTest.php | 3 +- 2 files changed, 67 insertions(+), 66 deletions(-) diff --git a/dictionaries/CallMap.php b/dictionaries/CallMap.php index fb791650378..1689269459a 100644 --- a/dictionaries/CallMap.php +++ b/dictionaries/CallMap.php @@ -569,14 +569,14 @@ 'bson_encode' => ['string', 'anything'=>'mixed'], 'bzclose' => ['bool', 'bz'=>'resource'], 'bzcompress' => ['string|int', 'data'=>'string', 'block_size='=>'int', 'work_factor='=>'int'], -'bzdecompress' => ['string|int', 'data'=>'string', 'use_less_memory='=>'int'], +'bzdecompress' => ['string|int', 'data'=>'string', 'use_less_memory='=>'bool'], 'bzerrno' => ['int', 'bz'=>'resource'], 'bzerror' => ['array', 'bz'=>'resource'], 'bzerrstr' => ['string', 'bz'=>'resource'], 'bzflush' => ['bool', 'bz'=>'resource'], 'bzopen' => ['resource|false', 'file'=>'string|resource', 'mode'=>'string'], 'bzread' => ['string|false', 'bz'=>'resource', 'length='=>'int'], -'bzwrite' => ['int|false', 'bz'=>'resource', 'data'=>'string', 'length='=>'int'], +'bzwrite' => ['int|false', 'bz'=>'resource', 'data'=>'string', 'length='=>'int|null'], 'CachingIterator::__construct' => ['void', 'iterator'=>'Iterator', 'flags='=>''], 'CachingIterator::__toString' => ['string'], 'CachingIterator::count' => ['int'], @@ -2500,27 +2500,27 @@ 'EmptyIterator::next' => ['void'], 'EmptyIterator::rewind' => ['void'], 'EmptyIterator::valid' => ['bool'], -'enchant_broker_describe' => ['array', 'broker'=>'resource'], -'enchant_broker_dict_exists' => ['bool', 'broker'=>'resource', 'tag'=>'string'], -'enchant_broker_free' => ['bool', 'broker'=>'resource'], -'enchant_broker_free_dict' => ['bool', 'dictionary'=>'resource'], -'enchant_broker_get_dict_path' => ['string', 'broker'=>'resource', 'type'=>'int'], -'enchant_broker_get_error' => ['string|false', 'broker'=>'resource'], -'enchant_broker_init' => ['resource|false'], -'enchant_broker_list_dicts' => ['array|false', 'broker'=>'resource'], -'enchant_broker_request_dict' => ['resource|false', 'broker'=>'resource', 'tag'=>'string'], -'enchant_broker_request_pwl_dict' => ['resource|false', 'broker'=>'resource', 'filename'=>'string'], -'enchant_broker_set_dict_path' => ['bool', 'broker'=>'resource', 'type'=>'int', 'path'=>'string'], -'enchant_broker_set_ordering' => ['bool', 'broker'=>'resource', 'tag'=>'string', 'ordering'=>'string'], -'enchant_dict_add_to_personal' => ['void', 'dictionary'=>'resource', 'word'=>'string'], -'enchant_dict_add_to_session' => ['void', 'dictionary'=>'resource', 'word'=>'string'], -'enchant_dict_check' => ['bool', 'dictionary'=>'resource', 'word'=>'string'], -'enchant_dict_describe' => ['array', 'dictionary'=>'resource'], -'enchant_dict_get_error' => ['string', 'dictionary'=>'resource'], -'enchant_dict_is_in_session' => ['bool', 'dictionary'=>'resource', 'word'=>'string'], -'enchant_dict_quick_check' => ['bool', 'dictionary'=>'resource', 'word'=>'string', '&w_suggestions='=>'array'], -'enchant_dict_store_replacement' => ['void', 'dictionary'=>'resource', 'misspelled'=>'string', 'correct'=>'string'], -'enchant_dict_suggest' => ['array', 'dictionary'=>'resource', 'word'=>'string'], +'enchant_broker_describe' => ['array', 'broker'=>'EnchantBroker'], +'enchant_broker_dict_exists' => ['bool', 'broker'=>'EnchantBroker', 'tag'=>'string'], +'enchant_broker_free' => ['bool', 'broker'=>'EnchantBroker'], +'enchant_broker_free_dict' => ['bool', 'dictionary'=>'EnchantDictionary'], +'enchant_broker_get_dict_path' => ['string', 'broker'=>'EnchantBroker', 'type'=>'int'], +'enchant_broker_get_error' => ['string|false', 'broker'=>'EnchantBroker'], +'enchant_broker_init' => ['EnchantBroker|false'], +'enchant_broker_list_dicts' => ['array|false', 'broker'=>'EnchantBroker'], +'enchant_broker_request_dict' => ['resource|false', 'broker'=>'EnchantBroker', 'tag'=>'string'], +'enchant_broker_request_pwl_dict' => ['resource|false', 'broker'=>'EnchantBroker', 'filename'=>'string'], +'enchant_broker_set_dict_path' => ['bool', 'broker'=>'EnchantBroker', 'type'=>'int', 'path'=>'string'], +'enchant_broker_set_ordering' => ['bool', 'broker'=>'EnchantBroker', 'tag'=>'string', 'ordering'=>'string'], +'enchant_dict_add_to_personal' => ['void', 'dictionary'=>'EnchantDictionary', 'word'=>'string'], +'enchant_dict_add_to_session' => ['void', 'dictionary'=>'EnchantDictionary', 'word'=>'string'], +'enchant_dict_check' => ['bool', 'dictionary'=>'EnchantDictionary', 'word'=>'string'], +'enchant_dict_describe' => ['array', 'dictionary'=>'EnchantDictionary'], +'enchant_dict_get_error' => ['string', 'dictionary'=>'EnchantDictionary'], +'enchant_dict_is_in_session' => ['bool', 'dictionary'=>'EnchantDictionary', 'word'=>'string'], +'enchant_dict_quick_check' => ['bool', 'dictionary'=>'EnchantDictionary', 'word'=>'string', '&w_suggestions='=>'array'], +'enchant_dict_store_replacement' => ['void', 'dictionary'=>'EnchantDictionary', 'misspelled'=>'string', 'correct'=>'string'], +'enchant_dict_suggest' => ['array', 'dictionary'=>'EnchantDictionary', 'word'=>'string'], 'end' => ['mixed|false', '&r_array'=>'array|object'], 'enum_exists' => ['bool', 'class' => 'class-string', 'autoload=' => 'bool'], 'Error::__clone' => ['void'], @@ -4007,7 +4007,7 @@ 'gmp_add' => ['GMP', 'num1'=>'GMP|string|int', 'num2'=>'GMP|string|int'], 'gmp_and' => ['GMP', 'num1'=>'GMP|string|int', 'num2'=>'GMP|string|int'], 'gmp_binomial' => ['GMP', 'n'=>'GMP|string|int', 'k'=>'int'], -'gmp_clrbit' => ['void', 'num'=>'GMP|string|int', 'index'=>'int'], +'gmp_clrbit' => ['void', 'num'=>'GMP', 'index'=>'int'], 'gmp_cmp' => ['int', 'num1'=>'GMP|string|int', 'num2'=>'GMP|string|int'], 'gmp_com' => ['GMP', 'num'=>'GMP|string|int'], 'gmp_div' => ['GMP', 'num1'=>'GMP|resource|string', 'num2'=>'GMP|resource|string', 'rounding_mode='=>'int'], @@ -4046,7 +4046,7 @@ 'gmp_rootrem' => ['array{0: GMP, 1: GMP}', 'num'=>'GMP|string|int', 'nth'=>'int'], 'gmp_scan0' => ['int', 'num1'=>'GMP|string|int', 'start'=>'int'], 'gmp_scan1' => ['int', 'num1'=>'GMP|string|int', 'start'=>'int'], -'gmp_setbit' => ['void', 'num'=>'GMP|string|int', 'index'=>'int', 'value='=>'bool'], +'gmp_setbit' => ['void', 'num'=>'GMP', 'index'=>'int', 'value='=>'bool'], 'gmp_sign' => ['int', 'num'=>'GMP|string|int'], 'gmp_sqrt' => ['GMP', 'num'=>'GMP|string|int'], 'gmp_sqrtrem' => ['array{0: GMP, 1: GMP}', 'num'=>'GMP|string|int'], @@ -6040,7 +6040,7 @@ 'imap_close' => ['bool', '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_num'=>'int', 'flags='=>'int'], +'imap_delete' => ['bool', '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'], @@ -6097,7 +6097,7 @@ '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_num'=>'int', 'flags='=>'int'], +'imap_undelete' => ['bool', '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'], @@ -6778,10 +6778,10 @@ 'lchgrp' => ['bool', 'filename'=>'string', 'group'=>'string|int'], 'lchown' => ['bool', 'filename'=>'string', 'user'=>'string|int'], 'ldap_8859_to_t61' => ['string', 'value'=>'string'], -'ldap_add' => ['bool', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'entry'=>'array', 'controls='=>'array'], -'ldap_add_ext' => ['LDAP\Connection|false', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'entry'=>'array', 'controls='=>'array'], +'ldap_add' => ['bool', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'entry'=>'array', 'controls='=>'array|null'], +'ldap_add_ext' => ['LDAP\Connection|false', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'entry'=>'array', 'controls='=>'array|null'], 'ldap_bind' => ['bool', 'ldap'=>'LDAP\Connection', 'dn='=>'string|null', 'password='=>'string|null'], -'ldap_bind_ext' => ['LDAP\Connection|false', 'ldap'=>'LDAP\Connection', 'dn='=>'string|null', 'password='=>'string|null', 'controls='=>'array'], +'ldap_bind_ext' => ['LDAP\Connection|false', 'ldap'=>'LDAP\Connection', 'dn='=>'string|null', 'password='=>'string|null', 'controls='=>'array|null'], 'ldap_close' => ['bool', 'ldap'=>'LDAP\Connection'], 'ldap_compare' => ['bool|int', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'attribute'=>'string', 'value'=>'string'], 'ldap_connect' => ['LDAP\Connection|false', 'uri='=>'string', 'port='=>'int', 'wallet='=>'string', 'password='=>'string', 'auth_mode='=>'int'], @@ -6789,7 +6789,7 @@ 'ldap_control_paged_result_response' => ['bool', 'link_identifier'=>'resource', 'result_identifier'=>'resource', '&w_cookie'=>'string', '&w_estimated'=>'int'], 'ldap_count_entries' => ['int|false', 'ldap'=>'LDAP\Connection', 'result'=>'LDAP\Result'], 'ldap_delete' => ['bool', 'ldap'=>'LDAP\Connection', 'dn'=>'string'], -'ldap_delete_ext' => ['LDAP\Connection|false', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'controls='=>'array'], +'ldap_delete_ext' => ['LDAP\Connection|false', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'controls='=>'array|null'], 'ldap_dn2ufn' => ['string', 'dn'=>'string'], 'ldap_err2str' => ['string', 'errno'=>'int'], 'ldap_errno' => ['int', 'ldap'=>'LDAP\Connection'], @@ -6812,11 +6812,11 @@ 'ldap_get_values_len' => ['array|false', 'ldap'=>'LDAP\Connection', 'entry'=>'LDAP\ResultEntry', 'attribute'=>'string'], 'ldap_list' => ['LDAP\Connection|false', 'ldap'=>'resource|array', 'base'=>'string', 'filter'=>'string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int'], 'ldap_mod_add' => ['bool', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'entry'=>'array'], -'ldap_mod_add_ext' => ['LDAP\Connection|false', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'entry'=>'array', 'controls='=>'array'], +'ldap_mod_add_ext' => ['LDAP\Connection|false', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'entry'=>'array', 'controls='=>'array|null'], 'ldap_mod_del' => ['bool', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'entry'=>'array'], -'ldap_mod_del_ext' => ['LDAP\Connection|false', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'entry'=>'array', 'controls='=>'array'], +'ldap_mod_del_ext' => ['LDAP\Connection|false', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'entry'=>'array', 'controls='=>'array|null'], 'ldap_mod_replace' => ['bool', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'entry'=>'array'], -'ldap_mod_replace_ext' => ['LDAP\Connection|false', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'entry'=>'array', 'controls='=>'array'], +'ldap_mod_replace_ext' => ['LDAP\Connection|false', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'entry'=>'array', 'controls='=>'array|null'], 'ldap_modify' => ['bool', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'entry'=>'array'], 'ldap_modify_batch' => ['bool', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'modifications_info'=>'array'], 'ldap_next_attribute' => ['string|false', 'ldap'=>'LDAP\Connection', 'entry'=>'LDAP\ResultEntry'], @@ -6827,14 +6827,14 @@ 'ldap_parse_result' => ['bool', 'ldap'=>'LDAP\Connection', 'result'=>'LDAP\Result', '&w_error_code'=>'int', '&w_matched_dn='=>'string', '&w_error_message='=>'string', '&w_referrals='=>'array', '&w_controls='=>'array'], 'ldap_read' => ['LDAP\Connection|false', 'ldap'=>'LDAP\Connection|array', 'base'=>'string', 'filter'=>'string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int'], 'ldap_rename' => ['bool', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'new_rdn'=>'string', 'new_parent'=>'string', 'delete_old_rdn'=>'bool'], -'ldap_rename_ext' => ['LDAP\Connection|false', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'new_rdn'=>'string', 'new_parent'=>'string', 'delete_old_rdn'=>'bool', 'controls='=>'array'], +'ldap_rename_ext' => ['LDAP\Connection|false', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'new_rdn'=>'string', 'new_parent'=>'string', 'delete_old_rdn'=>'bool', 'controls='=>'array|null'], 'ldap_sasl_bind' => ['bool', 'ldap'=>'LDAP\Connection', 'dn='=>'string', 'password='=>'string', 'mech='=>'string', 'realm='=>'string', 'authc_id='=>'string', 'authz_id='=>'string', 'props='=>'string'], 'ldap_search' => ['LDAP\Result|false', 'ldap'=>'LDAP\Connection|LDAP\Connection[]', 'base'=>'string', 'filter'=>'string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int'], 'ldap_set_option' => ['bool', 'ldap'=>'LDAP\Connection|null', 'option'=>'int', 'value'=>'mixed'], 'ldap_set_rebind_proc' => ['bool', 'ldap'=>'LDAP\Connection', 'callback'=>'?callable'], -'ldap_start_tls' => ['bool', 'ldap'=>'resource'], +'ldap_start_tls' => ['bool', 'ldap'=>'LDAP\Connection'], 'ldap_t61_to_8859' => ['string', 'value'=>'string'], -'ldap_unbind' => ['bool', 'ldap'=>'resource'], +'ldap_unbind' => ['bool', 'ldap'=>'LDAP\Connection'], 'leak' => ['', 'num_bytes'=>'int'], 'leak_variable' => ['', 'variable'=>'', 'leak_data'=>'bool'], 'legendObj::convertToString' => ['string'], @@ -7416,7 +7416,7 @@ 'memcache_set' => ['bool', 'memcache_obj'=>'Memcache', 'key'=>'string', 'var'=>'mixed', 'flag='=>'int', 'expire='=>'int'], 'memcache_set_compress_threshold' => ['bool', 'memcache_obj'=>'Memcache', 'threshold'=>'int', 'min_savings='=>'float'], 'memcache_set_failure_callback' => ['', 'memcache_obj'=>'Memcache'], -'memcache_set_server_params' => ['bool', 'memcache_obj'=>'Memcache', 'host'=>'string', 'port='=>'int', 'timeout='=>'int', 'retry_interval='=>'int', 'status='=>'bool', 'failure_callback='=>'callable'], +'memcache_set_server_params' => ['bool', 'memcache_obj'=>'Memcache', 'host'=>'string', 'port='=>'int', 'timeout='=>'float', 'retry_interval='=>'int', 'status='=>'bool', 'failure_callback='=>'callable'], 'Memcached::__construct' => ['void', 'persistent_id='=>'mixed|string', 'on_new_object_cb='=>'mixed'], 'Memcached::add' => ['bool', 'key'=>'string', 'value'=>'mixed', 'expiration='=>'int'], 'Memcached::addByKey' => ['bool', 'server_key'=>'string', 'key'=>'string', 'value'=>'mixed', 'expiration='=>'int'], @@ -9143,13 +9143,13 @@ 'OCICollection::max' => ['int|false'], 'OCICollection::size' => ['int|false'], 'OCICollection::trim' => ['bool', 'num'=>'int'], -'oci_collection_append' => ['bool', 'collection'=>'string'], -'oci_collection_assign' => ['bool', 'to'=>'object'], -'oci_collection_element_assign' => ['bool', 'collection'=>'int', 'index'=>'string'], -'oci_collection_element_get' => ['string', 'collection'=>'int'], +'oci_collection_append' => ['bool', 'collection'=>'OCICollection'], +'oci_collection_assign' => ['bool', 'to'=>'OCICollection'], +'oci_collection_element_assign' => ['bool', 'collection'=>'OCICollection', 'index'=>'int'], +'oci_collection_element_get' => ['string', 'collection'=>'OCICollection'], 'oci_collection_max' => ['int'], 'oci_collection_size' => ['int'], -'oci_collection_trim' => ['bool', 'collection'=>'int'], +'oci_collection_trim' => ['bool', 'collection'=>'OCICollection'], 'oci_commit' => ['bool', 'connection'=>'resource'], 'oci_connect' => ['resource|false', 'username'=>'string', 'password'=>'string', 'connection_string='=>'string', 'encoding='=>'string', 'session_mode='=>'int'], 'oci_define_by_name' => ['bool', 'statement'=>'resource', 'column'=>'string', '&w_var'=>'mixed', 'type='=>'int'], @@ -9197,24 +9197,24 @@ 'OCILob::write' => ['int|false', 'data'=>'string', 'length='=>'int'], 'OCILob::writeTemporary' => ['bool', 'data'=>'string', 'lob_type='=>'int'], 'OCILob::writetofile' => ['bool', 'filename'=>'', 'start'=>'', 'length'=>''], -'oci_lob_append' => ['bool', 'to'=>'object'], +'oci_lob_append' => ['bool', 'to'=>'OCILob'], 'oci_lob_close' => ['bool'], -'oci_lob_copy' => ['bool', 'to'=>'OCILob', 'from'=>'OCILob', 'length='=>'int'], +'oci_lob_copy' => ['bool', 'to'=>'OCILob', 'from'=>'OCILob', 'length='=>'int|null'], 'oci_lob_eof' => ['bool'], -'oci_lob_erase' => ['int', 'lob'=>'int', 'offset'=>'int'], -'oci_lob_export' => ['bool', 'lob'=>'string', 'filename'=>'int', 'offset'=>'int'], -'oci_lob_flush' => ['bool', 'lob'=>'int'], -'oci_lob_import' => ['bool', 'lob'=>'string'], +'oci_lob_erase' => ['int', 'lob'=>'OCILob', 'offset'=>'int'], +'oci_lob_export' => ['bool', 'lob'=>'OCILob', 'filename'=>'string', 'offset'=>'int'], +'oci_lob_flush' => ['bool', 'lob'=>'OCILob'], +'oci_lob_import' => ['bool', 'lob'=>'OCILob'], 'oci_lob_is_equal' => ['bool', 'lob1'=>'OCILob', 'lob2'=>'OCILob'], 'oci_lob_load' => ['string'], -'oci_lob_read' => ['string', 'lob'=>'int'], +'oci_lob_read' => ['string', 'lob'=>'OCILob'], 'oci_lob_rewind' => ['bool'], -'oci_lob_save' => ['bool', 'lob'=>'string', 'data'=>'int'], -'oci_lob_seek' => ['bool', 'lob'=>'int', 'offset'=>'int'], +'oci_lob_save' => ['bool', 'lob'=>'OCILob', 'data'=>'string'], +'oci_lob_seek' => ['bool', 'lob'=>'OCILob', 'offset'=>'int'], 'oci_lob_size' => ['int'], 'oci_lob_tell' => ['int'], -'oci_lob_truncate' => ['bool', 'lob'=>'int'], -'oci_lob_write' => ['int', 'lob'=>'string', 'data'=>'int'], +'oci_lob_truncate' => ['bool', 'lob'=>'OCILob'], +'oci_lob_write' => ['int', 'lob'=>'OCILob', 'data'=>'string'], 'oci_lob_write_temporary' => ['bool', 'value'=>'string', 'lob_type'=>'int'], 'oci_new_collection' => ['OCICollection|false', 'connection'=>'resource', 'type_name'=>'string', 'schema='=>'string'], 'oci_new_connect' => ['resource|false', 'username'=>'string', 'password'=>'string', 'connection_string='=>'string', 'encoding='=>'string', 'session_mode='=>'int'], @@ -9241,7 +9241,7 @@ 'oci_unregister_taf_callback' => ['bool', 'connection'=>'resource'], 'ocifetchinto' => ['int|bool', 'statement'=>'resource', '&w_result'=>'array', 'mode='=>'int'], 'ocigetbufferinglob' => ['bool'], -'ocisetbufferinglob' => ['bool', 'lob'=>'bool'], +'ocisetbufferinglob' => ['bool', 'lob'=>'OCILob'], 'octdec' => ['int|float', 'octal_string'=>'string'], 'odbc_autocommit' => ['mixed', 'odbc'=>'resource', 'enable='=>'bool'], 'odbc_binmode' => ['bool', 'statement'=>'resource', 'mode'=>'int'], @@ -9261,7 +9261,7 @@ 'odbc_fetch_array' => ['array|false', 'statement'=>'resource', 'row='=>'int'], 'odbc_fetch_into' => ['int', 'statement'=>'resource', '&w_array'=>'array', 'row='=>'int'], 'odbc_fetch_object' => ['object|false', 'statement'=>'resource', 'row='=>'int'], -'odbc_fetch_row' => ['bool', 'statement'=>'resource', 'row='=>'int'], +'odbc_fetch_row' => ['bool', 'statement'=>'resource', 'row='=>'int|null'], 'odbc_field_len' => ['int|false', 'statement'=>'resource', 'field'=>'int'], 'odbc_field_name' => ['string|false', 'statement'=>'resource', 'field'=>'int'], 'odbc_field_num' => ['int|false', 'statement'=>'resource', 'field'=>'string'], @@ -12235,7 +12235,7 @@ 'snmp_get_quick_print' => ['bool'], 'snmp_get_valueretrieval' => ['int'], 'snmp_read_mib' => ['bool', 'filename'=>'string'], -'snmp_set_enum_print' => ['bool', 'enable'=>'int'], +'snmp_set_enum_print' => ['bool', 'enable'=>'bool'], 'snmp_set_oid_numeric_print' => ['void', 'format'=>'int'], 'snmp_set_oid_output_format' => ['bool', 'format'=>'int'], 'snmp_set_quick_print' => ['bool', 'enable'=>'bool'], @@ -13758,7 +13758,7 @@ 'stomp_begin' => ['bool', 'transaction_id'=>'string', 'headers='=>'array', 'link='=>''], 'stomp_close' => ['bool', 'link'=>''], 'stomp_commit' => ['bool', 'transaction_id'=>'string', 'headers='=>'array', 'link='=>''], -'stomp_connect' => ['resource', 'broker='=>'string', 'username='=>'string', 'password='=>'string', 'headers='=>'array'], +'stomp_connect' => ['resource', 'broker='=>'string', 'username='=>'string', 'password='=>'string', 'headers='=>'array|null'], 'stomp_connect_error' => ['string'], 'stomp_error' => ['string', 'link'=>''], 'stomp_get_read_timeout' => ['array', 'link'=>''], @@ -14605,7 +14605,7 @@ 'tidy_get_release' => ['string'], 'tidy_get_root' => ['tidyNode', 'tidy'=>'tidy'], 'tidy_get_status' => ['int', 'tidy'=>'tidy'], -'tidy_getopt' => ['mixed', 'tidy'=>'string', 'option'=>'tidy'], +'tidy_getopt' => ['string|int|bool', 'tidy'=>'tidy', 'option'=>'string'], 'tidy_is_xhtml' => ['bool', 'tidy'=>'tidy'], 'tidy_is_xml' => ['bool', 'tidy'=>'tidy'], 'tidy_load_config' => ['void', 'filename'=>'string', 'encoding'=>'string'], @@ -15664,12 +15664,12 @@ 'xmlrpc_get_type' => ['string', 'value'=>'mixed'], 'xmlrpc_is_fault' => ['bool', 'arg'=>'array'], 'xmlrpc_parse_method_descriptions' => ['array', 'xml'=>'string'], -'xmlrpc_server_add_introspection_data' => ['int', 'server'=>'resource', 'desc'=>'array'], -'xmlrpc_server_call_method' => ['string', 'server'=>'resource', 'xml'=>'string', 'user_data'=>'mixed', 'output_options='=>'array'], -'xmlrpc_server_create' => ['resource'], -'xmlrpc_server_destroy' => ['int', 'server'=>'resource'], -'xmlrpc_server_register_introspection_callback' => ['bool', 'server'=>'resource', 'function'=>'string'], -'xmlrpc_server_register_method' => ['bool', 'server'=>'resource', 'method_name'=>'string', 'function'=>'string'], +'xmlrpc_server_add_introspection_data' => ['int', 'server'=>'XmlRpcServer', 'desc'=>'array'], +'xmlrpc_server_call_method' => ['string', 'server'=>'XmlRpcServer', 'xml'=>'string', 'user_data'=>'mixed', 'output_options='=>'array'], +'xmlrpc_server_create' => ['XmlRpcServer'], +'xmlrpc_server_destroy' => ['int', 'server'=>'XmlRpcServer'], +'xmlrpc_server_register_introspection_callback' => ['bool', 'server'=>'XmlRpcServer', 'function'=>'string'], +'xmlrpc_server_register_method' => ['bool', 'server'=>'XmlRpcServer', 'method_name'=>'string', 'function'=>'string'], 'xmlrpc_set_type' => ['bool', '&rw_value'=>'string|DateTime', 'type'=>'string'], 'XMLWriter::endAttribute' => ['bool'], 'XMLWriter::endCdata' => ['bool'], diff --git a/tests/Internal/Codebase/InternalCallMapHandlerTest.php b/tests/Internal/Codebase/InternalCallMapHandlerTest.php index caea03756ae..110703c1eaa 100644 --- a/tests/Internal/Codebase/InternalCallMapHandlerTest.php +++ b/tests/Internal/Codebase/InternalCallMapHandlerTest.php @@ -20,7 +20,8 @@ class InternalCallMapHandlerTest extends TestCase 'ctype_digit', 'ctype_lower', 'ctype_alnum', 'ctype_alpha', 'ctype_cntrl', 'ctype_graph', 'ctype_lower', 'ctype_print', 'ctype_punct', 'ctype_space', 'ctype_upper', 'ctype_xdigit', 'file_put_contents', 'sodium_crypto_generichash', 'sodium_crypto_generichash_final', - 'dom_import_simplexml', 'imagegd', 'imagegd2', 'pg_exec', 'mysqli_execute', 'array_multisort' + 'dom_import_simplexml', 'imagegd', 'imagegd2', 'pg_exec', 'mysqli_execute', 'array_multisort', + ]; /** From d8edad87759973c1c65ac0649a296e26ede744f9 Mon Sep 17 00:00:00 2001 From: Sam Mousa Date: Tue, 14 Jun 2022 17:12:32 +0200 Subject: [PATCH 29/88] fix: fix bad parameter names in callmap --- dictionaries/CallMap.php | 48 ++++++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/dictionaries/CallMap.php b/dictionaries/CallMap.php index 1689269459a..f2614e24414 100644 --- a/dictionaries/CallMap.php +++ b/dictionaries/CallMap.php @@ -7393,30 +7393,30 @@ 'Memcache::setCompressThreshold' => ['bool', 'threshold'=>'int', 'min_savings='=>'float'], 'Memcache::setFailureCallback' => [''], 'Memcache::setServerParams' => ['bool', 'host'=>'string', 'port='=>'int', 'timeout='=>'int', 'retry_interval='=>'int', 'status='=>'bool', 'failure_callback='=>'callable'], -'memcache_add' => ['bool', 'memcache_obj'=>'Memcache', 'key'=>'string', 'var'=>'mixed', 'flag='=>'int', 'expire='=>'int'], -'memcache_add_server' => ['bool', 'memcache_obj'=>'Memcache', 'host'=>'string', 'port='=>'int', 'persistent='=>'bool', 'weight='=>'int', 'timeout='=>'int', 'retry_interval='=>'int', 'status='=>'bool', 'failure_callback='=>'callable', 'timeoutms='=>'int'], -'memcache_append' => ['', 'memcache_obj'=>'Memcache'], -'memcache_cas' => ['', 'memcache_obj'=>'Memcache'], -'memcache_close' => ['bool', 'memcache_obj'=>'Memcache'], +'memcache_add' => ['bool', 'memcache'=>'MemcachePool', 'key'=>'string', 'value'=>'mixed', 'flag='=>'int', 'expire='=>'int'], +'memcache_add_server' => ['bool', 'memcache'=>'MemcachePool', 'host'=>'string', 'port='=>'int', 'persistent='=>'bool', 'weight='=>'int', 'timeout='=>'int', 'retry_interval='=>'int', 'status='=>'bool', 'failure_callback='=>'callable', 'timeoutms='=>'int'], +'memcache_append' => ['', 'memcache'=>'MemcachePool'], +'memcache_cas' => ['', 'memcache'=>'MemcachePool'], +'memcache_close' => ['bool', 'memcache'=>'MemcachePool'], 'memcache_connect' => ['Memcache|false', 'host'=>'string', 'port='=>'int', 'timeout='=>'int'], 'memcache_debug' => ['bool', 'on_off'=>'bool'], -'memcache_decrement' => ['int', 'memcache_obj'=>'Memcache', 'key'=>'string', 'value='=>'int'], -'memcache_delete' => ['bool', 'memcache_obj'=>'Memcache', 'key'=>'string', 'timeout='=>'int'], -'memcache_flush' => ['bool', 'memcache_obj'=>'Memcache'], -'memcache_get' => ['string', 'memcache_obj'=>'Memcache', 'key'=>'string', 'flags='=>'int'], -'memcache_get\'1' => ['array', 'memcache_obj'=>'Memcache', 'key'=>'string[]', 'flags='=>'int[]'], -'memcache_get_extended_stats' => ['array', 'memcache_obj'=>'Memcache', 'type='=>'string', 'slabid='=>'int', 'limit='=>'int'], -'memcache_get_server_status' => ['int', 'memcache_obj'=>'Memcache', 'host'=>'string', 'port='=>'int'], -'memcache_get_stats' => ['array', 'memcache_obj'=>'Memcache', 'type='=>'string', 'slabid='=>'int', 'limit='=>'int'], -'memcache_get_version' => ['string', 'memcache_obj'=>'Memcache'], -'memcache_increment' => ['int', 'memcache_obj'=>'Memcache', 'key'=>'string', 'value='=>'int'], +'memcache_decrement' => ['int', 'memcache'=>'MemcachePool', 'key'=>'string', 'value='=>'int'], +'memcache_delete' => ['bool', 'memcache'=>'MemcachePool', 'key'=>'string', 'timeout='=>'int'], +'memcache_flush' => ['bool', 'memcache'=>'MemcachePool'], +'memcache_get' => ['string', 'memcache'=>'MemcachePool', 'key'=>'string', 'flags='=>'int'], +'memcache_get\'1' => ['array', 'memcache'=>'Memcache', 'key'=>'string[]', 'flags='=>'int[]'], +'memcache_get_extended_stats' => ['array', 'memcache'=>'MemcachePool', 'type='=>'string', 'slabid='=>'int', 'limit='=>'int'], +'memcache_get_server_status' => ['int', 'memcache'=>'MemcachePool', 'host'=>'string', 'port='=>'int'], +'memcache_get_stats' => ['array', 'memcache'=>'MemcachePool', 'type='=>'string', 'slabid='=>'int', 'limit='=>'int'], +'memcache_get_version' => ['string', 'memcache'=>'MemcachePool'], +'memcache_increment' => ['int|false', 'memcache'=>'MemcachePool', 'key'=>'string', 'value='=>'int'], 'memcache_pconnect' => ['Memcache|false', 'host'=>'string', 'port='=>'int', 'timeout='=>'int'], -'memcache_prepend' => ['string', 'memcache_obj'=>'Memcache'], -'memcache_replace' => ['bool', 'memcache_obj'=>'Memcache', 'key'=>'string', 'var'=>'mixed', 'flag='=>'int', 'expire='=>'int'], -'memcache_set' => ['bool', 'memcache_obj'=>'Memcache', 'key'=>'string', 'var'=>'mixed', 'flag='=>'int', 'expire='=>'int'], -'memcache_set_compress_threshold' => ['bool', 'memcache_obj'=>'Memcache', 'threshold'=>'int', 'min_savings='=>'float'], -'memcache_set_failure_callback' => ['', 'memcache_obj'=>'Memcache'], -'memcache_set_server_params' => ['bool', 'memcache_obj'=>'Memcache', 'host'=>'string', 'port='=>'int', 'timeout='=>'float', 'retry_interval='=>'int', 'status='=>'bool', 'failure_callback='=>'callable'], +'memcache_prepend' => ['string', 'memcache'=>'MemcachePool'], +'memcache_replace' => ['bool', 'memcache'=>'MemcachePool', 'key'=>'string', 'var'=>'mixed', 'flag='=>'int', 'expire='=>'int'], +'memcache_set' => ['bool', 'memcache'=>'MemcachePool', 'key'=>'string', 'value'=>'mixed', 'flag='=>'int', 'expire='=>'int'], +'memcache_set_compress_threshold' => ['bool', 'memcache'=>'MemcachePool', 'threshold'=>'int', 'min_savings='=>'float'], +'memcache_set_failure_callback' => ['', 'memcache'=>'MemcachePool'], +'memcache_set_server_params' => ['bool', 'memcache'=>'MemcachePool', 'host'=>'string', 'port='=>'int', 'timeout='=>'float', 'retry_interval='=>'int', 'status='=>'bool', 'failure_callback='=>'callable'], 'Memcached::__construct' => ['void', 'persistent_id='=>'mixed|string', 'on_new_object_cb='=>'mixed'], 'Memcached::add' => ['bool', 'key'=>'string', 'value'=>'mixed', 'expiration='=>'int'], 'Memcached::addByKey' => ['bool', 'server_key'=>'string', 'key'=>'string', 'value'=>'mixed', 'expiration='=>'int'], @@ -12315,7 +12315,7 @@ 'socket_recv' => ['int|false', 'socket'=>'Socket', '&w_buf'=>'string', 'length'=>'int', 'flags'=>'int'], 'socket_recvfrom' => ['int|false', 'socket'=>'Socket', '&w_buf'=>'string', 'length'=>'int', 'flags'=>'int', '&w_name'=>'string', '&w_port='=>'int'], 'socket_recvmsg' => ['int|false', 'socket'=>'Socket', '&w_message'=>'array', 'flags='=>'int'], -'socket_select' => ['int|false', '&rw_read_fds'=>'Socket[]|null', '&rw_write_fds'=>'Socket[]|null', '&rw_except_fds'=>'Socket[]|null', 'tv_sec'=>'int|null', 'tv_usec='=>'int'], +'socket_select' => ['int|false', '&rw_read'=>'Socket[]|null', '&rw_write'=>'Socket[]|null', '&rw_except'=>'Socket[]|null', 'tv_sec'=>'int|null', 'tv_usec='=>'int'], 'socket_send' => ['int|false', 'socket'=>'Socket', 'buf'=>'string', 'length'=>'int', 'flags'=>'int'], 'socket_sendmsg' => ['int|false', 'socket'=>'Socket', 'message'=>'array', 'flags='=>'int'], 'socket_sendto' => ['int|false', 'socket'=>'Socket', 'data'=>'string', 'length'=>'int', 'flags'=>'int', 'address'=>'string', 'port='=>'int|null'], @@ -13755,8 +13755,8 @@ 'Stomp::unsubscribe' => ['bool', 'destination'=>'string', 'headers='=>'array', 'link='=>''], 'stomp_abort' => ['bool', 'transaction_id'=>'string', 'headers='=>'array', 'link='=>''], 'stomp_ack' => ['bool', 'msg'=>'', 'headers='=>'array', 'link='=>''], -'stomp_begin' => ['bool', 'transaction_id'=>'string', 'headers='=>'array', 'link='=>''], -'stomp_close' => ['bool', 'link'=>''], +'stomp_begin' => ['bool', 'link' => 'resource', 'transaction_id'=>'string', 'headers='=>'array|null'], +'stomp_close' => ['bool', 'link'=>'resource'], 'stomp_commit' => ['bool', 'transaction_id'=>'string', 'headers='=>'array', 'link='=>''], 'stomp_connect' => ['resource', 'broker='=>'string', 'username='=>'string', 'password='=>'string', 'headers='=>'array|null'], 'stomp_connect_error' => ['string'], From 4bafb96ceea8f62451af8b51e1c2d4f0f44e45b4 Mon Sep 17 00:00:00 2001 From: Alexandru Patranescu Date: Wed, 15 Jun 2022 07:30:20 +0300 Subject: [PATCH 30/88] allow decimal_separator and thousands_separator parameters to be nullable in PHP 7 and 8 --- dictionaries/CallMap.php | 2 +- dictionaries/CallMap_80_delta.php | 4 ++-- dictionaries/CallMap_historical.php | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/dictionaries/CallMap.php b/dictionaries/CallMap.php index fc7fff7ea65..d1c5e3dd9fd 100644 --- a/dictionaries/CallMap.php +++ b/dictionaries/CallMap.php @@ -9027,7 +9027,7 @@ 'nsapi_response_headers' => ['array'], 'nsapi_virtual' => ['bool', 'uri'=>'string'], 'nthmac' => ['string', 'clent'=>'string', 'data'=>'string'], -'number_format' => ['string', 'num'=>'float|int', 'decimals='=>'int', 'decimal_separator='=>'string', 'thousands_separator='=>'string'], +'number_format' => ['string', 'num'=>'float|int', 'decimals='=>'int', 'decimal_separator='=>'?string', 'thousands_separator='=>'?string'], 'NumberFormatter::__construct' => ['void', 'locale'=>'string', 'style'=>'int', 'pattern='=>'string'], 'NumberFormatter::create' => ['NumberFormatter|false', 'locale'=>'string', 'style'=>'int', 'pattern='=>'string'], 'NumberFormatter::format' => ['string|false', 'num'=>'', 'type='=>'int'], diff --git a/dictionaries/CallMap_80_delta.php b/dictionaries/CallMap_80_delta.php index f905592effe..1b364d52512 100644 --- a/dictionaries/CallMap_80_delta.php +++ b/dictionaries/CallMap_80_delta.php @@ -967,7 +967,7 @@ ], 'number_format' => [ 'old' => ['string', 'num'=>'float|int', 'decimals='=>'int'], - 'new' => ['string', 'num'=>'float|int', 'decimals='=>'int', 'decimal_separator='=>'string', 'thousands_separator='=>'string'], + 'new' => ['string', 'num'=>'float|int', 'decimals='=>'int', 'decimal_separator='=>'?string', 'thousands_separator='=>'?string'], ], 'ob_implicit_flush' => [ 'old' => ['void', 'enable='=>'int'], @@ -1615,7 +1615,7 @@ 'image2wbmp' => ['bool', 'im'=>'resource', 'filename='=>'?string', 'threshold='=>'int'], 'jpeg2wbmp' => ['bool', 'jpegname'=>'string', 'wbmpname'=>'string', 'dest_height'=>'int', 'dest_width'=>'int', 'threshold'=>'int'], 'ldap_sort' => ['bool', 'link_identifier'=>'resource', 'result_identifier'=>'resource', 'sortfilter'=>'string'], - 'number_format\'1' => ['string', 'num'=>'float|int', 'decimals'=>'int', 'decimal_separator'=>'string', 'thousands_separator'=>'string'], + 'number_format\'1' => ['string', 'num'=>'float|int', 'decimals'=>'int', 'decimal_separator'=>'?string', 'thousands_separator'=>'?string'], 'png2wbmp' => ['bool', 'pngname'=>'string', 'wbmpname'=>'string', 'dest_height'=>'int', 'dest_width'=>'int', 'threshold'=>'int'], 'read_exif_data' => ['array', 'filename'=>'string', 'sections_needed='=>'string', 'sub_arrays='=>'bool', 'read_thumbnail='=>'bool'], 'SimpleXMLIterator::rewind' => ['void'], diff --git a/dictionaries/CallMap_historical.php b/dictionaries/CallMap_historical.php index 3f8fac70fb8..f77fa29f0c2 100644 --- a/dictionaries/CallMap_historical.php +++ b/dictionaries/CallMap_historical.php @@ -13897,7 +13897,7 @@ 'nsapi_virtual' => ['bool', 'uri'=>'string'], 'nthmac' => ['string', 'clent'=>'string', 'data'=>'string'], 'number_format' => ['string', 'num'=>'float|int', 'decimals='=>'int'], - 'number_format\'1' => ['string', 'num'=>'float|int', 'decimals'=>'int', 'decimal_separator'=>'string', 'thousands_separator'=>'string'], + 'number_format\'1' => ['string', 'num'=>'float|int', 'decimals'=>'int', 'decimal_separator'=>'?string', 'thousands_separator'=>'?string'], 'numfmt_create' => ['NumberFormatter|false', 'locale'=>'string', 'style'=>'int', 'pattern='=>'string'], 'numfmt_format' => ['string|false', 'formatter'=>'NumberFormatter', 'num'=>'int|float', 'type='=>'int'], 'numfmt_format_currency' => ['string|false', 'formatter'=>'NumberFormatter', 'amount'=>'float', 'currency'=>'string'], From 923f600fab069b1548498f98f7a4a753ed0ddd9f Mon Sep 17 00:00:00 2001 From: Sam Mousa Date: Wed, 15 Jun 2022 09:06:07 +0200 Subject: [PATCH 31/88] fix: callmap delta for fgetss --- dictionaries/CallMap_80_delta.php | 1 + 1 file changed, 1 insertion(+) diff --git a/dictionaries/CallMap_80_delta.php b/dictionaries/CallMap_80_delta.php index b8637425e4f..79cb83b7643 100644 --- a/dictionaries/CallMap_80_delta.php +++ b/dictionaries/CallMap_80_delta.php @@ -1599,6 +1599,7 @@ ], ], 'removed' => [ + 'fgetss' => ['string|false', 'fp'=>'resource', 'length='=>'int', 'allowable_tags='=>'string'], 'PDOStatement::setFetchMode\'1' => ['bool', 'fetch_column'=>'int', 'colno'=>'int'], 'PDOStatement::setFetchMode\'2' => ['bool', 'fetch_class'=>'int', 'classname'=>'string', 'ctorargs'=>'array'], 'PDOStatement::setFetchMode\'3' => ['bool', 'fetch_into'=>'int', 'object'=>'object'], From 7bc5721d4c1cd13d8561a96ac81620cecbb1cca8 Mon Sep 17 00:00:00 2001 From: Sam Mousa Date: Wed, 15 Jun 2022 09:11:14 +0200 Subject: [PATCH 32/88] fix: php8.1 syntax in tests --- .../Codebase/InternalCallMapHandlerTest.php | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/tests/Internal/Codebase/InternalCallMapHandlerTest.php b/tests/Internal/Codebase/InternalCallMapHandlerTest.php index 110703c1eaa..3a4957b62c5 100644 --- a/tests/Internal/Codebase/InternalCallMapHandlerTest.php +++ b/tests/Internal/Codebase/InternalCallMapHandlerTest.php @@ -15,7 +15,7 @@ class InternalCallMapHandlerTest extends TestCase { - private array $ignoredFunctions = [ + private $ignoredFunctions = [ 'sprintf', 'printf', 'ctype_print', 'date_sunrise' /** deprecated in 8.1 */, 'ctype_digit', 'ctype_lower', 'ctype_alnum', 'ctype_alpha', 'ctype_cntrl', 'ctype_graph', 'ctype_lower', 'ctype_print', 'ctype_punct', 'ctype_space', 'ctype_upper', @@ -28,7 +28,7 @@ class InternalCallMapHandlerTest extends TestCase * * @var bool whether to skip params for which no definition can be found in the callMap */ - private $skipUndefinedParams = true; + private $skipUndefinedParams = false; /** * These are items that very likely need updating to PHP8.1 @@ -69,12 +69,12 @@ public function callMapEntryProvider(): iterable foreach($callMap as $function => $entry) { // Skip class methods - if (strpos($function, '::') !== false) { + if (strpos($function, '::') !== false || !function_exists($function)) { + continue; + } + if (!str_starts_with($function, 'memcache_')) { continue; } -// if (!str_starts_with($function, 'array_')) { -// continue; -// } // Skip functions with alternate signatures if (isset($callMap["$function'1"]) || preg_match("/\'\d$/", $function)) { continue; @@ -148,6 +148,8 @@ private function assertEntryIsCorrect(array $callMapEntry, string $functionName) foreach($rF->getParameters() as $parameter) { if ($this->skipUndefinedParams && !isset($normalizedEntries[$parameter->getName()])) { continue; + } else { + $this->assertArrayHasKey($parameter->getName(), $normalizedEntries); } $this->assertParameter($normalizedEntries[$parameter->getName()], $parameter, $functionName); } From 11b44b01a113cc0e48fd8345600abd6e3d8f6de8 Mon Sep 17 00:00:00 2001 From: Sam Mousa Date: Wed, 15 Jun 2022 09:11:58 +0200 Subject: [PATCH 33/88] fix: add imagefilter alternative signatures in delta 8.1 --- dictionaries/CallMap_81_delta.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/dictionaries/CallMap_81_delta.php b/dictionaries/CallMap_81_delta.php index 4f6092ad6f7..09682ee4810 100644 --- a/dictionaries/CallMap_81_delta.php +++ b/dictionaries/CallMap_81_delta.php @@ -46,8 +46,15 @@ 'ReflectionEnumUnitCase::getValue' => ['UnitEnum'], 'ReflectionEnumBackedCase::getBackingValue' => ['string|int'], 'ReflectionFunctionAbstract::isStatic' => ['bool'], + 'imagefilter\'1' => ['bool', 'image' => 'GdImage', 'filter' => '2', 'args' => 'int<-255,255>'], + 'imagefilter\'2' => ['bool', 'image' => 'GdImage', 'filter' => '3|10', 'args' => 'int'], + 'imagefilter\'3' => ['bool', 'image' => 'GdImage', 'filter' => '4', 'args' => 'int<0,255>', 'args2' => 'int<0,255>', 'args3' => 'int<0,255>', 'args4' => 'int<0,127>'], + 'imagefilter\'4' => ['bool', 'image' => 'GdImage', 'filter' => '11', 'args' => 'int', 'args2=' => 'bool'], + 'imagefilter\'5' => ['bool', 'image' => 'GdImage', 'filter' => '12', 'args' => 'int', 'args2=' => 'int', 'args3=' => 'array'], + ], + 'changed' => [ 'finfo_buffer' => [ 'old' => ['string|false', 'finfo'=>'resource', 'string'=>'string', 'flags='=>'int', 'context='=>'resource'], From c638006f5bca61790520adbf9250ad6f5a2361c9 Mon Sep 17 00:00:00 2001 From: Sam Mousa Date: Wed, 15 Jun 2022 09:27:40 +0200 Subject: [PATCH 34/88] fix: add memcache family of function to ignore list --- .../Codebase/InternalCallMapHandlerTest.php | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tests/Internal/Codebase/InternalCallMapHandlerTest.php b/tests/Internal/Codebase/InternalCallMapHandlerTest.php index 3a4957b62c5..3f2ed8f08a3 100644 --- a/tests/Internal/Codebase/InternalCallMapHandlerTest.php +++ b/tests/Internal/Codebase/InternalCallMapHandlerTest.php @@ -4,11 +4,15 @@ use Psalm\Internal\Analyzer\ProjectAnalyzer; use Psalm\Internal\Codebase\InternalCallMapHandler; +use Psalm\Internal\Codebase\Reflection; use Psalm\Internal\Provider\FakeFileProvider; use Psalm\Internal\Provider\Providers; +use Psalm\Internal\Type\Comparator\UnionTypeComparator; +use Psalm\Internal\Type\TypeParser; use Psalm\Tests\Internal\Provider\FakeParserCacheProvider; use Psalm\Tests\TestCase; use Psalm\Tests\TestConfig; +use Psalm\Type; use ReflectionFunction; use ReflectionNamedType; use ReflectionParameter; @@ -21,6 +25,9 @@ class InternalCallMapHandlerTest extends TestCase 'ctype_graph', 'ctype_lower', 'ctype_print', 'ctype_punct', 'ctype_space', 'ctype_upper', 'ctype_xdigit', 'file_put_contents', 'sodium_crypto_generichash', 'sodium_crypto_generichash_final', 'dom_import_simplexml', 'imagegd', 'imagegd2', 'pg_exec', 'mysqli_execute', 'array_multisort', + 'memcache_add_server', 'memcache_cas', 'memcache_pconnect', 'memcache_connect', 'memcache_delete', + 'memcache_replace', 'memcache_append', 'memcache_increment', 'memcache_set_failure_callback', 'memcache_decrement', + 'memcache_set_server_params', 'memcache_get_server_status', 'memcache_prepend', 'memcache_flush' ]; @@ -192,6 +199,13 @@ private function assertParameter(array $normalizedEntry, ReflectionParameter $pa */ private function assertTypeValidity(ReflectionNamedType $reflected, string $specified, string $message): void { + $expectedType = Reflection::getPsalmTypeFromReflectionType($reflected); + $parsedType = Type::parseString($specified); + + if (UnionTypeComparator::isContainedByInPhp($parsedType, $expectedType)) { + return; + } + // In case reflection returns mixed we assume any type specified in the callmap is more specific and correct if ($reflected->getName() === 'mixed') { return; From ded566e79a19e37f55cfe6073778042b44801f31 Mon Sep 17 00:00:00 2001 From: Sam Mousa Date: Wed, 15 Jun 2022 10:56:15 +0200 Subject: [PATCH 35/88] fix: callmap improvements and updated ignore list --- dictionaries/CallMap.php | 88 ++++---- .../Codebase/InternalCallMapHandlerTest.php | 197 +++++++++--------- 2 files changed, 138 insertions(+), 147 deletions(-) diff --git a/dictionaries/CallMap.php b/dictionaries/CallMap.php index f2614e24414..efc7794a357 100644 --- a/dictionaries/CallMap.php +++ b/dictionaries/CallMap.php @@ -361,7 +361,7 @@ 'ArithmeticError::getTraceAsString' => ['string'], 'array_change_key_case' => ['associative-array', 'array'=>'array', 'case='=>'int'], 'array_chunk' => ['list', 'array'=>'array', 'length'=>'int', 'preserve_keys='=>'bool'], -'array_column' => ['array', 'array'=>'array', 'column_key'=>'mixed', 'index_key='=>'mixed'], +'array_column' => ['array', 'array'=>'array', 'column_key'=>'int|string|null', 'index_key='=>'int|string|null'], 'array_combine' => ['associative-array', 'keys'=>'string[]|int[]', 'values'=>'array'], 'array_count_values' => ['associative-array', 'array'=>'array'], 'array_diff' => ['associative-array', 'array'=>'array', '...arrays='=>'array'], @@ -527,7 +527,7 @@ 'bcadd' => ['numeric-string', 'num1'=>'numeric-string', 'num2'=>'numeric-string', 'scale='=>'int|null'], 'bccomp' => ['int', 'num1'=>'numeric-string', 'num2'=>'numeric-string', 'scale='=>'int|null'], 'bcdiv' => ['numeric-string|null', 'dividend'=>'numeric-string', 'divisor'=>'numeric-string', 'scale='=>'int|null'], -'bcmod' => ['numeric-string|null', 'dividend'=>'numeric-string', 'divisor'=>'numeric-string', 'scale='=>'int|null'], +'bcmod' => ['numeric-string|null', 'num1'=>'numeric-string', 'num2'=>'numeric-string', 'scale='=>'int|null'], 'bcmul' => ['numeric-string', 'num1'=>'numeric-string', 'num2'=>'numeric-string', 'scale='=>'int|null'], 'bcompiler_load' => ['bool', 'filename'=>'string'], 'bcompiler_load_exe' => ['bool', 'filename'=>'string'], @@ -1543,7 +1543,7 @@ 'Couchbase\WildcardSearchQuery::jsonSerialize' => ['array'], 'Couchbase\zlibCompress' => ['string', 'data'=>'string'], 'Couchbase\zlibDecompress' => ['string', 'data'=>'string'], -'count' => ['int', 'value'=>'Countable|array|SimpleXMLElement|ResourceBundle', 'mode='=>'int'], +'count' => ['int', 'value'=>'Countable|array ', 'mode='=>'int'], 'count_chars' => ['array', 'input'=>'string', 'mode='=>'0|1|2'], 'count_chars\'1' => ['string', 'input'=>'string', 'mode='=>'3|4'], 'Countable::count' => ['int'], @@ -1752,15 +1752,15 @@ 'datefmt_get_locale' => ['string|false', 'formatter'=>'IntlDateFormatter', 'type='=>'int'], 'datefmt_get_pattern' => ['string', 'formatter'=>'IntlDateFormatter'], 'datefmt_get_timetype' => ['int', 'formatter'=>'IntlDateFormatter'], -'datefmt_get_timezone' => ['IntlTimeZone|false'], -'datefmt_get_timezone_id' => ['string', 'formatter'=>'IntlDateFormatter'], +'datefmt_get_timezone' => ['IntlTimeZone|false', 'formatter'=>'IntlDateFormatter'], +'datefmt_get_timezone_id' => ['string|false', 'formatter'=>'IntlDateFormatter'], 'datefmt_is_lenient' => ['bool', 'formatter'=>'IntlDateFormatter'], 'datefmt_localtime' => ['array|false', 'formatter'=>'IntlDateFormatter', 'string'=>'string', '&rw_offset='=>'int'], 'datefmt_parse' => ['int|false', 'formatter'=>'IntlDateFormatter', 'string'=>'string', '&rw_offset='=>'int'], 'datefmt_set_calendar' => ['bool', 'formatter'=>'IntlDateFormatter', 'calendar'=>'int'], 'datefmt_set_lenient' => ['?bool', 'formatter'=>'IntlDateFormatter', 'lenient'=>'bool'], 'datefmt_set_pattern' => ['bool', 'formatter'=>'IntlDateFormatter', 'pattern'=>'string'], -'datefmt_set_timezone' => ['bool', 'formatter'=>'IntlDateFormatter'], +'datefmt_set_timezone' => ['bool|null', 'formatter'=>'IntlDateFormatter', 'timezone' => 'IntlTimeZone|DateTimeZone|string|null'], 'datefmt_set_timezone_id' => ['bool', 'fmt'=>'IntlDateFormatter', 'zone'=>'string'], 'DateInterval::__construct' => ['void', 'spec'=>'string'], 'DateInterval::__set_state' => ['DateInterval', 'array'=>'array'], @@ -2522,7 +2522,7 @@ 'enchant_dict_store_replacement' => ['void', 'dictionary'=>'EnchantDictionary', 'misspelled'=>'string', 'correct'=>'string'], 'enchant_dict_suggest' => ['array', 'dictionary'=>'EnchantDictionary', 'word'=>'string'], 'end' => ['mixed|false', '&r_array'=>'array|object'], -'enum_exists' => ['bool', 'class' => 'class-string', 'autoload=' => 'bool'], +'enum_exists' => ['bool', 'enum' => 'class-string', 'autoload=' => 'bool'], 'Error::__clone' => ['void'], 'Error::__construct' => ['void', 'message='=>'string', 'code='=>'int', 'previous='=>'?Throwable|?Error'], 'Error::__toString' => ['string'], @@ -3292,8 +3292,8 @@ 'filter_input' => ['mixed|false', 'type'=>'int', 'var_name'=>'string', 'filter='=>'int', 'options='=>'array|int'], 'filter_input_array' => ['mixed|false', 'type'=>'int', 'options='=>'int|array', 'add_empty='=>'bool'], 'filter_list' => ['array'], -'filter_var' => ['mixed|false', 'value'=>'mixed', 'filter='=>'int', 'options='=>'mixed'], -'filter_var_array' => ['mixed|false', 'array'=>'array', 'options='=>'mixed', 'add_empty='=>'bool'], +'filter_var' => ['mixed|false', 'value'=>'mixed', 'filter='=>'int', 'options='=>'array|int'], +'filter_var_array' => ['array|null|false', 'array'=>'array', 'options='=>'array|int', 'add_empty='=>'bool'], 'FilterIterator::__construct' => ['void', 'iterator'=>'Iterator'], 'FilterIterator::accept' => ['bool'], 'FilterIterator::current' => ['mixed'], @@ -3324,7 +3324,7 @@ 'fpassthru' => ['int|false', 'stream'=>'resource'], 'fpm_get_status' => ['array|false'], 'fprintf' => ['int', 'stream'=>'resource', 'format'=>'string', '...values='=>'string|int|float'], -'fputcsv' => ['int|false', 'stream'=>'resource', 'fields'=>'array', 'separator='=>'string', 'enclosure='=>'string', 'escape='=>'string'], +'fputcsv' => ['int|false', 'stream'=>'resource', 'fields'=>'array', 'separator='=>'string', 'enclosure='=>'string', 'escape='=>'string', 'eol' => 'string'], 'fputs' => ['int|false', 'stream'=>'resource', 'data'=>'string', 'length='=>'int|null'], 'fread' => ['string|false', 'stream'=>'resource', 'length'=>'int'], 'frenchtojd' => ['int', 'month'=>'int', 'day'=>'int', 'year'=>'int'], @@ -3724,7 +3724,7 @@ 'get_called_class' => ['class-string'], 'get_cfg_var' => ['string|false', 'option'=>'string'], 'get_class' => ['class-string', 'object='=>'object'], -'get_class_methods' => ['list|null', 'object_or_class'=>'mixed'], +'get_class_methods' => ['list|null', 'object_or_class'=>'object|class-string'], 'get_class_vars' => ['array', 'class'=>'string'], 'get_current_user' => ['string'], 'get_debug_type' => ['string', 'value'=>'mixed'], @@ -3744,7 +3744,7 @@ 'get_magic_quotes_runtime' => ['int|false'], 'get_meta_tags' => ['array', 'filename'=>'string', 'use_include_path='=>'bool'], 'get_object_vars' => ['array', 'object'=>'object'], -'get_parent_class' => ['class-string|false', 'object_or_class='=>'mixed'], +'get_parent_class' => ['class-string|false', 'object_or_class='=>'object|class-string'], 'get_required_files' => ['list'], 'get_resource_id' => ['int', 'resource'=>'resource'], 'get_resource_type' => ['string', 'resource'=>'resource'], @@ -5336,7 +5336,7 @@ 'ifxus_tell_slob' => ['int', 'bid'=>'int'], 'ifxus_write_slob' => ['int', 'bid'=>'int', 'content'=>'string'], 'igbinary_serialize' => ['string|false', 'value'=>'mixed'], -'igbinary_unserialize' => ['mixed', 'string'=>'string'], +'igbinary_unserialize' => ['mixed', 'str'=>'string'], 'ignore_user_abort' => ['int', 'enable='=>'?bool'], 'iis_add_server' => ['int', 'path'=>'string', 'comment'=>'string', 'server_ip'=>'string', 'port'=>'int', 'host_name'=>'string', 'rights'=>'int', 'start_server'=>'int'], 'iis_get_dir_security' => ['int', 'server_instance'=>'int', 'virtual_path'=>'string'], @@ -6164,7 +6164,7 @@ 'inotify_init' => ['resource|false'], 'inotify_queue_len' => ['int', 'inotify_instance'=>'resource'], 'inotify_read' => ['array|false', 'inotify_instance'=>'resource'], -'inotify_rm_watch' => ['bool', 'inotify_instance'=>'resource', 'watch_descriptor'=>'int'], +'inotify_rm_watch' => ['bool', 'inotify_instance'=>'resource', 'mask'=>'int'], 'intdiv' => ['int', 'num1'=>'int', 'num2'=>'int'], 'interface_exists' => ['bool', 'interface'=>'string', 'autoload='=>'bool'], 'intl_error_name' => ['string', 'errorCode'=>'int'], @@ -6975,8 +6975,8 @@ 'Lua::include' => ['mixed', 'file'=>'string'], 'Lua::registerCallback' => ['Lua|null|false', 'name'=>'string', 'function'=>'callable'], 'LuaClosure::__invoke' => ['void', 'arg'=>'mixed', '...args='=>'mixed'], -'lzf_compress' => ['string', 'data'=>'string'], -'lzf_decompress' => ['string', 'data'=>'string'], +'lzf_compress' => ['string', 'string'=>'string'], +'lzf_decompress' => ['string', 'string'=>'string'], 'lzf_optimized_for' => ['int'], 'm_checkstatus' => ['int', 'conn'=>'resource', 'identifier'=>'int'], 'm_completeauthorizations' => ['int', 'conn'=>'resource', 'array'=>'int'], @@ -7018,7 +7018,7 @@ 'm_verifyconnection' => ['bool', 'conn'=>'resource', 'tf'=>'int'], 'm_verifysslcert' => ['bool', 'conn'=>'resource', 'tf'=>'int'], 'magic_quotes_runtime' => ['bool', 'new_setting'=>'bool'], -'mail' => ['bool', 'to'=>'string', 'subject'=>'string', 'message'=>'string', 'additional_headers='=>'string|array|null', 'additional_params='=>'string'], +'mail' => ['bool', 'to'=>'string', 'subject'=>'string', 'message'=>'string', 'additional_headers='=>'string|array', 'additional_params='=>'string'], 'mailparse_determine_best_xfer_encoding' => ['string', 'fp'=>'resource'], 'mailparse_msg_create' => ['resource'], 'mailparse_msg_extract_part' => ['void', 'mimemail'=>'resource', 'msgbody'=>'string', 'callbackfunc='=>'callable'], @@ -7799,7 +7799,7 @@ 'MongoDB\BSON\timestampinterface::getIncrement' => ['int'], 'MongoDB\BSON\timestampinterface::getTimestamp' => ['int'], 'MongoDB\BSON\toJSON' => ['string', 'bson'=>'string'], -'MongoDB\BSON\toPHP' => ['object', 'bson'=>'string', 'typeMap='=>'array'], +'MongoDB\BSON\toPHP' => ['object', 'bson'=>'string', 'typemap='=>'array'], 'MongoDB\BSON\undefined::__construct' => ['void'], 'MongoDB\BSON\undefined::__toString' => ['string'], 'MongoDB\BSON\undefined::jsonSerialize' => ['mixed'], @@ -9011,7 +9011,7 @@ 'Normalizer::getRawDecomposition' => ['string|null', 'input'=>'string'], 'Normalizer::isNormalized' => ['bool', 'input'=>'string', 'form='=>'int'], 'Normalizer::normalize' => ['string', 'input'=>'string', 'form='=>'int'], -'normalizer_get_raw_decomposition' => ['string|null', 'string'=>'string'], +'normalizer_get_raw_decomposition' => ['string|null', 'string'=>'string', 'form=' => 'int'], 'normalizer_is_normalized' => ['bool', 'string'=>'string', 'form='=>'int'], 'normalizer_normalize' => ['string', 'string'=>'string', 'form='=>'int'], 'notes_body' => ['array', 'server'=>'string', 'mailbox'=>'string', 'msg_number'=>'int'], @@ -9032,7 +9032,7 @@ 'nsapi_response_headers' => ['array'], 'nsapi_virtual' => ['bool', 'uri'=>'string'], 'nthmac' => ['string', 'clent'=>'string', 'data'=>'string'], -'number_format' => ['string', 'num'=>'float|int', 'decimals='=>'int', 'decimal_separator='=>'string', 'thousands_separator='=>'string'], +'number_format' => ['string', 'num'=>'float|int', 'decimals='=>'int', 'decimal_separator='=>'string|null', 'thousands_separator='=>'string|null'], 'NumberFormatter::__construct' => ['void', 'locale'=>'string', 'style'=>'int', 'pattern='=>'string'], 'NumberFormatter::create' => ['NumberFormatter|false', 'locale'=>'string', 'style'=>'int', 'pattern='=>'string'], 'NumberFormatter::format' => ['string|false', 'num'=>'', 'type='=>'int'], @@ -9093,7 +9093,7 @@ 'OAuth::setTimestamp' => ['mixed', 'timestamp'=>'string'], 'OAuth::setToken' => ['bool', 'token'=>'string', 'token_secret'=>'string'], 'OAuth::setVersion' => ['bool', 'version'=>'string'], -'oauth_get_sbs' => ['string', 'http_method'=>'string', 'uri'=>'string', 'request_parameters='=>'array'], +'oauth_get_sbs' => ['string', 'http_method'=>'string', 'uri'=>'string', 'parameters='=>'array'], 'oauth_urlencode' => ['string', 'uri'=>'string'], 'OAuthProvider::__construct' => ['void', 'params_array='=>'array'], 'OAuthProvider::addRequiredParameter' => ['bool', 'req_params'=>'string'], @@ -10277,7 +10277,7 @@ 'Postal\Expand::expand_address' => ['string[]', 'address'=>'string', 'options='=>'array'], 'Postal\Parser::parse_address' => ['array', 'address'=>'string', 'options='=>'array'], 'pow' => ['float|int', 'num'=>'int|float', 'exponent'=>'int|float'], -'preg_filter' => ['null|string|string[]', 'pattern'=>'mixed', 'replacement'=>'mixed', 'subject'=>'mixed', 'limit='=>'int', '&w_count='=>'int'], +'preg_filter' => ['null|string|string[]', 'pattern'=>'string|array', 'replacement'=>'string|array', 'subject'=>'string|array', 'limit='=>'int', '&w_count='=>'int|null'], 'preg_grep' => ['array|false', 'pattern'=>'string', 'array'=>'array', 'flags='=>'int'], 'preg_last_error' => ['int'], 'preg_match' => ['int|false', 'pattern'=>'string', 'subject'=>'string', '&w_matches='=>'string[]', 'flags='=>'0', 'offset='=>'int'], @@ -10287,7 +10287,7 @@ 'preg_replace' => ['string|string[]|null', 'pattern'=>'string|array', 'replacement'=>'string|array', 'subject'=>'string|array', 'limit='=>'int', '&w_count='=>'int'], 'preg_replace_callback' => ['string|null', 'pattern'=>'string|array', 'callback'=>'callable(string[]):string', 'subject'=>'string', 'limit='=>'int', '&w_count='=>'int'], 'preg_replace_callback\'1' => ['string[]|null', 'pattern'=>'string|array', 'callback'=>'callable(string[]):string', 'subject'=>'string[]', 'limit='=>'int', '&w_count='=>'int'], -'preg_replace_callback_array' => ['string|string[]|null', 'pattern'=>'array', 'subject'=>'string|array', 'limit='=>'int', '&w_count='=>'int'], +'preg_replace_callback_array' => ['string|string[]|null', 'pattern'=>'array', 'subject'=>'string|array', 'limit='=>'int', '&w_count='=>'int', 'flags=' => 'int'], 'preg_split' => ['list|false', 'pattern'=>'string', 'subject'=>'string', 'limit'=>'int', 'flags='=>'null'], 'preg_split\'1' => ['list|list>|false', 'pattern'=>'string', 'subject'=>'string', 'limit='=>'int', 'flags='=>'int'], 'prev' => ['mixed', '&r_array'=>'array|object'], @@ -12243,7 +12243,7 @@ 'snmpget' => ['string|false', 'hostname'=>'string', 'community'=>'string', 'object_id'=>'string', 'timeout='=>'int', 'retries='=>'int'], 'snmpgetnext' => ['string|false', 'hostname'=>'string', 'community'=>'string', 'object_id'=>'string', 'timeout='=>'int', 'retries='=>'int'], 'snmprealwalk' => ['array|false', 'hostname'=>'string', 'community'=>'string', 'object_id'=>'string', 'timeout='=>'int', 'retries='=>'int'], -'snmpset' => ['bool', 'hostname'=>'string', 'community'=>'string', 'object_id'=>'string', 'type'=>'string', 'value'=>'mixed', 'timeout='=>'int', 'retries='=>'int'], +'snmpset' => ['bool', 'hostname'=>'string', 'community'=>'string', 'object_id'=>'string', 'type'=>'string', 'value'=>'array|string', 'timeout='=>'int', 'retries='=>'int'], 'snmpwalk' => ['array|false', 'hostname'=>'string', 'community'=>'string', 'object_id'=>'string', 'timeout='=>'int', 'retries='=>'int'], 'snmpwalkoid' => ['array|false', 'hostname'=>'string', 'community'=>'string', 'object_id'=>'string', 'timeout='=>'int', 'retries='=>'int'], 'SoapClient::__call' => ['', 'function_name'=>'string', 'arguments'=>'array'], @@ -12294,39 +12294,39 @@ 'socket_addrinfo_connect' => ['Socket|false', 'address'=>'AddressInfo'], 'socket_addrinfo_explain' => ['array', 'address'=>'AddressInfo'], 'socket_addrinfo_lookup' => ['false|list', 'host'=>'string', 'service='=>'string|null', 'hints='=>'array'], -'socket_bind' => ['bool', 'socket'=>'Socket', 'addr'=>'string', 'port='=>'int'], +'socket_bind' => ['bool', 'socket'=>'Socket', 'address'=>'string', 'port='=>'int'], 'socket_clear_error' => ['void', 'socket='=>'Socket|null'], 'socket_close' => ['void', 'socket'=>'Socket'], -'socket_cmsg_space' => ['int', 'level'=>'int', 'type'=>'int'], -'socket_connect' => ['bool', 'socket'=>'Socket', 'addr'=>'string', 'port='=>'int|null'], +'socket_cmsg_space' => ['int|null', 'level'=>'int', 'type'=>'int', 'num=' => 'int'], +'socket_connect' => ['bool', 'socket'=>'Socket', 'address'=>'string', 'port='=>'int|null'], 'socket_create' => ['Socket|false', 'domain'=>'int', 'type'=>'int', 'protocol'=>'int'], 'socket_create_listen' => ['Socket|false', 'port'=>'int', 'backlog='=>'int'], -'socket_create_pair' => ['bool', 'domain'=>'int', 'type'=>'int', 'protocol'=>'int', '&w_fd'=>'Socket[]'], +'socket_create_pair' => ['bool', 'domain'=>'int', 'type'=>'int', 'protocol'=>'int', '&w_pair'=>'Socket[]'], 'socket_export_stream' => ['resource|false', 'socket'=>'Socket'], -'socket_get_option' => ['mixed|false', 'socket'=>'Socket', 'level'=>'int', 'optname'=>'int'], +'socket_get_option' => ['mixed|false', 'socket'=>'Socket', 'level'=>'int', 'option'=>'int'], 'socket_get_status' => ['array', 'stream'=>'Socket'], -'socket_getopt' => ['mixed', 'socket'=>'Socket', 'level'=>'int', 'optname'=>'int'], -'socket_getpeername' => ['bool', 'socket'=>'Socket', '&w_addr'=>'string', '&w_port='=>'int'], -'socket_getsockname' => ['bool', 'socket'=>'Socket', '&w_addr'=>'string', '&w_port='=>'int'], +'socket_getopt' => ['mixed', 'socket'=>'Socket', 'level'=>'int', 'option'=>'int'], +'socket_getpeername' => ['bool', 'socket'=>'Socket', '&w_address'=>'string', '&w_port='=>'int'], +'socket_getsockname' => ['bool', 'socket'=>'Socket', '&w_address'=>'string', '&w_port='=>'int'], 'socket_import_stream' => ['Socket|false|null', 'stream'=>'resource'], 'socket_last_error' => ['int', 'socket='=>'Socket|null'], 'socket_listen' => ['bool', 'socket'=>'Socket', 'backlog='=>'int'], -'socket_read' => ['string|false', 'socket'=>'Socket', 'length'=>'int', 'type='=>'int'], -'socket_recv' => ['int|false', 'socket'=>'Socket', '&w_buf'=>'string', 'length'=>'int', 'flags'=>'int'], -'socket_recvfrom' => ['int|false', 'socket'=>'Socket', '&w_buf'=>'string', 'length'=>'int', 'flags'=>'int', '&w_name'=>'string', '&w_port='=>'int'], +'socket_read' => ['string|false', 'socket'=>'Socket', 'length'=>'int', 'mode='=>'int'], +'socket_recv' => ['int|false', 'socket'=>'Socket', '&w_data'=>'string', 'length'=>'int', 'flags'=>'int'], +'socket_recvfrom' => ['int|false', 'socket'=>'Socket', '&w_data'=>'string', 'length'=>'int', 'flags'=>'int', '&w_address'=>'string', '&w_port='=>'int'], 'socket_recvmsg' => ['int|false', 'socket'=>'Socket', '&w_message'=>'array', 'flags='=>'int'], -'socket_select' => ['int|false', '&rw_read'=>'Socket[]|null', '&rw_write'=>'Socket[]|null', '&rw_except'=>'Socket[]|null', 'tv_sec'=>'int|null', 'tv_usec='=>'int'], -'socket_send' => ['int|false', 'socket'=>'Socket', 'buf'=>'string', 'length'=>'int', 'flags'=>'int'], +'socket_select' => ['int|false', '&rw_read'=>'Socket[]|null', '&rw_write'=>'Socket[]|null', '&rw_except'=>'Socket[]|null', 'seconds'=>'int|null', 'microseconds='=>'int'], +'socket_send' => ['int|false', 'socket'=>'Socket', 'data'=>'string', 'length'=>'int', 'flags'=>'int'], 'socket_sendmsg' => ['int|false', 'socket'=>'Socket', 'message'=>'array', 'flags='=>'int'], 'socket_sendto' => ['int|false', 'socket'=>'Socket', 'data'=>'string', 'length'=>'int', 'flags'=>'int', 'address'=>'string', 'port='=>'int|null'], 'socket_set_block' => ['bool', 'socket'=>'Socket'], -'socket_set_blocking' => ['bool', 'socket'=>'Socket', 'mode'=>'int'], +'socket_set_blocking' => ['bool', 'stream'=>'resource', 'enable'=>'bool'], 'socket_set_nonblock' => ['bool', 'socket'=>'Socket'], -'socket_set_option' => ['bool', 'socket'=>'Socket', 'level'=>'int', 'optname'=>'int', 'optval'=>'int|string|array'], +'socket_set_option' => ['bool', 'socket'=>'Socket', 'level'=>'int', 'option'=>'int', 'value'=>'int|string|array'], 'socket_set_timeout' => ['bool', 'stream'=>'resource', 'seconds'=>'int', 'microseconds='=>'int'], -'socket_setopt' => ['void', 'socket'=>'Socket', 'level'=>'int', 'optname'=>'int', 'optval'=>'int|string|array'], -'socket_shutdown' => ['bool', 'socket'=>'Socket', 'how='=>'int'], -'socket_strerror' => ['string', 'errno'=>'int'], +'socket_setopt' => ['void', 'socket'=>'Socket', 'level'=>'int', 'option'=>'int', 'value'=>'int|string|array'], +'socket_shutdown' => ['bool', 'socket'=>'Socket', 'mode='=>'int'], +'socket_strerror' => ['string', 'error_code'=>'int'], 'socket_write' => ['int|false', 'socket'=>'Socket', 'data'=>'string', 'length='=>'int|null'], 'socket_wsaprotocol_info_export' => ['string|false', 'socket'=>'Socket', 'process_id'=>'int'], 'socket_wsaprotocol_info_import' => ['Socket|false', 'info_id'=>'string'], @@ -13619,7 +13619,7 @@ 'sqlsrv_num_fields' => ['int|false', 'stmt'=>'resource'], 'sqlsrv_num_rows' => ['int|false', 'stmt'=>'resource'], 'sqlsrv_prepare' => ['resource|false', 'conn'=>'resource', 'sql'=>'string', 'params='=>'array', 'options='=>'array'], -'sqlsrv_query' => ['resource|false', 'conn'=>'resource', 'sql'=>'string', 'params='=>'array', 'options='=>'array'], +'sqlsrv_query' => ['resource|false', 'conn'=>'resource', 'tsql'=>'string', 'params='=>'array', 'options='=>'array'], 'sqlsrv_rollback' => ['bool', 'conn'=>'resource'], 'sqlsrv_rows_affected' => ['int|false', 'stmt'=>'resource'], 'sqlsrv_send_stream_data' => ['bool', 'stmt'=>'resource'], @@ -13904,7 +13904,7 @@ 'substr' => ['string|false', 'string'=>'string', 'offset'=>'int', 'length='=>'int|null'], 'substr_compare' => ['int|false', 'haystack'=>'string', 'needle'=>'string', 'offset'=>'int', 'length='=>'int|null', 'case_insensitive='=>'bool'], 'substr_count' => ['int', 'haystack'=>'string', 'needle'=>'string', 'offset='=>'int', 'length='=>'int|null'], -'substr_replace' => ['string|string[]', 'string'=>'string|string[]', 'replace'=>'mixed', 'offset'=>'mixed', 'length='=>'mixed'], +'substr_replace' => ['string|string[]', 'string'=>'string|string[]', 'replace'=>'string|string[]', 'offset'=>'int|int[]', 'length='=>'int|int[]|null'], 'suhosin_encrypt_cookie' => ['string|false', 'name'=>'string', 'value'=>'string'], 'suhosin_get_raw_cookies' => ['array'], 'SVM::__construct' => ['void'], diff --git a/tests/Internal/Codebase/InternalCallMapHandlerTest.php b/tests/Internal/Codebase/InternalCallMapHandlerTest.php index 3f2ed8f08a3..11acd484270 100644 --- a/tests/Internal/Codebase/InternalCallMapHandlerTest.php +++ b/tests/Internal/Codebase/InternalCallMapHandlerTest.php @@ -2,6 +2,7 @@ namespace Psalm\Tests\Internal\Codebase; +use Psalm\Codebase; use Psalm\Internal\Analyzer\ProjectAnalyzer; use Psalm\Internal\Codebase\InternalCallMapHandler; use Psalm\Internal\Codebase\Reflection; @@ -16,32 +17,83 @@ use ReflectionFunction; use ReflectionNamedType; use ReflectionParameter; +use ReflectionType; class InternalCallMapHandlerTest extends TestCase { - private $ignoredFunctions = [ + private static $ignoredFunctions = [ 'sprintf', 'printf', 'ctype_print', 'date_sunrise' /** deprecated in 8.1 */, - 'ctype_digit', 'ctype_lower', 'ctype_alnum', 'ctype_alpha', 'ctype_cntrl', - 'ctype_graph', 'ctype_lower', 'ctype_print', 'ctype_punct', 'ctype_space', 'ctype_upper', - 'ctype_xdigit', 'file_put_contents', 'sodium_crypto_generichash', 'sodium_crypto_generichash_final', - 'dom_import_simplexml', 'imagegd', 'imagegd2', 'pg_exec', 'mysqli_execute', 'array_multisort', - 'memcache_add_server', 'memcache_cas', 'memcache_pconnect', 'memcache_connect', 'memcache_delete', - 'memcache_replace', 'memcache_append', 'memcache_increment', 'memcache_set_failure_callback', 'memcache_decrement', - 'memcache_set_server_params', 'memcache_get_server_status', 'memcache_prepend', 'memcache_flush' + 'file_put_contents', + 'dom_import_simplexml', 'imagegd', 'imagegd2', 'mysqli_execute', 'array_multisort', + 'intlcal_from_date_time', 'simplexml_import_dom', 'imagefilledpolygon', + /** deprecated in 8.0 */ + 'zip_entry_close' ]; + private static $ignoredPrefixes = [ + 'memcache_', + 'gnupg_', + 'mailparse_', + 'xdiff_', + 'oci', + 'ldap_', + 'yaml_', + 'collator_', + 'ctype_', + 'imap_', + 'gmp_', + 'uopz_', + 'openssl_', + 'tidy_', + 'image', + 'transliterator_', + 'pg_', + 'odbc_', + 'sqlsrv_', + 'mysqli_', + 'apcu_', + 'bc', + 'sodium_', + 'intl', + ]; + + /** + * Initialized in setup + * @var string Regex + */ + private static $prefixRegex = '//'; + /** * * @var bool whether to skip params for which no definition can be found in the callMap */ private $skipUndefinedParams = false; - /** - * These are items that very likely need updating to PHP8.1 - * @var bool whether to skip params that are specified in the callmap as `resource` - */ - private $skipResources = false; + private static $codebase; + private static $callMap; + + + + + public static function setUpBeforeClass(): void + { + self::$prefixRegex = '/^(' . implode('|', self::$ignoredPrefixes) . ')/'; + $project_analyzer = new ProjectAnalyzer( + new TestConfig(), + new Providers( + new FakeFileProvider(), + new FakeParserCacheProvider() + ) + ); + self::$codebase = $project_analyzer->getCodebase(); + } + + public static function tearDownAfterClass(): void + { + self::$codebase = null; + + } /** * @covers \Psalm\Internal\Codebase\InternalCallMapHandler::getCallMap @@ -63,7 +115,6 @@ public function testGetcallmapReturnsAValidCallmap(): void public function callMapEntryProvider(): iterable { - $project_analyzer = new ProjectAnalyzer( new TestConfig(), new Providers( @@ -72,20 +123,16 @@ public function callMapEntryProvider(): iterable ) ); $callMap = InternalCallMapHandler::getCallMap(); - unset($project_analyzer); - foreach($callMap as $function => $entry) { // Skip class methods if (strpos($function, '::') !== false || !function_exists($function)) { continue; } - if (!str_starts_with($function, 'memcache_')) { - continue; - } // Skip functions with alternate signatures if (isset($callMap["$function'1"]) || preg_match("/\'\d$/", $function)) { continue; } + // if ($function != 'fprintf') continue; yield "$function: " . json_encode($entry) => [$function, $entry]; } @@ -104,6 +151,12 @@ public function testCallMapCompliesWithReflection(string $functionName, array $c if (!function_exists($functionName)) { $this->markTestSkipped("Function $functionName does not exist"); } + if (in_array($functionName, self::$ignoredFunctions)) { + $this->markTestSkipped("Function $functionName is ignored in config"); + } + if (preg_match(self::$prefixRegex, $functionName)) { + $this->markTestSkipped("Function $functionName has ignored prefix"); + } $this->assertEntryIsCorrect($callMapEntry, $functionName); } @@ -128,27 +181,35 @@ private function assertEntryIsCorrect(array $callMapEntry, string $functionName) 'byRef' => false, 'optional' => false, 'type' => $entry, - 'name' => $key ]; - // Strip prefixes. - if (strncmp($normalizedKey, '&rw_', 4) === 0) { - $normalizedEntry['byRef'] = true; - $normalizedEntry['refMode'] = 'rw'; - $normalizedKey = substr($normalizedKey, 4); - } elseif (strncmp($normalizedKey, '&w_', 3) === 0) { + if (strncmp($normalizedKey, '&', 1) === 0) { $normalizedEntry['byRef'] = true; - $normalizedEntry['refMode'] = 'w'; - $normalizedKey = substr($normalizedKey, 3); + $normalizedKey = substr($normalizedKey, 1); } + if (strncmp($normalizedKey, '...', 3) === 0) { $normalizedEntry['variadic'] = true; $normalizedKey = substr($normalizedKey, 3); } + + // Read the reference mode + if ($normalizedEntry['byRef']) { + $parts = explode('_', $normalizedKey, 2); + if (count($parts) === 2) { + $normalizedEntry['refMode'] = $parts[0]; + $normalizedKey = $parts[1]; + } else { + $normalizedEntry['refMode'] = 'rw'; + } + } + + // Strip prefixes. if (substr($normalizedKey, -1, 1) === "=") { $normalizedEntry['optional'] = true; $normalizedKey = substr($normalizedKey, 0, -1); } + $normalizedEntry['name'] = $normalizedKey; $normalizedEntries[$normalizedKey] = $normalizedEntry; } @@ -156,7 +217,7 @@ private function assertEntryIsCorrect(array $callMapEntry, string $functionName) if ($this->skipUndefinedParams && !isset($normalizedEntries[$parameter->getName()])) { continue; } else { - $this->assertArrayHasKey($parameter->getName(), $normalizedEntries); + $this->assertArrayHasKey($parameter->getName(), $normalizedEntries, "Callmap is missing entry for param {$parameter->getName()} in $functionName: " . print_r($normalizedEntries, true)); } $this->assertParameter($normalizedEntries[$parameter->getName()], $parameter, $functionName); } @@ -169,9 +230,6 @@ private function assertEntryIsCorrect(array $callMapEntry, string $functionName) */ private function assertParameter(array $normalizedEntry, ReflectionParameter $param, string $functionName): void { - if (in_array($functionName, $this->ignoredFunctions)) { - $this->markTestSkipped('Function is ignored in config'); - } $name = $param->getName(); // $identifier = "Param $functionName - $name"; try { @@ -179,93 +237,26 @@ private function assertParameter(array $normalizedEntry, ReflectionParameter $pa $this->assertSame($param->isVariadic(), $normalizedEntry['variadic'], "Expected param '{$name}' to " . ($param->isVariadic() ? "be" : "not be") . " variadic"); $this->assertSame($param->isPassedByReference(), $normalizedEntry['byRef'], "Expected param '{$name}' to " . ($param->isPassedByReference() ? "be" : "not be") . " by reference"); } catch(\Throwable $t) { - $this->markTestSkipped($t->getMessage()); + $this->markTestSkipped("Exception: " . $t->getMessage()); } $expectedType = $param->getType(); - if ($expectedType instanceof ReflectionNamedType) { + if (isset($expectedType)) { $this->assertTypeValidity($expectedType, $normalizedEntry['type'], "Param '{$name}' has incorrect type"); - } else { - // $this->markTestSkipped('Only simple named types are tested'); } - } /** * Since string equality is too strict, we do some extra checking here */ - private function assertTypeValidity(ReflectionNamedType $reflected, string $specified, string $message): void + private function assertTypeValidity(ReflectionType $reflected, string $specified, string $message): void { $expectedType = Reflection::getPsalmTypeFromReflectionType($reflected); $parsedType = Type::parseString($specified); - if (UnionTypeComparator::isContainedByInPhp($parsedType, $expectedType)) { - return; - } - - // In case reflection returns mixed we assume any type specified in the callmap is more specific and correct - if ($reflected->getName() === 'mixed') { - return; - } - - - if ($reflected->getName() === 'callable' && $reflected->allowsNull() - && preg_match('/^(null\|callable\(.*\):.*|callable\(.*\):.*\|null)$/', $specified) - ) { - return; - } - // Trim leading namespace separator - $specified = ltrim($specified, "\\"); - if ($reflected->getName() === 'array' && !$reflected->allowsNull()) { - if (preg_match('/^(array|list|non-empty-array)(<.*>|{.*})?$/', $specified) - || in_array($specified, ['string[]|int[]']) - ) { - return; - } - } elseif($reflected->getName() === 'array') { - // Optional array - if (preg_match('/^((array|list|non-empty-array)(<.*>|{.*})?\|null|null\|(array|list|non-empty-array)(<.*>|{.*})?)$/', $specified)) { - return; - } - } - - if ($reflected->getName() === 'float' && in_array($specified, ['int|float', 'float|int'])) { - return; - } - if ($reflected->getName() === 'bool' && in_array($specified, ['true', 'false'])) { - return; - } - if ($reflected->getName() === 'callable' && preg_match('/^callable\(/', $specified)) { - return; - } - - if ($reflected->getName() === 'string' && $specified === '?string' && $reflected->allowsNull()) { - return; - } - if ($reflected->getName() === 'string' && in_array($specified , ['class-string', 'numeric-string', 'string'])) { - return; - } - if ($reflected->getName() === 'int' && preg_match('/^(\d+|positive-int|int(<\d+,\d+>))?(\|(\d+|positive-int|int))*$/', $specified)) { - - // in_array($specified , [ - // 'positive-int', 'int', '0|positive-int', '256|512|1024|16384', '1|2|3|4|5|6|7' - // ])) { - return; - } - - - if ($reflected->allowsNull()) { - $escaped = preg_quote($reflected->getName()); - $this->assertMatchesRegularExpression("/^(\?{$escaped}|{$escaped}\|null|null\|{$escaped})$/", $specified, $message); - return; - } - - if ($this->skipResources && $specified === 'resource') { - return; - } - $this->assertEqualsIgnoringCase($reflected->getName(), $specified, $message); + $this->assertTrue(UnionTypeComparator::isContainedBy(self::$codebase, $parsedType, $expectedType), $message); } } From e826110417579bd9ed5343b7e3edcf6438e3e92c Mon Sep 17 00:00:00 2001 From: Sam Mousa Date: Wed, 15 Jun 2022 12:09:47 +0200 Subject: [PATCH 36/88] fix: ignore all faulty callmap entries and revert callmap changes --- dictionaries/CallMap.php | 573 +++++++++--------- dictionaries/CallMap_80_delta.php | 5 +- dictionaries/CallMap_81_delta.php | 7 - .../Codebase/InternalCallMapHandlerTest.php | 80 ++- 4 files changed, 349 insertions(+), 316 deletions(-) diff --git a/dictionaries/CallMap.php b/dictionaries/CallMap.php index efc7794a357..fc7fff7ea65 100644 --- a/dictionaries/CallMap.php +++ b/dictionaries/CallMap.php @@ -361,12 +361,12 @@ 'ArithmeticError::getTraceAsString' => ['string'], 'array_change_key_case' => ['associative-array', 'array'=>'array', 'case='=>'int'], 'array_chunk' => ['list', 'array'=>'array', 'length'=>'int', 'preserve_keys='=>'bool'], -'array_column' => ['array', 'array'=>'array', 'column_key'=>'int|string|null', 'index_key='=>'int|string|null'], +'array_column' => ['array', 'array'=>'array', 'column_key'=>'mixed', 'index_key='=>'mixed'], 'array_combine' => ['associative-array', 'keys'=>'string[]|int[]', 'values'=>'array'], 'array_count_values' => ['associative-array', 'array'=>'array'], -'array_diff' => ['associative-array', 'array'=>'array', '...arrays='=>'array'], -'array_diff_assoc' => ['associative-array', 'array'=>'array', '...arrays='=>'array'], -'array_diff_key' => ['associative-array', 'array'=>'array', '...arrays='=>'array'], +'array_diff' => ['associative-array', 'array'=>'array', 'arrays'=>'array', '...args='=>'array'], +'array_diff_assoc' => ['associative-array', 'array'=>'array', 'arrays'=>'array', '...args='=>'array'], +'array_diff_key' => ['associative-array', 'array'=>'array', 'arrays'=>'array', '...args='=>'array'], 'array_diff_uassoc' => ['associative-array', 'array'=>'array', 'rest'=>'array', 'data_comp_func'=>'callable(mixed,mixed):int'], 'array_diff_uassoc\'1' => ['associative-array', 'array'=>'array', 'rest'=>'array', 'arr3'=>'array', 'arg4'=>'array|callable(mixed,mixed):int', '...rest='=>'array|callable(mixed,mixed):int'], 'array_diff_ukey' => ['associative-array', 'array'=>'array', 'rest'=>'array', 'key_comp_func'=>'callable(mixed,mixed):int'], @@ -375,36 +375,36 @@ 'array_fill_keys' => ['array', 'keys'=>'array', 'value'=>'mixed'], 'array_filter' => ['associative-array', 'array'=>'array', 'callback='=>'callable(mixed,mixed=):scalar', 'mode='=>'int'], 'array_flip' => ['associative-array|associative-array', 'array'=>'array'], -'array_intersect' => ['associative-array', 'array'=>'array', '...arrays='=>'array'], -'array_intersect_assoc' => ['associative-array', 'array'=>'array', '...arrays='=>'array'], -'array_intersect_key' => ['associative-array', 'array'=>'array', '...arrays='=>'array'], +'array_intersect' => ['associative-array', 'array'=>'array', 'arrays'=>'array', '...args='=>'array'], +'array_intersect_assoc' => ['associative-array', 'array'=>'array', 'arrays'=>'array', '...args='=>'array'], +'array_intersect_key' => ['associative-array', 'array'=>'array', 'arrays'=>'array', '...args='=>'array'], 'array_intersect_uassoc' => ['associative-array', 'array'=>'array', 'rest'=>'array', 'key_compare_func'=>'callable(mixed,mixed):int'], 'array_intersect_uassoc\'1' => ['associative-array', 'array'=>'array', 'rest'=>'array', 'arr3'=>'array', 'arg4'=>'array|callable(mixed,mixed):int', '...rest'=>'array|callable(mixed,mixed):int'], 'array_intersect_ukey' => ['associative-array', 'array'=>'array', 'rest'=>'array', 'key_compare_func'=>'callable(mixed,mixed):int'], 'array_intersect_ukey\'1' => ['associative-array', 'array'=>'array', 'rest'=>'array', 'arr3'=>'array', 'arg4'=>'array|callable(mixed,mixed):int', '...rest'=>'array|callable(mixed,mixed):int'], 'array_is_list' => ['bool', 'array'=>'array'], -'array_key_exists' => ['bool', 'key'=>'string|int', 'array'=>'array'], +'array_key_exists' => ['bool', 'key'=>'string|int', 'array'=>'array|ArrayObject'], 'array_key_first' => ['int|string|null', 'array'=>'array'], 'array_key_last' => ['int|string|null', 'array'=>'array'], 'array_keys' => ['list', 'array'=>'array', 'filter_value='=>'mixed', 'strict='=>'bool'], 'array_map' => ['array', 'callback'=>'?callable', 'array'=>'array', '...arrays='=>'array'], -'array_merge' => ['array', '...arrays='=>'array'], -'array_merge_recursive' => ['array', '...arrays='=>'array'], -'array_multisort' => ['bool', '&rw_array'=>'array', 'array1_sort_order='=>'array|int', 'array1_sort_flags='=>'array|int', '...rest='=>'array|int'], +'array_merge' => ['array', 'arrays'=>'array', '...args='=>'array'], +'array_merge_recursive' => ['array', 'arrays'=>'array', '...args='=>'array'], +'array_multisort' => ['bool', '&rw_array'=>'array', 'rest='=>'array|int', 'array1_sort_flags='=>'array|int', '...args='=>'array|int'], 'array_pad' => ['array', 'array'=>'array', 'length'=>'int', 'value'=>'mixed'], 'array_pop' => ['mixed', '&rw_array'=>'array'], 'array_product' => ['int|float', 'array'=>'array'], -'array_push' => ['int', '&rw_array'=>'array', '...values='=>'mixed'], +'array_push' => ['int', '&rw_array'=>'array', 'values'=>'mixed', '...vars='=>'mixed'], 'array_rand' => ['int|string|array|array', 'array'=>'non-empty-array', 'num'=>'int'], 'array_rand\'1' => ['int|string', 'array'=>'array'], 'array_reduce' => ['mixed', 'array'=>'array', 'callback'=>'callable(mixed,mixed):mixed', 'initial='=>'mixed'], -'array_replace' => ['array', 'array'=>'array', '...replacements='=>'array'], -'array_replace_recursive' => ['array', 'array'=>'array', '...replacements='=>'array'], +'array_replace' => ['array', 'array'=>'array', 'replacements'=>'array', '...args='=>'array'], +'array_replace_recursive' => ['array', 'array'=>'array', 'replacements'=>'array', '...args='=>'array'], 'array_reverse' => ['array', 'array'=>'array', 'preserve_keys='=>'bool'], 'array_search' => ['int|string|false', 'needle'=>'mixed', 'haystack'=>'array', 'strict='=>'bool'], 'array_shift' => ['mixed|null', '&rw_array'=>'array'], 'array_slice' => ['array', 'array'=>'array', 'offset'=>'int', 'length='=>'?int', 'preserve_keys='=>'bool'], -'array_splice' => ['array', '&rw_array'=>'array', 'offset'=>'int', 'length='=>'int|null', 'replacement='=>'mixed'], +'array_splice' => ['array', '&rw_array'=>'array', 'offset'=>'int', 'length='=>'int', 'replacement='=>'array|string'], 'array_sum' => ['int|float', 'array'=>'array'], 'array_udiff' => ['associative-array', 'array'=>'array', 'rest'=>'array', 'data_comp_func'=>'callable(mixed,mixed):int'], 'array_udiff\'1' => ['associative-array', 'array'=>'array', 'rest'=>'array', 'arr3'=>'array', 'arg4'=>'array|callable(mixed,mixed):int', '...rest='=>'array|callable(mixed,mixed):int'], @@ -421,7 +421,7 @@ 'array_unique' => ['array', 'array'=>'array', 'flags='=>'0'], 'array_unique\'1' => ['array', 'array'=>'array', 'flags='=>'1'], 'array_unique\'2' => ['array', 'array'=>'array', 'flags='=>'2|5'], -'array_unshift' => ['int', '&rw_array'=>'array', '...values='=>'mixed'], +'array_unshift' => ['int', '&rw_array'=>'array', 'values'=>'mixed', '...vars='=>'mixed'], 'array_values' => ['list', 'array'=>'array'], 'array_walk' => ['bool', '&rw_array'=>'array', 'callback'=>'callable', 'arg='=>'mixed'], 'array_walk\'1' => ['bool', '&rw_array'=>'object', 'callback'=>'callable', 'arg='=>'mixed'], @@ -527,7 +527,7 @@ 'bcadd' => ['numeric-string', 'num1'=>'numeric-string', 'num2'=>'numeric-string', 'scale='=>'int|null'], 'bccomp' => ['int', 'num1'=>'numeric-string', 'num2'=>'numeric-string', 'scale='=>'int|null'], 'bcdiv' => ['numeric-string|null', 'dividend'=>'numeric-string', 'divisor'=>'numeric-string', 'scale='=>'int|null'], -'bcmod' => ['numeric-string|null', 'num1'=>'numeric-string', 'num2'=>'numeric-string', 'scale='=>'int|null'], +'bcmod' => ['numeric-string|null', 'dividend'=>'numeric-string', 'divisor'=>'numeric-string', 'scale='=>'int|null'], 'bcmul' => ['numeric-string', 'num1'=>'numeric-string', 'num2'=>'numeric-string', 'scale='=>'int|null'], 'bcompiler_load' => ['bool', 'filename'=>'string'], 'bcompiler_load_exe' => ['bool', 'filename'=>'string'], @@ -569,14 +569,14 @@ 'bson_encode' => ['string', 'anything'=>'mixed'], 'bzclose' => ['bool', 'bz'=>'resource'], 'bzcompress' => ['string|int', 'data'=>'string', 'block_size='=>'int', 'work_factor='=>'int'], -'bzdecompress' => ['string|int', 'data'=>'string', 'use_less_memory='=>'bool'], +'bzdecompress' => ['string|int', 'data'=>'string', 'use_less_memory='=>'int'], 'bzerrno' => ['int', 'bz'=>'resource'], 'bzerror' => ['array', 'bz'=>'resource'], 'bzerrstr' => ['string', 'bz'=>'resource'], 'bzflush' => ['bool', 'bz'=>'resource'], 'bzopen' => ['resource|false', 'file'=>'string|resource', 'mode'=>'string'], 'bzread' => ['string|false', 'bz'=>'resource', 'length='=>'int'], -'bzwrite' => ['int|false', 'bz'=>'resource', 'data'=>'string', 'length='=>'int|null'], +'bzwrite' => ['int|false', 'bz'=>'resource', 'data'=>'string', 'length='=>'int'], 'CachingIterator::__construct' => ['void', 'iterator'=>'Iterator', 'flags='=>''], 'CachingIterator::__toString' => ['string'], 'CachingIterator::count' => ['int'], @@ -1543,7 +1543,7 @@ 'Couchbase\WildcardSearchQuery::jsonSerialize' => ['array'], 'Couchbase\zlibCompress' => ['string', 'data'=>'string'], 'Couchbase\zlibDecompress' => ['string', 'data'=>'string'], -'count' => ['int', 'value'=>'Countable|array ', 'mode='=>'int'], +'count' => ['int', 'value'=>'Countable|array|SimpleXMLElement|ResourceBundle', 'mode='=>'int'], 'count_chars' => ['array', 'input'=>'string', 'mode='=>'0|1|2'], 'count_chars\'1' => ['string', 'input'=>'string', 'mode='=>'3|4'], 'Countable::count' => ['int'], @@ -1553,8 +1553,7 @@ 'crack_opendict' => ['resource|false', 'dictionary'=>'string'], 'crash' => [''], 'crc32' => ['int', 'string'=>'string'], -// While technically optional calling this without salt is bad and creates an E_NOTICE -'crypt' => ['string', 'string'=>'string', 'salt'=>'string'], +'crypt' => ['string', 'string'=>'string', 'salt='=>'string'], 'ctype_alnum' => ['bool', 'text'=>'string|int'], 'ctype_alpha' => ['bool', 'text'=>'string|int'], 'ctype_cntrl' => ['bool', 'text'=>'string|int'], @@ -1673,7 +1672,7 @@ 'curl_escape' => ['string|false', 'handle'=>'CurlHandle', 'string'=>'string'], 'curl_exec' => ['bool|string', 'handle'=>'CurlHandle'], 'curl_file_create' => ['CURLFile', 'filename'=>'string', 'mime_type='=>'string|null', 'posted_filename='=>'string|null'], -'curl_getinfo' => ['mixed', 'handle'=>'CurlHandle', 'option='=>'int|null'], +'curl_getinfo' => ['mixed', 'handle'=>'CurlHandle', 'option='=>'int'], 'curl_init' => ['CurlHandle|false', 'url='=>'string'], 'curl_multi_add_handle' => ['int', 'multi_handle'=>'CurlMultiHandle', 'handle'=>'CurlHandle'], 'curl_multi_close' => ['void', 'multi_handle'=>'CurlMultiHandle'], @@ -1696,7 +1695,7 @@ 'curl_share_setopt' => ['bool', 'share_handle'=>'CurlShareHandle', 'option'=>'int', 'value'=>'mixed'], 'curl_share_strerror' => ['?string', 'error_code'=>'int'], 'curl_strerror' => ['?string', 'error_code'=>'int'], -'curl_unescape' => ['string|false', 'handle'=>'CurlHandle', 'string'=>'string'], +'curl_unescape' => ['string|false', 'handle'=>'CurlShareHandle', 'string'=>'string'], 'curl_version' => ['array', 'version='=>'int'], 'CURLFile::__construct' => ['void', 'filename'=>'string', 'mimetype='=>'string', 'postfilename='=>'string'], 'CURLFile::__wakeup' => ['void'], @@ -1716,7 +1715,7 @@ 'date' => ['string', 'format'=>'string', 'timestamp='=>'?int'], 'date_add' => ['DateTime|false', 'object'=>'DateTime', 'interval'=>'DateInterval'], 'date_create' => ['DateTime|false', 'datetime='=>'string', 'timezone='=>'?DateTimeZone'], -'date_create_from_format' => ['DateTime|false', 'format'=>'string', 'datetime'=>'string', 'timezone='=>'\DateTimeZone|null'], +'date_create_from_format' => ['DateTime|false', 'format'=>'string', 'datetime'=>'string', 'timezone='=>'?\DateTimeZone'], 'date_create_immutable' => ['DateTimeImmutable|false', 'datetime='=>'string', 'timezone='=>'?DateTimeZone'], 'date_create_immutable_from_format' => ['DateTimeImmutable|false', 'format'=>'string', 'datetime'=>'string', 'timezone='=>'?DateTimeZone'], 'date_date_set' => ['DateTime|false', 'object'=>'DateTime', 'year'=>'int', 'month'=>'int', 'day'=>'int'], @@ -1727,7 +1726,7 @@ 'date_get_last_errors' => ['array{warning_count:int,warnings:array,error_count:int,errors:array}'], 'date_interval_create_from_date_string' => ['DateInterval', 'datetime'=>'string'], 'date_interval_format' => ['string', 'object'=>'DateInterval', 'format'=>'string'], -'date_isodate_set' => ['DateTime|false', 'object'=>'DateTime', 'year'=>'int', 'week'=>'int', 'dayOfWeek='=>'1|2|3|4|5|6|7'], +'date_isodate_set' => ['DateTime|false', 'object'=>'DateTime', 'year'=>'int', 'week'=>'int', 'dayOfWeek='=>'int|mixed'], 'date_modify' => ['DateTime|false', 'object'=>'DateTime', 'modifier'=>'string'], 'date_offset_get' => ['int|false', 'object'=>'DateTimeInterface'], 'date_parse' => ['array|false', 'datetime'=>'string'], @@ -1735,13 +1734,13 @@ 'date_sub' => ['DateTime|false', 'object'=>'DateTime', 'interval'=>'DateInterval'], 'date_sun_info' => ['array|false', 'timestamp'=>'int', 'latitude'=>'float', 'longitude'=>'float'], 'date_sunrise' => ['mixed', 'timestamp'=>'int', 'returnFormat='=>'int', 'latitude='=>'float', 'longitude='=>'float', 'zenith='=>'float', 'utcOffset='=>'float'], -'date_sunset' => ['string|int|float|false', 'timestamp'=>'int', 'returnFormat='=>'int', 'latitude='=>'float|null', 'longitude='=>'float|null', 'zenith='=>'float|null', 'utcOffset='=>'float|null'], -'date_time_set' => ['DateTime|false', 'object'=>'DateTime', 'hour'=>'int', 'minute'=>'int', 'second='=>'int', 'microsecond='=>'int'], +'date_sunset' => ['mixed', 'timestamp'=>'int', 'returnFormat='=>'int', 'latitude='=>'float', 'longitude='=>'float', 'zenith='=>'float', 'utcOffset='=>'float'], +'date_time_set' => ['DateTime|false', 'object'=>'', 'hour'=>'', 'minute'=>'', 'second='=>'', 'microsecond='=>''], 'date_timestamp_get' => ['int', 'object'=>'DateTimeInterface'], 'date_timestamp_set' => ['DateTime|false', 'object'=>'DateTime', 'timestamp'=>'int'], 'date_timezone_get' => ['DateTimeZone|false', 'object'=>'DateTimeInterface'], 'date_timezone_set' => ['DateTime|false', 'object'=>'DateTime', 'timezone'=>'DateTimeZone'], -'datefmt_create' => ['IntlDateFormatter|false', 'locale'=>'?string', 'dateType'=>'int', 'timeType'=>'int', 'timezone='=>'string|DateTimeZone|IntlTimeZone|null', 'calendar='=>'int|IntlCalendar|null', 'pattern='=>'string'], +'datefmt_create' => ['IntlDateFormatter|false', 'locale'=>'?string', 'dateType'=>'?int', 'timeType'=>'?int', 'timezone='=>'string|DateTimeZone|IntlTimeZone|null', 'calendar='=>'int|IntlCalendar|null', 'pattern='=>'string'], 'datefmt_format' => ['string|false', 'formatter'=>'IntlDateFormatter', 'datetime'=>'DateTime|IntlCalendar|array|int'], 'datefmt_format_object' => ['string|false', 'datetime'=>'object', 'format='=>'mixed', 'locale='=>'string'], 'datefmt_get_calendar' => ['int', 'formatter'=>'IntlDateFormatter'], @@ -1752,15 +1751,15 @@ 'datefmt_get_locale' => ['string|false', 'formatter'=>'IntlDateFormatter', 'type='=>'int'], 'datefmt_get_pattern' => ['string', 'formatter'=>'IntlDateFormatter'], 'datefmt_get_timetype' => ['int', 'formatter'=>'IntlDateFormatter'], -'datefmt_get_timezone' => ['IntlTimeZone|false', 'formatter'=>'IntlDateFormatter'], -'datefmt_get_timezone_id' => ['string|false', 'formatter'=>'IntlDateFormatter'], +'datefmt_get_timezone' => ['IntlTimeZone|false'], +'datefmt_get_timezone_id' => ['string', 'formatter'=>'IntlDateFormatter'], 'datefmt_is_lenient' => ['bool', 'formatter'=>'IntlDateFormatter'], -'datefmt_localtime' => ['array|false', 'formatter'=>'IntlDateFormatter', 'string'=>'string', '&rw_offset='=>'int'], -'datefmt_parse' => ['int|false', 'formatter'=>'IntlDateFormatter', 'string'=>'string', '&rw_offset='=>'int'], +'datefmt_localtime' => ['array|false', 'formatter'=>'IntlDateFormatter', 'string='=>'string', '&rw_offset='=>'int'], +'datefmt_parse' => ['int|false', 'formatter'=>'IntlDateFormatter', 'string='=>'string', '&rw_offset='=>'int'], 'datefmt_set_calendar' => ['bool', 'formatter'=>'IntlDateFormatter', 'calendar'=>'int'], 'datefmt_set_lenient' => ['?bool', 'formatter'=>'IntlDateFormatter', 'lenient'=>'bool'], 'datefmt_set_pattern' => ['bool', 'formatter'=>'IntlDateFormatter', 'pattern'=>'string'], -'datefmt_set_timezone' => ['bool|null', 'formatter'=>'IntlDateFormatter', 'timezone' => 'IntlTimeZone|DateTimeZone|string|null'], +'datefmt_set_timezone' => ['bool', 'formatter'=>'mixed'], 'datefmt_set_timezone_id' => ['bool', 'fmt'=>'IntlDateFormatter', 'zone'=>'string'], 'DateInterval::__construct' => ['void', 'spec'=>'string'], 'DateInterval::__set_state' => ['DateInterval', 'array'=>'array'], @@ -1974,7 +1973,7 @@ 'deaggregate' => ['', 'object'=>'object', 'class_name='=>'string'], 'debug_backtrace' => ['list', 'options='=>'int', 'limit='=>'int'], 'debug_print_backtrace' => ['void', 'options='=>'int', 'limit='=>'int'], -'debug_zval_dump' => ['void', 'value' => 'mixed', '...values='=>'mixed'], +'debug_zval_dump' => ['void', '...value'=>'mixed'], 'debugger_connect' => [''], 'debugger_connector_pid' => [''], 'debugger_get_server_start_time' => [''], @@ -1986,7 +1985,7 @@ 'define' => ['bool', 'constant_name'=>'string', 'value'=>'mixed', 'case_insensitive='=>'bool'], 'define_syslog_variables' => ['void'], 'defined' => ['bool', 'constant_name'=>'string'], -'deflate_add' => ['string|false', 'context'=>'DeflateContext', 'data'=>'string', 'flush_mode='=>'int'], +'deflate_add' => ['string|false', 'context'=>'resource', 'data'=>'string', 'flush_mode='=>'int'], 'deflate_init' => ['resource|false', 'encoding'=>'int', 'options='=>'array'], 'deg2rad' => ['float', 'num'=>'float'], 'dgettext' => ['string', 'domain'=>'string', 'message'=>'string'], @@ -2047,7 +2046,7 @@ 'dl' => ['bool', 'extension_filename'=>'string'], 'dngettext' => ['string', 'domain'=>'string', 'singular'=>'string', 'plural'=>'string', 'count'=>'int'], 'dns_check_record' => ['bool', 'hostname'=>'string', 'type='=>'string'], -'dns_get_mx' => ['bool', 'hostname'=>'string', '&w_hosts'=>'array', '&w_weights='=>'array|null'], +'dns_get_mx' => ['bool', 'hostname'=>'string', '&w_hosts'=>'array', '&w_weights'=>'array'], 'dns_get_record' => ['list|false', 'hostname'=>'string', 'type='=>'int', '&w_authoritative_name_servers='=>'array', '&w_additional_records='=>'array', 'raw='=>'bool'], 'dom_document_relaxNG_validate_file' => ['bool', 'filename'=>'string'], 'dom_document_relaxNG_validate_xml' => ['bool', 'source'=>'string'], @@ -2432,8 +2431,8 @@ 'Ds\Vector::sum' => ['int|float'], 'Ds\Vector::toArray' => ['array'], 'Ds\Vector::unshift' => ['void', '...values='=>'mixed'], -'easter_date' => ['int', 'year='=>'int|null', 'mode=' => 'int'], -'easter_days' => ['int', 'year='=>'int|null', 'mode='=>'int'], +'easter_date' => ['int', 'year='=>'int'], +'easter_days' => ['int', 'year='=>'int', 'mode='=>'int'], 'echo' => ['void', 'arg1'=>'string', '...args='=>'string'], 'eio_busy' => ['resource', 'delay'=>'int', 'pri='=>'int', 'callback='=>'callable', 'data='=>'mixed'], 'eio_cancel' => ['void', 'req'=>'resource'], @@ -2500,29 +2499,29 @@ 'EmptyIterator::next' => ['void'], 'EmptyIterator::rewind' => ['void'], 'EmptyIterator::valid' => ['bool'], -'enchant_broker_describe' => ['array', 'broker'=>'EnchantBroker'], -'enchant_broker_dict_exists' => ['bool', 'broker'=>'EnchantBroker', 'tag'=>'string'], -'enchant_broker_free' => ['bool', 'broker'=>'EnchantBroker'], -'enchant_broker_free_dict' => ['bool', 'dictionary'=>'EnchantDictionary'], -'enchant_broker_get_dict_path' => ['string', 'broker'=>'EnchantBroker', 'type'=>'int'], -'enchant_broker_get_error' => ['string|false', 'broker'=>'EnchantBroker'], -'enchant_broker_init' => ['EnchantBroker|false'], -'enchant_broker_list_dicts' => ['array|false', 'broker'=>'EnchantBroker'], -'enchant_broker_request_dict' => ['resource|false', 'broker'=>'EnchantBroker', 'tag'=>'string'], -'enchant_broker_request_pwl_dict' => ['resource|false', 'broker'=>'EnchantBroker', 'filename'=>'string'], -'enchant_broker_set_dict_path' => ['bool', 'broker'=>'EnchantBroker', 'type'=>'int', 'path'=>'string'], -'enchant_broker_set_ordering' => ['bool', 'broker'=>'EnchantBroker', 'tag'=>'string', 'ordering'=>'string'], -'enchant_dict_add_to_personal' => ['void', 'dictionary'=>'EnchantDictionary', 'word'=>'string'], -'enchant_dict_add_to_session' => ['void', 'dictionary'=>'EnchantDictionary', 'word'=>'string'], -'enchant_dict_check' => ['bool', 'dictionary'=>'EnchantDictionary', 'word'=>'string'], -'enchant_dict_describe' => ['array', 'dictionary'=>'EnchantDictionary'], -'enchant_dict_get_error' => ['string', 'dictionary'=>'EnchantDictionary'], -'enchant_dict_is_in_session' => ['bool', 'dictionary'=>'EnchantDictionary', 'word'=>'string'], -'enchant_dict_quick_check' => ['bool', 'dictionary'=>'EnchantDictionary', 'word'=>'string', '&w_suggestions='=>'array'], -'enchant_dict_store_replacement' => ['void', 'dictionary'=>'EnchantDictionary', 'misspelled'=>'string', 'correct'=>'string'], -'enchant_dict_suggest' => ['array', 'dictionary'=>'EnchantDictionary', 'word'=>'string'], +'enchant_broker_describe' => ['array', 'broker'=>'resource'], +'enchant_broker_dict_exists' => ['bool', 'broker'=>'resource', 'tag'=>'string'], +'enchant_broker_free' => ['bool', 'broker'=>'resource'], +'enchant_broker_free_dict' => ['bool', 'dictionary'=>'resource'], +'enchant_broker_get_dict_path' => ['string', 'broker'=>'resource', 'type'=>'int'], +'enchant_broker_get_error' => ['string|false', 'broker'=>'resource'], +'enchant_broker_init' => ['resource|false'], +'enchant_broker_list_dicts' => ['array|false', 'broker'=>'resource'], +'enchant_broker_request_dict' => ['resource|false', 'broker'=>'resource', 'tag'=>'string'], +'enchant_broker_request_pwl_dict' => ['resource|false', 'broker'=>'resource', 'filename'=>'string'], +'enchant_broker_set_dict_path' => ['bool', 'broker'=>'resource', 'type'=>'int', 'path'=>'string'], +'enchant_broker_set_ordering' => ['bool', 'broker'=>'resource', 'tag'=>'string', 'ordering'=>'string'], +'enchant_dict_add_to_personal' => ['void', 'dictionary'=>'resource', 'word'=>'string'], +'enchant_dict_add_to_session' => ['void', 'dictionary'=>'resource', 'word'=>'string'], +'enchant_dict_check' => ['bool', 'dictionary'=>'resource', 'word'=>'string'], +'enchant_dict_describe' => ['array', 'dictionary'=>'resource'], +'enchant_dict_get_error' => ['string', 'dictionary'=>'resource'], +'enchant_dict_is_in_session' => ['bool', 'dictionary'=>'resource', 'word'=>'string'], +'enchant_dict_quick_check' => ['bool', 'dictionary'=>'resource', 'word'=>'string', '&w_suggestions='=>'array'], +'enchant_dict_store_replacement' => ['void', 'dictionary'=>'resource', 'misspelled'=>'string', 'correct'=>'string'], +'enchant_dict_suggest' => ['array', 'dictionary'=>'resource', 'word'=>'string'], 'end' => ['mixed|false', '&r_array'=>'array|object'], -'enum_exists' => ['bool', 'enum' => 'class-string', 'autoload=' => 'bool'], +'enum_exists' => ['bool', 'class' => 'class-string', 'autoload=' => 'bool'], 'Error::__clone' => ['void'], 'Error::__construct' => ['void', 'message='=>'string', 'code='=>'int', 'previous='=>'?Throwable|?Error'], 'Error::__toString' => ['string'], @@ -2536,7 +2535,7 @@ 'error_clear_last' => ['void'], '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|null'], +'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'], 'ErrorException::__toString' => ['string'], @@ -2928,7 +2927,7 @@ 'explode' => ['list', 'separator'=>'string', 'string'=>'string', 'limit='=>'int'], 'expm1' => ['float', 'num'=>'float'], 'extension_loaded' => ['bool', 'extension'=>'string'], -'extract' => ['int', '&rw_array'=>'array', 'flags='=>'int', 'prefix='=>'string'], +'extract' => ['int', '&rw_array'=>'array', 'flags='=>'int', 'prefix='=>'?string'], 'ezmlm_hash' => ['int', 'addr'=>'string'], 'fam_cancel_monitor' => ['bool', 'fam'=>'resource', 'fam_monitor'=>'resource'], 'fam_close' => ['void', 'fam'=>'resource'], @@ -3226,11 +3225,12 @@ 'ffmpeg_movie::hasAudio' => ['bool'], 'ffmpeg_movie::hasVideo' => ['bool'], 'fgetc' => ['string|false', 'stream'=>'resource'], -'fgetcsv' => ['list|array{0: null}|false|null', 'stream'=>'resource', 'length='=>'int|null', 'separator='=>'string', 'enclosure='=>'string', 'escape='=>'string'], -'fgets' => ['string|false', 'stream'=>'resource', 'length='=>'int|null'], +'fgetcsv' => ['list|array{0: null}|false|null', 'stream'=>'resource', 'length='=>'int', 'separator='=>'string', 'enclosure='=>'string', 'escape='=>'string'], +'fgets' => ['string|false', 'stream'=>'resource', 'length='=>'int'], +'fgetss' => ['string|false', 'fp'=>'resource', 'length='=>'int', 'allowable_tags='=>'string'], 'file' => ['list|false', 'filename'=>'string', 'flags='=>'int', 'context='=>'resource'], 'file_exists' => ['bool', 'filename'=>'string'], -'file_get_contents' => ['string|false', 'filename'=>'string', 'use_include_path='=>'bool', 'context='=>'?resource', 'offset='=>'int', 'length='=>'int|null'], +'file_get_contents' => ['string|false', 'filename'=>'string', 'use_include_path='=>'bool', 'context='=>'?resource', 'offset='=>'int', 'length='=>'int'], 'file_put_contents' => ['int|false', 'filename'=>'string', 'data'=>'string|resource|array', 'flags='=>'int', 'context='=>'resource'], 'fileatime' => ['int|false', 'filename'=>'string'], 'filectime' => ['int|false', 'filename'=>'string'], @@ -3292,8 +3292,8 @@ 'filter_input' => ['mixed|false', 'type'=>'int', 'var_name'=>'string', 'filter='=>'int', 'options='=>'array|int'], 'filter_input_array' => ['mixed|false', 'type'=>'int', 'options='=>'int|array', 'add_empty='=>'bool'], 'filter_list' => ['array'], -'filter_var' => ['mixed|false', 'value'=>'mixed', 'filter='=>'int', 'options='=>'array|int'], -'filter_var_array' => ['array|null|false', 'array'=>'array', 'options='=>'array|int', 'add_empty='=>'bool'], +'filter_var' => ['mixed|false', 'value'=>'mixed', 'filter='=>'int', 'options='=>'mixed'], +'filter_var_array' => ['mixed|false', 'array'=>'array', 'options='=>'mixed', 'add_empty='=>'bool'], 'FilterIterator::__construct' => ['void', 'iterator'=>'Iterator'], 'FilterIterator::accept' => ['bool'], 'FilterIterator::current' => ['mixed'], @@ -3324,15 +3324,15 @@ 'fpassthru' => ['int|false', 'stream'=>'resource'], 'fpm_get_status' => ['array|false'], 'fprintf' => ['int', 'stream'=>'resource', 'format'=>'string', '...values='=>'string|int|float'], -'fputcsv' => ['int|false', 'stream'=>'resource', 'fields'=>'array', 'separator='=>'string', 'enclosure='=>'string', 'escape='=>'string', 'eol' => 'string'], -'fputs' => ['int|false', 'stream'=>'resource', 'data'=>'string', 'length='=>'int|null'], +'fputcsv' => ['int|false', 'stream'=>'resource', 'fields'=>'array', 'separator='=>'string', 'enclosure='=>'string', 'escape='=>'string'], +'fputs' => ['int|false', 'stream'=>'resource', 'data'=>'string', 'length='=>'int'], 'fread' => ['string|false', 'stream'=>'resource', 'length'=>'int'], 'frenchtojd' => ['int', 'month'=>'int', 'day'=>'int', 'year'=>'int'], 'fribidi_log2vis' => ['string', 'string'=>'string', 'direction'=>'string', 'charset'=>'int'], 'fscanf' => ['list', 'stream'=>'resource', 'format'=>'string'], 'fscanf\'1' => ['int', 'stream'=>'resource', 'format'=>'string', '&...w_vars='=>'string|int|float'], 'fseek' => ['int', 'stream'=>'resource', 'offset'=>'int', 'whence='=>'int'], -'fsockopen' => ['resource|false', 'hostname'=>'string', 'port='=>'int', '&w_error_code='=>'int|null', '&w_error_message='=>'string|null', 'timeout='=>'float|null'], +'fsockopen' => ['resource|false', 'hostname'=>'string', 'port='=>'int', '&w_error_code='=>'int', '&w_error_message='=>'string', 'timeout='=>'float'], 'fstat' => ['array{0: int, 1: int, 2: int, 3: int, 4: int, 5: int, 6: int, 7: int, 8: int, 9: int, 10: int, 11: int, 12: int, dev: int, ino: int, mode: int, nlink: int, uid: int, gid: int, rdev: int, size: int, atime: int, mtime: int, ctime: int, blksize: int, blocks: int}|false', 'stream'=>'resource'], 'ftell' => ['int|false', 'stream'=>'resource'], 'ftok' => ['int', 'filename'=>'string', 'project_id'=>'string'], @@ -3377,7 +3377,7 @@ 'func_get_args' => ['list'], 'func_num_args' => ['int'], 'function_exists' => ['bool', 'function'=>'string'], -'fwrite' => ['int|false', 'stream'=>'resource', 'data'=>'string', 'length='=>'int|null'], +'fwrite' => ['int|false', 'stream'=>'resource', 'data'=>'string', 'length='=>'int'], 'Fiber::__construct' => ['void', 'callback'=>'callable'], 'Fiber::start' => ['mixed', '...args'=>'mixed'], 'Fiber::resume' => ['mixed', 'value='=>'null|mixed'], @@ -3724,7 +3724,7 @@ 'get_called_class' => ['class-string'], 'get_cfg_var' => ['string|false', 'option'=>'string'], 'get_class' => ['class-string', 'object='=>'object'], -'get_class_methods' => ['list|null', 'object_or_class'=>'object|class-string'], +'get_class_methods' => ['list|null', 'object_or_class'=>'mixed'], 'get_class_vars' => ['array', 'class'=>'string'], 'get_current_user' => ['string'], 'get_debug_type' => ['string', 'value'=>'mixed'], @@ -3735,7 +3735,7 @@ 'get_defined_functions' => ['array>', 'exclude_disabled='=>'bool'], 'get_defined_vars' => ['array'], 'get_extension_funcs' => ['list|false', 'extension'=>'string'], -'get_headers' => ['array|false', 'url'=>'string', 'associative='=>'bool', 'context='=>'?resource'], +'get_headers' => ['array|false', 'url'=>'string', 'associative='=>'int', 'context='=>'resource'], 'get_html_translation_table' => ['array', 'table='=>'int', 'flags='=>'int', 'encoding='=>'string'], 'get_include_path' => ['string'], 'get_included_files' => ['list'], @@ -3744,14 +3744,14 @@ 'get_magic_quotes_runtime' => ['int|false'], 'get_meta_tags' => ['array', 'filename'=>'string', 'use_include_path='=>'bool'], 'get_object_vars' => ['array', 'object'=>'object'], -'get_parent_class' => ['class-string|false', 'object_or_class='=>'object|class-string'], +'get_parent_class' => ['class-string|false', 'object_or_class='=>'mixed'], 'get_required_files' => ['list'], 'get_resource_id' => ['int', 'resource'=>'resource'], 'get_resource_type' => ['string', 'resource'=>'resource'], 'get_resources' => ['array', 'type='=>'string'], 'getallheaders' => ['array|false'], 'getcwd' => ['string|false'], -'getdate' => ['array{seconds: int<0, 59>, minutes: int<0, 59>, hours: int<0, 23>, mday: int<1, 31>, wday: int<0, 6>, mon: int<1, 12>, year: int, yday: int<0, 365>, weekday: "Monday"|"Tuesday"|"Wednesday"|"Thursday"|"Friday"|"Saturday"|"Sunday", month: "January"|"February"|"March"|"April"|"May"|"June"|"July"|"August"|"September"|"October"|"November"|"December", 0: int}', 'timestamp='=>'int|null'], +'getdate' => ['array{seconds: int<0, 59>, minutes: int<0, 59>, hours: int<0, 23>, mday: int<1, 31>, wday: int<0, 6>, mon: int<1, 12>, year: int, yday: int<0, 365>, weekday: "Monday"|"Tuesday"|"Wednesday"|"Thursday"|"Friday"|"Saturday"|"Sunday", month: "January"|"February"|"March"|"April"|"May"|"June"|"July"|"August"|"September"|"October"|"November"|"December", 0: int}', 'timestamp='=>'int'], 'getenv' => ['string|false', 'name'=>'string', 'local_only='=>'bool'], 'getenv\'1' => ['array'], 'gethostbyaddr' => ['string|false', 'ip'=>'string'], @@ -4007,7 +4007,7 @@ 'gmp_add' => ['GMP', 'num1'=>'GMP|string|int', 'num2'=>'GMP|string|int'], 'gmp_and' => ['GMP', 'num1'=>'GMP|string|int', 'num2'=>'GMP|string|int'], 'gmp_binomial' => ['GMP', 'n'=>'GMP|string|int', 'k'=>'int'], -'gmp_clrbit' => ['void', 'num'=>'GMP', 'index'=>'int'], +'gmp_clrbit' => ['void', 'num'=>'GMP|string|int', 'index'=>'int'], 'gmp_cmp' => ['int', 'num1'=>'GMP|string|int', 'num2'=>'GMP|string|int'], 'gmp_com' => ['GMP', 'num'=>'GMP|string|int'], 'gmp_div' => ['GMP', 'num1'=>'GMP|resource|string', 'num2'=>'GMP|resource|string', 'rounding_mode='=>'int'], @@ -4046,7 +4046,7 @@ 'gmp_rootrem' => ['array{0: GMP, 1: GMP}', 'num'=>'GMP|string|int', 'nth'=>'int'], 'gmp_scan0' => ['int', 'num1'=>'GMP|string|int', 'start'=>'int'], 'gmp_scan1' => ['int', 'num1'=>'GMP|string|int', 'start'=>'int'], -'gmp_setbit' => ['void', 'num'=>'GMP', 'index'=>'int', 'value='=>'bool'], +'gmp_setbit' => ['void', 'num'=>'GMP|string|int', 'index'=>'int', 'value='=>'bool'], 'gmp_sign' => ['int', 'num'=>'GMP|string|int'], 'gmp_sqrt' => ['GMP', 'num'=>'GMP|string|int'], 'gmp_sqrtrem' => ['array{0: GMP, 1: GMP}', 'num'=>'GMP|string|int'], @@ -4106,7 +4106,7 @@ 'grapheme_strripos' => ['int|false', 'haystack'=>'string', 'needle'=>'string', 'offset='=>'int'], 'grapheme_strrpos' => ['int|false', 'haystack'=>'string', 'needle'=>'string', 'offset='=>'int'], 'grapheme_strstr' => ['string|false', 'haystack'=>'string', 'needle'=>'string', 'beforeNeedle='=>'bool'], -'grapheme_substr' => ['string|false', 'string'=>'string', 'offset'=>'int', 'length='=>'int|null'], +'grapheme_substr' => ['string|false', 'string'=>'string', 'offset'=>'int', 'length='=>'int'], 'gregoriantojd' => ['int', 'month'=>'int', 'day'=>'int', 'year'=>'int'], 'gridObj::set' => ['int', 'property_name'=>'string', 'new_value'=>''], 'Grpc\Call::__construct' => ['void', 'channel'=>'Grpc\Channel', 'method'=>'string', 'absolute_deadline'=>'Grpc\Timeval', 'host_override='=>'mixed'], @@ -4189,17 +4189,17 @@ 'gzeof' => ['bool|int', 'stream'=>'resource'], 'gzfile' => ['list', 'filename'=>'string', 'use_include_path='=>'int'], 'gzgetc' => ['string|false', 'stream'=>'resource'], -'gzgets' => ['string|false', 'stream'=>'resource', 'length='=>'int|null'], +'gzgets' => ['string|false', 'stream'=>'resource', 'length='=>'int'], 'gzinflate' => ['string|false', 'data'=>'string', 'max_length='=>'int'], 'gzopen' => ['resource|false', 'filename'=>'string', 'mode'=>'string', 'use_include_path='=>'int'], 'gzpassthru' => ['int|false', 'stream'=>'resource'], -'gzputs' => ['int', 'stream'=>'resource', 'data'=>'string', 'length='=>'int|null'], +'gzputs' => ['int', 'stream'=>'resource', 'data'=>'string', 'length='=>'int'], 'gzread' => ['string|false', 'stream'=>'resource', 'length'=>'int'], 'gzrewind' => ['bool', 'stream'=>'resource'], 'gzseek' => ['int', 'stream'=>'resource', 'offset'=>'int', 'whence='=>'int'], 'gztell' => ['int|false', 'stream'=>'resource'], 'gzuncompress' => ['string|false', 'data'=>'string', 'max_length='=>'int'], -'gzwrite' => ['int', 'stream'=>'resource', 'data'=>'string', 'length='=>'int|null'], +'gzwrite' => ['int', 'stream'=>'resource', 'data'=>'string', 'length='=>'int'], 'HaruAnnotation::setBorderStyle' => ['bool', 'width'=>'float', 'dash_on'=>'int', 'dash_off'=>'int'], 'HaruAnnotation::setHighlightMode' => ['bool', 'mode'=>'int'], 'HaruAnnotation::setIcon' => ['bool', 'icon'=>'int'], @@ -5283,7 +5283,7 @@ 'iconv_strlen' => ['0|positive-int|false', 'string'=>'string', 'encoding='=>'string'], 'iconv_strpos' => ['int|false', 'haystack'=>'string', 'needle'=>'string', 'offset='=>'int', 'encoding='=>'string'], 'iconv_strrpos' => ['int|false', 'haystack'=>'string', 'needle'=>'string', 'encoding='=>'string'], -'iconv_substr' => ['string|false', 'string'=>'string', 'offset'=>'int', 'length='=>'int|null', 'encoding='=>'string|null'], +'iconv_substr' => ['string|false', 'string'=>'string', 'offset'=>'int', 'length='=>'int', 'encoding='=>'string'], 'id3_get_frame_long_name' => ['string', 'frameid'=>'string'], 'id3_get_frame_short_name' => ['string', 'frameid'=>'string'], 'id3_get_genre_id' => ['int', 'genre'=>'string'], @@ -5293,7 +5293,7 @@ 'id3_get_version' => ['int', 'filename'=>'string'], 'id3_remove_tag' => ['bool', 'filename'=>'string', 'version='=>'int'], 'id3_set_tag' => ['bool', 'filename'=>'string', 'tag'=>'array', 'version='=>'int'], -'idate' => ['int', 'format'=>'string', 'timestamp='=>'int|null'], +'idate' => ['int', 'format'=>'string', 'timestamp='=>'int'], 'idn_strerror' => ['string', 'errorcode'=>'int'], 'idn_to_ascii' => ['string|false', 'domain'=>'string', 'flags='=>'int', 'variant='=>'int', '&w_idna_info='=>'array'], 'idn_to_utf8' => ['string|false', 'domain'=>'string', 'flags='=>'int', 'variant='=>'int', '&w_idna_info='=>'array'], @@ -5336,8 +5336,8 @@ 'ifxus_tell_slob' => ['int', 'bid'=>'int'], 'ifxus_write_slob' => ['int', 'bid'=>'int', 'content'=>'string'], 'igbinary_serialize' => ['string|false', 'value'=>'mixed'], -'igbinary_unserialize' => ['mixed', 'str'=>'string'], -'ignore_user_abort' => ['int', 'enable='=>'?bool'], +'igbinary_unserialize' => ['mixed', 'string'=>'string'], +'ignore_user_abort' => ['int', 'enable='=>'bool'], 'iis_add_server' => ['int', 'path'=>'string', 'comment'=>'string', 'server_ip'=>'string', 'port'=>'int', 'host_name'=>'string', 'rights'=>'int', 'start_server'=>'int'], 'iis_get_dir_security' => ['int', 'server_instance'=>'int', 'virtual_path'=>'string'], 'iis_get_script_map' => ['string', 'server_instance'=>'int', 'virtual_path'=>'string', 'script_extension'=>'string'], @@ -5381,7 +5381,7 @@ 'imagecolorset' => ['void', 'image'=>'GdImage', 'color'=>'int', 'red'=>'int', 'green'=>'int', 'blue'=>'int', 'alpha='=>'int'], 'imagecolorsforindex' => ['array|false', 'image'=>'GdImage', 'color'=>'int'], 'imagecolorstotal' => ['int|false', 'image'=>'GdImage'], -'imagecolortransparent' => ['int|false', 'image'=>'GdImage', 'color='=>'int|null'], +'imagecolortransparent' => ['int|false', 'image'=>'GdImage', 'color='=>'int'], 'imageconvolution' => ['bool', 'image'=>'GdImage', 'matrix'=>'array', 'divisor'=>'float', 'offset'=>'float'], 'imagecopy' => ['bool', 'dst_image'=>'GdImage', 'src_image'=>'GdImage', 'dst_x'=>'int', 'dst_y'=>'int', 'src_x'=>'int', 'src_y'=>'int', 'src_width'=>'int', 'src_height'=>'int'], 'imagecopymerge' => ['bool', 'dst_image'=>'GdImage', 'src_image'=>'GdImage', 'dst_x'=>'int', 'dst_y'=>'int', 'src_x'=>'int', 'src_y'=>'int', 'src_width'=>'int', 'src_height'=>'int', 'pct'=>'int'], @@ -5411,15 +5411,10 @@ 'imagefill' => ['bool', 'image'=>'GdImage', 'x'=>'int', 'y'=>'int', 'color'=>'int'], 'imagefilledarc' => ['bool', 'image'=>'GdImage', 'center_x'=>'int', 'center_y'=>'int', 'width'=>'int', 'height'=>'int', 'start_angle'=>'int', 'end_angle'=>'int', 'color'=>'int', 'style'=>'int'], 'imagefilledellipse' => ['bool', 'image'=>'GdImage', 'center_x'=>'int', 'center_y'=>'int', 'width'=>'int', 'height'=>'int', 'color'=>'int'], -'imagefilledpolygon' => ['bool', 'image'=>'GdImage', 'points'=>'array', 'color'=>'int'], +'imagefilledpolygon' => ['bool', 'image'=>'GdImage', 'points'=>'array', 'num_points_or_color'=>'int', 'color'=>'int'], 'imagefilledrectangle' => ['bool', 'image'=>'GdImage', 'x1'=>'int', 'y1'=>'int', 'x2'=>'int', 'y2'=>'int', 'color'=>'int'], 'imagefilltoborder' => ['bool', 'image'=>'GdImage', 'x'=>'int', 'y'=>'int', 'border_color'=>'int', 'color'=>'int'], -'imagefilter' => ['bool', 'image' => 'GdImage', 'filter' => '0|1|5|6|7|8'], -'imagefilter\'1' => ['bool', 'image' => 'GdImage', 'filter' => '2', 'args' => 'int<-255,255>'], -'imagefilter\'2' => ['bool', 'image' => 'GdImage', 'filter' => '3|10', 'args' => 'int'], -'imagefilter\'3' => ['bool', 'image' => 'GdImage', 'filter' => '4', 'args' => 'int<0,255>', 'args2' => 'int<0,255>', 'args3' => 'int<0,255>', 'args4' => 'int<0,127>'], -'imagefilter\'4' => ['bool', 'image' => 'GdImage', 'filter' => '11', 'args' => 'int', 'args2=' => 'bool'], -'imagefilter\'5' => ['bool', 'image' => 'GdImage', 'filter' => '12', 'args' => 'int', 'args2=' => 'int', 'args3=' => 'array'], +'imagefilter' => ['bool', 'image'=>'GdImage', 'filter'=>'int', 'args='=>'int', 'arg2='=>'int', 'arg3='=>'int', 'arg4='=>'int'], 'imageflip' => ['bool', 'image'=>'GdImage', 'mode'=>'int'], 'imagefontheight' => ['int', 'font'=>'int'], 'imagefontwidth' => ['int', 'font'=>'int'], @@ -5433,7 +5428,7 @@ 'imagegif' => ['bool', 'image'=>'GdImage', 'file='=>'string|resource|null'], 'imagegrabscreen' => ['false|GdImage'], 'imagegrabwindow' => ['false|GdImage', 'handle'=>'int', 'client_area='=>'int'], -'imageinterlace' => ['int|false', 'image'=>'GdImage', 'enable='=>'bool|null'], +'imageinterlace' => ['int|false', 'image'=>'GdImage', 'enable='=>'int'], 'imageistruecolor' => ['bool', 'image'=>'GdImage'], 'imagejpeg' => ['bool', 'image'=>'GdImage', 'file='=>'string|resource|null', 'quality='=>'int'], 'imagelayereffect' => ['bool', 'image'=>'GdImage', 'effect'=>'int'], @@ -5442,14 +5437,14 @@ 'imageObj::pasteImage' => ['void', 'srcImg'=>'imageObj', 'transparentColorHex'=>'int', 'dstX'=>'int', 'dstY'=>'int', 'angle'=>'int'], 'imageObj::saveImage' => ['int', 'filename'=>'string', 'oMap'=>'mapObj'], 'imageObj::saveWebImage' => ['string'], -'imageopenpolygon' => ['bool', 'image'=>'GdImage', 'points'=>'array{0: int, 1: int, 2: int, 3: int}', 'color'=>'int'], +'imageopenpolygon' => ['bool', 'image'=>'GdImage', 'points'=>'array', 'num_points'=>'int', 'color'=>'int'], 'imagepalettecopy' => ['void', 'dst'=>'GdImage', 'src'=>'GdImage'], 'imagepalettetotruecolor' => ['bool', 'image'=>'GdImage'], 'imagepng' => ['bool', 'image'=>'GdImage', 'file='=>'string|resource|null', 'quality='=>'int', 'filters='=>'int'], -'imagepolygon' => ['bool', 'image'=>'GdImage', 'points'=>'array', 'color'=>'int'], +'imagepolygon' => ['bool', 'image'=>'GdImage', 'points'=>'array', 'num_points_or_color'=>'int', 'color'=>'int'], 'imagerectangle' => ['bool', 'image'=>'GdImage', 'x1'=>'int', 'y1'=>'int', 'x2'=>'int', 'y2'=>'int', 'color'=>'int'], -'imageresolution' => ['array|bool', 'image'=>'GdImage', 'resolution_x='=>'int|null', 'resolution_y='=>'int|null'], -'imagerotate' => ['false|GdImage', 'image'=>'GdImage', 'angle'=>'float', 'background_color'=>'int', 'ignore_transparent='=>'bool'], +'imageresolution' => ['array|bool', 'image'=>'GdImage', 'resolution_x='=>'int', 'resolution_y='=>'int'], +'imagerotate' => ['false|GdImage', 'image'=>'GdImage', 'angle'=>'float', 'background_color'=>'int', 'ignore_transparent='=>'int'], 'imagesavealpha' => ['bool', 'image'=>'GdImage', 'enable'=>'bool'], 'imagescale' => ['false|GdImage', 'image'=>'GdImage', 'width'=>'int', 'height='=>'int', 'mode='=>'int'], 'imagesetbrush' => ['bool', 'image'=>'GdImage', 'brush'=>'GdImage'], @@ -5467,9 +5462,9 @@ 'imagettfbbox' => ['false|array', 'size'=>'float', 'angle'=>'float', 'font_filename'=>'string', 'string'=>'string'], 'imagettftext' => ['false|array', 'image'=>'GdImage', 'size'=>'float', 'angle'=>'float', 'x'=>'int', 'y'=>'int', 'color'=>'int', 'font_filename'=>'string', 'text'=>'string'], 'imagetypes' => ['int'], -'imagewbmp' => ['bool', 'image'=>'GdImage', 'file='=>'string|resource|null', 'foreground_color='=>'int|null'], +'imagewbmp' => ['bool', 'image'=>'GdImage', 'file='=>'string|resource|null', 'foreground_color='=>'int'], 'imagewebp' => ['bool', 'image'=>'GdImage', 'file='=>'string|resource|null', 'quality='=>'int'], -'imagexbm' => ['bool', 'image'=>'GdImage', 'filename='=>'?string', 'foreground_color='=>'?int'], +'imagexbm' => ['bool', 'image'=>'GdImage', 'filename='=>'?string', 'foreground_color='=>'int'], 'Imagick::__construct' => ['void', 'files='=>'string|string[]'], 'Imagick::__toString' => ['string'], 'Imagick::adaptiveBlurImage' => ['bool', 'radius'=>'float', 'sigma'=>'float', 'channel='=>'int'], @@ -6040,7 +6035,7 @@ 'imap_close' => ['bool', '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' => ['bool', 'imap'=>'IMAP\Connection', 'message_num'=>'int', 'flags='=>'int'], 'imap_deletemailbox' => ['bool', 'imap'=>'IMAP\Connection', 'mailbox'=>'string'], 'imap_errors' => ['array|false'], 'imap_expunge' => ['bool', 'imap'=>'IMAP\Connection'], @@ -6075,7 +6070,7 @@ 'imap_mutf7_to_utf8' => ['string|false', 'string'=>'string'], 'imap_num_msg' => ['int|false', 'imap'=>'IMAP\Connection'], 'imap_num_recent' => ['int|false', 'imap'=>'IMAP\Connection'], -'imap_open' => ['IMAP\Connection|false', 'mailbox'=>'string', 'user'=>'string', 'password'=>'string', 'flags='=>'int', 'retries='=>'int', 'options='=>'array'], +'imap_open' => ['IMAP\Connection|false', 'mailbox'=>'string', 'user'=>'string', 'password'=>'string', 'flags='=>'int', 'retries='=>'int', 'options='=>'?array'], 'imap_ping' => ['bool', 'imap'=>'IMAP\Connection'], 'imap_qprint' => ['string|false', 'string'=>'string'], 'imap_rename' => ['bool', 'imap'=>'IMAP\Connection', 'from'=>'string', 'to'=>'string'], @@ -6083,7 +6078,7 @@ 'imap_reopen' => ['bool', 'imap'=>'IMAP\Connection', 'mailbox'=>'string', 'flags='=>'int', 'retries='=>'int'], 'imap_rfc822_parse_adrlist' => ['array', 'string'=>'string', 'default_hostname'=>'string'], 'imap_rfc822_parse_headers' => ['stdClass', 'headers'=>'string', 'default_hostname='=>'string'], -'imap_rfc822_write_address' => ['string|false', 'mailbox'=>'string', 'hostname'=>'string', 'personal'=>'string'], +'imap_rfc822_write_address' => ['string|false', 'mailbox'=>'?string', 'hostname'=>'?string', 'personal'=>'?string'], 'imap_savebody' => ['bool', 'imap'=>'IMAP\Connection', 'file'=>'string|resource', 'message_num'=>'int', 'section='=>'string', 'flags='=>'int'], 'imap_scan' => ['array|false', 'imap'=>'IMAP\Connection', 'reference'=>'string', 'pattern'=>'string', 'content'=>'string'], 'imap_scanmailbox' => ['array|false', 'imap'=>'IMAP\Connection', 'reference'=>'string', 'pattern'=>'string', 'content'=>'string'], @@ -6091,13 +6086,13 @@ '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_sort' => ['array|false', 'imap'=>'IMAP\Connection', 'criteria'=>'int', 'reverse'=>'bool', 'flags='=>'int', 'search_criteria='=>'string|null', 'charset='=>'string|null'], +'imap_sort' => ['array|false', 'imap'=>'IMAP\Connection', 'criteria'=>'int', 'reverse'=>'int', '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' => ['bool', 'imap'=>'IMAP\Connection', 'message_num'=>'int', 'flags='=>'int'], 'imap_unsubscribe' => ['bool', 'imap'=>'IMAP\Connection', 'mailbox'=>'string'], 'imap_utf7_decode' => ['string|false', 'string'=>'string'], 'imap_utf7_encode' => ['string', 'string'=>'string'], @@ -6117,10 +6112,10 @@ 'InfiniteIterator::next' => ['void'], 'InfiniteIterator::rewind' => ['void'], 'InfiniteIterator::valid' => ['bool'], -'inflate_add' => ['string|false', 'context'=>'InflateContext', 'data'=>'string', 'flush_mode='=>'int'], -'inflate_get_read_len' => ['int|false', 'context'=>'InflateContext'], -'inflate_get_status' => ['int|false', 'context'=>'InflateContext'], -'inflate_init' => ['InflateContext|false', 'encoding'=>'int', 'options='=>'array'], +'inflate_add' => ['string|false', 'context'=>'resource', 'data'=>'string', 'flush_mode='=>'int'], +'inflate_get_read_len' => ['int|false', 'context'=>'resource'], +'inflate_get_status' => ['int|false', 'context'=>'resource'], +'inflate_init' => ['resource|false', 'encoding'=>'int', 'options='=>'array'], 'ingres_autocommit' => ['bool', 'link'=>'resource'], 'ingres_autocommit_state' => ['bool', 'link'=>'resource'], 'ingres_charset' => ['string', 'link'=>'resource'], @@ -6164,7 +6159,7 @@ 'inotify_init' => ['resource|false'], 'inotify_queue_len' => ['int', 'inotify_instance'=>'resource'], 'inotify_read' => ['array|false', 'inotify_instance'=>'resource'], -'inotify_rm_watch' => ['bool', 'inotify_instance'=>'resource', 'mask'=>'int'], +'inotify_rm_watch' => ['bool', 'inotify_instance'=>'resource', 'watch_descriptor'=>'int'], 'intdiv' => ['int', 'num1'=>'int', 'num2'=>'int'], 'interface_exists' => ['bool', 'interface'=>'string', 'autoload='=>'bool'], 'intl_error_name' => ['string', 'errorCode'=>'int'], @@ -6195,7 +6190,7 @@ '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|null'], +'intlcal_clear' => ['bool', 'calendar'=>'IntlCalendar', 'field='=>'int'], 'intlcal_create_instance' => ['IntlCalendar', 'timezone='=>'mixed', 'locale='=>'string'], 'intlcal_equals' => ['bool', 'calendar'=>'IntlCalendar', 'other'=>'IntlCalendar'], 'intlcal_field_difference' => ['int', 'calendar'=>'IntlCalendar', 'timestamp'=>'float', 'field'=>'int'], @@ -6219,12 +6214,12 @@ 'intlcal_get_time' => ['float', 'calendar'=>'IntlCalendar'], 'intlcal_get_time_zone' => ['IntlTimeZone', 'calendar'=>'IntlCalendar'], 'intlcal_get_type' => ['string', 'calendar'=>'IntlCalendar'], -'intlcal_get_weekend_transition' => ['int', 'calendar'=>'IntlCalendar', 'dayOfWeek'=>'1|2|3|4|5|6|7'], +'intlcal_get_weekend_transition' => ['int', 'calendar'=>'IntlCalendar', 'dayOfWeek'=>'string'], 'intlcal_in_daylight_time' => ['bool', 'calendar'=>'IntlCalendar'], 'intlcal_is_equivalent_to' => ['bool', 'calendar'=>'IntlCalendar', 'other'=>'IntlCalendar'], 'intlcal_is_lenient' => ['bool', 'calendar'=>'IntlCalendar'], 'intlcal_is_set' => ['bool', 'calendar'=>'IntlCalendar', 'field'=>'int'], -'intlcal_is_weekend' => ['bool', 'calendar'=>'IntlCalendar', 'timestamp='=>'?float'], +'intlcal_is_weekend' => ['bool', 'calendar'=>'IntlCalendar', 'timestamp='=>'float'], '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'], @@ -6399,7 +6394,7 @@ 'IntlException::getTraceAsString' => ['string'], 'intlgregcal_create_instance' => ['IntlGregorianCalendar', 'timezoneOrYear='=>'mixed', 'localeOrMonth='=>'string'], 'intlgregcal_get_gregorian_change' => ['float', 'calendar'=>'IntlGregorianCalendar'], -'intlgregcal_is_leap_year' => ['bool', 'calendar'=>'IntlGregorianCalendar', 'year' => 'int'], +'intlgregcal_is_leap_year' => ['bool', 'calendar'=>'int'], 'intlgregcal_set_gregorian_change' => ['void', 'calendar'=>'IntlGregorianCalendar', 'timestamp'=>'float'], 'IntlGregorianCalendar::__construct' => ['void'], 'IntlGregorianCalendar::add' => ['bool', 'field'=>'int', 'amount'=>'int'], @@ -6508,11 +6503,11 @@ 'IntlTimeZone::toDateTimeZone' => ['DateTimeZone|false'], 'IntlTimeZone::useDaylightTime' => ['bool'], 'intltz_count_equivalent_ids' => ['int', 'timezoneId'=>'string'], -'intltz_create_enumeration' => ['IntlIterator', 'countryOrRawOffset='=>'IntlTimeZone|string|int|float|null'], +'intltz_create_enumeration' => ['IntlIterator', 'countryOrRawOffset'=>'mixed'], 'intltz_create_time_zone' => ['IntlTimeZone', 'timezoneId'=>'string'], 'intltz_from_date_time_zone' => ['IntlTimeZone', 'timezone'=>'DateTimeZone'], 'intltz_get_canonical_id' => ['string', 'timezoneId'=>'string', '&isSystemId'=>'bool'], -'intltz_get_display_name' => ['string', 'timezone'=>'IntlTimeZone', 'dst='=>'bool', 'style='=>'int', 'locale='=>'string|null'], +'intltz_get_display_name' => ['string', 'timezone'=>'IntlTimeZone', 'dst'=>'bool', 'style'=>'int', 'locale'=>'string'], 'intltz_get_dst_savings' => ['int', 'timezone'=>'IntlTimeZone'], 'intltz_get_equivalent_id' => ['string', 'timezoneId'=>'string', 'offset'=>'int'], 'intltz_get_error_code' => ['int', 'timezone'=>'IntlTimeZone'], @@ -6543,7 +6538,7 @@ 'is_a' => ['bool', 'object_or_class'=>'mixed', 'class'=>'string', 'allow_string='=>'bool'], 'is_array' => ['bool', 'value'=>'mixed'], 'is_bool' => ['bool', 'value'=>'mixed'], -'is_callable' => ['bool', 'value'=>'mixed', 'syntax_only='=>'bool', '&w_callable_name='=>'string'], +'is_callable' => ['bool', 'value'=>'callable|mixed', 'syntax_only='=>'bool', '&w_callable_name='=>'string'], 'is_countable' => ['bool', 'value'=>'mixed'], 'is_dir' => ['bool', 'filename'=>'string'], 'is_double' => ['bool', 'value'=>'mixed'], @@ -6578,7 +6573,7 @@ 'Iterator::next' => ['void'], 'Iterator::rewind' => ['void'], 'Iterator::valid' => ['bool'], -'iterator_apply' => ['0|positive-int', 'iterator'=>'Traversable', 'callback'=>'callable(mixed):bool', 'args='=>'array|null'], +'iterator_apply' => ['0|positive-int', 'iterator'=>'Traversable', 'callback'=>'callable(mixed):bool', 'args='=>'array'], 'iterator_count' => ['0|positive-int', 'iterator'=>'Traversable'], 'iterator_to_array' => ['array', 'iterator'=>'Traversable', 'preserve_keys='=>'bool'], 'IteratorAggregate::getIterator' => ['Traversable'], @@ -6608,7 +6603,7 @@ 'jobqueue_license_info' => ['array'], 'join' => ['string', 'separator'=>'string', 'array'=>'array'], 'join\'1' => ['string', 'separator'=>'array'], -'json_decode' => ['mixed', 'json'=>'string', 'associative='=>'bool|null', 'depth='=>'int', 'flags='=>'int'], +'json_decode' => ['mixed', 'json'=>'string', 'associative='=>'bool', 'depth='=>'int', 'flags='=>'int'], 'json_encode' => ['string|false', 'value'=>'mixed', 'flags='=>'int', 'depth='=>'int'], 'json_last_error' => ['int'], 'json_last_error_msg' => ['string'], @@ -6778,10 +6773,10 @@ 'lchgrp' => ['bool', 'filename'=>'string', 'group'=>'string|int'], 'lchown' => ['bool', 'filename'=>'string', 'user'=>'string|int'], 'ldap_8859_to_t61' => ['string', 'value'=>'string'], -'ldap_add' => ['bool', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'entry'=>'array', 'controls='=>'array|null'], -'ldap_add_ext' => ['LDAP\Connection|false', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'entry'=>'array', 'controls='=>'array|null'], +'ldap_add' => ['bool', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'entry'=>'array', 'controls='=>'array'], +'ldap_add_ext' => ['LDAP\Connection|false', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'entry'=>'array', 'controls='=>'array'], 'ldap_bind' => ['bool', 'ldap'=>'LDAP\Connection', 'dn='=>'string|null', 'password='=>'string|null'], -'ldap_bind_ext' => ['LDAP\Connection|false', 'ldap'=>'LDAP\Connection', 'dn='=>'string|null', 'password='=>'string|null', 'controls='=>'array|null'], +'ldap_bind_ext' => ['LDAP\Connection|false', 'ldap'=>'LDAP\Connection', 'dn='=>'string|null', 'password='=>'string|null', 'controls='=>'array'], 'ldap_close' => ['bool', 'ldap'=>'LDAP\Connection'], 'ldap_compare' => ['bool|int', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'attribute'=>'string', 'value'=>'string'], 'ldap_connect' => ['LDAP\Connection|false', 'uri='=>'string', 'port='=>'int', 'wallet='=>'string', 'password='=>'string', 'auth_mode='=>'int'], @@ -6789,7 +6784,7 @@ 'ldap_control_paged_result_response' => ['bool', 'link_identifier'=>'resource', 'result_identifier'=>'resource', '&w_cookie'=>'string', '&w_estimated'=>'int'], 'ldap_count_entries' => ['int|false', 'ldap'=>'LDAP\Connection', 'result'=>'LDAP\Result'], 'ldap_delete' => ['bool', 'ldap'=>'LDAP\Connection', 'dn'=>'string'], -'ldap_delete_ext' => ['LDAP\Connection|false', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'controls='=>'array|null'], +'ldap_delete_ext' => ['LDAP\Connection|false', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'controls='=>'array'], 'ldap_dn2ufn' => ['string', 'dn'=>'string'], 'ldap_err2str' => ['string', 'errno'=>'int'], 'ldap_errno' => ['int', 'ldap'=>'LDAP\Connection'], @@ -6812,11 +6807,11 @@ 'ldap_get_values_len' => ['array|false', 'ldap'=>'LDAP\Connection', 'entry'=>'LDAP\ResultEntry', 'attribute'=>'string'], 'ldap_list' => ['LDAP\Connection|false', 'ldap'=>'resource|array', 'base'=>'string', 'filter'=>'string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int'], 'ldap_mod_add' => ['bool', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'entry'=>'array'], -'ldap_mod_add_ext' => ['LDAP\Connection|false', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'entry'=>'array', 'controls='=>'array|null'], +'ldap_mod_add_ext' => ['LDAP\Connection|false', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'entry'=>'array', 'controls='=>'array'], 'ldap_mod_del' => ['bool', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'entry'=>'array'], -'ldap_mod_del_ext' => ['LDAP\Connection|false', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'entry'=>'array', 'controls='=>'array|null'], +'ldap_mod_del_ext' => ['LDAP\Connection|false', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'entry'=>'array', 'controls='=>'array'], 'ldap_mod_replace' => ['bool', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'entry'=>'array'], -'ldap_mod_replace_ext' => ['LDAP\Connection|false', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'entry'=>'array', 'controls='=>'array|null'], +'ldap_mod_replace_ext' => ['LDAP\Connection|false', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'entry'=>'array', 'controls='=>'array'], 'ldap_modify' => ['bool', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'entry'=>'array'], 'ldap_modify_batch' => ['bool', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'modifications_info'=>'array'], 'ldap_next_attribute' => ['string|false', 'ldap'=>'LDAP\Connection', 'entry'=>'LDAP\ResultEntry'], @@ -6827,14 +6822,14 @@ 'ldap_parse_result' => ['bool', 'ldap'=>'LDAP\Connection', 'result'=>'LDAP\Result', '&w_error_code'=>'int', '&w_matched_dn='=>'string', '&w_error_message='=>'string', '&w_referrals='=>'array', '&w_controls='=>'array'], 'ldap_read' => ['LDAP\Connection|false', 'ldap'=>'LDAP\Connection|array', 'base'=>'string', 'filter'=>'string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int'], 'ldap_rename' => ['bool', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'new_rdn'=>'string', 'new_parent'=>'string', 'delete_old_rdn'=>'bool'], -'ldap_rename_ext' => ['LDAP\Connection|false', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'new_rdn'=>'string', 'new_parent'=>'string', 'delete_old_rdn'=>'bool', 'controls='=>'array|null'], +'ldap_rename_ext' => ['LDAP\Connection|false', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'new_rdn'=>'string', 'new_parent'=>'string', 'delete_old_rdn'=>'bool', 'controls='=>'array'], 'ldap_sasl_bind' => ['bool', 'ldap'=>'LDAP\Connection', 'dn='=>'string', 'password='=>'string', 'mech='=>'string', 'realm='=>'string', 'authc_id='=>'string', 'authz_id='=>'string', 'props='=>'string'], 'ldap_search' => ['LDAP\Result|false', 'ldap'=>'LDAP\Connection|LDAP\Connection[]', 'base'=>'string', 'filter'=>'string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int'], 'ldap_set_option' => ['bool', 'ldap'=>'LDAP\Connection|null', 'option'=>'int', 'value'=>'mixed'], 'ldap_set_rebind_proc' => ['bool', 'ldap'=>'LDAP\Connection', 'callback'=>'?callable'], -'ldap_start_tls' => ['bool', 'ldap'=>'LDAP\Connection'], +'ldap_start_tls' => ['bool', 'ldap'=>'resource'], 'ldap_t61_to_8859' => ['string', 'value'=>'string'], -'ldap_unbind' => ['bool', 'ldap'=>'LDAP\Connection'], +'ldap_unbind' => ['bool', 'ldap'=>'resource'], 'leak' => ['', 'num_bytes'=>'int'], 'leak_variable' => ['', 'variable'=>'', 'leak_data'=>'bool'], 'legendObj::convertToString' => ['string'], @@ -6889,9 +6884,9 @@ 'libxml_disable_entity_loader' => ['bool', 'disable='=>'bool'], 'libxml_get_errors' => ['array'], 'libxml_get_last_error' => ['LibXMLError|false'], -'libxml_set_external_entity_loader' => ['bool', 'resolver_function'=>'?callable'], +'libxml_set_external_entity_loader' => ['bool', 'resolver_function'=>'callable'], 'libxml_set_streams_context' => ['void', 'context'=>'resource'], -'libxml_use_internal_errors' => ['bool', 'use_errors='=>'bool|null'], +'libxml_use_internal_errors' => ['bool', 'use_errors='=>'bool'], 'LimitIterator::__construct' => ['void', 'iterator'=>'Iterator', 'offset='=>'int', 'count='=>'int'], 'LimitIterator::current' => ['mixed'], 'LimitIterator::getInnerIterator' => ['Iterator'], @@ -6949,7 +6944,7 @@ 'locale_parse' => ['array', 'locale'=>'string'], 'locale_set_default' => ['bool', 'locale'=>'string'], 'localeconv' => ['array'], -'localtime' => ['array', 'timestamp='=>'int|null', 'associative='=>'bool'], +'localtime' => ['array', 'timestamp='=>'int', 'associative='=>'bool'], 'log' => ['float', 'num'=>'float', 'base='=>'float'], 'log10' => ['float', 'num'=>'float'], 'log1p' => ['float', 'num'=>'float'], @@ -6963,7 +6958,7 @@ 'LogicException::getPrevious' => ['Throwable|LogicException|null'], 'LogicException::getTrace' => ['list\',args?:array}>'], 'LogicException::getTraceAsString' => ['string'], -'long2ip' => ['string', 'ip'=>'int'], +'long2ip' => ['string', 'ip'=>'string|int'], 'lstat' => ['array{0: int, 1: int, 2: int, 3: int, 4: int, 5: int, 6: int, 7: int, 8: int, 9: int, 10: int, 11: int, 12: int, dev: int, ino: int, mode: int, nlink: int, uid: int, gid: int, rdev: int, size: int, atime: int, mtime: int, ctime: int, blksize: int, blocks: int}|false', 'filename'=>'string'], 'ltrim' => ['string', 'string'=>'string', 'characters='=>'string'], 'Lua::__call' => ['mixed', 'lua_func'=>'callable', 'args='=>'array', 'use_self='=>'int'], @@ -6975,8 +6970,8 @@ 'Lua::include' => ['mixed', 'file'=>'string'], 'Lua::registerCallback' => ['Lua|null|false', 'name'=>'string', 'function'=>'callable'], 'LuaClosure::__invoke' => ['void', 'arg'=>'mixed', '...args='=>'mixed'], -'lzf_compress' => ['string', 'string'=>'string'], -'lzf_decompress' => ['string', 'string'=>'string'], +'lzf_compress' => ['string', 'data'=>'string'], +'lzf_decompress' => ['string', 'data'=>'string'], 'lzf_optimized_for' => ['int'], 'm_checkstatus' => ['int', 'conn'=>'resource', 'identifier'=>'int'], 'm_completeauthorizations' => ['int', 'conn'=>'resource', 'array'=>'int'], @@ -7018,7 +7013,7 @@ 'm_verifyconnection' => ['bool', 'conn'=>'resource', 'tf'=>'int'], 'm_verifysslcert' => ['bool', 'conn'=>'resource', 'tf'=>'int'], 'magic_quotes_runtime' => ['bool', 'new_setting'=>'bool'], -'mail' => ['bool', 'to'=>'string', 'subject'=>'string', 'message'=>'string', 'additional_headers='=>'string|array', 'additional_params='=>'string'], +'mail' => ['bool', 'to'=>'string', 'subject'=>'string', 'message'=>'string', 'additional_headers='=>'string|array|null', 'additional_params='=>'string'], 'mailparse_determine_best_xfer_encoding' => ['string', 'fp'=>'resource'], 'mailparse_msg_create' => ['resource'], 'mailparse_msg_extract_part' => ['void', 'mimemail'=>'resource', 'msgbody'=>'string', 'callbackfunc='=>'callable'], @@ -7393,30 +7388,30 @@ 'Memcache::setCompressThreshold' => ['bool', 'threshold'=>'int', 'min_savings='=>'float'], 'Memcache::setFailureCallback' => [''], 'Memcache::setServerParams' => ['bool', 'host'=>'string', 'port='=>'int', 'timeout='=>'int', 'retry_interval='=>'int', 'status='=>'bool', 'failure_callback='=>'callable'], -'memcache_add' => ['bool', 'memcache'=>'MemcachePool', 'key'=>'string', 'value'=>'mixed', 'flag='=>'int', 'expire='=>'int'], -'memcache_add_server' => ['bool', 'memcache'=>'MemcachePool', 'host'=>'string', 'port='=>'int', 'persistent='=>'bool', 'weight='=>'int', 'timeout='=>'int', 'retry_interval='=>'int', 'status='=>'bool', 'failure_callback='=>'callable', 'timeoutms='=>'int'], -'memcache_append' => ['', 'memcache'=>'MemcachePool'], -'memcache_cas' => ['', 'memcache'=>'MemcachePool'], -'memcache_close' => ['bool', 'memcache'=>'MemcachePool'], +'memcache_add' => ['bool', 'memcache_obj'=>'Memcache', 'key'=>'string', 'var'=>'mixed', 'flag='=>'int', 'expire='=>'int'], +'memcache_add_server' => ['bool', 'memcache_obj'=>'Memcache', 'host'=>'string', 'port='=>'int', 'persistent='=>'bool', 'weight='=>'int', 'timeout='=>'int', 'retry_interval='=>'int', 'status='=>'bool', 'failure_callback='=>'callable', 'timeoutms='=>'int'], +'memcache_append' => ['', 'memcache_obj'=>'Memcache'], +'memcache_cas' => ['', 'memcache_obj'=>'Memcache'], +'memcache_close' => ['bool', 'memcache_obj'=>'Memcache'], 'memcache_connect' => ['Memcache|false', 'host'=>'string', 'port='=>'int', 'timeout='=>'int'], 'memcache_debug' => ['bool', 'on_off'=>'bool'], -'memcache_decrement' => ['int', 'memcache'=>'MemcachePool', 'key'=>'string', 'value='=>'int'], -'memcache_delete' => ['bool', 'memcache'=>'MemcachePool', 'key'=>'string', 'timeout='=>'int'], -'memcache_flush' => ['bool', 'memcache'=>'MemcachePool'], -'memcache_get' => ['string', 'memcache'=>'MemcachePool', 'key'=>'string', 'flags='=>'int'], -'memcache_get\'1' => ['array', 'memcache'=>'Memcache', 'key'=>'string[]', 'flags='=>'int[]'], -'memcache_get_extended_stats' => ['array', 'memcache'=>'MemcachePool', 'type='=>'string', 'slabid='=>'int', 'limit='=>'int'], -'memcache_get_server_status' => ['int', 'memcache'=>'MemcachePool', 'host'=>'string', 'port='=>'int'], -'memcache_get_stats' => ['array', 'memcache'=>'MemcachePool', 'type='=>'string', 'slabid='=>'int', 'limit='=>'int'], -'memcache_get_version' => ['string', 'memcache'=>'MemcachePool'], -'memcache_increment' => ['int|false', 'memcache'=>'MemcachePool', 'key'=>'string', 'value='=>'int'], +'memcache_decrement' => ['int', 'memcache_obj'=>'Memcache', 'key'=>'string', 'value='=>'int'], +'memcache_delete' => ['bool', 'memcache_obj'=>'Memcache', 'key'=>'string', 'timeout='=>'int'], +'memcache_flush' => ['bool', 'memcache_obj'=>'Memcache'], +'memcache_get' => ['string', 'memcache_obj'=>'Memcache', 'key'=>'string', 'flags='=>'int'], +'memcache_get\'1' => ['array', 'memcache_obj'=>'Memcache', 'key'=>'string[]', 'flags='=>'int[]'], +'memcache_get_extended_stats' => ['array', 'memcache_obj'=>'Memcache', 'type='=>'string', 'slabid='=>'int', 'limit='=>'int'], +'memcache_get_server_status' => ['int', 'memcache_obj'=>'Memcache', 'host'=>'string', 'port='=>'int'], +'memcache_get_stats' => ['array', 'memcache_obj'=>'Memcache', 'type='=>'string', 'slabid='=>'int', 'limit='=>'int'], +'memcache_get_version' => ['string', 'memcache_obj'=>'Memcache'], +'memcache_increment' => ['int', 'memcache_obj'=>'Memcache', 'key'=>'string', 'value='=>'int'], 'memcache_pconnect' => ['Memcache|false', 'host'=>'string', 'port='=>'int', 'timeout='=>'int'], -'memcache_prepend' => ['string', 'memcache'=>'MemcachePool'], -'memcache_replace' => ['bool', 'memcache'=>'MemcachePool', 'key'=>'string', 'var'=>'mixed', 'flag='=>'int', 'expire='=>'int'], -'memcache_set' => ['bool', 'memcache'=>'MemcachePool', 'key'=>'string', 'value'=>'mixed', 'flag='=>'int', 'expire='=>'int'], -'memcache_set_compress_threshold' => ['bool', 'memcache'=>'MemcachePool', 'threshold'=>'int', 'min_savings='=>'float'], -'memcache_set_failure_callback' => ['', 'memcache'=>'MemcachePool'], -'memcache_set_server_params' => ['bool', 'memcache'=>'MemcachePool', 'host'=>'string', 'port='=>'int', 'timeout='=>'float', 'retry_interval='=>'int', 'status='=>'bool', 'failure_callback='=>'callable'], +'memcache_prepend' => ['string', 'memcache_obj'=>'Memcache'], +'memcache_replace' => ['bool', 'memcache_obj'=>'Memcache', 'key'=>'string', 'var'=>'mixed', 'flag='=>'int', 'expire='=>'int'], +'memcache_set' => ['bool', 'memcache_obj'=>'Memcache', 'key'=>'string', 'var'=>'mixed', 'flag='=>'int', 'expire='=>'int'], +'memcache_set_compress_threshold' => ['bool', 'memcache_obj'=>'Memcache', 'threshold'=>'int', 'min_savings='=>'float'], +'memcache_set_failure_callback' => ['', 'memcache_obj'=>'Memcache'], +'memcache_set_server_params' => ['bool', 'memcache_obj'=>'Memcache', 'host'=>'string', 'port='=>'int', 'timeout='=>'int', 'retry_interval='=>'int', 'status='=>'bool', 'failure_callback='=>'callable'], 'Memcached::__construct' => ['void', 'persistent_id='=>'mixed|string', 'on_new_object_cb='=>'mixed'], 'Memcached::add' => ['bool', 'key'=>'string', 'value'=>'mixed', 'expiration='=>'int'], 'Memcached::addByKey' => ['bool', 'server_key'=>'string', 'key'=>'string', 'value'=>'mixed', 'expiration='=>'int'], @@ -7799,7 +7794,7 @@ 'MongoDB\BSON\timestampinterface::getIncrement' => ['int'], 'MongoDB\BSON\timestampinterface::getTimestamp' => ['int'], 'MongoDB\BSON\toJSON' => ['string', 'bson'=>'string'], -'MongoDB\BSON\toPHP' => ['object', 'bson'=>'string', 'typemap='=>'array'], +'MongoDB\BSON\toPHP' => ['object', 'bson'=>'string', 'typeMap='=>'array'], 'MongoDB\BSON\undefined::__construct' => ['void'], 'MongoDB\BSON\undefined::__toString' => ['string'], 'MongoDB\BSON\undefined::jsonSerialize' => ['mixed'], @@ -8170,11 +8165,11 @@ 'msession_unlock' => ['int', 'session'=>'string', 'key'=>'int'], 'msg_get_queue' => ['resource', 'key'=>'int', 'permissions='=>'int'], 'msg_queue_exists' => ['bool', 'key'=>'int'], -'msg_receive' => ['bool', 'queue'=>'SysvMessageQueue', 'desired_message_type'=>'int', '&w_received_message_type'=>'int', 'max_message_size'=>'int', '&w_message'=>'mixed', 'unserialize='=>'bool', 'flags='=>'int', '&w_error_code='=>'int'], -'msg_remove_queue' => ['bool', 'queue'=>'SysvMessageQueue'], -'msg_send' => ['bool', 'queue'=>'SysvMessageQueue', 'message_type'=>'int', 'message'=>'mixed', 'serialize='=>'bool', 'blocking='=>'bool', '&w_error_code='=>'int'], -'msg_set_queue' => ['bool', 'queue'=>'SysvMessageQueue', 'data'=>'array'], -'msg_stat_queue' => ['array', 'queue'=>'SysvMessageQueue'], +'msg_receive' => ['bool', 'queue'=>'resource', 'desired_message_type'=>'int', '&w_received_message_type'=>'int', 'max_message_size'=>'int', '&w_message'=>'mixed', 'unserialize='=>'bool', 'flags='=>'int', '&w_error_code='=>'int'], +'msg_remove_queue' => ['bool', 'queue'=>'resource'], +'msg_send' => ['bool', 'queue'=>'resource', 'message_type'=>'int', 'message'=>'mixed', 'serialize='=>'bool', 'blocking='=>'bool', '&w_error_code='=>'int'], +'msg_set_queue' => ['bool', 'queue'=>'resource', 'data'=>'array'], +'msg_stat_queue' => ['array', 'queue'=>'resource'], 'msgfmt_create' => ['MessageFormatter', 'locale'=>'string', 'pattern'=>'string'], 'msgfmt_format' => ['string|false', 'formatter'=>'MessageFormatter', 'values'=>'array'], 'msgfmt_format_message' => ['string|false', 'locale'=>'string', 'pattern'=>'string', 'values'=>'array'], @@ -8513,7 +8508,7 @@ 'mysqli_num_rows' => ['int', 'result'=>'mysqli_result'], 'mysqli_options' => ['bool', 'mysql'=>'mysqli', 'option'=>'int', 'value'=>'string|int'], 'mysqli_ping' => ['bool', 'mysql'=>'mysqli'], -'mysqli_poll' => ['int|false', '&rw_read'=>'array|null', '&rw_write'=>'array|null', '&rw_error'=>'array|null', 'seconds'=>'0|positive-int', 'microseconds='=>'0|positive-int'], +'mysqli_poll' => ['int|false', 'read'=>'array', 'write'=>'array', 'error'=>'array', 'seconds'=>'int', 'microseconds='=>'int'], 'mysqli_prepare' => ['mysqli_stmt|false', 'mysql'=>'mysqli', 'query'=>'string'], 'mysqli_query' => ['mysqli_result|bool', 'mysql'=>'mysqli', 'query'=>'string', 'result_mode='=>'int'], 'mysqli_real_connect' => ['bool', 'mysql='=>'mysqli', 'hostname='=>'string|null', 'username='=>'string|null', 'password='=>'string|null', 'database='=>'string|null', 'port='=>'int|null', 'socket='=>'string|null', 'flags='=>'int'], @@ -9011,7 +9006,7 @@ 'Normalizer::getRawDecomposition' => ['string|null', 'input'=>'string'], 'Normalizer::isNormalized' => ['bool', 'input'=>'string', 'form='=>'int'], 'Normalizer::normalize' => ['string', 'input'=>'string', 'form='=>'int'], -'normalizer_get_raw_decomposition' => ['string|null', 'string'=>'string', 'form=' => 'int'], +'normalizer_get_raw_decomposition' => ['string|null', 'string'=>'string'], 'normalizer_is_normalized' => ['bool', 'string'=>'string', 'form='=>'int'], 'normalizer_normalize' => ['string', 'string'=>'string', 'form='=>'int'], 'notes_body' => ['array', 'server'=>'string', 'mailbox'=>'string', 'msg_number'=>'int'], @@ -9032,7 +9027,7 @@ 'nsapi_response_headers' => ['array'], 'nsapi_virtual' => ['bool', 'uri'=>'string'], 'nthmac' => ['string', 'clent'=>'string', 'data'=>'string'], -'number_format' => ['string', 'num'=>'float|int', 'decimals='=>'int', 'decimal_separator='=>'string|null', 'thousands_separator='=>'string|null'], +'number_format' => ['string', 'num'=>'float|int', 'decimals='=>'int', 'decimal_separator='=>'string', 'thousands_separator='=>'string'], 'NumberFormatter::__construct' => ['void', 'locale'=>'string', 'style'=>'int', 'pattern='=>'string'], 'NumberFormatter::create' => ['NumberFormatter|false', 'locale'=>'string', 'style'=>'int', 'pattern='=>'string'], 'NumberFormatter::format' => ['string|false', 'num'=>'', 'type='=>'int'], @@ -9093,7 +9088,7 @@ 'OAuth::setTimestamp' => ['mixed', 'timestamp'=>'string'], 'OAuth::setToken' => ['bool', 'token'=>'string', 'token_secret'=>'string'], 'OAuth::setVersion' => ['bool', 'version'=>'string'], -'oauth_get_sbs' => ['string', 'http_method'=>'string', 'uri'=>'string', 'parameters='=>'array'], +'oauth_get_sbs' => ['string', 'http_method'=>'string', 'uri'=>'string', 'request_parameters='=>'array'], 'oauth_urlencode' => ['string', 'uri'=>'string'], 'OAuthProvider::__construct' => ['void', 'params_array='=>'array'], 'OAuthProvider::addRequiredParameter' => ['bool', 'req_params'=>'string'], @@ -9143,13 +9138,13 @@ 'OCICollection::max' => ['int|false'], 'OCICollection::size' => ['int|false'], 'OCICollection::trim' => ['bool', 'num'=>'int'], -'oci_collection_append' => ['bool', 'collection'=>'OCICollection'], -'oci_collection_assign' => ['bool', 'to'=>'OCICollection'], -'oci_collection_element_assign' => ['bool', 'collection'=>'OCICollection', 'index'=>'int'], -'oci_collection_element_get' => ['string', 'collection'=>'OCICollection'], +'oci_collection_append' => ['bool', 'collection'=>'string'], +'oci_collection_assign' => ['bool', 'to'=>'object'], +'oci_collection_element_assign' => ['bool', 'collection'=>'int', 'index'=>'string'], +'oci_collection_element_get' => ['string', 'collection'=>'int'], 'oci_collection_max' => ['int'], 'oci_collection_size' => ['int'], -'oci_collection_trim' => ['bool', 'collection'=>'OCICollection'], +'oci_collection_trim' => ['bool', 'collection'=>'int'], 'oci_commit' => ['bool', 'connection'=>'resource'], 'oci_connect' => ['resource|false', 'username'=>'string', 'password'=>'string', 'connection_string='=>'string', 'encoding='=>'string', 'session_mode='=>'int'], 'oci_define_by_name' => ['bool', 'statement'=>'resource', 'column'=>'string', '&w_var'=>'mixed', 'type='=>'int'], @@ -9197,24 +9192,24 @@ 'OCILob::write' => ['int|false', 'data'=>'string', 'length='=>'int'], 'OCILob::writeTemporary' => ['bool', 'data'=>'string', 'lob_type='=>'int'], 'OCILob::writetofile' => ['bool', 'filename'=>'', 'start'=>'', 'length'=>''], -'oci_lob_append' => ['bool', 'to'=>'OCILob'], +'oci_lob_append' => ['bool', 'to'=>'object'], 'oci_lob_close' => ['bool'], -'oci_lob_copy' => ['bool', 'to'=>'OCILob', 'from'=>'OCILob', 'length='=>'int|null'], +'oci_lob_copy' => ['bool', 'to'=>'OCILob', 'from'=>'OCILob', 'length='=>'int'], 'oci_lob_eof' => ['bool'], -'oci_lob_erase' => ['int', 'lob'=>'OCILob', 'offset'=>'int'], -'oci_lob_export' => ['bool', 'lob'=>'OCILob', 'filename'=>'string', 'offset'=>'int'], -'oci_lob_flush' => ['bool', 'lob'=>'OCILob'], -'oci_lob_import' => ['bool', 'lob'=>'OCILob'], +'oci_lob_erase' => ['int', 'lob'=>'int', 'offset'=>'int'], +'oci_lob_export' => ['bool', 'lob'=>'string', 'filename'=>'int', 'offset'=>'int'], +'oci_lob_flush' => ['bool', 'lob'=>'int'], +'oci_lob_import' => ['bool', 'lob'=>'string'], 'oci_lob_is_equal' => ['bool', 'lob1'=>'OCILob', 'lob2'=>'OCILob'], 'oci_lob_load' => ['string'], -'oci_lob_read' => ['string', 'lob'=>'OCILob'], +'oci_lob_read' => ['string', 'lob'=>'int'], 'oci_lob_rewind' => ['bool'], -'oci_lob_save' => ['bool', 'lob'=>'OCILob', 'data'=>'string'], -'oci_lob_seek' => ['bool', 'lob'=>'OCILob', 'offset'=>'int'], +'oci_lob_save' => ['bool', 'lob'=>'string', 'data'=>'int'], +'oci_lob_seek' => ['bool', 'lob'=>'int', 'offset'=>'int'], 'oci_lob_size' => ['int'], 'oci_lob_tell' => ['int'], -'oci_lob_truncate' => ['bool', 'lob'=>'OCILob'], -'oci_lob_write' => ['int', 'lob'=>'OCILob', 'data'=>'string'], +'oci_lob_truncate' => ['bool', 'lob'=>'int'], +'oci_lob_write' => ['int', 'lob'=>'string', 'data'=>'int'], 'oci_lob_write_temporary' => ['bool', 'value'=>'string', 'lob_type'=>'int'], 'oci_new_collection' => ['OCICollection|false', 'connection'=>'resource', 'type_name'=>'string', 'schema='=>'string'], 'oci_new_connect' => ['resource|false', 'username'=>'string', 'password'=>'string', 'connection_string='=>'string', 'encoding='=>'string', 'session_mode='=>'int'], @@ -9241,7 +9236,7 @@ 'oci_unregister_taf_callback' => ['bool', 'connection'=>'resource'], 'ocifetchinto' => ['int|bool', 'statement'=>'resource', '&w_result'=>'array', 'mode='=>'int'], 'ocigetbufferinglob' => ['bool'], -'ocisetbufferinglob' => ['bool', 'lob'=>'OCILob'], +'ocisetbufferinglob' => ['bool', 'lob'=>'bool'], 'octdec' => ['int|float', 'octal_string'=>'string'], 'odbc_autocommit' => ['mixed', 'odbc'=>'resource', 'enable='=>'bool'], 'odbc_binmode' => ['bool', 'statement'=>'resource', 'mode'=>'int'], @@ -9261,7 +9256,7 @@ 'odbc_fetch_array' => ['array|false', 'statement'=>'resource', 'row='=>'int'], 'odbc_fetch_into' => ['int', 'statement'=>'resource', '&w_array'=>'array', 'row='=>'int'], 'odbc_fetch_object' => ['object|false', 'statement'=>'resource', 'row='=>'int'], -'odbc_fetch_row' => ['bool', 'statement'=>'resource', 'row='=>'int|null'], +'odbc_fetch_row' => ['bool', 'statement'=>'resource', 'row='=>'int'], 'odbc_field_len' => ['int|false', 'statement'=>'resource', 'field'=>'int'], 'odbc_field_name' => ['string|false', 'statement'=>'resource', 'field'=>'int'], 'odbc_field_num' => ['int|false', 'statement'=>'resource', 'field'=>'string'], @@ -9535,13 +9530,13 @@ 'pclose' => ['int', 'handle'=>'resource'], 'pcnlt_sigwaitinfo' => ['int', 'set'=>'array', '&w_siginfo'=>'array'], 'pcntl_alarm' => ['int', 'seconds'=>'int'], -'pcntl_async_signals' => ['bool', 'enable='=>'bool|null'], +'pcntl_async_signals' => ['bool', 'enable='=>'bool'], 'pcntl_errno' => ['int'], 'pcntl_exec' => ['null|false', 'path'=>'string', 'args='=>'array', 'env_vars='=>'array'], 'pcntl_fork' => ['int'], 'pcntl_get_last_error' => ['int'], -'pcntl_getpriority' => ['int', 'process_id='=>'int|null', 'mode='=>'int'], -'pcntl_setpriority' => ['bool', 'priority'=>'int', 'process_id='=>'int|null', 'mode='=>'int'], +'pcntl_getpriority' => ['int', 'process_id='=>'int', 'mode='=>'int'], +'pcntl_setpriority' => ['bool', 'priority'=>'int', 'process_id='=>'int', 'mode='=>'int'], 'pcntl_signal' => ['bool', 'signal'=>'int', 'handler'=>'callable():void|callable(int):void|callable(int,array):void|int', 'restart_syscalls='=>'bool'], 'pcntl_signal_dispatch' => ['bool'], 'pcntl_signal_get_handler' => ['int|string', 'signal'=>'int'], @@ -9927,11 +9922,11 @@ 'PDOStatement::rowCount' => ['int'], 'PDOStatement::setAttribute' => ['bool', 'attribute'=>'int', 'value'=>'mixed'], 'PDOStatement::setFetchMode' => ['bool', 'mode'=>'int', '...args='=> 'mixed'], -'pfsockopen' => ['resource|false', 'hostname'=>'string', 'port='=>'int', '&w_error_code='=>'int', '&w_error_message='=>'string', 'timeout='=>'float|null'], +'pfsockopen' => ['resource|false', 'hostname'=>'string', 'port='=>'int', '&w_error_code='=>'int', '&w_error_message='=>'string', 'timeout='=>'float'], 'pg_affected_rows' => ['int', 'result'=>'\PgSql\Result'], 'pg_cancel_query' => ['bool', 'connection'=>'\PgSql\Connection'], -'pg_client_encoding' => ['string', 'connection='=>'\PgSql\Connection|null'], -'pg_close' => ['bool', 'connection='=>'\PgSql\Connection|null'], +'pg_client_encoding' => ['string', 'connection='=>'\PgSql\Connection'], +'pg_close' => ['bool', 'connection='=>'\PgSql\Connection'], 'pg_connect' => ['\PgSql\Connection|false', 'connection_string'=>'string', 'flags='=>'int'], 'pg_connect_poll' => ['int', 'connection'=>'\PgSql\Connection'], 'pg_connection_busy' => ['bool', 'connection'=>'\PgSql\Connection'], @@ -9941,9 +9936,9 @@ 'pg_convert' => ['array|false', 'connection'=>'\PgSql\Connection', 'table_name'=>'string', 'values'=>'array', 'flags='=>'int'], 'pg_copy_from' => ['bool', 'connection'=>'\PgSql\Connection', 'table_name'=>'string', 'rows'=>'array', 'separator='=>'string', 'null_as='=>'string'], 'pg_copy_to' => ['array|false', 'connection'=>'\PgSql\Connection', 'table_name'=>'string', 'separator='=>'string', 'null_as='=>'string'], -'pg_dbname' => ['string', 'connection='=>'\PgSql\Connection|null'], +'pg_dbname' => ['string', 'connection='=>'\PgSql\Connection'], 'pg_delete' => ['string|bool', 'connection'=>'\PgSql\Connection', 'table_name'=>'string', 'conditions'=>'array', 'flags='=>'int'], -'pg_end_copy' => ['bool', 'connection='=>'\PgSql\Connection|null'], +'pg_end_copy' => ['bool', 'connection='=>'\PgSql\Connection'], 'pg_escape_bytea' => ['string', 'connection'=>'\PgSql\Connection', 'string'=>'string'], 'pg_escape_bytea\'1' => ['string', 'connection'=>'string'], 'pg_escape_identifier' => ['string|false', 'connection'=>'\PgSql\Connection', 'string'=>'string'], @@ -9977,10 +9972,10 @@ 'pg_free_result' => ['bool', 'result'=>'\PgSql\Result'], 'pg_get_notify' => ['array|false', 'result'=>'\PgSql\Result', 'mode='=>'int'], 'pg_get_pid' => ['int', 'connection'=>'\PgSql\Connection'], -'pg_get_result' => ['\PgSql\Result|false', 'connection'=>'\PgSql\Connection'], -'pg_host' => ['string', 'connection='=>'\PgSql\Connection|null'], +'pg_get_result' => ['\PgSql\Result|false', 'connection='=>'\PgSql\Connection'], +'pg_host' => ['string', 'connection='=>'\PgSql\Connection'], 'pg_insert' => ['\PgSql\Result|string|false', 'connection'=>'\PgSql\Connection', 'table_name'=>'string', 'values'=>'array', 'flags='=>'int'], -'pg_last_error' => ['string', 'connection='=>'\PgSql\Connection|null'], +'pg_last_error' => ['string', 'connection='=>'\PgSql\Connection', 'operation='=>'int'], 'pg_last_notice' => ['string|array|bool', 'connection'=>'\PgSql\Connection', 'mode='=>'int'], 'pg_last_oid' => ['string|int|false', 'result'=>'\PgSql\Result'], 'pg_lo_close' => ['bool', 'lob'=>'\PgSql\Lob'], @@ -9998,20 +9993,20 @@ 'pg_lo_truncate' => ['bool', 'lob'=>'\PgSql\Lob', 'size'=>'int'], 'pg_lo_unlink' => ['bool', 'connection'=>'\PgSql\Connection', 'oid'=>'int|string'], 'pg_lo_unlink\'1' => ['bool', 'connection'=>'int|string'], -'pg_lo_write' => ['int|false', 'lob'=>'\PgSql\Lob', 'data'=>'string', 'length='=>'int|null'], +'pg_lo_write' => ['int|false', 'lob'=>'\PgSql\Lob', 'data'=>'string', 'length='=>'int'], 'pg_meta_data' => ['array|false', 'connection'=>'\PgSql\Connection', 'table_name'=>'string', 'extended='=>'bool'], 'pg_num_fields' => ['int', 'result'=>'\PgSql\Result'], 'pg_num_rows' => ['int', 'result'=>'\PgSql\Result'], -'pg_options' => ['string', 'connection='=>'\PgSql\Connection|null'], +'pg_options' => ['string', 'connection='=>'\PgSql\Connection'], 'pg_parameter_status' => ['string|false', 'connection'=>'\PgSql\Connection', 'name'=>'string'], 'pg_parameter_status\'1' => ['string|false', 'connection'=>'string'], -'pg_pconnect' => ['\PgSql\Connection|false', 'connection_string'=>'string', 'flags='=>'int'], -'pg_ping' => ['bool', 'connection='=>'\PgSql\Connection|null'], -'pg_port' => ['int', 'connection='=>'\PgSql\Connection|null'], -'pg_prepare' => ['\PgSql\Result|false', 'connection'=>'\PgSql\Connection|null', 'stmtname'=>'string', 'query'=>'string'], -'pg_prepare\'1' => ['\PgSql\Result|false', 'stmtname'=>'string', 'query'=>'string'], +'pg_pconnect' => ['\PgSql\Connection|false', 'connection_string'=>'string', 'flags='=>'string', 'port='=>'string|int', 'options='=>'string', 'tty='=>'string', 'database='=>'string'], +'pg_ping' => ['bool', 'connection='=>'\PgSql\Connection'], +'pg_port' => ['int', 'connection='=>'\PgSql\Connection'], +'pg_prepare' => ['\PgSql\Result|false', 'connection'=>'\PgSql\Connection', 'statement_name'=>'string', 'query'=>'string'], +'pg_prepare\'1' => ['\PgSql\Result|false', 'connection'=>'string', 'statement_name'=>'string'], 'pg_put_line' => ['bool', 'connection'=>'\PgSql\Connection', 'data'=>'string'], -'pg_put_line\'1' => ['bool', 'data'=>'string'], +'pg_put_line\'1' => ['bool', 'connection'=>'string'], 'pg_query' => ['\PgSql\Result|false', 'connection'=>'\PgSql\Connection', 'query'=>'string'], 'pg_query\'1' => ['\PgSql\Result|false', 'connection'=>'string'], 'pg_query_params' => ['\PgSql\Result|false', 'connection'=>'\PgSql\Connection', 'query'=>'string', 'params'=>'array'], @@ -10030,13 +10025,13 @@ 'pg_set_error_verbosity' => ['int|false', 'connection'=>'\PgSql\Connection', 'verbosity'=>'int'], 'pg_set_error_verbosity\'1' => ['int|false', 'connection'=>'int'], 'pg_socket' => ['resource|false', 'connection'=>'\PgSql\Connection'], -'pg_trace' => ['bool', 'filename'=>'string', 'mode='=>"string", 'connection='=>'\PgSql\Connection|null'], +'pg_trace' => ['bool', 'filename'=>'string', 'mode='=>'string', 'connection='=>'\PgSql\Connection'], 'pg_transaction_status' => ['int', 'connection'=>'\PgSql\Connection'], -'pg_tty' => ['string', 'connection='=>'\PgSql\Connection|null'], +'pg_tty' => ['string', 'connection='=>'\PgSql\Connection'], 'pg_unescape_bytea' => ['string', 'string'=>'string'], -'pg_untrace' => ['bool', 'connection='=>'\PgSql\Connection|null'], +'pg_untrace' => ['bool', 'connection='=>'\PgSql\Connection'], 'pg_update' => ['string|bool', 'connection'=>'\PgSql\Connection', 'table_name'=>'string', 'values'=>'array', 'conditions'=>'array', 'flags='=>'int'], -'pg_version' => ['array', 'connection='=>'\PgSql\Connection|null'], +'pg_version' => ['array', 'connection='=>'\PgSql\Connection'], 'Phar::__construct' => ['void', 'fname'=>'string', 'flags='=>'int', 'alias='=>'string'], 'Phar::addEmptyDir' => ['void', 'dirname'=>'string'], 'Phar::addFile' => ['void', 'file'=>'string', 'localname='=>'string'], @@ -10277,7 +10272,7 @@ 'Postal\Expand::expand_address' => ['string[]', 'address'=>'string', 'options='=>'array'], 'Postal\Parser::parse_address' => ['array', 'address'=>'string', 'options='=>'array'], 'pow' => ['float|int', 'num'=>'int|float', 'exponent'=>'int|float'], -'preg_filter' => ['null|string|string[]', 'pattern'=>'string|array', 'replacement'=>'string|array', 'subject'=>'string|array', 'limit='=>'int', '&w_count='=>'int|null'], +'preg_filter' => ['null|string|string[]', 'pattern'=>'mixed', 'replacement'=>'mixed', 'subject'=>'mixed', 'limit='=>'int', '&w_count='=>'int'], 'preg_grep' => ['array|false', 'pattern'=>'string', 'array'=>'array', 'flags='=>'int'], 'preg_last_error' => ['int'], 'preg_match' => ['int|false', 'pattern'=>'string', 'subject'=>'string', '&w_matches='=>'string[]', 'flags='=>'0', 'offset='=>'int'], @@ -10287,7 +10282,7 @@ 'preg_replace' => ['string|string[]|null', 'pattern'=>'string|array', 'replacement'=>'string|array', 'subject'=>'string|array', 'limit='=>'int', '&w_count='=>'int'], 'preg_replace_callback' => ['string|null', 'pattern'=>'string|array', 'callback'=>'callable(string[]):string', 'subject'=>'string', 'limit='=>'int', '&w_count='=>'int'], 'preg_replace_callback\'1' => ['string[]|null', 'pattern'=>'string|array', 'callback'=>'callable(string[]):string', 'subject'=>'string[]', 'limit='=>'int', '&w_count='=>'int'], -'preg_replace_callback_array' => ['string|string[]|null', 'pattern'=>'array', 'subject'=>'string|array', 'limit='=>'int', '&w_count='=>'int', 'flags=' => 'int'], +'preg_replace_callback_array' => ['string|string[]|null', 'pattern'=>'array', 'subject'=>'string|array', 'limit='=>'int', '&w_count='=>'int'], 'preg_split' => ['list|false', 'pattern'=>'string', 'subject'=>'string', 'limit'=>'int', 'flags='=>'null'], 'preg_split\'1' => ['list|list>|false', 'pattern'=>'string', 'subject'=>'string', 'limit='=>'int', 'flags='=>'int'], 'prev' => ['mixed', '&r_array'=>'array|object'], @@ -12028,10 +12023,10 @@ 'SeekableIterator::rewind' => ['void'], 'SeekableIterator::seek' => ['void', 'position'=>'int'], 'SeekableIterator::valid' => ['bool'], -'sem_acquire' => ['bool', 'semaphore'=>'SysvSemaphore', 'non_blocking='=>'bool'], -'sem_get' => ['SysvSemaphore|false', 'key'=>'int', 'max_acquire='=>'int', 'permissions='=>'int', 'auto_release='=>'bool'], -'sem_release' => ['bool', 'semaphore'=>'SysvSemaphore'], -'sem_remove' => ['bool', 'semaphore'=>'SysvSemaphore'], +'sem_acquire' => ['bool', 'semaphore'=>'resource', 'non_blocking='=>'bool'], +'sem_get' => ['resource|false', 'key'=>'int', 'max_acquire='=>'int', 'permissions='=>'int', 'auto_release='=>'int'], +'sem_release' => ['bool', 'semaphore'=>'resource'], +'sem_remove' => ['bool', 'semaphore'=>'resource'], 'Serializable::__construct' => ['void'], 'Serializable::serialize' => ['?string'], 'Serializable::unserialize' => ['void', 'serialized'=>'string'], @@ -12051,7 +12046,7 @@ 'ServerResponse::setStatus' => ['void', 'status'=>'int'], 'ServerResponse::setVersion' => ['void', 'version'=>'string'], 'session_abort' => ['bool'], -'session_cache_expire' => ['int|false', 'value='=>'int|null'], +'session_cache_expire' => ['int', 'value='=>'int'], 'session_cache_limiter' => ['string', 'value='=>'string'], 'session_commit' => ['bool'], 'session_create_id' => ['string', 'prefix='=>'string'], @@ -12169,19 +12164,19 @@ 'shapeObj::union' => ['shapeObj', 'shape'=>'shapeObj'], 'shapeObj::within' => ['int', 'shape2'=>'shapeObj'], 'shell_exec' => ['string|false|null', 'command'=>'string'], -'shm_attach' => ['SysvSharedMemory|false', 'key'=>'int', 'size='=>'int|null', 'permissions='=>'int'], -'shm_detach' => ['bool', 'shm'=>'SysvSharedMemory'], -'shm_get_var' => ['mixed', 'shm'=>'SysvSharedMemory', 'key'=>'int'], -'shm_has_var' => ['bool', 'shm'=>'SysvSharedMemory', 'key'=>'int'], -'shm_put_var' => ['bool', 'shm'=>'SysvSharedMemory', 'key'=>'int', 'value'=>'mixed'], -'shm_remove' => ['bool', 'shm'=>'SysvSharedMemory'], -'shm_remove_var' => ['bool', 'shm'=>'SysvSharedMemory', 'key'=>'int'], -'shmop_close' => ['void', 'shmop'=>'Shmop'], -'shmop_delete' => ['bool', 'shmop'=>'Shmop'], +'shm_attach' => ['resource', 'key'=>'int', 'size='=>'int', 'permissions='=>'int'], +'shm_detach' => ['bool', 'shm'=>'resource'], +'shm_get_var' => ['mixed', 'shm'=>'resource', 'key'=>'int'], +'shm_has_var' => ['bool', 'shm'=>'resource', 'key'=>'int'], +'shm_put_var' => ['bool', 'shm'=>'resource', 'key'=>'int', 'value'=>'mixed'], +'shm_remove' => ['bool', 'shm'=>'resource'], +'shm_remove_var' => ['bool', 'shm'=>'resource', 'key'=>'int'], +'shmop_close' => ['void', 'shmop'=>'resource'], +'shmop_delete' => ['bool', 'shmop'=>'resource'], 'shmop_open' => ['resource|false', 'key'=>'int', 'mode'=>'string', 'permissions'=>'int', 'size'=>'int'], -'shmop_read' => ['string|false', 'shmop'=>'Shmop', 'offset'=>'int', 'size'=>'int'], -'shmop_size' => ['int', 'shmop'=>'Shmop'], -'shmop_write' => ['int|false', 'shmop'=>'Shmop', 'data'=>'string', 'offset'=>'int'], +'shmop_read' => ['string|false', 'shmop'=>'resource', 'offset'=>'int', 'size'=>'int'], +'shmop_size' => ['int', 'shmop'=>'resource'], +'shmop_write' => ['int|false', 'shmop'=>'resource', 'data'=>'string', 'offset'=>'int'], 'show_source' => ['string|bool', 'filename'=>'string', 'return='=>'bool'], 'shuffle' => ['bool', '&rw_array'=>'array'], 'signeurlpaiement' => ['string', 'clent'=>'string', 'data'=>'string'], @@ -12235,7 +12230,7 @@ 'snmp_get_quick_print' => ['bool'], 'snmp_get_valueretrieval' => ['int'], 'snmp_read_mib' => ['bool', 'filename'=>'string'], -'snmp_set_enum_print' => ['bool', 'enable'=>'bool'], +'snmp_set_enum_print' => ['bool', 'enable'=>'int'], 'snmp_set_oid_numeric_print' => ['void', 'format'=>'int'], 'snmp_set_oid_output_format' => ['bool', 'format'=>'int'], 'snmp_set_quick_print' => ['bool', 'enable'=>'bool'], @@ -12243,7 +12238,7 @@ 'snmpget' => ['string|false', 'hostname'=>'string', 'community'=>'string', 'object_id'=>'string', 'timeout='=>'int', 'retries='=>'int'], 'snmpgetnext' => ['string|false', 'hostname'=>'string', 'community'=>'string', 'object_id'=>'string', 'timeout='=>'int', 'retries='=>'int'], 'snmprealwalk' => ['array|false', 'hostname'=>'string', 'community'=>'string', 'object_id'=>'string', 'timeout='=>'int', 'retries='=>'int'], -'snmpset' => ['bool', 'hostname'=>'string', 'community'=>'string', 'object_id'=>'string', 'type'=>'string', 'value'=>'array|string', 'timeout='=>'int', 'retries='=>'int'], +'snmpset' => ['bool', 'hostname'=>'string', 'community'=>'string', 'object_id'=>'string', 'type'=>'string', 'value'=>'mixed', 'timeout='=>'int', 'retries='=>'int'], 'snmpwalk' => ['array|false', 'hostname'=>'string', 'community'=>'string', 'object_id'=>'string', 'timeout='=>'int', 'retries='=>'int'], 'snmpwalkoid' => ['array|false', 'hostname'=>'string', 'community'=>'string', 'object_id'=>'string', 'timeout='=>'int', 'retries='=>'int'], 'SoapClient::__call' => ['', 'function_name'=>'string', 'arguments'=>'array'], @@ -12293,40 +12288,40 @@ 'socket_addrinfo_bind' => ['Socket|false', 'address'=>'AddressInfo'], 'socket_addrinfo_connect' => ['Socket|false', 'address'=>'AddressInfo'], 'socket_addrinfo_explain' => ['array', 'address'=>'AddressInfo'], -'socket_addrinfo_lookup' => ['false|list', 'host'=>'string', 'service='=>'string|null', 'hints='=>'array'], -'socket_bind' => ['bool', 'socket'=>'Socket', 'address'=>'string', 'port='=>'int'], -'socket_clear_error' => ['void', 'socket='=>'Socket|null'], +'socket_addrinfo_lookup' => ['false|AddressInfo[]', 'host='=>'string|null', 'service='=>'mixed', 'hints='=>'array'], +'socket_bind' => ['bool', 'socket'=>'Socket', 'addr'=>'string', 'port='=>'int'], +'socket_clear_error' => ['void', 'socket='=>'Socket'], 'socket_close' => ['void', 'socket'=>'Socket'], -'socket_cmsg_space' => ['int|null', 'level'=>'int', 'type'=>'int', 'num=' => 'int'], -'socket_connect' => ['bool', 'socket'=>'Socket', 'address'=>'string', 'port='=>'int|null'], +'socket_cmsg_space' => ['int', 'level'=>'int', 'type'=>'int'], +'socket_connect' => ['bool', 'socket'=>'Socket', 'addr'=>'string', 'port='=>'int'], 'socket_create' => ['Socket|false', 'domain'=>'int', 'type'=>'int', 'protocol'=>'int'], 'socket_create_listen' => ['Socket|false', 'port'=>'int', 'backlog='=>'int'], -'socket_create_pair' => ['bool', 'domain'=>'int', 'type'=>'int', 'protocol'=>'int', '&w_pair'=>'Socket[]'], +'socket_create_pair' => ['bool', 'domain'=>'int', 'type'=>'int', 'protocol'=>'int', '&w_fd'=>'Socket[]'], 'socket_export_stream' => ['resource|false', 'socket'=>'Socket'], -'socket_get_option' => ['mixed|false', 'socket'=>'Socket', 'level'=>'int', 'option'=>'int'], +'socket_get_option' => ['mixed|false', 'socket'=>'Socket', 'level'=>'int', 'optname'=>'int'], 'socket_get_status' => ['array', 'stream'=>'Socket'], -'socket_getopt' => ['mixed', 'socket'=>'Socket', 'level'=>'int', 'option'=>'int'], -'socket_getpeername' => ['bool', 'socket'=>'Socket', '&w_address'=>'string', '&w_port='=>'int'], -'socket_getsockname' => ['bool', 'socket'=>'Socket', '&w_address'=>'string', '&w_port='=>'int'], +'socket_getopt' => ['mixed', 'socket'=>'Socket', 'level'=>'int', 'optname'=>'int'], +'socket_getpeername' => ['bool', 'socket'=>'Socket', '&w_addr'=>'string', '&w_port='=>'int'], +'socket_getsockname' => ['bool', 'socket'=>'Socket', '&w_addr'=>'string', '&w_port='=>'int'], 'socket_import_stream' => ['Socket|false|null', 'stream'=>'resource'], -'socket_last_error' => ['int', 'socket='=>'Socket|null'], +'socket_last_error' => ['int', 'socket='=>'Socket'], 'socket_listen' => ['bool', 'socket'=>'Socket', 'backlog='=>'int'], -'socket_read' => ['string|false', 'socket'=>'Socket', 'length'=>'int', 'mode='=>'int'], -'socket_recv' => ['int|false', 'socket'=>'Socket', '&w_data'=>'string', 'length'=>'int', 'flags'=>'int'], -'socket_recvfrom' => ['int|false', 'socket'=>'Socket', '&w_data'=>'string', 'length'=>'int', 'flags'=>'int', '&w_address'=>'string', '&w_port='=>'int'], -'socket_recvmsg' => ['int|false', 'socket'=>'Socket', '&w_message'=>'array', 'flags='=>'int'], -'socket_select' => ['int|false', '&rw_read'=>'Socket[]|null', '&rw_write'=>'Socket[]|null', '&rw_except'=>'Socket[]|null', 'seconds'=>'int|null', 'microseconds='=>'int'], -'socket_send' => ['int|false', 'socket'=>'Socket', 'data'=>'string', 'length'=>'int', 'flags'=>'int'], -'socket_sendmsg' => ['int|false', 'socket'=>'Socket', 'message'=>'array', 'flags='=>'int'], -'socket_sendto' => ['int|false', 'socket'=>'Socket', 'data'=>'string', 'length'=>'int', 'flags'=>'int', 'address'=>'string', 'port='=>'int|null'], +'socket_read' => ['string|false', 'socket'=>'Socket', 'length'=>'int', 'type='=>'int'], +'socket_recv' => ['int|false', 'socket'=>'Socket', '&w_buf'=>'string', 'length'=>'int', 'flags'=>'int'], +'socket_recvfrom' => ['int|false', 'socket'=>'Socket', '&w_buf'=>'string', 'length'=>'int', 'flags'=>'int', '&w_name'=>'string', '&w_port='=>'int'], +'socket_recvmsg' => ['int|false', 'socket'=>'Socket', '&w_message'=>'string', 'flags='=>'int'], +'socket_select' => ['int|false', '&rw_read_fds'=>'Socket[]|null', '&rw_write_fds'=>'Socket[]|null', '&rw_except_fds'=>'Socket[]|null', 'tv_sec'=>'int|null', 'tv_usec='=>'int'], +'socket_send' => ['int|false', 'socket'=>'Socket', 'buf'=>'string', 'length'=>'int', 'flags'=>'int'], +'socket_sendmsg' => ['int|false', 'socket'=>'Socket', 'message'=>'array', 'flags'=>'int'], +'socket_sendto' => ['int|false', 'socket'=>'Socket', 'buf'=>'string', 'length'=>'int', 'flags'=>'int', 'addr'=>'string', 'port='=>'int'], 'socket_set_block' => ['bool', 'socket'=>'Socket'], -'socket_set_blocking' => ['bool', 'stream'=>'resource', 'enable'=>'bool'], +'socket_set_blocking' => ['bool', 'socket'=>'Socket', 'mode'=>'int'], 'socket_set_nonblock' => ['bool', 'socket'=>'Socket'], -'socket_set_option' => ['bool', 'socket'=>'Socket', 'level'=>'int', 'option'=>'int', 'value'=>'int|string|array'], +'socket_set_option' => ['bool', 'socket'=>'Socket', 'level'=>'int', 'optname'=>'int', 'optval'=>'int|string|array'], 'socket_set_timeout' => ['bool', 'stream'=>'resource', 'seconds'=>'int', 'microseconds='=>'int'], -'socket_setopt' => ['void', 'socket'=>'Socket', 'level'=>'int', 'option'=>'int', 'value'=>'int|string|array'], -'socket_shutdown' => ['bool', 'socket'=>'Socket', 'mode='=>'int'], -'socket_strerror' => ['string', 'error_code'=>'int'], +'socket_setopt' => ['void', 'socket'=>'Socket', 'level'=>'int', 'optname'=>'int', 'optval'=>'int|string|array'], +'socket_shutdown' => ['bool', 'socket'=>'Socket', 'how='=>'int'], +'socket_strerror' => ['string', 'errno'=>'int'], 'socket_write' => ['int|false', 'socket'=>'Socket', 'data'=>'string', 'length='=>'int|null'], 'socket_wsaprotocol_info_export' => ['string|false', 'socket'=>'Socket', 'process_id'=>'int'], 'socket_wsaprotocol_info_import' => ['Socket|false', 'info_id'=>'string'], @@ -12363,8 +12358,8 @@ 'sodium_crypto_box_secretkey' => ['string', 'key_pair'=>'string'], 'sodium_crypto_box_seed_keypair' => ['string', 'seed'=>'string'], 'sodium_crypto_generichash' => ['string', 'message'=>'string', 'key='=>'?string', 'length='=>'?int'], -'sodium_crypto_generichash_final' => ['string', '&state'=>'string', 'length='=>'int'], -'sodium_crypto_generichash_init' => ['string', 'key='=>'string', 'length='=>'int'], +'sodium_crypto_generichash_final' => ['string', '&state'=>'string', 'length='=>'?int'], +'sodium_crypto_generichash_init' => ['string', 'key='=>'?string', 'length='=>'?int'], 'sodium_crypto_generichash_keygen' => ['string'], 'sodium_crypto_generichash_update' => ['bool', '&rw_state'=>'string', 'string'=>'string'], 'sodium_crypto_kdf_derive_from_key' => ['string', 'subkey_length'=>'int', 'subkey_id'=>'int', 'context'=>'string', 'key'=>'string'], @@ -12390,9 +12385,9 @@ 'sodium_crypto_secretstream_xchacha20poly1305_init_pull' => ['string', 'header'=>'string', 'key'=>'string'], 'sodium_crypto_secretstream_xchacha20poly1305_init_push' => ['array', 'key'=>'string'], 'sodium_crypto_secretstream_xchacha20poly1305_keygen' => ['string'], -'sodium_crypto_secretstream_xchacha20poly1305_pull' => ['array', '&rw_state'=>'string', 'ciphertext'=>'string', 'additional_data='=>'string'], -'sodium_crypto_secretstream_xchacha20poly1305_push' => ['string', '&rw_state'=>'string', 'message'=>'string', 'additional_data='=>'string', 'tag='=>'int'], -'sodium_crypto_secretstream_xchacha20poly1305_rekey' => ['void', '&rw_state'=>'string'], +'sodium_crypto_secretstream_xchacha20poly1305_pull' => ['array', '&r_state'=>'string', 'ciphertext'=>'string', 'additional_data='=>'string'], +'sodium_crypto_secretstream_xchacha20poly1305_push' => ['string', '&w_state'=>'string', 'message'=>'string', 'additional_data='=>'string', 'tag='=>'int'], +'sodium_crypto_secretstream_xchacha20poly1305_rekey' => ['void', 'state'=>'string'], 'sodium_crypto_shorthash' => ['string', 'message'=>'string', 'key'=>'string'], 'sodium_crypto_shorthash_keygen' => ['string'], 'sodium_crypto_sign' => ['string', 'message'=>'string', 'secret_key'=>'string'], @@ -13619,7 +13614,7 @@ 'sqlsrv_num_fields' => ['int|false', 'stmt'=>'resource'], 'sqlsrv_num_rows' => ['int|false', 'stmt'=>'resource'], 'sqlsrv_prepare' => ['resource|false', 'conn'=>'resource', 'sql'=>'string', 'params='=>'array', 'options='=>'array'], -'sqlsrv_query' => ['resource|false', 'conn'=>'resource', 'tsql'=>'string', 'params='=>'array', 'options='=>'array'], +'sqlsrv_query' => ['resource|false', 'conn'=>'resource', 'sql'=>'string', 'params='=>'array', 'options='=>'array'], 'sqlsrv_rollback' => ['bool', 'conn'=>'resource'], 'sqlsrv_rows_affected' => ['int|false', 'stmt'=>'resource'], 'sqlsrv_send_stream_data' => ['bool', 'stmt'=>'resource'], @@ -13755,10 +13750,10 @@ 'Stomp::unsubscribe' => ['bool', 'destination'=>'string', 'headers='=>'array', 'link='=>''], 'stomp_abort' => ['bool', 'transaction_id'=>'string', 'headers='=>'array', 'link='=>''], 'stomp_ack' => ['bool', 'msg'=>'', 'headers='=>'array', 'link='=>''], -'stomp_begin' => ['bool', 'link' => 'resource', 'transaction_id'=>'string', 'headers='=>'array|null'], -'stomp_close' => ['bool', 'link'=>'resource'], +'stomp_begin' => ['bool', 'transaction_id'=>'string', 'headers='=>'array', 'link='=>''], +'stomp_close' => ['bool', 'link'=>''], 'stomp_commit' => ['bool', 'transaction_id'=>'string', 'headers='=>'array', 'link='=>''], -'stomp_connect' => ['resource', 'broker='=>'string', 'username='=>'string', 'password='=>'string', 'headers='=>'array|null'], +'stomp_connect' => ['resource', 'broker='=>'string', 'username='=>'string', 'password='=>'string', 'headers='=>'array'], 'stomp_connect_error' => ['string'], 'stomp_error' => ['string', 'link'=>''], 'stomp_get_read_timeout' => ['array', 'link'=>''], @@ -13788,26 +13783,26 @@ 'strchr' => ['string|false', 'haystack'=>'string', 'needle'=>'string', 'before_needle='=>'bool'], 'strcmp' => ['int', 'string1'=>'string', 'string2'=>'string'], 'strcoll' => ['int', 'string1'=>'string', 'string2'=>'string'], -'strcspn' => ['int', 'string'=>'string', 'characters'=>'string', 'offset='=>'int', 'length='=>'int|null'], +'strcspn' => ['int', 'string'=>'string', 'characters'=>'string', 'offset='=>'int', 'length='=>'int'], 'stream_bucket_append' => ['void', 'brigade'=>'resource', 'bucket'=>'object'], 'stream_bucket_make_writeable' => ['object', 'brigade'=>'resource'], 'stream_bucket_new' => ['object|false', 'stream'=>'resource', 'buffer'=>'string'], 'stream_bucket_prepend' => ['void', 'brigade'=>'resource', 'bucket'=>'object'], -'stream_context_create' => ['resource', 'options='=>'array|null', 'params='=>'array|null'], -'stream_context_get_default' => ['resource', 'options='=>'array|null'], +'stream_context_create' => ['resource', 'options='=>'array', 'params='=>'array'], +'stream_context_get_default' => ['resource', 'options='=>'array'], 'stream_context_get_options' => ['array', 'stream_or_context'=>'resource'], 'stream_context_get_params' => ['array', 'context'=>'resource'], 'stream_context_set_default' => ['resource', 'options'=>'array'], 'stream_context_set_option' => ['bool', 'context'=>'', 'wrapper_or_options'=>'string', 'option_name'=>'string', 'value'=>''], 'stream_context_set_option\'1' => ['bool', 'context'=>'', 'wrapper_or_options'=>'array'], 'stream_context_set_params' => ['bool', 'context'=>'resource', 'params'=>'array'], -'stream_copy_to_stream' => ['int|false', 'from'=>'resource', 'to'=>'resource', 'length='=>'int|null', 'offset='=>'int'], +'stream_copy_to_stream' => ['int|false', 'from'=>'resource', 'to'=>'resource', 'length='=>'int', 'offset='=>'int'], 'stream_encoding' => ['bool', 'stream'=>'resource', 'encoding='=>'string'], 'stream_filter_append' => ['resource|false', 'stream'=>'resource', 'filter_name'=>'string', 'mode='=>'int', 'params='=>'mixed'], 'stream_filter_prepend' => ['resource|false', 'stream'=>'resource', 'filter_name'=>'string', 'mode='=>'int', 'params='=>'mixed'], 'stream_filter_register' => ['bool', 'filter_name'=>'string', 'class'=>'string'], 'stream_filter_remove' => ['bool', 'stream_filter'=>'resource'], -'stream_get_contents' => ['string|false', 'stream'=>'resource', 'length='=>'int|null', 'offset='=>'int'], +'stream_get_contents' => ['string|false', 'stream'=>'resource', 'length='=>'int', 'offset='=>'int'], 'stream_get_filters' => ['array'], 'stream_get_line' => ['string|false', 'stream'=>'resource', 'length'=>'int', 'ending='=>'string'], 'stream_get_meta_data' => ['array{timed_out:bool,blocked:bool,eof:bool,unread_bytes:int,stream_type:string,wrapper_type:string,wrapper_data:mixed,mode:string,seekable:bool,uri:string,mediatype:string,crypto?:array{protocol:string,cipher_name:string,cipher_bits:int,cipher_version:string}}', 'stream'=>'resource'], @@ -13818,15 +13813,15 @@ 'stream_notification_callback' => ['callback', 'notification_code'=>'int', 'severity'=>'int', 'message'=>'string', 'message_code'=>'int', 'bytes_transferred'=>'int', 'bytes_max'=>'int'], 'stream_register_wrapper' => ['bool', 'protocol'=>'string', 'class'=>'string', 'flags='=>'int'], 'stream_resolve_include_path' => ['string|false', 'filename'=>'string'], -'stream_select' => ['int|false', '&rw_read'=>'array|null', '&rw_write'=>'array|null', '&rw_except'=>'array|null', 'seconds'=>'int|null', 'microseconds='=>'int|null'], +'stream_select' => ['int|false', '&rw_read'=>'resource[]', '&rw_write'=>'?resource[]', '&rw_except'=>'?resource[]', 'seconds'=>'?int', 'microseconds='=>'?int'], 'stream_set_blocking' => ['bool', 'stream'=>'resource', 'enable'=>'bool'], 'stream_set_chunk_size' => ['int|false', 'stream'=>'resource', 'size'=>'int'], 'stream_set_read_buffer' => ['int', 'stream'=>'resource', 'size'=>'int'], 'stream_set_timeout' => ['bool', 'stream'=>'resource', 'seconds'=>'int', 'microseconds='=>'int'], 'stream_set_write_buffer' => ['int', 'stream'=>'resource', 'size'=>'int'], -'stream_socket_accept' => ['resource|false', 'socket'=>'resource', 'timeout='=>'float|null', '&w_peer_name='=>'string|null'], -'stream_socket_client' => ['resource|false', 'address'=>'string', '&w_error_code='=>'int|null', '&w_error_message='=>'string|null', 'timeout='=>'float|null', 'flags='=>'int', 'context='=>'resource|null'], -'stream_socket_enable_crypto' => ['int|bool', 'stream'=>'resource', 'enable'=>'bool', 'crypto_method='=>'int|null', 'session_stream='=>'resource|null'], +'stream_socket_accept' => ['resource|false', 'socket'=>'resource', 'timeout='=>'float', '&w_peer_name='=>'string'], +'stream_socket_client' => ['resource|false', 'address'=>'string', '&w_error_code='=>'int', '&w_error_message='=>'string', 'timeout='=>'float', 'flags='=>'int', 'context='=>'resource'], +'stream_socket_enable_crypto' => ['int|bool', 'stream'=>'resource', 'enable'=>'bool', 'crypto_method='=>'int', 'session_stream='=>'resource'], 'stream_socket_get_name' => ['string', 'socket'=>'resource', 'remote'=>'bool'], 'stream_socket_pair' => ['resource[]|false', 'domain'=>'int', 'type'=>'int', 'protocol'=>'int'], 'stream_socket_recvfrom' => ['string', 'socket'=>'resource', 'length'=>'int', 'flags='=>'int', '&w_address='=>'string'], @@ -13880,12 +13875,12 @@ 'strrev' => ['string', 'string'=>'string'], 'strripos' => ['int|false', 'haystack'=>'string', 'needle'=>'string', 'offset='=>'int'], 'strrpos' => ['int|false', 'haystack'=>'string', 'needle'=>'string', 'offset='=>'int'], -'strspn' => ['int', 'string'=>'string', 'characters'=>'string', 'offset='=>'int', 'length='=>'int|null'], +'strspn' => ['int', 'string'=>'string', 'characters'=>'string', 'offset='=>'int', 'length='=>'int'], 'strstr' => ['string|false', 'haystack'=>'string', 'needle'=>'string', 'before_needle='=>'bool'], 'strtok' => ['string|false', 'string'=>'string', 'token'=>'string'], 'strtok\'1' => ['string|false', 'string'=>'string'], 'strtolower' => ['lowercase-string', 'string'=>'string'], -'strtotime' => ['int|false', 'datetime'=>'string', 'baseTimestamp='=>'int|null'], +'strtotime' => ['int|false', 'datetime'=>'string', 'baseTimestamp='=>'int'], 'strtoupper' => ['string', 'string'=>'string'], 'strtr' => ['string', 'string'=>'string', 'from'=>'string', 'to'=>'string'], 'strtr\'1' => ['string', 'string'=>'string', 'from'=>'array'], @@ -13901,10 +13896,10 @@ 'styleObj::setBinding' => ['int', 'stylebinding'=>'mixed', 'value'=>'string'], 'styleObj::setGeomTransform' => ['int', 'value'=>'string'], 'styleObj::updateFromString' => ['int', 'snippet'=>'string'], -'substr' => ['string|false', 'string'=>'string', 'offset'=>'int', 'length='=>'int|null'], -'substr_compare' => ['int|false', 'haystack'=>'string', 'needle'=>'string', 'offset'=>'int', 'length='=>'int|null', 'case_insensitive='=>'bool'], -'substr_count' => ['int', 'haystack'=>'string', 'needle'=>'string', 'offset='=>'int', 'length='=>'int|null'], -'substr_replace' => ['string|string[]', 'string'=>'string|string[]', 'replace'=>'string|string[]', 'offset'=>'int|int[]', 'length='=>'int|int[]|null'], +'substr' => ['string|false', 'string'=>'string', 'offset'=>'int', 'length='=>'int'], +'substr_compare' => ['int|false', 'haystack'=>'string', 'needle'=>'string', 'offset'=>'int', 'length='=>'int', 'case_insensitive='=>'bool'], +'substr_count' => ['int', 'haystack'=>'string', 'needle'=>'string', 'offset='=>'int', 'length='=>'int'], +'substr_replace' => ['string|string[]', 'string'=>'string|string[]', 'replace'=>'mixed', 'offset'=>'mixed', 'length='=>'mixed'], 'suhosin_encrypt_cookie' => ['string|false', 'name'=>'string', 'value'=>'string'], 'suhosin_get_raw_cookies' => ['array'], 'SVM::__construct' => ['void'], @@ -14605,7 +14600,7 @@ 'tidy_get_release' => ['string'], 'tidy_get_root' => ['tidyNode', 'tidy'=>'tidy'], 'tidy_get_status' => ['int', 'tidy'=>'tidy'], -'tidy_getopt' => ['string|int|bool', 'tidy'=>'tidy', 'option'=>'string'], +'tidy_getopt' => ['mixed', 'tidy'=>'string', 'option'=>'tidy'], 'tidy_is_xhtml' => ['bool', 'tidy'=>'tidy'], 'tidy_is_xml' => ['bool', 'tidy'=>'tidy'], 'tidy_load_config' => ['void', 'filename'=>'string', 'encoding'=>'string'], @@ -14698,7 +14693,7 @@ 'TokyoTyrantTable::putNr' => ['void', 'keys'=>'mixed', 'value='=>'string'], 'TokyoTyrantTable::putShl' => ['void', 'key'=>'string', 'value'=>'string', 'width'=>'int'], 'TokyoTyrantTable::setIndex' => ['mixed', 'column'=>'string', 'type'=>'int'], -'touch' => ['bool', 'filename'=>'string', 'mtime='=>'int|null', 'atime='=>'int|null'], +'touch' => ['bool', 'filename'=>'string', 'mtime='=>'int', 'atime='=>'int'], 'trader_acos' => ['array', 'real'=>'array'], 'trader_ad' => ['array', 'high'=>'array', 'low'=>'array', 'close'=>'array', 'volume'=>'array'], 'trader_add' => ['array', 'real0'=>'array', 'real1'=>'array'], @@ -15101,7 +15096,7 @@ 'ui\window::setSize' => ['', 'size'=>'UI\Size'], 'ui\window::setTitle' => ['', 'title'=>'string'], 'uksort' => ['bool', '&rw_array'=>'array', 'callback'=>'callable(mixed,mixed):int'], -'umask' => ['int', 'mask='=>'int|null'], +'umask' => ['int', 'mask='=>'int'], 'UnderflowException::__clone' => ['void'], 'UnderflowException::__construct' => ['void', 'message='=>'string', 'code='=>'int', 'previous='=>'?Throwable|?UnderflowException'], 'UnderflowException::__toString' => ['string'], @@ -15123,7 +15118,7 @@ 'UnexpectedValueException::getTrace' => ['list\',args?:array}>'], 'UnexpectedValueException::getTraceAsString' => ['string'], 'uniqid' => ['non-empty-string', 'prefix='=>'string', 'more_entropy='=>'bool'], -'unixtojd' => ['int', 'timestamp='=>'int|null'], +'unixtojd' => ['int', 'timestamp='=>'int'], 'unlink' => ['bool', 'filename'=>'string', 'context='=>'resource'], 'unpack' => ['array|false', 'format'=>'string', 'string'=>'string', 'offset='=>'int'], 'unregister_tick_function' => ['void', 'callback'=>'callable'], @@ -15664,12 +15659,12 @@ 'xmlrpc_get_type' => ['string', 'value'=>'mixed'], 'xmlrpc_is_fault' => ['bool', 'arg'=>'array'], 'xmlrpc_parse_method_descriptions' => ['array', 'xml'=>'string'], -'xmlrpc_server_add_introspection_data' => ['int', 'server'=>'XmlRpcServer', 'desc'=>'array'], -'xmlrpc_server_call_method' => ['string', 'server'=>'XmlRpcServer', 'xml'=>'string', 'user_data'=>'mixed', 'output_options='=>'array'], -'xmlrpc_server_create' => ['XmlRpcServer'], -'xmlrpc_server_destroy' => ['int', 'server'=>'XmlRpcServer'], -'xmlrpc_server_register_introspection_callback' => ['bool', 'server'=>'XmlRpcServer', 'function'=>'string'], -'xmlrpc_server_register_method' => ['bool', 'server'=>'XmlRpcServer', 'method_name'=>'string', 'function'=>'string'], +'xmlrpc_server_add_introspection_data' => ['int', 'server'=>'resource', 'desc'=>'array'], +'xmlrpc_server_call_method' => ['string', 'server'=>'resource', 'xml'=>'string', 'user_data'=>'mixed', 'output_options='=>'array'], +'xmlrpc_server_create' => ['resource'], +'xmlrpc_server_destroy' => ['int', 'server'=>'resource'], +'xmlrpc_server_register_introspection_callback' => ['bool', 'server'=>'resource', 'function'=>'string'], +'xmlrpc_server_register_method' => ['bool', 'server'=>'resource', 'method_name'=>'string', 'function'=>'string'], 'xmlrpc_set_type' => ['bool', '&rw_value'=>'string|DateTime', 'type'=>'string'], 'XMLWriter::endAttribute' => ['bool'], 'XMLWriter::endCdata' => ['bool'], @@ -16758,7 +16753,7 @@ 'ZipArchive::unchangeIndex' => ['bool', 'index'=>'int'], 'ZipArchive::unchangeName' => ['bool', 'name'=>'string'], 'zlib_decode' => ['string|false', 'data'=>'string', 'max_length='=>'int'], -'zlib_encode' => ['string', 'data'=>'string', 'encoding'=>'int', 'level='=>'int<1,9>'], +'zlib_encode' => ['string', 'data'=>'string', 'encoding'=>'int', 'level='=>'string|int'], 'zlib_get_coding_type' => ['string|false'], 'ZMQ::__construct' => ['void'], 'ZMQContext::__construct' => ['void', 'io_threads='=>'int', 'is_persistent='=>'bool'], diff --git a/dictionaries/CallMap_80_delta.php b/dictionaries/CallMap_80_delta.php index 79cb83b7643..f905592effe 100644 --- a/dictionaries/CallMap_80_delta.php +++ b/dictionaries/CallMap_80_delta.php @@ -969,6 +969,10 @@ 'old' => ['string', 'num'=>'float|int', 'decimals='=>'int'], 'new' => ['string', 'num'=>'float|int', 'decimals='=>'int', 'decimal_separator='=>'string', 'thousands_separator='=>'string'], ], + 'ob_implicit_flush' => [ + 'old' => ['void', 'enable='=>'int'], + 'new' => ['void', 'enable='=>'bool'], + ], 'openssl_csr_export' => [ 'old' => ['bool', 'csr'=>'string|resource', '&w_output'=>'string', 'no_text='=>'bool'], 'new' => ['bool', 'csr'=>'OpenSSLCertificateSigningRequest|string', '&w_output'=>'string', 'no_text='=>'bool'], @@ -1599,7 +1603,6 @@ ], ], 'removed' => [ - 'fgetss' => ['string|false', 'fp'=>'resource', 'length='=>'int', 'allowable_tags='=>'string'], 'PDOStatement::setFetchMode\'1' => ['bool', 'fetch_column'=>'int', 'colno'=>'int'], 'PDOStatement::setFetchMode\'2' => ['bool', 'fetch_class'=>'int', 'classname'=>'string', 'ctorargs'=>'array'], 'PDOStatement::setFetchMode\'3' => ['bool', 'fetch_into'=>'int', 'object'=>'object'], diff --git a/dictionaries/CallMap_81_delta.php b/dictionaries/CallMap_81_delta.php index 09682ee4810..4f6092ad6f7 100644 --- a/dictionaries/CallMap_81_delta.php +++ b/dictionaries/CallMap_81_delta.php @@ -46,15 +46,8 @@ 'ReflectionEnumUnitCase::getValue' => ['UnitEnum'], 'ReflectionEnumBackedCase::getBackingValue' => ['string|int'], 'ReflectionFunctionAbstract::isStatic' => ['bool'], - 'imagefilter\'1' => ['bool', 'image' => 'GdImage', 'filter' => '2', 'args' => 'int<-255,255>'], - 'imagefilter\'2' => ['bool', 'image' => 'GdImage', 'filter' => '3|10', 'args' => 'int'], - 'imagefilter\'3' => ['bool', 'image' => 'GdImage', 'filter' => '4', 'args' => 'int<0,255>', 'args2' => 'int<0,255>', 'args3' => 'int<0,255>', 'args4' => 'int<0,127>'], - 'imagefilter\'4' => ['bool', 'image' => 'GdImage', 'filter' => '11', 'args' => 'int', 'args2=' => 'bool'], - 'imagefilter\'5' => ['bool', 'image' => 'GdImage', 'filter' => '12', 'args' => 'int', 'args2=' => 'int', 'args3=' => 'array'], - ], - 'changed' => [ 'finfo_buffer' => [ 'old' => ['string|false', 'finfo'=>'resource', 'string'=>'string', 'flags='=>'int', 'context='=>'resource'], diff --git a/tests/Internal/Codebase/InternalCallMapHandlerTest.php b/tests/Internal/Codebase/InternalCallMapHandlerTest.php index 11acd484270..19ec7f8d354 100644 --- a/tests/Internal/Codebase/InternalCallMapHandlerTest.php +++ b/tests/Internal/Codebase/InternalCallMapHandlerTest.php @@ -27,35 +27,77 @@ class InternalCallMapHandlerTest extends TestCase 'dom_import_simplexml', 'imagegd', 'imagegd2', 'mysqli_execute', 'array_multisort', 'intlcal_from_date_time', 'simplexml_import_dom', 'imagefilledpolygon', /** deprecated in 8.0 */ - 'zip_entry_close' + 'zip_entry_close', + 'date_time_set', + 'curl_unescape', + 'extract', + 'enum_exists', + 'igbinary_unserialize', + 'count', + 'lzf_compress', + 'long2ip', + 'array_column', + 'preg_replace_callback_array', + 'preg_filter', + 'zlib_encode', + 'inotify_rm_watch', + 'mail', + 'easter_date', + 'date_isodate_set', + 'snmpset', + 'get_class_methods', + 'filter_var_array', + 'deflate_add', + 'bzdecompress', + 'substr_replace', + 'lzf_decompress', + 'mongodb\bson\tophp', + 'fputcsv', + 'get_headers', + 'get_parent_class', + 'filter_var', + 'array_key_exists', + ]; private static $ignoredPrefixes = [ - 'memcache_', - 'gnupg_', - 'mailparse_', - 'xdiff_', - 'oci', - 'ldap_', - 'yaml_', + 'apcu_', + 'bc', 'collator_', 'ctype_', - 'imap_', + 'datefmt_', + 'enchant_', 'gmp_', - 'uopz_', - 'openssl_', - 'tidy_', + 'gnupg_', 'image', - 'transliterator_', - 'pg_', - 'odbc_', - 'sqlsrv_', + 'imap_', + 'inflate_', + 'intl', + 'ldap_', + 'mailparse_', + 'memcache_', + 'msg_', 'mysqli_', - 'apcu_', - 'bc', + 'normalizer_', + 'oauth_', + 'oci', + 'odbc_', + 'openssl_', + 'pg_', + 'sem_', + 'shm_', + 'shmop_', + 'snmp_', + 'socket_', 'sodium_', - 'intl', + 'sqlsrv_', + 'tidy_', + 'transliterator_', + 'uopz_', + 'xdiff_', + 'xmlrpc_server', + 'yaml_', ]; /** From a86dbf3640498eb55b6419f3d86c45cd993e78a9 Mon Sep 17 00:00:00 2001 From: kkmuffme <11071985+kkmuffme@users.noreply.github.com> Date: Sat, 11 Jun 2022 15:07:24 +0200 Subject: [PATCH 37/88] update test cases --- tests/FunctionLikeDocblockParserTest.php | 62 ++++++++++++++++++++++-- 1 file changed, 57 insertions(+), 5 deletions(-) diff --git a/tests/FunctionLikeDocblockParserTest.php b/tests/FunctionLikeDocblockParserTest.php index 44c6cf9ef37..5756333193b 100644 --- a/tests/FunctionLikeDocblockParserTest.php +++ b/tests/FunctionLikeDocblockParserTest.php @@ -4,15 +4,47 @@ use PHPUnit\Framework\TestCase as BaseTestCase; use PhpParser\Comment\Doc; +use PhpParser\Node\Scalar\String_; +use Psalm\CodeLocation; use Psalm\Exception\IncorrectDocblockException; +use Psalm\Internal\Analyzer\FileAnalyzer; +use Psalm\Internal\Analyzer\ProjectAnalyzer; use Psalm\Internal\PhpVisitor\Reflector\FunctionLikeDocblockParser; +use Psalm\Internal\Provider\FakeFileProvider; +use Psalm\Internal\Provider\Providers; use Psalm\Internal\RuntimeCaches; +use Psalm\Tests\Internal\Provider\FakeParserCacheProvider; class FunctionLikeDocblockParserTest extends BaseTestCase { + /** @var string */ + public $test_cased_function_id = 'hello_world'; + + /** @var CodeLocation */ + public $test_code_location; + public function setUp(): void { RuntimeCaches::clearAll(); + + $file_provider = new FakeFileProvider(); + + $providers = new Providers( + $file_provider, + new FakeParserCacheProvider() + ); + + $test_config = new TestConfig(); + + $project_analyzer = new ProjectAnalyzer( + $test_config, + $providers + ); + + $file_analyzer = new FileAnalyzer($project_analyzer, 'none/none.php', 'none.php'); + + $stmt = new String_('randomString'); + $this->test_code_location = new CodeLocation($file_analyzer, $stmt); } public function testDocblockDescription(): void @@ -29,7 +61,11 @@ public function testDocblockDescription(): void */ '; $php_parser_doc = new Doc($doc); - $function_docblock = FunctionLikeDocblockParser::parse($php_parser_doc); + $function_docblock = FunctionLikeDocblockParser::parse( + $php_parser_doc, + $this->test_code_location, + $this->test_cased_function_id + ); $this->assertSame('Some Description', $function_docblock->description); } @@ -49,7 +85,11 @@ public function testDocblockParamDescription(): void */ '; $php_parser_doc = new Doc($doc); - $function_docblock = FunctionLikeDocblockParser::parse($php_parser_doc); + $function_docblock = FunctionLikeDocblockParser::parse( + $php_parser_doc, + $this->test_code_location, + $this->test_cased_function_id + ); $this->assertTrue(isset($function_docblock->params[0]['description'])); $this->assertSame('The BLI tag to iterate over.', $function_docblock->params[0]['description']); @@ -67,7 +107,11 @@ public function testMisplacedVariableOnNextLine(): void $php_parser_doc = new Doc($doc); $this->expectException(IncorrectDocblockException::class); $this->expectExceptionMessage('Misplaced variable'); - FunctionLikeDocblockParser::parse($php_parser_doc); + FunctionLikeDocblockParser::parse( + $php_parser_doc, + $this->test_code_location, + $this->test_cased_function_id + ); } public function testPreferPsalmPrefixedAnnotationsOverPhpstanOnes(): void @@ -78,7 +122,11 @@ public function testPreferPsalmPrefixedAnnotationsOverPhpstanOnes(): void */ '; $php_parser_doc = new Doc($doc); - $function_docblock = FunctionLikeDocblockParser::parse($php_parser_doc); + $function_docblock = FunctionLikeDocblockParser::parse( + $php_parser_doc, + $this->test_code_location, + $this->test_cased_function_id + ); $this->assertSame([['T', 'of', 'string', false]], $function_docblock->templates); } @@ -90,7 +138,11 @@ public function testReturnsUnexpectedTags(): void */ '; $php_parser_doc = new Doc($doc, 0); - $function_docblock = FunctionLikeDocblockParser::parse($php_parser_doc); + $function_docblock = FunctionLikeDocblockParser::parse( + $php_parser_doc, + $this->test_code_location, + $this->test_cased_function_id + ); $this->assertEquals( [ 'psalm-import-type' => ['lines' => [1]], From 0484ee156064707fac40f4b614af44f61d97eb63 Mon Sep 17 00:00:00 2001 From: Sam Mousa Date: Wed, 15 Jun 2022 13:13:24 +0200 Subject: [PATCH 38/88] chore: remove unused property --- tests/Internal/Codebase/InternalCallMapHandlerTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Internal/Codebase/InternalCallMapHandlerTest.php b/tests/Internal/Codebase/InternalCallMapHandlerTest.php index 19ec7f8d354..9462658a117 100644 --- a/tests/Internal/Codebase/InternalCallMapHandlerTest.php +++ b/tests/Internal/Codebase/InternalCallMapHandlerTest.php @@ -113,7 +113,7 @@ class InternalCallMapHandlerTest extends TestCase private $skipUndefinedParams = false; private static $codebase; - private static $callMap; + From c3a97e025878449f15f503fe2f22c34082fdb7f3 Mon Sep 17 00:00:00 2001 From: Sam Mousa Date: Wed, 15 Jun 2022 13:16:24 +0200 Subject: [PATCH 39/88] fix: add typehints --- tests/Internal/Codebase/InternalCallMapHandlerTest.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/Internal/Codebase/InternalCallMapHandlerTest.php b/tests/Internal/Codebase/InternalCallMapHandlerTest.php index 9462658a117..76899c5b81f 100644 --- a/tests/Internal/Codebase/InternalCallMapHandlerTest.php +++ b/tests/Internal/Codebase/InternalCallMapHandlerTest.php @@ -21,6 +21,9 @@ class InternalCallMapHandlerTest extends TestCase { + /** + * @var string[] + */ private static $ignoredFunctions = [ 'sprintf', 'printf', 'ctype_print', 'date_sunrise' /** deprecated in 8.1 */, 'file_put_contents', @@ -61,6 +64,9 @@ class InternalCallMapHandlerTest extends TestCase ]; + /** + * @var string[] + */ private static $ignoredPrefixes = [ 'apcu_', 'bc', @@ -112,6 +118,10 @@ class InternalCallMapHandlerTest extends TestCase */ private $skipUndefinedParams = false; + /** + * + * @var Codebase + */ private static $codebase; From 0e7ea855d0290eec0a786421c60e10440dae0ce1 Mon Sep 17 00:00:00 2001 From: Sam Mousa Date: Wed, 15 Jun 2022 13:23:32 +0200 Subject: [PATCH 40/88] fix: ignore functions that fail on windows --- tests/Internal/Codebase/InternalCallMapHandlerTest.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/Internal/Codebase/InternalCallMapHandlerTest.php b/tests/Internal/Codebase/InternalCallMapHandlerTest.php index 76899c5b81f..bfddbb35ed5 100644 --- a/tests/Internal/Codebase/InternalCallMapHandlerTest.php +++ b/tests/Internal/Codebase/InternalCallMapHandlerTest.php @@ -60,6 +60,9 @@ class InternalCallMapHandlerTest extends TestCase 'get_parent_class', 'filter_var', 'array_key_exists', + 'sapi_windows_cp_get', + 'stream_select', + ]; @@ -164,6 +167,10 @@ public function testGetcallmapReturnsAValidCallmap(): void } } + /** + * + * @return iterable + */ public function callMapEntryProvider(): iterable { From b46f55666292fc3f6034112f114cb09621bdf0ca Mon Sep 17 00:00:00 2001 From: Sam Mousa Date: Wed, 15 Jun 2022 13:28:09 +0200 Subject: [PATCH 41/88] chore: fix cs --- .../Codebase/InternalCallMapHandlerTest.php | 38 ++++++++++--------- 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/tests/Internal/Codebase/InternalCallMapHandlerTest.php b/tests/Internal/Codebase/InternalCallMapHandlerTest.php index bfddbb35ed5..c1898799077 100644 --- a/tests/Internal/Codebase/InternalCallMapHandlerTest.php +++ b/tests/Internal/Codebase/InternalCallMapHandlerTest.php @@ -9,15 +9,26 @@ use Psalm\Internal\Provider\FakeFileProvider; use Psalm\Internal\Provider\Providers; use Psalm\Internal\Type\Comparator\UnionTypeComparator; -use Psalm\Internal\Type\TypeParser; use Psalm\Tests\Internal\Provider\FakeParserCacheProvider; use Psalm\Tests\TestCase; use Psalm\Tests\TestConfig; use Psalm\Type; use ReflectionFunction; -use ReflectionNamedType; use ReflectionParameter; use ReflectionType; +use Throwable; + +use function count; +use function explode; +use function function_exists; +use function implode; +use function in_array; +use function json_encode; +use function preg_match; +use function print_r; +use function strncmp; +use function strpos; +use function substr; class InternalCallMapHandlerTest extends TestCase { @@ -147,7 +158,6 @@ public static function setUpBeforeClass(): void public static function tearDownAfterClass(): void { self::$codebase = null; - } /** @@ -182,7 +192,7 @@ public function callMapEntryProvider(): iterable ) ); $callMap = InternalCallMapHandler::getCallMap(); - foreach($callMap as $function => $entry) { + foreach ($callMap as $function => $entry) { // Skip class methods if (strpos($function, '::') !== false || !function_exists($function)) { continue; @@ -194,13 +204,10 @@ public function callMapEntryProvider(): iterable // if ($function != 'fprintf') continue; yield "$function: " . json_encode($entry) => [$function, $entry]; } - - } /** * This function will test functions that are in the callmap AND currently defined - * @return void * @coversNothing * @depends testGetcallmapReturnsAValidCallmap * @dataProvider callMapEntryProvider @@ -217,7 +224,6 @@ public function testCallMapCompliesWithReflection(string $functionName, array $c $this->markTestSkipped("Function $functionName has ignored prefix"); } $this->assertEntryIsCorrect($callMapEntry, $functionName); - } private function assertEntryIsCorrect(array $callMapEntry, string $functionName): void @@ -233,7 +239,7 @@ private function assertEntryIsCorrect(array $callMapEntry, string $functionName) */ $normalizedEntries = []; - foreach($callMapEntry as $key => $entry) { + foreach ($callMapEntry as $key => $entry) { $normalizedKey = $key; $normalizedEntry = [ 'variadic' => false, @@ -270,9 +276,8 @@ private function assertEntryIsCorrect(array $callMapEntry, string $functionName) $normalizedEntry['name'] = $normalizedKey; $normalizedEntries[$normalizedKey] = $normalizedEntry; - } - foreach($rF->getParameters() as $parameter) { + foreach ($rF->getParameters() as $parameter) { if ($this->skipUndefinedParams && !isset($normalizedEntries[$parameter->getName()])) { continue; } else { @@ -280,7 +285,6 @@ private function assertEntryIsCorrect(array $callMapEntry, string $functionName) } $this->assertParameter($normalizedEntries[$parameter->getName()], $parameter, $functionName); } - } /** @@ -292,10 +296,10 @@ private function assertParameter(array $normalizedEntry, ReflectionParameter $pa $name = $param->getName(); // $identifier = "Param $functionName - $name"; try { - $this->assertSame($param->isOptional(), $normalizedEntry['optional'], "Expected param '{$name}' to " . ($param->isOptional() ? "be" : "not be") . " optional"); - $this->assertSame($param->isVariadic(), $normalizedEntry['variadic'], "Expected param '{$name}' to " . ($param->isVariadic() ? "be" : "not be") . " variadic"); - $this->assertSame($param->isPassedByReference(), $normalizedEntry['byRef'], "Expected param '{$name}' to " . ($param->isPassedByReference() ? "be" : "not be") . " by reference"); - } catch(\Throwable $t) { + $this->assertSame($param->isOptional(), $normalizedEntry['optional'], "Expected param '{$name}' to " . ($param->isOptional() ? "be" : "not be") . " optional"); + $this->assertSame($param->isVariadic(), $normalizedEntry['variadic'], "Expected param '{$name}' to " . ($param->isVariadic() ? "be" : "not be") . " variadic"); + $this->assertSame($param->isPassedByReference(), $normalizedEntry['byRef'], "Expected param '{$name}' to " . ($param->isPassedByReference() ? "be" : "not be") . " by reference"); + } catch (Throwable $t) { $this->markTestSkipped("Exception: " . $t->getMessage()); } @@ -304,8 +308,6 @@ private function assertParameter(array $normalizedEntry, ReflectionParameter $pa if (isset($expectedType)) { $this->assertTypeValidity($expectedType, $normalizedEntry['type'], "Param '{$name}' has incorrect type"); } - - } /** From df750709ccb800691a418e4df49b658456d536fc Mon Sep 17 00:00:00 2001 From: Sam Mousa Date: Wed, 15 Jun 2022 13:29:22 +0200 Subject: [PATCH 42/88] fix: ignore last remaining failing function --- tests/Internal/Codebase/InternalCallMapHandlerTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Internal/Codebase/InternalCallMapHandlerTest.php b/tests/Internal/Codebase/InternalCallMapHandlerTest.php index c1898799077..66c3720fe92 100644 --- a/tests/Internal/Codebase/InternalCallMapHandlerTest.php +++ b/tests/Internal/Codebase/InternalCallMapHandlerTest.php @@ -73,7 +73,7 @@ class InternalCallMapHandlerTest extends TestCase 'array_key_exists', 'sapi_windows_cp_get', 'stream_select', - + 'hash_hmac_file' ]; From cc58fd9401ae393a9f924e0a8c47092ceea41600 Mon Sep 17 00:00:00 2001 From: Sam Mousa Date: Wed, 15 Jun 2022 14:46:50 +0200 Subject: [PATCH 43/88] fix: add more typehints --- .../Internal/Codebase/InternalCallMapHandlerTest.php | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/tests/Internal/Codebase/InternalCallMapHandlerTest.php b/tests/Internal/Codebase/InternalCallMapHandlerTest.php index 66c3720fe92..1e17c5ad618 100644 --- a/tests/Internal/Codebase/InternalCallMapHandlerTest.php +++ b/tests/Internal/Codebase/InternalCallMapHandlerTest.php @@ -183,8 +183,11 @@ public function testGetcallmapReturnsAValidCallmap(): void */ public function callMapEntryProvider(): iterable { - - $project_analyzer = new ProjectAnalyzer( + /** + * This call is needed since InternalCallMapHandler uses the singleton that is initialized by it. + * @psalm-suppress all + **/ + new ProjectAnalyzer( new TestConfig(), new Providers( new FakeFileProvider(), @@ -226,6 +229,10 @@ public function testCallMapCompliesWithReflection(string $functionName, array $c $this->assertEntryIsCorrect($callMapEntry, $functionName); } + /** + * + * @param array $callMapEntry + */ private function assertEntryIsCorrect(array $callMapEntry, string $functionName): void { $rF = new ReflectionFunction($functionName); From f6be3efa427c65c8cd908a9118446a88df7a8ef1 Mon Sep 17 00:00:00 2001 From: Sam Mousa Date: Wed, 15 Jun 2022 15:05:06 +0200 Subject: [PATCH 44/88] fix: added more type annotations --- .../Codebase/InternalCallMapHandlerTest.php | 28 +++++++++++-------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/tests/Internal/Codebase/InternalCallMapHandlerTest.php b/tests/Internal/Codebase/InternalCallMapHandlerTest.php index 1e17c5ad618..736230ace06 100644 --- a/tests/Internal/Codebase/InternalCallMapHandlerTest.php +++ b/tests/Internal/Codebase/InternalCallMapHandlerTest.php @@ -179,13 +179,12 @@ public function testGetcallmapReturnsAValidCallmap(): void /** * - * @return iterable + * @return iterable}> */ public function callMapEntryProvider(): iterable { /** * This call is needed since InternalCallMapHandler uses the singleton that is initialized by it. - * @psalm-suppress all **/ new ProjectAnalyzer( new TestConfig(), @@ -214,9 +213,11 @@ public function callMapEntryProvider(): iterable * @coversNothing * @depends testGetcallmapReturnsAValidCallmap * @dataProvider callMapEntryProvider + * @param array $callMapEntry */ public function testCallMapCompliesWithReflection(string $functionName, array $callMapEntry): void { + /** @psalm-assert callable-string $functionName */ if (!function_exists($functionName)) { $this->markTestSkipped("Function $functionName does not exist"); } @@ -226,28 +227,33 @@ public function testCallMapCompliesWithReflection(string $functionName, array $c if (preg_match(self::$prefixRegex, $functionName)) { $this->markTestSkipped("Function $functionName has ignored prefix"); } + + unset($callMapEntry[0]); + /** @var array $callMapEntry */ $this->assertEntryIsCorrect($callMapEntry, $functionName); } /** * - * @param array $callMapEntry + * @param array $callMapEntryWithoutReturn + * @psalm-param callable-string $functionName */ - private function assertEntryIsCorrect(array $callMapEntry, string $functionName): void + private function assertEntryIsCorrect(array $callMapEntryWithoutReturn, string $functionName): void { $rF = new ReflectionFunction($functionName); - // For now, ignore return types. - unset($callMapEntry[0]); - /** * Parse the parameter names from the map. - * @var array */ $normalizedEntries = []; - foreach ($callMapEntry as $key => $entry) { + foreach ($callMapEntryWithoutReturn as $key => $entry) { $normalizedKey = $key; + /** + * + * @var array{byRef: bool, refMode: 'rw'|'w', variadic: bool, optional: bool, type: string} $normalizedEntry + */ $normalizedEntry = [ 'variadic' => false, 'byRef' => false, @@ -290,7 +296,7 @@ private function assertEntryIsCorrect(array $callMapEntry, string $functionName) } else { $this->assertArrayHasKey($parameter->getName(), $normalizedEntries, "Callmap is missing entry for param {$parameter->getName()} in $functionName: " . print_r($normalizedEntries, true)); } - $this->assertParameter($normalizedEntries[$parameter->getName()], $parameter, $functionName); + $this->assertParameter($normalizedEntries[$parameter->getName()], $parameter); } } @@ -298,7 +304,7 @@ private function assertEntryIsCorrect(array $callMapEntry, string $functionName) * * @param array{byRef: bool, refMode: 'rw'|'w', variadic: bool, optional: bool, type: string} $normalizedEntry */ - private function assertParameter(array $normalizedEntry, ReflectionParameter $param, string $functionName): void + private function assertParameter(array $normalizedEntry, ReflectionParameter $param): void { $name = $param->getName(); // $identifier = "Param $functionName - $name"; From d0cbf97e23fc500160b4ee5c45cdae458a205a5f Mon Sep 17 00:00:00 2001 From: Benjamin Morel Date: Thu, 16 Jun 2022 01:25:46 +0200 Subject: [PATCH 45/88] Add ReflectionClass::isEnum() --- dictionaries/CallMap.php | 2 ++ dictionaries/CallMap_81_delta.php | 2 ++ 2 files changed, 4 insertions(+) diff --git a/dictionaries/CallMap.php b/dictionaries/CallMap.php index d1c5e3dd9fd..0bdffd1126c 100644 --- a/dictionaries/CallMap.php +++ b/dictionaries/CallMap.php @@ -11354,6 +11354,7 @@ 'ReflectionClass::isAbstract' => ['bool'], 'ReflectionClass::isAnonymous' => ['bool'], 'ReflectionClass::isCloneable' => ['bool'], +'ReflectionClass::isEnum' => ['bool'], 'ReflectionClass::isFinal' => ['bool'], 'ReflectionClass::isInstance' => ['bool', 'object'=>'object'], 'ReflectionClass::isInstantiable' => ['bool'], @@ -11560,6 +11561,7 @@ 'ReflectionObject::isAbstract' => ['bool'], 'ReflectionObject::isAnonymous' => ['bool'], 'ReflectionObject::isCloneable' => ['bool'], +'ReflectionObject::isEnum' => ['bool'], 'ReflectionObject::isFinal' => ['bool'], 'ReflectionObject::isInstance' => ['bool', 'object'=>'object'], 'ReflectionObject::isInstantiable' => ['bool'], diff --git a/dictionaries/CallMap_81_delta.php b/dictionaries/CallMap_81_delta.php index 4f6092ad6f7..02ce93bc2b3 100644 --- a/dictionaries/CallMap_81_delta.php +++ b/dictionaries/CallMap_81_delta.php @@ -37,6 +37,7 @@ 'Fiber::getCurrent' => ['?self'], 'Fiber::suspend' => ['mixed', 'value='=>'null|mixed'], 'FiberError::__construct' => ['void'], + 'ReflectionClass::isEnum' => ['bool'], 'ReflectionEnum::getBackingType' => ['?ReflectionType'], 'ReflectionEnum::getCase' => ['ReflectionEnumUnitCase', 'name' => 'string'], 'ReflectionEnum::getCases' => ['list'], @@ -46,6 +47,7 @@ 'ReflectionEnumUnitCase::getValue' => ['UnitEnum'], 'ReflectionEnumBackedCase::getBackingValue' => ['string|int'], 'ReflectionFunctionAbstract::isStatic' => ['bool'], + 'ReflectionObject::isEnum' => ['bool'], ], 'changed' => [ From 9ce57e6c10cc5f39d83d447499b82cd93f2bd129 Mon Sep 17 00:00:00 2001 From: othercorey Date: Sun, 19 Jun 2022 07:44:53 -0500 Subject: [PATCH 46/88] Add missing ldap_unbind mapping for php 8.1 --- dictionaries/CallMap_81_delta.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/dictionaries/CallMap_81_delta.php b/dictionaries/CallMap_81_delta.php index 02ce93bc2b3..6c850bc0af0 100644 --- a/dictionaries/CallMap_81_delta.php +++ b/dictionaries/CallMap_81_delta.php @@ -639,6 +639,10 @@ 'old' => ['bool', 'ldap'=>'resource', 'callback'=>'?callable'], 'new' => ['bool', 'ldap'=>'LDAP\Connection', 'callback'=>'?callable'], ], + 'ldap_unbind' => [ + 'old' => ['bool', 'ldap'=>'resource'], + 'new' => ['bool', 'ldap'=>'LDAP\Connection'], + ], 'mysqli::connect' => [ 'old' => ['null|false', 'hostname='=>'string|null', 'username='=>'string|null', 'password='=>'string|null', 'database='=>'string|null', 'port='=>'int|null', 'socket='=>'string|null'], 'new' => ['bool', 'hostname='=>'string|null', 'username='=>'string|null', 'password='=>'string|null', 'database='=>'string|null', 'port='=>'int|null', 'socket='=>'string|null'], From 07b335d068193fa7c0175a245d883713971ee119 Mon Sep 17 00:00:00 2001 From: Corey Taylor Date: Mon, 20 Jun 2022 06:59:36 -0500 Subject: [PATCH 47/88] Update ldap parameter to ldap_unbind in callmap --- dictionaries/CallMap.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dictionaries/CallMap.php b/dictionaries/CallMap.php index 0bdffd1126c..1d5434f5ce5 100644 --- a/dictionaries/CallMap.php +++ b/dictionaries/CallMap.php @@ -6829,7 +6829,7 @@ 'ldap_set_rebind_proc' => ['bool', 'ldap'=>'LDAP\Connection', 'callback'=>'?callable'], 'ldap_start_tls' => ['bool', 'ldap'=>'resource'], 'ldap_t61_to_8859' => ['string', 'value'=>'string'], -'ldap_unbind' => ['bool', 'ldap'=>'resource'], +'ldap_unbind' => ['bool', 'ldap'=>'LDAP\Connection'], 'leak' => ['', 'num_bytes'=>'int'], 'leak_variable' => ['', 'variable'=>'', 'leak_data'=>'bool'], 'legendObj::convertToString' => ['string'], From 37549ee03c84d9f4da81df000e86941749011876 Mon Sep 17 00:00:00 2001 From: Sam Mousa Date: Wed, 22 Jun 2022 12:59:47 +0200 Subject: [PATCH 48/88] add test for ignore list cleanup --- composer.json | 1 + .../Codebase/InternalCallMapHandlerTest.php | 206 ++++++++++++++---- 2 files changed, 166 insertions(+), 41 deletions(-) diff --git a/composer.json b/composer.json index b0382b80b65..ddbe93f4c60 100644 --- a/composer.json +++ b/composer.json @@ -113,6 +113,7 @@ "lint": "parallel-lint ./src ./tests", "phpunit": "paratest --runner=WrapperRunner", "phpunit-std": "phpunit", + "verify-callmap": "phpunit tests/Internal/Codebase/InternalCallMapHandlerTest.php", "psalm": "@php ./psalm --find-dead-code", "tests": [ "@lint", diff --git a/tests/Internal/Codebase/InternalCallMapHandlerTest.php b/tests/Internal/Codebase/InternalCallMapHandlerTest.php index 736230ace06..0cfa58a7128 100644 --- a/tests/Internal/Codebase/InternalCallMapHandlerTest.php +++ b/tests/Internal/Codebase/InternalCallMapHandlerTest.php @@ -2,6 +2,10 @@ namespace Psalm\Tests\Internal\Codebase; +use InvalidArgumentException; +use phpDocumentor\Reflection\DocBlock\Tags\Var_; +use PHPUnit\Framework\Error\Warning; +use PHPUnit\Framework\ExpectationFailedException; use Psalm\Codebase; use Psalm\Internal\Analyzer\ProjectAnalyzer; use Psalm\Internal\Codebase\InternalCallMapHandler; @@ -18,6 +22,7 @@ use ReflectionType; use Throwable; +use function Amp\call; use function count; use function explode; use function function_exists; @@ -36,44 +41,97 @@ class InternalCallMapHandlerTest extends TestCase * @var string[] */ private static $ignoredFunctions = [ - 'sprintf', 'printf', 'ctype_print', 'date_sunrise' /** deprecated in 8.1 */, - 'file_put_contents', - 'dom_import_simplexml', 'imagegd', 'imagegd2', 'mysqli_execute', 'array_multisort', - 'intlcal_from_date_time', 'simplexml_import_dom', 'imagefilledpolygon', + 'array_multisort', + 'intlcal_from_date_time', 'imagefilledpolygon', /** deprecated in 8.0 */ 'zip_entry_close', - 'date_time_set', - 'curl_unescape', - 'extract', 'enum_exists', 'igbinary_unserialize', - 'count', 'lzf_compress', - 'long2ip', - 'array_column', 'preg_replace_callback_array', - 'preg_filter', - 'zlib_encode', 'inotify_rm_watch', - 'mail', 'easter_date', - 'date_isodate_set', 'snmpset', - 'get_class_methods', - 'filter_var_array', - 'deflate_add', 'bzdecompress', - 'substr_replace', 'lzf_decompress', 'mongodb\bson\tophp', 'fputcsv', - 'get_headers', - 'get_parent_class', - 'filter_var', - 'array_key_exists', 'sapi_windows_cp_get', - 'stream_select', - 'hash_hmac_file' + 'hash_hmac_file', + 'array_unshift', + 'array_diff', + 'array_replace_recursive', + 'array_intersect_assoc', + 'array_replace', + 'dns_get_mx', + 'array_intersect', + 'array_intersect_key', + 'array_merge', + 'array_diff_key', + 'crypt', + 'debug_zval_dump', + 'array_merge_recursive', + 'array_diff_assoc', + 'array_push', + 'pg_fetch_all', + 'pg_exec', + 'pg_send_execute', + 'datefmt_set_timezone', + 'socket_strerror', + 'sodium_crypto_kx_client_session_keys', + 'mysqli_stmt_bind_param', + 'socket_getsockname', + 'bcmod', + 'socket_getopt', + 'pg_exec', + 'socket_set_option', + 'socket_sendto', + 'socket_connect', + 'socket_select', + 'datefmt_create', + 'imagettftext', + 'mysqli_poll', + 'socket_recvfrom', + 'pg_select', + 'intltz_get_canonical_id', + 'imagexbm', + 'bcdiv', + 'pg_get_notify', + 'socket_get_option', + 'socket_create_pair', + 'socket_sendmsg', + 'normalizer_get_raw_decomposition', + 'socket_cmsg_space', + 'imagettfbbox', + 'pg_get_result', + 'imagepolygon', + 'imageopenpolygon', + 'socket_getpeername', + 'datefmt_get_timezone', + 'sodium_crypto_generichash_update', + 'socket_read', + 'imap_undelete', + 'socket_bind', + 'intlgregcal_create_instance', + 'imagesetinterpolation', + 'openssl_pkcs7_read', + 'mysqli_real_connect', + 'datefmt_parse', + 'socket_shutdown', + 'datefmt_localtime', + 'intltz_get_display_name', + 'imap_delete', + 'socket_setopt', + 'intlgregcal_is_leap_year', + 'intltz_create_enumeration', + 'imagefilter', + 'socket_addrinfo_lookup', + 'socket_recv', + 'sodium_crypto_secretstream_xchacha20poly1305_rekey', + 'bcpowmod', + 'socket_send', + 'socket_set_blocking' + ]; @@ -208,6 +266,71 @@ public function callMapEntryProvider(): iterable } } + /** + * + * @param string $functionName + * @return bool + */ + private function isIgnored(string $functionName) + { + /** @psalm-assert callable-string $functionName */ + if (in_array($functionName, self::$ignoredFunctions)) { + return true; + + } + // if (preg_match(self::$prefixRegex, $functionName)) { + // return true; + // $this->markTestSkipped("Function $functionName has ignored prefix"); + // } + return false; + } + + /** + * @dataProvider callMapEntryProvider + */ + public function testIgnoredFunctionsStillFail(string $functionName, array $callMapEntry): void + { + if (!$this->isIgnored($functionName)) { + // Dummy assertion to mark it as passed + $this->assertTrue(true); + return; + } + + $this->expectException(ExpectationFailedException::class); + + try { + unset($callMapEntry[0]); + /** @var array $callMapEntry */ + $this->assertEntryIsCorrect($callMapEntry, $functionName); + + + } catch(\InvalidArgumentException $t) { + // Silence this one for now. + $this->markTestSkipped('IA'); + } catch(\PHPUnit\Framework\SkippedTestError $t) { + die('this should not happen'); + } catch(ExpectationFailedException $e) { + // This is good! + throw $e; + } catch(InvalidArgumentException $e) { + // This can happen if a class does not exist, we handle the message to check for this case. + if (preg_match('/^Could not get class storage for (.*)$/', $e->getMessage(), $matches) + && !class_exists($matches[1]) + ) { + die("Class mentioned in callmap does not exist: " . $matches[1]); + } + + } catch(Warning $w) { + throw $w; + } catch(\Throwable $t) { + var_dump($t->getMessage()); + die(get_class($t)); + } + + $this->markTestIncomplete("Remove function '{$functionName}' from your ignores"); + // die("Function $functionName did not show error incallmap") ; + } + /** * This function will test functions that are in the callmap AND currently defined * @coversNothing @@ -217,16 +340,9 @@ public function callMapEntryProvider(): iterable */ public function testCallMapCompliesWithReflection(string $functionName, array $callMapEntry): void { - /** @psalm-assert callable-string $functionName */ - if (!function_exists($functionName)) { - $this->markTestSkipped("Function $functionName does not exist"); - } - if (in_array($functionName, self::$ignoredFunctions)) { + if ($this->isIgnored($functionName)) { $this->markTestSkipped("Function $functionName is ignored in config"); } - if (preg_match(self::$prefixRegex, $functionName)) { - $this->markTestSkipped("Function $functionName has ignored prefix"); - } unset($callMapEntry[0]); /** @var array $callMapEntry */ @@ -307,18 +423,13 @@ private function assertEntryIsCorrect(array $callMapEntryWithoutReturn, string $ private function assertParameter(array $normalizedEntry, ReflectionParameter $param): void { $name = $param->getName(); - // $identifier = "Param $functionName - $name"; - try { - $this->assertSame($param->isOptional(), $normalizedEntry['optional'], "Expected param '{$name}' to " . ($param->isOptional() ? "be" : "not be") . " optional"); - $this->assertSame($param->isVariadic(), $normalizedEntry['variadic'], "Expected param '{$name}' to " . ($param->isVariadic() ? "be" : "not be") . " variadic"); - $this->assertSame($param->isPassedByReference(), $normalizedEntry['byRef'], "Expected param '{$name}' to " . ($param->isPassedByReference() ? "be" : "not be") . " by reference"); - } catch (Throwable $t) { - $this->markTestSkipped("Exception: " . $t->getMessage()); - } + $this->assertSame($param->isOptional(), $normalizedEntry['optional'], "Expected param '{$name}' to " . ($param->isOptional() ? "be" : "not be") . " optional"); + $this->assertSame($param->isVariadic(), $normalizedEntry['variadic'], "Expected param '{$name}' to " . ($param->isVariadic() ? "be" : "not be") . " variadic"); + $this->assertSame($param->isPassedByReference(), $normalizedEntry['byRef'], "Expected param '{$name}' to " . ($param->isPassedByReference() ? "be" : "not be") . " by reference"); $expectedType = $param->getType(); - if (isset($expectedType)) { + if (isset($expectedType) && !empty($specified)) { $this->assertTypeValidity($expectedType, $normalizedEntry['type'], "Param '{$name}' has incorrect type"); } } @@ -329,8 +440,21 @@ private function assertParameter(array $normalizedEntry, ReflectionParameter $pa private function assertTypeValidity(ReflectionType $reflected, string $specified, string $message): void { $expectedType = Reflection::getPsalmTypeFromReflectionType($reflected); + + try { $parsedType = Type::parseString($specified); + } catch(\Throwable $t) { + die("Failed to parse type: $specified -- $message"); + } + try { $this->assertTrue(UnionTypeComparator::isContainedBy(self::$codebase, $parsedType, $expectedType), $message); + } catch(InvalidArgumentException $e) { + if (preg_match('/^Could not get class storage for (.*)$/', $e->getMessage(), $matches) + && !class_exists($matches[1]) + ) { + die("Class mentioned in callmap does not exist: " . $matches[1]); + } + } } } From d2467cfb7eccd88bbeb643dddb77672569cd8d4b Mon Sep 17 00:00:00 2001 From: Sam Mousa Date: Wed, 22 Jun 2022 13:28:17 +0200 Subject: [PATCH 49/88] sort ignore list add more functions to it --- .../Codebase/InternalCallMapHandlerTest.php | 214 +++++++++++------- 1 file changed, 132 insertions(+), 82 deletions(-) diff --git a/tests/Internal/Codebase/InternalCallMapHandlerTest.php b/tests/Internal/Codebase/InternalCallMapHandlerTest.php index 0cfa58a7128..559fcfb8c38 100644 --- a/tests/Internal/Codebase/InternalCallMapHandlerTest.php +++ b/tests/Internal/Codebase/InternalCallMapHandlerTest.php @@ -41,96 +41,149 @@ class InternalCallMapHandlerTest extends TestCase * @var string[] */ private static $ignoredFunctions = [ - 'array_multisort', - 'intlcal_from_date_time', 'imagefilledpolygon', - /** deprecated in 8.0 */ - 'zip_entry_close', - 'enum_exists', - 'igbinary_unserialize', - 'lzf_compress', - 'preg_replace_callback_array', - 'inotify_rm_watch', - 'easter_date', - 'snmpset', - 'bzdecompress', - 'lzf_decompress', - 'mongodb\bson\tophp', - 'fputcsv', - 'sapi_windows_cp_get', - 'hash_hmac_file', - 'array_unshift', + 'array_column', 'array_diff', - 'array_replace_recursive', - 'array_intersect_assoc', - 'array_replace', - 'dns_get_mx', + 'array_diff_assoc', + 'array_diff_key', 'array_intersect', + 'array_intersect_assoc', 'array_intersect_key', + 'array_key_exists', 'array_merge', - 'array_diff_key', - 'crypt', - 'debug_zval_dump', 'array_merge_recursive', - 'array_diff_assoc', + 'array_multisort', 'array_push', - 'pg_fetch_all', - 'pg_exec', - 'pg_send_execute', - 'datefmt_set_timezone', - 'socket_strerror', - 'sodium_crypto_kx_client_session_keys', - 'mysqli_stmt_bind_param', - 'socket_getsockname', + 'array_replace', + 'array_replace_recursive', + 'array_unshift', + 'bcdiv', 'bcmod', - 'socket_getopt', - 'pg_exec', - 'socket_set_option', - 'socket_sendto', - 'socket_connect', - 'socket_select', + 'bcpowmod', + 'bzdecompress', + 'count', + 'crypt', + 'date_isodate_set', 'datefmt_create', + 'datefmt_get_timezone', + 'datefmt_localtime', + 'datefmt_parse', + 'datefmt_set_timezone', + 'debug_zval_dump', + 'deflate_add', + 'dns_get_mx', + 'easter_date', + 'enum_exists', + 'extract', + 'filter_var', + 'filter_var_array', + 'fputcsv', + 'get_class_methods', + 'get_headers', + 'get_parent_class', + 'hash_hmac_file', + 'igbinary_unserialize', + 'imagefilter', + 'imagegd', + 'imagegd2', + 'imageinterlace', + 'imageopenpolygon', + 'imagepolygon', + 'imagerotate', + 'imagesetinterpolation', + 'imagettfbbox', 'imagettftext', - 'mysqli_poll', - 'socket_recvfrom', - 'pg_select', - 'intltz_get_canonical_id', 'imagexbm', - 'bcdiv', - 'pg_get_notify', - 'socket_get_option', - 'socket_create_pair', - 'socket_sendmsg', - 'normalizer_get_raw_decomposition', - 'socket_cmsg_space', - 'imagettfbbox', - 'pg_get_result', - 'imagepolygon', - 'imageopenpolygon', - 'socket_getpeername', - 'datefmt_get_timezone', - 'sodium_crypto_generichash_update', - 'socket_read', + 'imap_delete', + 'imap_open', + 'imap_rfc822_write_address', + 'imap_sort', 'imap_undelete', - 'socket_bind', + 'inflate_add', + 'inflate_get_read_len', + 'inflate_get_status', + 'inotify_rm_watch', + 'intlcal_from_date_time', 'imagefilledpolygon', + 'intlcal_get_weekend_transition', 'intlgregcal_create_instance', - 'imagesetinterpolation', - 'openssl_pkcs7_read', - 'mysqli_real_connect', - 'datefmt_parse', - 'socket_shutdown', - 'datefmt_localtime', - 'intltz_get_display_name', - 'imap_delete', - 'socket_setopt', 'intlgregcal_is_leap_year', 'intltz_create_enumeration', - 'imagefilter', + 'intltz_get_canonical_id', + 'intltz_get_display_name', + 'long2ip', + 'lzf_compress', + 'lzf_decompress', + 'mail', + 'mongodb\bson\tophp', + 'msg_receive', + 'msg_remove_queue', + 'msg_send', + 'msg_set_queue', + 'msg_stat_queue', + 'msg_stat_queue', + 'mysqli_poll', + 'mysqli_real_connect', + 'mysqli_stmt_bind_param', + 'normalizer_get_raw_decomposition', + 'openssl_pkcs7_read', + 'pg_exec', + 'pg_exec', + 'pg_fetch_all', + 'pg_get_notify', + 'pg_get_result', + 'pg_pconnect', + 'pg_select', + 'pg_send_execute', + 'preg_filter', + 'preg_replace_callback_array', + 'sapi_windows_cp_get', + 'sem_acquire', + 'sem_get', + 'sem_release', + 'sem_remove', + 'shm_detach', + 'shm_get_var', + 'shm_has_var', + 'shm_put_var', + 'shm_put_var', + 'shm_remove', + 'shm_remove_var', + 'shmop_close', + 'shmop_delete', + 'shmop_read', + 'shmop_size', + 'shmop_write', + 'snmpset', 'socket_addrinfo_lookup', + 'socket_bind', + 'socket_cmsg_space', + 'socket_connect', + 'socket_create_pair', + 'socket_get_option', + 'socket_getopt', + 'socket_getpeername', + 'socket_getsockname', + 'socket_read', 'socket_recv', - 'sodium_crypto_secretstream_xchacha20poly1305_rekey', - 'bcpowmod', + 'socket_recvfrom', + 'socket_recvmsg', + 'socket_select', 'socket_send', - 'socket_set_blocking' + 'socket_sendmsg', + 'socket_sendto', + 'socket_set_blocking', + 'socket_set_option', + 'socket_setopt', + 'socket_shutdown', + 'socket_strerror', + 'sodium_crypto_generichash', + 'sodium_crypto_generichash_final', + 'sodium_crypto_generichash_init', + 'sodium_crypto_generichash_update', + 'sodium_crypto_kx_client_session_keys', + 'sodium_crypto_secretstream_xchacha20poly1305_rekey', + 'substr_replace', + 'zip_entry_close', + 'zlib_encode', @@ -237,7 +290,7 @@ public function testGetcallmapReturnsAValidCallmap(): void /** * - * @return iterable}> + * @return iterable}> */ public function callMapEntryProvider(): iterable { @@ -267,8 +320,6 @@ public function callMapEntryProvider(): iterable } /** - * - * @param string $functionName * @return bool */ private function isIgnored(string $functionName) @@ -286,7 +337,10 @@ private function isIgnored(string $functionName) } /** + * @depends testGetcallmapReturnsAValidCallmap * @dataProvider callMapEntryProvider + * @coversNothing + * @psalm-param callable-string $functionName */ public function testIgnoredFunctionsStillFail(string $functionName, array $callMapEntry): void { @@ -320,11 +374,6 @@ public function testIgnoredFunctionsStillFail(string $functionName, array $callM die("Class mentioned in callmap does not exist: " . $matches[1]); } - } catch(Warning $w) { - throw $w; - } catch(\Throwable $t) { - var_dump($t->getMessage()); - die(get_class($t)); } $this->markTestIncomplete("Remove function '{$functionName}' from your ignores"); @@ -336,6 +385,7 @@ public function testIgnoredFunctionsStillFail(string $functionName, array $callM * @coversNothing * @depends testGetcallmapReturnsAValidCallmap * @dataProvider callMapEntryProvider + * @psalm-param callable-string $functionName * @param array $callMapEntry */ public function testCallMapCompliesWithReflection(string $functionName, array $callMapEntry): void @@ -429,7 +479,7 @@ private function assertParameter(array $normalizedEntry, ReflectionParameter $pa $expectedType = $param->getType(); - if (isset($expectedType) && !empty($specified)) { + if (isset($expectedType) && !empty($normalizedEntry['type'])) { $this->assertTypeValidity($expectedType, $normalizedEntry['type'], "Param '{$name}' has incorrect type"); } } From d13a428cb5ac70d34ad81e4ad704d51dd0f95ca6 Mon Sep 17 00:00:00 2001 From: Sam Mousa Date: Wed, 22 Jun 2022 14:57:40 +0200 Subject: [PATCH 50/88] update ignores, cleanup test file --- .../Codebase/InternalCallMapHandlerTest.php | 272 +++++++++++++----- 1 file changed, 198 insertions(+), 74 deletions(-) diff --git a/tests/Internal/Codebase/InternalCallMapHandlerTest.php b/tests/Internal/Codebase/InternalCallMapHandlerTest.php index 559fcfb8c38..d95f2f35255 100644 --- a/tests/Internal/Codebase/InternalCallMapHandlerTest.php +++ b/tests/Internal/Codebase/InternalCallMapHandlerTest.php @@ -26,7 +26,6 @@ use function count; use function explode; use function function_exists; -use function implode; use function in_array; use function json_encode; use function preg_match; @@ -41,6 +40,7 @@ class InternalCallMapHandlerTest extends TestCase * @var string[] */ private static $ignoredFunctions = [ + 'apcu_entry', 'array_column', 'array_diff', 'array_diff_assoc', @@ -72,6 +72,26 @@ class InternalCallMapHandlerTest extends TestCase 'deflate_add', 'dns_get_mx', 'easter_date', + 'enchant_broker_describe', + 'enchant_broker_dict_exists', + 'enchant_broker_free', + 'enchant_broker_free_dict', + 'enchant_broker_get_dict_path', + 'enchant_broker_get_error', + 'enchant_broker_list_dicts', + 'enchant_broker_request_dict', + 'enchant_broker_request_pwl_dict', + 'enchant_broker_set_dict_path', + 'enchant_broker_set_ordering', + 'enchant_dict_add_to_personal', + 'enchant_dict_add_to_session', + 'enchant_dict_check', + 'enchant_dict_describe', + 'enchant_dict_get_error', + 'enchant_dict_is_in_session', + 'enchant_dict_quick_check', + 'enchant_dict_store_replacement', + 'enchant_dict_suggest', 'enum_exists', 'extract', 'filter_var', @@ -80,8 +100,33 @@ class InternalCallMapHandlerTest extends TestCase 'get_class_methods', 'get_headers', 'get_parent_class', + 'gmp_clrbit', + 'gmp_div', + 'gmp_setbit', + 'gnupg_adddecryptkey', + 'gnupg_addencryptkey', + 'gnupg_addsignkey', + 'gnupg_cleardecryptkeys', + 'gnupg_clearencryptkeys', + 'gnupg_clearsignkeys', + 'gnupg_decrypt', + 'gnupg_decryptverify', + 'gnupg_encrypt', + 'gnupg_encryptsign', + 'gnupg_export', + 'gnupg_geterror', + 'gnupg_getprotocol', + 'gnupg_import', + 'gnupg_init', + 'gnupg_keyinfo', + 'gnupg_setarmor', + 'gnupg_seterrormode', + 'gnupg_setsignmode', + 'gnupg_sign', + 'gnupg_verify', 'hash_hmac_file', 'igbinary_unserialize', + 'imagefilledpolygon', 'imagefilter', 'imagegd', 'imagegd2', @@ -102,17 +147,65 @@ class InternalCallMapHandlerTest extends TestCase 'inflate_get_read_len', 'inflate_get_status', 'inotify_rm_watch', - 'intlcal_from_date_time', 'imagefilledpolygon', + 'intlcal_from_date_time', 'intlcal_get_weekend_transition', 'intlgregcal_create_instance', 'intlgregcal_is_leap_year', 'intltz_create_enumeration', 'intltz_get_canonical_id', 'intltz_get_display_name', + 'ldap_compare', + 'ldap_delete', + 'ldap_exop', + 'ldap_free_result', + 'ldap_get_option', + 'ldap_list', + 'ldap_mod_add', + 'ldap_mod_del', + 'ldap_mod_replace', + 'ldap_modify', + 'ldap_modify_batch', + 'ldap_next_entry', + 'ldap_parse_reference', + 'ldap_read', + 'ldap_rename', + 'ldap_search', + 'ldap_start_tls', 'long2ip', 'lzf_compress', 'lzf_decompress', 'mail', + 'mailparse_msg_extract_part', + 'mailparse_msg_extract_part_file', + 'mailparse_msg_extract_whole_part_file', + 'mailparse_msg_free', + 'mailparse_msg_get_part', + 'mailparse_msg_get_part_data', + 'mailparse_msg_get_structure', + 'mailparse_msg_parse', + 'mailparse_stream_encode', + 'memcache_add', + 'memcache_add_server', + 'memcache_append', + 'memcache_cas', + 'memcache_close', + 'memcache_connect', + 'memcache_decrement', + 'memcache_delete', + 'memcache_flush', + 'memcache_get_extended_stats', + 'memcache_get_server_status', + 'memcache_get_stats', + 'memcache_get_version', + 'memcache_increment', + 'memcache_pconnect', + 'memcache_pconnect', + 'memcache_prepend', + 'memcache_replace', + 'memcache_set', + 'memcache_set_compress_threshold', + 'memcache_set_failure_callback', + 'memcache_set_server_params', 'mongodb\bson\tophp', 'msg_receive', 'msg_remove_queue', @@ -124,6 +217,46 @@ class InternalCallMapHandlerTest extends TestCase 'mysqli_real_connect', 'mysqli_stmt_bind_param', 'normalizer_get_raw_decomposition', + 'oauth_get_sbs', + 'oci_collection_append', + 'oci_collection_assign', + 'oci_collection_element_assign', + 'oci_collection_element_get', + 'oci_collection_max', + 'oci_collection_size', + 'oci_collection_trim', + 'oci_fetch_object', + 'oci_field_is_null', + 'oci_field_name', + 'oci_field_precision', + 'oci_field_scale', + 'oci_field_size', + 'oci_field_type', + 'oci_field_type_raw', + 'oci_free_collection', + 'oci_free_descriptor', + 'oci_lob_append', + 'oci_lob_eof', + 'oci_lob_erase', + 'oci_lob_export', + 'oci_lob_flush', + 'oci_lob_import', + 'oci_lob_load', + 'oci_lob_read', + 'oci_lob_rewind', + 'oci_lob_save', + 'oci_lob_seek', + 'oci_lob_size', + 'oci_lob_tell', + 'oci_lob_truncate', + 'oci_lob_write', + 'oci_register_taf_callback', + 'oci_result', + 'ocigetbufferinglob', + 'ocisetbufferinglob', + 'odbc_procedurecolumns', + 'odbc_procedures', + 'odbc_result', 'openssl_pkcs7_read', 'pg_exec', 'pg_exec', @@ -144,7 +277,6 @@ class InternalCallMapHandlerTest extends TestCase 'shm_get_var', 'shm_has_var', 'shm_put_var', - 'shm_put_var', 'shm_remove', 'shm_remove_var', 'shmop_close', @@ -152,6 +284,8 @@ class InternalCallMapHandlerTest extends TestCase 'shmop_read', 'shmop_size', 'shmop_write', + 'snmp_set_enum_print', + 'snmp_set_valueretrieval', 'snmpset', 'socket_addrinfo_lookup', 'socket_bind', @@ -181,81 +315,69 @@ class InternalCallMapHandlerTest extends TestCase 'sodium_crypto_generichash_update', 'sodium_crypto_kx_client_session_keys', 'sodium_crypto_secretstream_xchacha20poly1305_rekey', + 'sqlsrv_connect', + 'sqlsrv_errors', + 'sqlsrv_fetch_array', + 'sqlsrv_fetch_object', + 'sqlsrv_get_field', + 'sqlsrv_prepare', + 'sqlsrv_query', + 'sqlsrv_server_info', + 'stomp_abort', + 'stomp_ack', + 'stomp_begin', + 'stomp_commit', + 'stomp_read_frame', + 'stomp_send', + 'stomp_set_read_timeout', + 'stomp_subscribe', + 'stomp_unsubscribe', 'substr_replace', + 'tidy_getopt', + 'uopz_allow_exit', + 'uopz_get_mock', + 'uopz_get_property', + 'uopz_get_return', + 'uopz_get_static', + 'uopz_set_mock', + 'uopz_set_property', + 'uopz_set_static', + 'uopz_unset_mock', + 'xdiff_file_bdiff', + 'xdiff_file_bdiff_size', + 'xdiff_file_diff', + 'xdiff_file_diff_binary', + 'xdiff_file_merge3', + 'xdiff_file_rabdiff', + 'xdiff_string_bdiff', + 'xdiff_string_bdiff_size', + 'xdiff_string_bpatch', + 'xdiff_string_diff', + 'xdiff_string_diff_binary', + 'xdiff_string_merge3', + 'xdiff_string_patch', + 'xdiff_string_patch_binary', + 'xdiff_string_rabdiff', + 'xmlrpc_server_add_introspection_data', + 'xmlrpc_server_call_method', + 'xmlrpc_server_destroy', + 'xmlrpc_server_register_introspection_callback', + 'xmlrpc_server_register_method', + 'yaml_emit', + 'yaml_emit_file', 'zip_entry_close', 'zlib_encode', - - ]; - /** - * @var string[] - */ - private static $ignoredPrefixes = [ - 'apcu_', - 'bc', - 'collator_', - 'ctype_', - 'datefmt_', - 'enchant_', - 'gmp_', - 'gnupg_', - 'image', - 'imap_', - 'inflate_', - 'intl', - 'ldap_', - 'mailparse_', - 'memcache_', - 'msg_', - 'mysqli_', - 'normalizer_', - 'oauth_', - 'oci', - 'odbc_', - 'openssl_', - 'pg_', - 'sem_', - 'shm_', - 'shmop_', - 'snmp_', - 'socket_', - 'sodium_', - 'sqlsrv_', - 'tidy_', - 'transliterator_', - 'uopz_', - 'xdiff_', - 'xmlrpc_server', - 'yaml_', - ]; - - /** - * Initialized in setup - * @var string Regex - */ - private static $prefixRegex = '//'; - - /** - * - * @var bool whether to skip params for which no definition can be found in the callMap - */ - private $skipUndefinedParams = false; - /** * * @var Codebase */ private static $codebase; - - - - public static function setUpBeforeClass(): void { - self::$prefixRegex = '/^(' . implode('|', self::$ignoredPrefixes) . ')/'; $project_analyzer = new ProjectAnalyzer( new TestConfig(), new Providers( @@ -266,6 +388,14 @@ public static function setUpBeforeClass(): void self::$codebase = $project_analyzer->getCodebase(); } + + public function testIgnoresAreSorted(): void + { + $ignoredFunctions = self::$ignoredFunctions; + sort($ignoredFunctions); + $this->assertSame($ignoredFunctions, self::$ignoredFunctions); + } + public static function tearDownAfterClass(): void { self::$codebase = null; @@ -306,6 +436,7 @@ public function callMapEntryProvider(): iterable ); $callMap = InternalCallMapHandler::getCallMap(); foreach ($callMap as $function => $entry) { + if ($function !== 'hash_hmac_file') continue; // Skip class methods if (strpos($function, '::') !== false || !function_exists($function)) { continue; @@ -329,14 +460,11 @@ private function isIgnored(string $functionName) return true; } - // if (preg_match(self::$prefixRegex, $functionName)) { - // return true; - // $this->markTestSkipped("Function $functionName has ignored prefix"); - // } return false; } /** + * @depends testIgnoresAreSorted * @depends testGetcallmapReturnsAValidCallmap * @dataProvider callMapEntryProvider * @coversNothing @@ -377,13 +505,13 @@ public function testIgnoredFunctionsStillFail(string $functionName, array $callM } $this->markTestIncomplete("Remove function '{$functionName}' from your ignores"); - // die("Function $functionName did not show error incallmap") ; } /** * This function will test functions that are in the callmap AND currently defined * @coversNothing * @depends testGetcallmapReturnsAValidCallmap + * @depends testIgnoresAreSorted * @dataProvider callMapEntryProvider * @psalm-param callable-string $functionName * @param array $callMapEntry @@ -457,11 +585,7 @@ private function assertEntryIsCorrect(array $callMapEntryWithoutReturn, string $ $normalizedEntries[$normalizedKey] = $normalizedEntry; } foreach ($rF->getParameters() as $parameter) { - if ($this->skipUndefinedParams && !isset($normalizedEntries[$parameter->getName()])) { - continue; - } else { - $this->assertArrayHasKey($parameter->getName(), $normalizedEntries, "Callmap is missing entry for param {$parameter->getName()} in $functionName: " . print_r($normalizedEntries, true)); - } + $this->assertArrayHasKey($parameter->getName(), $normalizedEntries, "Callmap is missing entry for param {$parameter->getName()} in $functionName: " . print_r($normalizedEntries, true)); $this->assertParameter($normalizedEntries[$parameter->getName()], $parameter); } } From f8e548e8110bdcb4280618e1eb9aae29ad6bd285 Mon Sep 17 00:00:00 2001 From: Sam Mousa Date: Wed, 22 Jun 2022 15:05:24 +0200 Subject: [PATCH 51/88] fix code style remove debug code --- .../Codebase/InternalCallMapHandlerTest.php | 31 +++++++------------ 1 file changed, 12 insertions(+), 19 deletions(-) diff --git a/tests/Internal/Codebase/InternalCallMapHandlerTest.php b/tests/Internal/Codebase/InternalCallMapHandlerTest.php index d95f2f35255..a9b979e1a92 100644 --- a/tests/Internal/Codebase/InternalCallMapHandlerTest.php +++ b/tests/Internal/Codebase/InternalCallMapHandlerTest.php @@ -3,9 +3,8 @@ namespace Psalm\Tests\Internal\Codebase; use InvalidArgumentException; -use phpDocumentor\Reflection\DocBlock\Tags\Var_; -use PHPUnit\Framework\Error\Warning; use PHPUnit\Framework\ExpectationFailedException; +use PHPUnit\Framework\SkippedTestError; use Psalm\Codebase; use Psalm\Internal\Analyzer\ProjectAnalyzer; use Psalm\Internal\Codebase\InternalCallMapHandler; @@ -20,9 +19,8 @@ use ReflectionFunction; use ReflectionParameter; use ReflectionType; -use Throwable; -use function Amp\call; +use function class_exists; use function count; use function explode; use function function_exists; @@ -30,6 +28,7 @@ use function json_encode; use function preg_match; use function print_r; +use function sort; use function strncmp; use function strpos; use function substr; @@ -436,7 +435,6 @@ public function callMapEntryProvider(): iterable ); $callMap = InternalCallMapHandler::getCallMap(); foreach ($callMap as $function => $entry) { - if ($function !== 'hash_hmac_file') continue; // Skip class methods if (strpos($function, '::') !== false || !function_exists($function)) { continue; @@ -451,14 +449,12 @@ public function callMapEntryProvider(): iterable } /** - * @return bool */ - private function isIgnored(string $functionName) + private function isIgnored(string $functionName): bool { /** @psalm-assert callable-string $functionName */ if (in_array($functionName, self::$ignoredFunctions)) { return true; - } return false; } @@ -484,24 +480,21 @@ public function testIgnoredFunctionsStillFail(string $functionName, array $callM unset($callMapEntry[0]); /** @var array $callMapEntry */ $this->assertEntryIsCorrect($callMapEntry, $functionName); - - - } catch(\InvalidArgumentException $t) { + } catch (InvalidArgumentException $t) { // Silence this one for now. $this->markTestSkipped('IA'); - } catch(\PHPUnit\Framework\SkippedTestError $t) { + } catch (SkippedTestError $t) { die('this should not happen'); - } catch(ExpectationFailedException $e) { + } catch (ExpectationFailedException $e) { // This is good! throw $e; - } catch(InvalidArgumentException $e) { + } catch (InvalidArgumentException $e) { // This can happen if a class does not exist, we handle the message to check for this case. if (preg_match('/^Could not get class storage for (.*)$/', $e->getMessage(), $matches) && !class_exists($matches[1]) ) { die("Class mentioned in callmap does not exist: " . $matches[1]); } - } $this->markTestIncomplete("Remove function '{$functionName}' from your ignores"); @@ -616,14 +609,14 @@ private function assertTypeValidity(ReflectionType $reflected, string $specified $expectedType = Reflection::getPsalmTypeFromReflectionType($reflected); try { - $parsedType = Type::parseString($specified); - } catch(\Throwable $t) { + $parsedType = Type::parseString($specified); + } catch (Throwable $t) { die("Failed to parse type: $specified -- $message"); } try { - $this->assertTrue(UnionTypeComparator::isContainedBy(self::$codebase, $parsedType, $expectedType), $message); - } catch(InvalidArgumentException $e) { + $this->assertTrue(UnionTypeComparator::isContainedBy(self::$codebase, $parsedType, $expectedType), $message); + } catch (InvalidArgumentException $e) { if (preg_match('/^Could not get class storage for (.*)$/', $e->getMessage(), $matches) && !class_exists($matches[1]) ) { From 08aea9c8ca15d06b8c26d882b876a129358376cc Mon Sep 17 00:00:00 2001 From: Sam Mousa Date: Wed, 22 Jun 2022 15:08:50 +0200 Subject: [PATCH 52/88] remove debug code --- tests/Internal/Codebase/InternalCallMapHandlerTest.php | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/tests/Internal/Codebase/InternalCallMapHandlerTest.php b/tests/Internal/Codebase/InternalCallMapHandlerTest.php index a9b979e1a92..2a4b9558774 100644 --- a/tests/Internal/Codebase/InternalCallMapHandlerTest.php +++ b/tests/Internal/Codebase/InternalCallMapHandlerTest.php @@ -608,11 +608,7 @@ private function assertTypeValidity(ReflectionType $reflected, string $specified { $expectedType = Reflection::getPsalmTypeFromReflectionType($reflected); - try { - $parsedType = Type::parseString($specified); - } catch (Throwable $t) { - die("Failed to parse type: $specified -- $message"); - } + $parsedType = Type::parseString($specified); try { $this->assertTrue(UnionTypeComparator::isContainedBy(self::$codebase, $parsedType, $expectedType), $message); From ac091902c19ba08aa643b7f87dbb9067a01c73cd Mon Sep 17 00:00:00 2001 From: Sam Mousa Date: Wed, 22 Jun 2022 15:32:35 +0200 Subject: [PATCH 53/88] php version specific ignores --- .../Codebase/InternalCallMapHandlerTest.php | 38 +++++++++++++------ 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/tests/Internal/Codebase/InternalCallMapHandlerTest.php b/tests/Internal/Codebase/InternalCallMapHandlerTest.php index 2a4b9558774..faa3b62282c 100644 --- a/tests/Internal/Codebase/InternalCallMapHandlerTest.php +++ b/tests/Internal/Codebase/InternalCallMapHandlerTest.php @@ -25,18 +25,24 @@ use function explode; use function function_exists; use function in_array; +use function is_int; use function json_encode; use function preg_match; use function print_r; -use function sort; +use function strcmp; use function strncmp; use function strpos; use function substr; +use const PHP_MAJOR_VERSION; +use const PHP_MINOR_VERSION; + class InternalCallMapHandlerTest extends TestCase { /** - * @var string[] + * Specify a function name as value, or a function name as key and + * an array containing the PHP versions in which to ignore this function as values. + * @var array> */ private static $ignoredFunctions = [ 'apcu_entry', @@ -95,7 +101,8 @@ class InternalCallMapHandlerTest extends TestCase 'extract', 'filter_var', 'filter_var_array', - 'fputcsv', + // https://www.php.net/manual/en/function.fputcsv.php + 'fputcsv' => ['8.1'], 'get_class_methods', 'get_headers', 'get_parent_class', @@ -198,7 +205,6 @@ class InternalCallMapHandlerTest extends TestCase 'memcache_get_version', 'memcache_increment', 'memcache_pconnect', - 'memcache_pconnect', 'memcache_prepend', 'memcache_replace', 'memcache_set', @@ -211,7 +217,6 @@ class InternalCallMapHandlerTest extends TestCase 'msg_send', 'msg_set_queue', 'msg_stat_queue', - 'msg_stat_queue', 'mysqli_poll', 'mysqli_real_connect', 'mysqli_stmt_bind_param', @@ -258,7 +263,6 @@ class InternalCallMapHandlerTest extends TestCase 'odbc_result', 'openssl_pkcs7_read', 'pg_exec', - 'pg_exec', 'pg_fetch_all', 'pg_get_notify', 'pg_get_result', @@ -331,6 +335,7 @@ class InternalCallMapHandlerTest extends TestCase 'stomp_set_read_timeout', 'stomp_subscribe', 'stomp_unsubscribe', + 'stream_select' => ['8.0'], 'substr_replace', 'tidy_getopt', 'uopz_allow_exit', @@ -388,11 +393,15 @@ public static function setUpBeforeClass(): void } - public function testIgnoresAreSorted(): void + public function testIgnoresAreSortedAndUnique(): void { - $ignoredFunctions = self::$ignoredFunctions; - sort($ignoredFunctions); - $this->assertSame($ignoredFunctions, self::$ignoredFunctions); + $previousFunction = ""; + foreach (self::$ignoredFunctions as $key => $value) { + $function = is_int($key) ? $value : $key; + + $this->assertGreaterThan(0, strcmp($function, $previousFunction)); + $previousFunction = $function; + } } public static function tearDownAfterClass(): void @@ -456,11 +465,16 @@ private function isIgnored(string $functionName): bool if (in_array($functionName, self::$ignoredFunctions)) { return true; } + + if (isset(self::$ignoredFunctions[$functionName]) && in_array(PHP_MAJOR_VERSION . '.' . PHP_MINOR_VERSION, self::$ignoredFunctions[$functionName])) { + return true; + } + return false; } /** - * @depends testIgnoresAreSorted + * @depends testIgnoresAreSortedAndUnique * @depends testGetcallmapReturnsAValidCallmap * @dataProvider callMapEntryProvider * @coversNothing @@ -504,7 +518,7 @@ public function testIgnoredFunctionsStillFail(string $functionName, array $callM * This function will test functions that are in the callmap AND currently defined * @coversNothing * @depends testGetcallmapReturnsAValidCallmap - * @depends testIgnoresAreSorted + * @depends testIgnoresAreSortedAndUnique * @dataProvider callMapEntryProvider * @psalm-param callable-string $functionName * @param array $callMapEntry From c393a20471815978f8def98b47d5d2df08d6b94e Mon Sep 17 00:00:00 2001 From: Sam Mousa Date: Wed, 22 Jun 2022 15:37:51 +0200 Subject: [PATCH 54/88] fix static analysis comments --- tests/Internal/Codebase/InternalCallMapHandlerTest.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/Internal/Codebase/InternalCallMapHandlerTest.php b/tests/Internal/Codebase/InternalCallMapHandlerTest.php index faa3b62282c..e5662751363 100644 --- a/tests/Internal/Codebase/InternalCallMapHandlerTest.php +++ b/tests/Internal/Codebase/InternalCallMapHandlerTest.php @@ -25,6 +25,7 @@ use function explode; use function function_exists; use function in_array; +use function is_array; use function is_int; use function json_encode; use function preg_match; @@ -397,6 +398,7 @@ public function testIgnoresAreSortedAndUnique(): void { $previousFunction = ""; foreach (self::$ignoredFunctions as $key => $value) { + /** @var string */ $function = is_int($key) ? $value : $key; $this->assertGreaterThan(0, strcmp($function, $previousFunction)); @@ -466,7 +468,9 @@ private function isIgnored(string $functionName): bool return true; } - if (isset(self::$ignoredFunctions[$functionName]) && in_array(PHP_MAJOR_VERSION . '.' . PHP_MINOR_VERSION, self::$ignoredFunctions[$functionName])) { + if (isset(self::$ignoredFunctions[$functionName]) + && is_array(self::$ignoredFunctions[$functionName]) + && in_array(PHP_MAJOR_VERSION . '.' . PHP_MINOR_VERSION, self::$ignoredFunctions[$functionName])) { return true; } From de8187d1384d91ac0de389e90c0a1c636951d31f Mon Sep 17 00:00:00 2001 From: Sam Date: Wed, 22 Jun 2022 19:04:28 +0200 Subject: [PATCH 55/88] Update tests/Internal/Codebase/InternalCallMapHandlerTest.php --- tests/Internal/Codebase/InternalCallMapHandlerTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/Internal/Codebase/InternalCallMapHandlerTest.php b/tests/Internal/Codebase/InternalCallMapHandlerTest.php index e5662751363..44f2f8df29c 100644 --- a/tests/Internal/Codebase/InternalCallMapHandlerTest.php +++ b/tests/Internal/Codebase/InternalCallMapHandlerTest.php @@ -463,7 +463,6 @@ public function callMapEntryProvider(): iterable */ private function isIgnored(string $functionName): bool { - /** @psalm-assert callable-string $functionName */ if (in_array($functionName, self::$ignoredFunctions)) { return true; } From b0532535131d331c567c7f492109d3ccc7b66965 Mon Sep 17 00:00:00 2001 From: Corey Taylor Date: Thu, 23 Jun 2022 00:29:11 -0500 Subject: [PATCH 56/88] Fix signatures of ldap_* functions --- dictionaries/CallMap.php | 56 +++++++++--------- dictionaries/CallMap_81_delta.php | 58 ++++++++++--------- .../Codebase/InternalCallMapHandlerTest.php | 17 ------ 3 files changed, 59 insertions(+), 72 deletions(-) diff --git a/dictionaries/CallMap.php b/dictionaries/CallMap.php index 1d5434f5ce5..15d8414ac88 100644 --- a/dictionaries/CallMap.php +++ b/dictionaries/CallMap.php @@ -6773,61 +6773,61 @@ 'lchgrp' => ['bool', 'filename'=>'string', 'group'=>'string|int'], 'lchown' => ['bool', 'filename'=>'string', 'user'=>'string|int'], 'ldap_8859_to_t61' => ['string', 'value'=>'string'], -'ldap_add' => ['bool', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'entry'=>'array', 'controls='=>'array'], -'ldap_add_ext' => ['LDAP\Connection|false', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'entry'=>'array', 'controls='=>'array'], +'ldap_add' => ['bool', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'entry'=>'array', 'controls='=>'?array'], +'ldap_add_ext' => ['LDAP\Result|false', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'entry'=>'array', 'controls='=>'?array'], 'ldap_bind' => ['bool', 'ldap'=>'LDAP\Connection', 'dn='=>'string|null', 'password='=>'string|null'], -'ldap_bind_ext' => ['LDAP\Connection|false', 'ldap'=>'LDAP\Connection', 'dn='=>'string|null', 'password='=>'string|null', 'controls='=>'array'], +'ldap_bind_ext' => ['LDAP\Result|false', 'ldap'=>'LDAP\Connection', 'dn='=>'string|null', 'password='=>'string|null', 'controls='=>'?array'], 'ldap_close' => ['bool', 'ldap'=>'LDAP\Connection'], -'ldap_compare' => ['bool|int', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'attribute'=>'string', 'value'=>'string'], +'ldap_compare' => ['bool|int', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'attribute'=>'string', 'value'=>'string', 'controls='=>'?array'], 'ldap_connect' => ['LDAP\Connection|false', 'uri='=>'string', 'port='=>'int', 'wallet='=>'string', 'password='=>'string', 'auth_mode='=>'int'], 'ldap_control_paged_result' => ['bool', 'link_identifier'=>'resource', 'pagesize'=>'int', 'iscritical='=>'bool', 'cookie='=>'string'], 'ldap_control_paged_result_response' => ['bool', 'link_identifier'=>'resource', 'result_identifier'=>'resource', '&w_cookie'=>'string', '&w_estimated'=>'int'], 'ldap_count_entries' => ['int|false', 'ldap'=>'LDAP\Connection', 'result'=>'LDAP\Result'], -'ldap_delete' => ['bool', 'ldap'=>'LDAP\Connection', 'dn'=>'string'], -'ldap_delete_ext' => ['LDAP\Connection|false', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'controls='=>'array'], +'ldap_delete' => ['bool', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'controls='=>'?array'], +'ldap_delete_ext' => ['LDAP\Result|false', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'controls='=>'?array'], 'ldap_dn2ufn' => ['string', 'dn'=>'string'], 'ldap_err2str' => ['string', 'errno'=>'int'], 'ldap_errno' => ['int', 'ldap'=>'LDAP\Connection'], 'ldap_error' => ['string', 'ldap'=>'LDAP\Connection'], 'ldap_escape' => ['string', 'value'=>'string', 'ignore='=>'string', 'flags='=>'int'], -'ldap_exop' => ['mixed', 'ldap'=>'LDAP\Connection', 'reqoid'=>'string', 'reqdata='=>'string', 'serverctrls='=>'array|null', '&w_response_data='=>'string', '&w_response_oid='=>'string'], +'ldap_exop' => ['mixed', 'ldap'=>'LDAP\Connection', 'request_oid'=>'string', 'request_data='=>'string', 'controls='=>'?array', '&w_response_data='=>'string', '&w_response_oid='=>'string'], 'ldap_exop_passwd' => ['bool|string', 'ldap'=>'LDAP\Connection', 'user='=>'string', 'old_password='=>'string', 'new_password='=>'string', '&w_controls='=>'array|null'], 'ldap_exop_refresh' => ['int|false', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'ttl'=>'int'], 'ldap_exop_whoami' => ['string|false', 'ldap'=>'LDAP\Connection'], 'ldap_explode_dn' => ['array|false', 'dn'=>'string', 'with_attrib'=>'int'], 'ldap_first_attribute' => ['string|false', 'ldap'=>'LDAP\Connection', 'entry'=>'LDAP\ResultEntry'], -'ldap_first_entry' => ['LDAP\Connection|false', 'ldap'=>'LDAP\Connection', 'result'=>'LDAP\Result'], -'ldap_first_reference' => ['LDAP\Connection|false', 'ldap'=>'LDAP\Connection', 'result'=>'LDAP\Result'], -'ldap_free_result' => ['bool', 'ldap'=>'LDAP\Connection'], +'ldap_first_entry' => ['LDAP\ResultEntry|false', 'ldap'=>'LDAP\Connection', 'result'=>'LDAP\Result'], +'ldap_first_reference' => ['LDAP\ResultEntry|false', 'ldap'=>'LDAP\Connection', 'result'=>'LDAP\Result'], +'ldap_free_result' => ['bool', 'result'=>'LDAP\Result'], 'ldap_get_attributes' => ['array|false', 'ldap'=>'LDAP\Connection', 'entry'=>'LDAP\ResultEntry'], 'ldap_get_dn' => ['string|false', 'ldap'=>'LDAP\Connection', 'entry'=>'LDAP\ResultEntry'], 'ldap_get_entries' => ['array|false', 'ldap'=>'LDAP\Connection', 'result'=>'LDAP\Result'], -'ldap_get_option' => ['bool', 'ldap'=>'LDAP\Connection', 'option'=>'int', '&w_value'=>'mixed'], +'ldap_get_option' => ['bool', 'ldap'=>'LDAP\Connection', 'option'=>'int', '&w_value='=>'array|string|int|null'], 'ldap_get_values' => ['array|false', 'ldap'=>'LDAP\Connection', 'entry'=>'LDAP\ResultEntry', 'attribute'=>'string'], 'ldap_get_values_len' => ['array|false', 'ldap'=>'LDAP\Connection', 'entry'=>'LDAP\ResultEntry', 'attribute'=>'string'], -'ldap_list' => ['LDAP\Connection|false', 'ldap'=>'resource|array', 'base'=>'string', 'filter'=>'string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int'], -'ldap_mod_add' => ['bool', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'entry'=>'array'], -'ldap_mod_add_ext' => ['LDAP\Connection|false', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'entry'=>'array', 'controls='=>'array'], -'ldap_mod_del' => ['bool', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'entry'=>'array'], -'ldap_mod_del_ext' => ['LDAP\Connection|false', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'entry'=>'array', 'controls='=>'array'], -'ldap_mod_replace' => ['bool', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'entry'=>'array'], -'ldap_mod_replace_ext' => ['LDAP\Connection|false', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'entry'=>'array', 'controls='=>'array'], -'ldap_modify' => ['bool', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'entry'=>'array'], -'ldap_modify_batch' => ['bool', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'modifications_info'=>'array'], +'ldap_list' => ['LDAP\Result|LDAP\Result[]|false', 'ldap'=>'LDAP\Connection|LDAP\Connection[]', 'base'=>'string', 'filter'=>'string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int', 'controls='=>'?array'], +'ldap_mod_add' => ['bool', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'entry'=>'array', 'controls='=>'?array'], +'ldap_mod_add_ext' => ['LDAP\Result|false', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'entry'=>'array', 'controls='=>'?array'], +'ldap_mod_del' => ['bool', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'entry'=>'array', 'controls='=>'?array'], +'ldap_mod_del_ext' => ['LDAP\Result|false', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'entry'=>'array', 'controls='=>'?array'], +'ldap_mod_replace' => ['bool', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'entry'=>'array', 'controls='=>'?array'], +'ldap_mod_replace_ext' => ['LDAP\Result|false', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'entry'=>'array', 'controls='=>'?array'], +'ldap_modify' => ['bool', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'entry'=>'array', 'controls='=>'?array'], +'ldap_modify_batch' => ['bool', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'modifications_info'=>'array', 'controls='=>'?array'], 'ldap_next_attribute' => ['string|false', 'ldap'=>'LDAP\Connection', 'entry'=>'LDAP\ResultEntry'], -'ldap_next_entry' => ['LDAP\Connection|false', 'ldap'=>'LDAP\Connection', 'result'=>'LDAP\Result'], -'ldap_next_reference' => ['LDAP\Connection|false', 'ldap'=>'LDAP\Connection', 'entry'=>'LDAP\ResultEntry'], +'ldap_next_entry' => ['LDAP\ResultEntry|false', 'ldap'=>'LDAP\Connection', 'entry'=>'LDAP\ResultEntry'], +'ldap_next_reference' => ['LDAP\ResultEntry|false', 'ldap'=>'LDAP\Connection', 'entry'=>'LDAP\ResultEntry'], 'ldap_parse_exop' => ['bool', 'ldap'=>'LDAP\Connection', 'result'=>'LDAP\Result', '&w_response_data='=>'string', '&w_response_oid='=>'string'], -'ldap_parse_reference' => ['bool', 'ldap'=>'LDAP\Connection', 'entry'=>'LDAP\ResultEntry', 'referrals'=>'array'], +'ldap_parse_reference' => ['bool', 'ldap'=>'LDAP\Connection', 'entry'=>'LDAP\ResultEntry', '&w_referrals'=>'array'], 'ldap_parse_result' => ['bool', 'ldap'=>'LDAP\Connection', 'result'=>'LDAP\Result', '&w_error_code'=>'int', '&w_matched_dn='=>'string', '&w_error_message='=>'string', '&w_referrals='=>'array', '&w_controls='=>'array'], -'ldap_read' => ['LDAP\Connection|false', 'ldap'=>'LDAP\Connection|array', 'base'=>'string', 'filter'=>'string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int'], -'ldap_rename' => ['bool', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'new_rdn'=>'string', 'new_parent'=>'string', 'delete_old_rdn'=>'bool'], -'ldap_rename_ext' => ['LDAP\Connection|false', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'new_rdn'=>'string', 'new_parent'=>'string', 'delete_old_rdn'=>'bool', 'controls='=>'array'], +'ldap_read' => ['LDAP\Result|LDAP\Result[]|false', 'ldap'=>'LDAP\Connection|LDAP\Connection[]', 'base'=>'string', 'filter'=>'string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int', 'controls='=>'?array'], +'ldap_rename' => ['bool', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'new_rdn'=>'string', 'new_parent'=>'string', 'delete_old_rdn'=>'bool', 'controls='=>'?array'], +'ldap_rename_ext' => ['LDAP\Result|false', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'new_rdn'=>'string', 'new_parent'=>'string', 'delete_old_rdn'=>'bool', 'controls='=>'?array'], 'ldap_sasl_bind' => ['bool', 'ldap'=>'LDAP\Connection', 'dn='=>'string', 'password='=>'string', 'mech='=>'string', 'realm='=>'string', 'authc_id='=>'string', 'authz_id='=>'string', 'props='=>'string'], -'ldap_search' => ['LDAP\Result|false', 'ldap'=>'LDAP\Connection|LDAP\Connection[]', 'base'=>'string', 'filter'=>'string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int'], +'ldap_search' => ['LDAP\Result|LDAP\Result[]|false', 'ldap'=>'LDAP\Connection|LDAP\Connection[]', 'base'=>'string', 'filter'=>'string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int', 'controls='=>'?array'], 'ldap_set_option' => ['bool', 'ldap'=>'LDAP\Connection|null', 'option'=>'int', 'value'=>'mixed'], 'ldap_set_rebind_proc' => ['bool', 'ldap'=>'LDAP\Connection', 'callback'=>'?callable'], -'ldap_start_tls' => ['bool', 'ldap'=>'resource'], +'ldap_start_tls' => ['bool', 'ldap'=>'LDAP\Connection'], 'ldap_t61_to_8859' => ['string', 'value'=>'string'], 'ldap_unbind' => ['bool', 'ldap'=>'LDAP\Connection'], 'leak' => ['', 'num_bytes'=>'int'], diff --git a/dictionaries/CallMap_81_delta.php b/dictionaries/CallMap_81_delta.php index 6c850bc0af0..a22c8b4613a 100644 --- a/dictionaries/CallMap_81_delta.php +++ b/dictionaries/CallMap_81_delta.php @@ -449,11 +449,11 @@ ], 'ldap_add' => [ 'old' => ['bool', 'ldap'=>'resource', 'dn'=>'string', 'entry'=>'array', 'controls='=>'array'], - 'new' => ['bool', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'entry'=>'array', 'controls='=>'array'], + 'new' => ['bool', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'entry'=>'array', 'controls='=>'?array'], ], 'ldap_add_ext' => [ 'old' => ['resource|false', 'ldap'=>'resource', 'dn'=>'string', 'entry'=>'array', 'controls='=>'array'], - 'new' => ['LDAP\Connection|false', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'entry'=>'array', 'controls='=>'array'], + 'new' => ['LDAP\Result|false', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'entry'=>'array', 'controls='=>'?array'], ], 'ldap_bind' => [ 'old' => ['bool', 'ldap'=>'resource', 'dn='=>'string|null', 'password='=>'string|null'], @@ -461,7 +461,7 @@ ], 'ldap_bind_ext' => [ 'old' => ['resource|false', 'ldap'=>'resource', 'dn='=>'string|null', 'password='=>'string|null', 'controls='=>'array'], - 'new' => ['LDAP\Connection|false', 'ldap'=>'LDAP\Connection', 'dn='=>'string|null', 'password='=>'string|null', 'controls='=>'array'], + 'new' => ['LDAP\Result|false', 'ldap'=>'LDAP\Connection', 'dn='=>'string|null', 'password='=>'string|null', 'controls='=>'?array'], ], 'ldap_close' => [ 'old' => ['bool', 'ldap'=>'resource'], @@ -469,7 +469,7 @@ ], 'ldap_compare' => [ 'old' => ['bool|int', 'ldap'=>'resource', 'dn'=>'string', 'attribute'=>'string', 'value'=>'string'], - 'new' => ['bool|int', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'attribute'=>'string', 'value'=>'string'], + 'new' => ['bool|int', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'attribute'=>'string', 'value'=>'string', 'controls='=>'?array'], ], 'ldap_connect' => [ 'old' => ['resource|false', 'uri='=>'string', 'port='=>'int', 'wallet='=>'string', 'password='=>'string', 'auth_mode='=>'int'], @@ -481,11 +481,11 @@ ], 'ldap_delete' => [ 'old' => ['bool', 'ldap'=>'resource', 'dn'=>'string'], - 'new' => ['bool', 'ldap'=>'LDAP\Connection', 'dn'=>'string'], + 'new' => ['bool', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'controls='=>'?array'], ], 'ldap_delete_ext' => [ 'old' => ['resource|false', 'ldap'=>'resource', 'dn'=>'string', 'controls='=>'array'], - 'new' => ['LDAP\Connection|false', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'controls='=>'array'], + 'new' => ['LDAP\Result|false', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'controls='=>'?array'], ], 'ldap_errno' => [ 'old' => ['int', 'ldap'=>'resource'], @@ -497,7 +497,7 @@ ], 'ldap_exop' => [ 'old' => ['mixed', 'ldap'=>'resource', 'reqoid'=>'string', 'reqdata='=>'string', 'serverctrls='=>'array|null', '&w_response_data='=>'string', '&w_response_oid='=>'string'], - 'new' => ['mixed', 'ldap'=>'LDAP\Connection', 'reqoid'=>'string', 'reqdata='=>'string', 'serverctrls='=>'array|null', '&w_response_data='=>'string', '&w_response_oid='=>'string'], + 'new' => ['mixed', 'ldap'=>'LDAP\Connection', 'request_oid'=>'string', 'request_data='=>'string', 'controls='=>'?array', '&w_response_data='=>'string', '&w_response_oid='=>'string'], ], 'ldap_exop_passwd' => [ 'old' => ['bool|string', 'ldap'=>'resource', 'user='=>'string', 'old_password='=>'string', 'new_password='=>'string', '&w_controls='=>'array|null'], @@ -517,15 +517,15 @@ ], 'ldap_first_entry' => [ 'old' => ['resource|false', 'ldap'=>'resource', 'result'=>'resource'], - 'new' => ['LDAP\Connection|false', 'ldap'=>'LDAP\Connection', 'result'=>'LDAP\Result'], + 'new' => ['LDAP\ResultEntry|false', 'ldap'=>'LDAP\Connection', 'result'=>'LDAP\Result'], ], 'ldap_first_reference' => [ 'old' => ['resource|false', 'ldap'=>'resource', 'result'=>'resource'], - 'new' => ['LDAP\Connection|false', 'ldap'=>'LDAP\Connection', 'result'=>'LDAP\Result'], + 'new' => ['LDAP\ResultEntry|false', 'ldap'=>'LDAP\Connection', 'result'=>'LDAP\Result'], ], 'ldap_free_result' => [ 'old' => ['bool', 'ldap'=>'resource'], - 'new' => ['bool', 'ldap'=>'LDAP\Connection'], + 'new' => ['bool', 'result'=>'LDAP\Result'], ], 'ldap_get_attributes' => [ 'old' => ['array|false', 'ldap'=>'resource', 'entry'=>'resource'], @@ -541,7 +541,7 @@ ], 'ldap_get_option' => [ 'old' => ['bool', 'ldap'=>'resource', 'option'=>'int', '&w_value'=>'mixed'], - 'new' => ['bool', 'ldap'=>'LDAP\Connection', 'option'=>'int', '&w_value'=>'mixed'], + 'new' => ['bool', 'ldap'=>'LDAP\Connection', 'option'=>'int', '&w_value='=>'array|string|int|null'], ], 'ldap_get_values' => [ 'old' => ['array|false', 'ldap'=>'resource', 'entry'=>'resource', 'attribute'=>'string'], @@ -553,39 +553,39 @@ ], 'ldap_list' => [ 'old' => ['resource|false', 'ldap'=>'resource|array', 'base'=>'string', 'filter'=>'string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int'], - 'new' => ['LDAP\Connection|false', 'ldap'=>'resource|array', 'base'=>'string', 'filter'=>'string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int'], + 'new' => ['LDAP\Result|LDAP\Result[]|false', 'ldap'=>'LDAP\Connection|LDAP\Connection[]', 'base'=>'string', 'filter'=>'string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int', 'controls='=>'?array'], ], 'ldap_mod_add' => [ 'old' => ['bool', 'ldap'=>'resource', 'dn'=>'string', 'entry'=>'array'], - 'new' => ['bool', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'entry'=>'array'], + 'new' => ['bool', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'entry'=>'array', 'controls='=>'?array'], ], 'ldap_mod_add_ext' => [ 'old' => ['resource|false', 'ldap'=>'resource', 'dn'=>'string', 'entry'=>'array', 'controls='=>'array'], - 'new' => ['LDAP\Connection|false', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'entry'=>'array', 'controls='=>'array'], + 'new' => ['LDAP\Result|false', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'entry'=>'array', 'controls='=>'?array'], ], 'ldap_mod_del' => [ 'old' => ['bool', 'ldap'=>'resource', 'dn'=>'string', 'entry'=>'array'], - 'new' => ['bool', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'entry'=>'array'], + 'new' => ['bool', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'entry'=>'array', 'controls='=>'?array'], ], 'ldap_mod_del_ext' => [ 'old' => ['resource|false', 'ldap'=>'resource', 'dn'=>'string', 'entry'=>'array', 'controls='=>'array'], - 'new' => ['LDAP\Connection|false', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'entry'=>'array', 'controls='=>'array'], + 'new' => ['LDAP\Result|false', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'entry'=>'array', 'controls='=>'?array'], ], 'ldap_mod_replace' => [ 'old' => ['bool', 'ldap'=>'resource', 'dn'=>'string', 'entry'=>'array'], - 'new' => ['bool', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'entry'=>'array'], + 'new' => ['bool', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'entry'=>'array', 'controls='=>'?array'], ], 'ldap_mod_replace_ext' => [ 'old' => ['resource|false', 'ldap'=>'resource', 'dn'=>'string', 'entry'=>'array', 'controls='=>'array'], - 'new' => ['LDAP\Connection|false', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'entry'=>'array', 'controls='=>'array'], + 'new' => ['LDAP\Result|false', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'entry'=>'array', 'controls='=>'?array'], ], 'ldap_modify' => [ 'old' => ['bool', 'ldap'=>'resource', 'dn'=>'string', 'entry'=>'array'], - 'new' => ['bool', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'entry'=>'array'], + 'new' => ['bool', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'entry'=>'array', 'controls='=>'?array'], ], 'ldap_modify_batch' => [ 'old' => ['bool', 'ldap'=>'resource', 'dn'=>'string', 'modifications_info'=>'array'], - 'new' => ['bool', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'modifications_info'=>'array'], + 'new' => ['bool', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'modifications_info'=>'array', 'controls='=>'?array'], ], 'ldap_next_attribute' => [ 'old' => ['string|false', 'ldap'=>'resource', 'entry'=>'resource'], @@ -593,11 +593,11 @@ ], 'ldap_next_entry' => [ 'old' => ['resource|false', 'ldap'=>'resource', 'result'=>'resource'], - 'new' => ['LDAP\Connection|false', 'ldap'=>'LDAP\Connection', 'result'=>'LDAP\Result'], + 'new' => ['LDAP\ResultEntry|false', 'ldap'=>'LDAP\Connection', 'entry'=>'LDAP\ResultEntry'], ], 'ldap_next_reference' => [ 'old' => ['resource|false', 'ldap'=>'resource', 'entry'=>'resource'], - 'new' => ['LDAP\Connection|false', 'ldap'=>'LDAP\Connection', 'entry'=>'LDAP\ResultEntry'], + 'new' => ['LDAP\ResultEntry|false', 'ldap'=>'LDAP\Connection', 'entry'=>'LDAP\ResultEntry'], ], 'ldap_parse_exop' => [ 'old' => ['bool', 'ldap'=>'resource', 'result'=>'resource', '&w_response_data='=>'string', '&w_response_oid='=>'string'], @@ -605,7 +605,7 @@ ], 'ldap_parse_reference' => [ 'old' => ['bool', 'ldap'=>'resource', 'entry'=>'resource', 'referrals'=>'array'], - 'new' => ['bool', 'ldap'=>'LDAP\Connection', 'entry'=>'LDAP\ResultEntry', 'referrals'=>'array'], + 'new' => ['bool', 'ldap'=>'LDAP\Connection', 'entry'=>'LDAP\ResultEntry', '&w_referrals'=>'array'], ], 'ldap_parse_result' => [ 'old' => ['bool', 'ldap'=>'resource', 'result'=>'resource', '&w_error_code'=>'int', '&w_matched_dn='=>'string', '&w_error_message='=>'string', '&w_referrals='=>'array', '&w_controls='=>'array'], @@ -613,15 +613,15 @@ ], 'ldap_read' => [ 'old' => ['resource|false', 'ldap'=>'resource|array', 'base'=>'string', 'filter'=>'string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int'], - 'new' => ['LDAP\Connection|false', 'ldap'=>'LDAP\Connection|array', 'base'=>'string', 'filter'=>'string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int'], + 'new' => ['LDAP\Result|LDAP\Result[]|false', 'ldap'=>'LDAP\Connection|LDAP\Connection[]', 'base'=>'string', 'filter'=>'string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int', 'controls='=>'?array'], ], 'ldap_rename' => [ 'old' => ['bool', 'ldap'=>'resource', 'dn'=>'string', 'new_rdn'=>'string', 'new_parent'=>'string', 'delete_old_rdn'=>'bool'], - 'new' => ['bool', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'new_rdn'=>'string', 'new_parent'=>'string', 'delete_old_rdn'=>'bool'], + 'new' => ['bool', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'new_rdn'=>'string', 'new_parent'=>'string', 'delete_old_rdn'=>'bool', 'controls='=>'?array'], ], 'ldap_rename_ext' => [ 'old' => ['resource|false', 'ldap'=>'resource', 'dn'=>'string', 'new_rdn'=>'string', 'new_parent'=>'string', 'delete_old_rdn'=>'bool', 'controls='=>'array'], - 'new' => ['LDAP\Connection|false', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'new_rdn'=>'string', 'new_parent'=>'string', 'delete_old_rdn'=>'bool', 'controls='=>'array'], + 'new' => ['LDAP\Result|false', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'new_rdn'=>'string', 'new_parent'=>'string', 'delete_old_rdn'=>'bool', 'controls='=>'?array'], ], 'ldap_sasl_bind' => [ 'old' => ['bool', 'ldap'=>'resource', 'dn='=>'string', 'password='=>'string', 'mech='=>'string', 'realm='=>'string', 'authc_id='=>'string', 'authz_id='=>'string', 'props='=>'string'], @@ -629,7 +629,7 @@ ], 'ldap_search' => [ 'old' => ['resource|false', 'ldap'=>'resource|resource[]', 'base'=>'string', 'filter'=>'string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int'], - 'new' => ['LDAP\Result|false', 'ldap'=>'LDAP\Connection|LDAP\Connection[]', 'base'=>'string', 'filter'=>'string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int'], + 'new' => ['LDAP\Result|LDAP\Result[]|false', 'ldap'=>'LDAP\Connection|LDAP\Connection[]', 'base'=>'string', 'filter'=>'string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int', 'controls='=>'?array'], ], 'ldap_set_option' => [ 'old' => ['bool', 'ldap'=>'resource|null', 'option'=>'int', 'value'=>'mixed'], @@ -639,6 +639,10 @@ 'old' => ['bool', 'ldap'=>'resource', 'callback'=>'?callable'], 'new' => ['bool', 'ldap'=>'LDAP\Connection', 'callback'=>'?callable'], ], + 'ldap_start_tls' => [ + 'old' => ['bool', 'ldap'=>'resource'], + 'new' => ['bool', 'ldap'=>'LDAP\Connection'], + ], 'ldap_unbind' => [ 'old' => ['bool', 'ldap'=>'resource'], 'new' => ['bool', 'ldap'=>'LDAP\Connection'], diff --git a/tests/Internal/Codebase/InternalCallMapHandlerTest.php b/tests/Internal/Codebase/InternalCallMapHandlerTest.php index 44f2f8df29c..e843cf83fde 100644 --- a/tests/Internal/Codebase/InternalCallMapHandlerTest.php +++ b/tests/Internal/Codebase/InternalCallMapHandlerTest.php @@ -161,23 +161,6 @@ class InternalCallMapHandlerTest extends TestCase 'intltz_create_enumeration', 'intltz_get_canonical_id', 'intltz_get_display_name', - 'ldap_compare', - 'ldap_delete', - 'ldap_exop', - 'ldap_free_result', - 'ldap_get_option', - 'ldap_list', - 'ldap_mod_add', - 'ldap_mod_del', - 'ldap_mod_replace', - 'ldap_modify', - 'ldap_modify_batch', - 'ldap_next_entry', - 'ldap_parse_reference', - 'ldap_read', - 'ldap_rename', - 'ldap_search', - 'ldap_start_tls', 'long2ip', 'lzf_compress', 'lzf_decompress', From b9cae0185584a100f9c295afbcbd84d8354c0750 Mon Sep 17 00:00:00 2001 From: Corey Taylor Date: Thu, 23 Jun 2022 15:06:09 -0500 Subject: [PATCH 57/88] Remove ldap functions dropped in php 8.0 --- dictionaries/CallMap.php | 2 -- dictionaries/CallMap_80_delta.php | 2 ++ 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dictionaries/CallMap.php b/dictionaries/CallMap.php index 15d8414ac88..cbb5ed7ab1b 100644 --- a/dictionaries/CallMap.php +++ b/dictionaries/CallMap.php @@ -6780,8 +6780,6 @@ 'ldap_close' => ['bool', 'ldap'=>'LDAP\Connection'], 'ldap_compare' => ['bool|int', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'attribute'=>'string', 'value'=>'string', 'controls='=>'?array'], 'ldap_connect' => ['LDAP\Connection|false', 'uri='=>'string', 'port='=>'int', 'wallet='=>'string', 'password='=>'string', 'auth_mode='=>'int'], -'ldap_control_paged_result' => ['bool', 'link_identifier'=>'resource', 'pagesize'=>'int', 'iscritical='=>'bool', 'cookie='=>'string'], -'ldap_control_paged_result_response' => ['bool', 'link_identifier'=>'resource', 'result_identifier'=>'resource', '&w_cookie'=>'string', '&w_estimated'=>'int'], 'ldap_count_entries' => ['int|false', 'ldap'=>'LDAP\Connection', 'result'=>'LDAP\Result'], 'ldap_delete' => ['bool', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'controls='=>'?array'], 'ldap_delete_ext' => ['LDAP\Result|false', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'controls='=>'?array'], diff --git a/dictionaries/CallMap_80_delta.php b/dictionaries/CallMap_80_delta.php index 1b364d52512..35d3518ac92 100644 --- a/dictionaries/CallMap_80_delta.php +++ b/dictionaries/CallMap_80_delta.php @@ -1614,6 +1614,8 @@ 'gzgetss' => ['string|false', 'zp'=>'resource', 'length'=>'int', 'allowable_tags='=>'string'], 'image2wbmp' => ['bool', 'im'=>'resource', 'filename='=>'?string', 'threshold='=>'int'], 'jpeg2wbmp' => ['bool', 'jpegname'=>'string', 'wbmpname'=>'string', 'dest_height'=>'int', 'dest_width'=>'int', 'threshold'=>'int'], + 'ldap_control_paged_result' => ['bool', 'link_identifier'=>'resource', 'pagesize'=>'int', 'iscritical='=>'bool', 'cookie='=>'string'], + 'ldap_control_paged_result_response' => ['bool', 'link_identifier'=>'resource', 'result_identifier'=>'resource', '&w_cookie'=>'string', '&w_estimated'=>'int'], 'ldap_sort' => ['bool', 'link_identifier'=>'resource', 'result_identifier'=>'resource', 'sortfilter'=>'string'], 'number_format\'1' => ['string', 'num'=>'float|int', 'decimals'=>'int', 'decimal_separator'=>'?string', 'thousands_separator'=>'?string'], 'png2wbmp' => ['bool', 'pngname'=>'string', 'wbmpname'=>'string', 'dest_height'=>'int', 'dest_width'=>'int', 'threshold'=>'int'], From 417bf87df0a407b7f932ef5e07d60b39b67314c5 Mon Sep 17 00:00:00 2001 From: Corey Taylor Date: Thu, 23 Jun 2022 16:46:47 -0500 Subject: [PATCH 58/88] Fix array_* function signatures --- dictionaries/CallMap.php | 28 ++++++++-------- dictionaries/CallMap_73_delta.php | 8 +++++ dictionaries/CallMap_74_delta.php | 8 +++++ dictionaries/CallMap_80_delta.php | 32 +++++++++++++++++++ dictionaries/CallMap_historical.php | 26 +++++++-------- .../Codebase/InternalCallMapHandlerTest.php | 15 --------- 6 files changed, 75 insertions(+), 42 deletions(-) diff --git a/dictionaries/CallMap.php b/dictionaries/CallMap.php index cbb5ed7ab1b..0ba97abb4b8 100644 --- a/dictionaries/CallMap.php +++ b/dictionaries/CallMap.php @@ -361,12 +361,12 @@ 'ArithmeticError::getTraceAsString' => ['string'], 'array_change_key_case' => ['associative-array', 'array'=>'array', 'case='=>'int'], 'array_chunk' => ['list', 'array'=>'array', 'length'=>'int', 'preserve_keys='=>'bool'], -'array_column' => ['array', 'array'=>'array', 'column_key'=>'mixed', 'index_key='=>'mixed'], +'array_column' => ['array', 'array'=>'array', 'column_key'=>'int|string|null', 'index_key='=>'int|string|null'], 'array_combine' => ['associative-array', 'keys'=>'string[]|int[]', 'values'=>'array'], 'array_count_values' => ['associative-array', 'array'=>'array'], -'array_diff' => ['associative-array', 'array'=>'array', 'arrays'=>'array', '...args='=>'array'], -'array_diff_assoc' => ['associative-array', 'array'=>'array', 'arrays'=>'array', '...args='=>'array'], -'array_diff_key' => ['associative-array', 'array'=>'array', 'arrays'=>'array', '...args='=>'array'], +'array_diff' => ['associative-array', 'array'=>'array', '...arrays='=>'array'], +'array_diff_assoc' => ['associative-array', 'array'=>'array', '...arrays='=>'array'], +'array_diff_key' => ['associative-array', 'array'=>'array', '...arrays='=>'array'], 'array_diff_uassoc' => ['associative-array', 'array'=>'array', 'rest'=>'array', 'data_comp_func'=>'callable(mixed,mixed):int'], 'array_diff_uassoc\'1' => ['associative-array', 'array'=>'array', 'rest'=>'array', 'arr3'=>'array', 'arg4'=>'array|callable(mixed,mixed):int', '...rest='=>'array|callable(mixed,mixed):int'], 'array_diff_ukey' => ['associative-array', 'array'=>'array', 'rest'=>'array', 'key_comp_func'=>'callable(mixed,mixed):int'], @@ -375,31 +375,31 @@ 'array_fill_keys' => ['array', 'keys'=>'array', 'value'=>'mixed'], 'array_filter' => ['associative-array', 'array'=>'array', 'callback='=>'callable(mixed,mixed=):scalar', 'mode='=>'int'], 'array_flip' => ['associative-array|associative-array', 'array'=>'array'], -'array_intersect' => ['associative-array', 'array'=>'array', 'arrays'=>'array', '...args='=>'array'], -'array_intersect_assoc' => ['associative-array', 'array'=>'array', 'arrays'=>'array', '...args='=>'array'], -'array_intersect_key' => ['associative-array', 'array'=>'array', 'arrays'=>'array', '...args='=>'array'], +'array_intersect' => ['associative-array', 'array'=>'array', '...arrays='=>'array'], +'array_intersect_assoc' => ['associative-array', 'array'=>'array', '...arrays='=>'array'], +'array_intersect_key' => ['associative-array', 'array'=>'array', '...arrays='=>'array'], 'array_intersect_uassoc' => ['associative-array', 'array'=>'array', 'rest'=>'array', 'key_compare_func'=>'callable(mixed,mixed):int'], 'array_intersect_uassoc\'1' => ['associative-array', 'array'=>'array', 'rest'=>'array', 'arr3'=>'array', 'arg4'=>'array|callable(mixed,mixed):int', '...rest'=>'array|callable(mixed,mixed):int'], 'array_intersect_ukey' => ['associative-array', 'array'=>'array', 'rest'=>'array', 'key_compare_func'=>'callable(mixed,mixed):int'], 'array_intersect_ukey\'1' => ['associative-array', 'array'=>'array', 'rest'=>'array', 'arr3'=>'array', 'arg4'=>'array|callable(mixed,mixed):int', '...rest'=>'array|callable(mixed,mixed):int'], 'array_is_list' => ['bool', 'array'=>'array'], -'array_key_exists' => ['bool', 'key'=>'string|int', 'array'=>'array|ArrayObject'], +'array_key_exists' => ['bool', 'key'=>'string|int', 'array'=>'array'], 'array_key_first' => ['int|string|null', 'array'=>'array'], 'array_key_last' => ['int|string|null', 'array'=>'array'], 'array_keys' => ['list', 'array'=>'array', 'filter_value='=>'mixed', 'strict='=>'bool'], 'array_map' => ['array', 'callback'=>'?callable', 'array'=>'array', '...arrays='=>'array'], -'array_merge' => ['array', 'arrays'=>'array', '...args='=>'array'], -'array_merge_recursive' => ['array', 'arrays'=>'array', '...args='=>'array'], +'array_merge' => ['array', '...arrays='=>'array'], +'array_merge_recursive' => ['array', '...arrays='=>'array'], 'array_multisort' => ['bool', '&rw_array'=>'array', 'rest='=>'array|int', 'array1_sort_flags='=>'array|int', '...args='=>'array|int'], 'array_pad' => ['array', 'array'=>'array', 'length'=>'int', 'value'=>'mixed'], 'array_pop' => ['mixed', '&rw_array'=>'array'], 'array_product' => ['int|float', 'array'=>'array'], -'array_push' => ['int', '&rw_array'=>'array', 'values'=>'mixed', '...vars='=>'mixed'], +'array_push' => ['int', '&rw_array'=>'array', '...values='=>'mixed'], 'array_rand' => ['int|string|array|array', 'array'=>'non-empty-array', 'num'=>'int'], 'array_rand\'1' => ['int|string', 'array'=>'array'], 'array_reduce' => ['mixed', 'array'=>'array', 'callback'=>'callable(mixed,mixed):mixed', 'initial='=>'mixed'], -'array_replace' => ['array', 'array'=>'array', 'replacements'=>'array', '...args='=>'array'], -'array_replace_recursive' => ['array', 'array'=>'array', 'replacements'=>'array', '...args='=>'array'], +'array_replace' => ['array', 'array'=>'array', '...replacements='=>'array'], +'array_replace_recursive' => ['array', 'array'=>'array', '...replacements='=>'array'], 'array_reverse' => ['array', 'array'=>'array', 'preserve_keys='=>'bool'], 'array_search' => ['int|string|false', 'needle'=>'mixed', 'haystack'=>'array', 'strict='=>'bool'], 'array_shift' => ['mixed|null', '&rw_array'=>'array'], @@ -421,7 +421,7 @@ 'array_unique' => ['array', 'array'=>'array', 'flags='=>'0'], 'array_unique\'1' => ['array', 'array'=>'array', 'flags='=>'1'], 'array_unique\'2' => ['array', 'array'=>'array', 'flags='=>'2|5'], -'array_unshift' => ['int', '&rw_array'=>'array', 'values'=>'mixed', '...vars='=>'mixed'], +'array_unshift' => ['int', '&rw_array'=>'array', '...values='=>'mixed'], 'array_values' => ['list', 'array'=>'array'], 'array_walk' => ['bool', '&rw_array'=>'array', 'callback'=>'callable', 'arg='=>'mixed'], 'array_walk\'1' => ['bool', '&rw_array'=>'object', 'callback'=>'callable', 'arg='=>'mixed'], diff --git a/dictionaries/CallMap_73_delta.php b/dictionaries/CallMap_73_delta.php index c54758bbc0d..91002d47056 100644 --- a/dictionaries/CallMap_73_delta.php +++ b/dictionaries/CallMap_73_delta.php @@ -50,6 +50,14 @@ 'socket_wsaprotocol_info_release' => ['bool', 'info_id'=>'string'], ], 'changed' => [ + 'array_push' => [ + 'old' => ['int', '&rw_array'=>'array', '...values'=>'mixed'], + 'new' => ['int', '&rw_array'=>'array', '...values='=>'mixed'], + ], + 'array_unshift' => [ + 'old' => ['int', '&rw_array'=>'array', '...values'=>'mixed'], + 'new' => ['int', '&rw_array'=>'array', '...values='=>'mixed'], + ], 'bcscale' => [ 'old' => ['int', 'scale'=>'int'], 'new' => ['int', 'scale='=>'int'], diff --git a/dictionaries/CallMap_74_delta.php b/dictionaries/CallMap_74_delta.php index 83b9108bd27..af5e0d976bf 100644 --- a/dictionaries/CallMap_74_delta.php +++ b/dictionaries/CallMap_74_delta.php @@ -20,6 +20,14 @@ 'mb_str_split' => ['list|false', 'string'=>'string', 'length='=>'positive-int', 'encoding='=>'string'], ], 'changed' => [ + 'array_merge' => [ + 'old' => ['array', '...arrays'=>'array'], + 'new' => ['array', '...arrays='=>'array'], + ], + 'array_merge_recursive' => [ + 'old' => ['array', '...arrays'=>'array'], + 'new' => ['array', '...arrays='=>'array'], + ], 'gzread' => [ 'old' => ['string|0', 'stream'=>'resource', 'length'=>'int'], 'new' => ['string|false', 'stream'=>'resource', 'length'=>'int'], diff --git a/dictionaries/CallMap_80_delta.php b/dictionaries/CallMap_80_delta.php index 35d3518ac92..1e38f8cbda3 100644 --- a/dictionaries/CallMap_80_delta.php +++ b/dictionaries/CallMap_80_delta.php @@ -133,10 +133,42 @@ 'old' => ['bool', 'name'=>'string', 'content'=>'string', 'isParam'=>'bool', 'publicId'=>'string', 'systemId'=>'string', 'notationData'=>'string'], 'new' => ['bool', 'name'=>'string', 'content'=>'string', 'isParam='=>'bool', 'publicId='=>'?string', 'systemId='=>'?string', 'notationData='=>'?string'], ], + 'array_column' => [ + 'old' => ['array', 'array'=>'array', 'column_key'=>'mixed', 'index_key='=>'mixed'], + 'new' => ['array', 'array'=>'array', 'column_key'=>'int|string|null', 'index_key='=>'int|string|null'], + ], 'array_combine' => [ 'old' => ['associative-array|false', 'keys'=>'string[]|int[]', 'values'=>'array'], 'new' => ['associative-array', 'keys'=>'string[]|int[]', 'values'=>'array'], ], + 'array_diff' => [ + 'old' => ['associative-array', 'array'=>'array', '...arrays'=>'array'], + 'new' => ['associative-array', 'array'=>'array', '...arrays='=>'array'], + ], + 'array_diff_assoc' => [ + 'old' => ['associative-array', 'array'=>'array', '...arrays'=>'array'], + 'new' => ['associative-array', 'array'=>'array', '...arrays='=>'array'], + ], + 'array_diff_key' => [ + 'old' => ['associative-array', 'array'=>'array', '...arrays'=>'array'], + 'new' => ['associative-array', 'array'=>'array', '...arrays='=>'array'], + ], + 'array_key_exists' => [ + 'old' => ['bool', 'key'=>'string|int', 'array'=>'array|object'], + 'new' => ['bool', 'key'=>'string|int', 'array'=>'array'], + ], + 'array_intersect' => [ + 'old' => ['associative-array', 'array'=>'array', '...arrays'=>'array'], + 'new' => ['associative-array', 'array'=>'array', '...arrays='=>'array'], + ], + 'array_intersect_assoc' => [ + 'old' => ['associative-array', 'array'=>'array', '...arrays'=>'array'], + 'new' => ['associative-array', 'array'=>'array', '...arrays='=>'array'], + ], + 'array_intersect_key' => [ + 'old' => ['associative-array', 'array'=>'array', '...arrays'=>'array'], + 'new' => ['associative-array', 'array'=>'array', '...arrays='=>'array'], + ], 'bcadd' => [ 'old' => ['numeric-string', 'num1'=>'numeric-string', 'num2'=>'numeric-string', 'scale='=>'int'], 'new' => ['numeric-string', 'num1'=>'numeric-string', 'num2'=>'numeric-string', 'scale='=>'int|null'], diff --git a/dictionaries/CallMap_historical.php b/dictionaries/CallMap_historical.php index f77fa29f0c2..506a844ee92 100644 --- a/dictionaries/CallMap_historical.php +++ b/dictionaries/CallMap_historical.php @@ -9510,9 +9510,9 @@ 'array_column' => ['array', 'array'=>'array', 'column_key'=>'mixed', 'index_key='=>'mixed'], 'array_combine' => ['associative-array|false', 'keys'=>'string[]|int[]', 'values'=>'array'], 'array_count_values' => ['associative-array', 'array'=>'array'], - 'array_diff' => ['associative-array', 'array'=>'array', 'arrays'=>'array', '...args='=>'array'], - 'array_diff_assoc' => ['associative-array', 'array'=>'array', 'arrays'=>'array', '...args='=>'array'], - 'array_diff_key' => ['associative-array', 'array'=>'array', 'arrays'=>'array', '...args='=>'array'], + 'array_diff' => ['associative-array', 'array'=>'array', '...arrays'=>'array'], + 'array_diff_assoc' => ['associative-array', 'array'=>'array', '...arrays'=>'array'], + 'array_diff_key' => ['associative-array', 'array'=>'array', '...arrays'=>'array'], 'array_diff_uassoc' => ['associative-array', 'array'=>'array', 'rest'=>'array', 'data_comp_func'=>'callable(mixed,mixed):int'], 'array_diff_uassoc\'1' => ['associative-array', 'array'=>'array', 'rest'=>'array', 'arr3'=>'array', 'arg4'=>'array|callable(mixed,mixed):int', '...rest='=>'array|callable(mixed,mixed):int'], 'array_diff_ukey' => ['associative-array', 'array'=>'array', 'rest'=>'array', 'key_comp_func'=>'callable(mixed,mixed):int'], @@ -9521,28 +9521,28 @@ 'array_fill_keys' => ['array', 'keys'=>'array', 'value'=>'mixed'], 'array_filter' => ['associative-array', 'array'=>'array', 'callback='=>'callable(mixed,mixed=):scalar', 'mode='=>'int'], 'array_flip' => ['associative-array|associative-array', 'array'=>'array'], - 'array_intersect' => ['associative-array', 'array'=>'array', 'arrays'=>'array', '...args='=>'array'], - 'array_intersect_assoc' => ['associative-array', 'array'=>'array', 'arrays'=>'array', '...args='=>'array'], - 'array_intersect_key' => ['associative-array', 'array'=>'array', 'arrays'=>'array', '...args='=>'array'], + 'array_intersect' => ['associative-array', 'array'=>'array', '...arrays'=>'array'], + 'array_intersect_assoc' => ['associative-array', 'array'=>'array', '...arrays'=>'array'], + 'array_intersect_key' => ['associative-array', 'array'=>'array', '...arrays'=>'array'], 'array_intersect_uassoc' => ['associative-array', 'array'=>'array', 'rest'=>'array', 'key_compare_func'=>'callable(mixed,mixed):int'], 'array_intersect_uassoc\'1' => ['associative-array', 'array'=>'array', 'rest'=>'array', 'arr3'=>'array', 'arg4'=>'array|callable(mixed,mixed):int', '...rest'=>'array|callable(mixed,mixed):int'], 'array_intersect_ukey' => ['associative-array', 'array'=>'array', 'rest'=>'array', 'key_compare_func'=>'callable(mixed,mixed):int'], 'array_intersect_ukey\'1' => ['associative-array', 'array'=>'array', 'rest'=>'array', 'arr3'=>'array', 'arg4'=>'array|callable(mixed,mixed):int', '...rest'=>'array|callable(mixed,mixed):int'], - 'array_key_exists' => ['bool', 'key'=>'string|int', 'array'=>'array|ArrayObject'], + 'array_key_exists' => ['bool', 'key'=>'string|int', 'array'=>'array|object'], 'array_keys' => ['list', 'array'=>'array', 'filter_value='=>'mixed', 'strict='=>'bool'], 'array_map' => ['array', 'callback'=>'?callable', 'array'=>'array', '...arrays='=>'array'], - 'array_merge' => ['array', 'arrays'=>'array', '...args='=>'array'], - 'array_merge_recursive' => ['array', 'arrays'=>'array', '...args='=>'array'], + 'array_merge' => ['array', '...arrays'=>'array'], + 'array_merge_recursive' => ['array', '...arrays'=>'array'], 'array_multisort' => ['bool', '&rw_array'=>'array', 'rest='=>'array|int', 'array1_sort_flags='=>'array|int', '...args='=>'array|int'], 'array_pad' => ['array', 'array'=>'array', 'length'=>'int', 'value'=>'mixed'], 'array_pop' => ['mixed', '&rw_array'=>'array'], 'array_product' => ['int|float', 'array'=>'array'], - 'array_push' => ['int', '&rw_array'=>'array', 'values'=>'mixed', '...vars='=>'mixed'], + 'array_push' => ['int', '&rw_array'=>'array', '...values'=>'mixed'], 'array_rand' => ['int|string|array|array', 'array'=>'non-empty-array', 'num'=>'int'], 'array_rand\'1' => ['int|string', 'array'=>'array'], 'array_reduce' => ['mixed', 'array'=>'array', 'callback'=>'callable(mixed,mixed):mixed', 'initial='=>'mixed'], - 'array_replace' => ['array', 'array'=>'array', 'replacements'=>'array', '...args='=>'array'], - 'array_replace_recursive' => ['array', 'array'=>'array', 'replacements'=>'array', '...args='=>'array'], + 'array_replace' => ['array', 'array'=>'array', '...replacements='=>'array'], + 'array_replace_recursive' => ['array', 'array'=>'array', '...replacements='=>'array'], 'array_reverse' => ['array', 'array'=>'array', 'preserve_keys='=>'bool'], 'array_search' => ['int|string|false', 'needle'=>'mixed', 'haystack'=>'array', 'strict='=>'bool'], 'array_shift' => ['mixed|null', '&rw_array'=>'array'], @@ -9564,7 +9564,7 @@ 'array_unique' => ['array', 'array'=>'array', 'flags='=>'0'], 'array_unique\'1' => ['array', 'array'=>'array', 'flags='=>'1'], 'array_unique\'2' => ['array', 'array'=>'array', 'flags='=>'2|5'], - 'array_unshift' => ['int', '&rw_array'=>'array', 'values'=>'mixed', '...vars='=>'mixed'], + 'array_unshift' => ['int', '&rw_array'=>'array', '...values'=>'mixed'], 'array_values' => ['list', 'array'=>'array'], 'array_walk' => ['bool', '&rw_array'=>'array', 'callback'=>'callable', 'arg='=>'mixed'], 'array_walk\'1' => ['bool', '&rw_array'=>'object', 'callback'=>'callable', 'arg='=>'mixed'], diff --git a/tests/Internal/Codebase/InternalCallMapHandlerTest.php b/tests/Internal/Codebase/InternalCallMapHandlerTest.php index e843cf83fde..24b4bb44c1d 100644 --- a/tests/Internal/Codebase/InternalCallMapHandlerTest.php +++ b/tests/Internal/Codebase/InternalCallMapHandlerTest.php @@ -47,21 +47,7 @@ class InternalCallMapHandlerTest extends TestCase */ private static $ignoredFunctions = [ 'apcu_entry', - 'array_column', - 'array_diff', - 'array_diff_assoc', - 'array_diff_key', - 'array_intersect', - 'array_intersect_assoc', - 'array_intersect_key', - 'array_key_exists', - 'array_merge', - 'array_merge_recursive', 'array_multisort', - 'array_push', - 'array_replace', - 'array_replace_recursive', - 'array_unshift', 'bcdiv', 'bcmod', 'bcpowmod', @@ -355,7 +341,6 @@ class InternalCallMapHandlerTest extends TestCase 'yaml_emit_file', 'zip_entry_close', 'zlib_encode', - ]; /** From e1e858512d61fb7bb099e838c32242b04f46defb Mon Sep 17 00:00:00 2001 From: Corey Taylor Date: Thu, 23 Jun 2022 21:50:41 -0500 Subject: [PATCH 59/88] Fix filter_var and filter_var_array signatures --- dictionaries/CallMap.php | 4 ++-- dictionaries/CallMap_historical.php | 4 ++-- tests/Internal/Codebase/InternalCallMapHandlerTest.php | 2 -- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/dictionaries/CallMap.php b/dictionaries/CallMap.php index cbb5ed7ab1b..9afdb324590 100644 --- a/dictionaries/CallMap.php +++ b/dictionaries/CallMap.php @@ -3292,8 +3292,8 @@ 'filter_input' => ['mixed|false', 'type'=>'int', 'var_name'=>'string', 'filter='=>'int', 'options='=>'array|int'], 'filter_input_array' => ['mixed|false', 'type'=>'int', 'options='=>'int|array', 'add_empty='=>'bool'], 'filter_list' => ['array'], -'filter_var' => ['mixed|false', 'value'=>'mixed', 'filter='=>'int', 'options='=>'mixed'], -'filter_var_array' => ['mixed|false', 'array'=>'array', 'options='=>'mixed', 'add_empty='=>'bool'], +'filter_var' => ['mixed|false', 'value'=>'mixed', 'filter='=>'int', 'options='=>'array|int'], +'filter_var_array' => ['array|false|null', 'array'=>'array', 'options='=>'array|int', 'add_empty='=>'bool'], 'FilterIterator::__construct' => ['void', 'iterator'=>'Iterator'], 'FilterIterator::accept' => ['bool'], 'FilterIterator::current' => ['mixed'], diff --git a/dictionaries/CallMap_historical.php b/dictionaries/CallMap_historical.php index f77fa29f0c2..797dfdd8b1d 100644 --- a/dictionaries/CallMap_historical.php +++ b/dictionaries/CallMap_historical.php @@ -10863,8 +10863,8 @@ 'filter_input' => ['mixed|false', 'type'=>'int', 'var_name'=>'string', 'filter='=>'int', 'options='=>'array|int'], 'filter_input_array' => ['mixed|false', 'type'=>'int', 'options='=>'int|array', 'add_empty='=>'bool'], 'filter_list' => ['array'], - 'filter_var' => ['mixed|false', 'value'=>'mixed', 'filter='=>'int', 'options='=>'mixed'], - 'filter_var_array' => ['mixed|false', 'array'=>'array', 'options='=>'mixed', 'add_empty='=>'bool'], + 'filter_var' => ['mixed|false', 'value'=>'mixed', 'filter='=>'int', 'options='=>'array|int'], + 'filter_var_array' => ['array|false|null', 'array'=>'array', 'options='=>'array|int', 'add_empty='=>'bool'], 'finfo::__construct' => ['void', 'options='=>'int', 'magic_file='=>'string'], 'finfo::buffer' => ['string|false', 'string'=>'string', 'options='=>'int', 'context='=>'resource'], 'finfo::file' => ['string|false', 'file_name'=>'string', 'options='=>'int', 'context='=>'resource'], diff --git a/tests/Internal/Codebase/InternalCallMapHandlerTest.php b/tests/Internal/Codebase/InternalCallMapHandlerTest.php index e843cf83fde..9212f13500f 100644 --- a/tests/Internal/Codebase/InternalCallMapHandlerTest.php +++ b/tests/Internal/Codebase/InternalCallMapHandlerTest.php @@ -100,8 +100,6 @@ class InternalCallMapHandlerTest extends TestCase 'enchant_dict_suggest', 'enum_exists', 'extract', - 'filter_var', - 'filter_var_array', // https://www.php.net/manual/en/function.fputcsv.php 'fputcsv' => ['8.1'], 'get_class_methods', From 3297e35d9a387ed3c47ce82ca210c87b737424ed Mon Sep 17 00:00:00 2001 From: Corey Taylor Date: Thu, 23 Jun 2022 22:45:47 -0500 Subject: [PATCH 60/88] Fix datefmt_* function signatures --- dictionaries/CallMap.php | 13 ++++++------- dictionaries/CallMap_80_delta.php | 4 ++++ dictionaries/CallMap_historical.php | 13 ++++++------- .../Codebase/InternalCallMapHandlerTest.php | 5 ----- 4 files changed, 16 insertions(+), 19 deletions(-) diff --git a/dictionaries/CallMap.php b/dictionaries/CallMap.php index cbb5ed7ab1b..81a1032d722 100644 --- a/dictionaries/CallMap.php +++ b/dictionaries/CallMap.php @@ -1740,7 +1740,7 @@ 'date_timestamp_set' => ['DateTime|false', 'object'=>'DateTime', 'timestamp'=>'int'], 'date_timezone_get' => ['DateTimeZone|false', 'object'=>'DateTimeInterface'], 'date_timezone_set' => ['DateTime|false', 'object'=>'DateTime', 'timezone'=>'DateTimeZone'], -'datefmt_create' => ['IntlDateFormatter|false', 'locale'=>'?string', 'dateType'=>'?int', 'timeType'=>'?int', 'timezone='=>'string|DateTimeZone|IntlTimeZone|null', 'calendar='=>'int|IntlCalendar|null', 'pattern='=>'string'], +'datefmt_create' => ['?IntlDateFormatter', 'locale'=>'?string', 'dateType='=>'int', 'timeType='=>'int', 'timezone='=>'DateTimeZone|IntlTimeZone|string|null', 'calendar='=>'IntlCalendar|int|null', 'pattern='=>'?string'], 'datefmt_format' => ['string|false', 'formatter'=>'IntlDateFormatter', 'datetime'=>'DateTime|IntlCalendar|array|int'], 'datefmt_format_object' => ['string|false', 'datetime'=>'object', 'format='=>'mixed', 'locale='=>'string'], 'datefmt_get_calendar' => ['int', 'formatter'=>'IntlDateFormatter'], @@ -1751,16 +1751,15 @@ 'datefmt_get_locale' => ['string|false', 'formatter'=>'IntlDateFormatter', 'type='=>'int'], 'datefmt_get_pattern' => ['string', 'formatter'=>'IntlDateFormatter'], 'datefmt_get_timetype' => ['int', 'formatter'=>'IntlDateFormatter'], -'datefmt_get_timezone' => ['IntlTimeZone|false'], -'datefmt_get_timezone_id' => ['string', 'formatter'=>'IntlDateFormatter'], +'datefmt_get_timezone' => ['IntlTimeZone|false', 'formatter'=>'IntlDateFormatter'], +'datefmt_get_timezone_id' => ['string|false', 'formatter'=>'IntlDateFormatter'], 'datefmt_is_lenient' => ['bool', 'formatter'=>'IntlDateFormatter'], -'datefmt_localtime' => ['array|false', 'formatter'=>'IntlDateFormatter', 'string='=>'string', '&rw_offset='=>'int'], -'datefmt_parse' => ['int|false', 'formatter'=>'IntlDateFormatter', 'string='=>'string', '&rw_offset='=>'int'], +'datefmt_localtime' => ['array|false', 'formatter'=>'IntlDateFormatter', 'string'=>'string', '&rw_offset='=>'int'], +'datefmt_parse' => ['int|false', 'formatter'=>'IntlDateFormatter', 'string'=>'string', '&rw_offset='=>'int'], 'datefmt_set_calendar' => ['bool', 'formatter'=>'IntlDateFormatter', 'calendar'=>'int'], 'datefmt_set_lenient' => ['?bool', 'formatter'=>'IntlDateFormatter', 'lenient'=>'bool'], 'datefmt_set_pattern' => ['bool', 'formatter'=>'IntlDateFormatter', 'pattern'=>'string'], -'datefmt_set_timezone' => ['bool', 'formatter'=>'mixed'], -'datefmt_set_timezone_id' => ['bool', 'fmt'=>'IntlDateFormatter', 'zone'=>'string'], +'datefmt_set_timezone' => ['false|null', 'formatter'=>'IntlDateFormatter', 'timezone'=>'IntlTimeZone|DateTimeZone|string|null'], 'DateInterval::__construct' => ['void', 'spec'=>'string'], 'DateInterval::__set_state' => ['DateInterval', 'array'=>'array'], 'DateInterval::__wakeup' => ['void'], diff --git a/dictionaries/CallMap_80_delta.php b/dictionaries/CallMap_80_delta.php index 35d3518ac92..f6a59275b67 100644 --- a/dictionaries/CallMap_80_delta.php +++ b/dictionaries/CallMap_80_delta.php @@ -309,6 +309,10 @@ 'old' => ['string|false', 'object'=>'DateTimeInterface', 'format'=>'string'], 'new' => ['string', 'object'=>'DateTimeInterface', 'format'=>'string'], ], + 'datefmt_create' => [ + 'old' => ['?IntlDateFormatter', 'locale'=>'?string', 'dateType'=>'int', 'timeType'=>'int', 'timezone='=>'DateTimeZone|IntlTimeZone|string|null', 'calendar='=>'IntlCalendar|int|null', 'pattern='=>'string'], + 'new' => ['?IntlDateFormatter', 'locale'=>'?string', 'dateType='=>'int', 'timeType='=>'int', 'timezone='=>'DateTimeZone|IntlTimeZone|string|null', 'calendar='=>'IntlCalendar|int|null', 'pattern='=>'?string'], + ], 'dom_import_simplexml' => [ 'old' => ['DOMElement|null', 'node'=>'SimpleXMLElement'], 'new' => ['DOMElement', 'node'=>'SimpleXMLElement'], diff --git a/dictionaries/CallMap_historical.php b/dictionaries/CallMap_historical.php index f77fa29f0c2..68df0d3e151 100644 --- a/dictionaries/CallMap_historical.php +++ b/dictionaries/CallMap_historical.php @@ -10187,7 +10187,7 @@ 'date_timestamp_set' => ['DateTime|false', 'object'=>'DateTime', 'timestamp'=>'int'], 'date_timezone_get' => ['DateTimeZone|false', 'object'=>'DateTimeInterface'], 'date_timezone_set' => ['DateTime|false', 'object'=>'DateTime', 'timezone'=>'DateTimeZone'], - 'datefmt_create' => ['IntlDateFormatter|false', 'locale'=>'?string', 'dateType'=>'?int', 'timeType'=>'?int', 'timezone='=>'string|DateTimeZone|IntlTimeZone|null', 'calendar='=>'int|IntlCalendar|null', 'pattern='=>'string'], + 'datefmt_create' => ['?IntlDateFormatter', 'locale'=>'?string', 'dateType'=>'int', 'timeType'=>'int', 'timezone='=>'DateTimeZone|IntlTimeZone|string|null', 'calendar='=>'IntlCalendar|int|null', 'pattern='=>'string'], 'datefmt_format' => ['string|false', 'formatter'=>'IntlDateFormatter', 'datetime'=>'DateTime|IntlCalendar|array|int'], 'datefmt_format_object' => ['string|false', 'datetime'=>'object', 'format='=>'mixed', 'locale='=>'string'], 'datefmt_get_calendar' => ['int', 'formatter'=>'IntlDateFormatter'], @@ -10198,16 +10198,15 @@ 'datefmt_get_locale' => ['string|false', 'formatter'=>'IntlDateFormatter', 'type='=>'int'], 'datefmt_get_pattern' => ['string', 'formatter'=>'IntlDateFormatter'], 'datefmt_get_timetype' => ['int', 'formatter'=>'IntlDateFormatter'], - 'datefmt_get_timezone' => ['IntlTimeZone|false'], - 'datefmt_get_timezone_id' => ['string', 'formatter'=>'IntlDateFormatter'], + 'datefmt_get_timezone' => ['IntlTimeZone|false', 'formatter'=>'IntlDateFormatter'], + 'datefmt_get_timezone_id' => ['string|false', 'formatter'=>'IntlDateFormatter'], 'datefmt_is_lenient' => ['bool', 'formatter'=>'IntlDateFormatter'], - 'datefmt_localtime' => ['array|false', 'formatter'=>'IntlDateFormatter', 'string='=>'string', '&rw_offset='=>'int'], - 'datefmt_parse' => ['int|false', 'formatter'=>'IntlDateFormatter', 'string='=>'string', '&rw_offset='=>'int'], + 'datefmt_localtime' => ['array|false', 'formatter'=>'IntlDateFormatter', 'string'=>'string', '&rw_offset='=>'int'], + 'datefmt_parse' => ['int|false', 'formatter'=>'IntlDateFormatter', 'string'=>'string', '&rw_offset='=>'int'], 'datefmt_set_calendar' => ['bool', 'formatter'=>'IntlDateFormatter', 'calendar'=>'int'], 'datefmt_set_lenient' => ['?bool', 'formatter'=>'IntlDateFormatter', 'lenient'=>'bool'], 'datefmt_set_pattern' => ['bool', 'formatter'=>'IntlDateFormatter', 'pattern'=>'string'], - 'datefmt_set_timezone' => ['bool', 'formatter'=>'mixed'], - 'datefmt_set_timezone_id' => ['bool', 'fmt'=>'IntlDateFormatter', 'zone'=>'string'], + 'datefmt_set_timezone' => ['false|null', 'formatter'=>'IntlDateFormatter', 'timezone'=>'IntlTimeZone|DateTimeZone|string|null'], 'db2_autocommit' => ['mixed', 'connection'=>'resource', 'value='=>'int'], 'db2_bind_param' => ['bool', 'stmt'=>'resource', 'parameter_number'=>'int', 'variable_name'=>'string', 'parameter_type='=>'int', 'data_type='=>'int', 'precision='=>'int', 'scale='=>'int'], 'db2_client_info' => ['object|false', 'connection'=>'resource'], diff --git a/tests/Internal/Codebase/InternalCallMapHandlerTest.php b/tests/Internal/Codebase/InternalCallMapHandlerTest.php index e843cf83fde..bde50c07734 100644 --- a/tests/Internal/Codebase/InternalCallMapHandlerTest.php +++ b/tests/Internal/Codebase/InternalCallMapHandlerTest.php @@ -69,11 +69,6 @@ class InternalCallMapHandlerTest extends TestCase 'count', 'crypt', 'date_isodate_set', - 'datefmt_create', - 'datefmt_get_timezone', - 'datefmt_localtime', - 'datefmt_parse', - 'datefmt_set_timezone', 'debug_zval_dump', 'deflate_add', 'dns_get_mx', From 933714f6ad1d7adc5df9c41d431e930cf637516d Mon Sep 17 00:00:00 2001 From: Benjamin Morel Date: Thu, 23 Jun 2022 10:36:26 +0200 Subject: [PATCH 61/88] Add ReflectionProperty properties --- dictionaries/PropertyMap.php | 4 ++++ src/Psalm/Internal/Codebase/Reflection.php | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/dictionaries/PropertyMap.php b/dictionaries/PropertyMap.php index 8c874598b0a..f769b171ca2 100644 --- a/dictionaries/PropertyMap.php +++ b/dictionaries/PropertyMap.php @@ -450,6 +450,10 @@ 'reflectionparameter' => [ 'name' => 'string', ], + 'reflectionproperty' => [ + 'name' => 'string', + 'class' => 'string', + ], 'phpparser\\node\\expr\\array_' => [ 'items' => 'array', ], diff --git a/src/Psalm/Internal/Codebase/Reflection.php b/src/Psalm/Internal/Codebase/Reflection.php index ed3b3fc1b2d..8a50103b2f6 100644 --- a/src/Psalm/Internal/Codebase/Reflection.php +++ b/src/Psalm/Internal/Codebase/Reflection.php @@ -133,9 +133,9 @@ public function registerClass(ReflectionClass $reflected_class): void $storage->properties[$property_name]->visibility = ClassLikeAnalyzer::VISIBILITY_PRIVATE; } - $property_id = (string)$class_property->class . '::$' . $property_name; + $property_id = $class_property->class . '::$' . $property_name; - $storage->declaring_property_ids[$property_name] = (string)$class_property->class; + $storage->declaring_property_ids[$property_name] = $class_property->class; $storage->appearing_property_ids[$property_name] = $property_id; if (!$class_property->isPrivate()) { From 450409f04586e7b4020d702f0ca26e5a4492272f Mon Sep 17 00:00:00 2001 From: AndrolGenhald Date: Fri, 24 Jun 2022 17:24:34 -0500 Subject: [PATCH 62/88] Infer literal string from encapsed (interpolated) string. --- .../Expression/EncapsulatedStringAnalyzer.php | 24 +++++++++++++++-- src/Psalm/Type/Union.php | 26 +++++++++++++++++++ 2 files changed, 48 insertions(+), 2 deletions(-) diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/EncapsulatedStringAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/EncapsulatedStringAnalyzer.php index a329ba448a2..dc6d8215af4 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/EncapsulatedStringAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/EncapsulatedStringAnalyzer.php @@ -3,6 +3,7 @@ namespace Psalm\Internal\Analyzer\Statements\Expression; use PhpParser; +use PhpParser\Node\Scalar\EncapsedStringPart; use Psalm\CodeLocation; use Psalm\Context; use Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer; @@ -29,6 +30,8 @@ public static function analyze( $all_literals = true; + $literal_string = ""; + foreach ($stmt->parts as $part) { if ($part instanceof PhpParser\Node\Scalar\EncapsedStringPart && $part->value @@ -42,7 +45,7 @@ public static function analyze( $part_type = $statements_analyzer->node_data->getType($part); - if ($part_type) { + if ($part_type !== null) { $casted_part_type = CastAnalyzer::castStringAttempt( $statements_analyzer, $context, @@ -54,6 +57,14 @@ public static function analyze( $all_literals = false; } + if ($literal_string !== null) { + if ($casted_part_type->isSingleLiteral()) { + $literal_string .= $casted_part_type->getSingleLiteral(); + } else { + $literal_string = null; + } + } + if ($statements_analyzer->data_flow_graph && !in_array('TaintedInput', $statements_analyzer->getSuppressedIssues()) ) { @@ -82,11 +93,20 @@ public static function analyze( } } } + } elseif ($part instanceof EncapsedStringPart) { + if ($literal_string !== null) { + $literal_string .= $part->value; + } + } else { + $all_literals = false; + $literal_string = null; } } if ($non_empty) { - if ($all_literals) { + if ($literal_string !== null) { + $new_type = Type::getString($literal_string); + } elseif ($all_literals) { $new_type = new Union([new TNonEmptyNonspecificLiteralString()]); } else { $new_type = new Union([new TNonEmptyString()]); diff --git a/src/Psalm/Type/Union.php b/src/Psalm/Type/Union.php index 16d1682ae61..8e8f7b00c77 100644 --- a/src/Psalm/Type/Union.php +++ b/src/Psalm/Type/Union.php @@ -1329,6 +1329,32 @@ public function hasLiteralValue(): bool || isset($this->types['true']); } + public function isSingleLiteral(): bool + { + return count($this->types) === 1 + && count($this->literal_int_types) + + count($this->literal_string_types) + + count($this->literal_float_types) === 1 + ; + } + + /** + * @return TLiteralInt|TLiteralString|TLiteralFloat + */ + public function getSingleLiteral() + { + if (!$this->isSingleLiteral()) { + throw new InvalidArgumentException("Not a single literal"); + } + + return ($literal = reset($this->literal_int_types)) !== false + ? $literal + : (($literal = reset($this->literal_string_types)) !== false + ? $literal + : reset($this->literal_float_types)) + ; + } + public function hasLiteralString(): bool { return count($this->literal_string_types) > 0; From 2559222f67140378ec32067b0b2f8a957ae4f447 Mon Sep 17 00:00:00 2001 From: AndrolGenhald Date: Fri, 24 Jun 2022 19:22:59 -0500 Subject: [PATCH 63/88] More interpolation and concatenation improvements. --- .../Statements/Expression/AssertionFinder.php | 59 ++++++++----------- .../Expression/BinaryOp/ConcatAnalyzer.php | 52 ++++++++-------- .../Statements/Expression/CastAnalyzer.php | 3 +- .../Expression/EncapsulatedStringAnalyzer.php | 24 +++++--- .../FilterVarReturnTypeProvider.php | 2 +- src/Psalm/Type/Atomic/TFalse.php | 2 + src/Psalm/Type/Atomic/TTrue.php | 2 + src/Psalm/Type/Union.php | 29 +++++++++ tests/BinaryOperationTest.php | 34 +++++++++++ tests/TypeReconciliation/EmptyTest.php | 29 +++++++++ 10 files changed, 165 insertions(+), 71 deletions(-) diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/AssertionFinder.php b/src/Psalm/Internal/Analyzer/Statements/Expression/AssertionFinder.php index e2fdfa395ae..7d4c2808c05 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/AssertionFinder.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/AssertionFinder.php @@ -3,6 +3,7 @@ namespace Psalm\Internal\Analyzer\Statements\Expression; use PhpParser; +use PhpParser\Node\Expr\BinaryOp; use PhpParser\Node\Expr\BinaryOp\Equal; use PhpParser\Node\Expr\BinaryOp\Greater; use PhpParser\Node\Expr\BinaryOp\GreaterOrEqual; @@ -103,6 +104,7 @@ class AssertionFinder 'is_iterable' => ['iterable'], 'is_countable' => ['countable'], ]; + /** * Gets all the type assertions in a conditional * @@ -1499,50 +1501,35 @@ protected static function hasNonEmptyCountEqualityCheck( PhpParser\Node\Expr\BinaryOp $conditional, ?int &$min_count ) { - $left_count = $conditional->left instanceof PhpParser\Node\Expr\FuncCall + if ($conditional->left instanceof PhpParser\Node\Expr\FuncCall && $conditional->left->name instanceof PhpParser\Node\Name && strtolower($conditional->left->name->parts[0]) === 'count' - && $conditional->left->getArgs(); - - $operator_greater_than_or_equal = - $conditional instanceof PhpParser\Node\Expr\BinaryOp\Greater - || $conditional instanceof PhpParser\Node\Expr\BinaryOp\GreaterOrEqual; - - if ($left_count - && $conditional->right instanceof PhpParser\Node\Scalar\LNumber - && $operator_greater_than_or_equal - && $conditional->right->value >= ( - $conditional instanceof PhpParser\Node\Expr\BinaryOp\Greater - ? 0 - : 1 - ) + && $conditional->left->getArgs() + && ($conditional instanceof BinaryOp\Greater || $conditional instanceof BinaryOp\GreaterOrEqual) ) { - $min_count = $conditional->right->value + - ($conditional instanceof PhpParser\Node\Expr\BinaryOp\Greater ? 1 : 0); - - return self::ASSIGNMENT_TO_RIGHT; - } - - $right_count = $conditional->right instanceof PhpParser\Node\Expr\FuncCall + $assignment_to = self::ASSIGNMENT_TO_RIGHT; + $compare_to = $conditional->right; + $comparison_adjustment = $conditional instanceof BinaryOp\Greater ? 1 : 0; + } elseif ($conditional->right instanceof PhpParser\Node\Expr\FuncCall && $conditional->right->name instanceof PhpParser\Node\Name && strtolower($conditional->right->name->parts[0]) === 'count' - && $conditional->right->getArgs(); - - $operator_less_than_or_equal = - $conditional instanceof PhpParser\Node\Expr\BinaryOp\Smaller - || $conditional instanceof PhpParser\Node\Expr\BinaryOp\SmallerOrEqual; + && $conditional->right->getArgs() + && ($conditional instanceof BinaryOp\Smaller || $conditional instanceof BinaryOp\SmallerOrEqual) + ) { + $assignment_to = self::ASSIGNMENT_TO_LEFT; + $compare_to = $conditional->left; + $comparison_adjustment = $conditional instanceof BinaryOp\Smaller ? 1 : 0; + } else { + return false; + } - if ($right_count - && $conditional->left instanceof PhpParser\Node\Scalar\LNumber - && $operator_less_than_or_equal - && $conditional->left->value >= ( - $conditional instanceof PhpParser\Node\Expr\BinaryOp\Smaller ? 0 : 1 - ) + // TODO get node type provider here somehow and check literal ints and int ranges + if ($compare_to instanceof PhpParser\Node\Scalar\LNumber + && $compare_to->value > (-1 * $comparison_adjustment) ) { - $min_count = $conditional->left->value + - ($conditional instanceof PhpParser\Node\Expr\BinaryOp\Smaller ? 1 : 0); + $min_count = $compare_to->value + $comparison_adjustment; - return self::ASSIGNMENT_TO_LEFT; + return $assignment_to; } return false; diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/BinaryOp/ConcatAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/BinaryOp/ConcatAnalyzer.php index fe2ec8b2181..c88b6c30536 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/BinaryOp/ConcatAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/BinaryOp/ConcatAnalyzer.php @@ -28,12 +28,14 @@ use Psalm\Type\Atomic\TFalse; use Psalm\Type\Atomic\TFloat; use Psalm\Type\Atomic\TInt; +use Psalm\Type\Atomic\TLiteralFloat; use Psalm\Type\Atomic\TLiteralInt; use Psalm\Type\Atomic\TLiteralString; use Psalm\Type\Atomic\TLowercaseString; use Psalm\Type\Atomic\TNamedObject; use Psalm\Type\Atomic\TNonEmptyNonspecificLiteralString; use Psalm\Type\Atomic\TNonEmptyString; +use Psalm\Type\Atomic\TNonspecificLiteralInt; use Psalm\Type\Atomic\TNonspecificLiteralString; use Psalm\Type\Atomic\TNull; use Psalm\Type\Atomic\TString; @@ -52,6 +54,8 @@ */ class ConcatAnalyzer { + private const MAX_LITERALS = 64; + /** * @param Union|null $result_type */ @@ -155,39 +159,35 @@ public static function analyze( self::analyzeOperand($statements_analyzer, $left, $left_type, 'Left', $context); self::analyzeOperand($statements_analyzer, $right, $right_type, 'Right', $context); - // If one of the types is a single int or string literal, and the other - // type is all string or int literals, combine them into new literal(s). + // If both types are specific literals, combine them into new literals $literal_concat = false; - if (($left_type->allStringLiterals() || $left_type->allIntLiterals()) - && ($right_type->allStringLiterals() || $right_type->allIntLiterals()) - ) { - $literal_concat = true; - $result_type_parts = []; - - foreach ($left_type->getAtomicTypes() as $left_type_part) { - assert($left_type_part instanceof TLiteralString || $left_type_part instanceof TLiteralInt); - foreach ($right_type->getAtomicTypes() as $right_type_part) { - assert($right_type_part instanceof TLiteralString || $right_type_part instanceof TLiteralInt); - $literal = $left_type_part->value . $right_type_part->value; - if (strlen($literal) >= $config->max_string_length) { - // Literal too long, use non-literal type instead - $literal_concat = false; - break 2; + if ($left_type->allSpecificLiterals() && $right_type->allSpecificLiterals()) { + $left_type_parts = $left_type->getAtomicTypes(); + $right_type_parts = $right_type->getAtomicTypes(); + $combinations = count($left_type_parts) * count($right_type_parts); + if ($combinations < self::MAX_LITERALS) { + $literal_concat = true; + $result_type_parts = []; + + foreach ($left_type->getAtomicTypes() as $left_type_part) { + foreach ($right_type->getAtomicTypes() as $right_type_part) { + $literal = $left_type_part->value . $right_type_part->value; + if (strlen($literal) >= $config->max_string_length) { + // Literal too long, use non-literal type instead + $literal_concat = false; + break 2; + } + + $result_type_parts[] = new TLiteralString($literal); } - - $result_type_parts[] = new TLiteralString($literal); } - } - if (!empty($result_type_parts)) { - if ($literal_concat && count($result_type_parts) < 64) { + if ($literal_concat) { + assert(count($result_type_parts) === $combinations); + assert(count($result_type_parts) !== 0); // #8163 $result_type = new Union($result_type_parts); - } else { - $result_type = new Union([new TNonEmptyNonspecificLiteralString]); } - - return; } } diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/CastAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/CastAnalyzer.php index 021b466ecf3..439ee3bf19e 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/CastAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/CastAnalyzer.php @@ -28,6 +28,7 @@ use Psalm\Type\Atomic\TInt; use Psalm\Type\Atomic\TKeyedArray; use Psalm\Type\Atomic\TList; +use Psalm\Type\Atomic\TLiteralFloat; use Psalm\Type\Atomic\TLiteralInt; use Psalm\Type\Atomic\TLiteralString; use Psalm\Type\Atomic\TMixed; @@ -327,7 +328,7 @@ public static function castStringAttempt( || $atomic_type instanceof TInt || $atomic_type instanceof TNumeric ) { - if ($atomic_type instanceof TLiteralInt) { + if ($atomic_type instanceof TLiteralInt || $atomic_type instanceof TLiteralFloat) { $castable_types[] = new TLiteralString((string) $atomic_type->value); } elseif ($atomic_type instanceof TNonspecificLiteralInt) { $castable_types[] = new TNonspecificLiteralString(); diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/EncapsulatedStringAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/EncapsulatedStringAnalyzer.php index dc6d8215af4..5454f3b0206 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/EncapsulatedStringAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/EncapsulatedStringAnalyzer.php @@ -11,8 +11,12 @@ use Psalm\Internal\DataFlow\DataFlowNode; use Psalm\Plugin\EventHandler\Event\AddRemoveTaintsEvent; use Psalm\Type; +use Psalm\Type\Atomic\TLiteralFloat; +use Psalm\Type\Atomic\TLiteralInt; +use Psalm\Type\Atomic\TLiteralString; use Psalm\Type\Atomic\TNonEmptyNonspecificLiteralString; use Psalm\Type\Atomic\TNonEmptyString; +use Psalm\Type\Atomic\TNonspecificLiteralInt; use Psalm\Type\Union; use function in_array; @@ -33,12 +37,6 @@ public static function analyze( $literal_string = ""; foreach ($stmt->parts as $part) { - if ($part instanceof PhpParser\Node\Scalar\EncapsedStringPart - && $part->value - ) { - $non_empty = true; - } - if (ExpressionAnalyzer::analyze($statements_analyzer, $part, $context) === false) { return false; } @@ -55,11 +53,22 @@ public static function analyze( if (!$casted_part_type->allLiterals()) { $all_literals = false; + } elseif (!$non_empty) { + // Check if all literals are nonempty + foreach ($casted_part_type->getAtomicTypes() as $atomic_literal) { + $non_empty = $atomic_literal instanceof TLiteralInt + || $atomic_literal instanceof TNonspecificLiteralInt + || $atomic_literal instanceof TLiteralFloat + || $atomic_literal instanceof TNonEmptyNonspecificLiteralString + || ($atomic_literal instanceof TLiteralString && $atomic_literal->value !== "") + ; + } } if ($literal_string !== null) { if ($casted_part_type->isSingleLiteral()) { - $literal_string .= $casted_part_type->getSingleLiteral(); + $literal_string .= $casted_part_type->getSingleLiteral()->value; + if (!$non_empty && $literal_string !== "") {} } else { $literal_string = null; } @@ -97,6 +106,7 @@ public static function analyze( if ($literal_string !== null) { $literal_string .= $part->value; } + $non_empty = $non_empty || $part->value !== ""; } else { $all_literals = false; $literal_string = null; diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/FilterVarReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/FilterVarReturnTypeProvider.php index 0e267608a7a..3ebc5e1d276 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/FilterVarReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/FilterVarReturnTypeProvider.php @@ -92,7 +92,7 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev if (isset($atomic_type->properties['options']) && $atomic_type->properties['options']->hasArray() - && ($options_array = $atomic_type->properties['options']->getAtomicTypes()['array']) + && ($options_array = $atomic_type->properties['options']->getAtomicTypes()['array'] ?? null) && $options_array instanceof TKeyedArray && isset($options_array->properties['default']) ) { diff --git a/src/Psalm/Type/Atomic/TFalse.php b/src/Psalm/Type/Atomic/TFalse.php index 5ffd3112f39..50e4a0bfd0c 100644 --- a/src/Psalm/Type/Atomic/TFalse.php +++ b/src/Psalm/Type/Atomic/TFalse.php @@ -7,6 +7,8 @@ */ class TFalse extends TBool { + public bool $value = false; + public function __toString(): string { return 'false'; diff --git a/src/Psalm/Type/Atomic/TTrue.php b/src/Psalm/Type/Atomic/TTrue.php index 29313a0d457..d5f2db43933 100644 --- a/src/Psalm/Type/Atomic/TTrue.php +++ b/src/Psalm/Type/Atomic/TTrue.php @@ -7,6 +7,8 @@ */ class TTrue extends TBool { + public bool $value = true; + public function __toString(): string { return 'true'; diff --git a/src/Psalm/Type/Union.php b/src/Psalm/Type/Union.php index 8e8f7b00c77..6ff34be3156 100644 --- a/src/Psalm/Type/Union.php +++ b/src/Psalm/Type/Union.php @@ -270,6 +270,7 @@ public function replaceTypes(array $types): void } /** + * @psalm-mutation-free * @return non-empty-array */ public function getAtomicTypes(): array @@ -1302,6 +1303,34 @@ public function allIntLiterals(): bool return true; } + /** + * @psalm-assert-if-true array< + * array-key, + * TLiteralString|TLiteralInt|TLiteralFloat|TFalse|TTrue + * > $this->getAtomicTypes() + */ + public function allSpecificLiterals(): bool + { + foreach ($this->types as $atomic_key_type) { + if (!$atomic_key_type instanceof TLiteralString + && !$atomic_key_type instanceof TLiteralInt + && !$atomic_key_type instanceof TLiteralFloat + && !$atomic_key_type instanceof TFalse + && !$atomic_key_type instanceof TTrue + ) { + return false; + } + } + + return true; + } + + /** + * @psalm-assert-if-true array< + * array-key, + * TLiteralString|TLiteralInt|TLiteralFloat|TNonspecificLiteralString|TNonSpecificLiteralInt|TFalse|TTrue + * > $this->getAtomicTypes() + */ public function allLiterals(): bool { foreach ($this->types as $atomic_key_type) { diff --git a/tests/BinaryOperationTest.php b/tests/BinaryOperationTest.php index 087a7f0f448..a1d761e1ae3 100644 --- a/tests/BinaryOperationTest.php +++ b/tests/BinaryOperationTest.php @@ -686,6 +686,40 @@ function foo(string $s1): string { return "Hello $s1 $s2"; }', ], + 'encapsedStringIsInferredAsLiteral' => [ + ' ['$interpolated===' => '"12.3foobar"'], + ], + 'concatenatedStringIsInferredAsLiteral' => [ + ' ['$concatenated===' => '"12.3foobar"'], + ], + 'encapsedNonEmptyNonSpecificLiteralString' => [ + ' ['$interpolated===' => 'non-empty-literal-string'], + ], + 'concatenatedNonEmptyNonSpecificLiteralString' => [ + ' ['$concatenated===' => 'non-empty-literal-string'], + ], 'literalIntConcatCreatesLiteral' => [ ' [ // #8163 + ' */ + $arr = [1]; + assert(count($arr) === $c); + ', + 'assertions' => ['$arr===' => 'non-empty-list'], + ], + 'SKIPPED-countWithIntRange' => [ // #8163 + ' */ + $c = 1; + /** @var list */ + $arr = [1]; + assert(count($arr) === $c); + ', + 'assertions' => ['$arr===' => 'non-empty-list'], + ], + 'SKIPPED-countEmptyWithIntRange' => [ // #8163 + ' */ + $c = 1; + /** @var list */ + $arr = [1]; + assert(count($arr) === $c); + ', + 'assertions' => ['$arr===' => 'list'], + ], ]; } From a804e459b66fa671cca7c02463a235ae65d4f149 Mon Sep 17 00:00:00 2001 From: AndrolGenhald Date: Fri, 24 Jun 2022 19:29:12 -0500 Subject: [PATCH 64/88] PHP 7.1 compatibility. --- src/Psalm/Type/Atomic/TFalse.php | 3 ++- src/Psalm/Type/Atomic/TTrue.php | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Psalm/Type/Atomic/TFalse.php b/src/Psalm/Type/Atomic/TFalse.php index 50e4a0bfd0c..b97abb6985f 100644 --- a/src/Psalm/Type/Atomic/TFalse.php +++ b/src/Psalm/Type/Atomic/TFalse.php @@ -7,7 +7,8 @@ */ class TFalse extends TBool { - public bool $value = false; + /** @var false */ + public $value = false; public function __toString(): string { diff --git a/src/Psalm/Type/Atomic/TTrue.php b/src/Psalm/Type/Atomic/TTrue.php index d5f2db43933..208c70ee5ad 100644 --- a/src/Psalm/Type/Atomic/TTrue.php +++ b/src/Psalm/Type/Atomic/TTrue.php @@ -7,7 +7,8 @@ */ class TTrue extends TBool { - public bool $value = true; + /** @var true */ + public $value = true; public function __toString(): string { From 3aea0987eff68859087be23e9c18b05bfd134ba8 Mon Sep 17 00:00:00 2001 From: AndrolGenhald Date: Fri, 24 Jun 2022 20:28:04 -0500 Subject: [PATCH 65/88] Fix non-empty encapsed string check. --- .../Expression/BinaryOp/ConcatAnalyzer.php | 2 -- .../Expression/EncapsulatedStringAnalyzer.php | 24 ++++++++++++------- tests/BinaryOperationTest.php | 10 ++++++++ 3 files changed, 26 insertions(+), 10 deletions(-) diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/BinaryOp/ConcatAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/BinaryOp/ConcatAnalyzer.php index c88b6c30536..e317de46e75 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/BinaryOp/ConcatAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/BinaryOp/ConcatAnalyzer.php @@ -28,14 +28,12 @@ use Psalm\Type\Atomic\TFalse; use Psalm\Type\Atomic\TFloat; use Psalm\Type\Atomic\TInt; -use Psalm\Type\Atomic\TLiteralFloat; use Psalm\Type\Atomic\TLiteralInt; use Psalm\Type\Atomic\TLiteralString; use Psalm\Type\Atomic\TLowercaseString; use Psalm\Type\Atomic\TNamedObject; use Psalm\Type\Atomic\TNonEmptyNonspecificLiteralString; use Psalm\Type\Atomic\TNonEmptyString; -use Psalm\Type\Atomic\TNonspecificLiteralInt; use Psalm\Type\Atomic\TNonspecificLiteralString; use Psalm\Type\Atomic\TNull; use Psalm\Type\Atomic\TString; diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/EncapsulatedStringAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/EncapsulatedStringAnalyzer.php index 5454f3b0206..0eeca85cc8c 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/EncapsulatedStringAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/EncapsulatedStringAnalyzer.php @@ -17,6 +17,7 @@ use Psalm\Type\Atomic\TNonEmptyNonspecificLiteralString; use Psalm\Type\Atomic\TNonEmptyString; use Psalm\Type\Atomic\TNonspecificLiteralInt; +use Psalm\Type\Atomic\TNonspecificLiteralString; use Psalm\Type\Union; use function in_array; @@ -55,20 +56,23 @@ public static function analyze( $all_literals = false; } elseif (!$non_empty) { // Check if all literals are nonempty + $non_empty = true; foreach ($casted_part_type->getAtomicTypes() as $atomic_literal) { - $non_empty = $atomic_literal instanceof TLiteralInt - || $atomic_literal instanceof TNonspecificLiteralInt - || $atomic_literal instanceof TLiteralFloat - || $atomic_literal instanceof TNonEmptyNonspecificLiteralString - || ($atomic_literal instanceof TLiteralString && $atomic_literal->value !== "") - ; + if (!$atomic_literal instanceof TLiteralInt + && !$atomic_literal instanceof TNonspecificLiteralInt + && !$atomic_literal instanceof TLiteralFloat + && !$atomic_literal instanceof TNonEmptyNonspecificLiteralString + && !($atomic_literal instanceof TLiteralString && $atomic_literal->value !== "") + ) { + $non_empty = false; + break; + } } } if ($literal_string !== null) { if ($casted_part_type->isSingleLiteral()) { $literal_string .= $casted_part_type->getSingleLiteral()->value; - if (!$non_empty && $literal_string !== "") {} } else { $literal_string = null; } @@ -121,7 +125,11 @@ public static function analyze( } else { $new_type = new Union([new TNonEmptyString()]); } - + } elseif ($all_literals) { + $new_type = new Union([new TNonspecificLiteralString()]); + } + if (isset($new_type)) { + assert($new_type instanceof Union); $new_type->parent_nodes = $stmt_type->parent_nodes; $stmt_type = $new_type; } diff --git a/tests/BinaryOperationTest.php b/tests/BinaryOperationTest.php index a1d761e1ae3..241e573e27d 100644 --- a/tests/BinaryOperationTest.php +++ b/tests/BinaryOperationTest.php @@ -720,6 +720,16 @@ function foo(string $s1): string { ', 'assertions' => ['$concatenated===' => 'non-empty-literal-string'], ], + 'encapsedPossiblyEmptyLiteralString' => [ + ' ['$interpolated===' => 'literal-string'], + ], 'literalIntConcatCreatesLiteral' => [ ' Date: Fri, 24 Jun 2022 21:03:33 -0500 Subject: [PATCH 66/88] CS fix. --- .../Statements/Expression/EncapsulatedStringAnalyzer.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/EncapsulatedStringAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/EncapsulatedStringAnalyzer.php index 0eeca85cc8c..d90b9dc3b4a 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/EncapsulatedStringAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/EncapsulatedStringAnalyzer.php @@ -20,6 +20,7 @@ use Psalm\Type\Atomic\TNonspecificLiteralString; use Psalm\Type\Union; +use function assert; use function in_array; class EncapsulatedStringAnalyzer From b671117417578dc4df45642879317b38f1b2a65a Mon Sep 17 00:00:00 2001 From: AndrolGenhald Date: Sat, 25 Jun 2022 02:03:26 -0500 Subject: [PATCH 67/88] Improve @psalm-internal and prevent usage of IssueBuffer::add(). --- src/Psalm/Internal/Analyzer/ClassAnalyzer.php | 14 +- .../Internal/Analyzer/CommentAnalyzer.php | 11 +- src/Psalm/Internal/Analyzer/FileAnalyzer.php | 2 +- .../Analyzer/FunctionLikeAnalyzer.php | 2 +- .../Internal/Analyzer/InterfaceAnalyzer.php | 2 +- .../Internal/Analyzer/NamespaceAnalyzer.php | 120 ++++++++++++++++-- .../Statements/Expression/ArrayAnalyzer.php | 2 +- .../Statements/Expression/AssertionFinder.php | 2 +- .../InstancePropertyAssignmentAnalyzer.php | 9 +- .../ExistingAtomicMethodCallAnalyzer.php | 2 +- .../Method/MethodCallProhibitionAnalyzer.php | 9 +- .../Expression/Call/NewAnalyzer.php | 17 +-- .../StaticMethod/AtomicStaticCallAnalyzer.php | 4 +- .../ExistingAtomicStaticCallAnalyzer.php | 2 +- .../Statements/Expression/CallAnalyzer.php | 8 +- .../Fetch/AtomicPropertyFetchAnalyzer.php | 5 +- .../Fetch/ClassConstFetchAnalyzer.php | 14 +- .../Internal/Analyzer/StatementsAnalyzer.php | 23 ++++ src/Psalm/Internal/Codebase/Populator.php | 10 +- .../Reflector/ClassLikeDocblockParser.php | 10 +- .../Reflector/ClassLikeNodeScanner.php | 29 +++-- .../Reflector/FunctionLikeDocblockParser.php | 10 +- .../Reflector/FunctionLikeDocblockScanner.php | 16 +-- .../Reflector/FunctionLikeNodeScanner.php | 13 +- .../Internal/Provider/StatementsProvider.php | 2 +- .../Scanner/ClassLikeDocblockComment.php | 4 +- src/Psalm/Internal/Scanner/DocblockParser.php | 41 ++++++ .../Scanner/FunctionDocblockComment.php | 4 +- .../Internal/Scanner/VarDocblockComment.php | 4 +- src/Psalm/Issue/InternalClass.php | 23 ++++ src/Psalm/IssueBuffer.php | 6 +- src/Psalm/Storage/ClassLikeStorage.php | 4 +- src/Psalm/Storage/FunctionLikeStorage.php | 4 +- src/Psalm/Storage/PropertyStorage.php | 4 +- tests/InternalAnnotationTest.php | 53 ++++++++ 35 files changed, 347 insertions(+), 138 deletions(-) diff --git a/src/Psalm/Internal/Analyzer/ClassAnalyzer.php b/src/Psalm/Internal/Analyzer/ClassAnalyzer.php index 6895a08727b..cb4e63491ba 100644 --- a/src/Psalm/Internal/Analyzer/ClassAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/ClassAnalyzer.php @@ -135,6 +135,7 @@ public function __construct(PhpParser\Node\Stmt $class, SourceAnalyzer $source, } } + /** @return non-empty-string */ public static function getAnonymousClassName(PhpParser\Node\Stmt\Class_ $class, string $file_path): string { return preg_replace('/[^A-Za-z0-9]/', '_', $file_path) @@ -251,7 +252,7 @@ public function analyze( } foreach ($storage->docblock_issues as $docblock_issue) { - IssueBuffer::add($docblock_issue); + IssueBuffer::maybeAdd($docblock_issue); } $classlike_storage_provider = $codebase->classlike_storage_provider; @@ -1647,7 +1648,7 @@ private function analyzeClassMethod( $config = Config::getInstance(); if ($stmt->stmts === null && !$stmt->isAbstract()) { - IssueBuffer::add( + IssueBuffer::maybeAdd( new ParseError( 'Non-abstract class method must have statements', new CodeLocation($this, $stmt) @@ -1660,7 +1661,7 @@ private function analyzeClassMethod( try { $method_analyzer = new MethodAnalyzer($stmt, $source); } catch (UnexpectedValueException $e) { - IssueBuffer::add( + IssueBuffer::maybeAdd( new ParseError( 'Problem loading method: ' . $e->getMessage(), new CodeLocation($this, $stmt) @@ -2506,11 +2507,12 @@ private function checkParentClass( ); } - if (!NamespaceAnalyzer::isWithin($fq_class_name, $parent_class_storage->internal)) { + if (!NamespaceAnalyzer::isWithinAny($fq_class_name, $parent_class_storage->internal)) { IssueBuffer::maybeAdd( new InternalClass( - $parent_fq_class_name . ' is internal to ' . $parent_class_storage->internal - . ' but called from ' . $fq_class_name, + $parent_fq_class_name . ' is internal to ' + . InternalClass::listToPhrase($parent_class_storage->internal) + . ' but called from ' . $fq_class_name, $code_location, $parent_fq_class_name ), diff --git a/src/Psalm/Internal/Analyzer/CommentAnalyzer.php b/src/Psalm/Internal/Analyzer/CommentAnalyzer.php index 7466e5557ce..5af3a43e8a3 100644 --- a/src/Psalm/Internal/Analyzer/CommentAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/CommentAnalyzer.php @@ -9,6 +9,7 @@ use Psalm\Exception\IncorrectDocblockException; use Psalm\Exception\TypeParseTreeException; use Psalm\FileSource; +use Psalm\Internal\Scanner\DocblockParser; use Psalm\Internal\Scanner\ParsedDocblock; use Psalm\Internal\Scanner\VarDocblockComment; use Psalm\Internal\Type\TypeAlias; @@ -21,7 +22,6 @@ use function preg_match; use function preg_replace; use function preg_split; -use function reset; use function rtrim; use function str_replace; use function strlen; @@ -236,14 +236,7 @@ private static function decorateVarDocblockComment( } } - if (isset($parsed_docblock->tags['psalm-internal'])) { - $psalm_internal = trim(reset($parsed_docblock->tags['psalm-internal'])); - - if (!$psalm_internal) { - throw new DocblockParseException('psalm-internal annotation used without specifying namespace'); - } - - $var_comment->psalm_internal = $psalm_internal; + if (count($var_comment->psalm_internal = DocblockParser::handlePsalmInternal($parsed_docblock)) !== 0) { $var_comment->internal = true; } diff --git a/src/Psalm/Internal/Analyzer/FileAnalyzer.php b/src/Psalm/Internal/Analyzer/FileAnalyzer.php index e59a1790789..0d57d4c24e1 100644 --- a/src/Psalm/Internal/Analyzer/FileAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/FileAnalyzer.php @@ -196,7 +196,7 @@ public function analyze( $statements_analyzer = new StatementsAnalyzer($this, $this->node_data); foreach ($file_storage->docblock_issues as $docblock_issue) { - IssueBuffer::add($docblock_issue); + IssueBuffer::maybeAdd($docblock_issue); } // if there are any leftover statements, evaluate them, diff --git a/src/Psalm/Internal/Analyzer/FunctionLikeAnalyzer.php b/src/Psalm/Internal/Analyzer/FunctionLikeAnalyzer.php index 6e3d216c61e..f9cafde5e92 100644 --- a/src/Psalm/Internal/Analyzer/FunctionLikeAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/FunctionLikeAnalyzer.php @@ -209,7 +209,7 @@ public function analyze( } foreach ($storage->docblock_issues as $docblock_issue) { - IssueBuffer::add($docblock_issue); + IssueBuffer::maybeAdd($docblock_issue); } $function_information = $this->getFunctionInformation( diff --git a/src/Psalm/Internal/Analyzer/InterfaceAnalyzer.php b/src/Psalm/Internal/Analyzer/InterfaceAnalyzer.php index c77781a0006..9c68e2eddab 100644 --- a/src/Psalm/Internal/Analyzer/InterfaceAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/InterfaceAnalyzer.php @@ -133,7 +133,7 @@ public function analyze(): void ); } } elseif ($stmt instanceof PhpParser\Node\Stmt\Property) { - IssueBuffer::add( + IssueBuffer::maybeAdd( new ParseError( 'Interfaces cannot have properties', new CodeLocation($this, $stmt) diff --git a/src/Psalm/Internal/Analyzer/NamespaceAnalyzer.php b/src/Psalm/Internal/Analyzer/NamespaceAnalyzer.php index 436e47bcd7f..06413e6bbbf 100644 --- a/src/Psalm/Internal/Analyzer/NamespaceAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/NamespaceAnalyzer.php @@ -12,11 +12,13 @@ use ReflectionProperty; use UnexpectedValueException; +use function assert; +use function count; use function implode; use function preg_replace; use function strpos; use function strtolower; -use function trim; +use function substr; /** * @internal @@ -152,33 +154,127 @@ public function getFileAnalyzer(): FileAnalyzer } /** - * Returns true if $className is the same as, or starts with $namespace, in a case-insensitive comparison. + * Returns true if $calling_identifier is the same as, or is within with $identifier, in a + * case-insensitive comparison. Identifiers can be namespaces, classlikes, functions, or methods. * + * @psalm-pure + * + * @throws InvalidArgumentException if $identifier is not a valid identifier + */ + public static function isWithin(string $calling_identifier, string $identifier): bool + { + $normalized_calling_ident = self::normalizeIdentifier($calling_identifier); + $normalized_ident = self::normalizeIdentifier($identifier); + + if ($normalized_calling_ident === $normalized_ident) { + return true; + } + + $normalized_calling_ident_parts = self::getIdentifierParts($normalized_calling_ident); + $normalized_ident_parts = self::getIdentifierParts($normalized_ident); + + if (count($normalized_calling_ident_parts) < count($normalized_ident_parts)) { + return false; + } + + for ($i = 0; $i < count($normalized_ident_parts); ++$i) { + if ($normalized_ident_parts[$i] !== $normalized_calling_ident_parts[$i]) { + return false; + } + } + + return true; + } + + /** + * Returns true if $calling_identifier is the same as or is within any identifier + * in $identifiers in a case-insensitive comparison, or if $identifiers is empty. + * Identifiers can be namespaces, classlikes, functions, or methods. * * @psalm-pure + * + * @psalm-assert-if-false !empty $identifiers + * + * @param list $identifiers */ - public static function isWithin(string $calling_namespace, string $namespace): bool + public static function isWithinAny(string $calling_identifier, array $identifiers): bool { - if ($namespace === '') { - return true; // required to prevent a warning from strpos with empty needle in PHP < 8 + if (count($identifiers) === 0) { + return true; } - $calling_namespace = strtolower(trim($calling_namespace, '\\') . '\\'); - $namespace = strtolower(trim($namespace, '\\') . '\\'); + foreach ($identifiers as $identifier) { + if (self::isWithin($calling_identifier, $identifier)) { + return true; + } + } - return $calling_namespace === $namespace - || strpos($calling_namespace, $namespace) === 0; + return false; } /** - * @param string $fullyQualifiedClassName, e.g. '\Psalm\Internal\Analyzer\NamespaceAnalyzer' + * @param non-empty-string $fullyQualifiedClassName, e.g. '\Psalm\Internal\Analyzer\NamespaceAnalyzer' * - * @return string , e.g. 'Psalm' + * @return non-empty-string , e.g. 'Psalm' * * @psalm-pure */ public static function getNameSpaceRoot(string $fullyQualifiedClassName): string { - return preg_replace('/^([^\\\]+).*/', '$1', $fullyQualifiedClassName); + $root_namespace = preg_replace('/^([^\\\]+).*/', '$1', $fullyQualifiedClassName); + if ($root_namespace === "") { + throw new InvalidArgumentException("Invalid classname \"$fullyQualifiedClassName\""); + } + return $root_namespace; + } + + /** + * @return ($lowercase is true ? lowercase-string : string) + * + * @psalm-pure + */ + public static function normalizeIdentifier(string $identifier, bool $lowercase = true): string + { + if ($identifier === "") { + return ""; + } + + $identifier = $identifier[0] === "\\" ? substr($identifier, 1) : $identifier; + return $lowercase ? strtolower($identifier) : $identifier; + } + + /** + * Splits an identifier into parts, eg `Foo\Bar::baz` becomes ["Foo", "\\", "Bar", "::", "baz"]. + * + * @return list + * + * @psalm-pure + */ + public static function getIdentifierParts(string $identifier): array + { + $parts = []; + while (($pos = strpos($identifier, "\\")) !== false) { + if ($pos > 0) { + $part = substr($identifier, 0, $pos); + assert($part !== ""); + $parts[] = $part; + } + $parts[] = "\\"; + $identifier = substr($identifier, $pos + 1); + } + if (($pos = strpos($identifier, "::")) !== false) { + if ($pos > 0) { + $part = substr($identifier, 0, $pos); + assert($part !== ""); + $parts[] = $part; + } + $parts[] = "::"; + $identifier = substr($identifier, $pos + 2); + } + if ($identifier !== "") { + $parts[] = $identifier; + } + + return $parts; } } diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/ArrayAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/ArrayAnalyzer.php index 8ad68bffe30..0a92b1a1cc3 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/ArrayAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/ArrayAnalyzer.php @@ -74,7 +74,7 @@ public static function analyze( foreach ($stmt->items as $item) { if ($item === null) { - IssueBuffer::add( + IssueBuffer::maybeAdd( new ParseError( 'Array element cannot be empty', new CodeLocation($statements_analyzer, $stmt) diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/AssertionFinder.php b/src/Psalm/Internal/Analyzer/Statements/Expression/AssertionFinder.php index e2fdfa395ae..69ae960dfd9 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/AssertionFinder.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/AssertionFinder.php @@ -959,7 +959,7 @@ protected static function processCustomAssertion( } } elseif ($assertion->var_id === '$this') { if (!$expr instanceof PhpParser\Node\Expr\MethodCall) { - IssueBuffer::add( + IssueBuffer::maybeAdd( new InvalidDocblock( 'Assertion of $this can be done only on method of a class', new CodeLocation($source, $expr) diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Assignment/InstancePropertyAssignmentAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Assignment/InstancePropertyAssignmentAnalyzer.php index 12a09c8d7b7..f5ebf74b12e 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Assignment/InstancePropertyAssignmentAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Assignment/InstancePropertyAssignmentAnalyzer.php @@ -37,6 +37,7 @@ use Psalm\Issue\ImplicitToStringCast; use Psalm\Issue\ImpurePropertyAssignment; use Psalm\Issue\InaccessibleProperty; +use Psalm\Issue\InternalClass; use Psalm\Issue\InternalProperty; use Psalm\Issue\InvalidPropertyAssignment; use Psalm\Issue\InvalidPropertyAssignmentValue; @@ -420,7 +421,7 @@ public static function analyzeStatement( foreach ($stmt->props as $prop) { if ($prop->default) { if ($stmt->isReadonly()) { - IssueBuffer::add( + IssueBuffer::maybeAdd( new InvalidPropertyAssignment( 'Readonly property ' . $context->self . '::$' . $prop->name->name . ' cannot have a default', @@ -1258,11 +1259,11 @@ private static function analyzeAtomicAssignment( ); } - if ($context->self && !NamespaceAnalyzer::isWithin($context->self, $property_storage->internal)) { + if ($context->self && !NamespaceAnalyzer::isWithinAny($context->self, $property_storage->internal)) { IssueBuffer::maybeAdd( new InternalProperty( - $property_id . ' is internal to ' . $property_storage->internal - . ' but called from ' . $context->self, + $property_id . ' is internal to ' . InternalClass::listToPhrase($property_storage->internal) + . ' but called from ' . $context->self, new CodeLocation($statements_analyzer->getSource(), $stmt), $property_id ), diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/ExistingAtomicMethodCallAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/ExistingAtomicMethodCallAnalyzer.php index 26faf4e593e..6dee09a63e3 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/ExistingAtomicMethodCallAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/ExistingAtomicMethodCallAnalyzer.php @@ -271,7 +271,7 @@ public static function analyze( $codebase, $context, $method_id, - $statements_analyzer->getNamespace(), + $statements_analyzer->getFullyQualifiedFunctionMethodOrNamespaceName(), $name_code_location, $statements_analyzer->getSuppressedIssues() ); diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/MethodCallProhibitionAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/MethodCallProhibitionAnalyzer.php index a3f4660a6ae..29c8bf22344 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/MethodCallProhibitionAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/MethodCallProhibitionAnalyzer.php @@ -8,6 +8,7 @@ use Psalm\Internal\Analyzer\NamespaceAnalyzer; use Psalm\Internal\MethodIdentifier; use Psalm\Issue\DeprecatedMethod; +use Psalm\Issue\InternalClass; use Psalm\Issue\InternalMethod; use Psalm\IssueBuffer; @@ -22,7 +23,7 @@ public static function analyze( Codebase $codebase, Context $context, MethodIdentifier $method_id, - ?string $namespace, + ?string $caller_identifier, CodeLocation $code_location, array $suppressed_issues ): ?bool { @@ -51,12 +52,12 @@ public static function analyze( if (!$context->collect_initializations && !$context->collect_mutations ) { - if (!NamespaceAnalyzer::isWithin($namespace ?: '', $storage->internal)) { + if (!NamespaceAnalyzer::isWithinAny($caller_identifier ?? "", $storage->internal)) { IssueBuffer::maybeAdd( new InternalMethod( 'The method ' . $codebase_methods->getCasedMethodId($method_id) - . ' is internal to ' . $storage->internal - . ' but called from ' . ($context->self ?: 'root namespace'), + . ' is internal to ' . InternalClass::listToPhrase($storage->internal) + . ' but called from ' . ($caller_identifier ?: 'root namespace'), $code_location, (string) $method_id ), diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/NewAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/NewAnalyzer.php index 2f9548c4195..863c38ec099 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/NewAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/NewAnalyzer.php @@ -349,12 +349,12 @@ private static function analyzeNamedConstructor( if ($context->self && !$context->collect_initializations && !$context->collect_mutations - && !NamespaceAnalyzer::isWithin($context->self, $storage->internal) + && !NamespaceAnalyzer::isWithinAny($context->self, $storage->internal) ) { IssueBuffer::maybeAdd( new InternalClass( - $fq_class_name . ' is internal to ' . $storage->internal - . ' but called from ' . $context->self, + $fq_class_name . ' is internal to ' . InternalClass::listToPhrase($storage->internal) + . ' but called from ' . $context->self, new CodeLocation($statements_analyzer->getSource(), $stmt), $fq_class_name ), @@ -411,16 +411,13 @@ private static function analyzeNamedConstructor( if ($declaring_method_id) { $method_storage = $codebase->methods->getStorage($declaring_method_id); - $namespace = $statements_analyzer->getNamespace() ?: ''; - if (!NamespaceAnalyzer::isWithin( - $namespace, - $method_storage->internal - )) { + $caller_identifier = $statements_analyzer->getFullyQualifiedFunctionMethodOrNamespaceName() ?: ''; + if (!NamespaceAnalyzer::isWithinAny($caller_identifier, $method_storage->internal)) { IssueBuffer::maybeAdd( new InternalMethod( 'Constructor ' . $codebase->methods->getCasedMethodId($declaring_method_id) - . ' is internal to ' . $method_storage->internal - . ' but called from ' . ($namespace ?: 'root namespace'), + . ' is internal to ' . InternalClass::listToPhrase($method_storage->internal) + . ' but called from ' . ($caller_identifier ?: 'root namespace'), new CodeLocation($statements_analyzer, $stmt), (string) $method_id ), diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/StaticMethod/AtomicStaticCallAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/StaticMethod/AtomicStaticCallAnalyzer.php index 4e85c2c0f70..673dc56c505 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/StaticMethod/AtomicStaticCallAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/StaticMethod/AtomicStaticCallAnalyzer.php @@ -792,10 +792,10 @@ function (PhpParser\Node\Arg $arg): PhpParser\Node\Expr\ArrayItem { ); } - if ($context->self && ! NamespaceAnalyzer::isWithin($context->self, $class_storage->internal)) { + if ($context->self && ! NamespaceAnalyzer::isWithinAny($context->self, $class_storage->internal)) { IssueBuffer::maybeAdd( new InternalClass( - $fq_class_name . ' is internal to ' . $class_storage->internal + $fq_class_name . ' is internal to ' . InternalClass::listToPhrase($class_storage->internal) . ' but called from ' . $context->self, new CodeLocation($statements_analyzer->getSource(), $stmt), $fq_class_name diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/StaticMethod/ExistingAtomicStaticCallAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/StaticMethod/ExistingAtomicStaticCallAnalyzer.php index 6951396f72a..7855200e95e 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/StaticMethod/ExistingAtomicStaticCallAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/StaticMethod/ExistingAtomicStaticCallAnalyzer.php @@ -71,7 +71,7 @@ public static function analyze( $codebase, $context, $method_id, - $statements_analyzer->getNamespace(), + $statements_analyzer->getFullyQualifiedFunctionMethodOrNamespaceName(), new CodeLocation($statements_analyzer->getSource(), $stmt), $statements_analyzer->getSuppressedIssues() ) === false) { diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/CallAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/CallAnalyzer.php index 7d898f9d31d..9cc9d85098e 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/CallAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/CallAnalyzer.php @@ -693,7 +693,7 @@ public static function applyAssertionsToContext( $exploded = explode('->', $assertion->var_id); if (count($exploded) < 2) { - IssueBuffer::add( + IssueBuffer::maybeAdd( new InvalidDocblock( 'Assert notation is malformed', new CodeLocation($statements_analyzer, $expr) @@ -707,7 +707,7 @@ public static function applyAssertionsToContext( $var_id = is_numeric($var_id) ? (int) $var_id : $var_id; if (!is_int($var_id) || !isset($args[$var_id])) { - IssueBuffer::add( + IssueBuffer::maybeAdd( new InvalidDocblock( 'Variable ' . $var_id . ' is not an argument so cannot be asserted', new CodeLocation($statements_analyzer, $expr) @@ -722,7 +722,7 @@ public static function applyAssertionsToContext( $arg_var_id = ExpressionIdentifier::getArrayVarId($arg_value, null, $statements_analyzer); if (!$arg_var_id) { - IssueBuffer::add( + IssueBuffer::maybeAdd( new InvalidDocblock( 'Variable being asserted as argument ' . ($var_id+1) . ' cannot be found in local scope', new CodeLocation($statements_analyzer, $expr) @@ -740,7 +740,7 @@ public static function applyAssertionsToContext( ); if (null !== $failedMessage) { - IssueBuffer::add( + IssueBuffer::maybeAdd( new InvalidDocblock($failedMessage, new CodeLocation($statements_analyzer, $expr)) ); continue; diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/AtomicPropertyFetchAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/AtomicPropertyFetchAnalyzer.php index afa84a3d7e4..b34e983ed32 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/AtomicPropertyFetchAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/AtomicPropertyFetchAnalyzer.php @@ -29,6 +29,7 @@ use Psalm\Internal\Type\TypeExpander; use Psalm\Issue\DeprecatedProperty; use Psalm\Issue\ImpurePropertyFetch; +use Psalm\Issue\InternalClass; use Psalm\Issue\InternalProperty; use Psalm\Issue\MissingPropertyType; use Psalm\Issue\NoInterfaceProperties; @@ -405,10 +406,10 @@ public static function analyze( $property_storage = $declaring_class_storage->properties[$prop_name]; - if ($context->self && !NamespaceAnalyzer::isWithin($context->self, $property_storage->internal)) { + if ($context->self && !NamespaceAnalyzer::isWithinAny($context->self, $property_storage->internal)) { IssueBuffer::maybeAdd( new InternalProperty( - $property_id . ' is internal to ' . $property_storage->internal + $property_id . ' is internal to ' . InternalClass::listToPhrase($property_storage->internal) . ' but called from ' . $context->self, new CodeLocation($statements_analyzer->getSource(), $stmt), $property_id diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/ClassConstFetchAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/ClassConstFetchAnalyzer.php index e6cb06ca161..8ed56437729 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/ClassConstFetchAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/ClassConstFetchAnalyzer.php @@ -343,12 +343,12 @@ public static function analyze( if ($context->self && !$context->collect_initializations && !$context->collect_mutations - && $const_class_storage->internal - && !NamespaceAnalyzer::isWithin($context->self, $const_class_storage->internal) + && !NamespaceAnalyzer::isWithinAny($context->self, $const_class_storage->internal) ) { IssueBuffer::maybeAdd( new InternalClass( - $fq_class_name . ' is internal to ' . $const_class_storage->internal + $fq_class_name . ' is internal to ' + . InternalClass::listToPhrase($const_class_storage->internal) . ' but called from ' . $context->self, new CodeLocation($statements_analyzer->getSource(), $stmt), $fq_class_name @@ -619,13 +619,13 @@ public static function analyze( if ($context->self && !$context->collect_initializations && !$context->collect_mutations - && $const_class_storage->internal - && !NamespaceAnalyzer::isWithin($context->self, $const_class_storage->internal) + && !NamespaceAnalyzer::isWithinAny($context->self, $const_class_storage->internal) ) { IssueBuffer::maybeAdd( new InternalClass( - $fq_class_name . ' is internal to ' . $const_class_storage->internal - . ' but called from ' . $context->self, + $fq_class_name . ' is internal to ' + . InternalClass::listToPhrase($const_class_storage->internal) + . ' but called from ' . $context->self, new CodeLocation($statements_analyzer->getSource(), $stmt), $fq_class_name ), diff --git a/src/Psalm/Internal/Analyzer/StatementsAnalyzer.php b/src/Psalm/Internal/Analyzer/StatementsAnalyzer.php index 3c3e040c1e7..dff3d60d7ac 100644 --- a/src/Psalm/Internal/Analyzer/StatementsAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/StatementsAnalyzer.php @@ -65,6 +65,7 @@ use function array_keys; use function array_merge; use function array_search; +use function assert; use function count; use function fwrite; use function get_class; @@ -1050,4 +1051,26 @@ public function getNodeTypeProvider(): NodeTypeProvider { return $this->node_data; } + + public function getFullyQualifiedFunctionMethodOrNamespaceName(): ?string + { + if ($this->source instanceof MethodAnalyzer) { + $fqcn = $this->getFQCLN(); + $method_name = $this->source->getFunctionLikeStorage($this)->cased_name; + assert($fqcn !== null && $method_name !== null); + + return "$fqcn::$method_name"; + } + + if ($this->source instanceof FunctionAnalyzer) { + $namespace = $this->getNamespace(); + $namespace = $namespace === "" ? "" : "$namespace\\"; + $function_name = $this->source->getFunctionLikeStorage($this)->cased_name; + assert($function_name !== null); + + return "{$namespace}{$function_name}"; + } + + return $this->getNamespace(); + } } diff --git a/src/Psalm/Internal/Codebase/Populator.php b/src/Psalm/Internal/Codebase/Populator.php index 09f672854ef..9d8328249c9 100644 --- a/src/Psalm/Internal/Codebase/Populator.php +++ b/src/Psalm/Internal/Codebase/Populator.php @@ -29,7 +29,6 @@ use function count; use function in_array; use function reset; -use function strlen; use function strpos; use function strtolower; @@ -258,16 +257,11 @@ private function populateClassLikeStorage(ClassLikeStorage $storage, array $depe if (!$storage->is_interface && !$storage->is_trait) { foreach ($storage->methods as $method) { - if (strlen($storage->internal) > strlen($method->internal)) { - $method->internal = $storage->internal; - } + $method->internal = array_merge($storage->internal, $method->internal); } - foreach ($storage->properties as $property) { - if (strlen($storage->internal) > strlen($property->internal)) { - $property->internal = $storage->internal; - } + $property->internal = array_merge($storage->internal, $property->internal); } } diff --git a/src/Psalm/Internal/PhpVisitor/Reflector/ClassLikeDocblockParser.php b/src/Psalm/Internal/PhpVisitor/Reflector/ClassLikeDocblockParser.php index d2bb03ce945..4572df37da3 100644 --- a/src/Psalm/Internal/PhpVisitor/Reflector/ClassLikeDocblockParser.php +++ b/src/Psalm/Internal/PhpVisitor/Reflector/ClassLikeDocblockParser.php @@ -16,6 +16,7 @@ use Psalm\Internal\Analyzer\ProjectAnalyzer; use Psalm\Internal\Provider\StatementsProvider; use Psalm\Internal\Scanner\ClassLikeDocblockComment; +use Psalm\Internal\Scanner\DocblockParser; use Psalm\Internal\Type\ParseTree\MethodParamTree; use Psalm\Internal\Type\ParseTree\MethodTree; use Psalm\Internal\Type\ParseTree\MethodWithReturnTypeTree; @@ -212,14 +213,7 @@ public static function parse( $info->consistent_templates = true; } - if (isset($parsed_docblock->tags['psalm-internal'])) { - $psalm_internal = trim(reset($parsed_docblock->tags['psalm-internal'])); - - if (!$psalm_internal) { - throw new DocblockParseException('psalm-internal annotation used without specifying namespace'); - } - - $info->psalm_internal = $psalm_internal; + if (count($info->psalm_internal = DocblockParser::handlePsalmInternal($parsed_docblock)) !== 0) { $info->internal = true; } diff --git a/src/Psalm/Internal/PhpVisitor/Reflector/ClassLikeNodeScanner.php b/src/Psalm/Internal/PhpVisitor/Reflector/ClassLikeNodeScanner.php index d2b0ed25fa8..91fbfbfee14 100644 --- a/src/Psalm/Internal/PhpVisitor/Reflector/ClassLikeNodeScanner.php +++ b/src/Psalm/Internal/PhpVisitor/Reflector/ClassLikeNodeScanner.php @@ -69,8 +69,8 @@ use function array_pop; use function array_shift; use function array_values; +use function assert; use function count; -use function explode; use function get_class; use function implode; use function preg_match; @@ -182,6 +182,7 @@ public function start(PhpParser\Node\Stmt\ClassLike $node): ?bool $fq_classlike_name = ($this->aliases->namespace ? $this->aliases->namespace . '\\' : '') . $node->name->name; + assert($fq_classlike_name !== ""); $fq_classlike_name_lc = strtolower($fq_classlike_name); @@ -251,7 +252,7 @@ public function start(PhpParser\Node\Stmt\ClassLike $node): ?bool && isset($this->aliases->uses[strtolower($class_name)]) && $this->aliases->uses[strtolower($class_name)] !== $fq_classlike_name ) { - IssueBuffer::add( + IssueBuffer::maybeAdd( new ParseError( 'Class name ' . $class_name . ' clashes with a use statement alias', $name_location ?? $class_location @@ -616,13 +617,10 @@ function (array $l, array $r): int { $storage->deprecated = $docblock_info->deprecated; - if ($docblock_info->internal - && !$docblock_info->psalm_internal - && $this->aliases->namespace - ) { - $storage->internal = explode('\\', $this->aliases->namespace)[0]; - } else { - $storage->internal = $docblock_info->psalm_internal ?? ''; + if (count($docblock_info->psalm_internal) !== 0) { + $storage->internal = $docblock_info->psalm_internal; + } elseif ($docblock_info->internal && $this->aliases->namespace) { + $storage->internal = [NamespaceAnalyzer::getNameSpaceRoot($this->aliases->namespace)]; } if ($docblock_info->final && !$storage->final) { @@ -738,7 +736,7 @@ function (array $l, array $r): int { } if ($attribute->fq_class_name === 'Psalm\\Internal' && !$storage->internal) { - $storage->internal = NamespaceAnalyzer::getNameSpaceRoot($fq_classlike_name); + $storage->internal = [NamespaceAnalyzer::getNameSpaceRoot($fq_classlike_name)]; } if ($attribute->fq_class_name === 'Psalm\\Immutable' @@ -1436,6 +1434,9 @@ private function getAttributeStorageFromStatement( return $storages; } + /** + * @param non-empty-string $fq_classlike_name + */ private function visitPropertyDeclaration( PhpParser\Node\Stmt\Property $stmt, Config $config, @@ -1534,9 +1535,9 @@ private function visitPropertyDeclaration( $property_storage->has_default = (bool)$property->default; $property_storage->deprecated = $var_comment ? $var_comment->deprecated : false; $property_storage->suppressed_issues = $var_comment ? $var_comment->suppressed_issues : []; - $property_storage->internal = $var_comment ? $var_comment->psalm_internal ?? '' : ''; - if (! $property_storage->internal && $var_comment && $var_comment->internal) { - $property_storage->internal = NamespaceAnalyzer::getNameSpaceRoot($fq_classlike_name); + $property_storage->internal = $var_comment ? $var_comment->psalm_internal : []; + if (count($property_storage->internal) === 0 && $var_comment && $var_comment->internal) { + $property_storage->internal = [NamespaceAnalyzer::getNameSpaceRoot($fq_classlike_name)]; } $property_storage->readonly = $stmt->isReadonly() || ($var_comment && $var_comment->readonly); $property_storage->allow_private_mutation = $var_comment ? $var_comment->allow_private_mutation : false; @@ -1648,7 +1649,7 @@ private function visitPropertyDeclaration( } if ($attribute->fq_class_name === 'Psalm\\Internal' && !$property_storage->internal) { - $property_storage->internal = NamespaceAnalyzer::getNameSpaceRoot($fq_classlike_name); + $property_storage->internal = [NamespaceAnalyzer::getNameSpaceRoot($fq_classlike_name)]; } if ($attribute->fq_class_name === 'Psalm\\Readonly') { diff --git a/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeDocblockParser.php b/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeDocblockParser.php index bb11e8b99a4..341d0f63918 100644 --- a/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeDocblockParser.php +++ b/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeDocblockParser.php @@ -8,6 +8,7 @@ use Psalm\Exception\DocblockParseException; use Psalm\Exception\IncorrectDocblockException; use Psalm\Internal\Analyzer\CommentAnalyzer; +use Psalm\Internal\Scanner\DocblockParser; use Psalm\Internal\Scanner\FunctionDocblockComment; use Psalm\Internal\Scanner\ParsedDocblock; use Psalm\Issue\InvalidDocblock; @@ -381,14 +382,7 @@ public static function parse( $info->internal = true; } - if (isset($parsed_docblock->tags['psalm-internal'])) { - $psalm_internal = trim(reset($parsed_docblock->tags['psalm-internal'])); - - if (!$psalm_internal) { - throw new DocblockParseException('@psalm-internal annotation used without specifying namespace'); - } - - $info->psalm_internal = $psalm_internal; + if (count($info->psalm_internal = DocblockParser::handlePsalmInternal($parsed_docblock)) !== 0) { $info->internal = true; } diff --git a/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeDocblockScanner.php b/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeDocblockScanner.php index e80c441ee82..d78bff90c63 100644 --- a/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeDocblockScanner.php +++ b/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeDocblockScanner.php @@ -10,6 +10,7 @@ use Psalm\Config; use Psalm\Exception\InvalidMethodOverrideException; use Psalm\Exception\TypeParseTreeException; +use Psalm\Internal\Analyzer\NamespaceAnalyzer; use Psalm\Internal\Scanner\FileScanner; use Psalm\Internal\Scanner\FunctionDocblockComment; use Psalm\Internal\Type\Comparator\UnionTypeComparator; @@ -96,17 +97,10 @@ public static function addDocblockInfo( $storage->deprecated = true; } - if ($docblock_info->internal - && !$docblock_info->psalm_internal - && $aliases->namespace - ) { - $storage->internal = explode('\\', $aliases->namespace)[0]; - } elseif (!$classlike_storage - || ($docblock_info->psalm_internal - && strlen($docblock_info->psalm_internal) > strlen($classlike_storage->internal) - ) - ) { - $storage->internal = $docblock_info->psalm_internal ?? ''; + if (count($docblock_info->psalm_internal) !== 0) { + $storage->internal = $docblock_info->psalm_internal; + } elseif ($docblock_info->internal && $aliases->namespace) { + $storage->internal = [NamespaceAnalyzer::getNameSpaceRoot($aliases->namespace)]; } if (($storage->internal || ($classlike_storage && $classlike_storage->internal)) diff --git a/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeNodeScanner.php b/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeNodeScanner.php index bb654dc77b8..969633e2e54 100644 --- a/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeNodeScanner.php +++ b/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeNodeScanner.php @@ -51,6 +51,7 @@ use UnexpectedValueException; use function array_keys; +use function array_merge; use function array_pop; use function array_search; use function count; @@ -60,7 +61,6 @@ use function in_array; use function is_string; use function spl_object_id; -use function strlen; use function strpos; use function strtolower; @@ -458,11 +458,8 @@ public function start(PhpParser\Node\FunctionLike $stmt, bool $fake_method = fal $doc_comment = $stmt->getDocComment(); - if ($classlike_storage - && !$classlike_storage->is_trait - && strlen($classlike_storage->internal) > strlen($storage->internal) - ) { - $storage->internal = $classlike_storage->internal; + if ($classlike_storage && !$classlike_storage->is_trait) { + $storage->internal = array_merge($classlike_storage->internal, $storage->internal); } if ($doc_comment) { @@ -594,7 +591,7 @@ public function start(PhpParser\Node\FunctionLike $stmt, bool $fake_method = fal } if (isset($classlike_storage->properties[$param_storage->name]) && $param_storage->location) { - IssueBuffer::add( + IssueBuffer::maybeAdd( new ParseError( 'Promoted property ' . $param_storage->name . ' clashes with an existing property', $param_storage->location @@ -722,7 +719,7 @@ public function start(PhpParser\Node\FunctionLike $stmt, bool $fake_method = fal } if ($attribute->fq_class_name === 'Psalm\\Internal' && !$storage->internal && $fq_classlike_name) { - $storage->internal = NamespaceAnalyzer::getNameSpaceRoot($fq_classlike_name); + $storage->internal = [NamespaceAnalyzer::getNameSpaceRoot($fq_classlike_name)]; } if ($attribute->fq_class_name === 'Psalm\\ExternalMutationFree' diff --git a/src/Psalm/Internal/Provider/StatementsProvider.php b/src/Psalm/Internal/Provider/StatementsProvider.php index 57456f6e5e2..d0b255e4e6e 100644 --- a/src/Psalm/Internal/Provider/StatementsProvider.php +++ b/src/Psalm/Internal/Provider/StatementsProvider.php @@ -498,7 +498,7 @@ public static function parseStatements( foreach ($error_handler->getErrors() as $error) { if ($error->hasColumnInfo()) { - IssueBuffer::add( + IssueBuffer::maybeAdd( new ParseError( $error->getMessage(), new ParseErrorLocation( diff --git a/src/Psalm/Internal/Scanner/ClassLikeDocblockComment.php b/src/Psalm/Internal/Scanner/ClassLikeDocblockComment.php index b91edd73e9a..a9168cc39fd 100644 --- a/src/Psalm/Internal/Scanner/ClassLikeDocblockComment.php +++ b/src/Psalm/Internal/Scanner/ClassLikeDocblockComment.php @@ -33,9 +33,9 @@ class ClassLikeDocblockComment /** * If set, the class is internal to the given namespace. * - * @var null|string + * @var list */ - public $psalm_internal; + public $psalm_internal = []; /** * @var string[] diff --git a/src/Psalm/Internal/Scanner/DocblockParser.php b/src/Psalm/Internal/Scanner/DocblockParser.php index 416f487e5a2..2ddf134e62c 100644 --- a/src/Psalm/Internal/Scanner/DocblockParser.php +++ b/src/Psalm/Internal/Scanner/DocblockParser.php @@ -2,8 +2,16 @@ namespace Psalm\Internal\Scanner; +use Psalm\Exception\DocblockParseException; + +use function array_filter; +use function array_map; +use function array_values; +use function assert; +use function count; use function explode; use function implode; +use function is_string; use function min; use function preg_match; use function preg_replace; @@ -256,4 +264,37 @@ private static function resolveTags(ParsedDocblock $docblock): void + ($docblock->tags['psalm-param-out'] ?? []); } } + + /** + * @return list + * @throws DocblockParseException when a @psalm-internal tag doesn't include a namespace + */ + public static function handlePsalmInternal(ParsedDocblock $parsed_docblock): array + { + if (isset($parsed_docblock->tags['psalm-internal'])) { + $psalm_internal = array_map("trim", $parsed_docblock->tags['psalm-internal']); + + if (count($psalm_internal) !== count(array_filter($psalm_internal))) { + throw new DocblockParseException('psalm-internal annotation used without specifying namespace'); + } + // assert($psalm_internal === array_filter($psalm_internal)); // TODO get this to work + assert(self::assertArrayOfNonEmptyString($psalm_internal)); + + return array_values($psalm_internal); + } + + return []; + } + + /** @psalm-assert-if-true array $arr */ + private static function assertArrayOfNonEmptyString(array $arr): bool + { + foreach ($arr as $val) { + if (!is_string($val) || $val === "") { + return false; + } + } + + return true; + } } diff --git a/src/Psalm/Internal/Scanner/FunctionDocblockComment.php b/src/Psalm/Internal/Scanner/FunctionDocblockComment.php index 89e41dbb8fd..a3819b4acf7 100644 --- a/src/Psalm/Internal/Scanner/FunctionDocblockComment.php +++ b/src/Psalm/Internal/Scanner/FunctionDocblockComment.php @@ -77,9 +77,9 @@ class FunctionDocblockComment /** * If set, the function is internal to the given namespace. * - * @var null|string + * @var list */ - public $psalm_internal; + public $psalm_internal = []; /** * Whether or not the function is internal diff --git a/src/Psalm/Internal/Scanner/VarDocblockComment.php b/src/Psalm/Internal/Scanner/VarDocblockComment.php index 7c38a339be1..8797530a664 100644 --- a/src/Psalm/Internal/Scanner/VarDocblockComment.php +++ b/src/Psalm/Internal/Scanner/VarDocblockComment.php @@ -51,9 +51,9 @@ class VarDocblockComment /** * If set, the property is internal to the given namespace. * - * @var null|string + * @var list */ - public $psalm_internal; + public $psalm_internal = []; /** * Whether or not the property is readonly diff --git a/src/Psalm/Issue/InternalClass.php b/src/Psalm/Issue/InternalClass.php index 26e2df0b64f..8062f1df436 100644 --- a/src/Psalm/Issue/InternalClass.php +++ b/src/Psalm/Issue/InternalClass.php @@ -2,8 +2,31 @@ namespace Psalm\Issue; +use function array_pop; +use function count; +use function implode; +use function reset; + class InternalClass extends ClassIssue { public const ERROR_LEVEL = 4; public const SHORTCODE = 174; + + /** @param non-empty-list $words */ + public static function listToPhrase(array $words): string + { + if (count($words) === 1) { + return reset($words); + } + + if (count($words) === 2) { + return implode(" and ", $words); + } + + $last_word = array_pop($words); + $phrase = implode(", ", $words); + $phrase = "$phrase, and $last_word"; + + return $phrase; + } } diff --git a/src/Psalm/IssueBuffer.php b/src/Psalm/IssueBuffer.php index 5c666574ffe..0fea8f035fc 100644 --- a/src/Psalm/IssueBuffer.php +++ b/src/Psalm/IssueBuffer.php @@ -244,7 +244,11 @@ public static function isSuppressed(CodeIssue $e, array $suppressed_issues = []) } /** - * Add an issue to be emitted + * Add an issue to be emitted. This method should normally not be used! Use IssueBuffer::maybeAdd instead. + * + * @psalm-internal Psalm\IssueBuffer + * @psalm-internal Psalm\Type\Reconciler::getValueForKey + * * @throws CodeException */ public static function add(CodeIssue $e, bool $is_fixable = false): bool diff --git a/src/Psalm/Storage/ClassLikeStorage.php b/src/Psalm/Storage/ClassLikeStorage.php index 01b36968975..a5252d8aaae 100644 --- a/src/Psalm/Storage/ClassLikeStorage.php +++ b/src/Psalm/Storage/ClassLikeStorage.php @@ -45,9 +45,9 @@ class ClassLikeStorage implements HasAttributesInterface public $deprecated = false; /** - * @var string + * @var list */ - public $internal = ''; + public $internal = []; /** * @var TTemplateParam[] diff --git a/src/Psalm/Storage/FunctionLikeStorage.php b/src/Psalm/Storage/FunctionLikeStorage.php index aba5fc68b36..fe23ea05059 100644 --- a/src/Psalm/Storage/FunctionLikeStorage.php +++ b/src/Psalm/Storage/FunctionLikeStorage.php @@ -74,9 +74,9 @@ abstract class FunctionLikeStorage implements HasAttributesInterface public $deprecated; /** - * @var string + * @var list */ - public $internal = ''; + public $internal = []; /** * @var bool diff --git a/src/Psalm/Storage/PropertyStorage.php b/src/Psalm/Storage/PropertyStorage.php index cbe8bf6c86c..b309a7f1356 100644 --- a/src/Psalm/Storage/PropertyStorage.php +++ b/src/Psalm/Storage/PropertyStorage.php @@ -78,9 +78,9 @@ class PropertyStorage implements HasAttributesInterface public $allow_private_mutation = false; /** - * @var string + * @var list */ - public $internal = ''; + public $internal = []; /** * @var ?string diff --git a/tests/InternalAnnotationTest.php b/tests/InternalAnnotationTest.php index dc6047713c2..604b6516d54 100644 --- a/tests/InternalAnnotationTest.php +++ b/tests/InternalAnnotationTest.php @@ -556,6 +556,59 @@ public function batBat() : void { } }', ], + 'psalmInternalMultipleNamespaces' => [ + ' [ + 'bar(); + } + } + } + ' + ], ]; } From e751a27eaf02f35f81e23bae236c3017d31eec40 Mon Sep 17 00:00:00 2001 From: AndrolGenhald Date: Sun, 26 Jun 2022 06:27:46 -0500 Subject: [PATCH 68/88] Fix crash when redefining method with fewer params (fixes #8141). --- .../Analyzer/FunctionLikeAnalyzer.php | 20 ++++++++++--------- tests/AttributeTest.php | 10 ++++++++++ 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/src/Psalm/Internal/Analyzer/FunctionLikeAnalyzer.php b/src/Psalm/Internal/Analyzer/FunctionLikeAnalyzer.php index 6e3d216c61e..d60a888f920 100644 --- a/src/Psalm/Internal/Analyzer/FunctionLikeAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/FunctionLikeAnalyzer.php @@ -1267,15 +1267,17 @@ private function processParams( $context->hasVariable('$' . $function_param->name); } - AttributesAnalyzer::analyze( - $this, - $context, - $function_param, - $param_stmts[$offset]->attrGroups, - AttributesAnalyzer::TARGET_PARAMETER - | ($function_param->promoted_property ? AttributesAnalyzer::TARGET_PROPERTY : 0), - $storage->suppressed_issues + $this->getSuppressedIssues() - ); + if (count($param_stmts) === count($params)) { + AttributesAnalyzer::analyze( + $this, + $context, + $function_param, + $param_stmts[$offset]->attrGroups, + AttributesAnalyzer::TARGET_PARAMETER + | ($function_param->promoted_property ? AttributesAnalyzer::TARGET_PROPERTY : 0), + $storage->suppressed_issues + $this->getSuppressedIssues() + ); + } } return $check_stmts; diff --git a/tests/AttributeTest.php b/tests/AttributeTest.php index 4931a67b435..efffdbad4de 100644 --- a/tests/AttributeTest.php +++ b/tests/AttributeTest.php @@ -391,6 +391,16 @@ class Foo {} class Bar {} ', ], + 'dontCrashWhenRedefiningStubbedMethodWithFewerParams' => [ + ' Date: Mon, 27 Jun 2022 23:41:51 +0200 Subject: [PATCH 69/88] Stop using deprecated string interpolation syntax in StatementsProvider.php MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This fixed the following PHP 8.2 deprecation: > Uncaught RuntimeException: PHP Error: Using ${var} in strings is deprecated, use {$var} instead in …/vimeo/psalm/src/Psalm/Internal/Provider/StatementsProvider.php:140 see: https://wiki.php.net/rfc/deprecate_dollar_brace_string_interpolation --- src/Psalm/Internal/Provider/StatementsProvider.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Psalm/Internal/Provider/StatementsProvider.php b/src/Psalm/Internal/Provider/StatementsProvider.php index d0b255e4e6e..431cb1585d4 100644 --- a/src/Psalm/Internal/Provider/StatementsProvider.php +++ b/src/Psalm/Internal/Provider/StatementsProvider.php @@ -137,7 +137,7 @@ public function getStatementsForFile(string $file_path, string $php_version, ?Pr if (!$this->parser_cache_provider || (!$config->isInProjectDirs($file_path) && strpos($file_path, 'vendor')) ) { - $cache_key = "${file_content_hash}:${php_version}"; + $cache_key = "{$file_content_hash}:{$php_version}"; if ($this->statements_volatile_cache->has($cache_key)) { return $this->statements_volatile_cache->get($cache_key); } From bd50c4e7b011af192f1b77e34e1aefd8452276bf Mon Sep 17 00:00:00 2001 From: naveen <172697+naveensrinivasan@users.noreply.github.com> Date: Tue, 28 Jun 2022 00:29:16 +0000 Subject: [PATCH 70/88] chore: Set permissions for GitHub actions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Restrict the GitHub token permissions only to the required ones; this way, even if the attackers will succeed in compromising your workflow, they won’t be able to do much. - Included permissions for the action. https://github.com/ossf/scorecard/blob/main/docs/checks.md#token-permissions https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#permissions https://docs.github.com/en/actions/using-jobs/assigning-permissions-to-jobs [Keeping your GitHub Actions and workflows secure Part 1: Preventing pwn requests](https://securitylab.github.com/research/github-actions-preventing-pwn-requests/) Signed-off-by: naveen <172697+naveensrinivasan@users.noreply.github.com> --- .github/workflows/ci.yml | 5 +++++ .github/workflows/shepherd.yml | 3 +++ .github/workflows/windows-ci.yml | 5 +++++ 3 files changed, 13 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a1347724da6..4dcae6d29b5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,6 +1,9 @@ name: Run unit tests on: [push, pull_request] +permissions: + contents: read + jobs: lint: name: Check PHP syntax @@ -42,6 +45,8 @@ jobs: - run: | git ls-files | grep \\\.php$ | grep -v ^dictionaries/scripts/* | ./vendor/bin/parallel-lint --stdin chunk-matrix: + permissions: + contents: none name: Generate Chunk Matrix runs-on: ubuntu-latest diff --git a/.github/workflows/shepherd.yml b/.github/workflows/shepherd.yml index 1ff14195871..b60bca06d65 100644 --- a/.github/workflows/shepherd.yml +++ b/.github/workflows/shepherd.yml @@ -2,6 +2,9 @@ name: Run Shepherd on: [push, pull_request] +permissions: + contents: read + jobs: build: runs-on: ubuntu-latest diff --git a/.github/workflows/windows-ci.yml b/.github/workflows/windows-ci.yml index dc5fd77c560..b901fcdf8fc 100644 --- a/.github/workflows/windows-ci.yml +++ b/.github/workflows/windows-ci.yml @@ -2,8 +2,13 @@ name: Run unit tests on Windows on: [push, pull_request] +permissions: + contents: read + jobs: chunk-matrix: + permissions: + contents: none name: Generate Chunk Matrix runs-on: ubuntu-latest From 4db928e923ae5a9b864561c8d955c4f670360362 Mon Sep 17 00:00:00 2001 From: kkmuffme <11071985+kkmuffme@users.noreply.github.com> Date: Tue, 28 Jun 2022 15:21:29 +0200 Subject: [PATCH 71/88] split loop for better performance * check isset for all elements first, as array_diff is much more expensive * Improves performance by 1% --- src/Psalm/Internal/Clause.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/Psalm/Internal/Clause.php b/src/Psalm/Internal/Clause.php index f861487c2cf..eb9fcbf6a8a 100644 --- a/src/Psalm/Internal/Clause.php +++ b/src/Psalm/Internal/Clause.php @@ -120,8 +120,14 @@ public function contains(Clause $other_clause): bool return false; } + foreach ($other_clause->possibilities as $var => $_) { + if (!isset($this->possibilities[$var])) { + return false; + } + } + foreach ($other_clause->possibilities as $var => $possible_types) { - if (!isset($this->possibilities[$var]) || count(array_diff($possible_types, $this->possibilities[$var]))) { + if (count(array_diff($possible_types, $this->possibilities[$var]))) { return false; } } From 4048bb9d8b7903f35aa8b41088203dca3e71bfca Mon Sep 17 00:00:00 2001 From: kkmuffme <11071985+kkmuffme@users.noreply.github.com> Date: Tue, 28 Jun 2022 19:04:09 +0200 Subject: [PATCH 72/88] only sort when necessary and faster hash * sort is much more expensive than count, so we only sort if we have something to sort * could implement for ksort too, but advantage there is minimal since we almost always have more than 1 possibility * use same hash algorithm as in other places (= faster) * reduces runtime by 2-3% --- src/Psalm/Internal/Clause.php | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/Psalm/Internal/Clause.php b/src/Psalm/Internal/Clause.php index eb9fcbf6a8a..7996923015e 100644 --- a/src/Psalm/Internal/Clause.php +++ b/src/Psalm/Internal/Clause.php @@ -10,15 +10,17 @@ use function array_unique; use function array_values; use function count; +use function hash; use function implode; use function ksort; -use function md5; use function reset; use function serialize; use function sort; use function strpos; use function substr; +use const PHP_VERSION_ID; + /** * @internal * @@ -106,11 +108,15 @@ public function __construct( } else { ksort($possibilities); - foreach ($possibilities as $i => $_) { + foreach ($possibilities as $i => $v) { + if (count($v) < 2) { + continue; + } sort($possibilities[$i]); } - $this->hash = md5(serialize($possibilities)); + $data = serialize($possibilities); + $this->hash = PHP_VERSION_ID >= 80100 ? hash('xxh128', $data) : hash('md4', $data); } } From 57239a7c8e633ad931676b77fb61a84ba56defb3 Mon Sep 17 00:00:00 2001 From: kkmuffme <11071985+kkmuffme@users.noreply.github.com> Date: Tue, 28 Jun 2022 15:48:35 +0200 Subject: [PATCH 73/88] assign keys to variable for better performance improves performance by ~1-1.5% --- src/Psalm/Internal/Algebra.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Psalm/Internal/Algebra.php b/src/Psalm/Internal/Algebra.php index f60f76446b1..7b59a0af77e 100644 --- a/src/Psalm/Internal/Algebra.php +++ b/src/Psalm/Internal/Algebra.php @@ -136,6 +136,7 @@ public static function simplifyCNF(array $clauses): array if (!$clause_a->reconcilable || $clause_a->wedge) { continue; } + $clause_a_keys = array_keys($clause_a->possibilities); if (count($clause_a->possibilities) !== 1 || count(array_values($clause_a->possibilities)[0]) !== 1) { foreach ($cloned_clauses as $clause_b) { @@ -143,7 +144,7 @@ public static function simplifyCNF(array $clauses): array continue; } - if (array_keys($clause_a->possibilities) === array_keys($clause_b->possibilities)) { + if ($clause_a_keys === array_keys($clause_b->possibilities)) { $opposing_keys = []; foreach ($clause_a->possibilities as $key => $a_possibilities) { From 9082eab91572f124606a2a26e04d4d6b2f178ee6 Mon Sep 17 00:00:00 2001 From: kkmuffme <11071985+kkmuffme@users.noreply.github.com> Date: Tue, 28 Jun 2022 15:27:58 +0200 Subject: [PATCH 74/88] improve cache hash performance * do not concatenate with timestamp as this is slow, since $file_contents may be big * use file contents not file path for cache hash only to ensure it works if file_path not set but file_content is * improves performance by ~5% --- .../Internal/Provider/ClassLikeStorageCacheProvider.php | 4 ++-- src/Psalm/Internal/Provider/FileStorageCacheProvider.php | 7 +++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/Psalm/Internal/Provider/ClassLikeStorageCacheProvider.php b/src/Psalm/Internal/Provider/ClassLikeStorageCacheProvider.php index 0c79452a647..9581702f0ed 100644 --- a/src/Psalm/Internal/Provider/ClassLikeStorageCacheProvider.php +++ b/src/Psalm/Internal/Provider/ClassLikeStorageCacheProvider.php @@ -110,9 +110,9 @@ public function getLatestFromCache( return $cached_value; } - private function getCacheHash(?string $file_path, ?string $file_contents): string + private function getCacheHash(?string $_unused_file_path, ?string $file_contents): string { - $data = ($file_path ? $file_contents : '') . $this->modified_timestamps; + $data = $file_contents ? $file_contents : $this->modified_timestamps; return PHP_VERSION_ID >= 80100 ? hash('xxh128', $data) : hash('md4', $data); } diff --git a/src/Psalm/Internal/Provider/FileStorageCacheProvider.php b/src/Psalm/Internal/Provider/FileStorageCacheProvider.php index 5d4a5b29b0d..d52b4165acf 100644 --- a/src/Psalm/Internal/Provider/FileStorageCacheProvider.php +++ b/src/Psalm/Internal/Provider/FileStorageCacheProvider.php @@ -117,9 +117,12 @@ public function removeCacheForFile(string $file_path): void } } - private function getCacheHash(string $file_path, string $file_contents): string + private function getCacheHash(string $_unused_file_path, string $file_contents): string { - $data = ($file_path ? $file_contents : '') . $this->modified_timestamps; + // do not concatenate, as $file_contents can be big and performance will be bad + // the timestamp is only needed if we don't have file contents + // as same contents should give same results, independent of when file was modified + $data = $file_contents ? $file_contents : $this->modified_timestamps; return PHP_VERSION_ID >= 80100 ? hash('xxh128', $data) : hash('md4', $data); } From 90586083e66e7bda677e6c7d350dd26e6602beec Mon Sep 17 00:00:00 2001 From: kkmuffme <11071985+kkmuffme@users.noreply.github.com> Date: Tue, 28 Jun 2022 20:46:23 +0200 Subject: [PATCH 75/88] check if file in cache already before adding * reduces I/O by 30% * minimal performance improvement (<0.5%) --- .../Internal/Provider/ClassLikeStorageCacheProvider.php | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/Psalm/Internal/Provider/ClassLikeStorageCacheProvider.php b/src/Psalm/Internal/Provider/ClassLikeStorageCacheProvider.php index 9581702f0ed..dae227f5520 100644 --- a/src/Psalm/Internal/Provider/ClassLikeStorageCacheProvider.php +++ b/src/Psalm/Internal/Provider/ClassLikeStorageCacheProvider.php @@ -17,6 +17,7 @@ use function igbinary_serialize; use function igbinary_unserialize; use function is_dir; +use function is_null; use function mkdir; use function serialize; use function strtolower; @@ -75,9 +76,15 @@ public function writeToCache(ClassLikeStorage $storage, string $file_path, strin { $fq_classlike_name_lc = strtolower($storage->name); - $cache_location = $this->getCacheLocationForClass($fq_classlike_name_lc, $file_path, true); $storage->hash = $this->getCacheHash($file_path, $file_contents); + // check if we have it in cache already + $cached_value = $this->loadFromCache($fq_classlike_name_lc, $file_path); + if (!is_null($cached_value) && $cached_value->hash === $storage->hash) { + return; + } + + $cache_location = $this->getCacheLocationForClass($fq_classlike_name_lc, $file_path, true); if ($this->config->use_igbinary) { file_put_contents($cache_location, igbinary_serialize($storage)); } else { From df6fdb99d7124280d883e98284f2fbef0abb6377 Mon Sep 17 00:00:00 2001 From: Benjamin Morel Date: Wed, 29 Jun 2022 18:22:46 +0200 Subject: [PATCH 76/88] Fix return type of ReflectionNamedType::getName() Fixes #8167 --- stubs/Reflection.phpstub | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stubs/Reflection.phpstub b/stubs/Reflection.phpstub index 7f453ba7bc2..cfbe3ba2375 100644 --- a/stubs/Reflection.phpstub +++ b/stubs/Reflection.phpstub @@ -142,7 +142,7 @@ class ReflectionNamedType extends ReflectionType public function getName(): string {} /** - * @psalm-assert-if-false class-string $this->getName() + * @psalm-assert-if-false class-string|'self'|'static' $this->getName() */ public function isBuiltin(): bool {} } From 4c9747203edc9f78c68300d0157aa6a8d616e2da Mon Sep 17 00:00:00 2001 From: naveen <172697+naveensrinivasan@users.noreply.github.com> Date: Thu, 30 Jun 2022 01:29:15 +0000 Subject: [PATCH 77/88] chore: Included githubactions in the dependabot config This should help with keeping the GitHub actions updated on new releases. This will also help with keeping it secure. Dependabot helps in keeping the supply chain secure https://docs.github.com/en/code-security/dependabot GitHub actions up to date https://docs.github.com/en/code-security/dependabot/working-with-dependabot/keeping-your-actions-up-to-date-with-dependabot https://github.com/ossf/scorecard/blob/main/docs/checks.md#dependency-update-tool Signed-off-by: naveen <172697+naveensrinivasan@users.noreply.github.com> --- .github/dependabot.yml | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000000..5ace4600a1f --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,6 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" From f4e518c3503161e883f82e473aef599736ec5d23 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 30 Jun 2022 17:04:59 +0000 Subject: [PATCH 78/88] build(deps): bump mheap/github-action-required-labels from 1 to 2 Bumps [mheap/github-action-required-labels](https://github.com/mheap/github-action-required-labels) from 1 to 2. - [Release notes](https://github.com/mheap/github-action-required-labels/releases) - [Commits](https://github.com/mheap/github-action-required-labels/compare/v1...v2) --- updated-dependencies: - dependency-name: mheap/github-action-required-labels dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/pr-labels.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pr-labels.yml b/.github/workflows/pr-labels.yml index 1366a7ef1a9..76ce186023b 100644 --- a/.github/workflows/pr-labels.yml +++ b/.github/workflows/pr-labels.yml @@ -6,7 +6,7 @@ jobs: label: runs-on: ubuntu-latest steps: - - uses: mheap/github-action-required-labels@v1 + - uses: mheap/github-action-required-labels@v2 with: mode: minimum count: 1 From 206a903e46233e1642415e6e03f5fa0432ab317d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 30 Jun 2022 17:05:02 +0000 Subject: [PATCH 79/88] build(deps): bump fkirc/skip-duplicate-actions from 3.4.0 to 4.0.0 Bumps [fkirc/skip-duplicate-actions](https://github.com/fkirc/skip-duplicate-actions) from 3.4.0 to 4.0.0. - [Release notes](https://github.com/fkirc/skip-duplicate-actions/releases) - [Commits](https://github.com/fkirc/skip-duplicate-actions/compare/v3.4.0...v4.0.0) --- updated-dependencies: - dependency-name: fkirc/skip-duplicate-actions dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .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 e5bd222aff7..0898928569b 100644 --- a/.github/workflows/build-phar.yml +++ b/.github/workflows/build-phar.yml @@ -15,7 +15,7 @@ jobs: should_skip: ${{ steps.skip_check.outputs.should_skip }} steps: - id: skip_check - uses: fkirc/skip-duplicate-actions@v3.4.0 + uses: fkirc/skip-duplicate-actions@v4.0.0 with: concurrent_skipping: always cancel_others: true From 236c24faeaf081859531fb6896132419d75f8662 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 30 Jun 2022 17:05:06 +0000 Subject: [PATCH 80/88] build(deps): bump actions/cache from 2 to 3 Bumps [actions/cache](https://github.com/actions/cache) from 2 to 3. - [Release notes](https://github.com/actions/cache/releases) - [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md) - [Commits](https://github.com/actions/cache/compare/v2...v3) --- updated-dependencies: - dependency-name: actions/cache dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/build-phar.yml | 2 +- .github/workflows/ci.yml | 4 ++-- .github/workflows/windows-ci.yml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build-phar.yml b/.github/workflows/build-phar.yml index e5bd222aff7..bf125046832 100644 --- a/.github/workflows/build-phar.yml +++ b/.github/workflows/build-phar.yml @@ -44,7 +44,7 @@ jobs: echo "::set-output name=vcs_cache::$(composer config cache-vcs-dir)" - name: Cache composer cache - uses: actions/cache@v2 + uses: actions/cache@v3 with: path: | ${{ steps.composer-cache.outputs.files_cache }} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4dcae6d29b5..89fd77cfb58 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,7 +25,7 @@ jobs: echo "::set-output name=vcs_cache::$(composer config cache-vcs-dir)" - name: Cache composer cache - uses: actions/cache@v2 + uses: actions/cache@v3 with: path: | ${{ steps.composer-cache.outputs.files_cache }} @@ -100,7 +100,7 @@ jobs: echo "::set-output name=vcs_cache::$(composer config cache-vcs-dir)" - name: Cache composer cache - uses: actions/cache@v2 + uses: actions/cache@v3 with: path: | ${{ steps.composer-cache.outputs.files_cache }} diff --git a/.github/workflows/windows-ci.yml b/.github/workflows/windows-ci.yml index b901fcdf8fc..a8001dc50a7 100644 --- a/.github/workflows/windows-ci.yml +++ b/.github/workflows/windows-ci.yml @@ -66,7 +66,7 @@ jobs: echo "::set-output name=vcs_cache::$(composer config cache-vcs-dir)" - name: Cache composer cache - uses: actions/cache@v2 + uses: actions/cache@v3 with: path: | ${{ steps.composer-cache.outputs.files_cache }} From 005d3e2f501ee6bd0deecc58cca2bb9329813f8d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 30 Jun 2022 17:05:08 +0000 Subject: [PATCH 81/88] build(deps): bump actions/checkout from 2 to 3 Bumps [actions/checkout](https://github.com/actions/checkout) from 2 to 3. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v2...v3) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/build-phar.yml | 2 +- .github/workflows/ci.yml | 4 ++-- .github/workflows/shepherd.yml | 2 +- .github/workflows/windows-ci.yml | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build-phar.yml b/.github/workflows/build-phar.yml index e5bd222aff7..b05551b27b8 100644 --- a/.github/workflows/build-phar.yml +++ b/.github/workflows/build-phar.yml @@ -35,7 +35,7 @@ jobs: tools: composer:v2 coverage: none - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Get Composer Cache Directories id: composer-cache diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4dcae6d29b5..177d778b003 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,7 +16,7 @@ jobs: tools: composer:v2 coverage: none - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Get Composer Cache Directories id: composer-cache @@ -91,7 +91,7 @@ jobs: coverage: none extensions: decimal - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Get Composer Cache Directories id: composer-cache diff --git a/.github/workflows/shepherd.yml b/.github/workflows/shepherd.yml index b60bca06d65..f8cf2780013 100644 --- a/.github/workflows/shepherd.yml +++ b/.github/workflows/shepherd.yml @@ -10,7 +10,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: shivammathur/setup-php@v2 with: php-version: '8.0' diff --git a/.github/workflows/windows-ci.yml b/.github/workflows/windows-ci.yml index b901fcdf8fc..814d681b484 100644 --- a/.github/workflows/windows-ci.yml +++ b/.github/workflows/windows-ci.yml @@ -57,7 +57,7 @@ jobs: tools: composer:v2 coverage: none - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Get Composer Cache Directories id: composer-cache From d0dcd543e4f901943a4efe627bee88e0effce7e7 Mon Sep 17 00:00:00 2001 From: Corey Taylor Date: Mon, 4 Jul 2022 22:13:55 -0500 Subject: [PATCH 82/88] Fix count() CallMap signature --- dictionaries/CallMap.php | 2 +- dictionaries/CallMap_80_delta.php | 4 ++++ tests/Internal/Codebase/InternalCallMapHandlerTest.php | 1 - 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/dictionaries/CallMap.php b/dictionaries/CallMap.php index 10f9d7189c7..669b958e326 100644 --- a/dictionaries/CallMap.php +++ b/dictionaries/CallMap.php @@ -1543,7 +1543,7 @@ 'Couchbase\WildcardSearchQuery::jsonSerialize' => ['array'], 'Couchbase\zlibCompress' => ['string', 'data'=>'string'], 'Couchbase\zlibDecompress' => ['string', 'data'=>'string'], -'count' => ['int', 'value'=>'Countable|array|SimpleXMLElement|ResourceBundle', 'mode='=>'int'], +'count' => ['int', 'value'=>'Countable|array', 'mode='=>'int'], 'count_chars' => ['array', 'input'=>'string', 'mode='=>'0|1|2'], 'count_chars\'1' => ['string', 'input'=>'string', 'mode='=>'3|4'], 'Countable::count' => ['int'], diff --git a/dictionaries/CallMap_80_delta.php b/dictionaries/CallMap_80_delta.php index 97dad08f1f0..08960af1425 100644 --- a/dictionaries/CallMap_80_delta.php +++ b/dictionaries/CallMap_80_delta.php @@ -213,6 +213,10 @@ 'old' => ['bool', 'typelib_name'=>'string', 'case_insensitive='=>'bool'], 'new' => ['bool', 'typelib_name'=>'string', 'case_insensitive='=>'true'], ], + 'count' => [ + 'old' => ['int', 'value'=>'Countable|array|SimpleXMLElement|ResourceBundle', 'mode='=>'int'], + 'new' => ['int', 'value'=>'Countable|array', 'mode='=>'int'], + ], 'count_chars' => [ 'old' => ['array|false', 'input'=>'string', 'mode='=>'0|1|2'], 'new' => ['array', 'input'=>'string', 'mode='=>'0|1|2'], diff --git a/tests/Internal/Codebase/InternalCallMapHandlerTest.php b/tests/Internal/Codebase/InternalCallMapHandlerTest.php index 198509bceab..00fc592f170 100644 --- a/tests/Internal/Codebase/InternalCallMapHandlerTest.php +++ b/tests/Internal/Codebase/InternalCallMapHandlerTest.php @@ -52,7 +52,6 @@ class InternalCallMapHandlerTest extends TestCase 'bcmod', 'bcpowmod', 'bzdecompress', - 'count', 'crypt', 'date_isodate_set', 'debug_zval_dump', From c71dcd581e68582c202708d593b4a136a37422aa Mon Sep 17 00:00:00 2001 From: Corey Taylor Date: Mon, 4 Jul 2022 22:27:00 -0500 Subject: [PATCH 83/88] Fix fputcsv() CallMap signature --- dictionaries/CallMap.php | 2 +- dictionaries/CallMap_81_delta.php | 4 ++++ tests/Internal/Codebase/InternalCallMapHandlerTest.php | 2 -- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/dictionaries/CallMap.php b/dictionaries/CallMap.php index 669b958e326..683997ab874 100644 --- a/dictionaries/CallMap.php +++ b/dictionaries/CallMap.php @@ -3323,7 +3323,7 @@ 'fpassthru' => ['int|false', 'stream'=>'resource'], 'fpm_get_status' => ['array|false'], 'fprintf' => ['int', 'stream'=>'resource', 'format'=>'string', '...values='=>'string|int|float'], -'fputcsv' => ['int|false', 'stream'=>'resource', 'fields'=>'array', 'separator='=>'string', 'enclosure='=>'string', 'escape='=>'string'], +'fputcsv' => ['int|false', 'stream'=>'resource', 'fields'=>'array', 'separator='=>'string', 'enclosure='=>'string', 'escape='=>'string', 'eol='=>'string'], 'fputs' => ['int|false', 'stream'=>'resource', 'data'=>'string', 'length='=>'int'], 'fread' => ['string|false', 'stream'=>'resource', 'length'=>'int'], 'frenchtojd' => ['int', 'month'=>'int', 'day'=>'int', 'year'=>'int'], diff --git a/dictionaries/CallMap_81_delta.php b/dictionaries/CallMap_81_delta.php index a22c8b4613a..b5d3efea585 100644 --- a/dictionaries/CallMap_81_delta.php +++ b/dictionaries/CallMap_81_delta.php @@ -71,6 +71,10 @@ 'old' => ['bool', 'finfo'=>'resource', 'flags'=>'int'], 'new' => ['bool', 'finfo'=>'finfo', 'flags'=>'int'], ], + 'fputcsv' => [ + 'old' => ['int|false', 'stream'=>'resource', 'fields'=>'array', 'separator='=>'string', 'enclosure='=>'string', 'escape='=>'string'], + 'new' => ['int|false', 'stream'=>'resource', 'fields'=>'array', 'separator='=>'string', 'enclosure='=>'string', 'escape='=>'string', 'eol='=>'string'], + ], 'ftp_connect' => [ 'old' => ['resource|false', 'hostname' => 'string', 'port=' => 'int', 'timeout=' => 'int'], 'new' => ['FTP\Connection|false', 'hostname' => 'string', 'port=' => 'int', 'timeout=' => 'int'], diff --git a/tests/Internal/Codebase/InternalCallMapHandlerTest.php b/tests/Internal/Codebase/InternalCallMapHandlerTest.php index 00fc592f170..29ce0fd614c 100644 --- a/tests/Internal/Codebase/InternalCallMapHandlerTest.php +++ b/tests/Internal/Codebase/InternalCallMapHandlerTest.php @@ -80,8 +80,6 @@ class InternalCallMapHandlerTest extends TestCase 'enchant_dict_suggest', 'enum_exists', 'extract', - // https://www.php.net/manual/en/function.fputcsv.php - 'fputcsv' => ['8.1'], 'get_class_methods', 'get_headers', 'get_parent_class', From 2469b6222dba1078eecae74164ac772db24e71ac Mon Sep 17 00:00:00 2001 From: Corey Taylor Date: Mon, 4 Jul 2022 22:29:26 -0500 Subject: [PATCH 84/88] Fix extract() CallMap signature --- dictionaries/CallMap.php | 2 +- dictionaries/CallMap_historical.php | 2 +- tests/Internal/Codebase/InternalCallMapHandlerTest.php | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/dictionaries/CallMap.php b/dictionaries/CallMap.php index 683997ab874..6d66e2d4bcd 100644 --- a/dictionaries/CallMap.php +++ b/dictionaries/CallMap.php @@ -2926,7 +2926,7 @@ 'explode' => ['list', 'separator'=>'string', 'string'=>'string', 'limit='=>'int'], 'expm1' => ['float', 'num'=>'float'], 'extension_loaded' => ['bool', 'extension'=>'string'], -'extract' => ['int', '&rw_array'=>'array', 'flags='=>'int', 'prefix='=>'?string'], +'extract' => ['int', '&rw_array'=>'array', 'flags='=>'int', 'prefix='=>'string'], 'ezmlm_hash' => ['int', 'addr'=>'string'], 'fam_cancel_monitor' => ['bool', 'fam'=>'resource', 'fam_monitor'=>'resource'], 'fam_close' => ['void', 'fam'=>'resource'], diff --git a/dictionaries/CallMap_historical.php b/dictionaries/CallMap_historical.php index ffff495f1fc..0f5a0f2d4c0 100644 --- a/dictionaries/CallMap_historical.php +++ b/dictionaries/CallMap_historical.php @@ -10544,7 +10544,7 @@ 'explode' => ['list|false', 'separator'=>'string', 'string'=>'string', 'limit='=>'int'], 'expm1' => ['float', 'num'=>'float'], 'extension_loaded' => ['bool', 'extension'=>'string'], - 'extract' => ['int', '&rw_array'=>'array', 'flags='=>'int', 'prefix='=>'?string'], + 'extract' => ['int', '&rw_array'=>'array', 'flags='=>'int', 'prefix='=>'string'], 'ezmlm_hash' => ['int', 'addr'=>'string'], 'fam_cancel_monitor' => ['bool', 'fam'=>'resource', 'fam_monitor'=>'resource'], 'fam_close' => ['void', 'fam'=>'resource'], diff --git a/tests/Internal/Codebase/InternalCallMapHandlerTest.php b/tests/Internal/Codebase/InternalCallMapHandlerTest.php index 29ce0fd614c..3ba9a2bb6dc 100644 --- a/tests/Internal/Codebase/InternalCallMapHandlerTest.php +++ b/tests/Internal/Codebase/InternalCallMapHandlerTest.php @@ -79,7 +79,6 @@ class InternalCallMapHandlerTest extends TestCase 'enchant_dict_store_replacement', 'enchant_dict_suggest', 'enum_exists', - 'extract', 'get_class_methods', 'get_headers', 'get_parent_class', From e0ebfe19a20156d19d40a7fbd8e53df759970372 Mon Sep 17 00:00:00 2001 From: Corey Taylor Date: Tue, 5 Jul 2022 00:50:34 -0500 Subject: [PATCH 85/88] Fix class functions CallMap signatures --- dictionaries/CallMap.php | 6 +++--- dictionaries/CallMap_80_delta.php | 8 ++++++++ dictionaries/CallMap_81_delta.php | 2 +- tests/Internal/Codebase/InternalCallMapHandlerTest.php | 3 --- 4 files changed, 12 insertions(+), 7 deletions(-) diff --git a/dictionaries/CallMap.php b/dictionaries/CallMap.php index 6d66e2d4bcd..15ff459d4c9 100644 --- a/dictionaries/CallMap.php +++ b/dictionaries/CallMap.php @@ -2520,7 +2520,7 @@ 'enchant_dict_store_replacement' => ['void', 'dictionary'=>'resource', 'misspelled'=>'string', 'correct'=>'string'], 'enchant_dict_suggest' => ['array', 'dictionary'=>'resource', 'word'=>'string'], 'end' => ['mixed|false', '&r_array'=>'array|object'], -'enum_exists' => ['bool', 'class' => 'class-string', 'autoload=' => 'bool'], +'enum_exists' => ['bool', 'enum' => 'class-string', 'autoload=' => 'bool'], 'Error::__clone' => ['void'], 'Error::__construct' => ['void', 'message='=>'string', 'code='=>'int', 'previous='=>'?Throwable|?Error'], 'Error::__toString' => ['string'], @@ -3723,7 +3723,7 @@ 'get_called_class' => ['class-string'], 'get_cfg_var' => ['string|false', 'option'=>'string'], 'get_class' => ['class-string', 'object='=>'object'], -'get_class_methods' => ['list|null', 'object_or_class'=>'mixed'], +'get_class_methods' => ['list', 'object_or_class'=>'object|class-string'], 'get_class_vars' => ['array', 'class'=>'string'], 'get_current_user' => ['string'], 'get_debug_type' => ['string', 'value'=>'mixed'], @@ -3743,7 +3743,7 @@ 'get_magic_quotes_runtime' => ['int|false'], 'get_meta_tags' => ['array', 'filename'=>'string', 'use_include_path='=>'bool'], 'get_object_vars' => ['array', 'object'=>'object'], -'get_parent_class' => ['class-string|false', 'object_or_class='=>'mixed'], +'get_parent_class' => ['class-string|false', 'object_or_class='=>'object|class-string'], 'get_required_files' => ['list'], 'get_resource_id' => ['int', 'resource'=>'resource'], 'get_resource_type' => ['string', 'resource'=>'resource'], diff --git a/dictionaries/CallMap_80_delta.php b/dictionaries/CallMap_80_delta.php index 08960af1425..e7e4323b5a2 100644 --- a/dictionaries/CallMap_80_delta.php +++ b/dictionaries/CallMap_80_delta.php @@ -357,6 +357,14 @@ 'old' => ['list|false', 'separator'=>'string', 'string'=>'string', 'limit='=>'int'], 'new' => ['list', 'separator'=>'string', 'string'=>'string', 'limit='=>'int'], ], + 'get_class_methods' => [ + 'old' => ['list|null', 'object_or_class'=>'mixed'], + 'new' => ['list', 'object_or_class'=>'object|class-string'], + ], + 'get_parent_class' => [ + 'old' => ['class-string|false', 'object_or_class='=>'mixed'], + 'new' => ['class-string|false', 'object_or_class='=>'object|class-string'], + ], 'gmdate' => [ 'old' => ['string', 'format'=>'string', 'timestamp='=>'int'], 'new' => ['string', 'format'=>'string', 'timestamp='=>'int|null'], diff --git a/dictionaries/CallMap_81_delta.php b/dictionaries/CallMap_81_delta.php index b5d3efea585..13c10cd77a1 100644 --- a/dictionaries/CallMap_81_delta.php +++ b/dictionaries/CallMap_81_delta.php @@ -17,7 +17,7 @@ return [ 'added' => [ 'array_is_list' => ['bool', 'array' => 'array'], - 'enum_exists' => ['bool', 'class' => 'class-string', 'autoload=' => 'bool'], + 'enum_exists' => ['bool', 'enum' => 'class-string', 'autoload=' => 'bool'], 'fsync' => ['bool', 'stream' => 'resource'], 'fdatasync' => ['bool', 'stream' => 'resource'], 'imageavif' => ['bool', 'image'=>'GdImage', 'file='=>'resource|string|null', 'quality='=>'int', 'speed='=>'int'], diff --git a/tests/Internal/Codebase/InternalCallMapHandlerTest.php b/tests/Internal/Codebase/InternalCallMapHandlerTest.php index 3ba9a2bb6dc..72ef664ae14 100644 --- a/tests/Internal/Codebase/InternalCallMapHandlerTest.php +++ b/tests/Internal/Codebase/InternalCallMapHandlerTest.php @@ -78,10 +78,7 @@ class InternalCallMapHandlerTest extends TestCase 'enchant_dict_quick_check', 'enchant_dict_store_replacement', 'enchant_dict_suggest', - 'enum_exists', - 'get_class_methods', 'get_headers', - 'get_parent_class', 'gmp_clrbit', 'gmp_div', 'gmp_setbit', From b1e3094baeff163dabc4199914fab78a0e688913 Mon Sep 17 00:00:00 2001 From: Corey Taylor Date: Sat, 25 Jun 2022 17:01:12 -0500 Subject: [PATCH 86/88] Allow testing expected CallMap return types and ignore functions that currently fail --- dictionaries/CallMap.php | 2 + dictionaries/CallMap_81_delta.php | 2 + .../Codebase/InternalCallMapHandlerTest.php | 270 +++++++++++++++--- 3 files changed, 240 insertions(+), 34 deletions(-) diff --git a/dictionaries/CallMap.php b/dictionaries/CallMap.php index 10f9d7189c7..e0086c01973 100644 --- a/dictionaries/CallMap.php +++ b/dictionaries/CallMap.php @@ -11453,7 +11453,9 @@ 'ReflectionFunctionAbstract::getShortName' => ['string'], 'ReflectionFunctionAbstract::getStartLine' => ['int|false'], 'ReflectionFunctionAbstract::getStaticVariables' => ['array'], +'ReflectionFunctionAbstract::getTentativeReturnType' => ['?ReflectionType'], 'ReflectionFunctionAbstract::hasReturnType' => ['bool'], +'ReflectionFunctionAbstract::hasTentativeReturnType' => ['bool'], 'ReflectionFunctionAbstract::inNamespace' => ['bool'], 'ReflectionFunctionAbstract::isClosure' => ['bool'], 'ReflectionFunctionAbstract::isDeprecated' => ['bool'], diff --git a/dictionaries/CallMap_81_delta.php b/dictionaries/CallMap_81_delta.php index a22c8b4613a..e4e9579260e 100644 --- a/dictionaries/CallMap_81_delta.php +++ b/dictionaries/CallMap_81_delta.php @@ -46,6 +46,8 @@ 'ReflectionEnumUnitCase::getEnum' => ['ReflectionEnum'], 'ReflectionEnumUnitCase::getValue' => ['UnitEnum'], 'ReflectionEnumBackedCase::getBackingValue' => ['string|int'], + 'ReflectionFunctionAbstract::getTentativeReturnType' => ['?ReflectionType'], + 'ReflectionFunctionAbstract::hasTentativeReturnType' => ['bool'], 'ReflectionFunctionAbstract::isStatic' => ['bool'], 'ReflectionObject::isEnum' => ['bool'], ], diff --git a/tests/Internal/Codebase/InternalCallMapHandlerTest.php b/tests/Internal/Codebase/InternalCallMapHandlerTest.php index 198509bceab..262cc73fb95 100644 --- a/tests/Internal/Codebase/InternalCallMapHandlerTest.php +++ b/tests/Internal/Codebase/InternalCallMapHandlerTest.php @@ -2,7 +2,9 @@ namespace Psalm\Tests\Internal\Codebase; +use Exception; use InvalidArgumentException; +use PHPUnit\Framework\AssertionFailedError; use PHPUnit\Framework\ExpectationFailedException; use PHPUnit\Framework\SkippedTestError; use Psalm\Codebase; @@ -16,6 +18,7 @@ use Psalm\Tests\TestCase; use Psalm\Tests\TestConfig; use Psalm\Type; +use ReflectionException; use ReflectionFunction; use ReflectionParameter; use ReflectionType; @@ -336,6 +339,167 @@ class InternalCallMapHandlerTest extends TestCase 'zlib_encode', ]; + /** + * List of function names to ignore only for return type checks. + * + * @var list + */ + private static $ignoredReturnTypeOnlyFunctions = [ + 'bcsqrt', + 'bzopen', + 'cal_from_jd', + 'collator_get_strength', + 'curl_multi_init', + 'date_add', + 'date_date_set', + 'date_diff', + 'date_offset_get', + 'date_parse', + 'date_sub', + 'date_sun_info', + 'date_sunrise', + 'date_sunset', + 'date_time_set', + 'date_timestamp_set', + 'date_timezone_set', + 'datefmt_set_lenient', + 'dba_open', + 'dba_popen', + 'deflate_init', + 'enchant_broker_init', + 'fgetcsv', + 'filter_input_array', + 'fopen', + 'fpassthru', + 'fsockopen', + 'ftp_get_option', + 'get_declared_traits', + 'gmp_export', + 'gmp_hamdist', + 'gmp_import', + 'gzeof', + 'gzopen', + 'gzpassthru', + 'hash', + 'hash_hkdf', + 'hash_hmac', + 'iconv_get_encoding', + 'igbinary_serialize', + 'imagecolorclosest', + 'imagecolorclosestalpha', + 'imagecolorclosesthwb', + 'imagecolorexact', + 'imagecolorexactalpha', + 'imagecolorresolve', + 'imagecolorresolvealpha', + 'imagecolorset', + 'imagecolorsforindex', + 'imagecolorstotal', + 'imagecolortransparent', + 'imageloadfont', + 'imagesx', + 'imagesy', + 'imap_mailboxmsginfo', + 'imap_msgno', + 'imap_num_recent', + 'inflate_init', + 'intlcal_get', + 'intlcal_get_keyword_values_for_locale', + 'intlgregcal_set_gregorian_change', + 'intltz_get_offset', + 'jddayofweek', + 'jdtounix', + 'ldap_count_entries', + 'ldap_exop', + 'ldap_get_attributes', + 'mb_encoding_aliases', + 'metaphone', + 'mongodb\\bson\\fromjson', + 'mongodb\\bson\\fromphp', + 'mongodb\\bson\\tojson', + 'msg_get_queue', + 'mysqli_stmt_get_warnings', + 'mysqli_stmt_insert_id', + 'numfmt_create', + 'ob_list_handlers', + 'odbc_autocommit', + 'odbc_columnprivileges', + 'odbc_columns', + 'odbc_connect', + 'odbc_do', + 'odbc_exec', + 'odbc_fetch_object', + 'odbc_foreignkeys', + 'odbc_gettypeinfo', + 'odbc_pconnect', + 'odbc_prepare', + 'odbc_primarykeys', + 'odbc_specialcolumns', + 'odbc_statistics', + 'odbc_tableprivileges', + 'odbc_tables', + 'opendir', + 'openssl_random_pseudo_bytes', + 'openssl_spki_export', + 'openssl_spki_export_challenge', + 'pack', + 'parse_url', + 'passthru', + 'pcntl_exec', + 'pcntl_signal_get_handler', + 'pcntl_strerror', + 'pfsockopen', + 'pg_port', + 'pg_socket', + 'popen', + 'proc_open', + 'pspell_config_create', + 'pspell_new', + 'pspell_new_config', + 'pspell_new_personal', + 'register_shutdown_function', + 'rewinddir', + 'set_error_handler', + 'set_exception_handler', + 'shm_attach', + 'shmop_open', + 'simplexml_import_dom', + 'sleep', + 'snmp_set_oid_numeric_print', + 'socket_export_stream', + 'socket_import_stream', + 'sodium_crypto_aead_chacha20poly1305_encrypt', + 'sodium_crypto_aead_chacha20poly1305_ietf_encrypt', + 'sodium_crypto_aead_xchacha20poly1305_ietf_encrypt', + 'spl_autoload_functions', + 'stream_bucket_new', + 'stream_context_create', + 'stream_context_get_default', + 'stream_context_set_default', + 'stream_filter_append', + 'stream_filter_prepend', + 'stream_set_chunk_size', + 'stream_socket_accept', + 'stream_socket_client', + 'stream_socket_server', + 'substr', + 'substr_compare', + 'timezone_abbreviations_list', + 'timezone_offset_get', + 'tmpfile', + 'user_error', + 'xml_get_current_byte_index', + 'xml_get_current_column_number', + 'xml_get_current_line_number', + 'xml_get_error_code', + 'xml_parser_get_option', + 'yaml_parse', + 'yaml_parse_file', + 'yaml_parse_url', + 'zip_open', + 'zip_read', + ]; + /** * * @var Codebase @@ -391,7 +555,7 @@ public function testGetcallmapReturnsAValidCallmap(): void /** * - * @return iterable}> + * @return iterable}> */ public function callMapEntryProvider(): iterable { @@ -437,45 +601,59 @@ private function isIgnored(string $functionName): bool return false; } + /** + */ + private function isReturnTypeOnlyIgnored(string $functionName): bool + { + return in_array($functionName, static::$ignoredReturnTypeOnlyFunctions, true); + } + /** * @depends testIgnoresAreSortedAndUnique * @depends testGetcallmapReturnsAValidCallmap * @dataProvider callMapEntryProvider * @coversNothing * @psalm-param callable-string $functionName + * @param array $callMapEntry */ public function testIgnoredFunctionsStillFail(string $functionName, array $callMapEntry): void { - if (!$this->isIgnored($functionName)) { + $functionIgnored = $this->isIgnored($functionName); + if (!$functionIgnored && !$this->isReturnTypeOnlyIgnored($functionName)) { // Dummy assertion to mark it as passed $this->assertTrue(true); return; } - $this->expectException(ExpectationFailedException::class); + $function = new ReflectionFunction($functionName); + /** @var string $entryReturnType */ + $entryReturnType = array_shift($callMapEntry); + + if ($functionIgnored) { + try { + /** @var array $callMapEntry */ + $this->assertEntryParameters($function, $callMapEntry); + $this->assertEntryReturnType($function, $entryReturnType); + } catch (AssertionFailedError $e) { + $this->assertTrue(true); + return; + } catch (ExpectationFailedException $e) { + $this->assertTrue(true); + return; + } + $this->fail("Remove '{$functionName}' from InternalCallMapHandlerTest::\$ignoredFunctions"); + } try { - unset($callMapEntry[0]); - /** @var array $callMapEntry */ - $this->assertEntryIsCorrect($callMapEntry, $functionName); - } catch (InvalidArgumentException $t) { - // Silence this one for now. - $this->markTestSkipped('IA'); - } catch (SkippedTestError $t) { - die('this should not happen'); + $this->assertEntryReturnType($function, $entryReturnType); + } catch (AssertionFailedError $e) { + $this->assertTrue(true); + return; } catch (ExpectationFailedException $e) { - // This is good! - throw $e; - } catch (InvalidArgumentException $e) { - // This can happen if a class does not exist, we handle the message to check for this case. - if (preg_match('/^Could not get class storage for (.*)$/', $e->getMessage(), $matches) - && !class_exists($matches[1]) - ) { - die("Class mentioned in callmap does not exist: " . $matches[1]); - } + $this->assertTrue(true); + return; } - - $this->markTestIncomplete("Remove function '{$functionName}' from your ignores"); + $this->fail("Remove '{$functionName}' from InternalCallMapHandlerTest::\$ignoredReturnTypeOnlyFunctions"); } /** @@ -485,7 +663,7 @@ public function testIgnoredFunctionsStillFail(string $functionName, array $callM * @depends testIgnoresAreSortedAndUnique * @dataProvider callMapEntryProvider * @psalm-param callable-string $functionName - * @param array $callMapEntry + * @param array $callMapEntry */ public function testCallMapCompliesWithReflection(string $functionName, array $callMapEntry): void { @@ -493,27 +671,31 @@ public function testCallMapCompliesWithReflection(string $functionName, array $c $this->markTestSkipped("Function $functionName is ignored in config"); } - unset($callMapEntry[0]); + $function = new ReflectionFunction($functionName); + /** @var string $entryReturnType */ + $entryReturnType = array_shift($callMapEntry); + /** @var array $callMapEntry */ - $this->assertEntryIsCorrect($callMapEntry, $functionName); + $this->assertEntryParameters($function, $callMapEntry); + + if (!$this->isReturnTypeOnlyIgnored($functionName)) { + $this->assertEntryReturnType($function, $entryReturnType); + } } /** * - * @param array $callMapEntryWithoutReturn - * @psalm-param callable-string $functionName + * @param array $entryParameters */ - private function assertEntryIsCorrect(array $callMapEntryWithoutReturn, string $functionName): void + private function assertEntryParameters(ReflectionFunction $function, array $entryParameters): void { - $rF = new ReflectionFunction($functionName); - /** * Parse the parameter names from the map. * @var array */ $normalizedEntries = []; - foreach ($callMapEntryWithoutReturn as $key => $entry) { + foreach ($entryParameters as $key => $entry) { $normalizedKey = $key; /** * @@ -555,8 +737,8 @@ private function assertEntryIsCorrect(array $callMapEntryWithoutReturn, string $ $normalizedEntry['name'] = $normalizedKey; $normalizedEntries[$normalizedKey] = $normalizedEntry; } - foreach ($rF->getParameters() as $parameter) { - $this->assertArrayHasKey($parameter->getName(), $normalizedEntries, "Callmap is missing entry for param {$parameter->getName()} in $functionName: " . print_r($normalizedEntries, true)); + foreach ($function->getParameters() as $parameter) { + $this->assertArrayHasKey($parameter->getName(), $normalizedEntries, "Callmap is missing entry for param {$parameter->getName()} in {$function->getName()}: " . print_r($normalizedEntries, true)); $this->assertParameter($normalizedEntries[$parameter->getName()], $parameter); } } @@ -579,6 +761,26 @@ private function assertParameter(array $normalizedEntry, ReflectionParameter $pa } } + /** + * + * @psalm-suppress UndefinedMethod + */ + public function assertEntryReturnType(ReflectionFunction $function, string $entryReturnType): void + { + if (version_compare(PHP_VERSION, '8.1.0', '>=')) { + /** @var \ReflectionType|null $expectedType */ + $expectedType = $function->hasTentativeReturnType() ? $function->getTentativeReturnType() : $function->getReturnType(); + } else { + $expectedType = $function->getReturnType(); + } + if ($expectedType === null) { + $this->assertSame('', $entryReturnType, 'CallMap entry has incorrect return type'); + return; + } + + $this->assertTypeValidity($expectedType, $entryReturnType, 'CallMap entry has incorrect return type'); + } + /** * Since string equality is too strict, we do some extra checking here */ @@ -594,7 +796,7 @@ private function assertTypeValidity(ReflectionType $reflected, string $specified if (preg_match('/^Could not get class storage for (.*)$/', $e->getMessage(), $matches) && !class_exists($matches[1]) ) { - die("Class mentioned in callmap does not exist: " . $matches[1]); + $this->fail("Class used in CallMap does not exist: {$matches[1]}"); } } } From 6c49dad38c0fcc274d06f0418e6a2dc20d719a9d Mon Sep 17 00:00:00 2001 From: Adrien Foulon <6115458+Tofandel@users.noreply.github.com> Date: Wed, 6 Jul 2022 02:36:20 +0200 Subject: [PATCH 87/88] fix: ltrim may return class-string #8218 Fixes #8218 --- stubs/CoreGenericFunctions.phpstub | 2 ++ 1 file changed, 2 insertions(+) diff --git a/stubs/CoreGenericFunctions.phpstub b/stubs/CoreGenericFunctions.phpstub index 800383c0dfb..4d33ebbd053 100644 --- a/stubs/CoreGenericFunctions.phpstub +++ b/stubs/CoreGenericFunctions.phpstub @@ -557,6 +557,8 @@ function trim(string $string, string $characters = " \t\n\r\0\x0B") : string {} /** * @psalm-pure * + * @return ($string is class-string ? ($characters is '\\' ? class-string : string) : string) + * * @psalm-flow ($string) -> return */ function ltrim(string $string, string $characters = " \t\n\r\0\x0B") : string {} From 31bee0c845db4ac50d117f16535183605608982a Mon Sep 17 00:00:00 2001 From: Corey Taylor Date: Wed, 6 Jul 2022 03:33:34 -0500 Subject: [PATCH 88/88] Fix codestyle erors in InternalCallMapHandlerTest --- tests/Internal/Codebase/InternalCallMapHandlerTest.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/Internal/Codebase/InternalCallMapHandlerTest.php b/tests/Internal/Codebase/InternalCallMapHandlerTest.php index 05f550b714f..e69385648bc 100644 --- a/tests/Internal/Codebase/InternalCallMapHandlerTest.php +++ b/tests/Internal/Codebase/InternalCallMapHandlerTest.php @@ -2,11 +2,9 @@ namespace Psalm\Tests\Internal\Codebase; -use Exception; use InvalidArgumentException; use PHPUnit\Framework\AssertionFailedError; use PHPUnit\Framework\ExpectationFailedException; -use PHPUnit\Framework\SkippedTestError; use Psalm\Codebase; use Psalm\Internal\Analyzer\ProjectAnalyzer; use Psalm\Internal\Codebase\InternalCallMapHandler; @@ -18,11 +16,11 @@ use Psalm\Tests\TestCase; use Psalm\Tests\TestConfig; use Psalm\Type; -use ReflectionException; use ReflectionFunction; use ReflectionParameter; use ReflectionType; +use function array_shift; use function class_exists; use function count; use function explode; @@ -37,9 +35,11 @@ use function strncmp; use function strpos; use function substr; +use function version_compare; use const PHP_MAJOR_VERSION; use const PHP_MINOR_VERSION; +use const PHP_VERSION; class InternalCallMapHandlerTest extends TestCase { @@ -761,7 +761,7 @@ private function assertParameter(array $normalizedEntry, ReflectionParameter $pa public function assertEntryReturnType(ReflectionFunction $function, string $entryReturnType): void { if (version_compare(PHP_VERSION, '8.1.0', '>=')) { - /** @var \ReflectionType|null $expectedType */ + /** @var ReflectionType|null $expectedType */ $expectedType = $function->hasTentativeReturnType() ? $function->getTentativeReturnType() : $function->getReturnType(); } else { $expectedType = $function->getReturnType();