Документация Laravel 10.x
Здесь ты найдешь сниппеты по Laravel и полезные советы по веб-разработке.
Laravel предоставляет выразительный, минимальный API вокруг компонента Symfony Process, позволяя вам удобно вызывать внешние процессы из вашего Laravel-приложения. Возможности обработки Laravel сосредоточены на наиболее распространенных случаях использования и прекрасном опыте разработчика.
Для вызова процесса вы можете использовать методы run
и start
, предлагаемые фасадом Process
. Метод run
вызовет процесс и будет ждать завершения выполнения процесса, в то время как метод start
используется для асинхронного выполнения процесса. Мы рассмотрим оба подхода в этой документации. Сначала давайте рассмотрим, как вызвать основной синхронный процесс и проверить его результат:
use Illuminate\Support\Facades\Process; $result = Process::run('ls -la'); return $result->output();
Конечно, экземпляр Illuminate\Contracts\Process\ProcessResult
, возвращаемый методом run
, предлагает различные полезные методы, которые могут быть использованы для проверки результата процесса:
$result = Process::run('ls -la'); $result->successful();$result->failed();$result->exitCode();$result->output();$result->errorOutput();
Если у вас есть результат процесса и вы хотите выбросить экземпляр Illuminate\Process\Exceptions\ProcessFailedException
, если код выхода больше нуля (тем самым указывая на сбой), вы можете использовать методы throw
и throwIf
. Если процесс не завершился ошибкой, экземпляр результата процесса будет возвращен:
$result = Process::run('ls -la')->throw(); $result = Process::run('ls -la')->throwIf($condition);
Конечно, вам может потребоваться настроить поведение процесса перед его вызовом. К счастью, Laravel позволяет вам настраивать различные функции процесса, такие как рабочий каталог, время ожидания и переменные среды.
Вы можете использовать метод path
для указания рабочего каталога процесса. Если этот метод не вызывается, процесс унаследует рабочий каталог текущего выполняющегося сценария PHP:
$result = Process::path(__DIR__)->run('ls -la');
Вы можете предоставлять ввод через "стандартный ввод" процесса с помощью метода input
:
$result = Process::input('Hello World')->run('cat');
По умолчанию процессы будут выбрасывать экземпляр Illuminate\Process\Exceptions\ProcessTimedOutException
после выполнения более 60 секунд. Однако вы можете настроить это поведение с помощью метода timeout
:
$result = Process::timeout(120)->run('bash import.sh');
Или, если вы хотите отключить тайм-аут процесса полностью, вы можете вызвать метод forever
:
$result = Process::forever()->run('bash import.sh');
Метод idleTimeout
может быть использован для указания максимального времени выполнения процесса без возвращения какого-либо вывода:
$result = Process::timeout(60)->idleTimeout(30)->run('bash import.sh');
Переменные окружения могут быть предоставлены процессу с помощью метода env
. Вызванный процесс также унаследует все переменные окружения, определенные в вашей системе:
$result = Process::forever() ->env(['IMPORT_PATH' => __DIR__]) ->run('bash import.sh');
Если вы хотите удалить унаследованную переменную окружения из вызванного процесса, вы можете предоставить этой переменной окружения значение false
:
$result = Process::forever() ->env(['LOAD_PATH' => false]) ->run('bash import.sh');
Метод tty
может быть использован для включения режима TTY для вашего процесса. Режим TTY соединяет ввод и вывод процесса с вводом и выводом вашей программы, что позволяет вашему процессу открывать редактор, такой как Vim или Nano, как процесс:
Process::forever()->tty()->run('vim');
Как было ранее обсуждено, вывод процесса может быть получен с помощью методов output
(stdout) и errorOutput
(stderr) на результате процесса:
use Illuminate\Support\Facades\Process; $result = Process::run('ls -la'); echo $result->output();echo $result->errorOutput();
Однако вывод также может быть собран в реальном времени, передав замыкание в качестве второго аргумента методу run
. Замыкание будет получать два аргумента: «тип» вывода (stdout
или stderr
) и сам вывод:
$result = Process::run('ls -la', function (string $type, string $output) { echo $output;});
Laravel также предлагает методы seeInOutput
и seeInErrorOutput
, которые предоставляют удобный способ определения, содержится ли заданная строка в выводе процесса:
if (Process::run('ls -la')->seeInOutput('laravel')) { // ...}
Если ваш процесс пишет большое количество вывода, которое вам не интересно, вы можете экономить память, отключив получение вывода. Для этого вызовите метод quietly
при построении процесса:
use Illuminate\Support\Facades\Process; $result = Process::quietly()->run('bash import.sh');
Иногда вам может потребоваться использовать вывод одного процесса в качестве ввода для другого процесса. Это часто называется «перенаправлением» вывода процесса в другой. Метод pipe
, предоставленный фасадом Process
, позволяет легко достичь этого. Метод pipe
будет выполняться синхронно и возвращать результат процесса для последнего процесса в конвейере:
use Illuminate\Process\Pipe;use Illuminate\Support\Facades\Process; $result = Process::pipe(function (Pipe $pipe) { $pipe->command('cat example.txt'); $pipe->command('grep -i "laravel"');}); if ($result->successful()) { // ...}
Если вам не нужно настраивать отдельные процессы, составляющие конвейер, вы можете просто передать массив строк команд методу pipe
:
$result = Process::pipe([ 'cat example.txt', 'grep -i "laravel"',]);
Вывод процесса может быть собран в реальном времени, передав замыкание в качестве второго аргумента методу pipe
. Замыкание будет получать два аргумента: «тип» вывода (stdout
или stderr
) и сам вывод:
$result = Process::pipe(function (Pipe $pipe) { $pipe->command('cat example.txt'); $pipe->command('grep -i "laravel"');}, function (string $type, string $output) { echo $output;});
Laravel также позволяет присваивать строковые ключи каждому процессу в конвейере с помощью метода as
. Этот ключ также будет передан замыканию вывода, предоставленному методу pipe
, что позволяет вам определить, к какому процессу относится вывод:
$result = Process::pipe(function (Pipe $pipe) { $pipe->as('first')->command('cat example.txt'); $pipe->as('second')->command('grep -i "laravel"');})->start(function (string $type, string $output, string $key) { // ...});
В то время как метод run
вызывает процессы синхронно, метод start
может быть использован для вызова процесса асинхронно. Это позволяет вашему приложению продолжать выполнять другие задачи, пока процесс выполняется в фоновом режиме. После вызова процесса вы можете использовать метод running
, чтобы определить, выполняется ли процесс:
$process = Process::timeout(120)->start('bash import.sh'); while ($process->running()) { // ...} $result = $process->wait();
Как вы могли заметить, вы можете вызвать метод wait
, чтобы дождаться завершения процесса и получить экземпляр результата процесса:
$process = Process::timeout(120)->start('bash import.sh'); // ... $result = $process->wait();
Метод id
может быть использован для получения операционной системой присвоенного идентификатора процесса, выполняющегося в данный момент:
$process = Process::start('bash import.sh'); return $process->id();
Метод signal
можно использовать для отправки «сигнала» выполняющемуся процессу. Список предопределенных констант сигналов можно найти в документации PHP:
$process->signal(SIGUSR2);
Во время выполнения асинхронного процесса можно получить весь текущий вывод с помощью методов output
и errorOutput
; однако вы можете использовать методы latestOutput
и latestErrorOutput
для доступа к выводу из процесса, который произошел с момента последнего получения вывода:
$process = Process::timeout(120)->start('bash import.sh'); while ($process->running()) { echo $process->latestOutput(); echo $process->latestErrorOutput(); sleep(1);}
Как и метод run
, вывод также может быть собран в реальном времени из асинхронных процессов, передав замыкание в качестве второго аргумента методу start
. Замыкание будет получать два аргумента: «тип» вывода (stdout
или stderr
) и сам вывод:
$process = Process::start('bash import.sh', function (string $type, string $output) { echo $output;}); $result = $process->wait();
Laravel также делает легким управление пулом одновременных асинхронных процессов, что позволяет вам легко выполнять множество задач одновременно. Для начала вызовите метод pool
, который принимает замыкание, получающее экземпляр Illuminate\Process\Pool
.
В этом замыкании вы можете определить процессы, принадлежащие пулу. После запуска пула процессов с помощью метода start
вы можете получить доступ к коллекции выполняемых процессов с помощью метода running
:
use Illuminate\Process\Pool;use Illuminate\Support\Facades\Process; $pool = Process::pool(function (Pool $pool) { $pool->path(__DIR__)->command('bash import-1.sh'); $pool->path(__DIR__)->command('bash import-2.sh'); $pool->path(__DIR__)->command('bash import-3.sh');})->start(function (string $type, string $output, int $key) { // ...}); while ($pool->running()->isNotEmpty()) { // ...} $results = $pool->wait();
Как видите, вы можете дождаться завершения выполнения всех процессов в пуле и получить их результаты с помощью метода wait
. Метод wait
возвращает объект с доступом к массиву, который позволяет вам получить доступ к результату процесса для каждого процесса в пуле по его ключу:
$results = $pool->wait(); echo $results[0]->output();
Или, для удобства, можно использовать метод concurrently
для запуска асинхронного пула процессов и немедленного ожидания результатов. Это может предоставить особенно выразительный синтаксис при комбинировании с возможностями деструктуризации массивов в PHP:
[$first, $second, $third] = Process::concurrently(function (Pool $pool) { $pool->path(__DIR__)->command('ls -la'); $pool->path(app_path())->command('ls -la'); $pool->path(storage_path())->command('ls -la');}); echo $first->output();
Доступ к результатам пула процессов через числовой ключ не является очень выразительным; поэтому Laravel позволяет вам присваивать строковые ключи каждому процессу в пуле с помощью метода as
. Этот ключ также будет передан замыканию, предоставленному методу start
, что позволяет вам определить, к какому процессу относится вывод:
$pool = Process::pool(function (Pool $pool) { $pool->as('first')->command('bash import-1.sh'); $pool->as('second')->command('bash import-2.sh'); $pool->as('third')->command('bash import-3.sh');})->start(function (string $type, string $output, string $key) { // ...}); $results = $pool->wait(); return $results['first']->output();
Поскольку метод running
пула процессов предоставляет коллекцию всех вызванных процессов в пуле, вы можете легко получить доступ к идентификаторам процессов пула:
$processIds = $pool->running()->each->id();
И, для удобства, вы можете вызвать метод signal
на пуле процессов для отправки сигнала каждому процессу в пуле:
$pool->signal(SIGUSR2);
Многие службы Laravel предоставляют функциональность для того, чтобы легко и выразительно писать тесты, и служба процесса Laravel не является исключением. Метод fake
фасада Process
позволяет указать Laravel вернуть замененные / фиктивные результаты, когда процессы вызываются.
Чтобы исследовать возможность Laravel фиктивных процессов, представим маршрут, который вызывает процесс:
use Illuminate\Support\Facades\Process;use Illuminate\Support\Facades\Route; Route::get('/import', function () { Process::run('bash import.sh'); return 'Import complete!';});
При тестировании этого маршрута мы можем указать Laravel вернуть фиктивный успешный результат процесса для каждого вызванного процесса, вызвав метод fake
фасада Process
без аргументов. Кроме того, мы даже можем утверждать, что данный процесс был "запущен":
<?php namespace Tests\Feature; use Illuminate\Process\PendingProcess;use Illuminate\Contracts\Process\ProcessResult;use Illuminate\Support\Facades\Process;use Tests\TestCase; class ExampleTest extends TestCase{ public function test_process_is_invoked(): void { Process::fake(); $response = $this->get('/'); // Простая проверка процесса... Process::assertRan('bash import.sh'); // Или проверка конфигурации процесса... Process::assertRan(function (PendingProcess $process, ProcessResult $result) { return $process->command === 'bash import.sh' && $process->timeout === 60; }); }}
Как обсуждалось ранее, вызов метода fake
фасада Process
указывает Laravel всегда возвращать успешный результат процесса без вывода. Однако вы можете легко указать вывод и код выхода для фиктивных процессов с использованием метода result
фасада Process
:
Process::fake([ '*' => Process::result( output: 'Test output', errorOutput: 'Test error output', exitCode: 1, ),]);
Как вы могли заметить в предыдущем примере, фасад Process
позволяет указать разные фиктивные результаты для каждого процесса, передав массив методу fake
.
Ключи массива должны представлять шаблоны команд, которые вы хотите подделать, и связанные с ними результаты. Знак *
можно использовать в качестве универсального символа. Все команды процессов, которые не были подделаны, будут фактически вызваны. Вы можете использовать метод result
фасада Process
для создания заглушек / фиктивных результатов для этих команд:
Process::fake([ 'cat *' => Process::result( output: 'Test "cat" output', ), 'ls *' => Process::result( output: 'Test "ls" output', ),]);
Если вам не нужно настраивать код выхода или вывод ошибок подделанного процесса, вам может быть удобнее указать фиктивные результаты процесса в виде обычных строк:
Process::fake([ 'cat *' => 'Test "cat" output', 'ls *' => 'Test "ls" output',]);
Если код, который вы тестируете, вызывает несколько процессов с одной и той же командой, вы можете захотеть присвоить каждому вызову процесса другой фиктивный результат. Это можно сделать с помощью метода sequence
фасада Process
:
Process::fake([ 'ls *' => Process::sequence() ->push(Process::result('First invocation')) ->push(Process::result('Second invocation')),]);
До сих пор мы в основном обсуждали подделку процессов, вызываемых синхронно с использованием метода run
. Однако, если вы пытаетесь протестировать код, который взаимодействует с асинхронными процессами, вызываемыми через start
, вам может понадобиться более сложный подход к описанию ваших фиктивных процессов.
Давайте, например, представим следующий маршрут, который взаимодействует с асинхронным процессом:
use Illuminate\Support\Facades\Log;use Illuminate\Support\Facades\Route; Route::get('/import', function () { $process = Process::start('bash import.sh'); while ($process->running()) { Log::info($process->latestOutput()); Log::info($process->latestErrorOutput()); } return 'Done';});
Чтобы правильно подделать этот процесс, нам нужно иметь возможность описать, сколько раз метод running
должен возвращать true
. Кроме того, мы можем захотеть указать несколько строк вывода, которые должны возвращаться последовательно. Для этого мы можем использовать метод describe
фасада Process
:
Process::fake([ 'bash import.sh' => Process::describe() ->output('First line of standard output') ->errorOutput('First line of error output') ->output('Second line of standard output') ->exitCode(0) ->iterations(3),]);
Давайте рассмотрим приведенный выше пример. С помощью методов output
и errorOutput
мы можем указать несколько строк вывода, которые будут возвращаться последовательно. Метод exitCode
можно использовать для указания окончательного кода выхода поддельного процесса. Наконец, метод iterations
можно использовать для указания, сколько раз метод running
должен возвращать true
.
Как уже обсуждалось, Laravel предоставляет несколько утверждений для процессов ваших функциональных тестов. Мы обсудим каждое из этих утверждений ниже:
Утверждение, что данный процесс был вызван:
use Illuminate\Support\Facades\Process; Process::assertRan('ls -la');
Метод assertRan
также принимает замыкание, которое получит экземпляр процесса и результат процесса, позволяя вам проверить настроенные параметры процесса. Если это замыкание возвращает true
, утверждение будет "пройдено":
Process::assertRan(fn ($process, $result) => $process->command === 'ls -la' && $process->path === __DIR__ && $process->timeout === 60);
Переменная $process
, переданная замыканию assertRan
, является экземпляром Illuminate\Process\PendingProcess
, в то время как $result
- это экземпляр Illuminate\Contracts\Process\ProcessResult
.
Утверждение, что данный процесс не был вызван:
use Illuminate\Support\Facades\Process; Process::assertDidntRun('ls -la');
Как и метод assertRan
, метод assertDidntRun
также принимает замыкание, которое получит экземпляр процесса и результат процесса, позволяя вам проверить настроенные параметры процесса. Если это замыкание возвращает true
, утверждение будет "не выполнено":
Process::assertDidntRun(fn (PendingProcess $process, ProcessResult $result) => $process->command === 'ls -la');
Утверждение, что данный процесс был вызван определенное количество раз:
use Illuminate\Support\Facades\Process; Process::assertRanTimes('ls -la', times: 3);
Метод assertRanTimes
также принимает замыкание, которое получит экземпляр процесса и результат процесса, позволяя вам проверить настроенные параметры процесса. Если это замыкание возвращает true
и процесс был вызван указанное количество раз, утверждение будет "пройдено":
Process::assertRanTimes(function (PendingProcess $process, ProcessResult $result) { return $process->command === 'ls -la';}, times: 3);
Если вы хотите убедиться, что все вызванные процессы были поддельными в пределах вашего индивидуального теста или полного тестового комплекта, вы можете вызвать метод preventStrayProcesses
. После вызова этого метода любые процессы, которые не имеют соответствующего фиктивного результата, выбросят исключение вместо того, чтобы запускать фактический процесс:
use Illuminate\Support\Facades\Process; Process::preventStrayProcesses(); Process::fake([ 'ls *' => 'Test output...',]);
// Возвращается фиктивный ответ... Process::run('ls -la');
// Вызывается исключение... Process::run('bash import.sh');