Skip to content
Open
Show file tree
Hide file tree
Changes from 3 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 .github/workflows/continuous-integration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@ jobs:
ci:
uses: ray-di/.github/.github/workflows/continuous-integration.yml@v1
with:
old_stable: '["7.2", "7.3", "7.4", "8.0", "8.1", "8.2", "8.3", "8.4"]'
old_stable: '["8.2", "8.3", "8.4"]'
current_stable: 8.5
6 changes: 2 additions & 4 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,12 @@
],
"require": {
"php": "^7.2 || ^8.0",
"doctrine/annotations": "^1.8 || ^2.0",
"doctrine/cache": "^1.10 || ^2.1",
"koriym/attributes": "^1.0",
"koriym/null-object": "^1.0",
"koriym/param-reader": "^1.0",
"koriym/printo": "^1.0",
"ray/aop": "^2.14",
"ray/di": "^2.18",
"ray/aop": "dev-php82",
"ray/di": "dev-php82",
"symfony/polyfill-php83": "^1.33"
},
"require-dev": {
Expand Down
4 changes: 2 additions & 2 deletions src/CompiledInjector.php
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,8 @@ final class CompiledInjector implements ScriptInjectorInterface
* @psalm-suppress UnresolvableInclude
* @ScriptDir
*/
#[ScriptDir]
public function __construct(string $scriptDir)
public function __construct(#[ScriptDir]
string $scriptDir)
{
$realPath = realpath($scriptDir);
if ($realPath === false || ! is_dir($realPath) || ! is_readable($realPath)) {
Expand Down
50 changes: 41 additions & 9 deletions src/InjectionPoint.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,13 @@
use Ray\Aop\ReflectionMethod;
use Ray\Di\Di\Qualifier;
use Ray\Di\InjectionPointInterface;
use Ray\ServiceLocator\ServiceLocator;
use ReflectionAttribute;
use ReflectionException;
use ReflectionParameter;

use function assert;
use function class_exists;
use function count;

/**
* @psalm-import-type ScriptDir from Types
Expand Down Expand Up @@ -95,20 +96,51 @@ public function getQualifiers(): array
* {@inheritDoc}
*
* @return object|null
*
* @throws ReflectionException
*/
public function getQualifier()
{
$reader = ServiceLocator::getReader();
$annotations = $reader->getMethodAnnotations($this->getMethod());
foreach ($annotations as $annotation) {
$maybeQualifers = $reader->getClassAnnotations(new \ReflectionClass($annotation));
foreach ($maybeQualifers as $maybeQualifer) {
if ($maybeQualifer instanceof Qualifier) {
return $annotation;
}
// Try method attributes first
$parameter = $this->getParameter();
$class = $parameter->getDeclaringClass();
$methodName = $parameter->getDeclaringFunction()->getShortName();
assert($class instanceof \ReflectionClass);

$nativeMethod = new \ReflectionMethod($class->getName(), $methodName);
$methodAttributes = $nativeMethod->getAttributes();

foreach ($methodAttributes as $attribute) {
if ($this->isQualifier($attribute)) {
return $attribute->newInstance();
}
}

// Try parameter attributes
$paramAttributes = $parameter->getAttributes();

foreach ($paramAttributes as $attribute) {
if ($this->isQualifier($attribute)) {
return $attribute->newInstance();
}
}

return null;
}

/**
* @phpstan-param ReflectionAttribute<object> $attribute
*
* @throws ReflectionException
*
* @psalm-suppress TooManyTemplateParams
*/
private function isQualifier(ReflectionAttribute $attribute): bool
{
$attributeClass = $attribute->getName();
$reflectionClass = new \ReflectionClass($attributeClass);
$classAttributes = $reflectionClass->getAttributes(Qualifier::class);

return count($classAttributes) > 0;
}
}
6 changes: 4 additions & 2 deletions tests/CompiledInjectorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,10 @@ public function testCompile(): void
$this->assertFileExists(__DIR__ . '/tmp/-Ray_Compiler_Annotation_Compile.php');
$this->assertFileExists(__DIR__ . '/tmp/-Ray_Di_Annotation_ScriptDir.php');
$this->assertFileExists(__DIR__ . '/tmp/Ray_Aop_MethodInvocation-.php');
$this->assertFileExists(__DIR__ . '/tmp/Koriym_ParamReader_ParamReaderInterface-.php');
$this->assertFileExists(__DIR__ . '/tmp/Ray_Di_AssistedInterceptor-.php');
// Note: Koriym_ParamReader_ParamReaderInterface is not generated in php82-dev branch
// $this->assertFileExists(__DIR__ . '/tmp/Koriym_ParamReader_ParamReaderInterface-.php');
// Note: AssistedInterceptor renamed to AssistedInjectInterceptor in php82-dev branch
$this->assertFileExists(__DIR__ . '/tmp/Ray_Di_AssistedInjectInterceptor-.php');
$this->assertFileExists(__DIR__ . '/tmp/Ray_Di_InjectorInterface-.php');
$this->assertFileExists(__DIR__ . '/tmp/Ray_Di_MethodInvocationProvider-.php');
$this->assertFileExists(__DIR__ . '/tmp/Ray_Di_ProviderInterface-.php');
Expand Down
24 changes: 11 additions & 13 deletions tests/Fake/Assisted/FakeAssistedConsumer.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,32 +11,30 @@ class FakeAssistedConsumer
{
/**
* @return FakeRobotInterface|null
*
* @Assisted({"robot"})
*/
public function assistOne($a, $b, ?FakeRobotInterface $robot = null)
public function assistOne(
$a,
$b,
#[Assisted] ?FakeRobotInterface $robot = null)
{
return $robot;
}

/**
* @Assisted({"var1"})
* @Named("var1=one")
*/
public function assistWithName($a, $var1 = null)
public function assistWithName(
$a,
#[Named("one")] #[Assisted] $var1 = null)
{
return $var1;
}

/**
* @return (FakeRobotInterface|mixed|null)[]
* @psalm-return array{0: mixed, 1: FakeRobotInterface|null}
*
* @Assisted({"var2", "robot"})
* @Named("var2=one")
*/
public function assistAny($var2 = null, ?FakeRobotInterface $robot = null)
{
public function assistAny(
#[Named("one")] #[Assisted] $var2 = null,
#[Assisted] ?FakeRobotInterface $robot = null
) {
return [$var2, $robot];
}
}
4 changes: 1 addition & 3 deletions tests/Fake/Assisted/FakeAssistedParamsConsumer.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,8 @@ class FakeAssistedParamsConsumer
{
/**
* @return array [int, FakeAbstractDb]
*
* @Assisted({"db"})
*/
public function getUser($id, ?FakeAbstractDb $db = null)
public function getUser($id, #[Assisted] ?FakeAbstractDb $db = null)
{
return [$id, $db];
}
Expand Down
38 changes: 12 additions & 26 deletions tests/Fake/FakeCar.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,54 +24,42 @@ class FakeCar implements FakeCarInterface
public $handle;
public $null = false;

/**
* @Inject
*/
#[Inject]
public function setTires(FakeTyreInterface $frontTyre, FakeTyreInterface $rearTyre, $null = null)
{
$this->frontTyre = $frontTyre;
$this->rearTyre = $rearTyre;
$this->null = $null;
}

/**
* @Inject(optional=true)
*/
#[Inject(optional: true)]
public function setHardtop(FakeHardtopInterface $hardtop)
{
$this->hardtop = $hardtop;
}

/**
* @Inject
* @Named("rightMirror=right,leftMirror=left")
*/
public function setMirrors(FakeMirrorInterface $rightMirror, FakeMirrorInterface $leftMirror)
{
#[Inject]
public function setMirrors(
#[Named('right')] FakeMirrorInterface $rightMirror,
#[Named('left')] FakeMirrorInterface $leftMirror
) {
$this->rightMirror = $rightMirror;
$this->leftMirror = $leftMirror;
}

/**
* @Inject
* @Named("right")
*/
public function setSpareMirror(FakeMirrorInterface $rightMirror)
#[Inject]
public function setSpareMirror(#[Named('right')] FakeMirrorInterface $rightMirror)
{
$this->spareMirror = $rightMirror;
}

/**
* @Inject
*/
#[Inject]
public function setHandle(FakeHandleInterface $handle)
{
$this->handle = $handle;
}

/**
* @Inject
*/
#[Inject]
public function setOil(FakeOilInterface $oil)
{
$this->oil = $oil;
Expand All @@ -86,9 +74,7 @@ public function __construct(FakeEngineInterface $engine)
}


/**
* @PostConstruct
*/
#[PostConstruct]
public function postConstruct()
{
$isEngineInstalled = $this->engine instanceof FakeEngine;
Expand Down
7 changes: 2 additions & 5 deletions tests/Fake/FakeHandleProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,8 @@ class FakeHandleProvider implements ProviderInterface
{
private $logo;

/**
* @Inject
* @Named("logo")
*/
public function __construct($logo = 'nardi')
#[Inject]
public function __construct(#[Named('logo')] $logo = 'nardi')
{
$this->logo = $logo;
}
Expand Down
1 change: 1 addition & 0 deletions tests/Fake/FakeLoggerConsumer.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ public function __construct(InjectorInterface $injector)
/**
* @FakeLoggerInject(type="MEMORY")
*/
#[FakeLoggerInject(type: 'MEMORY')]
public function setLogger(FakeLoggerInterface $logger)
{
$this->logger = $logger;
Expand Down
19 changes: 19 additions & 0 deletions tests/Fake/FakeLoggerInject.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,25 @@ final class FakeLoggerInject implements InjectInterface
/** @Enum({"MEMORY", "FILE", "DB"}) */
public $type;

/**
* @param string|array<string, string>|null $type
*/
public function __construct($type = null)
{
if (is_array($type) && isset($type['type'])) {
// Doctrine Annotations format: @FakeLoggerInject(type="MEMORY")
$this->type = $type['type'];
} elseif (is_string($type)) {
// Attribute format: #[FakeLoggerInject(type: 'MEMORY')]
$this->type = $type;
} elseif (is_array($type) && isset($type['value'])) {
// Doctrine Annotations format: @FakeLoggerInject("MEMORY")
$this->type = $type['value'];
} else {
$this->type = $type ?? 'MEMORY';
}
}

public function isOptional()
{
return true;
Expand Down
4 changes: 1 addition & 3 deletions tests/Fake/FakeOptional.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,7 @@ class FakeOptional
{
public $robot = null;

/**
* @Inject(optional=true)
*/
#[Inject(optional: true)]
public function setOptionalRobot(FakeRobotInterface $robot)
{
$this->robot = $robot;
Expand Down
18 changes: 2 additions & 16 deletions tests/Fake/MultiBindings/FakeMultiBindingAnnotation.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,23 +10,9 @@

final class FakeMultiBindingAnnotation
{
/**
* @var Map<FakeEngineInterface>
* @Set(FakeEngineInterface::class)
*/
public $engines;

/**
* @var Map<FakeRobotInterface>
* @Set(FakeRobotInterface::class)
*/
public $robots;

public function __construct(
Map $engines,
Map $robots
#[Set(FakeEngineInterface::class)] public Map $engines,
#[Set(FakeRobotInterface::class)] public Map $robots
){
$this->engines = $engines;
$this->robots = $robots;
}
}
Loading