psalm / psalm-plugin-symfony Goto Github PK
View Code? Open in Web Editor NEWPsalm Plugin for Symfony
License: MIT License
Psalm Plugin for Symfony
License: MIT License
Hi there,
Getting the following notice on master:
PHP Notice: Undefined index: resolvedName in /.../vendor/psalm/plugin-symfony/src/Handler/ContainerDependencyHandler.php on line 29
Thanks!
Hey team, what's the aim for this project? I see at the moment it's one stub there.
I've built a small collection (just 4, but still) of stubs here https://github.com/zerkms/symfony-psalm-plugin/tree/master/stubs
I'd rather contribute to other project than maintain a half-dead mine.
Hence a question: would my stubs be welcome if I PR them here?
And if "yes" - have you already thought how to version stubs from different symfony versions?
Copied from vimeo/psalm#3196 (opened by @tjveldhuizen)
I've made a reproducer which makes Psalm crash while analyzing the code.
$ git clone [email protected]:tjveldhuizen/psalm-crash-reproducer.git
$ cd psalm-crash-reproducer/
$ composer install
$ bin/phpunit
$ vendor/bin/psalm
Output with --debug: psalm-crash-reproducer.txt
tjv@PC:~/php-projects/temp/psalm-crash-reproducer$ php -v
PHP 7.2.29-1+ubuntu18.04.1+deb.sury.org+1 (cli) (built: Mar 20 2020 13:54:39) ( NTS )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.2.0, Copyright (c) 1998-2018 Zend Technologies
with Zend OPcache v7.2.29-1+ubuntu18.04.1+deb.sury.org+1, Copyright (c) 1999-2018, by Zend Technologies
with Xdebug v2.9.3, Copyright (c) 2002-2020, by Derick Rethans
tjv@PC:~/php-projects/temp/psalm-crash-reproducer$
tjv@PC:~/php-projects/temp/psalm-crash-reproducer$ vendor/bin/psalm
Scanning files...
Analyzing files...
Uncaught InvalidArgumentException: Could not get class storage for symfony\component\validator\validator\traceablevalidator in /home/tjv/php-projects/temp/psalm-crash-reproducer/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ClassLikeStorageProvider.php:45
Stack trace:
#0 /home/tjv/php-projects/temp/psalm-crash-reproducer/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/ClassLikes.php(548): Psalm\Internal\Provider\ClassLikeStorageProvider->get('symfony\\compone...')
#1 /home/tjv/php-projects/temp/psalm-crash-reproducer/vendor/vimeo/psalm/src/Psalm/Codebase.php(723): Psalm\Internal\Codebase\ClassLikes->classExtends('symfony\\compone...', 'symfony\\compone...')
#2 /home/tjv/php-projects/temp/psalm-crash-reproducer/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/TypeAnalyzer.php(704): Psalm\Codebase->classExtendsOrImplements('symfony\\compone...', 'symfony\\compone...')
#3 /home/tjv/php-projects/temp/psalm-crash-reproducer/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/TypeAnalyzer.php(903): Psalm\Internal\Analyzer\TypeAnalyzer::isObjectContainedByObject(Object(Psalm\Codebase), Object(Psalm\Type\Atomic\TNamedObject), Object(Psalm\Type\Atomic\TNamedObject), false, Object(Psalm\Internal\Analyzer\TypeComparisonResult))
#4 /home/tjv/php-projects/temp/psalm-crash-reproducer/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/TypeAnalyzer.php(147): Psalm\Internal\Analyzer\TypeAnalyzer::isAtomicContainedBy(Object(Psalm\Codebase), Object(Psalm\Type\Atomic\TNamedObject), Object(Psalm\Type\Atomic\TNamedObject), false, true, Object(Psalm\Internal\Analyzer\TypeComparisonResult))
#5 /home/tjv/php-projects/temp/psalm-crash-reproducer/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Assignment/PropertyAssignmentAnalyzer.php(855): Psalm\Internal\Analyzer\TypeAnalyzer::isContainedBy(Object(Psalm\Codebase), Object(Psalm\Type\Union), Object(Psalm\Type\Union), true, true, Object(Psalm\Internal\Analyzer\TypeComparisonResult))
#6 /home/tjv/php-projects/temp/psalm-crash-reproducer/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/AssignmentAnalyzer.php(730): Psalm\Internal\Analyzer\Statements\Expression\Assignment\PropertyAssignmentAnalyzer::analyzeInstance(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(PhpParser\Node\Expr\PropertyFetch), 'validator', Object(PhpParser\Node\Expr\MethodCall), Object(Psalm\Type\Union), Object(Psalm\Context))
#7 /home/tjv/php-projects/temp/psalm-crash-reproducer/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/ExpressionAnalyzer.php(120): Psalm\Internal\Analyzer\Statements\Expression\AssignmentAnalyzer::analyze(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(PhpParser\Node\Expr\PropertyFetch), Object(PhpParser\Node\Expr\MethodCall), Object(Psalm\Type\Union), Object(Psalm\Context), NULL)
#8 /home/tjv/php-projects/temp/psalm-crash-reproducer/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/StatementsAnalyzer.php(736): Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer::analyze(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(PhpParser\Node\Expr\Assign), Object(Psalm\Context), false, Object(Psalm\Context), true)
#9 /home/tjv/php-projects/temp/psalm-crash-reproducer/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/FunctionLikeAnalyzer.php(541): Psalm\Internal\Analyzer\StatementsAnalyzer->analyze(Array, Object(Psalm\Context), Object(Psalm\Context), true)
#10 /home/tjv/php-projects/temp/psalm-crash-reproducer/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/ClassAnalyzer.php(1750): Psalm\Internal\Analyzer\FunctionLikeAnalyzer->analyze(Object(Psalm\Context), Object(Psalm\Internal\Provider\NodeDataProvider), Object(Psalm\Context))
#11 /home/tjv/php-projects/temp/psalm-crash-reproducer/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/ClassAnalyzer.php(730): Psalm\Internal\Analyzer\ClassAnalyzer->analyzeClassMethod(Object(PhpParser\Node\Stmt\ClassMethod), Object(Psalm\Storage\ClassLikeStorage), Object(Psalm\Internal\Analyzer\ClassAnalyzer), Object(Psalm\Context), Object(Psalm\Context))
#12 /home/tjv/php-projects/temp/psalm-crash-reproducer/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/FileAnalyzer.php(201): Psalm\Internal\Analyzer\ClassAnalyzer->analyze(Object(Psalm\Context), Object(Psalm\Context))
#13 /home/tjv/php-projects/temp/psalm-crash-reproducer/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/Analyzer.php(357): Psalm\Internal\Analyzer\FileAnalyzer->analyze(NULL)
#14 /home/tjv/php-projects/temp/psalm-crash-reproducer/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/Analyzer.php(589): Psalm\Internal\Codebase\Analyzer->Psalm\Internal\Codebase\{closure}(0, '/home/tjv/php-p...')
#15 /home/tjv/php-projects/temp/psalm-crash-reproducer/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/Analyzer.php(267): Psalm\Internal\Codebase\Analyzer->doAnalysis(Object(Psalm\Internal\Analyzer\ProjectAnalyzer), 3)
#16 /home/tjv/php-projects/temp/psalm-crash-reproducer/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/ProjectAnalyzer.php(568): Psalm\Internal\Codebase\Analyzer->analyzeFiles(Object(Psalm\Internal\Analyzer\ProjectAnalyzer), 3, false, true)
#17 /home/tjv/php-projects/temp/psalm-crash-reproducer/vendor/vimeo/psalm/src/psalm.php(588): Psalm\Internal\Analyzer\ProjectAnalyzer->check('/home/tjv/php-p...', false)
#18 /home/tjv/php-projects/temp/psalm-crash-reproducer/vendor/vimeo/psalm/psalm(2): require_once('/home/tjv/php-p...')
#19 {main}
(Psalm 3.11.2@d470903722cfcbc1cd04744c5491d3e6d13ec3d9 crashed due to an uncaught Throwable)
Hi there,
This plugin is causing a large performance penalty which is most obvious on symfony projects with a large amount of services.
This line:
In my particular use case that's waiting 1 minute plus for thousands of services to be re-scanned - for a simple change to one file.
I have discussed this with @muglug on slack briefly, and the suggestion is that Codebase::queueClassLikeForScanning
needs a way to specify that the file to be scanned is not a stub.
Raising here for before that conversation disappears into the ether.
Thanks
Currently when pulling the repo and trying to install its dependencies it fails because the phpunit
version that is used only supports php ^7.1
, the earliest phpunit
version to support php 8
is phpunit 9.3.0
so I propose to update to at least this version if not updating to the latest 9.5.0
entirely.
ERROR: RepositoryStringShortcut - src/AppBundle/Validator/Constraints/UniqueDataValidator.php:34:62 - Use Entity::class syntax instead (see https://psalm.dev/000)
$exists = (bool) $this->entityManager->getRepository($constraint->entityClass)->findOneBy(
Here, code is retrieving class-string value from property of constraint instance, so I don't see how would I change that with ::class notation.
When specifying the mode argument for console-input's, the constants can be combined using the +
operator to have multiple different modes active at the same time. This works because the constants are bit-wise integers and the mode is checked for specific constants by checking if their bit is set in the mode integer:
class InputOption
{
const VALUE_NONE = 1;
const VALUE_REQUIRED = 2;
const VALUE_OPTIONAL = 4;
const VALUE_IS_ARRAY = 8;
...
public function __construct(string $name, $shortcut = null, int $mode = null, string $description = '', $default = null)
{
...
if (null === $mode) {
$mode = self::VALUE_NONE;
} elseif ($mode > 15 || $mode < 1) {
throw new InvalidArgumentException(sprintf('Option mode "%s" is not valid.', $mode));
}
...
public function isValueRequired()
{
return self::VALUE_REQUIRED === (self::VALUE_REQUIRED & $this->mode);
}
...
public function isArray()
{
return self::VALUE_IS_ARRAY === (self::VALUE_IS_ARRAY & $this->mode);
}
Yet this plugin does not recognize these combined constants and only allows one constant to be present at a time:
ERROR: InvalidConsoleOptionValue - src/Brille24/ProductionBundle/Command/CompareManufacturerCatalogsCommand.php:63:13 - Use Symfony\Component\Console\Input\InputOption constants (see https://psalm.dev/000)
InputOption::VALUE_REQUIRED + InputOption::VALUE_IS_ARRAY,
At the moment the only way to deal with these issues is to suppress them using psalm-suppress
.
The following class throws the error: Can not retrieve template name from given expression (Psalm\Type\Atomic\TString)
<?php
namespace App\Templating;
use Twig\Environment;
final class TwigTemplating implements Templating
{
private Environment $twig;
public function __construct(Environment $twig)
{
$this->twig = $twig;
}
/** @param array<array-key, mixed> $data */
public function render(string $template, array $data): string
{
return $this->twig->render($template, $data);
}
}
Stacktrace:
ncaught Exception: Can not retrieve template name from given expression (Psalm\Type\Atomic\TString)
Stack trace in the forked worker:
#0 /Users/myuser/projects/myapp/vendor/psalm/plugin-symfony/src/Twig/TwigUtils.php(20): Psalm\SymfonyPsalmPlugin\Twig\TwigUtils::resolveStringFromExpression(Object(Psalm\Type\Atomic\TString), Object(Psalm\Internal\Analyzer\StatementsAnalyzer))
#1 /Users/myuser/projects/myapp/vendor/psalm/plugin-symfony/src/Twig/AnalyzedTemplatesTainter.php(39): Psalm\SymfonyPsalmPlugin\Twig\TwigUtils::extractTemplateNameFromExpression(Object(PhpParser\Node\Expr\Variable), Object(Psalm\Internal\Analyzer\StatementsAnalyzer))
#2 /Users/myuser/projects/myapp/vendor/vimeo/psalm/src/Psalm/Internal/EventDispatcher.php(254): Psalm\SymfonyPsalmPlugin\Twig\AnalyzedTemplatesTainter::afterMethodCallAnalysis(Object(PhpParser\Node\Expr\MethodCall), 'Twig\\Environmen...', 'Twig\\Environmen...', 'Twig\\Environmen...', Object(Psalm\Context), Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(Psalm\Codebase), Array, Object(Psalm\Type\Union))
#3 /Users/myuser/projects/myapp/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/ExistingAtomicMethodCallAnalyzer.php(392): Psalm\Internal\EventDispatcher->dispatchAfterMethodCallAnalysis(Object(Psalm\Plugin\EventHandler\Event\AfterMethodCallAnalysisEvent))
#4 /Users/myuser/projects/myapp/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/AtomicMethodCallAnalyzer.php(401): Psalm\Internal\Analyzer\Statements\Expression\Call\Method\ExistingAtomicMethodCallAnalyzer::analyze(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(PhpParser\Node\Expr\MethodCall), Object(PhpParser\Node\Identifier), Array, Object(Psalm\Codebase), Object(Psalm\Context), Object(Psalm\Type\Atomic\TNamedObject), Object(Psalm\Type\Atomic\TNamedObject), '$this->twig', Object(Psalm\Internal\MethodIdentifier), Object(Psalm\Internal\Analyzer\Statements\Expression\Call\Method\AtomicMethodCallAnalysisResult))
#5 /Users/myuser/projects/myapp/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/MethodCallAnalyzer.php(184): Psalm\Internal\Analyzer\Statements\Expression\Call\Method\AtomicMethodCallAnalyzer::analyze(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(PhpParser\Node\Expr\MethodCall), Object(Psalm\Codebase), Object(Psalm\Context), Object(Psalm\Type\Atomic\TNamedObject), Object(Psalm\Type\Atomic\TNamedObject), false, '$this->twig', Object(Psalm\Internal\Analyzer\Statements\Expression\Call\Method\AtomicMethodCallAnalysisResult))
#6 /Users/myuser/projects/myapp/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/ExpressionAnalyzer.php(148): Psalm\Internal\Analyzer\Statements\Expression\Call\MethodCallAnalyzer::analyze(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(PhpParser\Node\Expr\MethodCall), Object(Psalm\Context))
#7 /Users/myuser/projects/myapp/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/ExpressionAnalyzer.php(40): Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer::handleExpression(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(PhpParser\Node\Expr\MethodCall), Object(Psalm\Context), false, NULL, false)
#8 /Users/myuser/projects/myapp/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/ReturnAnalyzer.php(141): Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer::analyze(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(PhpParser\Node\Expr\MethodCall), Object(Psalm\Context))
#9 /Users/myuser/projects/myapp/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/StatementsAnalyzer.php(504): Psalm\Internal\Analyzer\Statements\ReturnAnalyzer::analyze(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(PhpParser\Node\Stmt\Return_), Object(Psalm\Context))
#10 /Users/myuser/projects/myapp/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/StatementsAnalyzer.php(172): Psalm\Internal\Analyzer\StatementsAnalyzer::analyzeStatement(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(PhpParser\Node\Stmt\Return_), Object(Psalm\Context), Object(Psalm\Context))
#11 /Users/myuser/projects/myapp/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/FunctionLikeAnalyzer.php(421): Psalm\Internal\Analyzer\StatementsAnalyzer->analyze(Array, Object(Psalm\Context), Object(Psalm\Context), true)
#12 /Users/myuser/projects/myapp/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/ClassAnalyzer.php(1639): Psalm\Internal\Analyzer\FunctionLikeAnalyzer->analyze(Object(Psalm\Context), Object(Psalm\Internal\Provider\NodeDataProvider), Object(Psalm\Context))
#13 /Users/myuser/projects/myapp/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/ClassAnalyzer.php(387): Psalm\Internal\Analyzer\ClassAnalyzer->analyzeClassMethod(Object(PhpParser\Node\Stmt\ClassMethod), Object(Psalm\Storage\ClassLikeStorage), Object(Psalm\Internal\Analyzer\ClassAnalyzer), Object(Psalm\Context), Object(Psalm\Context))
#14 /Users/myuser/projects/myapp/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/FileAnalyzer.php(213): Psalm\Internal\Analyzer\ClassAnalyzer->analyze(Object(Psalm\Context), Object(Psalm\Context))
#15 /Users/myuser/projects/myapp/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/Analyzer.php(340): Psalm\Internal\Analyzer\FileAnalyzer->analyze(NULL)
#16 /Users/myuser/projects/myapp/vendor/vimeo/psalm/src/Psalm/Internal/Fork/Pool.php(193): Psalm\Internal\Codebase\Analyzer->Psalm\Internal\Codebase\{closure}(73, '/Users/myuser/p...')
#17 /Users/myuser/projects/myapp/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/Analyzer.php(406): Psalm\Internal\Fork\Pool->__construct(Array, Object(Closure), Object(Closure), Object(Closure), Object(Closure))
#18 /Users/myuser/projects/myapp/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/Analyzer.php(269): Psalm\Internal\Codebase\Analyzer->doAnalysis(Object(Psalm\Internal\Analyzer\ProjectAnalyzer), 3)
#19 /Users/myuser/projects/myapp/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/ProjectAnalyzer.php(635): Psalm\Internal\Codebase\Analyzer->analyzeFiles(Object(Psalm\Internal\Analyzer\ProjectAnalyzer), 3, false, true)
#20 /Users/myuser/projects/myapp/vendor/vimeo/psalm/src/psalm.php(676): Psalm\Internal\Analyzer\ProjectAnalyzer->check('/Users/myuser/p...', false)
#21 /Users/myuser/projects/myapp/vendor/vimeo/psalm/psalm(2): require_once('/Users/myuser/p...')
#22 {main} in /Users/myuser/projects/myapp/vendor/vimeo/psalm/src/Psalm/Internal/Fork/Pool.php:357
Stack trace:
#0 /Users/myuser/projects/myapp/vendor/vimeo/psalm/src/Psalm/Internal/Fork/Pool.php(389): Psalm\Internal\Fork\Pool->readResultsFromChildren()
#1 /Users/myuser/projects/myapp/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/Analyzer.php(473): Psalm\Internal\Fork\Pool->wait()
#2 /Users/myuser/projects/myapp/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/Analyzer.php(269): Psalm\Internal\Codebase\Analyzer->doAnalysis(Object(Psalm\Internal\Analyzer\ProjectAnalyzer), 3)
#3 /Users/myuser/projects/myapp/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/ProjectAnalyzer.php(635): Psalm\Internal\Codebase\Analyzer->analyzeFiles(Object(Psalm\Internal\Analyzer\ProjectAnalyzer), 3, false, true)
#4 /Users/myuser/projects/myapp/vendor/vimeo/psalm/src/psalm.php(676): Psalm\Internal\Analyzer\ProjectAnalyzer->check('/Users/myuser/p...', false)
#5 /Users/myuser/projects/myapp/vendor/vimeo/psalm/psalm(2): require_once('/Users/myuser/p...')
#6 {main}
(Psalm 4.4.1@9fd7a7d885b3a216cff8dec9d8c21a132f275224 crashed due to an uncaught Throwable)
Plugin keeps crashing on
return $this->container->get('twig')->render('AppBundle:DataProvider/GraduateJobs:job.xml.twig', [...]);
with error
Uncaught RuntimeException: The template AppBundle:DataProvider/GraduateJobs:job.xml.twig was not found. in /var/www/vendor/psalm/plugin-symfony/src/Twig/CachedTemplatesMapping.php:67
Stack trace:
#0 /var/www/vendor/psalm/plugin-symfony/src/Twig/CachedTemplatesTainter.php(58): Psalm\SymfonyPsalmPlugin\Twig\CachedTemplatesMapping::getCacheClassName('AppBundle:DataP...')
#1 /var/www/vendor/vimeo/psalm/src/Psalm/Internal/Provider/MethodReturnTypeProvider.php(105): Psalm\SymfonyPsalmPlugin\Twig\CachedTemplatesTainter::getMethodReturnType(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), 'Twig\\Environmen...', 'render', Array, Object(Psalm\Context), Object(Psalm\CodeLocation), NULL, NULL, NULL)
#2 /var/www/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/MethodCallReturnTypeFetcher.php(53): Psalm\Internal\Provider\MethodReturnTypeProvider->getReturnType(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), 'Twig\\Environmen...', 'render', Array, Object(Psalm\Context), Object(Psalm\CodeLocation), NULL)
#3 /var/www/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/AtomicMethodCallAnalyzer.php(680): Psalm\Internal\Analyzer\Statements\Expression\Call\Method\MethodCallReturnTypeFetcher::fetch(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(Psalm\Codebase), Object(PhpParser\Node\Expr\MethodCall), Object(Psalm\Context), Object(Psalm\Internal\MethodIdentifier), Object(Psalm\Internal\MethodIdentifier), Object(Psalm\Internal\MethodIdentifier), 'Twig\\Environmen...', Object(Psalm\Type\Atomic\TNamedObject), Object(Psalm\Type\Atomic\TNamedObject), Array, Object(Psalm\Internal\Analyzer\Statements\Expression\Call\Method\AtomicMethodCallAnalysisResult), Object(Psalm\Internal\Type\TemplateResult))
#4 /var/www/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/MethodCallAnalyzer.php(168): Psalm\Internal\Analyzer\Statements\Expression\Call\Method\AtomicMethodCallAnalyzer::analyze(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(PhpParser\Node\Expr\MethodCall), Object(Psalm\Codebase), Object(Psalm\Context), Object(Psalm\Type\Atomic\TNamedObject), Object(Psalm\Type\Atomic\TNamedObject), false, NULL, Object(Psalm\Internal\Analyzer\Statements\Expression\Call\Method\AtomicMethodCallAnalysisResult))
#5 /var/www/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/ExpressionAnalyzer.php(144): Psalm\Internal\Analyzer\Statements\Expression\Call\MethodCallAnalyzer::analyze(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(PhpParser\Node\Expr\MethodCall), Object(Psalm\Context))
#6 /var/www/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/ExpressionAnalyzer.php(39): Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer::handleExpression(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(PhpParser\Node\Expr\MethodCall), Object(Psalm\Context), false, NULL, false)
#7 /var/www/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/ReturnAnalyzer.php(135): Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer::analyze(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(PhpParser\Node\Expr\MethodCall), Object(Psalm\Context))
#8 /var/www/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/StatementsAnalyzer.php(457): Psalm\Internal\Analyzer\Statements\ReturnAnalyzer::analyze(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(PhpParser\Node\Stmt\Return_), Object(Psalm\Context))
#9 /var/www/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/StatementsAnalyzer.php(164): Psalm\Internal\Analyzer\StatementsAnalyzer::analyzeStatement(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(PhpParser\Node\Stmt\Return_), Object(Psalm\Context), Object(Psalm\Context))
#10 /var/www/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/FunctionLikeAnalyzer.php(591): Psalm\Internal\Analyzer\StatementsAnalyzer->analyze(Array, Object(Psalm\Context), Object(Psalm\Context), true)
#11 /var/www/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/ClassAnalyzer.php(1897): Psalm\Internal\Analyzer\FunctionLikeAnalyzer->analyze(Object(Psalm\Context), Object(Psalm\Internal\Provider\NodeDataProvider), Object(Psalm\Context))
#12 /var/www/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/ClassAnalyzer.php(734): Psalm\Internal\Analyzer\ClassAnalyzer->analyzeClassMethod(Object(PhpParser\Node\Stmt\ClassMethod), Object(Psalm\Storage\ClassLikeStorage), Object(Psalm\Internal\Analyzer\ClassAnalyzer), Object(Psalm\Context), Object(Psalm\Context))
#13 /var/www/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/FileAnalyzer.php(215): Psalm\Internal\Analyzer\ClassAnalyzer->analyze(Object(Psalm\Context), Object(Psalm\Context))
#14 /var/www/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/Analyzer.php(336): Psalm\Internal\Analyzer\FileAnalyzer->analyze(NULL)
#15 /var/www/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/Analyzer.php(576): Psalm\Internal\Codebase\Analyzer->Psalm\Internal\Codebase\{closure}(227, '/var/www/src/Ap...')
#16 /var/www/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/Analyzer.php(268): Psalm\Internal\Codebase\Analyzer->doAnalysis(Object(Psalm\Internal\Analyzer\ProjectAnalyzer), 1)
#17 /var/www/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/ProjectAnalyzer.php(639): Psalm\Internal\Codebase\Analyzer->analyzeFiles(Object(Psalm\Internal\Analyzer\ProjectAnalyzer), 1, false, true)
#18 /var/www/vendor/vimeo/psalm/src/psalm.php(677): Psalm\Internal\Analyzer\ProjectAnalyzer->check('/var/www/', false)
#19 /var/www/vendor/vimeo/psalm/psalm(2): require_once('/var/www/vendor...')
#20 {main}
(Psalm 3.16@d03e5ef057d6adc656c0ff7e166c50b73b4f48f3 crashed due to an uncaught Throwable)
even though there is such cache file present in var/cache/dev/twig/6c/6c3e07fca810aa921fb8bc653998ccb1b777f9ed15f04263962af77578011498.php
:
<?php
use Twig\Environment;
use Twig\Error\LoaderError;
use Twig\Error\RuntimeError;
use Twig\Extension\SandboxExtension;
use Twig\Markup;
use Twig\Sandbox\SecurityError;
use Twig\Sandbox\SecurityNotAllowedTagError;
use Twig\Sandbox\SecurityNotAllowedFilterError;
use Twig\Sandbox\SecurityNotAllowedFunctionError;
use Twig\Source;
use Twig\Template;
/* AppBundle:DataProvider/GraduateJobs:job.xml.twig */
class __TwigTemplate_943d73236d880c64fb3bff57201b2a5745c3c2ea7949e6cdfc27cd8cd2a37f48 extends \Twig\Template
{
private $source;
private $macros = [];
public function __construct(Environment $env)
{
parent::__construct($env);
$this->source = $this->getSourceContext();
$this->parent = false;
$this->blocks = [
];
}
protected function doDisplay(array $context, array $blocks = [])
{
$macros = $this->macros;
$__internal_085b0142806202599c7fe3b329164a92397d8978207a37e79d70b8c52599e33e = $this->extensions["Symfony\\Bundle\\WebProfilerBundle\\Twig\\WebProfilerExtension"];
$__internal_085b0142806202599c7fe3b329164a92397d8978207a37e79d70b8c52599e33e->enter($__internal_085b0142806202599c7fe3b329164a92397d8978207a37e79d70b8c52599e33e_prof = new \Twig\Profiler\Profile($this->getTemplateName(), "template", "AppBundle:DataProvider/GraduateJobs:job.xml.twig"));
$__internal_319393461309892924ff6e74d6d6e64287df64b63545b994e100d4ab223aed02 = $this->extensions["Symfony\\Bridge\\Twig\\Extension\\ProfilerExtension"];
$__internal_319393461309892924ff6e74d6d6e64287df64b63545b994e100d4ab223aed02->enter($__internal_319393461309892924ff6e74d6d6e64287df64b63545b994e100d4ab223aed02_prof = new \Twig\Profiler\Profile($this->getTemplateName(), "template", "AppBundle:DataProvider/GraduateJobs:job.xml.twig"));
// line 1
echo "<?xml version = '1.0' encoding = 'utf-8' ?>
<!DOCTYPE job PUBLIC 'gj-job-dtd' 'http://www.graduate-jobs.com/xml/job.dtd'>
<job>
<status>";
// line 4
echo twig_escape_filter($this->env, (isset($context["workMode"]) || array_key_exists("workMode", $context) ? $context["workMode"] : (function () { throw new RuntimeError('Variable "workMode" does not exist.', 4, $this->source); })()), "html", null, true);
echo "</status>
<accountName>";
// line 5
echo twig_escape_filter($this->env, (isset($context["accountName"]) || array_key_exists("accountName", $context) ? $context["accountName"] : (function () { throw new RuntimeError('Variable "accountName" does not exist.', 5, $this->source); })()), "html", null, true);
echo "</accountName>
<password>";
// line 6
echo twig_escape_filter($this->env, (isset($context["password"]) || array_key_exists("password", $context) ? $context["password"] : (function () { throw new RuntimeError('Variable "password" does not exist.', 6, $this->source); })()), "html", null, true);
echo "</password>
<title>";
// line 7
echo twig_escape_filter($this->env, (isset($context["title"]) || array_key_exists("title", $context) ? $context["title"] : (function () { throw new RuntimeError('Variable "title" does not exist.', 7, $this->source); })()), "html", null, true);
echo "</title>
<yourRef>";
// line 8
echo twig_escape_filter($this->env, (isset($context["yourRef"]) || array_key_exists("yourRef", $context) ? $context["yourRef"] : (function () { throw new RuntimeError('Variable "yourRef" does not exist.', 8, $this->source); })()), "html", null, true);
echo "</yourRef>
<keyword>";
// line 9
echo twig_escape_filter($this->env, (isset($context["keyword"]) || array_key_exists("keyword", $context) ? $context["keyword"] : (function () { throw new RuntimeError('Variable "keyword" does not exist.', 9, $this->source); })()), "html", null, true);
echo "</keyword>
<detail>";
// line 10
echo twig_escape_filter($this->env, (isset($context["detail"]) || array_key_exists("detail", $context) ? $context["detail"] : (function () { throw new RuntimeError('Variable "detail" does not exist.', 10, $this->source); })()), "html", null, true);
echo "</detail>
";
// line 11
$context['_parent'] = $context;
$context['_seq'] = twig_ensure_traversable((isset($context["locations"]) || array_key_exists("locations", $context) ? $context["locations"] : (function () { throw new RuntimeError('Variable "locations" does not exist.', 11, $this->source); })()));
foreach ($context['_seq'] as $context["_key"] => $context["location"]) {
// line 12
echo " <location>";
echo twig_escape_filter($this->env, $context["location"], "html", null, true);
echo "</location>
";
}
$_parent = $context['_parent'];
unset($context['_seq'], $context['_iterated'], $context['_key'], $context['location'], $context['_parent'], $context['loop']);
$context = array_intersect_key($context, $_parent) + $_parent;
// line 14
echo " ";
if (((isset($context["postcodes"]) || array_key_exists("postcodes", $context) ? $context["postcodes"] : (function () { throw new RuntimeError('Variable "postcodes" does not exist.', 14, $this->source); })()) != "")) {
// line 15
echo " <postcodes>";
echo twig_escape_filter($this->env, (isset($context["postcodes"]) || array_key_exists("postcodes", $context) ? $context["postcodes"] : (function () { throw new RuntimeError('Variable "postcodes" does not exist.', 15, $this->source); })()), "html", null, true);
echo "</postcodes>
";
}
// line 17
echo " <salary>";
echo twig_escape_filter($this->env, (isset($context["salary"]) || array_key_exists("salary", $context) ? $context["salary"] : (function () { throw new RuntimeError('Variable "salary" does not exist.', 17, $this->source); })()), "html", null, true);
echo "</salary>
<displayEndDate>";
// line 18
echo twig_escape_filter($this->env, (isset($context["displayEndDate"]) || array_key_exists("displayEndDate", $context) ? $context["displayEndDate"] : (function () { throw new RuntimeError('Variable "displayEndDate" does not exist.', 18, $this->source); })()), "html", null, true);
echo "</displayEndDate>
";
// line 19
$context['_parent'] = $context;
$context['_seq'] = twig_ensure_traversable((isset($context["sectors"]) || array_key_exists("sectors", $context) ? $context["sectors"] : (function () { throw new RuntimeError('Variable "sectors" does not exist.', 19, $this->source); })()));
foreach ($context['_seq'] as $context["_key"] => $context["sector"]) {
// line 20
echo " <industrySector>";
echo twig_escape_filter($this->env, $context["sector"], "html", null, true);
echo "</industrySector>
";
}
$_parent = $context['_parent'];
unset($context['_seq'], $context['_iterated'], $context['_key'], $context['sector'], $context['_parent'], $context['loop']);
$context = array_intersect_key($context, $_parent) + $_parent;
// line 22
echo " <applyTo>";
echo twig_escape_filter($this->env, (isset($context["applyTo"]) || array_key_exists("applyTo", $context) ? $context["applyTo"] : (function () { throw new RuntimeError('Variable "applyTo" does not exist.', 22, $this->source); })()), "html", null, true);
echo "</applyTo>
";
// line 23
$context['_parent'] = $context;
$context['_seq'] = twig_ensure_traversable((isset($context["sites"]) || array_key_exists("sites", $context) ? $context["sites"] : (function () { throw new RuntimeError('Variable "sites" does not exist.', 23, $this->source); })()));
foreach ($context['_seq'] as $context["_key"] => $context["site"]) {
// line 24
echo " <site>";
echo twig_escape_filter($this->env, $context["site"], "html", null, true);
echo "</site>
";
}
$_parent = $context['_parent'];
unset($context['_seq'], $context['_iterated'], $context['_key'], $context['site'], $context['_parent'], $context['loop']);
$context = array_intersect_key($context, $_parent) + $_parent;
// line 26
echo " <gradeRequired>";
echo twig_escape_filter($this->env, (isset($context["gradeRequired"]) || array_key_exists("gradeRequired", $context) ? $context["gradeRequired"] : (function () { throw new RuntimeError('Variable "gradeRequired" does not exist.', 26, $this->source); })()), "html", null, true);
echo "</gradeRequired>
</job>
";
$__internal_085b0142806202599c7fe3b329164a92397d8978207a37e79d70b8c52599e33e->leave($__internal_085b0142806202599c7fe3b329164a92397d8978207a37e79d70b8c52599e33e_prof);
$__internal_319393461309892924ff6e74d6d6e64287df64b63545b994e100d4ab223aed02->leave($__internal_319393461309892924ff6e74d6d6e64287df64b63545b994e100d4ab223aed02_prof);
}
public function getTemplateName()
{
return "AppBundle:DataProvider/GraduateJobs:job.xml.twig";
}
public function isTraitable()
{
return false;
}
public function getDebugInfo()
{
return array ( 138 => 26, 129 => 24, 125 => 23, 120 => 22, 111 => 20, 107 => 19, 103 => 18, 98 => 17, 92 => 15, 89 => 14, 80 => 12, 76 => 11, 72 => 10, 68 => 9, 64 => 8, 60 => 7, 56 => 6, 52 => 5, 48 => 4, 43 => 1,);
}
public function getSourceContext()
{
return new Source("<?xml version = '1.0' encoding = 'utf-8' ?>
<!DOCTYPE job PUBLIC 'gj-job-dtd' 'http://www.graduate-jobs.com/xml/job.dtd'>
<job>
<status>{{ workMode }}</status>
<accountName>{{ accountName }}</accountName>
<password>{{ password }}</password>
<title>{{ title }}</title>
<yourRef>{{ yourRef }}</yourRef>
<keyword>{{ keyword }}</keyword>
<detail>{{ detail }}</detail>
{% for location in locations %}
<location>{{ location }}</location>
{% endfor %}
{% if postcodes != '' %}
<postcodes>{{ postcodes }}</postcodes>
{% endif %}
<salary>{{ salary }}</salary>
<displayEndDate>{{ displayEndDate }}</displayEndDate>
{% for sector in sectors %}
<industrySector>{{ sector }}</industrySector>
{% endfor %}
<applyTo>{{ applyTo }}</applyTo>
{% for site in sites %}
<site>{{ site }}</site>
{% endfor %}
<gradeRequired>{{ gradeRequired }}</gradeRequired>
</job>
", "AppBundle:DataProvider/GraduateJobs:job.xml.twig", "/var/www/src/AppBundle/Resources/views/DataProvider/GraduateJobs/job.xml.twig");
}
}
Happy to assist with debugging the issue, but right now I am out of ideas, as I don't know how is this part designed to work.
Here's relevant part of config:
<plugins>
<pluginClass class="Psalm\SymfonyPsalmPlugin\Plugin">
<containerXml>var/cache/dev/srcKernelDevDebugContainer.xml</containerXml>
<twigCachePath>var/cache/dev/twig</twigCachePath>
</pluginClass>
<pluginClass class="Weirdan\DoctrinePsalmPlugin\Plugin"/>
</plugins>
<fileExtensions>
<extension name=".php" />
<extension name=".twig" checker="./vendor/psalm/plugin-symfony/src/Twig/TemplateFileAnalyzer.php"/>
</fileExtensions>
https://github.com/psalm/psalm-plugin-symfony/blob/master/src/Stubs/common/ParameterBag.stubphp#L27
This stub is in common, but it has to separated into a Symfony 4 and Symfony 5 case.
the Symfony 4 ParameterBag does not have the parameter in the all()
method: https://github.com/symfony/symfony/blob/4.4/src/Symfony/Component/HttpFoundation/ParameterBag.php#L36
Using the code
use Symfony\Component\Messenger\Envelope;
$envelope = new Envelope(new stdClass());
I got the following error:
Argument 1 of Symfony\Component\Messenger\Exception\HandlerFailedException::__construct expects Symfony\Component\Messenger\Envelope<object>, Symfony\Component\Messenger\Envelope<stdClass> provided (see https://psalm.dev/004)
Even change the type of envelope param, doents solve the problem
Reference
class CreateUserCommand extends ContainerAwareCommand
{
protected function configure(): void
{
$this
->setDescription('Create a user')
->setDefinition(
[
new InputArgument('email', InputArgument::REQUIRED, 'Email'),
],
)
}
/**
* @see Command
*/
protected function execute(InputInterface $input, OutputInterface $output): void
{
$userManager = $this->getContainer()->get(UserManager::class);
$user = new User();
$user->setEmail($input->getArgument('email'));
}
}
These kind of commands currently result in
ERROR: PossiblyInvalidArgument - src/AppBundle/Command/CreateUserCommand.php:63:25 - Argument 1 of AppBundle\Entity\User::setEmail expects null|string, possibly different type array<array-key, string>|bool|null|string provided (see https://psalm.dev/092)
$user->setEmail($input->getOption('email'));
hello
we need something like this
<?php
declare(strict_types=1);
namespace App\Psalm;
use PhpParser\Node\Stmt\ClassLike;
use Psalm\Codebase;
use Psalm\FileSource;
use Psalm\Plugin\Hook\AfterClassLikeVisitInterface;
use Psalm\Storage\ClassLikeStorage;
use Symfony\Component\DependencyInjection\ContainerAwareTrait;
class ContainerAwareTraitHandler implements AfterClassLikeVisitInterface
{
public static function afterClassLikeVisit(
ClassLike $stmt,
ClassLikeStorage $storage,
FileSource $statements_source,
Codebase $codebase,
array &$file_replacements = []
) {
if (ContainerAwareTrait::class === $storage->name) {
$storage->initialized_properties['container'] = true;
}
}
}
Hi. I have a problem when upgrading to version 2.2 on Alpine Linux in Docker images
php-cli-8.0-alpine, problem next :
Uncaught Error: Undefined constant "GLOB_BRACE" in /app/vendor/psalm/plugin-symfony/src/Plugin.php:36
Stack trace:
#0 /app/vendor/psalm/plugin-symfony/src/Plugin.php(89): Psalm\SymfonyPsalmPlugin\Plugin->getCommonStubs()
#1 /app/vendor/vimeo/psalm/src/Psalm/Config.php(1146): Psalm\SymfonyPsalmPlugin\Plugin->__invoke(Object(Psalm\PluginRegistrationSocket), Object(SimpleXMLElement))
#2 /app/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/ProjectAnalyzer.php(585): Psalm\Config->initializePlugins(Object(Psalm\Internal\Analyzer\ProjectAnalyzer))
#3 /app/vendor/vimeo/psalm/src/psalm.php(683): Psalm\Internal\Analyzer\ProjectAnalyzer->check('/app/', true)
#4 /app/vendor/vimeo/psalm/psalm(2): require_once('/app/vendor/vim...')
#5 {main}
Next Psalm\Exception\ConfigException: Failed to load plugin Psalm\SymfonyPsalmPlugin\Plugin in /app/vendor/vimeo/psalm/src/Psalm/Config.php:1148
Stack trace:
#0 /app/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/ProjectAnalyzer.php(585): Psalm\Config->initializePlugins(Object(Psalm\Internal\Analyzer\ProjectAnalyzer))
#1 /app/vendor/vimeo/psalm/src/psalm.php(683): Psalm\Internal\Analyzer\ProjectAnalyzer->check('/app/', true)
#2 /app/vendor/vimeo/psalm/psalm(2): require_once('/app/vendor/vim...')
#3 {main}
(Psalm 4.6.2@bca09d74adc704c4eaee36a3c3e9d379e290fc3b crashed due to an uncaught Throwable)
I think, this happens because this const doesn't isset in this linux dist. I find similar problem in Symfony 4 symfony/symfony@d7a0679 .
Help me, please.
Given this code:
return $this->twig->render('FooBundle:Bar:' . $var->getType() . '.html.twig', $params);
This fatal error occurs:
Uncaught RuntimeException: Can not retrieve template name from given expression (PhpParser\Node\Expr\BinaryOp\Concat) in /var/www/html/vendor/psalm/plugin-symfony/src/Twig/TwigUtils.php:27
Stack trace:
#0 /var/www/html/vendor/psalm/plugin-symfony/src/Twig/CachedTemplatesTainter.php(61): Psalm\SymfonyPsalmPlugin\Twig\TwigUtils::extractTemplateNameFromExpression(Object(PhpParser\Node\Expr\BinaryOp\Concat), Object(Psalm\Internal\Analyzer\StatementsAnalyzer))
#1 /var/www/html/vendor/vimeo/psalm/src/Psalm/Internal/Provider/MethodReturnTypeProvider.php(105): Psalm\SymfonyPsalmPlugin\Twig\CachedTemplatesTainter::getMethodReturnType(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), 'Twig\\Environmen...', 'render', Array, Object(Psalm\Context), Object(Psalm\CodeLocation), NULL, 'Twig_Environmen...', 'render')
#2 /var/www/html/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/MethodCallReturnTypeFetcher.php(70): Psalm\Internal\Provider\MethodReturnTypeProvider->getReturnType(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), 'Twig\\Environmen...', 'render', Array, Object(Psalm\Context), Object(Psalm\CodeLocation), NULL, 'Twig_Environmen...', 'render')
#3 /var/www/html/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/ExistingAtomicMethodCallAnalyzer.php(206): Psalm\Internal\Analyzer\Statements\Expression\Call\Method\MethodCallReturnTypeFetcher::fetch(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(Psalm\Codebase), Object(PhpParser\Node\Expr\MethodCall), Object(Psalm\Context), Object(Psalm\Internal\MethodIdentifier), Object(Psalm\Internal\MethodIdentifier), Object(Psalm\Internal\MethodIdentifier), 'Twig_Environmen...', Object(Psalm\Type\Atomic\TNamedObject), Object(Psalm\Type\Atomic\TNamedObject), Array, Object(Psalm\Internal\Analyzer\Statements\Expression\Call\Method\AtomicMethodCallAnalysisResult), Object(Psalm\Internal\Type\TemplateResult))
#4 /var/www/html/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/AtomicMethodCallAnalyzer.php(388): Psalm\Internal\Analyzer\Statements\Expression\Call\Method\ExistingAtomicMethodCallAnalyzer::analyze(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(PhpParser\Node\Expr\MethodCall), Object(PhpParser\Node\Identifier), Array, Object(Psalm\Codebase), Object(Psalm\Context), Object(Psalm\Type\Atomic\TNamedObject), Object(Psalm\Type\Atomic\TNamedObject), '$this->twig', Object(Psalm\Internal\MethodIdentifier), Object(Psalm\Internal\Analyzer\Statements\Expression\Call\Method\AtomicMethodCallAnalysisResult))
#5 /var/www/html/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/MethodCallAnalyzer.php(183): Psalm\Internal\Analyzer\Statements\Expression\Call\Method\AtomicMethodCallAnalyzer::analyze(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(PhpParser\Node\Expr\MethodCall), Object(Psalm\Codebase), Object(Psalm\Context), Object(Psalm\Type\Atomic\TNamedObject), Object(Psalm\Type\Atomic\TNamedObject), false, '$this->twig', Object(Psalm\Internal\Analyzer\Statements\Expression\Call\Method\AtomicMethodCallAnalysisResult))
#6 /var/www/html/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/ExpressionAnalyzer.php(151): Psalm\Internal\Analyzer\Statements\Expression\Call\MethodCallAnalyzer::analyze(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(PhpParser\Node\Expr\MethodCall), Object(Psalm\Context))
#7 /var/www/html/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/ExpressionAnalyzer.php(39): Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer::handleExpression(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(PhpParser\Node\Expr\MethodCall), Object(Psalm\Context), false, NULL, false)
#8 /var/www/html/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/ReturnAnalyzer.php(141): Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer::analyze(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(PhpParser\Node\Expr\MethodCall), Object(Psalm\Context))
#9 /var/www/html/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/StatementsAnalyzer.php(496): Psalm\Internal\Analyzer\Statements\ReturnAnalyzer::analyze(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(PhpParser\Node\Stmt\Return_), Object(Psalm\Context))
#10 /var/www/html/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/StatementsAnalyzer.php(171): Psalm\Internal\Analyzer\StatementsAnalyzer::analyzeStatement(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(PhpParser\Node\Stmt\Return_), Object(Psalm\Context), Object(Psalm\Context))
#11 /var/www/html/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/FunctionLikeAnalyzer.php(654): Psalm\Internal\Analyzer\StatementsAnalyzer->analyze(Array, Object(Psalm\Context), Object(Psalm\Context), true)
#12 /var/www/html/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/ClassAnalyzer.php(1978): Psalm\Internal\Analyzer\FunctionLikeAnalyzer->analyze(Object(Psalm\Context), Object(Psalm\Internal\Provider\NodeDataProvider), Object(Psalm\Context))
#13 /var/www/html/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/ClassAnalyzer.php(746): Psalm\Internal\Analyzer\ClassAnalyzer->analyzeClassMethod(Object(PhpParser\Node\Stmt\ClassMethod), Object(Psalm\Storage\ClassLikeStorage), Object(Psalm\Internal\Analyzer\ClassAnalyzer), Object(Psalm\Context), Object(Psalm\Context))
#14 /var/www/html/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/FileAnalyzer.php(211): Psalm\Internal\Analyzer\ClassAnalyzer->analyze(Object(Psalm\Context), Object(Psalm\Context))
#15 /var/www/html/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/Analyzer.php(340): Psalm\Internal\Analyzer\FileAnalyzer->analyze(NULL)
#16 /var/www/html/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/Analyzer.php(579): Psalm\Internal\Codebase\Analyzer->Psalm\Internal\Codebase\{closure}(825, '/var/www/html/j...')
#17 /var/www/html/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/Analyzer.php(269): Psalm\Internal\Codebase\Analyzer->doAnalysis(Object(Psalm\Internal\Analyzer\ProjectAnalyzer), 1)
#18 /var/www/html/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/ProjectAnalyzer.php(639): Psalm\Internal\Codebase\Analyzer->analyzeFiles(Object(Psalm\Internal\Analyzer\ProjectAnalyzer), 1, false, true)
#19 /var/www/html/vendor/vimeo/psalm/src/psalm.php(676): Psalm\Internal\Analyzer\ProjectAnalyzer->check('/var/www/html/', false)
#20 /var/www/html/vendor/vimeo/psalm/psalm(2): require_once('/var/www/html/v...')
#21 {main}
(Psalm 4.3.1@2feba22a005a18bf31d4c7b9bdb9252c73897476 crashed due to an uncaught Throwable)
Hello
we need something like this
<?php
declare(strict_types=1);
namespace App\Psalm;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassLike;
use Psalm\Codebase;
use Psalm\FileSource;
use Psalm\Plugin\Hook\AfterClassLikeVisitInterface;
use Psalm\Storage\ClassLikeStorage;
use Symfony\Contracts\Service\Attribute\Required;
class RequiredPropertyHandler implements AfterClassLikeVisitInterface
{
public static function afterClassLikeVisit(
ClassLike $stmt,
ClassLikeStorage $storage,
FileSource $statements_source,
Codebase $codebase,
array &$file_replacements = []
) {
if (!$stmt instanceof Class_) {
return;
}
foreach ($storage->properties as $name => $property) {
foreach ($property->attributes as $attribute) {
if (Required::class === $attribute->fq_class_name) {
$storage->initialized_properties[$name] = true;
break;
}
}
}
}
}
Usually options are declared without the --
prefix:
$this->addOption('boolean', null, InputOption::VALUE_NONE);
But symfony allows to add the prefix:
$this->addOption('--boolean', null, InputOption::VALUE_NONE);
But this plugin does not resolve the type if the prefix is added.
I get the following error when trying to run a taint analysis for the first time. Looks like a path is hard coded to baseDir/templates/
which isn't necessarily true for all projects.
Uncaught Twig\Error\LoaderError: The "templates" directory does not exist ("/var/www/html/templates"). in /var/www/html/vendor/twig/twig/src/Loader/FilesystemLoader.php:106
Stack trace:
#0 /var/www/html/vendor/twig/twig/src/Loader/FilesystemLoader.php(87): Twig\Loader\FilesystemLoader->addPath('templates', '__main__')
#1 /var/www/html/vendor/twig/twig/src/Loader/FilesystemLoader.php(45): Twig\Loader\FilesystemLoader->setPaths(Array)
#2 /var/www/html/vendor/psalm/plugin-symfony/src/Twig/TemplateFileAnalyzer.php(33): Twig\Loader\FilesystemLoader->__construct('templates', '/var/www/html/')
#3 /var/www/html/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/Analyzer.php(340): Psalm\SymfonyPsalmPlugin\Twig\TemplateFileAnalyzer->analyze(NULL)
#4 /var/www/html/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/Analyzer.php(579): Psalm\Internal\Codebase\Analyzer->Psalm\Internal\Codebase\{closure}(318, '/var/www/html/j...')
#5 /var/www/html/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/Analyzer.php(269): Psalm\Internal\Codebase\Analyzer->doAnalysis(Object(Psalm\Internal\Analyzer\ProjectAnalyzer), 1)
#6 /var/www/html/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/ProjectAnalyzer.php(639): Psalm\Internal\Codebase\Analyzer->analyzeFiles(Object(Psalm\Internal\Analyzer\ProjectAnalyzer), 1, false, true)
#7 /var/www/html/vendor/vimeo/psalm/src/psalm.php(676): Psalm\Internal\Analyzer\ProjectAnalyzer->check('/var/www/html/', false)
#8 /var/www/html/vendor/vimeo/psalm/psalm(2): require_once('/var/www/html/v...')
#9 {main}
(Psalm 4.3.1@2feba22a005a18bf31d4c7b9bdb9252c73897476 crashed due to an uncaught Throwable)
Currently it is possible add only one containerXml-File, that exists in the cache-path from environment for example "dev", but on build-server with environment "test" is an exception thrown , because the file doesn't exists . So my suggestion to allow more then one containerXml-File and only thrown the exception, if no containerXml-File from all listet files exists.
The following code
use App\Entity\Foo;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\Exception\InvalidArgumentException;
class MyConstraint extends Constraint
{
/**
* @var Foo
*/
public $foo;
/**
* @var string
*/
public $message = 'message';
/**
* @param mixed $options
*
* @return void
*/
public function __construct($options = null)
{
parent::__construct($options);
if (!$this->foo instanceof Foo) {
throw new InvalidArgumentException('The "foo" parameter value is not valid.');
}
}
/**
* @return string
*/
public function getDefaultOption()
{
return 'foo';
}
/**
* @return array
*/
public function getRequiredOptions()
{
return ['foo'];
}
}
Is returning an error:
UninitializedProperty - Cannot use uninitialized property $this->foo
But the property is initialized in the parent::construct()
since it does
$this->$option = $value
Is it something that can be improved in the symfony plugin (and how ?) or should psalm be more careful with the parent constructor ? @seferov @muglug
So our project combines AppBundle:DataProvider/GraduateJobs:job.xml.twig
and @App/DataProvider/GraduateJobs/job.xml.twig
syntaxes. In a project, both works, however, looks like plugin supports former one only. Can we support both? Otherwise we get
Scanning files...
Analyzing files...
Uncaught RuntimeException: The template @App/DataProvider/GraduateJobs/job.xml.twig was not found. in /var/www/vendor/psalm/plugin-symfony/src/Twig/CachedTemplatesMapping.php:69
Stack trace:
#0 /var/www/vendor/psalm/plugin-symfony/src/Twig/CachedTemplatesTainter.php(58): Psalm\SymfonyPsalmPlugin\Twig\CachedTemplatesMapping::getCacheClassName('@App/DataProvid...')
#1 /var/www/vendor/vimeo/psalm/src/Psalm/Internal/Provider/MethodReturnTypeProvider.php(105): Psalm\SymfonyPsalmPlugin\Twig\CachedTemplatesTainter::getMethodReturnType(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), 'Twig\\Environmen...', 'render', Array, Object(Psalm\Context), Object(Psalm\CodeLocation), NULL, NULL, NULL)
#2 /var/www/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/MethodCallReturnTypeFetcher.php(53): Psalm\Internal\Provider\MethodReturnTypeProvider->getReturnType(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), 'Twig\\Environmen...', 'render', Array, Object(Psalm\Context), Object(Psalm\CodeLocation), NULL)
#3 /var/www/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/AtomicMethodCallAnalyzer.php(680): Psalm\Internal\Analyzer\Statements\Expression\Call\Method\MethodCallReturnTypeFetcher::fetch(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(Psalm\Codebase), Object(PhpParser\Node\Expr\MethodCall), Object(Psalm\Context), Object(Psalm\Internal\MethodIdentifier), Object(Psalm\Internal\MethodIdentifier), Object(Psalm\Internal\MethodIdentifier), 'Twig\\Environmen...', Object(Psalm\Type\Atomic\TNamedObject), Object(Psalm\Type\Atomic\TNamedObject), Array, Object(Psalm\Internal\Analyzer\Statements\Expression\Call\Method\AtomicMethodCallAnalysisResult), Object(Psalm\Internal\Type\TemplateResult))
#4 /var/www/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/MethodCallAnalyzer.php(168): Psalm\Internal\Analyzer\Statements\Expression\Call\Method\AtomicMethodCallAnalyzer::analyze(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(PhpParser\Node\Expr\MethodCall), Object(Psalm\Codebase), Object(Psalm\Context), Object(Psalm\Type\Atomic\TNamedObject), Object(Psalm\Type\Atomic\TNamedObject), false, NULL, Object(Psalm\Internal\Analyzer\Statements\Expression\Call\Method\AtomicMethodCallAnalysisResult))
#5 /var/www/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/ExpressionAnalyzer.php(144): Psalm\Internal\Analyzer\Statements\Expression\Call\MethodCallAnalyzer::analyze(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(PhpParser\Node\Expr\MethodCall), Object(Psalm\Context))
#6 /var/www/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/ExpressionAnalyzer.php(39): Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer::handleExpression(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(PhpParser\Node\Expr\MethodCall), Object(Psalm\Context), false, NULL, false)
#7 /var/www/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/ReturnAnalyzer.php(135): Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer::analyze(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(PhpParser\Node\Expr\MethodCall), Object(Psalm\Context))
#8 /var/www/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/StatementsAnalyzer.php(457): Psalm\Internal\Analyzer\Statements\ReturnAnalyzer::analyze(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(PhpParser\Node\Stmt\Return_), Object(Psalm\Context))
#9 /var/www/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/StatementsAnalyzer.php(164): Psalm\Internal\Analyzer\StatementsAnalyzer::analyzeStatement(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(PhpParser\Node\Stmt\Return_), Object(Psalm\Context), Object(Psalm\Context))
#10 /var/www/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/FunctionLikeAnalyzer.php(591): Psalm\Internal\Analyzer\StatementsAnalyzer->analyze(Array, Object(Psalm\Context), Object(Psalm\Context), true)
#11 /var/www/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/ClassAnalyzer.php(1897): Psalm\Internal\Analyzer\FunctionLikeAnalyzer->analyze(Object(Psalm\Context), Object(Psalm\Internal\Provider\NodeDataProvider), Object(Psalm\Context))
#12 /var/www/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/ClassAnalyzer.php(734): Psalm\Internal\Analyzer\ClassAnalyzer->analyzeClassMethod(Object(PhpParser\Node\Stmt\ClassMethod), Object(Psalm\Storage\ClassLikeStorage), Object(Psalm\Internal\Analyzer\ClassAnalyzer), Object(Psalm\Context), Object(Psalm\Context))
#13 /var/www/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/FileAnalyzer.php(215): Psalm\Internal\Analyzer\ClassAnalyzer->analyze(Object(Psalm\Context), Object(Psalm\Context))
#14 /var/www/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/Analyzer.php(336): Psalm\Internal\Analyzer\FileAnalyzer->analyze(NULL)
#15 /var/www/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/Analyzer.php(576): Psalm\Internal\Codebase\Analyzer->Psalm\Internal\Codebase\{closure}(223, '/var/www/src/Ap...')
#16 /var/www/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/Analyzer.php(268): Psalm\Internal\Codebase\Analyzer->doAnalysis(Object(Psalm\Internal\Analyzer\ProjectAnalyzer), 1)
#17 /var/www/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/ProjectAnalyzer.php(639): Psalm\Internal\Codebase\Analyzer->analyzeFiles(Object(Psalm\Internal\Analyzer\ProjectAnalyzer), 1, false, true)
#18 /var/www/vendor/vimeo/psalm/src/psalm.php(677): Psalm\Internal\Analyzer\ProjectAnalyzer->check('/var/www/', false)
#19 /var/www/vendor/vimeo/psalm/psalm(2): require_once('/var/www/vendor...')
#20 {main}
(Psalm 3.16@d03e5ef057d6adc656c0ff7e166c50b73b4f48f3 crashed due to an uncaught Throwable)
cc @adrienlucas
The container name is case insensitive so the check must consider this.
From the Voter class
/**
* Perform a single access check operation on a given attribute, subject and token.
* It is safe to assume that $attribute and $subject already passed the "supports()" method check.
*
* @param string $attribute
* @param mixed $subject
*
* @return bool
*/
abstract protected function voteOnAttribute($attribute, $subject, TokenInterface $token);
Subject has a mixed
type, but since there is a supports
method, it's safe to restrict the $subject
type.
With the following method supports
public function supports($attribute, $subject)
{
if (self::CONTRACT_SHOW !== $attribute) {
return false;
}
if (!$subject instanceof Contract) {
return false;
}
return true;
}
It should be ok to write
/**
* @param string $attribute
* @param Contract $subject
* @param TokenInterface $token
*
* @return bool
*/
public function voteOnAttribute($attribute, $subject, TokenInterface $token)
Without getting a MoreSpecificImplementedParamType
error.
Same for others classes with a supports
method.
Is there a way to avoid the error ? Does a stub or something like this could be added to this plugin ? Or the only solution is to use psalm-suppress
?
Fatal error: Declaration of Psalm\SymfonyPsalmPlugin\Handler\HeaderBagHandler::getMethodReturnType(Psalm\StatementsSource $source, string $fq_classlike_name, string $method_name_lowercase, array $call_args, Psalm\Context $context, Psalm\CodeLocation $code_location, ?array $template_type_parameters = NULL, ?string $called_fq_classlike_name = NULL, ?string $called_method_name_lowercase = NULL) must be compatible with Psalm\Plugin\Hook\MethodReturnTypeProviderInterface::getMethodReturnType(Psalm\StatementsSource $source, string $fq_classlike_name, string $method_name_lowercase, array $call_args, Psalm\Context $context, Psalm\CodeLocation $code_location, ?array $template_type_parameters = NULL, ?string $called_fq_classlike_name = NULL, ?string $called_method_name_lowercase = NULL): ?Psalm\Type\Union in /github/workspace/vendor/psalm/plugin-symfony/src/Handler/HeaderBagHandler.php on line 27
The difference seems to be the return type being stricter in the interface.
psalm/psalm-plugin-symfony v1.4.3
CI via psalm/[email protected]
It would be nice to resolve the actual return type dependent on the config in configure()
method: phpstan/phpstan-symfony#20
This plugin doesn't currently seem to handle default argument values in commands. For example, the following will cause Psalm to throw ERROR: PossiblyNullArgument - … - Argument 1 of trim cannot be null, possibly null value provided (see https://psalm.dev/078)
, even though that argument can never be null, as far as I'm aware.
class FooCommand extends Command
{
protected function configure(): void
{
$this->addArgument('bar', InputArgument::OPTIONAL, 'Example optional argument.', 'defaultvalue');
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
trim($input->getArgument('bar'));
return 0;
}
}
class Test {
private RouterInterface $router;
public function __construct(RouterInterface $router) {
$routeCollection = $this->router->getRouteCollection();
// MixedAssignment: Unable to determine the type that $route is being assigned to
foreach ($routeCollection as $routeName => $route) {
// MixedArgumentTypeCoercion: Argument 1 of str_starts_with expects string, parent type array-key provided
if (str_starts_with($routeName, 'whatever')) {
echo 'hello!';
}
}
}
}
It looks for me that $route always will be an instance of \Symfony\Component\Routing\Route in this case, and $routeName always will be a string, not an array-key.
So this happens when doing $container->get(self::class)
I know it's weird code, but it shouldn't crash Psalm regardless. And happens only when this plugin is enabled. Following reproduces error for me, but in your case it would be of course incomplete.
class SettingManager extends CommonManager
{
public function isUpdatedLarsDatabase(): string
{
$larsStatus = $this->container->get(self::class)->get(
ImportLarsCsvCommand::SETTINGS_LARS_UPDATING_STATUS_KEY,
);
return $larsStatus->getValue();
}
}
Result it
Analyzing /var/www/src/AppBundle/Doctrine/Manager/SettingManager.php
Parsing /var/www/src/AppBundle/Doctrine/Manager/SettingManager.php
Uncaught InvalidArgumentException: Could not get class storage for self in /var/www/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ClassLikeStorageProvider.php:45
Stack trace:
#0 /var/www/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/AtomicMethodCallAnalyzer.php(161): Psalm\Internal\Provider\ClassLikeStorageProvider->get('self')
#1 /var/www/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/MethodCallAnalyzer.php(173): Psalm\Internal\Analyzer\Statements\Expression\Call\AtomicMethodCallAnalyzer::analyze(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(PhpParser\Node\Expr\MethodCall), Object(Psalm\Codebase), Object(Psalm\Context), Object(Psalm\Type\Atomic\TNamedObject), Object(Psalm\Type\Atomic\TNamedObject), false, NULL, Object(Psalm\Internal\Analyzer\Statements\Expression\Call\AtomicMethodCallAnalysisResult))
#2 /var/www/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/ExpressionAnalyzer.php(135): Psalm\Internal\Analyzer\Statements\Expression\Call\MethodCallAnalyzer::analyze(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(PhpParser\Node\Expr\MethodCall), Object(Psalm\Context))
#3 /var/www/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/AssignmentAnalyzer.php(245): Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer::analyze(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(PhpParser\Node\Expr\MethodCall), Object(Psalm\Context))
#4 /var/www/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/ExpressionAnalyzer.php(120): Psalm\Internal\Analyzer\Statements\Expression\AssignmentAnalyzer::analyze(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(PhpParser\Node\Expr\Variable), Object(PhpParser\Node\Expr\MethodCall), NULL, Object(Psalm\Context), NULL)
#5 /var/www/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/StatementsAnalyzer.php(751): Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer::analyze(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(PhpParser\Node\Expr\Assign), Object(Psalm\Context), false, Object(Psalm\Context), true)
#6 /var/www/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/FunctionLikeAnalyzer.php(541): Psalm\Internal\Analyzer\StatementsAnalyzer->analyze(Array, Object(Psalm\Context), Object(Psalm\Context), true)
#7 /var/www/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/ClassAnalyzer.php(1767): Psalm\Internal\Analyzer\FunctionLikeAnalyzer->analyze(Object(Psalm\Context), Object(Psalm\Internal\Provider\NodeDataProvider), Object(Psalm\Context))
#8 /var/www/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/ClassAnalyzer.php(727): Psalm\Internal\Analyzer\ClassAnalyzer->analyzeClassMethod(Object(PhpParser\Node\Stmt\ClassMethod), Object(Psalm\Storage\ClassLikeStorage), Object(Psalm\Internal\Analyzer\ClassAnalyzer), Object(Psalm\Context), Object(Psalm\Context))
#9 /var/www/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/FileAnalyzer.php(201): Psalm\Internal\Analyzer\ClassAnalyzer->analyze(Object(Psalm\Context), Object(Psalm\Context))
#10 /var/www/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/Analyzer.php(357): Psalm\Internal\Analyzer\FileAnalyzer->analyze(NULL)
#11 /var/www/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/Analyzer.php(589): Psalm\Internal\Codebase\Analyzer->Psalm\Internal\Codebase\{closure}(0, '/var/www/src/Ap...')
#12 /var/www/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/Analyzer.php(267): Psalm\Internal\Codebase\Analyzer->doAnalysis(Object(Psalm\Internal\Analyzer\ProjectAnalyzer), 1)
#13 /var/www/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/ProjectAnalyzer.php(1160): Psalm\Internal\Codebase\Analyzer->analyzeFiles(Object(Psalm\Internal\Analyzer\ProjectAnalyzer), 1, false, false)
#14 /var/www/vendor/vimeo/psalm/src/psalm.php(590): Psalm\Internal\Analyzer\ProjectAnalyzer->checkPaths(Array)
#15 /var/www/vendor/vimeo/psalm/psalm(2): require_once('/var/www/vendor...')
#16 {main}
(Psalm 3.11.4@58e1d8e68e5098bf4fbfdfb420c38d563f882549 crashed due to an uncaught Throwable)
It seems like Twig extensions don't work with the basic TemplateFileAnalyzer
.
This code uses Symfony extensions and produces this error:
{{ render_inline(controller('my_controller')) }}
Uncaught Twig\Error\SyntaxError: Unknown "controller" function. in /var/www/html/templates/test.html.twig:1
Stack trace:
#0 /var/www/html/vendor/twig/twig/src/ExpressionParser.php(470): Twig\ExpressionParser->getFunctionNodeClass('controller', 8)
#1 /var/www/html/vendor/twig/twig/src/ExpressionParser.php(243): Twig\ExpressionParser->getFunctionNode('controller', 8)
#2 /var/www/html/vendor/twig/twig/src/ExpressionParser.php(183): Twig\ExpressionParser->parsePrimaryExpression()
#3 /var/www/html/vendor/twig/twig/src/ExpressionParser.php(78): Twig\ExpressionParser->getPrimary()
#4 /var/www/html/vendor/twig/twig/src/ExpressionParser.php(621): Twig\ExpressionParser->parseExpression(0, false)
#5 /var/www/html/vendor/twig/twig/src/ExpressionParser.php(469): Twig\ExpressionParser->parseArguments(true)
#6 /var/www/html/vendor/twig/twig/src/ExpressionParser.php(243): Twig\ExpressionParser->getFunctionNode('render_inline', 8)
#7 /var/www/html/vendor/twig/twig/src/ExpressionParser.php(183): Twig\ExpressionParser->parsePrimaryExpression()
#8 /var/www/html/vendor/twig/twig/src/ExpressionParser.php(78): Twig\ExpressionParser->getPrimary()
#9 /var/www/html/vendor/twig/twig/src/Parser.php(166): Twig\ExpressionParser->parseExpression()
#10 /var/www/html/vendor/twig/twig/src/TokenParser/BlockTokenParser.php(47): Twig\Parser->subparse(Array, true)
#11 /var/www/html/vendor/twig/twig/src/Parser.php(209): Twig\TokenParser\BlockTokenParser->parse(Object(Twig\Token))
#12 /var/www/html/vendor/twig/twig/src/Parser.php(122): Twig\Parser->subparse(NULL, false)
#13 /var/www/html/vendor/twig/twig/src/Environment.php(735): Twig\Parser->parse(Object(Twig\TokenStream))
#14 /var/www/html/tests/psalm/TemplateFileAnalyzer.php(80): Twig\Environment->parse(Object(Twig\TokenStream))
#15 /var/www/html/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/Analyzer.php(340): TemplateFileAnalyzer->analyze(NULL)
#16 /var/www/html/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/Analyzer.php(579): Psalm\Internal\Codebase\Analyzer->Psalm\Internal\Codebase\{closure}(318, '/var/www/html/j...')
#17 /var/www/html/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/Analyzer.php(269): Psalm\Internal\Codebase\Analyzer->doAnalysis(Object(Psalm\Internal\Analyzer\ProjectAnalyzer), 1)
#18 /var/www/html/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/ProjectAnalyzer.php(639): Psalm\Internal\Codebase\Analyzer->analyzeFiles(Object(Psalm\Internal\Analyzer\ProjectAnalyzer), 1, false, true)
#19 /var/www/html/vendor/vimeo/psalm/src/psalm.php(676): Psalm\Internal\Analyzer\ProjectAnalyzer->check('/var/www/html/', false)
#20 /var/www/html/vendor/vimeo/psalm/psalm(2): require_once('/var/www/html/v...')
#21 {main}
(Psalm 4.3.1@2feba22a005a18bf31d4c7b9bdb9252c73897476 crashed due to an uncaught Throwable)
Plugin should add capability to infer reduced type from Collection::filter
based on assertions in a callback, similarly as Psalm currently does for array_filter
. Here is what should be supported: https://psalm.dev/r/1b5cbdc9c0
Backstory: From conversation with @muglug:
Add that to the doctrine plugin
This is the current annotation: https://github.com/doctrine/collections/blob/a4504c79efd8847cc77d10b70209ef838b10338f/lib/Doctrine/Common/Collections/Collection.php#L214-L225
there’s basically no way to annotate that in a docblock
As Symfony documentation states, services' names can be FQCNs. But plugin raises NamingConventionViolation
when service is accessed this way and I get:
ERROR: NamingConventionViolation - tests/... - Use snake_case for configuration parameter and service names (see https://psalm.dev/000)
$this->hub = self::$container->get(HubInterface::class);
Works OK when using like self::$container->get('event_dispatcher')
. But Sentry\State\HubInterface
does not have snake_case alias and I don't want to create it just for Psalm 😉 Would be great if naming convention was determined differently for parameters and services.
Declaration of Psalm\SymfonyPsalmPlugin\Twig\TemplateFileAnalyzer::analyze
has invalid signature override from FileAnalyzer in vimeo/psalm 4.x
PHP Warning: Declaration of Psalm\SymfonyPsalmPlugin\Twig\TemplateFileAnalyzer::analyze(?Psalm\Context $file_context = NULL, bool $preserve_analyzers = false, ?Psalm\Context $global_context = NULL): void should be compatible with Psalm\Internal\Analyzer\FileAnalyzer::analyze(?Psalm\Context $file_context = NULL, ?Psalm\Context $global_context = NULL): void in /var/www/vendor/psalm/plugin-symfony/src/Twig/TemplateFileAnalyzer.php on line 21
Warning: Declaration of Psalm\SymfonyPsalmPlugin\Twig\TemplateFileAnalyzer::analyze(?Psalm\Context $file_context = NULL, bool $preserve_analyzers = false, ?Psalm\Context $global_context = NULL): void should be compatible with Psalm\Internal\Analyzer\FileAnalyzer::analyze(?Psalm\Context $file_context = NULL, ?Psalm\Context $global_context = NULL): void in /var/www/vendor/psalm/plugin-symfony/src/Twig/TemplateFileAnalyzer.php on line 21
Plugin not handle service locator by using ServiceSubscriberInterface
PHP Notice: Undefined property: Psalm\Codebase::$taint in /home/vadim/projects/the-real-estate-tours/vendor/psalm/plugin-symfony/src/Twig/AnalyzedTemplatesTainter.php on line 27
We have a couple of classes implementing Symfony\Contracts\Service\ServiceSubscriberInterface
to allow autowiring of service locators in problems areas where there are issues with circular references. This plugin currently reports ERROR: PrivateService - … - Private service "Foo\Bar" used in container::get()
in these scenarios.
Is there a way to annotate our services accordingly, as I understand parsing the return value of ::getSubscribedServices()
would be impossible in many scenarios.
PHP Notice: Undefined property: PhpParser\Node\Expr\Variable::$value in .../vendor/psalm/plugin-symfony/src/Handler/ConsoleHandler.php on line 70
I think the happens if the code is something like $input->getOption($name)
The check in AnalyzedTemplatesTainter for Twig variables just checks for ([a-zA-Z]+)
while Twig variables could contain other characters (like _
). Having a variable named my_result
leads to:
Uncaught Exception: Argument 2 passed to Psalm\SymfonyPsalmPlugin\Twig\TemplateFileAnalyzer::getTaintNodeForTwigNamedVariable() must be of the type string, null given, called in /Users/myuser/projects/myapp/vendor/psalm/plugin-symfony/src/Twig/AnalyzedTemplatesTainter.php on line 45
Its a bit annoying to get around the MissingReturnType
errors on controller action methods, since they can return many different things (Response, array, any objects depending on view listeners).
Would be nice to have a config to disable MissingReturnType
if the method is a *Controller::*Action.
It would be nice to support the test container (Symfony\Bundle\FrameworkBundle\Test\TestContainer
), which allows to fetch private services for testing.
A use case would be the Symfony\Bundle\FrameworkBundle\Test\KernelTestCase::$container
. If you currently use this container in the tests, Psalm will report a PrivateService
issue and will not know the correct type (only object|null
).
I'm not sure how to do this properly. Maybe the ContainerHandler
could always set the type, even if the service is not public and then suppress the PrivateService
manualy for the tests directory?
Suppressing issues in this plugin is currently not possible according manual here: https://github.com/vimeo/psalm/blob/1c3796d2a02f322c36e7a167a2b468556440403f/docs/running_psalm/plugins/authoring_plugins.md#handling-custom-plugin-issues
I believe it's because your issues do not extend from PluginIssue.
Specifically, I want to (temporarily) suppress ContainerDependency
issue.
Psalm and most other relevant plugins allowed PHP 7.1 and PHP 7.2 again for Psalm V4.
Could you please activate them for the Symfony plugin as well? At least in the changes for 2.0 I see no new language features.
The Debug Container reading classes are treating the public attribute as true if not available. In the latest Symfony 5.X releases it should never be false anymore (can't test it right now) after the private attribute had been completely removed.
https://github.com/symfony/dependency-injection/blob/master/Dumper/XmlDumper.php#L111
Here is the concerning code of this plugin
Symfony 3.3 was the last version producing a XML where missing public attributes had a value of true.
If a detection of the correct value is complicated could we configure it?
#61 added support for Twig taint analysis with two different approaches (file analyzer and cached template files).
According to the pull request, "The two have their own advantages.". Unfortunately, it is not documented what the differences and advantages are of each approach.
Running Psalm on lowest level on PHP 7.4.6 I am getting many of the following errors:
Notice: Undefined property: PhpParser\Node\Expr\ClassConstFetch::$value in /app/vendor/psalm/plugin-symfony/src/Handler/ConsoleHandler.php on line 113
This seems to originate from Symfony commands with custom arguments or options in such form:
$this->addArgument(self::FOO_ARGUMENT_NAME, InputArgument::REQUIRED);
$this->addOption(self::FOO_OPTION_NAME, null, InputOption::VALUE_NONE);
More specifically it's caused by the argument or option name being referenced from a constant.
nikic/php-parser: v4.4.0
psalm/plugin-symfony: v1.2.2
I have several services that use the container by their very nature, like lazy-load-proxy-services or generic classes that execute methods on configurable services. This plugin flags these services with the ContainerDependency
issue, which i would like to suppress for these instances. Psalm itself allows suppressing any issues using the psalm-suppress
annotation, but this does not seem to work for the ContainerDependency
issue-type.
https://psalm.dev/docs/running_psalm/dealing_with_code_issues/
https://psalm.dev/docs/annotating_code/supported_annotations/#psalm-suppress-someissuename
$ bin/psalm src/Foo/Bar/SomeExampleLazyLoadProxy.php
Scanning files...
Analyzing files...
E
ERROR: ContainerDependency - src/Foo/Bar/SomeExampleLazyLoadProxy.php:26:5 - Container must not inject into services as dependency! Use dependency-injection. (see https://psalm.dev/000)
/**
* @psalm-suppress ContainerDependency
*/
public function __construct(
ContainerInterface $container,
string $serviceId
) {
$this->container = $container;
$this->serviceId = $serviceId;
}
------------------------------
1 errors found
------------------------------
Checks took 11.64 seconds and used 1,694.197MB of memory
Psalm was able to infer types for 93.8762% of the codebase
It even shows the suppress-annotation in the error-output itself.
I am not sure if this is an issue with psalm itself or with this plugin, but when looking at the issues in psalm itself that have to do with suppression, it seems to me that suppression works differently depending on the issue-type and the ContainerDependency
issue is colored differently then the other issues in the console, so I think that this is probably a problem with the issue-type ContainerDependency
itself.
Have I maybe applied the suppression in a wrong way? I tried different ways of specifying it (one-liner, on the class, on the assignment-line, supressing "all"), but suppressing this issue seems impossible at the moment.
Versions:
psalm/plugin-symfony
: v1.2.3
vimeo/psalm
: 3.11.6
symfony 3.4.41
PHP 7.2.31
.
protected function configure()
{
$this
->addOption('foo', null, InputOption::VALUE_OPTIONAL, 'desc', false)
;
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$foo = $input->getOption('foo');
if (false !== $foo) {
// option is set (with or without value)
}
// ...
}
This results in a RedundantCondition "string can never contain false".
But actually the value can be false, it is the default value.
I'm using this to differentiate between "option is not used" (false
) and "option is used without value" (null
).
See original issue at vimeo/psalm#4241
Hello,
In my code base, there are many services, who handled by symfony container (dependencies injection).
Sometimes I inject in my service an another service via a setter method.
Example:
<?php
final class MyServiceA {
}
final class MyServiceB {
private MyServiceA $a;
public function __construct(){}
/** @required */
private function setMyServiceA(MyServiceA $a): void { $this->a = $a; }
}
In symfony, if I annotate my setter with @required annotation, symfony will inject the service in setter just after the constructor. The property $a will therefore always initialized.
In my exemple, Psalm raise an PropertyNotSetInConstructor error.
It would be nice if Psalm would handle this annotation.
Thanks you for your jobs.
I'm trying to enable the plugin on https://github.com/redaxo/redaxo (for the console handler).
The warning is coming from this line:
Hello.
Given:
Psalm 4.6.2
psalm/symfony-plugin v.2.1.2
Symfony v4.4.*
PHP code:
namespace App;
interface SomeImportantInterface {}
class SomeLogic implements SomeImportantInterface {}
class Handler
{
public function __invoke(SomeImportantInterface $logic)
{
}
}
symfony DI config:
App\SomeImportantInterface:
class: App\SomeLogic
App\Handler:
autowire: true
psalm.xml:
<plugins>
<pluginClass class="Psalm\SymfonyPsalmPlugin\Plugin">
<containerXml>var/path/to/xml_dump.xml</containerXml>
</pluginClass>
</plugins>
psalm output:
ERROR: UnusedClass - src/App/SomeLogic.php:3:7 - Class App\SomeLogic is never used (see https://psalm.dev/075)
class SomeLogic implements SomeImportantInterface
It would be very helpful to detect this kind of cases and not to mark this classes as Unused.
I made (some) stubs for symfony/forms and need opinion if my approach is correct. Simple use-case (shortened, Route
is an entity):
/**
* @extends AbstractType<Route>
*/
class RouteType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$route = $builder->getData(); // <- psalm knows it is Route|null
$nrOfAssignedRoutes = $this->getNrOfAssignedAddresses($route);
}
private function getNrOfAssignedAddresses(?Route $route): int // no issues
{
return 42; // real code removed for simplicity
}
}
Controller usage:
/**
* Injected using ArgumentResolver, removed for simplicity. See old https://github.com/strictify/coding-challenge/blob/symfony-4/src/Controller/Product/CreateAction.php#L38
*
* @param FormInterface<RouteEntity> $form
*/
public function create(FormInterface $form): Response
{
if ($form->isSubmitted() && $form->isValid() && $data = $form->getData()) {
// psalm knows $data is instance of Route (not null)
// persist, flush, redirect
}
// render template
}
Future PHP could be:
public function create(SubmittedForm<RouteType> $submittedForm): Response
{
$data = $submittedForm->getDataOrThrowException(); // returns T
}
Assigning data type on form level also fixes buildView
and finishView
methods.
This approach could also detect errors when using this bundle: https://github.com/hitechcoding/strict-form-mapper-bundle/blob/master/docs/accessors.md
$builder->add('type', ChoiceType::class, [
'get_value' => fn(Route $route) => $route->getType(), // this can detect error is wrong param is typed
'update_value' => fn(string $type, Route $route) => $route->setType($type),
]);
or with same bundle for factories:
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'factory' => fn(string $name, string $type) => new Route($this->security->getAuthCompany(), $name, $type),
]);
}
or default empty_data
:
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'empty_data' => fn(FormInterface $form) => new Route($this->security->getAuthCompany(), $form->get('name')->getData(), $form->get('type')->getData()),
]);
}
Because of complexity and power of forms, I am asking if you think this is valid approach or if it can be improved somehow. I was thinking about adding $options
as second generic parameter so
/**
* @extends AbstractType<Route, array{some_external_parameter: bool}>
*/
class RouteType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$externalParameter = $options['some_external_parameter']; // <-- psalm would know it is bool value
}
}
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.