ajouter RunFile et LockFile
This commit is contained in:
		
							parent
							
								
									502331664e
								
							
						
					
					
						commit
						096bfe91f8
					
				@ -37,7 +37,7 @@ interface IReader extends _IFile {
 | 
			
		||||
   * verrouiller en mode partagé puis retourner un objet permettant de lire le
 | 
			
		||||
   * fichier.
 | 
			
		||||
   */
 | 
			
		||||
  function getReader(bool $lockedByCanRead=false): IReader;
 | 
			
		||||
  function getReader(bool $alreadyLocked=false): IReader;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * lire tout le contenu du fichier en une seule fois, puis, si $close==true,
 | 
			
		||||
@ -45,8 +45,8 @@ interface IReader extends _IFile {
 | 
			
		||||
   *
 | 
			
		||||
   * @throws IOException si une erreur se produit
 | 
			
		||||
   */
 | 
			
		||||
  function getContents(bool $close=true, bool $lockedByCanRead=false): string;
 | 
			
		||||
  function getContents(bool $close=true, bool $alreadyLocked=false): string;
 | 
			
		||||
 | 
			
		||||
  /** désérialiser le contenu du fichier, puis, si $close===true, le fermer */
 | 
			
		||||
  function unserialize(?array $options=null, bool $close=true, bool $lockedByCanRead=false);
 | 
			
		||||
  function unserialize(?array $options=null, bool $close=true, bool $alreadyLocked=false);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -30,11 +30,11 @@ interface IWriter extends _IFile {
 | 
			
		||||
   * verrouiller en mode exclusif puis retourner un objet permettant d'écrire
 | 
			
		||||
   * dans le fichier
 | 
			
		||||
   */
 | 
			
		||||
  function getWriter(bool $lockedByCanWrite=false): IWriter;
 | 
			
		||||
  function getWriter(bool $alreadyLocked=false): IWriter;
 | 
			
		||||
 | 
			
		||||
  /** écrire le contenu spécifié dans le fichier */
 | 
			
		||||
  function putContents(string $contents, bool $close=true, bool $lockedByCanWrite=false): void;
 | 
			
		||||
  function putContents(string $contents, bool $close=true, bool $alreadyLocked=false): void;
 | 
			
		||||
 | 
			
		||||
  /** sérialiser l'objet dans la destination */
 | 
			
		||||
  function serialize($object, bool $close=true, bool $lockedByCanWrite=false): void;
 | 
			
		||||
  function serialize($object, bool $close=true, bool $alreadyLocked=false): void;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										87
									
								
								src/file/app/LockFile.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										87
									
								
								src/file/app/LockFile.php
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,87 @@
 | 
			
		||||
<?php
 | 
			
		||||
namespace nur\sery\file\app;
 | 
			
		||||
 | 
			
		||||
use nur\sery\cl;
 | 
			
		||||
use nur\sery\file\base\SharedFile;
 | 
			
		||||
use nur\sery\output\msg;
 | 
			
		||||
use nur\sery\php\time\DateTime;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Class LockFile: une classe qui permet à une application de verrouiller
 | 
			
		||||
 * certaines actions
 | 
			
		||||
 */
 | 
			
		||||
class LockFile {
 | 
			
		||||
  const NAME = null;
 | 
			
		||||
 | 
			
		||||
  const TITLE = null;
 | 
			
		||||
 | 
			
		||||
  function __construct($file, ?string $name=null, ?string $title=null) {
 | 
			
		||||
    $this->file = new SharedFile($file);
 | 
			
		||||
    $this->name = $name ?? static::NAME;
 | 
			
		||||
    $this->title = $title ?? static::TITLE;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /** @var SharedFile */
 | 
			
		||||
  protected $file;
 | 
			
		||||
 | 
			
		||||
  /** @var ?string */
 | 
			
		||||
  protected $name;
 | 
			
		||||
 | 
			
		||||
  /** @var ?string */
 | 
			
		||||
  protected $title;
 | 
			
		||||
 | 
			
		||||
  protected function initData(): array {
 | 
			
		||||
    return [
 | 
			
		||||
      "name" => $this->name,
 | 
			
		||||
      "title" => $this->title,
 | 
			
		||||
      "locked" => false,
 | 
			
		||||
      "date_lock" => null,
 | 
			
		||||
      "date_release" => null,
 | 
			
		||||
    ];
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  function read(bool $close=true): array {
 | 
			
		||||
    $data = $this->file->unserialize(null, $close);
 | 
			
		||||
    if (!is_array($data)) $data = $this->initData();
 | 
			
		||||
    return $data;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  function isLocked(?array &$data=null): bool {
 | 
			
		||||
    $data = $this->read();
 | 
			
		||||
    return $data["locked"];
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  function warnIfLocked(?array $data=null): void {
 | 
			
		||||
    if ($data === null) $data = $this->read();
 | 
			
		||||
    if ($data["locked"]) {
 | 
			
		||||
      msg::warning("$data[name]: possède le verrou depuis $data[date_lock] -- $data[title]");
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  function lock(?array &$data=null): bool {
 | 
			
		||||
    $file = $this->file;
 | 
			
		||||
    $data = $this->read(false);
 | 
			
		||||
    if ($data["locked"]) {
 | 
			
		||||
      $file->close();
 | 
			
		||||
      return false;
 | 
			
		||||
    } else {
 | 
			
		||||
      $file->ftruncate();
 | 
			
		||||
      $file->serialize(cl::merge($data, [
 | 
			
		||||
        "locked" => true,
 | 
			
		||||
        "date_lock" => new DateTime(),
 | 
			
		||||
        "date_release" => null,
 | 
			
		||||
      ]));
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  function release(?array &$data=null): void {
 | 
			
		||||
    $file = $this->file;
 | 
			
		||||
    $data = $this->read(false);
 | 
			
		||||
    $file->ftruncate();
 | 
			
		||||
    $file->serialize(cl::merge($data, [
 | 
			
		||||
      "locked" => false,
 | 
			
		||||
      "date_release" => new DateTime(),
 | 
			
		||||
    ]));
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										129
									
								
								src/file/app/RunFile.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										129
									
								
								src/file/app/RunFile.php
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,129 @@
 | 
			
		||||
<?php
 | 
			
		||||
namespace nur\sery\file\app;
 | 
			
		||||
 | 
			
		||||
use nur\sery\cl;
 | 
			
		||||
use nur\sery\file\base\SharedFile;
 | 
			
		||||
use nur\sery\os\path;
 | 
			
		||||
use nur\sery\php\time\DateTime;
 | 
			
		||||
use nur\sery\str;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Class RunFile: une classe permettant de suivre le fonctionnement d'une
 | 
			
		||||
 * application qui tourne en tâche de fond
 | 
			
		||||
 */
 | 
			
		||||
class RunFile {
 | 
			
		||||
  const RUN_EXT = ".run";
 | 
			
		||||
  const LOCK_EXT = ".lock";
 | 
			
		||||
 | 
			
		||||
  const NAME = null;
 | 
			
		||||
 | 
			
		||||
  function __construct(string $file, ?string $name=null) {
 | 
			
		||||
    $file = path::ensure_ext($file, self::RUN_EXT);
 | 
			
		||||
    $this->file = new SharedFile($file);
 | 
			
		||||
    $this->name = $name ?? static::NAME;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /** @var SharedFile */
 | 
			
		||||
  protected $file;
 | 
			
		||||
 | 
			
		||||
  /** @var ?string */
 | 
			
		||||
  protected $name;
 | 
			
		||||
 | 
			
		||||
  protected static function merge(array $data, array $merge): array {
 | 
			
		||||
    return cl::merge($data, [
 | 
			
		||||
      "serial" => $data["serial"] + 1,
 | 
			
		||||
    ], $merge);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  protected function initData(bool $withDateStart=true): array {
 | 
			
		||||
    $dateStart = $withDateStart? new DateTime(): null;
 | 
			
		||||
    return [
 | 
			
		||||
      "name" => $this->name,
 | 
			
		||||
      "serial" => 0,
 | 
			
		||||
      "date_start" => $dateStart,
 | 
			
		||||
      "date_stop" => null,
 | 
			
		||||
      "action" => null,
 | 
			
		||||
      "action_date_start" => null,
 | 
			
		||||
      "action_max_step" => null,
 | 
			
		||||
      "action_current_step" => null,
 | 
			
		||||
      "action_date_step" => null,
 | 
			
		||||
    ];
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  function read(): array {
 | 
			
		||||
    $data = $this->file->unserialize();
 | 
			
		||||
    if (!is_array($data)) $data = $this->initData(false);
 | 
			
		||||
    return $data;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /** tester si l'application est démarrée */
 | 
			
		||||
  function isStarted(): bool {
 | 
			
		||||
    $data = $this->read();
 | 
			
		||||
    return $data["date_start"] !== null;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /** tester si l'application est arrêtée */
 | 
			
		||||
  function isStopped(): bool {
 | 
			
		||||
    $data = $this->read();
 | 
			
		||||
    return $data["date_stop"] !== null;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  function haveWorked(int $serial, ?int &$currentSerial=null): bool {
 | 
			
		||||
    $data = $this->read();
 | 
			
		||||
    return $serial !== $data["serial"];
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  protected function willWrite(): array {
 | 
			
		||||
    $file = $this->file;
 | 
			
		||||
    $file->lockWrite();
 | 
			
		||||
    $data = $file->unserialize(null, false, true);
 | 
			
		||||
    if (!is_array($data)) {
 | 
			
		||||
      $data = $this->initData();
 | 
			
		||||
      $file->ftruncate();
 | 
			
		||||
      $file->serialize($data, false, true);
 | 
			
		||||
    }
 | 
			
		||||
    $file->ftruncate();
 | 
			
		||||
    return [$file, $data];
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /** indiquer que l'application démarre */
 | 
			
		||||
  function start(): void {
 | 
			
		||||
    $this->file->serialize($this->initData());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /** indiquer le début d'une action */
 | 
			
		||||
  function action(?string $title, ?int $maxSteps=null): void {
 | 
			
		||||
    [$file, $data] = $this->willWrite();
 | 
			
		||||
    $file->serialize(self::merge($data, [
 | 
			
		||||
      "action" => $title,
 | 
			
		||||
      "action_date_start" => new DateTime(),
 | 
			
		||||
      "action_max_step" => $maxSteps,
 | 
			
		||||
      "action_current_step" => 0,
 | 
			
		||||
    ]));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /** indiquer qu'une étape est franchie dans l'action en cours */
 | 
			
		||||
  function step(int $nbSteps=1): void {
 | 
			
		||||
    [$file, $data] = $this->willWrite();
 | 
			
		||||
    $file->serialize(self::merge($data, [
 | 
			
		||||
      "action_date_step" => new DateTime(),
 | 
			
		||||
      "action_current_step" => $data["action_current_step"] + $nbSteps,
 | 
			
		||||
    ]));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /** indiquer que l'application s'arrête */
 | 
			
		||||
  function stop(): void {
 | 
			
		||||
    [$file, $data] = $this->willWrite();
 | 
			
		||||
    $file->serialize(self::merge($data, [
 | 
			
		||||
      "date_stop" => new DateTime(),
 | 
			
		||||
    ]));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  function getLockFile(?string $name=null, ?string $title=null): LockFile {
 | 
			
		||||
    $ext = self::LOCK_EXT;
 | 
			
		||||
    if ($name !== null) $ext = ".$name$ext";
 | 
			
		||||
    $file = path::ensure_ext($this->file->getFile(), $ext, self::RUN_EXT);
 | 
			
		||||
    $name = str::join("/", [$this->name, $name]);
 | 
			
		||||
    return new LockFile($file, $name, $title);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@ -234,6 +234,14 @@ class Stream extends AbstractIterator implements IReader, IWriter {
 | 
			
		||||
    return iterator_to_array($this);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * verrouiller le fichier en lecture de façon inconditionelle (ignorer la
 | 
			
		||||
   * valeur de $useLocking). bloquer jusqu'à ce que le verrou soit disponible
 | 
			
		||||
   */
 | 
			
		||||
  function lockRead(): void {
 | 
			
		||||
    $this->lock(LOCK_SH);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * 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
 | 
			
		||||
@ -248,8 +256,8 @@ class Stream extends AbstractIterator implements IReader, IWriter {
 | 
			
		||||
   * verrouiller en mode partagé puis retourner un objet permettant de lire le
 | 
			
		||||
   * fichier.
 | 
			
		||||
   */
 | 
			
		||||
  function getReader(bool $lockedByCanRead=false): IReader {
 | 
			
		||||
    if ($this->useLocking && !$lockedByCanRead) $this->lock(LOCK_SH);
 | 
			
		||||
  function getReader(bool $alreadyLocked=false): IReader {
 | 
			
		||||
    if ($this->useLocking && !$alreadyLocked) $this->lock(LOCK_SH);
 | 
			
		||||
    return new class($this->fd, ++$this->serial, $this) extends Stream {
 | 
			
		||||
      function __construct($fd, int $serial, Stream $parent) {
 | 
			
		||||
        $this->parent = $parent;
 | 
			
		||||
@ -270,9 +278,9 @@ class Stream extends AbstractIterator implements IReader, IWriter {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /** retourner le contenu du fichier sous forme de chaine */
 | 
			
		||||
  function getContents(bool $close=true, bool $lockedByCanRead=false): string {
 | 
			
		||||
  function getContents(bool $close=true, bool $alreadyLocked=false): string {
 | 
			
		||||
    $useLocking = $this->useLocking;
 | 
			
		||||
    if ($useLocking && !$lockedByCanRead) $this->lock(LOCK_SH);
 | 
			
		||||
    if ($useLocking && !$alreadyLocked) $this->lock(LOCK_SH);
 | 
			
		||||
    try {
 | 
			
		||||
      return IOException::ensure_valid(stream_get_contents($this->fd), $this->throwOnError);
 | 
			
		||||
    } finally {
 | 
			
		||||
@ -281,8 +289,8 @@ class Stream extends AbstractIterator implements IReader, IWriter {
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  function unserialize(?array $options=null, bool $close=true, bool $lockedByCanRead=false) {
 | 
			
		||||
    $args = [$this->getContents($lockedByCanRead)];
 | 
			
		||||
  function unserialize(?array $options=null, bool $close=true, bool $alreadyLocked=false) {
 | 
			
		||||
    $args = [$this->getContents($alreadyLocked)];
 | 
			
		||||
    if ($options !== null) $args[] = $options;
 | 
			
		||||
    return unserialize(...$args);
 | 
			
		||||
  }
 | 
			
		||||
@ -331,9 +339,10 @@ class Stream extends AbstractIterator implements IReader, IWriter {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /** @throws IOException */
 | 
			
		||||
  function ftruncate(int $size): self {
 | 
			
		||||
  function ftruncate(int $size=0, bool $rewind=true): self {
 | 
			
		||||
    $fd = $this->getResource();
 | 
			
		||||
    IOException::ensure_valid(ftruncate($fd, $size), $this->throwOnError);
 | 
			
		||||
    if ($rewind) rewind($fd);
 | 
			
		||||
    return $this;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -347,6 +356,14 @@ class Stream extends AbstractIterator implements IReader, IWriter {
 | 
			
		||||
    return $this;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * verrouiller le fichier en écriture de façon inconditionelle (ignorer la
 | 
			
		||||
   * valeur de $useLocking). bloquer jusqu'à ce que le verrou soit disponible
 | 
			
		||||
   */
 | 
			
		||||
  function lockWrite(): void {
 | 
			
		||||
    $this->lock(LOCK_EX);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * 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
 | 
			
		||||
@ -361,8 +378,8 @@ class Stream extends AbstractIterator implements IReader, IWriter {
 | 
			
		||||
   * verrouiller en mode exclusif puis retourner un objet permettant d'écrire
 | 
			
		||||
   * dans le fichier
 | 
			
		||||
   */
 | 
			
		||||
  function getWriter(bool $lockedByCanWrite=false): IWriter {
 | 
			
		||||
    if ($this->useLocking && !$lockedByCanWrite) $this->lock(LOCK_EX);
 | 
			
		||||
  function getWriter(bool $alreadyLocked=false): IWriter {
 | 
			
		||||
    if ($this->useLocking && !$alreadyLocked) $this->lock(LOCK_EX);
 | 
			
		||||
    return new class($this->fd, ++$this->serial, $this) extends Stream {
 | 
			
		||||
      function __construct($fd, int $serial, Stream $parent) {
 | 
			
		||||
        $this->parent = $parent;
 | 
			
		||||
@ -383,9 +400,9 @@ class Stream extends AbstractIterator implements IReader, IWriter {
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  function putContents(string $contents, bool $close=true, bool $lockedByCanWrite=false): void {
 | 
			
		||||
  function putContents(string $contents, bool $close=true, bool $alreadyLocked=false): void {
 | 
			
		||||
    $useLocking = $this->useLocking;
 | 
			
		||||
    if ($useLocking && !$lockedByCanWrite) $this->lock(LOCK_EX);
 | 
			
		||||
    if ($useLocking && !$alreadyLocked) $this->lock(LOCK_EX);
 | 
			
		||||
    try {
 | 
			
		||||
      $this->fwrite($contents);
 | 
			
		||||
    } finally {
 | 
			
		||||
@ -394,7 +411,7 @@ class Stream extends AbstractIterator implements IReader, IWriter {
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  function serialize($object, bool $close=true, bool $lockedByCanWrite=false): void {
 | 
			
		||||
    $this->putContents(serialize($object), $lockedByCanWrite);
 | 
			
		||||
  function serialize($object, bool $close=true, bool $alreadyLocked=false): void {
 | 
			
		||||
    $this->putContents(serialize($object), $alreadyLocked);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -12,9 +12,17 @@ abstract class _File extends Stream {
 | 
			
		||||
  /** @var string */
 | 
			
		||||
  protected $file;
 | 
			
		||||
 | 
			
		||||
  function getFile(): ?string {
 | 
			
		||||
    return $this->file;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /** @var string */
 | 
			
		||||
  protected $mode;
 | 
			
		||||
 | 
			
		||||
  function getMode(): string {
 | 
			
		||||
    return $this->mode;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /** @return resource */
 | 
			
		||||
  protected function open() {
 | 
			
		||||
    return IOException::ensure_valid(@fopen($this->file, $this->mode));
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user