Created
July 8, 2018 15:06
-
-
Save SOF3/070baf254dc579bd26e2eacf885fcf7c to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env php | |
<?php | |
//declare(strict_types=1); | |
const USAGE = /** @lang text */ | |
<<<USAGE | |
Usage: phar <action> <phar file> ... | |
- stub : phar s <phar> [value] | |
set from file: sf <phar> <stub file> | |
- metadata : phar m <phar> [format: DUMP|Json|Yaml|Serialized|Export|Print] | |
- list : phar l <phar> [subdir] | |
- recursive list : phar r <phar> [subdir] | |
sort size desc : rs <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($action === "sf" ? (file_get_contents($argv[3]) . " __HALT_COMPILER();") : $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, $action === "ls" ? 1 : 0, $action === "ls" ? -1 : 1); | |
exit(0); | |
case "r": | |
resolvePhar(); | |
$files = []; | |
foreach(new RecursiveIteratorIterator(new RecursiveDirectoryIterator( | |
$pharUrl . cleanPath($argv[3] ?? ""))) as $file){ | |
$files[] = clone $file; | |
} | |
listFiles($files, $action === "rs" ? 1 : 0, $action === "rs" ? -1 : 1); | |
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 = sys_get_temp_dir() . "/phar.vim." . rand(1e7, 1e8 - 1) . "." . $phar[$path]->getExtension(); | |
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($path !== "/" && $path !== "" && !isset($phar[$path])){ | |
echo "$path is not a file or directory in $pharFile\n"; | |
exit(1); | |
} | |
if($path === "/" || $path === "" || $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}"); | |
} | |
file_put_contents($target . $incl, file_get_contents($file->getPathname())); | |
} | |
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)){ | |
echo "[*] Adding file $file\n"; | |
$phar->addFile($from, $to); | |
}else{ | |
assert(is_dir($from)); | |
echo "[*] Adding directory $from\n"; | |
$from = cleanPath($from); | |
$to = cleanPath($to); | |
/** @var SplFileInfo $file */ | |
foreach(new RecursiveIteratorIterator(new RecursiveDirectoryIterator($from)) as $file){ | |
if(!$file->isFile()){ | |
continue; | |
} | |
echo "[*] Adding file $file\n"; | |
$phar->addFile($file->getPathname(), $to . substr($file->getPathname(), strlen($from))); | |
} | |
} | |
} | |
$phar->stopBuffering(); | |
echo "[*] Phar created in $pharFile\n"; | |
$gz = gzopen($pharFile . ".gz", "wb"); | |
$pf = fopen($pharFile, "rb"); | |
stream_copy_to_stream($pf, $gz); | |
gzclose($gz); | |
fclose($pf); | |
echo "[*] Phar.gz created in $pharFile.gz\n"; | |
exit(0); | |
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); | |
} | |
function listFiles(array $list, int $sortBy, int $sign = 1){ | |
global $pharUrl; | |
/** @var string[][] $files */ | |
$files = [ | |
["Name", "Size/KB", "Lines", "Created", "Modified", ""], | |
]; | |
$count = 0; | |
$sizeTotal = 0; | |
$linesTotal = 0; | |
foreach($list as $file){ | |
$files[] = [ | |
str_replace("\\", "/", substr($file->getPathname(), strlen($pharUrl))), | |
sprintf("%.2f", $file->getSize() / 1024), | |
$fileLines = $file->getType() === "file" ? count(file($file->getPathname())) : NAN, | |
date("M d, H:i:s", $file->getCTime()), | |
date("M d, H:i:s", $file->getMTime()), | |
]; | |
++$count; | |
$sizeTotal += $file->getSize(); | |
$linesTotal += $fileLines; | |
} | |
usort($files, function($a, $b) use($sortBy, $sign){ | |
if(isset($a[5])){ | |
return -1; | |
} | |
if(isset($b[5])){ | |
return 1; | |
} | |
return ($a[$sortBy] <=> $b[$sortBy]) * $sign; | |
}); | |
$files[] = ["Totally $count files", sprintf("%.2f", $sizeTotal / 1024), $linesTotal, "", ""]; | |
$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], " ", is_numeric($file[$i]) ? STR_PAD_LEFT : STR_PAD_RIGHT) . " "; | |
} | |
echo "|\n"; | |
} | |
echo $hr; | |
} | |
function cleanPath(string $path) : string{ | |
$trimmed = trim(str_replace("\\", "/", $path), "/"); | |
return $trimmed === "" ? "" : "$trimmed/"; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment