Используем IoC-контейнер Laravel на полную мощность

Laravel IoC-, , , Laravel . .







Laravel 5.4.26, .







Dependency Injection



, DI IoC — , "What is Dependency Injection?" Fabien Potencier ( Symfony).







(Container)



Laravel * app()



:







$container = app();
      
      





, .







*



Laravel Application, Container ( app()



), Container.







Illuminate\Container Laravel



Laravel Composer, :







use Illuminate\Container\Container;

$container = Container::getInstance();
      
      







— , type hinting:







class MyClass
{
    private $dependency;

    public function __construct(AnotherClass $dependency)
    {
        $this->dependency = $dependency;
    }
}
      
      





, new MyClass



, make()



:







$instance = $container->make(MyClass::class);
      
      





, :







$instance = new MyClass(new AnotherClass());
      
      





( , AnotherClass



. , ..)









, PHP-DI. :







class Mailer
{
    public function mail($recipient, $content)
    {
        // Send an email to the recipient
        // ...
    }
}
      
      





class UserManager
{
    private $mailer;

    public function __construct(Mailer $mailer)
    {
        $this->mailer = $mailer;
    }

    public function register($email, $password)
    {
        // Create the user account
        // ...

        // Send the user an email to say hello!
        $this->mailer->mail($email, 'Hello and welcome!');
    }
}
      
      





use Illuminate\Container\Container;

$container = Container::getInstance();

$userManager = $container->make(UserManager::class);
$userManager->register('dave@davejamesmiller.com', 'MySuperSecurePassword!');
      
      







:







interface MyInterface { /* ... */ }
interface AnotherInterface { /* ... */ }
      
      





, . ( , ):







class MyClass implements MyInterface
{
    private $dependency;

    public function __construct(AnotherInterface $dependency)
    {
        $this->dependency = $dependency;
    }
}
      
      





bind()



:







$container->bind(MyInterface::class, MyClass::class);
$container->bind(AnotherInterface::class, AnotherClass::class);
      
      





make()



:







$instance = $container->make(MyInterface::class);
      
      





: , :







Fatal error: Uncaught ReflectionException: Class MyInterface does not exist
      
      





, (new MyInterface



), .









— :







interface Cache
{
    public function get($key);
    public function put($key, $value);
}
      
      





class RedisCache implements Cache
{
    public function get($key) { /* ... */ }
    public function put($key, $value) { /* ... */ }
}
      
      





class Worker
{
    private $cache;

    public function __construct(Cache $cache)
    {
        $this->cache = $cache;
    }

    public function result()
    {
        // Use the cache for something...
        $result = $this->cache->get('worker');

        if ($result === null) {
            $result = do_something_slow();

            $this->cache->put('worker', $result);
        }

        return $result;
    }
}
      
      





use Illuminate\Container\Container;

$container = Container::getInstance();
$container->bind(Cache::class, RedisCache::class);

$result = $container->make(Worker::class)->result();
      
      







:







$container->bind(MyAbstract::class, MyConcreteClass::class);
      
      





(, ):







$container->bind(MySQLDatabase::class, CustomMySQLDatabase::class);
      
      





Custom Bindings



, bind()



:







$container->bind(Database::class, function (Container $container) {
    return new MySQLDatabase(MYSQL_HOST, MYSQL_PORT, MYSQL_USER, MYSQL_PASS);
});
      
      





, Database, MySQLDatabase ( , Singleton, ).







Container, , :







$container->bind(Logger::class, function (Container $container) {
    $filesystem = $container->make(Filesystem::class);

    return new FileLogger($filesystem, 'logs/error.log');
});
      
      





:







$container->bind(GitHub\Client::class, function (Container $container) {
    $client = new GitHub\Client;
    $client->setEnterpriseUrl(GITHUB_HOST);

    return $client;
});
      
      





Resolving Callbacks



, , resolving()



, :







$container->resolving(GitHub\Client::class, function ($client, Container $container) {
    $client->setEnterpriseUrl(GITHUB_HOST);
});
      
      





, . :







$container->resolving(Logger::class, function (Logger $logger) {
    $logger->setLevel('debug');
});

$container->resolving(FileLogger::class, function (FileLogger $logger) {
    $logger->setFilename('logs/debug.log');
});

$container->bind(Logger::class, FileLogger::class);

$logger = $container->make(Logger::class);
      
      





, ( ):







