+
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion build/composer-require-checker.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"array", "string", "int", "float", "bool", "iterable", "callable", "void", "object", "mixed",
"PHPUnit\\Framework\\TestCase", "PHPUnit\\Framework\\AssertionFailedError", "Composer\\Autoload\\ClassLoader", "PHPUnit\\Framework\\ExpectationFailedException",
"JSON_THROW_ON_ERROR", "JSON_INVALID_UTF8_IGNORE", "JsonSerializable", "SimpleXMLElement", "PHPStan\\ExtensionInstaller\\GeneratedConfig", "Nette\\DI\\InvalidConfigurationException",
"FILTER_SANITIZE_EMAIL", "FILTER_SANITIZE_EMAIL", "FILTER_SANITIZE_ENCODED", "FILTER_SANITIZE_MAGIC_QUOTES", "FILTER_SANITIZE_NUMBER_FLOAT",
"CURLOPT_SSL_VERIFYHOST", "FILTER_SANITIZE_EMAIL", "FILTER_SANITIZE_EMAIL", "FILTER_SANITIZE_ENCODED", "FILTER_SANITIZE_MAGIC_QUOTES", "FILTER_SANITIZE_NUMBER_FLOAT",
"FILTER_SANITIZE_NUMBER_INT", "FILTER_SANITIZE_SPECIAL_CHARS", "FILTER_SANITIZE_STRING", "FILTER_SANITIZE_URL", "FILTER_VALIDATE_BOOLEAN",
"FILTER_VALIDATE_EMAIL", "FILTER_VALIDATE_FLOAT", "FILTER_VALIDATE_INT", "FILTER_VALIDATE_IP", "FILTER_VALIDATE_MAC", "FILTER_VALIDATE_REGEXP",
"FILTER_VALIDATE_URL", "FILTER_NULL_ON_FAILURE", "FILTER_FORCE_ARRAY", "FILTER_SANITIZE_ADD_SLASHES", "FILTER_DEFAULT", "FILTER_UNSAFE_RAW", "opcache_invalidate", "ValueError", "ReflectionUnionType", "ReflectionIntersectionType", "Attribute", "ReflectionEnum", "ReflectionEnumBackedCase", "enum_exists",
Expand Down
1 change: 1 addition & 0 deletions conf/bleedingEdge.neon
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,4 @@ parameters:
phpDocParserRequireWhitespaceBeforeDescription: true
runtimeReflectionRules: true
notAnalysedTrait: true
curlSetOptTypes: true
7 changes: 7 additions & 0 deletions conf/config.neon
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ parameters:
phpDocParserRequireWhitespaceBeforeDescription: false
runtimeReflectionRules: false
notAnalysedTrait: false
curlSetOptTypes: false
fileExtensions:
- php
checkAdvancedIsset: false
Expand Down Expand Up @@ -254,6 +255,7 @@ parametersSchema:
phpDocParserRequireWhitespaceBeforeDescription: bool()
runtimeReflectionRules: bool()
notAnalysedTrait: bool()
curlSetOptTypes: bool()
])
fileExtensions: listOf(string())
checkAdvancedIsset: bool()
Expand Down Expand Up @@ -403,6 +405,8 @@ conditionalTags:
phpstan.rules.rule: %exceptions.check.tooWideThrowType%
PhpParser\NodeVisitor\NodeConnectingVisitor:
phpstan.parser.richParserNodeVisitor: %featureToggles.nodeConnectingVisitorCompatibility%
PHPStan\Parser\CurlSetOptArgVisitor:
phpstan.parser.richParserNodeVisitor: %featureToggles.curlSetOptTypes%

services:
-
Expand Down Expand Up @@ -437,6 +441,9 @@ services:
tags:
- phpstan.parser.richParserNodeVisitor

-
class: PHPStan\Parser\CurlSetOptArgVisitor

-
class: PHPStan\Parser\ArrowFunctionArgVisitor
tags:
Expand Down
25 changes: 25 additions & 0 deletions src/Parser/CurlSetOptArgVisitor.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php declare(strict_types = 1);

namespace PHPStan\Parser;

use PhpParser\Node;
use PhpParser\NodeVisitorAbstract;

