bug fixes

This commit is contained in:
Dennis Eichhorn 2024-05-12 00:06:29 +00:00
parent a7451d330d
commit f84b33f7fa
34 changed files with 1854 additions and 262 deletions

View File

@ -9,5 +9,5 @@ jobs:
- uses: actions/first-interaction@v1
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
issue-message: 'Thank you for createing this issue. We will check it as soon as possible.'
issue-message: 'Thank you for creating this issue. We will check it as soon as possible.'
pr-message: 'Thank you for your pull request. We will check it as soon as possible.'

View File

@ -392,9 +392,32 @@ class Account implements \JsonSerializable
'permissions' => $this->permissions,
'type' => $this->type,
'status' => $this->status,
'l11n' => $this->l11n,
];
}
public function from (array $account) : void
{
$this->id = $account['id'];
$this->name1 = $account['name'][0];
$this->name2 = $account['name'][1];
$this->name3 = $account['name'][2];
$this->email = $account['email'];
$this->login = $account['login'];
$this->type = $account['type'];
$this->status = $account['status'];
$this->l11n = Localization::fromJson($account['l11n']);
foreach (($account['groups'] ?? []) as $group) {
$this->groups[] = Group::fromJson($group);
}
foreach (($account['permissions'] ?? []) as $permission) {
$this->permissions[] = PermissionAbstract::fromJson($permission);
}
}
/**
* {@inheritdoc}
*/

View File

@ -109,6 +109,8 @@ class Group implements \JsonSerializable
'description' => $this->description,
'permissions' => $this->permissions,
'members' => $this->members,
'parents' => $this->parents,
'status' => $this->status,
];
}
@ -127,4 +129,22 @@ class Group implements \JsonSerializable
{
return $this->toArray();
}
public static function fromJson(array $group) : self
{
$new = new self();
$new->id = $group['id'];
$new->name = $group['name'];
$new->description = $group['description'];
$new->members = $group['members'];
$new->parents = $group['parents'];
$new->status = $group['status'];
foreach (($group['permissions'] ?? []) as $permission) {
$new->permissions[] = PermissionAbstract::fromJson($permission);
}
return $new;
}
}

View File

@ -360,7 +360,36 @@ class PermissionAbstract implements \JsonSerializable
'category' => $this->category,
'element' => $this->element,
'component' => $this->component,
'permission' => $this->getPermission(),
'hasRead' => $this->hasRead,
'hasModify' => $this->hasModify,
'hasCreate' => $this->hasCreate,
'hasDelete' => $this->hasDelete,
'defaultCPermissions' => $this->defaultCPermissions,
'hasPermission' => $this->hasPermission,
'defaultPPermissions' => $this->defaultPPermissions,
];
}
public static function fromJson(array $permission) : self
{
$new = new self();
$new->id = $permission['id'];
$new->unit = $permission['unit'];
$new->app = $permission['app'];
$new->module = $permission['module'];
$new->from = $permission['from'];
$new->category = $permission['category'];
$new->element = $permission['element'];
$new->component = $permission['component'];
$new->hasRead = $permission['hasRead'];
$new->hasModify = $permission['hasModify'];
$new->hasCreate = $permission['hasCreate'];
$new->hasDelete = $permission['hasDelete'];
$new->defaultCPermissions = $permission['defaultCPermissions'];
$new->hasPermission = $permission['hasPermission'];
$new->defaultPPermissions = $permission['defaultPPermissions'];
return $new;
}
}

View File

@ -14,6 +14,7 @@ declare(strict_types=1);
namespace phpOMS\DataStorage\Cache;
use phpOMS\DataStorage\Cache\Connection\ConnectionAbstract;
use phpOMS\DataStorage\Cache\Connection\ConnectionFactory;
use phpOMS\DataStorage\Cache\Connection\NullCache;
use phpOMS\DataStorage\DataStorageConnectionInterface;
@ -85,11 +86,11 @@ final class CachePool implements DataStoragePoolInterface
*
* @param string $key Cache to request
*
* @return DataStorageConnectionInterface
* @return ConnectionAbstract
*
* @since 1.0.0
*/
public function get(string $key = '') : DataStorageConnectionInterface
public function get(string $key = '') : ConnectionAbstract
{
if ((!empty($key) && !isset($this->pool[$key])) || empty($this->pool)) {
return new NullCache();

View File

@ -53,9 +53,9 @@ class ConnectionFactory
case CacheType::FILE:
return new FileCache($cacheData['path'] ?? '');
case CacheType::REDIS:
return new RedisCache($cacheData['data'] ?? []);
return new RedisCache($cacheData ?? []);
case CacheType::MEMCACHED:
return new MemCached($cacheData['data'] ?? []);
return new MemCached($cacheData ?? []);
default:
throw new \InvalidArgumentException('Cache "' . ($cacheData['type'] ?? '') . '" is not supported.');
}

View File

@ -113,7 +113,7 @@ final class RedisCache extends ConnectionAbstract
}
if ($expire > 0) {
$this->con->setEx((string) $key, $expire, $this->build($value));
$this->con->set((string) $key, $expire, $this->build($value));
return;
}
@ -131,10 +131,10 @@ final class RedisCache extends ConnectionAbstract
}
if ($expire > 0) {
return $this->con->setNx((string) $key, $this->build($value), $expire);
return $this->con->set((string) $key, $this->build($value), $expire);
}
return $this->con->setNx((string) $key, $this->build($value));
return $this->con->set((string) $key, $this->build($value));
}
/**

View File

@ -35,6 +35,14 @@ abstract class BuilderAbstract
*/
protected bool $isReadOnly = false;
/**
* Use prepared statement.
*
* @var bool
* @since 1.0.0
*/
public bool $usePreparedStmt = true;
/**
* Database connection.
*
@ -59,6 +67,14 @@ abstract class BuilderAbstract
*/
public string $raw = '';
/**
* Binds.
*
* @var array
* @since 1.0.0
*/
public array $binds = [];
/**
* Get connection
*
@ -85,6 +101,27 @@ abstract class BuilderAbstract
return $this->connection->con->quote($value);
}
/**
* Bind parameter.
*
* @param array $binds Binds
* @param ?string $key Key
*
* @return Builder
*
* @since 1.0.0
*/
public function bind(array $binds, ?string $key = null) : self
{
if ($key === null) {
$this->binds[] = $binds;
} else {
$this->binds[$key] = $binds;
}
return $this;
}
/**
* Get query type.
*

View File

@ -86,14 +86,14 @@ final class SQLiteConnection extends ConnectionAbstract
$this->close();
try {
if (!\is_file($this->dbdata['database'])) {
throw new \PDOException();
}
$this->con = new \PDO($this->dbdata['db'] . ':' . $this->dbdata['database']);
$this->con->setAttribute(\PDO::ATTR_EMULATE_PREPARES, false);
$this->con->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
if (!\is_file($this->dbdata['database'])) {
throw new \PDOException();
}
$this->status = DatabaseStatus::OK;
} catch (\PDOException $_) {
$this->con = new NullPDO();

View File

@ -139,8 +139,9 @@ abstract class GrammarAbstract
/**
* Expressionize elements.
*
* @param array $elements Elements
* @param bool $column Is column?
* @param array $elements Elements
* @param BuilderAbstract $query Builder
* @param bool $column Is column?
*
* @return string
*
@ -148,7 +149,7 @@ abstract class GrammarAbstract
*
* @since 1.0.0
*/
public function expressionizeTableColumn(array $elements, bool $column = true) : string
public function expressionizeTableColumn(array $elements, BuilderAbstract $query = null, bool $column = true) : string
{
$expression = '';
@ -160,6 +161,13 @@ abstract class GrammarAbstract
$expression .= $element . ', ';
} elseif ($element instanceof BuilderAbstract) {
$expression .= $element->toSql() . (\is_string($key) ? ' AS ' . $key : '') . ', ';
if ($query->usePreparedStmt && $element->usePreparedStmt && $query !== null) {
// If we have a subquery, we need to copy the binds over
foreach ($element->binds as $bind) {
$query->bind($bind);
};
}
} elseif ($element instanceof \Closure) {
$expression .= $element() . (\is_string($key) ? ' AS ' . $key : '') . ', ';
} else {
@ -219,6 +227,10 @@ abstract class GrammarAbstract
/**
* Compile value.
*
* If the query builder uses prepared statements it will use these prepared statements + data binds.
* If the query builder doesn't use prepared statements but the setting usePreparedStmt is set to true
* this function will try and convert every value that can be prepared to a prepared statement
*
* @param BuilderAbstract $query Query builder
* @param mixed $value Value
*
@ -230,10 +242,23 @@ abstract class GrammarAbstract
*/
protected function compileValue(BuilderAbstract $query, mixed $value) : string
{
$compiled = '';
$type = -1;
if (\is_string($value)) {
return $query->quote($value);
if ($query->usePreparedStmt) {
$type = \PDO::PARAM_STR;
$compiled = $value;
} else {
$compiled = $query->quote($value);
}
} elseif (\is_int($value)) {
return (string) $value;
if ($query->usePreparedStmt) {
$type = \PDO::PARAM_INT;
$compiled = $value;
} else {
$compiled = (string) $value;
}
} elseif (\is_array($value)) {
$value = \array_values($value);
$count = \count($value) - 1;
@ -243,29 +268,65 @@ abstract class GrammarAbstract
$values .= $this->compileValue($query, $value[$i]) . ', ';
}
return $values . $this->compileValue($query, $value[$count]) . ')';
} elseif ($value instanceof \DateTime || $value instanceof \DateTimeImmutable) {
return $query->quote($value->format($this->datetimeFormat));
$compiled = $values . $this->compileValue($query, $value[$count]) . ')';
} elseif ($value instanceof \DateTimeInterface) {
if ($query->usePreparedStmt) {
$type = \PDO::PARAM_STR;
$compiled = $value->format($this->datetimeFormat);
} else {
$compiled = $query->quote($value->format($this->datetimeFormat));
}
} elseif ($value === null) {
return 'NULL';
$compiled = 'NULL';
} elseif (\is_bool($value)) {
return (string) ((int) $value);
if ($query->usePreparedStmt) {
$type = \PDO::PARAM_BOOL;
$compiled = $value;
} else {
$compiled = (string) ((int) $value);
}
} elseif (\is_float($value)) {
return \rtrim(\rtrim(\number_format($value, 5, '.', ''), '0'), '.');
$compiled = \rtrim(\rtrim(\number_format($value, 5, '.', ''), '0'), '.');
} elseif ($value instanceof ColumnName) {
return $this->compileSystem($value->name);
$compiled = $this->compileSystem($value->name);
} elseif ($value instanceof BuilderAbstract) {
return '(' . \rtrim($value->toSql(), ';') . ')';
$compiled = '(' . \rtrim($value->toSql(), ';') . ')';
if ($query->usePreparedStmt && $value->usePreparedStmt) {
$type = -2;
}
} elseif ($value instanceof \JsonSerializable) {
$encoded = \json_encode($value);
return $encoded ? $encoded : 'NULL';
if ($query->usePreparedStmt) {
$type = $encoded ? \PDO::PARAM_STR : \PDO::PARAM_STR;
$compiled = $encoded ? $value : null;
} else {
$compiled = $encoded ? $query->quote($encoded) : 'NULL';
}
} elseif ($value instanceof SerializableInterface) {
return $value->serialize();
$compiled = $value->serialize();
} elseif ($value instanceof Parameter) {
return $value->__toString();
$compiled = $value->__toString();
} else {
throw new \InvalidArgumentException(\gettype($value));
}
if ($query->usePreparedStmt && $type !== -1) {
if ($type === -2) {
// If we have a subquery, we need to copy the binds over
foreach ($value->binds as $bind) {
$query->bind($bind);
}
} else {
$query->bind([
'value' => $compiled,
'type' => $type,
]);
$compiled = '?';
}
}
return $compiled;
}
}

View File

