Completed
Pull Request — master (#44)
by Pádraic
01:58
created

SelfUpdate   A

Complexity

Total Complexity 20

Size/Duplication

Total Lines 203
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 4

Importance

Changes 4
Bugs 1 Features 0
Metric Value
wmc 20
c 4
b 1
f 0
lcom 2
cbo 4
dl 0
loc 203
rs 10

11 Methods

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