Completed
Push — spip-3.0 ( 5d8b58 )
by cam
53:01 queued 42:30
created

sqlite_generique.php ➔ _sqlite_modifier_table()   F

Complexity

Conditions 26
Paths > 20000

Size

Total Lines 148
Code Lines 85

Duplication

Lines 10
Ratio 6.76 %

Importance

Changes 0
Metric Value
cc 26
eloc 85
c 0
b 0
f 0
nc 124432
nop 4
dl 10
loc 148
rs 2

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/* *************************************************************************\
4
 *  SPIP, Systeme de publication pour l'internet                           *
5
 *                                                                         *
6
 *  Copyright (c) 2001-2016                                                *
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 contient les fonctions gerant
15
 * les instructions SQL pour Sqlite
16
 *
17
 * @package SPIP\SQL\SQLite
18
 */
19
 
20
if (!defined('_ECRIRE_INC_VERSION')) return;
21
22
// TODO: get/set_caracteres ?
23
24
25
/*
26
 * 
27
 * regroupe le maximum de fonctions qui peuvent cohabiter
28
 * D'abord les fonctions d'abstractions de SPIP
29
 * 
30
 */
31
// http://doc.spip.org/@req_sqlite_dist
32
function req_sqlite_dist($addr, $port, $login, $pass, $db = '', $prefixe = '', $sqlite_version = ''){
33
	static $last_connect = array();
34
35
	// si provient de selectdb
36
	// un code pour etre sur que l'on vient de select_db()
37
	if (strpos($db, $code = '@selectdb@')!==false){
38 View Code Duplication
		foreach (array('addr', 'port', 'login', 'pass', 'prefixe') as $a){
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
39
			$$a = $last_connect[$a];
40
		}
41
		$db = str_replace($code, '', $db);
42
	}
43
44
	/*
45
	 * En sqlite, seule l'adresse du fichier est importante.
46
	 * Ce sera $db le nom,
47
	 * le path est $addr
48
	 * (_DIR_DB si $addr est vide)
49
	 */
50
	_sqlite_init();
51
52
	// determiner le dossier de la base : $addr ou _DIR_DB
53
	$f = _DIR_DB;
54
	if ($addr AND strpos($addr, '/')!==false)
55
		$f = rtrim($addr, '/').'/';
56
57
	// un nom de base demande et impossible d'obtenir la base, on s'en va :
58
	// il faut que la base existe ou que le repertoire parent soit writable
59
	if ($db AND !is_file($f .= $db.'.sqlite') AND !is_writable(dirname($f))){
60
		spip_log("base $f non trouvee ou droits en ecriture manquants", 'sqlite.'._LOG_HS);
61
		return false;
62
	}
63
64
	// charger les modules sqlite au besoin
65
	if (!_sqlite_charger_version($sqlite_version)){
66
		spip_log("Impossible de trouver/charger le module SQLite ($sqlite_version)!", 'sqlite.'._LOG_HS);
67
		return false;
68
	}
69
70
	// chargement des constantes
71
	// il ne faut pas definir les constantes avant d'avoir charge les modules sqlite
72
	$define = "spip_sqlite".$sqlite_version."_constantes";
73
	$define();
74
75
	$ok = false;
0 ignored issues
show
Unused Code introduced by
$ok is not used, you could remove the assignment.

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

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

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

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

Loading history...
76
	if (!$db){
77
		// si pas de db ->
78
		// base temporaire tant qu'on ne connait pas son vrai nom
79
		// pour tester la connexion
80
		$db = "_sqlite".$sqlite_version."_install";
81
		$tmp = _DIR_DB.$db.".sqlite";
82 View Code Duplication
		if ($sqlite_version==3){
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
83
			$ok = $link = new PDO("sqlite:$tmp");
84
		} else {
85
			$ok = $link = sqlite_open($tmp, _SQLITE_CHMOD, $err);
86
		}
87 View Code Duplication
	} else {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
88
		// Ouvrir (eventuellement creer la base)
89
		// si pas de version fourni, on essaie la 3, sinon la 2
90
		if ($sqlite_version==3){
91
			$ok = $link = new PDO("sqlite:$f");
92
		} else {
93
			$ok = $link = sqlite_open($f, _SQLITE_CHMOD, $err);
94
		}
95
	}
96
97
	if (!$ok){
98
		$e = sqlite_last_error($db);
99
		spip_log("Impossible d'ouvrir la base SQLite($sqlite_version) $f : $e", 'sqlite.'._LOG_HS);
100
		return false;
101
	}
102
103
	if ($link){
104
		$last_connect = array(
105
			'addr' => $addr,
106
			'port' => $port,
107
			'login' => $login,
108
			'pass' => $pass,
109
			'db' => $db,
110
			'prefixe' => $prefixe,
111
		);
112
		// etre sur qu'on definit bien les fonctions a chaque nouvelle connexion
113
		include_spip('req/sqlite_fonctions');
114
		_sqlite_init_functions($link);
115
	}
116
117
	return array(
118
		'db' => $db,
119
		'prefixe' => $prefixe ? $prefixe : $db,
120
		'link' => $link,
121
	);
122
}
123
124
125
/**
126
 * Fonction de requete generale, munie d'une trace a la demande
127
 *
128
 * @param string $query
129
 * 		Requete a executer
130
 * @param string $serveur
131
 * 		Nom du connecteur
132
 * @param bool $requeter
133
 * 		Effectuer la requete ?
134
 * 		- true pour executer
135
 * 		- false pour retourner le texte de la requete
136
 * @return bool|SQLiteResult|string
137
 * 		Resultat de la requete
138
 */
139
function spip_sqlite_query($query, $serveur = '', $requeter = true){
140
	#spip_log("spip_sqlite_query() > $query",'sqlite.'._LOG_DEBUG);
141
	#_sqlite_init(); // fait la premiere fois dans spip_sqlite
142
	$query = spip_sqlite::traduire_requete($query, $serveur);
143
	if (!$requeter) return $query;
144
	return spip_sqlite::executer_requete($query, $serveur);
145
}
146
147
148
/* ordre alphabetique pour les autres */
149
150
// http://doc.spip.org/@spip_sqlite_alter
151
function spip_sqlite_alter($query, $serveur = '', $requeter = true){
0 ignored issues
show
Unused Code introduced by
The parameter $requeter is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
152
153
	$query = spip_sqlite_query("ALTER $query", $serveur, false);
154
	// traduire la requete pour recuperer les bons noms de table
155
	$query = spip_sqlite::traduire_requete($query, $serveur);
156
157
	/*
158
		 * la il faut faire les transformations
159
		 * si ALTER TABLE x (DROP|CHANGE) y
160
		 *
161
		 * 1) recuperer "ALTER TABLE table "
162
		 * 2) spliter les sous requetes (,)
163
		 * 3) faire chaque requete independemment
164
		 */
165
166
	// 1
167
	if (preg_match("/\s*(ALTER(\s*IGNORE)?\s*TABLE\s*([^\s]*))\s*(.*)?/is", $query, $regs)){
168
		$debut = $regs[1];
169
		$table = $regs[3];
170
		$suite = $regs[4];
171
	} else {
172
		spip_log("SQLite : Probleme de ALTER TABLE mal forme dans $query", 'sqlite.'._LOG_ERREUR);
173
		return false;
174
	}
175
176
	// 2
177
	// il faudrait une regexp pour eviter de spliter ADD PRIMARY KEY (colA, colB)
178
	// tout en cassant "ADD PRIMARY KEY (colA, colB), ADD INDEX (chose)"... en deux
179
	// ou revoir l'api de sql_alter en creant un 
180
	// sql_alter_table($table,array($actions));
181
	$todo = explode(',', $suite);
182
183
	// on remet les morceaux dechires ensembles... que c'est laid !
184
	$todo2 = array();
185
	$i = 0;
186
	$ouverte = false;
187 View Code Duplication
	while ($do = array_shift($todo)){
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
188
		$todo2[$i] = isset($todo2[$i]) ? $todo2[$i].",".$do : $do;
189
		$o = (false!==strpos($do, "("));
190
		$f = (false!==strpos($do, ")"));
191
		if ($o AND !$f) $ouverte = true;
192
		elseif ($f) $ouverte = false;
193
		if (!$ouverte) $i++;
194
	}
195
196
	// 3	
197
	$resultats = array();
0 ignored issues
show
Unused Code introduced by
$resultats is not used, you could remove the assignment.

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

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

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

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

Loading history...
198
	foreach ($todo2 as $do){
199
		$do = trim($do);
200
		if (!preg_match('/(DROP PRIMARY KEY|DROP KEY|DROP INDEX|DROP COLUMN|DROP'
201
		                .'|CHANGE COLUMN|CHANGE|MODIFY|RENAME TO|RENAME'
202
		                .'|ADD PRIMARY KEY|ADD KEY|ADD INDEX|ADD UNIQUE KEY|ADD UNIQUE'
203
		                .'|ADD COLUMN|ADD'
204
		                .')\s*([^\s]*)\s*(.*)?/i', $do, $matches)){
205
			spip_log("SQLite : Probleme de ALTER TABLE, utilisation non reconnue dans : $do \n(requete d'origine : $query)", 'sqlite.'._LOG_ERREUR);
206
			return false;
207
		}
208
209
		$cle = strtoupper($matches[1]);
210
		$colonne_origine = $matches[2];
211
		$colonne_destination = '';
0 ignored issues
show
Unused Code introduced by
$colonne_destination is not used, you could remove the assignment.

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

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

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

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

Loading history...
212
213
		$def = $matches[3];
214
215
		// eluder une eventuelle clause before|after|first inutilisable
216
		$defr = rtrim(preg_replace('/(BEFORE|AFTER|FIRST)(.*)$/is', '', $def));
217
		$defo = $defr; // garder la def d'origine pour certains cas
218
		// remplacer les definitions venant de mysql
219
		$defr = _sqlite_remplacements_definitions_table($defr);
220
221
		// reinjecter dans le do
222
		$do = str_replace($def, $defr, $do);
223
		$def = $defr;
224
225
		switch ($cle) {
226
			// suppression d'un index
227
			case 'DROP KEY':
228
			case 'DROP INDEX':
229
				$nom_index = $colonne_origine;
230
				spip_sqlite_drop_index($nom_index, $table, $serveur);
231
				break;
232
233
			// suppression d'une pk
234 View Code Duplication
			case 'DROP PRIMARY KEY':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
235
				if (!_sqlite_modifier_table(
236
					$table,
237
					$colonne_origine,
238
					array('key' => array('PRIMARY KEY' => '')),
239
					$serveur)){
240
					return false;
241
				}
242
				break;
243
			// suppression d'une colonne
244
			case 'DROP COLUMN':
245 View Code Duplication
			case 'DROP':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
246
				if (!_sqlite_modifier_table(
247
					$table,
248
					array($colonne_origine => ""),
249
					'',
250
					$serveur)){
251
					return false;
252
				}
253
				break;
254
255
			case 'CHANGE COLUMN':
256
			case 'CHANGE':
257
				// recuperer le nom de la future colonne
258
			  // on reprend la def d'origine car _sqlite_modifier_table va refaire la translation
259
			  // en tenant compte de la cle primaire (ce qui est mieux)
260
				$def = trim($defo);
261
				$colonne_destination = substr($def, 0, strpos($def, ' '));
262
				$def = substr($def, strlen($colonne_destination)+1);
263
264
				if (!_sqlite_modifier_table(
265
					$table,
266
					array($colonne_origine => $colonne_destination),
267
					array('field' => array($colonne_destination => $def)),
268
					$serveur)){
269
					return false;
270
				}
271
				break;
272
273 View Code Duplication
			case 'MODIFY':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
274
			  // on reprend la def d'origine car _sqlite_modifier_table va refaire la translation
275
			  // en tenant compte de la cle primaire (ce qui est mieux)
276
				if (!_sqlite_modifier_table(
277
					$table,
278
					$colonne_origine,
279
					array('field' => array($colonne_origine => $defo)),
280
					$serveur)){
281
					return false;
282
				}
283
				break;
284
285
			// pas geres en sqlite2
286
			case 'RENAME':
287
				$do = "RENAME TO".substr($do, 6);
288
			case 'RENAME TO':
289
				if (_sqlite_is_version(3, '', $serveur)){
290 View Code Duplication
					if (!spip_sqlite::executer_requete("$debut $do", $serveur)){
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
291
						spip_log("SQLite : Erreur ALTER TABLE / RENAME : $query", 'sqlite.'._LOG_ERREUR);
292
						return false;
293
					}
294
					// artillerie lourde pour sqlite2 !
295
				} else {
296
					$table_dest = trim(substr($do, 9));
297 View Code Duplication
					if (!_sqlite_modifier_table(array($table => $table_dest), '', '', $serveur)){
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
298
						spip_log("SQLite : Erreur ALTER TABLE / RENAME : $query", 'sqlite.'._LOG_ERREUR);
299
						return false;
300
					}
301
				}
302
				break;
303
304
			// ajout d'une pk
305
			case 'ADD PRIMARY KEY':
306
				$pk = trim(substr($do, 16));
307
				$pk = ($pk[0]=='(') ? substr($pk, 1, -1) : $pk;
308
				if (!_sqlite_modifier_table(
309
					$table,
310
					$colonne_origine,
311
					array('key' => array('PRIMARY KEY' => $pk)),
312
					$serveur)){
313
					return false;
314
				}
315
				break;
316
			// ajout d'un index
317
			case 'ADD UNIQUE KEY':
318
			case 'ADD UNIQUE':
319
				$unique=true;
320
			case 'ADD INDEX':
321
			case 'ADD KEY':
322
				// peut etre "(colonne)" ou "nom_index (colonnes)"
323
				// bug potentiel si qqn met "(colonne, colonne)"
324
				//
325
				// nom_index (colonnes)
326
				if ($def){
327
					$colonnes = substr($def, 1, -1);
328
					$nom_index = $colonne_origine;
329
				}
330
				else {
331
					// (colonne)
332
					if ($colonne_origine[0]=="("){
333
						$colonnes = substr($colonne_origine, 1, -1);
334 View Code Duplication
						if (false!==strpos(",", $colonnes)){
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
335
							spip_log(_LOG_GRAVITE_ERREUR, "SQLite : Erreur, impossible de creer un index sur plusieurs colonnes"
336
							                              ." sans qu'il ait de nom ($table, ($colonnes))", 'sqlite');
0 ignored issues
show
Unused Code introduced by
The call to spip_log() has too many arguments starting with 'sqlite'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
337
							break;
338
						} else {
339
							$nom_index = $colonnes;
340
						}
341
					}
342
						// nom_index
343
					else {
344
						$nom_index = $colonnes = $colonne_origine;
345
					}
346
				}
347
				spip_sqlite_create_index($nom_index, $table, $colonnes, $unique, $serveur);
0 ignored issues
show
Bug introduced by
The variable $unique does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
348
				break;
349
350
			// pas geres en sqlite2
351
			case 'ADD COLUMN':
352
				$do = "ADD".substr($do, 10);
353
			case 'ADD':
354
			default:
355
				if (_sqlite_is_version(3, '', $serveur) AND !preg_match(',primary\s+key,i',$do)){
356 View Code Duplication
					if (!spip_sqlite::executer_requete("$debut $do", $serveur)){
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
357
						spip_log("SQLite : Erreur ALTER TABLE / ADD : $query", 'sqlite.'._LOG_ERREUR);
358
						return false;
359
					}
360
					break;
361
362
				}
363
				// artillerie lourde pour sqlite2 !
364
				// ou si la colonne est aussi primary key
365
				// cas du add id_truc int primary key
366
				// ajout d'une colonne qui passe en primary key directe
367
				else {
368
					$def = trim(substr($do, 3));
369
					$colonne_ajoutee = substr($def, 0, strpos($def, ' '));
370
					$def = substr($def, strlen($colonne_ajoutee)+1);
371
					$opts = array();
372
					if (preg_match(',primary\s+key,i',$def)){
373
						$opts['key'] = array('PRIMARY KEY' => $colonne_ajoutee);
374
						$def = preg_replace(',primary\s+key,i','',$def);
375
					}
376
					$opts['field'] = array($colonne_ajoutee => $def);
377 View Code Duplication
					if (!_sqlite_modifier_table($table, array($colonne_ajoutee), $opts, $serveur)){
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
378
						spip_log("SQLite : Erreur ALTER TABLE / ADD : $query", 'sqlite.'._LOG_ERREUR);
379
						return false;
380
					}
381
				}
382
				break;
383
		}
384
		// tout est bon, ouf !
385
		spip_log("SQLite ($serveur) : Changements OK : $debut $do", 'sqlite.'._LOG_INFO);
386
	}
387
388
	spip_log("SQLite ($serveur) : fin ALTER TABLE OK !", 'sqlite.'._LOG_INFO);
389
	return true;
390
}
391
392
393
/**
394
 * Fonction de creation d'une table SQL nommee $nom
395
 * http://doc.spip.org/@spip_sqlite_create
396
 *
397
 * @param string $nom
398
 * @param array $champs
399
 * @param array $cles
400
 * @param bool $autoinc
401
 * @param bool $temporary
402
 * @param string $serveur
403
 * @param bool $requeter
404
 * @return bool|SQLiteResult|string
405
 */
406
function spip_sqlite_create($nom, $champs, $cles, $autoinc = false, $temporary = false, $serveur = '', $requeter = true){
407
	$query = _sqlite_requete_create($nom, $champs, $cles, $autoinc, $temporary, $ifnotexists = true, $serveur, $requeter);
408
	if (!$query) return false;
409
	$res = spip_sqlite_query($query, $serveur, $requeter);
0 ignored issues
show
Bug introduced by
It seems like $query defined by _sqlite_requete_create($...e, $serveur, $requeter) on line 407 can also be of type boolean; however, spip_sqlite_query() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
410
411
	// SQLite ne cree pas les KEY sur les requetes CREATE TABLE
412
	// il faut donc les faire creer ensuite
413
	if (!$requeter) return $res;
414
415
	$ok = $res ? true : false;
416
	if ($ok){
417
		foreach ($cles as $k => $v){
418
			if (preg_match(',^(KEY|UNIQUE)\s,i',$k,$m)){
419
				$index = trim(substr($k,strlen($m[1])));
420
				$unique = (strlen($m[1])>3);
421
				$ok &= spip_sqlite_create_index($index, $nom, $v, $unique, $serveur);
422
			}
423
		}
424
	}
425
	return $ok ? true : false;
426
}
427
428
/**
429
 * Fonction pour creer une base de donnees SQLite
430
 *
431
 * @param string $nom le nom de la base (sans l'extension de fichier)
432
 * @param string $serveur le nom de la connexion
433
 * @param string $option options
0 ignored issues
show
Documentation introduced by
Should the type for parameter $option not be boolean|string?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
434
 *
435
 * @return bool true si la base est creee.
436
 **/
437
function spip_sqlite_create_base($nom, $serveur = '', $option = true){
0 ignored issues
show
Unused Code introduced by
The parameter $option is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
438
	$f = $nom.'.sqlite';
439
	if (strpos($nom, "/")===false)
440
		$f = _DIR_DB.$f;
441
	if (_sqlite_is_version(2, '', $serveur)){
442
		$ok = sqlite_open($f, _SQLITE_CHMOD, $err);
443
	} else {
444
		$ok = new PDO("sqlite:$f");
445
	}
446
	if ($ok){
447
		unset($ok);
448
		return true;
449
	}
450
	unset($ok);
451
	return false;
452
}
453
454
455
/**
456
 * Fonction de creation d'une vue SQL nommee $nom
457
 * http://doc.spip.org/@spip_sqlite_create_view
458
 *
459
 * @param string $nom
460
 * 		Nom de la vue a creer
461
 * @param string $query_select
462
 * 		Texte de la requete de selection servant de base a la vue
463
 * @param string $serveur
464
 * 		Nom du connecteur
465
 * @param bool $requeter
466
 * 		Effectuer la requete ?
467
 * 		- true pour executer
468
 * 		- false pour retourner le texte de la requete
469
 * @return bool|SQLiteResult|string
470
 * 		Resultat de la requete ou
471
 * 		- false si erreur ou si la vue existe deja
472
 * 		- string texte de la requete si $requeter vaut false
473
 */
474 View Code Duplication
function spip_sqlite_create_view($nom, $query_select, $serveur = '', $requeter = true){
0 ignored issues
show
Duplication introduced by
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
475
	if (!$query_select) return false;
476
	// vue deja presente
477
	if (sql_showtable($nom, false, $serveur)){
478
		spip_log("Echec creation d'une vue sql ($nom) car celle-ci existe deja (serveur:$serveur)", 'sqlite.'._LOG_ERREUR);
479
		return false;
480
	}
481
482
	$query = "CREATE VIEW $nom AS ".$query_select;
483
	return spip_sqlite_query($query, $serveur, $requeter);
484
}
485
486
/**
487
 * Fonction de creation d'un INDEX
488
 *
489
 * @param string $nom : nom de l'index
490
 * @param string $table : table sql de l'index
491
 * @param string/array $champs : liste de champs sur lesquels s'applique l'index
0 ignored issues
show
Documentation introduced by
The doc-type string/array could not be parsed: Unknown type name "string/array" at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
492
 * @param string $serveur : nom de la connexion sql utilisee
493
 * @param bool $requeter : true pour executer la requete ou false pour retourner le texte de la requete
494
 *
495
 * @return bool ou requete
0 ignored issues
show
Documentation introduced by
Should the return type not be boolean|SQLiteResult|string?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
496
 */
497
function spip_sqlite_create_index($nom, $table, $champs, $unique='', $serveur = '', $requeter = true){
498 View Code Duplication
	if (!($nom OR $table OR $champs)){
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
499
		spip_log("Champ manquant pour creer un index sqlite ($nom, $table, (".join(',', $champs)."))", 'sqlite.'._LOG_ERREUR);
500
		return false;
501
	}
502
503
	// SQLite ne differentie pas noms des index en fonction des tables
504
	// il faut donc creer des noms uniques d'index pour une base sqlite
505
	$nom = $table.'_'.$nom;
506
	// enlever d'eventuelles parentheses deja presentes sur champs
507
	if (!is_array($champs)){
508
		if ($champs[0]=="(") $champs = substr($champs, 1, -1);
509
		$champs = array($champs);
510
		// supprimer l'info de longueur d'index mysql en fin de champ
511
		$champs = preg_replace(",\(\d+\)$,","",$champs);
512
	}
513
514
	$ifnotexists = "";
515
	$version = spip_sqlite_fetch(spip_sqlite_query("select sqlite_version() AS sqlite_version",$serveur),'',$serveur);
516
	if (!function_exists('spip_version_compare')) include_spip('plugins/installer');
517
518 View Code Duplication
	if ($version AND spip_version_compare($version['sqlite_version'],'3.3.0','>=')) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
519
		$ifnotexists = ' IF NOT EXISTS';
520
	} else {
521
		/* simuler le IF EXISTS - version 2 et sqlite < 3.3a */
522
		$a = spip_sqlite_showtable($table, $serveur);
523
		if (isset($a['key']['KEY '.$nom])) return true;
524
	}
525
526
	$query = "CREATE ".($unique?"UNIQUE ":"")."INDEX$ifnotexists $nom ON $table (".join(',', $champs).")";
527
	$res = spip_sqlite_query($query, $serveur, $requeter);
528
	if (!$requeter) return $res;
529
	if ($res)
0 ignored issues
show
Coding Style introduced by
The if-else statement can be simplified to return (bool) $res;.
Loading history...
530
		return true;
531
	else
532
		return false;
533
}
534
535
/**
536
 * en PDO/sqlite3, il faut calculer le count par une requete count(*)
537
 * pour les resultats de SELECT
538
 * cela est fait sans spip_sqlite_query()
539
 * http://doc.spip.org/@spip_sqlite_count
540
 *
541
 * @param  $r
542
 * @param string $serveur
543
 * @param bool $requeter
544
 * @return int
545
 */
546
function spip_sqlite_count($r, $serveur = '', $requeter = true){
0 ignored issues
show
Unused Code introduced by
The parameter $requeter is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
547
	if (!$r) return 0;
548
549
	if (_sqlite_is_version(3, '', $serveur)){
550
		// select ou autre (insert, update,...) ?
551
552
		// (link,requete) a compter
553
		if (is_array($r->spipSqliteRowCount)){
554
			list($link,$query) = $r->spipSqliteRowCount;
555
			// amelioration possible a tester intensivement : pas de order by pour compter !
556
			// $query = preg_replace(",ORDER BY .+(LIMIT\s|HAVING\s|GROUP BY\s|$),Uims","\\1",$query);
557
			$query = "SELECT count(*) as zzzzsqlitecount FROM ($query)";
558
			$l = $link->query($query);
559
			$i = 0;
560
			if ($l AND $z = $l->fetch())
561
				$i = $z['zzzzsqlitecount'];
562
			$r->spipSqliteRowCount = $i;
563
		}
564
		if (isset($r->spipSqliteRowCount)){
565
			// Ce compte est faux s'il y a des limit dans la requete :(
566
			// il retourne le nombre d'enregistrements sans le limit
567
			return $r->spipSqliteRowCount;
568
		} else {
569
			return $r->rowCount();
570
		}
571
	} else {
572
		return sqlite_num_rows($r);
573
	}
574
}
575
576
577
// http://doc.spip.org/@spip_sqlite_countsel
578
function spip_sqlite_countsel($from = array(), $where = array(), $groupby = '', $having = array(), $serveur = '', $requeter = true){
579
	$c = !$groupby ? '*' : ('DISTINCT '.(is_string($groupby) ? $groupby : join(',', $groupby)));
580
	$r = spip_sqlite_select("COUNT($c)", $from, $where, '', '', '',
581
	                        $having, $serveur, $requeter);
582
	if ((is_resource($r) or is_object($r)) && $requeter){ // ressource : sqlite2, object : sqlite3
583
		if (_sqlite_is_version(3, '', $serveur)){
584
			list($r) = spip_sqlite_fetch($r, SPIP_SQLITE3_NUM, $serveur);
585
		} else {
586
			list($r) = spip_sqlite_fetch($r, SPIP_SQLITE2_NUM, $serveur);
587
		}
588
589
	}
590
	return $r;
591
}
592
593
594
// http://doc.spip.org/@spip_sqlite_delete
595
function spip_sqlite_delete($table, $where = '', $serveur = '', $requeter = true){
596
	$res = spip_sqlite_query(
597
		_sqlite_calculer_expression('DELETE FROM', $table, ',')
598
		._sqlite_calculer_expression('WHERE', $where),
599
		$serveur, $requeter);
600
601
	// renvoyer la requete inerte si demandee
602
	if (!$requeter) return $res;
603
604
	if ($res){
605
		$link = _sqlite_link($serveur);
606
		if (_sqlite_is_version(3, $link)){
607
			return $res->rowCount();
608
		} else {
609
			return sqlite_changes($link);
610
		}
611
	}
612
	else
613
		return false;
614
}
615
616
617
// http://doc.spip.org/@spip_sqlite_drop_table
618
function spip_sqlite_drop_table($table, $exist = '', $serveur = '', $requeter = true){
619
	if ($exist) $exist = " IF EXISTS";
620
621
	/* simuler le IF EXISTS - version 2 */
622 View Code Duplication
	if ($exist && _sqlite_is_version(2, '', $serveur)){
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
623
		$a = spip_sqlite_showtable($table, $serveur);
624
		if (!$a) return true;
625
		$exist = '';
626
	}
627
	if (spip_sqlite_query("DROP TABLE$exist $table", $serveur, $requeter))
0 ignored issues
show
Coding Style introduced by
The if-else statement can be simplified to return (bool) spip_sqlit..., $serveur, $requeter);.
Loading history...
628
		return true;
