Passed
Branch feature-dbal (4668d8)
by Thomas
02:38
created

Dbal::setBooleanFalse()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 3
cts 3
cp 1
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 1
crap 1
1
<?php
2
3
namespace ORM;
4
5
use ORM\Dbal\Column;
6
use ORM\Dbal\Type;
7
use ORM\Exceptions\NotScalar;
8
use ORM\Exceptions\UnsupportedDriver;
9
10
/**
11
 * Class Dbal
12
 *
13
 * This is the base class for the database abstraction layer.
14
 *
15
 * @package ORM
16
 * @author  Thomas Flori <[email protected]>
17
 */
18
abstract class Dbal
19
{
20
    /** @var EntityManager */
21
    protected $em;
22
23
    protected static $quotingCharacter = '"';
24
    protected static $identifierDivider = '.';
25
    protected static $booleanTrue = '1';
26
    protected static $booleanFalse = '0';
27
28
    protected static $registeredTypes = [];
29
    protected static $typeMapping = [];
30
31 17
    public static function setQuotingCharacter($char)
32
    {
33 17
        static::$quotingCharacter = $char;
34 17
    }
35
36 17
    public static function setIdentifierDivider($divider)
37
    {
38 17
        static::$identifierDivider = $divider;
39 17
    }
40
41 17
    public static function setBooleanTrue($true)
42
    {
43 17
        static::$booleanTrue = $true;
44 17
    }
45
46 17
    public static function setBooleanFalse($false)
47
    {
48 17
        static::$booleanFalse = $false;
49 17
    }
50
51
    /**
52
     * @return string
53
     */
54 1
    public static function getQuotingCharacter()
55
    {
56 1
        return static::$quotingCharacter;
57
    }
58
59
    /**
60
     * @return string
61
     */
62 1
    public static function getIdentifierDivider()
63
    {
64 1
        return static::$identifierDivider;
65
    }
66
67
    /**
68
     * @return string
69
     */
70 1
    public static function getBooleanTrue()
71
    {
72 1
        return static::$booleanTrue;
73
    }
74
75
    /**
76
     * @return string
77
     */
78 1
    public static function getBooleanFalse()
79
    {
80 1
        return static::$booleanFalse;
81
    }
82
83
    /**
84
     * Dbal constructor.
85
     *
86
     * @param EntityManager $entityManager
87
     */
88 278
    public function __construct(EntityManager $entityManager)
89
    {
90 278
        $this->em = $entityManager;
91 278
    }
92
93
    /**
94
     * Returns $identifier quoted for use in a sql statement
95
     *
96
     * @param string $identifier Identifier to quote
97
     * @return string
98
     */
99 163
    public function escapeIdentifier($identifier)
100
    {
101 163
        $q = static::$quotingCharacter;
102 163
        $d = static::$identifierDivider;
103 163
        return $q . str_replace($d, $q . $d . $q, $identifier) . $q;
104
    }
105
106
    /**
107
     * Returns $value formatted to use in a sql statement.
108
     *
109
     * @param  mixed  $value      The variable that should be returned in SQL syntax
110
     * @return string
111
     * @throws NotScalar
112
     */
113 162
    public function escapeValue($value)
114
    {
115 162
        switch (strtolower(gettype($value))) {
116 162
            case 'string':
117 119
                return $this->em->getConnection()->quote($value);
118
119 57
            case 'integer':
120 49
                return (string) $value;
121
122 8
            case 'double':
123 4
                return (string) $value;
124
125 4
            case 'null':
126 1
                return 'NULL';
127
128 3
            case 'boolean':
129 2
                return ($value) ? static::$booleanTrue : static::$booleanFalse;
130
131
            default:
132 1
                throw new NotScalar('$value has to be scalar data type. ' . gettype($value) . ' given');
133
        }
134
    }
135
136
    /**
137
     * Describe a table
138
     *
139
     * @param $table
140
     * @return Column[]
141
     * @throws UnsupportedDriver
142
     */
143 1
    public function describe($table)
144
    {
145 1
        throw new UnsupportedDriver('Not supported for this driver');
146
    }
147
148
    /**
149
     * Inserts $entity and returns the new ID for autoincrement or true
150
     *
151
     * @param Entity $entity
152
     * @param bool   $useAutoIncrement
153
     * @return bool|int
154
     * @throws UnsupportedDriver
155
     */
156 3
    public function insert($entity, $useAutoIncrement = true)
157
    {
158 3
        $statement = $this->buildInsertStatement($entity);
159
160 3
        if ($useAutoIncrement && $entity::isAutoIncremented()) {
161 1
            throw new UnsupportedDriver('Auto incremented column for this driver is not supported');
162
        }
163
164 2
        $this->em->getConnection()->query($statement);
165 2
        $this->em->sync($entity, true);
166 2
        return true;
167
    }
168
169
    /**
170
     * Update $entity in database
171
     *
172
     * @param Entity $entity
173
     * @return bool
174
     * @internal
175
     */
176 6
    public function update(Entity $entity)
177
    {
178 6
        $data = $entity->getData();
179 6
        $primaryKey = $entity->getPrimaryKey();
180
181 6
        $where = [];
182 6
        foreach ($primaryKey as $var => $value) {
183 6
            $col = $entity::getColumnName($var);
184 6
            $where[] = $this->escapeIdentifier($col) . ' = ' . $this->escapeValue($value);
185 6
            if (isset($data[$col])) {
186 6
                unset($data[$col]);
187
            }
188
        }
189
190 6
        $set = [];
191 6
        foreach ($data as $col => $value) {
192 6
            $set[] = $this->escapeIdentifier($col) . ' = ' . $this->escapeValue($value);
193
        }
194
195 6
        $statement = 'UPDATE ' . $this->escapeIdentifier($entity::getTableName()) . ' ' .
196 6
                     'SET ' . implode(',', $set) . ' ' .
197 6
                     'WHERE ' . implode(' AND ', $where);
198 6
        $this->em->getConnection()->query($statement);
199
200 3
        return true;
201
    }
202
203
    /**
204
     * Delete $entity from database
205
     *
206
     * This method does not delete from the map - you can still receive the entity via fetch.
207
     *
208
     * @param Entity $entity
209
     * @return bool
210
     */
211 6
    public function delete(Entity $entity)
212
    {
213 6
        $primaryKey = $entity->getPrimaryKey();
214 6
        $where = [];
215 6
        foreach ($primaryKey as $var => $value) {
216 6
            $col = $entity::getColumnName($var);
217 6
            $where[] = $this->escapeIdentifier($col) . ' = ' . $this->escapeValue($value);
218
        }
219
220 6
        $statement = 'DELETE FROM ' . $this->escapeIdentifier($entity::getTableName()) . ' ' .
221 6
                     'WHERE ' . implode(' AND ', $where);
222 6
        $this->em->getConnection()->query($statement);
223
224 4
        return true;
225
    }
226
227
    /**
228
     * Build the insert statement for $entity
229
     *
230
     * @param Entity $entity
231
     * @return string
232
     */
233 11
    protected function buildInsertStatement($entity)
234
    {
235 11
        $data = $entity->getData();
236
237
        $cols = array_map(function ($key) {
238 11
            return $this->escapeIdentifier($key);
239 11
        }, array_keys($data));
240
241 11
        $values = array_map(function ($value) use ($entity) {
242 11
            return $this->escapeValue($value);
243 11
        }, array_values($data));
244
245 11
        $statement = 'INSERT INTO ' . $this->escapeIdentifier($entity::getTableName()) . ' ' .
246 11
                     '(' . implode(',', $cols) . ') VALUES (' . implode(',', $values) . ')';
247
248 11
        return $statement;
249
    }
250
251 61
    protected function normalizeType($type)
252
    {
253 61
        $type = strtolower($type);
254
255 61
        if (($p = strpos($type, '(')) !== false && $p > 0) {
256 23
            $type = substr($type, 0, $p);
257
        }
258
259 61
        return $type;
260
    }
261
262 9
    protected function extractParenthesis($type)
263
    {
264 9
        if (preg_match('/\(([\d,]+)\)/', $type, $match)) {
265 4
            return $match[1];
266
        }
267
268 5
        return null;
269
    }
270
271 85
    protected function getType($columnDefinition)
272
    {
273 85
        if (isset(static::$typeMapping[$columnDefinition['data_type']])) {
274 76
            $class = static::$typeMapping[$columnDefinition['data_type']];
275
276
            switch ($class) {
277 76
                case Type\VarChar::class:
278 8
                    return new Type\VarChar($columnDefinition['character_maximum_length']);
0 ignored issues
show
Unused Code introduced by
The call to VarChar::__construct() has too many arguments starting with $columnDefinition['character_maximum_length'].

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
279 68
                case Type\DateTime::class:
280 8
                    return new Type\DateTime($columnDefinition['datetime_precision']);
0 ignored issues
show
Unused Code introduced by
The call to DateTime::__construct() has too many arguments starting with $columnDefinition['datetime_precision'].

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
281 60
                case Type\Time::class:
282 4
                    return new Type\Time($columnDefinition['datetime_precision']);
0 ignored issues
show
Unused Code introduced by
The call to Time::__construct() has too many arguments starting with $columnDefinition['datetime_precision'].

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
283
                default:
284 56
                    return new $class;
285
            }
286
        } else {
287 9
            foreach (self::$registeredTypes as $class) {
288
                if ($type = $class::fromDefinition($columnDefinition)) {
289
                    return $type;
290
                }
291
            }
292
293 9
            return new Type\Text();
294
        }
295
    }
296
297 85
    protected function columnFactory($columnDefinition, $type)
298
    {
299 85
        return new Column(
300 85
            $columnDefinition['column_name'],
301
            $type,
302 85
            $columnDefinition['column_default'] !== null,
303 85
            $columnDefinition['is_nullable']
304
        );
305
    }
306
}
307