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

Last change on this file since 103864 was 103864, checked in by spip.franck@…, 4 years ago

En finir une fois pour toutes avec spip-contrib, pour mettre à la place contrib.spip

File size: 16.2 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 @XMLObjectToArray(new SimpleXmlIterator($u));
423}
424/**
425 *
426 * object -> tableau
427 *
428 * @param  object $object The object to convert
429 * @return array
430 *
431 */
432function inc_object_to_array( $object ) {
433        if( !is_object( $object ) && !is_array( $object ) ) {
434                return $object;
435        }
436        if( is_object( $object ) ) {
437                $object = get_object_vars( $object );
438        }
439        return array_map( 'inc_object_to_array', $object );
440}
441
442/**
443 * yql -> tableau
444 * @throws Exception
445 * @param  string $u
446 * @return array|bool
447 */
448function inc_yql_to_array_dist($u) {
449        define('_YQL_ENDPOINT', 'http://query.yahooapis.com/v1/public/yql?&env=store%3A%2F%2Fdatatables.org%2Falltableswithkeys&q=');
450        $v = recuperer_page($url = _YQL_ENDPOINT.urlencode($u).'&format=json');
451        $w = json_decode($v);
452        if (!$w) {
453                throw new Exception('YQL: r&#233;ponse vide ou mal form&#233;e');
454                return false;
455        }
456        return inc_object_to_array($w);
457}
458
459/**
460 * sql -> tableau
461 * @param string $u
462 * @return array|bool
463 */
464function inc_sql_to_array_dist($u) {
465        # sortir le connecteur de $u
466        preg_match(',^(?:(\w+):)?(.*)$,S', $u, $v);
467        $serveur = (string) $v[1];
468        $req = trim($v[2]);
469        if ($s = sql_query($req, $serveur)) {
470                $r = array();
471                while ($t = sql_fetch($s))
472                        $r[] = $t;
473                return $r;
474        }
475        return false;
476}
477
478/**
479 * json -> tableau
480 * @param string $u
481 * @return array|bool
482 */
483function inc_json_to_array_dist($u) {
484        if (is_array($json = json_decode($u))
485        OR is_object($json))
486                return (array) $json;
487}
488
489/**
490 * csv -> tableau
491 * @param string $u
492 * @return array|bool
493 */
494function inc_csv_to_array_dist($u) {
495        include_spip('inc/csv');
496        list($entete,$csv) = analyse_csv($u);
497        array_unshift($csv,$entete);
498
499        include_spip('inc/charsets');
500        $i = 1 ;
501        foreach ($entete as $k => $v) {
502                if (trim($v) == "") $v = "col".$i ; // reperer des eventuelles cases vides
503                if (is_numeric($v) and $v < 0) $v = "__".$v; // ne pas risquer d'ecraser une cle numerique
504                if (is_numeric($v)) $v = "_".$v; // ne pas risquer d'ecraser une cle numerique
505                $v = strtolower(preg_replace(',\W+,', '_', translitteration($v)));
506                foreach ($csv as &$item)
507                        $item[$v] = &$item[$k];
508                $i++;   
509        }
510        return $csv;
511}
512
513/**
514 * RSS -> tableau
515 * @param string $u
516 * @return array|bool
517 */
518function inc_rss_to_array_dist($u) {
519        include_spip('inc/syndic');
520        if (is_array($rss = analyser_backend($u)))
521                $tableau = $rss;
522        return $tableau;
523}
524
525/**
526 * atom, alias de rss -> tableau
527 * @param string $u
528 * @return array|bool
529 */
530function inc_atom_to_array_dist($u) {
531        $g = charger_fonction('rss_to_array', 'inc');
532        return $g($u);
533}
534
535/**
536 * glob -> tableau
537 * lister des fichiers selon un masque, pour la syntaxe cf php.net/glob
538 * @param string $u
539 * @return array|bool
540 */
541function inc_glob_to_array_dist($u) {
542        $a = glob($u,
543                GLOB_MARK | GLOB_NOSORT | GLOB_BRACE
544        );
545        return $a ? $a : array();
546}
547
548/**
549 * pregfiles -> tableau
550 * lister des fichiers a partir d'un dossier de base et selon une regexp.
551 * pour la syntaxe cf la fonction spip preg_files
552 * @param string $dir
553 * @param string $regexp
554 * @param int $limit
555 * @return array|bool
556 */
557function inc_pregfiles_to_array_dist($dir, $regexp=-1, $limit=10000) {
558        return (array) preg_files($dir, $regexp, $limit);
559}
560
561/**
562 * ls -> tableau
563 * ls : lister des fichiers selon un masque glob
564 * et renvoyer aussi leurs donnees php.net/stat
565 * @param string $u
566 * @return array|bool
567 */
568function inc_ls_to_array_dist($u) {
569        $glob = charger_fonction('glob_to_array', 'inc');
570        $a = $glob($u);
571        foreach ($a as &$v) {
572                $b = (array) @stat($v);
573                foreach ($b as $k => $ignore)
574                        if (is_numeric($k)) unset($b[$k]);
575                $b['file'] = basename($v);
576                $v = array_merge(
577                        pathinfo($v),
578                        $b
579                );
580        }
581        return $a;
582}
583
584/**
585 * Object -> tableau
586 * @param Object $object
587 * @return array|bool
588 */
589function XMLObjectToArray($object){
590        $xml_array = array();
591        for( $object->rewind(); $object->valid(); $object->next() ) {
592                if(array_key_exists($key = $object->key(), $xml_array)){
593                        $key .= '-'.uniqid();
594                }
595                $vars = get_object_vars($object->current());
596                if (isset($vars['@attributes']))
597                        foreach($vars['@attributes'] as $k => $v)
598                        $xml_array[$key][$k] = $v;
599                if($object->hasChildren()){
600                        $xml_array[$key][] = XMLObjectToArray(
601                                $object->current());
602                }
603                else{
604                        $xml_array[$key][] = strval($object->current());
605                }
606        }
607        return $xml_array;
608}
609
610/**
611 * xml -> tableau
612 * @param  string $u
613 * @return array
614 * from cerdic et marcimat sur https://contrib.spip.net/Les-Iterateurs-pour-SPIP-2-1#forum457898
615 */
616
617function inc_simplexml_to_array_dist($u){
618        return array('root'=>@xmlObjToArr(simplexml_load_string($u)));
619}
620     
621    // http://www.php.net/manual/pt_BR/book.simplexml.php#108688 (17 mai 2012)
622    function xmlObjToArr($obj) {
623    # Cette fonction getDocNamespaces est tres gourmande sur de gros fichiers
624    # $namespace = $obj->getDocNamespaces(true);
625     
626            $namespace[NULL] = NULL;
627     
628            $children = array();
629            $attributes = array();
630            $name = strtolower((string)$obj->getName());
631     
632            $text = trim((string)$obj);
633            if( strlen($text) <= 0 ) {
634                $text = NULL;
635            }
636     
637            // get info for all namespaces
638            if(is_object($obj)) {
639                foreach( $namespace as $ns=>$nsUrl ) {
640                    // atributes
641                    $objAttributes = $obj->attributes($ns, true);
642                    foreach( $objAttributes as $attributeName => $attributeValue ) {
643                        $attribName = strtolower(trim((string)$attributeName));
644                        $attribVal = trim((string)$attributeValue);
645                        if (!empty($ns)) {
646                            $attribName = $ns . ':' . $attribName;
647                        }
648                        $attributes[$attribName] = $attribVal;
649                    }
650     
651                    // children
652                    $objChildren = $obj->children($ns, true);
653                    foreach( $objChildren as $childName=>$child ) {
654                        $childName = strtolower((string)$childName);
655                        if( !empty($ns) ) {
656                            $childName = $ns.':'.$childName;
657                        }
658                        $children[$childName][] = xmlObjToArr($child);
659                    }
660                }
661            }
662     
663            return array(
664                'name'=>$name,
665                'text'=>$text,
666                'attributes'=>$attributes,
667                'children'=>$children
668            );
669        }
670?>
Note: See TracBrowser for help on using the repository browser.