Completed
Push — master ( 1e5783...b9ed2d )
by Sebastian
02:56
created

Cmd::run()   B

Complexity

Conditions 4
Paths 6

Size

Total Lines 26
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

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