Completed
Push — master ( 2976ab...f587d3 )
by Tim
11s
created

EeProductBunchProcessor::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 65
Code Lines 58

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
dl 0
loc 65
ccs 0
cts 61
cp 0
rs 9.3571
c 1
b 0
f 0
cc 1
eloc 58
nc 1
nop 28
crap 2

How to fix   Long Method    Many Parameters   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
3
/**
4
 * TechDivision\Import\Product\Ee\Services\EeProductBunchProcessor
5
 *
6
 * NOTICE OF LICENSE
7
 *
8
 * This source file is subject to the Open Software License (OSL 3.0)
9
 * that is available through the world-wide-web at this URL:
10
 * http://opensource.org/licenses/osl-3.0.php
11
 *
12
 * PHP version 5
13
 *
14
 * @author    Tim Wagner <[email protected]>
15
 * @copyright 2016 TechDivision GmbH <[email protected]>
16
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
17
 * @link      https://github.com/techdivision/import-product-ee
18
 * @link      http://www.techdivision.com
19
 */
20
21
namespace TechDivision\Import\Product\Ee\Services;
22
23
use TechDivision\Import\Product\Services\ProductBunchProcessor;
24
use TechDivision\Import\Product\Ee\Actions\SequenceProductAction;
25
use TechDivision\Import\Actions\UrlRewriteAction;
26
use TechDivision\Import\Repositories\UrlRewriteRepository;
27
use TechDivision\Import\Product\Repositories\ProductRepository;
28
use TechDivision\Import\Product\Repositories\ProductWebsiteRepository;
29
use TechDivision\Import\Product\Ee\Repositories\ProductDatetimeRepository;
30
use TechDivision\Import\Product\Ee\Repositories\ProductDecimalRepository;
31
use TechDivision\Import\Product\Ee\Repositories\ProductIntRepository;
32
use TechDivision\Import\Product\Ee\Repositories\ProductTextRepository;
33
use TechDivision\Import\Product\Ee\Repositories\ProductVarcharRepository;
34
use TechDivision\Import\Product\Repositories\CategoryProductRepository;
35
use TechDivision\Import\Product\Repositories\StockStatusRepository;
36
use TechDivision\Import\Product\Repositories\StockItemRepository;
37
use TechDivision\Import\Product\Repositories\UrlRewriteProductCategoryRepository;
38
use TechDivision\Import\Repositories\EavAttributeOptionValueRepository;
39
use TechDivision\Import\Repositories\EavAttributeRepository;
40
use TechDivision\Import\Product\Actions\CategoryProductAction;
41
use TechDivision\Import\Product\Actions\ProductDatetimeAction;
42
use TechDivision\Import\Product\Actions\ProductDecimalAction;
43
use TechDivision\Import\Product\Actions\ProductIntAction;
44
use TechDivision\Import\Product\Actions\ProductAction;
45
use TechDivision\Import\Product\Actions\ProductTextAction;
46
use TechDivision\Import\Product\Actions\ProductVarcharAction;
47
use TechDivision\Import\Product\Actions\ProductWebsiteAction;
48
use TechDivision\Import\Product\Actions\StockItemAction;
49
use TechDivision\Import\Product\Actions\StockStatusAction;
50
use TechDivision\Import\Product\Actions\UrlRewriteProductCategoryAction;
51
52
/**
53
 * A SLSB providing methods to load sequence product data using a PDO connection.
54
 *
55
 * @author    Tim Wagner <[email protected]>
56
 * @copyright 2016 TechDivision GmbH <[email protected]>
57
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
58
 * @link      https://github.com/techdivision/import-product-ee
59
 * @link      http://www.techdivision.com
60
 */
61
class EeProductBunchProcessor extends ProductBunchProcessor implements EeProductBunchProcessorInterface
62
{
63
64
    /**
65
     * The action for sequence product CRUD methods.
66
     *
67
     * @var \TechDivision\Import\Product\Ee\Actions\SequenceProductAction
68
     */
69
    protected $sequenceProductAction;
70
    /**
71
     * Initialize the processor with the necessary assembler and repository instances.
72
     *
73
     * @param \PDO                                                                        $connection                          The PDO connection to use
74
     * @param \TechDivision\Import\Product\Ee\Actions\SequenceProductAction               $sequenceProductAction               The sequence product action to use
75
     * @param \TechDivision\Import\Product\Repositories\ProductRepository                 $productRepository                   The product repository to use
76
     * @param \TechDivision\Import\Product\Repositories\ProductWebsiteRepository          $productWebsiteRepository            The product website repository to use
77
     * @param \TechDivision\Import\Product\Repositories\ProductDatetimeRepository         $productDatetimeRepository           The product datetime repository to use
78
     * @param \TechDivision\Import\Product\Repositories\ProductDecimalRepository          $productDecimalRepository            The product decimal repository to use
79
     * @param \TechDivision\Import\Product\Repositories\ProductIntRepository              $productIntRepository                The product integer repository to use
80
     * @param \TechDivision\Import\Product\Repositories\ProductTextRepository             $productTextRepository               The product text repository to use
81
     * @param \TechDivision\Import\Product\Repositories\ProductVarcharRepository          $productVarcharRepository            The product varchar repository to use
82
     * @param \TechDivision\Import\Product\Repositories\CategoryProductRepository         $categoryProductRepository           The category product repository to use
83
     * @param \TechDivision\Import\Product\Repositories\StockStatusRepository             $stockStatusRepository               The stock status repository to use
84
     * @param \TechDivision\Import\Product\Repositories\StockItemRepository               $stockItemRepository                 The stock item repository to use
85
     * @param \TechDivision\Import\Repositories\UrlRewriteRepository                      $urlRewriteRepository                The URL rewrite repository to use
86
     * @param \TechDivision\Import\Repositories\UrlRewriteRepository                      $urlRewriteProductCategoryRepository The URL rewrite product category repository to use
87
     * @param \TechDivision\Import\Product\Repositories\EavAttributeOptionValueRepository $eavAttributeOptionValueRepository   The EAV attribute option value repository to use
88
     * @param \TechDivision\Import\Repositories\EavAttributeRepository                    $eavAttributeRepository              The EAV attribute repository to use
89
     * @param \TechDivision\Import\Product\Actions\CategoryProductAction                  $categoryProductAction               The category product action to use
90
     * @param \TechDivision\Import\Product\Actions\ProductDatetimeAction                  $productDatetimeAction               The product datetime action to use
91
     * @param \TechDivision\Import\Product\Actions\ProductDecimalAction                   $productDecimalAction                The product decimal action to use
92
     * @param \TechDivision\Import\Product\Actions\ProductIntAction                       $productIntAction                    The product integer action to use
93
     * @param \TechDivision\Import\Product\Actions\ProductAction                          $productAction                       The product action to use
94
     * @param \TechDivision\Import\Product\Actions\ProductTextAction                      $productTextAction                   The product text action to use
95
     * @param \TechDivision\Import\Product\Actions\ProductVarcharAction                   $productVarcharAction                The product varchar action to use
96
     * @param \TechDivision\Import\Product\Actions\ProductWebsiteAction                   $productWebsiteAction                The product website action to use
97
     * @param \TechDivision\Import\Product\Actions\StockItemAction                        $stockItemAction                     The stock item action to use
98
     * @param \TechDivision\Import\Product\Actions\StockStatusAction                      $stockStatusAction                   The stock status action to use
99
     * @param \TechDivision\Import\Actions\UrlRewriteAction                               $urlRewriteAction                    The URL rewrite action to use
100
     * @param \TechDivision\Import\Product\Actions\UrlRewriteProductCategoryAction        $urlRewriteProductCategoryAction     The URL rewrite product category action to use
101
     */
102
    public function __construct(
103
        \PDO $connection,
104
        SequenceProductAction $sequenceProductAction,
105
        ProductRepository $productRepository,
106
        ProductWebsiteRepository $productWebsiteRepository,
107
        ProductDatetimeRepository $productDatetimeRepository,
108
        ProductDecimalRepository $productDecimalRepository,
109
        ProductIntRepository $productIntRepository,
110
        ProductTextRepository $productTextRepository,
111
        ProductVarcharRepository $productVarcharRepository,
112
        CategoryProductRepository $categoryProductRepository,
113
        StockStatusRepository $stockStatusRepository,
114
        StockItemRepository $stockItemRepository,
115
        UrlRewriteRepository $urlRewriteRepository,
116
        UrlRewriteProductCategoryRepository $urlRewriteProductCategoryRepository,
117
        EavAttributeOptionValueRepository $eavAttributeOptionValueRepository,
118
        EavAttributeRepository $eavAttributeRepository,
119
        CategoryProductAction $categoryProductAction,
120
        ProductDatetimeAction $productDatetimeAction,
121
        ProductDecimalAction $productDecimalAction,
122
        ProductIntAction $productIntAction,
123
        ProductAction $productAction,
124
        ProductTextAction $productTextAction,
125
        ProductVarcharAction $productVarcharAction,
126
        ProductWebsiteAction $productWebsiteAction,
127
        StockItemAction $stockItemAction,
128
        StockStatusAction $stockStatusAction,
129
        UrlRewriteAction $urlRewriteAction,
130
        UrlRewriteProductCategoryAction $urlRewriteProductCategoryAction
131
    ) {
132
133
        // set the sequence product action
134
        $this->setSequenceProductAction($sequenceProductAction);
135
136
        // call parent constructor
137
        parent::__construct(
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class TechDivision\Import\Prod...s\ProductBunchProcessor as the method __construct() does only exist in the following sub-classes of TechDivision\Import\Prod...s\ProductBunchProcessor: TechDivision\Import\Prod...EeProductBunchProcessor. 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...
138
            $connection,
139
            $productRepository,
140
            $productWebsiteRepository,
141
            $productDatetimeRepository,
142
            $productDecimalRepository,
143
            $productIntRepository,
144
            $productTextRepository,
145
            $productVarcharRepository,
146
            $categoryProductRepository,
147
            $stockStatusRepository,
148
            $stockItemRepository,
149
            $urlRewriteRepository,
150
            $urlRewriteProductCategoryRepository,
151
            $eavAttributeOptionValueRepository,
152
            $eavAttributeRepository,
153
            $categoryProductAction,
154
            $productDatetimeAction,
155
            $productDecimalAction,
156
            $productIntAction,
157
            $productAction,
158
            $productTextAction,
159
            $productVarcharAction,
160
            $productWebsiteAction,
161
            $stockItemAction,
162
            $stockStatusAction,
163
            $urlRewriteAction,
164
            $urlRewriteProductCategoryAction
165
        );
166
    }
167
168
    /**
169
     * Set's the action with the sequence product CRUD methods.
170
     *
171
     * @param \TechDivision\Import\Product\Ee\Actions\SequenceProductAction $sequenceProductAction The action with the sequence product CRUD methods
172
     *
173
     * @return void
174
     */
175
    public function setSequenceProductAction($sequenceProductAction)
176
    {
177
        $this->sequenceProductAction = $sequenceProductAction;
178
    }
179
180
    /**
181
     * Return's the action with the sequence product CRUD methods.
182
     *
183
     * @return \TechDivision\Import\Product\Ee\Actions\SequenceProductAction The action instance
184
     */
185
    public function getSequenceProductAction()
186
    {
187
        return $this->sequenceProductAction;
188
    }
189
190
    /**
191
     * Return's the next available product entity ID.
192
     *
193
     * @return integer The next available product entity ID
194
     */
195
    public function nextIdentifier()
196
    {
197
        return $this->getSequenceProductAction()->nextIdentifier();
198
    }
199
200
    /**
201
     * Load's and return's the datetime attribute with the passed row/attribute/store ID.
202
     *
203
     * @param integer $rowId       The row ID of the attribute
204
     * @param integer $attributeId The attribute ID of the attribute
205
     * @param integer $storeId     The store ID of the attribute
206
     *
207
     * @return array|null The datetime attribute
208
     */
209
    public function loadProductDatetimeAttributeByRowIdAndAttributeIdAndStoreId($rowId, $attributeId, $storeId)
210
    {
211
        return  $this->getProductDatetimeRepository()->findOneByRowIdAndAttributeIdAndStoreId($rowId, $attributeId, $storeId);
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class TechDivision\Import\Prod...oductDatetimeRepository as the method findOneByRowIdAndAttributeIdAndStoreId() does only exist in the following sub-classes of TechDivision\Import\Prod...oductDatetimeRepository: TechDivision\Import\Prod...oductDatetimeRepository. 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...
212
    }
213
214
    /**
215
     * Load's and return's the decimal attribute with the passed row/attribute/store ID.
216
     *
217
     * @param integer $rowId       The row ID of the attribute
218
     * @param integer $attributeId The attribute ID of the attribute
219
     * @param integer $storeId     The store ID of the attribute
220
     *
221
     * @return array|null The decimal attribute
222
     */
223
    public function loadProductDecimalAttributeByRowIdAndAttributeIdAndStoreId($rowId, $attributeId, $storeId)
224
    {
225
        return  $this->getProductDecimalRepository()->findOneByRowIdAndAttributeIdAndStoreId($rowId, $attributeId, $storeId);
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class TechDivision\Import\Prod...roductDecimalRepository as the method findOneByRowIdAndAttributeIdAndStoreId() does only exist in the following sub-classes of TechDivision\Import\Prod...roductDecimalRepository: TechDivision\Import\Prod...roductDecimalRepository. 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...
226
    }
227
228
    /**
229
     * Load's and return's the integer attribute with the passed row/attribute/store ID.
230
     *
231
     * @param integer $rowId       The row ID of the attribute
232
     * @param integer $attributeId The attribute ID of the attribute
233
     * @param integer $storeId     The store ID of the attribute
234
     *
235
     * @return array|null The integer attribute
236
     */
237
    public function loadProductIntAttributeByRowIdAndAttributeIdAndStoreId($rowId, $attributeId, $storeId)
238
    {
239
        return $this->getProductIntRepository()->findOneByRowIdAndAttributeIdAndStoreId($rowId, $attributeId, $storeId);
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class TechDivision\Import\Prod...es\ProductIntRepository as the method findOneByRowIdAndAttributeIdAndStoreId() does only exist in the following sub-classes of TechDivision\Import\Prod...es\ProductIntRepository: TechDivision\Import\Prod...es\ProductIntRepository. 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...
240
    }
241
242
    /**
243
     * Load's and return's the text attribute with the passed row/attribute/store ID.
244
     *
245
     * @param integer $rowId       The row ID of the attribute
246
     * @param integer $attributeId The attribute ID of the attribute
247
     * @param integer $storeId     The store ID of the attribute
248
     *
249
     * @return array|null The text attribute
250
     */
251
    public function loadProductTextAttributeByRowIdAndAttributeIdAndStoreId($rowId, $attributeId, $storeId)
252
    {
253
        return $this->getProductTextRepository()->findOneByRowIdAndAttributeIdAndStoreId($rowId, $attributeId, $storeId);
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class TechDivision\Import\Prod...s\ProductTextRepository as the method findOneByRowIdAndAttributeIdAndStoreId() does only exist in the following sub-classes of TechDivision\Import\Prod...s\ProductTextRepository: TechDivision\Import\Prod...s\ProductTextRepository. 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...
254
    }
255
256
    /**
257
     * Load's and return's the varchar attribute with the passed row/attribute/store ID.
258
     *
259
     * @param integer $rowId       The row ID of the attribute
260
     * @param integer $attributeId The attribute ID of the attribute
261
     * @param integer $storeId     The store ID of the attribute
262
     *
263
     * @return array|null The varchar attribute
264
     */
265
    public function loadProductVarcharAttributeByRowIdAndAttributeIdAndStoreId($rowId, $attributeId, $storeId)
266
    {
267
        return $this->getProductVarcharRepository()->findOneByRowIdAndAttributeIdAndStoreId($rowId, $attributeId, $storeId);
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class TechDivision\Import\Prod...roductVarcharRepository as the method findOneByRowIdAndAttributeIdAndStoreId() does only exist in the following sub-classes of TechDivision\Import\Prod...roductVarcharRepository: TechDivision\Import\Prod...roductVarcharRepository. 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...
268
    }
269
}
270