2014-10-15 1 views
2

Я работаю над проектом PHP из командной строки и хочу иметь возможность воссоздать файл PHAR, который является моим артефактом развертывания. Проблема состоит в том, что я не могу создать два PHAR, которые имеют идентичные sha1sums и были созданы более чем на 1 секунду друг от друга. Я хотел бы иметь возможность точно воссоздать мой PHAR-файл, если входные файлы одинаковы (т. Е. Из одного и того же git-коммита).Как воссоздать файлы PHAR с идентичными sha1sums в разное время?

Следующий фрагмент кода демонстрирует проблему:

#!/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 велеречивого и PHP 5.3 на RHEL5 и обе версии ведут себя так же, как и не создавать одинаковые файлы ФАРЫ.

Я пытаюсь сделать это для того, чтобы следовать советам в книге Continuous Deployment по Jez Humble и David Фарли

Любая помощь приветствуется.

+1

Изменение системного времени. –

+0

Очень нежелательно менять время на производстве или даже на серверах разработки. Даже если вы забиваете системное время, тогда файлы PHAR могут по-прежнему отличаться, если процесс сборки занимает больше или меньше времени, чем исходный запуск. Не говоря уже о том, что некоторые сетевые операции, например, с kerberos, потерпят неудачу, если системные часы значительно отличаются от остального времени в мире. Изменение «системного времени» для одной команды было бы работоспособным, но я знаю, как это сделать, и это wo – edgester

+0

Изменение «системного времени» для одной команды будет kludgey, но работоспособным. Я не знаю, как изменить системное время для одного исполняемого файла/команды. Как я понимаю, gettimeofday() - это вызов ядра, который не может быть перехвачен LD_PRELOAD. Если бы я пошел на этот уровень усилий, то изменение исходного кода PHP или некоторый тип пост-обработки кажется более разумным. – edgester

ответ

2

Класс 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.

+0

Спасибо за это. Я тестировал его, и он работает для моего исходного образца кода, но он терпит неудачу, если присутствует заглушка. 'createDefaultStub()' добавляет "\ r \ n" после COMPILER_HALT. Использование '$ magic =" __HALT_COMPILER();? " , "> \ r \ n"; 'заставляет вещи работать для случая заглушки. – edgester

 Смежные вопросы

  • Нет связанных вопросов^_^