Создание / запись файла PHP в деструкторе - PullRequest
5 голосов
/ 08 июля 2011

При вызове file_put_contents() внутри деструктора это приводит к записи файлов в SERVER_ROOT ... (Yikes!) Временные решения?

tldr:

Я хочу кэшировать массив, возможно, содержащий сериализованные экземпляры классов. На данный момент я решил написать класс, который реализует кэш с использованием unserialize()/file_get_contents() и serialize()/file_put_contents(), а затем скрыть его функциональность за более общим классом Cache. (Я не знаю, будет ли у моего клиента общая память или PEAR и т. Д.)

<?php
    class CacheFile {
        private $filename;
        private $data;
        private $dirty = false;

        function __construct($filename) {
            $this->filename = $filename;
            $this->load();
        }

        function __destruct() {
            // Calling file_put_contents within a destructor causes files to be written in SERVER_ROOT...
            $this->flush();
        }

        private function load() {
            if(!file_exists($this->filename)) {
                $this->data = array();
            }
            else {
                $this->data = unserialize(file_get_contents($this->filename));
                // todo
            }
            $this->dirty = false;
        }

        private function persist() {
            file_put_contents($this->filename, serialize($this->data));
            $this->dirty = false;
        }

        public function get($key) {
            if(array_key_exists($key, $this->data)) {
                return $this->data[$key];
            }
            else {
                return false;
            }
        }

        public function set($key, $value) {
            if(!array_key_exists($key, $this->data)) {
                $dirty = true;
            }
            else if($this->data[$key] !== $value) {
                $dirty = true;
            }
            if($dirty) {
                $this->dirty = true;
                $this->data[$key] = $value;
            }
        }

        public function flush() {
            if($this->dirty) {
                $this->persist();
            }
        }
    }


    $cache = new CacheFile("cache");
    var_dump( $cache->get("item") );
    $cache->set("item", 42);
    //$cache->flush();
    var_dump( $cache->get("item") );
?>

Видите вызов flush() в деструкторе? Я действительно не хочу иметь функцию public flush(), потому что она зависит от реализации.

Ответы [ 3 ]

8 голосов
/ 08 июля 2011

Я предполагаю, что вы не указали полный путь в $this->filename.

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

Сравните с соответствующей заметкой в ​​ Руководстве по PHP :

Примечание:

Деструкторам, вызываемым во время завершения работы скрипта, уже отправлены заголовки HTTP. Рабочий каталог на этапе завершения работы скрипта может отличаться для некоторых SAPI (например, Apache).

Если вы сделаете этот путь абсолютным, он будет работать как положено.

Редактировать: После обновления кода это простой способ убедиться, что вы получили абсолютный путь:

$cache = new CacheFile(realpath("cache"));

или намного лучше в пределах конструктор:

$this->filename = realpath($filename);
2 голосов
/ 08 августа 2011

Вы можете создать дескриптор файла в load(), который можно использовать в __destruct() или flush().

1 голос
/ 08 июля 2011

Используете ли вы относительный путь в качестве $ filename? Я бы передал абсолютный путь туда, куда вы хотите файл. Если вы хотите, чтобы он был относительно вашего сценария, вы можете использовать что-то вроде:

$filename = dirname($_SERVER['SCRIPT_FILENAME']) . PATH_SEPARATOR . $filename;
...