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

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

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 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...
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 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...
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