Skip to content

Commit

Permalink
Merge pull request #277 from koriym/conditional_type
Browse files Browse the repository at this point in the history
Conditional type with phpstan
  • Loading branch information
koriym committed Jul 4, 2022
2 parents 5710172 + 709e6ad commit 1143ded
Show file tree
Hide file tree
Showing 28 changed files with 261 additions and 153 deletions.
6 changes: 4 additions & 2 deletions phpstan.neon
Expand Up @@ -9,5 +9,7 @@ parameters:
- tests/di/tmp/*
- tests/Di/tmp/*
- tests/di/Fake/*
checkGenericClassInNonGenericObjectType: false
checkMissingIterableValueType: false
ignoreErrors:
- '#Parameter \#1 \$interface of method Ray\\Di\\Injector::getInstance\(\) expects class-string\<\>, string given.#'
- '#contains generic class ReflectionAttribute but does not specify its types:#'
- '#generic class ReflectionAttribute but does not specify its types:#'
6 changes: 3 additions & 3 deletions psalm.xml
Expand Up @@ -6,10 +6,10 @@
xsi:schemaLocation="https://getpsalm.org/schema/config https://psalm.dev/schema/config"
>
<projectFiles>
<directory name="src/di" />
<directory name="tests/type" />
<directory name="src/di"/>
<directory name="tests/type"/>
</projectFiles>
<stubs>
<file name="tests/stub/BindInterface.phpstub"/>
</stubs>
</psalm>
<plugins><pluginClass class="Psalm\PhpUnitPlugin\Plugin"/></plugins></psalm>
1 change: 0 additions & 1 deletion src/di/AnnotatedClassMethods.php
Expand Up @@ -45,7 +45,6 @@ public function getConstructorName(ReflectionClass $class): Name

$named = $this->reader->getMethodAnnotation($constructor, Named::class);
if ($named instanceof Named) {
/** @var Named $named */
return new Name($named->value);
}

Expand Down
8 changes: 4 additions & 4 deletions src/di/Argument.php
Expand Up @@ -91,20 +91,20 @@ public function getMeta(): string
/**
* {@inheritDoc}
*/
public function serialize()
public function serialize(): ?string
{
return serialize($this->__serialize());
}

/**
* {@inheritDoc}
*
* @psalm-param string $serializedData
* @psalm-param string $data
*/
public function unserialize($serializedData)
public function unserialize($data): void
{
/** @var array{0: string, 1: bool, 2: string, 3: string, 4: string, 5: array{0: string, 1: string, 2:string}} $array */
$array = unserialize($serializedData, ['allowed_classes' => false]);
$array = unserialize($data, ['allowed_classes' => false]);
$this->__unserialize($array);
}

Expand Down
2 changes: 1 addition & 1 deletion src/di/AspectBind.php
Expand Up @@ -33,7 +33,7 @@ public function inject(Container $container): array
foreach ($interceptorClassNames as $interceptorClassName) {
/** @var class-string $interceptorClassName */
/** @psalm-suppress MixedAssignment */
$interceptor = $container->getInstance($interceptorClassName, Name::ANY);
$interceptor = $container->getInstance($interceptorClassName);
assert($interceptor instanceof MethodInterceptor);
$interceptors[] = $interceptor;
}
Expand Down
2 changes: 1 addition & 1 deletion src/di/AssistedInjectInterceptor.php
Expand Up @@ -47,7 +47,7 @@ public function invoke(MethodInvocation $invocation)
$namedArguments = $this->getNamedArguments($invocation);
foreach ($params as $param) {
/** @var list<ReflectionAttribute> $inject */
$inject = $param->getAttributes(InjectInterface::class, ReflectionAttribute::IS_INSTANCEOF);
$inject = $param->getAttributes(InjectInterface::class, ReflectionAttribute::IS_INSTANCEOF); // @phpstan-ignore-line
/** @var list<ReflectionAttribute> $assisted */
$assisted = $param->getAttributes(Assisted::class);
if (isset($assisted[0]) || isset($inject[0])) {
Expand Down
13 changes: 9 additions & 4 deletions src/di/AssistedInterceptor.php
Expand Up @@ -77,9 +77,8 @@ public function injectAssistedParameters(ReflectionMethod $method, Assisted $ass
$interface = $this->getInterface($type);
$name = $this->getName($method, $parameter);
$pos = $parameter->getPosition();
assert(class_exists($interface) || interface_exists($interface) || $interface === '');
/** @psalm-suppress MixedAssignment */
$arguments[$pos] = $this->injector->getInstance($interface, $name);
$arguments[$pos] = $this->injector->getInstance($interface, $name); // @phpstan-ignore-line
}

return $arguments;
Expand All @@ -104,8 +103,14 @@ private function getName(ReflectionMethod $method, ReflectionParameter $paramete
return Name::ANY;
}

private function getInterface(?ReflectionType $type): string
/**
* @return ''|class-string
*/
private function getInterface(?ReflectionType $type)
{
return $type instanceof ReflectionNamedType && ! in_array($type->getName(), Argument::UNBOUND_TYPE, true) ? $type->getName() : '';
$interface = $type instanceof ReflectionNamedType && ! in_array($type->getName(), Argument::UNBOUND_TYPE, true) ? $type->getName() : '';
assert($interface === '' || interface_exists($interface) || class_exists($interface));

return $interface;
}
}
4 changes: 1 addition & 3 deletions src/di/Container.php
Expand Up @@ -108,9 +108,7 @@ public function getDependency(string $index)
throw $this->unbound($index);
}

