|
#!/usr/bin/env php |
|
<?php |
|
declare(strict_types=1); |
|
|
|
const USAGE = /** @lang text */ |
|
<<<USAGE |
|
Usage: phar <action> <phar file> ... |
|
- stub : phar s <phar> [value] |
|
- metadata : phar m <phar> [format: DUMP|Json|Yaml|Serialized|Export|Print] |
|
- list : phar l <phar> [subdir] |
|
- recursive list : phar r <phar> [subdir] |
|
- edit with vim : phar v <phar> <subpath> |
|
- extract : phar x <phar> <subpath> [target] |
|
- delete : phar d <phar> <subpath> (recursive) |
|
- add : phar a <phar> <files ...> (recursive) |
|
|
|
USAGE; |
|
|
|
if(!isset($argv[2])){ |
|
echo USAGE; |
|
exit(2); |
|
} |
|
|
|
$action = strtolower($argv[1]); |
|
$pharFile = $argv[2]; |
|
$pharUrl = "phar://" . str_replace("\\", "/", realpath($pharFile)) . "/"; |
|
|
|
switch($action{0}){ |
|
case "s": |
|
resolvePhar(); |
|
echo "Phar stub:\n" . $phar->getStub() . "\n"; |
|
if(isset($argv[3])){ |
|
echo "Changing stub to:\n" . $argv[3] . "\n"; |
|
$phar->setStub($argv[3]); |
|
} |
|
exit(0); |
|
|
|
case "m": |
|
resolvePhar(); |
|
echo "Phar metadata are as follows: "; |
|
switch(strtolower($argv[3] ?? "d"){0}){ |
|
case "d": |
|
var_dump($phar->getMetadata()); |
|
exit(0); |
|
case "j": |
|
echo json_encode($phar->getMetadata(), JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n"; |
|
exit(0); |
|
case "y": |
|
echo yaml_emit($phar->getMetadata()); |
|
exit(0); |
|
case "s": |
|
echo "\n" . serialize($phar->getMetadata()) . "\n"; |
|
exit(0); |
|
case "e": |
|
echo "\n"; |
|
var_export($phar->getMetadata()); |
|
echo "\n"; |
|
exit(0); |
|
case "p": |
|
echo "\n"; |
|
print_r($phar->getMetadata()); |
|
echo "\n"; |
|
exit(0); |
|
} |
|
echo "Unknown format \"{$argv[3]{0}}\"\n"; |
|
exit(2); |
|
|
|
case "l": |
|
resolvePhar(); |
|
$files = []; |
|
foreach(new DirectoryIterator($pharUrl . cleanPath($argv[3] ?? "")) as $file){ |
|
$files[] = clone $file; |
|
} |
|
listFiles($files); |
|
exit(0); |
|
case "r": |
|
resolvePhar(); |
|
$files = []; |
|
foreach(new RecursiveIteratorIterator(new RecursiveDirectoryIterator( |
|
$pharUrl . cleanPath($argv[3] ?? ""))) as $file){ |
|
$files[] = clone $file; |
|
} |
|
listFiles($files); |
|
exit(0); |
|
|
|
case "v": |
|
resolvePhar(); |
|
if(!isset($argv[3])){ |
|
echo USAGE; |
|
exit(2); |
|
} |
|
$path = $argv[3]; |
|
if(!isset($phar[$path])){ |
|
echo "No file called $path in $pharFile\n"; |
|
exit(2); |
|
} |
|
if($phar[$path]->getType() !== "file"){ |
|
echo "$path is not a file\n"; |
|
} |
|
$tempFile = tempnam(sys_get_temp_dir(), "phv"); |
|
copy($phar[$path]->getPathname(), $tempFile); |
|
exec("mintty vi -n " . escapeshellarg($tempFile)); |
|
copy($tempFile, $phar[$path]->getPathname()); |
|
unlink($tempFile); |
|
exit(0); |
|
|
|
case "x": |
|
resolvePhar(); |
|
if(!isset($argv[3])){ |
|
echo USAGE; |
|
exit(2); |
|
} |
|
$path = $argv[3]; |
|
if(!isset($phar[$path])){ |
|
echo "$path is not a file or directory in $pharFile\n"; |
|
exit(1); |
|
} |
|
if($phar[$path]->getType() === "dir"){ |
|
if(!isset($argv[4])){ |
|
echo "Cannot output a directory to stdout\n"; |
|
echo USAGE; |
|
exit(2); |
|
} |
|
// extract directory |
|
$from = $pharUrl . cleanPath($path); |
|
$target = cleanPath($argv[4]); |
|
if(is_dir($target)){ |
|
echo "[WARNING] " . realpath($target) . " already exists\n"; |
|
} |
|
foreach(new RecursiveIteratorIterator(new RecursiveDirectoryIterator($from)) as $file){ |
|
$incl = substr($file->getPathname(), strlen($from)); |
|
if(!@mkdir(dirname($target . $incl), 0777, true) && !is_dir(dirname($target . $incl))){ |
|
throw new RuntimeException("Failed to create directory {$target}{$incl}"); |
|
} |
|
copy($file->getPathname(), $target . $incl); |
|
} |
|
exit(0); |
|
} |
|
|
|
if(!isset($argv[4])){ |
|
readfile($phar[$path]->getPathname()); |
|
exit(0); |
|
} |
|
copy($phar[$path]->getPathname(), $argv[4]); |
|
exit(0); |
|
|
|
case "d": |
|
if(!isset($argv[3])){ |
|
echo USAGE; |
|
exit(2); |
|
} |
|
$subdir = cleanPath($argv[3]); |
|
resolvePhar(); |
|
$phar->startBuffering(); |
|
foreach(new RecursiveIteratorIterator($phar) as $file => $v){ |
|
$file = str_replace("\\", "/", $file); |
|
if(strpos($file, $pharUrl . $subdir) === 0 || $file === $pharUrl . substr($subdir, 0, -1)){ |
|
$phar->delete(substr($file, strlen($pharUrl))); |
|
} |
|
} |
|
$phar->stopBuffering(); |
|
exit(0); |
|
|
|
case "a": |
|
if(!isset($argv[3])){ |
|
echo USAGE; |
|
exit(2); |
|
} |
|
$phar = new Phar($pharFile); |
|
$phar->startBuffering(); |
|
for($i = 3; $i < $argc; ++$i){ |
|
$arg = $argv[$i]; |
|
if(strpos($arg, "=") !== false){ |
|
list($from, $to) = explode("=", $arg, 2); |
|
}else{ |
|
$from = $to = $arg; |
|
} |
|
if(!file_exists($from)){ |
|
echo "No such file or directory: $from\n"; |
|
exit(1); |
|
} |
|
if(is_file($from)){ |
|
$phar->addFile($from, $to); |
|
}else{ |
|
assert(is_dir($from)); |
|
$from = cleanPath($from); |
|
$to = cleanPath($to); |
|
/** @var SplFileInfo $file */ |
|
foreach(new RecursiveIteratorIterator(new RecursiveDirectoryIterator($from)) as $file){ |
|
if(!$file->isFile()){ |
|
continue; |
|
} |
|
$phar->addFile($file->getPathname(), $to . substr($file->getPathname(), strlen($from))); |
|
} |
|
} |
|
} |
|
$phar->stopBuffering(); |
|
|
|
default: |
|
echo USAGE; |
|
exit(2); |
|
} |
|
|
|
function resolvePhar(){ |
|
global $pharFile, $phar; |
|
if(!is_file($pharFile)){ |
|
echo "Phar file does not exist: $pharFile\n"; |
|
exit(1); |
|
} |
|
$phar = new Phar($pharFile); |
|
} |
|
|
|
/** |
|
* @param DirectoryIterator[] $list |
|
*/ |
|
function listFiles(array $list){ |
|
global $pharUrl; |
|
/** @var string[][] $files */ |
|
$files = [ |
|
["Name", "Size/KB", "Type", "Created", "Modified", ""], |
|
]; |
|
foreach($list as $file){ |
|
$files[] = [ |
|
substr($file->getPathname(), strlen($pharUrl)), |
|
sprintf("%.2f", $file->getSize() / 1024), |
|
$file->getType(), |
|
date("M d, H:i:s", $file->getCTime()), |
|
date("M d, H:i:s", $file->getMTime()), |
|
]; |
|
} |
|
usort($files, function($a, $b){ |
|
if(isset($a[5])){ |
|
return -1; |
|
} |
|
if(isset($b[5])){ |
|
return 1; |
|
} |
|
return $a[0] <=> $b[0]; |
|
}); |
|
$maxLengths = [0, 0, 0, 0, 0]; |
|
foreach($files as $file){ |
|
for($i = 0; $i < 5; ++$i){ |
|
if($maxLengths[$i] < strlen($file[$i])){ |
|
$maxLengths[$i] = strlen($file[$i]); |
|
} |
|
} |
|
} |
|
$hr = str_repeat("-", array_sum($maxLengths) + count($maxLengths) * 3 + 1); |
|
foreach($files as $j => $file){ |
|
if($j < 2){ |
|
echo $hr . "\n"; |
|
} |
|
for($i = 0; $i < 5; ++$i){ |
|
echo "| " . str_pad($file[$i], $maxLengths[$i], " ", $i === 1 ? STR_PAD_LEFT : STR_PAD_RIGHT) . " "; |
|
} |
|
echo "|\n"; |
|
} |
|
echo $hr; |
|
} |
|
|
|
function cleanPath(string $path) : string{ |
|
$trimmed = trim(str_replace("\\", "/", $path), "/"); |
|
return $trimmed === "" ? "" : "$trimmed/"; |
|
} |