Completed
Push — master ( 3148c6...a9ccb0 )
by Tim
01:34 queued 10s
created

VariantSuperAttributeObserver::process()   C

Complexity

Conditions 8
Paths 33

Size

Total Lines 94

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 72

Importance

Changes 0
Metric Value
dl 0
loc 94
ccs 0
cts 62
cp 0
rs 6.8864
c 0
b 0
f 0
cc 8
nc 33
nop 0
crap 72

How to fix   Long Method   

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:

1
<?php
2
3
/**
4
 * TechDivision\Import\Product\Variant\Observers\VariantSuperAttributeObserver
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 2020 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-variant
18
 * @link      http://www.techdivision.com
19
 */
20
21
namespace TechDivision\Import\Product\Variant\Observers;
22
23
use TechDivision\Import\Utils\EntityStatus;
24
use TechDivision\Import\Utils\StoreViewCodes;
25
use TechDivision\Import\Utils\BackendTypeKeys;
26
use TechDivision\Import\Observers\StateDetectorInterface;
27
use TechDivision\Import\Observers\AttributeLoaderInterface;
28
use TechDivision\Import\Observers\DynamicAttributeObserverInterface;
29
use TechDivision\Import\Product\Utils\RelationTypes;
30
use TechDivision\Import\Product\Variant\Utils\ColumnKeys;
31
use TechDivision\Import\Product\Variant\Utils\MemberNames;
32
use TechDivision\Import\Product\Variant\Utils\EntityTypeCodes;
33
use TechDivision\Import\Product\Variant\Services\ProductVariantProcessorInterface;
34
use TechDivision\Import\Product\Observers\AbstractProductImportObserver;
35
use Doctrine\Common\Collections\Collection;
36
37
/**
38
 * Oberserver that provides functionality for the product variant super attributes replace operation.
39
 *
40
 * @author    Tim Wagner <[email protected]>
41
 * @copyright 2020 TechDivision GmbH <[email protected]>
42
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
43
 * @link      https://github.com/techdivision/import-product-variant
44
 * @link      http://www.techdivision.com
45
 */
46
class VariantSuperAttributeObserver extends AbstractProductImportObserver implements DynamicAttributeObserverInterface
47
{
48
49
    /**
50
     * The ID of the actual store to use.
51
     *
52
     * @var integer
53
     */
54
    protected $storeId;
55
56
    /**
57
     * The EAV attribute to handle.
58
     *
59
     * @var array
60
     */
61
    protected $eavAttribute;
62
63
    /**
64
     * The tempoarary stored product super attribute ID.
65
     *
66
     * @var integer
67
     */
68
    protected $productSuperAttributeId;
69
70
    /**
71
     * The product variant processor instance.
72
     *
73
     * @var \TechDivision\Import\Product\Variant\Services\ProductVariantProcessorInterface
74
     */
75
    protected $productVariantProcessor;
76
77
    /**
78
     * The attribute loader instance.
79
     *
80
     * @var \TechDivision\Import\Observers\AttributeLoaderInterface
81
     */
82
    protected $attributeLoader;
83
84
    /**
85
     * The collection with entity merger instances.
86
     *
87
     * @var \Doctrine\Common\Collections\Collection
88
     */
89
    protected $entityMergers;
90
91
    /**
92
     * Initialize the "dymanmic" columns.
93
     *
94
     * @var array
95
     */
96
    protected $columns = array(
97
        EntityTypeCodes::CATALOG_PRODUCT_SUPER_ATTRIBUTE => array(
98
            MemberNames::POSITION    => array(ColumnKeys::VARIANT_VARIATION_POSITION, BackendTypeKeys::BACKEND_TYPE_INT)
99
        ),
100
        EntityTypeCodes::CATALOG_PRODUCT_SUPER_ATTRIBUTE_LABEL => array(
101
            MemberNames::VALUE       => array(ColumnKeys::VARIANT_VARIATION_LABEL, BackendTypeKeys::BACKEND_TYPE_VARCHAR),
102
            MemberNames::USE_DEFAULT => array(ColumnKeys::VARIANT_VARIATION_USE_DEFAULT, BackendTypeKeys::BACKEND_TYPE_INT)
103
        )
104
    );
105
106
    /**
107
     * Initialize the observer with the passed product variant processor instance.
108
     *
109
     * @param \TechDivision\Import\Product\Variant\Services\ProductVariantProcessorInterface $productVariantProcessor The product variant processor instance
110
     * @param \TechDivision\Import\Observers\AttributeLoaderInterface|null                   $attributeLoader         The attribute loader instance
111
     * @param \Doctrine\Common\Collections\Collection|null                                   $entityMergers           The collection with the entity merger instances
112
     * @param \TechDivision\Import\Observers\StateDetectorInterface|null                     $stateDetector           The state detector instance
113
     */
114
    public function __construct(
115
        ProductVariantProcessorInterface $productVariantProcessor,
116
        AttributeLoaderInterface $attributeLoader = null,
117
        Collection $entityMergers = null,
118
        StateDetectorInterface $stateDetector = null
119
    ) {
120
121
        // initialize the product variant processor and the attribute loader instance
122
        $this->productVariantProcessor = $productVariantProcessor;
123
        $this->attributeLoader = $attributeLoader;
124
        $this->entityMergers = $entityMergers;
125
126
127
        // pass the state detector to the parent method
128
        parent::__construct($stateDetector);
129
    }
130
131
    /**
132
     * Return's the product variant processor instance.
133
     *
134
     * @return \TechDivision\Import\Product\Variant\Services\ProductVariantProcessorInterface The product variant processor instance
135
     */
136
    protected function getProductVariantProcessor()
137
    {
138
        return $this->productVariantProcessor;
139
    }
140
141
    /**
142
     * Process the observer's business logic.
143
     *
144
     * @return array The processed row
145
     */
146
    protected function process()
147
    {
148
149
        // extract the child SKU and attribute code from the row
150
        $parentSku = $this->getValue(ColumnKeys::VARIANT_PARENT_SKU);
151
        $childSku= $this->getValue(ColumnKeys::VARIANT_CHILD_SKU);
152
        $attributeCode = $this->getValue(ColumnKeys::VARIANT_ATTRIBUTE_CODE);
153
        $attributeSetCode = $this->getValue(ColumnKeys::ATTRIBUTE_SET_CODE);
154
155
        // query whether or not the super attribute has already been processed
156
        if ($this->hasBeenProcessedRelation($parentSku, $attributeCode, RelationTypes::VARIANT_SUPER_ATTRIBUTE)) {
157
            return;
158
        }
159
160
        // prepare the store view code
161
        $this->prepareStoreViewCode($this->getRow());
0 ignored issues
show
Unused Code introduced by
The call to VariantSuperAttributeObs...:prepareStoreViewCode() has too many arguments starting with $this->getRow().

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
162
163
        // preserve the parent ID
164
        $this->setParentId($this->mapParentSku($parentSku));
165
166
        try {
167
            // load the EAV attribute with the found attribute code
168
            $this->setEavAttribute($this->getEavAttributeByAttributeCode($attributeCode));
169
        } catch (\Exception $e) {
170
            // prepare a more detailed error message
171
            $message = $this->appendExceptionSuffix(
172
                sprintf(
173
                    'Can\'t find attribute code "%s" in attribut set "%s" for variant SKU "%s" to create simple SKU "%s"',
174
                    $attributeCode,
175
                    $attributeSetCode,
176
                    $parentSku,
177
                    $childSku
178
                )
179
            );
180
181
            // if we're NOT in debug mode, re-throw a more detailed exception
182
            $wrappedException = $this->wrapException(
183
                array(ColumnKeys::VARIANT_ATTRIBUTE_CODE),
0 ignored issues
show
Documentation introduced by
array(\TechDivision\Impo...VARIANT_ATTRIBUTE_CODE) is of type array<integer,?>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
184
                new \Exception($message, null, $e)
185
            );
186
187
            // query whether or not, debug mode is enabled
188
            if ($this->isDebugMode()) {
189
                // log a warning and return immediately
190
                $this->getSystemLogger()->warning($wrappedException->getMessage());
191
                return;
192
            }
193
194
            // else, throw the exception
195
            throw $wrappedException;
196
        }
197
198
        try {
199
            // initialize and save the super attribute
200
            $attr = $this->prepareDynamicAttributes(EntityTypeCodes::CATALOG_PRODUCT_SUPER_ATTRIBUTE, $this->prepareProductSuperAttributeAttributes());
201
            if ($this->hasChanges($productSuperAttribute = $this->initializeProductSuperAttribute($attr))) {
202
                $this->persistProductSuperAttribute($productSuperAttribute);
203
            }
204
205
            // initialize and save the super attribute label
206
            $attr = $this->prepareDynamicAttributes(EntityTypeCodes::CATALOG_PRODUCT_SUPER_ATTRIBUTE_LABEL, $this->prepareProductSuperAttributeLabelAttributes());
207
            if ($this->hasChanges($productSuperAttributeLabel = $this->initializeProductSuperAttributeLabel($attr))) {
208
                $this->persistProductSuperAttributeLabel($productSuperAttributeLabel);
209
            }
210
211
            // mark the super attribute as processed
212
            $this->addProcessedRelation($parentSku, $attributeCode, RelationTypes::VARIANT_SUPER_ATTRIBUTE);
213
        } catch (\Exception $e) {
214
            // prepare a more detailed error message
215
            $message = $this->appendExceptionSuffix(
216
                sprintf(
217
                    'Super attribute for SKU %s and attribute %s can\'t be created',
218
                    $parentSku,
219
                    $attributeCode
220
                )
221
            );
222
223
            // if we're NOT in debug mode, re-throw a more detailed exception
224
            $wrappedException = $this->wrapException(
225
                array(ColumnKeys::VARIANT_PARENT_SKU, ColumnKeys::VARIANT_ATTRIBUTE_CODE),
0 ignored issues
show
Documentation introduced by
array(\TechDivision\Impo...VARIANT_ATTRIBUTE_CODE) is of type array<integer,?>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
226
                new \Exception($message, null, $e)
227
            );
228
229
            // query whether or not, debug mode is enabled
230
            if ($this->isDebugMode()) {
231
                // log a warning and return immediately
232
                $this->getSystemLogger()->warning($wrappedException->getMessage());
233
                return;
234
            }
235
236
            // else, throw the exception
237
            throw $wrappedException;
238
        }
239
    }
240
241
    /**
242
     * Merge's and return's the entity with the passed attributes and set's the
243
     * passed status.
244
     *
245
     * @param array       $entity         The entity to merge the attributes into
246
     * @param array       $attr           The attributes to be merged
247
     * @param string|null $changeSetName  The change set name to use
248
     * @param string|null $entityTypeCode The entity type code to use
249
     *
250
     * @return array The merged entity
251
     * @todo https://github.com/techdivision/import/issues/179
252
     */
253
    protected function mergeEntity(array $entity, array $attr, $changeSetName = null, $entityTypeCode = null)
254
    {
255
        return array_merge(
256
            $entity,
257
            ($this->entityMergers && $this->entityMergers->containsKey($entityTypeCode)) ? $this->entityMergers->get($entityTypeCode)->merge($this, $entity, $attr) : $attr,
258
            array(EntityStatus::MEMBER_NAME => $this->detectState($entity, $attr, $changeSetName))
259
        );
260
    }
261
262
    /**
263
     * Appends the dynamic attributes to the static ones and returns them.
264
     *
265
     * @param string $entityTypeCode   The entity type code load to append the dynamic attributes for
266
     * @param array  $staticAttributes The array with the static attributes to append the dynamic to
267
     *
268
     * @return array The array with all available attributes
269
     */
270
    protected function prepareDynamicAttributes(string $entityTypeCode, array $staticAttributes) : array
271
    {
272
        return array_merge(
273
            $staticAttributes,
274
            $this->attributeLoader ? $this->attributeLoader->load($this, $this->columns[$entityTypeCode]) : array()
275
        );
276
    }
277
278
    /**
279
     * Prepare the product super attribute attributes that has to be persisted.
280
     *
281
     * @return array The prepared product attribute attributes
282
     */
283
    protected function prepareProductSuperAttributeAttributes()
284
    {
285
286
        // load the parent ID
287
        $parentId = $this->getParentId();
288
289
        // load the attribute ID and position
290
        $attributeId = $this->getAttributeId();
291
292
        // initialize the attributes and return them
293
        return $this->initializeEntity(
294
            $this->loadRawEntity(
295
                EntityTypeCodes::CATALOG_PRODUCT_SUPER_ATTRIBUTE,
296
                array(
297
                    MemberNames::PRODUCT_ID   => $parentId,
298
                    MemberNames::ATTRIBUTE_ID => $attributeId
299
                )
300
            )
301
        );
302
    }
303
304
    /**
305
     * Prepare the product super attribute label attributes that has to be persisted.
306
     *
307
     * @return array The prepared product super attribute label attributes
308
     */
309
    protected function prepareProductSuperAttributeLabelAttributes()
310
    {
311
312
        // extract the parent/child ID as well as option value and variation label from the row
313
        $label = $this->getValue(ColumnKeys::VARIANT_VARIATION_LABEL);
314
        $useDefault = $this->getValue(ColumnKeys::VARIANT_VARIATION_USE_DEFAULT, 0);
315
316
        // query whether or not we've to create super attribute labels
317
        if (empty($label)) {
318
            $label = $this->getFrontendLabel();
319
        }
320
321
        // initialize the attributes and return them
322
        return $this->initializeEntity(
323
            $this->loadRawEntity(
324
                EntityTypeCodes::CATALOG_PRODUCT_SUPER_ATTRIBUTE_LABEL,
325
                array(
326
                    MemberNames::PRODUCT_SUPER_ATTRIBUTE_ID => $this->getProductSuperAttributeId(),
327
                    MemberNames::STORE_ID                   => $this->getRowStoreId(StoreViewCodes::ADMIN),
328
                    MemberNames::USE_DEFAULT                => $useDefault,
329
                    MemberNames::VALUE                      => $label
330
                )
331
            )
332
        );
333
    }
334
335
    /**
336
     * Load's and return's a raw entity without primary key but the mandatory members only and nulled values.
337
     *
338
     * @param string $entityTypeCode The entity type code to return the raw entity for
339
     * @param array  $data           An array with data that will be used to initialize the raw entity with
340
     *
341
     * @return array The initialized entity
342
     */
343
    protected function loadRawEntity($entityTypeCode, array $data = array())
344
    {
345
        return $this->getProductVariantProcessor()->loadRawEntity($entityTypeCode, $data);
346
    }
347
348
    /**
349
     * Initialize the product super attribute with the passed attributes and returns an instance.
350
     *
351
     * @param array $attr The product super attribute attributes
352
     *
353
     * @return array The initialized product super attribute
354
     */
355
    protected function initializeProductSuperAttribute(array $attr)
356
    {
357
        return $attr;
358
    }
359
360
    /**
361
     * Initialize the product super attribute label with the passed attributes and returns an instance.
362
     *
363
     * @param array $attr The product super attribute label attributes
364
     *
365
     * @return array The initialized product super attribute label
366
     */
367
    protected function initializeProductSuperAttributeLabel(array $attr)
368
    {
369
        return $attr;
370
    }
371
372
    /**
373
     * Set's the actual EAV attribute.
374
     *
375
     * @param array $eavAttribute The actual EAV attribute
376
     *
377
     * @return void
378
     */
379
    protected function setEavAttribute(array $eavAttribute)
380
    {
381
        $this->eavAttribute = $eavAttribute;
382
    }
383
384
    /**
385
     * Return's the actual EAV attribute.
386
     *
387
     * @return array The actual EAV attribute
388
     */
389
    protected function getEavAttribute()
390
    {
391
        return $this->eavAttribute;
392
    }
393
394
    /**
395
     * Return's the frontend label from the actual EAV attribute.
396
     *
397
     * @return string The frontend label
398
     */
399
    protected function getFrontendLabel()
400
    {
401
        return $this->eavAttribute[MemberNames::FRONTENT_LABEL];
402
    }
403
404
    /**
405
     * Return's the attribute ID from the actual EAV attribute.
406
     *
407
     * @return integer The attribute ID
408
     */
409
    protected function getAttributeId()
410
    {
411
        return $this->eavAttribute[MemberNames::ATTRIBUTE_ID];
412
    }
413
414
    /**
415
     * Set's the actual product super attribute ID.
416
     *
417
     * @param integer $productSuperAttributeId The product super attribute ID
418
     *
419
     * @return void
420
     */
421
    protected function setProductSuperAttributeId($productSuperAttributeId)
422
    {
423
        $this->productSuperAttributeId = $productSuperAttributeId;
424
    }
425
426
    /**
427
     * Return's the product super attribute ID.
428
     *
429
     * @return integer The product super attribute ID
430
     */
431
    protected function getProductSuperAttributeId()
432
    {
433
        return $this->productSuperAttributeId;
434
    }
435
436
    /**
437
     * Map's the passed SKU of the parent product to it's PK.
438
     *
439
     * @param string $parentSku The SKU of the parent product
440
     *
441
     * @return integer The primary key used to create relations
442
     */
443
    protected function mapParentSku($parentSku)
444
    {
445
        return $this->mapSkuToEntityId($parentSku);
446
    }
447
448
    /**
449
     * Return the entity ID for the passed SKU.
450
     *
451
     * @param string $sku The SKU to return the entity ID for
452
     *
453
     * @return integer The mapped entity ID
454
     * @throws \Exception Is thrown if the SKU is not mapped yet
455
     */
456
    protected function mapSkuToEntityId($sku)
457
    {
458
        return $this->getSubject()->mapSkuToEntityId($sku);
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 mapSkuToEntityId() does only exist in the following implementations of said interface: TechDivision\Import\Prod...\AbstractProductSubject, TechDivision\Import\Product\Subjects\BunchSubject, TechDivision\Import\Prod...Subjects\VariantSubject.

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...
459
    }
460
461
    /**
462
     * Set's the ID of the parent product to relate the variant with.
463
     *
464
     * @param integer $parentId The ID of the parent product
465
     *
466
     * @return void
467
     */
468
    protected function setParentId($parentId)
469
    {
470
        $this->getSubject()->setParentId($parentId);
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 setParentId() does only exist in the following implementations of said interface: TechDivision\Import\Prod...Subjects\VariantSubject.

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...
471
    }
472
473
    /**
474
     * Return's the ID of the parent product to relate the variant with.
475
     *
476
     * @return integer The ID of the parent product
477
     */
478
    protected function getParentId()
479
    {
480
        return $this->getSubject()->getParentId();
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 getParentId() does only exist in the following implementations of said interface: TechDivision\Import\Prod...Subjects\VariantSubject.

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...
481
    }
482
483
    /**
484
     * Return's the store for the passed store code.
485
     *
486
     * @param string $storeCode The store code to return the store for
487
     *
488
     * @return array The requested store
489
     * @throws \Exception Is thrown, if the requested store is not available
490
     */
491
    protected function getStoreByStoreCode($storeCode)
492
    {
493
        return $this->getSubject()->getStoreByStoreCode($storeCode);
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 getStoreByStoreCode() does only exist in the following implementations of said interface: TechDivision\Import\Prod...\AbstractProductSubject, TechDivision\Import\Product\Subjects\BunchSubject, TechDivision\Import\Prod...Subjects\VariantSubject.

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...
494
    }
495
496
    /**
497
     * Return's an array with the available stores.
498
     *
499
     * @return array The available stores
500
     */
501
    protected function getStores()
502
    {
503
        return $this->getSubject()->getStores();
0 ignored issues
show
Bug introduced by
The method getStores() does not seem to exist on object<TechDivision\Impo...jects\SubjectInterface>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
504
    }
505
506
    /**
507
     * Return's the first EAV attribute for the passed attribute code.
508
     *
509
     * @param string $attributeCode The attribute code
510
     *
511
     * @return array The array with the EAV attribute
512
     */
513
    protected function getEavAttributeByAttributeCode($attributeCode)
514
    {
515
        return $this->getSubject()->getEavAttributeByAttributeCode($attributeCode);
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 getEavAttributeByAttributeCode() does only exist in the following implementations of said interface: TechDivision\Import\Observers\EntitySubjectImpl, TechDivision\Import\Prod...\AbstractProductSubject, TechDivision\Import\Product\Subjects\BunchSubject, TechDivision\Import\Prod...Subjects\VariantSubject, TechDivision\Import\Subjects\AbstractEavSubject, TechDivision\Import\Subjects\ValidatorSubject.

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...
516
    }
517
518
    /**
519
     * Persist's the passed product super attribute data and return's the ID.
520
     *
521
     * @param array $productSuperAttribute The product super attribute data to persist
522
     *
523
     * @return void
524
     */
525
    protected function persistProductSuperAttribute($productSuperAttribute)
526
    {
527
        $this->setProductSuperAttributeId($this->getProductVariantProcessor()->persistProductSuperAttribute($productSuperAttribute));
528
    }
529
530
    /**
531
     * Persist's the passed product super attribute label data and return's the ID.
532
     *
533
     * @param array $productSuperAttributeLabel The product super attribute label data to persist
534
     *
535
     * @return void
536
     */
537
    protected function persistProductSuperAttributeLabel($productSuperAttributeLabel)
538
    {
539
        return $this->getProductVariantProcessor()->persistProductSuperAttributeLabel($productSuperAttributeLabel);
540
    }
541
}
542