@ -102,10 +102,7 @@ final class DeleteMapper extends DataMapperAbstract
->from($this->mapper::TABLE)
->where($this->mapper::TABLE . '.' . $this->mapper::PRIMARYFIELD, '=', $objId);
$sth = $this->db->con->prepare($query->toSql());
if ($sth !== false) {
$sth->execute();
}
$query->execute();
}
/**
@ -250,9 +247,6 @@ final class DeleteMapper extends DataMapperAbstract
$relQuery->where($this->mapper::HAS_MANY[$member]['table'] . '.' . $this->mapper::HAS_MANY[$member]['external'], 'IN', $objIds);
}
$sth = $this->db->con->prepare($relQuery->toSql());
if ($sth !== false) {
$sth->execute();
}
$relQuery->execute();
}
}

View File

@ -347,9 +347,8 @@ final class ReadMapper extends DataMapperAbstract
$results = false;
try {
$sth = $this->db->con->prepare($query->toSql());
if ($sth !== false) {
$sth->execute();
$sth = $query->execute();
if ($sth !== null) {
$results = $sth->fetchAll(\PDO::FETCH_ASSOC);
}
} catch (\Throwable $t) {
@ -381,15 +380,13 @@ final class ReadMapper extends DataMapperAbstract
$query ??= $this->getQuery();
try {
$sth = $this->db->con->prepare($query->toSql());
if ($sth === false) {
$sth = $query->execute();
if ($sth === null) {
yield [];
return;
}
$sth->execute();
while ($row = $sth->fetch(\PDO::FETCH_ASSOC)) {
yield $row;
}

View File

@ -162,8 +162,8 @@ final class UpdateMapper extends DataMapperAbstract
}
}
$sth = $this->db->con->prepare($query->toSql());
if ($sth === false) {
$sth = $query->prepare();
if ($sth === null) {
throw new \Exception();
}
@ -442,12 +442,11 @@ final class UpdateMapper extends DataMapperAbstract
->on($many['table'] . '.' . $src, '=', $many['mapper']::TABLE . '.' . $many['mapper']::PRIMARYFIELD);
}
$sth = $this->db->con->prepare($query->toSql());
if ($sth === false) {
$sth = $query->execute();
if ($sth === null) {
continue;
}
$sth->execute();
$result = $sth->fetchAll(\PDO::FETCH_COLUMN);
if ($result === false) {

View File

@ -162,8 +162,8 @@ final class WriteMapper extends DataMapperAbstract
$query->insert($this->mapper::PRIMARYFIELD)->value(0);
}
$sth = $this->db->con->prepare($query->toSql());
if ($sth === false) {
$sth = $query->prepare();
if ($sth === null) {
throw new \Exception();
}
@ -438,8 +438,8 @@ final class WriteMapper extends DataMapperAbstract
$relQuery->values($src, $objId);
}
$sth = $this->db->con->prepare($relQuery->toSql());
if ($sth === false) {
$sth = $relQuery->prepare();
if ($sth === null) {
throw new \Exception();
}

View File

@ -189,14 +189,6 @@ class Builder extends BuilderAbstract
*/
public ?int $offset = null;
/**
* Binds.
*
* @var array
* @since 1.0.0
*/
private array $binds = [];
/**
* Union.
*
@ -341,26 +333,6 @@ class Builder extends BuilderAbstract
return $this;
}
/**
* Bind parameter.
*
* @param string|array $binds Binds
*
* @return Builder
*
* @since 1.0.0
*/
public function bind(string | array $binds) : self
{
if (\is_array($binds)) {
$this->binds += $binds;
} else {
$this->binds[] = $binds;
}
return $this;
}
/**
* {@inheritdoc}
*/
@ -1373,6 +1345,42 @@ class Builder extends BuilderAbstract
* {@inheritdoc}
*/
public function execute() : ?\PDOStatement
{
$sth = null;
try {
$sth = $this->prepare();
if ($sth !== null) {
$sth->execute();
}
} catch (\Throwable $t) {
// @codeCoverageIgnoreStart
\phpOMS\Log\FileLogger::getInstance()->error(
\phpOMS\Log\FileLogger::MSG_FULL, [
'message' => $t->getMessage() . ':' . $this->toSql(),
'line' => __LINE__,
'file' => self::class,
]
);
\phpOMS\Log\FileLogger::getInstance()->error(
\phpOMS\Log\FileLogger::MSG_FULL, [
'message' => \json_encode($this->binds),
'line' => __LINE__,
'file' => self::class,
]
);
$sth = null;
// @codeCoverageIgnoreEnd
}
return $sth;
}
/**
* {@inheritdoc}
*/
public function prepare() : ?\PDOStatement
{
$sth = null;
$sql = '';
@ -1384,12 +1392,12 @@ class Builder extends BuilderAbstract
}
foreach ($this->binds as $key => $bind) {
$type = self::getBindParamType($bind);
if (!isset($bind['type'])) {
$bind['type'] = self::getBindParamType($bind['value']);
}
$sth->bindParam($key, $bind, $type);
$sth->bindParam(\is_int($key) ? $key + 1 : $key, $bind['value'], $bind['type']);
}
$sth->execute();
} catch (\Throwable $t) {
// @codeCoverageIgnoreStart
\phpOMS\Log\FileLogger::getInstance()->error(
@ -1400,6 +1408,14 @@ class Builder extends BuilderAbstract
]
);
\phpOMS\Log\FileLogger::getInstance()->error(
\phpOMS\Log\FileLogger::MSG_FULL, [
'message' => \json_encode($this->binds),
'line' => __LINE__,
'file' => self::class,
]
);
$sth = null;
// @codeCoverageIgnoreEnd
}
@ -1447,7 +1463,12 @@ class Builder extends BuilderAbstract
} elseif ($column instanceof SerializableInterface) {
return $column->serialize();
} elseif ($column instanceof self) {
return \md5($column->toSql());
$tmp = $column->usePreparedStmt;
$column->usePreparedStmt = false;
$hash = \md5($column->toSql());
$column->usePreparedStmt = $tmp;
return $hash;
}
throw new \Exception();

View File

@ -156,7 +156,7 @@ class Grammar extends GrammarAbstract
*/
protected function compileSelects(Builder $query, array $columns) : string
{
$expression = $this->expressionizeTableColumn($columns, false);
$expression = $this->expressionizeTableColumn($columns, $query, false);
if ($expression === '') {
$expression = '*';
@ -207,7 +207,7 @@ class Grammar extends GrammarAbstract
*/
protected function compileUpdates(Builder $query, array $table) : string
{
$expression = $this->expressionizeTableColumn($table);
$expression = $this->expressionizeTableColumn($table, $query);
if ($expression === '') {
return '';
@ -243,7 +243,7 @@ class Grammar extends GrammarAbstract
*/
protected function compileFrom(Builder $query, array $table) : string
{
$expression = $this->expressionizeTableColumn($table);
$expression = $this->expressionizeTableColumn($table, $query);
if ($expression === '') {
return '';
@ -265,76 +265,73 @@ class Grammar extends GrammarAbstract
*/
public function compileWheres(Builder $query, array $wheres, bool $first = true) : string
{
$expression = '';
$outer = '';
foreach ($wheres as $where) {
foreach ($where as $element) {
$expression .= $this->compileWhereElement($element, $query, $first);
$first = false;
$expression = '';
$prefix = '';
if (!$first) {
$prefix = ' ' . \strtoupper($element['boolean']) . ' ';
}
if (\is_string($element['column'])) {
$expression .= $this->compileSystem($element['column']);
} elseif ($element['column'] instanceof Builder) {
$expression .= '(' . \rtrim($element['column']->toSql(), ';') . ')';
if ($query->usePreparedStmt && $element['column']->usePreparedStmt) {
// If we have a subquery, we need to copy the binds over
// BUT we are only allowed to do this once per wheres builder
foreach ($element['column']->binds as $bind) {
$query->bind($bind);
};
}
} elseif ($element['column'] instanceof \Closure) {
$expression .= $element['column']();
}
// Handle null for IN (...)
// This is not allowed and must be written as (IN (...) OR IS NULL)
$isArray = \is_array($element['value']);
$hasNull = false;
if ($isArray && ($key = \array_search(null, $element['value'], true)) !== false) {
$hasNull = true;
unset($element['value'][$key]);
if (empty($element['value'])) {
$element['operator'] = '=';
$element['value'] = null;
}
}
if (isset($element['value']) && (!empty($element['value']) || !$isArray)) {
if ($isArray && \count($element['value']) === 1) {
$element['value'] = \reset($element['value']);
$element['operator'] = '=';
}
$expression .= ' ' . \strtoupper($element['operator']) . ' ' . $this->compileValue($query, $element['value']);
if ($hasNull) {
$expression = '(' . $expression . ' OR ' . $this->compileSystem($element['column']) . ' IS NULL)';
}
} elseif ($element['value'] === null && !($element['column'] instanceof Builder)) {
$operator = $element['operator'] === '=' ? 'IS' : 'IS NOT';
$expression .= ' ' . $operator . ' ' . $this->compileValue($query, $element['value']);
}
$outer .= $prefix . $expression;
$first = false;
}
}
if ($expression === '') {
if ($outer === '') {
return '';
}
return 'WHERE ' . $expression;
}
/**
* Compile where element.
*
* @param array $element Element data
* @param Builder $query Query builder
* @param bool $first Is first element (useful for nesting)
*
* @return string
*
* @since 1.0.0
*/
protected function compileWhereElement(array $element, Builder $query, bool $first = true) : string
{
$expression = '';
$prefix = '';
if (!$first) {
$prefix = ' ' . \strtoupper($element['boolean']) . ' ';
}
if (\is_string($element['column'])) {
$expression .= $this->compileSystem($element['column']);
} elseif ($element['column'] instanceof Builder) {
$expression .= '(' . \rtrim($element['column']->toSql(), ';') . ')';
} elseif ($element['column'] instanceof \Closure) {
$expression .= $element['column']();
}
// Handle null for IN (...)
// This is not allowed and must be written as (IN (...) OR IS NULL)
$isArray = \is_array($element['value']);
$hasNull = false;
if ($isArray && ($key = \array_search(null, $element['value'], true)) !== false) {
$hasNull = true;
unset($element['value'][$key]);
if (empty($element['value'])) {
$element['operator'] = '=';
$element['value'] = null;
}
}
if (isset($element['value']) && (!empty($element['value']) || !$isArray)) {
$expression .= ' ' . \strtoupper($element['operator']) . ' ' . $this->compileValue($query, $element['value']);
if ($hasNull) {
$expression = '(' . $expression . ' OR ' . $this->compileSystem($element['column']) . ' IS NULL)';
}
} elseif ($element['value'] === null && !($element['column'] instanceof Builder)) {
$operator = $element['operator'] === '=' ? 'IS' : 'IS NOT';
$expression .= ' ' . $operator . ' ' . $this->compileValue($query, $element['value']);
}
return $prefix . $expression;
return 'WHERE ' . $outer;
}
/**
@ -349,6 +346,16 @@ class Grammar extends GrammarAbstract
*/
protected function compileLimit(Builder $query, int $limit) : string
{
if ($query->usePreparedStmt) {
$query->bind([
'value' => $limit,
'type' => \PDO::PARAM_INT,
]);
$limit = '?';
}
return 'LIMIT ' . $limit;
}
@ -364,6 +371,16 @@ class Grammar extends GrammarAbstract
*/
protected function compileOffset(Builder $query, int $offset) : string
{
if ($query->usePreparedStmt) {
$query->bind([
'value' => $offset,
'type' => \PDO::PARAM_INT,
]);
$offset = '?';
}
return 'OFFSET ' . $offset;
}
@ -390,6 +407,13 @@ class Grammar extends GrammarAbstract
$expression .= $join['table']() . (\is_string($join['alias']) ? ' as ' . $join['alias'] : '');
} elseif ($join['table'] instanceof Builder) {
$expression .= '(' . \rtrim($join['table']->toSql(), ';') . ')' . (\is_string($join['alias']) ? ' as ' . $join['alias'] : '');
if ($query->usePreparedStmt && $join['table']->usePreparedStmt) {
// If we have a subquery, we need to copy the binds over
foreach ($join['table']->binds as $bind) {
$query->bind($bind);
};
}
}
$expression .= $this->compileOn($query, $query->ons[$join['alias'] ?? $table]) . ' ';
@ -453,13 +477,20 @@ class Grammar extends GrammarAbstract
$expression .= $this->compileSystem($element['column']);
} elseif ($element['column'] instanceof Builder) {
$expression .= '(' . $element['column']->toSql() . ')';
if ($query->usePreparedStmt && $element['column']->usePreparedStmt) {
// If we have a subquery, we need to copy the binds over
foreach ($element['column']->binds as $bind) {
$query->bind($bind);
};
}
} elseif ($element['column'] instanceof \Closure) {
$expression .= $element['column']();
}
// @bug The on part of a join doesn't allow string values because they conflict with column name
// Other data types are possible because they don't conflict with the data type of columns (string)
// Consider to create a ColumnName() class.
// Consider to create a ColumnName() class or maybe StringValue() since we use string values less often.
// https://github.com/Karaka-Management/phpOMS/issues/369
if (isset($element['value'])) {
$expression .= ' ' . \strtoupper($element['operator']) . ' '
@ -628,7 +659,7 @@ class Grammar extends GrammarAbstract
$vals = '';
foreach ($values as $column => $value) {
$expression = $this->expressionizeTableColumn([$column], false);
$expression = $this->expressionizeTableColumn([$column], $query, false);
$vals .= $expression . ' = ' . $this->compileValue($query, $value) . ', ';
}

View File

@ -120,6 +120,12 @@ class Builder extends BuilderAbstract
*/
public array $alterAdd = [];
/**
* Has post query to run.
*
* @var bool
* @since 1.0.0
*/
public bool $hasPostQuery = false;
/**
@ -151,6 +157,32 @@ class Builder extends BuilderAbstract
$this->grammar = $connection->getSchemaGrammar();
}
/**
* Get the column type from a variable value.
*
* @param mixed $value Variable value
*
* @return string
*
* @since 1.0.0
*/
public static function getTypeFromVariable(mixed $value) : string
{
if (\is_string($value)) {
return 'TEXT';
} elseif (\is_int($value)) {
return 'INT';
} elseif (\is_bool($value)) {
return 'TINYINT(1)';
} elseif (\is_float($value)) {
return 'DECIMAL(10,6)';
} elseif ($value instanceof \DateTimeInterface) {
return 'DATETIME';
}
return 'TEXT';
}
/**
* Create schema builder from schema definition.
*
@ -168,6 +200,7 @@ class Builder extends BuilderAbstract
public static function createFromSchema(array $definition, ConnectionAbstract $connection) : self
{
$builder = new self($connection);
$builder->usePreparedStmt = false;
$builder->createTable($definition['name'] ?? '');
foreach ($definition['fields'] as $name => $def) {

View File

@ -129,14 +129,14 @@ class SQLiteGrammar extends Grammar
$fieldQuery .= ' ' . ($field['null'] ? '' : 'NOT ') . 'NULL';
}
if ($field['autoincrement'] ?? false) {
$fieldQuery .= ' AUTOINCREMENT';
}
if ($field['primary'] ?? false) {
$fieldQuery .= ' PRIMARY KEY';
}
if ($field['autoincrement'] ?? false) {
$fieldQuery .= ' AUTOINCREMENT';
}
$fieldQuery .= ',';
if ($field['unique'] ?? false) {

View File

@ -512,7 +512,9 @@ final class HttpRequest extends RequestAbstract
{
$useragent = $_SERVER['HTTP_USER_AGENT'] ?? '';
if (\preg_match('/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i', $useragent) || \preg_match('/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i', $useragent)) {
if (\preg_match('/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i', $useragent)
|| \preg_match('/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i', $useragent)
) {
return true; // @codeCoverageIgnore
}

View File

@ -1783,7 +1783,7 @@ class Email
$lookBack = 0;
do {
$offset = $avgLength - $lookBack;
$offset = (int) ($avgLength - $lookBack);
$chunk = \mb_substr($str, $i, $offset, $this->charset);
$chunk = \base64_encode($chunk);
++$lookBack;

View File

@ -526,6 +526,39 @@ abstract class ModuleAbstract
];
}
/**
* Create duplicate model create response.
*
* The response object contains the following data:
*
* * status = Response status
* * title = Response title (e.g. for frontend reporting)
* * message = Response message (e.g. for frontend reporting)
* * response = Response object (e.g. for validation/frontend reporting/form validation)
*
* @param RequestAbstract $request Request
* @param ResponseAbstract $response Response
* @param mixed $obj Response object
*
* @return void
*
* @since 1.0.0
*/
public function createDuplicateCreateResponse(
RequestAbstract $request,
ResponseAbstract $response,
mixed $obj
) : void
{
$response->header->set('Content-Type', MimeType::M_JSON . '; charset=utf-8', true);
$response->data[$request->uri->__toString()] = [
'status' => NotificationLevel::WARNING,
'title' => '',
'message' => $this->app->l11nManager->getText($response->header->l11n->language, '0', '0', 'DuplicateCreate'),
'response' => $obj,
];
}
/**
* Create invalid model update response.
*

View File

@ -117,11 +117,15 @@ final class Guard
public static function isSafeFile(string $path) : bool
{
if (!\str_ends_with($path, '.exe') && !self::isSafeNoneExecutable($path)) {
return false;
}
$tmp = \strtolower($path);
if (\str_ends_with($tmp, '.csv')) {
return self::isSafeXml($path);
} elseif (\str_ends_with($tmp, '.xml')) {
return self::isSafeCsv($path);
} elseif (\str_ends_with($tmp, '.xml')) {
return self::isSafeXml($path);
}
return true;
@ -175,13 +179,15 @@ final class Guard
return true;
}
static $specialChars = ['=', '+', '-', '@'];
while (($row = \fgetcsv($input)) !== false) {
foreach ($row as &$cell) {
if (\preg_match('/^[\x00-\x08\x0B\x0C\x0E-\x1F]+/', $cell) !== false) {
if (\preg_match('/^[\x00-\x08\x0B\x0C\x0E-\x1F]+/', $cell) === 1) {
return false;
}
if (\in_array($cell[0] ?? '', ['=', '+', '-', '@'])) {
if (\in_array($cell[0] ?? '', $specialChars)) {
return false;
}
}
@ -191,4 +197,32 @@ final class Guard
return true;
}
public static function isSafeNoneExecutable(string $path) : bool
{
$input = \fopen($path, 'r');
if (!$input) {
return true;
}
static $specialSignatures = [
"\x4D\x5A",
"\x7F\x45\x4C\x46",
];
$line = \fgets($input, 256);
\fclose($input);
if ($line === false) {
return true;
}
foreach ($specialSignatures as $sig) {
if (\mb_stripos($line, $sig) !== false) {
return false;
}
}
return true;
}
}

View File

@ -62,11 +62,24 @@ class Address extends Location
public function toArray() : array
{
return \array_merge(
parent::toArray(),
[
'id' => $this->id,
'name' => $this->name,
'fao' => $this->fao,
],
parent::toArray()
]
);
}
public static function fromJson(array $address) : self
{
$new = new self();
$new->from($address);
$new->id = $address['id'];
$new->name = $address['name'];
$new->fao = $address['fao'];
return $new;
}
}

View File

@ -372,6 +372,19 @@ class FloatInt implements SerializableInterface
$this->setInt((int) $value);
}
/**
* {@inheritdoc}
*/
public function jsonSerialize() : mixed
{
return \json_encode($this->getInt());
}
public static function fromJson(string $json) : self
{
return new self((int) $json);
}
/**
* Set money value.
*

View File

@ -157,6 +157,8 @@ class Location implements \JsonSerializable, SerializableInterface
public function toArray() : array
{
return [
'id' => $this->id,
'type' => $this->type,
'postal' => $this->postal,
'city' => $this->city,
'country' => $this->country,
@ -167,6 +169,19 @@ class Location implements \JsonSerializable, SerializableInterface
];
}
public function from(array $location) : void
{
$this->id = $location['id'];
$this->type = $location['type'];
$this->postal = $location['postal'];
$this->city = $location['city'];
$this->country = $location['country'];
$this->address = $location['address'];
$this->state = $location['state'];
$this->lat = $location['lat'];
$this->lon = $location['lon'];
}
/**
* {@inheritdoc}
*/

170
System/File/SearchUtils.php Normal file
View File

@ -0,0 +1,170 @@
<?php
/**
* Jingga
*
* PHP Version 8.2
*
* @package phpOMS\System\File
* @copyright Dennis Eichhorn
* @license OMS License 2.0
* @version 1.0.0
* @link https://jingga.app
*/
declare(strict_types=1);
namespace phpOMS\System\File;
/**
* Path exception class.
*
* @package phpOMS\System\File
* @license OMS License 2.0
* @link https://jingga.app
* @since 1.0.0
*/
final class SearchUtils
{
public static function findInFile(string $path, array $keywords, int $distance = 500) : array
{
$fp = \fopen($path, "r");
if ($fp === false) {
return [];
}
$positions = [];
$globalPos = 0;
while (!\feof($fp)) {
$line = \fgets($fp);
foreach ($keywords as $keyword) {
$pos = \stripos($line, $keyword);
if ($pos === false) {
continue;
}
while ($pos !== false) {
$positions[$keyword][] = $globalPos + $pos;
$pos = \stripos($line, $keyword, $pos + 1);
}
}
$globalPos += \strlen($line);
}
\fclose($fp);
if (empty($positions) || \count($keywords) !== \count($positions)) {
return [];
}
$start = \reset($keywords);
$distances = [];
foreach ($positions[$start] as $pos) {
if ($pos < 0) {
continue;
}
$distance = [
'start' => $pos,
'end' => $pos,
'distance' => 0,
];
foreach ($positions as $keyword => $found) {
$closestStart = null;
$closestEnd = null;
$inBetween = null;
foreach ($found as $pos2) {
if ($pos2 < 0) {
continue 2;
}
if ($pos2 >= $distance['start'] && $pos2 <= $distance['end']) {
$inBetween = $pos2;
break;
}
if ($closestStart === null
|| \abs($pos2 - $distance['start']) < \abs($closestStart - $distance['start'])
) {
$closestStart = $pos2;
}
if ($closestEnd === null
|| \abs($pos2 - $distance['end']) < \abs($closestEnd - $distance['end'])
) {
$closestEnd = $pos2;
}
}
// The following is only perfect for inBetween
// For the other cases there could be a scenario where the closer value is actually bad
// because for the next keyword the farther one would be inBetween
if ($inBetween !== null) {
continue; // Perfect
} elseif ($closestStart < $distance['start']
&& (\abs($closestStart - $distance['start']) <= \abs($closestEnd - $distance['end']) || $closestEnd > $distance['end'])) {
$distance['start'] = \min($distance['start'], $closestStart);
} else {
$distance['end'] = \max($distance['end'], $closestEnd);
}
}
$distance['distance'] = $distance['end'] - $distance['start'];
$distances[] = $distance;
}
if (empty($distances)) {
return [];
}
\uasort($distances, function (array $a, array $b) {
return $a['distance'] <=> $b['distance'];
});
return $distances;
}
public static function getTextExtract(string $path, int $pos, string $start, string $end) : string
{
$fp = \fopen($path, "r");
if ($fp === false) {
return [];
}
$startPos = -1;
while (($line = \fgets($fp)) !== false && \ftell($fp) < $pos) {
if (\stripos($line, $start) !== false) {
$startPos = \ftell($fp);
}
}
\fseek($fp, $pos);
$endPos = -1;
while (($line = \fgets($fp)) !== false) {
if (\stripos($line, $end) !== false) {
$endPos = \ftell($fp);
break;
}
}
if ($startPos < 0 || $endPos < 0) {
\fclose($fp);
return '';
}
\fseek($fp, $startPos);
$extract = \fread($fp, $endPos - $startPos);
\fclose($fp);
return $extract;
}
}

View File

@ -196,7 +196,7 @@ final class UriFactory
{
$success = false;
foreach (self::$uri as $key => $value) {
foreach (self::$uri as $key => $_) {
if (((bool) \preg_match('~^' . $pattern . '$~', $key))) {
unset(self::$uri[$key]);
$success = true;

View File

@ -16,6 +16,7 @@ namespace phpOMS\Utils\IO\Csv;
use phpOMS\DataStorage\Database\Connection\ConnectionAbstract;
use phpOMS\DataStorage\Database\Query\Builder;
use phpOMS\DataStorage\Database\Schema\Builder as SchemaBuilder;
use phpOMS\Utils\IO\IODatabaseMapper;
/**
@ -36,58 +37,60 @@ final class CsvDatabaseMapper implements IODatabaseMapper
*/
private ConnectionAbstract $con;
/**
* Path to source or destination
*
* @var string
* @since 1.0.0
*/
private string $path = '';
/**
* Constructor.
*
* @param ConnectionAbstract $con Database connection
* @param string $path File path
*
* @since 1.0.0
*/
public function __construct(ConnectionAbstract $con, string $path)
public function __construct(ConnectionAbstract $con)
{
$this->con = $con;
$this->path = $path;
$this->con = $con;
}
/**
* {@inheritdoc}
*/
public function insert() : void
public function createSchema(string $path, string $table = '') : void
{
$fp = \fopen($this->path, 'r');
$fp = \fopen($path, 'r');
if ($fp === false) {
return;
}
$table = \basename($this->path, '.csv');
$table = \strtr(empty($table) ? \basename($path, '.csv') : $table, ' ', '_');
$titles = [];
$delim = CsvSettings::getFileDelimiter($fp, 2);
// get column titles
$titles = \fgetcsv($fp, 4096);
$titles = \fgetcsv($fp, 4096, $delim);
if ($titles === false) {
\fclose($fp);
return;
}
$columns = \count($titles);
if ($columns === 0) {
$titles = \array_map(function(string $title) : string {
$title = \strtr(\trim($title), ' ', '_');
$title = \preg_replace('/[^a-zA-Z0-9_]/', '', $title);
return \strtr($title, ' ', '_');
}, $titles);
$cells = \fgetcsv($fp, null, $delim);
if ($cells === false) {
\fclose($fp);
return;
}
// insert data
$query = new Builder($this->con);
$query->insert(...$titles)->into($table);
$query = new SchemaBuilder($this->con);
$query->createTable($table);
while (($cells = \fgetcsv($fp)) !== false) {
$query->values(...$cells);
foreach ($cells as $idx => $cell) {
$datatype = SchemaBuilder::getTypeFromVariable($cell);
$query->field($titles[$idx], $datatype);
}
$query->execute();
@ -98,9 +101,78 @@ final class CsvDatabaseMapper implements IODatabaseMapper
/**
* {@inheritdoc}
*/
public function select(array $queries) : void
public function import(string $path, string $table = '', ?\Closure $transform = null) : void
{
$fp = \fopen($this->path, 'r+');
$fp = \fopen($path, 'r');
if ($fp === false) {
return;
}
$table = \strtr(empty($table) ? \basename($path, '.csv') : $table, ' ', '_');
$titles = [];
$delim = CsvSettings::getFileDelimiter($fp, 2);
// get column titles
$titles = \fgetcsv($fp, 4096, $delim);
if ($titles === false) {
\fclose($fp);
return;
}
$columns = \count($titles);
if ($columns === 0) {
\fclose($fp);
return;
}
$titles = \array_map(function(string $title) : string {
$title = \strtr(\trim($title), ' ', '_');
$title = \preg_replace('/[^a-zA-Z0-9_]/', '', $title);
return \strtr($title, ' ', '_');
}, $titles);
$titleCount = \count($titles);
do {
$counter = 0;
// insert data
$query = new Builder($this->con);
$query->insert(...$titles)->into($table);
while (($cells = \fgetcsv($fp, null, $delim)) !== false) {
if (\count($cells) !== $titleCount) {
continue;
}
if ($transform !== null) {
foreach ($cells as $idx => $cell) {
$cells[$idx] = $transform($titles[$idx], $cell);
}
}
$query->values(...$cells);
++$counter;
if ($counter > 250) {
break;
}
}
$query->execute();
} while ($cells !== false);
\fclose($fp);
}
/**
* {@inheritdoc}
*/
public function export(string $path, array $queries) : void
{
$fp = \fopen($path, 'r+');
if ($fp === false) {
return;
}
@ -112,6 +184,7 @@ final class CsvDatabaseMapper implements IODatabaseMapper
}
if ($key > 0) {
\fclose($fp);
return;
}
@ -140,27 +213,36 @@ final class CsvDatabaseMapper implements IODatabaseMapper
/**
* {@inheritdoc}
*/
public function update() : void
public function update(string $path, string $table = '') : void
{
$fp = \fopen($this->path, 'r+');
$fp = \fopen($path, 'r+');
if ($fp === false) {
return;
}
$table = \basename($this->path, '.csv');
$table = \strtr(empty($table) ? \basename($path, '.csv') : $table, ' ', '_');
$titles = [];
// get column titles
$titles = \fgetcsv($fp, 4096);
if ($titles === false) {
\fclose($fp);
return;
}
$columns = \count($titles);
if ($columns === 0) {
\fclose($fp);
return;
}
$titles = \array_map(function(string $title) : string {
$title = \strtr(\trim($title), ' ', '_');
$title = \preg_replace('/[^a-zA-Z0-9_]/', '', $title);
return \strtr($title, ' ', '_');
}, $titles);
$idCol = (string) \array_shift($titles);
// update data

View File

@ -27,29 +27,49 @@ interface IODatabaseMapper
/**
* Insert data from excel sheet into database
*
* @param string $path File path
* @param string $table Table name (empty = sheet name)
* @param null|\Closure $transform Transform data before import
*
* @return void
*
* @since 1.0.0
*/
public function insert() : void;
public function import(string $path, string $table = '', ?\Closure $transform = null) : void;
/**
* Select data from database and store in excel sheet
*
* @param string $path Output path
* @param \phpOMS\DataStorage\Database\Query\Builder[] $queries Queries to execute
*
* @return void
*
* @since 1.0.0
*/
public function select(array $queries) : void;
public function export(string $path, array $queries) : void;
/**
* Update data from excel sheet into database
*
* @param string $path File path
* @param string $table Table name (empty = sheet name)
*
* @return void
*
* @since 1.0.0
*/
public function update() : void;
public function update(string $path, string $table = '') : void;
/**
* Create database schema
*
* @param string $path File path
* @param string $table Table name (empty = sheet name)
*
* @return void
*
* @since 1.0.0
*/
public function createSchema(string $path, string $table = '') : void;
}

View File

@ -16,6 +16,7 @@ namespace phpOMS\Utils\IO\Spreadsheet;
use phpOMS\DataStorage\Database\Connection\ConnectionAbstract;
use phpOMS\DataStorage\Database\Query\Builder;
use phpOMS\DataStorage\Database\Schema\Builder as SchemaBuilder;
use phpOMS\Utils\IO\IODatabaseMapper;
use phpOMS\Utils\StringUtils;
@ -37,57 +38,50 @@ final class SpreadsheetDatabaseMapper implements IODatabaseMapper
*/
private ConnectionAbstract $con;
/**
* Path to source or destination
*
* @var string
* @since 1.0.0
*/
private string $path = '';
/**
* Constructor.
*
* @param ConnectionAbstract $con Database connection
* @param string $path File path
*
* @since 1.0.0
*/
public function __construct(ConnectionAbstract $con, string $path)
public function __construct(ConnectionAbstract $con)
{
$this->con = $con;
$this->path = $path;
}
/**
* {@inheritdoc}
*/
public function insert() : void
public function createSchema(string $path, string $table = '') : void
{
$reader = null;
if (StringUtils::endsWith($this->path, '.xlsx')) {
if (StringUtils::endsWith($path, '.xlsx')) {
$reader = new \PhpOffice\PhpSpreadsheet\Reader\Xlsx();
} elseif (StringUtils::endsWith($this->path, '.ods')) {
} elseif (StringUtils::endsWith($path, '.ods')) {
$reader = new \PhpOffice\PhpSpreadsheet\Reader\Ods();
} else {
$reader = new \PhpOffice\PhpSpreadsheet\Reader\Xls();
}
$reader->setReadDataOnly(true);
$sheet = $reader->load($this->path);
$sheet = $reader->load($path);
$tables = $sheet->getSheetCount();
for ($i = 0; $i < $tables; ++$i) {
$sheet->setActiveSheetIndex($i);
$workSheet = $sheet->getSheet($i);
$table = $workSheet->getTitle();
$table = \strtr(empty($table) ? $workSheet->getTitle() : $table, ' ', '_');
$titles = [];
// get column titles
$column = 1;
while (!empty($value = $workSheet->getCell(StringUtils::intToAlphabet($column) . 1)->getCalculatedValue())) {
$value = \strtr(\trim($value), ' ', '_');
$value = \preg_replace('/[^a-zA-Z0-9_]/', '', $value);
$titles[] = $value;
++$column;
}
@ -96,20 +90,22 @@ final class SpreadsheetDatabaseMapper implements IODatabaseMapper
continue;
}
// insert data
$query = new Builder($this->con);
$query->insert(...$titles)->into($table);
$query = new SchemaBuilder($this->con);
$query->createTable($table);
$line = 2;
while (!empty($workSheet->getCell('A' . $line)->getCalculatedValue())) {
$cells = [];
for ($j = 1; $j <= $columns; ++$j) {
$cells[] = $workSheet->getCell(StringUtils::intToAlphabet($j) . $line)->getCalculatedValue();
}
if (empty($workSheet->getCell('A' . $line)->getCalculatedValue())) {
continue;
}
++$line;
for ($j = 1; $j <= $columns; ++$j) {
$cells[] = $workSheet->getCell(StringUtils::intToAlphabet($j) . $line)->getCalculatedValue();
}
$query->values(...$cells);
foreach ($cells as $idx => $cell) {
$datatype = SchemaBuilder::getTypeFromVariable($cell);
$query->field($titles[$idx], $datatype);
}
$query->execute();
@ -119,7 +115,83 @@ final class SpreadsheetDatabaseMapper implements IODatabaseMapper
/**
* {@inheritdoc}
*/
public function select(array $queries) : void
public function import(string $path, string $table = '', ?\Closure $transform = null) : void
{
$reader = null;
if (StringUtils::endsWith($path, '.xlsx')) {
$reader = new \PhpOffice\PhpSpreadsheet\Reader\Xlsx();
} elseif (StringUtils::endsWith($path, '.ods')) {
$reader = new \PhpOffice\PhpSpreadsheet\Reader\Ods();
} else {
$reader = new \PhpOffice\PhpSpreadsheet\Reader\Xls();
}
$reader->setReadDataOnly(true);
$sheet = $reader->load($path);
$tables = $sheet->getSheetCount();
for ($i = 0; $i < $tables; ++$i) {
$sheet->setActiveSheetIndex($i);
$workSheet = $sheet->getSheet($i);
$table = \strtr(empty($table) ? $workSheet->getTitle() : $table, ' ', '_');
$titles = [];
// get column titles
$column = 1;
while (!empty($value = $workSheet->getCell(StringUtils::intToAlphabet($column) . 1)->getCalculatedValue())) {
$value = \strtr(\trim($value), ' ', '_');
$value = \preg_replace('/[^a-zA-Z0-9_]/', '', $value);
$titles[] = $value;
++$column;
}
$columns = \count($titles);
if ($columns === 0) {
continue;
}
$line = 2;
do {
$counter = 0;
// insert data
$query = new Builder($this->con);
$query->insert(...$titles)->into($table);
while ($hasData = !empty($workSheet->getCell('A' . $line)->getCalculatedValue())) {
$cells = [];
for ($j = 1; $j <= $columns; ++$j) {
$cells[] = $workSheet->getCell(StringUtils::intToAlphabet($j) . $line)->getCalculatedValue();
}
if ($transform !== null) {
foreach ($cells as $idx => $cell) {
$cells[$idx] = $transform($titles[$idx], $cell);
}
}
++$line;
$query->values(...$cells);
++$counter;
if ($counter > 250) {
break;
}
}
$query->execute();
} while ($hasData);
}
}
/**
* {@inheritdoc}
*/
public function export(string $path, array $queries) : void
{
$sheet = new \PhpOffice\PhpSpreadsheet\Spreadsheet();
$sheet->getProperties()
@ -169,44 +241,47 @@ final class SpreadsheetDatabaseMapper implements IODatabaseMapper
}
}
if (StringUtils::endsWith($this->path, '.xlsx')) {
(new \PhpOffice\PhpSpreadsheet\Writer\Xlsx($sheet))->save($this->path);
} elseif (StringUtils::endsWith($this->path, '.ods')) {
(new \PhpOffice\PhpSpreadsheet\Writer\Ods($sheet))->save($this->path);
if (StringUtils::endsWith($path, '.xlsx')) {
(new \PhpOffice\PhpSpreadsheet\Writer\Xlsx($sheet))->save($path);
} elseif (StringUtils::endsWith($path, '.ods')) {
(new \PhpOffice\PhpSpreadsheet\Writer\Ods($sheet))->save($path);
} else {
(new \PhpOffice\PhpSpreadsheet\Writer\Xls($sheet))->save($this->path);
(new \PhpOffice\PhpSpreadsheet\Writer\Xls($sheet))->save($path);
}
}
/**
* {@inheritdoc}
*/
public function update() : void
public function update(string $path, string $table = '') : void
{
$reader = null;
if (StringUtils::endsWith($this->path, '.xlsx')) {
if (StringUtils::endsWith($path, '.xlsx')) {
$reader = new \PhpOffice\PhpSpreadsheet\Reader\Xlsx();
} elseif (StringUtils::endsWith($this->path, '.ods')) {
} elseif (StringUtils::endsWith($path, '.ods')) {
$reader = new \PhpOffice\PhpSpreadsheet\Reader\Ods();
} else {
$reader = new \PhpOffice\PhpSpreadsheet\Reader\Xls();
}
$reader->setReadDataOnly(true);
$sheet = $reader->load($this->path);
$sheet = $reader->load($path);
$tables = $sheet->getSheetCount();
for ($i = 0; $i < $tables; ++$i) {
$sheet->setActiveSheetIndex($i);
$workSheet = $sheet->getSheet($i);
$table = $workSheet->getTitle();
$table = \strtr(empty($table) ? $workSheet->getTitle() : $table, ' ', '_');
$titles = [];
// get column titles
$column = 1;
while (!empty($value = $workSheet->getCell(StringUtils::intToAlphabet($column) . 1)->getCalculatedValue())) {
$value = \strtr(\trim($value), ' ', '_');
$value = \preg_replace('/[^a-zA-Z0-9_]/', '', $value);
$titles[] = $value;
++$column;
}

View File

@ -3510,7 +3510,7 @@ class Markdown
*
* @since 1.0.0
*/
protected function insertAbreviation(array $element) : array
protected function insertAbbreviation(array $element) : array
{
if (!isset($element['text'])) {
return $element;
@ -3570,7 +3570,7 @@ class Markdown
$this->currentMeaning = $meaning;
$inline['element'] = $this->elementApplyRecursiveDepthFirst(
'insertAbreviation',
'insertAbbreviation',
$inline['element']
);
}
@ -3727,7 +3727,8 @@ class Markdown
$dom = new \DOMDocument();
// http://stackoverflow.com/q/11309194/200145
$elementMarkup = \mb_convert_encoding($elementMarkup, 'HTML-ENTITIES', 'UTF-8');
// $elementMarkup = \mb_convert_encoding($elementMarkup, 'HTML-ENTITIES', 'UTF-8'); // Deprecated
$elementMarkup = \mb_encode_numericentity($elementMarkup, [0x80, 0x10FFFF, 0, ~0], 'UTF-8' );
// http://stackoverflow.com/q/4879946/200145
$dom->loadHTML($elementMarkup);
@ -4494,31 +4495,6 @@ class Markdown
return $element;
}
/**
* Handle element recursively
*
* @param string|\Closure $closure Closure for handling element
* @param array $element Element to handle
*
* @return array
*
* @since 1.0.0
*/
protected function elementApplyRecursive(string|\Closure $closure, array $element) : array
{
$element = \is_string($closure) ? $this->{$closure}($element) : $closure($element);
if (isset($element['elements'])) {
foreach ($element['elements'] as &$e) {
$e = $this->elementApplyRecursive($closure, $e);
}
} elseif (isset($element['element'])) {
$element['element'] = $this->elementApplyRecursive($closure, $element['element']);
}
return $element;
}
/**
* Handle element recursively
*

View File

@ -14,6 +14,8 @@ declare(strict_types=1);
namespace phpOMS\tests\DataStorage\Database\Query;
include_once __DIR__ . '/../../../Autoloader.php';
use phpOMS\DataStorage\Database\Connection\MysqlConnection;
use phpOMS\DataStorage\Database\Connection\PostgresConnection;
use phpOMS\DataStorage\Database\Connection\SQLiteConnection;
@ -29,6 +31,10 @@ final class BuilderTest extends \PHPUnit\Framework\TestCase
{
public static function dbConnectionProvider() : array
{
if (!isset($GLOBALS['CONFIG'])) {
$GLOBALS['CONFIG'] = include __DIR__ . '/../../../config.php';
}
$cons = [
[new MysqlConnection($GLOBALS['CONFIG']['db']['core']['masters']['admin'])],
[new PostgresConnection($GLOBALS['CONFIG']['db']['core']['postgresql']['admin'])],
@ -59,26 +65,31 @@ final class BuilderTest extends \PHPUnit\Framework\TestCase
$iE = $con->getGrammar()->systemIdentifierEnd;
$query = new Builder($con);
$query->usePreparedStmt = false;
$sql = 'SELECT [a].[test] FROM [a] WHERE [a].[test] = 1;';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->select('a.test')->from('a')->where('a.test', '=', 1)->toSql());
$query = new Builder($con);
$query->usePreparedStmt = false;
$sql = 'SELECT [a].[test] AS t FROM [a] AS b WHERE [a].[test] = 1;';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->selectAs('a.test', 't')->fromAs('a', 'b')->where('a.test', '=', 1)->toSql());
$query = new Builder($con);
$query->usePreparedStmt = false;
$sql = 'SELECT DISTINCT [a].[test] FROM [a] WHERE [a].[test] = 1;';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->select('a.test')->distinct()->from('a')->where('a.test', '=', 1)->toSql());
$query = new Builder($con);
$query->usePreparedStmt = false;
$sql = 'SELECT [a].[test], [b].[test] FROM [a], [b] WHERE [a].[test] = \'abc\';';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->select('a.test', 'b.test')->from('a', 'b')->where('a.test', '=', 'abc')->toSql());
$query = new Builder($con);
$query->usePreparedStmt = false;
$datetime = new \DateTime('now');
$sql = 'SELECT [a].[test], [b].[test] FROM [a], [b] WHERE [a].[test] = \'' . $datetime->format('Y-m-d H:i:s')
. '\';';
@ -86,6 +97,7 @@ final class BuilderTest extends \PHPUnit\Framework\TestCase
self::assertEquals($sql, $query->select('a.test', 'b.test')->from('a', 'b')->where('a.test', '=', $datetime)->toSql());
$query = new Builder($con);
$query->usePreparedStmt = false;
$sql = 'SELECT [a].[test], [b].[test] FROM [a], [b] WHERE [a].[test] = \'abc\' ORDER BY [a].[test] ASC, [b].[test] DESC;';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql,
@ -97,6 +109,7 @@ final class BuilderTest extends \PHPUnit\Framework\TestCase
);
$query = new Builder($con);
$query->usePreparedStmt = false;
$sql = 'SELECT [a].[test], [b].[test] FROM [a], [b] WHERE [a].[test] = :abcValue ORDER BY [a].[test] ASC, [b].[test] DESC;';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql,
@ -118,6 +131,7 @@ final class BuilderTest extends \PHPUnit\Framework\TestCase
$iE = $con->getGrammar()->systemIdentifierEnd;
$query = new Builder($con);
$query->usePreparedStmt = false;
$sql = 'SELECT [a].[test] FROM [a] AS b WHERE [a].[test] = 1 ORDER BY RAND() LIMIT 1;';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->random('a.test')->fromAs('a', 'b')->where('a.test', '=', 1)->toSql());
@ -131,6 +145,7 @@ final class BuilderTest extends \PHPUnit\Framework\TestCase
$iE = $con->getGrammar()->systemIdentifierEnd;
$query = new Builder($con);
$query->usePreparedStmt = false;
$sql = 'SELECT [a].[test] FROM [a] AS b ORDER BY RANDOM() LIMIT 1;';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->random('a.test')->fromAs('a', 'b')->where('a.test', '=', 1)->toSql());
@ -144,6 +159,7 @@ final class BuilderTest extends \PHPUnit\Framework\TestCase
$iE = $con->getGrammar()->systemIdentifierEnd;
$query = new Builder($con);
$query->usePreparedStmt = false;
$sql = 'SELECT [a].[test] FROM [a] AS b ORDER BY RANDOM() LIMIT 1;';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->random('a.test')->fromAs('a', 'b')->where('a.test', '=', 1)->toSql());
@ -157,6 +173,7 @@ final class BuilderTest extends \PHPUnit\Framework\TestCase
$iE = $con->getGrammar()->systemIdentifierEnd;
$query = new Builder($con);
$query->usePreparedStmt = false;
$sql = 'SELECT TOP 1 [a].[test] FROM [a] AS b ORDER BY IDX FETCH FIRST 1 ROWS ONLY;';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->random('a.test')->fromAs('a', 'b')->where('a.test', '=', 1)->toSql());
@ -177,31 +194,37 @@ final class BuilderTest extends \PHPUnit\Framework\TestCase
$iE = $con->getGrammar()->systemIdentifierEnd;
$query = new Builder($con);
$query->usePreparedStmt = false;
$sql = 'SELECT [a].[test] FROM [a] WHERE [a].[test] = 1 ORDER BY [a].[test] DESC;';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->select('a.test')->from('a')->where('a.test', '=', 1)->newest('a.test')->toSql());
$query = new Builder($con);
$query->usePreparedStmt = false;
$sql = 'SELECT [a].[test] FROM [a] WHERE [a].[test] = 1 ORDER BY [a].[test] ASC;';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->select('a.test')->from('a')->where('a.test', '=', 1)->oldest('a.test')->toSql());
$query = new Builder($con);
$query->usePreparedStmt = false;
$sql = 'SELECT [a].[test] FROM [a] WHERE [a].[test] = 1 ORDER BY [a].[test] DESC;';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->select('a.test')->from('a')->where('a.test', '=', 1)->orderBy('a.test', 'DESC')->toSql());
$query = new Builder($con);
$query->usePreparedStmt = false;
$sql = 'SELECT [a].[test] FROM [a] WHERE [a].[test] = 1 ORDER BY [a].[test] ASC;';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->select('a.test')->from('a')->where('a.test', '=', 1)->orderBy('a.test', 'ASC')->toSql());
$query = new Builder($con);
$query->usePreparedStmt = false;
$sql = 'SELECT [a].[test] FROM [a] WHERE [a].[test] = 1 ORDER BY [a].[test] DESC, [a].[test2] DESC;';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->select('a.test')->from('a')->where('a.test', '=', 1)->orderBy(['a.test', 'a.test2'], ['DESC', 'DESC'])->toSql());
$query = new Builder($con);
$query->usePreparedStmt = false;
$sql = 'SELECT [a].[test] FROM [a] WHERE [a].[test] = 1 ORDER BY [a].[test] ASC, [a].[test2] ASC;';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->select('a.test')->from('a')->where('a.test', '=', 1)->orderBy(['a.test', 'a.test2'], 'ASC')->toSql());
@ -222,11 +245,13 @@ final class BuilderTest extends \PHPUnit\Framework\TestCase
$iE = $con->getGrammar()->systemIdentifierEnd;
$query = new Builder($con);
$query->usePreparedStmt = false;
$sql = 'SELECT [a].[test] FROM [a] WHERE [a].[test] = 1 LIMIT 3;';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->select('a.test')->from('a')->where('a.test', '=', 1)->limit(3)->toSql());
$query = new Builder($con);
$query->usePreparedStmt = false;
$sql = 'SELECT [a].[test] FROM [a] WHERE [a].[test] = 1 OFFSET 3;';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->select('a.test')->from('a')->where('a.test', '=', 1)->offset(3)->toSql());
@ -247,19 +272,23 @@ final class BuilderTest extends \PHPUnit\Framework\TestCase
$iE = $con->getGrammar()->systemIdentifierEnd;
$query = new Builder($con);
$query->usePreparedStmt = false;
$sql = 'SELECT [a].[test] FROM [a] WHERE [a].[test] = 1 GROUP BY [a];';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->select('a.test')->from('a')->where('a.test', '=', 1)->groupBy('a')->toSql());
$query = new Builder($con);
$query->usePreparedStmt = false;
$sql = 'SELECT [a].[test] FROM [a] WHERE [a].[test] = 1 GROUP BY [a], [b];';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->select('a.test')->from('a')->where('a.test', '=', 1)->groupBy('a')->groupBy('b')->toSql());
$query = new Builder($con);
$query->usePreparedStmt = false;
self::assertEquals($sql, $query->select('a.test')->from('a')->where('a.test', '=', 1)->groupBy('a', 'b')->toSql());
$query = new Builder($con);
$query->usePreparedStmt = false;
$sql = 'SELECT [a].[test] FROM [a] WHERE [a].[test] = :test GROUP BY [a], [b];';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->select('a.test')->from('a')->where('a.test', '=', new Parameter('test'))->groupBy('a', 'b')->toSql());
@ -280,71 +309,85 @@ final class BuilderTest extends \PHPUnit\Framework\TestCase
$iE = $con->getGrammar()->systemIdentifierEnd;
$query = new Builder($con);
$query->usePreparedStmt = false;
$sql = 'SELECT [a].[test] FROM [a] WHERE [a].[test] = 1;';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->select('a.test')->from('a')->where('a.test', '=', 1)->toSql());
$query = new Builder($con);
$query->usePreparedStmt = false;
$sql = 'SELECT [a].[test] FROM [a] WHERE [a].[test] = 0;';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->select('a.test')->from('a')->where('a.test', '=', false)->toSql());
$query = new Builder($con);
$query->usePreparedStmt = false;
$sql = 'SELECT [a].[test] FROM [a] WHERE [a].[test] = 1;';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->select('a.test')->from('a')->where('a.test', '=', true)->toSql());
$query = new Builder($con);
$query->usePreparedStmt = false;
$sql = 'SELECT [a].[test] FROM [a] WHERE [a].[test] = \'string\';';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->select('a.test')->from('a')->where('a.test', '=', 'string')->toSql());
$query = new Builder($con);
$query->usePreparedStmt = false;
$sql = 'SELECT [a].[test] FROM [a] WHERE [a].[test] = 1.23;';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->select('a.test')->from('a')->where('a.test', '=', 1.23)->toSql());
$query = new Builder($con);
$query->usePreparedStmt = false;
$sql = 'SELECT [a].[test] FROM [a] WHERE [a].[test] = 1 AND [a].[test2] = 2;';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->select('a.test')->from('a')->where('a.test', '=', 1)->where('a.test2', '=', 2, 'and')->toSql());
$query = new Builder($con);
$query->usePreparedStmt = false;
$sql = 'SELECT [a].[test] FROM [a] WHERE [a].[test] = 1 AND [a].[test2] = 2;';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->select('a.test')->from('a')->where('a.test', '=', 1)->andWhere('a.test2', '=', 2)->toSql());
$query = new Builder($con);
$query->usePreparedStmt = false;
$sql = 'SELECT [a].[test] FROM [a] WHERE [a].[test] = 1 OR [a].[test2] = 2;';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->select('a.test')->from('a')->where('a.test', '=', 1)->where('a.test2', '=', 2, 'or')->toSql());
$query = new Builder($con);
$query->usePreparedStmt = false;
$sql = 'SELECT [a].[test] FROM [a] WHERE [a].[test] = 1 OR [a].[test2] = 2;';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->select('a.test')->from('a')->where('a.test', '=', 1)->orWhere('a.test2', '=', 2)->toSql());
$query = new Builder($con);
$query->usePreparedStmt = false;
$sql = 'SELECT [a].[test] FROM [a] WHERE [a].[test] = 1 OR [a].[test2] IS NULL;';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->select('a.test')->from('a')->where('a.test', '=', 1)->whereNull('a.test2', 'or')->toSql());
$query = new Builder($con);
$query->usePreparedStmt = false;
$sql = 'SELECT [a].[test] FROM [a] WHERE [a].[test] = 1 OR [a].[test2] IS NOT NULL;';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->select('a.test')->from('a')->where('a.test', '=', 1)->whereNotNull('a.test2', 'or')->toSql());
$query = new Builder($con);
$query->usePreparedStmt = false;
$sql = 'SELECT [a].[test] FROM [a] WHERE [a].[test] = 1 OR [a].[test2] IN (1, 2, 3);';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->select('a.test')->from('a')->where('a.test', '=', 1)->whereIn('a.test2', [1, 2, 3], 'or')->toSql());
$query = new Builder($con);
$query->usePreparedStmt = false;
$sql = 'SELECT [a].[test] FROM [a] WHERE [a].[test] = 1 OR [a].[test2] IN (\'a\', \'b\', \'c\');';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->select('a.test')->from('a')->where('a.test', '=', 1)->whereIn('a.test2', ['a', 'b', 'c'], 'or')->toSql());
$query = new Builder($con);
$query->usePreparedStmt = false;
$sql = 'SELECT [a].[test] FROM [a] WHERE [a].[test] = :testWhere OR [a].[test2] IN (\'a\', :bValue, \'c\');';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->select('a.test')->from('a')->where('a.test', '=', new Parameter('testWhere'))->whereIn('a.test2', ['a', new Parameter('bValue'), 'c'], 'or')->toSql());
@ -365,71 +408,85 @@ final class BuilderTest extends \PHPUnit\Framework\TestCase
$iE = $con->getGrammar()->systemIdentifierEnd;
$query = new Builder($con);
$query->usePreparedStmt = false;
$sql = 'SELECT [a].[test] FROM [a] JOIN [b] ON [a].[id] = [b].[id] WHERE [a].[test] = 1;';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->select('a.test')->from('a')->join('b')->on('a.id', '=', 'b.id')->where('a.test', '=', 1)->toSql());
$query = new Builder($con);
$query->usePreparedStmt = false;
$sql = 'SELECT [a].[test] FROM [a] JOIN [b] ON [a].[id] = [b].[id] OR [a].[id2] = [b].[id2] WHERE [a].[test] = 1;';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->select('a.test')->from('a')->join('b')->on('a.id', '=', 'b.id')->orOn('a.id2', '=', 'b.id2')->where('a.test', '=', 1)->toSql());
$query = new Builder($con);
$query->usePreparedStmt = false;
$sql = 'SELECT [a].[test] FROM [a] JOIN [b] ON [a].[id] = [b].[id] AND [a].[id2] = [b].[id2] WHERE [a].[test] = 1;';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->select('a.test')->from('a')->join('b')->on('a.id', '=', 'b.id')->andOn('a.id2', '=', 'b.id2')->where('a.test', '=', 1)->toSql());
$query = new Builder($con);
$query->usePreparedStmt = false;
$sql = 'SELECT [a].[test] FROM [a] LEFT JOIN [b] ON [a].[id] = [b].[id] WHERE [a].[test] = 1;';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->select('a.test')->from('a')->leftJoin('b')->on('a.id', '=', 'b.id')->where('a.test', '=', 1)->toSql());
$query = new Builder($con);
$query->usePreparedStmt = false;
$sql = 'SELECT [a].[test] FROM [a] LEFT OUTER JOIN [b] ON [a].[id] = [b].[id] WHERE [a].[test] = 1;';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->select('a.test')->from('a')->leftOuterJoin('b')->on('a.id', '=', 'b.id')->where('a.test', '=', 1)->toSql());
$query = new Builder($con);
$query->usePreparedStmt = false;
$sql = 'SELECT [a].[test] FROM [a] LEFT INNER JOIN [b] ON [a].[id] = [b].[id] WHERE [a].[test] = 1;';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->select('a.test')->from('a')->leftInnerJoin('b')->on('a.id', '=', 'b.id')->where('a.test', '=', 1)->toSql());
$query = new Builder($con);
$query->usePreparedStmt = false;
$sql = 'SELECT [a].[test] FROM [a] RIGHT JOIN [b] ON [a].[id] = [b].[id] WHERE [a].[test] = 1;';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->select('a.test')->from('a')->rightJoin('b')->on('a.id', '=', 'b.id')->where('a.test', '=', 1)->toSql());
$query = new Builder($con);
$query->usePreparedStmt = false;
$sql = 'SELECT [a].[test] FROM [a] RIGHT OUTER JOIN [b] ON [a].[id] = [b].[id] WHERE [a].[test] = 1;';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->select('a.test')->from('a')->rightOuterJoin('b')->on('a.id', '=', 'b.id')->where('a.test', '=', 1)->toSql());
$query = new Builder($con);
$query->usePreparedStmt = false;
$sql = 'SELECT [a].[test] FROM [a] RIGHT INNER JOIN [b] ON [a].[id] = [b].[id] WHERE [a].[test] = 1;';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->select('a.test')->from('a')->rightInnerJoin('b')->on('a.id', '=', 'b.id')->where('a.test', '=', 1)->toSql());
$query = new Builder($con);
$query->usePreparedStmt = false;
$sql = 'SELECT [a].[test] FROM [a] OUTER JOIN [b] ON [a].[id] = [b].[id] WHERE [a].[test] = 1;';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->select('a.test')->from('a')->outerJoin('b')->on('a.id', '=', 'b.id')->where('a.test', '=', 1)->toSql());
$query = new Builder($con);
$query->usePreparedStmt = false;
$sql = 'SELECT [a].[test] FROM [a] INNER JOIN [b] ON [a].[id] = [b].[id] WHERE [a].[test] = 1;';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->select('a.test')->from('a')->innerJoin('b')->on('a.id', '=', 'b.id')->where('a.test', '=', 1)->toSql());
$query = new Builder($con);
$query->usePreparedStmt = false;
$sql = 'SELECT [a].[test] FROM [a] CROSS JOIN [b] ON [a].[id] = [b].[id] WHERE [a].[test] = 1;';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->select('a.test')->from('a')->crossJoin('b')->on('a.id', '=', 'b.id')->where('a.test', '=', 1)->toSql());
$query = new Builder($con);
$query->usePreparedStmt = false;
$sql = 'SELECT [a].[test] FROM [a] FULL JOIN [b] ON [a].[id] = [b].[id] WHERE [a].[test] = 1;';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->select('a.test')->from('a')->fullJoin('b')->on('a.id', '=', 'b.id')->where('a.test', '=', 1)->toSql());
$query = new Builder($con);
$query->usePreparedStmt = false;
$sql = 'SELECT [a].[test] FROM [a] FULL OUTER JOIN [b] ON [a].[id] = [b].[id] WHERE [a].[test] = 1;';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->select('a.test')->from('a')->fullOuterJoin('b')->on('a.id', '=', 'b.id')->where('a.test', '=', 1)->toSql());
@ -450,27 +507,32 @@ final class BuilderTest extends \PHPUnit\Framework\TestCase
$iE = $con->getGrammar()->systemIdentifierEnd;
$query = new Builder($con);
$query->usePreparedStmt = false;
$sql = 'INSERT INTO [a] VALUES (1, \'test\');';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->insert()->into('a')->values(1, 'test')->toSql());
$query = new Builder($con);
$query->usePreparedStmt = false;
$sql = 'INSERT INTO [a] VALUES (1, \'test\');';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->insert()->into('a')->value([1, 'test'])->toSql());
$query = new Builder($con);
$query->usePreparedStmt = false;
$sql = 'INSERT INTO [a] ([test], [test2]) VALUES (1, \'test\');';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->insert('test', 'test2')->into('a')->values(1, 'test')->toSql());
self::assertEquals([[1, 'test']], $query->getValues());
$query = new Builder($con);
$query->usePreparedStmt = false;
$sql = 'INSERT INTO [a] ([test], [test2]) VALUES (1, \'test\'), (2, \'test2\');';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->insert('test', 'test2')->into('a')->values(1, 'test')->values(2, 'test2')->toSql());
$query = new Builder($con);
$query->usePreparedStmt = false;
$sql = 'INSERT INTO [a] ([test], [test2]) VALUES (:test, :test2);';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->insert('test', 'test2')->into('a')->values(new Parameter('test'), new Parameter('test2'))->toSql());
@ -491,11 +553,13 @@ final class BuilderTest extends \PHPUnit\Framework\TestCase
$iE = $con->getGrammar()->systemIdentifierEnd;
$query = new Builder($con);
$query->usePreparedStmt = false;
$sql = 'DELETE FROM [a] WHERE [a].[test] = 1;';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->delete()->from('a')->where('a.test', '=', 1)->toSql());
$query = new Builder($con);
$query->usePreparedStmt = false;
$sql = 'DELETE FROM [a] WHERE [a].[test] = :testVal;';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->delete()->from('a')->where('a.test', '=', new Parameter('testVal'))->toSql());
@ -516,21 +580,571 @@ final class BuilderTest extends \PHPUnit\Framework\TestCase
$iE = $con->getGrammar()->systemIdentifierEnd;
$query = new Builder($con);
$query->usePreparedStmt = false;
$sql = 'UPDATE [a] SET [test] = 1, [test2] = 2 WHERE [a].[test] = 1;';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->update('a')->set(['test' => 1])->set(['test2' => 2])->where('a.test', '=', 1)->toSql());
$query = new Builder($con);
$query->usePreparedStmt = false;
$sql = 'UPDATE [a] SET [test] = 1, [test2] = 2 WHERE [a].[test] = 1;';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->update('a')->sets('test', 1)->sets('test2', 2)->where('a.test', '=', 1)->toSql());
$query = new Builder($con);
$query->usePreparedStmt = false;
$sql = 'UPDATE [a] SET [test] = 1, [test2] = :test2 WHERE [a].[test] = :test3;';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->update('a')->set(['test' => 1])->set(['test2' => new Parameter('test2')])->where('a.test', '=', new Parameter('test3'))->toSql());
}
#[\PHPUnit\Framework\Attributes\DataProvider('dbConnectionProvider')]
#[\PHPUnit\Framework\Attributes\Group('framework')]
#[\PHPUnit\Framework\Attributes\TestDox('Mysql selects form a valid query')]
public function testSelectPrepared($con) : void
{
if (!$con->isInitialized()) {
self::markTestSkipped();
return;
}
$iS = $con->getGrammar()->systemIdentifierStart;
$iE = $con->getGrammar()->systemIdentifierEnd;
$query = new Builder($con);
$query->usePreparedStmt = true;
$sql = 'SELECT [a].[test] FROM [a] WHERE [a].[test] = ?;';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->select('a.test')->from('a')->where('a.test', '=', 1)->toSql());
$query = new Builder($con);
$query->usePreparedStmt = true;
$sql = 'SELECT [a].[test] AS t FROM [a] AS b WHERE [a].[test] = ?;';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->selectAs('a.test', 't')->fromAs('a', 'b')->where('a.test', '=', 1)->toSql());
$query = new Builder($con);
$query->usePreparedStmt = true;
$sql = 'SELECT DISTINCT [a].[test] FROM [a] WHERE [a].[test] = ?;';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->select('a.test')->distinct()->from('a')->where('a.test', '=', 1)->toSql());
$query = new Builder($con);
$query->usePreparedStmt = true;
$sql = 'SELECT [a].[test], [b].[test] FROM [a], [b] WHERE [a].[test] = ?;';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->select('a.test', 'b.test')->from('a', 'b')->where('a.test', '=', 'abc')->toSql());
$query = new Builder($con);
$query->usePreparedStmt = true;
$datetime = new \DateTime('now');
$sql = 'SELECT [a].[test], [b].[test] FROM [a], [b] WHERE [a].[test] = ?;';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->select('a.test', 'b.test')->from('a', 'b')->where('a.test', '=', $datetime)->toSql());
$query = new Builder($con);
$query->usePreparedStmt = true;
$sql = 'SELECT [a].[test], [b].[test] FROM [a], [b] WHERE [a].[test] = ? ORDER BY [a].[test] ASC, [b].[test] DESC;';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql,
$query->select('a.test', 'b.test')
->from('a', 'b')
->where('a.test', '=', 'abc')
->orderBy(['a.test', 'b.test', ], ['ASC', 'DESC', ])
->toSql()
);
$query = new Builder($con);
$query->usePreparedStmt = true;
$sql = 'SELECT [a].[test], [b].[test] FROM [a], [b] WHERE [a].[test] = :abcValue ORDER BY [a].[test] ASC, [b].[test] DESC;';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql,
$query->select('a.test', 'b.test')
->from('a', 'b')
->where('a.test', '=', new Parameter('abcValue'))
->orderBy(['a.test', 'b.test', ], ['ASC', 'DESC', ])
->toSql()
);
self::assertEquals($query->toSql(), $query->__toString());
}
public function testRandomMysqlPrepared() : void
{
$con = new MysqlConnection($GLOBALS['CONFIG']['db']['core']['masters']['admin']);
$iS = $con->getGrammar()->systemIdentifierStart;
$iE = $con->getGrammar()->systemIdentifierEnd;
$query = new Builder($con);
$query->usePreparedStmt = true;
$sql = 'SELECT [a].[test] FROM [a] AS b WHERE [a].[test] = ? ORDER BY RAND() LIMIT ?;';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->random('a.test')->fromAs('a', 'b')->where('a.test', '=', 1)->toSql());
}
public function testRandomPostgresqlPrepared() : void
{
$con = new PostgresConnection($GLOBALS['CONFIG']['db']['core']['postgresql']['admin']);
$iS = $con->getGrammar()->systemIdentifierStart;
$iE = $con->getGrammar()->systemIdentifierEnd;
$query = new Builder($con);
$query->usePreparedStmt = true;
$sql = 'SELECT [a].[test] FROM [a] AS b ORDER BY RANDOM() LIMIT ?;';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->random('a.test')->fromAs('a', 'b')->where('a.test', '=', 1)->toSql());
}
public function testRandomSQLitePrepared() : void
{
$con = new SQLiteConnection($GLOBALS['CONFIG']['db']['core']['sqlite']['admin']);
$iS = $con->getGrammar()->systemIdentifierStart;
$iE = $con->getGrammar()->systemIdentifierEnd;
$query = new Builder($con);
$query->usePreparedStmt = true;
$sql = 'SELECT [a].[test] FROM [a] AS b ORDER BY RANDOM() LIMIT ?;';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->random('a.test')->fromAs('a', 'b')->where('a.test', '=', 1)->toSql());
}
public function testRandomSqlServerPrepared() : void
{
$con = new SqlServerConnection($GLOBALS['CONFIG']['db']['core']['mssql']['admin']);
$iS = $con->getGrammar()->systemIdentifierStart;
$iE = $con->getGrammar()->systemIdentifierEnd;
$query = new Builder($con);
$query->usePreparedStmt = true;
$sql = 'SELECT TOP 1 [a].[test] FROM [a] AS b ORDER BY IDX FETCH FIRST 1 ROWS ONLY;';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->random('a.test')->fromAs('a', 'b')->where('a.test', '=', 1)->toSql());
}
#[\PHPUnit\Framework\Attributes\DataProvider('dbConnectionProvider')]
#[\PHPUnit\Framework\Attributes\Group('framework')]
#[\PHPUnit\Framework\Attributes\TestDox('Mysql orders form a valid query')]
public function testOrderPrepared($con) : void
{
if (!$con->isInitialized()) {
self::markTestSkipped();
return;
}
$iS = $con->getGrammar()->systemIdentifierStart;
$iE = $con->getGrammar()->systemIdentifierEnd;
$query = new Builder($con);
$query->usePreparedStmt = true;
$sql = 'SELECT [a].[test] FROM [a] WHERE [a].[test] = ? ORDER BY [a].[test] DESC;';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->select('a.test')->from('a')->where('a.test', '=', 1)->newest('a.test')->toSql());
$query = new Builder($con);
$query->usePreparedStmt = true;
$sql = 'SELECT [a].[test] FROM [a] WHERE [a].[test] = ? ORDER BY [a].[test] ASC;';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->select('a.test')->from('a')->where('a.test', '=', 1)->oldest('a.test')->toSql());
$query = new Builder($con);
$query->usePreparedStmt = true;
$sql = 'SELECT [a].[test] FROM [a] WHERE [a].[test] = ? ORDER BY [a].[test] DESC;';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->select('a.test')->from('a')->where('a.test', '=', 1)->orderBy('a.test', 'DESC')->toSql());
$query = new Builder($con);
$query->usePreparedStmt = true;
$sql = 'SELECT [a].[test] FROM [a] WHERE [a].[test] = ? ORDER BY [a].[test] ASC;';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->select('a.test')->from('a')->where('a.test', '=', 1)->orderBy('a.test', 'ASC')->toSql());
$query = new Builder($con);
$query->usePreparedStmt = true;
$sql = 'SELECT [a].[test] FROM [a] WHERE [a].[test] = ? ORDER BY [a].[test] DESC, [a].[test2] DESC;';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->select('a.test')->from('a')->where('a.test', '=', 1)->orderBy(['a.test', 'a.test2'], ['DESC', 'DESC'])->toSql());
$query = new Builder($con);
$query->usePreparedStmt = true;
$sql = 'SELECT [a].[test] FROM [a] WHERE [a].[test] = ? ORDER BY [a].[test] ASC, [a].[test2] ASC;';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->select('a.test')->from('a')->where('a.test', '=', 1)->orderBy(['a.test', 'a.test2'], 'ASC')->toSql());
}
#[\PHPUnit\Framework\Attributes\DataProvider('dbConnectionProvider')]
#[\PHPUnit\Framework\Attributes\Group('framework')]
#[\PHPUnit\Framework\Attributes\TestDox('Mysql offsets and limits form a valid query')]
public function testOffsetLimitPrepared($con) : void
{
if (!$con->isInitialized()) {
self::markTestSkipped();
return;
}
$iS = $con->getGrammar()->systemIdentifierStart;
$iE = $con->getGrammar()->systemIdentifierEnd;
$query = new Builder($con);
$query->usePreparedStmt = true;
$sql = 'SELECT [a].[test] FROM [a] WHERE [a].[test] = ? LIMIT ?;';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->select('a.test')->from('a')->where('a.test', '=', 1)->limit(3)->toSql());
$query = new Builder($con);
$query->usePreparedStmt = true;
$sql = 'SELECT [a].[test] FROM [a] WHERE [a].[test] = ? OFFSET ?;';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->select('a.test')->from('a')->where('a.test', '=', 1)->offset(3)->toSql());
}
#[\PHPUnit\Framework\Attributes\DataProvider('dbConnectionProvider')]
#[\PHPUnit\Framework\Attributes\Group('framework')]
#[\PHPUnit\Framework\Attributes\TestDox('Mysql groupings form a valid query')]
public function testGroupPrepared($con) : void
{
if (!$con->isInitialized()) {
self::markTestSkipped();
return;
}
$iS = $con->getGrammar()->systemIdentifierStart;
$iE = $con->getGrammar()->systemIdentifierEnd;
$query = new Builder($con);
$query->usePreparedStmt = true;
$sql = 'SELECT [a].[test] FROM [a] WHERE [a].[test] = ? GROUP BY [a];';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->select('a.test')->from('a')->where('a.test', '=', 1)->groupBy('a')->toSql());
$query = new Builder($con);
$query->usePreparedStmt = true;
$sql = 'SELECT [a].[test] FROM [a] WHERE [a].[test] = ? GROUP BY [a], [b];';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->select('a.test')->from('a')->where('a.test', '=', 1)->groupBy('a')->groupBy('b')->toSql());
$query = new Builder($con);
$query->usePreparedStmt = true;
self::assertEquals($sql, $query->select('a.test')->from('a')->where('a.test', '=', 1)->groupBy('a', 'b')->toSql());
$query = new Builder($con);
$query->usePreparedStmt = true;
$sql = 'SELECT [a].[test] FROM [a] WHERE [a].[test] = :test GROUP BY [a], [b];';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->select('a.test')->from('a')->where('a.test', '=', new Parameter('test'))->groupBy('a', 'b')->toSql());
}
#[\PHPUnit\Framework\Attributes\DataProvider('dbConnectionProvider')]
#[\PHPUnit\Framework\Attributes\Group('framework')]
#[\PHPUnit\Framework\Attributes\TestDox('Mysql wheres form a valid query')]
public function testWheresPrepared($con) : void
{
if (!$con->isInitialized()) {
self::markTestSkipped();
return;
}
$iS = $con->getGrammar()->systemIdentifierStart;
$iE = $con->getGrammar()->systemIdentifierEnd;
$query = new Builder($con);
$query->usePreparedStmt = true;
$sql = 'SELECT [a].[test] FROM [a] WHERE [a].[test] = ?;';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->select('a.test')->from('a')->where('a.test', '=', 1)->toSql());
$query = new Builder($con);
$query->usePreparedStmt = true;
$sql = 'SELECT [a].[test] FROM [a] WHERE [a].[test] = ?;';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->select('a.test')->from('a')->where('a.test', '=', false)->toSql());
$query = new Builder($con);
$query->usePreparedStmt = true;
$sql = 'SELECT [a].[test] FROM [a] WHERE [a].[test] = ?;';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->select('a.test')->from('a')->where('a.test', '=', true)->toSql());
$query = new Builder($con);
$query->usePreparedStmt = true;
$sql = 'SELECT [a].[test] FROM [a] WHERE [a].[test] = ?;';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->select('a.test')->from('a')->where('a.test', '=', 'string')->toSql());
$query = new Builder($con);
$query->usePreparedStmt = true;
$sql = 'SELECT [a].[test] FROM [a] WHERE [a].[test] = 1.23;';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->select('a.test')->from('a')->where('a.test', '=', 1.23)->toSql());
$query = new Builder($con);
$query->usePreparedStmt = true;
$sql = 'SELECT [a].[test] FROM [a] WHERE [a].[test] = ? AND [a].[test2] = ?;';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->select('a.test')->from('a')->where('a.test', '=', 1)->where('a.test2', '=', 2, 'and')->toSql());
$query = new Builder($con);
$query->usePreparedStmt = true;
$sql = 'SELECT [a].[test] FROM [a] WHERE [a].[test] = ? AND [a].[test2] = ?;';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->select('a.test')->from('a')->where('a.test', '=', 1)->andWhere('a.test2', '=', 2)->toSql());
$query = new Builder($con);
$query->usePreparedStmt = true;
$sql = 'SELECT [a].[test] FROM [a] WHERE [a].[test] = ? OR [a].[test2] = ?;';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->select('a.test')->from('a')->where('a.test', '=', 1)->where('a.test2', '=', 2, 'or')->toSql());
$query = new Builder($con);
$query->usePreparedStmt = true;
$sql = 'SELECT [a].[test] FROM [a] WHERE [a].[test] = ? OR [a].[test2] = ?;';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->select('a.test')->from('a')->where('a.test', '=', 1)->orWhere('a.test2', '=', 2)->toSql());
$query = new Builder($con);
$query->usePreparedStmt = true;
$sql = 'SELECT [a].[test] FROM [a] WHERE [a].[test] = ? OR [a].[test2] IS NULL;';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->select('a.test')->from('a')->where('a.test', '=', 1)->whereNull('a.test2', 'or')->toSql());
$query = new Builder($con);
$query->usePreparedStmt = true;
$sql = 'SELECT [a].[test] FROM [a] WHERE [a].[test] = ? OR [a].[test2] IS NOT NULL;';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->select('a.test')->from('a')->where('a.test', '=', 1)->whereNotNull('a.test2', 'or')->toSql());
$query = new Builder($con);
$query->usePreparedStmt = true;
$sql = 'SELECT [a].[test] FROM [a] WHERE [a].[test] = ? OR [a].[test2] IN (?, ?, ?);';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->select('a.test')->from('a')->where('a.test', '=', 1)->whereIn('a.test2', [1, 2, 3], 'or')->toSql());
$query = new Builder($con);
$query->usePreparedStmt = true;
$sql = 'SELECT [a].[test] FROM [a] WHERE [a].[test] = ? OR [a].[test2] IN (?, ?, ?);';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->select('a.test')->from('a')->where('a.test', '=', 1)->whereIn('a.test2', ['a', 'b', 'c'], 'or')->toSql());
$query = new Builder($con);
$query->usePreparedStmt = true;
$sql = 'SELECT [a].[test] FROM [a] WHERE [a].[test] = :testWhere OR [a].[test2] IN (?, :bValue, ?);';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->select('a.test')->from('a')->where('a.test', '=', new Parameter('testWhere'))->whereIn('a.test2', ['a', new Parameter('bValue'), 'c'], 'or')->toSql());
}
#[\PHPUnit\Framework\Attributes\DataProvider('dbConnectionProvider')]
#[\PHPUnit\Framework\Attributes\Group('framework')]
#[\PHPUnit\Framework\Attributes\TestDox('Mysql joins form a valid query')]
public function testJoinsPrepared($con) : void
{
if (!$con->isInitialized()) {
self::markTestSkipped();
return;
}
$iS = $con->getGrammar()->systemIdentifierStart;
$iE = $con->getGrammar()->systemIdentifierEnd;
$query = new Builder($con);
$query->usePreparedStmt = true;
$sql = 'SELECT [a].[test] FROM [a] JOIN [b] ON [a].[id] = [b].[id] WHERE [a].[test] = ?;';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->select('a.test')->from('a')->join('b')->on('a.id', '=', 'b.id')->where('a.test', '=', 1)->toSql());
$query = new Builder($con);
$query->usePreparedStmt = true;
$sql = 'SELECT [a].[test] FROM [a] JOIN [b] ON [a].[id] = [b].[id] OR [a].[id2] = [b].[id2] WHERE [a].[test] = ?;';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->select('a.test')->from('a')->join('b')->on('a.id', '=', 'b.id')->orOn('a.id2', '=', 'b.id2')->where('a.test', '=', 1)->toSql());
$query = new Builder($con);
$query->usePreparedStmt = true;
$sql = 'SELECT [a].[test] FROM [a] JOIN [b] ON [a].[id] = [b].[id] AND [a].[id2] = [b].[id2] WHERE [a].[test] = ?;';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->select('a.test')->from('a')->join('b')->on('a.id', '=', 'b.id')->andOn('a.id2', '=', 'b.id2')->where('a.test', '=', 1)->toSql());
$query = new Builder($con);
$query->usePreparedStmt = true;
$sql = 'SELECT [a].[test] FROM [a] LEFT JOIN [b] ON [a].[id] = [b].[id] WHERE [a].[test] = ?;';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->select('a.test')->from('a')->leftJoin('b')->on('a.id', '=', 'b.id')->where('a.test', '=', 1)->toSql());
$query = new Builder($con);
$query->usePreparedStmt = true;
$sql = 'SELECT [a].[test] FROM [a] LEFT OUTER JOIN [b] ON [a].[id] = [b].[id] WHERE [a].[test] = ?;';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->select('a.test')->from('a')->leftOuterJoin('b')->on('a.id', '=', 'b.id')->where('a.test', '=', 1)->toSql());
$query = new Builder($con);
$query->usePreparedStmt = true;
$sql = 'SELECT [a].[test] FROM [a] LEFT INNER JOIN [b] ON [a].[id] = [b].[id] WHERE [a].[test] = ?;';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->select('a.test')->from('a')->leftInnerJoin('b')->on('a.id', '=', 'b.id')->where('a.test', '=', 1)->toSql());
$query = new Builder($con);
$query->usePreparedStmt = true;
$sql = 'SELECT [a].[test] FROM [a] RIGHT JOIN [b] ON [a].[id] = [b].[id] WHERE [a].[test] = ?;';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->select('a.test')->from('a')->rightJoin('b')->on('a.id', '=', 'b.id')->where('a.test', '=', 1)->toSql());
$query = new Builder($con);
$query->usePreparedStmt = true;
$sql = 'SELECT [a].[test] FROM [a] RIGHT OUTER JOIN [b] ON [a].[id] = [b].[id] WHERE [a].[test] = ?;';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->select('a.test')->from('a')->rightOuterJoin('b')->on('a.id', '=', 'b.id')->where('a.test', '=', 1)->toSql());
$query = new Builder($con);
$query->usePreparedStmt = true;
$sql = 'SELECT [a].[test] FROM [a] RIGHT INNER JOIN [b] ON [a].[id] = [b].[id] WHERE [a].[test] = ?;';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->select('a.test')->from('a')->rightInnerJoin('b')->on('a.id', '=', 'b.id')->where('a.test', '=', 1)->toSql());
$query = new Builder($con);
$query->usePreparedStmt = true;
$sql = 'SELECT [a].[test] FROM [a] OUTER JOIN [b] ON [a].[id] = [b].[id] WHERE [a].[test] = ?;';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->select('a.test')->from('a')->outerJoin('b')->on('a.id', '=', 'b.id')->where('a.test', '=', 1)->toSql());
$query = new Builder($con);
$query->usePreparedStmt = true;
$sql = 'SELECT [a].[test] FROM [a] INNER JOIN [b] ON [a].[id] = [b].[id] WHERE [a].[test] = ?;';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->select('a.test')->from('a')->innerJoin('b')->on('a.id', '=', 'b.id')->where('a.test', '=', 1)->toSql());
$query = new Builder($con);
$query->usePreparedStmt = true;
$sql = 'SELECT [a].[test] FROM [a] CROSS JOIN [b] ON [a].[id] = [b].[id] WHERE [a].[test] = ?;';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->select('a.test')->from('a')->crossJoin('b')->on('a.id', '=', 'b.id')->where('a.test', '=', 1)->toSql());
$query = new Builder($con);
$query->usePreparedStmt = true;
$sql = 'SELECT [a].[test] FROM [a] FULL JOIN [b] ON [a].[id] = [b].[id] WHERE [a].[test] = ?;';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->select('a.test')->from('a')->fullJoin('b')->on('a.id', '=', 'b.id')->where('a.test', '=', 1)->toSql());
$query = new Builder($con);
$query->usePreparedStmt = true;
$sql = 'SELECT [a].[test] FROM [a] FULL OUTER JOIN [b] ON [a].[id] = [b].[id] WHERE [a].[test] = ?;';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->select('a.test')->from('a')->fullOuterJoin('b')->on('a.id', '=', 'b.id')->where('a.test', '=', 1)->toSql());
}
#[\PHPUnit\Framework\Attributes\DataProvider('dbConnectionProvider')]
#[\PHPUnit\Framework\Attributes\Group('framework')]
#[\PHPUnit\Framework\Attributes\TestDox('Mysql inserts form a valid query')]
public function testInsertPrepared($con) : void
{
if (!$con->isInitialized()) {
self::markTestSkipped();
return;
}
$iS = $con->getGrammar()->systemIdentifierStart;
$iE = $con->getGrammar()->systemIdentifierEnd;
$query = new Builder($con);
$query->usePreparedStmt = true;
$sql = 'INSERT INTO [a] VALUES (?, ?);';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->insert()->into('a')->values(1, 'test')->toSql());
$query = new Builder($con);
$query->usePreparedStmt = true;
$sql = 'INSERT INTO [a] VALUES (?, ?);';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->insert()->into('a')->value([1, 'test'])->toSql());
$query = new Builder($con);
$query->usePreparedStmt = true;
$sql = 'INSERT INTO [a] ([test], [test2]) VALUES (?, ?);';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->insert('test', 'test2')->into('a')->values(1, 'test')->toSql());
self::assertEquals([[1, 'test']], $query->getValues());
$query = new Builder($con);
$query->usePreparedStmt = true;
$sql = 'INSERT INTO [a] ([test], [test2]) VALUES (?, ?), (?, ?);';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->insert('test', 'test2')->into('a')->values(1, 'test')->values(2, 'test2')->toSql());
$query = new Builder($con);
$query->usePreparedStmt = true;
$sql = 'INSERT INTO [a] ([test], [test2]) VALUES (:test, :test2);';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->insert('test', 'test2')->into('a')->values(new Parameter('test'), new Parameter('test2'))->toSql());
}
#[\PHPUnit\Framework\Attributes\DataProvider('dbConnectionProvider')]
#[\PHPUnit\Framework\Attributes\Group('framework')]
#[\PHPUnit\Framework\Attributes\TestDox('Mysql deletes form a valid query')]
public function testDeletePrepared($con) : void
{
if (!$con->isInitialized()) {
self::markTestSkipped();
return;
}
$iS = $con->getGrammar()->systemIdentifierStart;
$iE = $con->getGrammar()->systemIdentifierEnd;
$query = new Builder($con);
$query->usePreparedStmt = true;
$sql = 'DELETE FROM [a] WHERE [a].[test] = ?;';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->delete()->from('a')->where('a.test', '=', 1)->toSql());
$query = new Builder($con);
$query->usePreparedStmt = true;
$sql = 'DELETE FROM [a] WHERE [a].[test] = :testVal;';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->delete()->from('a')->where('a.test', '=', new Parameter('testVal'))->toSql());
}
#[\PHPUnit\Framework\Attributes\DataProvider('dbConnectionProvider')]
#[\PHPUnit\Framework\Attributes\Group('framework')]
#[\PHPUnit\Framework\Attributes\TestDox('Mysql updates form a valid query')]
public function testUpdatePrepared($con) : void
{
if (!$con->isInitialized()) {
self::markTestSkipped();
return;
}
$iS = $con->getGrammar()->systemIdentifierStart;
$iE = $con->getGrammar()->systemIdentifierEnd;
$query = new Builder($con);
$query->usePreparedStmt = true;
$sql = 'UPDATE [a] SET [test] = ?, [test2] = ? WHERE [a].[test] = ?;';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->update('a')->set(['test' => 1])->set(['test2' => 2])->where('a.test', '=', 1)->toSql());
$query = new Builder($con);
$query->usePreparedStmt = true;
$sql = 'UPDATE [a] SET [test] = ?, [test2] = ? WHERE [a].[test] = ?;';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->update('a')->sets('test', 1)->sets('test2', 2)->where('a.test', '=', 1)->toSql());
$query = new Builder($con);
$query->usePreparedStmt = true;
$sql = 'UPDATE [a] SET [test] = ?, [test2] = :test2 WHERE [a].[test] = :test3;';
$sql = \strtr($sql, '[]', $iS . $iE);
self::assertEquals($sql, $query->update('a')->set(['test' => 1])->set(['test2' => new Parameter('test2')])->where('a.test', '=', new Parameter('test3'))->toSql());
}
#[\PHPUnit\Framework\Attributes\DataProvider('dbConnectionProvider')]
#[\PHPUnit\Framework\Attributes\Group('framework')]
#[\PHPUnit\Framework\Attributes\TestDox('Raw queries get output as defined')]

View File

@ -34,7 +34,7 @@ final class MarkdownTest extends \PHPUnit\Framework\TestCase
$data = \explode('.', $file);
if ($data[1] === 'md'
&& (\file_get_contents(__DIR__ . '/data/' . $data[0] . '.html') !== ($parsed = Markdown::parse(\file_get_contents(__DIR__ . '/data/' . $data[0] . '.md'))))
&& (($expected = \file_get_contents(__DIR__ . '/data/' . $data[0] . '.html')) !== ($parsed = Markdown::parse(\file_get_contents(__DIR__ . '/data/' . $data[0] . '.md'))))
) {
self::assertTrue(false, $file . "\n\n" . $parsed);
}

299
tests/config.php Normal file
View File

@ -0,0 +1,299 @@
<?php
return [
'db' => [
'core' => [
'masters' => [
'admin' => [
'db' => 'mysql', /* db type */
'host' => '127.0.0.1', /* db host address */
'port' => '3306', /* db host port */
'login' => 'test', /* db login name */
'password' => 'orange', /* db login password */
'database' => 'omt', /* db name */
'weight' => 1000, /* db table prefix */
'datetimeformat' => 'Y-m-d H:i:s',
],
'insert' => [
'db' => 'mysql', /* db type */
'host' => '127.0.0.1', /* db host address */
'port' => '3306', /* db host port */
'login' => 'test', /* db login name */
'password' => 'orange', /* db login password */
'database' => 'omt', /* db name */
'weight' => 1000, /* db table prefix */
'datetimeformat' => 'Y-m-d H:i:s',
],
'select' => [
'db' => 'mysql', /* db type */
'host' => '127.0.0.1', /* db host address */
'port' => '3306', /* db host port */
'login' => 'test', /* db login name */
'password' => 'orange', /* db login password */
'database' => 'omt', /* db name */
'weight' => 1000, /* db table prefix */
'datetimeformat' => 'Y-m-d H:i:s',
],
'update' => [
'db' => 'mysql', /* db type */
'host' => '127.0.0.1', /* db host address */
'port' => '3306', /* db host port */
'login' => 'test', /* db login name */
'password' => 'orange', /* db login password */
'database' => 'omt', /* db name */
'weight' => 1000, /* db table prefix */
'datetimeformat' => 'Y-m-d H:i:s',
],
'delete' => [
'db' => 'mysql', /* db type */
'host' => '127.0.0.1', /* db host address */
'port' => '3306', /* db host port */
'login' => 'test', /* db login name */
'password' => 'orange', /* db login password */
'database' => 'omt', /* db name */
'weight' => 1000, /* db table prefix */
'datetimeformat' => 'Y-m-d H:i:s',
],
'schema' => [
'db' => 'mysql', /* db type */
'host' => '127.0.0.1', /* db host address */
'port' => '3306', /* db host port */
'login' => 'test', /* db login name */
'password' => 'orange', /* db login password */
'database' => 'omt', /* db name */
'weight' => 1000, /* db table prefix */
'datetimeformat' => 'Y-m-d H:i:s',
],
],
'postgresql' => [
'admin' => [
'db' => 'pgsql', /* db type */
'host' => '127.0.0.1', /* db host address */
'port' => '5432', /* db host port */
'login' => 'test', /* db login name */
'password' => 'orange', /* db login password */
'database' => 'omt', /* db name */
'weight' => 1000, /* db table prefix */
'datetimeformat' => 'Y-m-d H:i:s',
],
'insert' => [
'db' => 'pgsql', /* db type */
'host' => '127.0.0.1', /* db host address */
'port' => '5432', /* db host port */
'login' => 'test', /* db login name */
'password' => 'orange', /* db login password */
'database' => 'omt', /* db name */
'weight' => 1000, /* db table prefix */
'datetimeformat' => 'Y-m-d H:i:s',
],
'select' => [
'db' => 'pgsql', /* db type */
'host' => '127.0.0.1', /* db host address */
'port' => '5432', /* db host port */
'login' => 'test', /* db login name */
'password' => 'orange', /* db login password */
'database' => 'omt', /* db name */
'weight' => 1000, /* db table prefix */
'datetimeformat' => 'Y-m-d H:i:s',
],
'update' => [
'db' => 'pgsql', /* db type */
'host' => '127.0.0.1', /* db host address */
'port' => '5432', /* db host port */
'login' => 'test', /* db login name */
'password' => 'orange', /* db login password */
'database' => 'omt', /* db name */
'weight' => 1000, /* db table prefix */
'datetimeformat' => 'Y-m-d H:i:s',
],
'delete' => [
'db' => 'pgsql', /* db type */
'host' => '127.0.0.1', /* db host address */
'port' => '5432', /* db host port */
'login' => 'test', /* db login name */
'password' => 'orange', /* db login password */
'database' => 'omt', /* db name */
'weight' => 1000, /* db table prefix */
'datetimeformat' => 'Y-m-d H:i:s',
],
'schema' => [
'db' => 'pgsql', /* db type */
'host' => '127.0.0.1', /* db host address */
'port' => '5432', /* db host port */
'login' => 'test', /* db login name */
'password' => 'orange', /* db login password */
'database' => 'omt', /* db name */
'weight' => 1000, /* db table prefix */
'datetimeformat' => 'Y-m-d H:i:s',
],
],
'sqlite' => [
'admin' => [
'db' => 'sqlite', /* db type */
'database' => __DIR__ . '/../Localization/Defaults/localization.sqlite', /* db name */
'weight' => 1000, /* db table prefix */
'datetimeformat' => 'Y-m-d H:i:s',
],
'insert' => [
'db' => 'sqlite', /* db type */
'database' => __DIR__ . '/../Localization/Defaults/localization.sqlite', /* db name */
'weight' => 1000, /* db table prefix */
'datetimeformat' => 'Y-m-d H:i:s',
],
'select' => [
'db' => 'sqlite', /* db type */
'database' => __DIR__ . '/../Localization/Defaults/localization.sqlite', /* db name */
'weight' => 1000, /* db table prefix */
'datetimeformat' => 'Y-m-d H:i:s',
],
'update' => [
'db' => 'sqlite', /* db type */
'database' => __DIR__ . '/../Localization/Defaults/localization.sqlite', /* db name */
'weight' => 1000, /* db table prefix */
'datetimeformat' => 'Y-m-d H:i:s',
],
'delete' => [
'db' => 'sqlite', /* db type */
'database' => __DIR__ . '/../Localization/Defaults/localization.sqlite', /* db name */
'weight' => 1000, /* db table prefix */
'datetimeformat' => 'Y-m-d H:i:s',
],
'schema' => [
'db' => 'sqlite', /* db type */
'database' => __DIR__ . '/../Localization/Defaults/localization.sqlite', /* db name */
'weight' => 1000, /* db table prefix */
'datetimeformat' => 'Y-m-d H:i:s',
],
],
'mssql' => [
'admin' => [
'db' => 'mssql', /* db type */
'host' => 'localhost', /* db host address */
'port' => '1433', /* db host port */
'login' => 'test', /* db login name */
'password' => 'orange', /* db login password */
'database' => 'omt', /* db name */
'weight' => 1000, /* db table prefix */
'datetimeformat' => 'Y-m-d H:i:s',
],
'insert' => [
'db' => 'mssql', /* db type */
'host' => 'localhost', /* db host address */
'port' => '1433', /* db host port */
'login' => 'test', /* db login name */
'password' => 'orange', /* db login password */
'database' => 'omt', /* db name */
'weight' => 1000, /* db table prefix */
'datetimeformat' => 'Y-m-d H:i:s',
],
'select' => [
'db' => 'mssql', /* db type */
'host' => 'localhost', /* db host address */
'port' => '1433', /* db host port */
'login' => 'test', /* db login name */
'password' => 'orange', /* db login password */
'database' => 'omt', /* db name */
'weight' => 1000, /* db table prefix */
'datetimeformat' => 'Y-m-d H:i:s',
],
'update' => [
'db' => 'mssql', /* db type */
'host' => 'localhost', /* db host address */
'port' => '1433', /* db host port */
'login' => 'test', /* db login name */
'password' => 'orange', /* db login password */
'database' => 'omt', /* db name */
'weight' => 1000, /* db table prefix */
'datetimeformat' => 'Y-m-d H:i:s',
],
'delete' => [
'db' => 'mssql', /* db type */
'host' => 'localhost', /* db host address */
'port' => '1433', /* db host port */
'login' => 'test', /* db login name */
'password' => 'orange', /* db login password */
'database' => 'omt', /* db name */
'weight' => 1000, /* db table prefix */
'datetimeformat' => 'Y-m-d H:i:s',
],
'schema' => [
'db' => 'mssql', /* db type */
'host' => 'localhost', /* db host address */
'port' => '1433', /* db host port */
'login' => 'test', /* db login name */
'password' => 'orange', /* db login password */
'database' => 'omt', /* db name */
'weight' => 1000, /* db table prefix */
'datetimeformat' => 'Y-m-d H:i:s',
],
],
],
],
'cache' => [
'redis' => [
'db' => 1,
'host' => '127.0.0.1',
'port' => 6379,
],
'memcached' => [
'host' => '127.0.0.1',
'port' => 11211,
],
],
'mail' => [
'imap' => [
'host' => '127.0.0.1',
'port' => 143,
'ssl' => false,
'user' => 'test',
'password' => '123456',
],
'pop3' => [
'host' => '127.0.0.1',
'port' => 25,
'ssl' => false,
'user' => 'test',
'password' => '123456',
],
],
'log' => [
'file' => [
'path' => __DIR__ . '/Logs',
],
],
'page' => [
'root' => '/',
'https' => false,
],
'app' => [
'path' => __DIR__,
'default' => [
'app' => 'Backend',
'id' => 'backend',
'lang' => 'en',
'theme' => 'Backend',
'org' => 1,
],
'domains' => [
'127.0.0.1' => [
'app' => 'Backend',
'id' => 'backend',
'lang' => 'en',
'theme' => 'Backend',
'org' => 1,
],
],
],
'socket' => [
'master' => [
'host' => '127.0.0.1',
'limit' => 300,
'port' => 4310,
],
],
'language' => [
'en',
],
'apis' => [
],
];