Статьи

Управление заданиями Cron с помощью PHP

CronTab, или «Cron Table», является системным процессом / демоном Linux, который облегчает планирование повторяющихся задач, облегчая нашу повседневную рутину.

В этом уроке мы создадим динамический класс PHP, который, используя безопасное соединение, предоставляет нам средства для манипулирования cronTab!

На Envato Market вы можете найти огромный выбор PHP-скриптов , многие из которых могут помочь в подобных ситуациях. Например, взгляните на Professional PHP Cronjobs , скрипт, который позволяет веб-мастерам повторять файлы / функции через заданный интервал времени.

Профессиональный PHP Cronjobs
Профессиональный PHP Cronjobs

Или вы можете нанять разработчика PHP на Envato Studio, чтобы создать что-то специально для вас. Будь то быстрое исправление или создание приложения с нуля, один из экспертов сможет вам помочь.

PHP разработчики на Envato Studio
PHP разработчики на Envato Studio

Посмотрим правде в глаза, возможность планировать задачи для запуска в фоновом режиме просто замечательно! От резервного копирования базы данных SQL, извлечения / отправки электронных писем до выполнения задач по очистке, анализа производительности или даже перехвата RSS-каналов задания cron являются фантастическими!

Хотя на первый взгляд синтаксис планирования новой работы может показаться устрашающим, понять его на самом деле довольно просто, если разбить его. Задание cron всегда будет иметь пять столбцов, каждый из которых представляет хронологический «оператор», за которым следует полный путь и команда для выполнения:

1
* * * * * home/path/to/command/the_command.sh

Каждый из хронологических столбцов имеет конкретное отношение к графику задачи. Они заключаются в следующем:

  • Минуты представляют минуты данного часа, 0-59 соответственно.
  • Часы представляют часы данного дня, 0-23 соответственно.
  • Дни представляют дни данного месяца, 1-31 соответственно.
  • Месяцы представляют месяцы данного года, 1-12 соответственно.
  • День недели представляет день недели с воскресенья по субботу в числовом виде 0-6 соответственно.

1
2
3
4
5
6
7
Minutes [0-59]
   |
   |
   |
   |
   |
   * * * * * home/path/to/command/the_command.sh

Так, например, если бы вы хотели запланировать задачу на 12 часов утра в первый день каждого месяца, это выглядело бы примерно так:

1
0 0 1 * * home/path/to/command/the_command.sh

Если бы мы хотели запланировать выполнение задачи каждую субботу в 8:30, мы бы написали ее следующим образом

1
30 8 * * 6 home/path/to/command/the_command.sh

Существует также ряд операторов, которые можно использовать для дальнейшей настройки расписания:

  • Запятые используются для создания списка значений через запятую для любого из столбцов cron.
  • Тире используется для указания диапазона значений.
  • Звездочки используются для указания значения «все» или «каждое».

По умолчанию cronTab будет отправлять уведомление по электронной почте при выполнении запланированной задачи.

По умолчанию cronTab будет отправлять уведомление по электронной почте при выполнении запланированной задачи. Однако во многих случаях это просто не нужно. Мы можем легко подавить эту функциональность, перенаправив стандартный вывод этой команды на устройство «черная дыра» или /dev/null . По сути, это файл, который будет отбрасывать все, что написано в нем. Перенаправление вывода осуществляется через оператор > . Давайте посмотрим, как мы можем перенаправить стандартный вывод в черную дыру с помощью нашего примера задания cron, которое запускает запланированное задание каждую субботу в 8:30:

1
30 8 * * 6 home/path/to/command/the_command.sh >/dev/null

Кроме того, если мы перенаправляем стандартный вывод на нулевое устройство, мы, вероятно, захотим перенаправить и стандартные ошибки. Мы можем сделать это, просто перенаправив стандартные ошибки туда, где стандартный вывод уже перенаправлен, на нулевое устройство!

1
30 8 * * 6 home/path/to/command/the_command.sh >/dev/null 2>&1

Чтобы управлять cronTab с помощью PHP, нам понадобится возможность выполнять команды на удаленном сервере как пользователь, чей cronTab мы редактируем. К счастью, PHP предоставляет нам простой способ сделать это через библиотеку SSH2. Вы можете или не можете установить эту библиотеку, так что если вы этого не сделаете, вы захотите установить ее:

