Completed
Push — 15.x ( 84e4c2...66410f )
by Tim
02:43
created

mapAttributeCodeByHeaderMapping()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 4
c 0
b 0
f 0
ccs 0
cts 4
cp 0
rs 10
cc 1
nc 1
nop 1
crap 2
1
<?php
2
3
/**
4
 * TechDivision\Import\Observers\GenericValidatorObserver
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 2019 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\Utils\RegistryKeys;
24
use TechDivision\Import\Subjects\SubjectInterface;
25
use TechDivision\Import\Services\RegistryProcessorInterface;
26
27
/**
28
 * Observer that invokes the callbacks to validate the actual row.
29
 *
30
 * @author    Tim Wagner <[email protected]>
31
 * @copyright 2019 TechDivision GmbH <[email protected]>
32
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
33
 * @link      https://github.com/techdivision/import
34
 * @link      http://www.techdivision.com
35
 */
36
class GenericValidatorObserver extends AbstractObserver
37
{
38
39
    /**
40
     * The registry processor instance.
41
     *
42
     * @var \TechDivision\Import\Services\RegistryProcessorInterface
43
     */
44
    protected $registryProcessor;
45
46
    /**
47
     * Initializes the callback with the loader instance.
48
     *
49
     * @param \TechDivision\Import\Services\RegistryProcessorInterface $registryProcessor The registry processor instance
50
     */
51
    public function __construct(RegistryProcessorInterface $registryProcessor)
52
    {
53
        $this->registryProcessor = $registryProcessor;
54
    }
55
56
    /**
57
     * Will be invoked by the action on the events the listener has been registered for.
58
     *
59
     * @param \TechDivision\Import\Subjects\SubjectInterface $subject The subject instance
60
     *
61
     * @return array The modified row
62
     * @see \TechDivision\Import\Observers\ObserverInterface::handle()
63
     */
64
    public function handle(SubjectInterface $subject)
65
    {
66
67
        // initialize the row
68
        $this->setSubject($subject);
69
        $this->setRow($subject->getRow());
70
71
        // process the functionality and return the row
72
        $this->process();
73
74
        // return the processed row
75
        return $this->getRow();
76
    }
77
78
    /**
79
     * Process the observer's business logic.
80
     *
81
     * @return array The processed row
82
     */
83
    protected function process()
84
    {
85
86
        // load the available header names
87
        $headerNames = array_keys($this->getHeaders());
88
89
        // iterate over the custom validations
90
        foreach ($headerNames as $headerName) {
91
            // map the header name to the attribute code, if an mapping is available
92
            $attributeCode = $this->mapAttributeCodeByHeaderMapping($headerName);
93
            // load the attribute value from the row
94
            $attributeValue = $this->getValue($attributeCode);
95
            // load the callbacks for the actual attribute code
96
            $callbacks = $this->getCallbacksByType($attributeCode);
97
            // invoke the registered callbacks
98
            foreach ($callbacks as $callback) {
99
                try {
100
                    $callback->handle($attributeCode, $attributeValue);
101
                } catch (\InvalidArgumentException $iea) {
102
                    // add the the validation result to the status
103
                    $this->mergeValidations(
104
                        array(
105
                            basename($this->getFilename()) => array(
106
                                $this->getSubject()->getLineNumber() => array(
107
                                    $attributeCode => $iea->getMessage()
108
                                )
109
                            )
110
                        )
111
                    );
112
                }
113
            }
114
        }
115
    }
116
117
    /**
118
     * Merge the passed validation errors into the status.
119
     *
120
     * @param array $validations The validation errors to merge
121
     *
122
     * @return void
123
     */
124
    protected function mergeValidations(array $validations)
125
    {
126
        $this->getSubject()->mergeValidations($validations);
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 mergeValidations() does only exist in the following implementations of said interface: 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...
127
    }
128
129
    /**
130
     * Map the passed attribute code, if a header mapping exists and return the
131
     * mapped mapping.
132
     *
133
     * @param string $attributeCode The attribute code to map
134
     *
135
     * @return string The mapped attribute code, or the original one
136
     */
137
    protected function mapAttributeCodeByHeaderMapping($attributeCode)
138
    {
139
        return $this->getSubject()->mapAttributeCodeByHeaderMapping($attributeCode);
140
    }
141
142
    /**
143
     * Return's the array with callbacks for the passed type.
144
     *
145
     * @param string $type The type of the callbacks to return
146
     *
147
     * @return array The callbacks
148
     */
149
    protected function getCallbacksByType($type)
150
    {
151
        return $this->getSubject()->getCallbacksByType($type);
152
    }
153
}
154