├── .github └── workflows │ └── ci.yml ├── .gitignore ├── LICENSE ├── README.md ├── composer.json ├── docs └── README.md ├── pharaoh ├── psalm.xml └── src ├── Pharaoh ├── PharDiff.php ├── PharError.php └── Pharaoh.php └── pharaoh.php /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: [push] 4 | 5 | jobs: 6 | tests: 7 | name: PHP ${{ matrix.php-versions }} Test on ${{ matrix.operating-system }} 8 | runs-on: ${{ matrix.operating-system }} 9 | strategy: 10 | matrix: 11 | operating-system: ['ubuntu-latest'] 12 | php-versions: ['7.1', '7.2', '7.3', '7.4', '8.0', '8.1', '8.2', '8.3'] 13 | phpunit-versions: ['latest'] 14 | steps: 15 | - name: Checkout 16 | uses: actions/checkout@v4 17 | 18 | - name: Setup PHP 19 | uses: shivammathur/setup-php@v2 20 | with: 21 | php-version: ${{ matrix.php-versions }} 22 | extensions: mbstring, intl, sodium 23 | ini-values: post_max_size=256M, max_execution_time=180 24 | tools: psalm, phpunit:${{ matrix.phpunit-versions }} 25 | 26 | - name: Install dependencies 27 | run: composer install 28 | 29 | - name: Modernize dependencies 30 | run: composer require --dev "phpunit/phpunit:>=4" 31 | 32 | - name: Install Psalm 33 | run: composer require --dev vimeo/psalm:^4 34 | 35 | - name: Static Analysis 36 | run: vendor/bin/psalm 37 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea 2 | /composer.lock 3 | /vendor 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 - 2018 Paragon Initiative Enterprises 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Pharaoh - PHAR diff utility 2 | 3 | > **Notice**: [Box](https://github.com/humbug/box) has a diff utility that does what Pharaoh set out to do, and is more 4 | > widely supported and adopted. We are deprecating Pharaoh in favor of `box diff`. 5 | 6 | [![Build Status](https://github.com/paragonie/pharaoh/actions/workflows/ci.yml/badge.svg)](https://github.com/paragonie/pharaoh/actions) 7 | [![Latest Stable Version](https://poser.pugx.org/paragonie/pharaoh/v/stable)](https://packagist.org/packages/paragonie/pharaoh) 8 | [![Latest Unstable Version](https://poser.pugx.org/paragonie/pharaoh/v/unstable)](https://packagist.org/packages/paragonie/pharaoh) 9 | [![License](https://poser.pugx.org/paragonie/pharaoh/license)](https://packagist.org/packages/paragonie/pharaoh) 10 | 11 | Display the differences between all of the files in two PHP Archives. 12 | 13 | In all likelihood, two PHP Archives will not be byte-for-byte compatible, 14 | so `sha256sum` will not yield the same result, even if they were both built 15 | from the same source code and contain the same contents. This is because 16 | Composer randomizes e.g. their autoloader class names. 17 | 18 | Copyright (c) 2015 - 2018 [Paragon Initiative Enterprises](https://paragonie.com). 19 | Check out our other [open source projects](https://paragonie.com/projects) too. 20 | 21 | ## Example 22 | 23 | To verify two PHP Archives were built from the same source code, first download 24 | the official distribution and then build a Phar from source. 25 | 26 | ### Basic Usage 27 | 28 | To see what differs between the two files, run this command: 29 | 30 | ```sh 31 | /path/to/pharaoh \ 32 | /path/to/distributed.phar \ 33 | /path/to/built-from-source-code.phar 34 | ``` 35 | 36 | Sample output: 37 | 38 | ```diff 39 | $ pharaoh dist/sodium-compat-php5.phar dist/sodium-compat-php7.phar 40 | diff --git a/tmp/phr_GPrhh5/vendor/autoload.php b/tmp/phr_SBYnr7/vendor/autoload.php 41 | index c20d4db..5c849e0 100644 42 | --- a/tmp/phr_GPrhh5/vendor/autoload.php 43 | +++ b/tmp/phr_SBYnr7/vendor/autoload.php 44 | @@ -4,4 +4,4 @@ 45 | 46 | require_once __DIR__ . '/composer/autoload_real.php'; 47 | 48 | -return ComposerAutoloaderInitf6d95af9246e0e0e98e255e3bc14c82b::getLoader(); 49 | +return ComposerAutoloaderInitd4c7400998bd39c407a1d41a47cd86c6::getLoader(); 50 | diff --git a/tmp/phr_GPrhh5/vendor/composer/autoload_real.php b/tmp/phr_SBYnr7/vendor/composer/autoload_real.php 51 | index a23d814..432c698 100644 52 | --- a/tmp/phr_GPrhh5/vendor/composer/autoload_real.php 53 | +++ b/tmp/phr_SBYnr7/vendor/composer/autoload_real.php 54 | @@ -2,7 +2,7 @@ 55 | 56 | 57 | 58 | -class ComposerAutoloaderInitf6d95af9246e0e0e98e255e3bc14c82b 59 | +class ComposerAutoloaderInitd4c7400998bd39c407a1d41a47cd86c6 60 | { 61 | private static $loader; 62 | 63 | @@ -19,15 +19,15 @@ if (null !== self::$loader) { 64 | return self::$loader; 65 | } 66 | 67 | -spl_autoload_register(array('ComposerAutoloaderInitf6d95af9246e0e0e98e255e3bc14c82b', 'loadClassLoader'), true, true); 68 | +spl_autoload_register(array('ComposerAutoloaderInitd4c7400998bd39c407a1d41a47cd86c6', 'loadClassLoader'), true, true); 69 | self::$loader = $loader = new \Composer\Autoload\ClassLoader(); 70 | -spl_autoload_unregister(array('ComposerAutoloaderInitf6d95af9246e0e0e98e255e3bc14c82b', 'loadClassLoader')); 71 | +spl_autoload_unregister(array('ComposerAutoloaderInitd4c7400998bd39c407a1d41a47cd86c6', 'loadClassLoader')); 72 | 73 | $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded()); 74 | if ($useStaticLoader) { 75 | require_once __DIR__ . '/autoload_static.php'; 76 | 77 | -call_user_func(\Composer\Autoload\ComposerStaticInitf6d95af9246e0e0e98e255e3bc14c82b::getInitializer($loader)); 78 | +call_user_func(\Composer\Autoload\ComposerStaticInitd4c7400998bd39c407a1d41a47cd86c6::getInitializer($loader)); 79 | } else { 80 | $map = require __DIR__ . '/autoload_namespaces.php'; 81 | foreach ($map as $namespace => $path) { 82 | @@ -48,19 +48,19 @@ $loader->addClassMap($classMap); 83 | $loader->register(true); 84 | 85 | if ($useStaticLoader) { 86 | -$includeFiles = Composer\Autoload\ComposerStaticInitf6d95af9246e0e0e98e255e3bc14c82b::$files; 87 | +$includeFiles = Composer\Autoload\ComposerStaticInitd4c7400998bd39c407a1d41a47cd86c6::$files; 88 | } else { 89 | $includeFiles = require __DIR__ . '/autoload_files.php'; 90 | } 91 | foreach ($includeFiles as $fileIdentifier => $file) { 92 | -composerRequiref6d95af9246e0e0e98e255e3bc14c82b($fileIdentifier, $file); 93 | +composerRequired4c7400998bd39c407a1d41a47cd86c6($fileIdentifier, $file); 94 | } 95 | 96 | return $loader; 97 | } 98 | } 99 | 100 | -function composerRequiref6d95af9246e0e0e98e255e3bc14c82b($fileIdentifier, $file) 101 | +function composerRequired4c7400998bd39c407a1d41a47cd86c6($fileIdentifier, $file) 102 | { 103 | if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) { 104 | require $file; 105 | diff --git a/tmp/phr_GPrhh5/vendor/composer/autoload_static.php b/tmp/phr_SBYnr7/vendor/composer/autoload_static.php 106 | index d10ce9b..55ba104 100644 107 | --- a/tmp/phr_GPrhh5/vendor/composer/autoload_static.php 108 | +++ b/tmp/phr_SBYnr7/vendor/composer/autoload_static.php 109 | @@ -4,7 +4,7 @@ 110 | 111 | namespace Composer\Autoload; 112 | 113 | -class ComposerStaticInitf6d95af9246e0e0e98e255e3bc14c82b 114 | +class ComposerStaticInitd4c7400998bd39c407a1d41a47cd86c6 115 | { 116 | public static $files = array ( 117 | '5255c38a0faeba867671b61dfda6d864' => __DIR__ . '/..' . '/paragonie/random_compat/lib/random.php', 118 | 119 | ``` 120 | 121 | ### GNU diffs (`-d`) 122 | 123 | By default, Pharaoh will use `git` to generate a diff of the code. If you'd prefer 124 | a GNU diff, pass the `-d` flag, like so: 125 | 126 | ```sh 127 | /path/to/pharaoh -d \ 128 | /path/to/distributed.phar \ 129 | /path/to/built-from-source-code.phar 130 | ``` 131 | 132 | Sample output: 133 | 134 | ```diff 135 | $ pharaoh -d dist/sodium-compat-php5.phar dist/sodium-compat-php7.phar 136 | Common subdirectories: /tmp/phr_EsTl2p/lib and /tmp/phr_UV3iJt/lib 137 | Common subdirectories: /tmp/phr_EsTl2p/src and /tmp/phr_UV3iJt/src 138 | Common subdirectories: /tmp/phr_EsTl2p/vendor and /tmp/phr_UV3iJt/vendor 139 | ``` 140 | 141 | ### File hashes (`-c`, `--check`) 142 | 143 | If you're more interested in verifying the authenticity of a Pharaoh's contents from 144 | a quick scan, you can use the `-c algo` or `--check=algo` arguments to specify the hash 145 | function to use. 146 | 147 | ```sh 148 | /path/to/pharaoh --check=sha256 \ 149 | /path/to/distributed.phar \ 150 | /path/to/built-from-source-code.phar 151 | 152 | # This is equivalent to the above command: 153 | /path/to/pharaoh -csha256 \ 154 | /path/to/distributed.phar \ 155 | /path/to/built-from-source-code.phar 156 | ``` 157 | 158 | Sample output: 159 | 160 | ```terminal 161 | $ pharaoh --check=blake2b dist/sodium-compat-php5.phar dist/sodium-compat-php7.phar 162 | /vendor/autoload.php 163 | 83d3edc0cc50bbe1d4a05ec1c269359b1eddeb0d7d706f81c7bfb52e7a2dd86c 3310968acbf487a14e38e55077cf792bcd649f48e001717a35506f12031c97a9 164 | /vendor/composer/autoload_static.php 165 | a3e53155cbc5faccc2f8bb1d28dfe202ac033504d0e72268847bf33429bb47df da215e6479739f87f04248880d0bfee78bd5d0828e61c8fb38a85902d42e844c 166 | /vendor/composer/autoload_real.php 167 | 70e1113f73dc73a61594ee5cb724c44019f8f1a79321e51532a3cf0ef582b50c 1fd1127fb6a245128b5901f5d1087a1fbe0477e525d477192bd91a7623cf152a 168 | ``` 169 | 170 | All hash functions supported by PHP are accepted here. Additionally, if you specify `blake2b`, 171 | Pharaoh will use [sodium_compat](https://github.com/paragonie/sodium_compat) to generate a BLAKE2b 172 | hash of each file. 173 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "paragonie/pharaoh", 3 | "description": "Compare PHARs from the Command Line", 4 | "keywords": [ 5 | "tool", 6 | "utility", 7 | "phar", 8 | "security", 9 | "auditing", 10 | "diff" 11 | ], 12 | "license": "MIT", 13 | "type": "library", 14 | "authors": [ 15 | { 16 | "name": "Scott Arciszewski", 17 | "email": "scott@paragonie.com", 18 | "homepage": "https://paragonie.com", 19 | "role": "Developer" 20 | } 21 | ], 22 | "require": { 23 | "php": "^7.1|^8", 24 | "paragonie/constant_time_encoding": "^2|^3", 25 | "paragonie/sodium_compat": "^1.3", 26 | "ulrichsg/getopt-php": "^3" 27 | }, 28 | "require-dev": { 29 | "vimeo/psalm": "^1|^2|^3|^4" 30 | }, 31 | "support": { 32 | "issues": "https://github.com/paragonie/pharaoh/issues", 33 | "email": "info@paragonie.com", 34 | "source": "https://github.com/paragonie/pharaoh" 35 | }, 36 | "bin": ["pharaoh"], 37 | "autoload": { 38 | "psr-4": { 39 | "ParagonIE\\Pharaoh\\": "src/Pharaoh/" 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # Pharaoh Documentation 2 | 3 | TODO -------------------------------------------------------------------------------- /pharaoh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | basedir=$( dirname $( readlink -f ${BASH_SOURCE[0]} ) ) 4 | php -dphar.readonly=0 $basedir/src/pharaoh.php $* 5 | -------------------------------------------------------------------------------- /psalm.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/Pharaoh/PharDiff.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | protected $c = [ 16 | '' => "\033[0;39m", 17 | 'red' => "\033[0;31m", 18 | 'green' => "\033[0;32m", 19 | 'blue' => "\033[1;34m", 20 | 'cyan' => "\033[1;36m", 21 | 'silver' => "\033[0;37m", 22 | 'yellow' => "\033[0;93m" 23 | ]; 24 | 25 | /** @var array */ 26 | private $phars = []; 27 | 28 | /** @var bool $verbose */ 29 | private $verbose = false; 30 | 31 | /** 32 | * Constructor uses dependency injection. 33 | * 34 | * @param \ParagonIE\Pharaoh\Pharaoh $pharA 35 | * @param \ParagonIE\Pharaoh\Pharaoh $pharB 36 | */ 37 | public function __construct(Pharaoh $pharA, Pharaoh $pharB) 38 | { 39 | $this->phars = [$pharA, $pharB]; 40 | } 41 | 42 | /** 43 | * Prints a git-formatted diff of the two phars. 44 | * 45 | * @psalm-suppress ForbiddenCode 46 | * @return int 47 | */ 48 | public function printGitDiff(): int 49 | { 50 | // Lazy way; requires git. Will replace with custom implementaiton later. 51 | 52 | $argA = \escapeshellarg($this->phars[0]->tmp); 53 | $argB = \escapeshellarg($this->phars[1]->tmp); 54 | /** @var string $diff */ 55 | $diff = `git diff --no-index $argA $argB`; 56 | echo $diff; 57 | if (empty($diff) && $this->verbose) { 58 | echo 'No differences encountered.', PHP_EOL; 59 | return 0; 60 | } 61 | return 1; 62 | } 63 | 64 | /** 65 | * Prints a GNU diff of the two phars. 66 | * 67 | * @psalm-suppress ForbiddenCode 68 | * @return int 69 | */ 70 | public function printGnuDiff(): int 71 | { 72 | // Lazy way. Will replace with custom implementaiton later. 73 | $argA = \escapeshellarg($this->phars[0]->tmp); 74 | $argB = \escapeshellarg($this->phars[1]->tmp); 75 | /** @var string $diff */ 76 | $diff = `diff $argA $argB`; 77 | echo $diff; 78 | if (empty($diff) && $this->verbose) { 79 | echo 'No differences encountered.', PHP_EOL; 80 | return 0; 81 | } 82 | return 1; 83 | } 84 | 85 | /** 86 | * Get hashes of all of the files in the two arrays. 87 | * 88 | * @param string $algo 89 | * @param string $dirA 90 | * @param string $dirB 91 | * @return array> 92 | * @throws \SodiumException 93 | */ 94 | public function hashChildren(string $algo,string $dirA, string $dirB) 95 | { 96 | /** 97 | * @var string $a 98 | * @var string $b 99 | */ 100 | $a = $b = ''; 101 | $filesA = $this->listAllFiles($dirA); 102 | $filesB = $this->listAllFiles($dirB); 103 | $numFiles = \max(\count($filesA), \count($filesB)); 104 | 105 | // Array of two empty arrays 106 | $hashes = [[], []]; 107 | for ($i = 0; $i < $numFiles; ++$i) { 108 | $thisFileA = (string) $filesA[$i]; 109 | $thisFileB = (string) $filesB[$i]; 110 | if (isset($filesA[$i])) { 111 | $a = \preg_replace('#^'.\preg_quote($dirA, '#').'#', '', $thisFileA); 112 | if (isset($filesB[$i])) { 113 | $b = \preg_replace('#^'.\preg_quote($dirB, '#').'#', '', $thisFileB); 114 | } else { 115 | $b = $a; 116 | } 117 | } elseif (isset($filesB[$i])) { 118 | $b = \preg_replace('#^'.\preg_quote($dirB, '#').'#', '', $thisFileB); 119 | $a = $b; 120 | } 121 | 122 | if (isset($filesA[$i])) { 123 | // A exists 124 | if (\strtolower($algo) === 'blake2b') { 125 | $hashes[0][$a] = Hex::encode(\ParagonIE_Sodium_File::generichash($thisFileA)); 126 | } else { 127 | $hashes[0][$a] = \hash_file($algo, $thisFileA); 128 | } 129 | } elseif (isset($filesB[$i])) { 130 | // A doesn't exist, B does 131 | $hashes[0][$a] = ''; 132 | } 133 | 134 | if (isset($filesB[$i])) { 135 | // B exists 136 | if (\strtolower($algo) === 'blake2b') { 137 | $hashes[1][$b] = Hex::encode(\ParagonIE_Sodium_File::generichash($thisFileB)); 138 | } else { 139 | $hashes[1][$b] = \hash_file($algo, $thisFileB); 140 | } 141 | } elseif (isset($filesA[$i])) { 142 | // B doesn't exist, A does 143 | $hashes[1][$b] = ''; 144 | } 145 | } 146 | return $hashes; 147 | } 148 | 149 | 150 | /** 151 | * List all the files in a directory (and subdirectories) 152 | * 153 | * @param string $folder - start searching here 154 | * @param string $extension - extensions to match 155 | * @return array 156 | */ 157 | private function listAllFiles($folder, $extension = '*') 158 | { 159 | /** 160 | * @var array $fileList 161 | * @var string $i 162 | * @var string $file 163 | * @var \RecursiveDirectoryIterator $dir 164 | * @var \RecursiveIteratorIterator $ite 165 | */ 166 | $dir = new \RecursiveDirectoryIterator($folder); 167 | $ite = new \RecursiveIteratorIterator($dir); 168 | if ($extension === '*') { 169 | $pattern = '/.*/'; 170 | } else { 171 | $pattern = '/.*\.' . $extension . '$/'; 172 | } 173 | $files = new \RegexIterator($ite, $pattern, \RegexIterator::GET_MATCH); 174 | 175 | /** @var array $fileList */ 176 | $fileList = []; 177 | 178 | /** 179 | * @var string[] $fileSub 180 | */ 181 | foreach($files as $fileSub) { 182 | $fileList = \array_merge($fileList, $fileSub); 183 | } 184 | 185 | /** 186 | * @var string $i 187 | * @var string $file 188 | */ 189 | foreach ($fileList as $i => $file) { 190 | if (\preg_match('#/\.{1,2}$#', $file)) { 191 | unset($fileList[$i]); 192 | } 193 | } 194 | return \array_values($fileList); 195 | } 196 | 197 | /** 198 | * Prints out all of the differences of checksums of the files contained 199 | * in both PHP archives. 200 | * 201 | * @param string $algo 202 | * @return int 203 | * @throws \SodiumException 204 | */ 205 | public function listChecksums(string $algo = 'sha384'): int 206 | { 207 | list($pharA, $pharB) = $this->hashChildren( 208 | $algo, 209 | $this->phars[0]->tmp, 210 | $this->phars[1]->tmp 211 | ); 212 | 213 | $diffs = 0; 214 | /** @var string $i */ 215 | foreach (\array_keys($pharA) as $i) { 216 | if (isset($pharA[$i]) && isset($pharB[$i])) { 217 | // We are NOT concerned about local timing attacks. 218 | if ($pharA[$i] !== $pharB[$i]) { 219 | ++$diffs; 220 | echo "\t", $i, 221 | "\n\t\t", $this->c['red'], $pharA[$i], $this->c[''], 222 | "\t", $this->c['green'], $pharB[$i], $this->c[''], 223 | "\n"; 224 | } elseif (!empty($pharA[$i]) && empty($pharB[$i])) { 225 | ++$diffs; 226 | echo "\t", $i, 227 | "\n\t\t", $this->c['red'], $pharA[$i], $this->c[''], 228 | "\t", \str_repeat('-', \strlen($pharA[$i])), 229 | "\n"; 230 | } elseif (!empty($pharB[$i]) && empty($pharA[$i])) { 231 | ++$diffs; 232 | echo "\t", $i, 233 | "\n\t\t", \str_repeat('-', \strlen($pharB[$i])), 234 | "\t", $this->c['green'], $pharB[$i], $this->c[''], 235 | "\n"; 236 | } 237 | } 238 | } 239 | if ($diffs === 0) { 240 | if ($this->verbose) { 241 | echo 'No differences encountered.', PHP_EOL; 242 | } 243 | return 0; 244 | } 245 | return 1; 246 | } 247 | 248 | /** 249 | * Verbose mode says something when there are no differences. 250 | * By default, you can just check the return value. 251 | * 252 | * @param bool $value 253 | * @return void 254 | */ 255 | public function setVerbose(bool $value) 256 | { 257 | $this->verbose = $value; 258 | } 259 | } 260 | -------------------------------------------------------------------------------- /src/Pharaoh/PharError.php: -------------------------------------------------------------------------------- 1 | phar = new \Phar($tmpFile); 60 | $this->phar->setAlias($alias); 61 | 62 | // Make a random folder in /tmp 63 | /** @var string|bool $tmp */ 64 | $tmp = \tempnam(\sys_get_temp_dir(), 'phr_'); 65 | if (!\is_string($tmp)) { 66 | throw new \Error('Could not create temporary file'); 67 | } 68 | 69 | $this->tmp = $tmp; 70 | \unlink($this->tmp); 71 | if (!\mkdir($this->tmp, 0755, true) && !\is_dir($this->tmp)) { 72 | throw new \Error('Could not create temporary directory'); 73 | } 74 | 75 | // Let's extract to our temporary directory 76 | $this->phar->extractTo($this->tmp); 77 | 78 | // Also extract the stub 79 | \file_put_contents( 80 | $this->tmp . '/' . self::$stubfile, 81 | $this->phar->getStub() 82 | ); 83 | } 84 | 85 | public function __destruct() 86 | { 87 | $path = $this->phar->getPath(); 88 | unset($this->phar); 89 | 90 | \Phar::unlinkArchive($path); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/pharaoh.php: -------------------------------------------------------------------------------- 1 | setVerbose(true); 28 | } 29 | 30 | if (!empty($opts['c'])) { 31 | $return = $diff->listChecksums($opts['c']); 32 | } elseif (!empty($opts['check'])) { 33 | $return = $diff->listChecksums($opts['check']); 34 | } elseif (isset($opts['d']) || isset($opts['diff'])) { 35 | $return = $diff->printGnuDiff(); 36 | } else { 37 | $return = $diff->printGitDiff(); 38 | } 39 | exit($return); 40 | --------------------------------------------------------------------------------