PHP libssh2 Установка / Настройка

Мы начнем наш класс с объявления четырех свойств, каждое из которых будет приватным:

  • $ connection представляет наше соединение / ресурс.
  • $ path будет представлять путь для этого файла.
  • $ handle будет представлять имя нашего временного файла cron.
  • $ cron_file представляет полный путь и имя временного файла cron.

Наш класс должен иметь возможность подключаться и аутентифицироваться как соответствующий пользователь, чтобы выполнять команды и иметь доступ к cronTab этого пользователя. Таким образом, мы установим соединение SSH2 и аутентифицируем его в самом конструкторе.

После установки аутентифицированного соединения нам понадобится метод для выполнения различных команд, которые мы будем выполнять. Мы назовем этот метод exec() . Обычно мы вызываем этот метод из других методов нашего класса, когда нам нужно выполнить команду на удаленном сервере.

Далее нам понадобится возможность записать cronTab в файл, чтобы мы имели к нему ощутимый доступ. Нам также понадобится способ удалить этот файл, когда мы закончим с ним. Давайте определим метод, который обрабатывает вывод cronTab в файл как write_to_file() а метод удаления этого файла — remove_file() .

Конечно, нам также понадобится способ создавать и удалять задания cron. Поэтому мы определим append_cronjob() и remove_cronjob() соответственно.

В случае, если мы удалили единственный / последний cronjob, нам понадобится возможность удалить весь cronTab, а не пытаться создать пустой cronTab. Мы будем использовать метод remove_crontab() для этого.

Наконец, мы создадим два вспомогательных метода для нашего класса. Первый из этих методов, который будет возвращать логическое значение, просто проверит наличие временного файла cron. Последний будет использоваться для отображения сообщений об ошибках в случае возникновения ошибки. Мы crontab_file_exists() эти методы crontab_file_exists() и error_message() соответственно.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<?php
 
   Class Ssh2_crontab_manager {
 
       private $connection;
       private $path;
       private $handle;
       private $cron_file;
 
       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() {…}
 
   }

Конструктор класса будет в первую очередь отвечать за установление и аутентификацию соединения SSH2. Он будет принимать четыре аргумента, каждый из которых будет иметь значение по умолчанию NULL :

  • $ host: представляет IP-адрес удаленного сервера, к которому мы хотим подключиться.

  • $ port: порт, который будет использоваться для соединения SSH2.
  • $ username: будет представлять логин пользователя для сервера.
  • $ password: представляет пароль пользователя для сервера.

1
function __construct($host=NULL, $port=NULL, $username=NULL, $password=NULL) {…}

В самом конструкторе первое свойство, которое мы определим, это $this->path который будет представлять «каталог по умолчанию» для наших временных файлов cron.

В идеале, мы просто использовали бы магическую константу PHP __DIR__ чтобы определить эту опору в качестве текущего рабочего каталога. Однако бывают случаи, когда эта константа не может быть определена. Поэтому мы проверим, определено ли __DIR__ .

Если это не так, мы должны получить текущий рабочий каталог вручную. Мы сделаем это, используя комбинацию функций strrpos() и substr() в сочетании с константой __FILE__ которая представляет полный путь и имя текущего файла.

Мы будем использовать strrpos() , которая возвращает позицию последнего вхождения подстроки, чтобы найти позицию последней косой черты в строке __FILE__ . Это значение, которое мы будем хранить как var $path_length , даст нам количество символов до, но не включая последнюю косую черту.

Функция substr() сортирует строку в том виде, в котором она возвращает определенную часть строки, основываясь на начальной позиции соединения и количестве символов, которые мы хотим вернуть.

Мы передадим три аргумента в функцию substr()

  • строка, с которой мы работаем
  • начальное местоположение соединения, в данном случае 0
  • конечное местоположение соединения, которое представлено переменной $path_length .

Затем мы объединяем косую черту в конце этой строки, которая даст нам текущий рабочий каталог.

