PHPのSlim3 Frameworkでは、以下のようなコードでgetリクエストが走った時のコールバック処理内で、DIコンテナへアクセスすることができます。
$app = new \Slim\App();
// myServiceをDIコンテナを取得
$container = $app->getContainer();
$container['myService'] = function ($container) {
$myService = new MyService();
return $myService;
};
$app->get('/foo', function ($req, $res, $args) {
// DIコンテナからオブジェクトを取得
// (下記のコードは、$this->myService;とも書ける)
$myService = $this->get('myService'); // なぜか、$thisでDIコンテナが取得できる!?
...
});
この中で不思議なのは、コールバック関数内で$thisでDIコンテナが取得できることです。
このコードで$thisがDIコンテナであることは、下記のコードで確認できます。
$app->get('/foo', function ($req, $res, $args) {
echo get_class($this); // "Slim\Container" が出力される
...
});
$thisがDIコンテナになる仕組み
仕組みは、get()メソッドの中身を追っていくとわかります。
実際にget()メソッドの中身を見ると、そのままmap()メソッドに処理を委譲しています。
# vendor\slim\slim\Slim\App.php
namespace Slim;
...
class App
{
...
public function get($pattern, $callable)
{
return $this->map(['GET'], $pattern, $callable);
}
で、map()メソッドを見ると以下のような実装になっています。
public function map(array $methods, $pattern, $callable)
{
if ($callable instanceof Closure) {
$callable = $callable->bindTo($this->container);
}
$route = $this->container->get('router')->map($methods, $pattern, $callable);
if (is_callable([$route, 'setContainer'])) {
$route->setContainer($this->container);
}
if (is_callable([$route, 'setOutputBuffering'])) {
$route->setOutputBuffering($this->container->get('settings')['outputBuffering']);
}
return $route;
}
この中で注目すべきは、下記の行です。
$callable = $callable->bindTo($this->container);
PHPでは無名関数は、Closureクラスのインスタンスとして実装されています。ClosureクラスにはbindTo()メソッドが用意されており、bindTo()のパラメータで渡されたオブジェクトを$thisオブジェクトとみなすことができるという仕組みなっています。
Slim3 FrameworkではこのbindTo()メソッドの仕組みを利用して、$thisがDIコンテナであるかのように見なしているわけです。
こちらもおススメ