Я работаю над проектом PHP для командной строки и хочу воссоздать файл PHAR, который является моим артефактом развертывания. Проблема в том, что я не могу создать два PHAR с одинаковыми значениями, которые были созданы на расстоянии более 1 секунды друг от друга. Я хотел бы иметь возможность точно воссоздать мой PHAR-файл, если входные файлы совпадают (т. Е. Получены из одного и того же git commit).
Следующий фрагмент кода демонстрирует проблему:
#!/usr/bin/php
<?php
$hashes = array();
$file_names = array('file1.phar','file2.phar');
foreach ($file_names as $name) {
if (file_exists($name)) {
unlink($name);
}
$phar = new Phar($name);
$phar->addFromString('cli.php', "cli\n");
$hashes[]=sha1_file($name);
// remove the sleep and the PHAR's are identical.
sleep(1);
}
if ($hashes[0]==$hashes[1]) {
echo "match\n";
} else {
echo "do not match\n";
}
Насколько я могу судить, поле «время модификации» для каждого файла в манифесте PHAR всегда установлено на текущее время, и, похоже, нет никакого способа или переопределения этого. Четное touch("phar://file1.phar/cli.php", 1413387555)
выдает ошибку:
touch(): Can not call touch() for a non-standard stream
Я запускал приведенный выше код в PHP 5.5.9 на Ubuntu Trusty и PHP 5.3 на RHEL5, и обе версии ведут себя одинаково и не могут создать идентичные файлы PHAR.
Я пытаюсь сделать это, чтобы следовать советам в книге Непрерывное развертывание Джез Хамбл и Дэвид Фарли
Любая помощь приветствуется.
В настоящее время класс Phar не позволяет пользователям изменять или даже получать доступ к времени изменения. Я думал о сохранении вашей строки во временном файле и использовании touch
изменить mtime, но это, похоже, не имеет никакого эффекта. Поэтому вам придется вручную изменить временные метки в созданных файлах, а затем заново создать подпись архива. Вот как это сделать с текущими версиями PHP:
<?php
$filename = "file1.phar";
$archive = file_get_contents($filename);
# Search for the start of the archive header
# See http://php.net/manual/de/phar.fileformat.phar.php
# This isn't the only valid way to write a PHAR archive, but it is what the Phar class
# currently does, so you should be fine (The docs say that the end-of-PHP-tag is optional)
$magic = "__HALT_COMPILER(); ?" . ">";
$end_of_code = strpos($archive, $magic) + strlen($magic);
$data_pos = $end_of_code;
# Skip that header
$data = unpack("Vmanifest_length/Vnumber_of_files/vapi_version/Vglobal_flags/Valias_length", substr($archive, $end_of_code, 18));
$data_pos += 18 + $data["alias_length"];
$metadata = unpack("Vlength", substr($archive, $data_pos, 4));
$data_pos += 4 + $metadata["length"];
for($i=0; $i<$data["number_of_files"]; $i++) {
# Now $data_pos points to the first file
# Files are explained here: http://php.net/manual/de/phar.fileformat.manifestfile.php
$filename_data = unpack("Vfilename_length", substr($archive, $data_pos, 4));
$data_pos += 4 + $filename_data["filename_length"];
$file_data = unpack("Vuncompressed_size/Vtimestamp/Vcompressed_size/VCRC32/Vflags/Vmetadata_length", substr($archive, $data_pos, 24));
# Change the timestamp to zeros (You can also use some other time here using pack("V", time()) instead of the zeros)
$archive = substr($archive, 0, $data_pos + 4) . "\0\0\0\0" . substr($archive, $data_pos + 8);
# Skip to the next file (it's _all_ the headers first, then file data)
$data_pos += 24 + $file_data["metadata_length"];
}
# Regenerate the file's signature
$sig_data = unpack("Vsigflags/C4magic", substr($archive, strlen($archive) - 8));
if($sig_data["magic1"] == ord("G") && $sig_data["magic2"] == ord("B") && $sig_data["magic3"] == ord("M") && $sig_data["magic4"] == ord("B")) {
if($sig_data["sigflags"] == 1) {
# MD5
$sig_pos = strlen($archive) - 8 - 16;
$archive = substr($archive, 0, $sig_pos) . pack("H32", md5(substr($archive, 0, $sig_pos))) . substr($archive, $sig_pos + 16);
}
else {
# SHA1
$sig_pos = strlen($archive) - 8 - 20;
$archive = substr($archive, 0, $sig_pos) . pack("H40", sha1(substr($archive, 0, $sig_pos))) . substr($archive, $sig_pos + 20);
}
# Note: The manual talks about SHA256/SHA512 support, but the according flags aren't documented yet. Currently,
# PHAR uses SHA1 by default, so there's nothing to worry about. You still might have to add those sometime.
}
file_put_contents($filename, $archive);
Я написал это специально для моей локальной версии PHP 5.5.9 и вашего примера выше. Скрипт будет работать для файлов, созданных аналогично вашему примеру кода сверху. Документация указывает на некоторые допустимые отклонения от этого формата. Есть комментарии в соответствующих строках кода; вам может понадобиться что-то добавить, если вы хотите поддерживать обычные файлы Phar.
Других решений пока нет …