[SPIP] ~2.1.12 -->2.1.25
[velocampus/web/www.git] / www / ecrire / req / sqlite_generique.php
1 <?php
2
3 /***************************************************************************\
4 * SPIP, Systeme de publication pour l'internet *
5 * *
6 * Copyright (c) 2001-2014 *
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 if (!defined('_ECRIRE_INC_VERSION')) return;
14
15 // infos :
16 // il ne faut pas avoir de PDO::CONSTANTE dans ce fichier sinon php4 se tue !
17 // idem, il ne faut pas de $obj->toto()->toto sinon php4 se tue !
18
19 # todo : get/set_caracteres ?
20
21
22 /*
23 *
24 * regroupe le maximum de fonctions qui peuvent cohabiter
25 * D'abord les fonctions d'abstractions de SPIP
26 *
27 */
28 // http://doc.spip.org/@req_sqlite_dist
29 function req_sqlite_dist($addr, $port, $login, $pass, $db='', $prefixe='', $sqlite_version=''){
30 static $last_connect = array();
31
32 // si provient de selectdb
33 // un code pour etre sur que l'on vient de select_db()
34 if (strpos($db, $code = '@selectdb@')!==false) {
35 foreach (array('addr','port','login','pass','prefixe') as $a){
36 $$a = $last_connect[$a];
37 }
38 $db = str_replace($code, '', $db);
39 }
40
41 /*
42 * En sqlite, seule l'adresse du fichier est importante.
43 * Ce sera $db le nom, et le path _DIR_DB
44 */
45 _sqlite_init();
46
47 // un nom de base demande et impossible d'obtenir la base, on s'en va
48 if ($db && !is_file($f = _DIR_DB . $db . '.sqlite') && !is_writable(_DIR_DB))
49 return false;
50
51
52 // charger les modules sqlite au besoin
53 if (!_sqlite_charger_version($sqlite_version)) {
54 spip_log("Impossible de trouver/charger le module SQLite ($sqlite_version)!");
55 return false;
56 }
57
58 // chargement des constantes
59 // il ne faut pas definir les constantes avant d'avoir charge les modules sqlite
60 $define = "spip_sqlite".$sqlite_version."_constantes";
61 $define();
62
63 $ok = false;
64 if (!$db){
65 // si installation -> base temporaire tant qu'on ne connait pas son vrai nom
66 if (defined('_ECRIRE_INSTALL') && _ECRIRE_INSTALL){
67 // creation d'une base temporaire pour le debut d'install
68 $db = "_sqlite".$sqlite_version."_install";
69 $tmp = _DIR_DB . $db . ".sqlite";
70 if ($sqlite_version == 3) {
71 $ok = $link = new PDO("sqlite:$tmp");
72 } else {
73 $ok = $link = sqlite_open($tmp, _SQLITE_CHMOD, $err);
74 }
75 // sinon, on arrete finalement
76 } else {
77 return false;
78 }
79 } else {
80 // Ouvrir (eventuellement creer la base)
81 // si pas de version fourni, on essaie la 3, sinon la 2
82 if ($sqlite_version == 3) {
83 $ok = $link = new PDO("sqlite:$f");
84 } else {
85 $ok = $link = sqlite_open($f, _SQLITE_CHMOD, $err);
86 }
87 }
88
89 if (!$ok){
90 spip_log("Impossible d'ouvrir la base de donnee SQLite ($sqlite_version) : $f ");
91 return false;
92 }
93
94 if ($link) {
95 $last_connect = array (
96 'addr' => $addr,
97 'port' => $port,
98 'login' => $login,
99 'pass' => $pass,
100 'db' => $db,
101 'prefixe' => $prefixe,
102 );
103 }
104
105 return array(
106 'db' => $db,
107 'prefixe' => $prefixe ? $prefixe : $db,
108 'link' => $link,
109 );
110 }
111
112
113
114 // Fonction de requete generale, munie d'une trace a la demande
115 // http://doc.spip.org/@spip_sqlite_query
116 function spip_sqlite_query($query, $serveur='',$requeter=true) {
117 #spip_log("spip_sqlite_query() > $query");
118 _sqlite_init();
119
120 $requete = new sqlite_traiter_requete($query, $serveur);
121 $requete->traduire_requete(); // mysql -> sqlite
122 if (!$requeter) return $requete->query;
123 return $requete->executer_requete();
124 }
125
126
127 /* ordre alphabetique pour les autres */
128
129 // http://doc.spip.org/@spip_sqlite_alter
130 function spip_sqlite_alter($query, $serveur='',$requeter=true){
131
132 $query = spip_sqlite_query("ALTER $query",$serveur,false);
133
134 /*
135 * la il faut faire les transformations
136 * si ALTER TABLE x (DROP|CHANGE) y
137 *
138 * 1) recuperer "ALTER TABLE table "
139 * 2) spliter les sous requetes (,)
140 * 3) faire chaque requete independemment
141 */
142
143 // 1
144 if (preg_match("/\s*(ALTER(\s*IGNORE)?\s*TABLE\s*([^\s]*))\s*(.*)?/is", $query, $regs)){
145 $debut = $regs[1];
146 $table = $regs[3];
147 $suite = $regs[4];
148 } else {
149 spip_log("SQLite : Probleme de ALTER TABLE mal forme dans $query", 'sqlite');
150 return false;
151 }
152
153 // 2
154 // il faudrait une regexp pour eviter de spliter ADD PRIMARY KEY (colA, colB)
155 // tout en cassant "ADD PRIMARY KEY (colA, colB), ADD INDEX (chose)"... en deux
156 // ou revoir l'api de sql_alter en creant un
157 // sql_alter_table($table,array($actions));
158 $todo = explode(',', $suite);
159
160 // on remet les morceaux dechires ensembles... que c'est laid !
161 $todo2 = array(); $i=0;
162 $ouverte = false;
163 while ($do = array_shift($todo)) {
164 $todo2[$i] = isset($todo2[$i]) ? $todo2[$i] . "," . $do : $do;
165 $o=(false!==strpos($do,"("));
166 $f=(false!==strpos($do,")"));
167 if ($o AND !$f) $ouverte=true;
168 elseif ($f) $ouverte=false;
169 if (!$ouverte) $i++;
170 }
171
172 // 3
173 $resultats = array();
174 foreach ($todo2 as $do){
175 $do = trim($do);
176 if (!preg_match('/(DROP PRIMARY KEY|DROP INDEX|DROP COLUMN|DROP'
177 .'|CHANGE COLUMN|CHANGE|MODIFY|RENAME TO|RENAME'
178 .'|ADD PRIMARY KEY|ADD INDEX|ADD COLUMN|ADD'
179 .')\s*([^\s]*)\s*(.*)?/', $do, $matches)){
180 spip_log("SQLite : Probleme de ALTER TABLE, utilisation non reconnue dans : $do \n(requete d'origine : $query)", 'sqlite');
181 return false;
182 }
183
184 $cle = strtoupper($matches[1]);
185 $colonne_origine = $matches[2];
186 $colonne_destination = '';
187
188 $def = $matches[3];
189
190 // eluder une eventuelle clause before|after|first inutilisable
191 $defr = rtrim(preg_replace('/(BEFORE|AFTER|FIRST)(.*)$/is','', $def));
192 // remplacer les definitions venant de mysql
193 $defr = _sqlite_remplacements_definitions_table($defr);
194
195 // reinjecter dans le do
196 $do = str_replace($def,$defr,$do);
197 $def = $defr;
198
199 switch($cle){
200 // suppression d'un index
201 case 'DROP INDEX':
202 $nom_index = $colonne_origine;
203 spip_sqlite_drop_index($nom_index, $table, $serveur);
204 break;
205
206 // suppression d'une pk
207 case 'DROP PRIMARY KEY':
208 if (!_sqlite_modifier_table(
209 $table,
210 $colonne_origine,
211 array('key'=>array('PRIMARY KEY'=>'')),
212 $serveur)){
213 return false;
214 }
215 break;
216 // suppression d'une colonne
217 case 'DROP COLUMN':
218 case 'DROP':
219 if (!_sqlite_modifier_table(
220 $table,
221 array($colonne_origine=>""),
222 '',
223 $serveur)){
224 return false;
225 }
226 break;
227
228 case 'CHANGE COLUMN':
229 case 'CHANGE':
230 // recuperer le nom de la future colonne
231 $def = trim($def);
232 $colonne_destination = substr($def, 0, strpos($def,' '));
233 $def = substr($def, strlen($colonne_destination)+1);
234
235 if (!_sqlite_modifier_table(
236 $table,
237 array($colonne_origine=>$colonne_destination),
238 array('field'=>array($colonne_destination=>$def)),
239 $serveur)){
240 return false;
241 }
242 break;
243
244 case 'MODIFY':
245 if (!_sqlite_modifier_table(
246 $table,
247 $colonne_origine,
248 array('field'=>array($colonne_origine=>$def)),
249 $serveur)){
250 return false;
251 }
252 break;
253
254 // pas geres en sqlite2
255 case 'RENAME':
256 $do = "RENAME TO" . substr($do,6);
257 case 'RENAME TO':
258 if (_sqlite_is_version(3, '', $serveur)){
259 $requete = new sqlite_traiter_requete("$debut $do", $serveur);
260 if (!$requete->executer_requete()){
261 spip_log("SQLite : Erreur ALTER TABLE / RENAME : $query", 'sqlite');
262 return false;
263 }
264 // artillerie lourde pour sqlite2 !
265 } else {
266 $table_dest = trim(substr($do, 9));
267 if (!_sqlite_modifier_table(array($table=>$table_dest), '', '', $serveur)){
268 spip_log("SQLite : Erreur ALTER TABLE / RENAME : $query", 'sqlite');
269 return false;
270 }
271 }
272 break;
273
274 // ajout d'une pk
275 case 'ADD PRIMARY KEY':
276 $pk = trim(substr($do,16));
277 $pk = ($pk[0]=='(') ? substr($pk,1,-1) : $pk;
278 if (!_sqlite_modifier_table(
279 $table,
280 $colonne_origine,
281 array('key'=>array('PRIMARY KEY'=>$pk)),
282 $serveur)){
283 return false;
284 }
285 break;
286 // ajout d'un index
287 case 'ADD INDEX':
288 // peut etre "(colonne)" ou "nom_index (colonnes)"
289 // bug potentiel si qqn met "(colonne, colonne)"
290 //
291 // nom_index (colonnes)
292 if ($def) {
293 $colonnes = substr($def,1,-1);
294 $nom_index = $colonne_origine;
295 }
296 else {
297 // (colonne)
298 if ($colonne_origine[0] == "(") {
299 $colonnes = substr($colonne_origine,1,-1);
300 if (false!==strpos(",",$colonnes)) {
301 spip_log("SQLite : Erreur, impossible de creer un index sur plusieurs colonnes"
302 ." sans qu'il ait de nom ($table, ($colonnes))", 'sqlite');
303 break;
304 } else {
305 $nom_index = $colonnes;
306 }
307 }
308 // nom_index
309 else {
310 $nom_index = $colonnes = $colonne_origine ;
311 }
312 }
313 spip_sqlite_create_index($nom_index, $table, $colonnes, $serveur);
314 break;
315
316 // pas geres en sqlite2
317 case 'ADD COLUMN':
318 $do = "ADD".substr($do, 10);
319 case 'ADD':
320 default:
321 if (_sqlite_is_version(3, '', $serveur)){
322 $requete = new sqlite_traiter_requete("$debut $do", $serveur);
323 if (!$requete->executer_requete()){
324 spip_log("SQLite : Erreur ALTER TABLE / ADD : $query", 'sqlite');
325 return false;
326 }
327 break;
328 // artillerie lourde pour sqlite2 !
329 } else {
330 $def = trim(substr($do, 3));
331 $colonne_ajoutee = substr($def, 0, strpos($def,' '));
332 $def = substr($def, strlen($colonne_ajoutee)+1);
333 if (!_sqlite_modifier_table($table, array($colonne_ajoutee), array('field'=>array($colonne_ajoutee=>$def)), $serveur)){
334 spip_log("SQLite : Erreur ALTER TABLE / ADD : $query", 'sqlite');
335 return false;
336 }
337 }
338 break;
339 }
340 // tout est bon, ouf !
341 spip_log("SQLite ($serveur) : Changements OK : $debut $do");
342 }
343
344 spip_log("SQLite ($serveur) : fin ALTER TABLE OK !");
345 return true;
346 }
347
348
349 // Fonction de creation d'une table SQL nommee $nom
350 // http://doc.spip.org/@spip_sqlite_create
351 function spip_sqlite_create($nom, $champs, $cles, $autoinc=false, $temporary=false, $serveur='',$requeter=true) {
352 $query = _sqlite_requete_create($nom, $champs, $cles, $autoinc, $temporary, $ifnotexists=true, $serveur, $requeter);
353 if (!$query) return false;
354 $res = spip_sqlite_query($query, $serveur, $requeter);
355
356 // SQLite ne cree pas les KEY sur les requetes CREATE TABLE
357 // il faut donc les faire creer ensuite
358 if (!$requeter) return $res;
359
360 $ok = $res ? true : false;
361 if ($ok) {
362 foreach($cles as $k=>$v) {
363 if (strpos($k, "KEY ") === 0) {
364 $index = preg_replace("/KEY +/", '',$k);
365 $ok &= $res = spip_sqlite_create_index($index, $nom, $v, $serveur);
366 }
367 }
368 }
369 return $ok ? true : false;
370 }
371
372 /**
373 * Fonction pour creer une base de donnees SQLite
374 *
375 * @param string $nom le nom de la base (sans l'extension de fichier)
376 * @param string $serveur le nom de la connexion
377 * @param string $option options
378 *
379 * @return bool true si la base est creee.
380 **/
381 function spip_sqlite_create_base($nom, $serveur='', $option=true) {
382 $f = _DIR_DB . $nom . '.sqlite';
383 if (_sqlite_is_version(2, '', $serveur)) {
384 $ok = sqlite_open($f, _SQLITE_CHMOD, $err);
385 } else {
386 $ok = new PDO("sqlite:$f");
387 }
388 if ($ok) {
389 unset($ok);
390 return true;
391 }
392 unset($ok);
393 return false;
394 }
395
396
397 // Fonction de creation d'une vue SQL nommee $nom
398 // http://doc.spip.org/@spip_sqlite_create_view
399 function spip_sqlite_create_view($nom, $query_select, $serveur='',$requeter=true) {
400 if (!$query_select) return false;
401 // vue deja presente
402 if (sql_showtable($nom, false, $serveur)) {
403 spip_log("Echec creation d'une vue sql ($nom) car celle-ci existe deja (serveur:$serveur)");
404 return false;
405 }
406
407 $query = "CREATE VIEW $nom AS ". $query_select;
408 return spip_sqlite_query($query, $serveur, $requeter);
409 }
410
411 /**
412 * Fonction de creation d'un INDEX
413 *
414 * @param string $nom : nom de l'index
415 * @param string $table : table sql de l'index
416 * @param string/array $champs : liste de champs sur lesquels s'applique l'index
417 * @param string $serveur : nom de la connexion sql utilisee
418 * @param bool $requeter : true pour executer la requete ou false pour retourner le texte de la requete
419 *
420 * @return bool ou requete
421 */
422 function spip_sqlite_create_index($nom, $table, $champs, $serveur='', $requeter=true) {
423 if (!($nom OR $table OR $champs)) {
424 spip_log("Champ manquant pour creer un index sqlite ($nom, $table, (".join(',',$champs)."))");
425 return false;
426 }
427
428 // SQLite ne differentie pas noms des index en fonction des tables
429 // il faut donc creer des noms uniques d'index pour une base sqlite
430 $nom = $table.'_'.$nom;
431 // enlever d'eventuelles parentheses deja presentes sur champs
432 if (!is_array($champs)){
433 if ($champs[0]=="(") $champs = substr($champs,1,-1);
434 $champs = array($champs);
435 }
436 $query = "CREATE INDEX $nom ON $table (" . join(',',$champs) . ")";
437 $res = spip_sqlite_query($query, $serveur, $requeter);
438 if (!$requeter) return $res;
439 if ($res)
440 return true;
441 else
442 return false;
443 }
444
445 // en PDO/sqlite3, il faut calculer le count par une requete count(*)
446 // pour les resultats de SELECT
447 // cela est fait sans spip_sqlite_query()
448 // http://doc.spip.org/@spip_sqlite_count
449 function spip_sqlite_count($r, $serveur='',$requeter=true) {
450 if (!$r) return 0;
451
452 if (_sqlite_is_version(3, '', $serveur)){
453 // select ou autre (insert, update,...) ?
454 if (isset($r->spipSqliteRowCount)) {
455 // Ce compte est faux s'il y a des limit dans la requete :(
456 // il retourne le nombre d'enregistrements sans le limit
457 return $r->spipSqliteRowCount;
458 } else {
459 return $r->rowCount();
460 }
461 } else {
462 return sqlite_num_rows($r);
463 }
464 }
465
466
467 // http://doc.spip.org/@spip_sqlite_countsel
468 function spip_sqlite_countsel($from = array(), $where = array(), $groupby = '', $having = array(), $serveur='',$requeter=true) {
469 $c = !$groupby ? '*' : ('DISTINCT ' . (is_string($groupby) ? $groupby : join(',', $groupby)));
470 $r = spip_sqlite_select("COUNT($c)", $from, $where,'', '', '',$having, $serveur, $requeter);
471 if ((is_resource($r) or is_object($r)) && $requeter) { // ressource : sqlite2, object : sqlite3
472 if (_sqlite_is_version(3,'',$serveur)){
473 list($n) = spip_sqlite_fetch($r, SPIP_SQLITE3_NUM, $serveur);
474 } else {
475 list($n) = spip_sqlite_fetch($r, SPIP_SQLITE2_NUM, $serveur);
476 }
477 spip_sqlite_free($r,$serveur);
478 }
479 return $n;
480 }
481
482
483
484 // http://doc.spip.org/@spip_sqlite_delete
485 function spip_sqlite_delete($table, $where='', $serveur='',$requeter=true) {
486 $res = spip_sqlite_query(
487 _sqlite_calculer_expression('DELETE FROM', $table, ',')
488 . _sqlite_calculer_expression('WHERE', $where),
489 $serveur, $requeter);
490
491 // renvoyer la requete inerte si demandee
492 if (!$requeter) return $res;
493
494 if ($res){
495 $link = _sqlite_link($serveur);
496 if (_sqlite_is_version(3, $link)) {
497 return $res->rowCount();
498 } else {
499 return sqlite_changes($link);
500 }
501 }
502 else
503 return false;
504 }
505
506
507 // http://doc.spip.org/@spip_sqlite_drop_table
508 function spip_sqlite_drop_table($table, $exist='', $serveur='',$requeter=true) {
509 if ($exist) $exist =" IF EXISTS";
510
511 /* simuler le IF EXISTS - version 2 */
512 if ($exist && _sqlite_is_version(2, '', $serveur)){
513 $a = spip_sqlite_showtable($table, $serveur);
514 if (!$a) return true;
515 $exist = '';
516 }
517 if (spip_sqlite_query("DROP TABLE$exist $table", $serveur, $requeter))
518 return true;
519 else
520 return false;
521 }
522
523 // supprime une vue
524 // http://doc.spip.org/@spip_sqlite_drop_view
525 function spip_sqlite_drop_view($view, $exist='', $serveur='',$requeter=true) {
526 if ($exist) $exist =" IF EXISTS";
527
528 /* simuler le IF EXISTS - version 2 */
529 if ($exist && _sqlite_is_version(2, '', $serveur)){
530 $a = spip_sqlite_showtable($view, $serveur);
531 if (!$a) return true;
532 $exist = '';
533 }
534
535 return spip_sqlite_query("DROP VIEW$exist $view", $serveur, $requeter);
536 }
537
538 /**
539 * Fonction de suppression d'un INDEX
540 *
541 * @param string $nom : nom de l'index
542 * @param string $table : table sql de l'index
543 * @param string $serveur : nom de la connexion sql utilisee
544 * @param bool $requeter : true pour executer la requete ou false pour retourner le texte de la requete
545 *
546 * @return bool ou requete
547 */
548 function spip_sqlite_drop_index($nom, $table, $serveur='', $requeter=true) {
549 if (!($nom OR $table)) {
550 spip_log("Champ manquant pour supprimer un index sqlite ($nom, $table)");
551 return false;
552 }
553
554 // SQLite ne differentie pas noms des index en fonction des tables
555 // il faut donc creer des noms uniques d'index pour une base sqlite
556 $index = $table.'_'.$nom;
557 $exist =" IF EXISTS";
558
559 /* simuler le IF EXISTS - version 2 */
560 if (_sqlite_is_version(2, '', $serveur)){
561 $a = spip_sqlite_showtable($table, $serveur);
562 if (!isset($a['key']['KEY '.$nom])) return true;
563 $exist = '';
564 }
565
566 $query = "DROP INDEX$exist $index";
567 return spip_sqlite_query($query, $serveur, $requeter);
568 }
569
570 /**
571 * Retourne la derniere erreur generee
572 *
573 * @param $serveur nom de la connexion
574 * @return string erreur eventuelle
575 **/
576 // http://doc.spip.org/@spip_sqlite_error
577 function spip_sqlite_error($query='', $serveur='') {
578 $link = _sqlite_link($serveur);
579
580 if (_sqlite_is_version(3, $link)) {
581 $errs = $link->errorInfo();
582 $s = '';
583 foreach($errs as $n=>$e){
584 $s .= "\n$n : $e";
585 }
586 } elseif ($link) {
587 $s = sqlite_error_string(sqlite_last_error($link));
588 } else {
589 $s = ": aucune ressource sqlite (link)";
590 }
591 if ($s) spip_log("$s - $query", 'sqlite');
592 return $s;
593 }
594
595 /**
596 * Retourne le numero de la derniere erreur SQL
597 * (sauf que SQLite semble ne connaitre que 0 ou 1)
598 *
599 * @param $serveur nom de la connexion
600 * @return int 0 pas d'erreur / 1 une erreur
601 **/
602 // http://doc.spip.org/@spip_sqlite_errno
603 function spip_sqlite_errno($serveur='') {
604 $link = _sqlite_link($serveur);
605
606 if (_sqlite_is_version(3, $link)){
607 $t = $link->errorInfo();
608 $s = $t[1];
609 } elseif ($link) {
610 $s = sqlite_last_error($link);
611 } else {
612 $s = ": aucune ressource sqlite (link)";
613 }
614
615 if ($s) spip_log("Erreur sqlite $s");
616
617 return $s ? 1 : 0;
618 }
619
620
621 // http://doc.spip.org/@spip_sqlite_explain
622 function spip_sqlite_explain($query, $serveur='',$requeter=true){
623 if (strpos(ltrim($query), 'SELECT') !== 0) return array();
624
625 $requete = new sqlite_traiter_requete("$query", $serveur);
626 $requete->traduire_requete(); // mysql -> sqlite
627 $requete->query = 'EXPLAIN ' . $requete->query;
628 if (!$requeter) return $requete;
629 // on ne trace pas ces requetes, sinon on obtient un tracage sans fin...
630 $requete->tracer = false;
631 $r = $requete->executer_requete();
632
633 return $r ? spip_sqlite_fetch($r, null, $serveur) : false; // hum ? etrange ca... a verifier
634 }
635
636
637 // http://doc.spip.org/@spip_sqlite_fetch
638 function spip_sqlite_fetch($r, $t='', $serveur='',$requeter=true) {
639
640 $link = _sqlite_link($serveur);
641 if (!$t) {
642 if (_sqlite_is_version(3, $link)) {
643 $t = SPIP_SQLITE3_ASSOC;
644 } else {
645 $t = SPIP_SQLITE2_ASSOC;
646 }
647 }
648
649
650 if (_sqlite_is_version(3, $link)){
651 if ($r) $retour = $r->fetch($t);
652 } elseif ($r) {
653 $retour = sqlite_fetch_array($r, $t);
654 }
655
656 // les version 2 et 3 parfois renvoie des 'table.titre' au lieu de 'titre' tout court ! pff !
657 // suppression de 'table.' pour toutes les cles (c'est un peu violent !)
658 if ($retour){
659 $new = array();
660 foreach ($retour as $cle=>$val){
661 if (($pos = strpos($cle, '.'))!==false){
662 $cle = substr($cle,++$pos);
663 }
664 $new[$cle] = $val;
665 }
666 $retour = &$new;
667 }
668
669 return $retour;
670 }
671
672
673 function spip_sqlite_seek($r, $row_number, $serveur='',$requeter=true) {
674 if ($r){
675 $link = _sqlite_link($serveur);
676 if (_sqlite_is_version(3, $link)){
677 // encore un truc de bien fichu : PDO ne PEUT PAS faire de seek ou de rewind...
678 // je me demande si pour sqlite 3 il ne faudrait pas mieux utiliser
679 // les nouvelles fonctions sqlite3_xx (mais encore moins presentes...)
680 return false;
681 }
682 else {
683 return sqlite_seek($r, $row_number);
684 }
685 }
686 }
687
688
689 // http://doc.spip.org/@spip_sqlite_free
690 function spip_sqlite_free(&$r, $serveur='',$requeter=true) {
691 unset($r);
692 return true;
693 //return sqlite_free_result($r);
694 }
695
696
697 // http://doc.spip.org/@spip_sqlite_get_charset
698 function spip_sqlite_get_charset($charset=array(), $serveur='',$requeter=true){
699 //$c = !$charset ? '' : (" LIKE "._q($charset['charset']));
700 //return spip_sqlite_fetch(sqlite_query(_sqlite_link($serveur), "SHOW CHARACTER SET$c"), NULL, $serveur);
701 }
702
703
704 // http://doc.spip.org/@spip_sqlite_hex
705 function spip_sqlite_hex($v){
706 return hexdec($v);
707 }
708
709
710 // http://doc.spip.org/@spip_sqlite_in
711 function spip_sqlite_in($val, $valeurs, $not='', $serveur='',$requeter=true) {
712 $n = $i = 0;
713 $in_sql ="";
714 while ($n = strpos($valeurs, ',', $n+1)) {
715 if ((++$i) >= 255) {
716 $in_sql .= "($val $not IN (" .
717 substr($valeurs, 0, $n) .
718 "))\n" .
719 ($not ? "AND\t" : "OR\t");
720 $valeurs = substr($valeurs, $n+1);
721 $i = $n = 0;
722 }
723 }
724 $in_sql .= "($val $not IN ($valeurs))";
725
726 return "($in_sql)";
727 }
728
729
730 // http://doc.spip.org/@spip_sqlite_insert
731 function spip_sqlite_insert($table, $champs, $valeurs, $desc='', $serveur='',$requeter=true) {
732
733 $connexion = $GLOBALS['connexions'][$serveur ? $serveur : 0];
734 $prefixe = $connexion['prefixe'];
735 $sqlite = $connexion['link'];
736 $db = $connexion['db'];
737
738 if ($prefixe) $table = preg_replace('/^spip/', $prefixe, $table);
739
740
741 if (isset($_GET['var_profile'])) {
742 include_spip('public/tracer');
743 $t = trace_query_start();
744 } else $t = 0 ;
745
746 $query="INSERT INTO $table ".($champs?"$champs VALUES $valeurs":"DEFAULT VALUES");
747
748
749 if ($r = spip_sqlite_query($query, $serveur, $requeter)) {
750 if (!$requeter) return $r;
751
752 if (_sqlite_is_version(3, $sqlite)) $nb = $sqlite->lastInsertId();
753 else $nb = sqlite_last_insert_rowid($sqlite);
754 } else $nb = 0;
755
756 $err = spip_sqlite_error($query, $serveur);
757 return $t ? trace_query_end($query, $t, $nb, $err, $serveur) : $nb;
758
759 }
760
761
762 // http://doc.spip.org/@spip_sqlite_insertq
763 function spip_sqlite_insertq($table, $couples=array(), $desc=array(), $serveur='',$requeter=true) {
764 if (!$desc) $desc = description_table($table);
765 if (!$desc) die("$table insertion sans description");
766 $fields = isset($desc['field'])?$desc['field']:array();
767
768 foreach ($couples as $champ => $val) {
769 $couples[$champ]= _sqlite_calculer_cite($val, $fields[$champ]);
770 }
771
772 // recherche de champs 'timestamp' pour mise a jour auto de ceux-ci
773 $couples = _sqlite_ajouter_champs_timestamp($table, $couples, $desc, $serveur);
774
775 // si aucun champ donne pour l'insertion, on en cherche un avec un DEFAULT
776 // sinon sqlite3 ne veut pas inserer
777 $cles = $valeurs = "";
778 if (count($couples)) {
779 $cles = "(".join(',',array_keys($couples)).")";
780 $valeurs = "(".join(',', $couples).")";
781 }
782
783 return spip_sqlite_insert($table, $cles , $valeurs , $desc, $serveur, $requeter);
784 }
785
786
787
788 // http://doc.spip.org/@spip_sqlite_insertq_multi
789 function spip_sqlite_insertq_multi($table, $tab_couples=array(), $desc=array(), $serveur='',$requeter=true) {
790 foreach ($tab_couples as $couples) {
791 $retour = spip_sqlite_insertq($table, $couples, $desc, $serveur, $requeter);
792 }
793 // renvoie le dernier id d'autoincrement ajoute
794 return $retour;
795 }
796
797
798
799 // http://doc.spip.org/@spip_sqlite_listdbs
800 function spip_sqlite_listdbs($serveur='',$requeter=true) {
801 _sqlite_init();
802
803 if (!is_dir($d = substr(_DIR_DB,0,-1))){
804 return array();
805 }
806
807 include_spip('inc/flock');
808 $bases = preg_files($d, $pattern = '(.*)\.sqlite$');
809 $bds = array();
810
811 foreach($bases as $b){
812 // pas de bases commencant pas sqlite
813 // (on s'en sert pour l'installation pour simuler la presence d'un serveur)
814 // les bases sont de la forme _sqliteX_tmp_spip_install.sqlite
815 if (strpos($b, '_sqlite')) continue;
816 $bds[] = preg_replace(";.*/$pattern;iS",'$1', $b);
817 }
818
819 return $bds;
820 }
821
822
823 // http://doc.spip.org/@spip_sqlite_multi
824 function spip_sqlite_multi ($objet, $lang) {
825 $r = "PREG_REPLACE("
826 . $objet
827 . ",'<multi>.*[\[]"
828 . $lang
829 . "[\]]([^\[]*).*</multi>', '$1') AS multi";
830 return $r;
831 }
832
833
834 /**
835 * Optimise une table SQL
836 * Note: Sqlite optimise TOUTE un fichier sinon rien.
837 * On evite donc 2 traitements sur la meme base dans un hit.
838 *
839 * @param $table nom de la table a optimiser
840 * @param $serveur nom de la connexion
841 * @param $requeter effectuer la requete ? sinon retourner son code
842 * @return bool|string true / false / requete
843 **/
844 // http://doc.spip.org/@spip_sqlite_optimize
845 function spip_sqlite_optimize($table, $serveur='', $requeter=true) {
846 static $do = false;
847 if ($requeter and $do) {return true;}
848 if ($requeter) { $do = true; }
849 return spip_sqlite_query("VACUUM", $serveur, $requeter);
850 }
851
852
853 // avoir le meme comportement que _q()
854 function spip_sqlite_quote($v, $type=''){
855 if (is_array($v)) return join(",", array_map('spip_sqlite_quote', $v));
856 if (is_int($v)) return strval($v);
857 if (strncmp($v,'0x',2)==0 AND ctype_xdigit(substr($v,2))) return hexdec(substr($v,2));
858 if ($type === 'int' AND !$v) return '0';
859
860 if (function_exists('sqlite_escape_string')) {
861 return "'" . sqlite_escape_string($v) . "'";
862 }
863
864 // trouver un link sqlite3 pour faire l'echappement
865 foreach ($GLOBALS['connexions'] as $s) {
866 if (_sqlite_is_version(3, $l = $s['link'])){
867 return $l->quote($v);
868 }
869 }
870 }
871
872
873 /**
874 * Tester si une date est proche de la valeur d'un champ
875 *
876 * @param string $champ le nom du champ a tester
877 * @param int $interval valeur de l'interval : -1, 4, ...
878 * @param string $unite utite utilisee (DAY, MONTH, YEAR, ...)
879 * @return string expression SQL
880 **/
881 function spip_sqlite_date_proche($champ, $interval, $unite)
882 {
883 $op = $interval > 0 ? '>' : '<';
884 return "($champ $op datetime('" . date("Y-m-d H:i:s") . "', '$interval $unite'))";
885 }
886
887
888 // http://doc.spip.org/@spip_sqlite_replace
889 function spip_sqlite_replace($table, $couples, $desc=array(), $serveur='',$requeter=true) {
890 if (!$desc) $desc = description_table($table);
891 if (!$desc) die("$table insertion sans description");
892 $fields = isset($desc['field'])?$desc['field']:array();
893
894 foreach ($couples as $champ => $val) {
895 $couples[$champ]= _sqlite_calculer_cite($val, $fields[$champ]);
896 }
897
898 // recherche de champs 'timestamp' pour mise a jour auto de ceux-ci
899 $couples = _sqlite_ajouter_champs_timestamp($table, $couples, $desc, $serveur);
900
901 return spip_sqlite_query("REPLACE INTO $table (" . join(',',array_keys($couples)) . ') VALUES (' .join(',',$couples) . ')', $serveur);
902 }
903
904
905
906 // http://doc.spip.org/@spip_sqlite_replace_multi
907 function spip_sqlite_replace_multi($table, $tab_couples, $desc=array(), $serveur='',$requeter=true) {
908
909 // boucler pour trainter chaque requete independemment
910 foreach ($tab_couples as $couples){
911 $retour = spip_sqlite_replace($table, $couples, $desc, $serveur,$requeter);
912 }
913 // renvoie le dernier id
914 return $retour;
915 }
916
917
918 // http://doc.spip.org/@spip_sqlite_select
919 function spip_sqlite_select($select, $from, $where='', $groupby='', $orderby='', $limit='', $having='', $serveur='',$requeter=true) {
920
921 // version() n'est pas connu de sqlite
922 $select = str_replace('version()', 'sqlite_version()',$select);
923
924 // recomposer from
925 $from = (!is_array($from) ? $from : _sqlite_calculer_select_as($from));
926
927 $query =
928 _sqlite_calculer_expression('SELECT', $select, ', ')
929 . _sqlite_calculer_expression('FROM', $from, ', ')
930 . _sqlite_calculer_expression('WHERE', $where)
931 . _sqlite_calculer_expression('GROUP BY', $groupby, ',')
932 . _sqlite_calculer_expression('HAVING', $having)
933 . ($orderby ? ("\nORDER BY " . _sqlite_calculer_order($orderby)) :'')
934 . ($limit ? "\nLIMIT $limit" : '');
935
936 return spip_sqlite_query($query, $serveur, $requeter);
937 }
938
939
940 // http://doc.spip.org/@spip_sqlite_selectdb
941 function spip_sqlite_selectdb($db, $serveur='',$requeter=true) {
942 _sqlite_init();
943
944 // interdire la creation d'une nouvelle base,
945 // sauf si on est dans l'installation
946 if (!is_file($f = _DIR_DB . $db . '.sqlite')
947 && (!defined('_ECRIRE_INSTALL') || !_ECRIRE_INSTALL))
948 return false;
949
950 // se connecter a la base indiquee
951 // avec les identifiants connus
952 $index = $serveur ? $serveur : 0;
953
954 if ($link = spip_connect_db('', '', '', '', '@selectdb@' . $db , $serveur, '', '')){
955 if (($db==$link['db']) && $GLOBALS['connexions'][$index] = $link)
956 return $db;
957 } else {
958 spip_log("Impossible de selectionner la base $db", 'sqlite');
959 return false;
960 }
961
962 }
963
964
965 // http://doc.spip.org/@spip_sqlite_set_charset
966 function spip_sqlite_set_charset($charset, $serveur='',$requeter=true){
967 #spip_log("changement de charset sql : "."SET NAMES "._q($charset));
968 # return spip_sqlite_query("SET NAMES ". spip_sqlite_quote($charset), $serveur); //<-- Passe pas !
969 }
970
971
972 // http://doc.spip.org/@spip_sqlite_showbase
973 function spip_sqlite_showbase($match, $serveur='',$requeter=true){
974 // type est le type d'entrée : table / index / view
975 // on ne retourne que les tables (?) et non les vues...
976 # ESCAPE non supporte par les versions sqlite <3
977 # return spip_sqlite_query("SELECT name FROM sqlite_master WHERE type='table' AND tbl_name LIKE "._q($match)." ESCAPE '\'", $serveur, $requeter);
978 $match = preg_quote($match);
979 $match = str_replace("\\\_","[[TIRETBAS]]",$match);
980 $match = str_replace("\\\%","[[POURCENT]]",$match);
981 $match = str_replace("_",".",$match);
982 $match = str_replace("%",".*",$match);
983 $match = str_replace("[[TIRETBAS]]","_",$match);
984 $match = str_replace("[[POURCENT]]","%",$match);
985 $match = "^$match$";
986 return spip_sqlite_query("SELECT name FROM sqlite_master WHERE type='table' AND tbl_name REGEXP "._q($match), $serveur, $requeter);
987 }
988
989
990 // http://doc.spip.org/@spip_sqlite_showtable
991 function spip_sqlite_showtable($nom_table, $serveur='',$requeter=true){
992
993 $query =
994 'SELECT sql, type FROM'
995 . ' (SELECT * FROM sqlite_master UNION ALL'
996 . ' SELECT * FROM sqlite_temp_master)'
997 . " WHERE tbl_name LIKE '$nom_table'"
998 . " AND type!='meta' AND sql NOT NULL AND name NOT LIKE 'sqlite_%'"
999 . ' ORDER BY substr(type,2,1), name';
1000
1001 $a = spip_sqlite_query($query, $serveur, $requeter);
1002 if (!$a) return "";
1003 if (!$requeter) return $a;
1004 if (!($a = spip_sqlite_fetch($a, null, $serveur))) return "";
1005 $vue = ($a['type'] == 'view'); // table | vue
1006
1007 // c'est une table
1008 // il faut parser le create
1009 if (!$vue) {
1010 if (!preg_match("/^[^(),]*\((([^()]*(\([^()]*\))?[^()]*)*)\)[^()]*$/", array_shift($a), $r))
1011 return "";
1012 else {
1013 $dec = $r[1];
1014 if (preg_match("/^(.*?),([^,]*KEY.*)$/s", $dec, $r)) {
1015 $namedkeys = $r[2];
1016 $dec = $r[1];
1017 }
1018 else
1019 $namedkeys = "";
1020
1021 $fields = array();
1022 foreach (explode(",",$dec) as $v) {
1023 preg_match("/^\s*([^\s]+)\s+(.*)/",$v,$r);
1024 // trim car 'Sqlite Manager' (plugin Firefox) utilise des guillemets
1025 // lorsqu'on modifie une table avec cet outil.
1026 // possible que d'autres fassent de meme.
1027 $fields[ trim(strtolower($r[1]),'"') ] = $r[2];
1028 }
1029 // key inclues dans la requete
1030 $keys = array();
1031 foreach(preg_split('/\)\s*,?/',$namedkeys) as $v) {
1032 if (preg_match("/^\s*([^(]*)\((.*)$/",$v,$r)) {
1033 $k = str_replace("`", '', trim($r[1]));
1034 $t = trim(strtolower(str_replace("`", '', $r[2])), '"');
1035 if ($k && !isset($keys[$k])) $keys[$k] = $t; else $keys[] = $t;
1036 }
1037 }
1038 // sinon ajouter les key index
1039 $query =
1040 'SELECT name,sql FROM'
1041 . ' (SELECT * FROM sqlite_master UNION ALL'
1042 . ' SELECT * FROM sqlite_temp_master)'
1043 . " WHERE tbl_name LIKE '$nom_table'"
1044 . " AND type='index' AND name NOT LIKE 'sqlite_%'"
1045 . 'ORDER BY substr(type,2,1), name';
1046 $a = spip_sqlite_query($query, $serveur, $requeter);
1047 while ($r = spip_sqlite_fetch($a, null, $serveur)) {
1048 $key = str_replace($nom_table.'_','',$r['name']); // enlever le nom de la table ajoute a l'index
1049 $colonnes = preg_replace(',.*\((.*)\).*,','$1',$r['sql']);
1050 $keys['KEY '.$key] = $colonnes;
1051 }
1052 }
1053 // c'est une vue, on liste les champs disponibles simplement
1054 } else {
1055 if ($res = sql_fetsel('*',$nom_table,'','','','1','',$serveur)){ // limit 1
1056 $fields = array();
1057 foreach($res as $c=>$v) $fields[$c]='';
1058 $keys = array();
1059 } else {
1060 return "";
1061 }
1062 }
1063 return array('field' => $fields, 'key' => $keys);
1064
1065 }
1066
1067
1068 // http://doc.spip.org/@spip_sqlite_update
1069 function spip_sqlite_update($table, $champs, $where='', $desc='', $serveur='',$requeter=true) {
1070 // recherche de champs 'timestamp' pour mise a jour auto de ceux-ci
1071 $champs = _sqlite_ajouter_champs_timestamp($table, $champs, $desc, $serveur);
1072
1073 $set = array();
1074 foreach ($champs as $champ => $val)
1075 $set[] = $champ . "=$val";
1076 if (!empty($set))
1077 return spip_sqlite_query(
1078 _sqlite_calculer_expression('UPDATE', $table, ',')
1079 . _sqlite_calculer_expression('SET', $set, ',')
1080 . _sqlite_calculer_expression('WHERE', $where),
1081 $serveur, $requeter);
1082 }
1083
1084
1085 // http://doc.spip.org/@spip_sqlite_updateq
1086 function spip_sqlite_updateq($table, $champs, $where='', $desc=array(), $serveur='',$requeter=true) {
1087
1088 if (!$champs) return;
1089 if (!$desc) $desc = description_table($table);
1090 if (!$desc) die("$table insertion sans description");
1091 $fields = $desc['field'];
1092
1093 // recherche de champs 'timestamp' pour mise a jour auto de ceux-ci
1094 $champs = _sqlite_ajouter_champs_timestamp($table, $champs, $desc, $serveur);
1095
1096 $set = array();
1097 foreach ($champs as $champ => $val) {
1098 $set[] = $champ . '=' . _sqlite_calculer_cite($val, $fields[$champ]);
1099 }
1100 return spip_sqlite_query(
1101 _sqlite_calculer_expression('UPDATE', $table, ',')
1102 . _sqlite_calculer_expression('SET', $set, ',')
1103 . _sqlite_calculer_expression('WHERE', $where),
1104 $serveur, $requeter);
1105 }
1106
1107
1108
1109 /*
1110 *
1111 * Ensuite les fonctions non abstraites
1112 * crees pour l'occasion de sqlite
1113 *
1114 */
1115
1116
1117 // fonction pour la premiere connexion a un serveur SQLite
1118 // http://doc.spip.org/@_sqlite_init
1119 function _sqlite_init(){
1120 if (!defined('_DIR_DB')) define('_DIR_DB', _DIR_ETC . 'bases/');
1121 if (!defined('_SQLITE_CHMOD')) define('_SQLITE_CHMOD', _SPIP_CHMOD);
1122
1123 if (!is_dir($d = _DIR_DB)){
1124 include_spip('inc/flock');
1125 sous_repertoire($d);
1126 }
1127 }
1128
1129
1130 // teste la version sqlite du link en cours
1131 // http://doc.spip.org/@_sqlite_is_version
1132 function _sqlite_is_version($version='', $link='', $serveur='',$requeter=true){
1133 if ($link==='') $link = _sqlite_link($serveur);
1134 if (!$link) return false;
1135 if (is_a($link, 'PDO')){
1136 $v = 3;
1137 } else {
1138 $v = 2;
1139 }
1140
1141 if (!$version) return $v;
1142 return ($version == $v);
1143 }
1144
1145
1146 // retrouver un link (et definir les fonctions externes sqlite->php)
1147 // $recharger devient inutile (a supprimer ?)
1148 // http://doc.spip.org/@_sqlite_link
1149 function _sqlite_link($serveur = '', $recharger = false){
1150 static $charge = array();
1151 if ($recharger) $charge[$serveur] = false;
1152
1153 $link = &$GLOBALS['connexions'][$serveur ? $serveur : 0]['link'];
1154
1155 if ($link && !$charge[$serveur]){
1156 include_spip('req/sqlite_fonctions');
1157 _sqlite_init_functions($link);
1158 $charge[$serveur] = true;
1159 }
1160 return $link;
1161 }
1162
1163
1164 /* ordre alphabetique pour les autres */
1165
1166
1167 // renvoie les bons echappements (pas sur les fonctions now())
1168 // http://doc.spip.org/@_sqlite_calculer_cite
1169 function _sqlite_calculer_cite($v, $type) {
1170 if (sql_test_date($type) AND preg_match('/^\w+\(/', $v))
1171 return $v;
1172 if (sql_test_int($type)) {
1173 if (is_numeric($v))
1174 return $v;
1175 if (ctype_xdigit(substr($v,2)) AND strncmp($v,'0x',2)==0)
1176 return hexdec(substr($v,2));
1177 }
1178 //else return ("'" . spip_sqlite_quote($v) . "'");
1179 return (spip_sqlite_quote($v));
1180 }
1181
1182
1183 // renvoie grosso modo "$expression join($join, $v)"
1184 // http://doc.spip.org/@_sqlite_calculer_expression
1185 function _sqlite_calculer_expression($expression, $v, $join = 'AND'){
1186 if (empty($v))
1187 return '';
1188
1189 $exp = "\n$expression ";
1190
1191 if (!is_array($v)) {
1192 return $exp . $v;
1193 } else {
1194 if (strtoupper($join) === 'AND')
1195 return $exp . join("\n\t$join ", array_map('_sqlite_calculer_where', $v));
1196 else
1197 return $exp . join($join, $v);
1198 }
1199 }
1200
1201
1202
1203
1204 // pour conversion 0+x ? (pas la peine en sqlite)
1205 // http://doc.spip.org/@_sqlite_calculer_order
1206 function _sqlite_calculer_order($orderby) {
1207 return (is_array($orderby)) ? join(", ", $orderby) : $orderby;
1208 }
1209
1210
1211 // renvoie des 'nom AS alias'
1212 // http://doc.spip.org/@_sqlite_calculer_select_as
1213 function _sqlite_calculer_select_as($args){
1214 $res = '';
1215 foreach($args as $k => $v) {
1216 if (substr($k,-1)=='@') {
1217 // c'est une jointure qui se refere au from precedent
1218 // pas de virgule
1219 $res .= ' ' . $v ;
1220 }
1221 else {
1222 if (!is_numeric($k)) {
1223 $p = strpos($v, " ");
1224 if ($p)
1225 $v = substr($v,0,$p) . " AS '$k'" . substr($v,$p);
1226 else $v .= " AS '$k'";
1227 }
1228 $res .= ', ' . $v ;
1229 }
1230 }
1231 return substr($res,2) . $join;
1232 }
1233
1234
1235 // renvoie les bonnes parentheses pour des where imbriquees
1236 // http://doc.spip.org/@_sqlite_calculer_where
1237 function _sqlite_calculer_where($v){
1238 if (!is_array($v))
1239 return $v ;
1240
1241 $op = array_shift($v);
1242 if (!($n=count($v)))
1243 return $op;
1244 else {
1245 $arg = _sqlite_calculer_where(array_shift($v));
1246 if ($n==1) {
1247 return "$op($arg)";
1248 } else {
1249 $arg2 = _sqlite_calculer_where(array_shift($v));
1250 if ($n==2) {
1251 return "($arg $op $arg2)";
1252 } else return "($arg $op ($arg2) : $v[0])";
1253 }
1254 }
1255 }
1256
1257
1258
1259 /*
1260 * Charger les modules sqlite (si possible) (juste la version demandee),
1261 * ou, si aucune version, renvoie les versions sqlite dispo
1262 * sur ce serveur dans un array
1263 */
1264 // http://doc.spip.org/@_sqlite_charger_version
1265 function _sqlite_charger_version($version=''){
1266 $versions = array();
1267
1268 // version 2
1269 if (!$version || $version == 2){
1270 if (charger_php_extension('sqlite')) {
1271 $versions[]=2;
1272 }
1273 }
1274
1275 // version 3
1276 if (!$version || $version == 3){
1277 if (charger_php_extension('pdo') && charger_php_extension('pdo_sqlite')) {
1278 $versions[]=3;
1279 }
1280 }
1281 if ($version) return in_array($version, $versions);
1282 return $versions;
1283 }
1284
1285
1286
1287 /**
1288 * Gestion des requetes ALTER non reconnues de SQLite :
1289 * ALTER TABLE table DROP column
1290 * ALTER TABLE table CHANGE [COLUMN] columnA columnB definition
1291 * ALTER TABLE table MODIFY column definition
1292 * ALTER TABLE table ADD|DROP PRIMARY KEY
1293 *
1294 * (MODIFY transforme en CHANGE columnA columnA) par spip_sqlite_alter()
1295 *
1296 * 1) creer une table B avec le nouveau format souhaite
1297 * 2) copier la table d'origine A vers B
1298 * 3) supprimer la table A
1299 * 4) renommer la table B en A
1300 * 5) remettre les index (qui sont supprimes avec la table A)
1301 *
1302 * @param string/array $table : nom_table, array(nom_table=>nom_futur)
1303 * @param string/array $col : nom_colonne, array(nom_colonne=>nom_futur)
1304 * @param array $opt : options comme les tables spip, qui sera merge a la table creee : array('field'=>array('nom'=>'syntaxe', ...), 'key'=>array('KEY nom'=>'colonne', ...))
1305 * @param string $serveur : nom de la connexion sql en cours
1306 *
1307 */
1308 // http://doc.spip.org/@_sqlite_modifier_table
1309 function _sqlite_modifier_table($table, $colonne, $opt=array(), $serveur=''){
1310
1311 if (is_array($table)) {
1312 reset($table);
1313 list($table_origine,$table_destination) = each($table);
1314 } else {
1315 $table_origine = $table_destination = $table;
1316 }
1317 // ne prend actuellement qu'un changement
1318 // mais pourra etre adapte pour changer plus qu'une colonne a la fois
1319 if (is_array($colonne)) {
1320 reset($colonne);
1321 list($colonne_origine,$colonne_destination) = each($colonne);
1322 } else {
1323 $colonne_origine = $colonne_destination = $colonne;
1324 }
1325 if (!isset($opt['field'])) $opt['field'] = array();
1326 if (!isset($opt['key'])) $opt['key'] = array();
1327
1328 // si les noms de tables sont differents, pas besoin de table temporaire
1329 // on prendra directement le nom de la future table
1330 $meme_table = ($table_origine == $table_destination);
1331
1332 $def_origine = sql_showtable($table_origine, false, $serveur);
1333 $table_tmp = $table_origine . '_tmp';
1334
1335 // 1) creer une table temporaire avec les modifications
1336 // - DROP : suppression de la colonne
1337 // - CHANGE : modification de la colonne
1338 // (foreach pour conserver l'ordre des champs)
1339
1340 // field
1341 $fields = array();
1342 // pour le INSERT INTO plus loin
1343 // stocker la correspondance nouvelles->anciennes colonnes
1344 $fields_correspondances = array();
1345 foreach ($def_origine['field'] as $c=>$d){
1346
1347 if ($colonne_origine && ($c == $colonne_origine)) {
1348 // si pas DROP
1349 if ($colonne_destination){
1350 $fields[$colonne_destination] = $opt['field'][$colonne_destination];
1351 $fields_correspondances[$colonne_destination] = $c;
1352 }
1353 } else {
1354 $fields[$c] = $d;
1355 $fields_correspondances[$c] = $c;
1356 }
1357 }
1358 // cas de ADD sqlite2 (ajout du champ en fin de table):
1359 if (!$colonne_origine && $colonne_destination){
1360 $fields[$colonne_destination] = $opt['field'][$colonne_destination];
1361 }
1362
1363 // key...
1364 $keys = array();
1365 foreach ($def_origine['key'] as $c=>$d){
1366 $c = str_replace($colonne_origine,$colonne_destination,$c);
1367 $d = str_replace($colonne_origine,$colonne_destination,$d);
1368 // seulement si on ne supprime pas la colonne !
1369 if ($d)
1370 $keys[$c] = $d;
1371 }
1372
1373 // autres keys, on merge
1374 $keys = array_merge($keys,$opt['key']);
1375 $queries = array();
1376 $queries[] = 'BEGIN TRANSACTION';
1377
1378 // copier dans destination (si differente de origine), sinon tmp
1379 $table_copie = ($meme_table) ? $table_tmp : $table_destination;
1380
1381 if ($q = _sqlite_requete_create(
1382 $table_copie,
1383 $fields,
1384 $keys,
1385 $autoinc=false,
1386 $temporary=false,
1387 $ifnotexists=true,
1388 $serveur)){
1389 $queries[] = $q;
1390 }
1391
1392
1393 // 2) y copier les champs qui vont bien
1394 $champs_dest = join(', ', array_keys($fields_correspondances));
1395 $champs_ori = join(', ', $fields_correspondances);
1396 $queries[] = "INSERT INTO $table_copie ($champs_dest) SELECT $champs_ori FROM $table_origine";
1397
1398 // 3) supprimer la table d'origine
1399 $queries[] = "DROP TABLE $table_origine";
1400
1401 // 4) renommer la table temporaire
1402 // avec le nom de la table destination
1403 // si necessaire
1404 if ($meme_table){
1405 if (_sqlite_is_version(3, '', $serveur)){
1406 $queries[] = "ALTER TABLE $table_copie RENAME TO $table_destination";
1407 } else {
1408 $queries[] = _sqlite_requete_create(
1409 $table_destination,
1410 $fields,
1411 $keys,
1412 $autoinc=false,
1413 $temporary=false,
1414 $ifnotexists=false, // la table existe puisqu'on est dans une transaction
1415 $serveur);
1416 $queries[] = "INSERT INTO $table_destination SELECT * FROM $table_copie";
1417 $queries[] = "DROP TABLE $table_copie";
1418 }
1419 }
1420
1421 // 5) remettre les index !
1422 foreach ($keys as $k=>$v) {
1423 if ($k=='PRIMARY KEY'){}
1424 else {
1425 // enlever KEY
1426 $k = substr($k,4);
1427 $queries[] = "CREATE INDEX $table_destination"."_$k ON $table_destination ($v)";
1428 }
1429 }
1430
1431 $queries[] = "COMMIT";
1432
1433
1434 // il faut les faire une par une car $query = join('; ', $queries).";"; ne fonctionne pas
1435 foreach ($queries as $q){
1436 $req = new sqlite_traiter_requete($q, $serveur);
1437 if (!$req->executer_requete()){
1438 spip_log("SQLite : ALTER TABLE table :"
1439 ." Erreur a l'execution de la requete : $q",'sqlite');
1440 return false;
1441 }
1442 }
1443
1444 return true;
1445 }
1446
1447
1448
1449
1450 /*
1451 * Nom des fonctions
1452 */
1453 // http://doc.spip.org/@_sqlite_ref_fonctions
1454 function _sqlite_ref_fonctions(){
1455 $fonctions = array(
1456 'alter' => 'spip_sqlite_alter',
1457 'count' => 'spip_sqlite_count',
1458 'countsel' => 'spip_sqlite_countsel',
1459 'create' => 'spip_sqlite_create',
1460 'create_base' => 'spip_sqlite_create_base',
1461 'create_view' => 'spip_sqlite_create_view',
1462 'date_proche' => 'spip_sqlite_date_proche',
1463 'delete' => 'spip_sqlite_delete',
1464 'drop_table' => 'spip_sqlite_drop_table',
1465 'drop_view' => 'spip_sqlite_drop_view',
1466 'errno' => 'spip_sqlite_errno',
1467 'error' => 'spip_sqlite_error',
1468 'explain' => 'spip_sqlite_explain',
1469 'fetch' => 'spip_sqlite_fetch',
1470 'seek' => 'spip_sqlite_seek',
1471 'free' => 'spip_sqlite_free',
1472 'hex' => 'spip_sqlite_hex',
1473 'in' => 'spip_sqlite_in',
1474 'insert' => 'spip_sqlite_insert',
1475 'insertq' => 'spip_sqlite_insertq',
1476 'insertq_multi' => 'spip_sqlite_insertq_multi',
1477 'listdbs' => 'spip_sqlite_listdbs',
1478 'multi' => 'spip_sqlite_multi',
1479 'optimize' => 'spip_sqlite_optimize',
1480 'query' => 'spip_sqlite_query',
1481 'quote' => 'spip_sqlite_quote',
1482 'replace' => 'spip_sqlite_replace',
1483 'replace_multi' => 'spip_sqlite_replace_multi',
1484 'select' => 'spip_sqlite_select',
1485 'selectdb' => 'spip_sqlite_selectdb',
1486 'set_charset' => 'spip_sqlite_set_charset',
1487 'get_charset' => 'spip_sqlite_get_charset',
1488 'showbase' => 'spip_sqlite_showbase',
1489 'showtable' => 'spip_sqlite_showtable',
1490 'update' => 'spip_sqlite_update',
1491 'updateq' => 'spip_sqlite_updateq',
1492 );
1493
1494 // association de chaque nom http d'un charset aux couples sqlite
1495 // SQLite supporte utf-8 et utf-16 uniquement.
1496 $charsets = array(
1497 'utf-8'=>array('charset'=>'utf8','collation'=>'utf8_general_ci'),
1498 //'utf-16be'=>array('charset'=>'utf16be','collation'=>'UTF-16BE'),// aucune idee de quoi il faut remplir dans es champs la
1499 //'utf-16le'=>array('charset'=>'utf16le','collation'=>'UTF-16LE')
1500 );
1501
1502 $fonctions['charsets'] = $charsets;
1503
1504 return $fonctions;
1505 }
1506
1507
1508
1509 // $query est une requete ou une liste de champs
1510 // http://doc.spip.org/@_sqlite_remplacements_definitions_table
1511 function _sqlite_remplacements_definitions_table($query,$autoinc=false){
1512 // quelques remplacements
1513 $num = "(\s*\([0-9]*\))?";
1514 $enum = "(\s*\([^\)]*\))?";
1515
1516 $remplace = array(
1517 '/enum'.$enum.'/is' => 'VARCHAR',
1518 '/binary/is' => '',
1519 '/COLLATE \w+_bin/is' => '',
1520 '/auto_increment/is' => '',
1521 '/(timestamp .* )ON .*$/is' => '\\1',
1522 '/character set \w+/is' => '',
1523 '/((big|small|medium|tiny)?int(eger)?)'.$num.'\s*unsigned/is' => '\\1 UNSIGNED',
1524 '/(text\s+not\s+null)\s*$/is' => "\\1 DEFAULT ''",
1525 );
1526
1527 // pour l'autoincrement, il faut des INTEGER NOT NULL PRIMARY KEY
1528 if ($autoinc)
1529 $remplace['/(big|small|medium|tiny)?int(eger)?'.$num.'/is'] = 'INTEGER';
1530
1531 return preg_replace(array_keys($remplace), $remplace, $query);
1532 }
1533
1534
1535 /*
1536 * Creer la requete pour la creation d'une table
1537 * retourne la requete pour utilisation par sql_create() et sql_alter()
1538 */
1539 // http://doc.spip.org/@_sqlite_requete_create
1540 function _sqlite_requete_create($nom, $champs, $cles, $autoinc=false, $temporary=false, $_ifnotexists=true, $serveur='',$requeter=true) {
1541 $query = $keys = $s = $p = '';
1542
1543 // certains plugins declarent les tables (permet leur inclusion dans le dump)
1544 // sans les renseigner (laisse le compilo recuperer la description)
1545 if (!is_array($champs) || !is_array($cles))
1546 return;
1547
1548 // sqlite ne gere pas KEY tout court dans une requete CREATE TABLE
1549 // il faut passer par des create index
1550 // Il gere par contre primary key !
1551 // Soit la PK est definie dans les cles, soit dans un champs
1552 if (!$c = $cles[$pk = "PRIMARY KEY"]) {
1553 foreach($champs as $k => $v) {
1554 if (false !== stripos($v,$pk)) {
1555 $c = $k;
1556 // on n'en a plus besoin dans field, vu que defini dans key
1557 $champs[$k] = preg_replace("/$pk/is", '', $champs[$k]);
1558 break;
1559 }
1560 }
1561 }
1562 if ($c) $keys = "\n\t\t$pk ($c)";
1563
1564 $champs = _sqlite_remplacements_definitions_table($champs, $autoinc);
1565 foreach($champs as $k => $v) {
1566 $query .= "$s\n\t\t$k $v";
1567 $s = ",";
1568 }
1569
1570 $ifnotexists = "";
1571 if ($_ifnotexists) {
1572 // simuler le IF NOT EXISTS - version 2
1573 if (_sqlite_is_version(2, '', $serveur)){
1574 $a = spip_sqlite_showtable($nom, $serveur);
1575 if ($a) return false;
1576 }
1577 // sinon l'ajouter en version 3
1578 else {
1579 $ifnotexists = ' IF NOT EXISTS';
1580 }
1581 }
1582
1583 $temporary = $temporary ? ' TEMPORARY':'';
1584 $q = "CREATE$temporary TABLE$ifnotexists $nom ($query" . ($keys ? ",$keys" : '') . ")\n";
1585
1586 return $q;
1587 }
1588
1589
1590
1591 /*
1592 * Retrouver les champs 'timestamp'
1593 * pour les ajouter aux 'insert' ou 'replace'
1594 * afin de simuler le fonctionnement de mysql
1595 *
1596 * stocke le resultat pour ne pas faire
1597 * de requetes showtable intempestives
1598 */
1599 // http://doc.spip.org/@_sqlite_ajouter_champs_timestamp
1600 function _sqlite_ajouter_champs_timestamp($table, $couples, $desc='', $serveur=''){
1601 static $tables = array();
1602
1603 if (!isset($tables[$table])){
1604
1605 if (!$desc){
1606 $f = charger_fonction('trouver_table', 'base');
1607 $desc = $f($table, $serveur);
1608 // si pas de description, on ne fait rien, ou on die() ?
1609 if (!$desc OR !$desc['field']) return $couples;
1610 }
1611
1612 // recherche des champs avec simplement 'TIMESTAMP'
1613 // cependant, il faudra peut etre etendre
1614 // avec la gestion de DEFAULT et ON UPDATE
1615 // mais ceux-ci ne sont pas utilises dans le core
1616 $tables[$table] = array();
1617
1618 foreach ($desc['field'] as $k=>$v){
1619 if (strpos(strtolower(ltrim($v)), 'timestamp')===0)
1620 $tables[$table][] = $k;
1621 }
1622 }
1623
1624 // ajout des champs type 'timestamp' absents
1625 foreach ($tables[$table] as $maj){
1626 if (!array_key_exists($maj, $couples))
1627 $couples[$maj] = "datetime('now')";
1628 }
1629 return $couples;
1630 }
1631
1632
1633
1634 /*
1635 * renvoyer la liste des versions sqlite disponibles
1636 * sur le serveur
1637 */
1638 // http://doc.spip.org/@spip_versions_sqlite
1639 function spip_versions_sqlite(){
1640 return _sqlite_charger_version();
1641 }
1642
1643
1644
1645
1646 /*
1647 * Classe pour partager les lancements de requete
1648 * - peut corriger la syntaxe des requetes pour la conformite a sqlite
1649 * - peut tracer les requetes
1650 *
1651 * Cette classe est presente essentiellement pour un preg_replace_callback
1652 * avec des parametres dans la fonction appelee que l'on souhaite incrementer
1653 * (fonction pour proteger les textes)
1654 *
1655 */
1656 class sqlite_traiter_requete{
1657 var $query = ''; // la requete
1658 var $queryCount = ''; // la requete pour compter
1659 var $serveur = ''; // le serveur
1660 var $link = ''; // le link (ressource) sqlite
1661 var $prefixe = ''; // le prefixe des tables
1662 var $db = ''; // le nom de la base
1663 var $tracer = false; // doit-on tracer les requetes (var_profile)
1664
1665 var $sqlite_version = ''; // Version de sqlite (2 ou 3)
1666
1667 // Pour les corrections a effectuer sur les requetes :
1668 var $textes = array(); // array(code=>'texte') trouvé
1669
1670
1671 // constructeur
1672 // http://doc.spip.org/@sqlite_traiter_requete
1673 function sqlite_traiter_requete($query, $serveur = ''){
1674 $this->query = $query;
1675 $this->serveur = strtolower($serveur);
1676
1677 if (!($this->link = _sqlite_link($this->serveur)) && (!defined('_ECRIRE_INSTALL') || !_ECRIRE_INSTALL)){
1678 spip_log("Aucune connexion sqlite (link)");
1679 return false;
1680 }
1681
1682 $this->sqlite_version =_sqlite_is_version('', $this->link);
1683
1684 $this->prefixe = $GLOBALS['connexions'][$this->serveur ? $this->serveur : 0]['prefixe'];
1685 $this->db = $GLOBALS['connexions'][$this->serveur ? $this->serveur : 0]['db'];
1686
1687 // tracage des requetes ?
1688 $this->tracer = (isset($_GET['var_profile']) && $_GET['var_profile']);
1689 }
1690
1691
1692 // lancer la requete $this->query,
1693 // faire le tracage si demande
1694 // http://doc.spip.org/@executer_requete
1695 function executer_requete(){
1696 $err = "";
1697 if ($this->tracer) {
1698 include_spip('public/tracer');
1699 $t = trace_query_start();
1700 } else $t = 0 ;
1701
1702 # spip_log("requete: $this->serveur >> $this->query",'query'); // boum ? pourquoi ?
1703 if ($this->link){
1704 // memoriser la derniere erreur PHP vue
1705 $e = error_get_last();
1706 // sauver la derniere requete
1707 $GLOBALS['connexions'][$this->serveur ? $this->serveur : 0]['last'] = $this->query;
1708
1709 if ($this->sqlite_version == 3) {
1710 $r = $this->link->query($this->query);
1711 // sauvegarde de la requete (elle y est deja dans $r->queryString)
1712 # $r->spipQueryString = $this->query;
1713
1714 // comptage : oblige de compter le nombre d'entrees retournees
1715 // par une requete SELECT
1716 // aucune autre solution ne donne le nombre attendu :( !
1717 // particulierement s'il y a des LIMIT dans la requete.
1718 if (strtoupper(substr(ltrim($this->query),0,6)) == 'SELECT'){
1719 if ($r) {
1720 $l = $this->link->query($this->query);
1721 $r->spipSqliteRowCount = count($l->fetchAll());
1722 unset($l);
1723 } elseif (is_a($r, 'PDOStatement')) {
1724 $r->spipSqliteRowCount = 0;
1725 }
1726 }
1727 } else {
1728 $r = sqlite_query($this->link, $this->query);
1729 }
1730
1731 // loger les warnings/erreurs eventuels de sqlite remontant dans PHP
1732 if ($err = error_get_last() AND $err!=$e) {
1733 $err = strip_tags($err['message'])." in ".$err['file']." line ".$err['line'];
1734 spip_log("$err - ".$this->query, 'sqlite');
1735 }
1736 else $err="";
1737
1738 } else {
1739 $r = false;
1740 }
1741
1742 if (spip_sqlite_errno($serveur))
1743 $err .= spip_sqlite_error($this->query, $serveur);
1744 return $t ? trace_query_end($this->query, $t, $r, $err, $serveur) : $r;
1745 }
1746
1747 // transformer la requete pour sqlite
1748 // enleve les textes, transforme la requete pour quelle soit
1749 // bien interpretee par sqlite, puis remet les textes
1750 // la fonction affecte $this->query
1751 // http://doc.spip.org/@traduire_requete
1752 function traduire_requete(){
1753 //
1754 // 1) Protection des textes en les remplacant par des codes
1755 //
1756 // enlever les 'textes' et initialiser avec
1757 list($this->query, $textes) = query_echappe_textes($this->query);
1758
1759 //
1760 // 2) Corrections de la requete
1761 //
1762 // Correction Create Database
1763 // Create Database -> requete ignoree
1764 if (strpos($this->query, 'CREATE DATABASE')===0){
1765 spip_log("Sqlite : requete non executee -> $this->query","sqlite");
1766 $this->query = "SELECT 1";
1767 }
1768
1769 // Correction Insert Ignore
1770 // INSERT IGNORE -> insert (tout court et pas 'insert or replace')
1771 if (strpos($this->query, 'INSERT IGNORE')===0){
1772 #spip_log("Sqlite : requete transformee -> $this->query","sqlite");
1773 $this->query = 'INSERT ' . substr($this->query,'13');
1774 }
1775
1776 // Correction des dates avec INTERVAL
1777 // utiliser sql_date_proche() de preference
1778 if (strpos($this->query, 'INTERVAL')!==false){
1779 $this->query = preg_replace_callback("/DATE_(ADD|SUB).*INTERVAL\s+(\d+)\s+([a-zA-Z]+)\)/U",
1780 array(&$this, '_remplacerDateParTime'),
1781 $this->query);
1782 }
1783
1784 // Correction Using
1785 // USING (non reconnu en sqlite2)
1786 // problematique car la jointure ne se fait pas du coup.
1787 if (($this->sqlite_version == 2) && (strpos($this->query, "USING")!==false)) {
1788 spip_log("'USING (champ)' n'est pas reconnu en SQLite 2. Utilisez 'ON table1.champ = table2.champ', 'sqlite'");
1789 $this->query = preg_replace('/USING\s*\([^\)]*\)/', '', $this->query);
1790 }
1791
1792 // Correction Field
1793 // remplace FIELD(table,i,j,k...) par CASE WHEN table=i THEN n ... ELSE 0 END
1794 if (strpos($this->query, 'FIELD')!==false){
1795 $this->query = preg_replace_callback('/FIELD\s*\(([^\)]*)\)/',
1796 array(&$this, '_remplacerFieldParCase'),
1797 $this->query);
1798 }
1799
1800 // Correction des noms de tables FROM
1801 // mettre les bons noms de table dans from, update, insert, replace...
1802 if (preg_match('/\s(SET|VALUES|WHERE|DATABASE)\s/i', $this->query, $regs)) {
1803 $suite = strstr($this->query, $regs[0]);
1804 $this->query = substr($this->query, 0, -strlen($suite));
1805 } else $suite ='';
1806 $pref = ($this->prefixe) ? $this->prefixe . "_": "";
1807 $this->query = preg_replace('/([,\s])spip_/', '\1'.$pref, $this->query) . $suite;
1808
1809 // Correction zero AS x
1810 // pg n'aime pas 0+x AS alias, sqlite, dans le meme style,
1811 // n'apprecie pas du tout SELECT 0 as x ... ORDER BY x
1812 // il dit que x ne doit pas ĂȘtre un integer dans le order by !
1813 // on remplace du coup x par vide() dans ce cas uniquement
1814 //
1815 // rien que pour public/vertebrer.php ?
1816 if ((strpos($this->query, "0 AS")!==false)){
1817 // on ne remplace que dans ORDER BY ou GROUP BY
1818 if (preg_match('/\s(ORDER|GROUP) BY\s/i', $this->query, $regs)) {
1819 $suite = strstr($this->query, $regs[0]);
1820 $this->query = substr($this->query, 0, -strlen($suite));
1821
1822 // on cherche les noms des x dans 0 AS x
1823 // on remplace dans $suite le nom par vide()
1824 preg_match_all('/\b0 AS\s*([^\s,]+)/', $this->query, $matches, PREG_PATTERN_ORDER);
1825 foreach ($matches[1] as $m){
1826 $suite = str_replace($m, 'VIDE()', $suite);
1827 }
1828 $this->query .= $suite;
1829 }
1830 }
1831
1832 // Correction possible des divisions entieres
1833 // Le standard SQL (lequel? ou?) semble indiquer que
1834 // a/b=c doit donner c entier si a et b sont entiers 4/3=1.
1835 // C'est ce que retournent effectivement SQL Server et SQLite
1836 // Ce n'est pas ce qu'applique MySQL qui retourne un reel : 4/3=1.333...
1837 //
1838 // On peut forcer la conversion en multipliant par 1.0 avant la division
1839 // /!\ SQLite 3.5.9 Debian/Ubuntu est victime d'un bug en plus !
1840 // cf. https://bugs.launchpad.net/ubuntu/+source/sqlite3/+bug/254228
1841 // http://www.sqlite.org/cvstrac/tktview?tn=3202
1842 // (4*1.0/3) n'est pas rendu dans ce cas !
1843 # $this->query = str_replace('/','* 1.00 / ',$this->query);
1844
1845 // Correction Antiquotes
1846 // ` => rien
1847 $this->query = str_replace('`','',$this->query);
1848
1849 // Correction critere REGEXP, non reconnu en sqlite2
1850 if (($this->sqlite_version == 2) && (strpos($this->query, 'REGEXP')!==false)){
1851 $this->query = preg_replace('/([^\s\(]*)(\s*)REGEXP(\s*)([^\s\)]*)/', 'REGEXP($4, $1)', $this->query);
1852 }
1853
1854
1855 //
1856 // 3) Remise en place des textes d'origine
1857 //
1858 // Correction Antiquotes et echappements
1859 // ` => rien
1860 if (strpos($this->query,'`')!==false)
1861 $this->query = str_replace('`','', $this->query);
1862
1863 $this->query = query_reinjecte_textes($this->query, $textes);
1864 }
1865
1866
1867
1868 // les callbacks
1869 // remplacer DATE_ / INTERVAL par DATE...strtotime
1870 // http://doc.spip.org/@_remplacerDateParTime
1871 function _remplacerDateParTime($matches){
1872 $op = strtoupper($matches[1] == 'ADD')?'+':'-';
1873 return "datetime('" . date("Y-m-d H:i:s") . "', '$op$matches[2] $matches[3]')";
1874 }
1875
1876 // callback ou l'on remplace FIELD(table,i,j,k...) par CASE WHEN table=i THEN n ... ELSE 0 END
1877 // http://doc.spip.org/@_remplacerFieldParCase
1878 function _remplacerFieldParCase($matches){
1879 $fields = substr($matches[0],6,-1); // ne recuperer que l'interieur X de field(X)
1880 $t = explode(',', $fields);
1881 $index = array_shift($t);
1882
1883 $res = '';
1884 $n=0;
1885 foreach($t as $v) {
1886 $n++;
1887 $res .= "\nWHEN $index=$v THEN $n";
1888 }
1889 return "CASE $res ELSE 0 END ";
1890 }
1891
1892 // callback ou l'on sauve le texte qui est cache dans un tableau $this->textes
1893 // http://doc.spip.org/@_remplacerTexteParCode
1894 function _remplacerTexteParCode($matches){
1895 $this->textes[$code = "%@##".count($this->textes)."##@%"] = $matches[1];
1896 return $code;
1897 }
1898
1899 }
1900
1901 ?>