629
	else
630
		return false;
631
}
632
633
/**
634
 * supprime une vue
635
 * http://doc.spip.org/@spip_sqlite_drop_view
636
 *
637
 * @param  $view
638
 * @param string $exist
639
 * @param string $serveur
640
 * @param bool $requeter
641
 * @return bool|SQLiteResult|string
642
 */
643
function spip_sqlite_drop_view($view, $exist = '', $serveur = '', $requeter = true){
644
	if ($exist) $exist = " IF EXISTS";
645
646
	/* simuler le IF EXISTS - version 2 */
647 View Code Duplication
	if ($exist && _sqlite_is_version(2, '', $serveur)){
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
648
		$a = spip_sqlite_showtable($view, $serveur);
649
		if (!$a) return true;
650
		$exist = '';
651
	}
652
653
	return spip_sqlite_query("DROP VIEW$exist $view", $serveur, $requeter);
654
}
655
656
/**
657
 * Fonction de suppression d'un INDEX
658
 *
659
 * @param string $nom : nom de l'index
660
 * @param string $table : table sql de l'index
661
 * @param string $serveur : nom de la connexion sql utilisee
662
 * @param bool $requeter : true pour executer la requete ou false pour retourner le texte de la requete
663
 *
664
 * @return bool ou requete
0 ignored issues
show
Documentation introduced by
Should the return type not be boolean|SQLiteResult|string?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
665
 */
666
function spip_sqlite_drop_index($nom, $table, $serveur = '', $requeter = true){
667
	if (!($nom OR $table)){
668
		spip_log("Champ manquant pour supprimer un index sqlite ($nom, $table)", 'sqlite.'._LOG_ERREUR);
669
		return false;
670
	}
671
672
	// SQLite ne differentie pas noms des index en fonction des tables
673
	// il faut donc creer des noms uniques d'index pour une base sqlite
674
	$index = $table.'_'.$nom;
675
	$exist = " IF EXISTS";
676
677
	/* simuler le IF EXISTS - version 2 */
678
	if (_sqlite_is_version(2, '', $serveur)){
679
		$a = spip_sqlite_showtable($table, $serveur);
680
		if (!isset($a['key']['KEY '.$nom])) return true;
681
		$exist = '';
682
	}
683
684
	$query = "DROP INDEX$exist $index";
685
	return spip_sqlite_query($query, $serveur, $requeter);
686
}
687
688
/**
689
 * Retourne la dernière erreur generée
690
 *
691
 * @param $serveur
692
 * 		nom de la connexion
693
 * @return string
694
 * 		erreur eventuelle
695
 **/
696 View Code Duplication
function spip_sqlite_error($query = '', $serveur = ''){
0 ignored issues
show
Duplication introduced by
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
697
	$link = _sqlite_link($serveur);
698
699
	if (_sqlite_is_version(3, $link)){
700
		$errs = $link->errorInfo();
701
		/*
702
			$errs[0]
703
				numero SQLState ('HY000' souvent lors d'une erreur)
704
				http://www.easysoft.com/developer/interfaces/odbc/sqlstate_status_return_codes.html
705
			$errs[1]
706
				numéro d'erreur SQLite (souvent 1 lors d'une erreur)
707
				http://www.sqlite.org/c3ref/c_abort.html
708
			$errs[2]
709
				Le texte du message d'erreur
710
		*/
711
		$s = '';
712
		if (ltrim($errs[0],'0')) { // 00000 si pas d'erreur
713
			$s = "$errs[2]";
714
		}
715
	} elseif ($link) {
716
		$s = sqlite_error_string(sqlite_last_error($link));
717
	} else {
718
		$s = ": aucune ressource sqlite (link)";
719
	}
720
	if ($s) spip_log("$s - $query", 'sqlite.'._LOG_ERREUR);
721
	return $s;
722
}
723
724
/**
725
 * Retourne le numero de la dernière erreur SQL
726
 *
727
 * Le numéro (en sqlite3/pdo) est un retour ODBC tel que (très souvent) HY000
728
 * http://www.easysoft.com/developer/interfaces/odbc/sqlstate_status_return_codes.html
729
 * 
730
 * @param string $serveur
731
 * 		nom de la connexion
732
 * @return int|string
733
 * 		0 pas d'erreur
734
 * 		1 ou autre erreur (en sqlite 2)
735
 * 		'HY000/1' : numéro de l'erreur SQLState / numéro d'erreur interne SQLite (en sqlite 3)
736
 **/
737 View Code Duplication
function spip_sqlite_errno($serveur = ''){
0 ignored issues
show
Duplication introduced by
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
738
	$link = _sqlite_link($serveur);
739
740
	if (_sqlite_is_version(3, $link)){
741
		$t = $link->errorInfo();
742
		$s = ltrim($t[0],'0'); // 00000 si pas d'erreur
743
		if ($s) $s .= ' / ' . $t[1]; // ajoute l'erreur du moteur SQLite
744
	} elseif ($link) {
745
		$s = sqlite_last_error($link);
746
	} else {
747
		$s = ": aucune ressource sqlite (link)";
748
	}
749
750
	if ($s) spip_log("Erreur sqlite $s", 'sqlite.'._LOG_ERREUR);
751
752
	return $s ? $s : 0;
753
}
754
755
756
// http://doc.spip.org/@spip_sqlite_explain
757
function spip_sqlite_explain($query, $serveur = '', $requeter = true){
758
	if (strpos(ltrim($query), 'SELECT')!==0) return array();
759
760
	$query = spip_sqlite::traduire_requete($query, $serveur);
761
	$query = 'EXPLAIN '.$query;
762
	if (!$requeter) return $query;
763
	// on ne trace pas ces requetes, sinon on obtient un tracage sans fin...
764
	$r = spip_sqlite::executer_requete($query, $serveur, false);
765
766
	return $r ? spip_sqlite_fetch($r, null, $serveur) : false; // hum ? etrange ca... a verifier
767
}
768
769
770
// http://doc.spip.org/@spip_sqlite_fetch
771
function spip_sqlite_fetch($r, $t = '', $serveur = '', $requeter = true){
0 ignored issues
show
Unused Code introduced by
The parameter $requeter is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
772
773
	$link = _sqlite_link($serveur);
774
	$is_v3 = _sqlite_is_version(3, $link);
775
	if (!$t)
776
		$t = ($is_v3 ? SPIP_SQLITE3_ASSOC : SPIP_SQLITE2_ASSOC);
777
778
	$retour = false;
779
	if ($r)
780
		$retour = ($is_v3 ? $r->fetch($t) : sqlite_fetch_array($r, $t));
781
782
	// les version 2 et 3 parfois renvoie des 'table.titre' au lieu de 'titre' tout court ! pff !
783
	// suppression de 'table.' pour toutes les cles (c'est un peu violent !)
784
	// c'est couteux : on ne verifie que la premiere ligne pour voir si on le fait ou non
785
	if ($retour
786
	  AND strpos(implode('',array_keys($retour)),'.')!==false){
787
		foreach ($retour as $cle => $val){
788
			if (($pos = strpos($cle, '.'))!==false){
789
				$retour[substr($cle, $pos+1)] = &$retour[$cle];
790
				unset($retour[$cle]);
791
			}
792
		}
793
	}
794
795
	return $retour;
796
}
797
798
799
function spip_sqlite_seek($r, $row_number, $serveur = '', $requeter = true){
0 ignored issues
show
Unused Code introduced by
The parameter $requeter is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
800
	if ($r){
801
		$link = _sqlite_link($serveur);
802
		if (_sqlite_is_version(3, $link)){
803
			// encore un truc de bien fichu : PDO ne PEUT PAS faire de seek ou de rewind...
804
			// je me demande si pour sqlite 3 il ne faudrait pas mieux utiliser
805
			// les nouvelles fonctions sqlite3_xx (mais encore moins presentes...)
806
			return false;
807
		}
808
		else {
809
			return sqlite_seek($r, $row_number);
810
		}
811
	}
812
}
813
814
815
// http://doc.spip.org/@spip_sqlite_free
816
function spip_sqlite_free(&$r, $serveur = '', $requeter = true){
0 ignored issues
show
Unused Code introduced by
The parameter $serveur is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $requeter is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
817
	unset($r);
818
	return true;
819
	//return sqlite_free_result($r);
820
}
821
822
823
// http://doc.spip.org/@spip_sqlite_get_charset
824
function spip_sqlite_get_charset($charset = array(), $serveur = '', $requeter = true){
0 ignored issues
show
Unused Code introduced by
The parameter $charset is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $serveur is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $requeter is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
825
	//$c = !$charset ? '' : (" LIKE "._q($charset['charset']));
826
	//return spip_sqlite_fetch(sqlite_query(_sqlite_link($serveur), "SHOW CHARACTER SET$c"), NULL, $serveur);
827
}
828
829
830
// http://doc.spip.org/@spip_sqlite_hex
831
function spip_sqlite_hex($v){
832
	return hexdec($v);
833
}
834
835
836
// http://doc.spip.org/@spip_sqlite_in
837 View Code Duplication
function spip_sqlite_in($val, $valeurs, $not = '', $serveur = '', $requeter = true){
0 ignored issues
show
Unused Code introduced by
The parameter $serveur is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $requeter is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Duplication introduced by
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
838
	$n = $i = 0;
839
	$in_sql = "";
840
	while ($n = strpos($valeurs, ',', $n+1)){
841
		if ((++$i)>=255){
842
			$in_sql .= "($val $not IN (".
843
			           substr($valeurs, 0, $n).
844
			           "))\n".
845
			           ($not ? "AND\t" : "OR\t");
846
			$valeurs = substr($valeurs, $n+1);
847
			$i = $n = 0;
848
		}
849
	}
850
	$in_sql .= "($val $not IN ($valeurs))";
851
852
	return "($in_sql)";
853
}
854
855
856
// http://doc.spip.org/@spip_sqlite_insert
857
function spip_sqlite_insert($table, $champs, $valeurs, $desc = '', $serveur = '', $requeter = true){
0 ignored issues
show
Unused Code introduced by
The parameter $desc is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
858
859
	$query = "INSERT INTO $table ".($champs ? "$champs VALUES $valeurs" : "DEFAULT VALUES");
860
	if ($r = spip_sqlite_query($query, $serveur, $requeter)){
861
		if (!$requeter) return $r;
862
		$nb = spip_sqlite::last_insert_id($serveur);
863
	}
864
	else
865
		$nb = 0;
866
867
	$err = spip_sqlite_error($query, $serveur);
0 ignored issues
show
Unused Code introduced by
$err is not used, you could remove the assignment.

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

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

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

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

Loading history...
868
	// cas particulier : ne pas substituer la reponse spip_sqlite_query si on est en profilage
869
	return isset($_GET['var_profile']) ? $r : $nb;
870
871
}
872
873
874
// http://doc.spip.org/@spip_sqlite_insertq
875
function spip_sqlite_insertq($table, $couples = array(), $desc = array(), $serveur = '', $requeter = true){
876
	if (!$desc) $desc = description_table($table, $serveur);
0 ignored issues
show
Bug Best Practice introduced by
The expression $desc of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
877
	if (!$desc) die("$table insertion sans description");
0 ignored issues
show
Coding Style Compatibility introduced by
The function spip_sqlite_insertq() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
878
	$fields = isset($desc['field']) ? $desc['field'] : array();
879
880
	foreach ($couples as $champ => $val){
881
		$couples[$champ] = _sqlite_calculer_cite($val, $fields[$champ]);
882
	}
883
884
	// recherche de champs 'timestamp' pour mise a jour auto de ceux-ci
885
	$couples = _sqlite_ajouter_champs_timestamp($table, $couples, $desc, $serveur);
886
887
	$cles = $valeurs = "";
888
	if (count($couples)){
889
		$cles = "(".join(',', array_keys($couples)).")";
890
		$valeurs = "(".join(',', $couples).")";
891
	}
892
893
	return spip_sqlite_insert($table, $cles, $valeurs, $desc, $serveur, $requeter);
894
}
895
896
897
// http://doc.spip.org/@spip_sqlite_insertq_multi
898
function spip_sqlite_insertq_multi($table, $tab_couples = array(), $desc = array(), $serveur = '', $requeter = true){
899
	if (!$desc) $desc = description_table($table, $serveur);
0 ignored issues
show
Bug Best Practice introduced by
The expression $desc of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
900
	if (!$desc) die("$table insertion sans description");
0 ignored issues
show
Coding Style Compatibility introduced by
The function spip_sqlite_insertq_multi() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
901
	if (!isset($desc['field']))
902
		$desc['field'] = array();
903
904
	// recuperer les champs 'timestamp' pour mise a jour auto de ceux-ci
905
	$maj = _sqlite_ajouter_champs_timestamp($table, array(), $desc, $serveur);
906
907
	// seul le nom de la table est a traduire ici :
908
	// le faire une seule fois au debut
909
	$query_start = "INSERT INTO $table ";
910
	$query_start = spip_sqlite::traduire_requete($query_start,$serveur);
911
912
	// ouvrir une transaction
913
	if ($requeter)
914
		spip_sqlite::demarrer_transaction($serveur);
915
916
	while ($couples = array_shift($tab_couples)){
917
		foreach ($couples as $champ => $val){
918
			$couples[$champ] = _sqlite_calculer_cite($val, $desc['field'][$champ]);
919
		}
920
921
		// inserer les champs timestamp par defaut
922
		$couples = array_merge($maj,$couples);
923
924
		$champs = $valeurs = "";
0 ignored issues
show
Unused Code introduced by
$valeurs is not used, you could remove the assignment.

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

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

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

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

Loading history...
Unused Code introduced by
$champs is not used, you could remove the assignment.

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

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

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

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

Loading history...
925
		if (count($couples)){
926
			$champs = "(".join(',', array_keys($couples)).")";
927
			$valeurs = "(".join(',', $couples).")";
928
			$query = $query_start."$champs VALUES $valeurs";
929
		}
930
		else
931
			$query = $query_start."DEFAULT VALUES";
932
		
933
		if ($requeter)
934
			$retour = spip_sqlite::executer_requete($query,$serveur);
935
936
		// sur le dernier couple uniquement
937
		if (!count($tab_couples)){
938
			$nb = 0;
0 ignored issues
show
Unused Code introduced by
$nb is not used, you could remove the assignment.

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

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

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

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

Loading history...
939
			if ($requeter)
940
				$nb = spip_sqlite::last_insert_id($serveur);
941
			else
942
				return $query;
943
		}
944
945
		$err = spip_sqlite_error($query, $serveur);
0 ignored issues
show
Unused Code introduced by
$err is not used, you could remove the assignment.

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

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

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

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

Loading history...
946
	}
947
948
	if ($requeter)
949
		spip_sqlite::finir_transaction($serveur);
950
951
	// renvoie le dernier id d'autoincrement ajoute
952
	// cas particulier : ne pas substituer la reponse spip_sqlite_query si on est en profilage
953
	return isset($_GET['var_profile']) ? $retour : $nb;
0 ignored issues
show
Bug introduced by
The variable $retour does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
Bug introduced by
The variable $nb does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
954
}
955
956
957
/**
958
 * Retourne si le moteur SQL prefere utiliser des transactions.
959
 *
960
 * @param 
961
 * @return bool true / false
962
**/
963
function spip_sqlite_preferer_transaction($serveur = '', $requeter = true) {
0 ignored issues
show
Unused Code introduced by
The parameter $serveur is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $requeter is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
964
	return true;
965
}
966
967
/**
968
 * Demarre une transaction.
969
 * Pratique pour des sql_updateq() dans un foreach,
970
 * parfois 100* plus rapide s'ils sont nombreux en sqlite ! 
971
 *
972
**/
973
function spip_sqlite_demarrer_transaction($serveur = '', $requeter = true) {
974
	if (!$requeter) return "BEGIN TRANSACTION";
975
	spip_sqlite::demarrer_transaction($serveur);
976
	return true;
977
}
978
979
/**
980
 * Cloture une transaction.
981
 *
982
**/
983
function spip_sqlite_terminer_transaction($serveur = '', $requeter = true) {
984
	if (!$requeter) return "COMMIT";
985
	spip_sqlite::finir_transaction($serveur);
986
	return true;
987
}
988
989
990
// http://doc.spip.org/@spip_sqlite_listdbs
991
function spip_sqlite_listdbs($serveur = '', $requeter = true){
0 ignored issues
show
Unused Code introduced by
The parameter $serveur is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $requeter is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
992
	_sqlite_init();
993
994
	if (!is_dir($d = substr(_DIR_DB, 0, -1))){
995
		return array();
996
	}
997
998
	include_spip('inc/flock');
999
	$bases = preg_files($d, $pattern = '(.*)\.sqlite$');
1000
	$bds = array();
1001
1002
	foreach ($bases as $b){
1003
		// pas de bases commencant pas sqlite 
1004
		// (on s'en sert pour l'installation pour simuler la presence d'un serveur)
1005
		// les bases sont de la forme _sqliteX_tmp_spip_install.sqlite
1006
		if (strpos($b, '_sqlite')) continue;
1007
		$bds[] = preg_replace(";.*/$pattern;iS", '$1', $b);
1008
	}
1009
1010
	return $bds;
1011
}
1012
1013
1014
// http://doc.spip.org/@spip_sqlite_multi
1015
function spip_sqlite_multi($objet, $lang){
1016
	$r = "EXTRAIRE_MULTI(" . $objet . ", '" . $lang . "') AS multi";
1017
	return $r;
1018
}
1019
1020
1021
/**
1022
 * Optimise une table SQL
1023
 * Note: Sqlite optimise TOUTE un fichier sinon rien.
1024
 * On evite donc 2 traitements sur la meme base dans un hit.
1025
 *
1026
 * @param $table nom de la table a optimiser
1027
 * @param $serveur nom de la connexion
1028
 * @param $requeter effectuer la requete ? sinon retourner son code
1029
 * @return bool|string true / false / requete
1030
 **/
