В предыдущих трёх статьях мы уже изучили контент, связанный с контейнером сервисов. Можно сказать, что контейнер сервисов — это душа всей платформы Laravel. С первого этапа запуска необходимо создать контейнер и загрузить его. все объекты обслуживания. Говоря о конвейерах, с ними все знакомы. В мире разработки программ приложения с конвейерным режимом можно увидеть повсюду. Аналогично, в среде Laravel он также является основным. Можно даже сказать, что сочетание конвейеров и сервисных контейнеров дает нам такую основу для использования.
Как упоминалось ранее, конвейерный режим очень распространен. Почему вы так говорите?
ps -ef | grep php
Это распространено? Часто пользуетесь? Эта команда Linux является командой канала. Результат предыдущей команды передается для выполнения следующей команде, как по каналу, позволяя результату запроса команды течь вниз. Это применение шаблона конвейера.
О чем еще можно подумать, кроме этого? Если вы следили за моей серией шаблонов проектирования PHP, то шаблон цепочки ответственности, очевидно, представляет собой применение шаблона конвейера в объектно-ориентированных языках.
Конвейерный режим обычно используется вместе с фильтрами. Что такое фильтр? По сути, нам нужны промежуточные методы для обработки запросов, такие как grep в приведенной выше команде, или такие команды, как wc и awk. Фактически, вы быстро обнаружите, что в среде Laravel наше промежуточное программное обеспечение представляет собой фильтр. Данные, которые мы хотим обработать, — это объект запроса Request.
Помните метод sendRequestThroughRouter(), который мы видели в сервисном контейнере? Кроме того, мы также говорили об этом, когда впервые говорили о промежуточном программном обеспечении. Давайте еще раз взглянем на его код.
protected function sendRequestThroughRouter($request)
{
$this->app->instance('request', $request);
Facade::clearResolvedInstance('request');
$this->bootstrap();
return (new Pipeline($this->app))
->send($request)
->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware)
->then($this->dispatchToRouter());
}
В этом коде последний возвращенный объект Pipeline является объектом конвейера. Давайте посмотрим, что означают эти методы.
public function __construct(Container $container = null)
{
$this->container = $container;
}
public function send($passable)
{
$this->passable = $passable;
return $this;
}
public function through($pipes)
{
$this->pipes = is_array($pipes) ? $pipes : func_get_args();
return $this;
}
Методы конструктора, send() иthrough() относительно просты. Они просто присваивают значения свойствам текущего объекта. В этом нет ничего особенного. Однако в объекте Pipeline все методы возвращают $this, что фактически реализует цепной вызов объекта.
Основное внимание уделяется методу then().
public function then(Closure $destination)
{
$pipeline = array_reduce(
array_reverse($this->pipes()), $this->carry(), $this->prepareDestination($destination)
);
return $pipeline($this->passable);
}
Этот метод удивительно прост, не так ли? В нем используется только один array_reduce(). Хорошо, теперь вы можете похвастаться перед интервьюером. Ядром конвейера или промежуточного программного обеспечения в Laravel на самом деле является метод array_reduce(). Чтобы понять, что делает метод then(), мы должны сначала понять, что делает array_reduce().
Сигнатура функции array_reduce() в официальной документации следующая:
array_reduce(array $array, callable $callback, mixed $initial = null): mixed
Его функция заключается в итеративном применении обратного вызова функции обратного вызова к каждой ячейке массива массива, тем самым сводя массив к одному значению. Если указан необязательный параметр Initial, этот параметр будет использоваться в качестве начального значения при начале обработки или будет возвращен как конечный результат, если массив пуст.
обратный вызов Эта функция обратного вызова будет иметь два параметра, а именно: функция переноса переносит возвращаемое значение последней итерации. Если итерация происходит впервые, то это значение является начальным. Другой параметр — item, который представляет собой каждое значение в массиве.
Разве ты не понимаешь? Это нормально, я тоже не могу этого понять, не паникуйте, просто посмотрите на примеры.
function sum($carry, $item)
{
$carry += $item;
return $carry;
}
function product($carry, $item)
{
$carry *= $item;
return $carry;
}
$a = array(1, 2, 3, 4, 5);
$x = array();
var_dump(array_reduce($a, "sum")); // int(15)
var_dump(array_reduce($a, "product", 10)); // int(1200), because: 10*1*2*3*4*5
var_dump(array_reduce($x, "sum", "No data to reduce")); // string(17) "No data to reduce"
Этот код является примером с официального сайта. Мы определяем метод sum() для накопления и метод product() для факториала. По результатам первых двух тестов видно, что передавая первый массив и затем вызывая метод sum(), мы завершаем функцию накопления и выводим уникальное значение результата. Во втором абзаце добавляется третий параметр, дающий значение по умолчанию 10. Результатом является дополнительный совокупный результат умножения 10. Последний абзац представляет собой пустой массив, возвращающий результат, заданный параметром Initial.
Разобравшись с array_reduce(), давайте вернемся и посмотрим на параметры, указанные в исходном коде фреймворка. Первый параметр — это содержимое каналов, возвращаемых после использования array_reverse(). Эти каналы передаются через методthrough(). Возвращаясь к ядру, мы обнаружим, что параметр, передаваемый этим методом, является переменной-членом $middleware промежуточного программного обеспечения, загруженного в нашу платформу.
Во время предыдущего процесса bootstrap() мы зарегистрировали все привязки промежуточного программного обеспечения, зарегистрированные в app/Http/Kernel.php, в контейнере службы. Таким образом, этот массив каналов содержит всю информацию о промежуточном программном обеспечении.
Следующий второй параметр — это вызываемая функция переноса(), которая представляет функцию обратного вызова в методе array_reduce().
protected function carry()
{
return function ($stack, $pipe) {
return function ($passable) use ($stack, $pipe) {
try {
if (is_callable($pipe)) {
return $pipe($passable, $stack);
} elseif (! is_object($pipe)) {
[$name, $parameters] = $this->parsePipeString($pipe);
$pipe = $this->getContainer()->make($name);
$parameters = array_merge([$passable, $stack], $parameters);
} else {
$parameters = [$passable, $stack];
}
$carry = method_exists($pipe, $this->method)
? $pipe->{$this->method}(...$parameters)
: $pipe(...$parameters);
return $this->handleCarry($carry);
} catch (Throwable $e) {
return $this->handleException($passable, $e);
}
};
};
}
Этот метод гораздо сложнее. Давайте посмотрим на это шаг за шагом.
Излишне говорить о параметрах: стек — это последнее возвращаемое значение, а канал — текущее значение, которое мы хотим обработать, которое является текущим объектом промежуточного программного обеспечения. В этой функции обратного вызова вызывается другой уровень функции обратного вызова, и эти два значения передаются посредством использования. Во внутренней функции обратного вызова наш параметр — это проходная переменная. Откуда это сносно? Не волнуйтесь, давайте сначала посмотрим на внутреннюю реализацию этой функции и, наконец, поговорим о приемлемости.
Вход в функцию try В сегменте кода первое решение таково: если pipe — это функция обратного вызова, вызовите ее напрямую и верните второе решение, если; pipe не предмет, а string слова, деконструкция pipe Информационный, сервисный контейнер make это, и параметры готовы; else То есть pipe является объектом, то passable и stack как его параметры. Наконец, если есть все объекты, метод объекта будет вызываться единообразно. handle Метод, название этого метода То есть this->method Имя метода, определенное атрибутом. внизу
В конечном итоге возвращается переменная $carry. Что это такое? Функция return next() в промежуточном программном обеспечении — это следующая функция обратного вызова в конвейере.
В приведенном выше коде у нас есть два слоя вложенных функций обратного вызова. Благодаря обучению мы знаем, что функция обратного вызова имеет характеристику отложенной загрузки. Другими словами, этот кусок кода — это когда мы наконец вызовем функцию обратного вызова. срабатывает, так когда же он вызывается?
public function then(Closure $destination)
{
$pipeline = array_reduce(
array_reverse($this->pipes()), $this->carry(), $this->prepareDestination($destination)
);
return $pipeline($this->passable);
}
Правильно, тогда() Последний метод return Вот, теперь знай passable Где это было передано? Обратите внимание, это passable èПоследний является значением по умолчанию initial Параметры — это все наш текущий запрос. Request Объект и маршрут Route объект. То есть сказано, что повсюду Laravel В рамках этого, то, что течет в наших трубах, является нашим Request Object, и то, что в конечном итоге возвращается, происходит после завершения обработки каждого промежуточного программного обеспечения и контроллера. Response объект. Промежуточное программное обеспечение, контроллеры и даже маршрутизация на самом деле являются фильтрами в нашем конвейере. В соответствии с нашими условиями и условиями бизнеса мы можем прервать или обработать запрос в любой момент. Теперь мы понимаем, что мы можем сделать в середине. Вы также можете напрямую вернуть результаты страницы. в маршруте.
Что ж, чтобы изучить конвейер, мы фактически снова разобрались со всем процессом ответа на запрос. Будьте плодотворны!
Непосредственная отладка конвейера может быть сложной, поскольку инфраструктура Laravel загружает много контента, но мы можем сами написать приложение конвейера для тестирования и установить точки останова для облегчения отладки.
первый,Нам нужно определить несколько фильтров,То есть наше промежуточное программное обеспечение, но нам не нужно его внедрять Laravel Стандартно, нужно только иметь handle() Метод хороший.
class AddDollar
{
public function handle($text, $next){
return $next("$".$text."$");
}
}
class AddTime
{
public function handle($text, $next){
$t = $next($text);
return $t . time();
}
}
class EmailChange
{
public function handle($text, $next){
return $next(str_replace("@", "#", $text));
}
}
Специальной функции нет, отфильтровываем Email в @ Символ становится # Нет. Многие веб-сайты имеют такую функцию, позволяющую избежать сканирования. Email адрес. Два других предназначены для добавления символов и временных меток. существовать AddTime При обработке мы используем задний Функции промежуточного программного обеспечения, То есть Добавить контент после того, как промежуточное программное обеспечение завершит обработку. Мы уже говорили об этом на курсах, связанных с промежуточным программным обеспечением.
Далее используйте конвейеры для обработки.
Route::get('pipeline/test1', function(){
$pipes = [
\App\PipelineTest\EmailChange::class,
\App\PipelineTest\AddTime::class,
new \App\PipelineTest\AddDollar(),
function($text, $next){
return $next("【".$text."】");
},
];
return app(\Illuminate\Pipeline\Pipeline::class)
->send("Проверьте контент и посмотрите, заменен ли он.Email:zyblog@zyblog.ddd")
->through($pipes)
->then(function ($text) {
return $text . "end";
});
// $[Проверьте содержимое и посмотрите, замените ли вы Email:zyblog#zyblog.ddd]$end1630978948
});
В этом тестовом коде мы используем строковые объекты экземпляров и функции обратного вызова для массива каналов для реализации фильтра промежуточного программного обеспечения. Вы можете видеть, что конечный результат вывода — именно то, что мы хотим.
Здесь вы можете установить точки остановки и ввести проверку конвейера, как эти промежуточные программы. array_reverse() меняет порядок промежуточного программного обеспечения, поэтому заднее промежуточное программное обеспечение добавляет содержимое данных в конце. Я оставлю эту часть отладки вам!
Можно сказать, что сервисные контейнеры и конвейеры (промежуточное программное обеспечение) Laravel в Самое основное содержание рамке, можно также сказать, что весь фреймворк построен на этих двух режимах. Понимание сервисного контейнера предназначено для решения проблемы зависимостей классов, а понимание конвейера — для решения проблемы потока данных запроса и ответа. Мы делаем это сами Web На самом деле разработка просто выполняет различные операции над двумя потоками данных: запроса и ответа.
После понимания двух основных частей в следующей статье мы рассмотрим, как реализовать очень часто используемую фасадную функцию в Laravel.