Completed
Push — master ( 3afdd0...9086d9 )
by Thomas
03:41
created

AbstractPhpStructVisitor::visitUseStatement()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 6
ccs 5
cts 5
cp 1
rs 9.4285
cc 2
eloc 4
nc 2
nop 1
crap 2
1
<?php
2
namespace gossi\codegen\parser\visitor;
3
4
use gossi\codegen\model\AbstractPhpMember;
5
use gossi\codegen\model\AbstractPhpStruct;
6
use gossi\codegen\model\PhpConstant;
7
use gossi\codegen\model\PhpMethod;
8
use gossi\codegen\model\PhpParameter;
9
use gossi\codegen\model\PhpProperty;
10
use gossi\docblock\tags\ParamTag;
11
use PhpParser\Comment\Doc;
12
use PhpParser\Node;
13
use PhpParser\Node\Const_;
14
use PhpParser\Node\Expr\ConstFetch;
15
use PhpParser\Node\Name;
16
use PhpParser\Node\Param;
17
use PhpParser\Node\Scalar\String_;
18
use PhpParser\Node\Stmt\Class_;
19
use PhpParser\Node\Stmt\ClassConst;
20
use PhpParser\Node\Stmt\ClassLike;
21
use PhpParser\Node\Stmt\ClassMethod;
22
use PhpParser\Node\Stmt\Interface_;
23
use PhpParser\Node\Stmt\Namespace_;
24
use PhpParser\Node\Stmt\Property;
25
use PhpParser\Node\Stmt\Trait_;
26
use PhpParser\Node\Stmt\TraitUse;
27
use PhpParser\Node\Stmt\UseUse;
28
use PhpParser\NodeVisitorAbstract;
29
use PhpParser\PrettyPrinter\Standard;
30
31
abstract class AbstractPhpStructVisitor extends NodeVisitorAbstract {
32
33
	private $constMap = [
34
		'false' => false,
35
		'true' => true
36
	];
37
38
	protected $struct;
39
40 7
	public function __construct(AbstractPhpStruct $struct) {
41 7
		$this->struct = $struct;
42 7
	}
43
44
	/**
45
	 * @return AbstractPhpStruct
46
	 */
47 7
	public function getStruct() {
48 7
		return $this->struct;
49
	}
50
51 7
	public function enterNode(Node $node) {
52 7
		switch ($node->getType()) {
53 7
			case 'Stmt_Namespace':
54 7
				$this->visitNamespace($node);
0 ignored issues
show
Compatibility introduced by
$node of type object<PhpParser\Node> is not a sub-type of object<PhpParser\Node\Stmt\Namespace_>. It seems like you assume a concrete implementation of the interface PhpParser\Node to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
55 7
				break;
56
57 7
			case 'Stmt_UseUse':
58 2
				$this->visitUseStatement($node);
0 ignored issues
show
Compatibility introduced by
$node of type object<PhpParser\Node> is not a sub-type of object<PhpParser\Node\Stmt\UseUse>. It seems like you assume a concrete implementation of the interface PhpParser\Node to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
59 2
				break;
60
61 7
			case 'Stmt_Class':
62 5
				$this->visitStruct($node);
0 ignored issues
show
Compatibility introduced by
$node of type object<PhpParser\Node> is not a sub-type of object<PhpParser\Node\Stmt\ClassLike>. It seems like you assume a concrete implementation of the interface PhpParser\Node to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
63 5
				$this->visitClass($node);
0 ignored issues
show
Compatibility introduced by
$node of type object<PhpParser\Node> is not a sub-type of object<PhpParser\Node\Stmt\Class_>. It seems like you assume a concrete implementation of the interface PhpParser\Node to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
64 5
				break;
65
66 7
			case 'Stmt_Interface':
67 1
				$this->visitStruct($node);
0 ignored issues
show
Compatibility introduced by
$node of type object<PhpParser\Node> is not a sub-type of object<PhpParser\Node\Stmt\ClassLike>. It seems like you assume a concrete implementation of the interface PhpParser\Node to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
68 1
				$this->visitInterface($node);
0 ignored issues
show
Compatibility introduced by
$node of type object<PhpParser\Node> is not a sub-type of object<PhpParser\Node\Stmt\Interface_>. It seems like you assume a concrete implementation of the interface PhpParser\Node to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
69 1
				break;
70
71 7
			case 'Stmt_Trait':
72 1
				$this->visitStruct($node);
0 ignored issues
show
Compatibility introduced by
$node of type object<PhpParser\Node> is not a sub-type of object<PhpParser\Node\Stmt\ClassLike>. It seems like you assume a concrete implementation of the interface PhpParser\Node to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
73 1
				$this->visitTrait($node);
0 ignored issues
show
Compatibility introduced by
$node of type object<PhpParser\Node> is not a sub-type of object<PhpParser\Node\Stmt\Trait_>. It seems like you assume a concrete implementation of the interface PhpParser\Node to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
74 1
				break;
75
76 7
			case 'Stmt_TraitUse':
77 2
				$this->visitTraitUse($node);
0 ignored issues
show
Compatibility introduced by
$node of type object<PhpParser\Node> is not a sub-type of object<PhpParser\Node\Stmt\TraitUse>. It seems like you assume a concrete implementation of the interface PhpParser\Node to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
78 2
				break;
79
80 7
			case 'Stmt_ClassConst':
81 2
				$this->visitConstants($node);
0 ignored issues
show
Compatibility introduced by
$node of type object<PhpParser\Node> is not a sub-type of object<PhpParser\Node\Stmt\ClassConst>. It seems like you assume a concrete implementation of the interface PhpParser\Node to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
82 2
				break;
83
84 7
			case 'Stmt_Property':
85 3
				$this->visitProperty($node);
0 ignored issues
show
Compatibility introduced by
$node of type object<PhpParser\Node> is not a sub-type of object<PhpParser\Node\Stmt\Property>. It seems like you assume a concrete implementation of the interface PhpParser\Node to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
86 3
				break;
87
88 7
			case 'Stmt_ClassMethod':
89 5
				$this->visitMethod($node);
0 ignored issues
show
Compatibility introduced by
$node of type object<PhpParser\Node> is not a sub-type of object<PhpParser\Node\Stmt\ClassMethod>. It seems like you assume a concrete implementation of the interface PhpParser\Node to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
90 5
				break;
91 7
		}
92 7
	}
93
94 7
	protected function visitStruct(ClassLike $node) {
95 7
		$this->struct->setName($node->name);
96
97 7
		if (($doc = $node->getDocComment()) !== null) {
98 4
			$this->struct->setDocblock($doc->getReformattedText());
99 4
			$docblock = $this->struct->getDocblock();
100 4
			$this->struct->setDescription($docblock->getShortDescription());
101 4
			$this->struct->setLongDescription($docblock->getLongDescription());
102 4
		}
103 7
	}
104
105
	protected function visitClass(Class_ $node) {}
106
107
	protected function visitInterface(Interface_ $node) {}
108
109
	protected function visitTrait(Trait_ $node) {}
0 ignored issues
show
Unused Code introduced by
The parameter $node is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
110
111 2
	protected function visitTraitUse(TraitUse $node) {
112 2
		foreach ($node->traits as $trait) {
113 2
			$this->struct->addTrait(implode('\\', $trait->parts));
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class gossi\codegen\model\AbstractPhpStruct as the method addTrait() does only exist in the following sub-classes of gossi\codegen\model\AbstractPhpStruct: gossi\codegen\model\PhpClass, gossi\codegen\model\PhpTrait. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends 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 sub-classes 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 parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
114 2
		}
115 2
	}
116
117 2
	protected function visitConstants(ClassConst $node) {
118 2
		$doc = $node->getDocComment();
119 2
		foreach ($node->consts as $const) {
120 2
			$this->visitConstant($const, $doc);
121 2
		}
122 2
	}
123
124 2
	protected function visitConstant(Const_ $node, Doc $doc = null) {
125 2
		$const = new PhpConstant($node->name, $this->getValue($node));
126 2
		$const->setValue($this->getValue($node->value));
127
128 2
		$this->parseMemberDocblock($const, $doc);
129
130 2
		$this->struct->setConstant($const);
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class gossi\codegen\model\AbstractPhpStruct as the method setConstant() does only exist in the following sub-classes of gossi\codegen\model\AbstractPhpStruct: gossi\codegen\model\PhpClass, gossi\codegen\model\PhpInterface. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends 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 sub-classes 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 parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
131 2
	}
132
133 3
	protected function visitProperty(Property $node) {
134 3
		$this->struct->setProperty($this->getProperty($node));
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class gossi\codegen\model\AbstractPhpStruct as the method setProperty() does only exist in the following sub-classes of gossi\codegen\model\AbstractPhpStruct: gossi\codegen\model\PhpClass, gossi\codegen\model\PhpTrait. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends 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 sub-classes 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 parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
135 3
	}
136
137 7
	protected function visitNamespace(Namespace_ $node) {
138 7
		if ($node->name !== null) {
139 7
			$this->struct->setNamespace(implode('\\', $node->name->parts));
140 7
		}
141 7
	}
142
143 2
	protected function visitUseStatement(UseUse $node) {
144 2
		$name = implode('\\', $node->name->parts);
145 2
		$alias = !empty($node->alias) ? $node->alias : null;
146
147 2
		$this->struct->addUseStatement($name, $alias);
148 2
	}
149
150 5
	protected function visitMethod(ClassMethod $node) {
151 5
		$m = new PhpMethod($node->name);
152 5
		$m->setAbstract($node->isAbstract());
153 5
		$m->setFinal($node->isFinal());
154 5
		$m->setVisibility($this->getVisibility($node));
155 5
		$m->setStatic($node->isStatic());
156 5
		$m->setReferenceReturned($node->returnsByRef());
157
158
		// docblock
159 5
		if (($doc = $node->getDocComment()) !== null) {
160 3
			$m->setDocblock($doc->getReformattedText());
161 3
			$docblock = $m->getDocblock();
162 3
			$m->setDescription($docblock->getShortDescription());
163 3
			$m->setLongDescription($docblock->getLongDescription());
164 3
		}
165
166
		// params
167 5
		$params = $m->getDocblock()->getTags('param');
168 5
		foreach ($node->params as $param) {
169
			/* @var $param Param */
170
171 2
			$p = new PhpParameter();
172 2
			$p->setName($param->name);
173 2
			$p->setPassedByReference($param->byRef);
174
175 2
			if (is_string($param->type)) {
176 1
				$p->setType($param->type);
177 2
			} else if ($param->type instanceof Name) {
178 1
				$p->setType(implode('\\', $param->type->parts));
179 1
			}
180
181 2
			$default = $param->default;
182 2
			if ($default !== null) {
183 2
				$this->setDefault($p, $default);
184 2
			}
185
186 2
			$tag = $params->find($p, function (ParamTag $t, $p) {
187 2
				return $t->getVariable() == '$' . $p->getName();
188 2
			});
189
190 2
			if ($tag !== null) {
191 2
				$p->setType($tag->getType(), $tag->getDescription());
192 2
			}
193
194 2
			$m->addParameter($p);
195 5
		}
196
197
		// return type and description
198 5
		$returns = $m->getDocblock()->getTags('return');
199 5
		if ($returns->size() > 0) {
200 1
			$return = $returns->get(0);
201 1
			$m->setType($return->getType(), $return->getDescription());
202 1
		}
203
204
		// body
205 5
		$stmts = $node->getStmts();
206 5
		if (is_array($stmts) && count($stmts)) {
207 1
			$prettyPrinter = new Standard();
208 1
			$m->setBody($prettyPrinter->prettyPrint($stmts));
209 1
		}
210
211 5
		$this->struct->setMethod($m);
212 5
	}
213
214 3
	protected function getProperty(Property $node) {
215 3
		$prop = $node->props[0];
216
217 3
		$p = new PhpProperty($prop->name);
218
219 3
		$default = $prop->default;
220 3
		if ($default !== null) {
221 2
			$this->setDefault($p, $default);
222 2
		}
223
224 3
		$p->setStatic($node->isStatic());
225 3
		$p->setVisibility($this->getVisibility($node));
226
227 3
		$this->parseMemberDocblock($p, $node->getDocComment());
228
229 3
		return $p;
230
	}
231
232 4
	private function parseMemberDocblock($member, Doc $doc = null) {
233 4
		if ($doc !== null) {
234 2
			$member->setDocblock($doc->getReformattedText());
235 2
			$docblock = $member->getDocblock();
236 2
			$member->setDescription($docblock->getShortDescription());
237 2
			$member->setLongDescription($docblock->getLongDescription());
238
239 2
			$vars = $docblock->getTags('var');
240 2
			if ($vars->size() > 0) {
241 2
				$var = $vars->get(0);
242 2
				$member->setType($var->getType(), $var->getDescription());
243 2
			}
244 2
		}
245 4
	}
246
247
	private function setDefault($obj, $default) {
248
		if ($default instanceof String_) {
249
			$obj->setValue($this->getValue($default));
250
		} else {
251
			$obj->setExpression($this->getValue($default));
252
		}
253 3
	}
254 3
255 1
	/**
256 1
	 * Returns the value from a node
257 1
	 *
258
	 * @param Node $node
259
	 * @return mixed
260
	 */
261
	private function getValue(Node $node) {
262
		if ($node instanceof ConstFetch) {
263 3
			$const = $node->name->parts[0];
264 3
			if (isset($this->constMap[$const])) {
265
				return $this->constMap[$const];
266 2
			}
267
268
			return $const;
269
		}
270
271
		if ($node instanceof String_) {
272
			return $node->value;
273
		}
274 5
	}
275 5
276 3
	/**
277
	 * Returns the visibility from a node
278
	 *
279 5
	 * @param Node $node
280 1
	 * @return string
281
	 */
282
	private function getVisibility(Node $node) {
283 5
		if ($node->isPrivate()) {
1 ignored issue
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface PhpParser\Node as the method isPrivate() does only exist in the following implementations of said interface: PhpParser\Node\Stmt\ClassMethod, PhpParser\Node\Stmt\Property.

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...
284
			return AbstractPhpMember::VISIBILITY_PRIVATE;
285
		}
286
287
		if ($node->isProtected()) {
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface PhpParser\Node as the method isProtected() does only exist in the following implementations of said interface: PhpParser\Node\Stmt\ClassMethod, PhpParser\Node\Stmt\Property.

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...
288
			return AbstractPhpMember::VISIBILITY_PROTECTED;
289
		}
290
291
		return AbstractPhpMember::VISIBILITY_PUBLIC;
292
	}
293
294
}
295