Skip to content

Instantly share code, notes, and snippets.

@SOF3
Last active August 26, 2017 16:23
Show Gist options
  • Save SOF3/320c53db774f6ca5e299290a48047e17 to your computer and use it in GitHub Desktop.
Save SOF3/320c53db774f6ca5e299290a48047e17 to your computer and use it in GitHub Desktop.

This is a simple CLI tool for managing phar files easily.

This tool is intended for Windows users using Linux-like shells.

Required executables/commands in the %PATH%:

  • mintty for starting a window that hosts the vim editor
  • vi for editing a temp file from phar v

If php is in the %PATH%, you may put this file in a directory in the %PATH% with or without a file extension, then execute this directly from the shell. For example, I save this as phar in one of the directories in my path variable, so I just execute phar l path/to/file.phar directly on Git Bash.

The phar a command has not been tested yet.

Usage:

- 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)
#!/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/";
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment