Completed
Push — master ( f5328c...5d3bf9 )
by Sebastian
03:08
created

Cmd::overrideConfigWithArgument()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 8
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 5
nc 2
nop 2
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
     * Is cmd executed from phar.
56
     *
57
     * @var boolean
58
     */
59
    private $isPhar;
60
61
    /**
62
     * Is version string printed already.
63
     *
64
     * @var boolean
65
     */
66
    private $isVersionStringPrinted = false;
67
68
    /**
69
     * List of given arguments
70
     *
71
     * @var array
72
     */
73
    private $arguments;
74
75
    /**
76
     * Runs the application.
77
     *
78
     * @param array $args
79
     */
80
    public function run(array $args)
81
    {
82
        $this->isPhar = defined('__PHPBU_PHAR__');
83
        $this->handleOpt($args);
84
85
        $ret        = self::EXIT_FAILURE;
86
        $factory    = new Factory();
87
        $runner     = new Runner($factory);
88
        $configFile = $this->findConfiguration();
89
90
        try {
91
            $this->printVersionString();
92
            $result = $runner->run($this->createConfiguration($configFile, $factory));
93
94
            if ($result->wasSuccessful()) {
95
                $ret = self::EXIT_SUCCESS;
96
            } elseif ($result->errorCount() > 0) {
97
                $ret = self::EXIT_EXCEPTION;
98
            }
99
        } catch (\Exception $e) {
100
            echo $e->getMessage() . PHP_EOL;
101
            $ret = self::EXIT_EXCEPTION;
102
        }
103
104
        exit($ret);
105
    }
106
107
    /**
108
     * Check arguments and load configuration file.
109
     *
110
     * @param array $args
111
     */
112
    protected function handleOpt(array $args)
113
    {
114
        try {
115
            $parser  = new Args($this->isPhar);
116
            $options = $parser->getOptions($args);
117
            $this->handleArgs($options);
118
        } catch (Exception $e) {
119
            $this->printError($e->getMessage(), true);
120
        }
121
    }
122
123
    /**
124
     * Handle the parsed command line options
125
     *
126
     * @param  array $options
127
     * @return void
128
     */
129
    protected function handleArgs(array $options)
130
    {
131
        foreach ($options as $option => $argument) {
132
            switch ($option) {
133
                case '--bootstrap':
134
                    $this->arguments['bootstrap'] = $argument;
135
                    break;
136
                case '--colors':
137
                    $this->arguments['colors'] = $argument;
138
                    break;
139
                case '--configuration':
140
                    $this->arguments['configuration'] = $argument;
141
                    break;
142
                case '--debug':
143
                    $this->arguments['debug'] = $argument;
144
                    break;
145
                case '-h':
146
                case '--help':
147
                    $this->printHelp();
148
                    exit(self::EXIT_SUCCESS);
149
                case 'include-path':
150
                    $this->arguments['include-path'] = $argument;
151
                    break;
152
                case '--limit':
153
                    $this->arguments['limit'] = $argument;
154
                    break;
155
                case '--self-upgrade':
156
                    $this->handleSelfUpgrade();
157
                    break;
158
                case '--version-check':
159
                    $this->handleVersionCheck();
160
                    break;
161
                case '--simulate':
162
                    $this->arguments['simulate'] = $argument;
163
                    break;
164
                case '-v':
165
                case '--verbose':
166
                    $this->arguments['verbose'] = true;
167
                    break;
168
                case '-V':
169
                case '--version':
170
                    $this->printVersionString();
171
                    exit(self::EXIT_SUCCESS);
172
            }
173
        }
174
    }
175
176
    /**
177
     * Try to find a configuration file.
178
     */
179
    protected function findConfiguration() : string
180
    {
181
        $configOption = $this->arguments['configuration'] ?? '';
182
183
        try {
184
            $finder = new Configuration\Finder();
185
            return $finder->findConfiguration($configOption);
186
        } catch (\Exception $e) {
187
            // config option given but still no config found
188
            if (!empty($configOption)) {
189
                $this->printError('Can\'t find configuration file.');
190
            }
191
            $this->printHelp();
192
            exit(self::EXIT_EXCEPTION);
193
        }
194
    }
195
196
    /**
197
     * Create a application configuration.
198
     *
199
     * @param  string             $configurationFile
200
     * @param  \phpbu\App\Factory $factory
201
     * @return \phpbu\App\Configuration
202
     */
203
    protected function createConfiguration(string $configurationFile, Factory $factory)
204
    {
205
        $configLoader  = Configuration\Loader\Factory::createLoader($configurationFile);
206
        $configuration = $configLoader->getConfiguration($factory);
207
208
        // command line arguments overrule the config file settings
209
        $this->overrideConfigWithArgument($configuration, 'verbose');
210
        $this->overrideConfigWithArgument($configuration, 'colors');
211
        $this->overrideConfigWithArgument($configuration, 'debug');
212
        $this->overrideConfigWithArgument($configuration, 'simulate');
213
        $this->overrideConfigWithArgument($configuration, 'bootstrap');
214
215
        // check for command line limit option
216
        $limitOption = Arr::getValue($this->arguments, 'limit');
217
        $configuration->setLimit(!empty($limitOption) ? explode(',', $limitOption) : []);
218
219
        // add a cli printer for some output
220
        $configuration->addLogger(
221
            new Result\PrinterCli(
222
                $configuration->getVerbose(),
223
                $configuration->getColors(),
224
                ($configuration->getDebug() || $configuration->isSimulation())
225
            )
226
        );
227
        return $configuration;
228
    }
