Completed
Push — master ( 40a934...77b4e9 )
by
unknown
02:28
created

ProductUrlRewriteObserver::createArtefact()   B

Complexity

Conditions 6
Paths 17

Size

Total Lines 39

Duplication

Lines 12
Ratio 30.77 %

Code Coverage

Tests 0
CRAP Score 42

Importance

Changes 0
Metric Value
dl 12
loc 39
ccs 0
cts 36
cp 0
rs 8.6737
c 0
b 0
f 0
cc 6
nc 17
nop 2
crap 42
1
<?php
2
3
/**
4
 * TechDivision\Import\Product\UrlRewrite\Observers\ProductUrlRewriteObserver
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-url-rewrite
18
 * @link      http://www.techdivision.com
19
 */
20
21
namespace TechDivision\Import\Product\UrlRewrite\Observers;
22
23
use TechDivision\Import\Observers\StateDetectorInterface;
24
use TechDivision\Import\Product\UrlRewrite\Services\ProductUrlRewriteProcessorInterface;
25
use TechDivision\Import\Utils\MemberNames;
26
use TechDivision\Import\Utils\StoreViewCodes;
27
use TechDivision\Import\Product\UrlRewrite\Utils\ColumnKeys;
28
use TechDivision\Import\Product\Observers\AbstractProductImportObserver;
29
30
/**
31
 * Observer that extracts the URL rewrite data to a specific CSV file.
32
 *
33
 * @author    Tim Wagner <[email protected]>
34
 * @copyright 2016 TechDivision GmbH <[email protected]>
35
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
36
 * @link      https://github.com/techdivision/import-product-url-rewrite
37
 * @link      http://www.techdivision.com
38
 */
