Skip to content

Commit

Permalink
Simplify class scanner even more, prepare to add caching for optimal …
Browse files Browse the repository at this point in the history
…development experience
  • Loading branch information
frederikbosch committed Jun 7, 2024
1 parent 43642d9 commit 58f58b7
Show file tree
Hide file tree
Showing 15 changed files with 412 additions and 177 deletions.
14 changes: 10 additions & 4 deletions docs/attributes.md
Original file line number Diff line number Diff line change
Expand Up @@ -205,9 +205,15 @@ class Route implements AttributeConfigInterface {
public function __construct(private string $method, private string $uri) {
}

public function define(Container $di, \ReflectionAttribute $attribute, \Reflector $annotatedTo): void
public function define(
Container $di,
object $attribute,
string $className,
int $attributeTarget,
array $targetConfig
): void
{
if ($reflector instanceof \ReflectionMethod) {
if ($attributeTarget === \Attribute::TARGET_METHOD) {
// considering the routes key is a lazy array, defined like this
// $resolver->values['routes'] = $container->lazyArray([]);
$di->values['routes']->append(
Expand All @@ -216,8 +222,8 @@ class Route implements AttributeConfigInterface {
$this->uri,
$container->lazyLazy(
$di->lazyCallable([
$di->lazyNew($reflector->getDeclaringClass()),
$reflector->getName()
$di->lazyNew($className),
$targetConfig['method']
])
)
)
Expand Down
39 changes: 28 additions & 11 deletions docs/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -175,14 +175,14 @@ The following example demonstrates how to scan your project source files for ann
controllers, services and repository classes into a blueprints.

```php
use Aura\Di\ClassScanner;
use Aura\Di\ClassScanner\ClassScannerConfig;
use Aura\Di\ContainerBuilder;

$builder = new ContainerBuilder();
$config_classes = [
new \MyApp\Config1,
new \MyApp\Config2,
new ClassScanner(
ClassScannerConfig::newScanner(
[$rootDir . '/app/src'], // these directories should be scanned for classes and annotations
['MyApp\\Controller\\', 'MyApp\\Service\\', 'MyApp\\Repository\\'], // classes inside these namespaces should be compiled
)
Expand All @@ -195,27 +195,44 @@ When using the `ClassScanner`, make sure to serialize and cache the container ou
not do that, directories will be scanned every instance of the container.

If your attribute cannot implement the `AttributeConfigInterface`, e.g. the attribute is defined in an external package,
you can pass a map with the class name of the attribute and an implementation of `AttributeConfigInterface`.
you can create an implementation of `AttributeConfigInterface` yourself, and annotate it with `#[DefineAttribute(ExternalAttribute::class)]`.
Then the class scanner will automatically pick up the annotation.

```php
use Aura\Di\AttributeConfigInterface;
use Aura\Di\Attribute\DefineAttribute;
use Aura\Di\ClassScanner\ClassScannerConfig;
use Aura\Di\Container;
use Symfony\Component\Routing\Attribute\Route;

new ClassScanner(
ClassScannerConfig::newScanner(
[$rootDir . '/app/src'], // these directories should be scanned for classes and annotations
['MyApp\\'], // classes inside these namespaces should be compiled,
[
Symfony\Component\Routing\Attribute\Route::class => new SymfonyRouteAttributeConfig()
]
)

#[DefineAttribute(Route::class)]
class SymfonyRouteAttributeConfig implements AttributeConfigInterface
{
public function define(Container $di, \ReflectionAttribute $attribute, \Reflector $annotatedTo): void
public function define(
Container $di,
object $attribute,
string $className,
int $attributeTarget,
array $targetConfig
): void
{
if ($annotatedTo instanceof ReflectionMethod) {
$route = $attribute->newInstance();
if ($attributeTarget === \Attribute::TARGET_METHOD) {
$invokableRoute = $di->lazyCallable([
$container->lazyNew($className),
$targetConfig['method']
]);

// these are not real parameters, but just examples
$di->values['routes'][] = new Symfony\Component\Routing\Route(
// ...
$attribute->getPath(),
$attribute->getMethods(),
$attribute->getName(),
$invokableRoute
);
}
}
Expand Down
5 changes: 2 additions & 3 deletions docs/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,15 +77,14 @@ composer require composer/class-map-generator
Creating a fully-featured container could look as follows:

```php
use Aura\Di\ClassScanner\ClassScannerConfig;
use Aura\Di\ContainerBuilder;
use Aura\Di\ClassScanner;
use Aura\Di\Resolver\ResolverFactory;

$serializedContainerFile = '/var/compiled.ser';
$config_classes = [
new \MyApp\Config1,
new \MyApp\Config2,
new ClassScanner(
ClassScannerConfig::newScanner(
[$rootDir . '/app/src'], // these directories should be scanned for classes and annotations
['MyApp\\'], // classes inside these namespaces should be compiled
)
Expand Down
29 changes: 29 additions & 0 deletions src/Attribute/DefineAttribute.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php
declare(strict_types=1);
/**
*
* This file is part of Aura for PHP.
*
* @license http://opensource.org/licenses/MIT MIT
*
*/

namespace Aura\Di\Attribute;

use Attribute;

#[Attribute(Attribute::TARGET_CLASS)]
class DefineAttribute
{
private string $className;

public function __construct(string $className)
{
$this->className = $className;
}

public function getClassName(): string
{
return $this->className;
}
}
8 changes: 7 additions & 1 deletion src/AttributeConfigInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,11 @@

interface AttributeConfigInterface
{
public function define(Container $di, \ReflectionAttribute $attribute, \Reflector $annotatedTo): void;
public function define(
Container $di,
object $attribute,
string $className,
int $attributeTarget,
array $targetConfig
): void;
}
152 changes: 0 additions & 152 deletions src/ClassScanner.php

This file was deleted.

46 changes: 46 additions & 0 deletions src/ClassScanner/AnnotatedAttribute.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<?php

declare(strict_types=1);

namespace Aura\Di\ClassScanner;

final class AnnotatedAttribute
{
private object $attributeInstance;
private string $className;
private int $attributeTarget;
private array $targetConfig;

public function __construct(
object $attributeInstance,
string $className,
int $attributeTarget,
array $targetConfig = [],
)
{
$this->attributeInstance = $attributeInstance;
$this->className = $className;
$this->attributeTarget = $attributeTarget;
$this->targetConfig = $targetConfig;
}

public function getAttributeInstance(): object
{
return $this->attributeInstance;
}

public function getClassName(): string
{
return $this->className;
}

public function getAttributeTarget(): int
{
return $this->attributeTarget;
}

public function getTargetConfig(): array
{
return $this->targetConfig;
}
}
23 changes: 23 additions & 0 deletions src/ClassScanner/ClassMap.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

declare(strict_types=1);

namespace Aura\Di\ClassScanner;

final class ClassMap
{
private array $classes = [];

public function addClass(string $class, string $filename, array $annotatedAttributes): void
{
$this->classes[$filename][$class] = $annotatedAttributes;
}

/**
* @return array<class-string, array<int, AnnotatedAttribute>>
*/
public function getClassMap(): array
{
return \array_merge([], ...\array_values($this->classes));
}
}
Loading

0 comments on commit 58f58b7

Please sign in to comment.