├── .phpunit-watcher.yml
├── .styleci.yml
├── CHANGELOG.md
├── LICENSE.md
├── Makefile
├── README.md
├── UPGRADE.md
├── composer-require-checker.json
├── composer.json
├── config
└── params.php
├── infection.json.dist
├── psalm.xml
├── rector.php
├── src
├── Cache
│ └── SchemaCache.php
├── Command
│ ├── AbstractCommand.php
│ ├── CommandInterface.php
│ ├── Param.php
│ ├── ParamBuilder.php
│ └── ParamInterface.php
├── Connection
│ ├── AbstractConnection.php
│ ├── AbstractDsn.php
│ ├── AbstractDsnSocket.php
│ ├── ConnectionInterface.php
│ ├── DsnInterface.php
│ └── ServerInfoInterface.php
├── Constant
│ ├── ColumnType.php
│ ├── DataType.php
│ ├── GettypeResult.php
│ ├── IndexType.php
│ ├── PhpType.php
│ ├── PseudoType.php
│ └── ReferentialAction.php
├── Constraint
│ ├── CheckConstraint.php
│ ├── Constraint.php
│ ├── ConstraintSchemaInterface.php
│ ├── DefaultValueConstraint.php
│ ├── ForeignKeyConstraint.php
│ └── IndexConstraint.php
├── Debug
│ ├── CommandInterfaceProxy.php
│ ├── ConnectionInterfaceProxy.php
│ ├── DatabaseCollector.php
│ └── TransactionInterfaceDecorator.php
├── Driver
│ ├── DriverInterface.php
│ └── Pdo
│ │ ├── AbstractPdoCommand.php
│ │ ├── AbstractPdoConnection.php
│ │ ├── AbstractPdoDriver.php
│ │ ├── AbstractPdoSchema.php
│ │ ├── AbstractPdoTransaction.php
│ │ ├── LogType.php
│ │ ├── PdoCommandInterface.php
│ │ ├── PdoConnectionInterface.php
│ │ ├── PdoDataReader.php
│ │ ├── PdoDriverInterface.php
│ │ └── PdoServerInfo.php
├── Exception
│ ├── ConvertException.php
│ ├── Exception.php
│ ├── IntegrityException.php
│ ├── InvalidArgumentException.php
│ ├── InvalidCallException.php
│ ├── InvalidConfigException.php
│ ├── InvalidParamException.php
│ ├── NotSupportedException.php
│ ├── PsrInvalidArgumentException.php
│ ├── StaleObjectException.php
│ ├── UnknownMethodException.php
│ └── UnknownPropertyException.php
├── Expression
│ ├── AbstractArrayExpressionBuilder.php
│ ├── AbstractStructuredExpressionBuilder.php
│ ├── ArrayExpression.php
│ ├── ArrayExpressionBuilder.php
│ ├── Expression.php
│ ├── ExpressionBuilder.php
│ ├── ExpressionBuilderInterface.php
│ ├── ExpressionInterface.php
│ ├── JsonExpression.php
│ ├── JsonExpressionBuilder.php
│ ├── StructuredExpression.php
│ └── StructuredExpressionBuilder.php
├── Helper
│ ├── DbArrayHelper.php
│ ├── DbStringHelper.php
│ └── DbUuidHelper.php
├── Profiler
│ ├── Context
│ │ ├── AbstractContext.php
│ │ ├── CommandContext.php
│ │ └── ConnectionContext.php
│ ├── ContextInterface.php
│ ├── ProfilerAwareInterface.php
│ ├── ProfilerAwareTrait.php
│ └── ProfilerInterface.php
├── Query
│ ├── BatchQueryResult.php
│ ├── BatchQueryResultInterface.php
│ ├── DataReaderInterface.php
│ ├── Query.php
│ ├── QueryExpressionBuilder.php
│ ├── QueryFunctionsInterface.php
│ ├── QueryInterface.php
│ └── QueryPartsInterface.php
├── QueryBuilder
│ ├── AbstractColumnDefinitionBuilder.php
│ ├── AbstractDDLQueryBuilder.php
│ ├── AbstractDMLQueryBuilder.php
│ ├── AbstractDQLQueryBuilder.php
│ ├── AbstractQueryBuilder.php
│ ├── ColumnDefinitionBuilderInterface.php
│ ├── Condition
│ │ ├── AbstractConjunctionCondition.php
│ │ ├── AbstractOverlapsCondition.php
│ │ ├── AndCondition.php
│ │ ├── ArrayOverlapsCondition.php
│ │ ├── BetweenColumnsCondition.php
│ │ ├── BetweenCondition.php
│ │ ├── Builder
│ │ │ ├── AbstractOverlapsConditionBuilder.php
│ │ │ ├── BetweenColumnsConditionBuilder.php
│ │ │ ├── BetweenConditionBuilder.php
│ │ │ ├── ConjunctionConditionBuilder.php
│ │ │ ├── ExistsConditionBuilder.php
│ │ │ ├── HashConditionBuilder.php
│ │ │ ├── InConditionBuilder.php
│ │ │ ├── LikeConditionBuilder.php
│ │ │ ├── NotConditionBuilder.php
│ │ │ └── SimpleConditionBuilder.php
│ │ ├── ExistsCondition.php
│ │ ├── HashCondition.php
│ │ ├── InCondition.php
│ │ ├── Interface
│ │ │ ├── BetweenColumnsConditionInterface.php
│ │ │ ├── BetweenConditionInterface.php
│ │ │ ├── ConditionInterface.php
│ │ │ ├── ConjunctionConditionInterface.php
│ │ │ ├── ExistConditionInterface.php
│ │ │ ├── HashConditionInterface.php
│ │ │ ├── InConditionInterface.php
│ │ │ ├── LikeConditionInterface.php
│ │ │ ├── NotConditionInterface.php
│ │ │ ├── OverlapsConditionInterface.php
│ │ │ └── SimpleConditionInterface.php
│ │ ├── JsonOverlapsCondition.php
│ │ ├── LikeCondition.php
│ │ ├── NotCondition.php
│ │ ├── OrCondition.php
│ │ └── SimpleCondition.php
│ ├── DDLQueryBuilderInterface.php
│ ├── DMLQueryBuilderInterface.php
│ ├── DQLQueryBuilderInterface.php
│ └── QueryBuilderInterface.php
├── Schema
│ ├── AbstractSchema.php
│ ├── AbstractTableSchema.php
│ ├── Column
│ │ ├── AbstractArrayColumn.php
│ │ ├── AbstractColumn.php
│ │ ├── AbstractColumnFactory.php
│ │ ├── AbstractJsonColumn.php
│ │ ├── AbstractStructuredColumn.php
│ │ ├── ArrayColumn.php
│ │ ├── ArrayLazyColumn.php
│ │ ├── BigIntColumn.php
│ │ ├── BinaryColumn.php
│ │ ├── BitColumn.php
│ │ ├── BooleanColumn.php
│ │ ├── ColumnBuilder.php
│ │ ├── ColumnFactoryInterface.php
│ │ ├── ColumnInterface.php
│ │ ├── DateTimeColumn.php
│ │ ├── DoubleColumn.php
│ │ ├── IntegerColumn.php
│ │ ├── JsonColumn.php
│ │ ├── JsonLazyColumn.php
│ │ ├── StringColumn.php
│ │ ├── StructuredColumn.php
│ │ └── StructuredLazyColumn.php
│ ├── Data
│ │ ├── AbstractLazyArray.php
│ │ ├── AbstractStructuredLazyArray.php
│ │ ├── JsonLazyArray.php
│ │ ├── LazyArray.php
│ │ ├── LazyArrayInterface.php
│ │ ├── LazyArrayTrait.php
│ │ └── StructuredLazyArray.php
│ ├── Quoter.php
│ ├── QuoterInterface.php
│ ├── SchemaInterface.php
│ └── TableSchemaInterface.php
├── Syntax
│ ├── AbstractSqlParser.php
│ └── ColumnDefinitionParser.php
└── Transaction
│ └── TransactionInterface.php
└── tests
├── AbstractColumnBuilderTest.php
├── AbstractColumnDefinitionParserTest.php
├── AbstractColumnFactoryTest.php
├── AbstractColumnTest.php
├── AbstractCommandTest.php
├── AbstractConnectionTest.php
├── AbstractPdoConnectionTest.php
├── AbstractQueryBuilderTest.php
├── AbstractQueryGetTableAliasTest.php
├── AbstractQueryTest.php
├── AbstractQuoterTest.php
├── AbstractSchemaTest.php
├── AbstractSqlParserTest.php
├── AbstractTableSchemaTest.php
├── Common
├── CommonBatchQueryResultTest.php
├── CommonColumnTest.php
├── CommonCommandTest.php
├── CommonConnectionTest.php
├── CommonPdoCommandTest.php
├── CommonPdoConnectionTest.php
├── CommonQueryBuilderTest.php
├── CommonQueryTest.php
└── CommonSchemaTest.php
├── Db
├── Cache
│ └── SchemaCacheTest.php
├── Command
│ ├── CommandTest.php
│ └── ParamBuilderTest.php
├── Connection
│ ├── ConnectionTest.php
│ ├── DsnSocketTest.php
│ └── DsnTest.php
├── Constraint
│ ├── CheckConstraintTest.php
│ ├── ConstraintTest.php
│ ├── DefaultValueConstraintTest.php
│ ├── ForeignKeyConstraintTest.php
│ └── IndexConstraintTest.php
├── Driver
│ └── Pdo
│ │ ├── PdoConnectionTest.php
│ │ ├── PdoDriverTest.php
│ │ └── PdoServerInfoTest.php
├── Exception
│ ├── ConvertExceptionTest.php
│ └── ExceptionTest.php
├── Expression
│ ├── ArrayExpressionBuilderTest.php
│ ├── ArrayExpressionTest.php
│ ├── JsonExpressionBuilderTest.php
│ ├── JsonExpressionTest.php
│ ├── StructuredExpressionBuilderTest.php
│ └── StructuredExpressionTest.php
├── Helper
│ ├── DbArrayHelperTest.php
│ ├── DbStringHelperTest.php
│ └── DbUuidHelperTest.php
├── Query
│ ├── QueryGetTableAliasTest.php
│ └── QueryTest.php
├── QueryBuilder
│ ├── ColumnDefinitionBuilderTest.php
│ ├── Condition
│ │ ├── AndConditionTest.php
│ │ ├── BetweenColumnsConditionTest.php
│ │ ├── BetweenConditionTest.php
│ │ ├── Builder
│ │ │ ├── BetweenColumnsConditionBuilderTest.php
│ │ │ └── LikeConditionBuilderTest.php
│ │ ├── ExistsConditionTest.php
│ │ ├── HashConditionTest.php
│ │ ├── InConditionTest.php
│ │ ├── LikeConditionTest.php
│ │ ├── NotConditionTest.php
│ │ └── SimpleConditionTest.php
│ └── QueryBuilderTest.php
├── Schema
│ ├── Column
│ │ ├── ColumnBuilderTest.php
│ │ ├── ColumnFactoryTest.php
│ │ └── ColumnTest.php
│ ├── Data
│ │ ├── LazyArrayJsonTest.php
│ │ ├── LazyArrayStructuredTest.php
│ │ └── LazyArrayTest.php
│ ├── QuoterTest.php
│ ├── SchemaTest.php
│ └── TableSchemaTest.php
└── Syntax
│ ├── ColumnDefinitionParserTest.php
│ └── SqlParserTest.php
├── Provider
├── ColumnBuilderProvider.php
├── ColumnDefinitionParserProvider.php
├── ColumnFactoryProvider.php
├── ColumnProvider.php
├── CommandPDOProvider.php
├── CommandProvider.php
├── DbArrayHelperProvider.php
├── QueryBuilderProvider.php
├── QueryProvider.php
├── QuoterProvider.php
├── SchemaProvider.php
└── SqlParserProvider.php
└── Support
├── AnyCaseValue.php
├── AnyValue.php
├── Assert.php
├── CompareValue.php
├── DbHelper.php
├── Fixture
└── db.sql
├── IntEnum.php
├── IsOneOfAssert.php
├── JsonSerializableObject.php
├── StringEnum.php
├── Stringable.php
├── Stub
├── Column.php
├── ColumnDefinitionBuilder.php
├── ColumnFactory.php
├── Command.php
├── Connection.php
├── DDLQueryBuilder.php
├── DMLQueryBuilder.php
├── DQLQueryBuilder.php
├── Dsn.php
├── DsnSocket.php
├── ExpressionBuilder.php
├── PdoDriver.php
├── QueryBuilder.php
├── Schema.php
├── SqlParser.php
├── TableSchema.php
└── Transaction.php
├── TestTrait.php
├── TraversableObject.php
└── string.txt
/.phpunit-watcher.yml:
--------------------------------------------------------------------------------
1 | watch:
2 | directories:
3 | - src
4 | - tests
5 | fileMask: '*.php'
6 | notifications:
7 | passingTests: false
8 | failingTests: false
9 | phpunit:
10 | binaryPath: vendor/bin/phpunit
11 | timeout: 180
12 |
--------------------------------------------------------------------------------
/.styleci.yml:
--------------------------------------------------------------------------------
1 | preset: psr12
2 | risky: true
3 |
4 | version: 8.1
5 |
6 | finder:
7 | exclude:
8 | - docs
9 | - vendor
10 |
11 | enabled:
12 | - alpha_ordered_traits
13 | - array_indentation
14 | - array_push
15 | - combine_consecutive_issets
16 | - combine_consecutive_unsets
17 | - combine_nested_dirname
18 | - declare_strict_types
19 | - dir_constant
20 | - fully_qualified_strict_types
21 | - function_to_constant
22 | - hash_to_slash_comment
23 | - is_null
24 | - logical_operators
25 | - magic_constant_casing
26 | - magic_method_casing
27 | - method_separation
28 | - modernize_types_casting
29 | - native_function_casing
30 | - native_function_type_declaration_casing
31 | - no_alias_functions
32 | - no_empty_comment
33 | - no_empty_phpdoc
34 | - no_empty_statement
35 | - no_extra_block_blank_lines
36 | - no_short_bool_cast
37 | - no_superfluous_elseif
38 | - no_unneeded_control_parentheses
39 | - no_unneeded_curly_braces
40 | - no_unneeded_final_method
41 | - no_unset_cast
42 | - no_unused_imports
43 | - no_unused_lambda_imports
44 | - no_useless_else
45 | - no_useless_return
46 | - normalize_index_brace
47 | - php_unit_dedicate_assert
48 | - php_unit_dedicate_assert_internal_type
49 | - php_unit_expectation
50 | - php_unit_mock
51 | - php_unit_mock_short_will_return
52 | - php_unit_namespaced
53 | - php_unit_no_expectation_annotation
54 | - phpdoc_no_empty_return
55 | - phpdoc_no_useless_inheritdoc
56 | - phpdoc_order
57 | - phpdoc_property
58 | - phpdoc_scalar
59 | - phpdoc_singular_inheritdoc
60 | - phpdoc_trim
61 | - phpdoc_trim_consecutive_blank_line_separation
62 | - phpdoc_type_to_var
63 | - phpdoc_types
64 | - phpdoc_types_order
65 | - print_to_echo
66 | - regular_callable_call
67 | - return_assignment
68 | - self_accessor
69 | - self_static_accessor
70 | - set_type_to_cast
71 | - short_array_syntax
72 | - short_list_syntax
73 | - simplified_if_return
74 | - single_quote
75 | - standardize_not_equals
76 | - ternary_to_null_coalescing
77 | - trailing_comma_in_multiline_array
78 | - unalign_double_arrow
79 | - unalign_equals
80 | - empty_loop_body_braces
81 | - integer_literal_case
82 | - union_type_without_spaces
83 |
84 | disabled:
85 | - function_declaration
86 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | Copyright © 2008 by Yii Software ()
2 | All rights reserved.
3 |
4 | Redistribution and use in source and binary forms, with or without
5 | modification, are permitted provided that the following conditions
6 | are met:
7 |
8 | * Redistributions of source code must retain the above copyright
9 | notice, this list of conditions and the following disclaimer.
10 | * Redistributions in binary form must reproduce the above copyright
11 | notice, this list of conditions and the following disclaimer in
12 | the documentation and/or other materials provided with the
13 | distribution.
14 | * Neither the name of Yii Software nor the names of its
15 | contributors may be used to endorse or promote products derived
16 | from this software without specific prior written permission.
17 |
18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
21 | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
22 | COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
23 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
24 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
28 | ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 | POSSIBILITY OF SUCH DAMAGE.
30 |
--------------------------------------------------------------------------------
/composer-require-checker.json:
--------------------------------------------------------------------------------
1 | {
2 | "symbol-whitelist" : [
3 | "Yiisoft\\Yii\\Debug\\Collector\\CollectorTrait",
4 | "Yiisoft\\Yii\\Debug\\Collector\\SummaryCollectorInterface"
5 | ]
6 | }
7 |
--------------------------------------------------------------------------------
/config/params.php:
--------------------------------------------------------------------------------
1 | [
11 | 'collectors' => [
12 | DatabaseCollector::class,
13 | ],
14 | 'trackedServices' => [
15 | ConnectionInterface::class => [ConnectionInterfaceProxy::class, DatabaseCollector::class],
16 | ],
17 | ],
18 | ];
19 |
--------------------------------------------------------------------------------
/infection.json.dist:
--------------------------------------------------------------------------------
1 | {
2 | "source": {
3 | "directories": [
4 | "src"
5 | ]
6 | },
7 | "logs": {
8 | "text": "php:\/\/stderr",
9 | "stryker": {
10 | "report": "master"
11 | }
12 | },
13 | "mutators": {
14 | "@default": true
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/psalm.xml:
--------------------------------------------------------------------------------
1 |
2 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/rector.php:
--------------------------------------------------------------------------------
1 | paths([
14 | __DIR__ . '/src',
15 | __DIR__ . '/tests',
16 | ]);
17 |
18 | // register a single rule
19 | $rectorConfig->rule(InlineConstructorDefaultToPropertyRector::class);
20 |
21 | // define sets of rules
22 | $rectorConfig->sets([
23 | LevelSetList::UP_TO_PHP_81,
24 | ]);
25 |
26 | $rectorConfig->skip([
27 | GetDebugTypeRector::class => [
28 | __DIR__ . '/tests/AbstractColumnTest.php',
29 | ],
30 | ReadOnlyPropertyRector::class,
31 | NullToStrictStringFuncCallArgRector::class,
32 | ]);
33 | };
34 |
--------------------------------------------------------------------------------
/src/Command/Param.php:
--------------------------------------------------------------------------------
1 | type;
35 | }
36 |
37 | public function getValue(): mixed
38 | {
39 | return $this->value;
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/Command/ParamBuilder.php:
--------------------------------------------------------------------------------
1 | driver:" . "unix_socket=$this->unixSocket";
33 |
34 | if ($this->databaseName !== null && $this->databaseName !== '') {
35 | $dsn .= ';' . "dbname=$this->databaseName";
36 | }
37 |
38 | $parts = [];
39 |
40 | foreach ($this->options as $key => $value) {
41 | $parts[] = "$key=$value";
42 | }
43 |
44 | if (!empty($parts)) {
45 | $dsn .= ';' . implode(';', $parts);
46 | }
47 |
48 | return $dsn;
49 | }
50 |
51 | /**
52 | * @return string The Data Source Name, or DSN, has the information required to connect to the database.
53 | */
54 | public function __toString(): string
55 | {
56 | return $this->asString();
57 | }
58 |
59 | /**
60 | * @return string|null The database name to connect to.
61 | */
62 | public function getDatabaseName(): string|null
63 | {
64 | return $this->databaseName;
65 | }
66 |
67 | /**
68 | * @return string The database driver to use.
69 | */
70 | public function getDriver(): string
71 | {
72 | return $this->driver;
73 | }
74 |
75 | /**
76 | * @return string The unix socket to connect to.
77 | */
78 | public function getUnixSocket(): string
79 | {
80 | return $this->unixSocket;
81 | }
82 |
83 | /**
84 | * @return array The options to use.
85 | */
86 | public function getOptions(): array
87 | {
88 | return $this->options;
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/src/Connection/DsnInterface.php:
--------------------------------------------------------------------------------
1 | 'utf8mb4']);
24 | * $pdoDriver = new PDODriver($dsn->asString(), 'username', 'password');
25 | * $connection = new Connection($pdoDriver, $schemaCache);
26 | * ```
27 | *
28 | * Will result in the DSN string `mysql:host=127.0.0.1;dbname=yiitest;port=3306;charset=utf8mb4`.
29 | *
30 | * Or unix socket:
31 | *
32 | * ```php
33 | * $dsn = new DsnSocket('mysql', '/var/run/mysqld/mysqld.sock', 'yiitest', '', ['charset' => 'utf8mb4']);
34 | * $pdoDriver = new PDODriver($dsn->asString(), 'username', 'password');
35 | * $connection = new Connection($pdoDriver, $schemaCache);
36 | * ```
37 | *
38 | * Will result in the DSN string `mysql:unix_socket=/var/run/mysqld/mysqld.sock;dbname=yiitest;charset=utf8mb4`.
39 | */
40 | public function asString(): string;
41 | }
42 |
--------------------------------------------------------------------------------
/src/Connection/ServerInfoInterface.php:
--------------------------------------------------------------------------------
1 | expression;
26 | }
27 |
28 | /**
29 | * Set the SQL of the `CHECK` constraint.
30 | *
31 | * @param string $value The SQL of the `CHECK` constraint.
32 | */
33 | public function expression(string $value): self
34 | {
35 | $this->expression = $value;
36 | return $this;
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/Constraint/Constraint.php:
--------------------------------------------------------------------------------
1 | columnNames;
27 | }
28 |
29 | /**
30 | * @return object|string|null The constraint name.
31 | */
32 | public function getName(): object|string|null
33 | {
34 | return $this->name;
35 | }
36 |
37 | /**
38 | * Set the list of column names the constraint belongs to.
39 | *
40 | * @param array|string|null $value The list of column names the constraint belongs to.
41 | */
42 | public function columnNames(array|string|null $value): static
43 | {
44 | $this->columnNames = $value;
45 | return $this;
46 | }
47 |
48 | /**
49 | * Set the constraint name.
50 | *
51 | * @param object|string|null $value The constraint name.
52 | */
53 | public function name(object|string|null $value): static
54 | {
55 | $this->name = $value;
56 | return $this;
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/Constraint/DefaultValueConstraint.php:
--------------------------------------------------------------------------------
1 | value;
29 | }
30 |
31 | /**
32 | * Set the default value as returned by the DBMS.
33 | *
34 | * @param mixed $value The default value as returned by the DBMS.
35 | */
36 | public function value(mixed $value): self
37 | {
38 | $this->value = $value;
39 | return $this;
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/Constraint/IndexConstraint.php:
--------------------------------------------------------------------------------
1 | isUnique;
26 | }
27 |
28 | /**
29 | * @return bool Whether the index was created for a primary key.
30 | */
31 | public function isPrimary(): bool
32 | {
33 | return $this->isPrimary;
34 | }
35 |
36 | /**
37 | * Set whether the index is unique.
38 | *
39 | * @param bool $value Whether the index is unique.
40 | */
41 | public function unique(bool $value): self
42 | {
43 | $this->isUnique = $value;
44 | return $this;
45 | }
46 |
47 | /**
48 | * Set whether the index was created for a primary key.
49 | *
50 | * @param bool $value whether the index was created for a primary key.
51 | */
52 | public function primary(bool $value): self
53 | {
54 | $this->isPrimary = $value;
55 | return $this;
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/Debug/TransactionInterfaceDecorator.php:
--------------------------------------------------------------------------------
1 | collector->collectTransactionStart($isolationLevel, $callStack['file'] . ':' . $callStack['line']);
25 |
26 | $this->decorated->begin($isolationLevel);
27 | }
28 |
29 | /**
30 | * @psalm-suppress PossiblyUndefinedArrayOffset
31 | */
32 | public function commit(): void
33 | {
34 | [$callStack] = debug_backtrace();
35 |
36 | $this->decorated->commit();
37 |
38 | $this->collector->collectTransactionCommit($callStack['file'] . ':' . $callStack['line']);
39 | }
40 |
41 | public function getLevel(): int
42 | {
43 | return $this->decorated->getLevel();
44 | }
45 |
46 | public function isActive(): bool
47 | {
48 | return $this->decorated->isActive();
49 | }
50 |
51 | /**
52 | * @psalm-suppress PossiblyUndefinedArrayOffset
53 | */
54 | public function rollBack(): void
55 | {
56 | [$callStack] = debug_backtrace();
57 |
58 | $this->decorated->rollBack();
59 |
60 | $this->collector->collectTransactionRollback($callStack['file'] . ':' . $callStack['line']);
61 | }
62 |
63 | public function setIsolationLevel(string $level): void
64 | {
65 | $this->decorated->{__FUNCTION__}(...func_get_args());
66 | }
67 |
68 | public function createSavepoint(string $name): void
69 | {
70 | $this->decorated->{__FUNCTION__}(...func_get_args());
71 | }
72 |
73 | public function rollBackSavepoint(string $name): void
74 | {
75 | $this->decorated->{__FUNCTION__}(...func_get_args());
76 | }
77 |
78 | public function releaseSavepoint(string $name): void
79 | {
80 | $this->decorated->{__FUNCTION__}(...func_get_args());
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/src/Driver/DriverInterface.php:
--------------------------------------------------------------------------------
1 | attributes = $attributes;
32 | }
33 |
34 | public function createConnection(): PDO
35 | {
36 | return new PDO($this->dsn, $this->username, $this->password, $this->attributes);
37 | }
38 |
39 | public function charset(string|null $charset): void
40 | {
41 | $this->charset = $charset;
42 | }
43 |
44 | public function getCharset(): string|null
45 | {
46 | return $this->charset;
47 | }
48 |
49 | public function getDsn(): string
50 | {
51 | return $this->dsn;
52 | }
53 |
54 | public function getPassword(): string
55 | {
56 | return $this->password;
57 | }
58 |
59 | public function getUsername(): string
60 | {
61 | return $this->username;
62 | }
63 |
64 | public function password(#[\SensitiveParameter] string $password): void
65 | {
66 | $this->password = $password;
67 | }
68 |
69 | public function username(string $username): void
70 | {
71 | $this->username = $username;
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/src/Driver/Pdo/AbstractPdoSchema.php:
--------------------------------------------------------------------------------
1 | db instanceof PdoConnectionInterface) {
28 | $cacheKey = [$this->db->getDriver()->getDsn(), $this->db->getDriver()->getUsername()];
29 | } else {
30 | throw new NotSupportedException('Only PDO connections are supported.');
31 | }
32 |
33 | return $cacheKey;
34 | }
35 |
36 | protected function getCacheKey(string $name): array
37 | {
38 | return [static::class, ...$this->generateCacheKey(), $this->db->getQuoter()->getRawTableName($name)];
39 | }
40 |
41 | protected function getCacheTag(): string
42 | {
43 | return md5(serialize([static::class, ...$this->generateCacheKey()]));
44 | }
45 |
46 | protected function getResultColumnCacheKey(array $metadata): string
47 | {
48 | return md5(serialize([static::class . '::getResultColumn', ...$this->generateCacheKey(), ...$metadata]));
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/Driver/Pdo/LogType.php:
--------------------------------------------------------------------------------
1 | version === null) {
27 | /** @var string */
28 | $this->version = $this->db->getActivePdo()->getAttribute(PDO::ATTR_SERVER_VERSION) ?? '';
29 | }
30 |
31 | return $this->version;
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/Exception/ConvertException.php:
--------------------------------------------------------------------------------
1 | e->getMessage() . PHP_EOL . 'The SQL being executed was: ' . $this->rawSql;
33 |
34 | $errorInfo = $this->e instanceof PDOException ? $this->e->errorInfo : null;
35 |
36 | return match (
37 | str_contains($message, self::MSG_INTEGRITY_EXCEPTION_1) ||
38 | str_contains($message, self::MGS_INTEGRITY_EXCEPTION_2) ||
39 | str_contains($message, self::MSG_INTEGRITY_EXCEPTION_3)
40 | ) {
41 | true => new IntegrityException($message, $errorInfo, $this->e),
42 | default => new Exception($message, $errorInfo, $this->e),
43 | };
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/Exception/Exception.php:
--------------------------------------------------------------------------------
1 | errorInfo, true);
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/Exception/IntegrityException.php:
--------------------------------------------------------------------------------
1 | queryBuilder->bindParam($param, $params);
31 | }
32 |
33 | protected function buildSubquery(QueryInterface $query, ArrayExpression $expression, array &$params): string
34 | {
35 | [$sql, $params] = $this->queryBuilder->build($query, $params);
36 |
37 | return "($sql)";
38 | }
39 |
40 | protected function buildValue(iterable $value, ArrayExpression $expression, array &$params): string
41 | {
42 | if (!is_array($value)) {
43 | $value = iterator_to_array($value, false);
44 | }
45 |
46 | return $this->buildStringValue(json_encode($value, JSON_THROW_ON_ERROR), $expression, $params);
47 | }
48 |
49 | protected function getLazyArrayValue(LazyArrayInterface $value): array|string
50 | {
51 | return match ($value::class) {
52 | LazyArray::class, JsonLazyArray::class, StructuredLazyArray::class => $value->getRawValue(),
53 | default => $value->getValue(),
54 | };
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/Expression/Expression.php:
--------------------------------------------------------------------------------
1 | select($expression)->scalar(); // SELECT NOW();
21 | * echo $now; // prints the current date
22 | * ```
23 | *
24 | * Expression objects are mainly created for passing raw SQL expressions to methods of
25 | * {@see \Yiisoft\Db\Query\QueryInterface} and related classes.
26 | *
27 | * @psalm-import-type ParamsType from ConnectionInterface
28 | */
29 | final class Expression implements ExpressionInterface, Stringable
30 | {
31 | /**
32 | * @psalm-param ParamsType $params
33 | */
34 | public function __construct(private string $expression, private array $params = [])
35 | {
36 | }
37 |
38 | /**
39 | * @return string The expression.
40 | */
41 | public function __toString(): string
42 | {
43 | return $this->expression;
44 | }
45 |
46 | /**
47 | * @return array List of parameters to bind to this expression. The keys are placeholders appearing in
48 | * {@see expression} and the values are the corresponding parameter values.
49 | *
50 | * @psalm-return ParamsType
51 | */
52 | public function getParams(): array
53 | {
54 | return $this->params;
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/Expression/ExpressionBuilderInterface.php:
--------------------------------------------------------------------------------
1 | `, `<`), combining expressions with logical
12 | * operators (such as `AND`, `OR`), and building sub-queries.
13 | *
14 | * The interface provides a consistent way for developers to build expressions for various types of
15 | * database queries, without having to worry about the specific syntax of the underlying database.
16 | *
17 | * @see ExpressionInterface
18 | */
19 | interface ExpressionBuilderInterface
20 | {
21 | }
22 |
--------------------------------------------------------------------------------
/src/Expression/ExpressionInterface.php:
--------------------------------------------------------------------------------
1 | 1, 'b' => 2]); // will be encoded to '{"a": 1, "b": 2}'
14 | * ```
15 | */
16 | final class JsonExpression implements ExpressionInterface
17 | {
18 | /**
19 | * @param mixed $value The JSON content. It can be represented as
20 | * - an `array` of values;
21 | * - an instance which implements {@see Traversable} or {@see JsonSerializable} and represents an array of values;
22 | * - an instance of {@see QueryInterface} that represents an SQL sub-query;
23 | * - a valid JSON encoded array as a `string`, e.g. `'[1,2,3]'` or `'{"a":1,"b":2}'`;
24 | * - any other value compatible with {@see \json_encode()} input requirements.
25 | * @param string|null $type Type of database column, value should be cast to. Defaults to `null`, meaning no explicit
26 | * casting will be performed. This property is used only for DBMSs that support different types of JSON.
27 | * For example, PostgresSQL has `json` and `jsonb` types.
28 | */
29 | public function __construct(private readonly mixed $value, private readonly string|null $type = null)
30 | {
31 | }
32 |
33 | /**
34 | * The JSON content. It can be represented as
35 | * - an `array` of values;
36 | * - an instance which implements {@see Traversable} or {@see JsonSerializable} and represents an array of values;
37 | * - an instance of {@see QueryInterface} that represents an SQL sub-query;
38 | * - a valid JSON encoded array as a `string`, e.g. `[1,2,3]` or `'{"a":1,"b":2}'`;
39 | * - any other value compatible with {@see \json_encode()} input requirements.
40 | */
41 | public function getValue(): mixed
42 | {
43 | return $this->value;
44 | }
45 |
46 | /**
47 | * Type of JSON, expression should be cast to. Defaults to `null`, meaning no explicit casting will be performed.
48 | * This property will be encountered only for DBMSs that support different types of JSON.
49 | * For example, PostgresSQL has `json` and `jsonb` types.
50 | */
51 | public function getType(): string|null
52 | {
53 | return $this->type;
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/Expression/StructuredExpressionBuilder.php:
--------------------------------------------------------------------------------
1 | queryBuilder->bindParam($param, $params);
30 | }
31 |
32 | protected function buildSubquery(QueryInterface $query, StructuredExpression $expression, array &$params): string
33 | {
34 | [$sql, $params] = $this->queryBuilder->build($query, $params);
35 |
36 | return "($sql)";
37 | }
38 |
39 | protected function buildValue(array|object $value, StructuredExpression $expression, array &$params): string
40 | {
41 | $value = $this->prepareValues($value, $expression);
42 | $param = new Param(json_encode(array_values($value), JSON_THROW_ON_ERROR), DataType::STRING);
43 |
44 | return $this->queryBuilder->bindParam($param, $params);
45 | }
46 |
47 | protected function getLazyArrayValue(LazyArrayInterface $value): array|string
48 | {
49 | return match ($value::class) {
50 | LazyArray::class, JsonLazyArray::class, StructuredLazyArray::class => $value->getRawValue(),
51 | default => $value->getValue(),
52 | };
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/Helper/DbStringHelper.php:
--------------------------------------------------------------------------------
1 | exception = $e;
24 | return $this;
25 | }
26 |
27 | public function asArray(): array
28 | {
29 | return [
30 | self::METHOD => $this->method,
31 | self::EXCEPTION => $this->exception,
32 | ];
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/Profiler/Context/CommandContext.php:
--------------------------------------------------------------------------------
1 | method);
20 | }
21 |
22 | public function getType(): string
23 | {
24 | return 'command';
25 | }
26 |
27 | public function asArray(): array
28 | {
29 | return parent::asArray() + [
30 | self::LOG_CONTEXT => $this->logContext,
31 | self::SQL => $this->sql,
32 | self::PARAMS => $this->params,
33 | ];
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/Profiler/Context/ConnectionContext.php:
--------------------------------------------------------------------------------
1 | profiler = $profiler;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/Profiler/ProfilerInterface.php:
--------------------------------------------------------------------------------
1 | createCommand('SELECT * FROM post');
20 | * $reader = $command->query();
21 | *
22 | * foreach ($reader as $row) {
23 | * $rows[] = $row;
24 | * }
25 | *
26 | * Note: That since DataReader is a forward-only stream, you can only traverse it once. Doing it the second time will
27 | * throw an exception.
28 | *
29 | * @extends Iterator
30 | *
31 | * @psalm-import-type IndexBy from QueryInterface
32 | * @psalm-import-type ResultCallbackOne from QueryInterface
33 | */
34 | interface DataReaderInterface extends Iterator, Countable
35 | {
36 | /**
37 | * Returns the index of the current row or null if {@see indexBy} property is specified and there is no row
38 | * at the current position.
39 | *
40 | * This method is required by the interface {@see Iterator}.
41 | */
42 | public function key(): int|string|null;
43 |
44 | /**
45 | * Returns the current row or false if there is no row at the current position.
46 | *
47 | * This method is required by the interface {@see Iterator}.
48 | */
49 | public function current(): array|object|false;
50 |
51 | /**
52 | * Sets `indexBy` property.
53 | *
54 | * @param Closure|string|null $indexBy The name of the column by which the query results should be indexed by.
55 | * This can also be a `Closure` instance (for example, anonymous function) that returns the index value based
56 | * on the given row data.
57 | *
58 | * The signature of the callable should be:
59 | *
60 | * ```php
61 | * function (array $row): array-key
62 | * {
63 | * // return the index value corresponding to $row
64 | * }
65 | * ```
66 | *
67 | * @psalm-param IndexBy|null $indexBy
68 | */
69 | public function indexBy(Closure|string|null $indexBy): static;
70 |
71 | /**
72 | * Sets the callback, to be called on all rows of the query result before returning them.
73 | *
74 | * For example:
75 | *
76 | * ```php
77 | * function (array $rows): array {
78 | * foreach ($rows as &$row) {
79 | * $row['name'] = strtoupper($row['name']);
80 | * }
81 | * return $rows;
82 | * }
83 | * ```
84 | *
85 | * @psalm-param ResultCallbackOne|null $resultCallback
86 | */
87 | public function resultCallback(Closure|null $resultCallback): static;
88 | }
89 |
--------------------------------------------------------------------------------
/src/Query/QueryExpressionBuilder.php:
--------------------------------------------------------------------------------
1 | queryBuilder->build($expression, $params);
33 | return "($sql)";
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/QueryBuilder/ColumnDefinitionBuilderInterface.php:
--------------------------------------------------------------------------------
1 | expressions;
21 | }
22 |
23 | public static function fromArrayDefinition(string $operator, array $operands): self
24 | {
25 | return new static($operands);
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/QueryBuilder/Condition/AndCondition.php:
--------------------------------------------------------------------------------
1 | column;
27 | }
28 |
29 | public function getIntervalEnd(): mixed
30 | {
31 | return $this->intervalEnd;
32 | }
33 |
34 | public function getIntervalStart(): mixed
35 | {
36 | return $this->intervalStart;
37 | }
38 |
39 | public function getOperator(): string
40 | {
41 | return $this->operator;
42 | }
43 |
44 | /**
45 | * Creates a condition based on the given operator and operands.
46 | *
47 | * @throws InvalidArgumentException If the number of operands isn't 3.
48 | */
49 | public static function fromArrayDefinition(string $operator, array $operands): self
50 | {
51 | if (!isset($operands[0], $operands[1], $operands[2])) {
52 | throw new InvalidArgumentException("Operator '$operator' requires three operands.");
53 | }
54 |
55 | return new self(self::validateColumn($operator, $operands[0]), $operator, $operands[1], $operands[2]);
56 | }
57 |
58 | /**
59 | * Validates the given column to be string or `ExpressionInterface`.
60 | *
61 | * @throws InvalidArgumentException If the column isn't a string or `ExpressionInterface`.
62 | */
63 | private static function validateColumn(string $operator, mixed $column): string|ExpressionInterface
64 | {
65 | if (is_string($column) || $column instanceof ExpressionInterface) {
66 | return $column;
67 | }
68 |
69 | throw new InvalidArgumentException(
70 | "Operator '$operator' requires column to be string or ExpressionInterface."
71 | );
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/src/QueryBuilder/Condition/Builder/AbstractOverlapsConditionBuilder.php:
--------------------------------------------------------------------------------
1 | queryBuilder->buildExpression($column);
24 | }
25 |
26 | return $this->queryBuilder->getQuoter()->quoteColumnName($column);
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/QueryBuilder/Condition/Builder/BetweenConditionBuilder.php:
--------------------------------------------------------------------------------
1 | getOperator();
38 | $column = $expression->getColumn();
39 | $column = $column instanceof ExpressionInterface ? $this->queryBuilder->buildExpression($column) : $column;
40 |
41 | if (!str_contains($column, '(')) {
42 | $column = $this->queryBuilder->getQuoter()->quoteColumnName($column);
43 | }
44 |
45 | $phName1 = $this->createPlaceholder($expression->getIntervalStart(), $params);
46 | $phName2 = $this->createPlaceholder($expression->getIntervalEnd(), $params);
47 |
48 | return "$column $operator $phName1 AND $phName2";
49 | }
50 |
51 | /**
52 | * Attaches `$value` to `$params` array and return placeholder.
53 | *
54 | * @throws Exception
55 | * @throws InvalidArgumentException
56 | * @throws InvalidConfigException
57 | * @throws NotSupportedException
58 | */
59 | protected function createPlaceholder(mixed $value, array &$params): string
60 | {
61 | if ($value instanceof ExpressionInterface) {
62 | return $this->queryBuilder->buildExpression($value, $params);
63 | }
64 |
65 | return $this->queryBuilder->bindParam($value, $params);
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/src/QueryBuilder/Condition/Builder/ExistsConditionBuilder.php:
--------------------------------------------------------------------------------
1 | getOperator();
35 | $query = $expression->getQuery();
36 | $sql = $this->queryBuilder->buildExpression($query, $params);
37 | return "$operator $sql";
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/QueryBuilder/Condition/Builder/HashConditionBuilder.php:
--------------------------------------------------------------------------------
1 | getHash() ?? [];
43 | $parts = [];
44 |
45 | /**
46 | * @psalm-var array $hash
47 | */
48 | foreach ($hash as $column => $value) {
49 | if (is_iterable($value) || $value instanceof QueryInterface) {
50 | /** IN condition */
51 | $parts[] = $this->queryBuilder->buildCondition(new InCondition($column, 'IN', $value), $params);
52 | } else {
53 | if (!str_contains($column, '(')) {
54 | $column = $this->queryBuilder->getQuoter()->quoteColumnName($column);
55 | }
56 |
57 | if ($value === null) {
58 | $parts[] = "$column IS NULL";
59 | } elseif ($value instanceof ExpressionInterface) {
60 | $parts[] = "$column=" . $this->queryBuilder->buildExpression($value, $params);
61 | } else {
62 | $phName = $this->queryBuilder->bindParam($value, $params);
63 | $parts[] = "$column=$phName";
64 | }
65 | }
66 | }
67 |
68 | return (count($parts) === 1) ? $parts[0] : ('(' . implode(') AND (', $parts) . ')');
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/src/QueryBuilder/Condition/Builder/NotConditionBuilder.php:
--------------------------------------------------------------------------------
1 | getCondition();
35 |
36 | if ($operand === '') {
37 | return '';
38 | }
39 |
40 | $expressionValue = $this->queryBuilder->buildCondition($operand, $params);
41 |
42 | return "{$this->getNegationOperator()} ($expressionValue)";
43 | }
44 |
45 | protected function getNegationOperator(): string
46 | {
47 | return 'NOT';
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/QueryBuilder/Condition/Builder/SimpleConditionBuilder.php:
--------------------------------------------------------------------------------
1 | getOperator();
38 | $column = $expression->getColumn();
39 | /** @psalm-var mixed $value */
40 | $value = $expression->getValue();
41 |
42 | if ($column instanceof ExpressionInterface) {
43 | $column = $this->queryBuilder->buildExpression($column, $params);
44 | } elseif (!str_contains($column, '(')) {
45 | $column = $this->queryBuilder->getQuoter()->quoteColumnName($column);
46 | }
47 |
48 | if ($value === null) {
49 | return "$column $operator NULL";
50 | }
51 |
52 | if ($value instanceof ExpressionInterface) {
53 | return "$column $operator {$this->queryBuilder->buildExpression($value, $params)}";
54 | }
55 |
56 | $phName = $this->queryBuilder->bindParam($value, $params);
57 |
58 | return "$column $operator $phName";
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/QueryBuilder/Condition/ExistsCondition.php:
--------------------------------------------------------------------------------
1 | operator;
23 | }
24 |
25 | public function getQuery(): QueryInterface
26 | {
27 | return $this->query;
28 | }
29 |
30 | /**
31 | * Creates a condition based on the given operator and operands.
32 | *
33 | * @throws InvalidArgumentException If the number of operands isn't 1, and the first operand isn't a query object.
34 | */
35 | public static function fromArrayDefinition(string $operator, array $operands): self
36 | {
37 | if (isset($operands[0]) && $operands[0] instanceof QueryInterface) {
38 | return new self($operator, $operands[0]);
39 | }
40 |
41 | throw new InvalidArgumentException('Sub query for EXISTS operator must be a Query object.');
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/QueryBuilder/Condition/HashCondition.php:
--------------------------------------------------------------------------------
1 | hash;
21 | }
22 |
23 | /**
24 | * Creates a condition based on the given operator and operands.
25 | */
26 | public static function fromArrayDefinition(string $operator, array $operands): self
27 | {
28 | return new self($operands);
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/QueryBuilder/Condition/Interface/BetweenColumnsConditionInterface.php:
--------------------------------------------------------------------------------
1 | ` or `<=`.
39 | */
40 | public function getOperator(): string;
41 |
42 | /**
43 | * @return array|ExpressionInterface|int|Iterator|string|null The value to the right of {@see operator}.
44 | */
45 | public function getValue(): array|int|string|Iterator|ExpressionInterface|null;
46 |
47 | /**
48 | * @return bool|null Whether the comparison is case-sensitive. `null` means using the default behavior.
49 | */
50 | public function getCaseSensitive(): ?bool;
51 | }
52 |
--------------------------------------------------------------------------------
/src/QueryBuilder/Condition/Interface/NotConditionInterface.php:
--------------------------------------------------------------------------------
1 | ` or `<=`.
21 | */
22 | public function getOperator(): string;
23 |
24 | /**
25 | * @return mixed The value to the right of {@see operator}.
26 | */
27 | public function getValue(): mixed;
28 | }
29 |
--------------------------------------------------------------------------------
/src/QueryBuilder/Condition/JsonOverlapsCondition.php:
--------------------------------------------------------------------------------
1 | condition;
28 | }
29 |
30 | /**
31 | * Creates a condition based on the given operator and operands.
32 | *
33 | * @throws InvalidArgumentException If the number of operands isn't 1.
34 | */
35 | public static function fromArrayDefinition(string $operator, array $operands): self
36 | {
37 | return new self(self::validateCondition($operator, $operands));
38 | }
39 |
40 | /**
41 | * Validate the given condition have at least 1 condition and to be `array`, `string`, `null` or `ExpressionInterface`.
42 | *
43 | * @throws InvalidArgumentException If the number of operands isn't 1.
44 | */
45 | private static function validateCondition(string $operator, array $condition): ExpressionInterface|array|null|string
46 | {
47 | if (count($condition) !== 1) {
48 | throw new InvalidArgumentException("Operator '$operator' requires exactly one operand.");
49 | }
50 |
51 | /** @psalm-var mixed $firstValue */
52 | $firstValue = array_shift($condition);
53 |
54 | if (
55 | is_array($firstValue) ||
56 | $firstValue instanceof ExpressionInterface ||
57 | is_string($firstValue) ||
58 | $firstValue === null
59 | ) {
60 | return $firstValue;
61 | }
62 |
63 | throw new InvalidArgumentException(
64 | "Operator '$operator' requires condition to be array, string, null or ExpressionInterface."
65 | );
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/src/QueryBuilder/Condition/OrCondition.php:
--------------------------------------------------------------------------------
1 | column;
26 | }
27 |
28 | public function getOperator(): string
29 | {
30 | return $this->operator;
31 | }
32 |
33 | public function getValue(): mixed
34 | {
35 | return $this->value;
36 | }
37 |
38 | /**
39 | * Creates a condition based on the given operator and operands.
40 | *
41 | * @throws InvalidArgumentException If the number of operands isn't 2.
42 | */
43 | public static function fromArrayDefinition(string $operator, array $operands): self
44 | {
45 | if (isset($operands[0]) && array_key_exists(1, $operands)) {
46 | return new self(self::validateColumn($operator, $operands[0]), $operator, $operands[1]);
47 | }
48 |
49 | throw new InvalidArgumentException("Operator '$operator' requires two operands.");
50 | }
51 |
52 | /**
53 | * Validate the given column to be `string` or `ExpressionInterface`.
54 | *
55 | * @throws InvalidArgumentException If the column isn't a `string` or `ExpressionInterface`.
56 | */
57 | private static function validateColumn(string $operator, mixed $column): string|ExpressionInterface
58 | {
59 | if (is_string($column) || $column instanceof ExpressionInterface) {
60 | return $column;
61 | }
62 |
63 | throw new InvalidArgumentException(
64 | "Operator '$operator' requires column to be string or ExpressionInterface."
65 | );
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/src/Schema/Column/AbstractArrayColumn.php:
--------------------------------------------------------------------------------
1 | column = $column;
41 | return $this;
42 | }
43 |
44 | /**
45 | * @return ColumnInterface|null The column of an array item.
46 | * @psalm-mutation-free
47 | */
48 | public function getColumn(): ColumnInterface|null
49 | {
50 | return $this->column;
51 | }
52 |
53 | /**
54 | * Set dimension of an array, must be greater than `0`.
55 | *
56 | * @psalm-param positive-int $dimension
57 | */
58 | public function dimension(int $dimension): static
59 | {
60 | $this->dimension = $dimension;
61 | return $this;
62 | }
63 |
64 | /**
65 | * @return int The dimension of the array.
66 | *
67 | * @psalm-return positive-int
68 | * @psalm-mutation-free
69 | */
70 | public function getDimension(): int
71 | {
72 | return $this->dimension;
73 | }
74 |
75 | /** @psalm-mutation-free */
76 | public function getPhpType(): string
77 | {
78 | return PhpType::ARRAY;
79 | }
80 |
81 | /**
82 | * @param iterable|LazyArrayInterface|QueryInterface|string|null $value
83 | * @psalm-suppress MoreSpecificImplementedParamType
84 | */
85 | public function dbTypecast(mixed $value): ExpressionInterface|null
86 | {
87 | if ($value === null || $value instanceof ExpressionInterface) {
88 | return $value;
89 | }
90 |
91 | return new ArrayExpression($value, $this);
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/src/Schema/Column/AbstractJsonColumn.php:
--------------------------------------------------------------------------------
1 | getDbType());
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/Schema/Column/AbstractStructuredColumn.php:
--------------------------------------------------------------------------------
1 |
25 | */
26 | protected array $columns = [];
27 |
28 | /**
29 | * Set columns of the structured type.
30 | *
31 | * @param ColumnInterface[] $columns The metadata of the structured type columns.
32 | * @psalm-param array $columns
33 | */
34 | public function columns(array $columns): static
35 | {
36 | $this->columns = $columns;
37 | return $this;
38 | }
39 |
40 | /**
41 | * Get the metadata of the structured type columns.
42 | *
43 | * @return ColumnInterface[]
44 | * @psalm-return array
45 | * @psalm-mutation-free
46 | */
47 | public function getColumns(): array
48 | {
49 | return $this->columns;
50 | }
51 |
52 | /** @psalm-mutation-free */
53 | public function getPhpType(): string
54 | {
55 | return PhpType::ARRAY;
56 | }
57 |
58 | /**
59 | * @param array|object|string|null $value
60 | * @psalm-suppress MoreSpecificImplementedParamType
61 | */
62 | public function dbTypecast(mixed $value): ExpressionInterface|null
63 | {
64 | if ($value === null || $value instanceof ExpressionInterface) {
65 | return $value;
66 | }
67 |
68 | return new StructuredExpression($value, $this);
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/src/Schema/Column/ArrayColumn.php:
--------------------------------------------------------------------------------
1 | getColumn(), $this->getDimension()))->getValue();
26 | }
27 |
28 | return $value;
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/Schema/Column/ArrayLazyColumn.php:
--------------------------------------------------------------------------------
1 | getColumn(), $this->getDimension());
26 | }
27 |
28 | return $value;
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/Schema/Column/BigIntColumn.php:
--------------------------------------------------------------------------------
1 | $this->dbTypecastString($value),
36 | GettypeResult::NULL => null,
37 | GettypeResult::INTEGER => $value,
38 | GettypeResult::DOUBLE => $this->dbTypecastString((string) $value),
39 | GettypeResult::BOOLEAN => $value ? 1 : 0,
40 | GettypeResult::OBJECT => match (true) {
41 | $value instanceof ExpressionInterface => $value,
42 | $value instanceof BackedEnum => is_int($value->value)
43 | ? $value->value
44 | : $this->dbTypecastString($value->value),
45 | $value instanceof DateTimeInterface => $value->getTimestamp(),
46 | $value instanceof Stringable => $this->dbTypecastString((string) $value),
47 | default => $this->throwWrongTypeException($value::class),
48 | },
49 | default => $this->throwWrongTypeException(gettype($value)),
50 | };
51 | }
52 |
53 | /** @psalm-mutation-free */
54 | public function getPhpType(): string
55 | {
56 | return PhpType::STRING;
57 | }
58 |
59 | public function phpTypecast(mixed $value): string|null
60 | {
61 | if ($value === null) {
62 | return null;
63 | }
64 |
65 | return (string) $value;
66 | }
67 |
68 | protected function dbTypecastString(string $value): int|string|null
69 | {
70 | if ($value === '') {
71 | return null;
72 | }
73 |
74 | return PHP_INT_MAX >= $value && $value >= PHP_INT_MIN ? (int) $value : $value;
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/src/Schema/Column/BinaryColumn.php:
--------------------------------------------------------------------------------
1 | new Param($value, PDO::PARAM_LOB),
28 | GettypeResult::RESOURCE => $value,
29 | GettypeResult::NULL => null,
30 | GettypeResult::INTEGER => (string) $value,
31 | GettypeResult::DOUBLE => (string) $value,
32 | GettypeResult::BOOLEAN => $value ? '1' : '0',
33 | GettypeResult::OBJECT => match (true) {
34 | $value instanceof ExpressionInterface => $value,
35 | $value instanceof BackedEnum => (string) $value->value,
36 | $value instanceof Stringable => (string) $value,
37 | default => $this->throwWrongTypeException($value::class),
38 | },
39 | default => $this->throwWrongTypeException(gettype($value)),
40 | };
41 | }
42 |
43 | public function phpTypecast(mixed $value): mixed
44 | {
45 | return $value;
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/Schema/Column/BitColumn.php:
--------------------------------------------------------------------------------
1 | null,
26 | default => $value instanceof ExpressionInterface ? $value : (int) $value,
27 | };
28 | }
29 |
30 | /** @psalm-mutation-free */
31 | public function getPhpType(): string
32 | {
33 | return PhpType::INT;
34 | }
35 |
36 | public function phpTypecast(mixed $value): int|null
37 | {
38 | if ($value === null) {
39 | return null;
40 | }
41 |
42 | return (int) $value;
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/Schema/Column/BooleanColumn.php:
--------------------------------------------------------------------------------
1 | true,
22 | false => false,
23 | null, '' => null,
24 | default => $value instanceof ExpressionInterface ? $value : (bool) $value,
25 | };
26 | }
27 |
28 | /** @psalm-mutation-free */
29 | public function getPhpType(): string
30 | {
31 | return PhpType::BOOL;
32 | }
33 |
34 | public function phpTypecast(mixed $value): bool|null
35 | {
36 | if ($value === null) {
37 | return null;
38 | }
39 |
40 | return $value && $value !== "\0";
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/Schema/Column/DoubleColumn.php:
--------------------------------------------------------------------------------
1 | $value,
30 | GettypeResult::INTEGER => $value,
31 | GettypeResult::NULL => null,
32 | GettypeResult::STRING => $value === '' ? null : (float) $value,
33 | GettypeResult::BOOLEAN => $value ? 1.0 : 0.0,
34 | GettypeResult::OBJECT => match (true) {
35 | $value instanceof ExpressionInterface => $value,
36 | $value instanceof BackedEnum => $value->value === ''
37 | ? null
38 | : (is_int($value->value) ? $value->value : (float) $value->value),
39 | $value instanceof DateTimeInterface => (float) $value->format('U.u'),
40 | $value instanceof Stringable => ($val = (string) $value) === '' ? null : (float) $val,
41 | default => $this->throwWrongTypeException($value::class),
42 | },
43 | default => $this->throwWrongTypeException(gettype($value)),
44 | };
45 | }
46 |
47 | /** @psalm-mutation-free */
48 | public function getPhpType(): string
49 | {
50 | return PhpType::FLOAT;
51 | }
52 |
53 | public function phpTypecast(mixed $value): float|null
54 | {
55 | if ($value === null) {
56 | return null;
57 | }
58 |
59 | return (float) $value;
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/Schema/Column/IntegerColumn.php:
--------------------------------------------------------------------------------
1 | $value,
29 | GettypeResult::NULL => null,
30 | GettypeResult::STRING => $value === '' ? null : (int) $value,
31 | GettypeResult::DOUBLE => (int) $value,
32 | GettypeResult::BOOLEAN => $value ? 1 : 0,
33 | GettypeResult::OBJECT => match (true) {
34 | $value instanceof ExpressionInterface => $value,
35 | $value instanceof BackedEnum => $value->value === '' ? null : (int) $value->value,
36 | $value instanceof DateTimeInterface => $value->getTimestamp(),
37 | $value instanceof Stringable => ($val = (string) $value) === '' ? null : (int) $val,
38 | default => $this->throwWrongTypeException($value::class),
39 | },
40 | default => $this->throwWrongTypeException(gettype($value)),
41 | };
42 | }
43 |
44 | /** @psalm-mutation-free */
45 | public function getPhpType(): string
46 | {
47 | return PhpType::INT;
48 | }
49 |
50 | public function phpTypecast(mixed $value): int|null
51 | {
52 | if ($value === null) {
53 | return null;
54 | }
55 |
56 | return (int) $value;
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/Schema/Column/JsonColumn.php:
--------------------------------------------------------------------------------
1 | new JsonLazyArray($value),
26 | default => json_decode($value, true, 512, JSON_THROW_ON_ERROR),
27 | };
28 | }
29 |
30 | return $value;
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/Schema/Column/StringColumn.php:
--------------------------------------------------------------------------------
1 | $value,
27 | GettypeResult::RESOURCE => $value,
28 | GettypeResult::NULL => null,
29 | GettypeResult::INTEGER => (string) $value,
30 | GettypeResult::DOUBLE => (string) $value,
31 | GettypeResult::BOOLEAN => $value ? '1' : '0',
32 | GettypeResult::OBJECT => match (true) {
33 | $value instanceof ExpressionInterface => $value,
34 | $value instanceof BackedEnum => (string) $value->value,
35 | $value instanceof Stringable => (string) $value,
36 | default => $this->throwWrongTypeException($value::class),
37 | },
38 | default => $this->throwWrongTypeException(gettype($value)),
39 | };
40 | }
41 |
42 | /** @psalm-mutation-free */
43 | public function getPhpType(): string
44 | {
45 | return PhpType::STRING;
46 | }
47 |
48 | public function phpTypecast(mixed $value): string|null
49 | {
50 | /** @var string|null $value */
51 | return $value;
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/Schema/Column/StructuredColumn.php:
--------------------------------------------------------------------------------
1 | getColumns()))->getValue();
26 | }
27 |
28 | return $value;
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/Schema/Column/StructuredLazyColumn.php:
--------------------------------------------------------------------------------
1 | getColumns());
26 | }
27 |
28 | return $value;
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/Schema/Data/JsonLazyArray.php:
--------------------------------------------------------------------------------
1 |
24 | * @template-implements IteratorAggregate
25 | */
26 | final class JsonLazyArray implements LazyArrayInterface, ArrayAccess, Countable, JsonSerializable, IteratorAggregate
27 | {
28 | use LazyArrayTrait;
29 |
30 | protected array|string $value;
31 |
32 | /**
33 | * @param string $value The string retrieved value from the database that can be parsed into an array.
34 | */
35 | public function __construct(
36 | string $value
37 | ) {
38 | $this->value = $value;
39 | }
40 |
41 | /**
42 | * Prepares the value to be used as an array or throws an exception if it's impossible.
43 | *
44 | * @psalm-assert array $this->value
45 | */
46 | protected function prepareValue(): void
47 | {
48 | if (is_string($this->value)) {
49 | $value = json_decode($this->value, true, 512, JSON_THROW_ON_ERROR);
50 |
51 | if (!is_array($value)) {
52 | throw new InvalidArgumentException('JSON value must be a valid string array representation.');
53 | }
54 |
55 | $this->value = $value;
56 | }
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/Schema/Data/LazyArray.php:
--------------------------------------------------------------------------------
1 | value;
27 | }
28 |
29 | /**
30 | * Returns parsed and typecasted value.
31 | */
32 | public function getValue(): array
33 | {
34 | $this->prepareValue();
35 |
36 | return $this->value;
37 | }
38 |
39 | public function jsonSerialize(): array
40 | {
41 | return $this->getValue();
42 | }
43 |
44 | /**
45 | * @param int|string $offset The offset to check.
46 | */
47 | public function offsetExists(mixed $offset): bool
48 | {
49 | $this->prepareValue();
50 |
51 | return isset($this->value[$offset]);
52 | }
53 |
54 | /**
55 | * @param int|string $offset The offset to retrieve.
56 | */
57 | public function offsetGet(mixed $offset): mixed
58 | {
59 | $this->prepareValue();
60 |
61 | return $this->value[$offset];
62 | }
63 |
64 | /**
65 | * @param int|string $offset The offset to assign the value to.
66 | */
67 | public function offsetSet(mixed $offset, mixed $value): void
68 | {
69 | $this->prepareValue();
70 |
71 | $this->value[$offset] = $value;
72 | }
73 |
74 | /**
75 | * @param int|string $offset The offset to unset.
76 | */
77 | public function offsetUnset(mixed $offset): void
78 | {
79 | $this->prepareValue();
80 |
81 | unset($this->value[$offset]);
82 | }
83 |
84 | public function count(): int
85 | {
86 | $this->prepareValue();
87 |
88 | return count($this->value);
89 | }
90 |
91 | public function getIterator(): ArrayIterator
92 | {
93 | $this->prepareValue();
94 |
95 | return new ArrayIterator($this->value);
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/src/Schema/Data/StructuredLazyArray.php:
--------------------------------------------------------------------------------
1 | getColumnBuilderClass();
34 |
35 | $column = $columnBuilderClass::$buildingMethod(...$args);
36 |
37 | $this->assertInstanceOf($expectedInstanceOf, $column);
38 | $this->assertSame($expectedType, $column->getType());
39 |
40 | $columnMethodResults = array_merge(
41 | ColumnBuilderProvider::DEFAULT_COLUMN_METHOD_RESULTS,
42 | $expectedMethodResults,
43 | );
44 |
45 | foreach ($columnMethodResults as $method => $result) {
46 | $this->assertSame($result, $column->$method());
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/tests/AbstractColumnDefinitionParserTest.php:
--------------------------------------------------------------------------------
1 | createColumnDefinitionParser();
29 |
30 | $this->assertSame($expected, $parser->parse($definition));
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/tests/AbstractColumnTest.php:
--------------------------------------------------------------------------------
1 | assertSame($type, $column->getType());
24 | $this->assertSame($phpType, $column->getPhpType());
25 | }
26 |
27 | #[DataProviderExternal(ColumnProvider::class, 'dbTypecastColumns')]
28 | public function testDbTypecastColumns(ColumnInterface $column, array $values)
29 | {
30 | // Set the timezone for testing purposes, could be any timezone except UTC
31 | $oldDatetime = date_default_timezone_get();
32 | date_default_timezone_set('America/New_York');
33 |
34 | foreach ($values as [$expected, $value]) {
35 | if (is_object($expected) && !(is_object($value) && $expected::class === $value::class)) {
36 | $this->assertEquals($expected, $column->dbTypecast($value));
37 | } else {
38 | $this->assertSame($expected, $column->dbTypecast($value));
39 | }
40 | }
41 |
42 | date_default_timezone_set($oldDatetime);
43 | }
44 |
45 | #[DataProviderExternal(ColumnProvider::class, 'dbTypecastColumnsWithException')]
46 | public function testDbTypecastColumnsWithException(ColumnInterface $column, mixed $value)
47 | {
48 | $type = is_object($value) ? $value::class : gettype($value);
49 |
50 | $this->expectException(InvalidArgumentException::class);
51 | $this->expectExceptionMessage("Wrong $type value for {$column->getType()} column.");
52 |
53 | $column->dbTypecast($value);
54 | }
55 |
56 | #[DataProviderExternal(ColumnProvider::class, 'phpTypecastColumns')]
57 | public function testPhpTypecastColumns(ColumnInterface $column, array $values)
58 | {
59 | foreach ($values as [$expected, $value]) {
60 | if (is_object($expected) && !(is_object($value) && $expected::class === $value::class)) {
61 | $this->assertEquals($expected, $column->phpTypecast($value));
62 | } else {
63 | $this->assertSame($expected, $column->phpTypecast($value));
64 | }
65 | }
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/tests/AbstractSchemaTest.php:
--------------------------------------------------------------------------------
1 | getConnection();
23 |
24 | $schema = $db->getSchema();
25 |
26 | $this->assertNull($schema->getDefaultSchema());
27 | }
28 |
29 | public function testGetDataType(): void
30 | {
31 | $values = [
32 | [null, DataType::NULL],
33 | ['', DataType::STRING],
34 | ['hello', DataType::STRING],
35 | [0, DataType::INTEGER],
36 | [1, DataType::INTEGER],
37 | [1337, DataType::INTEGER],
38 | [true, DataType::BOOLEAN],
39 | [false, DataType::BOOLEAN],
40 | [$fp = fopen(__FILE__, 'rb'), DataType::LOB],
41 | ];
42 |
43 | $db = $this->getConnection();
44 |
45 | $schema = $db->getSchema();
46 |
47 | foreach ($values as $value) {
48 | $this->assertSame(
49 | $value[1],
50 | $schema->getDataType($value[0]),
51 | 'type for value ' . print_r($value[0], true) . ' does not match.',
52 | );
53 | }
54 |
55 | fclose($fp);
56 | }
57 |
58 | public function testRefresh(): void
59 | {
60 | $db = $this->getConnection();
61 |
62 | $schema = $db->getSchema();
63 | $schema->refresh();
64 |
65 | $this->assertSame([], Assert::getInaccessibleProperty($schema, 'tableMetadata'));
66 | $this->assertSame([], Assert::getInaccessibleProperty($schema, 'tableNames'));
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/tests/AbstractSqlParserTest.php:
--------------------------------------------------------------------------------
1 | createSqlParser($sql);
21 |
22 | $this->assertSame($expectedPlaceholder, $parser->getNextPlaceholder($position));
23 | $this->assertSame($expectedPosition, $position);
24 | }
25 |
26 | /** @dataProvider \Yiisoft\Db\Tests\Provider\SqlParserProvider::getAllPlaceholders */
27 | public function testGetAllPlaceholders(string $sql, array $expectedPlaceholders, array $expectedPositions): void
28 | {
29 | $parser = $this->createSqlParser($sql);
30 |
31 | $placeholders = [];
32 | $positions = [];
33 |
34 | while (null !== $placeholder = $parser->getNextPlaceholder($position)) {
35 | $placeholders[] = $placeholder;
36 | $positions[] = $position;
37 | }
38 |
39 | $this->assertSame($expectedPlaceholders, $placeholders);
40 | $this->assertSame($expectedPositions, $positions);
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/tests/Common/CommonConnectionTest.php:
--------------------------------------------------------------------------------
1 | getConnection(true);
21 |
22 | $this->expectException(Exception::class);
23 |
24 | $db->transaction(
25 | static function () use ($db) {
26 | $db->createCommand()->insert('profile', ['description' => 'test transaction shortcut'])->execute();
27 |
28 | throw new Exception('Exception in transaction shortcut');
29 | }
30 | );
31 | $profilesCount = $db->createCommand(
32 | <<queryScalar();
36 |
37 | $this->assertSame(0, $profilesCount, 'profile should not be inserted in transaction shortcut');
38 |
39 | $db->close();
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/tests/Db/Command/ParamBuilderTest.php:
--------------------------------------------------------------------------------
1 | 1, 'name' => 'test', 'expression' => new Expression('NOW()')];
21 | $expression = new Expression('id = :id AND name = :name AND expression = :expression', $params);
22 | $paramBuilder = new ParamBuilder();
23 |
24 | $this->assertSame(':pv3', $paramBuilder->build($expression, $params));
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/tests/Db/Connection/ConnectionTest.php:
--------------------------------------------------------------------------------
1 | getConnection();
26 |
27 | $this->assertNull($db->getTableSchema('non_existing_table'));
28 | }
29 |
30 | public function testSerialized(): void
31 | {
32 | $this->expectException(NotSupportedException::class);
33 | $this->expectExceptionMessage(
34 | 'Yiisoft\Db\Tests\Support\Stub\Command::internalExecute is not supported by this DBMS.'
35 | );
36 |
37 | parent::testSerialized();
38 | }
39 |
40 | public function testConstructColumnFactory(): void
41 | {
42 | $columnFactory = new ColumnFactory();
43 |
44 | $db = new Connection($this->getDriver(), DbHelper::getSchemaCache(), $columnFactory);
45 |
46 | $this->assertSame($columnFactory, $db->getColumnFactory());
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/tests/Db/Connection/DsnTest.php:
--------------------------------------------------------------------------------
1 | assertSame('mysql', $dsn->getDriver());
22 | $this->assertSame('localhost', $dsn->getHost());
23 | $this->assertSame('yiitest', $dsn->getDatabaseName());
24 | $this->assertNull($dsn->getPort());
25 | $this->assertSame([], $dsn->getOptions());
26 | }
27 |
28 | public function testGetDatabaseName(): void
29 | {
30 | $dsn = new Dsn('mysql', 'localhost', 'yiitest', '3306', ['charset' => 'utf8']);
31 |
32 | $this->assertSame('yiitest', $dsn->getDatabaseName());
33 | }
34 |
35 | public function testGetDriver(): void
36 | {
37 | $dsn = new Dsn('mysql', 'localhost', 'yiitest', '3306', ['charset' => 'utf8']);
38 |
39 | $this->assertSame('mysql', $dsn->getDriver());
40 | }
41 |
42 | public function testGetDsn(): void
43 | {
44 | $dsn = new Dsn('mysql', 'localhost', 'yiitest', '3306', ['charset' => 'utf8']);
45 |
46 | $this->assertSame('mysql:host=localhost;dbname=yiitest;port=3306;charset=utf8', $dsn->asString());
47 | $this->assertSame('mysql:host=localhost;dbname=yiitest;port=3306;charset=utf8', $dsn->__toString());
48 | }
49 |
50 | public function testGetDsnWithoutDatabaseName(): void
51 | {
52 | $dsn = new Dsn('mysql', 'localhost', '', '3306', ['charset' => 'utf8']);
53 |
54 | $this->assertSame('mysql:host=localhost;port=3306;charset=utf8', $dsn->asString());
55 | $this->assertSame('mysql:host=localhost;port=3306;charset=utf8', $dsn->__toString());
56 | $this->assertEmpty($dsn->getDatabaseName());
57 |
58 | $dsn = new Dsn('mysql', 'localhost', null, '3306', ['charset' => 'utf8']);
59 |
60 | $this->assertSame('mysql:host=localhost;port=3306;charset=utf8', $dsn->asString());
61 | $this->assertSame('mysql:host=localhost;port=3306;charset=utf8', $dsn->__toString());
62 | $this->assertNull($dsn->getDatabaseName());
63 | }
64 |
65 | public function testGetHost(): void
66 | {
67 | $dsn = new Dsn('mysql', 'localhost', 'yiitest', '3306', ['charset' => 'utf8']);
68 |
69 | $this->assertSame('localhost', $dsn->getHost());
70 | }
71 |
72 | public function testGetPort(): void
73 | {
74 | $dsn = new Dsn('mysql', 'localhost', 'yiitest', '3306', ['charset' => 'utf8']);
75 |
76 | $this->assertSame('3306', $dsn->getPort());
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/tests/Db/Constraint/CheckConstraintTest.php:
--------------------------------------------------------------------------------
1 | assertSame('', $checkConstraint->getExpression());
22 |
23 | $checkConstraint = $checkConstraint->expression('expression');
24 |
25 | $this->assertSame('expression', $checkConstraint->getExpression());
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/tests/Db/Constraint/ConstraintTest.php:
--------------------------------------------------------------------------------
1 | assertNull($constraint->getColumnNames());
23 |
24 | $constraint->columnNames('columnNames');
25 |
26 | $this->assertSame('columnNames', $constraint->getColumnNames());
27 |
28 | $constraint->columnNames(['columnNames']);
29 |
30 | $this->assertSame(['columnNames'], $constraint->getColumnNames());
31 | }
32 |
33 | public function testGetName(): void
34 | {
35 | $constraint = new Constraint();
36 |
37 | $this->assertNull($constraint->getName());
38 |
39 | $constraint->name('name');
40 |
41 | $this->assertSame('name', $constraint->getName());
42 |
43 | $constraint->name(new stdClass());
44 |
45 | $this->assertInstanceOf(stdClass::class, $constraint->getName());
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/tests/Db/Constraint/DefaultValueConstraintTest.php:
--------------------------------------------------------------------------------
1 | assertNull($defaultValueConstraint->getValue());
23 |
24 | $defaultValueConstraint = $defaultValueConstraint->value('value');
25 |
26 | $this->assertSame('value', $defaultValueConstraint->getValue());
27 |
28 | $defaultValueConstraint = $defaultValueConstraint->value(new stdClass());
29 |
30 | $this->assertInstanceOf(stdClass::class, $defaultValueConstraint->getValue());
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/tests/Db/Constraint/ForeignKeyConstraintTest.php:
--------------------------------------------------------------------------------
1 | assertNull($foreignKeyConstraint->getForeignSchemaName());
22 |
23 | $foreignKeyConstraint = $foreignKeyConstraint->foreignSchemaName('foreignSchemaName');
24 |
25 | $this->assertSame('foreignSchemaName', $foreignKeyConstraint->getForeignSchemaName());
26 | }
27 |
28 | public function testGetForeignTableName(): void
29 | {
30 | $foreignKeyConstraint = new ForeignKeyConstraint();
31 |
32 | $this->assertNull($foreignKeyConstraint->getForeignTableName());
33 |
34 | $foreignKeyConstraint = $foreignKeyConstraint->foreignTableName('foreignTableName');
35 |
36 | $this->assertSame('foreignTableName', $foreignKeyConstraint->getForeignTableName());
37 | }
38 |
39 | public function testGetForeignColumnNames(): void
40 | {
41 | $foreignKeyConstraint = new ForeignKeyConstraint();
42 |
43 | $this->assertSame([], $foreignKeyConstraint->getForeignColumnNames());
44 |
45 | $foreignKeyConstraint = $foreignKeyConstraint->foreignColumnNames(['foreignColumnNames']);
46 |
47 | $this->assertSame(['foreignColumnNames'], $foreignKeyConstraint->getForeignColumnNames());
48 | }
49 |
50 | public function testGetOnUpdate(): void
51 | {
52 | $foreignKeyConstraint = new ForeignKeyConstraint();
53 |
54 | $this->assertNull($foreignKeyConstraint->getOnUpdate());
55 |
56 | $foreignKeyConstraint = $foreignKeyConstraint->onUpdate('onUpdate');
57 |
58 | $this->assertSame('onUpdate', $foreignKeyConstraint->getOnUpdate());
59 | }
60 |
61 | public function testGetOnDelete(): void
62 | {
63 | $foreignKeyConstraint = new ForeignKeyConstraint();
64 |
65 | $this->assertNull($foreignKeyConstraint->getOnDelete());
66 |
67 | $foreignKeyConstraint = $foreignKeyConstraint->onDelete('onDelete');
68 |
69 | $this->assertSame('onDelete', $foreignKeyConstraint->getOnDelete());
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/tests/Db/Constraint/IndexConstraintTest.php:
--------------------------------------------------------------------------------
1 | assertFalse($indexConstraint->isUnique());
22 |
23 | $indexConstraint = $indexConstraint->unique(true);
24 |
25 | $this->assertTrue($indexConstraint->isUnique());
26 | }
27 |
28 | public function testIsPrimary(): void
29 | {
30 | $indexConstraint = new IndexConstraint();
31 |
32 | $this->assertFalse($indexConstraint->isPrimary());
33 |
34 | $indexConstraint = $indexConstraint->primary(true);
35 |
36 | $this->assertTrue($indexConstraint->isPrimary());
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/tests/Db/Driver/Pdo/PdoConnectionTest.php:
--------------------------------------------------------------------------------
1 | setDsn('');
24 | $db = $this->getConnection();
25 |
26 | $this->expectException(InvalidConfigException::class);
27 | $this->expectExceptionMessage('Connection::dsn cannot be empty.');
28 |
29 | $db->open();
30 | }
31 |
32 | public function testGetLastInsertID(): void
33 | {
34 | $db = $this->getConnection();
35 |
36 | $this->expectException(InvalidCallException::class);
37 | $this->expectExceptionMessage('DB Connection is not active.');
38 |
39 | $db->getLastInsertId();
40 | }
41 |
42 | public function testQuoteValueString(): void
43 | {
44 | $db = $this->getConnection();
45 |
46 | $string = 'test string';
47 |
48 | $this->assertStringContainsString($string, $db->quoteValue($string));
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/tests/Db/Driver/Pdo/PdoDriverTest.php:
--------------------------------------------------------------------------------
1 | attributes([PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]);
24 |
25 | $this->assertSame(
26 | [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION],
27 | Assert::getInaccessibleProperty($pdoDriver, 'attributes'),
28 | );
29 | }
30 |
31 | public function testGetDriverName(): void
32 | {
33 | $dsn = 'sqlite::memory:';
34 | $pdoDriver = new PDODriver($dsn);
35 |
36 | $this->assertSame('db', $pdoDriver->getDriverName());
37 | }
38 |
39 | public function testSetCharSet(): void
40 | {
41 | $dsn = 'sqlite::memory:';
42 | $pdoDriver = new PDODriver($dsn);
43 | $pdoDriver->charset('utf8');
44 |
45 | $this->assertSame('utf8', $pdoDriver->getCharSet());
46 | }
47 |
48 | public function testGetPassword(): void
49 | {
50 | $dsn = 'sqlite::memory:';
51 | $pdoDriver = new PDODriver($dsn);
52 | $pdoDriver->password('password');
53 |
54 | $this->assertSame('password', $pdoDriver->getPassword());
55 | }
56 |
57 | public function testGetUsername(): void
58 | {
59 | $dsn = 'sqlite::memory:';
60 | $pdoDriver = new PDODriver($dsn);
61 | $pdoDriver->username('username');
62 |
63 | $this->assertSame('username', $pdoDriver->getUsername());
64 | }
65 |
66 | public function testSensitiveParameter(): void
67 | {
68 | if (PHP_VERSION_ID < 80200) {
69 | $this->markTestSkipped('SensitiveParameterValue is not available in PHP < 8.2');
70 | }
71 | $dsn = 'sqlite::memory:';
72 | try {
73 | new PDODriver($dsn, password: null);
74 | } catch (\TypeError $e) {
75 | $this->assertTrue($e->getTrace()[0]['args'][2] instanceof \SensitiveParameterValue);
76 | }
77 | $pdoDriver = new PDODriver($dsn);
78 | try {
79 | $pdoDriver->password(null);
80 | } catch (\TypeError $e) {
81 | $this->assertTrue($e->getTrace()[0]['args'][0] instanceof \SensitiveParameterValue);
82 | }
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/tests/Db/Driver/Pdo/PdoServerInfoTest.php:
--------------------------------------------------------------------------------
1 | getConnection();
18 | $serverInfo = $db->getServerInfo();
19 |
20 | $this->expectException(NotSupportedException::class);
21 | $this->expectExceptionMessage('Yiisoft\Db\Driver\Pdo\PdoServerInfo::getTimezone is not supported by this DBMS.');
22 |
23 | $serverInfo->getTimezone();
24 | }
25 |
26 | public function testGetVersion(): void
27 | {
28 | $db = $this->getConnection();
29 | $serverInfo = $db->getServerInfo();
30 |
31 | $this->assertIsString($serverInfo->getVersion());
32 |
33 | $db->close();
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/tests/Db/Exception/ConvertExceptionTest.php:
--------------------------------------------------------------------------------
1 | run();
24 |
25 | $this->assertSame($e, $exception->getPrevious());
26 | $this->assertSame('test' . PHP_EOL . 'The SQL being executed was: ' . $rawSql, $exception->getMessage());
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/tests/Db/Exception/ExceptionTest.php:
--------------------------------------------------------------------------------
1 | assertSame('test', $exception->getMessage());
22 | }
23 |
24 | public function testExceptionStringable(): void
25 | {
26 | $exception = new Exception('test');
27 |
28 | $this->assertStringContainsString(
29 | 'Yiisoft\Db\Exception\Exception: test in',
30 | (string) $exception,
31 | );
32 | }
33 |
34 | public function testExceptionWithPrevious(): void
35 | {
36 | $previous = new \Exception('previous');
37 | $exception = new Exception('test', [], $previous);
38 |
39 | $this->assertSame('test', $exception->getMessage());
40 | $this->assertSame($previous, $exception->getPrevious());
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/tests/Db/Expression/ArrayExpressionBuilderTest.php:
--------------------------------------------------------------------------------
1 | 1, 'b' => null], '{"a":1,"b":null}'],
35 | ['[1,2,3]', '[1,2,3]'],
36 | ['{"a":1,"b":null}', '{"a":1,"b":null}'],
37 | ];
38 | }
39 |
40 | #[DataProvider('buildProvider')]
41 | public function testBuild(iterable|LazyArrayInterface|string $value, string $expected): void
42 | {
43 | $db = $this->getConnection();
44 | $qb = $db->getQueryBuilder();
45 |
46 | $params = [];
47 | $builder = new ArrayExpressionBuilder($qb);
48 | $expression = new ArrayExpression($value);
49 |
50 | $this->assertSame(':qp0', $builder->build($expression, $params));
51 | $this->assertEquals([':qp0' => new Param($expected, DataType::STRING)], $params);
52 | }
53 |
54 | public function testBuildNull(): void
55 | {
56 | $db = $this->getConnection();
57 | $qb = $db->getQueryBuilder();
58 |
59 | $params = [];
60 | $builder = new ArrayExpressionBuilder($qb);
61 | $expression = new ArrayExpression(null);
62 |
63 | $this->assertSame('NULL', $builder->build($expression, $params));
64 | $this->assertSame([], $params);
65 | }
66 |
67 | public function testBuildQueryExpression(): void
68 | {
69 | $db = $this->getConnection();
70 | $qb = $db->getQueryBuilder();
71 |
72 | $params = [];
73 | $builder = new ArrayExpressionBuilder($qb);
74 | $expression = new ArrayExpression((new Query($db))->select('json_field')->from('json_table'));
75 |
76 | $this->assertSame('(SELECT [json_field] FROM [json_table])', $builder->build($expression, $params));
77 | $this->assertSame([], $params);
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/tests/Db/Expression/ArrayExpressionTest.php:
--------------------------------------------------------------------------------
1 | assertSame($value, $expression->getValue());
45 | $this->assertSame($type, $expression->getType());
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/tests/Db/Expression/JsonExpressionTest.php:
--------------------------------------------------------------------------------
1 | assertSame($value, $expression->getValue());
45 | $this->assertSame($type, $expression->getType());
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/tests/Db/Expression/StructuredExpressionTest.php:
--------------------------------------------------------------------------------
1 | ColumnBuilder::money(10, 2),
28 | 'currency' => ColumnBuilder::char(3),
29 | ]);
30 |
31 | return [
32 | [[5, 'USD'], null],
33 | [['value' => 5, 'currency' => 'USD'], null],
34 | [new ArrayIterator([5, 'USD']), $column],
35 | [new Query(self::getDb()), 'currency_money_structured'],
36 | [new JsonLazyArray('[5,"USD"]'), null],
37 | [(object) ['value' => 5, 'currency' => 'USD'], null],
38 | ];
39 | }
40 |
41 | #[DataProvider('constructProvider')]
42 | public function testConstruct(
43 | array|object|string $value,
44 | AbstractStructuredColumn|string|null $type = null
45 | ): void {
46 | $expression = new StructuredExpression($value, $type);
47 |
48 | $this->assertSame($value, $expression->getValue());
49 | $this->assertSame($type, $expression->getType());
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/tests/Db/Helper/DbStringHelperTest.php:
--------------------------------------------------------------------------------
1 | assertTrue(DbStringHelper::isReadQuery('SELECT * FROM tbl'));
18 | $this->assertTrue(DbStringHelper::isReadQuery('SELECT * FROM tbl WHERE id=1'));
19 | $this->assertTrue(DbStringHelper::isReadQuery('SELECT * FROM tbl WHERE id=1 LIMIT 1'));
20 | $this->assertTrue(DbStringHelper::isReadQuery('SELECT * FROM tbl WHERE id=1 LIMIT 1 OFFSET 1'));
21 | }
22 |
23 | public static function pascalCaseToIdProvider(): array
24 | {
25 | return [
26 | ['photo\\album_controller', 'Photo\\AlbumController',],
27 | ['photo\\album\\controller', 'Photo\\Album\\Controller',],
28 | ['post_tag', 'PostTag',],
29 | ['post_tag', 'postTag'],
30 | ['foo_ybar', 'fooYBar',],
31 | ];
32 | }
33 |
34 | public function testNormalizeFloat()
35 | {
36 | $this->assertSame('123', DbStringHelper::normalizeFloat(123));
37 | $this->assertSame('-123', DbStringHelper::normalizeFloat(-123));
38 | $this->assertSame('-2.5479E-70', DbStringHelper::normalizeFloat(-2.5479E-70));
39 | $this->assertSame('2.5479E-70', DbStringHelper::normalizeFloat(2.5479E-70));
40 | $this->assertSame('123.42', DbStringHelper::normalizeFloat(123.42));
41 | $this->assertSame('-123.42', DbStringHelper::normalizeFloat(-123.42));
42 | $this->assertSame('123.42', DbStringHelper::normalizeFloat('123.42'));
43 | $this->assertSame('-123.42', DbStringHelper::normalizeFloat('-123.42'));
44 | $this->assertSame('123.42', DbStringHelper::normalizeFloat('123,42'));
45 | $this->assertSame('-123.42', DbStringHelper::normalizeFloat('-123,42'));
46 | $this->assertSame('123123123.123', DbStringHelper::normalizeFloat('123.123.123,123'));
47 | $this->assertSame('123123123.123', DbStringHelper::normalizeFloat('123,123,123.123'));
48 | $this->assertSame('123123123.123', DbStringHelper::normalizeFloat('123 123 123,123'));
49 | $this->assertSame('123123123.123', DbStringHelper::normalizeFloat('123 123 123.123'));
50 | $this->assertSame('-123123123.123', DbStringHelper::normalizeFloat('-123 123 123.123'));
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/tests/Db/Query/QueryGetTableAliasTest.php:
--------------------------------------------------------------------------------
1 | getConnection();
28 | $qb = $db->getQueryBuilder();
29 |
30 | $cdb = new ColumnDefinitionBuilder($qb);
31 |
32 | $column = ColumnBuilder::integer();
33 |
34 | $this->assertEquals($cdb->build($column), $cdb->buildAlter($column));
35 | }
36 |
37 | public function testBuildEmptyDefaultForUuid(): void
38 | {
39 | $db = $this->getConnection();
40 | $qb = $db->getQueryBuilder();
41 |
42 | $cdb = new class ($qb) extends AbstractColumnDefinitionBuilder {
43 | protected function getDbType(ColumnInterface $column): string
44 | {
45 | return 'uuid';
46 | }
47 | };
48 |
49 | $column = ColumnBuilder::uuidPrimaryKey();
50 |
51 | $this->assertSame('uuid PRIMARY KEY', $cdb->build($column));
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/tests/Db/QueryBuilder/Condition/AndConditionTest.php:
--------------------------------------------------------------------------------
1 | 1, 'b' => 2]);
20 |
21 | $this->assertSame(['a' => 1, 'b' => 2], $andCondition->getExpressions());
22 | }
23 |
24 | public function testGetOperator(): void
25 | {
26 | $andCondition = new AndCondition(['a' => 1, 'b' => 2]);
27 |
28 | $this->assertSame('AND', $andCondition->getOperator());
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/tests/Db/QueryBuilder/Condition/BetweenConditionTest.php:
--------------------------------------------------------------------------------
1 | assertSame('date', $betweenCondition->getColumn());
23 | $this->assertSame('BETWEEN', $betweenCondition->getOperator());
24 | $this->assertSame(1, $betweenCondition->getIntervalStart());
25 | $this->assertSame(2, $betweenCondition->getIntervalEnd());
26 | }
27 |
28 | public function testFromArrayDefinition(): void
29 | {
30 | $betweenCondition = BetweenCondition::fromArrayDefinition('BETWEEN', ['date', 1, 2]);
31 |
32 | $this->assertSame('date', $betweenCondition->getColumn());
33 | $this->assertSame('BETWEEN', $betweenCondition->getOperator());
34 | $this->assertSame(1, $betweenCondition->getIntervalStart());
35 | $this->assertSame(2, $betweenCondition->getIntervalEnd());
36 | }
37 |
38 | public function testFromArrayDefinitionExceptionWithoutOperands(): void
39 | {
40 | $this->expectException(InvalidArgumentException::class);
41 | $this->expectExceptionMessage("Operator 'between' requires three operands.");
42 |
43 | BetweenCondition::fromArrayDefinition('between', []);
44 | }
45 |
46 | public function testFromArrayDefinitionExceptionColumns(): void
47 | {
48 | $this->expectException(InvalidArgumentException::class);
49 | $this->expectExceptionMessage("Operator 'between' requires column to be string or ExpressionInterface.");
50 |
51 | BetweenCondition::fromArrayDefinition('between', [1, 'min_value', 'max_value']);
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/tests/Db/QueryBuilder/Condition/Builder/BetweenColumnsConditionBuilderTest.php:
--------------------------------------------------------------------------------
1 | getConnection();
24 |
25 | $betweenColumnsCondition = new BetweenColumnsCondition(42, 'BETWEEN', '1', '100');
26 | $params = [];
27 |
28 | $this->assertSame(
29 | ':qp0 BETWEEN [1] AND [100]',
30 | (new BetweenColumnsConditionBuilder($db->getQueryBuilder()))->build($betweenColumnsCondition, $params)
31 | );
32 |
33 | $this->assertEquals([':qp0' => 42], $params);
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/tests/Db/QueryBuilder/Condition/Builder/LikeConditionBuilderTest.php:
--------------------------------------------------------------------------------
1 | getConnection();
25 |
26 | $likeCondition = new LikeCondition('column', 'invalid', 'value');
27 |
28 | $this->expectException(InvalidArgumentException::class);
29 | $this->expectExceptionMessage('Invalid operator in like condition: "INVALID"');
30 |
31 | (new LikeConditionBuilder($db->getQueryBuilder()))->build($likeCondition);
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/tests/Db/QueryBuilder/Condition/ExistsConditionTest.php:
--------------------------------------------------------------------------------
1 | createMock(ConnectionInterface::class)))
23 | ->select('id')
24 | ->from('users')
25 | ->where(['active' => 1]);
26 | $existCondition = new ExistsCondition('EXISTS', $query);
27 |
28 | $this->assertSame('EXISTS', $existCondition->getOperator());
29 | $this->assertSame($query, $existCondition->getQuery());
30 | }
31 |
32 | public function testFromArrayDefinition(): void
33 | {
34 | $query = (new Query($this->createMock(ConnectionInterface::class)))
35 | ->select('id')
36 | ->from('users')
37 | ->where(['active' => 1]);
38 | $existCondition = ExistsCondition::fromArrayDefinition('EXISTS', [$query]);
39 |
40 | $this->assertSame('EXISTS', $existCondition->getOperator());
41 | $this->assertSame($query, $existCondition->getQuery());
42 | }
43 |
44 | public function testFromArrayDefinitionExceptionQuery(): void
45 | {
46 | $this->expectException(InvalidArgumentException::class);
47 | $this->expectExceptionMessage('Sub query for EXISTS operator must be a Query object.');
48 |
49 | ExistsCondition::fromArrayDefinition('EXISTS', []);
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/tests/Db/QueryBuilder/Condition/HashConditionTest.php:
--------------------------------------------------------------------------------
1 | false, 'active' => true]);
20 |
21 | $this->assertSame(['expired' => false, 'active' => true], $hashCondition->getHash());
22 | }
23 |
24 | public function testFromArrayDefinition(): void
25 | {
26 | $hashCondition = HashCondition::fromArrayDefinition('AND', ['expired' => false, 'active' => true]);
27 |
28 | $this->assertSame(['expired' => false, 'active' => true], $hashCondition->getHash());
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/tests/Db/QueryBuilder/Condition/InConditionTest.php:
--------------------------------------------------------------------------------
1 | assertSame('id', $inCondition->getColumn());
23 | $this->assertSame('IN', $inCondition->getOperator());
24 | $this->assertSame([1, 2, 3], $inCondition->getValues());
25 | }
26 |
27 | public function testFromArrayDefinition(): void
28 | {
29 | $inCondition = InCondition::fromArrayDefinition('IN', ['id', [1, 2, 3]]);
30 |
31 | $this->assertSame('id', $inCondition->getColumn());
32 | $this->assertSame('IN', $inCondition->getOperator());
33 | $this->assertSame([1, 2, 3], $inCondition->getValues());
34 | }
35 |
36 | public function testFromArrayDefinitionException(): void
37 | {
38 | $this->expectException(InvalidArgumentException::class);
39 | $this->expectExceptionMessage("Operator 'IN' requires two operands.");
40 |
41 | InCondition::fromArrayDefinition('IN', []);
42 | }
43 |
44 | public function testFromArrayDefinitionExceptionColumn(): void
45 | {
46 | $this->expectException(InvalidArgumentException::class);
47 | $this->expectExceptionMessage("Operator 'IN' requires column to be string, array or Iterator.");
48 |
49 | InCondition::fromArrayDefinition('IN', [1, [1, 2, 3]]);
50 | }
51 |
52 | public function testFromArrayDefinitionExceptionValues(): void
53 | {
54 | $this->expectException(InvalidArgumentException::class);
55 | $this->expectExceptionMessage(
56 | "Operator 'IN' requires values to be array, Iterator, int or QueryInterface."
57 | );
58 |
59 | InCondition::fromArrayDefinition('IN', ['id', false]);
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/tests/Db/QueryBuilder/Condition/NotConditionTest.php:
--------------------------------------------------------------------------------
1 | assertSame('id = 1', $notCondition->getCondition());
23 | }
24 |
25 | public function testFromArrayDefinition(): void
26 | {
27 | $notCondition = NotCondition::fromArrayDefinition('NOT', ['id = 1']);
28 |
29 | $this->assertSame('id = 1', $notCondition->getCondition());
30 | }
31 |
32 | public function testFromArrayDefinitionException(): void
33 | {
34 | $this->expectException(InvalidArgumentException::class);
35 | $this->expectExceptionMessage("Operator 'NOT' requires exactly one operand.");
36 |
37 | NotCondition::fromArrayDefinition('NOT', []);
38 | }
39 |
40 | public function testFromArrayDefinitionExceptionCondition(): void
41 | {
42 | $this->expectException(InvalidArgumentException::class);
43 | $this->expectExceptionMessage(
44 | "Operator 'NOT' requires condition to be array, string, null or ExpressionInterface."
45 | );
46 |
47 | NotCondition::fromArrayDefinition('NOT', [false]);
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/tests/Db/QueryBuilder/Condition/SimpleConditionTest.php:
--------------------------------------------------------------------------------
1 | assertSame('id', $simpleCondition->getColumn());
23 | $this->assertSame('=', $simpleCondition->getOperator());
24 | $this->assertSame(1, $simpleCondition->getValue());
25 | }
26 |
27 | public function testFromArrayDefinition(): void
28 | {
29 | $simpleCondition = SimpleCondition::fromArrayDefinition('=', ['id', 1]);
30 |
31 | $this->assertSame('id', $simpleCondition->getColumn());
32 | $this->assertSame('=', $simpleCondition->getOperator());
33 | $this->assertSame(1, $simpleCondition->getValue());
34 | }
35 |
36 | public function testFromArrayDefinitionColumnException(): void
37 | {
38 | $this->expectException(InvalidArgumentException::class);
39 | $this->expectExceptionMessage("Operator '=' requires two operands.");
40 |
41 | SimpleCondition::fromArrayDefinition('=', []);
42 | }
43 |
44 | public function testFromArrayDefinitionValueException(): void
45 | {
46 | $this->expectException(InvalidArgumentException::class);
47 | $this->expectExceptionMessage("Operator 'IN' requires two operands.");
48 |
49 | SimpleCondition::fromArrayDefinition('IN', ['column']);
50 | }
51 |
52 | public function testFromArrayDefinitionExceptionColumn(): void
53 | {
54 | $this->expectException(InvalidArgumentException::class);
55 | $this->expectExceptionMessage(
56 | "Operator '=' requires column to be string or ExpressionInterface."
57 | );
58 |
59 | SimpleCondition::fromArrayDefinition('=', [1, 1]);
60 | }
61 |
62 | public function testNullSecondOperand(): void
63 | {
64 | $condition = SimpleCondition::fromArrayDefinition('=', ['id', null]);
65 |
66 | $this->assertNull($condition->getValue());
67 |
68 | $condition2 = new SimpleCondition('name', 'IS NOT', null);
69 |
70 | $this->assertSame('IS NOT', $condition2->getOperator());
71 | $this->assertNull($condition2->getValue());
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/tests/Db/Schema/Column/ColumnBuilderTest.php:
--------------------------------------------------------------------------------
1 | expectException(InvalidArgumentException::class);
21 | $this->expectExceptionMessage('Structured value must be a valid string representation.');
22 |
23 | $lazyArray->getValue();
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/tests/Db/Schema/Data/LazyArrayTest.php:
--------------------------------------------------------------------------------
1 | expectException(InvalidArgumentException::class);
21 | $this->expectExceptionMessage('Array value must be a valid string representation.');
22 |
23 | $lazyArray->getValue();
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/tests/Db/Schema/TableSchemaTest.php:
--------------------------------------------------------------------------------
1 | '']],
13 | ['int', ['type' => 'int']],
14 | ['int(10)', ['type' => 'int', 'size' => 10]],
15 | ['int UNSIGNED', ['type' => 'int', 'unsigned' => true]],
16 | ['int UNIQUE', ['type' => 'int', 'unique' => true]],
17 | ['int(10) UNSIGNED', ['type' => 'int', 'size' => 10, 'unsigned' => true]],
18 | ['int(10) UNSIGNED NOT NULL', ['type' => 'int', 'size' => 10, 'unsigned' => true, 'notNull' => true]],
19 | ['int(10) NOT NULL', ['type' => 'int', 'size' => 10, 'notNull' => true]],
20 | ['text NOT NULL', ['type' => 'text', 'notNull' => true]],
21 | ['text NULL', ['type' => 'text', 'notNull' => false]],
22 | ['text COLLATE utf8mb4', ['type' => 'text', 'extra' => 'COLLATE utf8mb4']],
23 | ['text DEFAULT NULL', ['type' => 'text', 'defaultValueRaw' => 'NULL']],
24 | ["text DEFAULT 'value'", ['type' => 'text', 'defaultValueRaw' => "'value'"]],
25 | ['varchar(36) DEFAULT uuid()', ['type' => 'varchar', 'size' => 36, 'defaultValueRaw' => 'uuid()']],
26 | ['varchar(36) DEFAULT uuid()::varchar(36)', ['type' => 'varchar', 'size' => 36, 'defaultValueRaw' => 'uuid()::varchar(36)']],
27 | ['int DEFAULT (1 + 2)', ['type' => 'int', 'defaultValueRaw' => '(1 + 2)']],
28 | ["int COMMENT '''Quoted'' comment'", ['type' => 'int', 'comment' => "'Quoted' comment"]],
29 | ['int CHECK (value > (1 + 5))', ['type' => 'int', 'check' => 'value > (1 + 5)']],
30 | ["enum('a','b','c')", ['type' => 'enum', 'enumValues' => ['a', 'b', 'c']]],
31 | ["enum('a','b','c') NOT NULL", ['type' => 'enum', 'enumValues' => ['a', 'b', 'c'], 'notNull' => true]],
32 | ['int[]', ['type' => 'int', 'dimension' => 1]],
33 | ['string(126)[][]', ['type' => 'string', 'size' => 126, 'dimension' => 2]],
34 | ];
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/tests/Provider/CommandPDOProvider.php:
--------------------------------------------------------------------------------
1 | '1',
23 | 'email' => 'user1@example.com',
24 | 'name' => 'user1',
25 | 'address' => 'address1',
26 | 'status' => '1',
27 | 'profile_id' => '1',
28 | ],
29 | ],
30 | ];
31 | }
32 |
33 | public static function bindParamsNonWhere(): array
34 | {
35 | return [
36 | [
37 | <<value = strtolower($v);
21 | }
22 | } else {
23 | $this->value = strtolower($value);
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/tests/Support/AnyValue.php:
--------------------------------------------------------------------------------
1 | allowedValues as $value) {
31 | $this->allowedValues[] = VarDumper::create($value)->asString();
32 | }
33 |
34 | $expectedAsString = implode(', ', $allowedValues);
35 |
36 | return "is one of $expectedAsString";
37 | }
38 |
39 | protected function matches($other): bool
40 | {
41 | return in_array($other, $this->allowedValues);
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/tests/Support/JsonSerializableObject.php:
--------------------------------------------------------------------------------
1 | data;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/tests/Support/StringEnum.php:
--------------------------------------------------------------------------------
1 | value;
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/tests/Support/Stub/Column.php:
--------------------------------------------------------------------------------
1 | isType($dbType) ? $dbType : ColumnType::STRING;
19 | }
20 |
21 | protected function isDbType(string $dbType): bool
22 | {
23 | return match ($dbType) {
24 | 'string', 'array' => false,
25 | 'varchar' => true,
26 | default => $this->isType($dbType),
27 | };
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/tests/Support/Stub/Command.php:
--------------------------------------------------------------------------------
1 | db->getQueryBuilder();
21 | }
22 |
23 | protected function internalExecute(): void
24 | {
25 | throw new NotSupportedException(__METHOD__ . ' is not supported by this DBMS.');
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/tests/Support/Stub/Connection.php:
--------------------------------------------------------------------------------
1 | setSql($sql);
25 | }
26 |
27 | if ($this->logger !== null) {
28 | $command->setLogger($this->logger);
29 | }
30 |
31 | if ($this->profiler !== null) {
32 | $command->setProfiler($this->profiler);
33 | }
34 |
35 | return $command->bindValues($params);
36 | }
37 |
38 | public function createTransaction(): TransactionInterface
39 | {
40 | return new Transaction($this);
41 | }
42 |
43 | public function getColumnFactory(): ColumnFactoryInterface
44 | {
45 | return $this->columnFactory ??= new ColumnFactory();
46 | }
47 |
48 | public function getQueryBuilder(): QueryBuilderInterface
49 | {
50 | return $this->queryBuilder ??= new QueryBuilder($this);
51 | }
52 |
53 | public function getQuoter(): QuoterInterface
54 | {
55 | return $this->quoter ??= new Quoter(['[', ']'], ['[', ']'], $this->getTablePrefix());
56 | }
57 |
58 | public function getSchema(): SchemaInterface
59 | {
60 | return $this->schema ??= new Schema($this, $this->schemaCache);
61 | }
62 |
63 | protected function initConnection(): void
64 | {
65 | if ($this->getEmulatePrepare() !== null) {
66 | $this->driver->attributes([PDO::ATTR_EMULATE_PREPARES => $this->getEmulatePrepare()]);
67 | }
68 |
69 | $this->pdo = $this->driver->createConnection();
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/tests/Support/Stub/DDLQueryBuilder.php:
--------------------------------------------------------------------------------
1 | getQuoter();
15 | $schema = $db->getSchema();
16 |
17 | parent::__construct(
18 | $db,
19 | new DDLQueryBuilder($this, $quoter, $schema),
20 | new DMLQueryBuilder($this, $quoter, $schema),
21 | new DQLQueryBuilder($this, $quoter),
22 | new ColumnDefinitionBuilder($this),
23 | );
24 | }
25 |
26 | protected function createSqlParser(string $sql): SqlParser
27 | {
28 | return new SqlParser($sql);
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/tests/Support/Stub/SqlParser.php:
--------------------------------------------------------------------------------
1 | length - 1;
15 |
16 | while ($this->position < $length) {
17 | $pos = $this->position++;
18 |
19 | match ($this->sql[$pos]) {
20 | ':' => ($word = $this->parseWord()) === ''
21 | ? $this->skipChars(':')
22 | : $result = ':' . $word,
23 | '"', "'" => $this->skipQuotedWithoutEscape($this->sql[$pos]),
24 | '-' => $this->sql[$this->position] === '-'
25 | ? ++$this->position && $this->skipToAfterChar("\n")
26 | : null,
27 | '/' => $this->sql[$this->position] === '*'
28 | ? ++$this->position && $this->skipToAfterString('*/')
29 | : null,
30 | default => null,
31 | };
32 |
33 | if ($result !== null) {
34 | $position = $pos;
35 |
36 | return $result;
37 | }
38 | }
39 |
40 | return null;
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/tests/Support/Stub/TableSchema.php:
--------------------------------------------------------------------------------
1 | getDriver(), DbHelper::getSchemaCache());
18 |
19 | if ($fixture) {
20 | DbHelper::loadFixture($db, __DIR__ . '/Fixture/db.sql');
21 | }
22 |
23 | return $db;
24 | }
25 |
26 | protected static function getDb(): PdoConnectionInterface
27 | {
28 | return new Stub\Connection(new PdoDriver('sqlite::memory:'), DbHelper::getSchemaCache());
29 | }
30 |
31 | protected function getDriver(): PdoDriverInterface
32 | {
33 | return new PdoDriver($this->dsn);
34 | }
35 |
36 | protected function getDriverName(): string
37 | {
38 | return 'db';
39 | }
40 |
41 | protected function setDsn(string $dsn): void
42 | {
43 | $this->dsn = $dsn;
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/tests/Support/TraversableObject.php:
--------------------------------------------------------------------------------
1 | data[$this->position];
36 | }
37 |
38 | public function next(): void
39 | {
40 | $this->position++;
41 | }
42 |
43 | public function key(): int
44 | {
45 | return $this->position;
46 | }
47 |
48 | public function valid(): bool
49 | {
50 | return array_key_exists($this->position, $this->data);
51 | }
52 |
53 | public function rewind(): void
54 | {
55 | $this->position = 0;
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/tests/Support/string.txt:
--------------------------------------------------------------------------------
1 | string
--------------------------------------------------------------------------------