$dependency = $this->container[$index];

return $dependency->inject($this);
return $this->container[$index]->inject($this);
}

/**
Expand Down
5 changes: 3 additions & 2 deletions src/di/Di/Set.php
Expand Up @@ -11,18 +11,19 @@
* @Annotation
* @Target({"METHOD","PROPERTY"})
* @NamedArgumentConstructor()
* @template T of object
*/
#[Attribute(Attribute::TARGET_PARAMETER | Attribute::TARGET_PROPERTY)]
final class Set
{
/** @var ""|class-string */
/** @var ''|class-string<T> */
public $interface;

/** @var string */
public $name;

/**
* @param ""|class-string $interface
* @param ''|class-string<T> $interface
*/
public function __construct(string $interface, string $name = '')
{
Expand Down
2 changes: 1 addition & 1 deletion src/di/Grapher.php
Expand Up @@ -55,7 +55,7 @@ function (string $class): void {
* Build an object graph with give constructor parameters
*
* @param string $class class name
* @param array<int, mixed> $params constuct paramteters
* @param array<int, mixed> $params construct parameters
*
* @return mixed
*/
Expand Down
11 changes: 4 additions & 7 deletions src/di/InjectionPoint.php
Expand Up @@ -115,23 +115,20 @@ public function __unserialize(array $array): void
[$this->reader, $this->pClass, $this->pFunction, $this->pName] = $array;
}

/**
* {@inheritDoc}
*/
public function serialize()
public function serialize(): ?string
{
return serialize($this->__serialize());
}

/**
* {@inheritDoc}
*
* @psalm-param string $serializedData
* @psalm-param string $data
*/
public function unserialize($serializedData)
public function unserialize($data): void
{
/** @var array{0: Reader, 1: string, 2: string, 3: string} $array */
$array = unserialize($serializedData, ['allowed_classes' => [Reader::class]]);
$array = unserialize($data, ['allowed_classes' => [Reader::class]]);
$this->__unserialize($array);
}
}
2 changes: 1 addition & 1 deletion src/di/Injector.php
Expand Up @@ -81,6 +81,6 @@ private function bind(string $class): void
new Bind($this->container, $class);
$bound = $this->container->getContainer()[$class . '-' . Name::ANY];
assert($bound instanceof Dependency);
$this->container->weaveAspect(new Compiler($this->classDir), $bound)->getInstance($class, Name::ANY);
$this->container->weaveAspect(new Compiler($this->classDir), $bound)->getInstance($class);
}
}
14 changes: 5 additions & 9 deletions src/di/InjectorInterface.php
Expand Up @@ -16,16 +16,12 @@ interface InjectorInterface
/**
* Return object graph
*
* @param class-string<T> $interface interface name|class name|empty-string
* @param string $name interface name space
* @psalm-param ''|class-string<T> $interface
* @phpstan-param ''|class-string $interface
* @param ''|class-string<T> $interface
* @param string $name
*
* @return T
* @psalm-return (T is object ? T : mixed)
* @phpstan-return mixed
* @return ($interface is '' ? mixed : T)
*
* @psalm-template T of object
* @template T of object
*/
public function getInstance($interface, $name = Name::ANY); // @phpstan-ignore-line
public function getInstance($interface, $name = Name::ANY);
}
8 changes: 4 additions & 4 deletions src/di/MethodInvocationProvider.php
Expand Up @@ -7,6 +7,9 @@
use Ray\Aop\MethodInvocation;
use Ray\Di\Exception\MethodInvocationNotAvailable;

/**
* @implements ProviderInterface<MethodInvocation>
*/
final class MethodInvocationProvider implements ProviderInterface
{
/** @var ?MethodInvocation */
Expand All @@ -17,10 +20,7 @@ public function set(MethodInvocation $invocation): void
$this->invocation = $invocation;
}

