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. Вы можете объявить класс как синглтон, если хотите переиспользовать один и тот же объект
  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 :-(']);

Примечания: метод wrap() не является частью интерфейса Container, он есть только в классе Container. См. [Pull Request(https://github.com/laravel/framework/pull/16800), в котором объясняется, почему параметры не передаются при переопределении.


Биндинг на основе контекста


Может случиться так, что вы захотите иметь разные реализации одного интерфейса в зависимости от места, где он необходим. Ниже показан немного измененный пример из документации 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.

Поделиться с друзьями
-->

Комментарии (19)


  1. Legion21
    01.07.2017 18:04
    -6

    Для меня laravel слишком медленный (((


    1. saggid
      01.07.2017 18:34

      По сравнению с чем? Для получения прироста скорости есть также Lumen — официальный "обрубок" от Laravel, созданный специально для более быстрой работы.


      1. SerafimArts
        01.07.2017 19:22

        У лары достаточно отрубить пару ненужных провайдеров и получится тот же самый люмен (ну и наоборот). С другой стороны, если на симфони или ещё что-то навешать те же плюшки, что есть по-умолчанию в ларке, то по скорости отличий не будет.


      1. Legion21
        01.07.2017 20:34
        -4

        Если использовать для full stack, то golang backend + reactjs front делают любой php фреймворк по производительности (даже если взять laravel + reactjs)


        1. saggid
          01.07.2017 20:39

          Неплохая связка в целом. Но что-то меня единственное что тут не особо прельщает — то что придётся рендерить со стороны сервера большую часть сайта из-за такой связки. То бишь, сюда надо добавить ещё Node.js в обязательной поставке для серверного рендера, а это очень большое потребление озу и в том числе и процессора. И я не уверен, что вся эта связка технологий будет прямо сильно удобнее и быстрее PHP + react + node.js. Как мы сейчас например делаем. При этом, реально серверным рендером у нс рендерятся только небольшие динамические компоненты, кусочки сайта, по сути. Когда всё остальное без проблем рендерится на php.


          1. Legion21
            01.07.2017 21:04
            -1

            Можно обойтись без серверного рендеринга впринципе… Все зависит от задач, а если учесть что API будет задействованно не только для web, но и для мобильных устройств, + всякие там ws и тд… то на PHP это дело + тот же Node поддерживать — неблагодарное дело


            1. saggid
              01.07.2017 21:08

              В целом, думаю, с вами можно спорить и дальше. Golang не столь удобен для разработки сайтов, чем PHP. Думаю, это очевидно. Что из аналогов Ларавел можно найти на Go? Пусть производительность будет немного ниже (ещё большой вопрос — насколько реально ниже). Зато Ларавел даст удобство и скорость разработки.


              1. Legion21
                01.07.2017 23:20
                -3

                Для разработки сайтов возможно, но микросервис RestFull API на нем пишется в несколько раз быстрее, чем на Symfony, Laravel и подобных… да и скорость выполнения с разницой в 10-100 раз это круто) Удобство это стимул развития Go в целом, для саморазвития очень советую…


                1. SerafimArts
                  02.07.2017 07:37
                  +1

                  Ну на счёт пишется быстрее в несколько раз — это перебор. Всё же ускорить написание ровно одной строчки (artisan make:controller) в несколько раз — довольно проблематично.


                  Ну и если говорить про сам Go — он довольно скучен и убог, как язык. Это не плохо, это его задача, быть простым как пробка и идеально выполнять свою задачу "модного паскаля с корутинками и тучей типов данных". Не надо на него вешать те задачи, с которыми он не справляется. Современный бекэнд, уровня Laravel и Rails он не осилит, ну вообще никак. А под микросервисы вообще пофигу что брать, пару-тройку апишных методов можно хоть по 10 раз на дню переписывать на разных языках =)


                  1. Legion21
                    02.07.2017 18:13

                    Видимо вы просто слабо знакомы с Go)


                    1. SerafimArts
                      02.07.2017 18:39

                      А вы так же слабо знакомы с темой топика, т.е. с ларкой. 1:1 =)


                1. seniorcote
                  02.07.2017 12:04

                  Сожалею, у вас оверинжиниринг :(


                1. Fesor
                  03.07.2017 00:42
                  +1

                  но микросервис RestFull API

                  как много buzz вордов… во первых микросервисы подавляющему большинству ненужны. Во вторых ваш RestFull API не более чем RPC. В случае с микро сервисной архитектурой у вас все равно будет перед вашими сервисами како-нибудь api gateway.


                  Во вторых, "пишется в несколько раз быстрее" только если вы никогда не писали апишки на этих самых Symfony/Laravel.


                  да и скорость выполнения с разницой в 10-100 раз это круто)

                  моя практика показывает разницу в 5-10 раз. И да, это круто, не спорю. Но только появляются свои нюансы. Мне нравится использовать go там где у меня задачи упираются в CPU (а это мягко скажем небольшой процент задач, почти все задачи и так решены на Си). А так мне не слишком то много дела выполняется запрос за 5ms или за 50ms, с учетом того что пинги в 3G/4G сетях побольше будут и пользователи разницы не особо почувствуют.


                  С другой стороны на PHP проще писать, если у вас один модуль вашего монолитного приложения сбоит и крэшится на каждом втором запросе, пользователи которые не делают запросов к этому модулю вообще ничего не почувствуют. Так же можно попросту забить на нюансы с управлением памятью в большинстве случаев. Идеальный инструмент чтобы быстренько накидывать продукты. Ну и опять же, ограничиваться одним инструментом для решения всех задач глупо, будь то go или php.


                  для саморазвития очень советую…

                  Но вы советуете Go… он весьма… примитивный язык. Ну то есть, да, можно поиграться с горутинами и синхронизацией но это если мы пишем какой-нибудь брокер сообщений. А если говорить про апишечку — то все становится чуточку проще.


                  Если уж говорить про "саморазвитие" — то тут скорее надо rust ковырять, или erlang на худой конец. А Go — это весело пару месяцев а потом скучно. Хотя как инструмент для своих задач он неплох.


                1. niksamokhvalov
                  04.07.2017 14:44

                  Поправка: RESTful, а не Full. И рест ресту рознь: сделать конвертацию выборки из БД в джейсон и сделать сервис с сортировкой, группировкой, фильтрацией и пр. — разной степени сложности задачи.


                  1. Fesor
                    04.07.2017 16:17

                    разной степени сложности задачи.

                    как по мне одно и то же с большего. Вот если у нас не тупо CRUD и списки/репорты — это тогда уже другое.


        1. VolCh
          05.07.2017 07:15

          Со всеми возможностями ОРМ, включая маппинг иерархии классов?


  1. newpy
    01.07.2017 18:39
    +1

    Спасибо, очень информативно и полезно.


  1. yvm
    05.07.2017 12:28

    Все круто, конечно, но где-то там в глубине «тупит» Reflection…


    1. Fesor
      05.07.2017 16:45

      оно кэшируется.