SelfUpdateCommand   A
last analyzed

Complexity

Total Complexity 17

Size/Duplication

Total Lines 140
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 6

Importance

Changes 4
Bugs 1 Features 0
Metric Value
wmc 17
c 4
b 1
f 0
lcom 1
cbo 6
dl 0
loc 140
rs 10

4 Methods

Rating   Name   Duplication   Size   Complexity  
A configure() 0 18 1
D execute() 0 97 14
A isEnabled() 0 4 1
A _exit() 0 4 1
1
<?php
2
3
namespace N98\Magento\Command;
4
5
use Composer\Downloader\FilesystemException;
6
use Composer\IO\ConsoleIO;
7
use Composer\Util\RemoteFilesystem;
8
use N98\Magento\Command\AbstractMagentoCommand;
9
use Symfony\Component\Console\Input\InputInterface;
10
use Symfony\Component\Console\Input\InputOption;
11
use Symfony\Component\Console\Output\OutputInterface;
12
13
/**
14
 * @codeCoverageIgnore
15
 * @author Igor Wiedler <[email protected]>
16
 * @author Christian Münch <[email protected]>
17
 */
18
class SelfUpdateCommand extends AbstractMagentoCommand
19
{
20
    protected function configure()
21
    {
22
        $this
23
            ->setName('self-update')
24
            ->setAliases(array('selfupdate'))
25
            ->addOption('unstable', null, InputOption::VALUE_NONE, 'Load unstable version from develop branch')
26
            ->setDescription('Updates n98-magerun.phar to the latest version.')
27
            ->setHelp(
28
<<<EOT
29
The <info>self-update</info> command checks github for newer
30
versions of n98-magerun and if found, installs the latest.
31
32
<info>php n98-magerun.phar self-update</info>
33
34
EOT
35
            )
36
        ;
37
    }
38
39
    /**
40
     * @return bool
41
     */
42
    public function isEnabled()
43
    {
44
        return $this->getApplication()->isPharMode();
45
    }
46
47
    protected function execute(InputInterface $input, OutputInterface $output)
0 ignored issues
show
Coding Style introduced by
execute uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
48
    {
49
        $localFilename = realpath($_SERVER['argv'][0]) ?: $_SERVER['argv'][0];
50
        $tempFilename = dirname($localFilename) . '/' . basename($localFilename, '.phar') . '-temp.phar';
51
52
        // check for permissions in local filesystem before start connection process
53
        if (!is_writable($tempDirectory = dirname($tempFilename))) {
54
            throw new FilesystemException(
55
                'n98-magerun2 update failed: the "' . $tempDirectory .
56
                '" directory used to download the temp file could not be written'
57
            );
58
        }
59
60
        if (!is_writable($localFilename)) {
61
            throw new FilesystemException(
62
                'n98-magerun2 update failed: the "' . $localFilename . '" file could not be written'
63
            );
64
        }
65
66
        $io = new ConsoleIO($input, $output, $this->getHelperSet());
0 ignored issues
show
Bug introduced by
It seems like $this->getHelperSet() can be null; however, __construct() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
67
        $rfs = new RemoteFilesystem($io);
68
69
        $loadUnstable = $input->getOption('unstable');
70
        if ($loadUnstable) {
71
            $versionTxtUrl = 'https://raw.githubusercontent.com/netz98/n98-magerun2/develop/version.txt';
72
            $remoteFilename = 'https://files.magerun.net/n98-magerun2-dev.phar';
73
        } else {
74
            $versionTxtUrl = 'https://raw.githubusercontent.com/netz98/n98-magerun2/master/version.txt';
75
            $remoteFilename = 'https://files.magerun.net/n98-magerun2.phar';
76
        }
77
78
        $latest = trim($rfs->getContents('raw.githubusercontent.com', $versionTxtUrl, false));
79
80
        if ($this->getApplication()->getVersion() !== $latest || $loadUnstable) {
81
            $output->writeln(sprintf("Updating to version <info>%s</info>.", $latest));
82
83
            $rfs->copy('raw.github.com', $remoteFilename, $tempFilename);
84
85
            if (!file_exists($tempFilename)) {
86
                $output->writeln('<error>The download of the new n98-magerun version failed for an unexpected reason');
87
88
                return 1;
89
            }
90
91
            try {
92
                \error_reporting(E_ALL); // supress notices
93
94
                @chmod($tempFilename, 0777 & ~umask());
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...
95
                // test the phar validity
96
                $phar = new \Phar($tempFilename);
97
                // free the variable to unlock the file
98
                unset($phar);
99
                @rename($tempFilename, $localFilename);
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...
100
                $output->writeln('<info>Successfully updated n98-magerun2</info>');
101
102
                if ($loadUnstable) {
103
                    $changeLogContent = $rfs->getContents(
104
                        'raw.github.com',
105
                        'https://raw.github.com/netz98/n98-magerun2/develop/CHANGELOG.md',
106
                        false
107
                    );
108
                } else {
109
                    $changeLogContent = $rfs->getContents(
110
                        'raw.github.com',
111
                        'https://raw.github.com/netz98/n98-magerun2/master/CHANGELOG.md',
112
                        false
113
                    );
114
                }
115
116
                if ($changeLogContent) {
117
                    $output->writeln($changeLogContent);
0 ignored issues
show
Bug introduced by
It seems like $changeLogContent can also be of type boolean; however, Symfony\Component\Consol...putInterface::writeln() does only seem to accept string|array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
118
                }
119
120
                if ($loadUnstable) {
121
                    $unstableFooterMessage = <<<UNSTABLE_FOOTER
122
<comment>
123
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
124
!! DEVELOPMENT VERSION. DO NOT USE IN PRODUCTION !!
125
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
126
</comment>
127
UNSTABLE_FOOTER;
128
                    $output->writeln($unstableFooterMessage);
129
                }
130
131
                $this->_exit();
132
            } catch (\Exception $e) {
133
                @unlink($tempFilename);
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...
134
                if (!$e instanceof \UnexpectedValueException && !$e instanceof \PharException) {
135
                    throw $e;
136
                }
137
                $output->writeln('<error>The download is corrupted (' . $e->getMessage() . ').</error>');
138
                $output->writeln('<error>Please re-run the self-update command to try again.</error>');
139
            }
140
        } else {
141
            $output->writeln("<info>You are using the latest n98-magerun version.</info>");
142
        }
143
    }
144
145
    /**
146
     * Stop execution
147
     *
148
     * This is a workaround to prevent warning of dispatcher after replacing
149
     * the phar file.
150
     *
151
     * @return void
152
     */
153
    protected function _exit()
154
    {
155
        exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method _exit() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
156
    }
157
}
158