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

SelfUpdate::rollback()   A

Complexity

Conditions 3
Paths 5

Size

Total Lines 14
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
c 3
b 0
f 0
dl 0
loc 14
rs 9.4285
cc 3
eloc 10
nc 5
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\Strategy\GithubStrategy;
12
13
class SelfUpdate extends BaseCommand
14
{
15
16
    const REMOTE_FILENAME = 'phpmnd.phar';
17
18
    /**
19
     * @var OutputInterface
20
     */
21
    private $output;
22
23
    /**
24
     * @var string
25
     */
26
    private $version;
27
28
    /**
29
     * Setup command and arguments.
30
     */
31
    protected function configure()
32
    {
33
        $this
34
            ->setName('self-update')
35
            ->setDescription('Update phpmnd.phar to most recent stable build.')
36
            ->addOption(
37
                'rollback',
38
                'r',
39
                InputOption::VALUE_NONE,
40
                'Rollback to previous version of PHPMND if available on filesystem.'
41
            )
42
            ->addOption(
43
                'check',
44
                'c',
45
                InputOption::VALUE_NONE,
46
                'Checks whether an update is available.'
47
            )
48
        ;
49
    }
50
51
    /**
52
     * Execute the command.
53
     *
54
     * @param InputInterface $input
55
     * @param OutputInterface $output
56
     */
57
    protected function execute(InputInterface $input, OutputInterface $output)
58
    {
59
        $this->output = $output;
60
        $this->version = $this->getApplication()->getVersion();
61
62
        /**
63
         * Check for ancilliary options
64
         */
65
        if ($input->getOption('rollback')) {
66
            $this->rollback();
67
            return;
68
        }
69
70
        if ($input->getOption('check')) {
71
            $this->printAvailableUpdates();
72
            return;
73
        }
74
75
        $this->updateToStableBuild();
76
    }
77
78
    /**
79
     * Perform update using phar-updater configured for stable versions.
80
     */
81
    private function updateToStableBuild()
82
    {
83
        $this->update($this->getStableUpdater());
84
    }
85
86
    /**
87
     * Get phar-updater instance.
88
     */
89
    private function getStableUpdater()
90
    {
91
        $updater = new Updater(null, false);
92
        $updater->setStrategy(Updater::STRATEGY_GITHUB);
93
        return $this->getGithubReleasesUpdater($updater);
94
    }
95
96
    /**
97
     * Perform in-place update of phar.
98
     */
99
    private function update(Updater $updater)
100
    {
101
        $this->output->writeln('Updating...'.PHP_EOL);
102
        try {
103
            $result = $updater->update();
104
105
            $newVersion = $updater->getNewVersion();
106
            $oldVersion = $updater->getOldVersion();
107
        
108
            if ($result) {
109
                $this->output->writeln('<info>PHPMND has been updated.</info>');
110
                $this->output->writeln(sprintf(
111
                    '<info>Current version is:</info> <options=bold>%s</options=bold>.',
112
                    $newVersion
113
                ));
114
                $this->output->writeln(sprintf(
115
                    '<info>Previous version was:</info> <options=bold>%s</options=bold>.',
116
                    $oldVersion
117
                ));
118
            } else {
119
                $this->output->writeln('<info>PHPMND is currently up to date.</info>');
120
                $this->output->writeln(sprintf(
121
                    '<info>Current version is:</info> <options=bold>%s</options=bold>.',
122
                    $oldVersion
123
                ));
124
            }
125
        } catch (\Exception $e) {
126
            $this->output->writeln(sprintf('<error>Error: %s</error>', $e->getMessage()));
127
        }
128
        $this->output->write(PHP_EOL);
129
    }
130
131
    /**
132
     * Attempt to rollback to the previous phar version.
133
     */
134
    private function rollback()
135
    {
136
        $updater = new Updater(null, false);
137
        try {
138
            $result = $updater->rollback();
139
            if ($result) {
140
                $this->output->writeln('<info>PHPMND has been rolled back to prior version.</info>');
141
            } else {
142
                $this->output->writeln('<error>Rollback failed for reasons unknown.</error>');
143
            }
144
        } catch (\Exception $e) {
145
            $this->output->writeln(sprintf('Error: <error>%s</error>', $e->getMessage()));
146
        }
147
    }
148
149
    private function printAvailableUpdates()
150
    {
151
        $this->printCurrentLocalVersion();
152
        $this->printCurrentStableVersion();
153
    }
154
155
    /**
156
     * Print the current version of the phar in use.
157
     */
158
    private function printCurrentLocalVersion()
159
    {
160
        $this->output->writeln(sprintf(
161
            'Your current local build version is: <options=bold>%s</options=bold>',
162
            $this->version
163
        ));
164
    }
165
166
    /**
167
     * Send updater to version printer.
168
     */
169
    private function printCurrentStableVersion()
170
    {
171
        $this->printVersion($this->getStableUpdater());
172
    }
173
174
    /**
175
     * Print a remotely available version.
176
     * @param  Updater $updater
177
     */
178
    private function printVersion(Updater $updater)
179
    {
180
        $stability = 'stable';
181
        try {
182
            if ($updater->hasUpdate()) {
183
                $this->output->writeln(sprintf(
184
                    'The current %s build available remotely is: <options=bold>%s</options=bold>',
185
                    $stability,
186
                    $updater->getNewVersion()
187
                ));
188
            } 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...
189
                $this->output->writeln(sprintf('There are no new %s builds available.', $stability));
190
            } else {
191
                $this->output->writeln(sprintf('You have the current %s build installed.', $stability));
192
            }
193
        } catch (\Exception $e) {
194
            $this->output->writeln(sprintf('Error: <error>%s</error>', $e->getMessage()));
195
        }
196
    }
197
198
    /**
199
     * @param  Updater $updater
200
     * @return Updater
201
     */
202
    private function getGithubReleasesUpdater(Updater $updater)
203
    {
204
        $updater->getStrategy()->setPackageName(Application::PACKAGIST_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...
205
        $updater->getStrategy()->setPharName(self::REMOTE_FILENAME);
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...
206
        $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...
207
        return $updater;
208
    }
209
}
210