Làm việc với quy trình hệ điều hành trong PHP

Hãy xem cách chúng tôi có thể thực hiện việc này và xem liệu chúng tôi có thể cải thiện Trải nghiệm dành cho nhà phát triển khi bạn cần làm việc với các lệnh cấp hệ điều hành từ ứng dụng PHP của mình không

Tôi đã tập trung vào các khía cạnh khác nhau trong phong cách lập trình của mình và cách tôi có thể làm cho nó tốt hơn trong vài năm qua, bắt đầu bằng cách tôi có thể tích hợp với HTTP tốt hơn và hướng đối tượng hơn. Tôi nghĩ rằng tôi đã tìm ra cách để làm điều này, vì vậy tôi đang chuyển sự chú ý của mình sang thứ khác

Có một số trường hợp bạn muốn làm việc với OS CLI trong các ứng dụng của mình. Trong ứng dụng web hoặc ứng dụng CLI khác, trước đây chúng tôi đã sử dụng các kỹ thuật như

1declare(strict_types=1);
2 
3namespace JustSteveKing\OS\Contracts;
4 
5use Symfony\Component\Process\Process;
6 
7interface ProcessContract
8{
9 public function build(): Process;
10}
6,
1declare(strict_types=1);
2 
3namespace JustSteveKing\OS\Contracts;
4 
5use Symfony\Component\Process\Process;
6 
7interface ProcessContract
8{
9 public function build(): Process;
10}
0,
1declare(strict_types=1);
2 
3namespace JustSteveKing\OS\Contracts;
4 
5use Symfony\Component\Process\Process;
6 
7interface ProcessContract
8{
9 public function build(): Process;
10}
1 và
1declare(strict_types=1);
2 
3namespace JustSteveKing\OS\Contracts;
4 
5use Symfony\Component\Process\Process;
6 
7interface ProcessContract
8{
9 public function build(): Process;
10}
2. Tuy nhiên, khi thành phần Quy trình Symfony xuất hiện, chúng tôi đã ngay lập tức được cứu

Thành phần quy trình Symfony làm cho việc tích hợp với các quy trình của hệ điều hành và nhận đầu ra trở nên cực kỳ đơn giản, nhưng vẫn còn một chút khó chịu khi chúng tôi tích hợp với thư viện này. Để chạy lệnh mà chúng ta muốn, chúng ta tạo một quy trình mới và truyền cho nó một mảng đối số

1$command = new Process(
2 command: ['git', 'push', 'origin', 'main'],
3);
4 
5$command->run();

Nhưng có cách nào chúng ta có thể nâng cao trải nghiệm của nhà phát triển không?

Chúng ta có thể chia chúng thành các loại sau để hiểu rõ hơn về các thành phần tạo ra một lệnh OS một cách hợp lý

thực thi được
tranh luận

Các đối số là cách chúng ta có thể tương tác;

Chúng tôi sẽ sử dụng các giao diện/hợp đồng để xác định các thành phần của mình nhằm chỉ định cách thức hoạt động của quy trình làm việc của chúng tôi và nếu chúng tôi trừu tượng hóa một chút, chúng tôi sẽ có một

1declare(strict_types=1);
2 
3namespace JustSteveKing\OS\Contracts;
4 
5use Symfony\Component\Process\Process;
6 
7interface ProcessContract
8{
9 public function build(): Process;
10}
3 và một
1declare(strict_types=1);
2 
3namespace JustSteveKing\OS\Contracts;
4 
5use Symfony\Component\Process\Process;
6 
7interface ProcessContract
8{
9 public function build(): Process;
10}
4 nhận các đối số. Hợp đồng Quy trình sẽ được thảo luận trước

_10

Quá trình của chúng tôi sẽ xây dựng một Lệnh để chúng tôi chạy, vì vậy hãy xem Hợp đồng Lệnh của chúng tôi. Ở đây chúng tôi đang nói rằng mỗi quy trình phải có khả năng được xây dựng và đầu ra của quy trình được tạo phải là Quy trình Symfony

_17

Khả năng trả về các đối số mà chúng ta có thể sử dụng để chuyển vào Quy trình Symfony dưới dạng lệnh là điều chúng ta muốn nhất từ ​​lệnh của mình

Chúng ta sẽ sử dụng git làm ví dụ vì hầu hết chúng ta đều có thể liên hệ với các lệnh git, vì vậy hãy ngừng nói về các ý tưởng và bắt đầu xem qua một ví dụ thực tế

Hãy bắt đầu bằng cách phát triển quy trình Git áp dụng Hợp đồng quy trình mà chúng ta vừa thảo luận

1class Git implements ProcessContract
2{
3 use HandlesGitCommands;
4 
5 private CommandContract $command;
6}

