Временный файл

oracloid

совсем кукус
Временный файл

Задача такая: для экономии оперативки я перехватываю всю выдачу скрипта и скидываю порциями в файл, очищая при этом буфер. В конце работы файл надо выдать пользователю. Файлы могут получаться большими (> 100 Мб).

Я вначале сделал работу с файлом через tmpfile(), чтобы не парицо в случае фатальных ошибок скрипта. Возникновение таких ошибок я не могу отследить, т.к. используются лямбда-функции, и что там в них за код неизвестно (он не мой). То есть у меня бывают ошибки парсинга, которые не может перехватить set_error_handler().

В случае успеха я отдаю файл так:
PHP:
fseek($this->swap_handle, 0);
echo fread($this->swap_handle, $this->swap_size);
И вот это echo мне очень не нравится!
В данном случае файл будет сначала зачитан в память, во временную переменную, и только потом выдан stdout.

Альтернатива - использовать readfile(), которая сразу выдает файл в поток.
Но тогда я не защищен от кучи временных файлов, которые останутся при аварийном завершении скрипта.
Каждый раз при старте скрипта искать и удалять такие файлы? А по каким критериям я пойму, что файл надо удалить? По дате? Но за несколько минут таких файлов может получиться очень много, а диски на сервере не резиновые.

Просто тупо делать foreach( glob() ) @unlink()? Открытые файлы не удалятся...

Какие есть мысли?
 

oracloid

совсем кукус
Большие отчеты (десятки Мб), и различные выгрузки данных в разных форматах. Мне нужно чтобы их формирование не пожирало ресурсы, т.к. эти процессы запускаются параллельно в большом количестве.
 

Yuriy_S

-=PHP-Club=-
При параллельной работе скриптов (в большом количестве) с данными такого объема ресурсы будут пожираться всегда. Вообще, PHP для решения подобной задачи выбран не совсем удачно.. Либо задача поставлена некорректно.
 

oracloid

совсем кукус
Yuriy_S
PHP прекрасно справляется, и кушает немного.
Самое узкое звено - выходной буфер.

CHEM_Eugene
ты не в теме
 

phprus

Moderator
Команда форума
oracloid
Возникновение таких ошибок я не могу отследить, т.к. используются лямбда-функции, и что там в них за код неизвестно (он не мой). То есть у меня бывают ошибки парсинга, которые не может перехватить set_error_handler().
А это как? Вроде-бы ошибка парсинга скрипта либо есть, либо ее нет, или она может быть плавающей?

Какие есть мысли?
Поставь перед этими скриптами nginx и он будет заниматься буферизацией ответов. В случае если в скрипте выскочит что-то типа parser error'а, то можно сделать так что php выдаст к примеру 500-й код ошибки, а nginx увидев это может отдать пользователю какую-либо страницу с нормальным сообщением об ошибке.
 

oracloid

совсем кукус
phprus
А это как? Вроде-бы ошибка парсинга скрипта либо есть, либо ее нет, или она может быть плавающей?
Говорю ж - анонимные функции - обработчики событий, которые создаются в рантайме. В них может быть вообще все, что угодно.

Поставь перед этими скриптами nginx и он будет заниматься буферизацией ответов.
Думал об этом. Но не могу этого сделать, т.к. все это крутится на сервере приложений Oracle, и там уже перед апачем стоит их webcache, их связку хрен разорвешь, они и апач переписали.
Да и потом, на своем сервере я может и сделал бы это, но система разворачивается у наших заказчиков (их много), у них разные платформы, и ваще я тогда работать перестану, только и буду эти прокси настраивать всем :)
 

phprus

Moderator
Команда форума
oracloid
Говорю ж - анонимные функции - обработчики событий,
Если анонимные функции создаются через create_function, то она в случае если не может создать функцию с таким кодом просто возвращает false и печатает ошибку:
PHP Parse error: * in ** : runtime-created function on line ***. Никакого прекращения работы всего скрипта не происходит.

я тогда работать перестану
А ты так и так работать перестанешь, так как костылестроение очень быстро сделает проект полностью не поддерживаемым. И с ростом числа существующих костылей потребность в новых костылях будет очень быстро возрастать.

Кстати можешь посмотреть на http://ru2.php.net/manual/ru/runkit.sandbox.php
 

oracloid

совсем кукус
phprus
если не может создать функцию с таким кодом просто возвращает false
Спасибо, в мануале об этом нет ни слова! (который в виде chm файла).
Но в любом случае там может быть любая бяка, вплоть до деления на ноль :)

