Completed
Pull Request — master (#44)
by Pádraic
02:07
created

SelfUpdate::getStableUpdater()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 6
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 4
nc 1
nop 0
1
<?php
2
3
namespace Povils\PHPMND\Console\Command;
4
5
use Povils\PHPMND\Console\Application;
6
use Symfony\Component\Console\Command\Command as BaseCommand;
7
use Symfony\Component\Console\Input\InputInterface;
8
use Symfony\Component\Console\Input\InputOption;
9
use Symfony\Component\Console\Output\OutputInterface;
10
use Humbug\SelfUpdate\Updater;
11
use Humbug\SelfUpdate\VersionParser;
12
use Humbug\SelfUpdate\Strategy\GithubStrategy;
13
14
class SelfUpdate extends BaseCommand
15
{
16
17
    /**
18
     * Packagist package name
19
     */
20
    const PACKAGE_NAME = 'povils/phpmnd';
21
22
    /**
23
     * This is the remote file name, not local name.
24
     */
25
    const FILE_NAME = 'phpmnd.phar';
26
27
    /**
28
     * @var OutputInterface
29
     */
30
    protected $output;
31
32
    /**
33
     * @var string
34
     */
35
    protected $version;
36
37
    /**
38
     * Setup command and arguments.
39
     */
40
    protected function configure()
41
    {
42
        $this
43
            ->setName('self-update')
44
            ->setDescription('Update phpmnd.phar to most recent stable build.')
45
            ->addOption(
46
                'stable',
47
                's',
48
                InputOption::VALUE_NONE,
49
                'Update to most recent stable version of PHPMND tagged on Github.'
50
            )
51
            ->addOption(
52
                'rollback',
53
                'r',
54
                InputOption::VALUE_NONE,
55
                'Rollback to previous version of PHPMND if available on filesystem.'
56
            )
57
            ->addOption(
58
                'check',
59
                'c',
60
                InputOption::VALUE_NONE,
61
                'Checks what updates are available.'
62
            )
63
        ;
64
    }
65
66
    /**
67
     * Execute the command.
68
     *
69
     * @param InputInterface $input
70
     * @param OutputInterface $output
71
     */
72
    protected function execute(InputInterface $input, OutputInterface $output)
73
    {
74
        $this->output = $output;
75
        $this->version = $this->getApplication()->getVersion();
76
        $parser = new VersionParser;
0 ignored issues
show
Unused Code introduced by
$parser is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
77
78
        /**
79
         * Check for ancilliary options
80
         */
81
        if ($input->getOption('rollback')) {
82
            $this->rollback();
83
            return;
84
        }
85
86
        if ($input->getOption('check')) {
87
            $this->printAvailableUpdates();
88
            return;
89
        }
90
91
        $this->updateToStableBuild();
92
    }
93
94
    /**
95
     * Perform update using phar-updater configured for stable versions.
96
     */
97
    protected function updateToStableBuild()
98
    {
99
        $this->update($this->getStableUpdater());
100
    }
101
102
    /**
103
     * Get phar-updater instance.
104
     */
105
    protected function getStableUpdater()
106
    {
107
        $updater = new Updater(null, false);
108
        $updater->setStrategy(Updater::STRATEGY_GITHUB);
109
        return $this->getGithubReleasesUpdater($updater);
110
    }
111
112
    /**
113
     * Perform in-place update of phar.
114
     */
115
    protected function update(Updater $updater)
116
    {
117
        $this->output->writeln('Updating...'.PHP_EOL);
118
        try {
119
            $result = $updater->update();
120
121
            $newVersion = $updater->getNewVersion();
122
            $oldVersion = $updater->getOldVersion();
123
        
124
            if ($result) {
125
                $this->output->writeln('<fg=green>PHPMND has been updated.</fg=green>');
126
                $this->output->writeln(sprintf(
127
                    '<fg=green>Current version is:</fg=green> <options=bold>%s</options=bold>.',
128
                    $newVersion
129
                ));
130
                $this->output->writeln(sprintf(
131
                    '<fg=green>Previous version was:</fg=green> <options=bold>%s</options=bold>.',
132
                    $oldVersion
133
                ));
134
            } else {
135
                $this->output->writeln('<fg=green>PHPMND is currently up to date.</fg=green>');
136
                $this->output->writeln(sprintf(
137
                    '<fg=green>Current version is:</fg=green> <options=bold>%s</options=bold>.',
138
                    $oldVersion
139
                ));
140
            }
141
        } catch (\Exception $e) {
142
            $this->output->writeln(sprintf('Error: <fg=yellow>%s</fg=yellow>', $e->getMessage()));
143
        }
144
        $this->output->write(PHP_EOL);
145
    }
146
147
    /**
148
     * Attempt to rollback to the previous phar version.
149
     */
150
    protected function rollback()
151
    {
152
        $updater = new Updater;
153
        try {
154
            $result = $updater->rollback();
155
            if ($result) {
156
                $this->output->writeln('<fg=green>PHPMND has been rolled back to prior version.</fg=green>');
157
            } else {
158
                $this->output->writeln('<fg=red>Rollback failed for reasons unknown.</fg=red>');
159
            }
160
        } catch (\Exception $e) {
161
            $this->output->writeln(sprintf('Error: <fg=yellow>%s</fg=yellow>', $e->getMessage()));
162
        }
163
    }
164
165
    protected function printAvailableUpdates()
166
    {
167
        $this->printCurrentLocalVersion();
168
        $this->printCurrentStableVersion();
169
    }
170
171
    /**
172
     * Print the current version of the phar in use.
173
     */
174
    protected function printCurrentLocalVersion()
175
    {
176
        $this->output->writeln(sprintf(
177
            'Your current local build version is: <options=bold>%s</options=bold>',
178
            $this->version
179
        ));
180
    }
181
182
    /**
183
     * Send updater to version printer.
184
     */
185
    protected function printCurrentStableVersion()
186
    {
187
        $this->printVersion($this->getStableUpdater());
188
    }
189
190
    /**
191
     * Print a remotely available version.
192
     * @param  Updater $updater
193
     */
194
    protected function printVersion(Updater $updater)
195
    {
196
        $stability = 'stable';
197
        try {
198
            if ($updater->hasUpdate()) {
199
                $this->output->writeln(sprintf(
200
                    'The current %s build available remotely is: <options=bold>%s</options=bold>',
201
                    $stability,
202
                    $updater->getNewVersion()
203
                ));
204
            } elseif (false == $updater->getNewVersion()) {
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing $updater->getNewVersion() of type string to the boolean false. If you are specifically checking for an empty string, consider using the more explicit === '' instead.
Loading history...
205
                $this->output->writeln(sprintf('There are no new %s builds available.', $stability));
206
            } else {
207
                $this->output->writeln(sprintf('You have the current %s build installed.', $stability));
208
            }
209
        } catch (\Exception $e) {
210
            $this->output->writeln(sprintf('Error: <fg=yellow>%s</fg=yellow>', $e->getMessage()));
211
        }
212
    }
213
214
    /**
215
     * Configure phar-updater with local phar details.
216
     * @param  Updater $updater
217
     * @return Updater
218
     */
219
    protected function getGithubReleasesUpdater(Updater $updater)
220
    {
221
        $updater->getStrategy()->setPackageName(self::PACKAGE_NAME);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Humbug\SelfUpdate\Strategy\StrategyInterface as the method setPackageName() does only exist in the following implementations of said interface: Humbug\SelfUpdate\Strategy\GithubStrategy, Humbug\Test\SelfUpdate\GithubTestStrategy.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
222
        $updater->getStrategy()->setPharName(self::FILE_NAME);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Humbug\SelfUpdate\Strategy\StrategyInterface as the method setPharName() does only exist in the following implementations of said interface: Humbug\SelfUpdate\Strategy\GithubStrategy, Humbug\Test\SelfUpdate\GithubTestStrategy.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
223
        $updater->getStrategy()->setCurrentLocalVersion($this->version);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Humbug\SelfUpdate\Strategy\StrategyInterface as the method setCurrentLocalVersion() does only exist in the following implementations of said interface: Humbug\SelfUpdate\Strategy\GithubStrategy, Humbug\Test\SelfUpdate\GithubTestStrategy.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
224
        return $updater;
225
    }
226
}
227