Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for compressed phar #7028

Closed
wants to merge 22 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
cc4b1d5
Added file extension check for Phar opening.
nazar-pc Apr 22, 2016
f02ad85
Implemented `Phar::isValidPharFilename()` method (PHP >= 5.3.0, PECL …
nazar-pc Apr 22, 2016
3c87b46
Preparation for tar/zip-based Phar support:
nazar-pc Apr 22, 2016
2b6b5fa
Some unification of `Phar` and `PharData` classes
nazar-pc Apr 22, 2016
7d9993e
Do not store file pointer inside `Phar` instance, no need for that
nazar-pc Apr 22, 2016
4e19975
Small decoupling of common code into generic constructor
nazar-pc Apr 22, 2016
19fca77
`Phar` and `PharData` unified further - both use the same `offset*` m…
nazar-pc Apr 22, 2016
c9731ff
Tar/Zip formats should be supported now in `Phar` class (needs to be …
nazar-pc Apr 22, 2016
7adb1bf
Unnecessary method removed
nazar-pc Apr 22, 2016
f25f293
Take a resource.
Orvid Apr 22, 2016
645e61b
Switch to streams when interfacing with runtime, though only Zip work…
nazar-pc Apr 23, 2016
32be78f
Completely stream-based Phar implementation
nazar-pc Apr 23, 2016
1f3225f
Added support for compressed Phar archives (`.phar.gz` and `.phar.bz2…
nazar-pc Apr 23, 2016
9e4eff5
Small fixes, tested and confirmed working support for:
nazar-pc Apr 23, 2016
e239e97
Small fixes, more tests for compression and signatures
nazar-pc Apr 23, 2016
fbd06a3
Ported original PHP test for `Phar::isValidPharFilename()` method, me…
nazar-pc Apr 24, 2016
b44295d
Redundant test removed, `is_valid_phar_filename` test already covers …
nazar-pc Apr 29, 2016
9727e07
Better memory efficiency when getting file contents from Phar-based a…
nazar-pc Apr 29, 2016
ad5fb4f
Better memory efficiency when parsing and getting file contents from …
nazar-pc Apr 29, 2016
cc43cb0
Throw exceptions for functionality that is not implemented instead of…
nazar-pc May 2, 2016
ef3c0be
Relax strict typing to allow tests to pass in repo-auth mode
nazar-pc May 4, 2016
ec47aa9
Trivial fix for test output
nazar-pc May 6, 2016
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions hphp/runtime/ext/phar/ext_phar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -59,9 +58,11 @@ static struct PharStreamWrapper : Stream::Wrapper {
nullptr,
SystemLib::s_PharClass
);
String contents = ret.toString();

return req::make<MemFile>(contents.data(), contents.size());
if (!ret.isResource()) {
return nullptr;
}
return dyn_cast_or_null<File>(ret.asResRef());
}

virtual int access(const String& path, int mode) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No way!!!that's so cool

Expand Down
1 change: 1 addition & 0 deletions hphp/system/php.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
174 changes: 166 additions & 8 deletions hphp/system/php/archive_handler/ArchiveHandler.ns.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
<?php

namespace __SystemLib {
use Phar;

class ArchiveEntryStat {
public function __construct(
public ?int $crc,
Expand All @@ -12,18 +14,174 @@ public function __construct(
}

abstract class ArchiveHandler {
abstract public function getContentsList(): Map<string, ArchiveEntryStat>;
abstract public function read(string $path): ?string;
protected Map<string, ArchiveEntryData> $entries = Map { };
protected ?string $alias;
protected ?string $stub;
protected array<string, array<int, int>> $fileOffsets = [];
protected string $apiVersion = '1.0.0';
protected $metadata;
protected ?string $signature;
protected int $signatureType = Phar::NONE;
protected $compressed = false;

// 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;
abstract public function close(): void;

public function stat($path): ?ArchiveEntryStat {
$contents = $this->getContentsList();
if ($contents->contains($path)) {
return $contents[$path];
abstract public function __construct(
string $path,
bool $preventHaltTokenCheck = true
);

public function stat(string $path): ?ArchiveEntryStat {
return $this->getEntriesMap()->get($path);
}

public function getAlias(): ?string {
return $this->alias;
}

public function setAlias(string $alias, int $len) {
throw new Exception('Not implemented yet');
}

public function getStub(): ?string {
return $this->stub;
}

public function setStub(string $stub, int $len = -1) {
throw new Exception('Not implemented yet');
}

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(): ?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;
}

// Custom methods used by Phar class internally

public function getEntriesMap(): Map<string, ArchiveEntryStat> {
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 $pos = 0;
private $stream;
protected ?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');
$this->compressed = Phar::GZ;
} else {
$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;
}
return null;
}
}
}
Loading