Completed
Push — master ( fdca64...241762 )
by cam
02:05
created
ecrire/action/cookie.php 2 patches
Indentation   +86 added lines, -86 removed lines patch added patch discarded remove patch
@@ -16,7 +16,7 @@  discard block
 block discarded – undo
16 16
  */
17 17
 
18 18
 if (!defined('_ECRIRE_INC_VERSION')) {
19
-	return;
19
+    return;
20 20
 }
21 21
 
22 22
 include_spip('inc/actions');
@@ -32,94 +32,94 @@  discard block
 block discarded – undo
32 32
  * @return void
33 33
  */
34 34
 function action_cookie_dist($set_cookie_admin = null, $change_session = null) {
35
-	$redirect_echec = $redirect = null;
36
-	$test_echec_cookie = null;
37
-	$url = '';
38
-	if (is_null($set_cookie_admin)) {
39
-		$set_cookie_admin = _request('cookie_admin');
40
-		$change_session = _request('change_session');
41
-		$test_echec_cookie = _request('test_echec_cookie');
35
+    $redirect_echec = $redirect = null;
36
+    $test_echec_cookie = null;
37
+    $url = '';
38
+    if (is_null($set_cookie_admin)) {
39
+        $set_cookie_admin = _request('cookie_admin');
40
+        $change_session = _request('change_session');
41
+        $test_echec_cookie = _request('test_echec_cookie');
42 42
 
43
-		// La cible de notre operation de connexion
44
-		$url = securiser_redirect_action(_request('url'));
45
-		$redirect = $url ?: generer_url_ecrire('accueil');
46
-		$redirect_echec = _request('url_echec');
47
-		if (!isset($redirect_echec)) {
48
-			if (str_contains((string) $redirect, (string) _DIR_RESTREINT_ABS)) {
49
-				$redirect_echec = generer_url_public('login', '', true);
50
-			} else {
51
-				$redirect_echec = $redirect;
52
-			}
53
-		}
54
-	}
43
+        // La cible de notre operation de connexion
44
+        $url = securiser_redirect_action(_request('url'));
45
+        $redirect = $url ?: generer_url_ecrire('accueil');
46
+        $redirect_echec = _request('url_echec');
47
+        if (!isset($redirect_echec)) {
48
+            if (str_contains((string) $redirect, (string) _DIR_RESTREINT_ABS)) {
49
+                $redirect_echec = generer_url_public('login', '', true);
50
+            } else {
51
+                $redirect_echec = $redirect;
52
+            }
53
+        }
54
+    }
55 55
 
56 56
 
57
-	// rejoue le cookie pour renouveler spip_session
58
-	if ($change_session == 'oui') {
59
-		$session = charger_fonction('session', 'inc');
60
-		$session(true);
61
-		spip_logger()->info('statut 204 pour ' . $_SERVER['REQUEST_URI']);
62
-		http_response_code(204); // No Content
63
-		return;
64
-	}
57
+    // rejoue le cookie pour renouveler spip_session
58
+    if ($change_session == 'oui') {
59
+        $session = charger_fonction('session', 'inc');
60
+        $session(true);
61
+        spip_logger()->info('statut 204 pour ' . $_SERVER['REQUEST_URI']);
62
+        http_response_code(204); // No Content
63
+        return;
64
+    }
65 65
 
66
-	// tentative de connexion en auth_http
67
-	if (_request('essai_auth_http') && !$GLOBALS['ignore_auth_http']) {
68
-		include_spip('inc/auth');
69
-		if (
70
-			@$_SERVER['PHP_AUTH_USER']
71
-			&& @$_SERVER['PHP_AUTH_PW']
72
-			&& ($auteur = lire_php_auth($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW']))
73
-		) {
74
-			auth_loger($auteur);
75
-			redirige_par_entete(parametre_url($redirect, 't', time(), '&'));
76
-		} else {
77
-			ask_php_auth(
78
-				_T('info_connexion_refusee'),
79
-				_T('login_login_pass_incorrect'),
80
-				_T('login_retour_site'),
81
-				'url=' . rawurlencode((string) $redirect),
82
-				_T('login_nouvelle_tentative'),
83
-				(str_contains((string) $url, (string) _DIR_RESTREINT_ABS))
84
-			);
85
-		}
86
-	} else {
87
-		// en cas de login sur bonjour=oui, on tente de poser un cookie
88
-		// puis de passer au login qui diagnostiquera l'echec de cookie
89
-		// le cas echeant.
90
-		if ($test_echec_cookie == 'oui') {
91
-			spip_setcookie('spip_session', 'test_echec_cookie', httponly: true);
92
-			if ($redirect) {
93
-				$redirect = parametre_url(
94
-					parametre_url($redirect_echec, 'var_echec_cookie', 'oui', '&'),
95
-					'url',
96
-					rawurlencode((string) $redirect),
97
-					'&'
98
-				);
99
-			}
100
-		} else {
101
-			$cook = $_COOKIE['spip_admin'] ?? '';
102
-			// Suppression cookie d'admin ?
103
-			if ($set_cookie_admin == 'non') {
104
-				if ($cook) {
105
-					spip_setcookie('spip_admin', $cook, time() - 3600 * 24, httponly: true);
106
-				}
107
-			} // Ajout de cookie d'admin
108
-			else {
109
-				if ($set_cookie_admin && _DUREE_COOKIE_ADMIN) {
110
-					spip_setcookie(
111
-						'spip_admin',
112
-						$set_cookie_admin,
113
-						time() + max(_DUREE_COOKIE_ADMIN, 2 * _RENOUVELLE_ALEA),
114
-						httponly: true
115
-					);
116
-				}
117
-			}
118
-		}
119
-	}
66
+    // tentative de connexion en auth_http
67
+    if (_request('essai_auth_http') && !$GLOBALS['ignore_auth_http']) {
68
+        include_spip('inc/auth');
69
+        if (
70
+            @$_SERVER['PHP_AUTH_USER']
71
+            && @$_SERVER['PHP_AUTH_PW']
72
+            && ($auteur = lire_php_auth($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW']))
73
+        ) {
74
+            auth_loger($auteur);
75
+            redirige_par_entete(parametre_url($redirect, 't', time(), '&'));
76
+        } else {
77
+            ask_php_auth(
78
+                _T('info_connexion_refusee'),
79
+                _T('login_login_pass_incorrect'),
80
+                _T('login_retour_site'),
81
+                'url=' . rawurlencode((string) $redirect),
82
+                _T('login_nouvelle_tentative'),
83
+                (str_contains((string) $url, (string) _DIR_RESTREINT_ABS))
84
+            );
85
+        }
86
+    } else {
87
+        // en cas de login sur bonjour=oui, on tente de poser un cookie
88
+        // puis de passer au login qui diagnostiquera l'echec de cookie
89
+        // le cas echeant.
90
+        if ($test_echec_cookie == 'oui') {
91
+            spip_setcookie('spip_session', 'test_echec_cookie', httponly: true);
92
+            if ($redirect) {
93
+                $redirect = parametre_url(
94
+                    parametre_url($redirect_echec, 'var_echec_cookie', 'oui', '&'),
95
+                    'url',
96
+                    rawurlencode((string) $redirect),
97
+                    '&'
98
+                );
99
+            }
100
+        } else {
101
+            $cook = $_COOKIE['spip_admin'] ?? '';
102
+            // Suppression cookie d'admin ?
103
+            if ($set_cookie_admin == 'non') {
104
+                if ($cook) {
105
+                    spip_setcookie('spip_admin', $cook, time() - 3600 * 24, httponly: true);
106
+                }
107
+            } // Ajout de cookie d'admin
108
+            else {
109
+                if ($set_cookie_admin && _DUREE_COOKIE_ADMIN) {
110
+                    spip_setcookie(
111
+                        'spip_admin',
112
+                        $set_cookie_admin,
113
+                        time() + max(_DUREE_COOKIE_ADMIN, 2 * _RENOUVELLE_ALEA),
114
+                        httponly: true
115
+                    );
116
+                }
117
+            }
118
+        }
119
+    }
120 120
 
121
-	// Redirection finale
122
-	if ($redirect) {
123
-		redirige_par_entete($redirect, true);
124
-	}
121
+    // Redirection finale
122
+    if ($redirect) {
123
+        redirige_par_entete($redirect, true);
124
+    }
125 125
 }
Please login to merge, or discard this patch.
Spacing   +2 added lines, -2 removed lines patch added patch discarded remove patch
@@ -58,7 +58,7 @@  discard block
 block discarded – undo
58 58
 	if ($change_session == 'oui') {
59 59
 		$session = charger_fonction('session', 'inc');
60 60
 		$session(true);
61
-		spip_logger()->info('statut 204 pour ' . $_SERVER['REQUEST_URI']);
61
+		spip_logger()->info('statut 204 pour '.$_SERVER['REQUEST_URI']);
62 62
 		http_response_code(204); // No Content
63 63
 		return;
64 64
 	}
@@ -78,7 +78,7 @@  discard block
 block discarded – undo
78 78
 				_T('info_connexion_refusee'),
79 79
 				_T('login_login_pass_incorrect'),
80 80
 				_T('login_retour_site'),
81
-				'url=' . rawurlencode((string) $redirect),
81
+				'url='.rawurlencode((string) $redirect),
82 82
 				_T('login_nouvelle_tentative'),
83 83
 				(str_contains((string) $url, (string) _DIR_RESTREINT_ABS))
84 84
 			);
Please login to merge, or discard this patch.
ecrire/src/Sql/Sqlite/Traducteur.php 2 patches
Indentation   +176 added lines, -176 removed lines patch added patch discarded remove patch
@@ -11,180 +11,180 @@
 block discarded – undo
11 11
  */
12 12
 class Traducteur
13 13
 {
14
-	/** Pour les corrections à effectuer sur les requêtes : array(code=>'texte') trouvé
15
-	 *
16
-	 * @var array
17
-	 */
18
-	public $textes = [];
19
-
20
-	protected LoggerInterface $logger;
21
-
22
-	/**
23
-	 * Constructeur
24
-	 */
25
-	public function __construct(
26
-		/** Requête à préparer */
27
-		public string $query,
28
-		/** Prefixe des tables à utiliser */
29
-		public string $prefixe,
30
-		/** Version SQLite (2 ou 3) */
31
-		public string $sqlite_version
32
-	) {
33
-		$this->logger = spip_logger('sqlite'); // FIXME: inject it.
34
-	}
35
-
36
-	/**
37
-	 * Transformer la requete pour SQLite
38
-	 *
39
-	 * Enlève les textes, transforme la requête pour quelle soit
40
-	 * bien interprétée par SQLite, puis remet les textes
41
-	 * la fonction affecte `$this->query`
42
-	 */
43
-	public function traduire_requete() {
44
-		//
45
-		// 1) Protection des textes en les remplacant par des codes
46
-		//
47
-		// enlever les 'textes' et initialiser avec
48
-		[$this->query, $textes] = query_echappe_textes($this->query);
49
-
50
-		//
51
-		// 2) Corrections de la requete
52
-		//
53
-		// Correction Create Database
54
-		// Create Database -> requete ignoree
55
-		if (str_starts_with((string) $this->query, 'CREATE DATABASE')) {
56
-			$this->logger->notice("Sqlite : requete non executee -> $this->query");
57
-			$this->query = 'SELECT 1';
58
-		}
59
-
60
-		// Correction Insert Ignore
61
-		// INSERT IGNORE -> insert (tout court et pas 'insert or replace')
62
-		if (str_starts_with((string) $this->query, 'INSERT IGNORE')) {
63
-			$this->logger->debug("Sqlite : requete transformee -> $this->query");
64
-			$this->query = 'INSERT ' . substr((string) $this->query, '13');
65
-		}
66
-
67
-		// Correction des dates avec INTERVAL
68
-		// utiliser sql_date_proche() de preference
69
-		if (str_contains((string) $this->query, 'INTERVAL')) {
70
-			$this->query = preg_replace_callback(
71
-				'/DATE_(ADD|SUB)(.*)INTERVAL\s+(\d+)\s+([a-zA-Z]+)\)/U',
72
-				fn(array $matches): string => $this->_remplacerDateParTime($matches),
73
-				(string) $this->query
74
-			);
75
-		}
76
-
77
-		if (str_contains((string) $this->query, 'LEFT(')) {
78
-			$this->query = str_replace('LEFT(', '_LEFT(', (string) $this->query);
79
-		}
80
-
81
-		if (str_contains((string) $this->query, 'TIMESTAMPDIFF(')) {
82
-			$this->query = preg_replace('/TIMESTAMPDIFF\(\s*([^,]*)\s*,/Uims', "TIMESTAMPDIFF('\\1',", (string) $this->query);
83
-		}
84
-
85
-		// Correction Field
86
-		// remplace FIELD(table,i,j,k...) par CASE WHEN table=i THEN n ... ELSE 0 END
87
-		if (str_contains((string) $this->query, 'FIELD')) {
88
-			$this->query = preg_replace_callback(
89
-				'/FIELD\s*\(([^\)]*)\)/',
90
-				fn(array $matches): string => $this->_remplacerFieldParCase($matches),
91
-				(string) $this->query
92
-			);
93
-		}
94
-
95
-		// Correction des noms de tables FROM
96
-		// mettre les bons noms de table dans from, update, insert, replace...
97
-		if (preg_match('/\s(SET|VALUES|WHERE|DATABASE)\s/iS', (string) $this->query, $regs)) {
98
-			$suite = strstr((string) $this->query, $regs[0]);
99
-			$this->query = substr((string) $this->query, 0, -strlen($suite));
100
-		} else {
101
-			$suite = '';
102
-		}
103
-		$pref = ($this->prefixe) ? $this->prefixe . '_' : '';
104
-		$this->query = preg_replace('/([,\s])spip_/S', '\1' . $pref, (string) $this->query) . $suite;
105
-
106
-		// Correction zero AS x
107
-		// pg n'aime pas 0+x AS alias, sqlite, dans le meme style,
108
-		// n'apprecie pas du tout SELECT 0 as x ... ORDER BY x
109
-		// il dit que x ne doit pas être un integer dans le order by !
110
-		// on remplace du coup x par vide() dans ce cas uniquement
111
-		//
112
-		// apparait dans public/vertebrer.php et dans le plugin menu aussi qui genere aussi ce genre de requete via un {par num #GET{tri_num}}
113
-		// mais est-ce encore un soucis pour sqlite en 2021 ? (ie commenter le preg_replace marche très bien en sqlite 3.28)
114
-		// on ne remplace que dans ORDER BY ou GROUP BY
115
-		if (str_contains($this->query, '0 AS') && preg_match('/\s(ORDER|GROUP) BY\s/i', $this->query, $regs)) {
116
-			$suite = strstr($this->query, $regs[0]);
117
-			$this->query = substr($this->query, 0, -strlen($suite));
118
-			// on cherche les noms des x dans 0 AS x
119
-			// on remplace dans $suite le nom par vide()
120
-			preg_match_all('/\b0 AS\s*([^\s,]+)/', $this->query, $matches, PREG_PATTERN_ORDER);
121
-			foreach ($matches[1] as $m) {
122
-				if (str_contains($suite, $m)) {
123
-					$suite = preg_replace(",\b$m\b,", 'VIDE()', $suite);
124
-				}
125
-			}
126
-			$this->query .= $suite;
127
-		}
128
-
129
-		// Correction possible des divisions entieres
130
-		// Le standard SQL (lequel? ou?) semble indiquer que
131
-		// a/b=c doit donner c entier si a et b sont entiers 4/3=1.
132
-		// C'est ce que retournent effectivement SQL Server et SQLite
133
-		// Ce n'est pas ce qu'applique MySQL qui retourne un reel : 4/3=1.333...
134
-		//
135
-		// On peut forcer la conversion en multipliant par 1.0 avant la division
136
-		// /!\ SQLite 3.5.9 Debian/Ubuntu est victime d'un bug en plus !
137
-		// cf. https://bugs.launchpad.net/ubuntu/+source/sqlite3/+bug/254228
138
-		//     http://www.sqlite.org/cvstrac/tktview?tn=3202
139
-		// (4*1.0/3) n'est pas rendu dans ce cas !
140
-		# $this->query = str_replace('/','* 1.00 / ',$this->query);
141
-
142
-		//
143
-		// 3) Remise en place des textes d'origine
144
-		//
145
-		// Correction Antiquotes et echappements
146
-		// ` => rien
147
-		if (str_contains($this->query, '`')) {
148
-			$this->query = str_replace('`', '', $this->query);
149
-		}
150
-
151
-		$this->query = query_reinjecte_textes($this->query, $textes);
152
-
153
-		return $this->query;
154
-	}
155
-
156
-	/**
157
-	 * Callback pour remplacer `DATE_` / `INTERVAL`
158
-	 * par `DATE ... strtotime`
159
-	 *
160
-	 * @param array $matches Captures
161
-	 * @return string texte de date compris par SQLite
162
-	 */
163
-	public function _remplacerDateParTime($matches) {
164
-		$op = strtoupper($matches[1] == 'ADD') ? '+' : '-';
165
-
166
-		return "datetime$matches[2] '$op$matches[3] $matches[4]')";
167
-	}
168
-
169
-	/**
170
-	 * Callback pour remplacer `FIELD(table,i,j,k...)`
171
-	 * par `CASE WHEN table=i THEN n ... ELSE 0 END`
172
-	 *
173
-	 * @param array $matches Captures
174
-	 * @return string texte de liste ordonnée compris par SQLite
175
-	 */
176
-	public function _remplacerFieldParCase($matches) {
177
-		$fields = substr((string) $matches[0], 6, -1); // ne recuperer que l'interieur X de field(X)
178
-		$t = explode(',', $fields);
179
-		$index = array_shift($t);
180
-
181
-		$res = '';
182
-		$n = 0;
183
-		foreach ($t as $v) {
184
-			$n++;
185
-			$res .= "\nWHEN $index=$v THEN $n";
186
-		}
187
-
188
-		return "CASE $res ELSE 0 END ";
189
-	}
14
+    /** Pour les corrections à effectuer sur les requêtes : array(code=>'texte') trouvé
15
+     *
16
+     * @var array
17
+     */
18
+    public $textes = [];
19
+
20
+    protected LoggerInterface $logger;
21
+
22
+    /**
23
+     * Constructeur
24
+     */
25
+    public function __construct(
26
+        /** Requête à préparer */
27
+        public string $query,
28
+        /** Prefixe des tables à utiliser */
29
+        public string $prefixe,
30
+        /** Version SQLite (2 ou 3) */
31
+        public string $sqlite_version
32
+    ) {
33
+        $this->logger = spip_logger('sqlite'); // FIXME: inject it.
34
+    }
35
+
36
+    /**
37
+     * Transformer la requete pour SQLite
38
+     *
39
+     * Enlève les textes, transforme la requête pour quelle soit
40
+     * bien interprétée par SQLite, puis remet les textes
41
+     * la fonction affecte `$this->query`
42
+     */
43
+    public function traduire_requete() {
44
+        //
45
+        // 1) Protection des textes en les remplacant par des codes
46
+        //
47
+        // enlever les 'textes' et initialiser avec
48
+        [$this->query, $textes] = query_echappe_textes($this->query);
49
+
50
+        //
51
+        // 2) Corrections de la requete
52
+        //
53
+        // Correction Create Database
54
+        // Create Database -> requete ignoree
55
+        if (str_starts_with((string) $this->query, 'CREATE DATABASE')) {
56
+            $this->logger->notice("Sqlite : requete non executee -> $this->query");
57
+            $this->query = 'SELECT 1';
58
+        }
59
+
60
+        // Correction Insert Ignore
61
+        // INSERT IGNORE -> insert (tout court et pas 'insert or replace')
62
+        if (str_starts_with((string) $this->query, 'INSERT IGNORE')) {
63
+            $this->logger->debug("Sqlite : requete transformee -> $this->query");
64
+            $this->query = 'INSERT ' . substr((string) $this->query, '13');
65
+        }
66
+
67
+        // Correction des dates avec INTERVAL
68
+        // utiliser sql_date_proche() de preference
69
+        if (str_contains((string) $this->query, 'INTERVAL')) {
70
+            $this->query = preg_replace_callback(
71
+                '/DATE_(ADD|SUB)(.*)INTERVAL\s+(\d+)\s+([a-zA-Z]+)\)/U',
72
+                fn(array $matches): string => $this->_remplacerDateParTime($matches),
73
+                (string) $this->query
74
+            );
75
+        }
76
+
77
+        if (str_contains((string) $this->query, 'LEFT(')) {
78
+            $this->query = str_replace('LEFT(', '_LEFT(', (string) $this->query);
79
+        }
80
+
81
+        if (str_contains((string) $this->query, 'TIMESTAMPDIFF(')) {
82
+            $this->query = preg_replace('/TIMESTAMPDIFF\(\s*([^,]*)\s*,/Uims', "TIMESTAMPDIFF('\\1',", (string) $this->query);
83
+        }
84
+
85
+        // Correction Field
86
+        // remplace FIELD(table,i,j,k...) par CASE WHEN table=i THEN n ... ELSE 0 END
87
+        if (str_contains((string) $this->query, 'FIELD')) {
88
+            $this->query = preg_replace_callback(
89
+                '/FIELD\s*\(([^\)]*)\)/',
90
+                fn(array $matches): string => $this->_remplacerFieldParCase($matches),
91
+                (string) $this->query
92
+            );
93
+        }
94
+
95
+        // Correction des noms de tables FROM
96
+        // mettre les bons noms de table dans from, update, insert, replace...
97
+        if (preg_match('/\s(SET|VALUES|WHERE|DATABASE)\s/iS', (string) $this->query, $regs)) {
98
+            $suite = strstr((string) $this->query, $regs[0]);
99
+            $this->query = substr((string) $this->query, 0, -strlen($suite));
100
+        } else {
101
+            $suite = '';
102
+        }
103
+        $pref = ($this->prefixe) ? $this->prefixe . '_' : '';
104
+        $this->query = preg_replace('/([,\s])spip_/S', '\1' . $pref, (string) $this->query) . $suite;
105
+
106
+        // Correction zero AS x
107
+        // pg n'aime pas 0+x AS alias, sqlite, dans le meme style,
108
+        // n'apprecie pas du tout SELECT 0 as x ... ORDER BY x
109
+        // il dit que x ne doit pas être un integer dans le order by !
110
+        // on remplace du coup x par vide() dans ce cas uniquement
111
+        //
112
+        // apparait dans public/vertebrer.php et dans le plugin menu aussi qui genere aussi ce genre de requete via un {par num #GET{tri_num}}
113
+        // mais est-ce encore un soucis pour sqlite en 2021 ? (ie commenter le preg_replace marche très bien en sqlite 3.28)
114
+        // on ne remplace que dans ORDER BY ou GROUP BY
115
+        if (str_contains($this->query, '0 AS') && preg_match('/\s(ORDER|GROUP) BY\s/i', $this->query, $regs)) {
116
+            $suite = strstr($this->query, $regs[0]);
117
+            $this->query = substr($this->query, 0, -strlen($suite));
118
+            // on cherche les noms des x dans 0 AS x
119
+            // on remplace dans $suite le nom par vide()
120
+            preg_match_all('/\b0 AS\s*([^\s,]+)/', $this->query, $matches, PREG_PATTERN_ORDER);
121
+            foreach ($matches[1] as $m) {
122
+                if (str_contains($suite, $m)) {
123
+                    $suite = preg_replace(",\b$m\b,", 'VIDE()', $suite);
124
+                }
125
+            }
126
+            $this->query .= $suite;
127
+        }
128
+
129
+        // Correction possible des divisions entieres
130
+        // Le standard SQL (lequel? ou?) semble indiquer que
131
+        // a/b=c doit donner c entier si a et b sont entiers 4/3=1.
132
+        // C'est ce que retournent effectivement SQL Server et SQLite
133
+        // Ce n'est pas ce qu'applique MySQL qui retourne un reel : 4/3=1.333...
134
+        //
135
+        // On peut forcer la conversion en multipliant par 1.0 avant la division
136
+        // /!\ SQLite 3.5.9 Debian/Ubuntu est victime d'un bug en plus !
137
+        // cf. https://bugs.launchpad.net/ubuntu/+source/sqlite3/+bug/254228
138
+        //     http://www.sqlite.org/cvstrac/tktview?tn=3202
139
+        // (4*1.0/3) n'est pas rendu dans ce cas !
140
+        # $this->query = str_replace('/','* 1.00 / ',$this->query);
141
+
142
+        //
143
+        // 3) Remise en place des textes d'origine
144
+        //
145
+        // Correction Antiquotes et echappements
146
+        // ` => rien
147
+        if (str_contains($this->query, '`')) {
148
+            $this->query = str_replace('`', '', $this->query);
149
+        }
150
+
151
+        $this->query = query_reinjecte_textes($this->query, $textes);
152
+
153
+        return $this->query;
154
+    }
155
+
156
+    /**
157
+     * Callback pour remplacer `DATE_` / `INTERVAL`
158
+     * par `DATE ... strtotime`
159
+     *
160
+     * @param array $matches Captures
161
+     * @return string texte de date compris par SQLite
162
+     */
163
+    public function _remplacerDateParTime($matches) {
164
+        $op = strtoupper($matches[1] == 'ADD') ? '+' : '-';
165
+
166
+        return "datetime$matches[2] '$op$matches[3] $matches[4]')";
167
+    }
168
+
169
+    /**
170
+     * Callback pour remplacer `FIELD(table,i,j,k...)`
171
+     * par `CASE WHEN table=i THEN n ... ELSE 0 END`
172
+     *
173
+     * @param array $matches Captures
174
+     * @return string texte de liste ordonnée compris par SQLite
175
+     */
176
+    public function _remplacerFieldParCase($matches) {
177
+        $fields = substr((string) $matches[0], 6, -1); // ne recuperer que l'interieur X de field(X)
178
+        $t = explode(',', $fields);
179
+        $index = array_shift($t);
180
+
181
+        $res = '';
182
+        $n = 0;
183
+        foreach ($t as $v) {
184
+            $n++;
185
+            $res .= "\nWHEN $index=$v THEN $n";
186
+        }
187
+
188
+        return "CASE $res ELSE 0 END ";
189
+    }
190 190
 }
Please login to merge, or discard this patch.
Spacing   +3 added lines, -3 removed lines patch added patch discarded remove patch
@@ -61,7 +61,7 @@  discard block
 block discarded – undo
61 61
 		// INSERT IGNORE -> insert (tout court et pas 'insert or replace')
62 62
 		if (str_starts_with((string) $this->query, 'INSERT IGNORE')) {
63 63
 			$this->logger->debug("Sqlite : requete transformee -> $this->query");
64
-			$this->query = 'INSERT ' . substr((string) $this->query, '13');
64
+			$this->query = 'INSERT '.substr((string) $this->query, '13');
65 65
 		}
66 66
 
67 67
 		// Correction des dates avec INTERVAL
@@ -100,8 +100,8 @@  discard block
 block discarded – undo
100 100
 		} else {
101 101
 			$suite = '';
102 102
 		}
103
-		$pref = ($this->prefixe) ? $this->prefixe . '_' : '';
104
-		$this->query = preg_replace('/([,\s])spip_/S', '\1' . $pref, (string) $this->query) . $suite;
103
+		$pref = ($this->prefixe) ? $this->prefixe.'_' : '';
104
+		$this->query = preg_replace('/([,\s])spip_/S', '\1'.$pref, (string) $this->query).$suite;
105 105
 
106 106
 		// Correction zero AS x
107 107
 		// pg n'aime pas 0+x AS alias, sqlite, dans le meme style,
Please login to merge, or discard this patch.
ecrire/src/Sql/Sqlite/Requeteur.php 2 patches
Indentation   +108 added lines, -108 removed lines patch added patch discarded remove patch
@@ -15,112 +15,112 @@
 block discarded – undo
15 15
 
16 16
 class Requeteur
17 17
 {
18
-	/** @var string texte de la requête */
19
-	public $query = ''; // la requete
20
-	/** @var string Nom de la connexion */
21
-	public $serveur = '';
22
-	/** @var \PDO|null Identifiant de la connexion SQLite */
23
-	public $link = null;
24
-	/** @var string Prefixe des tables SPIP */
25
-	public $prefixe = '';
26
-	/** @var string Nom de la base de donnée */
27
-	public $db = '';
28
-	/** @var bool Doit-on tracer les requetes (var_profile) ? */
29
-	public $tracer = false; // doit-on tracer les requetes (var_profile)
30
-
31
-	/** @var string Version de SQLite (2 ou 3) */
32
-	public $sqlite_version = '';
33
-
34
-	protected LoggerInterface $logger;
35
-
36
-	/**
37
-	 * Constructeur
38
-	 *
39
-	 * @param string $serveur
40
-	 */
41
-	public function __construct($serveur = '') {
42
-		_sqlite_init();
43
-		$this->serveur = strtolower($serveur);
44
-		$this->logger = spip_logger('sqlite'); // FIXME: inject it.
45
-
46
-		if (!($this->link = _sqlite_link($this->serveur)) && (!defined('_ECRIRE_INSTALL') || !_ECRIRE_INSTALL)) {
47
-			$this->logger->error('Aucune connexion sqlite (link)');
48
-
49
-			return;
50
-		}
51
-
52
-		$this->sqlite_version = _sqlite_is_version('', $this->link);
53
-
54
-		$this->prefixe = $GLOBALS['connexions'][$this->serveur ?: 0]['prefixe'];
55
-		$this->db = $GLOBALS['connexions'][$this->serveur ?: 0]['db'];
56
-
57
-		// tracage des requetes ?
58
-		$this->tracer = (isset($_GET['var_profile']) && $_GET['var_profile']);
59
-	}
60
-
61
-	/**
62
-	 * Lancer la requête transmise et faire le tracage si demandé
63
-	 *
64
-	 * @param string $query
65
-	 *     Requête à exécuter
66
-	 * @param bool|null $tracer
67
-	 *     true pour tracer la requête
68
-	 * @return bool|\PDOStatement|array
69
-	 */
70
-	public function executer_requete($query, $tracer = null) {
71
-		if (is_null($tracer)) {
72
-			$tracer = $this->tracer;
73
-		}
74
-		$err = '';
75
-		$t = 0;
76
-		if ($tracer || defined('_DEBUG_TRACE_QUERIES') && _DEBUG_TRACE_QUERIES) {
77
-			include_spip('public/tracer');
78
-			$t = trace_query_start();
79
-		}
80
-
81
-		# $this->logger->debug("requete: $this->serveur >> $query"); // boum ? pourquoi ?
82
-		if ($this->link) {
83
-			// memoriser la derniere erreur PHP vue
84
-			$last_error = (function_exists('error_get_last') ? error_get_last() : '');
85
-			$e = null;
86
-			// sauver la derniere requete
87
-			$GLOBALS['connexions'][$this->serveur ?: 0]['last'] = $query;
88
-			$GLOBALS['connexions'][$this->serveur ?: 0]['total_requetes']++;
89
-
90
-			try {
91
-				$r = $this->link->query($query);
92
-			} catch (\PDOException $e) {
93
-				$this->logger->error('PDOException: ' . $e->getMessage());
94
-				$r = false;
95
-			}
96
-
97
-			// loger les warnings/erreurs eventuels de sqlite remontant dans PHP
98
-			if ($e && $e instanceof \PDOException) {
99
-				$err = strip_tags($e->getMessage()) . ' in ' . $e->getFile() . ' line ' . $e->getLine();
100
-				$this->logger->error("$err - " . $query);
101
-			} elseif (($err = (function_exists('error_get_last') ? error_get_last() : '')) && $err != $last_error) {
102
-				$err = strip_tags($err['message']) . ' in ' . $err['file'] . ' line ' . $err['line'];
103
-				$this->logger->error("$err - " . $query);
104
-			} else {
105
-				$err = '';
106
-			}
107
-		} else {
108
-			$r = false;
109
-		}
110
-
111
-		if (spip_sqlite_errno($this->serveur)) {
112
-			$err .= spip_sqlite_error($query, $this->serveur);
113
-		}
114
-
115
-		return $t ? trace_query_end($query, $t, $r, $err, $this->serveur) : $r;
116
-	}
117
-
118
-	/**
119
-	 * Obtient l'identifiant de la dernière ligne insérée ou modifiée
120
-	 *
121
-	 * @return string|false
122
-	 **/
123
-	public function last_insert_id() {
124
-		return $this->link->lastInsertId();
125
-	}
18
+    /** @var string texte de la requête */
19
+    public $query = ''; // la requete
20
+    /** @var string Nom de la connexion */
21
+    public $serveur = '';
22
+    /** @var \PDO|null Identifiant de la connexion SQLite */
23
+    public $link = null;
24
+    /** @var string Prefixe des tables SPIP */
25
+    public $prefixe = '';
26
+    /** @var string Nom de la base de donnée */
27
+    public $db = '';
28
+    /** @var bool Doit-on tracer les requetes (var_profile) ? */
29
+    public $tracer = false; // doit-on tracer les requetes (var_profile)
30
+
31
+    /** @var string Version de SQLite (2 ou 3) */
32
+    public $sqlite_version = '';
33
+
34
+    protected LoggerInterface $logger;
35
+
36
+    /**
37
+     * Constructeur
38
+     *
39
+     * @param string $serveur
40
+     */
41
+    public function __construct($serveur = '') {
42
+        _sqlite_init();
43
+        $this->serveur = strtolower($serveur);
44
+        $this->logger = spip_logger('sqlite'); // FIXME: inject it.
45
+
46
+        if (!($this->link = _sqlite_link($this->serveur)) && (!defined('_ECRIRE_INSTALL') || !_ECRIRE_INSTALL)) {
47
+            $this->logger->error('Aucune connexion sqlite (link)');
48
+
49
+            return;
50
+        }
51
+
52
+        $this->sqlite_version = _sqlite_is_version('', $this->link);
53
+
54
+        $this->prefixe = $GLOBALS['connexions'][$this->serveur ?: 0]['prefixe'];
55
+        $this->db = $GLOBALS['connexions'][$this->serveur ?: 0]['db'];
56
+
57
+        // tracage des requetes ?
58
+        $this->tracer = (isset($_GET['var_profile']) && $_GET['var_profile']);
59
+    }
60
+
61
+    /**
62
+     * Lancer la requête transmise et faire le tracage si demandé
63
+     *
64
+     * @param string $query
65
+     *     Requête à exécuter
66
+     * @param bool|null $tracer
67
+     *     true pour tracer la requête
68
+     * @return bool|\PDOStatement|array
69
+     */
70
+    public function executer_requete($query, $tracer = null) {
71
+        if (is_null($tracer)) {
72
+            $tracer = $this->tracer;
73
+        }
74
+        $err = '';
75
+        $t = 0;
76
+        if ($tracer || defined('_DEBUG_TRACE_QUERIES') && _DEBUG_TRACE_QUERIES) {
77
+            include_spip('public/tracer');
78
+            $t = trace_query_start();
79
+        }
80
+
81
+        # $this->logger->debug("requete: $this->serveur >> $query"); // boum ? pourquoi ?
82
+        if ($this->link) {
83
+            // memoriser la derniere erreur PHP vue
84
+            $last_error = (function_exists('error_get_last') ? error_get_last() : '');
85
+            $e = null;
86
+            // sauver la derniere requete
87
+            $GLOBALS['connexions'][$this->serveur ?: 0]['last'] = $query;
88
+            $GLOBALS['connexions'][$this->serveur ?: 0]['total_requetes']++;
89
+
90
+            try {
91
+                $r = $this->link->query($query);
92
+            } catch (\PDOException $e) {
93
+                $this->logger->error('PDOException: ' . $e->getMessage());
94
+                $r = false;
95
+            }
96
+
97
+            // loger les warnings/erreurs eventuels de sqlite remontant dans PHP
98
+            if ($e && $e instanceof \PDOException) {
99
+                $err = strip_tags($e->getMessage()) . ' in ' . $e->getFile() . ' line ' . $e->getLine();
100
+                $this->logger->error("$err - " . $query);
101
+            } elseif (($err = (function_exists('error_get_last') ? error_get_last() : '')) && $err != $last_error) {
102
+                $err = strip_tags($err['message']) . ' in ' . $err['file'] . ' line ' . $err['line'];
103
+                $this->logger->error("$err - " . $query);
104
+            } else {
105
+                $err = '';
106
+            }
107
+        } else {
108
+            $r = false;
109
+        }
110
+
111
+        if (spip_sqlite_errno($this->serveur)) {
112
+            $err .= spip_sqlite_error($query, $this->serveur);
113
+        }
114
+
115
+        return $t ? trace_query_end($query, $t, $r, $err, $this->serveur) : $r;
116
+    }
117
+
118
+    /**
119
+     * Obtient l'identifiant de la dernière ligne insérée ou modifiée
120
+     *
121
+     * @return string|false
122
+     **/
123
+    public function last_insert_id() {
124
+        return $this->link->lastInsertId();
125
+    }
126 126
 }
Please login to merge, or discard this patch.
Spacing   +5 added lines, -5 removed lines patch added patch discarded remove patch
@@ -90,17 +90,17 @@
 block discarded – undo
90 90
 			try {
91 91
 				$r = $this->link->query($query);
92 92
 			} catch (\PDOException $e) {
93
-				$this->logger->error('PDOException: ' . $e->getMessage());
93
+				$this->logger->error('PDOException: '.$e->getMessage());
94 94
 				$r = false;
95 95
 			}
96 96
 
97 97
 			// loger les warnings/erreurs eventuels de sqlite remontant dans PHP
98 98
 			if ($e && $e instanceof \PDOException) {
99
-				$err = strip_tags($e->getMessage()) . ' in ' . $e->getFile() . ' line ' . $e->getLine();
100
-				$this->logger->error("$err - " . $query);
99
+				$err = strip_tags($e->getMessage()).' in '.$e->getFile().' line '.$e->getLine();
100
+				$this->logger->error("$err - ".$query);
101 101
 			} elseif (($err = (function_exists('error_get_last') ? error_get_last() : '')) && $err != $last_error) {
102
-				$err = strip_tags($err['message']) . ' in ' . $err['file'] . ' line ' . $err['line'];
103
-				$this->logger->error("$err - " . $query);
102
+				$err = strip_tags($err['message']).' in '.$err['file'].' line '.$err['line'];
103
+				$this->logger->error("$err - ".$query);
104 104
 			} else {
105 105
 				$err = '';
106 106
 			}
Please login to merge, or discard this patch.
ecrire/src/Texte/Collecteur/AbstractCollecteur.php 2 patches
Indentation   +282 added lines, -282 removed lines patch added patch discarded remove patch
@@ -12,286 +12,286 @@
 block discarded – undo
12 12
 namespace Spip\Texte\Collecteur;
13 13
 
14 14
 abstract class AbstractCollecteur {
15
-	protected static string $markPrefix = 'COLLECT';
16
-	protected string $markId;
17
-
18
-	public static array $listeBalisesBloc = [
19
-		'address', 'applet', 'article', 'aside',
20
-		'blockquote', 'button',
21
-		'center',
22
-		'dl', 'dt', 'dd', 'div',
23
-		'fieldset', 'figure', 'figcaption', 'footer', 'form',
24
-		'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'hr', 'hgroup', 'head', 'header',
25
-		'iframe',
26
-		'li',
27
-		'map', 'marquee',
28
-		'nav', 'noscript',
29
-		'object', 'ol',
30
-		'pre',
31
-		'section',
32
-		'table', 'tr', 'td', 'th', 'tbody', 'foot', 'textarea',
33
-		'ul',
34
-		'script', 'style'
35
-	];
36
-
37
-	/**
38
-	 * Collecteur générique des occurences d'une preg dans un texte avec leurs positions et longueur
39
-	 * @param string $texte
40
-	 *   texte à analyser pour la collecte
41
-	 * @param string $if_chars
42
-	 *   caractere(s) à tester avant de tenter la preg
43
-	 * @param string $start_with
44
-	 *   caractere(s) par lesquels commencent l'expression recherchée (permet de démarrer la preg à la prochaine occurence de cette chaine)
45
-	 * @param string $preg
46
-	 *   preg utilisée pour la collecte
47
-	 * @param int $max_items
48
-	 *   pour limiter le nombre de preg collectée (pour la detection simple de présence par exemple)
49
-	 * @return array
50
-	 */
51
-	protected static function collecteur(string $texte, string $if_chars, string $start_with, string $preg, int $max_items = 0): array {
52
-
53
-		$collection = [];
54
-		$pos = 0;
55
-		while (
56
-			(!$if_chars || str_contains($texte, $if_chars))
57
-			&& ($next = ($start_with ? strpos($texte, $start_with, $pos) : $pos)) !== false
58
-			&& preg_match($preg, $texte, $r, PREG_OFFSET_CAPTURE, $next)
59
-		) {
60
-			$found_pos = $r[0][1];
61
-			$found_length = strlen($r[0][0]);
62
-			$match = [
63
-				'raw' => $r[0][0],
64
-				'match' => array_column($r, 0),
65
-				'pos' => $found_pos,
66
-				'length' => $found_length
67
-			];
68
-
69
-			$collection[] = $match;
70
-
71
-			if ($max_items && count($collection) === $max_items) {
72
-				break;
73
-			}
74
-
75
-			$pos = $match['pos'] + $match['length'];
76
-		}
77
-
78
-		return $collection;
79
-	}
80
-
81
-	/**
82
-	 * Sanitizer une collection d'occurences
83
-	 */
84
-	protected function sanitizer_collection(array $collection, string $sanitize_callback): array {
85
-		foreach ($collection as &$c) {
86
-			$c['raw'] = $sanitize_callback($c['raw']);
87
-		}
88
-
89
-		return $collection;
90
-	}
91
-
92
-	/**
93
-	 * @return array
94
-	 */
95
-	public function collecter(string $texte, array $options = []): array {
96
-		return [];
97
-	}
98
-
99
-	public function detecter($texte): bool {
100
-		if (!empty($this->markId) && str_contains((string) $texte, $this->markId)) {
101
-			return true;
102
-		}
103
-		return !empty($this->collecter($texte, ['detecter_presence' => true]));
104
-	}
105
-
106
-	/**
107
-	 * Echapper les occurences de la collecte par un texte neutre du point de vue HTML
108
-	 *
109
-	 * @see retablir()
110
-	 * @param string $texte
111
-	 * @param array $options
112
-	 *   string $sanitize_callback
113
-	 * @return array
114
-	 *   texte, marqueur utilise pour echapper les modeles
115
-	 */
116
-	public function echapper(string $texte, array $options = []): string {
117
-		if (!function_exists('creer_uniqid')) {
118
-			include_spip('inc/acces');
119
-		}
120
-
121
-		$collection = $this->collecter($texte, $options);
122
-		if (!empty($options['sanitize_callback']) && is_callable($options['sanitize_callback'])) {
123
-			$collection = $this->sanitizer_collection($collection, $options['sanitize_callback']);
124
-		}
125
-
126
-		if ($collection !== []) {
127
-			if (empty($this->markId)) {
128
-				// generer un marqueur qui n'existe pas dans le texte
129
-				do {
130
-					$this->markId = substr(md5(uniqid(static::class, 1)), 0, 7);
131
-					$this->markId = '@|' . static::$markPrefix . $this->markId . '|';
132
-				} while (str_contains($texte, $this->markId));
133
-			}
134
-
135
-			$offset_pos = 0;
136
-			foreach ($collection as $c) {
137
-				$rempl = $this->markId . base64_encode((string) $c['raw']) . '|@';
138
-				$texte = substr_replace($texte, $rempl, $c['pos'] + $offset_pos, $c['length']);
139
-				$offset_pos += strlen($rempl) - $c['length'];
140
-			}
141
-		}
142
-
143
-		return $texte;
144
-	}
145
-
146
-
147
-	/**
148
-	 * Retablir les occurences échappées précédemment
149
-	 *
150
-	 * @see echapper()
151
-	 */
152
-	function retablir(string $texte): string {
153
-
154
-		if (!empty($this->markId)) {
155
-			$lm = strlen($this->markId);
156
-			$pos = 0;
157
-			while (
158
-				($p = strpos($texte, $this->markId, $pos)) !== false
159
-				&& ($end = strpos($texte, '|@', $p + $lm))
160
-			) {
161
-				$base64 = substr($texte, $p + $lm, $end - ($p + $lm));
162
-				if ($c = base64_decode($base64, true)) {
163
-					$texte = substr_replace($texte, $c, $p, $end + 2 - $p);
164
-					$pos = $p + strlen($c);
165
-				}
166
-				else {
167
-					$pos = $end;
168
-				}
169
-			}
170
-		}
171
-
172
-		return $texte;
173
-	}
174
-
175
-	/**
176
-	 * Detecter si un texte contient des balises bloc ou non
177
-	 */
178
-	public static function echappementTexteContientBaliseBloc(string $texte): bool {
179
-		static $pregBalisesBloc;
180
-
181
-		if ($pregBalisesBloc === null) {
182
-			$pregBalisesBloc = ',</?(' . implode('|', static::$listeBalisesBloc) . ')[>[:space:]],iS';
183
-		}
184
-		return (str_contains($texte, '<') && preg_match($pregBalisesBloc, $texte)) ? true : false;
185
-	}
186
-
187
-	/**
188
-	 * Creer un bloc base64 correspondant a $texte ; au besoin en marquant
189
-	 * une $source differente ;
190
-	 * si $isBloc n'est pas fourni, le script detecte automagiquement si ce qu'on
191
-	 * echappe est un div ou un span
192
-	 */
193
-	public static function echappementHtmlBase64(string $texte, string $source = '', ?bool $isBloc = null, array $attributs = []): string {
194
-
195
-		if ($texte === '') {
196
-			return '';
197
-		}
198
-
199
-		// Tester si on echappe en span ou en div
200
-		$isBloc ??= self::echappementTexteContientBaliseBloc($texte);
201
-		$tag = $isBloc ? 'div' : 'span';
202
-		$atts = '';
203
-		if (!empty($attributs)) {
204
-			if (!function_exists('attribut_html')) {
205
-				include_spip('inc/filtres');
206
-			}
207
-			foreach ($attributs as $k => $v) {
208
-				$atts .= " $k=\"" . \attribut_html($v) . '"';
209
-			}
210
-		}
211
-
212
-		// Decouper en morceaux, base64 a des probleme selon la taille de la pile
213
-		$taille = 30000;
214
-		$return = '';
215
-		for ($i = 0; $i < strlen($texte); $i += $taille) {
216
-			// Convertir en base64 et cacher dans un attribut
217
-			// utiliser les " pour eviter le re-encodage de ' et &#8217
218
-			$base64 = base64_encode(substr($texte, $i, $taille));
219
-			$return .= "<$tag class=\"base64$source\" title=\"$base64\"{$atts}></$tag>";
220
-		}
221
-
222
-		return $return;
223
-	}
224
-
225
-
226
-	/**
227
-	 * Rétablir les contenus échappés dans un texte en <(div|span) class="base64..."></(div|span)>
228
-	 * Rq: $source sert a faire des echappements "a soi" qui ne sont pas nettoyes
229
-	 * par propre() : exemple dans multi et dans typo()
230
-	 *
231
-	 * @see echappementHtmlBase64()
232
-	 */
233
-	public static function retablir_depuisHtmlBase64(string $texte, string $source = '', string $filtre = ''): string {
234
-		if (str_contains($texte, "base64$source")) {
235
-			# spip_logger()->info(spip_htmlspecialchars($texte));  ## pour les curieux
236
-			$max_prof = 5;
237
-			$encore = true;
238
-			while ($encore && str_contains($texte, 'base64' . $source) && $max_prof--) {
239
-				$encore = false;
240
-				foreach (['span', 'div'] as $tag) {
241
-					$htmlTagCollecteur = new HtmlTag(
242
-						$tag,
243
-						"@<{$tag}\s(class=['\"]base64{$source}['\"]\stitle=['\"]([^'\">]*)['\"][^>]*?)(/?)>\s*</{$tag}>@isS",
244
-						''
245
-					);
246
-					$collection = $htmlTagCollecteur->collecter($texte);
247
-					if (!empty($collection)) {
248
-						$collection = array_reverse($collection);
249
-						foreach ($collection as $c) {
250
-							$title = $c['match'][2];
251
-							if ($title && ($rempl = base64_decode($title, true))) {
252
-								$encore = true;
253
-								// recherche d'attributs supplementaires
254
-								$at = [];
255
-								foreach (['lang', 'dir'] as $attr) {
256
-									if ($a = extraire_attribut($c['match'][0], $attr)) {
257
-										$at[$attr] = $a;
258
-									}
259
-								}
260
-								if ($at) {
261
-									$rempl = "<$tag>$rempl</$tag>";
262
-									foreach ($at as $attr => $a) {
263
-										$rempl = inserer_attribut($rempl, $attr, $a);
264
-									}
265
-								}
266
-								if ($filtre) {
267
-									$rempl = $filtre($rempl);
268
-								}
269
-								$texte = substr_replace($texte, $rempl, $c['pos'], $c['length']);
270
-							}
271
-						}
272
-					}
273
-				}
274
-			}
275
-		}
276
-		return $texte;
277
-	}
278
-
279
-	/**
280
-	 * @param callable|null $callback_function
281
-	 */
282
-	public function echapper_enHtmlBase64(string $texte, string $source = '', $callback_function = null, array $callback_options = []): string {
283
-		$collection = $this->collecter($texte);
284
-		if (!empty($collection)) {
285
-			$collection = array_reverse($collection);
286
-			foreach ($collection as $c) {
287
-				$echap = $c['raw'];
288
-				if ($callback_function) {
289
-					$echap = $callback_function($c, $callback_options);
290
-				}
291
-				$echap = self::echappementHtmlBase64($echap, $source);
292
-				$texte = substr_replace($texte, $echap, $c['pos'], $c['length']);
293
-			}
294
-		}
295
-		return $texte;
296
-	}
15
+    protected static string $markPrefix = 'COLLECT';
16
+    protected string $markId;
17
+
18
+    public static array $listeBalisesBloc = [
19
+        'address', 'applet', 'article', 'aside',
20
+        'blockquote', 'button',
21
+        'center',
22
+        'dl', 'dt', 'dd', 'div',
23
+        'fieldset', 'figure', 'figcaption', 'footer', 'form',
24
+        'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'hr', 'hgroup', 'head', 'header',
25
+        'iframe',
26
+        'li',
27
+        'map', 'marquee',
28
+        'nav', 'noscript',
29
+        'object', 'ol',
30
+        'pre',
31
+        'section',
32
+        'table', 'tr', 'td', 'th', 'tbody', 'foot', 'textarea',
33
+        'ul',
34
+        'script', 'style'
35
+    ];
36
+
37
+    /**
38
+     * Collecteur générique des occurences d'une preg dans un texte avec leurs positions et longueur
39
+     * @param string $texte
40
+     *   texte à analyser pour la collecte
41
+     * @param string $if_chars
42
+     *   caractere(s) à tester avant de tenter la preg
43
+     * @param string $start_with
44
+     *   caractere(s) par lesquels commencent l'expression recherchée (permet de démarrer la preg à la prochaine occurence de cette chaine)
45
+     * @param string $preg
46
+     *   preg utilisée pour la collecte
47
+     * @param int $max_items
48
+     *   pour limiter le nombre de preg collectée (pour la detection simple de présence par exemple)
49
+     * @return array
50
+     */
51
+    protected static function collecteur(string $texte, string $if_chars, string $start_with, string $preg, int $max_items = 0): array {
52
+
53
+        $collection = [];
54
+        $pos = 0;
55
+        while (
56
+            (!$if_chars || str_contains($texte, $if_chars))
57
+            && ($next = ($start_with ? strpos($texte, $start_with, $pos) : $pos)) !== false
58
+            && preg_match($preg, $texte, $r, PREG_OFFSET_CAPTURE, $next)
59
+        ) {
60
+            $found_pos = $r[0][1];
61
+            $found_length = strlen($r[0][0]);
62
+            $match = [
63
+                'raw' => $r[0][0],
64
+                'match' => array_column($r, 0),
65
+                'pos' => $found_pos,
66
+                'length' => $found_length
67
+            ];
68
+
69
+            $collection[] = $match;
70
+
71
+            if ($max_items && count($collection) === $max_items) {
72
+                break;
73
+            }
74
+
75
+            $pos = $match['pos'] + $match['length'];
76
+        }
77
+
78
+        return $collection;
79
+    }
80
+
81
+    /**
82
+     * Sanitizer une collection d'occurences
83
+     */
84
+    protected function sanitizer_collection(array $collection, string $sanitize_callback): array {
85
+        foreach ($collection as &$c) {
86
+            $c['raw'] = $sanitize_callback($c['raw']);
87
+        }
88
+
89
+        return $collection;
90
+    }
91
+
92
+    /**
93
+     * @return array
94
+     */
95
+    public function collecter(string $texte, array $options = []): array {
96
+        return [];
97
+    }
98
+
99
+    public function detecter($texte): bool {
100
+        if (!empty($this->markId) && str_contains((string) $texte, $this->markId)) {
101
+            return true;
102
+        }
103
+        return !empty($this->collecter($texte, ['detecter_presence' => true]));
104
+    }
105
+
106
+    /**
107
+     * Echapper les occurences de la collecte par un texte neutre du point de vue HTML
108
+     *
109
+     * @see retablir()
110
+     * @param string $texte
111
+     * @param array $options
112
+     *   string $sanitize_callback
113
+     * @return array
114
+     *   texte, marqueur utilise pour echapper les modeles
115
+     */
116
+    public function echapper(string $texte, array $options = []): string {
117
+        if (!function_exists('creer_uniqid')) {
118
+            include_spip('inc/acces');
119
+        }
120
+
121
+        $collection = $this->collecter($texte, $options);
122
+        if (!empty($options['sanitize_callback']) && is_callable($options['sanitize_callback'])) {
123
+            $collection = $this->sanitizer_collection($collection, $options['sanitize_callback']);
124
+        }
125
+
126
+        if ($collection !== []) {
127
+            if (empty($this->markId)) {
128
+                // generer un marqueur qui n'existe pas dans le texte
129
+                do {
130
+                    $this->markId = substr(md5(uniqid(static::class, 1)), 0, 7);
131
+                    $this->markId = '@|' . static::$markPrefix . $this->markId . '|';
132
+                } while (str_contains($texte, $this->markId));
133
+            }
134
+
135
+            $offset_pos = 0;
136
+            foreach ($collection as $c) {
137
+                $rempl = $this->markId . base64_encode((string) $c['raw']) . '|@';
138
+                $texte = substr_replace($texte, $rempl, $c['pos'] + $offset_pos, $c['length']);
139
+                $offset_pos += strlen($rempl) - $c['length'];
140
+            }
141
+        }
142
+
143
+        return $texte;
144
+    }
145
+
146
+
147
+    /**
148
+     * Retablir les occurences échappées précédemment
149
+     *
150
+     * @see echapper()
151
+     */
152
+    function retablir(string $texte): string {
153
+
154
+        if (!empty($this->markId)) {
155
+            $lm = strlen($this->markId);
156
+            $pos = 0;
157
+            while (
158
+                ($p = strpos($texte, $this->markId, $pos)) !== false
159
+                && ($end = strpos($texte, '|@', $p + $lm))
160
+            ) {
161
+                $base64 = substr($texte, $p + $lm, $end - ($p + $lm));
162
+                if ($c = base64_decode($base64, true)) {
163
+                    $texte = substr_replace($texte, $c, $p, $end + 2 - $p);
164
+                    $pos = $p + strlen($c);
165
+                }
166
+                else {
167
+                    $pos = $end;
168
+                }
169
+            }
170
+        }
171
+
172
+        return $texte;
173
+    }
174
+
175
+    /**
176
+     * Detecter si un texte contient des balises bloc ou non
177
+     */
178
+    public static function echappementTexteContientBaliseBloc(string $texte): bool {
179
+        static $pregBalisesBloc;
180
+
181
+        if ($pregBalisesBloc === null) {
182
+            $pregBalisesBloc = ',</?(' . implode('|', static::$listeBalisesBloc) . ')[>[:space:]],iS';
183
+        }
184
+        return (str_contains($texte, '<') && preg_match($pregBalisesBloc, $texte)) ? true : false;
185
+    }
186
+
187
+    /**
188
+     * Creer un bloc base64 correspondant a $texte ; au besoin en marquant
189
+     * une $source differente ;
190
+     * si $isBloc n'est pas fourni, le script detecte automagiquement si ce qu'on
191
+     * echappe est un div ou un span
192
+     */
193
+    public static function echappementHtmlBase64(string $texte, string $source = '', ?bool $isBloc = null, array $attributs = []): string {
194
+
195
+        if ($texte === '') {
196
+            return '';
197
+        }
198
+
199
+        // Tester si on echappe en span ou en div
200
+        $isBloc ??= self::echappementTexteContientBaliseBloc($texte);
201
+        $tag = $isBloc ? 'div' : 'span';
202
+        $atts = '';
203
+        if (!empty($attributs)) {
204
+            if (!function_exists('attribut_html')) {
205
+                include_spip('inc/filtres');
206
+            }
207
+            foreach ($attributs as $k => $v) {
208
+                $atts .= " $k=\"" . \attribut_html($v) . '"';
209
+            }
210
+        }
211
+
212
+        // Decouper en morceaux, base64 a des probleme selon la taille de la pile
213
+        $taille = 30000;
214
+        $return = '';
215
+        for ($i = 0; $i < strlen($texte); $i += $taille) {
216
+            // Convertir en base64 et cacher dans un attribut
217
+            // utiliser les " pour eviter le re-encodage de ' et &#8217
218
+            $base64 = base64_encode(substr($texte, $i, $taille));
219
+            $return .= "<$tag class=\"base64$source\" title=\"$base64\"{$atts}></$tag>";
220
+        }
221
+
222
+        return $return;
223
+    }
224
+
225
+
226
+    /**
227
+     * Rétablir les contenus échappés dans un texte en <(div|span) class="base64..."></(div|span)>
228
+     * Rq: $source sert a faire des echappements "a soi" qui ne sont pas nettoyes
229
+     * par propre() : exemple dans multi et dans typo()
230
+     *
231
+     * @see echappementHtmlBase64()
232
+     */
233
+    public static function retablir_depuisHtmlBase64(string $texte, string $source = '', string $filtre = ''): string {
234
+        if (str_contains($texte, "base64$source")) {
235
+            # spip_logger()->info(spip_htmlspecialchars($texte));  ## pour les curieux
236
+            $max_prof = 5;
237
+            $encore = true;
238
+            while ($encore && str_contains($texte, 'base64' . $source) && $max_prof--) {
239
+                $encore = false;
240
+                foreach (['span', 'div'] as $tag) {
241
+                    $htmlTagCollecteur = new HtmlTag(
242
+                        $tag,
243
+                        "@<{$tag}\s(class=['\"]base64{$source}['\"]\stitle=['\"]([^'\">]*)['\"][^>]*?)(/?)>\s*</{$tag}>@isS",
244
+                        ''
245
+                    );
246
+                    $collection = $htmlTagCollecteur->collecter($texte);
247
+                    if (!empty($collection)) {
248
+                        $collection = array_reverse($collection);
249
+                        foreach ($collection as $c) {
250
+                            $title = $c['match'][2];
251
+                            if ($title && ($rempl = base64_decode($title, true))) {
252
+                                $encore = true;
253
+                                // recherche d'attributs supplementaires
254
+                                $at = [];
255
+                                foreach (['lang', 'dir'] as $attr) {
256
+                                    if ($a = extraire_attribut($c['match'][0], $attr)) {
257
+                                        $at[$attr] = $a;
258
+                                    }
259
+                                }
260
+                                if ($at) {
261
+                                    $rempl = "<$tag>$rempl</$tag>";
262
+                                    foreach ($at as $attr => $a) {
263
+                                        $rempl = inserer_attribut($rempl, $attr, $a);
264
+                                    }
265
+                                }
266
+                                if ($filtre) {
267
+                                    $rempl = $filtre($rempl);
268
+                                }
269
+                                $texte = substr_replace($texte, $rempl, $c['pos'], $c['length']);
270
+                            }
271
+                        }
272
+                    }
273
+                }
274
+            }
275
+        }
276
+        return $texte;
277
+    }
278
+
279
+    /**
280
+     * @param callable|null $callback_function
281
+     */
282
+    public function echapper_enHtmlBase64(string $texte, string $source = '', $callback_function = null, array $callback_options = []): string {
283
+        $collection = $this->collecter($texte);
284
+        if (!empty($collection)) {
285
+            $collection = array_reverse($collection);
286
+            foreach ($collection as $c) {
287
+                $echap = $c['raw'];
288
+                if ($callback_function) {
289
+                    $echap = $callback_function($c, $callback_options);
290
+                }
291
+                $echap = self::echappementHtmlBase64($echap, $source);
292
+                $texte = substr_replace($texte, $echap, $c['pos'], $c['length']);
293
+            }
294
+        }
295
+        return $texte;
296
+    }
297 297
 }
Please login to merge, or discard this patch.
Spacing   +5 added lines, -5 removed lines patch added patch discarded remove patch
@@ -128,13 +128,13 @@  discard block
 block discarded – undo
128 128
 				// generer un marqueur qui n'existe pas dans le texte
129 129
 				do {
130 130
 					$this->markId = substr(md5(uniqid(static::class, 1)), 0, 7);
131
-					$this->markId = '@|' . static::$markPrefix . $this->markId . '|';
131
+					$this->markId = '@|'.static::$markPrefix.$this->markId.'|';
132 132
 				} while (str_contains($texte, $this->markId));
133 133
 			}
134 134
 
135 135
 			$offset_pos = 0;
136 136
 			foreach ($collection as $c) {
137
-				$rempl = $this->markId . base64_encode((string) $c['raw']) . '|@';
137
+				$rempl = $this->markId.base64_encode((string) $c['raw']).'|@';
138 138
 				$texte = substr_replace($texte, $rempl, $c['pos'] + $offset_pos, $c['length']);
139 139
 				$offset_pos += strlen($rempl) - $c['length'];
140 140
 			}
@@ -179,7 +179,7 @@  discard block
 block discarded – undo
179 179
 		static $pregBalisesBloc;
180 180
 
181 181
 		if ($pregBalisesBloc === null) {
182
-			$pregBalisesBloc = ',</?(' . implode('|', static::$listeBalisesBloc) . ')[>[:space:]],iS';
182
+			$pregBalisesBloc = ',</?('.implode('|', static::$listeBalisesBloc).')[>[:space:]],iS';
183 183
 		}
184 184
 		return (str_contains($texte, '<') && preg_match($pregBalisesBloc, $texte)) ? true : false;
185 185
 	}
