ReflectorFactory::parseDocComment()   B
last analyzed

Complexity

Conditions 3
Paths 3

Size

Total Lines 28
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 3

Importance

Changes 0
Metric Value
dl 0
loc 28
ccs 13
cts 13
cp 1
rs 8.8571
c 0
b 0
f 0
cc 3
eloc 16
nc 3
nop 1
crap 3
1
<?php
2
/**
3
 * This file is part of the Composite Utils package.
4
 *
5
 * (c) Emily Shepherd <[email protected]>
6
 *
7
 * For the full copyright and license information, please view the
8
 * LICENSE.md file that was distributed with this source code.
9
 *
10
 * @package spaark/composite-utils
11
 * @author Emily Shepherd <[email protected]>
12
 * @license MIT
13
 */
14
15
namespace Spaark\CompositeUtils\Factory\Reflection;
16
17
use Spaark\CompositeUtils\Factory\BaseFactory;
18
use Spaark\CompositeUtils\Service\RawPropertyAccessor;
19
use Spaark\CompositeUtils\Model\Reflection\Reflector as SpaarkReflector;
20
use \Reflector as PHPNativeReflector;
21
22
/**
23
 * Abstract class for specific Reflection factories to extend
24
 */
25
abstract class ReflectorFactory extends BaseFactory
26
{
27
    const REFLECTION_OBJECT = null;
28
29
    /**
30
     * @var PHPNativeReflector
31
     */
32
    protected $reflector;
33
34
    /**
35
     * @var RawPropertyAccessor
36
     */
37
    protected $accessor;
38
39
    /**
40
     * @var SpaarkReflector
41
     */
42
    protected $object;
43
44
    /**
45
     * Creates a new ReflectorFactory using the provided native PHP
46
     * reflector as a basis
47
     *
48
     * @param PHPNativeReflector $reflector The reflector used to
49
     *     parse the item
50
     */
51 21
    public function __construct(PHPNativeReflector $reflector)
52
    {
53 21
        $class = static::REFLECTION_OBJECT;
54
55 21
        $this->object = new $class();
56 21
        $this->accessor = new RawPropertyAccessor($this->object);
57 21
        $this->reflector = $reflector;
58 21
    }
59
60
    /**
61
     * Parses the docblock comment for this item and searches for
62
     * annotations
63
     */
64 21
    protected function parseDocComment(array $acceptedParams)
65
    {
66 21
        preg_match_all
67
        (
68
              '/^'
69
            .     '[ \t]*\*[ \t]*'
70
            .     '@([a-zA-Z]+)'
71
            .     '(.*)'
72 21
            . '$/m',
73 21
            $this->reflector->getDocComment(),
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Reflector as the method getDocComment() does only exist in the following implementations of said interface: Doctrine\Common\Reflecti...ublicReflectionProperty, Doctrine\Common\Reflection\StaticReflectionClass, Doctrine\Common\Reflection\StaticReflectionMethod, Doctrine\Common\Reflecti...taticReflectionProperty, ReflectionClass, ReflectionFunction, ReflectionFunctionAbstract, ReflectionMethod, ReflectionObject, ReflectionProperty.

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...
74 21
            $matches
75
        );
76
77 21
        foreach ($matches[0] as $key => $value)
78
        {
79 21
            $name = strtolower($matches[1][$key]);
80 21
            $value = trim($matches[2][$key]);
81
82 21
            if (isset($acceptedParams[$name]))
83
            {
84 21
                call_user_func
85
                (
86 21
                    array($this, $acceptedParams[$name]),
87 21
                    $name, $value
88
                );
89
            }
90
        }
91 21
    }
92
93
    /**
94
     * Sets an annotation which has a boolean value
95
     *
96
     * @param string $name The name of the annotation
97
     * @param string $value The value of the annotation
98
     */
99 20
    protected function setBool($name, $value)
100
    {
101 20
        switch(strtolower($value))
102
        {
103 20
            case '':
104 19
            case 'true':
105 20
                $value = true;
106 20
                break;
107
108 19
            case 'false':
109 19
                $value = false;
110 19
                break;
111
112
            default:
113 19
                $value = (boolean)$value;
114
        }
115
116 20
        $this->accessor->setRawValue($name, $value);
117 20
    }
118
}
119
120