224 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			224 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
| <?php
 | |
| namespace nur\m\base;
 | |
| 
 | |
| use Iterator;
 | |
| use nur\A;
 | |
| use nur\m\IQuery;
 | |
| use nur\m\IRowIncarnation;
 | |
| use nur\m\IRowIterator;
 | |
| 
 | |
| abstract class Query implements IQuery {
 | |
|   /**
 | |
|    * retourner une chaine quotée pour SQL ou "null" si
 | |
|    * ($value === "" && !$allow_empty)
 | |
|    */
 | |
|   static function qv(string $value, bool $allowEmpty=false): string {
 | |
|     if ($value) return "'".str_replace("'", "''", $value)."'";
 | |
|     elseif ($allowEmpty) return "''";
 | |
|     else return "null";
 | |
|   }
 | |
| 
 | |
|   # nb de caractères minimum pour une recherche approximative
 | |
|   const LIKE_THRESHOLD = 2;
 | |
| 
 | |
|   static function likev($value, bool $partial=false, ?int $likeThreshold=null) {
 | |
|     if ($value === false || $value === null) return $value;
 | |
| 
 | |
|     if ($likeThreshold === null) $likeThreshold = static::LIKE_THRESHOLD;
 | |
|     if ($partial && strlen($value) >= $likeThreshold) {
 | |
|       // Rajouter un espace qui sera remplacé par %
 | |
|       if (!preg_match('/[- %]$/', $value)) $value .= " ";
 | |
|     }
 | |
|     return preg_replace('/[- ]+/', '%', $value);
 | |
|   }
 | |
| 
 | |
|   static function build_actual_query(string $query, ?array $bindings) {
 | |
|     if ($bindings !== null) {
 | |
|       AbstractConn::fix_sql_with_seq_bindings($query, $bindings);
 | |
|       foreach ($bindings as $name => $value) {
 | |
|         if (A::is_seq($value) && count($value) == 0) {
 | |
|           $value = null;
 | |
|         }
 | |
|         if ($value === null) {
 | |
|           $value = "null";
 | |
|           $query = str_replace(":$name", $value, $query);
 | |
|         } elseif (A::is_seq($value)) {
 | |
|           $values = $value;
 | |
|           $count = count($values);
 | |
|           for ($i = $count - 1; $i >= 0; $i--) {
 | |
|             $value = $values[$i];
 | |
|             if (is_array($value)) {
 | |
|               # tableau associatif avec les informations sur le type de la valeur
 | |
|               #XXX pour le moment, le type est ignoré
 | |
|               $value = $value["value"];
 | |
|             }
 | |
|             if (!is_string($value)) $value = strval($value);
 | |
|             // chaine
 | |
|             if (!preg_match('/^[1-9][0-9]*$/', $value)) {
 | |
|               $value = self::qv($value, true);
 | |
|             }
 | |
|             $query = str_replace(":${name}_$i", $value, $query);
 | |
|           }
 | |
|         } else {
 | |
|           if (is_array($value)) {
 | |
|             # tableau associatif avec les informations sur le type de la valeur
 | |
|             #XXX pour le moment, le type est ignoré
 | |
|             $value = $value["value"];
 | |
|           }
 | |
|           if (!is_string($value)) $value = strval($value);
 | |
|           // chaine
 | |
|           if (!preg_match('/^[1-9][0-9]*$/', $value)) {
 | |
|             $value = self::qv($value, true);
 | |
|           }
 | |
|           $query = str_replace(":$name", $value, $query);
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|     return str_replace("\n", " ", $query);
 | |
|   }
 | |
|   
 | |
|   const TYPE_SELECT = 1, TYPE_UPDATE = 2, TYPE_INSERT = 3;
 | |
| 
 | |
|   /** @var int type de requête */
 | |
|   protected $type;
 | |
| 
 | |
|   /** @var string|null la requête SQL effective */
 | |
|   protected $sql;
 | |
| 
 | |
|   /** @var array|null le filtre permettant de sélectionner les lignes */
 | |
|   protected $filter;
 | |
| 
 | |
|   /** @var mixed une ligne de données, pour mise à jour ou insertion */
 | |
|   protected $row;
 | |
| 
 | |
|   /** @var array|null le tableau retour */
 | |
|   protected $results;
 | |
| 
 | |
|   /** @var IRowIncarnation */
 | |
|   protected $incarnation;
 | |
| 
 | |
|   abstract protected function newRowIncarnation(): IRowIncarnation;
 | |
| 
 | |
|   abstract protected function newRowIterator(?string $sql, ?array &$bindings=null, ?IRowIncarnation $incarnation=null): IRowIterator;
 | |
| 
 | |
|   function setIncarnation(IRowIncarnation $incarnation): IQuery {
 | |
|     $this->incarnation = $incarnation;
 | |
|     return $this;
 | |
|   }
 | |
| 
 | |
|   #############################################################################
 | |
| 
 | |
|   /** @var string requête SQL de sélection par défaut */
 | |
|   const SQL_SELECT = null;
 | |
| 
 | |
|   function select($sql=null, ?array $filter=null): IQuery {
 | |
|     if ($sql === null) $sql = static::SQL_SELECT;
 | |
|     $this->type = self::TYPE_SELECT;
 | |
|     $this->sql = $sql;
 | |
|     $this->filter = $filter;
 | |
|     $this->row = null;
 | |
|     $result = null;
 | |
|     $this->results =& $result;
 | |
|     return $this;
 | |
|   }
 | |
| 
 | |
|   /** @var string requête SQL de mise à jour par défaut */
 | |
|   const SQL_UPDATE = null;
 | |
| 
 | |
|   function update($sql=null, ?array $filter=null, $row=null, ?array &$results=null): IQuery {
 | |
|     if ($sql === null) $sql = static::SQL_UPDATE;
 | |
|     $this->type = self::TYPE_UPDATE;
 | |
|     $this->sql = $sql;
 | |
|     $this->filter = $filter;
 | |
|     $this->row = $row;
 | |
|     $this->results =& $results;
 | |
|     return $this;
 | |
|   }
 | |
| 
 | |
|   /** @var string requête SQL d'insertion par défaut */
 | |
|   const SQL_INSERT = null;
 | |
| 
 | |
|   function insert($sql=null, $row=null, ?array &$results=null): IQuery {
 | |
|     if ($sql === null) $sql = static::SQL_INSERT;
 | |
|     $this->type = self::TYPE_INSERT;
 | |
|     $this->sql = $sql;
 | |
|     $this->filter = null;
 | |
|     $this->row = $row;
 | |
|     $this->results =& $results;
 | |
|     return $this;
 | |
|   }
 | |
| 
 | |
|   /** valider les filtres utilisés et retourner le cas échéant une requête sql mise à jour */
 | |
|   protected function validateFilter(): ?string {
 | |
|     return null;
 | |
|   }
 | |
| 
 | |
|   abstract protected function _execute(bool $commit): IRowIterator;
 | |
| 
 | |
|   function execute(bool $commit=false): IRowIterator {
 | |
|     return $this->_execute($commit);
 | |
|   }
 | |
| 
 | |
|   function execute2(?array $filter=null, $row=null, ?array &$results=null): IRowIterator {
 | |
|     if ($filter !== null) $this->filter = $filter;
 | |
|     if ($row !== null) $this->row = $row;
 | |
|     if ($results !== null) $this->results =& $results;
 | |
|     return $this->_execute(false);
 | |
|   }
 | |
| 
 | |
|   function all(): array { return $this->execute()->all(); }
 | |
|   function allVals(?string $name=null): array {
 | |
|     return rows::vals($this->all(), $name);
 | |
|   }
 | |
|   function first($default=null) { return $this->execute()->first($default); }
 | |
|   function firstVal(?string $name=null, $default=null) {
 | |
|     return rows::val($this->first(), $name, $default);
 | |
|   }
 | |
|   function numRows(): int { return $this->execute()->numRows(); }
 | |
|   function insertId() { return $this->execute()->insertId(); }
 | |
|   function one($default=null, ?bool $rewind=null): array { return $this->execute()->one($default, $rewind); }
 | |
|   function peek($default=null, ?bool $rewind=false): array { return $this->execute()->peek($default, $rewind); }
 | |
| 
 | |
|   /** @var Iterator */
 | |
|   protected $rowIterator;
 | |
| 
 | |
|   protected $firstRow;
 | |
| 
 | |
|   function getRow(?string $sql=null) {
 | |
|     if ($sql !== null) $this->search(null, $sql);
 | |
|     return $this->firstRow;
 | |
|   }
 | |
| 
 | |
|   function search(?array $filter, ?string $sql=null): bool {
 | |
|     if ($sql !== null) $this->sql = $sql;
 | |
|     $this->firstRow = null;
 | |
|     $this->rowIterator = null;
 | |
|     [$first, $second, $iterator] = $this->execute2($filter)->peek();
 | |
|     if ($second !== null) {
 | |
|       $this->rowIterator = $iterator;
 | |
|     } elseif ($first !== null) {
 | |
|       $this->firstRow = $first;
 | |
|     }
 | |
|     return $this->firstRow !== null;
 | |
|   }
 | |
| 
 | |
|   function isClosed(): bool {
 | |
|     $rowIterator = $this->rowIterator;
 | |
|     if ($rowIterator instanceof IRowIterator) return $rowIterator->isClosed();
 | |
|     else return true;
 | |
|   }
 | |
| 
 | |
|   function rewind(): void {
 | |
|     if ($this->isClosed()) $this->rowIterator = $this->execute();
 | |
|     $this->rowIterator->rewind();
 | |
|   }
 | |
|   function valid(): bool {
 | |
|     $valid = $this->rowIterator->valid();
 | |
|     if (!$valid) $this->rowIterator = null;
 | |
|     return $valid;
 | |
|   }
 | |
|   function key() { return $this->rowIterator->key(); }
 | |
|   function current() { return $this->rowIterator->current(); }
 | |
|   function next() { $this->rowIterator->next(); }
 | |
| }
 |