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)
{
}
}
class UserManager
{
private $mailer;
public function __construct(Mailer $mailer)
{
$this->mailer = $mailer;
}
public function register($email, $password)
{
$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()
{
$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') { }
$container->call('show_product', [1]);
$container->call('show_product', ['id' => 1]);
$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']);
$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]);
, ..:
- ,
- ,
:
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');
$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();
$container->instance(Session::class, new Session(['username' => 'danny']));
echo $auth->username();
.
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;
});
( ), :
$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'));
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));
$container->make(Database::class);
var_dump($container->resolved(Database::class));
unset()
(. bound()
).
unset($container[Database::class]);
var_dump($container->resolved(Database::class));
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.