Autowire and configure using PHP 8 Attributes in Laravel.
Via Composer
composer require jeroen-g/autowire
You will need the configuration file to change where it should look:
php artisan vendor:publish --tag=autowire.config
Are you tired of binding abstract interfaces to concrete classes all the time?
$this->app->bind(HelloInterface::class, WorldClass::class);
Use the PHP 8 attribute of this package to autowire any of your interfaces:
namespace App\Contracts;
use JeroenG\Autowire\Attribute\Autowire;
#[Autowire]
interface HelloInterface
{
public function hello(): string;
}
The class that implements that interface does not need any changes:
namespace App;
use App\Contracts\HelloInterface;
class WorldClass implements HelloInterface
{
public function hello(): string
{
return 'world';
}
}
The Autowire package will crawl through the classes and bind the abstract interface to the concrete class. If there already is a binding in the container it will skip the autowiring.
If you quickly want to tag all implementations of an interface, simply add the Tag
attribute to the interface:
namespace App\Contracts;
use JeroenG\Autowire\Attribute\Tag;
#[Tag('myTag')]
interface HelloInterface
{
public function hello(): string;
}
All implementations will now be available under the 'myTag' tag:
$this->app->when(Greeting::class)
->needs(HelloInterface::class)
->giveTagged('myTag');
If no tag value is specified in the attribute, the fully-namespaced name of the class will be used as the tag:
namespace App\Contracts;
use JeroenG\Autowire\Attribute\Tag;
#[Tag]
interface GoodbyeInterface
{
public function goodbye(): string;
}
$this->app->when(Greeting::class)
->needs(GoodbyeInterface::class)
->giveTagged(GoodbyeInterface::class);
Personally I like injection of dependencies over resolving them using make()
helpers.
However, that means writing binding definitions such as:
$this->app->when($x)->needs($y)->give($z);
Not anymore with the Configure attribute! Here is the WorldClass example again:
namespace App;
use App\Contracts\HelloInterface;
#[Configure(['$message' => 'world'])]
class WorldClass
{
private $message;
public function __construct($message)
{
$this->message = $message;
}
}
In this example message is a simple string. However, it can be a reference to a configuration value or other class too! The notations of config and service definitions is the same as used in Symfony.
// Will get the value set in config/app.php
#[Configure(['$message' => '%app.message%'])]
// Will inject an instance of the Message class
#[Configure(['$message' => '@App\Domain\Message'])]
// When you have multiple constructor arguments
#[Configure(['$message' => '%app.message%', '$logger' => '@Psr\Log\LoggerInterface'])]
If you use a lot of events, the EventServiceProvider
will very likely become long and messy:
protected $listen = [
Registered::class => [
UpdateLastLogin::class,
...
],
...
];
With the PHP 8 attribute of this package you can define the events for a listener alongside each other:
#[Listen(Registered::class)]
#[Listen(Login::class)]
class UpdateLastLoginListener {
...
}
The package will crawl through the classes and bind the listeners to the event classes.
The autowiring, configuration and listeners can be cached with the command php artisan autowire:cache
.
In a similar fashion it can be cleared with php artisan autowire:clear
.
Keep in mind that caching means that it won't crawl all the classes and changes to the annotations will not be loaded.
The package's configuration can be found in config/autowire.php
.
It should contain the list of directories where Autowire should look for both interfaces and implementations.
It is possible to use custom Attribute classes for either or both the autowiring or configuration functionality:
- Create a custom attribute class, making sure to implement either
JeroenG\Autowire\Attribute\AutowireInterface
orJeroenG\Autowire\Attribute\ConfigureInterface
, depending on the attribute you want to replace. - Add a
autowire_attribute
,configure_attribute
,tag_attribute
orlisten_attribute
setting to theconfig/autowire.php
file, containing the fully-namespaced name of your custom attribute class. - Use your custom attribute to mark the interface or class you want to autowire or configure.
Please see the changelog for more information on what has changed recently.
MIT. Please see the license file for more information.