nur-sery/nur_src/mapper/base/Consumer.php

223 lines
6.8 KiB
PHP
Raw Normal View History

2024-04-04 16:26:22 +04:00
<?php
namespace nur\mapper\base;
use ArrayAccess;
use Countable;
use nur\b\coll\IArray;
use nur\b\coll\TBaseArray;
use nur\b\coll\TGenericArray;
use nur\b\ICloseable;
use nur\b\params\Parametrable;
use nur\b\params\Tparametrable;
use nur\b\ValueException;
use nur\func;
use nur\mapper\base\oobd\IOobdManager;
use nur\mapper\base\oobd\TOobdManager;
/**
* Class Consumer: une classe qui applique des mappers sur les données d'un
* producer.
*/
class Consumer extends Parametrable implements ArrayAccess, Countable, IArray, ICloseable, IOobdManager {
use TBaseArray, TGenericArray, Tparametrable, TOobdManager;
function __construct($producer=null, ...$mappers) {
parent::__construct();
$this->data = [];
if ($producer !== null) $this->setProducer($producer);
$this->addAll($mappers);
}
function _haveMethod(string $method): bool {
return method_exists($this, $method);
}
/** @var iterable */
private $producer;
/** @var PushProducer */
private $pushProducer;
function setProducer($producer, ...$args): self {
$producer = producer_utils::ensure_producer($producer, $args);
$this->producer = $producer;
$this->pushProducer = $producer instanceof PushProducer? $producer: null;
return $this;
}
function set($key, $mapper): self { return $this->_set($key, mapper_utils::ensure_mapper_class($mapper)); }
function add($mapper): self { return $this->_set(null, mapper_utils::ensure_mapper_class($mapper)); }
/** @var bool */
private $setup = false;
private function ensureSetup(): bool {
if (!$this->setup) {
$this->setup();
$this->setup = true;
return true;
}
return false;
}
protected function setup(): void {
}
/** @var iterable */
private $pushIterator;
private function _ensureSharedOobdManager(iterable $iterator): void {
if ($iterator instanceof IOobdManager) {
# récupérer le gestionnaire partagé le cas échéant
$sharedOobdManager = $iterator->getSharedOobdManager();
if ($sharedOobdManager !== null) $this->setSharedOobdManager($sharedOobdManager);
}
}
private function buildIterator(bool $ensurePushable=false): array {
$this->ensureSetup();
if ($this->pushIterator === null) {
if (!$ensurePushable) {
$iterator = $this->producer;
if ($iterator === null) {
throw new ValueException("a producer is required");
}
if ($this->pushProducer === null) {
if ($iterator instanceof IOobdManager) {
# s'assurer qu'il y a toujours un gestionnaire partagé
$iterator->ensureSharedOobdManager();
}
$iterator = mapper_utils::assemble_mappers($this->data, $iterator);
$this->_ensureSharedOobdManager($iterator);
return [true, $iterator];
}
}
$iterator = $this->pushProducer;
if ($iterator === null) $iterator = new PushProducer();
# s'assurer qu'il y a toujours un gestionnaire partagé
$iterator->ensureSharedOobdManager();
$this->pushProducer = $iterator;
$this->pushIterator = mapper_utils::assemble_mappers($this->data, $iterator);
$this->_ensureSharedOobdManager($this->pushIterator);
}
return [false, $this->pushIterator];
}
private $iterator;
function hasOvalue(string $name): bool {
if ($this->_hasOobdValue($name)) {
return true;
} elseif ($this->sharedOobdManager !== null
&& $this->sharedOobdManager->hasOvalue($name)) {
return true;
} elseif ($this->iterator instanceof IOobdManager) {
return $this->iterator->hasOvalue($name);
} elseif ($this->pushIterator instanceof IOobdManager) {
return $this->pushIterator->hasOvalue($name);
}
return false;
}
/**
* c'est la méthode {@link consume()} qui construit $this->iterator. l'accès
* aux données OOB de la chaine de mapper n'est donc valide que dans les
* méthodes {@link _consume()} et {@link cook()}
*
* De même, en mode push, ce n'est qu'après l'appel d'une des méthodes
* {@link ensurePushable()}, {@link push()} ou {@link pushAll()} que les
* données OOB de la chaine de mapper sont disponibles
*/
function getOvalue(string $name, $default=null) {
if ($this->_hasOobdValue($name)) {
return $this->_getOobdValue($name, $default);
} elseif ($this->sharedOobdManager !== null
&& $this->sharedOobdManager->hasOvalue($name)) {
return $this->sharedOobdManager->getOvalue($name, $default);
} elseif ($this->iterator instanceof IOobdManager) {
return $this->iterator->getOvalue($name, $default);
} elseif ($this->pushIterator instanceof IOobdManager) {
return $this->pushIterator->getOvalue($name, $default);
}
return null;
}
function cook($item) {
}
protected function _consume(iterable $items): void {
$cookFunc = func::_prepare([$this, "cook"]);
foreach ($items as $key => $item) {
func::_call($cookFunc, [$item, $key]);
}
}
/**
* consommer toutes les valeurs du producer
*
* les méthodes {@link setup()} et {@link teardown()} sont appelées
* automatiquement
*/
function consume($producer=null, ...$mappers): void {
if ($producer !== null) $this->setProducer($producer);
if ($mappers) $this->resetAll($mappers);
$close = $this->ensureSetup();
try {
[$close, $iterator] = $this->buildIterator();
$this->iterator = $iterator;
$this->_consume($iterator);
} finally {
if ($close) $this->close();
}
}
protected function teardown(): void {
if ($this->iterator instanceof ICloseable) $this->iterator->close();
$this->iterator = null;
if ($this->pushIterator !== null) {
if ($this->pushIterator instanceof ICloseable) $this->pushIterator->close();
$this->pushProducer = null;
$this->pushIterator = null;
}
}
/** appeler la méthode {@link teardown()} si nécessaire */
function close(): void {
if ($this->setup) {
$this->teardown();
$this->setup = false;
}
}
/**
* s'assurer que les données OOB sont disponibles avant l'utilisation de
* {@link push()} ou {@link pushAll()}. cette méthode n'a pas d'autre utilité
*/
function ensurePushable(): void {
$this->buildIterator(true);
}
/**
* insérer une valeur, comme si elle provenait du producer.
*
* la méthodes {@link setup()} est appelée automatiquement si nécessaire, mais
* pas {@link teardown()}. il faut donc penser à appeler {@link close()} quand
* on a terminé
*/
function push($item, $key=null): void {
$this->buildIterator(true);
$this->pushProducer->push($item, $key);
$this->_consume($this->pushIterator);
}
/**
* insérer des valeurs, comme si elles provenaient du producer
*/
function pushAll(iterable $items): void {
$this->buildIterator(true);
$this->pushProducer->pushAll($items);
$this->_consume($this->pushIterator);
}
}