1031
// http://doc.spip.org/@spip_sqlite_optimize
1032
function spip_sqlite_optimize($table, $serveur = '', $requeter = true){
0 ignored issues
show
Unused Code introduced by
The parameter $table is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1033
	static $do = false;
1034
	if ($requeter and $do){
1035
		return true;
1036
	}
1037
	if ($requeter){
1038
		$do = true;
1039
	}
1040
	return spip_sqlite_query("VACUUM", $serveur, $requeter);
1041
}
1042
1043
1044
//
1045
/**
1046
 * echapper une valeur selon son type ou au mieux
1047
 * comme le fait _q() mais pour sqlite avec ses specificites
1048
 *
1049
 * @param string|array|number $v
1050
 * @param string $type
1051
 * @return string|number
1052
 */
1053 View Code Duplication
function spip_sqlite_quote($v, $type = ''){
0 ignored issues
show
Duplication introduced by
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1054
		if (!is_array($v))
1055
			return _sqlite_calculer_cite($v,$type);
1056
		// si c'est un tableau, le parcourir en propageant le type
1057
		foreach($v as $k=>$r)
1058
			$v[$k] = spip_sqlite_quote($r, $type);
1059
		return join(",", $v);
1060
}
1061
1062
1063
/**
1064
 * Tester si une date est proche de la valeur d'un champ
1065
 *
1066
 * @param string $champ le nom du champ a tester
1067
 * @param int $interval valeur de l'interval : -1, 4, ...
1068
 * @param string $unite utite utilisee (DAY, MONTH, YEAR, ...)
1069
 * @return string expression SQL
1070
 **/
1071
function spip_sqlite_date_proche($champ, $interval, $unite){
1072
	$op = (($interval <= 0) ? '>' : '<');
1073
	return "($champ $op datetime('".date("Y-m-d H:i:s")."', '$interval $unite'))";
1074
}
1075
1076
1077
// http://doc.spip.org/@spip_sqlite_replace
1078
function spip_sqlite_replace($table, $couples, $desc = array(), $serveur = '', $requeter = true){
0 ignored issues
show
Unused Code introduced by
The parameter $requeter is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1079
	if (!$desc) $desc = description_table($table, $serveur);
0 ignored issues
show
Bug Best Practice introduced by
The expression $desc of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
1080
	if (!$desc) die("$table insertion sans description");
0 ignored issues
show
Coding Style Compatibility introduced by
The function spip_sqlite_replace() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
1081
	$fields = isset($desc['field']) ? $desc['field'] : array();
1082
1083
	foreach ($couples as $champ => $val){
1084
		$couples[$champ] = _sqlite_calculer_cite($val, $fields[$champ]);
1085
	}
1086
1087
	// recherche de champs 'timestamp' pour mise a jour auto de ceux-ci
1088
	$couples = _sqlite_ajouter_champs_timestamp($table, $couples, $desc, $serveur);
1089
1090
	return spip_sqlite_query("REPLACE INTO $table (".join(',', array_keys($couples)).') VALUES ('.join(',', $couples).')', $serveur);
1091
}
1092
1093
1094
// http://doc.spip.org/@spip_sqlite_replace_multi
1095
function spip_sqlite_replace_multi($table, $tab_couples, $desc = array(), $serveur = '', $requeter = true){
1096
1097
	// boucler pour trainter chaque requete independemment
1098
	foreach ($tab_couples as $couples){
1099
		$retour = spip_sqlite_replace($table, $couples, $desc, $serveur, $requeter);
1100
	}
1101
	// renvoie le dernier id	
1102
	return $retour;
0 ignored issues
show
Bug introduced by
The variable $retour does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
1103
}
1104
1105
1106
// http://doc.spip.org/@spip_sqlite_select
1107
function spip_sqlite_select($select, $from, $where = '', $groupby = '', $orderby = '', $limit = '', $having = '', $serveur = '', $requeter = true){
1108
1109
	// version() n'est pas connu de sqlite
1110
	$select = str_replace('version()', 'sqlite_version()', $select);
1111
1112
	// recomposer from
1113
	$from = (!is_array($from) ? $from : _sqlite_calculer_select_as($from));
1114
1115
	$query =
1116
		_sqlite_calculer_expression('SELECT', $select, ', ')
1117
		._sqlite_calculer_expression('FROM', $from, ', ')
1118
		._sqlite_calculer_expression('WHERE', $where)
1119
		._sqlite_calculer_expression('GROUP BY', $groupby, ',')
1120
		._sqlite_calculer_expression('HAVING', $having)
1121
		.($orderby ? ("\nORDER BY "._sqlite_calculer_order($orderby)) : '')
1122
		.($limit ? "\nLIMIT $limit" : '');
1123
1124
	// dans un select, on doit renvoyer la requête en cas d'erreur
1125
	$res = spip_sqlite_query($query, $serveur, $requeter);
1126
	// texte de la requete demande ?
1127
	if (!$requeter) return $res;
1128
	// erreur survenue ?
1129
	if ($res === false) {
1130
		return spip_sqlite::traduire_requete($query, $serveur);
1131
	}
1132
	return $res;
1133
}
1134
1135
1136
/**
1137
 * Selectionne un fichier de base de donnees
1138
 *
1139
 * @param string $nom
0 ignored issues
show
Bug introduced by
There is no parameter named $nom. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
1140
 * 		Nom de la base a utiliser
1141
 * @param string $serveur
1142
 * 		Nom du connecteur
1143
 * @param bool $requeter
1144
 * 		Inutilise
1145
 * 
1146
 * @return bool|string
1147
 * 		Nom de la base en cas de success.
1148
 * 		False en cas d'erreur.
1149
**/
1150
function spip_sqlite_selectdb($db, $serveur = '', $requeter = true){
0 ignored issues
show
Unused Code introduced by
The parameter $requeter is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1151
	_sqlite_init();
1152
1153
	// interdire la creation d'une nouvelle base, 
1154
	// sauf si on est dans l'installation
1155
	if (!is_file($f = _DIR_DB.$db.'.sqlite')
1156
	    && (!defined('_ECRIRE_INSTALL') || !_ECRIRE_INSTALL)){
1157
		spip_log("Il est interdit de creer la base $db", 'sqlite.'._LOG_HS);
1158
		return false;
1159
	}
1160
1161
	// se connecter a la base indiquee
1162
	// avec les identifiants connus
1163
	$index = $serveur ? $serveur : 0;
1164
1165
	if ($link = spip_connect_db('', '', '', '', '@selectdb@'.$db, $serveur, '', '')){
1166 View Code Duplication
		if (($db==$link['db']) && $GLOBALS['connexions'][$index] = $link)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1167
			return $db;
1168
	} else {
1169
		spip_log("Impossible de selectionner la base $db", 'sqlite.'._LOG_HS);
1170
		return false;
1171
	}
1172
1173
}
1174
1175
1176
// http://doc.spip.org/@spip_sqlite_set_charset
1177
function spip_sqlite_set_charset($charset, $serveur = '', $requeter = true){
0 ignored issues
show
Unused Code introduced by
The parameter $charset is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $serveur is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $requeter is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1178
	# spip_log("Gestion charset sql a ecrire : "."SET NAMES "._q($charset), 'sqlite.'._LOG_ERREUR);
1179
	# return spip_sqlite_query("SET NAMES ". spip_sqlite_quote($charset), $serveur); //<-- Passe pas !
1180
}
1181
1182
1183
/**
1184
 * Retourne une ressource de la liste des tables de la base de données 
1185
 *
1186
 * @param string $match
1187
 *     Filtre sur tables à récupérer
1188
 * @param string $serveur
1189
 *     Connecteur de la base
1190
 * @param bool $requeter
1191
 *     true pour éxecuter la requête
1192
 *     false pour retourner le texte de la requête.
1193
 * @return ressource
0 ignored issues
show
Documentation introduced by
Should the return type not be boolean|SQLiteResult|string?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
1194
 *     Ressource à utiliser avec sql_fetch()
1195
**/
1196
function spip_sqlite_showbase($match, $serveur = '', $requeter = true){
1197
	// type est le type d'entrée : table / index / view
1198
	// on ne retourne que les tables (?) et non les vues...
1199
	# ESCAPE non supporte par les versions sqlite <3
1200
	#	return spip_sqlite_query("SELECT name FROM sqlite_master WHERE type='table' AND tbl_name LIKE "._q($match)." ESCAPE '\'", $serveur, $requeter);
1201
	$match = preg_quote($match);
1202
	$match = str_replace("\\\_", "[[TIRETBAS]]", $match);
1203
	$match = str_replace("\\\%", "[[POURCENT]]", $match);
1204
	$match = str_replace("_", ".", $match);
1205
	$match = str_replace("%", ".*", $match);
1206
	$match = str_replace("[[TIRETBAS]]", "_", $match);
1207
	$match = str_replace("[[POURCENT]]", "%", $match);
1208
	$match = "^$match$";
1209
	return spip_sqlite_query("SELECT name FROM sqlite_master WHERE type='table' AND tbl_name REGEXP "._q($match), $serveur, $requeter);
1210
}
1211
1212
define('_SQLITE_RE_SHOW_TABLE', '/^[^(),]*\(((?:[^()]*\((?:[^()]*\([^()]*\))?[^()]*\)[^()]*)*[^()]*)\)[^()]*$/');
1213
// http://doc.spip.org/@spip_sqlite_showtable
1214
function spip_sqlite_showtable($nom_table, $serveur = '', $requeter = true){
1215
	$query =
1216
		'SELECT sql, type FROM'
1217
		.' (SELECT * FROM sqlite_master UNION ALL'
1218
		.' SELECT * FROM sqlite_temp_master)'
1219
		." WHERE tbl_name LIKE '$nom_table'"
1220
		." AND type!='meta' AND sql NOT NULL AND name NOT LIKE 'sqlite_%'"
1221
		.' ORDER BY substr(type,2,1), name';
1222
1223
	$a = spip_sqlite_query($query, $serveur, $requeter);
1224
	if (!$a) return "";
1225
	if (!$requeter) return $a;
1226
	if (!($a = spip_sqlite_fetch($a, null, $serveur))) return "";
1227
	$vue = ($a['type']=='view'); // table | vue
1228
1229
	// c'est une table
1230
	// il faut parser le create
1231
	if (!$vue){
1232
		if (!preg_match(_SQLITE_RE_SHOW_TABLE, array_shift($a), $r)){
1233
			return "";
1234
		} else {
1235
			$desc = $r[1];
1236
			// extraction d'une KEY éventuelle en prenant garde de ne pas
1237
			// relever un champ dont le nom contient KEY (ex. ID_WHISKEY)
1238 View Code Duplication
			if (preg_match("/^(.*?),([^,]*\sKEY[ (].*)$/s", $desc, $r)){
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1239
				$namedkeys = $r[2];
1240
				$desc = $r[1];
1241
			}
1242
			else
1243
				$namedkeys = "";
1244
1245
			$fields = array();
1246
			$keys   = array();
1247
1248
			// enlever les contenus des valeurs DEFAULT 'xxx' qui pourraient perturber
1249
			// par exemple s'il contiennent une virgule.
1250
			// /!\ cela peut aussi echapper le nom des champs si la table a eu des operations avec SQLite Manager !
1251
			list($desc, $echaps) = query_echappe_textes($desc);
1252
1253
			// separer toutes les descriptions de champs, separes par des virgules
1254
			# /!\ explode peut exploser aussi DECIMAL(10,2) !
1255
			$k_precedent = null;
1256
			foreach (explode(",", $desc) as $v){
1257
1258
				preg_match("/^\s*([^\s]+)\s+(.*)/", $v, $r);
1259
				// Les cles de champs peuvent etre entourees
1260
				// de guillements doubles " , simples ', graves ` ou de crochets [ ],  ou rien.
1261
				// http://www.sqlite.org/lang_keywords.html
1262
				$k = strtolower(query_reinjecte_textes($r[1], $echaps)); // champ, "champ", [champ]...
0 ignored issues
show
Security Bug introduced by
It seems like $echaps can also be of type false; however, query_reinjecte_textes() does only seem to accept array, did you maybe forget to handle an error condition?
Loading history...
1263
				if ($char = strpbrk($k[0], '\'"[`')) {
1264
					$k = trim($k, $char);
1265
					if ($char == '[') $k = rtrim($k, ']');
1266
				}
1267
				$def = query_reinjecte_textes($r[2], $echaps); // valeur du champ
0 ignored issues
show
Security Bug introduced by
It seems like $echaps can also be of type false; however, query_reinjecte_textes() does only seem to accept array, did you maybe forget to handle an error condition?
Loading history...
1268
1269
				# rustine pour DECIMAL(10,2)
1270
				if (false !== strpos($k, ')')) {
1271
					$fields[$k_precedent] .= ',' . $k . ' ' . $def;
1272
					continue;
1273
				}
1274
				
1275
				$fields[$k] = $def;
1276
				$k_precedent = $k;
1277
				
1278
				// la primary key peut etre dans une des descriptions de champs
1279
				// et non en fin de table, cas encore decouvert avec Sqlite Manager
1280
				if (stripos($r[2], 'PRIMARY KEY') !== false) {
1281
					$keys['PRIMARY KEY'] = $k;
1282
				}
1283
			}
1284
			// key inclues dans la requete
1285 View Code Duplication
			foreach(preg_split('/\)\s*(,|$)/',$namedkeys) as $v) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1286
				if (preg_match("/^\s*([^(]*)\(([^(]*(\(\d+\))?)$/",$v,$r)) {
1287
					$k = str_replace("`", '', trim($r[1]));
1288
					$t = trim(strtolower(str_replace("`", '', $r[2])), '"');
1289
					if ($k && !isset($keys[$k])) $keys[$k] = $t; else $keys[] = $t;
1290
				}
1291
			}
1292
			// sinon ajouter les key index
1293
			$query =
1294
				'SELECT name,sql FROM'
1295
				.' (SELECT * FROM sqlite_master UNION ALL'
1296
				.' SELECT * FROM sqlite_temp_master)'
1297
				." WHERE tbl_name LIKE '$nom_table'"
1298
				." AND type='index' AND name NOT LIKE 'sqlite_%'"
1299
				.'ORDER BY substr(type,2,1), name';
1300
			$a = spip_sqlite_query($query, $serveur, $requeter);
1301
			while ($r = spip_sqlite_fetch($a, null, $serveur)){
1302
				$key = str_replace($nom_table.'_', '', $r['name']); // enlever le nom de la table ajoute a l'index
1303
				$colonnes = preg_replace(',.*\((.*)\).*,', '$1', $r['sql']);
1304
				$keys['KEY '.$key] = $colonnes;
1305
			}
1306
		}
1307
	}
