Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM php:8.3
FROM php:8.2

ADD --chmod=0755 https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions /usr/local/bin/

Expand Down
16 changes: 8 additions & 8 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,23 +19,23 @@
],
"require": {
"php": "~8.2.0 || ~8.3.0",
"phpstan/phpstan": "^1.12.4"
"phpstan/phpstan": "^2.0.1"
},
"require-dev": {
"laminas/laminas-cache": "^3.12.2",
"laminas/laminas-cache-storage-adapter-memory": "^2.3.0",
"laminas/laminas-filter": "^2.37.0",
"laminas/laminas-form": "^3.20.1",
"laminas/laminas-filter": "^2.39.0",
"laminas/laminas-form": "^3.21.0",
"laminas/laminas-hydrator": "^4.15.0",
"laminas/laminas-i18n": "^2.28.1",
"laminas/laminas-i18n": "^2.29.0",
"laminas/laminas-inputfilter": "^2.30.1",
"laminas/laminas-mail": "^2.25.1",
"laminas/laminas-mvc": "^3.7.0",
"laminas/laminas-paginator": "^2.18.1",
"laminas/laminas-paginator": "^2.19.0",
"laminas/laminas-validator": "^2.64.1",
"phpstan/phpstan-deprecation-rules": "^1.2.1",
"phpstan/phpstan-phpunit": "^1.4.0",
"phpunit/phpunit": "^9.6.21",
"phpstan/phpstan-deprecation-rules": "^2",
"phpstan/phpstan-phpunit": "^2",
"phpunit/phpunit": "^11.4.3",
"slam/php-cs-fixer-extensions": "^3.11.1"
},
"conflict": {
Expand Down
27 changes: 26 additions & 1 deletion phpstan-baseline.neon
Original file line number Diff line number Diff line change
@@ -1,6 +1,31 @@
parameters:
ignoreErrors:
-
message: "#^Call to static method PHPUnit\\\\Framework\\\\Assert\\:\\:assertInstanceOf\\(\\) with 'LaminasPhpStan\\\\\\\\TestAsset\\\\\\\\BarService' and Laminas\\\\Stdlib\\\\DispatchableInterface will always evaluate to false\\.$#"
message: '#^Creating new ReflectionClass is a runtime reflection concept that might not work in PHPStan because it uses fully static reflection engine\. Use objects retrieved from ReflectionProvider instead\.$#'
identifier: phpstanApi.runtimeReflection
count: 1
path: src/Rules/Laminas/ServiceManagerGetMethodCallRule.php

-
message: '#^Parameter \#1 \$config of method Laminas\\ServiceManager\\ServiceManager\:\:configure\(\) expects array\{abstract_factories\?\: array\<class\-string\<Laminas\\ServiceManager\\Factory\\AbstractFactoryInterface\>\|Laminas\\ServiceManager\\Factory\\AbstractFactoryInterface\>, aliases\?\: array\<string, string\>, delegators\?\: mixed, factories\?\: mixed, initializers\?\: mixed, invokables\?\: array\<string, string\>, lazy_services\?\: array\{class_map\?\: array\<string, class\-string\>, proxies_namespace\?\: non\-empty\-string, proxies_target_dir\?\: non\-empty\-string, write_proxy_files\?\: bool\}, services\?\: array\<string, array\<mixed\>\|object\>, \.\.\.\}, mixed given\.$#'
identifier: argument.type
count: 1
path: src/ServiceManagerLoader.php

-
message: '#^Parameter \#1 \$config of method Laminas\\ServiceManager\\ServiceManager\:\:configure\(\) expects array\{abstract_factories\?\: array\<class\-string\<Laminas\\ServiceManager\\Factory\\AbstractFactoryInterface\>\|Laminas\\ServiceManager\\Factory\\AbstractFactoryInterface\>, aliases\?\: array\<string, string\>, delegators\?\: mixed, factories\?\: mixed, initializers\?\: mixed, invokables\?\: array\<string, string\>, lazy_services\?\: array\{class_map\?\: array\<string, class\-string\>, proxies_namespace\?\: non\-empty\-string, proxies_target_dir\?\: non\-empty\-string, write_proxy_files\?\: bool\}, services\?\: array\<string, array\<mixed\>\|object\>, \.\.\.\}, non\-empty\-array given\.$#'
identifier: argument.type
count: 1
path: src/ServiceManagerLoader.php

-
message: '#^Creating new ReflectionClass is a runtime reflection concept that might not work in PHPStan because it uses fully static reflection engine\. Use objects retrieved from ReflectionProvider instead\.$#'
identifier: phpstanApi.runtimeReflection
count: 2
path: src/Type/Laminas/AbstractServiceLocatorGetDynamicReturnTypeExtension.php

-
message: '#^Call to static method PHPUnit\\Framework\\Assert\:\:assertInstanceOf\(\) with ''LaminasPhpStan\\\\TestAsset\\\\BarService'' and Laminas\\Stdlib\\DispatchableInterface will always evaluate to false\.$#'
identifier: staticMethod.impossibleType
count: 1
path: tests/Type/Laminas/ServiceManagerLoaderTest.php
10 changes: 5 additions & 5 deletions phpunit.xml
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
<?xml version="1.0"?>
<phpunit
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
bootstrap="./vendor/autoload.php"
colors="true"
verbose="true"
xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"
>
<coverage>
<include>
<directory suffix=".php">./src</directory>
</include>
<report>
<text outputFile="php://stdout" showOnlySummary="true"/>
</report>
</coverage>
<testsuite name="LaminasPhpStan">
<directory>./tests</directory>
</testsuite>
<source>
<include>
<directory>./src</directory>
</include>
</source>
</phpunit>
17 changes: 5 additions & 12 deletions src/Rules/Laminas/ServiceManagerGetMethodCallRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@
use LaminasPhpStan\ServiceManagerLoader;
use LaminasPhpStan\Type\Laminas\ObjectServiceManagerType;
use PhpParser\Node;
use PhpParser\Node\Arg;
use PhpParser\Node\Expr\MethodCall;
use PHPStan\Analyser\Scope;
use PHPStan\Reflection\ReflectionProvider;
use PHPStan\Rules\Rule;
use PHPStan\Rules\RuleErrorBuilder;
use PHPStan\Type\ObjectType;
use PHPStan\Type\Type;
use Psr\Container\ContainerInterface as PsrContainerInterface;
Expand All @@ -35,22 +35,15 @@ public function getNodeType(): string
return MethodCall::class;
}

