Skip to content

Commit

Permalink
[BC Break] Internal container refactoring.
Browse files Browse the repository at this point in the history
New version drops GoAspectContainer class and rely just on Container class now.
Container uses now generic methods, so IDE and PhpStan aware about types now.
Tags have been replaced with interface names, which simplifies usage of marker interfaces.
All unnecessary string variables were replaced with class FQN.
No more special methods to register advisors or pointcuts - they are just values in container.
  • Loading branch information
lisachenko committed Apr 14, 2024
1 parent ce9fe05 commit f5e90ff
Show file tree
Hide file tree
Showing 28 changed files with 463 additions and 524 deletions.
5 changes: 3 additions & 2 deletions src/Aop/Framework/AbstractInterceptor.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
namespace Go\Aop\Framework;

use Closure;
use Go\Aop\Aspect;
use Go\Aop\AspectException;
use Go\Aop\Intercept\Interceptor;
use Go\Aop\OrderedAdvice;
Expand Down Expand Up @@ -89,15 +90,15 @@ public static function serializeAdvice(Closure $adviceMethod): array
public static function unserializeAdvice(array $adviceData): Closure
{
// General unpacking supports only aspect's advices
if (!isset($adviceData['class'])) {
if (!isset($adviceData['class']) || !is_subclass_of($adviceData['class'], Aspect::class)) {
throw new AspectException('Could not unpack an interceptor without aspect name');
}
$aspectName = $adviceData['class'];
$methodName = $adviceData['name'];

// With aspect name and method name, we can restore back a closure for it
if (!isset(self::$localAdvicesCache["$aspectName->$methodName"])) {
$aspect = AspectKernel::getInstance()->getContainer()->getAspect($aspectName);
$aspect = AspectKernel::getInstance()->getContainer()->getService($aspectName);
$advice = (new ReflectionMethod($aspectName, $methodName))->getClosure($aspect);

assert(isset($advice), 'getClosure() can not be null on modern PHP versions');
Expand Down
7 changes: 6 additions & 1 deletion src/Aop/Pointcut/PointcutReference.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

namespace Go\Aop\Pointcut;

use Go\Aop\AspectException;
use Go\Aop\Pointcut;
use Go\Core\AspectContainer;
use Go\Core\AspectKernel;
Expand Down Expand Up @@ -68,7 +69,11 @@ public function __wakeup(): void
private function getPointcut(): Pointcut
{
if (!isset($this->pointcut)) {
$this->pointcut = $this->container->getPointcut($this->pointcutId);
$pointcutValue = $this->container->getValue($this->pointcutId);
if (!$pointcutValue instanceof Pointcut) {
throw new AspectException("Reference {$this->pointcutId} points not to a Pointcut.");
}
$this->pointcut = $pointcutValue;
}

return $this->pointcut;
Expand Down
10 changes: 4 additions & 6 deletions src/Aop/Support/LazyPointcutAdvisor.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@

use Go\Aop\Advice;
use Go\Aop\Pointcut;
use Go\Aop\Pointcut\PointcutLexer;
use Go\Aop\Pointcut\PointcutParser;
use Go\Aop\PointcutAdvisor;
use Go\Core\AspectContainer;

Expand Down Expand Up @@ -42,12 +44,8 @@ public function getPointcut(): Pointcut
{
if (!isset($this->pointcut)) {
// Inject these dependencies and make them lazy!

/** @var Pointcut\PointcutLexer $lexer */
$lexer = $this->container->get('aspect.pointcut.lexer');

/** @var Pointcut\PointcutParser $parser */
$parser = $this->container->get('aspect.pointcut.parser');
$lexer = $this->container->getService(PointcutLexer::class);
$parser = $this->container->getService(PointcutParser::class);

$tokenStream = $lexer->lex($this->pointcutExpression);
$this->pointcut = $parser->parse($tokenStream);
Expand Down
6 changes: 3 additions & 3 deletions src/Aop/Support/PointcutBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -84,9 +84,9 @@ public function declareError(string $pointcutExpression, string $message, int $e
*/
private function registerAdviceInContainer(string $pointcutExpression, Advice $adviceToInvoke): void
{
$this->container->registerAdvisor(
new LazyPointcutAdvisor($this->container, $pointcutExpression, $adviceToInvoke),
$this->getPointcutId($pointcutExpression)
$this->container->add(
$this->getPointcutId($pointcutExpression),
new LazyPointcutAdvisor($this->container, $pointcutExpression, $adviceToInvoke)
);
}

Expand Down
2 changes: 1 addition & 1 deletion src/Console/Command/BaseAspectCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ class BaseAspectCommand extends Command
/**
* Stores an instance of aspect kernel
*/
protected ?AspectKernel $aspectKernel = null;
protected AspectKernel $aspectKernel;

/**
* {@inheritDoc}
Expand Down
22 changes: 10 additions & 12 deletions src/Console/Command/DebugAdvisorCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,8 @@

use Go\Aop\Advisor;
use Go\Core\AdviceMatcher;
use Go\Core\AdviceMatcherInterface;
use Go\Core\AspectContainer;
use Go\Core\AspectLoader;
use Go\Core\CachedAspectLoader;
use Go\Instrument\FileSystem\Enumerator;
use Go\ParserReflection\ReflectionFile;
use ReflectionClass;
Expand Down Expand Up @@ -63,9 +62,10 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$io->title('Advisor debug information');

$advisorId = $input->getOption('advisor');
if (!$advisorId) {
if (empty($advisorId)) {
$this->showAdvisorsList($io);
} else {
assert(is_string($advisorId), "Option 'advisor' must be a string, " . gettype($advisorId) . " given");
$this->showAdvisorInformation($io, $advisorId);
}

Expand All @@ -81,7 +81,6 @@ private function showAdvisorsList(SymfonyStyle $io): void

$tableRows = [];
foreach ($advisors as $id => $advisor) {
[, $id] = explode('.', $id, 2);
$advice = $advisor->getAdvice();
$expression = '';
try {
Expand All @@ -106,11 +105,13 @@ private function showAdvisorInformation(SymfonyStyle $io, string $advisorId): vo
{
$aspectContainer = $this->aspectKernel->getContainer();

/** @var AdviceMatcherInterface $adviceMatcher */
$adviceMatcher = $aspectContainer->get('aspect.advice_matcher');
$adviceMatcher = $aspectContainer->getService(AdviceMatcher::class);
$this->loadAdvisorsList($aspectContainer);

$advisor = $aspectContainer->getAdvisor($advisorId);
$advisor = $aspectContainer->getValue($advisorId);
if (!$advisor instanceof Advisor) {
throw new \InvalidArgumentException("Invalid advisor {$advisorId} given");
}
$options = $this->aspectKernel->getOptions();

$enumerator = new Enumerator($options['appDir'], $options['includePaths'], $options['excludePaths']);
Expand Down Expand Up @@ -151,14 +152,11 @@ private function writeInfoAboutAdvices(SymfonyStyle $io, ReflectionClass $reflec
*/
private function loadAdvisorsList(AspectContainer $aspectContainer): array
{
/** @var AspectLoader $aspectLoader */
$aspectLoader = $aspectContainer->get('aspect.cached.loader');
$aspectLoader = $aspectContainer->getService(CachedAspectLoader::class);
$aspects = $aspectLoader->getUnloadedAspects();
foreach ($aspects as $aspect) {
$aspectLoader->loadAndRegister($aspect);
}
$advisors = $aspectContainer->getByTag('advisor');

return $advisors;
return $aspectContainer->getServicesByInterface(Advisor::class);
}
}
9 changes: 4 additions & 5 deletions src/Console/Command/DebugAspectCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,9 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$aspectName = $input->getOption('aspect');
if (!$aspectName) {
$io->text('<info>' . get_class($this->aspectKernel) . '</info> has following enabled aspects:');
$aspects = $container->getByTag('aspect');
} else {
$aspect = $container->getAspect($aspectName);
$aspects = $container->getServicesByInterface(Aspect::class);
} elseif (is_string($aspectName) && is_subclass_of($aspectName, Aspect::class)) {
$aspect = $container->getService($aspectName);
$aspects[] = $aspect;
}
$this->showRegisteredAspectsInfo($io, $aspects);
Expand Down Expand Up @@ -106,8 +106,7 @@ private function showAspectPointcutsAndAdvisors(SymfonyStyle $io, Aspect $aspect
{
$container = $this->aspectKernel->getContainer();

/** @var AspectLoader $aspectLoader */
$aspectLoader = $container->get('aspect.loader');
$aspectLoader = $container->getService(AspectLoader::class);
$io->writeln('<comment>Pointcuts and advices</comment>');

$aspectItems = $aspectLoader->load($aspect);
Expand Down
2 changes: 1 addition & 1 deletion src/Console/Command/DebugWeavingCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int

$io->title('Weaving debug information');

$cachePathManager = $this->aspectKernel->getContainer()->get('aspect.cache.path.manager');
$cachePathManager = $this->aspectKernel->getContainer()->getService(CachePathManager::class);
$warmer = new CacheWarmer($this->aspectKernel, new NullOutput());
$warmer->warmUp();

Expand Down
75 changes: 28 additions & 47 deletions src/Core/AspectContainer.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,7 @@
namespace Go\Core;

use OutOfBoundsException;
use Go\Aop\Advisor;
use Go\Aop\Aspect;
use Go\Aop\Pointcut;

/**
* Aspect container interface
Expand Down Expand Up @@ -68,77 +66,60 @@ interface AspectContainer
public const AOP_PROXIED_SUFFIX = '__AopProxied';

/**
* Return a service or value from the container
* Returns a service from the container.
*
* Supports lazy-initialization if value is defined as a closure, it will be invoked once to perform initialization.
*
* @param class-string<T> $className Class-name of service to retrieve from the container
* @return object&T
*
* @template T of object
*
* @return mixed
* @throws OutOfBoundsException if service was not found
*/
public function get(string $id);
public function getService(string $className): object;

/**
* Return list of service tagged with marker
* Return list of services tagged with marker interface
*
* @param class-string<T> $interfaceTagClassName Interface name of services to retrieve from the container
* @return T[]
*
* @template T
*/
public function getByTag(string $tag): array;
public function getServicesByInterface(string $interfaceTagClassName): array;

/**
* Returns a pointcut by identifier
* Returns a value from the container
*
* @param string $key Given key
*
* @return mixed
* @throws OutOfBoundsException if key was not found
*/
public function getPointcut(string $id): Pointcut;
public function getValue(string $key): mixed;

/**
* Checks if item with specified id is present in the container
*/
public function has(string $id): bool;

/**
* Store the pointcut in the container
*/
public function registerPointcut(Pointcut $pointcut, string $id): void;

/**
* Returns an advisor by identifier
*/
public function getAdvisor(string $id): Advisor;

/**
* Store the advisor in the container
*/
public function registerAdvisor(Advisor $advisor, string $id): void;

/**
* Register an aspect in the container
*/
public function registerAspect(Aspect $aspect): void;

/**
* Returns an aspect by id or class name
*/
public function getAspect(string $aspectName): Aspect;

/**
* Add an AOP resource to the container
* Resources is used to check the freshness of AOP cache
*
* @param string $resource Path to the resource
*/
public function addResource(string $resource);

/**
* Returns the list of AOP resources
*/
public function getResources(): array;

/**
* Checks the freshness of AOP cache
* Checks if there are any file resources with changes after since given timestamp
*
* @return bool Whether or not concrete file is fresh
* @return bool Whether or not there are new changes (filemtime of any resource is greater than given)
*/
public function isFresh(int $timestamp): bool;
public function hasAnyResourceChangedSince(int $timestamp): bool;

/**
* Set a service into the container
* Adds a new item into the container
*
* @param mixed $value Value to store
*/
public function set(string $id, $value, array $tags = []): void;
public function add(string $id, mixed $value): void;
}
Loading

0 comments on commit f5e90ff

Please sign in to comment.