Completed
Push — pac-235--rewrites-for-store-vi... ( 797678 )
by
unknown
10:11
created

ProductUrlRewriteObserver::createArtefact()   B

Complexity

Conditions 6
Paths 17

Size

Total Lines 39

Duplication

Lines 12
Ratio 30.77 %

Importance

Changes 0
Metric Value
dl 12
loc 39
rs 8.6737
c 0
b 0
f 0
cc 6
nc 17
nop 2
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
118
            // load product to see if already exist or new
119
            $product = $this->loadProduct($this->getValue(ColumnKeys::SKU));
120
121
            // Init admin row for memory overload
122
            $this->adminRow = array();
123
            // remember the admin row on SKU to be safe on later process
124
            $this->adminRow[$sku] = array(
125
                ColumnKeys::CATEGORIES         => $this->getValue(ColumnKeys::CATEGORIES),
126
                ColumnKeys::PRODUCT_WEBSITES   => $this->getValue(ColumnKeys::PRODUCT_WEBSITES),
127
                ColumnKeys::VISIBILITY         => $this->getValue(ColumnKeys::VISIBILITY),
128
                ColumnKeys::URL_KEY            => $this->getValue(ColumnKeys::URL_KEY)
129
            );
130
131
            // if not, load the websites the product is related with
132
            $websiteCodes = $this->getValue(ColumnKeys::PRODUCT_WEBSITES, array(), array($this, 'explode'));
133
134
            // load the store view codes of all websites
135
            foreach ($websiteCodes as $websiteCode) {
136
                $storeViewCodes = array_merge($storeViewCodes, $this->getStoreViewCodesByWebsiteCode($websiteCode));
137
            }
138
        } else {
139
            array_push($storeViewCodes, $storeViewCodeValue);
140
        }
141
142
        // iterate over the available image fields
143
        foreach ($storeViewCodes as $storeViewCode) {
144
            // iterate over the store view codes and query if artefacts are already available
145
            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...
146
                // if yes, load the artefacs
147
                $this->artefacts = $this->getArtefactsByTypeAndEntityId(ProductUrlRewriteObserver::ARTEFACT_TYPE, $lastEntityId);
148
149
                $foundArtefactToUpdate = false;
150
                // override the existing data with the store view specific one
151
                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...
152
                    // query whether or not a URL key has be specfied and the store view codes are equal
153
                    if ($this->hasValue(ColumnKeys::URL_KEY) && $this->artefacts[$i][ColumnKeys::STORE_VIEW_CODE] === $storeViewCode) {
154
                        $foundArtefactToUpdate = true;
155
                        // update the URL key
156
                        $this->artefacts[$i][ColumnKeys::URL_KEY] = $this->getValue(ColumnKeys::URL_KEY);
157
                        // update the visibility, if available
158
                        if ($this->hasValue(ColumnKeys::VISIBILITY)) {
159
                            $this->artefacts[$i][ColumnKeys::VISIBILITY] = $this->getValue(ColumnKeys::VISIBILITY);
160
                        }
161
162
                        // also update filename and line number
163
                        $this->artefacts[$i][ColumnKeys::ORIGINAL_DATA][ColumnKeys::ORIGINAL_FILENAME] = $this->getSubject()->getFilename();
164
                        $this->artefacts[$i][ColumnKeys::ORIGINAL_DATA][ColumnKeys::ORIGINAL_LINE_NUMBER] = $this->getSubject()->getLineNumber();
165
                    }
166
                }
167
                if (!$foundArtefactToUpdate) {
168
                    // if no arefacts are available, append new data
169
                    $this->createArtefact($sku, $storeViewCode);
170
                }
171
            } else {
172
                // On admin row and existing product check if url_key in database
173
                if ($storeViewCodeValue === StoreViewCodes::ADMIN && $product) {
174
                    // initialize last entity as primary key
175
                    $pk = $this->getPrimaryKeyId($product);
176
                    // initialize the entity type ID
177
                    $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...
178
                    $entityTypeId = (integer) $entityType[MemberNames::ENTITY_TYPE_ID];
179
                    // initialize store ID from store code
180
                    $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...
181
                    // take a look if url_key already exist
182
                    $foundExistingUrlKey = $this->getProductUrlRewriteProcessor()->loadProductVarcharAttributeByAttributeCodeAndEntityTypeIdAndStoreIdAndPK(
183
                        ColumnKeys::URL_KEY,
184
                        $entityTypeId,
185
                        $storeId,
186
                        $pk
187
                    );
188
                    // if url_key attribute found and same store as searched
189
                    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...
190
                        // skip for artefact as default entry
191
                        continue;
192
                    }
193
                }
194
                // if no arefacts are available, append new data
195
                $this->createArtefact($sku, $storeViewCode);
196
            }
197
        }
198
199
        // append the artefacts that has to be exported to the subject
200
        $this->addArtefacts($this->artefacts);
201
    }
202
203
    /**
204
     * @param array $product From loadProduct
205
     * @return mixed
206
     */
207
    protected function getPrimaryKeyId(array $product)
208
    {
209
        return $product[$this->getProductUrlRewriteProcessor()->getPrimaryKeyMemberName()];
210
    }
211
212
    /**
213
     * @param string $sku           The sku for the new url_key
214
     * @param string $storeViewCode The Storeview code
215
     */
216
    protected function createArtefact($sku, $storeViewCode)
217
    {
218
        if (isset($this->adminRow[$sku])) {
219 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...
220
                $this->setValue(ColumnKeys::CATEGORIES, $this->adminRow[$sku][ColumnKeys::CATEGORIES]);
221
            }
222 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...
223
                $this->setValue(ColumnKeys::PRODUCT_WEBSITES, $this->adminRow[$sku][ColumnKeys::PRODUCT_WEBSITES]);
224
            }
225 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...
226
                $this->setValue(ColumnKeys::VISIBILITY, $this->adminRow[$sku][ColumnKeys::VISIBILITY]);
227
            }
228 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...
229
                $this->setValue(ColumnKeys::URL_KEY, $this->adminRow[$sku][ColumnKeys::URL_KEY]);
230
            }
231
        }
232
233
        $artefact = $this->newArtefact(
234
            array(
235
                ColumnKeys::SKU                => $sku,
236
                ColumnKeys::STORE_VIEW_CODE    => $storeViewCode,
237
                ColumnKeys::CATEGORIES         => $this->getValue(ColumnKeys::CATEGORIES),
238
                ColumnKeys::PRODUCT_WEBSITES   => $this->getValue(ColumnKeys::PRODUCT_WEBSITES),
239
                ColumnKeys::VISIBILITY         => $this->getValue(ColumnKeys::VISIBILITY),
240
                ColumnKeys::URL_KEY            => $this->getValue(ColumnKeys::URL_KEY)
241
            ),
242
            array(
243
                ColumnKeys::SKU                => ColumnKeys::SKU,
244
                ColumnKeys::STORE_VIEW_CODE    => ColumnKeys::STORE_VIEW_CODE,
245
                ColumnKeys::CATEGORIES         => ColumnKeys::CATEGORIES,
246
                ColumnKeys::PRODUCT_WEBSITES   => ColumnKeys::PRODUCT_WEBSITES,
247
                ColumnKeys::VISIBILITY         => ColumnKeys::VISIBILITY,
248
                ColumnKeys::URL_KEY            => ColumnKeys::URL_KEY,
249
            )
250
        );
251
252
        // append the artefact to the artefacts
253
        $this->artefacts[] = $artefact;
254
    }
255
256
    /**
257
     * Load's and return's the product with the passed SKU.
258
     *
259
     * @param string $sku The SKU of the product to load
260
     *
261
     * @return array The product
262
     */
263
    protected function loadProduct($sku)
264
    {
265
        return $this->getProductUrlRewriteProcessor()->loadProduct($sku);
266
    }
267
268
    /**
269
     * Returns an array with the codes of the store views related with the passed website code.
270
     *
271
     * @param string $websiteCode The code of the website to return the store view codes for
272
     *
273
     * @return array The array with the matching store view codes
274
     */
275
    protected function getStoreViewCodesByWebsiteCode($websiteCode)
276
    {
277
        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...
278
    }
279
280
    /**
281
     * Queries whether or not artefacts for the passed type and entity ID are available.
282
     *
283
     * @param string $type     The artefact type, e. g. configurable
284
     * @param string $entityId The entity ID to return the artefacts for
285
     *
286
     * @return boolean TRUE if artefacts are available, else FALSE
287
     */
288
    protected function hasArtefactsByTypeAndEntityId($type, $entityId)
289
    {
290
        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...
291
    }
292
293
    /**
294
     * Return the artefacts for the passed type and entity ID.
295
     *
296
     * @param string $type     The artefact type, e. g. configurable
297
     * @param string $entityId The entity ID to return the artefacts for
298
     *
299
     * @return array The array with the artefacts
300
     * @throws \Exception Is thrown, if no artefacts are available
301
     */
302
    protected function getArtefactsByTypeAndEntityId($type, $entityId)
303
    {
304
        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...
305
    }
306
307
    /**
308
     * Create's and return's a new empty artefact entity.
309
     *
310
     * @param array $columns             The array with the column data
311
     * @param array $originalColumnNames The array with a mapping from the old to the new column names
312
     *
313
     * @return array The new artefact entity
314
     */
315
    protected function newArtefact(array $columns, array $originalColumnNames)
316
    {
317
        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...
318
    }
319
320
    /**
321
     * Add the passed product type artefacts to the product with the
322
     * last entity ID.
323
     *
324
     * @param array $artefacts The product type artefacts
325
     *
326
     * @return void
327
     * @uses \TechDivision\Import\Product\Media\Subjects\MediaSubject::getLastEntityId()
328
     */
329
    protected function addArtefacts(array $artefacts)
330
    {
331
        $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...
332
    }
333
}
334