Spanish (Español) translation by Elías Nicolás (you can also view the original English article)
El cronTab, o "Cron Table", es un proceso / daemon del sistema Linux que facilita la programación de tareas repetitivas, facilitando así nuestra rutina diaria.
En este tutorial, crearemos una clase PHP dinámica que, usando una conexión segura, nos proporciona un medio para manipular el cronTab.
Antecedentes - Una descripción del Crontab
Reconozcámoslo, tener la capacidad de programar tareas para ejecutar en segundo plano es simplemente genial. Desde hacer una copia de seguridad de una base de datos SQL, buscar / enviar correos electrónicos hasta ejecutar tareas de limpieza, analizar el rendimiento o incluso tomar fuentes RSS, ¡los trabajos cron son fantásticos!
Aunque la sintaxis de programar un nuevo trabajo puede parecer desalentadora a primera vista, en realidad es relativamente simple de entender una vez que se despieza. Un trabajo cron siempre tendrá cinco columnas, cada una de las cuales representa un "operador" cronológico seguido de la ruta completa y el comando para ejecutar:
1 |
* * * * * home/path/to/command/the_command.sh |
Cada una de las columnas cronológicas tiene una relevancia específica para el cronograma de la tarea. Ellos son los siguientes:
- Minutos representa los minutos de una hora dada, 0-59 respectivamente.
- Horas representa las horas de un día determinado, 0-23, respectivamente.
- Dias representa los días de un mes determinado, 1-31 respectivamente.
- Meses representa los meses de un año determinado, 1-12 respectivamente.
- El día de la semana representa el día de la semana, de domingo a sábado, numéricamente, como 0-6 respectivamente.
1 |
Minutes [0-59] |
2 |
| Hours [0-23] |
3 |
| | Days [1-31] |
4 |
| | | Months [1-12] |
5 |
| | | | Days of the Week [Numeric, 0-6] |
6 |
| | | | | |
7 |
* * * * * home/path/to/command/the_command.sh |
Entonces, por ejemplo, si uno quisiera programar una tarea para las 12 a.m. del primer día de cada mes, se vería algo como esto:
1 |
0 0 1 * * home/path/to/command/the_command.sh |
Si quisiéramos programar una tarea para que se ejecute todos los sábados a las 8:30 am, la escribiríamos de la siguiente manera:
1 |
30 8 * * 6 home/path/to/command/the_command.sh |
También hay una serie de operadores que se pueden utilizar para personalizar aún más el calendario:
- Comas se utiliza para crear una lista de valores separados por comas para cualquiera de las columnas cron.
- Guiones se usa para especificar un rango de valores.
- El asterisco se usa para especificar 'todos' o 'todos' los valores.
CronTab, de forma predeterminada, enviará una notificación por correo electrónico siempre que se ejecute una tarea programada.
CronTab, de forma predeterminada,
enviará una notificación por correo electrónico siempre que se ejecute
una tarea programada. En muchas circunstancias, sin embargo, esto
simplemente no es necesario. Sin
embargo, podemos suprimir fácilmente esta funcionalidad al redirigir la
salida estándar de este comando al 'agujero negro' o al dispositivo /dev/null. Básicamente, este es un archivo que descartará todo lo que
está escrito en él. La redirección de salida se realiza a través del
operador >. Echemos
un vistazo a cómo podemos redirigir la salida estándar al agujero negro
utilizando nuestro trabajo cron de muestra que ejecuta una tarea
programada todos los sábados a las 8:30 a.m.
1 |
30 8 * * 6 home/path/to/command/the_command.sh >/dev/null |
Además, si estamos redireccionando la salida estándar a un dispositivo nulo, probablemente también queremos redirigir los errores estándar. Podemos hacer esto simplemente redireccionando los errores estándar a donde la salida estándar ya está redirigida, ¡el dispositivo nulo!
1 |
30 8 * * 6 home/path/to/command/the_command.sh >/dev/null 2>&1 |
Paso 1 - El Blueprint
Para administrar el cronTab con PHP, necesitaremos la capacidad de ejecutar comandos, en el servidor remoto, como el usuario cuyo cronTab estamos editando. Afortunadamente, PHP nos proporciona una forma sencilla de hacerlo a través de la biblioteca SSH2. Puede o no tener esta biblioteca instalada, de modo que, si no lo hace, querrá instalarla:
Instalación / Configuración de PHP libssh2
Comenzaremos nuestra clase declarando cuatro propiedades, todas las cuales serán privadas:
- $connection representa nuestra conexión / recurso.
- $path representará la ruta para ese archivo.
- $handle representará el nombre de nuestro archivo cron temporal.
- $cron_file representa la ruta completa y el nombre del archivo cron temporal.
Nuestra clase debe ser capaz de conectarse y autenticarse, como un usuario apropiado, para ejecutar comandos y tener acceso al cronTab de ese usuario. Por lo tanto, estableceremos una conexión SSH2 y la autenticaremos dentro del propio constructor.
Con
una conexión autenticada en su lugar, necesitaremos un método para
manejar la ejecución de los diversos comandos que ejecutaremos. Llamaremos a este método exec(). En
general, llamaremos a este método desde los otros métodos de nuestra
clase cuando necesitemos ejecutar un comando en el servidor remoto.
A
continuación, necesitaremos la capacidad de escribir el cronTab en un
archivo para que tengamos acceso tangible a él. También necesitaremos
una forma de eliminar este archivo cuando hayamos terminado con él. Definamos
el método que maneja la salida de cronTab a un archivo como
write_to_file() y el método para eliminar este archivo como remove_file().
Por supuesto, también necesitaremos una forma de crear y eliminar
trabajos cron. Entonces, definiremos un método append_cronjob() y
remove_cronjob(), respectivamente.
En
el caso de que hayamos eliminado el único / último cronjob, querremos
la capacidad de eliminar todo el cronTab en lugar de tratar de crear un
cronTab vacío. Usaremos el método remove_crontab() para manejar
esto.
Por último, crearemos dos métodos de ayuda para nuestra clase. El
primero de estos métodos, que devolverá un valor booleano, simplemente
comprobará la existencia del archivo cron temporal. Este último se usará
para mostrar mensajes de error en caso de error. Llamaremos a este
método crontab_file_exists() y error_message(), respectivamente.
1 |
<?php
|
2 |
|
3 |
Class Ssh2_crontab_manager { |
4 |
|
5 |
private $connection; |
6 |
private $path; |
7 |
private $handle; |
8 |
private $cron_file; |
9 |
|
10 |
function __construct() {...} |
11 |
|
12 |
public function exec() {...} |
13 |
|
14 |
public function write_to_file() {...} |
15 |
|
16 |
public function remove_file() {...} |
17 |
|
18 |
public function append_cronjob() {...} |
19 |
|
20 |
public function remove_cronjob() {...} |
21 |
|
22 |
public function remove_crontab() {...} |
23 |
|
24 |
private function crontab_file_exists() {...} |
25 |
|
26 |
private function error_message() {...} |
27 |
|
28 |
}
|
Paso 2 - ¡El Constructor!
El constructor de clase será principalmente responsable de establecer y
autenticar la conexión SSH2. Tomará cuatro argumentos, cada uno de los
cuales tendrá un valor predeterminado de NULL:
- $host: representa la dirección IP del servidor remoto al que queremos conectarnos.
- $port: es el puerto que se utilizará para la conexión SSH2.
- $username: representará el nombre de inicio de sesión del usuario para el servidor.
- $password: representa la contraseña del usuario para el servidor.
1 |
function __construct($host=NULL, $port=NULL, $username=NULL, $password=NULL) {...} |
Dentro
del propio constructor, la primera propiedad que definiremos es $this->path, que representará un 'directorio predeterminado' para
nuestros archivos cron temporales.
Idealmente,
simplemente usaríamos la constante mágica de PHP __DIR__ para definir
esta propiedad como el directorio de trabajo actual. Sin embargo, hay
ocasiones en que esta constante no puede definirse. Como tal,
comprobaremos si __DIR__ está definido.
Si no es así, tendremos que
obtener el directorio de trabajo actual manualmente. Lo
haremos utilizando una combinación de las funciones strrpos() y substr() junto con la constante __FILE__ que representa la ruta completa y el
nombre del archivo actual.
Utilizaremos
strrpos(), que devuelve la posición de la última aparición de una
subcadena, para encontrar la posición de la última barra inclinada en la
cadena __FILE__. Este
valor, que almacenaremos como var $path_length, nos dará el número de
caracteres hasta, pero sin incluir, la última barra diagonal.
La
función substr() ordenará 'empalmar' una cadena en el sentido de que
devuelve una porción específica de una cadena en función de la posición
de inicio del empalme y la cantidad de caracteres que queremos
devolver.
Pasaremos tres argumentos a la función substr()
- la cadena con la que estamos trabajando
- la ubicación de inicio del empalme, en
este caso
0 - la ubicación final del empalme que está representado por
la variable
$path_length.
A continuación, concatenaremos una barra inclinada hacia el final de esta cadena que nos proporcionará el directorio de trabajo actual.
1 |
function __construct($host=NULL, $port=NULL, $username=NULL, $password=NULL) |
2 |
{
|
3 |
$path_length = strrpos(__FILE__, "/"); |
4 |
$this->path = substr(__FILE__, 0, $path_length) . '/'; |
5 |
}
|
Ahora,
definiremos un nombre de archivo predeterminado para el archivo cron
temporal como $this->handle y luego concatenaremos la ruta y
manejaremos props juntos como $this->cron_file. Este accesorio representará la ruta y el nombre de archivo predeterminados completos para el archivo cron temporal.
1 |
function __construct($host=NULL, $port=NULL, $username=NULL, $password=NULL) |
2 |
{
|
3 |
$path_length = strrpos(__FILE__, "/"); |
4 |
$this->path = substr(__FILE__, 0, $path_length) . '/'; |
5 |
$this->handle = 'crontab.txt'; |
6 |
$this->cron_file = "{$this->path}{$this->handle}"; |
7 |
}
|
Con estas propiedades definidas, ahora trabajaremos para establecer una conexión con el servidor y autenticarlo. Agregaremos un manejo de error básico a nuestra clase al estar envuelto el siguiente código en un bloque try / catch. De esta manera, podemos detectar errores y lanzar excepciones para proporcionar al usuario comentarios muy específicos.
Dentro
del bloque try, primero verificaremos que todos los argumentos hayan
sido definidos usando la función is_null() que devolverá verdadero o
falso. Si alguno de estos argumentos es NULL, lanzaremos una nueva
excepción con un mensaje apropiado.
1 |
function __construct($host=NULL, $port=NULL, $username=NULL, $password=NULL) |
2 |
{
|
3 |
$path_length = strrpos(__FILE__, "/"); |
4 |
$this->path = substr(__FILE__, 0, $path_length) . '/'; |
5 |
$this->handle = 'crontab.txt'; |
6 |
$this->cron_file = "{$this->path}{$this->handle}"; |
7 |
|
8 |
try
|
9 |
{
|
10 |
if (is_null($host) || is_null($port) || is_null($username) || is_null($password)) throw new Exception("Please specify the host, port, username and password!"); |
11 |
}
|
12 |
catch
|
13 |
{
|
14 |
|
15 |
}
|
16 |
}
|
A
continuación, definiremos $ this-> connection pasando los argumentos $host y $port a la función ssh2_connect() que establecerá una conexión
remota y devolverá ese recurso o FALSE si la conexión falla.
Como
usamos nuestro propio manejo de errores, también usaremos el operador
de control de errores @ que suprimirá cualquier mensaje de error para
esta función. Si la conexión no es exitosa, lanzaremos una nueva excepción con un mensaje apropiado en consecuencia.
1 |
function __construct($host=NULL, $port=NULL, $username=NULL, $password=NULL) |
2 |
{
|
3 |
$path_length = strrpos(__FILE__, "/"); |
4 |
$this->path = substr(__FILE__, 0, $path_length) . '/'; |
5 |
$this->handle = 'crontab.txt'; |
6 |
$this->cron_file = "{$this->path}{$this->handle}"; |
7 |
|
8 |
try
|
9 |
{
|
10 |
if (is_null($host) || is_null($port) || is_null($username) || is_null($password)) throw new Exception("Please specify the host, port, username and password!"); |
11 |
|
12 |
$this->connection = @ssh2_connect($host, $port); |
13 |
if ( ! $this->connection) throw new Exception("The SSH2 connection could not be established."); |
14 |
}
|
15 |
catch
|
16 |
{
|
17 |
|
18 |
}
|
19 |
}
|
Ahora
intentaremos autenticar / iniciar sesión utilizando la función
ssh2_auth_password() que pasa el recurso devuelto por nuestra conexión,
así como el nombre de usuario y la contraseña del usuario que estamos
ingresando. ssh2_auth_password() devolverá un booleano que representa el estado de
la autenticación mediante el cual podemos lanzar una nueva excepción si
falla la autenticación.
1 |
function __construct($host=NULL, $port=NULL, $username=NULL, $password=NULL) |
2 |
{
|
3 |
$path_length = strrpos(__FILE__, "/"); |
4 |
$this->path = substr(__FILE__, 0, $path_length) . '/'; |
5 |
$this->handle = 'crontab.txt'; |
6 |
$this->cron_file = "{$this->path}{$this->handle}"; |
7 |
|
8 |
try
|
9 |
{
|
10 |
if (is_null($host) || is_null($port) || is_null($username) || is_null($password)) throw new Exception("Please specify the host, port, username and password!"); |
11 |
|
12 |
$this->connection = @ssh2_connect($host, $port); |
13 |
if ( ! $this->connection) throw new Exception("The SSH2 connection could not be established."); |
14 |
|
15 |
$authentication = @ssh2_auth_password($this->connection, $username, $password); |
16 |
if ( ! $authentication) throw new Exception("Could not authenticate '{$username}' using password: '{$password}'."); |
17 |
}
|
18 |
catch
|
19 |
{
|
20 |
|
21 |
}
|
22 |
}
|
PHP intentará encontrar un bloque de catch correspondiente para cada excepción que se lanzó, y luego crear / pasar un objeto de excepción, que contiene un número de propiedades útiles, a este bloque.
Entonces,
en el bloque catch, usaremos el método getMessage() del objeto de
excepción para acceder y mostrar el mensaje definido en la
excepción.
Notarás que mostraremos el mensaje llamando al método
error_message() de nuestra clase. Aunque este método aún no está
definido, simplemente mostrará los mensajes de error de una manera
ordenada.
1 |
function __construct($host=NULL, $port=NULL, $username=NULL, $password=NULL) |
2 |
{
|
3 |
$path_length = strrpos(__FILE__, "/"); |
4 |
$this->path = substr(__FILE__, 0, $path_length) . '/'; |
5 |
$this->handle = 'crontab.txt'; |
6 |
$this->cron_file = "{$this->path}{$this->handle}"; |
7 |
|
8 |
try
|
9 |
{
|
10 |
if (is_null($host) || is_null($port) || is_null($username) || is_null($password)) throw new Exception("Please specify the host, port, username and password!"); |
11 |
|
12 |
$this->connection = @ssh2_connect($host, $port); |
13 |
if ( ! $this->connection) throw new Exception("The SSH2 connection could not be established."); |
14 |
|
15 |
$authentication = @ssh2_auth_password($this->connection, $username, $password); |
16 |
if ( ! $authentication) throw new Exception("Could not authenticate '{$username}' using password: '{$password}'."); |
17 |
}
|
18 |
catch (Exception $e) |
19 |
{
|
20 |
$this->error_message($e->getMessage()); |
21 |
}
|
22 |
}
|
Paso 3 - Ejecución de múltiples comandos
Este método será responsable de ejecutar comandos en el servidor remoto. Es comparable a ingresar comandos manualmente en un shell como, por
ejemplo, PuTTY. Para
permitir una mayor flexibilidad, haremos que este método sea público
para que los usuarios puedan ejecutar cualquier otro comando que
necesiten ejecutar. También permitiremos cualquier número de argumentos
siempre que se especifique al menos uno. Estos argumentos representarán
los comandos que queremos ejecutar usando la función ssh2_exec().
Entonces,
lo primero que haremos en este método es definir una variable que
represente un recuento de la cantidad total de argumentos pasados. Utilizaremos la función func_num_args() de PHP para obtener este
conteo.
1 |
public function exec() |
2 |
{
|
3 |
$argument_count = func_num_args(); |
4 |
}
|
Ahora, dentro de un bloque de prueba, comprobaremos si hemos pasado o no argumentos a este método. Si el recuento de argumentos es 0, lanzaremos una nueva excepción con un mensaje apropiado.
1 |
public function exec() |
2 |
{
|
3 |
$argument_count = func_num_args(); |
4 |
|
5 |
try
|
6 |
{
|
7 |
if ( ! $argument_count) throw new Exception("There is nothing to execute, no arguments specified."); |
8 |
}
|
9 |
catch
|
10 |
{
|
11 |
|
12 |
}
|
13 |
}
|
A
continuación, utilizando la función func_get_args() crearemos una
matriz de todos los argumentos que se pasaron a este método.
Usando
un operador ternario, luego definiremos una nueva variable, $command_string, como una representación de cadena de línea única de los
comandos de Linux reales que ejecutaremos.
Si
tenemos varios comandos para ejecutar, usaremos la función implode()
de PHP para analizar la matriz de argumentos en una cadena. Pasaremos
dos argumentos para implode (), el pegamento o separador, que es
básicamente una cadena que se insertará entre los elementos de la matriz
y la matriz misma. Separaremos cada elemento con
el operador de Linux, && ¡lo que nos permitirá ejecutar
múltiples comandos secuencialmente en una línea!
Por
el contrario, si solo hay un comando, definiremos el comando string
como $arguments[0] que representa el primer y único argumento /
comando.
1 |
public function exec() |
2 |
{
|
3 |
$argument_count = func_num_args(); |
4 |
|
5 |
try
|
6 |
{
|
7 |
if ( ! $argument_count) throw new Exception("There is nothing to execute, no arguments specified."); |
8 |
|
9 |
$arguments = func_get_args(); |
10 |
|
11 |
$command_string = ($argument_count > 1) ? implode(" && ", $arguments) : $arguments[0]; |
12 |
|
13 |
}
|
14 |
catch
|
15 |
{
|
16 |
|
17 |
}
|
18 |
}
|
Con
nuestros comandos listos y analizados como una cadena, ahora podemos
intentar ejecutarlos utilizando la función ssh2_exec(). Pasaremos
la identificación del enlace de conexión, $this->connection, así
como nuestra $command_string a esta función que devolverá una secuencia
en caso de éxito o falsa en caso de error.
Los flujos se definen como un objeto de recurso que exhibe un comportamiento transmisible... que puede leerse o escribirse de forma lineal.
Utilizaremos el operador de control de
errores @ aquí, nuevamente, para suprimir cualquier mensaje de error, ya
que lanzaremos nuestra propia excepción, con un mensaje apropiado, en
caso de que ocurra un error.
1 |
public function exec() |
2 |
{
|
3 |
$argument_count = func_num_args(); |
4 |
|
5 |
try
|
6 |
{
|
7 |
if ( ! $argument_count) throw new Exception("There is nothing to execute, no arguments specified."); |
8 |
|
9 |
$arguments = func_get_args(); |
10 |
|
11 |
$command_string = ($argument_count > 1) ? implode(" && ", $arguments) : $arguments[0]; |
12 |
|
13 |
$stream = @ssh2_exec($this->connection, $command_string); |
14 |
if ( ! $stream) throw new Exception("Unable to execute the specified commands: <br />{$command_string}"); |
15 |
|
16 |
}
|
17 |
catch
|
18 |
{
|
19 |
|
20 |
}
|
21 |
}
|
¡Eso es todo para el bloque try! Dentro
del bloque catch, simplemente llamaremos al método error_message() para mostrar cualquier mensaje de error a nuestro usuario. Con
los bloques try y catch ahora completos, devolveremos $this al final
del método exec() que hará que este método se pueda encadenar.
1 |
public function exec() |
2 |
{
|
3 |
$argument_count = func_num_args(); |
4 |
|
5 |
try
|
6 |
{
|
7 |
if ( ! $argument_count) throw new Exception("There is nothing to execute, no arguments specified."); |
8 |
|
9 |
$arguments = func_get_args(); |
10 |
|
11 |
$command_string = ($argument_count > 1) ? implode(" && ", $arguments) : $arguments[0]; |
12 |
|
13 |
$stream = @ssh2_exec($this->connection, $command_string); |
14 |
if ( ! $stream) throw new Exception("Unable to execute the specified commands: <br />{$command_string}"); |
15 |
|
16 |
}
|
17 |
catch
|
18 |
{
|
19 |
$this->error_message($e->getMessage()); |
20 |
}
|
21 |
|
22 |
return $this; |
23 |
}
|
Paso 4: Escribir CronTab en un archivo
El
siguiente método, write_to_file(), será responsable de escribir el
cronTab existente en un archivo temporal o crear un temp en
blanco. archivo no debería existir trabajos cron. Tomará dos
argumentos
- la ruta del archivo temporal que crearemos
- el nombre que debemos usar al crearlo.
Continuando
con la lógica de nuestros métodos de constructor y exec, estableceremos
los valores predeterminados para estos argumentos como NULL.
1 |
public function write_to_file($path=NULL, $handle=NULL) |
2 |
{
|
3 |
|
4 |
}
|
Lo primero
que haremos aquí es verificar si el archivo cron ya existe utilizando el
método booleano crontab_file_exists(), que crearemos en breve. Si el archivo existe, no hay necesidad de continuar. Si no lo hace,
utilizaremos un operador ternario para verificar los apoyos $path y $handle para determinar si son NULL o no. Si cualquiera de ellos es NULL,
usaremos los repliegues predefinidos de nuestro constructor para
definirlos.
Luego, concatenaremos estas propiedades juntas en una nueva propiedad que representará la ruta completa y el nombre de archivo para el archivo cron temporal.
1 |
public function write_to_file($path=NULL, $handle=NULL) |
2 |
{
|
3 |
if ( ! $this->crontab_file_exists()) |
4 |
{
|
5 |
$this->handle = (is_null($handle)) ? $this->handle : $handle; |
6 |
$this->path = (is_null($path)) ? $this->path : $path; |
7 |
|
8 |
$this->cron_file = "{$this->path}{$this->handle}"; |
9 |
}
|
10 |
}
|
A
continuación, utilizaremos el comando crontab de Linux, con el argumento
-l establecido, para mostrar los usuarios cronTab como salida estándar. Luego,
usando el operador > de Linux, redirigiremos la salida estándar, o
STDOUT, a nuestro archivo cron temporal en lugar de concatenar $this->cron_file en la cadena de comandos. Redirigir la salida a un
archivo, utilizando el operador >, siempre creará ese archivo si no
existe.
1 |
public function write_to_file($path=NULL, $handle=NULL) |
2 |
{
|
3 |
if ( ! $this->crontab_file_exists()) |
4 |
{
|
5 |
$this->handle = (is_null($handle)) ? $this->handle : $handle; |
6 |
$this->path = (is_null($path)) ? $this->path : $path; |
7 |
|
8 |
$this->cron_file = "{$this->path}{$this->handle}"; |
9 |
|
10 |
$init_cron = "crontab -l > {$this->cron_file}"; |
11 |
}
|
12 |
}
|
Esto funciona muy bien, pero solo si ya hay trabajos programados dentro de cronTab. Sin embargo, si no hay trabajos cron, ¡este archivo nunca se creará! Sin
embargo, si usamos el operador &&, podemos agregar comandos /
expresiones adicionales a esta cadena. Entonces, agreguemos una
expresión condicional para verificar que el archivo cron exista. Si el
archivo no existe, esta expresión se evaluará como falsa. Como tal,
podemos usar el || o operador "o" después de esta condición para crear
un nuevo archivo en blanco si es necesario.
Los comandos colocados
después de "o" se ejecutarán si las condiciones / condiciones se evalúan
como falsas. Ahora,
al usar el operador > otra vez, esta vez sin precederlo por un
comando específico, ¡podemos crear un nuevo archivo en blanco! Entonces,
esencialmente, esta cadena de comandos dará salida a cronTab a un
archivo, luego verifica si ese archivo existe, lo que indicaría que hay
entradas en el cronTab y luego creará un nuevo archivo en blanco si no
existe ya.
1 |
public function write_to_file($path=NULL, $handle=NULL) |
2 |
{
|
3 |
if ( ! $this->crontab_file_exists()) |
4 |
{
|
5 |
$this->handle = (is_null($handle)) ? $this->handle : $handle; |
6 |
$this->path = (is_null($path)) ? $this->path : $path; |
7 |
|
8 |
$this->cron_file = "{$this->path}{$this->handle}"; |
9 |
|
10 |
$init_cron = "crontab -l > {$this->cron_file} && [ -f {$this->cron_file} ] || > {$this->cron_file}"; |
11 |
}
|
12 |
}
|
Por último, llamaremos al método exec() y le pasaremos la cadena de comando como único argumento. Luego, para que este método también sea encadenable, devolveremos $this.
1 |
public function write_to_file($path=NULL, $handle=NULL) |
2 |
{
|
3 |
if ( ! $this->crontab_file_exists()) |
4 |
{
|
5 |
$this->handle = (is_null($handle)) ? $this->handle : $handle; |
6 |
$this->path = (is_null($path)) ? $this->path : $path; |
7 |
|
8 |
$this->cron_file = "{$this->path}{$this->handle}"; |
9 |
|
10 |
$init_cron = "crontab -l > {$this->cron_file} && [ -f {$this->cron_file} ] || > {$this->cron_file}"; |
11 |
|
12 |
$this->exec($init_cron); |
13 |
}
|
14 |
|
15 |
return $this; |
16 |
}
|
Paso 5 - Eliminar el archivo temporal Cron
Este método, remove_file() es tan fácil como fácil. Utilizaremos
nuestro método de ayuda, crontab_file_exists(), para verificar la
existencia del archivo cron temporal y luego ejecutaremos el comando de
Linux rm para eliminarlo si lo hace. Como de costumbre, también devolveremos $this para mantener el asociacion.
1 |
public function remove_file() |
2 |
{
|
3 |
if ($this->crontab_file_exists()) $this->exec("rm {$this->cron_file}"); |
4 |
|
5 |
return $this; |
6 |
}
|
Paso 6: creación de nuevos trabajos de cron
Este
método creará nuevos trabajos cron a través de la adición de nuevos
trabajos / líneas al archivo temporal cron y luego la ejecución del
comando crontab en ese archivo que instalará todos esos trabajos como un
nuevo cronTab. Como tal, append_cronjob() tomará
un argumento, $cron_jobs, que será una cadena o una matriz de cadenas
que representan los trabajos cron para anexar.
Comenzaremos este método determinando si el argumento $cron_jobs es NULL. Si
es así, llamaremos al método error_message() para detener cualquier
ejecución posterior y mostrar un mensaje de error al usuario.
1 |
public function append_cronjob($cron_jobs=NULL) |
2 |
{
|
3 |
if (is_null($cron_jobs)) $this->error_message("Nothing to append! Please specify a cron job or an array of cron jobs."); |
4 |
|
5 |
}
|
Básicamente, podemos hacer eco de nuestra tarea en el archivo cron redirigiendo de nuevo la salida estándar al archivo.
A
continuación, definiremos una nueva variable, $append_cronfile, como
una cadena, con el texto "echo" seguido de un espacio y una comilla
simple al final. Llenaremos esta cadena con los diversos trabajos de
cron que estamos agregando, así como la cita de cierre, momentáneamente. Construiremos esta cadena usando el operador de concatenación de
cadenas .=.
Usando un operador ternario, determinaremos si $cron_jobs es
una matriz o no. Si
es así, implotaremos esa matriz con nuevas líneas, \n, de modo que
cada trabajo cron se escriba en su propia línea dentro del archivo cron. Si
el argumento $cron_jobs no es una matriz, simplemente concatenaremos
ese trabajo en la cadena $append_cron sin ningún procesamiento
especial. De esta manera, tendremos una cadena con el formato adecuado
independientemente de si estamos trabajando con una matriz o
no.
Básicamente, podemos hacer eco de nuestra tarea en el archivo cron
redirigiendo de nuevo la salida estándar al archivo. Entonces,
usando el operador de concatenación de cadenas, añadiremos la comilla
simple de cierre a la cadena de comandos así como también al operador
Linux >> seguido por la ruta completa y el nombre de archivo para
el archivo cron. El
operador >>, a diferencia del operador > que siempre
sobrescribe un archivo, agrega la salida al final de un archivo. Entonces, al usar este operador, no sobrescribiremos ningún trabajo cron
existente.
1 |
public function append_cronjob($cron_jobs=NULL) |
2 |
{
|
3 |
if (is_null($cron_jobs)) $this->error_message("Nothing to append! Please specify a cron job or an array of cron jobs."); |
4 |
|
5 |
$append_cronfile = "echo '"; |
6 |
|
7 |
$append_cronfile .= (is_array($cron_jobs)) ? implode("\n", $cron_jobs) : $cron_jobs; |
8 |
|
9 |
$append_cronfile .= "' >> {$this->cron_file}"; |
10 |
|
11 |
}
|
Ahora definiremos una variable, como una cadena, con el comando que vamos a usar para instalar el nuevo archivo cron que estamos a punto de crear. Esto es tan simple como llamar al comando crontab seguido de la ruta y el nombre del archivo cron.
Sin
embargo, antes de ejecutar estos comandos a través del método exec (),
primero llamaremos al método write_to_file () para crear el archivo cron
temporal. Luego, dentro de una cadena,
ejecutaremos estos comandos y llamaremos al método remove_file () para
eliminar el archivo temporal. Por último, devolveremos return $this para que el método append_cronjob() sea encadenable.
1 |
public function append_cronjob($cron_jobs=NULL) |
2 |
{
|
3 |
if (is_null($cron_jobs)) $this->error_message("Nothing to append! Please specify a cron job or an array of cron jobs."); |
4 |
|
5 |
$append_cronfile = "echo '"; |
6 |
|
7 |
$append_cronfile .= (is_array($cron_jobs)) ? implode("\n", $cron_jobs) : $cron_jobs; |
8 |
|
9 |
$append_cronfile .= "' >> {$this->cron_file}"; |
10 |
|
11 |
$install_cron = "crontab {$this->cron_file}"; |
12 |
|
13 |
$this->write_to_file()->exec($append_cronfile, $install_cron)->remove_file(); |
14 |
|
15 |
return $this; |
16 |
}
|
Paso 7: eliminación de trabajos existentes de cron
Ahora que podemos crear nuevos trabajos cron, ¡es lógico que también
podamos eliminarlos! El método remove_cronjob() tomará un argumento que
será una expresión regular (simple). Este regEx se usará para encontrar
trabajos coincidentes dentro de cronTab y eliminarlos en consecuencia. Al
igual que con el método append_cronjob(), lo primero que haremos es
verificar si el argumento $cron_jobs es NULL y detener la ejecución si
es así. De lo contrario, llamaremos al método create_file() para
escribir la pestaña cron en un archivo.
1 |
public function remove_cronjob($cron_jobs=NULL) |
2 |
{
|
3 |
if (is_null($cron_jobs)) $this->error_message("Nothing to remove! Please specify a cron job or an array of cron jobs."); |
4 |
|
5 |
$this->write_to_file(); |
6 |
|
7 |
}
|
Con el archivo cron creado, ahora lo leeremos en una matriz usando la función file() de PHP. Esta función analizará un archivo dado en una matriz con cada línea como
un elemento de matriz. Pasaremos
nuestro archivo cron a esta función como primer argumento y luego
estableceremos un indicador especial, FILE_IGNORE_NEW_LINES, que
obligará a file() a ignorar todas las líneas nuevas. Por lo tanto,
tendremos una matriz con solo los trabajos de cron.
Si no hay trabajos
cron programados, esta matriz estará vacía. Posteriormente, no habrá
razón para continuar. Por lo tanto, verificaremos si $cron_array está
vacío y deteniremos la ejecución si es así.
Si cronTab no está vacío,
contaremos los elementos en $cron_array utilizando la función count ()
de PHP. Almacenaremos este valor como $original_count. Lo usaremos, en
breve, para determinar si hemos eliminado cualquier tarea cron de $cron_array.
1 |
public function remove_cronjob($cron_jobs=NULL) |
2 |
{
|
3 |
if (is_null($cron_jobs)) $this->error_message("Nothing to remove! Please specify a cron job or an array of cron jobs."); |
4 |
|
5 |
$this->write_to_file(); |
6 |
|
7 |
$cron_array = file($this->cron_file, FILE_IGNORE_NEW_LINES); |
8 |
|
9 |
if (empty($cron_array)) $this->error_message("Nothing to remove! The cronTab is already empty."); |
10 |
|
11 |
$original_count = count($cron_array); |
12 |
|
13 |
}
|
Ahora, determinaremos si el argumento $cron_jobs es una matriz o no. Si se trata de una matriz, la recorreremos con un ciclo foreach. Dentro
de ese bucle solo ejecutaremos una función, preg_grep(). Esta
ingeniosa función, a diferencia de preg_match(), devolverá una matriz
de todos los elementos de la matriz que coincidan con la expresión
regular especificada. En este caso, sin embargo, queremos que los
elementos de la matriz no coincidan. En
otras palabras, necesitamos una matriz de todos los trabajos cron que
vamos a mantener para que podamos inicializar cronTab con solo estos
trabajos. Como tal, estableceremos un indicador
especial aquí, PREG_GREP_INVERT, que hará que preg_grep() devuelva una
matriz de todos los elementos que no coinciden con la expresión regular. Por lo tanto, tendremos una variedad de todos los trabajos cron que
queremos mantener.
1 |
public function remove_cronjob($cron_jobs=NULL) |
2 |
{
|
3 |
if (is_null($cron_jobs)) $this->error_message("Nothing to remove! Please specify a cron job or an array of cron jobs."); |
4 |
|
5 |
$this->write_to_file(); |
6 |
|
7 |
$cron_array = file($this->cron_file, FILE_IGNORE_NEW_LINES); |
8 |
|
9 |
if (empty($cron_array)) $this->error_message("Nothing to remove! The cronTab is already empty."); |
10 |
|
11 |
$original_count = count($cron_array); |
12 |
|
13 |
if (is_array($cron_jobs)) |
14 |
{
|
15 |
foreach ($cron_jobs as $cron_regex) $cron_array = preg_grep($cron_regex, $cron_array, PREG_GREP_INVERT); |
16 |
}
|
17 |
else
|
18 |
{
|
19 |
|
20 |
}
|
21 |
}
|
Si el argumento $cron_jobs no es una matriz, procederemos de la misma manera pero sin ninguna iteración. De
nuevo, redefiniremos $cron_array como la matriz resultante de la
función preg_grep() con el indicador PREG_GREP_INVERT establecido.
1 |
public function remove_cronjob($cron_jobs=NULL) |
2 |
{
|
3 |
if (is_null($cron_jobs)) $this->error_message("Nothing to remove! Please specify a cron job or an array of cron jobs."); |
4 |
|
5 |
$this->write_to_file(); |
6 |
|
7 |
$cron_array = file($this->cron_file, FILE_IGNORE_NEW_LINES); |
8 |
|
9 |
if (empty($cron_array)) $this->error_message("Nothing to remove! The cronTab is already empty."); |
10 |
|
11 |
$original_count = count($cron_array); |
12 |
|
13 |
if (is_array($cron_jobs)) |
14 |
{
|
15 |
foreach ($cron_jobs as $cron_regex) $cron_array = preg_grep($cron_regex, $cron_array, PREG_GREP_INVERT); |
16 |
}
|
17 |
else
|
18 |
{
|
19 |
$cron_array = preg_grep($cron_jobs, $cron_array, PREG_GREP_INVERT); |
20 |
}
|
21 |
}
|
Con nuestro
conjunto $cron_array, ahora, compararemos la longitud actual de esta
matriz con su longitud original que almacenamos en caché en la variable $original_count. Si las longitudes son idénticas, simplemente devolveremos el método
remove_file() para eliminar el archivo cron temporal. Si no coinciden,
eliminaremos el cronTab existente y luego instalaremos el nuevo.
Los
métodos remove_file(), remove_crontab() y append_cronjob() devuelven
$this, por lo que al devolver estos métodos seguimos conservando que estos se pueda encandenar.
1 |
public function remove_cronjob($cron_jobs=NULL) |
2 |
{
|
3 |
if (is_null($cron_jobs)) $this->error_message("Nothing to remove! Please specify a cron job or an array of cron jobs."); |
4 |
|
5 |
$this->write_to_file(); |
6 |
|
7 |
$cron_array = file($this->cron_file, FILE_IGNORE_NEW_LINES); |
8 |
|
9 |
if (empty($cron_array)) $this->error_message("Nothing to remove! The cronTab is already empty."); |
10 |
|
11 |
$original_count = count($cron_array); |
12 |
|
13 |
if (is_array($cron_jobs)) |
14 |
{
|
15 |
foreach ($cron_jobs as $cron_regex) $cron_array = preg_grep($cron_regex, $cron_array, PREG_GREP_INVERT); |
16 |
}
|
17 |
else
|
18 |
{
|
19 |
$cron_array = preg_grep($cron_jobs, $cron_array, PREG_GREP_INVERT); |
20 |
}
|
21 |
|
22 |
return ($original_count === count($cron_array)) ? $this->remove_file() : $this->remove_crontab()->append_cronjob($cron_array); |
23 |
}
|
Paso 8 - Eliminando todo el Crontab
La eliminación de todo cronTab es relativamente simple de hacer. Básicamente, ejecutaremos el comando crontab con el conjunto de banderas
-r que elimina todo el cronTab para un usuario dado. Dado que el
crontab se ha eliminado, también podríamos eliminar el archivo cron
temporal, en caso de que exista. Luego devolveremos return $this para
preservar que se puedan encadenar.
1 |
public function remove_crontab() |
2 |
{
|
3 |
$this->exec("crontab -r")->remove_file(); |
4 |
|
5 |
return $this; |
6 |
}
|
Paso 9 - Algunos métodos útiles
Con
la peor parte de nuestra clase de administración de cron escrita, ahora
vamos a echar un vistazo a los dos métodos pequeños pero útiles que
hemos utilizado en toda nuestra clase, crontab_file_exists() y
error_message().
- $this->crontab_file_exists()
Este método simplemente devolverá el resultado del método
file_exists()de PHP, verdadero o falso, dependiendo de si el archivo cron temporal existe o no.
1 |
private function crontab_file_exists() |
2 |
{
|
3 |
return file_exists($this->cron_file); |
4 |
}
|
- $this->error_message($error)
Este método tomará un argumento, una cadena, que representa el mensaje de error que queremos mostrar. Luego llamaremos al método
die()de PHP para detener la ejecución y mostrar este mensaje. La cadena en sí, se concatenará en un elemento<pre>con un estilo simple aplicado.
1 |
private function error_message($error) |
2 |
{
|
3 |
die("<pre style='color:#EE2711'>ERROR: {$error}</pre>"); |
4 |
}
|
Paso 10 - ¡Poniéndolo todo junto!
Ahora que hemos completado nuestra clase de administración de cron, ¡echemos un vistazo a algunos ejemplos de cómo usarla!
- Instanciando
la clase y estableciendo una conexión autenticada:
Primero, creemos una nueva instancia de nuestra clase. Recuerde, tendremos que pasar la dirección IP, el puerto, el nombre de usuario y la contraseña al constructor de la clase.
1 |
$crontab = new Ssh2_crontab_manager('11.11.111.111', '22', 'my_username', 'my_password'); |
- Anexando un único trabajo cron:
Con una conexión autenticada en su lugar, echemos un vistazo a cómo podemos crear un nuevo trabajo cron único.
1 |
$crontab = new Ssh2_crontab_manager('11.11.111.111', '22', 'my_username', 'my_password'); |
2 |
$crontab->append_cronjob('30 8 * * 6 home/path/to/command/the_command.sh >/dev/null 2>&1'); |
- Anexar una matriz de trabajos cron:
Agregar varios trabajos cron es tan fácil como anexar un solo trabajo cron. Simplemente pasaremos una matriz al método
append_cronjob().
1 |
$crontab = new Ssh2_crontab_manager('11.11.111.111', '22', 'my_username', 'my_password'); |
2 |
|
3 |
$new_cronjobs = array( |
4 |
'0 0 1 * * home/path/to/command/the_command.sh', |
5 |
'30 8 * * 6 home/path/to/command/the_command.sh >/dev/null 2>&1'
|
6 |
);
|
7 |
|
8 |
$crontab->append_cronjob($new_cronjobs); |
- Eliminar un solo trabajo cron:
De manera similar a cómo creamos un único trabajo cron, ahora eliminaremos uno. Esta vez, sin embargo, utilizaremos una expresión regular para encontrar la tarea adecuada. Este regEx puede ser tan simple o tan complejo como lo necesite. De hecho, hay una serie de formas de regex para la tarea que está buscando. Por ejemplo, si la tarea que necesita eliminar es única en el sentido de que el comando que se está ejecutando no se utiliza en ningún otro lugar en el cronTab, puede especificar simplemente el nombre del comando como su regla. Además, si quisiera eliminar todas las tareas para un mes determinado, ¡simplemente podría escribir una expresión regular para encontrar una coincidencia para todos los trabajos de un mes determinado!
1 |
$crontab = new Ssh2_crontab_manager('11.11.111.111', '22', 'my_username', 'my_password'); |
2 |
|
3 |
$cron_regex = '/home\/path\/to\/command\/the_command\.sh\/'; |
4 |
|
5 |
$crontab->remove_cronjob($cron_regex); |
- Eliminar una matriz de trabajos cron:
La eliminación de múltiples trabajos cron se maneja de la misma manera que cuando se elimina un solo cronJob con una sola excepción, pasaremos una matriz de expresiones regulares de trabajos cron al método
remove_cronjob().
1 |
$crontab = new Ssh2_crontab_manager('11.11.111.111', '22', 'my_username', 'my_password'); |
2 |
|
3 |
$cron_regex = array( |
4 |
'/0 0 1 \* \*/', |
5 |
'/home\/path\/to\/command\/the_command\.sh\/'
|
6 |
);
|
7 |
|
8 |
$crontab->remove_cronjob($cron_regex); |
Conclusión
¡Eso es todo amigos! Espero que hayan disfrutado leyendo este artículo tanto como yo he disfrutado escribiéndolo y que hayan obtenido nuevos conocimientos sobre cronTab y cómo administrarlo con PHP. ¡Muchas Gracias Por Leer!



