PHP proc_open không đồng bộ

Nếu bạn cài đặt thành phần này bên ngoài ứng dụng Symfony, bạn phải yêu cầu tệp

$ composer require symfony/process
28 trong mã của mình để kích hoạt cơ chế tự động tải lớp do Composer cung cấp. Đọc bài viết này để biết thêm chi tiết

Lớp Quy trình thực thi một lệnh trong một quy trình con, quan tâm đến sự khác biệt giữa hệ điều hành và các đối số thoát để ngăn chặn các vấn đề bảo mật. Nó thay thế các hàm PHP như exec, passthru, shell_exec và system

1
2
3
4
5
6
7
8
9
10
11
12
use Symfony\Component\Process\Exception\ProcessFailedException;
use Symfony\Component\Process\Process;

$process = new Process[['ls', '-lsa']];
$process->run[];

// executes after the command finishes
if [!$process->isSuccessful[]] {
    throw new ProcessFailedException[$process];
}

echo $process->getOutput[];

Phương thức

$ composer require symfony/process
29 luôn trả về toàn bộ nội dung của đầu ra tiêu chuẩn của lệnh và
$ composer require symfony/process
30 nội dung của đầu ra lỗi. Ngoài ra, các phương thức và trả về đầu ra mới kể từ lần gọi cuối cùng

Phương thức xóa nội dung của đầu ra và xóa nội dung của đầu ra lỗi

Bạn cũng có thể sử dụng lớp Process với cấu trúc for each để lấy đầu ra khi nó được tạo. Theo mặc định, vòng lặp đợi đầu ra mới trước khi chuyển sang lần lặp tiếp theo

1
2
3
4
5
6
7
8
9
10
$process = new Process[['ls', '-lsa']];
$process->start[];

foreach [$process as $type => $data] {
    if [$process::OUT === $type] {
        echo "\nRead from stdout: ".$data;
    } else { // $process::ERR === $type
        echo "\nRead from stderr: ".$data;
    }
}

Mẹo

Thành phần Process bên trong sử dụng trình lặp PHP để lấy đầu ra trong khi nó được tạo. Trình vòng lặp đó được hiển thị thông qua phương thức

$ composer require symfony/process
31 để cho phép tùy chỉnh hành vi của nó

1
2
3
4
5
6
$process = new Process[['ls', '-lsa']];
$process->start[];
$iterator = $process->getIterator[$process::ITER_SKIP_ERR | $process::ITER_KEEP_OUTPUT];
foreach [$iterator as $data] {
    echo $data."\n";
}

Phương thức

$ composer require symfony/process
32 giống hệt với
$ composer require symfony/process
33, ngoại trừ việc nó sẽ đưa ra một ProcessFailedException nếu quy trình không thể thực hiện thành công [i. e. quá trình đã thoát với mã khác không]

1
2
3
4
5
6
7
8
9
10
11
12
1
2
3
4
5
6
7
8
9
10
11
12
3

Mẹo

Bạn có thể nhận được thời gian đầu ra cuối cùng tính bằng giây bằng cách sử dụng phương thức. Phương thức này trả về

$ composer require symfony/process
34 nếu quá trình chưa bắt đầu

Symfony sử dụng hàm proc_open của PHP để chạy các quy trình. Bạn có thể định cấu hình các tùy chọn được truyền cho đối số

$ composer require symfony/process
35 của
$ composer require symfony/process
36 bằng cách sử dụng phương thức
$ composer require symfony/process
37

$ composer require symfony/process
0
$ composer require symfony/process
1

Sử dụng một mảng các đối số là cách được khuyến nghị để xác định các lệnh. Điều này giúp bạn không bị thoát và cho phép gửi tín hiệu liền mạch [e. g. để dừng các tiến trình trong khi chúng chạy]

$ composer require symfony/process
2____03

Nếu bạn cần sử dụng chuyển hướng luồng, thực thi có điều kiện hoặc bất kỳ tính năng nào khác do trình bao của hệ điều hành cung cấp, bạn cũng có thể xác định các lệnh dưới dạng chuỗi bằng cách sử dụng nhà máy tĩnh

Mỗi hệ điều hành cung cấp một cú pháp khác nhau cho các dòng lệnh của chúng, do đó, bạn có trách nhiệm xử lý việc thoát và tính di động

