Completed
Pull Request — master (#41)
by Thomas
03:37
created

Dbal::escapeDouble()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
cc 1
eloc 2
nc 1
nop 1
crap 1
1
<?php
2
3
namespace ORM\Dbal;
4
5
use ORM\Entity;
6
use ORM\EntityManager;
7
use ORM\Exception\NotScalar;
8
use ORM\Exception\UnsupportedDriver;
9
10
/**
11
 * Base class for database abstraction
12
 *
13
 * @package ORM
14
 * @author  Thomas Flori <[email protected]>
15
 */
16
abstract class Dbal
17
{
18
    /** @var array */
19
    protected static $typeMapping = [];
20
21
    /** @var EntityManager */
22
    protected $entityManager;
23
    /** @var string */
24
    protected $quotingCharacter = '"';
25
    /** @var string */
26
    protected $identifierDivider = '.';
27
    /** @var string */
28
    protected $booleanTrue = '1';
29
    /** @var string */
30
    protected $booleanFalse = '0';
31
32
    /**
33
     * Dbal constructor.
34
     *
35
     * @param EntityManager $entityManager
36
     */
37 735
    public function __construct(EntityManager $entityManager, $options = [])
38
    {
39 735
        $this->entityManager = $entityManager;
40
41 735
        foreach ($options as $option => $value) {
42 4
            $this->setOption($option, $value);
43
        }
44 735
    }
45
46
    /**
47
     * Set $option to $value
48
     *
49
     * @param string $option
50
     * @param mixed $value
51
     * @return self
52
     */
53 22
    public function setOption($option, $value)
54
    {
55
        switch ($option) {
56 22
            case EntityManager::OPT_IDENTIFIER_DIVIDER:
57 1
                $this->identifierDivider = $value;
58 1
                break;
59
60 21
            case EntityManager::OPT_QUOTING_CHARACTER:
61 1
                $this->quotingCharacter = $value;
62 1
                break;
63
64 20
            case EntityManager::OPT_BOOLEAN_TRUE:
65 19
                $this->booleanTrue = $value;
66 19
                break;
67
68 19
            case EntityManager::OPT_BOOLEAN_FALSE:
69 19
                $this->booleanFalse = $value;
70 19
                break;
71
        }
72 22
        return $this;
73
    }
74
75
    /**
76
     * Returns $identifier quoted for use in a sql statement
77
     *
78
     * @param string $identifier Identifier to quote
79
     * @return string
80
     */
81 184
    public function escapeIdentifier($identifier)
82
    {
83 184
        $q = $this->quotingCharacter;
84 184
        $d = $this->identifierDivider;
85 184
        return $q . str_replace($d, $q . $d . $q, $identifier) . $q;
86
    }
87
88
    /**
89
     * Returns $value formatted to use in a sql statement.
90
     *
91
     * @param  mixed  $value The variable that should be returned in SQL syntax
92
     * @return string
93
     * @throws NotScalar
94
     */
95 183
    public function escapeValue($value)
96
    {
97 183
        $type = is_object($value) ? get_class($value) : gettype($value);
98 183
        $method = [$this, 'escape' . ucfirst($type)];
99
100 183
        if (is_callable($method)) {
101 182
            return call_user_func($method, $value);
102
        } else {
103 1
            throw new NotScalar('$value has to be scalar data type. ' . gettype($value) . ' given');
104
        }
105
    }
106
107
    /**
108
     * Describe a table
109
     *
110
     * @param string $table
111
     * @return Table|Column[]
112
     * @throws UnsupportedDriver
113
     */
114 1
    public function describe($table)
115
    {
116 1
        throw new UnsupportedDriver('Not supported for this driver');
117
    }
118
119
    /**
120
     * Inserts $entity in database and returns success
121
     *
122
     * @param Entity $entity
123
     * @param bool   $useAutoIncrement
124
     * @return bool
125
     * @throws UnsupportedDriver
126
     */
127 2
    public function insert(Entity $entity, $useAutoIncrement = true)
128
    {
129 2
        $statement = $this->buildInsertStatement($entity);
130
131 2
        if ($useAutoIncrement && $entity::isAutoIncremented()) {
132 1
            throw new UnsupportedDriver('Auto incremented column for this driver is not supported');
133
        }
134
135 1
        $this->entityManager->getConnection()->query($statement);
136 1
        return $this->entityManager->sync($entity, true);
137
    }
138
139
    /**
140
     * Update $entity in database and returns success
141
     *
142
     * @param Entity $entity
143
     * @return bool
144
     * @internal
145
     */
146 6
    public function update(Entity $entity)
147
    {
148 6
        $data = $entity->getData();
149 6
        $primaryKey = $entity->getPrimaryKey();
150
151 6
        $where = [];
152 6
        foreach ($primaryKey as $attribute => $value) {
153 6
            $col = $entity::getColumnName($attribute);
154 6
            $where[] = $this->escapeIdentifier($col) . ' = ' . $this->escapeValue($value);
155 6
            if (isset($data[$col])) {
156 6
                unset($data[$col]);
157
            }
158
        }
159
160 6
        $set = [];
161 6
        foreach ($data as $col => $value) {
162 6
            $set[] = $this->escapeIdentifier($col) . ' = ' . $this->escapeValue($value);
163
        }
164
165 6
        $statement = 'UPDATE ' . $this->escapeIdentifier($entity::getTableName()) . ' ' .
166 6
                     'SET ' . implode(',', $set) . ' ' .
167 6
                     'WHERE ' . implode(' AND ', $where);
168 6
        $this->entityManager->getConnection()->query($statement);
169
170 3
        return $this->entityManager->sync($entity, true);
171
    }
172
173
    /**
174
     * Delete $entity from database
175
     *
176
     * This method does not delete from the map - you can still receive the entity via fetch.
177
     *
178
     * @param Entity $entity
179
     * @return bool
180
     */
181 6
    public function delete(Entity $entity)
182
    {
183 6
        $primaryKey = $entity->getPrimaryKey();
184 6
        $where = [];
185 6
        foreach ($primaryKey as $attribute => $value) {
186 6
            $col = $entity::getColumnName($attribute);
187 6
            $where[] = $this->escapeIdentifier($col) . ' = ' . $this->escapeValue($value);
188
        }
189
190 6
        $statement = 'DELETE FROM ' . $this->escapeIdentifier($entity::getTableName()) . ' ' .
191 6
                     'WHERE ' . implode(' AND ', $where);
192 6
        $this->entityManager->getConnection()->query($statement);
193
194 4
        return true;
195
    }
196
197
    /**
198
     * Build the insert statement for $entity
199
     *
200
     * @param Entity $entity
201
     * @return string
202
     */
203 11
    protected function buildInsertStatement($entity)
204
    {
205 11
        $data = $entity->getData();
206
207
        $cols = array_map(function ($key) {
208 11
            return $this->escapeIdentifier($key);
209 11
        }, array_keys($data));
210
211 11
        $values = array_map(function ($value) use ($entity) {
212 11
            return $this->escapeValue($value);
213 11
        }, array_values($data));
214
215 11
        $statement = 'INSERT INTO ' . $this->escapeIdentifier($entity::getTableName()) . ' ' .
216 11
                     '(' . implode(',', $cols) . ') VALUES (' . implode(',', $values) . ')';
217
218 11
        return $statement;
219
    }
220
221
    /**
222
     * Update the autoincrement value
223
     *
224
     * @param Entity     $entity
225
     * @param int|string $value
226
     */
227 3
    protected function updateAutoincrement(Entity $entity, $value)
228
    {
229 3
        $var = $entity::getPrimaryKeyVars()[0];
230 3
        $column = $entity::getColumnName($var);
231
232 3
        $entity->setOriginalData(array_merge($entity->getData(), [
233 3
            $column => $value
234
        ]));
235 3
        $entity->__set($var, $value);
236 3
    }
237
238
    /**
239
     * Normalize $type
240
     *
241
     * The type returned by mysql is for example VARCHAR(20) - this function converts it to varchar
242
     *
243
     * @param string $type
244
     * @return string
245
     */
246 79
    protected function normalizeType($type)
247
    {
248 79
        $type = strtolower($type);
249
250 79
        if (($p = strpos($type, '(')) !== false && $p > 0) {
251 33
            $type = substr($type, 0, $p);
252
        }
253
254 79
        return trim($type);
255
    }
256
257
    /**
258
     * Extract content from parenthesis in $type
259
     *
260
     * @param string $type
261
     * @return string
262
     */
263 24
    protected function extractParenthesis($type)
264
    {
265 24
        if (preg_match('/\((.+)\)/', $type, $match)) {
266 16
            return $match[1];
267
        }
268
269 8
        return null;
270
    }
271
272
    /**
273
     * Escape a string for query
274
     *
275
     * @param string $value
276
     * @return string
277
     */
278 120
    protected function escapeString($value)
279
    {
280 120
        return $this->entityManager->getConnection()->quote($value);
281
    }
282
283
    /**
284
     * Escape an integer for query
285
     *
286
     * @param int $value
287
     * @return string
288
     */
289 50
    protected function escapeInteger($value)
290
    {
291 50
        return (string) $value;
292
    }
293
294
    /**
295
     * Escape a double for Query
296
     *
297
     * @param double $value
298
     * @return string
299
     */
300 4
    protected function escapeDouble($value)
301
    {
302 4
        return (string) $value;
303
    }
304
305
    /**
306
     * Escape NULL for query
307
     *
308
     * @return string
309
     */
310 1
    protected function escapeNULL()
311
    {
312 1
        return 'NULL';
313
    }
314
315
    /**
316
     * Escape a boolean for query
317
     *
318
     * @param bool $value
319
     * @return string
320
     */
321 20
    protected function escapeBoolean($value)
322
    {
323 20
        return ($value) ? $this->booleanTrue : $this->booleanFalse;
324
    }
325
326
    /**
327
     * Escape a date time object for query
328
     *
329
     * @param \DateTime $value
330
     * @return mixed
331
     */
332 1
    protected function escapeDateTime(\DateTime $value)
333
    {
334 1
        $value->setTimezone(new \DateTimeZone('UTC'));
335 1
        return $value->format('Y-m-d\TH:i:s.u\Z');
336
    }
337
}
338