AspectContext
This commit is contained in:
parent
6050e3bb72
commit
d35c3df06d
8 changed files with 70 additions and 19 deletions
13
src/AspectContext.php
Normal file
13
src/AspectContext.php
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace IceFox\Aspect;
|
||||||
|
|
||||||
|
readonly class AspectContext
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
public string $class,
|
||||||
|
public string $function,
|
||||||
|
public ?object $self,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -243,6 +243,14 @@ class AspectWeaver
|
||||||
$code = " {$visibility} {$static}function {$name}({$params}){$returnType}\n";
|
$code = " {$visibility} {$static}function {$name}({$params}){$returnType}\n";
|
||||||
$code .= " {\n";
|
$code .= " {\n";
|
||||||
|
|
||||||
|
// Create AspectContext for all aspects
|
||||||
|
$selfArg = $isStatic ? 'null' : '$this';
|
||||||
|
$code .= " \$context = new \\IceFox\\Aspect\\AspectContext(\n";
|
||||||
|
$code .= " class: '{$className}',\n";
|
||||||
|
$code .= " function: '{$name}',\n";
|
||||||
|
$code .= " self: {$selfArg}\n";
|
||||||
|
$code .= " );\n\n";
|
||||||
|
|
||||||
// Instantiate all aspects using their constructor arguments from attributes
|
// Instantiate all aspects using their constructor arguments from attributes
|
||||||
foreach ($aspects as $index => $aspectMetadata) {
|
foreach ($aspects as $index => $aspectMetadata) {
|
||||||
$aspectClass = $aspectMetadata->aspectClass;
|
$aspectClass = $aspectMetadata->aspectClass;
|
||||||
|
|
@ -252,12 +260,11 @@ class AspectWeaver
|
||||||
$args = $attribute->getArguments();
|
$args = $attribute->getArguments();
|
||||||
$aspectConstructorArguments = $this->buildConstructorArguments($args);
|
$aspectConstructorArguments = $this->buildConstructorArguments($args);
|
||||||
|
|
||||||
$code .= " \$aspect{$index} = new \\{$aspectClass}({$aspectConstructorArguments});\n";
|
// Pass context as first parameter before other constructor arguments
|
||||||
|
$constructorParams = $aspectConstructorArguments ? "\$context, {$aspectConstructorArguments}" : "\$context";
|
||||||
|
$code .= " \$aspect{$index} = new \\{$aspectClass}({$constructorParams});\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prepare target argument for aspect methods
|
|
||||||
$targetArg = $isStatic ? "static::class" : "\$this";
|
|
||||||
|
|
||||||
// Call all before methods in a single try-catch
|
// Call all before methods in a single try-catch
|
||||||
$code .= " \$currentAspect = '';\n";
|
$code .= " \$currentAspect = '';\n";
|
||||||
$code .= " try {\n";
|
$code .= " try {\n";
|
||||||
|
|
@ -266,8 +273,7 @@ class AspectWeaver
|
||||||
$aspectReflection = new ReflectionClass($aspectClass);
|
$aspectReflection = new ReflectionClass($aspectClass);
|
||||||
if ($aspectReflection->hasMethod('before')) {
|
if ($aspectReflection->hasMethod('before')) {
|
||||||
$code .= " \$currentAspect = \\{$aspectClass}::class;\n";
|
$code .= " \$currentAspect = \\{$aspectClass}::class;\n";
|
||||||
$beforeParams = $paramNames ? "{$targetArg}, {$paramNames}" : $targetArg;
|
$code .= " \$aspect{$index}->before({$paramNames});\n";
|
||||||
$code .= " \$aspect{$index}->before({$beforeParams});\n";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$code .= " } catch (\\Throwable \$e) {\n";
|
$code .= " } catch (\\Throwable \$e) {\n";
|
||||||
|
|
@ -291,7 +297,7 @@ class AspectWeaver
|
||||||
$aspectReflection = new ReflectionClass($aspectClass);
|
$aspectReflection = new ReflectionClass($aspectClass);
|
||||||
if ($aspectReflection->hasMethod('after')) {
|
if ($aspectReflection->hasMethod('after')) {
|
||||||
$code .= " \$currentAspect = \\{$aspectClass}::class;\n";
|
$code .= " \$currentAspect = \\{$aspectClass}::class;\n";
|
||||||
$code .= " \$result = \$aspect{$i}->after({$targetArg}, \$result);\n";
|
$code .= " \$result = \$aspect{$i}->after(\$result);\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$code .= " } catch (\\Throwable \$e) {\n";
|
$code .= " } catch (\\Throwable \$e) {\n";
|
||||||
|
|
@ -315,7 +321,7 @@ class AspectWeaver
|
||||||
$aspectReflection = new ReflectionClass($aspectClass);
|
$aspectReflection = new ReflectionClass($aspectClass);
|
||||||
if ($aspectReflection->hasMethod('after')) {
|
if ($aspectReflection->hasMethod('after')) {
|
||||||
$code .= " \$currentAspect = \\{$aspectClass}::class;\n";
|
$code .= " \$currentAspect = \\{$aspectClass}::class;\n";
|
||||||
$code .= " \$aspect{$i}->after({$targetArg}, null);\n";
|
$code .= " \$aspect{$i}->after(null);\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$code .= " } catch (\\Throwable \$e) {\n";
|
$code .= " } catch (\\Throwable \$e) {\n";
|
||||||
|
|
|
||||||
|
|
@ -3,19 +3,25 @@
|
||||||
namespace Tests\Aspects;
|
namespace Tests\Aspects;
|
||||||
|
|
||||||
use Attribute;
|
use Attribute;
|
||||||
|
use IceFox\Aspect\AspectContext;
|
||||||
|
|
||||||
#[Attribute(Attribute::TARGET_METHOD)]
|
#[Attribute(Attribute::TARGET_METHOD)]
|
||||||
class BasicAspect
|
class BasicAspect
|
||||||
{
|
{
|
||||||
public ?object $object = null;
|
public ?object $object = null;
|
||||||
|
|
||||||
public function before(object|string $target, mixed ...$args): void
|
public function __construct(
|
||||||
|
private readonly AspectContext $context,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public function before(mixed ...$args): void
|
||||||
{
|
{
|
||||||
$this->object = end($args);
|
$this->object = end($args);
|
||||||
$this->object->before = true;
|
$this->object->before = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function after(object|string $target, mixed $return): mixed
|
public function after(mixed $return): mixed
|
||||||
{
|
{
|
||||||
$this->object->after = true;
|
$this->object->after = true;
|
||||||
return $return;
|
return $return;
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
namespace Tests\Aspects;
|
namespace Tests\Aspects;
|
||||||
|
|
||||||
use Attribute;
|
use Attribute;
|
||||||
|
use IceFox\Aspect\AspectContext;
|
||||||
|
|
||||||
#[Attribute(Attribute::TARGET_METHOD)]
|
#[Attribute(Attribute::TARGET_METHOD)]
|
||||||
class ConfigurableAspect
|
class ConfigurableAspect
|
||||||
|
|
@ -10,13 +11,14 @@ class ConfigurableAspect
|
||||||
public static array $executionLog = [];
|
public static array $executionLog = [];
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
|
private readonly AspectContext $context,
|
||||||
public readonly string $prefix = 'default',
|
public readonly string $prefix = 'default',
|
||||||
public readonly int $multiplier = 1,
|
public readonly int $multiplier = 1,
|
||||||
public readonly bool $enabled = true,
|
public readonly bool $enabled = true,
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
public function before(object|string $target, mixed ...$args): void
|
public function before(mixed ...$args): void
|
||||||
{
|
{
|
||||||
if ($this->enabled) {
|
if ($this->enabled) {
|
||||||
self::$executionLog[] = [
|
self::$executionLog[] = [
|
||||||
|
|
@ -28,7 +30,7 @@ class ConfigurableAspect
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function after(object|string $target, mixed $result): mixed
|
public function after(mixed $result): mixed
|
||||||
{
|
{
|
||||||
if (!$this->enabled) {
|
if (!$this->enabled) {
|
||||||
return $result;
|
return $result;
|
||||||
|
|
|
||||||
|
|
@ -3,18 +3,24 @@
|
||||||
namespace Tests\Aspects;
|
namespace Tests\Aspects;
|
||||||
|
|
||||||
use Attribute;
|
use Attribute;
|
||||||
|
use IceFox\Aspect\AspectContext;
|
||||||
|
|
||||||
#[Attribute(Attribute::TARGET_METHOD)]
|
#[Attribute(Attribute::TARGET_METHOD)]
|
||||||
class LoggingAspect
|
class LoggingAspect
|
||||||
{
|
{
|
||||||
public static array $logs = [];
|
public static array $logs = [];
|
||||||
|
|
||||||
public function before(object|string $target, mixed ...$args): void
|
public function __construct(
|
||||||
|
private readonly AspectContext $context,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public function before(mixed ...$args): void
|
||||||
{
|
{
|
||||||
self::$logs[] = ['type' => 'logging_before', 'args' => $args];
|
self::$logs[] = ['type' => 'logging_before', 'args' => $args];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function after(object|string $target, mixed $result): mixed
|
public function after(mixed $result): mixed
|
||||||
{
|
{
|
||||||
self::$logs[] = ['type' => 'logging_after', 'result' => $result];
|
self::$logs[] = ['type' => 'logging_after', 'result' => $result];
|
||||||
return $result;
|
return $result;
|
||||||
|
|
|
||||||
|
|
@ -3,13 +3,19 @@
|
||||||
namespace Tests\Aspects;
|
namespace Tests\Aspects;
|
||||||
|
|
||||||
use Attribute;
|
use Attribute;
|
||||||
|
use IceFox\Aspect\AspectContext;
|
||||||
|
|
||||||
#[Attribute(Attribute::TARGET_METHOD)]
|
#[Attribute(Attribute::TARGET_METHOD)]
|
||||||
class ModifyingAspect
|
class ModifyingAspect
|
||||||
{
|
{
|
||||||
public static mixed $modifier = null;
|
public static mixed $modifier = null;
|
||||||
|
|
||||||
public function after(object|string $target, mixed $result): mixed
|
public function __construct(
|
||||||
|
private readonly AspectContext $context,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public function after(mixed $result): mixed
|
||||||
{
|
{
|
||||||
if (self::$modifier !== null) {
|
if (self::$modifier !== null) {
|
||||||
return (self::$modifier)($result);
|
return (self::$modifier)($result);
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
namespace Tests\Aspects;
|
namespace Tests\Aspects;
|
||||||
|
|
||||||
use Attribute;
|
use Attribute;
|
||||||
|
use IceFox\Aspect\AspectContext;
|
||||||
use RuntimeException;
|
use RuntimeException;
|
||||||
|
|
||||||
#[Attribute(Attribute::TARGET_METHOD)]
|
#[Attribute(Attribute::TARGET_METHOD)]
|
||||||
|
|
@ -11,14 +12,19 @@ class ThrowingAspect
|
||||||
public static bool $throwInBefore = false;
|
public static bool $throwInBefore = false;
|
||||||
public static bool $throwInAfter = false;
|
public static bool $throwInAfter = false;
|
||||||
|
|
||||||
public function before(object|string $target, mixed ...$args): void
|
public function __construct(
|
||||||
|
private readonly AspectContext $context,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public function before(mixed ...$args): void
|
||||||
{
|
{
|
||||||
if (self::$throwInBefore) {
|
if (self::$throwInBefore) {
|
||||||
throw new RuntimeException('Exception thrown in before()');
|
throw new RuntimeException('Exception thrown in before()');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function after(object|string $target, mixed $result): mixed
|
public function after(mixed $result): mixed
|
||||||
{
|
{
|
||||||
if (self::$throwInAfter) {
|
if (self::$throwInAfter) {
|
||||||
throw new RuntimeException('Exception thrown in after()');
|
throw new RuntimeException('Exception thrown in after()');
|
||||||
|
|
|
||||||
|
|
@ -3,18 +3,24 @@
|
||||||
namespace Tests\Aspects;
|
namespace Tests\Aspects;
|
||||||
|
|
||||||
use Attribute;
|
use Attribute;
|
||||||
|
use IceFox\Aspect\AspectContext;
|
||||||
|
|
||||||
#[Attribute(Attribute::TARGET_METHOD)]
|
#[Attribute(Attribute::TARGET_METHOD)]
|
||||||
class TrackingAspect
|
class TrackingAspect
|
||||||
{
|
{
|
||||||
public static array $calls = [];
|
public static array $calls = [];
|
||||||
|
|
||||||
public function before(object|string $target, mixed ...$args): void
|
public function __construct(
|
||||||
|
private readonly AspectContext $context,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public function before(mixed ...$args): void
|
||||||
{
|
{
|
||||||
self::$calls[] = ['event' => 'before', 'args' => $args];
|
self::$calls[] = ['event' => 'before', 'args' => $args];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function after(object|string $target, mixed $result): mixed
|
public function after(mixed $result): mixed
|
||||||
{
|
{
|
||||||
self::$calls[] = ['event' => 'after', 'result' => $result];
|
self::$calls[] = ['event' => 'after', 'result' => $result];
|
||||||
return $result;
|
return $result;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue