datetimeFormat = $format; } /** * Compile to query. * * @param BuilderAbstract $query Builder * * @return string * * @since 1.0.0 */ public function compileQuery(BuilderAbstract $query) : string { $components = $this->compileComponents($query); $queryString = ''; foreach ($components as $component) { if ($component !== '') { $queryString .= $component . ' '; } } return \substr($queryString, 0, -1) . ';'; } /** * Compile post querys. * * These are queries, which should be run after the main query (e.g. table alters, trigger definitions etc.) * * @param BuilderAbstract $query Builder * * @return string[] * * @since 1.0.0 */ public function compilePostQuerys(BuilderAbstract $query) : array { return []; } /** * Compile components. * * @param BuilderAbstract $query Builder * * @return string[] * * @throws \InvalidArgumentException * * @since 1.0.0 */ abstract protected function compileComponents(BuilderAbstract $query) : array; /** * Get date format. * * @return string * * @since 1.0.0 */ public function getDateFormat() : string { return 'Y-m-d H:i:s'; } /** * Expressionize elements. * * @param array $elements Elements * @param bool $column Is column? * * @return string * * @since 1.0.0 */ protected function expressionizeTableColumn(array $elements, bool $column = true) : string { $expression = ''; foreach ($elements as $key => $element) { if (\is_string($element) && $element !== '*') { $expression .= $this->compileSystem($element) . (\is_string($key) ? ' as ' . $key : '') . ', '; } elseif ($element === '*') { $expression .= '*, '; } elseif ($element instanceof \Closure) { $expression .= $element() . (\is_string($key) ? ' as ' . $key : '') . ', '; } elseif ($element instanceof BuilderAbstract) { $expression .= $element->toSql() . (\is_string($key) ? ' as ' . $key : '') . ', '; } else { throw new \InvalidArgumentException(); } } return \rtrim($expression, ', '); } /** * Compile system. * * A system is a table, a sub query or special keyword. * * @param string $system System * * @return string * * @since 1.0.0 */ protected function compileSystem(string $system) : string { $identifierStart = $this->systemIdentifierStart; $identifierEnd = $this->systemIdentifierEnd; foreach ($this->specialKeywords as $keyword) { if (\strrpos($system, $keyword, -\strlen($system)) !== false) { $identifierStart = ''; $identifierEnd = ''; break; } } // The following code could have been handled with \explode more elegantly but \explode needs more memory and more time // Normally this wouldn't be a problem but in this case there are so many function calls to this routine, // that it makes sense to make this "minor" improvement. if (($pos = \stripos($system, '.')) !== false) { $split = [\substr($system, 0, $pos), \substr($system, $pos + 1)]; return ($split[0] !== '*' ? $identifierStart : '') . $split[0] . ($split[0] !== '*' ? $identifierEnd : '') . '.' . ($split[1] !== '*' ? $identifierStart : '') . $split[1] . ($split[1] !== '*' ? $identifierEnd : ''); } return ($system !== '*' ? $identifierStart : '') . $system . ($system !== '*' ? $identifierEnd : ''); } /** * Compile value. * * @param BuilderAbstract $query Query builder * @param mixed $value Value * * @return string returns a string representation of the value * * @throws \InvalidArgumentException throws this exception if the value to compile is not supported by this function * * @since 1.0.0 */ protected function compileValue(BuilderAbstract $query, mixed $value) : string { if (\is_string($value)) { return $query->quote($value); } elseif (\is_int($value)) { return (string) $value; } elseif (\is_array($value)) { $value = \array_values($value); $count = \count($value) - 1; $values = '('; for ($i = 0; $i < $count; ++$i) { $values .= $this->compileValue($query, $value[$i]) . ', '; } return $values . $this->compileValue($query, $value[$count]) . ')'; } elseif ($value instanceof \DateTime) { return $query->quote($value->format($this->datetimeFormat)); } elseif ($value === null) { return 'NULL'; } elseif (\is_bool($value)) { return (string) ((int) $value); } elseif (\is_float($value)) { return \rtrim(\rtrim(\number_format($value, 5, '.', ''), '0'), '.'); } elseif ($value instanceof Column) { return '(' . \rtrim($this->compileColumnQuery($value), ';') . ')'; } elseif ($value instanceof BuilderAbstract) { return '(' . \rtrim($value->toSql(), ';') . ')'; } elseif ($value instanceof \JsonSerializable) { $encoded = \json_encode($value); return $encoded ? $encoded : 'NULL'; } elseif ($value instanceof SerializableInterface) { return $value->serialize(); } elseif ($value instanceof Parameter) { return $value->__toString(); } else { throw new \InvalidArgumentException(\gettype($value)); } } /** * Compile column query. * * @param Column $column Where query * * @return string * * @since 1.0.0 */ protected function compileColumnQuery(Column $column) : string { return $column->toSql(); } }