/**
* @return MethodInvocation
*/
public function get()
public function get(): MethodInvocation
{
if ($this->invocation === null) {
throw new MethodInvocationNotAvailable();
Expand Down
4 changes: 3 additions & 1 deletion src/di/MultiBinder.php
Expand Up @@ -63,7 +63,9 @@ public function to(string $class): void
}

/**
* @param class-string<ProviderInterface> $provider
* @param class-string<ProviderInterface<T>> $provider
*
* @template T of mixed
*/
public function toProvider(string $provider): void
{
Expand Down
1 change: 1 addition & 0 deletions src/di/MultiBinding/Map.php
Expand Up @@ -19,6 +19,7 @@
/**
* @template T
* @implements ArrayAccess<array-key, T>
* @implements IteratorAggregate<string, mixed>
*/
final class Map implements IteratorAggregate, ArrayAccess, Countable
{
Expand Down
13 changes: 11 additions & 2 deletions src/di/MultiBinding/MapProvider.php
Expand Up @@ -11,6 +11,9 @@
use Ray\Di\InjectorInterface;
use Ray\Di\ProviderInterface;

/**
* @implements ProviderInterface<Map>
*/
final class MapProvider implements ProviderInterface
{
/** @var MultiBindings */
Expand All @@ -22,9 +25,12 @@ final class MapProvider implements ProviderInterface
/** @var InjectorInterface */
private $injector;

/** @var ParamReaderInterface */
/** @var ParamReaderInterface<object> */
private $reader;

/**
* @param ParamReaderInterface<object> $reader
*/
public function __construct(
InjectionPointInterface $ip,
MultiBindings $multiBindings,
Expand All @@ -37,9 +43,12 @@ public function __construct(
$this->reader = $reader;
}

/**
* @return Map<mixed>
*/
public function get(): Map
{
/** @var ?Set $set */
/** @var ?Set<object> $set */
$set = $this->reader->getParametrAnnotation($this->ip->getParameter(), Set::class);
if ($set === null) {
throw new SetNotFound((string) $this->ip->getParameter());
Expand Down
8 changes: 5 additions & 3 deletions src/di/Name.php
Expand Up @@ -86,18 +86,20 @@ public static function withAttributes(ReflectionMethod $method): ?self
* @param non-empty-array<ReflectionAttribute> $attributes
*
* @throws ReflectionException
*
* @psalm-suppress MixedAssignment
* @psalm-suppress MixedArgument
*/
private static function getName(array $attributes): string
{
$refAttribute = $attributes[0];
/** @var Named|object $attribute */
$attribute = $refAttribute->newInstance();
if ($attribute instanceof Named) {
return $attribute->value;
}

$isQualifer = (bool) (new ReflectionClass($attribute))->getAttributes(Qualifier::class);
if ($isQualifer) {
$isQualifier = (bool) (new ReflectionClass($attribute))->getAttributes(Qualifier::class);
if ($isQualifier) {
return get_class($attribute);
}

Expand Down
19 changes: 15 additions & 4 deletions src/di/ProviderSetProvider.php
Expand Up @@ -8,6 +8,10 @@
use Ray\Di\Di\Set;
use Ray\Di\Exception\SetNotFound;

/**
* @implements ProviderInterface<mixed>
* @template T of object
*/
final class ProviderSetProvider implements ProviderInterface
{
/** @var InjectionPointInterface */
Expand All @@ -16,9 +20,12 @@ final class ProviderSetProvider implements ProviderInterface
/** @var InjectorInterface */
private $injector;

/** @var ParamReaderInterface */
/** @var ParamReaderInterface<T> */
private $reader;

/**
* @param ParamReaderInterface<T> $reader
*/
public function __construct(
InjectionPointInterface $ip,
InjectorInterface $injector,
Expand All @@ -34,8 +41,9 @@ public function __construct(
*/
public function get()
{
/** @var ?Set $set */
$set = $this->reader->getParametrAnnotation($this->ip->getParameter(), Set::class);
$param = $this->ip->getParameter();
/** @var ?Set<object> $set */
$set = $this->reader->getParametrAnnotation($param, Set::class); // @phpstan-ignore-line
if ($set === null) {
throw new SetNotFound((string) $this->ip->getParameter());
}
Expand All @@ -45,9 +53,12 @@ public function get()
/** @var InjectorInterface */
private $injector;

/** @var Set */
/** @var Set<object> */
private $set;

/**
* @param Set<object> $set
*/
public function __construct(InjectorInterface $injector, Set $set)
{
$this->injector = $injector;
Expand Down

0 comments on commit 1143ded

Please sign in to comment.