1308
	// c'est une vue, on liste les champs disponibles simplement
1309
	else {
1310
		if ($res = sql_fetsel('*', $nom_table, '', '', '', '1', '', $serveur)){ // limit 1
1311
			$fields = array();
1312
			foreach ($res as $c => $v) $fields[$c] = '';
1313
			$keys = array();
1314
		} else {
1315
			return "";
1316
		}
1317
	}
1318
	return array('field' => $fields, 'key' => $keys);
1319
1320
}
1321
1322
1323
// http://doc.spip.org/@spip_sqlite_update
1324
function spip_sqlite_update($table, $champs, $where = '', $desc = '', $serveur = '', $requeter = true){
1325
	// recherche de champs 'timestamp' pour mise a jour auto de ceux-ci
1326
	$champs = _sqlite_ajouter_champs_timestamp($table, $champs, $desc, $serveur);
1327
1328
	$set = array();
1329
	foreach ($champs as $champ => $val)
1330
		$set[] = $champ."=$val";
1331 View Code Duplication
	if (!empty($set))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1332
		return spip_sqlite_query(
1333
			_sqlite_calculer_expression('UPDATE', $table, ',')
1334
			._sqlite_calculer_expression('SET', $set, ',')
1335
			._sqlite_calculer_expression('WHERE', $where),
1336
			$serveur, $requeter);
1337
}
1338
1339
1340
// http://doc.spip.org/@spip_sqlite_updateq
1341
function spip_sqlite_updateq($table, $champs, $where = '', $desc = array(), $serveur = '', $requeter = true){
1342
1343
	if (!$champs) return;
1344
	if (!$desc) $desc = description_table($table, $serveur);
0 ignored issues
show
Bug Best Practice introduced by
The expression $desc of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
1345
	if (!$desc) die("$table insertion sans description");
0 ignored issues
show
Coding Style Compatibility introduced by
The function spip_sqlite_updateq() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
1346
	$fields = $desc['field'];
1347
1348
	// recherche de champs 'timestamp' pour mise a jour auto de ceux-ci
1349
	$champs = _sqlite_ajouter_champs_timestamp($table, $champs, $desc, $serveur);
1350
1351
	$set = array();
1352
	foreach ($champs as $champ => $val){
1353
		$set[] = $champ.'='._sqlite_calculer_cite($val, $fields[$champ]);
1354
	}
1355
	return spip_sqlite_query(
1356
		_sqlite_calculer_expression('UPDATE', $table, ',')
1357
		._sqlite_calculer_expression('SET', $set, ',')
1358
		._sqlite_calculer_expression('WHERE', $where),
1359
		$serveur, $requeter);
1360
}
1361
1362
1363
/*
1364
 * 
1365
 * Ensuite les fonctions non abstraites
1366
 * crees pour l'occasion de sqlite
1367
 * 
1368
 */
1369
1370
1371
/**
1372
 * fonction pour la premiere connexion a un serveur SQLite
1373
 * http://doc.spip.org/@_sqlite_init
1374
 *
1375
 * @return void
1376
 */
1377
function _sqlite_init(){
1378
	if (!defined('_DIR_DB')) define('_DIR_DB', _DIR_ETC.'bases/');
1379
	if (!defined('_SQLITE_CHMOD')) define('_SQLITE_CHMOD', _SPIP_CHMOD);
1380
1381
	if (!is_dir($d = _DIR_DB)){
1382
		include_spip('inc/flock');
1383
		sous_repertoire($d);
1384
	}
1385
}
1386
1387
1388
/**
1389
 * teste la version sqlite du link en cours
1390
 * http://doc.spip.org/@_sqlite_is_version
1391
 *
1392
 * @param string $version
1393
 * @param string $link
1394
 * @param string $serveur
1395
 * @param bool $requeter
1396
 * @return bool|int
1397
 */
1398
function _sqlite_is_version($version = '', $link = '', $serveur = '', $requeter = true){
0 ignored issues
show
Unused Code introduced by
The parameter $requeter is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1399
	if ($link==='') $link = _sqlite_link($serveur);
1400
	if (!$link) return false;
1401
	if ($link instanceof PDO){
1402
		$v = 3;
1403
	} else {
1404
		$v = 2;
1405
	}
1406
1407
	if (!$version) return $v;
1408
	return ($version==$v);
1409
}
1410
1411
1412
/**
1413
 * retrouver un link
1414
 * http://doc.spip.org/@_sqlite_link
1415
 *
1416
 * @param string $serveur
1417
 * @return
1418
 */
1419
function _sqlite_link($serveur = ''){
1420
	$link = &$GLOBALS['connexions'][$serveur ? $serveur : 0]['link'];
1421
	return $link;
1422
}
1423
1424
1425
/* ordre alphabetique pour les autres */
1426
1427
1428
/**
1429
 * renvoie les bons echappements (pas sur les fonctions now())
1430
 * http://doc.spip.org/@_sqlite_calculer_cite
1431
 *
1432
 * @param string|array|number $v
1433
 * @param string $type
1434
 * @return string|array|number
1435
 */
1436
function _sqlite_calculer_cite($v, $type){
1437
	if ($type){
1438
		if(is_null($v)
1439
			AND stripos($type,"NOT NULL")===false) return 'NULL'; // null php se traduit en NULL SQL
1440
1441
		if (sql_test_date($type) AND preg_match('/^\w+\(/', $v))
1442
			return $v;
1443
		if (sql_test_int($type)){
1444
			if (is_numeric($v))
1445
				return $v;
1446
			elseif (ctype_xdigit(substr($v, 2)) AND strncmp($v, '0x', 2)==0)
1447
				return hexdec(substr($v, 2));
1448
			else
1449
				return intval($v);
1450
		}
1451
	}
1452
	else {
1453
		// si on ne connait pas le type on le deduit de $v autant que possible
1454
		if (is_numeric($v))
1455
			return strval($v);
1456
	}
1457
1458
	if (function_exists('sqlite_escape_string')){
1459
		return "'".sqlite_escape_string($v)."'";
1460
	}
1461
1462
	// trouver un link sqlite3 pour faire l'echappement
1463
	foreach ($GLOBALS['connexions'] as $s){
1464
		if (_sqlite_is_version(3, $l = $s['link'])){
1465
			return $l->quote($v);
1466
		}
1467
	}
1468
1469
	// echapper les ' en ''
1470
	spip_log("Pas de methode sqlite_escape_string ni ->quote pour echapper","sqlite."._LOG_INFO_IMPORTANTE);
1471
	return  ("'" . str_replace("'","''",$v) . "'");
1472
}
1473
1474
1475
/**
1476
 * renvoie grosso modo "$expression join($join, $v)"
1477
 * http://doc.spip.org/@_sqlite_calculer_expression
1478
 *
1479
 * @param  $expression
1480
 * @param  $v
1481
 * @param string $join
1482
 * @return string
1483
 */
1484 View Code Duplication
function _sqlite_calculer_expression($expression, $v, $join = 'AND'){
0 ignored issues
show
Duplication introduced by
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1485
	if (empty($v))
1486
		return '';
1487
1488
	$exp = "\n$expression ";
1489
1490
	if (!is_array($v)){
1491
		return $exp.$v;
1492
	} else {
1493
		if (strtoupper($join)==='AND')
1494
			return $exp.join("\n\t$join ", array_map('_sqlite_calculer_where', $v));
1495
		else
1496
			return $exp.join($join, $v);
1497
	}
1498
}
1499
1500
1501
/**
1502
 * pour conversion 0+x ? (pas la peine en sqlite)
1503
 * http://doc.spip.org/@_sqlite_calculer_order
1504
 *
1505
 * @param  $orderby
1506
 * @return string
1507
 */
1508
function _sqlite_calculer_order($orderby){
1509
	return (is_array($orderby)) ? join(", ", $orderby) : $orderby;
1510
}
1511
1512
1513
// renvoie des 'nom AS alias' 
1514
// http://doc.spip.org/@_sqlite_calculer_select_as
1515 View Code Duplication
function _sqlite_calculer_select_as($args){
0 ignored issues
show
Duplication introduced by
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1516
	$res = '';
1517
	foreach ($args as $k => $v){
1518
		if (substr($k, -1)=='@'){
1519
			// c'est une jointure qui se refere au from precedent
1520
			// pas de virgule
1521
			$res .= '  '.$v;
1522
		}
1523
		else {
1524
			if (!is_numeric($k)){
1525
				$p = strpos($v, " ");
1526
				if ($p)
1527
					$v = substr($v, 0, $p)." AS '$k'".substr($v, $p);
1528
				else $v .= " AS '$k'";
1529
			}
1530
			$res .= ', '.$v;
1531
		}
1532
	}
1533
	return substr($res, 2);
1534
}
1535
1536
1537
/**
1538
 * renvoie les bonnes parentheses pour des where imbriquees
1539
 * http://doc.spip.org/@_sqlite_calculer_where
1540
 *
1541
 * @param  $v
1542
 * @return array|mixed|string
1543
 */