1
2
3
4
5
function __construct($host=NULL, $port=NULL, $username=NULL, $password=NULL)
   {
       $path_length = strrpos(__FILE__, «/»);
       $this->path = substr(__FILE__, 0, $path_length) .
   }

Теперь мы определим имя файла по умолчанию для временного файла cron как $this->handle а затем объединяем путь и обрабатываем реквизиты вместе как $this->cron_file . Эта опора будет представлять полный путь и имя файла по умолчанию для временного файла cron.

1
2
3
4
5
6
7
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 / catch. Таким образом, мы можем отлавливать ошибки и генерировать исключения, чтобы предоставить пользователю очень конкретную обратную связь.

В блоке try мы сначала проверим, чтобы все аргументы были определены с помощью функции is_null() которая будет возвращать true или false. Если какой-либо из этих аргументов NULL , мы сгенерируем новое исключение с соответствующим сообщением.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
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!»);
       }
       catch
       {
 
       }
   }

Далее мы определим соединение $this->connection , передав аргументы $host и $port в ssh2_connect() которая установит удаленное соединение и вернет этот ресурс или FALSE случае сбоя соединения.

Поскольку мы используем нашу собственную обработку ошибок, мы также будем использовать оператор контроля ошибок @ который будет подавлять любые сообщения об ошибках для этой функции. Если соединение не установлено, мы сгенерируем новое исключение с соответствующим сообщением.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
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 = @ssh2_connect($host, $port);
           if ( ! $this->connection) throw new Exception(«The SSH2 connection could not be established.»);
       }
       catch
       {
 
       }
   }

Теперь мы ssh2_auth_password() аутентифицироваться / войти в систему с помощью функции ssh2_auth_password() передающей ресурс, возвращаемый из нашего соединения, а также имя пользователя и пароль пользователя, в который мы ssh2_auth_password() . ssh2_auth_password() вернет логическое значение, которое представляет статус аутентификации, с помощью которого мы можем выдать новое исключение, если аутентификация не удалась.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
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 = @ssh2_connect($host, $port);
           if ( ! $this->connection) throw new Exception(«The SSH2 connection could not be established.»);
 
           $authentication = @ssh2_auth_password($this->connection, $username, $password);
           if ( ! $authentication) throw new Exception(«Could not authenticate ‘{$username}’ using password: ‘{$password}’.»);
       }
       catch
       {
 
       }
   }

PHP попытается найти соответствующий блок catch для каждого сгенерированного исключения, а затем создаст / передаст объект исключения, содержащий ряд полезных свойств, этому блоку.

Итак, в блоке catch мы будем использовать метод getMessage() объекта исключения для доступа и отображения сообщения, определенного в исключении.

Вы заметите, что мы будем отображать сообщение, вызвав метод error_message() нашего класса. Хотя этот метод еще не определен, он будет просто отображать сообщения об ошибках аккуратно.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
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 = @ssh2_connect($host, $port);
           if ( ! $this->connection) throw new Exception(«The SSH2 connection could not be established.»);
 
           $authentication = @ssh2_auth_password($this->connection, $username, $password);
           if ( ! $authentication) throw new Exception(«Could not authenticate ‘{$username}’ using password: ‘{$password}’.»);
       }
       catch (Exception $e)
       {
           $this->error_message($e->getMessage());
       }
   }

Этот метод будет отвечать за выполнение команд на удаленном сервере. Это сравнимо с ручным вводом команд в оболочку, например, PuTTY. Чтобы обеспечить большую гибкость, мы сделаем этот метод общедоступным, чтобы пользователи могли фактически выполнять любые другие команды, которые им могут понадобиться. Мы также допустим любое количество аргументов, если указан хотя бы один аргумент. Эти аргументы будут представлять команды, которые мы хотим запустить с помощью функции ssh2_exec() .

Итак, первое, что мы сделаем в этом методе, это определим переменную, представляющую общее количество переданных аргументов. Мы будем использовать func_num_args() PHP func_num_args() чтобы получить этот счетчик.

1
2
3
4
public function exec()
   {
       $argument_count = func_num_args();
   }

Теперь, в блоке try, мы проверим, переданы ли какие-либо аргументы этому методу. Если количество аргументов равно 0, мы сгенерируем новое исключение с соответствующим сообщением.

