Compare commits

...

27 Commits

Author SHA1 Message Date
Clemens Schwaighofer
f9558cd3aa Fix for the SetVarType / SetVarTypeNull
string: if it is stringable in anyway, set string (it converts)
this is not check IF it is a string value as it was before
int/float: same, if it is numerc it will be convert to int or float
All other stay the same

Note "set ..." imply to set, and not to convert to 0 if it is int
string that can be covnerted to int
2023-09-05 14:26:57 +09:00
Clemens Schwaighofer
ae3011fe22 php unit test fix for DB\IO 2023-09-01 18:29:00 +09:00
Clemens Schwaighofer
9b9dfeac69 phan error supress 2023-09-01 18:21:25 +09:00
Clemens Schwaighofer
33cb05a002 Update to Exceptions: add codes, update phpunit tests
DB Class throws Exception if on init it fails to connect to the DB,
will not throw Exception if failed connection during execution but
will do the normal retry and soft failure run
DB\ArrayIO will throw Exception on missing table array and table name

All Exceptions have a code set
2023-09-01 08:37:15 +09:00
Clemens Schwaighofer
ec110499a8 phan fixs 2023-08-31 19:06:16 +09:00
Clemens Schwaighofer
09839f3451 phpstan fixes 2023-08-31 19:04:45 +09:00
Clemens Schwaighofer
067e0aed5d L10n change Exception to RuntimeException 2023-08-31 18:08:34 +09:00
Clemens Schwaighofer
545de5c4a1 Fixed more Exceptions to be not Errors but Exceptions
DateTime, Session, FileWrite, Image, SymmetricEncryption

phpunit tests updated, run checks added
2023-08-31 18:06:02 +09:00
Clemens Schwaighofer
2fe37bf92a Exceptions change in Check\Colors, add in Cmbined\ArrayHandler
Chech\Colors now throws correct exceptions for wrong values
Combined\ArrayHandler will throw errors and not return false
2023-08-31 12:07:28 +09:00
Clemens Schwaighofer
cd81d15d9a Convert\Color methods will throw Exception instead of false on error
All Color methods will throw Exceptions:
LengthException,
InvalidArgumentException,
UnexpectedValueException

instead of returning bool: false

All methods will return valid color data as expected only
2023-08-31 10:45:33 +09:00
Clemens Schwaighofer
8a33ee5c15 Slight update for ACL\Login class exit codes
exit will add message as first parameter (string) next to code (int)
Log this to info or critical.
3000 -> 100: info
rest >=1000: critical
previous 4000 = 3000 (options not set)

update unit tests for this

Possible change idea: critical abort throw error?
2023-08-31 10:41:44 +09:00
Clemens Schwaighofer
46e1419ef5 phan checks and updates 2023-08-30 19:26:13 +09:00
Clemens Schwaighofer
c441063437 Composer updates 2023-08-30 19:25:48 +09:00
Clemens Schwaighofer
5290d5f351 Update db class tests in admin run 2023-08-28 09:28:17 +09:00
Clemens Schwaighofer
2635ccb82b edit.css: rename animation, Bug fix in DB\IO cursor_ext access and others
Make sure cursor_ext is set before we access it, else return null for
not set yet.
false for errors, else data value

Other class var access checks to be sure to never fail
2023-08-28 07:40:41 +09:00
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
Clemens Schwaighofer
46bc5f2da6 Json phpunit tests updates, fixes in test php with ignore for deprecated 2023-08-02 16:22:09 +09:00
Clemens Schwaighofer
d70182a84e Add JSON convert array to json with always return string
Allows the same post run error check like the other way around
2023-08-02 16:12:01 +09:00
Clemens Schwaighofer
7243f69826 Email Type class returns correct "false" instead of "bool" 2023-08-02 16:08:39 +09:00
Clemens Schwaighofer
1fc144e178 Composer Workspace global packages 2023-08-02 14:52:33 +09:00
Clemens Schwaighofer
c383a7b7b7 Update convert colors to return false and not bool
All convert color either return the color value or false.
To make sure any checker knows that we only return "value" or "false"
change all return bool to false
2023-08-02 07:29:49 +09:00
Clemens Schwaighofer
69077c384c phpdoc fix for DB\IO dbGetCursorNumRows 2023-08-01 09:00:43 +09:00
Clemens Schwaighofer
cfd49947ad Bug fix in ACL\Login: mnake sure ['base'] acl is int 2023-07-26 11:48:56 +09:00
Clemens Schwaighofer
6985dc4e9d ACL\Login fix for UNIT DEFAULT return
It has to be int or null but because the SQL result is undefined (string)
it needs to be converted on return if it is a numeric value, else
null will be returned (it is the edit access id PK so it has to be
numeric)
2023-07-24 09:11:32 +09:00
543 changed files with 12121 additions and 7373 deletions

View File

@@ -132,6 +132,8 @@ return [
// start ignore annotations
'PhanUnextractableAnnotationElementName',
'PhanUnextractableAnnotationSuffix',
// enum problems in comments
'PhanCommentObjectInClassConstantType'
],
// Override to hardcode existence and types of (non-builtin) globals in the global scope.

View File