39
class ProductUrlRewriteObserver extends AbstractProductImportObserver
40
{
41
42
    /**
43
     * The artefact type.
44
     *
45
     * @var string
46
     */
47
    const ARTEFACT_TYPE = 'url-rewrite';
48
49
    /**
50
     * The image artefacts that has to be exported.
51
     *
52
     * @var array
53
     */
54
    protected $artefacts = array();
55
56
    /**
57
     * The admin row for initialize artefacts that has to be exported.
58
     *
59
     * @var array
60
     */
61
    protected $adminRow = array();
62
63
    /**
64
     * The product bunch processor instance.
65
     *
66
     * @var \TechDivision\Import\Product\UrlRewrite\Services\ProductUrlRewriteProcessorInterface
67
     */
68
    protected $productUrlRewriteProcessor;
69
70
    /**
71
     * Initialize the observer with the passed product URL rewrite processor instance.
72
     *
73
     * @param \TechDivision\Import\Product\UrlRewrite\Services\ProductUrlRewriteProcessorInterface $productUrlRewriteProcessor The product URL rewrite processor instance
74
     * @param \TechDivision\Import\Observers\StateDetectorInterface|null                           $stateDetector              The state detector instance to use
75
     */
76
    public function __construct(
77
        ProductUrlRewriteProcessorInterface $productUrlRewriteProcessor,
78
        StateDetectorInterface $stateDetector = null
79
    ) {
80
        $this->productUrlRewriteProcessor = $productUrlRewriteProcessor;
81
        parent::__construct($stateDetector);
82
    }
83
84
    /**
85
     * Return's the product bunch processor instance.
86
     *
87
     * @return \TechDivision\Import\Product\UrlRewrite\Services\ProductUrlRewriteProcessorInterface The product URL rewrite processor instance
88
     */
89
    protected function getProductUrlRewriteProcessor()
90
    {
91
        return $this->productUrlRewriteProcessor;
92
    }
93
94
    /**
95
     * Process the observer's business logic.
96
     *
97
     * @return array The processed row
98
     */
99
    protected function process()
100
    {
101
        $product = null;
102
        // initialize the array for the artefacts and the store view codes
103
        $this->artefacts = array();
104
        $storeViewCodes = array();
105
106
        // load the SKU from the row
107
        $sku = $this->getValue(ColumnKeys::SKU);
108
109
        // prepare the store view code
110
        $this->getSubject()->prepareStoreViewCode();
111
112
        // try to load the store view code
113
        $storeViewCodeValue = $this->getSubject()->getStoreViewCode(StoreViewCodes::ADMIN);
114
115
        // query whether or not we've a store view code
116
        if ($storeViewCodeValue === StoreViewCodes::ADMIN) {
117
            // load product to see if already exist or new
118
            $product = $this->loadProduct($this->getValue(ColumnKeys::SKU));
119
120
            // Init admin row for memory overload
121
            $this->adminRow = array();
122
            // remember the admin row on SKU to be safe on later process
123
            $this->adminRow[$sku] = array(
124
                ColumnKeys::CATEGORIES         => $this->getValue(ColumnKeys::CATEGORIES),
125
                ColumnKeys::PRODUCT_WEBSITES   => $this->getValue(ColumnKeys::PRODUCT_WEBSITES),
126
                ColumnKeys::VISIBILITY         => $this->getValue(ColumnKeys::VISIBILITY),
127
                ColumnKeys::URL_KEY            => $this->getValue(ColumnKeys::URL_KEY)
128
            );
129
130
            // if not, load the websites the product is related with
131
            $websiteCodes = $this->getValue(ColumnKeys::PRODUCT_WEBSITES, array(), array($this, 'explode'));
132
133
            // load the store view codes of all websites
134
            foreach ($websiteCodes as $websiteCode) {
135
                $storeViewCodes = array_merge($storeViewCodes, $this->getStoreViewCodesByWebsiteCode($websiteCode));
136
            }
137
        } else {
138
            array_push($storeViewCodes, $storeViewCodeValue);
139
        }
140
141
        // iterate over the available image fields
142
        foreach ($storeViewCodes as $storeViewCode) {
143
            // iterate over the store view codes and query if artefacts are already available
144
            if ($this->hasArtefactsByTypeAndEntityId(ProductUrlRewriteObserver::ARTEFACT_TYPE, $lastEntityId = $this->getSubject()->getLastEntityId())) {
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface TechDivision\Import\Subjects\SubjectInterface as the method getLastEntityId() does only exist in the following implementations of said interface: TechDivision\Import\Observers\EntitySubjectImpl, TechDivision\Import\Plugins\ExportableSubjectImpl, TechDivision\Import\Prod...\AbstractProductSubject, TechDivision\Import\Product\Subjects\BunchSubject, TechDivision\Import\Prod...jects\UrlRewriteSubject, TechDivision\Import\Subjects\ExportableTraitImpl.

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...
145
                // if yes, load the artefacs
146
                $this->artefacts = $this->getArtefactsByTypeAndEntityId(ProductUrlRewriteObserver::ARTEFACT_TYPE, $lastEntityId);
147
148
                $foundArtefactToUpdate = false;
149
                // override the existing data with the store view specific one
150
                for ($i = 0; $i < sizeof($this->artefacts); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function sizeof() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
151
                    // query whether or not a URL key has be specfied and the store view codes are equal
152
                    if ($this->hasValue(ColumnKeys::URL_KEY) && $this->artefacts[$i][ColumnKeys::STORE_VIEW_CODE] === $storeViewCode) {
153
                        $foundArtefactToUpdate = true;
154
                        // update the URL key
155
                        $this->artefacts[$i][ColumnKeys::URL_KEY]    = $this->getValue(ColumnKeys::URL_KEY);
156
                        // update the visibility, if available
157
                        if ($this->hasValue(ColumnKeys::VISIBILITY)) {
158
                            $this->artefacts[$i][ColumnKeys::VISIBILITY] = $this->getValue(ColumnKeys::VISIBILITY);
159
                        }
160
161
                        // also update filename and line number
162
                        $this->artefacts[$i][ColumnKeys::ORIGINAL_DATA][ColumnKeys::ORIGINAL_FILENAME] = $this->getSubject()->getFilename();
163
                        $this->artefacts[$i][ColumnKeys::ORIGINAL_DATA][ColumnKeys::ORIGINAL_LINE_NUMBER] = $this->getSubject()->getLineNumber();
164
                    }
165
                }
166
                if (!$foundArtefactToUpdate) {
167
                    // if no arefacts are available, append new data
168
                    $this->createArtefact($sku, $storeViewCode);
169
                }
170
            } else {
171
                // On admin row and existing product check if url_key in database
172
                if ($storeViewCodeValue === StoreViewCodes::ADMIN && $product) {
173
                    // initialize last entity as primary key
174
                    $pk = $this->getPrimaryKeyId($product);
175
                    // initialize the entity type ID
176
                    $entityType = $this->getSubject()->getEntityType();
0 ignored issues
show
Bug introduced by
The method getEntityType() does not exist on TechDivision\Import\Subjects\SubjectInterface. Did you maybe mean getEntityTypeCode()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
177
                    $entityTypeId = (integer) $entityType[MemberNames::ENTITY_TYPE_ID];
178
                    // initialize store ID from store code
179
                    $storeId = $this->getSubject()->getRowStoreId($storeViewCode);
0 ignored issues
show
Bug introduced by
The method getRowStoreId() does not exist on TechDivision\Import\Subjects\SubjectInterface. Did you maybe mean getRow()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
180
                    // take a look if url_key already exist
181
                    $foundExistingUrlKey = $this->getProductUrlRewriteProcessor()->loadProductVarcharAttributeByAttributeCodeAndEntityTypeIdAndStoreIdAndPK(
182
                        ColumnKeys::URL_KEY,
183
                        $entityTypeId,
184
                        $storeId,
185
                        $pk
186
                    );
187
                    // if url_key attribute found and same store as searched
188
                    if ($foundExistingUrlKey && $foundExistingUrlKey[MemberNames::STORE_ID] == $storeId) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $foundExistingUrlKey of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
189
                        // skip for artefact as default entry
190
                        continue;
191
                    }
192
                }
193
                // if no arefacts are available, append new data
194
                $this->createArtefact($sku, $storeViewCode);
195
            }
196
        }
197
198
        // append the artefacts that has to be exported to the subject
199
        $this->addArtefacts($this->artefacts);
200
    }
