check services from cached container service file (#7)

* load with $config ContaimerServicefile, parse service<=>class

* fix psalm messages, optimize code

* debug plugin-config take only for symfony-psalm-plugin, some optimizations cache servicemap

* Housekeeping fix psalm

* Update ClassHandler.php

Co-authored-by: Farhad Safarov <farhad.safarov@gmail.com>
This commit is contained in:
patriziawacht 2020-03-09 07:36:04 +01:00 committed by GitHub
parent 4e45a9d033
commit 5e53558c97
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -22,7 +22,12 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
class ClassHandler implements AfterClassLikeAnalysisInterface, AfterMethodCallAnalysisInterface
{
/**
* {@inheritDoc}
* @psalm-var array<string, string>
*/
private static $classServiceMap = [];
/**
* {@inheritdoc}
*/
public static function afterStatementAnalysis(Node\Stmt\ClassLike $stmt, ClassLikeStorage $classlike_storage, StatementsSource $statements_source, Codebase $codebase, array &$file_replacements = [])
{
@ -43,7 +48,7 @@ class ClassHandler implements AfterClassLikeAnalysisInterface, AfterMethodCallAn
}
/**
* {@inheritDoc}
* {@inheritdoc}
*/
public static function afterMethodCallAnalysis(
Expr $expr,
@ -63,6 +68,15 @@ class ClassHandler implements AfterClassLikeAnalysisInterface, AfterMethodCallAn
$className = (string) $expr->args[0]->value->class->getAttribute('resolvedName');
$return_type_candidate = new Union([new TNamedObject($className)]);
}
if (!count(self::$classServiceMap)) {
self::$classServiceMap = self::loadServiceFile($codebase);
}
if ($return_type_candidate && count(self::$classServiceMap) && $expr->args[0]->value instanceof Node\Scalar\String_) {
$serviceName = (string) $expr->args[0]->value->value;
if (isset(self::$classServiceMap[$serviceName])) {
$return_type_candidate = new Union([new TNamedObject((string) self::$classServiceMap[$serviceName])]);
}
}
break;
case 'Symfony\Component\HttpFoundation\Request::getcontent':
if ($return_type_candidate) {
@ -85,4 +99,45 @@ class ClassHandler implements AfterClassLikeAnalysisInterface, AfterMethodCallAn
break;
}
}
/**
* @todo don't check every time if containerXml config is not set.
* @psalm-return array<string, string>
*/
private static function loadServiceFile(Codebase $codebase): array
{
$classServiceMap = [];
if (count($codebase->config->getPluginClasses())) {
foreach ($codebase->config->getPluginClasses() as $pluginClass) {
if ($pluginClass['class'] === str_replace('Handler', 'Plugin', __NAMESPACE__)) {
$simpleXmlConfig = $pluginClass['config'];
}
}
}
if (isset($simpleXmlConfig) && $simpleXmlConfig instanceof \SimpleXMLElement) {
$serviceFilePath = (string) $simpleXmlConfig->containerXml;
if (!file_exists($serviceFilePath)) {
return [];
}
$xml = simplexml_load_file($serviceFilePath);
if (!$xml->services instanceof \SimpleXMLElement) {
return $classServiceMap;
}
$services = $xml->services;
/** @psalm-suppress MixedAssignment */
if (count($services)) {
foreach ($services->service as $serviceObj) {
if (isset($serviceObj) && $serviceObj instanceof \SimpleXMLElement) {
$serviceAttributes = $serviceObj->attributes();
if ($serviceAttributes && isset($serviceAttributes['id']) && isset($serviceAttributes['class'])) {
$classServiceMap[(string) $serviceAttributes['id']] = (string) $serviceAttributes['class'];
}
}
}
}
}
return $classServiceMap;
}
}