class CurlSetOptArgVisitor extends NodeVisitorAbstract
{

public function enterNode(Node $node): ?Node
{
if ($node instanceof Node\Expr\FuncCall && $node->name instanceof Node\Name) {
$functionName = $node->name->toLowerString();
if ($functionName === 'curl_setopt') {
$args = $node->getArgs();
if (isset($args[0])) {
$args[0]->setAttribute('isCurlSetOptArg', true);
}
}
}
return null;
}

}
271 changes: 271 additions & 0 deletions src/Reflection/ParametersAcceptorSelector.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,33 @@
use PHPStan\Reflection\Php\DummyParameter;
use PHPStan\ShouldNotHappenException;
use PHPStan\TrinaryLogic;
use PHPStan\Type\Accessory\AccessoryNonEmptyStringType;
use PHPStan\Type\ArrayType;
use PHPStan\Type\BooleanType;
use PHPStan\Type\CallableType;
use PHPStan\Type\Constant\ConstantIntegerType;
use PHPStan\Type\ErrorType;
use PHPStan\Type\Generic\TemplateType;
use PHPStan\Type\Generic\TemplateTypeMap;
use PHPStan\Type\IntegerType;
use PHPStan\Type\LateResolvableType;
use PHPStan\Type\MixedType;
use PHPStan\Type\NullType;
use PHPStan\Type\ResourceType;
use PHPStan\Type\StringType;
use PHPStan\Type\Type;
use PHPStan\Type\TypeCombinator;
use PHPStan\Type\TypeTraverser;
use PHPStan\Type\UnionType;
use function array_key_last;
use function array_slice;
use function constant;
use function count;
use function defined;
use function sprintf;
use const ARRAY_FILTER_USE_BOTH;
use const ARRAY_FILTER_USE_KEY;
use const CURLOPT_SSL_VERIFYHOST;

/** @api */
class ParametersAcceptorSelector
Expand Down Expand Up @@ -99,6 +108,37 @@ public static function selectFromArgs(
];
}

if (count($args) >= 3 && (bool) $args[0]->getAttribute('isCurlSetOptArg')) {
$optType = $scope->getType($args[1]->value);
if ($optType instanceof ConstantIntegerType) {
$optValueType = self::getCurlOptValueType($optType->getValue());

if ($optValueType !== null) {
$acceptor = $parametersAcceptors[0];
$parameters = $acceptor->getParameters();

$parameters[2] = new NativeParameterReflection(
$parameters[2]->getName(),
$parameters[2]->isOptional(),
$optValueType,
$parameters[2]->passedByReference(),
$parameters[2]->isVariadic(),
$parameters[2]->getDefaultValue(),
);

$parametersAcceptors = [
new FunctionVariant(
$acceptor->getTemplateTypeMap(),
$acceptor->getResolvedTemplateTypeMap(),
$parameters,
$acceptor->isVariadic(),
$acceptor->getReturnType(),
),
];
}
}
}