/**
* @param MethodCall $node
*
* @return string[]
*/
/** @param MethodCall $node */
public function processNode(Node $node, Scope $scope): array
{
$args = $node->getArgs();
if (1 !== \count($args)) {
return [];
}

$firstArg = $args[0];
if (! $firstArg instanceof Arg) {
return [];
}
$firstArg = $args[0];
$argType = $scope->getType($firstArg->value);
$constantStrings = $argType->getConstantStrings();
if (1 !== \count($constantStrings)) {
Expand Down Expand Up @@ -92,14 +85,14 @@ public function processNode(Node $node, Scope $scope): array
}
}

return [\sprintf(
return [RuleErrorBuilder::message(\sprintf(
'The service "%s" was not configured in %s%s.',
$serviceName,
$calledOnType instanceof ObjectServiceManagerType
? $calledOnType->getServiceName()
: $calledOnType->getClassName(),
$classDoesNotExistNote
)];
))->identifier('servicemanager.servicenotconfigured')->build()];
}

/** @phpstan-assert-if-true ObjectType $type */
Expand Down
4 changes: 1 addition & 3 deletions src/ServiceManagerLoader.php
Original file line number Diff line number Diff line change
Expand Up @@ -67,12 +67,10 @@ public function getServiceLocator(string $serviceManagerName): ServiceLocatorInt
}
if (\class_exists(ServiceListenerFactory::class)) {
$refProp = new ReflectionProperty(ServiceListenerFactory::class, 'defaultServiceConfig');
$refProp->setAccessible(true);
$config = $refProp->getValue(new ServiceListenerFactory());
$config = $refProp->getValue(new ServiceListenerFactory());
\assert(\is_array($config));
\assert(\is_array($config['factories']));
unset($config['factories']['config']);
$refProp->setAccessible(false);
$serviceManager->configure($config);
}
foreach ($this->knownModules as $module) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,10 @@

