Completed
Push — master ( c37c6a...621c80 )
by Vitaly
03:01
created

Database::update()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 5
Bugs 0 Features 1
Metric Value
c 5
b 0
f 1
dl 0
loc 7
rs 9.4286
cc 1
eloc 5
nc 1
nop 2
1
<?php
2
/**
3
 * Created by PhpStorm.
4
 * User: egorov
5
 * Date: 09.05.2015
6
 * Time: 13:05
7
 */
8
namespace samsonframework\orm;
9
use samsonframework\orm\exception\EntityNotFound;
10
11
/**
12
 * Class Database
13
 * @package samsonframework\orm
14
 */
15
class Database
16
{
17
    /** @var \PDO Database driver */
18
    protected $driver;
19
20
    /** @var string Database name */
21
    protected $database;
22
23
    /** @var int Amount of milliseconds spent on queries */
24
    protected $elapsed;
25
26
    /** @var int Amount queries executed */
27
    protected $count;
28
29
    /**
30
     * Connect to a database using driver with parameters
31
     * @param string $database Database name
32
     * @param string $username Database username
33
     * @param string $password Database password
34
     * @param string $host Database host(localhost by default)
35
     * @param int $port Database port(3306 by default)
36
     * @param string $driver Database driver for interaction(MySQL by default)
37
     * @param string $charset Database character set
38
     * @return bool True if connection to database was successful
39
     */
40
    public function connect(
41
        $database,
42
        $username,
43
        $password,
44
        $host = 'localhost',
45
        $port = 3306,
46
        $driver = 'mysql',
47
        $charset = 'utf8'
48
    ) {
49
        // If we have not connected yet
50
        if (!isset($this->driver)) {
51
            $this->database = $database;
52
53
            // Check if configured database exists
54
            $this->driver = new PDO($host, $database, $username, $password, $charset, $port, $driver);
0 ignored issues
show
Documentation Bug introduced by
It seems like new \samsonframework\orm...harset, $port, $driver) of type object<samsonframework\orm\PDO> is incompatible with the declared type object<PDO> of property $driver.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
55
56
            // Set correct encodings
57
            $this->query("set character_set_client='utf8'");
58
            $this->query("set character_set_results='utf8'");
59
            $this->query("set collation_connection='utf8_general_ci'");
60
        }
61
    }
62
63
    /**
64
     * Get database name
65
     * @return string
66
     */
67
    public function database()
68
    {
69
        return $this->database;
70
    }
71
72
    /**
73
     * Get entity query manager
74
     * @param string $entity Entity identifier
75
     * @return Query Query manager instance
76
     */
77
    public function manager($entity)
78
    {
79
        return new Query($entity, $this);
80
    }
81
82
    /**
83
     * Intreal error beautifier
84
     * @param \Exception $e
85
     * @param $sql
86
     */
87
    private function outputError(\Exception $e, $sql, $text = 'Error executing database query:')
88
    {
89
        elapsed('erorr');
90
        echo("\n" . '<div style="font-size:12px; position:relative; background:red; z-index:9999999;">'
91
        .'<div style="padding:4px 10px;">'.$text.'</div>'
92
            .'<div style="padding:0px 10px;">['.htmlspecialchars($e->getMessage()).']</div>'
93
            .'<textarea style="display:block; width:100%; min-height:100px;">'.$sql . '</textarea></div>');
94
    }
95
96
    /**
97
     * Proxy function for executing database fetching logic with exception,
98
     * error, profile handling
99
     * @param callback $fetcher Callback for fetching
100
     * @return mixed Fetching function result
101
     */
102
    private function execute($fetcher)
