Letting PHPStan (2.x) analyze the following code/file leads to an internal error.
This is because PHPStan Laminas Framework assumes that properties in classes extending AbstractOptions are named a certain way, which is invalid because AbstractOptions only imposes naming conventions on property getters and setters but not underlying properties (which AbstractOptions does not know/access directly).
PHPStan Laminas Framework may check obvious candidates (queried property name, queried property name without underscores) first but since properties may be named "randomly", it must be prepared for those checks to fail and then provide some fallback handling.
declare(strict_types=1);
use Laminas\Stdlib\AbstractOptions;
require __DIR__ . '/../vendor/autoload.php';
/**
* @extends AbstractOptions<mixed>
*/
class FooOptions extends AbstractOptions
{
private string $fooBar = '';
/** @param iterable<string, mixed> $options */
public function __construct($options)
{
parent::__construct($options);
if ($this->foo_bar === '') {
throw new InvalidArgumentException('Missing option foo_bar');
}
echo $this->foo_bar;
}
public function getFooBar(): string
{
return $this->fooBar;
}
public function setFooBar(string $fooBar): void
{
$this->fooBar = $fooBar;
}
}
$ok = new FooOptions(['foo_bar' => 'baz']);
$broken = new FooOptions([]);
Internal error: Property $foo_bar was not found in reflection of class FooOptions. while analysing file
/path/to/src/FooOptions.php
## phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/src/Reflection/ClassReflection.php(587)
#0
/path/to/vendor/slam/phpstan-laminas-framework/src/Type/Laminas/StdlibAbstractOptionsPropertiesClassReflectionExtension.php(72):
PHPStan\Reflection\ClassReflection->getNativeProperty('foo_bar')
#1 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/src/Reflection/WrappedExtendedPropertyReflection.php(38):
PHPStan\Reflection\PropertyReflection@anonymous->getReadableType()
#2 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/src/Reflection/Type/CallbackUnresolvedPropertyPrototypeReflection.php(56):
PHPStan\Reflection\WrappedExtendedPropertyReflection->getReadableType()
#3 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/src/Reflection/Type/CallbackUnresolvedPropertyPrototypeReflection.php(48):
PHPStan\Reflection\Type\CallbackUnresolvedPropertyPrototypeReflection->transformPropertyWithStaticType(Object(PHPStan\Reflection\ClassReflection),
Object(PHPStan\Reflection\WrappedExtendedPropertyReflection))
#4 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/src/Type/StaticType.php(170):
PHPStan\Reflection\Type\CallbackUnresolvedPropertyPrototypeReflection->getTransformedProperty()
#5 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/MutatingScope.php(3860):
PHPStan\Type\StaticType->getProperty('foo_bar', Object(PHPStan\Analyser\MutatingScope))
#6 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/MutatingScope.php(3867):
PHPStan\Analyser\MutatingScope->getPropertyReflection(Object(PHPStan\Type\ThisType), 'foo_bar')
#7 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/MutatingScope.php(1560):
PHPStan\Analyser\MutatingScope->propertyFetchType(Object(PHPStan\Type\ThisType), 'foo_bar', Object(PhpParser\Node\Expr\PropertyFetch))
#8 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/MutatingScope.php(1566):
PHPStan\Analyser\MutatingScope->PHPStan\Analyser\{closure}()
#9 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/MutatingScope.php(546):
PHPStan\Analyser\MutatingScope->resolveType('$this->foo_bar', Object(PhpParser\Node\Expr\PropertyFetch))
#10 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/RicherScopeGetTypeHelper.php(29):
PHPStan\Analyser\MutatingScope->getType(Object(PhpParser\Node\Expr\PropertyFetch))
#11 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/MutatingScope.php(703):
PHPStan\Analyser\RicherScopeGetTypeHelper->getIdenticalResult(Object(PHPStan\Analyser\MutatingScope), Object(PhpParser\Node\Expr\BinaryOp\Identical))
#12 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/MutatingScope.php(546):
PHPStan\Analyser\MutatingScope->resolveType('$this->foo_bar ...', Object(PhpParser\Node\Expr\BinaryOp\Identical))
#13 /path/to/vendor/phpstan/phpstan-strict-rules/src/Rules/BooleansInConditions/BooleanRuleHelper.php(24):
PHPStan\Analyser\MutatingScope->getType(Object(PhpParser\Node\Expr\BinaryOp\Identical))
#14 /path/to/vendor/phpstan/phpstan-strict-rules/src/Rules/BooleansInConditions/BooleanInIfConditionRule.php(33):
PHPStan\Rules\BooleansInConditions\BooleanRuleHelper->passesAsBoolean(Object(PHPStan\Analyser\MutatingScope), Object(PhpParser\Node\Expr\BinaryOp\Identical))
#15 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/FileAnalyser.php(103):
PHPStan\Rules\BooleansInConditions\BooleanInIfConditionRule->processNode(Object(PhpParser\Node\Stmt\If_), Object(PHPStan\Analyser\MutatingScope))
#16 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/src/Node/ClassStatementsGatherer.php(116):
PHPStan\Analyser\FileAnalyser->PHPStan\Analyser\{closure}(Object(PhpParser\Node\Stmt\If_), Object(PHPStan\Analyser\MutatingScope))
#17 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScopeResolver.php(647):
PHPStan\Node\ClassStatementsGatherer->__invoke(Object(PhpParser\Node\Stmt\If_), Object(PHPStan\Analyser\MutatingScope))
#18 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScopeResolver.php(520):
PHPStan\Analyser\NodeScopeResolver::PHPStan\Analyser\{closure}(Object(PhpParser\Node\Stmt\If_), Object(PHPStan\Analyser\MutatingScope))
#19 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScopeResolver.php(450):
PHPStan\Analyser\NodeScopeResolver->processStmtNode(Object(PhpParser\Node\Stmt\If_), Object(PHPStan\Analyser\MutatingScope), Object(Closure),
Object(PHPStan\Analyser\StatementContext))
#20 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScopeResolver.php(646):
PHPStan\Analyser\NodeScopeResolver->processStmtNodes(Object(PhpParser\Node\Stmt\ClassMethod), Array, Object(PHPStan\Analyser\MutatingScope), Object(Closure),
Object(PHPStan\Analyser\StatementContext))
#21 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScopeResolver.php(450):
PHPStan\Analyser\NodeScopeResolver->processStmtNode(Object(PhpParser\Node\Stmt\ClassMethod), Object(PHPStan\Analyser\MutatingScope),
Object(PHPStan\Node\ClassStatementsGatherer), Object(PHPStan\Analyser\StatementContext))
#22 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScopeResolver.php(786):
PHPStan\Analyser\NodeScopeResolver->processStmtNodes(Object(PhpParser\Node\Stmt\Class_), Array, Object(PHPStan\Analyser\MutatingScope),
Object(PHPStan\Node\ClassStatementsGatherer), Object(PHPStan\Analyser\StatementContext))
#23 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScopeResolver.php(398):
PHPStan\Analyser\NodeScopeResolver->processStmtNode(Object(PhpParser\Node\Stmt\Class_), Object(PHPStan\Analyser\MutatingScope), Object(Closure),
Object(PHPStan\Analyser\StatementContext))
#24 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/FileAnalyser.php(162):
PHPStan\Analyser\NodeScopeResolver->processNodes(Array, Object(PHPStan\Analyser\MutatingScope), Object(Closure))
#25 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/src/Command/WorkerCommand.php(136):
PHPStan\Analyser\FileAnalyser->analyseFile('/path/to/...', Array, Object(PHPStan\Rules\LazyRegistry), Object(PHPStan\Collectors\Registry), NULL)
#26 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/vendor/evenement/evenement/src/EventEmitterTrait.php(111):
PHPStan\Command\WorkerCommand::PHPStan\Command\{closure}(Array)
#27 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/vendor/clue/ndjson-react/src/Decoder.php(117):
_PHPStan_2f712479f\Evenement\EventEmitter->emit('data', Array)
#28 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/vendor/evenement/evenement/src/EventEmitterTrait.php(111):
_PHPStan_2f712479f\Clue\React\NDJson\Decoder->handleData(Array)
#29 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/vendor/react/stream/src/Util.php(62):
_PHPStan_2f712479f\Evenement\EventEmitter->emit('data', Array)
#30 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/vendor/evenement/evenement/src/EventEmitterTrait.php(111):
_PHPStan_2f712479f\React\Stream\Util::_PHPStan_2f712479f\React\Stream\{closure}('{"action":"anal...')
#31 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/vendor/react/stream/src/DuplexResourceStream.php(168):
_PHPStan_2f712479f\Evenement\EventEmitter->emit('data', Array)
#32 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/vendor/react/event-loop/src/StreamSelectLoop.php(201):
_PHPStan_2f712479f\React\Stream\DuplexResourceStream->handleData(Resource id #5772)
#33 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/vendor/react/event-loop/src/StreamSelectLoop.php(173):
_PHPStan_2f712479f\React\EventLoop\StreamSelectLoop->waitForStreamActivity(NULL)
#34 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/src/Command/WorkerCommand.php(96):
_PHPStan_2f712479f\React\EventLoop\StreamSelectLoop->run()
#35 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/vendor/symfony/console/Command/Command.php(259):
PHPStan\Command\WorkerCommand->execute(Object(_PHPStan_2f712479f\Symfony\Component\Console\Input\ArgvInput),
Object(_PHPStan_2f712479f\Symfony\Component\Console\Output\ConsoleOutput))
#36 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/vendor/symfony/console/Application.php(870):
_PHPStan_2f712479f\Symfony\Component\Console\Command\Command->run(Object(_PHPStan_2f712479f\Symfony\Component\Console\Input\ArgvInput),
Object(_PHPStan_2f712479f\Symfony\Component\Console\Output\ConsoleOutput))
#37 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/vendor/symfony/console/Application.php(261):
_PHPStan_2f712479f\Symfony\Component\Console\Application->doRunCommand(Object(PHPStan\Command\WorkerCommand),
Object(_PHPStan_2f712479f\Symfony\Component\Console\Input\ArgvInput), Object(_PHPStan_2f712479f\Symfony\Component\Console\Output\ConsoleOutput))
#38 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/vendor/symfony/console/Application.php(157):
_PHPStan_2f712479f\Symfony\Component\Console\Application->doRun(Object(_PHPStan_2f712479f\Symfony\Component\Console\Input\ArgvInput),
Object(_PHPStan_2f712479f\Symfony\Component\Console\Output\ConsoleOutput))
#39 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/bin/phpstan(114):
_PHPStan_2f712479f\Symfony\Component\Console\Application->run()
#40 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/bin/phpstan(115): _PHPStan_2f712479f\{closure}()
#41 /path/to/vendor/phpstan/phpstan/phpstan(8): require('phar:///path/to/...')
#42 /path/to/vendor/bin/phpstan(119): include('/path/to/...')
#43 {main}
Letting PHPStan (2.x) analyze the following code/file leads to an internal error.
This is because PHPStan Laminas Framework assumes that properties in classes extending AbstractOptions are named a certain way, which is invalid because AbstractOptions only imposes naming conventions on property getters and setters but not underlying properties (which AbstractOptions does not know/access directly).
IMPORTANT NOTE
PHPStan Laminas Framework may check obvious candidates (queried property name, queried property name without underscores) first but since properties may be named "randomly", it must be prepared for those checks to fail and then provide some fallback handling.
Workaround: Rename properties
Code/file:
Error:
Stack trace: