| Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате |
| Форум программистов > PHP: Для профи > Прервать скрипт и продолжить на следующем запуске |
| Автор: maxbrown 16.6.2008, 10:35 |
| Задача: при наступлении некоего if($condition) корректно прервать работу некоего PHP-скрипта, а при следующем запуске (возможно, через несколько недель) возобновить с той же точки. Начатое решение: написана функция save_variables(), которая при вызове с параметром - именем переменной - регистрирует эту переменную у себя в static-массиве, а при вызове без параметров сохраняет все переменные, заявленные ей при предыдущих обращениях, в xml-файл и вносит запись о нём в MySQL-базу. Прерывание реализуется в виде заключения всего скрипта в некую функцию MyMain(), в которой в нужном месте вставляется: if ($condition) return save_variables(); Функция обратной заргузки сохранённых переменных пока не написана, но проблем с этим нет. А вот, с чем проблема есть: Проблема: Я не могу придумать элегантного решения для возврата в точку прерывания (было бы здорово использовать GOTO, но в PHP нет GOTO) и прошу помощи Клуба. UPD1: Предложены решения на основе паттерна State, суть которых сводится к модификации всех операторов ветвления/цикла в скрипте, чтобы в зависимости от наличия/отсутствия точки сохранения (она же "метка", она же "запись состояния") они либо выполнялись, либо пропускались скриптом на пути к нужной метке. Недостаток: по сути, это требует как минимум синтаксического анализа всего скрипта, что практически не поддаётся автоматизации. Пояснение: преобразованию в "прерывабельно-возобновибельные" подлежит заранее неизвестное большое количество заранее неизвестных скриптов на PHP. UPD2: Обсуждается вариант решения на posix_getpid и posix_kill WBR - Max Brown www.Wgent.com/kontakty.htm www.ObBot.com/contact.htm mail: "Max Brown <[email protected]>" (присутствие имени в поле To: обязательно!) ICQ: 6654627 (при запросе на ауторизацию указание причины обязательно!) Моб: +7-960-5213199 (Билайн) P.S. Приветствуются любые варианты (идеи, подсказки, в какую сторону копать), в том числе через команды Linux/Apache/PHP5. |
| Автор: Feldmarschall 16.6.2008, 11:18 |
| почитал комментарий в другую тему. впечатлился. |
| Автор: ArNic 16.6.2008, 11:22 | ||||
| Вот вычитал http://ili.com.ua/php/goto-in-php Goto нет в php, а иногда так хочется. “Не используйте оператор goto” – так твердили нам преподаватели в академии, и действительно оператор сей превращает код в полнейшую кашу. Поэтому разработчики php решили проблему кардинально – в php его просто нет. Но иногда очень хочется, чтобы он все таки был особенно когда пишешь обработчик какой-то формы где пользователь должен ввести сложный набор данных, а мы должны их обработать. Процесс этот проходить, как правило, в несколько этапов: печать самой формы, проверка полученных данных, предварительны показ, ну и например сохранение. Причем некоторые этапы могут повторятся : если проверка данных не прошла удачно переходим на печать формы, или если предварительный показ не устроил пользователя он может вернутся к вводу данных. Короче говоря некоторые куски кода могут задействоваться многократно. Использовать функции в этих условиях тоже не очень удобно – много входных и выходных переменных, функция должна выполнять слишком сложные действия, вообще коряво получается и читабельность кода резко падает. И я придумал такую вот конструкцию.
Здесь основной оператор множественного выбора switch заключен в бесконечный цикл do-while – таким образом мы группируем нужные действия в секциях case : break; и можем неограниченно переходить из одной секции к другой. В секции default: удобно сделать предварительный разбор данных например если входных данных нет то $action = ‘PRINT_FORM’ печатаем форму, если есть например $_POST [‘submit’] то тогда отправляем на обработку полученных данных $action = ‘CHECK_FORM’ . Передача управления между блоками switch происходит с помощью изменения переменной $action, ну а выход из цикла с помощью break 2; или exit; Так например в блоке печати основной формы можно смело поставить break 2; т.к вывод формы предполагает окончание скрипта. Конструкция очень гибкая и читабельная. Но есть один недостаток – если неправильно обработаны данные можно попасть в бесконечный цикл и скрипт зависает - это неприятно придется ждать целых 30 сек. Давайте установим ограничение на кол-во циклов оператора do-while, 20 хватит с головой. Теперь если мы что-то накасячили скрипт намотает 20 оборотов, остановится и вывалит предупреждение об екстренном останове. Отлаживать такую кострукцию очень удобно - достаточно после оператора swith распечатывать переменную $action и мы получим полную последовательность исполнения блоков.
Реальный обработчик формы конечно может быть намного сложней и обрабатывать данные в несколько форм-этапов. Хотя конечно можно все размазать по разным скриптам, и использовать функции для группировки кода тут уже дело вкуса. Добавлено через 3 минуты и 58 секунд кстати забыл сказать как нашел: в Google по поисковой фразе goto для php Добавлено через 7 минут и 53 секунды Да, и не забудь прочесть http://phpclub.ru/faq/goto |
| Автор: maxbrown 16.6.2008, 13:52 | ||
| ArNic, спасибо за код и ссылки, но Switch-Case не решает проблемы. К тому же, я вовсе не настаиваю на GOTO-решении, т.к. мне оно и самому не нравится. Утверждение: при помощи Switch мы не сможем перейти в нужную точку цикла. Пример: обработать несколько файлов, часть из которых на момент первого запуска скрипта может ещё не существовать. Обрабатывать один и тот же файл дважды - запрещается.
Плохое решение: function ProcessFiles($fnames, $i0=0) { for($i=$i0; $i<count($fnames); $i++) { Недостаток решения: ну Вы же понимаете, что на самом деле там не только for и не только $i. Там ещё впереди и вокруг тьма ветвлений, циклов etc. |
| Автор: sTa1kEr 16.6.2008, 14:04 |
| ArNic, Exceptions вам в помощь и не понадобятся никакие бесконечные циклы. |
| Автор: Feldmarschall 16.6.2008, 14:19 |
| maxbrown, мне неудобно столь маститому программисту указывать на детские ошибки, но проблема в данном случае происходит оттого, что путаются программа и данные. К программе применяется подход, как будто это данные. А решение заключается в том, что исполнение программы должно контролироваться входящими данными, а не программными ухищрениями. |
| Автор: maxbrown 16.6.2008, 14:41 |
| Feldmarschall, не такой уж я "маститый программист": как и у любого самоучки, у меня есть изрядные пробелы в знаниях. В частности, копнуть глубже Exceptions (sTa1kEr, спасибо за идею!) мне почему-то не пришло в голову. Что же касается "путания программы с данными", то к сожалению, в данном случае программа вообще является данными Но трабла в том, что пример с обработкой пока не существующих файлов - это всего лишь пример, а сама задача заключается именно в генерации заранее (sic!) неизвестных PHP-скриптов, которые обладали бы свойством прерывать по команде свою работу, а по другой команде - возобновлять её, как будто бы ничего не произошло. Если у Вас есть хорошая общая идея, как это можно реализовать, был бы за неё очень признателен. |
| Автор: krundetz 16.6.2008, 14:48 | ||
А причем здесь GOTO? У вас нет похоже четкого понимания зачем вообще эта конструкция применяется в других языках. Описываемая вами задача решается следующим способом: Создается точка остонова которая хранится на сервере допустим в базе данных. Это точка остонова является срезом системы в момент её остановки. При следующем запуске делаем проверку и если точка остонова имеется инициализируем ей состояние ситемы и начинаем работать. |
| Автор: maxbrown 16.6.2008, 14:58 |
| krundetz, GOTO ни при чём. Мне самому не нравится GOTO. Это лишь первый пришедший в голову, далеко не лучший и заведомо не существующий вариант решения А вот насчёт "Создается точка остонова ... точка остонова является срезом системы в момент её остановки... При следующем запуске инициализируем ей состояние ситемы и начинаем работать." - вот насчёт этого охотно почитал бы подробнее. Только надо поиметь в виду, что таких приостановленных задач в каждый момент времени может быть несколько, плюс ещё несколько работающих (не приостановленных), и заводить ради каждой из них виртуальный сервер будет неимоверно жирно. |
| Автор: krundetz 16.6.2008, 15:20 |
| maxbrown я не говорил про виртуальный сервер, я говорил про хранение точек отсанова в БД. Почитайте про паттерн Состояние, в вашей задаче это наиболее приближеное решение из известных мне. Насчет виртуального сервера. Если у вас продолжительность работы скрипта больше чем 30 секунд, точнее есть долгоиграющие скрипты скорее всего без этого не обойтись. Либо придется договариваться с хостерами в индивидуальном порядке. |
| Автор: maxbrown 16.6.2008, 17:35 |
| IZ@TOP, спасибо конечно, но я предпочёл бы обойтись без goto, тем более, что не факт, что мой провайдер перешёл на PHP5.3 krundetz, не подскажете ссылку точнее? А то я гуглил около часа, но нашёл только общие "языково-независимые" фразы, а конкретных примеров реализации pattern State на PHP так и не нагуглил. |
| Автор: krundetz 16.6.2008, 18:06 |
| Я тоже реализации его на PHP ни разу не видел но патnерн на то и паттерн что это некий каркас легко адаптируемый к любому языку в котором реализована OOP. Неплохим подспорьем может стать пример реализации этого паттерна на Java |
| Автор: yurik_l 16.6.2008, 18:17 |
| maxbrown, паттерн Состояние: http://www.fluffycat.com/PHP-Design-Patterns/State/ |
| Автор: maxbrown 16.6.2008, 19:32 |
| О! yurik_l, спасибо за пример. Теперь сразу всё стало понятно... И, к сожалению, стало также понятно, что этот паттерн в моём случае малопригоден: 1. Слишком уж в моём случае большое (а главное - заранее не известное!) количество состояний. Плюс внутри мы наблюдаем нечто ещё худшее, нежели Switch-Case: по сути, для каждого состояния описывается свой собственный метод, пусть и динамически присваеваемый! А у меня-то вместо State-зависимых методов - State-зависимые операторы! И получится в итоге вместо if($a==$b) { ... } конструкция вида if( this->GetSkipCondition("\$a==\$b") ) { ... }, а во что превратятся циклы foreach, я даже представить себе боюсь. 2. Я и так уже пошёл на серьёзную уступку самому себе, согласившись запихать код внутрь функции. Строго говоря, даже это было не вполне приемлемым. Втаскивать произвольный код ещё и внутрь объектов - это IMHO супер-крайняя мера, годится только если не придумаем ничего проще. 3. Блин, ну неужели нельзя придумать более простого способа? Задача-то была всего-навсего приостановить выполнение скрипта на какое-то длительное время... |
| Автор: krundetz 16.6.2008, 20:01 |
| Своими же словами подтверждаете что вам ничего не понятно. 1. Этот паттерн и направлен на то чтобы управлять неизвестным количеством состояний. 2. Я не говорил что это панацея я предлагал ознакомится. 3. Раз уж вы не додумались сами глядя на паттерн подкину вариант. у вас не будет никакого преостановления выполнения скрипта каждый раз скрипт будет отрабатывать по полной логике. Только в первом случае он по некоторому параметру прекратит работу сохранив предварительно все переменные которые он использует в базу данных. Далее при запуске этого скрипта вновь он будет проверять не сохранилось ли от предыдушего запуска что нибудь если сохранилось он должен все проанализировать и работать дальше. в вашем случае допустим всегда есть ограниченое количество случаев где при приходе этого параметра завершается работа скрипта тогда следоваетльно вы точно знаете что вам необходимо сохранять для того чтобы скрипт работал. Думаю дальшее понятно. Состояние ИМХО наиболее элегантный способ обойти такую ситуацию. |
| Автор: maxbrown 16.6.2008, 20:30 |
| "каждый раз скрипт будет отрабатывать по полной логике" - это и есть именно то, чего я хочу избежать. Я ведь правильно понимаю, вы предлагаете мне "прорубать насквозь" все встречающиеся на пути к Label1 логические операторы? if( !$skip && $condition ) вместо if($condition), elseif(!$skip) вместо else, for($i=$i0 и так далее. Вопрос на засыпку: как Вы себе представляете "прорубание" foreach? Наконец, просто представьте себе, что исходный код - чужой и сложный и разбираться в этом коде очень долго. А также, что таких исходных кодов очень много и единственное, что реально - это выцепить в этом коде определённый оператор, требующий приостановки скрипта и вставить несколько строчек до или после этого оператора. Ведь по сути код и будет чужим, а уж сложным он будет с гарантией, и если мне повезёт, то этих кодов будет к тому же действительно Много. Да, кстати, вы что-то говорили про время выполнения в 30 секунд. Это абсолютно нереально. Реально, в моём случае полное время выполнения задачи - несколько месяцев. При том, что сумма всех отрезков процессорного времени должна измеряться миллисекундами (скриптов, как я уже говорил, ожидается много). |
| Автор: krundetz 16.6.2008, 20:51 |
| Надо подумать ещё если придумаю подскажу. Насчект времени выполнения есть ограничение в 30 секунд которое стоит на время работы скрипта. Болшенство хостеров его оставляют без изменения некоторые даже уменьшают это чтобы на виртуальном хостинге где на одном сервере может висеть очень много сайтов процесорное время не расходовалось н плохо написаные и ресурсо емкие скрипты. Возможен ли вариант с использованием другого языка програмирования а также с использованием виртуального сервера? |
| Автор: maxbrown 16.6.2008, 21:30 |
| Другой язык - это сложно, т.к. исходники на PHP. А вот виртуальный сервер - это, похоже, вариант. Если ничего другого не останется. |
| Автор: IZ@TOP 17.6.2008, 12:48 |
| maxbrown, да, интересно было бы узнать что это за задача такая, требующая подобного извращения. Кстати, не вижу ничего плохого в том, что программа будет проходить вновь все участки в воспроизведенном состоянии до нужного логического блока. Программа, конечно, станет сложнее, но по иному, на мой взгляд, сделать практически нереально (если не брать в рассчет костыли и садо-мазо-извращения). А еще, на счет GOTO: можно было бы в программе, в тех местах, где это необходимо - генерировать метки и в состоянии записывать их идентификатор. После повторного запуска программы, если будет необходимо восстановить состояние, контроллер просто перекинет нас к нужной метке. |
| Автор: Fortop 17.6.2008, 13:56 |
| maxbrown, Механизм передачи команды скрипту проработан? Теперь что касается сохранения состояний. Изложу свои велосипедные изыскания по этому поводу, поскольку книг не читал. - Для того чтобы сохранить состояние необходимо скрипт разбить на участки кода максимально независимые друг от друга. (методы) - Для прерывания любого метода, в том числе и содержащего цикл foreach использовать все данные как очереди и стеки. Т.е. после обработки - удалять из массива данные. Таким образом при последующем возобновлении этого цикла, он будет обладать только необработанной информацией. Любой произвольный объект должен иметь свой UID. Для добавления возможности сохранения состояний потребуется так же генерировать "карту алгоритма". Возобновление работы Переход к точке останова должен осуществлять общий контроллер, который будет использовать сохраненное состояние последнего выполненного объекта и передаст управление на последний вызванный метод. Всю необходимую информацию он возмет из "карты алгоритма". К сожалению детально описать пока нет времени. Но нечно похожее (хотя и попроще), я использую, в некоторых местах, у себя в проекте. |
| Автор: Fortop 17.6.2008, 14:33 |
| Да, и... Если уж так боимся названия объект - можешь использовать обычные php файлы для каждого отдельного куска кода. В любом случае, при исполнении кода контроллером, будет происходить инициализация нужных ему(блоку кода) данных из сохраненного состояния и затем include этого блока кода. |
| Автор: maxbrown 17.6.2008, 17:08 | ||||||
Программы пишутся сотрудниками и далее транслируются на язык PHP. Сначала предполагалось, что к моменту запуска каждого скрипта все данные для него уже будут готовы и что скрипт успеет обработать все файлы за свой таймаут. Выяснилось, что во-первых, файлов может быть настолько много, что не успеет, а во-вторых, некоторые из этих файлов генерятся другими такими же программами и могут быть ещё не готовы, причём отслеживать готовность данных для дальнейшей обработки никому не интересно. PHP выбран потому, что он наиболее распространён и что в исключительных случаях можно вручную добавить уже в PHP-скрипт некоторые возможности, отсутствующие в исходном XML-языке.
Программы могут (точнее, почти наверняка будут) содержать линейные сегменты, не допускающие повторной обработки. Например, дозапись в какие-то файлы. |
| Автор: maxbrown 17.6.2008, 17:36 | ||
А вот здесь, пожалуйста, подробнее. Что за метки? Что за контроллер? Насколько "просто" перекинет? |
| Автор: krundetz 17.6.2008, 17:51 |
| Вы в любом случае не сможете обойтись без стека вызовов не обязательно его выполнять повторно просто пробегаясь по нему в ы придете в ту точку програмы которая вам нужна |
| Автор: Fortop 17.6.2008, 18:02 | ||||
По сути у тебя будут выполнятся не генерируемые скрипты, а один скрипт, который будет вызывать внутри себя по определенному алгоритму нужные файлы. Он же будет принимать команды, отвечать за сохранение текущего состояния и его загрузку. Что касается метки, то считай меткой уникальное имя файла. вызываются они в контроллере примерно так.
|
| Автор: maxbrown 17.6.2008, 23:01 |
| Друг подсказал по аське, что проблему возможно решить при помощи posix_getpid и posix_kill. Но он пишет в основном на си и питоне и не знает, как это реализуется в PHP. Если кто с посикс-расширениями работал, отзовитесь plz. |
| Автор: skyboy 18.6.2008, 09:50 | ||
и какой процесс ты собираешься прибивать? httpd апача? |
| Автор: Fortop 18.6.2008, 09:58 |
| maxbrown, какую из твоих проблем? Для того чтобы передать скрипту команду на завершение можно использовать нормальный способ, а не резать его на корню. Ты ведь состояние сохранить не успеешь, если воспользуешься советом друга. |
| Автор: Feldmarschall 18.6.2008, 10:02 | ||
Меня больше всего это восхищает. то есть, тут можно долго думать над решением практических проблем, но сама архитектура, когда кто-то пишет на хмл программы, эти программы транслируются в пхп, потом анализируются другим скриптом, сгенерированным из XML с дискретным прерыванием - поражает воображение и является отдельным законченным произведением искусства. |
| Автор: Fortop 18.6.2008, 10:12 |
Программы - звучит конечно слишком громко, но это могут банальные быть блоки расчетов Хотя реализация их на PHP.... но почему бы и нет? В принципе, я так понимаю, схема очень близка к распределенным вычислениям. Во всяком случае ее легко отмасштабировать на произвольное число модулей и на произвольный уровень вложенности. Нужно лишь чтобы каждый узел имел свой контроллер выполнения. |
| Автор: maxbrown 18.6.2008, 10:32 | ||
Fortop, posix_kill, несмотря на своё "страшное" название, используется не только для прибивания процессов на корню, но и для других целей в зависимости от передаваемого процессу сигнала. |
| Автор: Fortop 18.6.2008, 10:42 | ||
Вот как только ты расскажешь - как в PHP можно ловить и обрабатывать эти сигналы - я с тобой соглашусь. |
| Автор: maxbrown 18.6.2008, 10:51 | ||||
Не совсем так. Программы пишутся на XML - да, транслируются в PHP - да, потом анализируются - да, но не "другим скриптом, сгенерированным из XML", а просто другой программой. Компилятор XML->PHP доступен для редактирования (точнее, сам компилятор недоступен, но можно редактировать используемое в нем XSLT-преобразование) и в принципе, можно, переделав компилятор, перекомпилировать заново все исходные XML-программы, которые сохранились, а если что-то не сохранилось, то оставшиеся PHP-скрипты переписать уже вручную. Добавлено через 8 минут
Если бы я знал, я бы не спрашивал. Мне известно, что примерно таким способом на PHP пишут демонов, но эта область для меня пока terra incognita. |
| Автор: Fortop 18.6.2008, 11:10 |
Насколько я знаю - нет. Т.е. обмен сообщениями через названия файлов,через сами файлы, через сокеты, через базу - да, это реализуемо. Я почему и спрашивал, реализован ли у тебя механизм передачи команд скрипту. |
| Автор: Resha 23.10.2008, 12:03 | ||
Вы 100% пытаетесь сделать нечто, что вообще говоря вам можно и не делать (т.е., скорее всего, есть какое-то более стандартное решение). Представим себе работу по шитью шерстяных носков. Алгоритм выглядит примерно так: 1. Отмотать 100 м ниток. 2. Связать носок общей площадью 1000 см2. Выполняете программу, запоминая этап и его остояние: 1. Для первого этапа - длина отмотанной нитки. 2. Для второго - площадью навязанного носка. Если вдруг у вас случился сердечный приступ, то потом вы всегда сможете получить информацию, на чем вы остановились и сколько работы осталось сделать, чтобы перейти к следующему этапу. Задача утрирована, но в любом случае, при выполнении долгоиграющих скриптов все работает именно так Лично по моему опыту, после 2-3 часов работы скрипт может отпасть не выдав абсолютно никакой ошибки. Даже не знаю, по какой именно причине |
| Автор: kshyms 13.11.2008, 09:56 |
| IZ@TOP, Всякое новое это хорошо забытое старое! В фортране и бэйсике версии 1,2,3 он был еще в 80-90 годах. А в 6 версии PHP он тоже включен? goto - это полезный удобный операнд выхода из цикла или выполнения условия |
| Автор: bars80080 13.11.2008, 15:03 | ||||||
надо только помнить, что носки можно вязать спицами, а когда вам придут заказчики с мотивировкой связать носки для КрАЗа, им видити ли шины в холодную погоду надо защитить, то следует всё-такие задуматься, а может такие вещи делаются не из ниток и не спицами? это я к тому, что помимо гипертекстового процессора (php) есть другие механизмы, заточенные под соответствующие задачи
советую почитать это http://phpclub.ru/faq/goto |