1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109
| /**
* AbstractCSV est une classe abstraite qui ne peut donc pas être instanciée.
* Un ensemble de données csv n'est pas forcément issu d'un fichier mais peut provenir d'une chaîne
* ou d'un flux. On se limite donc aux caractèristiques d'un tel ensemble:
* - les caractères de séparation, protection, échappement.
* - la possibilité d'avoir des colonnes nommées à partir d'une ligne d'entète.
* - un nombre de champs fixe.
*
* À noter que la méthode checkFieldsNumber est abstraite ce qui impose aux classes qui héritent de
* de AbstractCSV de l'implémenter, mais laisse à leur discrètion les détails de cette implémentation.
* Celle-ci sera différente pour la lecture ou pour l'écriture de données csv, mais n'en sera pas moins
* nécessaire pour garantir l'intégrité des données dans les deux cas.
*/
abstract class CSV
{
protected string $separator = ',';
protected string $protection = '"';
protected string $escape = '\\';
protected ?int $fieldsNumber = null;
protected ?array $headers = null;
abstract function checkFieldsNumber(int $fieldsNumber):bool;
protected function hasHeaders():bool {
return (bool) $this->headers;
}
}
/**
* Une interface contient une ou plusieurs des méthodes relatives à un comportement précis.
* Les classes qui l'implémentent, "signent un contract" avec elle et ont l'obligation
* d'implémenter ses méthodes (à l'instar d'une méthode abstraite dont elles auraient héritée).
* Ceci fait, ces classes disposent du comportement en question.
*
* Dans le cas présent: une classe qui implémente iReader, dispose d'une méthode
* getReader() et peut se comporter comme un Reader. C'est aussi bête que ça.
*
* En sus de blinder le code, l'interface permet une souplesse dans le code, car au lieu de
* vérifier qu'un objet est une instance d'une des classes ayant le comportement recherché, il
* suffit de vérifier que l'objet provient d'une classe, quelle qu'elle soit, qui implémente
* l'interface, et ce, sans pour autant connaitre la classe, via un type hint.
* On peut résumer la chose ainsi:
* - Je me fout de savoir si c'est un canard tant que ça fait coin-coin.
*
*/
interface iReader
{
public function getReader();
}
interface iFile
{
protected function setLocation(string $location):void;
protected function exists():bool;
protected function isReadable():bool;
protected function isWritable():bool;
}
trait FileTrait
{
protected ?string $location;
protected function setLocation(string $location):void {
$this->location = $location;
}
protected function exists():bool {
return is_file($this->location);
}
protected function isReadable():bool {
return is_readable($this->location);
}
protected function isWritable():bool {
return is_writable($this->location);
}
}
class CSVFileReader extends CSV implements iReader, iFile
{
use FileTrait;
public function __construct($path)
{
$this->path = $path;
$this->open();
}
public function getReader() {
while ( false !== $fields = fgetcsv($this->handle) ) {
yield $fields;
}
$this->close($this->handle);
}
protected function checkFieldsNumber(int $fieldsNumber):bool {
return $this->fieldsNumber === $fieldsNumber;
}
} |
Partager