01
02
03
04
05
06
07
08
09
10
11
12
13
public function exec()
   {
       $argument_count = func_num_args();
 
       try
       {
           if ( ! $argument_count) throw new Exception(«There is nothing to execute, no arguments specified.»);
       }
       catch
       {
 
       }
   }

Далее, используя func_get_args() мы создадим массив всех аргументов, которые были переданы этому методу.

Используя троичный оператор, мы затем определим новую переменную $command_string в виде однострочного строкового представления реальных команд Linux, которые мы будем выполнять.

Если нам нужно выполнить несколько команд, мы будем использовать PHP-функцию implode() для разбора массива аргументов в строку. Мы передадим два аргумента в implode() , клей или разделитель, который в основном представляет собой просто строку, которая будет вставлена ​​между элементами массива и самим массивом. Мы разделим каждый элемент оператором Linux && что позволит нам выполнять несколько команд последовательно в одной строке!

И наоборот, если есть только одна команда, мы определим командную строку как $arguments[0] которая представляет первый и единственный аргумент / команду.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
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) ?
 
       }
       catch
       {
 
       }
   }

Теперь, когда наши команды готовы и проанализированы как строка, мы можем попытаться выполнить их с помощью функции ssh2_exec() . Мы передадим идентификатор функции соединения, $this->connection , а также нашу $command_string этой функции, которая будет возвращать поток в случае успеха или false при ошибке.

Потоки определяются как объект ресурса, который демонстрирует непрерывное поведение … которое может считываться или записываться линейным образом.

Здесь мы снова будем использовать оператор контроля ошибок @ , чтобы подавить любые сообщения об ошибках, так как мы будем выдавать собственное исключение с соответствующим сообщением в случае возникновения ошибки.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
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) ?
 
           $stream = @ssh2_exec($this->connection, $command_string);
           if ( ! $stream) throw new Exception(«Unable to execute the specified commands: <br />{$command_string}»);
 
       }
       catch
       {
 
       }
   }

Вот и все для блока try! В блоке catch мы просто вызовем метод error_message() , чтобы отобразить любые сообщения об ошибках для нашего пользователя. После завершения блоков try и catch мы вернем $this в конце метода exec() что сделает этот метод цепным!

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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) ?
 
           $stream = @ssh2_exec($this->connection, $command_string);
           if ( ! $stream) throw new Exception(«Unable to execute the specified commands: <br />{$command_string}»);
 
       }
       catch
       {
           $this->error_message($e->getMessage());
       }
 
       return $this;
   }

Следующий метод, write_to_file() , будет отвечать за запись существующего cronTab во временный файл или создание пустого файла temp. В файле не должно быть заданий cron. Это займет два аргумента

  • путь для временного файла, который мы будем создавать
  • имя, которое мы должны использовать при его создании.

Продолжая логику наших методов конструктора и exec, мы установим значения по умолчанию для этих аргументов как NULL .

1
2
3
4
public function write_to_file($path=NULL, $handle=NULL)
   {
 
   }

Первое, что мы сделаем здесь — это проверим, существует ли файл cron, используя логический crontab_file_exists() , который мы вскоре создадим. Если файл существует, нет необходимости продолжать. Если этого не произойдет, мы будем использовать троичный оператор, чтобы проверить реквизиты $path и $handle чтобы определить, являются ли они NULL . Если любой из них имеет NULL , мы будем использовать предопределенные запасные варианты из нашего конструктора, чтобы определить их.

Затем мы объединяем эти свойства вместе в новое свойство, которое будет представлять полный путь и имя файла для временного файла cron.

01
02
03
04
05
06
07
08
09
10
public function write_to_file($path=NULL, $handle=NULL)
   {
       if ( ! $this->crontab_file_exists())
       {
           $this->handle = (is_null($handle)) ?
           $this->path = (is_null($path)) ?
 
           $this->cron_file = «{$this->path}{$this->handle}»;
       }
   }

