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

Last change on this file since 43390 was 43390, checked in by cedric@…, 10 years ago

report de

http://core.spip.org/trac/spip/changeset/16909 : tri {par cle} sur la boucle (DATA)
http://core.spip.org/trac/spip/changeset/16908 : PhpDoc? sur iterateur (DATA)

File size: 12.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'
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                // les commandes connues pour l'iterateur POUR/DATA
144                // sont : {tableau #ARRAY} ; cle=...; valeur=...
145                // source URL
146                if (isset($this->command['source'])
147                AND isset($this->command['sourcemode'])) {
148
149                        # un peu crado : avant de charger le cache il faut charger
150                        # les class indispensables, sinon PHP ne saura pas gerer
151                        # l'objet en cache ; cf plugins/icalendar
152                        if (isset($this->command['sourcemode']))
153                                charger_fonction($this->command['sourcemode'] . '_to_array', 'inc', true);
154
155                        # avons-nous un cache dispo ?
156                        if (is_string($this->command['source']))
157                                $cle = 'datasource_'.md5($this->command['sourcemode'].':'.$this->command['source']);
158                        $cache = $this->cache_get($cle);
159                        if (isset($this->command['datacache']))
160                                $ttl = intval($this->command['datacache']);
161                        if ($cache
162                        AND ($cache['time'] + (isset($ttl) ? $ttl : $cache['ttl'])
163                                > time())
164                        AND !(_request('var_mode') === 'recalcul'
165                                AND include_spip('inc/autoriser')
166                                AND autoriser('recalcul')
167                        )) {
168                                $this->tableau = $cache['data'];
169                        }
170                        else try {
171                                # dommage que ca ne soit pas une option de yql_to_array...
172                                if ($this->command['sourcemode'] == 'yql')
173                                        if (!isset($ttl)) $ttl = 3600;
174
175                                if (isset($this->command['sourcemode'])
176                                AND in_array($this->command['sourcemode'],
177                                        array('table', 'array', 'tableau'))
178                                ) {
179                                        if (is_array($a = $this->command['source'])
180                                        OR (is_string($a)
181                                        AND $a = str_replace('&quot;', '"', $a) # fragile!
182                                        AND is_array($a = @unserialize($a)))
183                                        )
184                                                $this->tableau = $a;
185                                }
186                                else if (preg_match(',^https?://,', $this->command['source'])) {
187                                        include_spip('inc/distant');
188                                        $u = recuperer_page($this->command['source']);
189                                        if (!$u)
190                                                throw new Exception("404");
191                                        if (!isset($ttl)) $ttl = 24*3600;
192                                } else if (@is_readable($this->command['source'])) {
193                                        $u = spip_file_get_contents($this->command['source']);
194                                        if (!isset($ttl)) $ttl = 10;
195                                } else {
196                                        $u = $this->command['source'];
197                                        if (!isset($ttl)) $ttl = 10;
198                                }
199
200                                if (!$this->err
201                                AND $g = charger_fonction($this->command['sourcemode'] . '_to_array', 'inc', true)) {
202                                        if (is_array($a = $g($u))) {
203                                                $this->tableau = $a;
204                                        }
205                                }
206
207                                if (!is_array($this->tableau))
208                                        $this->err = true;
209
210                                if (!$this->err AND $ttl>0)
211                                        $this->cache_set($cle, $ttl);
212
213                        }
214                        catch (Exception $e) {
215                                $e = $e->getMessage();
216                                $err = sprintf("[%s, %s] $e",
217                                        $this->command['source'],
218                                        $this->command['sourcemode']);
219                                erreur_squelette(array($err, array()));
220                                $this->err = true;
221                        }
222
223                        # en cas d'erreur, utiliser le cache si encore dispo
224                        if ($this->err
225                        AND $cache) {
226                                $this->tableau = $cache['data'];
227                                $this->err = false;
228                        }
229
230                }
231
232                // Critere {liste X1, X2, X3}
233                if (isset($this->command['liste'])) {
234                        $this->tableau = $this->command['liste'];
235                }
236
237                // Si a ce stade on n'a pas de table, il y a un bug
238                if (!is_array($this->tableau)) {
239                        $this->err = true;
240                        spip_log("erreur datasource ".$this->command['source']);
241                }
242
243
244                // {datapath query.results}
245                // extraire le chemin "query.results" du tableau de donnees
246                if (!$this->err
247                AND is_array($this->command['datapath'])) {
248                        list(,$base) = each($this->command['datapath']);
249                        if (strlen($base = trim($base))) {
250                                $this->tableau = Iterateurs_table_valeur($this->tableau, $base);
251                                if (!is_array($this->tableau)) {
252                                        $this->tableau = array();
253                                        $this->err = true;
254                                        spip_log("datapath '$base' absent");
255                                }
256                        }
257                }
258
259                // tri {par x}
260                if ($this->command['orderby']) {
261                        $sortfunc = '';
262                        $aleas = 0;
263                        foreach($this->command['orderby'] as $tri) {
264                                if (preg_match(',^\.?([/\w]+)( DESC)?$,iS', $tri, $r)) {
265                                        // tri par cle
266                                        if ($r[1] == 'cle'){
267                                                ksort($this->tableau);
268                                        }
269                                        else {
270                                                # {par valeur}
271                                                if ($r[1] == 'valeur')
272                                                        $tv = '%s';
273                                                # {par hasard}
274                                                else if ($r[1] == 'alea') {
275                                                        if (!$aleas)
276                                                        $sortfunc .= 'static $aleas = array();';
277                                                        $aleas ++;
278                                                        $tv = '(isset($aleas['.$aleas.'][$v=%s])?$aleas['.$aleas.'][$v]:($aleas['.$aleas.'][$v]=rand(0,1)))';
279                                                }
280                                                # {par valeur/xx/yy} ??
281                                                else
282                                                        $tv = 'Iterateurs_table_valeur(%s, '.var_export($r[1],true).')';
283                                                $sortfunc .= '
284                                                $a = '.sprintf($tv,'$aa').';
285                                                $b = '.sprintf($tv,'$bb').';
286                                                if ($a <> $b)
287                                                        return ($a ' . ($r[2] ? '>' : '<').' $b) ? -1 : 1;';
288                                        }
289                                }
290                        }
291
292                        if ($sortfunc) {
293                                uasort($this->tableau, create_function('$aa,$bb',
294                                        $sortfunc.'
295                                        return 0;'
296                                ));
297                        }
298                }
299
300                // grouper les resultats {fusion /x/y/z} ;
301                if ($this->command['groupby']
302                AND strlen($fusion = $this->command['groupby'][0])) {
303                        $vu = array();
304                        foreach($this->tableau as $k => $v) {
305                                $val = Iterateurs_table_valeur($v, $fusion);
306                                if (isset($vu[$val]))
307                                        unset($this->tableau[$k]);
308                                else
309                                        $vu[$val] = true;
310                        }
311                }
312
313                $this->rewind();
314                #var_dump($this->tableau);
315        }
316
317
318        /**
319         * L'iterateur est-il encore valide ?
320         * @return bool
321         */
322        public function valid(){
323                return !is_null($this->cle);
324        }
325
326        /**
327         * Retourner la valeur
328         * @return null
329         */
330        public function current() {
331                return $this->valeur;
332        }
333
334        /**
335         * Retourner la cle
336         * @return null
337         */
338        public function key() {
339                return $this->cle;
340        }
341
342        /**
343         * Passer a la valeur suivante
344         * @return void
345         */
346        public function next(){
347                if ($this->valid())
348                        list($this->cle, $this->valeur) = each($this->tableau);
349        }
350
351        /**
352         * Compter le nombre total de resultats
353         * @return int
354         */
355        public function count() {
356                if (is_null($this->total))
357                        $this->total = count($this->tableau);
358          return $this->total;
359        }
360}
361
362/*
363 * Fonctions de transformation donnee => tableau
364 */
365
366/**
367 * file -> tableau
368 *
369 * @param  string $u
370 * @return array
371 */
372function inc_file_to_array_dist($u) {
373        return preg_split('/\r?\n/', $u);
374}
375
376/**
377 * plugins -> tableau
378 * @return unknown
379 */
380function inc_plugins_to_array_dist() {
381        include_spip('inc/plugin');
382        return liste_chemin_plugin_actifs();
383}
384
385/**
386 * xml -> tableau
387 * @param  string $u
388 * @return array
389 */
390function inc_xml_to_array_dist($u) {
391        return @ObjectToArray(new SimpleXmlIterator($u));
392}
393
394/**
395 * yql -> tableau
396 * @throws Exception
397 * @param  string $u
398 * @return array|bool
399 */
400function inc_yql_to_array_dist($u) {
401        define('_YQL_ENDPOINT', 'http://query.yahooapis.com/v1/public/yql?&env=store%3A%2F%2Fdatatables.org%2Falltableswithkeys&q=');
402        $v = recuperer_page($url = _YQL_ENDPOINT.urlencode($u).'&format=json');
403        $w = json_decode($v);
404        if (!$w) {
405                throw new Exception('YQL: r&#233;ponse vide ou mal form&#233;e');
406                return false;
407        }
408        return (array) $w;
409}
410
411/**
412 * sql -> tableau
413 * @param string $u
414 * @return array|bool
415 */
416function inc_sql_to_array_dist($u) {
417        # sortir le connecteur de $u
418        preg_match(',^(?:(\w+):)?(.*)$,S', $u, $v);
419        $serveur = (string) $v[1];
420        $req = trim($v[2]);
421        if ($s = sql_query($req, $serveur)) {
422                $r = array();
423                while ($t = sql_fetch($s))
424                        $r[] = $t;
425                return $r;
426        }
427        return false;
428}
429
430/**
431 * json -> tableau
432 * @param string $u
433 * @return array|bool
434 */
435function inc_json_to_array_dist($u) {
436        if (is_array($json = json_decode($u))
437        OR is_object($json))
438                return (array) $json;
439}
440
441/**
442 * csv -> tableau
443 * @param string $u
444 * @return array|bool
445 */
446function inc_csv_to_array_dist($u) {
447        include_spip('inc/csv');
448        list($entete,$csv) = analyse_csv($u);
449        array_unshift($csv,$entete);
450
451        include_spip('inc/charsets');
452        foreach ($entete as $k => $v) {
453                $v = strtolower(preg_replace(',\W+,', '_', translitteration($v)));
454                foreach ($csv as &$item)
455                        $item[$v] = &$item[$k];
456        }
457        return $csv;
458}
459
460/**
461 * RSS -> tableau
462 * @param string $u
463 * @return array|bool
464 */
465function inc_rss_to_array_dist($u) {
466        include_spip('inc/syndic');
467        if (is_array($rss = analyser_backend($u)))
468                $tableau = $rss;
469        return $tableau;
470}
471
472/**
473 * atom, alias de rss -> tableau
474 * @param string $u
475 * @return array|bool
476 */
477function inc_atom_to_array_dist($u) {
478        $g = charger_fonction('rss_to_array', 'inc');
479        return $g($u);
480}
481
482/**
483 * glob -> tableau
484 * lister des fichiers selon un masque, pour la syntaxe cf php.net/glob
485 * @param string $u
486 * @return array|bool
487 */
488function inc_glob_to_array_dist($u) {
489        return (array) glob($u,
490                GLOB_MARK | GLOB_NOSORT | GLOB_BRACE
491        );
492}
493
494/**
495 * ls -> tableau
496 * ls : lister des fichiers selon un masque glob
497 * et renvoyer aussi leurs donnees php.net/stat
498 * @param string $u
499 * @return array|bool
500 */
501function inc_ls_to_array_dist($u) {
502        $glob = charger_fonction('glob_to_array', 'inc');
503        $a = $glob($u);
504        foreach ($a as &$v) {
505                $b = (array) @stat($v);
506                foreach ($b as $k => $ignore)
507                        if (is_numeric($k)) unset($b[$k]);
508                $b['file'] = basename($v);
509                $v = array_merge(
510                        pathinfo($v),
511                        $b
512                );
513        }
514        return $a;
515}
516
517/**
518 * Object -> tableau
519 * @param Object $object
520 * @return array|bool
521 */
522function ObjectToArray($object){
523        $xml_array = array();
524        for( $object->rewind(); $object->valid(); $object->next() ) {
525                if(!array_key_exists($object->key(), $xml_array)){
526                        $xml_array[$object->key()] = array();
527                }
528                $vars = get_object_vars($object->current());
529                if (isset($vars['@attributes']))
530                        foreach($vars['@attributes'] as $k => $v)
531                        $xml_array[$object->key()][$k] = $v;
532                if($object->hasChildren()){
533                        $xml_array[$object->key()][] = ObjectToArray(
534                                $object->current());
535                }
536                else{
537                        $xml_array[$object->key()][] = strval($object->current());
538                }
539        }
540        return $xml_array;
541}
542?>
Note: See TracBrowser for help on using the repository browser.