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); } }