├── .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 | [](https://github.com/paragonie/pharaoh/actions)
7 | [](https://packagist.org/packages/paragonie/pharaoh)
8 | [](https://packagist.org/packages/paragonie/pharaoh)
9 | [](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 |
--------------------------------------------------------------------------------