229
230
    /**
231
     * Override configuration settings with command line arguments.
232
     *
233
     * @param \phpbu\App\Configuration $configuration
234
     * @param string                   $arg
235
     */
236
    protected function overrideConfigWithArgument(Configuration $configuration, string $arg)
237
    {
238
        $value = Arr::getValue($this->arguments, $arg);
239
        if (!empty($value)) {
240
            $setter = 'set' . ucfirst($arg);
241
            $configuration->{$setter}($value);
242
        }
243
    }
244
245
    /**
246
     * Handle the phar self-update.
247
     */
248
    protected function handleSelfUpgrade()
249
    {
250
        $this->printVersionString();
251
252
        // check if upgrade is necessary
253
        if (!$this->isPharOutdated($this->getLatestVersion())) {
254
            echo 'You already have the latest version of phpbu installed.' . PHP_EOL;
255
            exit(self::EXIT_SUCCESS);
256
        }
257
258
        $remoteFilename = 'http://phar.phpbu.de/phpbu.phar';
259
        $localFilename  = realpath($_SERVER['argv'][0]);
260
        $tempFilename   = basename($localFilename, '.phar') . '-temp.phar';
261
262
        echo 'Updating the phpbu PHAR ... ';
263
264
        $old  = error_reporting(0);
265
        $phar = file_get_contents($remoteFilename);
266
        error_reporting($old);
267
        if (!$phar) {
268
            echo ' failed' . PHP_EOL . 'Could not reach phpbu update site' . PHP_EOL;
269
            exit(self::EXIT_EXCEPTION);
270
        }
271
        file_put_contents($tempFilename, $phar);
272
273
        chmod($tempFilename, 0777 & ~umask());
274
275
        // check downloaded phar
276
        try {
277
            $phar = new Phar($tempFilename);
278
            unset($phar);
279
            // replace current phar with the new one
280
            rename($tempFilename, $localFilename);
281
        } catch (Exception $e) {
282
            // cleanup crappy phar
283
            unlink($tempFilename);
284
            echo 'failed' . PHP_EOL . $e->getMessage() . PHP_EOL;
285
            exit(self::EXIT_EXCEPTION);
286
        }
287
288
        echo 'done' . PHP_EOL;
289
        exit(self::EXIT_SUCCESS);
290
    }
291
292
    /**
293
     * Handle phar version-check.
294
     */
295
    protected function handleVersionCheck()
296
    {
297
        $this->printVersionString();
298
299
        $latestVersion = $this->getLatestVersion();
300
        if ($this->isPharOutdated($latestVersion)) {
301
            print 'You are not using the latest version of phpbu.' . PHP_EOL
302
                . 'Use "phpunit --self-upgrade" to install phpbu ' . $latestVersion . PHP_EOL;
303
        } else {
304
            print 'You are using the latest version of phpbu.' . PHP_EOL;
305
        }
306
        exit(self::EXIT_SUCCESS);
307
    }
308
309
    /**
310
     * Returns latest released phpbu version.
311
     *
312
     * @return string
313
     * @throws \RuntimeException
314
     */
315
    protected function getLatestVersion() : string
316
    {
317
        $old     = error_reporting(0);
318
        $version = file_get_contents('https://phar.phpbu.de/latest-version-of/phpbu');
319
        error_reporting($old);
320
        if (!$version) {
321
            echo 'Network-Error: Could not check latest version.' . PHP_EOL;
322
            exit(self::EXIT_EXCEPTION);
323
        }
324
        return $version;
325
    }
326
327
    /**
328
     * Check if current phar is outdated.
329
     *
330
     * @param  string $latestVersion
331
     * @return bool
332
     */
333
    protected function isPharOutdated(string $latestVersion) : bool
334
    {
335
        return version_compare($latestVersion, Version::id(), '>');
336
    }
337
338
    /**
339
     * Shows the current application version.
340
     */
341
    protected function printVersionString()
342
    {
343
        if ($this->isVersionStringPrinted) {
344
            return;
345
        }
346
347
        echo Version::getVersionString() . PHP_EOL . PHP_EOL;
348
        $this->isVersionStringPrinted = true;
349
    }
350
351
    /**
352
     * Show the help message.
353
     */
354
    protected function printHelp()
355
    {
356
        $this->printVersionString();
357
        echo <<<EOT
358
Usage: phpbu [option]
359
360
  --bootstrap=<file>     A "bootstrap" PHP file that is included before the backup.
361
  --configuration=<file> A phpbu xml config file.
362
  --colors               Use colors in output.
363
  --debug                Display debugging information during backup generation.
364
  --limit=<subset>       Limit backup execution to a subset.
365
  --simulate             Perform a trial run with no changes made.
366
  -h, --help             Print this usage information.
367
  -v, --verbose          Output more verbose information.
368
  -V, --version          Output version information and exit.
369
370
EOT;
371
        if ($this->isPhar) {
372
            echo '  --version-check        Check whether phpbu is the latest version.' . PHP_EOL;
373
            echo '  --self-upgrade         Upgrade phpbu to the latest version.' . PHP_EOL;
374
        }
375
    }
376
377
    /**
378
     * Shows some given error message.
379
     *
380
     * @param string $message
381
     * @param bool   $hint
382
     */
383
    private function printError($message, $hint = false)
384
    {
385
        $help = $hint ? ', use "phpbu -h" for help' : '';
386
        $this->printVersionString();
387
        echo $message . $help . PHP_EOL;
388
        exit(self::EXIT_EXCEPTION);
389
    }
390
391
    /**
392
     * Main method, is called by phpbu command and the phar file.
393
     */
394
    public static function main()
395
    {
396
        $app = new static();
397
        $app->run($_SERVER['argv']);
398
    }
399
}
400