Kategorie: PHP
Opublikowano 2020-06-10 18:40
Konfiguracja CRON na serwerze hostingowym z reguły jest prosta, choć może przysporzyć wiele kłopotów. Wszystko zależy od możliwości, jakie udostępnia nam nasz dostawca hostingu. Przedstawię teraz 4 sposoby na to jak skonfigurować zadania CRON w różnych okolicznościach.
W tym poradniku przedstawię wszystkie możliwe sposoby na automatyczne wykonywanie zadań CRON. Pokażę 3 drogi, jakie udostępniają nam dostawcy hostingów oraz jedno alternatywne rozwiązanie. Zakładam, że posiadasz już swój job scheduler i gotowe zadania do wykonania.
Przedstawione środowisko PHP to framework Laravel w wersji 5.8. Umożliwia on szybką i intuicyjną konfigurację job schedulera, który można wywołać jedną komendą w powłoce (shell). Poradnik będzie szczególnie przydatny dla osób korzystających z tego framework'a, gdyż podaję tu rozwiązania problemów, z którymi musiałem się uporać samemu. Niemniej jednak każdy scheduler będzie mógł zostać podpięty w analogiczny sposób.
Wielu dostawców hostingu ze względów bezpieczeństwa wyłącza funkcję PHP "proc_open", która jest wymagana przez klasę Kernel! Jeśli otrzymujesz błąd "The Process class relies on proc_open, which is not available on your PHP installation.", przeczytaj mój wpis, w którym pokazuję jak zrobić własny scheduler dla utworzonych zadań CRON i wykorzystać go w praktyce.
Hosting obsługuje max. 3 sposoby konfiguracji CRON:
Niezależnie od tego, który sposób wybierzemy, podczas konfiguracji interwał wykonywania skryptu powinniśmy ustawić na 1 minutę. Symbolizuje to zapis " * * * * * ", czyli każda minuta, godzina, dzień, miesiąc i dzień tygodnia. Przykładowe ustawienia w panelach hostingowych:
Czas na wybranie sposobu wykonania.
W tej tradycyjnej metodzie wystarczy, że podamy ścieżkę do pliku z wykonującym się schedulerem. W przypadku Laravel, jeśli chcemy skorzystać z metody klasy Artisan, w naszym surowym pliku PHP wystąpią problemy związane z brakującymi namespace'ami. W celu 'odpalenia' Laravel musimy dołączyć plik "autoload.php" z katalogu "vendor" i ręcznie wpisać ścieżkę klasy "Artisan", gdyż aliasy zdefiniowane w pliku "config/app.php" nie mają tu strefy wpływu.
// File "/server/cron.php"
require_once __DIR__ .'/../vendor/autoload.php';
use Illuminate\Support\Facades\Artisan;
$app = require __DIR__ .'/../bootstrap/app.php';
$app->make('Illuminate\Contracts\Console\Kernel')->bootstrap();
Artisan::call('schedule:run');
Określenie polecenia, które serwer ma wywoływać to najlepsza i najprostsza metoda, jeśli posiadamy takowe polecenie w naszym projekcie. Polecenie to wykonuje cały mechanizm i odpala harmonogram CRON. W przypadku Laravel jest to komenda "artisan schedule:run". Taką funkcję dla SSH możemy utworzyć sami, o ile faktycznie jej potrzebujemy i nie jest to przerost formy nad funkcjonalnością.
php /ścieżka-do-projektu/artisan schedule:run >> /dev/null 2>&1
Komendę rozpoczynamy poleceniem "php", które wykona skrypt pliku (w tym przypadku "artisan"). Należy pamiętać o określeniu wersji PHP (np. "php73"), jeśli domyślna jest zbyt niska. Może być konieczna zamiana na "php-cli", wszystko jest zależne od naszego serwera. Końcówka ">> /dev/null 2>&1" to dosyć specjalne polecenie, które w wielkim uproszczeniu wycisza wszelkie outputy naszego kodu, aby nie zwracał on żadnej wiadomości (w tym również błędów).
Jeśli z jakiś powodów powyższe metody nie działają, a za wszelką cenę chcemy wywołać naszą komendę SSH, możemy spróbować wykonać plik PHP, który wykona komendę powłoki. Służą do tego funkcje "exec" i "shell_exec", jednak przeważnie są one wyłączone przez dostawców hostingu ze względów bezpieczeństwa. Ale nie zaszkodzi spróbować! Na przykładzie Laravel wystarczy podać ścieżkę do pliku z surowym PHP, w którym umieścisz linijkę:
shell_exec("php73 artisan schedule:run");
Jeśli funkcje te są zablokowane ze względów bezpieczeństwa, zwrócony zostanie komunikat "proc_open() has been disabled for security reasons". W tym przypadku, o ile sposób nr 1 nie działa, powinniśmy stworzyć własny scheduler i uruchomić go poprzez wykonanie pliku.
Ciekawa alternatywa, gdy nie mamy możliwości wykonania komendy powłoki, a przy wykonywaniu pliku nie możemy sobie poradzić z konfiguracją (choć mam nadzieję, że po tym tutorialu już możecie). Wystarczy utworzyć nowy kontroler dla CRON i w nim wywołać metodę klasy "Artisan" jak w pierszym punkcie - bez obawy, ze musimy dołączać brakujące pliki. Minusem jest natomiast fakt, że jeśli nie możemy ustawić autoryzacji HTTP to adres URL może zostać wywołany przez osoby niepowołane, co w skrajnych przypadkach (czyli w odpowiednim czasie) może spowodować np. wielokrotne wysłanie maili do członków naszego newslettera. Jeśli mamy taką możliwość, możemy uzyskać od naszego hostingodawcy listę wykorzystywanych adresów IP i pozwalać na wykonanie CRON tylko, jeśli adres IP należy do zaufanych.
Przykładowe rozwiązanie dla Laravel:
web.php
Route::group(['prefix' => 'cron'], function(){
Route::get('/execute', 'admin\CronController@execute')->name('admin.cron.execute');
});
App\Http\Controllers\admin\CronController.php
namespace App\Http\Controllers\admin;
use App\Http\Controllers\Controller;
use App\Guards\CronIPChecker;
class CronController extends Controller{
public function __construct(){
CronIPChecker::abortIfNotTrustedIP();
}
public function execute(){
\Artisan::call('schedule:run');
return response('Complete', 200);
}
}
App\Guards\CronIPChecker.php
Musimy mieć pewność, że CRON zostanie wywołany tylko przez nasz serwer oraz ewentualnie nas samych. Stała "LocalIP" została zdefiniowana dla testów. Dla przejrzystości i ewentualnego rozszerzenia w przyszłości tworzymy osobną klasę "HostingProvider", skąd pobierać będziemy listę zweryfikowanych adresów IP.
namespace App\Guards;
use App\Entities\HostingProvider;
class CronIPChecker{
const LocalIP = '127.0.0.1';
public static function abortIfNotTrustedIP(){
$allowedIPs = HostingProvider::getIPs();
$currentIP = $_SERVER['REMOTE_ADDR'];
if( !in_array($currentIP, $allowedIPs) )
abort(403, 'Access forbidden.');
}
}
App\Entities\HostingProvider.php
namespace App\Entities;
class HostingProvider{
public static function getIPs(){
return config('hosting.IPs');
}
}
Nawet, jeśli hosting nie umożliwia nam konfiguracji CRON, wcale to nie oznacza, że w ogóle nie mamy takiej możliwości.
Całe szczęście w internecie można znaleźć wiele stron, służących do wykonywania zadań CRON. Oczywiście wymagają one do tego adresu URL, więc wymagana będzie realizacja punktu 3-ego. Po wstępnej analizie mogę polecić stronę cron-job.org, która jest w pełni darmowa (choć wymaga założenia konta jak chyba każda inna). Niewątpliwym jej plusem jest możliwość autoryzacji HTTP, więc nie musimy sami zabezpieczać skryptu, wystarczy użyć zabezpieczenia w pliku .htaccess - tutaj link pokazujący jak wymagać loginu i hasła tylko dla konkretnego URL.
Tak wygląda przykładowa konfiguracja na stronie cron-job.org.
Jak widać, podstawa to nasz job-scheduler, pozostaje tylko kwestia, w jaki sposób będziemy go wykonywać. Przedstawiłem wszystkie możliwości, jakie możemy napotkać na naszym hostingu oraz jedno alternatywne rozwiązanie. Podzielcie się swoją opinią czy ten poradnik był pomocny, każda uwaga jest na wagę złota.
A może znacie jeszcze inny sposób na wykonanie CRON?
Dołącz do newslettera i sam decyduj jakie treści chcesz otrzymywać.