PHP Laravel/Symfony 框架核心:IoC 容器、服务提供者与事件系统

各位观众老爷,大家好!欢迎来到今天的"PHP Laravel/Symfony 框架核心:IoC 容器、服务提供者与事件系统" 讲座。

今天咱们不搞那些虚头巴脑的概念,直接上干货。争取用最接地气的方式,把 Laravel 和 Symfony 这俩框架里最重要的核心机制给扒个精光。别害怕,咱们一步一个脚印,保证听完之后,你也能成为框架大师!

一、IoC 容器:掌握框架的"任督二脉"

IoC,Inversion of Control,控制反转。 听起来高大上,其实说白了,就是把创建对象这件事儿的控制权,从程序员手里交给框架来做。 以前我们自己 new 对象,现在让框架来 new,这就是控制反转。

为什么要有这个东西呢? 因为能解耦!

想象一下,你写了一个 UserController,里面要用到 UserService。 如果你直接在 UserController 里面 new UserService(),那 UserController 就和 UserService 紧紧地绑在一起了。 以后你想换个 UserService 的实现,比如用 BetterUserService,那就得改 UserController 的代码,简直是噩梦!

有了 IoC 容器,我们就可以这样玩:

  1. 告诉容器,UserService 应该用哪个类来实例化。
  2. UserController 里面,让容器自动注入 UserService 的实例。

这样,UserController 就不用关心 UserService 到底是怎么创建的,只需要知道它能用就行了。 解耦,就是这么简单粗暴!

1. Laravel 的 IoC 容器

Laravel 的 IoC 容器非常强大,它提供了很多方便的方法来绑定、解析依赖。

  • 绑定 (Binding): 告诉容器,某个接口或者类,应该用哪个具体类来实例化。

    • 简单绑定:

      // 将接口 AppServicesUserServiceInterface 绑定到实现类 AppServicesUserService
      $this->app->bind(AppServicesUserServiceInterface::class, AppServicesUserService::class);

      这种方式最直接,容器每次都会 new 一个新的 UserService 实例。

    • 单例绑定 (Singleton Binding):

      // 将接口 AppServicesUserServiceInterface 绑定到实现类 AppServicesUserService,并且只实例化一次
      $this->app->singleton(AppServicesUserServiceInterface::class, AppServicesUserService::class);

      singleton 保证了容器只会创建一个 UserService 实例,后续每次请求都会返回同一个实例。 适合无状态的服务。

    • 实例绑定 (Instance Binding):

      // 直接绑定一个已有的 UserService 实例
      $userService = new AppServicesUserService();
      $this->app->instance(AppServicesUserServiceInterface::class, $userService);

      这种方式可以让你手动创建一个对象,然后把它放到容器里,方便控制。

    • 上下文绑定 (Contextual Binding):

      // 当 UserController 需要 UserServiceInterface 时,使用 BetterUserService
      $this->app->when(AppHttpControllersUserController::class)
                ->needs(AppServicesUserServiceInterface::class)
                ->give(AppServicesBetterUserService::class);

      这个厉害了,可以根据不同的上下文,绑定不同的实现。 比如 UserController 需要 UserServiceInterface 的时候,就用 BetterUserService,其他地方还是用默认的 UserService

  • 解析 (Resolving): 从容器中获取实例。

    • 自动解析 (Automatic Resolution):

      Laravel 的 IoC 容器会自动解析构造函数的依赖。 比如:

      namespace AppHttpControllers;
      
      use AppServicesUserServiceInterface;
      
      class UserController extends Controller
      {
          protected $userService;
      
          public function __construct(UserServiceInterface $userService)
          {
              $this->userService = $userService;
          }
      
          public function index()
          {
              // 使用 $this->userService
          }
      }

      Laravel 会自动从容器中获取 UserServiceInterface 的实例,并注入到 UserController 的构造函数中。 这就是依赖注入 (Dependency Injection),简称 DI。

    • 手动解析 (Manual Resolution):

      $userService = $this->app->make(AppServicesUserServiceInterface::class);

      如果你想手动获取实例,可以用 make() 方法。

2. Symfony 的 IoC 容器

Symfony 的 IoC 容器也叫 Service Container,原理和 Laravel 差不多,都是用来管理对象的依赖关系。

  • 定义服务 (Defining Services):

    在 Symfony 中,我们通常在 config/services.yaml 文件中定义服务。

    services:
        AppServicesUserService:
            class: AppServicesUserService
        AppControllerUserController:
            arguments: ['@AppServicesUserService']

    这个 YAML 文件告诉 Symfony,AppServicesUserService 是一个服务,它的类是 AppServicesUserServiceAppControllerUserController 也是一个服务,它的构造函数需要一个 AppServicesUserService 的实例。

  • 自动装配 (Autowiring):

    Symfony 默认开启了自动装配,它可以自动解析构造函数的依赖,和 Laravel 的自动解析类似。

  • 手动获取服务 (Fetching Services):

    $userService = $this->container->get('AppServicesUserService');

    在 Symfony 的 Controller 中,你可以通过 $this->container 来获取服务。

