📜 ⬆️ ⬇️

Dependency Injection Container from PHPixie

image
I do not like DI containers. Yes, they are convenient, but over time a lot of problems arise with them, so PHPixie uses the classic approach with the Factory pattern. The ability to get any service from the container sometimes breaks the logical chain of the program, for example, when some validator pulls a service from a completely different bundle in Symfony2. It is even worse when it is used as a Service Locator where all dependencies are obtained through a call in the style of Locator :: get ('doctrine.entityManager') . In addition, different container implementations encourage configuration storage in YML and XML files, which sometimes makes debugging difficult. But recently, I remembered the phrase “Do not think that a developer is a fool”, that is, not to impose your point on when designing an architecture. In addition, it is difficult to argue with the fact that small projects are much easier to build using a container and / or dependency locator.

Meet the PHPixie DI.

It should be noted right away that it is not used in the framework itself, although it comes with the default bundle as a fully optional component. There are no dependencies, so you can use it completely separately from PHPixie.

Config
')
To build a container, you need to expand the base class and override the configure method in it, for example:

class Container extends \PHPixie\DI\Container\Root { public function configure() { //     $this->value('apiToken', '1234567890'); //    $this->callback('addFive', function($a, $b) { return $a + $b; }); //      , //      $this->build('twitterService', function() { return new TwitterService($this->apiToken()); //  return new TwitterService($this->get('apiToken')); }); //     : $this->instance('twitterService', TwitterService::class, ['@apiToken']); //      '@'        //   $this->group('user', function() { $this->instance('repository', UserRepository::class, ['@twitterService']); }); } } //   $container = new Container(); //    $container->get('apiToken'); $container->apiToken(); //     //    Container::apiToken(); Container::get('apiToken'); //   //        $container->add(6, 7); // 13 $container->call('add', [6, 7]); $callable = $container->get('add'); $callable(6, 7); //    $container->get('user.repository'); $userGroup = $container->user(); $userGroup->repository(); Container::user()->repository(); // ... 


As an additional bonus, using the container, you can access the methods of the classes, for example, let's say there is a getTweets method in the TwitterService class, then you can do this:

 $container->get('twitterService.getTweets'); // $container->twitterService()->getTweets(); //   $container->call('twitterService.getTweets.first.delete', [true]); // $container->twitterService()->getTweets()->first()->delete(true); //      


By the way, all methods value , callback , build and instance are declared as protected . So, after building the container, nothing can be added or changed to it, which will protect you from being able to shoot yourself in the foot of changing the container on the fly (debugging is very unpleasant then). But if you still need to configure it from the outside, you can always make it public. By the way, “configuration only from the inside” is one of my favorite features.

IDE Tips
It's worth mentioning that there is an opportunity to add annotations to the class that will allow most IDEs to prompt you with the names of the methods:

 /** * @method TwitterService twitterService() * @method static TwitterService twitterService() */ class Container { //... } 


Use with PHPixie

Create a container class in your bundle:
 namespace Project\App; //    ,    //     class Container extends \PHPixie\DefaultBundle\Container { public function configure() { //.... parent::configure(); } } 


And add it to Builder:
 namespace Project\App; class Builder extends \PHPixie\DefaultBundle\Builder { protected function buildContainer() { return new Container($this); } } 


Builder will automatically create an instance of the container, so you can safely use static methods. Well, a few examples:

 $container = $builder->container(); $container->get('components.orm'); $query = $container->call('components.orm.query', ['user']); $builder = Container::builder(); $frameworkBuilder = Container::frameworkBuilder(); 


I hope you enjoy the new component and add “phpixie / di”: "~ 3.0" to your composer.json .

Source: https://habr.com/ru/post/301648/


All Articles