Completed
Push — develop ( 0b8425...c51867 )
by Jaap
15s queued 11s
created

src/phpDocumentor/Descriptor/MethodDescriptor.php (2 issues)

undocumented call capabilities.

Bug Documentation Minor

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

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
    public function setParent($parent)
61
    {
62
        $this->setFullyQualifiedStructuralElementName(
63
            $parent->getFullyQualifiedStructuralElementName() . '::' . $this->getName() . '()'
64
        );
65
66
        // reset cached inherited element so that it can be re-detected.
67
        $this->inheritedElement = null;
68
69
        $this->parent = $parent;
70
    }
71
72
    /**
73
     * @return ClassDescriptor|InterfaceDescriptor|TraitDescriptor
74
     */
75
    public function getParent()
76
    {
77
        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
        foreach ($arguments as $argument) {
150
            $argument->setMethod($this);
151
        }
152
153 1
        $this->arguments = $arguments;
154 1
    }
155
156
    /**
157
     * @param string $name
158
     */
159
    public function addArgument($name, ArgumentDescriptor $argument)
160
    {
161
        $argument->setMethod($this);
162
        $this->arguments->set($name, $argument);
163
    }
164
165
    /**
166
     * {@inheritDoc}
167
     */
168 1
    public function getArguments()
169
    {
170 1
        return $this->arguments;
171
    }
172
173 2
    public function getResponse(): ReturnDescriptor
174
    {
175 2
        $definedReturn = new ReturnDescriptor('return');
176 2
        $definedReturn->setType($this->returnType);
177
178
        /** @var Collection|null $returnTags */
179 2
        $returnTags = $this->getReturn();
180
181 2
        if ($returnTags instanceof Collection && $returnTags->count() > 0) {
182
            /** @var ReturnDescriptor $returnTag */
183 1
            return current($returnTags->getAll());
184
        }
185
186 2
        return $definedReturn;
187
    }
188
189
    /**
190
     * Returns the file associated with the parent class, interface or trait.
191
     *
192
     * @return FileDescriptor
193
     */
194 1
    public function getFile()
195
    {
196 1
        return $this->getParent()->getFile();
197
    }
198
199
    /**
200
     * @return Collection
201
     */
202 1
    public function getReturn()
203
    {
204
        /** @var Collection $var */
205 1
        $var = $this->getTags()->get('return', new Collection());
206 1
        if ($var->count() !== 0) {
207 1
            return $var;
208
        }
209
210 1
        $inheritedElement = $this->getInheritedElement();
211 1
        if ($inheritedElement) {
212 1
            return $inheritedElement->getReturn();
0 ignored issues
show
Documentation Bug introduced by
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...
213
        }
214
215
        return new Collection();
216
    }
217
218
    /**
219
     * @return Collection
220
     */
221 1
    public function getParam()
222
    {
223
        /** @var Collection $var */
224 1
        $var = $this->getTags()->get('param', new Collection());
225 1
        if ($var instanceof Collection && $var->count() > 0) {
226 1
            return $var;
227
        }
228
229 1
        $inheritedElement = $this->getInheritedElement();
230 1
        if ($inheritedElement) {
231 1
            return $inheritedElement->getParam();
0 ignored issues
show
Documentation Bug introduced by
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...
232
        }
233
234
        return new Collection();
235
    }
236
237
    /**
238
     * Returns the Method from which this method should inherit its information, if any.
239
     *
240
     * The inheritance scheme for a method is more complicated than for most elements; the following business rules
241
     * apply:
242
     *
243
     * 1. if the parent class/interface extends another class or other interfaces (interfaces have multiple
244
     *    inheritance!) then:
245
     *    1. Check each parent class/interface's parent if they have a method with the exact same name
246
     *    2. if a method is found with the same name; return the first one encountered.
247
     * 2. if the parent is a class and implements interfaces, check each interface for a method with the exact same
248
     *    name. If such a method is found, return the first hit.
249
     *
250
     * @return MethodDescriptor|null
251
     */
252
    public function getInheritedElement()
253
    {
254
        if ($this->inheritedElement !== null) {
255
            return $this->inheritedElement;
256
        }
257
258
        /** @var ClassDescriptor|InterfaceDescriptor|null $associatedClass */
259
        $associatedClass = $this->getParent();
260
        if (!$associatedClass instanceof ClassDescriptor && !$associatedClass instanceof InterfaceDescriptor) {
261
            return null;
262
        }
263
264
        /** @var ClassDescriptor|InterfaceDescriptor $parentClass|null */
265
        $parentClass = $associatedClass->getParent();
266
        if ($parentClass instanceof ClassDescriptor || $parentClass instanceof Collection) {
267
            // the parent of a class is always a class, but the parent of an interface is a collection of interfaces.
268
            $parents = $parentClass instanceof ClassDescriptor ? [$parentClass] : $parentClass->getAll();
269
            foreach ($parents as $parent) {
270
                if ($parent instanceof ClassDescriptor || $parent instanceof InterfaceDescriptor) {
271
                    /** @var MethodDescriptor $parentMethod */
272
                    $parentMethod = $parent->getMethods()->get($this->getName());
273
                    if ($parentMethod) {
274
                        $this->inheritedElement = $parentMethod;
275
276
                        return $this->inheritedElement;
277
                    }
278
                }
279
            }
280
        }
281
282
        // also check all implemented interfaces next if the parent is a class and not an interface
283
        if ($associatedClass instanceof ClassDescriptor) {
284
            /** @var InterfaceDescriptor $interface */
285
            foreach ($associatedClass->getInterfaces() as $interface) {
286
                if (!$interface instanceof InterfaceDescriptor) {
287
                    continue;
288
                }
289
290
                /** @var MethodDescriptor $parentMethod */
291
                $parentMethod = $interface->getMethods()->get($this->getName());
292
                if ($parentMethod) {
293
                    $this->inheritedElement = $parentMethod;
294
295
                    return $this->inheritedElement;
296
                }
297
            }
298
        }
299
300
        return null;
301
    }
302
303
    /**
304 1
     * Sets return type of this method.
305
     */
306 1
    public function setReturnType(Type $returnType)
307 1
    {
308
        $this->returnType = $returnType;
309
    }
310
}
311