source: spip-zone/_plugins_/isocode/trunk/inc/isocode_read.php @ 100330

Last change on this file since 100330 was 100330, checked in by eric@…, 5 years ago

PSR SPIP.
Les fonctions de lectures par type de source ont été remplacées par une seule fonction générique.

  • Property svn:eol-style set to native
File size: 11.3 KB
Line 
1<?php
2/**
3 * Ce fichier contient la fonction générique de lecture d'un fichier CSV en un tableau d'éléments d'une
4 * table de la base de données.
5 *
6 * @package SPIP\ISOCODE\OUTILS
7 */
8if (!defined('_ECRIRE_INC_VERSION')) {
9        return;
10}
11
12
13/**
14 * Constitue, à partir, d'un fichier CSV ou XML ou d'une page HTML au format texte, un tableau des éléments
15 * prêt à être inséré dans une table de la base de données.
16 * La fonction utilise le service et le nom de table pour récupérer la configuration permettant l'analyse
17 * du fichier et sa traduction en élements de la table (délimiteur ou regexp, nom des colonnes...).
18 * Il est possible, pour chaque élément ou pour l'ensemble d'appliquer une fonction spécifique à la table
19 * qui complète l'élément.
20 *
21 * @param string $service
22 *        Nom du service associé à la lecture de la table.
23 * @param string $table
24 *        Nom de la table concernée par la lecture de la source.
25 *
26 * @return array
27 *        Tableau à deux éléments:
28 *        - index 0 : la liste des éléments à enregistrer dans la table concernée
29 *        - index 1 : le sha256 du fichier CSV source des éléments de la table
30 */
31function inc_isocode_read($service, $table) {
32
33        // Initialisations
34        $records = array();
35        $sha_file = false;
36        $f_complete_record = "${table}_complete_by_record";
37        $f_complete_table = "${table}_complete_by_table";
38
39        // Inclusion des configurations et des fonctions spécifiques au service qui fournit les données
40        // de la table à remplir.
41        include_spip("services/${service}/${service}_api");
42
43        // Acquisition de la configuration de lecture pour la table concernée.
44        $config = $GLOBALS['isocode'][$service]['tables'][$table];
45
46        // Détermination de la clé primaire de la table
47        $primary_key_table = get_primary_key($table);
48
49        // Initialisation d'un élément de la table par défaut (uniquement les champs de base)
50        // Cela permet de s'assurer que chaque élément du tableau de sortie aura la même structure
51        // quelque soit les données lues dans la source.
52        $default_fields = init_element_fields($table, $config['basic_fields']);
53
54        // Récupération du contenu du fichier ou de la page HTML source et du sha associé. Pour les fichiers CSV
55        // on renvoie aussi la liste des titres des colonnes.
56        list($content, $header, $sha) = get_source_content($service, $table, $config);
57        if ($content and $sha and $default_fields) {
58                // On n'analyse le contenu que si celui-ci a changé (sha différent de celui stocké).
59                include_spip('isocode_fonctions');
60                if (!isocode_comparer_sha($sha, $table)) {
61                        $primary_key_values = array();
62                        foreach ($content as $_element) {
63                                // Pour chaque élément on récupère un tableau associatif [titre colonne] = valeur colonne.
64                                $values = get_element_values($_element, $header, $config);
65                                // Création de chaque enregistrement de la table
66                                $fields = $default_fields;
67                                $pkey_element_exists = false;
68                                $pkey_element = array();
69                                foreach ($values as $_key => $_value) {
70                                        $key = trim($_key);
71                                        // Seuls les champs identifiés dans la configuration sont récupérés dans le fichier
72                                        if (isset($fields_config[$key])) {
73                                                $fields[$fields_config[$key]] = $_value ? trim($_value) : '';
74                                                // Vérifier si le champ en cours fait partie de la clé primaire et élaborer la clé
75                                                // primaire de l'élément en cours
76                                                if (in_array($fields_config[$key], $primary_key_table['list'])) {
77                                                        $pkey_element[$fields_config[$key]] = $_value;
78                                                        if (count($pkey_element) == $primary_key_table['count']) {
79                                                                $pkey_element_exists = true;
80                                                        }
81                                                }
82                                        } else {
83                                                spip_log("Le champ <${_key}> n'existe pas dans la configuration de la table ${table}", 'isocode' . _LOG_INFO);
84                                        }
85                                }
86                                // On ajoute l'élément que si la clé primaire a bien été trouvée et si la valeur de cette clé
87                                // n'est pas en doublon avec un élément déjà enregistré.
88                                if ($pkey_element_exists) {
89                                        ksort($pkey_element);
90                                        $pkey_element_value = implode(',', $pkey_element);
91                                        if (!in_array($pkey_element_value, $primary_key_values)) {
92                                                // On rajoute cette clé dans la liste
93                                                $primary_key_values[] = $pkey_element_value;
94                                                // Si besoin on appelle une fonction pour chaque enregistrement afin de le compléter
95                                                if (function_exists($f_complete_record)) {
96                                                        $fields = $f_complete_record($fields);
97                                                }
98                                                $records[] = $fields;
99                                        } else {
100                                                spip_log("L'entrée de clé primaire <${pkey_element_value}> de la table <${table}> est en doublon", 'isocode' . _LOG_ERREUR);
101                                        }
102                                } else {
103                                        spip_log("L'entrée <" . var_export($_element, true) . "> de la table <${table}> n'a pas de clé primaire", 'isocode' . _LOG_ERREUR);
104                                }
105                        }
106                        // Si besoin on appelle une fonction pour toute la table
107                        if (function_exists($f_complete_table)) {
108                                $records = $f_complete_table($records);
109                        }
110                }
111        }
112
113        return array($records, $sha_file);
114}
115
116
117/**
118 * @param $table
119 *
120 * @return array
121 */
122function get_primary_key($table) {
123
124        include_spip('base/objets');
125        $primary_key = array();
126
127        if ($id_key = id_table_objet($table)) {
128                // On stocke la clé sous forme de liste pour les tests d'appartenance.
129                $primary_key['list'] = explode(',', $id_key);
130                // On trie la liste et on recompose la clé sous forme de chaine pour la gestion des doublons.
131                sort($primary_key['list']);
132                $primary_key['name'] = implode(',', $primary_key['list']);
133                // On stocke le nombre de champs de la clé.
134                $primary_key['count'] = count($primary_key['list']);
135        }
136
137        return $primary_key;
138}
139
140
141/**
142 * Initialise un élément d'une table donnée avec les valeurs par défaut configurées dans
143 * la déclaration de la base ou avec une valeur prédéfinie par type.
144 *
145 * @param string $table
146 *        Nom de la table concernée par la lecture sans le préfixe `spip_`.
147 * @param array  $fields_config
148 *        Configuration de la correspondance entre le nom de la donnée dans la source
149 *        et celui du champ dans la table.
150 *
151 * @return array
152 */
153function init_element_fields($table, $fields_config) {
154
155        $fields = array();
156
157        // Acquisition de la description de la table (champs, clés, jointures) et définition
158        // de la regexp permettant d'isoler la valeur par défaut si elle existe.
159        $description = sql_showtable("spip_${table}");
160        $regexp_default = '/DEFAULT\s+\'(.*)\'/i';
161
162        if (!empty($description['field'])) {
163                foreach ($fields_config as $_field) {
164                        if (isset($description['field'][$_field])) {
165                                // On normalise la description du champ en supprimant les espaces inutiles
166                                $description['field'][$_field] = preg_replace('/\s2,/', ' ', $description['field'][$_field]);
167                                $field_description = explode(' ', $description['field'][$_field]);
168
169                                // On compare maintenant avec le format du champ
170                                if (isset($field_description[0])) {
171                                        $type = strtoupper($field_description[0]);
172
173                                        // On cherche une instruction DEFAULT
174                                        $default = null;
175                                        if (preg_match($regexp_default, $description['field'][$_field], $matches)) {
176                                                $default = $matches[1];
177                                        }
178
179                                        // On finalise l'initialisation du champ en fonction de son type
180                                        if ((strpos($type, 'CHAR') !== false) or (strpos($type, 'TEXT') !== false)
181                                                or (strpos($type, 'BLOB') !== false) or (strpos($type, 'BINARY') !== false)
182                                        ) {
183                                                $fields[$_field] = ($default != null) ? $default : '';
184                                        } elseif (strpos($type, 'DATE') !== false) {
185                                                $fields[$_field] = ($default != null) ? $default : '0000-00-00 00:00:00';
186                                        } else {
187                                                $fields[$_field] = ($default != null) ? intval($default) : 0;
188                                        }
189                                } else {
190                                        // On a un problème de configuration: on le trace et on arrête la boucle
191                                        // La table ne sera pas mise à jour.
192                                        $fields = array();
193                                        spip_log("La description du champ <${_field}> de la table <${table}> est mal formée", 'isocode' . _LOG_ERREUR);
194                                        break;
195                                }
196                        } else {
197                                // On a un problème de configuration: on le trace et on arrête la boucle.
198                                // La table ne sera pas mise à jour.
199                                $fields = array();
200                                spip_log("Le champ <${_field}> n'est pas un champ de la table <${table}>", 'isocode' . _LOG_ERREUR);
201                                break;
202                        }
203                }
204        }
205
206        return $fields;
207}
208
209/**
210 * @param $service
211 * @param $table
212 * @param $config
213 *        Configuration de la méthode de lecture de la source pour la table concernée.
214 *
215 * @return array
216 */
217function get_source_content($service, $table, $config) {
218
219        // Initialisation des données de sortie
220        $content = array();
221        $header = array();
222        $sha = false;
223
224        if ($config['populating'] == 'page_text') {
225                // Acquisition de la page ciblée par l'url
226                include_spip('inc/distant');
227                $options = array();
228                $flux = recuperer_url($config['url'], $options);
229                if (!empty($flux['page']) and ($sha = sha1($flux['page']))) {
230                        // Chaque élément est identifié soit par un délimiteur, soit par une regexp suivant la méthode configurée.
231                        if ($config['parsing']['element']['method'] == 'explode') {
232                                // On récupére donc un tableau des éléments à lire en utilisant la fonction explode
233                                $content = explode($config['parsing']['element']['delimiter'], $flux['page']);
234                        } else {
235                                // TODO : C'est une regexp... à compléter
236                        }
237                }
238        } else {
239                // La source est un fichier.
240                // On construit son nom et on lit son contenu en fonction du type du fichier.
241                $file = find_in_path("services/${service}/${table}{$config['extension']}");
242                if (file_exists($file) and ($sha = sha1_file($file))) {
243                        if ($config['populating'] == 'file_csv') {
244                                $lines = file($file);
245                                if ($lines) {
246                                        // La première ligne d'un CSV contient toujours les titres des colonnes.
247                                        // On sauvegarde ces titres dans une variable et on élimine la ligne du contenu retourné.
248                                        $header = explode($config['delimiter'], trim(array_shift($lines), "\r\n"));
249                                        $header = array_map('trim', $header);
250                                        // On renvoie le contenu sans titre
251                                        $content = $lines;
252                                }
253                        } elseif ($config['populating'] == 'file_xml') {
254                                include_spip('inc/flock');
255                                lire_fichier($file, $xml);
256                                $xml_tree = json_decode(json_encode(simplexml_load_string($xml)), true);
257
258                                include_spip('inc/filtres');
259                                if (table_valeur($xml_tree, $config['base'], '')) {
260                                        $content = table_valeur($xml_tree, $config['base'], '');
261                                }
262                        }
263                }
264        }
265
266        return array($content, $header, $sha);
267}
268
269
270/**
271 * @param $element
272 * @param $header
273 * @param $config
274 *
275 * @return array
276 */
277function get_element_values($element, $header, $config) {
278
279        $values = array();
280
281        if ($config['populating'] == 'file_csv') {
282                // Chaque valeur de colonne est séparée par le délimiteur configuré.
283                $columns = explode($config['delimiter'], trim($element, "\r\n"));
284                // On construit un tableau associatif [nom colonne] => valeur colonne
285                foreach ($header as $_key => $_header) {
286                        $values[$_header] = trim($columns[$_key]);
287                }
288        } elseif ($config['populating'] == 'page_text') {
289                // Chaque couple (nom donnée, valeur donnée) est identifiée par une REGEXP configurée
290                if (preg_match_all($config['parsing']['field']['regexp'], $element, $matches)) {
291                        // L'index 1 correspond à la liste des nom de données et l'index 2 à la liste des
292                        // valeur de données correspondantes.
293                        // Il faut donc reconstruire un tableau associatif [nom donnée] => valeur donnée
294                        foreach ($matches[1] as $_key => $_header) {
295                                $values[trim($_header)] = $matches[2][trim($_key)];
296                        }
297                }
298        } else {
299                // Fichier XML.
300                // Le tableau associatif (nom donnée, valeur donnée) est déjà correctement formé.
301                $values = $element;
302        }
303
304        return $values;
305}
Note: See TracBrowser for help on using the repository browser.