1. Code
  2. PHP

Administrar trabajos Cron con PHP

seguido de la ruta completa y el comando para ejecutar: * * * * * 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:
Scroll to top

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!