Далее мы будем использовать команду Linux crontab с установленным аргументом -l, чтобы отобразить пользовательский cronTab в качестве стандартного вывода. Затем, используя оператор Linux > , мы вместо этого перенаправим стандартный вывод, или STDOUT , в наш временный файл cron, объединяя $this->cron_file в командную строку! Перенаправление вывода в файл с помощью оператора > всегда создаст этот файл, если он не существует.

01
02
03
04
05
06
07
08
09
10
11
12
public function write_to_file($path=NULL, $handle=NULL)
   {
       if ( ! $this->crontab_file_exists())
       {
           $this->handle = (is_null($handle)) ?
           $this->path = (is_null($path)) ?
 
           $this->cron_file = «{$this->path}{$this->handle}»;
 
           $init_cron = «crontab -l > {$this->cron_file}»;
       }
   }

Это работает очень хорошо, но только если в cronTab уже запланированы задания. Однако, если нет заданий cron, этот файл никогда не будет создан! Используя оператор && , мы можем добавить дополнительные команды / выражения к этой строке. Итак, давайте добавим условное выражение, чтобы проверить, существует ли файл cron. Если файл не существует, это выражение оценивается как ложное. Таким образом, мы можем использовать || или оператор «или» после этого условия, чтобы создать новый пустой файл, если это необходимо!

Команды, помещенные после «или», будут выполняться, если условие / условия оцениваются как ложные. Теперь, снова используя оператор > , на этот раз без предшествующей ему конкретной команды, мы можем создать новый пустой файл! Таким образом, по сути, эта строка команд выведет cronTab в файл, затем проверит, существует ли этот файл, что указывает на наличие записей в cronTab, а затем создаст новый пустой файл, если он еще не существует!

01
02
03
04
05
06
07
08
09
10
11
12
public function write_to_file($path=NULL, $handle=NULL)
   {
       if ( ! $this->crontab_file_exists())
       {
           $this->handle = (is_null($handle)) ?
           $this->path = (is_null($path)) ?
 
           $this->cron_file = «{$this->path}{$this->handle}»;
 
           $init_cron = «crontab -l > {$this->cron_file} && [ -f {$this->cron_file} ] || > {$this->cron_file}»;
       }
   }

Наконец, мы вызовем метод exec() и передадим ему командную строку в качестве единственного аргумента. Затем, чтобы сделать этот метод цепным, мы вернем $this .

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
public function write_to_file($path=NULL, $handle=NULL)
   {
       if ( ! $this->crontab_file_exists())
       {
           $this->handle = (is_null($handle)) ?
           $this->path = (is_null($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;
   }

Этот метод remove_file() настолько прост, насколько это возможно! Мы будем использовать наш вспомогательный метод crontab_file_exists() , чтобы проверить существование временного файла cron, а затем выполним команду Linux rm чтобы удалить его, если он это сделает. Как обычно, мы также вернем $this , чтобы сохранить цепочечность.

1
2
3
4
5
6
public function remove_file()
   {
       if ($this->crontab_file_exists()) $this->exec(«rm {$this->cron_file}»);
 
       return $this;
   }

Этот метод создаст новые задания cron путем добавления новых заданий / строк во временный файл cron и последующего выполнения команды crontab для этого файла, которая установит все эти задания как новый cronTab. Таким образом, append_cronjob() будет принимать один аргумент, $cron_jobs , который будет либо строкой, либо массивом строк, представляющих задания cron для добавления.

Мы запустим этот метод, определив, имеет ли аргумент $cron_jobs NULL . Если это так, мы вызовем метод error_message() , чтобы остановить дальнейшее выполнение и отобразить сообщение об ошибке для пользователя.

1
2
3
4
5
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.»);
 
   }

По сути, мы можем просто перенести нашу задачу в файл cron, снова перенаправив стандартный вывод в файл.

Далее мы определим новую переменную $append_cronfile в виде строки с текстом «echo», за которым следует пробел и одна одиночная кавычка в конце. Мы будем на мгновение заполнять эту строку различными заданиями cron, которые мы добавляем, а также закрывающей кавычкой. Мы будем строить эту строку, используя оператор конкатенации строк .= .

Используя троичный оператор, мы определим, является ли $cron_jobs массивом или нет. Если это так, мы добавим в этот массив новые строки, \n , чтобы каждое задание cron записывалось на отдельной строке в файле cron. Если аргумент $cron_jobs не является массивом, мы просто объединяем это задание со строкой $append_cron без какой-либо специальной обработки. Таким образом, мы будем иметь правильно отформатированную строку независимо от того, работаем мы с массивом или нет.

По сути, мы можем просто перенести нашу задачу в файл cron, снова перенаправив стандартный вывод в файл. Итак, используя оператор конкатенации строк, мы добавим закрывающую одинарную кавычку к командной строке, а также оператор Linux >> за которым следует полный путь и имя файла для файла cron. Оператор >> , в отличие от оператора > который всегда перезаписывает файл, добавляет вывод в конец файла. Таким образом, используя этот оператор, мы не будем перезаписывать любые существующие задания cron.

01
02
03
04
05
06
07
08
09
10
11
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)) ?
        
       $append_cronfile .= «‘ >> {$this->cron_file}»;
 
   }

