Вам бонус- начислено 1 монета за дневную активность. Сейчас у вас 1 монета

IoC-контейнер Laravel

Лекция



Привет, Вы узнаете о том , что такое laravel, Разберем основные их виды и особенности использования. Еще будет много подробных примеров и описаний. Для того чтобы лучше понимать что такое laravel, ioc, dependency injection , настоятельно рекомендую прочитать все из категории Фреймворки. Famworks ( программная платформа).

Laravel имеет мощный IoC-контейнер, но, к сожалению, официальная документация Laravel не описывает все его возможности. Я решил изучить его и задокументировать для собственного использования.

Примеры в данной статье основаны на Laravel 5.4.26, другие версии могут отличаться.

Введение в Dependency Injection

Я не буду объяснять, что такое DI и IoC в этой статье — если вы не знакомы с этими принципами, вы можете прочитать статью "What is Dependency Injection?" от Fabien Potencier (создателя фреймворка Symfony).

Dependency Injection или “внедрение зависимости” — один из лучших паттернов проектирования приложения, который позволяет писать приложения более тестируемыми, а код, в свое время, становится более читабельным.
В этой статье я хочу рассказать о том, что такое Dependency Injection (DI) и как реализовывать его в Laravel.

Что такое Dependency Injection

На тему DI написано довольно таки много книг и статей, но если кратко, то DI — это процесс предоставления внешней зависимости программному компоненту. Инъекция зависимостей происходит при помощи IoC контейнера, который поставляется вместе с фреймворком или ставится отдельно.

Dependency Injection на практике

Давайте представим, что у нас есть класс Book

class Book {
    public $author; // автор
    public $title; // название
}

Собственно, это класс сущности “книга” и в нем описаны его свойства — автор и название.

У нас есть второй класс BookStorage. У него есть метод store, который создает новую запись в базе данных.

class BookStorage
{       
    public function store($author, $title)
    {        
        $book = new Book;
        $book->author = $author;
        $book->title = $title;        // сохраняем в БД
        // INSERT INTO `books` ...            
    }        
}

Для того чтобы воспользоваться этим методом, нам нужно написать:

$bookStorage = new BookStorage;
$bookStorage->store('Федор Михайлович Достоевский', 'Идиот');

Данная реализация подходит для большинства программ и нельзя назвать ее неправильной, но когда вы дойдете до тестирования данного кода — вы не сможете протестировать метод store класса BookStorage в полной изоляции т.к. создать Mock-объект Book у вас так просто не получится. Давайте теперь напишем класс BookStorage следуя паттерну DI.

class BookStorage
{
    public function store(Book $book) {
        // сохраняем в БД
    }
}

Для того, чтобы воспользоваться методом store вам теперь нужно передать готовый объект Book (или его Mock в тестах):

$book = new Book;
$book->author = 'Федор Михайлович Достоевский';
$book->title = 'Идиот';$bookStorage = new BookStorage;
$bookStorage->store($book);

Но это еще не все. Ведь мы физически никуда не сохранили данную запись. Нам нужен класс Database, который позволит нам вставить запись в БД. И теперь класс BookStorage хочет воспользоваться этим классом. Как бы мы реализовали это без DI?

class BookStorage
{
    private $db;
    public function __construct()
    {
        $this->db = new Database; // или Database::getInstance()
    }    public function store(Book $book)
    {
        $this->db->insert('books', [
            'author' => $book->author,
            'title'  => $book->title
        ]);
    }
}

В конструкторе мы создаем объект Database и записываем его в свойство класса, но опять же — мы не сможем протестировать класс BookStorage в полной изоляции, т.к. класс Database будет все равно создан физически. Давайте перепишем конструктор:

public function __construct(Database $db)
{
      $this->db = $db;
}

Вот теперь другое дело. В наших тестах мы сможем подставить Mock-объект класса Database. Теперь наш код сохранения записи в БД будет выглядеть следующим образом:

$book = new Book;
$book->author = 'Федор Михайлович Достоевский';
$book->title = 'Идиот';$database = new Database;$bookStorage = new BookStorage($database);
$bookStorage->store($book);

DI и IoC в Laravel

Кратко мы ознакомились с принципом DI, а теперь я хочу рассмотреть DI в рамках Laravel. В Laravel идет уже отличный IoC контейнер из коробки и как им можно пользоваться более эффективно я разберу в отдельной теме.
Давайте разберем на простом примере, как воспользоваться DI в Laravel. У нас есть контроллер BookController, который отдает информацию по книге по запросу GET /book/1.

Код без DI:

class BookController extends Controller
{        
    public function get($id)
    {            
        return Book::find($id);     
    }        
}

Код с DI:

class BookController extends Controller
{        
    private $book;        
    public function __construct(Book $book)
    {
        $this->book = $book;
    }    public function get($id)
    {
        return $this->book->find($id);      
    }        
}

Все выглядит замечательно, но тут напрашиваются вопросы: где создается объект Book, что в нем находится? Если кратко, то IoC создаст экземляр класса Book и просунет его в конструктор.

Получение контейнера (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) { /* ... Об этом говорит сайт https://intellect.icu  . */ }
    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. Вы можете объявить класс как синглтон, если хотите переиспользовать один и тот же объект
  3. Вы можете использовать интерфейс или произвольное имя вместо названия класса

Пример ниже будет работать:

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.

Исследование, описанное в статье про laravel, подчеркивает ее значимость в современном мире. Надеюсь, что теперь ты понял что такое laravel, ioc, dependency injection и для чего все это нужно, а если не понял, или есть замечания, то не стесняйся, пиши или спрашивай в комментариях, с удовольствием отвечу. Для того чтобы глубже понять настоятельно рекомендую изучить всю информацию из категории Фреймворки. Famworks ( программная платформа)

Ответы на вопросы для самопроверки пишите в комментариях, мы проверим, или же задавайте свой вопрос по данной теме.

создано: 2020-07-13
обновлено: 2024-11-14
5



Рейтиг 9 of 10. count vote: 2
Вы довольны ?:


Поделиться:

Найди готовое или заработай

С нашими удобными сервисами без комиссии*

Как это работает? | Узнать цену?

Найти исполнителя
$0 / весь год.
  • У вас есть задание, но нет времени его делать
  • Вы хотите найти профессионала для выплнения задания
  • Возможно примерение функции гаранта на сделку
  • Приорететная поддержка
  • идеально подходит для студентов, у которых нет времени для решения заданий
Готовое решение
$0 / весь год.
  • Вы можите продать(исполнителем) или купить(заказчиком) готовое решение
  • Вам предоставят готовое решение
  • Будет предоставлено в минимальные сроки т.к. задание уже готовое
  • Вы получите базовую гарантию 8 дней
  • Вы можете заработать на материалах
  • подходит как для студентов так и для преподавателей
Я исполнитель
$0 / весь год.
  • Вы профессионал своего дела
  • У вас есть опыт и желание зарабатывать
  • Вы хотите помочь в решении задач или написании работ
  • Возможно примерение функции гаранта на сделку
  • подходит для опытных студентов так и для преподавателей

Комментарии


Оставить комментарий
Если у вас есть какое-либо предложение, идея, благодарность или комментарий, не стесняйтесь писать. Мы очень ценим отзывы и рады услышать ваше мнение.
To reply

Фреймворки. Famworks ( программная платформа)

Термины: Фреймворки. Famworks ( программная платформа)