source: spip-zone/_plugins_/isocode/trunk/inc/isocode_sourcer.php @ 100347

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

Renommage.
Refactoring des messages du formulaire.

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