Твою мысль про костылестроение не очень понял. Я описал не костыль, а фичу, которая очень экономит оперативку. А вся проблема из-за недостаточно полной реализации обработки исключений в языке. Если бы только set_error_handler() отлавливала все уровни ошибок! Ну и readfile() тоже хороша: могла бы принимать в качестве параметра хендл открытого файла :D

Напоминаю тему топика: как удалить временный файл в случае аварийного завершения скрипта, или, если этого сделать нельзя, то как лучше организовать зачистку?
При чем тут Runkit_Sandbox?
 

kode

never knows best
Автор оригинала: oracloid
как удалить временный файл в случае аварийного завершения скрипта, или, если этого сделать нельзя, то как лучше организовать зачистку?
это? http://ru2.php.net/manual/en/function.register-shutdown-function.php

Код:
register_shutdown_function(create_function("","echo '!!!!!!!!!!!!!!Fuck';"));

throw new Exception("Hello");

->

<br />
<b>Fatal error</b>:  Uncaught exception 'Exception' with message 'Hello' in PHPDocument2:5
Stack trace:
#0 C:\Program Files\Zend\ZendStudio-5.5.1\bin\php5\dummy.php(1): include()
#1 {main}
  thrown in <b>PHPDocument2</b> on line <b>5</b><br />
!!!!!!!!!!!!!!Fuck
-~{}~ 23.07.08 14:59:

в общемто соглашусь с тобой что ошибки реализованны через попу (это историческая проблема). Те надо либо полнстью переходить на исключения (с доработкой finally) либо полностью переходить на обычные ошибка (trigger_error). Потому-что эта мешанина (из-за большого кол-ва разработчиков: кому-то нравится обьектный подход, кому-то процедурный - каждый городит своё). Лично мне ближе исключения, гораздо более удобный (привычный) способ
 

oracloid

совсем кукус
kode, спасибо! Я про эту функцию совершенно забыл! :)
тему можно закрывать.

-~{}~ 23.07.08 15:10:

tony2001
Я tmpfile() и использовал сначала, но мне не надо удалять файл при fclose(), т.к. я его потом хочу выдать с помощью readfile().
Ты почитай первое сообщение.
 

tony2001

TeaM PHPClub
не надо его закрывать fclose().
вместо readfile() можно использовать fpassthru(), ты перечитай мануал.
 

phprus

Moderator
Команда форума
oracloid
Твою мысль про костылестроение не очень понял.
Обертки заставляющие это:
Возникновение таких ошибок я не могу отследить, т.к. используются лямбда-функции, и что там в них за код неизвестно (он не мой). То есть у меня бывают ошибки парсинга, которые не может перехватить set_error_handler().
работать это костыли. Нормальный код при своей работе не должен вызывать ошибок, а попытка замаскировать всплывающие ошибки чем-либо это и есть костыли.

Ну и readfile() тоже хороша: могла бы принимать в качестве параметра хендл открытого файла
Есть [m]fpassthru[/m].

как удалить временный файл в случае аварийного завершения скрипта
Аварийного завершения скрипта быть не должно. Ошибки должны обрабатываться штатно.

При чем тут Runkit_Sandbox?
При том, что тебе не удаление временных файлов при ошибке нужно делать, а запускать потенциально ошибочный код в отдельной песочнице, которая на основной код повлиять никак не может.
 

oracloid

совсем кукус
tony2001
вот оно, то что было нужно для полного счастья! :D
спасибо!!!

-~{}~ 23.07.08 15:25:

phprus
При том, что тебе не удаление временных файлов при ошибке нужно делать, а запускать потенциально ошибочный код в отдельной песочнице, которая на основной код повлиять никак не может.
Спасибо, пошел читать!

-~{}~ 23.07.08 15:28:

phprus
еще вопрос: а эта песочница полностью изолирована, или из нее можно работать с объектами основного скрипта?
 

phprus

Moderator
Команда форума
oracloid
еще вопрос: а эта песочница полностью изолирована, или из нее можно работать с объектами основного скрипта?
А вот этого я не знаю. Я с ней не работал, так как у меня пока что не было задачь которые требовали-бы выполнять потенциально ошибочный и опасный код. Но судя по ману ей можно передать объекты из основной программы, а так-же из основной программы можно получать доступ к переменным, которые находятся внутри песочницы.
 

oracloid

совсем кукус
phprus
Да, я уже нашел, там у объекта есть свойства, которые управляют доступом к объектам родителя.
Жалко только, что это PEAR, причем пакет довольно старый.

Но в принципе, ошибки в рантайм-функциях могут возникать только на этапе разработки конкретного отчета. На этапе пром.эксплуатации они уже будут отлажены.
Пожалуй, оставлю как есть.
(Обработчики вызываются на разные события построения отчета, например, в датасетах при проходе по записям, при всяких инициализациях и т.п.)
 
Сверху