Failed Conditions
Pull Request — master (#8)
by Mateusz
05:50 queued 01:37
created

Installer::validateOptions()   B

Complexity

Conditions 5
Paths 4

Size

Total Lines 22
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 13.7937

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 22
ccs 5
cts 17
cp 0.2941
rs 8.6738
cc 5
eloc 13
nc 4
nop 0
crap 13.7937
1
<?php
2
3
/*
4
 * This file is part of the puli/installer package.
5
 *
6
 * (c) Bernhard Schussek <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace Puli\Installer;
13
14
use Exception;
15
use Humbug\SelfUpdate\VersionParser;
16
use Phar;
17
use PharException;
18
use RuntimeException;
19
use UnexpectedValueException;
20
21
/**
22
 * Installs the puli.phar on the local system.
23
 *
24
 * Use it like this:
25
 *
26
 * ```
27
 * $ curl -sS https://puli.io/installer | php
28
 * ```
29
 *
30
 * This file was adapted from the installer file bundled with Composer. For the
31
 * original file, authors and copyright information see
32
 *
33
 * https://github.com/composer/getcomposer.org/blob/master/web/installer
34
 *
35
 * @author Nils Adermann <[email protected]>
36
 * @author Jordi Boggiano <[email protected]>
37
 * @author Thomas Rudolph <[email protected]>
38
 * @author Bernhard Schussek <[email protected]>
39
 */
40
class Installer
41
{
42
    /**
43
     * The help text of the installer.
44
     */
45
    const HELP_TEXT = <<<HELP
46
Puli Installer
47
--------------
48
Options
49
--help               this help
50
--check              for checking environment only
51
--force              forces the installation even if the system requirements are not satisfied
52
--ansi               force ANSI color output
53
--no-ansi            disable ANSI color output
54
--quiet              do not output unimportant messages
55
--install-dir="..."  set the target installation directory
56
--version="..."      install a specific version
57
--filename="..."     set the target filename (default: puli.phar)
58
--disable-tls        disable SSL/TLS security for file downloads
59
60
HELP;
61
62
    /**
63
     * The api url to determine the available versions of puli.
64
     */
65
    const VERSION_API_URL = '%s://puli.io/download/versions.json';
66
67
    /**
68
     * The phar download url.
69
     */
70
    const PHAR_DOWNLOAD_URL = '%s://puli.io/download/%s/puli.phar';
71
72
    /**
73
     * @var string
74
     */
75
    private $stability;
76
77
    /**
78
     * @var bool
79
     */
80
    private $check;
81
82
    /**
83
     * @var bool
84
     */
85
    private $help;
86
87
    /**
88
     * @var bool
89
     */
90
    private $force;
91
92
    /**
93
     * @var bool
94
     */
95
    private $quiet;
96
97
    /**
98
     * @var bool
99
     */
100
    private $disableTls;
101
102
    /**
103
     * @var string
104
     */
105
    private $installDir;
106
107
    /**
108
     * @var string
109
     */
110
    private $version;
111
112
    /**
113
     * @var string
114
     */
115
    private $filename;
116
117
    /**
118
     * Runs the installer.
119
     *
120
     * @param array $argv The console arguments.
121
     *
122
     * @return int The return status.
123
     */
124 5
    public function run(array $argv)
125
    {
126 5
        $this->parseOptions($argv);
127
128 5
        if ($this->help) {
129
            echo self::HELP_TEXT;
130
131
            return 0;
132
        }
133
134 5
        $ok = $this->validateSystem() && $this->validateOptions();
135
136 5
        if ($this->disableTls) {
137
            $this->info(
138
                'You have instructed the Installer not to enforce SSL/TLS '.
139
                'security on remote HTTPS requests.'.PHP_EOL.
140
                'This will leave all downloads during installation vulnerable '.
141
                'to Man-In-The-Middle (MITM) attacks.'
142
            );
143
        }
144
145 5
        if ($this->check) {
146
            return $ok ? 0 : 1;
147
        }
148
149 5
        if ($ok || $this->force) {
150 5
            return $this->installPuli();
151
        }
152
153
        return 1;
154
    }
155
156
    /**
157
     * Installs puli to the current working directory.
158
     *
159
     * @return int The return status.
160
     *
161
     * @throws Exception
162
     */
163 5
    private function installPuli()
164
    {
165 5
        $workingDir = str_replace('\\', '/', getcwd());
166 5
        $installDir = is_dir($this->installDir) ? realpath($this->installDir) : $workingDir;
167 5
        $installPath = str_replace('\\', '/', $installDir).'/'.$this->filename;
168 5
        $shortInstallPath = $installPath;
169
170
        // Strip the current working directory if possible
171 5
        if (0 === strpos($shortInstallPath, $workingDir.'/')) {
172 3
            $shortInstallPath = substr($shortInstallPath, strlen($workingDir.'/'));
173 3
        }
174
175 5
        if (is_readable($installPath)) {
176
            @unlink($installPath);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
177
        }
178
179 5
        $httpClient = new HttpClient($this->disableTls);
180
181 5
        $versionUrl = sprintf(
182 5
            static::VERSION_API_URL,
183 5
            $this->disableTls ? 'http' : 'https'
184 5
        );
185
186 5
        $versions = array();
187 5
        for ($retries = 3; $retries > 0; --$retries) {
188 5
            if (!$this->quiet) {
189 5
                $this->info('Downloading available versions...');
190 5
            }
191
192
            try {
193 5
                $versions = $this->downloadVersions($httpClient, $versionUrl);
194 5
                break;
195
            } catch (RuntimeException $e) {
196
                $this->error($e->getMessage());
197
            }
198
        }
199
200 5
        if (0 === $retries || empty($versions)) {
201
            $this->error('fatal: The download failed repeatedly, aborting.');
202
203
            return 1;
204
        }
205
206 5
        $versionParser = new VersionParser($versions);
207 5
        if (!empty($this->version)) {
208 1
            if (!in_array($this->version, $versions, true)) {
209
                $this->error(sprintf(
210
                    'fatal: Could not find version: %s.',
211
                    $this->version
212
                ));
213
214
                return 1;
215
            }
216 5
        } elseif ('stable' === $this->stability) {
217 1
            $this->version = $versionParser->getMostRecentStable();
218 1
            if (false === $this->version) {
219 1
                $this->error('fatal: Could not find a stable version.');
220
221 1
                return 1;
222
            }
223
        } else {
224 3
            $this->version = $versionParser->getMostRecentAll();
225
        }
226
227 4
        $url = sprintf(
228 4
            static::PHAR_DOWNLOAD_URL,
229 4
            $this->disableTls ? 'http' : 'https',
230 4
            $this->version
231 4
        );
232
233 4
        for ($retries = 3; $retries > 0; --$retries) {
234 4
            if (!$this->quiet) {
235 4
                $this->info(sprintf(
236 4
                    'Downloading puli.phar at version %s...',
237 4
                    $this->version)
238 4
                );
239 4
            }
240
241 4
            if (!$this->downloadFile($httpClient, $url, $installPath)) {
242
                continue;
243
            }
244
245
            try {
246 4
                $this->validatePhar($installPath);
247 4
            } catch (Exception $e) {
248
                unlink($installPath);
249
250
                if (!$e instanceof UnexpectedValueException && !$e instanceof PharException) {
251
                    throw $e;
252
                }
253
254
                if ($retries > 0) {
255
                    if (!$this->quiet) {
256
                        $this->error('The download is corrupt, retrying...');
257
                    }
258
                } else {
259
                    $this->error(sprintf(
260
                        'fatal: The download is corrupt (%s), aborting.',
261
                        $e->getMessage()
262
                    ));
263
264
                    return 1;
265
                }
266
            }
267
268 4
            break;
269
        }
270
271 4
        if (0 === $retries) {
272
            $this->error('fatal: The download failed repeatedly, aborting.');
273
274
            return 1;
275
        }
276
277 4
        chmod($installPath, 0755);
278
279 4
        if (!$this->quiet) {
280 4
            $this->success(PHP_EOL.'Puli successfully installed to: '.$installPath);
281 4
            $this->info('Use it: php '.$shortInstallPath);
282 4
        }
283
284 4
        return 0;
285
    }
286
287
    /**
288
     * Downloads a URL to a path.
289
     *
290
     * @param HttpClient $httpClient The client to use.
291
     * @param string     $url        The URL to download.
292
     * @param string     $targetPath The path to download the URL to.
293
     *
294
     * @return bool Whether the download completed successfully.
295
     */
296 4
    private function downloadFile(HttpClient $httpClient, $url, $targetPath)
297
    {
298 4
        ErrorHandler::register();
299
300 4
        $fh = fopen($targetPath, 'w');
301
302 4
        if (!$fh) {
303
            $this->error(sprintf(
304
                'Could not create file %s: %s',
305
                $targetPath,
306
                implode(PHP_EOL, ErrorHandler::getErrors())
307
            ));
308
        }
309
310 4
        if (!fwrite($fh, $httpClient->download($url))) {
311
            $this->error(sprintf(
312
                'Download failed: %s',
313
                implode(PHP_EOL, ErrorHandler::getErrors())
314
            ));
315
        }
316
317 4
        fclose($fh);
318
319 4
        ErrorHandler::unregister();
320
321 4
        return !ErrorHandler::hasErrors();
322
    }
323
324
    /**
325
     * Downloads the available puli versions.
326
     *
327
     * @param HttpClient $httpClient The client to use.
328
     * @param string     $url        The URL to download.
329
     *
330
     * @return array The available versions, null if the download failed.
331
     *
332
     * @throws RuntimeException If an error occurs.
333
     */
334 5
    public function downloadVersions(HttpClient $httpClient, $url)
335
    {
336 5
        ErrorHandler::register();
337 5
        $versions = $httpClient->download($url);
338 5
        ErrorHandler::unregister();
339
340 5
        if (ErrorHandler::hasErrors()) {
341
            throw new RuntimeException(sprintf(
342
                'Could not download %s:'.PHP_EOL.'%s',
343
                $url,
344
                implode(PHP_EOL, ErrorHandler::getErrors())
345
            ));
346
        }
347
348 5
        $versions = json_decode($versions);
349 5
        if (json_last_error() !== JSON_ERROR_NONE || !is_array($versions)) {
350
            throw new RuntimeException(sprintf(
351
                'Could not download %s:'.PHP_EOL.'Malformed JSON returned.',
352
                $url
353
            ));
354
        }
355
356 5
        usort($versions, 'version_compare');
357
358 5
        return $versions;
359
    }
360
361
    /**
362
     * Validates that the given path is a valid PHAR file.
363
     *
364
     * @param string $path A path to a PHAR file.
365
     *
366
     * @throws Exception If an error occurs.
367
     */
368 4
    private function validatePhar($path)
369
    {
370 4
        $tmpFile = $path;
371
372
        try {
373
            // create a temp file ending in .phar since the Phar class only accepts that
374 4
            if ('.phar' !== substr($path, -5)) {
375
                copy($path, $path.'.tmp.phar');
376
                $tmpFile = $path.'.tmp.phar';
377
            }
378
379 4
            if (!ini_get('phar.readonly')) {
380
                // test the phar validity
381 4
                $phar = new Phar($tmpFile);
382
383
                // free the variable to unlock the file
384 4
                unset($phar);
385 4
            }
386 4
        } catch (Exception $e) {
387
            // clean up temp file if needed
388
            if ($path !== $tmpFile) {
389
                unlink($tmpFile);
390
            }
391
392
            throw $e;
393
        }
394
395
        // clean up temp file if needed
396 4
        if ($path !== $tmpFile) {
397
            unlink($tmpFile);
398
        }
399 4
    }
400
401
    /**
402
     * Check the platform for possible issues on running Puli.
403
     *
404
     * @return bool Whether the platform requirements are satisfied.
405
     */
406 5
    private function validateSystem()
407
    {
408 5
        $errors = array();
409 5
        $warnings = array();
410
411 5
        $iniPath = php_ini_loaded_file();
412 5
        $displayIniMessage = false;
413
414 5
        if ($iniPath) {
415 5
            $iniMessage = PHP_EOL.PHP_EOL.'The php.ini used by your command-line PHP is: '.$iniPath;
416 5
        } else {
417
            $iniMessage = PHP_EOL.PHP_EOL.'A php.ini file does not exist. You will have to create one.';
418
        }
419
420 5
        $iniMessage .= PHP_EOL.'If you can not modify the ini file, you can also run `php -d option=value` to modify ini values on the fly. You can use -d multiple times.';
421
422 5
        if (ini_get('detect_unicode')) {
423
            $errors['unicode'] = 'On';
424
        }
425
426 5
        if (extension_loaded('suhosin')) {
427
            $suhosin = ini_get('suhosin.executor.include.whitelist');
428
            $suhosinBlacklist = ini_get('suhosin.executor.include.blacklist');
429
            if (false === stripos($suhosin,
430
                    'phar') && (!$suhosinBlacklist || false !== stripos($suhosinBlacklist,
431
                        'phar'))
432
            ) {
433
                $errors['suhosin'] = $suhosin;
434
            }
435
        }
436
437 5
        if (!function_exists('json_decode')) {
438
            $errors['json'] = true;
439
        }
440
441 5
        if (!extension_loaded('Phar')) {
442
            $errors['phar'] = true;
443
        }
444
445 5
        if (!ini_get('allow_url_fopen')) {
446
            $errors['allow_url_fopen'] = true;
447
        }
448
449 5
        if (extension_loaded('ionCube Loader') && ioncube_loader_iversion() < 40009) {
450
            $errors['ioncube'] = ioncube_loader_version();
451
        }
452
453 5
        if (version_compare(PHP_VERSION, '5.3.9', '<')) {
454
            $errors['php'] = PHP_VERSION;
455
        }
456
457 5
        if (!extension_loaded('openssl') && $this->disableTls) {
458
            $warnings['openssl'] = true;
459 5
        } elseif (!extension_loaded('openssl')) {
460
            $errors['openssl'] = true;
461
        }
462
463 5
        if (!defined('HHVM_VERSION') && !extension_loaded('apcu') && ini_get('apc.enable_cli')) {
464
            $warnings['apc_cli'] = true;
465
        }
466
467 5
        ob_start();
468 5
        phpinfo(INFO_GENERAL);
469 5
        $phpinfo = ob_get_clean();
470 5
        if (preg_match('{Configure Command(?: *</td><td class="v">| *=> *)(.*?)(?:</td>|$)}m',
471 5
            $phpinfo, $match)) {
472 5
            $configure = $match[1];
473
474 5
            if (false !== strpos($configure, '--enable-sigchild')) {
475
                $warnings['sigchild'] = true;
476
            }
477
478 5
            if (false !== strpos($configure, '--with-curlwrappers')) {
479
                $warnings['curlwrappers'] = true;
480
            }
481 5
        }
482
483 5
        if (!empty($errors)) {
484
            $this->error('Some settings on your machine make Puli unable to work properly.');
485
            $this->error('Make sure that you fix the issues listed below and run this script again:');
486
487
            foreach ($errors as $error => $current) {
488
                $text = '';
489
                switch ($error) {
490
                    case 'json':
491
                        $text = PHP_EOL.'The json extension is missing.'.PHP_EOL;
492
                        $text .= 'Install it or recompile php without --disable-json';
493
                        break;
494
495
                    case 'phar':
496
                        $text = PHP_EOL.'The phar extension is missing.'.PHP_EOL;
497
                        $text .= 'Install it or recompile php without --disable-phar';
498
                        break;
499
500 View Code Duplication
                    case 'unicode':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
501
                        $text = PHP_EOL.'The detect_unicode setting must be disabled.'.PHP_EOL;
502
                        $text .= 'Add the following to the end of your `php.ini`:'.PHP_EOL;
503
                        $text .= '    detect_unicode = Off';
504
                        $displayIniMessage = true;
505
                        break;
506
507 View Code Duplication
                    case 'suhosin':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
508
                        $text = PHP_EOL.'The suhosin.executor.include.whitelist setting is incorrect.'.PHP_EOL;
509
                        $text .= 'Add the following to the end of your `php.ini` or suhosin.ini (Example path [for Debian]: /etc/php5/cli/conf.d/suhosin.ini):'.PHP_EOL;
510
                        $text .= '    suhosin.executor.include.whitelist = phar '.$current;
511
                        $displayIniMessage = true;
512
                        break;
513
514
                    case 'php':
515
                        $text = PHP_EOL."Your PHP ({$current}) is too old, you must upgrade to PHP 5.3.9 or higher.";
0 ignored issues
show
Coding Style Best Practice introduced by
As per coding-style, please use concatenation or sprintf for the variable $current instead of interpolation.

It is generally a best practice as it is often more readable to use concatenation instead of interpolation for variables inside strings.

// Instead of
$x = "foo $bar $baz";

// Better use either
$x = "foo " . $bar . " " . $baz;
$x = sprintf("foo %s %s", $bar, $baz);
Loading history...
516
                        break;
517
518 View Code Duplication
                    case 'allow_url_fopen':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
519
                        $text = PHP_EOL.'The allow_url_fopen setting is incorrect.'.PHP_EOL;
520
                        $text .= 'Add the following to the end of your `php.ini`:'.PHP_EOL;
521
                        $text .= '    allow_url_fopen = On';
522
                        $displayIniMessage = true;
523
                        break;
524
525 View Code Duplication
                    case 'ioncube':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
526
                        $text = PHP_EOL."Your ionCube Loader extension ($current) is incompatible with Phar files.".PHP_EOL;
0 ignored issues
show
Coding Style Best Practice introduced by
As per coding-style, please use concatenation or sprintf for the variable $current instead of interpolation.

It is generally a best practice as it is often more readable to use concatenation instead of interpolation for variables inside strings.

// Instead of
$x = "foo $bar $baz";

// Better use either
$x = "foo " . $bar . " " . $baz;
$x = sprintf("foo %s %s", $bar, $baz);
Loading history...
527
                        $text .= 'Upgrade to ionCube 4.0.9 or higher or remove this line (path may be different) from your `php.ini` to disable it:'.PHP_EOL;
528
                        $text .= '    zend_extension = /usr/lib/php5/20090626+lfs/ioncube_loader_lin_5.3.so';
529
                        $displayIniMessage = true;
530
                        break;
531
532
                    case 'openssl':
533
                        $text = PHP_EOL.'The openssl extension is missing, which means that secure HTTPS transfers are impossible.'.PHP_EOL;
534
                        $text .= 'If possible you should enable it or recompile php with --with-openssl';
535
                        break;
536
                }
537
538
                if ($displayIniMessage) {
539
                    $text .= $iniMessage;
540
                }
541
542
                $this->info($text);
543
            }
544
545
            echo PHP_EOL;
546
547
            return false;
548
        }
549
550 5
        if (!empty($warnings)) {
551
            $this->error('Some settings on your machine may cause stability issues with Puli.');
552
            $this->error('If you encounter issues, try to change the following:');
553
554
            foreach ($warnings as $warning => $current) {
555
                $text = '';
556
                switch ($warning) {
557 View Code Duplication
                    case 'apc_cli':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
558
                        $text = PHP_EOL.'The apc.enable_cli setting is incorrect.'.PHP_EOL;
559
                        $text .= 'Add the following to the end of your `php.ini`:'.PHP_EOL;
560
                        $text .= '    apc.enable_cli = Off';
561
                        $displayIniMessage = true;
562
                        break;
563
564
                    case 'sigchild':
565
                        $text = PHP_EOL.'PHP was compiled with --enable-sigchild which can cause issues on some platforms.'.PHP_EOL;
566
                        $text .= 'Recompile it without this flag if possible, see also:'.PHP_EOL;
567
                        $text .= '    https://bugs.php.net/bug.php?id=22999';
568
                        break;
569
570
                    case 'curlwrappers':
571
                        $text = PHP_EOL.'PHP was compiled with --with-curlwrappers which will cause issues with HTTP authentication and GitHub.'.PHP_EOL;
572
                        $text .= 'Recompile it without this flag if possible';
573
                        break;
574
575
                    case 'openssl':
576
                        $text = PHP_EOL.'The openssl extension is missing, which means that secure HTTPS transfers are impossible.'.PHP_EOL;
577
                        $text .= 'If possible you should enable it or recompile php with --with-openssl';
578
                        break;
579
                }
580
                if ($displayIniMessage) {
581
                    $text .= $iniMessage;
582
                }
583
                $this->info($text);
584
            }
585
586
            echo PHP_EOL;
587
588
            return true;
589
        }
590
591 5
        if (!$this->quiet) {
592 5
            $this->success('All settings correct for using Puli');
593 5
        }
594
595 5
        return true;
596
    }
597
598
    /**
599
     * Validate whether the passed command line options are correct.
600
     *
601
     * @return bool Returns `true` if the options are valid and `false` otherwise.
602
     */
603 5
    private function validateOptions()
604
    {
605 5
        $ok = true;
606
607 5
        if (false !== $this->installDir && !is_dir($this->installDir)) {
608
            $this->info(sprintf(
609
                'The defined install dir (%s) does not exist.',
610
                $this->installDir
611
            ));
612
            $ok = false;
613
        }
614
615 5
        if (false !== $this->version && 1 !== preg_match('/^\d+\.\d+\.\d+(\-(alpha|beta)\d+)*$/', $this->version)) {
616
            $this->info(sprintf(
617
                'The defined install version (%s) does not match release pattern.',
618
                $this->version
619
            ));
620
            $ok = false;
621
        }
622
623 5
        return $ok;
624
    }
625
626
    /**
627
     * Parses the command line options.
628
     *
629
     * @param string[] $argv The command line options.
630
     */
631 5
    private function parseOptions(array $argv)
632
    {
633 5
        $this->check = in_array('--check', $argv);
634 5
        $this->help = in_array('--help', $argv);
635 5
        $this->force = in_array('--force', $argv);
636 5
        $this->quiet = in_array('--quiet', $argv);
637 5
        $this->disableTls = in_array('--disable-tls', $argv);
638 5
        $this->installDir = false;
0 ignored issues
show
Documentation Bug introduced by
The property $installDir was declared of type string, but false is of type false. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
639 5
        $this->version = false;
0 ignored issues
show
Documentation Bug introduced by
The property $version was declared of type string, but false is of type false. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
640 5
        $this->filename = 'puli.phar';
641 5
        $this->stability = 'unstable';
642 5
        if (in_array('--stable', $argv)) {
643 1
            $this->stability = 'stable';
644 1
        }
645
646
        // --no-ansi wins over --ansi
647 5
        if (in_array('--no-ansi', $argv)) {
648 4
            define('USE_ANSI', false);
649 5
        } elseif (in_array('--ansi', $argv)) {
650
            define('USE_ANSI', true);
651
        } else {
652
            // On Windows, default to no ANSI, except in ANSICON and ConEmu.
653
            // Everywhere else, default to ANSI if stdout is a terminal.
654 1
            define('USE_ANSI', ('\\' === DIRECTORY_SEPARATOR)
655 1
                ? (false !== getenv('ANSICON') || 'ON' === getenv('ConEmuANSI'))
656 1
                : (function_exists('posix_isatty') && posix_isatty(1)));
657
        }
658
659 5
        foreach ($argv as $key => $val) {
660 5 View Code Duplication
            if (0 === strpos($val, '--install-dir')) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
661 2
                if (13 === strlen($val) && isset($argv[$key + 1])) {
662 2
                    $this->installDir = trim($argv[$key + 1]);
663 2
                } else {
664
                    $this->installDir = trim(substr($val, 14));
665
                }
666 2
            }
667
668 5 View Code Duplication
            if (0 === strpos($val, '--version')) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
669 1
                if (9 === strlen($val) && isset($argv[$key + 1])) {
670 1
                    $this->version = trim($argv[$key + 1]);
671 1
                } else {
672
                    $this->version = trim(substr($val, 10));
673
                }
674 1
            }
675
676 5 View Code Duplication
            if (0 === strpos($val, '--filename')) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
677
                if (10 === strlen($val) && isset($argv[$key + 1])) {
678
                    $this->filename = trim($argv[$key + 1]);
679
                } else {
680
                    $this->filename = trim(substr($val, 11));
681
                }
682
            }
683 5
        }
684 5
    }
685
686
    /**
687
     * Prints output indicating a success.
688
     *
689
     * @param string $text The text to print.
690
     */
691 5
    private function success($text)
692
    {
693 5
        printf(USE_ANSI ? "\033[0;32m%s\033[0m" : '%s', $text.PHP_EOL);
694 5
    }
695
696
    /**
697
     * Prints output indicating an error.
698
     *
699
     * @param string $text The text to print.
700
     */
701 1
    private function error($text)
702
    {
703 1
        printf(USE_ANSI ? "\033[31;31m%s\033[0m" : '%s', $text.PHP_EOL);
704 1
    }
705
706
    /**
707
     * Prints output indicating some information.
708
     *
709
     * @param string $text The text to print.
710
     */
711 5
    private function info($text)
712
    {
713 5
        printf(USE_ANSI ? "\033[33;33m%s\033[0m" : '%s', $text.PHP_EOL);
714 5
    }
715
}
716