Completed
Push — develop ( 2ee523...63ab30 )
by Jaap
58:50 queued 48:52
created

convertToArrayDescriptor()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 5
nc 1
nop 1
dl 0
loc 8
rs 9.4285
c 0
b 0
f 0
1
<?php
2
3
namespace phpDocumentor\Descriptor\Builder\Reflector\Tags;
4
5
use phpDocumentor\Descriptor\Builder\Reflector\AssemblerAbstract;
6
use phpDocumentor\Descriptor\Collection as DescriptorCollection;
7
use phpDocumentor\Descriptor\DescriptorAbstract;
8
use phpDocumentor\Descriptor\Type\CollectionDescriptor;
9
use phpDocumentor\Descriptor\Type\UnknownTypeDescriptor;
10
use phpDocumentor\Reflection\DocBlock\Type\Collection;
11
12
/**
13
 * Creates a Collection of type-related value objects for the given Type Collection from the Reflector.
14
 */
15
class TypeCollectionAssembler extends AssemblerAbstract
16
{
17
    /** @var string[] a mapping of types to class names of the Value Object class that describes each type */
18
    protected $typeToValueObjectClassName = array(
0 ignored issues
show
Comprehensibility Naming introduced by
The variable name $typeToValueObjectClassName exceeds the maximum configured length of 20.

Very long variable names usually make code harder to read. It is therefore recommended not to make variable names too verbose.

Loading history...
19
        'string'  => 'phpDocumentor\Descriptor\Type\StringDescriptor',
20
        'int'     => 'phpDocumentor\Descriptor\Type\IntegerDescriptor',
21
        'integer' => 'phpDocumentor\Descriptor\Type\IntegerDescriptor',
22
        'float'   => 'phpDocumentor\Descriptor\Type\FloatDescriptor',
23
        'boolean' => 'phpDocumentor\Descriptor\Type\BooleanDescriptor',
24
        'bool'    => 'phpDocumentor\Descriptor\Type\BooleanDescriptor',
25
    );
26
27
    /**
28
     * Creates a Descriptor from the provided data.
29
     *
30
     * @param Collection $data
31
     *
32
     * @return DescriptorCollection
33
     */
34
    public function create($data)
35
    {
36
        $collection = new DescriptorCollection();
37
38
        foreach ($data as $type) {
39
            $collection->add($this->createDescriptorForType($type));
40
        }
41
42
        return $collection;
43
    }
44
45
    /**
46
     * Creates a Type ValueObject (Descriptor) for the provided type string.
47
     *
48
     * @param string $type
49
     *
50
     * @return DescriptorAbstract
51
     */
52
    protected function createDescriptorForType($type)
53
    {
54
        if (!$this->isArrayNotation($type)) {
55
            $className = $this->findClassNameForType($type);
56
57
            return $className ? new $className() : new UnknownTypeDescriptor($type);
58
        }
59
60
        $type       = $this->extractTypeFromArrayNotation($type);
61
        $className  = $this->findClassNameForType($type);
62
        $descriptor = $className ? new $className() : new UnknownTypeDescriptor($type);
63
        $descriptor = $this->convertToArrayDescriptor($descriptor);
64
65
        return $descriptor;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $descriptor; (phpDocumentor\Descriptor\Type\CollectionDescriptor) is incompatible with the return type documented by phpDocumentor\Descriptor...createDescriptorForType of type phpDocumentor\Descriptor\DescriptorAbstract.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
66
    }
67
68
    /**
69
     * Detects if the given string representing a type equals an array.
70
     *
71
     * @param string $type
72
     *
73
     * @return boolean
74
     */
75
    protected function isArrayNotation($type)
76
    {
77
        return (substr($type, -2) == '[]');
78
    }
79
80
    /**
81
     * Returns the value-type from an array notation.
82
     *
83
     * @param string $type
84
     *
85
     * @return string
86
     */
87
    protected function extractTypeFromArrayNotation($type)
88
    {
89
        return substr($type, 0, -2);
90
    }
91
92
    /**
93
     * Wraps the given Descriptor inside a Collection Descriptor of type array and returns that.
94
     *
95
     * @param DescriptorAbstract $descriptor
96
     *
97
     * @return CollectionDescriptor
98
     */
99
    protected function convertToArrayDescriptor($descriptor)
100
    {
101
        $arrayDescriptor = new CollectionDescriptor('array');
0 ignored issues
show
Documentation introduced by
'array' is of type string, but the function expects a object<phpDocumentor\Des...terfaces\TypeInterface>.

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...
102
        $arrayDescriptor->setTypes(array($descriptor));
0 ignored issues
show
Documentation introduced by
array($descriptor) is of type array<integer,object<php...\DescriptorAbstract>"}>, but the function expects a array<integer,object<php...erfaces\TypeInterface>>.

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...
103
        $arrayDescriptor->setKeyTypes(array('mixed'));
0 ignored issues
show
Documentation introduced by
array('mixed') is of type array<integer,string,{"0":"string"}>, but the function expects a array<integer,object<php...erfaces\TypeInterface>>.

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...
104
105
        return $arrayDescriptor;
106
    }
107
108
    /**
109
     * Returns the class name of the Value Object class associated with a given type or false if the type is unknown.
110
     *
111
     * @param string $type
112
     *
113
     * @return string|boolean
114
     */
115
    protected function findClassNameForType($type)
116
    {
117
        $className = (isset($this->typeToValueObjectClassName[$type]))
118
            ? $this->typeToValueObjectClassName[$type]
119
            : false;
120
121
        return $className;
122
    }
123
}
124