From cc4b1d5dbfa42ab2f729fcf6d40ef49fc175d4d8 Mon Sep 17 00:00:00 2001 From: Nazar Mokrynskyi Date: Fri, 22 Apr 2016 12:51:53 +0300 Subject: [PATCH 01/22] Added file extension check for Phar opening. It should fail if `.phar` extension not found regardless of contents. --- hphp/system/php/phar/Phar.php | 21 +++++++++++++++---- .../ext_phar/phar_incorrect_extension.php | 3 +++ .../phar_incorrect_extension.php.expectf | 4 ++++ .../ext_phar/phar_incorrect_extension.php.foo | 0 4 files changed, 24 insertions(+), 4 deletions(-) create mode 100644 hphp/test/slow/ext_phar/phar_incorrect_extension.php create mode 100644 hphp/test/slow/ext_phar/phar_incorrect_extension.php.expectf create mode 100644 hphp/test/slow/ext_phar/phar_incorrect_extension.php.foo diff --git a/hphp/system/php/phar/Phar.php b/hphp/system/php/phar/Phar.php index 75d735c815f9fd..345f700daa3d00 100644 --- a/hphp/system/php/phar/Phar.php +++ b/hphp/system/php/phar/Phar.php @@ -38,6 +38,10 @@ class Phar extends RecursiveDirectoryIterator * Prevent the check for __HALT_COMPILER() */ private static $preventHaltTokenCheck = false; + /** + * Prevent the check for file extension + */ + private static $preventExtCheck = false; private $alias; private $fileInfo = array(); @@ -72,6 +76,12 @@ class Phar extends RecursiveDirectoryIterator * @throws UnexpectedValueException */ public function __construct($fname, $flags = null, $alias = null) { + if (!self::$preventExtCheck && !in_array('phar', explode('.', $fname))) { + throw new UnexpectedValueException( + "Cannot create phar '$fname', file extension (or combination) not". + ' recognised or the directory does not exist' + ); + } if (!is_file($fname)) { throw new UnexpectedValueException("$fname is not a file"); } @@ -852,7 +862,11 @@ final public static function getSupportedSignatures () { * @return bool TRUE on success or FALSE on failure. */ final public static function loadPhar($filename, $alias = null) { + // We need this hack because the stream wrapper should work + // even without the __HALT_COMPILER token + self::$preventHaltTokenCheck = true; new self($filename, null, $alias); + self::$preventHaltTokenCheck = false; return true; } @@ -871,7 +885,10 @@ final public static function loadPhar($filename, $alias = null) { * @return bool TRUE on success or FALSE on failure. */ public static function mapPhar($alias = null, $dataoffset = 0) { + // We need this hack because extension check during mapping is not needed + self::$preventExtCheck = true; new self(debug_backtrace()[0]['file'], null, $alias); + self::$preventExtCheck = false; return true; } @@ -1189,11 +1206,7 @@ private static function getPharAndFile($filename_or_alias) { if (is_file($filename)) { if (!isset(self::$aliases[$filename])) { - // We need this hack because the stream wrapper should work - // even without the __HALT_COMPILER token - self::$preventHaltTokenCheck = true; self::loadPhar($filename); - self::$preventHaltTokenCheck = false; } return array( diff --git a/hphp/test/slow/ext_phar/phar_incorrect_extension.php b/hphp/test/slow/ext_phar/phar_incorrect_extension.php new file mode 100644 index 00000000000000..53e1ae1ce6abbc --- /dev/null +++ b/hphp/test/slow/ext_phar/phar_incorrect_extension.php @@ -0,0 +1,3 @@ +__construct() +#1 {main} diff --git a/hphp/test/slow/ext_phar/phar_incorrect_extension.php.foo b/hphp/test/slow/ext_phar/phar_incorrect_extension.php.foo new file mode 100644 index 00000000000000..e69de29bb2d1d6 From f02ad85b4620a78a6804aa2a8f22a2b21e442e5a Mon Sep 17 00:00:00 2001 From: Nazar Mokrynskyi Date: Fri, 22 Apr 2016 14:08:25 +0300 Subject: [PATCH 02/22] Implemented `Phar::isValidPharFilename()` method (PHP >= 5.3.0, PECL phar >= 1.2.0), used in constructor now --- hphp/system/php/phar/Phar.php | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/hphp/system/php/phar/Phar.php b/hphp/system/php/phar/Phar.php index 345f700daa3d00..7c5e3cfa640da9 100644 --- a/hphp/system/php/phar/Phar.php +++ b/hphp/system/php/phar/Phar.php @@ -76,7 +76,7 @@ class Phar extends RecursiveDirectoryIterator * @throws UnexpectedValueException */ public function __construct($fname, $flags = null, $alias = null) { - if (!self::$preventExtCheck && !in_array('phar', explode('.', $fname))) { + if (!self::$preventExtCheck && !self::isValidPharFilename($fname)) { throw new UnexpectedValueException( "Cannot create phar '$fname', file extension (or combination) not". ' recognised or the directory does not exist' @@ -549,6 +549,26 @@ public function isFileFormat($fileformat) { return $fileformat === self::PHAR; } + /** + * ( excerpt from http://php.net/manual/en/phar.isvalidpharfilename.php ) + * + * + * @param string $filename The name or full path to a phar archive not yet + * created. + * @param bool $executable This parameter determines whether the filename + * should be treated as a phar executable archive, or + * a data non-executable archive. + * + * @return bool TRUE if the filename is valid, FALSE if not. + */ + public static function isValidPharFilename ( + $filename, + $executable = true + ) { + $parts = explode('.', $filename); + return $executable ? in_array('phar', $parts) : count($parts) > 1; + } + /** * ( excerpt from http://php.net/manual/en/phar.iswritable.php ) * From 3c87b4605f227e6bd65d2e694ee2d52fbb270a64 Mon Sep 17 00:00:00 2001 From: Nazar Mokrynskyi Date: Fri, 22 Apr 2016 16:03:26 +0300 Subject: [PATCH 03/22] Preparation for tar/zip-based Phar support: * added automatic content-based checks for distinguishing each format type regardless of its extension * phar archive constructor decoupled into separate method, stubs for tar/zip-based phars added * `Phar::isCompressed()` now returns correct result even though tar/zip-based phars are not properly supported yet --- hphp/system/php/phar/Phar.php | 147 +++++++++++++++++++++++++--------- 1 file changed, 111 insertions(+), 36 deletions(-) diff --git a/hphp/system/php/phar/Phar.php b/hphp/system/php/phar/Phar.php index 7c5e3cfa640da9..5316464461138d 100644 --- a/hphp/system/php/phar/Phar.php +++ b/hphp/system/php/phar/Phar.php @@ -30,6 +30,8 @@ class Phar extends RecursiveDirectoryIterator const PHP = 1; const PHPS = 2; + const HALT_TOKEN = '__HALT_COMPILER();'; + /** * A map from filename_or_alias => Phar object */ @@ -50,6 +52,10 @@ class Phar extends RecursiveDirectoryIterator private $manifest; private $contents; private $signature; + /** + * @var bool|int + */ + private $compressed = false; private $count; private $apiVersion; @@ -60,6 +66,8 @@ class Phar extends RecursiveDirectoryIterator private $iteratorRoot; private $iterator; + private $fp; + /** * ( excerpt from http://php.net/manual/en/phar.construct.php ) * @@ -67,7 +75,7 @@ class Phar extends RecursiveDirectoryIterator * @param string $fname Path to an existing Phar archive or to-be-created * archive. The file name's extension must contain * .phar. - * @param string $flags Flags to pass to parent class + * @param int $flags Flags to pass to parent class * RecursiveDirectoryIterator. * @param string $alias Alias with which this Phar archive should be referred * to in calls to stream functionality. @@ -85,45 +93,35 @@ public function __construct($fname, $flags = null, $alias = null) { if (!is_file($fname)) { throw new UnexpectedValueException("$fname is not a file"); } - $data = file_get_contents($fname); + $this->fp = fopen($fname, 'rb'); - $halt_token = '__HALT_COMPILER();'; - $pos = strpos($data, $halt_token); - if ($pos === false && !self::$preventHaltTokenCheck) { - throw new PharException('__HALT_COMPILER(); must be declared in a phar'); + $magic_number = fread($this->fp, 4); + // This is not a bullet-proof check, but should be good enough to catch ZIP + if (strcmp($magic_number, "PK\x03\x04") === 0) { + $this->construct_zip($fname, $flags, $alias); + return; } - $this->stub = substr($data, 0, $pos); - - $pos += strlen($halt_token); - // *sigh*. We have to allow whitespace then ending the file - // before we start the manifest - while ($data[$pos] == ' ') { - $pos += 1; + // Tar + BZ2 + if (strpos($magic_number, 'BZ') === 0) { + $this->compressed = self::BZ2; } - if ($data[$pos] == '?' && $data[$pos+1] == '>') { - $pos += 2; - } - while ($data[$pos] == "\r") { - $pos += 1; + // Tar + GZ + if (strpos($magic_number, "\x1F\x8B") === 0) { + $this->compressed = self::GZ; } - while ($data[$pos] == "\n") { - $pos += 1; + fseek($this->fp, 127); + $magic_number = fread($this->fp, 8); + // Compressed or just Tar + if ( + $this->compressed || + strpos($magic_number, "ustar\x0") === 0 || + strpos($magic_number, "ustar\x40\x40\x0") === 0 + ) { + $this->construct_tar($fname, $flags, $alias); + return; } - - $this->contents = substr($data, $pos); - $this->parsePhar($data, $pos); - - if ($alias) { - self::$aliases[$alias] = $this; - } - // From the manifest - if ($this->alias) { - self::$aliases[$this->alias] = $this; - } - // We also do filename lookups - self::$aliases[$fname] = $this; - - $this->iteratorRoot = 'phar://'.realpath($fname).'/'; + // Otherwise Phar + $this->construct_phar($fname, $flags, $alias); } /** @@ -532,7 +530,7 @@ public function isBuffering() { * @return mixed Phar::GZ, Phar::BZ2 or FALSE */ public function isCompressed() { - return false; + return $this->compressed; } /** @@ -977,6 +975,77 @@ final public static function webPhar( // This is in the default stub, but lets ignore it for now } + /** + * @param string $fname + * @param int $flags + * @param string $alias + * + * @throws PharException + */ + private function construct_phar ($fname, $flags, $alias) { + fseek($this->fp, 0); + $data = stream_get_contents($this->fp); + + $pos = strpos($data, self::HALT_TOKEN); + if ($pos === false && !self::$preventHaltTokenCheck) { + throw new PharException(self::HALT_TOKEN.' must be declared in a phar'); + } + $this->stub = substr($data, 0, $pos); + + $pos += strlen(self::HALT_TOKEN); + // *sigh*. We have to allow whitespace then ending the file + // before we start the manifest + while ($data[$pos] == ' ') { + $pos += 1; + } + if ($data[$pos] == '?' && $data[$pos+1] == '>') { + $pos += 2; + } + while ($data[$pos] == "\r") { + $pos += 1; + } + while ($data[$pos] == "\n") { + $pos += 1; + } + + $this->contents = substr($data, $pos); + $this->parsePhar($data, $pos); + + if ($alias) { + self::$aliases[$alias] = $this; + } + // From the manifest + if ($this->alias) { + self::$aliases[$this->alias] = $this; + } + // We also do filename lookups + self::$aliases[$fname] = $this; + + $this->iteratorRoot = 'phar://'.realpath($fname).'/'; + } + + /** + * @param string $fname + * @param int $flags + * @param string $alias + * + * @throws PharException + */ + private function construct_zip ($fname, $flags, $alias) { + // TODO: ZIP support + } + + /** + * @param string $fname + * @param int $flags + * @param string $alias + * + * @throws PharException + */ + private function construct_tar ($fname, $flags, $alias) { + // TODO: Tar support + } + private static function bytesToInt($str, &$pos, $len) { if (strlen($str) < $pos + $len) { throw new PharException( @@ -1302,4 +1371,10 @@ public function hasChildren() { public function getChildren() { return $this->getIterator()->getChildren(); } + + private function __destruct() { + if ($this->fp !== null) { + fclose($this->fp); + } + } } From 2b6b5fa0a8e44d5f8fb7c97037f5d28872d0a366 Mon Sep 17 00:00:00 2001 From: Nazar Mokrynskyi Date: Fri, 22 Apr 2016 18:51:27 +0300 Subject: [PATCH 04/22] Some unification of `Phar` and `PharData` classes --- hphp/system/php/phar/Phar.php | 17 +++++-- hphp/system/php/phar/PharData.php | 76 ++++++++++++++++++------------- 2 files changed, 57 insertions(+), 36 deletions(-) diff --git a/hphp/system/php/phar/Phar.php b/hphp/system/php/phar/Phar.php index 5316464461138d..2e7b141f07a756 100644 --- a/hphp/system/php/phar/Phar.php +++ b/hphp/system/php/phar/Phar.php @@ -63,9 +63,11 @@ class Phar extends RecursiveDirectoryIterator private $metadata; private $signatureFlags; - private $iteratorRoot; - private $iterator; - + protected $iteratorRoot; + protected $iterator; + /** + * @var null|resource + */ private $fp; /** @@ -93,6 +95,7 @@ public function __construct($fname, $flags = null, $alias = null) { if (!is_file($fname)) { throw new UnexpectedValueException("$fname is not a file"); } + $this->iteratorRoot = 'phar://'.realpath($fname).'/'; $this->fp = fopen($fname, 'rb'); $magic_number = fread($this->fp, 4); @@ -976,6 +979,8 @@ final public static function webPhar( } /** + * Constructor for Phar + * * @param string $fname * @param int $flags * @param string $alias @@ -1020,11 +1025,11 @@ private function construct_phar ($fname, $flags, $alias) { } // We also do filename lookups self::$aliases[$fname] = $this; - - $this->iteratorRoot = 'phar://'.realpath($fname).'/'; } /** + * Constructor for Zip + * * @param string $fname * @param int $flags * @param string $alias @@ -1036,6 +1041,8 @@ private function construct_zip ($fname, $flags, $alias) { } /** + * Constructor for Tar + * * @param string $fname * @param int $flags * @param string $alias diff --git a/hphp/system/php/phar/PharData.php b/hphp/system/php/phar/PharData.php index cd28af3fae9927..89ec658d02cfd0 100644 --- a/hphp/system/php/phar/PharData.php +++ b/hphp/system/php/phar/PharData.php @@ -14,7 +14,6 @@ class PharData extends Phar { private $fname; - private $iterator; private $archiveHandler; // This doc comment block generated by idl/sysdoc.php @@ -37,6 +36,7 @@ public function __construct( string $alias = null, int $format = Phar::TAR ) { + $this->iteratorRoot = 'phar://'.realpath($fname).'/'; $this->fname = $fname; if (substr($fname, -4) === '.zip' || $format === Phar::ZIP) { $this->archiveHandler = new __SystemLib\ZipArchiveHandler($fname); @@ -416,41 +416,60 @@ public function isWritable() { throw new Exception('Not implemented yet'); } - // This doc comment block generated by idl/sysdoc.php /** - * ( excerpt from http://php.net/manual/en/phardata.offsetset.php ) + * ( excerpt from http://php.net/manual/en/phar.offsetexists.php ) + * + * @param string $offset The filename (relative path) to look for in a Phar. + * + * @return bool TRUE if the file exists within the phar, or + * FALSE if not. + */ + public function offsetExists($offset) { + $contents = $this->archiveHandler->getContentsList(); + return isset($contents[$offset]); + } + + /** + * ( excerpt from http://php.net/manual/en/phar.offsetget.php ) * - * This is an implementation of the ArrayAccess interface allowing direct - * manipulation of the contents of a tar/zip archive using array access - * brackets. offsetSet is used for modifying an existing file, or adding a - * new file to a tar/zip archive. * - * @offset mixed The filename (relative path) to modify in a tar or - * zip archive. - * @value mixed Content of the file. + * @param string $offset The filename (relative path) to look for in a Phar. * - * @return mixed No return values. + * @return int A PharFileInfo object is returned that can be used to + * iterate over a file's contents or to retrieve + * information about the current file. + */ + public function offsetGet($offset) { + if (!$this->offsetExists($offset)) { + return null; + } + $contents = $this->archiveHandler->getContentsList(); + return new PharFileInfo($this->iteratorRoot.$offset, $contents[$offset]); + } + + /** + * ( excerpt from http://php.net/manual/en/phar.offsetset.php ) + * + * + * @param string $offset The filename (relative path) to modify in a Phar. + * @param string $value Content of the file. + * + * @return void No return values. */ public function offsetSet($offset, $value) { throw new Exception('Not implemented yet'); } - // This doc comment block generated by idl/sysdoc.php /** - * ( excerpt from http://php.net/manual/en/phardata.offsetunset.php ) + * ( excerpt from http://php.net/manual/en/phar.offsetunset.php ) * - * This is an implementation of the ArrayAccess interface allowing direct - * manipulation of the contents of a tar/zip archive using array access - * brackets. offsetUnset is used for deleting an existing file, and is - * called by the unset() language construct. * - * @offset mixed The filename (relative path) to modify in the - * tar/zip archive. + * @param string $offset The filename (relative path) to modify in a Phar. * - * @return mixed Returns TRUE on success or FALSE on failure. + * @return bool TRUE on success or FALSE on failure. */ public function offsetUnset($offset) { - throw new Exception('Not implemented yet'); + throw new UnexpectedValueException('phar is read-only'); } // This doc comment block generated by idl/sysdoc.php @@ -509,17 +528,12 @@ protected function getIterator() { return $this->iterator; } - $contents = $this->archiveHandler->getContentsList(); - $root = 'phar://'.realpath($this->fname).'/'; - $out = array(); - foreach ($contents as $fname => $stat) { - $out[$fname] = new PharfileInfo( - $root.$fname, - $stat - ); + $filenames = array_keys($this->archiveHandler->getContentsList()); + $info = array(); + foreach ($filenames as $filename) { + $info[$filename] = $this->offsetGet($filename); } - $this->iterator = $this->getIteratorFromList($root, $out); - + $this->iterator = $this->getIteratorFromList($this->iteratorRoot, $info); return $this->iterator; } } From 7d9993e722ca155b296f79f8821f6082db0278e4 Mon Sep 17 00:00:00 2001 From: Nazar Mokrynskyi Date: Fri, 22 Apr 2016 18:55:33 +0300 Subject: [PATCH 05/22] Do not store file pointer inside `Phar` instance, no need for that --- hphp/system/php/phar/Phar.php | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/hphp/system/php/phar/Phar.php b/hphp/system/php/phar/Phar.php index 2e7b141f07a756..f607b4e3f0e9ce 100644 --- a/hphp/system/php/phar/Phar.php +++ b/hphp/system/php/phar/Phar.php @@ -65,10 +65,6 @@ class Phar extends RecursiveDirectoryIterator protected $iteratorRoot; protected $iterator; - /** - * @var null|resource - */ - private $fp; /** * ( excerpt from http://php.net/manual/en/phar.construct.php ) @@ -96,11 +92,12 @@ public function __construct($fname, $flags = null, $alias = null) { throw new UnexpectedValueException("$fname is not a file"); } $this->iteratorRoot = 'phar://'.realpath($fname).'/'; - $this->fp = fopen($fname, 'rb'); + $fp = fopen($fname, 'rb'); - $magic_number = fread($this->fp, 4); + $magic_number = fread($fp, 4); // This is not a bullet-proof check, but should be good enough to catch ZIP if (strcmp($magic_number, "PK\x03\x04") === 0) { + fclose($fp); $this->construct_zip($fname, $flags, $alias); return; } @@ -112,8 +109,9 @@ public function __construct($fname, $flags = null, $alias = null) { if (strpos($magic_number, "\x1F\x8B") === 0) { $this->compressed = self::GZ; } - fseek($this->fp, 127); - $magic_number = fread($this->fp, 8); + fseek($fp, 127); + $magic_number = fread($fp, 8); + fclose($fp); // Compressed or just Tar if ( $this->compressed || @@ -988,8 +986,7 @@ final public static function webPhar( * @throws PharException */ private function construct_phar ($fname, $flags, $alias) { - fseek($this->fp, 0); - $data = stream_get_contents($this->fp); + $data = file_get_contents($fname); $pos = strpos($data, self::HALT_TOKEN); if ($pos === false && !self::$preventHaltTokenCheck) { @@ -1378,10 +1375,4 @@ public function hasChildren() { public function getChildren() { return $this->getIterator()->getChildren(); } - - private function __destruct() { - if ($this->fp !== null) { - fclose($this->fp); - } - } } From 4e199750b2dfa8ab11a7d78a985aa01340eda087 Mon Sep 17 00:00:00 2001 From: Nazar Mokrynskyi Date: Fri, 22 Apr 2016 19:11:44 +0300 Subject: [PATCH 06/22] Small decoupling of common code into generic constructor --- hphp/system/php/phar/Phar.php | 54 +++++++++++++++++------------------ 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/hphp/system/php/phar/Phar.php b/hphp/system/php/phar/Phar.php index f607b4e3f0e9ce..83b9fafa2eba06 100644 --- a/hphp/system/php/phar/Phar.php +++ b/hphp/system/php/phar/Phar.php @@ -99,30 +99,35 @@ public function __construct($fname, $flags = null, $alias = null) { if (strcmp($magic_number, "PK\x03\x04") === 0) { fclose($fp); $this->construct_zip($fname, $flags, $alias); - return; - } - // Tar + BZ2 - if (strpos($magic_number, 'BZ') === 0) { - $this->compressed = self::BZ2; - } - // Tar + GZ - if (strpos($magic_number, "\x1F\x8B") === 0) { - $this->compressed = self::GZ; + } else { + // Tar + BZ2 + if (strpos($magic_number, 'BZ') === 0) { + $this->compressed = self::BZ2; + } + // Tar + GZ + if (strpos($magic_number, "\x1F\x8B") === 0) { + $this->compressed = self::GZ; + } + fseek($fp, 127); + $magic_number = fread($fp, 8); + fclose($fp); + // Compressed or just Tar + if ( + $this->compressed || + strpos($magic_number, "ustar\x0") === 0 || + strpos($magic_number, "ustar\x40\x40\x0") === 0 + ) { + $this->construct_tar($fname, $flags, $alias); + } { + // Otherwise Phar + $this->construct_phar($fname, $flags, $alias); + } } - fseek($fp, 127); - $magic_number = fread($fp, 8); - fclose($fp); - // Compressed or just Tar - if ( - $this->compressed || - strpos($magic_number, "ustar\x0") === 0 || - strpos($magic_number, "ustar\x40\x40\x0") === 0 - ) { - $this->construct_tar($fname, $flags, $alias); - return; + if ($alias) { + self::$aliases[$alias] = $this; } - // Otherwise Phar - $this->construct_phar($fname, $flags, $alias); + // We also do filename lookups + self::$aliases[$fname] = $this; } /** @@ -1013,15 +1018,10 @@ private function construct_phar ($fname, $flags, $alias) { $this->contents = substr($data, $pos); $this->parsePhar($data, $pos); - if ($alias) { - self::$aliases[$alias] = $this; - } // From the manifest if ($this->alias) { self::$aliases[$this->alias] = $this; } - // We also do filename lookups - self::$aliases[$fname] = $this; } /** From 19fca771e567d48386f936a791007ce08e11f599 Mon Sep 17 00:00:00 2001 From: Nazar Mokrynskyi Date: Fri, 22 Apr 2016 23:13:59 +0300 Subject: [PATCH 07/22] `Phar` and `PharData` unified further - both use the same `offset*` methods, iterators and have some common properties. Functionality specific to Phar format moved from `Phar` class to new class `__SystemLib\PharArchiveHandler` which extends `__SystemLib\ArchiveHandler`. `__SystemLib\ArchiveHandler` was extended with additional necessary methods in order to fulfill the need to access additional information from `Phar` class. `__SystemLib\TarArchiveHandler`, `__SystemLib\ZipArchiveHandler` and `__SystemLib\PharArchiveHandler` unified to use the same naming for the same stuff. `__SystemLib\PharArchiveHandler` is partially stream-based, but still more work needed to handle big files nicely. Added missing `PharFileInfo::isCRCChecked()` method as defined in PHP docs. Public methods added to `PharFileInfo::getSize()` and `::getTimestamp()` added for internal use by `Phar` class (PHP for whatever reason defines, for instance, `::getCompressedSize()`, but not mentioned useful methods, so I've added them on top anyway). --- hphp/system/php.txt | 1 + .../php/archive_handler/ArchiveHandler.ns.php | 67 +++- .../archive_handler/PharArchiveHandler.ns.php | 192 +++++++++++ .../archive_handler/TarArchiveHandler.ns.php | 28 +- .../archive_handler/ZipArchiveHandler.ns.php | 44 +-- hphp/system/php/phar/Phar.php | 298 +++++------------- hphp/system/php/phar/PharData.php | 74 ----- hphp/system/php/phar/PharFileInfo.php | 88 +++--- .../test/slow/archive_handler/tar_archive.php | 2 +- 9 files changed, 427 insertions(+), 367 deletions(-) create mode 100644 hphp/system/php/archive_handler/PharArchiveHandler.ns.php diff --git a/hphp/system/php.txt b/hphp/system/php.txt index c8f2d9f078d1c5..0cad85e87e71df 100644 --- a/hphp/system/php.txt +++ b/hphp/system/php.txt @@ -71,6 +71,7 @@ hphp/system/php/redis/RedisException.php hphp/system/php/archive_handler/ArchiveHandler.ns.php hphp/system/php/archive_handler/TarArchiveHandler.ns.php +hphp/system/php/archive_handler/PharArchiveHandler.ns.php hphp/system/php/archive_handler/ZipArchiveHandler.ns.php hphp/system/php/phar/PharException.php diff --git a/hphp/system/php/archive_handler/ArchiveHandler.ns.php b/hphp/system/php/archive_handler/ArchiveHandler.ns.php index 1415c871fb4511..f77f6f5d07e52f 100644 --- a/hphp/system/php/archive_handler/ArchiveHandler.ns.php +++ b/hphp/system/php/archive_handler/ArchiveHandler.ns.php @@ -12,18 +12,71 @@ public function __construct( } abstract class ArchiveHandler { - abstract public function getContentsList(): Map; + protected Map $entries = Map { }; + protected ?string $alias; + protected ?string $stub; + protected ?resource $fp; + protected array> $fileOffsets = []; + protected string $apiVersion = '1.0.0'; + protected $metadata; + protected ?string $signature; + protected $compressed = false; + abstract public function read(string $path): ?string; abstract public function extractAllTo(string $path); abstract public function addFile(string $path, string $archivePath): bool; abstract public function close(): void; - public function stat($path): ?ArchiveEntryStat { - $contents = $this->getContentsList(); - if ($contents->contains($path)) { - return $contents[$path]; - } - return null; + public function stat(string $path): ?ArchiveEntryStat { + return $this->getEntriesMap()->get($path); + } + + public function getAlias(): ?string { + return $this->alias; + } + + public function setAlias(string $alias) { + //TODO + } + + public function getStub(): ?string { + return $this->stub; + } + + public function setStub(string $stub, int $len = -1) { + //TODO + } + + public function count(): int { + return $this->entries->count(); + } + + public function apiVersion(): string { + return $this->apiVersion; + } + + public function getMetadata() { + return $this->metadata; + } + + public function hasMetadata() { + return $this->metadata !== null; + } + + public function getSignature(): string { + return $this->signature; + } + + public function isCompressed() { + return $this->compressed(); + } + + // Custom methods used by Phar class internally + + abstract public function getFileContents(string $filename): string; + + public function getEntriesMap(): Map { + return $this->entries; } } } diff --git a/hphp/system/php/archive_handler/PharArchiveHandler.ns.php b/hphp/system/php/archive_handler/PharArchiveHandler.ns.php new file mode 100644 index 00000000000000..448263fba70a7e --- /dev/null +++ b/hphp/system/php/archive_handler/PharArchiveHandler.ns.php @@ -0,0 +1,192 @@ + $fileInfo = array(); + private int $archiveFlags; + public function __construct(string $path, $preventHaltTokenCheck) { + $this->fp = fopen($path, 'rb'); + $data = file_get_contents($path); + + $pos = strpos($data, Phar::HALT_TOKEN); + if ($pos === false && !$preventHaltTokenCheck) { + throw new PharException(Phar::HALT_TOKEN.' must be declared in a phar'); + } + $this->stub = substr($data, 0, $pos); + + $pos += strlen(Phar::HALT_TOKEN); + // *sigh*. We have to allow whitespace then ending the file + // before we start the manifest + while ($data[$pos] == ' ') { + $pos += 1; + } + if ($data[$pos] == '?' && $data[$pos+1] == '>') { + $pos += 2; + } + while ($data[$pos] == "\r") { + $pos += 1; + } + while ($data[$pos] == "\n") { + $pos += 1; + } + + $this->parsePhar($data, $pos); + } + + private function parsePhar($data, &$pos) { + $start = $pos; + $len = self::bytesToInt($data, $pos, 4); + $count = self::bytesToInt($data, $pos, 4); + $this->apiVersion = self::bytesToInt($data, $pos, 2); + $this->archiveFlags = self::bytesToInt($data, $pos, 4); + $alias_len = self::bytesToInt($data, $pos, 4); + $this->alias = self::substr($data, $pos, $alias_len); + $metadata_len = self::bytesToInt($data, $pos, 4); + $this->metadata = unserialize( + self::substr($data, $pos, $metadata_len) + ); + $this->parseFileInfo($data, $count, $pos); + if ($pos != $start + $len + 4) { + throw new PharException( + "Malformed manifest. Expected $len bytes, got $pos" + ); + } + foreach ($this->fileInfo as $filename => $info) { + $this->fileOffsets[$filename] = array($pos, $info[2]); + $pos += $info[2]; + $this->entries[$filename] = new ArchiveEntryStat( + $info[3], + $info[0], + $info[2], + $info[1] + ); + } + + // Try to see if there is a signature + if ($this->archiveFlags & Phar::SIGNATURE) { + if (strlen($data) < 8 || substr($data, -4) !== 'GBMB') { + // Not even the GBMB and the flags? + throw new PharException('phar has a broken signature'); + } + + $pos = strlen($data) - 8; + $signatureFlags = self::bytesToInt($data, $pos, 4); + switch ($signatureFlags) { + case Phar::MD5: + $digestSize = 16; + $digestName = 'md5'; + break; + case Phar::SHA1: + $digestSize = 20; + $digestName = 'sha1'; + break; + case Phar::SHA256: + $digestSize = 32; + $digestName = 'sha256'; + break; + case Phar::SHA512: + $digestSize = 64; + $digestName = 'sha512'; + break; + default: + throw new PharException('phar has a broken or unsupported signature'); + } + + if (strlen($data) < 8 + $digestSize) { + throw new PharException('phar has a broken signature'); + } + + $pos -= 4; + $signatureStart = $pos - $digestSize; + $this->signature = substr($data, $signatureStart, $digestSize); + $actualHash = self::verifyHash($data, $digestName, $signatureStart); + + if ($actualHash !== $this->signature) { + throw new PharException('phar has a broken signature'); + } + } + } + + private function parseFileInfo(string $str, int $count, &$pos) { + for ($i = 0; $i < $count; $i++) { + $filename_len = self::bytesToInt($str, $pos, 4); + $filename = self::substr($str, $pos, $filename_len); + $filesize = self::bytesToInt($str, $pos, 4); + $timestamp = self::bytesToInt($str, $pos, 4); + $compressed_filesize = self::bytesToInt($str, $pos, 4); + $crc32 = self::bytesToInt($str, $pos, 4); + $flags = self::bytesToInt($str, $pos, 4); + $metadata_len = self::bytesToInt($str, $pos, 4); + $metadata = self::bytesToInt($str, $pos, $metadata_len); + $this->fileInfo[$filename] = array( + $filesize, + $timestamp, + $compressed_filesize ?: $filesize, + $crc32, + $flags, + $metadata + ); + } + } + + private static function verifyHash($str, $algorithm, $signatureOffset) { + return hash($algorithm, substr($str, 0, $signatureOffset), true); + } + + private static function bytesToInt($str, &$pos, $len) { + if (strlen($str) < $pos + $len) { + throw new PharException( + "Corrupt phar, can't read $len bytes starting at offset $pos" + ); + } + $int = 0; + for ($i = 0; $i < $len; ++$i) { + $int |= ord($str[$pos++]) << (8*$i); + } + return $int; + } + + private static function substr($str, &$pos, $len) { + $ret = substr($str, $pos, $len); + $pos += $len; + return $ret; + } + + public function close(): void { + //TODO + } + + public function read(string $path): ?string { + $data = $this->za->getFromName($path); + if ($data === false) { + return null; + } + return $data; + } + + public function extractAllTo(string $path) { + //TODO + } + + public function addFile(string $path, string $archive_path): bool { + //TODO + } + + // Custom methods used by Phar class internally + + public function getFileContents(string $filename): string { + if (!isset($this->fileOffsets[$filename])) { + throw new PharException("No $filename in phar"); + } + list($offset, $size) = $this->fileOffsets[$filename]; + if ($size == 0) { + return ''; + } + fseek($this->fp, $offset); + return fread($this->fp, $size); + } + } +} diff --git a/hphp/system/php/archive_handler/TarArchiveHandler.ns.php b/hphp/system/php/archive_handler/TarArchiveHandler.ns.php index c5abcc54faa2f7..52cfec232aded9 100644 --- a/hphp/system/php/archive_handler/TarArchiveHandler.ns.php +++ b/hphp/system/php/archive_handler/TarArchiveHandler.ns.php @@ -1,16 +1,17 @@ $entries = Map { }; private Map $contents = Map { }; private Map $symlinks = Map { }; private string $path = ''; - private $fp = null; public function __construct(string $path) { $this->path = $path; - $this->entries = Map { }; if (file_exists($path)) { $this->readTar(); @@ -32,8 +33,10 @@ private function readTar() { if ($data === "\37\213") { $fp = gzopen($path, 'rb'); } else if ($data === 'BZ') { + $this->compressed = Phar::BZ2; $fp = bzopen($path, 'r'); } else { + $this->compressed = Phar::GZ; $fp = fopen($path, 'rb'); } @@ -57,7 +60,7 @@ private function readTar() { $this->entries[$filename] = new ArchiveEntryStat( /* crc = */ null, $len, - /* compressed size = */ null, + /* compressed size = */ $len, $timestamp ); $data = ""; @@ -89,23 +92,19 @@ private function readTar() { break; default: - throw new \Exception("type $type is not implemented yet"); + throw new Exception("type $type is not implemented yet"); } if ($len % 512 !== 0) { $leftover = 512 - ($len % 512); $zeros = fread($fp, $leftover); if (strlen(trim($zeros)) != 0) { - throw new \Exception("Malformed tar. Padding isn't zeros. $zeros"); + throw new Exception("Malformed tar. Padding isn't zeros. $zeros"); } } } } - public function getContentsList(): Map { - return $this->entries; - } - public function read(string $path): ?string { if ($this->contents->contains($path)) { return $this->contents[$path]; @@ -204,5 +203,14 @@ public function close(): void { $this->fp = null; } } + + // Custom methods used by Phar class internally + + public function getFileContents(string $filename): string { + if (!isset($this->contents[$filename])) { + throw new PharException("No $filename in phar"); + } + return $this->contents[$filename]; + } } } diff --git a/hphp/system/php/archive_handler/ZipArchiveHandler.ns.php b/hphp/system/php/archive_handler/ZipArchiveHandler.ns.php index 9be8f7d75438ef..1c3c068227859b 100644 --- a/hphp/system/php/archive_handler/ZipArchiveHandler.ns.php +++ b/hphp/system/php/archive_handler/ZipArchiveHandler.ns.php @@ -1,47 +1,40 @@ $contents; - private string $path = ''; + private ZipArchive $za; public function __construct(string $path) { - $this->path = $path; - $this->za = new \ZipArchive(); + $this->za = new ZipArchive(); if (file_exists($path)) { $this->za->open($path); + $this->fillEntries(); } else { - $this->za->open($path, \ZipArchive::CREATE); + $this->za->open($path, ZipArchive::CREATE); } } - public function close(): void { - $this->za->close(); - } - - public function getContentsList(): Map { - $contents = $this->contents; - if ($contents !== null) { - return $contents; - } - - $contents = Map { }; + private function fillEntries() { for ($i = 0; $i < $this->za->numFiles; ++$i) { $fname = $this->za->getNameIndex($i); if (substr($fname, -1) === '/') { continue; } $stat = $this->za->statIndex($i); - $contents[$fname] = new ArchiveEntryStat( + $this->entries[$fname] = new ArchiveEntryStat( $stat['crc'], $stat['size'], - $stat['comp_size'], + $stat['comp_size'] ?: $stat['size'], $stat['mtime'], ); } - $this->contents = $contents; - return $contents; + } + + public function close(): void { + $this->za->close(); } public function read(string $path): ?string { @@ -59,5 +52,14 @@ public function extractAllTo(string $path) { public function addFile(string $path, string $archive_path): bool { return $this->za->addFile($path, $archive_path); } + + // Custom methods used by Phar class internally + + public function getFileContents(string $filename): string { + if ($this->za->locateName($filename) === false) { + throw new PharException("No $filename in phar"); + } + return $this->getFromName($filename); + } } } diff --git a/hphp/system/php/phar/Phar.php b/hphp/system/php/phar/Phar.php index 83b9fafa2eba06..33f6071efdebd2 100644 --- a/hphp/system/php/phar/Phar.php +++ b/hphp/system/php/phar/Phar.php @@ -45,24 +45,11 @@ class Phar extends RecursiveDirectoryIterator */ private static $preventExtCheck = false; - private $alias; - private $fileInfo = array(); - private $fileOffsets = array(); - private $stub; - private $manifest; - private $contents; - private $signature; + protected $fname; /** - * @var bool|int + * @var __SystemLib\ArchiveHandler */ - private $compressed = false; - - private $count; - private $apiVersion; - private $archiveFlags; - private $metadata; - private $signatureFlags; - + protected $archiveHandler; protected $iteratorRoot; protected $iterator; @@ -91,41 +78,47 @@ public function __construct($fname, $flags = null, $alias = null) { if (!is_file($fname)) { throw new UnexpectedValueException("$fname is not a file"); } - $this->iteratorRoot = 'phar://'.realpath($fname).'/'; + $this->fname = $fname; $fp = fopen($fname, 'rb'); + $this->iteratorRoot = 'phar://'.realpath($fname).'/'; $magic_number = fread($fp, 4); // This is not a bullet-proof check, but should be good enough to catch ZIP if (strcmp($magic_number, "PK\x03\x04") === 0) { fclose($fp); - $this->construct_zip($fname, $flags, $alias); + $this->construct_zip($fname); } else { + $compressed = false; // Tar + BZ2 if (strpos($magic_number, 'BZ') === 0) { - $this->compressed = self::BZ2; + $compressed = true; } // Tar + GZ if (strpos($magic_number, "\x1F\x8B") === 0) { - $this->compressed = self::GZ; + $compressed = true; } fseek($fp, 127); $magic_number = fread($fp, 8); fclose($fp); // Compressed or just Tar if ( - $this->compressed || + $compressed || strpos($magic_number, "ustar\x0") === 0 || strpos($magic_number, "ustar\x40\x40\x0") === 0 ) { - $this->construct_tar($fname, $flags, $alias); + $this->construct_tar($fname); } { // Otherwise Phar - $this->construct_phar($fname, $flags, $alias); + $this->construct_phar($fname); } } if ($alias) { self::$aliases[$alias] = $this; } + // From the manifest + if ($this->getAlias()) { + self::$aliases[$this->getAlias()] = $this; + } // We also do filename lookups self::$aliases[$fname] = $this; } @@ -368,7 +361,7 @@ public function copy($oldfile, $newfile) { * number zero) if none. */ public function count() { - return $this->count; + return $this->archiveHandler->count(); } /** @@ -413,7 +406,7 @@ public function extractTo($pathto, $files = null, $overwrite = false) { } public function getAlias() { - return $this->alias; + return $this->archiveHandler->getAlias(); } public function getPath() { @@ -432,7 +425,7 @@ public function getPath() { * meta-data is stored. */ public function getMetadata() { - return $this->metadata; + return $this->archiveHandler->getMetadata(); } /** @@ -464,7 +457,7 @@ public function getModified() { * variable is set to true. */ public function getSignature() { - return null; + return $this->archiveHandler->getSignature(); } /** @@ -479,7 +472,7 @@ public function getSignature() { * (stub) of the current Phar archive. */ public function getStub() { - return $this->stub; + return $this->archiveHandler->getStub(); } /** @@ -494,7 +487,7 @@ public function getStub() { * file format documentation for more information. */ public function getVersion() { - return $this->apiVersion; + return $this->archiveHandler->apiVersion(); } /** @@ -507,7 +500,7 @@ public function getVersion() { * not. */ public function hasMetadata() { - return $this->metadata !== null; + return $this->archiveHandler->hasMetadata(); } /** @@ -536,7 +529,7 @@ public function isBuffering() { * @return mixed Phar::GZ, Phar::BZ2 or FALSE */ public function isCompressed() { - return $this->compressed; + return $this->archiveHandler->isCompressed(); } /** @@ -599,7 +592,7 @@ public function isWritable() { * FALSE if not. */ public function offsetExists($offset) { - return isset($this->fileInfo[$offset]); + return $this->archiveHandler->getEntriesMap()->containsKey($offset); } /** @@ -617,19 +610,11 @@ public function offsetExists($offset) { * information about the current file. */ public function offsetGet($offset) { - if (!$this->offsetExists($offset)) { + $entry = $this->archiveHandler->getEntriesMap()->get($offset); + if (!$entry) { return null; } - $fi = $this->fileInfo[$offset]; - return new PharFileInfo( - $this->iteratorRoot.$offset, - new __SystemLib\ArchiveEntryStat( - $fi[3], // crc32 - $fi[0], // size - $fi[2], // compressed size - $fi[1], // timestamp - ) - ); + return new PharFileInfo($this->iteratorRoot.$offset, $entry); } /** @@ -642,7 +627,7 @@ public function offsetGet($offset) { * @return void No return values. */ public function offsetSet($offset, $value) { - throw new UnexpectedValueException('phar is read-only'); + throw new Exception('Not implemented yet'); } /** @@ -667,7 +652,22 @@ public function offsetUnset($offset) { * @return bool */ public function setAlias($alias) { - $this->alias = $alias; + // TODO: Remove following line when write support implemented + throw new UnexpectedValueException('phar is read-only'); + if (preg_match('#[\\/:;]#', $alias)) { + throw new UnexpectedValueException( + "Invalid alias \"$alias\" specified for phar \"$this->fname\"" + ); + } + self::assertWriteSupport(); + if (isset(self::$aliases[$alias])) { + $path = self::$aliases[$alias]->fname; + throw new PharException( + "alias \"$alias\" is already used for archive \"$path\" and cannot". + ' be used for other archives' + ); + } + $this->archiveHandler->setAlias($alias); } /** @@ -728,14 +728,20 @@ public function setSignatureAlgorithm($sigtype, $privatekey = null) { * ( excerpt from http://php.net/manual/en/phar.setstub.php ) * * - * @param string $stub A string or an open stream handle to use as the - * executable stub for this phar archive. + * @param resource|string $stub A string or an open stream handle to use as + * the executable stub for this phar archive. * @param int $len * * @return bool TRUE on success or FALSE on failure. */ public function setStub($stub, $len = -1) { + // TODO: Remove following line when write support implemented throw new UnexpectedValueException('phar is read-only'); + self::assertWriteSupport(); + if (is_resource($stub)) { + $stub = stream_get_contents($stub); + } + $this->archiveHandler->setStub($stub, $len); } /** @@ -985,55 +991,24 @@ final public static function webPhar( * Constructor for Phar * * @param string $fname - * @param int $flags - * @param string $alias * * @throws PharException */ - private function construct_phar ($fname, $flags, $alias) { - $data = file_get_contents($fname); - - $pos = strpos($data, self::HALT_TOKEN); - if ($pos === false && !self::$preventHaltTokenCheck) { - throw new PharException(self::HALT_TOKEN.' must be declared in a phar'); - } - $this->stub = substr($data, 0, $pos); - - $pos += strlen(self::HALT_TOKEN); - // *sigh*. We have to allow whitespace then ending the file - // before we start the manifest - while ($data[$pos] == ' ') { - $pos += 1; - } - if ($data[$pos] == '?' && $data[$pos+1] == '>') { - $pos += 2; - } - while ($data[$pos] == "\r") { - $pos += 1; - } - while ($data[$pos] == "\n") { - $pos += 1; - } - - $this->contents = substr($data, $pos); - $this->parsePhar($data, $pos); - - // From the manifest - if ($this->alias) { - self::$aliases[$this->alias] = $this; - } + private function construct_phar($fname) { + $this->archiveHandler = new __SystemLib\PharArchiveHandler( + $fname, + self::$preventHaltTokenCheck + ); } /** * Constructor for Zip * * @param string $fname - * @param int $flags - * @param string $alias * * @throws PharException */ - private function construct_zip ($fname, $flags, $alias) { + private function construct_zip($fname) { // TODO: ZIP support } @@ -1041,123 +1016,13 @@ private function construct_zip ($fname, $flags, $alias) { * Constructor for Tar * * @param string $fname - * @param int $flags - * @param string $alias * * @throws PharException */ - private function construct_tar ($fname, $flags, $alias) { + private function construct_tar($fname) { // TODO: Tar support } - private static function bytesToInt($str, &$pos, $len) { - if (strlen($str) < $pos + $len) { - throw new PharException( - "Corrupt phar, can't read $len bytes starting at offset $pos" - ); - } - $int = 0; - for ($i = 0; $i < $len; ++$i) { - $int |= ord($str[$pos++]) << (8*$i); - } - return $int; - } - - private static function substr($str, &$pos, $len) { - $ret = substr($str, $pos, $len); - $pos += $len; - return $ret; - } - - private function parsePhar($data, &$pos) { - $start = $pos; - $len = self::bytesToInt($data, $pos, 4); - $this->count = self::bytesToInt($data, $pos, 4); - $this->apiVersion = self::bytesToInt($data, $pos, 2); - $this->archiveFlags = self::bytesToInt($data, $pos, 4); - $alias_len = self::bytesToInt($data, $pos, 4); - $this->alias = self::substr($data, $pos, $alias_len); - $metadata_len = self::bytesToInt($data, $pos, 4); - $this->metadata = unserialize( - self::substr($data, $pos, $metadata_len) - ); - $this->parseFileInfo($data, $pos); - if ($pos != $start + $len + 4) { - throw new PharException( - "Malformed manifest. Expected $len bytes, got $pos" - ); - } - foreach ($this->fileInfo as $key => $info) { - $this->fileOffsets[$key] = array($pos - $start, $info[2]); - $pos += $info[2]; - } - - // Try to see if there is a signature - if ($this->archiveFlags & self::SIGNATURE) { - if (strlen($data) < 8 || substr($data, -4) !== 'GBMB') { - // Not even the GBMB and the flags? - throw new PharException('phar has a broken signature'); - } - - $pos = strlen($data) - 8; - $this->signatureFlags = self::bytesToInt($data, $pos, 4); - switch ($this->signatureFlags) { - case self::MD5: - $digestSize = 16; - $digestName = 'md5'; - break; - case self::SHA1: - $digestSize = 20; - $digestName = 'sha1'; - break; - case self::SHA256: - $digestSize = 32; - $digestName = 'sha256'; - break; - case self::SHA512: - $digestSize = 64; - $digestName = 'sha512'; - break; - default: - throw new PharException('phar has a broken or unsupported signature'); - } - - if (strlen($data) < 8 + $digestSize) { - throw new PharException('phar has a broken signature'); - } - - $pos -= 4; - $signatureStart = $pos - $digestSize; - $this->signature = substr($data, $signatureStart, $digestSize); - $actualHash = self::verifyHash($data, $digestName, $signatureStart); - - if ($actualHash !== $this->signature) { - throw new PharException('phar has a broken signature'); - } - } - } - - private function parseFileInfo($str, &$pos) { - for ($i = 0; $i < $this->count; $i++) { - $filename_len = self::bytesToInt($str, $pos, 4); - $filename = self::substr($str, $pos, $filename_len); - $filesize = self::bytesToInt($str, $pos, 4); - $timestamp = self::bytesToInt($str, $pos, 4); - $compressed_filesize = self::bytesToInt($str, $pos, 4); - $crc32 = self::bytesToInt($str, $pos, 4); - $flags = self::bytesToInt($str, $pos, 4); - $metadata_len = self::bytesToInt($str, $pos, 4); - $metadata = self::bytesToInt($str, $pos, $metadata_len); - $this->fileInfo[$filename] = array( - $filesize, $timestamp, $compressed_filesize, $crc32, $flags, $metadata - ); - } - } - - private static function verifyHash($str, $algorithm, $signatureOffset) { - return hash($algorithm, substr($str, 0, $signatureOffset), true); - } - /** * A poor man's FileUtil::canonicalize in PHP */ @@ -1193,7 +1058,7 @@ private static function resolveDotDots($pieces) { */ private static function stat($full_filename) { list($phar, $filename) = self::getPharAndFile($full_filename); - if (!isset($phar->fileInfo[$filename])) { + if (!$phar->offsetExists($filename)) { $dir = self::opendir($full_filename); if (!$dir) { return false; @@ -1208,12 +1073,12 @@ private static function stat($full_filename) { ); } - $info = $phar->fileInfo[$filename]; + $info = $phar->offsetGet($filename); return array( - 'size' => $info[0], - 'atime' => $info[1], - 'mtime' => $info[1], - 'ctime' => $info[1], + 'size' => $info->getSize(), + 'atime' => $info->getTimestamp(), + 'mtime' => $info->getTimestamp(), + 'ctime' => $info->getTimestamp(), 'mode' => POSIX_S_IFREG, ); } @@ -1228,7 +1093,7 @@ private static function opendir($full_prefix) { $prefix = rtrim($prefix, '/'); $ret = array(); - foreach ($phar->fileInfo as $filename => $_) { + foreach ($phar->archiveHandler->getEntriesMap()->keys() as $filename) { if (!$prefix) { if (strpos($filename, '/') === false) { $ret[$filename] = true; @@ -1262,11 +1127,7 @@ private static function openPhar($full_filename) { } private function getFileData($filename) { - if (!isset($this->fileOffsets[$filename])) { - throw new PharException("No $filename in phar"); - } - $offsets = $this->fileOffsets[$filename]; - return substr($this->contents, $offsets[0], $offsets[1]); + return $this->archiveHandler->getFileContents($filename); } /** @@ -1312,7 +1173,10 @@ private static function getPharAndFile($filename_or_alias) { throw new PharException("Not a phar: $filename_or_alias"); } - protected function getIteratorFromList(string $root, array $list) { + protected function getIteratorFromList( + string $root, + array $list, + ) { $tree = array(); foreach ($list as $filename => $info) { $dir = dirname($filename); @@ -1339,10 +1203,12 @@ protected function getIterator() { if ($this->iterator !== null) { return $this->iterator; } - $filenames = array_keys($this->fileInfo); - $info = array(); - foreach ($filenames as $filename) { - $info[$filename] = $this->offsetGet($filename); + $info = []; + foreach ($this->archiveHandler->getEntriesMap() as $filename => $entry) { + $info[$filename] = new PharFileInfo( + $this->iteratorRoot.$filename, + $entry + ); } $this->iterator = $this->getIteratorFromList($this->iteratorRoot, $info); return $this->iterator; @@ -1375,4 +1241,12 @@ public function hasChildren() { public function getChildren() { return $this->getIterator()->getChildren(); } + + protected static function assertWriteSupport() { + if (ini_get('phar.readonly') != 0) { + throw new UnexpectedValueException( + 'Cannot write out phar archive, phar is read-only' + ); + } + } } diff --git a/hphp/system/php/phar/PharData.php b/hphp/system/php/phar/PharData.php index 89ec658d02cfd0..14d57dda3e624a 100644 --- a/hphp/system/php/phar/PharData.php +++ b/hphp/system/php/phar/PharData.php @@ -12,10 +12,6 @@ * */ class PharData extends Phar { - - private $fname; - private $archiveHandler; - // This doc comment block generated by idl/sysdoc.php /** * ( excerpt from http://php.net/manual/en/phardata.construct.php ) @@ -416,62 +412,6 @@ public function isWritable() { throw new Exception('Not implemented yet'); } - /** - * ( excerpt from http://php.net/manual/en/phar.offsetexists.php ) - * - * @param string $offset The filename (relative path) to look for in a Phar. - * - * @return bool TRUE if the file exists within the phar, or - * FALSE if not. - */ - public function offsetExists($offset) { - $contents = $this->archiveHandler->getContentsList(); - return isset($contents[$offset]); - } - - /** - * ( excerpt from http://php.net/manual/en/phar.offsetget.php ) - * - * - * @param string $offset The filename (relative path) to look for in a Phar. - * - * @return int A PharFileInfo object is returned that can be used to - * iterate over a file's contents or to retrieve - * information about the current file. - */ - public function offsetGet($offset) { - if (!$this->offsetExists($offset)) { - return null; - } - $contents = $this->archiveHandler->getContentsList(); - return new PharFileInfo($this->iteratorRoot.$offset, $contents[$offset]); - } - - /** - * ( excerpt from http://php.net/manual/en/phar.offsetset.php ) - * - * - * @param string $offset The filename (relative path) to modify in a Phar. - * @param string $value Content of the file. - * - * @return void No return values. - */ - public function offsetSet($offset, $value) { - throw new Exception('Not implemented yet'); - } - - /** - * ( excerpt from http://php.net/manual/en/phar.offsetunset.php ) - * - * - * @param string $offset The filename (relative path) to modify in a Phar. - * - * @return bool TRUE on success or FALSE on failure. - */ - public function offsetUnset($offset) { - throw new UnexpectedValueException('phar is read-only'); - } - // This doc comment block generated by idl/sysdoc.php /** * ( excerpt from http://php.net/manual/en/phardata.setalias.php ) @@ -522,18 +462,4 @@ public function setDefaultStub(string $index, string $webindex) { public function setStub(string $stub, int $len = -1) { throw new Exception('Not implemented yet'); } - - protected function getIterator() { - if ($this->iterator !== null) { - return $this->iterator; - } - - $filenames = array_keys($this->archiveHandler->getContentsList()); - $info = array(); - foreach ($filenames as $filename) { - $info[$filename] = $this->offsetGet($filename); - } - $this->iterator = $this->getIteratorFromList($this->iteratorRoot, $info); - return $this->iterator; - } } diff --git a/hphp/system/php/phar/PharFileInfo.php b/hphp/system/php/phar/PharFileInfo.php index f14cd3cf6b2e2a..7987ba34d5ee3b 100644 --- a/hphp/system/php/phar/PharFileInfo.php +++ b/hphp/system/php/phar/PharFileInfo.php @@ -9,29 +9,25 @@ */ class PharFileInfo extends SplFileInfo { - private $name; - private $stat; + private ?string $name; + private ?__SystemLib\ArchiveEntryStat $stat; - // This doc comment block generated by idl/sysdoc.php /** * ( excerpt from http://php.net/manual/en/pharfileinfo.construct.php ) * - * This should not be called directly. Instead, a PharFileInfo object is - * initialized by calling Phar::offsetGet() through array access. - * - * @filename mixed The full url to retrieve a file. If you wish to - * retrieve the information for the file my/file.php - * from the phar boo.phar, the entry should be - * phar://boo.phar/my/file.php. + * @param string $entry The full url to retrieve a file. If you wish to + * retrieve the information for the file my/file.php + * from the phar boo.phar, the entry should be + * phar://boo.phar/my/file.php. */ - public function __construct($filename, $data) { + public function __construct($entry, $data) { if (!($data instanceof __SystemLib\ArchiveEntryStat)) { throw new UnexpectedValueException( "PharFileInfo can only be constructed via Phar::offsetGet()" ); } - parent::__construct($filename); - $this->name = $filename; + parent::__construct($entry); + $this->name = $entry; $this->stat = $data; } @@ -115,21 +111,6 @@ public function decompress() { public function delMetadata() { } - // This doc comment block generated by idl/sysdoc.php - /** - * ( excerpt from - * http://php.net/manual/en/pharfileinfo.getcompressedsize.php ) - * - * This returns the size of the file within the Phar archive. Uncompressed - * files will return the same value for getCompressedSize as they will with - * filesize() - * - * @return mixed The size in bytes of the file within the Phar - * archive on disk. - */ - public function getCompressedSize() { - } - // This doc comment block generated by idl/sysdoc.php /** * ( excerpt from http://php.net/manual/en/pharfileinfo.getcrc32.php ) @@ -142,7 +123,20 @@ public function getCompressedSize() { public function getCRC32() { } - public function getContent() { + // This doc comment block generated by idl/sysdoc.php + /** + * ( excerpt from + * http://php.net/manual/en/pharfileinfo.getcompressedsize.php ) + * + * This returns the size of the file within the Phar archive. Uncompressed + * files will return the same value for getCompressedSize as they will with + * filesize() + * + * @return mixed The size in bytes of the file within the Phar + * archive on disk. + */ + public function getCompressedSize(): int { + return $this->stat->compresedSize; } // This doc comment block generated by idl/sysdoc.php @@ -185,6 +179,19 @@ public function getPharFlags() { public function hasMetadata() { } + // This doc comment block generated by idl/sysdoc.php + /** + * ( excerpt from http://php.net/manual/en/pharfileinfo.iscrcchecked.php ) + * + * This returns whether a file within a Phar archive has had its CRC + * verified. + * + * @return mixed TRUE if the file has had its CRC verified, FALSE if + * not. + */ + public function isCRCChecked() { + } + // This doc comment block generated by idl/sysdoc.php /** * ( excerpt from http://php.net/manual/en/pharfileinfo.iscompressed.php ) @@ -202,19 +209,6 @@ public function hasMetadata() { public function isCompressed($compression_type) { } - // This doc comment block generated by idl/sysdoc.php - /** - * ( excerpt from http://php.net/manual/en/pharfileinfo.iscrcchecked.php ) - * - * This returns whether a file within a Phar archive has had its CRC - * verified. - * - * @return mixed TRUE if the file has had its CRC verified, FALSE if - * not. - */ - public function isCRCChecked() { - } - // This doc comment block generated by idl/sysdoc.php /** * ( excerpt from http://php.net/manual/en/pharfileinfo.setmetadata.php ) @@ -243,4 +237,14 @@ public function isCRCChecked() { */ public function setMetadata($metadata) { } + + // Custom methods used by Phar class internally + + public function getSize(): int { + return $this->stat->size; + } + + public function getTimestamp(): int { + return $this->stat->timestamp; + } } diff --git a/hphp/test/slow/archive_handler/tar_archive.php b/hphp/test/slow/archive_handler/tar_archive.php index 7ebe60ea49682b..c116a03992b92a 100644 --- a/hphp/test/slow/archive_handler/tar_archive.php +++ b/hphp/test/slow/archive_handler/tar_archive.php @@ -1,4 +1,4 @@ getContentsList()->keys()); +var_dump($handler->getEntriesMap()->keys()); From c9731ffde2e816a1790b99e5442c34665c7811ef Mon Sep 17 00:00:00 2001 From: Nazar Mokrynskyi Date: Sat, 23 Apr 2016 00:59:41 +0300 Subject: [PATCH 08/22] Tar/Zip formats should be supported now in `Phar` class (needs to be tested and unit tests should be added). Fix for iterating Tar archives that contain type flag `5` (test case included file without entries with such flag). `Archive_Tar-1.3.11.tar.gz` has exactly the same contents as `Archive_Tar-1.3.11.tgz `, but with 3 directory entries (created using popular GUI tool `file-roller`). --- .../php/archive_handler/ArchiveHandler.ns.php | 5 ++ .../archive_handler/PharArchiveHandler.ns.php | 5 +- .../archive_handler/TarArchiveHandler.ns.php | 84 +++++++++++------- .../archive_handler/ZipArchiveHandler.ns.php | 23 ++++- hphp/system/php/phar/Phar.php | 81 ++++++----------- .../ext_phar/phar_data_extract.php.expect | 2 + ...phar_data_iterate_tar_with_dirs_inside.php | 19 ++++ ...a_iterate_tar_with_dirs_inside.php.expectf | 35 ++++++++ .../ext_phar/tgz/Archive_Tar-1.3.11.tar.gz | Bin 0 -> 18807 bytes 9 files changed, 169 insertions(+), 85 deletions(-) create mode 100644 hphp/test/slow/ext_phar/phar_data_iterate_tar_with_dirs_inside.php create mode 100644 hphp/test/slow/ext_phar/phar_data_iterate_tar_with_dirs_inside.php.expectf create mode 100644 hphp/test/slow/ext_phar/tgz/Archive_Tar-1.3.11.tar.gz diff --git a/hphp/system/php/archive_handler/ArchiveHandler.ns.php b/hphp/system/php/archive_handler/ArchiveHandler.ns.php index f77f6f5d07e52f..1aa10fbbe978cf 100644 --- a/hphp/system/php/archive_handler/ArchiveHandler.ns.php +++ b/hphp/system/php/archive_handler/ArchiveHandler.ns.php @@ -27,6 +27,11 @@ abstract public function extractAllTo(string $path); abstract public function addFile(string $path, string $archivePath): bool; abstract public function close(): void; + abstract public function __construct( + string $path, + bool $preventHaltTokenCheck = true + ); + public function stat(string $path): ?ArchiveEntryStat { return $this->getEntriesMap()->get($path); } diff --git a/hphp/system/php/archive_handler/PharArchiveHandler.ns.php b/hphp/system/php/archive_handler/PharArchiveHandler.ns.php index 448263fba70a7e..5c8c24ad668091 100644 --- a/hphp/system/php/archive_handler/PharArchiveHandler.ns.php +++ b/hphp/system/php/archive_handler/PharArchiveHandler.ns.php @@ -7,7 +7,10 @@ final class PharArchiveHandler extends ArchiveHandler { private array $fileInfo = array(); private int $archiveFlags; - public function __construct(string $path, $preventHaltTokenCheck) { + public function __construct( + string $path, + bool $preventHaltTokenCheck = true + ) { $this->fp = fopen($path, 'rb'); $data = file_get_contents($path); diff --git a/hphp/system/php/archive_handler/TarArchiveHandler.ns.php b/hphp/system/php/archive_handler/TarArchiveHandler.ns.php index 52cfec232aded9..f36fc057b953c0 100644 --- a/hphp/system/php/archive_handler/TarArchiveHandler.ns.php +++ b/hphp/system/php/archive_handler/TarArchiveHandler.ns.php @@ -10,11 +10,22 @@ final class TarArchiveHandler extends ArchiveHandler { private Map $symlinks = Map { }; private string $path = ''; - public function __construct(string $path) { + public function __construct( + string $path, + bool $preventHaltTokenCheck = true + ) { $this->path = $path; if (file_exists($path)) { $this->readTar(); + if ( + !$preventHaltTokenCheck && + strpos($this->stub, Phar::HALT_TOKEN) === false + ) { + throw new PharException( + Phar::HALT_TOKEN.' must be declared in a phar' + ); + } } } @@ -56,14 +67,9 @@ private function readTar() { $len = octdec(substr($header, 124, 12)); $timestamp = octdec(trim(substr($header, 136, 12))); - $type = substr($header, 156, 1); - $this->entries[$filename] = new ArchiveEntryStat( - /* crc = */ null, - $len, - /* compressed size = */ $len, - $timestamp - ); - $data = ""; + $type = $header[156]; + + $data = ''; $read_len = 0; while ($read_len != $len) { $read_data = fread($fp, $len - $read_len); @@ -71,28 +77,44 @@ private function readTar() { $read_len += strlen($read_data); } - switch ($type) { - case 'L': - $next_file_name = trim($data); - break; - - case '0': - case "\0": - $this->contents[$filename] = $data; - break; - - case '2': - // Assuming this is from GNU Tar - $target = trim(substr($header, 157, 100), "\0"); - $this->symlinks[$filename] = $target; - break; - - case '5': - // Directory, ignore - break; - - default: - throw new Exception("type $type is not implemented yet"); + // Hidden .phar directory should not appear in files listing + if (strpos($filename, '.phar') === 0) { + if ($filename == '.phar/stub.php') { + $this->stub = $data; + } else if ($filename == '.phar/alias.txt') { + $this->alias = $data; + } + } else if (substr($filename, -1) !== '/') { + $this->entries[$filename] = new ArchiveEntryStat( + /* crc = */ null, + $len, + /* compressed size = */ $len, + $timestamp + ); + + switch ($type) { + case 'L': + $next_file_name = trim($data); + break; + + case '0': + case "\0": + $this->contents[$filename] = $data; + break; + + case '2': + // Assuming this is from GNU Tar + $target = trim(substr($header, 157, 100), "\0"); + $this->symlinks[$filename] = $target; + break; + + case '5': + // Directory, ignore + break; + + default: + throw new Exception("type $type is not implemented yet"); + } } if ($len % 512 !== 0) { diff --git a/hphp/system/php/archive_handler/ZipArchiveHandler.ns.php b/hphp/system/php/archive_handler/ZipArchiveHandler.ns.php index 1c3c068227859b..815c9152b8ff16 100644 --- a/hphp/system/php/archive_handler/ZipArchiveHandler.ns.php +++ b/hphp/system/php/archive_handler/ZipArchiveHandler.ns.php @@ -1,17 +1,29 @@ za = new ZipArchive(); if (file_exists($path)) { $this->za->open($path); $this->fillEntries(); + if ( + !$preventHaltTokenCheck && + strpos($this->stub, Phar::HALT_TOKEN) === false + ) { + throw new PharException( + Phar::HALT_TOKEN.' must be declared in a phar' + ); + } } else { $this->za->open($path, ZipArchive::CREATE); } @@ -23,6 +35,15 @@ private function fillEntries() { if (substr($fname, -1) === '/') { continue; } + // Hidden .phar directory should not appear in files listing + if (strpos($fname, '.phar') === 0) { + if ($fname == '.phar/stub.php') { + $this->stub = $this->za->getFromName($fname); + } else if ($fname == '.phar/alias.txt') { + $this->alias = $this->za->getFromName($fname); + } + continue; + } $stat = $this->za->statIndex($i); $this->entries[$fname] = new ArchiveEntryStat( $stat['crc'], diff --git a/hphp/system/php/phar/Phar.php b/hphp/system/php/phar/Phar.php index 33f6071efdebd2..dfb3bbdeff6515 100644 --- a/hphp/system/php/phar/Phar.php +++ b/hphp/system/php/phar/Phar.php @@ -86,7 +86,10 @@ public function __construct($fname, $flags = null, $alias = null) { // This is not a bullet-proof check, but should be good enough to catch ZIP if (strcmp($magic_number, "PK\x03\x04") === 0) { fclose($fp); - $this->construct_zip($fname); + $this->archiveHandler = new __SystemLib\ZipArchiveHandler( + $fname, + self::$preventHaltTokenCheck + ); } else { $compressed = false; // Tar + BZ2 @@ -97,28 +100,34 @@ public function __construct($fname, $flags = null, $alias = null) { if (strpos($magic_number, "\x1F\x8B") === 0) { $compressed = true; } - fseek($fp, 127); + fseek($fp, 257); $magic_number = fread($fp, 8); fclose($fp); - // Compressed or just Tar + // Compressed or just plain Tar if ( $compressed || strpos($magic_number, "ustar\x0") === 0 || strpos($magic_number, "ustar\x40\x40\x0") === 0 ) { - $this->construct_tar($fname); - } { + $this->archiveHandler = new __SystemLib\TarArchiveHandler( + $fname, + self::$preventHaltTokenCheck + ); + } else { // Otherwise Phar - $this->construct_phar($fname); + $this->archiveHandler = new __SystemLib\PharArchiveHandler( + $fname, + self::$preventHaltTokenCheck + ); } } - if ($alias) { - self::$aliases[$alias] = $this; - } // From the manifest if ($this->getAlias()) { self::$aliases[$this->getAlias()] = $this; } + if ($alias) { + self::$aliases[$alias] = $this; + } // We also do filename lookups self::$aliases[$fname] = $this; } @@ -652,14 +661,12 @@ public function offsetUnset($offset) { * @return bool */ public function setAlias($alias) { - // TODO: Remove following line when write support implemented - throw new UnexpectedValueException('phar is read-only'); + if (preg_match('#[\\/:;]#', $alias)) { throw new UnexpectedValueException( "Invalid alias \"$alias\" specified for phar \"$this->fname\"" ); } - self::assertWriteSupport(); if (isset(self::$aliases[$alias])) { $path = self::$aliases[$alias]->fname; throw new PharException( @@ -667,7 +674,13 @@ public function setAlias($alias) { ' be used for other archives' ); } - $this->archiveHandler->setAlias($alias); + // Do not execute following when called from constructor + if (!debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS)[0]['object'] === $this) { + // TODO: Remove following line when write support implemented + throw new UnexpectedValueException('phar is read-only'); + self::assertWriteSupport(); + $this->archiveHandler->setAlias($alias); + } } /** @@ -959,7 +972,7 @@ public static function interceptFileFuncs() { * @return string the filename if valid, empty string otherwise. */ final public static function running(bool $retphar = true) { - $filename = debug_backtrace()[0]['file']; + $filename = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS)[0]['file']; $pharScheme = "phar://"; $pharExt = ".phar"; if(substr($filename, 0, strlen($pharScheme)) == $pharScheme) { @@ -975,54 +988,18 @@ final public static function running(bool $retphar = true) { } } } - return ""; + return ''; } final public static function webPhar( $alias, - $index = "index.php", + $index = 'index.php', $f404 = null, $mimetypes = null, $rewrites = null) { // This is in the default stub, but lets ignore it for now } - /** - * Constructor for Phar - * - * @param string $fname - * - * @throws PharException - */ - private function construct_phar($fname) { - $this->archiveHandler = new __SystemLib\PharArchiveHandler( - $fname, - self::$preventHaltTokenCheck - ); - } - - /** - * Constructor for Zip - * - * @param string $fname - * - * @throws PharException - */ - private function construct_zip($fname) { - // TODO: ZIP support - } - - /** - * Constructor for Tar - * - * @param string $fname - * - * @throws PharException - */ - private function construct_tar($fname) { - // TODO: Tar support - } - /** * A poor man's FileUtil::canonicalize in PHP */ diff --git a/hphp/test/slow/ext_phar/phar_data_extract.php.expect b/hphp/test/slow/ext_phar/phar_data_extract.php.expect index 20c22c2e8483a0..4dece6760403da 100644 --- a/hphp/test/slow/ext_phar/phar_data_extract.php.expect +++ b/hphp/test/slow/ext_phar/phar_data_extract.php.expect @@ -1,3 +1,5 @@ +string(25) "Archive_Tar-1.3.11.tar.gz" +int(3) string(22) "Archive_Tar-1.3.11.tgz" int(3) string(24) "Console_Getopt-1.3.1.tgz" diff --git a/hphp/test/slow/ext_phar/phar_data_iterate_tar_with_dirs_inside.php b/hphp/test/slow/ext_phar/phar_data_iterate_tar_with_dirs_inside.php new file mode 100644 index 00000000000000..1661459eaacf03 --- /dev/null +++ b/hphp/test/slow/ext_phar/phar_data_iterate_tar_with_dirs_inside.php @@ -0,0 +1,19 @@ + $data) { + $out[$fname] = array( + get_class($data), + $data->getFilename(), + $data->getPathname(), + (string) $data + ); + } + ksort($out); + var_dump($out); +} + +main(); diff --git a/hphp/test/slow/ext_phar/phar_data_iterate_tar_with_dirs_inside.php.expectf b/hphp/test/slow/ext_phar/phar_data_iterate_tar_with_dirs_inside.php.expectf new file mode 100644 index 00000000000000..e7f728608cb485 --- /dev/null +++ b/hphp/test/slow/ext_phar/phar_data_iterate_tar_with_dirs_inside.php.expectf @@ -0,0 +1,35 @@ +array(3) { + ["phar://%s/test/slow/ext_phar/tgz/Archive_Tar-1.3.11.tar.gz/Archive_Tar-1.3.11/Archive/Tar.php"]=> + array(4) { + [0]=> + string(12) "PharFileInfo" + [1]=> + string(7) "Tar.php" + [2]=> + string(%d) "phar://%s/test/slow/ext_phar/tgz/Archive_Tar-1.3.11.tar.gz/Archive_Tar-1.3.11/Archive/Tar.php" + [3]=> + string(%d) "phar://%s/test/slow/ext_phar/tgz/Archive_Tar-1.3.11.tar.gz/Archive_Tar-1.3.11/Archive/Tar.php" + } + ["phar://%s/test/slow/ext_phar/tgz/Archive_Tar-1.3.11.tar.gz/Archive_Tar-1.3.11/docs/Archive_Tar.txt"]=> + array(4) { + [0]=> + string(12) "PharFileInfo" + [1]=> + string(15) "Archive_Tar.txt" + [2]=> + string(%d) "phar://%s/test/slow/ext_phar/tgz/Archive_Tar-1.3.11.tar.gz/Archive_Tar-1.3.11/docs/Archive_Tar.txt" + [3]=> + string(%d) "phar://%s/test/slow/ext_phar/tgz/Archive_Tar-1.3.11.tar.gz/Archive_Tar-1.3.11/docs/Archive_Tar.txt" + } + ["phar://%s/test/slow/ext_phar/tgz/Archive_Tar-1.3.11.tar.gz/package.xml"]=> + array(4) { + [0]=> + string(12) "PharFileInfo" + [1]=> + string(11) "package.xml" + [2]=> + string(%d) "phar://%s/test/slow/ext_phar/tgz/Archive_Tar-1.3.11.tar.gz/package.xml" + [3]=> + string(%d) "phar://%s/test/slow/ext_phar/tgz/Archive_Tar-1.3.11.tar.gz/package.xml" + } +} diff --git a/hphp/test/slow/ext_phar/tgz/Archive_Tar-1.3.11.tar.gz b/hphp/test/slow/ext_phar/tgz/Archive_Tar-1.3.11.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..6c81e56857d4966b036ec28378f02e81b93626f6 GIT binary patch literal 18807 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ξMS6fTTZ3WlxZch4hMc_nig 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 literal 0 HcmV?d00001 From 7adb1bf03060ec554880a1b80e8807f920a4d5ae Mon Sep 17 00:00:00 2001 From: Nazar Mokrynskyi Date: Sat, 23 Apr 2016 01:08:39 +0300 Subject: [PATCH 09/22] Unnecessary method removed --- .../php/archive_handler/ArchiveHandler.ns.php | 4 +-- .../archive_handler/PharArchiveHandler.ns.php | 28 ++++++------------- .../archive_handler/TarArchiveHandler.ns.php | 16 +++-------- .../archive_handler/ZipArchiveHandler.ns.php | 13 ++------- hphp/system/php/phar/Phar.php | 8 +++--- 5 files changed, 20 insertions(+), 49 deletions(-) diff --git a/hphp/system/php/archive_handler/ArchiveHandler.ns.php b/hphp/system/php/archive_handler/ArchiveHandler.ns.php index 1aa10fbbe978cf..9526f16bb7c04a 100644 --- a/hphp/system/php/archive_handler/ArchiveHandler.ns.php +++ b/hphp/system/php/archive_handler/ArchiveHandler.ns.php @@ -22,7 +22,7 @@ abstract class ArchiveHandler { protected ?string $signature; protected $compressed = false; - abstract public function read(string $path): ?string; + abstract public function read(string $path): string; abstract public function extractAllTo(string $path); abstract public function addFile(string $path, string $archivePath): bool; abstract public function close(): void; @@ -78,8 +78,6 @@ public function isCompressed() { // Custom methods used by Phar class internally - abstract public function getFileContents(string $filename): string; - public function getEntriesMap(): Map { return $this->entries; } diff --git a/hphp/system/php/archive_handler/PharArchiveHandler.ns.php b/hphp/system/php/archive_handler/PharArchiveHandler.ns.php index 5c8c24ad668091..6f17e98f537f6a 100644 --- a/hphp/system/php/archive_handler/PharArchiveHandler.ns.php +++ b/hphp/system/php/archive_handler/PharArchiveHandler.ns.php @@ -162,12 +162,16 @@ public function close(): void { //TODO } - public function read(string $path): ?string { - $data = $this->za->getFromName($path); - if ($data === false) { - return null; + public function read(string $path): string { + if (!isset($this->fileOffsets[$path])) { + throw new PharException("No $path in phar"); } - return $data; + list($offset, $size) = $this->fileOffsets[$path]; + if ($size == 0) { + return ''; + } + fseek($this->fp, $offset); + return fread($this->fp, $size); } public function extractAllTo(string $path) { @@ -177,19 +181,5 @@ public function extractAllTo(string $path) { public function addFile(string $path, string $archive_path): bool { //TODO } - - // Custom methods used by Phar class internally - - public function getFileContents(string $filename): string { - if (!isset($this->fileOffsets[$filename])) { - throw new PharException("No $filename in phar"); - } - list($offset, $size) = $this->fileOffsets[$filename]; - if ($size == 0) { - return ''; - } - fseek($this->fp, $offset); - return fread($this->fp, $size); - } } } diff --git a/hphp/system/php/archive_handler/TarArchiveHandler.ns.php b/hphp/system/php/archive_handler/TarArchiveHandler.ns.php index f36fc057b953c0..6b0eef0d4ea37d 100644 --- a/hphp/system/php/archive_handler/TarArchiveHandler.ns.php +++ b/hphp/system/php/archive_handler/TarArchiveHandler.ns.php @@ -127,10 +127,11 @@ private function readTar() { } } - public function read(string $path): ?string { - if ($this->contents->contains($path)) { - return $this->contents[$path]; + public function read(string $path): string { + if (!$this->contents->contains($path)) { + throw new PharException("No $path in phar"); } + return $this->contents[$path]; } private function createFullPath( @@ -225,14 +226,5 @@ public function close(): void { $this->fp = null; } } - - // Custom methods used by Phar class internally - - public function getFileContents(string $filename): string { - if (!isset($this->contents[$filename])) { - throw new PharException("No $filename in phar"); - } - return $this->contents[$filename]; - } } } diff --git a/hphp/system/php/archive_handler/ZipArchiveHandler.ns.php b/hphp/system/php/archive_handler/ZipArchiveHandler.ns.php index 815c9152b8ff16..9af8d47dbe7b93 100644 --- a/hphp/system/php/archive_handler/ZipArchiveHandler.ns.php +++ b/hphp/system/php/archive_handler/ZipArchiveHandler.ns.php @@ -58,10 +58,10 @@ public function close(): void { $this->za->close(); } - public function read(string $path): ?string { + public function read(string $path): string { $data = $this->za->getFromName($path); if ($data === false) { - return null; + throw new PharException("No $path in phar"); } return $data; } @@ -73,14 +73,5 @@ public function extractAllTo(string $path) { public function addFile(string $path, string $archive_path): bool { return $this->za->addFile($path, $archive_path); } - - // Custom methods used by Phar class internally - - public function getFileContents(string $filename): string { - if ($this->za->locateName($filename) === false) { - throw new PharException("No $filename in phar"); - } - return $this->getFromName($filename); - } } } diff --git a/hphp/system/php/phar/Phar.php b/hphp/system/php/phar/Phar.php index dfb3bbdeff6515..ee31f03483fccc 100644 --- a/hphp/system/php/phar/Phar.php +++ b/hphp/system/php/phar/Phar.php @@ -1098,13 +1098,13 @@ private static function opendir($full_prefix) { * Used by the stream wrapper to open phar:// files. * Called from C++. */ - private static function openPhar($full_filename) { + private static function openPhar($full_filename): string { list($phar, $filename) = self::getPharAndFile($full_filename); return $phar->getFileData($filename); } - private function getFileData($filename) { - return $this->archiveHandler->getFileContents($filename); + private function getFileData($filename): string { + return $this->archiveHandler->read($filename); } /** @@ -1116,7 +1116,7 @@ private function getFileData($filename) { * * array([Phar object for alias], 'rest/of/path.php') */ - private static function getPharAndFile($filename_or_alias) { + private static function getPharAndFile(string $filename_or_alias) { if (strncmp($filename_or_alias, 'phar://', 7)) { throw new PharException("Not a phar: $filename_or_alias"); } From f25f29339177d5631e46e3a3ce5664d11154ed92 Mon Sep 17 00:00:00 2001 From: Orvid King Date: Fri, 22 Apr 2016 15:57:28 -0700 Subject: [PATCH 10/22] Take a resource. --- hphp/runtime/ext/phar/ext_phar.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/hphp/runtime/ext/phar/ext_phar.cpp b/hphp/runtime/ext/phar/ext_phar.cpp index 612be25b665502..f296b9eea387a9 100644 --- a/hphp/runtime/ext/phar/ext_phar.cpp +++ b/hphp/runtime/ext/phar/ext_phar.cpp @@ -59,9 +59,11 @@ static struct PharStreamWrapper : Stream::Wrapper { nullptr, SystemLib::s_PharClass ); - String contents = ret.toString(); - return req::make(contents.data(), contents.size()); + if (!ret.isResource()) { + return nullptr; + } + return dyn_cast_or_null(ret.asResRef()); } virtual int access(const String& path, int mode) { From 645e61bcf2cfdb1ef3d97e693d4ebed6fdcb0514 Mon Sep 17 00:00:00 2001 From: Nazar Mokrynskyi Date: Sat, 23 Apr 2016 10:51:19 +0300 Subject: [PATCH 11/22] Switch to streams when interfacing with runtime, though only Zip works efficiently, others require streams slicing of something similar to actually avoid copying data --- hphp/runtime/ext/phar/ext_phar.cpp | 1 - hphp/system/php/archive_handler/ArchiveHandler.ns.php | 2 +- .../php/archive_handler/PharArchiveHandler.ns.php | 8 +++++--- .../php/archive_handler/TarArchiveHandler.ns.php | 6 ++++-- .../php/archive_handler/ZipArchiveHandler.ns.php | 8 ++++---- hphp/system/php/phar/Phar.php | 10 ++++++---- 6 files changed, 20 insertions(+), 15 deletions(-) diff --git a/hphp/runtime/ext/phar/ext_phar.cpp b/hphp/runtime/ext/phar/ext_phar.cpp index f296b9eea387a9..4d2ab7e04e030b 100644 --- a/hphp/runtime/ext/phar/ext_phar.cpp +++ b/hphp/runtime/ext/phar/ext_phar.cpp @@ -22,7 +22,6 @@ #include "hphp/runtime/ext/extension.h" #include "hphp/runtime/base/stream-wrapper.h" #include "hphp/runtime/base/stream-wrapper-registry.h" -#include "hphp/runtime/base/mem-file.h" #include "hphp/runtime/base/directory.h" namespace HPHP { diff --git a/hphp/system/php/archive_handler/ArchiveHandler.ns.php b/hphp/system/php/archive_handler/ArchiveHandler.ns.php index 9526f16bb7c04a..bda935232284b9 100644 --- a/hphp/system/php/archive_handler/ArchiveHandler.ns.php +++ b/hphp/system/php/archive_handler/ArchiveHandler.ns.php @@ -22,7 +22,7 @@ abstract class ArchiveHandler { protected ?string $signature; protected $compressed = false; - abstract public function read(string $path): string; + abstract public function getStream(string $path): resource; abstract public function extractAllTo(string $path); abstract public function addFile(string $path, string $archivePath): bool; abstract public function close(): void; diff --git a/hphp/system/php/archive_handler/PharArchiveHandler.ns.php b/hphp/system/php/archive_handler/PharArchiveHandler.ns.php index 6f17e98f537f6a..40637ccd841037 100644 --- a/hphp/system/php/archive_handler/PharArchiveHandler.ns.php +++ b/hphp/system/php/archive_handler/PharArchiveHandler.ns.php @@ -162,16 +162,18 @@ public function close(): void { //TODO } - public function read(string $path): string { + public function getStream(string $path): resource { if (!isset($this->fileOffsets[$path])) { throw new PharException("No $path in phar"); } list($offset, $size) = $this->fileOffsets[$path]; if ($size == 0) { - return ''; + return fopen('php://temp', 'w+b'); } + $stream = fopen('php://temp', 'w+b');//TODO stream slice needed here fseek($this->fp, $offset); - return fread($this->fp, $size); + fwrite($stream, fread($this->fp, $size)); + return $stream; } public function extractAllTo(string $path) { diff --git a/hphp/system/php/archive_handler/TarArchiveHandler.ns.php b/hphp/system/php/archive_handler/TarArchiveHandler.ns.php index 6b0eef0d4ea37d..b5f82e6b99478d 100644 --- a/hphp/system/php/archive_handler/TarArchiveHandler.ns.php +++ b/hphp/system/php/archive_handler/TarArchiveHandler.ns.php @@ -127,11 +127,13 @@ private function readTar() { } } - public function read(string $path): string { + public function getStream(string $path): resource { if (!$this->contents->contains($path)) { throw new PharException("No $path in phar"); } - return $this->contents[$path]; + $stream = fopen('php://temp', 'w+b');//TODO stream slice needed here + fwrite($stream, $this->contents[$path]); + return $stream; } private function createFullPath( diff --git a/hphp/system/php/archive_handler/ZipArchiveHandler.ns.php b/hphp/system/php/archive_handler/ZipArchiveHandler.ns.php index 9af8d47dbe7b93..2ea0b2e94f96b6 100644 --- a/hphp/system/php/archive_handler/ZipArchiveHandler.ns.php +++ b/hphp/system/php/archive_handler/ZipArchiveHandler.ns.php @@ -58,12 +58,12 @@ public function close(): void { $this->za->close(); } - public function read(string $path): string { - $data = $this->za->getFromName($path); - if ($data === false) { + public function getStream(string $path): resource { + $stream = $this->za->getStream($path); + if (!is_resource($stream)) { throw new PharException("No $path in phar"); } - return $data; + return $stream; } public function extractAllTo(string $path) { diff --git a/hphp/system/php/phar/Phar.php b/hphp/system/php/phar/Phar.php index ee31f03483fccc..e9a29b4a8952c1 100644 --- a/hphp/system/php/phar/Phar.php +++ b/hphp/system/php/phar/Phar.php @@ -1098,13 +1098,15 @@ private static function opendir($full_prefix) { * Used by the stream wrapper to open phar:// files. * Called from C++. */ - private static function openPhar($full_filename): string { + private static function openPhar(string $full_filename): resource { list($phar, $filename) = self::getPharAndFile($full_filename); return $phar->getFileData($filename); } - private function getFileData($filename): string { - return $this->archiveHandler->read($filename); + private function getFileData(string $filename): resource { + $stream = $this->archiveHandler->getStream($filename); + fseek($stream, 0); + return $stream; } /** @@ -1117,7 +1119,7 @@ private function getFileData($filename): string { * array([Phar object for alias], 'rest/of/path.php') */ private static function getPharAndFile(string $filename_or_alias) { - if (strncmp($filename_or_alias, 'phar://', 7)) { + if (strpos($filename_or_alias, 'phar://') !== 0) { throw new PharException("Not a phar: $filename_or_alias"); } From 32be78f989e128b086ce112b0fbbb95f085ee55c Mon Sep 17 00:00:00 2001 From: Nazar Mokrynskyi Date: Sat, 23 Apr 2016 13:14:50 +0300 Subject: [PATCH 12/22] Completely stream-based Phar implementation --- .../php/archive_handler/ArchiveHandler.ns.php | 1 - .../archive_handler/PharArchiveHandler.ns.php | 170 +++++++++++------- .../archive_handler/TarArchiveHandler.ns.php | 1 + 3 files changed, 109 insertions(+), 63 deletions(-) diff --git a/hphp/system/php/archive_handler/ArchiveHandler.ns.php b/hphp/system/php/archive_handler/ArchiveHandler.ns.php index bda935232284b9..3c33579622ce33 100644 --- a/hphp/system/php/archive_handler/ArchiveHandler.ns.php +++ b/hphp/system/php/archive_handler/ArchiveHandler.ns.php @@ -15,7 +15,6 @@ abstract class ArchiveHandler { protected Map $entries = Map { }; protected ?string $alias; protected ?string $stub; - protected ?resource $fp; protected array> $fileOffsets = []; protected string $apiVersion = '1.0.0'; protected $metadata; diff --git a/hphp/system/php/archive_handler/PharArchiveHandler.ns.php b/hphp/system/php/archive_handler/PharArchiveHandler.ns.php index 40637ccd841037..3d82e30772313d 100644 --- a/hphp/system/php/archive_handler/PharArchiveHandler.ns.php +++ b/hphp/system/php/archive_handler/PharArchiveHandler.ns.php @@ -7,51 +7,71 @@ final class PharArchiveHandler extends ArchiveHandler { private array $fileInfo = array(); private int $archiveFlags; + private ?resource $stream; public function __construct( string $path, bool $preventHaltTokenCheck = true ) { - $this->fp = fopen($path, 'rb'); - $data = file_get_contents($path); - - $pos = strpos($data, Phar::HALT_TOKEN); - if ($pos === false && !$preventHaltTokenCheck) { + $stream = fopen($path, 'rb'); + $this->stream = $stream; + // We'll not get 0 as position because of `stub = stream_get_contents($stream, $pos, 0); + $pos += strlen(Phar::HALT_TOKEN); + // *sigh*. We have to allow whitespace then ending the file + // before we start the manifest + while (stream_get_contents($stream, 1, $pos) == ' ') { + $pos += 1; + } + if ( + stream_get_contents($stream, 1, $pos) == '?' && + stream_get_contents($stream, 1, $pos + 1) == '>' + ) { + $pos += 2; + } + while (stream_get_contents($stream, 1, $pos) == "\r") { + $pos += 1; + } + while (stream_get_contents($stream, 1, $pos) == "\n") { + $pos += 1; + } + } else if (!$preventHaltTokenCheck) { throw new PharException(Phar::HALT_TOKEN.' must be declared in a phar'); } - $this->stub = substr($data, 0, $pos); - $pos += strlen(Phar::HALT_TOKEN); - // *sigh*. We have to allow whitespace then ending the file - // before we start the manifest - while ($data[$pos] == ' ') { - $pos += 1; - } - if ($data[$pos] == '?' && $data[$pos+1] == '>') { - $pos += 2; - } - while ($data[$pos] == "\r") { - $pos += 1; - } - while ($data[$pos] == "\n") { - $pos += 1; - } + $this->parsePhar($stream, $pos); + } - $this->parsePhar($data, $pos); + private static function findToken(resource $stream, string $token): ?int { + $offset = 0; + $prev_data = ''; + $next_data = ''; + do { + $offset += strlen($prev_data); + $prev_data = $next_data; + $next_data = fread($stream, 1024); + $pos = strpos($prev_data.$next_data, $token); + if ($pos !== false) { + return $offset + $pos; + } + } while (!feof($stream)); + return null; } - private function parsePhar($data, &$pos) { + private function parsePhar(resource $stream, &$pos) { $start = $pos; - $len = self::bytesToInt($data, $pos, 4); - $count = self::bytesToInt($data, $pos, 4); - $this->apiVersion = self::bytesToInt($data, $pos, 2); - $this->archiveFlags = self::bytesToInt($data, $pos, 4); - $alias_len = self::bytesToInt($data, $pos, 4); - $this->alias = self::substr($data, $pos, $alias_len); - $metadata_len = self::bytesToInt($data, $pos, 4); + $len = self::bytesToInt($stream, $pos, 4); + $count = self::bytesToInt($stream, $pos, 4); + $this->apiVersion = self::bytesToInt($stream, $pos, 2); + $this->archiveFlags = self::bytesToInt($stream, $pos, 4); + $alias_len = self::bytesToInt($stream, $pos, 4); + $this->alias = self::substr($stream, $pos, $alias_len); + $metadata_len = self::bytesToInt($stream, $pos, 4); $this->metadata = unserialize( - self::substr($data, $pos, $metadata_len) + self::substr($stream, $pos, $metadata_len) ); - $this->parseFileInfo($data, $count, $pos); + $this->parseFileInfo($stream, $count, $pos); if ($pos != $start + $len + 4) { throw new PharException( "Malformed manifest. Expected $len bytes, got $pos" @@ -68,15 +88,19 @@ private function parsePhar($data, &$pos) { ); } + $signatureStart = $pos; + $signature = stream_get_contents($stream, -1, $pos); + $signatureSize = strlen($signature); + // Try to see if there is a signature if ($this->archiveFlags & Phar::SIGNATURE) { - if (strlen($data) < 8 || substr($data, -4) !== 'GBMB') { + if (substr($signature, -4) !== 'GBMB') { // Not even the GBMB and the flags? throw new PharException('phar has a broken signature'); } - $pos = strlen($data) - 8; - $signatureFlags = self::bytesToInt($data, $pos, 4); + $pos = $signatureStart + $signatureSize - 8; + $signatureFlags = self::bytesToInt($stream, $pos, 4); switch ($signatureFlags) { case Phar::MD5: $digestSize = 16; @@ -95,35 +119,39 @@ private function parsePhar($data, &$pos) { $digestName = 'sha512'; break; default: - throw new PharException('phar has a broken or unsupported signature'); + throw new PharException( + 'phar has a broken or unsupported signature' + ); } - if (strlen($data) < 8 + $digestSize) { + if ($signatureSize < 8 + $digestSize) { throw new PharException('phar has a broken signature'); } - $pos -= 4; - $signatureStart = $pos - $digestSize; - $this->signature = substr($data, $signatureStart, $digestSize); - $actualHash = self::verifyHash($data, $digestName, $signatureStart); + $this->signature = substr($signature, 0, $digestSize); + $computedSignature = self::computeSignature( + $stream, + $digestName, + $signatureSize + ); - if ($actualHash !== $this->signature) { + if (strcmp($computedSignature, $this->signature) !== 0) { throw new PharException('phar has a broken signature'); } } } - private function parseFileInfo(string $str, int $count, &$pos) { + private function parseFileInfo(resource $stream, int $count, &$pos) { for ($i = 0; $i < $count; $i++) { - $filename_len = self::bytesToInt($str, $pos, 4); - $filename = self::substr($str, $pos, $filename_len); - $filesize = self::bytesToInt($str, $pos, 4); - $timestamp = self::bytesToInt($str, $pos, 4); - $compressed_filesize = self::bytesToInt($str, $pos, 4); - $crc32 = self::bytesToInt($str, $pos, 4); - $flags = self::bytesToInt($str, $pos, 4); - $metadata_len = self::bytesToInt($str, $pos, 4); - $metadata = self::bytesToInt($str, $pos, $metadata_len); + $filename_len = self::bytesToInt($stream, $pos, 4); + $filename = self::substr($stream, $pos, $filename_len); + $filesize = self::bytesToInt($stream, $pos, 4); + $timestamp = self::bytesToInt($stream, $pos, 4); + $compressed_filesize = self::bytesToInt($stream, $pos, 4); + $crc32 = self::bytesToInt($stream, $pos, 4); + $flags = self::bytesToInt($stream, $pos, 4); + $metadata_len = self::bytesToInt($stream, $pos, 4); + $metadata = self::bytesToInt($stream, $pos, $metadata_len); $this->fileInfo[$filename] = array( $filesize, $timestamp, @@ -135,31 +163,50 @@ private function parseFileInfo(string $str, int $count, &$pos) { } } - private static function verifyHash($str, $algorithm, $signatureOffset) { - return hash($algorithm, substr($str, 0, $signatureOffset), true); + private static function computeSignature( + resource $stream, + string $algorithm, + int $signatureSize + ) { + rewind($stream); + $context = hash_init($algorithm); + $data = ''; + + while (!feof($stream)) { + $data .= fread($stream, 1024 * 1024); + hash_update($context, substr($data, 0, -$signatureSize)); + $data = substr($data, -$signatureSize); + } + + return hash_final($context, true); } - private static function bytesToInt($str, &$pos, $len) { - if (strlen($str) < $pos + $len) { + private static function bytesToInt(resource $stream, &$pos, int $len) { + $str = stream_get_contents($stream, $len, $pos); + if (strlen($str) < $len) { throw new PharException( "Corrupt phar, can't read $len bytes starting at offset $pos" ); } $int = 0; for ($i = 0; $i < $len; ++$i) { - $int |= ord($str[$pos++]) << (8*$i); + $int |= ord($str[$i]) << (8*$i); } + $pos += $len; return $int; } - private static function substr($str, &$pos, $len) { - $ret = substr($str, $pos, $len); + private static function substr(resource $stream, &$pos, int $len) { + $ret = stream_get_contents($stream, $len, $pos); $pos += $len; return $ret; } public function close(): void { - //TODO + if ($this->stream) { + fclose($this->stream); + $this->stream = null; + } } public function getStream(string $path): resource { @@ -171,8 +218,7 @@ public function getStream(string $path): resource { return fopen('php://temp', 'w+b'); } $stream = fopen('php://temp', 'w+b');//TODO stream slice needed here - fseek($this->fp, $offset); - fwrite($stream, fread($this->fp, $size)); + fwrite($stream, stream_get_contents($this->stream, $size, $offset)); return $stream; } diff --git a/hphp/system/php/archive_handler/TarArchiveHandler.ns.php b/hphp/system/php/archive_handler/TarArchiveHandler.ns.php index b5f82e6b99478d..875f7b7d7be3f5 100644 --- a/hphp/system/php/archive_handler/TarArchiveHandler.ns.php +++ b/hphp/system/php/archive_handler/TarArchiveHandler.ns.php @@ -9,6 +9,7 @@ final class TarArchiveHandler extends ArchiveHandler { private Map $contents = Map { }; private Map $symlinks = Map { }; private string $path = ''; + private ?resource $fp; public function __construct( string $path, From 1f3225f8463b6a3b13b60583c80906f250a17d70 Mon Sep 17 00:00:00 2001 From: Nazar Mokrynskyi Date: Sat, 23 Apr 2016 17:55:47 +0300 Subject: [PATCH 13/22] Added support for compressed Phar archives (`.phar.gz` and `.phar.bz2`) using `new Phar()`. Added support for `include 'phar:///path/to.phar'`, `include 'phar:///path/to.phar.gz'`, `include 'phar:///path/to.phar.bz2'` (gz and bz2 variations do not work without `phar://` protocol yet). For `include 'phar:///path/to.phar'` stub will be used. --- .../php/archive_handler/ArchiveHandler.ns.php | 65 ++++++++++- .../archive_handler/PharArchiveHandler.ns.php | 107 ++++++++---------- .../archive_handler/TarArchiveHandler.ns.php | 6 +- hphp/system/php/phar/Phar.php | 50 ++++---- hphp/test/slow/ext_phar/basic.phar.bz2 | Bin 0 -> 2691 bytes hphp/test/slow/ext_phar/basic.phar.gz | Bin 0 -> 2520 bytes hphp/test/slow/ext_phar/basic.php | 18 +++ hphp/test/slow/ext_phar/basic.php.expect | 9 +- 8 files changed, 171 insertions(+), 84 deletions(-) create mode 100644 hphp/test/slow/ext_phar/basic.phar.bz2 create mode 100644 hphp/test/slow/ext_phar/basic.phar.gz diff --git a/hphp/system/php/archive_handler/ArchiveHandler.ns.php b/hphp/system/php/archive_handler/ArchiveHandler.ns.php index 3c33579622ce33..01ae9572064856 100644 --- a/hphp/system/php/archive_handler/ArchiveHandler.ns.php +++ b/hphp/system/php/archive_handler/ArchiveHandler.ns.php @@ -1,6 +1,8 @@ { return $this->entries; } + + // Things used by Phar and Tar to overcome Bzip2 extension's limitation to + // seek in other way than SEEK_CUR, we need to maintain position manually:( + + private int $pos = 0; + private ?resource $stream; + private ?string $path; + + protected function open(string $path) { + $this->path = $path; + + $fp = fopen($path, 'rb'); + $data = fread($fp, 2); + fclose($fp); + + if ($data === 'BZ') { + $this->compressed = Phar::BZ2; + $this->stream = bzopen($path, 'r'); + } else if ($data === "\x1F\x8B") { + $this->stream = gzopen($path, 'rb'); + } else { + $this->compressed = Phar::GZ; + $this->stream = fopen($path, 'rb'); + } + } + + protected function stream_get_contents( + int $maxlength = -1, + int $offset = -1 + ): ?string { + if ($offset >= 0) { + $this->seek($offset); + } + $ret = stream_get_contents($this->stream, $maxlength); + $this->pos += strlen($ret); + return $ret; + } + + protected function rewind() { + $this->seek(0); + } + + protected function seek(int $position) { + if ($position < $this->pos && $this->compressed == Phar::BZ2) { + fclose($this->stream); + $this->stream = bzopen($this->path, 'r'); + $this->pos = 0; + } + fseek($this->stream, $position - $this->pos, SEEK_CUR); + $this->pos = $position; + } + + protected function eof(): bool { + return feof($this->stream); + } + + public function close(): void { + if ($this->stream) { + fclose($this->stream); + $this->stream = null; + } + } } } diff --git a/hphp/system/php/archive_handler/PharArchiveHandler.ns.php b/hphp/system/php/archive_handler/PharArchiveHandler.ns.php index 3d82e30772313d..2698bff672f3a0 100644 --- a/hphp/system/php/archive_handler/PharArchiveHandler.ns.php +++ b/hphp/system/php/archive_handler/PharArchiveHandler.ns.php @@ -7,71 +7,69 @@ final class PharArchiveHandler extends ArchiveHandler { private array $fileInfo = array(); private int $archiveFlags; - private ?resource $stream; public function __construct( string $path, bool $preventHaltTokenCheck = true ) { - $stream = fopen($path, 'rb'); - $this->stream = $stream; - // We'll not get 0 as position because of `open($path); + $pos = $this->haltTokenPosition(); if ($pos) { - $this->stub = stream_get_contents($stream, $pos, 0); + $this->stub = $this->stream_get_contents($pos, 0); $pos += strlen(Phar::HALT_TOKEN); // *sigh*. We have to allow whitespace then ending the file // before we start the manifest - while (stream_get_contents($stream, 1, $pos) == ' ') { + while ($this->stream_get_contents(1, $pos) == ' ') { $pos += 1; } if ( - stream_get_contents($stream, 1, $pos) == '?' && - stream_get_contents($stream, 1, $pos + 1) == '>' + $this->stream_get_contents(1, $pos) == '?' && + $this->stream_get_contents(1, $pos + 1) == '>' ) { $pos += 2; } - while (stream_get_contents($stream, 1, $pos) == "\r") { + while ($this->stream_get_contents(1, $pos) == "\r") { $pos += 1; } - while (stream_get_contents($stream, 1, $pos) == "\n") { + while ($this->stream_get_contents(1, $pos) == "\n") { $pos += 1; } } else if (!$preventHaltTokenCheck) { throw new PharException(Phar::HALT_TOKEN.' must be declared in a phar'); } - $this->parsePhar($stream, $pos); + $this->parsePhar($pos); } - private static function findToken(resource $stream, string $token): ?int { + private function haltTokenPosition(): int { $offset = 0; $prev_data = ''; $next_data = ''; do { $offset += strlen($prev_data); $prev_data = $next_data; - $next_data = fread($stream, 1024); - $pos = strpos($prev_data.$next_data, $token); + $next_data = $this->stream_get_contents(1024); + $pos = strpos($prev_data.$next_data, Phar::HALT_TOKEN); if ($pos !== false) { return $offset + $pos; } - } while (!feof($stream)); - return null; + } while (!$this->eof()); + // We'll not get 0 as position of real token because of `apiVersion = self::bytesToInt($stream, $pos, 2); - $this->archiveFlags = self::bytesToInt($stream, $pos, 4); - $alias_len = self::bytesToInt($stream, $pos, 4); - $this->alias = self::substr($stream, $pos, $alias_len); - $metadata_len = self::bytesToInt($stream, $pos, 4); + $len = $this->bytesToInt($pos, 4); + $count = $this->bytesToInt($pos, 4); + $this->apiVersion = $this->bytesToInt($pos, 2); + $this->archiveFlags = $this->bytesToInt($pos, 4); + $alias_len = $this->bytesToInt($pos, 4); + $this->alias = $this->substr($pos, $alias_len); + $metadata_len = $this->bytesToInt($pos, 4); $this->metadata = unserialize( - self::substr($stream, $pos, $metadata_len) + $this->substr($pos, $metadata_len) ); - $this->parseFileInfo($stream, $count, $pos); + $this->parseFileInfo($count, $pos); if ($pos != $start + $len + 4) { throw new PharException( "Malformed manifest. Expected $len bytes, got $pos" @@ -89,7 +87,7 @@ private function parsePhar(resource $stream, &$pos) { } $signatureStart = $pos; - $signature = stream_get_contents($stream, -1, $pos); + $signature = $this->stream_get_contents(-1, $pos); $signatureSize = strlen($signature); // Try to see if there is a signature @@ -100,7 +98,7 @@ private function parsePhar(resource $stream, &$pos) { } $pos = $signatureStart + $signatureSize - 8; - $signatureFlags = self::bytesToInt($stream, $pos, 4); + $signatureFlags = $this->bytesToInt($pos, 4); switch ($signatureFlags) { case Phar::MD5: $digestSize = 16; @@ -129,8 +127,7 @@ private function parsePhar(resource $stream, &$pos) { } $this->signature = substr($signature, 0, $digestSize); - $computedSignature = self::computeSignature( - $stream, + $computedSignature = $this->computeSignature( $digestName, $signatureSize ); @@ -141,17 +138,17 @@ private function parsePhar(resource $stream, &$pos) { } } - private function parseFileInfo(resource $stream, int $count, &$pos) { + private function parseFileInfo(int $count, &$pos) { for ($i = 0; $i < $count; $i++) { - $filename_len = self::bytesToInt($stream, $pos, 4); - $filename = self::substr($stream, $pos, $filename_len); - $filesize = self::bytesToInt($stream, $pos, 4); - $timestamp = self::bytesToInt($stream, $pos, 4); - $compressed_filesize = self::bytesToInt($stream, $pos, 4); - $crc32 = self::bytesToInt($stream, $pos, 4); - $flags = self::bytesToInt($stream, $pos, 4); - $metadata_len = self::bytesToInt($stream, $pos, 4); - $metadata = self::bytesToInt($stream, $pos, $metadata_len); + $filename_len = $this->bytesToInt($pos, 4); + $filename = $this->substr($pos, $filename_len); + $filesize = $this->bytesToInt($pos, 4); + $timestamp = $this->bytesToInt($pos, 4); + $compressed_filesize = $this->bytesToInt($pos, 4); + $crc32 = $this->bytesToInt($pos, 4); + $flags = $this->bytesToInt($pos, 4); + $metadata_len = $this->bytesToInt($pos, 4); + $metadata = $this->bytesToInt($pos, $metadata_len); $this->fileInfo[$filename] = array( $filesize, $timestamp, @@ -163,17 +160,16 @@ private function parseFileInfo(resource $stream, int $count, &$pos) { } } - private static function computeSignature( - resource $stream, + private function computeSignature( string $algorithm, int $signatureSize - ) { - rewind($stream); + ): string { + $this->rewind(); $context = hash_init($algorithm); $data = ''; - while (!feof($stream)) { - $data .= fread($stream, 1024 * 1024); + while (!$this->eof()) { + $data .= $this->stream_get_contents(1024 * 1024); hash_update($context, substr($data, 0, -$signatureSize)); $data = substr($data, -$signatureSize); } @@ -181,8 +177,8 @@ private static function computeSignature( return hash_final($context, true); } - private static function bytesToInt(resource $stream, &$pos, int $len) { - $str = stream_get_contents($stream, $len, $pos); + private function bytesToInt(&$pos, int $len) { + $str = $this->stream_get_contents($len, $pos); if (strlen($str) < $len) { throw new PharException( "Corrupt phar, can't read $len bytes starting at offset $pos" @@ -196,19 +192,12 @@ private static function bytesToInt(resource $stream, &$pos, int $len) { return $int; } - private static function substr(resource $stream, &$pos, int $len) { - $ret = stream_get_contents($stream, $len, $pos); + private function substr(&$pos, int $len) { + $ret = $this->stream_get_contents($len, $pos); $pos += $len; return $ret; } - public function close(): void { - if ($this->stream) { - fclose($this->stream); - $this->stream = null; - } - } - public function getStream(string $path): resource { if (!isset($this->fileOffsets[$path])) { throw new PharException("No $path in phar"); @@ -218,7 +207,7 @@ public function getStream(string $path): resource { return fopen('php://temp', 'w+b'); } $stream = fopen('php://temp', 'w+b');//TODO stream slice needed here - fwrite($stream, stream_get_contents($this->stream, $size, $offset)); + fwrite($stream, $this->stream_get_contents($size, $offset)); return $stream; } diff --git a/hphp/system/php/archive_handler/TarArchiveHandler.ns.php b/hphp/system/php/archive_handler/TarArchiveHandler.ns.php index 875f7b7d7be3f5..97581a08f82436 100644 --- a/hphp/system/php/archive_handler/TarArchiveHandler.ns.php +++ b/hphp/system/php/archive_handler/TarArchiveHandler.ns.php @@ -42,11 +42,11 @@ private function readTar() { $data = fread($fp, 2); fclose($fp); - if ($data === "\37\213") { - $fp = gzopen($path, 'rb'); - } else if ($data === 'BZ') { + if ($data === 'BZ') { $this->compressed = Phar::BZ2; $fp = bzopen($path, 'r'); + } else if ($data === "\x1F\x8B") { + $fp = gzopen($path, 'rb'); } else { $this->compressed = Phar::GZ; $fp = fopen($path, 'rb'); diff --git a/hphp/system/php/phar/Phar.php b/hphp/system/php/phar/Phar.php index e9a29b4a8952c1..8bec5a5d421a61 100644 --- a/hphp/system/php/phar/Phar.php +++ b/hphp/system/php/phar/Phar.php @@ -91,21 +91,17 @@ public function __construct($fname, $flags = null, $alias = null) { self::$preventHaltTokenCheck ); } else { - $compressed = false; - // Tar + BZ2 if (strpos($magic_number, 'BZ') === 0) { - $compressed = true; + fclose($fp); + $fp = bzopen($fname, 'r'); + } else if (strpos($magic_number, "\x1F\x8B") === 0) { + fclose($fp); + $fp = gzopen($fname, 'rb'); } - // Tar + GZ - if (strpos($magic_number, "\x1F\x8B") === 0) { - $compressed = true; - } - fseek($fp, 257); + fseek($fp, 257, SEEK_CUR); // Bzip2 only knows how to use SEEK_CUR $magic_number = fread($fp, 8); fclose($fp); - // Compressed or just plain Tar if ( - $compressed || strpos($magic_number, "ustar\x0") === 0 || strpos($magic_number, "ustar\x40\x40\x0") === 0 ) { @@ -114,7 +110,6 @@ public function __construct($fname, $flags = null, $alias = null) { self::$preventHaltTokenCheck ); } else { - // Otherwise Phar $this->archiveHandler = new __SystemLib\PharArchiveHandler( $fname, self::$preventHaltTokenCheck @@ -973,16 +968,15 @@ public static function interceptFileFuncs() { */ final public static function running(bool $retphar = true) { $filename = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS)[0]['file']; - $pharScheme = "phar://"; - $pharExt = ".phar"; - if(substr($filename, 0, strlen($pharScheme)) == $pharScheme) { + $pharScheme = 'phar://'; + $pharExt = '.phar'; + if (strpos($filename, $pharScheme) === 0) { $pharExtPos = strrpos($filename, $pharExt); - if($pharExtPos) { + if ($pharExtPos) { $endPos = $pharExtPos + strlen($pharExt); - if($retphar) { + if ($retphar) { return substr($filename, 0, $endPos); - } - else { + } else { return substr($filename, strlen($pharScheme), $endPos - strlen($pharScheme)); } @@ -1104,8 +1098,15 @@ private static function openPhar(string $full_filename): resource { } private function getFileData(string $filename): resource { - $stream = $this->archiveHandler->getStream($filename); - fseek($stream, 0); + if ($filename !== '') { + $stream = $this->archiveHandler->getStream($filename); + } else if ($stub = $this->getStub()) { + $stream = fopen('php://temp', 'w+b'); + fwrite($stream, $stub); + } else { + throw new PharException("No $filename in phar"); + } + rewind($stream); return $stream; } @@ -1119,6 +1120,15 @@ private function getFileData(string $filename): resource { * array([Phar object for alias], 'rest/of/path.php') */ private static function getPharAndFile(string $filename_or_alias) { + /** + * TODO: This is a hack, during `include 'phar:///path/to.phar';` + * `self::stat()` calls this method with `$filename_or_alias` that have + * double `phar://phar://` prefix; I have no idea why, but we can fix it + * here for now + */ + if (strpos($filename_or_alias, 'phar://phar://') === 0) { + $filename_or_alias = substr($filename_or_alias, 7); + } if (strpos($filename_or_alias, 'phar://') !== 0) { throw new PharException("Not a phar: $filename_or_alias"); } diff --git a/hphp/test/slow/ext_phar/basic.phar.bz2 b/hphp/test/slow/ext_phar/basic.phar.bz2 new file mode 100644 index 0000000000000000000000000000000000000000..1aff5886133d47a27f0a1a9c7ee8d7d0aa6ecb81 GIT binary patch literal 2691 zcmV-}3ViiKT4*#eL0KkKSv_atqW}c$|NizcFerEb|NsB*|GvNf|NkHW2!K!u0U!Vf z2m}H^U<;ftn%u_@Z5$&=cDpxfXaHy=hysiS#BWJx0{g>S*;1 z05+kZHldK(O&*{D>J0@MsM26XJQUa|`lC-!Gyoa^0iZMh0001R0MktX20#DSJ*AB zD%LKgzRZ@=cV*uwq!0C5f-z*wHCZQdbV)m^N?Eeg(%Xc4L5-F;0udTS5IbcA4CFjV z3^;wto5j@5WkyV;XI6KgZxt%_ox^*j#j4U0_oJl`;e|M1G1b@Ouk1cE18^GB+C9XL zhA|n9RE%mbhA6BCrIiz#=#}DfuFV8nyFJ#~qSB40YJ%Ho)xhj7twn<1qO^V#uAN(cIJhN1rygvUS2-O5z`9Q0 z*g51=R~RmPE+lL;mRx!7sWXvn_VmR^cv|*KOyJ1#=C4WU=&538cWA?v6%nJ2cI_@` z+|nVSIlgqU~$taKF-Nw@p&~CNI3L@*cOkrf;H zBalSO!v^S-iy~GNw%SQdwo_7rgOeQ#`le7}P-?tG5kZ>1wP$0(j&qD+DD&J|+BX2T zI0T9{A+l&-q>14I9F2Qm{XiAL$K4peCLFeWoF6h)}6laaKD6!Ozn z5k*!uz0HU~R`a1q**Tiz6x}l%vjm}|NgtftXEZgg;L8x4#ZL&PY=mJXhAK%XHaMF5 zsfw>7hL4pJ=CrF6-u7X8{gy+I* zPMPQ;5$-aJJK}eiQY+d7!le;Qk;75Ko@eyCJhZ2B8RJw)uwv8$q0HAr;BEPAu3LwS z!V({J~Xm}%S94)%+;?mPfZKc+`)i|h(d!T%vXvtM7mupX1r6~3MiveM~``oD8VhaY#fn>Bp zcuDnn3xe3q3_D;Ci%8QEI_?Tek(BbeU}#y3-r~ApuH+>QOuC0Wu`=yQLQ2p&$gB;*?>3F|y6b~4491UmOuQK0m!^y;on1VfD{DFI$w z>3=@g<^qWECDM73-@@a7ctOaCYCjwjN27DCS0gO@v}#mFGACC}*|KRpknsGlJy{Gw z8HkSLix1GKA9x}pf~9#bbs7PWUpED&bgZ~Py-R$8%qu;#hG~f`gap!Mdr(W$ z&THEAGM2L1mjREurDp>n+YrOsPgw!xp@cvJ5Xj|KG3U$J=pL(+tu!7GXw&d)Bm=hE z=w@+DqQIIujBML%wcwE?7idf*J}ktEIL%9}l|k=OtE{0TVny3U@2td?5LI&^lGZB` z@ZYfQe+&|WPU3Mzw}QKNv$uC@VVUce{pNVk?9@98RM{cSB~nDo2N4FKl+OnJLb-+o z)+(lqNQ0zB$qgttqgKjTt5VY0oDKx=Ny&+K-mY4bO7K=SGNO-MqD9=r0>NSjWFVKF zmzZtAZGeM1GobOXfe)bs0>O0w&@4bPIic11SDKCh1HXicxk1MAplA}fEU42wM2z$kbeLX3mNBFyb68~k}zo6A*Xb(@hca0v7-gpSPs z+hYN%5K@|uN%Jh~nz*$oURoDN&%EQ{868F`HQ=+RM={P+%$2(d$4DBqyh@zF=V|sU z!5XNDEQ)4v70ixF9Q=Vy!Pb5G9*Wv#Yd>dUq}q&wq-mjvpt^HFsAhfHIEa2RD%@PS z=sJ@(@$3V78s2P>ws>HdYDX&xC|zb=6poe}h6RL+C<&WrjN)7j7KO`BYU%60U7@O^ z1ISUQLcdI%piD}aFeH4 zdg5Q081ACD^5MyFC((*7LQNk)80_JU?`YZ%*6tzPo0C@01lAp=10l4Onbkoqg@UMy zs-(<@_TJ>@#;_tVM-p-O`8l3XP-%(3uJ6j3=QFdmw?lFoC056M4mT_=9P^_f*w&qg z{le`>D`ZKHg@opsOhhEYPHm7P88nYm)TV`&S4_-o;1T8|7-q?AMMP8^O`Z8(6fw~& z!DjNFM2IsisiYb(R6U{yXe18i5GJ6Ojn^Ebt0=&Bj`HRVYA2N^L|=GV^~el+@fA(6 z&}~VLR8GJu6Q25Yr9>W`Xya3cNyh(ffPCAH3Xwe?A x;||r_qakTy8chfR5CEHisVV^@Mvvw`SKCJg%^CG3|7fTFF64@Ep&)wC$3};c?^FN) literal 0 HcmV?d00001 diff --git a/hphp/test/slow/ext_phar/basic.phar.gz b/hphp/test/slow/ext_phar/basic.phar.gz new file mode 100644 index 0000000000000000000000000000000000000000..0ba02d055b50bcf448f82944dafde0139404a5ea GIT binary patch literal 2520 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 Date: Sat, 23 Apr 2016 18:31:00 +0300 Subject: [PATCH 14/22] Small fixes, tested and confirmed working support for: * .phar.tar * .phar.tar.gz * .phar.tar.bz2 * .phar.zip * .phar which is inside any of above --- .../archive_handler/PharArchiveHandler.ns.php | 1 + .../archive_handler/TarArchiveHandler.ns.php | 1 + hphp/system/php/phar/Phar.php | 7 +- hphp/test/slow/ext_phar/basic-tar-bz2.phar | Bin 0 -> 2776 bytes hphp/test/slow/ext_phar/basic-tar-gz.phar | Bin 0 -> 2638 bytes hphp/test/slow/ext_phar/basic-zip.phar | Bin 0 -> 7180 bytes hphp/test/slow/ext_phar/basic.phar.tar | Bin 0 -> 10752 bytes hphp/test/slow/ext_phar/basic.phar.tar.bz2 | Bin 0 -> 2776 bytes hphp/test/slow/ext_phar/basic.phar.tar.gz | Bin 0 -> 2638 bytes hphp/test/slow/ext_phar/basic.phar.zip | Bin 0 -> 7180 bytes hphp/test/slow/ext_phar/basic.php | 68 +++++++++++++++++- hphp/test/slow/ext_phar/basic.php.expect | 14 ++++ 12 files changed, 88 insertions(+), 3 deletions(-) create mode 100644 hphp/test/slow/ext_phar/basic-tar-bz2.phar create mode 100644 hphp/test/slow/ext_phar/basic-tar-gz.phar create mode 100644 hphp/test/slow/ext_phar/basic-zip.phar create mode 100644 hphp/test/slow/ext_phar/basic.phar.tar create mode 100644 hphp/test/slow/ext_phar/basic.phar.tar.bz2 create mode 100644 hphp/test/slow/ext_phar/basic.phar.tar.gz create mode 100644 hphp/test/slow/ext_phar/basic.phar.zip diff --git a/hphp/system/php/archive_handler/PharArchiveHandler.ns.php b/hphp/system/php/archive_handler/PharArchiveHandler.ns.php index 2698bff672f3a0..11287abe3a0646 100644 --- a/hphp/system/php/archive_handler/PharArchiveHandler.ns.php +++ b/hphp/system/php/archive_handler/PharArchiveHandler.ns.php @@ -208,6 +208,7 @@ public function getStream(string $path): resource { } $stream = fopen('php://temp', 'w+b');//TODO stream slice needed here fwrite($stream, $this->stream_get_contents($size, $offset)); + rewind($stream); return $stream; } diff --git a/hphp/system/php/archive_handler/TarArchiveHandler.ns.php b/hphp/system/php/archive_handler/TarArchiveHandler.ns.php index 97581a08f82436..113402545bce2e 100644 --- a/hphp/system/php/archive_handler/TarArchiveHandler.ns.php +++ b/hphp/system/php/archive_handler/TarArchiveHandler.ns.php @@ -134,6 +134,7 @@ public function getStream(string $path): resource { } $stream = fopen('php://temp', 'w+b');//TODO stream slice needed here fwrite($stream, $this->contents[$path]); + rewind($stream); return $stream; } diff --git a/hphp/system/php/phar/Phar.php b/hphp/system/php/phar/Phar.php index 8bec5a5d421a61..0ec10e30bb096f 100644 --- a/hphp/system/php/phar/Phar.php +++ b/hphp/system/php/phar/Phar.php @@ -94,11 +94,14 @@ public function __construct($fname, $flags = null, $alias = null) { if (strpos($magic_number, 'BZ') === 0) { fclose($fp); $fp = bzopen($fname, 'r'); + fseek($fp, 257, SEEK_CUR); // Bzip2 only knows how to use SEEK_CUR } else if (strpos($magic_number, "\x1F\x8B") === 0) { fclose($fp); $fp = gzopen($fname, 'rb'); + fseek($fp, 257); + } else { + fseek($fp, 257); } - fseek($fp, 257, SEEK_CUR); // Bzip2 only knows how to use SEEK_CUR $magic_number = fread($fp, 8); fclose($fp); if ( @@ -1103,10 +1106,10 @@ private function getFileData(string $filename): resource { } else if ($stub = $this->getStub()) { $stream = fopen('php://temp', 'w+b'); fwrite($stream, $stub); + rewind($stream); } else { throw new PharException("No $filename in phar"); } - rewind($stream); return $stream; } diff --git a/hphp/test/slow/ext_phar/basic-tar-bz2.phar b/hphp/test/slow/ext_phar/basic-tar-bz2.phar new file mode 100644 index 0000000000000000000000000000000000000000..7f7ac39e844b23b43d660d5acbef535ce810c407 GIT binary patch literal 2776 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 literal 0 HcmV?d00001 diff --git a/hphp/test/slow/ext_phar/basic-tar-gz.phar b/hphp/test/slow/ext_phar/basic-tar-gz.phar new file mode 100644 index 0000000000000000000000000000000000000000..8220289a3608c024a7afa6869d84d8487d13ed58 GIT binary patch literal 2638 zcmV-U3bFMciwFP!000001MON%bK}MlcIAkwfxc|G& z&TgyO*={3!x4pCbc>h1b;P+qC^cy-M`^4~6J1}~h=DEc5!m*Vk)}3i!q8OVbWeK%{ z@szUhO=86{O;WR3B~PCc+qcqmOmDrErKT|u>`hpEtKp>E^+HAyo5t*g=hGMS&<4q# zmVzcPwEek5$Fap`!cw=vUw8rv>(Kt9GYGVUYk0&Gd%AP zFN|UT&)@XVM&oaWuZ&B+8S6M+_eaO4UrDA1*2zAc#>@W5An)Fh_pl94&qhX-Z*C|1uc8WsTUkywE^mIW>+;$r~GGWQRFr z?So2E?i7=2>xy@v$Js3Nv3){WavYgzni){xh9Z1OvQ6T!zr7IBXz;q)VmjZiivAEND{V{TO1Dw7kE3pSn@=;f?R9X zAs_cii!-TvVinatB-rMGdefN?|JZodzw%9h@YcEe3+xF%B98kCz)5?n0jg%6O?Vpi zvRRaPQ^?+)W=CPl$jk5GoSU~T0Q`#dr|;UF*3QvhADw;8iCZ}$hi1!=RV>80@>5y`cXc<$X%xdnQq^&j7|8?^$4x1{GJypu6!qP1 z7Yrb#IsuV+7+dz0X`BPrL4w@O@RpiN4U#!BEg0v)TfgB<#*~nK2gV?Jx`|hAm~E}HL45$lO#&I zqRJPf{21-`B=nesHeHjUBi0dPHAns*oTJ`zw^!UB|Ez&&0Aj0Of{@VX!lKv8mX*|kw? zs6?)D2Q!ej;vih3QWr`zE5$3DmFtZNXk_2a;}iA#3gEybDgh+0o~hN$W0(BGHoU)d zU!Vd#eZLtUU1=gOvl2uoTtNy7dI)J4Z{UIj?E;DxV^4csO$PQ&$P zg&AdA*xUf47%%pmLgOe%pk5W05|cC#CPX+3rQ;$~bgBNTHik#}912?Il+`$&G|uhD zxzjkOjdQo$$u@XdRohV1e6fYf(>U=$=9&4x;#6P9uA19+wp&C|=2(s6?3Cj>WJ>BZ}5-42iFE z*ENY~bvr8ub{;=-QRuOUd2DP~rJ`o8dIF)QaCxa1*O|w2!i!wU0)Q%a7m$%tvPxYp zDLRmz123=dqk=%r0UOe5xS&(mCgg#LYThNgm*sk-tL0W6Hi$44il@$=Ky0{~4!AdzPW zztukVk5iB1gAe)puQv@XxJ$GSH}w~{@&5n+V|(fM-&V7|v-9}-??)KFfq%b;e?R^B wui$_G`1`y6{QJK@{qysa`RBj;@#|0R+v9i~kK=JXjt@8f57|~MGyp090EjaKLI3~& literal 0 HcmV?d00001 diff --git a/hphp/test/slow/ext_phar/basic-zip.phar b/hphp/test/slow/ext_phar/basic-zip.phar new file mode 100644 index 0000000000000000000000000000000000000000..d225bf5a314de33fe6ca98c79d7f8bee64221cd6 GIT binary patch literal 7180 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 E5B^eEUH||9 literal 0 HcmV?d00001 diff --git a/hphp/test/slow/ext_phar/basic.phar.tar b/hphp/test/slow/ext_phar/basic.phar.tar new file mode 100644 index 0000000000000000000000000000000000000000..ce5528fe6f16d46a6e9dc5cc749dce039071b4a5 GIT binary patch literal 10752 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 literal 0 HcmV?d00001 diff --git a/hphp/test/slow/ext_phar/basic.phar.tar.gz b/hphp/test/slow/ext_phar/basic.phar.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..8220289a3608c024a7afa6869d84d8487d13ed58 GIT binary patch literal 2638 zcmV-U3bFMciwFP!000001MON%bK}MlcIAkwfxc|G& z&TgyO*={3!x4pCbc>h1b;P+qC^cy-M`^4~6J1}~h=DEc5!m*Vk)}3i!q8OVbWeK%{ z@szUhO=86{O;WR3B~PCc+qcqmOmDrErKT|u>`hpEtKp>E^+HAyo5t*g=hGMS&<4q# zmVzcPwEek5$Fap`!cw=vUw8rv>(Kt9GYGVUYk0&Gd%AP zFN|UT&)@XVM&oaWuZ&B+8S6M+_eaO4UrDA1*2zAc#>@W5An)Fh_pl94&qhX-Z*C|1uc8WsTUkywE^mIW>+;$r~GGWQRFr z?So2E?i7=2>xy@v$Js3Nv3){WavYgzni){xh9Z1OvQ6T!zr7IBXz;q)VmjZiivAEND{V{TO1Dw7kE3pSn@=;f?R9X zAs_cii!-TvVinatB-rMGdefN?|JZodzw%9h@YcEe3+xF%B98kCz)5?n0jg%6O?Vpi zvRRaPQ^?+)W=CPl$jk5GoSU~T0Q`#dr|;UF*3QvhADw;8iCZ}$hi1!=RV>80@>5y`cXc<$X%xdnQq^&j7|8?^$4x1{GJypu6!qP1 z7Yrb#IsuV+7+dz0X`BPrL4w@O@RpiN4U#!BEg0v)TfgB<#*~nK2gV?Jx`|hAm~E}HL45$lO#&I zqRJPf{21-`B=nesHeHjUBi0dPHAns*oTJ`zw^!UB|Ez&&0Aj0Of{@VX!lKv8mX*|kw? zs6?)D2Q!ej;vih3QWr`zE5$3DmFtZNXk_2a;}iA#3gEybDgh+0o~hN$W0(BGHoU)d zU!Vd#eZLtUU1=gOvl2uoTtNy7dI)J4Z{UIj?E;DxV^4csO$PQ&$P zg&AdA*xUf47%%pmLgOe%pk5W05|cC#CPX+3rQ;$~bgBNTHik#}912?Il+`$&G|uhD zxzjkOjdQo$$u@XdRohV1e6fYf(>U=$=9&4x;#6P9uA19+wp&C|=2(s6?3Cj>WJ>BZ}5-42iFE z*ENY~bvr8ub{;=-QRuOUd2DP~rJ`o8dIF)QaCxa1*O|w2!i!wU0)Q%a7m$%tvPxYp zDLRmz123=dqk=%r0UOe5xS&(mCgg#LYThNgm*sk-tL0W6Hi$44il@$=Ky0{~4!AdzPW zztukVk5iB1gAe)puQv@XxJ$GSH}w~{@&5n+V|(fM-&V7|v-9}-??)KFfq%b;e?R^B wui$_G`1`y6{QJK@{qysa`RBj;@#|0R+v9i~kK=JXjt@8f57|~MGyp090EjaKLI3~& literal 0 HcmV?d00001 diff --git a/hphp/test/slow/ext_phar/basic.phar.zip b/hphp/test/slow/ext_phar/basic.phar.zip new file mode 100644 index 0000000000000000000000000000000000000000..d225bf5a314de33fe6ca98c79d7f8bee64221cd6 GIT binary patch literal 7180 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 E5B^eEUH||9 literal 0 HcmV?d00001 diff --git a/hphp/test/slow/ext_phar/basic.php b/hphp/test/slow/ext_phar/basic.php index db6a1bb5eeef54..eb57a0d79fcee7 100644 --- a/hphp/test/slow/ext_phar/basic.php +++ b/hphp/test/slow/ext_phar/basic.php @@ -1,5 +1,7 @@ Date: Sat, 23 Apr 2016 19:21:57 +0300 Subject: [PATCH 15/22] Small fixes, more tests for compression and signatures --- .../php/archive_handler/ArchiveHandler.ns.php | 28 +++++- .../archive_handler/PharArchiveHandler.ns.php | 4 +- .../archive_handler/TarArchiveHandler.ns.php | 2 +- hphp/system/php/phar/Phar.php | 10 +- hphp/test/slow/ext_phar/basic-tar.phar | Bin 0 -> 10752 bytes hphp/test/slow/ext_phar/basic.php | 10 ++ hphp/test/slow/ext_phar/basic.php.expect | 2 + hphp/test/slow/ext_phar/get_signature.php | 57 +++++++++++ .../slow/ext_phar/get_signature.php.expect | 55 ++++++++++ hphp/test/slow/ext_phar/is_compressed.php | 57 +++++++++++ .../slow/ext_phar/is_compressed.php.expect | 35 +++++++ hphp/test/slow/ext_phar/is_file_format.php | 93 +++++++++++++++++ .../slow/ext_phar/is_file_format.php.expect | 95 ++++++++++++++++++ .../slow/ext_phar/supported_compression.php | 2 + .../ext_phar/supported_compression.php.expect | 6 ++ .../slow/ext_phar/supported_signatures.php | 2 + .../ext_phar/supported_signatures.php.expect | 10 ++ 17 files changed, 458 insertions(+), 10 deletions(-) create mode 100644 hphp/test/slow/ext_phar/basic-tar.phar create mode 100644 hphp/test/slow/ext_phar/get_signature.php create mode 100644 hphp/test/slow/ext_phar/get_signature.php.expect create mode 100644 hphp/test/slow/ext_phar/is_compressed.php create mode 100644 hphp/test/slow/ext_phar/is_compressed.php.expect create mode 100644 hphp/test/slow/ext_phar/is_file_format.php create mode 100644 hphp/test/slow/ext_phar/is_file_format.php.expect create mode 100644 hphp/test/slow/ext_phar/supported_compression.php create mode 100644 hphp/test/slow/ext_phar/supported_compression.php.expect create mode 100644 hphp/test/slow/ext_phar/supported_signatures.php create mode 100644 hphp/test/slow/ext_phar/supported_signatures.php.expect diff --git a/hphp/system/php/archive_handler/ArchiveHandler.ns.php b/hphp/system/php/archive_handler/ArchiveHandler.ns.php index 01ae9572064856..2d64472fe43997 100644 --- a/hphp/system/php/archive_handler/ArchiveHandler.ns.php +++ b/hphp/system/php/archive_handler/ArchiveHandler.ns.php @@ -21,6 +21,7 @@ abstract class ArchiveHandler { protected string $apiVersion = '1.0.0'; protected $metadata; protected ?string $signature; + protected int $signatureType = Phar::NONE; protected $compressed = false; abstract public function getStream(string $path): resource; @@ -68,12 +69,31 @@ public function hasMetadata() { return $this->metadata !== null; } - public function getSignature(): string { - return $this->signature; + public function getSignature(): ?array { + switch ($this->signatureType) { + case Phar::MD5: + $hash_type = 'MD5'; + break; + case Phar::SHA1: + $hash_type = 'SHA-1'; + break; + case Phar::SHA256: + $hash_type = 'SHA-256'; + break; + case Phar::SHA512: + $hash_type = 'SHA-512'; + break; + default: + return null; + } + return [ + 'hash' => bin2hex($this->signature), + 'hash_type' => $hash_type + ]; } public function isCompressed() { - return $this->compressed(); + return $this->compressed; } // Custom methods used by Phar class internally @@ -101,8 +121,8 @@ protected function open(string $path) { $this->stream = bzopen($path, 'r'); } else if ($data === "\x1F\x8B") { $this->stream = gzopen($path, 'rb'); - } else { $this->compressed = Phar::GZ; + } else { $this->stream = fopen($path, 'rb'); } } diff --git a/hphp/system/php/archive_handler/PharArchiveHandler.ns.php b/hphp/system/php/archive_handler/PharArchiveHandler.ns.php index 11287abe3a0646..d44d649f4dc99c 100644 --- a/hphp/system/php/archive_handler/PharArchiveHandler.ns.php +++ b/hphp/system/php/archive_handler/PharArchiveHandler.ns.php @@ -98,8 +98,8 @@ private function parsePhar(&$pos) { } $pos = $signatureStart + $signatureSize - 8; - $signatureFlags = $this->bytesToInt($pos, 4); - switch ($signatureFlags) { + $this->signatureType = $this->bytesToInt($pos, 4); + switch ($this->signatureType) { case Phar::MD5: $digestSize = 16; $digestName = 'md5'; diff --git a/hphp/system/php/archive_handler/TarArchiveHandler.ns.php b/hphp/system/php/archive_handler/TarArchiveHandler.ns.php index 113402545bce2e..e0b7d8ae6380e5 100644 --- a/hphp/system/php/archive_handler/TarArchiveHandler.ns.php +++ b/hphp/system/php/archive_handler/TarArchiveHandler.ns.php @@ -46,9 +46,9 @@ private function readTar() { $this->compressed = Phar::BZ2; $fp = bzopen($path, 'r'); } else if ($data === "\x1F\x8B") { + $this->compressed = Phar::GZ; $fp = gzopen($path, 'rb'); } else { - $this->compressed = Phar::GZ; $fp = fopen($path, 'rb'); } diff --git a/hphp/system/php/phar/Phar.php b/hphp/system/php/phar/Phar.php index 0ec10e30bb096f..3e17de9fb3f0f8 100644 --- a/hphp/system/php/phar/Phar.php +++ b/hphp/system/php/phar/Phar.php @@ -52,6 +52,7 @@ class Phar extends RecursiveDirectoryIterator protected $archiveHandler; protected $iteratorRoot; protected $iterator; + protected int $format = self::PHAR; /** * ( excerpt from http://php.net/manual/en/phar.construct.php ) @@ -86,6 +87,7 @@ public function __construct($fname, $flags = null, $alias = null) { // This is not a bullet-proof check, but should be good enough to catch ZIP if (strcmp($magic_number, "PK\x03\x04") === 0) { fclose($fp); + $this->format = self::ZIP; $this->archiveHandler = new __SystemLib\ZipArchiveHandler( $fname, self::$preventHaltTokenCheck @@ -108,11 +110,13 @@ public function __construct($fname, $flags = null, $alias = null) { strpos($magic_number, "ustar\x0") === 0 || strpos($magic_number, "ustar\x40\x40\x0") === 0 ) { + $this->format = self::TAR; $this->archiveHandler = new __SystemLib\TarArchiveHandler( $fname, self::$preventHaltTokenCheck ); } else { + $this->format = self::PHAR; $this->archiveHandler = new __SystemLib\PharArchiveHandler( $fname, self::$preventHaltTokenCheck @@ -550,7 +554,7 @@ public function isCompressed() { * requested by the parameter */ public function isFileFormat($fileformat) { - return $fileformat === self::PHAR; + return $fileformat === $this->format; } /** @@ -866,7 +870,7 @@ final public static function canCompress($type = 0) { * extension. */ final public static function getSupportedCompression() { - return array(); + return [self::GZ, self::BZ2]; } /** @@ -880,7 +884,7 @@ final public static function getSupportedCompression() { * OpenSSL. */ final public static function getSupportedSignatures () { - return array(); + return ['MD5', 'SHA-1', 'SHA-256', 'SHA-512']; } /** diff --git a/hphp/test/slow/ext_phar/basic-tar.phar b/hphp/test/slow/ext_phar/basic-tar.phar new file mode 100644 index 0000000000000000000000000000000000000000..ce5528fe6f16d46a6e9dc5cc749dce039071b4a5 GIT binary patch literal 10752 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 + +echo "\n.phar:\n"; +var_dump((new Phar(__DIR__."/basic.phar"))->getSignature()); + +echo "\n.phar.gz:\n"; +var_dump((new Phar(__DIR__."/basic.phar.gz"))->getSignature()); + +echo "\n.phar.bz2:\n"; +var_dump((new Phar(__DIR__."/basic.phar.bz2"))->getSignature()); + +/** + * Tar-based Phar + */ +echo "\n.phar.tar:\n"; +var_dump((new Phar(__DIR__."/basic.phar.tar"))->getSignature()); + +echo "\n.phar.tar.gz:\n"; +var_dump((new Phar(__DIR__."/basic.phar.tar.gz"))->getSignature()); + +echo "\n.phar.tar.bz2:\n"; +var_dump((new Phar(__DIR__."/basic.phar.tar.bz2"))->getSignature()); + +/** + * Zip-based Phar + */ +echo "\n.phar.zip:\n"; +var_dump((new Phar(__DIR__."/basic.phar.zip"))->getSignature()); + +/** + * Tar with extension of regular Phar + */ +echo "\n.phar (which is .phar.tar):\n"; +var_dump((new Phar(__DIR__."/basic-tar.phar"))->getSignature()); + +/** + * Tar GZ with extension of regular Phar + */ +echo "\n.phar (which is .phar.tar.gz):\n"; +var_dump((new Phar(__DIR__."/basic-tar-gz.phar"))->getSignature()); + +/** + * Tar BZ2 with extension of regular Phar + */ +echo "\n.phar (which is .phar.tar.bz2):\n"; +var_dump((new Phar(__DIR__."/basic-tar-bz2.phar"))->getSignature()); + +/** + * Zip with extension of regular Phar + */ +echo "\n.phar (which is .phar.zip):\n"; +var_dump((new Phar(__DIR__."/basic-zip.phar"))->getSignature()); diff --git a/hphp/test/slow/ext_phar/get_signature.php.expect b/hphp/test/slow/ext_phar/get_signature.php.expect new file mode 100644 index 00000000000000..01a08c39de1b6b --- /dev/null +++ b/hphp/test/slow/ext_phar/get_signature.php.expect @@ -0,0 +1,55 @@ +.phar: +array(2) { + ["hash"]=> + string(40) "b9232afb6428fbfc2856cc74d35b84a1a37b960a" + ["hash_type"]=> + string(5) "SHA-1" +} + +.phar: +array(2) { + ["hash"]=> + string(40) "b9232afb6428fbfc2856cc74d35b84a1a37b960a" + ["hash_type"]=> + string(5) "SHA-1" +} + +.phar.gz: +array(2) { + ["hash"]=> + string(40) "c1e20e0b4f1aab244f7455b39025a09e01c919cc" + ["hash_type"]=> + string(5) "SHA-1" +} + +.phar.bz2: +array(2) { + ["hash"]=> + string(40) "b946f87e2d2fd37eabf53d78978111dc70cba212" + ["hash_type"]=> + string(5) "SHA-1" +} + +.phar.tar: +NULL + +.phar.tar.gz: +NULL + +.phar.tar.bz2: +NULL + +.phar.zip: +NULL + +.phar (which is .phar.tar): +NULL + +.phar (which is .phar.tar.gz): +NULL + +.phar (which is .phar.tar.bz2): +NULL + +.phar (which is .phar.zip): +NULL diff --git a/hphp/test/slow/ext_phar/is_compressed.php b/hphp/test/slow/ext_phar/is_compressed.php new file mode 100644 index 00000000000000..916e15cdbf0f24 --- /dev/null +++ b/hphp/test/slow/ext_phar/is_compressed.php @@ -0,0 +1,57 @@ +isCompressed()); + +echo "\n.phar:\n"; +var_dump((new Phar(__DIR__."/basic.phar"))->isCompressed()); + +echo "\n.phar.gz:\n"; +var_dump((new Phar(__DIR__."/basic.phar.gz"))->isCompressed()); + +echo "\n.phar.bz2:\n"; +var_dump((new Phar(__DIR__."/basic.phar.bz2"))->isCompressed()); + +/** + * Tar-based Phar + */ +echo "\n.phar.tar:\n"; +var_dump((new Phar(__DIR__."/basic.phar.tar"))->isCompressed()); + +echo "\n.phar.tar.gz:\n"; +var_dump((new Phar(__DIR__."/basic.phar.tar.gz"))->isCompressed()); + +echo "\n.phar.tar.bz2:\n"; +var_dump((new Phar(__DIR__."/basic.phar.tar.bz2"))->isCompressed()); + +/** + * Zip-based Phar + */ +echo "\n.phar.zip:\n"; +var_dump((new Phar(__DIR__."/basic.phar.zip"))->isCompressed()); + +/** + * Tar with extension of regular Phar + */ +echo "\n.phar (which is .phar.tar):\n"; +var_dump((new Phar(__DIR__."/basic-tar.phar"))->isCompressed()); + +/** + * Tar GZ with extension of regular Phar + */ +echo "\n.phar (which is .phar.tar.gz):\n"; +var_dump((new Phar(__DIR__."/basic-tar-gz.phar"))->isCompressed()); + +/** + * Tar BZ2 with extension of regular Phar + */ +echo "\n.phar (which is .phar.tar.bz2):\n"; +var_dump((new Phar(__DIR__."/basic-tar-bz2.phar"))->isCompressed()); + +/** + * Zip with extension of regular Phar + */ +echo "\n.phar (which is .phar.zip):\n"; +var_dump((new Phar(__DIR__."/basic-zip.phar"))->isCompressed()); diff --git a/hphp/test/slow/ext_phar/is_compressed.php.expect b/hphp/test/slow/ext_phar/is_compressed.php.expect new file mode 100644 index 00000000000000..88d7f47ac6ffc2 --- /dev/null +++ b/hphp/test/slow/ext_phar/is_compressed.php.expect @@ -0,0 +1,35 @@ +.phar: +bool(false) + +.phar: +bool(false) + +.phar.gz: +int(4096) + +.phar.bz2: +int(8192) + +.phar.tar: +bool(false) + +.phar.tar.gz: +int(4096) + +.phar.tar.bz2: +int(8192) + +.phar.zip: +bool(false) + +.phar (which is .phar.tar): +bool(false) + +.phar (which is .phar.tar.gz): +int(4096) + +.phar (which is .phar.tar.bz2): +int(8192) + +.phar (which is .phar.zip): +bool(false) diff --git a/hphp/test/slow/ext_phar/is_file_format.php b/hphp/test/slow/ext_phar/is_file_format.php new file mode 100644 index 00000000000000..c35c19c717ff42 --- /dev/null +++ b/hphp/test/slow/ext_phar/is_file_format.php @@ -0,0 +1,93 @@ +isFileFormat(Phar::PHAR)); +var_dump('is tar', $p->isFileFormat(Phar::TAR)); +var_dump('is zip', $p->isFileFormat(Phar::ZIP)); + +echo "\n.phar:\n"; +$p = new Phar(__DIR__."/basic.phar"); +var_dump('is phar', $p->isFileFormat(Phar::PHAR)); +var_dump('is tar', $p->isFileFormat(Phar::TAR)); +var_dump('is zip', $p->isFileFormat(Phar::ZIP)); + +echo "\n.phar.gz:\n"; +$p = new Phar(__DIR__."/basic.phar.gz"); +var_dump('is phar', $p->isFileFormat(Phar::PHAR)); +var_dump('is tar', $p->isFileFormat(Phar::TAR)); +var_dump('is zip', $p->isFileFormat(Phar::ZIP)); + +echo "\n.phar.bz2:\n"; +$p = new Phar(__DIR__."/basic.phar.bz2"); +var_dump('is phar', $p->isFileFormat(Phar::PHAR)); +var_dump('is tar', $p->isFileFormat(Phar::TAR)); +var_dump('is zip', $p->isFileFormat(Phar::ZIP)); + +/** + * Tar-based Phar + */ +echo "\n.phar.tar:\n"; +$p = new Phar(__DIR__."/basic.phar.tar"); +var_dump('is phar', $p->isFileFormat(Phar::PHAR)); +var_dump('is tar', $p->isFileFormat(Phar::TAR)); +var_dump('is zip', $p->isFileFormat(Phar::ZIP)); + +echo "\n.phar.tar.gz:\n"; +$p = new Phar(__DIR__."/basic.phar.tar.gz"); +var_dump('is phar', $p->isFileFormat(Phar::PHAR)); +var_dump('is tar', $p->isFileFormat(Phar::TAR)); +var_dump('is zip', $p->isFileFormat(Phar::ZIP)); + +echo "\n.phar.tar.bz2:\n"; +$p = new Phar(__DIR__."/basic.phar.tar.bz2"); +var_dump('is phar', $p->isFileFormat(Phar::PHAR)); +var_dump('is tar', $p->isFileFormat(Phar::TAR)); +var_dump('is zip', $p->isFileFormat(Phar::ZIP)); + +/** + * Zip-based Phar + */ +echo "\n.phar.zip:\n"; +$p = new Phar(__DIR__."/basic.phar.zip"); +var_dump('is phar', $p->isFileFormat(Phar::PHAR)); +var_dump('is tar', $p->isFileFormat(Phar::TAR)); +var_dump('is zip', $p->isFileFormat(Phar::ZIP)); + +/** + * Tar with extension of regular Phar + */ +echo "\n.phar (which is .phar.tar):\n"; +$p = new Phar(__DIR__."/basic-tar.phar"); +var_dump('is phar', $p->isFileFormat(Phar::PHAR)); +var_dump('is tar', $p->isFileFormat(Phar::TAR)); +var_dump('is zip', $p->isFileFormat(Phar::ZIP)); + +/** + * Tar GZ with extension of regular Phar + */ +echo "\n.phar (which is .phar.tar.gz):\n"; +$p = new Phar(__DIR__."/basic-tar-gz.phar"); +var_dump('is phar', $p->isFileFormat(Phar::PHAR)); +var_dump('is tar', $p->isFileFormat(Phar::TAR)); +var_dump('is zip', $p->isFileFormat(Phar::ZIP)); + +/** + * Tar BZ2 with extension of regular Phar + */ +echo "\n.phar (which is .phar.tar.bz2):\n"; +$p = new Phar(__DIR__."/basic-tar-bz2.phar"); +var_dump('is phar', $p->isFileFormat(Phar::PHAR)); +var_dump('is tar', $p->isFileFormat(Phar::TAR)); +var_dump('is zip', $p->isFileFormat(Phar::ZIP)); + +/** + * Zip with extension of regular Phar + */ +echo "\n.phar (which is .phar.zip):\n"; +$p = new Phar(__DIR__."/basic-zip.phar"); +var_dump('is phar', $p->isFileFormat(Phar::PHAR)); +var_dump('is tar', $p->isFileFormat(Phar::TAR)); +var_dump('is zip', $p->isFileFormat(Phar::ZIP)); diff --git a/hphp/test/slow/ext_phar/is_file_format.php.expect b/hphp/test/slow/ext_phar/is_file_format.php.expect new file mode 100644 index 00000000000000..7726677de50b44 --- /dev/null +++ b/hphp/test/slow/ext_phar/is_file_format.php.expect @@ -0,0 +1,95 @@ +.phar: +string(7) "is phar" +bool(true) +string(6) "is tar" +bool(false) +string(6) "is zip" +bool(false) + +.phar: +string(7) "is phar" +bool(true) +string(6) "is tar" +bool(false) +string(6) "is zip" +bool(false) + +.phar.gz: +string(7) "is phar" +bool(true) +string(6) "is tar" +bool(false) +string(6) "is zip" +bool(false) + +.phar.bz2: +string(7) "is phar" +bool(true) +string(6) "is tar" +bool(false) +string(6) "is zip" +bool(false) + +.phar.tar: +string(7) "is phar" +bool(false) +string(6) "is tar" +bool(true) +string(6) "is zip" +bool(false) + +.phar.tar.gz: +string(7) "is phar" +bool(false) +string(6) "is tar" +bool(true) +string(6) "is zip" +bool(false) + +.phar.tar.bz2: +string(7) "is phar" +bool(false) +string(6) "is tar" +bool(true) +string(6) "is zip" +bool(false) + +.phar.zip: +string(7) "is phar" +bool(false) +string(6) "is tar" +bool(false) +string(6) "is zip" +bool(true) + +.phar (which is .phar.tar): +string(7) "is phar" +bool(false) +string(6) "is tar" +bool(true) +string(6) "is zip" +bool(false) + +.phar (which is .phar.tar.gz): +string(7) "is phar" +bool(false) +string(6) "is tar" +bool(true) +string(6) "is zip" +bool(false) + +.phar (which is .phar.tar.bz2): +string(7) "is phar" +bool(false) +string(6) "is tar" +bool(true) +string(6) "is zip" +bool(false) + +.phar (which is .phar.zip): +string(7) "is phar" +bool(false) +string(6) "is tar" +bool(false) +string(6) "is zip" +bool(true) diff --git a/hphp/test/slow/ext_phar/supported_compression.php b/hphp/test/slow/ext_phar/supported_compression.php new file mode 100644 index 00000000000000..63ffaf6b69c1da --- /dev/null +++ b/hphp/test/slow/ext_phar/supported_compression.php @@ -0,0 +1,2 @@ + + int(4096) + [1]=> + int(8192) +} diff --git a/hphp/test/slow/ext_phar/supported_signatures.php b/hphp/test/slow/ext_phar/supported_signatures.php new file mode 100644 index 00000000000000..d67a70ac2a0e31 --- /dev/null +++ b/hphp/test/slow/ext_phar/supported_signatures.php @@ -0,0 +1,2 @@ + + string(3) "MD5" + [1]=> + string(5) "SHA-1" + [2]=> + string(7) "SHA-256" + [3]=> + string(7) "SHA-512" +} From fbd06a36011fd3774b2e7f7289db20f2a0620c54 Mon Sep 17 00:00:00 2001 From: Nazar Mokrynskyi Date: Sun, 24 Apr 2016 19:16:23 +0300 Subject: [PATCH 16/22] Ported original PHP test for `Phar::isValidPharFilename()` method, method itself adjusted to pass all the tests --- hphp/system/php/phar/Phar.php | 14 ++-- .../slow/ext_phar/is_valid_phar_filename.php | 78 +++++++++++++++++++ .../is_valid_phar_filename.php.expectf | 60 ++++++++++++++ 3 files changed, 146 insertions(+), 6 deletions(-) create mode 100644 hphp/test/slow/ext_phar/is_valid_phar_filename.php create mode 100644 hphp/test/slow/ext_phar/is_valid_phar_filename.php.expectf diff --git a/hphp/system/php/phar/Phar.php b/hphp/system/php/phar/Phar.php index 3e17de9fb3f0f8..b685bebe37a6c2 100644 --- a/hphp/system/php/phar/Phar.php +++ b/hphp/system/php/phar/Phar.php @@ -569,12 +569,14 @@ public function isFileFormat($fileformat) { * * @return bool TRUE if the filename is valid, FALSE if not. */ - public static function isValidPharFilename ( - $filename, - $executable = true - ) { - $parts = explode('.', $filename); - return $executable ? in_array('phar', $parts) : count($parts) > 1; + public static function isValidPharFilename( + string $filename, + bool $executable = true + ): bool { + $filename = basename($filename); + $pharExt = preg_match('/.+\.phar(\..+|$)/i', $filename) === 1; + return $executable ? $pharExt + : !$pharExt && strpos($filename, '.') !== false; } /** diff --git a/hphp/test/slow/ext_phar/is_valid_phar_filename.php b/hphp/test/slow/ext_phar/is_valid_phar_filename.php new file mode 100644 index 00000000000000..3f46293eaf4c5a --- /dev/null +++ b/hphp/test/slow/ext_phar/is_valid_phar_filename.php @@ -0,0 +1,78 @@ + Date: Fri, 29 Apr 2016 22:05:59 +0300 Subject: [PATCH 17/22] Redundant test removed, `is_valid_phar_filename` test already covers this and many more cases --- hphp/test/slow/ext_phar/phar_incorrect_extension.php | 3 --- hphp/test/slow/ext_phar/phar_incorrect_extension.php.expectf | 4 ---- hphp/test/slow/ext_phar/phar_incorrect_extension.php.foo | 0 3 files changed, 7 deletions(-) delete mode 100644 hphp/test/slow/ext_phar/phar_incorrect_extension.php delete mode 100644 hphp/test/slow/ext_phar/phar_incorrect_extension.php.expectf delete mode 100644 hphp/test/slow/ext_phar/phar_incorrect_extension.php.foo diff --git a/hphp/test/slow/ext_phar/phar_incorrect_extension.php b/hphp/test/slow/ext_phar/phar_incorrect_extension.php deleted file mode 100644 index 53e1ae1ce6abbc..00000000000000 --- a/hphp/test/slow/ext_phar/phar_incorrect_extension.php +++ /dev/null @@ -1,3 +0,0 @@ -__construct() -#1 {main} diff --git a/hphp/test/slow/ext_phar/phar_incorrect_extension.php.foo b/hphp/test/slow/ext_phar/phar_incorrect_extension.php.foo deleted file mode 100644 index e69de29bb2d1d6..00000000000000 From 9727e07eeac19c4e6434594e8a1b63ee693ceb99 Mon Sep 17 00:00:00 2001 From: Nazar Mokrynskyi Date: Fri, 29 Apr 2016 22:44:44 +0300 Subject: [PATCH 18/22] Better memory efficiency when getting file contents from Phar-based archive --- .../php/archive_handler/PharArchiveHandler.ns.php | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/hphp/system/php/archive_handler/PharArchiveHandler.ns.php b/hphp/system/php/archive_handler/PharArchiveHandler.ns.php index d44d649f4dc99c..87b693ef63bd7b 100644 --- a/hphp/system/php/archive_handler/PharArchiveHandler.ns.php +++ b/hphp/system/php/archive_handler/PharArchiveHandler.ns.php @@ -206,8 +206,14 @@ public function getStream(string $path): resource { if ($size == 0) { return fopen('php://temp', 'w+b'); } - $stream = fopen('php://temp', 'w+b');//TODO stream slice needed here - fwrite($stream, $this->stream_get_contents($size, $offset)); + $stream = fopen('php://temp', 'w+b'); + //TODO stream slice needed here + while ($size) { + $data = $this->stream_get_contents(min(1024, $size), $offset); + fwrite($stream, $data); + $size -= strlen($data); + $offset += strlen($data); + } rewind($stream); return $stream; } From ad5fb4f095736c3b63750e6a903078642c4cd0a6 Mon Sep 17 00:00:00 2001 From: Nazar Mokrynskyi Date: Fri, 29 Apr 2016 23:29:08 +0300 Subject: [PATCH 19/22] Better memory efficiency when parsing and getting file contents from Tar-based archive. More unification of Phar- and Tar-based archives handling. --- .../php/archive_handler/ArchiveHandler.ns.php | 24 +++++- .../archive_handler/PharArchiveHandler.ns.php | 22 +---- .../archive_handler/TarArchiveHandler.ns.php | 81 +++++++------------ hphp/test/slow/ext_phar/phar_data_extract.php | 2 +- 4 files changed, 53 insertions(+), 76 deletions(-) diff --git a/hphp/system/php/archive_handler/ArchiveHandler.ns.php b/hphp/system/php/archive_handler/ArchiveHandler.ns.php index 2d64472fe43997..61b0f784a1de03 100644 --- a/hphp/system/php/archive_handler/ArchiveHandler.ns.php +++ b/hphp/system/php/archive_handler/ArchiveHandler.ns.php @@ -24,7 +24,27 @@ abstract class ArchiveHandler { protected int $signatureType = Phar::NONE; protected $compressed = false; - abstract public function getStream(string $path): resource; + // Default implementation, used by Phar- and Tar-based archives + public function getStream(string $path): resource { + if (!isset($this->fileOffsets[$path])) { + throw new PharException("No $path in phar"); + } + list($offset, $size) = $this->fileOffsets[$path]; + if ($size == 0) { + return fopen('php://temp', 'w+b'); + } + $stream = fopen('php://temp', 'w+b'); + //TODO stream slice needed here + while ($size) { + $data = $this->stream_get_contents(min(1024, $size), $offset); + fwrite($stream, $data); + $size -= strlen($data); + $offset += strlen($data); + } + rewind($stream); + return $stream; + } + abstract public function extractAllTo(string $path); abstract public function addFile(string $path, string $archivePath): bool; @@ -107,7 +127,7 @@ public function getEntriesMap(): Map { private int $pos = 0; private ?resource $stream; - private ?string $path; + protected ?string $path; protected function open(string $path) { $this->path = $path; diff --git a/hphp/system/php/archive_handler/PharArchiveHandler.ns.php b/hphp/system/php/archive_handler/PharArchiveHandler.ns.php index 87b693ef63bd7b..a5ffcc665be45b 100644 --- a/hphp/system/php/archive_handler/PharArchiveHandler.ns.php +++ b/hphp/system/php/archive_handler/PharArchiveHandler.ns.php @@ -76,7 +76,7 @@ private function parsePhar(&$pos) { ); } foreach ($this->fileInfo as $filename => $info) { - $this->fileOffsets[$filename] = array($pos, $info[2]); + $this->fileOffsets[$filename] = [$pos, $info[2]]; $pos += $info[2]; $this->entries[$filename] = new ArchiveEntryStat( $info[3], @@ -198,26 +198,6 @@ private function substr(&$pos, int $len) { return $ret; } - public function getStream(string $path): resource { - if (!isset($this->fileOffsets[$path])) { - throw new PharException("No $path in phar"); - } - list($offset, $size) = $this->fileOffsets[$path]; - if ($size == 0) { - return fopen('php://temp', 'w+b'); - } - $stream = fopen('php://temp', 'w+b'); - //TODO stream slice needed here - while ($size) { - $data = $this->stream_get_contents(min(1024, $size), $offset); - fwrite($stream, $data); - $size -= strlen($data); - $offset += strlen($data); - } - rewind($stream); - return $stream; - } - public function extractAllTo(string $path) { //TODO } diff --git a/hphp/system/php/archive_handler/TarArchiveHandler.ns.php b/hphp/system/php/archive_handler/TarArchiveHandler.ns.php index e0b7d8ae6380e5..01c980bf7a02b3 100644 --- a/hphp/system/php/archive_handler/TarArchiveHandler.ns.php +++ b/hphp/system/php/archive_handler/TarArchiveHandler.ns.php @@ -8,7 +8,6 @@ final class TarArchiveHandler extends ArchiveHandler { private Map $contents = Map { }; private Map $symlinks = Map { }; - private string $path = ''; private ?resource $fp; public function __construct( @@ -16,9 +15,9 @@ public function __construct( bool $preventHaltTokenCheck = true ) { $this->path = $path; - if (file_exists($path)) { - $this->readTar(); + $this->open($path); + $this->parseTar(); if ( !$preventHaltTokenCheck && strpos($this->stub, Phar::HALT_TOKEN) === false @@ -30,31 +29,18 @@ public function __construct( } } - private function readTar() { + private function parseTar() { /* If you have GNU Tar installed, you should be able to find * the file format documentation (including header byte offsets) at: * - /usr/include/tar.h * - the tar info page (Top/Tar Internals/Standard) */ - $path = $this->path; - $fp = fopen($path, 'rb'); - $data = fread($fp, 2); - fclose($fp); - - if ($data === 'BZ') { - $this->compressed = Phar::BZ2; - $fp = bzopen($path, 'r'); - } else if ($data === "\x1F\x8B") { - $this->compressed = Phar::GZ; - $fp = gzopen($path, 'rb'); - } else { - $fp = fopen($path, 'rb'); - } - + $pos = 0; $next_file_name = null; - while (!feof($fp)) { - $header = fread($fp, 512); + while (!$this->eof()) { + $header = $this->stream_get_contents(512); + $pos += 512; // skip empty blocks if (!trim($header)) { continue; @@ -66,41 +52,33 @@ private function readTar() { $next_file_name = null; } - $len = octdec(substr($header, 124, 12)); + $size = octdec(substr($header, 124, 12)); $timestamp = octdec(trim(substr($header, 136, 12))); $type = $header[156]; - $data = ''; - $read_len = 0; - while ($read_len != $len) { - $read_data = fread($fp, $len - $read_len); - $data .= $read_data; - $read_len += strlen($read_data); - } - // Hidden .phar directory should not appear in files listing if (strpos($filename, '.phar') === 0) { if ($filename == '.phar/stub.php') { - $this->stub = $data; + $this->stub = $this->stream_get_contents($size); } else if ($filename == '.phar/alias.txt') { - $this->alias = $data; + $this->alias = $this->stream_get_contents($size); } } else if (substr($filename, -1) !== '/') { $this->entries[$filename] = new ArchiveEntryStat( /* crc = */ null, - $len, - /* compressed size = */ $len, + $size, + /* compressed size = */ $size, $timestamp ); switch ($type) { case 'L': - $next_file_name = trim($data); + $next_file_name = trim($this->stream_get_contents($size)); break; case '0': case "\0": - $this->contents[$filename] = $data; + $this->fileOffsets[$filename] = [$pos, $size]; break; case '2': @@ -117,27 +95,19 @@ private function readTar() { throw new Exception("type $type is not implemented yet"); } } - - if ($len % 512 !== 0) { - $leftover = 512 - ($len % 512); - $zeros = fread($fp, $leftover); + $pos += $size; + $this->seek($pos); + if ($size % 512 !== 0) { + $leftover = 512 - ($size % 512); + $zeros = $this->stream_get_contents($leftover); if (strlen(trim($zeros)) != 0) { throw new Exception("Malformed tar. Padding isn't zeros. $zeros"); } + $pos += $leftover; } } } - public function getStream(string $path): resource { - if (!$this->contents->contains($path)) { - throw new PharException("No $path in phar"); - } - $stream = fopen('php://temp', 'w+b');//TODO stream slice needed here - fwrite($stream, $this->contents[$path]); - rewind($stream); - return $stream; - } - private function createFullPath( string $root, string $partial_path, @@ -151,8 +121,15 @@ private function createFullPath( } public function extractAllTo(string $root) { - foreach ($this->contents as $path => $data) { - file_put_contents($this->createFullPath($root, $path), $data); + foreach ($this->fileOffsets as $path => list($offset, $size)) { + $fp = fopen($this->createFullPath($root, $path), 'wb'); + while ($size) { + $data = $this->stream_get_contents(min(1024, $size), $offset); + fwrite($fp, $data); + $size -= strlen($data); + $offset += strlen($data); + } + fclose($fp); } // Intentional difference to PHP5: PHP5 just creates an empty diff --git a/hphp/test/slow/ext_phar/phar_data_extract.php b/hphp/test/slow/ext_phar/phar_data_extract.php index 4c76578c95c013..890d1eaea74cb9 100644 --- a/hphp/test/slow/ext_phar/phar_data_extract.php +++ b/hphp/test/slow/ext_phar/phar_data_extract.php @@ -29,7 +29,7 @@ function getFileCount($path) { return $size; } -$directory = dirname(__FILE__)."/tgz"; +$directory = __DIR__."/tgz"; $iterator = new DirectoryIterator($directory); $files = array(); foreach ($iterator as $fileinfo) { From cc43cb0536f0756521f0d6f8a3060cc6cf41087b Mon Sep 17 00:00:00 2001 From: Nazar Mokrynskyi Date: Tue, 3 May 2016 02:30:29 +0300 Subject: [PATCH 20/22] Throw exceptions for functionality that is not implemented instead of ignoring it silently. Alias correctness check moved into its own method. Restored accidentally removed method description. --- .../php/archive_handler/ArchiveHandler.ns.php | 6 ++-- .../archive_handler/PharArchiveHandler.ns.php | 4 +-- hphp/system/php/phar/Phar.php | 32 ++++++++++--------- hphp/system/php/phar/PharFileInfo.php | 3 ++ 4 files changed, 25 insertions(+), 20 deletions(-) diff --git a/hphp/system/php/archive_handler/ArchiveHandler.ns.php b/hphp/system/php/archive_handler/ArchiveHandler.ns.php index 61b0f784a1de03..148ca8b547aaa4 100644 --- a/hphp/system/php/archive_handler/ArchiveHandler.ns.php +++ b/hphp/system/php/archive_handler/ArchiveHandler.ns.php @@ -61,8 +61,8 @@ public function getAlias(): ?string { return $this->alias; } - public function setAlias(string $alias) { - //TODO + public function setAlias(string $alias, int $len) { + throw new Exception('Not implemented yet'); } public function getStub(): ?string { @@ -70,7 +70,7 @@ public function getStub(): ?string { } public function setStub(string $stub, int $len = -1) { - //TODO + throw new Exception('Not implemented yet'); } public function count(): int { diff --git a/hphp/system/php/archive_handler/PharArchiveHandler.ns.php b/hphp/system/php/archive_handler/PharArchiveHandler.ns.php index a5ffcc665be45b..3d9babf16cecab 100644 --- a/hphp/system/php/archive_handler/PharArchiveHandler.ns.php +++ b/hphp/system/php/archive_handler/PharArchiveHandler.ns.php @@ -199,11 +199,11 @@ private function substr(&$pos, int $len) { } public function extractAllTo(string $path) { - //TODO + throw new Exception('Not implemented yet'); } public function addFile(string $path, string $archive_path): bool { - //TODO + throw new Exception('Not implemented yet'); } } } diff --git a/hphp/system/php/phar/Phar.php b/hphp/system/php/phar/Phar.php index b685bebe37a6c2..8c974b6714e6d9 100644 --- a/hphp/system/php/phar/Phar.php +++ b/hphp/system/php/phar/Phar.php @@ -123,11 +123,13 @@ public function __construct($fname, $flags = null, $alias = null) { ); } } - // From the manifest - if ($this->getAlias()) { - self::$aliases[$this->getAlias()] = $this; + $aliasFromManifest = $this->getAlias(); + if ($aliasFromManifest) { + $this->assertCorrectAlias($aliasFromManifest); + self::$aliases[$aliasFromManifest] = $this; } if ($alias) { + $this->assertCorrectAlias($alias); self::$aliases[$alias] = $this; } // We also do filename lookups @@ -665,12 +667,7 @@ public function offsetUnset($offset) { * @return bool */ public function setAlias($alias) { - - if (preg_match('#[\\/:;]#', $alias)) { - throw new UnexpectedValueException( - "Invalid alias \"$alias\" specified for phar \"$this->fname\"" - ); - } + $this->assertCorrectAlias($alias); if (isset(self::$aliases[$alias])) { $path = self::$aliases[$alias]->fname; throw new PharException( @@ -678,12 +675,17 @@ public function setAlias($alias) { ' be used for other archives' ); } - // Do not execute following when called from constructor - if (!debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS)[0]['object'] === $this) { - // TODO: Remove following line when write support implemented - throw new UnexpectedValueException('phar is read-only'); - self::assertWriteSupport(); - $this->archiveHandler->setAlias($alias); + // TODO: Remove following line when write support implemented + throw new UnexpectedValueException('phar is read-only'); + self::assertWriteSupport(); + $this->archiveHandler->setAlias($alias); + } + + private function assertCorrectAlias(string $alias) { + if (preg_match('#[\\/:;]#', $alias)) { + throw new UnexpectedValueException( + "Invalid alias \"$alias\" specified for phar \"$this->fname\"" + ); } } diff --git a/hphp/system/php/phar/PharFileInfo.php b/hphp/system/php/phar/PharFileInfo.php index 7987ba34d5ee3b..8b542e870b5a10 100644 --- a/hphp/system/php/phar/PharFileInfo.php +++ b/hphp/system/php/phar/PharFileInfo.php @@ -15,6 +15,9 @@ class PharFileInfo extends SplFileInfo { /** * ( excerpt from http://php.net/manual/en/pharfileinfo.construct.php ) * + * This should not be called directly. Instead, a PharFileInfo object is + * initialized by calling Phar::offsetGet() through array access. + * * @param string $entry The full url to retrieve a file. If you wish to * retrieve the information for the file my/file.php * from the phar boo.phar, the entry should be From ef3c0beb831dd5551b9d5b8e24de78ebc5d88537 Mon Sep 17 00:00:00 2001 From: Nazar Mokrynskyi Date: Wed, 4 May 2016 21:41:32 +0300 Subject: [PATCH 21/22] Relax strict typing to allow tests to pass in repo-auth mode --- hphp/system/php/archive_handler/ArchiveHandler.ns.php | 4 ++-- hphp/system/php/archive_handler/PharArchiveHandler.ns.php | 4 ++-- hphp/system/php/archive_handler/TarArchiveHandler.ns.php | 2 +- hphp/system/php/phar/PharFileInfo.php | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/hphp/system/php/archive_handler/ArchiveHandler.ns.php b/hphp/system/php/archive_handler/ArchiveHandler.ns.php index 148ca8b547aaa4..8cc5d8a786c934 100644 --- a/hphp/system/php/archive_handler/ArchiveHandler.ns.php +++ b/hphp/system/php/archive_handler/ArchiveHandler.ns.php @@ -125,8 +125,8 @@ public function getEntriesMap(): Map { // Things used by Phar and Tar to overcome Bzip2 extension's limitation to // seek in other way than SEEK_CUR, we need to maintain position manually:( - private int $pos = 0; - private ?resource $stream; + private $pos = 0; + private $stream; protected ?string $path; protected function open(string $path) { diff --git a/hphp/system/php/archive_handler/PharArchiveHandler.ns.php b/hphp/system/php/archive_handler/PharArchiveHandler.ns.php index 3d9babf16cecab..3238cf30ea51ad 100644 --- a/hphp/system/php/archive_handler/PharArchiveHandler.ns.php +++ b/hphp/system/php/archive_handler/PharArchiveHandler.ns.php @@ -6,7 +6,7 @@ final class PharArchiveHandler extends ArchiveHandler { private array $fileInfo = array(); - private int $archiveFlags; + private $archiveFlags; public function __construct( string $path, bool $preventHaltTokenCheck = true @@ -177,7 +177,7 @@ private function computeSignature( return hash_final($context, true); } - private function bytesToInt(&$pos, int $len) { + private function bytesToInt(&$pos, int $len): int { $str = $this->stream_get_contents($len, $pos); if (strlen($str) < $len) { throw new PharException( diff --git a/hphp/system/php/archive_handler/TarArchiveHandler.ns.php b/hphp/system/php/archive_handler/TarArchiveHandler.ns.php index 01c980bf7a02b3..8d051aceb4ba45 100644 --- a/hphp/system/php/archive_handler/TarArchiveHandler.ns.php +++ b/hphp/system/php/archive_handler/TarArchiveHandler.ns.php @@ -8,7 +8,7 @@ final class TarArchiveHandler extends ArchiveHandler { private Map $contents = Map { }; private Map $symlinks = Map { }; - private ?resource $fp; + private $fp; public function __construct( string $path, diff --git a/hphp/system/php/phar/PharFileInfo.php b/hphp/system/php/phar/PharFileInfo.php index 8b542e870b5a10..86719be4e69a78 100644 --- a/hphp/system/php/phar/PharFileInfo.php +++ b/hphp/system/php/phar/PharFileInfo.php @@ -9,7 +9,7 @@ */ class PharFileInfo extends SplFileInfo { - private ?string $name; + private $name; private ?__SystemLib\ArchiveEntryStat $stat; /** From ec47aa91867e716a3552aec9303889b9c89d96b7 Mon Sep 17 00:00:00 2001 From: Nazar Mokrynskyi Date: Fri, 6 May 2016 23:46:26 +0300 Subject: [PATCH 22/22] Trivial fix for test output --- hphp/test/slow/debugger/print-systemlib-class.php.expectf | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hphp/test/slow/debugger/print-systemlib-class.php.expectf b/hphp/test/slow/debugger/print-systemlib-class.php.expectf index f1af41ebaec3c6..873410f6506ebc 100644 --- a/hphp/test/slow/debugger/print-systemlib-class.php.expectf +++ b/hphp/test/slow/debugger/print-systemlib-class.php.expectf @@ -7,9 +7,9 @@ stdClass Object =new __SystemLib\TarArchiveHandler('/dev/null'); __SystemLib\TarArchiveHandler Object ( - [entries":"__SystemLib\TarArchiveHandler":private] => HH\Map Object + [contents":"__SystemLib\TarArchiveHandler":private] => HH\Map Object ( ) - [contents":"__SystemLib\TarArchiveHandler":private] => HH\Map Ob -There are more characters. Continue? [y/N]quit \ No newline at end of file + [symlinks":"__SystemLib\TarArchiveHandler":private] => HH\Map O +There are more characters. Continue? [y/N]quit