@@ -205,7 +205,7 @@  discard block
 block discarded – undo
205 205
 				include_spip('inc/filtres');
206 206
 			}
207 207
 			foreach ($attributs as $k => $v) {
208
-				$atts .= " $k=\"" . \attribut_html($v) . '"';
208
+				$atts .= " $k=\"".\attribut_html($v).'"';
209 209
 			}
210 210
 		}
211 211
 
@@ -235,7 +235,7 @@  discard block
 block discarded – undo
235 235
 			# spip_logger()->info(spip_htmlspecialchars($texte));  ## pour les curieux
236 236
 			$max_prof = 5;
237 237
 			$encore = true;
238
-			while ($encore && str_contains($texte, 'base64' . $source) && $max_prof--) {
238
+			while ($encore && str_contains($texte, 'base64'.$source) && $max_prof--) {
239 239
 				$encore = false;
240 240
 				foreach (['span', 'div'] as $tag) {
241 241
 					$htmlTagCollecteur = new HtmlTag(
Please login to merge, or discard this patch.
ecrire/src/ErrorHandler.php 2 patches
Indentation   +24 added lines, -24 removed lines patch added patch discarded remove patch
@@ -7,33 +7,33 @@
 block discarded – undo
7 7
  * @internal
8 8
  */
9 9
 final class ErrorHandler {
10
-	static bool $done = false;
10
+    static bool $done = false;
11 11
 
12
-	public static function setup(?int $error_level = null): void {
13
-		if (!self::$done) {
14
-			self::$done = true;
15
-			error_reporting($error_level);
16
-			set_error_handler(self::user_deprecated(...), E_USER_DEPRECATED);
17
-		}
18
-	}
12
+    public static function setup(?int $error_level = null): void {
13
+        if (!self::$done) {
14
+            self::$done = true;
15
+            error_reporting($error_level);
16
+            set_error_handler(self::user_deprecated(...), E_USER_DEPRECATED);
17
+        }
18
+    }
19 19
 
20
-	/** Loger les `trigger_deprecated()` */
21
-	private static function user_deprecated(int $errno, string $errstr, string $errfile, int $errline): bool {
22
-		if (!(\E_USER_DEPRECATED & $errno)) {
23
-			return false;
24
-		}
20
+    /** Loger les `trigger_deprecated()` */
21
+    private static function user_deprecated(int $errno, string $errstr, string $errfile, int $errline): bool {
22
+        if (!(\E_USER_DEPRECATED & $errno)) {
23
+            return false;
24
+        }
25 25
 
26
-		$backtrace = debug_backtrace();
27
-		array_shift($backtrace);
28
-		do {
29
-			$t = array_shift($backtrace);
30
-			$fqdn = ($t['class'] ?? '') . ($t['type'] ?? '') . ($t['function'] ?? '');
31
-		} while (in_array($fqdn, ['trigger_error', 'trigger_deprecation']));
26
+        $backtrace = debug_backtrace();
27
+        array_shift($backtrace);
28
+        do {
29
+            $t = array_shift($backtrace);
30
+            $fqdn = ($t['class'] ?? '') . ($t['type'] ?? '') . ($t['function'] ?? '');
31
+        } while (in_array($fqdn, ['trigger_error', 'trigger_deprecation']));
32 32
 
33
-		$errfile = $t['file'];
34
-		$errline = $t['line'];
33
+        $errfile = $t['file'];
34
+        $errline = $t['line'];
35 35
 
36
-		spip_logger('deprecated')->info(sprintf('%s in %s on line %s', $errstr, $errfile, $errline));
37
-		return false;
38
-	}
36
+        spip_logger('deprecated')->info(sprintf('%s in %s on line %s', $errstr, $errfile, $errline));
37
+        return false;
38
+    }
39 39
 }
Please login to merge, or discard this patch.
Spacing   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -27,7 +27,7 @@
 block discarded – undo
27 27
 		array_shift($backtrace);
28 28
 		do {
29 29
 			$t = array_shift($backtrace);
30
-			$fqdn = ($t['class'] ?? '') . ($t['type'] ?? '') . ($t['function'] ?? '');
30
+			$fqdn = ($t['class'] ?? '').($t['type'] ?? '').($t['function'] ?? '');
31 31
 		} while (in_array($fqdn, ['trigger_error', 'trigger_deprecation']));
32 32
 
33 33
 		$errfile = $t['file'];
Please login to merge, or discard this patch.
ecrire/src/Afficher/Minipage/Admin.php 2 patches
Indentation   +142 added lines, -142 removed lines patch added patch discarded remove patch
@@ -15,146 +15,146 @@
 block discarded – undo
15 15
  * Présentation des pages simplifiées d’admin pour envoyer un message à un utilisateur
16 16
  **/
17 17
 class Admin extends Page {
18
-	public const TYPE = 'admin';
19
-	protected function setOptions(array $options) {
20
-		$options['couleur_fond'] = '#999';
21
-		if (empty($options['css_files'])) {
22
-			$options['css_files'] = [];
23
-		}
24
-		array_unshift($options['css_files'], find_in_theme('minipres.css'));
25
-
26
-		$options['page_title'] = ($options['titre'] ?? '');
27
-
28
-		return $options;
29
-	}
30
-
31
-
32
-	/**
33
-	 * Retourne le début d'une page HTML minimale (de type installation ou erreur)
34
-	 *
35
-	 * @param array $options
36
-	 * @return string
37
-	 *    Code HTML
38
-	 */
39
-	public function installDebutPage($options = []) {
40
-
41
-		$options = $this->setOptions($options);
42
-		return parent::ouvreBody($options)
43
-			. parent::ouvreCorps($options);
44
-	}
45
-
46
-	/**
47
-	 * Retourne le fin d'une page HTML minimale (de type installation ou erreur)
48
-	 *
49
-	 * @param array $options
50
-	 * @return string
51
-	 *    Code HTML
52
-	 */
53
-	public function installFinPage($options = []) {
54
-
55
-		$options = $this->setOptions($options);
56
-		return parent::fermeCorps($options)
57
-			. parent::fermeBody();
58
-	}
59
-
60
-
61
-	/**
62
-	 * Retourne une page HTML contenant, dans une présentation minimale,
63
-	 * le contenu transmis dans `$corps`.
64
-	 *
65
-	 * Appelée pour afficher un message d’erreur (l’utilisateur n’a pas
66
-	 * accès à cette page par exemple).
67
-	 *
68
-	 * Lorsqu’aucun argument n’est transmis, un header 403 est renvoyé,
69
-	 * ainsi qu’un message indiquant une interdiction d’accès.
70
-	 *
71
-	 * @param string $corps
72
-	 *   Corps de la page
73
-	 * @param array $options
74
-	 * @return string
75
-	 *   HTML de la page
76
-	 * @see  ouvreBody()
77
-	 *   string $titre : Titre à l'affichage (différent de $page_title)
78
-	 *   int $status : status de la page
79
-	 *   string $footer : pied de la box en remplacement du bouton retour par défaut
80
-	 * @uses ouvreBody()
81
-	 * @uses fermeBody()
82
-	 *
83
-	 */
84
-	public function page($corps = '', $options = []) {
85
-
86
-		$footer = '';
87
-
88
-		$titre = $options['titre'] ?? '';
89
-		if (!$titre) {
90
-			if (empty($corps) && !isset($options['status'])) {
91
-				$options['status'] = 403;
92
-			}
93
-
94
-			if (
95
-				!$titre = _request('action')
96
-				&& !$titre = _request('exec')
97
-				&& !$titre = _request('page')
98
-			) {
99
-				$titre = '?';
100
-			}
101
-
102
-			$titre = spip_htmlspecialchars($titre);
103
-
104
-			$titre = ($titre == 'install')
105
-				? _T('avis_espace_interdit')
106
-				: $titre . '&nbsp;: ' . _T('info_acces_interdit');
107
-
108
-			$statut = $GLOBALS['visiteur_session']['statut'] ?? '';
109
-			$nom = $GLOBALS['visiteur_session']['nom'] ?? '';
110
-
111
-			if ($statut != '0minirezo') {
112
-				$titre = _T('info_acces_interdit');
113
-			}
114
-
115
-			if ($statut && test_espace_prive()) {
116
-				$footer = bouton_action(_T('public:accueil_site'), generer_url_ecrire('accueil'));
117
-			}
118
-			elseif (!empty($_COOKIE['spip_admin'])) {
119
-				$footer = bouton_action(_T('public:lien_connecter'), generer_url_public('login'));
120
-			}
121
-			else {
122
-				$footer = bouton_action(_T('public:accueil_site'), $GLOBALS['meta']['adresse_site'] ?? '');
123
-			}
124
-
125
-			spip_logger('minipres')->info($nom . " $titre " . $_SERVER['REQUEST_URI']);
126
-
127
-			$options['footer'] = $footer;
128
-			if (empty($corps)) {
129
-				$corps = "<div class='msg-alert error'>"
130
-					. $titre
131
-					. '</div>';
132
-				$options['titre'] = '';
133
-			}
134
-			else {
135
-				$options['titre'] = $titre;
136
-			}
137
-		}
138
-		else {
139
-			$options['titre'] = $titre;
140
-		}
141
-		$options['page_title'] = $titre;
142
-
143
-		$options = $this->setOptions($options);
144
-
145
-		$html = parent::page($corps, $options);
146
-
147
-		if (!_AJAX) {
148
-			return $html;
149
-		} else {
150
-			include_spip('inc/headers');
151
-			include_spip('inc/actions');
152
-			$url = self('&', true);
153
-			foreach ($_POST as $v => $c) {
154
-				$url = parametre_url($url, $v, $c, '&');
155
-			}
156
-			ajax_retour('<div>' . $titre . redirige_formulaire($url) . '</div>', false);
157
-			return '';
158
-		}
159
-	}
18
+    public const TYPE = 'admin';
19
+    protected function setOptions(array $options) {
20
+        $options['couleur_fond'] = '#999';
21
+        if (empty($options['css_files'])) {
22
+            $options['css_files'] = [];
23
+        }
24
+        array_unshift($options['css_files'], find_in_theme('minipres.css'));
25
+
26
+        $options['page_title'] = ($options['titre'] ?? '');
27
+
28
+        return $options;
29
+    }
30
+
31
+
32
+    /**
33
+     * Retourne le début d'une page HTML minimale (de type installation ou erreur)
34
+     *
35
+     * @param array $options
36
+     * @return string
37
+     *    Code HTML
38
+     */
39
+    public function installDebutPage($options = []) {
40
+
41
+        $options = $this->setOptions($options);
42
+        return parent::ouvreBody($options)
43
+            . parent::ouvreCorps($options);
44
+    }
45
+
46
+    /**
47
+     * Retourne le fin d'une page HTML minimale (de type installation ou erreur)
48
+     *
49
+     * @param array $options
50
+     * @return string
51
+     *    Code HTML
52
+     */
53
+    public function installFinPage($options = []) {
54
+
55
+        $options = $this->setOptions($options);
56
+        return parent::fermeCorps($options)
57
+            . parent::fermeBody();
58
+    }
59
+
60
+
61
+    /**
62
+     * Retourne une page HTML contenant, dans une présentation minimale,
63
+     * le contenu transmis dans `$corps`.
64
+     *
65
+     * Appelée pour afficher un message d’erreur (l’utilisateur n’a pas
66
+     * accès à cette page par exemple).
67
+     *
68
+     * Lorsqu’aucun argument n’est transmis, un header 403 est renvoyé,
69
+     * ainsi qu’un message indiquant une interdiction d’accès.
70
+     *
71
+     * @param string $corps
72
+     *   Corps de la page
73
+     * @param array $options
74
+     * @return string
75
+     *   HTML de la page
76
+     * @see  ouvreBody()
77
+     *   string $titre : Titre à l'affichage (différent de $page_title)
78
+     *   int $status : status de la page
79
+     *   string $footer : pied de la box en remplacement du bouton retour par défaut
80
+     * @uses ouvreBody()
81
+     * @uses fermeBody()
82
+     *
83
+     */
84
+    public function page($corps = '', $options = []) {
85
+
86
+        $footer = '';
87
+
88
+        $titre = $options['titre'] ?? '';
89
+        if (!$titre) {
90
+            if (empty($corps) && !isset($options['status'])) {
91
+                $options['status'] = 403;
92
+            }
93
+
94
+            if (
95
+                !$titre = _request('action')
96
+                && !$titre = _request('exec')
97
+                && !$titre = _request('page')
98
+            ) {
99
+                $titre = '?';
100
+            }
101
+
102
+            $titre = spip_htmlspecialchars($titre);
103
+
104
+            $titre = ($titre == 'install')
105
+                ? _T('avis_espace_interdit')
106
+                : $titre . '&nbsp;: ' . _T('info_acces_interdit');
107
+
108
+            $statut = $GLOBALS['visiteur_session']['statut'] ?? '';
109
+            $nom = $GLOBALS['visiteur_session']['nom'] ?? '';
110
+
111
+            if ($statut != '0minirezo') {
112
+                $titre = _T('info_acces_interdit');
113
+            }
114
+
115
+            if ($statut && test_espace_prive()) {
116
+                $footer = bouton_action(_T('public:accueil_site'), generer_url_ecrire('accueil'));
117
+            }
118
+            elseif (!empty($_COOKIE['spip_admin'])) {
119
+                $footer = bouton_action(_T('public:lien_connecter'), generer_url_public('login'));
120
+            }
121
+            else {
122
+                $footer = bouton_action(_T('public:accueil_site'), $GLOBALS['meta']['adresse_site'] ?? '');
123
+            }
124
+
125
+            spip_logger('minipres')->info($nom . " $titre " . $_SERVER['REQUEST_URI']);
126
+
127
+            $options['footer'] = $footer;
128
+            if (empty($corps)) {
129
+                $corps = "<div class='msg-alert error'>"
130
+                    . $titre
131
+                    . '</div>';
132
+                $options['titre'] = '';
133
+            }
134
+            else {
135
+                $options['titre'] = $titre;
136
+            }
137
+        }
138
+        else {
139
+            $options['titre'] = $titre;
140
+        }
141
+        $options['page_title'] = $titre;
142
+
143
+        $options = $this->setOptions($options);
144
+
145
+        $html = parent::page($corps, $options);
146
+
147
+        if (!_AJAX) {
148
+            return $html;
149
+        } else {
150
+            include_spip('inc/headers');
151
+            include_spip('inc/actions');
152
+            $url = self('&', true);
153
+            foreach ($_POST as $v => $c) {
154
+                $url = parametre_url($url, $v, $c, '&');
155
+            }
156
+            ajax_retour('<div>' . $titre . redirige_formulaire($url) . '</div>', false);
157
+            return '';
158
+        }
159
+    }
160 160
 }
Please login to merge, or discard this patch.
Spacing   +3 added lines, -3 removed lines patch added patch discarded remove patch
@@ -103,7 +103,7 @@  discard block
 block discarded – undo
103 103
 
104 104
 			$titre = ($titre == 'install')
105 105
 				? _T('avis_espace_interdit')
106
-				: $titre . '&nbsp;: ' . _T('info_acces_interdit');
106
+				: $titre.'&nbsp;: '._T('info_acces_interdit');
107 107
 
108 108
 			$statut = $GLOBALS['visiteur_session']['statut'] ?? '';
109 109
 			$nom = $GLOBALS['visiteur_session']['nom'] ?? '';
@@ -122,7 +122,7 @@  discard block
 block discarded – undo
122 122
 				$footer = bouton_action(_T('public:accueil_site'), $GLOBALS['meta']['adresse_site'] ?? '');
123 123
 			}
124 124
 
125
-			spip_logger('minipres')->info($nom . " $titre " . $_SERVER['REQUEST_URI']);
125
+			spip_logger('minipres')->info($nom." $titre ".$_SERVER['REQUEST_URI']);
126 126
 
127 127
 			$options['footer'] = $footer;
128 128
 			if (empty($corps)) {
@@ -153,7 +153,7 @@  discard block
 block discarded – undo
153 153
 			foreach ($_POST as $v => $c) {
154 154
 				$url = parametre_url($url, $v, $c, '&');
155 155
 			}
156
-			ajax_retour('<div>' . $titre . redirige_formulaire($url) . '</div>', false);
156
+			ajax_retour('<div>'.$titre.redirige_formulaire($url).'</div>', false);
157 157
 			return '';
158 158
 		}
159 159
 	}
Please login to merge, or discard this patch.
ecrire/src/Compilateur/Iterateur/Decorator.php 2 patches
Indentation   +557 added lines, -557 removed lines patch added patch discarded remove patch
@@ -9,569 +9,569 @@
 block discarded – undo
9 9
 
10 10
 class Decorator extends FilterIterator
11 11
 {
12
-	/**
13
-	 * Conditions de filtrage
14
-	 * ie criteres de selection.
15
-	 *
16
-	 * @var array
17
-	 */
18
-	protected $filtre = [];
19
-
20
-	/**
21
-	 * Fonction de filtrage compilee a partir des criteres de filtre.
22
-	 *
23
-	 * @var string
24
-	 */
25
-	protected $func_filtre;
26
-
27
-	/**
28
-	 * Critere {offset, limit}.
29
-	 *
30
-	 * @var int
31
-	 * @var int
32
-	 */
33
-	protected $offset;
34
-	protected $limit;
35
-
36
-	/**
37
-	 * nombre d'elements recuperes depuis la position 0,
38
-	 * en tenant compte des filtres.
39
-	 *
40
-	 * @var int
41
-	 */
42
-	protected $fetched = 0;
43
-
44
-	/**
45
-	 * Y a t'il une erreur ?
46
-	 *
47
-	 * @var bool
48
-	 */
49
-	protected $err = false;
50
-
51
-	// Extension SPIP des iterateurs PHP
52
-	/**
53
-	 * type de l'iterateur.
54
-	 *
55
-	 * @var string
56
-	 */
57
-	protected $type;
58
-
59
-	/**
60
-	 * position courante de l'iterateur.
61
-	 *
62
-	 * @var int
63
-	 */
64
-	protected $pos = 0;
65
-
66
-	/**
67
-	 * nombre total resultats dans l'iterateur.
68
-	 *
69
-	 * @var int
70
-	 */
71
-	protected $total;
72
-
73
-	/**
74
-	 * nombre maximal de recherche pour $total
75
-	 * si l'iterateur n'implemente pas de fonction specifique.
76
-	 */
77
-	protected $max = 100000;
78
-
79
-	protected LoggerInterface $logger;
80
-
81
-	/**
82
-	 * Liste des champs a inserer dans les $row
83
-	 * retournes par ->fetch().
84
-	 */
85
-	protected $select = [];
86
-	private readonly Iterator $iter;
87
-
88
-	public function __construct(
89
-		Iterator $iter,
90
-		/** Parametres de l'iterateur */
91
-		protected array $command,
92
-		/** Infos du compilateur */
93
-		protected array $info
94
-	) {
95
-		$this->logger = spip_logger(); // FIXME: inject it.
96
-		parent::__construct($iter);
97
-		parent::rewind(); // remettre a la premiere position (bug? connu de FilterIterator)
98
-
99
-		// recuperer l'iterateur transmis
100
-		$this->iter = $this->getInnerIterator();
101
-
102
-		// chercher la liste des champs a retourner par
103
-		// fetch si l'objet ne les calcule pas tout seul
104
-		if (!method_exists($this->iter, 'fetch')) {
105
-			$this->calculer_select();
106
-			$this->calculer_filtres();
107
-		}
108
-
109
-		// emptyIterator critere {si} faux n'a pas d'erreur !
110
-		if (property_exists($this->iter, 'err') && $this->iter->err !== null) {
111
-			$this->err = $this->iter->err;
112
-		}
113
-
114
-		// pas d'init a priori, le calcul ne sera fait qu'en cas de besoin (provoque une double requete souvent inutile en sqlite)
115
-		//$this->total = $this->count();
116
-	}
117
-
118
-	/**
119
-	 * Drapeau a activer en cas d'echec
120
-	 * (select SQL errone, non chargement des DATA, etc).
121
-	 */
122
-	public function err() {
123
-		if (method_exists($this->iter, 'err')) {
124
-			return $this->iter->err();
125
-		}
126
-		if (property_exists($this->iter, 'err')) {
127
-			return $this->iter->err;
128
-		}
129
-
130
-		return false;
131
-	}
132
-
133
-	// recuperer la valeur d'une balise #X
134
-	// en fonction des methodes
135
-	// et proprietes disponibles
136
-	public function get_select($nom) {
137
-		if (is_object($this->iter) && method_exists($this->iter, $nom)) {
138
-			try {
139
-				return $this->iter->{$nom}();
140
-			} catch (Exception) {
141
-				// #GETCHILDREN sur un fichier de DirectoryIterator ...
142
-				$this->logger->info("Methode {$nom} en echec sur " . $this->iter::class);
143
-				$this->logger->info("Cela peut être normal : retour d'une ligne de resultat ne pouvant pas calculer cette methode");
144
-
145
-				return '';
146
-			}
147
-		}
148
-		/*
12
+    /**
13
+     * Conditions de filtrage
14
+     * ie criteres de selection.
15
+     *
16
+     * @var array
17
+     */
18
+    protected $filtre = [];
19
+
20
+    /**
21
+     * Fonction de filtrage compilee a partir des criteres de filtre.
22
+     *
23
+     * @var string
24
+     */
25
+    protected $func_filtre;
26
+
27
+    /**
28
+     * Critere {offset, limit}.
29
+     *
30
+     * @var int
31
+     * @var int
32
+     */
33
+    protected $offset;
34
+    protected $limit;
35
+
36
+    /**
37
+     * nombre d'elements recuperes depuis la position 0,
38
+     * en tenant compte des filtres.
39
+     *
40
+     * @var int
41
+     */
42
+    protected $fetched = 0;
43
+
44
+    /**
45
+     * Y a t'il une erreur ?
46
+     *
47
+     * @var bool
48
+     */
49
+    protected $err = false;
50
+
51
+    // Extension SPIP des iterateurs PHP
52
+    /**
53
+     * type de l'iterateur.
54
+     *
55
+     * @var string
56
+     */
57
+    protected $type;
58
+
59
+    /**
60
+     * position courante de l'iterateur.
61
+     *
62
+     * @var int
63
+     */
64
+    protected $pos = 0;
65
+
66
+    /**
67
+     * nombre total resultats dans l'iterateur.
68
+     *
69
+     * @var int
70
+     */
71
+    protected $total;
72
+
73
+    /**
74
+     * nombre maximal de recherche pour $total
75
+     * si l'iterateur n'implemente pas de fonction specifique.
76
+     */
77
+    protected $max = 100000;
78
+
79
+    protected LoggerInterface $logger;
80
+
81
+    /**
82
+     * Liste des champs a inserer dans les $row
83
+     * retournes par ->fetch().
84
+     */
85
+    protected $select = [];
86
+    private readonly Iterator $iter;
87
+
88
+    public function __construct(
89
+        Iterator $iter,
90
+        /** Parametres de l'iterateur */
91
+        protected array $command,
92
+        /** Infos du compilateur */
93
+        protected array $info
94
+    ) {
95
+        $this->logger = spip_logger(); // FIXME: inject it.
96
+        parent::__construct($iter);
97
+        parent::rewind(); // remettre a la premiere position (bug? connu de FilterIterator)
98
+
99
+        // recuperer l'iterateur transmis
100
+        $this->iter = $this->getInnerIterator();
101
+
102
+        // chercher la liste des champs a retourner par
103
+        // fetch si l'objet ne les calcule pas tout seul
104
+        if (!method_exists($this->iter, 'fetch')) {
105
+            $this->calculer_select();
106
+            $this->calculer_filtres();
107
+        }
108
+
109
+        // emptyIterator critere {si} faux n'a pas d'erreur !
110
+        if (property_exists($this->iter, 'err') && $this->iter->err !== null) {
111
+            $this->err = $this->iter->err;
112
+        }
113
+
114
+        // pas d'init a priori, le calcul ne sera fait qu'en cas de besoin (provoque une double requete souvent inutile en sqlite)
115
+        //$this->total = $this->count();
116
+    }
117
+
118
+    /**
119
+     * Drapeau a activer en cas d'echec
120
+     * (select SQL errone, non chargement des DATA, etc).
121
+     */
122
+    public function err() {
123
+        if (method_exists($this->iter, 'err')) {
124
+            return $this->iter->err();
125
+        }
126
+        if (property_exists($this->iter, 'err')) {
127
+            return $this->iter->err;
128
+        }
129
+
130
+        return false;
131
+    }
132
+
133
+    // recuperer la valeur d'une balise #X
134
+    // en fonction des methodes
135
+    // et proprietes disponibles
136
+    public function get_select($nom) {
137
+        if (is_object($this->iter) && method_exists($this->iter, $nom)) {
138
+            try {
139
+                return $this->iter->{$nom}();
140
+            } catch (Exception) {
141
+                // #GETCHILDREN sur un fichier de DirectoryIterator ...
142
+                $this->logger->info("Methode {$nom} en echec sur " . $this->iter::class);
143
+                $this->logger->info("Cela peut être normal : retour d'une ligne de resultat ne pouvant pas calculer cette methode");
144
+
145
+                return '';
146
+            }
147
+        }
148
+        /*
149 149
 		if (property_exists($this->iter, $nom)) {
150 150
 			return $this->iter->$nom;
151 151
 		}*/
152
-		// cle et valeur par defaut
153
-		// ICI PLANTAGE SI ON NE CONTROLE PAS $nom
154
-		if (
155
-			in_array($nom, ['cle', 'valeur'])
156
-			&& method_exists($this, $nom)
157
-		) {
158
-			return $this->{$nom}();
159
-		}
160
-
161
-		// Par defaut chercher en xpath dans la valeur()
162
-		return table_valeur($this->valeur(), $nom, null);
163
-	}
164
-
165
-	public function next(): void {
166
-		++$this->pos;
167
-		parent::next();
168
-	}
169
-
170
-	/**
171
-	 * revient au depart.
172
-	 */
173
-	public function rewind(): void {
174
-		$this->pos = 0;
175
-		$this->fetched = 0;
176
-		parent::rewind();
177
-	}
178
-
179
-	/**
180
-	 * aller a la position absolue n,
181
-	 * comptee depuis le debut.
182
-	 *
183
-	 * @param int    $n
184
-	 *                         absolute pos
185
-	 * @param string $continue
186
-	 *                         param for sql_ api
187
-	 *
188
-	 * @return bool
189
-	 *              success or fail if not implemented
190
-	 */
191
-	public function seek($n = 0, $continue = null) {
192
-		if ($this->func_filtre || !method_exists($this->iter, 'seek') || !$this->iter->seek($n)) {
193
-			$this->seek_loop($n);
194
-		}
195
-		$this->pos = $n;
196
-		$this->fetched = $n;
197
-
198
-		return true;
199
-	}
200
-
201
-	/**
202
-	 * Avancer de $saut pas.
203
-	 *
204
-	 * @param int $saut
205
-	 * @param int|null $max
206
-	 *
207
-	 * @return int
208
-	 */
209
-	public function skip($saut, $max = null) {
210
-		// pas de saut en arriere autorise pour cette fonction
211
-		if (($saut = (int) $saut) <= 0) {
212
-			return $this->pos;
213
-		}
214
-		$seek = $this->pos + $saut;
215
-		// si le saut fait depasser le maxi, on libere la resource
216
-		// et on sort
217
-		if (is_null($max)) {
218
-			$max = $this->count();
219
-		}
220
-
221
-		if ($seek >= $max || $seek >= $this->count()) {
222
-			// sortie plus rapide que de faire next() jusqu'a la fin !
223
-			$this->free();
224
-
225
-			return $max;
226
-		}
227
-
228
-		$this->seek($seek);
229
-
230
-		return $this->pos;
231
-	}
232
-
233
-	/**
234
-	 * Renvoyer un tableau des donnees correspondantes
235
-	 * a la position courante de l'iterateur
236
-	 * en controlant si on respecte le filtre
237
-	 * Appliquer aussi le critere {offset,limit}.
238
-	 *
239
-	 * @return array|bool
240
-	 */
241
-	public function fetch() {
242
-		if (method_exists($this->iter, 'fetch')) {
243
-			return $this->iter->fetch();
244
-		}
245
-		while (
246
-				$this->valid()
247
-				&& (!$this->accept() || $this->offset !== null && $this->fetched++ < $this->offset)
248
-		) {
249
-			$this->next();
250
-		}
251
-
252
-		if (!$this->valid()) {
253
-			return false;
254
-		}
255
-
256
-		if (
257
-				$this->limit !== null
258
-				&& $this->fetched > $this->offset + $this->limit
259
-		) {
260
-			return false;
261
-		}
262
-
263
-		$r = [];
264
-		foreach ($this->select as $nom) {
265
-			$r[$nom] = $this->get_select($nom);
266
-		}
267
-		$this->next();
268
-
269
-		return $r;
270
-	}
271
-
272
-	// retourner la cle pour #CLE
273
-	public function cle() {
274
-		return $this->key();
275
-	}
276
-
277
-	// retourner la valeur pour #VALEUR
278
-	public function valeur() {
279
-		return $this->current();
280
-	}
281
-
282
-	/**
283
-	 * Accepte-t-on l'entree courante lue ?
284
-	 * On execute les filtres pour le savoir.
285
-	 */
286
-	public function accept(): bool {
287
-		if ($f = $this->func_filtre) {
288
-			return $f();
289
-		}
290
-
291
-		return true;
292
-	}
293
-
294
-	/**
295
-	 * liberer la ressource.
296
-	 *
297
-	 * @return bool
298
-	 */
299
-	public function free() {
300
-		if (method_exists($this->iter, 'free')) {
301
-			$this->iter->free();
302
-		}
303
-		$this->pos = $this->total = 0;
304
-
305
-		return true;
306
-	}
307
-
308
-	/**
309
-	 * Compter le nombre total de resultats
310
-	 * pour #TOTAL_BOUCLE.
311
-	 *
312
-	 * @return int
313
-	 */
314
-	public function count() {
315
-		if (is_null($this->total)) {
316
-			if (
317
-				method_exists($this->iter, 'count')
318
-				&& !$this->func_filtre
319
-			) {
320
-				return $this->total = $this->iter->count();
321
-			}
322
-			// compter les lignes et rembobiner
323
-			$total = 0;
324
-			$pos = $this->pos; // sauver la position
325
-			$this->rewind();
326
-			while ($this->fetch() && $total < $this->max) {
327
-				++$total;
328
-			}
329
-			$this->seek($pos);
330
-			$this->total = $total;
331
-		}
332
-
333
-		return $this->total;
334
-	}
335
-
336
-	/**
337
-	 * Assembler le tableau de filtres traduits depuis les conditions SQL
338
-	 * les filtres vides ou null sont ignores.
339
-	 *
340
-	 * @param array $filtres
341
-	 * @param string $operateur
342
-	 *
343
-	 * @return null|string
344
-	 */
345
-	protected function assembler_filtres($filtres, $operateur = 'AND') {
346
-		$filtres_string = [];
347
-		foreach ($filtres as $k => $v) {
348
-			// si c'est un tableau de OR/AND + 2 sous-filtres, on recurse pour transformer en chaine
349
-			if (is_array($v) && in_array(reset($v), ['OR', 'AND'])) {
350
-				$op = array_shift($v);
351
-				$v = $this->assembler_filtres($v, $op);
352
-			}
353
-			if (is_null($v) || !is_string($v) || empty($v)) {
354
-				continue;
355
-			}
356
-			$filtres_string[] = $v;
357
-		}
358
-
359
-		if ($filtres_string === []) {
360
-			return null;
361
-		}
362
-
363
-		return '(' . implode(") {$operateur} (", $filtres_string) . ')';
364
-	}
365
-
366
-	/**
367
-	 * Traduire un element du tableau where SQL en un filtre.
368
-	 *
369
-	 * @param array|string $v
370
-	 *
371
-	 * @return null|array|string
372
-	 */
373
-	protected function traduire_condition_sql_en_filtre($v) {
374
-		if (is_array($v)) {
375
-			if ((count($v) >= 2) && ('REGEXP' == $v[0]) && ("'.*'" == $v[2])) {
376
-				return 'true';
377
-			}
378
-			if ((count($v) >= 2) && ('LIKE' == $v[0]) && ("'%'" == $v[2])) {
379
-				return 'true';
380
-			}
381
-			$op = $v[0] ?: $v;
382
-		} else {
383
-			$op = $v;
384
-		}
385
-		if (!$op || 1 == $op || '0=0' == $op) {
386
-			return 'true';
387
-		}
388
-		if ('0=1' === $op) {
389
-			return 'false';
390
-		}
391
-		// traiter {cle IN a,b} ou {valeur !IN a,b}
392
-		if (preg_match(',^\(([\w/]+)(\s+NOT)?\s+IN\s+(\(.*\))\)$,', (string) $op, $regs)) {
393
-			return $this->composer_filtre($regs[1], 'IN', $regs[3], $regs[2]);
394
-		}
395
-
396
-		// 3 possibilites : count($v) =
397
-		// * 1 : {x y} ; on recoit $v[0] = y
398
-		// * 2 : {x !op y} ; on recoit $v[0] = 'NOT', $v[1] = array() // array du type {x op y}
399
-		// * 3 : {x op y} ; on recoit $v[0] = 'op', $v[1] = x, $v[2] = y
400
-
401
-		// 1 : forcement traite par un critere, on passe
402
-		if (!$v || !is_array($v) || 1 == count($v)) {
403
-			return null; // sera ignore
404
-		}
405
-		if (2 == count($v) && is_array($v[1])) {
406
-			return $this->composer_filtre($v[1][1], $v[1][0], $v[1][2], 'NOT');
407
-		}
408
-		if (3 == count($v)) {
409
-			// traiter le OR/AND suivi de 2 valeurs
410
-			if (in_array($op, ['OR', 'AND'])) {
411
-				array_shift($v);
412
-				foreach (array_keys($v) as $k) {
413
-					$v[$k] = $this->traduire_condition_sql_en_filtre($v[$k]);
414
-					if (null === $v[$k]) {
415
-						unset($v[$k]);
416
-					} elseif ('true' === $v[$k]) {
417
-						if ('OR' === $op) {
418
-							return 'true';
419
-						}
420
-						if ('AND' === $op) {
421
-							unset($v[$k]);
422
-						}
423
-					} elseif ('false' === $v[$k]) {
424
-						if ('OR' === $op) {
425
-							unset($v[$k]);
426
-						}
427
-						if ('AND' === $op) {
428
-							return 'false';
429
-						}
430
-					}
431
-				}
432
-				if ($v === []) {
433
-					return null;
434
-				}
435
-				if (1 === count($v)) {
436
-					return reset($v);
437
-				}
438
-				array_unshift($v, $op);
439
-
440
-				return $v;
441
-			}
442
-
443
-			return $this->composer_filtre($v[1], $v[0], $v[2]);
444
-		}
445
-
446
-		return null;  // sera ignore
447
-	}
448
-
449
-	/**
450
-	 * Calculer un filtre sur un champ du tableau.
451
-	 *
452
-	 * @param string $cle
453
-	 * @param string $op
454
-	 * @param string $valeur
455
-	 * @param bool $not
456
-	 *
457
-	 * @return null|string
458
-	 */
459
-	protected function composer_filtre($cle, $op, $valeur, $not = false) {
460
-		if (
461
-			method_exists($this->iter, 'exception_des_criteres')
462
-			&& in_array($cle, $this->iter->exception_des_criteres())
463
-		) {
464
-			return null;
465
-		}
466
-		// TODO: analyser le filtre pour refuser ce qu'on ne sait pas traiter ?
467
-		// mais c'est normalement deja opere par calculer_critere_infixe()
468
-		// qui regarde la description 'desc' (en casse reelle d'ailleurs : {isDir=1}
469
-		// ne sera pas vu si l'on a defini desc['field']['isdir'] pour que #ISDIR soit present.
470
-		// il faudrait peut etre definir les 2 champs isDir et isdir... a reflechir...
471
-
472
-		// if (!in_array($cle, array('cle', 'valeur')))
473
-		//	return;
474
-
475
-		$a = '$this->get_select(\'' . $cle . '\')';
476
-
477
-		$filtre = '';
478
-
479
-		if ('REGEXP' == $op) {
480
-			$filtre = 'filtrer("match", ' . $a . ', ' . str_replace('\"', '"', (string) $valeur) . ')';
481
-			$op = '';
482
-		} else {
483
-			if ('LIKE' == $op) {
484
-				$valeur = str_replace(['\"', '_', '%'], ['"', '.', '.*'], preg_quote((string) $valeur));
485
-				$filtre = 'filtrer("match", ' . $a . ', ' . $valeur . ')';
486
-				$op = '';
487
-			} else {
488
-				if ('=' == $op) {
489
-					$op = '==';
490
-				} else {
491
-					if ('IN' == $op) {
492
-						$filtre = 'in_array(' . $a . ', array' . $valeur . ')';
493
-						$op = '';
494
-					} else {
495
-						if (!in_array($op, ['<', '<=', '>', '>='])) {
496
-							$this->logger->info('operateur non reconnu ' . $op); // [todo] mettre une erreur de squelette
497
-							$op = '';
498
-						}
499
-					}
500
-				}
501
-			}
502
-		}
503
-
504
-		if ($op) {
505
-			$filtre = $a . $op . str_replace('\"', '"', (string) $valeur);
506
-		}
507
-
508
-		if ($not) {
509
-			$filtre = "!({$filtre})";
510
-		}
511
-
512
-		return $filtre;
513
-	}
514
-
515
-	// calcule les elements a retournes par fetch()
516
-	// enleve les elements inutiles du select()
517
-	//
518
-	private function calculer_select() {
519
-		if ($select = &$this->command['select']) {
520
-			foreach ($select as $s) {
521
-				// /!\ $s = '.nom'
522
-				if ('.' == $s[0]) {
523
-					$s = substr((string) $s, 1);
524
-				}
525
-				$this->select[] = $s;
526
-			}
527
-		}
528
-	}
529
-
530
-	private function calculer_filtres() {
531
-		// Issu de calculer_select() de public/composer L.519
532
-		// TODO: externaliser...
533
-		//
534
-		// retirer les criteres vides:
535
-		// {X ?} avec X absent de l'URL
536
-		// {par #ENV{X}} avec X absent de l'URL
537
-		// IN sur collection vide (ce dernier devrait pouvoir etre fait a la compil)
538
-		if ($where = &$this->command['where']) {
539
-			foreach ($where as $k => $v) {
540
-				$this->filtre[] = $this->traduire_condition_sql_en_filtre($v);
541
-			}
542
-		}
543
-
544
-		// critere {2,7}
545
-		if (isset($this->command['limit']) && $this->command['limit']) {
546
-			$limit = explode(',', (string) $this->command['limit']);
547
-			$this->offset = $limit[0];
548
-			$this->limit = $limit[1];
549
-		}
550
-
551
-		// Creer la fonction de filtrage sur $this
552
-		if ($this->filtre) {
553
-			if ($filtres = $this->assembler_filtres($this->filtre)) {
554
-				$filtres = 'return ' . $filtres . ';';
555
-				$this->func_filtre = fn () => eval($filtres);
556
-			} else {
557
-				$this->func_filtre = null;
558
-			}
559
-		}
560
-	}
561
-
562
-	/*
152
+        // cle et valeur par defaut
153
+        // ICI PLANTAGE SI ON NE CONTROLE PAS $nom
154
+        if (
155
+            in_array($nom, ['cle', 'valeur'])
156
+            && method_exists($this, $nom)
157
+        ) {
158
+            return $this->{$nom}();
159
+        }
160
+
161
+        // Par defaut chercher en xpath dans la valeur()
162
+        return table_valeur($this->valeur(), $nom, null);
163
+    }
164
+
165
+    public function next(): void {
166
+        ++$this->pos;
167
+        parent::next();
168
+    }
169
+
170
+    /**
171
+     * revient au depart.
172
+     */
173
+    public function rewind(): void {
174
+        $this->pos = 0;
175
+        $this->fetched = 0;
176
+        parent::rewind();
177
+    }
178
+
179
+    /**
180
+     * aller a la position absolue n,
181
+     * comptee depuis le debut.
182
+     *
183
+     * @param int    $n
184
+     *                         absolute pos
185
+     * @param string $continue
186
+     *                         param for sql_ api
187
+     *
188
+     * @return bool
189
+     *              success or fail if not implemented
190
+     */
191
+    public function seek($n = 0, $continue = null) {
192
+        if ($this->func_filtre || !method_exists($this->iter, 'seek') || !$this->iter->seek($n)) {
193
+            $this->seek_loop($n);
194
+        }
195
+        $this->pos = $n;
196
+        $this->fetched = $n;
197
+
198
+        return true;
199
+    }
200
+
201
+    /**
202
+     * Avancer de $saut pas.
203
+     *
204
+     * @param int $saut
205
+     * @param int|null $max
206
+     *
207
+     * @return int
208
+     */
209
+    public function skip($saut, $max = null) {
210
+        // pas de saut en arriere autorise pour cette fonction
211
+        if (($saut = (int) $saut) <= 0) {
212
+            return $this->pos;
213
+        }
214
+        $seek = $this->pos + $saut;
215
+        // si le saut fait depasser le maxi, on libere la resource
216
+        // et on sort
217
+        if (is_null($max)) {
218
+            $max = $this->count();
219
+        }
220
+
221
+        if ($seek >= $max || $seek >= $this->count()) {
222
+            // sortie plus rapide que de faire next() jusqu'a la fin !
223
+            $this->free();
224
+
225
+            return $max;
226
+        }
227
+
228
+        $this->seek($seek);
229
+
230
+        return $this->pos;
231
+    }
232
+
233
+    /**
234
+     * Renvoyer un tableau des donnees correspondantes
235
+     * a la position courante de l'iterateur
236
+     * en controlant si on respecte le filtre
237
+     * Appliquer aussi le critere {offset,limit}.
238
+     *
239
+     * @return array|bool
240
+     */
241
+    public function fetch() {
242
+        if (method_exists($this->iter, 'fetch')) {
243
+            return $this->iter->fetch();
244
+        }
245
+        while (
246
+                $this->valid()
247
+                && (!$this->accept() || $this->offset !== null && $this->fetched++ < $this->offset)
248
+        ) {
249
+            $this->next();
250
+        }
251
+
252
+        if (!$this->valid()) {
253
+            return false;
254
+        }
255
+
256
+        if (
257
+                $this->limit !== null
258
+                && $this->fetched > $this->offset + $this->limit
259
+        ) {
260
+            return false;
261
+        }
262
+
263
+        $r = [];
264
+        foreach ($this->select as $nom) {
265
+            $r[$nom] = $this->get_select($nom);
266
+        }
267
+        $this->next();
268
+
269
+        return $r;
270
+    }
271
+
272
+    // retourner la cle pour #CLE
273
+    public function cle() {
274
+        return $this->key();
275
+    }
276
+
277
+    // retourner la valeur pour #VALEUR
278
+    public function valeur() {
279
+        return $this->current();
280
+    }
281
+
282
+    /**
283
+     * Accepte-t-on l'entree courante lue ?
284
+     * On execute les filtres pour le savoir.
285
+     */
286
+    public function accept(): bool {
287
+        if ($f = $this->func_filtre) {
288
+            return $f();
289
+        }
290
+
291
+        return true;
292
+    }
293
+
294
+    /**
295
+     * liberer la ressource.
296
+     *
297
+     * @return bool
298
+     */
299
+    public function free() {
300
+        if (method_exists($this->iter, 'free')) {
301
+            $this->iter->free();
302
+        }
303
+        $this->pos = $this->total = 0;
304
+
305
+        return true;
306
+    }
307
+
308
+    /**
309
+     * Compter le nombre total de resultats
310
+     * pour #TOTAL_BOUCLE.
311
+     *
312
+     * @return int
313
+     */
314
+    public function count() {
315
+        if (is_null($this->total)) {
316
+            if (
317
+                method_exists($this->iter, 'count')
318
+                && !$this->func_filtre
319
+            ) {
320
+                return $this->total = $this->iter->count();
321
+            }
322
+            // compter les lignes et rembobiner
323
+            $total = 0;
324
+            $pos = $this->pos; // sauver la position
325
+            $this->rewind();
326
+            while ($this->fetch() && $total < $this->max) {
327
+                ++$total;
328
+            }
329
+            $this->seek($pos);
330
+            $this->total = $total;
331
+        }
332
+
333
+        return $this->total;
334
+    }
335
+
336
+    /**
337
+     * Assembler le tableau de filtres traduits depuis les conditions SQL
338
+     * les filtres vides ou null sont ignores.
339
+     *
340
+     * @param array $filtres
341
+     * @param string $operateur
342
+     *
343
+     * @return null|string
344
+     */
345
+    protected function assembler_filtres($filtres, $operateur = 'AND') {
346
+        $filtres_string = [];
347
+        foreach ($filtres as $k => $v) {
348
+            // si c'est un tableau de OR/AND + 2 sous-filtres, on recurse pour transformer en chaine
349
+            if (is_array($v) && in_array(reset($v), ['OR', 'AND'])) {
350
+                $op = array_shift($v);
351
+                $v = $this->assembler_filtres($v, $op);
352
+            }
353
+            if (is_null($v) || !is_string($v) || empty($v)) {
354
+                continue;
355
+            }
356
+            $filtres_string[] = $v;
357
+        }
358
+
359
+        if ($filtres_string === []) {
360
+            return null;
361
+        }
362
+
363
+        return '(' . implode(") {$operateur} (", $filtres_string) . ')';
364
+    }
365
+
366
+    /**
367
+     * Traduire un element du tableau where SQL en un filtre.
368
+     *
369
+     * @param array|string $v
370
+     *
371
+     * @return null|array|string
372
+     */
373
+    protected function traduire_condition_sql_en_filtre($v) {
374
+        if (is_array($v)) {
375
+            if ((count($v) >= 2) && ('REGEXP' == $v[0]) && ("'.*'" == $v[2])) {
376
+                return 'true';
377
+            }
378
+            if ((count($v) >= 2) && ('LIKE' == $v[0]) && ("'%'" == $v[2])) {
379
+                return 'true';
380
+            }
381
+            $op = $v[0] ?: $v;
382
+        } else {
383
+            $op = $v;
384
+        }
385
+        if (!$op || 1 == $op || '0=0' == $op) {
386
+            return 'true';
387
+        }
388
+        if ('0=1' === $op) {
389
+            return 'false';
390
+        }
391
+        // traiter {cle IN a,b} ou {valeur !IN a,b}
392
+        if (preg_match(',^\(([\w/]+)(\s+NOT)?\s+IN\s+(\(.*\))\)$,', (string) $op, $regs)) {
393
+            return $this->composer_filtre($regs[1], 'IN', $regs[3], $regs[2]);
394
+        }
395
+
396
+        // 3 possibilites : count($v) =
397
+        // * 1 : {x y} ; on recoit $v[0] = y
398
+        // * 2 : {x !op y} ; on recoit $v[0] = 'NOT', $v[1] = array() // array du type {x op y}
399
+        // * 3 : {x op y} ; on recoit $v[0] = 'op', $v[1] = x, $v[2] = y
400
+
401
+        // 1 : forcement traite par un critere, on passe
402
+        if (!$v || !is_array($v) || 1 == count($v)) {
403
+            return null; // sera ignore
404
+        }
405
+        if (2 == count($v) && is_array($v[1])) {
406
+            return $this->composer_filtre($v[1][1], $v[1][0], $v[1][2], 'NOT');
407
+        }
408
+        if (3 == count($v)) {
409
+            // traiter le OR/AND suivi de 2 valeurs
410
+            if (in_array($op, ['OR', 'AND'])) {
411
+                array_shift($v);
412
+                foreach (array_keys($v) as $k) {
413
+                    $v[$k] = $this->traduire_condition_sql_en_filtre($v[$k]);
414
+                    if (null === $v[$k]) {
415
+                        unset($v[$k]);
416
+                    } elseif ('true' === $v[$k]) {
417
+                        if ('OR' === $op) {
418
+                            return 'true';
419
+                        }
420
+                        if ('AND' === $op) {
421
+                            unset($v[$k]);
422
+                        }
423
+                    } elseif ('false' === $v[$k]) {
424
+                        if ('OR' === $op) {
425
+                            unset($v[$k]);
426
+                        }
427
+                        if ('AND' === $op) {
428
+                            return 'false';
429
+                        }
430
+                    }
431
+                }
432
+                if ($v === []) {
433
+                    return null;
434
+                }
435
+                if (1 === count($v)) {
436
+                    return reset($v);
437
+                }
438
+                array_unshift($v, $op);
439
+
440
+                return $v;
441
+            }
442
+
443
+            return $this->composer_filtre($v[1], $v[0], $v[2]);
444
+        }
445
+
446
+        return null;  // sera ignore
447
+    }
448
+
449
+    /**
450
+     * Calculer un filtre sur un champ du tableau.
451
+     *
452
+     * @param string $cle
453
+     * @param string $op
454
+     * @param string $valeur
455
+     * @param bool $not
456
+     *
457
+     * @return null|string
458
+     */
459
+    protected function composer_filtre($cle, $op, $valeur, $not = false) {
460
+        if (
461
+            method_exists($this->iter, 'exception_des_criteres')
462
+            && in_array($cle, $this->iter->exception_des_criteres())
463
+        ) {
464
+            return null;
465
+        }
466
+        // TODO: analyser le filtre pour refuser ce qu'on ne sait pas traiter ?
467
+        // mais c'est normalement deja opere par calculer_critere_infixe()
468
+        // qui regarde la description 'desc' (en casse reelle d'ailleurs : {isDir=1}
469
+        // ne sera pas vu si l'on a defini desc['field']['isdir'] pour que #ISDIR soit present.
470
+        // il faudrait peut etre definir les 2 champs isDir et isdir... a reflechir...
471
+
472
+        // if (!in_array($cle, array('cle', 'valeur')))
473
+        //	return;
474
+
475
+        $a = '$this->get_select(\'' . $cle . '\')';
476
+
477
+        $filtre = '';
478
+
479
+        if ('REGEXP' == $op) {
480
+            $filtre = 'filtrer("match", ' . $a . ', ' . str_replace('\"', '"', (string) $valeur) . ')';
481
+            $op = '';
482
+        } else {
483
+            if ('LIKE' == $op) {
484
+                $valeur = str_replace(['\"', '_', '%'], ['"', '.', '.*'], preg_quote((string) $valeur));
485
+                $filtre = 'filtrer("match", ' . $a . ', ' . $valeur . ')';
486
+                $op = '';
487
+            } else {
488
+                if ('=' == $op) {
489
+                    $op = '==';
490
+                } else {
491
+                    if ('IN' == $op) {
492
+                        $filtre = 'in_array(' . $a . ', array' . $valeur . ')';
493
+                        $op = '';
494
+                    } else {
495
+                        if (!in_array($op, ['<', '<=', '>', '>='])) {
496
+                            $this->logger->info('operateur non reconnu ' . $op); // [todo] mettre une erreur de squelette
497
+                            $op = '';
498
+                        }
499
+                    }
500
+                }
501
+            }
502
+        }
503
+
504
+        if ($op) {
505
+            $filtre = $a . $op . str_replace('\"', '"', (string) $valeur);
506
+        }
507
+
508
+        if ($not) {
509
+            $filtre = "!({$filtre})";
510
+        }
511
+
512
+        return $filtre;
513
+    }
514
+
515
+    // calcule les elements a retournes par fetch()
516
+    // enleve les elements inutiles du select()
517
+    //
518
+    private function calculer_select() {
519
+        if ($select = &$this->command['select']) {
520
+            foreach ($select as $s) {
521
+                // /!\ $s = '.nom'
522
+                if ('.' == $s[0]) {
523
+                    $s = substr((string) $s, 1);
524
+                }
525
+                $this->select[] = $s;
526
+            }
527
+        }
528
+    }
529
+
530
+    private function calculer_filtres() {
531
+        // Issu de calculer_select() de public/composer L.519
532
+        // TODO: externaliser...
533
+        //
534
+        // retirer les criteres vides:
535
+        // {X ?} avec X absent de l'URL
536
+        // {par #ENV{X}} avec X absent de l'URL
537
+        // IN sur collection vide (ce dernier devrait pouvoir etre fait a la compil)
538
+        if ($where = &$this->command['where']) {
539
+            foreach ($where as $k => $v) {
540
+                $this->filtre[] = $this->traduire_condition_sql_en_filtre($v);
541
+            }
542
+        }
543
+
544
+        // critere {2,7}
545
+        if (isset($this->command['limit']) && $this->command['limit']) {
546
+            $limit = explode(',', (string) $this->command['limit']);
547
+            $this->offset = $limit[0];
548
+            $this->limit = $limit[1];
549
+        }
550
+
551
+        // Creer la fonction de filtrage sur $this
552
+        if ($this->filtre) {
553
+            if ($filtres = $this->assembler_filtres($this->filtre)) {
554
+                $filtres = 'return ' . $filtres . ';';
555
+                $this->func_filtre = fn () => eval($filtres);
556
+            } else {
557
+                $this->func_filtre = null;
558
+            }
559
+        }
560
+    }
561
+
562
+    /*
563 563
 	 * aller a la position $n en parcourant
564 564
 	 * un par un tous les elements
565 565
 	 */
566
-	private function seek_loop($n) {
567
-		if ($this->pos > $n) {
568
-			$this->rewind();
569
-		}
566
+    private function seek_loop($n) {
567
+        if ($this->pos > $n) {
568
+            $this->rewind();
569
+        }
570 570
 
571
-		while ($this->pos < $n && $this->valid()) {
572
-			$this->next();
573
-		}
571
+        while ($this->pos < $n && $this->valid()) {
572
+            $this->next();
573
+        }
574 574
 
575
-		return true;
576
-	}
575
+        return true;
576
+    }
577 577
 }
Please login to merge, or discard this patch.
Spacing   +10 added lines, -10 removed lines patch added patch discarded remove patch
@@ -139,7 +139,7 @@  discard block
 block discarded – undo
139 139
 				return $this->iter->{$nom}();
140 140
 			} catch (Exception) {
141 141
 				// #GETCHILDREN sur un fichier de DirectoryIterator ...
142
-				$this->logger->info("Methode {$nom} en echec sur " . $this->iter::class);
142
+				$this->logger->info("Methode {$nom} en echec sur ".$this->iter::class);
143 143
 				$this->logger->info("Cela peut être normal : retour d'une ligne de resultat ne pouvant pas calculer cette methode");
144 144
 
145 145
 				return '';
@@ -360,7 +360,7 @@  discard block
 block discarded – undo
360 360
 			return null;
361 361
 		}
362 362
 
363
-		return '(' . implode(") {$operateur} (", $filtres_string) . ')';
363
+		return '('.implode(") {$operateur} (", $filtres_string).')';
364 364
 	}
365 365
 
366 366
 	/**
@@ -443,7 +443,7 @@  discard block
 block discarded – undo
443 443
 			return $this->composer_filtre($v[1], $v[0], $v[2]);
444 444
 		}
445 445
 
446
-		return null;  // sera ignore
446
+		return null; // sera ignore
447 447
 	}
448 448
 
449 449
 	/**
@@ -472,28 +472,28 @@  discard block
 block discarded – undo
472 472
 		// if (!in_array($cle, array('cle', 'valeur')))
473 473
 		//	return;
474 474
 
475
-		$a = '$this->get_select(\'' . $cle . '\')';
475
+		$a = '$this->get_select(\''.$cle.'\')';
476 476
 
477 477
 		$filtre = '';
478 478
 
479 479
 		if ('REGEXP' == $op) {
480
-			$filtre = 'filtrer("match", ' . $a . ', ' . str_replace('\"', '"', (string) $valeur) . ')';
480
+			$filtre = 'filtrer("match", '.$a.', '.str_replace('\"', '"', (string) $valeur).')';
481 481
 			$op = '';
482 482
 		} else {
483 483
 			if ('LIKE' == $op) {
484 484
 				$valeur = str_replace(['\"', '_', '%'], ['"', '.', '.*'], preg_quote((string) $valeur));
485
-				$filtre = 'filtrer("match", ' . $a . ', ' . $valeur . ')';
485
+				$filtre = 'filtrer("match", '.$a.', '.$valeur.')';
486 486
 				$op = '';
487 487
 			} else {
488 488
 				if ('=' == $op) {
489 489
 					$op = '==';
490 490
 				} else {
491 491
 					if ('IN' == $op) {
492
-						$filtre = 'in_array(' . $a . ', array' . $valeur . ')';
492
+						$filtre = 'in_array('.$a.', array'.$valeur.')';
493 493
 						$op = '';
494 494
 					} else {
495 495
 						if (!in_array($op, ['<', '<=', '>', '>='])) {
496
-							$this->logger->info('operateur non reconnu ' . $op); // [todo] mettre une erreur de squelette
496
+							$this->logger->info('operateur non reconnu '.$op); // [todo] mettre une erreur de squelette
497 497
 							$op = '';
498 498
 						}
499 499
 					}
@@ -502,7 +502,7 @@  discard block
 block discarded – undo
502 502
 		}
503 503
 
504 504
 		if ($op) {
505
-			$filtre = $a . $op . str_replace('\"', '"', (string) $valeur);
505
+			$filtre = $a.$op.str_replace('\"', '"', (string) $valeur);
506 506
 		}
507 507
 
508 508
 		if ($not) {
@@ -551,7 +551,7 @@  discard block
 block discarded – undo
551 551
 		// Creer la fonction de filtrage sur $this
552 552
 		if ($this->filtre) {
553 553
 			if ($filtres = $this->assembler_filtres($this->filtre)) {
554
-				$filtres = 'return ' . $filtres . ';';
554
+				$filtres = 'return '.$filtres.';';
555 555
 				$this->func_filtre = fn () => eval($filtres);
556 556
 			} else {
557 557
 				$this->func_filtre = null;
Please login to merge, or discard this patch.
ecrire/src/Compilateur/Iterateur/Factory.php 1 patch
Indentation   +54 added lines, -54 removed lines patch added patch discarded remove patch
@@ -12,61 +12,61 @@
 block discarded – undo
12 12
  */
13 13
 class Factory
14 14
 {
15
-	public static function create($iterateur, $command, $info = null) {
16
-		$logger = spip_logger(); // FIXME: inject it.
17
-		$iter = null;
18
-		// cas des SI {si expression} analises tres tot
19
-		// pour eviter le chargement de tout iterateur
20
-		if (isset($command['si'])) {
21
-			foreach ($command['si'] as $si) {
22
-				if (!$si) {
23
-					// $command pour boucle SQL peut generer des erreurs de compilation
24
-					// s'il est transmis alors qu'on est dans un iterateur vide
25
-					return new Decorator(new EmptyIterator(), [], $info);
26
-				}
27
-			}
28
-		}
15
+    public static function create($iterateur, $command, $info = null) {
16
+        $logger = spip_logger(); // FIXME: inject it.
17
+        $iter = null;
18
+        // cas des SI {si expression} analises tres tot
19
+        // pour eviter le chargement de tout iterateur
20
+        if (isset($command['si'])) {
21
+            foreach ($command['si'] as $si) {
22
+                if (!$si) {
23
+                    // $command pour boucle SQL peut generer des erreurs de compilation
24
+                    // s'il est transmis alors qu'on est dans un iterateur vide
25
+                    return new Decorator(new EmptyIterator(), [], $info);
26
+                }
27
+            }
28
+        }
29 29
 
30
-		// chercher un iterateur PHP existant (par exemple dans SPL)
31
-		// (il faudrait passer l'argument ->sql_serveur
32
-		// pour etre certain qu'on est sur un "php:")
33
-		if (class_exists($iterateur)) {
34
-			$a = $command['args'] ?? [];
30
+        // chercher un iterateur PHP existant (par exemple dans SPL)
31
+        // (il faudrait passer l'argument ->sql_serveur
32
+        // pour etre certain qu'on est sur un "php:")
33
+        if (class_exists($iterateur)) {
34
+            $a = $command['args'] ?? [];
35 35
 
36
-			// permettre de passer un Iterateur directement {args #ITERATEUR} :
37
-			// si on recoit deja un iterateur en argument, on l'utilise
38
-			if ((is_countable($a) ? count($a) : 0) == 1 && is_object($a[0]) && is_subclass_of($a[0], \Iterator::class)) {
39
-				$iter = $a[0];
40
-			} else {
41
-				// sinon, on cree un iterateur du type donne
42
-				// arguments de creation de l'iterateur...
43
-				try {
44
-					$iter = new $iterateur(...$a);
45
-				} catch (Exception $e) {
46
-					$logger->info("Erreur de chargement de l'iterateur {$iterateur}");
47
-					$logger->info($e->getMessage());
48
-					$iter = new EmptyIterator();
49
-				}
50
-			}
51
-		} else {
52
-			// chercher la classe d'iterateur Iterateur/XXX
53
-			// definie dans le fichier src/Compilateur/Iterateur/xxx.php
54
-			// FIXME: déclarer quelque part les iterateurs supplémentaires
55
-			$class = __NAMESPACE__ . '\\' . ucfirst(strtolower((string) $iterateur));
56
-			if (!class_exists($class)) {
57
-				// historique
58
-				// Chercher IterateurXXX
59
-				include_spip('iterateur/' . strtolower($iterateur));
60
-				$class = 'Iterateur' . $iterateur;
61
-				if (!class_exists($class)) {
62
-					exit("Iterateur {$iterateur} non trouv&#233;");
63
-					// si l'iterateur n'existe pas, on se rabat sur le generique
64
-					// $iter = new EmptyIterator();
65
-				}
66
-			}
67
-			$iter = new $class($command, $info);
68
-		}
36
+            // permettre de passer un Iterateur directement {args #ITERATEUR} :
37
+            // si on recoit deja un iterateur en argument, on l'utilise
38
+            if ((is_countable($a) ? count($a) : 0) == 1 && is_object($a[0]) && is_subclass_of($a[0], \Iterator::class)) {
39
+                $iter = $a[0];
40
+            } else {
41
+                // sinon, on cree un iterateur du type donne
42
+                // arguments de creation de l'iterateur...
43
+                try {
44
+                    $iter = new $iterateur(...$a);
45
+                } catch (Exception $e) {
46
+                    $logger->info("Erreur de chargement de l'iterateur {$iterateur}");
47
+                    $logger->info($e->getMessage());
48
+                    $iter = new EmptyIterator();
49
+                }
50
+            }
51
+        } else {
52
+            // chercher la classe d'iterateur Iterateur/XXX
53
+            // definie dans le fichier src/Compilateur/Iterateur/xxx.php
54
+            // FIXME: déclarer quelque part les iterateurs supplémentaires
55
+            $class = __NAMESPACE__ . '\\' . ucfirst(strtolower((string) $iterateur));
56
+            if (!class_exists($class)) {
57
+                // historique
58
+                // Chercher IterateurXXX
59
+                include_spip('iterateur/' . strtolower($iterateur));
60
+                $class = 'Iterateur' . $iterateur;
61
+                if (!class_exists($class)) {
62
+                    exit("Iterateur {$iterateur} non trouv&#233;");
63
+                    // si l'iterateur n'existe pas, on se rabat sur le generique
64
+                    // $iter = new EmptyIterator();
65
+                }
66
+            }
67
+            $iter = new $class($command, $info);
68
+        }
69 69
 
70
-		return new Decorator($iter, $command, $info);
71
-	}
70
+        return new Decorator($iter, $command, $info);
71
+    }
72 72
 }
Please login to merge, or discard this patch.
ecrire/src/Compilateur/Iterateur/Data.php 2 patches
Indentation   +465 added lines, -465 removed lines patch added patch discarded remove patch
@@ -12,473 +12,473 @@
 block discarded – undo
12 12
  */
13 13
 class Data extends AbstractIterateur implements Iterator
14 14
 {
15
-	/** Tableau de données */
16
-	protected array $tableau = [];
17
-
18
-	/**
19
-	 * Conditions de filtrage
20
-	 * ie criteres de selection
21
-	 */
22
-	protected array $filtre = [];
23
-
24
-	/**
25
-	 * Cle courante
26
-	 *
27
-	 * @var scalar
28
-	 */
29
-	protected $cle = null;
30
-
31
-	/**
32
-	 * Valeur courante
33
-	 *
34
-	 * @var mixed
35
-	 */
36
-	protected $valeur = null;
37
-
38
-	/**
39
-	 * Constructeur
40
-	 *
41
-	 * @param  $command
42
-	 * @param array $info
43
-	 */
44
-	public function __construct(array $command, array $info = []) {
45
-		include_spip('iterateur/data');
46
-		$this->type = 'DATA';
47
-		$this->command = $command;
48
-		$this->info = $info;
49
-		$this->select($command);
50
-	}
51
-
52
-	/**
53
-	 * Revenir au depart
54
-	 *
55
-	 * @return void
56
-	 */
57
-	public function rewind(): void {
58
-		reset($this->tableau);
59
-		$this->cle = array_key_first($this->tableau);
60
-		$this->valeur = current($this->tableau);
61
-		next($this->tableau);
62
-	}
63
-
64
-	/**
65
-	 * Déclarer les critères exceptions
66
-	 *
67
-	 * @return array
68
-	 */
69
-	public function exception_des_criteres() {
70
-		return ['tableau'];
71
-	}
72
-
73
-	/**
74
-	 * Récupérer depuis le cache si possible
75
-	 *
76
-	 * @param string $cle
77
-	 * @return mixed
78
-	 */
79
-	protected function cache_get($cle) {
80
-		if (!$cle) {
81
-			return;
82
-		}
83
-		# utiliser memoization si dispo
84
-		if (!function_exists('cache_get')) {
85
-			return;
86
-		}
87
-
88
-		return cache_get($cle);
89
-	}
90
-
91
-	/**
92
-	 * Stocker en cache si possible
93
-	 *
94
-	 * @param string $cle
95
-	 * @param int $ttl
96
-	 * @param null|mixed $valeur
97
-	 * @return bool
98
-	 */
99
-	protected function cache_set($cle, $ttl, $valeur = null) {
100
-		if (!$cle) {
101
-			return;
102
-		}
103
-		if (is_null($valeur)) {
104
-			$valeur = $this->tableau;
105
-		}
106
-		# utiliser memoization si dispo
107
-		if (!function_exists('cache_set')) {
108
-			return;
109
-		}
110
-
111
-		return cache_set(
112
-			$cle,
113
-			[
114
-				'data' => $valeur,
115
-				'time' => time(),
116
-				'ttl' => $ttl
117
-			],
118
-			3600 + $ttl
119
-		);
120
-		# conserver le cache 1h de plus que la validite demandee,
121
-		# pour le cas ou le serveur distant ne reponde plus
122
-	}
123
-
124
-	/**
125
-	 * Aller chercher les données de la boucle DATA
126
-	 *
127
-	 * @throws Exception
128
-	 * @param array $command
129
-	 * @return void
130
-	 */
131
-	protected function select($command) {
132
-
133
-		// l'iterateur DATA peut etre appele en passant (data:type)
134
-		// le type se retrouve dans la commande 'from'
135
-		// dans ce cas la le critere {source}, si present, n'a pas besoin du 1er argument
136
-		if (isset($this->command['from'][0])) {
137
-			if (isset($this->command['source']) && is_array($this->command['source'])) {
138
-				array_unshift($this->command['source'], $this->command['sourcemode']);
139
-			}
140
-			$this->command['sourcemode'] = $this->command['from'][0];
141
-		}
142
-
143
-		// cherchons differents moyens de creer le tableau de donnees
144
-		// les commandes connues pour l'iterateur DATA
145
-		// sont : {tableau #ARRAY} ; {cle=...} ; {valeur=...}
146
-
147
-		// {source format, [URL], [arg2]...}
148
-		if (
149
-			isset($this->command['source'])
150
-			&& isset($this->command['sourcemode'])
151
-		) {
152
-			$this->select_source();
153
-		}
154
-
155
-		// Critere {liste X1, X2, X3}
156
-		if (isset($this->command['liste'])) {
157
-			$this->select_liste();
158
-		}
159
-		if (isset($this->command['enum'])) {
160
-			$this->select_enum();
161
-		}
162
-
163
-		// Si a ce stade on n'a pas de table, il y a un bug
164
-		if (!is_array($this->tableau)) {
165
-			$this->err = true;
166
-			spip_logger()->info('erreur datasource ' . var_export($command, true));
167
-		}
168
-
169
-		// {datapath query.results}
170
-		// extraire le chemin "query.results" du tableau de donnees
171
-		if (
172
-			!$this->err
173
-			&& isset($this->command['datapath'])
174
-			&& is_array($this->command['datapath'])
175
-		) {
176
-			$this->select_datapath();
177
-		}
178
-
179
-		// tri {par x}
180
-		if ($this->command['orderby']) {
181
-			$this->select_orderby();
182
-		}
183
-
184
-		// grouper les resultats {fusion /x/y/z} ;
185
-		if ($this->command['groupby']) {
186
-			$this->select_groupby();
187
-		}
188
-
189
-		$this->rewind();
190
-		#var_dump($this->tableau);
191
-	}
192
-
193
-
194
-	/**
195
-	 * Aller chercher les donnees de la boucle DATA
196
-	 * depuis une source
197
-	 * {source format, [URL], [arg2]...}
198
-	 */
199
-	protected function select_source() {
200
-		# un peu crado : avant de charger le cache il faut charger
201
-		# les class indispensables, sinon PHP ne saura pas gerer
202
-		# l'objet en cache ; cf plugins/icalendar
203
-		# perf : pas de fonction table_to_array ! (table est deja un array)
204
-		if (
205
-			isset($this->command['sourcemode'])
206
-			&& !in_array($this->command['sourcemode'], ['table', 'array', 'tableau'])
207
-		) {
208
-			charger_fonction($this->command['sourcemode'] . '_to_array', 'inc', true);
209
-		}
210
-
211
-		# le premier argument peut etre un array, une URL etc.
212
-		$src = $this->command['source'][0] ?? null;
213
-
214
-		# avons-nous un cache dispo ?
215
-		$cle = null;
216
-		if (is_string($src)) {
217
-			$cle = 'datasource_' . md5($this->command['sourcemode'] . ':' . var_export($this->command['source'], true));
218
-		}
219
-
220
-		$cache = $this->cache_get($cle);
221
-		if (isset($this->command['datacache'])) {
222
-			$ttl = (int) $this->command['datacache'];
223
-		}
224
-		if (
225
-			$cache
226
-			&& $cache['time'] + ($ttl ?? $cache['ttl']) > time()
227
-			&& !(_request('var_mode') === 'recalcul' && include_spip('inc/autoriser') && autoriser('recalcul'))
228
-		) {
229
-			$this->tableau = $cache['data'];
230
-		} else {
231
-			try {
232
-				if (
233
-					isset($this->command['sourcemode'])
234
-					&& in_array(
235
-						$this->command['sourcemode'],
236
-						['table', 'array', 'tableau']
237
-					)
238
-				) {
239
-					if (
240
-						is_array($a = $src)
241
-						|| is_string($a) && ($a = str_replace('&quot;', '"', $a)) && is_array($a = @unserialize($a))
242
-					) {
243
-						$this->tableau = $a;
244
-					}
245
-				} else {
246
-					$data = $src;
247
-					if (is_string($src)) {
248
-						if (tester_url_absolue($src)) {
249
-							include_spip('inc/distant');
250
-							$data = recuperer_url($src, ['taille_max' => _DATA_SOURCE_MAX_SIZE]);
251
-							$data = $data['page'] ?? '';
252
-							if (!$data) {
253
-								throw new Exception('404');
254
-							}
255
-							if (!isset($ttl)) {
256
-								$ttl = 24 * 3600;
257
-							}
258
-						} elseif (@is_dir($src)) {
259
-							$data = $src;
260
-						} elseif (@is_readable($src) && @is_file($src)) {
261
-							$data = spip_file_get_contents($src);
262
-						}
263
-						if (!isset($ttl)) {
264
-							$ttl = 10;
265
-						}
266
-					}
267
-					if (
268
-						!$this->err
269
-						&& ($data_to_array = charger_fonction($this->command['sourcemode'] . '_to_array', 'inc', true))
270
-					) {
271
-						$args = $this->command['source'];
272
-						$args[0] = $data;
273
-						if (is_array($a = $data_to_array(...$args))) {
274
-							$this->tableau = $a;
275
-						}
276
-					}
277
-				}
278
-
279
-				if (!is_array($this->tableau)) {
280
-					$this->err = true;
281
-				}
282
-
283
-				if (!$this->err && isset($ttl) && $ttl > 0) {
284
-					$this->cache_set($cle, $ttl);
285
-				}
286
-			} catch (Exception $e) {
287
-				$e = $e->getMessage();
288
-				$err = sprintf(
289
-					"[%s, %s] $e",
290
-					$src,
291
-					$this->command['sourcemode']
292
-				);
293
-				erreur_squelette([$err, []]);
294
-				$this->err = true;
295
-			}
296
-		}
297
-
298
-		# en cas d'erreur, utiliser le cache si encore dispo
299
-		if ($this->err && $cache) {
300
-			$this->tableau = $cache['data'];
301
-			$this->err = false;
302
-		}
303
-	}
304
-
305
-
306
-	/**
307
-	 * Retourne un tableau donne depuis un critère liste
308
-	 *
309
-	 * Critère `{liste X1, X2, X3}`
310
-	 *
311
-	 * @see critere_DATA_liste_dist()
312
-	 *
313
-	 **/
314
-	protected function select_liste() {
315
-		# s'il n'y a qu'une valeur dans la liste, sans doute une #BALISE
316
-		if (!isset($this->command['liste'][1])) {
317
-			if (!is_array($this->command['liste'][0])) {
318
-				$this->command['liste'] = explode(',', (string) $this->command['liste'][0]);
319
-			} else {
320
-				$this->command['liste'] = $this->command['liste'][0];
321
-			}
322
-		}
323
-		$this->tableau = $this->command['liste'];
324
-	}
325
-
326
-	/**
327
-	 * Retourne un tableau donne depuis un critere liste
328
-	 * Critere {enum Xmin, Xmax}
329
-	 *
330
-	 **/
331
-	protected function select_enum() {
332
-		# s'il n'y a qu'une valeur dans la liste, sans doute une #BALISE
333
-		if (!isset($this->command['enum'][1])) {
334
-			if (!is_array($this->command['enum'][0])) {
335
-				$this->command['enum'] = explode(',', (string) $this->command['enum'][0]);
336
-			} else {
337
-				$this->command['enum'] = $this->command['enum'][0];
338
-			}
339
-		}
340
-		if ((is_countable($this->command['enum']) ? count($this->command['enum']) : 0) >= 3) {
341
-			$enum = range(
342
-				array_shift($this->command['enum']),
343
-				array_shift($this->command['enum']),
344
-				array_shift($this->command['enum'])
345
-			);
346
-		} else {
347
-			$enum = range(array_shift($this->command['enum']), array_shift($this->command['enum']));
348
-		}
349
-		$this->tableau = $enum;
350
-	}
351
-
352
-
353
-	/**
354
-	 * extraire le chemin "query.results" du tableau de donnees
355
-	 * {datapath query.results}
356
-	 *
357
-	 **/
358
-	protected function select_datapath() {
359
-		$base = reset($this->command['datapath']);
360
-		if (strlen($base = ltrim(trim((string) $base), '/'))) {
361
-			$results = table_valeur($this->tableau, $base);
362
-			if (is_array($results)) {
363
-				$this->tableau = $results;
364
-			} else {
365
-				$this->tableau = [];
366
-				$this->err = true;
367
-				spip_logger()->info("datapath '$base' absent");
368
-			}
369
-		}
370
-	}
371
-
372
-	/**
373
-	 * Ordonner les resultats
374
-	 * {par x}
375
-	 *
376
-	 **/
377
-	protected function select_orderby() {
378
-		$sortfunc = '';
379
-		foreach ($this->command['orderby'] as $tri) {
380
-			// virer le / initial pour les criteres de la forme {par /xx}
381
-			if (preg_match(',^\.?([/\w:_-]+)( DESC)?$,iS', ltrim((string) $tri, '/'), $r)) {
382
-				$r = array_pad($r, 3, null);
383
-
384
-				// tri par cle
385
-				if ($r[1] == 'cle') {
386
-					if (isset($r[2]) && $r[2]) {
387
-						krsort($this->tableau);
388
-					} else {
389
-						ksort($this->tableau);
390
-					}
391
-				} # {par hasard}
392
-				else {
393
-					if ($r[1] == 'hasard') {
394
-						$k = array_keys($this->tableau);
395
-						shuffle($k);
396
-						$v = [];
397
-						foreach ($k as $cle) {
398
-							$v[$cle] = $this->tableau[$cle];
399
-						}
400
-						$this->tableau = $v;
401
-					} else {
402
-						# {par valeur} ou {par valeur/xx/yy}
403
-						$tv = $r[1] == 'valeur' ? '%s' : 'table_valeur(%s, ' . var_export($r[1], true) . ')';
404
-						$sortfunc .= '
15
+    /** Tableau de données */
16
+    protected array $tableau = [];
17
+
18
+    /**
19
+     * Conditions de filtrage
20
+     * ie criteres de selection
21
+     */
22
+    protected array $filtre = [];
23
+
24
+    /**
25
+     * Cle courante
26
+     *
27
+     * @var scalar
28
+     */
29
+    protected $cle = null;
30
+
31
+    /**
32
+     * Valeur courante
33
+     *
34
+     * @var mixed
35
+     */
36
+    protected $valeur = null;
37
+
38
+    /**
39
+     * Constructeur
40
+     *
41
+     * @param  $command
42
+     * @param array $info
43
+     */
44
+    public function __construct(array $command, array $info = []) {
45
+        include_spip('iterateur/data');
46
+        $this->type = 'DATA';
47
+        $this->command = $command;
48
+        $this->info = $info;
49
+        $this->select($command);
50
+    }
51
+
52
+    /**
53
+     * Revenir au depart
54
+     *
55
+     * @return void
56
+     */
57
+    public function rewind(): void {
58
+        reset($this->tableau);
59
+        $this->cle = array_key_first($this->tableau);
60
+        $this->valeur = current($this->tableau);
61
+        next($this->tableau);
62
+    }
63
+
64
+    /**
65
+     * Déclarer les critères exceptions
66
+     *
67
+     * @return array
68
+     */
69
+    public function exception_des_criteres() {
70
+        return ['tableau'];
71
+    }
72
+
73
+    /**
74
+     * Récupérer depuis le cache si possible
75
+     *
76
+     * @param string $cle
77
+     * @return mixed
78
+     */
79
+    protected function cache_get($cle) {
80
+        if (!$cle) {
81
+            return;
82
+        }
83
+        # utiliser memoization si dispo
84
+        if (!function_exists('cache_get')) {
85
+            return;
86
+        }
87
+
88
+        return cache_get($cle);
89
+    }
90
+
91
+    /**
92
+     * Stocker en cache si possible
93
+     *
94
+     * @param string $cle
95
+     * @param int $ttl
96
+     * @param null|mixed $valeur
97
+     * @return bool
98
+     */
99
+    protected function cache_set($cle, $ttl, $valeur = null) {
100
+        if (!$cle) {
101
+            return;
102
+        }
103
+        if (is_null($valeur)) {
104
+            $valeur = $this->tableau;
105
+        }
106
+        # utiliser memoization si dispo
107
+        if (!function_exists('cache_set')) {
108
+            return;
109
+        }
110
+
111
+        return cache_set(
112
+            $cle,
113
+            [
114
+                'data' => $valeur,
115
+                'time' => time(),
116
+                'ttl' => $ttl
117
+            ],
118
+            3600 + $ttl
119
+        );
120
+        # conserver le cache 1h de plus que la validite demandee,
121
+        # pour le cas ou le serveur distant ne reponde plus
122
+    }
123
+
124
+    /**
125
+     * Aller chercher les données de la boucle DATA
126
+     *
127
+     * @throws Exception
128
+     * @param array $command
129
+     * @return void
130
+     */
131
+    protected function select($command) {
132
+
133
+        // l'iterateur DATA peut etre appele en passant (data:type)
134
+        // le type se retrouve dans la commande 'from'
135
+        // dans ce cas la le critere {source}, si present, n'a pas besoin du 1er argument
136
+        if (isset($this->command['from'][0])) {
137
+            if (isset($this->command['source']) && is_array($this->command['source'])) {
138
+                array_unshift($this->command['source'], $this->command['sourcemode']);
139
+            }
140
+            $this->command['sourcemode'] = $this->command['from'][0];
141
+        }
142
+
143
+        // cherchons differents moyens de creer le tableau de donnees
144
+        // les commandes connues pour l'iterateur DATA
145
+        // sont : {tableau #ARRAY} ; {cle=...} ; {valeur=...}
146
+
147
+        // {source format, [URL], [arg2]...}
148
+        if (
149
+            isset($this->command['source'])
150
+            && isset($this->command['sourcemode'])
151
+        ) {
152
+            $this->select_source();
153
+        }
154
+
155
+        // Critere {liste X1, X2, X3}
156
+        if (isset($this->command['liste'])) {
157
+            $this->select_liste();
158
+        }
159
+        if (isset($this->command['enum'])) {
160
+            $this->select_enum();
161
+        }
162
+
163
+        // Si a ce stade on n'a pas de table, il y a un bug
164
+        if (!is_array($this->tableau)) {
165
+            $this->err = true;
166
+            spip_logger()->info('erreur datasource ' . var_export($command, true));
167
+        }
168
+
169
+        // {datapath query.results}
170
+        // extraire le chemin "query.results" du tableau de donnees
171
+        if (
172
+            !$this->err
173
+            && isset($this->command['datapath'])
174
+            && is_array($this->command['datapath'])
175
+        ) {
176
+            $this->select_datapath();
177
+        }
178
+
179
+        // tri {par x}
180
+        if ($this->command['orderby']) {
181
+            $this->select_orderby();
182
+        }
183
+
184
+        // grouper les resultats {fusion /x/y/z} ;
185
+        if ($this->command['groupby']) {
186
+            $this->select_groupby();
187
+        }
188
+
189
+        $this->rewind();
190
+        #var_dump($this->tableau);
191
+    }
192
+
193
+
194
+    /**
195
+     * Aller chercher les donnees de la boucle DATA
196
+     * depuis une source
197
+     * {source format, [URL], [arg2]...}
198
+     */
199
+    protected function select_source() {
200
+        # un peu crado : avant de charger le cache il faut charger
201
+        # les class indispensables, sinon PHP ne saura pas gerer
202
+        # l'objet en cache ; cf plugins/icalendar
203
+        # perf : pas de fonction table_to_array ! (table est deja un array)
204
+        if (
205
+            isset($this->command['sourcemode'])
206
+            && !in_array($this->command['sourcemode'], ['table', 'array', 'tableau'])
207
+        ) {
208
+            charger_fonction($this->command['sourcemode'] . '_to_array', 'inc', true);
209
+        }
210
+
211
+        # le premier argument peut etre un array, une URL etc.
212
+        $src = $this->command['source'][0] ?? null;
213
+
214
+        # avons-nous un cache dispo ?
215
+        $cle = null;
216
+        if (is_string($src)) {
217
+            $cle = 'datasource_' . md5($this->command['sourcemode'] . ':' . var_export($this->command['source'], true));
218
+        }
219
+
220
+        $cache = $this->cache_get($cle);
221
+        if (isset($this->command['datacache'])) {
222
+            $ttl = (int) $this->command['datacache'];
223
+        }
224
+        if (
225
+            $cache
226
+            && $cache['time'] + ($ttl ?? $cache['ttl']) > time()
227
+            && !(_request('var_mode') === 'recalcul' && include_spip('inc/autoriser') && autoriser('recalcul'))
228
+        ) {
229
+            $this->tableau = $cache['data'];
230
+        } else {
231
+            try {
232
+                if (
233
+                    isset($this->command['sourcemode'])
234
+                    && in_array(
235
+                        $this->command['sourcemode'],
236
+                        ['table', 'array', 'tableau']
237
+                    )
238
+                ) {
239
+                    if (
240
+                        is_array($a = $src)
241
+                        || is_string($a) && ($a = str_replace('&quot;', '"', $a)) && is_array($a = @unserialize($a))
242
+                    ) {
243
+                        $this->tableau = $a;
244
+                    }
245
+                } else {
246
+                    $data = $src;
247
+                    if (is_string($src)) {
248
+                        if (tester_url_absolue($src)) {
249
+                            include_spip('inc/distant');
250
+                            $data = recuperer_url($src, ['taille_max' => _DATA_SOURCE_MAX_SIZE]);
251
+                            $data = $data['page'] ?? '';
252
+                            if (!$data) {
253
+                                throw new Exception('404');
254
+                            }
255
+                            if (!isset($ttl)) {
256
+                                $ttl = 24 * 3600;
257
+                            }
258
+                        } elseif (@is_dir($src)) {
259
+                            $data = $src;
260
+                        } elseif (@is_readable($src) && @is_file($src)) {
261
+                            $data = spip_file_get_contents($src);
262
+                        }
263
+                        if (!isset($ttl)) {
264
+                            $ttl = 10;
265
+                        }
266
+                    }
267
+                    if (
268
+                        !$this->err
269
+                        && ($data_to_array = charger_fonction($this->command['sourcemode'] . '_to_array', 'inc', true))
270
+                    ) {
271
+                        $args = $this->command['source'];
272
+                        $args[0] = $data;
273
+                        if (is_array($a = $data_to_array(...$args))) {
274
+                            $this->tableau = $a;
275
+                        }
276
+                    }
277
+                }
278
+
279
+                if (!is_array($this->tableau)) {
280
+                    $this->err = true;
281
+                }
282
+
283
+                if (!$this->err && isset($ttl) && $ttl > 0) {
284
+                    $this->cache_set($cle, $ttl);
285
+                }
286
+            } catch (Exception $e) {
287
+                $e = $e->getMessage();
288
+                $err = sprintf(
289
+                    "[%s, %s] $e",
290
+                    $src,
291
+                    $this->command['sourcemode']
292
+                );
293
+                erreur_squelette([$err, []]);
294
+                $this->err = true;
295
+            }
296
+        }
297
+
298
+        # en cas d'erreur, utiliser le cache si encore dispo
299
+        if ($this->err && $cache) {
300
+            $this->tableau = $cache['data'];
301
+            $this->err = false;
302
+        }
303
+    }
304
+
305
+
306
+    /**
307
+     * Retourne un tableau donne depuis un critère liste
308
+     *
309
+     * Critère `{liste X1, X2, X3}`
310
+     *
311
+     * @see critere_DATA_liste_dist()
312
+     *
313
+     **/
314
+    protected function select_liste() {
315
+        # s'il n'y a qu'une valeur dans la liste, sans doute une #BALISE
316
+        if (!isset($this->command['liste'][1])) {
317
+            if (!is_array($this->command['liste'][0])) {
318
+                $this->command['liste'] = explode(',', (string) $this->command['liste'][0]);
319
+            } else {
320
+                $this->command['liste'] = $this->command['liste'][0];
321
+            }
322
+        }
323
+        $this->tableau = $this->command['liste'];
324
+    }
325
+
326
+    /**
327
+     * Retourne un tableau donne depuis un critere liste
328
+     * Critere {enum Xmin, Xmax}
329
+     *
330
+     **/
331
+    protected function select_enum() {
332
+        # s'il n'y a qu'une valeur dans la liste, sans doute une #BALISE
333
+        if (!isset($this->command['enum'][1])) {
334
+            if (!is_array($this->command['enum'][0])) {
335
+                $this->command['enum'] = explode(',', (string) $this->command['enum'][0]);
336
+            } else {
337
+                $this->command['enum'] = $this->command['enum'][0];
338
+            }
339
+        }
340
+        if ((is_countable($this->command['enum']) ? count($this->command['enum']) : 0) >= 3) {
341
+            $enum = range(
342
+                array_shift($this->command['enum']),
343
+                array_shift($this->command['enum']),
344
+                array_shift($this->command['enum'])
345
+            );
346
+        } else {
347
+            $enum = range(array_shift($this->command['enum']), array_shift($this->command['enum']));
348
+        }
349
+        $this->tableau = $enum;
350
+    }
351
+
352
+
353
+    /**
354
+     * extraire le chemin "query.results" du tableau de donnees
355
+     * {datapath query.results}
356
+     *
357
+     **/
358
+    protected function select_datapath() {
359
+        $base = reset($this->command['datapath']);
360
+        if (strlen($base = ltrim(trim((string) $base), '/'))) {
361
+            $results = table_valeur($this->tableau, $base);
362
+            if (is_array($results)) {
363
+                $this->tableau = $results;
364
+            } else {
365
+                $this->tableau = [];
366
+                $this->err = true;
367
+                spip_logger()->info("datapath '$base' absent");
368
+            }
369
+        }
370
+    }
371
+
372
+    /**
373
+     * Ordonner les resultats
374
+     * {par x}
375
+     *
376
+     **/
377
+    protected function select_orderby() {
378
+        $sortfunc = '';
379
+        foreach ($this->command['orderby'] as $tri) {
380
+            // virer le / initial pour les criteres de la forme {par /xx}
381
+            if (preg_match(',^\.?([/\w:_-]+)( DESC)?$,iS', ltrim((string) $tri, '/'), $r)) {
382
+                $r = array_pad($r, 3, null);
383
+
384
+                // tri par cle
385
+                if ($r[1] == 'cle') {
386
+                    if (isset($r[2]) && $r[2]) {
387
+                        krsort($this->tableau);
388
+                    } else {
389
+                        ksort($this->tableau);
390
+                    }
391
+                } # {par hasard}
392
+                else {
393
+                    if ($r[1] == 'hasard') {
394
+                        $k = array_keys($this->tableau);
395
+                        shuffle($k);
396
+                        $v = [];
397
+                        foreach ($k as $cle) {
398
+                            $v[$cle] = $this->tableau[$cle];
399
+                        }
400
+                        $this->tableau = $v;
401
+                    } else {
402
+                        # {par valeur} ou {par valeur/xx/yy}
403
+                        $tv = $r[1] == 'valeur' ? '%s' : 'table_valeur(%s, ' . var_export($r[1], true) . ')';
404
+                        $sortfunc .= '
405 405
 					$a = ' . sprintf($tv, '$aa') . ';
406 406
 					$b = ' . sprintf($tv, '$bb') . ';
407 407
 					if ($a <> $b)
408 408
 						return ($a ' . (empty($r[2]) ? '<' : '>') . ' $b) ? -1 : 1;';
409
-					}
410
-				}
411
-			}
412
-		}
413
-
414
-		if ($sortfunc) {
415
-			$sortfunc .= "\n return 0;";
416
-			uasort($this->tableau, fn($aa, $bb) => eval($sortfunc));
417
-		}
418
-	}
419
-
420
-
421
-	/**
422
-	 * Grouper les resultats
423
-	 * {fusion /x/y/z}
424
-	 *
425
-	 **/
426
-	protected function select_groupby() {
427
-		// virer le / initial pour les criteres de la forme {fusion /xx}
428
-		if (strlen($fusion = ltrim((string) $this->command['groupby'][0], '/'))) {
429
-			$vu = [];
430
-			foreach ($this->tableau as $k => $v) {
431
-				$val = table_valeur($v, $fusion);
432
-				if (isset($vu[$val])) {
433
-					unset($this->tableau[$k]);
434
-				} else {
435
-					$vu[$val] = true;
436
-				}
437
-			}
438
-		}
439
-	}
440
-
441
-
442
-	/**
443
-	 * L'iterateur est-il encore valide ?
444
-	 */
445
-	public function valid(): bool {
446
-		return !is_null($this->cle);
447
-	}
448
-
449
-	/**
450
-	 * Retourner la valeur
451
-	 */
452
-	public function current(): mixed {
453
-		return $this->valeur;
454
-	}
455
-
456
-	/**
457
-	 * Retourner la cle
458
-	 */
459
-	public function key(): mixed {
460
-		return $this->cle;
461
-	}
462
-
463
-	/**
464
-	 * Passer a la valeur suivante
465
-	 */
466
-	public function next(): void {
467
-		if ($this->valid()) {
468
-			$this->cle = key($this->tableau);
469
-			$this->valeur = current($this->tableau);
470
-			next($this->tableau);
471
-		}
472
-	}
473
-
474
-	/**
475
-	 * Compter le nombre total de resultats
476
-	 */
477
-	public function count(): int {
478
-		if (is_null($this->total)) {
479
-			$this->total = count($this->tableau);
480
-		}
481
-
482
-		return $this->total;
483
-	}
409
+                    }
410
+                }
411
+            }
412
+        }
413
+
414
+        if ($sortfunc) {
415
+            $sortfunc .= "\n return 0;";
416
+            uasort($this->tableau, fn($aa, $bb) => eval($sortfunc));
417
+        }
418
+    }
419
+
420
+
421
+    /**
422
+     * Grouper les resultats
423
+     * {fusion /x/y/z}
424
+     *
425
+     **/
426
+    protected function select_groupby() {
427
+        // virer le / initial pour les criteres de la forme {fusion /xx}
428
+        if (strlen($fusion = ltrim((string) $this->command['groupby'][0], '/'))) {
429
+            $vu = [];
430
+            foreach ($this->tableau as $k => $v) {
431
+                $val = table_valeur($v, $fusion);
432
+                if (isset($vu[$val])) {
433
+                    unset($this->tableau[$k]);
434
+                } else {
435
+                    $vu[$val] = true;
436
+                }
437
+            }
438
+        }
439
+    }
440
+
441
+
442
+    /**
443
+     * L'iterateur est-il encore valide ?
444
+     */
445
+    public function valid(): bool {
446
+        return !is_null($this->cle);
447
+    }
448
+
449
+    /**
450
+     * Retourner la valeur
451
+     */
452
+    public function current(): mixed {
453
+        return $this->valeur;
454
+    }
455
+
456
+    /**
457
+     * Retourner la cle
458
+     */
459
+    public function key(): mixed {
460
+        return $this->cle;
461
+    }
462
+
463
+    /**
464
+     * Passer a la valeur suivante
465
+     */
466
+    public function next(): void {
467
+        if ($this->valid()) {
468
+            $this->cle = key($this->tableau);
469
+            $this->valeur = current($this->tableau);
470
+            next($this->tableau);
471
+        }
472
+    }
473
+
474
+    /**
475
+     * Compter le nombre total de resultats
476
+     */
477
+    public function count(): int {
478
+        if (is_null($this->total)) {
479
+            $this->total = count($this->tableau);
480
+        }
481
+
482
+        return $this->total;
483
+    }
484 484
 }
Please login to merge, or discard this patch.
Spacing   +8 added lines, -8 removed lines patch added patch discarded remove patch
@@ -163,7 +163,7 @@  discard block
 block discarded – undo
163 163
 		// Si a ce stade on n'a pas de table, il y a un bug
164 164
 		if (!is_array($this->tableau)) {
165 165
 			$this->err = true;
166
-			spip_logger()->info('erreur datasource ' . var_export($command, true));
166
+			spip_logger()->info('erreur datasource '.var_export($command, true));
167 167
 		}
168 168
 
169 169
 		// {datapath query.results}
@@ -205,7 +205,7 @@  discard block
 block discarded – undo
205 205
 			isset($this->command['sourcemode'])
206 206
 			&& !in_array($this->command['sourcemode'], ['table', 'array', 'tableau'])
207 207
 		) {
208
-			charger_fonction($this->command['sourcemode'] . '_to_array', 'inc', true);
208
+			charger_fonction($this->command['sourcemode'].'_to_array', 'inc', true);
209 209
 		}
