Compare commits

..

4 Commits

Author SHA1 Message Date
Clemens Schwaighofer
4f2ac2ed1b Change Logging class / method name and Debug Support for backtrace
Debug Support:
getCallerClass now returns level 1 class from the trace like the
getCallerMethod. There is also a new getCallerClassMethod that returns
namespace\class->method (or :: for static).

getCallerTopLevelClass works like getCallerClass did before and returns
the TOP level (first entry on the call stack that has a set class name)

Logging:
Do not use the Support getCallerClass/Method/File but call it inside
and use level 2 in trace to get the data we need For the last call
before debug call
Also update the strack trace for the debug call to use ->/:: for method
type
2023-08-22 13:28:59 +09:00
Clemens Schwaighofer
5b8e4e4e3e Core composer packages update 2023-08-22 13:04:19 +09:00
Clemens Schwaighofer
53192da571 www folder composer updates 2023-08-22 13:04:01 +09:00
Clemens Schwaighofer
f29e915068 class_test fixes for phpstan checks 2023-08-02 16:32:11 +09:00
279 changed files with 5377 additions and 4182 deletions

View File

@@ -562,10 +562,10 @@ final class CoreLibsDebugSupportTest extends TestCase
}
/**
* Undocumented function
* test the lowest one (one above base)
*
* @cover ::getCallerClass
* @testWith ["PHPUnit\\TextUI\\Command"]
* @testWith ["tests\\CoreLibsDebugSupportTest"]
* @testdox getCallerClass check if it returns $expected [$_dataName]
*
* @return void
@@ -578,6 +578,40 @@ final class CoreLibsDebugSupportTest extends TestCase
);
}
/**
* test highest return (top level)
*
* @cover ::getCallerTopLevelClass
* @testWith ["PHPUnit\\TextUI\\Command"]
* @testdox getCallerTopLevelClass check if it returns $expected [$_dataName]
*
* @return void
*/
public function testGetCallerTopLevelClass(string $expected): void
{
$this->assertEquals(
$expected,
Support::getCallerTopLevelClass()
);
}
/**
* test highest return (top level)
*
* @cover ::getCallerClassMethod
* @testWith ["tests\\CoreLibsDebugSupportTest->testGetCallerClassMethod"]
* @testdox getCallerClassMethod check if it returns $expected [$_dataName]
*
* @return void
*/
public function testGetCallerClassMethod(string $expected): void
{
$this->assertEquals(
$expected,
Support::getCallerClassMethod()
);
}
/**
* Undocumented function
*

View File

@@ -22,7 +22,7 @@ final class CoreLibsLoggingLoggingTest extends TestCase
. "\[[\w\.]+(:\d+)?\]\s{1}" // host:port
. "\[[\w\-\.\/]+:\d+\]\s{1}" // folder/file
. "\[\w+\]\s{1}" // run id
. "{[\w\\\\]+(::\w+)?}\s{1}"; // class
. "{[\w\\\\]+((::|->)\w+)?}\s{1}"; // class
public static function tearDownAfterClass(): void
{

75
composer.lock generated
View File

@@ -786,16 +786,16 @@
},
{
"name": "nikic/php-parser",
"version": "v4.16.0",
"version": "v4.17.1",
"source": {
"type": "git",
"url": "https://github.com/nikic/PHP-Parser.git",
"reference": "19526a33fb561ef417e822e85f08a00db4059c17"
"reference": "a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/19526a33fb561ef417e822e85f08a00db4059c17",
"reference": "19526a33fb561ef417e822e85f08a00db4059c17",
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d",
"reference": "a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d",
"shasum": ""
},
"require": {
@@ -836,9 +836,9 @@
],
"support": {
"issues": "https://github.com/nikic/PHP-Parser/issues",
"source": "https://github.com/nikic/PHP-Parser/tree/v4.16.0"
"source": "https://github.com/nikic/PHP-Parser/tree/v4.17.1"
},
"time": "2023-06-25T14:52:30+00:00"
"time": "2023-08-13T19:53:39+00:00"
},
{
"name": "phan/phan",
@@ -1031,16 +1031,16 @@
},
{
"name": "phpdocumentor/type-resolver",
"version": "1.7.2",
"version": "1.7.3",
"source": {
"type": "git",
"url": "https://github.com/phpDocumentor/TypeResolver.git",
"reference": "b2fe4d22a5426f38e014855322200b97b5362c0d"
"reference": "3219c6ee25c9ea71e3d9bbaf39c67c9ebd499419"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/b2fe4d22a5426f38e014855322200b97b5362c0d",
"reference": "b2fe4d22a5426f38e014855322200b97b5362c0d",
"url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/3219c6ee25c9ea71e3d9bbaf39c67c9ebd499419",
"reference": "3219c6ee25c9ea71e3d9bbaf39c67c9ebd499419",
"shasum": ""
},
"require": {
@@ -1083,9 +1083,9 @@
"description": "A PSR-5 based resolver of Class names, Types and Structural Element Names",
"support": {
"issues": "https://github.com/phpDocumentor/TypeResolver/issues",
"source": "https://github.com/phpDocumentor/TypeResolver/tree/1.7.2"
"source": "https://github.com/phpDocumentor/TypeResolver/tree/1.7.3"
},
"time": "2023-05-30T18:13:47+00:00"
"time": "2023-08-12T11:01:26+00:00"
},
{
"name": "phpstan/extension-installer",
@@ -1133,16 +1133,16 @@
},
{
"name": "phpstan/phpdoc-parser",
"version": "1.23.0",
"version": "1.23.1",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpdoc-parser.git",
"reference": "a2b24135c35852b348894320d47b3902a94bc494"
"reference": "846ae76eef31c6d7790fac9bc399ecee45160b26"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/a2b24135c35852b348894320d47b3902a94bc494",
"reference": "a2b24135c35852b348894320d47b3902a94bc494",
"url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/846ae76eef31c6d7790fac9bc399ecee45160b26",
"reference": "846ae76eef31c6d7790fac9bc399ecee45160b26",
"shasum": ""
},
"require": {
@@ -1174,22 +1174,22 @@
"description": "PHPDoc parser with support for nullable, intersection and generic types",
"support": {
"issues": "https://github.com/phpstan/phpdoc-parser/issues",
"source": "https://github.com/phpstan/phpdoc-parser/tree/1.23.0"
"source": "https://github.com/phpstan/phpdoc-parser/tree/1.23.1"
},
"time": "2023-07-23T22:17:56+00:00"
"time": "2023-08-03T16:32:59+00:00"
},
{
"name": "phpstan/phpstan",
"version": "1.10.26",
"version": "1.10.29",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpstan.git",
"reference": "5d660cbb7e1b89253a47147ae44044f49832351f"
"reference": "ee5d8f2d3977fb09e55603eee6fb53bdd76ee9c1"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/5d660cbb7e1b89253a47147ae44044f49832351f",
"reference": "5d660cbb7e1b89253a47147ae44044f49832351f",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/ee5d8f2d3977fb09e55603eee6fb53bdd76ee9c1",
"reference": "ee5d8f2d3977fb09e55603eee6fb53bdd76ee9c1",
"shasum": ""
},
"require": {
@@ -1238,25 +1238,25 @@
"type": "tidelift"
}
],
"time": "2023-07-19T12:44:37+00:00"
"time": "2023-08-14T13:24:11+00:00"
},
{
"name": "phpstan/phpstan-deprecation-rules",
"version": "1.1.3",
"version": "1.1.4",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpstan-deprecation-rules.git",
"reference": "a22b36b955a2e9a3d39fe533b6c1bb5359f9c319"
"reference": "089d8a8258ed0aeefdc7b68b6c3d25572ebfdbaa"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan-deprecation-rules/zipball/a22b36b955a2e9a3d39fe533b6c1bb5359f9c319",
"reference": "a22b36b955a2e9a3d39fe533b6c1bb5359f9c319",
"url": "https://api.github.com/repos/phpstan/phpstan-deprecation-rules/zipball/089d8a8258ed0aeefdc7b68b6c3d25572ebfdbaa",
"reference": "089d8a8258ed0aeefdc7b68b6c3d25572ebfdbaa",
"shasum": ""
},
"require": {
"php": "^7.2 || ^8.0",
"phpstan/phpstan": "^1.10"
"phpstan/phpstan": "^1.10.3"
},
"require-dev": {
"php-parallel-lint/php-parallel-lint": "^1.2",
@@ -1284,9 +1284,9 @@
"description": "PHPStan rules for detecting usage of deprecated classes, methods, properties, constants and traits.",
"support": {
"issues": "https://github.com/phpstan/phpstan-deprecation-rules/issues",
"source": "https://github.com/phpstan/phpstan-deprecation-rules/tree/1.1.3"
"source": "https://github.com/phpstan/phpstan-deprecation-rules/tree/1.1.4"
},
"time": "2023-03-17T07:50:08+00:00"
"time": "2023-08-05T09:02:04+00:00"
},
{
"name": "psr/container",
@@ -2402,16 +2402,16 @@
},
{
"name": "vimeo/psalm",
"version": "5.14.1",
"version": "5.15.0",
"source": {
"type": "git",
"url": "https://github.com/vimeo/psalm.git",
"reference": "b9d355e0829c397b9b3b47d0c0ed042a8a70284d"
"reference": "5c774aca4746caf3d239d9c8cadb9f882ca29352"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/vimeo/psalm/zipball/b9d355e0829c397b9b3b47d0c0ed042a8a70284d",
"reference": "b9d355e0829c397b9b3b47d0c0ed042a8a70284d",
"url": "https://api.github.com/repos/vimeo/psalm/zipball/5c774aca4746caf3d239d9c8cadb9f882ca29352",
"reference": "5c774aca4746caf3d239d9c8cadb9f882ca29352",
"shasum": ""
},
"require": {
@@ -2439,6 +2439,9 @@
"symfony/console": "^4.1.6 || ^5.0 || ^6.0",
"symfony/filesystem": "^5.4 || ^6.0"
},
"conflict": {
"nikic/php-parser": "4.17.0"
},
"provide": {
"psalm/psalm": "self.version"
},
@@ -2502,9 +2505,9 @@
],
"support": {
"issues": "https://github.com/vimeo/psalm/issues",
"source": "https://github.com/vimeo/psalm/tree/5.14.1"
"source": "https://github.com/vimeo/psalm/tree/5.15.0"
},
"time": "2023-08-01T05:16:55+00:00"
"time": "2023-08-20T23:07:30+00:00"
},
{
"name": "webmozart/assert",

View File

@@ -53,3 +53,6 @@ parameters:
# paths:
# - ...
# - ...
-
message: "#^Call to deprecated method #"
path: www/admin/class_test*.php

View File

@@ -764,17 +764,17 @@
},
{
"name": "nikic/php-parser",
"version": "v4.16.0",
"version_normalized": "4.16.0.0",
"version": "v4.17.1",
"version_normalized": "4.17.1.0",
"source": {
"type": "git",
"url": "https://github.com/nikic/PHP-Parser.git",
"reference": "19526a33fb561ef417e822e85f08a00db4059c17"
"reference": "a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/19526a33fb561ef417e822e85f08a00db4059c17",
"reference": "19526a33fb561ef417e822e85f08a00db4059c17",
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d",
"reference": "a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d",
"shasum": ""
},
"require": {
@@ -785,7 +785,7 @@
"ircmaxell/php-yacc": "^0.0.7",
"phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0"
},
"time": "2023-06-25T14:52:30+00:00",
"time": "2023-08-13T19:53:39+00:00",
"bin": [
"bin/php-parse"
],
@@ -817,7 +817,7 @@
],
"support": {
"issues": "https://github.com/nikic/PHP-Parser/issues",
"source": "https://github.com/nikic/PHP-Parser/tree/v4.16.0"
"source": "https://github.com/nikic/PHP-Parser/tree/v4.17.1"
},
"install-path": "../nikic/php-parser"
},
@@ -1021,17 +1021,17 @@
},
{
"name": "phpdocumentor/type-resolver",
"version": "1.7.2",
"version_normalized": "1.7.2.0",
"version": "1.7.3",
"version_normalized": "1.7.3.0",
"source": {
"type": "git",
"url": "https://github.com/phpDocumentor/TypeResolver.git",
"reference": "b2fe4d22a5426f38e014855322200b97b5362c0d"
"reference": "3219c6ee25c9ea71e3d9bbaf39c67c9ebd499419"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/b2fe4d22a5426f38e014855322200b97b5362c0d",
"reference": "b2fe4d22a5426f38e014855322200b97b5362c0d",
"url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/3219c6ee25c9ea71e3d9bbaf39c67c9ebd499419",
"reference": "3219c6ee25c9ea71e3d9bbaf39c67c9ebd499419",
"shasum": ""
},
"require": {
@@ -1050,7 +1050,7 @@
"rector/rector": "^0.13.9",
"vimeo/psalm": "^4.25"
},
"time": "2023-05-30T18:13:47+00:00",
"time": "2023-08-12T11:01:26+00:00",
"type": "library",
"extra": {
"branch-alias": {
@@ -1076,7 +1076,7 @@
"description": "A PSR-5 based resolver of Class names, Types and Structural Element Names",
"support": {
"issues": "https://github.com/phpDocumentor/TypeResolver/issues",
"source": "https://github.com/phpDocumentor/TypeResolver/tree/1.7.2"
"source": "https://github.com/phpDocumentor/TypeResolver/tree/1.7.3"
},
"install-path": "../phpdocumentor/type-resolver"
},
@@ -1129,17 +1129,17 @@
},
{
"name": "phpstan/phpdoc-parser",
"version": "1.23.0",
"version_normalized": "1.23.0.0",
"version": "1.23.1",
"version_normalized": "1.23.1.0",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpdoc-parser.git",
"reference": "a2b24135c35852b348894320d47b3902a94bc494"
"reference": "846ae76eef31c6d7790fac9bc399ecee45160b26"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/a2b24135c35852b348894320d47b3902a94bc494",
"reference": "a2b24135c35852b348894320d47b3902a94bc494",
"url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/846ae76eef31c6d7790fac9bc399ecee45160b26",
"reference": "846ae76eef31c6d7790fac9bc399ecee45160b26",
"shasum": ""
},
"require": {
@@ -1156,7 +1156,7 @@
"phpunit/phpunit": "^9.5",
"symfony/process": "^5.2"
},
"time": "2023-07-23T22:17:56+00:00",
"time": "2023-08-03T16:32:59+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
@@ -1173,23 +1173,23 @@
"description": "PHPDoc parser with support for nullable, intersection and generic types",
"support": {
"issues": "https://github.com/phpstan/phpdoc-parser/issues",
"source": "https://github.com/phpstan/phpdoc-parser/tree/1.23.0"
"source": "https://github.com/phpstan/phpdoc-parser/tree/1.23.1"
},
"install-path": "../phpstan/phpdoc-parser"
},
{
"name": "phpstan/phpstan",
"version": "1.10.26",
"version_normalized": "1.10.26.0",
"version": "1.10.29",
"version_normalized": "1.10.29.0",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpstan.git",
"reference": "5d660cbb7e1b89253a47147ae44044f49832351f"
"reference": "ee5d8f2d3977fb09e55603eee6fb53bdd76ee9c1"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/5d660cbb7e1b89253a47147ae44044f49832351f",
"reference": "5d660cbb7e1b89253a47147ae44044f49832351f",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/ee5d8f2d3977fb09e55603eee6fb53bdd76ee9c1",
"reference": "ee5d8f2d3977fb09e55603eee6fb53bdd76ee9c1",
"shasum": ""
},
"require": {
@@ -1198,7 +1198,7 @@
"conflict": {
"phpstan/phpstan-shim": "*"
},
"time": "2023-07-19T12:44:37+00:00",
"time": "2023-08-14T13:24:11+00:00",
"bin": [
"phpstan",
"phpstan.phar"
@@ -1244,22 +1244,22 @@
},
{
"name": "phpstan/phpstan-deprecation-rules",
"version": "1.1.3",
"version_normalized": "1.1.3.0",
"version": "1.1.4",
"version_normalized": "1.1.4.0",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpstan-deprecation-rules.git",
"reference": "a22b36b955a2e9a3d39fe533b6c1bb5359f9c319"
"reference": "089d8a8258ed0aeefdc7b68b6c3d25572ebfdbaa"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan-deprecation-rules/zipball/a22b36b955a2e9a3d39fe533b6c1bb5359f9c319",
"reference": "a22b36b955a2e9a3d39fe533b6c1bb5359f9c319",
"url": "https://api.github.com/repos/phpstan/phpstan-deprecation-rules/zipball/089d8a8258ed0aeefdc7b68b6c3d25572ebfdbaa",
"reference": "089d8a8258ed0aeefdc7b68b6c3d25572ebfdbaa",
"shasum": ""
},
"require": {
"php": "^7.2 || ^8.0",
"phpstan/phpstan": "^1.10"
"phpstan/phpstan": "^1.10.3"
},
"require-dev": {
"php-parallel-lint/php-parallel-lint": "^1.2",
@@ -1267,7 +1267,7 @@
"phpstan/phpstan-phpunit": "^1.0",
"phpunit/phpunit": "^9.5"
},
"time": "2023-03-17T07:50:08+00:00",
"time": "2023-08-05T09:02:04+00:00",
"type": "phpstan-extension",
"extra": {
"phpstan": {
@@ -1289,7 +1289,7 @@
"description": "PHPStan rules for detecting usage of deprecated classes, methods, properties, constants and traits.",
"support": {
"issues": "https://github.com/phpstan/phpstan-deprecation-rules/issues",
"source": "https://github.com/phpstan/phpstan-deprecation-rules/tree/1.1.3"
"source": "https://github.com/phpstan/phpstan-deprecation-rules/tree/1.1.4"
},
"install-path": "../phpstan/phpstan-deprecation-rules"
},
@@ -2505,17 +2505,17 @@
},
{
"name": "vimeo/psalm",
"version": "5.14.1",
"version_normalized": "5.14.1.0",
"version": "5.15.0",
"version_normalized": "5.15.0.0",
"source": {
"type": "git",
"url": "https://github.com/vimeo/psalm.git",
"reference": "b9d355e0829c397b9b3b47d0c0ed042a8a70284d"
"reference": "5c774aca4746caf3d239d9c8cadb9f882ca29352"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/vimeo/psalm/zipball/b9d355e0829c397b9b3b47d0c0ed042a8a70284d",
"reference": "b9d355e0829c397b9b3b47d0c0ed042a8a70284d",
"url": "https://api.github.com/repos/vimeo/psalm/zipball/5c774aca4746caf3d239d9c8cadb9f882ca29352",
"reference": "5c774aca4746caf3d239d9c8cadb9f882ca29352",
"shasum": ""
},
"require": {
@@ -2543,6 +2543,9 @@
"symfony/console": "^4.1.6 || ^5.0 || ^6.0",
"symfony/filesystem": "^5.4 || ^6.0"
},
"conflict": {
"nikic/php-parser": "4.17.0"
},
"provide": {
"psalm/psalm": "self.version"
},
@@ -2566,7 +2569,7 @@
"ext-curl": "In order to send data to shepherd",
"ext-igbinary": "^2.0.5 is required, used to serialize caching data"
},
"time": "2023-08-01T05:16:55+00:00",
"time": "2023-08-20T23:07:30+00:00",
"bin": [
"psalm",
"psalm-language-server",
@@ -2608,7 +2611,7 @@
],
"support": {
"issues": "https://github.com/vimeo/psalm/issues",
"source": "https://github.com/vimeo/psalm/tree/5.14.1"
"source": "https://github.com/vimeo/psalm/tree/5.15.0"
},
"install-path": "../vimeo/psalm"
},

View File

@@ -128,9 +128,9 @@
'dev_requirement' => true,
),
'nikic/php-parser' => array(
'pretty_version' => 'v4.16.0',
'version' => '4.16.0.0',
'reference' => '19526a33fb561ef417e822e85f08a00db4059c17',
'pretty_version' => 'v4.17.1',
'version' => '4.17.1.0',
'reference' => 'a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d',
'type' => 'library',
'install_path' => __DIR__ . '/../nikic/php-parser',
'aliases' => array(),
@@ -164,9 +164,9 @@
'dev_requirement' => true,
),
'phpdocumentor/type-resolver' => array(
'pretty_version' => '1.7.2',
'version' => '1.7.2.0',
'reference' => 'b2fe4d22a5426f38e014855322200b97b5362c0d',
'pretty_version' => '1.7.3',
'version' => '1.7.3.0',
'reference' => '3219c6ee25c9ea71e3d9bbaf39c67c9ebd499419',
'type' => 'library',
'install_path' => __DIR__ . '/../phpdocumentor/type-resolver',
'aliases' => array(),
@@ -182,27 +182,27 @@
'dev_requirement' => true,
),
'phpstan/phpdoc-parser' => array(
'pretty_version' => '1.23.0',
'version' => '1.23.0.0',
'reference' => 'a2b24135c35852b348894320d47b3902a94bc494',
'pretty_version' => '1.23.1',
'version' => '1.23.1.0',
'reference' => '846ae76eef31c6d7790fac9bc399ecee45160b26',
'type' => 'library',
'install_path' => __DIR__ . '/../phpstan/phpdoc-parser',
'aliases' => array(),
'dev_requirement' => true,
),
'phpstan/phpstan' => array(
'pretty_version' => '1.10.26',
'version' => '1.10.26.0',
'reference' => '5d660cbb7e1b89253a47147ae44044f49832351f',
'pretty_version' => '1.10.29',
'version' => '1.10.29.0',
'reference' => 'ee5d8f2d3977fb09e55603eee6fb53bdd76ee9c1',
'type' => 'library',
'install_path' => __DIR__ . '/../phpstan/phpstan',
'aliases' => array(),
'dev_requirement' => true,
),
'phpstan/phpstan-deprecation-rules' => array(
'pretty_version' => '1.1.3',
'version' => '1.1.3.0',
'reference' => 'a22b36b955a2e9a3d39fe533b6c1bb5359f9c319',
'pretty_version' => '1.1.4',
'version' => '1.1.4.0',
'reference' => '089d8a8258ed0aeefdc7b68b6c3d25572ebfdbaa',
'type' => 'phpstan-extension',
'install_path' => __DIR__ . '/../phpstan/phpstan-deprecation-rules',
'aliases' => array(),
@@ -211,7 +211,7 @@
'psalm/psalm' => array(
'dev_requirement' => true,
'provided' => array(
0 => '5.14.1',
0 => '5.15.0',
),
),
'psr/container' => array(
@@ -365,9 +365,9 @@
'dev_requirement' => true,
),
'vimeo/psalm' => array(
'pretty_version' => '5.14.1',
'version' => '5.14.1.0',
'reference' => 'b9d355e0829c397b9b3b47d0c0ed042a8a70284d',
'pretty_version' => '5.15.0',
'version' => '5.15.0.0',
'reference' => '5c774aca4746caf3d239d9c8cadb9f882ca29352',
'type' => 'library',
'install_path' => __DIR__ . '/../vimeo/psalm',
'aliases' => array(),

View File

@@ -221,7 +221,10 @@ non_empty_class_const_list:
;
class_const:
identifier_maybe_reserved '=' expr { $$ = Node\Const_[$1, $3]; }
T_STRING '=' expr
{ $$ = Node\Const_[new Node\Identifier($1, stackAttributes(#1)), $3]; }
| semi_reserved '=' expr
{ $$ = Node\Const_[new Node\Identifier($1, stackAttributes(#1)), $3]; }
;
inner_statement_list_ex:
@@ -722,6 +725,9 @@ class_statement:
| optional_attributes method_modifiers T_CONST class_const_list semi
{ $$ = new Stmt\ClassConst($4, $2, attributes(), $1);
$this->checkClassConst($$, #2); }
| optional_attributes method_modifiers T_CONST type_expr class_const_list semi
{ $$ = new Stmt\ClassConst($5, $2, attributes(), $1, $4);
$this->checkClassConst($$, #2); }
| optional_attributes method_modifiers T_FUNCTION optional_ref identifier_maybe_reserved '(' parameter_list ')'
optional_return_type method_body
{ $$ = Stmt\ClassMethod[$5, ['type' => $2, 'byRef' => $4, 'params' => $7, 'returnType' => $9, 'stmts' => $10, 'attrGroups' => $1]];
@@ -943,8 +949,8 @@ expr:
;
anonymous_class:
optional_attributes T_CLASS ctor_arguments extends_from implements_list '{' class_statement_list '}'
{ $$ = array(Stmt\Class_[null, ['type' => 0, 'extends' => $4, 'implements' => $5, 'stmts' => $7, 'attrGroups' => $1]], $3);
optional_attributes class_entry_type ctor_arguments extends_from implements_list '{' class_statement_list '}'
{ $$ = array(Stmt\Class_[null, ['type' => $2, 'extends' => $4, 'implements' => $5, 'stmts' => $7, 'attrGroups' => $1]], $3);
$this->checkClass($$[0], -1); }
;
@@ -1040,6 +1046,8 @@ constant:
class_constant:
class_name_or_var T_PAAMAYIM_NEKUDOTAYIM identifier_maybe_reserved
{ $$ = Expr\ClassConstFetch[$1, $3]; }
| class_name_or_var T_PAAMAYIM_NEKUDOTAYIM '{' expr '}'
{ $$ = Expr\ClassConstFetch[$1, $4]; }
/* We interpret an isolated FOO:: as an unfinished class constant fetch. It could also be
an unfinished static property fetch or unfinished scoped call. */
| class_name_or_var T_PAAMAYIM_NEKUDOTAYIM error

