-
-
Notifications
You must be signed in to change notification settings - Fork 78
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
7 changed files
with
242 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
<?php declare(strict_types=1); | ||
|
||
namespace mglaman\PHPStanDrupal\Drupal; | ||
|
||
use PHPStan\ShouldNotHappenException; | ||
|
||
final class ServiceMapStaticAccessor | ||
{ | ||
/** | ||
* @var \mglaman\PHPStanDrupal\Drupal\ServiceMap | ||
*/ | ||
private static $instance; | ||
|
||
private function __construct() | ||
{ | ||
} | ||
|
||
public static function registerInstance(ServiceMap $serviceMap): void | ||
{ | ||
self::$instance = $serviceMap; | ||
} | ||
|
||
public static function getInstance(): ServiceMap | ||
{ | ||
if (self::$instance === null) { | ||
throw new ShouldNotHappenException(); | ||
} | ||
|
||
return self::$instance; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
<?php declare(strict_types=1); | ||
|
||
namespace mglaman\PHPStanDrupal\Type; | ||
|
||
use mglaman\PHPStanDrupal\Drupal\ServiceMapStaticAccessor; | ||
use PHPStan\TrinaryLogic; | ||
use PHPStan\Type\CompoundType; | ||
use PHPStan\Type\Constant\ConstantStringType; | ||
use PHPStan\Type\StringType; | ||
use PHPStan\Type\Type; | ||
use PHPStan\Type\VerbosityLevel; | ||
|
||
final class ServiceIdType extends \PHPStan\Type\StringType | ||
{ | ||
public function __construct() | ||
{ | ||
parent::__construct(); | ||
} | ||
|
||
public function describe(VerbosityLevel $level): string | ||
{ | ||
return 'service-id-string'; | ||
} | ||
|
||
public function accepts(Type $type, bool $strictTypes): TrinaryLogic | ||
{ | ||
if ($type instanceof CompoundType) { | ||
return $type->isAcceptedBy($this, $strictTypes); | ||
} | ||
|
||
if ($type instanceof ConstantStringType) { | ||
$serviceDefinition = ServiceMapStaticAccessor::getInstance()->getService($type->getValue()); | ||
if ($serviceDefinition !== null) { | ||
return TrinaryLogic::createYes(); | ||
} | ||
// Some services may be dynamically defined, so return maybe. | ||
return TrinaryLogic::createMaybe(); | ||
} | ||
|
||
if ($type instanceof self) { | ||
return TrinaryLogic::createYes(); | ||
} | ||
|
||
if ($type instanceof StringType) { | ||
return TrinaryLogic::createMaybe(); | ||
} | ||
|
||
return TrinaryLogic::createNo(); | ||
} | ||
|
||
public function isSuperTypeOf(Type $type): TrinaryLogic | ||
{ | ||
if ($type instanceof ConstantStringType) { | ||
$serviceDefinition = ServiceMapStaticAccessor::getInstance()->getService($type->getValue()); | ||
if ($serviceDefinition !== null) { | ||
return TrinaryLogic::createYes(); | ||
} | ||
// Some services may be dynamically defined, so return maybe. | ||
return TrinaryLogic::createMaybe(); | ||
} | ||
|
||
if ($type instanceof self) { | ||
return TrinaryLogic::createYes(); | ||
} | ||
|
||
if ($type instanceof parent) { | ||
return TrinaryLogic::createMaybe(); | ||
} | ||
|
||
if ($type instanceof CompoundType) { | ||
return $type->isSubTypeOf($this); | ||
} | ||
|
||
return TrinaryLogic::createNo(); | ||
} | ||
|
||
/** | ||
* @param mixed[] $properties | ||
*/ | ||
public static function __set_state(array $properties) : \PHPStan\Type\Type | ||
{ | ||
return new self(); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
<?php declare(strict_types=1); | ||
|
||
namespace mglaman\PHPStanDrupal\Type; | ||
|
||
use PHPStan\Analyser\NameScope; | ||
use PHPStan\PhpDoc\TypeNodeResolverExtension; | ||
use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode; | ||
use PHPStan\PhpDocParser\Ast\Type\TypeNode; | ||
|
||
final class ServiceIdTypeNodeResolverExtension implements TypeNodeResolverExtension | ||
{ | ||
|
||
public function resolve(TypeNode $typeNode, NameScope $nameScope): ?\PHPStan\Type\Type | ||
{ | ||
if ($typeNode instanceof IdentifierTypeNode && $typeNode->__toString() === 'service-id-string') { | ||
return new \mglaman\PHPStanDrupal\Type\ServiceIdType(); | ||
} | ||
|
||
return null; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
<?php | ||
|
||
class Drupal { | ||
|
||
/** | ||
* @var \Symfony\Component\DependencyInjection\ContainerInterface|null | ||
*/ | ||
protected static $container; | ||
|
||
/** | ||
* @phpstan-param service-id-string $id | ||
*/ | ||
public static function service($id) { | ||
return static::getContainer()->get($id); | ||
} | ||
|
||
/** | ||
* @phpstan-param service-id-string $id | ||
*/ | ||
public static function hasService($id): bool { | ||
// Check hasContainer() first in order to always return a Boolean. | ||
return static::hasContainer() && static::getContainer()->has($id); | ||
} | ||
|
||
/** | ||
* @return \Symfony\Component\DependencyInjection\ContainerInterface | ||
*/ | ||
public static function getContainer(): \Symfony\Component\DependencyInjection\ContainerInterface { | ||
return static::$container; | ||
} | ||
|
||
public static function hasContainer(): bool { | ||
return static::$container !== NULL; | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
<?php declare(strict_types=1); | ||
|
||
namespace mglaman\PHPStanDrupal\Tests\Type; | ||
|
||
use Drupal\Core\Entity\EntityTypeManager; | ||
use mglaman\PHPStanDrupal\Drupal\ServiceMap; | ||
use mglaman\PHPStanDrupal\Drupal\ServiceMapStaticAccessor; | ||
use mglaman\PHPStanDrupal\Tests\AdditionalConfigFilesTrait; | ||
use PHPStan\Testing\PHPStanTestCase; | ||
use PHPStan\TrinaryLogic; | ||
use PHPStan\Type\Constant\ConstantStringType; | ||
use PHPStan\Type\StringType; | ||
use PHPStan\Type\Type; | ||
use PHPStan\Type\VerbosityLevel; | ||
|
||
final class ServiceIdTypeTest extends PHPStanTestCase { | ||
|
||
use AdditionalConfigFilesTrait; | ||
|
||
public function dataAccepts(): iterable | ||
{ | ||
yield 'self' => [ | ||
new \mglaman\PHPStanDrupal\Type\ServiceIdType(), | ||
new \mglaman\PHPStanDrupal\Type\ServiceIdType(), | ||
TrinaryLogic::createYes(), | ||
]; | ||
yield 'valid service' => [ | ||
new \mglaman\PHPStanDrupal\Type\ServiceIdType(), | ||
new ConstantStringType('entity_type.manager'), | ||
TrinaryLogic::createYes(), | ||
]; | ||
yield 'invalid service' => [ | ||
new \mglaman\PHPStanDrupal\Type\ServiceIdType(), | ||
new ConstantStringType('foo.manager'), | ||
TrinaryLogic::createMaybe(), | ||
]; | ||
yield 'generic string' => [ | ||
new \mglaman\PHPStanDrupal\Type\ServiceIdType(), | ||
new StringType(), | ||
TrinaryLogic::createMaybe(), | ||
]; | ||
} | ||
|
||
/** | ||
* @dataProvider dataAccepts | ||
*/ | ||
public function testAccepts(\mglaman\PHPStanDrupal\Type\ServiceIdType $type, Type $otherType, TrinaryLogic $expectedResult): void | ||
{ | ||
$serviceMap = new ServiceMap(); | ||
$serviceMap->setDrupalServices([ | ||
'entity_type.manager' => [ | ||
'class' => EntityTypeManager::class | ||
], | ||
]); | ||
ServiceMapStaticAccessor::registerInstance($serviceMap); | ||
$actualResult = $type->accepts($otherType, true); | ||
self::assertSame( | ||
$expectedResult->describe(), | ||
$actualResult->describe(), | ||
sprintf('%s -> accepts(%s)', $type->describe(VerbosityLevel::precise()), $otherType->describe(VerbosityLevel::precise())), | ||
); | ||
} | ||
|
||
} |