Skip to content

Commit 94d91b8

Browse files
committed
Merge branch 'main' of github.com:rcrdortiz/Axpecto
2 parents d8de030 + d77afd0 commit 94d91b8

33 files changed

+1625
-709
lines changed

composer.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
{
22
"name": "rcrdortiz/axpecto",
3-
"description": "PHP meta framework for building modern, AI oriented, aspect orientated development frameworks.",
3+
"description": "PHP metaframework for modern, AI‑augmented, aspect‑oriented development.",
44
"type": "library",
55
"version": "1.0.4",
6+
"license": "MIT",
67
"require": {
78
"php": ">=8.3",
89
"ext-mysqli": "*",
@@ -20,7 +21,6 @@
2021
"src/functions.php"
2122
]
2223
},
23-
"license": "GNU",
2424
"authors": [
2525
{
2626
"name": "Richard Ortiz",

src/Annotation/Annotation.php

Lines changed: 3 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -3,36 +3,21 @@
33
namespace Axpecto\Annotation;
44

55
use Attribute;
6-
use Axpecto\ClassBuilder\BuildHandler;
7-
use Axpecto\MethodExecution\MethodExecutionHandler;
86

97
/**
108
* Class Annotation
119
*
12-
* Represents an abstract base class for annotations in an Aspect-Oriented Programming (AOP) system.
10+
* Represents a base class for annotations in an Aspect-Oriented Programming (AOP) system.
1311
* Annotations can have associated handler classes that define how they are processed during
1412
* method execution or build phases. This class provides mechanisms to retrieve those handlers and
1513
* associate annotations with specific classes and methods.
1614
*
1715
* @package Axpecto\Aop
16+
*
17+
* @TODO Refactor this and possibly create a hierarchy of annotations with Annotation -> BuildAnnotation -> MethodExecutionAnnotation.
1818
*/
1919
#[Attribute]
2020
class Annotation {
21-
22-
/**
23-
* The handler for processing the method execution annotation.
24-
*
25-
* @var MethodExecutionHandler|null
26-
*/
27-
protected ?MethodExecutionHandler $methodExecutionHandler = null;
28-
29-
/**
30-
* The builder for the annotation, used during the build phase.
31-
*
32-
* @var BuildHandler|null
33-
*/
34-
protected ?BuildHandler $builder = null;
35-
3621
/**
3722
* The class associated with the annotation.
3823
*
@@ -47,42 +32,6 @@ class Annotation {
4732
*/
4833
protected ?string $annotatedMethod = null;
4934

50-
/**
51-
* Gets the BuildHandler for this annotation, if available.
52-
*
53-
* @return BuildHandler|null The builder for the annotation, or null if not set.
54-
*/
55-
public function getBuilder(): ?BuildHandler {
56-
return $this->builder;
57-
}
58-
59-
/**
60-
* Checks if this annotation is meant for the build phase.
61-
*
62-
* @return bool True if the annotation is used for building, false otherwise.
63-
*/
64-
public function isBuildAnnotation(): bool {
65-
return $this->builder instanceof BuildHandler;
66-
}
67-
68-
/**
69-
* Gets the MethodExecutionHandler for this annotation, if available.
70-
*
71-
* @return MethodExecutionHandler|null The handler for method execution, or null if not set.
72-
*/
73-
public function getMethodExecutionHandler(): ?MethodExecutionHandler {
74-
return $this->methodExecutionHandler;
75-
}
76-
77-
/**
78-
* Checks if this annotation is meant for method execution interception.
79-
*
80-
* @return bool True if it is a method execution annotation, false otherwise.
81-
*/
82-
public function isMethodExecutionAnnotation(): bool {
83-
return $this->methodExecutionHandler instanceof MethodExecutionHandler;
84-
}
85-
8635
/**
8736
* Sets the class name that this annotation is associated with.
8837
*
Lines changed: 114 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -1,146 +1,171 @@
11
<?php
22

3+
declare( strict_types=1 );
4+
35
namespace Axpecto\Annotation;
46

57
use Axpecto\Collection\Klist;
68
use Axpecto\Container\Container;
79
use Axpecto\Reflection\ReflectionUtils;
8-
use Exception;
10+
use ReflectionAttribute;
911
use ReflectionException;
10-
use ReflectionMethod;
12+
use ReflectionParameter;
1113

1214
/**
13-
* Class AnnotationReader
14-
*
15-
* This class is responsible for reading annotations on classes and methods, specifically
16-
* for use in an Aspect-Oriented Programming (AOP) context. It handles fetching annotations,
17-
* including build-related annotations, and supports dependency injection for these annotations.
15+
* Reads PHP8 attributes and turns them into AOP-style Annotation instances,
16+
* filtering by class vs. method targets and injecting their properties via DI.
1817
*
19-
* @template T
18+
* @template A of Annotation
19+
* @psalm-consistent-constructor
2020
*/
2121
class AnnotationReader {
22-
23-
/**
24-
* Constructor for AnnotationReader.
25-
*
26-
* @param Container $container The dependency injection container.
27-
* @param ReflectionUtils $reflect Utility for handling reflection of classes and methods.
28-
*/
2922
public function __construct(
3023
private readonly Container $container,
31-
private readonly ReflectionUtils $reflect,
24+
private readonly ReflectionUtils $reflection
3225
) {
3326
}
3427

3528
/**
36-
* Fetches annotations for a specific method.
29+
* Fetch all annotations of a given type on a class.
3730
*
38-
* @param class-string<T> $class The fully qualified class name.
39-
* @param string $method The method name.
40-
* @param string $annotationClass The annotation class to filter.
31+
* @template T
32+
* @param class-string<T> $class
33+
* @param class-string<A> $annotationClass
4134
*
42-
* @return Klist<Annotation> A list of annotations for the method.
43-
* @throws ReflectionException|Exception
35+
* @return Klist<T>
36+
* @throws ReflectionException
4437
*/
45-
public function getMethodAnnotations( string $class, string $method, string $annotationClass = Annotation::class ): Klist {
46-
return $this->mapAttributesToAnnotations(
47-
attributes: $this->reflect->getMethodAttributes( $class, $method ),
48-
annotationClass: $annotationClass
49-
);
38+
public function getClassAnnotations(
39+
string $class,
40+
string $annotationClass,
41+
): Klist {
42+
$raw = $this->reflection->getClassAttributes( $class );
43+
44+
return $this
45+
->filterAndInject( $raw, $annotationClass )
46+
->map( fn( Annotation $ann ): Annotation => $ann->setAnnotatedClass( $class ) );
5047
}
5148

5249
/**
53-
* Fetches build-related annotations for a method.
50+
* Fetch all annotations of a given type on a method.
5451
*
55-
* @param class-string<T> $class The fully qualified class name.
56-
* @param string $method The method name.
57-
* @param string $annotationClass The annotation class to filter.
52+
* @template T
53+
* @param class-string<T> $class
54+
* @param string $method
55+
* @param class-string<A> $annotationClass
5856
*
59-
* @return Klist<Annotation> A list of build annotations for the method.
57+
* @return Klist<T>
6058
* @throws ReflectionException
6159
*/
62-
public function getMethodExecutionAnnotations( string $class, string $method, string $annotationClass = Annotation::class ): Klist {
63-
return $this->getMethodAnnotations( $class, $method, $annotationClass )
64-
->filter( fn( Annotation $annotation ) => $annotation->isMethodExecutionAnnotation() )
65-
->map( fn( Annotation $annotation ) => $annotation->setAnnotatedClass( $class )->setAnnotatedMethod( $method ) );
60+
public function getMethodAnnotations(
61+
string $class,
62+
string $method,
63+
string $annotationClass,
64+
): Klist {
65+
$raw = $this->reflection->getMethodAttributes( $class, $method );
66+
67+
return $this
68+
->filterAndInject( $raw, $annotationClass )
69+
->map( fn( Annotation $ann ): Annotation => $ann
70+
->setAnnotatedClass( $class )
71+
->setAnnotatedMethod( $method )
72+
);
6673
}
6774

6875
/**
69-
* Fetches build-related annotations for a method.
76+
* Fetch both class‑level and method‑level annotations of a given type.
7077
*
71-
* @param class-string<T> $class The fully qualified class name.
72-
* @param string $method The method name.
73-
* @param string $annotationClass The annotation class to filter.
78+
* @template T
79+
* @param class-string<T> $class
80+
* @param class-string<A> $annotationClass
7481
*
75-
* @return Klist<Annotation> A list of build annotations for the method.
82+
* @return Klist<T>
7683
* @throws ReflectionException
7784
*/
78-
public function getMethodBuildAnnotations( string $class, string $method, string $annotationClass = Annotation::class ): Klist {
79-
return $this->getMethodAnnotations( $class, $method, $annotationClass )
80-
->filter( fn( Annotation $annotation ) => $annotation->isBuildAnnotation() )
81-
->map( fn( Annotation $annotation ) => $annotation->setAnnotatedClass( $class )->setAnnotatedMethod( $method ) );
85+
public function getAllAnnotations(
86+
string $class,
87+
string $annotationClass = Annotation::class
88+
): Klist {
89+
$classAnns = $this->getClassAnnotations( $class, $annotationClass );
90+
$methodAnns = $this->reflection
91+
->getAnnotatedMethods( $class, $annotationClass )
92+
->map( fn( \ReflectionMethod $m ) => $this->getMethodAnnotations( $class, $m->getName(), $annotationClass )
93+
)
94+
->flatten();
95+
96+
return $classAnns->merge( $methodAnns );
8297
}
8398

8499
/**
85-
* Fetches all build-related annotations for a class, including its methods.
100+
* Fetch all annotations of a given type on one of a method’s parameters.
86101
*
87-
* @param class-string<T> $class The fully qualified class name.
88-
* @param string $annotationClass The annotation class to filter.
102+
* @template T
103+
* @param class-string<T> $class
104+
* @param string $method
105+
* @param string $parameterName
106+
* @param class-string<A> $annotationClass
89107
*
90-
* @return Klist<Annotation> A list of all build annotations for the class.
108+
* @return Klist<A>
91109
* @throws ReflectionException
92110
*/
93-
public function getAllBuildAnnotations( string $class, string $annotationClass = Annotation::class ): Klist {
94-
return $this->reflect
95-
->getAnnotatedMethods( $class )
96-
->map( fn( ReflectionMethod $method ) => $this->getMethodBuildAnnotations( $class, $method->getName(), $annotationClass ) )
97-
->flatten()
98-
->merge( $this->getClassBuildAnnotations( $class, $annotationClass ) );
99-
}
111+
public function getParameterAnnotations(
112+
string $class,
113+
string $method,
114+
string $parameterName,
115+
string $annotationClass,
116+
): Klist {
117+
$parameter = listFrom( $this->reflection->getClassMethod( $class, $method )->getParameters() )
118+
->filter( fn( ReflectionParameter $p ) => $p->getName() === $parameterName )
119+
->firstOrNull();
100120

101-
/**
102-
* Fetches annotations for a class.
103-
*
104-
* @param class-string<T> $class The fully qualified class name.
105-
* @param string $annotationClass The annotation class to filter.
106-
*
107-
* @return Klist<Annotation> A list of annotations for the class.
108-
* @throws ReflectionException|Exception
109-
*/
110-
public function getClassAnnotations( string $class, string $annotationClass = Annotation::class ): Klist {
111-
return $this->mapAttributesToAnnotations(
112-
attributes: $this->reflect->getClassAttributes( $class ),
113-
annotationClass: $annotationClass
114-
);
121+
if ( ! $parameter ) {
122+
return emptyList();
123+
}
124+
125+
return listFrom( $parameter->getAttributes() )
126+
->map( fn( ReflectionAttribute $p ) => $p->newInstance() )
127+
->maybe( fn( Klist $attributes ) => $this->filterAndInject( $attributes, $annotationClass ) )
128+
->foreach( fn( Annotation $ann ) => $ann->setAnnotatedClass( $class )->setAnnotatedMethod( $method ) );
115129
}
116130

117131
/**
118-
* Fetches build-related annotations for a class.
132+
* Fetch a single annotation of a given type on a property.
119133
*
120-
* @param class-string<T> $class The fully qualified class name.
121-
* @param string $annotationClass The annotation class to filter.
134+
* @template T
135+
* @param class-string<T> $class
136+
* @param string $property
137+
* @param class-string<A> $annotationClass
122138
*
123-
* @return Klist<Annotation> A list of build annotations for the class.
124-
* @throws ReflectionException|Exception
139+
* @return A|null
140+
* @throws ReflectionException
125141
*/
126-
public function getClassBuildAnnotations( string $class, string $annotationClass = Annotation::class ): Klist {
127-
return $this->getClassAnnotations( $class, $annotationClass )
128-
->filter( fn( Annotation $annotation ) => $annotation->isBuildAnnotation() )
129-
->map( fn( Annotation $annotation ) => $annotation->setAnnotatedClass( $class ) );
142+
public function getPropertyAnnotation(
143+
string $class,
144+
string $property,
145+
string $annotationClass = Annotation::class
146+
): mixed {
147+
$attributes = $this->reflection
148+
->getReflectionClass( $class )
149+
->getProperty( $property )
150+
->getAttributes();
151+
152+
return listFrom( $attributes )
153+
->map( fn( ReflectionAttribute $a ) => $a->newInstance() )
154+
->maybe( fn( Klist $attributes ) => $this->filterAndInject( $attributes, $annotationClass ) )
155+
->firstOrNull()
156+
?->setAnnotatedClass( $class );
130157
}
131158

132159
/**
133-
* Maps attributes to annotations and applies property injection.
134-
*
135-
* @param Klist $attributes A list of attributes.
136-
* @param string $annotationClass The annotation class to filter.
160+
* @template T of Annotation
161+
* @param Klist<Annotation> $instances
162+
* @param class-string<T> $annotationClass
137163
*
138-
* @return Klist A list of filtered and injected annotations.
139-
* @throws Exception
164+
* @return Klist<T>
140165
*/
141-
private function mapAttributesToAnnotations( Klist $attributes, string $annotationClass ): Klist {
142-
return $attributes
143-
->filter( fn( $annotation ) => $annotation instanceof $annotationClass )
144-
->foreach( fn( $annotation ) => $this->container->applyPropertyInjection( $annotation ) );
166+
private function filterAndInject( Klist $instances, string $annotationClass ): Klist {
167+
return $instances
168+
->filter( fn( $i ) => is_a( $i, $annotationClass, true ) )
169+
->foreach( fn( Annotation $ann ) => $this->container->applyPropertyInjection( $ann ) );
145170
}
146171
}

src/Annotation/BuildAnnotation.php

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php
2+
3+
namespace Axpecto\Annotation;
4+
5+
use Attribute;
6+
use Axpecto\ClassBuilder\BuildHandler;
7+
8+
#[Attribute]
9+
class BuildAnnotation extends Annotation {
10+
/**
11+
* The builder for the annotation, used during the build phase.
12+
*
13+
* @var BuildHandler|null
14+
*/
15+
protected ?BuildHandler $builder = null;
16+
17+
/**
18+
* Gets the BuildHandler for this annotation, if available.
19+
*
20+
* @return BuildHandler|null The builder for the annotation, or null if not set.
21+
*/
22+
public function getBuilder(): ?BuildHandler {
23+
return $this->builder;
24+
}
25+
}

0 commit comments

Comments
 (0)