fd = $fd; $this->close = $close; $this->throwOnError = $throwOnError; if ($allowLocking === null) $allowLocking = static::ALLOW_LOCKING; $this->allowLocking = $allowLocking; } ############################################################################# # File /** * @return resource|null retourner la resource associée à ce fichier si cela * a du sens */ function getResource() { IOException::ensure_open($this->fd === null); $this->_streamAppendFilters($this->fd); return $this->fd; } protected function lock(int $operation, ?int &$wouldBlock=null): bool { $locked = flock($this->getResource(), $operation, $wouldBlock); if ($locked) return true; if ($operation & LOCK_NB) return false; else throw IOException::error(); } protected function unlock(bool $close=false): void { if ($this->fd !== null) { flock($this->fd, LOCK_UN); if ($close) $this->close(); } } function isatty(): bool { return stream_isatty($this->getResource()); } /** obtenir des informations sur le fichier */ function fstat(bool $reload=false): array { if ($this->stat === null || $reload) { $fd = $this->getResource(); $this->stat = IOException::ensure_value(fstat($fd), $this->throwOnError); } return $this->stat; } /** @throws IOException */ function ftell(): int { $fd = $this->getResource(); return IOException::ensure_value(ftell($fd), $this->throwOnError); } /** * @return int la position après avoir déplacé le pointeur * @throws IOException */ function fseek(int $offset, int $whence=SEEK_SET): int { $fd = $this->getResource(); IOException::ensure_value(fseek($fd, $offset, $whence), $this->throwOnError, -1); return $this->ftell(); } /** fermer le fichier si c'est nécessaire */ function close(bool $close=true, ?int $ifSerial=null): void { AbstractIterator::rewind(); if ($this->fd !== null && $close && $this->close && ($ifSerial === null || $this->serial === $ifSerial)) { fclose($this->fd); $this->fd = null; } } ############################################################################# # Reader /** @throws IOException */ function fread(int $length): string { $fd = $this->getResource(); return IOException::ensure_value(fread($fd, $length), $this->throwOnError); } /** @throws IOException */ function fgets(?int $length=null): string { $fd = $this->getResource(); return EOFException::ensure_not_eof(fgets($fd, $length), $this->throwOnError); } /** @throws IOException */ function fpassthru(): int { $fd = $this->getResource(); return IOException::ensure_value(fpassthru($fd), $this->throwOnError); } function readLine(): ?string { return str::strip_nl($this->fgets()); } /** * essayer de verrouiller le fichier en lecture. retourner true si l'opération * réussit. dans ce cas, il faut appeler {@link getReader()} avec l'argument * true */ function canRead(): bool { if ($this->allowLocking) return $this->lock(LOCK_SH + LOCK_NB); else return true; } /** * verrouiller en mode partagé puis retourner un objet permettant de lire le * fichier. */ function getReader(bool $lockedByCanRead=false): IReader { if ($this->allowLocking && !$lockedByCanRead) $this->lock(LOCK_SH); return new class($this->fd, ++$this->serial, $this) extends Stream { function __construct($fd, int $serial, Stream $parent) { $this->parent = $parent; parent::__construct($fd); } /** @var Stream */ private $parent; function close(bool $close=true): void { if ($this->parent !== null && $close) { $this->parent->close(true, $this->serial); $this->fd = null; $this->parent = null; } } }; } /** retourner le contenu du fichier sous forme de chaine */ function getContents(bool $close=true, bool $lockedByCanRead=false): string { $allowLocking = $this->allowLocking; if ($allowLocking && !$lockedByCanRead) $this->lock(LOCK_SH); try { return IOException::ensure_value(stream_get_contents($this->fd), $this->throwOnError); } finally { if ($allowLocking) $this->unlock($close); elseif ($close) $this->close(); } } function unserialize(?array $options=null, bool $close=true, bool $lockedByCanRead=false) { $args = [$this->getContents($lockedByCanRead)]; if ($options !== null) $args[] = $options; return unserialize(...$args); } ############################################################################# # Iterator protected function _setup(): void { } protected function _next(&$key) { return $this->fgets(); } protected function _teardown(): void { $this->fseek(0); } ############################################################################# # Writer /** @throws IOException */ function fwrite(string $data, ?int $length=null): int { $fd = $this->getResource(); if ($length === null) $r = fwrite($fd, $data); else $r = fwrite($fd, $data, $length); return IOException::ensure_value($r, $this->throwOnError); } /** @throws IOException */ function fflush(): void { $fd = $this->getResource(); IOException::ensure_value(fflush($fd), $this->throwOnError); } /** @throws IOException */ function ftruncate(int $size): void { $fd = $this->getResource(); IOException::ensure_value(ftruncate($fd, $size), $this->throwOnError); } function writeLines(?iterable $lines): IWriter { if ($lines !== null) { foreach ($lines as $line) { $this->fwrite($line); $this->fwrite("\n"); } } return $this; } /** * essayer de verrouiller le fichier en écriture. retourner true si l'opération * réussit. dans ce cas, il faut appeler {@link getWriter()} avec l'argument * true */ function canWrite(): bool { if ($this->allowLocking) return $this->lock(LOCK_EX + LOCK_NB); else return true; } /** * verrouiller en mode exclusif puis retourner un objet permettant d'écrire * dans le fichier */ function getWriter(bool $lockedByCanWrite=false): IWriter { if ($this->allowLocking && !$lockedByCanWrite) $this->lock(LOCK_EX); return new class($this->fd, ++$this->serial, $this) extends Stream { function __construct($fd, int $serial, Stream $parent) { $this->parent = $parent; $this->serial = $serial; parent::__construct($fd); } /** @var Stream */ private $parent; function close(bool $close=true): void { if ($this->parent !== null && $close) { $this->parent->close(true, $this->serial); $this->fd = null; $this->parent = null; } } }; } function putContents(string $contents, bool $close=true, bool $lockedByCanWrite=false): void { $allowLocking = $this->allowLocking; if ($allowLocking && !$lockedByCanWrite) $this->lock(LOCK_EX); try { $this->fwrite($contents); } finally { if ($allowLocking) $this->unlock($close); elseif ($close) $this->close(); } } function serialize($object, bool $close=true, bool $lockedByCanWrite=false): void { $this->putContents(serialize($object), $lockedByCanWrite); } }