Khi sử dụng chuỗi để xác định lệnh, đối số biến được truyền dưới dạng biến môi trường bằng cách sử dụng đối số thứ hai của phương thức

$ composer require symfony/process
33,
$ composer require symfony/process
32 hoặc
$ composer require symfony/process
40. Tham chiếu chúng cũng phụ thuộc vào hệ điều hành

$ composer require symfony/process
4
$ composer require symfony/process
5

Nếu bạn muốn tạo các lệnh di động độc lập với hệ điều hành, bạn có thể viết lệnh trên như sau

$ composer require symfony/process
2____07

Các lệnh di động yêu cầu sử dụng cú pháp dành riêng cho thành phần. khi đặt chính xác một tên biến thành

$ composer require symfony/process
41 và
$ composer require symfony/process
42, đối tượng quy trình sẽ thay thế nó bằng giá trị thoát của nó hoặc sẽ thất bại nếu không tìm thấy biến trong danh sách các biến môi trường được đính kèm với lệnh

Hàm tạo của lớp Process và tất cả các phương thức của nó liên quan đến việc thực thi các quy trình [

$ composer require symfony/process
33,
$ composer require symfony/process
32,
$ composer require symfony/process
40, v.v. ] cho phép chuyển một mảng các biến môi trường để đặt trong khi chạy quy trình

$ composer require symfony/process
0
$ composer require symfony/process
9

Ngoài các lọ env được truyền rõ ràng, các quy trình kế thừa tất cả các lọ env được xác định trong hệ thống của bạn. Bạn có thể ngăn chặn điều này bằng cách đặt thành

$ composer require symfony/process
46 các lọ env mà bạn muốn xóa

1
2
3
4
5
6
7
8
9
10
11
12
0
1
2
3
4
5
6
7
8
9
10
11
12
1

Khi thực thi một lệnh chạy dài [như

$ composer require symfony/process
47 đến máy chủ từ xa], bạn có thể đưa ra phản hồi cho người dùng cuối trong thời gian thực bằng cách chuyển một hàm ẩn danh cho phương thức

1
2
3
4
5
6
7
8
9
10
1
2
3
4
5
6
7
8
9
10
11
12
3

Ghi chú

Tính năng này sẽ không hoạt động như mong đợi trong các máy chủ sử dụng bộ đệm đầu ra PHP. Trong những trường hợp đó, hãy tắt tùy chọn output_buffering PHP hoặc sử dụng hàm ob_flush PHP để buộc gửi bộ đệm đầu ra

Bạn cũng có thể bắt đầu quy trình con và sau đó để nó chạy không đồng bộ, truy xuất đầu ra và trạng thái trong quy trình chính của bạn bất cứ khi nào bạn cần. Sử dụng phương thức để bắt đầu một quy trình không đồng bộ, phương thức để kiểm tra xem quy trình đã hoàn tất chưa và phương thức để lấy đầu ra

$ composer require symfony/process
4
1
2
3
4
5
6
7
8
9
10
11
12
5

Bạn cũng có thể đợi một quá trình kết thúc nếu bạn bắt đầu nó một cách không đồng bộ và đã hoàn thành các công việc khác

$ composer require symfony/process
4
1
2
3
4
5
6
7
8
9
10
11
12
7

Ghi chú

Phương thức đang chặn, nghĩa là mã của bạn sẽ dừng ở dòng này cho đến khi quá trình bên ngoài hoàn tất

Ghi chú

Nếu một

$ composer require symfony/process
48 được gửi trước khi tiến trình con có cơ hội hoàn thành, tiến trình máy chủ sẽ bị hủy [tùy thuộc vào hệ điều hành của bạn]. Nó có nghĩa là nhiệm vụ của bạn sẽ bị dừng lại ngay lập tức. Chạy một quy trình không đồng bộ không giống như chạy một quy trình tồn tại trong quy trình mẹ của nó

Nếu bạn muốn quy trình của mình tồn tại trong chu kỳ yêu cầu/phản hồi, bạn có thể tận dụng sự kiện

$ composer require symfony/process
49 và chạy lệnh của mình một cách đồng bộ bên trong sự kiện này. Xin lưu ý rằng
$ composer require symfony/process
49 chỉ được gọi nếu bạn sử dụng PHP-FPM

thận trọng

