diff --git a/.circleci/config.yml b/.circleci/config.yml
index 84ff0344..ff7e4471 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -26,7 +26,7 @@ jobs:
if [[ ! -f vendor/autoload.php ]]; then
curl https://getcomposer.org/composer-stable.phar --location --silent --output /usr/bin/composer; \
chmod +x /usr/bin/composer; \
- composer install --no-progress --no-interaction; \
+ composer install --no-progress --no-interaction --ignore-platform-req=ext-xdebug; \
fi
- save_cache:
key: composer-{{ checksum "composer.json" }}-{{ checksum "composer.lock" }}
@@ -93,7 +93,7 @@ jobs:
if [[ ! -f vendor/autoload.php ]]; then
curl https://getcomposer.org/composer-stable.phar --location --silent --output /usr/bin/composer; \
chmod +x /usr/bin/composer; \
- composer install --no-progress --no-interaction; \
+ composer install --no-progress --no-interaction --ignore-platform-req=ext-xdebug; \
fi
- save_cache:
key: composer-{{ checksum "composer.json" }}-{{ checksum "composer.lock" }}
diff --git a/.gitignore b/.gitignore
index e6d98de3..de392a34 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,6 +7,7 @@ composer.lock
.phpunit.cache
.idea
var/
+cov.xml
# For these quality tools, the *.dist form should be in VCS and the forms
# below should be reserved for local customizations.
diff --git a/Makefile b/Makefile
index f5ac45f5..76ca9a4a 100644
--- a/Makefile
+++ b/Makefile
@@ -1,12 +1,15 @@
+dev:
+ nix shell github:loophp/nix-shell --impure
+
phpcs:
- vendor/bin/phpcs -n
+ vendor/bin/phpcs -n --parallel=8
phpcbf:
vendor/bin/phpcbf
phpstan:
vendor/bin/phpstan clear-result-cache
- vendor/bin/phpstan analyse
+ php -d memory_limit=2G vendor/bin/phpstan analyse
phpcsfixer:
vendor/bin/php-cs-fixer fix --dry-run --allow-risky=yes --diff
@@ -15,4 +18,12 @@ test:
vendor/bin/phpunit --testdox
infection:
- vendor/bin/infection
+ XDEBUG_MODE=coverage vendor/bin/infection -j8
+
+PHPUNIT_REPORT_PATH = /tmp/phpunit_coverage_report
+coverage:
+ XDEBUG_MODE=coverage vendor/bin/phpunit \
+ --coverage-clover cov.xml \
+ --coverage-filter src \
+ --coverage-html $(PHPUNIT_REPORT_PATH)
+ xdg-open $(PHPUNIT_REPORT_PATH)/index.html
diff --git a/changelog.MD b/changelog.MD
index c4a0083f..01a66d48 100644
--- a/changelog.MD
+++ b/changelog.MD
@@ -1,5 +1,14 @@
# Changelog
+## Next version
+
+**Bugfixes**
+* Use backticks `` ` `` for table and column names in `Wizaplace\Etl\Database\*` classes (see https://github.com/wizacode/php-etl/issues/117)
+
+**Miscellaneous**
+* Nix shell dev environment `Makefile` command
+* Code coverage `Makefile` command
+
## 2.3
* Add the PHPDoc _@mixin Pipeline_ to the Etl class.
* Add _mixed_ return type to _Wizaplace\Etl\Row\offsetGet_.
diff --git a/composer.json b/composer.json
index 52cc4a91..e2e595c5 100644
--- a/composer.json
+++ b/composer.json
@@ -1,7 +1,16 @@
{
"name": "wizaplace/php-etl",
"description": "Extract, Transform and Load data using this PHP written migration library.",
- "keywords": ["etl", "extract", "transform", "load", "extraction", "transformation", "data", "symfony"],
+ "keywords": [
+ "etl",
+ "extract",
+ "transform",
+ "load",
+ "extraction",
+ "transformation",
+ "data",
+ "symfony"
+ ],
"license": "MIT",
"authors": [
{
@@ -22,7 +31,7 @@
},
"require": {
"php": "~8.1",
- "softcreatr/jsonpath": "^0.7.2"
+ "softcreatr/jsonpath": "^0.8.3"
},
"autoload": {
"psr-4": {
@@ -34,12 +43,13 @@
"ext-json": "*",
"ext-pdo_sqlite": "*",
"ext-xmlreader": "*",
+ "ext-xdebug": "*",
"friendsofphp/php-cs-fixer": "^3.4",
"infection/infection": ">=0.15",
"phpstan/extension-installer": "^1.0",
- "phpstan/phpstan": "^0.12.100",
- "phpstan/phpstan-deprecation-rules": "^0.12.6",
- "phpstan/phpstan-strict-rules": "^0.12.11",
+ "phpstan/phpstan": "^1.11",
+ "phpstan/phpstan-deprecation-rules": "^1.1.4",
+ "phpstan/phpstan-strict-rules": "^1.5.1",
"phpunit/phpunit": ">=8",
"squizlabs/php_codesniffer": "^3.5"
},
@@ -64,7 +74,10 @@
"phpstan --memory-limit=256M analyze"
],
"test": "phpunit",
- "check": ["@scan", "@test"]
+ "check": [
+ "@scan",
+ "@test"
+ ]
},
"minimum-stability": "dev",
"prefer-stable": true
diff --git a/docs/Extractors/Table.md b/docs/Extractors/Table.md
index 29130feb..debbcdc8 100644
--- a/docs/Extractors/Table.md
+++ b/docs/Extractors/Table.md
@@ -40,7 +40,7 @@ $options = [Table::CONNECTION => 'app'];
Array of conditions, each condition is either:
- `key` equals `value` , or
-- `key` _comparesTo_ `value` (comparesTo can be: =, <, <=, =>, >, or <>).
+- `key` _comparesTo_ `value` (comparesTo can be: =, <, <=, >=, >, or <>).
If you need more flexibility in the query creation, you may use the [Query extractor](Query.md).
diff --git a/docs/img/etl.svg b/docs/img/etl.svg
index 53524f1e..fdb70833 100644
--- a/docs/img/etl.svg
+++ b/docs/img/etl.svg
@@ -321,9 +321,9 @@
id="text896-3"
y="-90.151337"
x="35.713844"
- style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:7.05555582px;line-height:1.25;font-family:'Roboto Slab';-inkscape-font-specification:'Roboto Slab';text-align:justify;letter-spacing:0px;word-spacing:0px;text-anchor:start;fill:url(#fond);fill-opacity:1;stroke:none;stroke-width:0.52916664"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:7.05555582px;line-height:1.25;font-family:'sans-serif';-inkscape-font-specification:'sans-serif';text-align:justify;letter-spacing:0px;word-spacing:0px;text-anchor:start;fill:url(#fond);fill-opacity:1;stroke:none;stroke-width:0.52916664"
xml:space="preserve">
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:'Monospace';-inkscape-font-specification:'Monospace';stroke-width:0.26458332" />
@@ -398,7 +398,7 @@
style="opacity:1;fill:url(#extractor);fill-opacity:1;stroke:none;stroke-width:0.26458332;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:normal" />
Extractor
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4px;font-family:'Monospace';-inkscape-font-specification:'Monospace';fill:url(#fond);stroke-width:0.26458332">Extractor
Transformer
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4px;font-family:'Monospace';-inkscape-font-specification:'Monospace';text-align:end;text-anchor:end;fill:url(#fond);stroke-width:0.26458332">Transformer
ASSETS
COLORS
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:'Monospace';-inkscape-font-specification:'Monospace';fill:url(#bordure);stroke-width:1.19711387">COLORS
Json
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:11.86666679px;font-family:'Monospace';-inkscape-font-specification:'Monospace';fill:url(#fond);stroke-width:0.52916664">Json
-
-
- src
-
-
+
./tests/Unit
+
+
+ src
+
+
diff --git a/src/Database/Helpers.php b/src/Database/Helpers.php
new file mode 100644
index 00000000..47951d61
--- /dev/null
+++ b/src/Database/Helpers.php
@@ -0,0 +1,38 @@
+
+ * @copyright Copyright (c) Wizacha
+ * @copyright Copyright (c) Leonardo Marquine
+ * @license MIT
+ */
+
+declare(strict_types=1);
+
+namespace Wizaplace\Etl\Database;
+
+class Helpers
+{
+ public const DEFAULT_MASK = '{column}';
+ public const BACKTICKED_MASK = '`{column}`';
+
+ /**
+ * Join array elements using a string mask.
+ */
+ public static function implode(
+ array $columns,
+ string $mask = self::DEFAULT_MASK,
+ array $ignoreMask = ['*'] // No backticks for *
+ ): string {
+ $columns = array_map(
+ function ($column) use ($mask, $ignoreMask): string {
+ return \in_array($column, $ignoreMask, true)
+ ? $column
+ : str_replace(self::DEFAULT_MASK, $column, $mask);
+ },
+ $columns
+ );
+
+ return implode(', ', $columns);
+ }
+}
diff --git a/src/Database/Query.php b/src/Database/Query.php
index 65f56b8e..2a892d7a 100644
--- a/src/Database/Query.php
+++ b/src/Database/Query.php
@@ -30,8 +30,10 @@ class Query
/**
* The where constraints for the query.
+ *
+ * @var WhereInterface[]
*/
- protected array $wheres = [];
+ protected array $whereQueries = [];
/**
* Create a new Query instance.
@@ -76,11 +78,13 @@ public function getBindings(): array
*
* @return $this
*/
- public function select(string $table, array $columns = ['*']): Query
+ public function select(string $table, array $columns = null): Query
{
- $columns = $this->implode($columns);
+ $columns = \is_null($columns)
+ ? '*'
+ : Helpers::implode($columns, Helpers::BACKTICKED_MASK);
- $this->query[] = "select $columns from $table";
+ $this->query[] = "SELECT $columns FROM `$table`";
return $this;
}
@@ -94,11 +98,11 @@ public function insert(string $table, array $columns): Query
{
$this->bindings = array_merge($this->bindings, array_values($columns));
- $values = $this->implode($columns, '?');
+ $values = Helpers::implode($columns, '?');
- $columns = $this->implode(array_keys($columns));
+ $columns = Helpers::implode(array_keys($columns), Helpers::BACKTICKED_MASK);
- $this->query[] = "insert into $table ($columns) values ($values)";
+ $this->query[] = "INSERT INTO `$table` ($columns) VALUES ($values)";
return $this;
}
@@ -112,9 +116,12 @@ public function update(string $table, array $columns): Query
{
$this->bindings = array_merge($this->bindings, array_values($columns));
- $columns = $this->implode(array_keys($columns), '{column} = ?');
+ $columns = Helpers::implode(
+ array_keys($columns),
+ sprintf('%s = ?', Helpers::BACKTICKED_MASK),
+ );
- $this->query[] = "update $table set $columns";
+ $this->query[] = "UPDATE `$table` SET $columns";
return $this;
}
@@ -126,7 +133,7 @@ public function update(string $table, array $columns): Query
*/
public function delete(string $table): Query
{
- $this->query[] = "delete from {$table}";
+ $this->query[] = "DELETE FROM `$table`";
return $this;
}
@@ -139,15 +146,19 @@ public function delete(string $table): Query
public function where(array $columns): Query
{
foreach ($columns as $column => $value) {
- $condition = ['type' => 'Where', 'column' => $column, 'boolean' => 'and'];
if (is_scalar($value)) {
- $condition['operator'] = '=';
- $condition['value'] = $value;
+ $operator = WhereOperator::Equal;
} else {
- $condition['operator'] = $value[0];
- $condition['value'] = $value[1];
+ $operator = WhereOperator::from($value[0]);
+ $value = $value[1];
}
- $this->wheres[] = $condition;
+
+ $this->whereQueries[] = new WhereQuery(
+ boolean: WhereBoolean::And,
+ operator: $operator,
+ column: $column,
+ value: $value,
+ );
}
return $this;
@@ -160,24 +171,25 @@ public function where(array $columns): Query
*
* @return $this
*/
- public function whereIn($column, array $values, string $operator = 'in'): Query
- {
+ public function whereIn(
+ $column,
+ array $values,
+ WhereOperator $operator = WhereOperator::In
+ ): Query {
if (is_string($column)) {
- $this->wheres[] = [
- 'type' => 'WhereIn',
- 'column' => $column,
- 'values' => $values,
- 'operator' => $operator,
- 'boolean' => 'and',
- ];
+ $this->whereQueries[] = new WhereInQuery(
+ boolean: WhereBoolean::And,
+ operator: $operator,
+ column: $column,
+ multipleValues: $values,
+ );
} else {
- $this->wheres[] = [
- 'type' => 'CompositeWhereIn',
- 'columns' => $column,
- 'values' => $values,
- 'operator' => $operator,
- 'boolean' => 'and',
- ];
+ $this->whereQueries[] = new WhereInCompositeQuery(
+ boolean: WhereBoolean::And,
+ operator: $operator,
+ multipleColumns: $column, // :|
+ multipleValues: $values,
+ );
}
return $this;
@@ -192,7 +204,7 @@ public function whereIn($column, array $values, string $operator = 'in'): Query
*/
public function whereNotIn($column, array $values): Query
{
- return $this->whereIn($column, $values, 'not in');
+ return $this->whereIn($column, $values, WhereOperator::NotIn);
}
/**
@@ -200,96 +212,20 @@ public function whereNotIn($column, array $values): Query
*/
protected function compileWheres(): void
{
- if ([] === $this->wheres) {
+ if ([] === $this->whereQueries) {
return;
}
- $this->query[] = 'where';
-
- foreach ($this->wheres as $index => $condition) {
- $method = 'compile' . $condition['type'];
-
- if (0 == $index) {
- $condition['boolean'] = '';
- }
-
- $this->query[] = trim($this->{$method}($condition));
- }
- }
-
- /**
- * Compile the basic where statement.
- */
- protected function compileWhere(array $where): string
- {
- // All these if, empty, are here to clean the legacy code before the fork. See the git history.
- $boolean = array_key_exists('boolean', $where) ? $where['boolean'] : null;
- $column = array_key_exists('column', $where) ? $where['column'] : null;
- $operator = array_key_exists('operator', $where) ? $where['operator'] : null;
- $value = array_key_exists('value', $where) ? $where['value'] : null;
-
- $this->bindings[] = $value;
-
- return "$boolean $column $operator ?";
- }
-
- /**
- * Compile the where in statement.
- */
- protected function compileWhereIn(array $where): string
- {
- // All these if, empty, are here to clean the legacy code before the fork. See the git history.
- $boolean = array_key_exists('boolean', $where) ? $where['boolean'] : null;
- $column = array_key_exists('column', $where) ? $where['column'] : null;
- $operator = array_key_exists('operator', $where) ? $where['operator'] : null;
- $values = array_key_exists('values', $where) ? $where['values'] : null;
-
- $this->bindings = array_merge($this->bindings, $values);
-
- $parameters = $this->implode($values, '?');
-
- return "$boolean $column $operator ($parameters)";
- }
-
- /**
- * Compile the composite where in statement.
- */
- protected function compileCompositeWhereIn(array $where): string
- {
- // All these if, empty, are here to clean the legacy code before the fork. See the git history.
- $boolean = array_key_exists('boolean', $where) ? $where['boolean'] : null;
- $columns = array_key_exists('columns', $where) ? $where['columns'] : null;
- $operator = array_key_exists('operator', $where) ? $where['operator'] : null;
- $values = array_key_exists('values', $where) ? $where['values'] : null;
-
- sort($columns);
-
- $parameters = [];
+ $this->query[] = 'WHERE';
- foreach ($values as $value) {
- ksort($value);
+ foreach ($this->whereQueries as $index => $whereQuery) {
+ $result = $whereQuery->compile($index);
- $this->bindings = array_merge($this->bindings, array_values($value));
-
- $parameters[] = "({$this->implode($value, '?')})";
+ $this->query[] = $result->output;
+ $this->bindings = \array_merge(
+ $this->bindings,
+ $result->bindings,
+ );
}
-
- $parameters = $this->implode($parameters);
-
- $columns = $this->implode($columns);
-
- return "$boolean ($columns) $operator ($parameters)";
- }
-
- /**
- * Join array elements using a string mask.
- */
- protected function implode(array $columns, string $mask = '{column}'): string
- {
- $columns = array_map(function ($column) use ($mask): string {
- return str_replace('{column}', $column, $mask);
- }, $columns);
-
- return implode(', ', $columns);
}
}
diff --git a/src/Database/Statement.php b/src/Database/Statement.php
index ddd5983d..b52b38b9 100644
--- a/src/Database/Statement.php
+++ b/src/Database/Statement.php
@@ -26,7 +26,7 @@ class Statement
/**
* The where constraints for the query.
*/
- protected array $wheres = [];
+ protected array $whereStatements = [];
/**
* Create a new Statement instance.
@@ -68,11 +68,13 @@ public function toSql(): string
*
* @return $this
*/
- public function select(string $table, array $columns = ['*']): Statement
+ public function select(string $table, array $columns = null): Statement
{
- $columns = $this->implode($columns);
+ $columns = \is_null($columns)
+ ? '*'
+ : Helpers::implode($columns, Helpers::BACKTICKED_MASK);
- $this->query[] = "select $columns from $table";
+ $this->query[] = "SELECT $columns FROM `$table`";
return $this;
}
@@ -84,11 +86,11 @@ public function select(string $table, array $columns = ['*']): Statement
*/
public function insert(string $table, array $columns): Statement
{
- $values = $this->implode($columns, ':{column}');
+ $values = Helpers::implode($columns, ':{column}');
- $columns = $this->implode($columns);
+ $columns = Helpers::implode($columns, Helpers::BACKTICKED_MASK);
- $this->query[] = "insert into $table ($columns) values ($values)";
+ $this->query[] = "INSERT INTO `$table` ($columns) values ($values)";
return $this;
}
@@ -100,9 +102,16 @@ public function insert(string $table, array $columns): Statement
*/
public function update(string $table, array $columns): Statement
{
- $columns = $this->implode($columns, '{column} = :{column}');
+ $columns = Helpers::implode(
+ $columns,
+ \sprintf(
+ '%s = :%s',
+ Helpers::BACKTICKED_MASK,
+ Helpers::DEFAULT_MASK,
+ )
+ );
- $this->query[] = "update $table set $columns";
+ $this->query[] = "UPDATE `$table` SET $columns";
return $this;
}
@@ -114,7 +123,7 @@ public function update(string $table, array $columns): Statement
*/
public function delete(string $table): Statement
{
- $this->query[] = "delete from $table";
+ $this->query[] = "DELETE FROM `$table`";
return $this;
}
@@ -127,9 +136,11 @@ public function delete(string $table): Statement
public function where(array $columns): Statement
{
foreach ($columns as $column) {
- $this->wheres[] = [
- 'type' => 'Where', 'column' => $column, 'operator' => '=', 'boolean' => 'and',
- ];
+ $this->whereStatements[] = new WhereStatement(
+ boolean: WhereBoolean::And,
+ operator: WhereOperator::Equal,
+ column: $column,
+ );
}
return $this;
@@ -140,45 +151,16 @@ public function where(array $columns): Statement
*/
protected function compileWheres(): void
{
- if ([] === $this->wheres) {
+ if ([] === $this->whereStatements) {
return;
}
- $this->query[] = 'where';
+ $this->query[] = 'WHERE';
- foreach ($this->wheres as $index => $condition) {
- $method = 'compile' . $condition['type'];
+ foreach ($this->whereStatements as $index => $whereQuery) {
+ $result = $whereQuery->compile($index);
- if (0 == $index) {
- $condition['boolean'] = '';
- }
-
- $this->query[] = trim($this->{$method}($condition));
+ $this->query[] = $result->output;
}
}
-
- /**
- * Compile the basic where statement.
- */
- protected function compileWhere(array $where): string
- {
- // This code is here to remove the use of the extract() method in the original repo. See the git history.
- $boolean = array_key_exists('boolean', $where) ? $where['boolean'] : null;
- $column = array_key_exists('column', $where) ? $where['column'] : null;
- $operator = array_key_exists('operator', $where) ? $where['operator'] : null;
-
- return "$boolean $column $operator :$column";
- }
-
- /**
- * Join array elements using a string mask.
- */
- protected function implode(array $columns, string $mask = '{column}'): string
- {
- $columns = array_map(function ($column) use ($mask): string {
- return str_replace('{column}', $column, $mask);
- }, $columns);
-
- return implode(', ', $columns);
- }
}
diff --git a/src/Database/WhereBoolean.php b/src/Database/WhereBoolean.php
new file mode 100644
index 00000000..9b76058a
--- /dev/null
+++ b/src/Database/WhereBoolean.php
@@ -0,0 +1,18 @@
+
+ * @copyright Copyright (c) Wizacha
+ * @copyright Copyright (c) Leonardo Marquine
+ * @license MIT
+ */
+
+declare(strict_types=1);
+
+namespace Wizaplace\Etl\Database;
+
+enum WhereBoolean: string
+{
+ case And = 'AND';
+ case Nothing = '';
+}
diff --git a/src/Database/WhereCompileResult.php b/src/Database/WhereCompileResult.php
new file mode 100644
index 00000000..25565de2
--- /dev/null
+++ b/src/Database/WhereCompileResult.php
@@ -0,0 +1,21 @@
+
+ * @copyright Copyright (c) Wizacha
+ * @copyright Copyright (c) Leonardo Marquine
+ * @license MIT
+ */
+
+declare(strict_types=1);
+
+namespace Wizaplace\Etl\Database;
+
+class WhereCompileResult
+{
+ public function __construct(
+ public string $output,
+ public array $bindings,
+ ) {
+ }
+}
diff --git a/src/Database/WhereInCompositeQuery.php b/src/Database/WhereInCompositeQuery.php
new file mode 100644
index 00000000..0f613454
--- /dev/null
+++ b/src/Database/WhereInCompositeQuery.php
@@ -0,0 +1,58 @@
+
+ * @copyright Copyright (c) Wizacha
+ * @copyright Copyright (c) Leonardo Marquine
+ * @license MIT
+ */
+
+declare(strict_types=1);
+
+namespace Wizaplace\Etl\Database;
+
+class WhereInCompositeQuery implements WhereInterface
+{
+ public function __construct(
+ private WhereBoolean $boolean,
+ private WhereOperator $operator,
+ private array $multipleColumns,
+ private array $multipleValues,
+ ) {
+ }
+
+ public function compile(int $index): WhereCompileResult
+ {
+ sort($this->multipleColumns);
+
+ $parameters = [];
+ $bindings = [];
+ foreach ($this->multipleValues as $value) {
+ ksort($value);
+
+ $bindings = array_merge($bindings, array_values($value));
+
+ $parameters[] = \sprintf(
+ '(%s)',
+ Helpers::implode($value, '?')
+ );
+ }
+
+ $parameters = Helpers::implode($parameters);
+
+ $multipleColumns = Helpers::implode($this->multipleColumns, Helpers::BACKTICKED_MASK);
+
+ return new WhereCompileResult(
+ \trim(
+ \sprintf(
+ '%s (%s) %s (%s)',
+ $index > 0 ? $this->boolean->value : '',
+ $multipleColumns,
+ $this->operator->value,
+ $parameters,
+ )
+ ),
+ $bindings
+ );
+ }
+}
diff --git a/src/Database/WhereInQuery.php b/src/Database/WhereInQuery.php
new file mode 100644
index 00000000..0aadc3bd
--- /dev/null
+++ b/src/Database/WhereInQuery.php
@@ -0,0 +1,41 @@
+
+ * @copyright Copyright (c) Wizacha
+ * @copyright Copyright (c) Leonardo Marquine
+ * @license MIT
+ */
+
+declare(strict_types=1);
+
+namespace Wizaplace\Etl\Database;
+
+class WhereInQuery implements WhereInterface
+{
+ public function __construct(
+ private WhereBoolean $boolean,
+ private WhereOperator $operator,
+ private string $column,
+ private array $multipleValues,
+ ) {
+ }
+
+ public function compile(int $index): WhereCompileResult
+ {
+ $parameters = Helpers::implode($this->multipleValues, '?');
+
+ return new WhereCompileResult(
+ \trim(
+ \sprintf(
+ '%s `%s` %s (%s)',
+ $index > 0 ? $this->boolean->value : '',
+ $this->column,
+ $this->operator->value,
+ $parameters,
+ )
+ ),
+ $this->multipleValues,
+ );
+ }
+}
diff --git a/src/Database/WhereInterface.php b/src/Database/WhereInterface.php
new file mode 100644
index 00000000..e5c0e17d
--- /dev/null
+++ b/src/Database/WhereInterface.php
@@ -0,0 +1,17 @@
+
+ * @copyright Copyright (c) Wizacha
+ * @copyright Copyright (c) Leonardo Marquine
+ * @license MIT
+ */
+
+declare(strict_types=1);
+
+namespace Wizaplace\Etl\Database;
+
+interface WhereInterface
+{
+ public function compile(int $index): WhereCompileResult;
+}
diff --git a/src/Database/WhereOperator.php b/src/Database/WhereOperator.php
new file mode 100644
index 00000000..fd66ca8d
--- /dev/null
+++ b/src/Database/WhereOperator.php
@@ -0,0 +1,25 @@
+
+ * @copyright Copyright (c) Wizacha
+ * @copyright Copyright (c) Leonardo Marquine
+ * @license MIT
+ */
+
+declare(strict_types=1);
+
+namespace Wizaplace\Etl\Database;
+
+enum WhereOperator: string
+{
+ case Equal = '=';
+ case Less = '<';
+ case LessOrEqual = '<=';
+ case Greater = '>';
+ case GreaterOrEqual = '>=';
+ case NotEqual = '<>';
+ case NotEqualBis = '!=';
+ case In = 'IN';
+ case NotIn = 'NOT IN';
+}
diff --git a/src/Database/WhereQuery.php b/src/Database/WhereQuery.php
new file mode 100644
index 00000000..44d65ff8
--- /dev/null
+++ b/src/Database/WhereQuery.php
@@ -0,0 +1,38 @@
+
+ * @copyright Copyright (c) Wizacha
+ * @copyright Copyright (c) Leonardo Marquine
+ * @license MIT
+ */
+
+declare(strict_types=1);
+
+namespace Wizaplace\Etl\Database;
+
+class WhereQuery implements WhereInterface
+{
+ public function __construct(
+ private WhereBoolean $boolean,
+ private WhereOperator $operator,
+ private string $column,
+ private mixed $value,
+ ) {
+ }
+
+ public function compile(int $index): WhereCompileResult
+ {
+ return new WhereCompileResult(
+ \trim(
+ \sprintf(
+ '%s `%s` %s ?',
+ $index > 0 ? $this->boolean->value : '',
+ $this->column,
+ $this->operator->value,
+ )
+ ),
+ [$this->value],
+ );
+ }
+}
diff --git a/src/Database/WhereStatement.php b/src/Database/WhereStatement.php
new file mode 100644
index 00000000..5d9270ae
--- /dev/null
+++ b/src/Database/WhereStatement.php
@@ -0,0 +1,38 @@
+
+ * @copyright Copyright (c) Wizacha
+ * @copyright Copyright (c) Leonardo Marquine
+ * @license MIT
+ */
+
+declare(strict_types=1);
+
+namespace Wizaplace\Etl\Database;
+
+class WhereStatement implements WhereInterface
+{
+ public function __construct(
+ private WhereBoolean $boolean,
+ private WhereOperator $operator,
+ private string $column,
+ ) {
+ }
+
+ public function compile(int $index): WhereCompileResult
+ {
+ return new WhereCompileResult(
+ \trim(
+ \sprintf(
+ '%s `%s` %s :%s',
+ $index > 0 ? $this->boolean->value : '',
+ $this->column,
+ $this->operator->value,
+ $this->column,
+ )
+ ),
+ [],
+ );
+ }
+}
diff --git a/src/Etl.php b/src/Etl.php
index 9ebabfab..f0727e95 100644
--- a/src/Etl.php
+++ b/src/Etl.php
@@ -28,7 +28,7 @@ class Etl
/**
* Create a new Etl instance.
*/
- public function __construct(?Pipeline $pipeline = null)
+ public function __construct(Pipeline $pipeline = null)
{
$this->pipeline = $pipeline ?? new Pipeline();
}
@@ -40,12 +40,11 @@ public function __construct(?Pipeline $pipeline = null)
* Etl\Extractor\Csv needs a string
* Etl\Extractor\Collection an \Iterator
*
- * @param mixed $input
* @param array $options
*
* @return $this
*/
- public function extract(Extractor $extractor, $input, $options = []): Etl
+ public function extract(Extractor $extractor, mixed $input, $options = []): Etl
{
$extractor->input($input)->options($options);
diff --git a/src/Extractors/DateDimension.php b/src/Extractors/DateDimension.php
index 4e227f61..6a91c3e9 100644
--- a/src/Extractors/DateDimension.php
+++ b/src/Extractors/DateDimension.php
@@ -75,12 +75,12 @@ public function __construct()
$this->startDate ??= $this->getCenterDateTime()
->sub($defaultBoundInterval)
- ->format(static::CENTER_DATE_FORMAT);
+ ->format(self::CENTER_DATE_FORMAT);
$this->endDate ??= $this->getCenterDateTime()
->add($defaultBoundInterval)
->sub($dayInterval)
- ->format(static::CENTER_DATE_FORMAT);
+ ->format(self::CENTER_DATE_FORMAT);
}
/**
diff --git a/src/Extractors/Extractor.php b/src/Extractors/Extractor.php
index 3934b226..e26a3cc0 100644
--- a/src/Extractors/Extractor.php
+++ b/src/Extractors/Extractor.php
@@ -15,17 +15,14 @@
abstract class Extractor extends Step
{
- /** @var mixed */
- protected $input;
+ protected mixed $input;
/**
* Set the extractor input.
*
- * @param mixed $input
- *
* @return $this
*/
- public function input($input): Extractor
+ public function input(mixed $input): Extractor
{
$this->input = $input;
diff --git a/src/Extractors/Xml.php b/src/Extractors/Xml.php
index 881d144d..4584d4f7 100644
--- a/src/Extractors/Xml.php
+++ b/src/Extractors/Xml.php
@@ -58,9 +58,7 @@ public function extract(): \Generator
$value = $this->loop . $value;
}
- $this->reader = new \XMLReader();
-
- $this->reader->open($this->input);
+ $this->reader = \XMLReader::open($this->input);
while ($this->reader->read()) {
$this->addElementToPath();
diff --git a/src/Loaders/InsertUpdate.php b/src/Loaders/InsertUpdate.php
index 68c79736..0936dd4c 100644
--- a/src/Loaders/InsertUpdate.php
+++ b/src/Loaders/InsertUpdate.php
@@ -24,9 +24,9 @@ class InsertUpdate extends Insert
/**
* The primary key.
*
- * @var mixed
+ * @var string[]
*/
- protected $key = ['id'];
+ protected array $key = ['id'];
/**
* Indicates if existing destination rows in table should be updated.
@@ -38,14 +38,14 @@ class InsertUpdate extends Insert
*
* @var \PDOStatement|false|null
*/
- protected $select = null;
+ protected $select;
/**
* The update statement.
*
* @var \PDOStatement|false|null
*/
- protected $update = null;
+ protected $update;
/**
* Properties that can be set via the options method.
diff --git a/src/Loaders/Loader.php b/src/Loaders/Loader.php
index 8bea81e2..cca9ec0d 100644
--- a/src/Loaders/Loader.php
+++ b/src/Loaders/Loader.php
@@ -18,19 +18,15 @@ abstract class Loader extends Step
{
/**
* The loader output.
- *
- * @var mixed
*/
- protected $output;
+ protected mixed $output;
/**
* Set the loader output.
*
- * @param mixed $output
- *
* @return $this
*/
- public function output($output): Loader
+ public function output(mixed $output): Loader
{
$this->output = $output;
diff --git a/src/Row.php b/src/Row.php
index 89d4e7bf..a474b55e 100644
--- a/src/Row.php
+++ b/src/Row.php
@@ -38,10 +38,8 @@ public function __construct(array $attributes)
/**
* Set a row attribute.
- *
- * @param mixed $value
*/
- public function set(string $key, $value): self
+ public function set(string $key, mixed $value): self
{
$this->attributes[$key] = $value;
@@ -50,10 +48,8 @@ public function set(string $key, $value): self
/**
* Get a row attribute.
- *
- * @return mixed
*/
- public function get(string $key)
+ public function get(string $key): mixed
{
return $this->attributes[$key] ?? null;
}
@@ -68,10 +64,8 @@ public function remove(string $key): void
/**
* Get a row attribute, and remove it.
- *
- * @return mixed
*/
- public function pull(string $key)
+ public function pull(string $key): mixed
{
$value = $this->attributes[$key] ?? null;
@@ -92,7 +86,7 @@ public function transform(array $columns, callable $callback): void
}
foreach ($columns as $column) {
- $this->$column = $callback($this->$column);
+ $this->attributes[$column] = $callback($this->attributes[$column]);
}
}
@@ -142,28 +136,22 @@ public function isIncomplete(): bool
/**
* Dynamically retrieve attributes on the row.
- *
- * @return mixed
*/
- public function __get(string $key)
+ public function __get(string $key): mixed
{
return $this->attributes[$key];
}
/**
* Dynamically set attributes on the row.
- *
- * @param mixed $value
*/
- public function __set(string $key, $value): void
+ public function __set(string $key, mixed $value): void
{
$this->attributes[$key] = $value;
}
/**
* Determine if the given attribute exists.
- *
- * @param mixed $offset
*/
public function offsetExists($offset): bool
{
@@ -172,8 +160,6 @@ public function offsetExists($offset): bool
/**
* Get the value for a given offset.
- *
- * @param mixed $offset
*/
public function offsetGet($offset): mixed
{
@@ -182,9 +168,6 @@ public function offsetGet($offset): mixed
/**
* Set the value for a given offset.
- *
- * @param mixed $offset
- * @param mixed $value
*/
public function offsetSet($offset, $value): void
{
@@ -193,8 +176,6 @@ public function offsetSet($offset, $value): void
/**
* Unset the value for a given offset.
- *
- * @param mixed $offset
*/
public function offsetUnset($offset): void
{
diff --git a/src/Transformers/UniqueRows.php b/src/Transformers/UniqueRows.php
index c70cac1e..e0848fc7 100644
--- a/src/Transformers/UniqueRows.php
+++ b/src/Transformers/UniqueRows.php
@@ -70,9 +70,9 @@ public function transform(Row $row): void
/**
* Prepare the given row for comparison.
*
- * @return mixed
+ * @return Row|string
*/
- protected function prepare(Row $row)
+ protected function prepare(Row $row): mixed
{
$row = $row->toArray();
@@ -80,26 +80,28 @@ protected function prepare(Row $row)
$row = array_intersect_key($row, array_flip($this->columns));
}
- return $this->consecutive ? $row : md5(serialize($row));
+ return $this->consecutive
+ ? $row
+ : md5(serialize($row));
}
/**
* Verify if the subject is duplicate.
- *
- * @param mixed $subject
*/
- protected function isDuplicate($subject): bool
+ protected function isDuplicate(mixed $subject): bool
{
- return $this->consecutive ? $subject === $this->control : in_array($subject, $this->hashTable, true);
+ return $this->consecutive
+ ? $subject === $this->control
+ : in_array($subject, $this->hashTable, true);
}
/**
* Register the subject for future comparison.
- *
- * @param mixed $subject
*/
- protected function register($subject): void
+ protected function register(mixed $subject): void
{
- $this->consecutive ? $this->control = $subject : $this->hashTable[] = $subject;
+ $this->consecutive
+ ? $this->control = $subject
+ : $this->hashTable[] = $subject;
}
}
diff --git a/tests/Unit/Database/HelpersTest.php b/tests/Unit/Database/HelpersTest.php
new file mode 100644
index 00000000..bcd4c18f
--- /dev/null
+++ b/tests/Unit/Database/HelpersTest.php
@@ -0,0 +1,67 @@
+ [
+ [
+ ['*'],
+ '`{column}`',
+ ],
+ '*',
+ ],
+ 'do not backtick * in a list' => [
+ [
+ ['*', 'other'],
+ '`{column}`',
+ ],
+ '*, `other`',
+ ],
+ 'backtick * if not in ignoreMask' => [
+ [
+ ['*', 'other'],
+ '`{column}`',
+ ['other'],
+ ],
+ '`*`, other',
+ ],
+ 'common' => [
+ [
+ ['hello', 'world'],
+ '#{column}#',
+ ],
+ '#hello#, #world#',
+ ],
+ 'default' => [
+ [
+ ['hello', 'world'],
+ ],
+ 'hello, world',
+ ],
+ ];
+ }
+}
diff --git a/tests/Unit/Database/QueryTest.php b/tests/Unit/Database/QueryTest.php
index 618dcc02..729e935d 100644
--- a/tests/Unit/Database/QueryTest.php
+++ b/tests/Unit/Database/QueryTest.php
@@ -22,12 +22,12 @@ public function select(): void
$query = new Query($this->createMock('PDO'));
$query->select('users');
- static::assertEquals('select * from users', $query->toSql());
+ static::assertEquals('SELECT * FROM `users`', $query->toSql());
$query = new Query($this->createMock('PDO'));
$query->select('users', ['name', 'email']);
- static::assertEquals('select name, email from users', $query->toSql());
+ static::assertEquals('SELECT `name`, `email` FROM `users`', $query->toSql());
}
/** @test */
@@ -36,7 +36,7 @@ public function insert(): void
$query = new Query($this->createMock('PDO'));
$query->insert('users', ['name' => 'Jane Doe', 'email' => 'janedoe@example.com']);
- static::assertEquals('insert into users (name, email) values (?, ?)', $query->toSql());
+ static::assertEquals('INSERT INTO `users` (`name`, `email`) VALUES (?, ?)', $query->toSql());
static::assertEquals(['Jane Doe', 'janedoe@example.com'], $query->getBindings());
}
@@ -46,7 +46,7 @@ public function update(): void
$query = new Query($this->createMock('PDO'));
$query->update('users', ['name' => 'Jane Doe', 'email' => 'janedoe@example.com']);
- static::assertEquals('update users set name = ?, email = ?', $query->toSql());
+ static::assertEquals('UPDATE `users` SET `name` = ?, `email` = ?', $query->toSql());
static::assertEquals(['Jane Doe', 'janedoe@example.com'], $query->getBindings());
}
@@ -56,7 +56,7 @@ public function delete(): void
$query = new Query($this->createMock('PDO'));
$query->delete('users');
- static::assertEquals('delete from users', $query->toSql());
+ static::assertEquals('DELETE FROM `users`', $query->toSql());
static::assertEquals([], $query->getBindings());
}
@@ -66,7 +66,7 @@ public function where(): void
$query = new Query($this->createMock('PDO'));
$query->where(['name' => 'Jane Doe', 'email' => 'janedoe@example.com']);
- static::assertEquals('where name = ? and email = ?', $query->toSql());
+ static::assertEquals('WHERE `name` = ? AND `email` = ?', $query->toSql());
static::assertEquals(['Jane Doe', 'janedoe@example.com'], $query->getBindings());
}
@@ -76,7 +76,7 @@ public function whereIn(): void
$query = new Query($this->createMock('PDO'));
$query->whereIn('id', ['1', '2']);
- static::assertEquals('where id in (?, ?)', $query->toSql());
+ static::assertEquals('WHERE `id` IN (?, ?)', $query->toSql());
static::assertEquals(['1', '2'], $query->getBindings());
}
@@ -86,7 +86,7 @@ public function whereNotIn(): void
$query = new Query($this->createMock('PDO'));
$query->whereNotIn('id', ['1', '2']);
- static::assertEquals('where id not in (?, ?)', $query->toSql());
+ static::assertEquals('WHERE `id` NOT IN (?, ?)', $query->toSql());
static::assertEquals(['1', '2'], $query->getBindings());
}
@@ -96,7 +96,7 @@ public function compositeWhereIn(): void
$query = new Query($this->createMock('PDO'));
$query->whereIn(['id', 'company'], [['id' => '1', 'company' => '1'], ['id' => '2', 'company' => '1']]);
- static::assertEquals('where (company, id) in ((?, ?), (?, ?))', $query->toSql());
+ static::assertEquals('WHERE (`company`, `id`) IN ((?, ?), (?, ?))', $query->toSql());
static::assertEquals(['1', '1', '1', '2'], $query->getBindings());
}
@@ -106,7 +106,7 @@ public function compositeWhereNotIn(): void
$query = new Query($this->createMock('PDO'));
$query->whereNotIn(['id', 'company'], [['id' => '1', 'company' => '1'], ['id' => '2', 'company' => '1']]);
- static::assertEquals('where (company, id) not in ((?, ?), (?, ?))', $query->toSql());
+ static::assertEquals('WHERE (`company`, `id`) NOT IN ((?, ?), (?, ?))', $query->toSql());
static::assertEquals(['1', '1', '1', '2'], $query->getBindings());
}
diff --git a/tests/Unit/Database/StatementTest.php b/tests/Unit/Database/StatementTest.php
index c8b8018c..207cc156 100644
--- a/tests/Unit/Database/StatementTest.php
+++ b/tests/Unit/Database/StatementTest.php
@@ -24,12 +24,12 @@ public function select(): void
$statement = new Statement($this->createMock('PDO'));
$statement->select('users');
- static::assertEquals('select * from users', $statement->toSql());
+ static::assertEquals('SELECT * FROM `users`', $statement->toSql());
$statement = new Statement($this->createMock('PDO'));
$statement->select('users', ['name', 'email']);
- static::assertEquals('select name, email from users', $statement->toSql());
+ static::assertEquals('SELECT `name`, `email` FROM `users`', $statement->toSql());
}
/** @test */
@@ -38,7 +38,7 @@ public function insert(): void
$statement = new Statement($this->createMock('PDO'));
$statement->insert('users', ['name', 'email']);
- static::assertEquals('insert into users (name, email) values (:name, :email)', $statement->toSql());
+ static::assertEquals('INSERT INTO `users` (`name`, `email`) values (:name, :email)', $statement->toSql());
}
/** @test */
@@ -47,7 +47,7 @@ public function update(): void
$statement = new Statement($this->createMock('PDO'));
$statement->update('users', ['name', 'email']);
- static::assertEquals('update users set name = :name, email = :email', $statement->toSql());
+ static::assertEquals('UPDATE `users` SET `name` = :name, `email` = :email', $statement->toSql());
}
/** @test */
@@ -56,7 +56,7 @@ public function delete(): void
$statement = new Statement($this->createMock('PDO'));
$statement->delete('users');
- static::assertEquals('delete from users', $statement->toSql());
+ static::assertEquals('DELETE FROM `users`', $statement->toSql());
}
/** @test */
@@ -65,7 +65,7 @@ public function where(): void
$statement = new Statement($this->createMock('PDO'));
$statement->where(['name', 'email']);
- static::assertEquals('where name = :name and email = :email', $statement->toSql());
+ static::assertEquals('WHERE `name` = :name AND `email` = :email', $statement->toSql());
}
/** @test */
@@ -98,13 +98,9 @@ public function prepareInvalid(): void
$statement = new Statement($database);
$statement->select('foo', ['>']);
- try {
- $statement->prepare();
- static::fail('An exception should have been thrown');
- } catch (\PDOException $exception) {
- static::assertEquals('SQLSTATE[HY000]: General error: 1 near ">": syntax error', $exception->getMessage());
- } catch (\Exception $exception) {
- static::fail('An instance of ' . \PDOException::class . ' should have been thrown');
- }
+ $this->expectExceptionObject(
+ new \PDOException('SQLSTATE[HY000]: General error: 1 no such table: foo')
+ );
+ $statement->prepare();
}
}
diff --git a/tests/Unit/Extractors/AggregatorTest.php b/tests/Unit/Extractors/AggregatorTest.php
index 1b01f632..48625cff 100644
--- a/tests/Unit/Extractors/AggregatorTest.php
+++ b/tests/Unit/Extractors/AggregatorTest.php
@@ -230,7 +230,7 @@ public static function iteratorsProvider(): array
$simpleDataSet =
[
[
- static::arrayToIterator([
+ self::arrayToIterator([
['id' => 1, 'name' => 'John Doe', 'email' => 'johndoe@email.com'],
[], // should not happen
['impossible error'], // should not happen as well
@@ -238,7 +238,7 @@ public static function iteratorsProvider(): array
['id' => 3, 'name' => 'Incomplete1', 'email' => 'incomplete1@dirtydata'],
['id' => 4, 'name' => 'Incomplete2', 'email' => 'incomplete2@dirtydata'],
]),
- static::arrayToIterator([
+ self::arrayToIterator([
['email' => 'janedoe@email.com', 'twitter' => '@jane'],
['email' => 'johndoe@email.com', 'twitter' => '@john'],
['impossible error'], // should not happen as well
diff --git a/tests/Unit/Extractors/DateDimensionTest.php b/tests/Unit/Extractors/DateDimensionTest.php
index f6256a0e..b3d30d0b 100644
--- a/tests/Unit/Extractors/DateDimensionTest.php
+++ b/tests/Unit/Extractors/DateDimensionTest.php
@@ -205,7 +205,7 @@ public function defaultStart(): void
$year = (int) $firstDay->format('Y');
$result = \iterator_to_array($extractor->extract());
- $firstRow = reset($result[0]);
+ $firstRow = reset($result);
$lastRow = end($result);
static::assertStringMatchesFormat(
@@ -332,7 +332,7 @@ private function iterateDimensions(DateDimension $extractor): array
$currentDayTimestamp = (new \DateTimeImmutable($date[DateDimension::ROW_DATE_FULL]))->getTimestamp();
if (null !== $previousDayTimestamp) {
- $delta = $currentDayTimestamp - $previousDayTimestamp - static::DAY_AS_SECONDS;
+ $delta = $currentDayTimestamp - $previousDayTimestamp - self::DAY_AS_SECONDS;
}
if ($delta > 0) {
diff --git a/tests/Unit/Extractors/QueryTest.php b/tests/Unit/Extractors/QueryTest.php
index f89bdcbe..340c9b41 100644
--- a/tests/Unit/Extractors/QueryTest.php
+++ b/tests/Unit/Extractors/QueryTest.php
@@ -11,7 +11,9 @@
namespace Tests\Unit\Extractors;
+use PHPUnit\Framework\MockObject\MockObject;
use Tests\Tools\AbstractTestCase;
+use Wizaplace\Etl\Database\Manager;
use Wizaplace\Etl\Extractors\Query;
use Wizaplace\Etl\Row;
@@ -20,16 +22,32 @@ class QueryTest extends AbstractTestCase
/** @test */
public function defaultOptions(): void
{
- $statement = $this->createMock('PDOStatement');
- $statement->expects(static::once())->method('execute')->with([]);
- $statement->expects(static::exactly(3))->method('fetch')
- ->will(static::onConsecutiveCalls(['row1'], ['row2'], null));
-
- $connection = $this->createMock('PDO');
- $connection->expects(static::once())->method('prepare')->with('select query')->willReturn($statement);
-
- $manager = $this->createMock('Wizaplace\Etl\Database\Manager');
- $manager->expects(static::once())->method('pdo')->with('default')->willReturn($connection);
+ /** @var MockObject|\PDOStatement */
+ $statement = $this->createMock(\PDOStatement::class);
+ $statement
+ ->expects(static::once())
+ ->method('execute')
+ ->with([]);
+ $statement
+ ->expects(static::exactly(3))
+ ->method('fetch')
+ ->willReturn(['row1'], ['row2'], null);
+
+ /** @var MockObject|\PDO */
+ $connection = $this->createMock(\PDO::class);
+ $connection
+ ->expects(static::once())
+ ->method('prepare')
+ ->with('select query')
+ ->willReturn($statement);
+
+ /** @var MockObject|Manager */
+ $manager = $this->createMock(Manager::class);
+ $manager
+ ->expects(static::once())
+ ->method('pdo')
+ ->with('default')
+ ->willReturn($connection);
$extractor = new Query($manager);
@@ -41,16 +59,32 @@ public function defaultOptions(): void
/** @test */
public function customConnectionAndBindings(): void
{
- $statement = $this->createMock('PDOStatement');
- $statement->expects(static::once())->method('execute')->with(['binding']);
- $statement->expects(static::exactly(3))->method('fetch')
- ->will(static::onConsecutiveCalls(['row1'], ['row2'], null));
-
- $connection = $this->createMock('PDO');
- $connection->expects(static::once())->method('prepare')->with('select query')->willReturn($statement);
-
- $manager = $this->createMock('Wizaplace\Etl\Database\Manager');
- $manager->expects(static::once())->method('pdo')->with('connection')->willReturn($connection);
+ /** @var MockObject|\PDOStatement */
+ $statement = $this->createMock(\PDOStatement::class);
+ $statement
+ ->expects(static::once())
+ ->method('execute')
+ ->with(['binding']);
+ $statement
+ ->expects(static::exactly(3))
+ ->method('fetch')
+ ->willReturn(['row1'], ['row2'], null);
+
+ /** @var MockObject|\PDO */
+ $connection = $this->createMock(\PDO::class);
+ $connection
+ ->expects(static::once())
+ ->method('prepare')
+ ->with('select query')
+ ->willReturn($statement);
+
+ /** @var MockObject|Manager */
+ $manager = $this->createMock(Manager::class);
+ $manager
+ ->expects(static::once())
+ ->method('pdo')
+ ->with('connection')
+ ->willReturn($connection);
$extractor = new Query($manager);
diff --git a/tests/Unit/Extractors/TableTest.php b/tests/Unit/Extractors/TableTest.php
index 631e9afd..0fe184d2 100644
--- a/tests/Unit/Extractors/TableTest.php
+++ b/tests/Unit/Extractors/TableTest.php
@@ -11,10 +11,12 @@
namespace Tests\Unit\Extractors;
+use PHPUnit\Framework\MockObject\MockObject;
use Tests\Tools\AbstractTestCase;
use Wizaplace\Etl;
use Wizaplace\Etl\Database\ConnectionFactory;
use Wizaplace\Etl\Database\Manager;
+use Wizaplace\Etl\Database\Query;
use Wizaplace\Etl\Extractors\Table;
use Wizaplace\Etl\Row;
@@ -23,17 +25,37 @@ class TableTest extends AbstractTestCase
/** @test */
public function defaultOptions(): void
{
- $statement = $this->createMock('PDOStatement');
- $statement->expects(static::exactly(3))->method('fetch')
- ->will(static::onConsecutiveCalls(['row1'], ['row2'], null));
-
- $query = $this->createMock('Wizaplace\Etl\Database\Query');
- $query->expects(static::once())->method('select')->with('table', ['*'])->will(static::returnSelf());
- $query->expects(static::once())->method('where')->with([])->will(static::returnSelf());
- $query->expects(static::once())->method('execute')->willReturn($statement);
-
- $manager = $this->createMock('Wizaplace\Etl\Database\Manager');
- $manager->expects(static::once())->method('query')->with('default')->willReturn($query);
+ /** @var MockObject|\PDOStatement */
+ $statement = $this->createMock(\PDOStatement::class);
+ $statement
+ ->expects(static::exactly(3))
+ ->method('fetch')
+ ->willReturn(['row1'], ['row2'], null);
+
+ /** @var MockObject|Query */
+ $query = $this->createMock(Query::class);
+ $query
+ ->expects(static::once())
+ ->method('select')
+ ->with('table', ['*'])
+ ->willReturnSelf();
+ $query
+ ->expects(static::once())
+ ->method('where')
+ ->with([])
+ ->willReturnSelf();
+ $query
+ ->expects(static::once())
+ ->method('execute')
+ ->willReturn($statement);
+
+ /** @var MockObject|Manager */
+ $manager = $this->createMock(Manager::class);
+ $manager
+ ->expects(static::once())
+ ->method('query')
+ ->with('default')
+ ->willReturn($query);
$extractor = new Table($manager);
@@ -45,17 +67,38 @@ public function defaultOptions(): void
/** @test */
public function customConnectionColumnsAndWhereClause(): void
{
- $statement = $this->createMock('PDOStatement');
- $statement->expects(static::exactly(3))->method('fetch')
- ->will(static::onConsecutiveCalls(['row1'], ['row2'], null));
-
- $query = $this->createMock('Wizaplace\Etl\Database\Query');
- $query->expects(static::once())->method('select')->with('table', ['columns'])->will(static::returnSelf());
- $query->expects(static::once())->method('where')->with(['where'])->will(static::returnSelf());
- $query->expects(static::once())->method('execute')->willReturn($statement);
-
- $manager = $this->createMock('Wizaplace\Etl\Database\Manager');
- $manager->expects(static::once())->method('query')->with('connection')->willReturn($query);
+ /** @var MockObject|\PDOStatement */
+ $statement = $this
+ ->createMock(\PDOStatement::class);
+ $statement
+ ->expects(static::exactly(3))
+ ->method('fetch')
+ ->willReturn(['row1'], ['row2'], null);
+
+ /** @var MockObject|Query */
+ $query = $this->createMock(Query::class);
+ $query
+ ->expects(static::once())
+ ->method('select')
+ ->with('table', ['columns'])
+ ->willReturnSelf();
+ $query
+ ->expects(static::once())
+ ->method('where')
+ ->with(['where'])
+ ->willReturnSelf();
+ $query
+ ->expects(static::once())
+ ->method('execute')
+ ->willReturn($statement);
+
+ /** @var MockObject|Manager */
+ $manager = $this->createMock(Manager::class);
+ $manager
+ ->expects(static::once())
+ ->method('query')
+ ->with('connection')
+ ->willReturn($query);
$extractor = new Table($manager);