[SPIP] ~v3.0.20-->v3.0.25
[lhc/web/clavette_www.git] / www / ecrire / inc / rechercher.php
1 <?php
2
3 /***************************************************************************\
4 * SPIP, Systeme de publication pour l'internet *
5 * *
6 * Copyright (c) 2001-2016 *
7 * Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James *
8 * *
9 * Ce programme est un logiciel libre distribue sous licence GNU/GPL. *
10 * Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne. *
11 \***************************************************************************/
12
13
14 if (!defined('_ECRIRE_INC_VERSION')) return;
15
16
17 // Donne la liste des champs/tables ou l'on sait chercher/remplacer
18 // avec un poids pour le score
19 // http://doc.spip.org/@liste_des_champs
20 function liste_des_champs() {
21 static $liste=null;
22 if (is_null($liste)) {
23 $liste = array();
24 // recuperer les tables_objets_sql declarees
25 include_spip('base/objets');
26 $tables_objets = lister_tables_objets_sql();
27 foreach($tables_objets as $t=>$infos){
28 if ($infos['rechercher_champs']){
29 $liste[$infos['type']] = $infos['rechercher_champs'];
30 }
31 }
32 // puis passer dans le pipeline
33 $liste = pipeline('rechercher_liste_des_champs', $liste);
34 }
35 return $liste;
36 }
37
38
39 // Recherche des auteurs et mots-cles associes
40 // en ne regardant que le titre ou le nom
41 // http://doc.spip.org/@liste_des_jointures
42 function liste_des_jointures() {
43 static $liste=null;
44 if (is_null($liste)) {
45 $liste = array();
46 // recuperer les tables_objets_sql declarees
47 include_spip('base/objets');
48 $tables_objets = lister_tables_objets_sql();
49 foreach($tables_objets as $t=>$infos){
50 if ($infos['rechercher_jointures']){
51 $liste[$infos['type']] = $infos['rechercher_jointures'];
52 }
53 }
54 // puis passer dans le pipeline
55 $liste = pipeline('rechercher_liste_des_jointures', $liste);
56 }
57 return $liste;
58 }
59
60 function expression_recherche($recherche, $options) {
61 // ne calculer qu'une seule fois l'expression par hit
62 // (meme si utilisee dans plusieurs boucles)
63 static $expression = array();
64 $key = serialize(array($recherche, $options['preg_flags']));
65 if (isset($expression[$key]))
66 return $expression[$key];
67
68 $u = $GLOBALS['meta']['pcre_u'];
69 if ($u AND strpos($options['preg_flags'],$u)===false)
70 $options['preg_flags'] .= $u;
71 include_spip('inc/charsets');
72 $recherche = trim($recherche);
73
74 $is_preg = false;
75 if (substr($recherche, 0, 1) == '/' and substr($recherche, -1, 1) == '/' and strlen($recherche) > 2) {
76 // c'est une preg
77 $recherche_trans = translitteration($recherche);
78 $preg = $recherche_trans.$options['preg_flags'];
79 $is_preg = true;
80 }
81 else{
82 // s'il y a plusieurs mots il faut les chercher tous : oblige REGEXP
83 // sauf ceux de moins de 4 lettres (on supprime ainsi 'le', 'les', 'un',
84 // 'une', 'des' ...)
85 if (preg_match(",\s+,".$u, $recherche)){
86 $is_preg = true;
87 $recherche_inter = '|';
88 $recherche_mots = explode(' ', $recherche);
89 $min_long = defined('_RECHERCHE_MIN_CAR') ? _RECHERCHE_MIN_CAR : 4;
90 foreach ($recherche_mots as $mot) {
91 if (strlen($mot) >= $min_long) {
92 $recherche_inter .= $mot.' ';
93 }
94 }
95 // mais on cherche quand même l'expression complète, même si elle
96 // comporte des mots de moins de quatre lettres
97 $recherche = rtrim($recherche.preg_replace(',\s+,'.$u, '|', $recherche_inter), '|');
98 $recherche_trans = translitteration($recherche);
99 }
100
101 $preg = '/'.str_replace('/', '\\/', $recherche_trans).'/' . $options['preg_flags'];
102 }
103
104 // Si la chaine est inactive, on va utiliser LIKE pour aller plus vite
105 // ou si l'expression reguliere est invalide
106 if (!$is_preg
107 OR (@preg_match($preg,'')===FALSE) ) {
108 $methode = 'LIKE';
109 $u = $GLOBALS['meta']['pcre_u'];
110 // eviter les parentheses et autres caractères qui interferent avec pcre par la suite (dans le preg_match_all) s'il y a des reponses
111 $recherche = str_replace(
112 array('(',')','?','[', ']', '+', '*', '/'),
113 array('\(','\)','[?]', '\[', '\]', '\+', '\*', '\/'),
114 $recherche);
115 $recherche_trans = translitteration($recherche);
116 $recherche_mod = $recherche_trans;
117
118 // echapper les % et _
119 $q = str_replace(array('%','_'), array('\%', '\_'), trim($recherche));
120 // les expressions entre " " sont un mot a chercher tel quel
121 // -> on remplace les espaces par un _ et on enleve les guillemets
122 if (preg_match(',["][^"]+["],Uims',$q,$matches)){
123 foreach($matches as $match){
124 // corriger le like dans le $q
125 $word = preg_replace(",\s+,Uims","_",$match);
126 $word = trim($word,'"');
127 $q = str_replace($match,$word,$q);
128 // corriger la regexp
129 $word = preg_replace(",\s+,Uims","[\s]",$match);
130 $word = trim($word,'"');
131 $recherche_mod = str_replace($match,$word,$recherche_mod);
132 }
133 }
134 $q = sql_quote(
135 "%"
136 . preg_replace(",\s+,".$u, "%", $q)
137 . "%"
138 );
139
140 $preg = '/'.preg_replace(",\s+,".$u, ".+", trim($recherche_mod)).'/' . $options['preg_flags'];
141
142 } else {
143 $methode = 'REGEXP';
144 $q = sql_quote(trim($recherche, '/'));
145 }
146
147 // tous les caracteres transliterables de $q sont remplaces par un joker
148 // permet de matcher en SQL meme si on est sensible aux accents (SQLite)
149 $q_t = $q;
150 for($i = 0;$i<spip_strlen($q);$i++){
151 $char = spip_substr($q,$i,1);
152 if (!is_ascii($char)
153 AND $char_t = translitteration($char)
154 AND $char_t !== $char){
155 $q_t = str_replace($char,$is_preg?".":"_", $q_t);
156 }
157 }
158
159 $q = $q_t;
160
161 // fix : SQLite 3 est sensible aux accents, on jokerise les caracteres
162 // les plus frequents qui peuvent etre accentues
163 // (oui c'est tres dicustable...)
164 if (isset($GLOBALS['connexions'][$options['serveur']?$options['serveur']:0]['type'])
165 AND strncmp($GLOBALS['connexions'][$options['serveur']?$options['serveur']:0]['type'],'sqlite',6)==0){
166 $q_t = strtr($q,"aeuioc",$is_preg?"......":"______");
167 // si il reste au moins un char significatif...
168 if (preg_match(",[^'%_.],",$q_t))
169 $q = $q_t;
170 }
171
172 return $expression[$key] = array($methode, $q, $preg);
173 }
174
175
176 // Effectue une recherche sur toutes les tables de la base de donnees
177 // options :
178 // - toutvoir pour eviter autoriser(voir)
179 // - flags pour eviter les flags regexp par defaut (UimsS)
180 // - champs pour retourner les champs concernes
181 // - score pour retourner un score
182 // On peut passer les tables, ou une chaine listant les tables souhaitees
183 // http://doc.spip.org/@recherche_en_base
184 function recherche_en_base($recherche='', $tables=NULL, $options=array(), $serveur='') {
185 include_spip('base/abstract_sql');
186
187 if (!is_array($tables)) {
188 $liste = liste_des_champs();
189
190 if (is_string($tables)
191 AND $tables != '') {
192 $toutes = array();
193 foreach(explode(',', $tables) as $t)
194 if (isset($liste[$t]))
195 $toutes[$t] = $liste[$t];
196 $tables = $toutes;
197 unset($toutes);
198 } else
199 $tables = $liste;
200 }
201
202 if (!strlen($recherche) OR !count($tables))
203 return array();
204
205 include_spip('inc/autoriser');
206
207 // options par defaut
208 $options = array_merge(array(
209 'preg_flags' => 'UimsS',
210 'toutvoir' => false,
211 'champs' => false,
212 'score' => false,
213 'matches' => false,
214 'jointures' => false,
215 'serveur' => $serveur
216 ),
217 $options
218 );
219
220 $results = array();
221
222 // Utiliser l'iterateur (DATA:recherche)
223 // pour recuperer les couples (id_objet, score)
224 // Le resultat est au format {
225 // id1 = { 'score' => x, attrs => { } },
226 // id2 = { 'score' => x, attrs => { } },
227 // }
228 include_spip('inc/memoization');
229 foreach ($tables as $table => $champs) {
230 # lock via memoization, si dispo
231 if (function_exists('cache_lock'))
232 cache_lock($lock = 'recherche '.$table.' '.$recherche);
233
234 spip_timer('rech');
235
236 // TODO: ici plutot charger un iterateur via l'API iterateurs
237 include_spip('inc/recherche_to_array');
238 $to_array = charger_fonction('recherche_to_array', 'inc');
239 $results[$table] = $to_array($recherche,
240 array_merge($options, array('table' => $table, 'champs' => $champs))
241 );
242 ##var_dump($results[$table]);
243
244
245 spip_log("recherche $table ($recherche) : ".count($results[$table])." resultats ".spip_timer('rech'),'recherche');
246
247 if (isset($lock))
248 cache_unlock($lock);
249 }
250
251 return $results;
252 }
253
254
255 // Effectue une recherche sur toutes les tables de la base de donnees
256 // http://doc.spip.org/@remplace_en_base
257 function remplace_en_base($recherche='', $remplace=NULL, $tables=NULL, $options=array()) {
258 include_spip('inc/modifier');
259
260 // options par defaut
261 $options = array_merge(array(
262 'preg_flags' => 'UimsS',
263 'toutmodifier' => false
264 ),
265 $options
266 );
267 $options['champs'] = true;
268
269
270 if (!is_array($tables))
271 $tables = liste_des_champs();
272
273 $results = recherche_en_base($recherche, $tables, $options);
274
275 $preg = '/'.str_replace('/', '\\/', $recherche).'/' . $options['preg_flags'];
276
277 foreach ($results as $table => $r) {
278 $_id_table = id_table_objet($table);
279 foreach ($r as $id => $x) {
280 if ($options['toutmodifier']
281 OR autoriser('modifier', $table, $id)) {
282 $modifs = array();
283 foreach ($x['champs'] as $key => $val) {
284 if ($key == $_id_table) next;
285 $repl = preg_replace($preg, $remplace, $val);
286 if ($repl <> $val)
287 $modifs[$key] = $repl;
288 }
289 if ($modifs)
290 objet_modifier_champs($table, $id,
291 array(
292 'champs' => array_keys($modifs),
293 ),
294 $modifs);
295 }
296 }
297 }
298 }
299
300 ?>