Completed
Push — master ( 6a1fad...afb84c )
by Sebastian
05:39
created

Cmd::printVersionString()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 9
c 0
b 0
f 0
rs 9.6666
cc 2
eloc 5
nc 2
nop 0
1
<?php
2
/**
3
 * phpbu
4
 *
5
 * Copyright (c) 2014 - 2017 Sebastian Feldmann <[email protected]>
6
 *
7
 * Permission is hereby granted, free of charge, to any person obtaining a copy
8
 * of this software and associated documentation files (the "Software"), to deal
9
 * in the Software without restriction, including without limitation the rights
10
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
 * copies of the Software, and to permit persons to whom the Software is
12
 * furnished to do so, subject to the following conditions:
13
 *
14
 * The above copyright notice and this permission notice shall be included in
15
 * all copies or substantial portions of the Software.
16
 *
17
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
 * THE SOFTWARE.
24
 *
25
 * @package    phpbu
26
 * @author     Sebastian Feldmann <[email protected]>
27
 * @copyright  Sebastian Feldmann
28
 * @license    https://opensource.org/licenses/MIT The MIT License (MIT)
29
 * @link       http://phpbu.de/
30
 * @since      Class available since Release 1.0.0
31
 */
32
namespace phpbu\App;
33
34
use Phar;
35
use phpbu\App\Cmd\Args;
36
use phpbu\App\Util\Arr;
37
38
/**
39
 * Main application class.
40
 *
41
 * @package    phpbu
42
 * @author     Sebastian Feldmann <[email protected]>
43
 * @copyright  Sebastian Feldmann <[email protected]>
44
 * @license    https://opensource.org/licenses/MIT The MIT License (MIT)
45
 * @link       http://phpbu.de/
46
 * @since      Class available since Release 1.0.0
47
 */