Теперь мы определим переменную как строку с помощью команды, которую мы будем использовать для установки нового файла cron, который мы собираемся создать! Это так же просто, как вызов команды crontab с указанием пути и имени файла cron.

Однако перед выполнением этих команд с помощью метода exec() мы сначала вызовем метод write_to_file() , чтобы создать временный файл cron. Затем в цепочке мы выполним эти команды и вызовем метод remove_file() для удаления временного файла. Наконец, мы вернем return $this чтобы метод append_cronjob() цепным.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
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)) ?
        
       $append_cronfile .= «‘ >> {$this->cron_file}»;
        
       $install_cron = «crontab {$this->cron_file}»;
 
       $this->write_to_file()->exec($append_cronfile, $install_cron)->remove_file();
        
       return $this;
   }

Теперь, когда мы можем создавать новые задания cron, вполне логично, что у нас есть возможность их также удалять! Метод remove_cronjob() будет принимать один аргумент, который будет (простым) регулярным выражением. Этот regEx будет использоваться для поиска подходящих рабочих мест в cronTab и соответственно их удаления. Как и в случае с append_cronjob() , в первую очередь мы проверим, имеет ли аргумент $cron_jobs NULL и остановим выполнение, если оно есть. Если нет, мы вызовем метод create_file() чтобы записать вкладку cron в файл.

1
2
3
4
5
6
7
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, мы теперь смотрим его в массив с помощью PHP-функции file() . Эта функция будет анализировать данный файл в массив с каждой строкой в ​​качестве элемента массива. Мы передадим наш файл cron этой функции в качестве первого аргумента, а затем установим специальный флаг FILE_IGNORE_NEW_LINES , который заставит file() игнорировать все новые строки. Таким образом, у нас будет массив только с самими заданиями cron!

Если не запланировано никаких заданий cron, этот массив будет пустым. Впоследствии не будет причин для продолжения. Таким образом, мы проверим, пуст ли $cron_array и остановим выполнение, если оно есть.

Если cronTab не пустой, мы будем подсчитывать элементы в $cron_array используя PHP-функцию count() . Мы будем хранить это значение как $original_count . Вскоре мы будем использовать его, чтобы определить, удалили ли мы какие-либо задания cron из $cron_array .

01
02
03
04
05
06
07
08
09
10
11
12
13
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);
 
   }

Теперь мы определим, является $cron_jobs аргумент $cron_jobs массивом или нет. Если это массив, мы будем повторять его с помощью цикла foreach . В этом цикле мы будем выполнять только одну функцию, preg_grep() . Эта изящная функция, в отличие от preg_match() , будет возвращать массив всех элементов массива, которые соответствуют указанному регулярному выражению. В этом случае, однако, мы хотим, чтобы элементы массива не совпадали. Другими словами, нам нужен массив всех заданий cron, которые мы собираемся сохранить, чтобы мы могли инициализировать cronTab только этими заданиями. Таким образом, мы установим здесь специальный флаг, PREG_GREP_INVERT , который заставит preg_grep() возвращать массив всех элементов, которые не соответствуют регулярному выражению. Таким образом, у нас будет массив всех заданий cron, которые мы хотим сохранить!

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
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_jobs не является массивом, мы будем действовать почти таким же образом, но без какой-либо итерации. Опять же, мы переопределим $cron_array как результирующий массив из функции preg_grep() с PREG_GREP_INVERT флагом PREG_GREP_INVERT .

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
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);
       }
   }

