Passed
Pull Request — master (#41)
by Thomas
03:01
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 737
    public function __construct(EntityManager $entityManager, $options = [])
38
    {
39 737
        $this->entityManager = $entityManager;
40
41 737
        foreach ($options as $option => $value) {
42 4
            $this->setOption($option, $value);
43
        }
44 737
    }
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 and returns the new ID for autoincrement or true
121
     *
122
     * @param Entity $entity
123
     * @param bool   $useAutoIncrement
124
     * @return mixed
125
     * @throws UnsupportedDriver
126
     */
127 2
    public function insert($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
        $this->entityManager->sync($entity, true);
137 1
        return true;
138
    }
139
140
    /**
141
     * Update $entity in database
142
     *
143
     * @param Entity $entity
144
     * @return bool
145
     * @internal
146
     */
147 6
    public function update(Entity $entity)
148
    {
149 6
        $data = $entity->getData();
150 6
        $primaryKey = $entity->getPrimaryKey();
151
152 6
        $where = [];
153 6
        foreach ($primaryKey as $attribute => $value) {
154 6
            $col = $entity::getColumnName($attribute);
155 6
            $where[] = $this->escapeIdentifier($col) . ' = ' . $this->escapeValue($value);
156 6
            if (isset($data[$col])) {
157 6
                unset($data[$col]);
158
            }
159
        }
160
161 6
        $set = [];
162 6
        foreach ($data as $col => $value) {
163 6
            $set[] = $this->escapeIdentifier($col) . ' = ' . $this->escapeValue($value);
164
        }
165
166 6
        $statement = 'UPDATE ' . $this->escapeIdentifier($entity::getTableName()) . ' ' .
167 6
                     'SET ' . implode(',', $set) . ' ' .
168 6
                     'WHERE ' . implode(' AND ', $where);
169 6
        $this->entityManager->getConnection()->query($statement);
170
171 3
        return true;
172
    }
173
174
    /**
175
     * Delete $entity from database
176
     *
177
     * This method does not delete from the map - you can still receive the entity via fetch.
178
     *
179
     * @param Entity $entity
180
     * @return bool
181
     */
182 6
    public function delete(Entity $entity)
183
    {
184 6
        $primaryKey = $entity->getPrimaryKey();
185 6
        $where = [];
186 6
        foreach ($primaryKey as $attribute => $value) {
187 6
            $col = $entity::getColumnName($attribute);
188 6
            $where[] = $this->escapeIdentifier($col) . ' = ' . $this->escapeValue($value);
189
        }
190
191 6
        $statement = 'DELETE FROM ' . $this->escapeIdentifier($entity::getTableName()) . ' ' .
192 6
                     'WHERE ' . implode(' AND ', $where);
193 6
        $this->entityManager->getConnection()->query($statement);
194
195 4
        return true;
196
    }
197
198
    /**
199
     * Build the insert statement for $entity
200
     *
201
     * @param Entity $entity
202
     * @return string
203
     */
204 11
    protected function buildInsertStatement($entity)
205
    {
206 11
        $data = $entity->getData();
207
208
        $cols = array_map(function ($key) {
209 11
            return $this->escapeIdentifier($key);
210 11
        }, array_keys($data));
211
212 11
        $values = array_map(function ($value) use ($entity) {
213 11
            return $this->escapeValue($value);
214 11
        }, array_values($data));
215
216 11
        $statement = 'INSERT INTO ' . $this->escapeIdentifier($entity::getTableName()) . ' ' .
217 11
                     '(' . implode(',', $cols) . ') VALUES (' . implode(',', $values) . ')';
218
219 11
        return $statement;
220
    }
221
222
    /**
223
     * Normalize $type
224
     *
225
     * The type returned by mysql is for example VARCHAR(20) - this function converts it to varchar
226
     *
227
     * @param string $type
228
     * @return string
229
     */
230 79
    protected function normalizeType($type)
231
    {
232 79
        $type = strtolower($type);
233
234 79
        if (($p = strpos($type, '(')) !== false && $p > 0) {
235 33
            $type = substr($type, 0, $p);
236
        }
237
238 79
        return trim($type);
239
    }
240
241
    /**
242
     * Extract content from parenthesis in $type
243
     *
244
     * @param string $type
245
     * @return string
246
     */
247 24
    protected function extractParenthesis($type)
248
    {
249 24
        if (preg_match('/\((.+)\)/', $type, $match)) {
250 16
            return $match[1];
251
        }
252
253 8
        return null;
254
    }
255
256
    /**
257
     * Escape a string for query
258
     *
259
     * @param $value
260
     * @return string
261
     */
262 120
    protected function escapeString($value)
263
    {
264 120
        return $this->entityManager->getConnection()->quote($value);
265
    }
266
267
    /**
268
     * Escape an integer for query
269
     *
270
     * @param $value
271
     * @return string
272
     */
273 50
    protected function escapeInteger($value)
274
    {
275 50
        return (string) $value;
276
    }
277
278
    /**
279
     * Escape a double for Query
280
     *
281
     * @param $value
282
     * @return string
283
     */
284 4
    protected function escapeDouble($value)
285
    {
286 4
        return (string) $value;
287
    }
288
289
    /**
290
     * Escape NULL for query
291
     *
292
     * @return string
293
     */
294 1
    protected function escapeNULL()
295
    {
296 1
        return 'NULL';
297
    }
298
299
    /**
300
     * Escape a boolean for query
301
     *
302
     * @param $value
303
     * @return string
304
     */
305 20
    protected function escapeBoolean($value)
306
    {
307 20
        return ($value) ? $this->booleanTrue : $this->booleanFalse;
308
    }
309
310
    /**
311
     * Escape a date time object for query
312
     *
313
     * @param $value
314
     * @return mixed
315
     */
316 1
    protected function escapeDateTime($value)
317
    {
318 1
        $value->setTimezone(new \DateTimeZone('UTC'));
319 1
        return $value->format('Y-m-d\TH:i:s.u\Z');
320
    }
321
}
322