Completed
Push — master ( d07a45...da6ba5 )
by
unknown
02:06
created

src/Observers/AbstractObserver.php (3 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
/**
4
 * TechDivision\Import\Observers\AbstractObserver
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
18
 * @link      http://www.techdivision.com
19
 */
20
21
namespace TechDivision\Import\Observers;
22
23
use TechDivision\Import\RowTrait;
24
use TechDivision\Import\Utils\ScopeKeys;
25
use TechDivision\Import\Utils\LoggerKeys;
26
use TechDivision\Import\Utils\EntityStatus;
27
use TechDivision\Import\Subjects\SubjectInterface;
28
29
/**
30
 * An abstract observer implementation.
31
 *
32
 * @author    Tim Wagner <[email protected]>
33
 * @copyright 2016 TechDivision GmbH <[email protected]>
34
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
35
 * @link      https://github.com/techdivision/import
36
 * @link      http://www.techdivision.com
37
 */
38
abstract class AbstractObserver implements ObserverInterface
39
{
40
41
    /**
42
     * The trait that provides row handling functionality.
43
     *
44
     * @var TechDivision\Import\RowTrait
45
     */
46
    use RowTrait;
47
48
    /**
49
     * The obeserver's subject instance.
50
     *
51
     * @var \TechDivision\Import\Subjects\SubjectInterface
52
     */
53
    protected $subject;
54
55
    /**
56
     * The state detector instance.
57
     *
58
     * @var \TechDivision\Import\Observers\StateDetectorInterface
59
     */
60
    protected $stateDetector;
61
62
    /**
63
     * Initializes the observer with the state detector instance.
64
     *
65
     * @param \TechDivision\Import\Observers\StateDetectorInterface $stateDetector The state detector instance
66
     */
67 8
    public function __construct(StateDetectorInterface $stateDetector = null)
68
    {
69 8
        $this->stateDetector = $stateDetector;
70 8
    }
71
72
    /**
73
     * Set's the obeserver's subject instance to initialize the observer with.
74
     *
75
     * @param \TechDivision\Import\Subjects\SubjectInterface $subject The observer's subject
76
     *
77
     * @return void
78
     */
79 11
    protected function setSubject(SubjectInterface $subject)
80
    {
81 11
        $this->subject = $subject;
82 11
    }
83
84
    /**
85
     * Return's the observer's subject instance.
86
     *
87
     * @return object The observer's subject instance
88
     */
89 9
    public function getSubject()
90
    {
91 9
        return $this->subject;
92
    }
93
94
    /**
95
     * Return's the observer's state detector instance.
96
     *
97
     * @return \TechDivision\Import\Observers\StateDetectorInterface The state detector instance
98
     */
99 1
    protected function getStateDetector()
100
    {
101 1
        return $this->stateDetector;
102
    }
103
104
    /**
105
     * Initialize's and return's a new entity with the status 'create'.
106
     *
107
     * @param array $attr The attributes to merge into the new entity
108
     *
109
     * @return array The initialized entity
110
     */
111 3
    protected function initializeEntity(array $attr = array())
112
    {
113 3
        return array_merge($attr, array(EntityStatus::MEMBER_NAME => EntityStatus::STATUS_CREATE));
114
    }
115
116
    /**
117
     * Query whether or not the entity has to be processed.
118
     *
119
     * @param array $entity The entity to query for
120
     *
121
     * @return boolean TRUE if the entity has to be processed, else FALSE
122
     */
123 2
    protected function hasChanges(array $entity)
124
    {
125 2
        return in_array($entity[EntityStatus::MEMBER_NAME], array(EntityStatus::STATUS_CREATE, EntityStatus::STATUS_UPDATE));
126
    }
127
128
    /**
129
     * Detect's the entity state on the specific entity conditions and return's it.
130
     *
131
     * @param array       $entity        The entity loaded from the database
132
     * @param array       $attr          The entity data from the import file
133
     * @param string|null $changeSetName The change set name to use
134
     *
135
     * @return string The detected entity state
136
     */
137 1
    protected function detectState(array $entity, array $attr, $changeSetName = null)
138
    {
139 1
        return $this->getStateDetector() instanceof StateDetectorInterface ? $this->getStateDetector()->detect($this, $entity, $attr, $changeSetName) : EntityStatus::STATUS_UPDATE;
140
    }
141
142
    /**
143
     * Merge's and return's the entity with the passed attributes and set's the
144
     * passed status.
145
     *
146
     * @param array       $entity        The entity to merge the attributes into
147
     * @param array       $attr          The attributes to be merged
148
     * @param string|null $changeSetName The change set name to use
149
     *
150
     * @return array The merged entity
151
     */
152 1
    protected function mergeEntity(array $entity, array $attr, $changeSetName = null)
153
    {
154 1
        return array_merge($entity, $attr, array(EntityStatus::MEMBER_NAME => $this->detectState($entity, $attr, $changeSetName)));
155
    }
156
157
    /**
158
     * Merge's the passed status into the actual one.
159
     *
160
     * @param array $status The status to MergeBuilder
161
     *
162
     * @return void
163
     *
164
     * @codeCoverageIgnore
165
     */
166
    protected function mergeStatus(array $status)
167
    {
168
        $this->getSubject()->mergeStatus($status);
169
    }
170
171
    /**
172
     * Set's the array containing header row.
173
     *
174
     * @param array $headers The array with the header row
175
     *
176
     * @return void
177
     *
178
     * @codeCoverageIgnore
179
     */
180
    protected function setHeaders(array $headers)
181
    {
182
        $this->getSubject()->setHeaders($headers);
183
    }
184
185
    /**
186
     * Return's the array containing header row.
187
     *
188
     * @return array The array with the header row
189
     *
190
     * @codeCoverageIgnore
191
     */
192
    protected function getHeaders()
193
    {
194
        return $this->getSubject()->getHeaders();
195
    }
196
197
    /**
198
     * Return's the RegistryProcessor instance to handle the running threads.
199
     *
200
     * @return \TechDivision\Import\Services\RegistryProcessorInterface The registry processor instance
201
     *
202
     * @codeCoverageIgnore
203
     */
204
    protected function getRegistryProcessor()
205
    {
206
        return $this->getSubject()->getRegistryProcessor();
207
    }
208
209
    /**
210
     * Append's the exception suffix containing filename and line number to the
211
     * passed message. If no message has been passed, only the suffix will be
212
     * returned
213
     *
214
     * @param string|null $message    The message to append the exception suffix to
215
     * @param string|null $filename   The filename used to create the suffix
216
     * @param string|null $lineNumber The line number used to create the suffx
217
     *
218
     * @return string The message with the appended exception suffix
219
     *
220
     * @codeCoverageIgnore
221
     */
222
    protected function appendExceptionSuffix($message = null, $filename = null, $lineNumber = null)
223
    {
224
        return $this->getSubject()->appendExceptionSuffix($message, $filename, $lineNumber);
225
    }
226
227
    /**
228
     * Wraps the passed exeception into a new one by trying to resolve the original filname,
229
     * line number and column name and use it for a detailed exception message.
230
     *
231
     * @param string     $columnName The column name that should be resolved
232
     * @param \Exception $parent     The exception we want to wrap
233
     * @param string     $className  The class name of the exception type we want to wrap the parent one
234
     *
235
     * @return \Exception the wrapped exception
236
     *
237
     * @codeCoverageIgnore
238
     */
239
    protected function wrapException(
240
        $columnName,
241
        \Exception $parent = null,
242
        $className = '\TechDivision\Import\Exceptions\WrappedColumnException'
243
    ) {
244
        return $this->getSubject()->wrapException($columnName, $parent, $className);
245
    }
246
247
    /**
248
     * Queries whether or not debug mode is enabled or not, default is TRUE.
249
     *
250
     * @return boolean TRUE if debug mode is enabled, else FALSE
251
     *
252
     * @codeCoverageIgnore
253
     */
254
    protected function isDebugMode()
255
    {
256
        return $this->getSubject()->isDebugMode();
257
    }
258
259
    /**
260
     * Stop's observer execution on the actual row.
261
     *
262
     * @return void
263
     *
264
     * @codeCoverageIgnore
265
     */
266
    protected function skipRow()
267
    {
268
        $this->getSubject()->skipRow();
269
    }
270
271
    /**
272
     * Return's the name of the file to import.
273
     *
274
     * @return string The filename
275
     *
276
     * @codeCoverageIgnore
277
     */
278
    protected function getFilename()
279
    {
280
        return $this->getSubject()->getFilename();
281
    }
282
283
    /**
284
     * Return's the actual line number.
285
     *
286
     * @return integer The line number
287
     *
288
     * @codeCoverageIgnore
289
     */
290
    protected function getLineNumber()
291
    {
292
        return $this->getSubject()->getLineNumber();
293
    }
294
295
    /**
296
     * Return's the logger with the passed name, by default the system logger.
297
     *
298
     * @param string $name The name of the requested system logger
299
     *
300
     * @return \Psr\Log\LoggerInterface The logger instance
301
     * @throws \Exception Is thrown, if the requested logger is NOT available
302
     *
303
     * @codeCoverageIgnore
304
     */
305
    protected function getSystemLogger($name = LoggerKeys::SYSTEM)
306
    {
307
        return $this->getSubject()->getSystemLogger($name);
308
    }
309
310
    /**
311
     * Return's the array with the system logger instances.
312
     *
313
     * @return array The logger instance
314
     *
315
     * @codeCoverageIgnore
316
     */
317
    protected function getSystemLoggers()
318
    {
319
        return $this->getSubject()->getSystemLoggers();
320
    }
321
322
    /**
323
     * Return's the multiple field delimiter character to use, default value is comma (,).
324
     *
325
     * @return string The multiple field delimiter character
326
     *
327
     * @codeCoverageIgnore
328
     */
329
    protected function getMultipleFieldDelimiter()
330
    {
331
        return $this->getSubject()->getMultipleFieldDelimiter();
332
    }
333
334
    /**
335
     * Return's the multiple value delimiter character to use, default value is comma (|).
336
     *
337
     * @return string The multiple value delimiter character
338
     *
339
     * @codeCoverageIgnore
340
     */
341
    protected function getMultipleValueDelimiter()
342
    {
343
        return $this->getSubject()->getMultipleValueDelimiter();
344
    }
345
346
    /**
347
     * Queries whether or not the header with the passed name is available.
348
     *
349
     * @param string $name The header name to query
350
     *
351
     * @return boolean TRUE if the header is available, else FALSE
352
     *
353
     * @codeCoverageIgnore
354
     */
355
    public function hasHeader($name)
356
    {
357
        return $this->getSubject()->hasHeader($name);
358
    }
359
360
    /**
361
     * Return's the header value for the passed name.
362
     *
363
     * @param string $name The name of the header to return the value for
364
     *
365
     * @return mixed The header value
366
     * @throws \InvalidArgumentException Is thrown, if the header with the passed name is NOT available
367
     *
368
     * @codeCoverageIgnore
369
     */
370
    protected function getHeader($name)
371
    {
372
        return $this->getSubject()->getHeader($name);
373
    }
374
375
    /**
376
     * Add's the header with the passed name and position, if not NULL.
377
     *
378
     * @param string $name The header name to add
379
     *
380
     * @return integer The new headers position
381
     *
382
     * @codeCoverageIgnore
383
     */
384
    protected function addHeader($name)
385
    {
386
        return $this->getSubject()->addHeader($name);
387
    }
388
389
    /**
390
     * Return's the ID of the product that has been created recently.
391
     *
392
     * @return string The entity Id
393
     *
394
     * @codeCoverageIgnore
395
     */
396
    protected function getLastEntityId()
397
    {
398
        return $this->getSubject()->getLastEntityId();
0 ignored issues
show
The method getLastEntityId() 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...
399
    }
400
401
    /**
402
     * Return's the source date format to use.
403
     *
404
     * @return string The source date format
405
     *
406
     * @codeCoverageIgnore
407
     */
408
    protected function getSourceDateFormat()
409
    {
410
        return $this->getSubject()->getSourceDateFormat();
411
    }
412
413
    /**
414
     * Cast's the passed value based on the backend type information.
415
     *
416
     * @param string $backendType The backend type to cast to
417
     * @param mixed  $value       The value to be casted
418
     *
419
     * @return mixed The casted value
420
     *
421
     * @codeCoverageIgnore
422
     */
423
    public function castValueByBackendType($backendType, $value)
424
    {
425
        return $this->getSubject()->castValueByBackendType($backendType, $value);
0 ignored issues
show
It seems like you code against a concrete implementation and not the interface TechDivision\Import\Subjects\SubjectInterface as the method castValueByBackendType() does only exist in the following implementations of said interface: 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...
426
    }
427
428
    /**
429
     * Set's the store view code the create the product/attributes for.
430
     *
431
     * @param string $storeViewCode The store view code
432
     *
433
     * @return void
434
     *
435
     * @codeCoverageIgnore
436
     */
437
    protected function setStoreViewCode($storeViewCode)
438
    {
439
        $this->getSubject()->setStoreViewCode($storeViewCode);
440
    }
441
442
    /**
443
     * Return's the store view code the create the product/attributes for.
444
     *
445
     * @param string|null $default The default value to return, if the store view code has not been set
446
     *
447
     * @return string The store view code
448
     *
449
     * @codeCoverageIgnore
450
     */
451
    protected function getStoreViewCode($default = null)
452
    {
453
        return $this->getSubject()->getStoreViewCode($default);
454
    }
455
456
    /**
457
     * Prepare's the store view code in the subject.
458
     *
459
     * @return void
460
     *
461
     * @codeCoverageIgnore
462
     */
463
    protected function prepareStoreViewCode()
464
    {
465
        $this->getSubject()->prepareStoreViewCode();
466
    }
467
468
    /**
469
     * Return's the store ID of the store with the passed store view code
470
     *
471
     * @param string $storeViewCode The store view code to return the store ID for
472
     *
473
     * @return integer The ID of the store with the passed ID
474
     * @throws \Exception Is thrown, if the store with the actual code is not available
475
     *
476
     * @codeCoverageIgnore
477
     */
478
    protected function getStoreId($storeViewCode)
479
    {
480
        return $this->getSubject()->getStoreId($storeViewCode);
481
    }
482
483
    /**
484
     * Return's the store ID of the actual row, or of the default store
485
     * if no store view code is set in the CSV file.
486
     *
487
     * @param string|null $default The default store view code to use, if no store view code is set in the CSV file
488
     *
489
     * @return integer The ID of the actual store
490
     * @throws \Exception Is thrown, if the store with the actual code is not available
491
     *
492
     * @codeCoverageIgnore
493
     */
494
    protected function getRowStoreId($default = null)
495
    {
496
        return $this->getSubject()->getRowStoreId($default);
0 ignored issues
show
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...
497
    }
498
499
    /**
500
     * Tries to format the passed value to a valid date with format 'Y-m-d H:i:s'.
501
     * If the passed value is NOT a valid date, NULL will be returned.
502
     *
503
     * @param string|null $value The value to format
504
     *
505
     * @return string The formatted date
506
     *
507
     * @codeCoverageIgnore
508
     */
509
    protected function formatDate($value)
510
    {
511
        return $this->getSubject()->formatDate($value);
512
    }
513
514
    /**
515
     * Extracts the elements of the passed value by exploding them
516
     * with the also passed delimiter.
517
     *
518
     * @param string      $value     The value to extract
519
     * @param string|null $delimiter The delimiter used to extrace the elements
520
     *
521
     * @return array The exploded values
522
     *
523
     * @codeCoverageIgnore
524
     */
525
    protected function explode($value, $delimiter = null)
526
    {
527
        return $this->getSubject()->explode($value, $delimiter);
528
    }
529
530
    /**
531
     * Return's the Magento configuration value.
532
     *
533
     * @param string  $path    The Magento path of the requested configuration value
534
     * @param mixed   $default The default value that has to be returned, if the requested configuration value is not set
535
     * @param string  $scope   The scope the configuration value has been set
536
     * @param integer $scopeId The scope ID the configuration value has been set
537
     *
538
     * @return mixed The configuration value
539
     * @throws \Exception Is thrown, if nor a value can be found or a default value has been passed
540
     *
541
     * @codeCoverageIgnore
542
     */
543
    protected function getCoreConfigData($path, $default = null, $scope = ScopeKeys::SCOPE_DEFAULT, $scopeId = 0)
544
    {
545
        return $this->getSubject()->getCoreConfigData($path, $default, $scope, $scopeId);
546
    }
547
}
548