CyberJack

CSV reader using an iterator

Apr 28th, 2023
61
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
PHP 3.89 KB | None | 0 0
  1. <?php
  2.  
  3. declare(strict_types=1);
  4.  
  5. use Iterator;
  6. use SplFileObject;
  7.  
  8. use function array_combine;
  9. use function array_map;
  10. use function strlen;
  11. use function trim;
  12.  
  13. /**
  14.  * @implements Iterator<int, mixed>
  15.  */
  16. final class CsvReader implements Iterator
  17. {
  18.     private const BOM = "\xef\xbb\xbf";
  19.  
  20.     private bool $skipBom = false;
  21.     private bool $trimKeys = false;
  22.     private bool $trimValues = false;
  23.  
  24.     /** @var string[] */
  25.     private array $keys = [];
  26.  
  27.     public function __construct(
  28.         private readonly SplFileObject $fileObject,
  29.     ) {
  30.         $this->fileObject->setFlags(SplFileObject::READ_CSV);
  31.     }
  32.  
  33.     public function setDelimiter(string $delimiter): CsvReader
  34.     {
  35.         $this->fileObject->setCsvControl($delimiter);
  36.         $this->rewind();
  37.         return $this;
  38.     }
  39.  
  40.     public function setSkipEmptyLines(bool $skipEmptyRows): CsvReader
  41.     {
  42.         $flags = SplFileObject::READ_CSV;
  43.         if ($skipEmptyRows) {
  44.             $flags |= SplFileObject::READ_AHEAD;
  45.             $flags |= SplFileObject::SKIP_EMPTY;
  46.             $flags |= SplFileObject::DROP_NEW_LINE;
  47.         }
  48.         $this->fileObject->setFlags($flags);
  49.         $this->rewind();
  50.         return $this;
  51.     }
  52.  
  53.     public function setSkipBom(bool $skipBom): CsvReader
  54.     {
  55.         $this->skipBom = $skipBom;
  56.         return $this;
  57.     }
  58.  
  59.     public function setTrimKeys(bool $trimKeys): CsvReader
  60.     {
  61.         $this->trimKeys = $trimKeys;
  62.         $this->rewind();
  63.         return $this;
  64.     }
  65.  
  66.     public function setTrimValues(bool $trimValues): CsvReader
  67.     {
  68.         $this->trimValues = $trimValues;
  69.         $this->rewind();
  70.         return $this;
  71.     }
  72.  
  73.     public function rewind(): void
  74.     {
  75.         if ($this->skipBom) {
  76.             $flags = $this->fileObject->getFlags();
  77.             $this->fileObject->setFlags(0);
  78.             $this->fileObject->rewind();
  79.             $potentialBom = $this->fileObject->fread(strlen(self::BOM));
  80.             $this->fileObject->setFlags($flags);
  81.             if ($potentialBom !== self::BOM) {
  82.                 $this->fileObject->rewind();
  83.             }
  84.         } else {
  85.             $this->fileObject->rewind();
  86.         }
  87.         /** @var array<string> $keys */
  88.         $keys = $this->fileObject->current();
  89.         $this->keys = $this->trimKeys ? array_map('trim', $keys) : $keys;
  90.         $this->fileObject->next();
  91.     }
  92.  
  93.     public function valid(): bool
  94.     {
  95.         return $this->fileObject->valid();
  96.     }
  97.  
  98.     /**
  99.      * @return array<string, mixed>
  100.      */
  101.     public function current(): array
  102.     {
  103.         /** @var array<string> $values */
  104.         $values = $this->fileObject->current();
  105.         if ($this->trimValues) {
  106.             $values = array_map('trim', $values);
  107.         }
  108.         return array_combine($this->keys, $values);
  109.     }
  110.  
  111.     public function key(): int
  112.     {
  113.         return $this->fileObject->key() + 1;
  114.     }
  115.  
  116.     public function next(): void
  117.     {
  118.         $this->fileObject->next();
  119.     }
  120. }
  121.  
  122. // Example:
  123. // First create a temporary CSV file for demo purposes
  124. $data = [
  125.     ['Firstname', 'Lastname', 'Email'],
  126.     ['John', 'Doe', '[email protected]'],
  127.     ['Jane', 'Doe', '[email protected]'],
  128. ];
  129. $tempFile = new SplTempFileObject();
  130. foreach ($data as $fields) {
  131.     $tempFile->fputcsv($fields);
  132. }
  133.  
  134. // Then read the file and dump the records
  135. $csvReader = (new CsvReader($tempFile))
  136.     ->setDelimiter(',')
  137.     ->setSkipEmptyLines(true)
  138.     ->setTrimKeys(true)
  139.     ->setTrimValues(true);
  140.    
  141. foreach ($csvReader as $row) {
  142.     var_dump($row);
  143. }
  144.  
  145. // Output
  146. //
  147. // array(3) {
  148. //   ["Firstname"] => string(4) "John"
  149. //   ["Lastname"] => string(3) "Doe"
  150. //   ["Email"] => string(20) "[email protected]"
  151. // }
  152. //
  153. // array(3) {
  154. //   ["Firstname"] => string(4) "Jane"
  155. //   ["Lastname"] => string(3) "Doe"
  156. //   ["Email"] => string(20) "[email protected]"
  157. // }
  158.  
Advertisement
Add Comment
Please, Sign In to add comment