Skip to content

Instantly share code, notes, and snippets.

@SOF3
Created December 12, 2021 13:16
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save SOF3/f106a93ee6884d9c92dc93db221db596 to your computer and use it in GitHub Desktop.
Save SOF3/f106a93ee6884d9c92dc93db221db596 to your computer and use it in GitHub Desktop.
My personal phar tool
#!/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