Advertisement
  1. Code
  2. PHP

Управління Cron з PHP

Scroll to top
Read Time: 21 min

Ukrainian (українська мова) translation by Andy Yur (you can also view the original English article)

Для тих, хто не в курсі, cronTab, або "CronTable", - це спеціальний інструмент, доступний на Linux, який полегшує планування повторних завдань, таким чином, звільняє нас від «мавпячої роботи».

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

Лікнеп - Короткий огляд Crontab

Я думаю, ніхто не буде сперечатися з тим, що можливість автоматизувати якісь процеси - це дуже крута і корисна річ! Список наших можливостей прагне до нескінченності: ми можемо автоматизувати підтримку бази даних SQL, відправляти електронні листи передплатникам, управляти різними завданнями, аналізувати роботу і продуктивність нашого сайту, грабастать RSS стрічки - cron просто чудовий!

Незважаючи на те що на перший погляд синтаксис планування нового завдання cron може не на жарт спантеличити новачка, з часом і практикою все стане досить-таки просто. Якщо коротко, то у cron завдання є п'ять колонок, кожна з яких представляє хронологічний 'оператор' (час запуску), і шлях до самого файлу, який необхідно виконати:

Як ви вже зрозуміли, кожна з колонок є цифру (час), що відноситься до тієї чи іншої задачі. Давайте розглянемо їх детальніше:

  • Minutes представляють хвилини даного години, 0-59;
  • Hours представляють годинник даного дня, 0-23;
  • Days представляють дні цього місяця, 1-31;
  • Months представляють місяці цього року, 1-12;
  • Day of the week представляє день тижня, з неділі до суботи, в цифровій формі, як 0-6.

Наприклад, якби хтось хотів виконувати завдання в 0:00 в перший день кожного місяця, то це виглядало б приблизно так:

Якби ми хотіли намітити завдання, яка б виконувалася щосуботи о 8:30, то ми написали би наступний код:

Також існує безліч операторів, які можуть використовуватися для того, щоб налаштувати графік ще більш детально:

  • Коми використовуються, щоб розділяти значення в будь-який з колонок завдання;
  • Тире використовуються, щоб визначити діапазон значень;
  • Зірочка говорить про те, що потрібно використовувати 'все' або 'кожне' значення;

За замовчуванням cronTab відправляє поштове повідомлення всякий раз, коли запланована завдання виконане.

За замовчуванням cronTab відправляє поштове повідомлення всякий раз, коли запланована завдання виконане. Хоча в більшості випадках це не потрібне. Ми можемо легко розширити цю функціональність, переадресовуючи потік даних цієї команди до 'нульового пристрою' або пристрою /dev/null. По суті, в нашому випадку це буде файл, який відхиляє кожен йде до нього запит. Переадресація потоку даних здійснюється за допомогою оператора >. Давайте розглянемо приклад того, як ми можемо переадресувати стандартний потік даних до нульового пристрою, використовуючи cron задачу, яка буде запускатися щосуботи о 8:30:

До того ж до того, що у нас є, якщо ми переадресуємо потік даних до пристрою, який не існує, то напевно потрібно також переадресувати стандартні помилки. Ми можемо зробити це, просто переадресовуючи їх в той же місце, де і повинно знаходитися наш пристрій!

Крок 1 - Креслення

Для того щоб управляти cronTab за допомогою PHP, ми будемо мати потребу в можливості виконувати команди на віддаленому сервері. На щастя, PHP надає нам простий спосіб зробити - через бібліотеку SSH2. Для тих, у кого даної бібліотеки немає, доведеться зробити наступні кроки:

PHP libssh2 Установка / Конфігурація

Ми почнемо наш клас з оголошення чотирьох приватних властивостей:

  • $сonnection представляє наше підключення / ресурс;
  • $path представить шлях для файлу;
  • $handle представить назву нашого тимчасового cron файлу;
  • $cron_file представляє повний шлях і назву тимчасового cron файлу;

Наш клас повинен бути в змозі з'єднатися з сервером і підтвердити справжність користувача, для того, щоб тільки адміністратор міг користуватися цією утилітою. Таким чином, ми встановимо зв'язок SSH2 і підтвердимо справжність її в межах конструктора.

Після того, як користувач авторизується, йому знадобиться метод, який буде пов'язаний з виконанням різних команд, точніше сказати відповідальним за управління завданнями. Ми назвемо цей метод exec(). Даний метод ми будемо викликати з інших методів нашого класу, коли нам знадобиться виконати команду на віддаленому сервері.

Потім, нам знадобиться можливість вписати cronTab в файл - на це у нас повинні бути відповідні права. Також не потрібно забувати, що ми так само повинні бути в змозі видалити цей файл, коли він нам більше не буде потрібен. Отже, до нашого методу додається ще кілька методів, таких як write_to_file() та remove_file() для додавання та видалення файлів.

Звичайно, ми будемо також потребуватиме можливості створювати і видаляти cron завдання. Таким чином, ми визначимо методи append_cronjob() і remove_cronjob().

Після того, як ми видалили єдиний або останній cronjob, нам знадобиться можливість видалити весь cronTab замість того, щоб намагатися створити порожній. Для цього ми напишемо метод remove_crontab(), щоб реалізувати дану функціональність.

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

Крок 2 - Конструктор!

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

  • $host: представляє IP-адреса віддаленого сервера, з яким ми хочемо з'єднатися;

  • $port: порт, який буде використовуватися для зв'язку SSH2;
  • $username: користувач, який отримує доступ до сервера;
  • $password: пароль користувача для сервера.

Перше, що нам треба визначити в межах конструктора - це властивість $this->path, яке буде зберігати повний шлях за замовчуванням для наших тимчасових cron файлів.

Для цього ми будемо використовувати чарівну константу PHP __DIR__, яка дозволить нам визначити шлях скрипта, який ми викликаємо. Однак, є випадки, де ця константа не може бути визначена,  тому нам необхідно перевірити, чи доступне нам значення з константи __DIR__.

Якщо значення даної константи нам недоступно, тоді шлях потрібно отримати іншим способом. Для цього ми можемо скористатися функціями strrpos() і substr(), в які будемо передавати значення чарівної константи __FILE__, яка представить нам весь шлях і назву поточного файлу.

Для того щоб витягти тільки шлях, нам стане в нагоді функція strrpos(), яка поверне нам позицію слеша у значенні __FILE__. Це значення, яке ми збережемо як $path_length, дасть нам набір символів, не включаючи останній роздільник директорій.

Після ми будемо використовувати функцію substr() для того, щоб вирізати потрібний нам фрагмент рядки, а саме повний шлях.

У substr() ми передамо 3 аргументу:

  • рядок, з якою ми працюємо;
  • початкову позицію, яка в нашому випадку дорівнює 0;
  • кінцеву позицію, яка зберігається в змінній $path_length.

Після виконання даних функцій у нас буде сформовано повний шлях, якого потребує скрипт.

Тепер ми визначимо рядок, яка буде присвоюватися за замовчуванням для тимчасового cron файлу $this->handle, а потім об'єднаємо з повним шляхом, який вирахували раніше $this->cron_file. Ось ми і отримали те, що нам потрібно.

Після того, як ми вирахували необхідні нам значення, приступимо до роботи над створенням і перевіркою зв'язку з сервером. Весь цей функціонал ми помістимо в блок try / catch для того, щоб мати можливість відловлювати виникли помилки. У цьому випадку, ми можемо відловити помилки і викинути виняток, щоб надати користувачеві детальну інформацію про збій.

В межах блоку try ми спочатку перевіримо, щоб нам були доступні всі аргументи. Для цього будемо використовувати функцію is_null(), яка повертає істину або брехня. Якщо який-небудь з цих аргументів буде дорівнює NULL, то ми викинемо нове виключення з відповідним повідомленням.

Потім, ми визначимо $this-> connection, передавши аргументи $host і $port в функцію ssh2_connect() для того, щоб встановити з'єднання з сервером. Даний метод повертає FALSE, якщо з'єднання не було встановлено.

