Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Define voters, handlers, and permissions using PHP 8 attributes
- Loading branch information
Showing
24 changed files
with
465 additions
and
80 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
<?php | ||
|
||
/* | ||
* This file is part of the ICanBoogie package. | ||
* | ||
* (c) Olivier Laviale <olivier.laviale@gmail.com> | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
|
||
namespace ICanBoogie\MessageBus\Attribute; | ||
|
||
use Attribute; | ||
|
||
/** | ||
* Identifies a message handler. | ||
* | ||
* **Note:** The message type supported by the handler is inferred from its `__invoke` method. | ||
* | ||
* @readonly | ||
*/ | ||
#[Attribute(Attribute::TARGET_CLASS)] | ||
final class Handler | ||
{ | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
<?php | ||
|
||
/* | ||
* This file is part of the ICanBoogie package. | ||
* | ||
* (c) Olivier Laviale <olivier.laviale@gmail.com> | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
|
||
namespace ICanBoogie\MessageBus\Attribute; | ||
|
||
use Attribute; | ||
|
||
/** | ||
* Identifies a permission required to dispatch a message. | ||
* | ||
* A message can have multiple {@link Permission}s. | ||
* | ||
* @readonly | ||
*/ | ||
#[Attribute(Attribute::TARGET_CLASS | Attribute::IS_REPEATABLE)] | ||
final class Permission | ||
{ | ||
public function __construct( | ||
public string $permission | ||
) { | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
<?php | ||
|
||
namespace ICanBoogie\MessageBus\Attribute; | ||
|
||
use Attribute; | ||
|
||
/** | ||
* Identifies a voter and the permission it votes for. | ||
* | ||
* @readonly | ||
*/ | ||
#[Attribute(Attribute::TARGET_CLASS)] | ||
final class Vote | ||
{ | ||
public function __construct( | ||
public string $permission, | ||
) { | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
<?php | ||
|
||
/* | ||
* This file is part of the ICanBoogie package. | ||
* | ||
* (c) Olivier Laviale <olivier.laviale@gmail.com> | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
|
||
namespace ICanBoogie\MessageBus\Symfony; | ||
|
||
use ICanBoogie\MessageBus\Attribute; | ||
use LogicException; | ||
use olvlvl\ComposerAttributeCollector\Attributes; | ||
use ReflectionException; | ||
use ReflectionMethod; | ||
use ReflectionNamedType; | ||
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; | ||
use Symfony\Component\DependencyInjection\ContainerBuilder; | ||
use Symfony\Component\DependencyInjection\Definition; | ||
|
||
use function assert; | ||
|
||
/** | ||
* Creates handler and voter services according to their attributes. | ||
* | ||
* This compiler pass is meant to run before {@link MessageBusPass}. | ||
*/ | ||
final class MessageBusPassWithAttributes implements CompilerPassInterface | ||
{ | ||
public function __construct( | ||
private string $tagForHandler = MessageBusPass::DEFAULT_TAG_FOR_HANDLER, | ||
private string $attributeForMessage = MessageBusPass::DEFAULT_ATTRIBUTE_FOR_MESSAGE, | ||
private string $tagForPermission = MessageBusPass::DEFAULT_TAG_FOR_PERMISSION, | ||
private string $attributeForPermission = MessageBusPass::DEFAULT_ATTRIBUTE_FOR_PERMISSION, | ||
private string $tagForVoter = MessageBusPass::DEFAULT_TAG_FOR_VOTER, | ||
) { | ||
} | ||
|
||
/** | ||
* @throws ReflectionException | ||
*/ | ||
public function process(ContainerBuilder $container): void | ||
{ | ||
/** @var array<class-string, Definition> $definitions */ | ||
$definitions = []; | ||
|
||
/** | ||
* @var array<class-string, string[]> $permissions | ||
* Where _key_ is a command and _value_ its associated permissions. | ||
*/ | ||
$permissions = []; | ||
|
||
foreach (Attributes::findTargetClasses(Attribute\Permission::class) as $targetClass) { | ||
$permissions[$targetClass->name][] = $targetClass->attribute->permission; | ||
} | ||
|
||
foreach (Attributes::findTargetClasses(Attribute\Handler::class) as $targetClass) { | ||
$handler = $targetClass->name; | ||
$message = self::resolveMessage($handler); | ||
|
||
$definition = new Definition($handler); | ||
$definition->addTag($this->tagForHandler, [ $this->attributeForMessage => $message ]); | ||
|
||
foreach ($permissions[$message] ?? [] as $permission) { | ||
$definition->addTag($this->tagForPermission, [ $this->attributeForPermission => $permission ]); | ||
} | ||
|
||
$definitions[$handler] = $definition; | ||
} | ||
|
||
foreach (Attributes::findTargetClasses(Attribute\Vote::class) as $targetClass) { | ||
$voter = $targetClass->name; | ||
$permission = $targetClass->attribute->permission; | ||
|
||
$definition = new Definition($voter); | ||
$definition->addTag($this->tagForVoter, [ $this->attributeForPermission => $permission ]); | ||
|
||
$definitions[$voter] = $definition; | ||
} | ||
|
||
$container->addDefinitions($definitions); | ||
} | ||
|
||
/** | ||
* @param class-string $handler | ||
* | ||
* @return class-string | ||
* | ||
* @throws ReflectionException | ||
*/ | ||
private static function resolveMessage(string $handler): string | ||
{ | ||
$type = (new ReflectionMethod($handler, '__invoke')) | ||
->getParameters()[0] | ||
->getType() ?? throw new LogicException("Expected a type for the first argument"); | ||
|
||
assert($type instanceof ReflectionNamedType); | ||
|
||
// @phpstan-ignore-next-line | ||
return $type->getName(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
15 changes: 15 additions & 0 deletions
15
tests/Acme/MenuService/Application/MessageBus/CreateMenu.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
<?php | ||
|
||
namespace Acme\MenuService\Application\MessageBus; | ||
|
||
use ICanBoogie\MessageBus\Attribute\Permission; | ||
|
||
#[Permission(Permissions::IS_ADMIN)] | ||
#[Permission(Permissions::CAN_WRITE_MENU)] | ||
final class CreateMenu | ||
{ | ||
public function __construct( | ||
public /*readonly*/ int $id | ||
) { | ||
} | ||
} |
13 changes: 13 additions & 0 deletions
13
tests/Acme/MenuService/Application/MessageBus/CreateMenuHandler.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
<?php | ||
|
||
namespace Acme\MenuService\Application\MessageBus; | ||
|
||
use ICanBoogie\MessageBus\Attribute\Handler; | ||
|
||
#[Handler] | ||
final class CreateMenuHandler | ||
{ | ||
public function __invoke(CreateMenu $message): void | ||
{ | ||
} | ||
} |
15 changes: 15 additions & 0 deletions
15
tests/Acme/MenuService/Application/MessageBus/DeleteMenu.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
<?php | ||
|
||
namespace Acme\MenuService\Application\MessageBus; | ||
|
||
use ICanBoogie\MessageBus\Attribute\Permission; | ||
|
||
#[Permission(Permissions::IS_ADMIN)] | ||
#[Permission(Permissions::CAN_MANAGE_MENU)] | ||
final class DeleteMenu | ||
{ | ||
public function __construct( | ||
public /*readonly*/ int $id | ||
) { | ||
} | ||
} |
13 changes: 13 additions & 0 deletions
13
tests/Acme/MenuService/Application/MessageBus/DeleteMenuHandler.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
<?php | ||
|
||
namespace Acme\MenuService\Application\MessageBus; | ||
|
||
use ICanBoogie\MessageBus\Attribute\Handler; | ||
|
||
#[Handler] | ||
final class DeleteMenuHandler | ||
{ | ||
public function __invoke(DeleteMenu $message): void | ||
{ | ||
} | ||
} |
10 changes: 10 additions & 0 deletions
10
tests/Acme/MenuService/Application/MessageBus/Permissions.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
<?php | ||
|
||
namespace Acme\MenuService\Application\MessageBus; | ||
|
||
final class Permissions | ||
{ | ||
public const IS_ADMIN = 'is_admin'; | ||
public const CAN_WRITE_MENU = 'can_write_menu'; | ||
public const CAN_MANAGE_MENU = 'can_manage_menu'; | ||
} |
Oops, something went wrong.