Chúng tôi có một đặc điểm sẽ cho phép chúng tôi tập trung hóa cách mọi thứ được tạo và xây dựng cho quy trình Git của chúng tôi và Quy trình của chúng tôi thực hiện hợp đồng và có thuộc tính lệnh mà chúng tôi sẽ sử dụng để cho phép Quy trình của chúng tôi được xây dựng và thực thi trôi chảy. Chúng ta sẽ xem xét điều đó ngay bây giờ

_19

Do đó, đặc điểm của chúng tôi chứa một phương thức cho phép chúng tôi trừu tượng hóa các lệnh xây dựng, hiển thị việc triển khai chính hợp đồng quy trình và cung cấp hướng dẫn về cách xây dựng các quy trình

Cho đến thời điểm này, chúng tôi có thể xây dựng một quy trình và một lệnh tiềm năng, nhưng chúng tôi chưa tạo ra một lệnh. Chúng ta hãy xem lớp Git khác này, là một enum, mà chúng ta sử dụng để tạo Lệnh Git mới trong đặc điểm. Tuy nhiên, tôi sẽ trình bày một phiên bản cô đọng bởi vì, rất có thể, bạn muốn điều này ánh xạ tới mọi tiểu ban git mà bạn muốn hỗ trợ

________mười

Tiếp theo, chúng tôi gửi cái này đến Lệnh Git

1declare(strict_types=1);
2 
3namespace JustSteveKing\OS\Contracts;
4 
5use Symfony\Component\Process\Process;
6 
7interface ProcessContract
8{
9 public function build(): Process;
10}
1

Lớp này chấp nhận các đối số từ Quy trình của chúng tôi, hiện đang được xử lý bởi đặc điểm

1declare(strict_types=1);
2 
3namespace JustSteveKing\OS\Contracts;
4 
5use Symfony\Component\Process\Process;
6 
7interface ProcessContract
8{
9 public function build(): Process;
10}
5 của chúng tôi và chuyển đổi chúng thành các đối số mà Quy trình Symfony có thể hiểu được. Để giảm lỗi đường dẫn, chúng tôi sử dụng gói Symfony's
1declare(strict_types=1);
2 
3namespace JustSteveKing\OS\Contracts;
4 
5use Symfony\Component\Process\Process;
6 
7interface ProcessContract
8{
9 public function build(): Process;
10}
6;

Khi mọi thứ được kết hợp bên trong Quy trình Git của chúng tôi, nó trông giống như thế này

1declare(strict_types=1);
2 
3namespace JustSteveKing\OS\Contracts;
4 
5use Symfony\Component\Process\Process;
6 
7interface ProcessContract
8{
9 public function build(): Process;
10}
4

Bây giờ tất cả những gì chúng ta cần làm là tự thực thi mã để hoạt động trơn tru với git bên trong ứng dụng PHP của chúng ta

1declare(strict_types=1);
2 
3namespace JustSteveKing\OS\Contracts;
4 
5use Symfony\Component\Process\Process;
6 
7interface ProcessContract
8{
9 public function build(): Process;
10}
5

Điều duy nhất chúng tôi thay đổi là xây dựng một trình bao bọc hướng đối tượng xung quanh quá trình tạo quy trình này, điều này sẽ cho phép bạn tương tác với Quy trình Symfony và thực hiện tất cả mọi thứ với lệnh ở phía bên kia. Điều này làm cho mọi thứ mở rộng hơn theo cách có thể kiểm tra và mở rộng được, cho phép chúng tôi phát triển và duy trì ngữ cảnh độc đáo

Các ứng dụng của bạn sử dụng các lệnh của hệ điều hành có thường xuyên không?

SSH, MySQL hoặc thậm chí ansible hoặc terraform là những ví dụ tuyệt vời về điều này. Hãy tưởng tượng nếu bạn có thể chạy các kết xuất MySQL một cách hiệu quả theo lịch trình từ Laravel artisan mà không phải liên tục dựa vào các gói của bên thứ ba

Đôi khi bạn cần làm việc với các lệnh cấp hệ điều hành từ ứng dụng PHP của mình. Hãy xem cách chúng tôi có thể làm điều này và xem liệu chúng tôi có thể làm cho Trải nghiệm của nhà phát triển tốt hơn không

Trong vài năm qua, tôi đã tập trung vào nhiều khía cạnh khác nhau trong cách tôi viết mã và cách tôi có thể cải thiện nó. Tôi bắt đầu bằng cách xem xét cách tôi có thể tích hợp với HTTP tốt hơn và hướng đối tượng hơn. Tôi tin rằng tôi đã tìm ra cách để đạt được điều này và hiện đang tập trung sự chú ý của mình vào nơi khác

Có một số trường hợp bạn muốn làm việc với OS CLI trong các ứng dụng của mình. Trong ứng dụng web hoặc ứng dụng CLI khác. Trước đây, chúng tôi đã sử dụng các phương pháp như