Так як ми використовуємо нашу власну обробку помилок, то нам також потрібно передбачити обробку цих повідомлень оператором @.  Якщо скрипту не вдасться з'єднатися з сервером, то ми викинемо нове виключення з відповідним повідомленням.

Тепер ми спробуємо визначити справжність користувача, використовуючи функцію ssh2_auth_password(), в яку передамо підключення, ім'я користувача і його пароль. ssh2_auth_password() також повертає логічне значення, яке характеризує результат виконання авторизації на сервері.

PHP спробує знайти блок catch в тому випадку, якщо виникне якась помилка під час роботи скрипта, а потім створити об'єкт винятку, який містить багато корисної інформації про помилку.

Так, в блоці catch ми будемо використовувати метод getMessage(), для того щоб отримати і показати повідомлення, визначене у виключенні.

Ви, напевно, вже зрозуміли, що ми будемо показувати виключення через метод error_message(), який визначимо трохи пізніше. Хоча цей метод доки не визначений, він буде просто відображати повідомлення помилок охайним методом.

Крок 3 - Виконання множинних команд

Метод, який ми зараз напишемо, буде відповідальний за виконання команд на віддаленому сервері. Це можна порівняти з ручним входом на сервер через програму PuTTY. Для того щоб зберегти гнучку функціональність, ми зробимо методи публічними, щоб користувачі могли виконати будь-які команди, які їм необхідні для роботи. Також ми зробимо можливим будь-яке число аргументів. Ці аргументи є команди, які будуть запускатися функцією ssh2_exec().

Отже, перша річ, яку ми зробимо в цьому методі, - це визначимо змінну, що представляє кількість аргументів, які передаються в метод. Ми будемо використовувати функцію PHP func_num_args(), для того щоб отримати цю кількість.

Тепер, в межах блоку try, ми перевіримо, передають якісь аргументи на цей метод чи ні. Якщо кількість аргументів буде дорівнює 0, то ми кинемо нове виключення з відповідним повідомленням.

Потім, використовуємо функцію func_get_args() для створення безліч всіх аргументів, які передали в цей метод.

Використовуючи тернарний оператор, ми визначимо нову змінну, $command_string, як єдиний рядок команди в Linux, яку будемо виконувати.

Якщо у нас дійсно будуть множинні команди, то ми будемо використовувати функцію PHP, implode(), для того щоб розібрати безліч аргументів. У функцію implode() ми передаємо два аргументи: рядок і роздільник. Ми відділимо кожен елемент роздільником &&, який дозволить нам виконувати множинні команди, послідовно, на одній лінії!

Якщо ж буде введена тільки одна команда, то ми визначимо її як $arguments[0], який буде представляти першу і єдину команду.

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

Потоки, не визначені як об`єкт ресурсу, які відображають потокову поведінку.... які можуть читати з або писати в лінійну форму.

І в цьому випадку, ми будемо використовувати блок try / catch для того, щоб мати можливість відловити будь-які повідомлення про помилку з оператором @. Також ми будемо викидати наше власне виключення з відповідним повідомленням.

Вот для чого блок try! Тепер доповнимо цей код викликом методу error_message() для відображення помилок! З блоком try та catch ми повернемо $this в кінець методу exec(), який робить цей метод ланцюговим.

Крок 4 - Запис cronTab в файл

Наступний метод, write_to_file(), буде відповідальний за запис cronTab в тимчасовий файл або створення нового файлу. Файл не повинен мати робот cron. Для цього нам буде потрібно 2 аргументи:

  • шлях для тимчасового файлу;
  • ім'я файлу, який буде створений.

З аргументами вчинимо точно так же, як і в нашому конструкторі, а саме - встановимо значення за замовчуванням в NULL.

Перше, що ми тут зробимо, так це перевіримо за допомогою методу crontab_file_exists() існує cron файл. Якщо файл дійсно існує, то створювати його не потрібно. Якщо його немає, то ми будемо використовувати тернарний оператор, щоб перевірити наявність значень в змінних $path і $handle. и що він не дорівнює NULL. Якщо хоча б один з них буде дорівнює NULL, то ми запустимо процес, який їх визначить.

Після цього ми зв`яжемо дані в одне ціле, яке буде містити повний шлях для тимчасового cron файлу.

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