48
class Cmd
49
{
50
    const EXIT_SUCCESS   = 0;
51
    const EXIT_FAILURE   = 1;
52
    const EXIT_EXCEPTION = 2;
53
54
    /**
55
     * Ascii-Art app logo
56
     *
57
     * @var string
58
     */
59
    private static $logo = '             __          __
60
      ____  / /_  ____  / /_  __  __
61
     / __ \/ __ \/ __ \/ __ \/ / / /
62
    / /_/ / / / / /_/ / /_/ / /_/ /
63
   / .___/_/ /_/ .___/_.___/\__,_/
64
  /_/         /_/
65
';
66
67
    /**
68
     * Is cmd executed from phar.
69
     *
70
     * @var boolean
71
     */
72
    private $isPhar;
73
74
    /**
75
     * Is version string printed already.
76
     *
77
     * @var boolean
78
     */
79
    private $isVersionStringPrinted = false;
80
81
    /**
82
     * List of given arguments
83
     *
84
     * @var array
85
     */
86
    private $arguments;
87
88
    /**
89
     * Runs the application.
90
     *
91
     * @param array $args
92
     */
93
    public function run(array $args)
94
    {
95
        $this->isPhar = defined('__PHPBU_PHAR__');
96
        $this->handleOpt($args);
97
        $this->findConfiguration();
98
99
        $ret     = self::EXIT_FAILURE;
100
        $factory = new Factory();
101
        $runner  = new Runner($factory);
102
103
        try {
104
            $this->printVersionString();
105
            $result = $runner->run($this->createConfiguration($factory));
106
107
            if ($result->wasSuccessful()) {
108
                $ret = self::EXIT_SUCCESS;
109
            } elseif ($result->errorCount() > 0) {
110
                $ret = self::EXIT_EXCEPTION;
111
            }
112
        } catch (\Exception $e) {
113
            echo $e->getMessage() . PHP_EOL;
114
            $ret = self::EXIT_EXCEPTION;
115
        }
116
117
        exit($ret);
118
    }
119
120
    /**
121
     * Check arguments and load configuration file.
122
     *
123
     * @param array $args
124
     */
125
    protected function handleOpt(array $args)
126
    {
127
        try {
128
            $parser  = new Args($this->isPhar);
129
            $options = $parser->getOptions($args);
130
            $this->handleArgs($options);
131
        } catch (Exception $e) {
132
            $this->printError($e->getMessage(), true);
133
        }
134
    }
135
136
    /**
137
     * Handle the parsed command line options
138
     *
139
     * @param  array $options
140
     * @return void
141
     */
142
    protected function handleArgs(array $options)
143
    {
144
        foreach ($options as $option => $argument) {
145
            switch ($option) {
146
                case '--bootstrap':
147
                    $this->arguments['bootstrap'] = $argument;
148
                    break;
149
                case '--colors':
150
                    $this->arguments['colors'] = $argument;
151
                    break;
152
                case '--configuration':
153
                    $this->arguments['configuration'] = $argument;
154
                    break;
155
                case '--debug':
156
                    $this->arguments['debug'] = $argument;
157
                    break;
158
                case '-h':
159
                case '--help':
160
                    $this->printHelp();
161
                    exit(self::EXIT_SUCCESS);
162
                case 'include-path':
163
                    $this->arguments['include-path'] = $argument;
164
                    break;
165
                case '--limit':
166
                    $this->arguments['limit'] = $argument;
167
                    break;
168
                case '--self-upgrade':
169
                    $this->handleSelfUpgrade();
170
                    break;
171
                case '--version-check':
172
                    $this->handleVersionCheck();
173
                    break;
174
                case '--simulate':
175
                    $this->arguments['simulate'] = $argument;
176
                    break;
177
                case '-v':
178
                case '--verbose':
179
                    $this->arguments['verbose'] = true;
180
                    break;
181
                case '-V':
182
                case '--version':
183
                    $this->printVersionString();
184
                    exit(self::EXIT_SUCCESS);
185
            }
186
        }
187
    }
188
189
    /**
190
     * Try to find the configuration file.
191
     */
192
    protected function findConfiguration()
193
    {
194
        // check configuration argument
195
        // if configuration argument is a directory
196
        // check for default configuration files 'phpbu.xml' and 'phpbu.xml.dist'
197
        if (isset($this->arguments['configuration']) && is_file($this->arguments['configuration'])) {
198
            $this->arguments['configuration'] = realpath($this->arguments['configuration']);
199
        } elseif (isset($this->arguments['configuration']) && is_dir($this->arguments['configuration'])) {
200
            $this->findConfigurationInDir();
201
        } elseif (!isset($this->arguments['configuration'])) {
202
            // no configuration argument search for default configuration files
203
            // 'phpbu.xml' and 'phpbu.xml.dist' in current working directory
204
            $this->findConfigurationDefault();
205
        }
206
        // no config found, exit with some help output
207
        if (!isset($this->arguments['configuration'])) {
208
            $this->printLogo();
209
            $this->printHelp();
210
            exit(self::EXIT_EXCEPTION);
211
        }
212
    }
213
214
    /**
215
     * Check directory for default configuration files phpbu.xml, phpbu.xml.dist.
216
     *
217
     * @return void
218
     */
219
    protected function findConfigurationInDir()
220
    {
221
        $configurationFile = $this->arguments['configuration'] . '/phpbu.xml';
222
223
        if (file_exists($configurationFile)) {
224
            $this->arguments['configuration'] = realpath($configurationFile);
225
        } elseif (file_exists($configurationFile . '.dist')) {
226
            $this->arguments['configuration'] = realpath($configurationFile . '.dist');
227
        }
228
    }
229
230
    /**
231
     * Check default configuration files phpbu.xml, phpbu.xml.dist in current working directory.
232
     *
233
     * @return void
234
     */
235
    protected function findConfigurationDefault()
236
    {
237
        if (file_exists('phpbu.xml')) {
238
            $this->arguments['configuration'] = realpath('phpbu.xml');
239
        } elseif (file_exists('phpbu.xml.dist')) {
240
            $this->arguments['configuration'] = realpath('phpbu.xml.dist');
241
        }
242
    }
243
244
    /**
245
     * Create a application configuration.
246
     *
247
     * @param  \phpbu\App\Factory $factory
248
     * @return \phpbu\App\Configuration
249
     */
250
    protected function createConfiguration(Factory $factory)
251
    {
252
        $configLoader  = Configuration\Loader\Factory::createLoader($this->arguments['configuration']);
253
        $configuration = $configLoader->getConfiguration($factory);
254
255
        // command line arguments overrule the config file settings
256
        $this->overrideConfigWithArgument($configuration, 'verbose');
257
        $this->overrideConfigWithArgument($configuration, 'colors');
258
        $this->overrideConfigWithArgument($configuration, 'debug');
259
        $this->overrideConfigWithArgument($configuration, 'simulate');
260
        $this->overrideConfigWithArgument($configuration, 'bootstrap');
261
262
        // check for command line limit option
263
        $limitOption = Arr::getValue($this->arguments, 'limit');
264
        $configuration->setLimit(!empty($limitOption) ? explode(',', $limitOption) : []);
265
266
        // add a cli printer for some output
267
        $configuration->addLogger(
268
            new Result\PrinterCli(
269
                $configuration->getVerbose(),
270
                $configuration->getColors(),
271
                ($configuration->getDebug() || $configuration->isSimulation())
272
            )
273
        );
274
        return $configuration;
275
    }
276
277
    /**
278
     * Override configuration settings with command line arguments.
279
     *
280
     * @param \phpbu\App\Configuration $configuration
281
     * @param string                   $arg
282
     */
283
    protected function overrideConfigWithArgument(Configuration $configuration, string $arg)
284
    {
285
        $value = Arr::getValue($this->arguments, $arg);
286
        if (!empty($value)) {
287
            $setter = 'set' . ucfirst($arg);
288
            $configuration->{$setter}($value);
289
        }
290
    }
291
292
    /**
293
     * Handle the phar self-update.
294
     */
295
    protected function handleSelfUpgrade()
296
    {
297
        $this->printVersionString();
298
299
        // check if upgrade is necessary
300
        if (!$this->isPharOutdated($this->getLatestVersion())) {
301
            echo 'You already have the latest version of phpbu installed.' . PHP_EOL;
302
            exit(self::EXIT_SUCCESS);
303
        }
304
305
        $remoteFilename = 'http://phar.phpbu.de/phpbu.phar';
306
        $localFilename  = realpath($_SERVER['argv'][0]);
307
        $tempFilename   = basename($localFilename, '.phar') . '-temp.phar';
308
309
        echo 'Updating the phpbu PHAR ... ';
310
311
        $old  = error_reporting(0);
312
        $phar = file_get_contents($remoteFilename);
313
        error_reporting($old);
314
        if (!$phar) {
315
            echo ' failed' . PHP_EOL . 'Could not reach phpbu update site' . PHP_EOL;
316
            exit(self::EXIT_EXCEPTION);
317
        }
318
        file_put_contents($tempFilename, $phar);
319
320
        chmod($tempFilename, 0777 & ~umask());
321
322
        // check downloaded phar
323
        try {
324
            $phar = new Phar($tempFilename);
325
            unset($phar);
326
            // replace current phar with the new one
327
            rename($tempFilename, $localFilename);
328
        } catch (Exception $e) {
329
            // cleanup crappy phar
330
            unlink($tempFilename);
331
            echo 'failed' . PHP_EOL . $e->getMessage() . PHP_EOL;
332
            exit(self::EXIT_EXCEPTION);
333
        }
334
335
        echo 'done' . PHP_EOL;
336
        exit(self::EXIT_SUCCESS);
337
    }
338
339
    /**
340
     * Handle phar version-check.
341
     */
342
    protected function handleVersionCheck()
343
    {
344
        $this->printVersionString();
345
346
        $latestVersion = $this->getLatestVersion();
347
        if ($this->isPharOutdated($latestVersion)) {
348
            print 'You are not using the latest version of phpbu.' . PHP_EOL
349
                . 'Use "phpunit --self-upgrade" to install phpbu ' . $latestVersion . PHP_EOL;
350
        } else {
351
            print 'You are using the latest version of phpbu.' . PHP_EOL;
352
        }
353
        exit(self::EXIT_SUCCESS);
354
    }
355
356
    /**
357
     * Returns latest released phpbu version.
358
     *
359
     * @return string
360
     * @throws \RuntimeException
361
     */
362
    protected function getLatestVersion() : string
363
    {
364
        $old     = error_reporting(0);
365
        $version = file_get_contents('https://phar.phpbu.de/latest-version-of/phpbu');
366
        error_reporting($old);
367
        if (!$version) {
368
            echo 'Network-Error: Could not check latest version.' . PHP_EOL;
369
            exit(self::EXIT_EXCEPTION);
370
        }
371
        return $version;
372
    }
373
374
    /**
375
     * Check if current phar is outdated.
376
     *
377
     * @param  string $latestVersion
378
     * @return bool
379
     */
380
    protected function isPharOutdated(string $latestVersion) : bool
381
    {
382
        return version_compare($latestVersion, Version::id(), '>');
383
    }
384
385
    /**
386
     * Shows the current application version.
387
     */
388
    protected function printVersionString()
389
    {
390
        if ($this->isVersionStringPrinted) {
391
            return;
392
        }
393
394
        echo Version::getVersionString() . PHP_EOL . PHP_EOL;
395
        $this->isVersionStringPrinted = true;
396
    }
397
398
    /**
399
     * Show the phpbu logo
400
     */
401
    protected function printLogo()
402
    {
403
        echo self::$logo . PHP_EOL;
404
    }
405
406
    /**
407
     * Show the help message.
408
     */
409
    protected function printHelp()
410
    {
411
        $this->printVersionString();
412
        echo <<<EOT
413
Usage: phpbu [option]
414
415
  --bootstrap=<file>     A "bootstrap" PHP file that is included before the backup.
416
  --configuration=<file> A phpbu xml config file.
417
  --colors               Use colors in output.
418
  --debug                Display debugging information during backup generation.
419
  --limit=<subset>       Limit backup execution to a subset.
420
  --simulate             Perform a trial run with no changes made.
421
  -h, --help             Print this usage information.
422
  -v, --verbose          Output more verbose information.
423
  -V, --version          Output version information and exit.
424
425
EOT;
426
        if ($this->isPhar) {
427
            echo '  --version-check        Check whether phpbu is the latest version.' . PHP_EOL;
428
            echo '  --self-upgrade         Upgrade phpbu to the latest version.' . PHP_EOL;
429
        }
430
    }
431
432
    /**
433
     * Shows some given error message.
434
     *
435
     * @param string $message
436
     * @param bool   $hint
437
     */
438
    private function printError($message, $hint = false)
439
    {
440
        $help = $hint ? ', use "phpbu -h" for help' : '';
441
        $this->printVersionString();
442
        echo $message . $help . PHP_EOL;
443
        exit(self::EXIT_EXCEPTION);
444
    }
445
446
    /**
447
     * Main method, is called by phpbu command and the phar file.
448
     */
449
    public static function main()
450
    {
451
        $app = new static();
452
        $app->run($_SERVER['argv']);
453
    }
454
}
455