223 lines
		
	
	
		
			6.8 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			223 lines
		
	
	
		
			6.8 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
| <?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);
 | |
|   }
 | |
| }
 |