multiSchema = $multiSchema; } /** @var int */ protected $skipLines = 0; function setSkipLines(int $skipLines): void { $this->skipLines = $skipLines; } /** * @var ?bool faut-il analyser le premier élément du flux pour calculer la * liste des clés en entrée? null signifie que la valeur est dynamique: elle * vaut ($this->ppHeaders === null) * * Si ce champ est vrai, le premier élément est toujours consommé. cependant, * la liste des champs n'est analysée que si elle n'a pas été spécifiée au * préalable avec {@link setHeaders()} */ protected $parseHeaders; function setParseHeaders(?bool $parseHeaders): void { $this->parseHeaders = $parseHeaders; } /** * @var array liste des champs en entrée dans l'ordre. si cette valeur n'est * pas spécifiée, elle est calculée à partir du premier élément du flux. */ protected $headers; function setHeaders(?array $headers): void { $this->headers = $headers; } /** @var ?array mappings des colonnes vers les clés du tableau résultat */ protected $headerMappings; /** * $headerMappings peut être * - un tableau de la forme [include, exclude => null, dest => source] * - ou une chaine de la forme "include,=exclude,dest=source" * source est le nom de la colonne du flux CSV, dest est la clé dans le * tableau retourné * * - les éléments 'include' permettent d'inclure le champ spécifié. si aucun * champ include n'est spécifié, *tous* les champs sont inclus (sauf ceux qui * sont exclus, bien entendu) * - les éléments '=exclude' permettent d'exclure le champ spécifié. * - les éléments 'dest=source' permettent de renommer les champs: le champ * source dans le flux CSV devient le champ dest dans le tableau associatif */ function setHeaderMappings($headerMappings): void { if ($headerMappings !== null && !is_array($headerMappings)) { $mappings = explode(",", strval($headerMappings)); $headerMappings = []; foreach ($mappings as $mapping) { if (($index = strpos($mapping, "=")) !== false) { $dest = substr($mapping, 0, $index); $source = substr($mapping, $index + 1); if ($dest && $source) $headerMappings[$dest] = $source; elseif ($dest) $headerMappings[$dest] = null; else $headerMappings[$source] = null; } else { $headerMappings[] = $mapping; } } } $this->headerMappings = $headerMappings; } /** @var bool */ protected $shouldMapEmpty = false; /** @var mixed */ protected $mapEmpty = null; function setMapEmpty($mapEmpty): void { $this->shouldMapEmpty = true; $this->mapEmpty = $mapEmpty; } function _parseLine(): bool { if ($this->skipLines > 0) { $this->skipLines--; return false; } return true; } function checkHeaders(array $values): bool { if ($this->multiSchema && count($values) === 1 && $values[0] === null) { # ligne vide, changer de schéma $this->headers = null; return true; } $parseHeaders = $this->parseHeaders; $setParseHeaders = $parseHeaders !== null; if ($parseHeaders === null) $parseHeaders = $this->headers === null; if ($parseHeaders) { if ($this->headers === null) $this->headers = $values; if ($setParseHeaders) $this->parseHeaders = false; return true; } return false; } function mapValues(array $values): array { $headers = $this->headers; if ($headers === null) return $values; $row = []; $index = 0; foreach ($headers as $header) { $row[$header] = A::get($values, $index++, false); } return $row; } function mapRow(array $values): array { $row = $this->mapValues($values); $row = ut::reader_map_keys($row, $this->headerMappings); foreach ($row as &$value) { if ($value === "" && $this->shouldMapEmpty) $value = $this->mapEmpty; }; unset($value); return $row; } }