Создание инструментов управления сервером на языке PHP
Основные подходы к созданию серверных PHP скриптов
Серверный скрипт на PHP может выполнять задачи администрирования: резервное копирование, очистку логов, мониторинг ресурсов. Рассмотрим различные варианты реализации, начиная с простых и заканчивая более надёжными решениями.
Как организовать безопасное выполнение системных команд из PHP?
Наиболее эффективный способ - использовать встроенные функции exec() или shell_exec() с проверкой входных данных. Пример безопасного запуска команды df -h для получения информации о дисках:
$diskUsage = shell_exec('df -h 2>&1');
echo '<pre>' . htmlspecialchars($diskUsage) . '</pre>';скрипт сервера php (серверный php скрипт)
Такой подход позволяет захватить вывод команды и экранировать его для веб-отображения.
Ошибка: команда не выполняется из-за недостаточных прав. Решение: проверить пользователя веб-сервера (обычно www-data) и добавить ему права sudo на конкретные команды через файл /etc/sudoers.
Как запустить PHP-скрипт по расписанию (cron)?
Для регулярного выполнения скрипта используется cron. Пропишите задачу в crontab:
* * * * * /usr/bin/php /path/to/script.php >> /var/log/script.log 2>&1Цель: автоматический сбор метрик или очистка кэша. Проблема: скрипт может выполняться дольше интервала cron, что ведёт к наложению запусков. Решение: использовать блокировку файлом (flock).
$lock = fopen('/tmp/script.lock', 'c');
if (!flock($lock, LOCK_EX | LOCK_NB)) { exit('Already running'); }
// код скрипта
flock($lock, LOCK_UN);
fclose($lock);Типичная ошибка: в cron не указан полный путь к PHP. Используйте which php для определения пути.
Как обрабатывать аргументы командной строки в PHP?
Для написания универсального CLI-скрипта применяйте getopt(). Пример:
$options = getopt('a:b:', ['action:', 'target:']);
if ($options['action'] === 'backup') {
$target = $options['target'] ?? '/var/www';
echo 'Backup of ' . $target;
}Цель: скрипт с разными режимами (backup, restore, clean). Проблема: некорректный ввод аргументов. Реализуйте проверку:
if (!isset($options['action'])) { echo 'Usage: php script.php --action=backup'; exit(1); }Ошибка: короткие опции путаются с длинными. Рекомендуется использовать только длинные опции для ясности.
Как обеспечить выполнение скрипта только из командной строки, а не из веба?
Добавьте проверку на SAPI:
if (php_sapi_name() !== 'cli') { die('This script is CLI only.'); }Цель: предотвратить случайный запуск через веб-сервер, что может привести к уязвимостям.
Как логировать действия серверного скрипта?
Используйте error_log() с указанием файла:
error_log('Backup started at ' . date('Y-m-d H:i:s'), 3, '/var/log/myapp.log');Цель: отслеживание ошибок и успешных запусков. Проблема: переполнение журнала. Добавьте ротацию логов через logrotate или удаление старых записей внутри скрипта.
if (filesize($logfile) > 10485760) { unlink($logfile); }Расширенные примеры с пояснениями
Пример 1: Многопоточная обработка файлов через pcntl
Для ускорения обработки большого количества файлов (например, архивация) используйте дочерние процессы. Важно: модуль pcntl доступен только в CLI.
$files = glob('/tmp/data/*.txt');
$maxProcesses = 4;
$children = [];
foreach ($files as $file) {
$pid = pcntl_fork();
if ($pid == -1) { die('Fork failed'); }
if ($pid) {
$children[] = $pid;
if (count($children) >= $maxProcesses) {
pcntl_wait($status);
array_shift($children);
}
} else {
// код обработки файла
file_put_contents($file . '.processed', strtoupper(file_get_contents($file)));
exit(0);
}
}
foreach ($children as $child) { pcntl_wait($status); }
echo 'Done';Результат: создаются файлы .processed параллельно до 4 одновременно.
Проблема: при большом количестве файлов можно превысить лимит процессов. Используйте pcntl_wait() с опцией WNOHANG для неблокирующего ожидания.
Пример 2: Работа с сокетами для мониторинга портов
Скрипт проверяет доступность TCP-порта и отправляет уведомление при недоступности.
$host = '127.0.0.1';
$port = 3306;
$fp = @fsockopen($host, $port, $errno, $errstr, 5);
if (!$fp) {
$message = "Port $port on $host is down: $errstr";
error_log($message, 3, '/var/log/portmonitor.log');
mail('admin@example.com', 'Port Down', $message);
} else {
fclose($fp);
echo 'OK';
}Результат: при недоступности MySQL-порта отправляется email.
Ошибка: скрипт может зависнуть при блокировке сокета. Установите таймаут (5 секунд) и используйте @ для подавления предупреждений.
Пример 3: Резервное копирование базы данных с прогресс-баром
Скрипт делает дамп MySQL и выводит прогресс в терминале (CLI).
$db = 'mydb';
$backupFile = '/backup/' . $db . '_' . date('Ymd') . '.sql';
$cmd = "mysqldump --single-transaction $db 2>/dev/null";
$output = [];
$returnVar = 0;
exec($cmd, $output, $returnVar);
if ($returnVar === 0) {
$data = implode("\n", $output);
$totalLines = count($output);
$progress = 0;
$handle = fopen($backupFile, 'w');
foreach ($output as $line) {
fwrite($handle, $line . "\n");
$progress++;
echo "\rProgress: " . round(($progress/$totalLines)*100) . "%";
}
fclose($handle);
} else {
echo 'Backup failed';
}Результат: файл дампа и отображение процента выполнения в той же строке.
Проблема: при большом дампе скрипт потребляет много памяти. Используйте passthru() с записью потока напрямую в файл.
Пример 4: Интерактивный скрипт с вводом пароля
Для сценариев, где нужен пароль (например, расшифровка), используйте readline() с отключением эха.
echo 'Enter password: ';
system('stty -echo');
$password = trim(fgets(STDIN));
system('stty echo');
echo "\n";
// использование пароля
if ($password === 'secret') { echo 'Access granted'; }Результат: пароль не отображается при вводе.
Ошибка: stty не доступен в Windows. Альтернатива: использовать readline_callback_handler_install().