1declare(strict_types=1);
2 
3namespace JustSteveKing\OS\Contracts;
4 
5use Symfony\Component\Process\Process;
6 
7interface ProcessContract
8{
9 public function build(): Process;
10}
9 hoặc
1declare(strict_types=1);
2 
3namespace JustSteveKing\OS\Contracts;
4 
5use Symfony\Component\Process\Process;
6 
7interface ProcessContract
8{
9 public function build(): Process;
10}
30 hoặc
1declare(strict_types=1);
2 
3namespace JustSteveKing\OS\Contracts;
4 
5use Symfony\Component\Process\Process;
6 
7interface ProcessContract
8{
9 public function build(): Process;
10}
31 và
1declare(strict_types=1);
2 
3namespace JustSteveKing\OS\Contracts;
4 
5use Symfony\Component\Process\Process;
6 
7interface ProcessContract
8{
9 public function build(): Process;
10}
32. Sau đó, thành phần Quy trình Symfony xuất hiện và chúng tôi đã được cứu

Thành phần quy trình Symfony giúp dễ dàng tích hợp với các quy trình của hệ điều hành và nhận đầu ra. Nhưng cách chúng tôi tích hợp với thư viện này vẫn hơi khó chịu. Chúng tôi tạo một quy trình mới, chuyển vào một mảng các đối số để thực hiện lệnh mà chúng tôi muốn chạy. chúng ta hãy xem

1declare(strict_types=1);
2 
3namespace JustSteveKing\OS\Contracts;
4 
5use Symfony\Component\Process\Process;
6 
7interface ProcessContract
8{
9 public function build(): Process;
10}
6

Điều gì là sai với cách tiếp cận này? . Nhưng có cách nào chúng tôi cải thiện trải nghiệm của nhà phát triển không?

Để cải thiện trải nghiệm của nhà phát triển, trước tiên, chúng ta cần hiểu các thành phần tạo ra một lệnh hệ điều hành một cách hợp lý. Chúng ta có thể chia chúng thành

thực thi được
tranh luận

Tệp thực thi của chúng tôi là thứ mà chúng tôi tương tác trực tiếp, chẳng hạn như php, git, brew hoặc bất kỳ tệp nhị phân được cài đặt nào khác trên hệ thống của chúng tôi. Sau đó, các đối số là cách chúng ta có thể tương tác;

Vì vậy, nếu chúng ta trừu tượng hóa một chút, chúng ta sẽ có một

1declare(strict_types=1);
2 
3namespace JustSteveKing\OS\Contracts;
4 
5use Symfony\Component\Process\Process;
6 
7interface ProcessContract
8{
9 public function build(): Process;
10}
33 và một
1declare(strict_types=1);
2 
3namespace JustSteveKing\OS\Contracts;
4 
5use Symfony\Component\Process\Process;
6 
7interface ProcessContract
8{
9 public function build(): Process;
10}
34 lấy đối số. Chúng tôi sẽ sử dụng các giao diện/hợp đồng để xác định các thành phần của chúng tôi nhằm kiểm soát cách thức hoạt động của quy trình làm việc của chúng tôi. Hãy bắt đầu với Hợp đồng Quy trình

1declare(strict_types=1);
2 
3namespace JustSteveKing\OS\Contracts;
4 
5use Symfony\Component\Process\Process;
6 
7interface ProcessContract
8{
9 public function build(): Process;
10}
3

Ở đây chúng tôi đang nói rằng mỗi quy trình phải có khả năng được xây dựng và kết quả của quy trình được tạo phải là Quy trình Symfony. Quá trình của chúng tôi sẽ xây dựng một Lệnh để chúng tôi chạy, vì vậy bây giờ chúng ta hãy xem Hợp đồng Lệnh của chúng tôi

1declare(strict_types=1);
2 
3namespace JustSteveKing\OS\Contracts;
4 
5use Symfony\Component\Process\Process;
6 
7interface ProcessContract
8{
9 public function build(): Process;
10}
0

Điều chính mà chúng tôi muốn từ lệnh của mình là có thể được trả về dưới dạng đối số mà chúng tôi có thể chuyển vào Quy trình Symfony dưới dạng lệnh

Về ý tưởng thế là đủ, hãy xem qua một ví dụ thực tế. Chúng tôi sẽ sử dụng git làm ví dụ, vì hầu hết chúng ta đều có thể liên quan đến các lệnh git

Trước tiên, chúng ta hãy tạo một quy trình Git thực hiện Hợp đồng quy trình mà chúng ta vừa mô tả

1declare(strict_types=1);
2 
3namespace JustSteveKing\OS\Contracts;
4 
5use Symfony\Component\Process\Process;
6 
7interface ProcessContract
8{
9 public function build(): Process;
10}
1

