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