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

SelfUpdate::execute()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 20
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

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