-
-
Notifications
You must be signed in to change notification settings - Fork 9.4k
Commit
…Trait
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
<?php | ||
|
||
/* | ||
* This file is part of the Symfony package. | ||
* | ||
* (c) Fabien Potencier <fabien@symfony.com> | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
|
||
namespace Symfony\Contracts\Service; | ||
|
||
use Psr\Container\ContainerInterface; | ||
use Symfony\Contracts\Service\Attribute\Required; | ||
use Symfony\Contracts\Service\Attribute\SubscribedService; | ||
|
||
/** | ||
* Implementation of ServiceSubscriberInterface that determines subscribed services | ||
* from methods that have the #[SubscribedService] attribute. | ||
* | ||
* Service ids are available as "ClassName::methodName" so that the implementation | ||
* of subscriber methods can be just `return $this->container->get(__METHOD__);`. | ||
* | ||
* @author Kevin Bond <kevinbond@gmail.com> | ||
*/ | ||
trait ServiceMethodsSubscriberTrait | ||
{ | ||
protected ContainerInterface $container; | ||
|
||
public static function getSubscribedServices(): array | ||
Check failure on line 31 in src/Symfony/Contracts/Service/ServiceMethodsSubscriberTrait.php GitHub Actions / PsalmLessSpecificImplementedReturnType
|
||
{ | ||
$services = method_exists(get_parent_class(self::class) ?: '', __FUNCTION__) ? parent::getSubscribedServices() : []; | ||
Check failure on line 33 in src/Symfony/Contracts/Service/ServiceMethodsSubscriberTrait.php GitHub Actions / PsalmParentNotFound
|
||
|
||
foreach ((new \ReflectionClass(self::class))->getMethods() as $method) { | ||
if (self::class !== $method->getDeclaringClass()->name) { | ||
continue; | ||
} | ||
|
||
if (!$attribute = $method->getAttributes(SubscribedService::class)[0] ?? null) { | ||
continue; | ||
} | ||
|
||
if ($method->isStatic() || $method->isAbstract() || $method->isGenerator() || $method->isInternal() || $method->getNumberOfRequiredParameters()) { | ||
throw new \LogicException(sprintf('Cannot use "%s" on method "%s::%s()" (can only be used on non-static, non-abstract methods with no parameters).', SubscribedService::class, self::class, $method->name)); | ||
} | ||
|
||
if (!$returnType = $method->getReturnType()) { | ||
throw new \LogicException(sprintf('Cannot use "%s" on methods without a return type in "%s::%s()".', SubscribedService::class, $method->name, self::class)); | ||
} | ||
|
||
/* @var SubscribedService $attribute */ | ||
$attribute = $attribute->newInstance(); | ||
$attribute->key ??= self::class.'::'.$method->name; | ||
$attribute->type ??= $returnType instanceof \ReflectionNamedType ? $returnType->getName() : (string) $returnType; | ||
$attribute->nullable = $returnType->allowsNull(); | ||
|
||
if ($attribute->attributes) { | ||
$services[] = $attribute; | ||
} else { | ||
$services[$attribute->key] = ($attribute->nullable ? '?' : '').$attribute->type; | ||
} | ||
} | ||
|
||
return $services; | ||
} | ||
|
||
#[Required] | ||
public function setContainer(ContainerInterface $container): ?ContainerInterface | ||
{ | ||
$ret = null; | ||
if (method_exists(get_parent_class(self::class) ?: '', __FUNCTION__)) { | ||
$ret = parent::setContainer($container); | ||
Check failure on line 73 in src/Symfony/Contracts/Service/ServiceMethodsSubscriberTrait.php GitHub Actions / PsalmParentNotFound
|
||
} | ||
|
||
$this->container = $container; | ||
|
||
return $ret; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
<?php | ||
|
||
/* | ||
* This file is part of the Symfony package. | ||
* | ||
* (c) Fabien Potencier <fabien@symfony.com> | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
|
||
namespace Symfony\Contracts\Tests\Service; | ||
|
||
use Psr\Container\ContainerInterface; | ||
use Symfony\Contracts\Service\Attribute\Required; | ||
use Symfony\Contracts\Service\Attribute\SubscribedService; | ||
use Symfony\Contracts\Service\ServiceSubscriberInterface; | ||
use Symfony\Contracts\Service\ServiceSubscriberTrait; | ||
|
||
class LegacyParentTestService | ||
{ | ||
public function aParentService(): Service1 | ||
Check failure on line 22 in src/Symfony/Contracts/Tests/Service/LegacyTestService.php GitHub Actions / PsalmInvalidReturnType
|
||
{ | ||
} | ||
|
||
public function setContainer(ContainerInterface $container): ?ContainerInterface | ||
{ | ||
return $container; | ||
} | ||
} | ||
|
||
class LegacyTestService extends LegacyParentTestService implements ServiceSubscriberInterface | ||
{ | ||
use ServiceSubscriberTrait; | ||
|
||
#[SubscribedService] | ||
public function aService(): Service2 | ||
{ | ||
return $this->container->get(__METHOD__); | ||
Check failure on line 39 in src/Symfony/Contracts/Tests/Service/LegacyTestService.php GitHub Actions / PsalmUndefinedThisPropertyFetch
|
||
} | ||
|
||
#[SubscribedService] | ||
public function nullableService(): ?Service2 | ||
{ | ||
return $this->container->get(__METHOD__); | ||
Check failure on line 45 in src/Symfony/Contracts/Tests/Service/LegacyTestService.php GitHub Actions / PsalmUndefinedThisPropertyFetch
|
||
} | ||
|
||
#[SubscribedService(attributes: new Required())] | ||
public function withAttribute(): ?Service2 | ||
{ | ||
return $this->container->get(__METHOD__); | ||
Check failure on line 51 in src/Symfony/Contracts/Tests/Service/LegacyTestService.php GitHub Actions / PsalmUndefinedThisPropertyFetch
|
||
} | ||
} | ||
|
||
class LegacyChildTestService extends LegacyTestService | ||
{ | ||
#[SubscribedService()] | ||
public function aChildService(): LegacyService3 | ||
{ | ||
return $this->container->get(__METHOD__); | ||
Check failure on line 60 in src/Symfony/Contracts/Tests/Service/LegacyTestService.php GitHub Actions / PsalmUndefinedThisPropertyFetch
|
||
} | ||
} | ||
|
||
class LegacyParentWithMagicCall | ||
{ | ||
public function __call($method, $args) | ||
{ | ||
throw new \BadMethodCallException('Should not be called.'); | ||
} | ||
|
||
public static function __callStatic($method, $args) | ||
{ | ||
throw new \BadMethodCallException('Should not be called.'); | ||
} | ||
} | ||
|
||
class LegacyService3 | ||
{ | ||
} | ||
|
||
class LegacyParentTestService2 | ||
{ | ||
/** @var ContainerInterface */ | ||
protected $container; | ||
|
||
public function setContainer(ContainerInterface $container) | ||
{ | ||
$previous = $this->container ?? null; | ||
$this->container = $container; | ||
|
||
return $previous; | ||
} | ||
} |