Теперь с нашим набором $cron_array мы сравним текущую длину этого массива с его исходной длиной, которую мы кэшировали в переменной $original_count . Если длины одинаковы, мы просто вернем метод remove_file() чтобы удалить временный файл cron. Если они не совпадают, мы удалим существующий cronTab, а затем установим новый!

Все remove_file() , remove_crontab() и append_cronjob() возвращают $this поэтому, возвращая эти методы, мы все еще сохраняем цепочку этих методов.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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)) ?
   }

Удаление всего cronTab относительно просто. По сути, мы просто выполним команду crontab с установленным флагом -r, который удаляет весь cronTab для данного пользователя. Поскольку crontab был удален, мы могли бы также удалить временный файл cron, если он существует. Затем мы вернем return $this , чтобы сохранить возможность объединения.

1
2
3
4
5
6
public function remove_crontab()
   {
       $this->exec(«crontab -r»)->remove_file();
        
       return $this;
   }

После написания основной части нашего класса управления cron мы рассмотрим два небольших, но полезных метода, которые мы использовали в нашем классе: crontab_file_exists() и error_message() .

  • $ this-> crontab_file_exists ()

    Этот метод просто возвращает результат PHP- file_exists() , true или false, в зависимости от того, существует ли временный файл cron.

1
2
3
4
private function crontab_file_exists()
   {
       return file_exists($this->cron_file);
   }
  • $ this-> error_message ($ ошибка)

    Этот метод будет принимать один аргумент, строку, представляющую сообщение об ошибке, которое мы хотим отобразить. Затем мы вызовем PHP-метод die() , чтобы остановить выполнение и отобразить это сообщение. Сама строка будет объединена в элемент <pre> с примененным к ней неким простым стилем.

1
2
3
4
private function error_message($error)
   {
       die(«<pre style=’color:#EE2711′>ERROR: {$error}</pre>»);
   }

Теперь, когда мы завершили наш класс управления cron, давайте рассмотрим несколько примеров, как его использовать!

  • Создание класса и установление аутентифицированного соединения:

    Во-первых, давайте создадим новый экземпляр нашего класса. Помните, нам нужно передать IP-адрес, порт, имя пользователя и пароль конструктору класса.

1
$crontab = new Ssh2_crontab_manager(‘11.11.111.111′, ’22’, ‘my_username’, ‘my_password’);
  • Добавление одной работы cron:

    Установив соединение с проверкой подлинности, давайте посмотрим, как мы можем создать новую одиночную работу cron.

1
2
$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′);
  • Добавление массива заданий cron:

    Добавление нескольких заданий cron так же просто, как добавление одного задания cron. Мы просто append_cronjob() массив append_cronjob() .

1
2
3
4
5
6
7
8
$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);
  • Удаление одного задания cron:

    Аналогично тому, как мы создали одно задание cron, теперь мы удалим одно. На этот раз, однако, мы будем использовать регулярное выражение, чтобы найти подходящую задачу. Этот regEx может быть настолько простым или сложным, насколько вам нужно. На самом деле, существует множество способов регулярного выражения для задачи, которую вы ищете. Например, если задача, которую нужно удалить, уникальна тем, что выполняемая команда не используется где-либо еще в cronTab, вы можете просто указать имя команды в качестве regEx. Кроме того, если вы хотите удалить все задачи за определенный месяц, вы можете просто написать регулярное выражение, чтобы найти соответствие для всех заданий за месяц!

1
2
3
4
5
$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);
  • Удаление массива заданий cron:

    Удаление нескольких cronjobs обрабатывается так же, как удаление одного cronJob с одним исключением, мы передадим массив регулярных выражений задания cron remove_cronjob()методу.

1
2
3
4
5
6
7
8
$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);

Это все, ребята! Я надеюсь, что вам понравилось читать эту статью так же, как и мне, и что вы получили новое понимание cronTab и управления им с помощью PHP. Большое спасибо за чтение!