glaphire
@glaphire
PHP developer

Почему auto injection разрешен в Laravel?

В официальной документации Laravel есть пункт о автоматическом внедрении объектов в методах.
Пример класса, где я хочу автозаинжектить свой сервис

<?php
namespace App\Console\HabrParser;

use App\Services\ParserService;
use Illuminate\Console\Command;

class SaveLatestPosts extends Command
{
    protected $signature = 'habr-parser:save-latest-posts';

    protected $description = 'Saves latest N posts from top posts in database';

    public function handle(ParserService $parserService)
    {
        $parserService->getPagesLinks();
    }
}


Я понимаю, что под капотом работает рефлексия, которая находит класс и создает его, если класс указан в тайп-хинте метода.
Сохранила стектрейс вызовов, чтобы увидеть, как работает автоинжект.
Stacktrace

Exception trace:

  1   App\Services\ParserService::getPagesLinks()
      /habr-parser-app/app/Console/HabrParser/SaveLatestPosts.php:16

  2   App\Console\HabrParser\SaveLatestPosts::handle(Object(App\Services\ParserService))
      /habr-parser-app/vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php:32

  3   call_user_func_array()
      /habr-parser-app/vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php:32

  4   Illuminate\Container\BoundMethod::Illuminate\Container\{closure}()
      /habr-parser-app/vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php:90

  5   Illuminate\Container\BoundMethod::callBoundMethod(Object(Illuminate\Foundation\Application), Object(Closure))
      /habr-parser-app/vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php:34

  6   Illuminate\Container\BoundMethod::call(Object(Illuminate\Foundation\Application), [])
      /habr-parser-app/vendor/laravel/framework/src/Illuminate/Container/Container.php:576

  7   Illuminate\Container\Container::call()
      /habr-parser-app/vendor/laravel/framework/src/Illuminate/Console/Command.php:183

  8   Illuminate\Console\Command::execute(Object(Symfony\Component\Console\Input\ArgvInput), Object(Illuminate\Console\OutputStyle))
      /habr-parser-app/vendor/symfony/console/Command/Command.php:255

  9   Symfony\Component\Console\Command\Command::run(Object(Symfony\Component\Console\Input\ArgvInput), Object(Illuminate\Console\OutputStyle))
      /habr-parser-app/vendor/laravel/framework/src/Illuminate/Console/Command.php:170

  10  Illuminate\Console\Command::run(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
      /habr-parser-app/vendor/symfony/console/Application.php:915

  11  Symfony\Component\Console\Application::doRunCommand(Object(App\Console\HabrParser\SaveLatestPosts), Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
      /habr-parser-app/vendor/symfony/console/Application.php:272

  12  Symfony\Component\Console\Application::doRun(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
      /habr-parser-app/vendor/symfony/console/Application.php:148

  13  Symfony\Component\Console\Application::run(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
      /habr-parser-app/vendor/laravel/framework/src/Illuminate/Console/Application.php:90

  14  Illuminate\Console\Application::run(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
      /habr-parser-app/vendor/laravel/framework/src/Illuminate/Foundation/Console/Kernel.php:133

  15  Illuminate\Foundation\Console\Kernel::handle(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
      /habr-parser-app/artisan:37


Рефлексия для неприклепленных к сервис контейнеру классов и методов сосредоточена тут:
https://github.com/laravel/framework/blob/5.8/src/...

Вопрос: почему в принципе существует авто инжект, почему он не противоречит явному прикреплению сервиса к сервис контейнеру - разве это не обход этого принципа? Почему нет более четкого механизма, как явный autowiring сервисов в Symfony?
  • Вопрос задан
  • 318 просмотров
Решения вопроса 2
@D3lphi
А чему он, собственно, противоречит? Вот у вас есть какой-нибудь произвольный PostService, который вы инжектите в PostController. PostService зависит, например, от PostRepository и CommentRepository (которые являются интерфейсами). У PostRepository имеется конкретная реализация, например, DoctrinePostRepository. Тоже самое и у репозитория комментариев. Сервис контейнер об этом знает, ибо мы все это забиндили в сервис провайдере:
$container->singleton(PostRepository::class, DoctrinePostRepository::class);
$container->singleton(CommentRepository ::class, DoctrineCommentRepository ::class);

То есть, контейнер сумеет разрешить зависимости PostService. Теперь вопрос к вам: для чего нам явно указывать что-то в сервис провайдере, если контейнер и так в состоянии самостоятельно создать объект, передав ему нужные зависимости?
Ответ написан
glaphire
@glaphire Автор вопроса
PHP developer
После долгих поисков истины оставляю ссылку в помощь детям и внукам - читайте про dependency injection container вне документаций по фреймворкам
php-di.org/doc/understanding-di.html
Ответ написан
Комментировать
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы