[SPIP] v3.2.1-->v3.2.2
[lhc/web/www.git] / www / ecrire / req / sqlite_fonctions.php
1 <?php
2
3 /***************************************************************************\
4 * SPIP, Systeme de publication pour l'internet *
5 * *
6 * Copyright (c) 2001-2019 *
7 * Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James *
8 * *
9 * Ce programme est un logiciel libre distribue sous licence GNU/GPL. *
10 * Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne. *
11 \***************************************************************************/
12
13 /**
14 * Ce fichier déclare des fonctions étendant les fonctions natives de SQLite
15 *
16 * @package SPIP\Core\SQL\SQLite\Fonctions
17 */
18
19 if (!defined('_ECRIRE_INC_VERSION')) {
20 return;
21 }
22
23 /**
24 * Déclarer à SQLite des fonctions spécifiques utilisables dans les requêtes SQL
25 *
26 * SQLite ne supporte nativement que certaines fonctions dans les requêtes SQL.
27 * Cependant, il permet d'étendre très facilement celles-ci en déclarant de
28 * nouvelles fonctions.
29 *
30 * C'est ce qui est fait ici, en ajoutant des fonctions qui existent aussi
31 * dans d'autres moteurs, notamment en MySQL.
32 *
33 * @link http://www.sqlite.org/lang_corefunc.html Liste des fonctions natives
34 * @link http://sqlite.org/changes.html Liste des évolutions
35 *
36 * @param PDO|resource $sqlite Représente la connexion Sqlite
37 */
38 function _sqlite_init_functions(&$sqlite) {
39
40 if (!$sqlite) {
41 return false;
42 }
43
44
45 $fonctions = array(
46 // A
47 'ACOS' => array('acos', 1),
48 'ASIN' => array('asin', 1),
49 'ATAN' => array('atan', 1), // mysql accepte 2 params comme atan2… hum ?
50 'ATAN2' => array('atan2', 2),
51
52 // C
53 'CEIL' => array('_sqlite_func_ceil', 1),
54 'CONCAT' => array('_sqlite_func_concat', -1),
55 'COS' => array('cos', 1),
56
57 // D
58 'DATE_FORMAT' => array('_sqlite_func_strftime', 2),
59 'DAYOFMONTH' => array('_sqlite_func_dayofmonth', 1),
60 'DEGREES' => array('rad2deg', 1),
61
62 // E
63 'EXTRAIRE_MULTI' => array('_sqlite_func_extraire_multi', 2), // specifique a SPIP/sql_multi()
64 'EXP' => array('exp', 1),
65
66 // F
67 'FIND_IN_SET' => array('_sqlite_func_find_in_set', 2),
68 'FLOOR' => array('_sqlite_func_floor', 1),
69
70 // I
71 'IF' => array('_sqlite_func_if', 3),
72 'INSERT' => array('_sqlite_func_insert', 4),
73 'INSTR' => array('_sqlite_func_instr', 2),
74
75 // L
76 'LEAST' => array('_sqlite_func_least', 3),
77 '_LEFT' => array('_sqlite_func_left', 2),
78 # 'LENGTH' => array('strlen', 1), // present v1.0.4
79 # 'LOWER' => array('strtolower', 1), // present v2.4
80 # 'LTRIM' => array('ltrim', 1), // present
81
82 // N
83 'NOW' => array('_sqlite_func_now', 0),
84
85 // M
86 'MD5' => array('md5', 1),
87 'MONTH' => array('_sqlite_func_month', 1),
88
89 // P
90 'PREG_REPLACE' => array('_sqlite_func_preg_replace', 3),
91
92 // R
93 'RADIANS' => array('deg2rad', 1),
94 'RAND' => array('_sqlite_func_rand', 0), // sinon random() v2.4
95 'REGEXP' => array('_sqlite_func_regexp_match', 2), // critere REGEXP supporte a partir de v3.3.2
96 'RIGHT' => array('_sqlite_func_right', 2),
97 # 'RTRIM' => array('rtrim', 1), // present
98
99 // S
100 'SETTYPE' => array('settype', 2), // CAST present en v3.2.3
101 'SIN' => array('sin', 1),
102 'SQRT' => array('sqrt', 1),
103 'SUBSTRING' => array('_sqlite_func_substring' /*, 3*/), // peut etre appelee avec 2 ou 3 arguments, index base 1 et non 0
104
105 // T
106 'TAN' => array('tan', 1),
107 'TIMESTAMPDIFF' => array('_sqlite_timestampdiff' /*, 3*/),
108 'TO_DAYS' => array('_sqlite_func_to_days', 1),
109 # 'TRIM' => array('trim', 1), // present
110
111 // U
112 'UNIX_TIMESTAMP' => array('_sqlite_func_unix_timestamp', 1),
113 # 'UPPER' => array('strtoupper', 1), // present v2.4
114
115 // V
116 'VIDE' => array('_sqlite_func_vide', 0), // du vide pour SELECT 0 as x ... ORDER BY x -> ORDER BY vide()
117
118 // Y
119 'YEAR' => array('_sqlite_func_year', 1)
120 );
121
122
123 foreach ($fonctions as $f => $r) {
124 _sqlite_add_function($sqlite, $f, $r);
125 }
126
127 #spip_log('functions sqlite chargees ','sqlite.'._LOG_DEBUG);
128 }
129
130
131 /**
132 * Déclare une fonction à SQLite
133 *
134 * @note
135 * Permet au besoin de charger des fonctions
136 * ailleurs par _sqlite_init_functions();
137 *
138 * @uses _sqlite_is_version()
139 *
140 * @param PDO|resource $sqlite Représente la connexion Sqlite
141 * @param string $f Nom de la fonction à créer
142 * @param array $r Tableau indiquant :
143 * - le nom de la fonction à appeler,
144 * - le nombre de paramètres attendus de la fonction (-1 = infini, par défaut)
145 *
146 **/
147 function _sqlite_add_function(&$sqlite, &$f, &$r) {
148 if (_sqlite_is_version(3, $sqlite)) {
149 isset($r[1])
150 ? $sqlite->sqliteCreateFunction($f, $r[0], $r[1])
151 : $sqlite->sqliteCreateFunction($f, $r[0]);
152 } else {
153 isset($r[1])
154 ? sqlite_create_function($sqlite, $f, $r[0], $r[1])
155 : sqlite_create_function($sqlite, $f, $r[0]);
156 }
157 }
158
159 //
160 // SQLite : fonctions sqlite -> php
161 // entre autre auteurs : mlebas
162 //
163
164 function _sqlite_func_ceil($a) {
165 return ceil($a);
166 }
167
168 // http://code.spip.net/@_sqlite_func_concat
169 function _sqlite_func_concat() {
170 $args = func_get_args();
171
172 return join('', $args);
173 }
174
175
176 // http://code.spip.net/@_sqlite_func_dayofmonth
177 function _sqlite_func_dayofmonth($d) {
178 return _sqlite_func_date("d", $d);
179 }
180
181
182 // http://code.spip.net/@_sqlite_func_find_in_set
183 function _sqlite_func_find_in_set($num, $set) {
184 $rank = 0;
185 foreach (explode(",", $set) as $v) {
186 if ($v == $num) {
187 return (++$rank);
188 }
189 $rank++;
190 }
191
192 return 0;
193 }
194
195 function _sqlite_func_floor($a) {
196 return floor($a);
197 }
198
199 // http://code.spip.net/@_sqlite_func_if
200 function _sqlite_func_if($bool, $oui, $non) {
201 return ($bool) ? $oui : $non;
202 }
203
204
205 /*
206 * INSERT(chaine, index, longueur, chaine) MySQL
207 * Retourne une chaine de caracteres a partir d'une chaine dans laquelle "sschaine"
208 * a ete inseree a la position "index" en remplacant "longueur" caracteres.
209 */
210 // http://code.spip.net/@_sqlite_func_insert
211 function _sqlite_func_insert($s, $index, $longueur, $chaine) {
212 return
213 substr($s, 0, $index)
214 . $chaine
215 . substr(substr($s, $index), $longueur);
216 }
217
218
219 // http://code.spip.net/@_sqlite_func_instr
220 function _sqlite_func_instr($s, $search) {
221 return strpos($s, $search);
222 }
223
224
225 // http://code.spip.net/@_sqlite_func_least
226 function _sqlite_func_least() {
227 $arg_list = func_get_args();
228 $least = min($arg_list);
229
230 #spip_log("Passage avec LEAST : $least",'sqlite.'._LOG_DEBUG);
231 return $least;
232 }
233
234
235 // http://code.spip.net/@_sqlite_func_left
236 function _sqlite_func_left($s, $lenght) {
237 return substr($s, $lenght);
238 }
239
240
241 // http://code.spip.net/@_sqlite_func_now
242 function _sqlite_func_now() {
243 static $now = null;
244 if (is_null($now)) {
245 $now = date("Y-m-d H:i:s");
246 }
247
248 #spip_log("Passage avec NOW : $now",'sqlite.'._LOG_DEBUG);
249 return $now;
250 }
251
252
253 // http://code.spip.net/@_sqlite_func_month
254 function _sqlite_func_month($d) {
255 return _sqlite_func_date("m", $d);
256 }
257
258
259 // http://code.spip.net/@_sqlite_func_preg_replace
260 function _sqlite_func_preg_replace($quoi, $cherche, $remplace) {
261 $return = preg_replace('%' . $cherche . '%', $remplace, $quoi);
262
263 #spip_log("preg_replace : $quoi, $cherche, $remplace, $return",'sqlite.'._LOG_DEBUG);
264 return $return;
265 }
266
267 /**
268 * Extrait une langue d'un texte <multi>[fr] xxx [en] yyy</multi>
269 *
270 * @param string $quoi le texte contenant ou non un multi
271 * @param string $lang la langue a extraire
272 * @return string, l'extrait trouve.
273 **/
274 function _sqlite_func_extraire_multi($quoi, $lang) {
275 if (!defined('_EXTRAIRE_MULTI')) {
276 include_spip('inc/filtres');
277 }
278 if (!function_exists('approcher_langue')) {
279 include_spip('inc/lang');
280 }
281 if (preg_match_all(_EXTRAIRE_MULTI, $quoi, $regs, PREG_SET_ORDER)) {
282 foreach ($regs as $reg) {
283 // chercher la version de la langue courante
284 $trads = extraire_trads($reg[1]);
285 if ($l = approcher_langue($trads, $lang)) {
286 $trad = $trads[$l];
287 } else {
288 $trad = reset($trads);
289 }
290 $quoi = str_replace($reg[0], $trad, $quoi);
291 }
292 }
293
294 return $quoi;
295 }
296
297
298 // http://code.spip.net/@_sqlite_func_rand
299 function _sqlite_func_rand() {
300 return rand();
301 }
302
303
304 // http://code.spip.net/@_sqlite_func_right
305 function _sqlite_func_right($s, $length) {
306 return substr($s, 0 - $length);
307 }
308
309
310 // http://code.spip.net/@_sqlite_func_regexp_match
311 function _sqlite_func_regexp_match($cherche, $quoi) {
312 // optimiser un cas tres courant avec les requetes en base
313 if (!$quoi and !strlen($quoi)) {
314 return false;
315 }
316 $u = isset($GLOBALS['meta']['pcre_u']) ? $GLOBALS['meta']['pcre_u'] : 'u';
317 $return = preg_match('%' . $cherche . '%imsS' . $u, $quoi);
318
319 #spip_log("regexp_replace : $quoi, $cherche, $remplace, $return",'sqlite.'._LOG_DEBUG);
320 return $return;
321 }
322
323 // http://code.spip.net/@_sqlite_func_strftime
324 function _sqlite_func_strftime($date, $conv) {
325 return strftime($conv, is_int($date) ? $date : strtotime($date));
326 }
327
328 /**
329 * Nombre de jour entre 0000-00-00 et $d
330 *
331 * @link http://dev.mysql.com/doc/refman/5.5/en/date-and-time-functions.html#function_to-days
332 *
333 * @param string $d
334 * @return int
335 */
336 function _sqlite_func_to_days($d) {
337 static $offset = 719528; // nb de jour entre 0000-00-00 et timestamp 0=1970-01-01
338 $result = $offset + (int)ceil(_sqlite_func_unix_timestamp($d) / (24 * 3600));
339
340 #spip_log("Passage avec TO_DAYS : $d, $result",'sqlite.'._LOG_DEBUG);
341 return $result;
342 }
343
344 function _sqlite_func_substring($string, $start, $len = null) {
345 // SQL compte a partir de 1, php a partir de 0
346 $start = ($start > 0) ? $start - 1 : $start;
347 if (is_null($len)) {
348 return substr($string, $start);
349 } else {
350 return substr($string, $start, $len);
351 }
352 }
353
354 /**
355 * Calcul de la difference entre 2 timestamp, exprimes dans l'unite fournie en premier argument
356 *
357 * @link https://dev.mysql.com/doc/refman/5.5/en/date-and-time-functions.html#function_timestampdiff
358 *
359 * @param string $unit
360 * @param string $date1
361 * @param string $date2
362 * @return int
363 */
364 function _sqlite_timestampdiff($unit, $date1, $date2) {
365 // PHP >= 5.3
366 if (function_exists("date_diff")) {
367 $d1 = date_create($date1);
368 $d2 = date_create($date2);
369 $diff = date_diff($d1, $d2);
370 $inv = $diff->invert ? -1 : 1;
371 switch ($unit) {
372 case "YEAR":
373 return $inv * $diff->y;
374 case "QUARTER":
375 return $inv * (4 * $diff->y + intval(floor($diff->m / 3)));
376 case "MONTH":
377 return $inv * (12 * $diff->y + $diff->m);
378 case "WEEK":
379 return $inv * intval(floor($diff->days / 7));
380 case "DAY":
381 #var_dump($inv*$diff->days);
382 return $inv * $diff->days;
383 case "HOUR":
384 return $inv * (24 * $diff->days + $diff->h);
385 case "MINUTE":
386 return $inv * ((24 * $diff->days + $diff->h) * 60 + $diff->i);
387 case "SECOND":
388 return $inv * (((24 * $diff->days + $diff->h) * 60 + $diff->i) * 60 + $diff->s);
389 case "MICROSECOND":
390 return $inv * (((24 * $diff->days + $diff->h) * 60 + $diff->i) * 60 + $diff->s) * 1000000;
391 }
392
393 return 0;
394 } // PHP < 5.3
395 else {
396 $d1 = strtotime($date1);
397 $d2 = strtotime($date2);
398 $diff = $d2 - $d1;
399 $sign = ($diff < 0 ? -1 : 1);
400 $diff = $sign * $diff;
401 switch ($unit) {
402 case "YEAR":
403 $diff = $d2 - $d1;
404
405 return $sign * (date('Y', abs($diff)) - date('Y', 0));
406 case "QUARTER":
407 return $sign * (4 * (date('Y', abs($diff)) - date('Y', 0)) + intval(floor((date('m', $diff) - 1) / 3)));
408 case "MONTH":
409 return $sign * ((date('Y', $diff) - date('Y', 0)) * 12 + date('m', $diff) - 1);
410 case "WEEK":
411 return intval(floor(($d2 - $d1) / 3600 / 7));
412 case "DAY":
413 return intval(floor(($d2 - $d1) / 3600 / 24));
414 case "HOUR":
415 return intval(floor(($d2 - $d1) / 3600));
416 case "MINUTE":
417 return intval(floor(($d2 - $d1) / 60));
418 case "SECOND":
419 return $d2 - $d1;
420 case "MICROSECOND":
421 return $d2 - $d1 * 1000000;
422 }
423 }
424 }
425
426 // http://code.spip.net/@_sqlite_func_unix_timestamp
427 function _sqlite_func_unix_timestamp($d) {
428 static $mem = array();
429 static $n = 0;
430 if (isset($mem[$d])) {
431 return $mem[$d];
432 }
433 if ($n++ > 100) {
434 $mem = array();
435 $n = 0;
436 }
437
438 //2005-12-02 20:53:53
439 #spip_log("Passage avec UNIX_TIMESTAMP : $d",'sqlite.'._LOG_DEBUG);
440 if (!$d) {
441 return $mem[$d] = mktime();
442 }
443
444 // une pile plus grosse n'accelere pas le calcul
445 return $mem[$d] = strtotime($d);
446 }
447
448
449 // http://code.spip.net/@_sqlite_func_year
450 function _sqlite_func_year($d) {
451 return _sqlite_func_date("Y", $d);
452 }
453
454 /**
455 * version optimisee et memoizee de date() utilisee par
456 * _sqlite_func_year, _sqlite_func_month, _sqlite_func_dayofmonth
457 *
458 * @param string $quoi
459 * format : Y, m, ou d
460 * @param int $d
461 * timestamp
462 * @return int
463 */
464 function _sqlite_func_date($quoi, $d) {
465 static $mem = array();
466 static $n = 0;
467 if (isset($mem[$d])) {
468 return $mem[$d][$quoi];
469 }
470 if ($n++ > 100) {
471 $mem = array();
472 $n = 0;
473 }
474
475 $dec = date("Y-m-d", _sqlite_func_unix_timestamp($d));
476 $mem[$d] = array("Y" => substr($dec, 0, 4), "m" => substr($dec, 5, 2), "d" => substr($dec, 8, 2));
477
478 return $mem[$d][$quoi];
479 }
480
481 // http://code.spip.net/@_sqlite_func_vide
482 function _sqlite_func_vide() {
483 return;
484 }