SelfUpdateCommand::configure()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 0
dl 0
loc 17
rs 9.7
c 0
b 0
f 0
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 Exception;
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(<<<EOT
28
The <info>self-update</info> command checks github for newer
29
versions of n98-magerun and if found, installs the latest.
30
31
<info>php n98-magerun.phar self-update</info>
32
33
EOT
34
            )
35
        ;
36
    }
37
38
    /**
39
     * @return bool
40
     */
41
    public function isEnabled()
42
    {
43
        return $this->getApplication()->isPharMode();
44
    }
45
46
    /**
47
     * @param InputInterface  $input
48
     * @param OutputInterface $output
49
     *
50
     * @throws FilesystemException
51
     */
52
    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...
53
    {
54
        $localFilename = realpath($_SERVER['argv'][0]) ?: $_SERVER['argv'][0];
55
        $tempFilename = dirname($localFilename) . '/' . basename($localFilename, '.phar') . '-temp.phar';
56
57
        // check for permissions in local filesystem before start connection process
58
        if (!is_writable($tempDirectory = dirname($tempFilename))) {
59
            throw new FilesystemException(
60
                'n98-magerun update failed: the "' . $tempDirectory .
61
                '" directory used to download the temp file could not be written'
62
            );
63
        }
64
65
        if (!is_writable($localFilename)) {
66
            throw new FilesystemException(
67
                'n98-magerun update failed: the "' . $localFilename . '" file could not be written'
68
            );
69
        }
70
71
        $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...
72
        $rfs = new RemoteFilesystem($io);
73
74
        $loadUnstable = $input->getOption('unstable');
75
        if ($loadUnstable) {
76
            $versionTxtUrl = 'https://raw.githubusercontent.com/netz98/n98-magerun/develop/version.txt';
77
            $remoteFilename = 'https://files.magerun.net/n98-magerun-dev.phar';
78
        } else {
79
            $versionTxtUrl = 'https://raw.githubusercontent.com/netz98/n98-magerun/master/version.txt';
80
            $remoteFilename = 'https://files.magerun.net/n98-magerun.phar';
81
        }
82
83
        $latest = trim($rfs->getContents('raw.githubusercontent.com', $versionTxtUrl, false));
84
85
        if ($this->getApplication()->getVersion() !== $latest || $loadUnstable) {
86
            $output->writeln(sprintf("Updating to version <info>%s</info>.", $latest));
87
88
            $rfs->copy('raw.github.com', $remoteFilename, $tempFilename);
89
90
            if (!file_exists($tempFilename)) {
91
                $output->writeln('<error>The download of the new n98-magerun version failed for an unexpected reason');
92
93
                return 1;
94
            }
95
96
            try {
97
                \error_reporting(E_ALL); // supress notices
98
99
                @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...
100
                // test the phar validity
101
                $phar = new \Phar($tempFilename);
102
                // free the variable to unlock the file
103
                unset($phar);
104
                @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...
105
                $output->writeln('<info>Successfully updated n98-magerun</info>');
106
107
                if ($loadUnstable) {
108
                    $changeLogContent = $rfs->getContents(
109
                        'raw.github.com',
110
                        'https://raw.github.com/netz98/n98-magerun/develop/CHANGELOG.md',
111
                        false
112
                    );
113
                } else {
114
                    $changeLogContent = $rfs->getContents(
115
                        'raw.github.com',
116
                        'https://raw.github.com/netz98/n98-magerun/master/CHANGELOG.md',
117
                        false
118
                    );
119
                }
120
121
                if ($changeLogContent) {
122
                    $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...
123
                }
124
125
                if ($loadUnstable) {
126
                    $unstableFooterMessage = <<<UNSTABLE_FOOTER
127
<comment>
128
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
129
!! DEVELOPMENT VERSION. DO NOT USE IN PRODUCTION !!
130
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
131
</comment>
132
UNSTABLE_FOOTER;
133
                    $output->writeln($unstableFooterMessage);
134
                }
135
136
                $this->_exit();
137
            } catch (Exception $e) {
138
                @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...
139
                if (!$e instanceof \UnexpectedValueException && !$e instanceof \PharException) {
140
                    throw $e;
141
                }
142
                $output->writeln('<error>The download is corrupted (' . $e->getMessage() . ').</error>');
143
                $output->writeln('<error>Please re-run the self-update command to try again.</error>');
144
            }
145
        } else {
146
            $output->writeln("<info>You are using the latest n98-magerun version.</info>");
147
        }
148
    }
149
150
    /**
151
     * Stop execution
152
     *
153
     * This is a workaround to prevent warning of dispatcher after replacing
154
     * the phar file.
155
     *
156
     * @return void
157
     */
158
    protected function _exit()
159
    {
160
        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...
161
    }
162
}
163