Quy trình của chúng tôi triển khai hợp đồng và có thuộc tính lệnh mà chúng tôi sẽ sử dụng cho phép quy trình của chúng tôi được xây dựng và thực thi trôi chảy. Chúng tôi có một đặc điểm sẽ cho phép chúng tôi tập trung hóa cách mọi thứ được xây dựng và tạo ra cho quy trình Git của chúng tôi. Hãy để chúng tôi xem xét điều đó

1declare(strict_types=1);
2 
3namespace JustSteveKing\OS\Contracts;
4 
5use Symfony\Component\Process\Process;
6 
7interface ProcessContract
8{
9 public function build(): Process;
10}
2

Vì vậy, đặc điểm của chúng tôi cho thấy việc thực hiện chính hợp đồng quy trình và cung cấp hướng dẫn về cách xây dựng các quy trình. Nó cũng chứa một phương thức cho phép chúng ta trừu tượng hóa các lệnh xây dựng

Chúng tôi có thể tạo một quy trình và xây dựng một lệnh tiềm năng cho đến thời điểm này. Tuy nhiên, chúng tôi vẫn chưa thực hiện một lệnh. Chúng tôi tạo một Lệnh Git mới trong đặc điểm, sử dụng một lớp Git cho loại. Hãy xem lớp Git khác này, đó là một enum. Tuy nhiên, tôi sẽ hiển thị một phiên bản rút gọn - thực tế là bạn muốn điều này ánh xạ tới tất cả các lệnh con git mà bạn muốn hỗ trợ

1declare(strict_types=1);
2 
3namespace JustSteveKing\OS\Contracts;
4 
5use Symfony\Component\Process\Process;
6 
7interface ProcessContract
8{
9 public function build(): Process;
10}
3

Sau đó, chúng tôi chuyển thông tin này đến Lệnh Git

1declare(strict_types=1);
2 
3namespace JustSteveKing\OS\Contracts;
4 
5use Symfony\Component\Process\Process;
6 
7interface ProcessContract
8{
9 public function build(): Process;
10}
4

Trong lớp này, chúng tôi chấp nhận các đối số từ Quy trình của chúng tôi, hiện đang được xử lý bởi đặc điểm

1declare(strict_types=1);
2 
3namespace JustSteveKing\OS\Contracts;
4 
5use Symfony\Component\Process\Process;
6 
7interface ProcessContract
8{
9 public function build(): Process;
10}
35 của chúng tôi. Sau đó, chúng tôi có thể biến điều này thành các đối số mà Quy trình Symfony có thể hiểu được. Chúng tôi sử dụng
1declare(strict_types=1);
2 
3namespace JustSteveKing\OS\Contracts;
4 
5use Symfony\Component\Process\Process;
6 
7interface ProcessContract
8{
9 public function build(): Process;
10}
36 từ gói Symfony để cho phép chúng tôi giảm thiểu lỗi trong đường dẫn. Tuy nhiên, chúng tôi cũng muốn đưa ra một ngoại lệ nếu không thể tìm thấy tệp thực thi

Khi chúng tôi kết hợp tất cả lại với nhau trong Quy trình Git của mình, nó trông giống như thế này

1declare(strict_types=1);
2 
3namespace JustSteveKing\OS\Contracts;
4 
5use Symfony\Component\Process\Process;
6 
7interface ProcessContract
8{
9 public function build(): Process;
10}
7

Bây giờ tất cả những gì còn lại để chúng ta làm là tự chạy mã để chúng ta có thể làm việc với git một cách độc đáo bên trong ứng dụng PHP của chúng ta

1declare(strict_types=1);
2 
3namespace JustSteveKing\OS\Contracts;
4 
5use Symfony\Component\Process\Process;
6 
7interface ProcessContract
8{
9 public function build(): Process;
10}
8

Kết quả của phương thức đẩy sẽ cho phép bạn tương tác với Quy trình Symfony - nghĩa là bạn có thể thực hiện tất cả các loại với lệnh ở phía bên kia. Điều duy nhất chúng tôi đã thay đổi là xây dựng một trình bao bọc hướng đối tượng xung quanh việc tạo quy trình này. Điều này cho phép chúng tôi phát triển và duy trì ngữ cảnh độc đáo, đồng thời mở rộng mọi thứ theo cách có thể kiểm tra và mở rộng được

Bạn có thường xuyên làm việc với các lệnh của hệ điều hành trong các ứng dụng của mình không?

Một ví dụ tuyệt vời về điều này phải là SSH, MySQL hoặc thậm chí là ansible hoặc terraform. Hãy tưởng tượng nếu bạn có thể chạy các kết xuất MySQL một cách hiệu quả theo lịch trình từ Laravel artisan mà không phải lúc nào cũng sử dụng các gói của bên thứ ba