Crontab, hay bảng cron, là một quy trình/daemon hệ thống Linux tạo điều kiện thuận lợi cho việc lên lịch các tác vụ lặp đi lặp lại, từ đó giảm bớt công việc hàng ngày của chúng ta. Trong hướng dẫn này, chúng ta sẽ tạo một lớp PHP động, cho phép chúng ta thao tác với crontab bằng kết nối an toàn.
Tổng quan về Crontab
Có khả năng lên lịch các tác vụ để chạy ở chế độ nền thật tuyệt vời! Sao lưu cơ sở dữ liệu SQL, tìm nạp hoặc gửi email, chạy các tác vụ dọn dẹp, phân tích hiệu suất hoặc thậm chí lấy nguồn cấp dữ liệu RSS—các công việc định kỳ thật tuyệt vời!
Mặc dù cú pháp lập kế hoạch cho một công việc mới thoạt nhìn có vẻ khó khăn nhưng nó lại tương đối đơn giản để hiểu khi bạn chia nhỏ nó ra. Một công việc định kỳ sẽ luôn có năm cột, mỗi cột đại diện cho một toán tử theo trình tự thời gian , theo sau là đường dẫn đầy đủ và lệnh để thực thi:
Ví dụ
* * * * * home/path/to/command/the_command.sh
Mỗi cột theo trình tự thời gian có mức độ liên quan cụ thể với lịch trình của nhiệm vụ. Chúng như sau:
- Phút biểu thị số phút của một giờ nhất định, tương ứng là 0-59.
- Giờ đại diện cho giờ của một ngày nhất định, tương ứng là 0-23.
- Ngày đại diện cho các ngày của một tháng nhất định, tương ứng là 1-31.
- Tháng đại diện cho các tháng của một năm nhất định, tương ứng là 1-12.
- Ngày trong tuần biểu thị ngày trong tuần, từ Chủ Nhật đến Thứ Bảy, lần lượt bằng số từ 0-6.
Ví dụ
Phút [ 0-59]
| Giờ [ 0-23]
| | Ngày [ 1-31]
| | | Tháng [ 1-12]
| | | | Ngày trong tuần [ Số, 0-6]
| | | | |
* * * * * home/path/to/command/the_command.sh
Vì vậy, ví dụ: nếu bạn muốn lên lịch một nhiệm vụ vào lúc 12 giờ sáng vào ngày đầu tiên hàng tháng, nó sẽ trông giống như thế này:
Ví dụ
0 0 1 * * home/path/to/command/the_command.sh
Mặt khác, nếu bạn muốn lên lịch một tác vụ để chạy vào Thứ Bảy hàng tuần lúc 8:30 sáng, chúng tôi sẽ viết nó như sau:
Ví dụ
30 8 * * 6 home/path/to/command/the_command.sh
Ngoài ra còn có một số toán tử có thể được sử dụng để tùy chỉnh lịch trình hơn nữa:
- Dấu phẩy được sử dụng để tạo danh sách các giá trị được phân tách bằng dấu phẩy cho bất kỳ cột cron nào.
- Dấu gạch ngang được sử dụng để chỉ định một phạm vi giá trị.
- Dấu hoa thị được sử dụng để chỉ định tất cả hoặc mọi giá trị.
Theo mặc định, crontab sẽ gửi thông báo qua email bất cứ khi nào một tác vụ theo lịch trình được thực thi. Tuy nhiên, trong nhiều trường hợp, điều này là không cần thiết. Chúng ta có thể dễ dàng ngăn chặn chức năng này bằng cách chuyển hướng đầu ra tiêu chuẩn của lệnh này đến lỗ đen hoặc /dev/null
thiết bị. Về cơ bản, đây là một tập tin sẽ loại bỏ mọi thứ được ghi vào nó. Chuyển hướng đầu ra được thực hiện thông qua >
toán tử. Chúng ta hãy xem cách chúng ta có thể chuyển hướng đầu ra tiêu chuẩn đến lỗ đen bằng cách sử dụng công việc định kỳ mẫu, chạy một tác vụ theo lịch trình vào lúc 8:30 sáng Thứ Bảy hàng tuần:
Ví dụ
30 8 * * 6 home/path/to/command/the_command.sh > /dev/null
Ngoài ra, nếu chúng tôi đang chuyển hướng đầu ra tiêu chuẩn sang thiết bị null, có thể chúng tôi cũng muốn chuyển hướng các lỗi tiêu chuẩn. Chúng ta có thể thực hiện điều này bằng cách chuyển hướng các lỗi tiêu chuẩn đến nơi đầu ra tiêu chuẩn đã được chuyển hướng, thiết bị null!
Ví dụ
30 8 * * 6 home/path/to/command/the_command.sh > /dev/null 2>&1
Quản lý crontab bằng PHP
Để quản lý crontab bằng PHP, chúng ta cần có khả năng thực thi các lệnh trên máy chủ từ xa với tư cách là người dùng có crontab mà chúng ta đang chỉnh sửa. Mặc dù bạn có thể sử dụng thư viện SSH2 do PHP cung cấp, nhưng chúng ta sẽ sử dụng thư viện phpseclib , thư viện này hỗ trợ các giao thức khác nhau để giao tiếp với các máy chủ từ xa.
Cách cài đặt và sử dụng thư viện phpseclib
Thư viện phpseclib (Thư viện truyền thông an toàn PHP) là thư viện PHP cung cấp một tập hợp các phương thức và lớp cho các giao thức truyền thông an toàn như SSH, SFTP và SSL/TLS. Nó cho phép chúng tôi kết hợp các khả năng liên lạc an toàn vào các ứng dụng của mình mà không cần phải dựa vào các công cụ hoặc tiện ích mở rộng dòng lệnh bên ngoài.
Nếu bạn đang thắc mắc tại sao bạn lại thích sử dụng phpseclib hơn các hàm SSH2 cốt lõi do PHP cung cấp, hãy xem các lợi ích của thư viện phpseclib:
- Thư viện SSH2 cung cấp chức năng hạn chế và có thể không hỗ trợ tất cả các tính năng SSH
- Cung cấp tính trừu tượng, cho phép bạn chuyển đổi giữa các giao thức khác nhau một cách dễ dàng
- Tóm tắt sự phức tạp của các giao thức cơ bản
- Dễ sử dụng và trực quan
- Nó không yêu cầu bất kỳ sự phụ thuộc hoặc tiện ích mở rộng bên ngoài nào
- Hỗ trợ nhiều giao thức liên lạc an toàn, bao gồm SSH, SFTP, SCP, FTPS, SSL/TLS, v.v.
- Được ghi chép đầy đủ, được phát triển tích cực và hỗ trợ cộng đồng
Như bạn có thể thấy, thư viện phpseclib có thể cung cấp giải pháp toàn diện và mạnh mẽ hơn cho nhu cầu liên lạc an toàn.
Hãy xem bạn có thể cài đặt nó như thế nào. Bạn có thể cài đặt thư viện phpseclib bằng composer, như trong đoạn mã sau.
Ví dụ
$composer install phpseclib/phpseclib
Sau khi cài đặt xong, bạn có thể bắt đầu sử dụng thư viện phpseclib trong mã PHP của mình bằng cách đưa tập lệnh trình tải tự động vào đầu tập lệnh PHP, như minh họa trong đoạn mã sau.
Ví dụ
<?php
require 'vendor/autoload.php';
Chúng ta hãy xem nhanh cách bạn có thể thực hiện đăng nhập SSH2 từ xa.
Ví dụ
<?php
require 'vendor/autoload.php';
use phpseclib3\Net\SSH2;
$ssh = new SSH2('example.remote-server.com');
if (!$ssh->login('ssh-username', 'ssh-password')) {
die('Login to remote server failed.');
}
Đây là phần giới thiệu ngắn gọn về thư viện phpseclib.
Mẫu lớp Ssh2_crontab_manager
Trong phần này, chúng ta sẽ thảo luận về những hàm nào chúng ta sẽ cần triển khai trong lớp của mình.
Lớp của chúng ta phải có khả năng kết nối và xác thực, với tư cách là một người dùng thích hợp, để thực thi các lệnh và có quyền truy cập vào crontab của người dùng. Do đó, chúng tôi sẽ thiết lập kết nối SSH2 và xác thực nó trong chính hàm tạo.
Với một kết nối được xác thực đã có, chúng ta sẽ cần một phương pháp để xử lý việc thực thi các lệnh khác nhau mà chúng ta sẽ chạy. Chúng tôi sẽ sử dụng hàm exec()
được cung cấp bởi thư viện phpseclib. Nói chung, chúng ta sẽ gọi hàm này từ bên trong các hàm khác của lớp khi chúng ta cần thực thi một lệnh trên máy chủ từ xa.
Tiếp theo, chúng ta sẽ cần khả năng ghi crontab vào một tệp để chúng ta có quyền truy cập hữu hình vào nó. Chúng tôi cũng sẽ cần một cách để xóa tệp này khi chúng tôi hoàn thành nó. Chúng tôi sẽ xác định phương thức xử lý việc xuất crontab thành tệp dưới dạng write_to_file()
và hàm xóa tệp này dưới dạng remove_file()
.
Tất nhiên, chúng ta cũng cần một cách để tạo và loại bỏ các công việc định kỳ. Vì vậy, chúng tôi sẽ xác định hàm append_cronjob()
và remove_cronjob()
tương ứng.
Nếu chúng ta đã xóa cron job duy nhất hoặc cuối cùng, chúng ta sẽ có tùy chọn xóa toàn bộ crontab thay vì cố gắng tạo một crontab trống. Để xử lý tình huống này, chúng ta có thể sử dụng hàm remove_crontab()
này.
Cuối cùng, chúng ta sẽ tạo hai hàm trợ giúp cho lớp của mình. Hàm đầu tiên trong số này sẽ trả về giá trị boolean, sẽ chỉ kiểm tra sự tồn tại của tệp cron tạm thời. Cái sau sẽ được sử dụng để hiển thị thông báo lỗi nếu xảy ra lỗi. Chúng tôi sẽ đặt tên cho các hàm này crontab_file_exists()
và error_message()
.
Lớp PHP cơ bản của chúng ta sẽ trông như thế này:
Ví dụ
<?php
use phpseclib3\Net\SSH2;
Class Ssh2_crontab_manager {
private $connection;
private $path;
private $handle;
private $cron_file;
public function __construct() {...}
public function exec() {...}
public function write_to_file() {...}
public function remove_file() {...}
public function append_cronjob() {...}
public function remove_cronjob() {...}
public function remove_crontab() {...}
private function crontab_file_exists() {...}
private function error_message() {...}
}
?>
Triển khai lớp Ssh2_crontab_manager
Trong phần này, chúng ta sẽ triển khai các hàm khác nhau của lớp chúng ta.
Constructor
Lớp constructor sẽ chịu trách nhiệm chính trong việc thiết lập và xác thực kết nối SSH2.
Chúng ta hãy nhìn vào hàm __construct
này.
Ví dụ
public function __construct($host = null, $port = null, $username = null, $password = null)
{
$path_length = strrpos(__FILE__, "/");
$this->path = substr(__FILE__, 0, $path_length) . '/';
$this->handle = 'crontab.txt';
$this->cron_file = "{$this->path}{$this->handle}";
try {
if (is_null($host) || is_null($port) || is_null($username) || is_null($password)) {
throw new Exception("Please specify the host, port, username, and password!");
}
$this->connection = new SSH2($host, $port);
if (!$this->connection->login($username, $password)) {
throw new Exception("Could not authenticate '{$username}' using password: '{$password}'.");
}
} catch (Exception $e) {
$this->error_message($e->getMessage());
}
}
Đầu tiên, phải có bốn đối số, mỗi đối số sẽ có giá trị mặc định là NULL
:
$host
: đại diện cho địa chỉ IP của máy chủ từ xa mà chúng tôi muốn kết nối.$port
: cổng được sử dụng cho kết nối SSH2.$username
: đại diện cho tên đăng nhập của người dùng cho máy chủ.$password
: đại diện cho mật khẩu của người dùng cho máy chủ.
Đầu tiên, nó tính toán đường dẫn đến tệp hiện tại và đặt nó vào $this->path
biến. Nó cũng đặt tên của tệp cron sẽ được sử dụng $this->handle
và xây dựng đường dẫn đầy đủ đến tệp cron ở định dạng $this->cron_file
.
Sau đó, chúng tôi kiểm tra xem có thiếu tham số bắt buộc nào không và nếu có, nó sẽ đưa ra một ngoại lệ kèm theo thông báo lỗi thích hợp.
Tiếp theo, nó tạo một thể hiện của SSH2
lớp bằng cách chuyển các giá trị $host
và $port
để thiết lập kết nối SSH.
Cuối cùng, nó cố gắng xác thực bằng cách sử dụng cái được cung cấp $username
và $password
bằng cách gọi hàmlogin()
của đối tượng kết nối SSH2. Nếu xác thực thành công, kết nối sẽ được thiết lập. Nếu thất bại, một ngoại lệ sẽ được ném ra.
Hàm exec
Hàm exec
chịu trách nhiệm thực thi các lệnh trên máy chủ từ xa. Nó có thể so sánh với việc nhập thủ công các lệnh vào một shell như PuTTY. Để linh hoạt hơn, chúng tôi sẽ công khai phương thức này để người dùng có thể thực sự thực thi bất kỳ lệnh nào khác mà họ có thể cần chạy.
Chúng ta hãy nhìn vào hàm exec
này.
Ví dụ
public function exec()
{
$argument_count = func_num_args();
try {
if (!$argument_count) {
throw new Exception("There is nothing to execute, no arguments specified.");
}
$arguments = func_get_args();
$command_string = ($argument_count > 1) ? implode(" && ", $arguments) : $arguments[0];
$stream = $this->connection->exec($command_string);
if (!$stream) {
throw new Exception("Unable to execute the specified commands: <br />{$command_string}");
}
} catch (Exception $e) {
$this->error_message($e->getMessage());
}
return $this;
}
Hàm exec()
sử dụng hàm exec
của thư viện phpseclib để thực thi các lệnh.
Điều đầu tiên chúng ta sẽ làm trong phương pháp này là xác định một biến đại diện cho tổng số đối số được truyền. Chúng ta sẽ sử dụng hàm của PHP func_num_args()
để tính số này. Tiếp theo, trong khối thử, chúng tôi sẽ kiểm tra xem có bất kỳ đối số nào được truyền cho phương thức này hay không. Nếu số lượng đối số là 0, chúng tôi sẽ đưa ra một ngoại lệ mới kèm theo thông báo thích hợp.
Tiếp theo, bằng cách sử dụng hàm func_get_args()
, chúng ta sẽ tạo một mảng gồm tất cả các đối số được truyền cho phương thức này. Sau đó, bằng cách sử dụng toán tử ba ngôi, chúng ta sẽ xác định một biến mới, $command_string
, dưới dạng biểu diễn chuỗi một dòng của các lệnh Linux thực tế mà chúng ta sẽ thực thi.
Nếu có nhiều lệnh để thực thi, chúng ta sẽ sử dụng hàm implode()
của PHP để phân tích mảng đối số thành một chuỗi. Chúng ta sẽ chuyển hai đối số tới implode()
: keo hoặc dấu phân cách, về cơ bản chỉ là một chuỗi sẽ được chèn giữa các phần tử mảng và chính mảng đó. Chúng tôi sẽ tách từng phần tử bằng toán tử Linux &&
, cho phép chúng tôi thực thi nhiều lệnh một cách tuần tự, trên một dòng!
exec()
từ lớp SSH2 để thực thi chuỗi lệnh qua kết nối SSH đã thiết lập. Giá trị trả về được lưu trữ trong biến $stream
.Hàm write_to_file
Hàm tiếp theo, write_to_file()
, sẽ chịu trách nhiệm ghi crontab hiện có vào một tệp tạm thời hoặc tạo một tệp tạm thời trống nếu không có công việc định kỳ nào tồn tại. Nó sẽ có hai đối số:
- Đường dẫn của tệp tạm thời chúng tôi sẽ tạo
- Tên chúng ta nên sử dụng khi tạo nó
Hàm write_to_file
sẽ trông như thế này.
Ví dụ
public function write_to_file($path=NULL, $handle=NULL)
{
if (! $this->crontab_file_exists())
{
$this->handle = (is_null($handle)) ? $this->handle : $handle;
$this->path = (is_null($path)) ? $this->path : $path;
$this->cron_file = "{$this->path}{$this->handle}";
$init_cron = "crontab -l > {$this->cron_file} && [ -f {$this->cron_file} ] || > {$this->cron_file}";
$this->exec($init_cron);
}
return $this;
}
Đầu tiên, chúng tôi kiểm tra xem tệp cron đã tồn tại hay chưa bằng cách sử dụng hàm crontab_file_exists()
boolean mà chúng tôi sẽ tạo ngay sau đây. Nếu tệp đã tồn tại thì không cần phải tiếp tục. Nếu không, chúng tôi sẽ sử dụng toán tử bậc ba để kiểm tra các đạo cụ $path
và $handle
để xác định xem chúng có phải là NULL
. Nếu một trong hai giá trị đó là NULL
, chúng tôi sẽ sử dụng các giá trị dự phòng được xác định trước từ hàm tạo của chúng tôi để xác định chúng.
Sau đó, chúng ta sẽ ghép các thuộc tính này lại với nhau thành một thuộc tính mới, đại diện cho đường dẫn đầy đủ và tên tệp của tệp cron tạm thời.
Tiếp theo, chúng tôi đã sử dụng lệnh Linux crontab
với đối số -l
để hiển thị crontab của người dùng dưới dạng đầu ra tiêu chuẩn. Sau đó, bằng cách sử dụng toán tử của Linux >
, thay vào đó, chúng tôi sẽ chuyển hướng đầu ra tiêu chuẩn hoặc STDOUT
đến tệp cron tạm thời bằng cách nối $this->cron_file
vào chuỗi lệnh! Chuyển hướng đầu ra sang một tệp bằng toán tử >
sẽ luôn tạo tệp đó nếu nó không tồn tại.
Điều này hoạt động rất tốt, nhưng chỉ khi đã có công việc được lên lịch trong crontab. Nếu không có công việc định kỳ, tập tin này sẽ không bao giờ được tạo! Tuy nhiên, bằng cách sử dụng toán tử &&
, chúng ta có thể nối thêm các lệnh/biểu thức bổ sung vào chuỗi này. Vì vậy, hãy thêm một biểu thức điều kiện để kiểm tra xem tệp cron có tồn tại hay không. Nếu tệp không tồn tại, biểu thức này sẽ đánh giá là sai. Như vậy, sau đó chúng ta có thể sử dụng toán tử ||
hoặc or
sau điều kiện này để tạo một tệp trống mới nếu cần.
Các lệnh được đặt sau or
sẽ thực thi nếu điều kiện/điều kiện được đánh giá là sai. Bây giờ, bằng cách sử dụng lại toán tử >
, lần này không đặt trước nó bằng một lệnh cụ thể nào, chúng ta có thể tạo một tệp trống mới. Vì vậy, về cơ bản, chuỗi lệnh này sẽ xuất crontab thành một tệp và sau đó kiểm tra xem tệp đó có tồn tại hay không, điều này cho biết có các mục trong crontab, sau đó tạo một tệp trống mới nếu nó chưa tồn tại.
Cuối cùng, chúng ta đã gọi hàm exec()
này và truyền chuỗi lệnh cho nó làm đối số duy nhất. Sau đó, để làm cho hàm này có thể kết nối được, chúng tôi sẽ trả về $this
.
Hàm remove_file
Chúng ta hãy nhanh chóng xem xét hàm remove_file
.
Ví dụ
public function remove_file()
{
if ($this->crontab_file_exists()) $this->exec("rm {$this->cron_file}");
return $this;
}
Trong hàm này, chúng tôi đã sử dụng phương thức trợ giúp của mình, crontab_file_exists()
, để kiểm tra sự tồn tại của tệp cron tạm thời và sau đó thực thi lệnh Linux rm
để xóa nó nếu có. Như thường lệ, chúng tôi cũng sẽ quay lại $this
để duy trì khả năng kết nối.
Hàm append_cronjob
Hàm append_cronjob
tạo các công việc định kỳ mới bằng cách thêm các công việc/dòng mới vào tệp cron tạm thời, sau đó thực thi lệnh crontab
trên tệp đó, lệnh này sẽ cài đặt tất cả các công việc đó dưới dạng crontab mới.
Nó trông như thế này:
Ví dụ
public function append_cronjob($cron_jobs=NULL)
{
if (is_null($cron_jobs)) $this->error_message("Nothing to append! Please specify a cron job or an array of cron jobs.");
$append_cronfile = "echo '";
$append_cronfile .= (is_array($cron_jobs)) ? implode("\n", $cron_jobs) : $cron_jobs;
$append_cronfile .= "' >> {$this->cron_file}";
$install_cron = "crontab {$this->cron_file}";
$this->write_to_file()->exec($append_cronfile, $install_cron)->remove_file();
return $this;
}
Hàm append_cronjob()
lấy một đối số, $cron_jobs
, đối số này sẽ là một chuỗi hoặc một mảng các chuỗi biểu thị các công việc định kỳ cần nối thêm.
Chúng ta sẽ bắt đầu hàm này bằng cách xác định xem $cron_jobs
đối số có phải là NULL
. Nếu đúng như vậy, chúng tôi sẽ gọi hàm error_message()
này để tạm dừng mọi hoạt động thực thi tiếp theo và hiển thị thông báo lỗi cho người dùng.
Tiếp theo, chúng tôi đã xác định một biến mới, $append_cronfile
, dưới dạng một chuỗi, với văn bản echo
theo sau là khoảng trắng và một dấu ngoặc kép ở cuối. Chúng tôi sẽ điền vào chuỗi này các công việc định kỳ khác nhau mà chúng tôi đang thêm, cũng như câu trích dẫn kết thúc trong giây lát. Chúng ta sẽ xây dựng chuỗi này bằng toán tử nối chuỗi .=
.
Tiếp theo, bằng cách sử dụng toán tử bậc ba, chúng ta xác định xem $cron_jobs
có phải là mảng hay không. Nếu đúng như vậy, chúng tôi sẽ mã hóa mảng đó bằng các ký tự dòng mới \n
để mỗi lệnh cron được viết trên dòng riêng trong tệp cron. Nếu đối số $cron_jobs
không phải là một mảng, chúng ta sẽ chỉ nối công việc đó vào chuỗi $append_cron
mà không cần xử lý đặc biệt.
Về cơ bản, chúng ta chỉ có thể lặp lại nhiệm vụ của mình vào tệp cron bằng cách chuyển hướng lại đầu ra tiêu chuẩn vào tệp. Vì vậy, bằng cách sử dụng toán tử nối chuỗi, chúng tôi sẽ nối thêm dấu nháy đơn đóng vào chuỗi lệnh, cũng như toán tử Linux, >>
theo sau là đường dẫn đầy đủ và tên tệp cho tệp cron. Toán tử >>
, không giống như toán tử >
luôn ghi đè lên một tệp, sẽ thêm đầu ra vào cuối tệp. Vì vậy, bằng cách sử dụng toán tử này, chúng tôi sẽ không ghi đè bất kỳ công việc định kỳ nào hiện có.
Tiếp theo, chúng ta đã xác định một biến là một chuỗi, bằng lệnh mà chúng ta sẽ sử dụng để cài đặt tệp cron mới mà chúng ta sắp tạo. Điều này đơn giản như việc gọi lệnh crontab, theo sau là đường dẫn và tên tệp của tệp cron.
Tuy nhiên , cuối cùng, trước khi thực hiện các lệnh này thông qua hàm exec()
, trước tiên chúng ta sẽ gọi hàm write_to_file()
để tạo tệp cron tạm thời. Sau đó, trong một chuỗi, chúng ta sẽ thực thi các lệnh này và gọi hàm remove_file()
để xóa tệp tạm thời. Cuối cùng, chúng tôi sẽ quay lại $this
để hàm append_cronjob()
có thể kết nối được.
Hàm remove_cronjob
Bây giờ chúng ta có thể tạo các công việc định kỳ mới, điều hợp lý là chúng ta cũng có khả năng loại bỏ chúng! Và đó là mục đích của hàm remove_cronjob
này.
Chúng ta hãy nhìn vào nó.
Ví dụ
public function remove_cronjob($cron_jobs=NULL)
{
if (is_null($cron_jobs)) $this->error_message("Nothing to remove! Please specify a cron job or an array of cron jobs.");
$this->write_to_file();
$cron_array = file($this->cron_file, FILE_IGNORE_NEW_LINES);
if (empty($cron_array)) $this->error_message("Nothing to remove! The cronTab is already empty.");
$original_count = count($cron_array);
if (is_array($cron_jobs))
{
foreach ($cron_jobs as $cron_regex) $cron_array = preg_grep($cron_regex, $cron_array, PREG_GREP_INVERT);
}
else
{
$cron_array = preg_grep($cron_jobs, $cron_array, PREG_GREP_INVERT);
}
return ($original_count === count($cron_array)) ? $this->remove_file() : $this->remove_crontab()->append_cronjob($cron_array);
}
Nó nhận một đối số duy nhất, đó sẽ là một biểu thức chính quy (đơn giản). Nó sẽ được sử dụng để tìm các công việc phù hợp trong crontab và loại bỏ chúng cho phù hợp.
Với tệp cron đã được tạo, bây giờ chúng ta sẽ đọc nó thành một mảng bằng hàm file()
của PHP. Hàm này sẽ phân tích một tệp đã cho thành một mảng, với mỗi dòng là một phần tử mảng. Chúng tôi đã chuyển tệp cron của mình cho hàm này làm đối số đầu tiên và sau đó đặt một cờ đặc biệt, FILE_IGNORE_NEW_LINES
cờ này sẽ buộc file()
bỏ qua tất cả các dòng mới. Vì vậy, chúng ta có một mảng chỉ có các công việc định kỳ. Nếu không có công việc định kỳ nào được lên lịch, mảng này sẽ trống. Sau đó, sẽ không còn lý do gì để tiếp tục. Vì vậy, chúng tôi đã kiểm tra xem liệu $cron_array
có trống hay không và tạm dừng thực thi nếu có.
Bây giờ, chúng ta xác định xem đối số $cron_jobs
có phải là một mảng hay không. Nếu đó là một mảng, chúng ta lặp qua nó bằng một vòng lặp foreach
. Trong vòng lặp đó, chúng ta thực thi một hàm preg_grep()
. Hàm tiện lợi này, không giống như preg_match()
, sẽ trả về một mảng gồm tất cả các phần tử mảng khớp với biểu thức chính quy đã chỉ định. Tuy nhiên, trong trường hợp này, chúng tôi muốn các phần tử mảng không khớp. Nói cách khác, chúng ta cần một loạt tất cả các công việc định kỳ mà chúng ta sẽ giữ lại để có thể khởi tạo crontab chỉ với những công việc này. Do đó, chúng tôi sẽ đặt một cờ đặc biệt ở đây, PREG_GREP_INVERT
, điều này sẽ khiến preg_grep()
trả về một mảng gồm tất cả các phần tử không khớp với biểu thức chính quy. Vì vậy, chúng ta sẽ có một loạt tất cả các công việc định kỳ mà chúng ta muốn giữ lại.
Nếu đối số $cron_jobs
không phải là một mảng, chúng ta sẽ tiến hành theo cách tương tự nhưng không lặp lại. Một lần nữa, chúng ta sẽ xác định lại $cron_array
mảng kết quả từ hàm preg_grep()
có PREG_GREP_INVERT
tập hợp cờ.
Với tập hợp $cron_array
của chúng tôi, bây giờ chúng tôi so sánh độ dài hiện tại của mảng này với độ dài ban đầu mà chúng tôi đã lưu vào bộ đệm trong biến $original_count
. Nếu độ dài giống hệt nhau, chúng tôi sẽ chỉ trả về hàm remove_file()
xóa tệp cron tạm thời. Nếu chúng không khớp, chúng tôi sẽ xóa crontab hiện có và sau đó cài đặt crontab mới.
Hàm remove_crontab
Việc xóa toàn bộ crontab tương đối dễ thực hiện, như được hiển thị trong đoạn mã sau.
Ví dụ
public function remove_crontab()
{
$this->exec("crontab -r")->remove_file();
return $this;
}
Các hàm trợ giúp khác
Với nội dung chính của lớp quản lý cron đã được viết, bây giờ chúng ta sẽ xem xét hai hàmp nhỏ nhưng hữu ích mà chúng ta đã sử dụng trong lớp crontab_file_exists()
và error_message()
.
Hàm crontab_file_exists
Hàm này trả về kết quả của phương thức PHP file_exists()
, true
hoặc false
, tùy thuộc vào việc tệp cron tạm thời có tồn tại hay không.
Ví dụ
private function crontab_file_exists()
{
return file_exists($this->cron_file);
}
Hàmerror_message
Hàm này lấy một đối số duy nhất, một chuỗi, biểu thị thông báo lỗi mà chúng ta muốn hiển thị. Sau đó chúng ta sẽ gọi Hàm PHPdie()
để tạm dừng thực thi và hiển thị thông báo này. Bản thân chuỗi đó sẽ được nối thành một phần tử <pre>
có kiểu đơn giản được áp dụng cho nó.
Ví dụ
private function error_message($error)
{
die("<pre style='color:#EE2711'>ERROR: {$error}</pre>");
}
Lớp hoàn chỉnh trông như thế này:
Ví dụ
<?php
use phpseclib3\Net\SSH2;
Class Ssh2_crontab_manager {
private $connection;
private $path;
private $handle;
private $cron_file;
public function __construct($host = null, $port = null, $username = null, $password = null)
{
$path_length = strrpos(__FILE__, "/");
$this->path = substr(__FILE__, 0, $path_length) . '/';
$this->handle = 'crontab.txt';
$this->cron_file = "{$this->path}{$this->handle}";
try {
if (is_null($host) || is_null($port) || is_null($username) || is_null($password)) {
throw new Exception("Please specify the host, port, username, and password!");
}
$this->connection = new SSH2($host, $port);
if (!$this->connection->login($username, $password)) {
throw new Exception("Could not authenticate '{$username}' using password: '{$password}'.");
}
} catch (Exception $e) {
$this->error_message($e->getMessage());
}
}
public function exec()
{
$argument_count = func_num_args();
try {
if (!$argument_count) {
throw new Exception("There is nothing to execute, no arguments specified.");
}
$arguments = func_get_args();
$command_string = ($argument_count > 1) ? implode(" && ", $arguments) : $arguments[0];
$stream = $this->connection->exec($command_string);
if (!$stream) {
throw new Exception("Unable to execute the specified commands: <br />{$command_string}");
}
} catch (Exception $e) {
$this->error_message($e->getMessage());
}
return $this;
}
public function write_to_file($path=NULL, $handle=NULL)
{
if (! $this->crontab_file_exists())
{
$this->handle = (is_null($handle)) ? $this->handle : $handle;
$this->path = (is_null($path)) ? $this->path : $path;
$this->cron_file = "{$this->path}{$this->handle}";
$init_cron = "crontab -l > {$this->cron_file} && [ -f {$this->cron_file} ] || > {$this->cron_file}";
$this->exec($init_cron);
}
return $this;
}
public function remove_file()
{
if ($this->crontab_file_exists()) $this->exec("rm {$this->cron_file}");
return $this;
}
public function append_cronjob($cron_jobs=NULL)
{
if (is_null($cron_jobs)) $this->error_message("Nothing to append! Please specify a cron job or an array of cron jobs.");
$append_cronfile = "echo '";
$append_cronfile .= (is_array($cron_jobs)) ? implode("\n", $cron_jobs) : $cron_jobs;
$append_cronfile .= "' >> {$this->cron_file}";
$install_cron = "crontab {$this->cron_file}";
$this->write_to_file()->exec($append_cronfile, $install_cron)->remove_file();
return $this;
}
public function remove_cronjob($cron_jobs=NULL)
{
if (is_null($cron_jobs)) $this->error_message("Nothing to remove! Please specify a cron job or an array of cron jobs.");
$this->write_to_file();
$cron_array = file($this->cron_file, FILE_IGNORE_NEW_LINES);
if (empty($cron_array)) $this->error_message("Nothing to remove! The cronTab is already empty.");
$original_count = count($cron_array);
if (is_array($cron_jobs))
{
foreach ($cron_jobs as $cron_regex) $cron_array = preg_grep($cron_regex, $cron_array, PREG_GREP_INVERT);
}
else
{
$cron_array = preg_grep($cron_jobs, $cron_array, PREG_GREP_INVERT);
}
return ($original_count === count($cron_array)) ? $this->remove_file() : $this->remove_crontab()->append_cronjob($cron_array);
}
public function remove_crontab()
{
$this->exec("crontab -r")->remove_file();
return $this;
}
private function crontab_file_exists()
{
return file_exists($this->cron_file);
}
private function error_message($error)
{
die("<pre style='color:#EE2711'>ERROR: {$error}</pre>");
}
}
?>
Kết hợp tất cả cùng nhau
Bây giờ chúng ta đã hoàn thành lớp quản lý cron, hãy xem một vài ví dụ về cách sử dụng nó.
Khởi tạo lớp và thiết lập kết nối được xác thực
Đầu tiên, hãy tạo một thể hiện mới của lớp chúng ta. Hãy nhớ rằng chúng ta cần chuyển địa chỉ IP, cổng, tên người dùng và mật khẩu cho hàm tạo của lớp.
Ví dụ
<?php
require 'vendor/autoload.php';
include "./ssh2_crontab_manager.php";
$crontab = new Ssh2_crontab_manager('11.11.111.111', '22', 'my_username', 'my_password');
Bổ sung một cron job
Với một kết nối được xác thực đã sẵn sàng, chúng ta hãy xem cách chúng ta có thể tạo một công việc định kỳ mới, duy nhất.
Ví dụ
<?php
require 'vendor/autoload.php';
include "./ssh2_crontab_manager.php";
$crontab = new Ssh2_crontab_manager('11.11.111.111', '22', 'my_username', 'my_password');
$crontab->append_cronjob('30 8 * * 6 home/path/to/command/the_command.sh >/dev/null 2>&1');
Thêm nhiều cron job
Việc thêm nhiều công việc định kỳ cũng dễ dàng như việc thêm một công việc định kỳ. Chúng ta chỉ cần truyền một mảng vào hàm append_cronjob()
.
Ví dụ
<?php
require 'vendor/autoload.php';
include "./ssh2_crontab_manager.php";
$crontab = new Ssh2_crontab_manager('11.11.111.111', '22', 'my_username', 'my_password');
$new_cronjobs = array(
'0 0 1 * * home/path/to/command/the_command.sh',
'30 8 * * 6 home/path/to/command/the_command.sh >/dev/null 2>&1'
);
$crontab->append_cronjob($new_cronjobs);
Xóa một cron job
Theo cách tương tự như cách chúng tôi tạo một công việc định kỳ, bây giờ chúng tôi sẽ xóa một công việc định kỳ. Tuy nhiên, lần này chúng ta sẽ sử dụng biểu thức chính quy để tìm nhiệm vụ thích hợp. PHP Regex này có thể đơn giản hoặc phức tạp tùy theo nhu cầu của bạn. Trên thực tế, có một số cách để biểu thức chính quy cho tác vụ bạn đang tìm kiếm. Ví dụ: nếu tác vụ bạn cần xóa là duy nhất trong đó lệnh đang chạy không được sử dụng ở bất kỳ nơi nào khác trong crontab, bạn có thể chỉ cần chỉ định tên lệnh làm biểu thức chính quy của mình. Hơn nữa, nếu bạn muốn xóa tất cả công việc trong một tháng nhất định, bạn có thể chỉ cần viết một biểu thức chính quy để tìm kết quả phù hợp cho tất cả công việc của một tháng nhất định.
Ví dụ
<?php
require 'vendor/autoload.php';
include "./ssh2_crontab_manager.php";
$crontab = new Ssh2_crontab_manager('11.11.111.111', '22', 'my_username', 'my_password');
$cron_regex = '/home\/path\/to\/command\/the_command\.sh\/';
$crontab->remove_cronjob($cron_regex);
Loại bỏ nhiều cron job
Việc xóa nhiều cronjob được xử lý theo cách tương tự như xóa một cronjob, với một ngoại lệ duy nhất: chúng ta sẽ chuyển một mảng biểu thức chính quy của cronjob vào hàm remove_cronjob()
.
Ví dụ
<?php
require 'vendor/autoload.php';
include "./ssh2_crontab_manager.php";
$crontab = new Ssh2_crontab_manager('11.11.111.111', '22', 'my_username', 'my_password');
$cron_regex = array(
'/0 0 1 \* \*/',
'/home\/path\/to\/command\/the_command\.sh\/'
);
$crontab->remove_cronjob($cron_regex);
Phần kết luận
Chỉ vậy thôi các bạn! Tôi hy vọng bạn thích đọc bài viết này cũng như tôi thích viết nó và bạn đã có được những hiểu biết mới về crontab và quản lý nó bằng PHP. Cảm ơn bạn rất nhiều vì đã đọc.