ValidateProjectStep   A
last analyzed

Complexity

Total Complexity 12

Size/Duplication

Total Lines 99
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 5

Test Coverage

Coverage 0%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 12
lcom 1
cbo 5
dl 0
loc 99
ccs 0
cts 38
cp 0
rs 10
c 1
b 0
f 0

5 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 3 1
C execute() 0 41 7
A isValidComponentName() 0 4 1
A isValidPackageRepository() 0 4 1
A isComponentSpecificationComplete() 0 4 2
1
<?php
2
3
/**
4
 * Moodle component manager.
5
 *
6
 * @author Luke Carrier <[email protected]>
7
 * @copyright 2016 Luke Carrier
8
 * @license GPL-3.0+
9
 */
10
11
namespace ComponentManager\Step;
12
13
use ComponentManager\ComponentSpecification;
14
use ComponentManager\Exception\InvalidProjectException;
15
use ComponentManager\PackageRepository\PackageRepository;
16
use ComponentManager\Project\Project;
17
use Psr\Log\LoggerInterface;
18
19
/**
20
 * Validate the project file.
21
 */
22
class ValidateProjectStep implements Step {
23
    /**
24
     * Project to validate.
25
     *
26
     * @var Project
27
     */
28
    protected $project;
29
30
    /**
31
     * Initialiser.
32
     *
33
     * @param Project $project
34
     */
35
    public function __construct(Project $project) {
36
        $this->project = $project;
37
    }
38
39
    /**
40
     * @inheritdoc Step
41
     */
42
    public function execute($task, LoggerInterface $logger) {
43
        $componentSpecifications = $this->project->getProjectFile()->getComponentSpecifications();
44
        $packageRepositories = $this->project->getPackageRepositories();
45
46
        $result = true;
47
48
        if (!$this->project->getProjectFile()->getMoodleVersion()) {
49
            $result = false;
50
            $logger->warn('No moodle.version key; package operations will fail');
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Psr\Log\LoggerInterface as the method warn() does only exist in the following implementations of said interface: Monolog\Logger, Symfony\Bridge\Monolog\Logger.

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...
51
        }
52
53
        foreach ($componentSpecifications as $componentSpecification) {
54
            if (!$this->isValidComponentName($componentSpecification)) {
55
                $result = false;
56
                $logger->error('An invalid component name was specified', [
57
                    'componentName' => $componentSpecification->getName(),
58
                ]);
59
            }
60
61
            if (!$this->isComponentSpecificationComplete($componentSpecification)) {
62
                $result = false;
63
                $logger->error('An incomplete component specification was specified', [
64
                    'componentSpecification' => $componentSpecification,
65
                ]);
66
            }
67
68
            if (!$this->isValidPackageRepository($componentSpecification, $packageRepositories)) {
69
                $result = false;
70
                $logger->error('Component uses undeclared package repository', [
71
                    'componentName'         => $componentSpecification->getName(),
72
                    'packageRepositoryName' => $componentSpecification->getPackageRepository(),
73
                ]);
74
            }
75
76
            if (!$result) {
77
                throw new InvalidProjectException(
78
                        'The supplied project file is invalid',
79
                        InvalidProjectException::CODE_VALIDATION_FAILED);
80
            }
81
        }
82
    }
83
84
    /**
85
     * Does the supplied component have a valid name?
86
     *
87
     * @param ComponentSpecification $componentSpecification
88
     *
89
     * @return boolean
90
     */
91
    protected function isValidComponentName(ComponentSpecification $componentSpecification) {
92
        $parts = explode('_', $componentSpecification->getName(), 2);
93
        return count($parts) === 2;
94
    }
95
96
    /**
97
     * Is the supplied component's package repository known to us?
98
     *
99
     * @param ComponentSpecification $componentSpecification
100
     * @param PackageRepository[]    $packageRepositories
101
     *
102
     * @return boolean
103
     */
104
    protected function isValidPackageRepository(ComponentSpecification $componentSpecification, $packageRepositories) {
105
        return array_key_exists(
106
                $componentSpecification->getPackageRepository(), $packageRepositories);
107
    }
108
109
    /**
110
     * Is the supplied component's specification complete?
111
     *
112
     * @param ComponentSpecification $componentSpecification
113
     *
114
     * @return boolean
115
     */
116
    protected function isComponentSpecificationComplete(ComponentSpecification $componentSpecification) {
117
        return !!$componentSpecification->getPackageRepository()
118
                && !!$componentSpecification->getPackageSource();
119
    }
120
}
121