View File

@@ -19,6 +19,8 @@ class ClassConst implements PhpParser\Builder
/** @var Node\AttributeGroup[] */
protected $attributeGroups = [];
/** @var Identifier|Node\Name|Node\ComplexType */
protected $type;
/**
* Creates a class constant builder
@@ -116,6 +118,19 @@ class ClassConst implements PhpParser\Builder
return $this;
}
/**
* Sets the constant type.
*
* @param string|Node\Name|Identifier|Node\ComplexType $type
*
* @return $this
*/
public function setType($type) {
$this->type = BuilderHelpers::normalizeType($type);
return $this;
}
/**
* Returns the built class node.
*
@@ -126,7 +141,8 @@ class ClassConst implements PhpParser\Builder
$this->constants,
$this->flags,
$this->attributes,
$this->attributeGroups
$this->attributeGroups,
$this->type
);
}
}

View File

@@ -349,15 +349,15 @@ class BuilderFactory
/**
* Creates a class constant fetch node.
*
* @param string|Name|Expr $class Class name
* @param string|Identifier $name Constant name
* @param string|Name|Expr $class Class name
* @param string|Identifier|Expr $name Constant name
*
* @return Expr\ClassConstFetch
*/
public function classConstFetch($class, $name): Expr\ClassConstFetch {
return new Expr\ClassConstFetch(
BuilderHelpers::normalizeNameOrExpr($class),
BuilderHelpers::normalizeIdentifier($name)
BuilderHelpers::normalizeIdentifierOrExpr($name)
);
}

View File

@@ -19,6 +19,8 @@ class PrintableNewAnonClassNode extends Expr
{
/** @var Node\AttributeGroup[] PHP attribute groups */
public $attrGroups;
/** @var int Modifiers */
public $flags;
/** @var Node\Arg[] Arguments */
public $args;
/** @var null|Node\Name Name of extended class */
@@ -29,11 +31,12 @@ class PrintableNewAnonClassNode extends Expr
public $stmts;
public function __construct(
array $attrGroups, array $args, Node\Name $extends = null, array $implements,
array $attrGroups, int $flags, array $args, Node\Name $extends = null, array $implements,
array $stmts, array $attributes
) {
parent::__construct($attributes);
$this->attrGroups = $attrGroups;
$this->flags = $flags;
$this->args = $args;
$this->extends = $extends;
$this->implements = $implements;
@@ -46,7 +49,7 @@ class PrintableNewAnonClassNode extends Expr
// We don't assert that $class->name is null here, to allow consumers to assign unique names
// to anonymous classes for their own purposes. We simplify ignore the name here.
return new self(
$class->attrGroups, $newNode->args, $class->extends, $class->implements,
$class->attrGroups, $class->flags, $newNode->args, $class->extends, $class->implements,
$class->stmts, $newNode->getAttributes()
);
}
@@ -56,6 +59,6 @@ class PrintableNewAnonClassNode extends Expr
}
public function getSubNodeNames() : array {
return ['attrGroups', 'args', 'extends', 'implements', 'stmts'];
return ['attrGroups', 'flags', 'args', 'extends', 'implements', 'stmts'];
}
}

View File

