Composer Workspace global packages
This commit is contained in:
6
vendor/vimeo/psalm/composer.json
vendored
6
vendor/vimeo/psalm/composer.json
vendored
@@ -15,7 +15,7 @@
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": "^7.4 || ~8.0.0 || ~8.1.0 || ~8.2.0",
|
||||
"php": "^7.4 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0",
|
||||
"ext-SimpleXML": "*",
|
||||
"ext-ctype": "*",
|
||||
"ext-dom": "*",
|
||||
@@ -23,9 +23,9 @@
|
||||
"ext-libxml": "*",
|
||||
"ext-mbstring": "*",
|
||||
"ext-tokenizer": "*",
|
||||
"composer-runtime-api": "^2",
|
||||
"amphp/amp": "^2.4.2",
|
||||
"amphp/byte-stream": "^1.5",
|
||||
"composer-runtime-api": "^2",
|
||||
"composer/semver": "^1.4 || ^2.0 || ^3.0",
|
||||
"composer/xdebug-handler": "^2.0 || ^3.0",
|
||||
"dnoegel/php-xdg-base-dir": "^0.1.1",
|
||||
@@ -33,7 +33,7 @@
|
||||
"felixfbecker/language-server-protocol": "^1.5.2",
|
||||
"fidry/cpu-core-counter": "^0.4.1 || ^0.5.1",
|
||||
"netresearch/jsonmapper": "^1.0 || ^2.0 || ^3.0 || ^4.0",
|
||||
"nikic/php-parser": "^4.14",
|
||||
"nikic/php-parser": "^4.16",
|
||||
"sebastian/diff": "^4.0 || ^5.0",
|
||||
"spatie/array-to-xml": "^2.17.0 || ^3.0",
|
||||
"symfony/console": "^4.1.6 || ^5.0 || ^6.0",
|
||||
|
||||
1
vendor/vimeo/psalm/config.xsd
vendored
1
vendor/vimeo/psalm/config.xsd
vendored
@@ -33,6 +33,7 @@
|
||||
<xs:attribute name="name" type="xs:string" />
|
||||
<xs:attribute name="phpVersion" type="xs:string" />
|
||||
<xs:attribute name="serializer" type="xs:string" />
|
||||
<xs:attribute name="compressor" type="xs:string" />
|
||||
|
||||
<xs:attribute name="addParamDefaultToDocblockType" type="xs:boolean" default="false" />
|
||||
<xs:attribute name="allowFileIncludes" type="xs:boolean" default="true" />
|
||||
|
||||
223
vendor/vimeo/psalm/dictionaries/CallMap.php
vendored
223
vendor/vimeo/psalm/dictionaries/CallMap.php
vendored
@@ -337,7 +337,6 @@ return [
|
||||
'AppendIterator::next' => ['void'],
|
||||
'AppendIterator::rewind' => ['void'],
|
||||
'AppendIterator::valid' => ['bool'],
|
||||
'ArgumentCountError::__clone' => ['void'],
|
||||
'ArgumentCountError::__construct' => ['void', 'message='=>'string', 'code='=>'int', 'previous='=>'?Throwable'],
|
||||
'ArgumentCountError::__toString' => ['string'],
|
||||
'ArgumentCountError::__wakeup' => ['void'],
|
||||
@@ -348,7 +347,6 @@ return [
|
||||
'ArgumentCountError::getPrevious' => ['?Throwable'],
|
||||
'ArgumentCountError::getTrace' => ['list<array{file?:string,line?:int,function:string,class?:class-string,type?:\'::\'|\'->\',args?:array<mixed>}>'],
|
||||
'ArgumentCountError::getTraceAsString' => ['string'],
|
||||
'ArithmeticError::__clone' => ['void'],
|
||||
'ArithmeticError::__construct' => ['void', 'message='=>'string', 'code='=>'int', 'previous='=>'?Throwable'],
|
||||
'ArithmeticError::__toString' => ['string'],
|
||||
'ArithmeticError::__wakeup' => ['void'],
|
||||
@@ -425,10 +423,10 @@ return [
|
||||
'array_walk\'1' => ['bool', '&rw_array'=>'object', 'callback'=>'callable', 'arg='=>'mixed'],
|
||||
'array_walk_recursive' => ['bool', '&rw_array'=>'array', 'callback'=>'callable', 'arg='=>'mixed'],
|
||||
'array_walk_recursive\'1' => ['bool', '&rw_array'=>'object', 'callback'=>'callable', 'arg='=>'mixed'],
|
||||
'ArrayAccess::offsetExists' => ['bool', 'offset'=>'mixed'],
|
||||
'ArrayAccess::offsetGet' => ['mixed', 'offset'=>'mixed'],
|
||||
'ArrayAccess::offsetSet' => ['void', 'offset'=>'mixed', 'value'=>'mixed'],
|
||||
'ArrayAccess::offsetUnset' => ['void', 'offset'=>'mixed'],
|
||||
'ArrayAccess::offsetExists' => ['bool', 'offset'=>'int|string'],
|
||||
'ArrayAccess::offsetGet' => ['mixed', 'offset'=>'int|string'],
|
||||
'ArrayAccess::offsetSet' => ['void', 'offset'=>'int|string|null', 'value'=>'mixed'],
|
||||
'ArrayAccess::offsetUnset' => ['void', 'offset'=>'int|string'],
|
||||
'ArrayIterator::__construct' => ['void', 'array='=>'array|object', 'flags='=>'int'],
|
||||
'ArrayIterator::append' => ['void', 'value'=>'mixed'],
|
||||
'ArrayIterator::asort' => ['true', 'flags='=>'int'],
|
||||
@@ -443,7 +441,7 @@ return [
|
||||
'ArrayIterator::next' => ['void'],
|
||||
'ArrayIterator::offsetExists' => ['bool', 'key'=>'string|int'],
|
||||
'ArrayIterator::offsetGet' => ['mixed', 'key'=>'string|int'],
|
||||
'ArrayIterator::offsetSet' => ['void', 'key'=>'string|int', 'value'=>'mixed'],
|
||||
'ArrayIterator::offsetSet' => ['void', 'key'=>'string|int|null', 'value'=>'mixed'],
|
||||
'ArrayIterator::offsetUnset' => ['void', 'key'=>'string|int'],
|
||||
'ArrayIterator::rewind' => ['void'],
|
||||
'ArrayIterator::seek' => ['void', 'offset'=>'int'],
|
||||
@@ -467,7 +465,7 @@ return [
|
||||
'ArrayObject::natsort' => ['true'],
|
||||
'ArrayObject::offsetExists' => ['bool', 'key'=>'int|string'],
|
||||
'ArrayObject::offsetGet' => ['mixed|null', 'key'=>'int|string'],
|
||||
'ArrayObject::offsetSet' => ['void', 'key'=>'int|string', 'value'=>'mixed'],
|
||||
'ArrayObject::offsetSet' => ['void', 'key'=>'int|string|null', 'value'=>'mixed'],
|
||||
'ArrayObject::offsetUnset' => ['void', 'key'=>'int|string'],
|
||||
'ArrayObject::serialize' => ['string'],
|
||||
'ArrayObject::setFlags' => ['void', 'flags'=>'int'],
|
||||
@@ -491,7 +489,6 @@ return [
|
||||
'atan' => ['float', 'num'=>'float'],
|
||||
'atan2' => ['float', 'y'=>'float', 'x'=>'float'],
|
||||
'atanh' => ['float', 'num'=>'float'],
|
||||
'BadFunctionCallException::__clone' => ['void'],
|
||||
'BadFunctionCallException::__construct' => ['void', 'message='=>'string', 'code='=>'int', 'previous='=>'?Throwable'],
|
||||
'BadFunctionCallException::__toString' => ['string'],
|
||||
'BadFunctionCallException::getCode' => ['int'],
|
||||
@@ -501,7 +498,6 @@ return [
|
||||
'BadFunctionCallException::getPrevious' => ['?Throwable'],
|
||||
'BadFunctionCallException::getTrace' => ['list<array{file?:string,line?:int,function:string,class?:class-string,type?:\'::\'|\'->\',args?:array<mixed>}>'],
|
||||
'BadFunctionCallException::getTraceAsString' => ['string'],
|
||||
'BadMethodCallException::__clone' => ['void'],
|
||||
'BadMethodCallException::__construct' => ['void', 'message='=>'string', 'code='=>'int', 'previous='=>'?Throwable'],
|
||||
'BadMethodCallException::__toString' => ['string'],
|
||||
'BadMethodCallException::getCode' => ['int'],
|
||||
@@ -511,7 +507,8 @@ return [
|
||||
'BadMethodCallException::getPrevious' => ['?Throwable'],
|
||||
'BadMethodCallException::getTrace' => ['list<array{file?:string,line?:int,function:string,class?:class-string,type?:\'::\'|\'->\',args?:array<mixed>}>'],
|
||||
'BadMethodCallException::getTraceAsString' => ['string'],
|
||||
'base64_decode' => ['string|false', 'string'=>'string', 'strict='=>'bool'],
|
||||
'base64_decode' => ['string', 'string'=>'string', 'strict='=>'false'],
|
||||
'base64_decode\'1' => ['string|false', 'string'=>'string', 'strict='=>'true'],
|
||||
'base64_encode' => ['string', 'string'=>'string'],
|
||||
'base_convert' => ['string', 'num'=>'string', 'from_base'=>'int', 'to_base'=>'int'],
|
||||
'basename' => ['string', 'path'=>'string', 'suffix='=>'string'],
|
||||
@@ -660,7 +657,6 @@ return [
|
||||
'clearstatcache' => ['void', 'clear_realpath_cache='=>'bool', 'filename='=>'string'],
|
||||
'cli_get_process_title' => ['?string'],
|
||||
'cli_set_process_title' => ['bool', 'title'=>'string'],
|
||||
'ClosedGeneratorException::__clone' => ['void'],
|
||||
'ClosedGeneratorException::__toString' => ['string'],
|
||||
'ClosedGeneratorException::getCode' => ['int'],
|
||||
'ClosedGeneratorException::getFile' => ['string'],
|
||||
@@ -1351,7 +1347,7 @@ return [
|
||||
'datefmt_set_calendar' => ['bool', 'formatter'=>'IntlDateFormatter', 'calendar'=>'IntlCalendar|int|null'],
|
||||
'datefmt_set_lenient' => ['void', 'formatter'=>'IntlDateFormatter', 'lenient'=>'bool'],
|
||||
'datefmt_set_pattern' => ['bool', 'formatter'=>'IntlDateFormatter', 'pattern'=>'string'],
|
||||
'datefmt_set_timezone' => ['false|null', 'formatter'=>'IntlDateFormatter', 'timezone'=>'IntlTimeZone|DateTimeZone|string|null'],
|
||||
'datefmt_set_timezone' => ['bool', 'formatter'=>'IntlDateFormatter', 'timezone'=>'IntlTimeZone|DateTimeZone|string|null'],
|
||||
'DateInterval::__construct' => ['void', 'duration'=>'string'],
|
||||
'DateInterval::__set_state' => ['DateInterval', 'array'=>'array'],
|
||||
'DateInterval::__wakeup' => ['void'],
|
||||
@@ -1545,7 +1541,7 @@ return [
|
||||
'dcgettext' => ['string', 'domain'=>'string', 'message'=>'string', 'category'=>'int'],
|
||||
'dcngettext' => ['string', 'domain'=>'string', 'singular'=>'string', 'plural'=>'string', 'count'=>'int', 'category'=>'int'],
|
||||
'deaggregate' => ['', 'object'=>'object', 'class_name='=>'string'],
|
||||
'debug_backtrace' => ['list<array{file:string,line:int,function:string,class?:class-string,object?:object,type?:string,args?:list}>', 'options='=>'int', 'limit='=>'int'],
|
||||
'debug_backtrace' => ['list<array{file?:string,line?:int,function:string,class?:class-string,object?:object,type?:string,args?:list}>', 'options='=>'int', 'limit='=>'int'],
|
||||
'debug_print_backtrace' => ['void', 'options='=>'int', 'limit='=>'int'],
|
||||
'debug_zval_dump' => ['void', 'value'=>'mixed', '...values='=>'mixed'],
|
||||
'debugger_connect' => [''],
|
||||
@@ -1612,7 +1608,7 @@ return [
|
||||
'DirectoryIterator::setFileClass' => ['void', 'class='=>'class-string'],
|
||||
'DirectoryIterator::setInfoClass' => ['void', 'class='=>'class-string'],
|
||||
'DirectoryIterator::valid' => ['bool'],
|
||||
'dirname' => ['string', 'path'=>'string', 'levels='=>'int'],
|
||||
'dirname' => ['string', 'path'=>'string', 'levels='=>'int<1, max>'],
|
||||
'disk_free_space' => ['float|false', 'directory'=>'string'],
|
||||
'disk_total_space' => ['float|false', 'directory'=>'string'],
|
||||
'diskfreespace' => ['float|false', 'directory'=>'string'],
|
||||
@@ -1632,7 +1628,6 @@ return [
|
||||
'dom_xpath_query' => ['DOMNodeList', 'expr'=>'string', 'context'=>'DOMNode', 'registernodens'=>'bool'],
|
||||
'dom_xpath_register_ns' => ['bool', 'prefix'=>'string', 'uri'=>'string'],
|
||||
'dom_xpath_register_php_functions' => [''],
|
||||
'DomainException::__clone' => ['void'],
|
||||
'DomainException::__construct' => ['void', 'message='=>'string', 'code='=>'int', 'previous='=>'?Throwable'],
|
||||
'DomainException::__toString' => ['string'],
|
||||
'DomainException::__wakeup' => ['void'],
|
||||
@@ -2092,7 +2087,6 @@ return [
|
||||
'error_get_last' => ['?array{type:int,message:string,file:string,line:int}'],
|
||||
'error_log' => ['bool', 'message'=>'string', 'message_type='=>'int', 'destination='=>'?string', 'additional_headers='=>'?string'],
|
||||
'error_reporting' => ['int', 'error_level='=>'?int'],
|
||||
'ErrorException::__clone' => ['void'],
|
||||
'ErrorException::__construct' => ['void', 'message='=>'string', 'code='=>'int', 'severity='=>'int', 'filename='=>'?string', 'line='=>'?int', 'previous='=>'?Throwable'],
|
||||
'ErrorException::__toString' => ['string'],
|
||||
'ErrorException::getCode' => ['int'],
|
||||
@@ -3310,8 +3304,8 @@ return [
|
||||
'gethostbyname' => ['string', 'hostname'=>'string'],
|
||||
'gethostbynamel' => ['list<string>|false', 'hostname'=>'string'],
|
||||
'gethostname' => ['string|false'],
|
||||
'getimagesize' => ['array|false', 'filename'=>'string', '&w_image_info='=>'array'],
|
||||
'getimagesizefromstring' => ['array|false', 'string'=>'string', '&w_image_info='=>'array'],
|
||||
'getimagesize' => ['array{0:int, 1: int, 2: int, 3: string, mime: string, channels?: 3|4, bits?: int}|false', 'filename'=>'string', '&w_image_info='=>'array'],
|
||||
'getimagesizefromstring' => ['array{0:int, 1: int, 2: int, 3: string, mime: string, channels?: 3|4, bits?: int}|false', 'string'=>'string', '&w_image_info='=>'array'],
|
||||
'getlastmod' => ['int|false'],
|
||||
'getmxrr' => ['bool', 'hostname'=>'string', '&w_hosts'=>'array', '&w_weights='=>'array'],
|
||||
'getmygid' => ['int|false'],
|
||||
@@ -3329,7 +3323,7 @@ return [
|
||||
'gettimeofday' => ['array<string, int>'],
|
||||
'gettimeofday\'1' => ['float', 'as_float='=>'true'],
|
||||
'gettype' => ['string', 'value'=>'mixed'],
|
||||
'glob' => ['list<non-empty-string>|false', 'pattern'=>'non-empty-string', 'flags='=>'int<1, max>'],
|
||||
'glob' => ['false|list{0?:string, ...<non-empty-string>}', 'pattern'=>'string', 'flags='=>'int<0, max>'],
|
||||
'GlobIterator::__construct' => ['void', 'pattern'=>'string', 'flags='=>'int'],
|
||||
'GlobIterator::count' => ['int'],
|
||||
'GlobIterator::current' => ['FilesystemIterator|SplFileInfo|string'],
|
||||
@@ -3924,7 +3918,7 @@ return [
|
||||
'hash_hmac_algos' => ['list<string>'],
|
||||
'hash_hmac_file' => ['non-empty-string', 'algo'=>'string', 'filename'=>'string', 'key'=>'string', 'binary='=>'bool'],
|
||||
'hash_init' => ['HashContext', 'algo'=>'string', 'flags='=>'int', 'key='=>'string', 'options='=>'array{seed:scalar}'],
|
||||
'hash_pbkdf2' => ['non-empty-string', 'algo'=>'string', 'password'=>'string', 'salt'=>'string', 'iterations'=>'int', 'length='=>'int', 'binary='=>'bool'],
|
||||
'hash_pbkdf2' => ['non-empty-string', 'algo'=>'string', 'password'=>'string', 'salt'=>'string', 'iterations'=>'int', 'length='=>'int', 'binary='=>'bool', 'options=' => 'array'],
|
||||
'hash_update' => ['bool', 'context'=>'HashContext', 'data'=>'string'],
|
||||
'hash_update_file' => ['bool', 'context'=>'HashContext', 'filename'=>'string', 'stream_context='=>'?resource'],
|
||||
'hash_update_stream' => ['int', 'context'=>'HashContext', 'stream'=>'resource', 'length='=>'int'],
|
||||
@@ -4347,10 +4341,10 @@ return [
|
||||
'http\Message\Parser::stream' => ['int', 'stream'=>'resource', 'flags'=>'int', '&message'=>'http\Message'],
|
||||
'http\Params::__construct' => ['void', 'params='=>'mixed', 'param_sep='=>'mixed', 'arg_sep='=>'mixed', 'val_sep='=>'mixed', 'flags='=>'mixed'],
|
||||
'http\Params::__toString' => ['string'],
|
||||
'http\Params::offsetExists' => ['bool', 'name'=>'mixed'],
|
||||
'http\Params::offsetGet' => ['mixed', 'name'=>'mixed'],
|
||||
'http\Params::offsetSet' => ['void', 'name'=>'mixed', 'value'=>'mixed'],
|
||||
'http\Params::offsetUnset' => ['void', 'name'=>'mixed'],
|
||||
'http\Params::offsetExists' => ['bool', 'name'=>'int|string'],
|
||||
'http\Params::offsetGet' => ['mixed', 'name'=>'int|string'],
|
||||
'http\Params::offsetSet' => ['void', 'name'=>'int|string|null', 'value'=>'mixed'],
|
||||
'http\Params::offsetUnset' => ['void', 'name'=>'int|string'],
|
||||
'http\Params::toArray' => ['array'],
|
||||
'http\Params::toString' => ['string'],
|
||||
'http\QueryString::__construct' => ['void', 'querystring'=>'string'],
|
||||
@@ -4365,10 +4359,10 @@ return [
|
||||
'http\QueryString::getObject' => ['object|mixed', 'name'=>'string', 'defval='=>'mixed', 'delete='=>'bool|false'],
|
||||
'http\QueryString::getString' => ['string|mixed', 'name'=>'string', 'defval='=>'mixed', 'delete='=>'bool|false'],
|
||||
'http\QueryString::mod' => ['http\QueryString', 'params='=>'mixed'],
|
||||
'http\QueryString::offsetExists' => ['bool', 'offset'=>'mixed'],
|
||||
'http\QueryString::offsetGet' => ['mixed|null', 'offset'=>'mixed'],
|
||||
'http\QueryString::offsetSet' => ['void', 'offset'=>'mixed', 'value'=>'mixed'],
|
||||
'http\QueryString::offsetUnset' => ['void', 'offset'=>'mixed'],
|
||||
'http\QueryString::offsetExists' => ['bool', 'offset'=>'int|string'],
|
||||
'http\QueryString::offsetGet' => ['mixed|null', 'offset'=>'int|string'],
|
||||
'http\QueryString::offsetSet' => ['void', 'offset'=>'int|string|null', 'value'=>'mixed'],
|
||||
'http\QueryString::offsetUnset' => ['void', 'offset'=>'int|string'],
|
||||
'http\QueryString::serialize' => ['string'],
|
||||
'http\QueryString::set' => ['http\QueryString', 'params'=>'mixed'],
|
||||
'http\QueryString::toArray' => ['array'],
|
||||
@@ -4492,10 +4486,10 @@ return [
|
||||
'HttpQueryString::getObject' => ['', 'name'=>'', 'defval'=>'', 'delete'=>''],
|
||||
'HttpQueryString::getString' => ['', 'name'=>'', 'defval'=>'', 'delete'=>''],
|
||||
'HttpQueryString::mod' => ['HttpQueryString', 'params'=>'mixed'],
|
||||
'HttpQueryString::offsetExists' => ['bool', 'offset'=>'mixed'],
|
||||
'HttpQueryString::offsetGet' => ['mixed', 'offset'=>'mixed'],
|
||||
'HttpQueryString::offsetSet' => ['void', 'offset'=>'mixed', 'value'=>'mixed'],
|
||||
'HttpQueryString::offsetUnset' => ['void', 'offset'=>'mixed'],
|
||||
'HttpQueryString::offsetExists' => ['bool', 'offset'=>'int|string'],
|
||||
'HttpQueryString::offsetGet' => ['mixed', 'offset'=>'int|string'],
|
||||
'HttpQueryString::offsetSet' => ['void', 'offset'=>'int|string|null', 'value'=>'mixed'],
|
||||
'HttpQueryString::offsetUnset' => ['void', 'offset'=>'int|string'],
|
||||
'HttpQueryString::serialize' => ['string'],
|
||||
'HttpQueryString::set' => ['string', 'params'=>'mixed'],
|
||||
'HttpQueryString::singleton' => ['HttpQueryString', 'global='=>'bool'],
|
||||
@@ -5580,21 +5574,21 @@ return [
|
||||
'imap_body' => ['string|false', 'imap'=>'IMAP\Connection', 'message_num'=>'int', 'flags='=>'int'],
|
||||
'imap_bodystruct' => ['stdClass|false', 'imap'=>'IMAP\Connection', 'message_num'=>'int', 'section'=>'string'],
|
||||
'imap_check' => ['stdClass|false', 'imap'=>'IMAP\Connection'],
|
||||
'imap_clearflag_full' => ['bool', 'imap'=>'IMAP\Connection', 'sequence'=>'string', 'flag'=>'string', 'options='=>'int'],
|
||||
'imap_close' => ['bool', 'imap'=>'IMAP\Connection', 'flags='=>'int'],
|
||||
'imap_clearflag_full' => ['true', 'imap'=>'IMAP\Connection', 'sequence'=>'string', 'flag'=>'string', 'options='=>'int'],
|
||||
'imap_close' => ['true', 'imap'=>'IMAP\Connection', 'flags='=>'int'],
|
||||
'imap_create' => ['bool', 'imap'=>'IMAP\Connection', 'mailbox'=>'string'],
|
||||
'imap_createmailbox' => ['bool', 'imap'=>'IMAP\Connection', 'mailbox'=>'string'],
|
||||
'imap_delete' => ['bool', 'imap'=>'IMAP\Connection', 'message_nums'=>'string', 'flags='=>'int'],
|
||||
'imap_delete' => ['true', 'imap'=>'IMAP\Connection', 'message_nums'=>'string', 'flags='=>'int'],
|
||||
'imap_deletemailbox' => ['bool', 'imap'=>'IMAP\Connection', 'mailbox'=>'string'],
|
||||
'imap_errors' => ['array|false'],
|
||||
'imap_expunge' => ['bool', 'imap'=>'IMAP\Connection'],
|
||||
'imap_expunge' => ['true', 'imap'=>'IMAP\Connection'],
|
||||
'imap_fetch_overview' => ['array|false', 'imap'=>'IMAP\Connection', 'sequence'=>'string', 'flags='=>'int'],
|
||||
'imap_fetchbody' => ['string|false', 'imap'=>'IMAP\Connection', 'message_num'=>'int', 'section'=>'string', 'flags='=>'int'],
|
||||
'imap_fetchheader' => ['string|false', 'imap'=>'IMAP\Connection', 'message_num'=>'int', 'flags='=>'int'],
|
||||
'imap_fetchmime' => ['string|false', 'imap'=>'IMAP\Connection', 'message_num'=>'int', 'section'=>'string', 'flags='=>'int'],
|
||||
'imap_fetchstructure' => ['stdClass|false', 'imap'=>'IMAP\Connection', 'message_num'=>'int', 'flags='=>'int'],
|
||||
'imap_fetchtext' => ['string|false', 'imap'=>'IMAP\Connection', 'message_num'=>'int', 'flags='=>'int'],
|
||||
'imap_gc' => ['bool', 'imap'=>'IMAP\Connection', 'flags'=>'int'],
|
||||
'imap_gc' => ['true', 'imap'=>'IMAP\Connection', 'flags'=>'int'],
|
||||
'imap_get_quota' => ['array|false', 'imap'=>'IMAP\Connection', 'quota_root'=>'string'],
|
||||
'imap_get_quotaroot' => ['array|false', 'imap'=>'IMAP\Connection', 'mailbox'=>'string'],
|
||||
'imap_getacl' => ['array|false', 'imap'=>'IMAP\Connection', 'mailbox'=>'string'],
|
||||
@@ -5635,14 +5629,14 @@ return [
|
||||
'imap_search' => ['array|false', 'imap'=>'IMAP\Connection', 'criteria'=>'string', 'flags='=>'int', 'charset='=>'string'],
|
||||
'imap_set_quota' => ['bool', 'imap'=>'IMAP\Connection', 'quota_root'=>'string', 'mailbox_size'=>'int'],
|
||||
'imap_setacl' => ['bool', 'imap'=>'IMAP\Connection', 'mailbox'=>'string', 'user_id'=>'string', 'rights'=>'string'],
|
||||
'imap_setflag_full' => ['bool', 'imap'=>'IMAP\Connection', 'sequence'=>'string', 'flag'=>'string', 'options='=>'int'],
|
||||
'imap_setflag_full' => ['true', 'imap'=>'IMAP\Connection', 'sequence'=>'string', 'flag'=>'string', 'options='=>'int'],
|
||||
'imap_sort' => ['array|false', 'imap'=>'IMAP\Connection', 'criteria'=>'int', 'reverse'=>'bool', 'flags='=>'int', 'search_criteria='=>'?string', 'charset='=>'?string'],
|
||||
'imap_status' => ['stdClass|false', 'imap'=>'IMAP\Connection', 'mailbox'=>'string', 'flags'=>'int'],
|
||||
'imap_subscribe' => ['bool', 'imap'=>'IMAP\Connection', 'mailbox'=>'string'],
|
||||
'imap_thread' => ['array|false', 'imap'=>'IMAP\Connection', 'flags='=>'int'],
|
||||
'imap_timeout' => ['int|bool', 'timeout_type'=>'int', 'timeout='=>'int'],
|
||||
'imap_uid' => ['int|false', 'imap'=>'IMAP\Connection', 'message_num'=>'int'],
|
||||
'imap_undelete' => ['bool', 'imap'=>'IMAP\Connection', 'message_nums'=>'string', 'flags='=>'int'],
|
||||
'imap_undelete' => ['true', 'imap'=>'IMAP\Connection', 'message_nums'=>'string', 'flags='=>'int'],
|
||||
'imap_unsubscribe' => ['bool', 'imap'=>'IMAP\Connection', 'mailbox'=>'string'],
|
||||
'imap_utf7_decode' => ['string|false', 'string'=>'string'],
|
||||
'imap_utf7_encode' => ['string', 'string'=>'string'],
|
||||
@@ -5737,11 +5731,11 @@ return [
|
||||
'IntlBreakIterator::next' => ['int', 'offset='=>'?int'],
|
||||
'IntlBreakIterator::preceding' => ['int', 'offset'=>'int'],
|
||||
'IntlBreakIterator::previous' => ['int'],
|
||||
'IntlBreakIterator::setText' => ['?bool', 'text'=>'string'],
|
||||
'IntlBreakIterator::setText' => ['bool', 'text'=>'string'],
|
||||
'intlcal_add' => ['bool', 'calendar'=>'IntlCalendar', 'field'=>'int', 'value'=>'int'],
|
||||
'intlcal_after' => ['bool', 'calendar'=>'IntlCalendar', 'other'=>'IntlCalendar'],
|
||||
'intlcal_before' => ['bool', 'calendar'=>'IntlCalendar', 'other'=>'IntlCalendar'],
|
||||
'intlcal_clear' => ['bool', 'calendar'=>'IntlCalendar', 'field='=>'?int'],
|
||||
'intlcal_clear' => ['true', 'calendar'=>'IntlCalendar', 'field='=>'?int'],
|
||||
'intlcal_create_instance' => ['?IntlCalendar', 'timezone='=>'mixed', 'locale='=>'?string'],
|
||||
'intlcal_equals' => ['bool', 'calendar'=>'IntlCalendar', 'other'=>'IntlCalendar'],
|
||||
'intlcal_field_difference' => ['int|false', 'calendar'=>'IntlCalendar', 'timestamp'=>'float', 'field'=>'int'],
|
||||
@@ -5774,8 +5768,8 @@ return [
|
||||
'intlcal_roll' => ['bool', 'calendar'=>'IntlCalendar', 'field'=>'int', 'value'=>'mixed'],
|
||||
'intlcal_set' => ['bool', 'calendar'=>'IntlCalendar', 'year'=>'int', 'month'=>'int'],
|
||||
'intlcal_set\'1' => ['bool', 'calendar'=>'IntlCalendar', 'year'=>'int', 'month'=>'int', 'dayOfMonth='=>'int', 'hour='=>'int', 'minute='=>'int', 'second='=>'int'],
|
||||
'intlcal_set_first_day_of_week' => ['bool', 'calendar'=>'IntlCalendar', 'dayOfWeek'=>'int'],
|
||||
'intlcal_set_lenient' => ['bool', 'calendar'=>'IntlCalendar', 'lenient'=>'bool'],
|
||||
'intlcal_set_first_day_of_week' => ['true', 'calendar'=>'IntlCalendar', 'dayOfWeek'=>'int'],
|
||||
'intlcal_set_lenient' => ['true', 'calendar'=>'IntlCalendar', 'lenient'=>'bool'],
|
||||
'intlcal_set_repeated_wall_time_option' => ['true', 'calendar'=>'IntlCalendar', 'option'=>'int'],
|
||||
'intlcal_set_skipped_wall_time_option' => ['true', 'calendar'=>'IntlCalendar', 'option'=>'int'],
|
||||
'intlcal_set_time' => ['bool', 'calendar'=>'IntlCalendar', 'timestamp'=>'float'],
|
||||
@@ -5837,7 +5831,7 @@ return [
|
||||
'IntlChar::charType' => ['?int', 'codepoint'=>'int|string'],
|
||||
'IntlChar::chr' => ['?string', 'codepoint'=>'int|string'],
|
||||
'IntlChar::digit' => ['int|false|null', 'codepoint'=>'int|string', 'base='=>'int'],
|
||||
'IntlChar::enumCharNames' => ['?bool', 'start'=>'string|int', 'end'=>'string|int', 'callback'=>'callable(int,int,int):void', 'type='=>'int'],
|
||||
'IntlChar::enumCharNames' => ['bool', 'start'=>'string|int', 'end'=>'string|int', 'callback'=>'callable(int,int,int):void', 'type='=>'int'],
|
||||
'IntlChar::enumCharTypes' => ['void', 'callback'=>'callable(int,int,int):void'],
|
||||
'IntlChar::foldCase' => ['int|string|null', 'codepoint'=>'int|string', 'options='=>'int'],
|
||||
'IntlChar::forDigit' => ['int', 'digit'=>'int', 'base='=>'int'],
|
||||
@@ -5887,7 +5881,6 @@ return [
|
||||
'IntlChar::tolower' => ['int|string|null', 'codepoint'=>'int|string'],
|
||||
'IntlChar::totitle' => ['int|string|null', 'codepoint'=>'int|string'],
|
||||
'IntlChar::toupper' => ['int|string|null', 'codepoint'=>'int|string'],
|
||||
'IntlCodePointBreakIterator::__construct' => ['void'],
|
||||
'IntlCodePointBreakIterator::createCharacterInstance' => ['?IntlRuleBasedBreakIterator', 'locale='=>'?string'],
|
||||
'IntlCodePointBreakIterator::createCodePointInstance' => ['IntlCodePointBreakIterator'],
|
||||
'IntlCodePointBreakIterator::createLineInstance' => ['?IntlRuleBasedBreakIterator', 'locale='=>'?string'],
|
||||
@@ -5908,7 +5901,7 @@ return [
|
||||
'IntlCodePointBreakIterator::next' => ['int', 'offset='=>'?int'],
|
||||
'IntlCodePointBreakIterator::preceding' => ['int', 'offset'=>'int'],
|
||||
'IntlCodePointBreakIterator::previous' => ['int'],
|
||||
'IntlCodePointBreakIterator::setText' => ['?bool', 'text'=>'string'],
|
||||
'IntlCodePointBreakIterator::setText' => ['bool', 'text'=>'string'],
|
||||
'IntlDateFormatter::__construct' => ['void', 'locale'=>'?string', 'dateType='=>'int', 'timeType='=>'int', 'timezone='=>'IntlTimeZone|DateTimeZone|string|null', 'calendar='=>'IntlCalendar|int|null', 'pattern='=>'?string'],
|
||||
'IntlDateFormatter::create' => ['?IntlDateFormatter', 'locale'=>'?string', 'dateType='=>'int', 'timeType='=>'int', 'timezone='=>'IntlTimeZone|DateTimeZone|string|null', 'calendar='=>'IntlCalendar|int|null', 'pattern='=>'?string'],
|
||||
'IntlDateFormatter::format' => ['string|false', 'datetime'=>'IntlCalendar|DateTimeInterface|array{0: int, 1: int, 2: int, 3: int, 4: int, 5: int, 6: int, 7: int, 8: int}|array{tm_sec: int, tm_min: int, tm_hour: int, tm_mday: int, tm_mon: int, tm_year: int, tm_wday: int, tm_yday: int, tm_isdst: int}|string|int|float'],
|
||||
@@ -5929,8 +5922,7 @@ return [
|
||||
'IntlDateFormatter::setCalendar' => ['bool', 'calendar'=>'IntlCalendar|int|null'],
|
||||
'IntlDateFormatter::setLenient' => ['void', 'lenient'=>'bool'],
|
||||
'IntlDateFormatter::setPattern' => ['bool', 'pattern'=>'string'],
|
||||
'IntlDateFormatter::setTimeZone' => ['null|false', 'timezone'=>'IntlTimeZone|DateTimeZone|string|null'],
|
||||
'IntlException::__clone' => ['void'],
|
||||
'IntlDateFormatter::setTimeZone' => ['bool', 'timezone'=>'IntlTimeZone|DateTimeZone|string|null'],
|
||||
'IntlException::__construct' => ['void', 'message='=>'string', 'code='=>'int', 'previous='=>'?Throwable'],
|
||||
'IntlException::__toString' => ['string'],
|
||||
'IntlException::__wakeup' => ['void'],
|
||||
@@ -6026,7 +6018,7 @@ return [
|
||||
'IntlRuleBasedBreakIterator::next' => ['int', 'offset='=>'?int'],
|
||||
'IntlRuleBasedBreakIterator::preceding' => ['int', 'offset'=>'int'],
|
||||
'IntlRuleBasedBreakIterator::previous' => ['int'],
|
||||
'IntlRuleBasedBreakIterator::setText' => ['?bool', 'text'=>'string'],
|
||||
'IntlRuleBasedBreakIterator::setText' => ['bool', 'text'=>'string'],
|
||||
'IntlTimeZone::countEquivalentIDs' => ['int|false', 'timezoneId'=>'string'],
|
||||
'IntlTimeZone::createDefault' => ['IntlTimeZone'],
|
||||
'IntlTimeZone::createEnumeration' => ['IntlIterator|false', 'countryOrRawOffset='=>'IntlTimeZone|string|int|float|null'],
|
||||
@@ -6071,7 +6063,6 @@ return [
|
||||
'intltz_use_daylight_time' => ['bool', 'timezone'=>'IntlTimeZone'],
|
||||
'intlz_create_default' => ['IntlTimeZone'],
|
||||
'intval' => ['int', 'value'=>'mixed', 'base='=>'int'],
|
||||
'InvalidArgumentException::__clone' => ['void'],
|
||||
'InvalidArgumentException::__construct' => ['void', 'message='=>'string', 'code='=>'int', 'previous='=>'?Throwable'],
|
||||
'InvalidArgumentException::__toString' => ['string'],
|
||||
'InvalidArgumentException::getCode' => ['int'],
|
||||
@@ -6157,7 +6148,6 @@ return [
|
||||
'json_last_error' => ['int'],
|
||||
'json_last_error_msg' => ['string'],
|
||||
'json_validate' => ['bool', 'json'=>'string', 'depth='=>'positive-int', 'flags='=>'int'],
|
||||
'JsonException::__clone' => ['void'],
|
||||
'JsonException::__construct' => ['void', "message="=>"string", 'code='=>'int', 'previous='=>'?Throwable'],
|
||||
'JsonException::__toString' => ['string'],
|
||||
'JsonException::__wakeup' => ['void'],
|
||||
@@ -6188,10 +6178,10 @@ return [
|
||||
'Judy::memoryUsage' => ['int'],
|
||||
'Judy::next' => ['mixed', 'index'=>'mixed'],
|
||||
'Judy::nextEmpty' => ['mixed', 'index'=>'mixed'],
|
||||
'Judy::offsetExists' => ['bool', 'offset'=>'mixed'],
|
||||
'Judy::offsetGet' => ['mixed', 'offset'=>'mixed'],
|
||||
'Judy::offsetSet' => ['bool', 'offset'=>'mixed', 'value'=>'mixed'],
|
||||
'Judy::offsetUnset' => ['bool', 'offset'=>'mixed'],
|
||||
'Judy::offsetExists' => ['bool', 'offset'=>'int|string'],
|
||||
'Judy::offsetGet' => ['mixed', 'offset'=>'int|string'],
|
||||
'Judy::offsetSet' => ['bool', 'offset'=>'int|string|null', 'value'=>'mixed'],
|
||||
'Judy::offsetUnset' => ['bool', 'offset'=>'int|string'],
|
||||
'Judy::prev' => ['mixed', 'index'=>'mixed'],
|
||||
'Judy::prevEmpty' => ['mixed', 'index'=>'mixed'],
|
||||
'Judy::size' => ['int'],
|
||||
@@ -6384,7 +6374,6 @@ return [
|
||||
'legendObj::free' => ['void'],
|
||||
'legendObj::set' => ['int', 'property_name'=>'string', 'new_value'=>''],
|
||||
'legendObj::updateFromString' => ['int', 'snippet'=>'string'],
|
||||
'LengthException::__clone' => ['void'],
|
||||
'LengthException::__construct' => ['void', 'message='=>'string', 'code='=>'int', 'previous='=>'?Throwable'],
|
||||
'LengthException::__toString' => ['string'],
|
||||
'LengthException::getCode' => ['int'],
|
||||
@@ -6497,7 +6486,6 @@ return [
|
||||
'log' => ['float', 'num'=>'float', 'base='=>'float'],
|
||||
'log10' => ['float', 'num'=>'float'],
|
||||
'log1p' => ['float', 'num'=>'float'],
|
||||
'LogicException::__clone' => ['void'],
|
||||
'LogicException::__construct' => ['void', 'message='=>'string', 'code='=>'int', 'previous='=>'?Throwable'],
|
||||
'LogicException::__toString' => ['string'],
|
||||
'LogicException::getCode' => ['int'],
|
||||
@@ -7059,6 +7047,14 @@ return [
|
||||
'MongoDB::setReadPreference' => ['bool', 'read_preference'=>'string', 'tags='=>'array'],
|
||||
'MongoDB::setSlaveOkay' => ['bool', 'ok='=>'bool'],
|
||||
'MongoDB::setWriteConcern' => ['bool', 'w'=>'mixed', 'wtimeout='=>'int'],
|
||||
'MongoDB\BSON\fromJSON' => ['string', 'json' => 'string'],
|
||||
'MongoDB\BSON\fromPHP' => ['string', 'value' => 'object|array'],
|
||||
'MongoDB\BSON\toCanonicalExtendedJSON' => ['string', 'bson' => 'string'],
|
||||
'MongoDB\BSON\toJSON' => ['string', 'bson' => 'string'],
|
||||
'MongoDB\BSON\toPHP' => ['object|array', 'bson' => 'string', 'typemap=' => '?array'],
|
||||
'MongoDB\BSON\toRelaxedExtendedJSON' => ['string', 'bson' => 'string'],
|
||||
'MongoDB\Driver\Monitoring\addSubscriber' => ['void', 'subscriber' => 'MongoDB\Driver\Monitoring\Subscriber'],
|
||||
'MongoDB\Driver\Monitoring\removeSubscriber' => ['void', 'subscriber' => 'MongoDB\Driver\Monitoring\Subscriber'],
|
||||
'MongoDB\BSON\Binary::__construct' => ['void', 'data' => 'string', 'type=' => 'int'],
|
||||
'MongoDB\BSON\Binary::getData' => ['string'],
|
||||
'MongoDB\BSON\Binary::getType' => ['int'],
|
||||
@@ -7079,10 +7075,28 @@ return [
|
||||
'MongoDB\BSON\Decimal128::unserialize' => ['void', 'serialized' => 'string'],
|
||||
'MongoDB\BSON\Decimal128::jsonSerialize' => ['mixed'],
|
||||
'MongoDB\BSON\Decimal128Interface::__toString' => ['string'],
|
||||
'MongoDB\BSON\Document::fromBSON' => ['MongoDB\BSON\Document', 'bson' => 'string'],
|
||||
'MongoDB\BSON\Document::fromJSON' => ['MongoDB\BSON\Document', 'json' => 'string'],
|
||||
'MongoDB\BSON\Document::fromPHP' => ['MongoDB\BSON\Document', 'value' => 'object|array'],
|
||||
'MongoDB\BSON\Document::get' => ['mixed', 'key' => 'string'],
|
||||
'MongoDB\BSON\Document::getIterator' => ['MongoDB\BSON\Iterator'],
|
||||
'MongoDB\BSON\Document::has' => ['bool', 'key' => 'string'],
|
||||
'MongoDB\BSON\Document::toPHP' => ['object|array', 'typeMap=' => '?array'],
|
||||
'MongoDB\BSON\Document::toCanonicalExtendedJSON' => ['string'],
|
||||
'MongoDB\BSON\Document::toRelaxedExtendedJSON' => ['string'],
|
||||
'MongoDB\BSON\Document::__toString' => ['string'],
|
||||
'MongoDB\BSON\Document::serialize' => ['string'],
|
||||
'MongoDB\BSON\Document::unserialize' => ['void', 'serialized' => 'string'],
|
||||
'MongoDB\BSON\Int64::__construct' => ['void', 'value' => 'string|int'],
|
||||
'MongoDB\BSON\Int64::__toString' => ['string'],
|
||||
'MongoDB\BSON\Int64::serialize' => ['string'],
|
||||
'MongoDB\BSON\Int64::unserialize' => ['void', 'serialized' => 'string'],
|
||||
'MongoDB\BSON\Int64::jsonSerialize' => ['mixed'],
|
||||
'MongoDB\BSON\Iterator::current' => ['mixed'],
|
||||
'MongoDB\BSON\Iterator::key' => ['string|int'],
|
||||
'MongoDB\BSON\Iterator::next' => ['void'],
|
||||
'MongoDB\BSON\Iterator::rewind' => ['void'],
|
||||
'MongoDB\BSON\Iterator::valid' => ['bool'],
|
||||
'MongoDB\BSON\Javascript::__construct' => ['void', 'code' => 'string', 'scope=' => 'object|array|null'],
|
||||
'MongoDB\BSON\Javascript::getCode' => ['string'],
|
||||
'MongoDB\BSON\Javascript::getScope' => ['?object'],
|
||||
@@ -7107,6 +7121,14 @@ return [
|
||||
'MongoDB\BSON\ObjectId::jsonSerialize' => ['mixed'],
|
||||
'MongoDB\BSON\ObjectIdInterface::getTimestamp' => ['int'],
|
||||
'MongoDB\BSON\ObjectIdInterface::__toString' => ['string'],
|
||||
'MongoDB\BSON\PackedArray::fromPHP' => ['MongoDB\BSON\PackedArray', 'value' => 'array'],
|
||||
'MongoDB\BSON\PackedArray::get' => ['mixed', 'index' => 'int'],
|
||||
'MongoDB\BSON\PackedArray::getIterator' => ['MongoDB\BSON\Iterator'],
|
||||
'MongoDB\BSON\PackedArray::has' => ['bool', 'index' => 'int'],
|
||||
'MongoDB\BSON\PackedArray::toPHP' => ['object|array', 'typeMap=' => '?array'],
|
||||
'MongoDB\BSON\PackedArray::__toString' => ['string'],
|
||||
'MongoDB\BSON\PackedArray::serialize' => ['string'],
|
||||
'MongoDB\BSON\PackedArray::unserialize' => ['void', 'serialized' => 'string'],
|
||||
'MongoDB\BSON\Regex::__construct' => ['void', 'pattern' => 'string', 'flags=' => 'string'],
|
||||
'MongoDB\BSON\Regex::getPattern' => ['string'],
|
||||
'MongoDB\BSON\Regex::getFlags' => ['string'],
|
||||
@@ -7156,6 +7178,7 @@ return [
|
||||
'MongoDB\Driver\ClientEncryption::decrypt' => ['mixed', 'value' => 'MongoDB\BSON\Binary'],
|
||||
'MongoDB\Driver\ClientEncryption::deleteKey' => ['object', 'keyId' => 'MongoDB\BSON\Binary'],
|
||||
'MongoDB\Driver\ClientEncryption::encrypt' => ['MongoDB\BSON\Binary', 'value' => 'mixed', 'options=' => '?array'],
|
||||
'MongoDB\Driver\ClientEncryption::encryptExpression' => ['object', 'expr' => 'object|array', 'options=' => '?array'],
|
||||
'MongoDB\Driver\ClientEncryption::getKey' => ['?object', 'keyId' => 'MongoDB\BSON\Binary'],
|
||||
'MongoDB\Driver\ClientEncryption::getKeyByAltName' => ['?object', 'keyAltName' => 'string'],
|
||||
'MongoDB\Driver\ClientEncryption::getKeys' => ['MongoDB\Driver\Cursor'],
|
||||
@@ -7367,6 +7390,7 @@ return [
|
||||
'MongoDB\Driver\WriteResult::getUpsertedIds' => ['array'],
|
||||
'MongoDB\Driver\WriteResult::getWriteConcernError' => ['?MongoDB\Driver\WriteConcernError'],
|
||||
'MongoDB\Driver\WriteResult::getWriteErrors' => ['array'],
|
||||
'MongoDB\Driver\WriteResult::getErrorReplies' => ['array'],
|
||||
'MongoDB\Driver\WriteResult::isAcknowledged' => ['bool'],
|
||||
'MongoDBRef::create' => ['array', 'collection'=>'string', 'id'=>'mixed', 'database='=>'string'],
|
||||
'MongoDBRef::get' => ['?array', 'db'=>'MongoDB', 'ref'=>'array'],
|
||||
@@ -7615,7 +7639,7 @@ return [
|
||||
'mt_getrandmax' => ['int'],
|
||||
'mt_rand' => ['int', 'min'=>'int', 'max'=>'int'],
|
||||
'mt_rand\'1' => ['int'],
|
||||
'mt_srand' => ['void', 'seed='=>'int', 'mode='=>'int'],
|
||||
'mt_srand' => ['void', 'seed='=>'?int', 'mode='=>'int'],
|
||||
'MultipleIterator::__construct' => ['void', 'flags='=>'int'],
|
||||
'MultipleIterator::attachIterator' => ['void', 'iterator'=>'Iterator', 'info='=>'string|int|null'],
|
||||
'MultipleIterator::containsIterator' => ['bool', 'iterator'=>'Iterator'],
|
||||
@@ -8092,8 +8116,8 @@ return [
|
||||
'MysqlndUhPreparedStatement::__construct' => ['void'],
|
||||
'MysqlndUhPreparedStatement::execute' => ['bool', 'statement'=>'mysqlnd_prepared_statement'],
|
||||
'MysqlndUhPreparedStatement::prepare' => ['bool', 'statement'=>'mysqlnd_prepared_statement', 'query'=>'string'],
|
||||
'natcasesort' => ['bool', '&rw_array'=>'array'],
|
||||
'natsort' => ['bool', '&rw_array'=>'array'],
|
||||
'natcasesort' => ['true', '&rw_array'=>'array'],
|
||||
'natsort' => ['true', '&rw_array'=>'array'],
|
||||
'net_get_interfaces' => ['array<string,array<string,mixed>>|false'],
|
||||
'newrelic_add_custom_parameter' => ['bool', 'key'=>'string', 'value'=>'bool|float|int|string'],
|
||||
'newrelic_add_custom_tracer' => ['bool', 'function_name'=>'string'],
|
||||
@@ -8502,7 +8526,6 @@ return [
|
||||
'OuterIterator::next' => ['void'],
|
||||
'OuterIterator::rewind' => ['void'],
|
||||
'OuterIterator::valid' => ['bool'],
|
||||
'OutOfBoundsException::__clone' => ['void'],
|
||||
'OutOfBoundsException::__construct' => ['void', 'message='=>'string', 'code='=>'int', 'previous='=>'?Throwable'],
|
||||
'OutOfBoundsException::__toString' => ['string'],
|
||||
'OutOfBoundsException::getCode' => ['int'],
|
||||
@@ -8512,7 +8535,6 @@ return [
|
||||
'OutOfBoundsException::getPrevious' => ['?Throwable'],
|
||||
'OutOfBoundsException::getTrace' => ['list<array{file?:string,line?:int,function:string,class?:class-string,type?:\'::\'|\'->\',args?:array<mixed>}>'],
|
||||
'OutOfBoundsException::getTraceAsString' => ['string'],
|
||||
'OutOfRangeException::__clone' => ['void'],
|
||||
'OutOfRangeException::__construct' => ['void', 'message='=>'string', 'code='=>'int', 'previous='=>'?Throwable'],
|
||||
'OutOfRangeException::__toString' => ['string'],
|
||||
'OutOfRangeException::getCode' => ['int'],
|
||||
@@ -8539,7 +8561,6 @@ return [
|
||||
'outputformatObj::set' => ['int', 'property_name'=>'string', 'new_value'=>''],
|
||||
'outputformatObj::setOption' => ['void', 'property_name'=>'string', 'new_value'=>'string'],
|
||||
'outputformatObj::validate' => ['int'],
|
||||
'OverflowException::__clone' => ['void'],
|
||||
'OverflowException::__construct' => ['void', 'message='=>'string', 'code='=>'int', 'previous='=>'?Throwable'],
|
||||
'OverflowException::__toString' => ['string'],
|
||||
'OverflowException::getCode' => ['int'],
|
||||
@@ -8631,7 +8652,6 @@ return [
|
||||
'parse_ini_string' => ['array|false', 'ini_string'=>'string', 'process_sections='=>'bool', 'scanner_mode='=>'int'],
|
||||
'parse_str' => ['void', 'string'=>'string', '&w_result'=>'array'],
|
||||
'parse_url' => ['int|string|array|null|false', 'url'=>'string', 'component='=>'int'],
|
||||
'ParseError::__clone' => ['void'],
|
||||
'ParseError::__construct' => ['void', 'message='=>'string', 'code='=>'int', 'previous='=>'?Throwable'],
|
||||
'ParseError::__toString' => ['string'],
|
||||
'ParseError::getCode' => ['int'],
|
||||
@@ -9363,7 +9383,7 @@ return [
|
||||
'posix_getppid' => ['int'],
|
||||
'posix_getpwnam' => ['array{name: string, passwd: string, uid: int, gid: int, gecos: string, dir: string, shell: string}|false', 'username'=>'string'],
|
||||
'posix_getpwuid' => ['array{name: string, passwd: string, uid: int, gid: int, gecos: string, dir: string, shell: string}|false', 'user_id'=>'int'],
|
||||
'posix_getrlimit' => ['array{"soft core": string, "hard core": string, "soft data": string, "hard data": string, "soft stack": integer, "hard stack": string, "soft totalmem": string, "hard totalmem": string, "soft rss": string, "hard rss": string, "soft maxproc": integer, "hard maxproc": integer, "soft memlock": integer, "hard memlock": integer, "soft cpu": string, "hard cpu": string, "soft filesize": string, "hard filesize": string, "soft openfiles": integer, "hard openfiles": integer}|false'],
|
||||
'posix_getrlimit' => ['array{"soft core": string, "hard core": string, "soft data": string, "hard data": string, "soft stack": integer, "hard stack": string, "soft totalmem": string, "hard totalmem": string, "soft rss": string, "hard rss": string, "soft maxproc": integer, "hard maxproc": integer, "soft memlock": integer, "hard memlock": integer, "soft cpu": string, "hard cpu": string, "soft filesize": string, "hard filesize": string, "soft openfiles": integer, "hard openfiles": integer}|false', 'resource=' => '?int'],
|
||||
'posix_getsid' => ['int|false', 'process_id'=>'int'],
|
||||
'posix_getuid' => ['int'],
|
||||
'posix_initgroups' => ['bool', 'username'=>'string', 'group_id'=>'int'],
|
||||
@@ -9403,7 +9423,7 @@ return [
|
||||
'print' => ['int', 'arg'=>'string'],
|
||||
'print_r' => ['string', 'value'=>'mixed'],
|
||||
'print_r\'1' => ['true', 'value'=>'mixed', 'return='=>'bool'],
|
||||
'printf' => ['int', 'format'=>'string', '...values='=>'string|int|float'],
|
||||
'printf' => ['int<0, max>', 'format'=>'string', '...values='=>'string|int|float'],
|
||||
'proc_close' => ['int', 'process'=>'resource'],
|
||||
'proc_get_status' => ['array{command: string, pid: int, running: bool, signaled: bool, stopped: bool, exitcode: int, termsig: int, stopsig: int}', 'process'=>'resource'],
|
||||
'proc_nice' => ['bool', 'priority'=>'int'],
|
||||
@@ -9624,7 +9644,6 @@ return [
|
||||
'random_bytes' => ['non-empty-string', 'length'=>'positive-int'],
|
||||
'random_int' => ['int', 'min'=>'int', 'max'=>'int'],
|
||||
'range' => ['non-empty-array', 'start'=>'string|int|float', 'end'=>'string|int|float', 'step='=>'int<1, max>|float'],
|
||||
'RangeException::__clone' => ['void'],
|
||||
'RangeException::__construct' => ['void', 'message='=>'string', 'code='=>'int', 'previous='=>'?Throwable'],
|
||||
'RangeException::__toString' => ['string'],
|
||||
'RangeException::getCode' => ['int'],
|
||||
@@ -9814,7 +9833,7 @@ return [
|
||||
'RecursiveArrayIterator::next' => ['void'],
|
||||
'RecursiveArrayIterator::offsetExists' => ['bool', 'key'=>'string|int'],
|
||||
'RecursiveArrayIterator::offsetGet' => ['mixed', 'key'=>'string|int'],
|
||||
'RecursiveArrayIterator::offsetSet' => ['void', 'key'=>'string|int', 'value'=>'string'],
|
||||
'RecursiveArrayIterator::offsetSet' => ['void', 'key'=>'string|int|null', 'value'=>'string'],
|
||||
'RecursiveArrayIterator::offsetUnset' => ['void', 'key'=>'string|int'],
|
||||
'RecursiveArrayIterator::rewind' => ['void'],
|
||||
'RecursiveArrayIterator::seek' => ['void', 'offset'=>'int'],
|
||||
@@ -10624,12 +10643,10 @@ return [
|
||||
'ReflectionMethod::isVariadic' => ['bool'],
|
||||
'ReflectionMethod::returnsReference' => ['bool'],
|
||||
'ReflectionMethod::setAccessible' => ['void', 'accessible'=>'bool'],
|
||||
'ReflectionNamedType::__clone' => ['void'],
|
||||
'ReflectionNamedType::__toString' => ['string'],
|
||||
'ReflectionNamedType::allowsNull' => ['bool'],
|
||||
'ReflectionNamedType::getName' => ['string'],
|
||||
'ReflectionNamedType::isBuiltin' => ['bool'],
|
||||
'ReflectionObject::__clone' => ['void'],
|
||||
'ReflectionObject::__construct' => ['void', 'object'=>'object'],
|
||||
'ReflectionObject::__toString' => ['string'],
|
||||
'ReflectionObject::getConstant' => ['mixed', 'name'=>'string'],
|
||||
@@ -10814,7 +10831,7 @@ return [
|
||||
'RRDGraph::setOptions' => ['void', 'options'=>'array'],
|
||||
'RRDUpdater::__construct' => ['void', 'path'=>'string'],
|
||||
'RRDUpdater::update' => ['bool', 'values'=>'array', 'time='=>'string'],
|
||||
'rsort' => ['bool', '&rw_array'=>'array', 'flags='=>'int'],
|
||||
'rsort' => ['true', '&rw_array'=>'array', 'flags='=>'int'],
|
||||
'rtrim' => ['string', 'string'=>'string', 'characters='=>'string'],
|
||||
'runkit7_constant_add' => ['bool', 'constant_name'=>'string', 'value'=>'mixed', 'new_visibility='=>'int'],
|
||||
'runkit7_constant_redefine' => ['bool', 'constant_name'=>'string', 'value'=>'mixed', 'new_visibility='=>'?int'],
|
||||
@@ -10861,7 +10878,6 @@ return [
|
||||
'Runkit_Sandbox_Parent::__construct' => ['void'],
|
||||
'runkit_superglobals' => ['array'],
|
||||
'runkit_zval_inspect' => ['array', 'value'=>'mixed'],
|
||||
'RuntimeException::__clone' => ['void'],
|
||||
'RuntimeException::__construct' => ['void', 'message='=>'string', 'code='=>'int', 'previous='=>'?Throwable'],
|
||||
'RuntimeException::__toString' => ['string'],
|
||||
'RuntimeException::getCode' => ['int'],
|
||||
@@ -11303,7 +11319,7 @@ return [
|
||||
'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::offsetSet' => ['void', 'offset'=>'int|string|null', 'value'=>'mixed'],
|
||||
'SimpleXMLElement::offsetUnset' => ['void', 'offset'=>'int|string'],
|
||||
'SimpleXMLElement::registerXPathNamespace' => ['bool', 'prefix'=>'string', 'namespace'=>'string'],
|
||||
'SimpleXMLElement::saveXML' => ['string|bool', 'filename='=>'?string'],
|
||||
@@ -12721,7 +12737,7 @@ return [
|
||||
'sqlsrv_send_stream_data' => ['bool', 'stmt'=>'resource'],
|
||||
'sqlsrv_server_info' => ['array', 'conn'=>'resource'],
|
||||
'sqrt' => ['float', 'num'=>'float'],
|
||||
'srand' => ['void', 'seed='=>'int', 'mode='=>'int'],
|
||||
'srand' => ['void', 'seed='=>'?int', 'mode='=>'int'],
|
||||
'sscanf' => ['list<float|int|string|null>|int|null', 'string'=>'string', 'format'=>'string', '&...w_vars='=>'string|int|float|null'],
|
||||
'ssdeep_fuzzy_compare' => ['int', 'signature1'=>'string', 'signature2'=>'string'],
|
||||
'ssdeep_fuzzy_hash' => ['string', 'to_hash'=>'string'],
|
||||
@@ -13616,10 +13632,10 @@ return [
|
||||
'Thread::merge' => ['bool', 'from'=>'', 'overwrite='=>'mixed'],
|
||||
'Thread::notify' => ['bool'],
|
||||
'Thread::notifyOne' => ['bool'],
|
||||
'Thread::offsetExists' => ['bool', 'offset'=>'mixed'],
|
||||
'Thread::offsetGet' => ['mixed', 'offset'=>'mixed'],
|
||||
'Thread::offsetSet' => ['void', 'offset'=>'mixed', 'value'=>'mixed'],
|
||||
'Thread::offsetUnset' => ['void', 'offset'=>'mixed'],
|
||||
'Thread::offsetExists' => ['bool', 'offset'=>'int|string'],
|
||||
'Thread::offsetGet' => ['mixed', 'offset'=>'int|string'],
|
||||
'Thread::offsetSet' => ['void', 'offset'=>'int|string|null', 'value'=>'mixed'],
|
||||
'Thread::offsetUnset' => ['void', 'offset'=>'int|string'],
|
||||
'Thread::pop' => ['bool'],
|
||||
'Thread::run' => ['void'],
|
||||
'Thread::setGarbage' => ['void'],
|
||||
@@ -13645,10 +13661,10 @@ return [
|
||||
'Threaded::merge' => ['bool', 'from'=>'mixed', 'overwrite='=>'bool'],
|
||||
'Threaded::notify' => ['bool'],
|
||||
'Threaded::notifyOne' => ['bool'],
|
||||
'Threaded::offsetExists' => ['bool', 'offset'=>'mixed'],
|
||||
'Threaded::offsetGet' => ['mixed', 'offset'=>'mixed'],
|
||||
'Threaded::offsetSet' => ['void', 'offset'=>'mixed', 'value'=>'mixed'],
|
||||
'Threaded::offsetUnset' => ['void', 'offset'=>'mixed'],
|
||||
'Threaded::offsetExists' => ['bool', 'offset'=>'int|string'],
|
||||
'Threaded::offsetGet' => ['mixed', 'offset'=>'int|string'],
|
||||
'Threaded::offsetSet' => ['void', 'offset'=>'int|string|null', 'value'=>'mixed'],
|
||||
'Threaded::offsetUnset' => ['void', 'offset'=>'int|string'],
|
||||
'Threaded::pop' => ['bool'],
|
||||
'Threaded::run' => ['void'],
|
||||
'Threaded::setGarbage' => ['void'],
|
||||
@@ -13973,7 +13989,6 @@ return [
|
||||
'transliterator_transliterate' => ['string|false', 'transliterator'=>'Transliterator|string', 'string'=>'string', 'start='=>'int', 'end='=>'int'],
|
||||
'trigger_error' => ['bool', 'message'=>'string', 'error_level='=>'256|512|1024|16384'],
|
||||
'trim' => ['string', 'string'=>'string', 'characters='=>'string'],
|
||||
'TypeError::__clone' => ['void'],
|
||||
'TypeError::__construct' => ['void', 'message='=>'string', 'code='=>'int', 'previous='=>'?Throwable'],
|
||||
'TypeError::__toString' => ['string'],
|
||||
'TypeError::getCode' => ['int'],
|
||||
@@ -14196,7 +14211,6 @@ return [
|
||||
'ui\window::setTitle' => ['', 'title'=>'string'],
|
||||
'uksort' => ['true', '&rw_array'=>'array', 'callback'=>'callable(mixed,mixed):int'],
|
||||
'umask' => ['int', 'mask='=>'?int'],
|
||||
'UnderflowException::__clone' => ['void'],
|
||||
'UnderflowException::__construct' => ['void', 'message='=>'string', 'code='=>'int', 'previous='=>'?Throwable'],
|
||||
'UnderflowException::__toString' => ['string'],
|
||||
'UnderflowException::getCode' => ['int'],
|
||||
@@ -14206,7 +14220,6 @@ return [
|
||||
'UnderflowException::getPrevious' => ['?Throwable'],
|
||||
'UnderflowException::getTrace' => ['list<array{file?:string,line?:int,function:string,class?:class-string,type?:\'::\'|\'->\',args?:array<mixed>}>'],
|
||||
'UnderflowException::getTraceAsString' => ['string'],
|
||||
'UnexpectedValueException::__clone' => ['void'],
|
||||
'UnexpectedValueException::__construct' => ['void', 'message='=>'string', 'code='=>'int', 'previous='=>'?Throwable'],
|
||||
'UnexpectedValueException::__toString' => ['string'],
|
||||
'UnexpectedValueException::getCode' => ['int'],
|
||||
@@ -14364,7 +14377,7 @@ return [
|
||||
'VarnishStat::getSnapshot' => ['array'],
|
||||
'version_compare' => ['bool', 'version1'=>'string', 'version2'=>'string', 'operator'=>'\'<\'|\'lt\'|\'<=\'|\'le\'|\'>\'|\'gt\'|\'>=\'|\'ge\'|\'==\'|\'=\'|\'eq\'|\'!=\'|\'<>\'|\'ne\''],
|
||||
'version_compare\'1' => ['int', 'version1'=>'string', 'version2'=>'string'],
|
||||
'vfprintf' => ['int', 'stream'=>'resource', 'format'=>'string', 'values'=>'array'],
|
||||
'vfprintf' => ['int<0, max>', 'stream'=>'resource', 'format'=>'string', 'values'=>'array<string|int|float>'],
|
||||
'virtual' => ['bool', 'uri'=>'string'],
|
||||
'vpopmail_add_alias_domain' => ['bool', 'domain'=>'string', 'aliasdomain'=>'string'],
|
||||
'vpopmail_add_alias_domain_ex' => ['bool', 'olddomain'=>'string', 'newdomain'=>'string'],
|
||||
@@ -14383,8 +14396,8 @@ return [
|
||||
'vpopmail_error' => ['string'],
|
||||
'vpopmail_passwd' => ['bool', 'user'=>'string', 'domain'=>'string', 'password'=>'string', 'apop='=>'bool'],
|
||||
'vpopmail_set_user_quota' => ['bool', 'user'=>'string', 'domain'=>'string', 'quota'=>'string'],
|
||||
'vprintf' => ['int', 'format'=>'string', 'values'=>'array'],
|
||||
'vsprintf' => ['string', 'format'=>'string', 'values'=>'array'],
|
||||
'vprintf' => ['int<0, max>', 'format'=>'string', 'values'=>'array<string|int|float>'],
|
||||
'vsprintf' => ['string', 'format'=>'string', 'values'=>'array<string|int|float>'],
|
||||
'Vtiful\Kernel\Chart::__construct' => ['void', 'handle'=>'resource', 'type'=>'int'],
|
||||
'Vtiful\Kernel\Chart::axisNameX' => ['Vtiful\Kernel\Chart', 'name'=>'string'],
|
||||
'Vtiful\Kernel\Chart::axisNameY' => ['Vtiful\Kernel\Chart', 'name'=>'string'],
|
||||
@@ -14570,10 +14583,10 @@ return [
|
||||
'Worker::merge' => ['bool', 'from'=>'', 'overwrite='=>'mixed'],
|
||||
'Worker::notify' => ['bool'],
|
||||
'Worker::notifyOne' => ['bool'],
|
||||
'Worker::offsetExists' => ['bool', 'offset'=>'mixed'],
|
||||
'Worker::offsetGet' => ['mixed', 'offset'=>'mixed'],
|
||||
'Worker::offsetSet' => ['void', 'offset'=>'mixed', 'value'=>'mixed'],
|
||||
'Worker::offsetUnset' => ['void', 'offset'=>'mixed'],
|
||||
'Worker::offsetExists' => ['bool', 'offset'=>'int|string'],
|
||||
'Worker::offsetGet' => ['mixed', 'offset'=>'int|string'],
|
||||
'Worker::offsetSet' => ['void', 'offset'=>'int|string|null', 'value'=>'mixed'],
|
||||
'Worker::offsetUnset' => ['void', 'offset'=>'int|string'],
|
||||
'Worker::pop' => ['bool'],
|
||||
'Worker::run' => ['void'],
|
||||
'Worker::setGarbage' => ['void'],
|
||||
@@ -14903,10 +14916,10 @@ return [
|
||||
'Yaf\Config\Ini::get' => ['mixed', 'name='=>'mixed'],
|
||||
'Yaf\Config\Ini::key' => ['int|string'],
|
||||
'Yaf\Config\Ini::next' => ['void'],
|
||||
'Yaf\Config\Ini::offsetExists' => ['bool', 'name'=>'mixed'],
|
||||
'Yaf\Config\Ini::offsetGet' => ['mixed', 'name'=>'mixed'],
|
||||
'Yaf\Config\Ini::offsetSet' => ['void', 'name'=>'mixed', 'value'=>'mixed'],
|
||||
'Yaf\Config\Ini::offsetUnset' => ['void', 'name'=>'mixed'],
|
||||
'Yaf\Config\Ini::offsetExists' => ['bool', 'name'=>'int|string'],
|
||||
'Yaf\Config\Ini::offsetGet' => ['mixed', 'name'=>'int|string'],
|
||||
'Yaf\Config\Ini::offsetSet' => ['void', 'name'=>'int|string|null', 'value'=>'mixed'],
|
||||
'Yaf\Config\Ini::offsetUnset' => ['void', 'name'=>'int|string'],
|
||||
'Yaf\Config\Ini::readonly' => ['bool'],
|
||||
'Yaf\Config\Ini::rewind' => ['void'],
|
||||
'Yaf\Config\Ini::set' => ['Yaf\Config_Abstract', 'name'=>'string', 'value'=>'mixed'],
|
||||
@@ -14921,10 +14934,10 @@ return [
|
||||
'Yaf\Config\Simple::get' => ['mixed', 'name='=>'mixed'],
|
||||
'Yaf\Config\Simple::key' => ['int|string'],
|
||||
'Yaf\Config\Simple::next' => ['void'],
|
||||
'Yaf\Config\Simple::offsetExists' => ['bool', 'name'=>'mixed'],
|
||||
'Yaf\Config\Simple::offsetGet' => ['mixed', 'name'=>'mixed'],
|
||||
'Yaf\Config\Simple::offsetSet' => ['void', 'name'=>'mixed', 'value'=>'mixed'],
|
||||
'Yaf\Config\Simple::offsetUnset' => ['void', 'name'=>'mixed'],
|
||||
'Yaf\Config\Simple::offsetExists' => ['bool', 'name'=>'int|string'],
|
||||
'Yaf\Config\Simple::offsetGet' => ['mixed', 'name'=>'int|string'],
|
||||
'Yaf\Config\Simple::offsetSet' => ['void', 'name'=>'int|string|null', 'value'=>'mixed'],
|
||||
'Yaf\Config\Simple::offsetUnset' => ['void', 'name'=>'int|string'],
|
||||
'Yaf\Config\Simple::readonly' => ['bool'],
|
||||
'Yaf\Config\Simple::rewind' => ['void'],
|
||||
'Yaf\Config\Simple::set' => ['Yaf\Config_Abstract', 'name'=>'string', 'value'=>'mixed'],
|
||||
@@ -15191,10 +15204,10 @@ return [
|
||||
'Yaf\Session::has' => ['bool', 'name'=>'string'],
|
||||
'Yaf\Session::key' => ['int|string'],
|
||||
'Yaf\Session::next' => ['void'],
|
||||
'Yaf\Session::offsetExists' => ['bool', 'name'=>'mixed'],
|
||||
'Yaf\Session::offsetGet' => ['mixed', 'name'=>'mixed'],
|
||||
'Yaf\Session::offsetSet' => ['void', 'name'=>'mixed', 'value'=>'mixed'],
|
||||
'Yaf\Session::offsetUnset' => ['void', 'name'=>'mixed'],
|
||||
'Yaf\Session::offsetExists' => ['bool', 'name'=>'int|string'],
|
||||
'Yaf\Session::offsetGet' => ['mixed', 'name'=>'int|string'],
|
||||
'Yaf\Session::offsetSet' => ['void', 'name'=>'int|string|null', 'value'=>'mixed'],
|
||||
'Yaf\Session::offsetUnset' => ['void', 'name'=>'int|string'],
|
||||
'Yaf\Session::rewind' => ['void'],
|
||||
'Yaf\Session::set' => ['Yaf\Session|false', 'name'=>'string', 'value'=>'mixed'],
|
||||
'Yaf\Session::start' => ['Yaf\Session'],
|
||||
|
||||
116
vendor/vimeo/psalm/dictionaries/CallMap_83_delta.php
vendored
116
vendor/vimeo/psalm/dictionaries/CallMap_83_delta.php
vendored
@@ -25,8 +25,124 @@ return [
|
||||
'old' => ['array{runs:int,collected:int,threshold:int,roots:int}'],
|
||||
'new' => ['array{runs:int,collected:int,threshold:int,roots:int,running:bool,protected:bool,full:bool,buffer_size:int}'],
|
||||
],
|
||||
'srand' => [
|
||||
'old' => ['void', 'seed='=>'int', 'mode='=>'int'],
|
||||
'new' => ['void', 'seed='=>'?int', 'mode='=>'int'],
|
||||
],
|
||||
'mt_srand' => [
|
||||
'old' => ['void', 'seed='=>'int', 'mode='=>'int'],
|
||||
'new' =>['void', 'seed='=>'?int', 'mode='=>'int'],
|
||||
],
|
||||
'posix_getrlimit' => [
|
||||
'old' => ['array{"soft core": string, "hard core": string, "soft data": string, "hard data": string, "soft stack": integer, "hard stack": string, "soft totalmem": string, "hard totalmem": string, "soft rss": string, "hard rss": string, "soft maxproc": integer, "hard maxproc": integer, "soft memlock": integer, "hard memlock": integer, "soft cpu": string, "hard cpu": string, "soft filesize": string, "hard filesize": string, "soft openfiles": integer, "hard openfiles": integer}|false'],
|
||||
'new' => ['array{"soft core": string, "hard core": string, "soft data": string, "hard data": string, "soft stack": integer, "hard stack": string, "soft totalmem": string, "hard totalmem": string, "soft rss": string, "hard rss": string, "soft maxproc": integer, "hard maxproc": integer, "soft memlock": integer, "hard memlock": integer, "soft cpu": string, "hard cpu": string, "soft filesize": string, "hard filesize": string, "soft openfiles": integer, "hard openfiles": integer}|false', 'resource=' => '?int'],
|
||||
],
|
||||
'natcasesort' => [
|
||||
'old' => ['bool', '&rw_array'=>'array'],
|
||||
'new' => ['true', '&rw_array'=>'array'],
|
||||
],
|
||||
'natsort' => [
|
||||
'old' => ['bool', '&rw_array'=>'array'],
|
||||
'new' => ['true', '&rw_array'=>'array'],
|
||||
],
|
||||
'rsort' => [
|
||||
'old' => ['bool', '&rw_array'=>'array', 'flags='=>'int'],
|
||||
'new' => ['true', '&rw_array'=>'array', 'flags='=>'int'],
|
||||
],
|
||||
'hash_pbkdf2' => [
|
||||
'old' => ['non-empty-string', 'algo'=>'string', 'password'=>'string', 'salt'=>'string', 'iterations'=>'int', 'length='=>'int', 'binary='=>'bool'],
|
||||
'new' => ['non-empty-string', 'algo'=>'string', 'password'=>'string', 'salt'=>'string', 'iterations'=>'int', 'length='=>'int', 'binary='=>'bool', 'options=' => 'array'],
|
||||
],
|
||||
'imap_setflag_full' => [
|
||||
'old' => ['bool', 'imap'=>'IMAP\Connection', 'sequence'=>'string', 'flag'=>'string', 'options='=>'int'],
|
||||
'new' => ['true', 'imap'=>'IMAP\Connection', 'sequence'=>'string', 'flag'=>'string', 'options='=>'int'],
|
||||
],
|
||||
'imap_expunge' => [
|
||||
'old' => ['bool', 'imap'=>'IMAP\Connection'],
|
||||
'new' => ['true', 'imap'=>'IMAP\Connection'],
|
||||
],
|
||||
'imap_gc' => [
|
||||
'old' => ['bool', 'imap'=>'IMAP\Connection', 'flags'=>'int'],
|
||||
'new' => ['true', 'imap'=>'IMAP\Connection', 'flags'=>'int'],
|
||||
],
|
||||
'imap_undelete' => [
|
||||
'old' => ['bool', 'imap'=>'IMAP\Connection', 'message_nums'=>'string', 'flags='=>'int'],
|
||||
'new' => ['true', 'imap'=>'IMAP\Connection', 'message_nums'=>'string', 'flags='=>'int'],
|
||||
],
|
||||
'imap_delete' => [
|
||||
'old' => ['bool', 'imap'=>'IMAP\Connection', 'message_nums'=>'string', 'flags='=>'int'],
|
||||
'new' => ['true', 'imap'=>'IMAP\Connection', 'message_nums'=>'string', 'flags='=>'int'],
|
||||
],
|
||||
'imap_clearflag_full' => [
|
||||
'old' => ['bool', 'imap'=>'IMAP\Connection', 'sequence'=>'string', 'flag'=>'string', 'options='=>'int'],
|
||||
'new' => ['true', 'imap'=>'IMAP\Connection', 'sequence'=>'string', 'flag'=>'string', 'options='=>'int'],
|
||||
],
|
||||
'imap_close' => [
|
||||
'old' => ['bool', 'imap'=>'IMAP\Connection', 'flags='=>'int'],
|
||||
'new' => ['true', 'imap'=>'IMAP\Connection', 'flags='=>'int'],
|
||||
],
|
||||
'intlcal_clear' => [
|
||||
'old' => ['bool', 'calendar'=>'IntlCalendar', 'field='=>'?int'],
|
||||
'new' => ['true', 'calendar'=>'IntlCalendar', 'field='=>'?int'],
|
||||
],
|
||||
'intlcal_set_lenient' => [
|
||||
'old' => ['bool', 'calendar'=>'IntlCalendar', 'lenient'=>'bool'],
|
||||
'new' => ['true', 'calendar'=>'IntlCalendar', 'lenient'=>'bool'],
|
||||
],
|
||||
'intlcal_set_first_day_of_week' => [
|
||||
'old' => ['bool', 'calendar'=>'IntlCalendar', 'dayOfWeek'=>'int'],
|
||||
'new' => ['true', 'calendar'=>'IntlCalendar', 'dayOfWeek'=>'int'],
|
||||
],
|
||||
'datefmt_set_timezone' => [
|
||||
'old' => ['false|null', 'formatter'=>'IntlDateFormatter', 'timezone'=>'IntlTimeZone|DateTimeZone|string|null'],
|
||||
'new' => ['bool', 'formatter'=>'IntlDateFormatter', 'timezone'=>'IntlTimeZone|DateTimeZone|string|null'],
|
||||
],
|
||||
'IntlRuleBasedBreakIterator::setText' => [
|
||||
'old' => ['?bool', 'text'=>'string'],
|
||||
'new' => ['bool', 'text'=>'string'],
|
||||
],
|
||||
'IntlCodePointBreakIterator::setText' => [
|
||||
'old' => ['?bool', 'text'=>'string'],
|
||||
'new' => ['bool', 'text'=>'string'],
|
||||
],
|
||||
'IntlDateFormatter::setTimeZone' => [
|
||||
'old' => ['null|false', 'timezone'=>'IntlTimeZone|DateTimeZone|string|null'],
|
||||
'new' => ['bool', 'timezone'=>'IntlTimeZone|DateTimeZone|string|null'],
|
||||
],
|
||||
'IntlChar::enumCharNames' => [
|
||||
'old' => ['?bool', 'start'=>'string|int', 'end'=>'string|int', 'callback'=>'callable(int,int,int):void', 'type='=>'int'],
|
||||
'new' => ['bool', 'start'=>'string|int', 'end'=>'string|int', 'callback'=>'callable(int,int,int):void', 'type='=>'int'],
|
||||
],
|
||||
'IntlBreakIterator::setText' => [
|
||||
'old' => ['?bool', 'text'=>'string'],
|
||||
'new' => ['bool', 'text'=>'string'],
|
||||
],
|
||||
],
|
||||
|
||||
'removed' => [
|
||||
'OutOfBoundsException::__clone' => ['void'],
|
||||
'ArgumentCountError::__clone' => ['void'],
|
||||
'ArithmeticError::__clone' => ['void'],
|
||||
'BadFunctionCallException::__clone' => ['void'],
|
||||
'BadMethodCallException::__clone' => ['void'],
|
||||
'ClosedGeneratorException::__clone' => ['void'],
|
||||
'DomainException::__clone' => ['void'],
|
||||
'ErrorException::__clone' => ['void'],
|
||||
'IntlException::__clone' => ['void'],
|
||||
'InvalidArgumentException::__clone' => ['void'],
|
||||
'JsonException::__clone' => ['void'],
|
||||
'LengthException::__clone' => ['void'],
|
||||
'LogicException::__clone' => ['void'],
|
||||
'OutOfRangeException::__clone' => ['void'],
|
||||
'OverflowException::__clone' => ['void'],
|
||||
'ParseError::__clone' => ['void'],
|
||||
'RangeException::__clone' => ['void'],
|
||||
'ReflectionNamedType::__clone' => ['void'],
|
||||
'ReflectionObject::__clone' => ['void'],
|
||||
'RuntimeException::__clone' => ['void'],
|
||||
'TypeError::__clone' => ['void'],
|
||||
'UnderflowException::__clone' => ['void'],
|
||||
'UnexpectedValueException::__clone' => ['void'],
|
||||
'IntlCodePointBreakIterator::__construct' => ['void'],
|
||||
],
|
||||
];
|
||||
|
||||
@@ -209,10 +209,10 @@ return [
|
||||
'ArithmeticError::getPrevious' => ['?Throwable'],
|
||||
'ArithmeticError::getTrace' => ['list<array{file?:string,line?:int,function:string,class?:class-string,type?:\'::\'|\'->\',args?:array<mixed>}>'],
|
||||
'ArithmeticError::getTraceAsString' => ['string'],
|
||||
'ArrayAccess::offsetExists' => ['bool', 'offset'=>'mixed'],
|
||||
'ArrayAccess::offsetGet' => ['mixed', 'offset'=>'mixed'],
|
||||
'ArrayAccess::offsetSet' => ['void', 'offset'=>'mixed', 'value'=>'mixed'],
|
||||
'ArrayAccess::offsetUnset' => ['void', 'offset'=>'mixed'],
|
||||
'ArrayAccess::offsetExists' => ['bool', 'offset'=>'int|string'],
|
||||
'ArrayAccess::offsetGet' => ['mixed', 'offset'=>'int|string'],
|
||||
'ArrayAccess::offsetSet' => ['void', 'offset'=>'int|string|null', 'value'=>'mixed'],
|
||||
'ArrayAccess::offsetUnset' => ['void', 'offset'=>'int|string'],
|
||||
'ArrayIterator::__construct' => ['void', 'array='=>'array|object', 'flags='=>'int'],
|
||||
'ArrayIterator::append' => ['void', 'value'=>'mixed'],
|
||||
'ArrayIterator::asort' => ['true', 'flags='=>'int'],
|
||||
@@ -227,7 +227,7 @@ return [
|
||||
'ArrayIterator::next' => ['void'],
|
||||
'ArrayIterator::offsetExists' => ['bool', 'key'=>'string|int'],
|
||||
'ArrayIterator::offsetGet' => ['mixed', 'key'=>'string|int'],
|
||||
'ArrayIterator::offsetSet' => ['void', 'key'=>'string|int', 'value'=>'mixed'],
|
||||
'ArrayIterator::offsetSet' => ['void', 'key'=>'string|int|null', 'value'=>'mixed'],
|
||||
'ArrayIterator::offsetUnset' => ['void', 'key'=>'string|int'],
|
||||
'ArrayIterator::rewind' => ['void'],
|
||||
'ArrayIterator::seek' => ['void', 'offset'=>'int'],
|
||||
@@ -251,7 +251,7 @@ return [
|
||||
'ArrayObject::natsort' => ['true'],
|
||||
'ArrayObject::offsetExists' => ['bool', 'key'=>'int|string'],
|
||||
'ArrayObject::offsetGet' => ['mixed|null', 'key'=>'int|string'],
|
||||
'ArrayObject::offsetSet' => ['void', 'key'=>'int|string', 'value'=>'mixed'],
|
||||
'ArrayObject::offsetSet' => ['void', 'key'=>'int|string|null', 'value'=>'mixed'],
|
||||
'ArrayObject::offsetUnset' => ['void', 'key'=>'int|string'],
|
||||
'ArrayObject::serialize' => ['string'],
|
||||
'ArrayObject::setFlags' => ['void', 'flags'=>'int'],
|
||||
@@ -2236,10 +2236,10 @@ return [
|
||||
'HttpQueryString::getObject' => ['', 'name'=>'', 'defval'=>'', 'delete'=>''],
|
||||
'HttpQueryString::getString' => ['', 'name'=>'', 'defval'=>'', 'delete'=>''],
|
||||
'HttpQueryString::mod' => ['HttpQueryString', 'params'=>'mixed'],
|
||||
'HttpQueryString::offsetExists' => ['bool', 'offset'=>'mixed'],
|
||||
'HttpQueryString::offsetGet' => ['mixed', 'offset'=>'mixed'],
|
||||
'HttpQueryString::offsetSet' => ['void', 'offset'=>'mixed', 'value'=>'mixed'],
|
||||
'HttpQueryString::offsetUnset' => ['void', 'offset'=>'mixed'],
|
||||
'HttpQueryString::offsetExists' => ['bool', 'offset'=>'int|string'],
|
||||
'HttpQueryString::offsetGet' => ['mixed', 'offset'=>'int|string'],
|
||||
'HttpQueryString::offsetSet' => ['void', 'offset'=>'int|string|null', 'value'=>'mixed'],
|
||||
'HttpQueryString::offsetUnset' => ['void', 'offset'=>'int|string'],
|
||||
'HttpQueryString::serialize' => ['string'],
|
||||
'HttpQueryString::set' => ['string', 'params'=>'mixed'],
|
||||
'HttpQueryString::singleton' => ['HttpQueryString', 'global='=>'bool'],
|
||||
@@ -3289,10 +3289,10 @@ return [
|
||||
'Judy::memoryUsage' => ['int'],
|
||||
'Judy::next' => ['mixed', 'index'=>'mixed'],
|
||||
'Judy::nextEmpty' => ['mixed', 'index'=>'mixed'],
|
||||
'Judy::offsetExists' => ['bool', 'offset'=>'mixed'],
|
||||
'Judy::offsetGet' => ['mixed', 'offset'=>'mixed'],
|
||||
'Judy::offsetSet' => ['bool', 'offset'=>'mixed', 'value'=>'mixed'],
|
||||
'Judy::offsetUnset' => ['bool', 'offset'=>'mixed'],
|
||||
'Judy::offsetExists' => ['bool', 'offset'=>'int|string'],
|
||||
'Judy::offsetGet' => ['mixed', 'offset'=>'int|string'],
|
||||
'Judy::offsetSet' => ['bool', 'offset'=>'int|string|null', 'value'=>'mixed'],
|
||||
'Judy::offsetUnset' => ['bool', 'offset'=>'int|string'],
|
||||
'Judy::prev' => ['mixed', 'index'=>'mixed'],
|
||||
'Judy::prevEmpty' => ['mixed', 'index'=>'mixed'],
|
||||
'Judy::size' => ['int'],
|
||||
@@ -3731,6 +3731,14 @@ return [
|
||||
'MongoDBRef::create' => ['array', 'collection'=>'string', 'id'=>'mixed', 'database='=>'string'],
|
||||
'MongoDBRef::get' => ['?array', 'db'=>'MongoDB', 'ref'=>'array'],
|
||||
'MongoDBRef::isRef' => ['bool', 'ref'=>'mixed'],
|
||||
'MongoDB\BSON\fromJSON' => ['string', 'json' => 'string'],
|
||||
'MongoDB\BSON\fromPHP' => ['string', 'value' => 'object|array'],
|
||||
'MongoDB\BSON\toCanonicalExtendedJSON' => ['string', 'bson' => 'string'],
|
||||
'MongoDB\BSON\toJSON' => ['string', 'bson' => 'string'],
|
||||
'MongoDB\BSON\toPHP' => ['object|array', 'bson' => 'string', 'typemap=' => '?array'],
|
||||
'MongoDB\BSON\toRelaxedExtendedJSON' => ['string', 'bson' => 'string'],
|
||||
'MongoDB\Driver\Monitoring\addSubscriber' => ['void', 'subscriber' => 'MongoDB\Driver\Monitoring\Subscriber'],
|
||||
'MongoDB\Driver\Monitoring\removeSubscriber' => ['void', 'subscriber' => 'MongoDB\Driver\Monitoring\Subscriber'],
|
||||
'MongoDB\BSON\Binary::__construct' => ['void', 'data' => 'string', 'type=' => 'int'],
|
||||
'MongoDB\BSON\Binary::getData' => ['string'],
|
||||
'MongoDB\BSON\Binary::getType' => ['int'],
|
||||
@@ -3751,10 +3759,28 @@ return [
|
||||
'MongoDB\BSON\Decimal128::unserialize' => ['void', 'serialized' => 'string'],
|
||||
'MongoDB\BSON\Decimal128::jsonSerialize' => ['mixed'],
|
||||
'MongoDB\BSON\Decimal128Interface::__toString' => ['string'],
|
||||
'MongoDB\BSON\Document::fromBSON' => ['MongoDB\BSON\Document', 'bson' => 'string'],
|
||||
'MongoDB\BSON\Document::fromJSON' => ['MongoDB\BSON\Document', 'json' => 'string'],
|
||||
'MongoDB\BSON\Document::fromPHP' => ['MongoDB\BSON\Document', 'value' => 'object|array'],
|
||||
'MongoDB\BSON\Document::get' => ['mixed', 'key' => 'string'],
|
||||
'MongoDB\BSON\Document::getIterator' => ['MongoDB\BSON\Iterator'],
|
||||
'MongoDB\BSON\Document::has' => ['bool', 'key' => 'string'],
|
||||
'MongoDB\BSON\Document::toPHP' => ['object|array', 'typeMap=' => '?array'],
|
||||
'MongoDB\BSON\Document::toCanonicalExtendedJSON' => ['string'],
|
||||
'MongoDB\BSON\Document::toRelaxedExtendedJSON' => ['string'],
|
||||
'MongoDB\BSON\Document::__toString' => ['string'],
|
||||
'MongoDB\BSON\Document::serialize' => ['string'],
|
||||
'MongoDB\BSON\Document::unserialize' => ['void', 'serialized' => 'string'],
|
||||
'MongoDB\BSON\Int64::__construct' => ['void', 'value' => 'string|int'],
|
||||
'MongoDB\BSON\Int64::__toString' => ['string'],
|
||||
'MongoDB\BSON\Int64::serialize' => ['string'],
|
||||
'MongoDB\BSON\Int64::unserialize' => ['void', 'serialized' => 'string'],
|
||||
'MongoDB\BSON\Int64::jsonSerialize' => ['mixed'],
|
||||
'MongoDB\BSON\Iterator::current' => ['mixed'],
|
||||
'MongoDB\BSON\Iterator::key' => ['string|int'],
|
||||
'MongoDB\BSON\Iterator::next' => ['void'],
|
||||
'MongoDB\BSON\Iterator::rewind' => ['void'],
|
||||
'MongoDB\BSON\Iterator::valid' => ['bool'],
|
||||
'MongoDB\BSON\Javascript::__construct' => ['void', 'code' => 'string', 'scope=' => 'object|array|null'],
|
||||
'MongoDB\BSON\Javascript::getCode' => ['string'],
|
||||
'MongoDB\BSON\Javascript::getScope' => ['?object'],
|
||||
@@ -3779,6 +3805,14 @@ return [
|
||||
'MongoDB\BSON\ObjectId::jsonSerialize' => ['mixed'],
|
||||
'MongoDB\BSON\ObjectIdInterface::getTimestamp' => ['int'],
|
||||
'MongoDB\BSON\ObjectIdInterface::__toString' => ['string'],
|
||||
'MongoDB\BSON\PackedArray::fromPHP' => ['MongoDB\BSON\PackedArray', 'value' => 'array'],
|
||||
'MongoDB\BSON\PackedArray::get' => ['mixed', 'index' => 'int'],
|
||||
'MongoDB\BSON\PackedArray::getIterator' => ['MongoDB\BSON\Iterator'],
|
||||
'MongoDB\BSON\PackedArray::has' => ['bool', 'index' => 'int'],
|
||||
'MongoDB\BSON\PackedArray::toPHP' => ['object|array', 'typeMap=' => '?array'],
|
||||
'MongoDB\BSON\PackedArray::__toString' => ['string'],
|
||||
'MongoDB\BSON\PackedArray::serialize' => ['string'],
|
||||
'MongoDB\BSON\PackedArray::unserialize' => ['void', 'serialized' => 'string'],
|
||||
'MongoDB\BSON\Regex::__construct' => ['void', 'pattern' => 'string', 'flags=' => 'string'],
|
||||
'MongoDB\BSON\Regex::getPattern' => ['string'],
|
||||
'MongoDB\BSON\Regex::getFlags' => ['string'],
|
||||
@@ -3828,6 +3862,7 @@ return [
|
||||
'MongoDB\Driver\ClientEncryption::decrypt' => ['mixed', 'value' => 'MongoDB\BSON\Binary'],
|
||||
'MongoDB\Driver\ClientEncryption::deleteKey' => ['object', 'keyId' => 'MongoDB\BSON\Binary'],
|
||||
'MongoDB\Driver\ClientEncryption::encrypt' => ['MongoDB\BSON\Binary', 'value' => 'mixed', 'options=' => '?array'],
|
||||
'MongoDB\Driver\ClientEncryption::encryptExpression' => ['object', 'expr' => 'object|array', 'options=' => '?array'],
|
||||
'MongoDB\Driver\ClientEncryption::getKey' => ['?object', 'keyId' => 'MongoDB\BSON\Binary'],
|
||||
'MongoDB\Driver\ClientEncryption::getKeyByAltName' => ['?object', 'keyAltName' => 'string'],
|
||||
'MongoDB\Driver\ClientEncryption::getKeys' => ['MongoDB\Driver\Cursor'],
|
||||
@@ -4039,6 +4074,7 @@ return [
|
||||
'MongoDB\Driver\WriteResult::getUpsertedIds' => ['array'],
|
||||
'MongoDB\Driver\WriteResult::getWriteConcernError' => ['?MongoDB\Driver\WriteConcernError'],
|
||||
'MongoDB\Driver\WriteResult::getWriteErrors' => ['array'],
|
||||
'MongoDB\Driver\WriteResult::getErrorReplies' => ['array'],
|
||||
'MongoDB\Driver\WriteResult::isAcknowledged' => ['bool'],
|
||||
'MongoDate::__construct' => ['void', 'second='=>'int', 'usecond='=>'int'],
|
||||
'MongoDate::__toString' => ['string'],
|
||||
@@ -5137,7 +5173,7 @@ return [
|
||||
'RecursiveArrayIterator::next' => ['void'],
|
||||
'RecursiveArrayIterator::offsetExists' => ['bool', 'key'=>'string|int'],
|
||||
'RecursiveArrayIterator::offsetGet' => ['mixed', 'key'=>'string|int'],
|
||||
'RecursiveArrayIterator::offsetSet' => ['void', 'key'=>'string|int', 'value'=>'string'],
|
||||
'RecursiveArrayIterator::offsetSet' => ['void', 'key'=>'string|int|null', 'value'=>'string'],
|
||||
'RecursiveArrayIterator::offsetUnset' => ['void', 'key'=>'string|int'],
|
||||
'RecursiveArrayIterator::rewind' => ['void'],
|
||||
'RecursiveArrayIterator::seek' => ['void', 'offset'=>'int'],
|
||||
@@ -6641,7 +6677,7 @@ return [
|
||||
'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::offsetSet' => ['void', 'offset'=>'int|string|null', 'value'=>'mixed'],
|
||||
'SimpleXMLElement::offsetUnset' => ['void', 'offset'=>'int|string'],
|
||||
'SimpleXMLElement::registerXPathNamespace' => ['bool', 'prefix'=>'string', 'namespace'=>'string'],
|
||||
'SimpleXMLElement::saveXML' => ['string|bool', 'filename='=>'string'],
|
||||
@@ -7871,10 +7907,10 @@ return [
|
||||
'Thread::merge' => ['bool', 'from'=>'', 'overwrite='=>'mixed'],
|
||||
'Thread::notify' => ['bool'],
|
||||
'Thread::notifyOne' => ['bool'],
|
||||
'Thread::offsetExists' => ['bool', 'offset'=>'mixed'],
|
||||
'Thread::offsetGet' => ['mixed', 'offset'=>'mixed'],
|
||||
'Thread::offsetSet' => ['void', 'offset'=>'mixed', 'value'=>'mixed'],
|
||||
'Thread::offsetUnset' => ['void', 'offset'=>'mixed'],
|
||||
'Thread::offsetExists' => ['bool', 'offset'=>'int|string'],
|
||||
'Thread::offsetGet' => ['mixed', 'offset'=>'int|string'],
|
||||
'Thread::offsetSet' => ['void', 'offset'=>'int|string|null', 'value'=>'mixed'],
|
||||
'Thread::offsetUnset' => ['void', 'offset'=>'int|string'],
|
||||
'Thread::pop' => ['bool'],
|
||||
'Thread::run' => ['void'],
|
||||
'Thread::setGarbage' => ['void'],
|
||||
@@ -7900,10 +7936,10 @@ return [
|
||||
'Threaded::merge' => ['bool', 'from'=>'mixed', 'overwrite='=>'bool'],
|
||||
'Threaded::notify' => ['bool'],
|
||||
'Threaded::notifyOne' => ['bool'],
|
||||
'Threaded::offsetExists' => ['bool', 'offset'=>'mixed'],
|
||||
'Threaded::offsetGet' => ['mixed', 'offset'=>'mixed'],
|
||||
'Threaded::offsetSet' => ['void', 'offset'=>'mixed', 'value'=>'mixed'],
|
||||
'Threaded::offsetUnset' => ['void', 'offset'=>'mixed'],
|
||||
'Threaded::offsetExists' => ['bool', 'offset'=>'int|string'],
|
||||
'Threaded::offsetGet' => ['mixed', 'offset'=>'int|string'],
|
||||
'Threaded::offsetSet' => ['void', 'offset'=>'int|string|null', 'value'=>'mixed'],
|
||||
'Threaded::offsetUnset' => ['void', 'offset'=>'int|string'],
|
||||
'Threaded::pop' => ['bool'],
|
||||
'Threaded::run' => ['void'],
|
||||
'Threaded::setGarbage' => ['void'],
|
||||
@@ -8208,10 +8244,10 @@ return [
|
||||
'Worker::merge' => ['bool', 'from'=>'', 'overwrite='=>'mixed'],
|
||||
'Worker::notify' => ['bool'],
|
||||
'Worker::notifyOne' => ['bool'],
|
||||
'Worker::offsetExists' => ['bool', 'offset'=>'mixed'],
|
||||
'Worker::offsetGet' => ['mixed', 'offset'=>'mixed'],
|
||||
'Worker::offsetSet' => ['void', 'offset'=>'mixed', 'value'=>'mixed'],
|
||||
'Worker::offsetUnset' => ['void', 'offset'=>'mixed'],
|
||||
'Worker::offsetExists' => ['bool', 'offset'=>'int|string'],
|
||||
'Worker::offsetGet' => ['mixed', 'offset'=>'int|string'],
|
||||
'Worker::offsetSet' => ['void', 'offset'=>'int|string|null', 'value'=>'mixed'],
|
||||
'Worker::offsetUnset' => ['void', 'offset'=>'int|string'],
|
||||
'Worker::pop' => ['bool'],
|
||||
'Worker::run' => ['void'],
|
||||
'Worker::setGarbage' => ['void'],
|
||||
@@ -8368,10 +8404,10 @@ return [
|
||||
'Yaf\Config\Ini::get' => ['mixed', 'name='=>'mixed'],
|
||||
'Yaf\Config\Ini::key' => ['int|string'],
|
||||
'Yaf\Config\Ini::next' => ['void'],
|
||||
'Yaf\Config\Ini::offsetExists' => ['bool', 'name'=>'mixed'],
|
||||
'Yaf\Config\Ini::offsetGet' => ['mixed', 'name'=>'mixed'],
|
||||
'Yaf\Config\Ini::offsetSet' => ['void', 'name'=>'mixed', 'value'=>'mixed'],
|
||||
'Yaf\Config\Ini::offsetUnset' => ['void', 'name'=>'mixed'],
|
||||
'Yaf\Config\Ini::offsetExists' => ['bool', 'name'=>'int|string'],
|
||||
'Yaf\Config\Ini::offsetGet' => ['mixed', 'name'=>'int|string'],
|
||||
'Yaf\Config\Ini::offsetSet' => ['void', 'name'=>'int|string|null', 'value'=>'mixed'],
|
||||
'Yaf\Config\Ini::offsetUnset' => ['void', 'name'=>'int|string'],
|
||||
'Yaf\Config\Ini::readonly' => ['bool'],
|
||||
'Yaf\Config\Ini::rewind' => ['void'],
|
||||
'Yaf\Config\Ini::set' => ['Yaf\Config_Abstract', 'name'=>'string', 'value'=>'mixed'],
|
||||
@@ -8386,10 +8422,10 @@ return [
|
||||
'Yaf\Config\Simple::get' => ['mixed', 'name='=>'mixed'],
|
||||
'Yaf\Config\Simple::key' => ['int|string'],
|
||||
'Yaf\Config\Simple::next' => ['void'],
|
||||
'Yaf\Config\Simple::offsetExists' => ['bool', 'name'=>'mixed'],
|
||||
'Yaf\Config\Simple::offsetGet' => ['mixed', 'name'=>'mixed'],
|
||||
'Yaf\Config\Simple::offsetSet' => ['void', 'name'=>'mixed', 'value'=>'mixed'],
|
||||
'Yaf\Config\Simple::offsetUnset' => ['void', 'name'=>'mixed'],
|
||||
'Yaf\Config\Simple::offsetExists' => ['bool', 'name'=>'int|string'],
|
||||
'Yaf\Config\Simple::offsetGet' => ['mixed', 'name'=>'int|string'],
|
||||
'Yaf\Config\Simple::offsetSet' => ['void', 'name'=>'int|string|null', 'value'=>'mixed'],
|
||||
'Yaf\Config\Simple::offsetUnset' => ['void', 'name'=>'int|string'],
|
||||
'Yaf\Config\Simple::readonly' => ['bool'],
|
||||
'Yaf\Config\Simple::rewind' => ['void'],
|
||||
'Yaf\Config\Simple::set' => ['Yaf\Config_Abstract', 'name'=>'string', 'value'=>'mixed'],
|
||||
@@ -8656,10 +8692,10 @@ return [
|
||||
'Yaf\Session::has' => ['bool', 'name'=>'string'],
|
||||
'Yaf\Session::key' => ['int|string'],
|
||||
'Yaf\Session::next' => ['void'],
|
||||
'Yaf\Session::offsetExists' => ['bool', 'name'=>'mixed'],
|
||||
'Yaf\Session::offsetGet' => ['mixed', 'name'=>'mixed'],
|
||||
'Yaf\Session::offsetSet' => ['void', 'name'=>'mixed', 'value'=>'mixed'],
|
||||
'Yaf\Session::offsetUnset' => ['void', 'name'=>'mixed'],
|
||||
'Yaf\Session::offsetExists' => ['bool', 'name'=>'int|string'],
|
||||
'Yaf\Session::offsetGet' => ['mixed', 'name'=>'int|string'],
|
||||
'Yaf\Session::offsetSet' => ['void', 'name'=>'int|string|null', 'value'=>'mixed'],
|
||||
'Yaf\Session::offsetUnset' => ['void', 'name'=>'int|string'],
|
||||
'Yaf\Session::rewind' => ['void'],
|
||||
'Yaf\Session::set' => ['Yaf\Session|false', 'name'=>'string', 'value'=>'mixed'],
|
||||
'Yaf\Session::start' => ['Yaf\Session'],
|
||||
@@ -9404,7 +9440,8 @@ return [
|
||||
'atan' => ['float', 'num'=>'float'],
|
||||
'atan2' => ['float', 'y'=>'float', 'x'=>'float'],
|
||||
'atanh' => ['float', 'num'=>'float'],
|
||||
'base64_decode' => ['string|false', 'string'=>'string', 'strict='=>'bool'],
|
||||
'base64_decode' => ['string', 'string'=>'string', 'strict='=>'false'],
|
||||
'base64_decode\'1' => ['string|false', 'string'=>'string', 'strict='=>'true'],
|
||||
'base64_encode' => ['string', 'string'=>'string'],
|
||||
'base_convert' => ['string', 'num'=>'string', 'from_base'=>'int', 'to_base'=>'int'],
|
||||
'basename' => ['string', 'path'=>'string', 'suffix='=>'string'],
|
||||
@@ -9966,7 +10003,7 @@ return [
|
||||
'dcgettext' => ['string', 'domain'=>'string', 'message'=>'string', 'category'=>'int'],
|
||||
'dcngettext' => ['string', 'domain'=>'string', 'singular'=>'string', 'plural'=>'string', 'count'=>'int', 'category'=>'int'],
|
||||
'deaggregate' => ['', 'object'=>'object', 'class_name='=>'string'],
|
||||
'debug_backtrace' => ['list<array{file:string,line:int,function:string,class?:class-string,object?:object,type?:string,args?:list}>', 'options='=>'int', 'limit='=>'int'],
|
||||
'debug_backtrace' => ['list<array{file?:string,line?:int,function:string,class?:class-string,object?:object,type?:string,args?:list}>', 'options='=>'int', 'limit='=>'int'],
|
||||
'debug_print_backtrace' => ['void', 'options='=>'int', 'limit='=>'int'],
|
||||
'debug_zval_dump' => ['void', 'value'=>'mixed', '...values='=>'mixed'],
|
||||
'debugger_connect' => [''],
|
||||
@@ -9994,7 +10031,7 @@ return [
|
||||
'dio_truncate' => ['bool', 'fd'=>'resource', 'offset'=>'int'],
|
||||
'dio_write' => ['int', 'fd'=>'resource', 'data'=>'string', 'length='=>'int'],
|
||||
'dir' => ['Directory|false', 'directory'=>'string', 'context='=>'resource'],
|
||||
'dirname' => ['string', 'path'=>'string', 'levels='=>'int'],
|
||||
'dirname' => ['string', 'path'=>'string', 'levels='=>'int<1, max>'],
|
||||
'disk_free_space' => ['float|false', 'directory'=>'string'],
|
||||
'disk_total_space' => ['float|false', 'directory'=>'string'],
|
||||
'diskfreespace' => ['float|false', 'directory'=>'string'],
|
||||
@@ -10708,8 +10745,8 @@ return [
|
||||
'gethostbyname' => ['string', 'hostname'=>'string'],
|
||||
'gethostbynamel' => ['list<string>|false', 'hostname'=>'string'],
|
||||
'gethostname' => ['string|false'],
|
||||
'getimagesize' => ['array|false', 'filename'=>'string', '&w_image_info='=>'array'],
|
||||
'getimagesizefromstring' => ['array|false', 'string'=>'string', '&w_image_info='=>'array'],
|
||||
'getimagesize' => ['array{0:int, 1: int, 2: int, 3: string, mime: string, channels?: 3|4, bits?: int}|false', 'filename'=>'string', '&w_image_info='=>'array'],
|
||||
'getimagesizefromstring' => ['array{0:int, 1: int, 2: int, 3: string, mime: string, channels?: 3|4, bits?: int}|false', 'string'=>'string', '&w_image_info='=>'array'],
|
||||
'getlastmod' => ['int|false'],
|
||||
'getmxrr' => ['bool', 'hostname'=>'string', '&w_hosts'=>'array', '&w_weights='=>'array'],
|
||||
'getmygid' => ['int|false'],
|
||||
@@ -10727,7 +10764,7 @@ return [
|
||||
'gettimeofday' => ['array<string, int>'],
|
||||
'gettimeofday\'1' => ['float', 'as_float='=>'true'],
|
||||
'gettype' => ['string', 'value'=>'mixed'],
|
||||
'glob' => ['list<non-empty-string>|false', 'pattern'=>'non-empty-string', 'flags='=>'int<1, max>'],
|
||||
'glob' => ['false|list{0?:string, ...<non-empty-string>}', 'pattern'=>'string', 'flags='=>'int<0, 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'],
|
||||
@@ -11306,10 +11343,10 @@ return [
|
||||
'http\Message\Parser::stream' => ['int', 'stream'=>'resource', 'flags'=>'int', '&message'=>'http\Message'],
|
||||
'http\Params::__construct' => ['void', 'params='=>'mixed', 'param_sep='=>'mixed', 'arg_sep='=>'mixed', 'val_sep='=>'mixed', 'flags='=>'mixed'],
|
||||
'http\Params::__toString' => ['string'],
|
||||
'http\Params::offsetExists' => ['bool', 'name'=>'mixed'],
|
||||
'http\Params::offsetGet' => ['mixed', 'name'=>'mixed'],
|
||||
'http\Params::offsetSet' => ['void', 'name'=>'mixed', 'value'=>'mixed'],
|
||||
'http\Params::offsetUnset' => ['void', 'name'=>'mixed'],
|
||||
'http\Params::offsetExists' => ['bool', 'name'=>'int|string'],
|
||||
'http\Params::offsetGet' => ['mixed', 'name'=>'int|string'],
|
||||
'http\Params::offsetSet' => ['void', 'name'=>'int|string|null', 'value'=>'mixed'],
|
||||
'http\Params::offsetUnset' => ['void', 'name'=>'int|string'],
|
||||
'http\Params::toArray' => ['array'],
|
||||
'http\Params::toString' => ['string'],
|
||||
'http\QueryString::__construct' => ['void', 'querystring'=>'string'],
|
||||
@@ -11324,10 +11361,10 @@ return [
|
||||
'http\QueryString::getObject' => ['object|mixed', 'name'=>'string', 'defval='=>'mixed', 'delete='=>'bool|false'],
|
||||
'http\QueryString::getString' => ['string|mixed', 'name'=>'string', 'defval='=>'mixed', 'delete='=>'bool|false'],
|
||||
'http\QueryString::mod' => ['http\QueryString', 'params='=>'mixed'],
|
||||
'http\QueryString::offsetExists' => ['bool', 'offset'=>'mixed'],
|
||||
'http\QueryString::offsetGet' => ['mixed|null', 'offset'=>'mixed'],
|
||||
'http\QueryString::offsetSet' => ['void', 'offset'=>'mixed', 'value'=>'mixed'],
|
||||
'http\QueryString::offsetUnset' => ['void', 'offset'=>'mixed'],
|
||||
'http\QueryString::offsetExists' => ['bool', 'offset'=>'int|string'],
|
||||
'http\QueryString::offsetGet' => ['mixed|null', 'offset'=>'int|string'],
|
||||
'http\QueryString::offsetSet' => ['void', 'offset'=>'int|string|null', 'value'=>'mixed'],
|
||||
'http\QueryString::offsetUnset' => ['void', 'offset'=>'int|string'],
|
||||
'http\QueryString::serialize' => ['string'],
|
||||
'http\QueryString::set' => ['http\QueryString', 'params'=>'mixed'],
|
||||
'http\QueryString::toArray' => ['array'],
|
||||
@@ -13554,7 +13591,7 @@ return [
|
||||
'print' => ['int', 'arg'=>'string'],
|
||||
'print_r' => ['string', 'value'=>'mixed'],
|
||||
'print_r\'1' => ['true', 'value'=>'mixed', 'return='=>'bool'],
|
||||
'printf' => ['int', 'format'=>'string', '...values='=>'string|int|float'],
|
||||
'printf' => ['int<0, max>', 'format'=>'string', '...values='=>'string|int|float'],
|
||||
'proc_close' => ['int', 'process'=>'resource'],
|
||||
'proc_get_status' => ['array{command: string, pid: int, running: bool, signaled: bool, stopped: bool, exitcode: int, termsig: int, stopsig: int}|false', 'process'=>'resource'],
|
||||
'proc_nice' => ['bool', 'priority'=>'int'],
|
||||
@@ -15317,7 +15354,7 @@ return [
|
||||
'variant_xor' => ['mixed', 'left'=>'mixed', 'right'=>'mixed'],
|
||||
'version_compare' => ['bool', 'version1'=>'string', 'version2'=>'string', 'operator'=>'\'<\'|\'lt\'|\'<=\'|\'le\'|\'>\'|\'gt\'|\'>=\'|\'ge\'|\'==\'|\'=\'|\'eq\'|\'!=\'|\'<>\'|\'ne\''],
|
||||
'version_compare\'1' => ['int', 'version1'=>'string', 'version2'=>'string'],
|
||||
'vfprintf' => ['int', 'stream'=>'resource', 'format'=>'string', 'values'=>'array'],
|
||||
'vfprintf' => ['int<0, max>', 'stream'=>'resource', 'format'=>'string', 'values'=>'array<string|int|float>'],
|
||||
'virtual' => ['bool', 'uri'=>'string'],
|
||||
'vpopmail_add_alias_domain' => ['bool', 'domain'=>'string', 'aliasdomain'=>'string'],
|
||||
'vpopmail_add_alias_domain_ex' => ['bool', 'olddomain'=>'string', 'newdomain'=>'string'],
|
||||
@@ -15336,8 +15373,8 @@ return [
|
||||
'vpopmail_error' => ['string'],
|
||||
'vpopmail_passwd' => ['bool', 'user'=>'string', 'domain'=>'string', 'password'=>'string', 'apop='=>'bool'],
|
||||
'vpopmail_set_user_quota' => ['bool', 'user'=>'string', 'domain'=>'string', 'quota'=>'string'],
|
||||
'vprintf' => ['int', 'format'=>'string', 'values'=>'array'],
|
||||
'vsprintf' => ['string', 'format'=>'string', 'values'=>'array'],
|
||||
'vprintf' => ['int<0, max>', 'format'=>'string', 'values'=>'array<string|int|float>'],
|
||||
'vsprintf' => ['string', 'format'=>'string', 'values'=>'array<string|int|float>'],
|
||||
'w32api_deftype' => ['bool', 'typename'=>'string', 'member1_type'=>'string', 'member1_name'=>'string', '...args='=>'string'],
|
||||
'w32api_init_dtype' => ['resource', 'typename'=>'string', 'value'=>'', '...args='=>''],
|
||||
'w32api_invoke_function' => ['', 'funcname'=>'string', 'argument'=>'', '...args='=>''],
|
||||
|
||||
@@ -126,6 +126,19 @@ Optional keys can be denoted by a trailing `?`, e.g.:
|
||||
/** @return array{optional?: string, bar: int} */
|
||||
```
|
||||
|
||||
You can use "one-line" comments (similar to PHP), e.g:
|
||||
|
||||
```php
|
||||
/** @return array { // Array with comments.
|
||||
* // Comments can be placed on their own line.
|
||||
* foo: string, // An array key description.
|
||||
* bar: array {, // Another array key description.
|
||||
* 'foo//bar': string, // Array key with "//" in it's name.
|
||||
* },
|
||||
* }
|
||||
*/
|
||||
```
|
||||
|
||||
Tip: if you find yourself copying the same complex array shape over and over again to avoid `InvalidArgument` issues, try using [type aliases](utility_types.md#type-aliases), instead.
|
||||
|
||||
### Validating array shapes
|
||||
|
||||
@@ -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 of Foo, T>`](utility_types.md#class-string-mapt-as-foo-t)
|
||||
* [`class-string-map<T of Foo, T>`](utility_types.md#class-string-mapt-of-foo-t)
|
||||
* [`T[K]`](utility_types.md#tk)
|
||||
* [Type aliases](utility_types.md#type-aliases)
|
||||
* [Variable templates](utility_types.md#variable-templates)
|
||||
|
||||
@@ -414,6 +414,14 @@ Whether or not to allow `require`/`include` calls in your PHP. Defaults to `true
|
||||
```
|
||||
Allows you to hard-code a serializer for Psalm to use when caching data. By default, Psalm uses `ext-igbinary` *if* the version is greater than or equal to 2.0.5, otherwise it defaults to PHP's built-in serializer.
|
||||
|
||||
#### compressor
|
||||
```xml
|
||||
<psalm
|
||||
compressor="['lz4'|'deflate'|'off']"
|
||||
>
|
||||
```
|
||||
Allows you to hard-code a compressor for Psalm's cache. By default, Psalm uses `ext-zlib` deflate, if it's enabled.
|
||||
|
||||
#### threads
|
||||
```xml
|
||||
<psalm
|
||||
@@ -574,10 +582,11 @@ or interfaces defined in a stub file, this stub should be configured with attrib
|
||||
```
|
||||
|
||||
#### <ignoreExceptions>
|
||||
Optional. A list of exceptions to not report for `checkForThrowsDocblock` or `checkForThrowsInGlobalScope`. If an exception has `onlyGlobalScope` set to `true`, only `checkForThrowsInGlobalScope` is ignored for that exception, e.g.
|
||||
Optional. A list of exceptions to not report for `checkForThrowsDocblock` or `checkForThrowsInGlobalScope`. The `class` tag will make Psalm ignore only instances of the specified class, while `classAndDescendants` will make Psalm also ignore subclasses. If an exception has `onlyGlobalScope` set to `true`, only `checkForThrowsInGlobalScope` is ignored for that exception, e.g.
|
||||
```xml
|
||||
<ignoreExceptions>
|
||||
<class name="fully\qualified\path\Exc" onlyGlobalScope="true" />
|
||||
<classAndDescendants name="fully\qualified\path\OtherExc" />
|
||||
</ignoreExceptions>
|
||||
```
|
||||
|
||||
|
||||
@@ -6,7 +6,9 @@ It currently supports diagnostics (i.e. finding errors and warnings), go-to-defi
|
||||
|
||||
It works well in a variety of editors (listed alphabetically):
|
||||
|
||||
## Emacs
|
||||
## Client configuration
|
||||
|
||||
### Emacs
|
||||
|
||||
I got it working with [eglot](https://github.com/joaotavora/eglot)
|
||||
|
||||
@@ -27,13 +29,13 @@ This is the config I used:
|
||||
)
|
||||
```
|
||||
|
||||
## PhpStorm
|
||||
### PhpStorm
|
||||
|
||||
### Native Support
|
||||
#### Native Support
|
||||
|
||||
As of PhpStorm 2020.3 support for psalm is supported and on by default, you can read more about that [here](https://www.jetbrains.com/help/phpstorm/using-psalm.html)
|
||||
|
||||
### With LSP
|
||||
#### With LSP
|
||||
|
||||
Alternatively, psalm works with `gtache/intellij-lsp` plugin ([Jetbrains-approved version](https://plugins.jetbrains.com/plugin/10209-lsp-support), [latest version](https://github.com/gtache/intellij-lsp/releases/tag/v1.6.0)).
|
||||
|
||||
@@ -51,7 +53,7 @@ In the "Server definitions" tab you should add a definition for Psalm:
|
||||
|
||||
In the "Timeouts" tab you can adjust the initialization timeout. This is important if you have a large project. You should set the "Init" value to the number of milliseconds you allow Psalm to scan your entire project and your project's dependencies. For opening a couple of projects that use large PHP frameworks, on a high-end business laptop, try `240000` milliseconds for Init.
|
||||
|
||||
## Sublime Text
|
||||
### Sublime Text
|
||||
|
||||
I use the excellent Sublime [LSP plugin](https://github.com/tomv564/LSP) with the following config(Package Settings > LSP > Settings):
|
||||
```json
|
||||
@@ -64,7 +66,7 @@ I use the excellent Sublime [LSP plugin](https://github.com/tomv564/LSP) with th
|
||||
}
|
||||
```
|
||||
|
||||
## Vim & Neovim
|
||||
### Vim & Neovim
|
||||
|
||||
**ALE**
|
||||
|
||||
@@ -105,6 +107,15 @@ Add settings to `coc-settings.json`:
|
||||
}
|
||||
```
|
||||
|
||||
## VS Code
|
||||
### VS Code
|
||||
|
||||
[Get the Psalm plugin here](https://marketplace.visualstudio.com/items?itemName=getpsalm.psalm-vscode-plugin) (Requires VS Code 1.26+):
|
||||
|
||||
## Running the server in a docker container
|
||||
|
||||
Make sure you use `--map-folder` option. Using it without argument will map the server's CWD to the host's project root folder. You can also specify a custom mapping. For example:
|
||||
```bash
|
||||
docker-compose exec php /usr/share/php/psalm/psalm-language-server \
|
||||
-r=/var/www/html \
|
||||
--map-folder=/var/www/html:$PWD
|
||||
```
|
||||
|
||||
11
vendor/vimeo/psalm/src/Psalm/Codebase.php
vendored
11
vendor/vimeo/psalm/src/Psalm/Codebase.php
vendored
@@ -533,11 +533,12 @@ final class Codebase
|
||||
|
||||
public function cacheClassLikeStorage(ClassLikeStorage $classlike_storage, string $file_path): void
|
||||
{
|
||||
$file_contents = $this->file_provider->getContents($file_path);
|
||||
|
||||
if ($this->classlike_storage_provider->cache) {
|
||||
$this->classlike_storage_provider->cache->writeToCache($classlike_storage, $file_path, $file_contents);
|
||||
if (!$this->classlike_storage_provider->cache) {
|
||||
return;
|
||||
}
|
||||
|
||||
$file_contents = $this->file_provider->getContents($file_path);
|
||||
$this->classlike_storage_provider->cache->writeToCache($classlike_storage, $file_path, $file_contents);
|
||||
}
|
||||
|
||||
public function exhumeClassLikeStorage(string $fq_classlike_name, string $file_path): void
|
||||
@@ -553,6 +554,8 @@ final class Codebase
|
||||
$this->classlikes->addFullyQualifiedTraitName($storage->name, $file_path);
|
||||
} elseif ($storage->is_interface) {
|
||||
$this->classlikes->addFullyQualifiedInterfaceName($storage->name, $file_path);
|
||||
} elseif ($storage->is_enum) {
|
||||
$this->classlikes->addFullyQualifiedEnumName($storage->name, $file_path);
|
||||
} else {
|
||||
$this->classlikes->addFullyQualifiedClassName($storage->name, $file_path);
|
||||
}
|
||||
|
||||
62
vendor/vimeo/psalm/src/Psalm/Config.php
vendored
62
vendor/vimeo/psalm/src/Psalm/Config.php
vendored
@@ -68,6 +68,7 @@ use function file_exists;
|
||||
use function file_get_contents;
|
||||
use function flock;
|
||||
use function fopen;
|
||||
use function function_exists;
|
||||
use function get_class;
|
||||
use function get_defined_constants;
|
||||
use function get_defined_functions;
|
||||
@@ -331,6 +332,9 @@ class Config
|
||||
/** @var bool */
|
||||
public $use_igbinary = false;
|
||||
|
||||
/** @var 'lz4'|'deflate'|'off' */
|
||||
public $compressor = 'off';
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
@@ -1188,12 +1192,43 @@ class Config
|
||||
if (isset($config_xml['serializer'])) {
|
||||
$attribute_text = (string) $config_xml['serializer'];
|
||||
$config->use_igbinary = $attribute_text === 'igbinary';
|
||||
if ($config->use_igbinary
|
||||
&& (
|
||||
!function_exists('igbinary_serialize')
|
||||
|| !function_exists('igbinary_unserialize')
|
||||
)
|
||||
) {
|
||||
$config->use_igbinary = false;
|
||||
$config->config_warnings[] = '"serializer" set to "igbinary" but ext-igbinary seems to be missing on ' .
|
||||
'the system. Using php\'s build-in serializer.';
|
||||
}
|
||||
} elseif ($igbinary_version = phpversion('igbinary')) {
|
||||
$config->use_igbinary = version_compare($igbinary_version, '2.0.5') >= 0;
|
||||
}
|
||||
|
||||
if (isset($config_xml['compressor'])) {
|
||||
$compressor = (string) $config_xml['compressor'];
|
||||
if ($compressor === 'lz4') {
|
||||
if (function_exists('lz4_compress') && function_exists('lz4_uncompress')) {
|
||||
$config->compressor = 'lz4';
|
||||
} else {
|
||||
$config->config_warnings[] = '"compressor" set to "lz4" but ext-lz4 seems to be missing on the ' .
|
||||
'system. Disabling cache compressor.';
|
||||
}
|
||||
} elseif ($compressor === 'deflate') {
|
||||
if (function_exists('gzinflate') && function_exists('gzdeflate')) {
|
||||
$config->compressor = 'deflate';
|
||||
} else {
|
||||
$config->config_warnings[] = '"compressor" set to "deflate" but zlib seems to be missing on the ' .
|
||||
'system. Disabling cache compressor.';
|
||||
}
|
||||
}
|
||||
} elseif (function_exists('gzinflate') && function_exists('gzdeflate')) {
|
||||
$config->compressor = 'deflate';
|
||||
}
|
||||
|
||||
if (!isset($config_xml['findUnusedBaselineEntry'])) {
|
||||
$config->config_warnings[] = '"findUnusedBaselineEntry" will be defaulted to "true" in Psalm 6.'
|
||||
$config->config_warnings[] = '"findUnusedBaselineEntry" will default to "true" in Psalm 6.'
|
||||
. ' You should explicitly enable or disable this setting.';
|
||||
}
|
||||
|
||||
@@ -1202,7 +1237,7 @@ class Config
|
||||
$config->find_unused_code = $attribute_text === 'true' || $attribute_text === '1';
|
||||
$config->find_unused_variables = $config->find_unused_code;
|
||||
} else {
|
||||
$config->config_warnings[] = '"findUnusedCode" will be defaulted to "true" in Psalm 6.'
|
||||
$config->config_warnings[] = '"findUnusedCode" will default to "true" in Psalm 6.'
|
||||
. ' You should explicitly enable or disable this setting.';
|
||||
}
|
||||
|
||||
@@ -2218,28 +2253,27 @@ class Config
|
||||
$codebase->register_stub_files = true;
|
||||
|
||||
$dir_lvl_2 = dirname(__DIR__, 2);
|
||||
$stubsDir = $dir_lvl_2 . DIRECTORY_SEPARATOR . 'stubs' . DIRECTORY_SEPARATOR;
|
||||
$this->internal_stubs = [
|
||||
$dir_lvl_2 . DIRECTORY_SEPARATOR . 'stubs' . DIRECTORY_SEPARATOR . 'CoreGenericFunctions.phpstub',
|
||||
$dir_lvl_2 . DIRECTORY_SEPARATOR . 'stubs' . DIRECTORY_SEPARATOR . 'CoreGenericClasses.phpstub',
|
||||
$dir_lvl_2 . DIRECTORY_SEPARATOR . 'stubs' . DIRECTORY_SEPARATOR . 'CoreGenericIterators.phpstub',
|
||||
$dir_lvl_2 . DIRECTORY_SEPARATOR . 'stubs' . DIRECTORY_SEPARATOR . 'CoreImmutableClasses.phpstub',
|
||||
$dir_lvl_2 . DIRECTORY_SEPARATOR . 'stubs' . DIRECTORY_SEPARATOR . 'Reflection.phpstub',
|
||||
$dir_lvl_2 . DIRECTORY_SEPARATOR . 'stubs' . DIRECTORY_SEPARATOR . 'SPL.phpstub',
|
||||
$stubsDir . 'CoreGenericFunctions.phpstub',
|
||||
$stubsDir . 'CoreGenericClasses.phpstub',
|
||||
$stubsDir . 'CoreGenericIterators.phpstub',
|
||||
$stubsDir . 'CoreImmutableClasses.phpstub',
|
||||
$stubsDir . 'Reflection.phpstub',
|
||||
$stubsDir . 'SPL.phpstub',
|
||||
];
|
||||
|
||||
if ($codebase->analysis_php_version_id >= 8_00_00) {
|
||||
$stringable_path = $dir_lvl_2 . DIRECTORY_SEPARATOR . 'stubs' . DIRECTORY_SEPARATOR . 'Php80.phpstub';
|
||||
$this->internal_stubs[] = $stringable_path;
|
||||
$this->internal_stubs[] = $stubsDir . 'CoreGenericAttributes.phpstub';
|
||||
$this->internal_stubs[] = $stubsDir . 'Php80.phpstub';
|
||||
}
|
||||
|
||||
if ($codebase->analysis_php_version_id >= 8_01_00) {
|
||||
$stringable_path = $dir_lvl_2 . DIRECTORY_SEPARATOR . 'stubs' . DIRECTORY_SEPARATOR . 'Php81.phpstub';
|
||||
$this->internal_stubs[] = $stringable_path;
|
||||
$this->internal_stubs[] = $stubsDir . 'Php81.phpstub';
|
||||
}
|
||||
|
||||
if ($codebase->analysis_php_version_id >= 8_02_00) {
|
||||
$stringable_path = $dir_lvl_2 . DIRECTORY_SEPARATOR . 'stubs' . DIRECTORY_SEPARATOR . 'Php82.phpstub';
|
||||
$this->internal_stubs[] = $stringable_path;
|
||||
$this->internal_stubs[] = $stubsDir . 'Php82.phpstub';
|
||||
$this->php_extensions['random'] = true; // random is a part of the PHP core starting from PHP 8.2
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ use function glob;
|
||||
use function implode;
|
||||
use function is_array;
|
||||
use function is_dir;
|
||||
use function is_string;
|
||||
use function json_decode;
|
||||
use function ksort;
|
||||
use function max;
|
||||
@@ -46,6 +47,7 @@ final class Creator
|
||||
xmlns="https://getpsalm.org/schema/config"
|
||||
xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd"
|
||||
findUnusedBaselineEntry="true"
|
||||
findUnusedCode="true"
|
||||
>
|
||||
<projectFiles>
|
||||
<directory name="src" />
|
||||
@@ -249,6 +251,10 @@ final class Creator
|
||||
}
|
||||
|
||||
foreach ($paths as $path) {
|
||||
if (!is_string($path)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($path === '') {
|
||||
$nodes = [...$nodes, ...self::guessPhpFileDirs($current_dir)];
|
||||
|
||||
|
||||
119
vendor/vimeo/psalm/src/Psalm/Config/FileFilter.php
vendored
119
vendor/vimeo/psalm/src/Psalm/Config/FileFilter.php
vendored
@@ -10,12 +10,18 @@ use SimpleXMLElement;
|
||||
|
||||
use function array_filter;
|
||||
use function array_map;
|
||||
use function array_merge;
|
||||
use function array_shift;
|
||||
use function count;
|
||||
use function explode;
|
||||
use function glob;
|
||||
use function in_array;
|
||||
use function is_dir;
|
||||
use function is_iterable;
|
||||
use function is_string;
|
||||
use function preg_match;
|
||||
use function preg_replace;
|
||||
use function preg_split;
|
||||
use function readlink;
|
||||
use function realpath;
|
||||
use function restore_error_handler;
|
||||
@@ -52,12 +58,12 @@ class FileFilter
|
||||
protected $fq_classlike_names = [];
|
||||
|
||||
/**
|
||||
* @var array<string>
|
||||
* @var array<non-empty-string>
|
||||
*/
|
||||
protected $fq_classlike_patterns = [];
|
||||
|
||||
/**
|
||||
* @var array<string>
|
||||
* @var array<non-empty-string>
|
||||
*/
|
||||
protected $method_ids = [];
|
||||
|
||||
@@ -129,10 +135,12 @@ class FileFilter
|
||||
}
|
||||
|
||||
if (strpos($prospective_directory_path, '*') !== false) {
|
||||
$globs = array_map(
|
||||
'realpath',
|
||||
glob($prospective_directory_path, GLOB_ONLYDIR),
|
||||
);
|
||||
// Strip meaningless trailing recursive wildcard like "path/**/" or "path/**"
|
||||
$prospective_directory_path = preg_replace('#(\/\*\*)+\/?$#', '/', $prospective_directory_path);
|
||||
// Split by /**/, allow duplicated wildcards like "path/**/**/path" and any leading dir separator.
|
||||
/** @var non-empty-list<non-empty-string> $path_parts */
|
||||
$path_parts = preg_split('#(\/|\\\)(\*\*\/)+#', $prospective_directory_path);
|
||||
$globs = self::recursiveGlob($path_parts, true);
|
||||
|
||||
if (empty($globs)) {
|
||||
if ($allow_missing_files) {
|
||||
@@ -145,8 +153,8 @@ class FileFilter
|
||||
);
|
||||
}
|
||||
|
||||
foreach ($globs as $glob_index => $directory_path) {
|
||||
if (!$directory_path) {
|
||||
foreach ($globs as $glob_index => $glob_directory_path) {
|
||||
if (!$glob_directory_path) {
|
||||
if ($allow_missing_files) {
|
||||
continue;
|
||||
}
|
||||
@@ -158,14 +166,14 @@ class FileFilter
|
||||
}
|
||||
|
||||
if ($ignore_type_stats && $filter instanceof ProjectFileFilter) {
|
||||
$filter->ignore_type_stats[$directory_path] = true;
|
||||
$filter->ignore_type_stats[$glob_directory_path] = true;
|
||||
}
|
||||
|
||||
if ($declare_strict_types && $filter instanceof ProjectFileFilter) {
|
||||
$filter->declare_strict_types[$directory_path] = true;
|
||||
$filter->declare_strict_types[$glob_directory_path] = true;
|
||||
}
|
||||
|
||||
$filter->addDirectory($directory_path);
|
||||
$filter->addDirectory($glob_directory_path);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
@@ -246,13 +254,10 @@ class FileFilter
|
||||
}
|
||||
|
||||
if (strpos($prospective_file_path, '*') !== false) {
|
||||
$globs = array_map(
|
||||
'realpath',
|
||||
array_filter(
|
||||
glob($prospective_file_path, GLOB_NOSORT),
|
||||
'file_exists',
|
||||
),
|
||||
);
|
||||
// Split by /**/, allow duplicated wildcards like "path/**/**/path" and any leading dir separator.
|
||||
/** @var non-empty-list<non-empty-string> $path_parts */
|
||||
$path_parts = preg_split('#(\/|\\\)(\*\*\/)+#', $prospective_file_path);
|
||||
$globs = self::recursiveGlob($path_parts, false);
|
||||
|
||||
if (empty($globs)) {
|
||||
if ($allow_missing_files) {
|
||||
@@ -265,14 +270,18 @@ class FileFilter
|
||||
);
|
||||
}
|
||||
|
||||
foreach ($globs as $glob_index => $file_path) {
|
||||
if (!$file_path && !$allow_missing_files) {
|
||||
foreach ($globs as $glob_index => $glob_file_path) {
|
||||
if (!$glob_file_path) {
|
||||
if ($allow_missing_files) {
|
||||
continue;
|
||||
}
|
||||
|
||||
throw new ConfigException(
|
||||
'Could not resolve config path to ' . $base_dir . DIRECTORY_SEPARATOR .
|
||||
$file_path . ':' . $glob_index,
|
||||
);
|
||||
}
|
||||
$filter->addFile($file_path);
|
||||
$filter->addFile($glob_file_path);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
@@ -306,14 +315,18 @@ class FileFilter
|
||||
if (isset($config['referencedMethod']) && is_iterable($config['referencedMethod'])) {
|
||||
/** @var array $referenced_method */
|
||||
foreach ($config['referencedMethod'] as $referenced_method) {
|
||||
$method_id = (string) ($referenced_method['name'] ?? '');
|
||||
|
||||
if (!preg_match('/^[^:]+::[^:]+$/', $method_id) && !static::isRegularExpression($method_id)) {
|
||||
$method_id = $referenced_method['name'] ?? '';
|
||||
if (!is_string($method_id)
|
||||
|| (!preg_match('/^[^:]+::[^:]+$/', $method_id) && !static::isRegularExpression($method_id))) {
|
||||
throw new ConfigException(
|
||||
'Invalid referencedMethod ' . $method_id,
|
||||
'Invalid referencedMethod ' . ((string) $method_id),
|
||||
);
|
||||
}
|
||||
|
||||
if ($method_id === '') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$filter->method_ids[] = strtolower($method_id);
|
||||
}
|
||||
}
|
||||
@@ -321,7 +334,21 @@ class FileFilter
|
||||
if (isset($config['referencedFunction']) && is_iterable($config['referencedFunction'])) {
|
||||
/** @var array $referenced_function */
|
||||
foreach ($config['referencedFunction'] as $referenced_function) {
|
||||
$filter->method_ids[] = strtolower((string) ($referenced_function['name'] ?? ''));
|
||||
$function_id = $referenced_function['name'] ?? '';
|
||||
if (!is_string($function_id)
|
||||
|| (!preg_match('/^[a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*$/', $function_id)
|
||||
&& !preg_match('/^[^:]+::[^:]+$/', $function_id) // methods are also allowed
|
||||
&& !static::isRegularExpression($function_id))) {
|
||||
throw new ConfigException(
|
||||
'Invalid referencedFunction ' . ((string) $function_id),
|
||||
);
|
||||
}
|
||||
|
||||
if ($function_id === '') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$filter->method_ids[] = strtolower($function_id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -433,8 +460,15 @@ class FileFilter
|
||||
return self::loadFromArray($config, $base_dir, $inclusive);
|
||||
}
|
||||
|
||||
/**
|
||||
* @psalm-assert-if-true non-empty-string $string
|
||||
*/
|
||||
private static function isRegularExpression(string $string): bool
|
||||
{
|
||||
if ($string === '') {
|
||||
return false;
|
||||
}
|
||||
|
||||
set_error_handler(
|
||||
static fn(): bool => true,
|
||||
E_WARNING,
|
||||
@@ -445,6 +479,39 @@ class FileFilter
|
||||
return $is_regexp;
|
||||
}
|
||||
|
||||
/**
|
||||
* @mutation-free
|
||||
* @param non-empty-list<non-empty-string> $parts
|
||||
* @return array<string|false>
|
||||
*/
|
||||
private static function recursiveGlob(array $parts, bool $only_dir): array
|
||||
{
|
||||
if (count($parts) < 2) {
|
||||
if ($only_dir) {
|
||||
$list = glob($parts[0], GLOB_ONLYDIR | GLOB_NOSORT) ?: [];
|
||||
} else {
|
||||
$list = array_filter(
|
||||
glob($parts[0], GLOB_NOSORT) ?: [],
|
||||
'file_exists',
|
||||
);
|
||||
}
|
||||
|
||||
return array_map('realpath', $list);
|
||||
}
|
||||
|
||||
$first_dir = self::slashify($parts[0]);
|
||||
$paths = glob($first_dir . '*', GLOB_ONLYDIR | GLOB_NOSORT);
|
||||
$result = [];
|
||||
foreach ($paths as $path) {
|
||||
$parts[0] = $path;
|
||||
$result = array_merge($result, self::recursiveGlob($parts, $only_dir));
|
||||
}
|
||||
array_shift($parts);
|
||||
$parts[0] = $first_dir . $parts[0];
|
||||
|
||||
return array_merge($result, self::recursiveGlob($parts, $only_dir));
|
||||
}
|
||||
|
||||
/**
|
||||
* @psalm-pure
|
||||
*/
|
||||
|
||||
@@ -4,6 +4,9 @@ namespace Psalm\Internal;
|
||||
|
||||
use Psalm\Exception\ComplicatedExpressionException;
|
||||
use Psalm\Storage\Assertion;
|
||||
use Psalm\Type\Atomic\TArray;
|
||||
use Psalm\Type\Atomic\TKeyedArray;
|
||||
use Psalm\Type\Atomic\TList;
|
||||
use UnexpectedValueException;
|
||||
|
||||
use function array_filter;
|
||||
@@ -322,6 +325,9 @@ class Algebra
|
||||
/**
|
||||
* Look for clauses with only one possible value
|
||||
*
|
||||
* doesn't infer the "unset" correctly
|
||||
*
|
||||
* @psalm-suppress MoreSpecificReturnType
|
||||
* @param list<Clause> $clauses
|
||||
* @param array<string, bool> $cond_referenced_var_ids
|
||||
* @param array<string, array<int, array<int, Assertion>>> $active_truths
|
||||
@@ -388,6 +394,72 @@ class Algebra
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($truths as $var => $anded_types) {
|
||||
$has_list_or_array = false;
|
||||
foreach ($anded_types as $orred_types) {
|
||||
foreach ($orred_types as $assertion) {
|
||||
if ($assertion->isNegation()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!isset($assertion->type)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($assertion->type instanceof TList
|
||||
|| $assertion->type instanceof TArray
|
||||
|| $assertion->type instanceof TKeyedArray) {
|
||||
$has_list_or_array = true;
|
||||
// list/array are collapsed, therefore there can only be 1 and we can abort
|
||||
// otherwise we would have to remove them all individually
|
||||
// e.g. array<string, string> cannot be array<int, float>
|
||||
break 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($has_list_or_array === false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($anded_types as $key => $orred_types) {
|
||||
foreach ($orred_types as $index => $assertion) {
|
||||
// we only need to check negations
|
||||
// due to type collapsing, any negations for arrays are irrelevant
|
||||
if (!$assertion->isNegation()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!isset($assertion->type)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($assertion->type instanceof TList
|
||||
|| $assertion->type instanceof TArray
|
||||
|| $assertion->type instanceof TKeyedArray) {
|
||||
unset($truths[$var][$key][$index]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* doesn't infer the "unset" correctly
|
||||
*
|
||||
* @psalm-suppress DocblockTypeContradiction
|
||||
*/
|
||||
if ($truths[$var][$key] === []) {
|
||||
unset($truths[$var][$key]);
|
||||
} else {
|
||||
/**
|
||||
* doesn't infer the "unset" correctly
|
||||
*
|
||||
* @psalm-suppress RedundantFunctionCallGivenDocblockType
|
||||
*/
|
||||
$truths[$var][$key] = array_values($truths[$var][$key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** @psalm-suppress LessSpecificReturnStatement */
|
||||
return $truths;
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@ use Psalm\CodeLocation;
|
||||
use Psalm\FileManipulation;
|
||||
use Psalm\Internal\FileManipulation\FileManipulationBuffer;
|
||||
|
||||
use function implode;
|
||||
use function strtolower;
|
||||
|
||||
/**
|
||||
@@ -51,7 +50,7 @@ trait CanAlias
|
||||
$codebase = $this->getCodebase();
|
||||
|
||||
foreach ($stmt->uses as $use) {
|
||||
$use_path = implode('\\', $use->name->parts);
|
||||
$use_path = $use->name->toString();
|
||||
$use_path_lc = strtolower($use_path);
|
||||
$use_alias = $use->alias->name ?? $use->name->getLast();
|
||||
$use_alias_lc = strtolower($use_alias);
|
||||
@@ -106,12 +105,12 @@ trait CanAlias
|
||||
|
||||
public function visitGroupUse(PhpParser\Node\Stmt\GroupUse $stmt): void
|
||||
{
|
||||
$use_prefix = implode('\\', $stmt->prefix->parts);
|
||||
$use_prefix = $stmt->prefix->toString();
|
||||
|
||||
$codebase = $this->getCodebase();
|
||||
|
||||
foreach ($stmt->uses as $use) {
|
||||
$use_path = $use_prefix . '\\' . implode('\\', $use->name->parts);
|
||||
$use_path = $use_prefix . '\\' . $use->name->toString();
|
||||
$use_alias = $use->alias->name ?? $use->name->getLast();
|
||||
|
||||
switch ($use->type !== PhpParser\Node\Stmt\Use_::TYPE_UNKNOWN ? $use->type : $stmt->type) {
|
||||
|
||||
@@ -802,7 +802,20 @@ class ClassAnalyzer extends ClassLikeAnalyzer
|
||||
$codebase,
|
||||
);
|
||||
|
||||
if ($property_storage->location
|
||||
if ($guide_property_storage->readonly
|
||||
&& UnionTypeComparator::isContainedBy(
|
||||
$codebase,
|
||||
$property_type,
|
||||
$guide_property_type,
|
||||
false,
|
||||
false,
|
||||
null,
|
||||
false,
|
||||
false,
|
||||
)) {
|
||||
// if the original property is readonly, it cannot be written
|
||||
// therefore invariance is not a problem, if the parent type contains the child type
|
||||
} elseif ($property_storage->location
|
||||
&& !$property_type->equals($guide_property_type, false)
|
||||
&& $guide_class_storage->user_defined
|
||||
) {
|
||||
@@ -999,6 +1012,11 @@ class ClassAnalyzer extends ClassLikeAnalyzer
|
||||
return;
|
||||
}
|
||||
|
||||
// abstract constructors do not have any code, therefore cannot set any properties either
|
||||
if (isset($storage->methods['__construct']) && $storage->methods['__construct']->abstract) {
|
||||
return;
|
||||
}
|
||||
|
||||
$fq_class_name = $class_context->self ?: $this->fq_class_name;
|
||||
$fq_class_name_lc = strtolower($fq_class_name);
|
||||
|
||||
@@ -2003,7 +2021,7 @@ class ClassAnalyzer extends ClassLikeAnalyzer
|
||||
. ($interface_name instanceof PhpParser\Node\Name\FullyQualified
|
||||
? '\\'
|
||||
: $this->getNamespace() . '-')
|
||||
. implode('\\', $interface_name->parts),
|
||||
. $interface_name->toString(),
|
||||
);
|
||||
|
||||
$interface_location = new CodeLocation($this, $interface_name);
|
||||
@@ -2419,7 +2437,7 @@ class ClassAnalyzer extends ClassLikeAnalyzer
|
||||
. ($extended_class instanceof PhpParser\Node\Name\FullyQualified
|
||||
? '\\'
|
||||
: $this->getNamespace() . '-')
|
||||
. implode('\\', $extended_class->parts),
|
||||
. $extended_class->toString(),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -40,7 +40,6 @@ use function array_search;
|
||||
use function count;
|
||||
use function explode;
|
||||
use function gettype;
|
||||
use function implode;
|
||||
use function in_array;
|
||||
use function preg_match;
|
||||
use function preg_replace;
|
||||
@@ -421,15 +420,15 @@ abstract class ClassLikeAnalyzer extends SourceAnalyzer
|
||||
}
|
||||
|
||||
if ($class_name instanceof PhpParser\Node\Name\FullyQualified) {
|
||||
return implode('\\', $class_name->parts);
|
||||
return $class_name->toString();
|
||||
}
|
||||
|
||||
if (in_array($class_name->parts[0], ['self', 'static', 'parent'], true)) {
|
||||
return $class_name->parts[0];
|
||||
if (in_array($class_name->getFirst(), ['self', 'static', 'parent'], true)) {
|
||||
return $class_name->getFirst();
|
||||
}
|
||||
|
||||
return Type::getFQCLNFromString(
|
||||
implode('\\', $class_name->parts),
|
||||
$class_name->toString(),
|
||||
$aliases,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ use function preg_split;
|
||||
use function rtrim;
|
||||
use function str_replace;
|
||||
use function strlen;
|
||||
use function strpos;
|
||||
use function substr;
|
||||
use function substr_count;
|
||||
use function trim;
|
||||
@@ -259,7 +260,8 @@ class CommentAnalyzer
|
||||
public static function sanitizeDocblockType(string $docblock_type): string
|
||||
{
|
||||
$docblock_type = preg_replace('@^[ \t]*\*@m', '', $docblock_type);
|
||||
$docblock_type = preg_replace('/,\n\s+\}/', '}', $docblock_type);
|
||||
$docblock_type = preg_replace('/,\n\s+}/', '}', $docblock_type);
|
||||
|
||||
return str_replace("\n", '', $docblock_type);
|
||||
}
|
||||
|
||||
@@ -326,6 +328,22 @@ class CommentAnalyzer
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($char === '/' && $next_char === '/') {
|
||||
// Ignore the rest of the current line
|
||||
$i = strpos($return_block, "\n", $i);
|
||||
if ($i === false) {
|
||||
throw new IncorrectDocblockException(
|
||||
'Comment lines must be terminated with a new line character (\\n).',
|
||||
);
|
||||
}
|
||||
|
||||
// Remove trailing whitespaces (needed for `sanitizeDocblockType`)
|
||||
$type = rtrim($type);
|
||||
$type .= "\n";
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($char === '[' || $char === '{' || $char === '(' || $char === '<') {
|
||||
$brackets .= $char;
|
||||
} elseif ($char === ']' || $char === '}' || $char === ')' || $char === '>') {
|
||||
@@ -346,6 +364,11 @@ class CommentAnalyzer
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($next_char === '{') {
|
||||
$type .= ' ';
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($next_char === '|' || $next_char === '&') {
|
||||
$nexter_char = $i < $l - 2 ? $return_block[$i + 2] : null;
|
||||
|
||||
@@ -377,7 +400,7 @@ class CommentAnalyzer
|
||||
$remaining = trim(preg_replace('@^[ \t]*\* *@m', ' ', substr($return_block, $i + 1)));
|
||||
|
||||
if ($remaining) {
|
||||
return array_merge([rtrim($type)], preg_split('/[ \s]+/', $remaining) ?: []);
|
||||
return array_merge([rtrim($type)], preg_split('/\s+/', $remaining) ?: []);
|
||||
}
|
||||
|
||||
return [$type];
|
||||
|
||||
@@ -32,7 +32,6 @@ use function array_combine;
|
||||
use function array_diff_key;
|
||||
use function array_keys;
|
||||
use function count;
|
||||
use function implode;
|
||||
use function strpos;
|
||||
use function strtolower;
|
||||
|
||||
@@ -283,7 +282,7 @@ class FileAnalyzer extends SourceAnalyzer
|
||||
} elseif ($stmt instanceof PhpParser\Node\Stmt\ClassLike) {
|
||||
$this->populateClassLikeAnalyzers($stmt);
|
||||
} elseif ($stmt instanceof PhpParser\Node\Stmt\Namespace_) {
|
||||
$namespace_name = $stmt->name ? implode('\\', $stmt->name->parts) : '';
|
||||
$namespace_name = $stmt->name ? $stmt->name->toString() : '';
|
||||
|
||||
$namespace_analyzer = new NamespaceAnalyzer($stmt, $this);
|
||||
$namespace_analyzer->collectAnalyzableInformation();
|
||||
|
||||
@@ -11,6 +11,12 @@ use const STR_PAD_LEFT;
|
||||
*/
|
||||
class IssueData
|
||||
{
|
||||
public const SEVERITY_INFO = 'info';
|
||||
public const SEVERITY_ERROR = 'error';
|
||||
|
||||
/**
|
||||
* @var self::SEVERITY_*
|
||||
*/
|
||||
public string $severity;
|
||||
|
||||
public int $line_from;
|
||||
@@ -93,6 +99,7 @@ class IssueData
|
||||
public ?string $dupe_key = null;
|
||||
|
||||
/**
|
||||
* @param self::SEVERITY_* $severity
|
||||
* @param ?list<DataFlowNodeData|array{label: string, entry_path_type: string}> $taint_trace
|
||||
* @param ?list<DataFlowNodeData> $other_references
|
||||
*/
|
||||
|
||||
@@ -14,7 +14,6 @@ use UnexpectedValueException;
|
||||
|
||||
use function assert;
|
||||
use function count;
|
||||
use function implode;
|
||||
use function is_string;
|
||||
use function preg_replace;
|
||||
use function strpos;
|
||||
@@ -49,7 +48,7 @@ class NamespaceAnalyzer extends SourceAnalyzer
|
||||
{
|
||||
$this->source = $source;
|
||||
$this->namespace = $namespace;
|
||||
$this->namespace_name = $this->namespace->name ? implode('\\', $this->namespace->name->parts) : '';
|
||||
$this->namespace_name = $this->namespace->name ? $this->namespace->name->toString() : '';
|
||||
}
|
||||
|
||||
public function collectAnalyzableInformation(): void
|
||||
|
||||
@@ -1098,6 +1098,15 @@ class ProjectAnalyzer
|
||||
}
|
||||
}
|
||||
|
||||
public function finish(float $start_time, string $psalm_version): void
|
||||
{
|
||||
$this->codebase->file_reference_provider->removeDeletedFilesFromReferences();
|
||||
|
||||
if ($this->project_cache_provider) {
|
||||
$this->project_cache_provider->processSuccessfulRun($start_time, $psalm_version);
|
||||
}
|
||||
}
|
||||
|
||||
public function getConfig(): Config
|
||||
{
|
||||
return $this->config;
|
||||
|
||||
@@ -238,13 +238,13 @@ class IfConditionalAnalyzer
|
||||
|| $stmt instanceof PhpParser\Node\Expr\BinaryOp\Identical
|
||||
) {
|
||||
if ($stmt->left instanceof PhpParser\Node\Expr\ConstFetch
|
||||
&& $stmt->left->name->parts === ['true']
|
||||
&& $stmt->left->name->getParts() === ['true']
|
||||
) {
|
||||
return self::getDefinitelyEvaluatedExpressionAfterIf($stmt->right);
|
||||
}
|
||||
|
||||
if ($stmt->right instanceof PhpParser\Node\Expr\ConstFetch
|
||||
&& $stmt->right->name->parts === ['true']
|
||||
&& $stmt->right->name->getParts() === ['true']
|
||||
) {
|
||||
return self::getDefinitelyEvaluatedExpressionAfterIf($stmt->left);
|
||||
}
|
||||
@@ -282,13 +282,13 @@ class IfConditionalAnalyzer
|
||||
|| $stmt instanceof PhpParser\Node\Expr\BinaryOp\Identical
|
||||
) {
|
||||
if ($stmt->left instanceof PhpParser\Node\Expr\ConstFetch
|
||||
&& $stmt->left->name->parts === ['true']
|
||||
&& $stmt->left->name->getParts() === ['true']
|
||||
) {
|
||||
return self::getDefinitelyEvaluatedExpressionInsideIf($stmt->right);
|
||||
}
|
||||
|
||||
if ($stmt->right instanceof PhpParser\Node\Expr\ConstFetch
|
||||
&& $stmt->right->name->parts === ['true']
|
||||
&& $stmt->right->name->getParts() === ['true']
|
||||
) {
|
||||
return self::getDefinitelyEvaluatedExpressionInsideIf($stmt->left);
|
||||
}
|
||||
|
||||
@@ -358,16 +358,6 @@ class ElseIfAnalyzer
|
||||
$elseif_context->inside_loop,
|
||||
new CodeLocation($statements_analyzer->getSource(), $elseif, $outer_context->include_location),
|
||||
);
|
||||
|
||||
$updated_vars = [];
|
||||
|
||||
$outer_context->update(
|
||||
$elseif_context,
|
||||
$implied_outer_context,
|
||||
false,
|
||||
array_keys($negated_elseif_types),
|
||||
$updated_vars,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -209,7 +209,7 @@ class SwitchCaseAnalyzer
|
||||
}
|
||||
|
||||
if ($switch_condition instanceof PhpParser\Node\Expr\ConstFetch
|
||||
&& $switch_condition->name->parts === ['true']
|
||||
&& $switch_condition->name->getParts() === ['true']
|
||||
) {
|
||||
$case_equality_expr = $case->cond;
|
||||
} elseif (($switch_condition_type = $statements_analyzer->node_data->getType($switch_condition))
|
||||
|
||||
@@ -26,7 +26,8 @@ class WhileAnalyzer
|
||||
PhpParser\Node\Stmt\While_ $stmt,
|
||||
Context $context
|
||||
): ?bool {
|
||||
$while_true = ($stmt->cond instanceof PhpParser\Node\Expr\ConstFetch && $stmt->cond->name->parts === ['true'])
|
||||
$while_true = ($stmt->cond instanceof PhpParser\Node\Expr\ConstFetch
|
||||
&& $stmt->cond->name->getParts() === ['true'])
|
||||
|| (($t = $statements_analyzer->node_data->getType($stmt->cond))
|
||||
&& $t->isAlwaysTruthy());
|
||||
|
||||
|
||||
@@ -830,7 +830,7 @@ class AssertionFinder
|
||||
$if_types[$first_var_name] = [[new IsType(new TCallableString())]];
|
||||
}
|
||||
} elseif ($expr->name instanceof PhpParser\Node\Name
|
||||
&& strtolower($expr->name->parts[0]) === 'method_exists'
|
||||
&& strtolower($expr->name->getFirst()) === 'method_exists'
|
||||
&& isset($expr->getArgs()[1])
|
||||
&& $expr->getArgs()[1]->value instanceof PhpParser\Node\Scalar\String_
|
||||
) {
|
||||
@@ -1233,7 +1233,7 @@ class AssertionFinder
|
||||
FileSource $source
|
||||
): array {
|
||||
if ($stmt->class instanceof PhpParser\Node\Name) {
|
||||
if (!in_array(strtolower($stmt->class->parts[0]), ['self', 'static', 'parent'], true)) {
|
||||
if (!in_array(strtolower($stmt->class->getFirst()), ['self', 'static', 'parent'], true)) {
|
||||
$instanceof_class = ClassLikeAnalyzer::getFQCLNFromNameObject(
|
||||
$stmt->class,
|
||||
$source->getAliases(),
|
||||
@@ -1248,8 +1248,8 @@ class AssertionFinder
|
||||
}
|
||||
|
||||
if ($this_class_name
|
||||
&& (in_array(strtolower($stmt->class->parts[0]), ['self', 'static'], true))) {
|
||||
$is_static = $stmt->class->parts[0] === 'static';
|
||||
&& (in_array(strtolower($stmt->class->getFirst()), ['self', 'static'], true))) {
|
||||
$is_static = $stmt->class->getFirst() === 'static';
|
||||
$named_object = new TNamedObject($this_class_name, $is_static);
|
||||
|
||||
if ($is_static) {
|
||||
@@ -1297,13 +1297,13 @@ class AssertionFinder
|
||||
FileSource $source
|
||||
): ?int {
|
||||
if ($conditional->right instanceof PhpParser\Node\Expr\ConstFetch
|
||||
&& strtolower($conditional->right->name->parts[0]) === 'null'
|
||||
&& strtolower($conditional->right->name->getFirst()) === 'null'
|
||||
) {
|
||||
return self::ASSIGNMENT_TO_RIGHT;
|
||||
}
|
||||
|
||||
if ($conditional->left instanceof PhpParser\Node\Expr\ConstFetch
|
||||
&& strtolower($conditional->left->name->parts[0]) === 'null'
|
||||
&& strtolower($conditional->left->name->getFirst()) === 'null'
|
||||
) {
|
||||
return self::ASSIGNMENT_TO_LEFT;
|
||||
}
|
||||
@@ -1325,13 +1325,13 @@ class AssertionFinder
|
||||
PhpParser\Node\Expr\BinaryOp $conditional
|
||||
): ?int {
|
||||
if ($conditional->right instanceof PhpParser\Node\Expr\ConstFetch
|
||||
&& strtolower($conditional->right->name->parts[0]) === 'false'
|
||||
&& strtolower($conditional->right->name->getFirst()) === 'false'
|
||||
) {
|
||||
return self::ASSIGNMENT_TO_RIGHT;
|
||||
}
|
||||
|
||||
if ($conditional->left instanceof PhpParser\Node\Expr\ConstFetch
|
||||
&& strtolower($conditional->left->name->parts[0]) === 'false'
|
||||
&& strtolower($conditional->left->name->getFirst()) === 'false'
|
||||
) {
|
||||
return self::ASSIGNMENT_TO_LEFT;
|
||||
}
|
||||
@@ -1346,13 +1346,13 @@ class AssertionFinder
|
||||
PhpParser\Node\Expr\BinaryOp $conditional
|
||||
): ?int {
|
||||
if ($conditional->right instanceof PhpParser\Node\Expr\ConstFetch
|
||||
&& strtolower($conditional->right->name->parts[0]) === 'true'
|
||||
&& strtolower($conditional->right->name->getFirst()) === 'true'
|
||||
) {
|
||||
return self::ASSIGNMENT_TO_RIGHT;
|
||||
}
|
||||
|
||||
if ($conditional->left instanceof PhpParser\Node\Expr\ConstFetch
|
||||
&& strtolower($conditional->left->name->parts[0]) === 'true'
|
||||
&& strtolower($conditional->left->name->getFirst()) === 'true'
|
||||
) {
|
||||
return self::ASSIGNMENT_TO_LEFT;
|
||||
}
|
||||
@@ -1390,7 +1390,7 @@ class AssertionFinder
|
||||
) {
|
||||
if ($conditional->right instanceof PhpParser\Node\Expr\FuncCall
|
||||
&& $conditional->right->name instanceof PhpParser\Node\Name
|
||||
&& strtolower($conditional->right->name->parts[0]) === 'gettype'
|
||||
&& strtolower($conditional->right->name->getFirst()) === 'gettype'
|
||||
&& $conditional->right->getArgs()
|
||||
&& $conditional->left instanceof PhpParser\Node\Scalar\String_
|
||||
) {
|
||||
@@ -1399,7 +1399,7 @@ class AssertionFinder
|
||||
|
||||
if ($conditional->left instanceof PhpParser\Node\Expr\FuncCall
|
||||
&& $conditional->left->name instanceof PhpParser\Node\Name
|
||||
&& strtolower($conditional->left->name->parts[0]) === 'gettype'
|
||||
&& strtolower($conditional->left->name->getFirst()) === 'gettype'
|
||||
&& $conditional->left->getArgs()
|
||||
&& $conditional->right instanceof PhpParser\Node\Scalar\String_
|
||||
) {
|
||||
@@ -1418,7 +1418,7 @@ class AssertionFinder
|
||||
) {
|
||||
if ($conditional->right instanceof PhpParser\Node\Expr\FuncCall
|
||||
&& $conditional->right->name instanceof PhpParser\Node\Name
|
||||
&& strtolower($conditional->right->name->parts[0]) === 'get_debug_type'
|
||||
&& strtolower($conditional->right->name->getFirst()) === 'get_debug_type'
|
||||
&& $conditional->right->getArgs()
|
||||
&& ($conditional->left instanceof PhpParser\Node\Scalar\String_
|
||||
|| $conditional->left instanceof PhpParser\Node\Expr\ClassConstFetch)
|
||||
@@ -1428,7 +1428,7 @@ class AssertionFinder
|
||||
|
||||
if ($conditional->left instanceof PhpParser\Node\Expr\FuncCall
|
||||
&& $conditional->left->name instanceof PhpParser\Node\Name
|
||||
&& strtolower($conditional->left->name->parts[0]) === 'get_debug_type'
|
||||
&& strtolower($conditional->left->name->getFirst()) === 'get_debug_type'
|
||||
&& $conditional->left->getArgs()
|
||||
&& ($conditional->right instanceof PhpParser\Node\Scalar\String_
|
||||
|| $conditional->right instanceof PhpParser\Node\Expr\ClassConstFetch)
|
||||
@@ -1453,11 +1453,11 @@ class AssertionFinder
|
||||
|
||||
$right_get_class = $conditional->right instanceof PhpParser\Node\Expr\FuncCall
|
||||
&& $conditional->right->name instanceof PhpParser\Node\Name
|
||||
&& strtolower($conditional->right->name->parts[0]) === 'get_class';
|
||||
&& strtolower($conditional->right->name->getFirst()) === 'get_class';
|
||||
|
||||
$right_static_class = $conditional->right instanceof PhpParser\Node\Expr\ClassConstFetch
|
||||
&& $conditional->right->class instanceof PhpParser\Node\Name
|
||||
&& $conditional->right->class->parts === ['static']
|
||||
&& $conditional->right->class->getParts() === ['static']
|
||||
&& $conditional->right->name instanceof PhpParser\Node\Identifier
|
||||
&& strtolower($conditional->right->name->name) === 'class';
|
||||
|
||||
@@ -1492,11 +1492,11 @@ class AssertionFinder
|
||||
|
||||
$left_get_class = $conditional->left instanceof PhpParser\Node\Expr\FuncCall
|
||||
&& $conditional->left->name instanceof PhpParser\Node\Name
|
||||
&& strtolower($conditional->left->name->parts[0]) === 'get_class';
|
||||
&& strtolower($conditional->left->name->getFirst()) === 'get_class';
|
||||
|
||||
$left_static_class = $conditional->left instanceof PhpParser\Node\Expr\ClassConstFetch
|
||||
&& $conditional->left->class instanceof PhpParser\Node\Name
|
||||
&& $conditional->left->class->parts === ['static']
|
||||
&& $conditional->left->class->getParts() === ['static']
|
||||
&& $conditional->left->name instanceof PhpParser\Node\Identifier
|
||||
&& strtolower($conditional->left->name->name) === 'class';
|
||||
|
||||
@@ -1542,7 +1542,7 @@ class AssertionFinder
|
||||
) {
|
||||
if ($conditional->left instanceof PhpParser\Node\Expr\FuncCall
|
||||
&& $conditional->left->name instanceof PhpParser\Node\Name
|
||||
&& in_array(strtolower($conditional->left->name->parts[0]), ['count', 'sizeof'])
|
||||
&& in_array(strtolower($conditional->left->name->getFirst()), ['count', 'sizeof'])
|
||||
&& $conditional->left->getArgs()
|
||||
&& ($conditional instanceof BinaryOp\Greater || $conditional instanceof BinaryOp\GreaterOrEqual)
|
||||
) {
|
||||
@@ -1551,7 +1551,7 @@ class AssertionFinder
|
||||
$comparison_adjustment = $conditional instanceof BinaryOp\Greater ? 1 : 0;
|
||||
} elseif ($conditional->right instanceof PhpParser\Node\Expr\FuncCall
|
||||
&& $conditional->right->name instanceof PhpParser\Node\Name
|
||||
&& in_array(strtolower($conditional->right->name->parts[0]), ['count', 'sizeof'])
|
||||
&& in_array(strtolower($conditional->right->name->getFirst()), ['count', 'sizeof'])
|
||||
&& $conditional->right->getArgs()
|
||||
&& ($conditional instanceof BinaryOp\Smaller || $conditional instanceof BinaryOp\SmallerOrEqual)
|
||||
) {
|
||||
@@ -1584,7 +1584,7 @@ class AssertionFinder
|
||||
) {
|
||||
$left_count = $conditional->left instanceof PhpParser\Node\Expr\FuncCall
|
||||
&& $conditional->left->name instanceof PhpParser\Node\Name
|
||||
&& in_array(strtolower($conditional->left->name->parts[0]), ['count', 'sizeof'])
|
||||
&& in_array(strtolower($conditional->left->name->getFirst()), ['count', 'sizeof'])
|
||||
&& $conditional->left->getArgs();
|
||||
|
||||
$operator_less_than_or_equal =
|
||||
@@ -1603,7 +1603,7 @@ class AssertionFinder
|
||||
|
||||
$right_count = $conditional->right instanceof PhpParser\Node\Expr\FuncCall
|
||||
&& $conditional->right->name instanceof PhpParser\Node\Name
|
||||
&& in_array(strtolower($conditional->right->name->parts[0]), ['count', 'sizeof'])
|
||||
&& in_array(strtolower($conditional->right->name->getFirst()), ['count', 'sizeof'])
|
||||
&& $conditional->right->getArgs();
|
||||
|
||||
$operator_greater_than_or_equal =
|
||||
@@ -1633,7 +1633,7 @@ class AssertionFinder
|
||||
) {
|
||||
$left_count = $conditional->left instanceof PhpParser\Node\Expr\FuncCall
|
||||
&& $conditional->left->name instanceof PhpParser\Node\Name
|
||||
&& in_array(strtolower($conditional->left->name->parts[0]), ['count', 'sizeof'])
|
||||
&& in_array(strtolower($conditional->left->name->getFirst()), ['count', 'sizeof'])
|
||||
&& $conditional->left->getArgs();
|
||||
|
||||
if ($left_count && $conditional->right instanceof PhpParser\Node\Scalar\LNumber) {
|
||||
@@ -1644,7 +1644,7 @@ class AssertionFinder
|
||||
|
||||
$right_count = $conditional->right instanceof PhpParser\Node\Expr\FuncCall
|
||||
&& $conditional->right->name instanceof PhpParser\Node\Name
|
||||
&& in_array(strtolower($conditional->right->name->parts[0]), ['count', 'sizeof'])
|
||||
&& in_array(strtolower($conditional->right->name->getFirst()), ['count', 'sizeof'])
|
||||
&& $conditional->right->getArgs();
|
||||
|
||||
if ($right_count && $conditional->left instanceof PhpParser\Node\Scalar\LNumber) {
|
||||
@@ -1785,7 +1785,7 @@ class AssertionFinder
|
||||
) {
|
||||
$left_count = $conditional->left instanceof PhpParser\Node\Expr\FuncCall
|
||||
&& $conditional->left->name instanceof PhpParser\Node\Name
|
||||
&& in_array(strtolower($conditional->left->name->parts[0]), ['count', 'sizeof']);
|
||||
&& in_array(strtolower($conditional->left->name->getFirst()), ['count', 'sizeof']);
|
||||
|
||||
$right_number = $conditional->right instanceof PhpParser\Node\Scalar\LNumber
|
||||
&& $conditional->right->value === (
|
||||
@@ -1841,8 +1841,8 @@ class AssertionFinder
|
||||
StatementsAnalyzer $source
|
||||
): bool {
|
||||
if ($stmt->name instanceof PhpParser\Node\Name
|
||||
&& (strtolower($stmt->name->parts[0]) === 'is_a'
|
||||
|| strtolower($stmt->name->parts[0]) === 'is_subclass_of')
|
||||
&& (strtolower($stmt->name->getFirst()) === 'is_a'
|
||||
|| strtolower($stmt->name->getFirst()) === 'is_subclass_of')
|
||||
&& isset($stmt->getArgs()[1])
|
||||
) {
|
||||
$second_arg = $stmt->getArgs()[1]->value;
|
||||
@@ -1920,7 +1920,7 @@ class AssertionFinder
|
||||
): array {
|
||||
$if_types = [];
|
||||
if ($stmt->name instanceof PhpParser\Node\Name
|
||||
&& ($function_name = strtolower($stmt->name->parts[0]))
|
||||
&& ($function_name = strtolower($stmt->name->getFirst()))
|
||||
&& ($assertion_type = self::getIsAssertion($function_name))
|
||||
&& $source instanceof StatementsAnalyzer
|
||||
&& ($source->getNamespace() === null //either the namespace is null
|
||||
@@ -1954,7 +1954,7 @@ class AssertionFinder
|
||||
|
||||
protected static function hasCallableCheck(PhpParser\Node\Expr\FuncCall $stmt): bool
|
||||
{
|
||||
return $stmt->name instanceof PhpParser\Node\Name && strtolower($stmt->name->parts[0]) === 'is_callable';
|
||||
return $stmt->name instanceof PhpParser\Node\Name && strtolower($stmt->name->getFirst()) === 'is_callable';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1963,7 +1963,7 @@ class AssertionFinder
|
||||
protected static function hasClassExistsCheck(PhpParser\Node\Expr\FuncCall $stmt): int
|
||||
{
|
||||
if ($stmt->name instanceof PhpParser\Node\Name
|
||||
&& strtolower($stmt->name->parts[0]) === 'class_exists'
|
||||
&& strtolower($stmt->name->getFirst()) === 'class_exists'
|
||||
) {
|
||||
if (!isset($stmt->getArgs()[1])) {
|
||||
return 2;
|
||||
@@ -1972,7 +1972,7 @@ class AssertionFinder
|
||||
$second_arg = $stmt->getArgs()[1]->value;
|
||||
|
||||
if ($second_arg instanceof PhpParser\Node\Expr\ConstFetch
|
||||
&& strtolower($second_arg->name->parts[0]) === 'true'
|
||||
&& strtolower($second_arg->name->getFirst()) === 'true'
|
||||
) {
|
||||
return 2;
|
||||
}
|
||||
@@ -1989,7 +1989,7 @@ class AssertionFinder
|
||||
protected static function hasTraitExistsCheck(PhpParser\Node\Expr\FuncCall $stmt): int
|
||||
{
|
||||
if ($stmt->name instanceof PhpParser\Node\Name
|
||||
&& strtolower($stmt->name->parts[0]) === 'trait_exists'
|
||||
&& strtolower($stmt->name->getFirst()) === 'trait_exists'
|
||||
) {
|
||||
if (!isset($stmt->getArgs()[1])) {
|
||||
return 2;
|
||||
@@ -1998,7 +1998,7 @@ class AssertionFinder
|
||||
$second_arg = $stmt->getArgs()[1]->value;
|
||||
|
||||
if ($second_arg instanceof PhpParser\Node\Expr\ConstFetch
|
||||
&& strtolower($second_arg->name->parts[0]) === 'true'
|
||||
&& strtolower($second_arg->name->getFirst()) === 'true'
|
||||
) {
|
||||
return 2;
|
||||
}
|
||||
@@ -2011,29 +2011,29 @@ class AssertionFinder
|
||||
|
||||
protected static function hasEnumExistsCheck(PhpParser\Node\Expr\FuncCall $stmt): bool
|
||||
{
|
||||
return $stmt->name instanceof PhpParser\Node\Name && strtolower($stmt->name->parts[0]) === 'enum_exists';
|
||||
return $stmt->name instanceof PhpParser\Node\Name && strtolower($stmt->name->getFirst()) === 'enum_exists';
|
||||
}
|
||||
|
||||
protected static function hasInterfaceExistsCheck(PhpParser\Node\Expr\FuncCall $stmt): bool
|
||||
{
|
||||
return $stmt->name instanceof PhpParser\Node\Name && strtolower($stmt->name->parts[0]) === 'interface_exists';
|
||||
return $stmt->name instanceof PhpParser\Node\Name && strtolower($stmt->name->getFirst()) === 'interface_exists';
|
||||
}
|
||||
|
||||
protected static function hasFunctionExistsCheck(PhpParser\Node\Expr\FuncCall $stmt): bool
|
||||
{
|
||||
return $stmt->name instanceof PhpParser\Node\Name && strtolower($stmt->name->parts[0]) === 'function_exists';
|
||||
return $stmt->name instanceof PhpParser\Node\Name && strtolower($stmt->name->getFirst()) === 'function_exists';
|
||||
}
|
||||
|
||||
protected static function hasInArrayCheck(PhpParser\Node\Expr\FuncCall $stmt): bool
|
||||
{
|
||||
if ($stmt->name instanceof PhpParser\Node\Name
|
||||
&& strtolower($stmt->name->parts[0]) === 'in_array'
|
||||
&& strtolower($stmt->name->getFirst()) === 'in_array'
|
||||
&& isset($stmt->getArgs()[2])
|
||||
) {
|
||||
$second_arg = $stmt->getArgs()[2]->value;
|
||||
|
||||
if ($second_arg instanceof PhpParser\Node\Expr\ConstFetch
|
||||
&& strtolower($second_arg->name->parts[0]) === 'true'
|
||||
&& strtolower($second_arg->name->getFirst()) === 'true'
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
@@ -2045,12 +2045,12 @@ class AssertionFinder
|
||||
protected static function hasNonEmptyCountCheck(PhpParser\Node\Expr\FuncCall $stmt): bool
|
||||
{
|
||||
return $stmt->name instanceof PhpParser\Node\Name &&
|
||||
in_array(strtolower($stmt->name->parts[0]), ['count', 'sizeof']);
|
||||
in_array(strtolower($stmt->name->getFirst()), ['count', 'sizeof']);
|
||||
}
|
||||
|
||||
protected static function hasArrayKeyExistsCheck(PhpParser\Node\Expr\FuncCall $stmt): bool
|
||||
{
|
||||
return $stmt->name instanceof PhpParser\Node\Name && strtolower($stmt->name->parts[0]) === 'array_key_exists';
|
||||
return $stmt->name instanceof PhpParser\Node\Name && strtolower($stmt->name->getFirst()) === 'array_key_exists';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2254,6 +2254,7 @@ class AssertionFinder
|
||||
}
|
||||
|
||||
/**
|
||||
* @psalm-suppress MoreSpecificReturnType
|
||||
* @param PhpParser\Node\Expr\BinaryOp\NotIdentical|PhpParser\Node\Expr\BinaryOp\NotEqual $conditional
|
||||
* @return list<non-empty-array<string, non-empty-list<non-empty-list<Assertion>>>>
|
||||
*/
|
||||
@@ -2325,15 +2326,45 @@ class AssertionFinder
|
||||
}
|
||||
|
||||
if (count($notif_types) === 1) {
|
||||
$notif_types = $notif_types[0];
|
||||
$notif_type = $notif_types[0];
|
||||
|
||||
if (count($notif_types) === 1) {
|
||||
$if_types = Algebra::negateTypes($notif_types);
|
||||
if (count($notif_type) === 1) {
|
||||
$if_types = Algebra::negateTypes($notif_type);
|
||||
}
|
||||
}
|
||||
|
||||
$if_types = $if_types ? [$if_types] : [];
|
||||
|
||||
if ($if_types === [] && count($notif_types) === 2) {
|
||||
$check_var_assertion = null;
|
||||
$check_var = null;
|
||||
foreach ($notif_types as $notif_type) {
|
||||
foreach ($notif_type as $var => $assertions) {
|
||||
if (count($assertions) !== 1 || count($assertions[0]) !== 1) {
|
||||
$if_types = [];
|
||||
break 2;
|
||||
}
|
||||
|
||||
$is_not_assertion = $assertions[0][0] instanceof IsNotType ? true : false;
|
||||
if (!isset($check_var)) {
|
||||
$check_var_assertion = $is_not_assertion;
|
||||
$check_var = $var;
|
||||
continue;
|
||||
}
|
||||
|
||||
// only if we have 1 IsType and 1 IsNotType assertion for same variable
|
||||
if ($check_var !== $var
|
||||
|| !isset($check_var_assertion)
|
||||
|| $check_var_assertion === $is_not_assertion) {
|
||||
$if_types = [];
|
||||
break 2;
|
||||
}
|
||||
}
|
||||
|
||||
$if_types[] = Algebra::negateTypes($notif_type);
|
||||
}
|
||||
}
|
||||
|
||||
if ($codebase
|
||||
&& $source instanceof StatementsAnalyzer
|
||||
&& ($var_type = $source->node_data->getType($base_conditional))
|
||||
@@ -2373,6 +2404,7 @@ class AssertionFinder
|
||||
}
|
||||
}
|
||||
|
||||
/** @psalm-suppress LessSpecificReturnStatement */
|
||||
return $if_types;
|
||||
}
|
||||
|
||||
@@ -2951,6 +2983,7 @@ class AssertionFinder
|
||||
}
|
||||
|
||||
/**
|
||||
* @psalm-suppress MoreSpecificReturnType
|
||||
* @param PhpParser\Node\Expr\BinaryOp\Identical|PhpParser\Node\Expr\BinaryOp\Equal $conditional
|
||||
* @return list<non-empty-array<string, non-empty-list<non-empty-list<Assertion>>>>
|
||||
*/
|
||||
@@ -3022,15 +3055,48 @@ class AssertionFinder
|
||||
}
|
||||
|
||||
if (count($notif_types) === 1) {
|
||||
$notif_types = $notif_types[0];
|
||||
$notif_type = $notif_types[0];
|
||||
|
||||
if (count($notif_types) === 1) {
|
||||
$if_types = Algebra::negateTypes($notif_types);
|
||||
if (count($notif_type) === 1) {
|
||||
$if_types = Algebra::negateTypes($notif_type);
|
||||
}
|
||||
}
|
||||
|
||||
$if_types = $if_types ? [$if_types] : [];
|
||||
|
||||
// @psalm-assert-if-true and @psalm-assert-if-false for same variable in same function, e.g. array/list cases
|
||||
// @todo optionally extend this to arbitrary number of assert-if cases of multiple variables in the function
|
||||
// same code above too
|
||||
if ($if_types === [] && count($notif_types) === 2) {
|
||||
$check_var_assertion = null;
|
||||
$check_var = null;
|
||||
foreach ($notif_types as $notif_type) {
|
||||
foreach ($notif_type as $var => $assertions) {
|
||||
if (count($assertions) !== 1 || count($assertions[0]) !== 1) {
|
||||
$if_types = [];
|
||||
break 2;
|
||||
}
|
||||
|
||||
$is_not_assertion = $assertions[0][0] instanceof IsNotType ? true : false;
|
||||
if (!isset($check_var)) {
|
||||
$check_var_assertion = $is_not_assertion;
|
||||
$check_var = $var;
|
||||
continue;
|
||||
}
|
||||
|
||||
// only if we have 1 IsType and 1 IsNotType assertion for same variable
|
||||
if ($check_var !== $var
|
||||
|| !isset($check_var_assertion)
|
||||
|| $check_var_assertion === $is_not_assertion) {
|
||||
$if_types = [];
|
||||
break 2;
|
||||
}
|
||||
}
|
||||
|
||||
$if_types[] = Algebra::negateTypes($notif_type);
|
||||
}
|
||||
}
|
||||
|
||||
if ($codebase
|
||||
&& $source instanceof StatementsAnalyzer
|
||||
&& ($var_type = $source->node_data->getType($base_conditional))
|
||||
@@ -3066,6 +3132,7 @@ class AssertionFinder
|
||||
}
|
||||
}
|
||||
|
||||
/** @psalm-suppress LessSpecificReturnStatement */
|
||||
return $if_types;
|
||||
}
|
||||
|
||||
@@ -3408,56 +3475,53 @@ class AssertionFinder
|
||||
throw new UnexpectedValueException('$typed_value_position value');
|
||||
}
|
||||
|
||||
if ($var_name && $var_type) {
|
||||
$identical = $conditional instanceof PhpParser\Node\Expr\BinaryOp\Identical
|
||||
|| ($other_type
|
||||
&& (($var_type->isString(true) && $other_type->isString(true))
|
||||
|| ($var_type->isInt(true) && $other_type->isInt(true))
|
||||
|| ($var_type->isFloat() && $other_type->isFloat())
|
||||
)
|
||||
);
|
||||
//soit on a un === explicite, soit on compare des types strictement égaux
|
||||
$identical = $conditional instanceof PhpParser\Node\Expr\BinaryOp\Identical
|
||||
|| ($other_type && $var_type
|
||||
&& (($var_type->isString(true) && $other_type->isString(true))
|
||||
|| ($var_type->isInt(true) && $other_type->isInt(true))
|
||||
|| ($var_type->isFloat() && $other_type->isFloat())
|
||||
)
|
||||
);
|
||||
|
||||
if (count($var_type->getAtomicTypes()) === 1) {
|
||||
$orred_types = [];
|
||||
if ($var_name
|
||||
&& $var_type
|
||||
&& !$var_type->isMixed()
|
||||
&& count($var_type->getAtomicTypes()) === 1
|
||||
) {
|
||||
$orred_types = [];
|
||||
|
||||
foreach ($var_type->getAtomicTypes() as $atomic_var_type) {
|
||||
if ($identical) {
|
||||
$orred_types[] = new IsIdentical($atomic_var_type);
|
||||
} else {
|
||||
$orred_types[] = new IsLooselyEqual($atomic_var_type);
|
||||
}
|
||||
foreach ($var_type->getAtomicTypes() as $atomic_var_type) {
|
||||
if ($identical) {
|
||||
$orred_types[] = new IsIdentical($atomic_var_type);
|
||||
} else {
|
||||
$orred_types[] = new IsLooselyEqual($atomic_var_type);
|
||||
}
|
||||
|
||||
$if_types[$var_name] = [$orred_types];
|
||||
}
|
||||
|
||||
if ($other_var_name
|
||||
&& $other_type
|
||||
&& !$other_type->isMixed()
|
||||
&& count($other_type->getAtomicTypes()) === 1
|
||||
) {
|
||||
$orred_types = [];
|
||||
|
||||
foreach ($other_type->getAtomicTypes() as $atomic_other_type) {
|
||||
if ($identical) {
|
||||
$orred_types[] = new IsIdentical($atomic_other_type);
|
||||
} else {
|
||||
$orred_types[] = new IsLooselyEqual($atomic_other_type);
|
||||
}
|
||||
}
|
||||
|
||||
$if_types[$other_var_name] = [$orred_types];
|
||||
}
|
||||
$if_types[$var_name] = [$orred_types];
|
||||
}
|
||||
|
||||
if ($codebase
|
||||
if ($other_var_name
|
||||
&& $other_type
|
||||
&& $var_type
|
||||
&& ($conditional instanceof PhpParser\Node\Expr\BinaryOp\Identical
|
||||
|| ($other_type->isString()
|
||||
&& $var_type->isString())
|
||||
)
|
||||
&& !$other_type->isMixed()
|
||||
&& count($other_type->getAtomicTypes()) === 1
|
||||
&& $other_var_name[0] === '$' //don't try to assert on constant or other, it's too perf consuming
|
||||
) {
|
||||
$orred_types = [];
|
||||
|
||||
foreach ($other_type->getAtomicTypes() as $atomic_other_type) {
|
||||
if ($identical) {
|
||||
$orred_types[] = new IsIdentical($atomic_other_type);
|
||||
} else {
|
||||
$orred_types[] = new IsLooselyEqual($atomic_other_type);
|
||||
}
|
||||
}
|
||||
|
||||
$if_types[$other_var_name] = [$orred_types];
|
||||
}
|
||||
|
||||
if ($codebase && $other_type && $var_type && $identical) {
|
||||
self::handleParadoxicalAssertions(
|
||||
$source,
|
||||
$var_type,
|
||||
@@ -3486,8 +3550,8 @@ class AssertionFinder
|
||||
&& $expr->getArgs()[0]->value->name instanceof PhpParser\Node\Identifier
|
||||
&& strtolower($expr->getArgs()[0]->value->name->name) === 'class'
|
||||
&& $expr->getArgs()[0]->value->class instanceof PhpParser\Node\Name
|
||||
&& count($expr->getArgs()[0]->value->class->parts) === 1
|
||||
&& strtolower($expr->getArgs()[0]->value->class->parts[0]) === 'static'
|
||||
&& count($expr->getArgs()[0]->value->class->getParts()) === 1
|
||||
&& strtolower($expr->getArgs()[0]->value->class->getFirst()) === 'static'
|
||||
) {
|
||||
$first_var_name = '$this';
|
||||
}
|
||||
@@ -3498,14 +3562,14 @@ class AssertionFinder
|
||||
$third_arg = $expr->getArgs()[2]->value ?? null;
|
||||
|
||||
if ($third_arg instanceof PhpParser\Node\Expr\ConstFetch) {
|
||||
if (!in_array(strtolower($third_arg->name->parts[0]), ['true', 'false'])) {
|
||||
if (!in_array(strtolower($third_arg->name->getFirst()), ['true', 'false'])) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$third_arg_value = strtolower($third_arg->name->parts[0]);
|
||||
$third_arg_value = strtolower($third_arg->name->getFirst());
|
||||
} else {
|
||||
$third_arg_value = $expr->name instanceof PhpParser\Node\Name
|
||||
&& strtolower($expr->name->parts[0]) === 'is_subclass_of'
|
||||
&& strtolower($expr->name->getFirst()) === 'is_subclass_of'
|
||||
? 'true'
|
||||
: 'false';
|
||||
}
|
||||
@@ -3532,18 +3596,18 @@ class AssertionFinder
|
||||
) {
|
||||
$class_node = $second_arg->class;
|
||||
|
||||
if ($class_node->parts === ['static']) {
|
||||
if ($class_node->getParts() === ['static']) {
|
||||
if ($this_class_name) {
|
||||
$object = new TNamedObject($this_class_name, true);
|
||||
|
||||
$if_types[$first_var_name] = [[new IsAClass($object, $third_arg_value === 'true')]];
|
||||
}
|
||||
} elseif ($class_node->parts === ['self']) {
|
||||
} elseif ($class_node->getParts() === ['self']) {
|
||||
if ($this_class_name) {
|
||||
$object = new TNamedObject($this_class_name);
|
||||
$if_types[$first_var_name] = [[new IsAClass($object, $third_arg_value === 'true')]];
|
||||
}
|
||||
} elseif ($class_node->parts === ['parent']) {
|
||||
} elseif ($class_node->getParts() === ['parent']) {
|
||||
// do nothing
|
||||
} else {
|
||||
$object = new TNamedObject(
|
||||
|
||||
@@ -981,17 +981,23 @@ class AssignmentAnalyzer
|
||||
$context->references_to_external_scope[$lhs_var_id] = true;
|
||||
}
|
||||
if (strpos($rhs_var_id, '->') !== false) {
|
||||
IssueBuffer::maybeAdd(new UnsupportedPropertyReferenceUsage(
|
||||
new CodeLocation($statements_analyzer->getSource(), $stmt),
|
||||
));
|
||||
IssueBuffer::maybeAdd(
|
||||
new UnsupportedPropertyReferenceUsage(
|
||||
new CodeLocation($statements_analyzer->getSource(), $stmt),
|
||||
),
|
||||
$statements_analyzer->getSuppressedIssues(),
|
||||
);
|
||||
// Reference to object property, we always consider object properties to be an external scope for references
|
||||
// TODO handle differently so it's detected as unused if the object is unused?
|
||||
$context->references_to_external_scope[$lhs_var_id] = true;
|
||||
}
|
||||
if (strpos($rhs_var_id, '::') !== false) {
|
||||
IssueBuffer::maybeAdd(new UnsupportedPropertyReferenceUsage(
|
||||
new CodeLocation($statements_analyzer->getSource(), $stmt),
|
||||
));
|
||||
IssueBuffer::maybeAdd(
|
||||
new UnsupportedPropertyReferenceUsage(
|
||||
new CodeLocation($statements_analyzer->getSource(), $stmt),
|
||||
),
|
||||
$statements_analyzer->getSuppressedIssues(),
|
||||
);
|
||||
}
|
||||
|
||||
$lhs_location = new CodeLocation($statements_analyzer->getSource(), $stmt->var);
|
||||
|
||||
@@ -815,11 +815,9 @@ class ArithmeticOpAnalyzer
|
||||
if ($right_type_part instanceof TLiteralInt) {
|
||||
$literal_value_max = $right_type_part->value - 1;
|
||||
if ($always_positive) {
|
||||
$result_type = new Union([new TIntRange(0, $literal_value_max)]);
|
||||
$result_type = Type::getIntRange(0, $literal_value_max);
|
||||
} else {
|
||||
$result_type = new Union(
|
||||
[new TIntRange(-$literal_value_max, $literal_value_max)],
|
||||
);
|
||||
$result_type = Type::getIntRange(-$literal_value_max, $literal_value_max);
|
||||
}
|
||||
} else {
|
||||
if ($always_positive) {
|
||||
@@ -830,7 +828,7 @@ class ArithmeticOpAnalyzer
|
||||
}
|
||||
} else {
|
||||
$result_type = Type::combineUnionTypes(
|
||||
$always_positive ? new Union([new TIntRange(1, null)]) : Type::getInt(true),
|
||||
$always_positive ? Type::getIntRange(1, null) : Type::getInt(true),
|
||||
$result_type,
|
||||
);
|
||||
}
|
||||
@@ -1072,7 +1070,7 @@ class ArithmeticOpAnalyzer
|
||||
$min_value = $calculated_min_type !== null ? $calculated_min_type->getSingleIntLiteral()->value : null;
|
||||
$max_value = $calculated_max_type !== null ? $calculated_max_type->getSingleIntLiteral()->value : null;
|
||||
|
||||
$new_result_type = new Union([new TIntRange($min_value, $max_value)]);
|
||||
$new_result_type = Type::getIntRange($min_value, $max_value);
|
||||
|
||||
$result_type = Type::combineUnionTypes($new_result_type, $result_type);
|
||||
}
|
||||
@@ -1122,7 +1120,7 @@ class ArithmeticOpAnalyzer
|
||||
$min_value = min($x_1 * $y_1, $x_1 * $y_2, $x_2 * $y_1, $x_2 * $y_2);
|
||||
$max_value = max($x_1 * $y_1, $x_1 * $y_2, $x_2 * $y_1, $x_2 * $y_2);
|
||||
|
||||
$new_result_type = new Union([new TIntRange($min_value, $max_value)]);
|
||||
$new_result_type = Type::getIntRange($min_value, $max_value);
|
||||
} elseif ($right_type_part->isPositiveOrZero() && $left_type_part->isPositiveOrZero()) {
|
||||
// both operands are positive, result will be only positive
|
||||
$min_operand1 = $left_type_part->min_bound;
|
||||
@@ -1156,7 +1154,7 @@ class ArithmeticOpAnalyzer
|
||||
$min_value = $calculated_min_type !== null ? $calculated_min_type->getSingleIntLiteral()->value : null;
|
||||
$max_value = $calculated_max_type !== null ? $calculated_max_type->getSingleIntLiteral()->value : null;
|
||||
|
||||
$new_result_type = new Union([new TIntRange($min_value, $max_value)]);
|
||||
$new_result_type = Type::getIntRange($min_value, $max_value);
|
||||
} elseif ($right_type_part->isPositiveOrZero() && $left_type_part->isNegativeOrZero()) {
|
||||
// one operand is negative, result will be negative and we have to check min vs max
|
||||
$min_operand1 = $left_type_part->max_bound;
|
||||
@@ -1194,7 +1192,7 @@ class ArithmeticOpAnalyzer
|
||||
[$min_value, $max_value] = [$max_value, $min_value];
|
||||
}
|
||||
|
||||
$new_result_type = new Union([new TIntRange($min_value, $max_value)]);
|
||||
$new_result_type = Type::getIntRange($min_value, $max_value);
|
||||
} elseif ($right_type_part->isNegativeOrZero() && $left_type_part->isPositiveOrZero()) {
|
||||
// one operand is negative, result will be negative and we have to check min vs max
|
||||
$min_operand1 = $left_type_part->min_bound;
|
||||
@@ -1232,7 +1230,7 @@ class ArithmeticOpAnalyzer
|
||||
[$min_value, $max_value] = [$max_value, $min_value];
|
||||
}
|
||||
|
||||
$new_result_type = new Union([new TIntRange($min_value, $max_value)]);
|
||||
$new_result_type = Type::getIntRange($min_value, $max_value);
|
||||
} elseif ($right_type_part->isNegativeOrZero() && $left_type_part->isNegativeOrZero()) {
|
||||
// both operand are negative, result will be positive
|
||||
$min_operand1 = $left_type_part->max_bound;
|
||||
@@ -1266,7 +1264,7 @@ class ArithmeticOpAnalyzer
|
||||
$min_value = $calculated_min_type !== null ? $calculated_min_type->getSingleIntLiteral()->value : null;
|
||||
$max_value = $calculated_max_type !== null ? $calculated_max_type->getSingleIntLiteral()->value : null;
|
||||
|
||||
$new_result_type = new Union([new TIntRange($min_value, $max_value)]);
|
||||
$new_result_type = Type::getIntRange($min_value, $max_value);
|
||||
} else {
|
||||
$new_result_type = Type::getInt(true);
|
||||
}
|
||||
@@ -1283,7 +1281,7 @@ class ArithmeticOpAnalyzer
|
||||
//If Pow second operand is negative, the result will be float, if it's 0, it will be 1/-1, else positive
|
||||
if ($left_type_part->isPositive()) {
|
||||
if ($right_type_part->isPositive()) {
|
||||
$new_result_type = new Union([new TIntRange(1, null)]);
|
||||
$new_result_type = Type::getIntRange(1, null);
|
||||
} elseif ($right_type_part->isNegative()) {
|
||||
$new_result_type = Type::getFloat();
|
||||
} elseif ($right_type_part->min_bound === 0 && $right_type_part->max_bound === 0) {
|
||||
@@ -1296,9 +1294,9 @@ class ArithmeticOpAnalyzer
|
||||
if ($right_type_part->isPositive()) {
|
||||
if ($right_type_part->min_bound === $right_type_part->max_bound) {
|
||||
if ($right_type_part->max_bound % 2 === 0) {
|
||||
$new_result_type = new Union([new TIntRange(1, null)]);
|
||||
$new_result_type = Type::getIntRange(1, null);
|
||||
} else {
|
||||
$new_result_type = new Union([new TIntRange(null, -1)]);
|
||||
$new_result_type = Type::getIntRange(null, -1);
|
||||
}
|
||||
} else {
|
||||
$new_result_type = Type::getInt(true);
|
||||
@@ -1330,7 +1328,7 @@ class ArithmeticOpAnalyzer
|
||||
if ($right_type_part->min_bound === $right_type_part->max_bound
|
||||
&& $right_type_part->max_bound % 2 === 0
|
||||
) {
|
||||
$new_result_type = new Union([new TIntRange(1, null)]);
|
||||
$new_result_type = Type::getIntRange(1, null);
|
||||
} else {
|
||||
$new_result_type = Type::getInt(true);
|
||||
}
|
||||
@@ -1361,18 +1359,18 @@ class ArithmeticOpAnalyzer
|
||||
if ($left_type_part->isPositiveOrZero()) {
|
||||
if ($right_type_part->isPositive()) {
|
||||
$max = $right_type_part->min_bound - 1;
|
||||
$new_result_type = new Union([new TIntRange(0, $max)]);
|
||||
$new_result_type = Type::getIntRange(0, $max);
|
||||
} else {
|
||||
$max = $right_type_part->min_bound + 1;
|
||||
$new_result_type = new Union([new TIntRange($max, 0)]);
|
||||
$new_result_type = Type::getIntRange($max, 0);
|
||||
}
|
||||
} elseif ($left_type_part->isNegativeOrZero()) {
|
||||
if ($right_type_part->isPositive()) {
|
||||
$max = $right_type_part->min_bound - 1;
|
||||
$new_result_type = new Union([new TIntRange(-$max, 0)]);
|
||||
$new_result_type = Type::getIntRange(-$max, 0);
|
||||
} else {
|
||||
$max = $right_type_part->min_bound + 1;
|
||||
$new_result_type = new Union([new TIntRange(-$max, 0)]);
|
||||
$new_result_type = Type::getIntRange(-$max, 0);
|
||||
}
|
||||
} else {
|
||||
if ($right_type_part->isPositive()) {
|
||||
@@ -1380,7 +1378,7 @@ class ArithmeticOpAnalyzer
|
||||
} else {
|
||||
$max = -$right_type_part->min_bound - 1;
|
||||
}
|
||||
$new_result_type = new Union([new TIntRange(-$max, $max)]);
|
||||
$new_result_type = Type::getIntRange(-$max, $max);
|
||||
}
|
||||
}
|
||||
} elseif ($right_type_part->isPositive()) {
|
||||
@@ -1394,15 +1392,15 @@ class ArithmeticOpAnalyzer
|
||||
$new_result_type = Type::getListKey();
|
||||
}
|
||||
} elseif ($left_type_part->isNegativeOrZero()) {
|
||||
$new_result_type = new Union([new TIntRange(null, 0)]);
|
||||
$new_result_type = Type::getIntRange(null, 0);
|
||||
} else {
|
||||
$new_result_type = Type::getInt(true);
|
||||
}
|
||||
} elseif ($right_type_part->isNegative()) {
|
||||
if ($left_type_part->isPositiveOrZero()) {
|
||||
$new_result_type = new Union([new TIntRange(null, 0)]);
|
||||
$new_result_type = Type::getIntRange(null, 0);
|
||||
} elseif ($left_type_part->isNegativeOrZero()) {
|
||||
$new_result_type = new Union([new TIntRange(null, 0)]);
|
||||
$new_result_type = Type::getIntRange(null, 0);
|
||||
} else {
|
||||
$new_result_type = Type::getInt(true);
|
||||
}
|
||||
|
||||
@@ -261,7 +261,7 @@ class BinaryOpAnalyzer
|
||||
|| $stmt instanceof PhpParser\Node\Expr\BinaryOp\NotIdentical)
|
||||
&& $stmt->left instanceof PhpParser\Node\Expr\FuncCall
|
||||
&& $stmt->left->name instanceof PhpParser\Node\Name
|
||||
&& $stmt->left->name->parts === ['substr']
|
||||
&& $stmt->left->name->getParts() === ['substr']
|
||||
&& isset($stmt->left->getArgs()[1])
|
||||
&& $stmt_right_type
|
||||
&& $stmt_right_type->hasLiteralString()
|
||||
|
||||
@@ -268,7 +268,12 @@ class ArgumentsAnalyzer
|
||||
|
||||
$inferred_arg_type = $statements_analyzer->node_data->getType($arg->value);
|
||||
|
||||
if (null !== $inferred_arg_type && null !== $template_result && null !== $param && null !== $param->type) {
|
||||
if (null !== $inferred_arg_type
|
||||
&& null !== $template_result
|
||||
&& null !== $param
|
||||
&& null !== $param->type
|
||||
&& !$arg->unpack
|
||||
) {
|
||||
$codebase = $statements_analyzer->getCodebase();
|
||||
|
||||
TemplateStandinTypeReplacer::fillTemplateResult(
|
||||
|
||||
@@ -102,7 +102,7 @@ class FunctionCallAnalyzer extends CallAnalyzer
|
||||
&& isset($stmt->getArgs()[0])
|
||||
&& !$stmt->getArgs()[0]->unpack
|
||||
) {
|
||||
$original_function_id = implode('\\', $function_name->parts);
|
||||
$original_function_id = implode('\\', $function_name->getParts());
|
||||
|
||||
if ($original_function_id === 'call_user_func') {
|
||||
$other_args = array_slice($stmt->getArgs(), 1);
|
||||
@@ -160,7 +160,7 @@ class FunctionCallAnalyzer extends CallAnalyzer
|
||||
$set_inside_conditional = false;
|
||||
|
||||
if ($function_name instanceof PhpParser\Node\Name
|
||||
&& $function_name->parts === ['assert']
|
||||
&& $function_name->getParts() === ['assert']
|
||||
&& !$context->inside_conditional
|
||||
) {
|
||||
$context->inside_conditional = true;
|
||||
@@ -235,7 +235,10 @@ class FunctionCallAnalyzer extends CallAnalyzer
|
||||
$function_call_info->function_id,
|
||||
);
|
||||
|
||||
$template_result->lower_bounds += $already_inferred_lower_bounds;
|
||||
$template_result->lower_bounds = array_merge(
|
||||
$template_result->lower_bounds,
|
||||
$already_inferred_lower_bounds,
|
||||
);
|
||||
|
||||
if ($function_name instanceof PhpParser\Node\Name && $function_call_info->function_id) {
|
||||
$stmt_type = FunctionCallReturnTypeFetcher::fetch(
|
||||
@@ -319,7 +322,7 @@ class FunctionCallAnalyzer extends CallAnalyzer
|
||||
}
|
||||
|
||||
if ($function_name instanceof PhpParser\Node\Name
|
||||
&& $function_name->parts === ['assert']
|
||||
&& $function_name->getParts() === ['assert']
|
||||
&& isset($stmt->getArgs()[0])
|
||||
) {
|
||||
self::processAssertFunctionEffects(
|
||||
@@ -437,7 +440,7 @@ class FunctionCallAnalyzer extends CallAnalyzer
|
||||
$codebase = $statements_analyzer->getCodebase();
|
||||
$codebase_functions = $codebase->functions;
|
||||
|
||||
$original_function_id = implode('\\', $function_name->parts);
|
||||
$original_function_id = $function_name->toString();
|
||||
|
||||
if (!$function_name instanceof PhpParser\Node\Name\FullyQualified) {
|
||||
$function_call_info->function_id = $codebase_functions->getFullyQualifiedFunctionNameFromString(
|
||||
@@ -487,7 +490,7 @@ class FunctionCallAnalyzer extends CallAnalyzer
|
||||
$is_predefined = true;
|
||||
|
||||
$is_maybe_root_function = !$function_name instanceof PhpParser\Node\Name\FullyQualified
|
||||
&& count($function_name->parts) === 1;
|
||||
&& count($function_name->getParts()) === 1;
|
||||
|
||||
$args = $stmt->isFirstClassCallable() ? [] : $stmt->getArgs();
|
||||
|
||||
|
||||
@@ -383,7 +383,7 @@ class FunctionCallReturnTypeFetcher
|
||||
if ($min === $max) {
|
||||
return new Union([new TLiteralInt($max)]);
|
||||
}
|
||||
return new Union([new TIntRange($min, $max)]);
|
||||
return Type::getIntRange($min, $max);
|
||||
}
|
||||
|
||||
if ($atomic_types['array'] instanceof TArray
|
||||
|
||||
@@ -58,7 +58,6 @@ use Psalm\Type\Union;
|
||||
|
||||
use function array_map;
|
||||
use function array_values;
|
||||
use function implode;
|
||||
use function in_array;
|
||||
use function md5;
|
||||
use function preg_match;
|
||||
@@ -94,7 +93,7 @@ class NewAnalyzer extends CallAnalyzer
|
||||
}
|
||||
|
||||
if ($stmt->class instanceof PhpParser\Node\Name) {
|
||||
if (!in_array(strtolower($stmt->class->parts[0]), ['self', 'static', 'parent'], true)) {
|
||||
if (!in_array(strtolower($stmt->class->getFirst()), ['self', 'static', 'parent'], true)) {
|
||||
$aliases = $statements_analyzer->getAliases();
|
||||
|
||||
if ($context->calling_method_id
|
||||
@@ -102,7 +101,7 @@ class NewAnalyzer extends CallAnalyzer
|
||||
) {
|
||||
$codebase->file_reference_provider->addMethodReferenceToClassMember(
|
||||
$context->calling_method_id,
|
||||
'use:' . $stmt->class->parts[0] . ':' . md5($statements_analyzer->getFilePath()),
|
||||
'use:' . $stmt->class->getFirst() . ':' . md5($statements_analyzer->getFilePath()),
|
||||
false,
|
||||
);
|
||||
}
|
||||
@@ -114,7 +113,7 @@ class NewAnalyzer extends CallAnalyzer
|
||||
|
||||
$fq_class_name = $codebase->classlikes->getUnAliasedName($fq_class_name);
|
||||
} elseif ($context->self !== null) {
|
||||
switch ($stmt->class->parts[0]) {
|
||||
switch ($stmt->class->getFirst()) {
|
||||
case 'self':
|
||||
$class_storage = $codebase->classlike_storage_provider->get($context->self);
|
||||
$fq_class_name = $class_storage->name;
|
||||
@@ -152,7 +151,7 @@ class NewAnalyzer extends CallAnalyzer
|
||||
. ($stmt->class instanceof PhpParser\Node\Name\FullyQualified
|
||||
? '\\'
|
||||
: $statements_analyzer->getNamespace() . '-')
|
||||
. implode('\\', $stmt->class->parts),
|
||||
. $stmt->class->toString(),
|
||||
);
|
||||
}
|
||||
} elseif ($stmt->class instanceof PhpParser\Node\Stmt\Class_) {
|
||||
@@ -174,7 +173,7 @@ class NewAnalyzer extends CallAnalyzer
|
||||
if ($fq_class_name) {
|
||||
if ($codebase->alter_code
|
||||
&& $stmt->class instanceof PhpParser\Node\Name
|
||||
&& !in_array($stmt->class->parts[0], ['parent', 'static'])
|
||||
&& !in_array($stmt->class->getFirst(), ['parent', 'static'])
|
||||
) {
|
||||
$codebase->classlikes->handleClassLikeReferenceInMigration(
|
||||
$codebase,
|
||||
|
||||
@@ -56,10 +56,10 @@ class StaticCallAnalyzer extends CallAnalyzer
|
||||
if ($stmt->class instanceof PhpParser\Node\Name) {
|
||||
$fq_class_name = null;
|
||||
|
||||
if (count($stmt->class->parts) === 1
|
||||
&& in_array(strtolower($stmt->class->parts[0]), ['self', 'static', 'parent'], true)
|
||||
if (count($stmt->class->getParts()) === 1
|
||||
&& in_array(strtolower($stmt->class->getFirst()), ['self', 'static', 'parent'], true)
|
||||
) {
|
||||
if ($stmt->class->parts[0] === 'parent') {
|
||||
if ($stmt->class->getFirst() === 'parent') {
|
||||
$child_fq_class_name = $context->self;
|
||||
|
||||
$class_storage = $child_fq_class_name
|
||||
@@ -84,7 +84,7 @@ class StaticCallAnalyzer extends CallAnalyzer
|
||||
|
||||
$fq_class_name = $class_storage->name;
|
||||
} elseif ($context->self) {
|
||||
if ($stmt->class->parts[0] === 'static' && isset($context->vars_in_scope['$this'])) {
|
||||
if ($stmt->class->getFirst() === 'static' && isset($context->vars_in_scope['$this'])) {
|
||||
$fq_class_name = (string) $context->vars_in_scope['$this'];
|
||||
$lhs_type = $context->vars_in_scope['$this'];
|
||||
} else {
|
||||
@@ -93,7 +93,7 @@ class StaticCallAnalyzer extends CallAnalyzer
|
||||
} else {
|
||||
return !IssueBuffer::accepts(
|
||||
new NonStaticSelfCall(
|
||||
'Cannot use ' . $stmt->class->parts[0] . ' outside class context',
|
||||
'Cannot use ' . $stmt->class->getFirst() . ' outside class context',
|
||||
new CodeLocation($statements_analyzer->getSource(), $stmt),
|
||||
),
|
||||
$statements_analyzer->getSuppressedIssues(),
|
||||
@@ -111,7 +111,7 @@ class StaticCallAnalyzer extends CallAnalyzer
|
||||
) {
|
||||
$codebase->file_reference_provider->addMethodReferenceToClassMember(
|
||||
$context->calling_method_id,
|
||||
'use:' . $stmt->class->parts[0] . ':' . md5($statements_analyzer->getFilePath()),
|
||||
'use:' . $stmt->class->getFirst() . ':' . md5($statements_analyzer->getFilePath()),
|
||||
false,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -99,8 +99,8 @@ class AtomicStaticCallAnalyzer
|
||||
$statements_analyzer->getSuppressedIssues(),
|
||||
new ClassLikeNameOptions(
|
||||
$stmt->class instanceof PhpParser\Node\Name
|
||||
&& count($stmt->class->parts) === 1
|
||||
&& in_array(strtolower($stmt->class->parts[0]), ['self', 'static'], true),
|
||||
&& count($stmt->class->getParts()) === 1
|
||||
&& in_array(strtolower($stmt->class->getFirst()), ['self', 'static'], true),
|
||||
),
|
||||
)) {
|
||||
return;
|
||||
@@ -284,7 +284,7 @@ class AtomicStaticCallAnalyzer
|
||||
&& $fq_class_name
|
||||
&& !$moved_call
|
||||
&& $stmt->class instanceof PhpParser\Node\Name
|
||||
&& !in_array($stmt->class->parts[0], ['parent', 'static'])
|
||||
&& !in_array($stmt->class->getFirst(), ['parent', 'static'])
|
||||
) {
|
||||
$codebase->classlikes->handleClassLikeReferenceInMigration(
|
||||
$codebase,
|
||||
@@ -293,7 +293,7 @@ class AtomicStaticCallAnalyzer
|
||||
$fq_class_name,
|
||||
$context->calling_method_id,
|
||||
false,
|
||||
$stmt->class->parts[0] === 'self',
|
||||
$stmt->class->getFirst() === 'self',
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -718,7 +718,7 @@ class AtomicStaticCallAnalyzer
|
||||
if ($pseudo_method_storage->return_type) {
|
||||
return true;
|
||||
}
|
||||
} elseif ($stmt->class instanceof PhpParser\Node\Name && $stmt->class->parts[0] === 'parent'
|
||||
} elseif ($stmt->class instanceof PhpParser\Node\Name && $stmt->class->getFirst() === 'parent'
|
||||
&& !$codebase->methodExists($method_id)
|
||||
&& !$statements_analyzer->isStatic()
|
||||
) {
|
||||
@@ -846,7 +846,7 @@ class AtomicStaticCallAnalyzer
|
||||
}
|
||||
|
||||
if ((!$stmt->class instanceof PhpParser\Node\Name
|
||||
|| $stmt->class->parts[0] !== 'parent'
|
||||
|| $stmt->class->getFirst() !== 'parent'
|
||||
|| $statements_analyzer->isStatic())
|
||||
&& (
|
||||
!$context->self
|
||||
@@ -857,7 +857,7 @@ class AtomicStaticCallAnalyzer
|
||||
MethodAnalyzer::checkStatic(
|
||||
$method_id,
|
||||
($stmt->class instanceof PhpParser\Node\Name
|
||||
&& strtolower($stmt->class->parts[0]) === 'self')
|
||||
&& strtolower($stmt->class->getFirst()) === 'self')
|
||||
|| $context->self === $fq_class_name,
|
||||
!$statements_analyzer->isStatic(),
|
||||
$codebase,
|
||||
|
||||
@@ -156,7 +156,7 @@ class ExistingAtomicStaticCallAnalyzer
|
||||
|
||||
if ($found_generic_params
|
||||
&& $stmt->class instanceof PhpParser\Node\Name
|
||||
&& $stmt->class->parts === ['parent']
|
||||
&& $stmt->class->getParts() === ['parent']
|
||||
&& $context->self
|
||||
&& ($self_class_storage = $codebase->classlike_storage_provider->get($context->self))
|
||||
&& $self_class_storage->template_extended_params
|
||||
@@ -201,7 +201,7 @@ class ExistingAtomicStaticCallAnalyzer
|
||||
return;
|
||||
}
|
||||
|
||||
$fq_class_name = $stmt->class instanceof PhpParser\Node\Name && $stmt->class->parts === ['parent']
|
||||
$fq_class_name = $stmt->class instanceof PhpParser\Node\Name && $stmt->class->getParts() === ['parent']
|
||||
? (string) $statements_analyzer->getFQCLN()
|
||||
: $fq_class_name;
|
||||
|
||||
@@ -371,7 +371,7 @@ class ExistingAtomicStaticCallAnalyzer
|
||||
$new_fq_class_name,
|
||||
$context->calling_method_id,
|
||||
strtolower($old_declaring_fq_class_name) !== strtolower($new_fq_class_name),
|
||||
$stmt->class->parts[0] === 'self',
|
||||
$stmt->class->getFirst() === 'self',
|
||||
)) {
|
||||
$moved_call = true;
|
||||
}
|
||||
@@ -519,8 +519,8 @@ class ExistingAtomicStaticCallAnalyzer
|
||||
$lhs_type_part->defining_class,
|
||||
);
|
||||
} elseif ($stmt->class instanceof PhpParser\Node\Name
|
||||
&& count($stmt->class->parts) === 1
|
||||
&& in_array(strtolower($stmt->class->parts[0]), ['self', 'static', 'parent'], true)
|
||||
&& count($stmt->class->getParts()) === 1
|
||||
&& in_array(strtolower($stmt->class->getFirst()), ['self', 'static', 'parent'], true)
|
||||
&& $lhs_type_part instanceof TNamedObject
|
||||
&& $context->self
|
||||
) {
|
||||
|
||||
@@ -505,7 +505,7 @@ class CallAnalyzer
|
||||
&& $callable_arg->left->class instanceof Name
|
||||
&& $callable_arg->left->name instanceof Identifier
|
||||
&& strtolower($callable_arg->left->name->name) === 'class'
|
||||
&& !in_array(strtolower($callable_arg->left->class->parts[0]), ['self', 'static', 'parent'])
|
||||
&& !in_array(strtolower($callable_arg->left->class->getFirst()), ['self', 'static', 'parent'])
|
||||
&& $callable_arg->right instanceof PhpParser\Node\Scalar\String_
|
||||
&& preg_match('/^::[A-Za-z0-9]+$/', $callable_arg->right->value)
|
||||
) {
|
||||
|
||||
@@ -72,7 +72,7 @@ class ClassConstAnalyzer
|
||||
$statements_analyzer->node_data->setType($stmt, Type::getMixed());
|
||||
|
||||
if ($stmt->class instanceof PhpParser\Node\Name) {
|
||||
$first_part_lc = strtolower($stmt->class->parts[0]);
|
||||
$first_part_lc = strtolower($stmt->class->getFirst());
|
||||
|
||||
if ($first_part_lc === 'self' || $first_part_lc === 'static') {
|
||||
if (!$context->self) {
|
||||
@@ -128,7 +128,7 @@ class ClassConstAnalyzer
|
||||
$moved_class = false;
|
||||
|
||||
if ($codebase->alter_code
|
||||
&& !in_array($stmt->class->parts[0], ['parent', 'static'])
|
||||
&& !in_array($stmt->class->getFirst(), ['parent', 'static'])
|
||||
) {
|
||||
$moved_class = $codebase->classlikes->handleClassLikeReferenceInMigration(
|
||||
$codebase,
|
||||
@@ -137,7 +137,7 @@ class ClassConstAnalyzer
|
||||
$fq_class_name,
|
||||
$context->calling_method_id,
|
||||
false,
|
||||
$stmt->class->parts[0] === 'self',
|
||||
$stmt->class->getFirst() === 'self',
|
||||
);
|
||||
}
|
||||
|
||||
@@ -259,7 +259,7 @@ class ClassConstAnalyzer
|
||||
$class_visibility,
|
||||
$statements_analyzer,
|
||||
[],
|
||||
$stmt->class->parts[0] === "static",
|
||||
$stmt->class->getFirst() === "static",
|
||||
);
|
||||
} catch (InvalidArgumentException $_) {
|
||||
return true;
|
||||
|
||||
@@ -33,11 +33,11 @@ class ExpressionIdentifier
|
||||
&& $stmt->name instanceof PhpParser\Node\Identifier
|
||||
&& $stmt->class instanceof PhpParser\Node\Name
|
||||
) {
|
||||
if (count($stmt->class->parts) === 1
|
||||
&& in_array(strtolower($stmt->class->parts[0]), ['self', 'static', 'parent'], true)
|
||||
if (count($stmt->class->getParts()) === 1
|
||||
&& in_array(strtolower($stmt->class->getFirst()), ['self', 'static', 'parent'], true)
|
||||
) {
|
||||
if (!$this_class_name) {
|
||||
$fq_class_name = $stmt->class->parts[0];
|
||||
$fq_class_name = $stmt->class->getFirst();
|
||||
} else {
|
||||
$fq_class_name = $this_class_name;
|
||||
}
|
||||
@@ -47,7 +47,7 @@ class ExpressionIdentifier
|
||||
$stmt->class,
|
||||
$source->getAliases(),
|
||||
)
|
||||
: implode('\\', $stmt->class->parts);
|
||||
: implode('\\', $stmt->class->getParts());
|
||||
}
|
||||
|
||||
return $fq_class_name . '::$' . $stmt->name->name;
|
||||
@@ -124,7 +124,7 @@ class ExpressionIdentifier
|
||||
) {
|
||||
$offset = '$' . $stmt->dim->name;
|
||||
} elseif ($stmt->dim instanceof PhpParser\Node\Expr\ConstFetch) {
|
||||
$offset = implode('\\', $stmt->dim->name->parts);
|
||||
$offset = implode('\\', $stmt->dim->name->getParts());
|
||||
} elseif ($stmt->dim instanceof PhpParser\Node\Expr\PropertyFetch) {
|
||||
$object_id = self::getExtendedVarId($stmt->dim->var, $this_class_name, $source);
|
||||
|
||||
@@ -134,7 +134,7 @@ class ExpressionIdentifier
|
||||
} elseif ($stmt->dim instanceof PhpParser\Node\Expr\ClassConstFetch
|
||||
&& $stmt->dim->name instanceof PhpParser\Node\Identifier
|
||||
&& $stmt->dim->class instanceof PhpParser\Node\Name
|
||||
&& $stmt->dim->class->parts[0] === 'static'
|
||||
&& $stmt->dim->class->getFirst() === 'static'
|
||||
) {
|
||||
$offset = 'static::' . $stmt->dim->name;
|
||||
} elseif ($stmt->dim
|
||||
|
||||
@@ -16,7 +16,6 @@ use Psalm\Internal\Provider\NodeDataProvider;
|
||||
use Psalm\Issue\UndefinedConstant;
|
||||
use Psalm\IssueBuffer;
|
||||
use Psalm\Type;
|
||||
use Psalm\Type\Atomic\TIntRange;
|
||||
use Psalm\Type\Union;
|
||||
use ReflectionProperty;
|
||||
|
||||
@@ -36,7 +35,7 @@ class ConstFetchAnalyzer
|
||||
PhpParser\Node\Expr\ConstFetch $stmt,
|
||||
Context $context
|
||||
): void {
|
||||
$const_name = implode('\\', $stmt->name->parts);
|
||||
$const_name = $stmt->name->toString();
|
||||
|
||||
switch (strtolower($const_name)) {
|
||||
case 'null':
|
||||
@@ -175,7 +174,7 @@ class ConstFetchAnalyzer
|
||||
case 'PHP_INT_SIZE':
|
||||
case 'PHP_MAXPATHLEN':
|
||||
case 'PHP_VERSION_ID':
|
||||
return new Union([new TIntRange(1, null)]);
|
||||
return Type::getIntRange(1, null);
|
||||
|
||||
case 'PHP_FLOAT_EPSILON':
|
||||
case 'PHP_FLOAT_MAX':
|
||||
|
||||
@@ -51,10 +51,10 @@ class StaticPropertyFetchAnalyzer
|
||||
|
||||
$codebase = $statements_analyzer->getCodebase();
|
||||
|
||||
if (count($stmt->class->parts) === 1
|
||||
&& in_array(strtolower($stmt->class->parts[0]), ['self', 'static', 'parent'], true)
|
||||
if (count($stmt->class->getParts()) === 1
|
||||
&& in_array(strtolower($stmt->class->getFirst()), ['self', 'static', 'parent'], true)
|
||||
) {
|
||||
if ($stmt->class->parts[0] === 'parent') {
|
||||
if ($stmt->class->getFirst() === 'parent') {
|
||||
$fq_class_name = $statements_analyzer->getParentFQCLN();
|
||||
|
||||
if ($fq_class_name === null) {
|
||||
@@ -81,7 +81,7 @@ class StaticPropertyFetchAnalyzer
|
||||
) {
|
||||
$codebase->file_reference_provider->addMethodReferenceToClassMember(
|
||||
$context->calling_method_id,
|
||||
'use:' . $stmt->class->parts[0] . ':' . md5($statements_analyzer->getFilePath()),
|
||||
'use:' . $stmt->class->getFirst() . ':' . md5($statements_analyzer->getFilePath()),
|
||||
false,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -783,7 +783,7 @@ class VariableFetchAnalyzer
|
||||
'type' => $str,
|
||||
'tmp_name' => $str,
|
||||
'size' => Type::getListKey(),
|
||||
'error' => new Union([new TIntRange(0, 8)]),
|
||||
'error' => Type::getIntRange(0, 8),
|
||||
];
|
||||
|
||||
if ($files_full_path) {
|
||||
|
||||
@@ -335,7 +335,7 @@ class IncludeAnalyzer
|
||||
}
|
||||
} elseif ($stmt instanceof PhpParser\Node\Expr\FuncCall &&
|
||||
$stmt->name instanceof PhpParser\Node\Name &&
|
||||
$stmt->name->parts === ['dirname']
|
||||
$stmt->name->getParts() === ['dirname']
|
||||
) {
|
||||
if ($stmt->getArgs()) {
|
||||
$dir_level = 1;
|
||||
@@ -369,10 +369,14 @@ class IncludeAnalyzer
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($dir_level < 1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return dirname($evaled_path, $dir_level);
|
||||
}
|
||||
} elseif ($stmt instanceof PhpParser\Node\Expr\ConstFetch) {
|
||||
$const_name = implode('', $stmt->name->parts);
|
||||
$const_name = implode('', $stmt->name->getParts());
|
||||
|
||||
if (defined($const_name)) {
|
||||
$constant_value = constant($const_name);
|
||||
|
||||
@@ -39,7 +39,7 @@ class InstanceofAnalyzer
|
||||
if (ExpressionAnalyzer::analyze($statements_analyzer, $stmt->class, $context) === false) {
|
||||
return false;
|
||||
}
|
||||
} elseif (!in_array(strtolower($stmt->class->parts[0]), ['self', 'static', 'parent'], true)) {
|
||||
} elseif (!in_array(strtolower($stmt->class->getFirst()), ['self', 'static', 'parent'], true)) {
|
||||
if ($context->check_classes) {
|
||||
$codebase = $statements_analyzer->getCodebase();
|
||||
|
||||
@@ -62,7 +62,7 @@ class InstanceofAnalyzer
|
||||
. ($stmt->class instanceof PhpParser\Node\Name\FullyQualified
|
||||
? '\\'
|
||||
: $statements_analyzer->getNamespace() . '-')
|
||||
. implode('\\', $stmt->class->parts),
|
||||
. implode('\\', $stmt->class->getParts()),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ class MagicConstAnalyzer
|
||||
Context $context
|
||||
): void {
|
||||
if ($stmt instanceof PhpParser\Node\Scalar\MagicConst\Line) {
|
||||
$statements_analyzer->node_data->setType($stmt, Type::getInt());
|
||||
$statements_analyzer->node_data->setType($stmt, Type::getIntRange(1, null));
|
||||
} elseif ($stmt instanceof PhpParser\Node\Scalar\MagicConst\Class_) {
|
||||
$codebase = $statements_analyzer->getCodebase();
|
||||
|
||||
|
||||
@@ -78,11 +78,11 @@ class MatchAnalyzer
|
||||
if (!$switch_var_id) {
|
||||
if ($stmt->cond instanceof PhpParser\Node\Expr\FuncCall
|
||||
&& $stmt->cond->name instanceof PhpParser\Node\Name
|
||||
&& ($stmt->cond->name->parts === ['get_class']
|
||||
|| $stmt->cond->name->parts === ['gettype']
|
||||
|| $stmt->cond->name->parts === ['get_debug_type']
|
||||
|| $stmt->cond->name->parts === ['count']
|
||||
|| $stmt->cond->name->parts === ['sizeof'])
|
||||
&& ($stmt->cond->name->getParts() === ['get_class']
|
||||
|| $stmt->cond->name->getParts() === ['gettype']
|
||||
|| $stmt->cond->name->getParts() === ['get_debug_type']
|
||||
|| $stmt->cond->name->getParts() === ['count']
|
||||
|| $stmt->cond->name->getParts() === ['sizeof'])
|
||||
&& $stmt->cond->getArgs()
|
||||
) {
|
||||
$first_arg = $stmt->cond->getArgs()[0];
|
||||
|
||||
@@ -260,7 +260,7 @@ class SimpleTypeInferer
|
||||
}
|
||||
|
||||
if ($stmt instanceof PhpParser\Node\Expr\ConstFetch) {
|
||||
$name = strtolower($stmt->name->parts[0]);
|
||||
$name = strtolower($stmt->name->getFirst());
|
||||
if ($name === 'false') {
|
||||
return Type::getFalse();
|
||||
}
|
||||
@@ -273,7 +273,7 @@ class SimpleTypeInferer
|
||||
return Type::getNull();
|
||||
}
|
||||
|
||||
if ($stmt->name->parts[0] === '__NAMESPACE__') {
|
||||
if ($stmt->name->getFirst() === '__NAMESPACE__') {
|
||||
return Type::getString($aliases->namespace);
|
||||
}
|
||||
|
||||
@@ -287,7 +287,7 @@ class SimpleTypeInferer
|
||||
}
|
||||
|
||||
if ($stmt instanceof PhpParser\Node\Scalar\MagicConst\Line) {
|
||||
return Type::getInt();
|
||||
return Type::getIntRange(1, null);
|
||||
}
|
||||
|
||||
if ($stmt instanceof PhpParser\Node\Scalar\MagicConst\Class_
|
||||
@@ -306,18 +306,18 @@ class SimpleTypeInferer
|
||||
if ($stmt->class instanceof PhpParser\Node\Name
|
||||
&& $stmt->name instanceof PhpParser\Node\Identifier
|
||||
&& $fq_classlike_name
|
||||
&& $stmt->class->parts !== ['static']
|
||||
&& $stmt->class->parts !== ['parent']
|
||||
&& $stmt->class->getParts() !== ['static']
|
||||
&& $stmt->class->getParts() !== ['parent']
|
||||
) {
|
||||
if (isset($existing_class_constants[$stmt->name->name])
|
||||
&& $existing_class_constants[$stmt->name->name]->type
|
||||
) {
|
||||
if ($stmt->class->parts === ['self']) {
|
||||
if ($stmt->class->getParts() === ['self']) {
|
||||
return $existing_class_constants[$stmt->name->name]->type;
|
||||
}
|
||||
}
|
||||
|
||||
if ($stmt->class->parts === ['self']) {
|
||||
if ($stmt->class->getParts() === ['self']) {
|
||||
$const_fq_class_name = $fq_classlike_name;
|
||||
} else {
|
||||
$const_fq_class_name = ClassLikeAnalyzer::getFQCLNFromNameObject(
|
||||
|
||||
@@ -6,6 +6,7 @@ use PhpParser;
|
||||
use Psalm\Context;
|
||||
use Psalm\Internal\Analyzer\Statements\Expression\ExpressionIdentifier;
|
||||
use Psalm\Internal\Analyzer\StatementsAnalyzer;
|
||||
use Psalm\Type;
|
||||
use Psalm\Type\Atomic\TArray;
|
||||
use Psalm\Type\Atomic\TIntRange;
|
||||
use Psalm\Type\Atomic\TKeyedArray;
|
||||
@@ -90,7 +91,7 @@ class UnsetAnalyzer
|
||||
|
||||
if ($atomic_root_type->is_list && !$is_list && is_int($key_value)) {
|
||||
if ($key_value === 0) {
|
||||
$list_key = new Union([new TIntRange(1, null)]);
|
||||
$list_key = Type::getIntRange(1, null);
|
||||
} elseif ($key_value === 1) {
|
||||
$list_key = new Union([
|
||||
new TLiteralInt(0),
|
||||
|
||||
@@ -304,7 +304,7 @@ class StatementsAnalyzer extends SourceAnalyzer
|
||||
} elseif ($stmt instanceof PhpParser\Node\Stmt\Expression
|
||||
&& $stmt->expr instanceof PhpParser\Node\Expr\FuncCall
|
||||
&& $stmt->expr->name instanceof PhpParser\Node\Name
|
||||
&& $stmt->expr->name->parts === ['define']
|
||||
&& $stmt->expr->name->getParts() === ['define']
|
||||
&& isset($stmt->expr->getArgs()[1])
|
||||
) {
|
||||
$const_name = ConstFetchAnalyzer::getConstName(
|
||||
|
||||
128
vendor/vimeo/psalm/src/Psalm/Internal/Cache.php
vendored
Normal file
128
vendor/vimeo/psalm/src/Psalm/Internal/Cache.php
vendored
Normal file
@@ -0,0 +1,128 @@
|
||||
<?php
|
||||
|
||||
namespace Psalm\Internal;
|
||||
|
||||
use Psalm\Config;
|
||||
use Psalm\Internal\Provider\Providers;
|
||||
|
||||
use function file_exists;
|
||||
use function file_put_contents;
|
||||
use function gzdeflate;
|
||||
use function gzinflate;
|
||||
use function igbinary_serialize;
|
||||
use function igbinary_unserialize;
|
||||
use function is_writable;
|
||||
use function lz4_compress;
|
||||
use function lz4_uncompress;
|
||||
use function serialize;
|
||||
use function unlink;
|
||||
use function unserialize;
|
||||
|
||||
use const LOCK_EX;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
class Cache
|
||||
{
|
||||
private Config $config;
|
||||
|
||||
public bool $use_igbinary;
|
||||
|
||||
public function __construct(Config $config)
|
||||
{
|
||||
$this->config = $config;
|
||||
$this->use_igbinary = $config->use_igbinary;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array|object|string|null
|
||||
*/
|
||||
public function getItem(string $path)
|
||||
{
|
||||
if (!file_exists($path)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$cache = Providers::safeFileGetContents($path);
|
||||
if ($cache === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($this->config->compressor === 'deflate') {
|
||||
$inflated = @gzinflate($cache);
|
||||
} elseif ($this->config->compressor === 'lz4') {
|
||||
/**
|
||||
* @psalm-suppress UndefinedFunction
|
||||
* @var string|false $inflated
|
||||
*/
|
||||
$inflated = lz4_uncompress($cache);
|
||||
} else {
|
||||
$inflated = $cache;
|
||||
}
|
||||
|
||||
// invalid cache data
|
||||
if ($inflated === false) {
|
||||
$this->deleteItem($path);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($this->use_igbinary) {
|
||||
/** @var object|false $unserialized */
|
||||
$unserialized = @igbinary_unserialize($inflated);
|
||||
} else {
|
||||
/** @var object|false $unserialized */
|
||||
$unserialized = @unserialize($inflated);
|
||||
}
|
||||
|
||||
if ($unserialized === false) {
|
||||
$this->deleteItem($path);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
return $unserialized;
|
||||
}
|
||||
|
||||
public function deleteItem(string $path): void
|
||||
{
|
||||
if (@is_writable($path)) {
|
||||
@unlink($path);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array|object|string $item
|
||||
*/
|
||||
public function saveItem(string $path, $item): void
|
||||
{
|
||||
if ($this->use_igbinary) {
|
||||
$serialized = igbinary_serialize($item);
|
||||
} else {
|
||||
$serialized = serialize($item);
|
||||
}
|
||||
|
||||
if ($this->config->compressor === 'deflate') {
|
||||
$compressed = gzdeflate($serialized);
|
||||
} elseif ($this->config->compressor === 'lz4') {
|
||||
/**
|
||||
* @psalm-suppress UndefinedFunction
|
||||
* @var string|false $compressed
|
||||
*/
|
||||
$compressed = lz4_compress($serialized, 4);
|
||||
} else {
|
||||
$compressed = $serialized;
|
||||
}
|
||||
|
||||
if ($compressed !== false) {
|
||||
file_put_contents($path, $compressed, LOCK_EX);
|
||||
}
|
||||
// TODO: Error handling
|
||||
}
|
||||
|
||||
public function getCacheDirectory(): ?string
|
||||
{
|
||||
return $this->config->getCacheDirectory();
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,7 @@ use Psalm\Internal\Fork\PsalmRestarter;
|
||||
use Psalm\Internal\IncludeCollector;
|
||||
use Psalm\Internal\LanguageServer\ClientConfiguration;
|
||||
use Psalm\Internal\LanguageServer\LanguageServer as LanguageServerLanguageServer;
|
||||
use Psalm\Internal\LanguageServer\PathMapper;
|
||||
use Psalm\Report;
|
||||
|
||||
use function array_key_exists;
|
||||
@@ -18,6 +19,7 @@ use function array_search;
|
||||
use function array_slice;
|
||||
use function chdir;
|
||||
use function error_log;
|
||||
use function explode;
|
||||
use function fwrite;
|
||||
use function gc_disable;
|
||||
use function getcwd;
|
||||
@@ -31,6 +33,7 @@ use function is_string;
|
||||
use function preg_replace;
|
||||
use function realpath;
|
||||
use function setlocale;
|
||||
use function strlen;
|
||||
use function strpos;
|
||||
use function strtolower;
|
||||
use function substr;
|
||||
@@ -75,6 +78,7 @@ final class LanguageServer
|
||||
'find-dead-code',
|
||||
'help',
|
||||
'root:',
|
||||
'map-folder::',
|
||||
'use-ini-defaults',
|
||||
'version',
|
||||
'tcp:',
|
||||
@@ -127,6 +131,14 @@ final class LanguageServer
|
||||
|
||||
// get options from command line
|
||||
$options = getopt(implode('', $valid_short_options), $valid_long_options);
|
||||
if ($options === false) {
|
||||
// shouldn't really happen, but just in case
|
||||
fwrite(
|
||||
STDERR,
|
||||
'Failed to get CLI args' . PHP_EOL,
|
||||
);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (!array_key_exists('use-ini-defaults', $options)) {
|
||||
ini_set('display_errors', '1');
|
||||
@@ -169,6 +181,14 @@ final class LanguageServer
|
||||
-r, --root
|
||||
If running Psalm globally you'll need to specify a project root. Defaults to cwd
|
||||
|
||||
--map-folder[=SERVER_FOLDER:CLIENT_FOLDER]
|
||||
Specify folder to map between the client and the server. Use this when the client
|
||||
and server have different views of the filesystem (e.g. in a docker container).
|
||||
Defaults to mapping the rootUri provided by the client to the server's cwd,
|
||||
or `-r` if provided.
|
||||
|
||||
No mapping is done when this option is not specified.
|
||||
|
||||
--find-dead-code
|
||||
Look for dead code
|
||||
|
||||
@@ -291,6 +311,8 @@ final class LanguageServer
|
||||
|
||||
setlocale(LC_CTYPE, 'C');
|
||||
|
||||
$path_mapper = self::createPathMapper($options, $current_dir);
|
||||
|
||||
$path_to_config = CliUtils::getPathToConfig($options);
|
||||
|
||||
if (isset($options['tcp'])) {
|
||||
@@ -394,6 +416,49 @@ final class LanguageServer
|
||||
$clientConfiguration->TCPServerAddress = $options['tcp'] ?? null;
|
||||
$clientConfiguration->TCPServerMode = isset($options['tcp-server']);
|
||||
|
||||
LanguageServerLanguageServer::run($config, $clientConfiguration, $current_dir, $inMemory);
|
||||
LanguageServerLanguageServer::run($config, $clientConfiguration, $current_dir, $path_mapper, $inMemory);
|
||||
}
|
||||
|
||||
/** @param array<string,string|false|list<string|false>> $options */
|
||||
private static function createPathMapper(array $options, string $server_start_dir): PathMapper
|
||||
{
|
||||
if (!isset($options['map-folder'])) {
|
||||
// dummy no-op mapper
|
||||
return new PathMapper('/', '/');
|
||||
}
|
||||
|
||||
$map_folder = $options['map-folder'];
|
||||
|
||||
if ($map_folder === false) {
|
||||
// autoconfigured mapper
|
||||
return new PathMapper($server_start_dir, null);
|
||||
}
|
||||
|
||||
if (is_string($map_folder)) {
|
||||
if (strpos($map_folder, ':') === false) {
|
||||
fwrite(
|
||||
STDERR,
|
||||
'invalid format for --map-folder option' . PHP_EOL,
|
||||
);
|
||||
exit(1);
|
||||
}
|
||||
/** @psalm-suppress PossiblyUndefinedArrayOffset we just checked that we have the separator*/
|
||||
[$server_dir, $client_dir] = explode(':', $map_folder, 2);
|
||||
if (!strlen($server_dir) || !strlen($client_dir)) {
|
||||
fwrite(
|
||||
STDERR,
|
||||
'invalid format for --map-folder option, '
|
||||
. 'neither SERVER_FOLDER nor CLIENT_FOLDER can be empty' . PHP_EOL,
|
||||
);
|
||||
exit(1);
|
||||
}
|
||||
return new PathMapper($server_dir, $client_dir);
|
||||
}
|
||||
|
||||
fwrite(
|
||||
STDERR,
|
||||
'--map-folder option can only be specified once' . PHP_EOL,
|
||||
);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -166,6 +166,7 @@ final class Psalm
|
||||
|
||||
/**
|
||||
* @param array<int,string> $argv
|
||||
* @psalm-suppress ComplexMethod Maybe some of the option handling could be moved to its own function...
|
||||
*/
|
||||
public static function run(array $argv): void
|
||||
{
|
||||
@@ -183,6 +184,14 @@ final class Psalm
|
||||
throw new RuntimeException('Failed to parse CLI options');
|
||||
}
|
||||
|
||||
// debug CI environment
|
||||
if (!array_key_exists('debug', $options)
|
||||
&& 'true' === getenv('GITHUB_ACTIONS')
|
||||
&& '1' === getenv('RUNNER_DEBUG')
|
||||
) {
|
||||
$options['debug'] = false;
|
||||
}
|
||||
|
||||
self::forwardCliCall($options, $argv);
|
||||
|
||||
self::validateCliArguments($args);
|
||||
@@ -403,7 +412,24 @@ final class Psalm
|
||||
{
|
||||
return isset($options['output-format']) && is_string($options['output-format'])
|
||||
? $options['output-format']
|
||||
: Report::TYPE_CONSOLE;
|
||||
: self::findDefaultOutputFormat();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Report::TYPE_*
|
||||
*/
|
||||
private static function findDefaultOutputFormat(): string
|
||||
{
|
||||
$emulator = getenv('TERMINAL_EMULATOR');
|
||||
if (is_string($emulator) && substr($emulator, 0, 9) === 'JetBrains') {
|
||||
return Report::TYPE_PHP_STORM;
|
||||
}
|
||||
|
||||
if ('true' === getenv('GITHUB_ACTIONS')) {
|
||||
return Report::TYPE_GITHUB_ACTIONS;
|
||||
}
|
||||
|
||||
return Report::TYPE_CONSOLE;
|
||||
}
|
||||
|
||||
private static function initShowInfo(array $options): bool
|
||||
|
||||
@@ -687,6 +687,16 @@ class Analyzer
|
||||
}
|
||||
}
|
||||
|
||||
// This could be optimized by storing method references to files
|
||||
foreach ($file_reference_provider->getDeletedReferencedFiles() as $deleted_file) {
|
||||
foreach ($file_reference_provider->getFilesReferencingFile($deleted_file) as $file_referencing_deleted) {
|
||||
$methods_referencing_deleted = $this->analyzed_methods[$file_referencing_deleted] ?? [];
|
||||
foreach ($methods_referencing_deleted as $method_referencing_deleted => $_) {
|
||||
$newly_invalidated_methods[$method_referencing_deleted] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($newly_invalidated_methods as $method_id => $_) {
|
||||
foreach ($method_references_to_class_members as $i => $_) {
|
||||
unset($method_references_to_class_members[$i][$method_id]);
|
||||
@@ -1552,13 +1562,13 @@ class Analyzer
|
||||
$has_info = false;
|
||||
|
||||
foreach ($issues as $issue) {
|
||||
if ($issue->severity === 'error') {
|
||||
$has_error = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if ($issue->severity === 'info') {
|
||||
$has_info = true;
|
||||
switch ($issue->severity) {
|
||||
case IssueData::SEVERITY_INFO:
|
||||
$has_info = true;
|
||||
break;
|
||||
default:
|
||||
$has_error = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2347,15 +2347,31 @@ class ClassLikes
|
||||
$existing_classes,
|
||||
] = $thread_data;
|
||||
|
||||
$this->existing_classlikes_lc = array_merge($existing_classlikes_lc, $this->existing_classlikes_lc);
|
||||
$this->existing_classes_lc = array_merge($existing_classes_lc, $this->existing_classes_lc);
|
||||
$this->existing_traits_lc = array_merge($existing_traits_lc, $this->existing_traits_lc);
|
||||
$this->existing_traits = array_merge($existing_traits, $this->existing_traits);
|
||||
$this->existing_enums_lc = array_merge($existing_enums_lc, $this->existing_enums_lc);
|
||||
$this->existing_enums = array_merge($existing_enums, $this->existing_enums);
|
||||
$this->existing_interfaces_lc = array_merge($existing_interfaces_lc, $this->existing_interfaces_lc);
|
||||
$this->existing_interfaces = array_merge($existing_interfaces, $this->existing_interfaces);
|
||||
$this->existing_classes = array_merge($existing_classes, $this->existing_classes);
|
||||
$this->existing_classlikes_lc = self::mergeThreadData($existing_classlikes_lc, $this->existing_classlikes_lc);
|
||||
$this->existing_classes_lc = self::mergeThreadData($existing_classes_lc, $this->existing_classes_lc);
|
||||
$this->existing_traits_lc = self::mergeThreadData($existing_traits_lc, $this->existing_traits_lc);
|
||||
$this->existing_traits = self::mergeThreadData($existing_traits, $this->existing_traits);
|
||||
$this->existing_enums_lc = self::mergeThreadData($existing_enums_lc, $this->existing_enums_lc);
|
||||
$this->existing_enums = self::mergeThreadData($existing_enums, $this->existing_enums);
|
||||
$this->existing_interfaces_lc = self::mergeThreadData($existing_interfaces_lc, $this->existing_interfaces_lc);
|
||||
$this->existing_interfaces = self::mergeThreadData($existing_interfaces, $this->existing_interfaces);
|
||||
$this->existing_classes = self::mergeThreadData($existing_classes, $this->existing_classes);
|
||||
}
|
||||
|
||||
/**
|
||||
* @template T as string|lowercase-string
|
||||
* @param array<T, bool> $old
|
||||
* @param array<T, bool> $new
|
||||
* @return array<T, bool>
|
||||
*/
|
||||
private static function mergeThreadData(array $old, array $new): array
|
||||
{
|
||||
foreach ($new as $name => $value) {
|
||||
if (!isset($old[$name]) || (!$old[$name] && $value)) {
|
||||
$old[$name] = $value;
|
||||
}
|
||||
}
|
||||
return $old;
|
||||
}
|
||||
|
||||
public function getStorageFor(string $fq_class_name): ?ClassLikeStorage
|
||||
|
||||
@@ -8,6 +8,7 @@ use Psalm\CodeLocation;
|
||||
use Psalm\Codebase;
|
||||
use Psalm\Context;
|
||||
use Psalm\Internal\Analyzer\SourceAnalyzer;
|
||||
use Psalm\Internal\Analyzer\Statements\Expression\Call\ClassTemplateParamCollector;
|
||||
use Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer;
|
||||
use Psalm\Internal\Analyzer\StatementsAnalyzer;
|
||||
use Psalm\Internal\MethodIdentifier;
|
||||
@@ -18,6 +19,8 @@ use Psalm\Internal\Provider\MethodParamsProvider;
|
||||
use Psalm\Internal\Provider\MethodReturnTypeProvider;
|
||||
use Psalm\Internal\Provider\MethodVisibilityProvider;
|
||||
use Psalm\Internal\Type\Comparator\UnionTypeComparator;
|
||||
use Psalm\Internal\Type\TemplateInferredTypeReplacer;
|
||||
use Psalm\Internal\Type\TemplateResult;
|
||||
use Psalm\Internal\Type\TypeExpander;
|
||||
use Psalm\Internal\TypeVisitor\TypeLocalizer;
|
||||
use Psalm\StatementsSource;
|
||||
@@ -768,11 +771,26 @@ class Methods
|
||||
$candidate_type,
|
||||
);
|
||||
|
||||
if (((!$old_contained_by_new && !$new_contained_by_old)
|
||||
|| ($old_contained_by_new && $new_contained_by_old))
|
||||
&& !$candidate_type->hasTemplate()
|
||||
&& !$overridden_storage_return_type->hasTemplate()
|
||||
if ((!$old_contained_by_new && !$new_contained_by_old)
|
||||
|| ($old_contained_by_new && $new_contained_by_old)
|
||||
) {
|
||||
$found_generic_params = ClassTemplateParamCollector::collect(
|
||||
$source_analyzer->getCodebase(),
|
||||
$appearing_fq_class_storage,
|
||||
$appearing_fq_class_storage,
|
||||
$appearing_method_name,
|
||||
null,
|
||||
true,
|
||||
);
|
||||
|
||||
if ($found_generic_params) {
|
||||
$overridden_storage_return_type = TemplateInferredTypeReplacer::replace(
|
||||
$overridden_storage_return_type,
|
||||
new TemplateResult([], $found_generic_params),
|
||||
$source_analyzer->getCodebase(),
|
||||
);
|
||||
}
|
||||
|
||||
$attempted_intersection = null;
|
||||
if ($old_contained_by_new) { //implicitly $new_contained_by_old as well
|
||||
try {
|
||||
|
||||
@@ -158,6 +158,22 @@ class ClassStatementsDiffer extends AstDiffer
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($a->type xor $b->type) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($a->type && $b->type) {
|
||||
$a_type_start = (int) $a->type->getAttribute('startFilePos');
|
||||
$a_type_end = (int) $a->type->getAttribute('endFilePos');
|
||||
$b_type_start = (int) $b->type->getAttribute('startFilePos');
|
||||
$b_type_end = (int) $b->type->getAttribute('endFilePos');
|
||||
if (substr($a_code, $a_type_start, $a_type_end - $a_type_start + 1)
|
||||
!== substr($b_code, $b_type_start, $b_type_end - $b_type_start + 1)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
$body_change = substr($a_code, $a_comments_end, $a_end - $a_comments_end)
|
||||
!== substr($b_code, $b_comments_end, $b_end - $b_comments_end);
|
||||
} else {
|
||||
|
||||
@@ -129,7 +129,7 @@ class FileStatementsDiffer extends AstDiffer
|
||||
if ($use->alias) {
|
||||
$add_or_delete[] = 'use:' . (string) $use->alias;
|
||||
} else {
|
||||
$name_parts = $use->name->parts;
|
||||
$name_parts = $use->name->getParts();
|
||||
|
||||
$add_or_delete[] = 'use:' . end($name_parts);
|
||||
}
|
||||
@@ -156,7 +156,7 @@ class FileStatementsDiffer extends AstDiffer
|
||||
if ($use->alias) {
|
||||
$add_or_delete[] = 'use:' . (string) $use->alias;
|
||||
} else {
|
||||
$name_parts = $use->name->parts;
|
||||
$name_parts = $use->name->getParts();
|
||||
|
||||
$add_or_delete[] = 'use:' . end($name_parts);
|
||||
}
|
||||
|
||||
@@ -114,7 +114,7 @@ class NamespaceStatementsDiffer extends AstDiffer
|
||||
if ($use->alias) {
|
||||
$add_or_delete[] = 'use:' . (string) $use->alias;
|
||||
} else {
|
||||
$name_parts = $use->name->parts;
|
||||
$name_parts = $use->name->getParts();
|
||||
|
||||
$add_or_delete[] = 'use:' . end($name_parts);
|
||||
}
|
||||
@@ -128,7 +128,7 @@ class NamespaceStatementsDiffer extends AstDiffer
|
||||
if ($use->alias) {
|
||||
$add_or_delete[] = 'use:' . (string) $use->alias;
|
||||
} else {
|
||||
$name_parts = $use->name->parts;
|
||||
$name_parts = $use->name->getParts();
|
||||
|
||||
$add_or_delete[] = 'use:' . end($name_parts);
|
||||
}
|
||||
|
||||
@@ -98,6 +98,7 @@ class Pool
|
||||
* @param array<int, array<int, mixed>> $process_task_data_iterator
|
||||
* An array of task data items to be divided up among the
|
||||
* workers. The size of this is the number of forked processes.
|
||||
* @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint
|
||||
* @param Closure $startup_closure
|
||||
* A closure to execute upon starting a child
|
||||
* @param Closure(int, mixed):mixed $task_closure
|
||||
|
||||
83
vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/Client/Progress/LegacyProgress.php
vendored
Normal file
83
vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/Client/Progress/LegacyProgress.php
vendored
Normal file
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
|
||||
namespace Psalm\Internal\LanguageServer\Client\Progress;
|
||||
|
||||
use LanguageServerProtocol\LogMessage;
|
||||
use LanguageServerProtocol\MessageType;
|
||||
use LogicException;
|
||||
use Psalm\Internal\LanguageServer\ClientHandler;
|
||||
|
||||
/** @internal */
|
||||
final class LegacyProgress implements ProgressInterface
|
||||
{
|
||||
private const STATUS_INACTIVE = 'inactive';
|
||||
private const STATUS_ACTIVE = 'active';
|
||||
private const STATUS_FINISHED = 'finished';
|
||||
|
||||
private string $status = self::STATUS_INACTIVE;
|
||||
|
||||
private ClientHandler $handler;
|
||||
private ?string $title = null;
|
||||
|
||||
public function __construct(ClientHandler $handler)
|
||||
{
|
||||
$this->handler = $handler;
|
||||
}
|
||||
|
||||
public function begin(string $title, ?string $message = null, ?int $percentage = null): void
|
||||
{
|
||||
|
||||
if ($this->status === self::STATUS_ACTIVE) {
|
||||
throw new LogicException('Progress has already been started');
|
||||
}
|
||||
|
||||
if ($this->status === self::STATUS_FINISHED) {
|
||||
throw new LogicException('Progress has already been finished');
|
||||
}
|
||||
|
||||
$this->title = $title;
|
||||
|
||||
$this->notify($message);
|
||||
|
||||
$this->status = self::STATUS_ACTIVE;
|
||||
}
|
||||
|
||||
public function update(?string $message = null, ?int $percentage = null): void
|
||||
{
|
||||
if ($this->status === self::STATUS_FINISHED) {
|
||||
throw new LogicException('Progress has already been finished');
|
||||
}
|
||||
|
||||
if ($this->status === self::STATUS_INACTIVE) {
|
||||
throw new LogicException('Progress has not been started yet');
|
||||
}
|
||||
|
||||
$this->notify($message);
|
||||
}
|
||||
|
||||
public function end(?string $message = null): void
|
||||
{
|
||||
if ($this->status === self::STATUS_FINISHED) {
|
||||
throw new LogicException('Progress has already been finished');
|
||||
}
|
||||
|
||||
if ($this->status === self::STATUS_INACTIVE) {
|
||||
throw new LogicException('Progress has not been started yet');
|
||||
}
|
||||
|
||||
$this->notify($message);
|
||||
|
||||
$this->status = self::STATUS_FINISHED;
|
||||
}
|
||||
|
||||
private function notify(?string $message): void
|
||||
{
|
||||
$this->handler->notify(
|
||||
'telemetry/event',
|
||||
new LogMessage(
|
||||
MessageType::INFO,
|
||||
$this->title . (empty($message) ? '' : (': ' . $message)),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
121
vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/Client/Progress/Progress.php
vendored
Normal file
121
vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/Client/Progress/Progress.php
vendored
Normal file
@@ -0,0 +1,121 @@
|
||||
<?php
|
||||
|
||||
namespace Psalm\Internal\LanguageServer\Client\Progress;
|
||||
|
||||
use LogicException;
|
||||
use Psalm\Internal\LanguageServer\ClientHandler;
|
||||
|
||||
/** @internal */
|
||||
final class Progress implements ProgressInterface
|
||||
{
|
||||
private const STATUS_INACTIVE = 'inactive';
|
||||
private const STATUS_ACTIVE = 'active';
|
||||
private const STATUS_FINISHED = 'finished';
|
||||
|
||||
private string $status = self::STATUS_INACTIVE;
|
||||
|
||||
private ClientHandler $handler;
|
||||
private string $token;
|
||||
private bool $withPercentage = false;
|
||||
|
||||
public function __construct(ClientHandler $handler, string $token)
|
||||
{
|
||||
$this->handler = $handler;
|
||||
$this->token = $token;
|
||||
}
|
||||
|
||||
public function begin(
|
||||
string $title,
|
||||
?string $message = null,
|
||||
?int $percentage = null
|
||||
): void {
|
||||
if ($this->status === self::STATUS_ACTIVE) {
|
||||
throw new LogicException('Progress has already been started');
|
||||
}
|
||||
|
||||
if ($this->status === self::STATUS_FINISHED) {
|
||||
throw new LogicException('Progress has already been finished');
|
||||
}
|
||||
|
||||
$notification = [
|
||||
'token' => $this->token,
|
||||
'value' => [
|
||||
'kind' => 'begin',
|
||||
'title' => $title,
|
||||
],
|
||||
];
|
||||
|
||||
if ($message !== null) {
|
||||
$notification['value']['message'] = $message;
|
||||
}
|
||||
|
||||
if ($percentage !== null) {
|
||||
$notification['value']['percentage'] = $percentage;
|
||||
$this->withPercentage = true;
|
||||
}
|
||||
|
||||
$this->handler->notify('$/progress', $notification);
|
||||
|
||||
$this->status = self::STATUS_ACTIVE;
|
||||
}
|
||||
|
||||
public function end(?string $message = null): void
|
||||
{
|
||||
if ($this->status === self::STATUS_FINISHED) {
|
||||
throw new LogicException('Progress has already been finished');
|
||||
}
|
||||
|
||||
if ($this->status === self::STATUS_INACTIVE) {
|
||||
throw new LogicException('Progress has not been started yet');
|
||||
}
|
||||
|
||||
$notification = [
|
||||
'token' => $this->token,
|
||||
'value' => [
|
||||
'kind' => 'end',
|
||||
],
|
||||
];
|
||||
|
||||
if ($message !== null) {
|
||||
$notification['value']['message'] = $message;
|
||||
}
|
||||
|
||||
$this->handler->notify('$/progress', $notification);
|
||||
|
||||
$this->status = self::STATUS_FINISHED;
|
||||
}
|
||||
|
||||
public function update(?string $message = null, ?int $percentage = null): void
|
||||
{
|
||||
if ($this->status === self::STATUS_FINISHED) {
|
||||
throw new LogicException('Progress has already been finished');
|
||||
}
|
||||
|
||||
if ($this->status === self::STATUS_INACTIVE) {
|
||||
throw new LogicException('Progress has not been started yet');
|
||||
}
|
||||
|
||||
$notification = [
|
||||
'token' => $this->token,
|
||||
'value' => [
|
||||
'kind' => 'report',
|
||||
],
|
||||
];
|
||||
|
||||
if ($message !== null) {
|
||||
$notification['value']['message'] = $message;
|
||||
}
|
||||
|
||||
if ($percentage !== null) {
|
||||
if (!$this->withPercentage) {
|
||||
throw new LogicException(
|
||||
'Cannot update percentage for progress '
|
||||
. 'that was started without percentage',
|
||||
);
|
||||
}
|
||||
$notification['value']['percentage'] = $percentage;
|
||||
}
|
||||
|
||||
$this->handler->notify('$/progress', $notification);
|
||||
}
|
||||
}
|
||||
16
vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/Client/Progress/ProgressInterface.php
vendored
Normal file
16
vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/Client/Progress/ProgressInterface.php
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace Psalm\Internal\LanguageServer\Client\Progress;
|
||||
|
||||
/** @internal */
|
||||
interface ProgressInterface
|
||||
{
|
||||
public function begin(
|
||||
string $title,
|
||||
?string $message = null,
|
||||
?int $percentage = null
|
||||
): void;
|
||||
|
||||
public function update(?string $message = null, ?int $percentage = null): void;
|
||||
public function end(?string $message = null): void;
|
||||
}
|
||||
@@ -7,6 +7,9 @@ namespace Psalm\Internal\LanguageServer;
|
||||
use JsonMapper;
|
||||
use LanguageServerProtocol\LogMessage;
|
||||
use LanguageServerProtocol\LogTrace;
|
||||
use Psalm\Internal\LanguageServer\Client\Progress\LegacyProgress;
|
||||
use Psalm\Internal\LanguageServer\Client\Progress\Progress;
|
||||
use Psalm\Internal\LanguageServer\Client\Progress\ProgressInterface;
|
||||
use Psalm\Internal\LanguageServer\Client\TextDocument as ClientTextDocument;
|
||||
use Psalm\Internal\LanguageServer\Client\Workspace as ClientWorkspace;
|
||||
|
||||
@@ -64,7 +67,7 @@ class LanguageClient
|
||||
public function refreshConfiguration(): void
|
||||
{
|
||||
$capabilities = $this->server->clientCapabilities;
|
||||
if ($capabilities && $capabilities->workspace && $capabilities->workspace->configuration) {
|
||||
if ($capabilities->workspace->configuration ?? false) {
|
||||
$this->workspace->requestConfiguration('psalm')->onResolve(function ($error, $value): void {
|
||||
if ($error) {
|
||||
$this->server->logError('There was an error getting configuration');
|
||||
@@ -131,6 +134,15 @@ class LanguageClient
|
||||
);
|
||||
}
|
||||
|
||||
public function makeProgress(string $token): ProgressInterface
|
||||
{
|
||||
if ($this->server->clientCapabilities->window->workDoneProgress ?? false) {
|
||||
return new Progress($this->handler, $token);
|
||||
} else {
|
||||
return new LegacyProgress($this->handler);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Configuration Refreshed from Client
|
||||
*
|
||||
|
||||
@@ -88,6 +88,7 @@ use function stream_socket_server;
|
||||
use function strpos;
|
||||
use function substr;
|
||||
use function trim;
|
||||
use function uniqid;
|
||||
use function urldecode;
|
||||
|
||||
use const JSON_PRETTY_PRINT;
|
||||
@@ -142,13 +143,16 @@ class LanguageServer extends Dispatcher
|
||||
*/
|
||||
protected JsonMapper $mapper;
|
||||
|
||||
protected PathMapper $path_mapper;
|
||||
|
||||
public function __construct(
|
||||
ProtocolReader $reader,
|
||||
ProtocolWriter $writer,
|
||||
ProjectAnalyzer $project_analyzer,
|
||||
Codebase $codebase,
|
||||
ClientConfiguration $clientConfiguration,
|
||||
Progress $progress
|
||||
Progress $progress,
|
||||
PathMapper $path_mapper
|
||||
) {
|
||||
parent::__construct($this, '/');
|
||||
|
||||
@@ -158,6 +162,8 @@ class LanguageServer extends Dispatcher
|
||||
|
||||
$this->codebase = $codebase;
|
||||
|
||||
$this->path_mapper = $path_mapper;
|
||||
|
||||
$this->protocolWriter = $writer;
|
||||
|
||||
$this->protocolReader = $reader;
|
||||
@@ -240,6 +246,7 @@ class LanguageServer extends Dispatcher
|
||||
|
||||
$this->client = new LanguageClient($reader, $writer, $this, $clientConfiguration);
|
||||
|
||||
|
||||
$this->logInfo("Psalm Language Server ".PSALM_VERSION." has started.");
|
||||
}
|
||||
|
||||
@@ -250,6 +257,7 @@ class LanguageServer extends Dispatcher
|
||||
Config $config,
|
||||
ClientConfiguration $clientConfiguration,
|
||||
string $base_dir,
|
||||
PathMapper $path_mapper,
|
||||
bool $inMemory = false
|
||||
): void {
|
||||
$progress = new Progress();
|
||||
@@ -322,6 +330,7 @@ class LanguageServer extends Dispatcher
|
||||
$codebase,
|
||||
$clientConfiguration,
|
||||
$progress,
|
||||
$path_mapper,
|
||||
);
|
||||
Loop::run();
|
||||
} elseif ($clientConfiguration->TCPServerMode && $clientConfiguration->TCPServerAddress) {
|
||||
@@ -345,6 +354,7 @@ class LanguageServer extends Dispatcher
|
||||
$codebase,
|
||||
$clientConfiguration,
|
||||
$progress,
|
||||
$path_mapper,
|
||||
);
|
||||
Loop::run();
|
||||
}
|
||||
@@ -358,6 +368,7 @@ class LanguageServer extends Dispatcher
|
||||
$codebase,
|
||||
$clientConfiguration,
|
||||
$progress,
|
||||
$path_mapper,
|
||||
);
|
||||
Loop::run();
|
||||
}
|
||||
@@ -367,38 +378,36 @@ class LanguageServer extends Dispatcher
|
||||
* The initialize request is sent as the first request from the client to the server.
|
||||
*
|
||||
* @param ClientCapabilities $capabilities The capabilities provided by the client (editor)
|
||||
* @param int|null $processId The process Id of the parent process that started the server.
|
||||
* Is null if the process has not been started by another process. If the parent process is
|
||||
* not alive then the server should exit (see exit notification) its process.
|
||||
* @param ClientInfo|null $clientInfo Information about the client
|
||||
* @param string|null $locale The locale the client is currently showing the user interface
|
||||
* in. This must not necessarily be the locale of the operating
|
||||
* system.
|
||||
* @param string|null $rootPath The rootPath of the workspace. Is null if no folder is open.
|
||||
* @param mixed $initializationOptions
|
||||
* @param string|null $trace The initial trace setting. If omitted trace is disabled ('off').
|
||||
* @param string|null $workDoneToken The token to be used to report progress during init.
|
||||
* @psalm-return Promise<InitializeResult>
|
||||
* @psalm-suppress PossiblyUnusedParam
|
||||
*/
|
||||
public function initialize(
|
||||
ClientCapabilities $capabilities,
|
||||
?int $processId = null,
|
||||
?ClientInfo $clientInfo = null,
|
||||
?string $locale = null,
|
||||
?string $rootPath = null,
|
||||
?string $rootUri = null,
|
||||
$initializationOptions = null,
|
||||
?string $trace = null
|
||||
//?array $workspaceFolders = null //error in json-dispatcher
|
||||
?string $trace = null,
|
||||
?string $workDoneToken = null
|
||||
): Promise {
|
||||
$this->clientInfo = $clientInfo;
|
||||
$this->clientCapabilities = $capabilities;
|
||||
$this->trace = $trace;
|
||||
|
||||
|
||||
if ($rootUri !== null) {
|
||||
$this->path_mapper->configureClientRoot($this->getPathPart($rootUri));
|
||||
}
|
||||
|
||||
return call(
|
||||
/** @return Generator<int, true, mixed, InitializeResult> */
|
||||
function () {
|
||||
function () use ($workDoneToken) {
|
||||
$progress = $this->client->makeProgress($workDoneToken ?? uniqid('tkn', true));
|
||||
|
||||
$this->logInfo("Initializing...");
|
||||
$this->clientStatus('initializing');
|
||||
$progress->begin('Psalm', 'initializing');
|
||||
|
||||
// Eventually, this might block on something. Leave it as a generator.
|
||||
/** @psalm-suppress TypeDoesNotContainType */
|
||||
@@ -409,14 +418,14 @@ class LanguageServer extends Dispatcher
|
||||
$this->project_analyzer->serverMode($this);
|
||||
|
||||
$this->logInfo("Initializing: Getting code base...");
|
||||
$this->clientStatus('initializing', 'getting code base');
|
||||
$progress->update('getting code base');
|
||||
|
||||
$this->logInfo("Initializing: Scanning files ({$this->project_analyzer->threads} Threads)...");
|
||||
$this->clientStatus('initializing', 'scanning files');
|
||||
$progress->update('scanning files');
|
||||
$this->codebase->scanFiles($this->project_analyzer->threads);
|
||||
|
||||
$this->logInfo("Initializing: Registering stub files...");
|
||||
$this->clientStatus('initializing', 'registering stub files');
|
||||
$progress->update('registering stub files');
|
||||
$this->codebase->config->visitStubFiles($this->codebase, $this->project_analyzer->progress);
|
||||
|
||||
if ($this->textDocument === null) {
|
||||
@@ -538,11 +547,7 @@ class LanguageServer extends Dispatcher
|
||||
*
|
||||
* @since LSP 3.16.0
|
||||
*/
|
||||
if ($this->clientCapabilities &&
|
||||
$this->clientCapabilities->textDocument &&
|
||||
$this->clientCapabilities->textDocument->publishDiagnostics &&
|
||||
$this->clientCapabilities->textDocument->publishDiagnostics->dataSupport
|
||||
) {
|
||||
if ($this->clientCapabilities->textDocument->publishDiagnostics->dataSupport ?? false) {
|
||||
$serverCapabilities->codeActionProvider = true;
|
||||
}
|
||||
|
||||
@@ -560,7 +565,7 @@ class LanguageServer extends Dispatcher
|
||||
}
|
||||
|
||||
$this->logInfo("Initializing: Complete.");
|
||||
$this->clientStatus('initialized');
|
||||
$progress->end('initialized');
|
||||
|
||||
/**
|
||||
* Information about the server.
|
||||
@@ -728,10 +733,9 @@ class LanguageServer extends Dispatcher
|
||||
new Position($end_line - 1, $end_column - 1),
|
||||
);
|
||||
switch ($severity) {
|
||||
case Config::REPORT_INFO:
|
||||
case IssueData::SEVERITY_INFO:
|
||||
$diagnostic_severity = DiagnosticSeverity::WARNING;
|
||||
break;
|
||||
case Config::REPORT_ERROR:
|
||||
default:
|
||||
$diagnostic_severity = DiagnosticSeverity::ERROR;
|
||||
break;
|
||||
@@ -758,11 +762,7 @@ class LanguageServer extends Dispatcher
|
||||
*
|
||||
* @since LSP 3.16.0
|
||||
*/
|
||||
if ($this->clientCapabilities !== null &&
|
||||
$this->clientCapabilities->textDocument &&
|
||||
$this->clientCapabilities->textDocument->publishDiagnostics &&
|
||||
$this->clientCapabilities->textDocument->publishDiagnostics->codeDescriptionSupport
|
||||
) {
|
||||
if ($this->clientCapabilities->textDocument->publishDiagnostics->codeDescriptionSupport ?? false) {
|
||||
$diagnostic->codeDescription = new CodeDescription($issue_data->link);
|
||||
}
|
||||
|
||||
@@ -788,7 +788,7 @@ class LanguageServer extends Dispatcher
|
||||
);
|
||||
|
||||
if ($position !== false) {
|
||||
$issue_data->severity = Config::REPORT_INFO;
|
||||
$issue_data->severity = IssueData::SEVERITY_INFO;
|
||||
/** @psalm-suppress MixedArgument */
|
||||
array_splice($issue_baseline[$file][$type]['s'], $position, 1);
|
||||
/** @psalm-suppress MixedArrayAssignment, MixedOperand, MixedAssignment */
|
||||
@@ -797,7 +797,7 @@ class LanguageServer extends Dispatcher
|
||||
} else {
|
||||
/** @psalm-suppress MixedArrayAssignment */
|
||||
$issue_baseline[$file][$type]['s'] = [];
|
||||
$issue_data->severity = Config::REPORT_INFO;
|
||||
$issue_data->severity = IssueData::SEVERITY_INFO;
|
||||
/** @psalm-suppress MixedArrayAssignment, MixedOperand, MixedAssignment */
|
||||
$issue_baseline[$file][$type]['o']--;
|
||||
}
|
||||
@@ -806,7 +806,7 @@ class LanguageServer extends Dispatcher
|
||||
}, $data[$file_path] ?? []),
|
||||
function (IssueData $issue_data) {
|
||||
//Hide Warnings
|
||||
if ($issue_data->severity === Config::REPORT_INFO &&
|
||||
if ($issue_data->severity === IssueData::SEVERITY_INFO &&
|
||||
$this->client->clientConfiguration->hideWarnings
|
||||
) {
|
||||
return false;
|
||||
@@ -957,12 +957,15 @@ class LanguageServer extends Dispatcher
|
||||
|
||||
/**
|
||||
* Transforms an absolute file path into a URI as used by the language server protocol.
|
||||
*
|
||||
* @psalm-pure
|
||||
*/
|
||||
public static function pathToUri(string $filepath): string
|
||||
public function pathToUri(string $filepath): string
|
||||
{
|
||||
$filepath = trim(str_replace('\\', '/', $filepath), '/');
|
||||
$filepath = str_replace('\\', '/', $filepath);
|
||||
|
||||
$filepath = $this->path_mapper->mapServerToClient($oldpath = $filepath);
|
||||
$this->logDebug('Translated path to URI', ['from' => $oldpath, 'to' => $filepath]);
|
||||
|
||||
$filepath = trim($filepath, '/');
|
||||
$parts = explode('/', $filepath);
|
||||
// Don't %-encode the colon after a Windows drive letter
|
||||
$first = array_shift($parts);
|
||||
@@ -979,7 +982,29 @@ class LanguageServer extends Dispatcher
|
||||
/**
|
||||
* Transforms URI into file path
|
||||
*/
|
||||
public static function uriToPath(string $uri): string
|
||||
public function uriToPath(string $uri): string
|
||||
{
|
||||
$filepath = urldecode($this->getPathPart($uri));
|
||||
|
||||
if (strpos($filepath, ':') !== false) {
|
||||
if ($filepath[0] === '/') {
|
||||
$filepath = substr($filepath, 1);
|
||||
}
|
||||
$filepath = str_replace('/', '\\', $filepath);
|
||||
}
|
||||
|
||||
$filepath = $this->path_mapper->mapClientToServer($oldpath = $filepath);
|
||||
$this->logDebug('Translated URI to path', ['from' => $oldpath, 'to' => $filepath]);
|
||||
|
||||
$realpath = realpath($filepath);
|
||||
if ($realpath !== false) {
|
||||
return $realpath;
|
||||
}
|
||||
|
||||
return $filepath;
|
||||
}
|
||||
|
||||
private function getPathPart(string $uri): string
|
||||
{
|
||||
$fragments = parse_url($uri);
|
||||
if ($fragments === false
|
||||
@@ -989,21 +1014,21 @@ class LanguageServer extends Dispatcher
|
||||
) {
|
||||
throw new InvalidArgumentException("Not a valid file URI: $uri");
|
||||
}
|
||||
return $fragments['path'];
|
||||
}
|
||||
|
||||
$filepath = urldecode($fragments['path']);
|
||||
// the methods below forward special paths
|
||||
// like `$/cancelRequest` to `$this->cancelRequest()`
|
||||
// and `$/a/b/c` to `$this->a->b->c()`
|
||||
|
||||
if (strpos($filepath, ':') !== false) {
|
||||
if ($filepath[0] === '/') {
|
||||
$filepath = substr($filepath, 1);
|
||||
}
|
||||
$filepath = str_replace('/', '\\', $filepath);
|
||||
}
|
||||
public function __isset(string $prop_name): bool
|
||||
{
|
||||
return $prop_name === '$';
|
||||
}
|
||||
|
||||
$realpath = realpath($filepath);
|
||||
if ($realpath !== false) {
|
||||
return $realpath;
|
||||
}
|
||||
|
||||
return $filepath;
|
||||
/** @return static */
|
||||
public function __get(string $_prop_name): self
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
||||
61
vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/PathMapper.php
vendored
Normal file
61
vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/PathMapper.php
vendored
Normal file
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
namespace Psalm\Internal\LanguageServer;
|
||||
|
||||
use function rtrim;
|
||||
use function strlen;
|
||||
use function substr;
|
||||
|
||||
/** @internal */
|
||||
final class PathMapper
|
||||
{
|
||||
private string $server_root;
|
||||
private ?string $client_root;
|
||||
|
||||
public function __construct(string $server_root, ?string $client_root = null)
|
||||
{
|
||||
$this->server_root = $this->sanitizeFolderPath($server_root);
|
||||
$this->client_root = $this->sanitizeFolderPath($client_root);
|
||||
}
|
||||
|
||||
public function configureClientRoot(string $client_root): void
|
||||
{
|
||||
// ignore if preconfigured
|
||||
if ($this->client_root === null) {
|
||||
$this->client_root = $this->sanitizeFolderPath($client_root);
|
||||
}
|
||||
}
|
||||
|
||||
public function mapClientToServer(string $client_path): string
|
||||
{
|
||||
if ($this->client_root === null) {
|
||||
return $client_path;
|
||||
}
|
||||
|
||||
if (substr($client_path, 0, strlen($this->client_root)) === $this->client_root) {
|
||||
return $this->server_root . substr($client_path, strlen($this->client_root));
|
||||
}
|
||||
|
||||
return $client_path;
|
||||
}
|
||||
|
||||
public function mapServerToClient(string $server_path): string
|
||||
{
|
||||
if ($this->client_root === null) {
|
||||
return $server_path;
|
||||
}
|
||||
if (substr($server_path, 0, strlen($this->server_root)) === $this->server_root) {
|
||||
return $this->client_root . substr($server_path, strlen($this->server_root));
|
||||
}
|
||||
return $server_path;
|
||||
}
|
||||
|
||||
/** @return ($path is null ? null : string) */
|
||||
private function sanitizeFolderPath(?string $path): ?string
|
||||
{
|
||||
if ($path === null) {
|
||||
return $path;
|
||||
}
|
||||
return rtrim($path, '/');
|
||||
}
|
||||
}
|
||||
@@ -62,7 +62,7 @@ class FileReferenceCacheProvider extends InternalFileReferenceCacheProvider
|
||||
|
||||
public function __construct(Config $config)
|
||||
{
|
||||
$this->config = $config;
|
||||
parent::__construct($config);
|
||||
}
|
||||
|
||||
public function getCachedFileReferences(): ?array
|
||||
|
||||
@@ -19,30 +19,24 @@ class FileStorageCacheProvider extends InternalFileStorageCacheProvider
|
||||
{
|
||||
}
|
||||
|
||||
public function writeToCache(FileStorage $storage, string $file_contents): void
|
||||
/**
|
||||
* @param lowercase-string $file_path
|
||||
*/
|
||||
protected function storeInCache(string $file_path, FileStorage $storage): void
|
||||
{
|
||||
$file_path = strtolower($storage->file_path);
|
||||
$this->cache[$file_path] = $storage;
|
||||
}
|
||||
|
||||
public function getLatestFromCache(string $file_path, string $file_contents): ?FileStorage
|
||||
{
|
||||
$cached_value = $this->loadFromCache(strtolower($file_path));
|
||||
|
||||
if (!$cached_value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $cached_value;
|
||||
}
|
||||
|
||||
public function removeCacheForFile(string $file_path): void
|
||||
{
|
||||
unset($this->cache[strtolower($file_path)]);
|
||||
}
|
||||
|
||||
private function loadFromCache(string $file_path): ?FileStorage
|
||||
/**
|
||||
* @param lowercase-string $file_path
|
||||
*/
|
||||
protected function loadFromCache(string $file_path): ?FileStorage
|
||||
{
|
||||
return $this->cache[strtolower($file_path)] ?? null;
|
||||
return $this->cache[$file_path] ?? null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ class TextDocument
|
||||
['version' => $textDocument->version, 'uri' => $textDocument->uri],
|
||||
);
|
||||
|
||||
$file_path = LanguageServer::uriToPath($textDocument->uri);
|
||||
$file_path = $this->server->uriToPath($textDocument->uri);
|
||||
|
||||
$this->codebase->removeTemporaryFileChanges($file_path);
|
||||
$this->codebase->file_provider->openFile($file_path);
|
||||
@@ -97,7 +97,7 @@ class TextDocument
|
||||
['uri' => (array) $textDocument],
|
||||
);
|
||||
|
||||
$file_path = LanguageServer::uriToPath($textDocument->uri);
|
||||
$file_path = $this->server->uriToPath($textDocument->uri);
|
||||
|
||||
// reopen file
|
||||
$this->codebase->removeTemporaryFileChanges($file_path);
|
||||
@@ -119,7 +119,7 @@ class TextDocument
|
||||
['version' => $textDocument->version, 'uri' => $textDocument->uri],
|
||||
);
|
||||
|
||||
$file_path = LanguageServer::uriToPath($textDocument->uri);
|
||||
$file_path = $this->server->uriToPath($textDocument->uri);
|
||||
|
||||
if (count($contentChanges) === 1 && isset($contentChanges[0]) && $contentChanges[0]->range === null) {
|
||||
$new_content = $contentChanges[0]->text;
|
||||
@@ -154,7 +154,7 @@ class TextDocument
|
||||
['uri' => $textDocument->uri],
|
||||
);
|
||||
|
||||
$file_path = LanguageServer::uriToPath($textDocument->uri);
|
||||
$file_path = $this->server->uriToPath($textDocument->uri);
|
||||
|
||||
$this->codebase->file_provider->closeFile($file_path);
|
||||
$this->server->client->textDocument->publishDiagnostics($textDocument->uri, []);
|
||||
@@ -178,7 +178,7 @@ class TextDocument
|
||||
'textDocument/definition',
|
||||
);
|
||||
|
||||
$file_path = LanguageServer::uriToPath($textDocument->uri);
|
||||
$file_path = $this->server->uriToPath($textDocument->uri);
|
||||
|
||||
//This currently doesnt work right with out of project files
|
||||
if (!$this->codebase->config->isInProjectDirs($file_path)) {
|
||||
@@ -205,7 +205,7 @@ class TextDocument
|
||||
|
||||
return new Success(
|
||||
new Location(
|
||||
LanguageServer::pathToUri($code_location->file_path),
|
||||
$this->server->pathToUri($code_location->file_path),
|
||||
new Range(
|
||||
new Position($code_location->getLineNumber() - 1, $code_location->getColumn() - 1),
|
||||
new Position($code_location->getEndLineNumber() - 1, $code_location->getEndColumn() - 1),
|
||||
@@ -232,7 +232,7 @@ class TextDocument
|
||||
'textDocument/hover',
|
||||
);
|
||||
|
||||
$file_path = LanguageServer::uriToPath($textDocument->uri);
|
||||
$file_path = $this->server->uriToPath($textDocument->uri);
|
||||
|
||||
//This currently doesnt work right with out of project files
|
||||
if (!$this->codebase->config->isInProjectDirs($file_path)) {
|
||||
@@ -288,7 +288,7 @@ class TextDocument
|
||||
'textDocument/completion',
|
||||
);
|
||||
|
||||
$file_path = LanguageServer::uriToPath($textDocument->uri);
|
||||
$file_path = $this->server->uriToPath($textDocument->uri);
|
||||
|
||||
//This currently doesnt work right with out of project files
|
||||
if (!$this->codebase->config->isInProjectDirs($file_path)) {
|
||||
@@ -301,12 +301,8 @@ class TextDocument
|
||||
[$recent_type, $gap, $offset] = $completion_data;
|
||||
|
||||
if ($gap === '->' || $gap === '::') {
|
||||
$snippetSupport = ($this->server->clientCapabilities &&
|
||||
$this->server->clientCapabilities->textDocument &&
|
||||
$this->server->clientCapabilities->textDocument->completion &&
|
||||
$this->server->clientCapabilities->textDocument->completion->completionItem &&
|
||||
$this->server->clientCapabilities->textDocument->completion->completionItem->snippetSupport)
|
||||
? true : false;
|
||||
$snippetSupport = $this->server->clientCapabilities
|
||||
->textDocument->completion->completionItem->snippetSupport ?? false;
|
||||
$completion_items =
|
||||
$this->codebase->getCompletionItemsForClassishThing($recent_type, $gap, $snippetSupport);
|
||||
} elseif ($gap === '[') {
|
||||
@@ -360,7 +356,7 @@ class TextDocument
|
||||
'textDocument/signatureHelp',
|
||||
);
|
||||
|
||||
$file_path = LanguageServer::uriToPath($textDocument->uri);
|
||||
$file_path = $this->server->uriToPath($textDocument->uri);
|
||||
|
||||
//This currently doesnt work right with out of project files
|
||||
if (!$this->codebase->config->isInProjectDirs($file_path)) {
|
||||
@@ -402,10 +398,8 @@ class TextDocument
|
||||
* The code action request is sent from the client to the server to compute commands
|
||||
* for a given text document and range. These commands are typically code fixes to
|
||||
* either fix problems or to beautify/refactor code.
|
||||
*
|
||||
* @psalm-suppress PossiblyUnusedParam
|
||||
*/
|
||||
public function codeAction(TextDocumentIdentifier $textDocument, Range $range, CodeActionContext $context): Promise
|
||||
public function codeAction(TextDocumentIdentifier $textDocument, CodeActionContext $context): Promise
|
||||
{
|
||||
if (!$this->server->client->clientConfiguration->provideCodeActions) {
|
||||
return new Success(null);
|
||||
@@ -415,7 +409,7 @@ class TextDocument
|
||||
'textDocument/codeAction',
|
||||
);
|
||||
|
||||
$file_path = LanguageServer::uriToPath($textDocument->uri);
|
||||
$file_path = $this->server->uriToPath($textDocument->uri);
|
||||
|
||||
//Don't report code actions for files we arent watching
|
||||
if (!$this->codebase->config->isInProjectDirs($file_path)) {
|
||||
@@ -431,12 +425,12 @@ class TextDocument
|
||||
/** @var array{type: string, snippet: string, line_from: int, line_to: int} */
|
||||
$data = (array)$diagnostic->data;
|
||||
|
||||
//$file_path = LanguageServer::uriToPath($textDocument->uri);
|
||||
//$file_path = $this->server->uriToPath($textDocument->uri);
|
||||
//$contents = $this->codebase->file_provider->getContents($file_path);
|
||||
|
||||
$snippetRange = new Range(
|
||||
new Position($data['line_from']-1),
|
||||
new Position($data['line_to']),
|
||||
new Position($data['line_from'] - 1, 0),
|
||||
new Position($data['line_to'], 0),
|
||||
);
|
||||
|
||||
$indentation = '';
|
||||
|
||||
@@ -63,7 +63,7 @@ class Workspace
|
||||
$realFiles = array_filter(
|
||||
array_map(function (FileEvent $change) {
|
||||
try {
|
||||
return LanguageServer::uriToPath($change->uri);
|
||||
return $this->server->uriToPath($change->uri);
|
||||
} catch (InvalidArgumentException $e) {
|
||||
return null;
|
||||
}
|
||||
@@ -79,7 +79,7 @@ class Workspace
|
||||
}
|
||||
|
||||
foreach ($changes as $change) {
|
||||
$file_path = LanguageServer::uriToPath($change->uri);
|
||||
$file_path = $this->server->uriToPath($change->uri);
|
||||
|
||||
if ($composerLockFile === $file_path) {
|
||||
continue;
|
||||
@@ -108,10 +108,9 @@ class Workspace
|
||||
/**
|
||||
* A notification sent from the client to the server to signal the change of configuration settings.
|
||||
*
|
||||
* @param mixed $settings
|
||||
* @psalm-suppress PossiblyUnusedMethod, PossiblyUnusedParam
|
||||
* @psalm-suppress PossiblyUnusedMethod
|
||||
*/
|
||||
public function didChangeConfiguration($settings): void
|
||||
public function didChangeConfiguration(): void
|
||||
{
|
||||
$this->server->logDebug(
|
||||
'workspace/didChangeConfiguration',
|
||||
@@ -140,7 +139,7 @@ class Workspace
|
||||
case 'psalm.analyze.uri':
|
||||
/** @var array{uri: string} */
|
||||
$arguments = (array) $arguments;
|
||||
$file = LanguageServer::uriToPath($arguments['uri']);
|
||||
$file = $this->server->uriToPath($arguments['uri']);
|
||||
$this->codebase->reloadFiles(
|
||||
$this->project_analyzer,
|
||||
[$file],
|
||||
|
||||
@@ -839,10 +839,15 @@ class ClassLikeNodeScanner
|
||||
$classlike_storage->properties[$property_name] = new PropertyStorage();
|
||||
}
|
||||
|
||||
$classlike_storage->properties[$property_name]->type = $property_type;
|
||||
|
||||
$property_id = $fq_classlike_name . '::$' . $property_name;
|
||||
|
||||
if ($property_id === 'DateInterval::$days') {
|
||||
/** @psalm-suppress InaccessibleProperty We just parsed this type */
|
||||
$property_type->ignore_falsable_issues = true;
|
||||
}
|
||||
|
||||
$classlike_storage->properties[$property_name]->type = $property_type;
|
||||
|
||||
$classlike_storage->declaring_property_ids[$property_name] = $fq_classlike_name;
|
||||
$classlike_storage->appearing_property_ids[$property_name] = $property_id;
|
||||
}
|
||||
|
||||
@@ -32,10 +32,12 @@ use Psalm\Internal\Scanner\UnresolvedConstantComponent;
|
||||
use ReflectionClass;
|
||||
use ReflectionFunction;
|
||||
|
||||
use function array_merge;
|
||||
use function array_values;
|
||||
use function assert;
|
||||
use function class_exists;
|
||||
use function function_exists;
|
||||
use function implode;
|
||||
use function get_defined_constants;
|
||||
use function in_array;
|
||||
use function interface_exists;
|
||||
use function strtolower;
|
||||
@@ -139,7 +141,7 @@ class ExpressionResolver
|
||||
}
|
||||
|
||||
if ($stmt instanceof PhpParser\Node\Expr\ConstFetch) {
|
||||
$part0_lc = strtolower($stmt->name->parts[0]);
|
||||
$part0_lc = strtolower($stmt->name->getFirst());
|
||||
if ($part0_lc === 'false') {
|
||||
return new ScalarValue(false);
|
||||
}
|
||||
@@ -157,7 +159,7 @@ class ExpressionResolver
|
||||
}
|
||||
|
||||
return new Constant(
|
||||
implode('\\', $stmt->name->parts),
|
||||
$stmt->name->toString(),
|
||||
$stmt->name instanceof PhpParser\Node\Name\FullyQualified,
|
||||
);
|
||||
}
|
||||
@@ -190,13 +192,13 @@ class ExpressionResolver
|
||||
if ($stmt->class instanceof PhpParser\Node\Name
|
||||
&& $stmt->name instanceof PhpParser\Node\Identifier
|
||||
&& $fq_classlike_name
|
||||
&& $stmt->class->parts !== ['static']
|
||||
&& ($stmt->class->parts !== ['parent'] || $parent_fq_class_name !== null)
|
||||
&& $stmt->class->getParts() !== ['static']
|
||||
&& ($stmt->class->getParts() !== ['parent'] || $parent_fq_class_name !== null)
|
||||
) {
|
||||
if ($stmt->class->parts === ['self']) {
|
||||
if ($stmt->class->getParts() === ['self']) {
|
||||
$const_fq_class_name = $fq_classlike_name;
|
||||
} else {
|
||||
if ($stmt->class->parts === ['parent']) {
|
||||
if ($stmt->class->getParts() === ['parent']) {
|
||||
assert($parent_fq_class_name !== null);
|
||||
$const_fq_class_name = $parent_fq_class_name;
|
||||
} else {
|
||||
@@ -305,15 +307,28 @@ class ExpressionResolver
|
||||
&& $stmt->var->class instanceof PhpParser\Node\Name
|
||||
&& $stmt->var->name instanceof PhpParser\Node\Identifier
|
||||
&& $stmt->name instanceof PhpParser\Node\Identifier
|
||||
&& in_array($stmt->name->name, ['name', 'value', true])
|
||||
&& in_array($stmt->name->name, ['name', 'value'], true)
|
||||
&& ($stmt->var->class->getParts() !== ['self'] || $fq_classlike_name !== null)
|
||||
&& $stmt->var->class->getParts() !== ['static']
|
||||
&& ($stmt->var->class->getParts() !== ['parent'] || $parent_fq_class_name !== null)
|
||||
) {
|
||||
$enum_fq_class_name = ClassLikeAnalyzer::getFQCLNFromNameObject(
|
||||
$stmt->var->class,
|
||||
$aliases,
|
||||
);
|
||||
if ($stmt->var->class->getParts() === ['self']) {
|
||||
assert($fq_classlike_name !== null);
|
||||
$enum_fq_class_name = $fq_classlike_name;
|
||||
} else {
|
||||
if ($stmt->var->class->getParts() === ['parent']) {
|
||||
assert($parent_fq_class_name !== null);
|
||||
$enum_fq_class_name = $parent_fq_class_name;
|
||||
} else {
|
||||
$enum_fq_class_name = ClassLikeAnalyzer::getFQCLNFromNameObject(
|
||||
$stmt->var->class,
|
||||
$aliases,
|
||||
);
|
||||
}
|
||||
}
|
||||
if ($stmt->name->name === 'value') {
|
||||
return new EnumValueFetch($enum_fq_class_name, $stmt->var->name->name);
|
||||
} elseif ($stmt->name->name === 'name') {
|
||||
} else /*if ($stmt->name->name === 'name')*/ {
|
||||
return new EnumNameFetch($enum_fq_class_name, $stmt->var->name->name);
|
||||
}
|
||||
}
|
||||
@@ -355,18 +370,18 @@ class ExpressionResolver
|
||||
) && (
|
||||
(
|
||||
$expr->left instanceof PhpParser\Node\Expr\ConstFetch
|
||||
&& $expr->left->name->parts === ['PHP_VERSION_ID']
|
||||
&& $expr->left->name->getParts() === ['PHP_VERSION_ID']
|
||||
&& $expr->right instanceof PhpParser\Node\Scalar\LNumber
|
||||
) || (
|
||||
$expr->right instanceof PhpParser\Node\Expr\ConstFetch
|
||||
&& $expr->right->name->parts === ['PHP_VERSION_ID']
|
||||
&& $expr->right->name->getParts() === ['PHP_VERSION_ID']
|
||||
&& $expr->left instanceof PhpParser\Node\Scalar\LNumber
|
||||
)
|
||||
)
|
||||
) {
|
||||
$php_version_id = $codebase->analysis_php_version_id;
|
||||
$evaluator = new ConstExprEvaluator(static function (Expr $expr) use ($php_version_id) {
|
||||
if ($expr instanceof ConstFetch && $expr->name->parts === ['PHP_VERSION_ID']) {
|
||||
if ($expr instanceof ConstFetch && $expr->name->getParts() === ['PHP_VERSION_ID']) {
|
||||
return $php_version_id;
|
||||
}
|
||||
throw new ConstExprEvaluationException('unexpected');
|
||||
@@ -395,7 +410,7 @@ class ExpressionResolver
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($function->name->parts === ['function_exists']
|
||||
if ($function->name->getParts() === ['function_exists']
|
||||
&& isset($function->getArgs()[0])
|
||||
&& $function->getArgs()[0]->value instanceof PhpParser\Node\Scalar\String_
|
||||
&& function_exists($function->getArgs()[0]->value->value)
|
||||
@@ -409,7 +424,7 @@ class ExpressionResolver
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($function->name->parts === ['class_exists']
|
||||
if ($function->name->getParts() === ['class_exists']
|
||||
&& isset($function->getArgs()[0])
|
||||
) {
|
||||
$string_value = null;
|
||||
@@ -439,7 +454,7 @@ class ExpressionResolver
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($function->name->parts === ['interface_exists']
|
||||
if ($function->name->getParts() === ['interface_exists']
|
||||
&& isset($function->getArgs()[0])
|
||||
) {
|
||||
$string_value = null;
|
||||
@@ -469,7 +484,7 @@ class ExpressionResolver
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($function->name->parts === ['enum_exists']
|
||||
if ($function->name->getParts() === ['enum_exists']
|
||||
&& isset($function->getArgs()[0])
|
||||
) {
|
||||
$string_value = null;
|
||||
@@ -501,6 +516,19 @@ class ExpressionResolver
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($function->name->getParts() === ['defined']
|
||||
&& isset($function->getArgs()[0])
|
||||
&& $function->getArgs()[0]->value instanceof PhpParser\Node\Scalar\String_
|
||||
) {
|
||||
$predefined_constants = get_defined_constants(true);
|
||||
if (isset($predefined_constants['user'])) {
|
||||
unset($predefined_constants['user']);
|
||||
}
|
||||
$predefined_constants = array_merge(...array_values($predefined_constants));
|
||||
|
||||
return isset($predefined_constants[$function->getArgs()[0]->value->value]);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,8 +6,10 @@ use PhpParser;
|
||||
use Psalm\Aliases;
|
||||
use Psalm\Codebase;
|
||||
use Psalm\Config;
|
||||
use Psalm\Exception\DocblockParseException;
|
||||
use Psalm\Exception\FileIncludeException;
|
||||
use Psalm\Internal\Analyzer\ClassLikeAnalyzer;
|
||||
use Psalm\Internal\Analyzer\CommentAnalyzer;
|
||||
use Psalm\Internal\Analyzer\Statements\Expression\CallAnalyzer;
|
||||
use Psalm\Internal\Analyzer\Statements\Expression\Fetch\ConstFetchAnalyzer;
|
||||
use Psalm\Internal\Analyzer\Statements\Expression\IncludeAnalyzer;
|
||||
@@ -23,7 +25,6 @@ use function assert;
|
||||
use function defined;
|
||||
use function dirname;
|
||||
use function explode;
|
||||
use function implode;
|
||||
use function in_array;
|
||||
use function preg_match;
|
||||
use function strpos;
|
||||
@@ -80,7 +81,7 @@ class ExpressionScanner
|
||||
$file_storage->referenced_classlikes[strtolower($fq_classlike_name)] = $fq_classlike_name;
|
||||
}
|
||||
} elseif ($node instanceof PhpParser\Node\Expr\FuncCall && $node->name instanceof PhpParser\Node\Name) {
|
||||
$function_id = implode('\\', $node->name->parts);
|
||||
$function_id = $node->name->toString();
|
||||
|
||||
if (InternalCallMapHandler::inCallMap($function_id)) {
|
||||
self::registerClassMapFunctionCall(
|
||||
@@ -152,7 +153,29 @@ class ExpressionScanner
|
||||
$type_provider,
|
||||
$second_arg_value,
|
||||
$aliases,
|
||||
) ?? Type::getMixed();
|
||||
);
|
||||
|
||||
// allow docblocks to override the declared value to make constants in stubs configurable
|
||||
$doc_comment = $second_arg_value->getDocComment();
|
||||
if ($doc_comment) {
|
||||
try {
|
||||
$var_comments = CommentAnalyzer::getTypeFromComment($doc_comment, $file_scanner, $aliases);
|
||||
foreach ($var_comments as $var_comment) {
|
||||
if ($var_comment->type) {
|
||||
$const_type = $var_comment->type;
|
||||
}
|
||||
|
||||
// only check the first @var comment
|
||||
break;
|
||||
}
|
||||
} catch (DocblockParseException $e) {
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
|
||||
if ($const_type === null) {
|
||||
$const_type = Type::getMixed();
|
||||
}
|
||||
|
||||
$config = Config::getInstance();
|
||||
|
||||
|
||||
@@ -57,7 +57,6 @@ use function array_search;
|
||||
use function count;
|
||||
use function end;
|
||||
use function explode;
|
||||
use function implode;
|
||||
use function in_array;
|
||||
use function is_string;
|
||||
use function spl_object_id;
|
||||
@@ -371,7 +370,7 @@ class FunctionLikeNodeScanner
|
||||
&& $function_stmt->expr->expr instanceof PhpParser\Node\Expr\FuncCall
|
||||
&& $function_stmt->expr->expr->name instanceof PhpParser\Node\Name
|
||||
) {
|
||||
$inner_function_id = implode('\\', $function_stmt->expr->expr->name->parts);
|
||||
$inner_function_id = $function_stmt->expr->expr->name->toString();
|
||||
|
||||
if ($inner_function_id === 'func_get_arg'
|
||||
|| $inner_function_id === 'func_get_args'
|
||||
@@ -385,7 +384,7 @@ class FunctionLikeNodeScanner
|
||||
&& $function_stmt->cond->left->left instanceof PhpParser\Node\Expr\FuncCall
|
||||
&& $function_stmt->cond->left->left->name instanceof PhpParser\Node\Name
|
||||
) {
|
||||
$inner_function_id = implode('\\', $function_stmt->cond->left->left->name->parts);
|
||||
$inner_function_id = $function_stmt->cond->left->left->name->toString();
|
||||
|
||||
if ($inner_function_id === 'func_get_arg'
|
||||
|| $inner_function_id === 'func_get_args'
|
||||
@@ -806,7 +805,7 @@ class FunctionLikeNodeScanner
|
||||
$param_type = null;
|
||||
|
||||
$is_nullable = $param->default instanceof PhpParser\Node\Expr\ConstFetch &&
|
||||
strtolower($param->default->name->parts[0]) === 'null';
|
||||
strtolower($param->default->name->getFirst()) === 'null';
|
||||
|
||||
$param_typehint = $param->type;
|
||||
|
||||
|
||||
@@ -21,7 +21,6 @@ use Psalm\Type\Atomic\TNull;
|
||||
use Psalm\Type\Union;
|
||||
use UnexpectedValueException;
|
||||
|
||||
use function implode;
|
||||
use function strtolower;
|
||||
|
||||
/**
|
||||
@@ -137,7 +136,7 @@ class TypeHintResolver
|
||||
$codebase->scanner->queueClassLikeForScanning($fq_type_string);
|
||||
$file_storage->referenced_classlikes[strtolower($fq_type_string)] = $fq_type_string;
|
||||
} else {
|
||||
$lower_hint = strtolower($hint->parts[0]);
|
||||
$lower_hint = strtolower($hint->getFirst());
|
||||
|
||||
if ($classlike_storage
|
||||
&& ($lower_hint === 'self' || $lower_hint === 'static')
|
||||
@@ -149,7 +148,7 @@ class TypeHintResolver
|
||||
$fq_type_string .= '&static';
|
||||
}
|
||||
} else {
|
||||
$type_string = implode('\\', $hint->parts);
|
||||
$type_string = $hint->toString();
|
||||
$fq_type_string = ClassLikeAnalyzer::getFQCLNFromNameObject($hint, $aliases);
|
||||
|
||||
$codebase->scanner->queueClassLikeForScanning($fq_type_string);
|
||||
|
||||
@@ -39,7 +39,6 @@ use function array_pop;
|
||||
use function end;
|
||||
use function explode;
|
||||
use function get_class;
|
||||
use function implode;
|
||||
use function in_array;
|
||||
use function is_string;
|
||||
use function reset;
|
||||
@@ -395,7 +394,7 @@ class ReflectorVisitor extends PhpParser\NodeVisitorAbstract implements FileSour
|
||||
$this->namespace_name = $node->name;
|
||||
|
||||
$this->aliases = new Aliases(
|
||||
$node->name ? implode('\\', $node->name->parts) : '',
|
||||
$node->name ? $node->name->toString() : '',
|
||||
$this->aliases->uses,
|
||||
$this->aliases->functions,
|
||||
$this->aliases->constants,
|
||||
@@ -414,7 +413,7 @@ class ReflectorVisitor extends PhpParser\NodeVisitorAbstract implements FileSour
|
||||
private function handleUse(PhpParser\Node\Stmt\Use_ $node): void
|
||||
{
|
||||
foreach ($node->uses as $use) {
|
||||
$use_path = implode('\\', $use->name->parts);
|
||||
$use_path = $use->name->toString();
|
||||
|
||||
$use_alias = $use->alias->name ?? $use->name->getLast();
|
||||
|
||||
@@ -445,10 +444,10 @@ class ReflectorVisitor extends PhpParser\NodeVisitorAbstract implements FileSour
|
||||
|
||||
private function handleGroupUse(PhpParser\Node\Stmt\GroupUse $node): void
|
||||
{
|
||||
$use_prefix = implode('\\', $node->prefix->parts);
|
||||
$use_prefix = $node->prefix->toString();
|
||||
|
||||
foreach ($node->uses as $use) {
|
||||
$use_path = $use_prefix . '\\' . implode('\\', $use->name->parts);
|
||||
$use_path = $use_prefix . '\\' . $use->name->toString();
|
||||
$use_alias = $use->alias->name ?? $use->name->getLast();
|
||||
|
||||
switch ($use->type !== PhpParser\Node\Stmt\Use_::TYPE_UNKNOWN ? $use->type : $node->type) {
|
||||
@@ -490,13 +489,13 @@ class ReflectorVisitor extends PhpParser\NodeVisitorAbstract implements FileSour
|
||||
|
||||
if ($this->codebase->register_stub_files
|
||||
&& $node->name
|
||||
&& $node->name->parts === ['PHPSTORM_META']
|
||||
&& $node->name->getParts() === ['PHPSTORM_META']
|
||||
) {
|
||||
foreach ($node->stmts as $meta_stmt) {
|
||||
if ($meta_stmt instanceof PhpParser\Node\Stmt\Expression
|
||||
&& $meta_stmt->expr instanceof PhpParser\Node\Expr\FuncCall
|
||||
&& $meta_stmt->expr->name instanceof Name
|
||||
&& $meta_stmt->expr->name->parts === ['override']
|
||||
&& $meta_stmt->expr->name->getParts() === ['override']
|
||||
) {
|
||||
PhpStormMetaScanner::handleOverride($meta_stmt->expr->getArgs(), $this->codebase);
|
||||
}
|
||||
|
||||
@@ -32,13 +32,13 @@ class HtmlFunctionTainter implements AddTaintsInterface, RemoveTaintsInterface
|
||||
|| !$item instanceof PhpParser\Node\Expr\FuncCall
|
||||
|| $item->isFirstClassCallable()
|
||||
|| !$item->name instanceof PhpParser\Node\Name
|
||||
|| count($item->name->parts) !== 1
|
||||
|| count($item->name->getParts()) !== 1
|
||||
|| count($item->getArgs()) === 0
|
||||
) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$function_id = strtolower($item->name->parts[0]);
|
||||
$function_id = strtolower($item->name->getFirst());
|
||||
|
||||
if ($function_id === 'html_entity_decode'
|
||||
|| $function_id === 'htmlspecialchars_decode'
|
||||
@@ -84,13 +84,13 @@ class HtmlFunctionTainter implements AddTaintsInterface, RemoveTaintsInterface
|
||||
|| !$item instanceof PhpParser\Node\Expr\FuncCall
|
||||
|| $item->isFirstClassCallable()
|
||||
|| !$item->name instanceof PhpParser\Node\Name
|
||||
|| count($item->name->parts) !== 1
|
||||
|| count($item->name->getParts()) !== 1
|
||||
|| count($item->getArgs()) === 0
|
||||
) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$function_id = strtolower($item->name->parts[0]);
|
||||
$function_id = strtolower($item->name->getFirst());
|
||||
|
||||
if ($function_id === 'htmlentities'
|
||||
|| $function_id === 'htmlspecialchars'
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace Psalm\Internal\Provider;
|
||||
|
||||
use Psalm\Config;
|
||||
use Psalm\Internal\Cache;
|
||||
use Psalm\Storage\ClassLikeStorage;
|
||||
use RuntimeException;
|
||||
use UnexpectedValueException;
|
||||
@@ -10,22 +11,15 @@ use UnexpectedValueException;
|
||||
use function array_merge;
|
||||
use function dirname;
|
||||
use function file_exists;
|
||||
use function file_put_contents;
|
||||
use function filemtime;
|
||||
use function get_class;
|
||||
use function hash;
|
||||
use function igbinary_serialize;
|
||||
use function igbinary_unserialize;
|
||||
use function is_dir;
|
||||
use function is_null;
|
||||
use function mkdir;
|
||||
use function serialize;
|
||||
use function strtolower;
|
||||
use function unlink;
|
||||
use function unserialize;
|
||||
|
||||
use const DIRECTORY_SEPARATOR;
|
||||
use const LOCK_EX;
|
||||
use const PHP_VERSION_ID;
|
||||
|
||||
/**
|
||||
@@ -33,7 +27,7 @@ use const PHP_VERSION_ID;
|
||||
*/
|
||||
class ClassLikeStorageCacheProvider
|
||||
{
|
||||
private Config $config;
|
||||
private Cache $cache;
|
||||
|
||||
private string $modified_timestamps = '';
|
||||
|
||||
@@ -41,7 +35,7 @@ class ClassLikeStorageCacheProvider
|
||||
|
||||
public function __construct(Config $config)
|
||||
{
|
||||
$this->config = $config;
|
||||
$this->cache = new Cache($config);
|
||||
|
||||
$storage_dir = dirname(__DIR__, 2) . DIRECTORY_SEPARATOR . 'Storage' . DIRECTORY_SEPARATOR;
|
||||
|
||||
@@ -64,7 +58,7 @@ class ClassLikeStorageCacheProvider
|
||||
$this->modified_timestamps .= ' ' . filemtime($dependent_file_path);
|
||||
}
|
||||
|
||||
$this->modified_timestamps .= $this->config->computeHash();
|
||||
$this->modified_timestamps .= $config->computeHash();
|
||||
}
|
||||
|
||||
public function writeToCache(ClassLikeStorage $storage, string $file_path, string $file_contents): void
|
||||
@@ -80,11 +74,7 @@ class ClassLikeStorageCacheProvider
|
||||
}
|
||||
|
||||
$cache_location = $this->getCacheLocationForClass($fq_classlike_name_lc, $file_path, true);
|
||||
if ($this->config->use_igbinary) {
|
||||
file_put_contents($cache_location, igbinary_serialize($storage), LOCK_EX);
|
||||
} else {
|
||||
file_put_contents($cache_location, serialize($storage), LOCK_EX);
|
||||
}
|
||||
$this->cache->saveItem($cache_location, $storage);
|
||||
}
|
||||
|
||||
public function getLatestFromCache(
|
||||
@@ -104,7 +94,7 @@ class ClassLikeStorageCacheProvider
|
||||
if (@get_class($cached_value) === '__PHP_Incomplete_Class'
|
||||
|| $cache_hash !== $cached_value->hash
|
||||
) {
|
||||
unlink($this->getCacheLocationForClass($fq_classlike_name_lc, $file_path));
|
||||
$this->cache->deleteItem($this->getCacheLocationForClass($fq_classlike_name_lc, $file_path));
|
||||
|
||||
throw new UnexpectedValueException($fq_classlike_name_lc . ' should not be outdated');
|
||||
}
|
||||
@@ -118,31 +108,11 @@ class ClassLikeStorageCacheProvider
|
||||
return PHP_VERSION_ID >= 8_01_00 ? hash('xxh128', $data) : hash('md4', $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @psalm-suppress MixedAssignment
|
||||
*/
|
||||
private function loadFromCache(string $fq_classlike_name_lc, ?string $file_path): ?ClassLikeStorage
|
||||
{
|
||||
$cache_location = $this->getCacheLocationForClass($fq_classlike_name_lc, $file_path);
|
||||
|
||||
if (file_exists($cache_location)) {
|
||||
if ($this->config->use_igbinary) {
|
||||
$storage = igbinary_unserialize(Providers::safeFileGetContents($cache_location));
|
||||
|
||||
if ($storage instanceof ClassLikeStorage) {
|
||||
return $storage;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
$storage = unserialize(Providers::safeFileGetContents($cache_location));
|
||||
|
||||
if ($storage instanceof ClassLikeStorage) {
|
||||
return $storage;
|
||||
}
|
||||
|
||||
return null;
|
||||
$storage = $this->cache->getItem($this->getCacheLocationForClass($fq_classlike_name_lc, $file_path));
|
||||
if ($storage instanceof ClassLikeStorage) {
|
||||
return $storage;
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -153,7 +123,7 @@ class ClassLikeStorageCacheProvider
|
||||
?string $file_path,
|
||||
bool $create_directory = false
|
||||
): string {
|
||||
$root_cache_directory = $this->config->getCacheDirectory();
|
||||
$root_cache_directory = $this->cache->getCacheDirectory();
|
||||
|
||||
if (!$root_cache_directory) {
|
||||
throw new UnexpectedValueException('No cache directory defined');
|
||||
@@ -186,6 +156,6 @@ class ClassLikeStorageCacheProvider
|
||||
return $parser_cache_directory
|
||||
. DIRECTORY_SEPARATOR
|
||||
. $file_path_sha
|
||||
. ($this->config->use_igbinary ? '-igbinary' : '');
|
||||
. ($this->cache->use_igbinary ? '-igbinary' : '');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,6 +68,12 @@ class FakeFileProvider extends FileProvider
|
||||
$this->fake_file_times[$file_path] = (int)microtime(true);
|
||||
}
|
||||
|
||||
public function deleteFile(string $file_path): void
|
||||
{
|
||||
unset($this->fake_files[$file_path]);
|
||||
unset($this->fake_file_times[$file_path]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string> $file_extensions
|
||||
* @param null|callable(string):bool $filter
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -14,13 +14,12 @@ use function array_keys;
|
||||
use function array_merge;
|
||||
use function array_unique;
|
||||
use function explode;
|
||||
use function file_exists;
|
||||
|
||||
/**
|
||||
* @psalm-import-type FileMapType from Analyzer
|
||||
*
|
||||
* Used to determine which files reference other files, necessary for using the --diff
|
||||
* option from the command line.
|
||||
*
|
||||
* @psalm-import-type FileMapType from Analyzer
|
||||
* @internal
|
||||
*/
|
||||
class FileReferenceProvider
|
||||
@@ -164,10 +163,12 @@ class FileReferenceProvider
|
||||
*/
|
||||
private static array $method_param_uses = [];
|
||||
|
||||
private FileProvider $file_provider;
|
||||
public ?FileReferenceCacheProvider $cache = null;
|
||||
|
||||
public function __construct(?FileReferenceCacheProvider $cache = null)
|
||||
public function __construct(FileProvider $file_provider, ?FileReferenceCacheProvider $cache = null)
|
||||
{
|
||||
$this->file_provider = $file_provider;
|
||||
$this->cache = $cache;
|
||||
}
|
||||
|
||||
@@ -179,7 +180,7 @@ class FileReferenceProvider
|
||||
if (self::$deleted_files === null) {
|
||||
self::$deleted_files = array_filter(
|
||||
array_keys(self::$file_references),
|
||||
static fn(string $file_name): bool => !file_exists($file_name)
|
||||
fn(string $file_name): bool => !$this->file_provider->fileExists($file_name)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace Psalm\Internal\Provider;
|
||||
|
||||
use Psalm\Config;
|
||||
use Psalm\Internal\Cache;
|
||||
use Psalm\Storage\FileStorage;
|
||||
use RuntimeException;
|
||||
use UnexpectedValueException;
|
||||
@@ -10,21 +11,14 @@ use UnexpectedValueException;
|
||||
use function array_merge;
|
||||
use function dirname;
|
||||
use function file_exists;
|
||||
use function file_put_contents;
|
||||
use function filemtime;
|
||||
use function get_class;
|
||||
use function hash;
|
||||
use function igbinary_serialize;
|
||||
use function igbinary_unserialize;
|
||||
use function is_dir;
|
||||
use function mkdir;
|
||||
use function serialize;
|
||||
use function strtolower;
|
||||
use function unlink;
|
||||
use function unserialize;
|
||||
|
||||
use const DIRECTORY_SEPARATOR;
|
||||
use const LOCK_EX;
|
||||
use const PHP_VERSION_ID;
|
||||
|
||||
/**
|
||||
@@ -34,13 +28,13 @@ class FileStorageCacheProvider
|
||||
{
|
||||
private string $modified_timestamps = '';
|
||||
|
||||
private Config $config;
|
||||
private Cache $cache;
|
||||
|
||||
private const FILE_STORAGE_CACHE_DIRECTORY = 'file_cache';
|
||||
|
||||
public function __construct(Config $config)
|
||||
{
|
||||
$this->config = $config;
|
||||
$this->cache = new Cache($config);
|
||||
|
||||
$storage_dir = dirname(__DIR__, 2) . DIRECTORY_SEPARATOR . 'Storage' . DIRECTORY_SEPARATOR;
|
||||
|
||||
@@ -64,20 +58,24 @@ class FileStorageCacheProvider
|
||||
$this->modified_timestamps .= ' ' . filemtime($dependent_file_path);
|
||||
}
|
||||
|
||||
$this->modified_timestamps .= $this->config->computeHash();
|
||||
$this->modified_timestamps .= $config->computeHash();
|
||||
}
|
||||
|
||||
public function writeToCache(FileStorage $storage, string $file_contents): void
|
||||
{
|
||||
$file_path = strtolower($storage->file_path);
|
||||
$cache_location = $this->getCacheLocationForPath($file_path, true);
|
||||
$storage->hash = $this->getCacheHash($file_path, $file_contents);
|
||||
|
||||
if ($this->config->use_igbinary) {
|
||||
file_put_contents($cache_location, igbinary_serialize($storage), LOCK_EX);
|
||||
} else {
|
||||
file_put_contents($cache_location, serialize($storage), LOCK_EX);
|
||||
}
|
||||
$this->storeInCache($file_path, $storage);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param lowercase-string $file_path
|
||||
*/
|
||||
protected function storeInCache(string $file_path, FileStorage $storage): void
|
||||
{
|
||||
$cache_location = $this->getCacheLocationForPath($file_path, true);
|
||||
$this->cache->saveItem($cache_location, $storage);
|
||||
}
|
||||
|
||||
public function getLatestFromCache(string $file_path, string $file_contents): ?FileStorage
|
||||
@@ -105,11 +103,7 @@ class FileStorageCacheProvider
|
||||
|
||||
public function removeCacheForFile(string $file_path): void
|
||||
{
|
||||
$cache_path = $this->getCacheLocationForPath($file_path);
|
||||
|
||||
if (file_exists($cache_path)) {
|
||||
unlink($cache_path);
|
||||
}
|
||||
$this->cache->deleteItem($this->getCacheLocationForPath($file_path));
|
||||
}
|
||||
|
||||
private function getCacheHash(string $_unused_file_path, string $file_contents): string
|
||||
@@ -122,30 +116,13 @@ class FileStorageCacheProvider
|
||||
}
|
||||
|
||||
/**
|
||||
* @psalm-suppress MixedAssignment
|
||||
* @param lowercase-string $file_path
|
||||
*/
|
||||
private function loadFromCache(string $file_path): ?FileStorage
|
||||
protected function loadFromCache(string $file_path): ?FileStorage
|
||||
{
|
||||
$cache_location = $this->getCacheLocationForPath($file_path);
|
||||
|
||||
if (file_exists($cache_location)) {
|
||||
if ($this->config->use_igbinary) {
|
||||
$storage = igbinary_unserialize(Providers::safeFileGetContents($cache_location));
|
||||
|
||||
if ($storage instanceof FileStorage) {
|
||||
return $storage;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
$storage = unserialize(Providers::safeFileGetContents($cache_location));
|
||||
|
||||
if ($storage instanceof FileStorage) {
|
||||
return $storage;
|
||||
}
|
||||
|
||||
return null;
|
||||
$storage = $this->cache->getItem($this->getCacheLocationForPath($file_path));
|
||||
if ($storage instanceof FileStorage) {
|
||||
return $storage;
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -153,7 +130,7 @@ class FileStorageCacheProvider
|
||||
|
||||
private function getCacheLocationForPath(string $file_path, bool $create_directory = false): string
|
||||
{
|
||||
$root_cache_directory = $this->config->getCacheDirectory();
|
||||
$root_cache_directory = $this->cache->getCacheDirectory();
|
||||
|
||||
if (!$root_cache_directory) {
|
||||
throw new UnexpectedValueException('No cache directory defined');
|
||||
@@ -188,6 +165,6 @@ class FileStorageCacheProvider
|
||||
return $parser_cache_directory
|
||||
. DIRECTORY_SEPARATOR
|
||||
. $hash
|
||||
. ($this->config->use_igbinary ? '-igbinary' : '');
|
||||
. ($this->cache->use_igbinary ? '-igbinary' : '');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,6 +39,7 @@ use Psalm\Internal\Provider\ReturnTypeProvider\ParseUrlReturnTypeProvider;
|
||||
use Psalm\Internal\Provider\ReturnTypeProvider\PowReturnTypeProvider;
|
||||
use Psalm\Internal\Provider\ReturnTypeProvider\RandReturnTypeProvider;
|
||||
use Psalm\Internal\Provider\ReturnTypeProvider\RoundReturnTypeProvider;
|
||||
use Psalm\Internal\Provider\ReturnTypeProvider\SprintfReturnTypeProvider;
|
||||
use Psalm\Internal\Provider\ReturnTypeProvider\StrReplaceReturnTypeProvider;
|
||||
use Psalm\Internal\Provider\ReturnTypeProvider\StrTrReturnTypeProvider;
|
||||
use Psalm\Internal\Provider\ReturnTypeProvider\TriggerErrorReturnTypeProvider;
|
||||
@@ -105,6 +106,7 @@ class FunctionReturnTypeProvider
|
||||
$this->registerClass(MbInternalEncodingReturnTypeProvider::class);
|
||||
$this->registerClass(DateReturnTypeProvider::class);
|
||||
$this->registerClass(PowReturnTypeProvider::class);
|
||||
$this->registerClass(SprintfReturnTypeProvider::class);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -6,6 +6,7 @@ use JsonException;
|
||||
use PhpParser;
|
||||
use PhpParser\Node\Stmt;
|
||||
use Psalm\Config;
|
||||
use Psalm\Internal\Cache;
|
||||
use RuntimeException;
|
||||
use UnexpectedValueException;
|
||||
|
||||
@@ -15,20 +16,15 @@ use function file_put_contents;
|
||||
use function filemtime;
|
||||
use function gettype;
|
||||
use function hash;
|
||||
use function igbinary_serialize;
|
||||
use function igbinary_unserialize;
|
||||
use function is_array;
|
||||
use function is_dir;
|
||||
use function is_readable;
|
||||
use function is_writable;
|
||||
use function is_string;
|
||||
use function json_decode;
|
||||
use function json_encode;
|
||||
use function mkdir;
|
||||
use function scandir;
|
||||
use function serialize;
|
||||
use function touch;
|
||||
use function unlink;
|
||||
use function unserialize;
|
||||
|
||||
use const DIRECTORY_SEPARATOR;
|
||||
use const JSON_THROW_ON_ERROR;
|
||||
@@ -45,7 +41,7 @@ class ParserCacheProvider
|
||||
private const PARSER_CACHE_DIRECTORY = 'php-parser';
|
||||
private const FILE_CONTENTS_CACHE_DIRECTORY = 'file-caches';
|
||||
|
||||
private Config $config;
|
||||
private Cache $cache;
|
||||
|
||||
/**
|
||||
* A map of filename hashes to contents hashes
|
||||
@@ -65,7 +61,7 @@ class ParserCacheProvider
|
||||
|
||||
public function __construct(Config $config, bool $use_file_cache = true)
|
||||
{
|
||||
$this->config = $config;
|
||||
$this->cache = new Cache($config);
|
||||
$this->use_file_cache = $use_file_cache;
|
||||
}
|
||||
|
||||
@@ -92,15 +88,12 @@ class ParserCacheProvider
|
||||
&& is_readable($cache_location)
|
||||
&& filemtime($cache_location) > $file_modified_time
|
||||
) {
|
||||
if ($this->config->use_igbinary) {
|
||||
/** @var list<Stmt> */
|
||||
$stmts = igbinary_unserialize(Providers::safeFileGetContents($cache_location));
|
||||
} else {
|
||||
/** @var list<Stmt> */
|
||||
$stmts = unserialize(Providers::safeFileGetContents($cache_location));
|
||||
}
|
||||
$stmts = $this->cache->getItem($cache_location);
|
||||
|
||||
return $stmts;
|
||||
if (is_array($stmts)) {
|
||||
/** @var list<Stmt> $stmts */
|
||||
return $stmts;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -118,13 +111,12 @@ class ParserCacheProvider
|
||||
$cache_location = $this->getCacheLocationForPath($file_path, self::PARSER_CACHE_DIRECTORY);
|
||||
|
||||
if (is_readable($cache_location)) {
|
||||
if ($this->config->use_igbinary) {
|
||||
/** @var list<Stmt> */
|
||||
return igbinary_unserialize(Providers::safeFileGetContents($cache_location)) ?: null;
|
||||
}
|
||||
$stmts = $this->cache->getItem($cache_location);
|
||||
|
||||
/** @var list<Stmt> */
|
||||
return unserialize(Providers::safeFileGetContents($cache_location)) ?: null;
|
||||
if (is_array($stmts)) {
|
||||
/** @var list<Stmt> $stmts */
|
||||
return $stmts;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -138,11 +130,13 @@ class ParserCacheProvider
|
||||
|
||||
$cache_location = $this->getCacheLocationForPath($file_path, self::FILE_CONTENTS_CACHE_DIRECTORY);
|
||||
|
||||
if (is_readable($cache_location)) {
|
||||
return Providers::safeFileGetContents($cache_location);
|
||||
$cache_item = $this->cache->getItem($cache_location);
|
||||
|
||||
if (!is_string($cache_item)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return null;
|
||||
return $cache_item;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -155,7 +149,7 @@ class ParserCacheProvider
|
||||
}
|
||||
|
||||
if ($this->existing_file_content_hashes === null) {
|
||||
$root_cache_directory = $this->config->getCacheDirectory();
|
||||
$root_cache_directory = $this->cache->getCacheDirectory();
|
||||
$file_hashes_path = $root_cache_directory . DIRECTORY_SEPARATOR . self::FILE_HASHES;
|
||||
|
||||
if (!$root_cache_directory) {
|
||||
@@ -234,11 +228,7 @@ class ParserCacheProvider
|
||||
if ($touch_only) {
|
||||
touch($cache_location);
|
||||
} else {
|
||||
if ($this->config->use_igbinary) {
|
||||
file_put_contents($cache_location, igbinary_serialize($stmts), LOCK_EX);
|
||||
} else {
|
||||
file_put_contents($cache_location, serialize($stmts), LOCK_EX);
|
||||
}
|
||||
$this->cache->saveItem($cache_location, $stmts);
|
||||
|
||||
$file_cache_key = $this->getParserCacheKey($file_path);
|
||||
$this->new_file_content_hashes[$file_cache_key] = $file_content_hash;
|
||||
@@ -267,7 +257,7 @@ class ParserCacheProvider
|
||||
return;
|
||||
}
|
||||
|
||||
$root_cache_directory = $this->config->getCacheDirectory();
|
||||
$root_cache_directory = $this->cache->getCacheDirectory();
|
||||
|
||||
if (!$root_cache_directory) {
|
||||
return;
|
||||
@@ -299,13 +289,12 @@ class ParserCacheProvider
|
||||
}
|
||||
|
||||
$cache_location = $this->getCacheLocationForPath($file_path, self::FILE_CONTENTS_CACHE_DIRECTORY, true);
|
||||
|
||||
file_put_contents($cache_location, $file_contents, LOCK_EX);
|
||||
$this->cache->saveItem($cache_location, $file_contents);
|
||||
}
|
||||
|
||||
public function deleteOldParserCaches(float $time_before): int
|
||||
{
|
||||
$cache_directory = $this->config->getCacheDirectory();
|
||||
$cache_directory = $this->cache->getCacheDirectory();
|
||||
|
||||
$this->existing_file_content_hashes = null;
|
||||
$this->new_file_content_hashes = [];
|
||||
@@ -328,8 +317,8 @@ class ParserCacheProvider
|
||||
continue;
|
||||
}
|
||||
|
||||
if (filemtime($full_path) < $time_before && is_writable($full_path)) {
|
||||
unlink($full_path);
|
||||
if (filemtime($full_path) < $time_before) {
|
||||
$this->cache->deleteItem($full_path);
|
||||
++$removed_count;
|
||||
}
|
||||
}
|
||||
@@ -346,7 +335,7 @@ class ParserCacheProvider
|
||||
$hash = hash('md4', $file_path);
|
||||
}
|
||||
|
||||
return $hash . ($this->config->use_igbinary ? '-igbinary' : '') . '-r';
|
||||
return $hash . ($this->cache->use_igbinary ? '-igbinary' : '') . '-r';
|
||||
}
|
||||
|
||||
|
||||
@@ -355,7 +344,7 @@ class ParserCacheProvider
|
||||
string $subdirectory,
|
||||
bool $create_directory = false
|
||||
): string {
|
||||
$root_cache_directory = $this->config->getCacheDirectory();
|
||||
$root_cache_directory = $this->cache->getCacheDirectory();
|
||||
|
||||
if (!$root_cache_directory) {
|
||||
throw new UnexpectedValueException('No cache directory defined');
|
||||
|
||||
@@ -78,20 +78,19 @@ class ProjectCacheProvider
|
||||
|
||||
public function hasLockfileChanged(): bool
|
||||
{
|
||||
if (!file_exists($this->composer_lock_location)) {
|
||||
return true;
|
||||
}
|
||||
if (file_exists($this->composer_lock_location)) {
|
||||
$lockfile_contents = Providers::safeFileGetContents($this->composer_lock_location);
|
||||
if (!$lockfile_contents) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$lockfile_contents = Providers::safeFileGetContents($this->composer_lock_location);
|
||||
|
||||
if (!$lockfile_contents) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (PHP_VERSION_ID >= 8_01_00) {
|
||||
$hash = hash('xxh128', $lockfile_contents);
|
||||
if (PHP_VERSION_ID >= 8_01_00) {
|
||||
$hash = hash('xxh128', $lockfile_contents);
|
||||
} else {
|
||||
$hash = hash('md4', $lockfile_contents);
|
||||
}
|
||||
} else {
|
||||
$hash = hash('md4', $lockfile_contents);
|
||||
$hash = '';
|
||||
}
|
||||
|
||||
$changed = $hash !== $this->getComposerLockHash();
|
||||
|
||||
@@ -51,7 +51,7 @@ class Providers
|
||||
$parser_cache_provider,
|
||||
$file_storage_cache_provider,
|
||||
);
|
||||
$this->file_reference_provider = new FileReferenceProvider($file_reference_cache_provider);
|
||||
$this->file_reference_provider = new FileReferenceProvider($file_provider, $file_reference_cache_provider);
|
||||
}
|
||||
|
||||
public static function safeFileGetContents(string $path): string
|
||||
|
||||
@@ -7,7 +7,6 @@ use Psalm\Plugin\EventHandler\Event\FunctionReturnTypeProviderEvent;
|
||||
use Psalm\Plugin\EventHandler\FunctionReturnTypeProviderInterface;
|
||||
use Psalm\Type;
|
||||
use Psalm\Type\Atomic\TArray;
|
||||
use Psalm\Type\Atomic\TIntRange;
|
||||
use Psalm\Type\Atomic\TKeyedArray;
|
||||
use Psalm\Type\Atomic\TNonEmptyArray;
|
||||
use Psalm\Type\Union;
|
||||
@@ -99,10 +98,10 @@ class ArrayFillReturnTypeProvider implements FunctionReturnTypeProviderInterface
|
||||
) {
|
||||
return new Union([
|
||||
new TNonEmptyArray([
|
||||
new Union([new TIntRange(
|
||||
Type::getIntRange(
|
||||
$first_arg_type->getSingleIntLiteral()->value,
|
||||
$second_arg_type->getSingleIntLiteral()->value,
|
||||
)]),
|
||||
),
|
||||
$value_type_from_third_arg,
|
||||
]),
|
||||
]);
|
||||
|
||||
@@ -9,6 +9,7 @@ use Psalm\Plugin\EventHandler\Event\FunctionReturnTypeProviderEvent;
|
||||
use Psalm\Plugin\EventHandler\FunctionReturnTypeProviderInterface;
|
||||
use Psalm\Type;
|
||||
use Psalm\Type\Atomic\TLiteralInt;
|
||||
use Psalm\Type\Atomic\TNonEmptyString;
|
||||
use Psalm\Type\Union;
|
||||
|
||||
use function array_values;
|
||||
@@ -61,6 +62,16 @@ class DirnameReturnTypeProvider implements FunctionReturnTypeProviderInterface
|
||||
$statements_source->getCodebase()->config,
|
||||
);
|
||||
|
||||
if ($evaled_path === null) {
|
||||
$type = $node_type_provider->getType($call_args[0]->value);
|
||||
if ($type !== null && $type->isSingle()) {
|
||||
$atomic_type = array_values($type->getAtomicTypes())[0];
|
||||
if ($atomic_type instanceof TNonEmptyString) {
|
||||
return Type::getNonEmptyString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($evaled_path === null) {
|
||||
return Type::getString();
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ use Psalm\Internal\Analyzer\StatementsAnalyzer;
|
||||
use Psalm\Plugin\EventHandler\Event\MethodReturnTypeProviderEvent;
|
||||
use Psalm\Plugin\EventHandler\MethodReturnTypeProviderInterface;
|
||||
use Psalm\Type;
|
||||
use Psalm\Type\Atomic\TIntRange;
|
||||
use Psalm\Type\Atomic\TKeyedArray;
|
||||
use Psalm\Type\Atomic\TLiteralInt;
|
||||
use Psalm\Type\Union;
|
||||
@@ -57,10 +56,10 @@ class ImagickPixelColorReturnTypeProvider implements MethodReturnTypeProviderInt
|
||||
if (isset($formats[0])) {
|
||||
$types []= new Union([
|
||||
new TKeyedArray([
|
||||
'r' => new Union([new TIntRange(0, 255)]),
|
||||
'g' => new Union([new TIntRange(0, 255)]),
|
||||
'b' => new Union([new TIntRange(0, 255)]),
|
||||
'a' => new Union([new TIntRange(0, 1)]),
|
||||
'r' => Type::getIntRange(0, 255),
|
||||
'g' => Type::getIntRange(0, 255),
|
||||
'b' => Type::getIntRange(0, 255),
|
||||
'a' => Type::getIntRange(0, 1),
|
||||
]),
|
||||
]);
|
||||
}
|
||||
@@ -77,10 +76,10 @@ class ImagickPixelColorReturnTypeProvider implements MethodReturnTypeProviderInt
|
||||
if (isset($formats[2])) {
|
||||
$types []= new Union([
|
||||
new TKeyedArray([
|
||||
'r' => new Union([new TIntRange(0, 255)]),
|
||||
'g' => new Union([new TIntRange(0, 255)]),
|
||||
'b' => new Union([new TIntRange(0, 255)]),
|
||||
'a' => new Union([new TIntRange(0, 255)]),
|
||||
'r' => Type::getIntRange(0, 255),
|
||||
'g' => Type::getIntRange(0, 255),
|
||||
'b' => Type::getIntRange(0, 255),
|
||||
'a' => Type::getIntRange(0, 255),
|
||||
]),
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -127,7 +127,7 @@ class MinMaxReturnTypeProvider implements FunctionReturnTypeProviderInterface
|
||||
return Type::getInt(false, $min_potential_int);
|
||||
}
|
||||
|
||||
return new Union([new TIntRange($min_potential_int, $max_potential_int)]);
|
||||
return Type::getIntRange($min_potential_int, $max_potential_int);
|
||||
}
|
||||
|
||||
//if we're dealing with non-int elements, just combine them all together
|
||||
|
||||
@@ -27,88 +27,268 @@ class PdoStatementReturnTypeProvider implements MethodReturnTypeProviderInterfac
|
||||
public static function getMethodReturnType(MethodReturnTypeProviderEvent $event): ?Union
|
||||
{
|
||||
$config = Config::getInstance();
|
||||
$method_name_lowercase = $event->getMethodNameLowercase();
|
||||
|
||||
if (!$config->php_extensions["pdo"]) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($method_name_lowercase === 'fetch') {
|
||||
return self::handleFetch($event);
|
||||
}
|
||||
|
||||
if ($method_name_lowercase === 'fetchall') {
|
||||
return self::handleFetchAll($event);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static function handleFetch(MethodReturnTypeProviderEvent $event): ?Union
|
||||
{
|
||||
$source = $event->getSource();
|
||||
$call_args = $event->getCallArgs();
|
||||
$method_name_lowercase = $event->getMethodNameLowercase();
|
||||
if ($method_name_lowercase === 'fetch'
|
||||
&& $config->php_extensions["pdo"]
|
||||
&& isset($call_args[0])
|
||||
$fetch_mode = 0;
|
||||
|
||||
if (isset($call_args[0])
|
||||
&& ($first_arg_type = $source->getNodeTypeProvider()->getType($call_args[0]->value))
|
||||
&& $first_arg_type->isSingleIntLiteral()
|
||||
) {
|
||||
$fetch_mode = $first_arg_type->getSingleIntLiteral()->value;
|
||||
}
|
||||
|
||||
switch ($fetch_mode) {
|
||||
case 2: // PDO::FETCH_ASSOC - array<string,scalar|null>|false
|
||||
return new Union([
|
||||
new TArray([
|
||||
Type::getString(),
|
||||
new Union([
|
||||
new TScalar(),
|
||||
new TNull(),
|
||||
switch ($fetch_mode) {
|
||||
case 2: // PDO::FETCH_ASSOC - array<string,scalar|null>|false
|
||||
return new Union([
|
||||
new TArray([
|
||||
Type::getString(),
|
||||
new Union([
|
||||
new TScalar(),
|
||||
new TNull(),
|
||||
]),
|
||||
]),
|
||||
new TFalse(),
|
||||
]);
|
||||
|
||||
case 4: // PDO::FETCH_BOTH - array<array-key,scalar|null>|false
|
||||
return new Union([
|
||||
new TArray([
|
||||
Type::getArrayKey(),
|
||||
new Union([
|
||||
new TScalar(),
|
||||
new TNull(),
|
||||
]),
|
||||
]),
|
||||
new TFalse(),
|
||||
]);
|
||||
|
||||
case 6: // PDO::FETCH_BOUND - bool
|
||||
return Type::getBool();
|
||||
|
||||
case 7: // PDO::FETCH_COLUMN - scalar|null|false
|
||||
return new Union([
|
||||
new TScalar(),
|
||||
new TNull(),
|
||||
new TFalse(),
|
||||
]);
|
||||
|
||||
case 8: // PDO::FETCH_CLASS - object|false
|
||||
return new Union([
|
||||
new TObject(),
|
||||
new TFalse(),
|
||||
]);
|
||||
|
||||
case 1: // PDO::FETCH_LAZY - object|false
|
||||
// This actually returns a PDORow object, but that class is
|
||||
// undocumented, and its attributes are all dynamic anyway
|
||||
return new Union([
|
||||
new TObject(),
|
||||
new TFalse(),
|
||||
]);
|
||||
|
||||
case 11: // PDO::FETCH_NAMED - array<string, scalar|null|list<scalar|null>>|false
|
||||
return new Union([
|
||||
new TArray([
|
||||
Type::getString(),
|
||||
new Union([
|
||||
new TScalar(),
|
||||
new TNull(),
|
||||
Type::getListAtomic(
|
||||
new Union([
|
||||
new TScalar(),
|
||||
new TNull(),
|
||||
]),
|
||||
),
|
||||
]),
|
||||
]),
|
||||
new TFalse(),
|
||||
]);
|
||||
|
||||
case 12: // PDO::FETCH_KEY_PAIR - array<array-key,scalar|null>
|
||||
return new Union([
|
||||
new TArray([
|
||||
Type::getArrayKey(),
|
||||
new Union([
|
||||
new TScalar(),
|
||||
new TNull(),
|
||||
]),
|
||||
]),
|
||||
]);
|
||||
|
||||
case 3: // PDO::FETCH_NUM - list<scalar|null>|false
|
||||
return new Union([
|
||||
Type::getListAtomic(
|
||||
new Union([
|
||||
new TScalar(),
|
||||
new TNull(),
|
||||
]),
|
||||
),
|
||||
new TFalse(),
|
||||
]);
|
||||
|
||||
case 5: // PDO::FETCH_OBJ - stdClass|false
|
||||
return new Union([
|
||||
new TNamedObject('stdClass'),
|
||||
new TFalse(),
|
||||
]);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static function handleFetchAll(MethodReturnTypeProviderEvent $event): ?Union
|
||||
{
|
||||
$source = $event->getSource();
|
||||
$call_args = $event->getCallArgs();
|
||||
$fetch_mode = 0;
|
||||
|
||||
if (isset($call_args[0])
|
||||
&& ($first_arg_type = $source->getNodeTypeProvider()->getType($call_args[0]->value))
|
||||
&& $first_arg_type->isSingleIntLiteral()
|
||||
) {
|
||||
$fetch_mode = $first_arg_type->getSingleIntLiteral()->value;
|
||||
}
|
||||
|
||||
$fetch_class_name = null;
|
||||
|
||||
if (isset($call_args[1])
|
||||
&& ($second_arg_type = $source->getNodeTypeProvider()->getType($call_args[1]->value))
|
||||
&& $second_arg_type->isSingleStringLiteral()
|
||||
) {
|
||||
$fetch_class_name = $second_arg_type->getSingleStringLiteral()->value;
|
||||
}
|
||||
|
||||
switch ($fetch_mode) {
|
||||
case 2: // PDO::FETCH_ASSOC - list<array<string,scalar|null>>
|
||||
return new Union([
|
||||
Type::getListAtomic(
|
||||
new Union([
|
||||
new TArray([
|
||||
Type::getString(),
|
||||
new Union([
|
||||
new TScalar(),
|
||||
new TNull(),
|
||||
]),
|
||||
]),
|
||||
]),
|
||||
new TFalse(),
|
||||
]);
|
||||
),
|
||||
]);
|
||||
|
||||
case 4: // PDO::FETCH_BOTH - array<array-key,scalar|null>|false
|
||||
return new Union([
|
||||
new TArray([
|
||||
Type::getArrayKey(),
|
||||
new Union([
|
||||
new TScalar(),
|
||||
new TNull(),
|
||||
case 4: // PDO::FETCH_BOTH - list<array<array-key,scalar|null>>
|
||||
return new Union([
|
||||
Type::getListAtomic(
|
||||
new Union([
|
||||
new TArray([
|
||||
Type::getArrayKey(),
|
||||
new Union([
|
||||
new TScalar(),
|
||||
new TNull(),
|
||||
]),
|
||||
]),
|
||||
]),
|
||||
new TFalse(),
|
||||
]);
|
||||
),
|
||||
]);
|
||||
|
||||
case 6: // PDO::FETCH_BOUND - bool
|
||||
return Type::getBool();
|
||||
case 6: // PDO::FETCH_BOUND - list<bool>
|
||||
return new Union([
|
||||
Type::getListAtomic(
|
||||
Type::getBool(),
|
||||
),
|
||||
]);
|
||||
|
||||
case 8: // PDO::FETCH_CLASS - object|false
|
||||
return new Union([
|
||||
new TObject(),
|
||||
new TFalse(),
|
||||
]);
|
||||
case 7: // PDO::FETCH_COLUMN - list<scalar|null>
|
||||
return new Union([
|
||||
Type::getListAtomic(
|
||||
new Union([
|
||||
new TScalar(),
|
||||
new TNull(),
|
||||
]),
|
||||
),
|
||||
]);
|
||||
|
||||
case 1: // PDO::FETCH_LAZY - object|false
|
||||
// This actually returns a PDORow object, but that class is
|
||||
// undocumented, and its attributes are all dynamic anyway
|
||||
return new Union([
|
||||
new TObject(),
|
||||
new TFalse(),
|
||||
]);
|
||||
case 8: // PDO::FETCH_CLASS - list<object>
|
||||
return new Union([
|
||||
Type::getListAtomic(
|
||||
new Union([
|
||||
$fetch_class_name ? new TNamedObject($fetch_class_name) : new TObject(),
|
||||
]),
|
||||
),
|
||||
]);
|
||||
|
||||
case 11: // PDO::FETCH_NAMED - array<string, scalar|list<scalar>>|false
|
||||
return new Union([
|
||||
new TArray([
|
||||
Type::getString(),
|
||||
new Union([
|
||||
new TScalar(),
|
||||
Type::getListAtomic(Type::getScalar()),
|
||||
case 11: // PDO::FETCH_NAMED - list<array<string, scalar|null|list<scalar|null>>>
|
||||
return new Union([
|
||||
Type::getListAtomic(
|
||||
new Union([
|
||||
new TArray([
|
||||
Type::getString(),
|
||||
new Union([
|
||||
new TScalar(),
|
||||
new TNull(),
|
||||
Type::getListAtomic(
|
||||
new Union([
|
||||
new TScalar(),
|
||||
new TNull(),
|
||||
]),
|
||||
),
|
||||
]),
|
||||
]),
|
||||
]),
|
||||
new TFalse(),
|
||||
]);
|
||||
),
|
||||
]);
|
||||
|
||||
case 3: // PDO::FETCH_NUM - list<scalar|null>|false
|
||||
return new Union([
|
||||
Type::getListAtomic(
|
||||
new Union([
|
||||
new TScalar(),
|
||||
new TNull(),
|
||||
]),
|
||||
),
|
||||
new TFalse(),
|
||||
]);
|
||||
case 12: // PDO::FETCH_KEY_PAIR - array<array-key,scalar|null>
|
||||
return new Union([
|
||||
new TArray([
|
||||
Type::getArrayKey(),
|
||||
new Union([
|
||||
new TScalar(),
|
||||
new TNull(),
|
||||
]),
|
||||
]),
|
||||
]);
|
||||
|
||||
case 5: // PDO::FETCH_OBJ - stdClass|false
|
||||
return new Union([
|
||||
new TNamedObject('stdClass'),
|
||||
new TFalse(),
|
||||
]);
|
||||
}
|
||||
case 3: // PDO::FETCH_NUM - list<list<scalar|null>>
|
||||
return new Union([
|
||||
Type::getListAtomic(
|
||||
new Union([
|
||||
Type::getListAtomic(
|
||||
new Union([
|
||||
new TScalar(),
|
||||
new TNull(),
|
||||
]),
|
||||
),
|
||||
]),
|
||||
),
|
||||
]);
|
||||
|
||||
case 5: // PDO::FETCH_OBJ - list<stdClass>
|
||||
return new Union([
|
||||
Type::getListAtomic(
|
||||
new Union([
|
||||
new TNamedObject('stdClass'),
|
||||
]),
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
@@ -63,6 +63,6 @@ class RandReturnTypeProvider implements FunctionReturnTypeProviderInterface
|
||||
}
|
||||
}
|
||||
|
||||
return new Union([new TIntRange($min_value, $max_value)]);
|
||||
return Type::getIntRange($min_value, $max_value);
|
||||
}
|
||||
}
|
||||
|
||||
329
vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/SprintfReturnTypeProvider.php
vendored
Normal file
329
vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/SprintfReturnTypeProvider.php
vendored
Normal file
@@ -0,0 +1,329 @@
|
||||
<?php
|
||||
|
||||
namespace Psalm\Internal\Provider\ReturnTypeProvider;
|
||||
|
||||
use ArgumentCountError;
|
||||
use Psalm\Issue\InvalidArgument;
|
||||
use Psalm\Issue\TooFewArguments;
|
||||
use Psalm\Issue\TooManyArguments;
|
||||
use Psalm\IssueBuffer;
|
||||
use Psalm\Plugin\EventHandler\Event\FunctionReturnTypeProviderEvent;
|
||||
use Psalm\Plugin\EventHandler\FunctionReturnTypeProviderInterface;
|
||||
use Psalm\Type;
|
||||
use Psalm\Type\Atomic\TClassString;
|
||||
use Psalm\Type\Atomic\TFloat;
|
||||
use Psalm\Type\Atomic\TInt;
|
||||
use Psalm\Type\Atomic\TLiteralString;
|
||||
use Psalm\Type\Atomic\TNonEmptyString;
|
||||
use Psalm\Type\Atomic\TNumeric;
|
||||
use Psalm\Type\Union;
|
||||
use ValueError;
|
||||
|
||||
use function array_fill;
|
||||
use function array_pop;
|
||||
use function count;
|
||||
use function is_string;
|
||||
use function preg_match;
|
||||
use function sprintf;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
class SprintfReturnTypeProvider implements FunctionReturnTypeProviderInterface
|
||||
{
|
||||
/**
|
||||
* @return array<lowercase-string>
|
||||
*/
|
||||
public static function getFunctionIds(): array
|
||||
{
|
||||
return [
|
||||
'printf',
|
||||
'sprintf',
|
||||
];
|
||||
}
|
||||
|
||||
public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $event): ?Union
|
||||
{
|
||||
$statements_source = $event->getStatementsSource();
|
||||
$call_args = $event->getCallArgs();
|
||||
|
||||
$has_splat_args = false;
|
||||
$node_type_provider = $statements_source->getNodeTypeProvider();
|
||||
foreach ($call_args as $call_arg) {
|
||||
$type = $node_type_provider->getType($call_arg->value);
|
||||
if ($type === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// if it's an array, used with splat operator
|
||||
// we cannot validate it reliably below and report false positive errors
|
||||
if ($type->isArray()) {
|
||||
$has_splat_args = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// there is only 1 array argument, fall back to the default handling
|
||||
// eventually this could be refined
|
||||
// to check if it's an array with literal string as first element for further checking
|
||||
if (count($call_args) === 1 && $has_splat_args === true) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// it makes no sense to use sprintf when there is only 1 arg (the format)
|
||||
// as it wouldn't have any placeholders
|
||||
if (count($call_args) === 1 && $event->getFunctionId() === 'sprintf') {
|
||||
IssueBuffer::maybeAdd(
|
||||
new TooFewArguments(
|
||||
'Too few arguments for ' . $event->getFunctionId() . ', expecting at least 2 arguments',
|
||||
$event->getCodeLocation(),
|
||||
$event->getFunctionId(),
|
||||
),
|
||||
$statements_source->getSuppressedIssues(),
|
||||
);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// PHP 7 handling for formats that do not contain anything but placeholders
|
||||
$is_falsable = true;
|
||||
foreach ($call_args as $index => $call_arg) {
|
||||
$type = $node_type_provider->getType($call_arg->value);
|
||||
if ($type === null && $index === 0 && $event->getFunctionId() === 'printf') {
|
||||
break;
|
||||
}
|
||||
|
||||
if ($type === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($index === 0 && $type->isSingleStringLiteral()) {
|
||||
if ($type->getSingleStringLiteral()->value === '') {
|
||||
IssueBuffer::maybeAdd(
|
||||
new InvalidArgument(
|
||||
'Argument 1 of ' . $event->getFunctionId() . ' must not be an empty string',
|
||||
$event->getCodeLocation(),
|
||||
$event->getFunctionId(),
|
||||
),
|
||||
$statements_source->getSuppressedIssues(),
|
||||
);
|
||||
|
||||
if ($event->getFunctionId() === 'printf') {
|
||||
return Type::getInt(false, 0);
|
||||
}
|
||||
|
||||
return Type::getString('');
|
||||
}
|
||||
|
||||
// there are probably additional formats that return an empty string, this is just a starting point
|
||||
if (preg_match('/^%(?:\d+\$)?[-+]?0(?:\.0)?s$/', $type->getSingleStringLiteral()->value) === 1) {
|
||||
IssueBuffer::maybeAdd(
|
||||
new InvalidArgument(
|
||||
'The pattern of argument 1 of ' . $event->getFunctionId()
|
||||
. ' will always return an empty string',
|
||||
$event->getCodeLocation(),
|
||||
$event->getFunctionId(),
|
||||
),
|
||||
$statements_source->getSuppressedIssues(),
|
||||
);
|
||||
|
||||
if ($event->getFunctionId() === 'printf') {
|
||||
return Type::getInt(false, 0);
|
||||
}
|
||||
|
||||
return Type::getString('');
|
||||
}
|
||||
|
||||
// these placeholders are too complex to handle for now
|
||||
if (preg_match(
|
||||
'/%(?:\d+\$)?[-+]?(?:\d+|\*)(?:\.(?:\d+|\*))?[bcdouxXeEfFgGhHs]/',
|
||||
$type->getSingleStringLiteral()->value,
|
||||
) === 1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// assume a random, high number for tests
|
||||
$provided_placeholders_count = $has_splat_args === true ? 100 : count($call_args) - 1;
|
||||
$dummy = array_fill(0, $provided_placeholders_count, '');
|
||||
|
||||
// check if we have enough/too many arguments and a valid format
|
||||
$initial_result = null;
|
||||
while (count($dummy) > -1) {
|
||||
$result = null;
|
||||
try {
|
||||
// before PHP 8, an uncatchable Warning is thrown if too few arguments are passed
|
||||
// which is ignored and handled below instead
|
||||
$result = @sprintf($type->getSingleStringLiteral()->value, ...$dummy);
|
||||
if ($initial_result === null) {
|
||||
$initial_result = $result;
|
||||
|
||||
if ($result === $type->getSingleStringLiteral()->value) {
|
||||
IssueBuffer::maybeAdd(
|
||||
new InvalidArgument(
|
||||
'Argument 1 of ' . $event->getFunctionId()
|
||||
. ' does not contain any placeholders',
|
||||
$event->getCodeLocation(),
|
||||
$event->getFunctionId(),
|
||||
),
|
||||
$statements_source->getSuppressedIssues(),
|
||||
);
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
} catch (ValueError $value_error) {
|
||||
// PHP 8
|
||||
// the format is invalid
|
||||
IssueBuffer::maybeAdd(
|
||||
new InvalidArgument(
|
||||
'Argument 1 of ' . $event->getFunctionId() . ' is invalid - '
|
||||
. $value_error->getMessage(),
|
||||
$event->getCodeLocation(),
|
||||
$event->getFunctionId(),
|
||||
),
|
||||
$statements_source->getSuppressedIssues(),
|
||||
);
|
||||
|
||||
break 2;
|
||||
} catch (ArgumentCountError $error) {
|
||||
// PHP 8
|
||||
if (count($dummy) === $provided_placeholders_count) {
|
||||
IssueBuffer::maybeAdd(
|
||||
new TooFewArguments(
|
||||
'Too few arguments for ' . $event->getFunctionId(),
|
||||
$event->getCodeLocation(),
|
||||
$event->getFunctionId(),
|
||||
),
|
||||
$statements_source->getSuppressedIssues(),
|
||||
);
|
||||
|
||||
break 2;
|
||||
}
|
||||
}
|
||||
|
||||
if ($result === false && count($dummy) === $provided_placeholders_count) {
|
||||
// could be invalid format or too few arguments
|
||||
// we cannot distinguish this in PHP 7 without additional checks
|
||||
$max_dummy = array_fill(0, 100, '');
|
||||
$result = @sprintf($type->getSingleStringLiteral()->value, ...$max_dummy);
|
||||
if ($result === false) {
|
||||
// the format is invalid
|
||||
IssueBuffer::maybeAdd(
|
||||
new InvalidArgument(
|
||||
'Argument 1 of ' . $event->getFunctionId() . ' is invalid',
|
||||
$event->getCodeLocation(),
|
||||
$event->getFunctionId(),
|
||||
),
|
||||
$statements_source->getSuppressedIssues(),
|
||||
);
|
||||
} else {
|
||||
IssueBuffer::maybeAdd(
|
||||
new TooFewArguments(
|
||||
'Too few arguments for ' . $event->getFunctionId(),
|
||||
$event->getCodeLocation(),
|
||||
$event->getFunctionId(),
|
||||
),
|
||||
$statements_source->getSuppressedIssues(),
|
||||
);
|
||||
}
|
||||
|
||||
return Type::getFalse();
|
||||
}
|
||||
|
||||
// we can only validate the format and arg 1 when using splat
|
||||
if ($has_splat_args === true) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (is_string($result) && count($dummy) + 1 <= $provided_placeholders_count) {
|
||||
IssueBuffer::maybeAdd(
|
||||
new TooManyArguments(
|
||||
'Too many arguments for the number of placeholders in ' . $event->getFunctionId(),
|
||||
$event->getCodeLocation(),
|
||||
$event->getFunctionId(),
|
||||
),
|
||||
$statements_source->getSuppressedIssues(),
|
||||
);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (!is_string($result)) {
|
||||
break;
|
||||
}
|
||||
|
||||
// abort if it's empty, since we checked everything
|
||||
if (array_pop($dummy) === null) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($event->getFunctionId() === 'printf') {
|
||||
// printf only has the format validated above
|
||||
// don't change the return type
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($initial_result !== null && $initial_result !== false && $initial_result !== '') {
|
||||
return Type::getNonEmptyString();
|
||||
}
|
||||
|
||||
// if we didn't have any valid result
|
||||
// the pattern is invalid or not yet supported by the return type provider
|
||||
if ($initial_result === null || $initial_result === false) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// the initial result is an empty string
|
||||
// which means the format is valid and it depends on the args, whether it is non-empty-string or not
|
||||
$is_falsable = false;
|
||||
}
|
||||
|
||||
if ($index === 0 && $event->getFunctionId() === 'printf') {
|
||||
// printf only has the format validated above
|
||||
// don't change the return type
|
||||
break;
|
||||
}
|
||||
|
||||
if ($index === 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// if the function has more arguments than the pattern has placeholders, this could be a false positive
|
||||
// if the param is not used in the pattern
|
||||
if ($type->isNonEmptyString() || $type->isInt() || $type->isFloat()) {
|
||||
return Type::getNonEmptyString();
|
||||
}
|
||||
|
||||
// check for unions of either
|
||||
$atomic_types = $type->getAtomicTypes();
|
||||
if ($atomic_types === []) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($atomic_types as $atomic_type) {
|
||||
if ($atomic_type instanceof TNonEmptyString
|
||||
|| $atomic_type instanceof TClassString
|
||||
|| ($atomic_type instanceof TLiteralString && $atomic_type->value !== '')
|
||||
|| $atomic_type instanceof TInt
|
||||
|| $atomic_type instanceof TFloat
|
||||
|| $atomic_type instanceof TNumeric) {
|
||||
// valid non-empty types, potentially there are more though
|
||||
continue;
|
||||
}
|
||||
|
||||
// empty or generic string
|
||||
// or other unhandled type
|
||||
continue 2;
|
||||
}
|
||||
|
||||
return Type::getNonEmptyString();
|
||||
}
|
||||
|
||||
if ($is_falsable === false) {
|
||||
return Type::getString();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -6,13 +6,10 @@ use Psalm\Internal\Analyzer\StatementsAnalyzer;
|
||||
use Psalm\Plugin\EventHandler\Event\FunctionReturnTypeProviderEvent;
|
||||
use Psalm\Plugin\EventHandler\FunctionReturnTypeProviderInterface;
|
||||
use Psalm\Type;
|
||||
use Psalm\Type\Atomic\TNull;
|
||||
use Psalm\Type\Atomic\TString;
|
||||
use Psalm\Type\Union;
|
||||
|
||||
use function call_user_func;
|
||||
use function count;
|
||||
use function in_array;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
@@ -27,13 +24,10 @@ class StrReplaceReturnTypeProvider implements FunctionReturnTypeProviderInterfac
|
||||
return [
|
||||
'str_replace',
|
||||
'str_ireplace',
|
||||
'substr_replace',
|
||||
'preg_replace',
|
||||
'preg_replace_callback',
|
||||
];
|
||||
}
|
||||
|
||||
public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $event): Union
|
||||
public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $event): ?Union
|
||||
{
|
||||
$statements_source = $event->getStatementsSource();
|
||||
$call_args = $event->getCallArgs();
|
||||
@@ -41,47 +35,34 @@ class StrReplaceReturnTypeProvider implements FunctionReturnTypeProviderInterfac
|
||||
if (!$statements_source instanceof StatementsAnalyzer
|
||||
|| count($call_args) < 3
|
||||
) {
|
||||
return Type::getMixed();
|
||||
// use the defaults, it will already report an error for the invalid params
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($subject_type = $statements_source->node_data->getType($call_args[2]->value)) {
|
||||
if (!$subject_type->hasString() && $subject_type->hasArray()) {
|
||||
return Type::getArray();
|
||||
if (!$subject_type->isSingleStringLiteral()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$return_type = Type::getString();
|
||||
|
||||
if (in_array($function_id, ['str_replace', 'str_ireplace'], true)
|
||||
&& $subject_type->isSingleStringLiteral()
|
||||
$first_arg = $statements_source->node_data->getType($call_args[0]->value);
|
||||
$second_arg = $statements_source->node_data->getType($call_args[1]->value);
|
||||
if ($first_arg
|
||||
&& $second_arg && $first_arg->isSingleStringLiteral()
|
||||
&& $second_arg->isSingleStringLiteral()
|
||||
) {
|
||||
$first_arg = $statements_source->node_data->getType($call_args[0]->value);
|
||||
$second_arg = $statements_source->node_data->getType($call_args[1]->value);
|
||||
if ($first_arg
|
||||
&& $second_arg && $first_arg->isSingleStringLiteral()
|
||||
&& $second_arg->isSingleStringLiteral()
|
||||
) {
|
||||
/**
|
||||
* @var string $replaced_string
|
||||
*/
|
||||
$replaced_string = call_user_func(
|
||||
$function_id,
|
||||
$first_arg->getSingleStringLiteral()->value,
|
||||
$second_arg->getSingleStringLiteral()->value,
|
||||
$subject_type->getSingleStringLiteral()->value,
|
||||
);
|
||||
$return_type = Type::getString($replaced_string);
|
||||
}
|
||||
} elseif (in_array($function_id, ['preg_replace', 'preg_replace_callback'], true)) {
|
||||
$codebase = $statements_source->getCodebase();
|
||||
|
||||
$return_type = new Union([new TString, new TNull()], [
|
||||
'ignore_nullable_issues' => $codebase->config->ignore_internal_nullable_issues,
|
||||
]);
|
||||
/**
|
||||
* @var string $replaced_string
|
||||
*/
|
||||
$replaced_string = call_user_func(
|
||||
$function_id,
|
||||
$first_arg->getSingleStringLiteral()->value,
|
||||
$second_arg->getSingleStringLiteral()->value,
|
||||
$subject_type->getSingleStringLiteral()->value,
|
||||
);
|
||||
return Type::getString($replaced_string);
|
||||
}
|
||||
|
||||
return $return_type;
|
||||
}
|
||||
|
||||
return Type::getMixed();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,8 +52,6 @@ class StatementsProvider
|
||||
|
||||
private ?FileStorageCacheProvider $file_storage_cache_provider = null;
|
||||
|
||||
private StatementsVolatileCache $statements_volatile_cache;
|
||||
|
||||
/**
|
||||
* @var array<string, array<string, bool>>
|
||||
*/
|
||||
@@ -97,7 +95,6 @@ class StatementsProvider
|
||||
$this->parser_cache_provider = $parser_cache_provider;
|
||||
$this->this_modified_time = filemtime(__FILE__);
|
||||
$this->file_storage_cache_provider = $file_storage_cache_provider;
|
||||
$this->statements_volatile_cache = StatementsVolatileCache::getInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -132,20 +129,11 @@ class StatementsProvider
|
||||
if (!$this->parser_cache_provider
|
||||
|| (!$config->isInProjectDirs($file_path) && strpos($file_path, 'vendor'))
|
||||
) {
|
||||
$cache_key = "{$file_content_hash}:{$analysis_php_version_id}";
|
||||
if ($this->statements_volatile_cache->has($cache_key)) {
|
||||
return $this->statements_volatile_cache->get($cache_key);
|
||||
}
|
||||
|
||||
$progress->debug('Parsing ' . $file_path . "\n");
|
||||
|
||||
$has_errors = false;
|
||||
|
||||
$stmts = self::parseStatements($file_contents, $analysis_php_version_id, $has_errors, $file_path);
|
||||
|
||||
$this->statements_volatile_cache->set($cache_key, $stmts);
|
||||
|
||||
return $stmts;
|
||||
return self::parseStatements($file_contents, $analysis_php_version_id, $has_errors, $file_path);
|
||||
}
|
||||
|
||||
$stmts = $this->parser_cache_provider->loadStatementsFromCache(
|
||||
|
||||
@@ -1,100 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Psalm\Internal\Provider;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use PhpParser\Node\Stmt;
|
||||
|
||||
use function array_key_exists;
|
||||
use function array_search;
|
||||
use function array_splice;
|
||||
use function count;
|
||||
use function is_null;
|
||||
use function key;
|
||||
use function reset;
|
||||
|
||||
/**
|
||||
* @internal Owned by StatementsProvider
|
||||
* @todo: track variables size instead
|
||||
*/
|
||||
final class StatementsVolatileCache
|
||||
{
|
||||
/**
|
||||
* @var array<string, list<Stmt>>
|
||||
*/
|
||||
protected array $cache = [];
|
||||
|
||||
/**
|
||||
* @var array<int, string>
|
||||
*/
|
||||
protected array $access = [];
|
||||
|
||||
protected int $max_size;
|
||||
|
||||
protected static ?StatementsVolatileCache $instance = null;
|
||||
|
||||
public function __construct(int $max_size = 4096)
|
||||
{
|
||||
$this->max_size = $max_size;
|
||||
}
|
||||
|
||||
public static function getInstance(): StatementsVolatileCache
|
||||
{
|
||||
if (is_null(self::$instance)) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
public function has(string $key): bool
|
||||
{
|
||||
return array_key_exists($key, $this->cache);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<Stmt>
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function get(string $key): array
|
||||
{
|
||||
if (! $this->has($key)) {
|
||||
throw new InvalidArgumentException('Given $key does not exists');
|
||||
}
|
||||
|
||||
$access_index = array_search($key, $this->access);
|
||||
if (false !== $access_index) {
|
||||
array_splice($this->access, $access_index, 1);
|
||||
}
|
||||
$this->access[] = $key;
|
||||
|
||||
return $this->cache[$key];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param list<Stmt> $content
|
||||
*/
|
||||
public function set(string $key, array $content): void
|
||||
{
|
||||
if (count($this->cache) > $this->max_size) {
|
||||
reset($this->access);
|
||||
|
||||
$oldest_key_index = key($this->access);
|
||||
|
||||
if (! is_null($oldest_key_index)) {
|
||||
$oldest_key = $this->access[$oldest_key_index];
|
||||
unset($this->cache[$oldest_key]);
|
||||
unset($this->access[$oldest_key_index]);
|
||||
}
|
||||
}
|
||||
|
||||
$this->cache[$key] = $content;
|
||||
$this->access[] = $key;
|
||||
}
|
||||
|
||||
public function clearCache(): void
|
||||
{
|
||||
$this->cache = [];
|
||||
$this->access = [];
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,6 @@ use Psalm\Internal\Provider\ClassLikeStorageProvider;
|
||||
use Psalm\Internal\Provider\FileReferenceProvider;
|
||||
use Psalm\Internal\Provider\FileStorageProvider;
|
||||
use Psalm\Internal\Provider\StatementsProvider;
|
||||
use Psalm\Internal\Provider\StatementsVolatileCache;
|
||||
use Psalm\Internal\Scanner\ParsedDocblock;
|
||||
use Psalm\Internal\Type\TypeTokenizer;
|
||||
use Psalm\IssueBuffer;
|
||||
@@ -42,6 +41,5 @@ abstract class RuntimeCaches
|
||||
StatementsProvider::clearLexer();
|
||||
StatementsProvider::clearParser();
|
||||
ParsedDocblock::resetNewlineBetweenAnnotations();
|
||||
StatementsVolatileCache::getInstance()->clearCache();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ use function count;
|
||||
use function explode;
|
||||
use function implode;
|
||||
use function is_string;
|
||||
use function ltrim;
|
||||
use function min;
|
||||
use function preg_match;
|
||||
use function preg_replace;
|
||||
@@ -54,7 +55,8 @@ class DocblockParser
|
||||
}
|
||||
|
||||
// Normalize multi-line @specials.
|
||||
$lines = explode("\n", $docblock);
|
||||
$lines = explode("\n", str_replace("\t", ' ', $docblock));
|
||||
$has_r = strpos($docblock, "\r") === false ? false : true;
|
||||
|
||||
$special = [];
|
||||
|
||||
@@ -62,9 +64,9 @@ class DocblockParser
|
||||
|
||||
$last = false;
|
||||
foreach ($lines as $k => $line) {
|
||||
if (preg_match('/^[ \t]*\*?\s*@\w/i', $line)) {
|
||||
if (strpos($line, '@') !== false && preg_match('/^ *\*?\s*@\w/', $line)) {
|
||||
$last = $k;
|
||||
} elseif (preg_match('/^\s*\r?$/', $line)) {
|
||||
} elseif (trim($line) === '') {
|
||||
$last = false;
|
||||
} elseif ($last !== false) {
|
||||
$old_last_line = $lines[$last];
|
||||
@@ -78,26 +80,29 @@ class DocblockParser
|
||||
|
||||
foreach ($lines as $k => $line) {
|
||||
$original_line_length = strlen($line);
|
||||
|
||||
$line = str_replace("\r", '', $line);
|
||||
if ($has_r === true) {
|
||||
$line = str_replace("\r", '', $line);
|
||||
}
|
||||
|
||||
if ($first_line_padding === null) {
|
||||
$asterisk_pos = strpos($line, '*');
|
||||
|
||||
if ($asterisk_pos) {
|
||||
if ($asterisk_pos === 0 || $asterisk_pos === 1) {
|
||||
$first_line_padding = '';
|
||||
} elseif ($asterisk_pos > 1) {
|
||||
$first_line_padding = substr($line, 0, $asterisk_pos - 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (preg_match('/^[ \t]*\*?\s*@([\w\-\\\:]+)[\t ]*(.*)$/sm', $line, $matches, PREG_OFFSET_CAPTURE)) {
|
||||
if (preg_match('/^ *\*?\s*@([\w\-\\\:]+) *(.*)$/sm', $line, $matches, PREG_OFFSET_CAPTURE)) {
|
||||
/** @var array<int, array{string, int}> $matches */
|
||||
[, $type_info, $data_info] = $matches;
|
||||
|
||||
[$type] = $type_info;
|
||||
[$data, $data_offset] = $data_info;
|
||||
|
||||
if (strpos($data, '*')) {
|
||||
$data = rtrim(preg_replace('/^[ \t]*\*\s*$/m', '', $data));
|
||||
if (strpos($data, '*') !== false) {
|
||||
$data = rtrim(preg_replace('/^ *\*\s*$/m', '', $data));
|
||||
}
|
||||
|
||||
if (empty($special[$type])) {
|
||||
@@ -111,10 +116,9 @@ class DocblockParser
|
||||
unset($lines[$k]);
|
||||
} else {
|
||||
// Strip the leading *, if present.
|
||||
$text = $lines[$k];
|
||||
$text = str_replace("\t", ' ', $text);
|
||||
$text = preg_replace('/^ *\*/', '', $text, 1);
|
||||
$lines[$k] = $text;
|
||||
// technically only need to preg_replace('/^ *\*/', '', $lines[$k], 1)
|
||||
// however it's slower and removing all spaces and * is fine
|
||||
$lines[$k] = ltrim($lines[$k], ' *');
|
||||
}
|
||||
|
||||
$line_offset += $original_line_length + 1;
|
||||
@@ -122,13 +126,20 @@ class DocblockParser
|
||||
|
||||
// Smush the whole docblock to the left edge.
|
||||
$min_indent = 80;
|
||||
$reached_first_non_empty_line = false;
|
||||
foreach ($lines as $k => $line) {
|
||||
$indent = strspn($line, ' ');
|
||||
if ($indent === strlen($line)) {
|
||||
// This line consists of only spaces. Trim it completely.
|
||||
if ($reached_first_non_empty_line === false) {
|
||||
// remove any leading empty lines here, to avoid a preg_replace later
|
||||
unset($lines[$k]);
|
||||
continue;
|
||||
}
|
||||
$lines[$k] = '';
|
||||
continue;
|
||||
}
|
||||
$reached_first_non_empty_line = true;
|
||||
$min_indent = min($indent, $min_indent);
|
||||
}
|
||||
if ($min_indent > 0) {
|
||||
@@ -142,10 +153,6 @@ class DocblockParser
|
||||
$docblock = implode("\n", $lines);
|
||||
$docblock = rtrim($docblock);
|
||||
|
||||
// Trim any empty lines off the front, but leave the indent level if there
|
||||
// is one.
|
||||
$docblock = preg_replace('/^\s*\n/', '', $docblock, 1);
|
||||
|
||||
$parsed = new ParsedDocblock($docblock, $special, $first_line_padding ?: '');
|
||||
|
||||
self::resolveTags($parsed);
|
||||
|
||||
@@ -15,7 +15,6 @@ use Psalm\Type\Union;
|
||||
use ReflectionProperty;
|
||||
|
||||
use function count;
|
||||
use function implode;
|
||||
use function is_string;
|
||||
use function str_replace;
|
||||
use function strpos;
|
||||
@@ -45,7 +44,7 @@ class PhpStormMetaScanner
|
||||
|
||||
$map = [];
|
||||
|
||||
if ($args[1]->value->name->parts === ['map']
|
||||
if ($args[1]->value->name->getParts() === ['map']
|
||||
&& $args[1]->value->getArgs()
|
||||
&& $args[1]->value->getArgs()[0]->value instanceof PhpParser\Node\Expr\Array_
|
||||
) {
|
||||
@@ -59,7 +58,7 @@ class PhpStormMetaScanner
|
||||
&& strtolower($array_item->value->name->name)
|
||||
) {
|
||||
$map[$array_item->key->value] = new Union([
|
||||
new TNamedObject(implode('\\', $array_item->value->class->parts)),
|
||||
new TNamedObject($array_item->value->class->toString()),
|
||||
]);
|
||||
} elseif ($array_item->value instanceof PhpParser\Node\Scalar\String_) {
|
||||
$map[$array_item->key->value] = $array_item->value->value;
|
||||
@@ -93,7 +92,7 @@ class PhpStormMetaScanner
|
||||
&& strtolower($array_item->value->name->name)
|
||||
) {
|
||||
$map[$meta_key] = new Union([
|
||||
new TNamedObject(implode('\\', $array_item->value->class->parts)),
|
||||
new TNamedObject($array_item->value->class->toString()),
|
||||
]);
|
||||
} elseif ($array_item->value instanceof PhpParser\Node\Scalar\String_) {
|
||||
$map[$meta_key] = $array_item->value->value;
|
||||
@@ -104,7 +103,7 @@ class PhpStormMetaScanner
|
||||
|
||||
$type_offset = null;
|
||||
|
||||
if ($args[1]->value->name->parts === ['type']
|
||||
if ($args[1]->value->name->getParts() === ['type']
|
||||
&& $args[1]->value->getArgs()
|
||||
&& $args[1]->value->getArgs()[0]->value instanceof PhpParser\Node\Scalar\LNumber
|
||||
) {
|
||||
@@ -113,7 +112,7 @@ class PhpStormMetaScanner
|
||||
|
||||
$element_type_offset = null;
|
||||
|
||||
if ($args[1]->value->name->parts === ['elementType']
|
||||
if ($args[1]->value->name->getParts() === ['elementType']
|
||||
&& $args[1]->value->getArgs()
|
||||
&& $args[1]->value->getArgs()[0]->value instanceof PhpParser\Node\Scalar\LNumber
|
||||
) {
|
||||
@@ -128,7 +127,7 @@ class PhpStormMetaScanner
|
||||
|| $identifier->getArgs()[0]->value instanceof PhpParser\Node\Scalar\LNumber
|
||||
)
|
||||
) {
|
||||
$meta_fq_classlike_name = implode('\\', $identifier->class->parts);
|
||||
$meta_fq_classlike_name = $identifier->class->toString();
|
||||
|
||||
$meta_method_name = strtolower($identifier->name->name);
|
||||
|
||||
@@ -280,7 +279,7 @@ class PhpStormMetaScanner
|
||||
|| $identifier->getArgs()[0]->value instanceof PhpParser\Node\Scalar\LNumber
|
||||
)
|
||||
) {
|
||||
$function_id = strtolower(implode('\\', $identifier->name->parts));
|
||||
$function_id = strtolower($identifier->name->toString());
|
||||
|
||||
if ($map) {
|
||||
$offset = 0;
|
||||
|
||||
@@ -771,12 +771,25 @@ class AssertionReconciler extends Reconciler
|
||||
}
|
||||
|
||||
if ($type_1_param->getId() !== $type_2_param->getId()) {
|
||||
$type_1_param = $type_2_param;
|
||||
$type_1_param = $type_2_param->setPossiblyUndefined($type_1_param->possibly_undefined);
|
||||
}
|
||||
}
|
||||
unset($type_1_param);
|
||||
|
||||
$matching_atomic_type = $type_1_atomic->setProperties($type_1_properties);
|
||||
if ($type_1_atomic->fallback_params === null) {
|
||||
$fallback_types = null;
|
||||
} else {
|
||||
//any fallback type is now the value of iterable
|
||||
$fallback_types = [$type_1_atomic->fallback_params[0], $type_2_param];
|
||||
}
|
||||
|
||||
$matching_atomic_type = new TKeyedArray(
|
||||
$type_1_properties,
|
||||
$type_1_atomic->class_strings,
|
||||
$fallback_types,
|
||||
$type_1_atomic->is_list,
|
||||
$type_1_atomic->from_docblock,
|
||||
);
|
||||
$atomic_comparison_results->type_coerced = true;
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user