103
    {
104
        $result = array();
105
106
        if (isset($this->driver)) {
107
            // Store timestamp
108
            $tsLast = microtime(true);
109
110
            try { // Call fetcher
111
                // Get argument and remove first one
112
                $args = func_get_args();
113
                array_shift($args);
114
115
                // Proxy calling of fetcher function with passing parameters
116
                $result = call_user_func_array($fetcher, $args);
117
            } catch (\PDOException $e) {
118
                $this->outputError($e, $sql, 'Error executing ['.$fetcher.']');
0 ignored issues
show
Bug introduced by
The variable $sql does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
119
            }
120
121
            // Store queries count
122
            $this->count++;
123
124
            // Count elapsed time
125
            $this->elapsed += microtime(true) - $tsLast;
126
        }
127
128
        return $result;
129
    }
130
131
    /**
132
     * High-level database query executor
133
     * @param string $sql SQL statement
134
     * @return mixed Database query result
135
     */
136
    private function innerQuery($sql)
137
    {
138
        try {
139
            // Perform database query
140
            $result = $this->driver->prepare($sql)->execute();
0 ignored issues
show
Unused Code introduced by
$result is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
141
        } catch (\PDOException $e) {
142
            $this->outputError($e, $sql);
143
        }
144
    }
145
146
    /**
147
     * Retrieve array of records from a database, if $className is passed method
148
     * will try to create an object of that type. If request has failed than
149
     * method will return empty array of stdClass all arrays regarding to $className is
150
     * passed or not.
151
     *
152
     * @param string $sql Query text
153
     * @return array Collection of arrays or objects
154
     */
155
    private function innerFetch($sql, $className = null)
156
    {
157
        try {
158
            // Perform database query
159
            if (!isset($className)) { // Return array
160
                return $this->driver->query($sql)->fetchAll(\PDO::FETCH_ASSOC);
161
            } else { // Create object of passed class name
162
                return $this->driver->query($sql)->fetchAll(\PDO::FETCH_CLASS, $className, array(&$this));
163
            }
164
        } catch (\PDOException $e) {
165
            $this->outputError($e, $sql, 'Fetching database records:');
166
        }
167
    }
168
169
    /**
170
     * Special accelerated function to retrieve db record fields instead of objects
171
     *
172
     * @param string $className
173
     * @param mixed $query
174
     * @param string $field
175
     *
176
     * @return array
177
     */
178
    private function innerFetchColumn($className, $query, $field)
179
    {
180
        // Get SQL
181
        $sql = $this->prepareSQL($className, $query);
0 ignored issues
show
Bug introduced by
The method prepareSQL() does not seem to exist on object<samsonframework\orm\Database>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
182
183
        // TODO: Remove old attributes retrieval
184
        // Get table column index by its name
185
        $columnIndex = array_search($field, array_values($className::$_table_attributes));
186
187
        try {
188
            // Perform database query
189
            return $this->driver->query($sql)->fetchAll(\PDO::FETCH_COLUMN, $columnIndex);
190
        } catch (\PDOException $e) {
191
            $this->outputError($e, $sql, 'Error fetching records column values:');
192
        }
193
    }
194
195
    /**
196
     * High-level database query executor
197
     * @param string $sql SQL statement
198
     * @return mixed Database query result
199
     */
200
    public function query($sql)
201
    {
202
        return $this->execute(array($this, 'innerQuery'), $sql);
203
    }
204
205
    /**
206
     * Retrieve array of records from a database, if $className is passed method
207
     * will try to create an object of that type. If request has failed than
208
     * method will return empty array of stdClass all arrays regarding to $className is
209
     * passed or not.
210
     *
211
     * @param string $sql Query text
212
     * @return array Collection of arrays or objects
213
     */
214
    public function fetch($sql)
215
    {
216
        return $this->execute(array($this, 'innerFetch'), $sql);
217
    }
218
219
    /**
220
     * Special accelerated function to retrieve db record fields instead of objects
221
     *
222
     * @param string $className
223
     * @param mixed $query
224
     * @param string $field
225
     *
226
     * @return array
227
     */
228
    public function fetchColumn($className, $query, $field)