@@ -167,8 +167,10 @@ final class CoreLibsACLLoginTest extends TestCase
// change_password, pw_username, pw_old_password, pw_new_password,
// pw_new_password_confirm
// 3[session]: override session set
// 4[error] : expected error code, 0 for all ok, 3000 for login page view
// note that 1000 (no db), 2000 (no session) must be tested too
// 4[error] : expected error code, 0 for all ok, 100 for login page view
// note that 1000 (no db), 2000 (no session), 3000 (options set error)
// must be tested too
// <1000 info, >=1000 critical error
// 5[return] : expected return array, eg login_error code,
// or other info data to match
$tests = [
@@ -180,7 +182,7 @@ final class CoreLibsACLLoginTest extends TestCase
[],
[],
[],
3000,
100,
[
'login_error' => 0,
'error_string' => 'Success: <b>No error</b>',
@@ -198,7 +200,7 @@ final class CoreLibsACLLoginTest extends TestCase
[],
[],
[],
3000,
100,
[
'login_error' => 0,
'error_string' => 'Success: <b>No error</b>',
@@ -221,7 +223,7 @@ final class CoreLibsACLLoginTest extends TestCase
[],
[],
[],
3000,
100,
[
'login_error' => 0,
'error_string' => 'Success: <b>No error</b>',
@@ -308,7 +310,7 @@ final class CoreLibsACLLoginTest extends TestCase
'login_password' => '',
],
[],
3000,
100,
[
'login_error' => 102,
'error_string' => '<span style="color: red;">Fatal Error:</span> '
@@ -329,7 +331,7 @@ final class CoreLibsACLLoginTest extends TestCase
'login_password' => 'abc',
],
[],
3000,
100,
[
'login_error' => 102,
'error_string' => '<span style="color: red;">Fatal Error:</span> '
@@ -350,7 +352,7 @@ final class CoreLibsACLLoginTest extends TestCase
'login_password' => '',
],
[],
3000,
100,
[
'login_error' => 102,
'error_string' => '<span style="color: red;">Fatal Error:</span> '
@@ -371,7 +373,7 @@ final class CoreLibsACLLoginTest extends TestCase
'login_password' => 'abc',
],
[],
3000,
100,
[
'login_error' => 1010,
'error_string' => '<span style="color: red;">Fatal Error:</span> '
@@ -395,7 +397,7 @@ final class CoreLibsACLLoginTest extends TestCase
'login_password' => 'abc',
],
[],
3000,
100,
[
// default password is plain text
'login_error' => 1012,
@@ -421,7 +423,7 @@ final class CoreLibsACLLoginTest extends TestCase
'login_password' => 'admin',
],
[],
3000,
100,
[
'login_error' => 106,
'error_string' => '<span style="color: red;">Fatal Error:</span> '
@@ -446,7 +448,7 @@ final class CoreLibsACLLoginTest extends TestCase
'login_password' => 'admin',
],
[],
3000,
100,
[
'login_error' => 104,
'error_string' => '<span style="color: red;">Fatal Error:</span> '
@@ -471,7 +473,7 @@ final class CoreLibsACLLoginTest extends TestCase
'login_password' => 'admin',
],
[],
3000,
100,
[
'login_error' => 105,
'error_string' => '<span style="color: red;">Fatal Error:</span> '
@@ -520,7 +522,7 @@ final class CoreLibsACLLoginTest extends TestCase
'login_password' => 'admin',
],
[],
3000,
100,
[
'login_error' => 107,
'error_string' => '<span style="color: red;">Fatal Error:</span> '
@@ -574,7 +576,7 @@ final class CoreLibsACLLoginTest extends TestCase
'login_password' => 'admin',
],
[],
3000,
100,
[
'login_error' => 107,
'error_string' => '<span style="color: red;">Fatal Error:</span> '
@@ -600,7 +602,7 @@ final class CoreLibsACLLoginTest extends TestCase
'login_password' => 'admin',
],
[],
3000,
100,
[
'login_error' => 107,
'error_string' => '<span style="color: red;">Fatal Error:</span> '
@@ -625,7 +627,7 @@ final class CoreLibsACLLoginTest extends TestCase
'login_password' => 'admin',
],
[],
3000,
100,
[
'login_error' => 108,
'error_string' => '<span style="color: red;">Fatal Error:</span> '
@@ -761,7 +763,7 @@ final class CoreLibsACLLoginTest extends TestCase
],
[],
[],
3000,
100,
[
'login_error' => 1010,
'error_string' => '<span style="color: red;">Fatal Error:</span> '
@@ -853,7 +855,7 @@ final class CoreLibsACLLoginTest extends TestCase
],
[],
[],
3000,
100,
[
'login_error' => 1101,
'error_string' => '<span style="color: red;">Fatal Error:</span> '
@@ -909,7 +911,7 @@ final class CoreLibsACLLoginTest extends TestCase
],
[],
[],
3000,
100,
[
'login_error' => 1102,
'error_string' => '<span style="color: red;">Fatal Error:</span> '
@@ -965,7 +967,7 @@ final class CoreLibsACLLoginTest extends TestCase
],
[],
[],
3000,
100,
[
'login_error' => 1102,
'error_string' => '<span style="color: red;">Fatal Error:</span> '
@@ -992,7 +994,7 @@ final class CoreLibsACLLoginTest extends TestCase
],
[],
[],
3000,
100,
[
'login_error' => 1102,
'error_string' => '<span style="color: red;">Fatal Error:</span> '
@@ -1133,7 +1135,7 @@ final class CoreLibsACLLoginTest extends TestCase
$login_mock->expects($this->any())
->method('loginTerminate')
->will(
$this->returnCallback(function ($code) {
$this->returnCallback(function ($message, $code) {
throw new \Exception('', $code);
})
);
@@ -1227,7 +1229,11 @@ final class CoreLibsACLLoginTest extends TestCase
$login_mock->loginSetMaxLoginErrorCount($mock_settings['max_login_error_count']);
// temporary wrong password
$_POST['login_password'] = 'wrong';
for ($run = 1, $max_run = $login_mock->loginGetMaxLoginErrorCount(); $run <= $max_run; $run++) {
for (
$run = 1, $max_run = $login_mock->loginGetMaxLoginErrorCount();
$run <= $max_run;
$run++
) {
try {
$login_mock->loginMainCall();
} catch (\Exception $e) {
@@ -1475,10 +1481,10 @@ final class CoreLibsACLLoginTest extends TestCase
// print "AJAX: " . $login_mock->loginGetAjaxFlag() . "\n";
// print "AJAX GLOBAL: " . ($GLOBALS['AJAX_PAGE'] ?? '{f}') . "\n";
// print "Login error expext: " . ($expected['login_error'] ?? '{0}') . "\n";
// if this is 3000, then we do further error checks
// if this is 100, then we do further error checks
if (
$e->getCode() == 3000 ||
!empty($_POST['login_exit']) && $_POST['login_exit'] == 3000
$e->getCode() == 100 ||
!empty($_POST['login_exit']) && $_POST['login_exit'] == 100
) {
$this->assertEquals(
$expected['login_error'],
@@ -1816,7 +1822,7 @@ final class CoreLibsACLLoginTest extends TestCase
$login_mock->expects($this->any())
->method('loginTerminate')
->will(
$this->returnCallback(function ($code) {
$this->returnCallback(function ($message, $code) {
throw new \Exception('', $code);
})
);
@@ -1930,7 +1936,7 @@ final class CoreLibsACLLoginTest extends TestCase
$login_mock->expects($this->any())
->method('loginTerminate')
->will(
$this->returnCallback(function ($code) {
$this->returnCallback(function ($message, $code) {
throw new \Exception('', $code);
})
);
@@ -2018,7 +2024,7 @@ final class CoreLibsACLLoginTest extends TestCase
$login_mock->expects($this->any())
->method('loginTerminate')
->will(
$this->returnCallback(function ($code) {
$this->returnCallback(function ($message, $code) {
throw new \Exception('', $code);
})
);
@@ -2114,7 +2120,7 @@ final class CoreLibsACLLoginTest extends TestCase
$login_mock->expects($this->any())
->method('loginTerminate')
->will(
$this->returnCallback(function ($code) {
$this->returnCallback(function ($message, $code) {
throw new \Exception('', $code);
})
);

View File

@@ -13,6 +13,11 @@ use PHPUnit\Framework\TestCase;
*/
final class CoreLibsCheckColorsTest extends TestCase
{
/**
* Undocumented function
*
* @return array<mixed>
*/
public function validateColorProvider(): array
{
/*
@@ -321,7 +326,7 @@ final class CoreLibsCheckColorsTest extends TestCase
*/
public function testValidateColorException(int $flag): void
{
$this->expectException(\Exception::class);
$this->expectException(\UnexpectedValueException::class);
\CoreLibs\Check\Colors::validateColor('#ffffff', $flag);
}
}

View File

@@ -518,17 +518,20 @@ final class CoreLibsCombinedArrayHandlerTest extends TestCase
return [
// error <2 arguments
'too view arguments' => [
'ArgumentCountError',
'arrayMergeRecursive needs two or more array arguments',
[1]
],
// error <2 arrays
'only one array' => [
'ArgumentCountError',
'arrayMergeRecursive needs two or more array arguments',
[1],
true,
],
// error element is not array
'non array between array' => [
'TypeError',
'arrayMergeRecursive encountered a non array argument',
[1],
'string',
@@ -947,18 +950,20 @@ final class CoreLibsCombinedArrayHandlerTest extends TestCase
*/
public function testArrayMergeRecursiveWarningA(): void
{
set_error_handler(
static function (int $errno, string $errstr): never {
throw new Exception($errstr, $errno);
},
E_USER_WARNING
);
// set_error_handler(
// static function (int $errno, string $errstr): never {
// throw new Exception($errstr, $errno);
// },
// E_USER_WARNING
// );
$arrays = func_get_args();
// first is expected warning
$exception = array_shift($arrays);
$warning = array_shift($arrays);
// phpunit 10.0 compatible
$this->expectException($exception);
$this->expectExceptionMessage($warning);
\CoreLibs\Combined\ArrayHandler::arrayMergeRecursive(...$arrays);

View File

@@ -309,45 +309,73 @@ final class CoreLibsCombinedDateTimeTest extends TestCase
'2020-12-12',
'2021-12-12',
-1,
null,
null,
],
'dates equal' => [
'2020-12-12',
'2020-12-12',
0,
null,
null,
],
'second date smaller' => [
'2021-12-12',
'2020-12-12',
1
1,
null,
null,
],
'dates equal with different time' => [
'2020-12-12 12:12:12',
'2020-12-12 13:13:13',
0,
null,
null,
],
'invalid dates --' => [
'--',
'--',
false
false,
'UnexpectedValueException',
1,
],
'empty dates' => [
'',
'',
false
false,
'UnexpectedValueException',
1
],
'invalid dates' => [
'not a date',
'not a date either',
false,
'UnexpectedValueException',
2
],
'invalid end date' => [
'1990-01-01',
'not a date either',
false,
'UnexpectedValueException',
3
],
'out of bound dates' => [
'1900-1-1',
'9999-12-31',
-1
-1,
null,
null,
]
];
}
/**
* Undocumented function
*
* @return array<mixed>
*/
public function dateTimeCompareProvider(): array
{
return [
@@ -355,51 +383,85 @@ final class CoreLibsCombinedDateTimeTest extends TestCase
'2020-12-12',
'2021-12-12',
-1,
null,
null,
],
'dates equal no timestamp' => [
'2020-12-12',
'2020-12-12',
0,
null,
null,
],
'second date smaller no timestamp' => [
'2021-12-12',
'2020-12-12',
1
1,
null,
null,
],
'date equal first time smaller' => [
'2020-12-12 12:12:12',
'2020-12-12 13:13:13',
-1,
null,
null,
],
'date equal time equal' => [
'2020-12-12 12:12:12',
'2020-12-12 12:12:12',
0,
null,
null,
],
'date equal second time smaller' => [
'2020-12-12 13:13:13',
'2020-12-12 12:12:12',
1,
null,
null,
],
'valid date invalid time' => [
'2020-12-12 13:99:13',
'2020-12-12 12:12:99',
false,
'UnexpectedValueException',
2
],
'valid date invalid end time' => [
'2020-12-12 13:12:13',
'2020-12-12 12:12:99',
false,
'UnexpectedValueException',
3
],
'invalid datetimes --' => [
'--',
'--',
false,
'UnexpectedValueException',
1
],
'empty datetimess' => [
'',
'',
false,
'UnexpectedValueException',
1
],
'invalid datetimes' => [
'invalid date times' => [
'not a date',
'not a date either',
false,
'UnexpectedValueException',
2
],
'invalid end date time' => [
'1990-01-01 12:12:12',
'not a date either',
false,
'UnexpectedValueException',
3
],
];
}
@@ -614,10 +676,21 @@ final class CoreLibsCombinedDateTimeTest extends TestCase
* @param string $input_a
* @param string $input_b
* @param int|bool $expected
* @param string|null $exception
* @param int|null $exception_code
* @return void
*/
public function testCompareDate(string $input_a, string $input_b, $expected): void
{
public function testCompareDate(
string $input_a,
string $input_b,
int|bool $expected,
?string $exception,
?int $exception_code
): void {
if ($expected === false) {
$this->expectException($exception);
$this->expectExceptionCode($exception_code);
}
$this->assertEquals(
$expected,
\CoreLibs\Combined\DateTime::compareDate($input_a, $input_b)
@@ -634,10 +707,21 @@ final class CoreLibsCombinedDateTimeTest extends TestCase
* @param string $input_a
* @param string $input_b
* @param int|bool $expected
* @param string|null $exception
* @param int|null $exception_code
* @return void
*/
public function testCompareDateTime(string $input_a, string $input_b, $expected): void
{
public function testCompareDateTime(
string $input_a,
string $input_b,
int|bool $expected,
?string $exception,
?int $exception_code
): void {
if ($expected === false) {
$this->expectException($exception);
$this->expectExceptionCode($exception_code);
}
$this->assertEquals(
$expected,
\CoreLibs\Combined\DateTime::compareDateTime($input_a, $input_b)

View File

@@ -59,6 +59,27 @@ final class CoreLibsConvertColorsTest extends TestCase
3 => false,
4 => false
],
'invalid color red ' => [
0 => -12,
1 => 12,
2 => 12,
3 => false,
4 => false
],
'invalid color green ' => [
0 => 12,
1 => -12,
2 => 12,
3 => false,
4 => false
],
'invalid color blue ' => [
0 => 12,
1 => 12,
2 => -12,
3 => false,
4 => false
],
];
}
@@ -150,10 +171,40 @@ final class CoreLibsConvertColorsTest extends TestCase
'valid' => true,
],
// invalid values
'invalid color' => [
'rgb' => [-12, 300, 12],
'hsb' => [-12, 300, 12],
'hsl' => [-12, 300, 12],
'invalid color r/h/h low' => [
'rgb' => [-1, 12, 12],
'hsb' => [-1, 50, 50],
'hsl' => [-1, 50, 50],
'valid' => false,
],
'invalid color r/h/h high' => [
'rgb' => [256, 12, 12],
'hsb' => [361, 50, 50],
'hsl' => [361, 50, 50],
'valid' => false,
],
'invalid color g/s/s low' => [
'rgb' => [12, -1, 12],
'hsb' => [1, -1, 50],
'hsl' => [1, -1, 50],
'valid' => false,
],
'invalid color g/s/s high' => [
'rgb' => [12, 256, 12],
'hsb' => [1, 101, 50],
'hsl' => [1, 101, 50],
'valid' => false,
],
'invalid color b/b/l low' => [
'rgb' => [12, 12, -1],
'hsb' => [1, 50, -1],
'hsl' => [1, 50, -1],
'valid' => false,
],
'invalid color b/b/l high' => [
'rgb' => [12, 12, 256],
'hsb' => [1, 50, 101],
'hsl' => [1, 50, 101],
'valid' => false,
],
];
@@ -246,11 +297,22 @@ final class CoreLibsConvertColorsTest extends TestCase
* @param int $input_r
* @param int $input_g
* @param int $input_b
* @param string|bool $expected_hash
* @param string|bool $expected
* @return void
*/
public function testRgb2hex(int $input_r, int $input_g, int $input_b, $expected_hash, $expected)
{
public function testRgb2hex(
int $input_r,
int $input_g,
int $input_b,
string|bool $expected_hash,
string|bool $expected
) {
// if expected hash is or expected is false, we need to check for
// LengthException
if ($expected_hash === false || $expected === false) {
$this->expectException(\LengthException::class);
}
// with #
$this->assertEquals(
$expected_hash,
@@ -292,11 +354,19 @@ final class CoreLibsConvertColorsTest extends TestCase
*/
public function testHex2rgb(
string $input,
$expected,
$expected_str,
array|bool $expected,
string|bool $expected_str,
string $separator,
$expected_str_sep
string|bool $expected_str_sep
): void {
if ($expected === false || $expected_str === false || $expected_str_sep === false) {
$hex_string = preg_replace("/[^0-9A-Fa-f]/", '', $input);
if (!is_string($hex_string)) {
$this->expectException(\InvalidArgumentException::class);
} else {
$this->expectException(\UnexpectedValueException::class);
}
}
$this->assertEquals(
$expected,
\CoreLibs\Convert\Colors::hex2rgb($input)
@@ -324,8 +394,11 @@ final class CoreLibsConvertColorsTest extends TestCase
* @param array|bool $expected
* @return void
*/
public function testRgb2hsb(int $input_r, int $input_g, int $input_b, $expected): void
public function testRgb2hsb(int $input_r, int $input_g, int $input_b, array|bool $expected): void
{
if ($expected === false) {
$this->expectException(\LengthException::class);
}
$this->assertEquals(
$expected,
\CoreLibs\Convert\Colors::rgb2hsb($input_r, $input_g, $input_b)
@@ -345,8 +418,12 @@ final class CoreLibsConvertColorsTest extends TestCase
* @param array|bool $expected
* @return void
*/
public function testHsb2rgb(float $input_h, float $input_s, float $input_b, $expected): void
public function testHsb2rgb(float $input_h, float $input_s, float $input_b, array|bool $expected): void
{
if ($expected === false) {
$this->expectException(\LengthException::class);
$expected = [];
}
$this->assertEquals(
$expected,
\CoreLibs\Convert\Colors::hsb2rgb($input_h, $input_s, $input_b)
@@ -366,8 +443,11 @@ final class CoreLibsConvertColorsTest extends TestCase
* @param array|bool $expected
* @return void
*/
public function testRgb2hsl(int $input_r, int $input_g, int $input_b, $expected): void
public function testRgb2hsl(int $input_r, int $input_g, int $input_b, array|bool $expected): void
{
if ($expected === false) {
$this->expectException(\LengthException::class);
}
$this->assertEquals(
$expected,
\CoreLibs\Convert\Colors::rgb2hsl($input_r, $input_g, $input_b)
@@ -387,8 +467,11 @@ final class CoreLibsConvertColorsTest extends TestCase
* @param array|bool $expected
* @return void
*/
public function testHsl2rgb($input_h, float $input_s, float $input_l, $expected): void
public function testHsl2rgb(int|float $input_h, float $input_s, float $input_l, array|bool $expected): void
{
if ($expected === false) {
$this->expectException(\LengthException::class);
}
$this->assertEquals(
$expected,
\CoreLibs\Convert\Colors::hsl2rgb($input_h, $input_s, $input_l)
@@ -406,11 +489,11 @@ final class CoreLibsConvertColorsTest extends TestCase
*/
public function testHslHsb360hue(): void
{
$this->assertNotFalse(
$this->assertIsArray(
\CoreLibs\Convert\Colors::hsl2rgb(360.0, 90.5, 41.2),
'HSL to RGB with 360 hue'
);
$this->assertNotFalse(
$this->assertIsArray(
\CoreLibs\Convert\Colors::hsb2rgb(360, 95, 78.0),
'HSB to RGB with 360 hue'
);

View File

@@ -16,7 +16,7 @@ final class CoreLibsConvertJsonTest extends TestCase
/**
* test list for json convert tests
*
* @return array
* @return array<mixed>
*/
public function jsonProvider(): array
{
@@ -54,10 +54,36 @@ final class CoreLibsConvertJsonTest extends TestCase
];
}
/**
* Undocumented function
*
* @return array<mixed>
*/
public function jsonArrayProvider(): array
{
return [
'valid json' => [
[
'm' => 2,
'f' => 'sub_2'
],
'{"m":2,"f":"sub_2"}',
],
'empty json array' => [
[],
'[]'
],
'empty json hash' => [
['' => ''],
'{"":""}'
]
];
}
/**
* json error list
*
* @return array JSON error list
* @return array<mixed> JSON error list
*/
public function jsonErrorProvider(): array
{
@@ -127,7 +153,7 @@ final class CoreLibsConvertJsonTest extends TestCase
*
* @param string|null $input
* @param bool $flag
* @param array $expected
* @param array<mixed> $expected
* @return void
*/
public function testJsonConvertToArray(?string $input, bool $flag, array $expected): void
@@ -146,7 +172,8 @@ final class CoreLibsConvertJsonTest extends TestCase
* @testdox jsonGetLastError $input will be $expected_i/$expected_s [$_dataName]
*
* @param string|null $input
* @param string $expected
* @param int $expected_i
* @param string $expected_s
* @return void
*/
public function testJsonGetLastError(?string $input, int $expected_i, string $expected_s): void
@@ -161,6 +188,25 @@ final class CoreLibsConvertJsonTest extends TestCase
\CoreLibs\Convert\Json::jsonGetLastError(true)
);
}
/**
* Undocumented function
*
* @covers ::jsonConvertArrayTo
* @dataProvider jsonArrayProvider
* @testdox jsonConvertArrayTo $input (Override: $flag) will be $expected [$_dataName]
*
* @param array<mixed> $input
* @param string $expected
* @return void
*/
public function testJsonConvertArrayto(array $input, string $expected): void
{
$this->assertEquals(
$expected,
\CoreLibs\Convert\Json::jsonConvertArrayTo($input)
);
}
}
// __END__

View File

@@ -43,12 +43,17 @@ final class CoreLibsConvertSetVarTypeNullTest extends TestCase
'int, no override' => [
1,
null,
null
'1'
],
'int, override set' => [
1,
'not int',
'not int'
'1'
],
'array, override set' => [
[1, 2],
null,
null
]
];
}
@@ -201,7 +206,7 @@ final class CoreLibsConvertSetVarTypeNullTest extends TestCase
'float' => [
1.5,
null,
null
1
]
];
}
@@ -349,7 +354,7 @@ final class CoreLibsConvertSetVarTypeNullTest extends TestCase
'int' => [
1,
null,
null
1.0
]
];
}

View File

@@ -43,11 +43,16 @@ final class CoreLibsConvertSetVarTypeTest extends TestCase
'int, no override' => [
1,
null,
''
'1'
],
'int, override set' => [
1,
'not int',
'1'
],
'array, override set' => [
[1, 2],
'not int',
'not int'
]
];
@@ -189,7 +194,7 @@ final class CoreLibsConvertSetVarTypeTest extends TestCase
'float' => [
1.5,
null,
0
1
]
];
}
@@ -330,7 +335,7 @@ final class CoreLibsConvertSetVarTypeTest extends TestCase
'int' => [
1,
null,
0.0
1.0
]
];
}
@@ -341,7 +346,7 @@ final class CoreLibsConvertSetVarTypeTest extends TestCase
* @dataProvider varSetTypeFloatProvider
* @testdox setFloat $input with override $default will be $expected [$_dataName]
*
* @param mixed $input
* @param mixed $input
* @param float|null $default
* @param float $expected
* @return void

View File

@@ -30,8 +30,10 @@ final class CoreLibsCreateSessionTest extends TestCase
// setSessionName: true/false,
// checkActiveSession: true/false, [1st call, 2nd call]
// getSessionId: string or false
// 3: exepcted name (session)
// 4: expected error string
// 3: exepcted name (session)]
// 4: Exception thrown on error
// 5: exception code, null for none
// 6: expected error string
return [
'session parameter' => [
'sessionNameParameter',
@@ -44,7 +46,9 @@ final class CoreLibsCreateSessionTest extends TestCase
'getSessionId' => '1234abcd4567'
],
'sessionNameParameter',
''
null,
null,
'',
],
'session globals' => [
'sessionNameGlobals',
@@ -57,7 +61,9 @@ final class CoreLibsCreateSessionTest extends TestCase
'getSessionId' => '1234abcd4567'
],
'sessionNameGlobals',
''
null,
null,
'',
],
'session name default' => [
'',
@@ -70,7 +76,9 @@ final class CoreLibsCreateSessionTest extends TestCase
'getSessionId' => '1234abcd4567'
],
'',
''
null,
null,
'',
],
// error checks
// 1: we are in cli
@@ -85,6 +93,8 @@ final class CoreLibsCreateSessionTest extends TestCase
'getSessionId' => '1234abcd4567'
],
'',
'RuntimeException',
1,
'[SESSION] No sessions in php cli'
],
// 2: session disabled
@@ -99,6 +109,8 @@ final class CoreLibsCreateSessionTest extends TestCase
'getSessionId' => '1234abcd4567'
],
'',
'RuntimeException',
2,
'[SESSION] Sessions are disabled'
],
// 3: invalid session name: string
@@ -113,6 +125,8 @@ final class CoreLibsCreateSessionTest extends TestCase
'getSessionId' => '1234abcd4567'
],
'',
'UnexpectedValueException',
3,
'[SESSION] Invalid session name: 1invalid$session#;'
],
// 3: invalid session name: only numbers
@@ -127,6 +141,8 @@ final class CoreLibsCreateSessionTest extends TestCase
'getSessionId' => '1234abcd4567'
],
'',
'UnexpectedValueException',
3,
'[SESSION] Invalid session name: 123'
],
// 3: invalid session name: invalid name short
@@ -143,6 +159,8 @@ final class CoreLibsCreateSessionTest extends TestCase
'getSessionId' => '1234abcd4567'
],
'',
'RuntimeException',
4,
'[SESSION] Failed to activate session'
],
// 5: get session id return false
@@ -157,6 +175,8 @@ final class CoreLibsCreateSessionTest extends TestCase
'getSessionId' => false
],
'',
'UnexpectedValueException',
5,
'[SESSION] getSessionId did not return a session id'
],
];
@@ -173,6 +193,7 @@ final class CoreLibsCreateSessionTest extends TestCase
* @param string $type
* @param array<mixed> $mock_data
* @param string $expected
* @param string|null $exception
* @param string $expected_error
* @return void
*/
@@ -181,6 +202,8 @@ final class CoreLibsCreateSessionTest extends TestCase
string $type,
array $mock_data,
string $expected,
?string $exception,
?int $exception_code,
string $expected_error
): void {
// override expected
@@ -224,6 +247,11 @@ final class CoreLibsCreateSessionTest extends TestCase
// regex for session id
$ression_id_regex = "/^\w+$/";
if ($exception !== null) {
$this->expectException($exception);
$this->expectExceptionCode($exception_code);
}
unset($GLOBALS['SET_SESSION_NAME']);
$session_id = '';
switch ($type) {
@@ -253,13 +281,6 @@ final class CoreLibsCreateSessionTest extends TestCase
$expected,
$session_mock->getSessionName()
);
} else {
// false checks
$this->assertEquals(
$expected_error,
$session_mock->getErrorStr(),
'error assert'
);
}
}

View File

@@ -445,12 +445,14 @@ final class CoreLibsDBIOTest extends TestCase
{
// 0: connection array
// 1: status after connection
// 2: exception name
// 2: info string
// 3: ???
return [
'invalid connection' => [
self::$db_config['invalid'],
false,
'RuntimeException',
"-DB-info-> Connected to db '' with schema 'public' as user "
. "'' at host '' on port '5432' with ssl mode 'allow' **** "
. "-DB-info-> DB IO Class debug output: Yes **** ",
@@ -459,6 +461,7 @@ final class CoreLibsDBIOTest extends TestCase
'valid connection' => [
self::$db_config['valid'],
true,
'',
"-DB-info-> Connected to db 'corelibs_db_io_test' with "
. "schema 'public' as user 'corelibs_db_io_test' at host "
. "'localhost' on port '5432' with ssl mode 'allow' **** "
@@ -475,13 +478,21 @@ final class CoreLibsDBIOTest extends TestCase
* @dataProvider connectionProvider
* @testdox Connection will be $expected [$_dataName]
*
* @param array $connection
* @param bool $expected_status
* @param string $exception
* @param string $expected_string
* @return void
*/
public function testConnection(
array $connection,
bool $expected_status,
string $exception,
string $expected_string
): void {
if ($expected_status === false) {
$this->expectException($exception);
}
$db = new \CoreLibs\DB\IO(
$connection,
self::$log
@@ -722,6 +733,10 @@ final class CoreLibsDBIOTest extends TestCase
*/
public function testGetSetting(string $connection, array $settings): void
{
// if settings are all empty -> assume exception
if (empty($settings['db_name']) && empty($settings['db_user'])) {
$this->expectException('RuntimeException');
}
$db = new \CoreLibs\DB\IO(
self::$db_config[$connection],
self::$log

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
{

265
composer.lock generated
View File

@@ -481,25 +481,29 @@
},
{
"name": "doctrine/deprecations",
"version": "v1.0.0",
"version": "v1.1.1",
"source": {
"type": "git",
"url": "https://github.com/doctrine/deprecations.git",
"reference": "0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de"
"reference": "612a3ee5ab0d5dd97b7cf3874a6efe24325efac3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/doctrine/deprecations/zipball/0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de",
"reference": "0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de",
"url": "https://api.github.com/repos/doctrine/deprecations/zipball/612a3ee5ab0d5dd97b7cf3874a6efe24325efac3",
"reference": "612a3ee5ab0d5dd97b7cf3874a6efe24325efac3",
"shasum": ""
},
"require": {
"php": "^7.1|^8.0"
"php": "^7.1 || ^8.0"
},
"require-dev": {
"doctrine/coding-standard": "^9",
"phpunit/phpunit": "^7.5|^8.5|^9.5",
"psr/log": "^1|^2|^3"
"phpstan/phpstan": "1.4.10 || 1.10.15",
"phpstan/phpstan-phpunit": "^1.0",
"phpunit/phpunit": "^7.5 || ^8.5 || ^9.5",
"psalm/plugin-phpunit": "0.18.4",
"psr/log": "^1 || ^2 || ^3",
"vimeo/psalm": "4.30.0 || 5.12.0"
},
"suggest": {
"psr/log": "Allows logging deprecations via PSR-3 logger implementation"
@@ -518,9 +522,9 @@
"homepage": "https://www.doctrine-project.org/",
"support": {
"issues": "https://github.com/doctrine/deprecations/issues",
"source": "https://github.com/doctrine/deprecations/tree/v1.0.0"
"source": "https://github.com/doctrine/deprecations/tree/v1.1.1"
},
"time": "2022-05-02T15:47:09+00:00"
"time": "2023-06-03T09:27:29+00:00"
},
{
"name": "felixfbecker/advanced-json-rpc",
@@ -782,16 +786,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": {
@@ -832,9 +836,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": "phan/phan",
@@ -1027,16 +1031,16 @@
},
{
"name": "phpdocumentor/type-resolver",
"version": "1.7.1",
"version": "1.7.3",
"source": {
"type": "git",
"url": "https://github.com/phpDocumentor/TypeResolver.git",
"reference": "dfc078e8af9c99210337325ff5aa152872c98714"
"reference": "3219c6ee25c9ea71e3d9bbaf39c67c9ebd499419"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/dfc078e8af9c99210337325ff5aa152872c98714",
"reference": "dfc078e8af9c99210337325ff5aa152872c98714",
"url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/3219c6ee25c9ea71e3d9bbaf39c67c9ebd499419",
"reference": "3219c6ee25c9ea71e3d9bbaf39c67c9ebd499419",
"shasum": ""
},
"require": {
@@ -1079,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.1"
"source": "https://github.com/phpDocumentor/TypeResolver/tree/1.7.3"
},
"time": "2023-03-27T19:02:04+00:00"
"time": "2023-08-12T11:01:26+00:00"
},
{
"name": "phpstan/extension-installer",
@@ -1129,22 +1133,23 @@
},
{
"name": "phpstan/phpdoc-parser",
"version": "1.21.0",
"version": "1.23.1",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpdoc-parser.git",
"reference": "6df62b08faef4f899772bc7c3bbabb93d2b7a21c"
"reference": "846ae76eef31c6d7790fac9bc399ecee45160b26"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/6df62b08faef4f899772bc7c3bbabb93d2b7a21c",
"reference": "6df62b08faef4f899772bc7c3bbabb93d2b7a21c",
"url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/846ae76eef31c6d7790fac9bc399ecee45160b26",
"reference": "846ae76eef31c6d7790fac9bc399ecee45160b26",
"shasum": ""
},
"require": {
"php": "^7.2 || ^8.0"
},
"require-dev": {
"doctrine/annotations": "^2.0",
"nikic/php-parser": "^4.15",
"php-parallel-lint/php-parallel-lint": "^1.2",
"phpstan/extension-installer": "^1.0",
@@ -1169,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.21.0"
"source": "https://github.com/phpstan/phpdoc-parser/tree/1.23.1"
},
"time": "2023-05-17T13:13:44+00:00"
"time": "2023-08-03T16:32:59+00:00"
},
{
"name": "phpstan/phpstan",
"version": "1.10.15",
"version": "1.10.32",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpstan.git",
"reference": "762c4dac4da6f8756eebb80e528c3a47855da9bd"
"reference": "c47e47d3ab03137c0e121e77c4d2cb58672f6d44"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/762c4dac4da6f8756eebb80e528c3a47855da9bd",
"reference": "762c4dac4da6f8756eebb80e528c3a47855da9bd",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/c47e47d3ab03137c0e121e77c4d2cb58672f6d44",
"reference": "c47e47d3ab03137c0e121e77c4d2cb58672f6d44",
"shasum": ""
},
"require": {
@@ -1233,25 +1238,25 @@
"type": "tidelift"
}
],
"time": "2023-05-09T15:28:01+00:00"
"time": "2023-08-24T21:54:50+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",
@@ -1279,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",
@@ -1471,16 +1476,16 @@
},
{
"name": "spatie/array-to-xml",
"version": "3.1.6",
"version": "3.2.0",
"source": {
"type": "git",
"url": "https://github.com/spatie/array-to-xml.git",
"reference": "e210b98957987c755372465be105d32113f339a4"
"reference": "f9ab39c808500c347d5a8b6b13310bd5221e39e7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/spatie/array-to-xml/zipball/e210b98957987c755372465be105d32113f339a4",
"reference": "e210b98957987c755372465be105d32113f339a4",
"url": "https://api.github.com/repos/spatie/array-to-xml/zipball/f9ab39c808500c347d5a8b6b13310bd5221e39e7",
"reference": "f9ab39c808500c347d5a8b6b13310bd5221e39e7",
"shasum": ""
},
"require": {
@@ -1518,7 +1523,7 @@
"xml"
],
"support": {
"source": "https://github.com/spatie/array-to-xml/tree/3.1.6"
"source": "https://github.com/spatie/array-to-xml/tree/3.2.0"
},
"funding": [
{
@@ -1530,27 +1535,27 @@
"type": "github"
}
],
"time": "2023-05-11T14:04:07+00:00"
"time": "2023-07-19T18:30:26+00:00"
},
{
"name": "symfony/console",
"version": "v6.2.11",
"version": "v6.3.4",
"source": {
"type": "git",
"url": "https://github.com/symfony/console.git",
"reference": "5aa03db8ef0a5457c316ec580e69562d97734c77"
"reference": "eca495f2ee845130855ddf1cf18460c38966c8b6"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/console/zipball/5aa03db8ef0a5457c316ec580e69562d97734c77",
"reference": "5aa03db8ef0a5457c316ec580e69562d97734c77",
"url": "https://api.github.com/repos/symfony/console/zipball/eca495f2ee845130855ddf1cf18460c38966c8b6",
"reference": "eca495f2ee845130855ddf1cf18460c38966c8b6",
"shasum": ""
},
"require": {
"php": ">=8.1",
"symfony/deprecation-contracts": "^2.1|^3",
"symfony/deprecation-contracts": "^2.5|^3",
"symfony/polyfill-mbstring": "~1.0",
"symfony/service-contracts": "^1.1|^2|^3",
"symfony/service-contracts": "^2.5|^3",
"symfony/string": "^5.4|^6.0"
},
"conflict": {
@@ -1572,12 +1577,6 @@
"symfony/process": "^5.4|^6.0",
"symfony/var-dumper": "^5.4|^6.0"
},
"suggest": {
"psr/log": "For using the console logger",
"symfony/event-dispatcher": "",
"symfony/lock": "",
"symfony/process": ""
},
"type": "library",
"autoload": {
"psr-4": {
@@ -1610,7 +1609,7 @@
"terminal"
],
"support": {
"source": "https://github.com/symfony/console/tree/v6.2.11"
"source": "https://github.com/symfony/console/tree/v6.3.4"
},
"funding": [
{
@@ -1626,20 +1625,20 @@
"type": "tidelift"
}
],
"time": "2023-05-26T08:16:21+00:00"
"time": "2023-08-16T10:10:12+00:00"
},
{
"name": "symfony/deprecation-contracts",
"version": "v3.2.1",
"version": "v3.3.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/deprecation-contracts.git",
"reference": "e2d1534420bd723d0ef5aec58a22c5fe60ce6f5e"
"reference": "7c3aff79d10325257a001fcf92d991f24fc967cf"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/e2d1534420bd723d0ef5aec58a22c5fe60ce6f5e",
"reference": "e2d1534420bd723d0ef5aec58a22c5fe60ce6f5e",
"url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/7c3aff79d10325257a001fcf92d991f24fc967cf",
"reference": "7c3aff79d10325257a001fcf92d991f24fc967cf",
"shasum": ""
},
"require": {
@@ -1648,7 +1647,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "3.3-dev"
"dev-main": "3.4-dev"
},
"thanks": {
"name": "symfony/contracts",
@@ -1677,7 +1676,7 @@
"description": "A generic function and convention to trigger deprecation notices",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/deprecation-contracts/tree/v3.2.1"
"source": "https://github.com/symfony/deprecation-contracts/tree/v3.3.0"
},
"funding": [
{
@@ -1693,20 +1692,20 @@
"type": "tidelift"
}
],
"time": "2023-03-01T10:25:55+00:00"
"time": "2023-05-23T14:45:45+00:00"
},
{
"name": "symfony/filesystem",
"version": "v6.2.10",
"version": "v6.3.1",
"source": {
"type": "git",
"url": "https://github.com/symfony/filesystem.git",
"reference": "fd588debf7d1bc16a2c84b4b3b71145d9946b894"
"reference": "edd36776956f2a6fcf577edb5b05eb0e3bdc52ae"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/filesystem/zipball/fd588debf7d1bc16a2c84b4b3b71145d9946b894",
"reference": "fd588debf7d1bc16a2c84b4b3b71145d9946b894",
"url": "https://api.github.com/repos/symfony/filesystem/zipball/edd36776956f2a6fcf577edb5b05eb0e3bdc52ae",
"reference": "edd36776956f2a6fcf577edb5b05eb0e3bdc52ae",
"shasum": ""
},
"require": {
@@ -1740,7 +1739,7 @@
"description": "Provides basic utilities for the filesystem",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/filesystem/tree/v6.2.10"
"source": "https://github.com/symfony/filesystem/tree/v6.3.1"
},
"funding": [
{
@@ -1756,20 +1755,20 @@
"type": "tidelift"
}
],
"time": "2023-04-18T13:46:08+00:00"
"time": "2023-06-01T08:30:39+00:00"
},
{
"name": "symfony/polyfill-ctype",
"version": "v1.27.0",
"version": "v1.28.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-ctype.git",
"reference": "5bbc823adecdae860bb64756d639ecfec17b050a"
"reference": "ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/5bbc823adecdae860bb64756d639ecfec17b050a",
"reference": "5bbc823adecdae860bb64756d639ecfec17b050a",
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb",
"reference": "ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb",
"shasum": ""
},
"require": {
@@ -1784,7 +1783,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "1.27-dev"
"dev-main": "1.28-dev"
},
"thanks": {
"name": "symfony/polyfill",
@@ -1822,7 +1821,7 @@
"portable"
],
"support": {
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.27.0"
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.28.0"
},
"funding": [
{
@@ -1838,20 +1837,20 @@
"type": "tidelift"
}
],
"time": "2022-11-03T14:55:06+00:00"
"time": "2023-01-26T09:26:14+00:00"
},
{
"name": "symfony/polyfill-intl-grapheme",
"version": "v1.27.0",
"version": "v1.28.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-intl-grapheme.git",
"reference": "511a08c03c1960e08a883f4cffcacd219b758354"
"reference": "875e90aeea2777b6f135677f618529449334a612"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/511a08c03c1960e08a883f4cffcacd219b758354",
"reference": "511a08c03c1960e08a883f4cffcacd219b758354",
"url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/875e90aeea2777b6f135677f618529449334a612",
"reference": "875e90aeea2777b6f135677f618529449334a612",
"shasum": ""
},
"require": {
@@ -1863,7 +1862,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "1.27-dev"
"dev-main": "1.28-dev"
},
"thanks": {
"name": "symfony/polyfill",
@@ -1903,7 +1902,7 @@
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.27.0"
"source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.28.0"
},
"funding": [
{
@@ -1919,20 +1918,20 @@
"type": "tidelift"
}
],
"time": "2022-11-03T14:55:06+00:00"
"time": "2023-01-26T09:26:14+00:00"
},
{
"name": "symfony/polyfill-intl-normalizer",
"version": "v1.27.0",
"version": "v1.28.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-intl-normalizer.git",
"reference": "19bd1e4fcd5b91116f14d8533c57831ed00571b6"
"reference": "8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/19bd1e4fcd5b91116f14d8533c57831ed00571b6",
"reference": "19bd1e4fcd5b91116f14d8533c57831ed00571b6",
"url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92",
"reference": "8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92",
"shasum": ""
},
"require": {
@@ -1944,7 +1943,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "1.27-dev"
"dev-main": "1.28-dev"
},
"thanks": {
"name": "symfony/polyfill",
@@ -1987,7 +1986,7 @@
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.27.0"
"source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.28.0"
},
"funding": [
{
@@ -2003,20 +2002,20 @@
"type": "tidelift"
}
],
"time": "2022-11-03T14:55:06+00:00"
"time": "2023-01-26T09:26:14+00:00"
},
{
"name": "symfony/polyfill-mbstring",
"version": "v1.27.0",
"version": "v1.28.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-mbstring.git",
"reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534"
"reference": "42292d99c55abe617799667f454222c54c60e229"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/8ad114f6b39e2c98a8b0e3bd907732c207c2b534",
"reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534",
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/42292d99c55abe617799667f454222c54c60e229",
"reference": "42292d99c55abe617799667f454222c54c60e229",
"shasum": ""
},
"require": {
@@ -2031,7 +2030,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "1.27-dev"
"dev-main": "1.28-dev"
},
"thanks": {
"name": "symfony/polyfill",
@@ -2070,7 +2069,7 @@
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.27.0"
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.28.0"
},
"funding": [
{
@@ -2086,20 +2085,20 @@
"type": "tidelift"
}
],
"time": "2022-11-03T14:55:06+00:00"
"time": "2023-07-28T09:04:16+00:00"
},
{
"name": "symfony/polyfill-php80",
"version": "v1.27.0",
"version": "v1.28.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php80.git",
"reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936"
"reference": "6caa57379c4aec19c0a12a38b59b26487dcfe4b5"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936",
"reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936",
"url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/6caa57379c4aec19c0a12a38b59b26487dcfe4b5",
"reference": "6caa57379c4aec19c0a12a38b59b26487dcfe4b5",
"shasum": ""
},
"require": {
@@ -2108,7 +2107,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "1.27-dev"
"dev-main": "1.28-dev"
},
"thanks": {
"name": "symfony/polyfill",
@@ -2153,7 +2152,7 @@
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-php80/tree/v1.27.0"
"source": "https://github.com/symfony/polyfill-php80/tree/v1.28.0"
},
"funding": [
{
@@ -2169,20 +2168,20 @@
"type": "tidelift"
}
],
"time": "2022-11-03T14:55:06+00:00"
"time": "2023-01-26T09:26:14+00:00"
},
{
"name": "symfony/service-contracts",
"version": "v3.2.1",
"version": "v3.3.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/service-contracts.git",
"reference": "a8c9cedf55f314f3a186041d19537303766df09a"
"reference": "40da9cc13ec349d9e4966ce18b5fbcd724ab10a4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/service-contracts/zipball/a8c9cedf55f314f3a186041d19537303766df09a",
"reference": "a8c9cedf55f314f3a186041d19537303766df09a",
"url": "https://api.github.com/repos/symfony/service-contracts/zipball/40da9cc13ec349d9e4966ce18b5fbcd724ab10a4",
"reference": "40da9cc13ec349d9e4966ce18b5fbcd724ab10a4",
"shasum": ""
},
"require": {
@@ -2192,13 +2191,10 @@
"conflict": {
"ext-psr": "<1.1|>=2"
},
"suggest": {
"symfony/service-implementation": ""
},
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "3.3-dev"
"dev-main": "3.4-dev"
},
"thanks": {
"name": "symfony/contracts",
@@ -2238,7 +2234,7 @@
"standards"
],
"support": {
"source": "https://github.com/symfony/service-contracts/tree/v3.2.1"
"source": "https://github.com/symfony/service-contracts/tree/v3.3.0"
},
"funding": [
{
@@ -2254,20 +2250,20 @@
"type": "tidelift"
}
],
"time": "2023-03-01T10:32:47+00:00"
"time": "2023-05-23T14:45:45+00:00"
},
{
"name": "symfony/string",
"version": "v6.2.8",
"version": "v6.3.2",
"source": {
"type": "git",
"url": "https://github.com/symfony/string.git",
"reference": "193e83bbd6617d6b2151c37fff10fa7168ebddef"
"reference": "53d1a83225002635bca3482fcbf963001313fb68"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/string/zipball/193e83bbd6617d6b2151c37fff10fa7168ebddef",
"reference": "193e83bbd6617d6b2151c37fff10fa7168ebddef",
"url": "https://api.github.com/repos/symfony/string/zipball/53d1a83225002635bca3482fcbf963001313fb68",
"reference": "53d1a83225002635bca3482fcbf963001313fb68",
"shasum": ""
},
"require": {
@@ -2278,13 +2274,13 @@
"symfony/polyfill-mbstring": "~1.0"
},
"conflict": {
"symfony/translation-contracts": "<2.0"
"symfony/translation-contracts": "<2.5"
},
"require-dev": {
"symfony/error-handler": "^5.4|^6.0",
"symfony/http-client": "^5.4|^6.0",
"symfony/intl": "^6.2",
"symfony/translation-contracts": "^2.0|^3.0",
"symfony/translation-contracts": "^2.5|^3.0",
"symfony/var-exporter": "^5.4|^6.0"
},
"type": "library",
@@ -2324,7 +2320,7 @@
"utf8"
],
"support": {
"source": "https://github.com/symfony/string/tree/v6.2.8"
"source": "https://github.com/symfony/string/tree/v6.3.2"
},
"funding": [
{
@@ -2340,7 +2336,7 @@
"type": "tidelift"
}
],
"time": "2023-03-20T16:06:02+00:00"
"time": "2023-07-05T08:41:27+00:00"
},
{
"name": "tysonandre/var_representation_polyfill",
@@ -2406,16 +2402,16 @@
},
{
"name": "vimeo/psalm",
"version": "5.12.0",
"version": "5.15.0",
"source": {
"type": "git",
"url": "https://github.com/vimeo/psalm.git",
"reference": "f90118cdeacd0088e7215e64c0c99ceca819e176"
"reference": "5c774aca4746caf3d239d9c8cadb9f882ca29352"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/vimeo/psalm/zipball/f90118cdeacd0088e7215e64c0c99ceca819e176",
"reference": "f90118cdeacd0088e7215e64c0c99ceca819e176",
"url": "https://api.github.com/repos/vimeo/psalm/zipball/5c774aca4746caf3d239d9c8cadb9f882ca29352",
"reference": "5c774aca4746caf3d239d9c8cadb9f882ca29352",
"shasum": ""
},
"require": {
@@ -2436,13 +2432,16 @@
"felixfbecker/language-server-protocol": "^1.5.2",
"fidry/cpu-core-counter": "^0.4.1 || ^0.5.1",
"netresearch/jsonmapper": "^1.0 || ^2.0 || ^3.0 || ^4.0",
"nikic/php-parser": "^4.14",
"php": "^7.4 || ~8.0.0 || ~8.1.0 || ~8.2.0",
"nikic/php-parser": "^4.16",
"php": "^7.4 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0",
"sebastian/diff": "^4.0 || ^5.0",
"spatie/array-to-xml": "^2.17.0 || ^3.0",
"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"
},
@@ -2506,9 +2505,9 @@
],
"support": {
"issues": "https://github.com/vimeo/psalm/issues",
"source": "https://github.com/vimeo/psalm/tree/5.12.0"
"source": "https://github.com/vimeo/psalm/tree/5.15.0"
},
"time": "2023-05-22T21:19:03+00:00"
"time": "2023-08-20T23:07:30+00:00"
},
{
"name": "webmozart/assert",

View File

@@ -1,47 +0,0 @@
parameters:
ignoreErrors:
-
message: "#^Parameter \\#1 \\$connection of function pg_escape_bytea expects PgSql\\\\Connection\\|string, object\\|resource given\\.$#"
count: 1
path: www/lib/CoreLibs/DB/SQL/PgSQL.php
-
message: "#^Parameter \\#1 \\$connection of function pg_escape_identifier expects PgSql\\\\Connection\\|string, object\\|resource given\\.$#"
count: 1
path: www/lib/CoreLibs/DB/SQL/PgSQL.php
-
message: "#^Parameter \\#1 \\$connection of function pg_escape_literal expects PgSql\\\\Connection\\|string, object\\|resource given\\.$#"
count: 1
path: www/lib/CoreLibs/DB/SQL/PgSQL.php
-
message: "#^Parameter \\#1 \\$connection of function pg_escape_string expects PgSql\\\\Connection\\|string, object\\|resource given\\.$#"
count: 1
path: www/lib/CoreLibs/DB/SQL/PgSQL.php
-
message: "#^Parameter \\#1 \\$connection of function pg_execute expects PgSql\\\\Connection\\|string, object\\|resource given\\.$#"
count: 1
path: www/lib/CoreLibs/DB/SQL/PgSQL.php
-
message: "#^Parameter \\#1 \\$connection of function pg_parameter_status expects PgSql\\\\Connection\\|string, object\\|resource given\\.$#"
count: 1
path: www/lib/CoreLibs/DB/SQL/PgSQL.php
-
message: "#^Parameter \\#1 \\$connection of function pg_prepare expects PgSql\\\\Connection\\|string, object\\|resource given\\.$#"
count: 1
path: www/lib/CoreLibs/DB/SQL/PgSQL.php
-
message: "#^Parameter \\#1 \\$connection of function pg_query expects PgSql\\\\Connection\\|string, object\\|resource given\\.$#"
count: 1
path: www/lib/CoreLibs/DB/SQL/PgSQL.php
-
message: "#^Parameter \\#1 \\$connection of function pg_query_params expects PgSql\\\\Connection\\|string, object\\|resource given\\.$#"
count: 1
path: www/lib/CoreLibs/DB/SQL/PgSQL.php

View File

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

View File

@@ -441,31 +441,35 @@
},
{
"name": "doctrine/deprecations",
"version": "v1.0.0",
"version_normalized": "1.0.0.0",
"version": "v1.1.1",
"version_normalized": "1.1.1.0",
"source": {
"type": "git",
"url": "https://github.com/doctrine/deprecations.git",
"reference": "0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de"
"reference": "612a3ee5ab0d5dd97b7cf3874a6efe24325efac3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/doctrine/deprecations/zipball/0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de",
"reference": "0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de",
"url": "https://api.github.com/repos/doctrine/deprecations/zipball/612a3ee5ab0d5dd97b7cf3874a6efe24325efac3",
"reference": "612a3ee5ab0d5dd97b7cf3874a6efe24325efac3",
"shasum": ""
},
"require": {
"php": "^7.1|^8.0"
"php": "^7.1 || ^8.0"
},
"require-dev": {
"doctrine/coding-standard": "^9",
"phpunit/phpunit": "^7.5|^8.5|^9.5",
"psr/log": "^1|^2|^3"
"phpstan/phpstan": "1.4.10 || 1.10.15",
"phpstan/phpstan-phpunit": "^1.0",
"phpunit/phpunit": "^7.5 || ^8.5 || ^9.5",
"psalm/plugin-phpunit": "0.18.4",
"psr/log": "^1 || ^2 || ^3",
"vimeo/psalm": "4.30.0 || 5.12.0"
},
"suggest": {
"psr/log": "Allows logging deprecations via PSR-3 logger implementation"
},
"time": "2022-05-02T15:47:09+00:00",
"time": "2023-06-03T09:27:29+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
@@ -481,7 +485,7 @@
"homepage": "https://www.doctrine-project.org/",
"support": {
"issues": "https://github.com/doctrine/deprecations/issues",
"source": "https://github.com/doctrine/deprecations/tree/v1.0.0"
"source": "https://github.com/doctrine/deprecations/tree/v1.1.1"
},
"install-path": "../doctrine/deprecations"
},
@@ -760,17 +764,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": {
@@ -781,7 +785,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"
],
@@ -813,7 +817,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"
},
@@ -1017,17 +1021,17 @@
},
{
"name": "phpdocumentor/type-resolver",
"version": "1.7.1",
"version_normalized": "1.7.1.0",
"version": "1.7.3",
"version_normalized": "1.7.3.0",
"source": {
"type": "git",
"url": "https://github.com/phpDocumentor/TypeResolver.git",
"reference": "dfc078e8af9c99210337325ff5aa152872c98714"
"reference": "3219c6ee25c9ea71e3d9bbaf39c67c9ebd499419"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/dfc078e8af9c99210337325ff5aa152872c98714",
"reference": "dfc078e8af9c99210337325ff5aa152872c98714",
"url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/3219c6ee25c9ea71e3d9bbaf39c67c9ebd499419",
"reference": "3219c6ee25c9ea71e3d9bbaf39c67c9ebd499419",
"shasum": ""
},
"require": {
@@ -1046,7 +1050,7 @@
"rector/rector": "^0.13.9",
"vimeo/psalm": "^4.25"
},
"time": "2023-03-27T19:02:04+00:00",
"time": "2023-08-12T11:01:26+00:00",
"type": "library",
"extra": {
"branch-alias": {
@@ -1072,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.1"
"source": "https://github.com/phpDocumentor/TypeResolver/tree/1.7.3"
},
"install-path": "../phpdocumentor/type-resolver"
},
@@ -1125,23 +1129,24 @@
},
{
"name": "phpstan/phpdoc-parser",
"version": "1.21.0",
"version_normalized": "1.21.0.0",
"version": "1.23.1",
"version_normalized": "1.23.1.0",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpdoc-parser.git",
"reference": "6df62b08faef4f899772bc7c3bbabb93d2b7a21c"
"reference": "846ae76eef31c6d7790fac9bc399ecee45160b26"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/6df62b08faef4f899772bc7c3bbabb93d2b7a21c",
"reference": "6df62b08faef4f899772bc7c3bbabb93d2b7a21c",
"url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/846ae76eef31c6d7790fac9bc399ecee45160b26",
"reference": "846ae76eef31c6d7790fac9bc399ecee45160b26",
"shasum": ""
},
"require": {
"php": "^7.2 || ^8.0"
},
"require-dev": {
"doctrine/annotations": "^2.0",
"nikic/php-parser": "^4.15",
"php-parallel-lint/php-parallel-lint": "^1.2",
"phpstan/extension-installer": "^1.0",
@@ -1151,7 +1156,7 @@
"phpunit/phpunit": "^9.5",
"symfony/process": "^5.2"
},
"time": "2023-05-17T13:13:44+00:00",
"time": "2023-08-03T16:32:59+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
@@ -1168,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.21.0"
"source": "https://github.com/phpstan/phpdoc-parser/tree/1.23.1"
},
"install-path": "../phpstan/phpdoc-parser"
},
{
"name": "phpstan/phpstan",
"version": "1.10.15",
"version_normalized": "1.10.15.0",
"version": "1.10.32",
"version_normalized": "1.10.32.0",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpstan.git",
"reference": "762c4dac4da6f8756eebb80e528c3a47855da9bd"
"reference": "c47e47d3ab03137c0e121e77c4d2cb58672f6d44"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/762c4dac4da6f8756eebb80e528c3a47855da9bd",
"reference": "762c4dac4da6f8756eebb80e528c3a47855da9bd",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/c47e47d3ab03137c0e121e77c4d2cb58672f6d44",
"reference": "c47e47d3ab03137c0e121e77c4d2cb58672f6d44",
"shasum": ""
},
"require": {
@@ -1193,7 +1198,7 @@
"conflict": {
"phpstan/phpstan-shim": "*"
},
"time": "2023-05-09T15:28:01+00:00",
"time": "2023-08-24T21:54:50+00:00",
"bin": [
"phpstan",
"phpstan.phar"
@@ -1239,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",
@@ -1262,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": {
@@ -1284,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"
},
@@ -1538,17 +1543,17 @@
},
{
"name": "spatie/array-to-xml",
"version": "3.1.6",
"version_normalized": "3.1.6.0",
"version": "3.2.0",
"version_normalized": "3.2.0.0",
"source": {
"type": "git",
"url": "https://github.com/spatie/array-to-xml.git",
"reference": "e210b98957987c755372465be105d32113f339a4"
"reference": "f9ab39c808500c347d5a8b6b13310bd5221e39e7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/spatie/array-to-xml/zipball/e210b98957987c755372465be105d32113f339a4",
"reference": "e210b98957987c755372465be105d32113f339a4",
"url": "https://api.github.com/repos/spatie/array-to-xml/zipball/f9ab39c808500c347d5a8b6b13310bd5221e39e7",
"reference": "f9ab39c808500c347d5a8b6b13310bd5221e39e7",
"shasum": ""
},
"require": {
@@ -1560,7 +1565,7 @@
"pestphp/pest": "^1.21",
"spatie/pest-plugin-snapshots": "^1.1"
},
"time": "2023-05-11T14:04:07+00:00",
"time": "2023-07-19T18:30:26+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
@@ -1588,7 +1593,7 @@
"xml"
],
"support": {
"source": "https://github.com/spatie/array-to-xml/tree/3.1.6"
"source": "https://github.com/spatie/array-to-xml/tree/3.2.0"
},
"funding": [
{
@@ -1604,24 +1609,24 @@
},
{
"name": "symfony/console",
"version": "v6.2.11",
"version_normalized": "6.2.11.0",
"version": "v6.3.4",
"version_normalized": "6.3.4.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/console.git",
"reference": "5aa03db8ef0a5457c316ec580e69562d97734c77"
"reference": "eca495f2ee845130855ddf1cf18460c38966c8b6"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/console/zipball/5aa03db8ef0a5457c316ec580e69562d97734c77",
"reference": "5aa03db8ef0a5457c316ec580e69562d97734c77",
"url": "https://api.github.com/repos/symfony/console/zipball/eca495f2ee845130855ddf1cf18460c38966c8b6",
"reference": "eca495f2ee845130855ddf1cf18460c38966c8b6",
"shasum": ""
},
"require": {
"php": ">=8.1",
"symfony/deprecation-contracts": "^2.1|^3",
"symfony/deprecation-contracts": "^2.5|^3",
"symfony/polyfill-mbstring": "~1.0",
"symfony/service-contracts": "^1.1|^2|^3",
"symfony/service-contracts": "^2.5|^3",
"symfony/string": "^5.4|^6.0"
},
"conflict": {
@@ -1643,13 +1648,7 @@
"symfony/process": "^5.4|^6.0",
"symfony/var-dumper": "^5.4|^6.0"
},
"suggest": {
"psr/log": "For using the console logger",
"symfony/event-dispatcher": "",
"symfony/lock": "",
"symfony/process": ""
},
"time": "2023-05-26T08:16:21+00:00",
"time": "2023-08-16T10:10:12+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
@@ -1683,7 +1682,7 @@
"terminal"
],
"support": {
"source": "https://github.com/symfony/console/tree/v6.2.11"
"source": "https://github.com/symfony/console/tree/v6.3.4"
},
"funding": [
{
@@ -1703,27 +1702,27 @@
},
{
"name": "symfony/deprecation-contracts",
"version": "v3.2.1",
"version_normalized": "3.2.1.0",
"version": "v3.3.0",
"version_normalized": "3.3.0.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/deprecation-contracts.git",
"reference": "e2d1534420bd723d0ef5aec58a22c5fe60ce6f5e"
"reference": "7c3aff79d10325257a001fcf92d991f24fc967cf"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/e2d1534420bd723d0ef5aec58a22c5fe60ce6f5e",
"reference": "e2d1534420bd723d0ef5aec58a22c5fe60ce6f5e",
"url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/7c3aff79d10325257a001fcf92d991f24fc967cf",
"reference": "7c3aff79d10325257a001fcf92d991f24fc967cf",
"shasum": ""
},
"require": {
"php": ">=8.1"
},
"time": "2023-03-01T10:25:55+00:00",
"time": "2023-05-23T14:45:45+00:00",
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "3.3-dev"
"dev-main": "3.4-dev"
},
"thanks": {
"name": "symfony/contracts",
@@ -1753,7 +1752,7 @@
"description": "A generic function and convention to trigger deprecation notices",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/deprecation-contracts/tree/v3.2.1"
"source": "https://github.com/symfony/deprecation-contracts/tree/v3.3.0"
},
"funding": [
{
@@ -1773,17 +1772,17 @@
},
{
"name": "symfony/filesystem",
"version": "v6.2.10",
"version_normalized": "6.2.10.0",
"version": "v6.3.1",
"version_normalized": "6.3.1.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/filesystem.git",
"reference": "fd588debf7d1bc16a2c84b4b3b71145d9946b894"
"reference": "edd36776956f2a6fcf577edb5b05eb0e3bdc52ae"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/filesystem/zipball/fd588debf7d1bc16a2c84b4b3b71145d9946b894",
"reference": "fd588debf7d1bc16a2c84b4b3b71145d9946b894",
"url": "https://api.github.com/repos/symfony/filesystem/zipball/edd36776956f2a6fcf577edb5b05eb0e3bdc52ae",
"reference": "edd36776956f2a6fcf577edb5b05eb0e3bdc52ae",
"shasum": ""
},
"require": {
@@ -1791,7 +1790,7 @@
"symfony/polyfill-ctype": "~1.8",
"symfony/polyfill-mbstring": "~1.8"
},
"time": "2023-04-18T13:46:08+00:00",
"time": "2023-06-01T08:30:39+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
@@ -1819,7 +1818,7 @@
"description": "Provides basic utilities for the filesystem",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/filesystem/tree/v6.2.10"
"source": "https://github.com/symfony/filesystem/tree/v6.3.1"
},
"funding": [
{
@@ -1839,17 +1838,17 @@
},
{
"name": "symfony/polyfill-ctype",
"version": "v1.27.0",
"version_normalized": "1.27.0.0",
"version": "v1.28.0",
"version_normalized": "1.28.0.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-ctype.git",
"reference": "5bbc823adecdae860bb64756d639ecfec17b050a"
"reference": "ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/5bbc823adecdae860bb64756d639ecfec17b050a",
"reference": "5bbc823adecdae860bb64756d639ecfec17b050a",
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb",
"reference": "ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb",
"shasum": ""
},
"require": {
@@ -1861,11 +1860,11 @@
"suggest": {
"ext-ctype": "For best performance"
},
"time": "2022-11-03T14:55:06+00:00",
"time": "2023-01-26T09:26:14+00:00",
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "1.27-dev"
"dev-main": "1.28-dev"
},
"thanks": {
"name": "symfony/polyfill",
@@ -1904,7 +1903,7 @@
"portable"
],
"support": {
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.27.0"
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.28.0"
},
"funding": [
{
@@ -1924,17 +1923,17 @@
},
{
"name": "symfony/polyfill-intl-grapheme",
"version": "v1.27.0",
"version_normalized": "1.27.0.0",
"version": "v1.28.0",
"version_normalized": "1.28.0.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-intl-grapheme.git",
"reference": "511a08c03c1960e08a883f4cffcacd219b758354"
"reference": "875e90aeea2777b6f135677f618529449334a612"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/511a08c03c1960e08a883f4cffcacd219b758354",
"reference": "511a08c03c1960e08a883f4cffcacd219b758354",
"url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/875e90aeea2777b6f135677f618529449334a612",
"reference": "875e90aeea2777b6f135677f618529449334a612",
"shasum": ""
},
"require": {
@@ -1943,11 +1942,11 @@
"suggest": {
"ext-intl": "For best performance"
},
"time": "2022-11-03T14:55:06+00:00",
"time": "2023-01-26T09:26:14+00:00",
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "1.27-dev"
"dev-main": "1.28-dev"
},
"thanks": {
"name": "symfony/polyfill",
@@ -1988,7 +1987,7 @@
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.27.0"
"source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.28.0"
},
"funding": [
{
@@ -2008,17 +2007,17 @@
},
{
"name": "symfony/polyfill-intl-normalizer",
"version": "v1.27.0",
"version_normalized": "1.27.0.0",
"version": "v1.28.0",
"version_normalized": "1.28.0.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-intl-normalizer.git",
"reference": "19bd1e4fcd5b91116f14d8533c57831ed00571b6"
"reference": "8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/19bd1e4fcd5b91116f14d8533c57831ed00571b6",
"reference": "19bd1e4fcd5b91116f14d8533c57831ed00571b6",
"url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92",
"reference": "8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92",
"shasum": ""
},
"require": {
@@ -2027,11 +2026,11 @@
"suggest": {
"ext-intl": "For best performance"
},
"time": "2022-11-03T14:55:06+00:00",
"time": "2023-01-26T09:26:14+00:00",
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "1.27-dev"
"dev-main": "1.28-dev"
},
"thanks": {
"name": "symfony/polyfill",
@@ -2075,7 +2074,7 @@
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.27.0"
"source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.28.0"
},
"funding": [
{
@@ -2095,17 +2094,17 @@
},
{
"name": "symfony/polyfill-mbstring",
"version": "v1.27.0",
"version_normalized": "1.27.0.0",
"version": "v1.28.0",
"version_normalized": "1.28.0.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-mbstring.git",
"reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534"
"reference": "42292d99c55abe617799667f454222c54c60e229"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/8ad114f6b39e2c98a8b0e3bd907732c207c2b534",
"reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534",
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/42292d99c55abe617799667f454222c54c60e229",
"reference": "42292d99c55abe617799667f454222c54c60e229",
"shasum": ""
},
"require": {
@@ -2117,11 +2116,11 @@
"suggest": {
"ext-mbstring": "For best performance"
},
"time": "2022-11-03T14:55:06+00:00",
"time": "2023-07-28T09:04:16+00:00",
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "1.27-dev"
"dev-main": "1.28-dev"
},
"thanks": {
"name": "symfony/polyfill",
@@ -2161,7 +2160,7 @@
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.27.0"
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.28.0"
},
"funding": [
{
@@ -2181,27 +2180,27 @@
},
{
"name": "symfony/polyfill-php80",
"version": "v1.27.0",
"version_normalized": "1.27.0.0",
"version": "v1.28.0",
"version_normalized": "1.28.0.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php80.git",
"reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936"
"reference": "6caa57379c4aec19c0a12a38b59b26487dcfe4b5"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936",
"reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936",
"url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/6caa57379c4aec19c0a12a38b59b26487dcfe4b5",
"reference": "6caa57379c4aec19c0a12a38b59b26487dcfe4b5",
"shasum": ""
},
"require": {
"php": ">=7.1"
},
"time": "2022-11-03T14:55:06+00:00",
"time": "2023-01-26T09:26:14+00:00",
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "1.27-dev"
"dev-main": "1.28-dev"
},
"thanks": {
"name": "symfony/polyfill",
@@ -2247,7 +2246,7 @@
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-php80/tree/v1.27.0"
"source": "https://github.com/symfony/polyfill-php80/tree/v1.28.0"
},
"funding": [
{
@@ -2267,17 +2266,17 @@
},
{
"name": "symfony/service-contracts",
"version": "v3.2.1",
"version_normalized": "3.2.1.0",
"version": "v3.3.0",
"version_normalized": "3.3.0.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/service-contracts.git",
"reference": "a8c9cedf55f314f3a186041d19537303766df09a"
"reference": "40da9cc13ec349d9e4966ce18b5fbcd724ab10a4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/service-contracts/zipball/a8c9cedf55f314f3a186041d19537303766df09a",
"reference": "a8c9cedf55f314f3a186041d19537303766df09a",
"url": "https://api.github.com/repos/symfony/service-contracts/zipball/40da9cc13ec349d9e4966ce18b5fbcd724ab10a4",
"reference": "40da9cc13ec349d9e4966ce18b5fbcd724ab10a4",
"shasum": ""
},
"require": {
@@ -2287,14 +2286,11 @@
"conflict": {
"ext-psr": "<1.1|>=2"
},
"suggest": {
"symfony/service-implementation": ""
},
"time": "2023-03-01T10:32:47+00:00",
"time": "2023-05-23T14:45:45+00:00",
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "3.3-dev"
"dev-main": "3.4-dev"
},
"thanks": {
"name": "symfony/contracts",
@@ -2335,7 +2331,7 @@
"standards"
],
"support": {
"source": "https://github.com/symfony/service-contracts/tree/v3.2.1"
"source": "https://github.com/symfony/service-contracts/tree/v3.3.0"
},
"funding": [
{
@@ -2355,17 +2351,17 @@
},
{
"name": "symfony/string",
"version": "v6.2.8",
"version_normalized": "6.2.8.0",
"version": "v6.3.2",
"version_normalized": "6.3.2.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/string.git",
"reference": "193e83bbd6617d6b2151c37fff10fa7168ebddef"
"reference": "53d1a83225002635bca3482fcbf963001313fb68"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/string/zipball/193e83bbd6617d6b2151c37fff10fa7168ebddef",
"reference": "193e83bbd6617d6b2151c37fff10fa7168ebddef",
"url": "https://api.github.com/repos/symfony/string/zipball/53d1a83225002635bca3482fcbf963001313fb68",
"reference": "53d1a83225002635bca3482fcbf963001313fb68",
"shasum": ""
},
"require": {
@@ -2376,16 +2372,16 @@
"symfony/polyfill-mbstring": "~1.0"
},
"conflict": {
"symfony/translation-contracts": "<2.0"
"symfony/translation-contracts": "<2.5"
},
"require-dev": {
"symfony/error-handler": "^5.4|^6.0",
"symfony/http-client": "^5.4|^6.0",
"symfony/intl": "^6.2",
"symfony/translation-contracts": "^2.0|^3.0",
"symfony/translation-contracts": "^2.5|^3.0",
"symfony/var-exporter": "^5.4|^6.0"
},
"time": "2023-03-20T16:06:02+00:00",
"time": "2023-07-05T08:41:27+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
@@ -2424,7 +2420,7 @@
"utf8"
],
"support": {
"source": "https://github.com/symfony/string/tree/v6.2.8"
"source": "https://github.com/symfony/string/tree/v6.3.2"
},
"funding": [
{
@@ -2509,17 +2505,17 @@
},
{
"name": "vimeo/psalm",
"version": "5.12.0",
"version_normalized": "5.12.0.0",
"version": "5.15.0",
"version_normalized": "5.15.0.0",
"source": {
"type": "git",
"url": "https://github.com/vimeo/psalm.git",
"reference": "f90118cdeacd0088e7215e64c0c99ceca819e176"
"reference": "5c774aca4746caf3d239d9c8cadb9f882ca29352"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/vimeo/psalm/zipball/f90118cdeacd0088e7215e64c0c99ceca819e176",
"reference": "f90118cdeacd0088e7215e64c0c99ceca819e176",
"url": "https://api.github.com/repos/vimeo/psalm/zipball/5c774aca4746caf3d239d9c8cadb9f882ca29352",
"reference": "5c774aca4746caf3d239d9c8cadb9f882ca29352",
"shasum": ""
},
"require": {
@@ -2540,13 +2536,16 @@
"felixfbecker/language-server-protocol": "^1.5.2",
"fidry/cpu-core-counter": "^0.4.1 || ^0.5.1",
"netresearch/jsonmapper": "^1.0 || ^2.0 || ^3.0 || ^4.0",
"nikic/php-parser": "^4.14",
"php": "^7.4 || ~8.0.0 || ~8.1.0 || ~8.2.0",
"nikic/php-parser": "^4.16",
"php": "^7.4 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0",
"sebastian/diff": "^4.0 || ^5.0",
"spatie/array-to-xml": "^2.17.0 || ^3.0",
"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"
},
@@ -2570,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-05-22T21:19:03+00:00",
"time": "2023-08-20T23:07:30+00:00",
"bin": [
"psalm",
"psalm-language-server",
@@ -2612,7 +2611,7 @@
],
"support": {
"issues": "https://github.com/vimeo/psalm/issues",
"source": "https://github.com/vimeo/psalm/tree/5.12.0"
"source": "https://github.com/vimeo/psalm/tree/5.15.0"
},
"install-path": "../vimeo/psalm"
},

View File

@@ -65,9 +65,9 @@
'dev_requirement' => true,
),
'doctrine/deprecations' => array(
'pretty_version' => 'v1.0.0',
'version' => '1.0.0.0',
'reference' => '0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de',
'pretty_version' => 'v1.1.1',
'version' => '1.1.1.0',
'reference' => '612a3ee5ab0d5dd97b7cf3874a6efe24325efac3',
'type' => 'library',
'install_path' => __DIR__ . '/../doctrine/deprecations',
'aliases' => array(),
@@ -128,9 +128,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(),
@@ -164,9 +164,9 @@
'dev_requirement' => true,
),
'phpdocumentor/type-resolver' => array(
'pretty_version' => '1.7.1',
'version' => '1.7.1.0',
'reference' => 'dfc078e8af9c99210337325ff5aa152872c98714',
'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.21.0',
'version' => '1.21.0.0',
'reference' => '6df62b08faef4f899772bc7c3bbabb93d2b7a21c',
'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.15',
'version' => '1.10.15.0',
'reference' => '762c4dac4da6f8756eebb80e528c3a47855da9bd',
'pretty_version' => '1.10.32',
'version' => '1.10.32.0',
'reference' => 'c47e47d3ab03137c0e121e77c4d2cb58672f6d44',
'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.12.0',
0 => '5.15.0',
),
),
'psr/container' => array(
@@ -257,99 +257,99 @@
'dev_requirement' => true,
),
'spatie/array-to-xml' => array(
'pretty_version' => '3.1.6',
'version' => '3.1.6.0',
'reference' => 'e210b98957987c755372465be105d32113f339a4',
'pretty_version' => '3.2.0',
'version' => '3.2.0.0',
'reference' => 'f9ab39c808500c347d5a8b6b13310bd5221e39e7',
'type' => 'library',
'install_path' => __DIR__ . '/../spatie/array-to-xml',
'aliases' => array(),
'dev_requirement' => true,
),
'symfony/console' => array(
'pretty_version' => 'v6.2.11',
'version' => '6.2.11.0',
'reference' => '5aa03db8ef0a5457c316ec580e69562d97734c77',
'pretty_version' => 'v6.3.4',
'version' => '6.3.4.0',
'reference' => 'eca495f2ee845130855ddf1cf18460c38966c8b6',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/console',
'aliases' => array(),
'dev_requirement' => true,
),
'symfony/deprecation-contracts' => array(
'pretty_version' => 'v3.2.1',
'version' => '3.2.1.0',
'reference' => 'e2d1534420bd723d0ef5aec58a22c5fe60ce6f5e',
'pretty_version' => 'v3.3.0',
'version' => '3.3.0.0',
'reference' => '7c3aff79d10325257a001fcf92d991f24fc967cf',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/deprecation-contracts',
'aliases' => array(),
'dev_requirement' => true,
),
'symfony/filesystem' => array(
'pretty_version' => 'v6.2.10',
'version' => '6.2.10.0',
'reference' => 'fd588debf7d1bc16a2c84b4b3b71145d9946b894',
'pretty_version' => 'v6.3.1',
'version' => '6.3.1.0',
'reference' => 'edd36776956f2a6fcf577edb5b05eb0e3bdc52ae',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/filesystem',
'aliases' => array(),
'dev_requirement' => true,
),
'symfony/polyfill-ctype' => array(
'pretty_version' => 'v1.27.0',
'version' => '1.27.0.0',
'reference' => '5bbc823adecdae860bb64756d639ecfec17b050a',
'pretty_version' => 'v1.28.0',
'version' => '1.28.0.0',
'reference' => 'ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/polyfill-ctype',
'aliases' => array(),
'dev_requirement' => true,
),
'symfony/polyfill-intl-grapheme' => array(
'pretty_version' => 'v1.27.0',
'version' => '1.27.0.0',
'reference' => '511a08c03c1960e08a883f4cffcacd219b758354',
'pretty_version' => 'v1.28.0',
'version' => '1.28.0.0',
'reference' => '875e90aeea2777b6f135677f618529449334a612',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/polyfill-intl-grapheme',
'aliases' => array(),
'dev_requirement' => true,
),
'symfony/polyfill-intl-normalizer' => array(
'pretty_version' => 'v1.27.0',
'version' => '1.27.0.0',
'reference' => '19bd1e4fcd5b91116f14d8533c57831ed00571b6',
'pretty_version' => 'v1.28.0',
'version' => '1.28.0.0',
'reference' => '8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/polyfill-intl-normalizer',
'aliases' => array(),
'dev_requirement' => true,
),
'symfony/polyfill-mbstring' => array(
'pretty_version' => 'v1.27.0',
'version' => '1.27.0.0',
'reference' => '8ad114f6b39e2c98a8b0e3bd907732c207c2b534',
'pretty_version' => 'v1.28.0',
'version' => '1.28.0.0',
'reference' => '42292d99c55abe617799667f454222c54c60e229',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/polyfill-mbstring',
'aliases' => array(),
'dev_requirement' => true,
),
'symfony/polyfill-php80' => array(
'pretty_version' => 'v1.27.0',
'version' => '1.27.0.0',
'reference' => '7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936',
'pretty_version' => 'v1.28.0',
'version' => '1.28.0.0',
'reference' => '6caa57379c4aec19c0a12a38b59b26487dcfe4b5',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/polyfill-php80',
'aliases' => array(),
'dev_requirement' => true,
),
'symfony/service-contracts' => array(
'pretty_version' => 'v3.2.1',
'version' => '3.2.1.0',
'reference' => 'a8c9cedf55f314f3a186041d19537303766df09a',
'pretty_version' => 'v3.3.0',
'version' => '3.3.0.0',
'reference' => '40da9cc13ec349d9e4966ce18b5fbcd724ab10a4',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/service-contracts',
'aliases' => array(),
'dev_requirement' => true,
),
'symfony/string' => array(
'pretty_version' => 'v6.2.8',
'version' => '6.2.8.0',
'reference' => '193e83bbd6617d6b2151c37fff10fa7168ebddef',
'pretty_version' => 'v6.3.2',
'version' => '6.3.2.0',
'reference' => '53d1a83225002635bca3482fcbf963001313fb68',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/string',
'aliases' => array(),
@@ -365,9 +365,9 @@
'dev_requirement' => true,
),
'vimeo/psalm' => array(
'pretty_version' => '5.12.0',
'version' => '5.12.0.0',
'reference' => 'f90118cdeacd0088e7215e64c0c99ceca819e176',
'pretty_version' => '5.15.0',
'version' => '5.15.0.0',
'reference' => '5c774aca4746caf3d239d9c8cadb9f882ca29352',
'type' => 'library',
'install_path' => __DIR__ . '/../vimeo/psalm',
'aliases' => array(),

View File

@@ -19,13 +19,16 @@ Enable Doctrine deprecations to be sent to a PSR3 logger:
```
Enable Doctrine deprecations to be sent as `@trigger_error($message, E_USER_DEPRECATED)`
messages.
messages by setting the `DOCTRINE_DEPRECATIONS` environment variable to `trigger`.
Alternatively, call:
```php
\Doctrine\Deprecations\Deprecation::enableWithTriggerError();
```
If you only want to enable deprecation tracking, without logging or calling `trigger_error` then call:
If you only want to enable deprecation tracking, without logging or calling `trigger_error`
then set the `DOCTRINE_DEPRECATIONS` environment variable to `track`.
Alternatively, call:
```php
\Doctrine\Deprecations\Deprecation::enableTrackingDeprecations();

View File

@@ -1,22 +1,28 @@
{
"name": "doctrine/deprecations",
"type": "library",
"description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.",
"homepage": "https://www.doctrine-project.org/",
"license": "MIT",
"type": "library",
"homepage": "https://www.doctrine-project.org/",
"require": {
"php": "^7.1|^8.0"
"php": "^7.1 || ^8.0"
},
"require-dev": {
"phpunit/phpunit": "^7.5|^8.5|^9.5",
"psr/log": "^1|^2|^3",
"doctrine/coding-standard": "^9"
"doctrine/coding-standard": "^9",
"phpstan/phpstan": "1.4.10 || 1.10.15",
"phpstan/phpstan-phpunit": "^1.0",
"phpunit/phpunit": "^7.5 || ^8.5 || ^9.5",
"psalm/plugin-phpunit": "0.18.4",
"psr/log": "^1 || ^2 || ^3",
"vimeo/psalm": "4.30.0 || 5.12.0"
},
"suggest": {
"psr/log": "Allows logging deprecations via PSR-3 logger implementation"
},
"autoload": {
"psr-4": {"Doctrine\\Deprecations\\": "lib/Doctrine/Deprecations"}
"psr-4": {
"Doctrine\\Deprecations\\": "lib/Doctrine/Deprecations"
}
},
"autoload-dev": {
"psr-4": {

View File

@@ -8,6 +8,7 @@ use Psr\Log\LoggerInterface;
use function array_key_exists;
use function array_reduce;
use function assert;
use function debug_backtrace;
use function sprintf;
use function strpos;
@@ -46,8 +47,8 @@ class Deprecation
private const TYPE_TRIGGER_ERROR = 2;
private const TYPE_PSR_LOGGER = 4;
/** @var int */
private static $type = self::TYPE_NONE;
/** @var int-mask-of<self::TYPE_*>|null */
private static $type;
/** @var LoggerInterface|null */
private static $logger;
@@ -56,6 +57,9 @@ class Deprecation
private static $ignoredPackages = [];
/** @var array<string,int> */
private static $triggeredDeprecations = [];
/** @var array<string,bool> */
private static $ignoredLinks = [];
/** @var bool */
@@ -68,21 +72,27 @@ class Deprecation
* deprecation. It is additionally used to de-duplicate the trigger of the
* same deprecation during a request.
*
* @param mixed $args
* @param float|int|string $args
*/
public static function trigger(string $package, string $link, string $message, ...$args): void
{
if (self::$type === self::TYPE_NONE) {
$type = self::$type ?? self::getTypeFromEnv();
if ($type === self::TYPE_NONE) {
return;
}
if (array_key_exists($link, self::$ignoredLinks)) {
self::$ignoredLinks[$link]++;
} else {
self::$ignoredLinks[$link] = 1;
if (isset(self::$ignoredLinks[$link])) {
return;
}
if (self::$deduplication === true && self::$ignoredLinks[$link] > 1) {
if (array_key_exists($link, self::$triggeredDeprecations)) {
self::$triggeredDeprecations[$link]++;
} else {
self::$triggeredDeprecations[$link] = 1;
}
if (self::$deduplication === true && self::$triggeredDeprecations[$link] > 1) {
return;
}
@@ -114,18 +124,20 @@ class Deprecation
* deprecation tracking is enabled even during deduplication, because it
* needs to call {@link debug_backtrace()}
*
* @param mixed $args
* @param float|int|string $args
*/
public static function triggerIfCalledFromOutside(string $package, string $link, string $message, ...$args): void
{
if (self::$type === self::TYPE_NONE) {
$type = self::$type ?? self::getTypeFromEnv();
if ($type === self::TYPE_NONE) {
return;
}
$backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2);
// first check that the caller is not from a tests folder, in which case we always let deprecations pass
if (strpos($backtrace[1]['file'], DIRECTORY_SEPARATOR . 'tests' . DIRECTORY_SEPARATOR) === false) {
if (isset($backtrace[1]['file'], $backtrace[0]['file']) && strpos($backtrace[1]['file'], DIRECTORY_SEPARATOR . 'tests' . DIRECTORY_SEPARATOR) === false) {
$path = DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR . $package . DIRECTORY_SEPARATOR;
if (strpos($backtrace[0]['file'], $path) === false) {
@@ -137,13 +149,17 @@ class Deprecation
}
}
if (array_key_exists($link, self::$ignoredLinks)) {
self::$ignoredLinks[$link]++;
} else {
self::$ignoredLinks[$link] = 1;
if (isset(self::$ignoredLinks[$link])) {
return;
}
if (self::$deduplication === true && self::$ignoredLinks[$link] > 1) {
if (array_key_exists($link, self::$triggeredDeprecations)) {
self::$triggeredDeprecations[$link]++;
} else {
self::$triggeredDeprecations[$link] = 1;
}
if (self::$deduplication === true && self::$triggeredDeprecations[$link] > 1) {
return;
}
@@ -157,31 +173,35 @@ class Deprecation
}
/**
* @param array<mixed> $backtrace
* @param list<array{function: string, line?: int, file?: string, class?: class-string, type?: string, args?: mixed[], object?: object}> $backtrace
*/
private static function delegateTriggerToBackend(string $message, array $backtrace, string $link, string $package): void
{
if ((self::$type & self::TYPE_PSR_LOGGER) > 0) {
$type = self::$type ?? self::getTypeFromEnv();
if (($type & self::TYPE_PSR_LOGGER) > 0) {
$context = [
'file' => $backtrace[0]['file'],
'line' => $backtrace[0]['line'],
'file' => $backtrace[0]['file'] ?? null,
'line' => $backtrace[0]['line'] ?? null,
'package' => $package,
'link' => $link,
];
assert(self::$logger !== null);
self::$logger->notice($message, $context);
}
if (! ((self::$type & self::TYPE_TRIGGER_ERROR) > 0)) {
if (! (($type & self::TYPE_TRIGGER_ERROR) > 0)) {
return;
}
$message .= sprintf(
' (%s:%d called by %s:%d, %s, package %s)',
self::basename($backtrace[0]['file']),
$backtrace[0]['line'],
self::basename($backtrace[1]['file']),
$backtrace[1]['line'],
self::basename($backtrace[0]['file'] ?? 'native code'),
$backtrace[0]['line'] ?? 0,
self::basename($backtrace[1]['file'] ?? 'native code'),
$backtrace[1]['line'] ?? 0,
$link,
$package
);
@@ -205,16 +225,19 @@ class Deprecation
public static function enableTrackingDeprecations(): void
{
self::$type = self::$type ?? 0;
self::$type |= self::TYPE_TRACK_DEPRECATIONS;
}
public static function enableWithTriggerError(): void
{
self::$type = self::$type ?? 0;
self::$type |= self::TYPE_TRIGGER_ERROR;
}
public static function enableWithPsrLogger(LoggerInterface $logger): void
{
self::$type = self::$type ?? 0;
self::$type |= self::TYPE_PSR_LOGGER;
self::$logger = $logger;
}
@@ -229,9 +252,10 @@ class Deprecation
self::$type = self::TYPE_NONE;
self::$logger = null;
self::$deduplication = true;
self::$ignoredLinks = [];
foreach (self::$ignoredLinks as $link => $count) {
self::$ignoredLinks[$link] = 0;
foreach (self::$triggeredDeprecations as $link => $count) {
self::$triggeredDeprecations[$link] = 0;
}
}
@@ -243,13 +267,13 @@ class Deprecation
public static function ignoreDeprecations(string ...$links): void
{
foreach ($links as $link) {
self::$ignoredLinks[$link] = 0;
self::$ignoredLinks[$link] = true;
}
}
public static function getUniqueTriggeredDeprecationsCount(): int
{
return array_reduce(self::$ignoredLinks, static function (int $carry, int $count) {
return array_reduce(self::$triggeredDeprecations, static function (int $carry, int $count) {
return $carry + $count;
}, 0);
}
@@ -261,6 +285,28 @@ class Deprecation
*/
public static function getTriggeredDeprecations(): array
{
return self::$ignoredLinks;
return self::$triggeredDeprecations;
}
/**
* @return int-mask-of<self::TYPE_*>
*/
private static function getTypeFromEnv(): int
{
switch ($_SERVER['DOCTRINE_DEPRECATIONS'] ?? $_ENV['DOCTRINE_DEPRECATIONS'] ?? null) {
case 'trigger':
self::$type = self::TYPE_TRIGGER_ERROR;
break;
case 'track':
self::$type = self::TYPE_TRACK_DEPRECATIONS;
break;
default:
self::$type = self::TYPE_NONE;
break;
}
return self::$type;
}
}

View File

@@ -0,0 +1,9 @@
parameters:
level: 6
paths:
- lib
- tests
includes:
- vendor/phpstan/phpstan-phpunit/extension.neon
- vendor/phpstan/phpstan-phpunit/rules.neon

30
vendor/doctrine/deprecations/psalm.xml vendored Normal file
View File

@@ -0,0 +1,30 @@
<?xml version="1.0"?>
<psalm
errorLevel="1"
resolveFromConfigFile="true"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="https://getpsalm.org/schema/config"
xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd"
findUnusedBaselineEntry="true"
findUnusedCode="false"
>
<projectFiles>
<directory name="lib/Doctrine/Deprecations" />
<directory name="tests/Doctrine/Deprecations" />
<ignoreFiles>
<directory name="vendor" />
</ignoreFiles>
</projectFiles>
<plugins>
<pluginClass class="Psalm\PhpUnitPlugin\Plugin"/>
</plugins>
<issueHandlers>
<DeprecatedMethod>
<errorLevel type="suppress">
<!-- Remove when dropping support for PHPUnit 9.6 -->
<referencedMethod name="PHPUnit\Framework\TestCase::expectDeprecation"/>
<referencedMethod name="PHPUnit\Framework\TestCase::expectDeprecationMessage"/>
</errorLevel>
</DeprecatedMethod>
</issueHandlers>
</psalm>

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'];
}
}

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

@@ -6,7 +6,10 @@ use PhpParser\NodeAbstract;
class Name extends NodeAbstract
{
/** @var string[] Parts of the name */
/**
* @var string[] Parts of the name
* @deprecated Use getParts() instead
*/
public $parts;
private static $specialClassNames = [
@@ -30,6 +33,15 @@ class Name extends NodeAbstract
return ['parts'];
}
/**
* Get parts of name (split by the namespace separator).
*
* @return string[] Parts of name
*/
public function getParts(): array {
return $this->parts;
}
/**
* Gets the first part of the name, i.e. everything before the first namespace separator.
*

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'];
}
/**

View File

@@ -2627,7 +2627,7 @@ class Php5 extends \PhpParser\ParserAbstract
$this->semValue = new Expr\ArrayItem($this->semStack[$stackPos-(2-2)], null, true, $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes);
},
552 => function ($stackPos) {
$this->semValue = new Expr\ArrayItem($this->semStack[$stackPos-(2-2)], null, false, $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes, true, $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes);
$this->semValue = new Expr\ArrayItem($this->semStack[$stackPos-(2-2)], null, false, $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes, true);
},
553 => function ($stackPos) {
$this->semStack[$stackPos-(2-1)][] = $this->semStack[$stackPos-(2-2)]; $this->semValue = $this->semStack[$stackPos-(2-1)];

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

@@ -15,7 +15,7 @@ namespace phpDocumentor\Reflection\PseudoTypes;
use phpDocumentor\Reflection\PseudoType;
use phpDocumentor\Reflection\Type;
use phpDocumentor\Reflection\Types\Float_;
use phpDocumentor\Reflection\Types\String_;
use function sprintf;
@@ -36,7 +36,7 @@ class StringValue implements PseudoType
public function underlyingType(): Type
{
return new Float_();
return new String_();
}
public function __toString(): string

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

@@ -15,6 +15,8 @@ For the complete list of supported PHPDoc features check out PHPStan documentati
* [PHPDoc Types](https://phpstan.org/writing-php-code/phpdoc-types) (list of PHPDoc types)
* [phpdoc-parser API Reference](https://phpstan.github.io/phpdoc-parser/namespace-PHPStan.PhpDocParser.html) with all the AST node types etc.
This parser also supports parsing [Doctrine Annotations](https://github.com/doctrine/annotations). The AST nodes live in the [PHPStan\PhpDocParser\Ast\PhpDoc\Doctrine namespace](https://phpstan.github.io/phpdoc-parser/namespace-PHPStan.PhpDocParser.Ast.PhpDoc.Doctrine.html). The support needs to be turned on by setting `bool $parseDoctrineAnnotations` to `true` in `Lexer` and `PhpDocParser` class constructors.
## Installation
```
@@ -91,12 +93,13 @@ $phpDocNode = $phpDocParser->parse($tokens); // PhpDocNode
$cloningTraverser = new NodeTraverser([new CloningVisitor()]);
/** @var PhpDocNode $newPhpDocNode */
$printer = new Printer();
[$newPhpDocNode] = $cloningTraverser->traverse([$phpDocNode]);
// change something in $newPhpDocNode
$newPhpDocNode->getParamTagValues()[0]->type = new IdentifierTypeNode('Ipsum');
// print changed PHPDoc
$printer = new Printer();
$newPhpDoc = $printer->printFormatPreserving($newPhpDocNode, $phpDocNode, $tokens);
echo $newPhpDoc; // '/** @param Ipsum $a */'
```

View File

@@ -6,6 +6,7 @@
"php": "^7.2 || ^8.0"
},
"require-dev": {
"doctrine/annotations": "^2.0",
"nikic/php-parser": "^4.15",
"php-parallel-lint/php-parallel-lint": "^1.2",
"phpstan/extension-installer": "^1.0",

View File

@@ -8,7 +8,7 @@ namespace PHPStan\PhpDocParser\Ast;
* Copyright (c) 2011, Nikita Popov
* All rights reserved.
*/
abstract class AbstractNodeVisitor implements NodeVisitor
abstract class AbstractNodeVisitor implements NodeVisitor // phpcs:ignore SlevomatCodingStandard.Classes.SuperfluousAbstractClassNaming.SuperfluousPrefix
{
public function beforeTraverse(array $nodes): ?array

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

@@ -0,0 +1,35 @@
<?php declare(strict_types = 1);
namespace PHPStan\PhpDocParser\Ast\PhpDoc\Doctrine;
use PHPStan\PhpDocParser\Ast\Node;
use PHPStan\PhpDocParser\Ast\NodeAttributes;
use function implode;
class DoctrineAnnotation implements Node
{
use NodeAttributes;
/** @var string */
public $name;
/** @var list<DoctrineArgument> */
public $arguments;
/**
* @param list<DoctrineArgument> $arguments
*/
public function __construct(string $name, array $arguments)
{
$this->name = $name;
$this->arguments = $arguments;
}
public function __toString(): string
{
$arguments = implode(', ', $this->arguments);
return $this->name . '(' . $arguments . ')';
}
}

View File

@@ -0,0 +1,43 @@
<?php declare(strict_types = 1);
namespace PHPStan\PhpDocParser\Ast\PhpDoc\Doctrine;
use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprNode;
use PHPStan\PhpDocParser\Ast\Node;
use PHPStan\PhpDocParser\Ast\NodeAttributes;
use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
/**
* @phpstan-type ValueType = DoctrineAnnotation|IdentifierTypeNode|DoctrineArray|ConstExprNode
*/
class DoctrineArgument implements Node
{
use NodeAttributes;
/** @var IdentifierTypeNode|null */
public $key;
/** @var ValueType */
public $value;
/**
* @param ValueType $value
*/
public function __construct(?IdentifierTypeNode $key, $value)
{
$this->key = $key;
$this->value = $value;
}
public function __toString(): string
{
if ($this->key === null) {
return (string) $this->value;
}
return $this->key . '=' . $this->value;
}
}

View File

@@ -0,0 +1,32 @@
<?php declare(strict_types = 1);
namespace PHPStan\PhpDocParser\Ast\PhpDoc\Doctrine;
use PHPStan\PhpDocParser\Ast\Node;
use PHPStan\PhpDocParser\Ast\NodeAttributes;
use function implode;
class DoctrineArray implements Node
{
use NodeAttributes;
/** @var list<DoctrineArrayItem> */
public $items;
/**
* @param list<DoctrineArrayItem> $items
*/
public function __construct(array $items)
{
$this->items = $items;
}
public function __toString(): string
{
$items = implode(', ', $this->items);
return '{' . $items . '}';
}
}

View File

@@ -0,0 +1,47 @@
<?php declare(strict_types = 1);
namespace PHPStan\PhpDocParser\Ast\PhpDoc\Doctrine;
use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprIntegerNode;
use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprStringNode;
use PHPStan\PhpDocParser\Ast\ConstExpr\ConstFetchNode;
use PHPStan\PhpDocParser\Ast\Node;
use PHPStan\PhpDocParser\Ast\NodeAttributes;
use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
/**
* @phpstan-import-type ValueType from DoctrineArgument
* @phpstan-type KeyType = ConstExprIntegerNode|ConstExprStringNode|IdentifierTypeNode|ConstFetchNode|null
*/
class DoctrineArrayItem implements Node
{
use NodeAttributes;
/** @var KeyType */
public $key;
/** @var ValueType */
public $value;
/**
* @param KeyType $key
* @param ValueType $value
*/
public function __construct($key, $value)
{
$this->key = $key;
$this->value = $value;
}
public function __toString(): string
{
if ($this->key === null) {
return (string) $this->value;
}
return $this->key . '=' . $this->value;
}
}

View File

@@ -0,0 +1,36 @@
<?php declare(strict_types = 1);
namespace PHPStan\PhpDocParser\Ast\PhpDoc\Doctrine;
use PHPStan\PhpDocParser\Ast\NodeAttributes;
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagValueNode;
use function trim;
class DoctrineTagValueNode implements PhpDocTagValueNode
{
use NodeAttributes;
/** @var DoctrineAnnotation */
public $annotation;
/** @var string (may be empty) */
public $description;
public function __construct(
DoctrineAnnotation $annotation,
string $description
)
{
$this->annotation = $annotation;
$this->description = $description;
}
public function __toString(): string
{
return trim("{$this->annotation} {$this->description}");
}
}

View File

@@ -3,6 +3,7 @@
namespace PHPStan\PhpDocParser\Ast\PhpDoc;
use PHPStan\PhpDocParser\Ast\NodeAttributes;
use PHPStan\PhpDocParser\Ast\PhpDoc\Doctrine\DoctrineTagValueNode;
use function trim;
class PhpDocTagNode implements PhpDocChildNode
@@ -25,6 +26,10 @@ class PhpDocTagNode implements PhpDocChildNode
public function __toString(): string
{
if ($this->value instanceof DoctrineTagValueNode) {
return (string) $this->value;
}
return trim("{$this->name} {$this->value}");
}

View File

@@ -30,23 +30,25 @@ class Lexer
public const TOKEN_OPEN_PHPDOC = 15;
public const TOKEN_CLOSE_PHPDOC = 16;
public const TOKEN_PHPDOC_TAG = 17;
public const TOKEN_FLOAT = 18;
public const TOKEN_INTEGER = 19;
public const TOKEN_SINGLE_QUOTED_STRING = 20;
public const TOKEN_DOUBLE_QUOTED_STRING = 21;
public const TOKEN_IDENTIFIER = 22;
public const TOKEN_THIS_VARIABLE = 23;
public const TOKEN_VARIABLE = 24;
public const TOKEN_HORIZONTAL_WS = 25;
public const TOKEN_PHPDOC_EOL = 26;
public const TOKEN_OTHER = 27;
public const TOKEN_END = 28;
public const TOKEN_COLON = 29;
public const TOKEN_WILDCARD = 30;
public const TOKEN_OPEN_CURLY_BRACKET = 31;
public const TOKEN_CLOSE_CURLY_BRACKET = 32;
public const TOKEN_NEGATED = 33;
public const TOKEN_ARROW = 34;
public const TOKEN_DOCTRINE_TAG = 18;
public const TOKEN_FLOAT = 19;
public const TOKEN_INTEGER = 20;
public const TOKEN_SINGLE_QUOTED_STRING = 21;
public const TOKEN_DOUBLE_QUOTED_STRING = 22;
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 => '\'&\'',
@@ -72,11 +74,13 @@ class Lexer
self::TOKEN_OPEN_PHPDOC => '\'/**\'',
self::TOKEN_CLOSE_PHPDOC => '\'*/\'',
self::TOKEN_PHPDOC_TAG => 'TOKEN_PHPDOC_TAG',
self::TOKEN_DOCTRINE_TAG => 'TOKEN_DOCTRINE_TAG',
self::TOKEN_PHPDOC_EOL => 'TOKEN_PHPDOC_EOL',
self::TOKEN_FLOAT => 'TOKEN_FLOAT',
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',
@@ -90,9 +94,17 @@ class Lexer
public const TYPE_OFFSET = 1;
public const LINE_OFFSET = 2;
/** @var bool */
private $parseDoctrineAnnotations;
/** @var string|null */
private $regexp;
public function __construct(bool $parseDoctrineAnnotations = false)
{
$this->parseDoctrineAnnotations = $parseDoctrineAnnotations;
}
/**
* @return list<array{string, int, int}>
*/
@@ -160,17 +172,22 @@ class Lexer
self::TOKEN_PHPDOC_TAG => '@(?:[a-z][a-z0-9-\\\\]+:)?[a-z][a-z0-9-\\\\]*+',
self::TOKEN_PHPDOC_EOL => '\\r?+\\n[\\x09\\x20]*+(?:\\*(?!/)\\x20?+)?',
self::TOKEN_FLOAT => '(?:-?[0-9]++(_[0-9]++)*\\.[0-9]*(_[0-9]++)*+(?:e-?[0-9]++(_[0-9]++)*)?)|(?:-?[0-9]*+(_[0-9]++)*\\.[0-9]++(_[0-9]++)*(?:e-?[0-9]++(_[0-9]++)*)?)|(?:-?[0-9]++(_[0-9]++)*e-?[0-9]++(_[0-9]++)*)',
self::TOKEN_INTEGER => '-?(?:(?:0b[0-1]++(_[0-1]++)*)|(?:0o[0-7]++(_[0-7]++)*)|(?:0x[0-9a-f]++(_[0-9a-f]++)*)|(?:[0-9]++(_[0-9]++)*))',
self::TOKEN_FLOAT => '[+\-]?(?:(?:[0-9]++(_[0-9]++)*\\.[0-9]*+(_[0-9]++)*(?:e[+\-]?[0-9]++(_[0-9]++)*)?)|(?:[0-9]*+(_[0-9]++)*\\.[0-9]++(_[0-9]++)*(?:e[+\-]?[0-9]++(_[0-9]++)*)?)|(?:[0-9]++(_[0-9]++)*e[+\-]?[0-9]++(_[0-9]++)*))',
self::TOKEN_INTEGER => '[+\-]?(?:(?:0b[0-1]++(_[0-1]++)*)|(?:0o[0-7]++(_[0-7]++)*)|(?:0x[0-9a-f]++(_[0-9a-f]++)*)|(?:[0-9]++(_[0-9]++)*))',
self::TOKEN_SINGLE_QUOTED_STRING => '\'(?:\\\\[^\\r\\n]|[^\'\\r\\n\\\\])*+\'',
self::TOKEN_DOUBLE_QUOTED_STRING => '"(?:\\\\[^\\r\\n]|[^"\\r\\n\\\\])*+"',
self::TOKEN_WILDCARD => '\\*',
// anything but TOKEN_CLOSE_PHPDOC or TOKEN_HORIZONTAL_WS or TOKEN_EOL
self::TOKEN_OTHER => '(?:(?!\\*/)[^\\s])++',
];
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
$patterns[self::TOKEN_OTHER] = '(?:(?!\\*/)[^\\s])++';
foreach ($patterns as $type => &$pattern) {
$pattern = '(?:' . $pattern . ')(*MARK:' . $type . ')';
}

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();
@@ -245,22 +317,14 @@ class ConstExprParser
*/
private function enrichWithAttributes(TokenIterator $tokens, Ast\ConstExpr\ConstExprNode $node, int $startLine, int $startIndex): Ast\ConstExpr\ConstExprNode
{
$endLine = $tokens->currentTokenLine();
$endIndex = $tokens->currentTokenIndex();
if ($this->useLinesAttributes) {
$node->setAttribute(Ast\Attribute::START_LINE, $startLine);
$node->setAttribute(Ast\Attribute::END_LINE, $endLine);
$node->setAttribute(Ast\Attribute::END_LINE, $tokens->currentTokenLine());
}
if ($this->useIndexAttributes) {
$tokensArray = $tokens->getTokens();
$endIndex--;
if ($tokensArray[$endIndex][Lexer::TYPE_OFFSET] === Lexer::TOKEN_HORIZONTAL_WS) {
$endIndex--;
}
$node->setAttribute(Ast\Attribute::START_INDEX, $startIndex);
$node->setAttribute(Ast\Attribute::END_INDEX, $endIndex);
$node->setAttribute(Ast\Attribute::END_INDEX, $tokens->endIndexOfLastRelevantToken());
}
return $node;

View File

@@ -2,15 +2,25 @@
namespace PHPStan\PhpDocParser\Parser;
use LogicException;
use PHPStan\PhpDocParser\Ast;
use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprIntegerNode;
use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprStringNode;
use PHPStan\PhpDocParser\Ast\ConstExpr\ConstFetchNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\Doctrine;
use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
use PHPStan\PhpDocParser\Lexer\Lexer;
use PHPStan\ShouldNotHappenException;
use function array_key_exists;
use function array_values;
use function count;
use function rtrim;
use function str_replace;
use function trim;
/**
* @phpstan-import-type ValueType from Doctrine\DoctrineArgument as DoctrineValueType
*/
class PhpDocParser
{
@@ -25,18 +35,27 @@ class PhpDocParser
/** @var ConstExprParser */
private $constantExprParser;
/** @var ConstExprParser */
private $doctrineConstantExprParser;
/** @var bool */
private $requireWhitespaceBeforeDescription;
/** @var bool */
private $preserveTypeAliasesWithInvalidTypes;
/** @var bool */
private $parseDoctrineAnnotations;
/** @var bool */
private $useLinesAttributes;
/** @var bool */
private $useIndexAttributes;
/** @var bool */
private $textBetweenTagsBelongsToDescription;
/**
* @param array{lines?: bool, indexes?: bool} $usedAttributes
*/
@@ -45,15 +64,20 @@ class PhpDocParser
ConstExprParser $constantExprParser,
bool $requireWhitespaceBeforeDescription = false,
bool $preserveTypeAliasesWithInvalidTypes = false,
array $usedAttributes = []
array $usedAttributes = [],
bool $parseDoctrineAnnotations = false,
bool $textBetweenTagsBelongsToDescription = false
)
{
$this->typeParser = $typeParser;
$this->constantExprParser = $constantExprParser;
$this->doctrineConstantExprParser = $constantExprParser->toDoctrine();
$this->requireWhitespaceBeforeDescription = $requireWhitespaceBeforeDescription;
$this->preserveTypeAliasesWithInvalidTypes = $preserveTypeAliasesWithInvalidTypes;
$this->parseDoctrineAnnotations = $parseDoctrineAnnotations;
$this->useLinesAttributes = $usedAttributes['lines'] ?? false;
$this->useIndexAttributes = $usedAttributes['indexes'] ?? false;
$this->textBetweenTagsBelongsToDescription = $textBetweenTagsBelongsToDescription;
}
@@ -64,10 +88,44 @@ class PhpDocParser
$children = [];
if (!$tokens->isCurrentTokenType(Lexer::TOKEN_CLOSE_PHPDOC)) {
$children[] = $this->parseChild($tokens);
while ($tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL) && !$tokens->isCurrentTokenType(Lexer::TOKEN_CLOSE_PHPDOC)) {
if ($this->parseDoctrineAnnotations) {
if (!$tokens->isCurrentTokenType(Lexer::TOKEN_CLOSE_PHPDOC)) {
$lastChild = $this->parseChild($tokens);
$children[] = $lastChild;
while (!$tokens->isCurrentTokenType(Lexer::TOKEN_CLOSE_PHPDOC)) {
if (
$lastChild instanceof Ast\PhpDoc\PhpDocTagNode
&& (
$lastChild->value instanceof Doctrine\DoctrineTagValueNode
|| $lastChild->value instanceof Ast\PhpDoc\GenericTagValueNode
)
) {
$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
if ($tokens->isCurrentTokenType(Lexer::TOKEN_CLOSE_PHPDOC)) {
break;
}
$lastChild = $this->parseChild($tokens);
$children[] = $lastChild;
continue;
}
if (!$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL)) {
break;
}
if ($tokens->isCurrentTokenType(Lexer::TOKEN_CLOSE_PHPDOC)) {
break;
}
$lastChild = $this->parseChild($tokens);
$children[] = $lastChild;
}
}
} else {
if (!$tokens->isCurrentTokenType(Lexer::TOKEN_CLOSE_PHPDOC)) {
$children[] = $this->parseChild($tokens);
while ($tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL) && !$tokens->isCurrentTokenType(Lexer::TOKEN_CLOSE_PHPDOC)) {
$children[] = $this->parseChild($tokens);
}
}
}
@@ -105,6 +163,7 @@ class PhpDocParser
}
/** @phpstan-impure */
private function parseChild(TokenIterator $tokens): Ast\PhpDoc\PhpDocChildNode
{
if ($tokens->isCurrentTokenType(Lexer::TOKEN_PHPDOC_TAG)) {
@@ -113,6 +172,26 @@ class PhpDocParser
return $this->enrichWithAttributes($tokens, $this->parseTag($tokens), $startLine, $startIndex);
}
if ($tokens->isCurrentTokenType(Lexer::TOKEN_DOCTRINE_TAG)) {
$startLine = $tokens->currentTokenLine();
$startIndex = $tokens->currentTokenIndex();
$tag = $tokens->currentTokenValue();
$tokens->next();
$tagStartLine = $tokens->currentTokenLine();
$tagStartIndex = $tokens->currentTokenIndex();
return $this->enrichWithAttributes($tokens, new Ast\PhpDoc\PhpDocTagNode(
$tag,
$this->enrichWithAttributes(
$tokens,
$this->parseDoctrineTagValue($tokens, $tag),
$tagStartLine,
$tagStartIndex
)
), $startLine, $startIndex);
}
$startLine = $tokens->currentTokenLine();
$startIndex = $tokens->currentTokenIndex();
$text = $this->parseText($tokens);
@@ -127,27 +206,14 @@ class PhpDocParser
*/
private function enrichWithAttributes(TokenIterator $tokens, Ast\Node $tag, int $startLine, int $startIndex): Ast\Node
{
$endLine = $tokens->currentTokenLine();
$endIndex = $tokens->currentTokenIndex();
if ($this->useLinesAttributes) {
$tag->setAttribute(Ast\Attribute::START_LINE, $startLine);
$tag->setAttribute(Ast\Attribute::END_LINE, $endLine);
$tag->setAttribute(Ast\Attribute::END_LINE, $tokens->currentTokenLine());
}
if ($this->useIndexAttributes) {
$tokensArray = $tokens->getTokens();
if ($tokensArray[$endIndex][Lexer::TYPE_OFFSET] === Lexer::TOKEN_CLOSE_PHPDOC) {
$endIndex--;
if ($tokensArray[$endIndex][Lexer::TYPE_OFFSET] === Lexer::TOKEN_HORIZONTAL_WS) {
$endIndex--;
}
} elseif ($tokensArray[$endIndex][Lexer::TYPE_OFFSET] === Lexer::TOKEN_PHPDOC_EOL) {
$endIndex--;
}
$tag->setAttribute(Ast\Attribute::START_INDEX, $startIndex);
$tag->setAttribute(Ast\Attribute::END_INDEX, $endIndex);
$tag->setAttribute(Ast\Attribute::END_INDEX, $tokens->endIndexOfLastRelevantToken());
}
return $tag;
@@ -158,29 +224,144 @@ class PhpDocParser
{
$text = '';
while (!$tokens->isCurrentTokenType(Lexer::TOKEN_PHPDOC_EOL)) {
$text .= $tokens->getSkippedHorizontalWhiteSpaceIfAny() . $tokens->joinUntil(Lexer::TOKEN_PHPDOC_EOL, Lexer::TOKEN_CLOSE_PHPDOC, Lexer::TOKEN_END);
$endTokens = [Lexer::TOKEN_PHPDOC_EOL, Lexer::TOKEN_CLOSE_PHPDOC, Lexer::TOKEN_END];
if ($this->textBetweenTagsBelongsToDescription) {
$endTokens = [Lexer::TOKEN_CLOSE_PHPDOC, Lexer::TOKEN_END];
}
$savepoint = false;
// if the next token is EOL, everything below is skipped and empty string is returned
while ($this->textBetweenTagsBelongsToDescription || !$tokens->isCurrentTokenType(Lexer::TOKEN_PHPDOC_EOL)) {
$tmpText = $tokens->getSkippedHorizontalWhiteSpaceIfAny() . $tokens->joinUntil(Lexer::TOKEN_PHPDOC_EOL, ...$endTokens);
$text .= $tmpText;
// stop if we're not at EOL - meaning it's the end of PHPDoc
if (!$tokens->isCurrentTokenType(Lexer::TOKEN_PHPDOC_EOL)) {
break;
}
if ($this->textBetweenTagsBelongsToDescription) {
if (!$savepoint) {
$tokens->pushSavePoint();
$savepoint = true;
} elseif ($tmpText !== '') {
$tokens->dropSavePoint();
$tokens->pushSavePoint();
}
}
$tokens->pushSavePoint();
$tokens->next();
if ($tokens->isCurrentTokenType(Lexer::TOKEN_PHPDOC_TAG, Lexer::TOKEN_PHPDOC_EOL, Lexer::TOKEN_CLOSE_PHPDOC, Lexer::TOKEN_END)) {
// if we're at EOL, check what's next
// if next is a PHPDoc tag, EOL, or end of PHPDoc, stop
if ($tokens->isCurrentTokenType(Lexer::TOKEN_PHPDOC_TAG, Lexer::TOKEN_DOCTRINE_TAG, ...$endTokens)) {
$tokens->rollback();
break;
}
// otherwise if the next is text, continue building the description string
$tokens->dropSavePoint();
$text .= "\n";
$text .= $tokens->getDetectedNewline() ?? "\n";
}
if ($savepoint) {
$tokens->rollback();
$text = rtrim($text, $tokens->getDetectedNewline() ?? "\n");
}
return new Ast\PhpDoc\PhpDocTextNode(trim($text, " \t"));
}
private function parseOptionalDescriptionAfterDoctrineTag(TokenIterator $tokens): string
{
$text = '';
$endTokens = [Lexer::TOKEN_PHPDOC_EOL, Lexer::TOKEN_CLOSE_PHPDOC, Lexer::TOKEN_END];
if ($this->textBetweenTagsBelongsToDescription) {
$endTokens = [Lexer::TOKEN_CLOSE_PHPDOC, Lexer::TOKEN_END];
}
$savepoint = false;
// if the next token is EOL, everything below is skipped and empty string is returned
while ($this->textBetweenTagsBelongsToDescription || !$tokens->isCurrentTokenType(Lexer::TOKEN_PHPDOC_EOL)) {
$tmpText = $tokens->getSkippedHorizontalWhiteSpaceIfAny() . $tokens->joinUntil(Lexer::TOKEN_PHPDOC_TAG, Lexer::TOKEN_DOCTRINE_TAG, Lexer::TOKEN_PHPDOC_EOL, ...$endTokens);
$text .= $tmpText;
// stop if we're not at EOL - meaning it's the end of PHPDoc
if (!$tokens->isCurrentTokenType(Lexer::TOKEN_PHPDOC_EOL)) {
if (!$tokens->isPrecededByHorizontalWhitespace()) {
return trim($text . $this->parseText($tokens)->text, " \t");
}
if ($tokens->isCurrentTokenType(Lexer::TOKEN_PHPDOC_TAG)) {
$tokens->pushSavePoint();
$child = $this->parseChild($tokens);
if ($child instanceof Ast\PhpDoc\PhpDocTagNode) {
if (
$child->value instanceof Ast\PhpDoc\GenericTagValueNode
|| $child->value instanceof Doctrine\DoctrineTagValueNode
) {
$tokens->rollback();
break;
}
if ($child->value instanceof Ast\PhpDoc\InvalidTagValueNode) {
$tokens->rollback();
$tokens->pushSavePoint();
$tokens->next();
if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_PARENTHESES)) {
$tokens->rollback();
break;
}
$tokens->rollback();
return trim($text . $this->parseText($tokens)->text, " \t");
}
}
$tokens->rollback();
return trim($text . $this->parseText($tokens)->text, " \t");
}
break;
}
if ($this->textBetweenTagsBelongsToDescription) {
if (!$savepoint) {
$tokens->pushSavePoint();
$savepoint = true;
} elseif ($tmpText !== '') {
$tokens->dropSavePoint();
$tokens->pushSavePoint();
}
}
$tokens->pushSavePoint();
$tokens->next();
// if we're at EOL, check what's next
// if next is a PHPDoc tag, EOL, or end of PHPDoc, stop
if ($tokens->isCurrentTokenType(Lexer::TOKEN_PHPDOC_TAG, Lexer::TOKEN_DOCTRINE_TAG, ...$endTokens)) {
$tokens->rollback();
break;
}
// otherwise if the next is text, continue building the description string
$tokens->dropSavePoint();
$text .= $tokens->getDetectedNewline() ?? "\n";
}
if ($savepoint) {
$tokens->rollback();
$text = rtrim($text, $tokens->getDetectedNewline() ?? "\n");
}
return trim($text, " \t");
}
public function parseTag(TokenIterator $tokens): Ast\PhpDoc\PhpDocTagNode
{
$tag = $tokens->currentTokenValue();
@@ -312,7 +493,17 @@ class PhpDocParser
break;
default:
if ($this->parseDoctrineAnnotations) {
if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_PARENTHESES)) {
$tagValue = $this->parseDoctrineTagValue($tokens, $tag);
} else {
$tagValue = new Ast\PhpDoc\GenericTagValueNode($this->parseOptionalDescriptionAfterDoctrineTag($tokens));
}
break;
}
$tagValue = new Ast\PhpDoc\GenericTagValueNode($this->parseOptionalDescription($tokens));
break;
}
@@ -327,6 +518,298 @@ class PhpDocParser
}
private function parseDoctrineTagValue(TokenIterator $tokens, string $tag): Ast\PhpDoc\PhpDocTagValueNode
{
$startLine = $tokens->currentTokenLine();
$startIndex = $tokens->currentTokenIndex();
return new Doctrine\DoctrineTagValueNode(
$this->enrichWithAttributes(
$tokens,
new Doctrine\DoctrineAnnotation($tag, $this->parseDoctrineArguments($tokens, false)),
$startLine,
$startIndex
),
$this->parseOptionalDescriptionAfterDoctrineTag($tokens)
);
}
/**
* @return list<Doctrine\DoctrineArgument>
*/
private function parseDoctrineArguments(TokenIterator $tokens, bool $deep): array
{
if (!$tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_PARENTHESES)) {
return [];
}
if (!$deep) {
$tokens->addEndOfLineToSkippedTokens();
}
$arguments = [];
try {
$tokens->consumeTokenType(Lexer::TOKEN_OPEN_PARENTHESES);
do {
if ($tokens->isCurrentTokenType(Lexer::TOKEN_CLOSE_PARENTHESES)) {
break;
}
$arguments[] = $this->parseDoctrineArgument($tokens);
} while ($tokens->tryConsumeTokenType(Lexer::TOKEN_COMMA));
} finally {
if (!$deep) {
$tokens->removeEndOfLineFromSkippedTokens();
}
}
$tokens->consumeTokenType(Lexer::TOKEN_CLOSE_PARENTHESES);
return $arguments;
}
private function parseDoctrineArgument(TokenIterator $tokens): Doctrine\DoctrineArgument
{
if (!$tokens->isCurrentTokenType(Lexer::TOKEN_IDENTIFIER)) {
$startLine = $tokens->currentTokenLine();
$startIndex = $tokens->currentTokenIndex();
return $this->enrichWithAttributes(
$tokens,
new Doctrine\DoctrineArgument(null, $this->parseDoctrineArgumentValue($tokens)),
$startLine,
$startIndex
);
}
$startLine = $tokens->currentTokenLine();
$startIndex = $tokens->currentTokenIndex();
try {
$tokens->pushSavePoint();
$currentValue = $tokens->currentTokenValue();
$tokens->consumeTokenType(Lexer::TOKEN_IDENTIFIER);
$key = $this->enrichWithAttributes(
$tokens,
new IdentifierTypeNode($currentValue),
$startLine,
$startIndex
);
$tokens->consumeTokenType(Lexer::TOKEN_EQUAL);
$value = $this->parseDoctrineArgumentValue($tokens);
$tokens->dropSavePoint();
return $this->enrichWithAttributes(
$tokens,
new Doctrine\DoctrineArgument($key, $value),
$startLine,
$startIndex
);
} catch (ParserException $e) {
$tokens->rollback();
return $this->enrichWithAttributes(
$tokens,
new Doctrine\DoctrineArgument(null, $this->parseDoctrineArgumentValue($tokens)),
$startLine,
$startIndex
);
}
}
/**
* @return DoctrineValueType
*/
private function parseDoctrineArgumentValue(TokenIterator $tokens)
{
$startLine = $tokens->currentTokenLine();
$startIndex = $tokens->currentTokenIndex();
if ($tokens->isCurrentTokenType(Lexer::TOKEN_PHPDOC_TAG, Lexer::TOKEN_DOCTRINE_TAG)) {
$name = $tokens->currentTokenValue();
$tokens->next();
return $this->enrichWithAttributes(
$tokens,
new Doctrine\DoctrineAnnotation($name, $this->parseDoctrineArguments($tokens, true)),
$startLine,
$startIndex
);
}
if ($tokens->tryConsumeTokenType(Lexer::TOKEN_OPEN_CURLY_BRACKET)) {
$items = [];
do {
if ($tokens->isCurrentTokenType(Lexer::TOKEN_CLOSE_CURLY_BRACKET)) {
break;
}
$items[] = $this->parseDoctrineArrayItem($tokens);
} while ($tokens->tryConsumeTokenType(Lexer::TOKEN_COMMA));
$tokens->consumeTokenType(Lexer::TOKEN_CLOSE_CURLY_BRACKET);
return $this->enrichWithAttributes(
$tokens,
new Doctrine\DoctrineArray($items),
$startLine,
$startIndex
);
}
$currentTokenValue = $tokens->currentTokenValue();
$tokens->pushSavePoint(); // because of ConstFetchNode
if ($tokens->tryConsumeTokenType(Lexer::TOKEN_IDENTIFIER)) {
$identifier = $this->enrichWithAttributes(
$tokens,
new Ast\Type\IdentifierTypeNode($currentTokenValue),
$startLine,
$startIndex
);
if (!$tokens->isCurrentTokenType(Lexer::TOKEN_DOUBLE_COLON)) {
$tokens->dropSavePoint();
return $identifier;
}
$tokens->rollback(); // because of ConstFetchNode
} else {
$tokens->dropSavePoint(); // because of ConstFetchNode
}
$exception = new ParserException(
$tokens->currentTokenValue(),
$tokens->currentTokenType(),
$tokens->currentTokenOffset(),
Lexer::TOKEN_IDENTIFIER,
null,
$tokens->currentTokenLine()
);
try {
$constExpr = $this->doctrineConstantExprParser->parse($tokens, true);
if ($constExpr instanceof Ast\ConstExpr\ConstExprArrayNode) {
throw $exception;
}
return $constExpr;
} catch (LogicException $e) {
throw $exception;
}
}
private function parseDoctrineArrayItem(TokenIterator $tokens): Doctrine\DoctrineArrayItem
{
$startLine = $tokens->currentTokenLine();
$startIndex = $tokens->currentTokenIndex();
try {
$tokens->pushSavePoint();
$key = $this->parseDoctrineArrayKey($tokens);
if (!$tokens->tryConsumeTokenType(Lexer::TOKEN_EQUAL)) {
if (!$tokens->tryConsumeTokenType(Lexer::TOKEN_COLON)) {
$tokens->consumeTokenType(Lexer::TOKEN_EQUAL); // will throw exception
}
}
$value = $this->parseDoctrineArgumentValue($tokens);
$tokens->dropSavePoint();
return $this->enrichWithAttributes(
$tokens,
new Doctrine\DoctrineArrayItem($key, $value),
$startLine,
$startIndex
);
} catch (ParserException $e) {
$tokens->rollback();
return $this->enrichWithAttributes(
$tokens,
new Doctrine\DoctrineArrayItem(null, $this->parseDoctrineArgumentValue($tokens)),
$startLine,
$startIndex
);
}
}
/**
* @return ConstExprIntegerNode|ConstExprStringNode|IdentifierTypeNode|ConstFetchNode
*/
private function parseDoctrineArrayKey(TokenIterator $tokens)
{
$startLine = $tokens->currentTokenLine();
$startIndex = $tokens->currentTokenIndex();
if ($tokens->isCurrentTokenType(Lexer::TOKEN_INTEGER)) {
$key = new Ast\ConstExpr\ConstExprIntegerNode(str_replace('_', '', $tokens->currentTokenValue()));
$tokens->next();
} 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)) {
$value = $tokens->currentTokenValue();
$tokens->next();
$key = $this->doctrineConstantExprParser->parseDoctrineString($value, $tokens);
} else {
$currentTokenValue = $tokens->currentTokenValue();
$tokens->pushSavePoint(); // because of ConstFetchNode
if (!$tokens->tryConsumeTokenType(Lexer::TOKEN_IDENTIFIER)) {
$tokens->dropSavePoint();
throw new ParserException(
$tokens->currentTokenValue(),
$tokens->currentTokenType(),
$tokens->currentTokenOffset(),
Lexer::TOKEN_IDENTIFIER,
null,
$tokens->currentTokenLine()
);
}
if (!$tokens->isCurrentTokenType(Lexer::TOKEN_DOUBLE_COLON)) {
$tokens->dropSavePoint();
return $this->enrichWithAttributes(
$tokens,
new IdentifierTypeNode($currentTokenValue),
$startLine,
$startIndex
);
}
$tokens->rollback();
$constExpr = $this->doctrineConstantExprParser->parse($tokens, true);
if (!$constExpr instanceof Ast\ConstExpr\ConstFetchNode) {
throw new ParserException(
$tokens->currentTokenValue(),
$tokens->currentTokenType(),
$tokens->currentTokenOffset(),
Lexer::TOKEN_IDENTIFIER,
null,
$tokens->currentTokenLine()
);
}
return $constExpr;
}
return $this->enrichWithAttributes($tokens, $key, $startLine, $startIndex);
}
/**
* @return Ast\PhpDoc\ParamTagValueNode|Ast\PhpDoc\TypelessParamTagValueNode
*/

View File

@@ -9,6 +9,7 @@ use function assert;
use function count;
use function in_array;
use function strlen;
use function substr;
class TokenIterator
{
@@ -22,6 +23,12 @@ class TokenIterator
/** @var int[] */
private $savePoints = [];
/** @var list<int> */
private $skippedTokenTypes = [Lexer::TOKEN_HORIZONTAL_WS];
/** @var string|null */
private $newline = null;
/**
* @param list<array{string, int, int}> $tokens
*/
@@ -30,11 +37,7 @@ class TokenIterator
$this->tokens = $tokens;
$this->index = $index;
if ($this->tokens[$this->index][Lexer::TYPE_OFFSET] !== Lexer::TOKEN_HORIZONTAL_WS) {
return;
}
$this->index++;
$this->skipIrrelevantTokens();
}
@@ -103,6 +106,21 @@ class TokenIterator
}
public function endIndexOfLastRelevantToken(): int
{
$endIndex = $this->currentTokenIndex();
$endIndex--;
while (in_array($this->tokens[$endIndex][Lexer::TYPE_OFFSET], $this->skippedTokenTypes, true)) {
if (!isset($this->tokens[$endIndex - 1])) {
break;
}
$endIndex--;
}
return $endIndex;
}
public function isCurrentTokenValue(string $tokenValue): bool
{
return $this->tokens[$this->index][Lexer::VALUE_OFFSET] === $tokenValue;
@@ -130,13 +148,14 @@ class TokenIterator
$this->throwError($tokenType);
}
$this->index++;
if (($this->tokens[$this->index][Lexer::TYPE_OFFSET] ?? -1) !== Lexer::TOKEN_HORIZONTAL_WS) {
return;
if ($tokenType === Lexer::TOKEN_PHPDOC_EOL) {
if ($this->newline === null) {
$this->detectNewline();
}
}
$this->index++;
$this->skipIrrelevantTokens();
}
@@ -150,12 +169,7 @@ class TokenIterator
}
$this->index++;
if (($this->tokens[$this->index][Lexer::TYPE_OFFSET] ?? -1) !== Lexer::TOKEN_HORIZONTAL_WS) {
return;
}
$this->index++;
$this->skipIrrelevantTokens();
}
@@ -167,10 +181,7 @@ class TokenIterator
}
$this->index++;
if ($this->tokens[$this->index][Lexer::TYPE_OFFSET] === Lexer::TOKEN_HORIZONTAL_WS) {
$this->index++;
}
$this->skipIrrelevantTokens();
return true;
}
@@ -183,16 +194,30 @@ class TokenIterator
return false;
}
$this->index++;
if ($this->tokens[$this->index][Lexer::TYPE_OFFSET] === Lexer::TOKEN_HORIZONTAL_WS) {
$this->index++;
if ($tokenType === Lexer::TOKEN_PHPDOC_EOL) {
if ($this->newline === null) {
$this->detectNewline();
}
}
$this->index++;
$this->skipIrrelevantTokens();
return true;
}
private function detectNewline(): void
{
$value = $this->currentTokenValue();
if (substr($value, 0, 2) === "\r\n") {
$this->newline = "\r\n";
} elseif (substr($value, 0, 1) === "\n") {
$this->newline = "\n";
}
}
public function getSkippedHorizontalWhiteSpaceIfAny(): string
{
if ($this->index > 0 && $this->tokens[$this->index - 1][Lexer::TYPE_OFFSET] === Lexer::TOKEN_HORIZONTAL_WS) {
@@ -217,12 +242,34 @@ class TokenIterator
public function next(): void
{
$this->index++;
$this->skipIrrelevantTokens();
}
if ($this->tokens[$this->index][Lexer::TYPE_OFFSET] !== Lexer::TOKEN_HORIZONTAL_WS) {
private function skipIrrelevantTokens(): void
{
if (!isset($this->tokens[$this->index])) {
return;
}
$this->index++;
while (in_array($this->tokens[$this->index][Lexer::TYPE_OFFSET], $this->skippedTokenTypes, true)) {
if (!isset($this->tokens[$this->index + 1])) {
break;
}
$this->index++;
}
}
public function addEndOfLineToSkippedTokens(): void
{
$this->skippedTokenTypes = [Lexer::TOKEN_HORIZONTAL_WS, Lexer::TOKEN_PHPDOC_EOL];
}
public function removeEndOfLineFromSkippedTokens(): void
{
$this->skippedTokenTypes = [Lexer::TOKEN_HORIZONTAL_WS];
}
/** @phpstan-impure */
@@ -319,6 +366,11 @@ class TokenIterator
return false;
}
public function getDetectedNewline(): ?string
{
return $this->newline;
}
/**
* Whether the given position is immediately surrounded by parenthesis.
*/

View File

@@ -70,23 +70,14 @@ class TypeParser
*/
public function enrichWithAttributes(TokenIterator $tokens, Ast\Node $type, int $startLine, int $startIndex): Ast\Node
{
$endLine = $tokens->currentTokenLine();
$endIndex = $tokens->currentTokenIndex();
if ($this->useLinesAttributes) {
$type->setAttribute(Ast\Attribute::START_LINE, $startLine);
$type->setAttribute(Ast\Attribute::END_LINE, $endLine);
$type->setAttribute(Ast\Attribute::END_LINE, $tokens->currentTokenLine());
}
if ($this->useIndexAttributes) {
$tokensArray = $tokens->getTokens();
$endIndex--;
if ($tokensArray[$endIndex][Lexer::TYPE_OFFSET] === Lexer::TOKEN_HORIZONTAL_WS) {
$endIndex--;
}
$type->setAttribute(Ast\Attribute::START_INDEX, $startIndex);
$type->setAttribute(Ast\Attribute::END_INDEX, $endIndex);
$type->setAttribute(Ast\Attribute::END_INDEX, $tokens->endIndexOfLastRelevantToken());
}
return $type;

View File

@@ -10,6 +10,11 @@ use PHPStan\PhpDocParser\Ast\Node;
use PHPStan\PhpDocParser\Ast\PhpDoc\AssertTagMethodValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\AssertTagPropertyValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\AssertTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\Doctrine\DoctrineAnnotation;
use PHPStan\PhpDocParser\Ast\PhpDoc\Doctrine\DoctrineArgument;
use PHPStan\PhpDocParser\Ast\PhpDoc\Doctrine\DoctrineArray;
use PHPStan\PhpDocParser\Ast\PhpDoc\Doctrine\DoctrineArrayItem;
use PHPStan\PhpDocParser\Ast\PhpDoc\Doctrine\DoctrineTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\ExtendsTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\ImplementsTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\MethodTagValueNode;
@@ -95,6 +100,8 @@ final class Printer
GenericTypeNode::class . '->genericTypes' => ', ',
ConstExprArrayNode::class . '->items' => ', ',
MethodTagValueNode::class . '->parameters' => ', ',
DoctrineArray::class . '->items' => ', ',
DoctrineAnnotation::class . '->arguments' => ', ',
];
/**
@@ -106,6 +113,8 @@ final class Printer
CallableTypeNode::class . '->parameters' => ['(', '', ''],
ArrayShapeNode::class . '->items' => ['{', '', ''],
ObjectShapeNode::class . '->items' => ['{', '', ''],
DoctrineArray::class . '->items' => ['{', '', ''],
DoctrineAnnotation::class . '->arguments' => ['(', '', ''],
];
/** @var array<string, list<class-string<TypeNode>>> */
@@ -186,6 +195,10 @@ final class Printer
return $node->text;
}
if ($node instanceof PhpDocTagNode) {
if ($node->value instanceof DoctrineTagValueNode) {
return $this->print($node->value);
}
return trim(sprintf('%s %s', $node->name, $this->print($node->value)));
}
if ($node instanceof PhpDocTagValueNode) {
@@ -211,6 +224,18 @@ final class Printer
$isOptional = $node->isOptional ? '=' : '';
return trim("{$type}{$isReference}{$isVariadic}{$node->parameterName}") . $isOptional;
}
if ($node instanceof DoctrineAnnotation) {
return (string) $node;
}
if ($node instanceof DoctrineArgument) {
return (string) $node;
}
if ($node instanceof DoctrineArray) {
return (string) $node;
}
if ($node instanceof DoctrineArrayItem) {
return (string) $node;
}
throw new LogicException(sprintf('Unknown node type %s', get_class($node)));
}
@@ -491,7 +516,7 @@ final class Printer
[$isMultiline, $beforeAsteriskIndent, $afterAsteriskIndent] = $this->isMultiline($tokenIndex, $originalNodes, $originalTokens);
if ($insertStr === "\n * ") {
$insertStr = sprintf("\n%s*%s", $beforeAsteriskIndent, $afterAsteriskIndent);
$insertStr = sprintf('%s%s*%s', $originalTokens->getDetectedNewline() ?? "\n", $beforeAsteriskIndent, $afterAsteriskIndent);
}
foreach ($diff as $i => $diffElem) {
@@ -524,7 +549,7 @@ final class Printer
}
if ($insertNewline) {
$result .= $insertStr . sprintf("\n%s*%s", $beforeAsteriskIndent, $afterAsteriskIndent);
$result .= $insertStr . sprintf('%s%s*%s', $originalTokens->getDetectedNewline() ?? "\n", $beforeAsteriskIndent, $afterAsteriskIndent);
} else {
$result .= $insertStr;
}
@@ -534,7 +559,8 @@ final class Printer
}
$parenthesesNeeded = isset($this->parenthesesListMap[$mapKey])
&& in_array(get_class($newNode), $this->parenthesesListMap[$mapKey], true);
&& in_array(get_class($newNode), $this->parenthesesListMap[$mapKey], true)
&& !in_array(get_class($originalNode), $this->parenthesesListMap[$mapKey], true);
$addParentheses = $parenthesesNeeded && !$originalTokens->hasParentheses($itemStartPos, $itemEndPos);
if ($addParentheses) {
$result .= '(';
@@ -567,7 +593,7 @@ final class Printer
$itemEndPos = $tokenIndex - 1;
if ($insertNewline) {
$result .= $insertStr . sprintf("\n%s*%s", $beforeAsteriskIndent, $afterAsteriskIndent);
$result .= $insertStr . sprintf('%s%s*%s', $originalTokens->getDetectedNewline() ?? "\n", $beforeAsteriskIndent, $afterAsteriskIndent);
} else {
$result .= $insertStr;
}
@@ -636,7 +662,7 @@ final class Printer
if (!$first) {
$result .= $insertStr;
if ($insertNewline) {
$result .= sprintf("\n%s*%s", $beforeAsteriskIndent, $afterAsteriskIndent);
$result .= sprintf('%s%s*%s', $originalTokens->getDetectedNewline() ?? "\n", $beforeAsteriskIndent, $afterAsteriskIndent);
}
}
@@ -777,6 +803,12 @@ final class Printer
$mapKey = get_class($node) . '->' . $subNodeName;
$parenthesesNeeded = isset($this->parenthesesMap[$mapKey])
&& in_array(get_class($subNode), $this->parenthesesMap[$mapKey], true);
if ($subNode->getAttribute(Attribute::ORIGINAL_NODE) !== null) {
$parenthesesNeeded = $parenthesesNeeded
&& !in_array(get_class($subNode->getAttribute(Attribute::ORIGINAL_NODE)), $this->parenthesesMap[$mapKey], true);
}
$addParentheses = $parenthesesNeeded && !$originalTokens->hasParentheses($subStartPos, $subEndPos);
if ($addParentheses) {
$result .= '(';

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/C5cAFAmRaZmcACgkQUcZzBf/C
5cAn/Q//fbWiR/qaSvlHpk73KH7iDfoHwNvRrHSQODZdMa4PGiEbL+SXsKnRxFDo
kEJZwgU5qi3WMflt7Ml3dYDlQDgoDerdaiySYFoBcv1NXDWKoF7+Egy1AHxpfNq+
FMCkZNR2ulSaYUCofM4GkTNap4yVkPCy289ZU6yUmRnJxF+hh/CFfdVPAPbwh/a6
UqV3R2ENJZSbtA1pzSTBpUPQGQ9qcsqngKyNyxk1hEd9opdMg2eSFvO1e1ZZm/Tk
Kgh5wCbsbSJuRPGO4vbiybTeO/qXPDlHV6oA5SHnjJ4H24phCsHdyJHHvLQmrUeR
BKHgnH1y/b5J9cgr9OgEQJK9TMHHd6dii9//Qp+0rUZIDZ4Ym2lDSA/Vn/D9GoV3
zo4QYzW3TvE3QMdnLcX/ZtaLliPdDYIaYUXOiyaYwLFGVxSWZWOC5IN0G0bLJb39
Ca/z839nkWdMqg68q/oHC2Nk/v/KZnKg1RlRjYhj53T6nr0JDEiaYMyETSOIFsVX
AcCQnLLwMndUAibJAyORDnTk+ipg0SecFoPvvhea1BtlTfhSDIlrT4OPKZ5nExzd
nR/zGbIH8lCvsBc+hq+Kgodtfs5nauwEOwlVUwet26xL1YKOd0jxz+Zp6tgk0wba
cMf5L9fm85j83DQYr7Ukaaj81kmMujRWDo/dRojKhUlJUrNnjXA=
=jTtX
iQIzBAABCgAdFiEEynwsejDI6OEnSoR2UcZzBf/C5cAFAmTn0ZAACgkQUcZzBf/C
5cCXIA/9Gmo24JCtSO2yzad3Gi+Jlv0kJtbTMiFgnteWGOq+eXDVC4o9A8I671ei
wkVuv2Gesez0hCcEvFT+QIUwS/xek3tvlGEqj7y9QPtPjhWb1x8hLyy8mlxwhFQh
w17ecQncsyRH0yLkNJB8LkMNNi6t+EjZfrxtyezeKGfVT8HM439Wy/VjccybcJEc
+8Ld5odxf9WINDUq2D+ysiQ9uW0EJsaxe227SZ4Vm9MgPQw0PFFMXxWn96Bb/Eyv
ryB8nKL/Hp3nVzcQdcjaZ4L9K6aH0FO4VtSSdSYaHA3jVxTBugr19FTHiWEEJalS
zv0j97fDkH4xlnzuW74j/35rOqZlMd6NBzS0/eHvpuJ6kfz0KPHXnhW3EQWu6cTY
MTrXo5CfSAfpsMSp6w1evnDRS4z9pqNQ5hmdA2hl+3qTwixyYjP19EKxKfTWC7Qr
o7SwJruaCl6MfQ3dTtvZrRYtvDyJxIUKXGlAaUMA2NP4rh7vUWGwgSJmSwaSs9wP
Fv5cTJ+NO3XRDF5RaWF/MNWH1JjupCwQxlsQthW8Dyy1W2pMtv84AY8jk508boo1
9nEIa/nrKbmjz2fSuIWWVVKrUD6qR5JpF4Odd4p+NR0h8WT/2JAF9rW38WZXWrmh
MsSLy056l96wOB3CnNWtCx7M2FXXGlcOK61Kv7+ybu4A6oFybGg=
=4S4l
-----END PGP SIGNATURE-----

View File

@@ -2,6 +2,21 @@
All notable changes to `array-to-xml` will be documented in this file
## 3.1.6 - 2023-05-11
### What's Changed
- V3 - Code smell ('incorrect' method call) by @ExeQue in https://github.com/spatie/array-to-xml/pull/208
- Bump dependabot/fetch-metadata from 1.3.5 to 1.3.6 by @dependabot in https://github.com/spatie/array-to-xml/pull/210
- Bump dependabot/fetch-metadata from 1.3.6 to 1.4.0 by @dependabot in https://github.com/spatie/array-to-xml/pull/214
- Add addXmlDeclaration parameter by @silnex in https://github.com/spatie/array-to-xml/pull/216
### New Contributors
- @silnex made their first contribution in https://github.com/spatie/array-to-xml/pull/216
**Full Changelog**: https://github.com/spatie/array-to-xml/compare/3.1.5...3.1.6
## 3.1.5 - 2022-12-24
### What's Changed

View File

@@ -239,6 +239,58 @@ This will result in:
</helloyouluckypeople>
```
### Using Closure values
The package can use Closure values:
```php
$users = [
[
'name' => 'one',
'age' => 10,
],
[
'name' => 'two',
'age' => 12,
],
];
$array = [
'users' => function () use ($users) {
$new_users = [];
foreach ($users as $user) {
$new_users[] = array_merge(
$user,
[
'double_age' => $user['age'] * 2,
]
);
}
return $new_users;
},
];
ArrayToXml::convert($array)
```
This will result in:
```xml
<?xml version="1.0"?>
<root>
<users>
<name>one</name>
<age>10</age>
<double_age>20</double_age>
</users>
<users>
<name>two</name>
<age>12</age>
<double_age>24</double_age>
</users>
</root>
```
### Handling numeric keys
The package can also can handle numeric keys:

View File

@@ -2,6 +2,7 @@
namespace Spatie\ArrayToXml;
use Closure;
use DOMDocument;
use DOMElement;
use DOMException;
@@ -143,6 +144,10 @@ class ArrayToXml
protected function convertElement(DOMElement $element, mixed $value): void
{
if ($value instanceof Closure) {
$value = $value();
}
$sequential = $this->isArrayAllKeySequential($value);
if (! is_array($value)) {

View File

@@ -105,11 +105,14 @@ class Application implements ResetInterface
/**
* @final
*/
public function setDispatcher(EventDispatcherInterface $dispatcher)
public function setDispatcher(EventDispatcherInterface $dispatcher): void
{
$this->dispatcher = $dispatcher;
}
/**
* @return void
*/
public function setCommandLoader(CommandLoaderInterface $commandLoader)
{
$this->commandLoader = $commandLoader;
@@ -118,12 +121,15 @@ class Application implements ResetInterface
public function getSignalRegistry(): SignalRegistry
{
if (!$this->signalRegistry) {
throw new RuntimeException('Signals are not supported. Make sure that the `pcntl` extension is installed and that "pcntl_*" functions are not disabled by your php.ini\'s "disable_functions" directive.');
throw new RuntimeException('Signals are not supported. Make sure that the "pcntl" extension is installed and that "pcntl_*" functions are not disabled by your php.ini\'s "disable_functions" directive.');
}
return $this->signalRegistry;
}
/**
* @return void
*/
public function setSignalsToDispatchEvent(int ...$signalsToDispatchEvent)
{
$this->signalsToDispatchEvent = $signalsToDispatchEvent;
@@ -317,10 +323,16 @@ class Application implements ResetInterface
return $exitCode;
}
/**
* @return void
*/
public function reset()
{
}
/**
* @return void
*/
public function setHelperSet(HelperSet $helperSet)
{
$this->helperSet = $helperSet;
@@ -334,6 +346,9 @@ class Application implements ResetInterface
return $this->helperSet ??= $this->getDefaultHelperSet();
}
/**
* @return void
*/
public function setDefinition(InputDefinition $definition)
{
$this->definition = $definition;
@@ -404,6 +419,8 @@ class Application implements ResetInterface
/**
* Sets whether to catch exceptions or not during commands execution.
*
* @return void
*/
public function setCatchExceptions(bool $boolean)
{
@@ -420,6 +437,8 @@ class Application implements ResetInterface
/**
* Sets whether to automatically exit after a command execution or not.
*
* @return void
*/
public function setAutoExit(bool $boolean)
{
@@ -436,7 +455,9 @@ class Application implements ResetInterface
/**
* Sets the application name.
**/
*
* @return void
*/
public function setName(string $name)
{
$this->name = $name;
@@ -452,6 +473,8 @@ class Application implements ResetInterface
/**
* Sets the application version.
*
* @return void
*/
public function setVersion(string $version)
{
@@ -490,6 +513,8 @@ class Application implements ResetInterface
* If a Command is not enabled it will not be added.
*
* @param Command[] $commands An array of commands
*
* @return void
*/
public function addCommands(array $commands)
{
@@ -687,9 +712,7 @@ class Application implements ResetInterface
if ($alternatives = $this->findAlternatives($name, $allCommands)) {
// remove hidden commands
$alternatives = array_filter($alternatives, function ($name) {
return !$this->get($name)->isHidden();
});
$alternatives = array_filter($alternatives, fn ($name) => !$this->get($name)->isHidden());
if (1 == \count($alternatives)) {
$message .= "\n\nDid you mean this?\n ";
@@ -840,9 +863,7 @@ class Application implements ResetInterface
}
if (str_contains($message, "@anonymous\0")) {
$message = preg_replace_callback('/[a-zA-Z_\x7f-\xff][\\\\a-zA-Z0-9_\x7f-\xff]*+@anonymous\x00.*?\.php(?:0x?|:[0-9]++\$)[0-9a-fA-F]++/', function ($m) {
return class_exists($m[0], false) ? (get_parent_class($m[0]) ?: key(class_implements($m[0])) ?: 'class').'@anonymous' : $m[0];
}, $message);
$message = preg_replace_callback('/[a-zA-Z_\x7f-\xff][\\\\a-zA-Z0-9_\x7f-\xff]*+@anonymous\x00.*?\.php(?:0x?|:[0-9]++\$)[0-9a-fA-F]++/', fn ($m) => class_exists($m[0], false) ? (get_parent_class($m[0]) ?: key(class_implements($m[0])) ?: 'class').'@anonymous' : $m[0], $message);
}
$width = $this->terminal->getWidth() ? $this->terminal->getWidth() - 1 : \PHP_INT_MAX;
@@ -903,6 +924,8 @@ class Application implements ResetInterface
/**
* Configures the input and output instances based on the user arguments and options.
*
* @return void
*/
protected function configureIO(InputInterface $input, OutputInterface $output)
{
@@ -977,44 +1000,62 @@ class Application implements ResetInterface
}
}
if ($this->signalsToDispatchEvent) {
$commandSignals = $command instanceof SignalableCommandInterface ? $command->getSubscribedSignals() : [];
$commandSignals = $command instanceof SignalableCommandInterface ? $command->getSubscribedSignals() : [];
if ($commandSignals || $this->dispatcher && $this->signalsToDispatchEvent) {
if (!$this->signalRegistry) {
throw new RuntimeException('Unable to subscribe to signal events. Make sure that the "pcntl" extension is installed and that "pcntl_*" functions are not disabled by your php.ini\'s "disable_functions" directive.');
}
if ($commandSignals || null !== $this->dispatcher) {
if (!$this->signalRegistry) {
throw new RuntimeException('Unable to subscribe to signal events. Make sure that the `pcntl` extension is installed and that "pcntl_*" functions are not disabled by your php.ini\'s "disable_functions" directive.');
}
if (Terminal::hasSttyAvailable()) {
$sttyMode = shell_exec('stty -g');
if (Terminal::hasSttyAvailable()) {
$sttyMode = shell_exec('stty -g');
foreach ([\SIGINT, \SIGTERM] as $signal) {
$this->signalRegistry->register($signal, static function () use ($sttyMode) {
shell_exec('stty '.$sttyMode);
});
}
foreach ([\SIGINT, \SIGTERM] as $signal) {
$this->signalRegistry->register($signal, static fn () => shell_exec('stty '.$sttyMode));
}
}
if (null !== $this->dispatcher) {
if ($this->dispatcher) {
// We register application signals, so that we can dispatch the event
foreach ($this->signalsToDispatchEvent as $signal) {
$event = new ConsoleSignalEvent($command, $input, $output, $signal);
$this->signalRegistry->register($signal, function ($signal, $hasNext) use ($event) {
$this->signalRegistry->register($signal, function ($signal) use ($event, $command, $commandSignals) {
$this->dispatcher->dispatch($event, ConsoleEvents::SIGNAL);
$exitCode = $event->getExitCode();
// No more handlers, we try to simulate PHP default behavior
if (!$hasNext) {
if (!\in_array($signal, [\SIGUSR1, \SIGUSR2], true)) {
exit(0);
// If the command is signalable, we call the handleSignal() method
if (\in_array($signal, $commandSignals, true)) {
$exitCode = $command->handleSignal($signal, $exitCode);
// BC layer for Symfony <= 5
if (null === $exitCode) {
trigger_deprecation('symfony/console', '6.3', 'Not returning an exit code from "%s::handleSignal()" is deprecated, return "false" to keep the command running or "0" to exit successfully.', get_debug_type($command));
$exitCode = 0;
}
}
if (false !== $exitCode) {
exit($exitCode);
}
});
}
// then we register command signals, but not if already handled after the dispatcher
$commandSignals = array_diff($commandSignals, $this->signalsToDispatchEvent);
}
foreach ($commandSignals as $signal) {
$this->signalRegistry->register($signal, [$command, 'handleSignal']);
$this->signalRegistry->register($signal, function (int $signal) use ($command): void {
$exitCode = $command->handleSignal($signal);
// BC layer for Symfony <= 5
if (null === $exitCode) {
trigger_deprecation('symfony/console', '6.3', 'Not returning an exit code from "%s::handleSignal()" is deprecated, return "false" to keep the command running or "0" to exit successfully.', get_debug_type($command));
$exitCode = 0;
}
if (false !== $exitCode) {
exit($exitCode);
}
});
}
}
@@ -1170,7 +1211,7 @@ class Application implements ResetInterface
}
}
$alternatives = array_filter($alternatives, function ($lev) use ($threshold) { return $lev < 2 * $threshold; });
$alternatives = array_filter($alternatives, fn ($lev) => $lev < 2 * $threshold);
ksort($alternatives, \SORT_NATURAL | \SORT_FLAG_CASE);
return array_keys($alternatives);
@@ -1261,7 +1302,7 @@ class Application implements ResetInterface
return $namespaces;
}
private function init()
private function init(): void
{
if ($this->initialized) {
return;

View File

@@ -1,6 +1,13 @@
CHANGELOG
=========
6.3
---
* Add support for choosing exit code while handling signal, or to not exit at all
* Add `ProgressBar::setPlaceholderFormatter` to set a placeholder attached to a instance, instead of being global.
* Add `ReStructuredTextDescriptor`
6.2
---

View File

@@ -141,12 +141,17 @@ class Command
* Ignores validation errors.
*
* This is mainly useful for the help command.
*
* @return void
*/
public function ignoreValidationErrors()
{
$this->ignoreValidationErrors = true;
}
/**
* @return void
*/
public function setApplication(Application $application = null)
{
if (1 > \func_num_args()) {
@@ -162,6 +167,9 @@ class Command
$this->fullDefinition = null;
}
/**
* @return void
*/
public function setHelperSet(HelperSet $helperSet)
{
$this->helperSet = $helperSet;
@@ -198,6 +206,8 @@ class Command
/**
* Configures the current command.
*
* @return void
*/
protected function configure()
{
@@ -228,6 +238,8 @@ class Command
* This method is executed before the InputDefinition is validated.
* This means that this is the only place where the command can
* interactively ask for values of missing required arguments.
*
* @return void
*/
protected function interact(InputInterface $input, OutputInterface $output)
{
@@ -242,6 +254,8 @@ class Command
*
* @see InputInterface::bind()
* @see InputInterface::validate()
*
* @return void
*/
protected function initialize(InputInterface $input, OutputInterface $output)
{
@@ -378,7 +392,7 @@ class Command
*
* @internal
*/
public function mergeApplicationDefinition(bool $mergeArgs = true)
public function mergeApplicationDefinition(bool $mergeArgs = true): void
{
if (null === $this->application) {
return;
@@ -702,7 +716,7 @@ class Command
*
* @throws InvalidArgumentException When the name is invalid
*/
private function validateName(string $name)
private function validateName(string $name): void
{
if (!preg_match('/^[^\:]++(\:[^\:]++)*$/', $name)) {
throw new InvalidArgumentException(sprintf('Command name "%s" is invalid.', $name));

View File

@@ -74,7 +74,7 @@ final class CompleteCommand extends Command
;
}
protected function initialize(InputInterface $input, OutputInterface $output)
protected function initialize(InputInterface $input, OutputInterface $output): void
{
$this->isDebug = filter_var(getenv('SYMFONY_COMPLETION_DEBUG'), \FILTER_VALIDATE_BOOL);
}
@@ -134,12 +134,12 @@ final class CompleteCommand extends Command
$completionInput->bind($command->getDefinition());
if (CompletionInput::TYPE_OPTION_NAME === $completionInput->getCompletionType()) {
$this->log(' Completing option names for the <comment>'.\get_class($command instanceof LazyCommand ? $command->getCommand() : $command).'</> command.');
$this->log(' Completing option names for the <comment>'.($command instanceof LazyCommand ? $command->getCommand() : $command)::class.'</> command.');
$suggestions->suggestOptions($command->getDefinition()->getOptions());
} else {
$this->log([
' Completing using the <comment>'.\get_class($command instanceof LazyCommand ? $command->getCommand() : $command).'</> class.',
' Completing using the <comment>'.($command instanceof LazyCommand ? $command->getCommand() : $command)::class.'</> class.',
' Completing <comment>'.$completionInput->getCompletionType().'</> for <comment>'.$completionInput->getCompletionName().'</>',
]);
if (null !== $compval = $completionInput->getCompletionValue()) {
@@ -155,7 +155,7 @@ final class CompleteCommand extends Command
$this->log('<info>Suggestions:</>');
if ($options = $suggestions->getOptionSuggestions()) {
$this->log(' --'.implode(' --', array_map(function ($o) { return $o->getName(); }, $options)));
$this->log(' --'.implode(' --', array_map(fn ($o) => $o->getName(), $options)));
} elseif ($values = $suggestions->getValueSuggestions()) {
$this->log(' '.implode(' ', $values));
} else {

View File

@@ -39,7 +39,7 @@ final class DumpCompletionCommand extends Command
private array $supportedShells;
protected function configure()
protected function configure(): void
{
$fullCommand = $_SERVER['PHP_SELF'];
$commandName = basename($fullCommand);

View File

@@ -27,6 +27,9 @@ class HelpCommand extends Command
{
private Command $command;
/**
* @return void
*/
protected function configure()
{
$this->ignoreValidationErrors();
@@ -34,12 +37,8 @@ class HelpCommand extends Command
$this
->setName('help')
->setDefinition([
new InputArgument('command_name', InputArgument::OPTIONAL, 'The command name', 'help', function () {
return array_keys((new ApplicationDescription($this->getApplication()))->getCommands());
}),
new InputOption('format', null, InputOption::VALUE_REQUIRED, 'The output format (txt, xml, json, or md)', 'txt', function () {
return (new DescriptorHelper())->getFormats();
}),
new InputArgument('command_name', InputArgument::OPTIONAL, 'The command name', 'help', fn () => array_keys((new ApplicationDescription($this->getApplication()))->getCommands())),
new InputOption('format', null, InputOption::VALUE_REQUIRED, 'The output format (txt, xml, json, or md)', 'txt', fn () => (new DescriptorHelper())->getFormats()),
new InputOption('raw', null, InputOption::VALUE_NONE, 'To output raw command help'),
])
->setDescription('Display help for a command')
@@ -58,6 +57,9 @@ EOF
;
}
/**
* @return void
*/
public function setCommand(Command $command)
{
$this->command = $command;

View File

@@ -25,18 +25,17 @@ use Symfony\Component\Console\Output\OutputInterface;
*/
class ListCommand extends Command
{
/**
* @return void
*/
protected function configure()
{
$this
->setName('list')
->setDefinition([
new InputArgument('namespace', InputArgument::OPTIONAL, 'The namespace name', null, function () {
return array_keys((new ApplicationDescription($this->getApplication()))->getNamespaces());
}),
new InputArgument('namespace', InputArgument::OPTIONAL, 'The namespace name', null, fn () => array_keys((new ApplicationDescription($this->getApplication()))->getNamespaces())),
new InputOption('raw', null, InputOption::VALUE_NONE, 'To output raw command list'),
new InputOption('format', null, InputOption::VALUE_REQUIRED, 'The output format (txt, xml, json, or md)', 'txt', function () {
return (new DescriptorHelper())->getFormats();
}),
new InputOption('format', null, InputOption::VALUE_REQUIRED, 'The output format (txt, xml, json, or md)', 'txt', fn () => (new DescriptorHelper())->getFormats()),
new InputOption('short', null, InputOption::VALUE_NONE, 'To skip describing commands\' arguments'),
])
->setDescription('List commands')

View File

@@ -32,7 +32,7 @@ trait LockableTrait
private function lock(string $name = null, bool $blocking = false): bool
{
if (!class_exists(SemaphoreStore::class)) {
throw new LogicException('To enable the locking feature you must install the symfony/lock component.');
throw new LogicException('To enable the locking feature you must install the symfony/lock component. Try running "composer require symfony/lock".');
}
if (null !== $this->lock) {
@@ -58,7 +58,7 @@ trait LockableTrait
/**
* Releases the command lock if there is one.
*/
private function release()
private function release(): void
{
if ($this->lock) {
$this->lock->release();

View File

@@ -25,6 +25,10 @@ interface SignalableCommandInterface
/**
* The method will be called when the application is signaled.
*
* @param int|false $previousExitCode
* @return int|false The exit code to return or false to continue the normal execution
*/
public function handleSignal(int $signal): void;
public function handleSignal(int $signal, /* int|false $previousExitCode = 0 */);
}

View File

@@ -34,7 +34,7 @@ final class CompletionInput extends ArgvInput
private $tokens;
private $currentIndex;
private $completionType;
private $completionName = null;
private $completionName;
private $completionValue = '';
/**

View File

@@ -29,6 +29,9 @@ use Symfony\Component\DependencyInjection\TypedReference;
*/
class AddConsoleCommandPass implements CompilerPassInterface
{
/**
* @return void
*/
public function process(ContainerBuilder $container)
{
$commandServices = $container->findTaggedServiceIds('console.command', true);

View File

@@ -79,7 +79,7 @@ class ApplicationDescription
return $this->commands[$name] ?? $this->aliases[$name];
}
private function inspectApplication()
private function inspectApplication(): void
{
$this->commands = [];
$this->namespaces = [];

View File

@@ -26,12 +26,9 @@ use Symfony\Component\Console\Output\OutputInterface;
*/
abstract class Descriptor implements DescriptorInterface
{
/**
* @var OutputInterface
*/
protected $output;
protected OutputInterface $output;
public function describe(OutputInterface $output, object $object, array $options = [])
public function describe(OutputInterface $output, object $object, array $options = []): void
{
$this->output = $output;
@@ -45,10 +42,7 @@ abstract class Descriptor implements DescriptorInterface
};
}
/**
* Writes content to output.
*/
protected function write(string $content, bool $decorated = false)
protected function write(string $content, bool $decorated = false): void
{
$this->output->write($content, false, $decorated ? OutputInterface::OUTPUT_NORMAL : OutputInterface::OUTPUT_RAW);
}
@@ -56,25 +50,25 @@ abstract class Descriptor implements DescriptorInterface
/**
* Describes an InputArgument instance.
*/
abstract protected function describeInputArgument(InputArgument $argument, array $options = []);
abstract protected function describeInputArgument(InputArgument $argument, array $options = []): void;
/**
* Describes an InputOption instance.
*/
abstract protected function describeInputOption(InputOption $option, array $options = []);
abstract protected function describeInputOption(InputOption $option, array $options = []): void;
/**
* Describes an InputDefinition instance.
*/
abstract protected function describeInputDefinition(InputDefinition $definition, array $options = []);
abstract protected function describeInputDefinition(InputDefinition $definition, array $options = []): void;
/**
* Describes a Command instance.
*/
abstract protected function describeCommand(Command $command, array $options = []);
abstract protected function describeCommand(Command $command, array $options = []): void;
/**
* Describes an Application instance.
*/
abstract protected function describeApplication(Application $application, array $options = []);
abstract protected function describeApplication(Application $application, array $options = []): void;
}

View File

@@ -20,5 +20,8 @@ use Symfony\Component\Console\Output\OutputInterface;
*/
interface DescriptorInterface
{
/**
* @return void
*/
public function describe(OutputInterface $output, object $object, array $options = []);
}

View File

@@ -26,12 +26,12 @@ use Symfony\Component\Console\Input\InputOption;
*/
class JsonDescriptor extends Descriptor
{
protected function describeInputArgument(InputArgument $argument, array $options = [])
protected function describeInputArgument(InputArgument $argument, array $options = []): void
{
$this->writeData($this->getInputArgumentData($argument), $options);
}
protected function describeInputOption(InputOption $option, array $options = [])
protected function describeInputOption(InputOption $option, array $options = []): void
{
$this->writeData($this->getInputOptionData($option), $options);
if ($option->isNegatable()) {
@@ -39,17 +39,17 @@ class JsonDescriptor extends Descriptor
}
}
protected function describeInputDefinition(InputDefinition $definition, array $options = [])
protected function describeInputDefinition(InputDefinition $definition, array $options = []): void
{
$this->writeData($this->getInputDefinitionData($definition), $options);
}
protected function describeCommand(Command $command, array $options = [])
protected function describeCommand(Command $command, array $options = []): void
{
$this->writeData($this->getCommandData($command, $options['short'] ?? false), $options);
}
protected function describeApplication(Application $application, array $options = [])
protected function describeApplication(Application $application, array $options = []): void
{
$describedNamespace = $options['namespace'] ?? null;
$description = new ApplicationDescription($application, $describedNamespace, true);
@@ -81,7 +81,7 @@ class JsonDescriptor extends Descriptor
/**
* Writes data as json.
*/
private function writeData(array $data, array $options)
private function writeData(array $data, array $options): void
{
$flags = $options['json_encoding'] ?? 0;

View File

@@ -28,7 +28,7 @@ use Symfony\Component\Console\Output\OutputInterface;
*/
class MarkdownDescriptor extends Descriptor
{
public function describe(OutputInterface $output, object $object, array $options = [])
public function describe(OutputInterface $output, object $object, array $options = []): void
{
$decorated = $output->isDecorated();
$output->setDecorated(false);
@@ -38,12 +38,12 @@ class MarkdownDescriptor extends Descriptor
$output->setDecorated($decorated);
}
protected function write(string $content, bool $decorated = true)
protected function write(string $content, bool $decorated = true): void
{
parent::write($content, $decorated);
}
protected function describeInputArgument(InputArgument $argument, array $options = [])
protected function describeInputArgument(InputArgument $argument, array $options = []): void
{
$this->write(
'#### `'.($argument->getName() ?: '<none>')."`\n\n"
@@ -54,7 +54,7 @@ class MarkdownDescriptor extends Descriptor
);
}
protected function describeInputOption(InputOption $option, array $options = [])
protected function describeInputOption(InputOption $option, array $options = []): void
{
$name = '--'.$option->getName();
if ($option->isNegatable()) {
@@ -75,15 +75,13 @@ class MarkdownDescriptor extends Descriptor
);
}
protected function describeInputDefinition(InputDefinition $definition, array $options = [])
protected function describeInputDefinition(InputDefinition $definition, array $options = []): void
{
if ($showArguments = \count($definition->getArguments()) > 0) {
$this->write('### Arguments');
foreach ($definition->getArguments() as $argument) {
$this->write("\n\n");
if (null !== $describeInputArgument = $this->describeInputArgument($argument)) {
$this->write($describeInputArgument);
}
$this->describeInputArgument($argument);
}
}
@@ -95,14 +93,12 @@ class MarkdownDescriptor extends Descriptor
$this->write('### Options');
foreach ($definition->getOptions() as $option) {
$this->write("\n\n");
if (null !== $describeInputOption = $this->describeInputOption($option)) {
$this->write($describeInputOption);
}
$this->describeInputOption($option);
}
}
}
protected function describeCommand(Command $command, array $options = [])
protected function describeCommand(Command $command, array $options = []): void
{
if ($options['short'] ?? false) {
$this->write(
@@ -110,9 +106,7 @@ class MarkdownDescriptor extends Descriptor
.str_repeat('-', Helper::width($command->getName()) + 2)."\n\n"
.($command->getDescription() ? $command->getDescription()."\n\n" : '')
.'### Usage'."\n\n"
.array_reduce($command->getAliases(), function ($carry, $usage) {
return $carry.'* `'.$usage.'`'."\n";
})
.array_reduce($command->getAliases(), fn ($carry, $usage) => $carry.'* `'.$usage.'`'."\n")
);
return;
@@ -125,9 +119,7 @@ class MarkdownDescriptor extends Descriptor
.str_repeat('-', Helper::width($command->getName()) + 2)."\n\n"
.($command->getDescription() ? $command->getDescription()."\n\n" : '')
.'### Usage'."\n\n"
.array_reduce(array_merge([$command->getSynopsis()], $command->getAliases(), $command->getUsages()), function ($carry, $usage) {
return $carry.'* `'.$usage.'`'."\n";
})
.array_reduce(array_merge([$command->getSynopsis()], $command->getAliases(), $command->getUsages()), fn ($carry, $usage) => $carry.'* `'.$usage.'`'."\n")
);
if ($help = $command->getProcessedHelp()) {
@@ -142,7 +134,7 @@ class MarkdownDescriptor extends Descriptor
}
}
protected function describeApplication(Application $application, array $options = [])
protected function describeApplication(Application $application, array $options = []): void
{
$describedNamespace = $options['namespace'] ?? null;
$description = new ApplicationDescription($application, $describedNamespace);
@@ -157,16 +149,12 @@ class MarkdownDescriptor extends Descriptor
}
$this->write("\n\n");
$this->write(implode("\n", array_map(function ($commandName) use ($description) {
return sprintf('* [`%s`](#%s)', $commandName, str_replace(':', '', $description->getCommand($commandName)->getName()));
}, $namespace['commands'])));
$this->write(implode("\n", array_map(fn ($commandName) => sprintf('* [`%s`](#%s)', $commandName, str_replace(':', '', $description->getCommand($commandName)->getName())), $namespace['commands'])));
}
foreach ($description->getCommands() as $command) {
$this->write("\n\n");
if (null !== $describeCommand = $this->describeCommand($command, $options)) {
$this->write($describeCommand);
}
$this->describeCommand($command, $options);
}
}

View File

@@ -0,0 +1,272 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Console\Descriptor;
use Symfony\Component\Console\Application;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Helper\Helper;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputDefinition;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\String\UnicodeString;
class ReStructuredTextDescriptor extends Descriptor
{
// <h1>
private string $partChar = '=';
// <h2>
private string $chapterChar = '-';
// <h3>
private string $sectionChar = '~';
// <h4>
private string $subsectionChar = '.';
// <h5>
private string $subsubsectionChar = '^';
// <h6>
private string $paragraphsChar = '"';
private array $visibleNamespaces = [];
public function describe(OutputInterface $output, object $object, array $options = []): void
{
$decorated = $output->isDecorated();
$output->setDecorated(false);
parent::describe($output, $object, $options);
$output->setDecorated($decorated);
}
/**
* Override parent method to set $decorated = true.
*/
protected function write(string $content, bool $decorated = true): void
{
parent::write($content, $decorated);
}
protected function describeInputArgument(InputArgument $argument, array $options = []): void
{
$this->write(
$argument->getName() ?: '<none>'."\n".str_repeat($this->paragraphsChar, Helper::width($argument->getName()))."\n\n"
.($argument->getDescription() ? preg_replace('/\s*[\r\n]\s*/', "\n", $argument->getDescription())."\n\n" : '')
.'- **Is required**: '.($argument->isRequired() ? 'yes' : 'no')."\n"
.'- **Is array**: '.($argument->isArray() ? 'yes' : 'no')."\n"
.'- **Default**: ``'.str_replace("\n", '', var_export($argument->getDefault(), true)).'``'
);
}
protected function describeInputOption(InputOption $option, array $options = []): void
{
$name = '\-\-'.$option->getName();
if ($option->isNegatable()) {
$name .= '|\-\-no-'.$option->getName();
}
if ($option->getShortcut()) {
$name .= '|-'.str_replace('|', '|-', $option->getShortcut());
}
$optionDescription = $option->getDescription() ? preg_replace('/\s*[\r\n]\s*/', "\n\n", $option->getDescription())."\n\n" : '';
$optionDescription = (new UnicodeString($optionDescription))->ascii();
$this->write(
$name."\n".str_repeat($this->paragraphsChar, Helper::width($name))."\n\n"
.$optionDescription
.'- **Accept value**: '.($option->acceptValue() ? 'yes' : 'no')."\n"
.'- **Is value required**: '.($option->isValueRequired() ? 'yes' : 'no')."\n"
.'- **Is multiple**: '.($option->isArray() ? 'yes' : 'no')."\n"
.'- **Is negatable**: '.($option->isNegatable() ? 'yes' : 'no')."\n"
.'- **Default**: ``'.str_replace("\n", '', var_export($option->getDefault(), true)).'``'."\n"
);
}
protected function describeInputDefinition(InputDefinition $definition, array $options = []): void
{
if ($showArguments = ((bool) $definition->getArguments())) {
$this->write("Arguments\n".str_repeat($this->subsubsectionChar, 9))."\n\n";
foreach ($definition->getArguments() as $argument) {
$this->write("\n\n");
$this->describeInputArgument($argument);
}
}
if ($nonDefaultOptions = $this->getNonDefaultOptions($definition)) {
if ($showArguments) {
$this->write("\n\n");
}
$this->write("Options\n".str_repeat($this->subsubsectionChar, 7)."\n\n");
foreach ($nonDefaultOptions as $option) {
$this->describeInputOption($option);
$this->write("\n");
}
}
}
protected function describeCommand(Command $command, array $options = []): void
{
if ($options['short'] ?? false) {
$this->write(
'``'.$command->getName()."``\n"
.str_repeat($this->subsectionChar, Helper::width($command->getName()))."\n\n"
.($command->getDescription() ? $command->getDescription()."\n\n" : '')
."Usage\n".str_repeat($this->paragraphsChar, 5)."\n\n"
.array_reduce($command->getAliases(), static fn ($carry, $usage) => $carry.'- ``'.$usage.'``'."\n")
);
return;
}
$command->mergeApplicationDefinition(false);
foreach ($command->getAliases() as $alias) {
$this->write('.. _'.$alias.":\n\n");
}
$this->write(
$command->getName()."\n"
.str_repeat($this->subsectionChar, Helper::width($command->getName()))."\n\n"
.($command->getDescription() ? $command->getDescription()."\n\n" : '')
."Usage\n".str_repeat($this->subsubsectionChar, 5)."\n\n"
.array_reduce(array_merge([$command->getSynopsis()], $command->getAliases(), $command->getUsages()), static fn ($carry, $usage) => $carry.'- ``'.$usage.'``'."\n")
);
if ($help = $command->getProcessedHelp()) {
$this->write("\n");
$this->write($help);
}
$definition = $command->getDefinition();
if ($definition->getOptions() || $definition->getArguments()) {
$this->write("\n\n");
$this->describeInputDefinition($definition);
}
}
protected function describeApplication(Application $application, array $options = []): void
{
$description = new ApplicationDescription($application, $options['namespace'] ?? null);
$title = $this->getApplicationTitle($application);
$this->write($title."\n".str_repeat($this->partChar, Helper::width($title)));
$this->createTableOfContents($description, $application);
$this->describeCommands($application, $options);
}
private function getApplicationTitle(Application $application): string
{
if ('UNKNOWN' === $application->getName()) {
return 'Console Tool';
}
if ('UNKNOWN' !== $application->getVersion()) {
return sprintf('%s %s', $application->getName(), $application->getVersion());
}
return $application->getName();
}
private function describeCommands($application, array $options): void
{
$title = 'Commands';
$this->write("\n\n$title\n".str_repeat($this->chapterChar, Helper::width($title))."\n\n");
foreach ($this->visibleNamespaces as $namespace) {
if ('_global' === $namespace) {
$commands = $application->all('');
$this->write('Global'."\n".str_repeat($this->sectionChar, Helper::width('Global'))."\n\n");
} else {
$commands = $application->all($namespace);
$this->write($namespace."\n".str_repeat($this->sectionChar, Helper::width($namespace))."\n\n");
}
foreach ($this->removeAliasesAndHiddenCommands($commands) as $command) {
$this->describeCommand($command, $options);
$this->write("\n\n");
}
}
}
private function createTableOfContents(ApplicationDescription $description, Application $application): void
{
$this->setVisibleNamespaces($description);
$chapterTitle = 'Table of Contents';
$this->write("\n\n$chapterTitle\n".str_repeat($this->chapterChar, Helper::width($chapterTitle))."\n\n");
foreach ($this->visibleNamespaces as $namespace) {
if ('_global' === $namespace) {
$commands = $application->all('');
} else {
$commands = $application->all($namespace);
$this->write("\n\n");
$this->write($namespace."\n".str_repeat($this->sectionChar, Helper::width($namespace))."\n\n");
}
$commands = $this->removeAliasesAndHiddenCommands($commands);
$this->write("\n\n");
$this->write(implode("\n", array_map(static fn ($commandName) => sprintf('- `%s`_', $commandName), array_keys($commands))));
}
}
private function getNonDefaultOptions(InputDefinition $definition): array
{
$globalOptions = [
'help',
'quiet',
'verbose',
'version',
'ansi',
'no-interaction',
];
$nonDefaultOptions = [];
foreach ($definition->getOptions() as $option) {
// Skip global options.
if (!\in_array($option->getName(), $globalOptions)) {
$nonDefaultOptions[] = $option;
}
}
return $nonDefaultOptions;
}
private function setVisibleNamespaces(ApplicationDescription $description): void
{
$commands = $description->getCommands();
foreach ($description->getNamespaces() as $namespace) {
try {
$namespaceCommands = $namespace['commands'];
foreach ($namespaceCommands as $key => $commandName) {
if (!\array_key_exists($commandName, $commands)) {
// If the array key does not exist, then this is an alias.
unset($namespaceCommands[$key]);
} elseif ($commands[$commandName]->isHidden()) {
unset($namespaceCommands[$key]);
}
}
if (!$namespaceCommands) {
// If the namespace contained only aliases or hidden commands, skip the namespace.
continue;
}
} catch (\Exception) {
}
$this->visibleNamespaces[] = $namespace['id'];
}
}
private function removeAliasesAndHiddenCommands(array $commands): array
{
foreach ($commands as $key => $command) {
if ($command->isHidden() || \in_array($key, $command->getAliases(), true)) {
unset($commands[$key]);
}
}
unset($commands['completion']);
return $commands;
}
}

View File

@@ -28,7 +28,7 @@ use Symfony\Component\Console\Input\InputOption;
*/
class TextDescriptor extends Descriptor
{
protected function describeInputArgument(InputArgument $argument, array $options = [])
protected function describeInputArgument(InputArgument $argument, array $options = []): void
{
if (null !== $argument->getDefault() && (!\is_array($argument->getDefault()) || \count($argument->getDefault()))) {
$default = sprintf('<comment> [default: %s]</comment>', $this->formatDefaultValue($argument->getDefault()));
@@ -48,7 +48,7 @@ class TextDescriptor extends Descriptor
), $options);
}
protected function describeInputOption(InputOption $option, array $options = [])
protected function describeInputOption(InputOption $option, array $options = []): void
{
if ($option->acceptValue() && null !== $option->getDefault() && (!\is_array($option->getDefault()) || \count($option->getDefault()))) {
$default = sprintf('<comment> [default: %s]</comment>', $this->formatDefaultValue($option->getDefault()));
@@ -83,7 +83,7 @@ class TextDescriptor extends Descriptor
), $options);
}
protected function describeInputDefinition(InputDefinition $definition, array $options = [])
protected function describeInputDefinition(InputDefinition $definition, array $options = []): void
{
$totalWidth = $this->calculateTotalWidthForOptions($definition->getOptions());
foreach ($definition->getArguments() as $argument) {
@@ -122,7 +122,7 @@ class TextDescriptor extends Descriptor
}
}
protected function describeCommand(Command $command, array $options = [])
protected function describeCommand(Command $command, array $options = []): void
{
$command->mergeApplicationDefinition(false);
@@ -157,7 +157,7 @@ class TextDescriptor extends Descriptor
}
}
protected function describeApplication(Application $application, array $options = [])
protected function describeApplication(Application $application, array $options = []): void
{
$describedNamespace = $options['namespace'] ?? null;
$description = new ApplicationDescription($application, $describedNamespace);
@@ -193,9 +193,7 @@ class TextDescriptor extends Descriptor
}
// calculate max. width based on available commands per namespace
$width = $this->getColumnWidth(array_merge(...array_values(array_map(function ($namespace) use ($commands) {
return array_intersect($namespace['commands'], array_keys($commands));
}, array_values($namespaces)))));
$width = $this->getColumnWidth(array_merge(...array_values(array_map(fn ($namespace) => array_intersect($namespace['commands'], array_keys($commands)), array_values($namespaces)))));
if ($describedNamespace) {
$this->writeText(sprintf('<comment>Available commands for the "%s" namespace:</comment>', $describedNamespace), $options);
@@ -204,9 +202,7 @@ class TextDescriptor extends Descriptor
}
foreach ($namespaces as $namespace) {
$namespace['commands'] = array_filter($namespace['commands'], function ($name) use ($commands) {
return isset($commands[$name]);
});
$namespace['commands'] = array_filter($namespace['commands'], fn ($name) => isset($commands[$name]));
if (!$namespace['commands']) {
continue;
@@ -230,7 +226,7 @@ class TextDescriptor extends Descriptor
}
}
private function writeText(string $content, array $options = [])
private function writeText(string $content, array $options = []): void
{
$this->write(
isset($options['raw_text']) && $options['raw_text'] ? strip_tags($content) : $content,

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