Completed
Pull Request — master (#58)
by Tobias
07:12 queued 05:05
created

ClassDiscovery::findOneByType()   B

Complexity

Conditions 5
Paths 5

Size

Total Lines 15
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 13.575

Importance

Changes 2
Bugs 0 Features 1
Metric Value
c 2
b 0
f 1
dl 0
loc 15
ccs 3
cts 10
cp 0.3
rs 8.8571
cc 5
eloc 11
nc 5
nop 1
crap 13.575
1
<?php
2
3
namespace Http\Discovery;
4
5
use Http\Discovery\Exception\NotFoundException;
6
use Http\Discovery\Exception\PuliNotAvailableException;
7
use Http\Discovery\FallbackStrategy\HttpClients;
8
use Http\Discovery\FallbackStrategy\DiactorosFactory;
9
use Http\Discovery\FallbackStrategy\GuzzleFactory;
10
use Puli\Discovery\Api\Discovery;
11
12
/**
13
 * Registry that based find results on class existence.
14
 *
15
 * @author David de Boer <[email protected]>
16
 * @author Márk Sági-Kazár <[email protected]>
17
 */
18
abstract class ClassDiscovery
19
{
20
    /**
21
     * @var GeneratedPuliFactory
22
     */
23
    private static $puliFactory;
24
25
    /**
26
     * @var Discovery
27
     */
28
    private static $puliDiscovery;
29
30
    /**
31
     * @return GeneratedPuliFactory
32
     */
33 10
    public static function getPuliFactory()
34
    {
35 10
        if (null === self::$puliFactory) {
36
            if (!defined('PULI_FACTORY_CLASS')) {
37
                throw new PuliNotAvailableException('Puli Factory is not available');
38
            }
39
40
            $puliFactoryClass = PULI_FACTORY_CLASS;
41
42
            if (!class_exists($puliFactoryClass)) {
43
                throw new PuliNotAvailableException('Puli Factory class does not exist');
44
            }
45
46
            self::$puliFactory = new $puliFactoryClass();
47
        }
48
49 10
        return self::$puliFactory;
50
    }
51
52
    /**
53
     * Sets the Puli factory.
54
     *
55
     * @param object $puliFactory
56
     */
57 21
    public static function setPuliFactory($puliFactory)
58
    {
59 21
        if (!is_callable([$puliFactory, 'createRepository']) || !is_callable([$puliFactory, 'createDiscovery'])) {
60
            throw new \InvalidArgumentException('The Puli Factory must expose a repository and a discovery');
61
        }
62
63 21
        self::$puliFactory = $puliFactory;
64 21
        self::$puliDiscovery = null;
65 21
    }
66
67
    /**
68
     * Resets the factory.
69
     */
70 21
    public static function resetPuliFactory()
71
    {
72 21
        self::$puliFactory = null;
73 21
        self::$puliDiscovery = null;
74 21
    }
75
76
    /**
77
     * Returns the Puli discovery layer.
78
     *
79
     * @return Discovery
80
     */
81 9
    public static function getPuliDiscovery()
82
    {
83 9
        if (!isset(self::$puliDiscovery)) {
84 9
            $factory = self::getPuliFactory();
85 9
            $repository = $factory->createRepository();
86
87 9
            self::$puliDiscovery = $factory->createDiscovery($repository);
88 9
        }
89
90 9
        return self::$puliDiscovery;
91
    }
92
93
    /**
94
     * Finds a class.
95
     *
96
     * @param $type
97
     *
98
     * @return string
99
     *
100
     * @throws NotFoundException
101
     */
102 8
    public static function findOneByType($type)
103
    {
104
        try {
105 8
            return self::puliFindOneByType($type);
106 1
        } catch (PuliNotAvailableException $e) {
107
            if (false !== $class = HttpClients::findOneByType($type)) {
108
                return $class;
109
            } elseif (false !== $class = GuzzleFactory::findOneByType($type)) {
110
                return $class;
111
            } elseif (false !== $class = DiactorosFactory::findOneByType($type)) {
112
                return $class;
113
            }
114
            throw new NotFoundException('Could not find resource using Puli nor common Guzzle/Diactoros classes', 0, $e);
115
        }
116
    }
117
118
    /**
119
     * Finds a class using Puli.
120
     *
121
     * @param $type
122
     *
123
     * @return string
124
     *
125
     * @throws NotFoundException
126
     * @throws PuliNotAvailableException
127
     */
128 8
    private static function puliFindOneByType($type)
129
    {
130 8
        $bindings = self::getPuliDiscovery()->findBindings($type);
131
132 8
        foreach ($bindings as $binding) {
133 7
            if ($binding->hasParameterValue('depends')) {
134 1
                $dependency = $binding->getParameterValue('depends');
135
136 1
                if (!self::evaluateCondition($dependency)) {
137 1
                    continue;
138
                }
139
            }
140
141
            // TODO: check class binding
142 7
            return $binding->getClassName();
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Puli\Discovery\Api\Binding\Binding as the method getClassName() does only exist in the following implementations of said interface: Puli\Discovery\Binding\ClassBinding.

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...
143 1
        }
144
145 1
        throw new NotFoundException(sprintf('Resource of type "%s" not found', $type));
146
    }
147
148
    /**
149
     * Finds a resource.
150
     *
151
     * @return object
152
     */
153
    public static function find()
154
    {
155
        throw new \LogicException('Not implemented');
156
    }
157
158
    /**
159
     * Evaulates conditions to boolean.
160
     *
161
     * @param mixed $condition
162
     *
163
     * @return bool
164
     */
165 1
    protected static function evaluateCondition($condition)
166
    {
167 1
        if (is_string($condition)) {
168
            // Should be extended for functions, extensions???
169
            return class_exists($condition);
170 1
        } elseif (is_callable($condition)) {
171
            return $condition();
172 1
        } elseif (is_bool($condition)) {
173 1
            return $condition;
174
        } elseif (is_array($condition)) {
175
            $evaluatedCondition = true;
176
177
            // Immediately stop execution if the condition is false
178
            for ($i = 0; $i < count($condition) && false !== $evaluatedCondition; ++$i) {
179
                $evaluatedCondition &= static::evaluateCondition($condition[$i]);
180
            }
181
182
            return $evaluatedCondition;
183
        }
184
185
        return false;
186
    }
187
}
188