* @author Dennis Eichhorn * @copyright 2013 Dennis Eichhorn * @license OMS License 1.0 * @version 1.0.0 * @link http://orange-management.com */ namespace phpOMS\DataStorage\Database\Query\Grammar; use phpOMS\DataStorage\Database\Query\Builder; use phpOMS\DataStorage\Database\Query\Column; use phpOMS\DataStorage\Database\Query\QueryType; /** * Database query grammar. * * @category Framework * @package phpOMS\DataStorage\Database * @author OMS Development Team * @author Dennis Eichhorn * @license OMS License 1.0 * @link http://orange-management.com * @since 1.0.0 */ class Grammar extends \phpOMS\DataStorage\Database\Grammar { /** * Comment style. * * @var \string * @since 1.0.0 */ protected $comment = '--'; /** * String quotes style. * * @var \string * @since 1.0.0 */ protected $valueQuotes = '\''; /** * System identifier. * * @var \string * @since 1.0.0 */ public $systemIdentifier = '"'; /** * And operator. * * @var \string * @since 1.0.0 */ protected $and = 'AND'; /** * Or operator. * * @var \string * @since 1.0.0 */ protected $or = 'OR'; /** * Select components. * * @var \string[] * @since 1.0.0 */ protected $selectComponents = [ 'aggregate', 'selects', 'from', 'joins', 'wheres', 'havings', 'groups', 'orders', 'limit', 'offset', 'unions', 'lock', ]; /** * Insert components. * * @var \string[] * @since 1.0.0 */ protected $insertComponents = [ 'into', 'inserts', 'values', ]; /** * Update components. * * @var \string[] * @since 1.0.0 */ protected $updateComponents = [ 'updates', 'sets', 'wheres', ]; /** * Compile to query. * * @param Builder $query Builder * * @return \string * * @throws * * @since 1.0.0 * @author Dennis Eichhorn */ public function compileQuery($query) : \string { return trim( implode(' ', array_filter( $this->compileComponents($query), function ($value) { return (string) $value !== ''; } ) ) ) . ';'; } /** * Compile components. * * @param Builder $query Builder * * @return \string[] * * @throws * * @since 1.0.0 * @author Dennis Eichhorn */ protected function compileComponents(Builder $query) : array { $sql = []; switch ($query->getType()) { case QueryType::SELECT: $components = $this->selectComponents; break; case QueryType::INSERT: $components = $this->insertComponents; break; case QueryType::UPDATE: $components = null; break; case QueryType::DELETE: $components = null; break; default: throw new \InvalidArgumentException('Unknown query type.'); } /* Loop all possible query components and if they exist compile them. */ foreach ($components as $component) { if (isset($query->{$component}) && !empty($query->{$component})) { $sql[$component] = $this->{'compile' . ucfirst($component)}($query, $query->{$component}); } } return $sql; } /** * Compile select. * * @param Builder $query Builder * @param array $columns Columns * * @return \string * * @since 1.0.0 * @author Dennis Eichhorn */ protected function compileSelects(Builder $query, array $columns) : \string { $expression = $this->expressionizeTableColumn($columns, $query->getPrefix()); if ($expression == '') { return ''; } return ($query->distinct ? 'SELECT DISTINCT ' : 'SELECT ') . $expression; } /** * Expressionize elements. * * @param array $elements Elements * @param \string $prefix Prefix for table * * @return \string * * @since 1.0.0 * @author Dennis Eichhorn */ protected function expressionizeTableColumn(array $elements, \string $prefix = '') : \string { $expression = ''; foreach ($elements as $key => $element) { if (is_string($element) && $element !== '*') { $expression .= $this->compileSystem($element, $prefix) . ', '; } elseif (is_string($element) && $element === '*') { $expression .= '*, '; } elseif ($element instanceof \Closure) { $expression .= $element() . ', '; } elseif ($element instanceof Builder) { $expression .= $element->toSql() . ', '; } else { throw new \InvalidArgumentException(); } } return rtrim($expression, ', '); } /** * Compile from. * * @param Builder $query Builder * @param array $table Tables * * @return \string * * @since 1.0.0 * @author Dennis Eichhorn */ protected function compileFrom(Builder $query, array $table) : \string { $expression = $this->expressionizeTableColumn($table, $query->getPrefix()); if ($expression == '') { return ''; } return 'FROM ' . $expression; } /** * Compile where. * * @param Builder $query Builder * @param array $wheres Where elmenets * @param \bool $first Is first element (usefull for nesting) * * @return \string * * @since 1.0.0 * @author Dennis Eichhorn */ protected function compileWheres(Builder $query, array $wheres, \bool $first = true) : \string { $expression = ''; foreach ($wheres as $key => $where) { foreach ($where as $key2 => $element) { if (!$first) { $expression .= ' ' . strtoupper($element['boolean']) . ' '; } if (is_string($element['column'])) { $expression .= $this->compileSystem($element['column'], $query->getPrefix()) . ' ' . strtoupper($element['operator']) . ' ' . $this->compileValue($element['value'], $query->getPrefix()); } elseif ($element['column'] instanceof \Closure) { } elseif ($element['column'] instanceof Builder) { } if (is_string($element['value'])) { } elseif ($element['value'] instanceof \Closure) { } elseif ($element['value'] instanceof Builder) { } elseif ($element['value'] instanceof \DateTime) { } elseif (is_int($element['value'])) { } elseif (is_bool($element['value'])) { } elseif (is_null($element['value'])) { } elseif (is_float($element['value'])) { } elseif (is_array($element['value'])) { // is bind } $first = false; } } if ($expression == '') { return ''; } return 'WHERE ' . $expression; } /** * Compile value. * * @param array|string|\Closure $value Value * @param \string $prefix Prefix in case value is a table * * @return \string * * @throws \InvalidArgumentException * * @since 1.0.0 * @author Dennis Eichhorn */ protected function compileValue($value, $prefix = '') : \string { if (is_string($value)) { return $this->valueQuotes . $value . $this->valueQuotes; } elseif (is_int($value)) { return $value; } elseif (is_array($value)) { $values = ''; foreach ($value as $val) { $values .= $this->compileValue($val) . ', '; } return '(' . rtrim($values, ', ') . ')'; } elseif ($value instanceof \DateTime) { return $this->valueQuotes . $value->format('Y-m-d h:m:s') . $this->valueQuotes; } elseif (is_null($value)) { return 'NULL'; } elseif (is_bool($value)) { return (string) ((int) $value); } elseif ($value instanceof Column) { return $this->compileSystem($value->getColumn(), $prefix); } else { throw new \InvalidArgumentException(); } } /** * Compile system. * * @param array|\string $system System * @param \string $prefix Prefix for table * * @return \string * * @since 1.0.0 * @author Dennis Eichhorn */ protected function compileSystem($system, \string $prefix = '') : \string { if (count($split = explode('.', $system)) == 2) { return $this->compileSystem($prefix . $split[0]) . '.' . $this->compileSystem($split[1]); } else { return $this->systemIdentifier . $prefix . $system . $this->systemIdentifier; } } /** * Compile limit. * * @param Builder $query Builder * @param \int $limit Limit * * @return \string * * @since 1.0.0 * @author Dennis Eichhorn */ protected function compileLimit(Builder $query, $limit) : \string { return 'LIMIT ' . $limit; } /** * Compile offset. * * @param Builder $query Builder * @param \int $offset Offset * * @return \string * * @since 1.0.0 * @author Dennis Eichhorn */ protected function compileOffset(Builder $query, $offset) : \string { return 'OFFSET ' . $offset; } private function compileJoins() { return ''; } private function compileGroups(Builder $query, array $groups) { $expression = ''; foreach ($groups as $group) { $expression .= $this->compileSystem($group, $query->getPrefix()) . ', '; } $expression = rtrim($expression, ', '); return 'GROUP BY ' . $expression; } /** * Compile offset. * * @param Builder $query Builder * @param array $orders Order * * @return \string * * @since 1.0.0 * @author Dennis Eichhorn */ private function compileOrders(Builder $query, array $orders) : \string { $expression = ''; foreach ($orders as $order) { $expression .= $this->compileSystem($order['column'], $query->getPrefix()) . ' ' . $order['order'] . ', '; } if ($expression == '') { return ''; } $expression = rtrim($expression, ', '); return 'ORDER BY ' . $expression; } private function compileUnions() { return ''; } private function compileLock() { return ''; } /** * Compile insert into table. * * @param Builder $query Builder * @param \string $table Table * * @return \string * * @since 1.0.0 * @author Dennis Eichhorn */ protected function compileInto(Builder $query, $table) : \string { return 'INSERT INTO ' . $this->compileSystem($table, $query->getPrefix()); } /** * Compile insert into columns. * * @param Builder $query Builder * @param array $columns Columns * * @return \string * * @since 1.0.0 * @author Dennis Eichhorn */ protected function compileInserts(Builder $query, array $columns) : \string { $cols = ''; foreach ($columns as $column) { $cols .= $this->compileSystem($column) . ', '; } if ($cols == '') { return ''; } return '(' . rtrim($cols, ', ') . ')'; } /** * Compile insert values. * * @param Builder $query Builder * @param array $values Values * * @return \string * * @since 1.0.0 * @author Dennis Eichhorn */ protected function compileValues(Builder $query, array $values) : \string { $vals = ''; foreach ($values as $value) { $vals .= $this->compileValue($value) . ', '; } if ($vals == '') { return ''; } return 'VALUES ' . rtrim($vals, ', '); } }