Completed
Push — develop ( d73a05...7ce957 )
by Mike
07:24
created

UpdateCommand::configure()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 0
dl 0
loc 13
rs 9.8333
c 0
b 0
f 0
1
<?php
2
declare(strict_types=1);
3
4
/**
5
 * This file is part of phpDocumentor.
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 *
10
 * @author    Mike van Riel <[email protected]>
11
 * @copyright 2010-2018 Mike van Riel / Naenius (http://www.naenius.com)
12
 * @license   http://www.opensource.org/licenses/mit-license.php MIT
13
 * @link      http://phpdoc.org
14
 */
15
16
namespace phpDocumentor\Console\Command\Phar;
17
18
use \Exception;
19
use Humbug\SelfUpdate\Strategy\GithubStrategy;
20
use Humbug\SelfUpdate\Updater;
21
use phpDocumentor\Application;
22
use Symfony\Component\Console\Command\Command;
23
use Symfony\Component\Console\Input\InputInterface;
24
use Symfony\Component\Console\Input\InputOption;
25
use Symfony\Component\Console\Output\OutputInterface;
26
27
/**
28
 * Updates phpDocumentor.phar to the latest version.
29
 *
30
 * ```
31
 * $ php phpDocumentor.phar phar:update [-m|--major] [-p|--pre] [version]
32
 * ```
33
 *
34
 * @codeCoverageIgnore file should be refactored first before it is testable and not high on prio listing
35
 */
36
class UpdateCommand extends Command
37
{
38
    const PHAR_URL = 'https://github.com/phpDocumentor/phpDocumentor2/releases/latest';
39
40
    /**
41
     * Initializes this command and sets the name, description, options and arguments.
42
     */
43
    protected function configure(): void
44
    {
45
        $this->setName('phar:update')
46
            ->setAliases(['selfupdate', 'self-update'])
47
            ->setDescription('Updates the binary with the latest version.')
48
            ->addOption(
49
                'rollback',
50
                null,
51
                InputOption::VALUE_NONE,
52
                'Rollsback the updated binary to the last version.'
53
            )
54
            ->addOption('pre', 'p', InputOption::VALUE_NONE, 'Allow pre-release version update');
55
    }
56
57
    /**
58
     * Executes the business logic involved with this command.
59
     */
60
    protected function execute(InputInterface $input, OutputInterface $output): int
61
    {
62
        $output->writeln('Looking for updates...');
63
64
        $allowPreRelease = $input->getOption('pre');
65
66
        if (PHP_VERSION_ID < 50600) {
67
            $message = 'Self updating is not available in PHP versions under 5.6.' . "\n";
68
            $message .= 'The latest version can be found at ' . self::PHAR_URL;
69
            $output->writeln(sprintf('<error>%s</error>', $message));
70
            return 1;
71
        } elseif (Application::VERSION() === ('@package_version@')) {
72
            $output->writeln('<error>Self updating has been disabled in source version.</error>');
73
            return 1;
74
        }
75
76
        $exitCode = 1;
77
78
        $updater = new Updater();
79
        $updater->setStrategy(Updater::STRATEGY_GITHUB);
80
        $updater->getStrategy()->setPackageName('phpdocumentor/phpDocumentor2');
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...
81
        $updater->getStrategy()->setPharName('phpDocumentor.phar');
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...
82
        $updater->getStrategy()->getCurrentLocalVersion(Application::VERSION());
0 ignored issues
show
Documentation introduced by
\phpDocumentor\Application::VERSION() is of type string, but the function expects a object<Humbug\SelfUpdate\Updater>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
83
84
        if ($allowPreRelease) {
85
            $updater->getStrategy()->setStability(GithubStrategy::ANY);
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 setStability() 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...
86
        }
87
88
        try {
89
            if ($input->getOption('rollback')) {
90
                $output->writeln('Rolling back to previous version...');
91
                $result = $updater->rollback();
92
            } else {
93
                if (!$updater->hasUpdate()) {
94
                    $output->writeln('No new version available.');
95
                    return 0;
96
                }
97
98
                $output->writeln('Updating to newer version...');
99
                $result = $updater->update();
100
            }
101
102
            if ($result) {
103
                $new = $updater->getNewVersion();
104
                $old = $updater->getOldVersion();
105
106
                $output->writeln(sprintf('Updated from %s to %s', $old, $new));
107
                $exitCode = 0;
108
            }
109
        } catch (Exception $e) {
110
            $output->writeln(sprintf('<error>%s</error>', $e->getMessage()));
111
        }
112
113
        return $exitCode;
114
    }
115
}
116