Completed
Push — master ( 39c07f...aa04c1 )
by Jitendra
11s
created

UpdateCommand::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 14
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 10
nc 1
nop 0
dl 0
loc 14
rs 9.9332
c 0
b 0
f 0

1 Method

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

88
        /** @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...
89
90
        $this->emit('_exit');
91
    }
92
93
    protected function updateTo(string $version, int $size)
94
    {
95
        $io = $this->app()->io();
96
97
        $currentPhar  = $this->getPharPathFor(null);
98
        $versionPhar  = $this->getPharPathFor($version);
99
        $sourceUrl    = \str_replace('{version}', $version, static::PHAR_URL);
100
101
        $io->bold("Downloading phar $version ...", true);
102
103
        // Create new $version phar
104
        $saved = @\file_put_contents($versionPhar, \shell_exec("curl -sSL $sourceUrl"));
105
106
        if ($saved < $size) {
107
            @\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

107
            /** @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...
108
109
            throw new RuntimeException("Couldnt download the phar for $version");
110
        }
111
112
        try {
113
            @\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

113
            /** @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...
114
115
            if (!\ini_get('phar.readonly')) {
116
                $phar = new \Phar($versionPhar);
117
                unset($phar);
118
            }
119
120
            // Take backup of current
121
            @\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

121
            /** @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...
122
123
            // Replace current with new $version
124
            @\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

124
            /** @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...
125
126
            $io->ok('Done', true);
127
        } catch (\Throwable $e) {
128
            $io->error('Couldnt update to ' . $version, true);
129
        }
130
    }
131
132
    protected function getPharPathFor(string $version = null): string
133
    {
134
        if (false === $thisPhint = \realpath($_SERVER['argv'][0])) {
135
            $thisPhint = $this->_pathUtil->getPhintPath('phint.phar');
136
        }
137
138
        if (empty($thisPhint)) {
139
            throw new RuntimeException('Couldnt locate phint path, make sure you have HOME in the env vars');
140
        }
141
142
        if (empty($version)) {
143
            return $thisPhint;
144
        }
145
146
        $pathTemplate = '%s.%s.phar';
147
148
        return \sprintf($pathTemplate, \str_replace('.phar', '', $thisPhint), $version);
149
    }
150
}
151