diff --git a/disallowed-loose-calls.neon b/disallowed-loose-calls.neon index 869582f..3e51f05 100644 --- a/disallowed-loose-calls.neon +++ b/disallowed-loose-calls.neon @@ -9,4 +9,4 @@ parameters: function: 'htmlspecialchars()' message: 'set the $flags parameter to `ENT_QUOTES` to also convert single quotes to entities to prevent some HTML injection bugs' allowParamFlagsAnywhere: - 2: ::ENT_QUOTES + 2: ::constant(ENT_QUOTES) diff --git a/extension.neon b/extension.neon index 2517bab..0595190 100644 --- a/extension.neon +++ b/extension.neon @@ -13,83 +13,196 @@ parameters: parametersSchema: allowInRootDir: schema(string(), nullable()) filesRootDir: schema(string(), nullable()) - # These should be defined using `structure` with listed keys but it seems to me that PHPStan requires - # all keys to be present in a structure but `message` & `allow*`/`disallow*` are optional. disallowedNamespaces: listOf( - arrayOf( - anyOf( - string(), - listOf(string()), - arrayOf(anyOf(int(), string(), bool())), - ) - ) + structure([ + ?namespace: anyOf(string(), listOf(string())), + ?class: anyOf(string(), listOf(string())), + ?exclude: anyOf(string(), listOf(string())), + ?message: string(), + ?allowIn: listOf(string()), + ?allowExceptIn: listOf(string()), + ?disallowIn: listOf(string()), + ?errorIdentifier: string(), + ?errorTip: string(), + ]) ) disallowedClasses: listOf( - arrayOf( - anyOf( - string(), - listOf(string()), - arrayOf(anyOf(int(), string(), bool())), - ) - ) + structure([ + ?namespace: anyOf(string(), listOf(string())), + ?class: anyOf(string(), listOf(string())), + ?exclude: anyOf(string(), listOf(string())), + ?message: string(), + ?allowIn: listOf(string()), + ?allowExceptIn: listOf(string()), + ?disallowIn: listOf(string()), + ?errorIdentifier: string(), + ?errorTip: string(), + ]) ) disallowedMethodCalls: listOf( - arrayOf( - anyOf( - string(), - listOf(string()), - arrayOf(anyOf(int(), string(), bool())), - listOf(arrayOf(anyOf(int(), string(), bool()))), - ) - ) + structure([ + ?function: anyOf(string(), listOf(string())), + ?method: anyOf(string(), listOf(string())), + ?exclude: anyOf(string(), listOf(string())), + ?definedIn: anyOf(string(), listOf(string())), + ?message: string(), + ?allowIn: listOf(string()), + ?allowExceptIn: listOf(string()), + ?disallowIn: listOf(string()), + ?allowInFunctions: listOf(string()), + ?allowInMethods: listOf(string()), + ?allowExceptInFunctions: listOf(string()), + ?allowExceptInMethods: listOf(string()), + ?disallowInFunctions: listOf(string()), + ?disallowInMethods: listOf(string()), + ?allowParamsInAllowed: arrayOf(anyOf(int(), string(), bool(), structure([position: int(), ?value: anyOf(int(), string(), bool()), ?name: string()])), anyOf(int(), string())), + ?allowParamsInAllowedAnyValue: arrayOf(anyOf(int(), structure([position: int(), ?value: anyOf(int(), string(), bool()), ?name: string()])), anyOf(int(), string())), + ?allowParamFlagsInAllowed: arrayOf(anyOf(int(), structure([position: int(), ?value: int(), ?name: string()])), anyOf(int(), string())), + ?allowParamsAnywhere: arrayOf(anyOf(int(), string(), bool(), structure([position: int(), ?value: anyOf(int(), string(), bool()), ?name: string()])), anyOf(int(), string())), + ?allowParamsAnywhereAnyValue: arrayOf(anyOf(int(), structure([position: int(), ?value: anyOf(int(), string(), bool()), ?name: string()])), anyOf(int(), string())), + ?allowParamFlagsAnywhere: arrayOf(anyOf(int(), structure([position: int(), ?value: int(), ?name: string()])), anyOf(int(), string())), + ?allowExceptParamsInAllowed: arrayOf(anyOf(int(), string(), bool(), structure([position: int(), ?value: anyOf(int(), string(), bool()), ?name: string()])), anyOf(int(), string())), + ?allowExceptParamFlagsInAllowed: arrayOf(anyOf(int(), structure([position: int(), ?value: int(), ?name: string()])), anyOf(int(), string())), + ?disallowParamFlagsInAllowed: arrayOf(anyOf(int(), structure([position: int(), ?value: int(), ?name: string()])), anyOf(int(), string())), + ?disallowParamsInAllowed: arrayOf(anyOf(int(), string(), bool(), structure([position: int(), ?value: anyOf(int(), string(), bool()), ?name: string()])), anyOf(int(), string())), + ?allowExceptParams: arrayOf(anyOf(int(), string(), bool(), structure([position: int(), ?value: anyOf(int(), string(), bool()), ?name: string()])), anyOf(int(), string())), + ?disallowParams: arrayOf(anyOf(int(), string(), bool(), structure([position: int(), ?value: anyOf(int(), string(), bool()), ?name: string()])), anyOf(int(), string())), + ?allowExceptParamFlags: arrayOf(anyOf(int(), structure([position: int(), ?value: int(), ?name: string()])), anyOf(int(), string())), + ?disallowParamFlags: arrayOf(anyOf(int(), structure([position: int(), ?value: int(), ?name: string()])), anyOf(int(), string())), + ?allowExceptCaseInsensitiveParams: arrayOf(anyOf(int(), string(), bool(), structure([position: int(), ?value: anyOf(int(), string(), bool()), ?name: string()])), anyOf(int(), string())), + ?disallowCaseInsensitiveParams: arrayOf(anyOf(int(), string(), bool(), structure([position: int(), ?value: anyOf(int(), string(), bool()), ?name: string()])), anyOf(int(), string())), + ?errorIdentifier: string(), + ?errorTip: string(), + ]) ) disallowedStaticCalls: listOf( - arrayOf( - anyOf( - string(), - listOf(string()), - arrayOf(anyOf(int(), string(), bool())), - listOf(arrayOf(anyOf(int(), string(), bool()))), - ) - ) + structure([ + ?function: anyOf(string(), listOf(string())), + ?method: anyOf(string(), listOf(string())), + ?exclude: anyOf(string(), listOf(string())), + ?definedIn: anyOf(string(), listOf(string())), + ?message: string(), + ?allowIn: listOf(string()), + ?allowExceptIn: listOf(string()), + ?disallowIn: listOf(string()), + ?allowInFunctions: listOf(string()), + ?allowInMethods: listOf(string()), + ?allowExceptInFunctions: listOf(string()), + ?allowExceptInMethods: listOf(string()), + ?disallowInFunctions: listOf(string()), + ?disallowInMethods: listOf(string()), + ?allowParamsInAllowed: arrayOf(anyOf(int(), string(), bool(), structure([position: int(), ?value: anyOf(int(), string(), bool()), ?name: string()])), anyOf(int(), string())), + ?allowParamsInAllowedAnyValue: arrayOf(anyOf(int(), structure([position: int(), ?value: anyOf(int(), string(), bool()), ?name: string()])), anyOf(int(), string())), + ?allowParamFlagsInAllowed: arrayOf(anyOf(int(), structure([position: int(), ?value: int(), ?name: string()])), anyOf(int(), string())), + ?allowParamsAnywhere: arrayOf(anyOf(int(), string(), bool(), structure([position: int(), ?value: anyOf(int(), string(), bool()), ?name: string()])), anyOf(int(), string())), + ?allowParamsAnywhereAnyValue: arrayOf(anyOf(int(), structure([position: int(), ?value: anyOf(int(), string(), bool()), ?name: string()])), anyOf(int(), string())), + ?allowParamFlagsAnywhere: arrayOf(anyOf(int(), structure([position: int(), ?value: int(), ?name: string()])), anyOf(int(), string())), + ?allowExceptParamsInAllowed: arrayOf(anyOf(int(), string(), bool(), structure([position: int(), ?value: anyOf(int(), string(), bool()), ?name: string()])), anyOf(int(), string())), + ?allowExceptParamFlagsInAllowed: arrayOf(anyOf(int(), structure([position: int(), ?value: int(), ?name: string()])), anyOf(int(), string())), + ?disallowParamFlagsInAllowed: arrayOf(anyOf(int(), structure([position: int(), ?value: int(), ?name: string()])), anyOf(int(), string())), + ?disallowParamsInAllowed: arrayOf(anyOf(int(), string(), bool(), structure([position: int(), ?value: anyOf(int(), string(), bool()), ?name: string()])), anyOf(int(), string())), + ?allowExceptParams: arrayOf(anyOf(int(), string(), bool(), structure([position: int(), ?value: anyOf(int(), string(), bool()), ?name: string()])), anyOf(int(), string())), + ?disallowParams: arrayOf(anyOf(int(), string(), bool(), structure([position: int(), ?value: anyOf(int(), string(), bool()), ?name: string()])), anyOf(int(), string())), + ?allowExceptParamFlags: arrayOf(anyOf(int(), structure([position: int(), ?value: int(), ?name: string()])), anyOf(int(), string())), + ?disallowParamFlags: arrayOf(anyOf(int(), structure([position: int(), ?value: int(), ?name: string()])), anyOf(int(), string())), + ?allowExceptCaseInsensitiveParams: arrayOf(anyOf(int(), string(), bool(), structure([position: int(), ?value: anyOf(int(), string(), bool()), ?name: string()])), anyOf(int(), string())), + ?disallowCaseInsensitiveParams: arrayOf(anyOf(int(), string(), bool(), structure([position: int(), ?value: anyOf(int(), string(), bool()), ?name: string()])), anyOf(int(), string())), + ?errorIdentifier: string(), + ?errorTip: string(), + ]) ) disallowedFunctionCalls: listOf( - arrayOf( - anyOf( - string(), - listOf(string()), - arrayOf(anyOf(int(), string(), bool())), - listOf(arrayOf(anyOf(int(), string(), bool()))), - ) - ) + structure([ + ?function: anyOf(string(), listOf(string())), + ?method: anyOf(string(), listOf(string())), + ?exclude: anyOf(string(), listOf(string())), + ?definedIn: anyOf(string(), listOf(string())), + ?message: string(), + ?allowIn: listOf(string()), + ?allowExceptIn: listOf(string()), + ?disallowIn: listOf(string()), + ?allowInFunctions: listOf(string()), + ?allowInMethods: listOf(string()), + ?allowExceptInFunctions: listOf(string()), + ?allowExceptInMethods: listOf(string()), + ?disallowInFunctions: listOf(string()), + ?disallowInMethods: listOf(string()), + ?allowParamsInAllowed: arrayOf(anyOf(int(), string(), bool(), structure([position: int(), ?value: anyOf(int(), string(), bool()), ?name: string()])), anyOf(int(), string())), + ?allowParamsInAllowedAnyValue: arrayOf(anyOf(int(), structure([position: int(), ?value: anyOf(int(), string(), bool()), ?name: string()])), anyOf(int(), string())), + ?allowParamFlagsInAllowed: arrayOf(anyOf(int(), structure([position: int(), ?value: int(), ?name: string()])), anyOf(int(), string())), + ?allowParamsAnywhere: arrayOf(anyOf(int(), string(), bool(), structure([position: int(), ?value: anyOf(int(), string(), bool()), ?name: string()])), anyOf(int(), string())), + ?allowParamsAnywhereAnyValue: arrayOf(anyOf(int(), structure([position: int(), ?value: anyOf(int(), string(), bool()), ?name: string()])), anyOf(int(), string())), + ?allowParamFlagsAnywhere: arrayOf(anyOf(int(), structure([position: int(), ?value: int(), ?name: string()])), anyOf(int(), string())), + ?allowExceptParamsInAllowed: arrayOf(anyOf(int(), string(), bool(), structure([position: int(), ?value: anyOf(int(), string(), bool()), ?name: string()])), anyOf(int(), string())), + ?allowExceptParamFlagsInAllowed: arrayOf(anyOf(int(), structure([position: int(), ?value: int(), ?name: string()])), anyOf(int(), string())), + ?disallowParamFlagsInAllowed: arrayOf(anyOf(int(), structure([position: int(), ?value: int(), ?name: string()])), anyOf(int(), string())), + ?disallowParamsInAllowed: arrayOf(anyOf(int(), string(), bool(), structure([position: int(), ?value: anyOf(int(), string(), bool()), ?name: string()])), anyOf(int(), string())), + ?allowExceptParams: arrayOf(anyOf(int(), string(), bool(), structure([position: int(), ?value: anyOf(int(), string(), bool()), ?name: string()])), anyOf(int(), string())), + ?disallowParams: arrayOf(anyOf(int(), string(), bool(), structure([position: int(), ?value: anyOf(int(), string(), bool()), ?name: string()])), anyOf(int(), string())), + ?allowExceptParamFlags: arrayOf(anyOf(int(), structure([position: int(), ?value: int(), ?name: string()])), anyOf(int(), string())), + ?disallowParamFlags: arrayOf(anyOf(int(), structure([position: int(), ?value: int(), ?name: string()])), anyOf(int(), string())), + ?allowExceptCaseInsensitiveParams: arrayOf(anyOf(int(), string(), bool(), structure([position: int(), ?value: anyOf(int(), string(), bool()), ?name: string()])), anyOf(int(), string())), + ?disallowCaseInsensitiveParams: arrayOf(anyOf(int(), string(), bool(), structure([position: int(), ?value: anyOf(int(), string(), bool()), ?name: string()])), anyOf(int(), string())), + ?errorIdentifier: string(), + ?errorTip: string(), + ]) ) disallowedConstants: listOf( - arrayOf( - anyOf( - string(), - listOf(string()), - arrayOf(anyOf(int(), string(), bool())), - ) - ) + structure([ + ?class: string(), + ?constant: anyOf(string(), listOf(string())), + ?message: string(), + ?allowIn: listOf(string()), + ?allowExceptIn: listOf(string()), + ?disallowIn: listOf(string()), + ?errorIdentifier: string(), + ?errorTip: string(), + ]) ) disallowedSuperglobals: listOf( - arrayOf( - anyOf( - string(), - listOf(string()), - arrayOf(anyOf(int(), string(), bool())), - ) - ) + structure([ + ?superglobal: anyOf(string(), listOf(string())), + ?message: string(), + ?allowIn: listOf(string()), + ?allowExceptIn: list(string()), + ?disallowIn: list(string()), + ?errorIdentifier: string(), + ?errorTip: string(), + ]) ) disallowedAttributes: listOf( - arrayOf( - anyOf( - string(), - listOf(string()), - arrayOf(anyOf(int(), string(), bool())), - listOf(arrayOf(anyOf(int(), string(), bool()))), - ) - ) + structure([ + attribute: anyOf(string(), listOf(string())), + ?exclude: anyOf(string(), listOf(string())), + ?message: string(), + ?allowIn: listOf(string()), + ?allowExceptIn: listOf(string()), + ?disallowIn: listOf(string()), + ?allowInFunctions: listOf(string()), + ?allowInMethods: listOf(string()), + ?allowExceptInFunctions: listOf(string()), + ?allowExceptInMethods: listOf(string()), + ?disallowInFunctions: listOf(string()), + ?disallowInMethods: listOf(string()), + ?allowParamsInAllowed: arrayOf(anyOf(int(), string(), bool(), structure([position: int(), ?value: anyOf(int(), string(), bool()), ?name: string()])), anyOf(int(), string())), + ?allowParamsInAllowedAnyValue: arrayOf(anyOf(int(), structure([position: int(), ?value: anyOf(int(), string(), bool()), ?name: string()])), anyOf(int(), string())), + ?allowParamFlagsInAllowed: arrayOf(anyOf(int(), structure([position: int(), ?value: int(), ?name: string()])), anyOf(int(), string())), + ?allowParamsAnywhere: arrayOf(anyOf(int(), string(), bool(), structure([position: int(), ?value: anyOf(int(), string(), bool()), ?name: string()])), anyOf(int(), string())), + ?allowParamsAnywhereAnyValue: arrayOf(anyOf(int(), structure([position: int(), ?value: anyOf(int(), string(), bool()), ?name: string()])), anyOf(int(), string())), + ?allowParamFlagsAnywhere: arrayOf(anyOf(int(), structure([position: int(), ?value: int(), ?name: string()])), anyOf(int(), string())), + ?allowExceptParamsInAllowed: arrayOf(anyOf(int(), string(), bool(), structure([position: int(), ?value: anyOf(int(), string(), bool()), ?name: string()])), anyOf(int(), string())), + ?allowExceptParamFlagsInAllowed: arrayOf(anyOf(int(), structure([position: int(), ?value: int(), ?name: string()])), anyOf(int(), string())), + ?disallowParamFlagsInAllowed: arrayOf(anyOf(int(), structure([position: int(), ?value: int(), ?name: string()])), anyOf(int(), string())), + ?disallowParamsInAllowed: arrayOf(anyOf(int(), string(), bool(), structure([position: int(), ?value: anyOf(int(), string(), bool()), ?name: string()])), anyOf(int(), string())), + ?allowExceptParams: arrayOf(anyOf(int(), string(), bool(), structure([position: int(), ?value: anyOf(int(), string(), bool()), ?name: string()])), anyOf(int(), string())), + ?disallowParams: arrayOf(anyOf(int(), string(), bool(), structure([position: int(), ?value: anyOf(int(), string(), bool()), ?name: string()])), anyOf(int(), string())), + ?allowExceptParamFlags: arrayOf(anyOf(int(), structure([position: int(), ?value: int(), ?name: string()])), anyOf(int(), string())), + ?disallowParamFlags: arrayOf(anyOf(int(), structure([position: int(), ?value: int(), ?name: string()])), anyOf(int(), string())), + ?allowExceptCaseInsensitiveParams: arrayOf(anyOf(int(), string(), bool(), structure([position: int(), ?value: anyOf(int(), string(), bool()), ?name: string()])), anyOf(int(), string())), + ?disallowCaseInsensitiveParams: arrayOf(anyOf(int(), string(), bool(), structure([position: int(), ?value: anyOf(int(), string(), bool()), ?name: string()])), anyOf(int(), string())), + errorIdentifier: string(), + errorTip: string(), + ]) ) services: diff --git a/src/DisallowedConstantFactory.php b/src/DisallowedConstantFactory.php index df73153..8bfd35b 100644 --- a/src/DisallowedConstantFactory.php +++ b/src/DisallowedConstantFactory.php @@ -20,7 +20,7 @@ public function __construct(Normalizer $normalizer) /** - * @param array, allowExceptIn?:list, disallowIn?:list, errorIdentifier?:string, errorTip?:string}> $config + * @param array, message?:string, allowIn?:list, allowExceptIn?:list, disallowIn?:list, errorIdentifier?:string, errorTip?:string}> $config * @return list * @throws ShouldNotHappenException */ diff --git a/src/DisallowedNamespaceFactory.php b/src/DisallowedNamespaceFactory.php index 4a92081..a1780e2 100644 --- a/src/DisallowedNamespaceFactory.php +++ b/src/DisallowedNamespaceFactory.php @@ -20,7 +20,7 @@ public function __construct(Normalizer $normalizer) /** - * @param array, message?:string, allowIn?:list, allowExceptIn?:list, disallowIn?:list, errorIdentifier?:string, errorTip?:string}> $config + * @param array, class?:string|list, exclude?:string|list, message?:string, allowIn?:list, allowExceptIn?:list, disallowIn?:list, errorIdentifier?:string, errorTip?:string}> $config * @return list */ public function createFromConfig(array $config): array diff --git a/src/DisallowedSuperglobalFactory.php b/src/DisallowedSuperglobalFactory.php index 10a5f3b..940a476 100644 --- a/src/DisallowedSuperglobalFactory.php +++ b/src/DisallowedSuperglobalFactory.php @@ -25,7 +25,7 @@ class DisallowedSuperglobalFactory implements DisallowedVariableFactory /** - * @param array, allowExceptIn?:list, disallowIn?:list, errorIdentifier?:string, errorTip?:string}> $config + * @param array, message?:string, allowIn?:list, allowExceptIn?:list, disallowIn?:list, errorIdentifier?:string, errorTip?:string}> $config * @return list * @throws ShouldNotHappenException */ diff --git a/src/Usages/ClassConstantUsages.php b/src/Usages/ClassConstantUsages.php index f8a2d58..7ed49ee 100644 --- a/src/Usages/ClassConstantUsages.php +++ b/src/Usages/ClassConstantUsages.php @@ -46,7 +46,7 @@ class ClassConstantUsages implements Rule * @param DisallowedConstantFactory $disallowedConstantFactory * @param TypeResolver $typeResolver * @param Formatter $formatter - * @param array}> $disallowedConstants + * @param array, message?:string, allowIn?:list}> $disallowedConstants * @throws ShouldNotHappenException */ public function __construct( diff --git a/src/Usages/NamespaceUsages.php b/src/Usages/NamespaceUsages.php index 815f03c..24f42ad 100644 --- a/src/Usages/NamespaceUsages.php +++ b/src/Usages/NamespaceUsages.php @@ -43,7 +43,7 @@ class NamespaceUsages implements Rule * @param DisallowedNamespaceRuleErrors $disallowedNamespaceRuleErrors * @param DisallowedNamespaceFactory $disallowNamespaceFactory * @param Normalizer $normalizer - * @param array}> $forbiddenNamespaces + * @param array, class?:string|list, exclude?:string|list, message?:string, allowIn?:list, allowExceptIn?:list, disallowIn?:list, errorIdentifier?:string, errorTip?:string}> $forbiddenNamespaces */ public function __construct( DisallowedNamespaceRuleErrors $disallowedNamespaceRuleErrors, diff --git a/tests/Configs/LooseConfigFunctionCallsTest.php b/tests/Configs/LooseConfigFunctionCallsTest.php index 55c13f3..c875169 100644 --- a/tests/Configs/LooseConfigFunctionCallsTest.php +++ b/tests/Configs/LooseConfigFunctionCallsTest.php @@ -3,54 +3,16 @@ namespace Spaze\PHPStan\Rules\Disallowed\Configs; -use Nette\Neon\Neon; -use PHPStan\File\FileHelper; use PHPStan\Rules\Rule; -use PHPStan\ShouldNotHappenException; use PHPStan\Testing\RuleTestCase; -use Spaze\PHPStan\Rules\Disallowed\Allowed\Allowed; -use Spaze\PHPStan\Rules\Disallowed\Allowed\AllowedPath; use Spaze\PHPStan\Rules\Disallowed\Calls\FunctionCalls; -use Spaze\PHPStan\Rules\Disallowed\DisallowedCallFactory; -use Spaze\PHPStan\Rules\Disallowed\File\FilePath; -use Spaze\PHPStan\Rules\Disallowed\Formatter\Formatter; -use Spaze\PHPStan\Rules\Disallowed\Identifier\Identifier; -use Spaze\PHPStan\Rules\Disallowed\Normalizer\Normalizer; -use Spaze\PHPStan\Rules\Disallowed\RuleErrors\DisallowedCallsRuleErrors; class LooseConfigFunctionCallsTest extends RuleTestCase { - /** - * @throws ShouldNotHappenException - */ protected function getRule(): Rule { - // Load the configuration from this file - $config = Neon::decode(file_get_contents(__DIR__ . '/../../disallowed-loose-calls.neon')); - // emulate how the real config loader expands constants that are used in the config file above (e.g. ::ENT_QUOTES) - foreach ($config['parameters']['disallowedFunctionCalls'] as &$call) { - foreach (['allowParamsAnywhere', 'allowParamFlagsAnywhere'] as $key) { - if (!isset($call[$key])) { - continue; - } - foreach ($call[$key] as &$param) { - if (is_string($param) && preg_match('/^::([A-Z0-9_]+)$/', $param, $matches)) { - $param = constant($matches[1]); - } - } - } - } - $normalizer = new Normalizer(); - $formatter = new Formatter($normalizer); - $filePath = new FilePath(new FileHelper(__DIR__)); - $allowed = new Allowed($formatter, $normalizer, new AllowedPath($filePath)); - return new FunctionCalls( - new DisallowedCallsRuleErrors($allowed, new Identifier(), $filePath, $formatter), - new DisallowedCallFactory($formatter, $normalizer, $allowed), - $this->createReflectionProvider(), - $config['parameters']['disallowedFunctionCalls'] - ); + return self::getContainer()->getByType(FunctionCalls::class); } @@ -67,4 +29,13 @@ public function testRule(): void ]); } + + public static function getAdditionalConfigFiles(): array + { + return [ + __DIR__ . '/../../extension.neon', + __DIR__ . '/../../disallowed-loose-calls.neon', + ]; + } + }