201
202
    /**
203
     * @param array $product From loadProduct
204
     * @return mixed
205
     */
206
    protected function getPrimaryKeyId(array $product)
207
    {
208
        return $product[$this->getProductUrlRewriteProcessor()->getPrimaryKeyMemberName()];
209
    }
210
211
    /**
212
     * @param string $sku           The sku for the new url_key
213
     * @param string $storeViewCode The Storeview code
214
     *
215
     * @return void
216
     */
217
    protected function createArtefact($sku, $storeViewCode)
218
    {
219
        if (isset($this->adminRow[$sku])) {
220 View Code Duplication
            if (!$this->hasValue(ColumnKeys::CATEGORIES)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
221
                $this->setValue(ColumnKeys::CATEGORIES, $this->adminRow[$sku][ColumnKeys::CATEGORIES]);
222
            }
223 View Code Duplication
            if (!$this->hasValue(ColumnKeys::PRODUCT_WEBSITES)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
224
                $this->setValue(ColumnKeys::PRODUCT_WEBSITES, $this->adminRow[$sku][ColumnKeys::PRODUCT_WEBSITES]);
225
            }
226 View Code Duplication
            if (!$this->hasValue(ColumnKeys::VISIBILITY)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
227
                $this->setValue(ColumnKeys::VISIBILITY, $this->adminRow[$sku][ColumnKeys::VISIBILITY]);
228
            }
229 View Code Duplication
            if (!$this->hasValue(ColumnKeys::URL_KEY)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
230
                $this->setValue(ColumnKeys::URL_KEY, $this->adminRow[$sku][ColumnKeys::URL_KEY]);
231
            }
232
        }
233
234
        $artefact = $this->newArtefact(
235
            array(
236
                ColumnKeys::SKU                => $sku,
237
                ColumnKeys::STORE_VIEW_CODE    => $storeViewCode,
238
                ColumnKeys::CATEGORIES         => $this->getValue(ColumnKeys::CATEGORIES),
239
                ColumnKeys::PRODUCT_WEBSITES   => $this->getValue(ColumnKeys::PRODUCT_WEBSITES),
240
                ColumnKeys::VISIBILITY         => $this->getValue(ColumnKeys::VISIBILITY),
241
                ColumnKeys::URL_KEY            => $this->getValue(ColumnKeys::URL_KEY)
242
            ),
243
            array(
244
                ColumnKeys::SKU                => ColumnKeys::SKU,
245
                ColumnKeys::STORE_VIEW_CODE    => ColumnKeys::STORE_VIEW_CODE,
246
                ColumnKeys::CATEGORIES         => ColumnKeys::CATEGORIES,
247
                ColumnKeys::PRODUCT_WEBSITES   => ColumnKeys::PRODUCT_WEBSITES,
248
                ColumnKeys::VISIBILITY         => ColumnKeys::VISIBILITY,
249
                ColumnKeys::URL_KEY            => ColumnKeys::URL_KEY,
250
            )
251
        );
252
253
        // append the artefact to the artefacts
254
        $this->artefacts[] = $artefact;
255
    }