1544 View Code Duplication
function _sqlite_calculer_where($v){
0 ignored issues
show
Duplication introduced by
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1545
	if (!is_array($v))
1546
		return $v;
1547
1548
	$op = array_shift($v);
1549
	if (!($n = count($v)))
1550
		return $op;
1551
	else {
1552
		$arg = _sqlite_calculer_where(array_shift($v));
1553
		if ($n==1){
1554
			return "$op($arg)";
1555
		} else {
1556
			$arg2 = _sqlite_calculer_where(array_shift($v));
1557
			if ($n==2){
1558
				return "($arg $op $arg2)";
1559
			} else return "($arg $op ($arg2) : $v[0])";
1560
		}
1561
	}
1562
}
1563
1564
1565
/**
1566
 * Charger les modules sqlite (si possible) (juste la version demandee),
1567
 * ou, si aucune version, renvoie les versions sqlite dispo
1568
 * sur ce serveur dans un array
1569
 *
1570
 * http://doc.spip.org/@_sqlite_charger_version
1571
 *
1572
 * @param string $version
1573
 * @return array|bool
1574
 */
1575
function _sqlite_charger_version($version = ''){
1576
	$versions = array();
1577
1578
	// version 2
1579
	if (!$version || $version==2){
1580
		if (charger_php_extension('sqlite')){
1581
			$versions[] = 2;
1582
		}
1583
	}
1584
1585
	// version 3
1586
	if (!$version || $version==3){
1587
		if (charger_php_extension('pdo') && charger_php_extension('pdo_sqlite')){
1588
			$versions[] = 3;
1589
		}
1590
	}
1591
	if ($version) return in_array($version, $versions);
1592
	return $versions;
1593
}
1594
1595
1596
/**
1597
 * Gestion des requetes ALTER non reconnues de SQLite :
1598
 * ALTER TABLE table DROP column
1599
 * ALTER TABLE table CHANGE [COLUMN] columnA columnB definition
1600
 * ALTER TABLE table MODIFY column definition
1601
 * ALTER TABLE table ADD|DROP PRIMARY KEY
1602
 *
1603
 * (MODIFY transforme en CHANGE columnA columnA) par spip_sqlite_alter()
1604
 *
1605
 * 1) creer une table B avec le nouveau format souhaite
1606
 * 2) copier la table d'origine A vers B
1607
 * 3) supprimer la table A
1608
 * 4) renommer la table B en A
1609
 * 5) remettre les index (qui sont supprimes avec la table A)
1610
 *
1611
 * http://doc.spip.org/@_sqlite_modifier_table
1612
 *
1613
 * @param string/array $table : nom_table, array(nom_table=>nom_futur)
0 ignored issues
show
Documentation introduced by
The doc-type string/array could not be parsed: Unknown type name "string/array" at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
1614
 * @param string/array $col : nom_colonne, array(nom_colonne=>nom_futur)
0 ignored issues
show
Documentation introduced by
The doc-type string/array could not be parsed: Unknown type name "string/array" at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
Bug introduced by
There is no parameter named $col. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
1615
 * @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', ...))
1616
 * @param string $serveur : nom de la connexion sql en cours
1617
 *
1618
 */
1619
function _sqlite_modifier_table($table, $colonne, $opt = array(), $serveur = ''){
1620
1621
	if (is_array($table)){
1622
		reset($table);
1623
		list($table_origine,$table_destination) = each($table);
1624
	} else {
1625
		$table_origine = $table_destination = $table;
1626
	}
1627
	// ne prend actuellement qu'un changement
1628
	// mais pourra etre adapte pour changer plus qu'une colonne a la fois
1629
	if (is_array($colonne)){
1630
		reset($colonne);
1631
		list($colonne_origine,$colonne_destination) = each($colonne);
1632
	} else {
1633
		$colonne_origine = $colonne_destination = $colonne;
1634
	}
1635
	if (!isset($opt['field'])) $opt['field'] = array();
1636
	if (!isset($opt['key'])) $opt['key'] = array();
1637
1638
	// si les noms de tables sont differents, pas besoin de table temporaire
1639
	// on prendra directement le nom de la future table
1640
	$meme_table = ($table_origine==$table_destination);
1641
1642
	$def_origine = sql_showtable($table_origine, false, $serveur);
1643 View Code Duplication
	if (!$def_origine OR !isset($def_origine['field'])){
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1644
		spip_log("Alter table impossible sur $table_origine : table non trouvee",'sqlite'._LOG_ERREUR);
1645
		return false;
1646
	}
1647
1648
1649
	$table_tmp = $table_origine.'_tmp';
1650
1651
	// 1) creer une table temporaire avec les modifications	
1652
	// - DROP : suppression de la colonne
1653
	// - CHANGE : modification de la colonne
1654
	// (foreach pour conserver l'ordre des champs)
1655
1656
	// field 
1657
	$fields = array();
1658
	// pour le INSERT INTO plus loin
1659
	// stocker la correspondance nouvelles->anciennes colonnes
1660
	$fields_correspondances = array();
1661
	foreach ($def_origine['field'] as $c => $d){
1662
1663
		if ($colonne_origine && ($c==$colonne_origine)){
1664
			// si pas DROP
1665
			if ($colonne_destination){
1666
				$fields[$colonne_destination] = $opt['field'][$colonne_destination];
1667
				$fields_correspondances[$colonne_destination] = $c;
1668
			}
1669
		} else {
1670
			$fields[$c] = $d;
1671
			$fields_correspondances[$c] = $c;
1672
		}
1673
	}
1674
	// cas de ADD sqlite2 (ajout du champ en fin de table):
1675
	if (!$colonne_origine && $colonne_destination){
1676
		$fields[$colonne_destination] = $opt['field'][$colonne_destination];
1677
	}
1678
1679
	// key...
1680
	$keys = array();
1681
	foreach ($def_origine['key'] as $c => $d){
1682
		$c = str_replace($colonne_origine, $colonne_destination, $c);
1683
		$d = str_replace($colonne_origine, $colonne_destination, $d);
1684
		// seulement si on ne supprime pas la colonne !
1685
		if ($d)
1686
			$keys[$c] = $d;
1687
	}
1688
1689
	// autres keys, on merge
1690
	$keys = array_merge($keys, $opt['key']);
1691
	$queries = array();
1692
1693
	// copier dans destination (si differente de origine), sinon tmp
1694
	$table_copie = ($meme_table) ? $table_tmp : $table_destination;
1695
	$autoinc = (isset($keys['PRIMARY KEY'])
1696
					AND stripos($keys['PRIMARY KEY'],',')===false
1697
					AND stripos($fields[$keys['PRIMARY KEY']],'default')===false);
1698
1699
	if ($q = _sqlite_requete_create(
1700
		$table_copie,
1701
		$fields,
1702
		$keys,
1703
		$autoinc,
1704
		$temporary = false,
1705
		$ifnotexists = true,
1706
		$serveur)){
1707
		$queries[] = $q;
1708
	}
1709
1710
1711
	// 2) y copier les champs qui vont bien
1712
	$champs_dest = join(', ', array_keys($fields_correspondances));
1713
	$champs_ori = join(', ', $fields_correspondances);
1714
	$queries[] = "INSERT INTO $table_copie ($champs_dest) SELECT $champs_ori FROM $table_origine";
1715
1716
	// 3) supprimer la table d'origine
1717
	$queries[] = "DROP TABLE $table_origine";
1718
1719
	// 4) renommer la table temporaire 
1720
	// avec le nom de la table destination
1721
	// si necessaire
1722
	if ($meme_table){
1723
		if (_sqlite_is_version(3, '', $serveur)){
1724
			$queries[] = "ALTER TABLE $table_copie RENAME TO $table_destination";
1725
		} else {
1726
			$queries[] = _sqlite_requete_create(
1727
				$table_destination,
1728
				$fields,
1729
				$keys,
1730
				$autoinc,
1731
				$temporary = false,
1732
				$ifnotexists = false, // la table existe puisqu'on est dans une transaction
1733
				$serveur);
1734
			$queries[] = "INSERT INTO $table_destination SELECT * FROM $table_copie";
1735
			$queries[] = "DROP TABLE $table_copie";
1736
		}
1737
	}
1738
1739
	// 5) remettre les index !
1740
	foreach ($keys as $k => $v){
1741 View Code Duplication
		if ($k=='PRIMARY KEY'){
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1742
		}
1743
		else {
1744
			// enlever KEY
1745
			$k = substr($k, 4);
1746
			$queries[] = "CREATE INDEX $table_destination"."_$k ON $table_destination ($v)";
1747
		}
1748
	}
1749
1750
1751
	if (count($queries)){
1752
		spip_sqlite::demarrer_transaction($serveur);
1753
		// il faut les faire une par une car $query = join('; ', $queries).";"; ne fonctionne pas
1754
		foreach ($queries as $q){
1755
			if (!spip_sqlite::executer_requete($q, $serveur)){
1756
				spip_log(_LOG_GRAVITE_ERREUR, "SQLite : ALTER TABLE table :"
1757
																			." Erreur a l'execution de la requete : $q", 'sqlite');
0 ignored issues
show
Unused Code introduced by
The call to spip_log() has too many arguments starting with 'sqlite'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
1758
				spip_sqlite::annuler_transaction($serveur);
1759
				return false;
1760
			}
1761
		}
1762
		spip_sqlite::finir_transaction($serveur);
1763
	}
1764
1765
	return true;
1766
}
1767
1768
1769
/**
1770
 * Nom des fonctions
1771
 * http://doc.spip.org/@_sqlite_ref_fonctions
1772
 *
1773
 * @return array
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use array<string,string|arra...,array<string,string>>>.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
1774
 */
1775
function _sqlite_ref_fonctions(){
1776
	$fonctions = array(
1777
		'alter' => 'spip_sqlite_alter',
1778
		'count' => 'spip_sqlite_count',
1779
		'countsel' => 'spip_sqlite_countsel',
1780
		'create' => 'spip_sqlite_create',
1781
		'create_base' => 'spip_sqlite_create_base',
1782
		'create_view' => 'spip_sqlite_create_view',
1783
		'date_proche' => 'spip_sqlite_date_proche',
1784
		'delete' => 'spip_sqlite_delete',
1785
		'drop_table' => 'spip_sqlite_drop_table',
1786
		'drop_view' => 'spip_sqlite_drop_view',
1787
		'errno' => 'spip_sqlite_errno',
1788
		'error' => 'spip_sqlite_error',
1789
		'explain' => 'spip_sqlite_explain',
1790
		'fetch' => 'spip_sqlite_fetch',
1791
		'seek' => 'spip_sqlite_seek',
1792
		'free' => 'spip_sqlite_free',
1793
		'hex' => 'spip_sqlite_hex',
1794
		'in' => 'spip_sqlite_in',
1795
		'insert' => 'spip_sqlite_insert',
1796
		'insertq' => 'spip_sqlite_insertq',
1797
		'insertq_multi' => 'spip_sqlite_insertq_multi',
1798
		'listdbs' => 'spip_sqlite_listdbs',
1799
		'multi' => 'spip_sqlite_multi',
1800
		'optimize' => 'spip_sqlite_optimize',
1801
		'query' => 'spip_sqlite_query',
1802
		'quote' => 'spip_sqlite_quote',
1803
		'replace' => 'spip_sqlite_replace',
1804
		'replace_multi' => 'spip_sqlite_replace_multi',
1805
		'select' => 'spip_sqlite_select',
1806
		'selectdb' => 'spip_sqlite_selectdb',
1807
		'set_charset' => 'spip_sqlite_set_charset',
1808
		'get_charset' => 'spip_sqlite_get_charset',
1809
		'showbase' => 'spip_sqlite_showbase',
1810
		'showtable' => 'spip_sqlite_showtable',
1811
		'update' => 'spip_sqlite_update',
1812
		'updateq' => 'spip_sqlite_updateq',
1813
		'preferer_transaction' => 'spip_sqlite_preferer_transaction',
1814
		'demarrer_transaction' => 'spip_sqlite_demarrer_transaction',
1815
		'terminer_transaction' => 'spip_sqlite_terminer_transaction',
1816
	);
1817
1818
	// association de chaque nom http d'un charset aux couples sqlite 
1819
	// SQLite supporte utf-8 et utf-16 uniquement.
1820
	$charsets = array(
1821
		'utf-8' => array('charset' => 'utf8', 'collation' => 'utf8_general_ci'),
1822
		//'utf-16be'=>array('charset'=>'utf16be','collation'=>'UTF-16BE'),// aucune idee de quoi il faut remplir dans es champs la
1823
		//'utf-16le'=>array('charset'=>'utf16le','collation'=>'UTF-16LE')
1824
	);
1825
1826
	$fonctions['charsets'] = $charsets;
1827
1828
	return $fonctions;
1829
}
1830
1831
1832
/**
1833
 * $query est une requete ou une liste de champs
1834
 * http://doc.spip.org/@_sqlite_remplacements_definitions_table
1835
 *
1836
 * @param  $query
1837
 * @param bool $autoinc
1838
 * @return mixed
1839
 */
