182 lines
		
	
	
		
			5.1 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			182 lines
		
	
	
		
			5.1 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
| <?php
 | |
| namespace nulib\db\_private;
 | |
| 
 | |
| use nulib\cl;
 | |
| use nulib\str;
 | |
| use nulib\ValueException;
 | |
| 
 | |
| class _select extends _common {
 | |
|   const SCHEMA = [
 | |
|     "prefix" => "?string",
 | |
|     "schema" => "?array",
 | |
|     "cols" => "?array",
 | |
|     "col_prefix" => "?string",
 | |
|     "from" => "?string",
 | |
|     "where" => "?array",
 | |
|     "order by" => "?array",
 | |
|     "group by" => "?array",
 | |
|     "having" => "?array",
 | |
|     "suffix" => "?string",
 | |
|   ];
 | |
| 
 | |
|   static function isa(string $sql): bool {
 | |
|     return preg_match("/^select\b/i", $sql);
 | |
|   }
 | |
| 
 | |
|   private static function add_prefix(string $col, ?string $prefix): string {
 | |
|     if ($prefix === null) return $col;
 | |
|     if (strpos($col, ".") !== false) return $col;
 | |
|     return "$prefix$col";
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * parser une chaine de la forme
 | |
|    * "select [COLS] [from TABLE] [where CONDS] [order by ORDERS] [group by GROUPS] [having CONDS]"
 | |
|    */
 | |
|   static function parse(array $query, ?array &$bindings=null): string {
 | |
|     # fusionner d'abord toutes les parties séquentielles
 | |
|     $usersql = $tmpsql = self::merge_seq($query);
 | |
| 
 | |
|     ### vérifier la présence des parties nécessaires
 | |
|     $sql = [];
 | |
| 
 | |
|     ## préfixe
 | |
|     if (($prefix = $query["prefix"] ?? null) !== null) $sql[] = $prefix;
 | |
| 
 | |
|     ## select
 | |
|     self::consume('(select(?:\s*distinct)?)\s*', $tmpsql, $ms);
 | |
|     $sql[] = $ms[1];
 | |
| 
 | |
|     ## cols
 | |
|     $usercols = [];
 | |
|     if (self::consume('(.*?)\s*(?=$|\bfrom\b)', $tmpsql, $ms)) {
 | |
|       if ($ms[1]) $usercols[] = $ms[1];
 | |
|     }
 | |
|     $colPrefix = $query["col_prefix"] ?? null;
 | |
|     if ($colPrefix !== null) str::add_suffix($colPrefix, ".");
 | |
|     $tmpcols = cl::withn($query["cols"] ?? null);
 | |
|     $schema = $query["schema"] ?? null;
 | |
|     if ($tmpcols !== null) {
 | |
|       $cols = [];
 | |
|       $index = 0;
 | |
|       foreach ($tmpcols as $key => $col) {
 | |
|         if ($key === $index) {
 | |
|           $index++;
 | |
|           $cols[] = $col;
 | |
|           $usercols[] = self::add_prefix($col, $colPrefix);
 | |
|         } else {
 | |
|           $cols[] = $key;
 | |
|           $usercols[] = self::add_prefix($col, $colPrefix)." as $key";
 | |
|         }
 | |
|       }
 | |
|     } else {
 | |
|       $cols = null;
 | |
|       if ($schema && is_array($schema) && !in_array("*", $usercols)) {
 | |
|         $cols = array_keys($schema);
 | |
|         foreach ($cols as $col) {
 | |
|           $usercols[] = self::add_prefix($col, $colPrefix);
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|     if (!$usercols && !$cols) $usercols = [self::add_prefix("*", $colPrefix)];
 | |
|     $sql[] = implode(", ", $usercols);
 | |
| 
 | |
|     ## from
 | |
|     $from = $query["from"] ?? null;
 | |
|     if (self::consume('from\s+([a-z_][a-z0-9_.]*)\s*(?=;?\s*$|\bwhere\b)', $tmpsql, $ms)) {
 | |
|       if ($from === null) $from = $ms[1];
 | |
|       $sql[] = "from";
 | |
|       $sql[] = $from;
 | |
|     } elseif ($from !== null) {
 | |
|       $sql[] = "from";
 | |
|       $sql[] = $from;
 | |
|     } else {
 | |
|       throw new ValueException("expected table name: $usersql");
 | |
|     }
 | |
| 
 | |
|     ## where
 | |
|     $userwhere = [];
 | |
|     if (self::consume('where\b\s*(.*?)(?=;?\s*$|\border\s+by\b)', $tmpsql, $ms)) {
 | |
|       if ($ms[1]) $userwhere[] = $ms[1];
 | |
|     }
 | |
|     $where = cl::withn($query["where"] ?? null);
 | |
|     if ($where !== null) self::parse_conds($where, $userwhere, $bindings);
 | |
|     if ($userwhere) {
 | |
|       $sql[] = "where";
 | |
|       $sql[] = implode(" and ", $userwhere);
 | |
|     }
 | |
| 
 | |
|     ## order by
 | |
|     $userorderby = [];
 | |
|     if (self::consume('order\s+by\b\s*(.*?)(?=;?\s*$|\bgroup\s+by\b)', $tmpsql, $ms)) {
 | |
|       if ($ms[1]) $userorderby[] = $ms[1];
 | |
|     }
 | |
|     $orderby = cl::withn($query["order by"] ?? null);
 | |
|     if ($orderby !== null) {
 | |
|       $index = 0;
 | |
|       foreach ($orderby as $key => $value) {
 | |
|         if ($key === $index) {
 | |
|           $userorderby[] = $value;
 | |
|           $index++;
 | |
|         } else {
 | |
|           if ($value === null) $value = false;
 | |
|           if (!is_bool($value)) {
 | |
|             $userorderby[] = "$key $value";
 | |
|           } elseif ($value) {
 | |
|             $userorderby[] = $key;
 | |
|           }
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|     if ($userorderby) {
 | |
|       $sql[] = "order by";
 | |
|       $sql[] = implode(", ", $userorderby);
 | |
|     }
 | |
|     ## group by
 | |
|     $usergroupby = [];
 | |
|     if (self::consume('group\s+by\b\s*(.*?)(?=;?\s*$|\bhaving\b)', $tmpsql, $ms)) {
 | |
|       if ($ms[1]) $usergroupby[] = $ms[1];
 | |
|     }
 | |
|     $groupby = cl::withn($query["group by"] ?? null);
 | |
|     if ($groupby !== null) {
 | |
|       $index = 0;
 | |
|       foreach ($groupby as $key => $value) {
 | |
|         if ($key === $index) {
 | |
|           $usergroupby[] = $value;
 | |
|           $index++;
 | |
|         } else {
 | |
|           if ($value === null) $value = false;
 | |
|           if (!is_bool($value)) {
 | |
|             $usergroupby[] = "$key $value";
 | |
|           } elseif ($value) {
 | |
|             $usergroupby[] = $key;
 | |
|           }
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|     if ($usergroupby) {
 | |
|       $sql[] = "group by";
 | |
|       $sql[] = implode(", ", $usergroupby);
 | |
|     }
 | |
| 
 | |
|     ## having
 | |
|     $userhaving = [];
 | |
|     if (self::consume('having\b\s*(.*?)(?=;?\s*$)', $tmpsql, $ms)) {
 | |
|       if ($ms[1]) $userhaving[] = $ms[1];
 | |
|     }
 | |
|     $having = cl::withn($query["having"] ?? null);
 | |
|     if ($having !== null) self::parse_conds($having, $userhaving, $bindings);
 | |
|     if ($userhaving) {
 | |
|       $sql[] = "having";
 | |
|       $sql[] = implode(" and ", $userhaving);
 | |
|     }
 | |
| 
 | |
|     ## suffixe
 | |
|     if (($suffix = $query["suffix"] ?? null) !== null) $sql[] = $suffix;
 | |
| 
 | |
|     ## fin de la requête
 | |
|     self::check_eof($tmpsql, $usersql);
 | |
|     return implode(" ", $sql);
 | |
|   }
 | |
| }
 |