256
257
    /**
258
     * Load's and return's the product with the passed SKU.
259
     *
260
     * @param string $sku The SKU of the product to load
261
     *
262
     * @return array The product
263
     */
264
    protected function loadProduct($sku)
265
    {
266
        return $this->getProductUrlRewriteProcessor()->loadProduct($sku);
267
    }
268
269
    /**
270
     * Returns an array with the codes of the store views related with the passed website code.
271
     *
272
     * @param string $websiteCode The code of the website to return the store view codes for
273
     *
274
     * @return array The array with the matching store view codes
275
     */
276
    protected function getStoreViewCodesByWebsiteCode($websiteCode)
277
    {
278
        return $this->getSubject()->getStoreViewCodesByWebsiteCode($websiteCode);
0 ignored issues
show
Bug introduced by
The method getStoreViewCodesByWebsiteCode() does not exist on TechDivision\Import\Subjects\SubjectInterface. Did you maybe mean getStoreViewCode()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
279
    }
280
281
    /**
282
     * Queries whether or not artefacts for the passed type and entity ID are available.
283
     *
284
     * @param string $type     The artefact type, e. g. configurable
285
     * @param string $entityId The entity ID to return the artefacts for
286
     *
287
     * @return boolean TRUE if artefacts are available, else FALSE
288
     */
289
    protected function hasArtefactsByTypeAndEntityId($type, $entityId)
290
    {
291
        return $this->getSubject()->hasArtefactsByTypeAndEntityId($type, $entityId);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface TechDivision\Import\Subjects\SubjectInterface as the method hasArtefactsByTypeAndEntityId() does only exist in the following implementations of said interface: TechDivision\Import\Product\Subjects\BunchSubject, TechDivision\Import\Subjects\ExportableTraitImpl.

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...
292
    }
293
294
    /**
295
     * Return the artefacts for the passed type and entity ID.
296
     *
297
     * @param string $type     The artefact type, e. g. configurable
298
     * @param string $entityId The entity ID to return the artefacts for
299
     *
300
     * @return array The array with the artefacts
301
     * @throws \Exception Is thrown, if no artefacts are available
302
     */
303
    protected function getArtefactsByTypeAndEntityId($type, $entityId)
304
    {
305
        return $this->getSubject()->getArtefactsByTypeAndEntityId($type, $entityId);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface TechDivision\Import\Subjects\SubjectInterface as the method getArtefactsByTypeAndEntityId() does only exist in the following implementations of said interface: TechDivision\Import\Plugins\ExportableSubjectImpl, TechDivision\Import\Product\Subjects\BunchSubject, TechDivision\Import\Subjects\ExportableTraitImpl.

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...
306
    }
307
308
    /**
309
     * Create's and return's a new empty artefact entity.
310
     *
311
     * @param array $columns             The array with the column data
312
     * @param array $originalColumnNames The array with a mapping from the old to the new column names
313
     *
314
     * @return array The new artefact entity
315
     */
316
    protected function newArtefact(array $columns, array $originalColumnNames)
317
    {
318
        return $this->getSubject()->newArtefact($columns, $originalColumnNames);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface TechDivision\Import\Subjects\SubjectInterface as the method newArtefact() does only exist in the following implementations of said interface: TechDivision\Import\Plugins\ExportableSubjectImpl, TechDivision\Import\Product\Subjects\BunchSubject, TechDivision\Import\Subjects\ExportableTraitImpl.

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...
319
    }
320
321
    /**
322
     * Add the passed product type artefacts to the product with the
323
     * last entity ID.
324
     *
325
     * @param array $artefacts The product type artefacts
326
     *
327
     * @return void
328
     * @uses \TechDivision\Import\Product\Media\Subjects\MediaSubject::getLastEntityId()
329
     */
330
    protected function addArtefacts(array $artefacts)
331
    {
332
        $this->getSubject()->addArtefacts(ProductUrlRewriteObserver::ARTEFACT_TYPE, $artefacts);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface TechDivision\Import\Subjects\SubjectInterface as the method addArtefacts() does only exist in the following implementations of said interface: TechDivision\Import\Plugins\ExportableSubjectImpl, TechDivision\Import\Product\Subjects\BunchSubject, TechDivision\Import\Subjects\ExportableTraitImpl.

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...
333
    }
334
}
335