Completed
Push — master ( 9086d9...713b15 )
by Thomas
07:07
created

AbstractPhpStructVisitor::visitTrait()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 1
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 1
ccs 0
cts 0
cp 0
rs 10
cc 1
eloc 1
nc 1
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
use PhpParser\Node\Scalar\LNumber;
31
use PhpParser\Node\Scalar\DNumber;
32
use PhpParser\Node\Scalar\MagicConst;
33
34
abstract class AbstractPhpStructVisitor extends NodeVisitorAbstract {
35
36
	private $constMap = [
37
		'false' => false,
38
		'true' => true
39
	];
40
41
	protected $struct;
42
43 8
	public function __construct(AbstractPhpStruct $struct) {
44 8
		$this->struct = $struct;
45 8
	}
46
47
	/**
48
	 * @return AbstractPhpStruct
49
	 */
50 8
	public function getStruct() {
51 8
		return $this->struct;
52
	}
53
54 8
	public function enterNode(Node $node) {
55 8
		switch ($node->getType()) {
56 8
			case 'Stmt_Namespace':
57 8
				$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...
58 8
				break;
59
60 8
			case 'Stmt_UseUse':
61 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...
62 2
				break;
63
64 8
			case 'Stmt_Class':
65 6
				$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...
66 6
				$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...
67 6
				break;
68
69 8
			case 'Stmt_Interface':
70 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...
71 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...
72 1
				break;
73
74 8
			case 'Stmt_Trait':
75 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...
76 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...
77 1
				break;
78
79 8
			case 'Stmt_TraitUse':
80 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...
81 2
				break;
82
83 8
			case 'Stmt_ClassConst':
84 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...
85 2
				break;
86
87 8
			case 'Stmt_Property':
88 4
				$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...
89 4
				break;
90
91 8
			case 'Stmt_ClassMethod':
92 6
				$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...
93 6
				break;
94 8
		}
95 8
	}
96
97 8
	protected function visitStruct(ClassLike $node) {
98 8
		$this->struct->setName($node->name);
99
100 8
		if (($doc = $node->getDocComment()) !== null) {
101 4
			$this->struct->setDocblock($doc->getReformattedText());
102 4
			$docblock = $this->struct->getDocblock();
103 4
			$this->struct->setDescription($docblock->getShortDescription());
104 4
			$this->struct->setLongDescription($docblock->getLongDescription());
105 4
		}
106 8
	}
107
108
	protected function visitClass(Class_ $node) {}
109
110
	protected function visitInterface(Interface_ $node) {}
111
112
	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...
113
114 2
	protected function visitTraitUse(TraitUse $node) {
115 2
		foreach ($node->traits as $trait) {
116 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...
117 2
		}
118 2
	}
119
120 2
	protected function visitConstants(ClassConst $node) {
121 2
		$doc = $node->getDocComment();
122 2
		foreach ($node->consts as $const) {
123 2
			$this->visitConstant($const, $doc);
124 2
		}
125 2
	}
126
127 2
	protected function visitConstant(Const_ $node, Doc $doc = null) {
128 2
		$const = new PhpConstant($node->name);
129 2
		$this->setDefault($const, $node->value);
130 2
		$this->parseMemberDocblock($const, $doc);
131
132 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...
133 2
	}
134
135 4
	protected function visitProperty(Property $node) {
136 4
		$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...
137 4
	}
138
139 8
	protected function visitNamespace(Namespace_ $node) {
140 8
		if ($node->name !== null) {
141 8
			$this->struct->setNamespace(implode('\\', $node->name->parts));
142 8
		}
143 8
	}
144
145 2
	protected function visitUseStatement(UseUse $node) {
146 2
		$name = implode('\\', $node->name->parts);
147 2
		$alias = !empty($node->alias) ? $node->alias : null;
148
149 2
		$this->struct->addUseStatement($name, $alias);
150 2
	}
151
152 6
	protected function visitMethod(ClassMethod $node) {
153 6
		$m = new PhpMethod($node->name);
154 6
		$m->setAbstract($node->isAbstract());
155 6
		$m->setFinal($node->isFinal());
156 6
		$m->setVisibility($this->getVisibility($node));
157 6
		$m->setStatic($node->isStatic());
158 6
		$m->setReferenceReturned($node->returnsByRef());
159
160
		// docblock
161 6
		if (($doc = $node->getDocComment()) !== null) {
162 3
			$m->setDocblock($doc->getReformattedText());
163 3
			$docblock = $m->getDocblock();
164 3
			$m->setDescription($docblock->getShortDescription());
165 3
			$m->setLongDescription($docblock->getLongDescription());
166 3
		}
167
168
		// params
169 6
		$params = $m->getDocblock()->getTags('param');
170 6
		foreach ($node->params as $param) {
171
			/* @var $param Param */
172
173 3
			$p = new PhpParameter();
174 3
			$p->setName($param->name);
175 3
			$p->setPassedByReference($param->byRef);
176
177 3
			if (is_string($param->type)) {
178 1
				$p->setType($param->type);
179 3
			} else if ($param->type instanceof Name) {
180 1
				$p->setType(implode('\\', $param->type->parts));
181 1
			}
182
183 3
			$default = $param->default;
184 3
			if ($default !== null) {
185 3
				$this->setDefault($p, $default);
186 3
			}
187
188 3
			$tag = $params->find($p, function (ParamTag $t, $p) {
189 2
				return $t->getVariable() == '$' . $p->getName();
190 3
			});
191
192 3
			if ($tag !== null) {
193 2
				$p->setType($tag->getType(), $tag->getDescription());
194 2
			}
195
196 3
			$m->addParameter($p);
197 6
		}
198
199
		// return type and description
200 6
		$returns = $m->getDocblock()->getTags('return');
201 6
		if ($returns->size() > 0) {
202 1
			$return = $returns->get(0);
203 1
			$m->setType($return->getType(), $return->getDescription());
204 1
		}
205
206
		// body
207 6
		$stmts = $node->getStmts();
208 6
		if (is_array($stmts) && count($stmts)) {
209 1
			$prettyPrinter = new Standard();
210 1
			$m->setBody($prettyPrinter->prettyPrint($stmts));
211 1
		}
212
213 6
		$this->struct->setMethod($m);
214 6
	}
215
216 4
	protected function getProperty(Property $node) {
217 4
		$prop = $node->props[0];
218
219 4
		$p = new PhpProperty($prop->name);
220
221 4
		$default = $prop->default;
222 4
		if ($default !== null) {
223 3
			$this->setDefault($p, $default);
224 3
		}
225
226 4
		$p->setStatic($node->isStatic());
227 4
		$p->setVisibility($this->getVisibility($node));
228
229 4
		$this->parseMemberDocblock($p, $node->getDocComment());
230
231 4
		return $p;
232
	}
233
234 5
	private function parseMemberDocblock($member, Doc $doc = null) {
235 5
		if ($doc !== null) {
236 2
			$member->setDocblock($doc->getReformattedText());
237 2
			$docblock = $member->getDocblock();
238 2
			$member->setDescription($docblock->getShortDescription());
239 2
			$member->setLongDescription($docblock->getLongDescription());
240
241 2
			$vars = $docblock->getTags('var');
242 2
			if ($vars->size() > 0) {
243 2
				$var = $vars->get(0);
244 2
				$member->setType($var->getType(), $var->getDescription());
245 2
			}
246 2
		}
247 5
	}
248
249 4
	private function setDefault($obj, Node $default) {
250 4
		if ($default instanceof String_) {
251 4
			$obj->setValue($this->getValue($default));
252 4
		} else {
253 3
			$obj->setExpression($this->getValue($default));
254
		}
255 4
	}
256
257
	/**
258
	 * Returns the value from a node
259
	 *
260
	 * @param Node $node
261
	 * @return mixed
262
	 */
263 4
	private function getValue(Node $node) {
264 4
		if ($node instanceof ConstFetch) {
265 2
			$const = $node->name->parts[0];
266 2
			if (isset($this->constMap[$const])) {
267 2
				return $this->constMap[$const];
268
			}
269
270 1
			return $const;
271
		}
272
273 4
		if ($node instanceof String_ || $node instanceof LNumber || $node instanceof DNumber) {
274 4
			return $node->value;
275
		}
276
277
		if ($node instanceof MagicConst) {
278
			return $node->getName();
279
		}
280
	}
281
282
	/**
283
	 * Returns the visibility from a node
284
	 *
285
	 * @param Node $node
286
	 * @return string
287
	 */
288 6
	private function getVisibility(Node $node) {
289 6
		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...
290 4
			return AbstractPhpMember::VISIBILITY_PRIVATE;
291
		}
292
293 6
		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...
294 1
			return AbstractPhpMember::VISIBILITY_PROTECTED;
295
		}
296
297 6
		return AbstractPhpMember::VISIBILITY_PUBLIC;
298
	}
299
300
}
301