二、服务提供者:框架的"启动引擎"

服务提供者 (Service Provider) 是 Laravel 和 Symfony 用来启动各种服务的关键组件。 它们负责注册绑定到 IoC 容器,加载配置文件,注册事件监听器,等等。 可以理解为框架的"启动引擎"。

1. Laravel 的服务提供者

Laravel 的服务提供者是一个继承自 IlluminateSupportServiceProvider 的类,通常包含 register()boot() 两个方法。

  • register() 方法:

    这个方法用来绑定服务到 IoC 容器。

    namespace AppProviders;
    
    use IlluminateSupportServiceProvider;
    use AppServicesUserServiceInterface;
    use AppServicesUserService;
    
    class AppServiceProvider extends ServiceProvider
    {
        public function register()
        {
            $this->app->bind(UserServiceInterface::class, UserService::class);
        }
    }
  • boot() 方法:

    这个方法在所有服务注册完成后执行,用来执行一些启动任务,比如加载配置文件,注册路由,等等。

    namespace AppProviders;
    
    use IlluminateSupportServiceProvider;
    use IlluminateSupportFacadesBlade;
    
    class AppServiceProvider extends ServiceProvider
    {
        public function boot()
        {
            Blade::directive('datetime', function ($expression) {
                return "<?php echo ($expression)->format('Y-m-d H:i:s'); ?>";
            });
        }
    }

    上面的代码注册了一个 Blade 指令,可以在视图中使用 @datetime($date) 来格式化日期。

  • 注册服务提供者:

    config/app.php 文件的 providers 数组中注册服务提供者。

    'providers' => [
        // 其他服务提供者
        AppProvidersAppServiceProvider::class,
    ],

2. Symfony 的服务提供者

Symfony 没有直接对应的 "服务提供者" 概念,但是它提供了 Kernel Events 和 Compiler Passes 来实现类似的功能。

  • Kernel Events:

    Symfony 的 Kernel 会触发一系列的事件,比如 kernel.requestkernel.responsekernel.exception 等等。 你可以监听这些事件,并在事件发生时执行一些操作。

    namespace AppEventListener;
    
    use SymfonyComponentHttpKernelEventRequestEvent;
    
    class RequestListener
    {
        public function onKernelRequest(RequestEvent $event)
        {
            // 在请求开始时执行一些操作
            $request = $event->getRequest();
            $request->attributes->set('startTime', microtime(true));
        }
    }

    需要在 config/services.yaml 中注册监听器:

    services:
        AppEventListenerRequestListener:
            tags:
                - { name: kernel.event_listener, event: kernel.request, method: onKernelRequest }
  • Compiler Passes:

    Compiler Passes 允许你在容器编译的过程中修改容器的定义。 可以用来动态地添加服务,修改服务的配置,等等。

    namespace AppDependencyInjectionCompiler;
    
    use SymfonyComponentDependencyInjectionCompilerCompilerPassInterface;
    use SymfonyComponentDependencyInjectionContainerBuilder;
    use SymfonyComponentDependencyInjectionReference;
    
    class AddUserCheckerCompilerPass implements CompilerPassInterface
    {
        public function process(ContainerBuilder $container)
        {
            if (!$container->hasDefinition('security.authentication.manager')) {
                return;
            }
    
            $definition = $container->getDefinition(
                'security.authentication.manager'
            );
    
            $taggedServices = $container->findTaggedServiceIds(
                'app.user_checker'
            );
    
            foreach ($taggedServices as $id => $tags) {
                $definition->addMethodCall(
                    'addUserChecker',
                    [new Reference($id)]
                );
            }
        }
    }

    需要在 Kernel 中注册 Compiler Pass:

    namespace App;
    
    use AppDependencyInjectionCompilerAddUserCheckerCompilerPass;
    use SymfonyComponentDependencyInjectionContainerBuilder;
    use SymfonyComponentHttpKernelKernel as BaseKernel;
    
    class Kernel extends BaseKernel
    {
        protected function build(ContainerBuilder $container)
        {
            $container->addCompilerPass(new AddUserCheckerCompilerPass());
        }
    }

三、事件系统:框架的"消息中心"

事件系统 (Event System) 允许你在应用程序的不同部分之间解耦。 一个组件可以触发一个事件,而其他组件可以监听这个事件并执行相应的操作。 就像一个消息中心,负责传递各种通知。

1. Laravel 的事件系统