229
    {
230
        return $this->execute(array($this, 'innerFetchColumn'), $className, $query, $field);
231
    }
232
233
    public function create($className, &$object = null)
234
    {
235
        // ??
236
        $fields = $this->getQueryFields($className, $object);
237
238
        $this->query('INSERT INTO `' . $className::$_table_name . '` (`'
239
            . implode('`,`', array_keys($fields)) . '`)
240
            VALUES (' . implode(',', $fields) . ')'
241
        );
242
243
        // Return last inserted row identifier
244
        return $this->driver->lastInsertId();
245
    }
246
247
    public function update($className, &$object)
248
    {
249
        $this->query('UPDATE `' . $className::$_table_name . '` SET '
250
            . implode(',', $this->getQueryFields($className, $object, true))
251
            . ' WHERE ' . $className::$_table_name . '.' . $className::$_primary . '="'
252
            . $object->id . '"');
253
    }
254
255
    public function delete($className, &$object)
256
    {
257
        $this->query('DELETE FROM `' . $className::$_table_name . '` WHERE '
258
            . $className::$_primary . ' = "' . $object->id . '"'
259
        );
260
    }
261
262
    /** Count query result */
263
    public function count($className, $query)
264
    {
265
        // Get SQL
266
        $sql = 'SELECT Count(*) as __Count FROM (' . $this->prepareSQL($className, $query) . ') as __table';
0 ignored issues
show
Bug introduced by
The method prepareSQL() does not seem to exist on object<samsonframework\orm\Database>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
267
268
        // Выполним запрос к БД
269
        $result = $this->fetch($sql);
270
271
        return $result[0]['__Count'];
272
    }
273
274
    /**
275
     * Выполнить защиту значения поля для его безопасного использования в запросах
276
     *
277
     * @param string $value Значения поля для запроса
278
     * @return string $value Безопасное представление значения поля для запроса
279
     */
280
    protected function protectQueryValue($value)
281
    {
282
        // If magic quotes are on - remove slashes
283
        if (get_magic_quotes_gpc()) {
284
            $value = stripslashes($value);
285
        }
286
287
        // Normally escape string
288
        $value = $this->driver->quote($value);
289
290
        // Return value in quotes
291
        return $value;
292
    }
293
294
    /**
295
     * Prepare create & update SQL statements fields
296
     * @param string $className Entity name
297
     * @param Record $object Database object to get values(if needed)
298
     * @param bool $straight Way of forming SQL field statements
299
     * @return array Collection of key => value with SQL fields statements
300
     */
301
    protected function &getQueryFields($className, & $object = null, $straight = false)
302
    {
303
        // Результирующая коллекция
304
        $collection = array();
305
306
        // Установим флаг получения значений атрибутов из переданного объекта
307
        $use_values = isset($object);
308
309
        // Переберем "настоящее" имена атрибутов схемы данных для объекта
310
        foreach ($className::$_table_attributes as $attribute => $map_attribute) {
311
            // Отметки времени не заполняем
312
            if ($className::$_types[$attribute] == 'timestamp') {
313
                continue;
314
            }
315
316
            // Основной ключ не заполняем
317
            if ($className::$_primary == $attribute) {
318
                continue;
319
            }
320
321
            // Получим значение атрибута объекта защитив от инъекций, если объект передан
322
            $value = $use_values ? $this->driver->quote($object->$map_attribute) : '';
323
324
            // Добавим значение поля, в зависимости от вида вывывода метода
325
            $collection[$map_attribute] = ($straight ? $className::$_table_name . '.' . $map_attribute . '=' : '') . $value;
326
        }
327
328
        // Вернем полученную коллекцию
329
        return $collection;
330
    }
331
332
    /** @deprecated Use query() */
333
    public function &simple_query($sql)
0 ignored issues
show
Coding Style introduced by
Method name "Database::simple_query" is not in camel caps format
Loading history...
334
    {
335
        return $this->query($sql);
336
    }
337
}
338