1840
function _sqlite_remplacements_definitions_table($query, $autoinc = false){
1841
	// quelques remplacements
1842
	$num = "(\s*\([0-9]*\))?";
1843
	$enum = "(\s*\([^\)]*\))?";
1844
1845
	$remplace = array(
1846
		'/enum'.$enum.'/is' => 'VARCHAR(255)',
1847
		'/COLLATE \w+_bin/is' => 'COLLATE BINARY',
1848
		'/COLLATE \w+_ci/is' => 'COLLATE NOCASE',
1849
		'/auto_increment/is' => '',
1850
		'/(timestamp .* )ON .*$/is' => '\\1',
1851
		'/character set \w+/is' => '',
1852
		'/((big|small|medium|tiny)?int(eger)?)'.$num.'\s*unsigned/is' => '\\1 UNSIGNED',
1853
		'/(text\s+not\s+null(\s+collate\s+\w+)?)\s*$/is' => "\\1 DEFAULT ''",
1854
		'/((char|varchar)'.$num.'\s+not\s+null(\s+collate\s+\w+)?)\s*$/is' => "\\1 DEFAULT ''",
1855
		'/(datetime\s+not\s+null)\s*$/is' => "\\1 DEFAULT '0000-00-00 00:00:00'",
1856
		'/(date\s+not\s+null)\s*$/is' => "\\1 DEFAULT '0000-00-00'",
1857
	);
1858
1859
	// pour l'autoincrement, il faut des INTEGER NOT NULL PRIMARY KEY
1860
	$remplace_autocinc = array(
1861
		'/(big|small|medium|tiny)?int(eger)?'.$num.'/is' => 'INTEGER'
1862
	);
1863
	// pour les int non autoincrement, il faut un DEFAULT
1864
	$remplace_nonautocinc = array(
1865
		'/((big|small|medium|tiny)?int(eger)?'.$num.'\s+not\s+null)\s*$/is' => "\\1 DEFAULT 0",
1866
	);
1867
1868
	if (is_string($query)){
1869
		$query = preg_replace(array_keys($remplace), $remplace, $query);
1870
		if ($autoinc OR preg_match(',AUTO_INCREMENT,is',$query))
1871
			$query = preg_replace(array_keys($remplace_autocinc), $remplace_autocinc, $query);
1872
		else{
1873
			$query = preg_replace(array_keys($remplace_nonautocinc), $remplace_nonautocinc, $query);
1874
			$query = _sqlite_collate_ci($query);
1875
		}
1876
	}
1877
	elseif(is_array($query)){
1878
		foreach($query as $k=>$q) {
1879
			$ai = ($autoinc?$k==$autoinc:preg_match(',AUTO_INCREMENT,is',$q));
1880
			$query[$k] = preg_replace(array_keys($remplace), $remplace, $query[$k]);
1881
			if ($ai)
1882
				$query[$k] = preg_replace(array_keys($remplace_autocinc), $remplace_autocinc, $query[$k]);
1883
			else{
1884
				$query[$k] = preg_replace(array_keys($remplace_nonautocinc), $remplace_nonautocinc, $query[$k]);
1885
				$query[$k] = _sqlite_collate_ci($query[$k]);
1886
			}
1887
		}
1888
	}
1889
	return $query;
1890
}
1891
1892
/**
1893
 * Definir la collation d'un champ en fonction de si une collation est deja explicite
1894
 * et du par defaut que l'on veut NOCASE
1895
 * @param string $champ
1896
 * @return string
1897
 */
1898
function _sqlite_collate_ci($champ){
1899
	if (stripos($champ,"COLLATE")!==false)
1900
		return $champ;
1901
	if (stripos($champ,"BINARY")!==false)
1902
		return str_ireplace("BINARY","COLLATE BINARY",$champ);
1903
	if (preg_match(",^(char|varchar|(long|small|medium|tiny)?text),i",$champ))
1904
		return $champ . " COLLATE NOCASE";
1905
1906
	return $champ;
1907
}
1908
1909
1910
/**
1911
 * Creer la requete pour la creation d'une table
1912
 * retourne la requete pour utilisation par sql_create() et sql_alter()
1913
 *
1914
 * http://doc.spip.org/@_sqlite_requete_create
1915
 *
1916
 * @param  $nom
1917
 * @param  $champs
1918
 * @param  $cles
1919
 * @param bool $autoinc
1920
 * @param bool $temporary
1921
 * @param bool $_ifnotexists
1922
 * @param string $serveur
1923
 * @param bool $requeter
1924
 * @return bool|string
1925
 */
1926
function _sqlite_requete_create($nom, $champs, $cles, $autoinc = false, $temporary = false, $_ifnotexists = true, $serveur = '', $requeter = true){
0 ignored issues
show
Unused Code introduced by
The parameter $requeter is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1927
	$query = $keys = $s = $p = '';
0 ignored issues
show
Unused Code introduced by
$p is not used, you could remove the assignment.

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

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

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

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

Loading history...
1928
1929
	// certains plugins declarent les tables  (permet leur inclusion dans le dump)
1930
	// sans les renseigner (laisse le compilo recuperer la description)
1931
	if (!is_array($champs) || !is_array($cles))
1932
		return;
1933
1934
	// sqlite ne gere pas KEY tout court dans une requete CREATE TABLE
1935
	// il faut passer par des create index
1936
	// Il gere par contre primary key !
1937
	// Soit la PK est definie dans les cles, soit dans un champs
1938
	$c = ""; // le champ de cle primaire
0 ignored issues
show
Unused Code introduced by
$c is not used, you could remove the assignment.

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

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

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

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

Loading history...
1939
	if (!isset($cles[$pk = "PRIMARY KEY"]) OR !$c = $cles[$pk]){
1940
		foreach ($champs as $k => $v){
1941
			if (false!==stripos($v, $pk)){
1942
				$c = $k;
1943
				// on n'en a plus besoin dans field, vu que defini dans key
1944
				$champs[$k] = preg_replace("/$pk/is", '', $champs[$k]);
1945
				break;
1946
			}
1947
		}
1948
	}
1949
	if ($c) $keys = "\n\t\t$pk ($c)";
1950
	// Pas de DEFAULT 0 sur les cles primaires en auto-increment
1951
	if (isset($champs[$c])
1952
		AND stripos($champs[$c],"default 0")!==false){
1953
		$champs[$c] = trim(str_ireplace("default 0","",$champs[$c]));
1954
	}
1955
1956
	$champs = _sqlite_remplacements_definitions_table($champs, $autoinc?$c:false);
1957
	foreach ($champs as $k => $v){
1958
		$query .= "$s\n\t\t$k $v";
1959
		$s = ",";
1960
	}
1961
1962
	$ifnotexists = "";
1963
	if ($_ifnotexists){
1964
1965
		$version = spip_sqlite_fetch(spip_sqlite_query("select sqlite_version() AS sqlite_version",$serveur),'',$serveur);
1966
		if (!function_exists('spip_version_compare')) include_spip('plugins/installer');
1967
1968 View Code Duplication
		if ($version AND spip_version_compare($version['sqlite_version'],'3.3.0','>=')) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1969
			$ifnotexists = ' IF NOT EXISTS';
1970
		} else {
1971
			/* simuler le IF EXISTS - version 2 et sqlite < 3.3a */
1972
			$a = spip_sqlite_showtable($table, $serveur);
0 ignored issues
show
Bug introduced by
The variable $table does not exist. Did you forget to declare it?

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

Loading history...
1973
			if (isset($a['key']['KEY '.$nom])) return true;
1974
		}
1975
1976
	}
1977
1978
	$temporary = $temporary ? ' TEMPORARY' : '';
1979
	$q = "CREATE$temporary TABLE$ifnotexists $nom ($query".($keys ? ",$keys" : '').")\n";
1980
1981
	return $q;
1982
}
1983
1984
1985
/**
1986
 * Retrouver les champs 'timestamp'
1987
 * pour les ajouter aux 'insert' ou 'replace'
1988
 * afin de simuler le fonctionnement de mysql
1989
 *
1990
 * stocke le resultat pour ne pas faire
1991
 * de requetes showtable intempestives
1992
 *
1993
 * http://doc.spip.org/@_sqlite_ajouter_champs_timestamp
1994
 *
1995
 * @param  $table
1996
 * @param  $couples
1997
 * @param string $desc
1998
 * @param string $serveur
1999
 * @return
2000
 */
2001
function _sqlite_ajouter_champs_timestamp($table, $couples, $desc = '', $serveur = ''){
2002
	static $tables = array();
2003
2004
	if (!isset($tables[$table])){
2005
2006 View Code Duplication
		if (!$desc){
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2007
			$trouver_table = charger_fonction('trouver_table', 'base');
2008
			$desc = $trouver_table($table, $serveur);
2009
			// si pas de description, on ne fait rien, ou on die() ?
2010
			if (!$desc) return $couples;
2011
		}
2012
2013
		// recherche des champs avec simplement 'TIMESTAMP'
2014
		// cependant, il faudra peut etre etendre
2015
		// avec la gestion de DEFAULT et ON UPDATE
2016
		// mais ceux-ci ne sont pas utilises dans le core
2017
		$tables[$table] = array();
2018
2019
		foreach ($desc['field'] as $k => $v){
2020 View Code Duplication
			if (strpos(strtolower(ltrim($v)), 'timestamp')===0)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2021
				$tables[$table][$k] = "datetime('now')";
2022
		}
2023
	}
2024
2025
	// ajout des champs type 'timestamp' absents
2026
	return array_merge($tables[$table],$couples);
2027
}
2028
2029
2030
/**
2031
 * renvoyer la liste des versions sqlite disponibles
2032
 * sur le serveur
2033
 * http://doc.spip.org/@spip_versions_sqlite
2034
 *
2035
 * @return array|bool
2036
 */
2037
function spip_versions_sqlite(){
2038
	return _sqlite_charger_version();
2039
}
2040
2041
2042
class spip_sqlite {
2043
	static $requeteurs = array();
2044
	static $transaction_en_cours = array();
2045
2046
	function spip_sqlite(){}
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
2047
2048
	/**
2049
	 * Retourne une unique instance du requêteur
2050
	 *
2051
	 * Retourne une instance unique du requêteur pour une connexion SQLite
2052
	 * donnée
2053
	 *
2054
	 * @param string $serveur
2055
	 * 		Nom du connecteur
2056
	 * @return sqlite_requeteur
2057
	 * 		Instance unique du requêteur
2058
	**/
2059
	static function requeteur($serveur){
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
2060
		if (!isset(spip_sqlite::$requeteurs[$serveur]))
2061
			spip_sqlite::$requeteurs[$serveur] = new sqlite_requeteur($serveur);
2062
		return spip_sqlite::$requeteurs[$serveur];
2063
	}
2064
2065
	static function traduire_requete($query, $serveur){
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
2066
		$requeteur = spip_sqlite::requeteur($serveur);
2067
		$traducteur = new sqlite_traducteur($query, $requeteur->prefixe,$requeteur->sqlite_version);
2068
		return $traducteur->traduire_requete();
2069
	}
2070
2071
	static function demarrer_transaction($serveur){
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
2072
		spip_sqlite::executer_requete("BEGIN TRANSACTION",$serveur);
2073
		spip_sqlite::$transaction_en_cours[$serveur] = true;
2074
	}
2075
2076
	static function executer_requete($query, $serveur, $tracer=null){
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
2077
		$requeteur = spip_sqlite::requeteur($serveur);
2078
		return $requeteur->executer_requete($query, $tracer);
2079
	}
2080
2081
	static function last_insert_id($serveur){
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
2082
		$requeteur = spip_sqlite::requeteur($serveur);
2083
		return $requeteur->last_insert_id($serveur);
0 ignored issues
show
Unused Code introduced by
The call to sqlite_requeteur::last_insert_id() has too many arguments starting with $serveur.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
2084
	}
2085
2086
	static function annuler_transaction($serveur){
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
2087
		spip_sqlite::executer_requete("ROLLBACK",$serveur);
2088
		spip_sqlite::$transaction_en_cours[$serveur] = false;
2089
	}
2090
2091
	static function finir_transaction($serveur){
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
2092
		// si pas de transaction en cours, ne rien faire et le dire
2093
		if (!isset (spip_sqlite::$transaction_en_cours[$serveur])
2094
		  OR spip_sqlite::$transaction_en_cours[$serveur]==false)
2095
			return false;
2096
		// sinon fermer la transaction et retourner true
2097
		spip_sqlite::executer_requete("COMMIT",$serveur);
2098
		spip_sqlite::$transaction_en_cours[$serveur] = false;
2099
		return true;
2100
	}
2101
}
2102
2103
/*
2104
 * Classe pour partager les lancements de requete
2105
 * instanciee une fois par $serveur
2106
 * - peut corriger la syntaxe des requetes pour la conformite a sqlite
2107
 * - peut tracer les requetes
2108
 * 
2109
 */
