|
1
|
|
|
<?php |
|
2
|
|
|
|
|
3
|
|
|
/* For licensing terms, see /license.txt */ |
|
4
|
|
|
|
|
5
|
|
|
use Doctrine\Common\Annotations\AnnotationRegistry; |
|
6
|
|
|
use Doctrine\Common\EventManager; |
|
7
|
|
|
use Doctrine\DBAL\Connection; |
|
8
|
|
|
use Doctrine\DBAL\Driver\Statement; |
|
9
|
|
|
use Doctrine\DBAL\Types\Type; |
|
10
|
|
|
use Doctrine\ORM\Configuration; |
|
11
|
|
|
use Doctrine\ORM\EntityManager; |
|
12
|
|
|
|
|
13
|
|
|
class Database |
|
14
|
|
|
{ |
|
15
|
|
|
/** |
|
16
|
|
|
* @var EntityManager |
|
17
|
|
|
*/ |
|
18
|
|
|
private static $em; |
|
19
|
|
|
private static $connection; |
|
20
|
|
|
|
|
21
|
|
|
/** |
|
22
|
|
|
* Setup doctrine only for the installation. |
|
23
|
|
|
* |
|
24
|
|
|
* @param array $params |
|
25
|
|
|
* @param string $entityRootPath |
|
26
|
|
|
*/ |
|
27
|
|
|
public function connect($params = [], $entityRootPath = '') |
|
28
|
|
|
{ |
|
29
|
|
|
$config = self::getDoctrineConfig($entityRootPath); |
|
30
|
|
|
$config->setAutoGenerateProxyClasses(true); |
|
31
|
|
|
$config->setEntityNamespaces( |
|
32
|
|
|
[ |
|
33
|
|
|
'ChamiloCoreBundle' => 'Chamilo\CoreBundle\Entity', |
|
34
|
|
|
'ChamiloCourseBundle' => 'Chamilo\CourseBundle\Entity', |
|
35
|
|
|
] |
|
36
|
|
|
); |
|
37
|
|
|
|
|
38
|
|
|
$params['charset'] = 'utf8'; |
|
39
|
|
|
$sysPath = api_get_path(SYMFONY_SYS_PATH); |
|
40
|
|
|
|
|
41
|
|
|
$cache = new Doctrine\Common\Cache\ArrayCache(); |
|
42
|
|
|
// standard annotation reader |
|
43
|
|
|
$annotationReader = new Doctrine\Common\Annotations\AnnotationReader(); |
|
44
|
|
|
$cachedAnnotationReader = new Doctrine\Common\Annotations\CachedReader( |
|
45
|
|
|
$annotationReader, // use reader |
|
46
|
|
|
$cache // and a cache driver |
|
47
|
|
|
); |
|
48
|
|
|
|
|
49
|
|
|
$evm = new EventManager(); |
|
50
|
|
|
$timestampableListener = new Gedmo\Timestampable\TimestampableListener(); |
|
51
|
|
|
$timestampableListener->setAnnotationReader($cachedAnnotationReader); |
|
52
|
|
|
$evm->addEventSubscriber($timestampableListener); |
|
53
|
|
|
|
|
54
|
|
|
$driverChain = new \Doctrine\Persistence\Mapping\Driver\MappingDriverChain(); |
|
55
|
|
|
// load superclass metadata mapping only, into driver chain |
|
56
|
|
|
// also registers Gedmo annotations.NOTE: you can personalize it |
|
57
|
|
|
Gedmo\DoctrineExtensions::registerAbstractMappingIntoDriverChainORM( |
|
58
|
|
|
$driverChain, // our metadata driver chain, to hook into |
|
59
|
|
|
$cachedAnnotationReader // our cached annotation reader |
|
60
|
|
|
); |
|
61
|
|
|
|
|
62
|
|
|
AnnotationRegistry::registerLoader( |
|
63
|
|
|
function ($class) use ($sysPath) { |
|
64
|
|
|
$file = str_replace("\\", DIRECTORY_SEPARATOR, $class).".php"; |
|
65
|
|
|
$file = str_replace('Symfony/Component/Validator', '', $file); |
|
66
|
|
|
$file = str_replace('Symfony\Component\Validator', '', $file); |
|
67
|
|
|
$file = str_replace('Symfony/Component/Serializer', '', $file); |
|
68
|
|
|
|
|
69
|
|
|
$fileToInclude = $sysPath.'vendor/symfony/validator/'.$file; |
|
70
|
|
|
|
|
71
|
|
|
if (file_exists($fileToInclude)) { |
|
72
|
|
|
// file exists makes sure that the loader fails silently |
|
73
|
|
|
require_once $fileToInclude; |
|
74
|
|
|
|
|
75
|
|
|
return true; |
|
76
|
|
|
} |
|
77
|
|
|
|
|
78
|
|
|
$fileToInclude = $sysPath.'vendor/symfony/validator/Constraints/'.$file; |
|
79
|
|
|
if (file_exists($fileToInclude)) { |
|
80
|
|
|
// file exists makes sure that the loader fails silently |
|
81
|
|
|
require_once $fileToInclude; |
|
82
|
|
|
|
|
83
|
|
|
return true; |
|
84
|
|
|
} |
|
85
|
|
|
|
|
86
|
|
|
$fileToInclude = $sysPath.'vendor/symfony/serializer/'.$file; |
|
87
|
|
|
|
|
88
|
|
|
if (file_exists($fileToInclude)) { |
|
89
|
|
|
// file exists makes sure that the loader fails silently |
|
90
|
|
|
require_once $fileToInclude; |
|
91
|
|
|
|
|
92
|
|
|
return true; |
|
93
|
|
|
} |
|
94
|
|
|
} |
|
95
|
|
|
); |
|
96
|
|
|
|
|
97
|
|
|
AnnotationRegistry::registerFile( |
|
98
|
|
|
$sysPath.'vendor/api-platform/core/src/Annotation/ApiResource.php' |
|
99
|
|
|
); |
|
100
|
|
|
AnnotationRegistry::registerFile( |
|
101
|
|
|
$sysPath.'vendor/api-platform/core/src/Annotation/ApiFilter.php' |
|
102
|
|
|
); |
|
103
|
|
|
AnnotationRegistry::registerFile( |
|
104
|
|
|
$sysPath.'vendor/api-platform/core/src/Annotation/ApiProperty.php' |
|
105
|
|
|
); |
|
106
|
|
|
AnnotationRegistry::registerFile( |
|
107
|
|
|
$sysPath.'vendor/api-platform/core/src/Annotation/ApiSubresource.php' |
|
108
|
|
|
); |
|
109
|
|
|
|
|
110
|
|
|
$entityManager = EntityManager::create($params, $config, $evm); |
|
111
|
|
|
|
|
112
|
|
|
if (false === Type::hasType('uuid')) { |
|
113
|
|
|
Type::addType('uuid', \Symfony\Bridge\Doctrine\Types\UuidType::class); |
|
114
|
|
|
} |
|
115
|
|
|
|
|
116
|
|
|
$connection = $entityManager->getConnection(); |
|
117
|
|
|
AnnotationRegistry::registerFile( |
|
118
|
|
|
$sysPath.'vendor/symfony/doctrine-bridge/Validator/Constraints/UniqueEntity.php' |
|
119
|
|
|
); |
|
120
|
|
|
|
|
121
|
|
|
$this->setConnection($connection); |
|
122
|
|
|
$this->setManager($entityManager); |
|
123
|
|
|
} |
|
124
|
|
|
|
|
125
|
|
|
/** |
|
126
|
|
|
* @param EntityManager $em |
|
127
|
|
|
*/ |
|
128
|
|
|
public static function setManager($em) |
|
129
|
|
|
{ |
|
130
|
|
|
self::$em = $em; |
|
131
|
|
|
} |
|
132
|
|
|
|
|
133
|
|
|
public static function setConnection(Connection $connection) |
|
134
|
|
|
{ |
|
135
|
|
|
self::$connection = $connection; |
|
136
|
|
|
} |
|
137
|
|
|
|
|
138
|
|
|
/** |
|
139
|
|
|
* @return Connection |
|
140
|
|
|
*/ |
|
141
|
|
|
public static function getConnection() |
|
142
|
|
|
{ |
|
143
|
|
|
return self::$connection; |
|
144
|
|
|
} |
|
145
|
|
|
|
|
146
|
|
|
/** |
|
147
|
|
|
* @return EntityManager |
|
148
|
|
|
*/ |
|
149
|
|
|
public static function getManager() |
|
150
|
|
|
{ |
|
151
|
|
|
return self::$em; |
|
152
|
|
|
} |
|
153
|
|
|
|
|
154
|
|
|
/** |
|
155
|
|
|
* Returns the name of the main database. |
|
156
|
|
|
* |
|
157
|
|
|
* @return string |
|
158
|
|
|
*/ |
|
159
|
|
|
public static function get_main_database() |
|
160
|
|
|
{ |
|
161
|
|
|
return self::getManager()->getConnection()->getDatabase(); |
|
162
|
|
|
} |
|
163
|
|
|
|
|
164
|
|
|
/** |
|
165
|
|
|
* Get main table. |
|
166
|
|
|
* |
|
167
|
|
|
* @param string $table |
|
168
|
|
|
* |
|
169
|
|
|
* @return string |
|
170
|
|
|
*/ |
|
171
|
|
|
public static function get_main_table($table) |
|
172
|
|
|
{ |
|
173
|
|
|
return $table; |
|
174
|
|
|
} |
|
175
|
|
|
|
|
176
|
|
|
/** |
|
177
|
|
|
* Get course table. |
|
178
|
|
|
* |
|
179
|
|
|
* @param string $table |
|
180
|
|
|
* |
|
181
|
|
|
* @return string |
|
182
|
|
|
*/ |
|
183
|
|
|
public static function get_course_table($table) |
|
184
|
|
|
{ |
|
185
|
|
|
return DB_COURSE_PREFIX.$table; |
|
186
|
|
|
} |
|
187
|
|
|
|
|
188
|
|
|
/** |
|
189
|
|
|
* Counts the number of rows in a table. |
|
190
|
|
|
* |
|
191
|
|
|
* @param string $table The table of which the rows should be counted |
|
192
|
|
|
* |
|
193
|
|
|
* @return int the number of rows in the given table |
|
194
|
|
|
* |
|
195
|
|
|
* @deprecated |
|
196
|
|
|
*/ |
|
197
|
|
|
public static function count_rows($table) |
|
198
|
|
|
{ |
|
199
|
|
|
$obj = self::fetch_object(self::query("SELECT COUNT(*) AS n FROM $table")); |
|
200
|
|
|
|
|
201
|
|
|
return $obj->n; |
|
202
|
|
|
} |
|
203
|
|
|
|
|
204
|
|
|
/** |
|
205
|
|
|
* Returns the number of affected rows in the last database operation. |
|
206
|
|
|
* |
|
207
|
|
|
* @return int |
|
208
|
|
|
*/ |
|
209
|
|
|
public static function affected_rows($result) |
|
210
|
|
|
{ |
|
211
|
|
|
return $result->rowCount(); |
|
212
|
|
|
} |
|
213
|
|
|
|
|
214
|
|
|
/** |
|
215
|
|
|
* Escapes a string to insert into the database as text. |
|
216
|
|
|
* |
|
217
|
|
|
* @param string $string |
|
218
|
|
|
* |
|
219
|
|
|
* @return string |
|
220
|
|
|
*/ |
|
221
|
|
|
public static function escape_string($string) |
|
222
|
|
|
{ |
|
223
|
|
|
$string = self::getManager()->getConnection()->quote($string); |
|
224
|
|
|
// The quote method from PDO also adds quotes around the string, which |
|
225
|
|
|
// is not how the legacy mysql_real_escape_string() was used in |
|
226
|
|
|
// Chamilo, so we need to remove the quotes around. Using trim will |
|
227
|
|
|
// remove more than one quote if they are sequenced, generating |
|
228
|
|
|
// broken queries and SQL injection risks |
|
229
|
|
|
return substr($string, 1, -1); |
|
230
|
|
|
} |
|
231
|
|
|
|
|
232
|
|
|
/** |
|
233
|
|
|
* Gets the array from a SQL result (as returned by Database::query). |
|
234
|
|
|
* |
|
235
|
|
|
* @param string $option Optional: "ASSOC","NUM" or "BOTH" |
|
236
|
|
|
* |
|
237
|
|
|
* @return array|mixed |
|
238
|
|
|
*/ |
|
239
|
|
|
public static function fetch_array($result, $option = 'BOTH') |
|
240
|
|
|
{ |
|
241
|
|
|
if (false === $result) { |
|
242
|
|
|
return []; |
|
243
|
|
|
} |
|
244
|
|
|
|
|
245
|
|
|
return $result->fetch(self::customOptionToDoctrineOption($option)); |
|
246
|
|
|
} |
|
247
|
|
|
|
|
248
|
|
|
/** |
|
249
|
|
|
* Gets an associative array from a SQL result (as returned by Database::query). |
|
250
|
|
|
* |
|
251
|
|
|
* @return array |
|
252
|
|
|
*/ |
|
253
|
|
|
public static function fetch_assoc($result) |
|
254
|
|
|
{ |
|
255
|
|
|
return $result->fetch(PDO::FETCH_ASSOC); |
|
256
|
|
|
} |
|
257
|
|
|
|
|
258
|
|
|
/** |
|
259
|
|
|
* Gets the next row of the result of the SQL query |
|
260
|
|
|
* (as returned by Database::query) in an object form. |
|
261
|
|
|
* |
|
262
|
|
|
* @return mixed |
|
263
|
|
|
*/ |
|
264
|
|
|
public static function fetch_object($result) |
|
265
|
|
|
{ |
|
266
|
|
|
return $result->fetch(PDO::FETCH_OBJ); |
|
267
|
|
|
} |
|
268
|
|
|
|
|
269
|
|
|
/** |
|
270
|
|
|
* Gets the array from a SQL result (as returned by Database::query) |
|
271
|
|
|
* help achieving database independence. |
|
272
|
|
|
* |
|
273
|
|
|
* @return mixed |
|
274
|
|
|
*/ |
|
275
|
|
|
public static function fetch_row($result) |
|
276
|
|
|
{ |
|
277
|
|
|
if (false === $result) { |
|
278
|
|
|
return []; |
|
279
|
|
|
} |
|
280
|
|
|
|
|
281
|
|
|
return $result->fetch(PDO::FETCH_NUM); |
|
282
|
|
|
} |
|
283
|
|
|
|
|
284
|
|
|
/** |
|
285
|
|
|
* Gets the ID of the last item inserted into the database. |
|
286
|
|
|
* |
|
287
|
|
|
* @return string |
|
288
|
|
|
*/ |
|
289
|
|
|
public static function insert_id() |
|
290
|
|
|
{ |
|
291
|
|
|
return self::getManager()->getConnection()->lastInsertId(); |
|
292
|
|
|
} |
|
293
|
|
|
|
|
294
|
|
|
/** |
|
295
|
|
|
* @return int |
|
296
|
|
|
*/ |
|
297
|
|
|
public static function num_rows($result) |
|
298
|
|
|
{ |
|
299
|
|
|
if (false === $result) { |
|
300
|
|
|
return 0; |
|
301
|
|
|
} |
|
302
|
|
|
|
|
303
|
|
|
return $result->rowCount(); |
|
304
|
|
|
} |
|
305
|
|
|
|
|
306
|
|
|
/** |
|
307
|
|
|
* Acts as the relative *_result() function of most DB drivers and fetches a |
|
308
|
|
|
* specific line and a field. |
|
309
|
|
|
* |
|
310
|
|
|
* @param int $row |
|
311
|
|
|
* @param string $field |
|
312
|
|
|
* |
|
313
|
|
|
* @return mixed |
|
314
|
|
|
*/ |
|
315
|
|
|
public static function result($resource, $row, $field = '') |
|
316
|
|
|
{ |
|
317
|
|
|
if ($resource->rowCount() > 0) { |
|
318
|
|
|
$result = $resource->fetchAll(PDO::FETCH_BOTH); |
|
319
|
|
|
|
|
320
|
|
|
return $result[$row][$field]; |
|
321
|
|
|
} |
|
322
|
|
|
|
|
323
|
|
|
return false; |
|
324
|
|
|
} |
|
325
|
|
|
|
|
326
|
|
|
/** |
|
327
|
|
|
* @param string $query |
|
328
|
|
|
* |
|
329
|
|
|
* @return Statement |
|
330
|
|
|
*/ |
|
331
|
|
|
public static function query($query) |
|
332
|
|
|
{ |
|
333
|
|
|
$connection = self::getManager()->getConnection(); |
|
334
|
|
|
$result = null; |
|
335
|
|
|
try { |
|
336
|
|
|
$result = $connection->executeQuery($query); |
|
337
|
|
|
} catch (Exception $e) { |
|
338
|
|
|
self::handleError($e); |
|
339
|
|
|
} |
|
340
|
|
|
|
|
341
|
|
|
return $result; |
|
342
|
|
|
} |
|
343
|
|
|
|
|
344
|
|
|
/** |
|
345
|
|
|
* @param Exception $e |
|
346
|
|
|
*/ |
|
347
|
|
|
public static function handleError($e) |
|
348
|
|
|
{ |
|
349
|
|
|
$debug = 'test' === api_get_setting('server_type'); |
|
350
|
|
|
if ($debug) { |
|
351
|
|
|
throw $e; |
|
352
|
|
|
exit; |
|
|
|
|
|
|
353
|
|
|
} else { |
|
354
|
|
|
error_log($e->getMessage()); |
|
355
|
|
|
api_not_allowed(false, get_lang('An error has occurred. Please contact your system administrator.')); |
|
356
|
|
|
exit; |
|
|
|
|
|
|
357
|
|
|
} |
|
358
|
|
|
} |
|
359
|
|
|
|
|
360
|
|
|
/** |
|
361
|
|
|
* @param string $option |
|
362
|
|
|
* |
|
363
|
|
|
* @return int |
|
364
|
|
|
*/ |
|
365
|
|
|
public static function customOptionToDoctrineOption($option) |
|
366
|
|
|
{ |
|
367
|
|
|
switch ($option) { |
|
368
|
|
|
case 'ASSOC': |
|
369
|
|
|
return PDO::FETCH_ASSOC; |
|
370
|
|
|
break; |
|
|
|
|
|
|
371
|
|
|
case 'NUM': |
|
372
|
|
|
return PDO::FETCH_NUM; |
|
373
|
|
|
break; |
|
374
|
|
|
case 'BOTH': |
|
375
|
|
|
default: |
|
376
|
|
|
return PDO::FETCH_BOTH; |
|
377
|
|
|
break; |
|
378
|
|
|
} |
|
379
|
|
|
} |
|
380
|
|
|
|
|
381
|
|
|
/** |
|
382
|
|
|
* Stores a query result into an array. |
|
383
|
|
|
* |
|
384
|
|
|
* @author Olivier Brouckaert |
|
385
|
|
|
* |
|
386
|
|
|
* @param Statement $result - the return value of the query |
|
387
|
|
|
* @param string $option BOTH, ASSOC, or NUM |
|
388
|
|
|
* |
|
389
|
|
|
* @return array - the value returned by the query |
|
390
|
|
|
*/ |
|
391
|
|
|
public static function store_result($result, $option = 'BOTH') |
|
392
|
|
|
{ |
|
393
|
|
|
return $result->fetchAll(self::customOptionToDoctrineOption($option)); |
|
394
|
|
|
} |
|
395
|
|
|
|
|
396
|
|
|
/** |
|
397
|
|
|
* Database insert. |
|
398
|
|
|
* |
|
399
|
|
|
* @param string $table_name |
|
400
|
|
|
* @param array $attributes |
|
401
|
|
|
* @param bool $show_query |
|
402
|
|
|
* |
|
403
|
|
|
* @return false|int |
|
404
|
|
|
*/ |
|
405
|
|
|
public static function insert($table_name, $attributes, $show_query = false) |
|
406
|
|
|
{ |
|
407
|
|
|
if (empty($attributes) || empty($table_name)) { |
|
408
|
|
|
return false; |
|
409
|
|
|
} |
|
410
|
|
|
|
|
411
|
|
|
$params = array_keys($attributes); |
|
412
|
|
|
|
|
413
|
|
|
if (!empty($params)) { |
|
414
|
|
|
$sql = 'INSERT INTO '.$table_name.' ('.implode(',', $params).') |
|
415
|
|
|
VALUES (:'.implode(', :', $params).')'; |
|
416
|
|
|
|
|
417
|
|
|
if ($show_query) { |
|
418
|
|
|
var_dump($sql); |
|
|
|
|
|
|
419
|
|
|
error_log($sql); |
|
420
|
|
|
} |
|
421
|
|
|
|
|
422
|
|
|
$result = false; |
|
423
|
|
|
try { |
|
424
|
|
|
$statement = self::getConnection()->prepare($sql); |
|
425
|
|
|
$result = $statement->execute($attributes); |
|
426
|
|
|
} catch (Exception $e) { |
|
427
|
|
|
self::handleError($e); |
|
428
|
|
|
} |
|
429
|
|
|
|
|
430
|
|
|
if ($result) { |
|
431
|
|
|
return (int) self::getManager()->getConnection()->lastInsertId(); |
|
432
|
|
|
} |
|
433
|
|
|
} |
|
434
|
|
|
|
|
435
|
|
|
return false; |
|
436
|
|
|
} |
|
437
|
|
|
|
|
438
|
|
|
/** |
|
439
|
|
|
* @param string $tableName use Database::get_main_table |
|
440
|
|
|
* @param array $attributes Values to updates |
|
441
|
|
|
* Example: $params['name'] = 'Julio'; $params['lastname'] = 'Montoya'; |
|
442
|
|
|
* @param array $whereConditions where conditions i.e array('id = ?' =>'4') |
|
443
|
|
|
* @param bool $showQuery |
|
444
|
|
|
* |
|
445
|
|
|
* @return bool|int |
|
446
|
|
|
*/ |
|
447
|
|
|
public static function update( |
|
448
|
|
|
$tableName, |
|
449
|
|
|
$attributes, |
|
450
|
|
|
$whereConditions = [], |
|
451
|
|
|
$showQuery = false |
|
452
|
|
|
) { |
|
453
|
|
|
if (!empty($tableName) && !empty($attributes)) { |
|
454
|
|
|
$updateSql = ''; |
|
455
|
|
|
$count = 1; |
|
456
|
|
|
|
|
457
|
|
|
foreach ($attributes as $key => $value) { |
|
458
|
|
|
if ($showQuery) { |
|
459
|
|
|
echo $key.': '.$value.PHP_EOL; |
|
460
|
|
|
} |
|
461
|
|
|
$updateSql .= "$key = :$key "; |
|
462
|
|
|
if ($count < count($attributes)) { |
|
463
|
|
|
$updateSql .= ', '; |
|
464
|
|
|
} |
|
465
|
|
|
$count++; |
|
466
|
|
|
} |
|
467
|
|
|
|
|
468
|
|
|
if (!empty($updateSql)) { |
|
469
|
|
|
// Parsing and cleaning the where conditions |
|
470
|
|
|
$whereReturn = self::parse_where_conditions($whereConditions); |
|
471
|
|
|
$sql = "UPDATE $tableName SET $updateSql $whereReturn "; |
|
472
|
|
|
|
|
473
|
|
|
try { |
|
474
|
|
|
$statement = self::getManager()->getConnection()->prepare($sql); |
|
475
|
|
|
$result = $statement->execute($attributes); |
|
476
|
|
|
} catch (Exception $e) { |
|
477
|
|
|
self::handleError($e); |
|
478
|
|
|
} |
|
479
|
|
|
|
|
480
|
|
|
if ($showQuery) { |
|
481
|
|
|
var_dump($sql); |
|
|
|
|
|
|
482
|
|
|
var_dump($attributes); |
|
483
|
|
|
var_dump($whereConditions); |
|
484
|
|
|
} |
|
485
|
|
|
|
|
486
|
|
|
if ($result && $statement) { |
|
|
|
|
|
|
487
|
|
|
return $statement->rowCount(); |
|
488
|
|
|
} |
|
489
|
|
|
} |
|
490
|
|
|
} |
|
491
|
|
|
|
|
492
|
|
|
return false; |
|
493
|
|
|
} |
|
494
|
|
|
|
|
495
|
|
|
/** |
|
496
|
|
|
* Experimental useful database finder. |
|
497
|
|
|
* |
|
498
|
|
|
* @param mixed|array $columns |
|
499
|
|
|
* @param string $table_name |
|
500
|
|
|
* @param array $conditions |
|
501
|
|
|
* @param string $type_result |
|
502
|
|
|
* @param string $option |
|
503
|
|
|
* @param bool $debug |
|
504
|
|
|
* |
|
505
|
|
|
* @return array |
|
506
|
|
|
* |
|
507
|
|
|
* @todo lot of stuff to do here |
|
508
|
|
|
* @todo known issues, it doesn't work when using LIKE conditions |
|
509
|
|
|
* |
|
510
|
|
|
* @example array('where'=> array('course_code LIKE "?%"')) |
|
511
|
|
|
* @example array('where'=> array('type = ? AND category = ?' => array('setting', 'Plugins')) |
|
512
|
|
|
* @example array('where'=> array('name = "Julio" AND lastname = "montoya"')) |
|
513
|
|
|
*/ |
|
514
|
|
|
public static function select( |
|
515
|
|
|
$columns, |
|
516
|
|
|
$table_name, |
|
517
|
|
|
$conditions = [], |
|
518
|
|
|
$type_result = 'all', |
|
519
|
|
|
$option = 'ASSOC', |
|
520
|
|
|
$debug = false |
|
521
|
|
|
) { |
|
522
|
|
|
if ($type_result === 'count') { |
|
523
|
|
|
$conditions['LIMIT'] = null; |
|
524
|
|
|
$conditions['limit'] = null; |
|
525
|
|
|
} |
|
526
|
|
|
$conditions = self::parse_conditions($conditions); |
|
527
|
|
|
|
|
528
|
|
|
//@todo we could do a describe here to check the columns ... |
|
529
|
|
|
if (is_array($columns)) { |
|
530
|
|
|
$clean_columns = implode(',', $columns); |
|
531
|
|
|
} else { |
|
532
|
|
|
if ('*' === $columns) { |
|
533
|
|
|
$clean_columns = '*'; |
|
534
|
|
|
} else { |
|
535
|
|
|
$clean_columns = (string) $columns; |
|
536
|
|
|
} |
|
537
|
|
|
} |
|
538
|
|
|
|
|
539
|
|
|
if ($type_result === 'count') { |
|
540
|
|
|
$clean_columns = ' count(*) count '; |
|
541
|
|
|
} |
|
542
|
|
|
$sql = "SELECT $clean_columns FROM $table_name $conditions"; |
|
543
|
|
|
if ($debug) { |
|
544
|
|
|
var_dump($sql); |
|
|
|
|
|
|
545
|
|
|
} |
|
546
|
|
|
$result = self::query($sql); |
|
547
|
|
|
if ($type_result === 'count') { |
|
548
|
|
|
$row = self::fetch_array($result, $option); |
|
549
|
|
|
if ($row) { |
|
550
|
|
|
return (int) $row['count']; |
|
551
|
|
|
} |
|
552
|
|
|
|
|
553
|
|
|
return 0; |
|
554
|
|
|
} |
|
555
|
|
|
$array = []; |
|
556
|
|
|
|
|
557
|
|
|
if ('all' === $type_result) { |
|
558
|
|
|
while ($row = self::fetch_array($result, $option)) { |
|
559
|
|
|
if (isset($row['id'])) { |
|
560
|
|
|
$array[$row['id']] = $row; |
|
561
|
|
|
} else { |
|
562
|
|
|
$array[] = $row; |
|
563
|
|
|
} |
|
564
|
|
|
} |
|
565
|
|
|
} else { |
|
566
|
|
|
$array = self::fetch_array($result, $option); |
|
567
|
|
|
} |
|
568
|
|
|
|
|
569
|
|
|
return $array; |
|
570
|
|
|
} |
|
571
|
|
|
|
|
572
|
|
|
/** |
|
573
|
|
|
* Parses WHERE/ORDER conditions i.e array('where'=>array('id = ?' =>'4'), 'order'=>'id DESC'). |
|
574
|
|
|
* |
|
575
|
|
|
* @todo known issues, it doesn't work when using |
|
576
|
|
|
* LIKE conditions example: array('where'=>array('course_code LIKE "?%"')) |
|
577
|
|
|
* |
|
578
|
|
|
* @param array $conditions |
|
579
|
|
|
* |
|
580
|
|
|
* @return string Partial SQL string to add to longer query |
|
581
|
|
|
*/ |
|
582
|
|
|
public static function parse_conditions($conditions) |
|
583
|
|
|
{ |
|
584
|
|
|
if (empty($conditions)) { |
|
585
|
|
|
return ''; |
|
586
|
|
|
} |
|
587
|
|
|
$return_value = $where_return = ''; |
|
588
|
|
|
foreach ($conditions as $type_condition => $condition_data) { |
|
589
|
|
|
if (false == $condition_data) { |
|
590
|
|
|
continue; |
|
591
|
|
|
} |
|
592
|
|
|
$type_condition = strtolower($type_condition); |
|
593
|
|
|
switch ($type_condition) { |
|
594
|
|
|
case 'where': |
|
595
|
|
|
foreach ($condition_data as $condition => $value_array) { |
|
596
|
|
|
if (is_array($value_array)) { |
|
597
|
|
|
$clean_values = []; |
|
598
|
|
|
foreach ($value_array as $item) { |
|
599
|
|
|
$item = self::escape_string($item); |
|
600
|
|
|
$clean_values[] = $item; |
|
601
|
|
|
} |
|
602
|
|
|
} else { |
|
603
|
|
|
$value_array = self::escape_string($value_array); |
|
604
|
|
|
$clean_values = [$value_array]; |
|
605
|
|
|
} |
|
606
|
|
|
|
|
607
|
|
|
if (!empty($condition) && '' != $clean_values) { |
|
608
|
|
|
$condition = str_replace('%', "'@percentage@'", $condition); //replace "%" |
|
609
|
|
|
$condition = str_replace("'?'", "%s", $condition); |
|
610
|
|
|
$condition = str_replace("?", "%s", $condition); |
|
611
|
|
|
|
|
612
|
|
|
$condition = str_replace("@%s@", "@-@", $condition); |
|
613
|
|
|
$condition = str_replace("%s", "'%s'", $condition); |
|
614
|
|
|
$condition = str_replace("@-@", "@%s@", $condition); |
|
615
|
|
|
|
|
616
|
|
|
// Treat conditions as string |
|
617
|
|
|
$condition = vsprintf($condition, $clean_values); |
|
618
|
|
|
$condition = str_replace('@percentage@', '%', $condition); //replace "%" |
|
619
|
|
|
$where_return .= $condition; |
|
620
|
|
|
} |
|
621
|
|
|
} |
|
622
|
|
|
|
|
623
|
|
|
if (!empty($where_return)) { |
|
624
|
|
|
$return_value = " WHERE $where_return"; |
|
625
|
|
|
} |
|
626
|
|
|
break; |
|
627
|
|
|
case 'order': |
|
628
|
|
|
$order_array = $condition_data; |
|
629
|
|
|
|
|
630
|
|
|
if (!empty($order_array)) { |
|
631
|
|
|
// 'order' => 'id desc, name desc' |
|
632
|
|
|
$order_array = self::escape_string($order_array); |
|
633
|
|
|
$new_order_array = explode(',', $order_array); |
|
634
|
|
|
$temp_value = []; |
|
635
|
|
|
|
|
636
|
|
|
foreach ($new_order_array as $element) { |
|
637
|
|
|
$element = explode(' ', $element); |
|
638
|
|
|
$element = array_filter($element); |
|
639
|
|
|
$element = array_values($element); |
|
640
|
|
|
|
|
641
|
|
|
if (!empty($element[1])) { |
|
642
|
|
|
$element[1] = strtolower($element[1]); |
|
643
|
|
|
$order = 'DESC'; |
|
644
|
|
|
if (in_array($element[1], ['desc', 'asc'])) { |
|
645
|
|
|
$order = $element[1]; |
|
646
|
|
|
} |
|
647
|
|
|
$temp_value[] = ' `'.$element[0].'` '.$order.' '; |
|
648
|
|
|
} else { |
|
649
|
|
|
//by default DESC |
|
650
|
|
|
$temp_value[] = ' `'.$element[0].'` DESC '; |
|
651
|
|
|
} |
|
652
|
|
|
} |
|
653
|
|
|
if (!empty($temp_value)) { |
|
654
|
|
|
$return_value .= ' ORDER BY '.implode(', ', $temp_value); |
|
655
|
|
|
} |
|
656
|
|
|
} |
|
657
|
|
|
break; |
|
658
|
|
|
case 'limit': |
|
659
|
|
|
$limit_array = explode(',', $condition_data); |
|
660
|
|
|
if (!empty($limit_array)) { |
|
661
|
|
|
if (count($limit_array) > 1) { |
|
662
|
|
|
$return_value .= ' LIMIT '.intval($limit_array[0]).' , '.intval($limit_array[1]); |
|
663
|
|
|
} else { |
|
664
|
|
|
$return_value .= ' LIMIT '.intval($limit_array[0]); |
|
665
|
|
|
} |
|
666
|
|
|
} |
|
667
|
|
|
break; |
|
668
|
|
|
} |
|
669
|
|
|
} |
|
670
|
|
|
|
|
671
|
|
|
return $return_value; |
|
672
|
|
|
} |
|
673
|
|
|
|
|
674
|
|
|
/** |
|
675
|
|
|
* @param array $conditions |
|
676
|
|
|
* |
|
677
|
|
|
* @return string |
|
678
|
|
|
*/ |
|
679
|
|
|
public static function parse_where_conditions($conditions) |
|
680
|
|
|
{ |
|
681
|
|
|
return self::parse_conditions(['where' => $conditions]); |
|
682
|
|
|
} |
|
683
|
|
|
|
|
684
|
|
|
/** |
|
685
|
|
|
* @param string $table_name |
|
686
|
|
|
* @param array $where_conditions |
|
687
|
|
|
* @param bool $show_query |
|
688
|
|
|
* |
|
689
|
|
|
* @return int |
|
690
|
|
|
*/ |
|
691
|
|
|
public static function delete($table_name, $where_conditions, $show_query = false) |
|
692
|
|
|
{ |
|
693
|
|
|
$where_return = self::parse_where_conditions($where_conditions); |
|
694
|
|
|
$sql = "DELETE FROM $table_name $where_return "; |
|
695
|
|
|
if ($show_query) { |
|
696
|
|
|
echo $sql; |
|
697
|
|
|
echo '<br />'; |
|
698
|
|
|
} |
|
699
|
|
|
$result = self::query($sql); |
|
700
|
|
|
$affected_rows = self::affected_rows($result); |
|
701
|
|
|
//@todo should return affected_rows for |
|
702
|
|
|
return $affected_rows; |
|
703
|
|
|
} |
|
704
|
|
|
|
|
705
|
|
|
/** |
|
706
|
|
|
* Get Doctrine configuration. |
|
707
|
|
|
* |
|
708
|
|
|
* @param string $path |
|
709
|
|
|
* |
|
710
|
|
|
* @return Configuration |
|
711
|
|
|
*/ |
|
712
|
|
|
public static function getDoctrineConfig($path) |
|
713
|
|
|
{ |
|
714
|
|
|
$isDevMode = true; // Forces doctrine to use ArrayCache instead of apc/xcache/memcache/redis |
|
715
|
|
|
$isSimpleMode = false; // related to annotations @Entity |
|
716
|
|
|
$cache = null; |
|
717
|
|
|
$path = !empty($path) ? $path : api_get_path(SYMFONY_SYS_PATH); |
|
718
|
|
|
|
|
719
|
|
|
$paths = [ |
|
720
|
|
|
$path.'src/Chamilo/CoreBundle/Entity', |
|
721
|
|
|
$path.'src/Chamilo/CourseBundle/Entity', |
|
722
|
|
|
]; |
|
723
|
|
|
|
|
724
|
|
|
$proxyDir = $path.'var/cache/'; |
|
725
|
|
|
|
|
726
|
|
|
return \Doctrine\ORM\Tools\Setup::createAnnotationMetadataConfiguration( |
|
727
|
|
|
$paths, |
|
728
|
|
|
$isDevMode, |
|
729
|
|
|
$proxyDir, |
|
730
|
|
|
$cache, |
|
731
|
|
|
$isSimpleMode |
|
732
|
|
|
); |
|
733
|
|
|
} |
|
734
|
|
|
|
|
735
|
|
|
/** |
|
736
|
|
|
* @param string $table |
|
737
|
|
|
* |
|
738
|
|
|
* @return bool |
|
739
|
|
|
*/ |
|
740
|
|
|
public static function tableExists($table) |
|
741
|
|
|
{ |
|
742
|
|
|
return self::getManager()->getConnection()->getSchemaManager()->tablesExist($table); |
|
743
|
|
|
} |
|
744
|
|
|
|
|
745
|
|
|
/** |
|
746
|
|
|
* @param string $table |
|
747
|
|
|
* |
|
748
|
|
|
* @return \Doctrine\DBAL\Schema\Column[] |
|
749
|
|
|
*/ |
|
750
|
|
|
public static function listTableColumns($table) |
|
751
|
|
|
{ |
|
752
|
|
|
return self::getManager()->getConnection()->getSchemaManager()->listTableColumns($table); |
|
753
|
|
|
} |
|
754
|
|
|
|
|
755
|
|
|
public static function escapeField($field) |
|
756
|
|
|
{ |
|
757
|
|
|
return self::escape_string(preg_replace("/[^a-zA-Z0-9_]/", '', $field)); |
|
758
|
|
|
} |
|
759
|
|
|
} |
|
760
|
|
|
|
This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.
Unreachable code is most often the result of
return,dieorexitstatements that have been added for debug purposes.In the above example, the last
return falsewill never be executed, because a return statement has already been met in every possible execution path.