$container->resolving(function ($object, Container $container) {
    // ...
});
      
      







extend()



, :







$container->extend(APIClient::class, function ($client, Container $container) {
    return new APIClientDecorator($client);
});
      
      





, , .







Singleton



, ( , bind()



), ( ). , singleton()



bind()



:







$container->singleton(Cache::class, RedisCache::class);
      
      





:







$container->singleton(Database::class, function (Container $container) {
    return new MySQLDatabase('localhost', 'testdb', 'user', 'pass');
});
      
      





, , , :







$container->singleton(MySQLDatabase::class);
      
      





, .







, , instance()



. , Laravel , Container :







$container->instance(Container::class, $container);
      
      







, type hinting make()



:







$container->bind('database', MySQLDatabase::class);

$db = $container->make('database');
      
      





, , alias()



:







$container->singleton(Cache::class, RedisCache::class);
$container->alias(Cache::class, 'cache');

$cache1 = $container->make(Cache::class);
$cache2 = $container->make('cache');

assert($cache1 === $cache2);
      
      







(, ):







$container->instance('database.name', 'testdb');

$db_name = $container->make('database.name');
      
      





array-access , :







$container['database.name'] = 'testdb';

$db_name = $container['database.name'];
      
      





-:







$container->singleton('database', function (Container $container) {
    return new MySQLDatabase(
        $container['database.host'],
        $container['database.name'],
        $container['database.user'],
        $container['database.pass']
    );
});
      
      





( Laravel , — Config, PHP-DI ).







: array-access make()



:







$db = $container['database'];
      
      





Dependency Injection



DI , Laravel DI :







function do_something(Cache $cache) { /* ... */ }

$result = $container->call('do_something');
      
      





:







function show_product(Cache $cache, $id, $tab = 'details') { /* ... */ }

// show_product($cache, 1)
$container->call('show_product', [1]);
$container->call('show_product', ['id' => 1]);

// show_product($cache, 1, 'spec')
$container->call('show_product', [1, 'spec']);
$container->call('show_product', ['id' => 1, 'tab' => 'spec']);
      
      





DI :









$closure = function (Cache $cache) { /* ... */ };

$container->call($closure);
      
      







class SomeClass
{
    public static function staticMethod(Cache $cache) { /* ... */ }
}
      
      





$container->call(['SomeClass', 'staticMethod']);
// or:
$container->call('SomeClass::staticMethod');
      
      







class PostController
{
    public function index(Cache $cache) { /* ... */ }
    public function show(Cache $cache, $id) { /* ... */ }
}
      
      





$controller = $container->make(PostController::class);

$container->call([$controller, 'index']);
$container->call([$controller, 'show'], ['id' => 1]);
      
      







Container ClassName@methodName



. :







$container->call('PostController@index');
$container->call('PostController@show', ['id' => 4]);
      
      





, ..:







  1. ,
  2. ,


:







class PostController
{
    public function __construct(Request $request) { /* ... */ }
    public function index(Cache $cache) { /* ... */ }
}
      
      





$container->singleton('post', PostController::class);
$container->call('post@index');
      
      





, " " . , . Laravel :







$container->call(MyEventHandler::class, $parameters, 'handle');

// Equivalent to:
$container->call('MyEventHandler@handle', $parameters);
      
      







bindMethod()



, , :







$container->bindMethod('PostController@index', function ($controller, $container) {
    $posts = get_posts(...);

    return $controller->index($posts);
});
      
      





, :







$container->call('PostController@index');
$container->call('PostController', [], 'index');
$container->call([new PostController, 'index']);
      
      





, call()



, :







$container->call('PostController@index', ['Not used :-(']);
      
      





: bindMethod()



Container, Container. . Pull Request, , .









, , . Laravel:







$container
    ->when(PhotoController::class)
    ->needs(Filesystem::class)
    ->give(LocalFilesystem::class);

$container
    ->when(VideoController::class)
    ->needs(Filesystem::class)
    ->give(S3Filesystem::class);
      
      





PhotoController VideoController Filesystem, .







give()



, bind()



:







$container
    ->when(VideoController::class)
    ->needs(Filesystem::class)
    ->give(function () {
        return Storage::disk('s3');
    });
      
      





:







$container->instance('s3', $s3Filesystem);

$container
    ->when(VideoController::class)
    ->needs(Filesystem::class)
    ->give('s3');
      
      







, (, ..). ( ) needs()



, give()



