diff --git a/src/Infer/Handler/ConcatHandler.php b/src/Infer/Handler/ConcatHandler.php new file mode 100644 index 00000000..73094b61 --- /dev/null +++ b/src/Infer/Handler/ConcatHandler.php @@ -0,0 +1,29 @@ +setType( + $node, + new ConcatenatedStringType(TypeHelper::flattenStringConcatTypes([ + $scope->getType($node->left), + $scope->getType($node->right), + ])), + ); + } +} diff --git a/src/Infer/TypeInferer.php b/src/Infer/TypeInferer.php index e0a442bb..1cd84902 100644 --- a/src/Infer/TypeInferer.php +++ b/src/Infer/TypeInferer.php @@ -9,6 +9,7 @@ use Dedoc\Scramble\Infer\Handler\ArrayItemHandler; use Dedoc\Scramble\Infer\Handler\AssignHandler; use Dedoc\Scramble\Infer\Handler\ClassHandler; +use Dedoc\Scramble\Infer\Handler\ConcatHandler; use Dedoc\Scramble\Infer\Handler\CreatesScope; use Dedoc\Scramble\Infer\Handler\ExceptionInferringExtensions; use Dedoc\Scramble\Infer\Handler\ExpressionTypeInferringExtensions; @@ -41,6 +42,7 @@ public function __construct( $this->handlers = [ new FunctionLikeHandler(), new AssignHandler(), + new ConcatHandler(), new ClassHandler(), new PropertyHandler(), new ArrayHandler(), diff --git a/src/Support/OperationExtensions/RulesExtractor/ValidateCallExtractor.php b/src/Support/OperationExtensions/RulesExtractor/ValidateCallExtractor.php index 99bafaf2..7bda6e7e 100644 --- a/src/Support/OperationExtensions/RulesExtractor/ValidateCallExtractor.php +++ b/src/Support/OperationExtensions/RulesExtractor/ValidateCallExtractor.php @@ -2,12 +2,8 @@ namespace Dedoc\Scramble\Support\OperationExtensions\RulesExtractor; -use Dedoc\Scramble\Infer; -use Dedoc\Scramble\Infer\Scope\GlobalScope; -use Dedoc\Scramble\Infer\Scope\Scope; use Dedoc\Scramble\Support\RouteInfo; use Illuminate\Http\Request; -use Illuminate\Routing\Route; use PhpParser\Node; use PhpParser\NodeFinder; use PhpParser\PrettyPrinter\Standard; @@ -85,6 +81,9 @@ public function extract(RouteInfo $routeInfo) if ($validationRules) { $type = $routeInfo->getMethodScopeTypeResolver()->getType($validationRules); +// dump([ +// $routeInfo->className().'@'.$routeInfo->methodName() => $type->toString(), +// ]); } if ($validationRules) { diff --git a/src/Support/Type/ConcatenatedStringType.php b/src/Support/Type/ConcatenatedStringType.php new file mode 100644 index 00000000..af976bae --- /dev/null +++ b/src/Support/Type/ConcatenatedStringType.php @@ -0,0 +1,28 @@ + $p->toString(), $this->parts)).')'; + } +} diff --git a/src/Support/Type/TypeHelper.php b/src/Support/Type/TypeHelper.php index 686481d5..0f23922e 100644 --- a/src/Support/Type/TypeHelper.php +++ b/src/Support/Type/TypeHelper.php @@ -146,4 +146,15 @@ public static function createTypeFromValue(mixed $value) return null; // @todo: object } + + /** + * @param Type[] $parts + */ + public static function flattenStringConcatTypes(array $parts): array + { + return collect($parts) + ->flatMap(fn ($t) => $t instanceof ConcatenatedStringType ? $t->parts : [$t]) + ->values() + ->all(); + } } diff --git a/tests/Infer/Scope/ScopeTest.php b/tests/Infer/Scope/ScopeTest.php index 64c47c8d..d1d24c31 100644 --- a/tests/Infer/Scope/ScopeTest.php +++ b/tests/Infer/Scope/ScopeTest.php @@ -11,3 +11,14 @@ function getStatementTypeForScopeTest(string $statement, array $extensions = []) ['$foo->bar', 'unknown'], ['$foo->bar->{"baz"}', 'unknown'], ]); + +it('infers concat string type', function ($code, $expectedTypeString) { + expect(getStatementTypeForScopeTest($code)->toString())->toBe($expectedTypeString); +})->with([ + ['"a"."b"."c"', 'string(string(a), string(b), string(c))'], +]); +it('infers concat string type with unknowns', function ($code, $expectedTypeString) { + expect(getStatementTypeForScopeTest($code)->toString())->toBe($expectedTypeString); +})->with([ + ['"a"."b".auth()->user()->id', 'string(string(a), string(b), unknown)'], +]);