Laravel 的事件系统非常强大,它提供了事件、监听器、订阅者等概念。

  • 事件 (Event): 一个代表发生了某件事情的类。

    namespace AppEvents;
    
    use IlluminateQueueSerializesModels;
    
    class UserRegistered
    {
        use SerializesModels;
    
        public $user;
    
        public function __construct($user)
        {
            $this->user = $user;
        }
    }
  • 监听器 (Listener): 一个监听特定事件并执行相应操作的类。

    namespace AppListeners;
    
    use AppEventsUserRegistered;
    use IlluminateContractsQueueShouldQueue;
    
    class SendWelcomeEmail implements ShouldQueue
    {
        public function handle(UserRegistered $event)
        {
            // 发送欢迎邮件给用户
            Mail::to($event->user->email)->send(new AppMailWelcomeEmail($event->user));
        }
    }

    ShouldQueue 接口表示该监听器应该被异步执行。

  • 注册事件监听器:

    app/Providers/EventServiceProvider.php 文件中注册事件监听器。

    namespace AppProviders;
    
    use IlluminateSupportFacadesEvent;
    use IlluminateFoundationSupportProvidersEventServiceProvider as ServiceProvider;
    
    class EventServiceProvider extends ServiceProvider
    {
        protected $listen = [
            AppEventsUserRegistered::class => [
                AppListenersSendWelcomeEmail::class,
            ],
        ];
    
        public function boot()
        {
            parent::boot();
    
            //
        }
    }
  • 触发事件 (Dispatching Events):

    event(new AppEventsUserRegistered($user));

    或者使用 Event facade:

    Event::dispatch(new AppEventsUserRegistered($user));
  • 事件订阅者 (Event Subscribers):

    事件订阅者是一个类,可以订阅多个事件。

    namespace AppListeners;
    
    class UserEventSubscriber
    {
        public function handleUserRegistered($event)
        {
            // 处理用户注册事件
        }
    
        public function handleUserLogin($event)
        {
            // 处理用户登录事件
        }
    
        public function subscribe($events)
        {
            $events->listen(
                'AppEventsUserRegistered',
                'AppListenersUserEventSubscriber@handleUserRegistered'
            );
    
            $events->listen(
                'AppEventsUserLogin',
                'AppListenersUserEventSubscriber@handleUserLogin'
            );
        }
    }

    注册事件订阅者:

    namespace AppProviders;
    
    use IlluminateSupportFacadesEvent;
    use IlluminateFoundationSupportProvidersEventServiceProvider as ServiceProvider;
    
    class EventServiceProvider extends ServiceProvider
    {
        protected $subscribe = [
            AppListenersUserEventSubscriber::class,
        ];
    
        public function boot()
        {
            parent::boot();
    
            //
        }
    }

2. Symfony 的事件系统

Symfony 的事件系统基于 Symfony 的 EventDispatcher 组件。

  • 事件 (Event): 一个继承自 SymfonyComponentEventDispatcherEvent 的类。

    namespace AppEvent;
    
    use SymfonyComponentEventDispatcherEvent;
    
    class UserRegisteredEvent extends Event
    {
        const NAME = 'user.registered';
    
        protected $user;
    
        public function __construct($user)
        {
            $this->user = $user;
        }
    
        public function getUser()
        {
            return $this->user;
        }
    }
  • 事件监听器 (Event Listener): 一个监听特定事件并执行相应操作的类。

    namespace AppEventListener;
    
    use AppEventUserRegisteredEvent;
    use SymfonyComponentMailerMailerInterface;
    use SymfonyComponentMimeEmail;
    
    class UserRegisteredListener
    {
        private $mailer;
    
        public function __construct(MailerInterface $mailer)
        {
            $this->mailer = $mailer;
        }
    
        public function onUserRegistered(UserRegisteredEvent $event)
        {
            $user = $event->getUser();
    
            $email = (new Email())
                ->from('[email protected]')
                ->to($user->getEmail())
                ->subject('Welcome!')
                ->text('Welcome to our website!');
    
            $this->mailer->send($email);
        }
    }
  • 注册事件监听器:

    config/services.yaml 文件中注册事件监听器。

    services:
        AppEventListenerUserRegisteredListener:
            arguments: ['@mailer.mailer']
            tags:
                - { name: kernel.event_listener, event: AppEventUserRegisteredEvent::NAME, method: onUserRegistered }
  • 触发事件 (Dispatching Events):

    use AppEventUserRegisteredEvent;
    use SymfonyComponentEventDispatcherEventDispatcherInterface;
    
    public function registerUser(EventDispatcherInterface $dispatcher)
    {
        // ... 注册用户 ...
    
        $event = new UserRegisteredEvent($user);
        $dispatcher->dispatch($event, UserRegisteredEvent::NAME);
    }

四、总结:掌握核心,才能玩转框架

特性 Laravel Symfony 描述
IoC 容器 $this->appbindsingletonmake $this->containerservices.yaml,自动装配 用于管理对象的依赖关系,解耦代码。
服务提供者 ServiceProviderregisterboot Kernel Events,Compiler Passes 用于启动各种服务,注册绑定到 IoC 容器,加载配置文件,注册事件监听器等。
事件系统 EventListenerSubscriberevent() EventListenerEventDispatcherdispatch() 允许在应用程序的不同部分之间解耦,一个组件可以触发一个事件,而其他组件可以监听这个事件并执行相应的操作。

今天我们深入探讨了 Laravel 和 Symfony 框架的核心:IoC 容器、服务提供者和事件系统。 理解这些概念对于构建可维护、可扩展的应用程序至关重要。 掌握了这些核心概念,你就能更好地理解框架的运行机制,更高效地开发应用程序。

希望今天的讲座对你有所帮助! 谢谢大家!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注