use Laminas\ServiceManager\AbstractPluginManager;
use LaminasPhpStan\ServiceManagerLoader;
use PhpParser\Node\Arg;
use PhpParser\Node\Expr\MethodCall;
use PHPStan\Analyser\Scope;
use PHPStan\Reflection\MethodReflection;
use PHPStan\Reflection\ReflectionProvider;
use PHPStan\ShouldNotHappenException;
use PHPStan\Type\DynamicMethodReturnTypeExtension;
use PHPStan\Type\MixedType;
use PHPStan\Type\NeverType;
Expand Down Expand Up @@ -48,15 +46,7 @@ final public function getTypeFromMethodCall(

$serviceManager = $this->serviceManagerLoader->getServiceLocator($calledOnType->getObjectClassNames()[0]);

$firstArg = $args[0];
if (! $firstArg instanceof Arg) {
throw new ShouldNotHappenException(\sprintf(
'Argument passed to %s::%s should be a string, %s given',
$methodReflection->getDeclaringClass()->getName(),
$methodReflection->getName(),
$firstArg->getType()
));
}
$firstArg = $args[0];
$argType = $scope->getType($firstArg->value);
$constantStringTypes = $argType->getConstantStrings();
if (1 !== \count($constantStringTypes)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
namespace LaminasPhpStan\Type\Laminas\PluginMethodDynamicReturnTypeExtension;

use LaminasPhpStan\ServiceManagerLoader;
use PhpParser\Node\Arg;
use PhpParser\Node\Expr\MethodCall;
use PHPStan\Analyser\Scope;
use PHPStan\Reflection\MethodReflection;
Expand Down Expand Up @@ -36,17 +35,9 @@ final public function getTypeFromMethodCall(
Scope $scope
): Type {
$firstArg = $methodCall->getArgs()[0];
if (! $firstArg instanceof Arg) {
throw new ShouldNotHappenException(\sprintf(
'Argument passed to %s::%s should be a string, %s given',
$methodReflection->getDeclaringClass()->getName(),
$methodReflection->getName(),
$firstArg->getType()
));
}
$argType = $scope->getType($firstArg->value);
$strings = $argType->getConstantStrings();
$plugin = 1 === \count($strings) ? $strings[0]->getValue() : null;
$argType = $scope->getType($firstArg->value);
$strings = $argType->getConstantStrings();
$plugin = 1 === \count($strings) ? $strings[0]->getValue() : null;

if (null !== $plugin) {
$pluginManager = $this->serviceManagerLoader->getServiceLocator($this->getPluginManagerName());
Expand Down
4 changes: 2 additions & 2 deletions tests/LaminasIntegration/IntegrationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
*/
final class IntegrationTest extends LevelsTestCase
{
public function dataTopics(): array
public static function dataTopics(): array
{
return [
['serviceManagerDynamicReturn'],
Expand All @@ -36,7 +36,7 @@ public function getPhpStanExecutablePath(): string
return __DIR__ . '/../../vendor/bin/phpstan';
}

public function getPhpStanConfigPath(): ?string
public function getPhpStanConfigPath(): string
{
return __DIR__ . '/phpstan.neon';
}
Expand Down
7 changes: 7 additions & 0 deletions tests/LaminasIntegration/data/controllerPluginMethod-10.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[
{
"message": "Cannot call method isFoo() on mixed.",
"line": 16,
"ignorable": true
}
]
12 changes: 12 additions & 0 deletions tests/LaminasIntegration/data/serviceManagerDynamicReturn-4.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[
{
"message": "Expression \"(static function (\\LaminasPhpStan\\TestAsset\\FooService $fooService): void {…\" on a separate line does not do anything.",
"line": 52,
"ignorable": true
},
{
"message": "Expression \"(static function (\\LaminasPhpStan\\TestAsset\\FooInterface $fooService): void {…\" on a separate line does not do anything.",
"line": 55,
"ignorable": true
}
]
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"ignorable": true
},
{
"message": "Property class@anonymous/stdlibAbstractOptionsProperties.php:27::$myxyz is not writable.",
"message": "Property Laminas\\Stdlib\\AbstractOptions@anonymous/stdlibAbstractOptionsProperties.php:27::$myxyz is not writable.",
"line": 37,
"ignorable": true
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[
{
"message": "Property class@anonymous/stdlibAbstractOptionsProperties.php:27::$myxyz is never read, only written.",
"message": "Property Laminas\\Stdlib\\AbstractOptions@anonymous/stdlibAbstractOptionsProperties.php:27::$myxyz is never read, only written.",
"line": 28,
"ignorable": true
}
Expand Down
7 changes: 3 additions & 4 deletions tests/Type/Laminas/PluginMethodReflectionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,20 @@
namespace LaminasPhpStan\Tests\Type\Laminas;

use LaminasPhpStan\Type\Laminas\PluginMethodReflection;
use PHPStan\Reflection\ClassReflection;
use PHPStan\Reflection\FunctionVariant;
use PHPStan\Testing\PHPStanTestCase;
use PHPStan\TrinaryLogic;
use PHPStan\Type\ObjectType;
use PHPUnit\Framework\TestCase;
use stdClass;

/**
* @covers \LaminasPhpStan\Type\Laminas\PluginMethodReflection
*/
final class PluginMethodReflectionTest extends TestCase
final class PluginMethodReflectionTest extends PHPStanTestCase
{
public function testTrivialUsage(): void
{
$declaringClass = $this->createMock(ClassReflection::class);
$declaringClass = $this->createReflectionProvider()->getClass(stdClass::class);
$methodName = 'redirect';
$returnType = new ObjectType(stdClass::class);

Expand Down