TagFilterTest   A
last analyzed

Complexity

Total Complexity 4

Size/Duplication

Total Lines 260
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 6

Importance

Changes 0
Metric Value
wmc 4
lcom 2
cbo 6
dl 0
loc 260
rs 10
c 0
b 0
f 0

4 Methods

Rating   Name   Duplication   Size   Complexity  
A testFilterFeature() 0 25 1
A testIsFeatureMatchFilter() 0 48 1
B testIsScenarioMatchFilter() 0 93 1
B testFilterFeatureWithTaggedExamples() 0 88 1
1
<?php
2
3
namespace Tests\Behat\Gherkin\Filter;
4
5
use Behat\Gherkin\Filter\TagFilter;
6
use Behat\Gherkin\Node\ExampleTableNode;
7
use Behat\Gherkin\Node\FeatureNode;
8
use Behat\Gherkin\Node\OutlineNode;
9
use Behat\Gherkin\Node\ScenarioNode;
10
use PHPUnit\Framework\TestCase;
11
12
class TagFilterTest extends TestCase
13
{
14
    public function testFilterFeature()
15
    {
16
        $feature = new FeatureNode(null, null, array('wip'), null, array(), null, null, null, 1);
17
        $filter = new TagFilter('@wip');
18
        $this->assertEquals($feature, $filter->filterFeature($feature));
19
20
        $scenarios = array(
21
            new ScenarioNode(null, array(), array(), null, 2),
22
            $matchedScenario = new ScenarioNode(null, array('wip'), array(), null, 4)
23
        );
24
        $feature = new FeatureNode(null, null, array(), null, $scenarios, null, null, null, 1);
25
        $filteredFeature = $filter->filterFeature($feature);
26
27
        $this->assertSame(array($matchedScenario), $filteredFeature->getScenarios());
28
29
        $filter = new TagFilter('~@wip');
30
        $scenarios = array(
31
            $matchedScenario = new ScenarioNode(null, array(), array(), null, 2),
32
            new ScenarioNode(null, array('wip'), array(), null, 4)
33
        );
34
        $feature = new FeatureNode(null, null, array(), null, $scenarios, null, null, null, 1);
35
        $filteredFeature = $filter->filterFeature($feature);
36
37
        $this->assertSame(array($matchedScenario), $filteredFeature->getScenarios());
38
    }
39
40
    public function testIsFeatureMatchFilter()
41
    {
42
        $feature = new FeatureNode(null, null, array(), null, array(), null, null, null, 1);
43
44
        $filter = new TagFilter('@wip');
45
        $this->assertFalse($filter->isFeatureMatch($feature));
46
47
        $feature = new FeatureNode(null, null, array('wip'), null, array(), null, null, null, 1);
48
        $this->assertTrue($filter->isFeatureMatch($feature));
49
50
        $filter = new TagFilter('~@done');
51
        $this->assertTrue($filter->isFeatureMatch($feature));
52
53
        $feature = new FeatureNode(null, null, array('wip', 'done'), null, array(), null, null, null, 1);
54
        $this->assertFalse($filter->isFeatureMatch($feature));
55
56
        $feature = new FeatureNode(null, null, array('tag1', 'tag2', 'tag3'), null, array(), null, null, null, 1);
57
        $filter = new TagFilter('@tag5,@tag4,@tag6');
58
        $this->assertFalse($filter->isFeatureMatch($feature));
59
60
        $feature = new FeatureNode(null, null, array(
61
            'tag1',
62
            'tag2',
63
            'tag3',
64
            'tag5'
65
        ), null, array(), null, null, null, 1);
66
        $this->assertTrue($filter->isFeatureMatch($feature));
67
68
        $filter = new TagFilter('@wip&&@vip');
69
        $feature = new FeatureNode(null, null, array('wip', 'done'), null, array(), null, null, null, 1);
70
        $this->assertFalse($filter->isFeatureMatch($feature));
71
72
        $feature = new FeatureNode(null, null, array('wip', 'done', 'vip'), null, array(), null, null, null, 1);
73
        $this->assertTrue($filter->isFeatureMatch($feature));
74
75
        $filter = new TagFilter('@wip,@vip&&@user');
76
        $feature = new FeatureNode(null, null, array('wip'), null, array(), null, null, null, 1);
77
        $this->assertFalse($filter->isFeatureMatch($feature));
78
79
        $feature = new FeatureNode(null, null, array('vip'), null, array(), null, null, null, 1);
80
        $this->assertFalse($filter->isFeatureMatch($feature));
81
82
        $feature = new FeatureNode(null, null, array('wip', 'user'), null, array(), null, null, null, 1);
83
        $this->assertTrue($filter->isFeatureMatch($feature));
84
85
        $feature = new FeatureNode(null, null, array('vip', 'user'), null, array(), null, null, null, 1);
86
        $this->assertTrue($filter->isFeatureMatch($feature));
87
    }
88
89
    public function testIsScenarioMatchFilter()
90
    {
91
        $feature = new FeatureNode(null, null, array('feature-tag'), null, array(), null, null, null, 1);
92
        $scenario = new ScenarioNode(null, array(), array(), null, 2);
93
94
        $filter = new TagFilter('@wip');
95
        $this->assertFalse($filter->isScenarioMatch($feature, $scenario));
96
97
        $filter = new TagFilter('~@done');
98
        $this->assertTrue($filter->isScenarioMatch($feature, $scenario));
99
100
        $scenario = new ScenarioNode(null, array(
101
            'tag1',
102
            'tag2',
103
            'tag3'
104
        ), array(), null, 2);
105
        $filter = new TagFilter('@tag5,@tag4,@tag6');
106
        $this->assertFalse($filter->isScenarioMatch($feature, $scenario));
107
108
        $scenario = new ScenarioNode(null, array(
109
            'tag1',
110
            'tag2',
111
            'tag3',
112
            'tag5'
113
        ), array(), null, 2);
114
        $this->assertTrue($filter->isScenarioMatch($feature, $scenario));
115
116
        $filter = new TagFilter('@wip&&@vip');
117
        $scenario = new ScenarioNode(null, array('wip', 'not-done'), array(), null, 2);
118
        $this->assertFalse($filter->isScenarioMatch($feature, $scenario));
119
120
        $scenario = new ScenarioNode(null, array(
121
            'wip',
122
            'not-done',
123
            'vip'
124
        ), array(), null, 2);
125
        $this->assertTrue($filter->isScenarioMatch($feature, $scenario));
126
127
        $filter = new TagFilter('@wip,@vip&&@user');
128
        $scenario = new ScenarioNode(null, array(
129
            'wip'
130
        ), array(), null, 2);
131
        $this->assertFalse($filter->isScenarioMatch($feature, $scenario));
132
133
        $scenario = new ScenarioNode(null, array('vip'), array(), null, 2);
134
        $this->assertFalse($filter->isScenarioMatch($feature, $scenario));
135
136
        $scenario = new ScenarioNode(null, array('wip', 'user'), array(), null, 2);
137
        $this->assertTrue($filter->isScenarioMatch($feature, $scenario));
138
139
        $filter = new TagFilter('@feature-tag&&@user');
140
        $scenario = new ScenarioNode(null, array('wip', 'user'), array(), null, 2);
141
        $this->assertTrue($filter->isScenarioMatch($feature, $scenario));
142
143
        $filter = new TagFilter('@feature-tag&&@user');
144
        $scenario = new ScenarioNode(null, array('wip'), array(), null, 2);
145
        $this->assertFalse($filter->isScenarioMatch($feature, $scenario));
146
147
        $scenario = new OutlineNode(null, array('wip'), array(), array(
148
            new ExampleTableNode(array(), null, array('etag1', 'etag2')),
149
            new ExampleTableNode(array(), null, array('etag2', 'etag3')),
150
        ), null, 2);
151
152
        $tagFilter = new TagFilter('@etag3');
153
        $this->assertTrue($tagFilter->isScenarioMatch($feature, $scenario));
154
155
        $tagFilter = new TagFilter('~@etag3');
156
        $this->assertTrue($tagFilter->isScenarioMatch($feature, $scenario));
157
158
        $tagFilter = new TagFilter('@wip');
159
        $this->assertTrue($tagFilter->isScenarioMatch($feature, $scenario));
160
161
        $tagFilter = new TagFilter('@wip&&@etag3');
162
        $this->assertTrue($tagFilter->isScenarioMatch($feature, $scenario));
163
164
        $tagFilter = new TagFilter('@feature-tag&&@etag1&&@wip');
165
        $this->assertTrue($tagFilter->isScenarioMatch($feature, $scenario));
166
167
        $tagFilter = new TagFilter('@feature-tag&&~@etag11111&&@wip');
168
        $this->assertTrue($tagFilter->isScenarioMatch($feature, $scenario));
169
170
        $tagFilter = new TagFilter('@feature-tag&&~@etag1&&@wip');
171
        $this->assertTrue($tagFilter->isScenarioMatch($feature, $scenario));
172
173
        $tagFilter = new TagFilter('@feature-tag&&@etag2');
174
        $this->assertTrue($tagFilter->isScenarioMatch($feature, $scenario));
175
176
        $tagFilter = new TagFilter('~@etag1&&~@etag3');
177
        $this->assertFalse($tagFilter->isScenarioMatch($feature, $scenario));
178
179
        $tagFilter = new TagFilter('@etag1&&@etag3');
180
        $this->assertFalse($tagFilter->isScenarioMatch($feature, $scenario), "Tags from different examples tables");
181
    }
182
183
    public function testFilterFeatureWithTaggedExamples()
184
    {
185
        $exampleTableNode1 = new ExampleTableNode(array(), null, array('etag1', 'etag2'));
186
        $exampleTableNode2 = new ExampleTableNode(array(), null, array('etag2', 'etag3'));
187
        $scenario = new OutlineNode(null, array('wip'), array(), array(
188
            $exampleTableNode1,
189
            $exampleTableNode2,
190
        ), null, 2);
191
        $feature = new FeatureNode(null, null, array('feature-tag'), null, array($scenario), null, null, null, 1);
192
193
        $tagFilter = new TagFilter('@etag2');
194
        $matched = $tagFilter->filterFeature($feature);
195
        $scenarioInterfaces = $matched->getScenarios();
196
        $this->assertEquals($scenario, $scenarioInterfaces[0]);
197
198
        $tagFilter = new TagFilter('@etag1');
199
        $matched = $tagFilter->filterFeature($feature);
200
        $scenarioInterfaces = $matched->getScenarios();
201
        /** @noinspection PhpUndefinedMethodInspection */
202
        $this->assertEquals(array($exampleTableNode1), $scenarioInterfaces[0]->getExampleTables());
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Behat\Gherkin\Node\ScenarioInterface as the method getExampleTables() does only exist in the following implementations of said interface: Behat\Gherkin\Node\OutlineNode.

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...
203
204
        $tagFilter = new TagFilter('~@etag3');
205
        $matched = $tagFilter->filterFeature($feature);
206
        $scenarioInterfaces = $matched->getScenarios();
207
        /** @noinspection PhpUndefinedMethodInspection */
208
        $this->assertEquals(array($exampleTableNode1), $scenarioInterfaces[0]->getExampleTables());
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Behat\Gherkin\Node\ScenarioInterface as the method getExampleTables() does only exist in the following implementations of said interface: Behat\Gherkin\Node\OutlineNode.

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...
209
210
        $tagFilter = new TagFilter('@wip');
211
        $matched = $tagFilter->filterFeature($feature);
212
        $scenarioInterfaces = $matched->getScenarios();
213
        $this->assertEquals($scenario, $scenarioInterfaces[0]);
214
215
        $tagFilter = new TagFilter('@wip&&@etag3');
216
        $matched = $tagFilter->filterFeature($feature);
217
        $scenarioInterfaces = $matched->getScenarios();
218
        /** @noinspection PhpUndefinedMethodInspection */
219
        $this->assertEquals(array($exampleTableNode2), $scenarioInterfaces[0]->getExampleTables());
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Behat\Gherkin\Node\ScenarioInterface as the method getExampleTables() does only exist in the following implementations of said interface: Behat\Gherkin\Node\OutlineNode.

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...
220
221
        $tagFilter = new TagFilter('@feature-tag&&@etag1&&@wip');
222
        $matched = $tagFilter->filterFeature($feature);
223
        $scenarioInterfaces = $matched->getScenarios();
224
        /** @noinspection PhpUndefinedMethodInspection */
225
        $this->assertEquals(array($exampleTableNode1), $scenarioInterfaces[0]->getExampleTables());
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Behat\Gherkin\Node\ScenarioInterface as the method getExampleTables() does only exist in the following implementations of said interface: Behat\Gherkin\Node\OutlineNode.

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...
226
227
        $tagFilter = new TagFilter('@feature-tag&&~@etag11111&&@wip');
228
        $matched = $tagFilter->filterFeature($feature);
229
        $scenarioInterfaces = $matched->getScenarios();
230
        $this->assertEquals($scenario, $scenarioInterfaces[0]);
231
232
        $tagFilter = new TagFilter('@feature-tag&&~@etag1&&@wip');
233
        $matched = $tagFilter->filterFeature($feature);
234
        $scenarioInterfaces = $matched->getScenarios();
235
        /** @noinspection PhpUndefinedMethodInspection */
236
        $this->assertEquals(array($exampleTableNode2), $scenarioInterfaces[0]->getExampleTables());
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Behat\Gherkin\Node\ScenarioInterface as the method getExampleTables() does only exist in the following implementations of said interface: Behat\Gherkin\Node\OutlineNode.

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...
237
238
        $tagFilter = new TagFilter('@feature-tag&&@etag2');
239
        $matched = $tagFilter->filterFeature($feature);
240
        $scenarioInterfaces = $matched->getScenarios();
241
        $this->assertEquals($scenario, $scenarioInterfaces[0]);
242
243
        $exampleTableNode1 = new ExampleTableNode(array(), null, array('etag1', 'etag'));
244
        $exampleTableNode2 = new ExampleTableNode(array(), null, array('etag2', 'etag22', 'etag'));
245
        $exampleTableNode3 = new ExampleTableNode(array(), null, array('etag3', 'etag22', 'etag'));
246
        $exampleTableNode4 = new ExampleTableNode(array(), null, array('etag4', 'etag'));
247
        $scenario1 = new OutlineNode(null, array('wip'), array(), array(
248
            $exampleTableNode1,
249
            $exampleTableNode2,
250
        ), null, 2);
251
        $scenario2 = new OutlineNode(null, array('wip'), array(), array(
252
            $exampleTableNode3,
253
            $exampleTableNode4,
254
        ), null, 2);
255
        $feature = new FeatureNode(null, null, array('feature-tag'), null, array($scenario1, $scenario2), null, null, null, 1);
256
257
        $tagFilter = new TagFilter('@etag');
258
        $matched = $tagFilter->filterFeature($feature);
259
        $scenarioInterfaces = $matched->getScenarios();
260
        $this->assertEquals(array($scenario1, $scenario2), $scenarioInterfaces);
261
262
        $tagFilter = new TagFilter('@etag22');
263
        $matched = $tagFilter->filterFeature($feature);
264
        $scenarioInterfaces = $matched->getScenarios();
265
        $this->assertEquals(2, count($scenarioInterfaces));
266
        /** @noinspection PhpUndefinedMethodInspection */
267
        $this->assertEquals(array($exampleTableNode2), $scenarioInterfaces[0]->getExampleTables());
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Behat\Gherkin\Node\ScenarioInterface as the method getExampleTables() does only exist in the following implementations of said interface: Behat\Gherkin\Node\OutlineNode.

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...
268
        /** @noinspection PhpUndefinedMethodInspection */
269
        $this->assertEquals(array($exampleTableNode3), $scenarioInterfaces[1]->getExampleTables());
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Behat\Gherkin\Node\ScenarioInterface as the method getExampleTables() does only exist in the following implementations of said interface: Behat\Gherkin\Node\OutlineNode.

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...
270
    }
271
}
272