Compare commits

...

40 Commits

Author SHA1 Message Date
Clemens Schwaighofer
8497144053 Admin\Backend level check, DB\IO Error/Warning message update
on Admin\Backend init check that the provided default acl level is valid

DB\IO warning and error drop the "db :" prefix part as this is not needed
we have [DB_ERROR] and [DB_WARNING] sub prefixes anyway, also we run
dedicated log level alerts with context
2023-07-14 15:01:46 +09:00
Clemens Schwaighofer
2006798388 Init set empty db config if db config not found 2023-07-10 08:18:49 +09:00
Clemens Schwaighofer
bf63d850ca Add new DateTime class has date range weekened method
dateRangeHasWeekend with two dates, checks if between those two dates
a weekend (sat or sun) is set
2023-07-04 11:43:27 +09:00
Clemens Schwaighofer
53e267ce24 Updates and fixes from phan/phpstan runs 2023-06-28 15:30:08 +09:00
Clemens Schwaighofer
1754ecf2ee HtmlBuilder: change all return error to Throw, update Element/StrinReplace
We do not return old style bool on error, we throw Exceptions: HtmlBuilderExcpetion

Element has more classes to set tag, id, etc with basic checks for valid data

String Replace to set strings is one array with key -> value entries

Errors thrown on index for element/replace blocks
2023-06-28 14:16:44 +09:00
Clemens Schwaighofer
3c37899a48 Rename Replace to StringReplace to match the actual content
Replace is too general. it is String Replace
2023-06-27 18:33:04 +09:00
Clemens Schwaighofer
0436cfe3da HtmlBuilder classes for Object, Array, String Replace build
Object build is a replicata from the JS one
Array is similar but build on pure Array elements
String replace is just a simple string replacer for now

General\Error for overall error handling
General\Settings for Object/Array based checks and settings
2023-06-27 18:30:26 +09:00
Clemens Schwaighofer
3606de1a00 Back add some checks for the phfo function that matches the php one 2023-06-27 18:30:00 +09:00
Clemens Schwaighofer
3081439eda Switch edit_base CSS name from ADMIN_STYLESHEET to EDIT_BASE_STYLESHEET 2023-06-27 18:29:28 +09:00
Clemens Schwaighofer
7af0e74b85 Fix phpUnit test name for Security\SymmetricEncryption 2023-06-26 14:25:36 +09:00
Clemens Schwaighofer
7748b83a6b DB\IO add stack trace to debug/error/warning calls
To add the actuall call reference for DB IO debug calls we add the
call trace as context options
2023-06-16 13:17:49 +09:00
Clemens Schwaighofer
f83293ff1a Info comments for DB\IO convert options 2023-06-13 11:47:38 +09:00
Clemens Schwaighofer
9c3be2942e phan and phpstan fixes, also add a convert flag reset to original
dbResetConvertFlag resets to the settings given on init of class
2023-06-09 18:23:28 +09:00
Clemens Schwaighofer
ee62bd98ee Add auto type convert for DB\IO
set via db options "db_convert_type" as array with "on", "json", "numeric",
"bytea"

"on" only converts know good types: "bool", "int"

"json" will convert json/jsonb to array
"bytea" will decode escaped bytea to string (note: this might change to resource)

"numeric" will convert to float.
NOTE: if a numeric number is too large a covnersion might drop data.
Use with care.

Convert flags can be chagned with dbSetConvertFlag and dbUnsetConvertFlag

All convert flags are in "DB\Options\Convert" as enum.
2023-06-09 17:01:03 +09:00
Clemens Schwaighofer
02e9610fad Add a general log method to Logger class
the params order is the actual correct one:
log level, message, context, group_id, prefix

not that group_id and prefix are only used if log level is debug

Switched debug params order for context and prefix so prefix is last
2023-06-09 16:59:22 +09:00
Clemens Schwaighofer
8a41db4649 Output\Image cleanup for phpstan 2023-06-09 12:16:49 +09:00
Clemens Schwaighofer
e27ea3dc9f DB\IO phpdoc update 2023-06-09 10:24:26 +09:00
Clemens Schwaighofer
ec4bf54d81 DB\IO phpdoc layout update
Add line between params and method description
2023-06-09 10:21:02 +09:00
Clemens Schwaighofer
ec3ca787fa Logging: prepare message only if log level is high enough
Also some clean ups on internal method call parameters
2023-06-05 09:30:26 +09:00
Clemens Schwaighofer
86acbbb85b Per run logging was not set if flag changed
if setFlag was set for per_run or per_date the init values where not set

Fixed that on setFlag it is checked if we have per_date or per_run and
then set if not set.

Not that for date, set the flag will set a new date, for per run no.
2023-06-02 17:38:09 +09:00
Clemens Schwaighofer
8e0af7a5f7 Class var init fixes 2023-06-01 13:03:46 +09:00
Clemens Schwaighofer
b022662dfc Removed restrictions for printAr to only accept arrays
print_r call takes any value, changed that for all functions

array -> mixed
2023-06-01 12:00:20 +09:00
Clemens Schwaighofer
3039ebf913 Unlink files after test 2023-06-01 11:02:53 +09:00
Clemens Schwaighofer
e2e080c404 Bug fix for DB\IO 2023-06-01 10:58:11 +09:00
Clemens Schwaighofer
4671143d1c phpunit debug fixes 2023-06-01 09:13:59 +09:00
Clemens Schwaighofer
b492558cca Logging unit test regex fix 2023-06-01 08:54:47 +09:00
Clemens Schwaighofer
64e76530d4 deprecated log method call name change in test file 2023-06-01 08:43:47 +09:00
Clemens Schwaighofer
0b93f9f146 Bug fixes and minor updates
- Removed echo from Support Debug dumpVar call
- deprecated DB\IO toggle dbDebug and changed set/get to be like normal
  ones where set just sets and doesn't return anything
- Renamed the logJsDebug to loggingLevelIsDebug
  (other levels can be checked with ->getLoggingLevel()->includes(Level::...))

Adjusted tests for all changes
2023-06-01 08:40:55 +09:00
Clemens Schwaighofer
4c6fe1cd6c Fix Logger\Logger Psr Excpetion calls 2023-05-31 18:44:40 +09:00
Clemens Schwaighofer
83ba48f598 Remove db_debug flag from DB\IO, is set from Logging level 2023-05-31 18:41:13 +09:00
Clemens Schwaighofer
62c6de8244 minor phan config fix 2023-05-31 17:32:00 +09:00
Clemens Schwaighofer
1c2f9f0c2c ACL\Login if euid was set from SESSION and EUID is not int, error
Force to (int)
2023-05-31 16:30:25 +09:00
Clemens Schwaighofer
30bb0e8895 phpunit checks and fixes 2023-05-31 16:14:40 +09:00
Clemens Schwaighofer
75c4c98de8 Convert all classes to strict variable types
All variable declarations in all classes have a strict type set

Exception: constants (will be setable from PHP 8.3 on), resources (no type)

Debug\LoggingLegacy is kept as is, will be deprecated
2023-05-31 15:58:06 +09:00
Clemens Schwaighofer
f72055909b Update in class error/warning/etc calls instead of debug calls
For anything that is not debug use the proper reporting level
2023-05-30 18:30:31 +09:00
Clemens Schwaighofer
b0a8783276 Logging class major change, Debug\Support update
old Debug\Logging is in Debug\LoggingLegacy and Debug\Logging extends
Logging\Logging

Logging\Logging is a new class with most of the functionality except
there is no more print/outout to screen, but we use the default log
levels (RFC5424)
The plan is to be a frontend between the old type class and
Monolog\Monolog

Updated all other classes to use new class interface
2023-05-30 18:12:24 +09:00
Clemens Schwaighofer
7b5ad92e66 Composer updates 2023-05-29 16:21:51 +09:00
Clemens Schwaighofer
250067927a fdebug deprecated message update, update debug logger tester script 2023-05-25 17:55:04 +09:00
Clemens Schwaighofer
7c2cbbaca7 Fix Password phpunit test class name 2023-05-24 15:57:34 +09:00
Clemens Schwaighofer
ac037eabde New Secruity namespace added
Move Passwords from Check to Security and deprecate old

Add new SymmetricEncryption and CreateKey

CreateKey class just creates keys for the SymmetricEncryption
SymmetricEncryption uses the hex2bin calls to convert the hex key to the
internal binary key

Example:
$key = CreateKey::generateRandomKey();
$encrypted = SymmetricEncryption::encrypt($string, $key);
$decrypted = SymmetricEncryption::decrypt($encrypted, $key);

Above $key must be stored in some secure location (.env file)
2023-05-24 15:47:02 +09:00
435 changed files with 20532 additions and 6032 deletions

View File

@@ -128,7 +128,10 @@ return [
'PhanWriteOnlyPublicProperty',
'PhanUnreferencedConstant',
'PhanWriteOnlyPublicProperty',
'PhanReadOnlyPublicProperty'
'PhanReadOnlyPublicProperty',
// start ignore annotations
'PhanUnextractableAnnotationElementName',
'PhanUnextractableAnnotationSuffix',
],
// Override to hardcode existence and types of (non-builtin) globals in the global scope.

View File

@@ -68,13 +68,10 @@ final class CoreLibsACLLoginTest extends TestCase
// logger is always needed
// define basic connection set valid and one invalid
self::$log = new \CoreLibs\Debug\Logging([
self::$log = new \CoreLibs\Logging\Logging([
// 'log_folder' => __DIR__ . DIRECTORY_SEPARATOR . 'log',
'log_folder' => DIRECTORY_SEPARATOR . 'tmp',
'file_id' => 'CoreLibs-ACL-Login-Test',
'debug_all' => true,
'echo_all' => false,
'print_all' => true,
'log_file_id' => 'CoreLibs-ACL-Login-Test',
]);
// test database we need to connect do, if not possible this test is skipped
self::$db = new \CoreLibs\DB\IO(

View File

@@ -458,6 +458,47 @@ final class CoreLibsCombinedDateTimeTest extends TestCase
];
}
/**
* Undocumented function
*
* @return array
*/
public function dateRangeHasWeekendProvider(): array
{
return [
'no weekend' => [
'2023-07-03',
'2023-07-04',
false
],
'start weekend sat' => [
'2023-07-01',
'2023-07-04',
true
],
'start weekend sun' => [
'2023-07-02',
'2023-07-04',
true
],
'end weekend sat' => [
'2023-07-03',
'2023-07-08',
true
],
'end weekend sun' => [
'2023-07-03',
'2023-07-09',
true
],
'long period > 6 days' => [
'2023-07-03',
'2023-07-27',
true
]
];
}
/**
* date string convert test
*
@@ -780,6 +821,29 @@ final class CoreLibsCombinedDateTimeTest extends TestCase
$output
);
}
/**
* Undocumented function
*
* @covers ::dateRangeHasWeekend
* @dataProvider dateRangeHasWeekendProvider
* @testdox dateRangeHasWeekend $start_date and $end_date are expected weekend $expected [$_dataName]
*
* @param string $start_date
* @param string $end_date
* @param bool $expected
* @return void
*/
public function testDateRangeHasWeekend(
string $start_date,
string $end_date,
bool $expected
): void {
$this->assertEquals(
$expected,
\CoreLibs\Combined\DateTime::dateRangeHasWeekend($start_date, $end_date)
);
}
}
// __END__

View File

@@ -22,12 +22,9 @@ final class CoreLibsCreateEmailTest extends TestCase
*/
public static function setUpBeforeClass(): void
{
self::$log = new \CoreLibs\Debug\Logging([
self::$log = new \CoreLibs\Logging\Logging([
'log_folder' => DIRECTORY_SEPARATOR . 'tmp',
'file_id' => 'CoreLibs-Create-Email-Test',
'debug_all' => true,
'echo_all' => false,
'print_all' => true,
'log_file_id' => 'CoreLibs-Create-Email-Test',
]);
}
@@ -624,7 +621,7 @@ final class CoreLibsCreateEmailTest extends TestCase
// force new set for each run
self::$log->setLogUniqueId(true);
// set on of unique log id
self::$log->setLogPer('run', true);
self::$log->setLogFlag(\CoreLibs\Logging\Logger\Flag::per_run);
// init logger
$status = \CoreLibs\Create\Email::sendEmail(
$subject,
@@ -646,7 +643,9 @@ final class CoreLibsCreateEmailTest extends TestCase
// assert content: must load JSON from log file
if ($status == 2) {
// open file, get last entry with 'SEND EMAIL JSON' key
$file = file_get_contents(self::$log->getLogFileName());
$file = file_get_contents(
self::$log->getLogFolder() . self::$log->getLogFile()
);
if ($file !== false) {
// extract SEND EMAIL JSON line
$found = preg_match_all("/^.* <SEND EMAIL JSON> - (.*)$/m", $file, $matches);

View File

@@ -21,45 +21,83 @@ final class CoreLibsCreateUidsTest extends TestCase
public function uniqIdProvider(): array
{
return [
// number length
'too short' => [
0 => 1,
1 => 4,
2 => null
],
'valid length: 10' => [
0 => 10,
1 => 10,
2 => null
],
'valid length: 9, auto length' => [
0 => 9,
1 => 8,
2 => null
],
'valid length: 9, force length' => [
0 => 9,
1 => 9,
2 => true,
],
'very long: 512' => [
0 => 512,
1 => 512,
2 => null
],
// below is all legacy
'md5 hash' => [
0 => 'md5',
1 => 32,
2 => null
],
'sha256 hash' => [
0 => 'sha256',
1 => 64
1 => 64,
2 => null
],
'ripemd160 hash' => [
0 => 'ripemd160',
1 => 40
1 => 40,
2 => null
],
'adler32 hash' => [
0 => 'adler32',
1 => 8
1 => 8,
2 => null
],
'not in list hash but valid' => [
'not in list, set default length' => [
0 => 'sha3-512',
1 => strlen(hash('sha3-512', 'A'))
1 => 64,
2 => null
],
'default hash not set' => [
0 => null,
1 => 64,
2 => null
],
'invalid name' => [
0 => 'iamnotavalidhash',
1 => 64,
2 => null
],
'auto: ' . \CoreLibs\Create\Uids::DEFAULT_HASH => [
0 => \CoreLibs\Create\Uids::DEFAULT_HASH,
1 => strlen(hash(\CoreLibs\Create\Uids::DEFAULT_HASH, 'A'))
// auto calls
'auto: ' . \CoreLibs\Create\Uids::DEFAULT_UNNIQ_ID_LENGTH => [
0 => \CoreLibs\Create\Uids::DEFAULT_UNNIQ_ID_LENGTH,
1 => 64,
2 => null
],
'auto: ' . \CoreLibs\Create\Uids::STANDARD_HASH_LONG => [
0 => \CoreLibs\Create\Uids::STANDARD_HASH_LONG,
1 => strlen(hash(\CoreLibs\Create\Uids::STANDARD_HASH_LONG, 'A'))
1 => strlen(hash(\CoreLibs\Create\Uids::STANDARD_HASH_LONG, 'A')),
2 => null
],
'auto: ' . \CoreLibs\Create\Uids::STANDARD_HASH_SHORT => [
0 => \CoreLibs\Create\Uids::STANDARD_HASH_SHORT,
1 => strlen(hash(\CoreLibs\Create\Uids::STANDARD_HASH_SHORT, 'A'))
1 => strlen(hash(\CoreLibs\Create\Uids::STANDARD_HASH_SHORT, 'A')),
2 => null
],
];
}
@@ -105,25 +143,26 @@ final class CoreLibsCreateUidsTest extends TestCase
*
* @covers ::uniqId
* @dataProvider uniqIdProvider
* @testdox uniqId $input will be length $expected [$_dataName]
* @testdox uniqId $input will be length $expected (Force $flag) [$_dataName]
*
* @param string|null $input
* @param int|string|null $input
* @param string $expected
* @param bool|null $flag
* @return void
*/
public function testUniqId(?string $input, int $expected): void
public function testUniqId(int|string|null $input, int $expected, ?bool $flag): void
{
if ($input === null) {
$this->assertEquals(
$expected,
strlen(\CoreLibs\Create\Uids::uniqId())
);
$uniq_id_length = strlen(\CoreLibs\Create\Uids::uniqId());
} elseif ($flag === null) {
$uniq_id_length = strlen(\CoreLibs\Create\Uids::uniqId($input));
} else {
$this->assertEquals(
$expected,
strlen(\CoreLibs\Create\Uids::uniqId($input))
);
$uniq_id_length = strlen(\CoreLibs\Create\Uids::uniqId($input, $flag));
}
$this->assertEquals(
$expected,
$uniq_id_length
);
}
/**

View File

@@ -37,6 +37,8 @@ namespace tests;
use PHPUnit\Framework\TestCase;
use PHPUnit\Framework\MockObject\MockObject;
use CoreLibs\Logging\Logger\Level;
use CoreLibs\DB\Options\Convert;
/**
* Test class for DB\IO + DB\SQL\PgSQL
@@ -59,20 +61,6 @@ final class CoreLibsDBIOTest extends TestCase
'db_type' => 'pgsql',
'db_encoding' => '',
'db_ssl' => 'allow', // allow, disable, require, prefer
'db_debug' => true,
],
// same as valid, but db debug is off
'valid_debug_false' => [
'db_name' => 'corelibs_db_io_test',
'db_user' => 'corelibs_db_io_test',
'db_pass' => 'corelibs_db_io_test',
'db_host' => 'localhost',
'db_port' => 5432,
'db_schema' => 'public',
'db_type' => 'pgsql',
'db_encoding' => '',
'db_ssl' => 'allow', // allow, disable, require, prefer
'db_debug' => false,
],
// same as valid, but encoding is set
'valid_with_encoding_utf8' => [
@@ -85,7 +73,6 @@ final class CoreLibsDBIOTest extends TestCase
'db_type' => 'pgsql',
'db_encoding' => 'UTF-8',
'db_ssl' => 'allow', // allow, disable, require, prefer
'db_debug' => true,
],
// valid with no schema set
'valid_no_schema' => [
@@ -98,7 +85,6 @@ final class CoreLibsDBIOTest extends TestCase
'db_type' => 'pgsql',
'db_encoding' => '',
'db_ssl' => 'allow', // allow, disable, require, prefer
'db_debug' => true,
],
// invalid (missing db name)
'invalid' => [
@@ -111,7 +97,6 @@ final class CoreLibsDBIOTest extends TestCase
'db_type' => 'pgsql',
'db_encoding' => '',
'db_ssl' => 'allow', // allow, disable, require, prefer
'db_debug' => true,
],
];
private static $log;
@@ -132,14 +117,12 @@ final class CoreLibsDBIOTest extends TestCase
);
}
// define basic connection set valid and one invalid
self::$log = new \CoreLibs\Debug\Logging([
self::$log = new \CoreLibs\Logging\Logging([
// 'log_folder' => __DIR__ . DIRECTORY_SEPARATOR . 'log',
'log_folder' => DIRECTORY_SEPARATOR . 'tmp',
'file_id' => 'CoreLibs-DB-IO-Test',
'debug_all' => false,
'echo_all' => false,
'print_all' => false,
'log_file_id' => 'CoreLibs-DB-IO-Test',
]);
// will be true, default logging is true
$db = new \CoreLibs\DB\IO(
self::$db_config['valid'],
self::$log
@@ -537,6 +520,9 @@ final class CoreLibsDBIOTest extends TestCase
*/
public function debugSetProvider(): array
{
// 0: db connecdtion
// 1: override log flag, null for default
// 2: set flag
return [
'default debug set' => [
// what base connection
@@ -544,11 +530,6 @@ final class CoreLibsDBIOTest extends TestCase
// actions (set)
null,
// set exepected
self::$db_config['valid']['db_debug'],
],
'set debug to true' => [
'valid_debug_false',
true,
true,
],
'set debug to false' => [
@@ -559,99 +540,46 @@ final class CoreLibsDBIOTest extends TestCase
];
}
/**
* test set for toggleDEbug
*
* @return array
*/
public function debugToggleProvider(): array
{
return [
'default debug set' => [
// what base connection
'valid',
// actions
null,
// toggle is inverse
self::$db_config['valid']['db_debug'] ? false : true,
],
'toggle debug to true' => [
'valid_debug_false',
true,
true,
],
'toggle debug to false' => [
'valid',
false,
false,
]
];
}
/**
* Test dbSetDbug, dbGetDebug
*
* @covers ::dbGetDbug
* @covers ::dbSetDebug
* @dataProvider debugSetProvider
* @testdox Setting debug $set will be $expected [$_dataName]
* @testdox Set and Get Debug flag
*
* @return void
*/
public function testDbSetDebug(
string $connection,
?bool $set,
bool $expected
): void {
public function testDbSetDebug(): void
{
$connection = 'valid';
// default set, expect true
$db = new \CoreLibs\DB\IO(
self::$db_config[$connection],
self::$log
);
$this->assertEquals(
$expected,
$set === null ?
$db->dbSetDebug() :
$db->dbSetDebug($set)
$this->assertTrue(
$db->dbGetDebug()
);
// must always match
$this->assertEquals(
$expected,
// switch off
$db->dbSetDebug(false);
$this->assertFalse(
$db->dbGetDebug()
);
$db->dbClose();
}
/**
* Test dbToggleDebug, dbGetDebug
*
* @covers ::dbGetDbug
* @covers ::dbSetDebug
* @dataProvider debugToggleProvider
* @testdox Toggle debug $toggle will be $expected [$_dataName]
*
* @return void
*/
public function testDbToggleDebug(
string $connection,
?bool $toggle,
bool $expected
): void {
// second conenction with log set NOT debug
$log = new \CoreLibs\Logging\Logging([
// 'log_folder' => __DIR__ . DIRECTORY_SEPARATOR . 'log',
'log_folder' => DIRECTORY_SEPARATOR . 'tmp',
'log_file_id' => 'CoreLibs-DB-IO-Test',
'log_level' => \CoreLibs\Logging\Logger\Level::Notice,
]);
$db = new \CoreLibs\DB\IO(
self::$db_config[$connection],
self::$log
$log
);
$this->assertEquals(
$expected,
$toggle === null ?
$db->dbToggleDebug() :
$db->dbToggleDebug($toggle)
);
// must always match
$this->assertEquals(
$expected,
$this->assertFalse(
$db->dbGetDebug()
);
$db->dbClose();
}
// - set max query call sets
@@ -809,7 +737,6 @@ final class CoreLibsDBIOTest extends TestCase
'host' => 'db_host',
'port' => 'db_port',
'ssl' => 'db_ssl',
'debug' => 'db_debug',
'password' => '***',
] as $read => $compare
) {
@@ -4631,6 +4558,176 @@ final class CoreLibsDBIOTest extends TestCase
$db->dbClose();
}
// testing auto convert
/**
* Undocumented function
*
* @covers ::dbSetConvertFlag
* @testdox Check convert type works
*
* @return void
*/
public function testConvertType(): void
{
$db = new \CoreLibs\DB\IO(
self::$db_config['valid'],
self::$log
);
$bytea_data = $db->dbEscapeBytea(
file_get_contents(__DIR__ . DIRECTORY_SEPARATOR . 'CoreLibsDBIOTest.php') ?: ''
);
$query_insert = <<<SQL
INSERT INTO table_with_primary_key (
uid,
row_int, row_numeric, row_varchar, row_varchar_literal,
row_json, row_jsonb, row_bytea, row_timestamp,
row_date, row_interval, row_array_int, row_array_varchar
) VALUES (
$1,
$2, $3, $4, $5,
$6, $7, $8, $9,
$10, $11, $12, $13
)
SQL;
$db->dbExecParams(
$query_insert,
[
'CONVERT_TYPE_TEST',
1, 1.5, 'varchar', 'varchar literla',
json_encode(['json', 'a', 1, true, 'sub' => ['b', 'c']]),
json_encode(['jsonb', 'a', 1, true, 'sub' => ['b', 'c']]),
$bytea_data, date('Y-m-d H:i:s'), date('Y-m-d'), date('H:m:s'),
'{1,2,3}', '{"a","b","c"}'
]
);
$type_layout = [
'uid' => 'string',
'row_int' => 'int',
'row_numeric' => 'float',
'row_varchar' => 'string',
'row_varchar_literal' => 'string',
'row_json' => 'json',
'row_jsonb' => 'json',
'row_bytea' => 'bytea',
'row_timestamp' => 'string',
'row_date' => 'string',
'row_interval' => 'string',
'row_array_int' => 'string',
'row_array_varchar' => 'string'
];
$query_select = <<<SQL
SELECT
uid,
row_int, row_numeric, row_varchar, row_varchar_literal,
row_json, row_jsonb, row_bytea, row_timestamp,
row_date, row_interval, row_array_int, row_array_varchar
FROM
table_with_primary_key
WHERE
uid = $1
SQL;
$res = $db->dbReturnRowParams($query_select, ['CONVERT_TYPE_TEST']);
// all hast to be string
foreach ($res as $key => $value) {
$this->assertIsString($value, 'Aseert string for column: ' . $key);
}
// convert base only
$db->dbSetConvertFlag(Convert::on);
$res = $db->dbReturnRowParams($query_select, ['CONVERT_TYPE_TEST']);
foreach ($res as $key => $value) {
if (is_numeric($key)) {
$name = $db->dbGetFieldName($key);
} else {
$name = $key;
}
switch ($type_layout[$name]) {
case 'int':
$this->assertIsInt($value, 'Aseert int for column: ' . $key . '/' . $name);
break;
default:
$this->assertIsString($value, 'Aseert string for column: ' . $key . '/' . $name);
break;
}
}
$db->dbSetConvertFlag(Convert::numeric);
$res = $db->dbReturnRowParams($query_select, ['CONVERT_TYPE_TEST']);
foreach ($res as $key => $value) {
if (is_numeric($key)) {
$name = $db->dbGetFieldName($key);
} else {
$name = $key;
}
switch ($type_layout[$name]) {
case 'int':
$this->assertIsInt($value, 'Aseert int for column: ' . $key . '/' . $name);
break;
case 'float':
$this->assertIsFloat($value, 'Aseert float for column: ' . $key . '/' . $name);
break;
default:
$this->assertIsString($value, 'Aseert string for column: ' . $key . '/' . $name);
break;
}
}
$db->dbSetConvertFlag(Convert::json);
$res = $db->dbReturnRowParams($query_select, ['CONVERT_TYPE_TEST']);
foreach ($res as $key => $value) {
if (is_numeric($key)) {
$name = $db->dbGetFieldName($key);
} else {
$name = $key;
}
switch ($type_layout[$name]) {
case 'int':
$this->assertIsInt($value, 'Aseert int for column: ' . $key . '/' . $name);
break;
case 'float':
$this->assertIsFloat($value, 'Aseert float for column: ' . $key . '/' . $name);
break;
case 'json':
case 'jsonb':
$this->assertIsArray($value, 'Aseert array for column: ' . $key . '/' . $name);
break;
default:
$this->assertIsString($value, 'Aseert string for column: ' . $key . '/' . $name);
break;
}
}
$db->dbSetConvertFlag(Convert::bytea);
$res = $db->dbReturnRowParams($query_select, ['CONVERT_TYPE_TEST']);
foreach ($res as $key => $value) {
if (is_numeric($key)) {
$name = $db->dbGetFieldName($key);
} else {
$name = $key;
}
switch ($type_layout[$name]) {
case 'int':
$this->assertIsInt($value, 'Aseert int for column: ' . $key . '/' . $name);
break;
case 'float':
$this->assertIsFloat($value, 'Aseert float for column: ' . $key . '/' . $name);
break;
case 'json':
case 'jsonb':
$this->assertIsArray($value, 'Aseert array for column: ' . $key . '/' . $name);
break;
case 'bytea':
// for hex types it must not start with \x
$this->assertStringStartsNotWith(
'\x',
$value,
'Aseert bytes not starts with \x for column: ' . $key . '/' . $name
);
break;
default:
$this->assertIsString($value, 'Aseert string for column: ' . $key . '/' . $name);
break;
}
}
}
// - internal read data (post exec)
// dbGetNumRows, dbGetNumFields, dbGetFieldNames,
// dbGetQuery, dbGetQueryHash, dbGetDbh
@@ -4662,7 +4759,7 @@ final class CoreLibsDBIOTest extends TestCase
. "('Foxtrott', 'Tango', 789, '1982-10-15') ",
null,
//
2,
3,
4,
['row_varchar', 'row_varchar_literal', 'row_int', 'row_date'],
['varchar', 'varchar', 'int4', 'date'],
@@ -4779,9 +4876,16 @@ final class CoreLibsDBIOTest extends TestCase
$db->dbExecParams($query, $params);
}
$this->assertInstanceOf(
'PgSql\Result',
$db->dbGetCursor(),
'Failed assert dbGetCursor'
);
$this->assertEquals(
$compare_query ?? $query,
$db->dbGetQuery()
$db->dbGetQuery(),
'Failed assert dbGetQuery'
);
$this->assertEquals(
// perhaps move that somewhere else?
@@ -4794,7 +4898,8 @@ final class CoreLibsDBIOTest extends TestCase
($params === null ?
$db->dbGetQueryHash($query) :
$db->dbGetQueryHash($query, $params)
)
),
'Failed assertdbGetQueryHash '
);
$this->assertEquals(
$expected_rows,
@@ -4816,6 +4921,35 @@ final class CoreLibsDBIOTest extends TestCase
$db->dbGetFieldTypes(),
'Failed assert dbGetFieldTypes'
);
// check FieldNameTypes matches
$this->assertEquals(
array_combine(
$expected_col_names,
$expected_col_types
),
$db->dbGetFieldNameTypes(),
'Failed assert dbGetFieldNameTypes'
);
// check pos matches name
// name matches type
// pos matches type
foreach ($expected_col_names as $pos => $name) {
$this->assertEquals(
$name,
$db->dbGetFieldName($pos),
'Failed assert dbGetFieldName: ' . $pos . ' => ' . $name
);
$this->assertEquals(
$expected_col_types[$pos],
$db->dbGetFieldType($name),
'Failed assert dbGetFieldType: ' . $name . ' => ' . $expected_col_types[$pos]
);
$this->assertEquals(
$expected_col_types[$pos],
$db->dbGetFieldType($pos),
'Failed assert dbGetFieldType: ' . $pos . ' => ' . $expected_col_types[$pos]
);
}
$dbh = $db->dbGetDbh();
$this->assertIsObject(
$dbh

View File

@@ -10,12 +10,13 @@ use PHPUnit\Framework\TestCase;
/**
* Test class for Debug\Logging
* @coversDefaultClass \CoreLibs\Debug\Logging
* @testdox \CoreLibs\Debug\Logging method tests
* @coversDefaultClass \CoreLibs\Debug\LoggingLegacy
* @testdox \CoreLibs\Debug\LoggingLegacy method tests
*/
final class CoreLibsDebugLoggingTest extends TestCase
final class CoreLibsDebugLoggingLegacyTest extends TestCase
{
private const LOG_FOLDER = __DIR__ . DIRECTORY_SEPARATOR . 'log' . DIRECTORY_SEPARATOR;
/**
* test set for options BASIC
*
@@ -24,7 +25,7 @@ final class CoreLibsDebugLoggingTest extends TestCase
* 1: expected
* 2: override
* override:
* - constant for COSNTANTS
* - constant for CONSTANTS
* - global for _GLOBALS
*
* @return array
@@ -138,7 +139,7 @@ final class CoreLibsDebugLoggingTest extends TestCase
// catch this with the message
$this->expectExceptionMessage($deprecation_message);
}
$log = new \CoreLibs\Debug\Logging($options);
$log = new \CoreLibs\Debug\LoggingLegacy($options);
// reset error handler
restore_error_handler();
// check that settings match
@@ -308,7 +309,7 @@ final class CoreLibsDebugLoggingTest extends TestCase
// catch this with the message
$this->expectExceptionMessage($deprecation_message);
}
$log = new \CoreLibs\Debug\Logging($options);
$log = new \CoreLibs\Debug\LoggingLegacy($options);
// reset error handler
restore_error_handler();
// check current
@@ -385,7 +386,7 @@ final class CoreLibsDebugLoggingTest extends TestCase
bool $expected_get
): void {
// neutral start with default
$log = new \CoreLibs\Debug\Logging([
$log = new \CoreLibs\Debug\LoggingLegacy([
'file_id' => 'testSetGetLogLevelAll',
'log_folder' => self::LOG_FOLDER
]);
@@ -510,7 +511,7 @@ final class CoreLibsDebugLoggingTest extends TestCase
$expected_get
): void {
// neutral start with default
$log = new \CoreLibs\Debug\Logging([
$log = new \CoreLibs\Debug\LoggingLegacy([
'file_id' => 'testSetGetLogLevel',
'log_folder' => self::LOG_FOLDER
]);
@@ -592,7 +593,7 @@ final class CoreLibsDebugLoggingTest extends TestCase
bool $expected_get
): void {
// neutral start with default
$log = new \CoreLibs\Debug\Logging([
$log = new \CoreLibs\Debug\LoggingLegacy([
'file_id' => 'testSetGetLogPer',
'log_folder' => self::LOG_FOLDER
]);
@@ -624,7 +625,7 @@ final class CoreLibsDebugLoggingTest extends TestCase
public function testSetGetLogPrintFileDate(bool $input, bool $expected_set, bool $expected_get): void
{
// neutral start with default
$log = new \CoreLibs\Debug\Logging([
$log = new \CoreLibs\Debug\LoggingLegacy([
'file_id' => 'testSetGetLogPrintFileDate',
'log_folder' => self::LOG_FOLDER
]);
@@ -693,7 +694,7 @@ final class CoreLibsDebugLoggingTest extends TestCase
*/
public function testPrAr(array $input, string $expected): void
{
$log = new \CoreLibs\Debug\Logging([
$log = new \CoreLibs\Debug\LoggingLegacy([
'file_id' => 'testPrAr',
'log_folder' => self::LOG_FOLDER
]);
@@ -757,7 +758,7 @@ final class CoreLibsDebugLoggingTest extends TestCase
*/
public function testPrBl(bool $input, ?string $true, ?string $false, string $expected): void
{
$log = new \CoreLibs\Debug\Logging([
$log = new \CoreLibs\Debug\LoggingLegacy([
'file_id' => 'testPrBl',
'log_folder' => self::LOG_FOLDER
]);
@@ -932,7 +933,7 @@ final class CoreLibsDebugLoggingTest extends TestCase
// remove any files named /tmp/error_log_TestDebug*.log
array_map('unlink', glob($options['log_folder'] . 'error_msg_' . $options['file_id'] . '*.log'));
// init logger
$log = new \CoreLibs\Debug\Logging($options);
$log = new \CoreLibs\Debug\LoggingLegacy($options);
// * debug (A/B)
// NULL check for strip/prefix
$this->assertEquals(
@@ -1046,13 +1047,13 @@ final class CoreLibsDebugLoggingTest extends TestCase
public function testLogUniqueId(bool $option, bool $override): void
{
if ($option === true) {
$log = new \CoreLibs\Debug\Logging([
$log = new \CoreLibs\Debug\LoggingLegacy([
'file_id' => 'testLogUniqueId',
'log_folder' => self::LOG_FOLDER,
'per_run' => $option
]);
} else {
$log = new \CoreLibs\Debug\Logging([
$log = new \CoreLibs\Debug\LoggingLegacy([
'file_id' => 'testLogUniqueId',
'log_folder' => self::LOG_FOLDER
]);

View File

@@ -5,6 +5,7 @@ declare(strict_types=1);
namespace tests;
use PHPUnit\Framework\TestCase;
use CoreLibs\Debug\Support;
/**
* Test class for Debug\Support
@@ -40,6 +41,32 @@ final class CoreLibsDebugSupportTest extends TestCase
];
}
/**
* Undocumented function
*
* @cover ::printTime
* @dataProvider printTimeProvider
* @testdox printTime test with $microtime and match to regex [$_dataName]
*
* @param int|null $mircrotime
* @param string $expected
* @return void
*/
public function testPrintTime(?int $microtime, string $regex): void
{
if ($microtime === null) {
$this->assertMatchesRegularExpression(
$regex,
Support::printTime()
);
} else {
$this->assertMatchesRegularExpression(
$regex,
Support::printTime($microtime)
);
}
}
/**
* Undocumented function
*
@@ -50,18 +77,55 @@ final class CoreLibsDebugSupportTest extends TestCase
return [
'empty array' => [
0 => [],
1 => "<pre>Array\n(\n)\n</pre>"
1 => "<pre>Array\n(\n)\n</pre>",
2 => "Array\n(\n)\n",
],
'simple array' => [
0 => ['a', 'b'],
1 => "<pre>Array\n(\n"
. " [0] => a\n"
. " [1] => b\n"
. ")\n</pre>"
. ")\n</pre>",
2 => "Array\n(\n"
. " [0] => a\n"
. " [1] => b\n"
. ")\n",
],
];
}
/**
* Undocumented function
*
* @cover ::printAr
* @cover ::printArray
* @dataProvider printArrayProvider
* @testdox printAr/printArray $input will be $expected [$_dataName]
*
* @param array $input
* @param string $expected
* @param string $expected_strip
* @return void
*/
public function testPrintAr(array $input, string $expected, string $expected_strip): void
{
$this->assertEquals(
$expected,
Support::printAr($input),
'assert printAr'
);
$this->assertEquals(
$expected,
Support::printArray($input),
'assert printArray'
);
$this->assertEquals(
$expected_strip,
Support::prAr($input),
'assert prAr'
);
}
/**
* Undocumented function
*
@@ -73,27 +137,31 @@ final class CoreLibsDebugSupportTest extends TestCase
'true input default' => [
0 => true,
1 => [],
2 => 'true'
2 => 'true',
3 => 'true',
],
'false input default' => [
0 => false,
1 => [],
2 => 'false'
2 => 'false',
3 => 'false'
],
'false input param name' => [
0 => false,
1 => [
'name' => 'param test'
],
2 => '<b>param test</b>: false'
2 => '<b>param test</b>: false',
3 => 'false'
],
'true input param name, true override' => [
0 => true,
1 => [
'name' => 'param test',
'true' => 'ok'
'true' => 'ok',
],
2 => '<b>param test</b>: ok'
2 => '<b>param test</b>: ok',
3 => 'ok',
],
'false input param name, true override, false override' => [
0 => false,
@@ -102,11 +170,77 @@ final class CoreLibsDebugSupportTest extends TestCase
'true' => 'ok',
'false' => 'not',
],
2 => '<b>param test</b>: not'
2 => '<b>param test</b>: not',
3 => 'not'
],
];
}
/**
* Undocumented function
*
* @cover ::printBool
* @dataProvider printBoolProvider
* @testdox printBool $input will be $expected [$_dataName]
*
* @param bool $input
* @param array $params
* @param string $expected
* @param string $expected_strip
* @return void
*/
public function testPrintBool(bool $input, array $params, string $expected, string $expected_strip): void
{
if (
isset($params['name']) &&
isset($params['true']) &&
isset($params['false'])
) {
$string = Support::printBool(
$input,
$params['name'],
$params['true'],
$params['false']
);
$string_strip = Support::prBl(
$input,
$params['true'],
$params['false']
);
} elseif (isset($params['name']) && isset($params['true'])) {
$string = Support::printBool(
$input,
$params['name'],
$params['true']
);
$string_strip = Support::prBl(
$input,
$params['true'],
);
} elseif (isset($params['name'])) {
$string = Support::printBool(
$input,
$params['name']
);
$string_strip = Support::prBl(
$input
);
} else {
$string = Support::printBool($input);
$string_strip = Support::prBl($input);
}
$this->assertEquals(
$expected,
$string,
'assert printBool'
);
$this->assertEquals(
$expected_strip,
$string_strip,
'assert prBl'
);
}
/**
* Undocumented function
*
@@ -169,12 +303,10 @@ final class CoreLibsDebugSupportTest extends TestCase
'an array, no html' => [
['a', 'b'],
true,
"##HTMLPRE##"
. "Array\n(\n"
"Array\n(\n"
. " [0] => a\n"
. " [1] => b\n"
. ")\n"
. "##/HTMLPRE##",
. ")\n",
],
// resource
'a resource' => [
@@ -191,6 +323,261 @@ final class CoreLibsDebugSupportTest extends TestCase
];
}
/**
* Undocumented function
*
* @cover ::printToString
* @dataProvider printToStringProvider
* @testdox printToString $input with $flag will be $expected [$_dataName]
*
* @param mixed $input anything
* @param boolean|null $flag html flag, only for string and array
* @param string $expected always string
* @return void
*/
public function testPrintToString(mixed $input, ?bool $flag, string $expected): void
{
if ($flag === null) {
// if expected starts with / and ends with / then this is a regex compare
if (
substr($expected, 0, 1) == '/' &&
substr($expected, -1, 1) == '/'
) {
$this->assertMatchesRegularExpression(
$expected,
Support::printToString($input)
);
} else {
$this->assertEquals(
$expected,
Support::printToString($input)
);
}
} else {
$this->assertEquals(
$expected,
Support::printToString($input, $flag)
);
}
}
/**
* Undocumented function
*
* @return array
*/
public function providerDumpExportVar(): array
{
return [
'string' => [
'input' => 'string',
'flag' => null,
'expected_dump' => 'string(6) "string"' . "\n",
'expected_export' => "<pre>'string'</pre>",
],
'string, no html' => [
'input' => 'string',
'flag' => true,
'expected_dump' => 'string(6) "string"' . "\n",
'expected_export' => "'string'",
],
// int
'int' => [
'input' => 6,
'flag' => null,
'expected_dump' => 'int(6)' . "\n",
'expected_export' => "<pre>6</pre>",
],
// float
'float' => [
'input' => 1.6,
'flag' => null,
'expected_dump' => 'float(1.6)' . "\n",
'expected_export' => "<pre>1.6</pre>",
],
// bool
'bool' => [
'input' => true,
'flag' => null,
'expected_dump' => 'bool(true)' . "\n",
'expected_export' => "<pre>true</pre>",
],
// array
'array' => [
'input' => ['string', true],
'flag' => null,
'expected_dump' => "array(2) {\n"
. " [0]=>\n"
. " string(6) \"string\"\n"
. " [1]=>\n"
. " bool(true)\n"
. "}\n",
'expected_export' => "<pre>array (\n"
. " 0 => 'string',\n"
. " 1 => true,\n"
. ")</pre>",
],
// more
];
}
/**
* Undocumented function
*
* @cover ::dumpVar
* @cover ::exportVar
* @dataProvider providerDumpExportVar
* @testdox dump/exportVar $input with $flag will be $expected_dump / $expected_export [$_dataName]
*
* @param mixed $input
* @param bool|null $flag
* @param string $expected_dump
* @param string $expected_export
* @return void
*/
public function testDumpExportVar(mixed $input, ?bool $flag, string $expected_dump, string $expected_export): void
{
if ($flag === null) {
$dump = Support::dumpVar($input);
$export = Support::exportVar($input);
} else {
$dump = Support::dumpVar($input, $flag);
$export = Support::exportVar($input, $flag);
}
$this->assertEquals(
$expected_dump,
$dump,
'assert dumpVar'
);
$this->assertEquals(
$expected_export,
$export,
'assert dumpVar'
);
}
/**
* Undocumented function
*
* @cover ::getCallerFileLine
* @testWith ["vendor/phpunit/phpunit/src/Framework/TestCase.php:"]
* @testdox getCallerFileLine check based on regex /[\w\-\/]/vendor/phpunit/phpunit/src/Framework/TestCase.php:\d+ [$_dataName]
*
* @param string $expected
* @return void
*/
public function testGetCallerFileLine(): void
{
// regex prefix with path "/../" and then fixed vendor + \d+
$regex = "/^\/[\w\-\/]+\/vendor\/phpunit\/phpunit\/src\/Framework\/TestCase.php:\d+$/";
$this->assertMatchesRegularExpression(
$regex,
Support::getCallerFileLine()
);
}
/**
* Undocumented function
*
* @cover ::getCallerMethod
* @testWith ["testGetCallerMethod"]
* @testdox getCallerMethod check if it returns $expected [$_dataName]
*
* @return void
*/
public function testGetCallerMethod(string $expected): void
{
$this->assertEquals(
$expected,
Support::getCallerMethod()
);
}
/**
* Undocumented function
*
* @cover ::getCallerMethodList
* @testWith [["main", "run", "run", "run", "run", "run", "run", "runBare", "runTest", "testGetCallerMethodList"]]
* @testdox getCallerMethodList check if it returns $expected [$_dataName]
*
* @param array $expected
* @return void
*/
public function testGetCallerMethodList(array $expected): void
{
$compare = Support::getCallerMethodList();
// 10: legact
// 11: direct
// 12: full call
switch (count($compare)) {
case 10:
// add nothing
$this->assertEquals(
$expected,
Support::getCallerMethodList(),
'assert expected 10'
);
break;
case 11:
// add one "run" before "runBare"
// array_splice(
// $expected,
// 7,
// 0,
// ['run']
// );
array_splice(
$expected,
0,
0,
['include']
);
$this->assertEquals(
$expected,
Support::getCallerMethodList(),
'assert expected 11'
);
break;
case 12:
// add two "run" before "runBare"
array_splice(
$expected,
7,
0,
['run']
);
array_splice(
$expected,
0,
0,
['include']
);
$this->assertEquals(
$expected,
Support::getCallerMethodList(),
'assert expected 12'
);
break;
}
}
/**
* Undocumented function
*
* @cover ::getCallerClass
* @testWith ["PHPUnit\\TextUI\\Command"]
* @testdox getCallerClass check if it returns $expected [$_dataName]
*
* @return void
*/
public function testGetCallerClass(string $expected): void
{
$this->assertEquals(
$expected,
Support::getCallerClass()
);
}
/**
* Undocumented function
*
@@ -236,205 +623,6 @@ final class CoreLibsDebugSupportTest extends TestCase
];
}
/**
* Undocumented function
*
* @cover ::printTime
* @dataProvider printTimeProvider
* @testdox printTime test with $microtime and match to regex [$_dataName]
*
* @param int|null $mircrotime
* @param string $expected
* @return void
*/
public function testPrintTime(?int $microtime, string $regex): void
{
if ($microtime === null) {
$this->assertMatchesRegularExpression(
$regex,
\CoreLibs\Debug\Support::printTime()
);
} else {
$this->assertMatchesRegularExpression(
$regex,
\CoreLibs\Debug\Support::printTime($microtime)
);
}
}
/**
* Undocumented function
*
* @cover ::printAr
* @cover ::printArray
* @dataProvider printArrayProvider
* @testdox printAr/printArray $input will be $expected [$_dataName]
*
* @param array $input
* @param string $expected
* @return void
*/
public function testPrintAr(array $input, string $expected): void
{
$this->assertEquals(
$expected,
\CoreLibs\Debug\Support::printAr($input),
'assert printAr'
);
$this->assertEquals(
$expected,
\CoreLibs\Debug\Support::printArray($input),
'assert printArray'
);
}
/**
* Undocumented function
*
* @cover ::printBool
* @dataProvider printBoolProvider
* @testdox printBool $input will be $expected [$_dataName]
*
* @param bool $input
* @param array $params
* @param string $expected
* @return void
*/
public function testPrintBool(bool $input, array $params, string $expected): void
{
if (
isset($params['name']) &&
isset($params['true']) &&
isset($params['false'])
) {
$string = \CoreLibs\Debug\Support::printBool(
$input,
$params['name'],
$params['true'],
$params['false']
);
} elseif (isset($params['name']) && isset($params['true'])) {
$string = \CoreLibs\Debug\Support::printBool(
$input,
$params['name'],
$params['true']
);
} elseif (isset($params['name'])) {
$string = \CoreLibs\Debug\Support::printBool(
$input,
$params['name']
);
} else {
$string = \CoreLibs\Debug\Support::printBool($input);
}
$this->assertEquals(
$expected,
$string,
'assert printBool'
);
}
/**
* Undocumented function
*
* @cover ::printToString
* @dataProvider printToStringProvider
* @testdox printToString $input with $flag will be $expected [$_dataName]
*
* @param mixed $input anything
* @param boolean|null $flag html flag, only for string and array
* @param string $expected always string
* @return void
*/
public function testPrintToString(mixed $input, ?bool $flag, string $expected): void
{
if ($flag === null) {
// if expected starts with / and ends with / then this is a regex compare
if (
substr($expected, 0, 1) == '/' &&
substr($expected, -1, 1) == '/'
) {
$this->assertMatchesRegularExpression(
$expected,
\CoreLibs\Debug\Support::printToString($input)
);
} else {
$this->assertEquals(
$expected,
\CoreLibs\Debug\Support::printToString($input)
);
}
} else {
$this->assertEquals(
$expected,
\CoreLibs\Debug\Support::printToString($input, $flag)
);
}
}
/**
* Undocumented function
*
* @cover ::getCallerMethod
* @testWith ["testGetCallerMethod"]
* @testdox getCallerMethod check if it returns $expected [$_dataName]
*
* @return void
*/
public function testGetCallerMethod(string $expected): void
{
$this->assertEquals(
$expected,
\CoreLibs\Debug\Support::getCallerMethod()
);
}
/**
* Undocumented function
*
* @cover ::getCallerMethodList
* @testWith [["main", "run", "run", "run", "run", "run", "run", "runBare", "runTest", "testGetCallerMethodList"],["include", "main", "run", "run", "run", "run", "run", "run", "run", "runBare", "runTest", "testGetCallerMethodList"]]
* @testdox getCallerMethodList check if it returns $expected [$_dataName]
*
* @param array $expected
* @return void
*/
public function testGetCallerMethodList(array $expected, array $expected_group): void
{
$compare = \CoreLibs\Debug\Support::getCallerMethodList();
// if we direct call we have 10, if we call as folder we get 11
if (count($compare) == 10) {
$this->assertEquals(
$expected,
\CoreLibs\Debug\Support::getCallerMethodList(),
'assert expected 10'
);
} else {
$this->assertEquals(
$expected_group,
\CoreLibs\Debug\Support::getCallerMethodList(),
'assert expected group'
);
}
}
/**
* Undocumented function
*
* @cover ::getCallerClass
* @testWith ["PHPUnit\\TextUI\\Command"]
* @testdox getCallerClass check if it returns $expected [$_dataName]
*
* @return void
*/
public function testGetCallerClass(string $expected): void
{
$this->assertEquals(
$expected,
\CoreLibs\Debug\Support::getCallerClass()
);
}
/**
* Undocumented function
*
@@ -453,19 +641,19 @@ final class CoreLibsDebugSupportTest extends TestCase
if ($replace === null && $flag === null) {
$this->assertEquals(
$expected,
\CoreLibs\Debug\Support::debugString($input),
Support::debugString($input),
'assert all default'
);
} elseif ($flag === null) {
$this->assertEquals(
$expected,
\CoreLibs\Debug\Support::debugString($input, $replace),
Support::debugString($input, $replace),
'assert flag default'
);
} else {
$this->assertEquals(
$expected,
\CoreLibs\Debug\Support::debugString($input, $replace, $flag),
Support::debugString($input, $replace, $flag),
'assert all set'
);
}

View File

@@ -67,17 +67,17 @@ final class CoreLibsGetSystemTest extends TestCase
'original set' => [
0 => null,
1 => 'NOHOST',
2 => 'NOPORT',
2 => 0,
],
'override set no port' => [
0 => 'foo.org',
1 => 'foo.org',
2 => '80'
2 => 80
],
'override set with port' => [
0 => 'foo.org:443',
1 => 'foo.org',
2 => '443'
2 => 443
]
];
}
@@ -138,10 +138,10 @@ final class CoreLibsGetSystemTest extends TestCase
*
* @param string|null $input
* @param string $expected_host
* @param string $expected_port
* @param int $expected_port
* @return void
*/
public function testGetHostNanme(?string $input, string $expected_host, string $expected_port): void
public function testGetHostNanme(?string $input, string $expected_host, int $expected_port): void
{
// print "HOSTNAME: " . $_SERVER['HTTP_HOST'] . "<br>";
// print "SERVER: " . print_r($_SERVER, true) . "\n";

View File

@@ -0,0 +1,847 @@
<?php
declare(strict_types=1);
namespace tests;
use PHPUnit\Framework\TestCase;
use CoreLibs\Logging\Logger\Level;
use CoreLibs\Logging\Logger\Flag;
// TODO: setLogPer test log file written matches pattern
/**
* Test class for Logging
* @coversDefaultClass \CoreLibs\Logging\Logging
* @testdox \CoreLibs\Logging\Logging method tests
*/
final class CoreLibsLoggingLoggingTest extends TestCase
{
private const LOG_FOLDER = __DIR__ . DIRECTORY_SEPARATOR . 'log' . DIRECTORY_SEPARATOR;
private const REGEX_BASE = "\[[\d\-\s\.:]+\]\s{1}" // date
. "\[[\w\.]+(:\d+)?\]\s{1}" // host:port
. "\[[\w\-\.\/]+:\d+\]\s{1}" // folder/file
. "\[\w+\]\s{1}" // run id
. "{[\w\\\\]+(::\w+)?}\s{1}"; // class
public static function tearDownAfterClass(): void
{
array_map('unlink', glob(self::LOG_FOLDER . '*.log'));
}
/**
* test set for options BASIC
*
* 0: options
* - null for NOT set
* 1: expected
* 2: override
* override:
* - constant for COSNTANTS
* - global for _GLOBALS
*
* @return array
*/
public function optionsProvider(): array
{
return [
'log folder set' => [
'options' => [
'log_folder' => DIRECTORY_SEPARATOR . 'tmp',
'log_file_id' => 'testClassInit',
],
'expected' => [
'log_folder' => DIRECTORY_SEPARATOR . 'tmp' . DIRECTORY_SEPARATOR,
'log_level' => Level::Debug,
'log_file_id' => 'testClassInit',
],
'override' => [],
],
// -> deprecation warning, log_folder must be set
'no log folder set' => [
'options' => [
'log_file_id' => 'testClassInit'
],
'expected' => [
'log_folder' => getcwd() . DIRECTORY_SEPARATOR,
'log_level' => Level::Debug,
'log_file_id' => 'testClassInit',
],
'override' => []
],
// -> upcoming deprecated
'file_id set but not log_file_id' => [
'options' => [
'log_folder' => DIRECTORY_SEPARATOR . 'tmp' . DIRECTORY_SEPARATOR,
'file_id' => 'testClassInit',
],
'expected' => [
'log_folder' => DIRECTORY_SEPARATOR . 'tmp' . DIRECTORY_SEPARATOR,
'log_level' => Level::Debug,
'log_file_id' => 'testClassInit',
],
'override' => [],
],
// both file_id and log_file_id set -> WARNING
'file_id and log_file_id set' => [
'options' => [
'log_folder' => DIRECTORY_SEPARATOR . 'tmp' . DIRECTORY_SEPARATOR,
'file_id' => 'testClassInit',
'log_file_id' => 'testClassInit',
],
'expected' => [
'log_folder' => DIRECTORY_SEPARATOR . 'tmp' . DIRECTORY_SEPARATOR,
'log_level' => Level::Debug,
'log_file_id' => 'testClassInit',
],
'override' => [],
],
// no log file id set -> error,
'nothing set' => [
'options' => [],
'expected' => [
'log_folder' => getcwd() . DIRECTORY_SEPARATOR,
'log_level' => Level::Debug,
'log_file_id' => 'NOHOST-0_PHPUnit-TextUI-Command',
],
'override' => []
]
];
}
/**
* init logging class
*
* @dataProvider optionsProvider
* @testdox init test [$_dataName]
*
* @param array $options
* @param array $expected
* @param array $override
* @return void
*/
public function testClassInit(array $options, array $expected, array $override): void
{
if (!empty($override['constant'])) {
foreach ($override['constant'] as $var => $value) {
if (!defined($var)) {
define($var, $value);
}
}
// for deprecated no log_folder set
// if base is defined and it does have AAASetupData set
// change the log_folder "Debug" to "AAASetupData"
if (
defined('BASE') &&
strpos(BASE, DIRECTORY_SEPARATOR . 'AAASetupData') !== false
) {
$expected['log_folder'] = str_replace(
DIRECTORY_SEPARATOR . 'Debug',
DIRECTORY_SEPARATOR . 'AAASetupData',
$expected['log_folder']
);
}
}
// if not log folder and constant set -> expect E_USER_DEPRECATION
if (!empty($override['constant']) && empty($options['log_folder'])) {
// the deprecation message
$deprecation_message = 'options: log_folder must be set. '
. 'Setting via BASE and LOG constants is deprecated';
// convert E_USER_DEPRECATED to a exception
set_error_handler(
static function (int $errno, string $errstr): never {
throw new \Exception($errstr, $errno);
},
E_USER_DEPRECATED
);
// catch this with the message
$this->expectExceptionMessage($deprecation_message);
}
// alert for log file id with globals
if (!empty($override['constant']) && empty($options['log_file_id'])) {
//
}
// alert for log file id and file id set
if (
!empty($options['log_file_id']) &&
!empty($options['file_id'])
) {
set_error_handler(
static function (int $errno, string $errstr): never {
throw new \InvalidArgumentException($errstr, $errno);
},
E_USER_WARNING
);
$error_message = 'options: "file_id" is deprecated use: "log_file_id".';
$this->expectExceptionMessage($error_message);
$this->expectException(\InvalidArgumentException::class);
set_error_handler(
static function (int $errno, string $errstr): never {
throw new \Exception($errstr, $errno);
},
E_USER_DEPRECATED
);
$this->expectException(\Exception::class);
// $error_message = 'options: both log_file_id and log_id are set at the same time, will use log_file_id';
// $this->expectExceptionMessage($error_message);
}
// empty log folder
if (empty($override['constant']) && empty($options['log_folder'])) {
$this->expectException(\InvalidArgumentException::class);
$this->expectExceptionMessageMatches("/^Missing mandatory option: \"/");
} elseif (empty($options['log_file_id']) && !empty($options['file_id'])) {
// the deprecation message
$deprecation_message = 'options: "file_id" is deprecated use: "log_file_id".';
// convert E_USER_DEPRECATED to a exception
set_error_handler(
static function (int $errno, string $errstr): never {
throw new \Exception($errstr, $errno);
},
E_USER_DEPRECATED
);
// catch this with the message
$this->expectExceptionMessage($deprecation_message);
}
$log = new \CoreLibs\Logging\Logging($options);
// reset error handler
restore_error_handler();
// check that settings match
$this->assertEquals(
$expected['log_folder'],
$log->getLogFolder(),
'log folder not matching'
);
$this->assertEquals(
$expected['log_file_id'],
$log->getLogFileId(),
'log file id not matching'
);
}
// test all setters/getters
// setLoggingLevel
// getLoggingLevel
// loggingLevelIsDebug
/**
* Undocumented function
*
* @covers ::setLoggingLevel
* @covers ::getLoggingLevel
* @covers ::loggingLevelIsDebug
* @testdox setLoggingLevel set/get checks
*
* @return void
*/
public function testSetLoggingLevel(): void
{
// valid that is not Debug
$log = new \CoreLibs\Logging\Logging([
'log_file_id' => 'testSetLoggingLevel',
'log_folder' => self::LOG_FOLDER,
'log_level' => Level::Info
]);
$this->assertEquals(
Level::Info,
$log->getLoggingLevel()
);
$this->assertFalse(
$log->loggingLevelIsDebug()
);
// not set, should be debug]
$log = new \CoreLibs\Logging\Logging([
'log_file_id' => 'testSetLoggingLevel',
'log_folder' => self::LOG_FOLDER,
]);
$this->assertEquals(
Level::Debug,
$log->getLoggingLevel()
);
$this->assertTrue(
$log->loggingLevelIsDebug()
);
// invalid, should be debug, will throw excpetion too
$this->expectException(\InvalidArgumentException::class);
$this->expectExceptionMessage('Option: "log_level" is not of instance \CoreLibs\Logging\Logger\Level');
$log = new \CoreLibs\Logging\Logging([
'log_file_id' => 'testSetLoggingLevel',
'log_folder' => self::LOG_FOLDER,
'log_level' => 'I'
]);
$this->assertEquals(
Level::Debug,
$log->getLoggingLevel()
);
$this->assertTrue(
$log->loggingLevelIsDebug()
);
// set valid, then change
$log = new \CoreLibs\Logging\Logging([
'log_file_id' => 'testSetLoggingLevel',
'log_folder' => self::LOG_FOLDER,
'log_level' => Level::Info
]);
$this->assertEquals(
Level::Info,
$log->getLoggingLevel()
);
$log->setLoggingLevel(Level::Notice);
$this->assertEquals(
Level::Notice,
$log->getLoggingLevel()
);
// illegal logging level
$this->expectException(\Psr\Log\InvalidArgumentException::class);
$this->expectExceptionMessageMatches("/^Level \"NotGood\" is not defined, use one of: /");
$log->setLoggingLevel('NotGood');
}
// setLogFileId
// getLogFileId
/**
* Undocumented function
*
* @covers ::setLogFileId
* @covers ::getLogFileId
* @testdox setLogFileId set/get checks
*
* @return void
*/
public function testLogFileId(): void
{
$log = new \CoreLibs\Logging\Logging([
'log_file_id' => 'testLogFileId',
'log_folder' => self::LOG_FOLDER
]);
// bad, keep same
$log->setLogFileId('$$##$%#$%&');
$this->assertEquals(
'testLogFileId',
$log->getLogFileId()
);
// good, change
$log->setLogFileId('validID');
$this->assertEquals(
'validID',
$log->getLogFileId()
);
// invalid on init
$this->expectException(\Psr\Log\InvalidArgumentException::class);
$this->expectExceptionMessage('LogFileId: no log file id set');
$log = new \CoreLibs\Logging\Logging([
'log_file_id' => '$$$"#"#$"#$',
'log_folder' => self::LOG_FOLDER
]);
}
// setLogUniqueId
// getLogUniqueId
/**
* Undocumented function
*
* @return array
*/
public function logUniqueIdProvider(): array
{
return [
'option set' => [
'option' => true,
'override' => false,
],
'direct set' => [
'option' => false,
'override' => false,
],
'override set' => [
'option' => false,
'override' => true,
],
'option and override set' => [
'option' => false,
'override' => true,
],
];
}
/**
* Undocumented function
*
* @covers ::setLogUniqueId
* @covers ::getLogUniqueId
* @dataProvider logUniqueIdProvider
* @testdox per run log id set test: option: $option, override: $override [$_dataName]
*
* @param bool $option
* @param bool $override
* @return void
*/
public function testLogUniqueId(bool $option, bool $override): void
{
if ($option === true) {
$log = new \CoreLibs\Logging\Logging([
'log_file_id' => 'testLogUniqueId',
'log_folder' => self::LOG_FOLDER,
'log_per_run' => $option
]);
} else {
$log = new \CoreLibs\Logging\Logging([
'log_file_id' => 'testLogUniqueId',
'log_folder' => self::LOG_FOLDER
]);
$log->setLogUniqueId();
}
$per_run_id = $log->getLogUniqueId();
$this->assertMatchesRegularExpression(
"/^\d{4}-\d{2}-\d{2}_\d{6}_U_[a-z0-9]{8}$/",
$per_run_id,
'assert per log run id 1st'
);
if ($override === true) {
$log->setLogUniqueId(true);
$per_run_id_2nd = $log->getLogUniqueId();
$this->assertMatchesRegularExpression(
"/^\d{4}-\d{2}-\d{2}_\d{6}_U_[a-z0-9]{8}$/",
$per_run_id_2nd,
'assert per log run id 2nd'
);
$this->assertNotEquals(
$per_run_id,
$per_run_id_2nd,
'1st and 2nd don\'t match'
);
}
}
// setLogDate
// getLogDate
/**
* Undocumented function
*
* @covers ::setLogDate
* @covers ::getLogDate
* @testdox setLogDate set/get checks
*
* @return void
*/
public function testSetLogDate(): void
{
$log = new \CoreLibs\Logging\Logging([
'log_file_id' => 'testLogFileId',
'log_folder' => self::LOG_FOLDER,
'log_per_date' => true,
]);
$this->assertEquals(
date('Y-m-d'),
$log->getLogDate()
);
}
// setLogFlag
// getLogFlag
// unsetLogFlag
// getLogFlags
// Logger\Flag
/**
* Undocumented function
*
* @covers Logger\Flag
* @testdox Logger\Flag enum test
*
* @return void
*/
public function testLoggerFlag(): void
{
// logger flags to check that they exist
$flags = [
'all_off' => 0,
'per_run' => 1,
'per_date' => 2,
'per_group' => 4,
'per_page' => 8,
'per_class' => 16,
'per_level' => 32,
];
// from int -> return value
foreach ($flags as $name => $value) {
$this->assertEquals(
Flag::fromName($name),
Flag::fromValue($value)
);
}
}
/**
* Undocumented function
*
* @covers ::setLogFlag
* @covers ::getLogFlag
* @covers ::unsetLogFlag
* @covers ::getLogFlags
* @testdox setLogDate set/get checks
*
* @return void
*/
public function testSetLogFlag(): void
{
$log = new \CoreLibs\Logging\Logging([
'log_file_id' => 'testSetLogFlag',
'log_folder' => self::LOG_FOLDER,
]);
// set valid log flag
$log->setLogFlag(Flag::per_run);
$this->assertTrue(
$log->getLogFlag(Flag::per_run)
);
// flags seum
$this->assertEquals(
Flag::per_run->value,
$log->getLogFlags(),
);
// unset valid log flag
$log->unsetLogFlag(Flag::per_run);
$this->assertFalse(
$log->getLogFlag(Flag::per_run)
);
// illegal Flags cannot be set, they will throw eerros onrun
// test multi set and sum is equals set
$log->setLogFlag(Flag::per_date);
$log->setLogFlag(Flag::per_group);
$this->assertEquals(
Flag::per_date->value + Flag::per_group->value,
$log->getLogFlags()
);
}
// setLogFolder
// getLogFolder
/**
* Undocumented function
*
* @covers ::setLogFolder
* @covers ::getLogFolder
* @testdox setLogFolder set/get checks, init check
*
* @return void
*/
public function testSetLogFolder(): void
{
// set to good folder
$log = new \CoreLibs\Logging\Logging([
'log_file_id' => 'testSetLogFolder',
'log_folder' => self::LOG_FOLDER,
]);
$this->assertEquals(
self::LOG_FOLDER,
$log->getLogFolder()
);
// set to a good other folder
$log->setLogFolder(DIRECTORY_SEPARATOR . 'tmp' . DIRECTORY_SEPARATOR);
$this->assertEquals(
DIRECTORY_SEPARATOR . 'tmp' . DIRECTORY_SEPARATOR,
$log->getLogFolder()
);
// good other folder with missing trailing slash
$log->setLogFolder(DIRECTORY_SEPARATOR . 'tmp');
$this->assertEquals(
DIRECTORY_SEPARATOR . 'tmp' . DIRECTORY_SEPARATOR,
$log->getLogFolder()
);
// a bad folder -> last good folder
$log->setLogFolder('I-am-not existing');
$this->assertEquals(
DIRECTORY_SEPARATOR . 'tmp' . DIRECTORY_SEPARATOR,
$log->getLogFolder()
);
// init with a bad folder
$this->expectException(\Psr\Log\InvalidArgumentException::class);
$this->expectExceptionMessage('Folder: "I-am-bad" is not writeable for logging');
$log = new \CoreLibs\Logging\Logging([
'log_file_id' => 'testSetLogFolderInvalid',
'log_folder' => 'I-am-bad',
]);
}
// getLogFile (no set, only correct after log run)
// setLogMaxFileSize
// getLogMaxFileSize
/**
* Undocumented function
*
* @covers ::setLogMaxFileSize
* @covers ::getLogMaxFileSize
* @testdox setLogMaxFileSize set/get checks, init check
*
* @return void
*/
public function testSetLogMaxFileSize(): void
{
// init set to 0
$log = new \CoreLibs\Logging\Logging([
'log_file_id' => 'testSetLogMaxFileSize',
'log_folder' => self::LOG_FOLDER,
]);
$this->assertEquals(
0,
$log->getLogMaxFileSize()
);
// set to new, valid size
$file_size = 200 * 1024;
$valid = $log->setLogMaxFileSize($file_size);
$this->assertTrue($valid);
$this->assertEquals(
$file_size,
$log->getLogMaxFileSize()
);
// invalid size, < 0, will be last and return false
$valid = $log->setLogMaxFileSize(-1);
$this->assertFalse($valid);
$this->assertEquals(
$file_size,
$log->getLogMaxFileSize()
);
// too small (< MIN_LOG_MAX_FILESIZE)
$valid = $log->setLogMaxFileSize($log::MIN_LOG_MAX_FILESIZE - 1);
$this->assertFalse($valid);
$this->assertEquals(
$file_size,
$log->getLogMaxFileSize()
);
}
// getOption (option params)
/**
* Undocumented function
*
* @covers ::getOption
* @testdox getOption checks
*
* @return void
*/
public function testGetOption(): void
{
$log = new \CoreLibs\Logging\Logging([
'log_file_id' => 'testGetOption',
'log_folder' => self::LOG_FOLDER,
]);
$this->assertEquals(
self::LOG_FOLDER,
$log->getOption('log_folder')
);
// not found
$this->assertNull(
$log->getOption('I_do not exist')
);
}
// test all logger functions
// debug (special)
// info
// notice
// warning
// error
// critical
// alert
// emergency
/**
* Undocumented function
*
* @covers ::debug
* @testdox debug checks
*
* @return void
*/
public function testDebug(): void
{
// init logger
$log = new \CoreLibs\Logging\Logging([
'log_file_id' => 'testDebug',
'log_folder' => self::LOG_FOLDER,
]);
// clean all data in folder first
array_map('unlink', glob($log->getLogFolder() . $log->getLogFileId() . '*.log'));
$group_id = 'G';
$message = 'D';
$log_status = $log->debug($group_id, $message);
$this->assertTrue($log_status, 'debug write successful');
$file_content = file_get_contents(
$log->getLogFolder() . $log->getLogFile()
) ?: '';
$log_level = $log->getLoggingLevel()->getName();
// [2023-05-30 15:51:39.36128800] [NOHOST:0]
// [www/vendor/bin/phpunit] [7b9d0747] {PHPUnit\TextUI\Command}
// <DEBUG:G> D
$this->assertMatchesRegularExpression(
"/" . self::REGEX_BASE
. "<$log_level:$group_id>\s{1}" // log level / group id
. "$message" // message
. "/",
$file_content
);
}
/**
* Undocumented function
*
* @return array
*/
public function providerLoggingLevelWrite(): array
{
return [
'info' => [
'message' => 'I',
'file_id' => Level::Info->name,
'level' => Level::Info
],
'notice' => [
'message' => 'N',
'file_id' => Level::Notice->name,
'level' => Level::Notice
],
'warning' => [
'message' => 'W',
'file_id' => Level::Warning->name,
'level' => Level::Warning
],
'error' => [
'message' => 'E',
'file_id' => Level::Error->name,
'level' => Level::Error
],
'crticial' => [
'message' => 'C',
'file_id' => Level::Critical->name,
'level' => Level::Critical
],
'alert' => [
'message' => 'A',
'file_id' => Level::Alert->name,
'level' => Level::Alert
],
'emergency' => [
'message' => 'Em',
'file_id' => Level::Emergency->name,
'level' => Level::Emergency
],
];
}
/**
* Undocumented function
*
* @covers ::info
* @covers ::notice
* @covers ::warning
* @covers ::error
* @covers ::critical
* @covers ::alert
* @covers ::emergency
* @dataProvider providerLoggingLevelWrite
* @testdox logging level write checks for $level [$_dataName]
*
* @return void
*/
public function testLoggingLevelWrite(string $message, string $file_id, Level $level): void
{
// init logger
$log = new \CoreLibs\Logging\Logging([
'log_file_id' => 'test' . $file_id,
'log_folder' => self::LOG_FOLDER,
'log_level' => $level,
]);
// clean all data in folder first
array_map('unlink', glob($log->getLogFolder() . $log->getLogFileId() . '*.log'));
switch ($level->value) {
case 200:
$log_status = $log->info($message);
break;
case 250:
$log_status = $log->notice($message);
break;
case 300:
$log_status = $log->warning($message);
break;
case 400:
$log_status = $log->error($message);
break;
case 500:
$log_status = $log->critical($message);
break;
case 550:
$log_status = $log->alert($message);
break;
case 600:
$log_status = $log->emergency($message);
break;
}
$this->assertTrue($log_status, 'log write successful');
$file_content = file_get_contents(
$log->getLogFolder() . $log->getLogFile()
) ?: '';
$log_level = $log->getLoggingLevel()->getName();
$this->assertMatchesRegularExpression(
"/" . self::REGEX_BASE
. "<$log_level>\s{1}" // log level / group id
. "$message" // message
. "/",
$file_content,
'log message regex'
);
}
// check log level that writer writes in correct level
// also that non debug ignores prefix/group
/**
* Undocumented function
*
* @covers ::log
* @testdox log() general call test
*
* @return void
*/
public function testLoggingLog(): void
{
// init logger
$log = new \CoreLibs\Logging\Logging([
'log_file_id' => 'testLoggingLog',
'log_folder' => self::LOG_FOLDER,
'log_per_level' => true,
]);
$log_ok = $log->log(Level::Debug, 'DEBUG', group_id: 'GROUP_ID', prefix: 'PREFIX:');
$this->assertTrue($log_ok, 'assert ::log (debug) OK');
$this->assertEquals(
$log->getLogFile(),
$log->getLogFileId() . '_DEBUG.log'
);
$log_ok = $log->log(Level::Info, 'INFO', group_id: 'GROUP_ID', prefix: 'PREFIX:');
$this->assertTrue($log_ok, 'assert ::log (info) OK');
$this->assertEquals(
$log->getLogFile(),
$log->getLogFileId() . '_INFO.log'
);
}
// must test flow:
// init normal
// log -> check file name
// set per date
// log -> check file name
// and same for per_run
// deprecated calls check?
}
// __END__

3
4dev/tests/Logging/log/.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
*log
*LOG
!.gitignore

View File

@@ -7,11 +7,11 @@ namespace tests;
use PHPUnit\Framework\TestCase;
/**
* Test class for Check\Password
* @coversDefaultClass \CoreLibs\Check\Password
* @testdox \CoreLibs\Check\Password method tests
* Test class for Security\Password
* @coversDefaultClass \CoreLibs\Security\Password
* @testdox \CoreLibs\Security\Password method tests
*/
final class CoreLibsCheckPasswordTest extends TestCase
final class CoreLibsSecurityPasswordTest extends TestCase
{
public function passwordProvider(): array
{
@@ -46,7 +46,7 @@ final class CoreLibsCheckPasswordTest extends TestCase
{
$this->assertEquals(
$expected,
\CoreLibs\Check\Password::passwordVerify($input, \CoreLibs\Check\Password::passwordSet($input_hash))
\CoreLibs\Security\Password::passwordVerify($input, \CoreLibs\Security\Password::passwordSet($input_hash))
);
}
@@ -65,7 +65,7 @@ final class CoreLibsCheckPasswordTest extends TestCase
{
$this->assertEquals(
$expected,
\CoreLibs\Check\Password::passwordRehashCheck($input)
\CoreLibs\Security\Password::passwordRehashCheck($input)
);
}
}

View File

@@ -0,0 +1,172 @@
<?php
declare(strict_types=1);
namespace tests;
use PHPUnit\Framework\TestCase;
use CoreLibs\Security\CreateKey;
use CoreLibs\Security\SymmetricEncryption;
/**
* Test class for Security\SymmetricEncryption and Security\CreateKey
* @coversDefaultClass \CoreLibs\Security\SymmetricEncryption
* @testdox \CoreLibs\Security\SymmetricEncryption method tests
*/
final class CoreLibsSecuritySymmetricEncryptionTest extends TestCase
{
/**
* Undocumented function
*
* @return array
*/
public function providerEncryptDecryptSuccess(): array
{
return [
'valid string' => [
'input' => 'I am a secret',
'expected' => 'I am a secret',
],
];
}
/**
* test encrypt/decrypt produce correct output
*
* @covers ::generateRandomKey
* @covers ::encrypt
* @covers ::decrypt
* @dataProvider providerEncryptDecryptSuccess
* @testdox encrypt/decrypt $input must be $expected [$_dataName]
*
* @param string $input
* @param string $expected
* @return void
*/
public function testEncryptDecryptSuccess(string $input, string $expected): void
{
$key = CreateKey::generateRandomKey();
$encrypted = SymmetricEncryption::encrypt($input, $key);
$decrypted = SymmetricEncryption::decrypt($encrypted, $key);
$this->assertEquals(
$expected,
$decrypted
);
}
/**
* Undocumented function
*
* @return array
*/
public function providerEncryptFailed(): array
{
return [
'wrong decryption key' => [
'input' => 'I am a secret',
'excpetion_message' => 'Invalid Key'
],
];
}
/**
* Test decryption with wrong key
*
* @covers ::generateRandomKey
* @covers ::encrypt
* @covers ::decrypt
* @dataProvider providerEncryptFailed
* @testdox decrypt with wrong key $input throws $exception_message [$_dataName]
*
* @param string $input
* @param string $exception_message
* @return void
*/
public function testEncryptFailed(string $input, string $exception_message): void
{
$key = CreateKey::generateRandomKey();
$encrypted = SymmetricEncryption::encrypt($input, $key);
$wrong_key = CreateKey::generateRandomKey();
$this->expectExceptionMessage($exception_message);
SymmetricEncryption::decrypt($encrypted, $wrong_key);
}
/**
* Undocumented function
*
* @return array
*/
public function providerWrongKey(): array
{
return [
'not hex key' => [
'key' => 'not_a_hex_key',
'exception_message' => 'Invalid hex key'
],
'too short hex key' => [
'key' => '1cabd5cba9e042f12522f4ff2de5c31d233b',
'excpetion_message' => 'Key is not the correct size (must be '
. 'SODIUM_CRYPTO_SECRETBOX_KEYBYTES bytes long).'
],
];
}
/**
* test invalid key provided to decrypt or encrypt
*
* @covers ::encrypt
* @covers ::decrypt
* @dataProvider providerWrongKey
* @testdox wrong key $key throws $exception_message [$_dataName]
*
* @param string $key
* @param string $exception_message
* @return void
*/
public function testWrongKey(string $key, string $exception_message): void
{
$this->expectExceptionMessage($exception_message);
SymmetricEncryption::encrypt('test', $key);
// we must encrypt valid thing first so we can fail with the wrong kjey
$enc_key = CreateKey::generateRandomKey();
$encrypted = SymmetricEncryption::encrypt('test', $enc_key);
$this->expectExceptionMessage($exception_message);
SymmetricEncryption::decrypt($encrypted, $key);
}
/**
* Undocumented function
*
* @return array
*/
public function providerWrongCiphertext(): array
{
return [
'too short ciphertext' => [
'input' => 'short',
'exception_message' => 'Invalid ciphertext (too short)'
],
];
}
/**
* Undocumented function
*
* @covers ::decrypt
* @dataProvider providerWrongCiphertext
* @testdox too short ciphertext $input throws $exception_message [$_dataName]
*
* @param string $input
* @param string $exception_message
* @return void
*/
public function testWrongCiphertext(string $input, string $exception_message): void
{
$key = CreateKey::generateRandomKey();
$this->expectExceptionMessage($exception_message);
SymmetricEncryption::decrypt($input, $key);
}
}
// __END__

View File

@@ -0,0 +1,34 @@
<?php
declare(strict_types=1);
namespace tests;
use PHPUnit\Framework\TestCase;
use CoreLibs\Template\HtmlBuilder\Block;
/**
* Test class for Template\HtmlBuilder\Block
* @coversDefaultClass \CoreLibs\Template\HtmlBuilder\Block
* @testdox \CoreLibs\Template\HtmlBuilder\Block method tests
*/
final class CoreLibsTemplateHtmlBuilderBlockTest extends TestCase
{
public function testCreateBlock(): void
{
$el = Block::cel('div', 'id', 'content', ['css'], ['onclick' => 'foo();']);
$this->assertEquals(
'<div id="id" class="css" onclick="foo();">content</div>',
Block::buildHtml($el)
);
}
// ael
// aelx|addSub
// resetSub
// acssel/rcssel/scssel
// buildHtml
// buildHtmlFromList|printHtmlFromArray
}
// __END__

View File

@@ -0,0 +1,546 @@
<?php
declare(strict_types=1);
namespace tests;
use PHPUnit\Framework\TestCase;
use CoreLibs\Template\HtmlBuilder\Element;
use CoreLibs\Template\HtmlBuilder\General\Error;
use CoreLibs\Template\HtmlBuilder\General\HtmlBuilderExcpetion;
/**
* Test class for Template\HtmlBuilder\Element
* @coversDefaultClass \CoreLibs\Template\HtmlBuilder\Element
* @testdox \CoreLibs\Template\HtmlBuilder\Element method tests
*/
final class CoreLibsTemplateHtmlBuilderElementTest extends TestCase
{
public function providerCreateElements(): array
{
return [
'simple div' => [
'tag' => 'div',
'id' => 'id',
'content' => 'content',
'css' => ['css'],
'options' => ['onclick' => 'foo();'],
'expected' => '<div id="id" class="css" onclick="foo();">content</div>'
],
'simple input' => [
'tag' => 'input',
'id' => 'id',
'content' => null,
'css' => ['css'],
'options' => ['name' => 'name', 'onclick' => 'foo();'],
'expected' => '<input id="id" name="name" class="css" onclick="foo();">'
]
];
}
/**
* Undocumented function
*
* @covers ::Element
* @covers ::buildHtml
* @covers ::getTag
* @covers ::getId
* @covers ::getContent
* @covers ::getOptions
* @covers ::getCss
* @dataProvider providerCreateElements
* @testdox create single new Element test [$_dataName]
*
* @param string $tag
* @param string|null $id
* @param string|null $content
* @param array|null $css
* @param array|null $options
* @param string $expected
* @return void
*/
public function testCreateElement(
string $tag,
?string $id,
?string $content,
?array $css,
?array $options,
string $expected
): void {
$el = new Element($tag, $id ?? '', $content ?? '', $css ?? [], $options ?? []);
$this->assertEquals(
$expected,
$el->buildHtml(),
'element creation failed'
);
$this->assertEquals(
$tag,
$el->getTag(),
'get tag failed'
);
if ($id !== null) {
$this->assertEquals(
$id,
$el->getId(),
'get id failed'
);
}
if ($content !== null) {
$this->assertEquals(
$content,
$el->getContent(),
'get content failed'
);
}
if ($css !== null) {
$this->assertEquals(
$css,
$el->getCss(),
'get css failed'
);
}
if ($options !== null) {
$this->assertEquals(
$options,
$el->getOptions(),
'get options failed'
);
}
if (!empty($options['name'])) {
$this->assertEquals(
$options['name'],
$el->getName(),
'get name failed'
);
}
}
/**
* css add/remove
*
* @cover ::getCss
* @cover ::addCss
* @cover ::removeCss
* @testdox test handling of adding and removing css classes
*
* @return void
*/
public function testCssHandling(): void
{
$el = new Element('div', 'css-test', 'CSS content');
$this->assertEqualsCanonicalizing(
[],
$el->getCss(),
'check empty css'
);
$el->addCss('foo');
$this->assertEqualsCanonicalizing(
['foo'],
$el->getCss(),
'check added one css'
);
$el->removeCss('foo');
$this->assertEqualsCanonicalizing(
[],
$el->getCss(),
'check remove added css'
);
// add serveral
// remove some of them
$el->addCss('a', 'b', 'c');
$this->assertEqualsCanonicalizing(
['a', 'b', 'c'],
$el->getCss(),
'check added some css'
);
$el->removeCss('a', 'c');
// $this->assertArray
$this->assertEqualsCanonicalizing(
['b'],
$el->getCss(),
'check remove some css'
);
// chained add and remove
$el->addCss('a', 'b', 'c', 'd')->removeCss('b', 'd');
$this->assertEqualsCanonicalizing(
['a', 'c'],
$el->getCss(),
'check chain add remove some css'
);
$el->resetCss();
$this->assertEqualsCanonicalizing(
[],
$el->getCss(),
'check reset css'
);
// remove something that does not eixst
$el->addCss('exists');
$el->removeCss('not');
$this->assertEqualsCanonicalizing(
['exists'],
$el->getCss(),
'check remove not exitsing'
);
}
/**
* nested test
*
* @testdox nested test and loop assign detection
*
* @return void
*/
public function testBuildNested(): void
{
Error::resetMessages();
$el = new Element('div', 'build-test');
$el_sub = new Element('div', 'sub-1');
$el->addSub($el_sub);
$this->assertEquals(
'<div id="build-test"><div id="sub-1"></div></div>',
$el->buildHtml(),
'nested build failed'
);
// this would create a loop, throws error
$this->expectException(HtmlBuilderExcpetion::class);
$this->expectExceptionMessage("Cannot assign Element to itself, this would create an infinite loop");
$el_sub->addSub($el_sub);
$this->assertEquals(
'<div id="sub-1"></div>',
$el_sub->buildHtml(),
'loop detection failed'
);
$this->assertTrue(
Error::hasError(),
'failed to throw error post loop detection'
);
$this->assertEquals(
[[
'level' => 'Error',
'id' => '100',
'message' => 'Cannot assign Element to itself, this would create an infinite loop',
'context' => ['tag' => 'div', 'id' => 'sub-1']
]],
Error::getMessages(),
'check error is 100 failed'
);
// get sub
$this->assertEquals(
[$el_sub],
$el->getSub(),
'get sub failed'
);
// reset sub
$el->resetSub();
$this->assertEquals(
[],
$el->getSub(),
'reset sub failed'
);
}
/**
* Undocumented function
*
* @testdox updated nested connection
*
* @return void
*/
public function testNestedChangeContent(): void
{
$el = new Element('div', 'build-test');
$el_s_1 = new Element('div', 'sub-1');
$el_s_2 = new Element('div', 'sub-2');
$el_s_3 = new Element('div', 'sub-3');
$el_s_4 = new Element('div', 'sub-4');
$el->addSub($el_s_1, $el_s_2);
// only sub -1, -2
$this->assertEquals(
'<div id="build-test"><div id="sub-1"></div><div id="sub-2"></div></div>',
$el->buildHtml(),
'check basic nested'
);
// now add -3, -4 to both -1 and -2
$el_s_1->addSub($el_s_3, $el_s_4);
$el_s_2->addSub($el_s_3, $el_s_4);
$this->assertEquals(
'<div id="build-test"><div id="sub-1"><div id="sub-3"></div><div id="sub-4">'
. '</div></div><div id="sub-2"><div id="sub-3"></div><div id="sub-4"></div>'
. '</div></div>',
$el->buildHtml(),
'check nested added'
);
// now add some css to el_s_3, will update in both sets
$el_s_3->addCss('red');
$this->assertEquals(
'<div id="build-test"><div id="sub-1"><div id="sub-3" class="red"></div><div id="sub-4">'
. '</div></div><div id="sub-2"><div id="sub-3" class="red"></div><div id="sub-4"></div>'
. '</div></div>',
$el->buildHtml(),
'check nested u@dated'
);
}
/**
* Undocumented function
*
* @testdox test change tag/id/content
*
* @return void
*/
public function testChangeElementData(): void
{
$el = new Element('div', 'id', 'Content');
// content change
$this->assertEquals(
'Content',
$el->getContent(),
'set content'
);
$el->setContent('New Content');
$this->assertEquals(
'New Content',
$el->getContent(),
'changed content'
);
$this->assertEquals(
'div',
$el->getTag(),
'set tag'
);
$el->setTag('span');
$this->assertEquals(
'span',
$el->getTag(),
'changed tag'
);
$this->assertEquals(
'id',
$el->getId(),
'set id'
);
$el->setId('id-2');
$this->assertEquals(
'id-2',
$el->getId(),
'changed id'
);
}
/**
* Undocumented function
*
* @testdox test change options
*
* @return void
*/
public function testChangeOptions(): void
{
$el = new Element('button', 'id', 'Action', ['css'], ['value' => '3']);
$this->assertEquals(
['value' => '3'],
$el->getOptions(),
'set option'
);
$el->setOptions([
'value' => '2'
]);
$this->assertEquals(
['value' => '2'],
$el->getOptions(),
'changed option'
);
// add a new one
$el->setOptions([
'Foo' => 'bar',
'Moo' => 'cow'
]);
$this->assertEquals(
[
'value' => '2',
'Foo' => 'bar',
'Moo' => 'cow'
],
$el->getOptions(),
'changed option'
);
}
// build output
// build from array list
/**
* Undocumented function
*
* @testdox build element tree from object
*
* @return void
*/
public function testBuildHtmlObject(): void
{
// build a simple block
// div -> div -> button
// -> div -> span
// -> div -> input
$el = new Element('div', 'master', '', ['master']);
$el->addSub(
Element::addElement(
new Element('div', 'div-button', '', ['dv-bt']),
new Element('button', 'button-id', 'Click me', ['bt-red'], [
'OnClick' => 'action();',
'value' => 'click',
'type' => 'button'
]),
),
Element::addElement(
new Element('div', 'div-span', '', ['dv-sp']),
new Element('span', 'span-id', 'Big important message<br>other', ['red']),
),
Element::addElement(
new Element('div', 'div-input', '', ['dv-in']),
new Element('input', 'input-id', '', ['in-blue'], [
'OnClick' => 'otherAction();',
'value' => 'Touch',
'type' => 'button'
]),
),
);
$this->assertEquals(
'<div id="master" class="master">'
. '<div id="div-button" class="dv-bt">'
. '<button id="button-id" name="button-id" class="bt-red" OnClick="action();" '
. 'value="click" type="button">Click me</button>'
. '</div>'
. '<div id="div-span" class="dv-sp">'
. '<span id="span-id" class="red">Big important message<br>other</span>'
. '</div>'
. '<div id="div-input" class="dv-in">'
. '<input id="input-id" name="input-id" '
. 'class="in-blue" OnClick="otherAction();" value="Touch" type="button">'
. '</div>'
. '</div>',
$el->buildHtml()
);
}
/**
* Undocumented function
*
* @testdox build elements from array list
*
* @return void
*/
public function testbuildHtmlArray(): void
{
$this->assertEquals(
'<div id="id-1">A</div>'
. '<div id="id-2">B</div>'
. '<div id="id-3">C</div>',
Element::buildHtmlFromList([
new Element('div', 'id-1', 'A'),
new Element('div', 'id-2', 'B'),
new Element('div', 'id-3', 'C'),
])
);
}
/**
* Undocumented function
*
* @testdox check for invalid tag detection, possible invalid id, possible invalid css
*
* @return void
*/
public function testInvalidElement(): void
{
Error::resetMessages();
$this->expectException(HtmlBuilderExcpetion::class);
$this->expectExceptionMessage("Could not create Element");
$el = new Element('');
$this->assertTrue(
Error::hasError(),
'failed to set error invalid tag detection'
);
$this->assertEquals(
[[
'level' => 'Error',
'id' => '201',
'message' => 'invalid or empty tag',
'context' => ['tag' => '']
]],
Error::getMessages(),
'check error message failed'
);
// if we set invalid tag
$el = new Element('div');
$this->expectException(HtmlBuilderExcpetion::class);
$this->expectExceptionMessageMatches("/^Invalid or empty tag: /");
$this->expectExceptionMessage("Invalid or empty tag: 123123");
$el->setTag('123123');
$this->assertTrue(
Error::hasError(),
'failed to set error invalid tag detection'
);
$this->assertEquals(
[[
'level' => 'Error',
'id' => '201',
'message' => 'invalid or empty tag',
'context' => ['tag' => '']
]],
Error::getMessages(),
'check error message failed'
);
// invalid id (warning)
Error::resetMessages();
$el = new Element('div', '-$a15');
$this->assertTrue(
Error::hasWarning(),
'failed to set warning invalid id detection'
);
$this->assertEquals(
[[
'level' => 'Warning',
'id' => '202',
'message' => 'possible invalid id',
'context' => ['id' => '-$a15', 'tag' => 'div']
]],
Error::getMessages(),
'check error message failed'
);
// invalid name
Error::resetMessages();
$el = new Element('div', 'valid', '', [], ['name' => '-$asdf&']);
$this->assertTrue(
Error::hasWarning(),
'failed to set warning invalid name detection'
);
$this->assertEquals(
[[
'level' => 'Warning',
'id' => '203',
'message' => 'possible invalid name',
'context' => ['name' => '-$asdf&', 'id' => 'valid', 'tag' => 'div']
]],
Error::getMessages(),
'check error message failed'
);
}
// static add element
// print object/array
}
// __END__

View File

@@ -0,0 +1,65 @@
<?php
declare(strict_types=1);
namespace tests;
use PHPUnit\Framework\TestCase;
use CoreLibs\Template\HtmlBuilder\StringReplace;
/**
* Test class for Template\HtmlBuilder\StringReplace
* @coversDefaultClass \CoreLibs\Template\HtmlBuilder\StringReplace
* @testdox \CoreLibs\Template\HtmlBuilder\StringReplace method tests
*/
final class CoreLibsTemplateHtmlBuilderStringReplaceTest extends TestCase
{
/**
* Undocumented function
*
* @covers ::replaceData
* @testdox test basic replaceData
*
* @return void
*/
public function testReplaceData(): void
{
$html_block = <<<HTML
<div id="{ID}" class="{CSS}">
{CONTENT}
</div>
HTML;
$this->assertEquals(
<<<HTML
<div id="block-id" class="blue,red">
Some content here<br>with bla bla inside
</div>
HTML,
StringReplace::replaceData(
$html_block,
[
'ID' => 'block-id',
'CSS' => join(',', ['blue', 'red']),
'{CONTENT}' => 'Some content here<br>with bla bla inside',
]
)
);
}
/**
* Undocumented function
*
* @testdox replaceData error
*
* @return void
*/
/* public function testReplaceDataErrors(): void
{
$this->expectException(HtmlBuilderExcpetion::class);
$this->expectExceptionMessage("Replace and content array count differ");
StringReplace::replaceData('<span>{FOO}</span>', ['{FOO}', '{BAR}'], ['foo']);
} */
}
// __END__

View File

@@ -16,6 +16,7 @@
}
},
"require": {
"php": ">=8.1"
"php": ">=8.1",
"psr/log": "^2.0 || ^3.0"
}
}

241
composer.lock generated
View File

@@ -4,8 +4,59 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "c633a27ea30371ec870c8065ca4ae4cd",
"packages": [],
"content-hash": "9a62d2bdd387b7a6f599b27964325845",
"packages": [
{
"name": "psr/log",
"version": "3.0.0",
"source": {
"type": "git",
"url": "https://github.com/php-fig/log.git",
"reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/log/zipball/fe5ea303b0887d5caefd3d431c3e61ad47037001",
"reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001",
"shasum": ""
},
"require": {
"php": ">=8.0.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.x-dev"
}
},
"autoload": {
"psr-4": {
"Psr\\Log\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PHP-FIG",
"homepage": "https://www.php-fig.org/"
}
],
"description": "Common interface for logging libraries",
"homepage": "https://github.com/php-fig/log",
"keywords": [
"log",
"psr",
"psr-3"
],
"support": {
"source": "https://github.com/php-fig/log/tree/3.0.0"
},
"time": "2021-07-14T16:46:02+00:00"
}
],
"packages-dev": [
{
"name": "amphp/amp",
@@ -680,16 +731,16 @@
},
{
"name": "netresearch/jsonmapper",
"version": "v4.1.0",
"version": "v4.2.0",
"source": {
"type": "git",
"url": "https://github.com/cweiske/jsonmapper.git",
"reference": "cfa81ea1d35294d64adb9c68aa4cb9e92400e53f"
"reference": "f60565f8c0566a31acf06884cdaa591867ecc956"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/cweiske/jsonmapper/zipball/cfa81ea1d35294d64adb9c68aa4cb9e92400e53f",
"reference": "cfa81ea1d35294d64adb9c68aa4cb9e92400e53f",
"url": "https://api.github.com/repos/cweiske/jsonmapper/zipball/f60565f8c0566a31acf06884cdaa591867ecc956",
"reference": "f60565f8c0566a31acf06884cdaa591867ecc956",
"shasum": ""
},
"require": {
@@ -725,22 +776,22 @@
"support": {
"email": "cweiske@cweiske.de",
"issues": "https://github.com/cweiske/jsonmapper/issues",
"source": "https://github.com/cweiske/jsonmapper/tree/v4.1.0"
"source": "https://github.com/cweiske/jsonmapper/tree/v4.2.0"
},
"time": "2022-12-08T20:46:14+00:00"
"time": "2023-04-09T17:37:40+00:00"
},
{
"name": "nikic/php-parser",
"version": "v4.15.4",
"version": "v4.15.5",
"source": {
"type": "git",
"url": "https://github.com/nikic/PHP-Parser.git",
"reference": "6bb5176bc4af8bcb7d926f88718db9b96a2d4290"
"reference": "11e2663a5bc9db5d714eedb4277ee300403b4a9e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/6bb5176bc4af8bcb7d926f88718db9b96a2d4290",
"reference": "6bb5176bc4af8bcb7d926f88718db9b96a2d4290",
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/11e2663a5bc9db5d714eedb4277ee300403b4a9e",
"reference": "11e2663a5bc9db5d714eedb4277ee300403b4a9e",
"shasum": ""
},
"require": {
@@ -781,9 +832,9 @@
],
"support": {
"issues": "https://github.com/nikic/PHP-Parser/issues",
"source": "https://github.com/nikic/PHP-Parser/tree/v4.15.4"
"source": "https://github.com/nikic/PHP-Parser/tree/v4.15.5"
},
"time": "2023-03-05T19:49:14+00:00"
"time": "2023-05-19T20:20:00+00:00"
},
{
"name": "phan/phan",
@@ -1034,22 +1085,22 @@
},
{
"name": "phpstan/extension-installer",
"version": "1.2.0",
"version": "1.3.1",
"source": {
"type": "git",
"url": "https://github.com/phpstan/extension-installer.git",
"reference": "f06dbb052ddc394e7896fcd1cfcd533f9f6ace40"
"reference": "f45734bfb9984c6c56c4486b71230355f066a58a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/extension-installer/zipball/f06dbb052ddc394e7896fcd1cfcd533f9f6ace40",
"reference": "f06dbb052ddc394e7896fcd1cfcd533f9f6ace40",
"url": "https://api.github.com/repos/phpstan/extension-installer/zipball/f45734bfb9984c6c56c4486b71230355f066a58a",
"reference": "f45734bfb9984c6c56c4486b71230355f066a58a",
"shasum": ""
},
"require": {
"composer-plugin-api": "^2.0",
"php": "^7.2 || ^8.0",
"phpstan/phpstan": "^1.8.0"
"phpstan/phpstan": "^1.9.0"
},
"require-dev": {
"composer/composer": "^2.0",
@@ -1072,28 +1123,29 @@
"description": "Composer plugin for automatic installation of PHPStan extensions",
"support": {
"issues": "https://github.com/phpstan/extension-installer/issues",
"source": "https://github.com/phpstan/extension-installer/tree/1.2.0"
"source": "https://github.com/phpstan/extension-installer/tree/1.3.1"
},
"time": "2022-10-17T12:59:16+00:00"
"time": "2023-05-24T08:59:17+00:00"
},
{
"name": "phpstan/phpdoc-parser",
"version": "1.16.1",
"version": "1.21.0",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpdoc-parser.git",
"reference": "e27e92d939e2e3636f0a1f0afaba59692c0bf571"
"reference": "6df62b08faef4f899772bc7c3bbabb93d2b7a21c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/e27e92d939e2e3636f0a1f0afaba59692c0bf571",
"reference": "e27e92d939e2e3636f0a1f0afaba59692c0bf571",
"url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/6df62b08faef4f899772bc7c3bbabb93d2b7a21c",
"reference": "6df62b08faef4f899772bc7c3bbabb93d2b7a21c",
"shasum": ""
},
"require": {
"php": "^7.2 || ^8.0"
},
"require-dev": {
"nikic/php-parser": "^4.15",
"php-parallel-lint/php-parallel-lint": "^1.2",
"phpstan/extension-installer": "^1.0",
"phpstan/phpstan": "^1.5",
@@ -1117,22 +1169,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.16.1"
"source": "https://github.com/phpstan/phpdoc-parser/tree/1.21.0"
},
"time": "2023-02-07T18:11:17+00:00"
"time": "2023-05-17T13:13:44+00:00"
},
{
"name": "phpstan/phpstan",
"version": "1.10.8",
"version": "1.10.15",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpstan.git",
"reference": "0166aef76e066f0dd2adc2799bdadfa1635711e9"
"reference": "762c4dac4da6f8756eebb80e528c3a47855da9bd"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/0166aef76e066f0dd2adc2799bdadfa1635711e9",
"reference": "0166aef76e066f0dd2adc2799bdadfa1635711e9",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/762c4dac4da6f8756eebb80e528c3a47855da9bd",
"reference": "762c4dac4da6f8756eebb80e528c3a47855da9bd",
"shasum": ""
},
"require": {
@@ -1181,7 +1233,7 @@
"type": "tidelift"
}
],
"time": "2023-03-24T10:28:16+00:00"
"time": "2023-05-09T15:28:01+00:00"
},
{
"name": "phpstan/phpstan-deprecation-rules",
@@ -1284,56 +1336,6 @@
},
"time": "2021-11-05T16:47:00+00:00"
},
{
"name": "psr/log",
"version": "3.0.0",
"source": {
"type": "git",
"url": "https://github.com/php-fig/log.git",
"reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/log/zipball/fe5ea303b0887d5caefd3d431c3e61ad47037001",
"reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001",
"shasum": ""
},
"require": {
"php": ">=8.0.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.x-dev"
}
},
"autoload": {
"psr-4": {
"Psr\\Log\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PHP-FIG",
"homepage": "https://www.php-fig.org/"
}
],
"description": "Common interface for logging libraries",
"homepage": "https://github.com/php-fig/log",
"keywords": [
"log",
"psr",
"psr-3"
],
"support": {
"source": "https://github.com/php-fig/log/tree/3.0.0"
},
"time": "2021-07-14T16:46:02+00:00"
},
{
"name": "sabre/event",
"version": "5.1.4",
@@ -1402,16 +1404,16 @@
},
{
"name": "sebastian/diff",
"version": "5.0.1",
"version": "5.0.3",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/diff.git",
"reference": "aae9a0a43bff37bd5d8d0311426c87bf36153f02"
"reference": "912dc2fbe3e3c1e7873313cc801b100b6c68c87b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/aae9a0a43bff37bd5d8d0311426c87bf36153f02",
"reference": "aae9a0a43bff37bd5d8d0311426c87bf36153f02",
"url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/912dc2fbe3e3c1e7873313cc801b100b6c68c87b",
"reference": "912dc2fbe3e3c1e7873313cc801b100b6c68c87b",
"shasum": ""
},
"require": {
@@ -1457,7 +1459,7 @@
"support": {
"issues": "https://github.com/sebastianbergmann/diff/issues",
"security": "https://github.com/sebastianbergmann/diff/security/policy",
"source": "https://github.com/sebastianbergmann/diff/tree/5.0.1"
"source": "https://github.com/sebastianbergmann/diff/tree/5.0.3"
},
"funding": [
{
@@ -1465,20 +1467,20 @@
"type": "github"
}
],
"time": "2023-03-23T05:12:41+00:00"
"time": "2023-05-01T07:48:21+00:00"
},
{
"name": "spatie/array-to-xml",
"version": "3.1.5",
"version": "3.1.6",
"source": {
"type": "git",
"url": "https://github.com/spatie/array-to-xml.git",
"reference": "13f76acef5362d15c71ae1ac6350cc3df5e25e43"
"reference": "e210b98957987c755372465be105d32113f339a4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/spatie/array-to-xml/zipball/13f76acef5362d15c71ae1ac6350cc3df5e25e43",
"reference": "13f76acef5362d15c71ae1ac6350cc3df5e25e43",
"url": "https://api.github.com/repos/spatie/array-to-xml/zipball/e210b98957987c755372465be105d32113f339a4",
"reference": "e210b98957987c755372465be105d32113f339a4",
"shasum": ""
},
"require": {
@@ -1516,7 +1518,7 @@
"xml"
],
"support": {
"source": "https://github.com/spatie/array-to-xml/tree/3.1.5"
"source": "https://github.com/spatie/array-to-xml/tree/3.1.6"
},
"funding": [
{
@@ -1528,20 +1530,20 @@
"type": "github"
}
],
"time": "2022-12-24T13:43:51+00:00"
"time": "2023-05-11T14:04:07+00:00"
},
{
"name": "symfony/console",
"version": "v6.2.7",
"version": "v6.2.11",
"source": {
"type": "git",
"url": "https://github.com/symfony/console.git",
"reference": "cbad09eb8925b6ad4fb721c7a179344dc4a19d45"
"reference": "5aa03db8ef0a5457c316ec580e69562d97734c77"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/console/zipball/cbad09eb8925b6ad4fb721c7a179344dc4a19d45",
"reference": "cbad09eb8925b6ad4fb721c7a179344dc4a19d45",
"url": "https://api.github.com/repos/symfony/console/zipball/5aa03db8ef0a5457c316ec580e69562d97734c77",
"reference": "5aa03db8ef0a5457c316ec580e69562d97734c77",
"shasum": ""
},
"require": {
@@ -1603,12 +1605,12 @@
"homepage": "https://symfony.com",
"keywords": [
"cli",
"command line",
"command-line",
"console",
"terminal"
],
"support": {
"source": "https://github.com/symfony/console/tree/v6.2.7"
"source": "https://github.com/symfony/console/tree/v6.2.11"
},
"funding": [
{
@@ -1624,7 +1626,7 @@
"type": "tidelift"
}
],
"time": "2023-02-25T17:00:03+00:00"
"time": "2023-05-26T08:16:21+00:00"
},
{
"name": "symfony/deprecation-contracts",
@@ -1695,16 +1697,16 @@
},
{
"name": "symfony/filesystem",
"version": "v6.2.7",
"version": "v6.2.10",
"source": {
"type": "git",
"url": "https://github.com/symfony/filesystem.git",
"reference": "82b6c62b959f642d000456f08c6d219d749215b3"
"reference": "fd588debf7d1bc16a2c84b4b3b71145d9946b894"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/filesystem/zipball/82b6c62b959f642d000456f08c6d219d749215b3",
"reference": "82b6c62b959f642d000456f08c6d219d749215b3",
"url": "https://api.github.com/repos/symfony/filesystem/zipball/fd588debf7d1bc16a2c84b4b3b71145d9946b894",
"reference": "fd588debf7d1bc16a2c84b4b3b71145d9946b894",
"shasum": ""
},
"require": {
@@ -1738,7 +1740,7 @@
"description": "Provides basic utilities for the filesystem",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/filesystem/tree/v6.2.7"
"source": "https://github.com/symfony/filesystem/tree/v6.2.10"
},
"funding": [
{
@@ -1754,7 +1756,7 @@
"type": "tidelift"
}
],
"time": "2023-02-14T08:44:56+00:00"
"time": "2023-04-18T13:46:08+00:00"
},
{
"name": "symfony/polyfill-ctype",
@@ -2256,16 +2258,16 @@
},
{
"name": "symfony/string",
"version": "v6.2.7",
"version": "v6.2.8",
"source": {
"type": "git",
"url": "https://github.com/symfony/string.git",
"reference": "67b8c1eec78296b85dc1c7d9743830160218993d"
"reference": "193e83bbd6617d6b2151c37fff10fa7168ebddef"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/string/zipball/67b8c1eec78296b85dc1c7d9743830160218993d",
"reference": "67b8c1eec78296b85dc1c7d9743830160218993d",
"url": "https://api.github.com/repos/symfony/string/zipball/193e83bbd6617d6b2151c37fff10fa7168ebddef",
"reference": "193e83bbd6617d6b2151c37fff10fa7168ebddef",
"shasum": ""
},
"require": {
@@ -2322,7 +2324,7 @@
"utf8"
],
"support": {
"source": "https://github.com/symfony/string/tree/v6.2.7"
"source": "https://github.com/symfony/string/tree/v6.2.8"
},
"funding": [
{
@@ -2338,7 +2340,7 @@
"type": "tidelift"
}
],
"time": "2023-02-24T10:42:00+00:00"
"time": "2023-03-20T16:06:02+00:00"
},
{
"name": "tysonandre/var_representation_polyfill",
@@ -2404,16 +2406,16 @@
},
{
"name": "vimeo/psalm",
"version": "5.8.0",
"version": "5.12.0",
"source": {
"type": "git",
"url": "https://github.com/vimeo/psalm.git",
"reference": "9cf4f60a333f779ad3bc704a555920e81d4fdcda"
"reference": "f90118cdeacd0088e7215e64c0c99ceca819e176"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/vimeo/psalm/zipball/9cf4f60a333f779ad3bc704a555920e81d4fdcda",
"reference": "9cf4f60a333f779ad3bc704a555920e81d4fdcda",
"url": "https://api.github.com/repos/vimeo/psalm/zipball/f90118cdeacd0088e7215e64c0c99ceca819e176",
"reference": "f90118cdeacd0088e7215e64c0c99ceca819e176",
"shasum": ""
},
"require": {
@@ -2445,6 +2447,7 @@
"psalm/psalm": "self.version"
},
"require-dev": {
"amphp/phpunit-util": "^2.0",
"bamarni/composer-bin-plugin": "^1.4",
"brianium/paratest": "^6.9",
"ext-curl": "*",
@@ -2503,9 +2506,9 @@
],
"support": {
"issues": "https://github.com/vimeo/psalm/issues",
"source": "https://github.com/vimeo/psalm/tree/5.8.0"
"source": "https://github.com/vimeo/psalm/tree/5.12.0"
},
"time": "2023-03-09T04:14:35+00:00"
"time": "2023-05-22T21:19:03+00:00"
},
{
"name": "webmozart/assert",

View File

@@ -39,9 +39,9 @@ parameters:
- www/vendor
# ignore errores with
ignoreErrors:
- # in the class_test tree we allow deprecated calls
message: "#^Call to deprecated method #"
path: %currentWorkingDirectory%/www/admin/class_test.*.php
# - # in the class_test tree we allow deprecated calls
# message: "#^Call to deprecated method #"
# path: %currentWorkingDirectory%/www/admin/class_test.*.php
# - '#Expression in empty\(\) is always falsy.#'
# -
# message: '#Reflection error: [a-zA-Z0-9\\_]+ not found.#'

View File

@@ -706,17 +706,17 @@
},
{
"name": "netresearch/jsonmapper",
"version": "v4.1.0",
"version_normalized": "4.1.0.0",
"version": "v4.2.0",
"version_normalized": "4.2.0.0",
"source": {
"type": "git",
"url": "https://github.com/cweiske/jsonmapper.git",
"reference": "cfa81ea1d35294d64adb9c68aa4cb9e92400e53f"
"reference": "f60565f8c0566a31acf06884cdaa591867ecc956"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/cweiske/jsonmapper/zipball/cfa81ea1d35294d64adb9c68aa4cb9e92400e53f",
"reference": "cfa81ea1d35294d64adb9c68aa4cb9e92400e53f",
"url": "https://api.github.com/repos/cweiske/jsonmapper/zipball/f60565f8c0566a31acf06884cdaa591867ecc956",
"reference": "f60565f8c0566a31acf06884cdaa591867ecc956",
"shasum": ""
},
"require": {
@@ -730,7 +730,7 @@
"phpunit/phpunit": "~7.5 || ~8.0 || ~9.0",
"squizlabs/php_codesniffer": "~3.5"
},
"time": "2022-12-08T20:46:14+00:00",
"time": "2023-04-09T17:37:40+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
@@ -754,23 +754,23 @@
"support": {
"email": "cweiske@cweiske.de",
"issues": "https://github.com/cweiske/jsonmapper/issues",
"source": "https://github.com/cweiske/jsonmapper/tree/v4.1.0"
"source": "https://github.com/cweiske/jsonmapper/tree/v4.2.0"
},
"install-path": "../netresearch/jsonmapper"
},
{
"name": "nikic/php-parser",
"version": "v4.15.4",
"version_normalized": "4.15.4.0",
"version": "v4.15.5",
"version_normalized": "4.15.5.0",
"source": {
"type": "git",
"url": "https://github.com/nikic/PHP-Parser.git",
"reference": "6bb5176bc4af8bcb7d926f88718db9b96a2d4290"
"reference": "11e2663a5bc9db5d714eedb4277ee300403b4a9e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/6bb5176bc4af8bcb7d926f88718db9b96a2d4290",
"reference": "6bb5176bc4af8bcb7d926f88718db9b96a2d4290",
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/11e2663a5bc9db5d714eedb4277ee300403b4a9e",
"reference": "11e2663a5bc9db5d714eedb4277ee300403b4a9e",
"shasum": ""
},
"require": {
@@ -781,7 +781,7 @@
"ircmaxell/php-yacc": "^0.0.7",
"phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0"
},
"time": "2023-03-05T19:49:14+00:00",
"time": "2023-05-19T20:20:00+00:00",
"bin": [
"bin/php-parse"
],
@@ -813,7 +813,7 @@
],
"support": {
"issues": "https://github.com/nikic/PHP-Parser/issues",
"source": "https://github.com/nikic/PHP-Parser/tree/v4.15.4"
"source": "https://github.com/nikic/PHP-Parser/tree/v4.15.5"
},
"install-path": "../nikic/php-parser"
},
@@ -1078,30 +1078,30 @@
},
{
"name": "phpstan/extension-installer",
"version": "1.2.0",
"version_normalized": "1.2.0.0",
"version": "1.3.1",
"version_normalized": "1.3.1.0",
"source": {
"type": "git",
"url": "https://github.com/phpstan/extension-installer.git",
"reference": "f06dbb052ddc394e7896fcd1cfcd533f9f6ace40"
"reference": "f45734bfb9984c6c56c4486b71230355f066a58a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/extension-installer/zipball/f06dbb052ddc394e7896fcd1cfcd533f9f6ace40",
"reference": "f06dbb052ddc394e7896fcd1cfcd533f9f6ace40",
"url": "https://api.github.com/repos/phpstan/extension-installer/zipball/f45734bfb9984c6c56c4486b71230355f066a58a",
"reference": "f45734bfb9984c6c56c4486b71230355f066a58a",
"shasum": ""
},
"require": {
"composer-plugin-api": "^2.0",
"php": "^7.2 || ^8.0",
"phpstan/phpstan": "^1.8.0"
"phpstan/phpstan": "^1.9.0"
},
"require-dev": {
"composer/composer": "^2.0",
"php-parallel-lint/php-parallel-lint": "^1.2.0",
"phpstan/phpstan-strict-rules": "^0.11 || ^0.12 || ^1.0"
},
"time": "2022-10-17T12:59:16+00:00",
"time": "2023-05-24T08:59:17+00:00",
"type": "composer-plugin",
"extra": {
"class": "PHPStan\\ExtensionInstaller\\Plugin"
@@ -1119,29 +1119,30 @@
"description": "Composer plugin for automatic installation of PHPStan extensions",
"support": {
"issues": "https://github.com/phpstan/extension-installer/issues",
"source": "https://github.com/phpstan/extension-installer/tree/1.2.0"
"source": "https://github.com/phpstan/extension-installer/tree/1.3.1"
},
"install-path": "../phpstan/extension-installer"
},
{
"name": "phpstan/phpdoc-parser",
"version": "1.16.1",
"version_normalized": "1.16.1.0",
"version": "1.21.0",
"version_normalized": "1.21.0.0",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpdoc-parser.git",
"reference": "e27e92d939e2e3636f0a1f0afaba59692c0bf571"
"reference": "6df62b08faef4f899772bc7c3bbabb93d2b7a21c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/e27e92d939e2e3636f0a1f0afaba59692c0bf571",
"reference": "e27e92d939e2e3636f0a1f0afaba59692c0bf571",
"url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/6df62b08faef4f899772bc7c3bbabb93d2b7a21c",
"reference": "6df62b08faef4f899772bc7c3bbabb93d2b7a21c",
"shasum": ""
},
"require": {
"php": "^7.2 || ^8.0"
},
"require-dev": {
"nikic/php-parser": "^4.15",
"php-parallel-lint/php-parallel-lint": "^1.2",
"phpstan/extension-installer": "^1.0",
"phpstan/phpstan": "^1.5",
@@ -1150,7 +1151,7 @@
"phpunit/phpunit": "^9.5",
"symfony/process": "^5.2"
},
"time": "2023-02-07T18:11:17+00:00",
"time": "2023-05-17T13:13:44+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
@@ -1167,23 +1168,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.16.1"
"source": "https://github.com/phpstan/phpdoc-parser/tree/1.21.0"
},
"install-path": "../phpstan/phpdoc-parser"
},
{
"name": "phpstan/phpstan",
"version": "1.10.8",
"version_normalized": "1.10.8.0",
"version": "1.10.15",
"version_normalized": "1.10.15.0",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpstan.git",
"reference": "0166aef76e066f0dd2adc2799bdadfa1635711e9"
"reference": "762c4dac4da6f8756eebb80e528c3a47855da9bd"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/0166aef76e066f0dd2adc2799bdadfa1635711e9",
"reference": "0166aef76e066f0dd2adc2799bdadfa1635711e9",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/762c4dac4da6f8756eebb80e528c3a47855da9bd",
"reference": "762c4dac4da6f8756eebb80e528c3a47855da9bd",
"shasum": ""
},
"require": {
@@ -1192,7 +1193,7 @@
"conflict": {
"phpstan/phpstan-shim": "*"
},
"time": "2023-03-24T10:28:16+00:00",
"time": "2023-05-09T15:28:01+00:00",
"bin": [
"phpstan",
"phpstan.phar"
@@ -1467,17 +1468,17 @@
},
{
"name": "sebastian/diff",
"version": "5.0.1",
"version_normalized": "5.0.1.0",
"version": "5.0.3",
"version_normalized": "5.0.3.0",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/diff.git",
"reference": "aae9a0a43bff37bd5d8d0311426c87bf36153f02"
"reference": "912dc2fbe3e3c1e7873313cc801b100b6c68c87b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/aae9a0a43bff37bd5d8d0311426c87bf36153f02",
"reference": "aae9a0a43bff37bd5d8d0311426c87bf36153f02",
"url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/912dc2fbe3e3c1e7873313cc801b100b6c68c87b",
"reference": "912dc2fbe3e3c1e7873313cc801b100b6c68c87b",
"shasum": ""
},
"require": {
@@ -1487,7 +1488,7 @@
"phpunit/phpunit": "^10.0",
"symfony/process": "^4.2 || ^5"
},
"time": "2023-03-23T05:12:41+00:00",
"time": "2023-05-01T07:48:21+00:00",
"type": "library",
"extra": {
"branch-alias": {
@@ -1525,7 +1526,7 @@
"support": {
"issues": "https://github.com/sebastianbergmann/diff/issues",
"security": "https://github.com/sebastianbergmann/diff/security/policy",
"source": "https://github.com/sebastianbergmann/diff/tree/5.0.1"
"source": "https://github.com/sebastianbergmann/diff/tree/5.0.3"
},
"funding": [
{
@@ -1537,17 +1538,17 @@
},
{
"name": "spatie/array-to-xml",
"version": "3.1.5",
"version_normalized": "3.1.5.0",
"version": "3.1.6",
"version_normalized": "3.1.6.0",
"source": {
"type": "git",
"url": "https://github.com/spatie/array-to-xml.git",
"reference": "13f76acef5362d15c71ae1ac6350cc3df5e25e43"
"reference": "e210b98957987c755372465be105d32113f339a4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/spatie/array-to-xml/zipball/13f76acef5362d15c71ae1ac6350cc3df5e25e43",
"reference": "13f76acef5362d15c71ae1ac6350cc3df5e25e43",
"url": "https://api.github.com/repos/spatie/array-to-xml/zipball/e210b98957987c755372465be105d32113f339a4",
"reference": "e210b98957987c755372465be105d32113f339a4",
"shasum": ""
},
"require": {
@@ -1559,7 +1560,7 @@
"pestphp/pest": "^1.21",
"spatie/pest-plugin-snapshots": "^1.1"
},
"time": "2022-12-24T13:43:51+00:00",
"time": "2023-05-11T14:04:07+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
@@ -1587,7 +1588,7 @@
"xml"
],
"support": {
"source": "https://github.com/spatie/array-to-xml/tree/3.1.5"
"source": "https://github.com/spatie/array-to-xml/tree/3.1.6"
},
"funding": [
{
@@ -1603,17 +1604,17 @@
},
{
"name": "symfony/console",
"version": "v6.2.7",
"version_normalized": "6.2.7.0",
"version": "v6.2.11",
"version_normalized": "6.2.11.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/console.git",
"reference": "cbad09eb8925b6ad4fb721c7a179344dc4a19d45"
"reference": "5aa03db8ef0a5457c316ec580e69562d97734c77"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/console/zipball/cbad09eb8925b6ad4fb721c7a179344dc4a19d45",
"reference": "cbad09eb8925b6ad4fb721c7a179344dc4a19d45",
"url": "https://api.github.com/repos/symfony/console/zipball/5aa03db8ef0a5457c316ec580e69562d97734c77",
"reference": "5aa03db8ef0a5457c316ec580e69562d97734c77",
"shasum": ""
},
"require": {
@@ -1648,7 +1649,7 @@
"symfony/lock": "",
"symfony/process": ""
},
"time": "2023-02-25T17:00:03+00:00",
"time": "2023-05-26T08:16:21+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
@@ -1677,12 +1678,12 @@
"homepage": "https://symfony.com",
"keywords": [
"cli",
"command line",
"command-line",
"console",
"terminal"
],
"support": {
"source": "https://github.com/symfony/console/tree/v6.2.7"
"source": "https://github.com/symfony/console/tree/v6.2.11"
},
"funding": [
{
@@ -1772,17 +1773,17 @@
},
{
"name": "symfony/filesystem",
"version": "v6.2.7",
"version_normalized": "6.2.7.0",
"version": "v6.2.10",
"version_normalized": "6.2.10.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/filesystem.git",
"reference": "82b6c62b959f642d000456f08c6d219d749215b3"
"reference": "fd588debf7d1bc16a2c84b4b3b71145d9946b894"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/filesystem/zipball/82b6c62b959f642d000456f08c6d219d749215b3",
"reference": "82b6c62b959f642d000456f08c6d219d749215b3",
"url": "https://api.github.com/repos/symfony/filesystem/zipball/fd588debf7d1bc16a2c84b4b3b71145d9946b894",
"reference": "fd588debf7d1bc16a2c84b4b3b71145d9946b894",
"shasum": ""
},
"require": {
@@ -1790,7 +1791,7 @@
"symfony/polyfill-ctype": "~1.8",
"symfony/polyfill-mbstring": "~1.8"
},
"time": "2023-02-14T08:44:56+00:00",
"time": "2023-04-18T13:46:08+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
@@ -1818,7 +1819,7 @@
"description": "Provides basic utilities for the filesystem",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/filesystem/tree/v6.2.7"
"source": "https://github.com/symfony/filesystem/tree/v6.2.10"
},
"funding": [
{
@@ -2354,17 +2355,17 @@
},
{
"name": "symfony/string",
"version": "v6.2.7",
"version_normalized": "6.2.7.0",
"version": "v6.2.8",
"version_normalized": "6.2.8.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/string.git",
"reference": "67b8c1eec78296b85dc1c7d9743830160218993d"
"reference": "193e83bbd6617d6b2151c37fff10fa7168ebddef"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/string/zipball/67b8c1eec78296b85dc1c7d9743830160218993d",
"reference": "67b8c1eec78296b85dc1c7d9743830160218993d",
"url": "https://api.github.com/repos/symfony/string/zipball/193e83bbd6617d6b2151c37fff10fa7168ebddef",
"reference": "193e83bbd6617d6b2151c37fff10fa7168ebddef",
"shasum": ""
},
"require": {
@@ -2384,7 +2385,7 @@
"symfony/translation-contracts": "^2.0|^3.0",
"symfony/var-exporter": "^5.4|^6.0"
},
"time": "2023-02-24T10:42:00+00:00",
"time": "2023-03-20T16:06:02+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
@@ -2423,7 +2424,7 @@
"utf8"
],
"support": {
"source": "https://github.com/symfony/string/tree/v6.2.7"
"source": "https://github.com/symfony/string/tree/v6.2.8"
},
"funding": [
{
@@ -2508,17 +2509,17 @@
},
{
"name": "vimeo/psalm",
"version": "5.8.0",
"version_normalized": "5.8.0.0",
"version": "5.12.0",
"version_normalized": "5.12.0.0",
"source": {
"type": "git",
"url": "https://github.com/vimeo/psalm.git",
"reference": "9cf4f60a333f779ad3bc704a555920e81d4fdcda"
"reference": "f90118cdeacd0088e7215e64c0c99ceca819e176"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/vimeo/psalm/zipball/9cf4f60a333f779ad3bc704a555920e81d4fdcda",
"reference": "9cf4f60a333f779ad3bc704a555920e81d4fdcda",
"url": "https://api.github.com/repos/vimeo/psalm/zipball/f90118cdeacd0088e7215e64c0c99ceca819e176",
"reference": "f90118cdeacd0088e7215e64c0c99ceca819e176",
"shasum": ""
},
"require": {
@@ -2550,6 +2551,7 @@
"psalm/psalm": "self.version"
},
"require-dev": {
"amphp/phpunit-util": "^2.0",
"bamarni/composer-bin-plugin": "^1.4",
"brianium/paratest": "^6.9",
"ext-curl": "*",
@@ -2568,7 +2570,7 @@
"ext-curl": "In order to send data to shepherd",
"ext-igbinary": "^2.0.5 is required, used to serialize caching data"
},
"time": "2023-03-09T04:14:35+00:00",
"time": "2023-05-22T21:19:03+00:00",
"bin": [
"psalm",
"psalm-language-server",
@@ -2610,7 +2612,7 @@
],
"support": {
"issues": "https://github.com/vimeo/psalm/issues",
"source": "https://github.com/vimeo/psalm/tree/5.8.0"
"source": "https://github.com/vimeo/psalm/tree/5.12.0"
},
"install-path": "../vimeo/psalm"
},
@@ -2700,7 +2702,6 @@
"phpstan/phpstan",
"phpstan/phpstan-deprecation-rules",
"psr/container",
"psr/log",
"sabre/event",
"sebastian/diff",
"spatie/array-to-xml",

View File

@@ -119,18 +119,18 @@
'dev_requirement' => true,
),
'netresearch/jsonmapper' => array(
'pretty_version' => 'v4.1.0',
'version' => '4.1.0.0',
'reference' => 'cfa81ea1d35294d64adb9c68aa4cb9e92400e53f',
'pretty_version' => 'v4.2.0',
'version' => '4.2.0.0',
'reference' => 'f60565f8c0566a31acf06884cdaa591867ecc956',
'type' => 'library',
'install_path' => __DIR__ . '/../netresearch/jsonmapper',
'aliases' => array(),
'dev_requirement' => true,
),
'nikic/php-parser' => array(
'pretty_version' => 'v4.15.4',
'version' => '4.15.4.0',
'reference' => '6bb5176bc4af8bcb7d926f88718db9b96a2d4290',
'pretty_version' => 'v4.15.5',
'version' => '4.15.5.0',
'reference' => '11e2663a5bc9db5d714eedb4277ee300403b4a9e',
'type' => 'library',
'install_path' => __DIR__ . '/../nikic/php-parser',
'aliases' => array(),
@@ -173,27 +173,27 @@
'dev_requirement' => true,
),
'phpstan/extension-installer' => array(
'pretty_version' => '1.2.0',
'version' => '1.2.0.0',
'reference' => 'f06dbb052ddc394e7896fcd1cfcd533f9f6ace40',
'pretty_version' => '1.3.1',
'version' => '1.3.1.0',
'reference' => 'f45734bfb9984c6c56c4486b71230355f066a58a',
'type' => 'composer-plugin',
'install_path' => __DIR__ . '/../phpstan/extension-installer',
'aliases' => array(),
'dev_requirement' => true,
),
'phpstan/phpdoc-parser' => array(
'pretty_version' => '1.16.1',
'version' => '1.16.1.0',
'reference' => 'e27e92d939e2e3636f0a1f0afaba59692c0bf571',
'pretty_version' => '1.21.0',
'version' => '1.21.0.0',
'reference' => '6df62b08faef4f899772bc7c3bbabb93d2b7a21c',
'type' => 'library',
'install_path' => __DIR__ . '/../phpstan/phpdoc-parser',
'aliases' => array(),
'dev_requirement' => true,
),
'phpstan/phpstan' => array(
'pretty_version' => '1.10.8',
'version' => '1.10.8.0',
'reference' => '0166aef76e066f0dd2adc2799bdadfa1635711e9',
'pretty_version' => '1.10.15',
'version' => '1.10.15.0',
'reference' => '762c4dac4da6f8756eebb80e528c3a47855da9bd',
'type' => 'library',
'install_path' => __DIR__ . '/../phpstan/phpstan',
'aliases' => array(),
@@ -211,7 +211,7 @@
'psalm/psalm' => array(
'dev_requirement' => true,
'provided' => array(
0 => '5.8.0',
0 => '5.12.0',
),
),
'psr/container' => array(
@@ -230,7 +230,7 @@
'type' => 'library',
'install_path' => __DIR__ . '/../psr/log',
'aliases' => array(),
'dev_requirement' => true,
'dev_requirement' => false,
),
'psr/log-implementation' => array(
'dev_requirement' => true,
@@ -248,27 +248,27 @@
'dev_requirement' => true,
),
'sebastian/diff' => array(
'pretty_version' => '5.0.1',
'version' => '5.0.1.0',
'reference' => 'aae9a0a43bff37bd5d8d0311426c87bf36153f02',
'pretty_version' => '5.0.3',
'version' => '5.0.3.0',
'reference' => '912dc2fbe3e3c1e7873313cc801b100b6c68c87b',
'type' => 'library',
'install_path' => __DIR__ . '/../sebastian/diff',
'aliases' => array(),
'dev_requirement' => true,
),
'spatie/array-to-xml' => array(
'pretty_version' => '3.1.5',
'version' => '3.1.5.0',
'reference' => '13f76acef5362d15c71ae1ac6350cc3df5e25e43',
'pretty_version' => '3.1.6',
'version' => '3.1.6.0',
'reference' => 'e210b98957987c755372465be105d32113f339a4',
'type' => 'library',
'install_path' => __DIR__ . '/../spatie/array-to-xml',
'aliases' => array(),
'dev_requirement' => true,
),
'symfony/console' => array(
'pretty_version' => 'v6.2.7',
'version' => '6.2.7.0',
'reference' => 'cbad09eb8925b6ad4fb721c7a179344dc4a19d45',
'pretty_version' => 'v6.2.11',
'version' => '6.2.11.0',
'reference' => '5aa03db8ef0a5457c316ec580e69562d97734c77',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/console',
'aliases' => array(),
@@ -284,9 +284,9 @@
'dev_requirement' => true,
),
'symfony/filesystem' => array(
'pretty_version' => 'v6.2.7',
'version' => '6.2.7.0',
'reference' => '82b6c62b959f642d000456f08c6d219d749215b3',
'pretty_version' => 'v6.2.10',
'version' => '6.2.10.0',
'reference' => 'fd588debf7d1bc16a2c84b4b3b71145d9946b894',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/filesystem',
'aliases' => array(),
@@ -347,9 +347,9 @@
'dev_requirement' => true,
),
'symfony/string' => array(
'pretty_version' => 'v6.2.7',
'version' => '6.2.7.0',
'reference' => '67b8c1eec78296b85dc1c7d9743830160218993d',
'pretty_version' => 'v6.2.8',
'version' => '6.2.8.0',
'reference' => '193e83bbd6617d6b2151c37fff10fa7168ebddef',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/string',
'aliases' => array(),
@@ -365,9 +365,9 @@
'dev_requirement' => true,
),
'vimeo/psalm' => array(
'pretty_version' => '5.8.0',
'version' => '5.8.0.0',
'reference' => '9cf4f60a333f779ad3bc704a555920e81d4fdcda',
'pretty_version' => '5.12.0',
'version' => '5.12.0.0',
'reference' => 'f90118cdeacd0088e7215e64c0c99ceca819e176',
'type' => 'library',
'install_path' => __DIR__ . '/../vimeo/psalm',
'aliases' => array(),

View File

@@ -0,0 +1,46 @@
name: JsonMapper tests
on: [push, workflow_dispatch]
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
php-versions:
- '7.1'
- '7.2'
- '7.3'
- '7.4'
- '8.0'
- '8.1'
- '8.2'
name: PHP ${{ matrix.php-versions }}
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Install PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-versions }}
tools: composer
coverage: xdebug
- name: Get composer cache directory
id: composer-cache
run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
- name: Cache dependencies
uses: actions/cache@v3
with:
path: ${{ steps.composer-cache.outputs.dir }}
key: ${{ runner.os }}-composer-${{ matrix.php-versions }}-${{ hashFiles('**/composer.lock') }}
restore-keys: ${{ runner.os }}-composer-${{ matrix.php-versions }}-
- name: Install dependencies
run: composer install --no-interaction --prefer-dist
- name: Run unit tests
run: XDEBUG_MODE=coverage ./vendor/bin/phpunit --coverage-text
- name: Check codestyle
run: ./vendor/bin/phpcs --standard=PEAR src/

View File

@@ -75,7 +75,7 @@ class JsonMapper
public $bStrictNullTypes = true;
/**
* Allow mapping of private and proteted properties.
* Allow mapping of private and protected properties.
*
* @var boolean
*/
@@ -131,8 +131,8 @@ class JsonMapper
/**
* Map data all data in $json into the given $object instance.
*
* @param object|array $json JSON object structure from json_decode()
* @param object $object Object to map $json data into
* @param object|array $json JSON object structure from json_decode()
* @param object|class-string $object Object to map $json data into
*
* @return mixed Mapped object is returned.
* @see mapArray()
@@ -145,13 +145,18 @@ class JsonMapper
. ', ' . gettype($json) . ' given.'
);
}
if (!is_object($object)) {
if (!is_object($object) && (!is_string($object) || !class_exists($object))) {
throw new InvalidArgumentException(
'JsonMapper::map() requires second argument to be an object'
'JsonMapper::map() requires second argument to '
. 'be an object or existing class name'
. ', ' . gettype($object) . ' given.'
);
}
if (is_string($object)) {
$object = $this->createInstance($object);
}
$strClassName = get_class($object);
$rc = new ReflectionClass($object);
$strNs = $rc->getNamespaceName();
@@ -177,10 +182,15 @@ class JsonMapper
. ' in object of type ' . $strClassName
);
} else if ($this->undefinedPropertyHandler !== null) {
call_user_func(
$undefinedPropertyKey = call_user_func(
$this->undefinedPropertyHandler,
$object, $key, $jvalue
);
if (is_string($undefinedPropertyKey)) {
list($hasProperty, $accessor, $type, $isNullable)
= $this->inspectProperty($rc, $undefinedPropertyKey);
}
} else {
$this->log(
'info',
@@ -188,7 +198,10 @@ class JsonMapper
array('property' => $key, 'class' => $strClassName)
);
}
continue;
if (!$hasProperty) {
continue;
}
}
if ($accessor === null) {
@@ -229,7 +242,9 @@ class JsonMapper
} else if ($this->isObjectOfSameType($type, $jvalue)) {
$this->setProperty($object, $accessor, $jvalue);
continue;
} else if ($this->isSimpleType($type)) {
} else if ($this->isSimpleType($type)
&& !(is_array($jvalue) && $this->hasVariadicArrayType($accessor))
) {
if ($type === 'string' && is_object($jvalue)) {
throw new JsonMapper_Exception(
'JSON property "' . $key . '" in class "'
@@ -268,8 +283,11 @@ class JsonMapper
} else {
$array = $this->createInstance($proptype, false, $jvalue);
}
} else if (is_array($jvalue) && $this->hasVariadicArrayType($accessor)) {
$array = array();
$subtype = $type;
} else {
if (is_a($type, 'ArrayObject', true)) {
if (is_a($type, 'ArrayAccess', true)) {
$array = $this->createInstance($type, false, $jvalue);
}
}
@@ -625,6 +643,8 @@ class JsonMapper
}
if ($accessor instanceof ReflectionProperty) {
$accessor->setValue($object, $value);
} else if (is_array($value) && $this->hasVariadicArrayType($accessor)) {
$accessor->invoke($object, ...$value);
} else {
//setter method
$accessor->invoke($object, $value);
@@ -647,6 +667,12 @@ class JsonMapper
$class, $useParameter = false, $jvalue = null
) {
if ($useParameter) {
if (PHP_VERSION_ID >= 80100
&& is_subclass_of($class, \BackedEnum::class)
) {
return $class::from($jvalue);
}
return new $class($jvalue);
} else {
$reflectClass = new ReflectionClass($class);
@@ -758,6 +784,32 @@ class JsonMapper
return substr($strType, -2) === '[]';
}
/**
* Returns true if accessor is a method and has only one parameter
* which is variadic.
*
* @param ReflectionMethod|ReflectionProperty|null $accessor accessor
* to set value
*
* @return bool
*/
protected function hasVariadicArrayType($accessor)
{
if (!$accessor instanceof ReflectionMethod) {
return false;
}
$parameters = $accessor->getParameters();
if (count($parameters) !== 1) {
return false;
}
$parameter = $parameters[0];
return $parameter->isVariadic();
}
/**
* Checks if the given type is nullable
*

View File

@@ -19,6 +19,8 @@ class Param implements PhpParser\Builder
protected $variadic = false;
protected $flags = 0;
/** @var Node\AttributeGroup[] */
protected $attributeGroups = [];
@@ -95,6 +97,50 @@ class Param implements PhpParser\Builder
return $this;
}
/**
* Makes the (promoted) parameter public.
*
* @return $this The builder instance (for fluid interface)
*/
public function makePublic() {
$this->flags = BuilderHelpers::addModifier($this->flags, Node\Stmt\Class_::MODIFIER_PUBLIC);
return $this;
}
/**
* Makes the (promoted) parameter protected.
*
* @return $this The builder instance (for fluid interface)
*/
public function makeProtected() {
$this->flags = BuilderHelpers::addModifier($this->flags, Node\Stmt\Class_::MODIFIER_PROTECTED);
return $this;
}
/**
* Makes the (promoted) parameter private.
*
* @return $this The builder instance (for fluid interface)
*/
public function makePrivate() {
$this->flags = BuilderHelpers::addModifier($this->flags, Node\Stmt\Class_::MODIFIER_PRIVATE);
return $this;
}
/**
* Makes the (promoted) parameter readonly.
*
* @return $this The builder instance (for fluid interface)
*/
public function makeReadonly() {
$this->flags = BuilderHelpers::addModifier($this->flags, Node\Stmt\Class_::MODIFIER_READONLY);
return $this;
}
/**
* Adds an attribute group.
*
@@ -116,7 +162,7 @@ class Param implements PhpParser\Builder
public function getNode() : Node {
return new Node\Param(
new Node\Expr\Variable($this->name),
$this->default, $this->type, $this->byRef, $this->variadic, [], 0, $this->attributeGroups
$this->default, $this->type, $this->byRef, $this->variadic, [], $this->flags, $this->attributeGroups
);
}
}

View File

@@ -72,6 +72,22 @@ Add `phpstan` key in the extension `composer.json`'s `extra` section:
}
```
## Ignoring a particular extension
You may want to disable auto-installation of a particular extension to handle installation manually. Ignore an extension by adding an `extra.phpstan/extension-installer.ignore` array in `composer.json` that specifies a list of packages to ignore:
```json
{
"extra": {
"phpstan/extension-installer": {
"ignore": [
"phpstan/phpstan-phpunit"
]
}
}
}
```
## Limitations
The extension installer depends on Composer script events, therefore you cannot use `--no-scripts` flag.

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

@@ -8,7 +8,7 @@
"require": {
"php": "^7.2 || ^8.0",
"composer-plugin-api": "^2.0",
"phpstan/phpstan": "^1.8.0"
"phpstan/phpstan": "^1.9.0"
},
"require-dev": {
"composer/composer": "^2.0",

View File

@@ -3,21 +3,30 @@
namespace PHPStan\ExtensionInstaller;
/**
* This is a stub class: it is in place only for scenarios where Composer
* is run with a `--no-scripts` flag, in which scenarios this stub class
* is not being replaced.
*
* If you are reading this docBlock inside your `vendor/` dir, then this means
* that phpstan/extension-installer didn't correctly install.
*
* This class is generated by phpstan/extension-installer.
* @internal
*/
final class GeneratedConfig
{
public const EXTENSIONS = [];
public const EXTENSIONS = array (
'phpstan/phpstan-deprecation-rules' =>
array (
'install_path' => '/storage/var/www/html/developers/clemens/core_data/php_libraries/trunk/vendor/phpstan/phpstan-deprecation-rules',
'relative_install_path' => '../../phpstan-deprecation-rules',
'extra' =>
array (
'includes' =>
array (
0 => 'rules.neon',
),
),
'version' => '1.1.3',
),
);
public const NOT_INSTALLED = [];
public const NOT_INSTALLED = array (
);
private function __construct()
{

View File

@@ -19,6 +19,7 @@ use function is_file;
use function ksort;
use function md5;
use function md5_file;
use function sort;
use function sprintf;
use function strpos;
use function var_export;
@@ -97,9 +98,18 @@ PHP;
}
$notInstalledPackages = [];
$installedPackages = [];
$ignoredPackages = [];
$data = [];
$fs = new Filesystem();
$ignore = [];
$packageExtra = $composer->getPackage()->getExtra();
if (isset($packageExtra['phpstan/extension-installer']['ignore'])) {
$ignore = $packageExtra['phpstan/extension-installer']['ignore'];
}
foreach ($composer->getRepositoryManager()->getLocalRepository()->getPackages() as $package) {
if (
$package->getType() !== 'phpstan-extension'
@@ -119,7 +129,15 @@ PHP;
continue;
}
if (in_array($package->getName(), $ignore, true)) {
$ignoredPackages[] = $package->getName();
continue;
}
$installPath = $installationManager->getInstallPath($package);
if ($installPath === null) {
continue;
}
$absoluteInstallPath = $fs->isAbsolutePath($installPath)
? $installPath
@@ -138,6 +156,7 @@ PHP;
ksort($data);
ksort($installedPackages);
ksort($notInstalledPackages);
sort($ignoredPackages);
$generatedConfigFileContents = sprintf(self::$generatedFileTemplate, var_export($data, true), var_export($notInstalledPackages, true));
file_put_contents($generatedConfigFilePath, $generatedConfigFileContents);
@@ -154,6 +173,10 @@ PHP;
foreach (array_keys($notInstalledPackages) as $name) {
$io->write(sprintf('> <comment>%s:</comment> not supported', $name));
}
foreach ($ignoredPackages as $name) {
$io->write(sprintf('> <comment>%s:</comment> ignored', $name));
}
}
}

View File

@@ -1,4 +1,4 @@
<h1 align="center">PHPDoc-Parser for PHPStan</h1>
<h1 align="center">PHPDoc Parser for PHPStan</h1>
<p align="center">
<a href="https://github.com/phpstan/phpdoc-parser/actions"><img src="https://github.com/phpstan/phpdoc-parser/workflows/Build/badge.svg" alt="Build Status"></a>
@@ -7,11 +7,99 @@
<a href="https://phpstan.org/"><img src="https://img.shields.io/badge/PHPStan-enabled-brightgreen.svg?style=flat" alt="PHPStan Enabled"></a>
</p>
* [PHPStan](https://phpstan.org)
This library `phpstan/phpdoc-parser` represents PHPDocs with an AST (Abstract Syntax Tree). It supports parsing and modifying PHPDocs.
------
For the complete list of supported PHPDoc features check out PHPStan documentation. PHPStan is the main (but not the only) user of this library.
Next generation phpDoc parser with support for intersection types and generics.
* [PHPDoc Basics](https://phpstan.org/writing-php-code/phpdocs-basics) (list of PHPDoc tags)
* [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.
## Installation
```
composer require phpstan/phpdoc-parser
```
## Basic usage
```php
<?php
require_once __DIR__ . '/vendor/autoload.php';
use PHPStan\PhpDocParser\Ast\PhpDoc\ParamTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocNode;
use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
use PHPStan\PhpDocParser\Lexer\Lexer;
use PHPStan\PhpDocParser\Parser\ConstExprParser;
use PHPStan\PhpDocParser\Parser\PhpDocParser;
use PHPStan\PhpDocParser\Parser\TokenIterator;
use PHPStan\PhpDocParser\Parser\TypeParser;
// basic setup
$lexer = new Lexer();
$constExprParser = new ConstExprParser();
$typeParser = new TypeParser($constExprParser);
$phpDocParser = new PhpDocParser($typeParser, $constExprParser);
// parsing and reading a PHPDoc string
$tokens = new TokenIterator($lexer->tokenize('/** @param Lorem $a */'));
$phpDocNode = $phpDocParser->parse($tokens); // PhpDocNode
$paramTags = $phpDocNode->getParamTagValues(); // ParamTagValueNode[]
echo $paramTags[0]->parameterName; // '$a'
echo $paramTags[0]->type; // IdentifierTypeNode - 'Lorem'
```
### Format-preserving printer
This component can be used to modify the AST
and print it again as close as possible to the original.
It's heavily inspired by format-preserving printer component in [nikic/PHP-Parser](https://github.com/nikic/PHP-Parser).
```php
<?php
require_once __DIR__ . '/vendor/autoload.php';
use PHPStan\PhpDocParser\Ast\NodeTraverser;
use PHPStan\PhpDocParser\Ast\NodeVisitor\CloningVisitor;
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocNode;
use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
use PHPStan\PhpDocParser\Lexer\Lexer;
use PHPStan\PhpDocParser\Parser\ConstExprParser;
use PHPStan\PhpDocParser\Parser\PhpDocParser;
use PHPStan\PhpDocParser\Parser\TokenIterator;
use PHPStan\PhpDocParser\Parser\TypeParser;
use PHPStan\PhpDocParser\Printer\Printer;
// basic setup with enabled required lexer attributes
$usedAttributes = ['lines' => true, 'indexes' => true];
$lexer = new Lexer();
$constExprParser = new ConstExprParser(true, true, $usedAttributes);
$typeParser = new TypeParser($constExprParser, true, $usedAttributes);
$phpDocParser = new PhpDocParser($typeParser, $constExprParser, true, true, $usedAttributes);
$tokens = new TokenIterator($lexer->tokenize('/** @param Lorem $a */'));
$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');
$newPhpDoc = $printer->printFormatPreserving($newPhpDocNode, $phpDocNode, $tokens);
echo $newPhpDoc; // '/** @param Ipsum $a */'
```
## Code of Conduct

View File

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

View File

@@ -0,0 +1,31 @@
parameters:
ignoreErrors:
-
message: "#^Method PHPStan\\\\PhpDocParser\\\\Ast\\\\ConstExpr\\\\QuoteAwareConstExprStringNode\\:\\:escapeDoubleQuotedString\\(\\) should return string but returns string\\|null\\.$#"
count: 1
path: src/Ast/ConstExpr/QuoteAwareConstExprStringNode.php
-
message: "#^Cannot use array destructuring on array\\<int, array\\<PHPStan\\\\PhpDocParser\\\\Ast\\\\Node\\>\\|int\\|string\\>\\|null\\.$#"
count: 1
path: src/Ast/NodeTraverser.php
-
message: "#^Strict comparison using \\=\\=\\= between 2 and 2 will always evaluate to true\\.$#"
count: 2
path: src/Ast/NodeTraverser.php
-
message: "#^Variable property access on PHPStan\\\\PhpDocParser\\\\Ast\\\\Node\\.$#"
count: 1
path: src/Ast/NodeTraverser.php
-
message: "#^Method PHPStan\\\\PhpDocParser\\\\Parser\\\\StringUnescaper\\:\\:parseEscapeSequences\\(\\) should return string but returns string\\|null\\.$#"
count: 1
path: src/Parser/StringUnescaper.php
-
message: "#^Variable property access on PHPStan\\\\PhpDocParser\\\\Ast\\\\Node\\.$#"
count: 2
path: src/Printer/Printer.php

View File

@@ -0,0 +1,34 @@
<?php declare(strict_types = 1);
namespace PHPStan\PhpDocParser\Ast;
/**
* Inspired by https://github.com/nikic/PHP-Parser/tree/36a6dcd04e7b0285e8f0868f44bd4927802f7df1
*
* Copyright (c) 2011, Nikita Popov
* All rights reserved.
*/
abstract class AbstractNodeVisitor implements NodeVisitor
{
public function beforeTraverse(array $nodes): ?array
{
return null;
}
public function enterNode(Node $node)
{
return null;
}
public function leaveNode(Node $node)
{
return null;
}
public function afterTraverse(array $nodes): ?array
{
return null;
}
}

View File

@@ -0,0 +1,16 @@
<?php declare(strict_types = 1);
namespace PHPStan\PhpDocParser\Ast;
final class Attribute
{
public const START_LINE = 'startLine';
public const END_LINE = 'endLine';
public const START_INDEX = 'startIndex';
public const END_INDEX = 'endIndex';
public const ORIGINAL_NODE = 'originalNode';
}

View File

@@ -0,0 +1,78 @@
<?php declare(strict_types = 1);
namespace PHPStan\PhpDocParser\Ast\ConstExpr;
use PHPStan\PhpDocParser\Ast\NodeAttributes;
use function addcslashes;
use function assert;
use function dechex;
use function ord;
use function preg_replace_callback;
use function sprintf;
use function str_pad;
use function strlen;
use const STR_PAD_LEFT;
class QuoteAwareConstExprStringNode extends ConstExprStringNode implements ConstExprNode
{
public const SINGLE_QUOTED = 1;
public const DOUBLE_QUOTED = 2;
use NodeAttributes;
/** @var self::SINGLE_QUOTED|self::DOUBLE_QUOTED */
public $quoteType;
/**
* @param self::SINGLE_QUOTED|self::DOUBLE_QUOTED $quoteType
*/
public function __construct(string $value, int $quoteType)
{
parent::__construct($value);
$this->quoteType = $quoteType;
}
public function __toString(): string
{
if ($this->quoteType === self::SINGLE_QUOTED) {
// from https://github.com/nikic/PHP-Parser/blob/0ffddce52d816f72d0efc4d9b02e276d3309ef01/lib/PhpParser/PrettyPrinter/Standard.php#L1007
return sprintf("'%s'", addcslashes($this->value, '\'\\'));
}
// from https://github.com/nikic/PHP-Parser/blob/0ffddce52d816f72d0efc4d9b02e276d3309ef01/lib/PhpParser/PrettyPrinter/Standard.php#L1010-L1040
return sprintf('"%s"', $this->escapeDoubleQuotedString());
}
private function escapeDoubleQuotedString(): string
{
$quote = '"';
$escaped = addcslashes($this->value, "\n\r\t\f\v$" . $quote . '\\');
// Escape control characters and non-UTF-8 characters.
// Regex based on https://stackoverflow.com/a/11709412/385378.
$regex = '/(
[\x00-\x08\x0E-\x1F] # Control characters
| [\xC0-\xC1] # Invalid UTF-8 Bytes
| [\xF5-\xFF] # Invalid UTF-8 Bytes
| \xE0(?=[\x80-\x9F]) # Overlong encoding of prior code point
| \xF0(?=[\x80-\x8F]) # Overlong encoding of prior code point
| [\xC2-\xDF](?![\x80-\xBF]) # Invalid UTF-8 Sequence Start
| [\xE0-\xEF](?![\x80-\xBF]{2}) # Invalid UTF-8 Sequence Start
| [\xF0-\xF4](?![\x80-\xBF]{3}) # Invalid UTF-8 Sequence Start
| (?<=[\x00-\x7F\xF5-\xFF])[\x80-\xBF] # Invalid UTF-8 Sequence Middle
| (?<![\xC2-\xDF]|[\xE0-\xEF]|[\xE0-\xEF][\x80-\xBF]|[\xF0-\xF4]|[\xF0-\xF4][\x80-\xBF]|[\xF0-\xF4][\x80-\xBF]{2})[\x80-\xBF] # Overlong Sequence
| (?<=[\xE0-\xEF])[\x80-\xBF](?![\x80-\xBF]) # Short 3 byte sequence
| (?<=[\xF0-\xF4])[\x80-\xBF](?![\x80-\xBF]{2}) # Short 4 byte sequence
| (?<=[\xF0-\xF4][\x80-\xBF])[\x80-\xBF](?![\x80-\xBF]) # Short 4 byte sequence (2)
)/x';
return preg_replace_callback($regex, static function ($matches) {
assert(strlen($matches[0]) === 1);
$hex = dechex(ord($matches[0]));
return '\\x' . str_pad($hex, 2, '0', STR_PAD_LEFT);
}, $escaped);
}
}

View File

@@ -0,0 +1,312 @@
<?php declare(strict_types = 1);
namespace PHPStan\PhpDocParser\Ast;
use LogicException;
use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocChildNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagValueNode;
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
use function array_keys;
use function array_pop;
use function array_splice;
use function count;
use function get_class;
use function get_object_vars;
use function gettype;
use function is_array;
use function sprintf;
/**
* Inspired by https://github.com/nikic/PHP-Parser/tree/36a6dcd04e7b0285e8f0868f44bd4927802f7df1
*
* Copyright (c) 2011, Nikita Popov
* All rights reserved.
*/
final class NodeTraverser
{
/**
* If NodeVisitor::enterNode() returns DONT_TRAVERSE_CHILDREN, child nodes
* of the current node will not be traversed for any visitors.
*
* For subsequent visitors enterNode() will still be called on the current
* node and leaveNode() will also be invoked for the current node.
*/
public const DONT_TRAVERSE_CHILDREN = 1;
/**
* If NodeVisitor::enterNode() or NodeVisitor::leaveNode() returns
* STOP_TRAVERSAL, traversal is aborted.
*
* The afterTraverse() method will still be invoked.
*/
public const STOP_TRAVERSAL = 2;
/**
* If NodeVisitor::leaveNode() returns REMOVE_NODE for a node that occurs
* in an array, it will be removed from the array.
*
* For subsequent visitors leaveNode() will still be invoked for the
* removed node.
*/
public const REMOVE_NODE = 3;
/**
* If NodeVisitor::enterNode() returns DONT_TRAVERSE_CURRENT_AND_CHILDREN, child nodes
* of the current node will not be traversed for any visitors.
*
* For subsequent visitors enterNode() will not be called as well.
* leaveNode() will be invoked for visitors that has enterNode() method invoked.
*/
public const DONT_TRAVERSE_CURRENT_AND_CHILDREN = 4;
/** @var list<NodeVisitor> Visitors */
private $visitors = [];
/** @var bool Whether traversal should be stopped */
private $stopTraversal;
/**
* @param list<NodeVisitor> $visitors
*/
public function __construct(array $visitors)
{
$this->visitors = $visitors;
}
/**
* Traverses an array of nodes using the registered visitors.
*
* @param Node[] $nodes Array of nodes
*
* @return Node[] Traversed array of nodes
*/
public function traverse(array $nodes): array
{
$this->stopTraversal = false;
foreach ($this->visitors as $visitor) {
$return = $visitor->beforeTraverse($nodes);
if ($return === null) {
continue;
}
$nodes = $return;
}
$nodes = $this->traverseArray($nodes);
foreach ($this->visitors as $visitor) {
$return = $visitor->afterTraverse($nodes);
if ($return === null) {
continue;
}
$nodes = $return;
}
return $nodes;
}
/**
* Recursively traverse a node.
*
* @param Node $node Node to traverse.
*
* @return Node Result of traversal (may be original node or new one)
*/
private function traverseNode(Node $node): Node
{
$subNodeNames = array_keys(get_object_vars($node));
foreach ($subNodeNames as $name) {
$subNode =& $node->$name;
if (is_array($subNode)) {
$subNode = $this->traverseArray($subNode);
if ($this->stopTraversal) {
break;
}
} elseif ($subNode instanceof Node) {
$traverseChildren = true;
$breakVisitorIndex = null;
foreach ($this->visitors as $visitorIndex => $visitor) {
$return = $visitor->enterNode($subNode);
if ($return === null) {
continue;
}
if ($return instanceof Node) {
$this->ensureReplacementReasonable($subNode, $return);
$subNode = $return;
} elseif ($return === self::DONT_TRAVERSE_CHILDREN) {
$traverseChildren = false;
} elseif ($return === self::DONT_TRAVERSE_CURRENT_AND_CHILDREN) {
$traverseChildren = false;
$breakVisitorIndex = $visitorIndex;
break;
} elseif ($return === self::STOP_TRAVERSAL) {
$this->stopTraversal = true;
break 2;
} else {
throw new LogicException(
'enterNode() returned invalid value of type ' . gettype($return)
);
}
}
if ($traverseChildren) {
$subNode = $this->traverseNode($subNode);
if ($this->stopTraversal) {
break;
}
}
foreach ($this->visitors as $visitorIndex => $visitor) {
$return = $visitor->leaveNode($subNode);
if ($return !== null) {
if ($return instanceof Node) {
$this->ensureReplacementReasonable($subNode, $return);
$subNode = $return;
} elseif ($return === self::STOP_TRAVERSAL) {
$this->stopTraversal = true;
break 2;
} elseif (is_array($return)) {
throw new LogicException(
'leaveNode() may only return an array ' .
'if the parent structure is an array'
);
} else {
throw new LogicException(
'leaveNode() returned invalid value of type ' . gettype($return)
);
}
}
if ($breakVisitorIndex === $visitorIndex) {
break;
}
}
}
}
return $node;
}
/**
* Recursively traverse array (usually of nodes).
*
* @param mixed[] $nodes Array to traverse
*
* @return mixed[] Result of traversal (may be original array or changed one)
*/
private function traverseArray(array $nodes): array
{
$doNodes = [];
foreach ($nodes as $i => &$node) {
if ($node instanceof Node) {
$traverseChildren = true;
$breakVisitorIndex = null;
foreach ($this->visitors as $visitorIndex => $visitor) {
$return = $visitor->enterNode($node);
if ($return === null) {
continue;
}
if ($return instanceof Node) {
$this->ensureReplacementReasonable($node, $return);
$node = $return;
} elseif (is_array($return)) {
$doNodes[] = [$i, $return];
continue 2;
} elseif ($return === self::REMOVE_NODE) {
$doNodes[] = [$i, []];
continue 2;
} elseif ($return === self::DONT_TRAVERSE_CHILDREN) {
$traverseChildren = false;
} elseif ($return === self::DONT_TRAVERSE_CURRENT_AND_CHILDREN) {
$traverseChildren = false;
$breakVisitorIndex = $visitorIndex;
break;
} elseif ($return === self::STOP_TRAVERSAL) {
$this->stopTraversal = true;
break 2;
} else {
throw new LogicException(
'enterNode() returned invalid value of type ' . gettype($return)
);
}
}
if ($traverseChildren) {
$node = $this->traverseNode($node);
if ($this->stopTraversal) {
break;
}
}
foreach ($this->visitors as $visitorIndex => $visitor) {
$return = $visitor->leaveNode($node);
if ($return !== null) {
if ($return instanceof Node) {
$this->ensureReplacementReasonable($node, $return);
$node = $return;
} elseif (is_array($return)) {
$doNodes[] = [$i, $return];
break;
} elseif ($return === self::REMOVE_NODE) {
$doNodes[] = [$i, []];
break;
} elseif ($return === self::STOP_TRAVERSAL) {
$this->stopTraversal = true;
break 2;
} else {
throw new LogicException(
'leaveNode() returned invalid value of type ' . gettype($return)
);
}
}
if ($breakVisitorIndex === $visitorIndex) {
break;
}
}
} elseif (is_array($node)) {
throw new LogicException('Invalid node structure: Contains nested arrays');
}
}
if (count($doNodes) > 0) {
while ([$i, $replace] = array_pop($doNodes)) {
array_splice($nodes, $i, 1, $replace);
}
}
return $nodes;
}
private function ensureReplacementReasonable(Node $old, Node $new): void
{
if ($old instanceof TypeNode && !$new instanceof TypeNode) {
throw new LogicException(sprintf('Trying to replace TypeNode with %s', get_class($new)));
}
if ($old instanceof ConstExprNode && !$new instanceof ConstExprNode) {
throw new LogicException(sprintf('Trying to replace ConstExprNode with %s', get_class($new)));
}
if ($old instanceof PhpDocChildNode && !$new instanceof PhpDocChildNode) {
throw new LogicException(sprintf('Trying to replace PhpDocChildNode with %s', get_class($new)));
}
if ($old instanceof PhpDocTagValueNode && !$new instanceof PhpDocTagValueNode) {
throw new LogicException(sprintf('Trying to replace PhpDocTagValueNode with %s', get_class($new)));
}
}
}

View File

@@ -0,0 +1,87 @@
<?php declare(strict_types = 1);
namespace PHPStan\PhpDocParser\Ast;
/**
* Inspired by https://github.com/nikic/PHP-Parser/tree/36a6dcd04e7b0285e8f0868f44bd4927802f7df1
*
* Copyright (c) 2011, Nikita Popov
* All rights reserved.
*/
interface NodeVisitor
{
/**
* Called once before traversal.
*
* Return value semantics:
* * null: $nodes stays as-is
* * otherwise: $nodes is set to the return value
*
* @param Node[] $nodes Array of nodes
*
* @return Node[]|null Array of nodes
*/
public function beforeTraverse(array $nodes): ?array;
/**
* Called when entering a node.
*
* Return value semantics:
* * null
* => $node stays as-is
* * array (of Nodes)
* => The return value is merged into the parent array (at the position of the $node)
* * NodeTraverser::REMOVE_NODE
* => $node is removed from the parent array
* * NodeTraverser::DONT_TRAVERSE_CHILDREN
* => Children of $node are not traversed. $node stays as-is
* * NodeTraverser::DONT_TRAVERSE_CURRENT_AND_CHILDREN
* => Further visitors for the current node are skipped, and its children are not
* traversed. $node stays as-is.
* * NodeTraverser::STOP_TRAVERSAL
* => Traversal is aborted. $node stays as-is
* * otherwise
* => $node is set to the return value
*
* @param Node $node Node
*
* @return Node|Node[]|NodeTraverser::*|null Replacement node (or special return value)
*/
public function enterNode(Node $node);
/**
* Called when leaving a node.
*
* Return value semantics:
* * null
* => $node stays as-is
* * NodeTraverser::REMOVE_NODE
* => $node is removed from the parent array
* * NodeTraverser::STOP_TRAVERSAL
* => Traversal is aborted. $node stays as-is
* * array (of Nodes)
* => The return value is merged into the parent array (at the position of the $node)
* * otherwise
* => $node is set to the return value
*
* @param Node $node Node
*
* @return Node|Node[]|NodeTraverser::REMOVE_NODE|NodeTraverser::STOP_TRAVERSAL|null Replacement node (or special return value)
*/
public function leaveNode(Node $node);
/**
* Called once after traversal.
*
* Return value semantics:
* * null: $nodes stays as-is
* * otherwise: $nodes is set to the return value
*
* @param Node[] $nodes Array of nodes
*
* @return Node[]|null Array of nodes
*/
public function afterTraverse(array $nodes): ?array;
}

View File

@@ -0,0 +1,20 @@
<?php declare(strict_types = 1);
namespace PHPStan\PhpDocParser\Ast\NodeVisitor;
use PHPStan\PhpDocParser\Ast\AbstractNodeVisitor;
use PHPStan\PhpDocParser\Ast\Attribute;
use PHPStan\PhpDocParser\Ast\Node;
final class CloningVisitor extends AbstractNodeVisitor
{
public function enterNode(Node $originalNode)
{
$node = clone $originalNode;
$node->setAttribute(Attribute::ORIGINAL_NODE, $originalNode);
return $node;
}
}

View File

@@ -31,10 +31,11 @@ class InvalidTagValueNode implements PhpDocTagValueNode
$exception->getCurrentOffset(),
$exception->getExpectedTokenType(),
$exception->getExpectedTokenValue(),
$exception->getCurrentTokenLine(),
];
}
public function __get(string $name)
public function __get(string $name): ?ParserException
{
if ($name !== 'exception') {
trigger_error(sprintf('Undefined property: %s::$%s', self::class, $name), E_USER_WARNING);

View File

@@ -30,6 +30,10 @@ class MethodTagValueNode implements PhpDocTagValueNode
/** @var string (may be empty) */
public $description;
/**
* @param MethodTagValueParameterNode[] $parameters
* @param TemplateTagValueNode[] $templateTypes
*/
public function __construct(bool $isStatic, ?TypeNode $returnType, string $methodName, array $parameters, string $description, array $templateTypes = [])
{
$this->isStatic = $isStatic;

View File

@@ -23,6 +23,7 @@ class ArrayShapeNode implements TypeNode
public $kind;
/**
* @param ArrayShapeItemNode[] $items
* @param self::KIND_* $kind
*/
public function __construct(array $items, bool $sealed = true, string $kind = self::KIND_ARRAY)

View File

@@ -20,6 +20,14 @@ class ArrayTypeNode implements TypeNode
public function __toString(): string
{
if (
$this->type instanceof CallableTypeNode
|| $this->type instanceof ConstTypeNode
|| $this->type instanceof NullableTypeNode
) {
return '(' . $this->type . ')[]';
}
return $this->type . '[]';
}

View File

@@ -19,6 +19,9 @@ class CallableTypeNode implements TypeNode
/** @var TypeNode */
public $returnType;
/**
* @param CallableTypeParameterNode[] $parameters
*/
public function __construct(IdentifierTypeNode $identifier, array $parameters, TypeNode $returnType)
{
$this->identifier = $identifier;
@@ -29,8 +32,12 @@ class CallableTypeNode implements TypeNode
public function __toString(): string
{
$returnType = $this->returnType;
if ($returnType instanceof self) {
$returnType = "({$returnType})";
}
$parameters = implode(', ', $this->parameters);
return "{$this->identifier}({$parameters}): {$this->returnType}";
return "{$this->identifier}({$parameters}): {$returnType}";
}
}

View File

@@ -41,8 +41,8 @@ class CallableTypeParameterNode implements Node
$type = "{$this->type} ";
$isReference = $this->isReference ? '&' : '';
$isVariadic = $this->isVariadic ? '...' : '';
$default = $this->isOptional ? ' = default' : '';
return trim("{$type}{$isReference}{$isVariadic}{$this->parameterName}") . $default;
$isOptional = $this->isOptional ? '=' : '';
return trim("{$type}{$isReference}{$isVariadic}{$this->parameterName}") . $isOptional;
}
}

View File

@@ -25,6 +25,10 @@ class GenericTypeNode implements TypeNode
/** @var (self::VARIANCE_*)[] */
public $variances;
/**
* @param TypeNode[] $genericTypes
* @param (self::VARIANCE_*)[] $variances
*/
public function __construct(IdentifierTypeNode $type, array $genericTypes, array $variances = [])
{
$this->type = $type;

View File

@@ -3,6 +3,7 @@
namespace PHPStan\PhpDocParser\Ast\Type;
use PHPStan\PhpDocParser\Ast\NodeAttributes;
use function array_map;
use function implode;
class IntersectionTypeNode implements TypeNode
@@ -13,6 +14,9 @@ class IntersectionTypeNode implements TypeNode
/** @var TypeNode[] */
public $types;
/**
* @param TypeNode[] $types
*/
public function __construct(array $types)
{
$this->types = $types;
@@ -21,7 +25,13 @@ class IntersectionTypeNode implements TypeNode
public function __toString(): string
{
return '(' . implode(' & ', $this->types) . ')';
return '(' . implode(' & ', array_map(static function (TypeNode $type): string {
if ($type instanceof NullableTypeNode) {
return '(' . $type . ')';
}
return (string) $type;
}, $this->types)) . ')';
}
}

View File

@@ -0,0 +1,38 @@
<?php declare(strict_types = 1);
namespace PHPStan\PhpDocParser\Ast\Type;
use PHPStan\PhpDocParser\Ast\NodeAttributes;
use PHPStan\PhpDocParser\Parser\ParserException;
class InvalidTypeNode implements TypeNode
{
use NodeAttributes;
/** @var mixed[] */
private $exceptionArgs;
public function __construct(ParserException $exception)
{
$this->exceptionArgs = [
$exception->getCurrentTokenValue(),
$exception->getCurrentTokenType(),
$exception->getCurrentOffset(),
$exception->getExpectedTokenType(),
$exception->getExpectedTokenValue(),
$exception->getCurrentTokenLine(),
];
}
public function getException(): ParserException
{
return new ParserException(...$this->exceptionArgs);
}
public function __toString(): string
{
return '*Invalid type*';
}
}

View File

@@ -0,0 +1,48 @@
<?php declare(strict_types = 1);
namespace PHPStan\PhpDocParser\Ast\Type;
use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprStringNode;
use PHPStan\PhpDocParser\Ast\NodeAttributes;
use function sprintf;
class ObjectShapeItemNode implements TypeNode
{
use NodeAttributes;
/** @var ConstExprStringNode|IdentifierTypeNode */
public $keyName;
/** @var bool */
public $optional;
/** @var TypeNode */
public $valueType;
/**
* @param ConstExprStringNode|IdentifierTypeNode $keyName
*/
public function __construct($keyName, bool $optional, TypeNode $valueType)
{
$this->keyName = $keyName;
$this->optional = $optional;
$this->valueType = $valueType;
}
public function __toString(): string
{
if ($this->keyName !== null) {
return sprintf(
'%s%s: %s',
(string) $this->keyName,
$this->optional ? '?' : '',
(string) $this->valueType
);
}
return (string) $this->valueType;
}
}

View File

@@ -0,0 +1,31 @@
<?php declare(strict_types = 1);
namespace PHPStan\PhpDocParser\Ast\Type;
use PHPStan\PhpDocParser\Ast\NodeAttributes;
use function implode;
class ObjectShapeNode implements TypeNode
{
use NodeAttributes;
/** @var ObjectShapeItemNode[] */
public $items;
/**
* @param ObjectShapeItemNode[] $items
*/
public function __construct(array $items)
{
$this->items = $items;
}
public function __toString(): string
{
$items = $this->items;
return 'object{' . implode(', ', $items) . '}';
}
}

View File

@@ -23,6 +23,14 @@ class OffsetAccessTypeNode implements TypeNode
public function __toString(): string
{
if (
$this->type instanceof CallableTypeNode
|| $this->type instanceof ConstTypeNode
|| $this->type instanceof NullableTypeNode
) {
return '(' . $this->type . ')[' . $this->offset . ']';
}
return $this->type . '[' . $this->offset . ']';
}

View File

@@ -3,6 +3,7 @@
namespace PHPStan\PhpDocParser\Ast\Type;
use PHPStan\PhpDocParser\Ast\NodeAttributes;
use function array_map;
use function implode;
class UnionTypeNode implements TypeNode
@@ -13,6 +14,9 @@ class UnionTypeNode implements TypeNode
/** @var TypeNode[] */
public $types;
/**
* @param TypeNode[] $types
*/
public function __construct(array $types)
{
$this->types = $types;
@@ -21,7 +25,13 @@ class UnionTypeNode implements TypeNode
public function __toString(): string
{
return '(' . implode(' | ', $this->types) . ')';
return '(' . implode(' | ', array_map(static function (TypeNode $type): string {
if ($type instanceof NullableTypeNode) {
return '(' . $type . ')';
}
return (string) $type;
}, $this->types)) . ')';
}
}

View File

@@ -88,10 +88,14 @@ class Lexer
public const VALUE_OFFSET = 0;
public const TYPE_OFFSET = 1;
public const LINE_OFFSET = 2;
/** @var string|null */
private $regexp;
/**
* @return list<array{string, int, int}>
*/
public function tokenize(string $s): array
{
if ($this->regexp === null) {
@@ -101,11 +105,18 @@ class Lexer
preg_match_all($this->regexp, $s, $matches, PREG_SET_ORDER);
$tokens = [];
$line = 1;
foreach ($matches as $match) {
$tokens[] = [$match[0], (int) $match['MARK']];
$type = (int) $match['MARK'];
$tokens[] = [$match[0], $type, $line];
if ($type !== self::TOKEN_PHPDOC_EOL) {
continue;
}
$line++;
}
$tokens[] = ['', self::TOKEN_END];
$tokens[] = ['', self::TOKEN_END, $line];
return $tokens;
}
@@ -149,8 +160,8 @@ 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]*+(?:e-?[0-9]++)?)|(?:-?[0-9]*+\\.[0-9]++(?:e-?[0-9]++)?)|(?:-?[0-9]++e-?[0-9]++)',
self::TOKEN_INTEGER => '-?(?:(?:0b[0-1]++)|(?:0o[0-7]++)|(?:0x[0-9a-f]++)|(?:[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\\\\])*+"',

View File

@@ -4,10 +4,6 @@ namespace PHPStan\PhpDocParser\Parser;
use PHPStan\PhpDocParser\Ast;
use PHPStan\PhpDocParser\Lexer\Lexer;
use function chr;
use function hexdec;
use function octdec;
use function preg_replace_callback;
use function str_replace;
use function strtolower;
use function substr;
@@ -15,49 +11,93 @@ use function substr;
class ConstExprParser
{
private const REPLACEMENTS = [
'\\' => '\\',
'n' => "\n",
'r' => "\r",
't' => "\t",
'f' => "\f",
'v' => "\v",
'e' => "\x1B",
];
/** @var bool */
private $unescapeStrings;
public function __construct(bool $unescapeStrings = false)
/** @var bool */
private $quoteAwareConstExprString;
/** @var bool */
private $useLinesAttributes;
/** @var bool */
private $useIndexAttributes;
/**
* @param array{lines?: bool, indexes?: bool} $usedAttributes
*/
public function __construct(
bool $unescapeStrings = false,
bool $quoteAwareConstExprString = false,
array $usedAttributes = []
)
{
$this->unescapeStrings = $unescapeStrings;
$this->quoteAwareConstExprString = $quoteAwareConstExprString;
$this->useLinesAttributes = $usedAttributes['lines'] ?? false;
$this->useIndexAttributes = $usedAttributes['indexes'] ?? false;
}
public function parse(TokenIterator $tokens, bool $trimStrings = false): Ast\ConstExpr\ConstExprNode
{
$startLine = $tokens->currentTokenLine();
$startIndex = $tokens->currentTokenIndex();
if ($tokens->isCurrentTokenType(Lexer::TOKEN_FLOAT)) {
$value = $tokens->currentTokenValue();
$tokens->next();
return new Ast\ConstExpr\ConstExprFloatNode($value);
return $this->enrichWithAttributes(
$tokens,
new Ast\ConstExpr\ConstExprFloatNode(str_replace('_', '', $value)),
$startLine,
$startIndex
);
}
if ($tokens->isCurrentTokenType(Lexer::TOKEN_INTEGER)) {
$value = $tokens->currentTokenValue();
$tokens->next();
return new Ast\ConstExpr\ConstExprIntegerNode($value);
return $this->enrichWithAttributes(
$tokens,
new Ast\ConstExpr\ConstExprIntegerNode(str_replace('_', '', $value)),
$startLine,
$startIndex
);
}
if ($tokens->isCurrentTokenType(Lexer::TOKEN_SINGLE_QUOTED_STRING, Lexer::TOKEN_DOUBLE_QUOTED_STRING)) {
$value = $tokens->currentTokenValue();
$type = $tokens->currentTokenType();
if ($trimStrings) {
if ($this->unescapeStrings) {
$value = self::unescapeString($value);
$value = StringUnescaper::unescapeString($value);
} else {
$value = substr($value, 1, -1);
}
}
$tokens->next();
return new Ast\ConstExpr\ConstExprStringNode($value);
if ($this->quoteAwareConstExprString) {
return $this->enrichWithAttributes(
$tokens,
new Ast\ConstExpr\QuoteAwareConstExprStringNode(
$value,
$type === Lexer::TOKEN_SINGLE_QUOTED_STRING
? Ast\ConstExpr\QuoteAwareConstExprStringNode::SINGLE_QUOTED
: Ast\ConstExpr\QuoteAwareConstExprStringNode::DOUBLE_QUOTED
),
$startLine,
$startIndex
);
}
return $this->enrichWithAttributes(
$tokens,
new Ast\ConstExpr\ConstExprStringNode($value),
$startLine,
$startIndex
);
} elseif ($tokens->isCurrentTokenType(Lexer::TOKEN_IDENTIFIER)) {
$identifier = $tokens->currentTokenValue();
@@ -65,14 +105,29 @@ class ConstExprParser
switch (strtolower($identifier)) {
case 'true':
return new Ast\ConstExpr\ConstExprTrueNode();
return $this->enrichWithAttributes(
$tokens,
new Ast\ConstExpr\ConstExprTrueNode(),
$startLine,
$startIndex
);
case 'false':
return new Ast\ConstExpr\ConstExprFalseNode();
return $this->enrichWithAttributes(
$tokens,
new Ast\ConstExpr\ConstExprFalseNode(),
$startLine,
$startIndex
);
case 'null':
return new Ast\ConstExpr\ConstExprNullNode();
return $this->enrichWithAttributes(
$tokens,
new Ast\ConstExpr\ConstExprNullNode(),
$startLine,
$startIndex
);
case 'array':
$tokens->consumeTokenType(Lexer::TOKEN_OPEN_PARENTHESES);
return $this->parseArray($tokens, Lexer::TOKEN_CLOSE_PARENTHESES);
return $this->parseArray($tokens, Lexer::TOKEN_CLOSE_PARENTHESES, $startIndex);
}
if ($tokens->tryConsumeTokenType(Lexer::TOKEN_DOUBLE_COLON)) {
@@ -106,29 +161,43 @@ class ConstExprParser
break;
}
return new Ast\ConstExpr\ConstFetchNode($identifier, $classConstantName);
return $this->enrichWithAttributes(
$tokens,
new Ast\ConstExpr\ConstFetchNode($identifier, $classConstantName),
$startLine,
$startIndex
);
}
return new Ast\ConstExpr\ConstFetchNode('', $identifier);
return $this->enrichWithAttributes(
$tokens,
new Ast\ConstExpr\ConstFetchNode('', $identifier),
$startLine,
$startIndex
);
} elseif ($tokens->tryConsumeTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) {
return $this->parseArray($tokens, Lexer::TOKEN_CLOSE_SQUARE_BRACKET);
return $this->parseArray($tokens, Lexer::TOKEN_CLOSE_SQUARE_BRACKET, $startIndex);
}
throw new ParserException(
$tokens->currentTokenValue(),
$tokens->currentTokenType(),
$tokens->currentTokenOffset(),
Lexer::TOKEN_IDENTIFIER
Lexer::TOKEN_IDENTIFIER,
null,
$tokens->currentTokenLine()
);
}
private function parseArray(TokenIterator $tokens, int $endToken): Ast\ConstExpr\ConstExprArrayNode
private function parseArray(TokenIterator $tokens, int $endToken, int $startIndex): Ast\ConstExpr\ConstExprArrayNode
{
$items = [];
$startLine = $tokens->currentTokenLine();
if (!$tokens->tryConsumeTokenType($endToken)) {
do {
$items[] = $this->parseArrayItem($tokens);
@@ -136,12 +205,20 @@ class ConstExprParser
$tokens->consumeTokenType($endToken);
}
return new Ast\ConstExpr\ConstExprArrayNode($items);
return $this->enrichWithAttributes(
$tokens,
new Ast\ConstExpr\ConstExprArrayNode($items),
$startLine,
$startIndex
);
}
private function parseArrayItem(TokenIterator $tokens): Ast\ConstExpr\ConstExprArrayItemNode
{
$startLine = $tokens->currentTokenLine();
$startIndex = $tokens->currentTokenIndex();
$expr = $this->parse($tokens);
if ($tokens->tryConsumeTokenType(Lexer::TOKEN_DOUBLE_ARROW)) {
@@ -153,78 +230,40 @@ class ConstExprParser
$value = $expr;
}
return new Ast\ConstExpr\ConstExprArrayItemNode($key, $value);
}
private static function unescapeString(string $string): string
{
$quote = $string[0];
if ($quote === '\'') {
return str_replace(
['\\\\', '\\\''],
['\\', '\''],
substr($string, 1, -1)
);
}
return self::parseEscapeSequences(substr($string, 1, -1), '"');
}
/**
* Implementation based on https://github.com/nikic/PHP-Parser/blob/b0edd4c41111042d43bb45c6c657b2e0db367d9e/lib/PhpParser/Node/Scalar/String_.php#L90-L130
*/
private static function parseEscapeSequences(string $str, string $quote): string
{
$str = str_replace('\\' . $quote, $quote, $str);
return preg_replace_callback(
'~\\\\([\\\\nrtfve]|[xX][0-9a-fA-F]{1,2}|[0-7]{1,3}|u\{([0-9a-fA-F]+)\})~',
static function ($matches) {
$str = $matches[1];
if (isset(self::REPLACEMENTS[$str])) {
return self::REPLACEMENTS[$str];
}
if ($str[0] === 'x' || $str[0] === 'X') {
return chr(hexdec(substr($str, 1)));
}
if ($str[0] === 'u') {
return self::codePointToUtf8(hexdec($matches[2]));
}
return chr(octdec($str));
},
$str
return $this->enrichWithAttributes(
$tokens,
new Ast\ConstExpr\ConstExprArrayItemNode($key, $value),
$startLine,
$startIndex
);
}
/**
* Implementation based on https://github.com/nikic/PHP-Parser/blob/b0edd4c41111042d43bb45c6c657b2e0db367d9e/lib/PhpParser/Node/Scalar/String_.php#L132-L154
* @template T of Ast\ConstExpr\ConstExprNode
* @param T $node
* @return T
*/
private static function codePointToUtf8(int $num): string
private function enrichWithAttributes(TokenIterator $tokens, Ast\ConstExpr\ConstExprNode $node, int $startLine, int $startIndex): Ast\ConstExpr\ConstExprNode
{
if ($num <= 0x7F) {
return chr($num);
}
if ($num <= 0x7FF) {
return chr(($num >> 6) + 0xC0)
. chr(($num & 0x3F) + 0x80);
}
if ($num <= 0xFFFF) {
return chr(($num >> 12) + 0xE0)
. chr((($num >> 6) & 0x3F) + 0x80)
. chr(($num & 0x3F) + 0x80);
}
if ($num <= 0x1FFFFF) {
return chr(($num >> 18) + 0xF0)
. chr((($num >> 12) & 0x3F) + 0x80)
. chr((($num >> 6) & 0x3F) + 0x80)
. chr(($num & 0x3F) + 0x80);
$endLine = $tokens->currentTokenLine();
$endIndex = $tokens->currentTokenIndex();
if ($this->useLinesAttributes) {
$node->setAttribute(Ast\Attribute::START_LINE, $startLine);
$node->setAttribute(Ast\Attribute::END_LINE, $endLine);
}
// Invalid UTF-8 codepoint escape sequence: Codepoint too large
return "\xef\xbf\xbd";
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);
}
return $node;
}
}

View File

@@ -7,6 +7,7 @@ use PHPStan\PhpDocParser\Lexer\Lexer;
use function assert;
use function json_encode;
use function sprintf;
use const JSON_INVALID_UTF8_SUBSTITUTE;
use const JSON_UNESCAPED_SLASHES;
use const JSON_UNESCAPED_UNICODE;
@@ -28,12 +29,16 @@ class ParserException extends Exception
/** @var string|null */
private $expectedTokenValue;
/** @var int|null */
private $currentTokenLine;
public function __construct(
string $currentTokenValue,
int $currentTokenType,
int $currentOffset,
int $expectedTokenType,
?string $expectedTokenValue = null
?string $expectedTokenValue = null,
?int $currentTokenLine = null
)
{
$this->currentTokenValue = $currentTokenValue;
@@ -41,13 +46,15 @@ class ParserException extends Exception
$this->currentOffset = $currentOffset;
$this->expectedTokenType = $expectedTokenType;
$this->expectedTokenValue = $expectedTokenValue;
$this->currentTokenLine = $currentTokenLine;
parent::__construct(sprintf(
'Unexpected token %s, expected %s%s at offset %d',
'Unexpected token %s, expected %s%s at offset %d%s',
$this->formatValue($currentTokenValue),
Lexer::TOKEN_LABELS[$expectedTokenType],
$expectedTokenValue !== null ? sprintf(' (%s)', $this->formatValue($expectedTokenValue)) : '',
$currentOffset
$currentOffset,
$currentTokenLine === null ? '' : sprintf(' on line %d', $currentTokenLine)
));
}
@@ -82,9 +89,15 @@ class ParserException extends Exception
}
public function getCurrentTokenLine(): ?int
{
return $this->currentTokenLine;
}
private function formatValue(string $value): string
{
$json = json_encode($value, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
$json = json_encode($value, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_INVALID_UTF8_SUBSTITUTE);
assert($json !== false);
return $json;

View File

@@ -28,11 +28,32 @@ class PhpDocParser
/** @var bool */
private $requireWhitespaceBeforeDescription;
public function __construct(TypeParser $typeParser, ConstExprParser $constantExprParser, bool $requireWhitespaceBeforeDescription = false)
/** @var bool */
private $preserveTypeAliasesWithInvalidTypes;
/** @var bool */
private $useLinesAttributes;
/** @var bool */
private $useIndexAttributes;
/**
* @param array{lines?: bool, indexes?: bool} $usedAttributes
*/
public function __construct(
TypeParser $typeParser,
ConstExprParser $constantExprParser,
bool $requireWhitespaceBeforeDescription = false,
bool $preserveTypeAliasesWithInvalidTypes = false,
array $usedAttributes = []
)
{
$this->typeParser = $typeParser;
$this->constantExprParser = $constantExprParser;
$this->requireWhitespaceBeforeDescription = $requireWhitespaceBeforeDescription;
$this->preserveTypeAliasesWithInvalidTypes = $preserveTypeAliasesWithInvalidTypes;
$this->useLinesAttributes = $usedAttributes['lines'] ?? false;
$this->useIndexAttributes = $usedAttributes['indexes'] ?? false;
}
@@ -54,30 +75,82 @@ class PhpDocParser
$tokens->consumeTokenType(Lexer::TOKEN_CLOSE_PHPDOC);
} catch (ParserException $e) {
$name = '';
$startLine = $tokens->currentTokenLine();
$startIndex = $tokens->currentTokenIndex();
if (count($children) > 0) {
$lastChild = $children[count($children) - 1];
if ($lastChild instanceof Ast\PhpDoc\PhpDocTagNode) {
$name = $lastChild->name;
$startLine = $tokens->currentTokenLine();
$startIndex = $tokens->currentTokenIndex();
}
}
$tag = new Ast\PhpDoc\PhpDocTagNode(
$name,
$this->enrichWithAttributes(
$tokens,
new Ast\PhpDoc\InvalidTagValueNode($e->getMessage(), $e),
$startLine,
$startIndex
)
);
$tokens->forwardToTheEnd();
return new Ast\PhpDoc\PhpDocNode([
new Ast\PhpDoc\PhpDocTagNode($name, new Ast\PhpDoc\InvalidTagValueNode($e->getMessage(), $e)),
]);
return $this->enrichWithAttributes($tokens, new Ast\PhpDoc\PhpDocNode([$this->enrichWithAttributes($tokens, $tag, $startLine, $startIndex)]), 1, 0);
}
return new Ast\PhpDoc\PhpDocNode(array_values($children));
return $this->enrichWithAttributes($tokens, new Ast\PhpDoc\PhpDocNode(array_values($children)), 1, 0);
}
private function parseChild(TokenIterator $tokens): Ast\PhpDoc\PhpDocChildNode
{
if ($tokens->isCurrentTokenType(Lexer::TOKEN_PHPDOC_TAG)) {
return $this->parseTag($tokens);
$startLine = $tokens->currentTokenLine();
$startIndex = $tokens->currentTokenIndex();
return $this->enrichWithAttributes($tokens, $this->parseTag($tokens), $startLine, $startIndex);
}
return $this->parseText($tokens);
$startLine = $tokens->currentTokenLine();
$startIndex = $tokens->currentTokenIndex();
$text = $this->parseText($tokens);
return $this->enrichWithAttributes($tokens, $text, $startLine, $startIndex);
}
/**
* @template T of Ast\Node
* @param T $tag
* @return T
*/
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);
}
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);
}
return $tag;
}
@@ -120,6 +193,9 @@ class PhpDocParser
public function parseTagValue(TokenIterator $tokens, string $tag): Ast\PhpDoc\PhpDocTagValueNode
{
$startLine = $tokens->currentTokenLine();
$startIndex = $tokens->currentTokenIndex();
try {
$tokens->pushSavePoint();
@@ -247,7 +323,7 @@ class PhpDocParser
$tagValue = new Ast\PhpDoc\InvalidTagValueNode($this->parseOptionalDescription($tokens), $e);
}
return $tagValue;
return $this->enrichWithAttributes($tokens, $tagValue, $startLine, $startIndex);
}
@@ -257,9 +333,7 @@ class PhpDocParser
private function parseParamTagValue(TokenIterator $tokens): Ast\PhpDoc\PhpDocTagValueNode
{
if (
$tokens->isCurrentTokenType(Lexer::TOKEN_REFERENCE)
|| $tokens->isCurrentTokenType(Lexer::TOKEN_VARIADIC)
|| $tokens->isCurrentTokenType(Lexer::TOKEN_VARIABLE)
$tokens->isCurrentTokenType(Lexer::TOKEN_REFERENCE, Lexer::TOKEN_VARIADIC, Lexer::TOKEN_VARIABLE)
) {
$type = null;
} else {
@@ -329,6 +403,8 @@ class PhpDocParser
private function parseMethodTagValue(TokenIterator $tokens): Ast\PhpDoc\MethodTagValueNode
{
$isStatic = $tokens->tryConsumeTokenValue('static');
$startLine = $tokens->currentTokenLine();
$startIndex = $tokens->currentTokenIndex();
$returnTypeOrMethodName = $this->typeParser->parse($tokens);
if ($tokens->isCurrentTokenType(Lexer::TOKEN_IDENTIFIER)) {
@@ -337,7 +413,9 @@ class PhpDocParser
$tokens->next();
} elseif ($returnTypeOrMethodName instanceof Ast\Type\IdentifierTypeNode) {
$returnType = $isStatic ? new Ast\Type\IdentifierTypeNode('static') : null;
$returnType = $isStatic
? $this->typeParser->enrichWithAttributes($tokens, new Ast\Type\IdentifierTypeNode('static'), $startLine, $startIndex)
: null;
$methodName = $returnTypeOrMethodName->name;
$isStatic = false;
@@ -347,9 +425,12 @@ class PhpDocParser
}
$templateTypes = [];
if ($tokens->tryConsumeTokenType(Lexer::TOKEN_OPEN_ANGLE_BRACKET)) {
do {
$templateTypes[] = $this->parseTemplateTagValue($tokens, false);
$startLine = $tokens->currentTokenLine();
$startIndex = $tokens->currentTokenIndex();
$templateTypes[] = $this->enrichWithAttributes($tokens, $this->parseTemplateTagValue($tokens, false), $startLine, $startIndex);
} while ($tokens->tryConsumeTokenType(Lexer::TOKEN_COMMA));
$tokens->consumeTokenType(Lexer::TOKEN_CLOSE_ANGLE_BRACKET);
}
@@ -370,6 +451,9 @@ class PhpDocParser
private function parseMethodTagValueParameter(TokenIterator $tokens): Ast\PhpDoc\MethodTagValueParameterNode
{
$startLine = $tokens->currentTokenLine();
$startIndex = $tokens->currentTokenIndex();
switch ($tokens->currentTokenType()) {
case Lexer::TOKEN_IDENTIFIER:
case Lexer::TOKEN_OPEN_PARENTHESES:
@@ -394,7 +478,12 @@ class PhpDocParser
$defaultValue = null;
}
return new Ast\PhpDoc\MethodTagValueParameterNode($parameterType, $isReference, $isVariadic, $parameterName, $defaultValue);
return $this->enrichWithAttributes(
$tokens,
new Ast\PhpDoc\MethodTagValueParameterNode($parameterType, $isReference, $isVariadic, $parameterName, $defaultValue),
$startLine,
$startIndex
);
}
private function parseTemplateTagValue(TokenIterator $tokens, bool $parseDescription): Ast\PhpDoc\TemplateTagValueNode
@@ -426,10 +515,15 @@ class PhpDocParser
private function parseExtendsTagValue(string $tagName, TokenIterator $tokens): Ast\PhpDoc\PhpDocTagValueNode
{
$startLine = $tokens->currentTokenLine();
$startIndex = $tokens->currentTokenIndex();
$baseType = new IdentifierTypeNode($tokens->currentTokenValue());
$tokens->consumeTokenType(Lexer::TOKEN_IDENTIFIER);
$type = $this->typeParser->parseGeneric($tokens, $baseType);
$type = $this->typeParser->parseGeneric(
$tokens,
$this->typeParser->enrichWithAttributes($tokens, $baseType, $startLine, $startIndex)
);
$description = $this->parseOptionalDescription($tokens);
@@ -453,6 +547,34 @@ class PhpDocParser
// support psalm-type syntax
$tokens->tryConsumeTokenType(Lexer::TOKEN_EQUAL);
if ($this->preserveTypeAliasesWithInvalidTypes) {
$startLine = $tokens->currentTokenLine();
$startIndex = $tokens->currentTokenIndex();
try {
$type = $this->typeParser->parse($tokens);
if (!$tokens->isCurrentTokenType(Lexer::TOKEN_CLOSE_PHPDOC)) {
if (!$tokens->isCurrentTokenType(Lexer::TOKEN_PHPDOC_EOL)) {
throw new ParserException(
$tokens->currentTokenValue(),
$tokens->currentTokenType(),
$tokens->currentTokenOffset(),
Lexer::TOKEN_PHPDOC_EOL,
null,
$tokens->currentTokenLine()
);
}
}
return new Ast\PhpDoc\TypeAliasTagValueNode($alias, $type);
} catch (ParserException $e) {
$this->parseOptionalDescription($tokens);
return new Ast\PhpDoc\TypeAliasTagValueNode(
$alias,
$this->enrichWithAttributes($tokens, new Ast\Type\InvalidTypeNode($e), $startLine, $startIndex)
);
}
}
$type = $this->typeParser->parse($tokens);
return new Ast\PhpDoc\TypeAliasTagValueNode($alias, $type);
@@ -465,8 +587,16 @@ class PhpDocParser
$tokens->consumeTokenValue(Lexer::TOKEN_IDENTIFIER, 'from');
$identifierStartLine = $tokens->currentTokenLine();
$identifierStartIndex = $tokens->currentTokenIndex();
$importedFrom = $tokens->currentTokenValue();
$tokens->consumeTokenType(Lexer::TOKEN_IDENTIFIER);
$importedFromType = $this->enrichWithAttributes(
$tokens,
new IdentifierTypeNode($importedFrom),
$identifierStartLine,
$identifierStartIndex
);
$importedAs = null;
if ($tokens->tryConsumeTokenValue('as')) {
@@ -474,7 +604,7 @@ class PhpDocParser
$tokens->consumeTokenType(Lexer::TOKEN_IDENTIFIER);
}
return new Ast\PhpDoc\TypeAliasImportTagValueNode($importedAlias, new IdentifierTypeNode($importedFrom), $importedAs);
return new Ast\PhpDoc\TypeAliasImportTagValueNode($importedAlias, $importedFromType, $importedAs);
}
/**

View File

@@ -0,0 +1,96 @@
<?php declare(strict_types = 1);
namespace PHPStan\PhpDocParser\Parser;
use function chr;
use function hexdec;
use function octdec;
use function preg_replace_callback;
use function str_replace;
use function substr;
class StringUnescaper
{
private const REPLACEMENTS = [
'\\' => '\\',
'n' => "\n",
'r' => "\r",
't' => "\t",
'f' => "\f",
'v' => "\v",
'e' => "\x1B",
];
public static function unescapeString(string $string): string
{
$quote = $string[0];
if ($quote === '\'') {
return str_replace(
['\\\\', '\\\''],
['\\', '\''],
substr($string, 1, -1)
);
}
return self::parseEscapeSequences(substr($string, 1, -1), '"');
}
/**
* Implementation based on https://github.com/nikic/PHP-Parser/blob/b0edd4c41111042d43bb45c6c657b2e0db367d9e/lib/PhpParser/Node/Scalar/String_.php#L90-L130
*/
private static function parseEscapeSequences(string $str, string $quote): string
{
$str = str_replace('\\' . $quote, $quote, $str);
return preg_replace_callback(
'~\\\\([\\\\nrtfve]|[xX][0-9a-fA-F]{1,2}|[0-7]{1,3}|u\{([0-9a-fA-F]+)\})~',
static function ($matches) {
$str = $matches[1];
if (isset(self::REPLACEMENTS[$str])) {
return self::REPLACEMENTS[$str];
}
if ($str[0] === 'x' || $str[0] === 'X') {
return chr((int) hexdec(substr($str, 1)));
}
if ($str[0] === 'u') {
return self::codePointToUtf8((int) hexdec($matches[2]));
}
return chr((int) octdec($str));
},
$str
);
}
/**
* Implementation based on https://github.com/nikic/PHP-Parser/blob/b0edd4c41111042d43bb45c6c657b2e0db367d9e/lib/PhpParser/Node/Scalar/String_.php#L132-L154
*/
private static function codePointToUtf8(int $num): string
{
if ($num <= 0x7F) {
return chr($num);
}
if ($num <= 0x7FF) {
return chr(($num >> 6) + 0xC0)
. chr(($num & 0x3F) + 0x80);
}
if ($num <= 0xFFFF) {
return chr(($num >> 12) + 0xE0)
. chr((($num >> 6) & 0x3F) + 0x80)
. chr(($num & 0x3F) + 0x80);
}
if ($num <= 0x1FFFFF) {
return chr(($num >> 18) + 0xF0)
. chr((($num >> 12) & 0x3F) + 0x80)
. chr((($num >> 6) & 0x3F) + 0x80)
. chr(($num & 0x3F) + 0x80);
}
// Invalid UTF-8 codepoint escape sequence: Codepoint too large
return "\xef\xbf\xbd";
}
}

View File

@@ -2,6 +2,7 @@
namespace PHPStan\PhpDocParser\Parser;
use LogicException;
use PHPStan\PhpDocParser\Lexer\Lexer;
use function array_pop;
use function assert;
@@ -12,7 +13,7 @@ use function strlen;
class TokenIterator
{
/** @var mixed[][] */
/** @var list<array{string, int, int}> */
private $tokens;
/** @var int */
@@ -21,6 +22,9 @@ class TokenIterator
/** @var int[] */
private $savePoints = [];
/**
* @param list<array{string, int, int}> $tokens
*/
public function __construct(array $tokens, int $index = 0)
{
$this->tokens = $tokens;
@@ -34,6 +38,36 @@ class TokenIterator
}
/**
* @return list<array{string, int, int}>
*/
public function getTokens(): array
{
return $this->tokens;
}
public function getContentBetween(int $startPos, int $endPos): string
{
if ($startPos < 0 || $endPos > count($this->tokens)) {
throw new LogicException();
}
$content = '';
for ($i = $startPos; $i < $endPos; $i++) {
$content .= $this->tokens[$i][Lexer::VALUE_OFFSET];
}
return $content;
}
public function getTokenCount(): int
{
return count($this->tokens);
}
public function currentTokenValue(): string
{
return $this->tokens[$this->index][Lexer::VALUE_OFFSET];
@@ -57,6 +91,18 @@ class TokenIterator
}
public function currentTokenLine(): int
{
return $this->tokens[$this->index][Lexer::LINE_OFFSET];
}
public function currentTokenIndex(): int
{
return $this->index;
}
public function isCurrentTokenValue(string $tokenValue): bool
{
return $this->tokens[$this->index][Lexer::VALUE_OFFSET] === $tokenValue;
@@ -217,8 +263,69 @@ class TokenIterator
$this->currentTokenType(),
$this->currentTokenOffset(),
$expectedTokenType,
$expectedTokenValue
$expectedTokenValue,
$this->currentTokenLine()
);
}
/**
* Check whether the position is directly preceded by a certain token type.
*
* During this check TOKEN_HORIZONTAL_WS and TOKEN_PHPDOC_EOL are skipped
*/
public function hasTokenImmediatelyBefore(int $pos, int $expectedTokenType): bool
{
$tokens = $this->tokens;
$pos--;
for (; $pos >= 0; $pos--) {
$token = $tokens[$pos];
$type = $token[Lexer::TYPE_OFFSET];
if ($type === $expectedTokenType) {
return true;
}
if (!in_array($type, [
Lexer::TOKEN_HORIZONTAL_WS,
Lexer::TOKEN_PHPDOC_EOL,
], true)) {
break;
}
}
return false;
}
/**
* Check whether the position is directly followed by a certain token type.
*
* During this check TOKEN_HORIZONTAL_WS and TOKEN_PHPDOC_EOL are skipped
*/
public function hasTokenImmediatelyAfter(int $pos, int $expectedTokenType): bool
{
$tokens = $this->tokens;
$pos++;
for ($c = count($tokens); $pos < $c; $pos++) {
$token = $tokens[$pos];
$type = $token[Lexer::TYPE_OFFSET];
if ($type === $expectedTokenType) {
return true;
}
if (!in_array($type, [
Lexer::TOKEN_HORIZONTAL_WS,
Lexer::TOKEN_PHPDOC_EOL,
], true)) {
break;
}
}
return false;
}
/**
* Whether the given position is immediately surrounded by parenthesis.
*/
public function hasParentheses(int $startPos, int $endPos): bool
{
return $this->hasTokenImmediatelyBefore($startPos, Lexer::TOKEN_OPEN_PARENTHESES)
&& $this->hasTokenImmediatelyAfter($endPos, Lexer::TOKEN_CLOSE_PARENTHESES);
}
}

View File

@@ -6,6 +6,7 @@ use LogicException;
use PHPStan\PhpDocParser\Ast;
use PHPStan\PhpDocParser\Lexer\Lexer;
use function in_array;
use function str_replace;
use function strpos;
use function trim;
@@ -15,14 +16,35 @@ class TypeParser
/** @var ConstExprParser|null */
private $constExprParser;
public function __construct(?ConstExprParser $constExprParser = null)
/** @var bool */
private $quoteAwareConstExprString;
/** @var bool */
private $useLinesAttributes;
/** @var bool */
private $useIndexAttributes;
/**
* @param array{lines?: bool, indexes?: bool} $usedAttributes
*/
public function __construct(
?ConstExprParser $constExprParser = null,
bool $quoteAwareConstExprString = false,
array $usedAttributes = []
)
{
$this->constExprParser = $constExprParser;
$this->quoteAwareConstExprString = $quoteAwareConstExprString;
$this->useLinesAttributes = $usedAttributes['lines'] ?? false;
$this->useIndexAttributes = $usedAttributes['indexes'] ?? false;
}
/** @phpstan-impure */
public function parse(TokenIterator $tokens): Ast\Type\TypeNode
{
$startLine = $tokens->currentTokenLine();
$startIndex = $tokens->currentTokenIndex();
if ($tokens->isCurrentTokenType(Lexer::TOKEN_NULLABLE)) {
$type = $this->parseNullable($tokens);
@@ -37,12 +59,45 @@ class TypeParser
}
}
return $this->enrichWithAttributes($tokens, $type, $startLine, $startIndex);
}
/**
* @internal
* @template T of Ast\Node
* @param T $type
* @return T
*/
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);
}
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);
}
return $type;
}
/** @phpstan-impure */
private function subParse(TokenIterator $tokens): Ast\Type\TypeNode
{
$startLine = $tokens->currentTokenLine();
$startIndex = $tokens->currentTokenIndex();
if ($tokens->isCurrentTokenType(Lexer::TOKEN_NULLABLE)) {
$type = $this->parseNullable($tokens);
@@ -66,13 +121,16 @@ class TypeParser
}
}
return $type;
return $this->enrichWithAttributes($tokens, $type, $startLine, $startIndex);
}
/** @phpstan-impure */
private function parseAtomic(TokenIterator $tokens): Ast\Type\TypeNode
{
$startLine = $tokens->currentTokenLine();
$startIndex = $tokens->currentTokenIndex();
if ($tokens->tryConsumeTokenType(Lexer::TOKEN_OPEN_PARENTHESES)) {
$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
$type = $this->subParse($tokens);
@@ -81,26 +139,26 @@ class TypeParser
$tokens->consumeTokenType(Lexer::TOKEN_CLOSE_PARENTHESES);
if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) {
return $this->tryParseArrayOrOffsetAccess($tokens, $type);
$type = $this->tryParseArrayOrOffsetAccess($tokens, $type);
}
return $type;
return $this->enrichWithAttributes($tokens, $type, $startLine, $startIndex);
}
if ($tokens->tryConsumeTokenType(Lexer::TOKEN_THIS_VARIABLE)) {
$type = new Ast\Type\ThisTypeNode();
$type = $this->enrichWithAttributes($tokens, new Ast\Type\ThisTypeNode(), $startLine, $startIndex);
if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) {
return $this->tryParseArrayOrOffsetAccess($tokens, $type);
$type = $this->tryParseArrayOrOffsetAccess($tokens, $type);
}
return $type;
return $this->enrichWithAttributes($tokens, $type, $startLine, $startIndex);
}
$currentTokenValue = $tokens->currentTokenValue();
$tokens->pushSavePoint(); // because of ConstFetchNode
if ($tokens->tryConsumeTokenType(Lexer::TOKEN_IDENTIFIER)) {
$type = new Ast\Type\IdentifierTypeNode($currentTokenValue);
$type = $this->enrichWithAttributes($tokens, new Ast\Type\IdentifierTypeNode($currentTokenValue), $startLine, $startIndex);
if (!$tokens->isCurrentTokenType(Lexer::TOKEN_DOUBLE_COLON)) {
$tokens->dropSavePoint(); // because of ConstFetchNode
@@ -124,15 +182,22 @@ class TypeParser
} elseif ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) {
$type = $this->tryParseArrayOrOffsetAccess($tokens, $type);
} elseif (in_array($type->name, ['array', 'list'], true) && $tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_CURLY_BRACKET) && !$tokens->isPrecededByHorizontalWhitespace()) {
$type = $this->parseArrayShape($tokens, $type, $type->name);
} elseif (in_array($type->name, ['array', 'list', 'object'], true) && $tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_CURLY_BRACKET) && !$tokens->isPrecededByHorizontalWhitespace()) {
if ($type->name === 'object') {
$type = $this->parseObjectShape($tokens);
} else {
$type = $this->parseArrayShape($tokens, $type, $type->name);
}
if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) {
$type = $this->tryParseArrayOrOffsetAccess($tokens, $type);
$type = $this->tryParseArrayOrOffsetAccess(
$tokens,
$this->enrichWithAttributes($tokens, $type, $startLine, $startIndex)
);
}
}
return $type;
return $this->enrichWithAttributes($tokens, $type, $startLine, $startIndex);
} else {
$tokens->rollback(); // because of ConstFetchNode
}
@@ -144,7 +209,9 @@ class TypeParser
$tokens->currentTokenValue(),
$tokens->currentTokenType(),
$tokens->currentTokenOffset(),
Lexer::TOKEN_IDENTIFIER
Lexer::TOKEN_IDENTIFIER,
null,
$tokens->currentTokenLine()
);
if ($this->constExprParser === null) {
@@ -157,7 +224,7 @@ class TypeParser
throw $exception;
}
return new Ast\Type\ConstTypeNode($constExpr);
return $this->enrichWithAttributes($tokens, new Ast\Type\ConstTypeNode($constExpr), $startLine, $startIndex);
} catch (LogicException $e) {
throw $exception;
}
@@ -336,7 +403,14 @@ class TypeParser
$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
if ($tokens->tryConsumeTokenType(Lexer::TOKEN_CLOSE_ANGLE_BRACKET)) {
// trailing comma case
return new Ast\Type\GenericTypeNode($baseType, $genericTypes, $variances);
$type = new Ast\Type\GenericTypeNode($baseType, $genericTypes, $variances);
$startLine = $baseType->getAttribute(Ast\Attribute::START_LINE);
$startIndex = $baseType->getAttribute(Ast\Attribute::START_INDEX);
if ($startLine !== null && $startIndex !== null) {
$type = $this->enrichWithAttributes($tokens, $type, $startLine, $startIndex);
}
return $type;
}
[$genericTypes[], $variances[]] = $this->parseGenericTypeArgument($tokens);
$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
@@ -345,7 +419,14 @@ class TypeParser
$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
$tokens->consumeTokenType(Lexer::TOKEN_CLOSE_ANGLE_BRACKET);
return new Ast\Type\GenericTypeNode($baseType, $genericTypes, $variances);
$type = new Ast\Type\GenericTypeNode($baseType, $genericTypes, $variances);
$startLine = $baseType->getAttribute(Ast\Attribute::START_LINE);
$startIndex = $baseType->getAttribute(Ast\Attribute::START_INDEX);
if ($startLine !== null && $startIndex !== null) {
$type = $this->enrichWithAttributes($tokens, $type, $startLine, $startIndex);
}
return $type;
}
@@ -355,9 +436,11 @@ class TypeParser
*/
public function parseGenericTypeArgument(TokenIterator $tokens): array
{
$startLine = $tokens->currentTokenLine();
$startIndex = $tokens->currentTokenIndex();
if ($tokens->tryConsumeTokenType(Lexer::TOKEN_WILDCARD)) {
return [
new Ast\Type\IdentifierTypeNode('mixed'),
$this->enrichWithAttributes($tokens, new Ast\Type\IdentifierTypeNode('mixed'), $startLine, $startIndex),
Ast\Type\GenericTypeNode::VARIANCE_BIVARIANT,
];
}
@@ -397,7 +480,10 @@ class TypeParser
$tokens->consumeTokenType(Lexer::TOKEN_CLOSE_PARENTHESES);
$tokens->consumeTokenType(Lexer::TOKEN_COLON);
$returnType = $this->parseCallableReturnType($tokens);
$startLine = $tokens->currentTokenLine();
$startIndex = $tokens->currentTokenIndex();
$returnType = $this->enrichWithAttributes($tokens, $this->parseCallableReturnType($tokens), $startLine, $startIndex);
return new Ast\Type\CallableTypeNode($identifier, $parameters, $returnType);
}
@@ -406,6 +492,8 @@ class TypeParser
/** @phpstan-impure */
private function parseCallableParameter(TokenIterator $tokens): Ast\Type\CallableTypeParameterNode
{
$startLine = $tokens->currentTokenLine();
$startIndex = $tokens->currentTokenIndex();
$type = $this->parse($tokens);
$isReference = $tokens->tryConsumeTokenType(Lexer::TOKEN_REFERENCE);
$isVariadic = $tokens->tryConsumeTokenType(Lexer::TOKEN_VARIADIC);
@@ -419,37 +507,141 @@ class TypeParser
}
$isOptional = $tokens->tryConsumeTokenType(Lexer::TOKEN_EQUAL);
return new Ast\Type\CallableTypeParameterNode($type, $isReference, $isVariadic, $parameterName, $isOptional);
return $this->enrichWithAttributes(
$tokens,
new Ast\Type\CallableTypeParameterNode($type, $isReference, $isVariadic, $parameterName, $isOptional),
$startLine,
$startIndex
);
}
/** @phpstan-impure */
private function parseCallableReturnType(TokenIterator $tokens): Ast\Type\TypeNode
{
$startLine = $tokens->currentTokenLine();
$startIndex = $tokens->currentTokenIndex();
if ($tokens->isCurrentTokenType(Lexer::TOKEN_NULLABLE)) {
$type = $this->parseNullable($tokens);
return $this->parseNullable($tokens);
} elseif ($tokens->tryConsumeTokenType(Lexer::TOKEN_OPEN_PARENTHESES)) {
$type = $this->parse($tokens);
$tokens->consumeTokenType(Lexer::TOKEN_CLOSE_PARENTHESES);
if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) {
$type = $this->tryParseArrayOrOffsetAccess($tokens, $type);
}
return $type;
} elseif ($tokens->tryConsumeTokenType(Lexer::TOKEN_THIS_VARIABLE)) {
$type = new Ast\Type\ThisTypeNode();
if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) {
$type = $this->tryParseArrayOrOffsetAccess($tokens, $this->enrichWithAttributes(
$tokens,
$type,
$startLine,
$startIndex
));
}
return $type;
} else {
$type = new Ast\Type\IdentifierTypeNode($tokens->currentTokenValue());
$tokens->consumeTokenType(Lexer::TOKEN_IDENTIFIER);
$currentTokenValue = $tokens->currentTokenValue();
$tokens->pushSavePoint(); // because of ConstFetchNode
if ($tokens->tryConsumeTokenType(Lexer::TOKEN_IDENTIFIER)) {
$type = new Ast\Type\IdentifierTypeNode($currentTokenValue);
if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_ANGLE_BRACKET)) {
$type = $this->parseGeneric($tokens, $type);
if (!$tokens->isCurrentTokenType(Lexer::TOKEN_DOUBLE_COLON)) {
if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_ANGLE_BRACKET)) {
$type = $this->parseGeneric(
$tokens,
$this->enrichWithAttributes(
$tokens,
$type,
$startLine,
$startIndex
)
);
if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) {
$type = $this->tryParseArrayOrOffsetAccess($tokens, $this->enrichWithAttributes(
$tokens,
$type,
$startLine,
$startIndex
));
}
} elseif (in_array($type->name, ['array', 'list'], true) && $tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_CURLY_BRACKET) && !$tokens->isPrecededByHorizontalWhitespace()) {
$type = $this->parseArrayShape($tokens, $type, $type->name);
} elseif ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) {
$type = $this->tryParseArrayOrOffsetAccess($tokens, $this->enrichWithAttributes(
$tokens,
$type,
$startLine,
$startIndex
));
} elseif (in_array($type->name, ['array', 'list', 'object'], true) && $tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_CURLY_BRACKET) && !$tokens->isPrecededByHorizontalWhitespace()) {
if ($type->name === 'object') {
$type = $this->parseObjectShape($tokens);
} else {
$type = $this->parseArrayShape($tokens, $this->enrichWithAttributes(
$tokens,
$type,
$startLine,
$startIndex
), $type->name);
}
if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) {
$type = $this->tryParseArrayOrOffsetAccess($tokens, $this->enrichWithAttributes(
$tokens,
$type,
$startLine,
$startIndex
));
}
}
return $type;
} else {
$tokens->rollback(); // because of ConstFetchNode
}
} else {
$tokens->dropSavePoint(); // because of ConstFetchNode
}
}
if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) {
$type = $this->tryParseArrayOrOffsetAccess($tokens, $type);
$exception = new ParserException(
$tokens->currentTokenValue(),
$tokens->currentTokenType(),
$tokens->currentTokenOffset(),
Lexer::TOKEN_IDENTIFIER,
null,
$tokens->currentTokenLine()
);
if ($this->constExprParser === null) {
throw $exception;
}
return $type;
try {
$constExpr = $this->constExprParser->parse($tokens, true);
if ($constExpr instanceof Ast\ConstExpr\ConstExprArrayNode) {
throw $exception;
}
$type = new Ast\Type\ConstTypeNode($constExpr);
if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) {
$type = $this->tryParseArrayOrOffsetAccess($tokens, $this->enrichWithAttributes(
$tokens,
$type,
$startLine,
$startIndex
));
}
return $type;
} catch (LogicException $e) {
throw $exception;
}
}
@@ -473,6 +665,8 @@ class TypeParser
/** @phpstan-impure */
private function tryParseArrayOrOffsetAccess(TokenIterator $tokens, Ast\Type\TypeNode $type): Ast\Type\TypeNode
{
$startLine = $type->getAttribute(Ast\Attribute::START_LINE);
$startIndex = $type->getAttribute(Ast\Attribute::START_INDEX);
try {
while ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) {
$tokens->pushSavePoint();
@@ -485,10 +679,28 @@ class TypeParser
$tokens->consumeTokenType(Lexer::TOKEN_CLOSE_SQUARE_BRACKET);
$tokens->dropSavePoint();
$type = new Ast\Type\OffsetAccessTypeNode($type, $offset);
if ($startLine !== null && $startIndex !== null) {
$type = $this->enrichWithAttributes(
$tokens,
$type,
$startLine,
$startIndex
);
}
} else {
$tokens->consumeTokenType(Lexer::TOKEN_CLOSE_SQUARE_BRACKET);
$tokens->dropSavePoint();
$type = new Ast\Type\ArrayTypeNode($type);
if ($startLine !== null && $startIndex !== null) {
$type = $this->enrichWithAttributes(
$tokens,
$type,
$startLine,
$startIndex
);
}
}
}
@@ -539,6 +751,8 @@ class TypeParser
/** @phpstan-impure */
private function parseArrayShapeItem(TokenIterator $tokens): Ast\Type\ArrayShapeItemNode
{
$startLine = $tokens->currentTokenLine();
$startIndex = $tokens->currentTokenIndex();
try {
$tokens->pushSavePoint();
$key = $this->parseArrayShapeKey($tokens);
@@ -547,12 +761,22 @@ class TypeParser
$value = $this->parse($tokens);
$tokens->dropSavePoint();
return new Ast\Type\ArrayShapeItemNode($key, $optional, $value);
return $this->enrichWithAttributes(
$tokens,
new Ast\Type\ArrayShapeItemNode($key, $optional, $value),
$startLine,
$startIndex
);
} catch (ParserException $e) {
$tokens->rollback();
$value = $this->parse($tokens);
return new Ast\Type\ArrayShapeItemNode(null, false, $value);
return $this->enrichWithAttributes(
$tokens,
new Ast\Type\ArrayShapeItemNode(null, false, $value),
$startLine,
$startIndex
);
}
}
@@ -562,16 +786,28 @@ class TypeParser
*/
private function parseArrayShapeKey(TokenIterator $tokens)
{
$startIndex = $tokens->currentTokenIndex();
$startLine = $tokens->currentTokenLine();
if ($tokens->isCurrentTokenType(Lexer::TOKEN_INTEGER)) {
$key = new Ast\ConstExpr\ConstExprIntegerNode($tokens->currentTokenValue());
$key = new Ast\ConstExpr\ConstExprIntegerNode(str_replace('_', '', $tokens->currentTokenValue()));
$tokens->next();
} elseif ($tokens->isCurrentTokenType(Lexer::TOKEN_SINGLE_QUOTED_STRING)) {
$key = new Ast\ConstExpr\ConstExprStringNode(trim($tokens->currentTokenValue(), "'"));
if ($this->quoteAwareConstExprString) {
$key = new Ast\ConstExpr\QuoteAwareConstExprStringNode(StringUnescaper::unescapeString($tokens->currentTokenValue()), Ast\ConstExpr\QuoteAwareConstExprStringNode::SINGLE_QUOTED);
} else {
$key = new Ast\ConstExpr\ConstExprStringNode(trim($tokens->currentTokenValue(), "'"));
}
$tokens->next();
} elseif ($tokens->isCurrentTokenType(Lexer::TOKEN_DOUBLE_QUOTED_STRING)) {
$key = new Ast\ConstExpr\ConstExprStringNode(trim($tokens->currentTokenValue(), '"'));
if ($this->quoteAwareConstExprString) {
$key = new Ast\ConstExpr\QuoteAwareConstExprStringNode(StringUnescaper::unescapeString($tokens->currentTokenValue()), Ast\ConstExpr\QuoteAwareConstExprStringNode::DOUBLE_QUOTED);
} else {
$key = new Ast\ConstExpr\ConstExprStringNode(trim($tokens->currentTokenValue(), '"'));
}
$tokens->next();
} else {
@@ -579,7 +815,86 @@ class TypeParser
$tokens->consumeTokenType(Lexer::TOKEN_IDENTIFIER);
}
return $key;
return $this->enrichWithAttributes(
$tokens,
$key,
$startLine,
$startIndex
);
}
/**
* @phpstan-impure
*/
private function parseObjectShape(TokenIterator $tokens): Ast\Type\ObjectShapeNode
{
$tokens->consumeTokenType(Lexer::TOKEN_OPEN_CURLY_BRACKET);
$items = [];
do {
$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
if ($tokens->tryConsumeTokenType(Lexer::TOKEN_CLOSE_CURLY_BRACKET)) {
return new Ast\Type\ObjectShapeNode($items);
}
$items[] = $this->parseObjectShapeItem($tokens);
$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
} while ($tokens->tryConsumeTokenType(Lexer::TOKEN_COMMA));
$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
$tokens->consumeTokenType(Lexer::TOKEN_CLOSE_CURLY_BRACKET);
return new Ast\Type\ObjectShapeNode($items);
}
/** @phpstan-impure */
private function parseObjectShapeItem(TokenIterator $tokens): Ast\Type\ObjectShapeItemNode
{
$startLine = $tokens->currentTokenLine();
$startIndex = $tokens->currentTokenIndex();
$key = $this->parseObjectShapeKey($tokens);
$optional = $tokens->tryConsumeTokenType(Lexer::TOKEN_NULLABLE);
$tokens->consumeTokenType(Lexer::TOKEN_COLON);
$value = $this->parse($tokens);
return $this->enrichWithAttributes($tokens, new Ast\Type\ObjectShapeItemNode($key, $optional, $value), $startLine, $startIndex);
}
/**
* @phpstan-impure
* @return Ast\ConstExpr\ConstExprStringNode|Ast\Type\IdentifierTypeNode
*/
private function parseObjectShapeKey(TokenIterator $tokens)
{
$startLine = $tokens->currentTokenLine();
$startIndex = $tokens->currentTokenIndex();
if ($tokens->isCurrentTokenType(Lexer::TOKEN_SINGLE_QUOTED_STRING)) {
if ($this->quoteAwareConstExprString) {
$key = new Ast\ConstExpr\QuoteAwareConstExprStringNode(StringUnescaper::unescapeString($tokens->currentTokenValue()), Ast\ConstExpr\QuoteAwareConstExprStringNode::SINGLE_QUOTED);
} else {
$key = new Ast\ConstExpr\ConstExprStringNode(trim($tokens->currentTokenValue(), "'"));
}
$tokens->next();
} elseif ($tokens->isCurrentTokenType(Lexer::TOKEN_DOUBLE_QUOTED_STRING)) {
if ($this->quoteAwareConstExprString) {
$key = new Ast\ConstExpr\QuoteAwareConstExprStringNode(StringUnescaper::unescapeString($tokens->currentTokenValue()), Ast\ConstExpr\QuoteAwareConstExprStringNode::DOUBLE_QUOTED);
} else {
$key = new Ast\ConstExpr\ConstExprStringNode(trim($tokens->currentTokenValue(), '"'));
}
$tokens->next();
} else {
$key = new Ast\Type\IdentifierTypeNode($tokens->currentTokenValue());
$tokens->consumeTokenType(Lexer::TOKEN_IDENTIFIER);
}
return $this->enrichWithAttributes($tokens, $key, $startLine, $startIndex);
}
}

View File

@@ -0,0 +1,44 @@
<?php declare(strict_types = 1);
namespace PHPStan\PhpDocParser\Printer;
/**
* Inspired by https://github.com/nikic/PHP-Parser/tree/36a6dcd04e7b0285e8f0868f44bd4927802f7df1
*
* Copyright (c) 2011, Nikita Popov
* All rights reserved.
*
* Implements the Myers diff algorithm.
*
* @internal
*/
class DiffElem
{
public const TYPE_KEEP = 0;
public const TYPE_REMOVE = 1;
public const TYPE_ADD = 2;
public const TYPE_REPLACE = 3;
/** @var self::TYPE_* */
public $type;
/** @var mixed Is null for add operations */
public $old;
/** @var mixed Is null for remove operations */
public $new;
/**
* @param self::TYPE_* $type
* @param mixed $old Is null for add operations
* @param mixed $new Is null for remove operations
*/
public function __construct(int $type, $old, $new)
{
$this->type = $type;
$this->old = $old;
$this->new = $new;
}
}

View File

@@ -0,0 +1,196 @@
<?php declare(strict_types = 1);
namespace PHPStan\PhpDocParser\Printer;
use Exception;
use function array_reverse;
use function count;
/**
* Inspired by https://github.com/nikic/PHP-Parser/tree/36a6dcd04e7b0285e8f0868f44bd4927802f7df1
*
* Copyright (c) 2011, Nikita Popov
* All rights reserved.
*
* Implements the Myers diff algorithm.
*
* Myers, Eugene W. "An O (ND) difference algorithm and its variations."
* Algorithmica 1.1 (1986): 251-266.
*
* @template T
* @internal
*/
class Differ
{
/** @var callable(T, T): bool */
private $isEqual;
/**
* Create differ over the given equality relation.
*
* @param callable(T, T): bool $isEqual Equality relation
*/
public function __construct(callable $isEqual)
{
$this->isEqual = $isEqual;
}
/**
* Calculate diff (edit script) from $old to $new.
*
* @param T[] $old Original array
* @param T[] $new New array
*
* @return DiffElem[] Diff (edit script)
*/
public function diff(array $old, array $new): array
{
[$trace, $x, $y] = $this->calculateTrace($old, $new);
return $this->extractDiff($trace, $x, $y, $old, $new);
}
/**
* Calculate diff, including "replace" operations.
*
* If a sequence of remove operations is followed by the same number of add operations, these
* will be coalesced into replace operations.
*
* @param T[] $old Original array
* @param T[] $new New array
*
* @return DiffElem[] Diff (edit script), including replace operations
*/
public function diffWithReplacements(array $old, array $new): array
{
return $this->coalesceReplacements($this->diff($old, $new));
}
/**
* @param T[] $old
* @param T[] $new
* @return array{array<int, array<int, int>>, int, int}
*/
private function calculateTrace(array $old, array $new): array
{
$n = count($old);
$m = count($new);
$max = $n + $m;
$v = [1 => 0];
$trace = [];
for ($d = 0; $d <= $max; $d++) {
$trace[] = $v;
for ($k = -$d; $k <= $d; $k += 2) {
if ($k === -$d || ($k !== $d && $v[$k - 1] < $v[$k + 1])) {
$x = $v[$k + 1];
} else {
$x = $v[$k - 1] + 1;
}
$y = $x - $k;
while ($x < $n && $y < $m && ($this->isEqual)($old[$x], $new[$y])) {
$x++;
$y++;
}
$v[$k] = $x;
if ($x >= $n && $y >= $m) {
return [$trace, $x, $y];
}
}
}
throw new Exception('Should not happen');
}
/**
* @param array<int, array<int, int>> $trace
* @param T[] $old
* @param T[] $new
* @return DiffElem[]
*/
private function extractDiff(array $trace, int $x, int $y, array $old, array $new): array
{
$result = [];
for ($d = count($trace) - 1; $d >= 0; $d--) {
$v = $trace[$d];
$k = $x - $y;
if ($k === -$d || ($k !== $d && $v[$k - 1] < $v[$k + 1])) {
$prevK = $k + 1;
} else {
$prevK = $k - 1;
}
$prevX = $v[$prevK];
$prevY = $prevX - $prevK;
while ($x > $prevX && $y > $prevY) {
$result[] = new DiffElem(DiffElem::TYPE_KEEP, $old[$x - 1], $new[$y - 1]);
$x--;
$y--;
}
if ($d === 0) {
break;
}
while ($x > $prevX) {
$result[] = new DiffElem(DiffElem::TYPE_REMOVE, $old[$x - 1], null);
$x--;
}
while ($y > $prevY) {
$result[] = new DiffElem(DiffElem::TYPE_ADD, null, $new[$y - 1]);
$y--;
}
}
return array_reverse($result);
}
/**
* Coalesce equal-length sequences of remove+add into a replace operation.
*
* @param DiffElem[] $diff
* @return DiffElem[]
*/
private function coalesceReplacements(array $diff): array
{
$newDiff = [];
$c = count($diff);
for ($i = 0; $i < $c; $i++) {
$diffType = $diff[$i]->type;
if ($diffType !== DiffElem::TYPE_REMOVE) {
$newDiff[] = $diff[$i];
continue;
}
$j = $i;
while ($j < $c && $diff[$j]->type === DiffElem::TYPE_REMOVE) {
$j++;
}
$k = $j;
while ($k < $c && $diff[$k]->type === DiffElem::TYPE_ADD) {
$k++;
}
if ($j - $i === $k - $j) {
$len = $j - $i;
for ($n = 0; $n < $len; $n++) {
$newDiff[] = new DiffElem(
DiffElem::TYPE_REPLACE,
$diff[$i + $n]->old,
$diff[$j + $n]->new
);
}
} else {
for (; $i < $k; $i++) {
$newDiff[] = $diff[$i];
}
}
$i = $k - 1;
}
return $newDiff;
}
}

View File

@@ -0,0 +1,796 @@
<?php declare(strict_types = 1);
namespace PHPStan\PhpDocParser\Printer;
use LogicException;
use PHPStan\PhpDocParser\Ast\Attribute;
use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprArrayNode;
use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprNode;
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\ExtendsTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\ImplementsTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\MethodTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\MethodTagValueParameterNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\MixinTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\ParamOutTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\ParamTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocChildNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTextNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\PropertyTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\ReturnTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\SelfOutTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\TemplateTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\ThrowsTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\TypeAliasImportTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\TypeAliasTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\UsesTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\VarTagValueNode;
use PHPStan\PhpDocParser\Ast\Type\ArrayShapeItemNode;
use PHPStan\PhpDocParser\Ast\Type\ArrayShapeNode;
use PHPStan\PhpDocParser\Ast\Type\ArrayTypeNode;
use PHPStan\PhpDocParser\Ast\Type\CallableTypeNode;
use PHPStan\PhpDocParser\Ast\Type\CallableTypeParameterNode;
use PHPStan\PhpDocParser\Ast\Type\ConditionalTypeForParameterNode;
use PHPStan\PhpDocParser\Ast\Type\ConditionalTypeNode;
use PHPStan\PhpDocParser\Ast\Type\ConstTypeNode;
use PHPStan\PhpDocParser\Ast\Type\GenericTypeNode;
use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
use PHPStan\PhpDocParser\Ast\Type\IntersectionTypeNode;
use PHPStan\PhpDocParser\Ast\Type\InvalidTypeNode;
use PHPStan\PhpDocParser\Ast\Type\NullableTypeNode;
use PHPStan\PhpDocParser\Ast\Type\ObjectShapeItemNode;
use PHPStan\PhpDocParser\Ast\Type\ObjectShapeNode;
use PHPStan\PhpDocParser\Ast\Type\OffsetAccessTypeNode;
use PHPStan\PhpDocParser\Ast\Type\ThisTypeNode;
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
use PHPStan\PhpDocParser\Ast\Type\UnionTypeNode;
use PHPStan\PhpDocParser\Lexer\Lexer;
use PHPStan\PhpDocParser\Parser\TokenIterator;
use function array_keys;
use function array_map;
use function count;
use function get_class;
use function get_object_vars;
use function implode;
use function in_array;
use function is_array;
use function preg_match_all;
use function sprintf;
use function strlen;
use function strpos;
use function trim;
use const PREG_SET_ORDER;
/**
* Inspired by https://github.com/nikic/PHP-Parser/tree/36a6dcd04e7b0285e8f0868f44bd4927802f7df1
*
* Copyright (c) 2011, Nikita Popov
* All rights reserved.
*/
final class Printer
{
/** @var Differ<Node> */
private $differ;
/**
* Map From "{$class}->{$subNode}" to string that should be inserted
* between elements of this list subnode
*
* @var array<string, string>
*/
private $listInsertionMap = [
PhpDocNode::class . '->children' => "\n * ",
UnionTypeNode::class . '->types' => '|',
IntersectionTypeNode::class . '->types' => '&',
ArrayShapeNode::class . '->items' => ', ',
ObjectShapeNode::class . '->items' => ', ',
CallableTypeNode::class . '->parameters' => ', ',
GenericTypeNode::class . '->genericTypes' => ', ',
ConstExprArrayNode::class . '->items' => ', ',
MethodTagValueNode::class . '->parameters' => ', ',
];
/**
* [$find, $extraLeft, $extraRight]
*
* @var array<string, array{string|null, string, string}>
*/
private $emptyListInsertionMap = [
CallableTypeNode::class . '->parameters' => ['(', '', ''],
ArrayShapeNode::class . '->items' => ['{', '', ''],
ObjectShapeNode::class . '->items' => ['{', '', ''],
];
/** @var array<string, list<class-string<TypeNode>>> */
private $parenthesesMap = [
CallableTypeNode::class . '->returnType' => [
CallableTypeNode::class,
UnionTypeNode::class,
IntersectionTypeNode::class,
],
ArrayTypeNode::class . '->type' => [
CallableTypeNode::class,
UnionTypeNode::class,
IntersectionTypeNode::class,
ConstTypeNode::class,
NullableTypeNode::class,
],
OffsetAccessTypeNode::class . '->type' => [
CallableTypeNode::class,
UnionTypeNode::class,
IntersectionTypeNode::class,
ConstTypeNode::class,
NullableTypeNode::class,
],
];
/** @var array<string, list<class-string<TypeNode>>> */
private $parenthesesListMap = [
IntersectionTypeNode::class . '->types' => [
IntersectionTypeNode::class,
UnionTypeNode::class,
NullableTypeNode::class,
],
UnionTypeNode::class . '->types' => [
IntersectionTypeNode::class,
UnionTypeNode::class,
NullableTypeNode::class,
],
];
public function printFormatPreserving(PhpDocNode $node, PhpDocNode $originalNode, TokenIterator $originalTokens): string
{
$this->differ = new Differ(static function ($a, $b) {
if ($a instanceof Node && $b instanceof Node) {
return $a === $b->getAttribute(Attribute::ORIGINAL_NODE);
}
return false;
});
$tokenIndex = 0;
$result = $this->printArrayFormatPreserving(
$node->children,
$originalNode->children,
$originalTokens,
$tokenIndex,
PhpDocNode::class,
'children'
);
if ($result !== null) {
return $result . $originalTokens->getContentBetween($tokenIndex, $originalTokens->getTokenCount());
}
return $this->print($node);
}
public function print(Node $node): string
{
if ($node instanceof PhpDocNode) {
return "/**\n *" . implode("\n *", array_map(
function (PhpDocChildNode $child): string {
$s = $this->print($child);
return $s === '' ? '' : ' ' . $s;
},
$node->children
)) . "\n */";
}
if ($node instanceof PhpDocTextNode) {
return $node->text;
}
if ($node instanceof PhpDocTagNode) {
return trim(sprintf('%s %s', $node->name, $this->print($node->value)));
}
if ($node instanceof PhpDocTagValueNode) {
return $this->printTagValue($node);
}
if ($node instanceof TypeNode) {
return $this->printType($node);
}
if ($node instanceof ConstExprNode) {
return $this->printConstExpr($node);
}
if ($node instanceof MethodTagValueParameterNode) {
$type = $node->type !== null ? $this->print($node->type) . ' ' : '';
$isReference = $node->isReference ? '&' : '';
$isVariadic = $node->isVariadic ? '...' : '';
$default = $node->defaultValue !== null ? ' = ' . $this->print($node->defaultValue) : '';
return "{$type}{$isReference}{$isVariadic}{$node->parameterName}{$default}";
}
if ($node instanceof CallableTypeParameterNode) {
$type = $this->print($node->type) . ' ';
$isReference = $node->isReference ? '&' : '';
$isVariadic = $node->isVariadic ? '...' : '';
$isOptional = $node->isOptional ? '=' : '';
return trim("{$type}{$isReference}{$isVariadic}{$node->parameterName}") . $isOptional;
}
throw new LogicException(sprintf('Unknown node type %s', get_class($node)));
}
private function printTagValue(PhpDocTagValueNode $node): string
{
// only nodes that contain another node are handled here
// the rest falls back on (string) $node
if ($node instanceof AssertTagMethodValueNode) {
$isNegated = $node->isNegated ? '!' : '';
$isEquality = $node->isEquality ? '=' : '';
$type = $this->printType($node->type);
return trim("{$isNegated}{$isEquality}{$type} {$node->parameter}->{$node->method}() {$node->description}");
}
if ($node instanceof AssertTagPropertyValueNode) {
$isNegated = $node->isNegated ? '!' : '';
$isEquality = $node->isEquality ? '=' : '';
$type = $this->printType($node->type);
return trim("{$isNegated}{$isEquality}{$type} {$node->parameter}->{$node->property} {$node->description}");
}
if ($node instanceof AssertTagValueNode) {
$isNegated = $node->isNegated ? '!' : '';
$isEquality = $node->isEquality ? '=' : '';
$type = $this->printType($node->type);
return trim("{$isNegated}{$isEquality}{$type} {$node->parameter} {$node->description}");
}
if ($node instanceof ExtendsTagValueNode || $node instanceof ImplementsTagValueNode) {
$type = $this->printType($node->type);
return trim("{$type} {$node->description}");
}
if ($node instanceof MethodTagValueNode) {
$static = $node->isStatic ? 'static ' : '';
$returnType = $node->returnType !== null ? $this->printType($node->returnType) . ' ' : '';
$parameters = implode(', ', array_map(function (MethodTagValueParameterNode $parameter): string {
return $this->print($parameter);
}, $node->parameters));
$description = $node->description !== '' ? " {$node->description}" : '';
$templateTypes = count($node->templateTypes) > 0 ? '<' . implode(', ', array_map(function (TemplateTagValueNode $templateTag): string {
return $this->print($templateTag);
}, $node->templateTypes)) . '>' : '';
return "{$static}{$returnType}{$node->methodName}{$templateTypes}({$parameters}){$description}";
}
if ($node instanceof MixinTagValueNode) {
$type = $this->printType($node->type);
return trim("{$type} {$node->description}");
}
if ($node instanceof ParamOutTagValueNode) {
$type = $this->printType($node->type);
return trim("{$type} {$node->parameterName} {$node->description}");
}
if ($node instanceof ParamTagValueNode) {
$reference = $node->isReference ? '&' : '';
$variadic = $node->isVariadic ? '...' : '';
$type = $this->printType($node->type);
return trim("{$type} {$reference}{$variadic}{$node->parameterName} {$node->description}");
}
if ($node instanceof PropertyTagValueNode) {
$type = $this->printType($node->type);
return trim("{$type} {$node->propertyName} {$node->description}");
}
if ($node instanceof ReturnTagValueNode) {
$type = $this->printType($node->type);
return trim("{$type} {$node->description}");
}
if ($node instanceof SelfOutTagValueNode) {
$type = $this->printType($node->type);
return trim($type . ' ' . $node->description);
}
if ($node instanceof TemplateTagValueNode) {
$bound = $node->bound !== null ? ' of ' . $this->printType($node->bound) : '';
$default = $node->default !== null ? ' = ' . $this->printType($node->default) : '';
return trim("{$node->name}{$bound}{$default} {$node->description}");
}
if ($node instanceof ThrowsTagValueNode) {
$type = $this->printType($node->type);
return trim("{$type} {$node->description}");
}
if ($node instanceof TypeAliasImportTagValueNode) {
return trim(
"{$node->importedAlias} from " . $this->printType($node->importedFrom)
. ($node->importedAs !== null ? " as {$node->importedAs}" : '')
);
}
if ($node instanceof TypeAliasTagValueNode) {
$type = $this->printType($node->type);
return trim("{$node->alias} {$type}");
}
if ($node instanceof UsesTagValueNode) {
$type = $this->printType($node->type);
return trim("{$type} {$node->description}");
}
if ($node instanceof VarTagValueNode) {
$type = $this->printType($node->type);
return trim("{$type} " . trim("{$node->variableName} {$node->description}"));
}
return (string) $node;
}
private function printType(TypeNode $node): string
{
if ($node instanceof ArrayShapeNode) {
$items = array_map(function (ArrayShapeItemNode $item): string {
return $this->printType($item);
}, $node->items);
if (! $node->sealed) {
$items[] = '...';
}
return $node->kind . '{' . implode(', ', $items) . '}';
}
if ($node instanceof ArrayShapeItemNode) {
if ($node->keyName !== null) {
return sprintf(
'%s%s: %s',
$this->print($node->keyName),
$node->optional ? '?' : '',
$this->printType($node->valueType)
);
}
return $this->printType($node->valueType);
}
if ($node instanceof ArrayTypeNode) {
return $this->printOffsetAccessType($node->type) . '[]';
}
if ($node instanceof CallableTypeNode) {
if ($node->returnType instanceof CallableTypeNode || $node->returnType instanceof UnionTypeNode || $node->returnType instanceof IntersectionTypeNode) {
$returnType = $this->wrapInParentheses($node->returnType);
} else {
$returnType = $this->printType($node->returnType);
}
$parameters = implode(', ', array_map(function (CallableTypeParameterNode $parameterNode): string {
return $this->print($parameterNode);
}, $node->parameters));
return "{$node->identifier}({$parameters}): {$returnType}";
}
if ($node instanceof ConditionalTypeForParameterNode) {
return sprintf(
'(%s %s %s ? %s : %s)',
$node->parameterName,
$node->negated ? 'is not' : 'is',
$this->printType($node->targetType),
$this->printType($node->if),
$this->printType($node->else)
);
}
if ($node instanceof ConditionalTypeNode) {
return sprintf(
'(%s %s %s ? %s : %s)',
$this->printType($node->subjectType),
$node->negated ? 'is not' : 'is',
$this->printType($node->targetType),
$this->printType($node->if),
$this->printType($node->else)
);
}
if ($node instanceof ConstTypeNode) {
return $this->printConstExpr($node->constExpr);
}
if ($node instanceof GenericTypeNode) {
$genericTypes = [];
foreach ($node->genericTypes as $index => $type) {
$variance = $node->variances[$index] ?? GenericTypeNode::VARIANCE_INVARIANT;
if ($variance === GenericTypeNode::VARIANCE_INVARIANT) {
$genericTypes[] = $this->printType($type);
} elseif ($variance === GenericTypeNode::VARIANCE_BIVARIANT) {
$genericTypes[] = '*';
} else {
$genericTypes[] = sprintf('%s %s', $variance, $this->print($type));
}
}
return $node->type . '<' . implode(', ', $genericTypes) . '>';
}
if ($node instanceof IdentifierTypeNode) {
return $node->name;
}
if ($node instanceof IntersectionTypeNode || $node instanceof UnionTypeNode) {
$items = [];
foreach ($node->types as $type) {
if (
$type instanceof IntersectionTypeNode
|| $type instanceof UnionTypeNode
|| $type instanceof NullableTypeNode
) {
$items[] = $this->wrapInParentheses($type);
continue;
}
$items[] = $this->printType($type);
}
return implode($node instanceof IntersectionTypeNode ? '&' : '|', $items);
}
if ($node instanceof InvalidTypeNode) {
return (string) $node;
}
if ($node instanceof NullableTypeNode) {
if ($node->type instanceof IntersectionTypeNode || $node->type instanceof UnionTypeNode) {
return '?(' . $this->printType($node->type) . ')';
}
return '?' . $this->printType($node->type);
}
if ($node instanceof ObjectShapeNode) {
$items = array_map(function (ObjectShapeItemNode $item): string {
return $this->printType($item);
}, $node->items);
return 'object{' . implode(', ', $items) . '}';
}
if ($node instanceof ObjectShapeItemNode) {
if ($node->keyName !== null) {
return sprintf(
'%s%s: %s',
$this->print($node->keyName),
$node->optional ? '?' : '',
$this->printType($node->valueType)
);
}
return $this->printType($node->valueType);
}
if ($node instanceof OffsetAccessTypeNode) {
return $this->printOffsetAccessType($node->type) . '[' . $this->printType($node->offset) . ']';
}
if ($node instanceof ThisTypeNode) {
return (string) $node;
}
throw new LogicException(sprintf('Unknown node type %s', get_class($node)));
}
private function wrapInParentheses(TypeNode $node): string
{
return '(' . $this->printType($node) . ')';
}
private function printOffsetAccessType(TypeNode $type): string
{
if (
$type instanceof CallableTypeNode
|| $type instanceof UnionTypeNode
|| $type instanceof IntersectionTypeNode
|| $type instanceof ConstTypeNode
|| $type instanceof NullableTypeNode
) {
return $this->wrapInParentheses($type);
}
return $this->printType($type);
}
private function printConstExpr(ConstExprNode $node): string
{
// this is fine - ConstExprNode classes do not contain nodes that need smart printer logic
return (string) $node;
}
/**
* @param Node[] $nodes
* @param Node[] $originalNodes
*/
private function printArrayFormatPreserving(array $nodes, array $originalNodes, TokenIterator $originalTokens, int &$tokenIndex, string $parentNodeClass, string $subNodeName): ?string
{
$diff = $this->differ->diffWithReplacements($originalNodes, $nodes);
$mapKey = $parentNodeClass . '->' . $subNodeName;
$insertStr = $this->listInsertionMap[$mapKey] ?? null;
$result = '';
$beforeFirstKeepOrReplace = true;
$delayedAdd = [];
$insertNewline = false;
[$isMultiline, $beforeAsteriskIndent, $afterAsteriskIndent] = $this->isMultiline($tokenIndex, $originalNodes, $originalTokens);
if ($insertStr === "\n * ") {
$insertStr = sprintf("\n%s*%s", $beforeAsteriskIndent, $afterAsteriskIndent);
}
foreach ($diff as $i => $diffElem) {
$diffType = $diffElem->type;
$newNode = $diffElem->new;
$originalNode = $diffElem->old;
if ($diffType === DiffElem::TYPE_KEEP || $diffType === DiffElem::TYPE_REPLACE) {
$beforeFirstKeepOrReplace = false;
if (!$newNode instanceof Node || !$originalNode instanceof Node) {
return null;
}
$itemStartPos = $originalNode->getAttribute(Attribute::START_INDEX);
$itemEndPos = $originalNode->getAttribute(Attribute::END_INDEX);
if ($itemStartPos < 0 || $itemEndPos < 0 || $itemStartPos < $tokenIndex) {
throw new LogicException();
}
$result .= $originalTokens->getContentBetween($tokenIndex, $itemStartPos);
if (count($delayedAdd) > 0) {
foreach ($delayedAdd as $delayedAddNode) {
$parenthesesNeeded = isset($this->parenthesesListMap[$mapKey])
&& in_array(get_class($delayedAddNode), $this->parenthesesListMap[$mapKey], true);
if ($parenthesesNeeded) {
$result .= '(';
}
$result .= $this->printNodeFormatPreserving($delayedAddNode, $originalTokens);
if ($parenthesesNeeded) {
$result .= ')';
}
if ($insertNewline) {
$result .= $insertStr . sprintf("\n%s*%s", $beforeAsteriskIndent, $afterAsteriskIndent);
} else {
$result .= $insertStr;
}
}
$delayedAdd = [];
}
$parenthesesNeeded = isset($this->parenthesesListMap[$mapKey])
&& in_array(get_class($newNode), $this->parenthesesListMap[$mapKey], true);
$addParentheses = $parenthesesNeeded && !$originalTokens->hasParentheses($itemStartPos, $itemEndPos);
if ($addParentheses) {
$result .= '(';
}
$result .= $this->printNodeFormatPreserving($newNode, $originalTokens);
if ($addParentheses) {
$result .= ')';
}
$tokenIndex = $itemEndPos + 1;
} elseif ($diffType === DiffElem::TYPE_ADD) {
if ($insertStr === null) {
return null;
}
if (!$newNode instanceof Node) {
return null;
}
if ($insertStr === ', ' && $isMultiline) {
$insertStr = ',';
$insertNewline = true;
}
if ($beforeFirstKeepOrReplace) {
// Will be inserted at the next "replace" or "keep" element
$delayedAdd[] = $newNode;
continue;
}
$itemEndPos = $tokenIndex - 1;
if ($insertNewline) {
$result .= $insertStr . sprintf("\n%s*%s", $beforeAsteriskIndent, $afterAsteriskIndent);
} else {
$result .= $insertStr;
}
$parenthesesNeeded = isset($this->parenthesesListMap[$mapKey])
&& in_array(get_class($newNode), $this->parenthesesListMap[$mapKey], true);
if ($parenthesesNeeded) {
$result .= '(';
}
$result .= $this->printNodeFormatPreserving($newNode, $originalTokens);
if ($parenthesesNeeded) {
$result .= ')';
}
$tokenIndex = $itemEndPos + 1;
} elseif ($diffType === DiffElem::TYPE_REMOVE) {
if (!$originalNode instanceof Node) {
return null;
}
$itemStartPos = $originalNode->getAttribute(Attribute::START_INDEX);
$itemEndPos = $originalNode->getAttribute(Attribute::END_INDEX);
if ($itemStartPos < 0 || $itemEndPos < 0) {
throw new LogicException();
}
if ($i === 0) {
// If we're removing from the start, keep the tokens before the node and drop those after it,
// instead of the other way around.
$originalTokensArray = $originalTokens->getTokens();
for ($j = $tokenIndex; $j < $itemStartPos; $j++) {
if ($originalTokensArray[$j][Lexer::TYPE_OFFSET] === Lexer::TOKEN_PHPDOC_EOL) {
break;
}
$result .= $originalTokensArray[$j][Lexer::VALUE_OFFSET];
}
}
$tokenIndex = $itemEndPos + 1;
}
}
if (count($delayedAdd) > 0) {
if (!isset($this->emptyListInsertionMap[$mapKey])) {
return null;
}
[$findToken, $extraLeft, $extraRight] = $this->emptyListInsertionMap[$mapKey];
if ($findToken !== null) {
$originalTokensArray = $originalTokens->getTokens();
for (; $tokenIndex < count($originalTokensArray); $tokenIndex++) {
$result .= $originalTokensArray[$tokenIndex][Lexer::VALUE_OFFSET];
if ($originalTokensArray[$tokenIndex][Lexer::VALUE_OFFSET] !== $findToken) {
continue;
}
$tokenIndex++;
break;
}
}
$first = true;
$result .= $extraLeft;
foreach ($delayedAdd as $delayedAddNode) {
if (!$first) {
$result .= $insertStr;
if ($insertNewline) {
$result .= sprintf("\n%s*%s", $beforeAsteriskIndent, $afterAsteriskIndent);
}
}
$result .= $this->printNodeFormatPreserving($delayedAddNode, $originalTokens);
$first = false;
}
$result .= $extraRight;
}
return $result;
}
/**
* @param Node[] $nodes
* @return array{bool, string, string}
*/
private function isMultiline(int $initialIndex, array $nodes, TokenIterator $originalTokens): array
{
$isMultiline = count($nodes) > 1;
$pos = $initialIndex;
$allText = '';
/** @var Node|null $node */
foreach ($nodes as $node) {
if (!$node instanceof Node) {
continue;
}
$endPos = $node->getAttribute(Attribute::END_INDEX) + 1;
$text = $originalTokens->getContentBetween($pos, $endPos);
$allText .= $text;
if (strpos($text, "\n") === false) {
// We require that a newline is present between *every* item. If the formatting
// is inconsistent, with only some items having newlines, we don't consider it
// as multiline
$isMultiline = false;
}
$pos = $endPos;
}
$c = preg_match_all('~\n(?<before>[\\x09\\x20]*)\*(?<after>\\x20*)~', $allText, $matches, PREG_SET_ORDER);
if ($c === 0) {
return [$isMultiline, '', ''];
}
$before = '';
$after = '';
foreach ($matches as $match) {
if (strlen($match['before']) > strlen($before)) {
$before = $match['before'];
}
if (strlen($match['after']) <= strlen($after)) {
continue;
}
$after = $match['after'];
}
return [$isMultiline, $before, $after];
}
private function printNodeFormatPreserving(Node $node, TokenIterator $originalTokens): string
{
/** @var Node|null $originalNode */
$originalNode = $node->getAttribute(Attribute::ORIGINAL_NODE);
if ($originalNode === null) {
return $this->print($node);
}
$class = get_class($node);
if ($class !== get_class($originalNode)) {
throw new LogicException();
}
$startPos = $originalNode->getAttribute(Attribute::START_INDEX);
$endPos = $originalNode->getAttribute(Attribute::END_INDEX);
if ($startPos < 0 || $endPos < 0) {
throw new LogicException();
}
$result = '';
$pos = $startPos;
$subNodeNames = array_keys(get_object_vars($node));
foreach ($subNodeNames as $subNodeName) {
$subNode = $node->$subNodeName;
$origSubNode = $originalNode->$subNodeName;
if (
(!$subNode instanceof Node && $subNode !== null)
|| (!$origSubNode instanceof Node && $origSubNode !== null)
) {
if ($subNode === $origSubNode) {
// Unchanged, can reuse old code
continue;
}
if (is_array($subNode) && is_array($origSubNode)) {
// Array subnode changed, we might be able to reconstruct it
$listResult = $this->printArrayFormatPreserving(
$subNode,
$origSubNode,
$originalTokens,
$pos,
$class,
$subNodeName
);
if ($listResult === null) {
return $this->print($node);
}
$result .= $listResult;
continue;
}
return $this->print($node);
}
if ($origSubNode === null) {
if ($subNode === null) {
// Both null, nothing to do
continue;
}
return $this->print($node);
}
$subStartPos = $origSubNode->getAttribute(Attribute::START_INDEX);
$subEndPos = $origSubNode->getAttribute(Attribute::END_INDEX);
if ($subStartPos < 0 || $subEndPos < 0) {
throw new LogicException();
}
if ($subNode === null) {
return $this->print($node);
}
$result .= $originalTokens->getContentBetween($pos, $subStartPos);
$mapKey = get_class($node) . '->' . $subNodeName;
$parenthesesNeeded = isset($this->parenthesesMap[$mapKey])
&& in_array(get_class($subNode), $this->parenthesesMap[$mapKey], true);
$addParentheses = $parenthesesNeeded && !$originalTokens->hasParentheses($subStartPos, $subEndPos);
if ($addParentheses) {
$result .= '(';
}
$result .= $this->printNodeFormatPreserving($subNode, $originalTokens);
if ($addParentheses) {
$result .= ')';
}
$pos = $subEndPos + 1;
}
return $result . $originalTokens->getContentBetween($pos, $endPos + 1);
}
}

View File

@@ -23,7 +23,6 @@ final class PharAutoloader
self::$composerAutoloader = require 'phar://' . __DIR__ . '/phpstan.phar/vendor/autoload.php';
require_once 'phar://' . __DIR__ . '/phpstan.phar/vendor/jetbrains/phpstorm-stubs/PhpStormStubsMap.php';
require_once 'phar://' . __DIR__ . '/phpstan.phar/vendor/react/async/src/functions_include.php';
require_once 'phar://' . __DIR__ . '/phpstan.phar/vendor/react/promise-stream/src/functions_include.php';
require_once 'phar://' . __DIR__ . '/phpstan.phar/vendor/react/promise-timer/src/functions_include.php';
require_once 'phar://' . __DIR__ . '/phpstan.phar/vendor/react/promise/src/functions_include.php';
require_once 'phar://' . __DIR__ . '/phpstan.phar/vendor/ringcentral/psr7/src/functions_include.php';

Binary file not shown.

View File

@@ -1,16 +1,16 @@
-----BEGIN PGP SIGNATURE-----
iQIzBAABCgAdFiEEynwsejDI6OEnSoR2UcZzBf/C5cAFAmQdeygACgkQUcZzBf/C
5cAe5Q//YDKPvVm4HlLT90M/Ov0IJ8brPP6LN0fbMV93TJlXFhh6Ph79YUwxRWve
FPYr93HDCSOdlg3q3xRNgPcUim5+7U5xf5ze5RWIBR07IDY5+i9Os66CLRALlO4u
ToXHCO2k0hw26sSskRXmF2IrNIibvPjVjR+ephFGFenWI0S7vq5cYGqOnzwdURyu
ZNIswHWQsJSGLn4AXfDnushBCy3w5IsSgnENIWD7L9a37A45kek+iHETcX1OLTOd
AlJOvQ0l2OAE4kMx8tailGYtJo9yLnjtSLw6xQbdw5mf47iapm1U09C52XvYsZg4
oZNCJ8QFHR1YbbLpdMxPFcMQgbVLGKBwHYcpsi2VdMKdR+7altlF9govkvBLxxm7
Polq9ya0fS7wAJP0vMESGeP6UJi68DMWH7hxJ7d9tyBieYJHSpm5q5QSYjj4RxM5
LiTTv9ug8DFIsLJiw1CplE4pxtJ82arXBqggpqO15MRNxwzyJmY7XIHtEZI2dl+d
BImd5bWl6nCkhKEYPs+6SEt/caXIz/XERap5gO9Q8UBx5jaKPHEKRbuvTgbW1Ods
fFNDgIsQyg+56LzIxgp2a6IUVeeQSrL4kjeHYQDBMzG0P78ZXjXdUl3kYG3TJHmb
QNWgYcz3jJJd/F5YSMPpAPT/gpcD4FBnHZ+mlLnGvXvuuGh+MyU=
=vW1l
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
-----END PGP SIGNATURE-----

View File

@@ -2,6 +2,18 @@
All notable changes are documented in this file using the [Keep a CHANGELOG](http://keepachangelog.com/) principles.
## [5.0.3] - 2023-05-01
### Changed
* [#119](https://github.com/sebastianbergmann/diff/pull/119): Improve performance of `TimeEfficientLongestCommonSubsequenceCalculator`
## [5.0.2] - 2023-05-01
### Changed
* [#118](https://github.com/sebastianbergmann/diff/pull/118): Improve performance of `MemoryEfficientLongestCommonSubsequenceCalculator`
## [5.0.1] - 2023-03-23
### Fixed
@@ -92,6 +104,8 @@ All notable changes are documented in this file using the [Keep a CHANGELOG](htt
* This component is no longer supported on PHP 5.6
[5.0.3]: https://github.com/sebastianbergmann/diff/compare/5.0.2...5.0.3
[5.0.2]: https://github.com/sebastianbergmann/diff/compare/5.0.1...5.0.2
[5.0.1]: https://github.com/sebastianbergmann/diff/compare/5.0.0...5.0.1
[5.0.0]: https://github.com/sebastianbergmann/diff/compare/4.0.4...5.0.0
[4.0.4]: https://github.com/sebastianbergmann/diff/compare/4.0.3...4.0.4

View File

@@ -202,3 +202,5 @@ The code above yields the output below:
)
)
)
Note: If the chunk size is 0 lines, i.e., `getStartRange()` or `getEndRange()` return 0, the number of line returned by `getStart()` or `getEnd()` is one lower than one would expect. It is the line number after which the chunk should be inserted or deleted; in all other cases, it gives the first line number of the replaced range of lines.

View File

@@ -4,7 +4,7 @@ If you believe you have found a security vulnerability in the library that is de
**Please do not report security vulnerabilities through public GitHub issues, discussions, or pull requests.**
Instead, please send an email to `sebastian@phpunit.de`.
Instead, please email `sebastian@phpunit.de`.
Please include as much of the information listed below as you can to help us better understand and resolve the issue:
@@ -22,9 +22,9 @@ This information will help us triage your report more quickly.
The library that is developed in this repository was either extracted from [PHPUnit](https://github.com/sebastianbergmann/phpunit) or developed specifically as a dependency for PHPUnit.
The library is developed with a focus on development environments and the command-line. No specific testing or hardening with regard to using the library in a HTTP or web context or with untrusted input data is performed. The library might also contain functionality that intentionally exposes internal application data for debugging purposes.
The library is developed with a focus on development environments and the command-line. No specific testing or hardening with regard to using the library in an HTTP or web context or with untrusted input data is performed. The library might also contain functionality that intentionally exposes internal application data for debugging purposes.
If the library is used in a web application, the application developer is responsible for filtering inputs or escaping outputs as necessary and for verifying that the used functionality is safe for use within the intended context.
Vulnerabilities specific to the use outside of a development context will be fixed as applicable, provided that the fix does not have an averse effect on the primary use case for development purposes.
Vulnerabilities specific to the use outside a development context will be fixed as applicable, provided that the fix does not have an averse effect on the primary use case for development purposes.

View File

@@ -30,14 +30,10 @@ use SebastianBergmann\Diff\Output\DiffOutputBuilderInterface;
final class Differ
{
public const OLD = 0;
public const ADDED = 1;
public const REMOVED = 2;
public const DIFF_LINE_END_WARNING = 3;
public const OLD = 0;
public const ADDED = 1;
public const REMOVED = 2;
public const DIFF_LINE_END_WARNING = 3;
public const NO_LINE_END_EOF_WARNING = 4;
private DiffOutputBuilderInterface $outputBuilder;

View File

@@ -11,10 +11,8 @@ namespace SebastianBergmann\Diff;
final class Line
{
public const ADDED = 1;
public const REMOVED = 2;
public const ADDED = 1;
public const REMOVED = 2;
public const UNCHANGED = 3;
private int $type;
private string $content;

View File

@@ -78,7 +78,12 @@ final class MemoryEfficientLongestCommonSubsequenceCalculator implements Longest
if ($from[$i] === $to[$j]) {
$current[$j + 1] = $prev[$j] + 1;
} else {
$current[$j + 1] = max($current[$j], $prev[$j + 1]);
// don't use max() to avoid function call overhead
if ($current[$j] > $prev[$j + 1]) {
$current[$j + 1] = $current[$j];
} else {
$current[$j + 1] = $prev[$j + 1];
}
}
}
}

View File

@@ -37,12 +37,24 @@ final class TimeEfficientLongestCommonSubsequenceCalculator implements LongestCo
for ($i = 1; $i <= $fromLength; $i++) {
for ($j = 1; $j <= $toLength; $j++) {
$o = ($j * $width) + $i;
$matrix[$o] = max(
$matrix[$o - 1],
$matrix[$o - $width],
$from[$i - 1] === $to[$j - 1] ? $matrix[$o - $width - 1] + 1 : 0
);
$o = ($j * $width) + $i;
// don't use max() to avoid function call overhead
$firstOrLast = $from[$i - 1] === $to[$j - 1] ? $matrix[$o - $width - 1] + 1 : 0;
if ($matrix[$o - 1] > $matrix[$o - $width]) {
if ($firstOrLast > $matrix[$o - 1]) {
$matrix[$o] = $firstOrLast;
} else {
$matrix[$o] = $matrix[$o - 1];
}
} else {
if ($firstOrLast > $matrix[$o - $width]) {
$matrix[$o] = $firstOrLast;
} else {
$matrix[$o] = $matrix[$o - $width];
}
}
}
}

View File

@@ -2,6 +2,23 @@
All notable changes to `array-to-xml` will be documented in this file
## 3.1.5 - 2022-12-24
### What's Changed
- Add Dependabot Automation by @patinthehat in https://github.com/spatie/array-to-xml/pull/196
- Bump actions/checkout from 2 to 3 by @dependabot in https://github.com/spatie/array-to-xml/pull/197
- Fix PHP version by @parallels999 in https://github.com/spatie/array-to-xml/pull/198
- fix deprecated `passing null as string type` by @trin4ik in https://github.com/spatie/array-to-xml/pull/204
### New Contributors
- @dependabot made their first contribution in https://github.com/spatie/array-to-xml/pull/197
- @parallels999 made their first contribution in https://github.com/spatie/array-to-xml/pull/198
- @trin4ik made their first contribution in https://github.com/spatie/array-to-xml/pull/204
**Full Changelog**: https://github.com/spatie/array-to-xml/compare/3.1.4...3.1.5
## 3.1.4 - 2022-11-24
### What's Changed

View File

@@ -1,6 +1,3 @@
[<img src="https://github-ads.s3.eu-central-1.amazonaws.com/support-ukraine.svg?t=1" />](https://supportukrainenow.org)
# Convert an array to xml
[![Latest Version](https://img.shields.io/github/release/spatie/array-to-xml.svg?style=flat-square)](https://github.com/spatie/array-to-xml/releases)

View File

@@ -24,7 +24,8 @@ class ArrayToXml
string | null $xmlEncoding = null,
string $xmlVersion = '1.0',
array $domProperties = [],
bool | null $xmlStandalone = null
bool | null $xmlStandalone = null,
bool $addXmlDeclaration = true
) {
$this->document = new DOMDocument($xmlVersion, $xmlEncoding ?? '');
@@ -36,6 +37,8 @@ class ArrayToXml
$this->setDomProperties($domProperties);
}
$this->addXmlDeclaration = $addXmlDeclaration;
$this->replaceSpacesByUnderScoresInKeyNames = $replaceSpacesByUnderScoresInKeyNames;
if (! empty($array) && $this->isArrayAllKeySequential($array)) {
@@ -61,7 +64,8 @@ class ArrayToXml
string $xmlEncoding = null,
string $xmlVersion = '1.0',
array $domProperties = [],
bool $xmlStandalone = null
bool $xmlStandalone = null,
bool $addXmlDeclaration = true,
): string {
$converter = new static(
$array,
@@ -70,7 +74,8 @@ class ArrayToXml
$xmlEncoding,
$xmlVersion,
$domProperties,
$xmlStandalone
$xmlStandalone,
$addXmlDeclaration
);
return $converter->toXml();
@@ -80,7 +85,7 @@ class ArrayToXml
{
return $this->addXmlDeclaration
? $this->document->saveXML()
: $this->document->saveXml($this->document->documentElement);
: $this->document->saveXML($this->document->documentElement);
}
public function toDom(): DOMDocument

View File

@@ -173,10 +173,10 @@ final class CompleteCommand extends Command
throw $e;
}
return self::FAILURE;
return 2;
}
return self::SUCCESS;
return 0;
}
private function createCompletionInput(InputInterface $input): CompletionInput

View File

@@ -48,14 +48,16 @@ final class DumpCompletionCommand extends Command
$shell = $this->guessShell();
[$rcFile, $completionFile] = match ($shell) {
'fish' => ['~/.config/fish/config.fish', "/etc/fish/completions/$commandName.fish"],
'zsh' => ['~/.zshrc', '$fpath[1]/'.$commandName],
'zsh' => ['~/.zshrc', '$fpath[1]/_'.$commandName],
default => ['~/.bashrc', "/etc/bash_completion.d/$commandName"],
};
$supportedShells = implode(', ', $this->getSupportedShells());
$this
->setHelp(<<<EOH
The <info>%command.name%</> command dumps the shell completion script required
to use shell autocompletion (currently, bash and fish completion is supported).
to use shell autocompletion (currently, {$supportedShells} completion are supported).
<comment>Static installation
-------------------</>
@@ -94,7 +96,7 @@ EOH
if ($input->getOption('debug')) {
$this->tailDebugLog($commandName, $output);
return self::SUCCESS;
return 0;
}
$shell = $input->getArgument('shell') ?? self::guessShell();
@@ -111,12 +113,12 @@ EOH
$output->writeln(sprintf('<error>Shell not detected, Symfony shell completion only supports "%s").</>', implode('", "', $supportedShells)));
}
return self::INVALID;
return 2;
}
$output->write(str_replace(['{{ COMMAND_NAME }}', '{{ VERSION }}'], [$commandName, CompleteCommand::COMPLETION_API_VERSION], file_get_contents($completionFile)));
return self::SUCCESS;
return 0;
}
private static function guessShell(): string
@@ -141,8 +143,19 @@ EOH
*/
private function getSupportedShells(): array
{
return $this->supportedShells ??= array_map(function ($f) {
return pathinfo($f, \PATHINFO_EXTENSION);
}, glob(__DIR__.'/../Resources/completion.*'));
if (isset($this->supportedShells)) {
return $this->supportedShells;
}
$shells = [];
foreach (new \DirectoryIterator(__DIR__.'/../Resources/') as $file) {
if (str_starts_with($file->getBasename(), 'completion.') && $file->isFile()) {
$shells[] = $file->getExtension();
}
}
sort($shells);
return $this->supportedShells = $shells;
}
}

View File

@@ -83,7 +83,8 @@ class OutputFormatterStyle implements OutputFormatterStyleInterface
public function apply(string $text): string
{
$this->handlesHrefGracefully ??= 'JetBrains-JediTerm' !== getenv('TERMINAL_EMULATOR')
&& (!getenv('KONSOLE_VERSION') || (int) getenv('KONSOLE_VERSION') > 201100);
&& (!getenv('KONSOLE_VERSION') || (int) getenv('KONSOLE_VERSION') > 201100)
&& !isset($_SERVER['IDEA_INITIAL_DIRECTORY']);
if (null !== $this->href && $this->handlesHrefGracefully) {
$text = "\033]8;;$this->href\033\\$text\033]8;;\033\\";

View File

@@ -123,7 +123,18 @@ class QuestionHelper extends Helper
}
if (false === $ret) {
$isBlocked = stream_get_meta_data($inputStream)['blocked'] ?? true;
if (!$isBlocked) {
stream_set_blocking($inputStream, true);
}
$ret = $this->readInput($inputStream, $question);
if (!$isBlocked) {
stream_set_blocking($inputStream, false);
}
if (false === $ret) {
throw new MissingInputException('Aborted.');
}
@@ -496,13 +507,11 @@ class QuestionHelper extends Helper
return self::$stdinIsInteractive = @posix_isatty(fopen('php://stdin', 'r'));
}
if (!\function_exists('exec')) {
if (!\function_exists('shell_exec')) {
return self::$stdinIsInteractive = true;
}
exec('stty 2> /dev/null', $output, $status);
return self::$stdinIsInteractive = 1 !== $status;
return self::$stdinIsInteractive = (bool) shell_exec('stty 2> '.('\\' === \DIRECTORY_SEPARATOR ? 'NUL' : '/dev/null'));
}
/**

View File

@@ -804,7 +804,7 @@ class Table
$textContent = Helper::removeDecoration($this->output->getFormatter(), $cell);
$textLength = Helper::width($textContent);
if ($textLength > 0) {
$contentColumns = str_split($textContent, ceil($textLength / $cell->getColspan()));
$contentColumns = mb_str_split($textContent, ceil($textLength / $cell->getColspan()));
foreach ($contentColumns as $position => $content) {
$row[$i + $position] = $content;
}

View File

@@ -37,7 +37,7 @@ class InputArgument
/**
* @param string $name The argument name
* @param int|null $mode The argument mode: self::REQUIRED or self::OPTIONAL
* @param int|null $mode The argument mode: a bit mask of self::REQUIRED, self::OPTIONAL and self::IS_ARRAY
* @param string $description A description text
* @param string|bool|int|float|array|null $default The default value (for self::OPTIONAL mode only)
* @param array|\Closure(CompletionInput,CompletionSuggestions):list<string|Suggestion> $suggestedValues The values used for input completion

View File

@@ -1,3 +1,5 @@
#compdef {{ COMMAND_NAME }}
# This file is part of the Symfony package.
#
# (c) Fabien Potencier <fabien@symfony.com>

View File

@@ -123,20 +123,19 @@ class Terminal
return self::$stty;
}
// skip check if exec function is disabled
if (!\function_exists('exec')) {
// skip check if shell_exec function is disabled
if (!\function_exists('shell_exec')) {
return false;
}
exec('stty 2>&1', $output, $exitcode);
return self::$stty = 0 === $exitcode;
return self::$stty = (bool) shell_exec('stty 2> '.('\\' === \DIRECTORY_SEPARATOR ? 'NUL' : '/dev/null'));
}
private static function initDimensions()
{
if ('\\' === \DIRECTORY_SEPARATOR) {
if (preg_match('/^(\d+)x(\d+)(?: \((\d+)x(\d+)\))?$/', trim(getenv('ANSICON')), $matches)) {
$ansicon = getenv('ANSICON');
if (false !== $ansicon && preg_match('/^(\d+)x(\d+)(?: \((\d+)x(\d+)\))?$/', trim($ansicon), $matches)) {
// extract [w, H] from "wxh (WxH)"
// or [w, h] from "wxh"
self::$width = (int) $matches[1];
@@ -216,6 +215,8 @@ class Terminal
2 => ['pipe', 'w'],
];
$cp = \function_exists('sapi_windows_cp_set') ? sapi_windows_cp_get() : 0;
$process = proc_open($command, $descriptorspec, $pipes, null, null, ['suppress_errors' => true]);
if (!\is_resource($process)) {
return null;
@@ -226,6 +227,10 @@ class Terminal
fclose($pipes[2]);
proc_close($process);
if ($cp) {
sapi_windows_cp_set($cp);
}
return $info;
}
}

View File

@@ -2,7 +2,7 @@
"name": "symfony/console",
"type": "library",
"description": "Eases the creation of beautiful and testable command line interfaces",
"keywords": ["console", "cli", "command line", "terminal"],
"keywords": ["console", "cli", "command-line", "terminal"],
"homepage": "https://symfony.com",
"license": "MIT",
"authors": [

View File

@@ -161,7 +161,7 @@ class Filesystem
}
} elseif (is_dir($file)) {
if (!$isRecursive) {
$tmpName = \dirname(realpath($file)).'/.'.strrev(strtr(base64_encode(random_bytes(2)), '/=', '-.'));
$tmpName = \dirname(realpath($file)).'/.'.strrev(strtr(base64_encode(random_bytes(2)), '/=', '-_'));
if (file_exists($tmpName)) {
try {

View File

@@ -55,6 +55,9 @@ final class EnglishInflector implements InflectorInterface
// indices (index), appendices (appendix), prices (price)
['seci', 4, false, true, ['ex', 'ix', 'ice']],
// codes (code)
['sedoc', 5, false, true, 'code'],
// selfies (selfie)
['seifles', 7, true, true, 'selfie'],
@@ -64,6 +67,9 @@ final class EnglishInflector implements InflectorInterface
// movies (movie)
['seivom', 6, true, true, 'movie'],
// names (name)
['seman', 5, true, false, 'name'],
// conspectuses (conspectus), prospectuses (prospectus)
['sesutcep', 8, true, true, 'pectus'],

View File

@@ -44,6 +44,7 @@
},
"require-dev": {
"ext-curl": "*",
"amphp/phpunit-util": "^2.0",
"bamarni/composer-bin-plugin": "^1.4",
"brianium/paratest": "^6.9",
"mockery/mockery": "^1.5",

View File

@@ -62,6 +62,7 @@
<xs:attribute name="reportMixedIssues" type="xs:boolean" default="true" />
<xs:attribute name="useDocblockTypes" type="xs:boolean" default="true" />
<xs:attribute name="useDocblockPropertyTypes" type="xs:boolean" default="false" />
<xs:attribute name="docblockPropertyTypesSealProperties" type="xs:boolean" default="true" />
<xs:attribute name="usePhpDocMethodsWithoutMagicCall" type="xs:boolean" default="false" />
<xs:attribute name="usePhpDocPropertiesWithoutMagicCall" type="xs:boolean" default="false" />
<xs:attribute name="skipChecksOnUnresolvableIncludes" type="xs:boolean" default="false" />
@@ -249,6 +250,7 @@
<xs:element name="InaccessibleClassConstant" type="IssueHandlerType" minOccurs="0" />
<xs:element name="InaccessibleMethod" type="MethodIssueHandlerType" minOccurs="0" />
<xs:element name="InaccessibleProperty" type="IssueHandlerType" minOccurs="0" />
<xs:element name="InheritorViolation" type="ClassIssueHandlerType" minOccurs="0" />
<xs:element name="InterfaceInstantiation" type="IssueHandlerType" minOccurs="0" />
<xs:element name="InternalClass" type="ClassIssueHandlerType" minOccurs="0" />
<xs:element name="InternalMethod" type="MethodIssueHandlerType" minOccurs="0" />
@@ -480,6 +482,7 @@
<xs:element name="UnresolvableInclude" type="IssueHandlerType" minOccurs="0" />
<xs:element name="UnsafeGenericInstantiation" type="IssueHandlerType" minOccurs="0" />
<xs:element name="UnsafeInstantiation" type="IssueHandlerType" minOccurs="0" />
<xs:element name="UnsupportedPropertyReferenceUsage" type="IssueHandlerType" minOccurs="0" />
<xs:element name="UnsupportedReferenceUsage" type="IssueHandlerType" minOccurs="0" />
<xs:element name="UnusedBaselineEntry" type="IssueHandlerType" minOccurs="0" />
<xs:element name="UnusedClass" type="ClassIssueHandlerType" minOccurs="0" />

View File

@@ -418,9 +418,7 @@ return [
'array_uintersect_assoc\'1' => ['array', 'array'=>'array', 'rest'=>'array', 'arr3'=>'array', 'arg4'=>'array|callable', '...rest='=>'array|callable(mixed,mixed):int'],
'array_uintersect_uassoc' => ['array', 'array'=>'array', 'rest'=>'array', 'data_compare_func'=>'callable(mixed,mixed):int', 'key_compare_func'=>'callable(mixed,mixed):int'],
'array_uintersect_uassoc\'1' => ['array', 'array'=>'array', 'rest'=>'array', 'arr3'=>'array', 'arg4'=>'array|callable(mixed,mixed):int', 'arg5'=>'array|callable(mixed,mixed):int', '...rest='=>'array|callable(mixed,mixed):int'],
'array_unique' => ['array', 'array'=>'array', 'flags='=>'0'],
'array_unique\'1' => ['array<int|float|string|null>', 'array'=>'array<int|float|string|null>', 'flags='=>'1'],
'array_unique\'2' => ['array<int|float|string|bool|\Stringable|null>', 'array'=>'array<int|float|string|bool|\Stringable|null>', 'flags='=>'2|5'],
'array_unique' => ['array', 'array'=>'array', 'flags='=>'int'],
'array_unshift' => ['int', '&rw_array'=>'array', '...values='=>'mixed'],
'array_values' => ['list<mixed>', 'array'=>'array'],
'array_walk' => ['bool', '&rw_array'=>'array', 'callback'=>'callable', 'arg='=>'mixed'],
@@ -612,7 +610,7 @@ return [
'CallbackFilterIterator::next' => ['void'],
'CallbackFilterIterator::rewind' => ['void'],
'CallbackFilterIterator::valid' => ['bool'],
'ceil' => ['float', 'num'=>'float'],
'ceil' => ['float', 'num'=>'float|int'],
'chdb::__construct' => ['void', 'pathname'=>'string'],
'chdb::get' => ['string', 'key'=>'string'],
'chdb_create' => ['bool', 'pathname'=>'string', 'data'=>'array'],
@@ -1260,7 +1258,7 @@ return [
'cubrid_unbuffered_query' => ['resource', 'query'=>'string', 'conn_identifier='=>''],
'cubrid_version' => ['string'],
'curl_close' => ['void', 'handle'=>'CurlHandle'],
'curl_copy_handle' => ['CurlHandle', 'handle'=>'CurlHandle'],
'curl_copy_handle' => ['CurlHandle|false', 'handle'=>'CurlHandle'],
'curl_errno' => ['int', 'handle'=>'CurlHandle'],
'curl_error' => ['string', 'handle'=>'CurlHandle'],
'curl_escape' => ['string|false', 'handle'=>'CurlHandle', 'string'=>'string'],
@@ -1293,7 +1291,6 @@ return [
'curl_unescape' => ['string|false', 'handle'=>'CurlHandle', 'string'=>'string'],
'curl_version' => ['array', 'version='=>'int'],
'CURLFile::__construct' => ['void', 'filename'=>'string', 'mime_type='=>'?string', 'posted_filename='=>'?string'],
'CURLFile::__wakeup' => ['void'],
'CURLFile::getFilename' => ['string'],
'CURLFile::getMimeType' => ['string'],
'CURLFile::getPostFilename' => ['string'],
@@ -1321,7 +1318,7 @@ return [
'date_get_last_errors' => ['array{warning_count:int,warnings:array<int,string>,error_count:int,errors:array<int,string>}|false'],
'date_interval_create_from_date_string' => ['DateInterval', 'datetime'=>'string'],
'date_interval_format' => ['string', 'object'=>'DateInterval', 'format'=>'string'],
'date_isodate_set' => ['DateTime|false', 'object'=>'DateTime', 'year'=>'int', 'week'=>'int', 'dayOfWeek='=>'int|mixed'],
'date_isodate_set' => ['DateTime', 'object'=>'DateTime', 'year'=>'int', 'week'=>'int', 'dayOfWeek='=>'int'],
'date_modify' => ['DateTime|false', 'object'=>'DateTime', 'modifier'=>'string'],
'date_offset_get' => ['int', 'object'=>'DateTimeInterface'],
'date_parse' => ['array', 'datetime'=>'string'],
@@ -1350,7 +1347,7 @@ return [
'datefmt_get_timezone_id' => ['string|false', 'formatter'=>'IntlDateFormatter'],
'datefmt_is_lenient' => ['bool', 'formatter'=>'IntlDateFormatter'],
'datefmt_localtime' => ['array|false', 'formatter'=>'IntlDateFormatter', 'string'=>'string', '&rw_offset='=>'int'],
'datefmt_parse' => ['int|false', 'formatter'=>'IntlDateFormatter', 'string'=>'string', '&rw_offset='=>'int'],
'datefmt_parse' => ['float|int|false', 'formatter'=>'IntlDateFormatter', 'string'=>'string', '&rw_offset='=>'int'],
'datefmt_set_calendar' => ['bool', 'formatter'=>'IntlDateFormatter', 'calendar'=>'IntlCalendar|int|null'],
'datefmt_set_lenient' => ['void', 'formatter'=>'IntlDateFormatter', 'lenient'=>'bool'],
'datefmt_set_pattern' => ['bool', 'formatter'=>'IntlDateFormatter', 'pattern'=>'string'],
@@ -1463,20 +1460,20 @@ return [
'db2_tableprivileges' => [''],
'db2_tables' => ['resource|false', 'connection'=>'resource', 'qualifier='=>'?string', 'schema='=>'?string', 'table_name='=>'?string', 'table_type='=>'?string'],
'dba_close' => ['void', 'dba'=>'resource'],
'dba_delete' => ['bool', 'key'=>'string', 'dba'=>'resource'],
'dba_exists' => ['bool', 'key'=>'string', 'dba'=>'resource'],
'dba_fetch' => ['string|false', 'key'=>'string', 'skip'=>'int', 'dba'=>'resource'],
'dba_fetch\'1' => ['string|false', 'key'=>'string', 'skip'=>'resource'],
'dba_delete' => ['bool', 'key'=>'array|string', 'dba'=>'resource'],
'dba_exists' => ['bool', 'key'=>'array|string', 'dba'=>'resource'],
'dba_fetch' => ['string|false', 'key'=>'array|string', 'skip'=>'int', 'dba'=>'resource'],
'dba_fetch\'1' => ['string|false', 'key'=>'array|string', 'skip'=>'resource'],
'dba_firstkey' => ['string', 'dba'=>'resource'],
'dba_handlers' => ['array', 'full_info='=>'bool'],
'dba_insert' => ['bool', 'key'=>'string', 'value'=>'string', 'dba'=>'resource'],
'dba_insert' => ['bool', 'key'=>'array|string', 'value'=>'string', 'dba'=>'resource'],
'dba_key_split' => ['array|false', 'key'=>'string|false|null'],
'dba_list' => ['array'],
'dba_nextkey' => ['string', 'dba'=>'resource'],
'dba_open' => ['resource', 'path'=>'string', 'mode'=>'string', 'handler='=>'?string', 'permission='=>'int', 'map_size='=>'int', 'flags='=>'?int'],
'dba_optimize' => ['bool', 'dba'=>'resource'],
'dba_popen' => ['resource', 'path'=>'string', 'mode'=>'string', 'handler='=>'?string', 'permission='=>'int', 'map_size='=>'int', 'flags='=>'?int'],
'dba_replace' => ['bool', 'key'=>'string', 'value'=>'string', 'dba'=>'resource'],
'dba_replace' => ['bool', 'key'=>'array|string', 'value'=>'string', 'dba'=>'resource'],
'dba_sync' => ['bool', 'dba'=>'resource'],
'dbase_add_record' => ['bool', 'dbase_identifier'=>'resource', 'record'=>'array'],
'dbase_close' => ['bool', 'dbase_identifier'=>'resource'],
@@ -1990,7 +1987,7 @@ return [
'Ds\Vector::sum' => ['int|float'],
'Ds\Vector::toArray' => ['array'],
'Ds\Vector::unshift' => ['void', '...values='=>'mixed'],
'easter_date' => ['int', 'year='=>'?int'],
'easter_date' => ['int', 'year='=>'?int', 'mode='=>'int'],
'easter_days' => ['int', 'year='=>'?int', 'mode='=>'int'],
'echo' => ['void', 'arg1'=>'string', '...args='=>'string'],
'eio_busy' => ['resource', 'delay'=>'int', 'pri='=>'int', 'callback='=>'callable', 'data='=>'mixed'],
@@ -2882,7 +2879,7 @@ return [
'finfo_set_flags' => ['bool', 'finfo'=>'finfo', 'flags'=>'int'],
'floatval' => ['float', 'value'=>'mixed'],
'flock' => ['bool', 'stream'=>'resource', 'operation'=>'int', '&w_would_block='=>'int'],
'floor' => ['float', 'num'=>'float'],
'floor' => ['float', 'num'=>'float|int'],
'flush' => ['void'],
'fmod' => ['float', 'num1'=>'float', 'num2'=>'float'],
'fnmatch' => ['bool', 'pattern'=>'string', 'filename'=>'string', 'flags='=>'int'],
@@ -3159,7 +3156,6 @@ return [
'Gender\Gender::get' => ['int', 'name'=>'string', 'country='=>'int'],
'Gender\Gender::isNick' => ['array', 'name0'=>'string', 'name1'=>'string', 'country='=>'int'],
'Gender\Gender::similarNames' => ['array', 'name'=>'string', 'country='=>'int'],
'Generator::__wakeup' => ['void'],
'Generator::current' => ['mixed'],
'Generator::getReturn' => ['mixed'],
'Generator::key' => ['mixed'],
@@ -3280,8 +3276,8 @@ return [
'get_called_class' => ['class-string'],
'get_cfg_var' => ['string|false', 'option'=>'string'],
'get_class' => ['class-string', 'object='=>'object'],
'get_class_methods' => ['list<string>', 'object_or_class'=>'object|class-string'],
'get_class_vars' => ['array<string,mixed>', 'class'=>'string'],
'get_class_methods' => ['list<non-falsy-string>', 'object_or_class'=>'object|class-string'],
'get_class_vars' => ['array<non-falsy-string,mixed>', 'class'=>'string'],
'get_current_user' => ['string'],
'get_debug_type' => ['string', 'value'=>'mixed'],
'get_declared_classes' => ['list<class-string>'],
@@ -3333,7 +3329,7 @@ return [
'gettimeofday' => ['array<string, int>'],
'gettimeofday\'1' => ['float', 'as_float='=>'true'],
'gettype' => ['string', 'value'=>'mixed'],
'glob' => ['list<string>|false', 'pattern'=>'string', 'flags='=>'int'],
'glob' => ['list<non-empty-string>|false', 'pattern'=>'non-empty-string', 'flags='=>'int<1, max>'],
'GlobIterator::__construct' => ['void', 'pattern'=>'string', 'flags='=>'int'],
'GlobIterator::count' => ['int'],
'GlobIterator::current' => ['FilesystemIterator|SplFileInfo|string'],
@@ -3555,9 +3551,7 @@ return [
'GmagickPixel::setcolorvalue' => ['GmagickPixel', 'color'=>'int', 'value'=>'float'],
'gmdate' => ['string', 'format'=>'string', 'timestamp='=>'int|null'],
'gmmktime' => ['int|false', 'hour'=>'int', 'minute='=>'int|null', 'second='=>'int|null', 'month='=>'int|null', 'day='=>'int|null', 'year='=>'int|null'],
'GMP::__construct' => ['void'],
'GMP::__serialize' => ['array'],
'GMP::__toString' => ['numeric-string'],
'GMP::__unserialize' => ['void', 'data'=>'array'],
'gmp_abs' => ['GMP', 'num'=>'GMP|string|int'],
'gmp_add' => ['GMP', 'num1'=>'GMP|string|int', 'num2'=>'GMP|string|int'],
@@ -3742,19 +3736,19 @@ return [
'gzdeflate' => ['string|false', 'data'=>'string', 'level='=>'int', 'encoding='=>'int'],
'gzencode' => ['string|false', 'data'=>'string', 'level='=>'int', 'encoding='=>'int'],
'gzeof' => ['bool', 'stream'=>'resource'],
'gzfile' => ['list<string>', 'filename'=>'string', 'use_include_path='=>'int'],
'gzfile' => ['list<string>|false', 'filename'=>'string', 'use_include_path='=>'int'],
'gzgetc' => ['string|false', 'stream'=>'resource'],
'gzgets' => ['string|false', 'stream'=>'resource', 'length='=>'?int'],
'gzinflate' => ['string|false', 'data'=>'string', 'max_length='=>'int'],
'gzopen' => ['resource|false', 'filename'=>'string', 'mode'=>'string', 'use_include_path='=>'int'],
'gzpassthru' => ['int', 'stream'=>'resource'],
'gzputs' => ['int', 'stream'=>'resource', 'data'=>'string', 'length='=>'?int'],
'gzputs' => ['int|false', 'stream'=>'resource', 'data'=>'string', 'length='=>'?int'],
'gzread' => ['string|false', 'stream'=>'resource', 'length'=>'int'],
'gzrewind' => ['bool', 'stream'=>'resource'],
'gzseek' => ['int', 'stream'=>'resource', 'offset'=>'int', 'whence='=>'int'],
'gztell' => ['int|false', 'stream'=>'resource'],
'gzuncompress' => ['string|false', 'data'=>'string', 'max_length='=>'int'],
'gzwrite' => ['int', 'stream'=>'resource', 'data'=>'string', 'length='=>'?int'],
'gzwrite' => ['int|false', 'stream'=>'resource', 'data'=>'string', 'length='=>'?int'],
'HaruAnnotation::setBorderStyle' => ['bool', 'width'=>'float', 'dash_on'=>'int', 'dash_off'=>'int'],
'HaruAnnotation::setHighlightMode' => ['bool', 'mode'=>'int'],
'HaruAnnotation::setIcon' => ['bool', 'icon'=>'int'],
@@ -6129,10 +6123,10 @@ return [
'Iterator::rewind' => ['void'],
'Iterator::valid' => ['bool'],
'iterator_apply' => ['0|positive-int', 'iterator'=>'Traversable', 'callback'=>'callable(mixed):bool', 'args='=>'?array'],
'iterator_count' => ['0|positive-int', 'iterator'=>'Traversable'],
'iterator_to_array' => ['array', 'iterator'=>'Traversable', 'preserve_keys='=>'bool'],
'iterator_count' => ['0|positive-int', 'iterator'=>'Traversable|array'],
'iterator_to_array' => ['array', 'iterator'=>'Traversable|array', 'preserve_keys='=>'bool'],
'IteratorAggregate::getIterator' => ['Traversable'],
'IteratorIterator::__construct' => ['void', 'it'=>'Traversable'],
'IteratorIterator::__construct' => ['void', 'iterator'=>'Traversable', 'class='=>'?string'],
'IteratorIterator::current' => ['mixed'],
'IteratorIterator::getInnerIterator' => ['Iterator'],
'IteratorIterator::key' => ['mixed'],
@@ -6164,7 +6158,7 @@ return [
'json_last_error_msg' => ['string'],
'json_validate' => ['bool', 'json'=>'string', 'depth='=>'positive-int', 'flags='=>'int'],
'JsonException::__clone' => ['void'],
'JsonException::__construct' => ['void'],
'JsonException::__construct' => ['void', "message="=>"string", 'code='=>'int', 'previous='=>'?Throwable'],
'JsonException::__toString' => ['string'],
'JsonException::__wakeup' => ['void'],
'JsonException::getCode' => ['int'],
@@ -6339,7 +6333,7 @@ return [
'ldap_count_entries' => ['int', 'ldap'=>'LDAP\Connection', 'result'=>'LDAP\Result'],
'ldap_delete' => ['bool', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'controls='=>'?array'],
'ldap_delete_ext' => ['LDAP\Result|false', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'controls='=>'?array'],
'ldap_dn2ufn' => ['string', 'dn'=>'string'],
'ldap_dn2ufn' => ['string|false', 'dn'=>'string'],
'ldap_err2str' => ['string', 'errno'=>'int'],
'ldap_errno' => ['int', 'ldap'=>'LDAP\Connection'],
'ldap_error' => ['string', 'ldap'=>'LDAP\Connection'],
@@ -6359,7 +6353,7 @@ return [
'ldap_get_option' => ['bool', 'ldap'=>'LDAP\Connection', 'option'=>'int', '&w_value='=>'array|string|int'],
'ldap_get_values' => ['array|false', 'ldap'=>'LDAP\Connection', 'entry'=>'LDAP\ResultEntry', 'attribute'=>'string'],
'ldap_get_values_len' => ['array|false', 'ldap'=>'LDAP\Connection', 'entry'=>'LDAP\ResultEntry', 'attribute'=>'string'],
'ldap_list' => ['LDAP\Result|LDAP\Result[]|false', 'ldap'=>'LDAP\Connection|LDAP\Connection[]', 'base'=>'string', 'filter'=>'string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int', 'controls='=>'?array'],
'ldap_list' => ['LDAP\Result|LDAP\Result[]|false', 'ldap'=>'LDAP\Connection|LDAP\Connection[]', 'base'=>'array|string', 'filter'=>'array|string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int', 'controls='=>'?array'],
'ldap_mod_add' => ['bool', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'entry'=>'array', 'controls='=>'?array'],
'ldap_mod_add_ext' => ['LDAP\Result|false', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'entry'=>'array', 'controls='=>'?array'],
'ldap_mod_del' => ['bool', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'entry'=>'array', 'controls='=>'?array'],
@@ -6374,11 +6368,11 @@ return [
'ldap_parse_exop' => ['bool', 'ldap'=>'LDAP\Connection', 'result'=>'LDAP\Result', '&w_response_data='=>'string', '&w_response_oid='=>'string'],
'ldap_parse_reference' => ['bool', 'ldap'=>'LDAP\Connection', 'entry'=>'LDAP\ResultEntry', '&w_referrals'=>'array'],
'ldap_parse_result' => ['bool', 'ldap'=>'LDAP\Connection', 'result'=>'LDAP\Result', '&w_error_code'=>'int', '&w_matched_dn='=>'string', '&w_error_message='=>'string', '&w_referrals='=>'array', '&w_controls='=>'array'],
'ldap_read' => ['LDAP\Result|LDAP\Result[]|false', 'ldap'=>'LDAP\Connection|LDAP\Connection[]', 'base'=>'string', 'filter'=>'string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int', 'controls='=>'?array'],
'ldap_read' => ['LDAP\Result|LDAP\Result[]|false', 'ldap'=>'LDAP\Connection|LDAP\Connection[]', 'base'=>'array|string', 'filter'=>'array|string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int', 'controls='=>'?array'],
'ldap_rename' => ['bool', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'new_rdn'=>'string', 'new_parent'=>'string', 'delete_old_rdn'=>'bool', 'controls='=>'?array'],
'ldap_rename_ext' => ['LDAP\Result|false', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'new_rdn'=>'string', 'new_parent'=>'string', 'delete_old_rdn'=>'bool', 'controls='=>'?array'],
'ldap_sasl_bind' => ['bool', 'ldap'=>'LDAP\Connection', 'dn='=>'?string', 'password='=>'?string', 'mech='=>'?string', 'realm='=>'?string', 'authc_id='=>'?string', 'authz_id='=>'?string', 'props='=>'?string'],
'ldap_search' => ['LDAP\Result|LDAP\Result[]|false', 'ldap'=>'LDAP\Connection|LDAP\Connection[]', 'base'=>'string', 'filter'=>'string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int', 'controls='=>'?array'],
'ldap_search' => ['LDAP\Result|LDAP\Result[]|false', 'ldap'=>'LDAP\Connection|LDAP\Connection[]', 'base'=>'array|string', 'filter'=>'array|string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int', 'controls='=>'?array'],
'ldap_set_option' => ['bool', 'ldap'=>'LDAP\Connection|null', 'option'=>'int', 'value'=>'mixed'],
'ldap_set_rebind_proc' => ['bool', 'ldap'=>'LDAP\Connection', 'callback'=>'?callable'],
'ldap_start_tls' => ['bool', 'ldap'=>'LDAP\Connection'],
@@ -6442,14 +6436,14 @@ return [
'libxml_set_external_entity_loader' => ['bool', 'resolver_function'=>'(callable(string,string,array{directory:?string,intSubName:?string,extSubURI:?string,extSubSystem:?string}):(resource|string|null))|null'],
'libxml_set_streams_context' => ['void', 'context'=>'resource'],
'libxml_use_internal_errors' => ['bool', 'use_errors='=>'?bool'],
'LimitIterator::__construct' => ['void', 'iterator'=>'Iterator', 'offset='=>'int', 'count='=>'int'],
'LimitIterator::__construct' => ['void', 'iterator'=>'Iterator', 'offset='=>'int', 'limit='=>'int'],
'LimitIterator::current' => ['mixed'],
'LimitIterator::getInnerIterator' => ['Iterator'],
'LimitIterator::getPosition' => ['int'],
'LimitIterator::key' => ['mixed'],
'LimitIterator::next' => ['void'],
'LimitIterator::rewind' => ['void'],
'LimitIterator::seek' => ['int', 'position'=>'int'],
'LimitIterator::seek' => ['int', 'offset'=>'int'],
'LimitIterator::valid' => ['bool'],
'lineObj::__construct' => ['void'],
'lineObj::add' => ['int', 'point'=>'pointObj'],
@@ -6848,14 +6842,14 @@ return [
'memory_reset_peak_usage' => ['void'],
'MessageFormatter::__construct' => ['void', 'locale'=>'string', 'pattern'=>'string'],
'MessageFormatter::create' => ['MessageFormatter', 'locale'=>'string', 'pattern'=>'string'],
'MessageFormatter::format' => ['false|string', 'args'=>'array'],
'MessageFormatter::formatMessage' => ['false|string', 'locale'=>'string', 'pattern'=>'string', 'args'=>'array'],
'MessageFormatter::format' => ['false|string', 'values'=>'array'],
'MessageFormatter::formatMessage' => ['false|string', 'locale'=>'string', 'pattern'=>'string', 'values'=>'array'],
'MessageFormatter::getErrorCode' => ['int'],
'MessageFormatter::getErrorMessage' => ['string'],
'MessageFormatter::getLocale' => ['string'],
'MessageFormatter::getPattern' => ['string'],
'MessageFormatter::parse' => ['array|false', 'value'=>'string'],
'MessageFormatter::parseMessage' => ['array|false', 'locale'=>'string', 'pattern'=>'string', 'source'=>'string'],
'MessageFormatter::parse' => ['array|false', 'string'=>'string'],
'MessageFormatter::parseMessage' => ['array|false', 'locale'=>'string', 'pattern'=>'string', 'message'=>'string'],
'MessageFormatter::setPattern' => ['bool', 'pattern'=>'string'],
'metaphone' => ['string', 'string'=>'string', 'max_phonemes='=>'int'],
'method_exists' => ['bool', 'object_or_class'=>'object|class-string|interface-string|enum-string', 'method'=>'string'],
@@ -7065,7 +7059,7 @@ return [
'MongoDB::setReadPreference' => ['bool', 'read_preference'=>'string', 'tags='=>'array'],
'MongoDB::setSlaveOkay' => ['bool', 'ok='=>'bool'],
'MongoDB::setWriteConcern' => ['bool', 'w'=>'mixed', 'wtimeout='=>'int'],
'MongoDB\BSON\Binary::__construct' => ['void', 'data' => 'string', 'type' => 'int'],
'MongoDB\BSON\Binary::__construct' => ['void', 'data' => 'string', 'type=' => 'int'],
'MongoDB\BSON\Binary::getData' => ['string'],
'MongoDB\BSON\Binary::getType' => ['int'],
'MongoDB\BSON\Binary::__toString' => ['string'],
@@ -7623,7 +7617,7 @@ return [
'mt_rand\'1' => ['int'],
'mt_srand' => ['void', 'seed='=>'int', 'mode='=>'int'],
'MultipleIterator::__construct' => ['void', 'flags='=>'int'],
'MultipleIterator::attachIterator' => ['void', 'iterator'=>'Iterator', 'infos='=>'string'],
'MultipleIterator::attachIterator' => ['void', 'iterator'=>'Iterator', 'info='=>'string|int|null'],
'MultipleIterator::containsIterator' => ['bool', 'iterator'=>'Iterator'],
'MultipleIterator::countIterators' => ['int'],
'MultipleIterator::current' => ['array|false'],
@@ -7811,7 +7805,6 @@ return [
'mysqli::commit' => ['bool', 'flags='=>'int', 'name='=>'?string'],
'mysqli::connect' => ['bool', 'hostname='=>'string|null', 'username='=>'string|null', 'password='=>'string|null', 'database='=>'string|null', 'port='=>'int|null', 'socket='=>'string|null'],
'mysqli::debug' => ['bool', 'options'=>'string'],
'mysqli::disable_reads_from_master' => ['bool'],
'mysqli::dump_debug_info' => ['bool'],
'mysqli::escape_string' => ['string', 'string'=>'string'],
'mysqli::execute_query' => ['mysqli_result|bool', 'query'=>'non-empty-string', 'params='=>'list<mixed>|null'],
@@ -7826,23 +7819,19 @@ return [
'mysqli::next_result' => ['bool'],
'mysqli::options' => ['bool', 'option'=>'int', 'value'=>'string|int'],
'mysqli::ping' => ['bool'],
'mysqli::poll' => ['int|false', '&w_read'=>'array', '&w_write'=>'array', '&w_error'=>'array', 'seconds'=>'int', 'microseconds='=>'int'],
'mysqli::poll' => ['int|false', '&w_read'=>'?array', '&w_error'=>'?array', '&w_reject'=>'array', 'seconds'=>'int', 'microseconds='=>'int'],
'mysqli::prepare' => ['mysqli_stmt|false', 'query'=>'string'],
'mysqli::query' => ['bool|mysqli_result', 'query'=>'string', 'result_mode='=>'int'],
'mysqli::real_connect' => ['bool', 'hostname='=>'string|null', 'username='=>'string|null', 'password='=>'string|null', 'database='=>'string|null', 'port='=>'int|null', 'socket='=>'string|null', 'flags='=>'int'],
'mysqli::real_connect' => ['bool', 'hostname='=>'?string', 'username='=>'?string', 'password='=>'?string', 'database='=>'?string', 'port='=>'?int', 'socket='=>'?string', 'flags='=>'int'],
'mysqli::real_escape_string' => ['string', 'string'=>'string'],
'mysqli::real_query' => ['bool', 'query'=>'string'],
'mysqli::reap_async_query' => ['mysqli_result|false'],
'mysqli::refresh' => ['bool', 'flags'=>'int'],
'mysqli::release_savepoint' => ['bool', 'name'=>'string'],
'mysqli::rollback' => ['bool', 'flags='=>'int', 'name='=>'?string'],
'mysqli::rpl_query_type' => ['int', 'query'=>'string'],
'mysqli::savepoint' => ['bool', 'name'=>'string'],
'mysqli::select_db' => ['bool', 'database'=>'string'],
'mysqli::send_query' => ['bool', 'query'=>'string'],
'mysqli::set_charset' => ['bool', 'charset'=>'string'],
'mysqli::set_local_infile_default' => ['void'],
'mysqli::set_local_infile_handler' => ['bool', 'read_func='=>'callable'],
'mysqli::set_opt' => ['bool', 'option'=>'int', 'value'=>'string|int'],
'mysqli::ssl_set' => ['bool', 'key'=>'?string', 'certificate'=>'?string', 'ca_certificate'=>'?string', 'ca_path'=>'?string', 'cipher_algos'=>'?string'],
'mysqli::stat' => ['string|false'],
@@ -7864,8 +7853,6 @@ return [
'mysqli_debug' => ['true', 'options'=>'string'],
'mysqli_disable_reads_from_master' => ['bool', 'link'=>'mysqli'],
'mysqli_disable_rpl_parse' => ['bool', 'link'=>'mysqli'],
'mysqli_driver::embedded_server_end' => ['void'],
'mysqli_driver::embedded_server_start' => ['bool', 'start'=>'int', 'arguments'=>'array', 'groups'=>'array'],
'mysqli_dump_debug_info' => ['bool', 'mysql'=>'mysqli'],
'mysqli_embedded_server_end' => ['void'],
'mysqli_embedded_server_start' => ['bool', 'start'=>'int', 'arguments'=>'array', 'groups'=>'array'],
@@ -7885,9 +7872,9 @@ return [
'mysqli_fetch_array\'2' => ['list<null|int|float|string>|false|null', 'result'=>'mysqli_result', 'mode='=>'2'],
'mysqli_fetch_assoc' => ['array<string,null|int|float|string>|false|null', 'result'=>'mysqli_result'],
'mysqli_fetch_column' => ['null|int|float|string|false', 'result'=>'mysqli_result', 'column='=>'int'],
'mysqli_fetch_field' => ['object|false', 'result'=>'mysqli_result'],
'mysqli_fetch_field_direct' => ['object|false', 'result'=>'mysqli_result', 'index'=>'int'],
'mysqli_fetch_fields' => ['stdClass[]', 'result'=>'mysqli_result'],
'mysqli_fetch_field' => ['object{name:non-empty-string,orgname:string,table:string,orgtable:string,max_length:int,length:int,charsetnr:int,flags:int,type:int,decimals:int,db:string,def:string,catalog:string}|false', 'result'=>'mysqli_result'],
'mysqli_fetch_field_direct' => ['object{name:non-empty-string,orgname:string,table:string,orgtable:string,max_length:int,length:int,charsetnr:int,flags:int,type:int,decimals:int,db:string,def:string,catalog:string}|false', 'result'=>'mysqli_result', 'index'=>'int'],
'mysqli_fetch_fields' => ['list<object{name:non-empty-string,orgname:string,table:string,orgtable:string,max_length:int,length:int,charsetnr:int,flags:int,type:int,decimals:int,db:string,def:string,catalog:string}>', 'result'=>'mysqli_result'],
'mysqli_fetch_lengths' => ['array|false', 'result'=>'mysqli_result'],
'mysqli_fetch_object' => ['object|false|null', 'result'=>'mysqli_result', 'class='=>'string', 'constructor_args='=>'array'],
'mysqli_fetch_row' => ['list<null|int|float|string>|false|null', 'result'=>'mysqli_result'],
@@ -7920,10 +7907,10 @@ return [
'mysqli_num_rows' => ['int<0, max>|numeric-string', 'result'=>'mysqli_result'],
'mysqli_options' => ['bool', 'mysql'=>'mysqli', 'option'=>'int', 'value'=>'string|int'],
'mysqli_ping' => ['bool', 'mysql'=>'mysqli'],
'mysqli_poll' => ['int|false', 'read'=>'array', 'write'=>'array', 'error'=>'array', 'seconds'=>'int', 'microseconds='=>'int'],
'mysqli_poll' => ['int|false', '&w_read'=>'?array', '&w_error'=>'?array', '&w_reject'=>'array', 'seconds'=>'int', 'microseconds='=>'int'],
'mysqli_prepare' => ['mysqli_stmt|false', 'mysql'=>'mysqli', 'query'=>'string'],
'mysqli_query' => ['mysqli_result|bool', 'mysql'=>'mysqli', 'query'=>'string', 'result_mode='=>'int'],
'mysqli_real_connect' => ['bool', 'mysql='=>'mysqli', 'hostname='=>'string|null', 'username='=>'string|null', 'password='=>'string|null', 'database='=>'string|null', 'port='=>'int|null', 'socket='=>'string|null', 'flags='=>'int'],
'mysqli_real_connect' => ['bool', 'mysql'=>'mysqli', 'hostname='=>'?string', 'username='=>'?string', 'password='=>'?string', 'database='=>'?string', 'port='=>'?int', 'socket='=>'?string', 'flags='=>'int'],
'mysqli_real_escape_string' => ['string', 'mysql'=>'mysqli', 'string'=>'string'],
'mysqli_real_query' => ['bool', 'mysql'=>'mysqli', 'query'=>'string'],
'mysqli_reap_async_query' => ['mysqli_result|false', 'mysql'=>'mysqli'],
@@ -7941,9 +7928,9 @@ return [
'mysqli_result::fetch_array\'2' => ['list<null|int|float|string>|false|null', 'mode='=>'2'],
'mysqli_result::fetch_assoc' => ['array<string,null|int|float|string>|false|null'],
'mysqli_result::fetch_column' => ['null|int|float|string|false', 'column='=>'int'],
'mysqli_result::fetch_field' => ['object|false'],
'mysqli_result::fetch_field_direct' => ['object|false', 'index'=>'int'],
'mysqli_result::fetch_fields' => ['stdClass[]'],
'mysqli_result::fetch_field' => ['object{name:non-empty-string,orgname:string,table:string,orgtable:string,max_length:int,length:int,charsetnr:int,flags:int,type:int,decimals:int,db:string,def:string,catalog:string}|false'],
'mysqli_result::fetch_field_direct' => ['object{name:non-empty-string,orgname:string,table:string,orgtable:string,max_length:int,length:int,charsetnr:int,flags:int,type:int,decimals:int,db:string,def:string,catalog:string}|false', 'index'=>'int'],
'mysqli_result::fetch_fields' => ['list<object{name:non-empty-string,orgname:string,table:string,orgtable:string,max_length:int,length:int,charsetnr:int,flags:int,type:int,decimals:int,db:string,def:string,catalog:string}>'],
'mysqli_result::fetch_object' => ['object|false|null', 'class='=>'string', 'constructor_args='=>'array'],
'mysqli_result::fetch_row' => ['list<null|int|float|string>|false|null'],
'mysqli_result::field_seek' => ['bool', 'index'=>'int'],
@@ -7965,10 +7952,10 @@ return [
'mysqli_sqlstate' => ['string', 'mysql'=>'mysqli'],
'mysqli_ssl_set' => ['true', 'mysql'=>'mysqli', 'key'=>'?string', 'certificate'=>'?string', 'ca_certificate'=>'?string', 'ca_path'=>'?string', 'cipher_algos'=>'?string'],
'mysqli_stat' => ['string|false', 'mysql'=>'mysqli'],
'mysqli_stmt::__construct' => ['void', 'mysql'=>'mysqli', 'query'=>'string'],
'mysqli_stmt::__construct' => ['void', 'mysql'=>'mysqli', 'query='=>'?string'],
'mysqli_stmt::attr_get' => ['int', 'attribute'=>'int'],
'mysqli_stmt::attr_set' => ['bool', 'attribute'=>'int', 'value'=>'int'],
'mysqli_stmt::bind_param' => ['bool', 'types'=>'string', '&vars'=>'mixed', '&...args='=>'mixed'],
'mysqli_stmt::bind_param' => ['bool', 'types'=>'string', '&var'=>'mixed', '&...vars='=>'mixed'],
'mysqli_stmt::bind_result' => ['bool', '&w_var1'=>'', '&...w_vars='=>''],
'mysqli_stmt::close' => ['bool'],
'mysqli_stmt::data_seek' => ['void', 'offset'=>'int'],
@@ -7988,7 +7975,7 @@ return [
'mysqli_stmt_affected_rows' => ['int<-1, max>|numeric-string', 'statement'=>'mysqli_stmt'],
'mysqli_stmt_attr_get' => ['int', 'statement'=>'mysqli_stmt', 'attribute'=>'int'],
'mysqli_stmt_attr_set' => ['bool', 'statement'=>'mysqli_stmt', 'attribute'=>'int', 'value'=>'int'],
'mysqli_stmt_bind_param' => ['bool', 'statement'=>'mysqli_stmt', 'types'=>'string', '&vars'=>'mixed', '&...args='=>'mixed'],
'mysqli_stmt_bind_param' => ['bool', 'statement'=>'mysqli_stmt', 'types'=>'string', '&var'=>'mixed', '&...vars='=>'mixed'],
'mysqli_stmt_bind_result' => ['bool', 'statement'=>'mysqli_stmt', '&w_var1'=>'', '&...w_vars='=>''],
'mysqli_stmt_close' => ['true', 'statement'=>'mysqli_stmt'],
'mysqli_stmt_data_seek' => ['void', 'statement'=>'mysqli_stmt', 'offset'=>'int'],
@@ -8163,28 +8150,28 @@ return [
'nsapi_response_headers' => ['array'],
'nsapi_virtual' => ['bool', 'uri'=>'string'],
'nthmac' => ['string', 'clent'=>'string', 'data'=>'string'],
'number_format' => ['string', 'num'=>'float|int', 'decimals='=>'int', 'decimal_separator='=>'?string', 'thousands_separator='=>'?string'],
'number_format' => ['string', 'num'=>'float', 'decimals='=>'int', 'decimal_separator='=>'?string', 'thousands_separator='=>'?string'],
'NumberFormatter::__construct' => ['void', 'locale'=>'string', 'style'=>'int', 'pattern='=>'?string'],
'NumberFormatter::create' => ['NumberFormatter|null', 'locale'=>'string', 'style'=>'int', 'pattern='=>'?string'],
'NumberFormatter::format' => ['string|false', 'num'=>'', 'type='=>'int'],
'NumberFormatter::formatCurrency' => ['string|false', 'num'=>'float', 'currency'=>'string'],
'NumberFormatter::getAttribute' => ['int|false', 'attr'=>'int'],
'NumberFormatter::formatCurrency' => ['string|false', 'amount'=>'float', 'currency'=>'string'],
'NumberFormatter::getAttribute' => ['int|float|false', 'attribute'=>'int'],
'NumberFormatter::getErrorCode' => ['int'],
'NumberFormatter::getErrorMessage' => ['string'],
'NumberFormatter::getLocale' => ['string', 'type='=>'int'],
'NumberFormatter::getPattern' => ['string|false'],
'NumberFormatter::getSymbol' => ['string|false', 'attr'=>'int'],
'NumberFormatter::getTextAttribute' => ['string|false', 'attr'=>'int'],
'NumberFormatter::parse' => ['float|false', 'string'=>'string', 'type='=>'int', '&rw_position='=>'int'],
'NumberFormatter::parseCurrency' => ['float|false', 'string'=>'string', '&w_currency'=>'string', '&rw_position='=>'int'],
'NumberFormatter::setAttribute' => ['bool', 'attr'=>'int', 'value'=>''],
'NumberFormatter::getSymbol' => ['string|false', 'symbol'=>'int'],
'NumberFormatter::getTextAttribute' => ['string|false', 'attribute'=>'int'],
'NumberFormatter::parse' => ['int|float|false', 'string'=>'string', 'type='=>'int', '&rw_offset='=>'int'],
'NumberFormatter::parseCurrency' => ['float|false', 'string'=>'string', '&w_currency'=>'string', '&rw_offset='=>'int'],
'NumberFormatter::setAttribute' => ['bool', 'attribute'=>'int', 'value'=>'int|float'],
'NumberFormatter::setPattern' => ['bool', 'pattern'=>'string'],
'NumberFormatter::setSymbol' => ['bool', 'attr'=>'int', 'symbol'=>'string'],
'NumberFormatter::setTextAttribute' => ['bool', 'attr'=>'int', 'value'=>'string'],
'NumberFormatter::setSymbol' => ['bool', 'symbol'=>'int', 'value'=>'string'],
'NumberFormatter::setTextAttribute' => ['bool', 'attribute'=>'int', 'value'=>'string'],
'numfmt_create' => ['NumberFormatter|null', 'locale'=>'string', 'style'=>'int', 'pattern='=>'?string'],
'numfmt_format' => ['string|false', 'formatter'=>'NumberFormatter', 'num'=>'int|float', 'type='=>'int'],
'numfmt_format_currency' => ['string|false', 'formatter'=>'NumberFormatter', 'amount'=>'float', 'currency'=>'string'],
'numfmt_get_attribute' => ['int|false', 'formatter'=>'NumberFormatter', 'attribute'=>'int'],
'numfmt_get_attribute' => ['float|int|false', 'formatter'=>'NumberFormatter', 'attribute'=>'int'],
'numfmt_get_error_code' => ['int', 'formatter'=>'NumberFormatter'],
'numfmt_get_error_message' => ['string', 'formatter'=>'NumberFormatter'],
'numfmt_get_locale' => ['string', 'formatter'=>'NumberFormatter', 'type='=>'int'],
@@ -8193,7 +8180,7 @@ return [
'numfmt_get_text_attribute' => ['string|false', 'formatter'=>'NumberFormatter', 'attribute'=>'int'],
'numfmt_parse' => ['float|int|false', 'formatter'=>'NumberFormatter', 'string'=>'string', 'type='=>'int', '&rw_offset='=>'int'],
'numfmt_parse_currency' => ['float|false', 'formatter'=>'NumberFormatter', 'string'=>'string', '&w_currency'=>'string', '&rw_offset='=>'int'],
'numfmt_set_attribute' => ['bool', 'formatter'=>'NumberFormatter', 'attribute'=>'int', 'value'=>'int'],
'numfmt_set_attribute' => ['bool', 'formatter'=>'NumberFormatter', 'attribute'=>'int', 'value'=>'float|int'],
'numfmt_set_pattern' => ['bool', 'formatter'=>'NumberFormatter', 'pattern'=>'string'],
'numfmt_set_symbol' => ['bool', 'formatter'=>'NumberFormatter', 'symbol'=>'int', 'value'=>'string'],
'numfmt_set_text_attribute' => ['bool', 'formatter'=>'NumberFormatter', 'attribute'=>'int', 'value'=>'string'],
@@ -8408,9 +8395,9 @@ return [
'odbc_pconnect' => ['resource|false', 'dsn'=>'string', 'user'=>'string', 'password'=>'string', 'cursor_option='=>'int'],
'odbc_prepare' => ['resource|false', 'odbc'=>'resource', 'query'=>'string'],
'odbc_primarykeys' => ['resource|false', 'odbc'=>'resource', 'catalog'=>'?string', 'schema'=>'string', 'table'=>'string'],
'odbc_procedurecolumns' => ['resource|false', 'odbc'=>'resource', 'catalog'=>'string', 'schema'=>'string', 'procedure'=>'string', 'column'=>'string'],
'odbc_procedures' => ['resource|false', 'odbc'=>'resource', 'catalog'=>'string', 'schema'=>'string', 'procedure'=>'string'],
'odbc_result' => ['mixed|false', 'statement'=>'resource', 'field'=>'mixed'],
'odbc_procedurecolumns' => ['resource|false', 'odbc'=>'resource', 'catalog='=>'?string', 'schema='=>'?string', 'procedure='=>'?string', 'column='=>'?string'],
'odbc_procedures' => ['resource|false', 'odbc'=>'resource', 'catalog='=>'?string', 'schema='=>'?string', 'procedure='=>'?string'],
'odbc_result' => ['string|bool|null', 'statement'=>'resource', 'field'=>'string|int'],
'odbc_result_all' => ['int|false', 'statement'=>'resource', 'format='=>'string'],
'odbc_rollback' => ['bool', 'odbc'=>'resource'],
'odbc_setoption' => ['bool', 'odbc'=>'resource', 'which'=>'int', 'option'=>'int', 'value'=>'int'],
@@ -8476,7 +8463,7 @@ return [
'openssl_pkcs12_read' => ['bool', 'pkcs12'=>'string', '&w_certificates'=>'array', 'passphrase'=>'string'],
'openssl_pkcs7_decrypt' => ['bool', 'input_filename'=>'string', 'output_filename'=>'string', 'certificate'=>'OpenSSLCertificate|string', 'private_key='=>'OpenSSLAsymmetricKey|OpenSSLCertificate|array{OpenSSLAsymmetricKey|OpenSSLCertificate|string, string}|string|null'],
'openssl_pkcs7_encrypt' => ['bool', 'input_filename'=>'string', 'output_filename'=>'string', 'certificate'=>'OpenSSLCertificate|list<OpenSSLCertificate|string>|string', 'headers'=>'array|null', 'flags='=>'int', 'cipher_algo='=>'int'],
'openssl_pkcs7_read' => ['bool', 'input_filename'=>'string', '&w_certificates'=>'array'],
'openssl_pkcs7_read' => ['bool', 'data'=>'string', '&w_certificates'=>'array'],
'openssl_pkcs7_sign' => ['bool', 'input_filename'=>'string', 'output_filename'=>'string', 'certificate'=>'OpenSSLCertificate|string', 'private_key'=>'OpenSSLAsymmetricKey|OpenSSLCertificate|array{OpenSSLAsymmetricKey|OpenSSLCertificate|string, string}|string', 'headers'=>'array|null', 'flags='=>'int', 'untrusted_certificates_filename='=>'string|null'],
'openssl_pkcs7_verify' => ['bool|int', 'input_filename'=>'string', 'flags'=>'int', 'signers_certificates_filename='=>'?string', 'ca_info='=>'array', 'untrusted_certificates_filename='=>'?string', 'content='=>'?string', 'output_filename='=>'?string'],
'openssl_pkey_derive' => ['string|false', 'public_key'=>'OpenSSLAsymmetricKey|OpenSSLCertificate|array{OpenSSLAsymmetricKey|OpenSSLCertificate|string, string}|string', 'private_key'=>'OpenSSLAsymmetricKey|OpenSSLCertificate|array{OpenSSLAsymmetricKey|OpenSSLCertificate|string, string}|string', 'key_length='=>'int'],
@@ -8998,8 +8985,6 @@ return [
'PDFlib::utf32_to_utf16' => ['string', 'utf32string'=>'string', 'ordering'=>'string'],
'PDFlib::utf8_to_utf16' => ['string', 'utf8string'=>'string', 'ordering'=>'string'],
'PDO::__construct' => ['void', 'dsn'=>'string', 'username='=>'?string', 'password='=>'?string', 'options='=>'?array'],
'PDO::__sleep' => ['list<string>'],
'PDO::__wakeup' => ['void'],
'PDO::beginTransaction' => ['bool'],
'PDO::commit' => ['bool'],
'PDO::cubrid_schema' => ['array', 'schema_type'=>'int', 'table_name='=>'string', 'col_name='=>'string'],
@@ -9038,8 +9023,6 @@ return [
'PDOException::getPrevious' => ['?Throwable'],
'PDOException::getTrace' => ['list<array{file?:string,line?:int,function:string,class?:class-string,type?:\'::\'|\'->\',args?:array<mixed>}>'],
'PDOException::getTraceAsString' => ['string'],
'PDOStatement::__sleep' => ['list<string>'],
'PDOStatement::__wakeup' => ['void'],
'PDOStatement::bindColumn' => ['bool', 'column'=>'string|int', '&rw_var'=>'mixed', 'type='=>'int', 'maxLength='=>'int', 'driverOptions='=>'mixed'],
'PDOStatement::bindParam' => ['bool', 'param'=>'string|int', '&rw_var'=>'mixed', 'type='=>'int', 'maxLength='=>'int', 'driverOptions='=>'mixed'],
'PDOStatement::bindValue' => ['bool', 'param'=>'string|int', 'value'=>'mixed', 'type='=>'int'],
@@ -9640,7 +9623,7 @@ return [
'rand\'1' => ['int'],
'random_bytes' => ['non-empty-string', 'length'=>'positive-int'],
'random_int' => ['int', 'min'=>'int', 'max'=>'int'],
'range' => ['array', 'start'=>'mixed', 'end'=>'mixed', 'step='=>'int|float'],
'range' => ['non-empty-array', 'start'=>'string|int|float', 'end'=>'string|int|float', 'step='=>'int<1, max>|float'],
'RangeException::__clone' => ['void'],
'RangeException::__construct' => ['void', 'message='=>'string', 'code='=>'int', 'previous='=>'?Throwable'],
'RangeException::__toString' => ['string'],
@@ -10443,7 +10426,7 @@ return [
'RedisCluster::zUnionStore' => ['int', 'Output'=>'string', 'ZSetKeys'=>'array', 'Weights='=>'?array', 'aggregateFunction='=>'string'],
'Reflection::getModifierNames' => ['list<string>', 'modifiers'=>'int'],
'ReflectionClass::__clone' => ['void'],
'ReflectionClass::__construct' => ['void', 'argument'=>'object|class-string'],
'ReflectionClass::__construct' => ['void', 'objectOrClass'=>'object|class-string'],
'ReflectionClass::__toString' => ['string'],
'ReflectionClass::getAttributes' => ['list<ReflectionAttribute>', 'name='=>'?string', 'flags='=>'int'],
'ReflectionClass::getConstant' => ['mixed', 'name'=>'string'],
@@ -10477,7 +10460,7 @@ return [
'ReflectionClass::hasConstant' => ['bool', 'name'=>'string'],
'ReflectionClass::hasMethod' => ['bool', 'name'=>'string'],
'ReflectionClass::hasProperty' => ['bool', 'name'=>'string'],
'ReflectionClass::implementsInterface' => ['bool', 'interface_name'=>'interface-string|ReflectionClass'],
'ReflectionClass::implementsInterface' => ['bool', 'interface'=>'interface-string|ReflectionClass'],
'ReflectionClass::inNamespace' => ['bool'],
'ReflectionClass::isAbstract' => ['bool'],
'ReflectionClass::isAnonymous' => ['bool'],
@@ -10497,7 +10480,7 @@ return [
'ReflectionClass::newInstanceArgs' => ['object', 'args='=>'list<mixed>|array<string, mixed>'],
'ReflectionClass::newInstanceWithoutConstructor' => ['object'],
'ReflectionClass::setStaticPropertyValue' => ['void', 'name'=>'string', 'value'=>'mixed'],
'ReflectionClassConstant::__construct' => ['void', 'class'=>'mixed', 'name'=>'string'],
'ReflectionClassConstant::__construct' => ['void', 'class'=>'object|class-string', 'constant'=>'string'],
'ReflectionClassConstant::__toString' => ['string'],
'ReflectionClassConstant::getAttributes' => ['list<ReflectionAttribute>', 'name='=>'?string', 'flags='=>'int'],
'ReflectionClassConstant::getDeclaringClass' => ['ReflectionClass'],
@@ -10530,7 +10513,7 @@ return [
'ReflectionExtension::info' => ['void'],
'ReflectionExtension::isPersistent' => ['bool'],
'ReflectionExtension::isTemporary' => ['bool'],
'ReflectionFunction::__construct' => ['void', 'name'=>'callable-string|Closure'],
'ReflectionFunction::__construct' => ['void', 'function'=>'callable-string|Closure'],
'ReflectionFunction::__toString' => ['string'],
'ReflectionFunction::getClosure' => ['Closure'],
'ReflectionFunction::getClosureScopeClass' => ['ReflectionClass'],
@@ -10592,7 +10575,7 @@ return [
'ReflectionFunctionAbstract::isUserDefined' => ['bool'],
'ReflectionFunctionAbstract::isVariadic' => ['bool'],
'ReflectionFunctionAbstract::returnsReference' => ['bool'],
'ReflectionGenerator::__construct' => ['void', 'generator'=>'object'],
'ReflectionGenerator::__construct' => ['void', 'generator'=>'Generator'],
'ReflectionGenerator::getExecutingFile' => ['string'],
'ReflectionGenerator::getExecutingGenerator' => ['Generator'],
'ReflectionGenerator::getExecutingLine' => ['int'],
@@ -10640,17 +10623,17 @@ return [
'ReflectionMethod::isUserDefined' => ['bool'],
'ReflectionMethod::isVariadic' => ['bool'],
'ReflectionMethod::returnsReference' => ['bool'],
'ReflectionMethod::setAccessible' => ['void', 'visible'=>'bool'],
'ReflectionMethod::setAccessible' => ['void', 'accessible'=>'bool'],
'ReflectionNamedType::__clone' => ['void'],
'ReflectionNamedType::__toString' => ['string'],
'ReflectionNamedType::allowsNull' => ['bool'],
'ReflectionNamedType::getName' => ['string'],
'ReflectionNamedType::isBuiltin' => ['bool'],
'ReflectionObject::__clone' => ['void'],
'ReflectionObject::__construct' => ['void', 'argument'=>'object'],
'ReflectionObject::__construct' => ['void', 'object'=>'object'],
'ReflectionObject::__toString' => ['string'],
'ReflectionObject::getConstant' => ['mixed', 'name'=>'string'],
'ReflectionObject::getConstants' => ['array<string,mixed>'],
'ReflectionObject::getConstants' => ['array<string,mixed>', 'filter='=>'?int'],
'ReflectionObject::getConstructor' => ['?ReflectionMethod'],
'ReflectionObject::getDefaultProperties' => ['array'],
'ReflectionObject::getDocComment' => ['false|string'],
@@ -10669,7 +10652,7 @@ return [
'ReflectionObject::getProperties' => ['ReflectionProperty[]', 'filter='=>'?int'],
'ReflectionObject::getProperty' => ['ReflectionProperty', 'name'=>'string'],
'ReflectionObject::getReflectionConstant' => ['ReflectionClassConstant', 'name'=>'string'],
'ReflectionObject::getReflectionConstants' => ['list<\ReflectionClassConstant>'],
'ReflectionObject::getReflectionConstants' => ['list<\ReflectionClassConstant>', 'filter='=>'?int'],
'ReflectionObject::getShortName' => ['string'],
'ReflectionObject::getStartLine' => ['false|int'],
'ReflectionObject::getStaticProperties' => ['ReflectionProperty[]'],
@@ -10680,7 +10663,7 @@ return [
'ReflectionObject::hasConstant' => ['bool', 'name'=>'string'],
'ReflectionObject::hasMethod' => ['bool', 'name'=>'string'],
'ReflectionObject::hasProperty' => ['bool', 'name'=>'string'],
'ReflectionObject::implementsInterface' => ['bool', 'interface_name'=>'ReflectionClass|string'],
'ReflectionObject::implementsInterface' => ['bool', 'interface'=>'ReflectionClass|interface-string'],
'ReflectionObject::inNamespace' => ['bool'],
'ReflectionObject::isAbstract' => ['bool'],
'ReflectionObject::isAnonymous' => ['bool'],
@@ -10701,7 +10684,7 @@ return [
'ReflectionObject::newInstanceWithoutConstructor' => ['object'],
'ReflectionObject::setStaticPropertyValue' => ['void', 'name'=>'string', 'value'=>'string'],
'ReflectionParameter::__clone' => ['void'],
'ReflectionParameter::__construct' => ['void', 'function'=>'', 'parameter'=>''],
'ReflectionParameter::__construct' => ['void', 'function'=>'string|array|object', 'param'=>'int|string'],
'ReflectionParameter::__toString' => ['string'],
'ReflectionParameter::allowsNull' => ['bool'],
'ReflectionParameter::canBePassedByValue' => ['bool'],
@@ -10723,7 +10706,7 @@ return [
'ReflectionParameter::isPassedByReference' => ['bool'],
'ReflectionParameter::isVariadic' => ['bool'],
'ReflectionProperty::__clone' => ['void'],
'ReflectionProperty::__construct' => ['void', 'class'=>'', 'name'=>'string'],
'ReflectionProperty::__construct' => ['void', 'class'=>'object|class-string', 'property'=>'string'],
'ReflectionProperty::__toString' => ['string'],
'ReflectionProperty::getAttributes' => ['list<ReflectionAttribute>', 'name='=>'?string', 'flags='=>'int'],
'ReflectionProperty::getDeclaringClass' => ['ReflectionClass'],
@@ -10738,7 +10721,7 @@ return [
'ReflectionProperty::isProtected' => ['bool'],
'ReflectionProperty::isPublic' => ['bool'],
'ReflectionProperty::isStatic' => ['bool'],
'ReflectionProperty::setAccessible' => ['void', 'visible'=>'bool'],
'ReflectionProperty::setAccessible' => ['void', 'accessible'=>'bool'],
'ReflectionProperty::setValue' => ['void', 'object'=>'null|object', 'value'=>''],
'ReflectionProperty::setValue\'1' => ['void', 'value'=>''],
'ReflectionType::__clone' => ['void'],
@@ -10776,13 +10759,13 @@ return [
'rename' => ['bool', 'from'=>'string', 'to'=>'string', 'context='=>'resource'],
'rename_function' => ['bool', 'original_name'=>'string', 'new_name'=>'string'],
'reset' => ['mixed|false', '&r_array'=>'array|object'],
'ResourceBundle::__construct' => ['void', 'locale'=>'string', 'bundlename'=>'string', 'fallback='=>'bool'],
'ResourceBundle::__construct' => ['void', 'locale'=>'?string', 'bundle'=>'?string', 'fallback='=>'bool'],
'ResourceBundle::count' => ['int'],
'ResourceBundle::create' => ['?ResourceBundle', 'locale'=>'string', 'bundlename'=>'string', 'fallback='=>'bool'],
'ResourceBundle::create' => ['?ResourceBundle', 'locale'=>'?string', 'bundle'=>'?string', 'fallback='=>'bool'],
'ResourceBundle::get' => ['mixed', 'index'=>'string|int', 'fallback='=>'bool'],
'ResourceBundle::getErrorCode' => ['int'],
'ResourceBundle::getErrorMessage' => ['string'],
'ResourceBundle::getLocales' => ['array', 'bundlename'=>'string'],
'ResourceBundle::getLocales' => ['array|false', 'bundle'=>'string'],
'resourcebundle_count' => ['int', 'bundle'=>'ResourceBundle'],
'resourcebundle_create' => ['?ResourceBundle', 'locale'=>'?string', 'bundle'=>'?string', 'fallback='=>'bool'],
'resourcebundle_get' => ['mixed|null', 'bundle'=>'ResourceBundle', 'index'=>'string|int', 'fallback='=>'bool'],
@@ -10795,7 +10778,7 @@ return [
'rewind' => ['bool', 'stream'=>'resource'],
'rewinddir' => ['void', 'dir_handle='=>'resource'],
'rmdir' => ['bool', 'directory'=>'string', 'context='=>'resource'],
'round' => ['float', 'num'=>'float', 'precision='=>'int', 'mode='=>'0|positive-int'],
'round' => ['float', 'num'=>'float|int', 'precision='=>'int', 'mode='=>'0|positive-int'],
'rpm_close' => ['bool', 'rpmr'=>'resource'],
'rpm_get_tag' => ['mixed', 'rpmr'=>'resource', 'tagnum'=>'int'],
'rpm_is_valid' => ['bool', 'filename'=>'string'],
@@ -11169,18 +11152,18 @@ return [
'ServerResponse::setStatus' => ['void', 'status'=>'int'],
'ServerResponse::setVersion' => ['void', 'version'=>'string'],
'session_abort' => ['bool'],
'session_cache_expire' => ['int', 'value='=>'?int'],
'session_cache_limiter' => ['string', 'value='=>'?string'],
'session_cache_expire' => ['int|false', 'value='=>'?int'],
'session_cache_limiter' => ['string|false', 'value='=>'?string'],
'session_commit' => ['bool'],
'session_create_id' => ['string', 'prefix='=>'string'],
'session_create_id' => ['string|false', 'prefix='=>'string'],
'session_decode' => ['bool', 'data'=>'string'],
'session_destroy' => ['bool'],
'session_encode' => ['string'],
'session_encode' => ['string|false'],
'session_gc' => ['int|false'],
'session_get_cookie_params' => ['array'],
'session_id' => ['string|false', 'id='=>'?string'],
'session_is_registered' => ['bool', 'name'=>'string'],
'session_module_name' => ['string', 'module='=>'?string'],
'session_module_name' => ['string|false', 'module='=>'?string'],
'session_name' => ['string|false', 'name='=>'?string'],
'session_pgsql_add_error' => ['bool', 'error_level'=>'int', 'error_message='=>'string'],
'session_pgsql_get_error' => ['array', 'with_error_message='=>'bool'],
@@ -11192,7 +11175,7 @@ return [
'session_register' => ['bool', 'name'=>'mixed', '...args='=>'mixed'],
'session_register_shutdown' => ['void'],
'session_reset' => ['bool'],
'session_save_path' => ['string', 'path='=>'?string'],
'session_save_path' => ['string|false', 'path='=>'?string'],
'session_set_cookie_params' => ['bool', 'lifetime'=>'int', 'path='=>'?string', 'domain='=>'?string', 'secure='=>'?bool', 'httponly='=>'?bool'],
'session_set_cookie_params\'1' => ['bool', 'options'=>'array{lifetime?:?int,path?:?string,domain?:?string,secure?:?bool,httponly?:?bool,samesite?:?string}'],
'session_set_save_handler' => ['bool', 'open'=>'callable(string,string):bool', 'close'=>'callable():bool', 'read'=>'callable(string):string', 'write'=>'callable(string,string):bool', 'destroy'=>'callable(string):bool', 'gc'=>'callable(string):bool', 'create_sid='=>'callable():string', 'validate_sid='=>'callable(string):bool', 'update_timestamp='=>'callable(string):bool'],
@@ -11205,9 +11188,9 @@ return [
'SessionHandler::close' => ['bool'],
'SessionHandler::create_sid' => ['string'],
'SessionHandler::destroy' => ['bool', 'id'=>'string'],
'SessionHandler::gc' => ['bool', 'maxlifetime'=>'int'],
'SessionHandler::open' => ['bool', 'save_path'=>'string', 'session_name'=>'string'],
'SessionHandler::read' => ['string', 'id'=>'string'],
'SessionHandler::gc' => ['int|false', 'max_lifetime'=>'int'],
'SessionHandler::open' => ['bool', 'path'=>'string', 'name'=>'string'],
'SessionHandler::read' => ['string|false', 'id'=>'string'],
'SessionHandler::write' => ['bool', 'id'=>'string', 'data'=>'string'],
'SessionHandlerInterface::close' => ['bool'],
'SessionHandlerInterface::destroy' => ['bool', 'id'=>'string'],
@@ -11305,40 +11288,40 @@ return [
'simplexml_import_dom' => ['?SimpleXMLElement', 'node'=>'DOMNode', 'class_name='=>'?string'],
'simplexml_load_file' => ['SimpleXMLElement|false', 'filename'=>'string', 'class_name='=>'?string', 'options='=>'int', 'namespace_or_prefix='=>'string', 'is_prefix='=>'bool'],
'simplexml_load_string' => ['SimpleXMLElement|false', 'data'=>'string', 'class_name='=>'?string', 'options='=>'int', 'namespace_or_prefix='=>'string', 'is_prefix='=>'bool'],
'SimpleXMLElement::__construct' => ['void', 'data'=>'string', 'options='=>'int', 'data_is_url='=>'bool', 'ns='=>'string', 'is_prefix='=>'bool'],
'SimpleXMLElement::__construct' => ['void', 'data'=>'string', 'options='=>'int', 'dataIsURL='=>'bool', 'namespaceOrPrefix='=>'string', 'isPrefix='=>'bool'],
'SimpleXMLElement::__get' => ['SimpleXMLElement', 'name'=>'string'],
'SimpleXMLElement::__toString' => ['string'],
'SimpleXMLElement::addAttribute' => ['void', 'name'=>'string', 'value='=>'string', 'ns='=>'string'],
'SimpleXMLElement::addChild' => ['SimpleXMLElement', 'name'=>'string', 'value='=>'string', 'ns='=>'string'],
'SimpleXMLElement::addAttribute' => ['void', 'qualifiedName'=>'string', 'value'=>'string', 'namespace='=>'?string'],
'SimpleXMLElement::addChild' => ['?SimpleXMLElement', 'qualifiedName'=>'string', 'value='=>'?string', 'namespace='=>'?string'],
'SimpleXMLElement::asXML' => ['string|bool', 'filename='=>'?string'],
'SimpleXMLElement::asXML\'1' => ['string|false'],
'SimpleXMLElement::attributes' => ['?SimpleXMLElement', 'ns='=>'string', 'is_prefix='=>'bool'],
'SimpleXMLElement::children' => ['SimpleXMLElement', 'ns='=>'string', 'is_prefix='=>'bool'],
'SimpleXMLElement::attributes' => ['?SimpleXMLElement', 'namespaceOrPrefix='=>'?string', 'isPrefix='=>'bool'],
'SimpleXMLElement::children' => ['?SimpleXMLElement', 'namespaceOrPrefix='=>'?string', 'isPrefix='=>'bool'],
'SimpleXMLElement::count' => ['int'],
'SimpleXMLElement::getDocNamespaces' => ['string[]', 'recursive='=>'bool', 'from_root='=>'bool'],
'SimpleXMLElement::getDocNamespaces' => ['array<string,string>', 'recursive='=>'bool', 'fromRoot='=>'bool'],
'SimpleXMLElement::getName' => ['string'],
'SimpleXMLElement::getNamespaces' => ['string[]', 'recursive='=>'bool'],
'SimpleXMLElement::getNamespaces' => ['array<string,string>', 'recursive='=>'bool'],
'SimpleXMLElement::offsetExists' => ['bool', 'offset'=>'int|string'],
'SimpleXMLElement::offsetGet' => ['SimpleXMLElement', 'offset'=>'int|string'],
'SimpleXMLElement::offsetSet' => ['void', 'offset'=>'int|string', 'value'=>'mixed'],
'SimpleXMLElement::offsetUnset' => ['void', 'offset'=>'int|string'],
'SimpleXMLElement::registerXPathNamespace' => ['bool', 'prefix'=>'string', 'ns'=>'string'],
'SimpleXMLElement::registerXPathNamespace' => ['bool', 'prefix'=>'string', 'namespace'=>'string'],
'SimpleXMLElement::saveXML' => ['string|bool', 'filename='=>'?string'],
'SimpleXMLElement::xpath' => ['SimpleXMLElement[]|false', 'path'=>'string'],
'SimpleXMLElement::xpath' => ['SimpleXMLElement[]|false|null', 'expression'=>'string'],
'sin' => ['float', 'num'=>'float'],
'sinh' => ['float', 'num'=>'float'],
'sizeof' => ['int<0, max>', 'value'=>'Countable|array', 'mode='=>'int'],
'sleep' => ['int', 'seconds'=>'0|positive-int'],
'snmp2_get' => ['string|false', 'hostname'=>'string', 'community'=>'string', 'object_id'=>'string', 'timeout='=>'int', 'retries='=>'int'],
'snmp2_getnext' => ['string|false', 'hostname'=>'string', 'community'=>'string', 'object_id'=>'string', 'timeout='=>'int', 'retries='=>'int'],
'snmp2_real_walk' => ['array|false', 'hostname'=>'string', 'community'=>'string', 'object_id'=>'string', 'timeout='=>'int', 'retries='=>'int'],
'snmp2_set' => ['bool', 'hostname'=>'string', 'community'=>'string', 'object_id'=>'string', 'type'=>'string', 'value'=>'string', 'timeout='=>'int', 'retries='=>'int'],
'snmp2_walk' => ['array|false', 'hostname'=>'string', 'community'=>'string', 'object_id'=>'string', 'timeout='=>'int', 'retries='=>'int'],
'snmp3_get' => ['string|false', 'hostname'=>'string', 'security_name'=>'string', 'security_level'=>'string', 'auth_protocol'=>'string', 'auth_passphrase'=>'string', 'privacy_protocol'=>'string', 'privacy_passphrase'=>'string', 'object_id'=>'string', 'timeout='=>'int', 'retries='=>'int'],
'snmp3_getnext' => ['string|false', 'hostname'=>'string', 'security_name'=>'string', 'security_level'=>'string', 'auth_protocol'=>'string', 'auth_passphrase'=>'string', 'privacy_protocol'=>'string', 'privacy_passphrase'=>'string', 'object_id'=>'string', 'timeout='=>'int', 'retries='=>'int'],
'snmp3_real_walk' => ['array|false', 'hostname'=>'string', 'security_name'=>'string', 'security_level'=>'string', 'auth_protocol'=>'string', 'auth_passphrase'=>'string', 'privacy_protocol'=>'string', 'privacy_passphrase'=>'string', 'object_id'=>'string', 'timeout='=>'int', 'retries='=>'int'],
'snmp3_set' => ['bool', 'hostname'=>'string', 'security_name'=>'string', 'security_level'=>'string', 'auth_protocol'=>'string', 'auth_passphrase'=>'string', 'privacy_protocol'=>'string', 'privacy_passphrase'=>'string', 'object_id'=>'string', 'type'=>'string', 'value'=>'string', 'timeout='=>'int', 'retries='=>'int'],
'snmp3_walk' => ['array|false', 'hostname'=>'string', 'security_name'=>'string', 'security_level'=>'string', 'auth_protocol'=>'string', 'auth_passphrase'=>'string', 'privacy_protocol'=>'string', 'privacy_passphrase'=>'string', 'object_id'=>'string', 'timeout='=>'int', 'retries='=>'int'],
'snmp2_get' => ['string|false', 'hostname'=>'string', 'community'=>'string', 'object_id'=>'array|string', 'timeout='=>'int', 'retries='=>'int'],
'snmp2_getnext' => ['string|false', 'hostname'=>'string', 'community'=>'string', 'object_id'=>'array|string', 'timeout='=>'int', 'retries='=>'int'],
'snmp2_real_walk' => ['array|false', 'hostname'=>'string', 'community'=>'string', 'object_id'=>'array|string', 'timeout='=>'int', 'retries='=>'int'],
'snmp2_set' => ['bool', 'hostname'=>'string', 'community'=>'string', 'object_id'=>'array|string', 'type'=>'array|string', 'value'=>'array|string', 'timeout='=>'int', 'retries='=>'int'],
'snmp2_walk' => ['array|false', 'hostname'=>'string', 'community'=>'string', 'object_id'=>'array|string', 'timeout='=>'int', 'retries='=>'int'],
'snmp3_get' => ['string|false', 'hostname'=>'string', 'security_name'=>'string', 'security_level'=>'string', 'auth_protocol'=>'string', 'auth_passphrase'=>'string', 'privacy_protocol'=>'string', 'privacy_passphrase'=>'string', 'object_id'=>'array|string', 'timeout='=>'int', 'retries='=>'int'],
'snmp3_getnext' => ['string|false', 'hostname'=>'string', 'security_name'=>'string', 'security_level'=>'string', 'auth_protocol'=>'string', 'auth_passphrase'=>'string', 'privacy_protocol'=>'string', 'privacy_passphrase'=>'string', 'object_id'=>'array|string', 'timeout='=>'int', 'retries='=>'int'],
'snmp3_real_walk' => ['array|false', 'hostname'=>'string', 'security_name'=>'string', 'security_level'=>'string', 'auth_protocol'=>'string', 'auth_passphrase'=>'string', 'privacy_protocol'=>'string', 'privacy_passphrase'=>'string', 'object_id'=>'array|string', 'timeout='=>'int', 'retries='=>'int'],
'snmp3_set' => ['bool', 'hostname'=>'string', 'security_name'=>'string', 'security_level'=>'string', 'auth_protocol'=>'string', 'auth_passphrase'=>'string', 'privacy_protocol'=>'string', 'privacy_passphrase'=>'string', 'object_id'=>'array|string', 'type'=>'array|string', 'value'=>'array|string', 'timeout='=>'int', 'retries='=>'int'],
'snmp3_walk' => ['array|false', 'hostname'=>'string', 'security_name'=>'string', 'security_level'=>'string', 'auth_protocol'=>'string', 'auth_passphrase'=>'string', 'privacy_protocol'=>'string', 'privacy_passphrase'=>'string', 'object_id'=>'array|string', 'timeout='=>'int', 'retries='=>'int'],
'SNMP::__construct' => ['void', 'version'=>'int', 'hostname'=>'string', 'community'=>'string', 'timeout='=>'int', 'retries='=>'int'],
'SNMP::close' => ['bool'],
'SNMP::get' => ['array|string|false', 'objectId'=>'string|array', 'preserveKeys='=>'bool'],
@@ -11347,7 +11330,7 @@ return [
'SNMP::getnext' => ['string|array|false', 'objectId'=>'string|array'],
'SNMP::set' => ['bool', 'objectId'=>'string|array', 'type'=>'string|array', 'value'=>'string|array'],
'SNMP::setSecurity' => ['bool', 'securityLevel'=>'string', 'authProtocol='=>'string', 'authPassphrase='=>'string', 'privacyProtocol='=>'string', 'privacyPassphrase='=>'string', 'contextName='=>'string', 'contextEngineId='=>'string'],
'SNMP::walk' => ['array|false', 'objectId'=>'string', 'suffixAsKey='=>'bool', 'maxRepetitions='=>'int', 'nonRepeaters='=>'int'],
'SNMP::walk' => ['array|false', 'objectId'=>'array|string', 'suffixAsKey='=>'bool', 'maxRepetitions='=>'int', 'nonRepeaters='=>'int'],
'snmp_get_quick_print' => ['bool'],
'snmp_get_valueretrieval' => ['int'],
'snmp_read_mib' => ['bool', 'filename'=>'string'],
@@ -11356,12 +11339,12 @@ return [
'snmp_set_oid_output_format' => ['true', 'format'=>'int'],
'snmp_set_quick_print' => ['bool', 'enable'=>'bool'],
'snmp_set_valueretrieval' => ['true', 'method'=>'int'],
'snmpget' => ['string|false', 'hostname'=>'string', 'community'=>'string', 'object_id'=>'string', 'timeout='=>'int', 'retries='=>'int'],
'snmpgetnext' => ['string|false', 'hostname'=>'string', 'community'=>'string', 'object_id'=>'string', 'timeout='=>'int', 'retries='=>'int'],
'snmprealwalk' => ['array|false', 'hostname'=>'string', 'community'=>'string', 'object_id'=>'string', 'timeout='=>'int', 'retries='=>'int'],
'snmpset' => ['bool', 'hostname'=>'string', 'community'=>'string', 'object_id'=>'string', 'type'=>'string|string[]', 'value'=>'string|string[]', 'timeout='=>'int', 'retries='=>'int'],
'snmpwalk' => ['array|false', 'hostname'=>'string', 'community'=>'string', 'object_id'=>'string', 'timeout='=>'int', 'retries='=>'int'],
'snmpwalkoid' => ['array|false', 'hostname'=>'string', 'community'=>'string', 'object_id'=>'string', 'timeout='=>'int', 'retries='=>'int'],
'snmpget' => ['string|false', 'hostname'=>'string', 'community'=>'string', 'object_id'=>'array|string', 'timeout='=>'int', 'retries='=>'int'],
'snmpgetnext' => ['string|false', 'hostname'=>'string', 'community'=>'string', 'object_id'=>'array|string', 'timeout='=>'int', 'retries='=>'int'],
'snmprealwalk' => ['array|false', 'hostname'=>'string', 'community'=>'string', 'object_id'=>'array|string', 'timeout='=>'int', 'retries='=>'int'],
'snmpset' => ['bool', 'hostname'=>'string', 'community'=>'string', 'object_id'=>'array|string', 'type'=>'string|string[]', 'value'=>'string|string[]', 'timeout='=>'int', 'retries='=>'int'],
'snmpwalk' => ['array|false', 'hostname'=>'string', 'community'=>'string', 'object_id'=>'array|string', 'timeout='=>'int', 'retries='=>'int'],
'snmpwalkoid' => ['array|false', 'hostname'=>'string', 'community'=>'string', 'object_id'=>'array|string', 'timeout='=>'int', 'retries='=>'int'],
'SoapClient::__call' => ['', 'function_name'=>'string', 'arguments'=>'array'],
'SoapClient::__construct' => ['void', 'wsdl'=>'mixed', 'options='=>'array|null'],
'SoapClient::__doRequest' => ['?string', 'request'=>'string', 'location'=>'string', 'action'=>'string', 'version'=>'int', 'one_way='=>'bool'],
@@ -11506,7 +11489,7 @@ return [
'sodium_crypto_secretstream_xchacha20poly1305_init_pull' => ['string', 'header'=>'string', 'key'=>'string'],
'sodium_crypto_secretstream_xchacha20poly1305_init_push' => ['array', 'key'=>'string'],
'sodium_crypto_secretstream_xchacha20poly1305_keygen' => ['non-empty-string'],
'sodium_crypto_secretstream_xchacha20poly1305_pull' => ['array', '&r_state'=>'string', 'ciphertext'=>'string', 'additional_data='=>'string'],
'sodium_crypto_secretstream_xchacha20poly1305_pull' => ['array|false', '&r_state'=>'string', 'ciphertext'=>'string', 'additional_data='=>'string'],
'sodium_crypto_secretstream_xchacha20poly1305_push' => ['string', '&w_state'=>'string', 'message'=>'string', 'additional_data='=>'string', 'tag='=>'int'],
'sodium_crypto_secretstream_xchacha20poly1305_rekey' => ['void', '&w_state'=>'string'],
'sodium_crypto_shorthash' => ['string', 'message'=>'string', 'key'=>'string'],
@@ -12281,10 +12264,10 @@ return [
'SplDoublyLinkedList::isEmpty' => ['bool'],
'SplDoublyLinkedList::key' => ['int'],
'SplDoublyLinkedList::next' => ['void'],
'SplDoublyLinkedList::offsetExists' => ['bool', 'index'=>'mixed'],
'SplDoublyLinkedList::offsetGet' => ['mixed', 'index'=>'mixed'],
'SplDoublyLinkedList::offsetSet' => ['void', 'index'=>'mixed', 'value'=>'mixed'],
'SplDoublyLinkedList::offsetUnset' => ['void', 'index'=>'mixed'],
'SplDoublyLinkedList::offsetExists' => ['bool', 'index'=>'int'],
'SplDoublyLinkedList::offsetGet' => ['mixed', 'index'=>'int'],
'SplDoublyLinkedList::offsetSet' => ['void', 'index'=>'?int', 'value'=>'mixed'],
'SplDoublyLinkedList::offsetUnset' => ['void', 'index'=>'int'],
'SplDoublyLinkedList::pop' => ['mixed'],
'SplDoublyLinkedList::prev' => ['void'],
'SplDoublyLinkedList::push' => ['void', 'value'=>'mixed'],
@@ -12300,7 +12283,6 @@ return [
'SplEnum::getConstList' => ['array', 'include_default='=>'bool'],
'SplFileInfo::__construct' => ['void', 'filename'=>'string'],
'SplFileInfo::__toString' => ['string'],
'SplFileInfo::__wakeup' => ['void'],
'SplFileInfo::getATime' => ['int|false'],
'SplFileInfo::getBasename' => ['string', 'suffix='=>'string'],
'SplFileInfo::getCTime' => ['int|false'],
@@ -12390,19 +12372,15 @@ return [
'SplFixedArray::__construct' => ['void', 'size='=>'int'],
'SplFixedArray::__wakeup' => ['void'],
'SplFixedArray::count' => ['int'],
'SplFixedArray::current' => ['mixed'],
'SplFixedArray::fromArray' => ['SplFixedArray', 'array'=>'array', 'preserveKeys='=>'bool'],
'SplFixedArray::getIterator' => ['Iterator'],
'SplFixedArray::getSize' => ['int'],
'SplFixedArray::key' => ['int'],
'SplFixedArray::next' => ['void'],
'SplFixedArray::offsetExists' => ['bool', 'index'=>'int'],
'SplFixedArray::offsetGet' => ['mixed', 'index'=>'int'],
'SplFixedArray::offsetSet' => ['void', 'index'=>'int', 'value'=>'mixed'],
'SplFixedArray::offsetUnset' => ['void', 'index'=>'int'],
'SplFixedArray::rewind' => ['void'],
'SplFixedArray::setSize' => ['bool', 'size'=>'int'],
'SplFixedArray::toArray' => ['array'],
'SplFixedArray::valid' => ['bool'],
'SplHeap::__construct' => ['void'],
'SplHeap::compare' => ['int', 'value1'=>'mixed', 'value2'=>'mixed'],
'SplHeap::count' => ['int'],
@@ -12586,7 +12564,7 @@ return [
'Spoofchecker::setChecks' => ['void', 'checks'=>'int'],
'Spoofchecker::setRestrictionLevel' => ['void', 'level'=>'int'],
'sprintf' => ['string', 'format'=>'string', '...values='=>'string|int|float'],
'SQLite3::__construct' => ['void', 'filename'=>'string', 'flags='=>'int', 'encryptionKey='=>'?string'],
'SQLite3::__construct' => ['void', 'filename'=>'string', 'flags='=>'int', 'encryptionKey='=>'string'],
'SQLite3::busyTimeout' => ['bool', 'milliseconds'=>'int'],
'SQLite3::changes' => ['int'],
'SQLite3::close' => ['bool'],
@@ -12600,7 +12578,7 @@ return [
'SQLite3::lastErrorMsg' => ['string'],
'SQLite3::lastInsertRowID' => ['int'],
'SQLite3::loadExtension' => ['bool', 'name'=>'string'],
'SQLite3::open' => ['void', 'filename'=>'string', 'flags='=>'int', 'encryptionKey='=>'?string'],
'SQLite3::open' => ['void', 'filename'=>'string', 'flags='=>'int', 'encryptionKey='=>'string'],
'SQLite3::openBlob' => ['resource|false', 'table'=>'string', 'column'=>'string', 'rowid'=>'int', 'database='=>'string', 'flags='=>'int'],
'SQLite3::prepare' => ['SQLite3Stmt|false', 'query'=>'string'],
'SQLite3::query' => ['SQLite3Result|false', 'query'=>'string'],
@@ -12723,7 +12701,7 @@ return [
'sqlsrv_commit' => ['bool', 'conn'=>'resource'],
'sqlsrv_configure' => ['bool', 'setting'=>'string', 'value'=>'mixed'],
'sqlsrv_connect' => ['resource|false', 'serverName'=>'string', 'connectionInfo='=>'array'],
'sqlsrv_errors' => ['?array', 'errorsOrWarnings='=>'int'],
'sqlsrv_errors' => ['?array', 'errorsAndOrWarnings='=>'int'],
'sqlsrv_execute' => ['bool', 'stmt'=>'resource'],
'sqlsrv_fetch' => ['?bool', 'stmt'=>'resource', 'row='=>'int', 'offset='=>'int'],
'sqlsrv_fetch_array' => ['array|null|false', 'stmt'=>'resource', 'fetchType='=>'int', 'row='=>'int', 'offset='=>'int'],
@@ -12898,7 +12876,7 @@ return [
'str_replace' => ['string|string[]', 'search'=>'string|array', 'replace'=>'string|array', 'subject'=>'string|array', '&w_count='=>'int'],
'str_rot13' => ['string', 'string'=>'string'],
'str_shuffle' => ['string', 'string'=>'string'],
'str_split' => ['list<string>', 'string'=>'string', 'length='=>'positive-int'],
'str_split' => ['list<non-empty-string>', 'string'=>'string', 'length='=>'positive-int'],
'str_starts_with' => ['bool', 'haystack'=>'string', 'needle'=>'string'],
'str_word_count' => ['array<int, string>|int', 'string'=>'string', 'format='=>'int', 'characters='=>'?string'],
'strcasecmp' => ['int', 'string1'=>'string', 'string2'=>'string'],
@@ -12913,7 +12891,7 @@ return [
'stream_context_create' => ['resource', 'options='=>'?array', 'params='=>'?array'],
'stream_context_get_default' => ['resource', 'options='=>'?array'],
'stream_context_get_options' => ['array', 'stream_or_context'=>'resource'],
'stream_context_get_params' => ['array', 'context'=>'resource'],
'stream_context_get_params' => ['array{notification:string,options:array}', 'context'=>'resource'],
'stream_context_set_default' => ['resource', 'options'=>'array'],
'stream_context_set_option' => ['bool', 'context'=>'', 'wrapper_or_options'=>'string', 'option_name'=>'string', 'value'=>''],
'stream_context_set_option\'1' => ['bool', 'context'=>'', 'wrapper_or_options'=>'array'],
@@ -12944,10 +12922,10 @@ return [
'stream_socket_accept' => ['resource|false', 'socket'=>'resource', 'timeout='=>'?float', '&w_peer_name='=>'string'],
'stream_socket_client' => ['resource|false', 'address'=>'string', '&w_error_code='=>'int', '&w_error_message='=>'string', 'timeout='=>'?float', 'flags='=>'int', 'context='=>'?resource'],
'stream_socket_enable_crypto' => ['int|bool', 'stream'=>'resource', 'enable'=>'bool', 'crypto_method='=>'?int', 'session_stream='=>'?resource'],
'stream_socket_get_name' => ['string', 'socket'=>'resource', 'remote'=>'bool'],
'stream_socket_get_name' => ['string|false', 'socket'=>'resource', 'remote'=>'bool'],
'stream_socket_pair' => ['resource[]|false', 'domain'=>'int', 'type'=>'int', 'protocol'=>'int'],
'stream_socket_recvfrom' => ['string', 'socket'=>'resource', 'length'=>'int', 'flags='=>'int', '&w_address='=>'string'],
'stream_socket_sendto' => ['int', 'socket'=>'resource', 'data'=>'string', 'flags='=>'int', 'address='=>'string'],
'stream_socket_recvfrom' => ['string|false', 'socket'=>'resource', 'length'=>'int', 'flags='=>'int', '&w_address='=>'string'],
'stream_socket_sendto' => ['int|false', 'socket'=>'resource', 'data'=>'string', 'flags='=>'int', 'address='=>'string'],
'stream_socket_server' => ['resource|false', 'address'=>'string', '&w_error_code='=>'int', '&w_error_message='=>'string', 'flags='=>'int', 'context='=>'resource'],
'stream_socket_shutdown' => ['bool', 'stream'=>'resource', 'mode'=>'int'],
'stream_supports_lock' => ['bool', 'stream'=>'resource'],
@@ -13603,7 +13581,7 @@ return [
'SyncSharedMemory::size' => ['int'],
'SyncSharedMemory::write' => ['int', 'string='=>'string', 'start='=>'int'],
'sys_get_temp_dir' => ['string'],
'sys_getloadavg' => ['array'],
'sys_getloadavg' => ['array|false'],
'syslog' => ['true', 'priority'=>'int', 'message'=>'string'],
'system' => ['string|false', 'command'=>'string', '&w_result_code='=>'int'],
'taint' => ['bool', '&rw_string'=>'string', '&...w_other_strings='=>'string'],
@@ -13985,7 +13963,7 @@ return [
'Transliterator::getErrorCode' => ['int'],
'Transliterator::getErrorMessage' => ['string'],
'Transliterator::listIDs' => ['array'],
'Transliterator::transliterate' => ['string|false', 'subject'=>'string', 'start='=>'int', 'end='=>'int'],
'Transliterator::transliterate' => ['string|false', 'string'=>'string', 'start='=>'int', 'end='=>'int'],
'transliterator_create' => ['?Transliterator', 'id'=>'string', 'direction='=>'int'],
'transliterator_create_from_rules' => ['?Transliterator', 'rules'=>'string', 'direction='=>'int'],
'transliterator_create_inverse' => ['?Transliterator', 'transliterator'=>'Transliterator'],
@@ -14008,8 +13986,8 @@ return [
'uasort' => ['true', '&rw_array'=>'array', 'callback'=>'callable(mixed,mixed):int'],
'ucfirst' => ['string', 'string'=>'string'],
'UConverter::__construct' => ['void', 'destination_encoding='=>'?string', 'source_encoding='=>'?string'],
'UConverter::convert' => ['string', 'string'=>'string', 'reverse='=>'bool'],
'UConverter::fromUCallback' => ['mixed', 'reason'=>'int', 'source'=>'string', 'codePoint'=>'string', '&w_error'=>'int'],
'UConverter::convert' => ['string', 'str'=>'string', 'reverse='=>'bool'],
'UConverter::fromUCallback' => ['string|int|array|null', 'reason'=>'int', 'source'=>'array', 'codePoint'=>'int', '&w_error'=>'int'],
'UConverter::getAliases' => ['array|false|null', 'name'=>'string'],
'UConverter::getAvailable' => ['array'],
'UConverter::getDestinationEncoding' => ['string|false|null'],
@@ -14020,12 +13998,12 @@ return [
'UConverter::getSourceType' => ['int|false|null'],
'UConverter::getStandards' => ['?array'],
'UConverter::getSubstChars' => ['string|false|null'],
'UConverter::reasonText' => ['string', 'reason='=>'int'],
'UConverter::reasonText' => ['string', 'reason'=>'int'],
'UConverter::setDestinationEncoding' => ['bool', 'encoding'=>'string'],
'UConverter::setSourceEncoding' => ['bool', 'encoding'=>'string'],
'UConverter::setSubstChars' => ['bool', 'chars'=>'string'],
'UConverter::toUCallback' => ['string|int|array|null', 'reason'=>'int', 'source'=>'string', 'codeUnits'=>'string', '&w_error'=>'int'],
'UConverter::transcode' => ['string', 'string'=>'string', 'toEncoding'=>'string', 'fromEncoding'=>'string', 'options='=>'?array'],
'UConverter::transcode' => ['string', 'str'=>'string', 'toEncoding'=>'string', 'fromEncoding'=>'string', 'options='=>'?array'],
'ucwords' => ['string', 'string'=>'string', 'separators='=>'string'],
'udm_add_search_limit' => ['bool', 'agent'=>'resource', 'var'=>'int', 'value'=>'string'],
'udm_alloc_agent' => ['resource', 'dbaddr'=>'string', 'dbmode='=>'string'],
@@ -14239,7 +14217,7 @@ return [
'UnexpectedValueException::getTrace' => ['list<array{file?:string,line?:int,function:string,class?:class-string,type?:\'::\'|\'->\',args?:array<mixed>}>'],
'UnexpectedValueException::getTraceAsString' => ['string'],
'uniqid' => ['non-empty-string', 'prefix='=>'string', 'more_entropy='=>'bool'],
'unixtojd' => ['int', 'timestamp='=>'?int'],
'unixtojd' => ['int|false', 'timestamp='=>'?int'],
'unlink' => ['bool', 'filename'=>'string', 'context='=>'resource'],
'unpack' => ['array|false', 'format'=>'string', 'string'=>'string', 'offset='=>'int'],
'unregister_tick_function' => ['void', 'callback'=>'callable'],
@@ -14502,17 +14480,12 @@ return [
'wddx_packet_start' => ['resource|false', 'comment='=>'string'],
'wddx_serialize_value' => ['string|false', 'value'=>'mixed', 'comment='=>'string'],
'wddx_serialize_vars' => ['string|false', 'var_name'=>'mixed', '...vars='=>'mixed'],
'WeakMap::__construct' => ['void'],
'WeakMap::count' => ['int'],
'WeakMap::current' => ['mixed'],
'WeakMap::key' => ['object'],
'WeakMap::next' => ['void'],
'WeakMap::getIterator' => ['Iterator'],
'WeakMap::offsetExists' => ['bool', 'object'=>'object'],
'WeakMap::offsetGet' => ['mixed', 'object'=>'object'],
'WeakMap::offsetSet' => ['void', 'object'=>'object', 'value'=>'mixed'],
'WeakMap::offsetUnset' => ['void', 'object'=>'object'],
'WeakMap::rewind' => ['void'],
'WeakMap::valid' => ['bool'],
'Weakref::acquire' => ['bool'],
'Weakref::get' => ['object'],
'Weakref::release' => ['bool'],
@@ -14728,7 +14701,7 @@ return [
'xml_parser_create' => ['XMLParser', 'encoding='=>'?string'],
'xml_parser_create_ns' => ['XMLParser', 'encoding='=>'?string', 'separator='=>'string'],
'xml_parser_free' => ['bool', 'parser'=>'XMLParser'],
'xml_parser_get_option' => ['string', 'parser'=>'XMLParser', 'option'=>'int'],
'xml_parser_get_option' => ['string|int', 'parser'=>'XMLParser', 'option'=>'int'],
'xml_parser_set_option' => ['bool', 'parser'=>'XMLParser', 'option'=>'int', 'value'=>'mixed'],
'xml_set_character_data_handler' => ['true', 'parser'=>'XMLParser', 'handler'=>'callable'],
'xml_set_default_handler' => ['true', 'parser'=>'XMLParser', 'handler'=>'callable'],
@@ -14753,18 +14726,18 @@ return [
'XMLReader::expand' => ['DOMNode|false', 'baseNode='=>'?DOMNode'],
'XMLReader::getAttribute' => ['?string', 'name'=>'string'],
'XMLReader::getAttributeNo' => ['?string', 'index'=>'int'],
'XMLReader::getAttributeNs' => ['?string', 'name'=>'string', 'namespaceuri'=>'string'],
'XMLReader::getAttributeNs' => ['?string', 'name'=>'string', 'namespace'=>'string'],
'XMLReader::getParserProperty' => ['bool', 'property'=>'int'],
'XMLReader::isValid' => ['bool'],
'XMLReader::lookupNamespace' => ['?string', 'prefix'=>'string'],
'XMLReader::moveToAttribute' => ['bool', 'name'=>'string'],
'XMLReader::moveToAttributeNo' => ['bool', 'index'=>'int'],
'XMLReader::moveToAttributeNs' => ['bool', 'localname'=>'string', 'namespaceuri'=>'string'],
'XMLReader::moveToAttributeNs' => ['bool', 'name'=>'string', 'namespace'=>'string'],
'XMLReader::moveToElement' => ['bool'],
'XMLReader::moveToFirstAttribute' => ['bool'],
'XMLReader::moveToNextAttribute' => ['bool'],
'XMLReader::next' => ['bool', 'localname='=>'string'],
'XMLReader::open' => ['bool', 'uri'=>'string', 'encoding='=>'?string', 'options='=>'int'],
'XMLReader::next' => ['bool', 'name='=>'?string'],
'XMLReader::open' => ['bool|XmlReader', 'uri'=>'string', 'encoding='=>'?string', 'flags='=>'int'],
'XMLReader::read' => ['bool'],
'XMLReader::readInnerXML' => ['string'],
'XMLReader::readOuterXML' => ['string'],
@@ -14773,7 +14746,7 @@ return [
'XMLReader::setRelaxNGSchema' => ['bool', 'filename'=>'?string'],
'XMLReader::setRelaxNGSchemaSource' => ['bool', 'source'=>'?string'],
'XMLReader::setSchema' => ['bool', 'filename'=>'?string'],
'XMLReader::XML' => ['bool', 'source'=>'string', 'encoding='=>'?string', 'options='=>'int'],
'XMLReader::XML' => ['bool|XMLReader', 'source'=>'string', 'encoding='=>'?string', 'flags='=>'int'],
'XMLWriter::endAttribute' => ['bool'],
'XMLWriter::endCdata' => ['bool'],
'XMLWriter::endComment' => ['bool'],
@@ -14866,13 +14839,13 @@ return [
'XsltProcessor::getSecurityPrefs' => ['int'],
'XSLTProcessor::hasExsltSupport' => ['bool'],
'XSLTProcessor::importStylesheet' => ['bool', 'stylesheet'=>'object'],
'XSLTProcessor::registerPHPFunctions' => ['void', 'functions='=>'mixed'],
'XSLTProcessor::registerPHPFunctions' => ['void', 'functions='=>'array|string|null'],
'XSLTProcessor::removeParameter' => ['bool', 'namespace'=>'string', 'name'=>'string'],
'XSLTProcessor::setParameter' => ['bool', 'namespace'=>'string', 'name'=>'string', 'value'=>'string'],
'XSLTProcessor::setParameter\'1' => ['bool', 'namespace'=>'string', 'options'=>'array'],
'XSLTProcessor::setProfiling' => ['bool', 'filename'=>'?string'],
'XsltProcessor::setSecurityPrefs' => ['int', 'preferences'=>'int'],
'XSLTProcessor::transformToDoc' => ['DOMDocument|false', 'document'=>'DOMNode'],
'XSLTProcessor::transformToDoc' => ['DOMDocument|false', 'document'=>'DOMNode', 'returnClass='=>'?string'],
'XSLTProcessor::transformToURI' => ['int', 'document'=>'DOMDocument', 'uri'=>'string'],
'XSLTProcessor::transformToXML' => ['string|false', 'document'=>'DOMDocument'],
'yac::__construct' => ['void', 'prefix='=>'string'],
@@ -15778,7 +15751,7 @@ return [
'zip_entry_compressedsize' => ['int', 'zip_entry'=>'resource'],
'zip_entry_compressionmethod' => ['string', 'zip_entry'=>'resource'],
'zip_entry_filesize' => ['int', 'zip_entry'=>'resource'],
'zip_entry_name' => ['string', 'zip_entry'=>'resource'],
'zip_entry_name' => ['string|false', 'zip_entry'=>'resource'],
'zip_entry_open' => ['bool', 'zip_dp'=>'resource', 'zip_entry'=>'resource', 'mode='=>'string'],
'zip_entry_read' => ['string|false', 'zip_entry'=>'resource', 'len='=>'int'],
'zip_open' => ['resource|int|false', 'filename'=>'string'],
@@ -15806,8 +15779,8 @@ return [
'ZipArchive::getStream' => ['resource|false', 'name'=>'string'],
'ZipArchive::getStreamIndex' => ['resource|false', 'index'=>'int', 'flags='=>'int'],
'ZipArchive::getStreamName' => ['resource|false', 'name'=>'string', 'flags='=>'int'],
'ZipArchive::isCompressionMethodSupported' => ['bool', 'method'=>'int', 'encode='=>'bool'],
'ZipArchive::isEncryptionMethodSupported' => ['bool', 'method'=>'int', 'encode='=>'bool'],
'ZipArchive::isCompressionMethodSupported' => ['bool', 'method'=>'int', 'enc='=>'bool'],
'ZipArchive::isEncryptionMethodSupported' => ['bool', 'method'=>'int', 'enc='=>'bool'],
'ZipArchive::locateName' => ['int|false', 'name'=>'string', 'flags='=>'int'],
'ZipArchive::open' => ['int|bool', 'filename'=>'string', 'flags='=>'int'],
'ZipArchive::registerCancelCallback' => ['bool', 'callback'=>'callable'],
@@ -15818,9 +15791,9 @@ return [
'ZipArchive::setArchiveComment' => ['bool', 'comment'=>'string'],
'ZipArchive::setCommentIndex' => ['bool', 'index'=>'int', 'comment'=>'string'],
'ZipArchive::setCommentName' => ['bool', 'name'=>'string', 'comment'=>'string'],
'ZipArchive::setCompressionIndex' => ['bool', 'index'=>'int', 'comp_method'=>'int', 'comp_flags='=>'int'],
'ZipArchive::setCompressionName' => ['bool', 'name'=>'string', 'comp_method'=>'int', 'comp_flags='=>'int'],
'ZipArchive::setEncryptionIndex' => ['bool', 'index'=>'int', 'method'=>'string', 'password='=>'?string'],
'ZipArchive::setCompressionIndex' => ['bool', 'index'=>'int', 'method'=>'int', 'compflags='=>'int'],
'ZipArchive::setCompressionName' => ['bool', 'name'=>'string', 'method'=>'int', 'compflags='=>'int'],
'ZipArchive::setEncryptionIndex' => ['bool', 'index'=>'int', 'method'=>'int', 'password='=>'?string'],
'ZipArchive::setEncryptionName' => ['bool', 'name'=>'string', 'method'=>'int', 'password='=>'?string'],
'ZipArchive::setExternalAttributesIndex' => ['bool', 'index'=>'int', 'opsys'=>'int', 'attr'=>'int', 'flags='=>'int'],
'ZipArchive::setExternalAttributesName' => ['bool', 'name'=>'string', 'opsys'=>'int', 'attr'=>'int', 'flags='=>'int'],

View File

@@ -30,7 +30,7 @@ return [
'sapi_windows_cp_get' => ['int', 'kind='=>'string'],
'sapi_windows_cp_is_utf8' => ['bool'],
'sapi_windows_cp_set' => ['bool', 'codepage'=>'int'],
'session_create_id' => ['string', 'prefix='=>'string'],
'session_create_id' => ['string|false', 'prefix='=>'string'],
'session_gc' => ['int|false'],
],
'changed' => [
@@ -42,6 +42,10 @@ return [
'old' => ['string|false', 'value'=>'IntlCalendar|DateTime|array{0: int, 1: int, 2: int, 3: int, 4: int, 5: int, 6: int, 7: int, 8: int}|array{tm_sec: int, tm_min: int, tm_hour: int, tm_mday: int, tm_mon: int, tm_year: int, tm_wday: int, tm_yday: int, tm_isdst: int}|string|int|float'],
'new' => ['string|false', 'value'=>'IntlCalendar|DateTimeInterface|array{0: int, 1: int, 2: int, 3: int, 4: int, 5: int, 6: int, 7: int, 8: int}|array{tm_sec: int, tm_min: int, tm_hour: int, tm_mday: int, tm_mon: int, tm_year: int, tm_wday: int, tm_yday: int, tm_isdst: int}|string|int|float'],
],
'SessionHandler::gc' => [
'old' => ['bool', 'max_lifetime'=>'int'],
'new' => ['int|false', 'max_lifetime'=>'int'],
],
'SQLite3::createFunction' => [
'old' => ['bool', 'name'=>'string', 'callback'=>'callable', 'argCount='=>'int'],
'new' => ['bool', 'name'=>'string', 'callback'=>'callable', 'argCount='=>'int', 'flags='=>'int'],

View File

@@ -19,7 +19,7 @@ return [
'DOMNodeList::count' => ['int'],
'ReflectionClass::isIterable' => ['bool'],
'ZipArchive::count' => ['int'],
'ZipArchive::setEncryptionIndex' => ['bool', 'index'=>'int', 'method'=>'string', 'password='=>'string'],
'ZipArchive::setEncryptionIndex' => ['bool', 'index'=>'int', 'method'=>'int', 'password='=>'string'],
'ZipArchive::setEncryptionName' => ['bool', 'name'=>'string', 'method'=>'int', 'password='=>'string'],
'ftp_append' => ['bool', 'ftp'=>'resource', 'remote_filename'=>'string', 'local_filename'=>'string', 'mode='=>'int'],
'hash_hmac_algos' => ['list<string>'],
@@ -103,7 +103,7 @@ return [
'sodium_crypto_secretstream_xchacha20poly1305_init_pull' => ['string', 'header'=>'string', 'key'=>'string'],
'sodium_crypto_secretstream_xchacha20poly1305_init_push' => ['array', 'key'=>'string'],
'sodium_crypto_secretstream_xchacha20poly1305_keygen' => ['non-empty-string'],
'sodium_crypto_secretstream_xchacha20poly1305_pull' => ['array', '&r_state'=>'string', 'ciphertext'=>'string', 'additional_data='=>'string'],
'sodium_crypto_secretstream_xchacha20poly1305_pull' => ['array|false', '&r_state'=>'string', 'ciphertext'=>'string', 'additional_data='=>'string'],
'sodium_crypto_secretstream_xchacha20poly1305_push' => ['string', '&w_state'=>'string', 'message'=>'string', 'additional_data='=>'string', 'tag='=>'int'],
'sodium_crypto_secretstream_xchacha20poly1305_rekey' => ['void', '&w_state'=>'string'],
'sodium_crypto_shorthash' => ['string', 'message'=>'string', 'key'=>'string'],

View File

@@ -18,7 +18,7 @@ return [
'added' => [
'DateTime::createFromImmutable' => ['static', 'object'=>'DateTimeImmutable'],
'JsonException::__clone' => ['void'],
'JsonException::__construct' => ['void'],
'JsonException::__construct' => ['void', "message="=>"string", 'code='=>'int', 'previous='=>'?Throwable'],
'JsonException::__toString' => ['string'],
'JsonException::__wakeup' => ['void'],
'JsonException::getCode' => ['int'],
@@ -77,8 +77,8 @@ return [
'new' => ['bool|string', 'ldap'=>'resource', 'user='=>'string', 'old_password='=>'string', 'new_password='=>'string', '&w_controls='=>'array'],
],
'ldap_list' => [
'old' => ['resource|false', 'ldap'=>'resource|array', 'base'=>'string', 'filter'=>'string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int'],
'new' => ['resource|false', 'ldap'=>'resource|array', 'base'=>'string', 'filter'=>'string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int', 'controls='=>'array'],
'old' => ['resource|false', 'ldap'=>'resource|array', 'base'=>'array|string', 'filter'=>'array|string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int'],
'new' => ['resource|false', 'ldap'=>'resource|array', 'base'=>'array|string', 'filter'=>'array|string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int', 'controls='=>'array'],
],
'ldap_mod_add' => [
'old' => ['bool', 'ldap'=>'resource', 'dn'=>'string', 'entry'=>'array'],
@@ -101,16 +101,16 @@ return [
'new' => ['bool', 'ldap'=>'resource', 'dn'=>'string', 'modifications_info'=>'array', 'controls='=>'array'],
],
'ldap_read' => [
'old' => ['resource|false', 'ldap'=>'resource|array', 'base'=>'string', 'filter'=>'string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int'],
'new' => ['resource|false', 'ldap'=>'resource|array', 'base'=>'string', 'filter'=>'string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int', 'controls='=>'array'],
'old' => ['resource|false', 'ldap'=>'resource|array', 'base'=>'array|string', 'filter'=>'array|string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int'],
'new' => ['resource|false', 'ldap'=>'resource|array', 'base'=>'array|string', 'filter'=>'array|string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int', 'controls='=>'array'],
],
'ldap_rename' => [
'old' => ['bool', 'ldap'=>'resource', 'dn'=>'string', 'new_rdn'=>'string', 'new_parent'=>'string', 'delete_old_rdn'=>'bool'],
'new' => ['bool', 'ldap'=>'resource', 'dn'=>'string', 'new_rdn'=>'string', 'new_parent'=>'string', 'delete_old_rdn'=>'bool', 'controls='=>'array'],
],
'ldap_search' => [
'old' => ['resource|false', 'ldap'=>'resource|resource[]', 'base'=>'string', 'filter'=>'string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int'],
'new' => ['resource|false', 'ldap'=>'resource|resource[]', 'base'=>'string', 'filter'=>'string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int', 'controls='=>'array'],
'old' => ['resource[]|resource|false', 'ldap'=>'resource|resource[]', 'base'=>'array|string', 'filter'=>'array|string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int'],
'new' => ['resource[]|resource|false', 'ldap'=>'resource|resource[]', 'base'=>'array|string', 'filter'=>'array|string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int', 'controls='=>'array'],
],
],
'removed' => [

View File

@@ -28,6 +28,13 @@ return [
'ReflectionParameter::getAttributes' => ['list<ReflectionAttribute>', 'name='=>'?string', 'flags='=>'int'],
'ReflectionProperty::getAttributes' => ['list<ReflectionAttribute>', 'name='=>'?string', 'flags='=>'int'],
'ReflectionUnionType::getTypes' => ['list<ReflectionNamedType>'],
'SplFixedArray::getIterator' => ['Iterator'],
'WeakMap::count' => ['int'],
'WeakMap::getIterator' => ['Iterator'],
'WeakMap::offsetExists' => ['bool', 'object'=>'object'],
'WeakMap::offsetGet' => ['mixed', 'object'=>'object'],
'WeakMap::offsetSet' => ['void', 'object'=>'object', 'value'=>'mixed'],
'WeakMap::offsetUnset' => ['void', 'object'=>'object'],
'fdiv' => ['float', 'num1'=>'float', 'num2'=>'float'],
'get_debug_type' => ['string', 'value'=>'mixed'],
'get_resource_id' => ['int', 'resource'=>'resource'],
@@ -217,6 +224,10 @@ return [
'old' => ['string', 'locale'=>'string', 'displayLocale='=>'string'],
'new' => ['string', 'locale'=>'string', 'displayLocale='=>'?string'],
],
'mysqli_stmt::__construct' => [
'old' => ['void', 'mysql'=>'mysqli', 'query='=>'string'],
'new' => ['void', 'mysql'=>'mysqli', 'query='=>'?string'],
],
'NumberFormatter::__construct' => [
'old' => ['void', 'locale'=>'string', 'style'=>'int', 'pattern='=>'string'],
'new' => ['void', 'locale'=>'string', 'style'=>'int', 'pattern='=>'?string'],
@@ -373,6 +384,14 @@ return [
'old' => ['?Closure', 'object='=>'object'],
'new' => ['Closure', 'object='=>'?object'],
],
'ReflectionObject::getConstants' => [
'old' => ['array<string,mixed>'],
'new' => ['array<string,mixed>', 'filter='=>'?int'],
],
'ReflectionObject::getReflectionConstants' => [
'old' => ['list<\ReflectionClassConstant>'],
'new' => ['list<\ReflectionClassConstant>', 'filter='=>'?int'],
],
'ReflectionObject::newInstanceArgs' => [
'old' => ['object', 'args='=>'list<mixed>'],
'new' => ['object', 'args='=>'list<mixed>|array<string, mixed>'],
@@ -461,6 +480,10 @@ return [
'old' => ['string|false'],
'new' => ['string'],
],
'XMLReader::next' => [
'old' => ['bool', 'name='=>'string'],
'new' => ['bool', 'name='=>'?string'],
],
'XMLWriter::startAttributeNs' => [
'old' => ['bool', 'prefix'=>'string', 'name'=>'string', 'namespace'=>'?string'],
'new' => ['bool', 'prefix'=>'?string', 'name'=>'string', 'namespace'=>'?string'],
@@ -478,8 +501,8 @@ return [
'new' => ['string'],
],
'ZipArchive::setEncryptionIndex' => [
'old' => ['bool', 'index'=>'int', 'method'=>'string', 'password='=>'string'],
'new' => ['bool', 'index'=>'int', 'method'=>'string', 'password='=>'?string'],
'old' => ['bool', 'index'=>'int', 'method'=>'int', 'password='=>'string'],
'new' => ['bool', 'index'=>'int', 'method'=>'int', 'password='=>'?string'],
],
'ZipArchive::setEncryptionName' => [
'old' => ['bool', 'name'=>'string', 'method'=>'int', 'password='=>'string'],
@@ -618,8 +641,8 @@ return [
'new' => ['void', 'handle'=>'CurlHandle'],
],
'curl_copy_handle' => [
'old' => ['resource', 'ch'=>'resource'],
'new' => ['CurlHandle', 'handle'=>'CurlHandle'],
'old' => ['resource|false', 'ch'=>'resource'],
'new' => ['CurlHandle|false', 'handle'=>'CurlHandle'],
],
'curl_errno' => [
'old' => ['int', 'ch'=>'resource'],
@@ -798,8 +821,8 @@ return [
'new' => ['DOMElement', 'node'=>'SimpleXMLElement'],
],
'easter_date' => [
'old' => ['int', 'year='=>'int'],
'new' => ['int', 'year='=>'?int'],
'old' => ['int', 'year='=>'int', 'mode='=>'int'],
'new' => ['int', 'year='=>'?int', 'mode='=>'int'],
],
'easter_days' => [
'old' => ['int', 'year='=>'int', 'mode='=>'int'],
@@ -934,8 +957,8 @@ return [
'new' => ['int|false', 'stream'=>'resource', 'data'=>'string', 'length='=>'?int'],
],
'get_class_methods' => [
'old' => ['list<string>|null', 'object_or_class'=>'mixed'],
'new' => ['list<string>', 'object_or_class'=>'object|class-string'],
'old' => ['list<non-falsy-string>|null', 'object_or_class'=>'mixed'],
'new' => ['list<non-falsy-string>', 'object_or_class'=>'object|class-string'],
],
'get_headers' => [
'old' => ['array|false', 'url'=>'string', 'associative='=>'int', 'context='=>'?resource'],
@@ -982,12 +1005,12 @@ return [
'new' => ['string|false', 'stream'=>'resource', 'length='=>'?int'],
],
'gzputs' => [
'old' => ['int', 'stream'=>'resource', 'data'=>'string', 'length='=>'int'],
'new' => ['int', 'stream'=>'resource', 'data'=>'string', 'length='=>'?int'],
'old' => ['int|false', 'stream'=>'resource', 'data'=>'string', 'length='=>'int'],
'new' => ['int|false', 'stream'=>'resource', 'data'=>'string', 'length='=>'?int'],
],
'gzwrite' => [
'old' => ['int', 'stream'=>'resource', 'data'=>'string', 'length='=>'int'],
'new' => ['int', 'stream'=>'resource', 'data'=>'string', 'length='=>'?int'],
'old' => ['int|false', 'stream'=>'resource', 'data'=>'string', 'length='=>'int'],
'new' => ['int|false', 'stream'=>'resource', 'data'=>'string', 'length='=>'?int'],
],
'hash' => [
'old' => ['string|false', 'algo'=>'string', 'data'=>'string', 'binary='=>'bool'],
@@ -1506,8 +1529,8 @@ return [
'new' => ['bool|string', 'ldap'=>'resource', 'user='=>'string', 'old_password='=>'string', 'new_password='=>'string', '&w_controls='=>'array|null'],
],
'ldap_list' => [
'old' => ['resource|false', 'ldap'=>'resource|array', 'base'=>'string', 'filter'=>'string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int', 'controls='=>'array'],
'new' => ['resource|false', 'ldap'=>'resource|array', 'base'=>'string', 'filter'=>'string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int', 'controls='=>'?array'],
'old' => ['resource|false', 'ldap'=>'resource|array', 'base'=>'array|string', 'filter'=>'array|string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int', 'controls='=>'array'],
'new' => ['resource|false', 'ldap'=>'resource|array', 'base'=>'array|string', 'filter'=>'array|string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int', 'controls='=>'?array'],
],
'ldap_rename_ext' => [
'old' => ['resource|false', 'ldap'=>'resource', 'dn'=>'string', 'new_rdn'=>'string', 'new_parent'=>'string', 'delete_old_rdn'=>'bool', 'controls='=>'array'],
@@ -1546,16 +1569,16 @@ return [
'new' => ['bool', 'ldap'=>'resource', 'dn'=>'string', 'modifications_info'=>'array', 'controls='=>'?array'],
],
'ldap_read' => [
'old' => ['resource|false', 'ldap'=>'resource|array', 'base'=>'string', 'filter'=>'string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int', 'controls='=>'array'],
'new' => ['resource|false', 'ldap'=>'resource|array', 'base'=>'string', 'filter'=>'string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int', 'controls='=>'?array'],
'old' => ['resource|false', 'ldap'=>'resource|array', 'base'=>'array|string', 'filter'=>'array|string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int', 'controls='=>'array'],
'new' => ['resource|false', 'ldap'=>'resource|array', 'base'=>'array|string', 'filter'=>'array|string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int', 'controls='=>'?array'],
],
'ldap_rename' => [
'old' => ['bool', 'ldap'=>'resource', 'dn'=>'string', 'new_rdn'=>'string', 'new_parent'=>'string', 'delete_old_rdn'=>'bool', 'controls='=>'array'],
'new' => ['bool', 'ldap'=>'resource', 'dn'=>'string', 'new_rdn'=>'string', 'new_parent'=>'string', 'delete_old_rdn'=>'bool', 'controls='=>'?array'],
],
'ldap_search' => [
'old' => ['resource|false', 'ldap'=>'resource|resource[]', 'base'=>'string', 'filter'=>'string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int', 'controls='=>'array'],
'new' => ['resource|false', 'ldap'=>'resource|resource[]', 'base'=>'string', 'filter'=>'string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int', 'controls='=>'?array'],
'old' => ['resource[]|resource|false', 'ldap'=>'resource|resource[]', 'base'=>'array|string', 'filter'=>'array|string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int', 'controls='=>'array'],
'new' => ['resource[]|resource|false', 'ldap'=>'resource|resource[]', 'base'=>'array|string', 'filter'=>'array|string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int', 'controls='=>'?array'],
],
'ldap_set_rebind_proc' => [
'old' => ['bool', 'ldap'=>'resource', 'callback'=>'callable'],
@@ -1866,8 +1889,8 @@ return [
'new' => ['bool', 'mysql'=>'mysqli', 'flags='=>'int', 'name='=>'?string'],
],
'number_format' => [
'old' => ['string', 'num'=>'float|int', 'decimals='=>'int'],
'new' => ['string', 'num'=>'float|int', 'decimals='=>'int', 'decimal_separator='=>'?string', 'thousands_separator='=>'?string'],
'old' => ['string', 'num'=>'float', 'decimals='=>'int'],
'new' => ['string', 'num'=>'float', 'decimals='=>'int', 'decimal_separator='=>'?string', 'thousands_separator='=>'?string'],
],
'numfmt_create' => [
'old' => ['NumberFormatter|null', 'locale'=>'string', 'style'=>'int', 'pattern='=>'string'],
@@ -2186,28 +2209,28 @@ return [
'new' => ['bool', 'semaphore'=>'SysvSemaphore'],
],
'session_cache_expire' => [
'old' => ['int', 'value='=>'int'],
'new' => ['int', 'value='=>'?int'],
'old' => ['int|false', 'value='=>'int'],
'new' => ['int|false', 'value='=>'?int'],
],
'session_cache_limiter' => [
'old' => ['string', 'value='=>'string'],
'new' => ['string', 'value='=>'?string'],
'old' => ['string|false', 'value='=>'string'],
'new' => ['string|false', 'value='=>'?string'],
],
'session_id' => [
'old' => ['string|false', 'id='=>'string'],
'new' => ['string|false', 'id='=>'?string'],
],
'session_module_name' => [
'old' => ['string', 'module='=>'string'],
'new' => ['string', 'module='=>'?string'],
'old' => ['string|false', 'module='=>'string'],
'new' => ['string|false', 'module='=>'?string'],
],
'session_name' => [
'old' => ['string|false', 'name='=>'string'],
'new' => ['string|false', 'name='=>'?string'],
],
'session_save_path' => [
'old' => ['string', 'path='=>'string'],
'new' => ['string', 'path='=>'?string'],
'old' => ['string|false', 'path='=>'string'],
'new' => ['string|false', 'path='=>'?string'],
],
'session_set_cookie_params' => [
'old' => ['bool', 'lifetime'=>'int', 'path='=>'string', 'domain='=>'string', 'secure='=>'bool', 'httponly='=>'bool'],
@@ -2578,8 +2601,8 @@ return [
'new' => ['int', 'mask='=>'?int'],
],
'unixtojd' => [
'old' => ['int', 'timestamp='=>'int'],
'new' => ['int', 'timestamp='=>'?int'],
'old' => ['int|false', 'timestamp='=>'int'],
'new' => ['int|false', 'timestamp='=>'?int'],
],
'xml_get_current_byte_index' => [
'old' => ['int|false', 'parser'=>'resource'],
@@ -2618,8 +2641,8 @@ return [
'new' => ['bool', 'parser'=>'XMLParser'],
],
'xml_parser_get_option' => [
'old' => ['string|false', 'parser'=>'resource', 'option'=>'int'],
'new' => ['string', 'parser'=>'XMLParser', 'option'=>'int'],
'old' => ['string|int', 'parser'=>'resource', 'option'=>'int'],
'new' => ['string|int', 'parser'=>'XMLParser', 'option'=>'int'],
],
'xml_parser_set_option' => [
'old' => ['bool', 'parser'=>'resource', 'option'=>'int', 'value'=>'mixed'],
@@ -2862,7 +2885,7 @@ return [
'ldap_control_paged_result' => ['bool', 'link_identifier'=>'resource', 'pagesize'=>'int', 'iscritical='=>'bool', 'cookie='=>'string'],
'ldap_control_paged_result_response' => ['bool', 'link_identifier'=>'resource', 'result_identifier'=>'resource', '&w_cookie'=>'string', '&w_estimated'=>'int'],
'ldap_sort' => ['bool', 'link_identifier'=>'resource', 'result_identifier'=>'resource', 'sortfilter'=>'string'],
'number_format\'1' => ['string', 'num'=>'float|int', 'decimals'=>'int', 'decimal_separator'=>'?string', 'thousands_separator'=>'?string'],
'number_format\'1' => ['string', 'num'=>'float', 'decimals'=>'int', 'decimal_separator'=>'?string', 'thousands_separator'=>'?string'],
'png2wbmp' => ['bool', 'pngname'=>'string', 'wbmpname'=>'string', 'dest_height'=>'int', 'dest_width'=>'int', 'threshold'=>'int'],
'read_exif_data' => ['array', 'filename'=>'string', 'sections_needed='=>'string', 'sub_arrays='=>'bool', 'read_thumbnail='=>'bool'],
'Reflection::export' => ['?string', 'r'=>'reflector', 'return='=>'bool'],
@@ -2883,6 +2906,11 @@ return [
'SimpleXMLIterator::next' => ['void'],
'SimpleXMLIterator::hasChildren' => ['bool'],
'SimpleXMLIterator::getChildren' => ['?SimpleXMLIterator'],
'SplFixedArray::current' => ['mixed'],
'SplFixedArray::key' => ['int'],
'SplFixedArray::next' => ['void'],
'SplFixedArray::rewind' => ['void'],
'SplFixedArray::valid' => ['bool'],
'SplTempFileObject::fgetss' => ['string', 'allowable_tags='=>'string'],
'xmlrpc_decode' => ['mixed', 'xml'=>'string', 'encoding='=>'string'],
'xmlrpc_decode_request' => ['?array', 'xml'=>'string', '&w_method'=>'string', 'encoding='=>'string'],

View File

@@ -619,8 +619,8 @@ return [
'new' => ['array|false', 'ldap'=>'LDAP\Connection', 'entry'=>'LDAP\ResultEntry', 'attribute'=>'string'],
],
'ldap_list' => [
'old' => ['resource|false', 'ldap'=>'resource|array', 'base'=>'string', 'filter'=>'string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int', 'controls='=>'?array'],
'new' => ['LDAP\Result|LDAP\Result[]|false', 'ldap'=>'LDAP\Connection|LDAP\Connection[]', 'base'=>'string', 'filter'=>'string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int', 'controls='=>'?array'],
'old' => ['resource|false', 'ldap'=>'resource|array', 'base'=>'array|string', 'filter'=>'array|string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int', 'controls='=>'?array'],
'new' => ['LDAP\Result|LDAP\Result[]|false', 'ldap'=>'LDAP\Connection|LDAP\Connection[]', 'base'=>'array|string', 'filter'=>'array|string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int', 'controls='=>'?array'],
],
'ldap_mod_add' => [
'old' => ['bool', 'ldap'=>'resource', 'dn'=>'string', 'entry'=>'array', 'controls='=>'?array'],
@@ -679,8 +679,8 @@ return [
'new' => ['bool', 'ldap'=>'LDAP\Connection', 'result'=>'LDAP\Result', '&w_error_code'=>'int', '&w_matched_dn='=>'string', '&w_error_message='=>'string', '&w_referrals='=>'array', '&w_controls='=>'array'],
],
'ldap_read' => [
'old' => ['resource|false', 'ldap'=>'resource|array', 'base'=>'string', 'filter'=>'string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int', 'controls='=>'?array'],
'new' => ['LDAP\Result|LDAP\Result[]|false', 'ldap'=>'LDAP\Connection|LDAP\Connection[]', 'base'=>'string', 'filter'=>'string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int', 'controls='=>'?array'],
'old' => ['resource|false', 'ldap'=>'resource|array', 'base'=>'array|string', 'filter'=>'array|string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int', 'controls='=>'?array'],
'new' => ['LDAP\Result|LDAP\Result[]|false', 'ldap'=>'LDAP\Connection|LDAP\Connection[]', 'base'=>'array|string', 'filter'=>'array|string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int', 'controls='=>'?array'],
],
'ldap_rename' => [
'old' => ['bool', 'ldap'=>'resource', 'dn'=>'string', 'new_rdn'=>'string', 'new_parent'=>'string', 'delete_old_rdn'=>'bool', 'controls='=>'?array'],
@@ -695,8 +695,8 @@ return [
'new' => ['bool', 'ldap'=>'LDAP\Connection', 'dn='=>'?string', 'password='=>'?string', 'mech='=>'?string', 'realm='=>'?string', 'authc_id='=>'?string', 'authz_id='=>'?string', 'props='=>'?string'],
],
'ldap_search' => [
'old' => ['resource|false', 'ldap'=>'resource|resource[]', 'base'=>'string', 'filter'=>'string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int', 'controls='=>'?array'],
'new' => ['LDAP\Result|LDAP\Result[]|false', 'ldap'=>'LDAP\Connection|LDAP\Connection[]', 'base'=>'string', 'filter'=>'string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int', 'controls='=>'?array'],
'old' => ['resource[]|resource|false', 'ldap'=>'resource|resource[]', 'base'=>'array|string', 'filter'=>'array|string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int', 'controls='=>'?array'],
'new' => ['LDAP\Result|LDAP\Result[]|false', 'ldap'=>'LDAP\Connection|LDAP\Connection[]', 'base'=>'array|string', 'filter'=>'array|string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int', 'controls='=>'?array'],
],
'ldap_set_option' => [
'old' => ['bool', 'ldap'=>'resource|null', 'option'=>'int', 'value'=>'mixed'],

View File

@@ -41,9 +41,17 @@ return [
'old' => ['resource', 'path'=>'string', 'mode'=>'string', 'handler='=>'string', '...handler_params='=>'string'],
'new' => ['resource', 'path'=>'string', 'mode'=>'string', 'handler='=>'?string', 'permission='=>'int', 'map_size='=>'int', 'flags='=>'?int'],
],
'iterator_count' => [
'old' => ['0|positive-int', 'iterator'=>'Traversable'],
'new' => ['0|positive-int', 'iterator'=>'Traversable|array'],
],
'iterator_to_array' => [
'old' => ['array', 'iterator'=>'Traversable', 'preserve_keys='=>'bool'],
'new' => ['array', 'iterator'=>'Traversable|array', 'preserve_keys='=>'bool'],
],
'str_split' => [
'old' => ['non-empty-list<string>', 'string'=>'string', 'length='=>'positive-int'],
'new' => ['list<string>', 'string'=>'string', 'length='=>'positive-int'],
'new' => ['list<non-empty-string>', 'string'=>'string', 'length='=>'positive-int'],
],
],

View File

@@ -293,7 +293,6 @@ return [
'COMPersistHelper::SaveToStream' => ['int', 'stream'=>''],
'COMPersistHelper::__construct' => ['void', 'variant'=>'object'],
'CURLFile::__construct' => ['void', 'filename'=>'string', 'mime_type='=>'string', 'posted_filename='=>'string'],
'CURLFile::__wakeup' => ['void'],
'CURLFile::getFilename' => ['string'],
'CURLFile::getMimeType' => ['string'],
'CURLFile::getPostFilename' => ['string'],
@@ -1623,8 +1622,6 @@ return [
'GEOSWKTWriter::setRoundingPrecision' => ['void', 'prec'=>'int'],
'GEOSWKTWriter::setTrim' => ['void', 'trim'=>'bool'],
'GEOSWKTWriter::write' => ['string', 'geom'=>'GEOSGeometry'],
'GMP::__construct' => ['void'],
'GMP::__toString' => ['numeric-string'],
'GearmanClient::__construct' => ['void'],
'GearmanClient::addOptions' => ['bool', 'options'=>'int'],
'GearmanClient::addServer' => ['bool', 'host='=>'string', 'port='=>'int'],
@@ -1738,7 +1735,6 @@ return [
'Gender\Gender::get' => ['int', 'name'=>'string', 'country='=>'int'],
'Gender\Gender::isNick' => ['array', 'name0'=>'string', 'name1'=>'string', 'country='=>'int'],
'Gender\Gender::similarNames' => ['array', 'name'=>'string', 'country='=>'int'],
'Generator::__wakeup' => ['void'],
'Generator::current' => ['mixed'],
'Generator::getReturn' => ['mixed'],
'Generator::key' => ['mixed'],
@@ -3265,7 +3261,7 @@ return [
'Iterator::rewind' => ['void'],
'Iterator::valid' => ['bool'],
'IteratorAggregate::getIterator' => ['Traversable'],
'IteratorIterator::__construct' => ['void', 'it'=>'Traversable'],
'IteratorIterator::__construct' => ['void', 'iterator'=>'Traversable', 'class='=>'?string'],
'IteratorIterator::current' => ['mixed'],
'IteratorIterator::getInnerIterator' => ['Iterator'],
'IteratorIterator::key' => ['mixed'],
@@ -3387,14 +3383,14 @@ return [
'LevelDBWriteBatch::delete' => ['', 'key'=>'', 'write_options='=>'array'],
'LevelDBWriteBatch::put' => ['', 'key'=>'', 'value'=>'', 'write_options='=>'array'],
'LevelDBWriteBatch::set' => ['', 'key'=>'', 'value'=>'', 'write_options='=>'array'],
'LimitIterator::__construct' => ['void', 'iterator'=>'Iterator', 'offset='=>'int', 'count='=>'int'],
'LimitIterator::__construct' => ['void', 'iterator'=>'Iterator', 'offset='=>'int', 'limit='=>'int'],
'LimitIterator::current' => ['mixed'],
'LimitIterator::getInnerIterator' => ['Iterator'],
'LimitIterator::getPosition' => ['int'],
'LimitIterator::key' => ['mixed'],
'LimitIterator::next' => ['void'],
'LimitIterator::rewind' => ['void'],
'LimitIterator::seek' => ['int', 'position'=>'int'],
'LimitIterator::seek' => ['int', 'offset'=>'int'],
'LimitIterator::valid' => ['bool'],
'Locale::acceptFromHttp' => ['string|false', 'header'=>'string'],
'Locale::canonicalize' => ['string', 'locale'=>'string'],
@@ -3539,14 +3535,14 @@ return [
'Memcached::touchByKey' => ['bool', 'server_key'=>'string', 'key'=>'string', 'expiration='=>'int'],
'MessageFormatter::__construct' => ['void', 'locale'=>'string', 'pattern'=>'string'],
'MessageFormatter::create' => ['MessageFormatter', 'locale'=>'string', 'pattern'=>'string'],
'MessageFormatter::format' => ['false|string', 'args'=>'array'],
'MessageFormatter::formatMessage' => ['false|string', 'locale'=>'string', 'pattern'=>'string', 'args'=>'array'],
'MessageFormatter::format' => ['false|string', 'values'=>'array'],
'MessageFormatter::formatMessage' => ['false|string', 'locale'=>'string', 'pattern'=>'string', 'values'=>'array'],
'MessageFormatter::getErrorCode' => ['int'],
'MessageFormatter::getErrorMessage' => ['string'],
'MessageFormatter::getLocale' => ['string'],
'MessageFormatter::getPattern' => ['string'],
'MessageFormatter::parse' => ['array|false', 'value'=>'string'],
'MessageFormatter::parseMessage' => ['array|false', 'locale'=>'string', 'pattern'=>'string', 'source'=>'string'],
'MessageFormatter::parse' => ['array|false', 'string'=>'string'],
'MessageFormatter::parseMessage' => ['array|false', 'locale'=>'string', 'pattern'=>'string', 'message'=>'string'],
'MessageFormatter::setPattern' => ['bool', 'pattern'=>'string'],
'Mongo::__construct' => ['void', 'server='=>'string', 'options='=>'array', 'driver_options='=>'array'],
'Mongo::__get' => ['MongoDB', 'dbname'=>'string'],
@@ -3735,7 +3731,7 @@ return [
'MongoDBRef::create' => ['array', 'collection'=>'string', 'id'=>'mixed', 'database='=>'string'],
'MongoDBRef::get' => ['?array', 'db'=>'MongoDB', 'ref'=>'array'],
'MongoDBRef::isRef' => ['bool', 'ref'=>'mixed'],
'MongoDB\BSON\Binary::__construct' => ['void', 'data' => 'string', 'type' => 'int'],
'MongoDB\BSON\Binary::__construct' => ['void', 'data' => 'string', 'type=' => 'int'],
'MongoDB\BSON\Binary::getData' => ['string'],
'MongoDB\BSON\Binary::getType' => ['int'],
'MongoDB\BSON\Binary::__toString' => ['string'],
@@ -4191,7 +4187,7 @@ return [
'MongoWriteConcernException::getTrace' => ['list<array{file?:string,line?:int,function:string,class?:class-string,type?:\'::\'|\'->\',args?:array<mixed>}>'],
'MongoWriteConcernException::getTraceAsString' => ['string'],
'MultipleIterator::__construct' => ['void', 'flags='=>'int'],
'MultipleIterator::attachIterator' => ['void', 'iterator'=>'Iterator', 'infos='=>'string'],
'MultipleIterator::attachIterator' => ['void', 'iterator'=>'Iterator', 'info='=>'string|int|null'],
'MultipleIterator::containsIterator' => ['bool', 'iterator'=>'Iterator'],
'MultipleIterator::countIterators' => ['int'],
'MultipleIterator::current' => ['array|false'],
@@ -4273,20 +4269,20 @@ return [
'NumberFormatter::__construct' => ['void', 'locale'=>'string', 'style'=>'int', 'pattern='=>'string'],
'NumberFormatter::create' => ['NumberFormatter|null', 'locale'=>'string', 'style'=>'int', 'pattern='=>'string'],
'NumberFormatter::format' => ['string|false', 'num'=>'', 'type='=>'int'],
'NumberFormatter::formatCurrency' => ['string|false', 'num'=>'float', 'currency'=>'string'],
'NumberFormatter::getAttribute' => ['int|false', 'attr'=>'int'],
'NumberFormatter::formatCurrency' => ['string|false', 'amount'=>'float', 'currency'=>'string'],
'NumberFormatter::getAttribute' => ['int|float|false', 'attribute'=>'int'],
'NumberFormatter::getErrorCode' => ['int'],
'NumberFormatter::getErrorMessage' => ['string'],
'NumberFormatter::getLocale' => ['string', 'type='=>'int'],
'NumberFormatter::getPattern' => ['string|false'],
'NumberFormatter::getSymbol' => ['string|false', 'attr'=>'int'],
'NumberFormatter::getTextAttribute' => ['string|false', 'attr'=>'int'],
'NumberFormatter::parse' => ['float|false', 'string'=>'string', 'type='=>'int', '&rw_position='=>'int'],
'NumberFormatter::parseCurrency' => ['float|false', 'string'=>'string', '&w_currency'=>'string', '&rw_position='=>'int'],
'NumberFormatter::setAttribute' => ['bool', 'attr'=>'int', 'value'=>''],
'NumberFormatter::getSymbol' => ['string|false', 'symbol'=>'int'],
'NumberFormatter::getTextAttribute' => ['string|false', 'attribute'=>'int'],
'NumberFormatter::parse' => ['int|float|false', 'string'=>'string', 'type='=>'int', '&rw_offset='=>'int'],
'NumberFormatter::parseCurrency' => ['float|false', 'string'=>'string', '&w_currency'=>'string', '&rw_offset='=>'int'],
'NumberFormatter::setAttribute' => ['bool', 'attribute'=>'int', 'value'=>'int|float'],
'NumberFormatter::setPattern' => ['bool', 'pattern'=>'string'],
'NumberFormatter::setSymbol' => ['bool', 'attr'=>'int', 'symbol'=>'string'],
'NumberFormatter::setTextAttribute' => ['bool', 'attr'=>'int', 'value'=>'string'],
'NumberFormatter::setSymbol' => ['bool', 'symbol'=>'int', 'value'=>'string'],
'NumberFormatter::setTextAttribute' => ['bool', 'attribute'=>'int', 'value'=>'string'],
'OAuth::__construct' => ['void', 'consumer_key'=>'string', 'consumer_secret'=>'string', 'signature_method='=>'string', 'auth_type='=>'int'],
'OAuth::disableDebug' => ['bool'],
'OAuth::disableRedirects' => ['bool'],
@@ -4710,8 +4706,6 @@ return [
'PDFlib::utf32_to_utf16' => ['string', 'utf32string'=>'string', 'ordering'=>'string'],
'PDFlib::utf8_to_utf16' => ['string', 'utf8string'=>'string', 'ordering'=>'string'],
'PDO::__construct' => ['void', 'dsn'=>'string', 'username='=>'?string', 'password='=>'?string', 'options='=>'?array'],
'PDO::__sleep' => ['list<string>'],
'PDO::__wakeup' => ['void'],
'PDO::beginTransaction' => ['bool'],
'PDO::commit' => ['bool'],
'PDO::cubrid_schema' => ['array', 'schema_type'=>'int', 'table_name='=>'string', 'col_name='=>'string'],
@@ -4749,8 +4743,6 @@ return [
'PDOException::getPrevious' => ['?Throwable'],
'PDOException::getTrace' => ['list<array{file?:string,line?:int,function:string,class?:class-string,type?:\'::\'|\'->\',args?:array<mixed>}>'],
'PDOException::getTraceAsString' => ['string'],
'PDOStatement::__sleep' => ['list<string>'],
'PDOStatement::__wakeup' => ['void'],
'PDOStatement::bindColumn' => ['bool', 'column'=>'string|int', '&rw_var'=>'mixed', 'type='=>'int', 'maxLength='=>'int', 'driverOptions='=>'mixed'],
'PDOStatement::bindParam' => ['bool', 'param'=>'string|int', '&rw_var'=>'mixed', 'type='=>'int', 'maxLength='=>'int', 'driverOptions='=>'mixed'],
'PDOStatement::bindValue' => ['bool', 'param'=>'string|int', 'value'=>'mixed', 'type='=>'int'],
@@ -5758,7 +5750,7 @@ return [
'Reflection::export' => ['?string', 'r'=>'reflector', 'return='=>'bool'],
'Reflection::getModifierNames' => ['list<string>', 'modifiers'=>'int'],
'ReflectionClass::__clone' => ['void'],
'ReflectionClass::__construct' => ['void', 'argument'=>'object|class-string'],
'ReflectionClass::__construct' => ['void', 'objectOrClass'=>'object|class-string'],
'ReflectionClass::__toString' => ['string'],
'ReflectionClass::export' => ['?string', 'argument'=>'string|object', 'return='=>'bool'],
'ReflectionClass::getConstant' => ['mixed', 'name'=>'string'],
@@ -5792,7 +5784,7 @@ return [
'ReflectionClass::hasConstant' => ['bool', 'name'=>'string'],
'ReflectionClass::hasMethod' => ['bool', 'name'=>'string'],
'ReflectionClass::hasProperty' => ['bool', 'name'=>'string'],
'ReflectionClass::implementsInterface' => ['bool', 'interface_name'=>'interface-string|ReflectionClass'],
'ReflectionClass::implementsInterface' => ['bool', 'interface'=>'interface-string|ReflectionClass'],
'ReflectionClass::inNamespace' => ['bool'],
'ReflectionClass::isAbstract' => ['bool'],
'ReflectionClass::isAnonymous' => ['bool'],
@@ -5810,7 +5802,7 @@ return [
'ReflectionClass::newInstanceArgs' => ['object', 'args='=>'list<mixed>'],
'ReflectionClass::newInstanceWithoutConstructor' => ['object'],
'ReflectionClass::setStaticPropertyValue' => ['void', 'name'=>'string', 'value'=>'mixed'],
'ReflectionClassConstant::__construct' => ['void', 'class'=>'mixed', 'name'=>'string'],
'ReflectionClassConstant::__construct' => ['void', 'class'=>'object|class-string', 'constant'=>'string'],
'ReflectionClassConstant::__toString' => ['string'],
'ReflectionClassConstant::export' => ['string', 'class'=>'mixed', 'name'=>'string', 'return='=>'bool'],
'ReflectionClassConstant::getDeclaringClass' => ['ReflectionClass'],
@@ -5836,7 +5828,7 @@ return [
'ReflectionExtension::info' => ['void'],
'ReflectionExtension::isPersistent' => ['bool'],
'ReflectionExtension::isTemporary' => ['bool'],
'ReflectionFunction::__construct' => ['void', 'name'=>'callable-string|Closure'],
'ReflectionFunction::__construct' => ['void', 'function'=>'callable-string|Closure'],
'ReflectionFunction::__toString' => ['string'],
'ReflectionFunction::export' => ['?string', 'name'=>'string', 'return='=>'bool'],
'ReflectionFunction::getClosure' => ['Closure'],
@@ -5896,7 +5888,7 @@ return [
'ReflectionFunctionAbstract::isUserDefined' => ['bool'],
'ReflectionFunctionAbstract::isVariadic' => ['bool'],
'ReflectionFunctionAbstract::returnsReference' => ['bool'],
'ReflectionGenerator::__construct' => ['void', 'generator'=>'object'],
'ReflectionGenerator::__construct' => ['void', 'generator'=>'Generator'],
'ReflectionGenerator::getExecutingFile' => ['string'],
'ReflectionGenerator::getExecutingGenerator' => ['Generator'],
'ReflectionGenerator::getExecutingLine' => ['int'],
@@ -5946,14 +5938,14 @@ return [
'ReflectionMethod::isUserDefined' => ['bool'],
'ReflectionMethod::isVariadic' => ['bool'],
'ReflectionMethod::returnsReference' => ['bool'],
'ReflectionMethod::setAccessible' => ['void', 'visible'=>'bool'],
'ReflectionMethod::setAccessible' => ['void', 'accessible'=>'bool'],
'ReflectionNamedType::__clone' => ['void'],
'ReflectionNamedType::__toString' => ['string'],
'ReflectionNamedType::allowsNull' => ['bool'],
'ReflectionNamedType::getName' => ['string'],
'ReflectionNamedType::isBuiltin' => ['bool'],
'ReflectionObject::__clone' => ['void'],
'ReflectionObject::__construct' => ['void', 'argument'=>'object'],
'ReflectionObject::__construct' => ['void', 'object'=>'object'],
'ReflectionObject::__toString' => ['string'],
'ReflectionObject::export' => ['?string', 'argument'=>'object', 'return='=>'bool'],
'ReflectionObject::getConstant' => ['mixed', 'name'=>'string'],
@@ -5987,7 +5979,7 @@ return [
'ReflectionObject::hasConstant' => ['bool', 'name'=>'string'],
'ReflectionObject::hasMethod' => ['bool', 'name'=>'string'],
'ReflectionObject::hasProperty' => ['bool', 'name'=>'string'],
'ReflectionObject::implementsInterface' => ['bool', 'interface_name'=>'ReflectionClass|string'],
'ReflectionObject::implementsInterface' => ['bool', 'interface'=>'ReflectionClass|interface-string'],
'ReflectionObject::inNamespace' => ['bool'],
'ReflectionObject::isAbstract' => ['bool'],
'ReflectionObject::isAnonymous' => ['bool'],
@@ -6007,7 +5999,7 @@ return [
'ReflectionObject::newInstanceWithoutConstructor' => ['object'],
'ReflectionObject::setStaticPropertyValue' => ['void', 'name'=>'string', 'value'=>'string'],
'ReflectionParameter::__clone' => ['void'],
'ReflectionParameter::__construct' => ['void', 'function'=>'', 'parameter'=>''],
'ReflectionParameter::__construct' => ['void', 'function'=>'string|array|object', 'param'=>'int|string'],
'ReflectionParameter::__toString' => ['string'],
'ReflectionParameter::allowsNull' => ['bool'],
'ReflectionParameter::canBePassedByValue' => ['bool'],
@@ -6029,7 +6021,7 @@ return [
'ReflectionParameter::isPassedByReference' => ['bool'],
'ReflectionParameter::isVariadic' => ['bool'],
'ReflectionProperty::__clone' => ['void'],
'ReflectionProperty::__construct' => ['void', 'class'=>'', 'name'=>'string'],
'ReflectionProperty::__construct' => ['void', 'class'=>'object|class-string', 'property'=>'string'],
'ReflectionProperty::__toString' => ['string'],
'ReflectionProperty::export' => ['?string', 'class'=>'mixed', 'name'=>'string', 'return='=>'bool'],
'ReflectionProperty::getDeclaringClass' => ['ReflectionClass'],
@@ -6043,7 +6035,7 @@ return [
'ReflectionProperty::isProtected' => ['bool'],
'ReflectionProperty::isPublic' => ['bool'],
'ReflectionProperty::isStatic' => ['bool'],
'ReflectionProperty::setAccessible' => ['void', 'visible'=>'bool'],
'ReflectionProperty::setAccessible' => ['void', 'accessible'=>'bool'],
'ReflectionProperty::setValue' => ['void', 'object'=>'null|object', 'value'=>''],
'ReflectionProperty::setValue\'1' => ['void', 'value'=>''],
'ReflectionType::__clone' => ['void'],
@@ -6076,13 +6068,13 @@ return [
'RegexIterator::setMode' => ['void', 'mode'=>'int'],
'RegexIterator::setPregFlags' => ['void', 'pregFlags'=>'int'],
'RegexIterator::valid' => ['bool'],
'ResourceBundle::__construct' => ['void', 'locale'=>'string', 'bundlename'=>'string', 'fallback='=>'bool'],
'ResourceBundle::__construct' => ['void', 'locale'=>'?string', 'bundle'=>'?string', 'fallback='=>'bool'],
'ResourceBundle::count' => ['int'],
'ResourceBundle::create' => ['?ResourceBundle', 'locale'=>'string', 'bundlename'=>'string', 'fallback='=>'bool'],
'ResourceBundle::create' => ['?ResourceBundle', 'locale'=>'?string', 'bundle'=>'?string', 'fallback='=>'bool'],
'ResourceBundle::get' => ['mixed', 'index'=>'string|int', 'fallback='=>'bool'],
'ResourceBundle::getErrorCode' => ['int'],
'ResourceBundle::getErrorMessage' => ['string'],
'ResourceBundle::getLocales' => ['array', 'bundlename'=>'string'],
'ResourceBundle::getLocales' => ['array|false', 'bundle'=>'string'],
'Runkit_Sandbox::__construct' => ['void', 'options='=>'array'],
'Runkit_Sandbox_Parent' => [''],
'Runkit_Sandbox_Parent::__construct' => ['void'],
@@ -6193,8 +6185,8 @@ return [
'SNMP::getnext' => ['string|array|false', 'objectId'=>'string|array'],
'SNMP::set' => ['bool', 'objectId'=>'string|array', 'type'=>'string|array', 'value'=>'string|array'],
'SNMP::setSecurity' => ['bool', 'securityLevel'=>'string', 'authProtocol='=>'string', 'authPassphrase='=>'string', 'privacyProtocol='=>'string', 'privacyPassphrase='=>'string', 'contextName='=>'string', 'contextEngineId='=>'string'],
'SNMP::walk' => ['array|false', 'objectId'=>'string', 'suffixAsKey='=>'bool', 'maxRepetitions='=>'int', 'nonRepeaters='=>'int'],
'SQLite3::__construct' => ['void', 'filename'=>'string', 'flags='=>'int', 'encryptionKey='=>'?string'],
'SNMP::walk' => ['array|false', 'objectId'=>'array|string', 'suffixAsKey='=>'bool', 'maxRepetitions='=>'int', 'nonRepeaters='=>'int'],
'SQLite3::__construct' => ['void', 'filename'=>'string', 'flags='=>'int', 'encryptionKey='=>'string'],
'SQLite3::busyTimeout' => ['bool', 'milliseconds'=>'int'],
'SQLite3::changes' => ['int'],
'SQLite3::close' => ['bool'],
@@ -6208,7 +6200,7 @@ return [
'SQLite3::lastErrorMsg' => ['string'],
'SQLite3::lastInsertRowID' => ['int'],
'SQLite3::loadExtension' => ['bool', 'name'=>'string'],
'SQLite3::open' => ['void', 'filename'=>'string', 'flags='=>'int', 'encryptionKey='=>'?string'],
'SQLite3::open' => ['void', 'filename'=>'string', 'flags='=>'int', 'encryptionKey='=>'string'],
'SQLite3::openBlob' => ['resource|false', 'table'=>'string', 'column'=>'string', 'rowid'=>'int', 'dbname='=>'string'],
'SQLite3::prepare' => ['SQLite3Stmt|false', 'query'=>'string'],
'SQLite3::query' => ['SQLite3Result|false', 'query'=>'string'],
@@ -6619,9 +6611,9 @@ return [
'SessionHandler::close' => ['bool'],
'SessionHandler::create_sid' => ['string'],
'SessionHandler::destroy' => ['bool', 'id'=>'string'],
'SessionHandler::gc' => ['bool', 'maxlifetime'=>'int'],
'SessionHandler::open' => ['bool', 'save_path'=>'string', 'session_name'=>'string'],
'SessionHandler::read' => ['string', 'id'=>'string'],
'SessionHandler::gc' => ['bool', 'max_lifetime'=>'int'],
'SessionHandler::open' => ['bool', 'path'=>'string', 'name'=>'string'],
'SessionHandler::read' => ['string|false', 'id'=>'string'],
'SessionHandler::write' => ['bool', 'id'=>'string', 'data'=>'string'],
'SessionHandlerInterface::close' => ['bool'],
'SessionHandlerInterface::destroy' => ['bool', 'id'=>'string'],
@@ -6634,26 +6626,26 @@ return [
'SessionUpdateTimestampHandler::validateId' => ['char', 'id'=>'string'],
'SessionUpdateTimestampHandlerInterface::updateTimestamp' => ['bool', 'key'=>'string', 'value'=>'string'],
'SessionUpdateTimestampHandlerInterface::validateId' => ['bool', 'key'=>'string'],
'SimpleXMLElement::__construct' => ['void', 'data'=>'string', 'options='=>'int', 'data_is_url='=>'bool', 'ns='=>'string', 'is_prefix='=>'bool'],
'SimpleXMLElement::__construct' => ['void', 'data'=>'string', 'options='=>'int', 'dataIsURL='=>'bool', 'namespaceOrPrefix='=>'string', 'isPrefix='=>'bool'],
'SimpleXMLElement::__get' => ['SimpleXMLElement', 'name'=>'string'],
'SimpleXMLElement::__toString' => ['string'],
'SimpleXMLElement::addAttribute' => ['void', 'name'=>'string', 'value='=>'string', 'ns='=>'string'],
'SimpleXMLElement::addChild' => ['SimpleXMLElement', 'name'=>'string', 'value='=>'string', 'ns='=>'string'],
'SimpleXMLElement::addAttribute' => ['void', 'qualifiedName'=>'string', 'value'=>'string', 'namespace='=>'?string'],
'SimpleXMLElement::addChild' => ['?SimpleXMLElement', 'qualifiedName'=>'string', 'value='=>'?string', 'namespace='=>'?string'],
'SimpleXMLElement::asXML' => ['string|bool', 'filename'=>'string'],
'SimpleXMLElement::asXML\'1' => ['string|false'],
'SimpleXMLElement::attributes' => ['?SimpleXMLElement', 'ns='=>'string', 'is_prefix='=>'bool'],
'SimpleXMLElement::children' => ['SimpleXMLElement', 'ns='=>'string', 'is_prefix='=>'bool'],
'SimpleXMLElement::attributes' => ['?SimpleXMLElement', 'namespaceOrPrefix='=>'?string', 'isPrefix='=>'bool'],
'SimpleXMLElement::children' => ['?SimpleXMLElement', 'namespaceOrPrefix='=>'?string', 'isPrefix='=>'bool'],
'SimpleXMLElement::count' => ['int'],
'SimpleXMLElement::getDocNamespaces' => ['string[]', 'recursive='=>'bool', 'from_root='=>'bool'],
'SimpleXMLElement::getDocNamespaces' => ['array<string,string>', 'recursive='=>'bool', 'fromRoot='=>'bool'],
'SimpleXMLElement::getName' => ['string'],
'SimpleXMLElement::getNamespaces' => ['string[]', 'recursive='=>'bool'],
'SimpleXMLElement::getNamespaces' => ['array<string,string>', 'recursive='=>'bool'],
'SimpleXMLElement::offsetExists' => ['bool', 'offset'=>'int|string'],
'SimpleXMLElement::offsetGet' => ['SimpleXMLElement', 'offset'=>'int|string'],
'SimpleXMLElement::offsetSet' => ['void', 'offset'=>'int|string', 'value'=>'mixed'],
'SimpleXMLElement::offsetUnset' => ['void', 'offset'=>'int|string'],
'SimpleXMLElement::registerXPathNamespace' => ['bool', 'prefix'=>'string', 'ns'=>'string'],
'SimpleXMLElement::registerXPathNamespace' => ['bool', 'prefix'=>'string', 'namespace'=>'string'],
'SimpleXMLElement::saveXML' => ['string|bool', 'filename='=>'string'],
'SimpleXMLElement::xpath' => ['SimpleXMLElement[]|false', 'path'=>'string'],
'SimpleXMLElement::xpath' => ['SimpleXMLElement[]|false|null', 'expression'=>'string'],
'SimpleXMLIterator::current' => ['?SimpleXMLIterator'],
'SimpleXMLIterator::getChildren' => ['?SimpleXMLIterator'],
'SimpleXMLIterator::hasChildren' => ['bool'],
@@ -7496,10 +7488,10 @@ return [
'SplDoublyLinkedList::isEmpty' => ['bool'],
'SplDoublyLinkedList::key' => ['int'],
'SplDoublyLinkedList::next' => ['void'],
'SplDoublyLinkedList::offsetExists' => ['bool', 'index'=>'mixed'],
'SplDoublyLinkedList::offsetGet' => ['mixed', 'index'=>'mixed'],
'SplDoublyLinkedList::offsetSet' => ['void', 'index'=>'mixed', 'value'=>'mixed'],
'SplDoublyLinkedList::offsetUnset' => ['void', 'index'=>'mixed'],
'SplDoublyLinkedList::offsetExists' => ['bool', 'index'=>'int'],
'SplDoublyLinkedList::offsetGet' => ['mixed', 'index'=>'int'],
'SplDoublyLinkedList::offsetSet' => ['void', 'index'=>'?int', 'value'=>'mixed'],
'SplDoublyLinkedList::offsetUnset' => ['void', 'index'=>'int'],
'SplDoublyLinkedList::pop' => ['mixed'],
'SplDoublyLinkedList::prev' => ['void'],
'SplDoublyLinkedList::push' => ['void', 'value'=>'mixed'],
@@ -7515,7 +7507,6 @@ return [
'SplEnum::getConstList' => ['array', 'include_default='=>'bool'],
'SplFileInfo::__construct' => ['void', 'filename'=>'string'],
'SplFileInfo::__toString' => ['string'],
'SplFileInfo::__wakeup' => ['void'],
'SplFileInfo::getATime' => ['int|false'],
'SplFileInfo::getBasename' => ['string', 'suffix='=>'string'],
'SplFileInfo::getCTime' => ['int|false'],
@@ -7989,7 +7980,7 @@ return [
'Transliterator::getErrorCode' => ['int'],
'Transliterator::getErrorMessage' => ['string'],
'Transliterator::listIDs' => ['array'],
'Transliterator::transliterate' => ['string|false', 'subject'=>'string', 'start='=>'int', 'end='=>'int'],
'Transliterator::transliterate' => ['string|false', 'string'=>'string', 'start='=>'int', 'end='=>'int'],
'TypeError::__clone' => ['void'],
'TypeError::__construct' => ['void', 'message='=>'string', 'code='=>'int', 'previous='=>'?Throwable'],
'TypeError::__toString' => ['string'],
@@ -8001,8 +7992,8 @@ return [
'TypeError::getTrace' => ['list<array{file?:string,line?:int,function:string,class?:class-string,type?:\'::\'|\'->\',args?:array<mixed>}>'],
'TypeError::getTraceAsString' => ['string'],
'UConverter::__construct' => ['void', 'destination_encoding='=>'?string', 'source_encoding='=>'?string'],
'UConverter::convert' => ['string', 'string'=>'string', 'reverse='=>'bool'],
'UConverter::fromUCallback' => ['mixed', 'reason'=>'int', 'source'=>'string', 'codePoint'=>'string', '&w_error'=>'int'],
'UConverter::convert' => ['string', 'str'=>'string', 'reverse='=>'bool'],
'UConverter::fromUCallback' => ['string|int|array|null', 'reason'=>'int', 'source'=>'array', 'codePoint'=>'int', '&w_error'=>'int'],
'UConverter::getAliases' => ['array|false|null', 'name'=>'string'],
'UConverter::getAvailable' => ['array'],
'UConverter::getDestinationEncoding' => ['string|false|null'],
@@ -8013,12 +8004,12 @@ return [
'UConverter::getSourceType' => ['int|false|null'],
'UConverter::getStandards' => ['?array'],
'UConverter::getSubstChars' => ['string|false|null'],
'UConverter::reasonText' => ['string', 'reason='=>'int'],
'UConverter::reasonText' => ['string', 'reason'=>'int'],
'UConverter::setDestinationEncoding' => ['bool', 'encoding'=>'string'],
'UConverter::setSourceEncoding' => ['bool', 'encoding'=>'string'],
'UConverter::setSubstChars' => ['bool', 'chars'=>'string'],
'UConverter::toUCallback' => ['string|int|array|null', 'reason'=>'int', 'source'=>'string', 'codeUnits'=>'string', '&w_error'=>'int'],
'UConverter::transcode' => ['string', 'string'=>'string', 'toEncoding'=>'string', 'fromEncoding'=>'string', 'options='=>'?array'],
'UConverter::transcode' => ['string', 'str'=>'string', 'toEncoding'=>'string', 'fromEncoding'=>'string', 'options='=>'?array'],
'UnderflowException::__clone' => ['void'],
'UnderflowException::__construct' => ['void', 'message='=>'string', 'code='=>'int', 'previous='=>'?Throwable'],
'UnderflowException::__toString' => ['string'],
@@ -8183,17 +8174,6 @@ return [
'Vtiful\Kernel\Validation::validationType' => ['?Vtiful\Kernel\Validation', 'type'=>'int'],
'Vtiful\Kernel\Validation::valueList' => ['?Vtiful\Kernel\Validation', 'value_list'=>'array'],
'Vtiful\Kernel\Validation::valueNumber' => ['?Vtiful\Kernel\Validation', 'value_number'=>'int'],
'WeakMap::__construct' => ['void'],
'WeakMap::count' => ['int'],
'WeakMap::current' => ['mixed'],
'WeakMap::key' => ['object'],
'WeakMap::next' => ['void'],
'WeakMap::offsetExists' => ['bool', 'object'=>'object'],
'WeakMap::offsetGet' => ['mixed', 'object'=>'object'],
'WeakMap::offsetSet' => ['void', 'object'=>'object', 'value'=>'mixed'],
'WeakMap::offsetUnset' => ['void', 'object'=>'object'],
'WeakMap::rewind' => ['void'],
'WeakMap::valid' => ['bool'],
'Weakref::acquire' => ['bool'],
'Weakref::get' => ['object'],
'Weakref::release' => ['bool'],
@@ -8252,23 +8232,23 @@ return [
'XMLDiff\File::merge' => ['string', 'src'=>'string', 'diff'=>'string'],
'XMLDiff\Memory::diff' => ['string', 'from'=>'string', 'to'=>'string'],
'XMLDiff\Memory::merge' => ['string', 'src'=>'string', 'diff'=>'string'],
'XMLReader::XML' => ['bool', 'source'=>'string', 'encoding='=>'?string', 'options='=>'int'],
'XMLReader::XML' => ['bool|XMLReader', 'source'=>'string', 'encoding='=>'?string', 'flags='=>'int'],
'XMLReader::close' => ['bool'],
'XMLReader::expand' => ['DOMNode|false', 'baseNode='=>'?DOMNode'],
'XMLReader::getAttribute' => ['?string', 'name'=>'string'],
'XMLReader::getAttributeNo' => ['?string', 'index'=>'int'],
'XMLReader::getAttributeNs' => ['?string', 'name'=>'string', 'namespaceuri'=>'string'],
'XMLReader::getAttributeNs' => ['?string', 'name'=>'string', 'namespace'=>'string'],
'XMLReader::getParserProperty' => ['bool', 'property'=>'int'],
'XMLReader::isValid' => ['bool'],
'XMLReader::lookupNamespace' => ['?string', 'prefix'=>'string'],
'XMLReader::moveToAttribute' => ['bool', 'name'=>'string'],
'XMLReader::moveToAttributeNo' => ['bool', 'index'=>'int'],
'XMLReader::moveToAttributeNs' => ['bool', 'localname'=>'string', 'namespaceuri'=>'string'],
'XMLReader::moveToAttributeNs' => ['bool', 'name'=>'string', 'namespace'=>'string'],
'XMLReader::moveToElement' => ['bool'],
'XMLReader::moveToFirstAttribute' => ['bool'],
'XMLReader::moveToNextAttribute' => ['bool'],
'XMLReader::next' => ['bool', 'localname='=>'string'],
'XMLReader::open' => ['bool', 'uri'=>'string', 'encoding='=>'?string', 'options='=>'int'],
'XMLReader::next' => ['bool', 'name='=>'string'],
'XMLReader::open' => ['bool|XmlReader', 'uri'=>'string', 'encoding='=>'?string', 'flags='=>'int'],
'XMLReader::read' => ['bool'],
'XMLReader::readInnerXML' => ['string'],
'XMLReader::readOuterXML' => ['string'],
@@ -8322,12 +8302,12 @@ return [
'XSLTProcessor::getParameter' => ['string|false', 'namespace'=>'string', 'name'=>'string'],
'XSLTProcessor::hasExsltSupport' => ['bool'],
'XSLTProcessor::importStylesheet' => ['bool', 'stylesheet'=>'object'],
'XSLTProcessor::registerPHPFunctions' => ['void', 'functions='=>'mixed'],
'XSLTProcessor::registerPHPFunctions' => ['void', 'functions='=>'array|string|null'],
'XSLTProcessor::removeParameter' => ['bool', 'namespace'=>'string', 'name'=>'string'],
'XSLTProcessor::setParameter' => ['bool', 'namespace'=>'string', 'name'=>'string', 'value'=>'string'],
'XSLTProcessor::setParameter\'1' => ['bool', 'namespace'=>'string', 'options'=>'array'],
'XSLTProcessor::setProfiling' => ['bool', 'filename'=>'?string'],
'XSLTProcessor::transformToDoc' => ['DOMDocument|false', 'document'=>'DOMNode'],
'XSLTProcessor::transformToDoc' => ['DOMDocument|false', 'document'=>'DOMNode', 'returnClass='=>'?string'],
'XSLTProcessor::transformToURI' => ['int', 'document'=>'DOMDocument', 'uri'=>'string'],
'XSLTProcessor::transformToXML' => ['string|false', 'document'=>'DOMDocument'],
'Xcom::__construct' => ['void', 'fabric_url='=>'string', 'fabric_token='=>'string', 'capability_token='=>'string'],
@@ -9216,8 +9196,8 @@ return [
'ZipArchive::getNameIndex' => ['string|false', 'index'=>'int', 'flags='=>'int'],
'ZipArchive::getStatusString' => ['string|false'],
'ZipArchive::getStream' => ['resource|false', 'name'=>'string'],
'ZipArchive::isCompressionMethodSupported' => ['bool', 'method'=>'int', 'encode='=>'bool'],
'ZipArchive::isEncryptionMethodSupported' => ['bool', 'method'=>'int', 'encode='=>'bool'],
'ZipArchive::isCompressionMethodSupported' => ['bool', 'method'=>'int', 'enc='=>'bool'],
'ZipArchive::isEncryptionMethodSupported' => ['bool', 'method'=>'int', 'enc='=>'bool'],
'ZipArchive::locateName' => ['int|false', 'name'=>'string', 'flags='=>'int'],
'ZipArchive::open' => ['int|bool', 'filename'=>'string', 'flags='=>'int'],
'ZipArchive::registerCancelCallback' => ['bool', 'callback'=>'callable'],
@@ -9228,8 +9208,8 @@ return [
'ZipArchive::setArchiveComment' => ['bool', 'comment'=>'string'],
'ZipArchive::setCommentIndex' => ['bool', 'index'=>'int', 'comment'=>'string'],
'ZipArchive::setCommentName' => ['bool', 'name'=>'string', 'comment'=>'string'],
'ZipArchive::setCompressionIndex' => ['bool', 'index'=>'int', 'comp_method'=>'int', 'comp_flags='=>'int'],
'ZipArchive::setCompressionName' => ['bool', 'name'=>'string', 'comp_method'=>'int', 'comp_flags='=>'int'],
'ZipArchive::setCompressionIndex' => ['bool', 'index'=>'int', 'method'=>'int', 'compflags='=>'int'],
'ZipArchive::setCompressionName' => ['bool', 'name'=>'string', 'method'=>'int', 'compflags='=>'int'],
'ZipArchive::setExternalAttributesIndex' => ['bool', 'index'=>'int', 'opsys'=>'int', 'attr'=>'int', 'flags='=>'int'],
'ZipArchive::setExternalAttributesName' => ['bool', 'name'=>'string', 'opsys'=>'int', 'attr'=>'int', 'flags='=>'int'],
'ZipArchive::setMtimeIndex' => ['bool', 'index'=>'int', 'timestamp'=>'int', 'flags='=>'int'],
@@ -9401,9 +9381,7 @@ return [
'array_uintersect_assoc\'1' => ['array', 'array'=>'array', 'rest'=>'array', 'arr3'=>'array', 'arg4'=>'array|callable', '...rest='=>'array|callable(mixed,mixed):int'],
'array_uintersect_uassoc' => ['array', 'array'=>'array', 'rest'=>'array', 'data_compare_func'=>'callable(mixed,mixed):int', 'key_compare_func'=>'callable(mixed,mixed):int'],
'array_uintersect_uassoc\'1' => ['array', 'array'=>'array', 'rest'=>'array', 'arr3'=>'array', 'arg4'=>'array|callable(mixed,mixed):int', 'arg5'=>'array|callable(mixed,mixed):int', '...rest='=>'array|callable(mixed,mixed):int'],
'array_unique' => ['array', 'array'=>'array', 'flags='=>'0'],
'array_unique\'1' => ['array<int|float|string|null>', 'array'=>'array<int|float|string|null>', 'flags='=>'1'],
'array_unique\'2' => ['array<int|float|string|bool|\Stringable|null>', 'array'=>'array<int|float|string|bool|\Stringable|null>', 'flags='=>'2|5'],
'array_unique' => ['array', 'array'=>'array', 'flags='=>'int'],
'array_unshift' => ['int', '&rw_array'=>'array', '...values'=>'mixed'],
'array_values' => ['list<mixed>', 'array'=>'array'],
'array_walk' => ['bool', '&rw_array'=>'array', 'callback'=>'callable', 'arg='=>'mixed'],
@@ -9500,7 +9478,7 @@ return [
'call_user_func_array' => ['mixed|false', 'callback'=>'callable', 'args'=>'list<mixed>'],
'call_user_method' => ['mixed', 'method_name'=>'string', 'object'=>'object', 'parameter='=>'mixed', '...args='=>'mixed'],
'call_user_method_array' => ['mixed', 'method_name'=>'string', 'object'=>'object', 'params'=>'list<mixed>'],
'ceil' => ['float', 'num'=>'float'],
'ceil' => ['float', 'num'=>'float|int'],
'chdb::__construct' => ['void', 'pathname'=>'string'],
'chdb::get' => ['string', 'key'=>'string'],
'chdb_create' => ['bool', 'pathname'=>'string', 'data'=>'array'],
@@ -9763,7 +9741,7 @@ return [
'cubrid_unbuffered_query' => ['resource', 'query'=>'string', 'conn_identifier='=>''],
'cubrid_version' => ['string'],
'curl_close' => ['void', 'ch'=>'resource'],
'curl_copy_handle' => ['resource', 'ch'=>'resource'],
'curl_copy_handle' => ['resource|false', 'ch'=>'resource'],
'curl_errno' => ['int', 'ch'=>'resource'],
'curl_error' => ['string', 'ch'=>'resource'],
'curl_escape' => ['string|false', 'ch'=>'resource', 'string'=>'string'],
@@ -9812,7 +9790,7 @@ return [
'date_get_last_errors' => ['array{warning_count:int,warnings:array<int,string>,error_count:int,errors:array<int,string>}|false'],
'date_interval_create_from_date_string' => ['DateInterval', 'datetime'=>'string'],
'date_interval_format' => ['string', 'object'=>'DateInterval', 'format'=>'string'],
'date_isodate_set' => ['DateTime|false', 'object'=>'DateTime', 'year'=>'int', 'week'=>'int', 'dayOfWeek='=>'int|mixed'],
'date_isodate_set' => ['DateTime', 'object'=>'DateTime', 'year'=>'int', 'week'=>'int', 'dayOfWeek='=>'int'],
'date_modify' => ['DateTime|false', 'object'=>'DateTime', 'modifier'=>'string'],
'date_offset_get' => ['int|false', 'object'=>'DateTimeInterface'],
'date_parse' => ['array|false', 'datetime'=>'string'],
@@ -9841,7 +9819,7 @@ return [
'datefmt_get_timezone_id' => ['string|false', 'formatter'=>'IntlDateFormatter'],
'datefmt_is_lenient' => ['bool', 'formatter'=>'IntlDateFormatter'],
'datefmt_localtime' => ['array|false', 'formatter'=>'IntlDateFormatter', 'string'=>'string', '&rw_offset='=>'int'],
'datefmt_parse' => ['int|false', 'formatter'=>'IntlDateFormatter', 'string'=>'string', '&rw_offset='=>'int'],
'datefmt_parse' => ['float|int|false', 'formatter'=>'IntlDateFormatter', 'string'=>'string', '&rw_offset='=>'int'],
'datefmt_set_calendar' => ['bool', 'formatter'=>'IntlDateFormatter', 'calendar'=>'IntlCalendar|int|null'],
'datefmt_set_lenient' => ['void', 'formatter'=>'IntlDateFormatter', 'lenient'=>'bool'],
'datefmt_set_pattern' => ['bool', 'formatter'=>'IntlDateFormatter', 'pattern'=>'string'],
@@ -9903,20 +9881,20 @@ return [
'db2_tableprivileges' => [''],
'db2_tables' => ['resource|false', 'connection'=>'resource', 'qualifier='=>'?string', 'schema='=>'?string', 'table_name='=>'?string', 'table_type='=>'?string'],
'dba_close' => ['void', 'dba'=>'resource'],
'dba_delete' => ['bool', 'key'=>'string', 'dba'=>'resource'],
'dba_exists' => ['bool', 'key'=>'string', 'dba'=>'resource'],
'dba_fetch' => ['string|false', 'key'=>'string', 'skip'=>'int', 'dba'=>'resource'],
'dba_fetch\'1' => ['string|false', 'key'=>'string', 'skip'=>'resource'],
'dba_delete' => ['bool', 'key'=>'array|string', 'dba'=>'resource'],
'dba_exists' => ['bool', 'key'=>'array|string', 'dba'=>'resource'],
'dba_fetch' => ['string|false', 'key'=>'array|string', 'skip'=>'int', 'dba'=>'resource'],
'dba_fetch\'1' => ['string|false', 'key'=>'array|string', 'skip'=>'resource'],
'dba_firstkey' => ['string', 'dba'=>'resource'],
'dba_handlers' => ['array', 'full_info='=>'bool'],
'dba_insert' => ['bool', 'key'=>'string', 'value'=>'string', 'dba'=>'resource'],
'dba_insert' => ['bool', 'key'=>'array|string', 'value'=>'string', 'dba'=>'resource'],
'dba_key_split' => ['array|false', 'key'=>'string|false|null'],
'dba_list' => ['array'],
'dba_nextkey' => ['string', 'dba'=>'resource'],
'dba_open' => ['resource', 'path'=>'string', 'mode'=>'string', 'handler='=>'string', '...handler_params='=>'string'],
'dba_optimize' => ['bool', 'dba'=>'resource'],
'dba_popen' => ['resource', 'path'=>'string', 'mode'=>'string', 'handler='=>'string', '...handler_params='=>'string'],
'dba_replace' => ['bool', 'key'=>'string', 'value'=>'string', 'dba'=>'resource'],
'dba_replace' => ['bool', 'key'=>'array|string', 'value'=>'string', 'dba'=>'resource'],
'dba_sync' => ['bool', 'dba'=>'resource'],
'dbase_add_record' => ['bool', 'dbase_identifier'=>'resource', 'record'=>'array'],
'dbase_close' => ['bool', 'dbase_identifier'=>'resource'],
@@ -10048,7 +10026,7 @@ return [
'dotnet_load' => ['int', 'assembly_name'=>'string', 'datatype_name='=>'string', 'codepage='=>'int'],
'doubleval' => ['float', 'value'=>'mixed'],
'each' => ['array{0:int|string,key:int|string,1:mixed,value:mixed}', '&r_arr'=>'array'],
'easter_date' => ['int', 'year='=>'int'],
'easter_date' => ['int', 'year='=>'int', 'mode='=>'int'],
'easter_days' => ['int', 'year='=>'int', 'mode='=>'int'],
'echo' => ['void', 'arg1'=>'string', '...args='=>'string'],
'eio_busy' => ['resource', 'delay'=>'int', 'pri='=>'int', 'callback='=>'callable', 'data='=>'mixed'],
@@ -10514,7 +10492,7 @@ return [
'finfo_set_flags' => ['bool', 'finfo'=>'resource', 'flags'=>'int'],
'floatval' => ['float', 'value'=>'mixed'],
'flock' => ['bool', 'stream'=>'resource', 'operation'=>'int', '&w_would_block='=>'int'],
'floor' => ['float', 'num'=>'float'],
'floor' => ['float', 'num'=>'float|int'],
'flush' => ['void'],
'fmod' => ['float', 'num1'=>'float', 'num2'=>'float'],
'fnmatch' => ['bool', 'pattern'=>'string', 'filename'=>'string', 'flags='=>'int'],
@@ -10699,8 +10677,8 @@ return [
'get_called_class' => ['class-string'],
'get_cfg_var' => ['string|false', 'option'=>'string'],
'get_class' => ['class-string', 'object='=>'object'],
'get_class_methods' => ['list<string>|null', 'object_or_class'=>'mixed'],
'get_class_vars' => ['array<string,mixed>', 'class'=>'string'],
'get_class_methods' => ['list<non-falsy-string>|null', 'object_or_class'=>'mixed'],
'get_class_vars' => ['array<non-falsy-string,mixed>', 'class'=>'string'],
'get_current_user' => ['string'],
'get_declared_classes' => ['list<class-string>'],
'get_declared_interfaces' => ['list<class-string>'],
@@ -10749,7 +10727,7 @@ return [
'gettimeofday' => ['array<string, int>'],
'gettimeofday\'1' => ['float', 'as_float='=>'true'],
'gettype' => ['string', 'value'=>'mixed'],
'glob' => ['list<string>|false', 'pattern'=>'string', 'flags='=>'int'],
'glob' => ['list<non-empty-string>|false', 'pattern'=>'non-empty-string', 'flags='=>'int<1, max>'],
'gmdate' => ['string', 'format'=>'string', 'timestamp='=>'int'],
'gmmktime' => ['int|false', 'hour='=>'int', 'minute='=>'int', 'second='=>'int', 'month='=>'int', 'day='=>'int', 'year='=>'int'],
'gmp_abs' => ['GMP', 'num'=>'GMP|string|int'],
@@ -10899,20 +10877,20 @@ return [
'gzdeflate' => ['string|false', 'data'=>'string', 'level='=>'int', 'encoding='=>'int'],
'gzencode' => ['string|false', 'data'=>'string', 'level='=>'int', 'encoding='=>'int'],
'gzeof' => ['bool', 'stream'=>'resource'],
'gzfile' => ['list<string>', 'filename'=>'string', 'use_include_path='=>'int'],
'gzfile' => ['list<string>|false', 'filename'=>'string', 'use_include_path='=>'int'],
'gzgetc' => ['string|false', 'stream'=>'resource'],
'gzgets' => ['string|false', 'stream'=>'resource', 'length='=>'int'],
'gzgetss' => ['string|false', 'zp'=>'resource', 'length'=>'int', 'allowable_tags='=>'string'],
'gzinflate' => ['string|false', 'data'=>'string', 'max_length='=>'int'],
'gzopen' => ['resource|false', 'filename'=>'string', 'mode'=>'string', 'use_include_path='=>'int'],
'gzpassthru' => ['int', 'stream'=>'resource'],
'gzputs' => ['int', 'stream'=>'resource', 'data'=>'string', 'length='=>'int'],
'gzputs' => ['int|false', 'stream'=>'resource', 'data'=>'string', 'length='=>'int'],
'gzread' => ['string|0', 'stream'=>'resource', 'length'=>'int'],
'gzrewind' => ['bool', 'stream'=>'resource'],
'gzseek' => ['int', 'stream'=>'resource', 'offset'=>'int', 'whence='=>'int'],
'gztell' => ['int|false', 'stream'=>'resource'],
'gzuncompress' => ['string|false', 'data'=>'string', 'max_length='=>'int'],
'gzwrite' => ['int', 'stream'=>'resource', 'data'=>'string', 'length='=>'int'],
'gzwrite' => ['int|false', 'stream'=>'resource', 'data'=>'string', 'length='=>'int'],
'hash' => ['string|false', 'algo'=>'string', 'data'=>'string', 'binary='=>'bool'],
'hashTableObj::clear' => ['void'],
'hashTableObj::get' => ['string', 'key'=>'string'],
@@ -12127,7 +12105,7 @@ return [
'ldap_count_entries' => ['int', 'ldap'=>'resource', 'result'=>'resource'],
'ldap_delete' => ['bool', 'ldap'=>'resource', 'dn'=>'string'],
'ldap_delete_ext' => ['resource|false', 'ldap'=>'resource', 'dn'=>'string', 'controls='=>'array'],
'ldap_dn2ufn' => ['string', 'dn'=>'string'],
'ldap_dn2ufn' => ['string|false', 'dn'=>'string'],
'ldap_err2str' => ['string', 'errno'=>'int'],
'ldap_errno' => ['int', 'ldap'=>'resource'],
'ldap_error' => ['string', 'ldap'=>'resource'],
@@ -12143,7 +12121,7 @@ return [
'ldap_get_option' => ['bool', 'ldap'=>'resource', 'option'=>'int', '&w_value='=>'array|string|int'],
'ldap_get_values' => ['array|false', 'ldap'=>'resource', 'entry'=>'resource', 'attribute'=>'string'],
'ldap_get_values_len' => ['array|false', 'ldap'=>'resource', 'entry'=>'resource', 'attribute'=>'string'],
'ldap_list' => ['resource|false', 'ldap'=>'resource|array', 'base'=>'string', 'filter'=>'string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int'],
'ldap_list' => ['resource|false', 'ldap'=>'resource|array', 'base'=>'array|string', 'filter'=>'array|string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int'],
'ldap_mod_add' => ['bool', 'ldap'=>'resource', 'dn'=>'string', 'entry'=>'array'],
'ldap_mod_add_ext' => ['resource|false', 'ldap'=>'resource', 'dn'=>'string', 'entry'=>'array', 'controls='=>'array'],
'ldap_mod_del' => ['bool', 'ldap'=>'resource', 'dn'=>'string', 'entry'=>'array'],
@@ -12157,11 +12135,11 @@ return [
'ldap_next_reference' => ['resource|false', 'ldap'=>'resource', 'entry'=>'resource'],
'ldap_parse_reference' => ['bool', 'ldap'=>'resource', 'entry'=>'resource', '&w_referrals'=>'array'],
'ldap_parse_result' => ['bool', 'ldap'=>'resource', 'result'=>'resource', '&w_error_code'=>'int', '&w_matched_dn='=>'string', '&w_error_message='=>'string', '&w_referrals='=>'array', '&w_controls='=>'array'],
'ldap_read' => ['resource|false', 'ldap'=>'resource|array', 'base'=>'string', 'filter'=>'string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int'],
'ldap_read' => ['resource|false', 'ldap'=>'resource|array', 'base'=>'array|string', 'filter'=>'array|string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int'],
'ldap_rename' => ['bool', 'ldap'=>'resource', 'dn'=>'string', 'new_rdn'=>'string', 'new_parent'=>'string', 'delete_old_rdn'=>'bool'],
'ldap_rename_ext' => ['resource|false', 'ldap'=>'resource', 'dn'=>'string', 'new_rdn'=>'string', 'new_parent'=>'string', 'delete_old_rdn'=>'bool', 'controls='=>'array'],
'ldap_sasl_bind' => ['bool', 'ldap'=>'resource', 'dn='=>'string', 'password='=>'string', 'mech='=>'string', 'realm='=>'string', 'authc_id='=>'string', 'authz_id='=>'string', 'props='=>'string'],
'ldap_search' => ['resource|false', 'ldap'=>'resource|resource[]', 'base'=>'string', 'filter'=>'string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int'],
'ldap_search' => ['resource[]|resource|false', 'ldap'=>'resource|resource[]', 'base'=>'array|string', 'filter'=>'array|string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int'],
'ldap_set_option' => ['bool', 'ldap'=>'resource|null', 'option'=>'int', 'value'=>'mixed'],
'ldap_set_rebind_proc' => ['bool', 'ldap'=>'resource', 'callback'=>'callable'],
'ldap_sort' => ['bool', 'link_identifier'=>'resource', 'result_identifier'=>'resource', 'sortfilter'=>'string'],
@@ -12726,7 +12704,6 @@ return [
'mysqli::commit' => ['bool', 'flags='=>'int', 'name='=>'string'],
'mysqli::connect' => ['null|false', 'hostname='=>'string', 'username='=>'string', 'password='=>'string', 'database='=>'string', 'port='=>'int', 'socket='=>'string'],
'mysqli::debug' => ['bool', 'options'=>'string'],
'mysqli::disable_reads_from_master' => ['bool'],
'mysqli::dump_debug_info' => ['bool'],
'mysqli::escape_string' => ['string', 'string'=>'string'],
'mysqli::get_charset' => ['object'],
@@ -12740,23 +12717,19 @@ return [
'mysqli::next_result' => ['bool'],
'mysqli::options' => ['bool', 'option'=>'int', 'value'=>'string|int'],
'mysqli::ping' => ['bool'],
'mysqli::poll' => ['int|false', '&w_read'=>'array', '&w_write'=>'array', '&w_error'=>'array', 'seconds'=>'int', 'microseconds='=>'int'],
'mysqli::poll' => ['int|false', '&w_read'=>'?array', '&w_error'=>'?array', '&w_reject'=>'array', 'seconds'=>'int', 'microseconds='=>'int'],
'mysqli::prepare' => ['mysqli_stmt|false', 'query'=>'string'],
'mysqli::query' => ['bool|mysqli_result', 'query'=>'string', 'result_mode='=>'int'],
'mysqli::real_connect' => ['bool', 'hostname='=>'string|null', 'username='=>'string|null', 'password='=>'string|null', 'database='=>'string|null', 'port='=>'int|null', 'socket='=>'string|null', 'flags='=>'int'],
'mysqli::real_connect' => ['bool', 'hostname='=>'?string', 'username='=>'?string', 'password='=>'?string', 'database='=>'?string', 'port='=>'?int', 'socket='=>'?string', 'flags='=>'int'],
'mysqli::real_escape_string' => ['string', 'string'=>'string'],
'mysqli::real_query' => ['bool', 'query'=>'string'],
'mysqli::reap_async_query' => ['mysqli_result|false'],
'mysqli::refresh' => ['bool', 'flags'=>'int'],
'mysqli::release_savepoint' => ['bool', 'name'=>'string'],
'mysqli::rollback' => ['bool', 'flags='=>'int', 'name='=>'string'],
'mysqli::rpl_query_type' => ['int', 'query'=>'string'],
'mysqli::savepoint' => ['bool', 'name'=>'string'],
'mysqli::select_db' => ['bool', 'database'=>'string'],
'mysqli::send_query' => ['bool', 'query'=>'string'],
'mysqli::set_charset' => ['bool', 'charset'=>'string'],
'mysqli::set_local_infile_default' => ['void'],
'mysqli::set_local_infile_handler' => ['bool', 'read_func='=>'callable'],
'mysqli::set_opt' => ['bool', 'option'=>'int', 'value'=>'string|int'],
'mysqli::ssl_set' => ['bool', 'key'=>'?string', 'certificate'=>'?string', 'ca_certificate'=>'?string', 'ca_path'=>'?string', 'cipher_algos'=>'?string'],
'mysqli::stat' => ['string|false'],
@@ -12778,8 +12751,6 @@ return [
'mysqli_debug' => ['true', 'options'=>'string'],
'mysqli_disable_reads_from_master' => ['bool', 'link'=>'mysqli'],
'mysqli_disable_rpl_parse' => ['bool', 'link'=>'mysqli'],
'mysqli_driver::embedded_server_end' => ['void'],
'mysqli_driver::embedded_server_start' => ['bool', 'start'=>'int', 'arguments'=>'array', 'groups'=>'array'],
'mysqli_dump_debug_info' => ['bool', 'mysql'=>'mysqli'],
'mysqli_embedded_server_end' => ['void'],
'mysqli_embedded_server_start' => ['bool', 'start'=>'int', 'arguments'=>'array', 'groups'=>'array'],
@@ -12797,9 +12768,9 @@ return [
'mysqli_fetch_array\'1' => ['array<string,null|int|float|string>|false|null', 'result'=>'mysqli_result', 'mode='=>'1'],
'mysqli_fetch_array\'2' => ['list<null|int|float|string>|false|null', 'result'=>'mysqli_result', 'mode='=>'2'],
'mysqli_fetch_assoc' => ['array<string,null|int|float|string>|false|null', 'result'=>'mysqli_result'],
'mysqli_fetch_field' => ['object|false', 'result'=>'mysqli_result'],
'mysqli_fetch_field_direct' => ['object|false', 'result'=>'mysqli_result', 'index'=>'int'],
'mysqli_fetch_fields' => ['stdClass[]', 'result'=>'mysqli_result'],
'mysqli_fetch_field' => ['object{name:non-empty-string,orgname:string,table:string,orgtable:string,max_length:int,length:int,charsetnr:int,flags:int,type:int,decimals:int,db:string,def:string,catalog:string}|false', 'result'=>'mysqli_result'],
'mysqli_fetch_field_direct' => ['object{name:non-empty-string,orgname:string,table:string,orgtable:string,max_length:int,length:int,charsetnr:int,flags:int,type:int,decimals:int,db:string,def:string,catalog:string}|false', 'result'=>'mysqli_result', 'index'=>'int'],
'mysqli_fetch_fields' => ['list<object{name:non-empty-string,orgname:string,table:string,orgtable:string,max_length:int,length:int,charsetnr:int,flags:int,type:int,decimals:int,db:string,def:string,catalog:string}>', 'result'=>'mysqli_result'],
'mysqli_fetch_lengths' => ['array|false', 'result'=>'mysqli_result'],
'mysqli_fetch_object' => ['object|false|null', 'result'=>'mysqli_result', 'class='=>'string', 'constructor_args='=>'array'],
'mysqli_fetch_row' => ['list<null|int|float|string>|false|null', 'result'=>'mysqli_result'],
@@ -12832,10 +12803,10 @@ return [
'mysqli_num_rows' => ['int<0, max>|numeric-string', 'result'=>'mysqli_result'],
'mysqli_options' => ['bool', 'mysql'=>'mysqli', 'option'=>'int', 'value'=>'string|int'],
'mysqli_ping' => ['bool', 'mysql'=>'mysqli'],
'mysqli_poll' => ['int|false', 'read'=>'array', 'write'=>'array', 'error'=>'array', 'seconds'=>'int', 'microseconds='=>'int'],
'mysqli_poll' => ['int|false', '&w_read'=>'?array', '&w_error'=>'?array', '&w_reject'=>'array', 'seconds'=>'int', 'microseconds='=>'int'],
'mysqli_prepare' => ['mysqli_stmt|false', 'mysql'=>'mysqli', 'query'=>'string'],
'mysqli_query' => ['mysqli_result|bool', 'mysql'=>'mysqli', 'query'=>'string', 'result_mode='=>'int'],
'mysqli_real_connect' => ['bool', 'mysql='=>'mysqli', 'hostname='=>'string|null', 'username='=>'string|null', 'password='=>'string|null', 'database='=>'string|null', 'port='=>'int|null', 'socket='=>'string|null', 'flags='=>'int'],
'mysqli_real_connect' => ['bool', 'mysql'=>'mysqli', 'hostname='=>'?string', 'username='=>'?string', 'password='=>'?string', 'database='=>'?string', 'port='=>'?int', 'socket='=>'?string', 'flags='=>'int'],
'mysqli_real_escape_string' => ['string', 'mysql'=>'mysqli', 'string'=>'string'],
'mysqli_real_query' => ['bool', 'mysql'=>'mysqli', 'query'=>'string'],
'mysqli_reap_async_query' => ['mysqli_result|false', 'mysql'=>'mysqli'],
@@ -12852,9 +12823,9 @@ return [
'mysqli_result::fetch_array\'1' => ['array<string,null|int|float|string>|false|null', 'mode='=>'1'],
'mysqli_result::fetch_array\'2' => ['list<null|int|float|string>|false|null', 'mode='=>'2'],
'mysqli_result::fetch_assoc' => ['array<string,null|int|float|string>|false|null'],
'mysqli_result::fetch_field' => ['object|false'],
'mysqli_result::fetch_field_direct' => ['object|false', 'index'=>'int'],
'mysqli_result::fetch_fields' => ['stdClass[]'],
'mysqli_result::fetch_field' => ['object{name:non-empty-string,orgname:string,table:string,orgtable:string,max_length:int,length:int,charsetnr:int,flags:int,type:int,decimals:int,db:string,def:string,catalog:string}|false'],
'mysqli_result::fetch_field_direct' => ['object{name:non-empty-string,orgname:string,table:string,orgtable:string,max_length:int,length:int,charsetnr:int,flags:int,type:int,decimals:int,db:string,def:string,catalog:string}|false', 'index'=>'int'],
'mysqli_result::fetch_fields' => ['list<object{name:non-empty-string,orgname:string,table:string,orgtable:string,max_length:int,length:int,charsetnr:int,flags:int,type:int,decimals:int,db:string,def:string,catalog:string}>'],
'mysqli_result::fetch_object' => ['object|false|null', 'class='=>'string', 'constructor_args='=>'array'],
'mysqli_result::fetch_row' => ['list<null|int|float|string>|false|null'],
'mysqli_result::field_seek' => ['bool', 'index'=>'int'],
@@ -12876,10 +12847,10 @@ return [
'mysqli_sqlstate' => ['string', 'mysql'=>'mysqli'],
'mysqli_ssl_set' => ['true', 'mysql'=>'mysqli', 'key'=>'?string', 'certificate'=>'?string', 'ca_certificate'=>'?string', 'ca_path'=>'?string', 'cipher_algos'=>'?string'],
'mysqli_stat' => ['string|false', 'mysql'=>'mysqli'],
'mysqli_stmt::__construct' => ['void', 'mysql'=>'mysqli', 'query'=>'string'],
'mysqli_stmt::__construct' => ['void', 'mysql'=>'mysqli', 'query='=>'string'],
'mysqli_stmt::attr_get' => ['int', 'attribute'=>'int'],
'mysqli_stmt::attr_set' => ['bool', 'attribute'=>'int', 'value'=>'int'],
'mysqli_stmt::bind_param' => ['bool', 'types'=>'string', '&vars'=>'mixed', '&...args='=>'mixed'],
'mysqli_stmt::bind_param' => ['bool', 'types'=>'string', '&var'=>'mixed', '&...vars='=>'mixed'],
'mysqli_stmt::bind_result' => ['bool', '&w_var1'=>'', '&...w_vars='=>''],
'mysqli_stmt::close' => ['bool'],
'mysqli_stmt::data_seek' => ['void', 'offset'=>'int'],
@@ -12899,7 +12870,7 @@ return [
'mysqli_stmt_affected_rows' => ['int<-1, max>|numeric-string', 'statement'=>'mysqli_stmt'],
'mysqli_stmt_attr_get' => ['int', 'statement'=>'mysqli_stmt', 'attribute'=>'int'],
'mysqli_stmt_attr_set' => ['bool', 'statement'=>'mysqli_stmt', 'attribute'=>'int', 'value'=>'int'],
'mysqli_stmt_bind_param' => ['bool', 'statement'=>'mysqli_stmt', 'types'=>'string', '&vars'=>'mixed', '&...args='=>'mixed'],
'mysqli_stmt_bind_param' => ['bool', 'statement'=>'mysqli_stmt', 'types'=>'string', '&var'=>'mixed', '&...vars='=>'mixed'],
'mysqli_stmt_bind_result' => ['bool', 'statement'=>'mysqli_stmt', '&w_var1'=>'', '&...w_vars='=>''],
'mysqli_stmt_close' => ['true', 'statement'=>'mysqli_stmt'],
'mysqli_stmt_data_seek' => ['void', 'statement'=>'mysqli_stmt', 'offset'=>'int'],
@@ -13008,12 +12979,12 @@ return [
'nsapi_response_headers' => ['array'],
'nsapi_virtual' => ['bool', 'uri'=>'string'],
'nthmac' => ['string', 'clent'=>'string', 'data'=>'string'],
'number_format' => ['string', 'num'=>'float|int', 'decimals='=>'int'],
'number_format\'1' => ['string', 'num'=>'float|int', 'decimals'=>'int', 'decimal_separator'=>'?string', 'thousands_separator'=>'?string'],
'number_format' => ['string', 'num'=>'float', 'decimals='=>'int'],
'number_format\'1' => ['string', 'num'=>'float', 'decimals'=>'int', 'decimal_separator'=>'?string', 'thousands_separator'=>'?string'],
'numfmt_create' => ['NumberFormatter|null', 'locale'=>'string', 'style'=>'int', 'pattern='=>'string'],
'numfmt_format' => ['string|false', 'formatter'=>'NumberFormatter', 'num'=>'int|float', 'type='=>'int'],
'numfmt_format_currency' => ['string|false', 'formatter'=>'NumberFormatter', 'amount'=>'float', 'currency'=>'string'],
'numfmt_get_attribute' => ['int|false', 'formatter'=>'NumberFormatter', 'attribute'=>'int'],
'numfmt_get_attribute' => ['float|int|false', 'formatter'=>'NumberFormatter', 'attribute'=>'int'],
'numfmt_get_error_code' => ['int', 'formatter'=>'NumberFormatter'],
'numfmt_get_error_message' => ['string', 'formatter'=>'NumberFormatter'],
'numfmt_get_locale' => ['string', 'formatter'=>'NumberFormatter', 'type='=>'int'],
@@ -13022,7 +12993,7 @@ return [
'numfmt_get_text_attribute' => ['string|false', 'formatter'=>'NumberFormatter', 'attribute'=>'int'],
'numfmt_parse' => ['float|int|false', 'formatter'=>'NumberFormatter', 'string'=>'string', 'type='=>'int', '&rw_offset='=>'int'],
'numfmt_parse_currency' => ['float|false', 'formatter'=>'NumberFormatter', 'string'=>'string', '&w_currency'=>'string', '&rw_offset='=>'int'],
'numfmt_set_attribute' => ['bool', 'formatter'=>'NumberFormatter', 'attribute'=>'int', 'value'=>'int'],
'numfmt_set_attribute' => ['bool', 'formatter'=>'NumberFormatter', 'attribute'=>'int', 'value'=>'float|int'],
'numfmt_set_pattern' => ['bool', 'formatter'=>'NumberFormatter', 'pattern'=>'string'],
'numfmt_set_symbol' => ['bool', 'formatter'=>'NumberFormatter', 'symbol'=>'int', 'value'=>'string'],
'numfmt_set_text_attribute' => ['bool', 'formatter'=>'NumberFormatter', 'attribute'=>'int', 'value'=>'string'],
@@ -13163,9 +13134,9 @@ return [
'odbc_pconnect' => ['resource|false', 'dsn'=>'string', 'user'=>'string', 'password'=>'string', 'cursor_option='=>'int'],
'odbc_prepare' => ['resource|false', 'odbc'=>'resource', 'query'=>'string'],
'odbc_primarykeys' => ['resource|false', 'odbc'=>'resource', 'catalog'=>'?string', 'schema'=>'string', 'table'=>'string'],
'odbc_procedurecolumns' => ['resource|false', 'odbc'=>'resource', 'catalog'=>'string', 'schema'=>'string', 'procedure'=>'string', 'column'=>'string'],
'odbc_procedures' => ['resource|false', 'odbc'=>'resource', 'catalog'=>'string', 'schema'=>'string', 'procedure'=>'string'],
'odbc_result' => ['mixed|false', 'statement'=>'resource', 'field'=>'mixed'],
'odbc_procedurecolumns' => ['resource|false', 'odbc'=>'resource', 'catalog='=>'?string', 'schema='=>'?string', 'procedure='=>'?string', 'column='=>'?string'],
'odbc_procedures' => ['resource|false', 'odbc'=>'resource', 'catalog='=>'?string', 'schema='=>'?string', 'procedure='=>'?string'],
'odbc_result' => ['string|bool|null', 'statement'=>'resource', 'field'=>'string|int'],
'odbc_result_all' => ['int|false', 'statement'=>'resource', 'format='=>'string'],
'odbc_rollback' => ['bool', 'odbc'=>'resource'],
'odbc_setoption' => ['bool', 'odbc'=>'resource', 'which'=>'int', 'option'=>'int', 'value'=>'int'],
@@ -13229,7 +13200,7 @@ return [
'openssl_pkcs12_read' => ['bool', 'pkcs12'=>'string', '&w_certificates'=>'array', 'passphrase'=>'string'],
'openssl_pkcs7_decrypt' => ['bool', 'input_filename'=>'string', 'output_filename'=>'string', 'certificate'=>'string|resource', 'private_key='=>'string|resource|array'],
'openssl_pkcs7_encrypt' => ['bool', 'input_filename'=>'string', 'output_filename'=>'string', 'certificate'=>'string|resource|array', 'headers'=>'array', 'flags='=>'int', 'cipher_algo='=>'int'],
'openssl_pkcs7_read' => ['bool', 'input_filename'=>'string', '&w_certificates'=>'array'],
'openssl_pkcs7_read' => ['bool', 'data'=>'string', '&w_certificates'=>'array'],
'openssl_pkcs7_sign' => ['bool', 'input_filename'=>'string', 'output_filename'=>'string', 'certificate'=>'string|resource', 'private_key'=>'string|resource|array', 'headers'=>'array', 'flags='=>'int', 'untrusted_certificates_filename='=>'string'],
'openssl_pkcs7_verify' => ['bool|int', 'input_filename'=>'string', 'flags'=>'int', 'signers_certificates_filename='=>'string', 'ca_info='=>'array', 'untrusted_certificates_filename='=>'string', 'content='=>'string', 'output_filename='=>'string'],
'openssl_pkey_export' => ['bool', 'key'=>'resource', '&w_output'=>'string', 'passphrase='=>'string|null', 'options='=>'array'],
@@ -13758,7 +13729,7 @@ return [
'rand\'1' => ['int'],
'random_bytes' => ['non-empty-string', 'length'=>'positive-int'],
'random_int' => ['int', 'min'=>'int', 'max'=>'int'],
'range' => ['array', 'start'=>'mixed', 'end'=>'mixed', 'step='=>'int|float'],
'range' => ['non-empty-array', 'start'=>'string|int|float', 'end'=>'string|int|float', 'step='=>'int<1, max>|float'],
'rar_allow_broken_set' => ['bool', 'rarfile'=>'RarArchive', 'allow_broken'=>'bool'],
'rar_broken_is' => ['bool', 'rarfile'=>'rararchive'],
'rar_close' => ['bool', 'rarfile'=>'rararchive'],
@@ -13823,7 +13794,7 @@ return [
'rewind' => ['bool', 'stream'=>'resource'],
'rewinddir' => ['void', 'dir_handle='=>'resource'],
'rmdir' => ['bool', 'directory'=>'string', 'context='=>'resource'],
'round' => ['float', 'num'=>'float', 'precision='=>'int', 'mode='=>'0|positive-int'],
'round' => ['float', 'num'=>'float|int', 'precision='=>'int', 'mode='=>'0|positive-int'],
'rpm_close' => ['bool', 'rpmr'=>'resource'],
'rpm_get_tag' => ['mixed', 'rpmr'=>'resource', 'tagnum'=>'int'],
'rpm_is_valid' => ['bool', 'filename'=>'string'],
@@ -13907,16 +13878,16 @@ return [
'sem_remove' => ['bool', 'semaphore'=>'resource'],
'serialize' => ['string', 'value'=>'mixed'],
'session_abort' => ['bool'],
'session_cache_expire' => ['int', 'value='=>'int'],
'session_cache_limiter' => ['string', 'value='=>'string'],
'session_cache_expire' => ['int|false', 'value='=>'int'],
'session_cache_limiter' => ['string|false', 'value='=>'string'],
'session_commit' => ['bool'],
'session_decode' => ['bool', 'data'=>'string'],
'session_destroy' => ['bool'],
'session_encode' => ['string'],
'session_encode' => ['string|false'],
'session_get_cookie_params' => ['array'],
'session_id' => ['string|false', 'id='=>'string'],
'session_is_registered' => ['bool', 'name'=>'string'],
'session_module_name' => ['string', 'module='=>'string'],
'session_module_name' => ['string|false', 'module='=>'string'],
'session_name' => ['string|false', 'name='=>'string'],
'session_pgsql_add_error' => ['bool', 'error_level'=>'int', 'error_message='=>'string'],
'session_pgsql_get_error' => ['array', 'with_error_message='=>'bool'],
@@ -13928,7 +13899,7 @@ return [
'session_register' => ['bool', 'name'=>'mixed', '...args='=>'mixed'],
'session_register_shutdown' => ['void'],
'session_reset' => ['bool'],
'session_save_path' => ['string', 'path='=>'string'],
'session_save_path' => ['string|false', 'path='=>'string'],
'session_set_cookie_params' => ['bool', 'lifetime'=>'int', 'path='=>'string', 'domain='=>'string', 'secure='=>'bool', 'httponly='=>'bool'],
'session_set_save_handler' => ['bool', 'open'=>'callable(string,string):bool', 'close'=>'callable():bool', 'read'=>'callable(string):string', 'write'=>'callable(string,string):bool', 'destroy'=>'callable(string):bool', 'gc'=>'callable(string):bool', 'create_sid='=>'callable():string', 'validate_sid='=>'callable(string):bool', 'update_timestamp='=>'callable(string):bool'],
'session_set_save_handler\'1' => ['bool', 'open'=>'SessionHandlerInterface', 'close='=>'bool'],
@@ -14024,16 +13995,16 @@ return [
'sinh' => ['float', 'num'=>'float'],
'sizeof' => ['int<0, max>', 'value'=>'Countable|array|SimpleXMLElement', 'mode='=>'int'],
'sleep' => ['int|false', 'seconds'=>'0|positive-int'],
'snmp2_get' => ['string|false', 'hostname'=>'string', 'community'=>'string', 'object_id'=>'string', 'timeout='=>'int', 'retries='=>'int'],
'snmp2_getnext' => ['string|false', 'hostname'=>'string', 'community'=>'string', 'object_id'=>'string', 'timeout='=>'int', 'retries='=>'int'],
'snmp2_real_walk' => ['array|false', 'hostname'=>'string', 'community'=>'string', 'object_id'=>'string', 'timeout='=>'int', 'retries='=>'int'],
'snmp2_set' => ['bool', 'hostname'=>'string', 'community'=>'string', 'object_id'=>'string', 'type'=>'string', 'value'=>'string', 'timeout='=>'int', 'retries='=>'int'],
'snmp2_walk' => ['array|false', 'hostname'=>'string', 'community'=>'string', 'object_id'=>'string', 'timeout='=>'int', 'retries='=>'int'],
'snmp3_get' => ['string|false', 'hostname'=>'string', 'security_name'=>'string', 'security_level'=>'string', 'auth_protocol'=>'string', 'auth_passphrase'=>'string', 'privacy_protocol'=>'string', 'privacy_passphrase'=>'string', 'object_id'=>'string', 'timeout='=>'int', 'retries='=>'int'],
'snmp3_getnext' => ['string|false', 'hostname'=>'string', 'security_name'=>'string', 'security_level'=>'string', 'auth_protocol'=>'string', 'auth_passphrase'=>'string', 'privacy_protocol'=>'string', 'privacy_passphrase'=>'string', 'object_id'=>'string', 'timeout='=>'int', 'retries='=>'int'],
'snmp3_real_walk' => ['array|false', 'hostname'=>'string', 'security_name'=>'string', 'security_level'=>'string', 'auth_protocol'=>'string', 'auth_passphrase'=>'string', 'privacy_protocol'=>'string', 'privacy_passphrase'=>'string', 'object_id'=>'string', 'timeout='=>'int', 'retries='=>'int'],
'snmp3_set' => ['bool', 'hostname'=>'string', 'security_name'=>'string', 'security_level'=>'string', 'auth_protocol'=>'string', 'auth_passphrase'=>'string', 'privacy_protocol'=>'string', 'privacy_passphrase'=>'string', 'object_id'=>'string', 'type'=>'string', 'value'=>'string', 'timeout='=>'int', 'retries='=>'int'],
'snmp3_walk' => ['array|false', 'hostname'=>'string', 'security_name'=>'string', 'security_level'=>'string', 'auth_protocol'=>'string', 'auth_passphrase'=>'string', 'privacy_protocol'=>'string', 'privacy_passphrase'=>'string', 'object_id'=>'string', 'timeout='=>'int', 'retries='=>'int'],
'snmp2_get' => ['string|false', 'hostname'=>'string', 'community'=>'string', 'object_id'=>'array|string', 'timeout='=>'int', 'retries='=>'int'],
'snmp2_getnext' => ['string|false', 'hostname'=>'string', 'community'=>'string', 'object_id'=>'array|string', 'timeout='=>'int', 'retries='=>'int'],
'snmp2_real_walk' => ['array|false', 'hostname'=>'string', 'community'=>'string', 'object_id'=>'array|string', 'timeout='=>'int', 'retries='=>'int'],
'snmp2_set' => ['bool', 'hostname'=>'string', 'community'=>'string', 'object_id'=>'array|string', 'type'=>'array|string', 'value'=>'array|string', 'timeout='=>'int', 'retries='=>'int'],
'snmp2_walk' => ['array|false', 'hostname'=>'string', 'community'=>'string', 'object_id'=>'array|string', 'timeout='=>'int', 'retries='=>'int'],
'snmp3_get' => ['string|false', 'hostname'=>'string', 'security_name'=>'string', 'security_level'=>'string', 'auth_protocol'=>'string', 'auth_passphrase'=>'string', 'privacy_protocol'=>'string', 'privacy_passphrase'=>'string', 'object_id'=>'array|string', 'timeout='=>'int', 'retries='=>'int'],
'snmp3_getnext' => ['string|false', 'hostname'=>'string', 'security_name'=>'string', 'security_level'=>'string', 'auth_protocol'=>'string', 'auth_passphrase'=>'string', 'privacy_protocol'=>'string', 'privacy_passphrase'=>'string', 'object_id'=>'array|string', 'timeout='=>'int', 'retries='=>'int'],
'snmp3_real_walk' => ['array|false', 'hostname'=>'string', 'security_name'=>'string', 'security_level'=>'string', 'auth_protocol'=>'string', 'auth_passphrase'=>'string', 'privacy_protocol'=>'string', 'privacy_passphrase'=>'string', 'object_id'=>'array|string', 'timeout='=>'int', 'retries='=>'int'],
'snmp3_set' => ['bool', 'hostname'=>'string', 'security_name'=>'string', 'security_level'=>'string', 'auth_protocol'=>'string', 'auth_passphrase'=>'string', 'privacy_protocol'=>'string', 'privacy_passphrase'=>'string', 'object_id'=>'array|string', 'type'=>'array|string', 'value'=>'array|string', 'timeout='=>'int', 'retries='=>'int'],
'snmp3_walk' => ['array|false', 'hostname'=>'string', 'security_name'=>'string', 'security_level'=>'string', 'auth_protocol'=>'string', 'auth_passphrase'=>'string', 'privacy_protocol'=>'string', 'privacy_passphrase'=>'string', 'object_id'=>'array|string', 'timeout='=>'int', 'retries='=>'int'],
'snmp_get_quick_print' => ['bool'],
'snmp_get_valueretrieval' => ['int'],
'snmp_read_mib' => ['bool', 'filename'=>'string'],
@@ -14042,12 +14013,12 @@ return [
'snmp_set_oid_output_format' => ['true', 'format'=>'int'],
'snmp_set_quick_print' => ['bool', 'enable'=>'bool'],
'snmp_set_valueretrieval' => ['true', 'method'=>'int'],
'snmpget' => ['string|false', 'hostname'=>'string', 'community'=>'string', 'object_id'=>'string', 'timeout='=>'int', 'retries='=>'int'],
'snmpgetnext' => ['string|false', 'hostname'=>'string', 'community'=>'string', 'object_id'=>'string', 'timeout='=>'int', 'retries='=>'int'],
'snmprealwalk' => ['array|false', 'hostname'=>'string', 'community'=>'string', 'object_id'=>'string', 'timeout='=>'int', 'retries='=>'int'],
'snmpset' => ['bool', 'hostname'=>'string', 'community'=>'string', 'object_id'=>'string', 'type'=>'string|string[]', 'value'=>'string|string[]', 'timeout='=>'int', 'retries='=>'int'],
'snmpwalk' => ['array|false', 'hostname'=>'string', 'community'=>'string', 'object_id'=>'string', 'timeout='=>'int', 'retries='=>'int'],
'snmpwalkoid' => ['array|false', 'hostname'=>'string', 'community'=>'string', 'object_id'=>'string', 'timeout='=>'int', 'retries='=>'int'],
'snmpget' => ['string|false', 'hostname'=>'string', 'community'=>'string', 'object_id'=>'array|string', 'timeout='=>'int', 'retries='=>'int'],
'snmpgetnext' => ['string|false', 'hostname'=>'string', 'community'=>'string', 'object_id'=>'array|string', 'timeout='=>'int', 'retries='=>'int'],
'snmprealwalk' => ['array|false', 'hostname'=>'string', 'community'=>'string', 'object_id'=>'array|string', 'timeout='=>'int', 'retries='=>'int'],
'snmpset' => ['bool', 'hostname'=>'string', 'community'=>'string', 'object_id'=>'array|string', 'type'=>'string|string[]', 'value'=>'string|string[]', 'timeout='=>'int', 'retries='=>'int'],
'snmpwalk' => ['array|false', 'hostname'=>'string', 'community'=>'string', 'object_id'=>'array|string', 'timeout='=>'int', 'retries='=>'int'],
'snmpwalkoid' => ['array|false', 'hostname'=>'string', 'community'=>'string', 'object_id'=>'array|string', 'timeout='=>'int', 'retries='=>'int'],
'socket_accept' => ['resource|false', 'socket'=>'resource'],
'socket_bind' => ['bool', 'socket'=>'resource', 'address'=>'string', 'port='=>'int'],
'socket_clear_error' => ['void', 'socket='=>'resource'],
@@ -14144,7 +14115,7 @@ return [
'sqlsrv_commit' => ['bool', 'conn'=>'resource'],
'sqlsrv_configure' => ['bool', 'setting'=>'string', 'value'=>'mixed'],
'sqlsrv_connect' => ['resource|false', 'serverName'=>'string', 'connectionInfo='=>'array'],
'sqlsrv_errors' => ['?array', 'errorsOrWarnings='=>'int'],
'sqlsrv_errors' => ['?array', 'errorsAndOrWarnings='=>'int'],
'sqlsrv_execute' => ['bool', 'stmt'=>'resource'],
'sqlsrv_fetch' => ['?bool', 'stmt'=>'resource', 'row='=>'int', 'offset='=>'int'],
'sqlsrv_fetch_array' => ['array|null|false', 'stmt'=>'resource', 'fetchType='=>'int', 'row='=>'int', 'offset='=>'int'],
@@ -14340,7 +14311,7 @@ return [
'stream_context_create' => ['resource', 'options='=>'array', 'params='=>'array'],
'stream_context_get_default' => ['resource', 'options='=>'array'],
'stream_context_get_options' => ['array', 'stream_or_context'=>'resource'],
'stream_context_get_params' => ['array', 'context'=>'resource'],
'stream_context_get_params' => ['array{notification:string,options:array}', 'context'=>'resource'],
'stream_context_set_default' => ['resource', 'options'=>'array'],
'stream_context_set_option' => ['bool', 'context'=>'', 'wrapper_or_options'=>'string', 'option_name'=>'string', 'value'=>''],
'stream_context_set_option\'1' => ['bool', 'context'=>'', 'wrapper_or_options'=>'array'],
@@ -14370,10 +14341,10 @@ return [
'stream_socket_accept' => ['resource|false', 'socket'=>'resource', 'timeout='=>'float', '&w_peer_name='=>'string'],
'stream_socket_client' => ['resource|false', 'address'=>'string', '&w_error_code='=>'int', '&w_error_message='=>'string', 'timeout='=>'float', 'flags='=>'int', 'context='=>'resource'],
'stream_socket_enable_crypto' => ['int|bool', 'stream'=>'resource', 'enable'=>'bool', 'crypto_method='=>'?int', 'session_stream='=>'resource'],
'stream_socket_get_name' => ['string', 'socket'=>'resource', 'remote'=>'bool'],
'stream_socket_get_name' => ['string|false', 'socket'=>'resource', 'remote'=>'bool'],
'stream_socket_pair' => ['resource[]|false', 'domain'=>'int', 'type'=>'int', 'protocol'=>'int'],
'stream_socket_recvfrom' => ['string', 'socket'=>'resource', 'length'=>'int', 'flags='=>'int', '&w_address='=>'string'],
'stream_socket_sendto' => ['int', 'socket'=>'resource', 'data'=>'string', 'flags='=>'int', 'address='=>'string'],
'stream_socket_recvfrom' => ['string|false', 'socket'=>'resource', 'length'=>'int', 'flags='=>'int', '&w_address='=>'string'],
'stream_socket_sendto' => ['int|false', 'socket'=>'resource', 'data'=>'string', 'flags='=>'int', 'address='=>'string'],
'stream_socket_server' => ['resource|false', 'address'=>'string', '&w_error_code='=>'int', '&w_error_message='=>'string', 'flags='=>'int', 'context='=>'resource'],
'stream_socket_shutdown' => ['bool', 'stream'=>'resource', 'mode'=>'int'],
'stream_supports_lock' => ['bool', 'stream'=>'resource'],
@@ -14808,7 +14779,7 @@ return [
'symbolObj::setPoints' => ['int', 'double'=>'array'],
'symlink' => ['bool', 'target'=>'string', 'link'=>'string'],
'sys_get_temp_dir' => ['string'],
'sys_getloadavg' => ['array'],
'sys_getloadavg' => ['array|false'],
'syslog' => ['true', 'priority'=>'int', 'message'=>'string'],
'system' => ['string|false', 'command'=>'string', '&w_result_code='=>'int'],
'taint' => ['bool', '&rw_string'=>'string', '&...w_other_strings='=>'string'],
@@ -15259,7 +15230,7 @@ return [
'uksort' => ['true', '&rw_array'=>'array', 'callback'=>'callable(mixed,mixed):int'],
'umask' => ['int', 'mask='=>'int'],
'uniqid' => ['non-empty-string', 'prefix='=>'string', 'more_entropy='=>'bool'],
'unixtojd' => ['int', 'timestamp='=>'int'],
'unixtojd' => ['int|false', 'timestamp='=>'int'],
'unlink' => ['bool', 'filename'=>'string', 'context='=>'resource'],
'unpack' => ['array', 'format'=>'string', 'string'=>'string'],
'unregister_tick_function' => ['void', 'callback'=>'callable'],
@@ -15534,7 +15505,7 @@ return [
'xml_parser_create' => ['resource', 'encoding='=>'string'],
'xml_parser_create_ns' => ['resource', 'encoding='=>'string', 'separator='=>'string'],
'xml_parser_free' => ['bool', 'parser'=>'resource'],
'xml_parser_get_option' => ['string|false', 'parser'=>'resource', 'option'=>'int'],
'xml_parser_get_option' => ['string|int', 'parser'=>'resource', 'option'=>'int'],
'xml_parser_set_option' => ['bool', 'parser'=>'resource', 'option'=>'int', 'value'=>'mixed'],
'xml_set_character_data_handler' => ['true', 'parser'=>'resource', 'handler'=>'callable'],
'xml_set_default_handler' => ['true', 'parser'=>'resource', 'handler'=>'callable'],
@@ -15689,7 +15660,7 @@ return [
'zip_entry_compressedsize' => ['int', 'zip_entry'=>'resource'],
'zip_entry_compressionmethod' => ['string', 'zip_entry'=>'resource'],
'zip_entry_filesize' => ['int', 'zip_entry'=>'resource'],
'zip_entry_name' => ['string', 'zip_entry'=>'resource'],
'zip_entry_name' => ['string|false', 'zip_entry'=>'resource'],
'zip_entry_open' => ['bool', 'zip_dp'=>'resource', 'zip_entry'=>'resource', 'mode='=>'string'],
'zip_entry_read' => ['string|false', 'zip_entry'=>'resource', 'len='=>'int'],
'zip_open' => ['resource|int|false', 'filename'=>'string'],

View File

@@ -7,23 +7,25 @@ Psalm supports a wide range of docblock annotations.
Psalm uses the following PHPDoc tags to understand your code:
- [`@var`](https://docs.phpdoc.org/latest/guide/references/phpdoc/tags/var.html)
Used for specifying the types of properties and variables@
Used for specifying the types of properties and variables
- [`@return`](https://docs.phpdoc.org/latest/guide/references/phpdoc/tags/return.html)
Used for specifying the return types of functions, methods and closures
- [`@param`](https://docs.phpdoc.org/latest/guide/references/phpdoc/tags/param.html)
Used for specifying types of parameters passed to functions, methods and closures
- [`@property`](https://docs.phpdoc.org/latest/guide/references/phpdoc/tags/property.html)
Used to specify what properties can be accessed on an object that uses `__get` and `__set`
- [`@property-read`](https://docs.phpdoc.org/latest/guide/references/phpdoc/tags/property-read.html)
- [`@property-read`](https://docs.phpdoc.org/latest/guide/references/phpdoc/tags/property.html)
Used to specify what properties can be read on object that uses `__get`
- [`@property-write`](https://docs.phpdoc.org/latest/guide/references/phpdoc/tags/property-write.html)
- [`@property-write`](https://docs.phpdoc.org/latest/guide/references/phpdoc/tags/property.html)
Used to specify what properties can be written on object that uses `__set`
- [`@method`](https://docs.phpdoc.org/latest/guide/references/phpdoc/tags/method.html)
Used to specify which magic methods are available on object that uses `__call`.
- [`@deprecated`](https://docs.phpdoc.org/latest/guide/references/phpdoc/tags/deprecated.html)
Used to mark functions, methods, classes and interfaces as being deprecated
- [`@internal`](https://docs.phpdoc.org/latest/guide/references/phpdoc/tags/internal.html)
used to mark classes, functions and properties that are internal to an application or library.
Used to mark classes, functions and properties that are internal to an application or library.
- [`@mixin`](#mixins)
Used to tell Psalm that the current class proxies the methods and properties of the referenced class.
### Off-label usage of the `@var` tag
@@ -46,6 +48,49 @@ function bat(): string {
return $_GET['bat'];
}
```
### @mixins
Adding `@mixin` to a classes docblock tells Psalm that the class proxies will proxy the methods and properties of the referenced class.
```php
class A
{
public string $a = 'A';
public function doA(): void
{
}
}
/**
* @mixin A
*/
class B
{
public string $b = 'B';
public function doB(): void
{
}
public function __call($name, $arguments)
{
(new A())->$name(...$arguments);
}
public function __get($name)
{
(new A())->$name;
}
}
$b = new B();
$b->doB();
$b->doA(); // works
echo $b->b;
echo $b->a; // works
```
## Psalm-specific tags
@@ -157,9 +202,10 @@ takesFoo(getFoo());
This provides the same, but for `false`. Psalm uses this internally for functions like `preg_replace`, which can return false if the given input has encoding errors, but where 99.9% of the time the function operates as expected.
### `@psalm-seal-properties`
### `@psalm-seal-properties`, `@psalm-no-seal-properties`
If you have a magic property getter/setter, you can use `@psalm-seal-properties` to instruct Psalm to disallow getting and setting any properties not contained in a list of `@property` (or `@property-read`/`@property-write`) annotations.
This is automatically enabled with the configuration option `sealAllProperties` and can be disabled for a class with `@psalm-no-seal-properties`
```php
<?php
@@ -181,6 +227,29 @@ $a = new A();
$a->bar = 5; // this call fails
```
### `@psalm-seal-methods`, `@psalm-no-seal-methods`
If you have a magic method caller, you can use `@psalm-seal-methods` to instruct Psalm to disallow calling any methods not contained in a list of `@method` annotations.
This is automatically enabled with the configuration option `sealAllMethods` and can be disabled for a class with `@psalm-no-seal-methods`
```php
<?php
/**
* @method foo(): string
* @psalm-seal-methods
*/
class A {
public function __call(string $name, array $args) {
if ($name === "foo") {
return "hello";
}
}
}
$a = new A();
$b = $a->bar(); // this call fails
```
### `@psalm-internal`
Used to mark a class, property or function as internal to a given namespace. Psalm treats this slightly differently to
@@ -446,7 +515,18 @@ $username = $_GET['username']; // prints something like "test.php:4 $username: m
```
*Note*: it throws [special low-level issue](../running_psalm/issues/Trace.md), so you have to set errorLevel to 1, override it in config or invoke Psalm with `--show-info=true`.
*Note*: it throws [special low-level issue](../running_psalm/issues/Trace.md).
To see it, you can set the global `errorLevel` to 1, or invoke Psalm with
`--show-info=true`, but both these solutions will probably result in a lot of
output. Another solution is to selectively bump the error level of the issue,
so that you only get one more error:
```xml
<!-- psalm.xml -->
<issueHandlers>
<Trace errorLevel="error"/>
</issueHandlers>
```
### `@psalm-check-type`
@@ -596,7 +676,7 @@ class Foo
}
```
When Psalm encounters variable property, it treats all properties in given class as potentially referenced.
With `@psalm-ignore-variable-property` annotation, this reference is ignored.
With `@psalm-ignore-variable-property` annotation, this reference is ignored.
While `PossiblyUnusedProperty` would be emitted in both cases, using `@psalm-ignore-variable-property`
would allow [Psalter](../manipulating_code/fixing.md) to delete `Foo::$bar`.
@@ -642,9 +722,9 @@ function (): Generator {
```
This annotation supports only generic types, meaning that e.g. `@psalm-yield string` would be ignored.
### `@psalm-api`
### `@api`, `@psalm-api`
Used to tell Psalm that a class is used, even if no references to it can be
Used to tell Psalm that a class or method is used, even if no references to it can be
found. Unused issues will be suppressed.
For example, in frameworks, controllers are often invoked "magically" without
@@ -657,6 +737,22 @@ any explicit references to them in your code. You should mark these classes with
class UnreferencedClass {}
```
### `@psalm-inheritors`
Used to tell Psalm that a class can only be extended by a certain subset of classes.
For example,
```php
<?php
/**
* @psalm-inheritors FooClass|BarClass
*/
class BaseClass {}
class FooClass extends BaseClass {}
class BarClass extends BaseClass {}
class BazClass extends BaseClass {} // this is an error
```
## Type Syntax
Psalm supports PHPDocs [type syntax](https://docs.phpdoc.org/latest/guide/guides/types.html), and also the [proposed PHPDoc PSR type syntax](https://github.com/php-fig/fig-standards/blob/master/proposed/phpdoc.md#appendix-a-types).

View File

@@ -203,6 +203,17 @@ $options['verbose'] = isset($options['verbose']);
handleOptions($options);
```
`...` is a shorthand for `...<array-key, mixed>` you can use other array generic types to provide more information about the open shape.
```php
// This is an open array
/** @param array{someKey: string, ...} */
// Which is the same as
/** @param array{someKey: string, ...<array-key, mixed>} */
// But it can be further locked down with a shape ...<TKey, TValue>
/** @return array{someKey: string, ...<int, bool>} */
```
## Callable arrays
An array holding a callable, like PHP's native `call_user_func()` and friends supports it:

View File

@@ -46,7 +46,7 @@ Atomic types are the basic building block of all type information used in Psalm.
* [`key-of<T>`](utility_types.md#key-oft)
* [`value-of<T>`](utility_types.md#value-oft)
* [`properties-of<T>`](utility_types.md#properties-oft)
* [`class-string-map<T as Foo, T>`](utility_types.md#class-string-mapt-as-foo-t)
* [`class-string-map<T of Foo, T>`](utility_types.md#class-string-mapt-as-foo-t)
* [`T[K]`](utility_types.md#tk)
* [Type aliases](utility_types.md#type-aliases)
* [Variable templates](utility_types.md#variable-templates)

View File

@@ -148,7 +148,7 @@ $b = asArray(new B);
/** @psalm-trace $b */; // array{foo: string, bar: int, baz: float}
```
## class-string-map&lt;T as Foo, T&gt;
## class-string-map&lt;T of Foo, T&gt;
Used to indicate an array where each value is equal an instance of the class string contained in the key:
@@ -166,11 +166,11 @@ class Foo {}
class Bar extends Foo {}
class A {
/** @var class-string-map<T as Foo, T> */
/** @var class-string-map<T of Foo, T> */
private static array $map = [];
/**
* @template U as Foo
* @template U of Foo
* @param class-string<U> $class
* @return U
*/
@@ -191,7 +191,7 @@ $bar = A::get(Bar::class);
/** @psalm-trace $bar */; // Bar
```
If we had used an `array<class-string<Foo>, Foo>` instead of a `class-string-map<T as Foo, T>` in the above example, we would've gotten some false positive `InvalidReturnStatement` issues, caused by the lack of a type assertion inside the `isset`.
If we had used an `array<class-string<Foo>, Foo>` instead of a `class-string-map<T of Foo, T>` in the above example, we would've gotten some false positive `InvalidReturnStatement` issues, caused by the lack of a type assertion inside the `isset`.
On the other hand, when using `class-string-map`, Psalm assumes that the value obtained by using a key `class-string<T>` is always equal to `T`.
Unbounded templates can also be used for unrelated classes:
@@ -250,8 +250,8 @@ Used to get the value corresponding to the specified key:
<?php
/**
* @template T as array
* @template TKey as string
* @template T of array
* @template TKey of string
* @param T $arr
* @param TKey $k
* @return T[TKey]
@@ -325,9 +325,9 @@ Variable templates allow directly using variables instead of template types, for
<?php
/**
* @template TA as string
* @template TB as string
* @template TChoose as bool
* @template TA of string
* @template TB of string
* @template TChoose of bool
* @param TA $a
* @param TB $b
* @param TChoose $choose

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