Created
December 12, 2021 13:16
-
-
Save SOF3/f106a93ee6884d9c92dc93db221db596 to your computer and use it in GitHub Desktop.
My personal phar tool
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