Xin lưu ý rằng nếu bạn làm điều đó, quy trình PHP-FPM đã nói sẽ không khả dụng để phục vụ bất kỳ yêu cầu mới nào cho đến khi quy trình con kết thúc. Điều này có nghĩa là bạn có thể nhanh chóng chặn nhóm FPM của mình nếu bạn không đủ cẩn thận. Đó là lý do tại sao tốt hơn hết là không nên làm bất kỳ điều gì thú vị ngay cả sau khi yêu cầu được gửi đi, mà thay vào đó hãy sử dụng hàng đợi công việc

lấy một đối số tùy chọn. một cuộc gọi lại được gọi nhiều lần trong khi quá trình vẫn đang chạy, chuyển vào đầu ra và loại của nó

1
2
3
4
5
6
7
8
9
10
1
2
3
4
5
6
7
8
9
10
11
12
9

Thay vì đợi cho đến khi quá trình kết thúc, bạn có thể sử dụng phương thức giữ hoặc dừng chờ dựa trên một số logic PHP. Ví dụ sau bắt đầu một tiến trình đang chạy dài và kiểm tra đầu ra của nó để đợi cho đến khi nó được khởi tạo hoàn toàn

use Symfony\Component\Process\Exception\ProcessFailedException;
use Symfony\Component\Process\Process;

$process = new Process[['ls', '-lsa']];
$process->run[];

// executes after the command finishes
if [!$process->isSuccessful[]] {
    throw new ProcessFailedException[$process];
}

echo $process->getOutput[];
0
use Symfony\Component\Process\Exception\ProcessFailedException;
use Symfony\Component\Process\Process;

$process = new Process[['ls', '-lsa']];
$process->run[];

// executes after the command finishes
if [!$process->isSuccessful[]] {
    throw new ProcessFailedException[$process];
}

echo $process->getOutput[];
1

Trước khi một quá trình được bắt đầu, bạn có thể chỉ định đầu vào tiêu chuẩn của nó bằng cách sử dụng phương thức hoặc đối số thứ 4 của hàm tạo. Đầu vào được cung cấp có thể là một chuỗi, tài nguyên luồng hoặc đối tượng

$ composer require symfony/process
51

$ composer require symfony/process
0
use Symfony\Component\Process\Exception\ProcessFailedException;
use Symfony\Component\Process\Process;

$process = new Process[['ls', '-lsa']];
$process->run[];

// executes after the command finishes
if [!$process->isSuccessful[]] {
    throw new ProcessFailedException[$process];
}

echo $process->getOutput[];
3

Khi đầu vào này được ghi đầy đủ vào đầu vào tiêu chuẩn của quy trình con, đường ống tương ứng sẽ được đóng lại

Để ghi vào đầu vào tiêu chuẩn của quy trình con trong khi nó đang chạy, thành phần này cung cấp lớp InputStream

use Symfony\Component\Process\Exception\ProcessFailedException;
use Symfony\Component\Process\Process;

$process = new Process[['ls', '-lsa']];
$process->run[];

// executes after the command finishes
if [!$process->isSuccessful[]] {
    throw new ProcessFailedException[$process];
}

echo $process->getOutput[];
4
use Symfony\Component\Process\Exception\ProcessFailedException;
use Symfony\Component\Process\Process;

$process = new Process[['ls', '-lsa']];
$process->run[];

// executes after the command finishes
if [!$process->isSuccessful[]] {
    throw new ProcessFailedException[$process];
}

echo $process->getOutput[];
5

Phương thức chấp nhận vô hướng, tài nguyên luồng hoặc đối tượng

$ composer require symfony/process
51 làm đối số. Như thể hiện trong ví dụ trên, bạn cần gọi phương thức một cách rõ ràng khi bạn viết xong vào đầu vào tiêu chuẩn của quy trình con

Đầu vào của một quy trình cũng có thể được xác định bằng các luồng PHP

use Symfony\Component\Process\Exception\ProcessFailedException;
use Symfony\Component\Process\Process;

$process = new Process[['ls', '-lsa']];
$process->run[];

// executes after the command finishes
if [!$process->isSuccessful[]] {
    throw new ProcessFailedException[$process];
}

echo $process->getOutput[];
6
use Symfony\Component\Process\Exception\ProcessFailedException;
use Symfony\Component\Process\Process;

$process = new Process[['ls', '-lsa']];
$process->run[];

// executes after the command finishes
if [!$process->isSuccessful[]] {
    throw new ProcessFailedException[$process];
}

echo $process->getOutput[];
7

Tất cả các ví dụ trên cho thấy rằng chương trình của bạn có quyền kiểm soát đầu vào của một quy trình [sử dụng

$ composer require symfony/process
53] và đầu ra từ quy trình đó [sử dụng
$ composer require symfony/process
29]. Thành phần Quy trình có hai chế độ đặc biệt điều chỉnh mối quan hệ giữa chương trình của bạn và quy trình. teletype [tty] và pseudo-teletype [pty]

Ở chế độ TTY, bạn kết nối đầu vào và đầu ra của quy trình với đầu vào và đầu ra của chương trình của bạn. Ví dụ, điều này cho phép mở một trình chỉnh sửa như Vim hoặc Nano dưới dạng một quy trình. Bạn bật chế độ TTY bằng cách gọi

use Symfony\Component\Process\Exception\ProcessFailedException;
use Symfony\Component\Process\Process;

$process = new Process[['ls', '-lsa']];
$process->run[];

// executes after the command finishes
if [!$process->isSuccessful[]] {
    throw new ProcessFailedException[$process];
}

echo $process->getOutput[];
8
use Symfony\Component\Process\Exception\ProcessFailedException;
use Symfony\Component\Process\Process;

$process = new Process[['ls', '-lsa']];
$process->run[];

// executes after the command finishes
if [!$process->isSuccessful[]] {
    throw new ProcessFailedException[$process];
}

echo $process->getOutput[];
9

Ở chế độ PTY, chương trình của bạn hoạt động như một thiết bị đầu cuối cho quy trình thay vì đầu vào và đầu ra đơn giản. Một số chương trình hoạt động khác khi tương tác với một thiết bị đầu cuối thực thay vì một chương trình khác. Chẳng hạn, một số chương trình nhắc nhập mật khẩu khi nói chuyện với thiết bị đầu cuối. Sử dụng để kích hoạt chế độ này

Bất kỳ quá trình không đồng bộ nào cũng có thể bị dừng bất cứ lúc nào với phương thức. Phương thức này có hai đối số. thời gian chờ và tín hiệu. Khi hết thời gian chờ, tín hiệu được gửi đến quy trình đang chạy. Tín hiệu mặc định được gửi đến một quy trình là

$ composer require symfony/process
55. Vui lòng đọc phần này để tìm hiểu thêm về xử lý tín hiệu trong thành phần Quy trình

1
2
3
4
5
6
1
2
3
4
5
6
7
8
9
10
1

Nếu bạn muốn thực thi một số mã PHP trong sự cô lập, hãy sử dụng

$ composer require symfony/process
56 để thay thế

use Symfony\Component\Process\Exception\ProcessFailedException;
use Symfony\Component\Process\Process;

$process = new Process[['ls', '-lsa']];
$process->run[];

// executes after the command finishes
if [!$process->isSuccessful[]] {
    throw new ProcessFailedException[$process];
}

echo $process->getOutput[];
8
1
2
3
4
5
6
7
8
9
10
3

Theo mặc định, các quy trình có thời gian chờ là 60 giây, nhưng bạn có thể thay đổi thời gian chờ này bằng cách chuyển một thời gian chờ khác [tính bằng giây] sang phương thức

$ composer require symfony/process
57

1
2
3
4
5
6
7
8
9
10
4
1
2
3
4
5
6
7
8
9
10
5

Nếu hết thời gian chờ, một ProcessTimedOutException sẽ bị ném

Đối với các lệnh chạy dài, bạn có trách nhiệm thực hiện kiểm tra thời gian chờ thường xuyên

use Symfony\Component\Process\Exception\ProcessFailedException;
use Symfony\Component\Process\Process;

$process = new Process[['ls', '-lsa']];
$process->run[];

// executes after the command finishes
if [!$process->isSuccessful[]] {
    throw new ProcessFailedException[$process];
}

echo $process->getOutput[];
0
1
2
3
4
5
6
7
8
9
10
7

Mẹo

Bạn có thể lấy thời gian bắt đầu quy trình bằng phương pháp

$ composer require symfony/process
58

