Completed
Pull Request — master (#17)
by Jitendra
03:59
created

UpdateCommand   A

Complexity

Total Complexity 15

Size/Duplication

Total Lines 123
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 58
dl 0
loc 123
rs 10
c 0
b 0
f 0
wmc 15

5 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 11 1
A rollback() 0 22 3
A updateTo() 0 34 4
A execute() 0 23 5
A getPharPathFor() 0 11 2
1
<?php
2
3
namespace Ahc\Phint\Console;
4
5
use Ahc\Cli\Input\Command;
6
use Ahc\Cli\IO\Interactor;
7
use Ahc\Phint\Generator\CollisionHandler;
8
use Ahc\Phint\Generator\TwigGenerator;
9
use Ahc\Phint\Util\Composer;
10
use Ahc\Phint\Util\Git;
11
use Ahc\Phint\Util\Inflector;
12
use Ahc\Phint\Util\Path;
13
14
/**
15
 * Some ideas related to phar taken from `composer selfupdate`.
16
 */
17
class UpdateCommand extends Command
18
{
19
    const PHAR_URL = 'https://github.com/adhocore/phint/releases/download/{version}/phint.phar';
20
21
    public function __construct()
22
    {
23
        parent::__construct('update', 'Update Phint to lastest version');
24
25
        $this
26
            ->option('-r --rollback', 'Rollback to earlier version', 'boolval', false)->on([$this, 'rollback'])
27
            ->usage(
28
                '<bold>  phint update</end>        Updates to latest version<eol/>' .
29
                '<bold>  phint u</end>             Also updates to latest version<eol/>' .
30
                '<bold>  phint update</end> <comment>-r</end>     Rolls back to prev version<eol/>' .
31
                '<bold>  phint u</end> <comment>--rollback</end>  Also rolls back to prev version<eol/>'
32
            );
33
    }
34
35
    /**
36
     * Execute the command action.
37
     */
38
    public function execute()
39
    {
40
        $io = $this->app()->io();
41
        $io->bold('Fetching latest version ...', true);
42
43
        $release = \shell_exec('curl -sSL https://api.github.com/repos/adhocore/phint/releases/latest');
44
        $release = \json_decode($release);
45
46
        if (\JSON_ERROR_NONE !== \json_last_error() || empty($release->assets[0])) {
47
            $io->bgRed('Error getting latest release', true);
48
49
            return;
50
        }
51
52
        $latest = $release->tag_name;
53
54
        if (!\version_compare($this->_version, $latest, '<')) {
55
            $io->bgGreen('You seem to have latest version already', true);
56
        } else {
57
            $this->updateTo($latest, $release->assets[0]->size);
58
59
            if (\is_file($this->getPharPathFor(null) . '.old')) {
60
                $io->colors('You can run <comment>phint selfupdate --rollback</end> to revert<eol/>');
61
            }
62
        }
63
    }
64
65
    /**
66
     * Perform rollback.
67
     */
68
    public function rollback()
69
    {
70
        $io = $this->app()->io();
71
72
        $io->bold('Rolling back to earlier version ...', true);
73
74
        $thisPhar = $this->getPharPathFor(null);
75
        $oldPhar  = $thisPhar . '.old';
76
77
        if (!\is_file($oldPhar)) {
78
            throw new \RuntimeException('No old version locally available');
79
        }
80
81
        $oldPerms = \fileperms($thisPhar);
82
83
        if (@\rename($oldPhar, $thisPhar)) {
84
            $io->ok('Done', true);
85
        }
86
87
        @\chmod($thisPhar, $oldPerms);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for chmod(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

87
        /** @scrutinizer ignore-unhandled */ @\chmod($thisPhar, $oldPerms);

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...
88
89
        $this->emit('_exit');
90
    }
91
92
    protected function updateTo(string $version, int $size)
93
    {
94
        $io = $this->app()->io();
95
96
        $currentPhar  = $this->getPharPathFor(null);
97
        $versionPhar  = $this->getPharPathFor($version);
98
        $sourceUrl    = \str_replace('{version}', $version, static::PHAR_URL);
99
100
        // Create new $version phar
101
        $saved = @\file_put_contents($versionPhar, \shell_exec("curl -sSL $sourceUrl"));
102
103
        if ($saved < $size) {
104
            @\unlink($versionPhar);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for unlink(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

104
            /** @scrutinizer ignore-unhandled */ @\unlink($versionPhar);

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...
105
106
            throw new \RuntimeException("Couldnt download the phar for $version");
107
        }
108
109
        try {
110
            @\chmod($versionPhar, \fileperms($currentPhar));
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for chmod(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

110
            /** @scrutinizer ignore-unhandled */ @\chmod($versionPhar, \fileperms($currentPhar));

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...
111
112
            if (!\ini_get('phar.readonly')) {
113
                $phar = new \Phar($versionPhar);
114
                unset($phar);
115
            }
116
117
            // Take backup of current
118
            @\copy($currentPhar, $currentPhar . '.old');
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for copy(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

118
            /** @scrutinizer ignore-unhandled */ @\copy($currentPhar, $currentPhar . '.old');

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...
119
120
            // Replace current with new $version
121
            @\rename($versionPhar, $currentPhar);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for rename(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

121
            /** @scrutinizer ignore-unhandled */ @\rename($versionPhar, $currentPhar);

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...
122
123
            $io->ok('Done', true);
124
        } catch (\Throwable $e) {
125
            $io->error('Couldnt update to ' . $version, true);
126
        }
127
    }
128
129
    protected function getPharPathFor(string $version = null): string
130
    {
131
        $thisPhint = $_SERVER['argv'][0];
132
133
        if ($version === null) {
134
            return $thisPhint;
135
        }
136
137
        $pathTemplate = "%s.%s.phar";
138
139
        return \sprintf($pathTemplate, \str_replace('.phar', '', $thisPhint), $version);
140
    }
141
}
142