Completed
Push — develop ( 373768...b82813 )
by Mike
05:50
created

MethodDescriptor   A

Complexity

Total Complexity 42

Size/Duplication

Total Lines 288
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 6

Test Coverage

Coverage 75%

Importance

Changes 0
Metric Value
dl 0
loc 288
rs 9.0399
c 0
b 0
f 0
ccs 72
cts 96
cp 0.75
wmc 42
lcom 2
cbo 6

20 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 6 1
A setAbstract() 0 4 1
A isAbstract() 0 4 1
A setFinal() 0 4 1
A isFinal() 0 4 1
A setStatic() 0 4 1
A isStatic() 0 4 1
A setVisibility() 0 4 1
A getVisibility() 0 4 1
A setParent() 0 11 1
A getParent() 0 4 1
A setArguments() 0 9 2
A addArgument() 0 5 1
A getArguments() 0 4 1
A getResponse() 0 15 3
A getFile() 0 4 1
A getReturn() 0 15 3
A getParam() 0 15 4
C getInheritedElement() 0 50 15
A setReturnType() 0 4 1

How to fix   Complexity   

Complex Class

Complex classes like MethodDescriptor often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use MethodDescriptor, and based on these observations, apply Extract Interface, too.

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\Descriptor;
17
18
use phpDocumentor\Descriptor\Tag\ReturnDescriptor;
19
use phpDocumentor\Reflection\Type;
20
21
/**
22
 * Descriptor representing a Method in a Class, Interface or Trait.
23
 */
