1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/* |
4
|
|
|
* This file is part of the Ocrend Framewok 2 package. |
5
|
|
|
* |
6
|
|
|
* (c) Ocrend Software <[email protected]> |
7
|
|
|
* |
8
|
|
|
* For the full copyright and license information, please view the LICENSE |
9
|
|
|
* file that was distributed with this source code. |
10
|
|
|
*/ |
11
|
|
|
|
12
|
|
|
namespace Ocrend\Kernel\Database; |
13
|
|
|
|
14
|
|
|
/** |
15
|
|
|
* Clase para conectar todos los modelos del sistema y compartir la configuración. |
16
|
|
|
* Inicializa elementos escenciales como la conexión con la base de datos. |
17
|
|
|
* |
18
|
|
|
* @author Brayan Narváez <[email protected]> |
19
|
|
|
*/ |
20
|
|
|
|
21
|
|
|
class Database extends \PDO { |
22
|
|
|
|
23
|
|
|
/** |
24
|
|
|
* Contiene la instancia de conexión a la base de datos |
25
|
|
|
* |
26
|
|
|
* @var Database |
27
|
|
|
*/ |
28
|
|
|
private static $inst; |
29
|
|
|
|
30
|
|
|
/** |
31
|
|
|
* Inicia la instancia de conexión, si esta ya ha sido declarada antes, no la duplica y ahorra memoria. |
32
|
|
|
* |
33
|
|
|
* @param string|null $name : Nombre de la base de datos a conectar |
34
|
|
|
* @param string|null $motor: Motor de la base de datos a conectar |
35
|
|
|
* @param bool $new_instance: true para iniciar una nueva instancia (al querer conectar a una DB distinta) |
36
|
|
|
* |
37
|
|
|
* @return Database : Instancia de conexión |
38
|
|
|
*/ |
39
|
|
|
final public static function Start($name = null, $motor = null, bool $new_instance = false) : Database { |
40
|
|
|
global $config; |
41
|
|
|
|
42
|
|
|
if (!self::$inst instanceof self or $new_instance) { |
43
|
|
|
self::$inst = new self( |
44
|
|
|
null === $name ? $config['database']['name'] : $name, |
45
|
|
|
null === $motor ? $config['database']['motor'] : $motor |
46
|
|
|
); |
47
|
|
|
} |
48
|
|
|
|
49
|
|
|
return self::$inst; |
50
|
|
|
} |
51
|
|
|
|
52
|
|
|
/** |
53
|
|
|
* Motor de base de datos MySQL |
54
|
|
|
* |
55
|
|
|
* @param array $params: Lista de parámetros de configuración |
56
|
|
|
*/ |
57
|
|
|
final private function motor_mysql(array $params) { |
58
|
|
|
parent::__construct('mysql:host='.$params['host'].';dbname='.$params['name'], |
59
|
|
|
$params['user'], |
60
|
|
|
$params['pass'], |
61
|
|
|
array(\PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8', |
62
|
|
|
\PDO::ATTR_EMULATE_PREPARES => false, |
63
|
|
|
\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION |
64
|
|
|
)); |
65
|
|
|
} |
66
|
|
|
|
67
|
|
|
/** |
68
|
|
|
* Motor de base de datos SQLite |
69
|
|
|
* |
70
|
|
|
* @param array $params: Lista de parámetros de configuración |
71
|
|
|
*/ |
72
|
|
|
final private function motor_sqlite(array $params) { |
73
|
|
|
parent::__construct('sqlite:'.$params['name']); |
74
|
|
|
} |
75
|
|
|
|
76
|
|
|
/** |
77
|
|
|
* Motor de base de datos Cubrid |
78
|
|
|
* |
79
|
|
|
* @param array $params: Lista de parámetros de configuración |
80
|
|
|
*/ |
81
|
|
View Code Duplication |
final private function motor_cubrid(array $params) { |
|
|
|
|
82
|
|
|
parent::__construct('cubrid:host='.$params['host'].';dbname='.$params['name'].';port='.$params['port'], |
83
|
|
|
$params['user'], |
84
|
|
|
$params['pass'],array( |
85
|
|
|
\PDO::ATTR_EMULATE_PREPARES => false, |
86
|
|
|
\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION |
87
|
|
|
)); |
88
|
|
|
} |
89
|
|
|
|
90
|
|
|
/** |
91
|
|
|
* Motor de base de datos Firebird |
92
|
|
|
* |
93
|
|
|
* @param array $params: Lista de parámetros de configuración |
94
|
|
|
*/ |
95
|
|
|
final private function motor_firebird(array $params) { |
96
|
|
|
parent::__construct('firebird:dbname='.$params['host'].':'.$params['name'], |
97
|
|
|
$params['user'], |
98
|
|
|
$params['pass'],array( |
99
|
|
|
\PDO::ATTR_EMULATE_PREPARES => false, |
100
|
|
|
\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION |
101
|
|
|
)); |
102
|
|
|
} |
103
|
|
|
|
104
|
|
|
/** |
105
|
|
|
* Motor de base de datos ODBC |
106
|
|
|
* |
107
|
|
|
* @param array $params: Lista de parámetros de configuración |
108
|
|
|
*/ |
109
|
|
|
final private function motor_odbc(array $params) { |
110
|
|
|
parent::__construct('odbc:'.$params['name'], |
111
|
|
|
$params['user'], |
112
|
|
|
$params['pass'] |
113
|
|
|
); |
114
|
|
|
} |
115
|
|
|
|
116
|
|
|
/** |
117
|
|
|
* Motor de base de datos Oracle |
118
|
|
|
* |
119
|
|
|
* @param array $params: Lista de parámetros de configuración |
120
|
|
|
*/ |
121
|
|
|
final private function motor_oracle(array $params) { |
122
|
|
|
parent::__construct('oci:dbname=(DESCRIPTION = |
123
|
|
|
(ADDRESS_LIST = |
124
|
|
|
(ADDRESS = (PROTOCOL = '.$params['protocol'].')(HOST = '.$params['host'].')(PORT = '.$params['port'].')) |
125
|
|
|
) |
126
|
|
|
(CONNECT_DATA = |
127
|
|
|
(SERVICE_NAME = '.$params['name'].') |
128
|
|
|
) |
129
|
|
|
);charset=utf8',$params['database']['user'],$params['database']['pass'], |
130
|
|
|
array(\PDO::ATTR_EMULATE_PREPARES => false, |
131
|
|
|
\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION, |
132
|
|
|
\PDO::ATTR_DEFAULT_FETCH_MODE => \PDO::FETCH_ASSOC |
133
|
|
|
)); |
134
|
|
|
} |
135
|
|
|
|
136
|
|
|
/** |
137
|
|
|
* Motor de base de datos PostgreSQL |
138
|
|
|
* |
139
|
|
|
* @param array $params: Lista de parámetros de configuración |
140
|
|
|
*/ |
141
|
|
View Code Duplication |
final private function motor_postgresql(array $params) { |
|
|
|
|
142
|
|
|
parent::__construct('pgsql:host='.$params['host'].';dbname='.$params['name'].';charset=utf8', |
143
|
|
|
$params['user'], |
144
|
|
|
$params['pass'],array( |
145
|
|
|
\PDO::ATTR_EMULATE_PREPARES => false, |
146
|
|
|
\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION |
147
|
|
|
)); |
148
|
|
|
} |
149
|
|
|
|
150
|
|
|
/** |
151
|
|
|
* Motor de base de datos MSSQL |
152
|
|
|
* Añadido por marc2684 https://github.com/prinick96/Ocrend-Framework/issues/7 |
153
|
|
|
* |
154
|
|
|
* @param array $params: Lista de parámetros de configuración |
155
|
|
|
*/ |
156
|
|
|
final private function motor_mssql(array $params) { |
157
|
|
|
parent::__construct('sqlsrv:Server='.$params['host'].';Database='.$params['name'].';ConnectionPooling=0', |
158
|
|
|
$params['user'], |
159
|
|
|
$params['pass'],array(\PDO::ATTR_EMULATE_PREPARES => false, |
160
|
|
|
\PDO::ATTR_ERRMODE => |
161
|
|
|
\PDO::ERRMODE_EXCEPTION |
162
|
|
|
)); |
163
|
|
|
} |
164
|
|
|
|
165
|
|
|
/** |
166
|
|
|
* Inicia la conexión con la base de datos seleccionada |
167
|
|
|
* |
168
|
|
|
* @param string|null $name : Nombre de la base de datos a conectar |
169
|
|
|
* @param string|null $motor: Motor de la base de datos a conectar |
170
|
|
|
* @param bool $new_instance: true para iniciar una nueva instancia (al querer conectar a una DB distinta) |
171
|
|
|
* |
172
|
|
|
* @throws \RuntimeException si el motor no existe |
173
|
|
|
* @throws \RuntimeException si existe algún problema de conexión con la base de datos |
174
|
|
|
* @return Database : Instancia de conexión |
175
|
|
|
*/ |
176
|
|
|
final public function __construct(string $name, string $motor) { |
177
|
|
|
global $config; |
178
|
|
|
|
179
|
|
|
try { |
180
|
|
|
# Verificar existencia del motor |
181
|
|
|
if(method_exists($this,'motor_' . $motor)) { |
182
|
|
|
$this->{'motor_' . $motor}(array( |
183
|
|
|
'host' => $config['database']['host'], |
184
|
|
|
'name' => $name, |
185
|
|
|
'user' => $config['database']['user'], |
186
|
|
|
'pass' => $config['database']['pass'], |
187
|
|
|
'port' => $config['database']['port'], |
188
|
|
|
'protocol' => $config['database']['protocol'] |
189
|
|
|
)); |
190
|
|
|
} else { |
191
|
|
|
throw new \RuntimeException('Motor '. $motor .' de conexión no identificado.'); |
192
|
|
|
} |
193
|
|
|
} catch (\PDOException $e) { |
194
|
|
|
throw new \RuntimeException('Problema al conectar con la base de datos: ' . $e->getMessage()); |
195
|
|
|
} |
196
|
|
|
} |
197
|
|
|
|
198
|
|
|
/** |
199
|
|
|
* Convierte en arreglo asociativo de todos los resultados arrojados por una query |
200
|
|
|
* |
201
|
|
|
* @param object \PDOStatement $query, valor devuelto de la query |
202
|
|
|
* |
203
|
|
|
* @return array arreglo asociativo |
204
|
|
|
*/ |
205
|
|
|
final public function fetch_array(\PDOStatement $query) : array { |
206
|
|
|
return $query->fetchAll(\PDO::FETCH_ASSOC); |
207
|
|
|
} |
208
|
|
|
|
209
|
|
|
/** |
210
|
|
|
* Cantidad de filas encontradas después de un SELECT |
211
|
|
|
* |
212
|
|
|
* @param object \PDOStatement $query, valor devuelto de la query |
213
|
|
|
* |
214
|
|
|
* @return int numero de filas encontradas |
215
|
|
|
*/ |
216
|
|
|
final public function rows(\PDOStatement $query) : int { |
217
|
|
|
return $query->rowCount(); |
218
|
|
|
} |
219
|
|
|
|
220
|
|
|
/** |
221
|
|
|
* Sana un valor para posteriormente ser introducido en una query |
222
|
|
|
* |
223
|
|
|
* @param null/string/int/float a sanar |
224
|
|
|
* |
225
|
|
|
* @return mixed elemento sano |
226
|
|
|
*/ |
227
|
|
|
final public function scape($e) { |
228
|
|
|
if (null === $e) { |
229
|
|
|
return ''; |
230
|
|
|
} |
231
|
|
|
|
232
|
|
|
if (is_numeric($e) and $e <= 2147483647) { |
233
|
|
|
if (explode('.', $e)[0] != $e) { |
234
|
|
|
return (float) $e; |
235
|
|
|
} |
236
|
|
|
return (int) $e; |
237
|
|
|
} |
238
|
|
|
|
239
|
|
|
return (string) trim(str_replace(['\\', "\x00", '\n', '\r', "'", '"', "\x1a"], ['\\\\', '\\0', '\\n', '\\r', "\'", '\"', '\\Z'], $e)); |
240
|
|
|
} |
241
|
|
|
|
242
|
|
|
/** |
243
|
|
|
* Borra una serie de elementos de forma segura de una tabla en la base de datos |
244
|
|
|
* |
245
|
|
|
* @param string $table: Tabla a la cual se le quiere remover un elemento |
246
|
|
|
* @param string $where: Condición de borrado que define quien/quienes son dichos elementos |
247
|
|
|
* @param string $limit: Por defecto se limita a borrar un solo elemento que cumpla el $where |
248
|
|
|
* |
249
|
|
|
* @return \PDOStatement |
250
|
|
|
*/ |
251
|
|
|
final public function delete(string $table, string $where, string $limit = 'LIMIT 1') : \PDOStatement { |
252
|
|
|
return $this->query("DELETE FROM $table WHERE $where $limit;"); |
253
|
|
|
} |
254
|
|
|
|
255
|
|
|
/** |
256
|
|
|
* Inserta una serie de elementos a una tabla en la base de datos |
257
|
|
|
* |
258
|
|
|
* @param string $table: Tabla a la cual se le va a insertar elementos |
259
|
|
|
* @param array $e: Arreglo asociativo de elementos, con la estrctura 'campo_en_la_tabla' => 'valor_a_insertar_en_ese_campo', |
260
|
|
|
* todos los elementos del arreglo $e, serán sanados por el método sin necesidad de hacerlo manualmente al crear el arreglo |
261
|
|
|
* |
262
|
|
|
* @throws \RuntimeException si el arreglo está vacío |
263
|
|
|
* @return \PDOStatement |
264
|
|
|
*/ |
265
|
|
|
final public function insert(string $table, array $e) : \PDOStatement { |
266
|
|
|
if (sizeof($e) == 0) { |
267
|
|
|
throw new \RuntimeException('El arreglo pasado por $this->db->insert(\'' . $table . '\',...) está vacío.'); |
268
|
|
|
} |
269
|
|
|
|
270
|
|
|
$query = "INSERT INTO $table ("; |
271
|
|
|
$values = ''; |
272
|
|
|
foreach ($e as $campo => $v) { |
273
|
|
|
$query .= $campo . ','; |
274
|
|
|
$values .= '\'' . $this->scape($v) . '\','; |
275
|
|
|
} |
276
|
|
|
$query[strlen($query) - 1] = ')'; |
277
|
|
|
$values[strlen($values) - 1] = ')'; |
278
|
|
|
$query .= ' VALUES (' . $values . ';'; |
279
|
|
|
|
280
|
|
|
return $this->query($query); |
281
|
|
|
} |
282
|
|
|
|
283
|
|
|
/** |
284
|
|
|
* Actualiza elementos de una tabla en la base de datos según una condición |
285
|
|
|
* |
286
|
|
|
* @param string $table: Tabla a actualizar |
287
|
|
|
* @param array $e: Arreglo asociativo de elementos, con la estrctura 'campo_en_la_tabla' => 'valor_a_insertar_en_ese_campo', |
288
|
|
|
* todos los elementos del arreglo $e, serán sanados por el método sin necesidad de hacerlo manualmente al crear el arreglo |
289
|
|
|
* @param string $where: Condición que indica quienes serán modificados |
290
|
|
|
* @param string $limite: Límite de elementos modificados, por defecto los modifica a todos |
291
|
|
|
* |
292
|
|
|
* @throws \RuntimeException si el arreglo está vacío |
293
|
|
|
* @return \PDOStatement |
294
|
|
|
*/ |
295
|
|
|
final public function update(string $table, array $e, string $where, string $limit = '') : \PDOStatement { |
296
|
|
|
if (sizeof($e) == 0) { |
297
|
|
|
throw new \RuntimeException('El arreglo pasado por $this->db->update(\'' . $table . '\'...) está vacío.'); |
298
|
|
|
} |
299
|
|
|
|
300
|
|
|
$query = "UPDATE $table SET "; |
301
|
|
|
foreach ($e as $campo => $valor) { |
302
|
|
|
$query .= $campo . '=\'' . $this->scape($valor) . '\','; |
303
|
|
|
} |
304
|
|
|
$query[strlen($query) - 1] = ' '; |
305
|
|
|
$query .= "WHERE $where $limit;"; |
306
|
|
|
|
307
|
|
|
return $this->query($query); |
308
|
|
|
} |
309
|
|
|
|
310
|
|
|
/** |
311
|
|
|
* Selecciona y lista en un arreglo asociativo/numérico los resultados de una búsqueda en la base de datos |
312
|
|
|
* |
313
|
|
|
* @param string $e: Elementos a seleccionar separados por coma |
314
|
|
|
* @param string $tbale: Tabla de la cual se quiere extraer los elementos $e |
315
|
|
|
* @param string $where: Condición que indica quienes son los que se extraen, si no se coloca extrae todos |
316
|
|
|
* @param string $limite: Límite de elemntos a traer, por defecto trae TODOS los que cumplan $where |
317
|
|
|
* |
318
|
|
|
* @return mixed false si no encuentra ningún resultado, array asociativo/numérico si consigue al menos uno |
319
|
|
|
*/ |
320
|
|
|
final public function select(string $e, string $table, string $where = '1 = 1', string $limit = "") { |
321
|
|
|
return $this->query_select("SELECT $e FROM $table WHERE $where $limit;"); |
322
|
|
|
} |
323
|
|
|
|
324
|
|
|
/** |
325
|
|
|
* Realiza una query, ideal para trabajar con SELECTS, JOINS, etc |
326
|
|
|
* |
327
|
|
|
* @return array|false si no encuentra ningún resultado, array asociativo/numérico si consigue al menos uno |
328
|
|
|
*/ |
329
|
|
|
final public function query_select(string $query) { |
330
|
|
|
$sql = $this->query($query); |
331
|
|
|
$result = $sql->fetchAll(); |
332
|
|
|
$sql->closeCursor(); |
333
|
|
|
|
334
|
|
|
if (sizeof($result) > 0) { |
335
|
|
|
return $result; |
336
|
|
|
} |
337
|
|
|
|
338
|
|
|
return false; |
339
|
|
|
} |
340
|
|
|
|
341
|
|
|
/** |
342
|
|
|
* Alert para evitar clonaciones |
343
|
|
|
* |
344
|
|
|
* @throws \RuntimeException si se intenta clonar la conexión |
345
|
|
|
* @return void |
346
|
|
|
*/ |
347
|
|
|
final public function __clone() { |
348
|
|
|
throw new \RuntimeException('Estás intentando clonar la Conexión'); |
349
|
|
|
} |
350
|
|
|
|
351
|
|
|
} |
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.