@@ -10,15 +10,15 @@ class ClassConstFetch extends Expr
{
/** @var Name|Expr Class name */
public $class;
/** @var Identifier|Error Constant name */
/** @var Identifier|Expr|Error Constant name */
public $name;
/**
* Constructs a class const fetch node.
*
* @param Name|Expr $class Class name
* @param string|Identifier|Error $name Constant name
* @param array $attributes Additional attributes
* @param Name|Expr $class Class name
* @param string|Identifier|Expr|Error $name Constant name
* @param array $attributes Additional attributes
*/
public function __construct($class, $name, array $attributes = []) {
$this->attributes = $attributes;
@@ -29,7 +29,7 @@ class ClassConstFetch extends Expr
public function getSubNodeNames() : array {
return ['class', 'name'];
}
public function getType() : string {
return 'Expr_ClassConstFetch';
}

View File

@@ -10,31 +10,36 @@ class ClassConst extends Node\Stmt
public $flags;
/** @var Node\Const_[] Constant declarations */
public $consts;
/** @var Node\AttributeGroup[] */
/** @var Node\AttributeGroup[] PHP attribute groups */
public $attrGroups;
/** @var Node\Identifier|Node\Name|Node\ComplexType|null Type declaration */
public $type;
/**
* Constructs a class const list node.
*
* @param Node\Const_[] $consts Constant declarations
* @param int $flags Modifiers
* @param array $attributes Additional attributes
* @param Node\AttributeGroup[] $attrGroups PHP attribute groups
* @param Node\Const_[] $consts Constant declarations
* @param int $flags Modifiers
* @param array $attributes Additional attributes
* @param Node\AttributeGroup[] $attrGroups PHP attribute groups
* @param null|string|Node\Identifier|Node\Name|Node\ComplexType $type Type declaration
*/
public function __construct(
array $consts,
int $flags = 0,
array $attributes = [],
array $attrGroups = []
array $attrGroups = [],
$type = null
) {
$this->attributes = $attributes;
$this->flags = $flags;
$this->consts = $consts;
$this->attrGroups = $attrGroups;
$this->type = \is_string($type) ? new Node\Identifier($type) : $type;
}
public function getSubNodeNames() : array {
return ['attrGroups', 'flags', 'consts'];
return ['attrGroups', 'flags', 'type', 'consts'];
}
/**

File diff suppressed because it is too large Load Diff

View File

@@ -529,7 +529,7 @@ class Standard extends PrettyPrinterAbstract
}
protected function pExpr_StaticCall(Expr\StaticCall $node) {
return $this->pDereferenceLhs($node->class) . '::'
return $this->pStaticDereferenceLhs($node->class) . '::'
. ($node->name instanceof Expr
? ($node->name instanceof Expr\Variable
? $this->p($node->name)
@@ -606,7 +606,7 @@ class Standard extends PrettyPrinterAbstract
}
protected function pExpr_ClassConstFetch(Expr\ClassConstFetch $node) {
return $this->pDereferenceLhs($node->class) . '::' . $this->p($node->name);
return $this->pStaticDereferenceLhs($node->class) . '::' . $this->pObjectProperty($node->name);
}
protected function pExpr_PropertyFetch(Expr\PropertyFetch $node) {
@@ -618,7 +618,7 @@ class Standard extends PrettyPrinterAbstract
}
protected function pExpr_StaticPropertyFetch(Expr\StaticPropertyFetch $node) {
return $this->pDereferenceLhs($node->class) . '::$' . $this->pObjectProperty($node->name);
return $this->pStaticDereferenceLhs($node->class) . '::$' . $this->pObjectProperty($node->name);
}
protected function pExpr_ShellExec(Expr\ShellExec $node) {
@@ -814,7 +814,9 @@ class Standard extends PrettyPrinterAbstract
protected function pStmt_ClassConst(Stmt\ClassConst $node) {
return $this->pAttrGroups($node->attrGroups)
. $this->pModifiers($node->flags)
. 'const ' . $this->pCommaSeparated($node->consts) . ';';
. 'const '
. (null !== $node->type ? $this->p($node->type) . ' ' : '')
. $this->pCommaSeparated($node->consts) . ';';
}
protected function pStmt_Function(Stmt\Function_ $node) {
@@ -1067,6 +1069,14 @@ class Standard extends PrettyPrinterAbstract
}
}
protected function pStaticDereferenceLhs(Node $node) {
if (!$this->staticDereferenceLhsRequiresParens($node)) {
return $this->p($node);
} else {
return '(' . $this->p($node) . ')';
}
}
protected function pCallLhs(Node $node) {
if (!$this->callLhsRequiresParens($node)) {
return $this->p($node);
@@ -1075,9 +1085,12 @@ class Standard extends PrettyPrinterAbstract
}
}
protected function pNewVariable(Node $node) {
// TODO: This is not fully accurate.
return $this->pDereferenceLhs($node);
protected function pNewVariable(Node $node): string {
if (!$this->newOperandRequiresParens($node)) {
return $this->p($node);
} else {
return '(' . $this->p($node) . ')';
}
}
/**

View File

@@ -21,6 +21,8 @@ abstract class PrettyPrinterAbstract
const FIXUP_BRACED_NAME = 4; // Name operand that may require bracing
const FIXUP_VAR_BRACED_NAME = 5; // Name operand that may require ${} bracing
const FIXUP_ENCAPSED = 6; // Encapsed string part
const FIXUP_NEW = 7; // New/instanceof operand
const FIXUP_STATIC_DEREF_LHS = 8; // LHS of static dereferencing operation
protected $precedenceMap = [
// [precedence, associativity]
@@ -977,6 +979,19 @@ abstract class PrettyPrinterAbstract
return '(' . $this->p($subNode) . ')';
}
break;
case self::FIXUP_STATIC_DEREF_LHS:
if ($this->staticDereferenceLhsRequiresParens($subNode)
&& !$this->origTokens->haveParens($subStartPos, $subEndPos)
) {
return '(' . $this->p($subNode) . ')';
}
break;
case self::FIXUP_NEW:
if ($this->newOperandRequiresParens($subNode)
&& !$this->origTokens->haveParens($subStartPos, $subEndPos)) {
return '(' . $this->p($subNode) . ')';
}
break;
case self::FIXUP_BRACED_NAME:
case self::FIXUP_VAR_BRACED_NAME:
if ($subNode instanceof Expr
@@ -1047,13 +1062,26 @@ abstract class PrettyPrinterAbstract
}
/**
* Determines whether the LHS of a dereferencing operation must be wrapped in parenthesis.
* Determines whether the LHS of an array/object operation must be wrapped in parentheses.
*
* @param Node $node LHS of dereferencing operation
*
* @return bool Whether parentheses are required
*/
protected function dereferenceLhsRequiresParens(Node $node) : bool {
// A constant can occur on the LHS of an array/object deref, but not a static deref.
return $this->staticDereferenceLhsRequiresParens($node)
&& !$node instanceof Expr\ConstFetch;
}
/**
* Determines whether the LHS of a static operation must be wrapped in parentheses.
*
* @param Node $node LHS of dereferencing operation
*
* @return bool Whether parentheses are required
*/
protected function staticDereferenceLhsRequiresParens(Node $node): bool {
return !($node instanceof Expr\Variable
|| $node instanceof Node\Name
|| $node instanceof Expr\ArrayDimFetch
@@ -1066,10 +1094,31 @@ abstract class PrettyPrinterAbstract
|| $node instanceof Expr\StaticCall
|| $node instanceof Expr\Array_
|| $node instanceof Scalar\String_
|| $node instanceof Expr\ConstFetch
|| $node instanceof Expr\ClassConstFetch);
}
/**
* Determines whether an expression used in "new" or "instanceof" requires parentheses.
*
* @param Node $node New or instanceof operand
*
* @return bool Whether parentheses are required
*/
protected function newOperandRequiresParens(Node $node): bool {
if ($node instanceof Node\Name || $node instanceof Expr\Variable) {
return false;
}
if ($node instanceof Expr\ArrayDimFetch || $node instanceof Expr\PropertyFetch ||
$node instanceof Expr\NullsafePropertyFetch
) {
return $this->newOperandRequiresParens($node->var);
}
if ($node instanceof Expr\StaticPropertyFetch) {
return $this->newOperandRequiresParens($node->class);
}
return true;
}
/**
* Print modifiers, including trailing whitespace.
*
@@ -1171,7 +1220,7 @@ abstract class PrettyPrinterAbstract
Expr\PostDec::class => ['var' => self::FIXUP_PREC_LEFT],
Expr\Instanceof_::class => [
'expr' => self::FIXUP_PREC_LEFT,
'class' => self::FIXUP_PREC_RIGHT, // TODO: FIXUP_NEW_VARIABLE
'class' => self::FIXUP_NEW,
],
Expr\Ternary::class => [
'cond' => self::FIXUP_PREC_LEFT,
@@ -1179,10 +1228,13 @@ abstract class PrettyPrinterAbstract
],
Expr\FuncCall::class => ['name' => self::FIXUP_CALL_LHS],
Expr\StaticCall::class => ['class' => self::FIXUP_DEREF_LHS],
Expr\StaticCall::class => ['class' => self::FIXUP_STATIC_DEREF_LHS],
Expr\ArrayDimFetch::class => ['var' => self::FIXUP_DEREF_LHS],
Expr\ClassConstFetch::class => ['var' => self::FIXUP_DEREF_LHS],
Expr\New_::class => ['class' => self::FIXUP_DEREF_LHS], // TODO: FIXUP_NEW_VARIABLE
Expr\ClassConstFetch::class => [
'class' => self::FIXUP_STATIC_DEREF_LHS,
'name' => self::FIXUP_BRACED_NAME,
],
Expr\New_::class => ['class' => self::FIXUP_NEW],
Expr\MethodCall::class => [
'var' => self::FIXUP_DEREF_LHS,
'name' => self::FIXUP_BRACED_NAME,
@@ -1192,7 +1244,7 @@ abstract class PrettyPrinterAbstract
'name' => self::FIXUP_BRACED_NAME,
],
Expr\StaticPropertyFetch::class => [
'class' => self::FIXUP_DEREF_LHS,
'class' => self::FIXUP_STATIC_DEREF_LHS,
'name' => self::FIXUP_VAR_BRACED_NAME,
],
Expr\PropertyFetch::class => [
@@ -1278,6 +1330,7 @@ abstract class PrettyPrinterAbstract
'Param->default' => $stripEquals,
'Stmt_Break->num' => $stripBoth,
'Stmt_Catch->var' => $stripLeft,
'Stmt_ClassConst->type' => $stripRight,
'Stmt_ClassMethod->returnType' => $stripColon,
'Stmt_Class->extends' => ['left' => \T_EXTENDS],
'Stmt_Enum->scalarType' => $stripColon,
@@ -1319,6 +1372,7 @@ abstract class PrettyPrinterAbstract
'Stmt_Break->num' => [\T_BREAK, false, ' ', null],
'Stmt_Catch->var' => [null, false, ' ', null],
'Stmt_ClassMethod->returnType' => [')', false, ' : ', null],
'Stmt_ClassConst->type' => [\T_CONST, false, ' ', null],
'Stmt_Class->extends' => [null, false, ' extends ', null],
'Stmt_Enum->scalarType' => [null, false, ' : ', null],
'Stmt_EnumCase->expr' => [null, false, ' = ', null],
@@ -1508,6 +1562,7 @@ abstract class PrettyPrinterAbstract
'Stmt_ClassMethod->flags' => \T_FUNCTION,
'Stmt_Class->flags' => \T_CLASS,
'Stmt_Property->flags' => \T_VARIABLE,
'Expr_PrintableNewAnonClass->flags' => \T_CLASS,
'Param->flags' => \T_VARIABLE,
//'Stmt_TraitUseAdaptation_Alias->newModifier' => 0, // TODO
];

View File

@@ -181,6 +181,7 @@ final class ContextFactory
$currentNamespace = $this->parseNamespace($tokens);
break;
case T_CLASS:
case T_TRAIT:
// Fast-forward the iterator through the class so that any
// T_USE tokens found within are skipped - these are not
// valid namespace use statements so should be ignored.

View File

@@ -0,0 +1,42 @@
<?php declare(strict_types = 1);
namespace PHPStan\PhpDocParser\Ast\ConstExpr;
use PHPStan\PhpDocParser\Ast\NodeAttributes;
use function sprintf;
use function str_replace;
use function strlen;
use function substr;
class DoctrineConstExprStringNode extends ConstExprStringNode
{
use NodeAttributes;
/** @var string */
public $value;
public function __construct(string $value)
{
parent::__construct($value);
$this->value = $value;
}
public function __toString(): string
{
return self::escape($this->value);
}
public static function unescape(string $value): string
{
// from https://github.com/doctrine/annotations/blob/a9ec7af212302a75d1f92fa65d3abfbd16245a2a/lib/Doctrine/Common/Annotations/DocLexer.php#L103-L107
return str_replace('""', '"', substr($value, 1, strlen($value) - 2));
}
private static function escape(string $value): string
{
// from https://github.com/phpstan/phpdoc-parser/issues/205#issuecomment-1662323656
return sprintf('"%s"', str_replace('"', '""', $value));
}
}

View File

@@ -35,19 +35,20 @@ class Lexer
public const TOKEN_INTEGER = 20;
public const TOKEN_SINGLE_QUOTED_STRING = 21;
public const TOKEN_DOUBLE_QUOTED_STRING = 22;
public const TOKEN_IDENTIFIER = 23;
public const TOKEN_THIS_VARIABLE = 24;
public const TOKEN_VARIABLE = 25;
public const TOKEN_HORIZONTAL_WS = 26;
public const TOKEN_PHPDOC_EOL = 27;
public const TOKEN_OTHER = 28;
public const TOKEN_END = 29;
public const TOKEN_COLON = 30;
public const TOKEN_WILDCARD = 31;
public const TOKEN_OPEN_CURLY_BRACKET = 32;
public const TOKEN_CLOSE_CURLY_BRACKET = 33;
public const TOKEN_NEGATED = 34;
public const TOKEN_ARROW = 35;
public const TOKEN_DOCTRINE_ANNOTATION_STRING = 23;
public const TOKEN_IDENTIFIER = 24;
public const TOKEN_THIS_VARIABLE = 25;
public const TOKEN_VARIABLE = 26;
public const TOKEN_HORIZONTAL_WS = 27;
public const TOKEN_PHPDOC_EOL = 28;
public const TOKEN_OTHER = 29;
public const TOKEN_END = 30;
public const TOKEN_COLON = 31;
public const TOKEN_WILDCARD = 32;
public const TOKEN_OPEN_CURLY_BRACKET = 33;
public const TOKEN_CLOSE_CURLY_BRACKET = 34;
public const TOKEN_NEGATED = 35;
public const TOKEN_ARROW = 36;
public const TOKEN_LABELS = [
self::TOKEN_REFERENCE => '\'&\'',
@@ -79,6 +80,7 @@ class Lexer
self::TOKEN_INTEGER => 'TOKEN_INTEGER',
self::TOKEN_SINGLE_QUOTED_STRING => 'TOKEN_SINGLE_QUOTED_STRING',
self::TOKEN_DOUBLE_QUOTED_STRING => 'TOKEN_DOUBLE_QUOTED_STRING',
self::TOKEN_DOCTRINE_ANNOTATION_STRING => 'TOKEN_DOCTRINE_ANNOTATION_STRING',
self::TOKEN_IDENTIFIER => 'type',
self::TOKEN_THIS_VARIABLE => '\'$this\'',
self::TOKEN_VARIABLE => 'variable',
@@ -180,6 +182,7 @@ class Lexer
if ($this->parseDoctrineAnnotations) {
$patterns[self::TOKEN_DOCTRINE_TAG] = '@[a-z_\\\\][a-z0-9_\:\\\\]*[a-z_][a-z0-9_]*';
$patterns[self::TOKEN_DOCTRINE_ANNOTATION_STRING] = '"(?:""|[^"])*+"';
}
// anything but TOKEN_CLOSE_PHPDOC or TOKEN_HORIZONTAL_WS or TOKEN_EOL

View File

@@ -23,6 +23,9 @@ class ConstExprParser
/** @var bool */
private $useIndexAttributes;
/** @var bool */
private $parseDoctrineStrings;
/**
* @param array{lines?: bool, indexes?: bool} $usedAttributes
*/
@@ -36,6 +39,24 @@ class ConstExprParser
$this->quoteAwareConstExprString = $quoteAwareConstExprString;
$this->useLinesAttributes = $usedAttributes['lines'] ?? false;
$this->useIndexAttributes = $usedAttributes['indexes'] ?? false;
$this->parseDoctrineStrings = false;
}
/**
* @internal
*/
public function toDoctrine(): self
{
$self = new self(
$this->unescapeStrings,
$this->quoteAwareConstExprString,
[
'lines' => $this->useLinesAttributes,
'indexes' => $this->useIndexAttributes,
]
);
$self->parseDoctrineStrings = true;
return $self;
}
public function parse(TokenIterator $tokens, bool $trimStrings = false): Ast\ConstExpr\ConstExprNode
@@ -66,7 +87,41 @@ class ConstExprParser
);
}
if ($this->parseDoctrineStrings && $tokens->isCurrentTokenType(Lexer::TOKEN_DOCTRINE_ANNOTATION_STRING)) {
$value = $tokens->currentTokenValue();
$tokens->next();
return $this->enrichWithAttributes(
$tokens,
new Ast\ConstExpr\DoctrineConstExprStringNode(Ast\ConstExpr\DoctrineConstExprStringNode::unescape($value)),
$startLine,
$startIndex
);
}
if ($tokens->isCurrentTokenType(Lexer::TOKEN_SINGLE_QUOTED_STRING, Lexer::TOKEN_DOUBLE_QUOTED_STRING)) {
if ($this->parseDoctrineStrings) {
if ($tokens->isCurrentTokenType(Lexer::TOKEN_SINGLE_QUOTED_STRING)) {
throw new ParserException(
$tokens->currentTokenValue(),
$tokens->currentTokenType(),
$tokens->currentTokenOffset(),
Lexer::TOKEN_DOUBLE_QUOTED_STRING,
null,
$tokens->currentTokenLine()
);
}
$value = $tokens->currentTokenValue();
$tokens->next();
return $this->enrichWithAttributes(
$tokens,
$this->parseDoctrineString($value, $tokens),
$startLine,
$startIndex
);
}
$value = $tokens->currentTokenValue();
$type = $tokens->currentTokenType();
if ($trimStrings) {
@@ -214,6 +269,23 @@ class ConstExprParser
}
/**
* This method is supposed to be called with TokenIterator after reading TOKEN_DOUBLE_QUOTED_STRING and shifting
* to the next token.
*/
public function parseDoctrineString(string $text, TokenIterator $tokens): Ast\ConstExpr\DoctrineConstExprStringNode
{
// Because of how Lexer works, a valid Doctrine string
// can consist of a sequence of TOKEN_DOUBLE_QUOTED_STRING and TOKEN_DOCTRINE_ANNOTATION_STRING
while ($tokens->isCurrentTokenType(Lexer::TOKEN_DOUBLE_QUOTED_STRING, Lexer::TOKEN_DOCTRINE_ANNOTATION_STRING)) {
$text .= $tokens->currentTokenValue();
$tokens->next();
}
return new Ast\ConstExpr\DoctrineConstExprStringNode(Ast\ConstExpr\DoctrineConstExprStringNode::unescape($text));
}
private function parseArrayItem(TokenIterator $tokens): Ast\ConstExpr\ConstExprArrayItemNode
{
$startLine = $tokens->currentTokenLine();

View File

@@ -35,6 +35,9 @@ class PhpDocParser
/** @var ConstExprParser */
private $constantExprParser;
/** @var ConstExprParser */
private $doctrineConstantExprParser;
/** @var bool */
private $requireWhitespaceBeforeDescription;
@@ -68,6 +71,7 @@ class PhpDocParser
{
$this->typeParser = $typeParser;
$this->constantExprParser = $constantExprParser;
$this->doctrineConstantExprParser = $constantExprParser->toDoctrine();
$this->requireWhitespaceBeforeDescription = $requireWhitespaceBeforeDescription;
$this->preserveTypeAliasesWithInvalidTypes = $preserveTypeAliasesWithInvalidTypes;
$this->parseDoctrineAnnotations = $parseDoctrineAnnotations;
@@ -688,7 +692,7 @@ class PhpDocParser
);
try {
$constExpr = $this->constantExprParser->parse($tokens, true);
$constExpr = $this->doctrineConstantExprParser->parse($tokens, true);
if ($constExpr instanceof Ast\ConstExpr\ConstExprArrayNode) {
throw $exception;
}
@@ -750,14 +754,15 @@ class PhpDocParser
$key = new Ast\ConstExpr\ConstExprIntegerNode(str_replace('_', '', $tokens->currentTokenValue()));
$tokens->next();
} elseif ($tokens->isCurrentTokenType(Lexer::TOKEN_SINGLE_QUOTED_STRING)) {
$key = new Ast\ConstExpr\QuoteAwareConstExprStringNode(StringUnescaper::unescapeString($tokens->currentTokenValue()), Ast\ConstExpr\QuoteAwareConstExprStringNode::SINGLE_QUOTED);
} elseif ($tokens->isCurrentTokenType(Lexer::TOKEN_DOCTRINE_ANNOTATION_STRING)) {
$key = new Ast\ConstExpr\DoctrineConstExprStringNode(Ast\ConstExpr\DoctrineConstExprStringNode::unescape($tokens->currentTokenValue()));
$tokens->next();
} elseif ($tokens->isCurrentTokenType(Lexer::TOKEN_DOUBLE_QUOTED_STRING)) {
$key = new Ast\ConstExpr\QuoteAwareConstExprStringNode(StringUnescaper::unescapeString($tokens->currentTokenValue()), Ast\ConstExpr\QuoteAwareConstExprStringNode::DOUBLE_QUOTED);
$value = $tokens->currentTokenValue();
$tokens->next();
$key = $this->doctrineConstantExprParser->parseDoctrineString($value, $tokens);
} else {
$currentTokenValue = $tokens->currentTokenValue();
@@ -786,7 +791,7 @@ class PhpDocParser
}
$tokens->rollback();
$constExpr = $this->constantExprParser->parse($tokens, true);
$constExpr = $this->doctrineConstantExprParser->parse($tokens, true);
if (!$constExpr instanceof Ast\ConstExpr\ConstFetchNode) {
throw new ParserException(
$tokens->currentTokenValue(),

View File

@@ -36,3 +36,48 @@ In case you don't own the code which you want to be considered deprecated, use [
/** @deprecated */
class ThirdPartyClass {}
```
## Custom deprecated scopes
Usage of deprecated code is not reported in code that is also deprecated:
```php
/** @deprecated */
function doFoo(): void
{
// not reported:
anotherDeprecatedFunction();
}
```
If you have [a different way](https://github.com/phpstan/phpstan-deprecation-rules/issues/64) of marking code that calls deprecated symbols on purpose and you don't want these calls to be reported either, you can write an extension by implementing the [`DeprecatedScopeResolver`](https://github.com/phpstan/phpstan-deprecation-rules/blob/1.1.x/src/Rules/Deprecations/DeprecatedScopeResolver.php) interface.
For example if you mark your PHPUnit tests that test deprecated code with `@group legacy`, you can implement the extension this way:
```php
class GroupLegacyScopeResolver implements DeprecatedScopeResolver
{
public function isScopeDeprecated(Scope $scope): bool
{
$function = $scope->getFunction();
return $function !== null
&& $function->getDocComment() !== null
&& strpos($function->getDocComment(), '@group legacy') !== false;
}
}
```
And register it in your [configuration file](https://phpstan.org/config-reference):
```neon
services:
-
class: GroupLegacyScopeResolver
tags:
- phpstan.deprecations.deprecatedScopeResolver
```
[Learn more about Scope](https://phpstan.org/developing-extensions/scope), a core concept for implementing custom PHPStan extensions.

View File

@@ -1,8 +1,9 @@
{
"require-dev": {
"consistence-community/coding-standard": "^3.10",
"dealerdirect/phpcodesniffer-composer-installer": "^0.7.0",
"slevomat/coding-standard": "^7.0"
"consistence-community/coding-standard": "^3.11.0",
"dealerdirect/phpcodesniffer-composer-installer": "^1.0.0",
"slevomat/coding-standard": "^8.8.0",
"squizlabs/php_codesniffer": "^3.5.3"
},
"config": {
"allow-plugins": {

View File

@@ -4,35 +4,35 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "4485bbedba7bcc71ace5f69dbb9b6c47",
"content-hash": "e69c1916405a7e3c8001c1b609a0ee61",
"packages": [],
"packages-dev": [
{
"name": "consistence-community/coding-standard",
"version": "3.11.1",
"version": "3.11.3",
"source": {
"type": "git",
"url": "https://github.com/consistence-community/coding-standard.git",
"reference": "4632fead8c9ee8f50044fcbce9f66c797b34c0df"
"reference": "f38e06327d5bf80ff5ff523a2c05e623b5e8d8b1"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/consistence-community/coding-standard/zipball/4632fead8c9ee8f50044fcbce9f66c797b34c0df",
"reference": "4632fead8c9ee8f50044fcbce9f66c797b34c0df",
"url": "https://api.github.com/repos/consistence-community/coding-standard/zipball/f38e06327d5bf80ff5ff523a2c05e623b5e8d8b1",
"reference": "f38e06327d5bf80ff5ff523a2c05e623b5e8d8b1",
"shasum": ""
},
"require": {
"php": ">=7.4",
"slevomat/coding-standard": "~7.0",
"squizlabs/php_codesniffer": "~3.6.0"
"php": "~8.0",
"slevomat/coding-standard": "~8.0",
"squizlabs/php_codesniffer": "~3.7.0"
},
"replace": {
"consistence/coding-standard": "3.10.*"
},
"require-dev": {
"phing/phing": "2.16.4",
"php-parallel-lint/php-parallel-lint": "1.3.0",
"phpunit/phpunit": "9.5.4"
"phing/phing": "2.17.0",
"php-parallel-lint/php-parallel-lint": "1.3.1",
"phpunit/phpunit": "9.5.10"
},
"type": "library",
"autoload": {
@@ -70,41 +70,44 @@
],
"support": {
"issues": "https://github.com/consistence-community/coding-standard/issues",
"source": "https://github.com/consistence-community/coding-standard/tree/3.11.1"
"source": "https://github.com/consistence-community/coding-standard/tree/3.11.3"
},
"time": "2021-05-03T18:13:22+00:00"
"time": "2023-03-27T14:55:41+00:00"
},
{
"name": "dealerdirect/phpcodesniffer-composer-installer",
"version": "v0.7.2",
"version": "v1.0.0",
"source": {
"type": "git",
"url": "https://github.com/Dealerdirect/phpcodesniffer-composer-installer.git",
"reference": "1c968e542d8843d7cd71de3c5c9c3ff3ad71a1db"
"url": "https://github.com/PHPCSStandards/composer-installer.git",
"reference": "4be43904336affa5c2f70744a348312336afd0da"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Dealerdirect/phpcodesniffer-composer-installer/zipball/1c968e542d8843d7cd71de3c5c9c3ff3ad71a1db",
"reference": "1c968e542d8843d7cd71de3c5c9c3ff3ad71a1db",
"url": "https://api.github.com/repos/PHPCSStandards/composer-installer/zipball/4be43904336affa5c2f70744a348312336afd0da",
"reference": "4be43904336affa5c2f70744a348312336afd0da",
"shasum": ""
},
"require": {
"composer-plugin-api": "^1.0 || ^2.0",
"php": ">=5.3",
"php": ">=5.4",
"squizlabs/php_codesniffer": "^2.0 || ^3.1.0 || ^4.0"
},
"require-dev": {
"composer/composer": "*",
"ext-json": "*",
"ext-zip": "*",
"php-parallel-lint/php-parallel-lint": "^1.3.1",
"phpcompatibility/php-compatibility": "^9.0"
"phpcompatibility/php-compatibility": "^9.0",
"yoast/phpunit-polyfills": "^1.0"
},
"type": "composer-plugin",
"extra": {
"class": "Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\Plugin"
"class": "PHPCSStandards\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\Plugin"
},
"autoload": {
"psr-4": {
"Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\": "src/"
"PHPCSStandards\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
@@ -120,7 +123,7 @@
},
{
"name": "Contributors",
"homepage": "https://github.com/Dealerdirect/phpcodesniffer-composer-installer/graphs/contributors"
"homepage": "https://github.com/PHPCSStandards/composer-installer/graphs/contributors"
}
],
"description": "PHP_CodeSniffer Standards Composer Installer Plugin",
@@ -144,23 +147,23 @@
"tests"
],
"support": {
"issues": "https://github.com/dealerdirect/phpcodesniffer-composer-installer/issues",
"source": "https://github.com/dealerdirect/phpcodesniffer-composer-installer"
"issues": "https://github.com/PHPCSStandards/composer-installer/issues",
"source": "https://github.com/PHPCSStandards/composer-installer"
},
"time": "2022-02-04T12:51:07+00:00"
"time": "2023-01-05T11:28:13+00:00"
},
{
"name": "phpstan/phpdoc-parser",
"version": "1.5.1",
"version": "1.20.4",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpdoc-parser.git",
"reference": "981cc368a216c988e862a75e526b6076987d1b50"
"reference": "7d568c87a9df9c5f7e8b5f075fc469aa8cb0a4cd"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/981cc368a216c988e862a75e526b6076987d1b50",
"reference": "981cc368a216c988e862a75e526b6076987d1b50",
"url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/7d568c87a9df9c5f7e8b5f075fc469aa8cb0a4cd",
"reference": "7d568c87a9df9c5f7e8b5f075fc469aa8cb0a4cd",
"shasum": ""
},
"require": {
@@ -170,6 +173,7 @@
"php-parallel-lint/php-parallel-lint": "^1.2",
"phpstan/extension-installer": "^1.0",
"phpstan/phpstan": "^1.5",
"phpstan/phpstan-phpunit": "^1.1",
"phpstan/phpstan-strict-rules": "^1.0",
"phpunit/phpunit": "^9.5",
"symfony/process": "^5.2"
@@ -189,48 +193,48 @@
"description": "PHPDoc parser with support for nullable, intersection and generic types",
"support": {
"issues": "https://github.com/phpstan/phpdoc-parser/issues",
"source": "https://github.com/phpstan/phpdoc-parser/tree/1.5.1"
"source": "https://github.com/phpstan/phpdoc-parser/tree/1.20.4"
},
"time": "2022-05-05T11:32:40+00:00"
"time": "2023-05-02T09:19:37+00:00"
},
{
"name": "slevomat/coding-standard",
"version": "7.2.1",
"version": "8.12.0",
"source": {
"type": "git",
"url": "https://github.com/slevomat/coding-standard.git",
"reference": "aff06ae7a84e4534bf6f821dc982a93a5d477c90"
"reference": "cc04334ed0ce5a251389112fbd2dbe1dbc931ae8"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/slevomat/coding-standard/zipball/aff06ae7a84e4534bf6f821dc982a93a5d477c90",
"reference": "aff06ae7a84e4534bf6f821dc982a93a5d477c90",
"url": "https://api.github.com/repos/slevomat/coding-standard/zipball/cc04334ed0ce5a251389112fbd2dbe1dbc931ae8",
"reference": "cc04334ed0ce5a251389112fbd2dbe1dbc931ae8",
"shasum": ""
},
"require": {
"dealerdirect/phpcodesniffer-composer-installer": "^0.6.2 || ^0.7",
"dealerdirect/phpcodesniffer-composer-installer": "^0.6.2 || ^0.7 || ^1.0",
"php": "^7.2 || ^8.0",
"phpstan/phpdoc-parser": "^1.5.1",
"squizlabs/php_codesniffer": "^3.6.2"
"phpstan/phpdoc-parser": ">=1.20.0 <1.21.0",
"squizlabs/php_codesniffer": "^3.7.1"
},
"require-dev": {
"phing/phing": "2.17.3",
"phing/phing": "2.17.4",
"php-parallel-lint/php-parallel-lint": "1.3.2",
"phpstan/phpstan": "1.4.10|1.7.1",
"phpstan/phpstan-deprecation-rules": "1.0.0",
"phpstan/phpstan-phpunit": "1.0.0|1.1.1",
"phpstan/phpstan-strict-rules": "1.2.3",
"phpunit/phpunit": "7.5.20|8.5.21|9.5.20"
"phpstan/phpstan": "1.10.15",
"phpstan/phpstan-deprecation-rules": "1.1.3",
"phpstan/phpstan-phpunit": "1.3.11",
"phpstan/phpstan-strict-rules": "1.5.1",
"phpunit/phpunit": "7.5.20|8.5.21|9.6.8|10.1.3"
},
"type": "phpcodesniffer-standard",
"extra": {
"branch-alias": {
"dev-master": "7.x-dev"
"dev-master": "8.x-dev"
}
},
"autoload": {
"psr-4": {
"SlevomatCodingStandard\\": "SlevomatCodingStandard"
"SlevomatCodingStandard\\": "SlevomatCodingStandard/"
}
},
"notification-url": "https://packagist.org/downloads/",
@@ -238,9 +242,13 @@
"MIT"
],
"description": "Slevomat Coding Standard for PHP_CodeSniffer complements Consistence Coding Standard by providing sniffs with additional checks.",
"keywords": [
"dev",
"phpcs"
],
"support": {
"issues": "https://github.com/slevomat/coding-standard/issues",
"source": "https://github.com/slevomat/coding-standard/tree/7.2.1"
"source": "https://github.com/slevomat/coding-standard/tree/8.12.0"
},
"funding": [
{
@@ -252,20 +260,20 @@
"type": "tidelift"
}
],
"time": "2022-05-25T10:58:12+00:00"
"time": "2023-05-14T20:06:01+00:00"
},
{
"name": "squizlabs/php_codesniffer",
"version": "3.6.2",
"version": "3.7.2",
"source": {
"type": "git",
"url": "https://github.com/squizlabs/PHP_CodeSniffer.git",
"reference": "5e4e71592f69da17871dba6e80dd51bce74a351a"
"reference": "ed8e00df0a83aa96acf703f8c2979ff33341f879"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/5e4e71592f69da17871dba6e80dd51bce74a351a",
"reference": "5e4e71592f69da17871dba6e80dd51bce74a351a",
"url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/ed8e00df0a83aa96acf703f8c2979ff33341f879",
"reference": "ed8e00df0a83aa96acf703f8c2979ff33341f879",
"shasum": ""
},
"require": {
@@ -301,14 +309,15 @@
"homepage": "https://github.com/squizlabs/PHP_CodeSniffer",
"keywords": [
"phpcs",
"standards"
"standards",
"static analysis"
],
"support": {
"issues": "https://github.com/squizlabs/PHP_CodeSniffer/issues",
"source": "https://github.com/squizlabs/PHP_CodeSniffer",
"wiki": "https://github.com/squizlabs/PHP_CodeSniffer/wiki"
},
"time": "2021-12-12T21:44:58+00:00"
"time": "2023-02-22T23:07:41+00:00"
}
],
"aliases": [],

View File

@@ -7,7 +7,7 @@
],
"require": {
"php": "^7.2 || ^8.0",
"phpstan/phpstan": "^1.10"
"phpstan/phpstan": "^1.10.3"
},
"require-dev": {
"php-parallel-lint/php-parallel-lint": "^1.2",

View File

@@ -2,8 +2,19 @@ parameters:
deprecationRulesInstalled: true
services:
-
class: PHPStan\Rules\Deprecations\DeprecatedClassHelper
-
class: PHPStan\Rules\Deprecations\DeprecatedClassHelper
-
class: PHPStan\DependencyInjection\LazyDeprecatedScopeResolverProvider
-
class: PHPStan\Rules\Deprecations\DeprecatedScopeHelper
factory: @PHPStan\DependencyInjection\LazyDeprecatedScopeResolverProvider::get
-
class: PHPStan\Rules\Deprecations\DefaultDeprecatedScopeResolver
tags:
- phpstan.deprecations.deprecatedScopeResolver
rules:
- PHPStan\Rules\Deprecations\AccessDeprecatedPropertyRule

View File

@@ -0,0 +1,33 @@
<?php declare(strict_types = 1);
namespace PHPStan\DependencyInjection;
use PHPStan\Rules\Deprecations\DeprecatedScopeHelper;
final class LazyDeprecatedScopeResolverProvider
{
public const EXTENSION_TAG = 'phpstan.deprecations.deprecatedScopeResolver';
/** @var Container */
private $container;
/** @var DeprecatedScopeHelper */
private $scopeHelper;
public function __construct(Container $container)
{
$this->container = $container;
}
public function get(): DeprecatedScopeHelper
{
if ($this->scopeHelper === null) {
$this->scopeHelper = new DeprecatedScopeHelper(
$this->container->getServicesByTag(self::EXTENSION_TAG)
);
}
return $this->scopeHelper;
}
}

View File

@@ -21,9 +21,13 @@ class AccessDeprecatedPropertyRule implements Rule
/** @var ReflectionProvider */
private $reflectionProvider;
public function __construct(ReflectionProvider $reflectionProvider)
/** @var DeprecatedScopeHelper */
private $deprecatedScopeHelper;
public function __construct(ReflectionProvider $reflectionProvider, DeprecatedScopeHelper $deprecatedScopeHelper)
{
$this->reflectionProvider = $reflectionProvider;
$this->deprecatedScopeHelper = $deprecatedScopeHelper;
}
public function getNodeType(): string
@@ -33,7 +37,7 @@ class AccessDeprecatedPropertyRule implements Rule
public function processNode(Node $node, Scope $scope): array
{
if (DeprecatedScopeHelper::isScopeDeprecated($scope)) {
if ($this->deprecatedScopeHelper->isScopeDeprecated($scope)) {
return [];
}

View File

@@ -28,10 +28,14 @@ class AccessDeprecatedStaticPropertyRule implements Rule
/** @var RuleLevelHelper */
private $ruleLevelHelper;
public function __construct(ReflectionProvider $reflectionProvider, RuleLevelHelper $ruleLevelHelper)
/** @var DeprecatedScopeHelper */
private $deprecatedScopeHelper;
public function __construct(ReflectionProvider $reflectionProvider, RuleLevelHelper $ruleLevelHelper, DeprecatedScopeHelper $deprecatedScopeHelper)
{
$this->reflectionProvider = $reflectionProvider;
$this->ruleLevelHelper = $ruleLevelHelper;
$this->deprecatedScopeHelper = $deprecatedScopeHelper;
}
public function getNodeType(): string
@@ -41,7 +45,7 @@ class AccessDeprecatedStaticPropertyRule implements Rule
public function processNode(Node $node, Scope $scope): array
{
if (DeprecatedScopeHelper::isScopeDeprecated($scope)) {
if ($this->deprecatedScopeHelper->isScopeDeprecated($scope)) {
return [];
}

View File

@@ -20,9 +20,13 @@ class CallToDeprecatedFunctionRule implements Rule
/** @var ReflectionProvider */
private $reflectionProvider;
public function __construct(ReflectionProvider $reflectionProvider)
/** @var DeprecatedScopeHelper */
private $deprecatedScopeHelper;
public function __construct(ReflectionProvider $reflectionProvider, DeprecatedScopeHelper $deprecatedScopeHelper)
{
$this->reflectionProvider = $reflectionProvider;
$this->deprecatedScopeHelper = $deprecatedScopeHelper;
}
public function getNodeType(): string
@@ -32,7 +36,7 @@ class CallToDeprecatedFunctionRule implements Rule
public function processNode(Node $node, Scope $scope): array
{
if (DeprecatedScopeHelper::isScopeDeprecated($scope)) {
if ($this->deprecatedScopeHelper->isScopeDeprecated($scope)) {
return [];
}

View File

@@ -21,9 +21,13 @@ class CallToDeprecatedMethodRule implements Rule
/** @var ReflectionProvider */
private $reflectionProvider;
public function __construct(ReflectionProvider $reflectionProvider)
/** @var DeprecatedScopeHelper */
private $deprecatedScopeHelper;
public function __construct(ReflectionProvider $reflectionProvider, DeprecatedScopeHelper $deprecatedScopeHelper)
{
$this->reflectionProvider = $reflectionProvider;
$this->deprecatedScopeHelper = $deprecatedScopeHelper;
}
public function getNodeType(): string
@@ -33,7 +37,7 @@ class CallToDeprecatedMethodRule implements Rule
public function processNode(Node $node, Scope $scope): array
{
if (DeprecatedScopeHelper::isScopeDeprecated($scope)) {
if ($this->deprecatedScopeHelper->isScopeDeprecated($scope)) {
return [];
}

View File

@@ -28,10 +28,14 @@ class CallToDeprecatedStaticMethodRule implements Rule
/** @var RuleLevelHelper */
private $ruleLevelHelper;
public function __construct(ReflectionProvider $reflectionProvider, RuleLevelHelper $ruleLevelHelper)
/** @var DeprecatedScopeHelper */
private $deprecatedScopeHelper;
public function __construct(ReflectionProvider $reflectionProvider, RuleLevelHelper $ruleLevelHelper, DeprecatedScopeHelper $deprecatedScopeHelper)
{
$this->reflectionProvider = $reflectionProvider;
$this->ruleLevelHelper = $ruleLevelHelper;
$this->deprecatedScopeHelper = $deprecatedScopeHelper;
}
public function getNodeType(): string
@@ -41,7 +45,7 @@ class CallToDeprecatedStaticMethodRule implements Rule
public function processNode(Node $node, Scope $scope): array
{
if (DeprecatedScopeHelper::isScopeDeprecated($scope)) {
if ($this->deprecatedScopeHelper->isScopeDeprecated($scope)) {
return [];
}

View File

@@ -0,0 +1,30 @@
<?php declare(strict_types = 1);
namespace PHPStan\Rules\Deprecations;
use PHPStan\Analyser\Scope;
final class DefaultDeprecatedScopeResolver implements DeprecatedScopeResolver
{
public function isScopeDeprecated(Scope $scope): bool
{
$class = $scope->getClassReflection();
if ($class !== null && $class->isDeprecated()) {
return true;
}
$trait = $scope->getTraitReflection();
if ($trait !== null && $trait->isDeprecated()) {
return true;
}
$function = $scope->getFunction();
if ($function !== null && $function->isDeprecated()->yes()) {
return true;
}
return false;
}
}

View File

@@ -7,21 +7,23 @@ use PHPStan\Analyser\Scope;
class DeprecatedScopeHelper
{
public static function isScopeDeprecated(Scope $scope): bool
/** @var DeprecatedScopeResolver[] */
private $resolvers;
/**
* @param DeprecatedScopeResolver[] $checkers
*/
public function __construct(array $checkers)
{
$class = $scope->getClassReflection();
if ($class !== null && $class->isDeprecated()) {
return true;
}
$this->resolvers = $checkers;
}
$trait = $scope->getTraitReflection();
if ($trait !== null && $trait->isDeprecated()) {
return true;
}
$function = $scope->getFunction();
if ($function !== null && $function->isDeprecated()->yes()) {
return true;
public function isScopeDeprecated(Scope $scope): bool
{
foreach ($this->resolvers as $checker) {
if ($checker->isScopeDeprecated($scope)) {
return true;
}
}
return false;

View File

@@ -0,0 +1,27 @@
<?php declare(strict_types = 1);
namespace PHPStan\Rules\Deprecations;
use PHPStan\Analyser\Scope;
/**
* This is the interface for custom deprecated scope resolvers.
*
* To register it in the configuration file use the `phpstan.deprecations.deprecatedScopeResolver` service tag:
*
* ```
* services:
* -
* class: App\PHPStan\MyExtension
* tags:
* - phpstan.deprecations.deprecatedScopeResolver
* ```
*
* @api
*/
interface DeprecatedScopeResolver
{
public function isScopeDeprecated(Scope $scope): bool;
}

View File

@@ -28,10 +28,14 @@ class FetchingClassConstOfDeprecatedClassRule implements Rule
/** @var RuleLevelHelper */
private $ruleLevelHelper;
public function __construct(ReflectionProvider $reflectionProvider, RuleLevelHelper $ruleLevelHelper)
/** @var DeprecatedScopeHelper */
private $deprecatedScopeHelper;
public function __construct(ReflectionProvider $reflectionProvider, RuleLevelHelper $ruleLevelHelper, DeprecatedScopeHelper $deprecatedScopeHelper)
{
$this->reflectionProvider = $reflectionProvider;
$this->ruleLevelHelper = $ruleLevelHelper;
$this->deprecatedScopeHelper = $deprecatedScopeHelper;
}
public function getNodeType(): string
@@ -41,7 +45,7 @@ class FetchingClassConstOfDeprecatedClassRule implements Rule
public function processNode(Node $node, Scope $scope): array
{
if (DeprecatedScopeHelper::isScopeDeprecated($scope)) {
if ($this->deprecatedScopeHelper->isScopeDeprecated($scope)) {
return [];
}

View File

@@ -19,12 +19,16 @@ class FetchingDeprecatedConstRule implements Rule
/** @var ReflectionProvider */
private $reflectionProvider;
/** @var DeprecatedScopeHelper */
private $deprecatedScopeHelper;
/** @var array<string,string> */
private $deprecatedConstants = [];
public function __construct(ReflectionProvider $reflectionProvider)
public function __construct(ReflectionProvider $reflectionProvider, DeprecatedScopeHelper $deprecatedScopeHelper)
{
$this->reflectionProvider = $reflectionProvider;
$this->deprecatedScopeHelper = $deprecatedScopeHelper;
// phpcs:ignore SlevomatCodingStandard.ControlStructures.EarlyExit.EarlyExitNotUsed
if (PHP_VERSION_ID >= 70300) {
@@ -40,7 +44,7 @@ class FetchingDeprecatedConstRule implements Rule
public function processNode(Node $node, Scope $scope): array
{
if (DeprecatedScopeHelper::isScopeDeprecated($scope)) {
if ($this->deprecatedScopeHelper->isScopeDeprecated($scope)) {
return [];
}

View File

@@ -19,9 +19,13 @@ class ImplementationOfDeprecatedInterfaceRule implements Rule
/** @var ReflectionProvider */
private $reflectionProvider;
public function __construct(ReflectionProvider $reflectionProvider)
/** @var DeprecatedScopeHelper */
private $deprecatedScopeHelper;
public function __construct(ReflectionProvider $reflectionProvider, DeprecatedScopeHelper $deprecatedScopeHelper)
{
$this->reflectionProvider = $reflectionProvider;
$this->deprecatedScopeHelper = $deprecatedScopeHelper;
}
public function getNodeType(): string
@@ -31,7 +35,7 @@ class ImplementationOfDeprecatedInterfaceRule implements Rule
public function processNode(Node $node, Scope $scope): array
{
if (DeprecatedScopeHelper::isScopeDeprecated($scope)) {
if ($this->deprecatedScopeHelper->isScopeDeprecated($scope)) {
return [];
}

View File

@@ -19,9 +19,13 @@ class InheritanceOfDeprecatedClassRule implements Rule
/** @var ReflectionProvider */
private $reflectionProvider;
public function __construct(ReflectionProvider $reflectionProvider)
/** @var DeprecatedScopeHelper */
private $deprecatedScopeHelper;
public function __construct(ReflectionProvider $reflectionProvider, DeprecatedScopeHelper $deprecatedScopeHelper)
{
$this->reflectionProvider = $reflectionProvider;
$this->deprecatedScopeHelper = $deprecatedScopeHelper;
}
public function getNodeType(): string
@@ -31,7 +35,7 @@ class InheritanceOfDeprecatedClassRule implements Rule
public function processNode(Node $node, Scope $scope): array
{
if (DeprecatedScopeHelper::isScopeDeprecated($scope)) {
if ($this->deprecatedScopeHelper->isScopeDeprecated($scope)) {
return [];
}

View File

@@ -26,10 +26,14 @@ class InstantiationOfDeprecatedClassRule implements Rule
/** @var RuleLevelHelper */
private $ruleLevelHelper;
public function __construct(ReflectionProvider $reflectionProvider, RuleLevelHelper $ruleLevelHelper)
/** @var DeprecatedScopeHelper */
private $deprecatedScopeHelper;
public function __construct(ReflectionProvider $reflectionProvider, RuleLevelHelper $ruleLevelHelper, DeprecatedScopeHelper $deprecatedScopeHelper)
{
$this->reflectionProvider = $reflectionProvider;
$this->ruleLevelHelper = $ruleLevelHelper;
$this->deprecatedScopeHelper = $deprecatedScopeHelper;
}
public function getNodeType(): string
@@ -39,7 +43,7 @@ class InstantiationOfDeprecatedClassRule implements Rule
public function processNode(Node $node, Scope $scope): array
{
if (DeprecatedScopeHelper::isScopeDeprecated($scope)) {
if ($this->deprecatedScopeHelper->isScopeDeprecated($scope)) {
return [];
}

View File

@@ -18,9 +18,13 @@ class TypeHintDeprecatedInClassMethodSignatureRule implements Rule
/** @var DeprecatedClassHelper */
private $deprecatedClassHelper;
public function __construct(DeprecatedClassHelper $deprecatedClassHelper)
/** @var DeprecatedScopeHelper */
private $deprecatedScopeHelper;
public function __construct(DeprecatedClassHelper $deprecatedClassHelper, DeprecatedScopeHelper $deprecatedScopeHelper)
{
$this->deprecatedClassHelper = $deprecatedClassHelper;
$this->deprecatedScopeHelper = $deprecatedScopeHelper;
}
public function getNodeType(): string
@@ -30,7 +34,7 @@ class TypeHintDeprecatedInClassMethodSignatureRule implements Rule
public function processNode(Node $node, Scope $scope): array
{
if (DeprecatedScopeHelper::isScopeDeprecated($scope)) {
if ($this->deprecatedScopeHelper->isScopeDeprecated($scope)) {
return [];
}

View File

@@ -18,9 +18,13 @@ class TypeHintDeprecatedInClosureSignatureRule implements Rule
/** @var DeprecatedClassHelper */
private $deprecatedClassHelper;
public function __construct(DeprecatedClassHelper $deprecatedClassHelper)
/** @var DeprecatedScopeHelper */
private $deprecatedScopeHelper;
public function __construct(DeprecatedClassHelper $deprecatedClassHelper, DeprecatedScopeHelper $deprecatedScopeHelper)
{
$this->deprecatedClassHelper = $deprecatedClassHelper;
$this->deprecatedScopeHelper = $deprecatedScopeHelper;
}
public function getNodeType(): string
@@ -30,7 +34,7 @@ class TypeHintDeprecatedInClosureSignatureRule implements Rule
public function processNode(Node $node, Scope $scope): array
{
if (DeprecatedScopeHelper::isScopeDeprecated($scope)) {
if ($this->deprecatedScopeHelper->isScopeDeprecated($scope)) {
return [];
}

View File

@@ -19,9 +19,13 @@ class TypeHintDeprecatedInFunctionSignatureRule implements Rule
/** @var DeprecatedClassHelper */
private $deprecatedClassHelper;
public function __construct(DeprecatedClassHelper $deprecatedClassHelper)
/** @var DeprecatedScopeHelper */
private $deprecatedScopeHelper;
public function __construct(DeprecatedClassHelper $deprecatedClassHelper, DeprecatedScopeHelper $deprecatedScopeHelper)
{
$this->deprecatedClassHelper = $deprecatedClassHelper;
$this->deprecatedScopeHelper = $deprecatedScopeHelper;
}
public function getNodeType(): string
@@ -31,7 +35,7 @@ class TypeHintDeprecatedInFunctionSignatureRule implements Rule
public function processNode(Node $node, Scope $scope): array
{
if (DeprecatedScopeHelper::isScopeDeprecated($scope)) {
if ($this->deprecatedScopeHelper->isScopeDeprecated($scope)) {
return [];
}

View File

@@ -14,6 +14,14 @@ use function sprintf;
class UsageOfDeprecatedCastRule implements Rule
{
/** @var DeprecatedScopeHelper */
private $deprecatedScopeHelper;
public function __construct(DeprecatedScopeHelper $deprecatedScopeHelper)
{
$this->deprecatedScopeHelper = $deprecatedScopeHelper;
}
public function getNodeType(): string
{
return Cast::class;
@@ -21,7 +29,7 @@ class UsageOfDeprecatedCastRule implements Rule
public function processNode(Node $node, Scope $scope): array
{
if (DeprecatedScopeHelper::isScopeDeprecated($scope)) {
if ($this->deprecatedScopeHelper->isScopeDeprecated($scope)) {
return [];
}

View File

@@ -20,9 +20,13 @@ class UsageOfDeprecatedTraitRule implements Rule
/** @var ReflectionProvider */
private $reflectionProvider;
public function __construct(ReflectionProvider $reflectionProvider)
/** @var DeprecatedScopeHelper */
private $deprecatedScopeHelper;
public function __construct(ReflectionProvider $reflectionProvider, DeprecatedScopeHelper $deprecatedScopeHelper)
{
$this->reflectionProvider = $reflectionProvider;
$this->deprecatedScopeHelper = $deprecatedScopeHelper;
}
public function getNodeType(): string
@@ -32,7 +36,7 @@ class UsageOfDeprecatedTraitRule implements Rule
public function processNode(Node $node, Scope $scope): array
{
if (DeprecatedScopeHelper::isScopeDeprecated($scope)) {
if ($this->deprecatedScopeHelper->isScopeDeprecated($scope)) {
return [];
}

Binary file not shown.

View File

@@ -1,16 +1,16 @@
-----BEGIN PGP SIGNATURE-----
iQIzBAABCgAdFiEEynwsejDI6OEnSoR2UcZzBf/C5cAFAmS32poACgkQUcZzBf/C
5cCvmg/+JJmyX663fa+FHy7ED2SexVuChivpbp82dyLx1gRAl15rtNG4zjxNRfnW
6GpsysMhKqrN7p6xur18ZkLqdFKAjeNnpTunnh/ADetcrs8wzLNyAy7luQtyXAuj
SOv5f/Yitg9yvZ2GHrbzchQuSjkbUR2KroBYsRhwVTH7pMIgdvysRBYiENfbz350
n91WOCApDnVCygzEhBbhkwA/xklJnUxkRJX3AlbbCwES9K64ELyGd0BqJ1Ohy2a7
4cFjwRJq9/tXf99fyncamN8xyBdvYBXSNRNMPYcjKqKIZCOePlR8Q3b7nt154w+e
w2qnAevOB4dYzJaSjwJlaVQYR1YIQ7NlYkGboONq/lrtJlEejDdiRmGvgHZ8nSYW
Ob2JwqgYDfUPfsnXAwXM+whpUNJi30MDB7MSw3SiDlyw690HheT/DCKOJ9yNUiOB
TSGkbIGW/ASett78gowjwniYdryE5ufUPwZbkSaFC3CDysHfs6Jgc+lxe3wnOHtD
WyPl1TqDRNuLOZ26TgxI3gGEYqMcVDYQfmuiOakoebHx6j0bpvyEaP51j0/JFpu6
okKulXgC1DUluKFWMPhobPQRZ8zC29macnU74JvmJIiUhfiP2Pl16D+XcjFW++zH
EDEghcCdgz0pIF6UI5j02rbNAfu7Oo685pnYeXq0DexgXjqoFOE=
=NF4z
iQIzBAABCgAdFiEEynwsejDI6OEnSoR2UcZzBf/C5cAFAmTaKt8ACgkQUcZzBf/C
5cBjcA//TOyzijQKOpxHJ73Y0F9j5169FDZE95exCWDYxOLkFOr8V/lvjZhXESqh
g5qSn93q4BjAq60pjocZe/sBHxPpyxVLmvWzZcwozP0fauZABsPAXuhPrGtuBn7Q
v7ykPPycayBUiCNNcUi00x5d9+5V4Fvd3URDg/eC/rpBb7O3mA2UWcQrR+bx178z
v91dnztyti8U8PL4ol+BZr56g7mm75wy/oFhDgr4h4xtmH6RzDiCa3DUyKyNqUIG
KqAILtxhcSYRFoDWlakwKGY6lGQrrcrcwKZL+d9yEdXxrgsV0qw5y58b3Tmz39vR
A2CapzNmpuUaSIH7jjdiFak6fAKwKsgZysKRilbMLoON2AS+NZmezqMsX2invrPl
QgURzFJZ2iP2i+83Z02nIB3DiHAZOapQwBEfqhdPq6yG5SNFh5ZvLsSPbbEI2jRL
b29ehUYag3ccxe7tvU2nwOWljMg6zKKAVFTxyGRQ7PW5P7FFxYZ6KCW+25wFj2/d
sXfOAnr+TWmftNkCs8HFkKV2EyxPcslHk3hKfBFh+3GqL1UtcvQdM0NxfwQmklG3
FZ5J+P7UmbZhqXdNIXXtxUa9N7pnEJ1b9sL8DpXd0lCSqjiHwH+euXauouiVcLXd
jl2N0U5M/Qc8tIfeGCxEYm81wTzLi44XcHv62yPqST3UzZ0jYEI=
=jF08
-----END PGP SIGNATURE-----

View File

@@ -39,6 +39,9 @@
"symfony/console": "^4.1.6 || ^5.0 || ^6.0",
"symfony/filesystem": "^5.4 || ^6.0"
},
"conflict": {
"nikic/php-parser": "4.17.0"
},
"provide": {
"psalm/psalm": "self.version"
},

View File

@@ -1679,10 +1679,10 @@ return [
'DOMDocument::getElementsByTagName' => ['DOMNodeList', 'qualifiedName'=>'string'],
'DOMDocument::getElementsByTagNameNS' => ['DOMNodeList', 'namespace'=>'?string', 'localName'=>'string'],
'DOMDocument::importNode' => ['DOMNode|false', 'node'=>'DOMNode', 'deep='=>'bool'],
'DOMDocument::load' => ['DOMDocument|bool', 'filename'=>'string', 'options='=>'int'],
'DOMDocument::load' => ['bool', 'filename'=>'string', 'options='=>'int'],
'DOMDocument::loadHTML' => ['bool', 'source'=>'non-empty-string', 'options='=>'int'],
'DOMDocument::loadHTMLFile' => ['bool', 'filename'=>'string', 'options='=>'int'],
'DOMDocument::loadXML' => ['DOMDocument|bool', 'source'=>'non-empty-string', 'options='=>'int'],
'DOMDocument::loadXML' => ['bool', 'source'=>'non-empty-string', 'options='=>'int'],
'DOMDocument::normalizeDocument' => ['void'],
'DOMDocument::registerNodeClass' => ['bool', 'baseClass'=>'string', 'extendedClass'=>'?string'],
'DOMDocument::relaxNGValidate' => ['bool', 'filename'=>'string'],
@@ -7825,10 +7825,10 @@ return [
'mysqli::begin_transaction' => ['bool', 'flags='=>'int', 'name='=>'?string'],
'mysqli::change_user' => ['bool', 'username'=>'string', 'password'=>'string', 'database'=>'?string'],
'mysqli::character_set_name' => ['string'],
'mysqli::close' => ['bool'],
'mysqli::close' => ['true'],
'mysqli::commit' => ['bool', 'flags='=>'int', 'name='=>'?string'],
'mysqli::connect' => ['bool', 'hostname='=>'string|null', 'username='=>'string|null', 'password='=>'string|null', 'database='=>'string|null', 'port='=>'int|null', 'socket='=>'string|null'],
'mysqli::debug' => ['bool', 'options'=>'string'],
'mysqli::debug' => ['true', 'options'=>'string'],
'mysqli::dump_debug_info' => ['bool'],
'mysqli::escape_string' => ['string', 'string'=>'string'],
'mysqli::execute_query' => ['mysqli_result|bool', 'query'=>'non-empty-string', 'params='=>'list<mixed>|null'],
@@ -7857,7 +7857,7 @@ return [
'mysqli::select_db' => ['bool', 'database'=>'string'],
'mysqli::set_charset' => ['bool', 'charset'=>'string'],
'mysqli::set_opt' => ['bool', 'option'=>'int', 'value'=>'string|int'],
'mysqli::ssl_set' => ['bool', 'key'=>'?string', 'certificate'=>'?string', 'ca_certificate'=>'?string', 'ca_path'=>'?string', 'cipher_algos'=>'?string'],
'mysqli::ssl_set' => ['true', 'key'=>'?string', 'certificate'=>'?string', 'ca_certificate'=>'?string', 'ca_path'=>'?string', 'cipher_algos'=>'?string'],
'mysqli::stat' => ['string|false'],
'mysqli::stmt_init' => ['mysqli_stmt'],
'mysqli::store_result' => ['mysqli_result|false', 'mode='=>'int'],
@@ -7903,7 +7903,7 @@ return [
'mysqli_fetch_object' => ['object|false|null', 'result'=>'mysqli_result', 'class='=>'string', 'constructor_args='=>'array'],
'mysqli_fetch_row' => ['list<null|int|float|string>|false|null', 'result'=>'mysqli_result'],
'mysqli_field_count' => ['int', 'mysql'=>'mysqli'],
'mysqli_field_seek' => ['bool', 'result'=>'mysqli_result', 'index'=>'int'],
'mysqli_field_seek' => ['true', 'result'=>'mysqli_result', 'index'=>'int'],
'mysqli_field_tell' => ['int', 'result'=>'mysqli_result'],
'mysqli_free_result' => ['void', 'result'=>'mysqli_result'],
'mysqli_get_cache_stats' => ['array|false'],
@@ -7957,7 +7957,7 @@ return [
'mysqli_result::fetch_fields' => ['list<object{name:non-empty-string,orgname:string,table:string,orgtable:string,max_length:int,length:int,charsetnr:int,flags:int,type:int,decimals:int,db:string,def:string,catalog:string}>'],
'mysqli_result::fetch_object' => ['object|false|null', 'class='=>'string', 'constructor_args='=>'array'],
'mysqli_result::fetch_row' => ['list<null|int|float|string>|false|null'],
'mysqli_result::field_seek' => ['bool', 'index'=>'int'],
'mysqli_result::field_seek' => ['true', 'index'=>'int'],
'mysqli_result::free' => ['void'],
'mysqli_result::free_result' => ['void'],
'mysqli_rollback' => ['bool', 'mysql'=>'mysqli', 'flags='=>'int', 'name='=>'?string'],
@@ -7981,7 +7981,7 @@ return [
'mysqli_stmt::attr_set' => ['bool', 'attribute'=>'int', 'value'=>'int'],
'mysqli_stmt::bind_param' => ['bool', 'types'=>'string', '&var'=>'mixed', '&...vars='=>'mixed'],
'mysqli_stmt::bind_result' => ['bool', '&w_var1'=>'', '&...w_vars='=>''],
'mysqli_stmt::close' => ['bool'],
'mysqli_stmt::close' => ['true'],
'mysqli_stmt::data_seek' => ['void', 'offset'=>'int'],
'mysqli_stmt::execute' => ['bool', 'params='=>'list<mixed>|null'],
'mysqli_stmt::fetch' => ['bool|null'],

View File

@@ -100,6 +100,22 @@ return [
'old' => ['DOMNodeList', 'namespace'=>'string', 'localName'=>'string'],
'new' => ['DOMNodeList', 'namespace'=>'?string', 'localName'=>'string'],
],
'DOMDocument::load' => [
'old' => ['DOMDocument|bool', 'filename'=>'string', 'options='=>'int'],
'new' => ['bool', 'filename'=>'string', 'options='=>'int'],
],
'DOMDocument::loadXML' => [
'old' => ['DOMDocument|bool', 'source'=>'non-empty-string', 'options='=>'int'],
'new' => ['bool', 'source'=>'non-empty-string', 'options='=>'int'],
],
'DOMDocument::loadHTML' => [
'old' => ['DOMDocument|bool', 'source'=>'non-empty-string', 'options='=>'int'],
'new' => ['bool', 'source'=>'non-empty-string', 'options='=>'int'],
],
'DOMDocument::loadHTMLFile' => [
'old' => ['DOMDocument|bool', 'filename'=>'string', 'options='=>'int'],
'new' => ['bool', 'filename'=>'string', 'options='=>'int'],
],
'DOMImplementation::createDocument' => [
'old' => ['DOMDocument|false', 'namespace='=>'string', 'qualifiedName='=>'string', 'doctype='=>'DOMDocumentType'],
'new' => ['DOMDocument|false', 'namespace='=>'?string', 'qualifiedName='=>'string', 'doctype='=>'?DOMDocumentType'],
@@ -224,6 +240,14 @@ return [
'old' => ['string', 'locale'=>'string', 'displayLocale='=>'string'],
'new' => ['string', 'locale'=>'string', 'displayLocale='=>'?string'],
],
'mysqli_field_seek' => [
'old' => ['bool', 'result'=>'mysqli_result', 'index'=>'int'],
'new' => ['true', 'result'=>'mysqli_result', 'index'=>'int'],
],
'mysqli_result::field_seek' => [
'old' => ['bool', 'index'=>'int'],
'new' => ['true', 'index'=>'int'],
],
'mysqli_stmt::__construct' => [
'old' => ['void', 'mysql'=>'mysqli', 'query='=>'string'],
'new' => ['void', 'mysql'=>'mysqli', 'query='=>'?string'],

View File

@@ -94,6 +94,10 @@ return [
'old' => ['int|false', 'fields'=>'array<array-key, null|scalar|Stringable>', 'separator='=>'string', 'enclosure='=>'string', 'escape='=>'string'],
'new' => ['int|false', 'fields'=>'array<array-key, null|scalar|Stringable>', 'separator='=>'string', 'enclosure='=>'string', 'escape='=>'string', 'eol='=>'string'],
],
'hash_pbkdf2' => [
'old' => ['non-empty-string', 'algo'=>'string', 'password'=>'string', 'salt'=>'string', 'iterations'=>'int', 'length='=>'int', 'binary='=>'bool'],
'new' => ['non-empty-string', 'algo'=>'string', 'password'=>'string', 'salt'=>'string', 'iterations'=>'int', 'length='=>'int', 'binary='=>'bool', 'options=' => 'array'],
],
'finfo_buffer' => [
'old' => ['string|false', 'finfo'=>'resource', 'string'=>'string', 'flags='=>'int', 'context='=>'resource'],
'new' => ['string|false', 'finfo'=>'finfo', 'string'=>'string', 'flags='=>'int', 'context='=>'resource'],

View File

@@ -49,10 +49,6 @@ return [
'old' => ['bool', '&rw_array'=>'array', 'flags='=>'int'],
'new' => ['true', '&rw_array'=>'array', 'flags='=>'int'],
],
'hash_pbkdf2' => [
'old' => ['non-empty-string', 'algo'=>'string', 'password'=>'string', 'salt'=>'string', 'iterations'=>'int', 'length='=>'int', 'binary='=>'bool'],
'new' => ['non-empty-string', 'algo'=>'string', 'password'=>'string', 'salt'=>'string', 'iterations'=>'int', 'length='=>'int', 'binary='=>'bool', 'options=' => 'array'],
],
'imap_setflag_full' => [
'old' => ['bool', 'imap'=>'IMAP\Connection', 'sequence'=>'string', 'flag'=>'string', 'options='=>'int'],
'new' => ['true', 'imap'=>'IMAP\Connection', 'sequence'=>'string', 'flag'=>'string', 'options='=>'int'],

View File

@@ -733,8 +733,8 @@ return [
'DOMDocument::getElementsByTagNameNS' => ['DOMNodeList', 'namespace'=>'string', 'localName'=>'string'],
'DOMDocument::importNode' => ['DOMNode|false', 'node'=>'DOMNode', 'deep='=>'bool'],
'DOMDocument::load' => ['DOMDocument|bool', 'filename'=>'string', 'options='=>'int'],
'DOMDocument::loadHTML' => ['bool', 'source'=>'non-empty-string', 'options='=>'int'],
'DOMDocument::loadHTMLFile' => ['bool', 'filename'=>'string', 'options='=>'int'],
'DOMDocument::loadHTML' => ['DOMDocument|bool', 'source'=>'non-empty-string', 'options='=>'int'],
'DOMDocument::loadHTMLFile' => ['DOMDocument|bool', 'filename'=>'string', 'options='=>'int'],
'DOMDocument::loadXML' => ['DOMDocument|bool', 'source'=>'non-empty-string', 'options='=>'int'],
'DOMDocument::normalizeDocument' => ['void'],
'DOMDocument::registerNodeClass' => ['bool', 'baseClass'=>'string', 'extendedClass'=>'?string'],
@@ -12737,10 +12737,10 @@ return [
'mysqli::begin_transaction' => ['bool', 'flags='=>'int', 'name='=>'string'],
'mysqli::change_user' => ['bool', 'username'=>'string', 'password'=>'string', 'database'=>'?string'],
'mysqli::character_set_name' => ['string'],
'mysqli::close' => ['bool'],
'mysqli::close' => ['true'],
'mysqli::commit' => ['bool', 'flags='=>'int', 'name='=>'string'],
'mysqli::connect' => ['null|false', 'hostname='=>'string', 'username='=>'string', 'password='=>'string', 'database='=>'string', 'port='=>'int', 'socket='=>'string'],
'mysqli::debug' => ['bool', 'options'=>'string'],
'mysqli::debug' => ['true', 'options'=>'string'],
'mysqli::dump_debug_info' => ['bool'],
'mysqli::escape_string' => ['string', 'string'=>'string'],
'mysqli::get_charset' => ['object'],
@@ -12768,7 +12768,7 @@ return [
'mysqli::select_db' => ['bool', 'database'=>'string'],
'mysqli::set_charset' => ['bool', 'charset'=>'string'],
'mysqli::set_opt' => ['bool', 'option'=>'int', 'value'=>'string|int'],
'mysqli::ssl_set' => ['bool', 'key'=>'?string', 'certificate'=>'?string', 'ca_certificate'=>'?string', 'ca_path'=>'?string', 'cipher_algos'=>'?string'],
'mysqli::ssl_set' => ['true', 'key'=>'?string', 'certificate'=>'?string', 'ca_certificate'=>'?string', 'ca_path'=>'?string', 'cipher_algos'=>'?string'],
'mysqli::stat' => ['string|false'],
'mysqli::stmt_init' => ['mysqli_stmt'],
'mysqli::store_result' => ['mysqli_result|false', 'mode='=>'int'],
@@ -12889,7 +12889,7 @@ return [
'mysqli_stmt::attr_set' => ['bool', 'attribute'=>'int', 'value'=>'int'],
'mysqli_stmt::bind_param' => ['bool', 'types'=>'string', '&var'=>'mixed', '&...vars='=>'mixed'],
'mysqli_stmt::bind_result' => ['bool', '&w_var1'=>'', '&...w_vars='=>''],
'mysqli_stmt::close' => ['bool'],
'mysqli_stmt::close' => ['true'],
'mysqli_stmt::data_seek' => ['void', 'offset'=>'int'],
'mysqli_stmt::execute' => ['bool'],
'mysqli_stmt::fetch' => ['bool|null'],

View File

@@ -699,6 +699,7 @@ final class Codebase
);
}
/** @psalm-mutation-free */
public function classExtendsOrImplements(string $fq_class_name, string $possible_parent): bool
{
return $this->classlikes->classExtends($fq_class_name, $possible_parent)

View File

@@ -42,7 +42,6 @@ use Psalm\Progress\Progress;
use Psalm\Progress\VoidProgress;
use RuntimeException;
use SimpleXMLElement;
use SimpleXMLIterator;
use Symfony\Component\Filesystem\Path;
use Throwable;
use UnexpectedValueException;
@@ -728,8 +727,6 @@ class Config
$this->eventDispatcher = new EventDispatcher();
$this->universal_object_crates = [
strtolower(stdClass::class),
strtolower(SimpleXMLElement::class),
strtolower(SimpleXMLIterator::class),
];
}
@@ -1030,7 +1027,6 @@ class Config
/**
* @param non-empty-string $file_contents
* @psalm-suppress MixedMethodCall
* @psalm-suppress MixedAssignment
* @psalm-suppress MixedArgument
* @psalm-suppress MixedPropertyFetch
@@ -1161,12 +1157,13 @@ class Config
}
if (isset($config_xml['autoloader'])) {
$autoloader_path = $config->base_dir . DIRECTORY_SEPARATOR . $config_xml['autoloader'];
$autoloader = (string) $config_xml['autoloader'];
$autoloader_path = $config->base_dir . DIRECTORY_SEPARATOR . $autoloader;
if (!file_exists($autoloader_path)) {
// in here for legacy reasons where people put absolute paths but psalm resolved it relative
if ($config_xml['autoloader']->__toString()[0] === '/') {
$autoloader_path = $config_xml['autoloader']->__toString();
if ($autoloader[0] === '/') {
$autoloader_path = $autoloader;
}
if (!file_exists($autoloader_path)) {
@@ -1312,7 +1309,7 @@ class Config
);
}
if (isset($config_xml->fileExtensions)) {
if (isset($config_xml->fileExtensions->extension)) {
$config->file_extensions = [];
$config->loadFileExtensions($config_xml->fileExtensions->extension);
@@ -1336,7 +1333,6 @@ class Config
if (isset($config_xml->ignoreExceptions)) {
if (isset($config_xml->ignoreExceptions->class)) {
/** @var SimpleXMLElement $exception_class */
foreach ($config_xml->ignoreExceptions->class as $exception_class) {
$exception_name = (string) $exception_class['name'];
$global_attribute_text = (string) $exception_class['onlyGlobalScope'];
@@ -1347,7 +1343,6 @@ class Config
}
}
if (isset($config_xml->ignoreExceptions->classAndDescendants)) {
/** @var SimpleXMLElement $exception_class */
foreach ($config_xml->ignoreExceptions->classAndDescendants as $exception_class) {
$exception_name = (string) $exception_class['name'];
$global_attribute_text = (string) $exception_class['onlyGlobalScope'];
@@ -1401,7 +1396,6 @@ class Config
// this plugin loading system borrows heavily from etsy/phan
if (isset($config_xml->plugins)) {
if (isset($config_xml->plugins->plugin)) {
/** @var SimpleXMLElement $plugin */
foreach ($config_xml->plugins->plugin as $plugin) {
$plugin_file_name = (string) $plugin['filename'];
@@ -1413,7 +1407,6 @@ class Config
}
}
if (isset($config_xml->plugins->pluginClass)) {
/** @var SimpleXMLElement $plugin */
foreach ($config_xml->plugins->pluginClass as $plugin) {
$plugin_class_name = $plugin['class'];
// any child elements are used as plugin configuration
@@ -1429,21 +1422,23 @@ class Config
if (isset($config_xml->issueHandlers)) {
foreach ($config_xml->issueHandlers as $issue_handlers) {
/** @var SimpleXMLElement $issue_handler */
foreach ($issue_handlers->children() as $key => $issue_handler) {
if ($key === 'PluginIssue') {
$custom_class_name = (string) $issue_handler['name'];
/** @var string $key */
$config->issue_handlers[$custom_class_name] = IssueHandler::loadFromXMLElement(
$issue_handler,
$base_dir,
);
} else {
/** @var string $key */
$config->issue_handlers[$key] = IssueHandler::loadFromXMLElement(
$issue_handler,
$base_dir,
);
$issue_handler_children = $issue_handlers->children();
if ($issue_handler_children) {
foreach ($issue_handler_children as $key => $issue_handler) {
if ($key === 'PluginIssue') {
$custom_class_name = (string)$issue_handler['name'];
/** @var string $key */
$config->issue_handlers[$custom_class_name] = IssueHandler::loadFromXMLElement(
$issue_handler,
$base_dir,
);
} else {
/** @var string $key */
$config->issue_handlers[$key] = IssueHandler::loadFromXMLElement(
$issue_handler,
$base_dir,
);
}
}
}
}
@@ -2263,6 +2258,10 @@ class Config
$stubsDir . 'SPL.phpstub',
];
if ($codebase->analysis_php_version_id >= 7_04_00) {
$this->internal_stubs[] = $stubsDir . 'Php74.phpstub';
}
if ($codebase->analysis_php_version_id >= 8_00_00) {
$this->internal_stubs[] = $stubsDir . 'CoreGenericAttributes.phpstub';
$this->internal_stubs[] = $stubsDir . 'Php80.phpstub';

View File

@@ -389,7 +389,6 @@ class FileFilter
if ($e->directory) {
$config['directory'] = [];
/** @var SimpleXMLElement $directory */
foreach ($e->directory as $directory) {
$config['directory'][] = [
'name' => (string) $directory['name'],
@@ -402,7 +401,6 @@ class FileFilter
if ($e->file) {
$config['file'] = [];
/** @var SimpleXMLElement $file */
foreach ($e->file as $file) {
$config['file'][]['name'] = (string) $file['name'];
}
@@ -410,7 +408,6 @@ class FileFilter
if ($e->referencedClass) {
$config['referencedClass'] = [];
/** @var SimpleXMLElement $referenced_class */
foreach ($e->referencedClass as $referenced_class) {
$config['referencedClass'][]['name'] = strtolower((string)$referenced_class['name']);
}
@@ -418,7 +415,6 @@ class FileFilter
if ($e->referencedMethod) {
$config['referencedMethod'] = [];
/** @var SimpleXMLElement $referenced_method */
foreach ($e->referencedMethod as $referenced_method) {
$config['referencedMethod'][]['name'] = (string)$referenced_method['name'];
}
@@ -426,7 +422,6 @@ class FileFilter
if ($e->referencedFunction) {
$config['referencedFunction'] = [];
/** @var SimpleXMLElement $referenced_function */
foreach ($e->referencedFunction as $referenced_function) {
$config['referencedFunction'][]['name'] = strtolower((string)$referenced_function['name']);
}
@@ -434,7 +429,6 @@ class FileFilter
if ($e->referencedProperty) {
$config['referencedProperty'] = [];
/** @var SimpleXMLElement $referenced_property */
foreach ($e->referencedProperty as $referenced_property) {
$config['referencedProperty'][]['name'] = strtolower((string)$referenced_property['name']);
}
@@ -442,7 +436,6 @@ class FileFilter
if ($e->referencedConstant) {
$config['referencedConstant'] = [];
/** @var SimpleXMLElement $referenced_constant */
foreach ($e->referencedConstant as $referenced_constant) {
$config['referencedConstant'][]['name'] = strtolower((string)$referenced_constant['name']);
}
@@ -450,8 +443,6 @@ class FileFilter
if ($e->referencedVariable) {
$config['referencedVariable'] = [];
/** @var SimpleXMLElement $referenced_variable */
foreach ($e->referencedVariable as $referenced_variable) {
$config['referencedVariable'][]['name'] = strtolower((string)$referenced_variable['name']);
}

View File

@@ -38,9 +38,10 @@ final class IssueHandler
}
}
/** @var SimpleXMLElement $error_level */
foreach ($e->errorLevel as $error_level) {
$handler->custom_levels[] = ErrorLevelFileFilter::loadFromXMLElement($error_level, $base_dir, true);
if (isset($e->errorLevel)) {
foreach ($e->errorLevel as $error_level) {
$handler->custom_levels[] = ErrorLevelFileFilter::loadFromXMLElement($error_level, $base_dir, true);
}
}
return $handler;

View File

@@ -28,7 +28,6 @@ final class ProjectFileFilter extends FileFilter
throw new ConfigException('Cannot nest ignoreFiles inside itself');
}
/** @var SimpleXMLElement $e->ignoreFiles */
$filter->file_filter = static::loadFromXMLElement($e->ignoreFiles, $base_dir, false);
}

View File

@@ -12,7 +12,8 @@ use Psalm\Internal\Scope\LoopScope;
use Psalm\Internal\Type\AssertionReconciler;
use Psalm\Storage\FunctionLikeStorage;
use Psalm\Type\Atomic\DependentType;
use Psalm\Type\Atomic\TArray;
use Psalm\Type\Atomic\TIntRange;
use Psalm\Type\Atomic\TNull;
use Psalm\Type\Union;
use RuntimeException;
@@ -868,10 +869,19 @@ final class Context
public function defineGlobals(): void
{
$globals = [
// not sure why this is declared here again, see VariableFetchAnalyzer
'$argv' => new Union([
new TArray([Type::getInt(), Type::getString()]),
Type::getNonEmptyListAtomic(Type::getString()),
new TNull(),
], [
'ignore_nullable_issues' => true,
]),
'$argc' => new Union([
new TIntRange(1, null),
new TNull(),
], [
'ignore_nullable_issues' => true,
]),
'$argc' => Type::getInt(),
];
$config = Config::getInstance();

View File

@@ -200,6 +200,10 @@ class ProjectAnalyzer
UnnecessaryVarAnnotation::class,
];
private const PHP_VERSION_REGEX = '^(0|[1-9]\d*)\.(0|[1-9]\d*)(?:\..*)?$';
private const PHP_SUPPORTED_VERSIONS_REGEX = '^(5\.[456]|7\.[01234]|8\.[0123])(\..*)?$';
/**
* @param array<ReportOptions> $generated_report_options
*/
@@ -1179,8 +1183,16 @@ class ProjectAnalyzer
*/
public function setPhpVersion(string $version, string $source): void
{
if (!preg_match('/^(5\.[456]|7\.[01234]|8\.[012])(\..*)?$/', $version)) {
throw new UnexpectedValueException('Expecting a version number in the format x.y');
if (!preg_match('/' . self::PHP_VERSION_REGEX . '/', $version)) {
throw new UnexpectedValueException('Expecting a version number in the format x.y or x.y.z');
}
if (!preg_match('/' . self::PHP_SUPPORTED_VERSIONS_REGEX . '/', $version)) {
throw new UnexpectedValueException(
'Psalm supports PHP version ">=5.4". The specified version '
. $version
. " is either not supported or doesn't exist.",
);
}
[$php_major_version, $php_minor_version] = explode('.', $version);

View File

@@ -755,20 +755,6 @@ class ForeachAnalyzer
$has_valid_iterator = true;
if ($iterator_atomic_type instanceof TNamedObject
&& strtolower($iterator_atomic_type->value) === 'simplexmlelement'
) {
$value_type = Type::combineUnionTypes(
$value_type,
new Union([$iterator_atomic_type]),
);
$key_type = Type::combineUnionTypes(
$key_type,
Type::getString(),
);
}
if ($iterator_atomic_type instanceof TIterable
|| (strtolower($iterator_atomic_type->value) === 'traversable'
|| $codebase->classImplements(

View File

@@ -29,6 +29,7 @@ use Psalm\Issue\UndefinedMethod;
use Psalm\IssueBuffer;
use Psalm\Type;
use Psalm\Type\Atomic\TNamedObject;
use Psalm\Type\Atomic\TObject;
use Psalm\Type\Atomic\TTemplateParam;
use Psalm\Type\Union;
@@ -412,7 +413,7 @@ class MethodCallAnalyzer extends CallAnalyzer
$types = $class_type->getAtomicTypes();
foreach ($types as $key => &$type) {
if (!$type instanceof TNamedObject) {
if (!$type instanceof TNamedObject && !$type instanceof TObject) {
unset($types[$key]);
} else {
$type = $type->setFromDocblock(false);

View File

@@ -1737,8 +1737,12 @@ class ArrayFetchAnalyzer
?Union &$array_access_type,
bool &$has_array_access
): void {
if (strtolower($type->value) === 'simplexmlelement') {
$call_array_access_type = new Union([new TNamedObject('SimpleXMLElement')]);
$codebase = $statements_analyzer->getCodebase();
if (strtolower($type->value) === 'simplexmlelement'
|| ($codebase->classExists($type->value)
&& $codebase->classExtendsOrImplements($type->value, 'SimpleXMLElement'))
) {
$call_array_access_type = new Union([new TNull(), new TNamedObject('SimpleXMLElement')]);
} elseif (strtolower($type->value) === 'domnodelist' && $stmt->dim) {
$old_data_provider = $statements_analyzer->node_data;

View File

@@ -228,7 +228,7 @@ class AtomicPropertyFetchAnalyzer
self::handleEnumValue($statements_analyzer, $stmt, $stmt_var_type, $class_storage);
} elseif ($prop_name === 'name') {
$has_valid_fetch_type = true;
self::handleEnumName($statements_analyzer, $stmt, $lhs_type_part);
self::handleEnumName($statements_analyzer, $stmt, $stmt_var_type, $class_storage);
} else {
self::handleNonExistentProperty(
$statements_analyzer,
@@ -979,16 +979,31 @@ class AtomicPropertyFetchAnalyzer
private static function handleEnumName(
StatementsAnalyzer $statements_analyzer,
PropertyFetch $stmt,
Atomic $lhs_type_part
Union $stmt_var_type,
ClassLikeStorage $class_storage
): void {
if ($lhs_type_part instanceof TEnumCase) {
$statements_analyzer->node_data->setType(
$stmt,
new Union([Type::getAtomicStringFromLiteral($lhs_type_part->case_name)]),
);
} else {
$statements_analyzer->node_data->setType($stmt, Type::getNonEmptyString());
$relevant_enum_cases = array_filter(
$stmt_var_type->getAtomicTypes(),
static fn(Atomic $type): bool => $type instanceof TEnumCase,
);
$relevant_enum_case_names = array_map(
static fn(TEnumCase $enumCase): string => $enumCase->case_name,
$relevant_enum_cases,
);
if (empty($relevant_enum_case_names)) {
$relevant_enum_case_names = array_keys($class_storage->enum_cases);
}
$statements_analyzer->node_data->setType(
$stmt,
empty($relevant_enum_case_names)
? Type::getNonEmptyString()
: new Union(array_map(
fn(string $name): TString => Type::getAtomicStringFromLiteral($name),
$relevant_enum_case_names,
)),
);
}
private static function handleEnumValue(

View File

@@ -3,9 +3,12 @@
namespace Psalm\Internal\Analyzer\Statements\Expression;
use PhpParser;
use Psalm\CodeLocation;
use Psalm\Context;
use Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer;
use Psalm\Internal\Analyzer\StatementsAnalyzer;
use Psalm\Issue\InvalidArgument;
use Psalm\IssueBuffer;
use Psalm\Type;
/**
@@ -30,6 +33,15 @@ class IssetAnalyzer
$context->vars_in_scope[$var_id] = Type::getMixed();
$context->vars_possibly_in_scope[$var_id] = true;
}
} elseif (!self::isValidStatement($isset_var)) {
IssueBuffer::maybeAdd(
new InvalidArgument(
'Isset only works with variables and array elements',
new CodeLocation($statements_analyzer->getSource(), $isset_var),
'empty',
),
$statements_analyzer->getSuppressedIssues(),
);
}
self::analyzeIssetVar($statements_analyzer, $isset_var, $context);
@@ -49,4 +61,15 @@ class IssetAnalyzer
$context->inside_isset = false;
}
private static function isValidStatement(PhpParser\Node\Expr $stmt): bool
{
return $stmt instanceof PhpParser\Node\Expr\Variable
|| $stmt instanceof PhpParser\Node\Expr\ArrayDimFetch
|| $stmt instanceof PhpParser\Node\Expr\PropertyFetch
|| $stmt instanceof PhpParser\Node\Expr\StaticPropertyFetch
|| $stmt instanceof PhpParser\Node\Expr\NullsafePropertyFetch
|| $stmt instanceof PhpParser\Node\Expr\ClassConstFetch
|| $stmt instanceof PhpParser\Node\Expr\AssignRef;
}
}

View File

@@ -12,6 +12,7 @@ use Psalm\Exception\ConfigNotFoundException;
use Psalm\Internal\Analyzer\ProjectAnalyzer;
use Psalm\Report;
use RuntimeException;
use UnexpectedValueException;
use function array_filter;
use function array_key_exists;
@@ -485,7 +486,15 @@ final class CliUtils
}
if ($version !== null && $source !== null) {
$project_analyzer->setPhpVersion($version, $source);
try {
$project_analyzer->setPhpVersion($version, $source);
} catch (UnexpectedValueException $e) {
fwrite(
STDERR,
$e->getMessage() . PHP_EOL,
);
exit(1);
}
}
}

View File

@@ -597,6 +597,7 @@ class ClassLikes
/**
* Determine whether or not a class extends a parent
*
* @psalm-mutation-free
* @throws UnpopulatedClasslikeException when called on unpopulated class
* @throws InvalidArgumentException when class does not exist
*/
@@ -620,6 +621,8 @@ class ClassLikes
/**
* Check whether a class implements an interface
*
* @psalm-mutation-free
*/
public function classImplements(string $fq_class_name, string $interface): bool
{

View File

@@ -23,7 +23,6 @@ use LanguageServerProtocol\CodeDescription;
use LanguageServerProtocol\CompletionOptions;
use LanguageServerProtocol\Diagnostic;
use LanguageServerProtocol\DiagnosticSeverity;
use LanguageServerProtocol\ExecuteCommandOptions;
use LanguageServerProtocol\InitializeResult;
use LanguageServerProtocol\InitializeResultServerInfo;
use LanguageServerProtocol\LogMessage;
@@ -446,9 +445,6 @@ class LanguageServer extends Dispatcher
$serverCapabilities = new ServerCapabilities();
//The server provides execute command support.
$serverCapabilities->executeCommandProvider = new ExecuteCommandOptions(['test']);
$textDocumentSyncOptions = new TextDocumentSyncOptions();
//Open and close notifications are sent to the server.

View File

@@ -455,25 +455,6 @@ class TextDocument
],
]),
);
/*
$fixers["fixAll.{$diagnostic->data->type}"] = new CodeAction(
"FixAll {$diagnostic->data->type} for this file",
CodeActionKind::QUICK_FIX,
null,
null,
null,
null,
new Command(
"Fix All",
"psalm.fixall",
[
'uri' => $textDocument->uri,
'type' => $diagnostic->data->type
]
)
);
*/
}
if (empty($fixers)) {

View File

@@ -11,7 +11,6 @@ use Psalm\Internal\Provider\ReturnTypeProvider\DateTimeModifyReturnTypeProvider;
use Psalm\Internal\Provider\ReturnTypeProvider\DomNodeAppendChild;
use Psalm\Internal\Provider\ReturnTypeProvider\ImagickPixelColorReturnTypeProvider;
use Psalm\Internal\Provider\ReturnTypeProvider\PdoStatementReturnTypeProvider;
use Psalm\Internal\Provider\ReturnTypeProvider\SimpleXmlElementAsXml;
use Psalm\Plugin\EventHandler\Event\MethodReturnTypeProviderEvent;
use Psalm\Plugin\EventHandler\MethodReturnTypeProviderInterface;
use Psalm\StatementsSource;
@@ -39,7 +38,6 @@ class MethodReturnTypeProvider
$this->registerClass(DomNodeAppendChild::class);
$this->registerClass(ImagickPixelColorReturnTypeProvider::class);
$this->registerClass(SimpleXmlElementAsXml::class);
$this->registerClass(PdoStatementReturnTypeProvider::class);
$this->registerClass(ClosureFromCallableReturnTypeProvider::class);
$this->registerClass(DateTimeModifyReturnTypeProvider::class);

View File

@@ -1,34 +0,0 @@
<?php
namespace Psalm\Internal\Provider\ReturnTypeProvider;
use Psalm\Plugin\EventHandler\Event\MethodReturnTypeProviderEvent;
use Psalm\Plugin\EventHandler\MethodReturnTypeProviderInterface;
use Psalm\Type;
use Psalm\Type\Union;
use function count;
/**
* @internal
*/
class SimpleXmlElementAsXml implements MethodReturnTypeProviderInterface
{
public static function getClassLikeNames(): array
{
return ['SimpleXMLElement'];
}
public static function getMethodReturnType(MethodReturnTypeProviderEvent $event): ?Union
{
$call_args = $event->getCallArgs();
$method_name_lowercase = $event->getMethodNameLowercase();
if ($method_name_lowercase === 'asxml'
&& !count($call_args)
) {
return Type::parseString('string|false');
}
return null;
}
}

View File

@@ -4,6 +4,7 @@ namespace Psalm\Internal\Provider\ReturnTypeProvider;
use ArgumentCountError;
use Psalm\Issue\InvalidArgument;
use Psalm\Issue\RedundantFunctionCall;
use Psalm\Issue\TooFewArguments;
use Psalm\Issue\TooManyArguments;
use Psalm\IssueBuffer;
@@ -25,6 +26,7 @@ use function count;
use function is_string;
use function preg_match;
use function sprintf;
use function strlen;
/**
* @internal
@@ -47,6 +49,11 @@ class SprintfReturnTypeProvider implements FunctionReturnTypeProviderInterface
$statements_source = $event->getStatementsSource();
$call_args = $event->getCallArgs();
// invalid - will already report an error for the params anyway
if (count($call_args) < 1) {
return null;
}
$has_splat_args = false;
$node_type_provider = $statements_source->getNodeTypeProvider();
foreach ($call_args as $call_arg) {
@@ -67,17 +74,29 @@ class SprintfReturnTypeProvider implements FunctionReturnTypeProviderInterface
// eventually this could be refined
// to check if it's an array with literal string as first element for further checking
if (count($call_args) === 1 && $has_splat_args === true) {
IssueBuffer::maybeAdd(
new RedundantFunctionCall(
'Using the splat operator is redundant, as v' . $event->getFunctionId()
. ' without splat operator can be used instead of ' . $event->getFunctionId(),
$event->getCodeLocation(),
),
$statements_source->getSuppressedIssues(),
);
return null;
}
// it makes no sense to use sprintf when there is only 1 arg (the format)
// as it wouldn't have any placeholders
if (count($call_args) === 1 && $event->getFunctionId() === 'sprintf') {
// if it's a literal string, we can check it further though!
$first_arg_type = $node_type_provider->getType($call_args[0]->value);
if (count($call_args) === 1
&& ($first_arg_type === null || !$first_arg_type->isSingleStringLiteral())) {
IssueBuffer::maybeAdd(
new TooFewArguments(
'Too few arguments for ' . $event->getFunctionId() . ', expecting at least 2 arguments',
new RedundantFunctionCall(
'Using ' . $event->getFunctionId()
. ' with a single argument is redundant, since there are no placeholder params to be substituted',
$event->getCodeLocation(),
$event->getFunctionId(),
),
$statements_source->getSuppressedIssues(),
);
@@ -89,7 +108,10 @@ class SprintfReturnTypeProvider implements FunctionReturnTypeProviderInterface
$is_falsable = true;
foreach ($call_args as $index => $call_arg) {
$type = $node_type_provider->getType($call_arg->value);
if ($type === null && $index === 0 && $event->getFunctionId() === 'printf') {
// printf only has the format validated above
// don't change the return type
break;
}
@@ -100,10 +122,9 @@ class SprintfReturnTypeProvider implements FunctionReturnTypeProviderInterface
if ($index === 0 && $type->isSingleStringLiteral()) {
if ($type->getSingleStringLiteral()->value === '') {
IssueBuffer::maybeAdd(
new InvalidArgument(
'Argument 1 of ' . $event->getFunctionId() . ' must not be an empty string',
new RedundantFunctionCall(
'Calling ' . $event->getFunctionId() . ' with an empty first argument does nothing',
$event->getCodeLocation(),
$event->getFunctionId(),
),
$statements_source->getSuppressedIssues(),
);
@@ -158,17 +179,48 @@ class SprintfReturnTypeProvider implements FunctionReturnTypeProviderInterface
$initial_result = $result;
if ($result === $type->getSingleStringLiteral()->value) {
IssueBuffer::maybeAdd(
new InvalidArgument(
'Argument 1 of ' . $event->getFunctionId()
. ' does not contain any placeholders',
$event->getCodeLocation(),
$event->getFunctionId(),
),
$statements_source->getSuppressedIssues(),
);
if (count($call_args) > 1) {
// we need to report this here too, since we return early without further validation
// otherwise people who have suspended RedundantFunctionCall errors
// will not get an error for this
IssueBuffer::maybeAdd(
new TooManyArguments(
'Too many arguments for the number of placeholders in '
. $event->getFunctionId(),
$event->getCodeLocation(),
$event->getFunctionId(),
),
$statements_source->getSuppressedIssues(),
);
}
return null;
// the same error as above, but we have validated the pattern now
if (count($call_args) === 1) {
IssueBuffer::maybeAdd(
new RedundantFunctionCall(
'Using ' . $event->getFunctionId()
. ' with a single argument is redundant,'
. ' since there are no placeholder params to be substituted',
$event->getCodeLocation(),
),
$statements_source->getSuppressedIssues(),
);
} else {
IssueBuffer::maybeAdd(
new RedundantFunctionCall(
'Argument 1 of ' . $event->getFunctionId()
. ' does not contain any placeholders',
$event->getCodeLocation(),
),
$statements_source->getSuppressedIssues(),
);
}
if ($event->getFunctionId() === 'printf') {
return Type::getInt(false, strlen($type->getSingleStringLiteral()->value));
}
return $type;
}
}
} catch (ValueError $value_error) {

View File

@@ -323,7 +323,7 @@ abstract class Atomic implements TypeNode
return $analysis_php_version_id !== null ? new TNamedObject($value) : new TScalar();
case 'null':
if ($analysis_php_version_id === null || $analysis_php_version_id >= 8_00_00) {
if ($analysis_php_version_id === null || $analysis_php_version_id >= 7_00_00) {
return new TNull();
}

View File

@@ -424,6 +424,10 @@ class Reconciler
{
foreach ($new_types as $nk => $type) {
if (strpos($nk, '[') || strpos($nk, '->')) {
$type = array_values($type);
if (!isset($type[0][0])) {
continue;
}
if ($type[0][0] instanceof IsEqualIsset
|| $type[0][0] instanceof IsIsset
|| $type[0][0] instanceof NonEmpty

View File

@@ -1359,9 +1359,10 @@ function realpath(string $path) {}
*
* @param numeric-string $num1
* @param numeric-string $num2
* @param int|null $scale
* @return (PHP_MAJOR_VERSION is 8 ? numeric-string : ($num2 is "0" ? null : numeric-string))
*/
function bcdiv(string $num1, string $num2, int $scale = 0): ?string {}
function bcdiv(string $num1, string $num2, ?int $scale = null): ?string {}
/**
* @psalm-pure

11
vendor/vimeo/psalm/stubs/Php74.phpstub vendored Normal file
View File

@@ -0,0 +1,11 @@
<?php
/**
* @psalm-pure
*
* @psalm-taint-escape html
* @psalm-flow ($string) -> return
*
* @param null|string|array<array-key,string> $allowed_tags
*/
function strip_tags(string $string, null|string|array $allowed_tags = null) : string {}

View File

@@ -471,13 +471,13 @@ class DOMDocument extends DOMNode implements DOMParentNode
public function importNode(DOMNode $node, bool $deep = false) {}
/**
* @return DOMDocument|false
* @return bool
* @psalm-ignore-falsable-return
**/
public function load(string $filename, int $options = 0) {}
/**
* @return DOMDocument|false
* @return bool
* @psalm-ignore-falsable-return
*/
public function loadXML(string $source, int $options = 0) {}
@@ -492,10 +492,10 @@ class DOMDocument extends DOMNode implements DOMParentNode
*/
public function save(string $filename, int $options = 0) {}
/** @return DOMDocument|bool */
/** @return bool */
public function loadHTML(string $source, int $options = 0) {}
/** @return DOMDocument|bool */
/** @return bool */
public function loadHTMLFile(string $filename, int $options = 0) {}
/**

View File

@@ -25,6 +25,7 @@ function simplexml_import_dom(SimpleXMLElement|DOMNode $node, ?string $class_nam
/**
* @implements Traversable<string, SimpleXMLElement>
* @psalm-no-seal-properties
*/
class SimpleXMLElement implements Traversable, Countable
{
@@ -63,6 +64,8 @@ class SimpleXMLElement implements Traversable, Countable
public function __toString(): string {}
public function count(): int {}
public function __get(string $name): SimpleXMLElement|SimpleXMLIterator|null {}
}
/**

View File

@@ -0,0 +1,42 @@
<?php // phpcs:ignore warning
/**
* @phan-file-suppress PhanTypeSuspiciousStringExpression
*/
declare(strict_types=1);
error_reporting(E_ALL | E_STRICT | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
ob_start();
// sample config
require 'config.php';
// define log file id
$LOG_FILE_ID = 'classTest_class_calls';
ob_end_flush();
$log = new CoreLibs\Logging\Logging([
'log_folder' => BASE . LOG,
'log_file_id' => $LOG_FILE_ID,
'log_per_date' => true,
]);
// db connection and attach logger
$db = new CoreLibs\DB\IO(DB_CONFIG, $log);
// define a list of from to color sets for conversion test
$PAGE_NAME = 'TEST CLASS: CLASS CALLS';
print "<!DOCTYPE html>";
print "<html><head><title>" . $PAGE_NAME . "</title><head>";
print "<body>";
print '<div><a href="class_test.php">Class Test Master</a></div>';
print '<div><h1>' . $PAGE_NAME . '</h1></div>';
$test = new \TestCalls\Test($db, $log);
$test->testDbCall();
print "</body></html>";
// __END__

View File

@@ -169,6 +169,7 @@ class TestL
public function test(?string $ts = null): bool
{
print "* GETCALLERCLASS(INSIDE CLASS): " . \CoreLibs\Debug\Support::getCallerClass() . "<br>";
print "* GETCALLERTOPCLASS(INSIDE CLASS): " . \CoreLibs\Debug\Support::getCallerTopLevelClass() . "<br>";
$this->log->debug('TESTL', 'Logging in class testL' . ($ts !== null ? ': ' . $ts : ''));
$this->log->debug('TESTL', 'Some other message');
return true;
@@ -191,6 +192,7 @@ class TestR extends TestL
public function subTest(): bool
{
print "** GETCALLERCLASS(INSIDE EXTND CLASS): " . \CoreLibs\Debug\Support::getCallerClass() . "<br>";
print "** GETCALLERTOPCLASS(INSIDE EXTND CLASS): " . \CoreLibs\Debug\Support::getCallerTopLevelClass() . "<br>";
$this->log->debug('TESTR', 'Logging in class testR (extends testL)');
$this->test('TESTR INSIDE');
$this->log->debug('TESTR', 'Array: '

View File

@@ -75,7 +75,6 @@ print "EL_O: <pre>" . print_r($el_o, true) . "</pre>";
echo "<hr>";
print "buildHtml(): <pre>" . htmlentities($el_o->buildHtml()) . "</pre>";
echo "<hr>";
/** @phpstan-ignore-next-line */
print "phfo(\$el_o): <pre>" . htmlentities($el_o::printHtmlFromObject($el_o, true)) . "</pre>";
echo "<hr>";
print "phfa(\$el_list): <pre>" . htmlentities($el_o::buildHtmlFromList($el_o_list, true)) . "</pre>";

View File

@@ -51,7 +51,6 @@ echo "<br><b>AUTO DETECT</b><br>";
// DEPRECATED
// $get_locale = Language\GetLocale::setLocale();
// print "[AUTO, DEPRECATED]: " . Support::printAr($get_locale) . "<br>";
/** @phpstan-ignore-next-line */
$get_locale = Language\GetLocale::setLocaleFromSession(
SITE_LOCALE,
str_replace('/', '', CONTENT_PATH),
@@ -59,7 +58,6 @@ $get_locale = Language\GetLocale::setLocaleFromSession(
BASE . INCLUDES . LOCALE
);
print "[NAMED CONSTANTS OUTSIDE]: " . Support::printAr($get_locale) . "<br>";
/** @phpstan-ignore-next-line */
$get_locale = Language\GetLocale::setLocaleFromSession(
'en',
'foo',
@@ -76,7 +74,6 @@ $_SESSION['DEFAULT_LOCALE'] = 'ja_JP.UTF-8';
$_SESSION['DEFAULT_CHARSET'] = 'UTF-8';
$_SESSION['DEFAULT_DOMAIN'] = 'admin';
$_SESSION['LOCALE_PATH'] = BASE . INCLUDES . LOCALE;
/** @phpstan-ignore-next-line */
$get_locale = Language\GetLocale::setLocaleFromSession(
SITE_LOCALE,
SITE_DOMAIN,
@@ -93,7 +90,6 @@ $_SESSION['DEFAULT_LOCALE'] = '00000#####';
$_SESSION['DEFAULT_CHARSET'] = '';
$_SESSION['DEFAULT_DOMAIN'] = 'admin';
$_SESSION['LOCALE_PATH'] = BASE . INCLUDES . LOCALE;
/** @phpstan-ignore-next-line */
$get_locale = Language\GetLocale::setLocaleFromSession(
SITE_LOCALE,
SITE_DOMAIN,

View File

@@ -114,6 +114,7 @@ $test_files = [
'class_test.autoloader.php' => 'Class Test: AUTOLOADER',
'class_test.config.link.php' => 'Class Test: CONFIG LINK',
'class_test.config.direct.php' => 'Class Test: CONFIG DIRECT',
'class_test.class-calls.php' => 'Class Test: CLASS CALLS',
'subfolder/class_test.config.direct.php' => 'Class Test: CONFIG DIRECT SUB',
];

View File

@@ -35,7 +35,6 @@ print '<div><h1>' . $PAGE_NAME . '</h1></div>';
print "ALREADY from config.php: " . \CoreLibs\Debug\Support::printAr($_ENV) . "<br>";
// test .env in local
/** @phpstan-ignore-next-line */
$status = \CoreLibs\Get\DotEnv::readEnvFile('.', 'test.env');
print "test.env: STATUS: " . $status . "<br>";
print "AFTER reading test.env file: " . \CoreLibs\Debug\Support::printAr($_ENV) . "<br>";

49
www/composer.lock generated
View File

@@ -273,16 +273,16 @@
},
{
"name": "nikic/php-parser",
"version": "v4.15.5",
"version": "v4.17.1",
"source": {
"type": "git",
"url": "https://github.com/nikic/PHP-Parser.git",
"reference": "11e2663a5bc9db5d714eedb4277ee300403b4a9e"
"reference": "a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/11e2663a5bc9db5d714eedb4277ee300403b4a9e",
"reference": "11e2663a5bc9db5d714eedb4277ee300403b4a9e",
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d",
"reference": "a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d",
"shasum": ""
},
"require": {
@@ -323,9 +323,9 @@
],
"support": {
"issues": "https://github.com/nikic/PHP-Parser/issues",
"source": "https://github.com/nikic/PHP-Parser/tree/v4.15.5"
"source": "https://github.com/nikic/PHP-Parser/tree/v4.17.1"
},
"time": "2023-05-19T20:20:00+00:00"
"time": "2023-08-13T19:53:39+00:00"
},
{
"name": "phar-io/manifest",
@@ -440,16 +440,16 @@
},
{
"name": "phpunit/php-code-coverage",
"version": "9.2.26",
"version": "9.2.27",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-code-coverage.git",
"reference": "443bc6912c9bd5b409254a40f4b0f4ced7c80ea1"
"reference": "b0a88255cb70d52653d80c890bd7f38740ea50d1"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/443bc6912c9bd5b409254a40f4b0f4ced7c80ea1",
"reference": "443bc6912c9bd5b409254a40f4b0f4ced7c80ea1",
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/b0a88255cb70d52653d80c890bd7f38740ea50d1",
"reference": "b0a88255cb70d52653d80c890bd7f38740ea50d1",
"shasum": ""
},
"require": {
@@ -505,7 +505,8 @@
],
"support": {
"issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.26"
"security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy",
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.27"
},
"funding": [
{
@@ -513,7 +514,7 @@
"type": "github"
}
],
"time": "2023-03-06T12:58:08+00:00"
"time": "2023-07-26T13:44:30+00:00"
},
{
"name": "phpunit/php-file-iterator",
@@ -758,16 +759,16 @@
},
{
"name": "phpunit/phpunit",
"version": "9.6.8",
"version": "9.6.11",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git",
"reference": "17d621b3aff84d0c8b62539e269e87d8d5baa76e"
"reference": "810500e92855eba8a7a5319ae913be2da6f957b0"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/17d621b3aff84d0c8b62539e269e87d8d5baa76e",
"reference": "17d621b3aff84d0c8b62539e269e87d8d5baa76e",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/810500e92855eba8a7a5319ae913be2da6f957b0",
"reference": "810500e92855eba8a7a5319ae913be2da6f957b0",
"shasum": ""
},
"require": {
@@ -841,7 +842,7 @@
"support": {
"issues": "https://github.com/sebastianbergmann/phpunit/issues",
"security": "https://github.com/sebastianbergmann/phpunit/security/policy",
"source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.8"
"source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.11"
},
"funding": [
{
@@ -857,7 +858,7 @@
"type": "tidelift"
}
],
"time": "2023-05-11T05:14:45+00:00"
"time": "2023-08-19T07:10:56+00:00"
},
{
"name": "sebastian/cli-parser",
@@ -1365,16 +1366,16 @@
},
{
"name": "sebastian/global-state",
"version": "5.0.5",
"version": "5.0.6",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/global-state.git",
"reference": "0ca8db5a5fc9c8646244e629625ac486fa286bf2"
"reference": "bde739e7565280bda77be70044ac1047bc007e34"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/0ca8db5a5fc9c8646244e629625ac486fa286bf2",
"reference": "0ca8db5a5fc9c8646244e629625ac486fa286bf2",
"url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bde739e7565280bda77be70044ac1047bc007e34",
"reference": "bde739e7565280bda77be70044ac1047bc007e34",
"shasum": ""
},
"require": {
@@ -1417,7 +1418,7 @@
],
"support": {
"issues": "https://github.com/sebastianbergmann/global-state/issues",
"source": "https://github.com/sebastianbergmann/global-state/tree/5.0.5"
"source": "https://github.com/sebastianbergmann/global-state/tree/5.0.6"
},
"funding": [
{
@@ -1425,7 +1426,7 @@
"type": "github"
}
],
"time": "2022-02-14T08:28:10+00:00"
"time": "2023-08-02T09:26:13+00:00"
},
{
"name": "sebastian/lines-of-code",

View File

@@ -556,7 +556,7 @@ class Backend
string $suffix = '',
int $min_steps = 1,
bool $name_pos_back = false
) {
): string {
// get the build layout
$html_time = \CoreLibs\Output\Form\Elements::printDateTime(
$year,

View File

@@ -805,7 +805,10 @@ class IO
$call_stack[] =
($call_trace['file'] ?? 'n/f') . ':'
. ($call_trace['line'] ?? '-') . ':'
. (!empty($call_trace['class']) ? $call_trace['class'] . '->' : '')
. (!empty($call_trace['class']) ?
$call_trace['class'] . ($call_trace['type'] ?? '') :
''
)
. $call_trace['function'];
}
$context = [
@@ -825,7 +828,7 @@ class IO
);
break;
default:
// used named arguments so we can easy change the order of debug
// used named arguments so we can easy change the order of debug
$this->log->debug(
group_id: $debug_id,
message: $error_string,

View File

@@ -405,7 +405,7 @@ class LoggingLegacy
// set per class, but don't use get_class as we will only get self
$rpl_string = !$this->log_per_class ? '' : '_'
// set sub class settings
. str_replace('\\', '-', Support::getCallerClass());
. str_replace('\\', '-', Support::getCallerTopLevelClass());
$fn = str_replace('{CLASS}', $rpl_string, $fn); // create output filename
// if request to write to one file
@@ -756,7 +756,7 @@ class LoggingLegacy
return $status;
}
// get the last class entry and wrie that
$class = Support::getCallerClass();
$class = Support::getCallerTopLevelClass();
// get timestamp
$timestamp = Support::printTime();
// same string put for print (no html data inside)
@@ -855,7 +855,7 @@ class LoggingLegacy
. 'border-bottom: 1px solid black; margin: 10px 0 10px 0; '
. 'background-color: white; color: black;">'
. '<div style="font-size: 12px;">{<span style="font-style: italic; color: #928100;">'
. Support::getCallerClass() . '</span>}</div>';
. Support::getCallerTopLevelClass() . '</span>}</div>';
$string_output = $string_prefix . $string_output
. '<div><span style="font-style: italic; color: #108db3;">Script Run Time:</span> '
. $script_end . '</div>'

View File

@@ -79,10 +79,10 @@ class Support
* default true: true, false: false
*
* @param bool $bool Variable to convert
* @param string $name [default: ''] Prefix name
* @param string $true [default: 'true'] True string
* @param string $false [default: 'false'] False string
* @param bool $no_html [default: false] if true do not print html
* @param string $name [=''] Prefix name
* @param string $true [='true'] True string
* @param string $false [='false'] False string
* @param bool $no_html [=false] if true do not print html
* @return string String with converted bool text for debug
*/
public static function printBool(
@@ -104,8 +104,8 @@ class Support
* Convert bool value to string value. Short name alias for printBool
*
* @param bool $bool Bool value to be transformed
* @param string $true [default: 'true'] Override default string 'true'
* @param string $false [default: 'false'] Override default string 'false'
* @param string $true [='true'] Override default string 'true'
* @param string $false [=false'] Override default string 'false'
* @return string $true or $false string for true/false bool
*/
public static function prBl(
@@ -159,7 +159,7 @@ class Support
* Recommended debug output
*
* @param mixed $data Anything
* @param bool $no_html [default=false] If true strip all html tags
* @param bool $no_html [=false] If true strip all html tags
* (for text print)
* @return string A text string
*/
@@ -203,7 +203,7 @@ class Support
* exports (dumps) var, in more printable design, but without detail info
*
* @param mixed $data Anything
* @param bool $no_html If true true do not add <pre> tags
* @param bool $no_html [=false] If true true do not add <pre> tags
* @return string A text string
*/
public static function exportVar(mixed $data, bool $no_html = false): string
@@ -217,7 +217,7 @@ class Support
* Return file name and line number where this was called
* One level up
*
* @param int $level trace level, default 1
* @param int $level [=1] trace level
* @return string|null null or file name:line number
*/
public static function getCallerFileLine(int $level = 1): ?string
@@ -238,14 +238,14 @@ class Support
* eg for debugging, this function does this
*
* call this method in the child method and you get the parent function that called
* @param int $level trace level, default 1
* @return ?string null or the function that called the function
* where this method is called
* @param int $level [=1] trace level
* @return string|null null or the function that called the function
* where this method is called
*/
public static function getCallerMethod(int $level = 1): ?string
{
$traces = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
// print \CoreLibs\Debug\Support::printAr($traces);
// print "getCallerMethod:<br>" . \CoreLibs\Debug\Support::printAr($traces);
// We should check from top down if unset?
// sets the start point here, and in level two (the sub call) we find this
if (isset($traces[$level])) {
@@ -254,6 +254,41 @@ class Support
return null;
}
/**
* get the class that first called it and skip the base class
* Companion method to getCallerMethod
*
* @param int $level [=1] trace level
* @return ?string null if class not found
*/
public static function getCallerClass(int $level = 1): ?string
{
$traces = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
// print "getCallerClass:<br>" . \CoreLibs\Debug\Support::printAr($traces);
if (isset($traces[$level])) {
return $traces[$level]['class'] ?? null;
}
return null;
}
/**
* Returns class and method together
*
* @param int $level [=1] travel level
* @return string|null null if trace level not found, else namespace class and method
*/
public static function getCallerClassMethod(int $level = 1): ?string
{
$traces = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
// print "getCallerClass:<br>" . \CoreLibs\Debug\Support::printAr($traces);
if (isset($traces[$level])) {
return ($traces[$level]['class'] ?? '-')
. ($traces[$level]['type'] ?? '')
. $traces[$level]['function'];
}
return null;
}
/**
* Returns array with all methods in the call stack in the order so that last
* called is last in order
@@ -283,25 +318,21 @@ class Support
* Is mostly used in debug log statements to get the class where the debug
* was called
* gets top level class
* loops over the debug backtrace until if finds the first class (from the end)
* loops over the debug backtrace until if finds the first class (from the end)
*
* @return string Class name with namespace
*/
public static function getCallerClass(): string
public static function getCallerTopLevelClass(): string
{
$backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
// ?? [['class' => get_called_class()]];
// TODO make sure that this doesn't loop forver
$traces = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
// print "getCallerClass:<br>" . \CoreLibs\Debug\Support::printAr($traces);
$class = null;
while ($class === null && count($backtrace) > 0) {
// if current is
// [function] => debug
// [class] => CoreLibs\Debug\Logging
// then return
// (OUTSIDE) because it was not called from a class method
// or return file name
$get_class = array_pop($backtrace);
$class = $get_class['class'] ?? null;
// reverse and stop at first set class, this is the top level one
foreach (array_reverse($traces) as $trace) {
$class = $trace['class'] ?? null;
if (!empty($class)) {
break;
}
}
// on null or empty return empty string
return empty($class) ? '' : $class;

View File

@@ -381,7 +381,7 @@ class Logging
// auto set (should be deprecated in future)
$this->setLogFileId(
str_replace(':', '-', $this->host_name) . '_'
. str_replace('\\', '-', Support::getCallerClass())
. str_replace('\\', '-', Support::getCallerTopLevelClass())
);
}
if (empty($this->getLogFileId())) {
@@ -460,7 +460,7 @@ class Logging
// set per class, but don't use get_class as we will only get self
$rpl_string = !$this->getLogFlag(Flag::per_class) ? '' : '_'
// set sub class settings
. str_replace('\\', '-', Support::getCallerClass());
. str_replace('\\', '-', Support::getCallerTopLevelClass());
$fn = str_replace('{CLASS}', $rpl_string, $fn); // create output filename
// if request to write to one file
@@ -526,7 +526,10 @@ class Logging
/**
* Prepare the log message with all needed info blocks:
* [timestamp] [host name] [file path + file] [running uid] {class} <debug level/group id> - message
* [timestamp] [host name] [file path + file::row number] [running uid] {class::/->method}
* <debug level:debug group id> - message
* Note: group id is only for debug level
* if no method can be found or no class is found a - will be wirtten
*
* @param Level $level Log level we will write to
* @param string|Stringable $message The message to write
@@ -545,16 +548,32 @@ class Logging
if (!$this->checkLogLevel($level)) {
return '';
}
$file_line = '';
$caller_class_method = '-';
$traces = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
// print "[" . $level->getName() . "] [$message] prepareLog:<br>" . Support::printAr($traces);
// file + line: call not this but one before (the one that calls this)
$file_line = Support::getCallerFileLine(2) ??
System::getPageName(System::FULL_PATH);
// get the last class entry and wrie that
$class = Support::getCallerClass();
// method/function: prepareLog->(debug|info|...)->[THIS]
$method = Support::getCallerMethod(3);
if ($method !== null) {
$class .= '::' . $method;
// start from this level, if unset fall down until we are at null
$start_trace_level = 2;
for ($trace_level = $start_trace_level; $trace_level >= 0; $trace_level--) {
if (isset($traces[$trace_level])) {
$file_line = ($traces[$trace_level]['file'] ?? $traces[$trace_level]['function'])
. ':' . ($traces[$trace_level]['line'] ?? '-');
// as namespace\class->method
$caller_class_method =
// get the last call before we are in the Logging class
($traces[$trace_level]['class'] ?? '')
// connector, if unkown use ==
. ($traces[$trace_level]['type'] ?? '')
// method/function: prepareLog->(debug|info|...)->[THIS]
. $traces[$trace_level]['function'];
break;
}
}
if (empty($file_line)) {
$file_line = System::getPageName(System::FULL_PATH);
}
// print "CLASS: " . $class . "<br>";
// get timestamp
$timestamp = Support::printTime();
@@ -574,7 +593,7 @@ class Logging
. '[' . $this->host_name . '] '
. '[' . $file_line . '] '
. '[' . $this->running_uid . '] '
. '{' . $class . '} '
. '{' . $caller_class_method . '} '
. '<' . strtoupper($group_str) . '> '
. $message
. $context_str;

View File

@@ -4,40 +4,28 @@
* TEST sets for DB::IO
*/
namespace Test\DB;
use CoreLibs\DB\IO;
namespace TestCalls\DB;
class TestDB
{
/** @var IO */
private $db;
/** @var array<mixed> */
private $config;
/** @var \CoreLibs\DB\IO */
private \CoreLibs\DB\IO $db;
/** @var \CoreLibs\Logging\Logging */
private \CoreLibs\Logging\Logging $log;
/** @var \TestCalls\Test */
public $main;
/**
* Undocumented function
*
* @param \CoreLibs\Logging\Logging $log
* @param \TestCalls\Test $main
*/
public function __construct(
\CoreLibs\Logging\Logging $log
\TestCalls\Test $main
) {
$this->config = [
'db_name' => $_ENV['DB_NAME_TEST'] ?? '',
'db_user' => $_ENV['DB_USER_TEST'] ?? '',
'db_pass' => $_ENV['DB_PASS_TEST'] ?? '',
'db_host' => $_ENV['DB_HOST_TEST'] ?? '',
'db_port' => 5432,
'db_schema' => 'public',
'db_type' => 'pgsql',
'db_encoding' => '',
'db_ssl' => 'allow'
];
$this->db = new IO(
$this->config,
$log
);
$this->db = $main->db;
$this->log = $main->log;
}
/**
@@ -47,6 +35,7 @@ class TestDB
*/
private function testDBa(): void
{
$this->log->debug('TEST DB', 'Call in testDBa');
$this->db->dbInfo();
}
@@ -57,6 +46,7 @@ class TestDB
*/
public function testRunDB(): void
{
$this->log->debug('TEST DB', 'Call in testRunDB');
$this->testDBa();
}
}

View File

@@ -14,25 +14,33 @@
declare(strict_types=1);
namespace Test;
namespace TestCalls;
use Test\DB;
use TestCalls\DB;
class Test
{
/** @var DB\TestDB */
private $test_db;
/** @var \CoreLibs\DB\IO */
public \CoreLibs\DB\IO $db;
/** @var \CoreLibs\Logging\Logging */
public \CoreLibs\Logging\Logging $log;
public function __construct(
\CoreLibs\DB\IO $db,
\CoreLibs\Logging\Logging $log
) {
$this->db = $db;
$this->log = $log;
// calls all tests
$this->testPrivate();
$this->testProtected();
$this->testPublic();
// call intern
$this->test_db = new DB\TestDB($log);
$this->test_db = new DB\TestDB($this);
}
public function __destruct()
@@ -82,6 +90,28 @@ class Test
{
$this->test_db->testRunDB();
}
/**
* Undocumented function
*
* @return void
*/
public function testDbCall(): void
{
$q = <<<SQL
SELECT
type, sdate, integer
FROM
foobar
LIMIT
1;
SQL;
if (is_array($res = $this->db->dbReturnRow($q))) {
print "OUTPUT: " . $this->log->prAr($res);
} else {
$this->log->error('Failure to run query');
}
}
}
// __END__

View File

@@ -139,6 +139,7 @@ return array(
'PHPUnit\\Framework\\Constraint\\LogicalXor' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Operator/LogicalXor.php',
'PHPUnit\\Framework\\Constraint\\ObjectEquals' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Object/ObjectEquals.php',
'PHPUnit\\Framework\\Constraint\\ObjectHasAttribute' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Object/ObjectHasAttribute.php',
'PHPUnit\\Framework\\Constraint\\ObjectHasProperty' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Object/ObjectHasProperty.php',
'PHPUnit\\Framework\\Constraint\\Operator' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Operator/Operator.php',
'PHPUnit\\Framework\\Constraint\\RegularExpression' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/String/RegularExpression.php',
'PHPUnit\\Framework\\Constraint\\SameSize' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Cardinality/SameSize.php',
@@ -369,8 +370,8 @@ return array(
'PHPUnit\\TextUI\\XmlConfiguration\\Migrator' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrator.php',
'PHPUnit\\TextUI\\XmlConfiguration\\MoveAttributesFromFilterWhitelistToCoverage' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/MoveAttributesFromFilterWhitelistToCoverage.php',
'PHPUnit\\TextUI\\XmlConfiguration\\MoveAttributesFromRootToCoverage' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/MoveAttributesFromRootToCoverage.php',
'PHPUnit\\TextUI\\XmlConfiguration\\MoveWhitelistDirectoriesToCoverage' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/MoveWhitelistDirectoriesToCoverage.php',
'PHPUnit\\TextUI\\XmlConfiguration\\MoveWhitelistExcludesToCoverage' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/MoveWhitelistExcludesToCoverage.php',
'PHPUnit\\TextUI\\XmlConfiguration\\MoveWhitelistIncludesToCoverage' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/MoveWhitelistIncludesToCoverage.php',
'PHPUnit\\TextUI\\XmlConfiguration\\PHPUnit' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/PHPUnit/PHPUnit.php',
'PHPUnit\\TextUI\\XmlConfiguration\\Php' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/PHP/Php.php',
'PHPUnit\\TextUI\\XmlConfiguration\\PhpHandler' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/PHP/PhpHandler.php',
@@ -876,8 +877,8 @@ return array(
'Smarty_Variable' => $vendorDir . '/egrajp/smarty-extended/src/sysplugins/smarty_variable.php',
'TPC_yyStackEntry' => $vendorDir . '/egrajp/smarty-extended/src/sysplugins/smarty_internal_configfileparser.php',
'TP_yyStackEntry' => $vendorDir . '/egrajp/smarty-extended/src/sysplugins/smarty_internal_templateparser.php',
'Test\\DB\\TestDB' => $baseDir . '/lib/Test/DB/TestDB.php',
'Test\\Test' => $baseDir . '/lib/Test/Test.php',
'TestCalls\\DB\\TestDB' => $baseDir . '/lib/Test/DB/TestDB.php',
'TestCalls\\Test' => $baseDir . '/lib/Test/Test.php',
'TheSeer\\Tokenizer\\Exception' => $vendorDir . '/theseer/tokenizer/src/Exception.php',
'TheSeer\\Tokenizer\\NamespaceUri' => $vendorDir . '/theseer/tokenizer/src/NamespaceUri.php',
'TheSeer\\Tokenizer\\NamespaceUriException' => $vendorDir . '/theseer/tokenizer/src/NamespaceUriException.php',

View File

@@ -190,6 +190,7 @@ class ComposerStaticInit10fe8fe2ec4017b8644d2b64bcf398b9
'PHPUnit\\Framework\\Constraint\\LogicalXor' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Operator/LogicalXor.php',
'PHPUnit\\Framework\\Constraint\\ObjectEquals' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Object/ObjectEquals.php',
'PHPUnit\\Framework\\Constraint\\ObjectHasAttribute' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Object/ObjectHasAttribute.php',
'PHPUnit\\Framework\\Constraint\\ObjectHasProperty' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Object/ObjectHasProperty.php',
'PHPUnit\\Framework\\Constraint\\Operator' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Operator/Operator.php',
'PHPUnit\\Framework\\Constraint\\RegularExpression' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/String/RegularExpression.php',
'PHPUnit\\Framework\\Constraint\\SameSize' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Cardinality/SameSize.php',
@@ -420,8 +421,8 @@ class ComposerStaticInit10fe8fe2ec4017b8644d2b64bcf398b9
'PHPUnit\\TextUI\\XmlConfiguration\\Migrator' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrator.php',
'PHPUnit\\TextUI\\XmlConfiguration\\MoveAttributesFromFilterWhitelistToCoverage' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/MoveAttributesFromFilterWhitelistToCoverage.php',
'PHPUnit\\TextUI\\XmlConfiguration\\MoveAttributesFromRootToCoverage' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/MoveAttributesFromRootToCoverage.php',
'PHPUnit\\TextUI\\XmlConfiguration\\MoveWhitelistDirectoriesToCoverage' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/MoveWhitelistDirectoriesToCoverage.php',
'PHPUnit\\TextUI\\XmlConfiguration\\MoveWhitelistExcludesToCoverage' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/MoveWhitelistExcludesToCoverage.php',
'PHPUnit\\TextUI\\XmlConfiguration\\MoveWhitelistIncludesToCoverage' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/MoveWhitelistIncludesToCoverage.php',
'PHPUnit\\TextUI\\XmlConfiguration\\PHPUnit' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/PHPUnit/PHPUnit.php',
'PHPUnit\\TextUI\\XmlConfiguration\\Php' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/PHP/Php.php',
'PHPUnit\\TextUI\\XmlConfiguration\\PhpHandler' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/PHP/PhpHandler.php',
@@ -927,8 +928,8 @@ class ComposerStaticInit10fe8fe2ec4017b8644d2b64bcf398b9
'Smarty_Variable' => __DIR__ . '/..' . '/egrajp/smarty-extended/src/sysplugins/smarty_variable.php',
'TPC_yyStackEntry' => __DIR__ . '/..' . '/egrajp/smarty-extended/src/sysplugins/smarty_internal_configfileparser.php',
'TP_yyStackEntry' => __DIR__ . '/..' . '/egrajp/smarty-extended/src/sysplugins/smarty_internal_templateparser.php',
'Test\\DB\\TestDB' => __DIR__ . '/../..' . '/lib/Test/DB/TestDB.php',
'Test\\Test' => __DIR__ . '/../..' . '/lib/Test/Test.php',
'TestCalls\\DB\\TestDB' => __DIR__ . '/../..' . '/lib/Test/DB/TestDB.php',
'TestCalls\\Test' => __DIR__ . '/../..' . '/lib/Test/Test.php',
'TheSeer\\Tokenizer\\Exception' => __DIR__ . '/..' . '/theseer/tokenizer/src/Exception.php',
'TheSeer\\Tokenizer\\NamespaceUri' => __DIR__ . '/..' . '/theseer/tokenizer/src/NamespaceUri.php',
'TheSeer\\Tokenizer\\NamespaceUriException' => __DIR__ . '/..' . '/theseer/tokenizer/src/NamespaceUriException.php',

View File

@@ -227,17 +227,17 @@
},
{
"name": "nikic/php-parser",
"version": "v4.15.5",
"version_normalized": "4.15.5.0",
"version": "v4.17.1",
"version_normalized": "4.17.1.0",
"source": {
"type": "git",
"url": "https://github.com/nikic/PHP-Parser.git",
"reference": "11e2663a5bc9db5d714eedb4277ee300403b4a9e"
"reference": "a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/11e2663a5bc9db5d714eedb4277ee300403b4a9e",
"reference": "11e2663a5bc9db5d714eedb4277ee300403b4a9e",
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d",
"reference": "a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d",
"shasum": ""
},
"require": {
@@ -248,7 +248,7 @@
"ircmaxell/php-yacc": "^0.0.7",
"phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0"
},
"time": "2023-05-19T20:20:00+00:00",
"time": "2023-08-13T19:53:39+00:00",
"bin": [
"bin/php-parse"
],
@@ -280,7 +280,7 @@
],
"support": {
"issues": "https://github.com/nikic/PHP-Parser/issues",
"source": "https://github.com/nikic/PHP-Parser/tree/v4.15.5"
"source": "https://github.com/nikic/PHP-Parser/tree/v4.17.1"
},
"install-path": "../nikic/php-parser"
},
@@ -403,17 +403,17 @@
},
{
"name": "phpunit/php-code-coverage",
"version": "9.2.26",
"version_normalized": "9.2.26.0",
"version": "9.2.27",
"version_normalized": "9.2.27.0",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-code-coverage.git",
"reference": "443bc6912c9bd5b409254a40f4b0f4ced7c80ea1"
"reference": "b0a88255cb70d52653d80c890bd7f38740ea50d1"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/443bc6912c9bd5b409254a40f4b0f4ced7c80ea1",
"reference": "443bc6912c9bd5b409254a40f4b0f4ced7c80ea1",
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/b0a88255cb70d52653d80c890bd7f38740ea50d1",
"reference": "b0a88255cb70d52653d80c890bd7f38740ea50d1",
"shasum": ""
},
"require": {
@@ -438,7 +438,7 @@
"ext-pcov": "PHP extension that provides line coverage",
"ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage"
},
"time": "2023-03-06T12:58:08+00:00",
"time": "2023-07-26T13:44:30+00:00",
"type": "library",
"extra": {
"branch-alias": {
@@ -471,7 +471,8 @@
],
"support": {
"issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.26"
"security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy",
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.27"
},
"funding": [
{
@@ -736,17 +737,17 @@
},
{
"name": "phpunit/phpunit",
"version": "9.6.8",
"version_normalized": "9.6.8.0",
"version": "9.6.11",
"version_normalized": "9.6.11.0",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git",
"reference": "17d621b3aff84d0c8b62539e269e87d8d5baa76e"
"reference": "810500e92855eba8a7a5319ae913be2da6f957b0"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/17d621b3aff84d0c8b62539e269e87d8d5baa76e",
"reference": "17d621b3aff84d0c8b62539e269e87d8d5baa76e",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/810500e92855eba8a7a5319ae913be2da6f957b0",
"reference": "810500e92855eba8a7a5319ae913be2da6f957b0",
"shasum": ""
},
"require": {
@@ -782,7 +783,7 @@
"ext-soap": "To be able to generate mocks based on WSDL files",
"ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage"
},
"time": "2023-05-11T05:14:45+00:00",
"time": "2023-08-19T07:10:56+00:00",
"bin": [
"phpunit"
],
@@ -822,7 +823,7 @@
"support": {
"issues": "https://github.com/sebastianbergmann/phpunit/issues",
"security": "https://github.com/sebastianbergmann/phpunit/security/policy",
"source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.8"
"source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.11"
},
"funding": [
{
@@ -1423,17 +1424,17 @@
},
{
"name": "sebastian/global-state",
"version": "5.0.5",
"version_normalized": "5.0.5.0",
"version": "5.0.6",
"version_normalized": "5.0.6.0",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/global-state.git",
"reference": "0ca8db5a5fc9c8646244e629625ac486fa286bf2"
"reference": "bde739e7565280bda77be70044ac1047bc007e34"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/0ca8db5a5fc9c8646244e629625ac486fa286bf2",
"reference": "0ca8db5a5fc9c8646244e629625ac486fa286bf2",
"url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bde739e7565280bda77be70044ac1047bc007e34",
"reference": "bde739e7565280bda77be70044ac1047bc007e34",
"shasum": ""
},
"require": {
@@ -1448,7 +1449,7 @@
"suggest": {
"ext-uopz": "*"
},
"time": "2022-02-14T08:28:10+00:00",
"time": "2023-08-02T09:26:13+00:00",
"type": "library",
"extra": {
"branch-alias": {
@@ -1478,7 +1479,7 @@
],
"support": {
"issues": "https://github.com/sebastianbergmann/global-state/issues",
"source": "https://github.com/sebastianbergmann/global-state/tree/5.0.5"
"source": "https://github.com/sebastianbergmann/global-state/tree/5.0.6"
},
"funding": [
{

View File

@@ -56,9 +56,9 @@
'dev_requirement' => true,
),
'nikic/php-parser' => array(
'pretty_version' => 'v4.15.5',
'version' => '4.15.5.0',
'reference' => '11e2663a5bc9db5d714eedb4277ee300403b4a9e',
'pretty_version' => 'v4.17.1',
'version' => '4.17.1.0',
'reference' => 'a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d',
'type' => 'library',
'install_path' => __DIR__ . '/../nikic/php-parser',
'aliases' => array(),
@@ -83,9 +83,9 @@
'dev_requirement' => true,
),
'phpunit/php-code-coverage' => array(
'pretty_version' => '9.2.26',
'version' => '9.2.26.0',
'reference' => '443bc6912c9bd5b409254a40f4b0f4ced7c80ea1',
'pretty_version' => '9.2.27',
'version' => '9.2.27.0',
'reference' => 'b0a88255cb70d52653d80c890bd7f38740ea50d1',
'type' => 'library',
'install_path' => __DIR__ . '/../phpunit/php-code-coverage',
'aliases' => array(),
@@ -128,9 +128,9 @@
'dev_requirement' => true,
),
'phpunit/phpunit' => array(
'pretty_version' => '9.6.8',
'version' => '9.6.8.0',
'reference' => '17d621b3aff84d0c8b62539e269e87d8d5baa76e',
'pretty_version' => '9.6.11',
'version' => '9.6.11.0',
'reference' => '810500e92855eba8a7a5319ae913be2da6f957b0',
'type' => 'library',
'install_path' => __DIR__ . '/../phpunit/phpunit',
'aliases' => array(),
@@ -218,9 +218,9 @@
'dev_requirement' => true,
),
'sebastian/global-state' => array(
'pretty_version' => '5.0.5',
'version' => '5.0.5.0',
'reference' => '0ca8db5a5fc9c8646244e629625ac486fa286bf2',
'pretty_version' => '5.0.6',
'version' => '5.0.6.0',
'reference' => 'bde739e7565280bda77be70044ac1047bc007e34',
'type' => 'library',
'install_path' => __DIR__ . '/../sebastian/global-state',
'aliases' => array(),

View File

@@ -1008,7 +1008,7 @@ array_pair:
| expr { $$ = Expr\ArrayItem[$1, null, false]; }
| expr T_DOUBLE_ARROW ampersand variable { $$ = Expr\ArrayItem[$4, $1, true]; }
| ampersand variable { $$ = Expr\ArrayItem[$2, null, true]; }
| T_ELLIPSIS expr { $$ = Expr\ArrayItem[$2, null, false, attributes(), true]; }
| T_ELLIPSIS expr { $$ = new Expr\ArrayItem($2, null, false, attributes(), true); }
;
encaps_list:

View File

@@ -221,7 +221,10 @@ non_empty_class_const_list:
;
class_const:
identifier_maybe_reserved '=' expr { $$ = Node\Const_[$1, $3]; }
T_STRING '=' expr
{ $$ = Node\Const_[new Node\Identifier($1, stackAttributes(#1)), $3]; }
| semi_reserved '=' expr
{ $$ = Node\Const_[new Node\Identifier($1, stackAttributes(#1)), $3]; }
;
inner_statement_list_ex:
@@ -722,6 +725,9 @@ class_statement:
| optional_attributes method_modifiers T_CONST class_const_list semi
{ $$ = new Stmt\ClassConst($4, $2, attributes(), $1);
$this->checkClassConst($$, #2); }
| optional_attributes method_modifiers T_CONST type_expr class_const_list semi
{ $$ = new Stmt\ClassConst($5, $2, attributes(), $1, $4);
$this->checkClassConst($$, #2); }
| optional_attributes method_modifiers T_FUNCTION optional_ref identifier_maybe_reserved '(' parameter_list ')'
optional_return_type method_body
{ $$ = Stmt\ClassMethod[$5, ['type' => $2, 'byRef' => $4, 'params' => $7, 'returnType' => $9, 'stmts' => $10, 'attrGroups' => $1]];
@@ -943,8 +949,8 @@ expr:
;
anonymous_class:
optional_attributes T_CLASS ctor_arguments extends_from implements_list '{' class_statement_list '}'
{ $$ = array(Stmt\Class_[null, ['type' => 0, 'extends' => $4, 'implements' => $5, 'stmts' => $7, 'attrGroups' => $1]], $3);
optional_attributes class_entry_type ctor_arguments extends_from implements_list '{' class_statement_list '}'
{ $$ = array(Stmt\Class_[null, ['type' => $2, 'extends' => $4, 'implements' => $5, 'stmts' => $7, 'attrGroups' => $1]], $3);
$this->checkClass($$[0], -1); }
;
@@ -1040,6 +1046,8 @@ constant:
class_constant:
class_name_or_var T_PAAMAYIM_NEKUDOTAYIM identifier_maybe_reserved
{ $$ = Expr\ClassConstFetch[$1, $3]; }
| class_name_or_var T_PAAMAYIM_NEKUDOTAYIM '{' expr '}'
{ $$ = Expr\ClassConstFetch[$1, $4]; }
/* We interpret an isolated FOO:: as an unfinished class constant fetch. It could also be
an unfinished static property fetch or unfinished scoped call. */
| class_name_or_var T_PAAMAYIM_NEKUDOTAYIM error
@@ -1194,7 +1202,7 @@ array_pair:
| expr T_DOUBLE_ARROW expr { $$ = Expr\ArrayItem[$3, $1, false]; }
| expr T_DOUBLE_ARROW ampersand variable { $$ = Expr\ArrayItem[$4, $1, true]; }
| expr T_DOUBLE_ARROW list_expr { $$ = Expr\ArrayItem[$3, $1, false]; }
| T_ELLIPSIS expr { $$ = Expr\ArrayItem[$2, null, false, attributes(), true]; }
| T_ELLIPSIS expr { $$ = new Expr\ArrayItem($2, null, false, attributes(), true); }
| /* empty */ { $$ = null; }
;

View File

@@ -19,6 +19,8 @@ class ClassConst implements PhpParser\Builder
/** @var Node\AttributeGroup[] */
protected $attributeGroups = [];
/** @var Identifier|Node\Name|Node\ComplexType */
protected $type;
/**
* Creates a class constant builder
@@ -116,6 +118,19 @@ class ClassConst implements PhpParser\Builder
return $this;
}
/**
* Sets the constant type.
*
* @param string|Node\Name|Identifier|Node\ComplexType $type
*
* @return $this
*/
public function setType($type) {
$this->type = BuilderHelpers::normalizeType($type);
return $this;
}
/**
* Returns the built class node.
*
@@ -126,7 +141,8 @@ class ClassConst implements PhpParser\Builder
$this->constants,
$this->flags,
$this->attributes,
$this->attributeGroups
$this->attributeGroups,
$this->type
);
}
}

View File

@@ -349,15 +349,15 @@ class BuilderFactory
/**
* Creates a class constant fetch node.
*
* @param string|Name|Expr $class Class name
* @param string|Identifier $name Constant name
* @param string|Name|Expr $class Class name
* @param string|Identifier|Expr $name Constant name
*
* @return Expr\ClassConstFetch
*/
public function classConstFetch($class, $name): Expr\ClassConstFetch {
return new Expr\ClassConstFetch(
BuilderHelpers::normalizeNameOrExpr($class),
BuilderHelpers::normalizeIdentifier($name)
BuilderHelpers::normalizeIdentifierOrExpr($name)
);
}

View File

@@ -19,6 +19,8 @@ class PrintableNewAnonClassNode extends Expr
{
/** @var Node\AttributeGroup[] PHP attribute groups */
public $attrGroups;
/** @var int Modifiers */
public $flags;
/** @var Node\Arg[] Arguments */
public $args;
/** @var null|Node\Name Name of extended class */
@@ -29,11 +31,12 @@ class PrintableNewAnonClassNode extends Expr
public $stmts;
public function __construct(
array $attrGroups, array $args, Node\Name $extends = null, array $implements,
array $attrGroups, int $flags, array $args, Node\Name $extends = null, array $implements,
array $stmts, array $attributes
) {
parent::__construct($attributes);
$this->attrGroups = $attrGroups;
$this->flags = $flags;
$this->args = $args;
$this->extends = $extends;
$this->implements = $implements;
@@ -46,7 +49,7 @@ class PrintableNewAnonClassNode extends Expr
// We don't assert that $class->name is null here, to allow consumers to assign unique names
// to anonymous classes for their own purposes. We simplify ignore the name here.
return new self(
$class->attrGroups, $newNode->args, $class->extends, $class->implements,
$class->attrGroups, $class->flags, $newNode->args, $class->extends, $class->implements,
$class->stmts, $newNode->getAttributes()
);
}
@@ -56,6 +59,6 @@ class PrintableNewAnonClassNode extends Expr
}
public function getSubNodeNames() : array {
return ['attrGroups', 'args', 'extends', 'implements', 'stmts'];
return ['attrGroups', 'flags', 'args', 'extends', 'implements', 'stmts'];
}
}

Some files were not shown because too many files have changed in this diff Show More