210 210
 
211 211
 		# le premier argument peut etre un array, une URL etc.
@@ -214,7 +214,7 @@  discard block
 block discarded – undo
214 214
 		# avons-nous un cache dispo ?
215 215
 		$cle = null;
216 216
 		if (is_string($src)) {
217
-			$cle = 'datasource_' . md5($this->command['sourcemode'] . ':' . var_export($this->command['source'], true));
217
+			$cle = 'datasource_'.md5($this->command['sourcemode'].':'.var_export($this->command['source'], true));
218 218
 		}
219 219
 
220 220
 		$cache = $this->cache_get($cle);
@@ -266,7 +266,7 @@  discard block
 block discarded – undo
266 266
 					}
267 267
 					if (
268 268
 						!$this->err
269
-						&& ($data_to_array = charger_fonction($this->command['sourcemode'] . '_to_array', 'inc', true))
269
+						&& ($data_to_array = charger_fonction($this->command['sourcemode'].'_to_array', 'inc', true))
270 270
 					) {
271 271
 						$args = $this->command['source'];
272 272
 						$args[0] = $data;
@@ -400,12 +400,12 @@  discard block
 block discarded – undo
400 400
 						$this->tableau = $v;
401 401
 					} else {
402 402
 						# {par valeur} ou {par valeur/xx/yy}
403
-						$tv = $r[1] == 'valeur' ? '%s' : 'table_valeur(%s, ' . var_export($r[1], true) . ')';
403
+						$tv = $r[1] == 'valeur' ? '%s' : 'table_valeur(%s, '.var_export($r[1], true).')';
404 404
 						$sortfunc .= '
405
-					$a = ' . sprintf($tv, '$aa') . ';
406
-					$b = ' . sprintf($tv, '$bb') . ';
405
+					$a = ' . sprintf($tv, '$aa').';
406
+					$b = ' . sprintf($tv, '$bb').';
407 407
 					if ($a <> $b)
408
-						return ($a ' . (empty($r[2]) ? '<' : '>') . ' $b) ? -1 : 1;';
408
+						return ($a ' . (empty($r[2]) ? '<' : '>').' $b) ? -1 : 1;';
409 409
 					}
410 410
 				}
411 411
 			}
Please login to merge, or discard this patch.