24
class MethodDescriptor extends DescriptorAbstract implements Interfaces\MethodInterface, Interfaces\VisibilityInterface
25
{
26
    /** @var ClassDescriptor|InterfaceDescriptor|TraitDescriptor $parent */
27
    protected $parent;
28
29
    /** @var bool $abstract */
30
    protected $abstract = false;
31
32
    /** @var bool $final */
33
    protected $final = false;
34
35
    /** @var bool $static */
36
    protected $static = false;
37
38
    /** @var string $visibility */
39
    protected $visibility = 'public';
40
41
    /** @var Collection */
42
    protected $arguments;
43
44
    /** @var Type */
45
    private $returnType;
46
47
    /**
48
     * Initializes the all properties representing a collection with a new Collection object.
49
     */
50 1
    public function __construct()
51
    {
52 1
        parent::__construct();
53
54 1
        $this->setArguments(new Collection());
55 1
    }
56
57
    /**
58
     * @param ClassDescriptor|InterfaceDescriptor|TraitDescriptor $parent
59
     */
60 1
    public function setParent($parent)
61
    {
62 1
        $this->setFullyQualifiedStructuralElementName(
63 1
            $parent->getFullyQualifiedStructuralElementName() . '::' . $this->getName() . '()'
64
        );
65
66
        // reset cached inherited element so that it can be re-detected.
67 1
        $this->inheritedElement = null;
68
69 1
        $this->parent = $parent;
70 1
    }
71
72
    /**
73
     * @return ClassDescriptor|InterfaceDescriptor|TraitDescriptor
74
     */
75 1
    public function getParent()
76
    {
77 1
        return $this->parent;
78
    }
79
80
    /**
81
     * {@inheritDoc}
82
     */
83 1
    public function setAbstract($abstract)
84
    {
85 1
        $this->abstract = $abstract;
86 1
    }
87
88
    /**
89
     * {@inheritDoc}
90
     */
91 1
    public function isAbstract()
92
    {
93 1
        return $this->abstract;
94
    }
95
96
    /**
97
     * {@inheritDoc}
98
     */
99 1
    public function setFinal($final)
100
    {
101 1
        $this->final = $final;
102 1
    }
103
104
    /**
105
     * {@inheritDoc}
106
     */
107 1
    public function isFinal()
108
    {
109 1
        return $this->final;
110
    }
111
112
    /**
113
     * {@inheritDoc}
114
     */
115 1
    public function setStatic($static)
116
    {
117 1
        $this->static = $static;
118 1
    }
119
120
    /**
121
     * {@inheritDoc}
122
     */
123 1
    public function isStatic()
124
    {
125 1
        return $this->static;
126
    }
127
128
    /**
129
     * {@inheritDoc}
130
     */
131 1
    public function setVisibility($visibility)
132
    {
133 1
        $this->visibility = $visibility;
134 1
    }
135
136
    /**
137
     * {@inheritDoc}
138
     */
139 1
    public function getVisibility()
140
    {
141 1
        return $this->visibility;
142
    }
143
144
    /**
145
     * {@inheritDoc}
146
     */
147 1
    public function setArguments(Collection $arguments)
148
    {
149 1
        $this->arguments = new Collection();
150
151 1
        foreach ($arguments as $argument) {
152 1
            assert($argument instanceof ArgumentDescriptor);
153 1
            $this->addArgument($argument->getName(), $argument);
154
        }
155 1
    }
156
157
    /**
158
     * @param string $name
159
     */
160 1
    public function addArgument($name, ArgumentDescriptor $argument)
161
    {
162 1
        $argument->setMethod($this);
163 1
        $this->arguments->set($name, $argument);
164 1
    }
165
166
    /**
167
     * {@inheritDoc}
168
     */
169 1
    public function getArguments()
170
    {
171 1
        return $this->arguments;
172
    }
173
174 2
    public function getResponse(): ReturnDescriptor
175
    {
176 2
        $definedReturn = new ReturnDescriptor('return');
177 2
        $definedReturn->setType($this->returnType);
178
179
        /** @var Collection|null $returnTags */
180 2
        $returnTags = $this->getReturn();
181
182 2
        if ($returnTags instanceof Collection && $returnTags->count() > 0) {
183
            /** @var ReturnDescriptor $returnTag */
184 1
            return current($returnTags->getAll());
185
        }
186
187 2
        return $definedReturn;
188
    }
189
190
    /**
191
     * Returns the file associated with the parent class, interface or trait.
192
     *
193
     * @return FileDescriptor
194
     */
195 1
    public function getFile()
196
    {
197 1
        return $this->getParent()->getFile();
198
    }
199
200
    /**
201
     * @return Collection
202
     */
203 1
    public function getReturn()
204
    {
205
        /** @var Collection $var */
206 1
        $var = $this->getTags()->get('return', new Collection());
207 1
        if ($var->count() !== 0) {
208 1
            return $var;
209
        }
210
211 1
        $inheritedElement = $this->getInheritedElement();
212 1
        if ($inheritedElement) {
213 1
            return $inheritedElement->getReturn();
0 ignored issues
show
Documentation Bug introduced by Mike van Riel
The method getReturn does not exist on object<phpDocumentor\Des...tor\DescriptorAbstract>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
214
        }
215
216 1
        return new Collection();
217
    }
218
219
    /**
220
     * @return Collection
221
     */
222 1
    public function getParam()
223
    {
224
        /** @var Collection $var */
225 1
        $var = $this->getTags()->get('param', new Collection());
226 1
        if ($var instanceof Collection && $var->count() > 0) {
227 1
            return $var;
228
        }
229
230 1
        $inheritedElement = $this->getInheritedElement();
231 1
        if ($inheritedElement) {
232 1
            return $inheritedElement->getParam();
0 ignored issues
show
Documentation Bug introduced by Mike van Riel
The method getParam does not exist on object<phpDocumentor\Des...tor\DescriptorAbstract>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
233
        }
234
235 1
        return new Collection();
236
    }
237
238
    /**
239
     * Returns the Method from which this method should inherit its information, if any.
240
     *
241
     * The inheritance scheme for a method is more complicated than for most elements; the following business rules
242
     * apply:
243
     *
244
     * 1. if the parent class/interface extends another class or other interfaces (interfaces have multiple
245
     *    inheritance!) then:
246
     *    1. Check each parent class/interface's parent if they have a method with the exact same name
247
     *    2. if a method is found with the same name; return the first one encountered.
248
     * 2. if the parent is a class and implements interfaces, check each interface for a method with the exact same
249
     *    name. If such a method is found, return the first hit.
250
     *
251
     * @return MethodDescriptor|null
252
     */
253
    public function getInheritedElement()
254
    {
255
        if ($this->inheritedElement !== null) {
256
            return $this->inheritedElement;
257
        }
258
259
        /** @var ClassDescriptor|InterfaceDescriptor|null $associatedClass */
260
        $associatedClass = $this->getParent();
261
        if (!$associatedClass instanceof ClassDescriptor && !$associatedClass instanceof InterfaceDescriptor) {
262
            return null;
263
        }
264
265
        /** @var ClassDescriptor|InterfaceDescriptor $parentClass|null */
266
        $parentClass = $associatedClass->getParent();
267
        if ($parentClass instanceof ClassDescriptor || $parentClass instanceof Collection) {
268
            // the parent of a class is always a class, but the parent of an interface is a collection of interfaces.
269
            $parents = $parentClass instanceof ClassDescriptor ? [$parentClass] : $parentClass->getAll();
270
            foreach ($parents as $parent) {
271
                if ($parent instanceof ClassDescriptor || $parent instanceof InterfaceDescriptor) {
272
                    /** @var MethodDescriptor $parentMethod */
273
                    $parentMethod = $parent->getMethods()->get($this->getName());
274
                    if ($parentMethod) {
275
                        $this->inheritedElement = $parentMethod;
276
277
                        return $this->inheritedElement;
278
                    }
279
                }
280
            }
281
        }
282
283
        // also check all implemented interfaces next if the parent is a class and not an interface
284
        if ($associatedClass instanceof ClassDescriptor) {
285
            /** @var InterfaceDescriptor $interface */
286
            foreach ($associatedClass->getInterfaces() as $interface) {
287
                if (!$interface instanceof InterfaceDescriptor) {
288
                    continue;
289
                }
290
291
                /** @var MethodDescriptor $parentMethod */
292
                $parentMethod = $interface->getMethods()->get($this->getName());
293
                if ($parentMethod) {
294
                    $this->inheritedElement = $parentMethod;
295
296
                    return $this->inheritedElement;
297
                }
298
            }
299
        }
300
301
        return null;
302
    }
303
304
    /**
305
     * Sets return type of this method.
306
     */
307 1
    public function setReturnType(Type $returnType)
308
    {
309 1
        $this->returnType = $returnType;
310 1
    }
311
}
312