我正在开发一个命令行PHP项目,并且希望能够重新创建作为部署工件的PHAR文件。面临的挑战是,我无法创建两个具有相同sha1sum且相距超过1秒的PHAR。如果输入文件相同(即来自相同的git commit),我希望能够完全重新创建PHAR文件。
以下代码段演示了该问题:
#!/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
我在ubuntu trusty的PHP 5.5.9和RHEL5的PHP 5.3上运行了上述代码,两个版本的行为方式相同,无法创建相同的PHAR文件。
为了遵循Jez Humble和David Farley的《持续部署》一书中的建议,我正在尝试执行此操作
任何帮助表示赞赏。
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文件,可能必须在此处添加一些内容。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句