2110
class sqlite_requeteur {
2111
	var $query = ''; // la requete
2112
	var $serveur = ''; // le serveur
2113
	var $link = ''; // le link (ressource) sqlite
2114
	var $prefixe = ''; // le prefixe des tables
2115
	var $db = ''; // le nom de la base 
2116
	var $tracer = false; // doit-on tracer les requetes (var_profile)
2117
2118
	var $sqlite_version = ''; // Version de sqlite (2 ou 3)
2119
2120
	/**
2121
	 * constructeur
2122
	 * http://doc.spip.org/@sqlite_traiter_requete
2123
	 *
2124
	 * @param  $query
2125
	 * @param string $serveur
2126
	 * @return bool
0 ignored issues
show
Documentation introduced by
Should the return type not be false|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
2127
	 */
2128
	function sqlite_requeteur($serveur = ''){
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
2129
		_sqlite_init();
2130
		$this->serveur = strtolower($serveur);
2131
2132
		if (!($this->link = _sqlite_link($this->serveur)) && (!defined('_ECRIRE_INSTALL') || !_ECRIRE_INSTALL)){
2133
			spip_log("Aucune connexion sqlite (link)", 'sqlite.'._LOG_ERREUR);
2134
			return false;
2135
		}
2136
2137
		$this->sqlite_version = _sqlite_is_version('', $this->link);
0 ignored issues
show
Documentation Bug introduced by
It seems like _sqlite_is_version('', $this->link) of type integer or boolean is incompatible with the declared type string of property $sqlite_version.

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

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

Loading history...
2138
2139
		$this->prefixe = $GLOBALS['connexions'][$this->serveur ? $this->serveur : 0]['prefixe'];
2140
		$this->db = $GLOBALS['connexions'][$this->serveur ? $this->serveur : 0]['db'];
2141
2142
		// tracage des requetes ?
2143
		$this->tracer = (isset($_GET['var_profile']) && $_GET['var_profile']);
2144
	}
2145
2146
	/**
2147
	 * lancer la requete $query,
2148
	 * faire le tracage si demande
2149
	 * http://doc.spip.org/@executer_requete
2150
	 *
2151
	 * @return bool|SQLiteResult
2152
	 */
2153
	function executer_requete($query, $tracer=null){
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
2154
		if (is_null($tracer))
2155
			$tracer = $this->tracer;
2156
		$err = "";
2157
		$t = 0;
2158
		if ($tracer){
2159
			include_spip('public/tracer');
2160
			$t = trace_query_start();
2161
		}
2162
		
2163
		# spip_log("requete: $this->serveur >> $query",'sqlite.'._LOG_DEBUG); // boum ? pourquoi ?
2164
		if ($this->link){
2165
			// memoriser la derniere erreur PHP vue
2166
			$e = (function_exists('error_get_last')?error_get_last():"");
2167
			// sauver la derniere requete
2168
			$GLOBALS['connexions'][$this->serveur ? $this->serveur : 0]['last'] = $query;
2169
2170
			if ($this->sqlite_version==3){
2171
				$r = $this->link->query($query);
0 ignored issues
show
Bug introduced by
The method query cannot be called on $this->link (of type string).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
2172
				// sauvegarde de la requete (elle y est deja dans $r->queryString)
2173
				# $r->spipQueryString = $query;
2174
2175
				// comptage : oblige de compter le nombre d'entrees retournees 
2176
				// par une requete SELECT
2177
				// aucune autre solution ne donne le nombre attendu :( !
2178
				// particulierement s'il y a des LIMIT dans la requete.
2179
				if (strtoupper(substr(ltrim($query), 0, 6))=='SELECT'){
2180
					if ($r){
2181
						// noter le link et la query pour faire le comptage *si* on en a besoin
2182
						$r->spipSqliteRowCount = array($this->link,$query);
2183
					}
2184
					elseif ($r instanceof PDOStatement) {
2185
						$r->spipSqliteRowCount = 0;
0 ignored issues
show
Bug introduced by
The property spipSqliteRowCount does not seem to exist in PDOStatement.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
2186
					}
2187
				}
2188
			}
2189
			else {
2190
				$r = sqlite_query($this->link, $query);
2191
			}
2192
2193
			// loger les warnings/erreurs eventuels de sqlite remontant dans PHP
2194
			if ($err = (function_exists('error_get_last')?error_get_last():"") AND $err!=$e){
2195
				$err = strip_tags($err['message'])." in ".$err['file']." line ".$err['line'];
2196
				spip_log("$err - ".$query, 'sqlite.'._LOG_ERREUR);
2197
			}
2198
			else $err = "";
2199
2200
		}
2201
		else {
2202
			$r = false;
2203
		}
2204
2205
		if (spip_sqlite_errno($this->serveur))
2206
			$err .= spip_sqlite_error($query, $this->serveur);
2207
		return $t ? trace_query_end($query, $t, $r, $err, $this->serveur) : $r;
2208
	}
2209
2210
	function last_insert_id(){
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
2211
		if ($this->sqlite_version==3)
2212
			return $this->link->lastInsertId();
0 ignored issues
show
Bug introduced by
The method lastInsertId cannot be called on $this->link (of type string).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
2213
		else
2214
			return sqlite_last_insert_rowid($this->link);
2215
	}
2216
}
2217
2218
2219
/**
2220
 * Cette classe est presente essentiellement pour un preg_replace_callback
2221
 * avec des parametres dans la fonction appelee que l'on souhaite incrementer
2222
 * (fonction pour proteger les textes)
2223
 */
2224
class sqlite_traducteur {
2225
	var $query = '';
2226
	var $prefixe = ''; // le prefixe des tables
2227
	var $sqlite_version = ''; // Version de sqlite (2 ou 3)
2228
	
2229
	// Pour les corrections a effectuer sur les requetes :
2230
	var $textes = array(); // array(code=>'texte') trouvé
2231
2232
	function sqlite_traducteur($query, $prefixe, $sqlite_version){
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
2233
		$this->query = $query;
2234
		$this->prefixe = $prefixe;
2235
		$this->sqlite_version = $sqlite_version;
2236
	}
2237
2238
	/**
2239
	 * transformer la requete pour sqlite
2240
	 * enleve les textes, transforme la requete pour quelle soit
2241
	 * bien interpretee par sqlite, puis remet les textes
2242
	 * la fonction affecte $this->query
2243
	 * http://doc.spip.org/@traduire_requete
2244
	 *
2245
	 * @return void
0 ignored issues
show
Documentation introduced by
Should the return type not be string?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
2246
	 */
2247
	function traduire_requete(){
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
2248
		//
2249
		// 1) Protection des textes en les remplacant par des codes
2250
		//
2251
		// enlever les 'textes' et initialiser avec
2252
		list($this->query, $textes) = query_echappe_textes($this->query);
2253
2254
		//
2255
		// 2) Corrections de la requete
2256
		//
2257
		// Correction Create Database
2258
		// Create Database -> requete ignoree
2259 View Code Duplication
		if (strpos($this->query, 'CREATE DATABASE')===0){
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2260
			spip_log("Sqlite : requete non executee -> $this->query", 'sqlite.'._LOG_AVERTISSEMENT);
2261
			$this->query = "SELECT 1";
2262
		}
2263
2264
		// Correction Insert Ignore
2265
		// INSERT IGNORE -> insert (tout court et pas 'insert or replace')
2266 View Code Duplication
		if (strpos($this->query, 'INSERT IGNORE')===0){
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2267
			spip_log("Sqlite : requete transformee -> $this->query", 'sqlite.'._LOG_DEBUG);
2268
			$this->query = 'INSERT '.substr($this->query, '13');
2269
		}
2270
2271
		// Correction des dates avec INTERVAL
2272
		// utiliser sql_date_proche() de preference
2273 View Code Duplication
		if (strpos($this->query, 'INTERVAL')!==false){
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2274
			$this->query = preg_replace_callback("/DATE_(ADD|SUB)(.*)INTERVAL\s+(\d+)\s+([a-zA-Z]+)\)/U",
2275
			                                     array(&$this, '_remplacerDateParTime'),
2276
			                                     $this->query);
2277
		}
2278
2279 View Code Duplication
		if (strpos($this->query, 'LEFT(')!==false){
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2280
			$this->query = str_replace('LEFT(','_LEFT(',$this->query);
2281
		}
2282
2283
		if (strpos($this->query, 'TIMESTAMPDIFF(')!==false){
2284
			$this->query = preg_replace('/TIMESTAMPDIFF\(\s*([^,]*)\s*,/Uims',"TIMESTAMPDIFF('\\1',",$this->query);
2285
		}
2286
2287
2288
		// Correction Using
2289
		// USING (non reconnu en sqlite2)
2290
		// problematique car la jointure ne se fait pas du coup.
2291
		if (($this->sqlite_version==2) && (strpos($this->query, "USING")!==false)){
2292
			spip_log("'USING (champ)' n'est pas reconnu en SQLite 2. Utilisez 'ON table1.champ = table2.champ'", 'sqlite.'._LOG_ERREUR);
2293
			$this->query = preg_replace('/USING\s*\([^\)]*\)/', '', $this->query);
2294
		}
2295
2296
		// Correction Field
2297
		// remplace FIELD(table,i,j,k...) par CASE WHEN table=i THEN n ... ELSE 0 END
2298 View Code Duplication
		if (strpos($this->query, 'FIELD')!==false){
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2299
			$this->query = preg_replace_callback('/FIELD\s*\(([^\)]*)\)/',
2300
			                                     array(&$this, '_remplacerFieldParCase'),
2301
			                                     $this->query);
2302
		}
2303
2304
		// Correction des noms de tables FROM
2305
		// mettre les bons noms de table dans from, update, insert, replace...
2306
		if (preg_match('/\s(SET|VALUES|WHERE|DATABASE)\s/iS', $this->query, $regs)){
2307
			$suite = strstr($this->query, $regs[0]);
2308
			$this->query = substr($this->query, 0, -strlen($suite));
2309
		}
2310
		else
2311
			$suite = '';
2312
		$pref = ($this->prefixe) ? $this->prefixe."_" : "";
2313
		$this->query = preg_replace('/([,\s])spip_/S', '\1'.$pref, $this->query).$suite;
2314
2315
		// Correction zero AS x
2316
		// pg n'aime pas 0+x AS alias, sqlite, dans le meme style, 
2317
		// n'apprecie pas du tout SELECT 0 as x ... ORDER BY x
2318
		// il dit que x ne doit pas être un integer dans le order by !
2319
		// on remplace du coup x par vide() dans ce cas uniquement
2320
		//
2321
		// rien que pour public/vertebrer.php ?
2322
		if ((strpos($this->query, "0 AS")!==false)){
2323
			// on ne remplace que dans ORDER BY ou GROUP BY
2324
			if (preg_match('/\s(ORDER|GROUP) BY\s/i', $this->query, $regs)){
2325
				$suite = strstr($this->query, $regs[0]);
2326
				$this->query = substr($this->query, 0, -strlen($suite));
2327
2328
				// on cherche les noms des x dans 0 AS x
2329
				// on remplace dans $suite le nom par vide()
2330
				preg_match_all('/\b0 AS\s*([^\s,]+)/', $this->query, $matches, PREG_PATTERN_ORDER);
2331
				foreach ($matches[1] as $m){
2332
					$suite = str_replace($m, 'VIDE()', $suite);
2333
				}
2334
				$this->query .= $suite;
2335
			}
2336
		}
2337
2338
		// Correction possible des divisions entieres
2339
		// Le standard SQL (lequel? ou?) semble indiquer que
2340
		// a/b=c doit donner c entier si a et b sont entiers 4/3=1.
2341
		// C'est ce que retournent effectivement SQL Server et SQLite
2342
		// Ce n'est pas ce qu'applique MySQL qui retourne un reel : 4/3=1.333...
2343
		// 
2344
		// On peut forcer la conversion en multipliant par 1.0 avant la division
2345
		// /!\ SQLite 3.5.9 Debian/Ubuntu est victime d'un bug en plus ! 
2346
		// cf. https://bugs.launchpad.net/ubuntu/+source/sqlite3/+bug/254228
2347
		//     http://www.sqlite.org/cvstrac/tktview?tn=3202
2348
		// (4*1.0/3) n'est pas rendu dans ce cas !
2349
		# $this->query = str_replace('/','* 1.00 / ',$this->query);
2350
2351
2352
		// Correction critere REGEXP, non reconnu en sqlite2
2353
		if (($this->sqlite_version==2) && (strpos($this->query, 'REGEXP')!==false)){
2354
			$this->query = preg_replace('/([^\s\(]*)(\s*)REGEXP(\s*)([^\s\)]*)/', 'REGEXP($4, $1)', $this->query);
2355
		}
2356
2357
		//
2358
		// 3) Remise en place des textes d'origine
2359
		//
2360
		// Correction Antiquotes et echappements
2361
		// ` => rien
2362 View Code Duplication
		if (strpos($this->query,'`')!==false)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2363
			$this->query = str_replace('`','', $this->query);
2364
2365
		$this->query = query_reinjecte_textes($this->query, $textes);
0 ignored issues
show
Security Bug introduced by
It seems like $textes can also be of type false; however, query_reinjecte_textes() does only seem to accept array, did you maybe forget to handle an error condition?
Loading history...
2366
2367
		return $this->query;
2368
	}
2369
2370
2371
	/**
2372
	 * les callbacks
2373
	 * remplacer DATE_ / INTERVAL par DATE...strtotime
2374
	 * http://doc.spip.org/@_remplacerDateParTime
2375
	 *
2376
	 * @param  $matches
2377
	 * @return string
2378
	 */
2379
	function _remplacerDateParTime($matches){
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
2380
		$op = strtoupper($matches[1]=='ADD') ? '+' : '-';
2381
		return "datetime$matches[2] '$op$matches[3] $matches[4]')";
2382
	}
2383
2384
	/**
2385
	 * callback ou l'on remplace FIELD(table,i,j,k...) par CASE WHEN table=i THEN n ... ELSE 0 END
2386
	 * http://doc.spip.org/@_remplacerFieldParCase
2387
	 *
2388
	 * @param  $matches
2389
	 * @return string
2390
	 */
2391
	function _remplacerFieldParCase($matches){
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
2392
		$fields = substr($matches[0], 6, -1); // ne recuperer que l'interieur X de field(X)
2393
		$t = explode(',', $fields);
2394
		$index = array_shift($t);
2395
2396
		$res = '';
2397
		$n = 0;
2398
		foreach ($t as $v){
2399
			$n++;
2400
			$res .= "\nWHEN $index=$v THEN $n";
2401
		}
2402
		return "CASE $res ELSE 0 END ";
2403
	}
2404
2405
}
2406
2407
?>
0 ignored issues
show
Best Practice introduced by
It is not recommended to use PHP's closing tag ?> in files other than templates.

Using a closing tag in PHP files that only contain PHP code is not recommended as you might accidentally add whitespace after the closing tag which would then be output by PHP. This can cause severe problems, for example headers cannot be sent anymore.

A simple precaution is to leave off the closing tag as it is not required, and it also has no negative effects whatsoever.

Loading history...
2408