Ngược lại với thời gian chờ của đoạn trước, thời gian chờ không hoạt động chỉ xem xét thời gian kể từ khi quá trình tạo ra đầu ra cuối cùng

1
2
3
4
5
6
1
2
3
4
5
6
7
8
9
10
9

Trong trường hợp trên, một quy trình được coi là hết thời gian, khi tổng thời gian chạy vượt quá 3600 giây hoặc quy trình không tạo ra bất kỳ đầu ra nào trong 60 giây

Khi chạy chương trình không đồng bộ, bạn có thể gửi tín hiệu POSIX bằng phương thức

use Symfony\Component\Process\Exception\ProcessFailedException;
use Symfony\Component\Process\Process;

$process = new Process[['ls', '-lsa']];
$process->run[];

// executes after the command finishes
if [!$process->isSuccessful[]] {
    throw new ProcessFailedException[$process];
}

echo $process->getOutput[];
8
$process = new Process[['ls', '-lsa']];
$process->start[];

foreach [$process as $type => $data] {
    if [$process::OUT === $type] {
        echo "\nRead from stdout: ".$data;
    } else { // $process::ERR === $type
        echo "\nRead from stderr: ".$data;
    }
}
1

Bạn có thể truy cập pid của một tiến trình đang chạy bằng phương thức

1
2
3
4
5
6
$process = new Process[['ls', '-lsa']];
$process->start[];

foreach [$process as $type => $data] {
    if [$process::OUT === $type] {
        echo "\nRead from stdout: ".$data;
    } else { // $process::ERR === $type
        echo "\nRead from stderr: ".$data;
    }
}
3

Vì đầu ra tiêu chuẩn và đầu ra lỗi luôn được tìm nạp từ quy trình cơ bản, nên có thể tắt đầu ra trong một số trường hợp để tiết kiệm bộ nhớ. Sử dụng và để chuyển đổi tính năng này

1
2
3
4
5
6
7
8
9
10
4
$process = new Process[['ls', '-lsa']];
$process->start[];

foreach [$process as $type => $data] {
    if [$process::OUT === $type] {
        echo "\nRead from stdout: ".$data;
    } else { // $process::ERR === $type
        echo "\nRead from stderr: ".$data;
    }
}
5

thận trọng

Bạn không thể bật hoặc tắt đầu ra trong khi quá trình đang chạy

Nếu bạn tắt đầu ra, bạn không thể truy cập

$ composer require symfony/process
29,
$ composer require symfony/process
60,
$ composer require symfony/process
30,
$ composer require symfony/process
62 hoặc
$ composer require symfony/process
63

Tuy nhiên, có thể chuyển một cuộc gọi lại tới các phương thức

$ composer require symfony/process
64,
$ composer require symfony/process
65 hoặc
$ composer require symfony/process
66 để xử lý đầu ra của quy trình theo kiểu truyền phát

Thành phần Process cung cấp một lớp tiện ích được gọi là ExecutableFinder để tìm và trả về đường dẫn tuyệt đối của tệp thực thi

1
2
3
4
5
6
7
8
9
10
4
$process = new Process[['ls', '-lsa']];
$process->start[];

foreach [$process as $type => $data] {
    if [$process::OUT === $type] {
        echo "\nRead from stdout: ".$data;
    } else { // $process::ERR === $type
        echo "\nRead from stderr: ".$data;
    }
}
7

Phương thức này cũng nhận các tham số bổ sung để chỉ định giá trị mặc định để trả về và các thư mục bổ sung để tìm tệp thực thi

1
2
3
4
5
6
7
8
9
10
11
12
0
$process = new Process[['ls', '-lsa']];
$process->start[];

foreach [$process as $type => $data] {
    if [$process::OUT === $type] {
        echo "\nRead from stdout: ".$data;
    } else { // $process::ERR === $type
        echo "\nRead from stderr: ".$data;
    }
}
9

Thành phần này cũng cung cấp một lớp tiện ích đặc biệt gọi là PhpExecutableFinder trả về đường dẫn tuyệt đối của tệp nhị phân PHP thực thi có sẵn trên máy chủ của bạn

1
2
3
4
5
6
7
8
9
10
4
1
2
3
4
5
6
1

Một tiện ích khác được cung cấp bởi thành phần này là một phương thức có tên trả về liệu TTY có được hỗ trợ trên hệ điều hành hiện tại hay không

Chủ Đề