DynamicMethodReturnTypeExtension: Array shapes from SQL queries #4395
-
Hi, this evening I played with phpmyadmin/sql-parser + PHPStan, and it's working excellent together, and I could also get easily the types from the database, but ... Questions:
Sorry for these two different topics together in one discussion, but I hoped that it's easier to understand what I try to do in this way. <?php
declare(strict_types=1);
namespace test\PHPStanHelper;
use PhpParser\Node\Expr\MethodCall;
use PHPStan\Analyser\Scope;
use PHPStan\Reflection\MethodReflection;
use PHPStan\Type\ArrayType;
use PHPStan\Type\IntegerType;
use PHPStan\Type\Type;
use PHPStan\Type\TypeCombinator;
final class ViewFactoryReturnTypeExtension implements \PHPStan\Type\DynamicMethodReturnTypeExtension
{
/**
* @return string
*/
public function getClass(): string {
return \ViewFactory::class;
}
/**
* @param \PHPStan\Reflection\MethodReflection $methodReflection
*
* @return bool
*/
public function isMethodSupported(MethodReflection $methodReflection): bool {
return strtolower($methodReflection->getName()) === 'fetchbyfilterarray';
}
/**
* @param \PHPStan\Reflection\MethodReflection $methodReflection
* @param \PhpParser\Node\Expr\MethodCall $methodCall
* @param \PHPStan\Analyser\Scope $scope
*
* @return \PHPStan\Type\Type
*/
public function getTypeFromMethodCall(MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope): Type {
$class = $methodReflection->getDeclaringClass();
$methodName = $methodReflection->getName();
$methodNativReflection = new \ReflectionMethod($class->getName(), $methodName);
$filename = $methodNativReflection->getFileName();
$start_line = $methodNativReflection->getStartLine() - 1;
$end_line = $methodNativReflection->getEndLine();
$length = $end_line - $start_line;
$source = \file((string)$filename);
$code = \implode("", \array_slice((array)$source, $start_line, $length));
$regex = '@(SELECT\s.*\s)FROM@si';
$matches = [];
\preg_match($regex, $code, $matches, \PREG_OFFSET_CAPTURE, 0);
$sqlParser = new \PhpMyAdmin\SqlParser\Parser($matches[1][0]);
$sqlStatement = $sqlParser->statements[0];
$returnKeys = [];
$returnValues = [];
foreach ($sqlStatement->expr ?? [] as $sqlFieldTmp) {
$fieldName = $sqlFieldTmp->alias ?? $sqlFieldTmp->column ?? '';
if (\trim($fieldName) === '') {
continue;
}
$returnKeys[] = new \PHPStan\Type\Constant\ConstantStringType($fieldName);
$returnValues[] = new \PHPStan\Type\MixedType();
}
if (count($returnValues) === 0) {
$returnTypes[] = new ArrayType(
new \PHPStan\Type\StringType(),
new \PHPStan\Type\MixedType()
);
return new ArrayType(new IntegerType(), TypeCombinator::union(...$returnTypes));
}
return new ArrayType(new IntegerType(), new \PHPStan\Type\Constant\ConstantArrayType($returnKeys, $returnValues));
}
} |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 9 replies
-
You should use the power of the AST. MethodReflection will tell you the declaring class, which in turn will tell you the file name. You should then parse the file with
I don't get it, are you asking about array shapes? https://phpstan.org/writing-php-code/phpdoc-types#array-shapes |
Beta Was this translation helpful? Give feedback.
You should use the power of the AST. MethodReflection will tell you the declaring class, which in turn will tell you the file name. You should then parse the file with
PHPStan\Parser\Parser
(ask for it in the constructor) and traverse the AST with PHP-Parser's NodeTraverser.I don't get it, are you asking about array shapes? https://phpstan.org/writing-php-code/phpdoc-types#array-shapes