автоматический запуск php скрипта
Фоновое выполнение скрипта на PHP без crontab
Озадачили меня тут написать демона на PHP. Т.е. скрипт, который будет заданное количество раз в заданное количество часов в случайное время (всегда случайное) выполнять определенные действия, и все это без использования cron’a.
До этого никогда не заморачивался, а тут после постановки задачи, начал было думать что так нельзя, что php скрипт надо вызывать браузером…ну задача то поставлена, надо выполнять.
Первая мысль — отключить ограничение времени выполнения скрипта. Запрещено хостером.
Вторая мысль — яваскриптом повторять аякс-запрос периодически (да хоть раз в секунду). — нельзя (требование заказчика).
Выяснилось, собственно, что и браузер открыт не должен быть, и крон нельзя использовать, и работать скрипт должен независимо от пользователя, бесконечно долго. Естественно, он должен минимум грузить систему.
1. Пачка сигарет, ночь, гугл, доки, книги, мануалы….
goto 1…
На выходе получаю:
Задача_1:
Реализовать генератор времен выполнения скрипта, исходя из заданных количества раз и количества часов. Хранить где-то эти времена.
Задача_2:
Работать после закрытия браузера
Задача_3:
Не вылетать после окончания ограничения времени выполнения скрипта
Задача_4:
Выполнять в нужное время какие-то действия.
Итак…
Пишем в конфиге исходные данные:
Далее пишем функцию, которая поможет нам сгенерировать времена запуска.
В ней мы генерируем случайное число от 0 до количества секунд в исходном интервале.
Далее сгенерируем и запишем в сессию массив времен запуска. Предварительно отсортируем массив по возрастанию, чтобы сначала шло раннее время (машину времени я еще не успел создать).
Теперь надо заставить скрипт работать, не обращая внимания на максимальное время выполнения, установленное сервером.
Принцип таков:
1) Определяем время начала работы скрипта;
2) Определяем установленное ограничение на время выполнения.
3) Запускаем цикл, внутри которого считаем текущее время и вычисляем общее время работы скрипта, сверяем текущее время со значениями в массиве времен запуска, и если совпадение есть, выполняем заданные действия (у меня они в файле exec.php). Для запуска файлов используем сокеты.
4) Повторяем цикл пока время работы скрипта не приблизится к максимально разрешенному. Я поставил — пока до максимального времени не останется 5 секунд.
Итак… считаем начальные данные по времени:
Собственно, цикл. Комментарии в коде.
Ну и, если разрешенное время подходит к концу, то завершаем цикл и благополучно запускаем этот же скрипт другие процессом (в 5 секунд точно уложимся)
Собственно, готово.
Далее у меня много заморочек было в выполнении тех самых действий — там надо было робота написать для поиска ссылок по заданным ссылкам.
Когда дописал все, озадачился полезным применением…Использовать его можно как службу. Он может следить за чем-то в сети и уведомлять Вас, например, по почте. И не надо никаких cron’ов.
Скрипт можно еще оптимизировать — доработкой не занимался.
Кстати, вот от чего я не смог оторваться — браузер все же придется открыть, чтобы изначально запустить скрипт.
Запуск PHP скрипта по расписанию cron. Когда не всё так ясно
В этой статье я расскажу о некоторых тонкостях запуска php-скриптов на хостингах, незнание которых может попортить немало нервов и начинающим программистам, и мастерам средней руки.
Причина написания статьи: проблемы с запуском скриптов на хостингах с разными настройками. А поскольку настройки могут быть разными, информация приводимая для общих случаев могут не подходить и приводить в заблуждения.
Немного теории по этим ссылкам: тут и тут, для тех хочет освежить память.
Случай первый
В настройках операционной системы не указаны пути по умолчанию. Как следствие следующая команда в cron не будет выполнена.
Правильной командой будет второй вариант, где мы пропишем полный путь до интерпретатора php.
Есть ещё несколько способов запуска php скрипта описанных здесь. Интересным будет здесь то, что php скрипт запускается как файл с командами для консоли и тут можно написать целую тучу команд и описать всевозможные варианты на любой вкус. Код выглядит так.
В команде для выполнения в cron прописывается путь к скрипту и только. В скрипте ставятся символы #!, а дальше просто пишем нужные нам команды на языке bash.
Случай второй
Выполнение скрипта при запросе из браузера приводит к выводу страницы в браузер. А при выполнении скрипта через cron приводит к выводу текста страницы в командную строку. Тут может быть несколько вариантов. Система может быть настроена на сохранение результатов вывода в консоль в виде файла. Причем файл этот может размешаться не в самом типичном месте. Постепенно это может забить всё пространство на диске. Часто под сайт дают место в 1 Гигабайт, 500 мегабайт. И даже встречались хостинги с 50 и 10 мегабайт под сайт.
Как вариант, вывод может быть перенаправлен на почтовый ящик, который заботливый хостер ненавязчиво подарил вам и прописал в настройках хостинга как email по умолчанию. При каждом выполнении скрипта весь текст, выводящийся в консоль, будет оформлен в письмо. Проблемы могут начаться неожиданно. Если задание cron выполняется часто, а у почты хостинга прописано ограничение на количество писем в день, почта просто ляжет (заблокируется провайдером как потенциальный спамер). И как неприятные последствия вы получите отказ в регистрации пользователей, уведомление пользователей и д.р., что подвязано на почту.
Решение старо как мир. Нужно сделать перенаправление вывода из консоли в пустоту. Делается это добавлением команды в конце команды крона.
Иногда админы хостинга берут на себя обязанность ненавязчиво поставить их за пользователя. Тут тоже может быть подводный камень.
Случай третий
Ситуация проста. Нужно отладить скрипт, запускаемый планировщиком. Можно попытаться сделать это средствами php, заставлять скрипт писать логии и т.п. Но есть способ куда проще, нужно перенаправить вывод в файл. Команда проста, дополнительный параметр к нашей команде:
Её надо добавить в конце команды:
Знак «>» указывает системе о перенаправлении вывода. Далее имя файла. В нашем случае указан абсолютный путь. Этот пример не составляет труда найти в интернете. Но тут нас может поджидать неприятность, вытекающая из второго случая. Заботливый хостер автоматически добавляет перенаправление вывода в конце нашей строки. И иногда маскирует это. В итоге получается команда вида:
В итоге вывод снова перенаправлен в пустоту и выходной файл будет пуст. Тут хостеру можно указать на его ошибку, что он уж слишком перехитрил с настройками. А можно сразу воспользоваться костылём. После команды перенаправления в файл закончить команду символами &&. Эти два символа используются в командной строке для объединения нескольких команд в одной строке. Они дают командной строке понять, что команда окончена и дальше идет следующая команда. К ней и применяется перенаправление в пустоту. В итоге и перенаправление в пустоту осталось и лог файл записан верно. Пример команды:
Случай четвёртый
Первое, что находишь в интернете по этой проблеме – совет прописать в кроне команду смены директории:
Но в каких-то случаях это не помогает. Выход есть. Один из них взять всё в свои руки и задать недостающее окружение для работы скрипта. Информации про это в интернете уже больше.
Иногда просто хватает вписать следующий код в начале скрипта и пути снова становятся рабочими.
Как видите, всё прописано функциями и утруждаться настройками не надо.
Предисловие переводчика
Данная статья является вольным переводом-пересказом поста The End of Autoloading
. Оригинальная статья не первой свежести, поэтому код приведенный в примерах может быть не актуален. В теме, которую затрагивает статья, самое главное — общий взгляд, а не конкретные примеры.
Предисловие
Автозагрузка в PHP отлично экономит время. Это позволяет писать скрипты не задумываясь о путях к библиотекам, которые вы используете. Но с приходом неймспейсов и под влиянием Java-стиля современных фреймворков ситуация изменилась. В ближайшем будущем автозагрузка будет повсеместно, но без единой выгоды старого ее стиля.
До автозагрузки. Пути к файлам.
До появления автозагрузки, в каждом скрипте необходимо было указывать пути к используемым библиотекам. Исходный код выглядел так:
Зависимости явно прописывались в начале каждого скрипта. В этом примере зависимость очевидна, даже если вызов PEAR_DependencyDB находится на 328 строке.
Приход SPL-автозагрузки.
Позже появилась функция spl_autoload_register(), позволившая убрать вызовы require/require_once. Замечательным было то, что теперь появилась возможность использовать классы без необходимости знания где они расположены. Например:
Смотрите, здесь нет ни единого вызова require/require_once, при том, что этот класс зависим от sfActions, PostPeer, и Criteria классов. Разработчики смогли заниматься бизнес-логикой, не тратя время на поиск путей зависимостей. Это было действительно удобно.
Реализации автозагрузки.
Реализации автозагрузки варьируются. Некоторые библиотеки используют список всех классов, которые нужно подключить. Например:
Эта техника позволила скрыть пути к классам, но заставила разработчика библиотеки обновлять “карту” автозагрузки, каждый раз, при добавлении нового класса. Другая техника использует проход по структуре папок проекта в поиске нужного класса. Такой подход позволил подменять классы фреймворка своими, т.к. проход по папкам происходил в определенном порядке: пользовательские папки, проекта, плагинов и фреймворка. Таким образом разработчик мог создать класс ClassName, который подменит ClassName предоставленный плагином, т.к. загрузится раньше.
Автозагрузка с неймспейсами
Приход неймспейсов изменил техники автозагрузки. Авторы фреймвроков объединились, что бы унифицировать техники автозагрузки, для возможности технического взаимодействия между различными библиотеками. Было решено, что явное лучше неявного и полное имя класса будет относительным путем к файлу.
Были выработаны принципы именования и файловой структуры, а также реализация класса SplClassLoader. Этот подход сейчас используется практически во всех современных фреймворках. Пример кода:
Здесь все также нет require благодаря автозагрузке. Автозагрузчик ищет Symfony\Framework\WebBundle\Controller класс в файле Symfony/Framework/WebBundle/Controller.php.
Все хорошо, все довольны. Зависимости явные и подмена класса происходит легко:
Конец все удобствам.
Использование use в предыдущем примере вам ничего не напоминает? Это ведь очень похоже на старый добрый require/require_once, не так ли?
Привнесенная неймспейсами многословность в первую очередь снижает легкость использования автозагрузки. Но проблема не только в том, что нужно писать больше кода, в этом вам может помочь IDE с автодополнением, а в том, что вам нужно знать полные имена нужных вам классов. Вы должны очень хорошо знать классы фреймворка, чтобы использовать их. Это шаг назад по сравнению с автозагрузкой “первого поколения”, где было достаточно знать имя класса.
Другого пути нет.
Правда было бы замечательно, использовать современные библиотеки без знания их файловой структуры? Что если бы вы могли написать контроллер так:
Умный автозагрузчик перехватил бы вызов класса Controller, загрузил бы файл Symfony/Framework/WebBundle/Controller.php и динамически создал алиас с Symfony\Framework\WebBundle\Controller на Controller. К сожалению в PHP use создает алиас во время компиляции, поэтому такой ход не сработает. Конечно есть возможность сделать подобное используя eval, но это, наверное, даже хуже, чем подключать файлы вручную. Также создание таких алиасов при работе с фреймворком не возможно по причине конфликта с ленивой загрузкой и конфликта имен классов, например:
Symfony\Framework\WebBundle\Command
и
Symfony\Components\Console\Command\Command
Пока авторы фреймворков не изменят свой взгляд на автозагрузку, будущее PHP мне видится многословным.
Решение проблемы.
Лично я, думаю, что многословность сильно замедляет разработку. Например возьмем микрофреймворки – они дают возможность обработать запрос быстро, при минимуме MVC-разделения. Сравним код примерного приложения написанного с использованием Slim (автозагрузка без неймспейсов) и Silex (автозагрузка с неймспейсами):
Во втором примере, автозагрузка делает все только сложнее.
Разработчики современных фреймворков объясняют, что многословность – это цена, которую мы платим за качество кода. Я не уверен, что хочу платить эту цену. Я не хочу видеть как PHP превращается в Java, где код превосходен с точки зрения Computer Science, но очень затратен в написании. Это побуждает желание к использованию других языков, где вопрос автозагрузки с неймспейсами не стоит и при этом, быстрая разработка возможна.
Возьмем например Ruby. Существует такой фреймворк как Sinatra, используя который HelloWorld-приложение становится очень лаконичным:
Ой, смотрите, здесь же используется require! И при этом, все пишется очень быстро и легко в использовании.
Запуск скрипта в определенное время
Собственно, как реализовать автозапуск скрипта?)
Помощь в написании контрольных, курсовых и дипломных работ здесь.
Запуск скрипта в определенное время.
Можно ли сделать так, чтобы мой скрипт запускался в определенное время. Т.е. мне нужно, чтобы.
Запуск скрипта в определенное время
Подскажите по сабжу плиз. например как сделано при отправке открыток, те отправить через день.
Выполнение PHP скрипта в определенное время
Требуется в определенное время выполнять скрипт, подскажите хотя бы в каком направлении думать.
KOPOJI, есть куча вариантов задач, которые должны выполниться независимо от того, есть на сайте пользователи или нету.
Ну а сервисы просто альтернатива для тех, кто не умеет сделать задачу для cron.
Или кому влом каждый раз настраивать крон при переезде с хостинга на хостинг.
Не касательно данной конкретной задачи, просто альтернатива системному крону.
впечатления, зачастую, обманчивы. Вот, к примеру, piminov с тобой не согласен 🙂
Помощь в написании контрольных, курсовых и дипломных работ здесь.
Автоматическое срабатывание php-скрипта в определенное время
Здравствуйте. Нужна помощь новичку. Не могу разобраться как реализовать фоновое выполнения.
Выполнение PHP скрипта в определенное время указанное пользователем в форме
Требуется чтобы в форме клиент выбирал удобное для него время обратного звонка (отдельно часы и.
Срабатывание скрипта через определенное время
Подскажите как можно сделать так что бы определенный скрипт срабатывал после определенного времени.
Запуск скрипта в определенное время
Нужно каждый час из global.asax запускать скрипт. Как такое можно реализовать?
Запуск скрипта через определенное время
как сделать так, чтоб скрипт раз в минуту сам себя перезапускал? и делал это 100 раз? sleep нельзя.
Запускаем PHP-скриптики через php-fpm без web-сервера. Или свой FastCGI-клиент (под капотом)
Приветствую всех читателей «Хабра».
Дисклеймер
Статья получилась довольно длинная и тем кто не хочет читать предысторию, а хочет перейти сразу к сути прошу прямиком к главе «Решение».
Вступление
В данной статье хотелось бы рассказать о решении довольно нестандартной задачи, с которой пришлось столкнуться во время рабочего процесса. А именно, нам понадобилось запускать в цикле кучу php скриптов. О причинах и о спорности подобного архитектурного решения в данной статье распространяться не буду, т.к. собственно она и не про это вовсе, просто была задача, ее нужно было решить и решение показалось мне достаточно интересным чтобы им поделиться с Вами, тем более манов по данному вопросу в интернете я не нашел совсем (ну разумеется кроме официальных спецификаций). Спеки конечно это хорошо и в них конечно все есть, но думаю вы согласитесь, что если вы не особо знакомы с темой, да и еще и ограничены по времени то разбираться в них то еще удовольствие.
Для кого эта статья
Для всех кто работает с web-ом и о протоколе FastCgi знает лишь что это протокол в соответствии с котороым web-сервер запускает php скриптики, но хочет более детально его изучить и заглянуть под капот.
Обоснование (зачем эта статья)
Но при запуске каждого скрипта, будет создаваться окружение, запускаться отдельный процесс, в общем как то затратно по ресурсам нам показалось. Данную реализацию отвергли. Второе что пришло на ум это конечно же php-fpm, он ведь такой крутой, всего один раз запускает окружение, следит за памятью, все там логирует, корректно запускает и останавливает скрипты, в общем все делает круто, и нам конечно же этот путь понравился больше.
Но вот незадача, в теории то мы знали как это работает, в общих чертах (как оказалось в очень общих), но вот реализовать этот протокол на практике без участия web-сервера оказалось довольно трудно. Чтение спецификаций и пару часов безуспешных попыток показали что для реализации потребуется время, которого у нас на тот момент не было. Манов по реализации данной затеи, в которых было бы просто и понятно описано данное взаимодействие не нашлось, спеки наскоком взять тоже не удалось, из готовых решений нашли питоновский скрипт и пыховскую либу на гитхабе, которую в итоге не захотели тащить к себе в проект (может это и не правлиьно но не особо мы любим всякие сторонние библиотеки да еще и не очень то и популярные, а значит и не проверенные). В общем по итогу от этой идеи мы отказались и реализовали все это через старых добрый rabbitmq.
Хоть задачу в итоге и решили, но разобраться в FastCgi детально я все таки решил, и в добавок решил написать об этом статью, в которой будет просто и подробно описано как заставить php-fpm запустить php скрипт без web-сервера, а точнее в качестве web-сервера будет другой скрипт, далее его буду называть Fcgi клиент. В общем надеюсь что данная статья поможет тем кто столкнулся с такой же задачей как и мы и прочитав ее сможет быстро все написать как ему надо.
Творческий поиск (ложный путь)
Итак проблема обозначена, надо приступать к решению. Естественно как любой «нормальный» программист для решения задачи, про которую ни где не написано что делать и что вводить в консоль, я не стал читать и переводить спецификацию, а сразу же придумал свое «гениальное» решение. Суть его в следующем, я знаю что nginx (мы используем nginx и чтобы не писать далее дурацкое — web-сервер, буду писать nginx, так как то посимпатичнее) что то передает в php-fpm, это что то php-fpm обрабатывает и на основе него запускает скрипт, что ж вроде все просто, возьму да залогирую то что передает nginx и передам то же самое.
Тут поможет великолепный netcat (UNIX-утилита для работы с сетевым трафиком, которая по моему может практически все). Итак ставим netcat на прослушивание локального порта, а nginx настраиваем на работу с php файлами через сокет (естественно сокет на том же порту который слушает netcat)
Проверить что все ок, можно обратившись через браузер на адрес 127.0.0.1:9000 должна быть следующая картина
настраиваем nginx чтобы он php скрипты обрабатывал через сокет на 9000 порту (в настройках ‘/etc/nginx/sites-available/default’, конечно могут отличаться)
После этих манипуляций проверим что же получилось, обратившись к php скрипту через браузер
Видно что nginx отправил переменные окружения, а также непечатаемые символы, то есть данные были переданы в двоичной кодировке, а это значит что так просто их нельзя скопировать и послать в сокет php-fpm. Если сохранить их в файл например то они сохраняться в 16-ричной кодировке, выглядеть это будет примено так
Но это тоже мало что нам дает, наверное чисто теоретически их можно перевести в двоичную кодировку, каким то образом (даже не представляю каким) их отправить в сокет fpm, и даже есть вероятность что весь этот велосипед как то сработает, и даже запустит какой то скрипт, но уж как то все это страшненько и кривенько.
Стало ясно что данный путь совершенно неверный, сами видите насколько все это убого выглядит, и тем более все эти действия не позволят нам управлять соединением, и ни как не приблизят к пониманию взаимодействия между php-fpm и nginx.
Все пропало, изучения спецификации не миновать!
Решение (тут собственно начинается вся соль данной статьи)
Теоретическая подготовка
Давайте теперь рассмотрим как же все таки происходит соединение и обмен данными между nginx и php-fpm. Немного теории, все общение происходит как уже понятно через сокеты, далее будем рассматривать конкретно соединение через TCP сокет.
Единицей информации в протоколе FastCgi является cgi запись. Такие записи сервер отправляет приложению и точно такие же записи получает в ответ.
Немного теории (структуры)
Далее рассмотрим структуру записи. Для понимания из чего состоит запись нужно понимать что из себя представляют Си подобные структуры и понимать их обозначения. Для тех кто не знает далее это будет кратко (но достаточно для понимания) описано. Описать постараюсь как можно проще, в детали углубляться тут нет смысла, да и боюсь что в деталях запутаюсь, главное чтобы было общее понимание.
Структуры представляют собой просто напросто набор байтов, и нотацию к ним позволяющую их интерпретировать. То есть у вас есть просто последовательность нулей и единиц, и в этой последовательности зашифрованы какие то данные, но пока у вас к этой последовательности нет аннотации то эти данные для вас не представляют никакой ценности, т.к. интерпретировать их вы не можете.
Что тут видно, у нас есть некоторые биты, что это за биты мы понятия не имеем. Ну давайте попробуем например их разделить на байты и представить в десятичной системе
Отлично мы интерпретировали их и получили какие то результаты, допустим что эти данные отвечают за то сколько определенная квартира должна за электроэнергию. Получается что в доме 222 квартира номер 2 должна заплатить 88 рублей. А что еще за две цифры, что с ними делать просто отбросить? Конечно нет! дело в том что мы не имели нотации (формата) которая подсказала бы нам как интерпретировать данные, и интерпретировали их по своему, в связи с этим получили не только бесполезный, но и вредный результат. В итоге квартира 2 заплатила совершенно не то что должна была. (примеры конечно надуманные и служат лишь для того чтобы более понятно объяснить ситуацию)
Теперь посмотрим как же мы должны были интерпретировать правильно эти данные, имея нотацию (формат). Далее буду называть вещи своими именами, а именно нотация = формат (вот тут форматы).
Теперь все сходиться в доме №222 квартира 600 за электричество должна 1000 рублей Думаю теперь ясна важность формата, и теперь понятно как примерно выглядит условно Си подобная структура. (прошу обратить внимания, тут цель не детально объяснить что такое эти структуры, а дать общее понимание что это такое и как это работает)
Условное обозначение данной структуры будет такое
Еще немного теории (FastCgi записи)
Как я уже сказал выше единицей информации в протоколе FastCgi являются записи. Записи сервер отправляет приложению и такие же записи получает в ответ. Запись состоит из заголовка и тела с данными.
Далее идет само тело записи:
Вот пример самой простой FastCgi записи в двоичном виде с форматом
Практика
Скрипт клиент и передающий сокет
Для передачи данных будем использовать стандартное php расширение socket. И первое что нужно будет сделать — это настроить php-fpm на прослушивание порта на локальном хосте, например 9000. Это делается в большинстве случаем в файле ‘/etc/php/7.3/fpm/pool.d/www.conf’, путь конечно зависит от настроек вашей системы. Там нужно прописать примерно следующее (всю портянку привожу чтобы можно было сориентироваться, главная секция здесь listen)
После настройки fpm, следующим этапом будет подключение к сокету
Начало запроса FCGI_BEGIN_REQUEST
Для открытия соединения мы должны отправить запись с типом FCGI_BEGIN_REQUEST = 1 Заголовок записи будет такой (для приведения числовых значений к бинарной строке с заданным форматом будет использована php функция pack())
Тело записи для открытия соединения должно содержать роль записи и флаг управляющий соединением
Итак запись для открытия соединения успешно отправлена, php-fpm ее примет и далее будет ожидать от нас дальнейшей записи в которой нужно передать данные для разворачивания окружения и запуска скрипта.
Передача параметров окружения FCGI_PARAMS
В данной записи мы будем передавать все параметры которые нужны для разворачивания окружения, а так же имя скрипта который нам надо будет запустить.
Минимальные необходимые параметры окружения
Первое что нам тут нужно сделать — это подготовить необходимые переменные, то есть пары имя => значение, которые мы передадим приложению.
Структура пар имя значение будет такая
Идет сначала 1 байт — длинна имени, потом 1 байт значение
В нашем случае и имя и значения короткие и подходят под первый вариант, по этому его и будем рассматривать.
Закодируем наши переменные в соответствии форматом
Получение ответа FCGI_PARAMS
Собственно после того как все предыдущее проделано, и приложению отправлено все что оно ожидает, оно начинает работу и нам остается только забрать результат этой работы из сокета.
Помним что в ответ мы получаем такие же записи и нам их тоже нужно интерпретировать.
Получаем заголовок, он всегда равен 8 байт (получать данные будем по байту)
Теперь в соответствии с полученной длинной тела ответа сделаем еще одно чтение из сокета
Ура все сработало! Наконец то!
Что мы имеем в ответе, если например в этом файле
то в ответе получим в итоге
Итоги
Много тут не буду писать итак статья длинная получилась. Надеюсь она кому то поможет. И приведу сам итоговый скрипт, он получился совсем небольшой. Конечно он в таком виде довольно мало может, и в нем нет обработки ошибок и всего этого, но ему это и не надо, он нужен как пример, чтобы показать основы.