, :







$container
    ->when(MySQLDatabase::class)
    ->needs('$username')
    ->give(DB_USER);
      
      





give()



, , , :







$container
    ->when(MySQLDatabase::class)
    ->needs('$username')
    ->give(function () {
        return config('database.user');
    });
      
      





give()



(, give('database.user')



), , . :







$container
    ->when(MySQLDatabase::class)
    ->needs('$username')
    ->give(function (Container $container) {
        return $container['database.user'];
    });
      
      







( ) :







$container->tag(MyPlugin::class, 'plugin');
$container->tag(AnotherPlugin::class, 'plugin');
      
      





:







foreach ($container->tagged('plugin') as $plugin) {
    $plugin->init();
}
      
      





tag()



:







$container->tag([MyPlugin::class, AnotherPlugin::class], 'plugin');
$container->tag(MyPlugin::class, ['plugin', 'plugin.admin']);
      
      





Rebinding



: , .







, rebinding()



, . , Auth, Auth :







$container->singleton(Auth::class, function (Container $container) {
    $auth = new Auth;
    $auth->setSession($container->make(Session::class));

    $container->rebinding(Session::class, function ($container, $session) use ($auth) {
        $auth->setSession($session);
    });

    return $auth;
});

$container->instance(Session::class, new Session(['username' => 'dave']));

$auth = $container->make(Auth::class);
echo $auth->username(); // dave

$container->instance(Session::class, new Session(['username' => 'danny']));
echo $auth->username(); // danny
      
      





.







refresh()



, — refresh()



:







$container->singleton(Auth::class, function (Container $container) {
    $auth = new Auth;
    $auth->setSession($container->make(Session::class));

    $container->refresh(Session::class, $auth, 'setSession');

    return $auth;
});
      
      





( ), :







//  ,      `singleton()`  `bind()`    
$container->singleton(Session::class);

$container->singleton(Auth::class, function (Container $container) {
    $auth = new Auth;

    $auth->setSession($container->refresh(Session::class, $auth, 'setSession'));

    return $auth;
});
      
      





, , .







: Container, Container.







Overriding Constructor Parameters



makeWith()



. (.. ). - :







class Post
{
    public function __construct(Database $db, int $id) { /* ... */ }
}
      
      





$post1 = $container->makeWith(Post::class, ['id' => 1]);
$post2 = $container->makeWith(Post::class, ['id' => 2]);
      
      





: Laravel >=5.3 make($class, $parameters)



. Laravel 5.4, makeWith



5.4.16. , Laravel 5.5 make()



.









, , .







bound()



bound()



, , bind()



, singleton()



, instance()



alias()



:







if (! $container->bound('database.user')) {
    // ...
}
      
      





isset array-access :







if (! isset($container['database.user'])) {
    // ...
}
      
      





, binding()



, instance()



, alias()



unset()



:







unset($container['database.user']);

var_dump($container->bound('database.user')); // false
      
      





bindIf()



bindIf()



, bind()



, , (. bound()



). , .







$container->bindIf(Loader::class, FallbackLoader::class);
      
      





singletonIf()



, bindIf($abstract, $concrete, true):







$container->bindIf(Loader::class, FallbackLoader::class, true);
      
      





:







if (! $container->bound(Loader::class)) {
    $container->singleton(Loader::class, FallbackLoader::class);
}
      
      





resolved()



resolved()



true, .







var_dump($container->resolved(Database::class)); // false

$container->make(Database::class);

var_dump($container->resolved(Database::class)); // true
      
      





unset()



(. bound()



).







unset($container[Database::class]);

var_dump($container->resolved(Database::class)); // false
      
      





factory()



factory()



, make()



.







$dbFactory = $container->factory(Database::class);

$db = $dbFactory();
      
      





wrap()



wrap()



, . , ; :







$cacheGetter = function (Cache $cache, $key) {
    return $cache->get($key);
};

$usernameGetter = $container->wrap($cacheGetter, ['username']);

$username = $usernameGetter();
      
      





: wrap()



Container, Container.







afterResolving()



afterResolving()



, resolving()



, , , , resolving()



.









isShared()



– , /

isAlias()



– ,

hasMethodBinding()



– ,

getBindings()





getAlias($abstract)



– /

forgetInstance($abstract)





forgetInstances()





flush()



– ,

setInstance()



– , getInstance() (: setInstance(null)



, )







: Container.








All Articles