diff --git a/README.md b/README.md
index 22506a4..62ee377 100644
--- a/README.md
+++ b/README.md
@@ -40,12 +40,12 @@ includes:
- vendor/struggle-for-php/sfp-phpstan-psr-log/extension.neon
```
-and, set parameters `enableLogLevelMethodRule` and `enableContextTypeRule`
+and, set parameters `enableLogMethodLevelRule` and `enableContextTypeRule`
```neon
parameters:
sfpPsrLog:
- enableLogLevelMethodRule: true # default:false
+ enableLogMethodLevelRule: true # default:false
enableContextTypeRule: true # default:false
```
diff --git a/composer-require-checker.json b/composer-require-checker.json
index c4317ba..b5f420a 100644
--- a/composer-require-checker.json
+++ b/composer-require-checker.json
@@ -25,6 +25,7 @@
"PHPStan\\Type\\FloatType",
"PHPStan\\Type\\IntegerType",
"PHPStan\\Type\\StringType",
- "PHPStan\\Type\\TypeCombinator"
+ "PHPStan\\Type\\TypeCombinator",
+ "PHPStan\\Reflection\\ClassReflection"
]
}
diff --git a/example/phpstan.default.neon b/example/phpstan.default.neon
index afd9d55..b4b54f9 100644
--- a/example/phpstan.default.neon
+++ b/example/phpstan.default.neon
@@ -4,7 +4,7 @@
# docker run -v $(realpath .):/github/workspace -w=/github/workspace ghcr.io/laminas/laminas-continuous-integration:1 \
# '{"php":"8.2","dependencies":"latest","extensions":[],"ini":["memory_limit=-1"],"command":"./vendor/bin/phpstan analyse -c ./example/phpstan.default.neon --no-progress --error-format=junit | xmllint --format -"}'
# docker run -v $(realpath .):/github/workspace -w=/github/workspace ghcr.io/laminas/laminas-continuous-integration:1 \
-# '{"php":"8.2","dependencies":"latest","extensions":[],"ini":["memory_limit=-1"],"command":"diff <(./vendor/bin/phpstan analyse -c ./example/phpstan.default.neon --no-progress --error-format=junit | xmllint --format -) ./test/example.output"}'
+# '{"php":"8.2","dependencies":"latest","extensions":[],"ini":["memory_limit=-1"],"command":"diff <(./vendor/bin/phpstan analyse -c ./example/phpstan.default.neon --no-progress --error-format=junit | xmllint --format -) ./test/example.default.output"}'
parameters:
level: 5
diff --git a/example/phpstan.enableContextTypeRule.neon b/example/phpstan.enableContextTypeRule.neon
index 886ea3c..8e2e1fe 100644
--- a/example/phpstan.enableContextTypeRule.neon
+++ b/example/phpstan.enableContextTypeRule.neon
@@ -4,7 +4,7 @@
# docker run -v $(realpath .):/github/workspace -w=/github/workspace ghcr.io/laminas/laminas-continuous-integration:1 \
# '{"php":"8.2","dependencies":"latest","extensions":[],"ini":["memory_limit=-1"],"command":"./vendor/bin/phpstan analyse -c ./example/phpstan.enableContextTypeRule.neon --no-progress --error-format=junit | xmllint --format -"}'
# docker run -v $(realpath .):/github/workspace -w=/github/workspace ghcr.io/laminas/laminas-continuous-integration:1 \
-# '{"php":"8.2","dependencies":"latest","extensions":[],"ini":["memory_limit=-1"],"command":"diff <(./vendor/bin/phpstan analyse -c ./example/phpstan.enableContextTypeRule.neon --no-progress --error-format=junit | xmllint --format -) ./test/example.output"}'
+# '{"php":"8.2","dependencies":"latest","extensions":[],"ini":["memory_limit=-1"],"command":"diff <(./vendor/bin/phpstan analyse -c ./example/phpstan.enableContextTypeRule.neon --no-progress --error-format=junit | xmllint --format -) ./test/example.enableContextTypeRule.output"}'
parameters:
level: 5
@@ -13,7 +13,7 @@ parameters:
paths:
- %currentWorkingDirectory%/example/src
sfpPsrLog:
- enableLogLevelMethodRule: true
+ enableLogMethodLevelRule: true
enableContextTypeRule: true
# services:
diff --git a/example/phpstan.recommend.neon b/example/phpstan.recommend.neon
index 26c380c..f0e8506 100644
--- a/example/phpstan.recommend.neon
+++ b/example/phpstan.recommend.neon
@@ -4,7 +4,7 @@
# docker run -v $(realpath .):/github/workspace -w=/github/workspace ghcr.io/laminas/laminas-continuous-integration:1 \
# '{"php":"8.2","dependencies":"latest","extensions":[],"ini":["memory_limit=-1"],"command":"./vendor/bin/phpstan analyse -c ./example/phpstan.recommend.neon --no-progress --error-format=junit | xmllint --format -"}'
# docker run -v $(realpath .):/github/workspace -w=/github/workspace ghcr.io/laminas/laminas-continuous-integration:1 \
-# '{"php":"8.2","dependencies":"latest","extensions":[],"ini":["memory_limit=-1"],"command":"diff <(./vendor/bin/phpstan analyse -c ./example/phpstan.recommend.neon --no-progress --error-format=junit | xmllint --format -) ./test/example.output"}'
+# '{"php":"8.2","dependencies":"latest","extensions":[],"ini":["memory_limit=-1"],"command":"diff <(./vendor/bin/phpstan analyse -c ./example/phpstan.recommend.neon --no-progress --error-format=junit | xmllint --format -) ./test/example.recommend.output"}'
parameters:
level: 5
diff --git a/phpcs.xml b/phpcs.xml
index 8712e33..f299cc5 100644
--- a/phpcs.xml
+++ b/phpcs.xml
@@ -3,26 +3,28 @@
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="./vendor/squizlabs/php_codesniffer/phpcs.xsd">
-
-
-
-
-
+
+
+
+
+
-
-
+
+
-
- src
- test
+
+ src
+ test
- test/TypeProvider/data/*
+ test/TypeProvider/data/*
-
-
+
+
+
+
-
-
-
-
+
+
+
+
diff --git a/rules.neon b/rules.neon
index 24c0a71..4a28263 100644
--- a/rules.neon
+++ b/rules.neon
@@ -4,7 +4,7 @@ parametersSchema:
enableMessageStaticStringRule: bool(),
reportContextExceptionLogLevel: schema(string(), nullable()),
contextKeyOriginalPattern: schema(string(), nullable()),
- enableLogLevelMethodRule: bool()
+ enableLogMethodLevelRule: bool()
enableContextTypeRule: bool()
])
@@ -14,7 +14,7 @@ parameters:
enableMessageStaticStringRule: true
reportContextExceptionLogLevel: 'debug'
contextKeyOriginalPattern: null
- enableLogLevelMethodRule: false
+ enableLogMethodLevelRule: false
enableContextTypeRule: false
conditionalTags:
@@ -23,7 +23,7 @@ conditionalTags:
Sfp\PHPStan\Psr\Log\Rules\MessageStaticStringRule:
phpstan.rules.rule: %sfpPsrLog.enableMessageStaticStringRule%
Sfp\PHPStan\Psr\Log\Rules\LogMethodLevelRule:
- phpstan.rules.rule: %sfpPsrLog.enableLogLevelMethodRule%
+ phpstan.rules.rule: %sfpPsrLog.enableLogMethodLevelRule%
Sfp\PHPStan\Psr\Log\Rules\ContextTypeRule:
phpstan.rules.rule: %sfpPsrLog.enableContextTypeRule%
@@ -51,20 +51,25 @@ services:
-
class: Sfp\PHPStan\Psr\Log\Rules\ContextTypeRule
- arguments:
- contextTypeProvider: @sfpPsrLogContextTypeProvider
- sfpPsrLogContextTypeProvider :
- class: Sfp\PHPStan\Psr\Log\TypeProvider\Psr3ContextTypeProvider
- arguments:
- exceptionClass: '\Throwable'
+ # -
+ # class: Sfp\PHPStan\Psr\Log\Rules\ContextTypeRule
+ # arguments:
+ # contextTypeProviderResolver: @contextTypeProviderResolver
+
- # # BigQuery
+ # contextTypeProviderResolver:
+
+ # psrLogContextTypeProvider :
+ # class: Sfp\PHPStan\Psr\Log\TypeProvider\Psr3ContextTypeProvider
+ # arguments:
+ # exceptionClass: '\Exception'
+
+ # BigQuery
#
- # sfpPsrLogContextTypeProvider :
+ # psrLogContextTypeProvider :
# class: Sfp\PHPStan\Psr\Log\TypeProvider\BigQueryContextTypeProvider
# arguments:
# schemaFile:
-
# -
# class: Sfp\PHPStan\Psr\Log\TypeMapping\BigQuery\GenericTableFieldSchemaJsonPayloadTypeMapper
diff --git a/src/Rules/ContextTypeRule.php b/src/Rules/ContextTypeRule.php
index 26223b3..6f629fd 100644
--- a/src/Rules/ContextTypeRule.php
+++ b/src/Rules/ContextTypeRule.php
@@ -10,8 +10,9 @@
use PHPStan\Rules\RuleErrorBuilder;
use PHPStan\ShouldNotHappenException;
use PHPStan\Type\ObjectType;
-use Sfp\PHPStan\Psr\Log\TypeProvider\ContextTypeProviderInterface;
use Sfp\PHPStan\Psr\Log\TypeProvider\Psr3ContextTypeProvider;
+use Sfp\PHPStan\Psr\Log\TypeProviderResolver\AnyScopeContextTypeProviderResolver;
+use Sfp\PHPStan\Psr\Log\TypeProviderResolver\ContextTypeProviderResolverInterface;
use function count;
use function in_array;
@@ -22,12 +23,12 @@
*/
final class ContextTypeRule implements Rule
{
- /** @var ContextTypeProviderInterface */
- private $contextTypeProvider;
+ /** @var ContextTypeProviderResolverInterface */
+ private $contextTypeProviderResolver;
- public function __construct(?ContextTypeProviderInterface $contextTypeProvider = null)
+ public function __construct(?ContextTypeProviderResolverInterface $contextTypeProviderResolver)
{
- $this->contextTypeProvider = $contextTypeProvider ?? new Psr3ContextTypeProvider();
+ $this->contextTypeProviderResolver = $contextTypeProviderResolver ?? new AnyScopeContextTypeProviderResolver(new Psr3ContextTypeProvider());
}
public function getNodeType(): string
@@ -62,22 +63,6 @@ public function processNode(Node $node, Scope $scope): array
$contextArgumentNo = 1;
if ($methodName === 'log') {
- if (count($args) < 2) {
- return [];
- }
-
- $logLevelType = $scope->getType($args[0]->value);
-
- $logLevels = [];
- foreach ($logLevelType->getConstantStrings() as $constantString) {
- $logLevels[] = $constantString->getValue();
- }
-
- if (count($logLevels) === 0) {
- // cant find logLevels
- return [];
- }
-
$contextArgumentNo = 2;
} elseif (! in_array($methodName, LogLevelListInterface::LOGGER_LEVEL_METHODS, true)) {
return [];
@@ -87,8 +72,9 @@ public function processNode(Node $node, Scope $scope): array
return [];
}
- $expectedContextType = $this->contextTypeProvider->getType();
- $argContextType = $scope->getType($args[$contextArgumentNo]->value);
+ $argContextType = $scope->getType($args[$contextArgumentNo]->value);
+
+ $expectedContextType = $this->contextTypeProviderResolver->resolveContextTypeProvider($scope, $argContextType)->getType();
$ret = $expectedContextType->accepts($argContextType, true);
diff --git a/src/Rules/LogMethodLevelRule.php b/src/Rules/LogMethodLevelRule.php
index 1543ef8..3d87623 100644
--- a/src/Rules/LogMethodLevelRule.php
+++ b/src/Rules/LogMethodLevelRule.php
@@ -12,7 +12,6 @@
use PHPStan\Type\ObjectType;
use function count;
-use function implode;
use function in_array;
use function sprintf;
@@ -22,7 +21,7 @@
final class LogMethodLevelRule implements Rule
{
private const ERROR_INVALID_LEVEL = <<<'MESSAGE'
-Parameter #1 $level of method Psr\Log\LoggerInterface::log() expects 'alert'|'critical'|'debug'|'emergency'|'error'|'info'|'notice'|'warning', '%s' given.
+Parameter #1 $level of method Psr\Log\LoggerInterface::log() expects 'alert'|'critical'|'debug'|'emergency'|'error'|'info'|'notice'|'warning', %s given.
MESSAGE;
public function getNodeType(): string
@@ -67,12 +66,9 @@ public function processNode(Node $node, Scope $scope): array
}
if (count($logLevels) === 0) {
- // cant find logLevels
return [
RuleErrorBuilder::message(
- <<<'MESSAGE'
-Parameter #1 $level of method Psr\Log\LoggerInterface::log() expects 'alert'|'critical'|'debug'|'emergency'|'error'|'info'|'notice'|'warning'.
-MESSAGE
+ sprintf(self::ERROR_INVALID_LEVEL, $logLevelType->toPhpDocNode()->__toString())
)->identifier('sfpPsrLog.logMethodLevel')->build(),
];
}
@@ -90,7 +86,7 @@ public function processNode(Node $node, Scope $scope): array
return [
RuleErrorBuilder::message(
- sprintf(self::ERROR_INVALID_LEVEL, implode(', ', $invalidLogLevels))
+ sprintf(self::ERROR_INVALID_LEVEL, $logLevelType->toPhpDocNode()->__toString())
)->identifier('sfpPsrLog.logMethodLevel')->build(),
];
}
diff --git a/src/TypeMapping/BigQuery/TableFieldSchemaJsonPayloadTypeMapperInterface.php b/src/TypeMapping/BigQuery/TableFieldSchemaJsonPayloadTypeMapperInterface.php
index f23f848..745ff5a 100644
--- a/src/TypeMapping/BigQuery/TableFieldSchemaJsonPayloadTypeMapperInterface.php
+++ b/src/TypeMapping/BigQuery/TableFieldSchemaJsonPayloadTypeMapperInterface.php
@@ -9,6 +9,7 @@
/**
* @see https://cloud.google.com/bigquery/docs/reference/rest/v2/tables?hl=en#TableFieldSchema
*
+ * @api
* @phpstan-type non_record_field_type 'STRING'|'BYTES'|'INTEGER'|'INT64'|'FLOAT'|'FLOAT64'|'BOOLEAN'|'BOOL'|'TIMESTAMP'|'DATE'|'TIME'|'DATETIME'|'GEOGRAPHY'|'NUMERIC'|'BIGNUMERIC'|'JSON'|'RANGE'
* @phpstan-type field_type non_record_field_type|'RECORD'|'STRUCT'
* @phpstan-type schema_item_minimal array{name: string, type: field_type}
diff --git a/src/TypeProvider/ContextTypeProviderInterface.php b/src/TypeProvider/ContextTypeProviderInterface.php
index 3765b97..e779dd2 100644
--- a/src/TypeProvider/ContextTypeProviderInterface.php
+++ b/src/TypeProvider/ContextTypeProviderInterface.php
@@ -6,6 +6,9 @@
use PHPStan\Type\Type;
+/**
+ * @api
+ */
interface ContextTypeProviderInterface
{
public function getType(): Type;
diff --git a/src/TypeProviderResolver/AnyScopeContextTypeProviderResolver.php b/src/TypeProviderResolver/AnyScopeContextTypeProviderResolver.php
new file mode 100644
index 0000000..7f01f27
--- /dev/null
+++ b/src/TypeProviderResolver/AnyScopeContextTypeProviderResolver.php
@@ -0,0 +1,24 @@
+contextTypeProvider = $contextTypeProvider;
+ }
+
+ public function resolveContextTypeProvider(Scope $scope, Type $contextType): ContextTypeProviderInterface
+ {
+ return $this->contextTypeProvider;
+ }
+}
diff --git a/src/TypeProviderResolver/ContextTypeProviderResolverInterface.php b/src/TypeProviderResolver/ContextTypeProviderResolverInterface.php
new file mode 100644
index 0000000..7549c15
--- /dev/null
+++ b/src/TypeProviderResolver/ContextTypeProviderResolverInterface.php
@@ -0,0 +1,14 @@
+ */
+ private $layerSet;
+
+ /** @var AnyScopeContextTypeProviderResolver */
+ private $anyScopeContextTypeProviderResolver;
+
+ /** @var bool */
+ private $fallbackAnyScope;
+
+ /**
+ * @phpstan-param array $layerSet
+ */
+ public function __construct(array $layerSet, bool $fallbackAnyScope = true)
+ {
+ $this->layerSet = $layerSet;
+ $this->fallbackAnyScope = $fallbackAnyScope;
+ $this->anyScopeContextTypeProviderResolver = new AnyScopeContextTypeProviderResolver(new Psr3ContextTypeProvider());
+ }
+
+ public function resolveContextTypeProvider(Scope $scope, Type $contextType): ContextTypeProviderInterface
+ {
+ $classReflection = $scope->getClassReflection();
+ if (! $classReflection instanceof ClassReflection) {
+ if ($this->fallbackAnyScope) {
+ return $this->anyScopeContextTypeProviderResolver->resolveContextTypeProvider($scope, $contextType);
+ }
+ throw new LogicException('can not find belongs to ');
+ }
+
+ foreach ($this->layerSet as $interface => $contextTypeProvider) {
+ if ($classReflection->implementsInterface($interface)) {
+ return $contextTypeProvider;
+ }
+ }
+
+ if (! $this->fallbackAnyScope) {
+ throw new LogicException('can not find belongs to ');
+ }
+
+ return $this->anyScopeContextTypeProviderResolver->resolveContextTypeProvider($scope, $contextType);
+ }
+}
diff --git a/test/Rules/ContextTypeRuleTest.php b/test/Rules/ContextTypeRuleTest.php
index d442562..af5f74e 100644
--- a/test/Rules/ContextTypeRuleTest.php
+++ b/test/Rules/ContextTypeRuleTest.php
@@ -9,7 +9,8 @@
use Sfp\PHPStan\Psr\Log\Rules\ContextTypeRule;
use Sfp\PHPStan\Psr\Log\TypeMapping\BigQuery\GenericTableFieldSchemaJsonPayloadTypeMapper;
use Sfp\PHPStan\Psr\Log\TypeProvider\BigQueryContextTypeProvider;
-use Sfp\PHPStan\Psr\Log\TypeProvider\ContextTypeProviderInterface;
+use Sfp\PHPStan\Psr\Log\TypeProviderResolver\AnyScopeContextTypeProviderResolver;
+use Sfp\PHPStan\Psr\Log\TypeProviderResolver\ContextTypeProviderResolverInterface;
use function sprintf;
@@ -19,12 +20,12 @@
*/
final class ContextTypeRuleTest extends RuleTestCase
{
- /** @var null|ContextTypeProviderInterface */
- private $contextTypeProvider;
+ /** @var null|ContextTypeProviderResolverInterface */
+ private $contextTypeProviderResolver;
protected function getRule(): Rule
{
- return new ContextTypeRule($this->contextTypeProvider);
+ return new ContextTypeRule($this->contextTypeProviderResolver);
}
/**
@@ -32,7 +33,7 @@ protected function getRule(): Rule
*/
public function testProcessNode(): void
{
- $this->contextTypeProvider = null;
+ $this->contextTypeProviderResolver = null;
$this->analyse([__DIR__ . '/data/contextType.php'], [
[
sprintf(
@@ -49,7 +50,8 @@ public function testProcessNode(): void
*/
public function testProcessNodeWithBigQueryContextTypeProvider(): void
{
- $this->contextTypeProvider = new BigQueryContextTypeProvider(__DIR__ . '/../TypeProvider/data/bigQuerySchema.json', new GenericTableFieldSchemaJsonPayloadTypeMapper());
+ $contextTypeProvider = new BigQueryContextTypeProvider(__DIR__ . '/../TypeProvider/data/bigQuerySchema.json', new GenericTableFieldSchemaJsonPayloadTypeMapper());
+ $this->contextTypeProviderResolver = new AnyScopeContextTypeProviderResolver($contextTypeProvider);
$this->analyse([__DIR__ . '/data/contextType.php'], [
[
sprintf(
diff --git a/test/Rules/LogMethodLevelRuleTest.php b/test/Rules/LogMethodLevelRuleTest.php
index f7bc4a7..6ca060b 100644
--- a/test/Rules/LogMethodLevelRuleTest.php
+++ b/test/Rules/LogMethodLevelRuleTest.php
@@ -27,13 +27,17 @@ public function testLogMethodLevel(): void
23,
],
[
- "Parameter #1 \$level of method Psr\Log\LoggerInterface::log() expects 'alert'|'critical'|'debug'|'emergency'|'error'|'info'|'notice'|'warning'.",
+ "Parameter #1 \$level of method Psr\Log\LoggerInterface::log() expects 'alert'|'critical'|'debug'|'emergency'|'error'|'info'|'notice'|'warning', string given.",
24,
],
[
- "Parameter #1 \$level of method Psr\Log\LoggerInterface::log() expects 'alert'|'critical'|'debug'|'emergency'|'error'|'info'|'notice'|'warning', 'foo, panic' given.",
+ "Parameter #1 \$level of method Psr\Log\LoggerInterface::log() expects 'alert'|'critical'|'debug'|'emergency'|'error'|'info'|'notice'|'warning', ('foo' | 'info' | 'panic') given.",
25,
],
+ [
+ "Parameter #1 \$level of method Psr\Log\LoggerInterface::log() expects 'alert'|'critical'|'debug'|'emergency'|'error'|'info'|'notice'|'warning', 100 given.",
+ 26,
+ ],
]);
}
}
diff --git a/test/Rules/data/logMethodLevel.php b/test/Rules/data/logMethodLevel.php
index d398327..4bfe377 100644
--- a/test/Rules/data/logMethodLevel.php
+++ b/test/Rules/data/logMethodLevel.php
@@ -23,4 +23,5 @@ function main(
$logger->log('panic', 'message');
$logger->log($unknownLevel, 'message');
$logger->log($invalidLevels, 'message');
+ $logger->log(100, 'message');
}
diff --git a/test/example.enableContextTypeRule.output b/test/example.enableContextTypeRule.output
index 5276f50..732f50e 100644
--- a/test/example.enableContextTypeRule.output
+++ b/test/example.enableContextTypeRule.output
@@ -1,7 +1,7 @@
-
+