Це добре працює, але тільки якщо завдання заплановано з cronTab. Якщо там немає завдань cron, цей файл николи не буде створен! Використовуючи оператор &&, ми можемо перевірити кілька команд / виразів. Давайте додамо умовний вираз для визначення, які файли cron існують.  Якщо файл не існує, цей вираз будет як визначен як помилковий. У цьому зв'язку, якщо потрібно, ми можемо використовувати оператор || або "or" після цієї умови для створення нового бланку файлу.

Команди, які знаходяться після "or", будут виконувати умову/умови визначенні, як помилкові. Відтепер, якщо використати оператор > знов, на цей раз без попередньої команди, ми зможемо створити новий файл. Так, цей ряд команд буде записан в файл cronTab, з перевіркою існування цього файлу. Якщо файла не існує, буде строен новий.

Нарешті, ми викличемо метод exec() і передамо рядок з командою. Потім, для того щоб зробити цей метод ланцюговим, ми вернемо $this.

Крок 5 - Видаляємо тимчасовий файл

Метод remove_file() простий настільки, наскільки це може бути! Тут ми скористаємося допоміжним методом crontab_file_exists(), щоб перевірити існування тимчасового cron файлу і потім виконаємо команду rm Linux, щоб видалити його. Як завжди ми також повернемо $this.

Крок 6 - Створення нового завдання Сron

Метод, який ми зараз напишемо, буде створювати нову cron задачу у тимчасовий файл, після чого виконає команду crontab. Метод append_cronjob() буде приймати один аргумент $cron_jobs, що представляє собою послідовність або безліч послідовностей cron завдань.

На самому початку цього методу ми перевіримо, чи є аргумент $cron_jobs рівним NULL. Якщо так, то ми викличемо метод error_message(), щоб зупинити скрипт і показати користувачеві повідомлення про помилку.

Фактично, ми можемо вивести наше завдання в наш файл cron через перенаправляючи стандартний вивід знову в файл.

Потім ми визначимо нову змінну $append_cronfile з текстом "echo", після якої стоїть пробіл і лапки. Цей рядок ми будемо використовувати для різних cron завдань. Для конкатенації рядків будемо використовувати вираз =.

Використовуючи тернарний оператор, ми визначимо, чи містить $cron_jobs безліч команд. Якщо це буде так, то нам потрібно розділити команди символом \n так, щоб кожна cron завдання розташовувалася на своїй власній рядку. Якщо $cron_jobs не має масиву, ми просто зв'язуємо це завдання з $append_cron. Цим способом ми будемо мати відформатований стринг незалежно працюємо з ним або ні.

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

Тепер ми визначимо змінну, як строку, з допомогою команди ми використаємо встановлення нового файлу cron! Це простий виклик команди cronTab через path та filename з файлу cron.

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

Крок 7 - Видалення існуючих завданнь cron

Тепер, коли ми можемо створити нові cron завдання, за логікою речей, нам необхідно мати можливість їх видалити! Метод remove_cronjob() буде приймати один аргумент, який представляє з себе регулярне виразом. Цей regEx буде використовуватися для того, щоб знайти відповідність завдання в межах cronTab і видалити їх. Як з методом append_cronjob(), в першу чергу ми зробимо перевірку аргументу $cron_jobs на те, що він NULL. Якщо ні, ми будемо викликати метод create_file() для того, щоб записати cron tab у файл.

Коли буде створен файл cron, ми тепер будемо читати його в масиві використовуючи функцію PHP file(). Ця функція розбере отриманий файл в масив с кожною строкою, як елемент масиву. Ми пропустимо наш файл cron через цю функцію як перший аргумент, а потім вибрати спеціальний флаг, FILE_IGNORE_NEW_LINES, який змушує file() ігнорувати усі нові лінії. Таким чином, ми отримаємо масив с завданнями cron з них самих!

