Passed
Branch feature-validator (55f4e3)
by Thomas
03:27
created

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