PHP вне браузера: Как писать кроссплатформенные CLI-приложения

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

PHP — это не только про веб

Когда говорят «PHP», большинство сразу представляют себе ленту новостей ВКонтакте, корзину в интернет-магазине или бэкенд WordPress. Это логично: язык рождался как инструмент для веба. Но что, если я скажу вам, что PHP отлично справляется с задачами, которые обычно отдают Python, Bash или Go? Речь о кроссплатформенной разработке на PHP — создании утилит, которые работают и на Windows, и на Linux, и на macOS без переписывания кода.

В этой статье мы не будем учить вас верстать формы. Мы покажем, как написать консольный файловый менеджер, парсер логов и систему мониторинга папок — и всё это на чистом PHP, без единого веб-сервера. Вы узнаете, как обходить подводные камни разных ОС и писать код, который запускается везде одинаково.

Готовы? Тогда открывайте терминал и приготовьтесь удивляться возможностям старого доброго PHP.

Почему PHP для кроссплатформенных задач? Три железных аргумента

Прежде чем нырять в код, давайте разберёмся, зачем вообще использовать PHP для desktop- и server-утилит. У Python для этого есть Django и Flask, у Node.js — Express. Зачем нам PHP?

  • Повсеместная доступность. PHP установлен почти на каждом shared-хостинге и на 80% серверов в мире. Если вы пишете инструмент для администрирования, велика вероятность, что на целевой машине уже есть PHP. Не нужно ставить дополнительный рантайм.
  • Низкий порог входа для веб-разработчиков. Вы уже знаете синтаксис, функции работы со строками и массивами. Вам не нужно учить новый язык, чтобы автоматизировать рутину на сервере.
  • Богатая стандартная библиотека. В PHP есть всё для работы с файлами, процессами, сетевыми сокетами и zip-архивами. А PECL-расширения вроде parallel или pthreads добавляют многопоточность.

Конечно, PHP не идеален для GUI-приложений (хотя есть PHP-GTK). Но для консольных утилит и демонов — это зрелый и стабильный инструмент. Давайте посмотрим, как писать такой код правильно.

Основы кроссплатформенного PHP-кода

Первое, что нужно запомнить: разделители путей. На Linux это /, на Windows — \. PHP предоставляет константу DIRECTORY_SEPARATOR, но ещё удобнее использовать функцию DIRECTORY_SEPARATOR или универсальный слеш / — Windows его прекрасно понимает с PHP 7+.

Правильная работа с путями

<?php
// Плохо — жёстко зашитый слеш
$path = '/var/log/errors.log'; // Упадёт на Windows

// Хорошо — используем DIRECTORY_SEPARATOR
$path = 'var' . DIRECTORY_SEPARATOR . 'log' . DIRECTORY_SEPARATOR . 'errors.log';

// Отлично — используем / и realpath()
$path = __DIR__ . '/logs/errors.log';
echo $path; // Работает на обеих ОС
?>

Второй важный момент — кодировка. На Windows по умолчанию консоль часто работает в CP866, а PHP скрипты — в UTF-8. Для вывода кириллицы в Windows используйте функцию mb_convert_encoding() или установите кодировку через chcp 65001 в начале скрипта.

Пример: кроссплатформенное чтение файла

<?php
function readLogFile(string $filename): string {
    // Проверяем существование файла
    if (!file_exists($filename)) {
        throw new RuntimeException("Файл $filename не найден");
    }
    
    // Читаем содержимое
    $content = file_get_contents($filename);
    
    // Определяем ОС и конвертируем кодировку для вывода
    if (PHP_OS_FAMILY === 'Windows') {
        $content = mb_convert_encoding($content, 'CP866', 'UTF-8');
    }
    
    return $content;
}

echo readLogFile('logs/app.log');
?>

Создаём реальную утилиту: мониторинг изменений в папке

Давайте напишем полезную вещь — скрипт, который следит за папкой и выводит список новых и изменённых файлов. Это классическая задача для кроссплатформенной разработки на PHP, которая пригодится для деплоя, бэкапов или синхронизации.

Шаг 1. Получаем список файлов

<?php
function scanDirectory(string $dir): array {
    $files = [];
    $iterator = new RecursiveIteratorIterator(
        new RecursiveDirectoryIterator($dir, RecursiveDirectoryIterator::SKIP_DOTS)
    );
    
    foreach ($iterator as $fileinfo) {
        if ($fileinfo->isFile()) {
            $files[$fileinfo->getPathname()] = $fileinfo->getMTime();
        }
    }
    
    return $files;
}
?>

Этот код работает на любой ОС. RecursiveDirectoryIterator правильно обрабатывает и слеши, и обратные слеши.

Шаг 2. Сравниваем с предыдущим состоянием

<?php
function detectChanges(string $dir, string $stateFile = 'state.json'): array {
    $current = scanDirectory($dir);
    $previous = [];
    
    // Загружаем предыдущее состояние
    if (file_exists($stateFile)) {
        $previous = json_decode(file_get_contents($stateFile), true) ?? [];
    }
    
    $newFiles = [];
    $modifiedFiles = [];
    $deletedFiles = [];
    
    // Ищем новые и изменённые
    foreach ($current as $path => $mtime) {
        if (!isset($previous[$path])) {
            $newFiles[] = $path;
        } elseif ($previous[$path] !== $mtime) {
            $modifiedFiles[] = $path;
        }
    }
    
    // Ищем удалённые
    foreach ($previous as $path => $mtime) {
        if (!isset($current[$path])) {
            $deletedFiles[] = $path;
        }
    }
    
    // Сохраняем текущее состояние
    file_put_contents($stateFile, json_encode($current, JSON_PRETTY_PRINT));
    
    return [
        'new' => $newFiles,
        'modified' => $modifiedFiles,
        'deleted' => $deletedFiles
    ];
}
?>

Шаг 3. Запускаем в цикле с задержкой

<?php
// Устанавливаем кодировку консоли для Windows
if (PHP_OS_FAMILY === 'Windows') {
    system('chcp 65001 > nul');
}

echo "Мониторинг папки 'data/' запущен. Нажмите Ctrl+C для выхода.\n";

while (true) {
    $changes = detectChanges('data/');
    
    if (!empty($changes['new'])) {
        echo "Новые файлы:\n";
        foreach ($changes['new'] as $file) {
            echo "  + " . $file . "\n";
        }
    }
    
    if (!empty($                

Блоги

Рекомендаций по книгам