Якщо завдань не буде, цей масив буде порожній. Тоді немає причин продовжувати. Таким чином, ми перевіримо порожність $cron_array та зупинимо його, якщо це так.

Якщо cronTab НЕ буде порожній, то тоді порахуємо елементи в $cron_array, використовуючи функцію count(). Отримане значення збережемо в змінній $original_count. Ми використаємо це далі для визначення та видалення любого завдання cron з $cron_array.

Тепер у нас є можливість дізнатися чи містить $cron_jobs кілька значень чи ні. Якщо так, то ми скористаємося циклом foreach для обробки кожного значення. На кожному кроці будемо використовувати функцію preg_grep().  Ця витончена функція, мало чим відрізняється від preg_match().  У цьому випадку, однак, ми хочемо отримати елементи множини, які не відповідають шаблоном. Таким чином, у нас буде безліч всієї cron роботи, якої ми хочемо зберегти! Тут ми використаємо спеціальний флаг, PREG_GREP_INVERT, який буде причиною повернення preg_grep() у будь я кий масив елементів, які не відповідають регулярним виразам. Таки чином ми матимемо масив усіх завдань cron, які ми бажаємо зберегти!

Якщо аргументом $cron_jobs НЕ буде масивом, то ми продовжимо рухатися в майже такій же манері, але без циклів. І знову, ми визначимо $cron_array як кінцевий масив з функції preg_grep() з встановленим флагом PREG_GREP_INVERT.

Далі ми порівняємо значення $cron_array з $original_count. Якщо довжини будуть ідентичні, то ми просто викличемо метод remove_file(), щоб видалити тимчасовий cron файл. Якщо вони не будуть відповідати, то ми видалимо існуючий cronTab, а потім створимо новий!

Все методи remove_file()remove_crontab() та append_cronjob() вертають $this, при цьому вони зберігають ланцюг.

Крок 8 - Видалення cronTab

Видалення всього cronTab зробити дуже просто. По суті ми тільки виконаємо команду crontab -r, яка видалить весь cronTab для даного користувача. Так як crontab був видалений, ми могли б також видалити тимчасовий cron файл. Потім ми введемо return $this, щоб зберегти ланцюг.

Крок 9 - Кілька допоміжних методів

Тепер пропоную розглянути класи, які ми вже використовували. Це crontab_file_exists() та error_message().

  • $this->crontab_file_exists()

    Цей метод просто повертає результат функції file_exists() в залежності від того, чи існує тимчасовий cron файл.

  • $this->error_message($error)

    Цей метод приймає повідомлення про помилку, яке ми хочемо показати. Потім ми викликаємо метод PHP die() для зупинки виконання коду та відображення повідомлення. Строка повинна мати елементи <pre> для деталізації та кращого розуміння помилки.

Крок 10 - Збираємо все в купу!

Тепер давайте подивимося, як користуватися створеним нами дітищем:

  • Установка з'єднання:

    По-перше, давайте створимо новий приклад нашого класу. Пам'ятайте, що нам потрібно передати ip, порт, логін та пароль для конструктору классу.

  • Створення одного завдання:

    З аутентифікацією з'єднання, давайте розглянемо як ми можемо створити одне нове завдання cron.

  • Створення декількох завдань:

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

  • Видалення одного завдання cron:

    Подібно тому, як ми створили одне завдання, видалимо одне. Але на цей раз мі будемо використовувати регулярні виразки для пошуку потрібного завдання. regEx може бути простіше, ніж ти вважаєш. Фактично, є багато способів regEx для потрібного завдання. Наприклад, якщо завдання, що потрібно видалити унікальне, можна просто вказати специфічне им'я команди. Також, якщо ви хотіли видалити всі завдання для певного місяця, ви можете написати виразку для пошуку завдань для потрібного місяця.

  • Видалення масиву завдань cron:

    Видалення багатьох завдань cron виглядає также як видалення одного, тільки з зазначенням масиву виразок для методу remove_cronjob().

Висновок

От і все! Сподіваюся, урок був для вас корисний, і ви зможете застосувати накопичені знання в ваших проектах. Успіхів!

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.