source: spip-zone/_plugins_/iterateurs/iterateur/data.php @ 67787

Last change on this file since 67787 was 67787, checked in by booz@…, 7 years ago

corriger aussi le cas de la valeur 0 et des entiers négatifs, tester avec un fichier 'A -1 B 0 1 -2'

File size: 13.6 KB
Line 
1<?php
2
3/***************************************************************************\
4 *  SPIP, Systeme de publication pour l'internet                           *
5 *                                                                         *
6 *  Copyright (c) 2001-2011                                                *
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
13if (!defined('_ECRIRE_INC_VERSION')) return;
14
15include_spip('iterateur/iterateur');
16
17
18/**
19 * creer une boucle sur un iterateur DATA
20 * annonce au compilo les "champs" disponibles
21 *
22 * @param  $b
23 * @return
24 */
25function iterateur_DATA_dist($b) {
26        $b->iterateur = 'DATA'; # designe la classe d'iterateur
27        $b->show = array(
28                'field' => array(
29                        'cle' => 'STRING',
30                        'valeur' => 'STRING',
31                        '*' => 'ALL' // Champ joker *
32                )
33        );
34        $b->select[] = '.valeur';
35        return $b;
36}
37
38
39/**
40 * IterateurDATA pour iterer sur des donnees
41 */
42class IterateurDATA implements Iterator {
43        /**
44         * tableau de donnees
45         * @var array
46         */
47        protected $tableau = array();
48
49        /**
50         * Conditions de filtrage
51         * ie criteres de selection
52         * @var array
53         */
54        protected $filtre = array();
55
56
57        /**
58         * Cle courante
59         * @var null
60         */
61        protected $cle = null;
62
63        /**
64         * Valeur courante
65         * @var null
66         */
67        protected $valeur = null;
68
69        /**
70         * Constructeur
71         *
72         * @param  $command
73         * @param array $info
74         */
75        public function __construct($command, $info=array()) {
76                $this->type='DATA';
77                $this->command = $command;
78                $this->info = $info;
79
80                $this->select($command);
81        }
82
83        /**
84         * Revenir au depart
85         * @return void
86         */
87        public function rewind() {
88                reset($this->tableau);
89                list($this->cle, $this->valeur) = each($this->tableau);
90        }
91
92        /**
93         * Declarer les criteres exceptions
94         * @return array
95         */
96        public function exception_des_criteres() {
97                return array('tableau');
98        }
99
100        /**
101         * Recuperer depuis le cache si possible
102         * @param  $cle
103         * @return
104         */
105        protected function cache_get($cle) {
106                if (!$cle) return;
107                # utiliser memoization si dispo
108                include_spip('inc/memoization');
109                if (!function_exists('cache_get')) return;
110                return cache_get($cle);
111        }
112
113        /**
114         * Srtocker en cache si possible
115         * @param  $cle
116         * @param  $ttl
117         * @return
118         */
119        protected function cache_set($cle, $ttl) {
120                if (!$cle) return;
121                # utiliser memoization si dispo
122                include_spip('inc/memoization');
123                if (!function_exists('cache_set')) return;
124                return cache_set($cle,
125                        array(
126                                'data' => $this->tableau,
127                                'time' => time(),
128                                'ttl' => $ttl
129                        ),
130                        3600 + $ttl);
131                        # conserver le cache 1h deplus que la validite demandee,
132                        # pour le cas ou le serveur distant ne repond plus
133        }
134
135        /**
136         * Aller chercher les donnees de la boucle DATA
137         *
138         * @throws Exception
139         * @param  $command
140         * @return void
141         */
142        protected function select($command) {
143
144                // l'iterateur DATA peut etre appele en passant (data:type)
145                // le type se retrouve dans la commande 'from'
146                // dans ce cas la le critere {source} n'a pas besoin du 1er argument
147                if (isset($this->command['from'][0])) {
148                        array_unshift($this->command['source'], $this->command['sourcemode']);
149                        $this->command['sourcemode'] = $this->command['from'][0];
150                }
151               
152                // les commandes connues pour l'iterateur DATA
153                // sont : {tableau #ARRAY} ; {cle=...} ; {valeur=...}
154                // {source format, [URL], [arg2]...}
155               
156                if (isset($this->command['source'])
157                AND isset($this->command['sourcemode'])) {
158
159                        # un peu crado : avant de charger le cache il faut charger
160                        # les class indispensables, sinon PHP ne saura pas gerer
161                        # l'objet en cache ; cf plugins/icalendar
162                        # perf : pas de fonction table_to_array ! (table est deja un array)
163                        if (isset($this->command['sourcemode'])
164                          AND !in_array($this->command['sourcemode'],array('table', 'array', 'tableau')))
165                                charger_fonction($this->command['sourcemode'] . '_to_array', 'inc', true);
166
167                        # le premier argument peut etre un array, une URL etc.
168                        $src = $this->command['source'][0];
169
170                        # avons-nous un cache dispo ?
171                        if (is_string($src))
172                                $cle = 'datasource_'.md5($this->command['sourcemode'].':'.var_export($this->command['source'],true));
173
174                        $cache = $this->cache_get($cle);
175                        if (isset($this->command['datacache']))
176                                $ttl = intval($this->command['datacache']);
177                        if ($cache
178                        AND ($cache['time'] + (isset($ttl) ? $ttl : $cache['ttl'])
179                                > time())
180                        AND !(_request('var_mode') === 'recalcul'
181                                AND include_spip('inc/autoriser')
182                                AND autoriser('recalcul')
183                        )) {
184                                $this->tableau = $cache['data'];
185                        }
186                        else try {
187                                # dommage que ca ne soit pas une option de yql_to_array...
188                                if ($this->command['sourcemode'] == 'yql')
189                                        if (!isset($ttl)) $ttl = 3600;
190
191                                if (isset($this->command['sourcemode'])
192                                AND in_array($this->command['sourcemode'],
193                                        array('table', 'array', 'tableau'))
194                                ) {
195                                        if (is_array($a = $src)
196                                        OR (is_string($a)
197                                        AND $a = str_replace('&quot;', '"', $a) # fragile!
198                                        AND is_array($a = @unserialize($a)))
199                                        )
200                                                $this->tableau = $a;
201                                }
202                                else {
203                                        if (preg_match(',^https?://,', $src)) {
204                                                include_spip('inc/distant');
205                                                $u = recuperer_page($src);
206                                                if (!$u)
207                                                        throw new Exception("404");
208                                                if (!isset($ttl)) $ttl = 24*3600;
209                                        } else if (@is_dir($src)) {
210                                                $u = $src;
211                                                if (!isset($ttl)) $ttl = 10;
212                                        } else if (@is_readable($src) && @is_file($src)) {
213                                                $u = spip_file_get_contents($src);
214                                                if (!isset($ttl)) $ttl = 10;
215                                        } else {
216                                                $u = $src;
217                                                if (!isset($ttl)) $ttl = 10;
218                                        }
219                                        if (!$this->err
220                                        AND $g = charger_fonction($this->command['sourcemode'] . '_to_array', 'inc', true)) {
221                                                $args = $this->command['source'];
222                                                $args[0] = $u;
223                                                if (is_array($a = call_user_func_array($g,$args))) {
224                                                        $this->tableau = $a;
225                                                }
226                                        }
227                                }
228
229                                if (!is_array($this->tableau))
230                                        $this->err = true;
231
232                                if (!$this->err AND $ttl>0)
233                                        $this->cache_set($cle, $ttl);
234
235                        }
236                        catch (Exception $e) {
237                                $e = $e->getMessage();
238                                $err = sprintf("[%s, %s] $e",
239                                        $src,
240                                        $this->command['sourcemode']);
241                                erreur_squelette(array($err, array()));
242                                $this->err = true;
243                        }
244
245                        # en cas d'erreur, utiliser le cache si encore dispo
246                        if ($this->err
247                        AND $cache) {
248                                $this->tableau = $cache['data'];
249                                $this->err = false;
250                        }
251
252                }
253
254                // Critere {liste X1, X2, X3}
255                if (isset($this->command['liste'])) {
256                        # s'il n'y a qu'une valeur dans la liste, sans doute une #BALISE
257                        if (!isset($this->command['liste'][1])) {
258                                $this->command['liste'] = explode(',', $this->command['liste'][0]);
259                        }
260                        $this->tableau = $this->command['liste'];
261                }
262
263                // Si a ce stade on n'a pas de table, il y a un bug
264                if (!is_array($this->tableau)) {
265                        $this->err = true;
266                        spip_log("erreur datasource ".$src);
267                }
268
269
270                // {datapath query.results}
271                // extraire le chemin "query.results" du tableau de donnees
272                if (!$this->err
273                AND is_array($this->command['datapath'])) {
274                        list(,$base) = each($this->command['datapath']);
275                        if (strlen($base = trim($base))) {
276                                $this->tableau = Iterateurs_table_valeur($this->tableau, $base);
277                                if (!is_array($this->tableau)) {
278                                        $this->tableau = array();
279                                        $this->err = true;
280                                        spip_log("datapath '$base' absent");
281                                }
282                        }
283                }
284
285                // tri {par x}
286                if ($this->command['orderby']) {
287                        $sortfunc = '';
288                        $aleas = 0;
289                        foreach($this->command['orderby'] as $tri) {
290                                if (preg_match(',^\.?([/\w]+)( DESC)?$,iS', $tri, $r)) {
291                                        // tri par cle
292                                        if ($r[1] == 'cle'){
293                                                if ($r[2])
294                                                        krsort($this->tableau);
295                                                else
296                                                        ksort($this->tableau);
297                                        }
298                                        # {par hasard}
299                                        else if ($r[1] == 'alea') {
300                                                $k = array_keys($this->tableau);
301                                                shuffle($k);
302                                                $v = array();
303                                                foreach($k as $cle)
304                                                        $v[$cle] = $this->tableau[$cle];
305                                                $this->tableau = $v;
306                                        }
307                                        else {
308                                                # {par valeur}
309                                                if ($r[1] == 'valeur')
310                                                        $tv = '%s';
311                                                # {par valeur/xx/yy} ??
312                                                else
313                                                        $tv = 'Iterateurs_table_valeur(%s, '.var_export($r[1],true).')';
314                                                $sortfunc .= '
315                                                $a = '.sprintf($tv,'$aa').';
316                                                $b = '.sprintf($tv,'$bb').';
317                                                if ($a <> $b)
318                                                        return ($a ' . ($r[2] ? '>' : '<').' $b) ? -1 : 1;';
319                                        }
320                                }
321                        }
322
323                        if ($sortfunc) {
324                                uasort($this->tableau, create_function('$aa,$bb',
325                                        $sortfunc.'
326                                        return 0;'
327                                ));
328                        }
329                }
330
331                // grouper les resultats {fusion /x/y/z} ;
332                if ($this->command['groupby']
333                AND strlen($fusion = $this->command['groupby'][0])) {
334                        $vu = array();
335                        foreach($this->tableau as $k => $v) {
336                                $val = Iterateurs_table_valeur($v, $fusion);
337                                if (isset($vu[$val]))
338                                        unset($this->tableau[$k]);
339                                else
340                                        $vu[$val] = true;
341                        }
342                }
343
344                $this->rewind();
345                #var_dump($this->tableau);
346        }
347
348
349        /**
350         * L'iterateur est-il encore valide ?
351         * @return bool
352         */
353        public function valid(){
354                return !is_null($this->cle);
355        }
356
357        /**
358         * Retourner la valeur
359         * @return null
360         */
361        public function current() {
362                return $this->valeur;
363        }
364
365        /**
366         * Retourner la cle
367         * @return null
368         */
369        public function key() {
370                return $this->cle;
371        }
372
373        /**
374         * Passer a la valeur suivante
375         * @return void
376         */
377        public function next(){
378                if ($this->valid())
379                        list($this->cle, $this->valeur) = each($this->tableau);
380        }
381
382        /**
383         * Compter le nombre total de resultats
384         * @return int
385         */
386        public function count() {
387                if (is_null($this->total))
388                        $this->total = count($this->tableau);
389          return $this->total;
390        }
391}
392
393/*
394 * Fonctions de transformation donnee => tableau
395 */
396
397/**
398 * file -> tableau
399 *
400 * @param  string $u
401 * @return array
402 */
403function inc_file_to_array_dist($u) {
404        return preg_split('/\r?\n/', $u);
405}
406
407/**
408 * plugins -> tableau
409 * @return unknown
410 */
411function inc_plugins_to_array_dist() {
412        include_spip('inc/plugin');
413        return liste_chemin_plugin_actifs();
414}
415
416/**
417 * xml -> tableau
418 * @param  string $u
419 * @return array
420 */
421function inc_xml_to_array_dist($u) {
422        return @ObjectToArray(new SimpleXmlIterator($u));
423}
424
425/**
426 * yql -> tableau
427 * @throws Exception
428 * @param  string $u
429 * @return array|bool
430 */
431function inc_yql_to_array_dist($u) {
432        define('_YQL_ENDPOINT', 'http://query.yahooapis.com/v1/public/yql?&env=store%3A%2F%2Fdatatables.org%2Falltableswithkeys&q=');
433        $v = recuperer_page($url = _YQL_ENDPOINT.urlencode($u).'&format=json');
434        $w = json_decode($v);
435        if (!$w) {
436                throw new Exception('YQL: r&#233;ponse vide ou mal form&#233;e');
437                return false;
438        }
439        return (array) $w;
440}
441
442/**
443 * sql -> tableau
444 * @param string $u
445 * @return array|bool
446 */
447function inc_sql_to_array_dist($u) {
448        # sortir le connecteur de $u
449        preg_match(',^(?:(\w+):)?(.*)$,S', $u, $v);
450        $serveur = (string) $v[1];
451        $req = trim($v[2]);
452        if ($s = sql_query($req, $serveur)) {
453                $r = array();
454                while ($t = sql_fetch($s))
455                        $r[] = $t;
456                return $r;
457        }
458        return false;
459}
460
461/**
462 * json -> tableau
463 * @param string $u
464 * @return array|bool
465 */
466function inc_json_to_array_dist($u) {
467        if (is_array($json = json_decode($u))
468        OR is_object($json))
469                return (array) $json;
470}
471
472/**
473 * csv -> tableau
474 * @param string $u
475 * @return array|bool
476 */
477function inc_csv_to_array_dist($u) {
478        include_spip('inc/csv');
479        list($entete,$csv) = analyse_csv($u);
480        array_unshift($csv,$entete);
481
482        include_spip('inc/charsets');
483        foreach ($entete as $k => $v) {
484                if (is_numeric($v) and $v < 0) $v = "__".$v; // ne pas risquer d'ecraser une cle numerique
485                if (is_numeric($v)) $v = "_".$v; // ne pas risquer d'ecraser une cle numerique
486                $v = strtolower(preg_replace(',\W+,', '_', translitteration($v)));
487                foreach ($csv as &$item)
488                        $item[$v] = &$item[$k];
489        }
490        return $csv;
491}
492
493/**
494 * RSS -> tableau
495 * @param string $u
496 * @return array|bool
497 */
498function inc_rss_to_array_dist($u) {
499        include_spip('inc/syndic');
500        if (is_array($rss = analyser_backend($u)))
501                $tableau = $rss;
502        return $tableau;
503}
504
505/**
506 * atom, alias de rss -> tableau
507 * @param string $u
508 * @return array|bool
509 */
510function inc_atom_to_array_dist($u) {
511        $g = charger_fonction('rss_to_array', 'inc');
512        return $g($u);
513}
514
515/**
516 * glob -> tableau
517 * lister des fichiers selon un masque, pour la syntaxe cf php.net/glob
518 * @param string $u
519 * @return array|bool
520 */
521function inc_glob_to_array_dist($u) {
522        return (array) glob($u,
523                GLOB_MARK | GLOB_NOSORT | GLOB_BRACE
524        );
525}
526
527/**
528 * pregfiles -> tableau
529 * lister des fichiers a partir d'un dossier de base et selon une regexp.
530 * pour la syntaxe cf la fonction spip preg_files
531 * @param string $dir
532 * @param string $regexp
533 * @param int $limit
534 * @return array|bool
535 */
536function inc_pregfiles_to_array_dist($dir, $regexp=-1, $limit=10000) {
537        return (array) preg_files($dir, $regexp, $limit);
538}
539
540/**
541 * ls -> tableau
542 * ls : lister des fichiers selon un masque glob
543 * et renvoyer aussi leurs donnees php.net/stat
544 * @param string $u
545 * @return array|bool
546 */
547function inc_ls_to_array_dist($u) {
548        $glob = charger_fonction('glob_to_array', 'inc');
549        $a = $glob($u);
550        foreach ($a as &$v) {
551                $b = (array) @stat($v);
552                foreach ($b as $k => $ignore)
553                        if (is_numeric($k)) unset($b[$k]);
554                $b['file'] = basename($v);
555                $v = array_merge(
556                        pathinfo($v),
557                        $b
558                );
559        }
560        return $a;
561}
562
563/**
564 * Object -> tableau
565 * @param Object $object
566 * @return array|bool
567 */
568function ObjectToArray($object){
569        $xml_array = array();
570        for( $object->rewind(); $object->valid(); $object->next() ) {
571                if(array_key_exists($key = $object->key(), $xml_array)){
572                        $key .= '-'.uniqid();
573                }
574                $vars = get_object_vars($object->current());
575                if (isset($vars['@attributes']))
576                        foreach($vars['@attributes'] as $k => $v)
577                        $xml_array[$key][$k] = $v;
578                if($object->hasChildren()){
579                        $xml_array[$key][] = ObjectToArray(
580                                $object->current());
581                }
582                else{
583                        $xml_array[$key][] = strval($object->current());
584                }
585        }
586        return $xml_array;
587}
588?>
Note: See TracBrowser for help on using the repository browser.