Completed
Push — master ( 31bba2...d233b4 )
by Marcus
07:35
created

ProductMediaObserver::processAdditionalImages()   B

Complexity

Conditions 8
Paths 4

Size

Total Lines 57

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 72

Importance

Changes 0
Metric Value
dl 0
loc 57
rs 7.6937
c 0
b 0
f 0
ccs 0
cts 0
cp 0
cc 8
nc 4
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\Media\Observers\ProductMediaObserver
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-media
18
 * @link      http://www.techdivision.com
19
 */
20
21
namespace TechDivision\Import\Product\Media\Observers;
22
23
use TechDivision\Import\Product\Media\Utils\ColumnKeys;
24
use TechDivision\Import\Product\Observers\AbstractProductImportObserver;
25
26
/**
27
 * Observer that extracts theproduct's media data from a CSV file to be added to media specifi CSV file.
28
 *
29
 * @author    Tim Wagner <[email protected]>
30
 * @copyright 2016 TechDivision GmbH <[email protected]>
31
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
32
 * @link      https://github.com/techdivision/import-product-media
33
 * @link      http://www.techdivision.com
34
 */
35
class ProductMediaObserver extends AbstractProductImportObserver
36
{
37
38
    /**
39
     * The artefact type.
40
     *
41
     * @var string
42
     */
43
    const ARTEFACT_TYPE = 'media';
44
45
    /**
46
     * The the default image label.
47
     *
48
     * @var string
49
     * @deprecated Since 23.0.0
50
     */
51
    const DEFAULT_IMAGE_LABEL = 'Image';
52
53
    /**
54
     * The default image position.
55
     *
56
     * @var int
57
     * @deprecated Since 23.0.0
58
     */
59
    const DEFAULT_IMAGE_POSITION = 0;
60
61
    /**
62
     * The image artefacts that have to be exported.
63
     *
64
     * @var array
65
     */
66
    protected $artefacts = array();
67
68
    /**
69
     * The array with names of the images that should be hidden on the product detail page.
70
     *
71
     * @var array
72
     */
73
    protected $imagesToHide = array();
74
75
    /**
76
     * The array with names of the images that has to be disabled.
77
     *
78
     * @var array
79
     */
80
    protected $disabledImages = array();
81
82
    /**
83
     * Holds the image values of the main row.
84
     *
85
     * @var array
86
     */
87
    protected $mainRow = array();
88
89
    /**
90
     * Process the observer's business logic.
91
     *
92
     * @return array The processed row
93
     */
94
    protected function process()
95
    {
96
97
        // reset the values of the parent row, if the SKU changes
98
        if ($this->isLastSku($this->getValue(ColumnKeys::SKU)) === false) {
99
            $this->mainRow = array();
100
        }
101
102
        // initialize the arrays for the the artefacts,
103
        // the hidden as well as the disabled images
104
        $this->artefacts = array();
105
        $this->imagesToHide = array();
106
        $this->disabledImages = array();
107
108
        // load the images that has to be hidden on product
109
        // detail page or completely disabled
110
        $this->loadImagesToHide();
111
        $this->loadImagesToDisable();
112
113
        // process the images/additional images
114
        $this->processImages();
115
        $this->processAdditionalImages();
116
117
        // append the artefacts that has to be exported to the subject
118
        $this->addArtefacts($this->artefacts);
119
    }
120
121
    /**
122
     * Resolve's the value with the passed colum name from the actual row. If a callback will
123
     * be passed, the callback will be invoked with the found value as parameter. If
124
     * the value is NULL or empty, the default value will be returned.
125
     *
126
     * @param string        $name     The name of the column to return the value for
127
     * @param mixed|null    $default  The default value, that has to be returned, if the row's value is empty
128
     * @param callable|null $callback The callback that has to be invoked on the value, e. g. to format it
129
     *
130
     * @return mixed|null The, almost formatted, value
131
     * @see \TechDivision\Import\Observers\AbstractObserver::getValue()
132
     */
133
    protected function getImageValue($name, $default = null, callable $callback = null)
134
    {
135
136
        // query whether or not the a image value is available, return it if yes
137
        if ($this->hasValue($name) && $this->isLastSku($this->getValue(ColumnKeys::SKU)) === false) {
138
            return $this->mainRow[$name] = $this->getValue($name, $default, $callback);
139
        }
140
141
        // try to load it from the parent rows
142
        if (isset($this->mainRow[$name])) {
143
            return $this->mainRow[$name];
144
        }
145
    }
146
147
    /**
148
     * Parses the column and exports the image data to a separate file.
149
     *
150
     * @return void
151
     */
152
    protected function processImages()
153
    {
154
155
        // load the store view code
156
        $storeViewCode = $this->getValue(ColumnKeys::STORE_VIEW_CODE);
157
        $attributeSetCode = $this->getValue(ColumnKeys::ATTRIBUTE_SET_CODE);
158
159
        // load the parent SKU from the row
160
        $parentSku = $this->getValue(ColumnKeys::SKU);
161
162
        // load the media roles
163
        $mediaRoles = $this->getMediaRoles();
164
165
        // iterate over the available image fields
166
        foreach ($mediaRoles as $mediaAttrColumnNames) {
167
            // query whether or not the column contains an image name
168
            if ($image = $this->getImageValue($imageColumnName = $mediaAttrColumnNames[ColumnKeys::IMAGE_PATH])) {
169
                // load the original image path and query whether or not an image with the name already exists
170
                if (isset($this->artefacts[$imagePath = $this->getInversedImageMapping($image)])) {
171
                    continue;
172
                }
173
174
                // initialize the label text
175
                $labelText = null;
176
177
                // query whether or not a custom label text has been passed
178
                if ($this->hasValue($labelColumnName = $mediaAttrColumnNames[ColumnKeys::IMAGE_LABEL])) {
179
                    $labelText = $this->getValue($mediaAttrColumnNames[ColumnKeys::IMAGE_LABEL]);
180
                }
181
182
                // initialize the default image position
183
                $position = null;
184
185
                // query and retrieve optional image position
186
                if ($this->hasValue($mediaAttrColumnNames[ColumnKeys::IMAGE_POSITION])) {
187
                    $position = $this->getValue($mediaAttrColumnNames[ColumnKeys::IMAGE_POSITION]);
188
                }
189
190
                // prepare the new base image
191
                $artefact = $this->newArtefact(
192
                    array(
193
                        ColumnKeys::STORE_VIEW_CODE        => $storeViewCode,
194
                        ColumnKeys::ATTRIBUTE_SET_CODE     => $attributeSetCode,
195
                        ColumnKeys::IMAGE_PARENT_SKU       => $parentSku,
196
                        ColumnKeys::IMAGE_PATH             => $imagePath,
197
                        ColumnKeys::IMAGE_PATH_NEW         => $image,
198
                        ColumnKeys::HIDE_FROM_PRODUCT_PAGE => in_array($image, $this->imagesToHide) ? 1 : 0,
199
                        ColumnKeys::MEDIA_TYPE             => 'image',
200
                        ColumnKeys::IMAGE_LABEL            => $labelText,
201
                        ColumnKeys::IMAGE_POSITION         => $position,
202
                        ColumnKeys::IMAGE_DISABLED         => in_array($image, $this->disabledImages) ? 1 : 0,
203
                    ),
204
                    array(
205
                        ColumnKeys::STORE_VIEW_CODE        => ColumnKeys::STORE_VIEW_CODE,
206
                        ColumnKeys::ATTRIBUTE_SET_CODE     => ColumnKeys::ATTRIBUTE_SET_CODE,
207
                        ColumnKeys::IMAGE_PARENT_SKU       => ColumnKeys::SKU,
208
                        ColumnKeys::IMAGE_PATH             => $imageColumnName,
209
                        ColumnKeys::IMAGE_PATH_NEW         => $imageColumnName,
210
                        ColumnKeys::HIDE_FROM_PRODUCT_PAGE => ColumnKeys::HIDE_FROM_PRODUCT_PAGE,
211
                        ColumnKeys::MEDIA_TYPE             => null,
212
                        ColumnKeys::IMAGE_LABEL            => $labelColumnName,
213
                        ColumnKeys::IMAGE_POSITION         => $mediaAttrColumnNames[ColumnKeys::IMAGE_POSITION],
214
                        ColumnKeys::IMAGE_DISABLED         => ColumnKeys::DISABLED_IMAGES
215
                    )
216
                );
217
218
                // append the base image to the artefacts
219
                $this->artefacts[$imagePath] = $artefact;
220
            }
221
        }
222
    }
223
224
    /**
225
     * Parses the column and exports the additional image data to a separate file.
226
     *
227
     * @return void
228
     */
229
    protected function processAdditionalImages()
230
    {
231
232
        // load the store view code
233
        $storeViewCode = $this->getValue(ColumnKeys::STORE_VIEW_CODE);
234
        $attributeSetCode = $this->getValue(ColumnKeys::ATTRIBUTE_SET_CODE);
235
236
        // load the parent SKU from the row
237
        $parentSku = $this->getValue(ColumnKeys::SKU);
238
239
        // query whether or not, we've additional images
240
        if ($additionalImages = $this->getImageValue(ColumnKeys::ADDITIONAL_IMAGES, null, array($this, 'explode'))) {
241
            // expand the additional image labels, if available
242
            $additionalImageLabels = $this->getValue(ColumnKeys::ADDITIONAL_IMAGE_LABELS, array(), array($this, 'explode'));
243
            // retrieve the additional image positions
244
            $additionalImagePositions = $this->getValue(ColumnKeys::ADDITIONAL_IMAGE_POSITIONS, array(), array($this, 'explode'));
245
246
            // initialize the images with the found values
247
            foreach ($additionalImages as $key => $additionalImage) {
248
                // load the original image path and query whether or not an image with the name already exists
249
                if (isset($this->artefacts[$imagePath = $this->getInversedImageMapping($additionalImage)])) {
250
                    continue;
251
                }
252
253
                // prepare the additional image
254
                $artefact = $this->newArtefact(
255
                    array(
256
                        ColumnKeys::STORE_VIEW_CODE        => $storeViewCode,
257
                        ColumnKeys::ATTRIBUTE_SET_CODE     => $attributeSetCode,
258
                        ColumnKeys::IMAGE_PARENT_SKU       => $parentSku,
259
                        ColumnKeys::IMAGE_PATH             => $imagePath,
260
                        ColumnKeys::IMAGE_PATH_NEW         => $additionalImage,
261
                        ColumnKeys::HIDE_FROM_PRODUCT_PAGE => in_array($additionalImage, $this->imagesToHide) ? 1 : 0,
262
                        ColumnKeys::MEDIA_TYPE             => 'image',
263
                        ColumnKeys::IMAGE_LABEL            => isset($additionalImageLabels[$key]) ? $additionalImageLabels[$key] : null,
264
                        ColumnKeys::IMAGE_POSITION         => isset($additionalImagePositions[$key]) ? $additionalImagePositions[$key] : null,
265
                        ColumnKeys::IMAGE_DISABLED         => in_array($additionalImage, $this->disabledImages) ? 1 : 0
266
                    ),
267
                    array(
268
                        ColumnKeys::STORE_VIEW_CODE        => ColumnKeys::STORE_VIEW_CODE,
269
                        ColumnKeys::ATTRIBUTE_SET_CODE     => ColumnKeys::ATTRIBUTE_SET_CODE,
270
                        ColumnKeys::IMAGE_PARENT_SKU       => ColumnKeys::SKU,
271
                        ColumnKeys::IMAGE_PATH             => ColumnKeys::ADDITIONAL_IMAGES,
272
                        ColumnKeys::IMAGE_PATH_NEW         => ColumnKeys::ADDITIONAL_IMAGES,
273
                        ColumnKeys::HIDE_FROM_PRODUCT_PAGE => ColumnKeys::HIDE_FROM_PRODUCT_PAGE,
274
                        ColumnKeys::MEDIA_TYPE             => null,
275
                        ColumnKeys::IMAGE_LABEL            => ColumnKeys::ADDITIONAL_IMAGE_LABELS,
276
                        ColumnKeys::IMAGE_POSITION         => ColumnKeys::ADDITIONAL_IMAGE_POSITIONS,
277
                        ColumnKeys::IMAGE_DISABLED         => ColumnKeys::DISABLED_IMAGES
278
                    )
279
                );
280
281
                // append the additional image to the artefacts
282
                $this->artefacts[$imagePath] = $artefact;
283
            }
284
        }
285
    }
286
287
    /**
288
     * Load the images that has to be hidden on the product detail page.
289
     *
290
     * @return void
291
     */
292 View Code Duplication
    protected function loadImagesToHide()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
293
    {
294
295
        // load the array with the images that has to be hidden
296
        $hideFromProductPage = $this->getValue(ColumnKeys::HIDE_FROM_PRODUCT_PAGE, array(), array($this, 'explode'));
297
298
        // map the image names, because probably they have been renamed by the upload functionality
299
        foreach ($hideFromProductPage as $filename) {
300
            $this->imagesToHide[] = $this->getImageMapping($filename);
301
        }
302
    }
303
304
    /**
305
     * Load the images that has to be disabled
306
     *
307
     * @return void
308
     */
309 View Code Duplication
    protected function loadImagesToDisable()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
310
    {
311
312
        // load the array with the images that has to be disabled
313
        $disabledImages = $this->getValue(ColumnKeys::DISABLED_IMAGES, array(), array($this, 'explode'));
314
315
        // map the image names, because probably they have been renamed by the upload functionality
316
        foreach ($disabledImages as $filename) {
317
            $this->disabledImages[] = $this->getImageMapping($filename);
318
        }
319
    }
320
321
    /**
322
     * Return's the array with the available image types and their label columns.
323
     *
324
     * @return array The array with the available image types
325
     */
326
    protected function getImageTypes()
327
    {
328
        return $this->getSubject()->getImageTypes();
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 getImageTypes() does only exist in the following implementations of said interface: TechDivision\Import\Prod...a\Subjects\MediaSubject, TechDivision\Import\Prod...\AbstractProductSubject, TechDivision\Import\Product\Subjects\BunchSubject.

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...
329
    }
330
331
    /**
332
     * Return's the default image label.
333
     *
334
     * @return string|null The default image label
335
     * @deprecated Since 23.0.0
336
     */
337
    protected function getDefaultImageLabel()
338
    {
339
        return ProductMediaObserver::DEFAULT_IMAGE_LABEL;
0 ignored issues
show
Deprecated Code introduced by
The constant TechDivision\Import\Prod...er::DEFAULT_IMAGE_LABEL has been deprecated with message: Since 23.0.0

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
340
    }
341
342
    /**
343
     * Returns the default image position.
344
     *
345
     * @return int The default image position
346
     * @deprecated Since 23.0.0
347
     */
348
    protected function getDefaultImagePosition()
349
    {
350
        return ProductMediaObserver::DEFAULT_IMAGE_POSITION;
0 ignored issues
show
Deprecated Code introduced by
The constant TechDivision\Import\Prod...:DEFAULT_IMAGE_POSITION has been deprecated with message: Since 23.0.0

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
351
    }
352
353
    /**
354
     * Returns the mapped filename (which is the new filename).
355
     *
356
     * @param string $filename The filename to map
357
     *
358
     * @return string The mapped filename
359
     */
360
    protected function getImageMapping($filename)
361
    {
362
        return $this->getSubject()->getImageMapping($filename);
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 getImageMapping() does only exist in the following implementations of said interface: TechDivision\Import\Obse...s\FileUploadSubjectImpl, TechDivision\Import\Prod...a\Subjects\MediaSubject, TechDivision\Import\Product\Subjects\BunchSubject.

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...
363
    }
364
365
    /**
366
     * Returns the original filename for passed one (which is the new filename).
367
     *
368
     * @param string $newFilename The new filename to return the original one for
369
     *
370
     * @return string The original filename
371
     */
372
    protected function getInversedImageMapping($newFilename)
373
    {
374
        return $this->getSubject()->getInversedImageMapping($newFilename);
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 getInversedImageMapping() does only exist in the following implementations of said interface: TechDivision\Import\Obse...s\FileUploadSubjectImpl, TechDivision\Import\Prod...a\Subjects\MediaSubject, TechDivision\Import\Product\Subjects\BunchSubject.

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...
375
    }
376
377
    /**
378
     * Returns the media roles of the subject.
379
     *
380
     * @return mixed
381
     */
382
    protected function getMediaRoles()
383
    {
384
        return $this->getSubject()->getMediaRoles();
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 getMediaRoles() does only exist in the following implementations of said interface: TechDivision\Import\Product\Subjects\BunchSubject.

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...
385
    }
386
387
    /**
388
     * Create's and return's a new empty artefact entity.
389
     *
390
     * @param array $columns             The array with the column data
391
     * @param array $originalColumnNames The array with a mapping from the old to the new column names
392
     *
393
     * @return array The new artefact entity
394
     */
395
    protected function newArtefact(array $columns, array $originalColumnNames)
396
    {
397
        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\Prod...a\Subjects\MediaSubject, 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...
398
    }
399
400
    /**
401
     * Add the passed product type artefacts to the product with the
402
     * last entity ID.
403
     *
404
     * @param array $artefacts The product type artefacts
405
     *
406
     * @return void
407
     * @uses \TechDivision\Import\Product\Media\Subjects\MediaSubject::getLastEntityId()
408
     */
409
    protected function addArtefacts(array $artefacts)
410
    {
411
        $this->getSubject()->addArtefacts(ProductMediaObserver::ARTEFACT_TYPE, $artefacts, false);
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\Prod...a\Subjects\MediaSubject, 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...
412
    }
413
}
414