From 29a5955b38c8c9ec2d5cd6b93360ce67a8d6a22d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20=C5=A0pa=C4=8Dek?= Date: Tue, 19 Dec 2023 23:11:08 +0100 Subject: [PATCH] Can specify params with a doctype in `typeString` config option Close #233 --- docs/allow-attributes.md | 2 + docs/allow-with-flags.md | 3 + docs/allow-with-parameters.md | 30 +- extension.neon | 112 +++--- src/Allowed/Allowed.php | 38 +- src/Params/Param.php | 5 +- src/Params/ParamValue.php | 20 +- src/Params/ParamValueAny.php | 3 - .../ParamValueCaseInsensitiveExcept.php | 30 +- src/Params/ParamValueExcept.php | 12 +- src/Params/ParamValueExceptAny.php | 3 - src/Params/ParamValueFlag.php | 35 ++ src/Params/ParamValueFlagExcept.php | 13 +- src/Params/ParamValueFlagSpecific.php | 13 +- src/Params/ParamValueSpecific.php | 12 +- ...TypeStringParamsInvalidFlagsConfigTest.php | 56 +++ .../FunctionCallsTypeStringParamsTest.php | 335 ++++++++++++++++++ tests/src/Functions.php | 28 ++ .../functionCallsTypeStringParams.php | 33 ++ .../functionCallsTypeStringParams.php | 33 ++ 20 files changed, 665 insertions(+), 151 deletions(-) create mode 100644 src/Params/ParamValueFlag.php create mode 100644 tests/Calls/FunctionCallsTypeStringParamsInvalidFlagsConfigTest.php create mode 100644 tests/Calls/FunctionCallsTypeStringParamsTest.php create mode 100644 tests/src/disallowed-allow/functionCallsTypeStringParams.php create mode 100644 tests/src/disallowed/functionCallsTypeStringParams.php diff --git a/docs/allow-attributes.md b/docs/allow-attributes.md index 1f406bd..7028c5e 100644 --- a/docs/allow-attributes.md +++ b/docs/allow-attributes.md @@ -12,3 +12,5 @@ parameters: position: 1 name: repositoryClass ``` + +You can also use `value` or `typeString` directives, just like with functions or methods. diff --git a/docs/allow-with-flags.md b/docs/allow-with-flags.md index 0ced319..c267812 100644 --- a/docs/allow-with-flags.md +++ b/docs/allow-with-flags.md @@ -26,3 +26,6 @@ parameters: position: 2 value: ::JSON_HEX_APOS ``` + +Just like with regular parameters, you can also use `typeString` instead of `value`. +The extra bonus this brings is unions: if you want to (dis)allow a parameter when either the flag `1` or `2` is set, use `typeString: 1 | 2`. Note that the `|` operator here is not the PHP's _bitwise or_ operator. diff --git a/docs/allow-with-parameters.md b/docs/allow-with-parameters.md index eab4c4c..e3444f2 100644 --- a/docs/allow-with-parameters.md +++ b/docs/allow-with-parameters.md @@ -1,6 +1,9 @@ ## Allow with specified parameters only -You can also narrow down the allowed items when called with some parameters (applies only to disallowed method, static & function calls, for obvious reasons). _Please note that for now, only scalar values are supported in the configuration, not arrays._ +You can also narrow down the allowed items when called with some parameters (applies only to disallowed method, static & function calls, for obvious reasons). +Only scalar values and no arrays are supported with the `value` configuration directive, but with the `typeString` directive, +arrays and unions are also supported, and generally anything you can express with a PHPDoc type string, if it makes sense. +When `typeString` is specified, `value` directive is ignored for the given parameter. For example, you want to disallow calling `print_r()` but want to allow `print_r(..., true)`. This can be done with optional `allowParamsInAllowed` or `allowParamsAnywhere` configuration keys: @@ -184,3 +187,28 @@ parameters: ``` But because the "positional _or_ named" limitation described above applies here as well, I generally don't recommend using these shortcuts and instead recommend specifying both `position` and `name` keys. + +### PHPDoc type strings + +Instead of the `value` directive, you can use the `typeString` directive which allows you to specify arrays, unions, and anything that can be expressed with PHPDoc: + +```neon +parameters: + disallowedFunctionCalls: + # ... + allowParamsInAllowed: + - + position: 1 + name: 'message' + typeString: "'foo'" +``` + +The above example is the same as writing `value: foo` but because you want to specify a literal type string, you need to enclose the string in single quotes to indicate it's a string, not a class name. With integers, `typeString: 1` is the same as `value: 1`. + +Type string allows you to specify: +- Arrays, e.g. `typeString: array{}` meaning empty array, or vice versa with `typeString: non-empty-array`, or even `typeString: array{foo:'bar'}` meaning an array with a `foo` key and `bar` string value +- Unions, e.g. `typeString: 1|2`, `typeString: "'foo'|'bar'"`, where the former example means the value must be an integer `1` or an integer `2`, and the latter means the value must be a string `foo` or `bar` +- Classes, e.g. `typeString: DateTime` which means an object of that class, or a child class od that class +- Any type as [understood by PHPStan](https://phpstan.org/writing-php-code/phpdoc-types), but not everything may make sense in your case + +If both `typeString` and `value` directives are specified, the `value` directive is ignored. diff --git a/extension.neon b/extension.neon index 1cfcb31..78d63e3 100644 --- a/extension.neon +++ b/extension.neon @@ -55,24 +55,24 @@ parametersSchema: ?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())), + ?allowParamsInAllowed: arrayOf(anyOf(int(), string(), bool(), structure([position: int(), ?value: anyOf(int(), string(), bool()), ?typeString: string(), ?name: string()])), anyOf(int(), string())), ?allowParamsInAllowedAnyValue: arrayOf(anyOf(int(), structure([position: int(), ?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())), + ?allowParamFlagsInAllowed: arrayOf(anyOf(int(), structure([position: int(), ?value: int(), ?typeString: string(), ?name: string()])), anyOf(int(), string())), + ?allowParamsAnywhere: arrayOf(anyOf(int(), string(), bool(), structure([position: int(), ?value: anyOf(int(), string(), bool()), ?typeString: string(), ?name: string()])), anyOf(int(), string())), ?allowParamsAnywhereAnyValue: arrayOf(anyOf(int(), structure([position: int(), ?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())), + ?allowParamFlagsAnywhere: arrayOf(anyOf(int(), structure([position: int(), ?value: int(), ?typeString: string(), ?name: string()])), anyOf(int(), string())), + ?allowExceptParamsInAllowed: arrayOf(anyOf(int(), string(), bool(), structure([position: int(), ?value: anyOf(int(), string(), bool()), ?typeString: string(), ?name: string()])), anyOf(int(), string())), + ?allowExceptParamFlagsInAllowed: arrayOf(anyOf(int(), structure([position: int(), ?value: int(), ?typeString: string(), ?name: string()])), anyOf(int(), string())), + ?disallowParamFlagsInAllowed: arrayOf(anyOf(int(), structure([position: int(), ?value: int(), ?typeString: string(), ?name: string()])), anyOf(int(), string())), + ?disallowParamsInAllowed: arrayOf(anyOf(int(), string(), bool(), structure([position: int(), ?value: anyOf(int(), string(), bool()), ?typeString: string(), ?name: string()])), anyOf(int(), string())), + ?allowExceptParams: arrayOf(anyOf(int(), string(), bool(), structure([position: int(), ?value: anyOf(int(), string(), bool()), ?typeString: string(), ?name: string()])), anyOf(int(), string())), + ?disallowParams: arrayOf(anyOf(int(), string(), bool(), structure([position: int(), ?value: anyOf(int(), string(), bool()), ?typeString: string(), ?name: string()])), anyOf(int(), string())), ?allowExceptParamsAnyValue: arrayOf(anyOf(int(), structure([position: int(), ?name: string()])), anyOf(int(), string())), ?disallowParamsAnyValue: arrayOf(anyOf(int(), structure([position: int(), ?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())), + ?allowExceptParamFlags: arrayOf(anyOf(int(), structure([position: int(), ?value: int(), ?typeString: string(), ?name: string()])), anyOf(int(), string())), + ?disallowParamFlags: arrayOf(anyOf(int(), structure([position: int(), ?value: int(), ?typeString: string(), ?name: string()])), anyOf(int(), string())), + ?allowExceptCaseInsensitiveParams: arrayOf(anyOf(int(), string(), bool(), structure([position: int(), ?value: anyOf(int(), string(), bool()), ?typeString: string(), ?name: string()])), anyOf(int(), string())), + ?disallowCaseInsensitiveParams: arrayOf(anyOf(int(), string(), bool(), structure([position: int(), ?value: anyOf(int(), string(), bool()), ?typeString: string(), ?name: string()])), anyOf(int(), string())), ?errorIdentifier: string(), ?errorTip: string(), ]) @@ -93,24 +93,24 @@ parametersSchema: ?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())), + ?allowParamsInAllowed: arrayOf(anyOf(int(), string(), bool(), structure([position: int(), ?value: anyOf(int(), string(), bool()), ?typeString: string(), ?name: string()])), anyOf(int(), string())), ?allowParamsInAllowedAnyValue: arrayOf(anyOf(int(), structure([position: int(), ?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())), + ?allowParamFlagsInAllowed: arrayOf(anyOf(int(), structure([position: int(), ?value: int(), ?typeString: string(), ?name: string()])), anyOf(int(), string())), + ?allowParamsAnywhere: arrayOf(anyOf(int(), string(), bool(), structure([position: int(), ?value: anyOf(int(), string(), bool()), ?typeString: string(), ?name: string()])), anyOf(int(), string())), ?allowParamsAnywhereAnyValue: arrayOf(anyOf(int(), structure([position: int(), ?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())), + ?allowParamFlagsAnywhere: arrayOf(anyOf(int(), structure([position: int(), ?value: int(), ?typeString: string(), ?name: string()])), anyOf(int(), string())), + ?allowExceptParamsInAllowed: arrayOf(anyOf(int(), string(), bool(), structure([position: int(), ?value: anyOf(int(), string(), bool()), ?typeString: string(), ?name: string()])), anyOf(int(), string())), + ?allowExceptParamFlagsInAllowed: arrayOf(anyOf(int(), structure([position: int(), ?value: int(), ?typeString: string(), ?name: string()])), anyOf(int(), string())), + ?disallowParamFlagsInAllowed: arrayOf(anyOf(int(), structure([position: int(), ?value: int(), ?typeString: string(), ?name: string()])), anyOf(int(), string())), + ?disallowParamsInAllowed: arrayOf(anyOf(int(), string(), bool(), structure([position: int(), ?value: anyOf(int(), string(), bool()), ?typeString: string(), ?name: string()])), anyOf(int(), string())), + ?allowExceptParams: arrayOf(anyOf(int(), string(), bool(), structure([position: int(), ?value: anyOf(int(), string(), bool()), ?typeString: string(), ?name: string()])), anyOf(int(), string())), + ?disallowParams: arrayOf(anyOf(int(), string(), bool(), structure([position: int(), ?value: anyOf(int(), string(), bool()), ?typeString: string(), ?name: string()])), anyOf(int(), string())), ?allowExceptParamsAnyValue: arrayOf(anyOf(int(), structure([position: int(), ?name: string()])), anyOf(int(), string())), ?disallowParamsAnyValue: arrayOf(anyOf(int(), structure([position: int(), ?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())), + ?allowExceptParamFlags: arrayOf(anyOf(int(), structure([position: int(), ?value: int(), ?typeString: string(), ?name: string()])), anyOf(int(), string())), + ?disallowParamFlags: arrayOf(anyOf(int(), structure([position: int(), ?value: int(), ?typeString: string(), ?name: string()])), anyOf(int(), string())), + ?allowExceptCaseInsensitiveParams: arrayOf(anyOf(int(), string(), bool(), structure([position: int(), ?value: anyOf(int(), string(), bool()), ?typeString: string(), ?name: string()])), anyOf(int(), string())), + ?disallowCaseInsensitiveParams: arrayOf(anyOf(int(), string(), bool(), structure([position: int(), ?value: anyOf(int(), string(), bool()), ?typeString: string(), ?name: string()])), anyOf(int(), string())), ?errorIdentifier: string(), ?errorTip: string(), ]) @@ -131,24 +131,24 @@ parametersSchema: ?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())), + ?allowParamsInAllowed: arrayOf(anyOf(int(), string(), bool(), structure([position: int(), ?value: anyOf(int(), string(), bool()), ?typeString: string(), ?name: string()])), anyOf(int(), string())), ?allowParamsInAllowedAnyValue: arrayOf(anyOf(int(), structure([position: int(), ?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())), + ?allowParamFlagsInAllowed: arrayOf(anyOf(int(), structure([position: int(), ?value: int(), ?typeString: string(), ?name: string()])), anyOf(int(), string())), + ?allowParamsAnywhere: arrayOf(anyOf(int(), string(), bool(), structure([position: int(), ?value: anyOf(int(), string(), bool()), ?typeString: string(), ?name: string()])), anyOf(int(), string())), ?allowParamsAnywhereAnyValue: arrayOf(anyOf(int(), structure([position: int(), ?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())), + ?allowParamFlagsAnywhere: arrayOf(anyOf(int(), structure([position: int(), ?value: int(), ?typeString: string(), ?name: string()])), anyOf(int(), string())), + ?allowExceptParamsInAllowed: arrayOf(anyOf(int(), string(), bool(), structure([position: int(), ?value: anyOf(int(), string(), bool()), ?typeString: string(), ?name: string()])), anyOf(int(), string())), + ?allowExceptParamFlagsInAllowed: arrayOf(anyOf(int(), structure([position: int(), ?value: int(), ?typeString: string(), ?name: string()])), anyOf(int(), string())), + ?disallowParamFlagsInAllowed: arrayOf(anyOf(int(), structure([position: int(), ?value: int(), ?typeString: string(), ?name: string()])), anyOf(int(), string())), + ?disallowParamsInAllowed: arrayOf(anyOf(int(), string(), bool(), structure([position: int(), ?value: anyOf(int(), string(), bool()), ?typeString: string(), ?name: string()])), anyOf(int(), string())), + ?allowExceptParams: arrayOf(anyOf(int(), string(), bool(), structure([position: int(), ?value: anyOf(int(), string(), bool()), ?typeString: string(), ?name: string()])), anyOf(int(), string())), + ?disallowParams: arrayOf(anyOf(int(), string(), bool(), structure([position: int(), ?value: anyOf(int(), string(), bool()), ?typeString: string(), ?name: string()])), anyOf(int(), string())), ?allowExceptParamsAnyValue: arrayOf(anyOf(int(), structure([position: int(), ?name: string()])), anyOf(int(), string())), ?disallowParamsAnyValue: arrayOf(anyOf(int(), structure([position: int(), ?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())), + ?allowExceptParamFlags: arrayOf(anyOf(int(), structure([position: int(), ?value: int(), ?typeString: string(), ?name: string()])), anyOf(int(), string())), + ?disallowParamFlags: arrayOf(anyOf(int(), structure([position: int(), ?value: int(), ?typeString: string(), ?name: string()])), anyOf(int(), string())), + ?allowExceptCaseInsensitiveParams: arrayOf(anyOf(int(), string(), bool(), structure([position: int(), ?value: anyOf(int(), string(), bool()), ?typeString: string(), ?name: string()])), anyOf(int(), string())), + ?disallowCaseInsensitiveParams: arrayOf(anyOf(int(), string(), bool(), structure([position: int(), ?value: anyOf(int(), string(), bool()), ?typeString: string(), ?name: string()])), anyOf(int(), string())), ?errorIdentifier: string(), ?errorTip: string(), ]) @@ -190,24 +190,24 @@ parametersSchema: ?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())), + ?allowParamsInAllowed: arrayOf(anyOf(int(), string(), bool(), structure([position: int(), ?value: anyOf(int(), string(), bool()), ?typeString: string(), ?name: string()])), anyOf(int(), string())), ?allowParamsInAllowedAnyValue: arrayOf(anyOf(int(), structure([position: int(), ?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())), + ?allowParamFlagsInAllowed: arrayOf(anyOf(int(), structure([position: int(), ?value: int(), ?typeString: string(), ?name: string()])), anyOf(int(), string())), + ?allowParamsAnywhere: arrayOf(anyOf(int(), string(), bool(), structure([position: int(), ?value: anyOf(int(), string(), bool()), ?typeString: string(), ?name: string()])), anyOf(int(), string())), ?allowParamsAnywhereAnyValue: arrayOf(anyOf(int(), structure([position: int(), ?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())), + ?allowParamFlagsAnywhere: arrayOf(anyOf(int(), structure([position: int(), ?value: int(), ?typeString: string(), ?name: string()])), anyOf(int(), string())), + ?allowExceptParamsInAllowed: arrayOf(anyOf(int(), string(), bool(), structure([position: int(), ?value: anyOf(int(), string(), bool()), ?typeString: string(), ?name: string()])), anyOf(int(), string())), + ?allowExceptParamFlagsInAllowed: arrayOf(anyOf(int(), structure([position: int(), ?value: int(), ?typeString: string(), ?name: string()])), anyOf(int(), string())), + ?disallowParamFlagsInAllowed: arrayOf(anyOf(int(), structure([position: int(), ?value: int(), ?typeString: string(), ?name: string()])), anyOf(int(), string())), + ?disallowParamsInAllowed: arrayOf(anyOf(int(), string(), bool(), structure([position: int(), ?value: anyOf(int(), string(), bool()), ?typeString: string(), ?name: string()])), anyOf(int(), string())), + ?allowExceptParams: arrayOf(anyOf(int(), string(), bool(), structure([position: int(), ?value: anyOf(int(), string(), bool()), ?typeString: string(), ?name: string()])), anyOf(int(), string())), + ?disallowParams: arrayOf(anyOf(int(), string(), bool(), structure([position: int(), ?value: anyOf(int(), string(), bool()), ?typeString: string(), ?name: string()])), anyOf(int(), string())), ?allowExceptParamsAnyValue: arrayOf(anyOf(int(), structure([position: int(), ?name: string()])), anyOf(int(), string())), ?disallowParamsAnyValue: arrayOf(anyOf(int(), structure([position: int(), ?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())), + ?allowExceptParamFlags: arrayOf(anyOf(int(), structure([position: int(), ?value: int(), ?typeString: string(), ?name: string()])), anyOf(int(), string())), + ?disallowParamFlags: arrayOf(anyOf(int(), structure([position: int(), ?value: int(), ?typeString: string(), ?name: string()])), anyOf(int(), string())), + ?allowExceptCaseInsensitiveParams: arrayOf(anyOf(int(), string(), bool(), structure([position: int(), ?value: anyOf(int(), string(), bool()), ?typeString: string(), ?name: string()])), anyOf(int(), string())), + ?disallowCaseInsensitiveParams: arrayOf(anyOf(int(), string(), bool(), structure([position: int(), ?value: anyOf(int(), string(), bool()), ?typeString: string(), ?name: string()])), anyOf(int(), string())), ?errorIdentifier: string(), ?errorTip: string(), ]) diff --git a/src/Allowed/Allowed.php b/src/Allowed/Allowed.php index d5fc886..8fdd401 100644 --- a/src/Allowed/Allowed.php +++ b/src/Allowed/Allowed.php @@ -5,8 +5,13 @@ use PhpParser\Node\Arg; use PHPStan\Analyser\Scope; +use PHPStan\PhpDoc\TypeStringResolver; use PHPStan\Reflection\FunctionReflection; use PHPStan\Reflection\MethodReflection; +use PHPStan\Type\Constant\ConstantBooleanType; +use PHPStan\Type\Constant\ConstantIntegerType; +use PHPStan\Type\Constant\ConstantStringType; +use PHPStan\Type\NullType; use PHPStan\Type\Type; use PHPStan\Type\UnionType; use Spaze\PHPStan\Rules\Disallowed\DisallowedWithParams; @@ -36,12 +41,20 @@ class Allowed /** @var AllowedPath */ private $allowedPath; + /** @var TypeStringResolver */ + private $typeStringResolver; - public function __construct(Formatter $formatter, Normalizer $normalizer, AllowedPath $allowedPath) - { + + public function __construct( + Formatter $formatter, + Normalizer $normalizer, + AllowedPath $allowedPath, + TypeStringResolver $typeStringResolver + ) { $this->formatter = $formatter; $this->normalizer = $normalizer; $this->allowedPath = $allowedPath; + $this->typeStringResolver = $typeStringResolver; } @@ -242,7 +255,7 @@ public function getConfig(array $allowed): AllowedConfig * @template T of ParamValue * @param class-string $class * @param int|string $key - * @param int|bool|string|null|array{position:int, value?:int|bool|string, name?:string} $value + * @param int|bool|string|null|array{position:int, value?:int|bool|string, typeString?:string, name?:string} $value * @return T * @throws UnsupportedParamTypeInConfigException */ @@ -253,6 +266,7 @@ private function paramFactory(string $class, $key, $value): ParamValue $paramPosition = $value['position']; $paramName = $value['name'] ?? null; $paramValue = $value['value'] ?? null; + $typeString = $value['typeString'] ?? null; } elseif (in_array($class, [ParamValueAny::class, ParamValueExceptAny::class], true)) { if (is_numeric($value)) { $paramPosition = (int)$value; @@ -261,22 +275,34 @@ private function paramFactory(string $class, $key, $value): ParamValue $paramPosition = null; $paramName = (string)$value; } - $paramValue = null; + $paramValue = $typeString = null; } else { $paramPosition = (int)$key; $paramName = null; $paramValue = $value; + $typeString = null; } } else { $paramPosition = null; $paramName = $key; $paramValue = $value; + $typeString = null; } - if (!is_int($paramValue) && !is_bool($paramValue) && !is_string($paramValue) && !is_null($paramValue)) { + if ($typeString) { + $type = $this->typeStringResolver->resolve($typeString); + } elseif (is_int($paramValue)) { + $type = new ConstantIntegerType($paramValue); + } elseif (is_bool($paramValue)) { + $type = new ConstantBooleanType($paramValue); + } elseif (is_string($paramValue)) { + $type = new ConstantStringType($paramValue); + } elseif (is_null($paramValue)) { + $type = new NullType(); + } else { throw new UnsupportedParamTypeInConfigException($paramPosition, $paramName, gettype($paramValue)); } - return new $class($paramPosition, $paramName, $paramValue); + return new $class($paramPosition, $paramName, $type); } } diff --git a/src/Params/Param.php b/src/Params/Param.php index 18d39b2..05ffda8 100644 --- a/src/Params/Param.php +++ b/src/Params/Param.php @@ -21,9 +21,6 @@ public function getPosition(): ?int; public function getName(): ?string; - /** - * @return int|bool|string|null - */ - public function getValue(); + public function getType(): Type; } diff --git a/src/Params/ParamValue.php b/src/Params/ParamValue.php index 35b8e0d..9863349 100644 --- a/src/Params/ParamValue.php +++ b/src/Params/ParamValue.php @@ -5,9 +5,6 @@ use PHPStan\Type\Type; -/** - * @template T of int|bool|string|null - */ abstract class ParamValue implements Param { @@ -17,8 +14,8 @@ abstract class ParamValue implements Param /** @var ?string */ private $name; - /** @var T */ - private $value; + /** @var Type */ + private $type; abstract public function matches(Type $type): bool; @@ -27,13 +24,13 @@ abstract public function matches(Type $type): bool; /** * @param int|null $position * @param string|null $name - * @param T $value + * @param Type $type */ - final public function __construct(?int $position, ?string $name, $value) + final public function __construct(?int $position, ?string $name, Type $type) { $this->position = $position; $this->name = $name; - $this->value = $value; + $this->type = $type; } @@ -49,12 +46,9 @@ public function getName(): ?string } - /** - * @return T - */ - public function getValue() + public function getType(): Type { - return $this->value; + return $this->type; } } diff --git a/src/Params/ParamValueAny.php b/src/Params/ParamValueAny.php index df14a93..60a4927 100644 --- a/src/Params/ParamValueAny.php +++ b/src/Params/ParamValueAny.php @@ -5,9 +5,6 @@ use PHPStan\Type\Type; -/** - * @extends ParamValue - */ final class ParamValueAny extends ParamValue { diff --git a/src/Params/ParamValueCaseInsensitiveExcept.php b/src/Params/ParamValueCaseInsensitiveExcept.php index cd812d3..f389690 100644 --- a/src/Params/ParamValueCaseInsensitiveExcept.php +++ b/src/Params/ParamValueCaseInsensitiveExcept.php @@ -3,38 +3,18 @@ namespace Spaze\PHPStan\Rules\Disallowed\Params; +use PHPStan\Type\Constant\ConstantStringType; use PHPStan\Type\Type; -use Spaze\PHPStan\Rules\Disallowed\Exceptions\UnsupportedParamTypeException; -/** - * @extends ParamValue - */ class ParamValueCaseInsensitiveExcept extends ParamValue { - /** - * @throws UnsupportedParamTypeException - */ public function matches(Type $type): bool { - if (!$type->isConstantScalarValue()->yes()) { - throw new UnsupportedParamTypeException(); - } - $values = []; - foreach ($type->getConstantScalarValues() as $value) { - $values[] = $this->getLowercaseValue($value); - } - return !in_array($this->getLowercaseValue($this->getValue()), $values, true); - } - - - /** - * @param mixed $value - * @return mixed - */ - private function getLowercaseValue($value) - { - return is_string($value) ? strtolower($value) : $value; + $fn = function (ConstantStringType $string): string { + return strtolower($string->getValue()); + }; + return array_intersect(array_map($fn, $type->getConstantStrings()), array_map($fn, $this->getType()->getConstantStrings())) === []; } } diff --git a/src/Params/ParamValueExcept.php b/src/Params/ParamValueExcept.php index 9e8603b..c6381f7 100644 --- a/src/Params/ParamValueExcept.php +++ b/src/Params/ParamValueExcept.php @@ -4,23 +4,13 @@ namespace Spaze\PHPStan\Rules\Disallowed\Params; use PHPStan\Type\Type; -use Spaze\PHPStan\Rules\Disallowed\Exceptions\UnsupportedParamTypeException; -/** - * @extends ParamValue - */ class ParamValueExcept extends ParamValue { - /** - * @throws UnsupportedParamTypeException - */ public function matches(Type $type): bool { - if (!$type->isConstantScalarValue()->yes()) { - throw new UnsupportedParamTypeException(); - } - return !in_array($this->getValue(), $type->getConstantScalarValues(), true); + return !$this->getType()->isSuperTypeOf($type)->yes(); } } diff --git a/src/Params/ParamValueExceptAny.php b/src/Params/ParamValueExceptAny.php index 91b443e..634aee4 100644 --- a/src/Params/ParamValueExceptAny.php +++ b/src/Params/ParamValueExceptAny.php @@ -5,9 +5,6 @@ use PHPStan\Type\Type; -/** - * @extends ParamValue - */ final class ParamValueExceptAny extends ParamValue { diff --git a/src/Params/ParamValueFlag.php b/src/Params/ParamValueFlag.php new file mode 100644 index 0000000..d48fda8 --- /dev/null +++ b/src/Params/ParamValueFlag.php @@ -0,0 +1,35 @@ +getType()->getConstantScalarValues() as $value) { + if (!is_int($value)) { + throw new UnsupportedParamTypeInConfigException($this->getPosition(), $this->getName(), gettype($value) . ' of ' . $this->getType()->describe(VerbosityLevel::precise())); + } + if (($value & $type->getValue()) !== 0) { + return true; + } + } + return false; + } + +} diff --git a/src/Params/ParamValueFlagExcept.php b/src/Params/ParamValueFlagExcept.php index 7392a58..6186b1d 100644 --- a/src/Params/ParamValueFlagExcept.php +++ b/src/Params/ParamValueFlagExcept.php @@ -3,25 +3,20 @@ namespace Spaze\PHPStan\Rules\Disallowed\Params; -use PHPStan\Type\Constant\ConstantIntegerType; use PHPStan\Type\Type; use Spaze\PHPStan\Rules\Disallowed\Exceptions\UnsupportedParamTypeException; +use Spaze\PHPStan\Rules\Disallowed\Exceptions\UnsupportedParamTypeInConfigException; -/** - * @extends ParamValue - */ -class ParamValueFlagExcept extends ParamValue +class ParamValueFlagExcept extends ParamValueFlag { /** * @throws UnsupportedParamTypeException + * @throws UnsupportedParamTypeInConfigException */ public function matches(Type $type): bool { - if (!$type instanceof ConstantIntegerType) { - throw new UnsupportedParamTypeException(); - } - return ($this->getValue() & $type->getValue()) === 0; + return !$this->isFlagSet($type); } } diff --git a/src/Params/ParamValueFlagSpecific.php b/src/Params/ParamValueFlagSpecific.php index 40ab077..fcc6817 100644 --- a/src/Params/ParamValueFlagSpecific.php +++ b/src/Params/ParamValueFlagSpecific.php @@ -3,25 +3,20 @@ namespace Spaze\PHPStan\Rules\Disallowed\Params; -use PHPStan\Type\Constant\ConstantIntegerType; use PHPStan\Type\Type; use Spaze\PHPStan\Rules\Disallowed\Exceptions\UnsupportedParamTypeException; +use Spaze\PHPStan\Rules\Disallowed\Exceptions\UnsupportedParamTypeInConfigException; -/** - * @extends ParamValue - */ -class ParamValueFlagSpecific extends ParamValue +class ParamValueFlagSpecific extends ParamValueFlag { /** * @throws UnsupportedParamTypeException + * @throws UnsupportedParamTypeInConfigException */ public function matches(Type $type): bool { - if (!$type instanceof ConstantIntegerType) { - throw new UnsupportedParamTypeException(); - } - return ($this->getValue() & $type->getValue()) !== 0; + return $this->isFlagSet($type); } } diff --git a/src/Params/ParamValueSpecific.php b/src/Params/ParamValueSpecific.php index aebaee1..73b817c 100644 --- a/src/Params/ParamValueSpecific.php +++ b/src/Params/ParamValueSpecific.php @@ -4,23 +4,13 @@ namespace Spaze\PHPStan\Rules\Disallowed\Params; use PHPStan\Type\Type; -use Spaze\PHPStan\Rules\Disallowed\Exceptions\UnsupportedParamTypeException; -/** - * @extends ParamValue - */ class ParamValueSpecific extends ParamValue { - /** - * @throws UnsupportedParamTypeException - */ public function matches(Type $type): bool { - if (!$type->isConstantScalarValue()->yes()) { - throw new UnsupportedParamTypeException(); - } - return $type->getConstantScalarValues() === [$this->getValue()]; + return $this->getType()->isSuperTypeOf($type)->yes(); } } diff --git a/tests/Calls/FunctionCallsTypeStringParamsInvalidFlagsConfigTest.php b/tests/Calls/FunctionCallsTypeStringParamsInvalidFlagsConfigTest.php new file mode 100644 index 0000000..2b2bb93 --- /dev/null +++ b/tests/Calls/FunctionCallsTypeStringParamsInvalidFlagsConfigTest.php @@ -0,0 +1,56 @@ +getByType(DisallowedCallsRuleErrors::class), + $container->getByType(DisallowedCallFactory::class), + $this->createReflectionProvider(), + [ + [ + 'function' => '\Foo\Bar\Waldo\intParam1()', + 'allowParamFlagsAnywhere' => [ + [ + 'position' => 1, + 'typeString' => "2|'bruh'", + ], + ], + ], + ] + ); + } + + + public function testException(): void + { + $this->expectException(UnsupportedParamTypeInConfigException::class); + $this->expectExceptionMessage("Parameter #1 has an unsupported type string of 2|'bruh' specified in configuration"); + $this->analyse([__DIR__ . '/../src/disallowed/functionCallsTypeStringParams.php'], []); + } + + + public static function getAdditionalConfigFiles(): array + { + return [ + __DIR__ . '/../../extension.neon', + ]; + } + +} diff --git a/tests/Calls/FunctionCallsTypeStringParamsTest.php b/tests/Calls/FunctionCallsTypeStringParamsTest.php new file mode 100644 index 0000000..27e0eae --- /dev/null +++ b/tests/Calls/FunctionCallsTypeStringParamsTest.php @@ -0,0 +1,335 @@ +getByType(DisallowedCallsRuleErrors::class), + $container->getByType(DisallowedCallFactory::class), + $this->createReflectionProvider(), + [ + [ + 'function' => '\Foo\Bar\Waldo\config()', + 'allowIn' => [ + __DIR__ . '/../src/disallowed-allow/*.php', + __DIR__ . '/../src/*-allow/*.*', + ], + 'allowParamsInAllowed' => [ + [ + 'position' => 1, + 'value' => 'is ignored when typeString is specified', + 'typeString' => "array{foo:'bar'}", + ], + ], + 'allowParamsAnywhere' => [ + [ + 'position' => 2, + 'value' => 'is ignored when typeString is specified', + 'typeString' => "array{waldo:'baz', pine:'apple', 'orly':array{0, -1}}", + ], + ], + ], + [ + 'function' => '\Foo\Bar\Waldo\foo()', + 'allowIn' => [ + __DIR__ . '/../src/disallowed-allow/*.php', + __DIR__ . '/../src/*-allow/*.*', + ], + 'allowExceptParamsInAllowed' => [ + [ + 'position' => 1, + 'value' => 'is ignored when typeString is specified', + 'typeString' => "'foo'|'pizza'", + ], + ], + ], + [ + 'function' => '\Foo\Bar\Waldo\bar()', + 'allowIn' => [ + __DIR__ . '/../src/disallowed-allow/*.php', + __DIR__ . '/../src/*-allow/*.*', + ], + 'allowExceptParams' => [ + [ + 'position' => 1, + 'value' => 'is ignored when typeString is specified', + 'typeString' => "'bar'|'pub'", + ], + ], + ], + [ + 'function' => '\Foo\Bar\Waldo\baz()', + 'allowIn' => [ + __DIR__ . '/../src/disallowed-allow/*.php', + __DIR__ . '/../src/*-allow/*.*', + ], + 'allowExceptCaseInsensitiveParams' => [ + [ + 'position' => 1, + 'value' => 'is ignored when typeString is specified', + 'typeString' => "'inSensitive'|'Just a Little'", + ], + ], + ], + [ + 'function' => '\Foo\Bar\Waldo\mocky()', + 'allowIn' => [ + __DIR__ . '/../src/disallowed-allow/*.php', + __DIR__ . '/../src/*-allow/*.*', + ], + 'allowParamsAnywhere' => [ + [ + 'position' => 1, + 'value' => 'is ignored when typeString is specified', + 'typeString' => "'moc'|'ky'", + ], + ], + ], + [ + 'function' => '\Foo\Bar\Waldo\arrayParam1()', + 'allowIn' => [ + __DIR__ . '/../src/disallowed-allow/*.php', + __DIR__ . '/../src/*-allow/*.*', + ], + 'disallowParams' => [ + [ + 'position' => 1, + 'name' => 'param', + 'value' => 'is ignored when typeString is specified', + 'typeString' => 'array{}', + ], + ], + ], + [ + 'function' => '\Foo\Bar\Waldo\arrayParam2()', + 'allowParamsAnywhere' => [ + [ + 'position' => 1, + 'name' => 'param', + 'value' => 'is ignored when typeString is specified', + 'typeString' => 'non-empty-array', + ], + ], + ], + [ + 'function' => '\Foo\Bar\Waldo\intParam1()', + 'allowIn' => [ + __DIR__ . '/../src/disallowed-allow/*.php', + __DIR__ . '/../src/*-allow/*.*', + ], + 'allowParamFlagsInAllowed' => [ + [ + 'position' => 1, + 'name' => 'param', + 'value' => 'is ignored when typeString is specified', + 'typeString' => '2', + ], + ], + ], + [ + 'function' => '\Foo\Bar\Waldo\intParam2()', + 'allowIn' => [ + __DIR__ . '/../src/disallowed-allow/*.php', + __DIR__ . '/../src/*-allow/*.*', + ], + 'allowParamFlagsAnywhere' => [ + [ + 'position' => 1, + 'name' => 'param', + 'value' => 'is ignored when typeString is specified', + 'typeString' => '2|8', + ], + ], + ], + [ + 'function' => '\Foo\Bar\Waldo\intParam3()', + 'allowIn' => [ + __DIR__ . '/../src/disallowed-allow/*.php', + __DIR__ . '/../src/*-allow/*.*', + ], + 'allowExceptParamFlagsInAllowed' => [ + [ + 'position' => 1, + 'name' => 'param', + 'value' => 'is ignored when typeString is specified', + 'typeString' => '2|8', + ], + ], + ], + [ + 'function' => '\Foo\Bar\Waldo\intParam4()', + 'allowIn' => [ + __DIR__ . '/../src/disallowed-allow/*.php', + __DIR__ . '/../src/*-allow/*.*', + ], + 'allowExceptParamFlags' => [ + [ + 'position' => 1, + 'name' => 'param', + 'value' => 'is ignored when typeString is specified', + 'typeString' => '2|8', + ], + ], + ], + [ + 'function' => '\Foo\Bar\Waldo\mixedParam1()', + 'allowIn' => [ + __DIR__ . '/../src/disallowed-allow/*.php', + __DIR__ . '/../src/*-allow/*.*', + ], + 'allowExceptParamsInAllowed' => [ + [ + 'position' => 1, + 'name' => 'param', + 'value' => 'is ignored when typeString is specified', + 'typeString' => 'DateTimeInterface', + ], + ], + ], + ] + ); + } + + + public function testRule(): void + { + // Based on the configuration above, in this file: + $this->analyse([__DIR__ . '/../src/disallowed/functionCallsTypeStringParams.php'], [ + [ + // expect this error message: + 'Calling Foo\Bar\Waldo\config() (as config()) is forbidden.', + // on this line: + 6, + ], + [ + 'Calling Foo\Bar\Waldo\config() (as config()) is forbidden.', + 7, + ], + [ + 'Calling Foo\Bar\Waldo\foo() is forbidden.', + 9, + ], + [ + 'Calling Foo\Bar\Waldo\foo() is forbidden.', + 10, + ], + [ + 'Calling Foo\Bar\Waldo\bar() is forbidden.', + 11, + ], + [ + 'Calling Foo\Bar\Waldo\baz() is forbidden.', + 14, + ], + [ + 'Calling Foo\Bar\Waldo\arrayParam1() is forbidden.', + 15, + ], + [ + 'Calling Foo\Bar\Waldo\arrayParam2() is forbidden.', + 17, + ], + [ + 'Calling Foo\Bar\Waldo\mocky() is forbidden.', + 21, + ], + [ + 'Calling Foo\Bar\Waldo\intParam1() is forbidden.', + 22, + ], + [ + 'Calling Foo\Bar\Waldo\intParam1() is forbidden.', + 23, + ], + [ + 'Calling Foo\Bar\Waldo\intParam2() is forbidden.', + 25, + ], + [ + 'Calling Foo\Bar\Waldo\intParam3() is forbidden.', + 26, + ], + [ + 'Calling Foo\Bar\Waldo\intParam3() is forbidden.', + 27, + ], + [ + 'Calling Foo\Bar\Waldo\intParam4() is forbidden.', + 28, + ], + [ + 'Calling Foo\Bar\Waldo\intParam4() is forbidden.', + 29, + ], + [ + 'Calling Foo\Bar\Waldo\mixedParam1() is forbidden.', + 31, + ], + [ + 'Calling Foo\Bar\Waldo\mixedParam1() is forbidden.', + 32, + ], + [ + 'Calling Foo\Bar\Waldo\mixedParam1() is forbidden.', + 33, + ], + ]); + // Based on the configuration above, no errors in this file: + $this->analyse([__DIR__ . '/../src/disallowed-allow/functionCallsTypeStringParams.php'], [ + [ + // expect this error message: + 'Calling Foo\Bar\Waldo\config() (as config()) is forbidden.', + // on this line: + 6, + ], + [ + 'Calling Foo\Bar\Waldo\foo() is forbidden.', + 9, + ], + [ + 'Calling Foo\Bar\Waldo\arrayParam2() is forbidden.', + 17, + ], + [ + 'Calling Foo\Bar\Waldo\intParam1() is forbidden.', + 23, + ], + [ + 'Calling Foo\Bar\Waldo\intParam3() is forbidden.', + 26, + ], + [ + 'Calling Foo\Bar\Waldo\mixedParam1() is forbidden.', + 31, + ], + [ + 'Calling Foo\Bar\Waldo\mixedParam1() is forbidden.', + 32, + ], + ]); + } + + + public static function getAdditionalConfigFiles(): array + { + return [ + __DIR__ . '/../../extension.neon', + ]; + } + +} diff --git a/tests/src/Functions.php b/tests/src/Functions.php index f782e5c..2dd2def 100644 --- a/tests/src/Functions.php +++ b/tests/src/Functions.php @@ -47,3 +47,31 @@ function mocky(string $className): void function config($key = null, $default = null) { } + +function arrayParam1(array $param): void +{ +} + +function arrayParam2(array $param): void +{ +} + +function intParam1(int $param): void +{ +} + +function intParam2(int $param): void +{ +} + +function intParam3(int $param): void +{ +} + +function intParam4(int $param): void +{ +} + +function mixedParam1($param): void +{ +} diff --git a/tests/src/disallowed-allow/functionCallsTypeStringParams.php b/tests/src/disallowed-allow/functionCallsTypeStringParams.php new file mode 100644 index 0000000..6038b97 --- /dev/null +++ b/tests/src/disallowed-allow/functionCallsTypeStringParams.php @@ -0,0 +1,33 @@ + 'bar']); // allowed +config(['foo' => 'bar'], ['what' => 'ever']); // allowed by path but param #1 must match +\Foo\Bar\Waldo\foo('foo'); // disallowed param value +\Foo\Bar\Waldo\foo('bar'); // allowed by path, allowed param value +\Foo\Bar\Waldo\bar('bar'); // allowed by path +\Foo\Bar\Waldo\bar('baz'); // allowed by path +\Foo\Bar\Waldo\baz('CaSe'); // allowed param +\Foo\Bar\Waldo\baz('iNsEnSiTiVe'); // allowed by path +\Foo\Bar\Waldo\arrayParam1([]); // allowed by path +\Foo\Bar\Waldo\arrayParam1(['foo']); // allowed by path +\Foo\Bar\Waldo\arrayParam2([]); // allowed by path +\Foo\Bar\Waldo\arrayParam2(['bar']); // allowed by path +\Foo\Bar\Waldo\mocky('moc'); // allowed param +\Foo\Bar\Waldo\mocky('ky'); // allowed param +\Foo\Bar\Waldo\mocky('mocky'); // allowed param +\Foo\Bar\Waldo\intParam1(1 | 2); // allowed flag +\Foo\Bar\Waldo\intParam1(1 | 4); // not an allowed flag +\Foo\Bar\Waldo\intParam2(1 | 2); // allowed flag +\Foo\Bar\Waldo\intParam2(1 | 4); // allowed by path +\Foo\Bar\Waldo\intParam3(1 | 2); // disallowed flag +\Foo\Bar\Waldo\intParam3(1 | 4); // not a disallowed flag +\Foo\Bar\Waldo\intParam4(1 | 2); // allowed by path +\Foo\Bar\Waldo\intParam4(8 | 16); // allowed by path +\Foo\Bar\Waldo\intParam4(1 | 4); // allowed by path +\Foo\Bar\Waldo\mixedParam1(new DateTime()); // disallowed param +\Foo\Bar\Waldo\mixedParam1(new DateTimeImmutable()); // disallowed param +\Foo\Bar\Waldo\mixedParam1(new Exception); // not a disallowed param diff --git a/tests/src/disallowed/functionCallsTypeStringParams.php b/tests/src/disallowed/functionCallsTypeStringParams.php new file mode 100644 index 0000000..0cbcd78 --- /dev/null +++ b/tests/src/disallowed/functionCallsTypeStringParams.php @@ -0,0 +1,33 @@ + 'bar']); // disallowed +config(['foo' => 'BAH'], ['waldo' => 'baz', 'pine' => 'apple', 'orly' => [0, -1]]); // param #2 is allowed anywhere, param #1 is irrelevant +\Foo\Bar\Waldo\foo('foo'); // disallowed +\Foo\Bar\Waldo\foo('bar'); // disallowed +\Foo\Bar\Waldo\bar('bar'); // disallowed param +\Foo\Bar\Waldo\bar('baz'); // allowed param +\Foo\Bar\Waldo\baz('CaSe'); // allowed param +\Foo\Bar\Waldo\baz('iNsEnSiTiVe'); // disallowed case-insensitive value +\Foo\Bar\Waldo\arrayParam1([]); // disallowed param +\Foo\Bar\Waldo\arrayParam1(['foo']); // allowed param +\Foo\Bar\Waldo\arrayParam2([]); // disallowed param +\Foo\Bar\Waldo\arrayParam2(['bar']); // allowed param +\Foo\Bar\Waldo\mocky('moc'); // allowed param +\Foo\Bar\Waldo\mocky('ky'); // allowed param +\Foo\Bar\Waldo\mocky('mocky'); // not allowed param +\Foo\Bar\Waldo\intParam1(1 | 2); // disallowed +\Foo\Bar\Waldo\intParam1(1 | 4); // disallowed +\Foo\Bar\Waldo\intParam2(1 | 2); // allowed flag +\Foo\Bar\Waldo\intParam2(1 | 4); // disallowed +\Foo\Bar\Waldo\intParam3(1 | 2); // disallowed +\Foo\Bar\Waldo\intParam3(1 | 4); // disallowed +\Foo\Bar\Waldo\intParam4(1 | 2); // disallowed flag +\Foo\Bar\Waldo\intParam4(8 | 16); // disallowed flag +\Foo\Bar\Waldo\intParam4(1 | 4); // not a disallowed flag +\Foo\Bar\Waldo\mixedParam1(new DateTime()); // disallowed +\Foo\Bar\Waldo\mixedParam1(new DateTimeImmutable()); // disallowed +\Foo\Bar\Waldo\mixedParam1(new Exception); // disallowed