Использование phar Stream для записи файлов PharData - программирование

Использование phar Stream для записи файлов PharData

Когда я попробую это:

$phar = new PharData('./phar.tar', 0, null, Phar::TAR);
$phar->addEmptyDir('test');

file_put_contents('phar://./phar.tar/test/foo.txt', 'bar');

Я получаю следующую ошибку:

Предупреждение: file_put_contents (phar://./phar.tar/test/foo.txt): не удалось open stream: Невозможно создать phar './phar.tar', расширение файла (или комбинация) не распознается или каталог не существует

Я могу использовать file_get_contents, но не должен работать файл_put_contents?


Чтобы сделать вещи еще более странными, я пробовал идею @hakre, предложенную в его комментарии, и она работает!

$phar = new PharData('./phar.tar', 0, null, Phar::TAR);
$phar->addEmptyDir('test');

if (file_exists('./phar.phar') !== true)
{
    symlink('./phar.tar', './phar.phar');
}

file_put_contents('phar://./phar.phar/test/foo.txt', 'bar');
var_dump(file_get_contents('phar://./phar.tar/test/foo.txt')); // string(3) "bar"
4b9b3361

Ответ 1

Введение

Я думаю, что это в основном связано с тем, что вы работаете с Phar::TAR файлами, потому что

file_put_contents("phar://./test.tar/foo.txt", "hello world");
                                  ^
                                  |+---- Note this tar

Возвращает

Предупреждение: file_put_contents (phar://./test.tar/foo.txt): не удалось открыть поток: не удается создать phar './test.tar', расширение файла (или комбинацию) не распознано или каталог не существует

Пока

file_put_contents("phar://./test.phar/foo.txt", "hello world"); // Works file
                                  ^
                                  |+---- Note This phar

Возвращает

//No Errors

Знать проблему

Если вы посмотрите на подробный пример в документе PHP, это была известная проблема для прошлого 2 years и приведенный пример следующие

Пример

$p = new PharData(dirname(__FILE__) . '/phartest.zip', 0, 'phartest', Phar::ZIP);
$p->addFromString('testfile.txt', 'this is just some test text');

// This works
echo file_get_contents('phar://phartest.zip/testfile.txt');

// This Fails
file_put_contents('phar://phartest.zip/testfile.txt', 'Thist is text for testfile.txt');

$context = stream_context_create(array(
        'phar' => array(
                'compress' => Phar::ZIP
        )
));

// This Fails
file_put_contents('phar://phartest.zip/testfile.txt', 'Thist is text for testfile.txt', 0, $context);

// This works but only with 'r' readonly mode.
$f = fopen('phar://C:\\Inetpub\\wwwroot\\PACT\\test\\phartest.zip\\testfile.txt', 'r');

Рабочая альтернатива

Не используйте .tar или .zip имеет свой файл extension, но установите сжатие, как показано в PHP DOC

Что я имею в виду?

$context = stream_context_create(array(
        'phar' => array(
                'compress' => Phar::GZ //set compression
        )
));

file_put_contents('phar://sample.phar/somefile.txt', "Sample Data", 0, $context);
                                   ^
                                   |= Use phar not tar

Совет

Я действительно думаю, что вы должны придерживаться PharData реализуемо и доказано, что оно работает. Единственный способ сделать

Я должен использовать file_put_contents

Затем напишите свою собственную обертку и зарегистрируйте ее с помощью stream_wrapper_register, вот простой Prof of Concept

stream_wrapper_register("alixphar", "AlixPhar");
file_put_contents('alixphar://phar.tar/test/foo.txt', 'hey'); 
                         ^
                         |+-- Write with alixphar

echo file_get_contents('alixphar://phar.tar/test/foo.txt'),PHP_EOL;
echo file_get_contents('phar://phar.tar/test/foo.txt'),PHP_EOL;

Выход

hey     <----- alixphar
hey     <----- phar

Примечание. Этот класс не должен использоваться в процессе производства... Это просто пример и не удалось выполнить некоторые тестовые примеры

class AlixPhar {
    private $pos;
    private $stream;
    private $types;
    private $dir;
    private $pharContainer, $pharType, $pharFile, $pharDir = null;
    private $fp;

    function __construct() {
        $this->types = array(
                "tar" => Phar::TAR,
                "zip" => Phar::ZIP,
                "phar" => Phar::PHAR
        );
        // your phar dir to help avoid using path before the phar file
        $this->dir = __DIR__;
    }

    private function parsePath($path) {
        $url = parse_url($path);
        $this->pharContainer = $url['host'];
        $this->pharType = $this->types[pathinfo($this->pharContainer, PATHINFO_EXTENSION)];
        if (strpos($url['path'], ".") !== false) {
            list($this->pharDir, $this->pharFile, , ) = array_values(pathinfo($url['path']));
        } else {
            $this->pharDir = $url['path'];
        }
    }

    public function stream_open($path, $mode, $options, &$opened_path) {
        $this->parsePath($path);
        $this->stream = new PharData($this->dir . "/" . $this->pharContainer, 0, null, $this->pharType);
        if (! empty($this->pharDir))
            $this->stream->addEmptyDir($this->pharDir);

        if ($mode == "rb") {
            $this->fp = fopen("phar://" . $this->pharContainer . "/" . $this->pharDir . "/" . $this->pharFile, $mode);
        }
        return true;
    }

    public function stream_write($data) {
        $this->pos += strlen($data);
        $this->stream->addFromString($this->pharDir . "/" . $this->pharFile, $data);
        return $this->pos;
    }

    public function stream_read($count) {
        return fread($this->fp, $count);
    }

    public function stream_tell() {
        return ftell($this->fp);
    }

    public function stream_eof() {
        return feof($this->fp);
    }

    public function stream_stat() {
        return fstat($this->fp);
    }
}

Ответ 2

У меня есть несколько ответов для вас - надеюсь, это поможет вам.

Прежде всего, с file_put_contents(), я заметил некоторую странность. Во-первых, что я сделал, следуйте примеру ссылки, которую вы опубликовали. Я смог добавить переменную $context, но все еще имел ошибку. Что сработало для меня, это изменить имя файла на phar.phar. Возможно, обработчик phar://не может понять расширение .tar?

Во всяком случае, это рабочий код - , но я не рекомендую его

$context = stream_context_create(array('phar' =>
                                array('compress' => Phar::GZ)),
                                array('metadata' => array('user' => 'cellog')));

file_put_contents('phar://./phar.phar/test/foo.txt', 'bar', 0, $context);

Вместо этого используйте это.

Использовать меня - рекомендуется

$phar = new PharData('./phar.tar', 0, null, Phar::TAR);
$phar->addEmptyDir('test');
$phar->addFile('./bar', 'foo.txt');

Если вам действительно нужно использовать file_put_contents(), возможно, существует другая проблема, с которой мы можем помочь. Спасибо и удачи!

Ответ 3

У меня есть пара других альтернативных подходов, которые, я думаю, немного более аккуратны. Они избегают пользовательских оболочек, символических ссылок или создания дополнительных файлов, чтобы использовать функцию addFile.

Вариант A

Используйте функцию addFromString, которая работает аналогично file_put_contents. Код ниже создает TAR-архив, добавляет к нему файл и сжимает его.

$phar = new PharData("./archive.tar", 0, null, Phar::TAR);
$phar->addFromString('out/fileA.txt','The quick brown fox jumped over the lazy dog');
$phar->compress(Phar::GZ); # Creates archive.tar.gz

Вариант B

Если вам действительно нужно по какой-то причине использовать file_put_contents. Вы можете создать архив PHAR, используя его, затем повторно открыть и преобразовать в другой тип архива.

file_put_contents('phar://archive2.phar/out/fileB.txt', "Colourless green ideas sleep furiously");
$tarphar = new Phar('archive2.phar');
$tar = $tarphar->convertToData(Phar::TAR); # Creates archive2.tar
$zip = $tarphar->convertToData(Phar::ZIP); # Creates archive2.zip
$tgz = $tarphar->convertToData(Phar::TAR, Phar::GZ, '.tar.gz'); # Creates archive2.tar.gz