Completed
Push — 16.x ( 78d55e...ce5281 )
by Tim
02:16 queued 11s
created

AbstractBaseProcessor::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 8
ccs 0
cts 8
cp 0
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 3
crap 2
1
<?php
2
3
/**
4
 * TechDivision\Import\Actions\Processors\AbstractBaseProcessor
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\Actions\Processors;
22
23
use TechDivision\Import\Utils\EntityStatus;
24
use TechDivision\Import\Connection\ConnectionInterface;
25
use TechDivision\Import\Repositories\SqlStatementRepositoryInterface;
26
use TechDivision\Import\Utils\SanitizerInterface;
27
28
/**
29
 * An abstract processor implementation provide basic CRUD functionality.
30
 *
31
 * @author    Tim Wagner <[email protected]>
32
 * @copyright 2016 TechDivision GmbH <[email protected]>
33
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
34
 * @link      https://github.com/techdivision/import
35
 * @link      http://www.techdivision.com
36
 */
37
abstract class AbstractBaseProcessor extends AbstractProcessor
38
{
39
40
    /**
41
     * The array with the statements to be prepared.
42
     *
43
     * @var array
44
     */
45
    protected $statements = array();
46
47
    /**
48
     * The array with the prepared statements.
49
     *
50
     * @var array
51
     */
52
    protected $preparedStatements = array();
53
54
    /**
55
     * The array holding row data sanitizer.
56
     *
57
     * @var \ArrayObject
58
     */
59
    protected $sanitizers;
60
61
    /**
62
     * The default statement name.
63
     *
64
     * @var string
65
     */
66
    protected $defaultStatementName;
67
68
    /**
69
     * Initialize the processor with the passed connection and utility class name, as well as optional sanitizers.
70
     *
71
     * @param ConnectionInterface $connection
72
     * @param SqlStatementRepositoryInterface $sqlStatementRepository
73
     * @param \ArrayObject $sanitizers
74
     */
75
    public function __construct(
76
        ConnectionInterface $connection,
77
        SqlStatementRepositoryInterface $sqlStatementRepository,
78
        \ArrayObject $sanitizers = null
79
    ) {
80
        parent::__construct($connection, $sqlStatementRepository);
81
        $this->setSanitizers($sanitizers ?? new \ArrayObject());
82
    }
83
84
    /**
85
     * Return's the array with the SQL statements that has to be prepared.
86
     *
87
     * @return array The SQL statements to be prepared
88
     */
89
    protected function getStatements()
90
    {
91
        return $this->statements;
92
    }
93
94
    /**
95
     * Add's the prepared statement.
96
     *
97
     * @param string        $name              The unique name of the prepared statement
98
     * @param \PDOStatement $preparedStatement The prepared statement
99
     *
100
     * @return void
101
     */
102
    protected function addPreparedStatement($name, \PDOStatement $preparedStatement)
103
    {
104
        $this->preparedStatements[$name] = $preparedStatement;
105
    }
106
107
    /**
108
     * Return's the prepared statement with the passed name or the default one.
109
     *
110
     * @param string|null $name        The name of the prepared statement to return
111
     * @param string|null $defaultName The name of the default prepared statement
112
     *
113
     * @return \PDOStatement The prepared statement
114
     */
115
    protected function getPreparedStatement($name = null, $defaultName = null)
116
    {
117
118
        // try to load the prepared statement, or use the default one
119
        if (isset($this->preparedStatements[$name])) {
120
            return $this->preparedStatements[$name];
121
        }
122
123
        // return the default prepared statement
124
        return $this->preparedStatements[$defaultName];
125
    }
126
127
    /**
128
     * Gets sanitizers list.
129
     *
130
     * @return \ArrayObject
131
     */
132
    public function getSanitizers(): \ArrayObject
133
    {
134
        return $this->sanitizers;
135
    }
136
137
    /**
138
     * Sets sanitizers list.
139
     *
140
     * @param \ArrayObject $sanitizers
141
     */
142
    public function setSanitizers(\ArrayObject $sanitizers): void
143
    {
144
        $this->sanitizers = $sanitizers;
145
    }
146
147
    /**
148
     * Qeuery whether or not the prepared statement is available or not.
149
     *
150
     * @param string $name The nqme of the prepared statement
151
     *
152
     * @return boolean TRUE if the prepared statement is available, else FALSE
153
     */
154
    protected function hasPreparedStatement($name)
155
    {
156
        return isset($this->preparedStatements[$name]);
157
    }
158
159
    /**
160
     * The array with the prepared statements.
161
     *
162
     * @return array The prepared statments
163
     */
164
    protected function getPreparedStatements()
165
    {
166
        return $this->preparedStatements;
167
    }
168
169
    /**
170
     * Prepare's and return's the passed row by removing the
171
     * entity status.
172
     *
173
     * @param array $row The row to prepare
174
     *
175
     * @return array The prepared row
176
     */
177
    protected function prepareRow(array $row)
178
    {
179
180
        // remove the entity status
181
        unset($row[EntityStatus::MEMBER_NAME]);
182
183
        // return the prepared row
184
        return $row;
185
    }
186
187
    /**
188
     * Return's the name of the processor's default statement.
189
     *
190
     * @return string The statement name
191
     */
192
    public function getDefaultStatementName()
193
    {
194
        return $this->defaultStatementName;
195
    }
196
197
    /**
198
     * Implements the CRUD functionality the processor is responsible for,
199
     * can be one of CREATE, READ, UPDATE or DELETE a entity.
200
     *
201
     * @param array       $row                  The row to persist
202
     * @param string|null $name                 The name of the prepared statement that has to be executed
203
     * @param string|null $primaryKeyMemberName The primary key member name of the entity to use
204
     *
205
     * @return void
206
     */
207
    public function execute($row, $name = null, $primaryKeyMemberName = null)
208
    {
209
        $statement = $this->getPreparedStatement($name, $this->getDefaultStatementName());
210
        $row = $this->sanitize($row, $statement);
211
212
        try {
213
            // finally execute the prepared statement
214
            $statement->execute($this->prepareRow($row));
215
        } catch (\PDOException $pdoe) {
216
            // initialize the SQL statement with the placeholders
217
            $sql = $statement->queryString;
218
219
            // replace the placeholders with the values
220
            foreach ($row as $key => $value) {
221
                $sql = str_replace(sprintf(':%s', $key), $value, $sql);
222
            }
223
224
            // prepare the error message itself
225
            $message = sprintf('%s when executing SQL "%s"', $pdoe->getMessage(), preg_replace('/\r\n\s\s+/', ' ', $sql));
226
227
            // re-throw the exception with a more detailed error message
228
            throw new \PDOException($message, null, $pdoe);
229
        }
230
    }
231
232
    /**
233
     * Initializes the proceessor with the prepared statements.
234
     *
235
     * @return void
236
     */
237
    public function init()
238
    {
239
240
        // load the statements
241
        $statements = $this->getStatements();
242
243
        // initialize the default statement name
244
        $this->defaultStatementName = $this->firstKey($statements);
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->firstKey($statements) can also be of type integer. However, the property $defaultStatementName is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
245
246
        foreach ($statements as $name => $statement) {
247
            $this->addPreparedStatement($name, $this->getConnection()->prepare($statement));
248
        }
249
    }
250
251
    /**
252
     * Returns the first key of the passed array.
253
     *
254
     * This method has been used instead of the PHP function array_key_first, because
255
     * this function will be available with PHP >= 7.3.0.
256
     *
257
     * @param array $array The array to return the first key for
258
     *
259
     * @return mixed|NULL The first key or NULL
260
     * @link https://www.php.net/array_key_first
261
     */
262
    private function firstKey(array $array)
263
    {
264
265
        // load the array keys
266
        $keys = array_keys($array);
267
268
        // try to load and return the first key
269
        foreach ($keys as $key) {
270
            return $key;
271
        }
272
273
        // return NULL otherwise
274
        return null;
275
    }
276
277
    /**
278
     * Passes row data and statement to sanitizers list.
279
     *
280
     * @param array $row
281
     * @param \PDOStatement $statement
282
     * @return array
283
     */
284
    protected function sanitize(array $row, \PDOStatement $statement)
285
    {
286
        $rawStatement = $statement->queryString;
287
        /** @var SanitizerInterface $sanitizer */
288
        foreach ($this->sanitizers as $sanitizer) {
289
            $row = $sanitizer->execute($row, $rawStatement);
290
        }
291
292
        return $row;
293
    }
294
}
295