laravel 简单聊聊singleton的实现过程

文章目录

场景

  • 最近在看laravel request lifecycle;laravel 在完成Illuminate\Foundation\Application的实例化之后,接着就注册了三个Kernel的singleton ; 那么singleton 完成了什么操作了呢?
  • 下面就以$app->singleton(Illuminate\Contracts\Http\Kernel::class, App\Http\Kernel::class);为例聊聊实现的过程
$app->singleton(
    Illuminate\Contracts\Http\Kernel::class,
    App\Http\Kernel::class
);

$app->singleton(
    Illuminate\Contracts\Console\Kernel::class,
    App\Console\Kernel::class
);

$app->singleton(
    Illuminate\Contracts\Debug\ExceptionHandler::class,
    App\Exceptions\Handler::class
);

解析

  • singleton 本质调用的是 Illuminate\Container\Container的bind function

    • $concrete = $this->getClosure($abstract, $concrete); 将$concrete 转换成 Closure function , 里面的封装很重要,return $container->make($concrete, $parameters);
    • $this->bindings[$abstract] = compact('concrete', 'shared'); 将$concrete 和$share 填写到应用的bindings属性上,key 是$abstract
  • 从服务容器中解析的会发生什么呢?

  • 本质上触发的是Illuminate\Container\Container的resolve function

  • if (isset($this->instances[$abstract]) && ! $needsContextualBuild) {return $this->instances[$abstract]; } 除了第一次之后的resolve才会从instances中返回实例

  • $concrete = $this->getConcrete($abstract); 这里获取到的就是上面填充应用的bindings属性, return $this->bindings[$abstract]['concrete'];

  • $object = $this->build($concrete); 这里面的执行非常有意思,

    • 因为$concrete是Closure 所以会执行到这里 c o n c r e t e ( concrete( concrete(this, $this->getLastParameterOverride());
    • 因为这个Closure function函数内部封装的是return $container->make($concrete, $parameters); 不过这次的参数 $container->make(‘App\Http\Kernel::class’, []); 再次触发resove函数,then build(‘App\Http\Kernel::class’)
    • 因为 $concrete === “App\Http\Kernel” $reflector = new ReflectionClass($concrete); return $reflector->newInstanceArgs($instances);
    • 综上得到的是 App\Http\Kernel 实例
  • if ($this->isShared($abstract) && ! $needsContextualBuild) {$this->instances[$abstract] = $object;}
    因为singleton所以实例会存储起来 下次直接从instances属性中获取实例

  • $this->resolved[$abstract] = true; 设置 $abstract resolved过了

  • 返回 App\Http\Kernel::class 实例

源码

    /**
     * Resolve the given type from the container.
     *
     * @param  string  $abstract
     * @param  array  $parameters
     * @return mixed
     */
    protected function resolve($abstract, $parameters = [])
    {
        $abstract = $this->getAlias($abstract);

        $needsContextualBuild = ! empty($parameters) || ! is_null(
            $this->getContextualConcrete($abstract)
        );
        // If an instance of the type is currently being managed as a singleton we'll
        // just return an existing instance instead of instantiating new instances
        // so the developer can keep using the same objects instance every time.
        if (isset($this->instances[$abstract]) && ! $needsContextualBuild) {
            return $this->instances[$abstract];
        }
        $this->with[] = $parameters;

        $concrete = $this->getConcrete($abstract);

        // We're ready to instantiate an instance of the concrete type registered for
        // the binding. This will instantiate the types, as well as resolve any of
        // its "nested" dependencies recursively until all have gotten resolved.
        if ($this->isBuildable($concrete, $abstract)) {
            $object = $this->build($concrete);
        } else {
            $object = $this->make($concrete);
        }

        // If we defined any extenders for this type, we'll need to spin through them
        // and apply them to the object being built. This allows for the extension
        // of services, such as changing configuration or decorating the object.
        foreach ($this->getExtenders($abstract) as $extender) {
            $object = $extender($object, $this);
        }

        // If the requested type is registered as a singleton we'll want to cache off
        // the instances in "memory" so we can return it later without creating an
        // entirely new instance of an object on each subsequent request for it.
        if ($this->isShared($abstract) && ! $needsContextualBuild) {
            $this->instances[$abstract] = $object;
        }

        $this->fireResolvingCallbacks($abstract, $object);

        // Before returning, we will also set the resolved flag to "true" and pop off
        // the parameter overrides for this build. After those two things are done
        // we will be ready to return back the fully constructed class instance.
        $this->resolved[$abstract] = true;
        array_pop($this->with);
        

        return $object;
    }

    /**
     * Determine if the given concrete is buildable.
     *
     * @param  mixed   $concrete
     * @param  string  $abstract
     * @return bool
     */
    protected function isBuildable($concrete, $abstract)
    {
        return $concrete === $abstract || $concrete instanceof Closure;
    }

    /**
     * Instantiate a concrete instance of the given type.
     *
     * @param  string  $concrete
     * @return mixed
     *
     * @throws \Illuminate\Contracts\Container\BindingResolutionException
     */
    public function build($concrete)
    {
        // If the concrete type is actually a Closure, we will just execute it and
        // hand back the results of the functions, which allows functions to be
        // used as resolvers for more fine-tuned resolution of these objects.
        if ($concrete instanceof Closure) {
            return $concrete($this, $this->getLastParameterOverride());
        }
        
        $reflector = new ReflectionClass($concrete);

        // If the type is not instantiable, the developer is attempting to resolve
        // an abstract type such as an Interface of Abstract Class and there is
        // no binding registered for the abstractions so we need to bail out.
        if (! $reflector->isInstantiable()) {
            return $this->notInstantiable($concrete);
        }

        $this->buildStack[] = $concrete;

        $constructor = $reflector->getConstructor();

        // If there are no constructors, that means there are no dependencies then
        // we can just resolve the instances of the objects right away, without
        // resolving any other types or dependencies out of these containers.
        if (is_null($constructor)) {
            array_pop($this->buildStack);
            return new $concrete;
        }

        $dependencies = $constructor->getParameters();

        // Once we have all the constructor's parameters we can create each of the
        // dependency instances and then use the reflection instances to make a
        // new instance of this class, injecting the created dependencies in.
        $instances = $this->resolveDependencies(
            $dependencies
        );

        array_pop($this->buildStack);

        return $reflector->newInstanceArgs($instances);
    }

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要在 Laravel 中整合 PHPSocket.io 实现 web 消息推送,你需要遵循以下步骤: 1. 安装 PHPSocket.io 你可以使用 Composer 包管理器安装 PHPSocket.io。在终端中,导航到 Laravel 项目目录并运行以下命令: ``` composer require elephant-io/elephant.io ``` 2. 配置 PHPSocket.io 在 Laravel 项目中,你需要在 `config/app.php` 文件中添加以下行: ```php 'aliases' => [ // ... 'ElephantIO' => 'ElephantIO\Engine\SocketIO\Version1X', ], ``` 3. 创建 PHPSocket.io 服务提供者 在 Laravel 项目中,你需要创建 PHPSocket.io 服务提供者。运行以下命令来创建: ``` php artisan make:provider SocketIoServiceProvider ``` 在 `SocketIoServiceProvider` 类中,你需要添加以下内容: ```php use ElephantIO\Client; use ElephantIO\Engine\SocketIO\Version1X; public function register() { $this->app->singleton(Client::class, function ($app) { $client = new Client(new Version1X('http://localhost:3000')); return $client; }); } ``` 此代码将在 Laravel 应用程序中注册 `Client` 类的单例实例。 4. 创建推送事件 在 Laravel 项目中,你需要创建推送事件。运行以下命令来创建: ``` php artisan make:event PushNotification ``` 在 `PushNotification` 类中,你需要添加以下内容: ```php public $message; public function __construct($message) { $this->message = $message; } public function broadcastOn() { return new PrivateChannel('push-notification'); } ``` 此代码将创建一个名为 `PushNotification` 的事件类,并在构造函数中接受要推送的消息。`broadcastOn` 方法指定了事件应该广播到的频道。 5. 创建事件监听器 在 Laravel 项目中,你需要创建事件监听器。运行以下命令来创建: ``` php artisan make:listener PushNotificationListener ``` 在 `PushNotificationListener` 类中,你需要添加以下内容: ```php use ElephantIO\Client; protected $socket; public function __construct(Client $socket) { $this->socket = $socket; } public function handle(PushNotification $event) { $this->socket->initialize(); $this->socket->emit('push-notification', ['message' => $event->message]); $this->socket->close(); } ``` 此代码将创建一个名为 `PushNotificationListener` 的事件监听器,并在构造函数中注入 `Client` 实例。`handle` 方法将使用 `Client` 类向 Socket.io 服务器发送消息。 6. 注册事件和监听器 在 Laravel 项目中,你需要在 `EventServiceProvider` 类中注册事件和监听器。在 `EventServiceProvider` 类中,你需要添加以下内容: ```php protected $listen = [ PushNotification::class => [ PushNotificationListener::class, ], ]; ``` 此代码将注册 `PushNotification` 事件和 `PushNotificationListener` 监听器。 7. 发送推送消息 在 Laravel 项目中,你可以使用以下代码发送推送消息: ```php event(new PushNotification('Hello world!')); ``` 这将触发 `PushNotification` 事件,并将消息发送到 Socket.io 服务器。然后,`PushNotificationListener` 监听器将从 Socket.io 服务器接收到消息并发送到连接到服务器的所有客户端。 以上是整合 PHPSocket.io 实现 web 消息推送的步骤。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值