if (isset($args[0]) && (bool) $args[0]->getAttribute('isArrayFilterArg')) {
if (isset($args[2])) {
$mode = $scope->getType($args[2]->value);
Expand Down Expand Up @@ -465,4 +505,235 @@ private static function getIterableKeyType(Type $type): Type
return $type->getIterableKeyType();
}

private static function getCurlOptValueType(int $curlOpt): ?Type
{
if (defined('CURLOPT_SSL_VERIFYHOST') && $curlOpt === CURLOPT_SSL_VERIFYHOST) {
return new UnionType([new ConstantIntegerType(0), new ConstantIntegerType(2)]);
}

$boolConstants = [
'CURLOPT_AUTOREFERER',
'CURLOPT_COOKIESESSION',
'CURLOPT_CERTINFO',
'CURLOPT_CONNECT_ONLY',
'CURLOPT_CRLF',
'CURLOPT_DISALLOW_USERNAME_IN_URL',
'CURLOPT_DNS_SHUFFLE_ADDRESSES',
'CURLOPT_HAPROXYPROTOCOL',
'CURLOPT_SSH_COMPRESSION',
'CURLOPT_DNS_USE_GLOBAL_CACHE',
'CURLOPT_FAILONERROR',
'CURLOPT_SSL_FALSESTART',
'CURLOPT_FILETIME',
'CURLOPT_FOLLOWLOCATION',
'CURLOPT_FORBID_REUSE',
'CURLOPT_FRESH_CONNECT',
'CURLOPT_FTP_USE_EPRT',
'CURLOPT_FTP_USE_EPSV',
'CURLOPT_FTP_CREATE_MISSING_DIRS',
'CURLOPT_FTPAPPEND',
'CURLOPT_TCP_NODELAY',
'CURLOPT_FTPASCII',
'CURLOPT_FTPLISTONLY',
'CURLOPT_HEADER',
'CURLOPT_HTTP09_ALLOWED',
'CURLOPT_HTTPGET',
'CURLOPT_HTTPPROXYTUNNEL',
'CURLOPT_HTTP_CONTENT_DECODING',
'CURLOPT_KEEP_SENDING_ON_ERROR',
'CURLOPT_MUTE',
'CURLOPT_NETRC',
'CURLOPT_NOBODY',
'CURLOPT_NOPROGRESS',
'CURLOPT_NOSIGNAL',
'CURLOPT_PATH_AS_IS',
'CURLOPT_PIPEWAIT',
'CURLOPT_POST',
'CURLOPT_PUT',
'CURLOPT_RETURNTRANSFER',
'CURLOPT_SASL_IR',
'CURLOPT_SSL_ENABLE_ALPN',
'CURLOPT_SSL_ENABLE_NPN',
'CURLOPT_SSL_VERIFYPEER',
'CURLOPT_SSL_VERIFYSTATUS',
'CURLOPT_PROXY_SSL_VERIFYPEER',
'CURLOPT_SUPPRESS_CONNECT_HEADERS',
'CURLOPT_TCP_FASTOPEN',
'CURLOPT_TFTP_NO_OPTIONS',
'CURLOPT_TRANSFERTEXT',
'CURLOPT_UNRESTRICTED_AUTH',
'CURLOPT_UPLOAD',
'CURLOPT_VERBOSE',
];
foreach ($boolConstants as $constName) {
if (defined($constName) && constant($constName) === $curlOpt) {
return new BooleanType();
}
}

$intConstants = [
'CURLOPT_BUFFERSIZE',
'CURLOPT_CONNECTTIMEOUT',
'CURLOPT_CONNECTTIMEOUT_MS',
'CURLOPT_DNS_CACHE_TIMEOUT',
'CURLOPT_EXPECT_100_TIMEOUT_MS',
'CURLOPT_HAPPY_EYEBALLS_TIMEOUT_MS',
'CURLOPT_FTPSSLAUTH',
'CURLOPT_HEADEROPT',
'CURLOPT_HTTP_VERSION',
'CURLOPT_HTTPAUTH',
'CURLOPT_INFILESIZE',
'CURLOPT_LOW_SPEED_LIMIT',
'CURLOPT_LOW_SPEED_TIME',
'CURLOPT_MAXCONNECTS',
'CURLOPT_MAXREDIRS',
'CURLOPT_PORT',
'CURLOPT_POSTREDIR',
'CURLOPT_PROTOCOLS',
'CURLOPT_PROXYAUTH',
'CURLOPT_PROXYPORT',
'CURLOPT_PROXYTYPE',
'CURLOPT_REDIR_PROTOCOLS',
'CURLOPT_RESUME_FROM',
'CURLOPT_SOCKS5_AUTH',
'CURLOPT_SSL_OPTIONS',
'CURLOPT_SSL_VERIFYHOST',
'CURLOPT_SSLVERSION',
'CURLOPT_PROXY_SSL_OPTIONS',
'CURLOPT_PROXY_SSL_VERIFYHOST',
'CURLOPT_PROXY_SSLVERSION',
'CURLOPT_STREAM_WEIGHT',
'CURLOPT_TCP_KEEPALIVE',
'CURLOPT_TCP_KEEPIDLE',
'CURLOPT_TCP_KEEPINTVL',
'CURLOPT_TIMECONDITION',
'CURLOPT_TIMEOUT',
'CURLOPT_TIMEOUT_MS',
'CURLOPT_TIMEVALUE',
'CURLOPT_TIMEVALUE_LARGE',
'CURLOPT_MAX_RECV_SPEED_LARGE',
'CURLOPT_SSH_AUTH_TYPES',
'CURLOPT_IPRESOLVE',
'CURLOPT_FTP_FILEMETHOD',
];
foreach ($intConstants as $constName) {
if (defined($constName) && constant($constName) === $curlOpt) {
return new IntegerType();
}
}

$nonEmptyStringConstants = [
'CURLOPT_ABSTRACT_UNIX_SOCKET',
'CURLOPT_CAINFO',
'CURLOPT_CAPATH',
'CURLOPT_COOKIE',
'CURLOPT_COOKIEFILE',
'CURLOPT_COOKIEJAR',
'CURLOPT_COOKIELIST',
'CURLOPT_CUSTOMREQUEST',
'CURLOPT_DEFAULT_PROTOCOL',
'CURLOPT_DNS_INTERFACE',
'CURLOPT_DNS_LOCAL_IP4',
'CURLOPT_DNS_LOCAL_IP6',
'CURLOPT_EGDSOCKET',
'CURLOPT_FTPPORT',
'CURLOPT_INTERFACE',
'CURLOPT_KEYPASSWD',
'CURLOPT_KRB4LEVEL',
'CURLOPT_LOGIN_OPTIONS',
'CURLOPT_PINNEDPUBLICKEY',
'CURLOPT_POSTFIELDS',
'CURLOPT_PRIVATE',
'CURLOPT_PRE_PROXY',
'CURLOPT_PROXY',
'CURLOPT_PROXY_SERVICE_NAME',
'CURLOPT_PROXY_CAINFO',
'CURLOPT_PROXY_CAPATH',
'CURLOPT_PROXY_CRLFILE',
'CURLOPT_PROXY_KEYPASSWD',
'CURLOPT_PROXY_PINNEDPUBLICKEY',
'CURLOPT_PROXY_SSLCERT',
'CURLOPT_PROXY_SSLCERTTYPE',
'CURLOPT_PROXY_SSL_CIPHER_LIST',
'CURLOPT_PROXY_TLS13_CIPHERS',
'CURLOPT_PROXY_SSLKEY',
'CURLOPT_PROXY_SSLKEYTYPE',
'CURLOPT_PROXY_TLSAUTH_PASSWORD',
'CURLOPT_PROXY_TLSAUTH_TYPE',
'CURLOPT_PROXY_TLSAUTH_USERNAME',
'CURLOPT_PROXYUSERPWD',
'CURLOPT_RANDOM_FILE',
'CURLOPT_RANGE',
'CURLOPT_REFERER',
'CURLOPT_SERVICE_NAME',
'CURLOPT_SSH_HOST_PUBLIC_KEY_MD5',
'CURLOPT_SSH_PUBLIC_KEYFILE',
'CURLOPT_SSH_PRIVATE_KEYFILE',
'CURLOPT_SSL_CIPHER_LIST',
'CURLOPT_SSLCERT',
'CURLOPT_SSLCERTPASSWD',
'CURLOPT_SSLCERTTYPE',
'CURLOPT_SSLENGINE',
'CURLOPT_SSLENGINE_DEFAULT',
'CURLOPT_SSLKEY',
'CURLOPT_SSLKEYPASSWD',
'CURLOPT_SSLKEYTYPE',
'CURLOPT_TLS13_CIPHERS',
'CURLOPT_UNIX_SOCKET_PATH',
'CURLOPT_URL',
'CURLOPT_USERAGENT',
'CURLOPT_USERNAME',
'CURLOPT_PASSWORD',
'CURLOPT_USERPWD',
'CURLOPT_XOAUTH2_BEARER',
];
foreach ($nonEmptyStringConstants as $constName) {
if (defined($constName) && constant($constName) === $curlOpt) {
return TypeCombinator::intersect(
new StringType(),
new AccessoryNonEmptyStringType(),
);
}
}

$stringConstants = [
'CURLOPT_ENCODING',
];
foreach ($stringConstants as $constName) {
if (defined($constName) && constant($constName) === $curlOpt) {
return new StringType();
}
}

$arrayConstants = [
'CURLOPT_CONNECT_TO',
'CURLOPT_HTTP200ALIASES',
'CURLOPT_HTTPHEADER',
'CURLOPT_POSTQUOTE',
'CURLOPT_PROXYHEADER',
'CURLOPT_QUOTE',
'CURLOPT_RESOLVE',
];
foreach ($arrayConstants as $constName) {
if (defined($constName) && constant($constName) === $curlOpt) {
return new ArrayType(new MixedType(), new MixedType());
}
}

$resourceConstants = [
'CURLOPT_FILE',
'CURLOPT_INFILE',
'CURLOPT_STDERR',
'CURLOPT_WRITEHEADER',
];
foreach ($resourceConstants as $constName) {
if (defined($constName) && constant($constName) === $curlOpt) {
return new ResourceType();
}
}

// unknown constant
return null;
}

}
Loading
点击 这是indexloc提供的php浏览器服务,不要输入任何密码和下载