From ad0956b3aa071e0780fea19a5c67dea9b195698d Mon Sep 17 00:00:00 2001 From: Baptiste Langlade Date: Mon, 17 Nov 2025 15:06:06 +0100 Subject: [PATCH 01/24] require php 8.4 --- .github/workflows/ci.yml | 8 ++++---- CHANGELOG.md | 6 ++++++ composer.json | 2 +- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e26152e..8193a2c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,7 +8,7 @@ jobs: strategy: matrix: os: [ubuntu-latest] - php-version: ['8.2', '8.3', '8.4'] + php-version: ['8.4'] dependency-versions: ['lowest', 'highest'] name: 'BlackBox' services: @@ -50,7 +50,7 @@ jobs: strategy: matrix: os: [ubuntu-latest] - php-version: ['8.2', '8.3', '8.4'] + php-version: ['8.4'] dependency-versions: ['lowest', 'highest'] name: 'Coverage' services: @@ -95,7 +95,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - php-version: ['8.2', '8.3'] + php-version: ['8.4'] dependencies: ['lowest', 'highest'] name: 'Psalm' steps: @@ -116,7 +116,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - php-version: ['8.2'] + php-version: ['8.4'] name: 'CS' steps: - name: Checkout diff --git a/CHANGELOG.md b/CHANGELOG.md index a3c05db..69f10f1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## [Unreleased] + +### Changed + +- Requires PHP `8.4` + ## 4.2.0 - 2025-04-09 ### Added diff --git a/composer.json b/composer.json index f79ac9a..59f4136 100644 --- a/composer.json +++ b/composer.json @@ -15,7 +15,7 @@ "issues": "http://github.com/formal-php/access-layer/issues" }, "require": { - "php": "~8.2", + "php": "~8.4", "innmind/immutable": "~4.0|~5.0", "innmind/url": "~4.0", "innmind/specification": "~4.1", From be365004a043007f3308d6897cadcf0bb0e3d466 Mon Sep 17 00:00:00 2001 From: Baptiste Langlade Date: Mon, 17 Nov 2025 15:06:57 +0100 Subject: [PATCH 02/24] reuse workflows --- .github/workflows/ci.yml | 39 ++--------------------------- .github/workflows/documentation.yml | 22 ++-------------- 2 files changed, 4 insertions(+), 57 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8193a2c..102b785 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -92,41 +92,6 @@ jobs: with: token: ${{ secrets.CODECOV_TOKEN }} psalm: - runs-on: ubuntu-latest - strategy: - matrix: - php-version: ['8.4'] - dependencies: ['lowest', 'highest'] - name: 'Psalm' - steps: - - name: Checkout - uses: actions/checkout@v2 - - name: Setup PHP - uses: shivammathur/setup-php@v2 - with: - php-version: ${{ matrix.php-version }} - extensions: mbstring, intl - - name: Composer - uses: "ramsey/composer-install@v2" - with: - dependency-versions: ${{ matrix.dependencies }} - - name: Psalm - run: vendor/bin/psalm --shepherd + uses: innmind/github-workflows/.github/workflows/psalm-matrix.yml@next cs: - runs-on: ubuntu-latest - strategy: - matrix: - php-version: ['8.4'] - name: 'CS' - steps: - - name: Checkout - uses: actions/checkout@v2 - - name: Setup PHP - uses: shivammathur/setup-php@v2 - with: - php-version: ${{ matrix.php-version }} - extensions: mbstring, intl - - name: Composer - uses: "ramsey/composer-install@v2" - - name: CS - run: vendor/bin/php-cs-fixer fix --diff --dry-run + uses: innmind/github-workflows/.github/workflows/cs.yml@next diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml index 74cd16c..39064b6 100644 --- a/.github/workflows/documentation.yml +++ b/.github/workflows/documentation.yml @@ -2,27 +2,9 @@ name: Documentation on: push: branches: [master] - permissions: contents: write jobs: deploy: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - name: Configure Git Credentials - run: | - git config user.name github-actions[bot] - git config user.email 41898282+github-actions[bot]@users.noreply.github.com - - uses: actions/setup-python@v5 - with: - python-version: 3.x - - run: echo "cache_id=$(date --utc '+%V')" >> $GITHUB_ENV - - uses: actions/cache@v4 - with: - key: mkdocs-material-${{ env.cache_id }} - path: .cache - restore-keys: | - mkdocs-material- - - run: pip install mkdocs-material - - run: mkdocs gh-deploy --force + uses: innmind/github-workflows/.github/workflows/documentation.yml@main + secrets: inherit From bf1e24c3ddd2ee9789ee347d152cfbce51340055 Mon Sep 17 00:00:00 2001 From: Baptiste Langlade Date: Mon, 17 Nov 2025 15:07:26 +0100 Subject: [PATCH 03/24] add workflow to create releases --- .github/workflows/release.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..b25ad8a --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,11 @@ +name: Create release + +on: + push: + tags: + - '*' + +jobs: + release: + uses: innmind/github-workflows/.github/workflows/release.yml@main + secrets: inherit From 0d7c2f0cc35cb35373bddd366c5eaa3a06cead2f Mon Sep 17 00:00:00 2001 From: Baptiste Langlade Date: Mon, 17 Nov 2025 15:14:18 +0100 Subject: [PATCH 04/24] remove unused PDO::persistent() --- CHANGELOG.md | 4 ++++ src/Connection/PDO.php | 9 ++------- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 69f10f1..18d92fa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,10 @@ - Requires PHP `8.4` +### Removed + +- `Formal\AccessLayer\Connection\PDO::persistent()` + ## 4.2.0 - 2025-04-09 ### Added diff --git a/src/Connection/PDO.php b/src/Connection/PDO.php index 281d248..f328117 100644 --- a/src/Connection/PDO.php +++ b/src/Connection/PDO.php @@ -25,7 +25,7 @@ final class PDO implements Connection private \PDO $pdo; private Driver $driver; - private function __construct(Url $dsn, array $options = []) + private function __construct(Url $dsn) { $dsnUser = $dsn->authority()->userInformation()->user(); $dsnPassword = $dsn->authority()->userInformation()->password(); @@ -67,7 +67,7 @@ private function __construct(Url $dsn, array $options = []) $charset, ); - $this->pdo = new \PDO($pdoDsn, $user, $password, $options); + $this->pdo = new \PDO($pdoDsn, $user, $password); } #[\Override] @@ -95,11 +95,6 @@ public static function of(Url $dsn): self return new self($dsn); } - public static function persistent(Url $dsn): self - { - return new self($dsn, [\PDO::ATTR_PERSISTENT => true]); - } - /** * @param callable(): bool $action * From 1f7c9e5d5e7dbd37032cc86bf508460f16c4a9f4 Mon Sep 17 00:00:00 2001 From: Baptiste Langlade Date: Mon, 17 Nov 2025 15:21:06 +0100 Subject: [PATCH 05/24] make Connection a final class --- CHANGELOG.md | 1 + src/Connection.php | 43 ++++++++++++++++++++++++++++--- src/Connection/Implementation.php | 21 +++++++++++++++ src/Connection/Lazy.php | 5 +++- src/Connection/Logger.php | 11 +++++--- src/Connection/PDO.php | 6 +++-- 6 files changed, 77 insertions(+), 10 deletions(-) create mode 100644 src/Connection/Implementation.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 18d92fa..5568c83 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ ### Changed - Requires PHP `8.4` +- `Formal\AccessLayer\Connection` is now a final class, all previous implementations are now flagged as internal ### Removed diff --git a/src/Connection.php b/src/Connection.php index c026595..2b33b40 100644 --- a/src/Connection.php +++ b/src/Connection.php @@ -3,15 +3,52 @@ namespace Formal\AccessLayer; -use Formal\AccessLayer\Exception\QueryFailed; +use Formal\AccessLayer\{ + Connection\Implementation, + Connection\PDO, + Connection\Lazy, + Connection\Logger, + Exception\QueryFailed, +}; +use Innmind\Url\Url; use Innmind\Immutable\Sequence; +use Psr\Log\LoggerInterface; -interface Connection +final class Connection { + private function __construct( + private Implementation $implementation, + ) { + } + /** * @throws QueryFailed * * @return Sequence */ - public function __invoke(Query $query): Sequence; + public function __invoke(Query $query): Sequence + { + return ($this->implementation)($query); + } + + public static function new(Url $dsn): self + { + return new self(PDO::of($dsn)); + } + + /** + * @param callable(): Connection $load + */ + public static function lazy(callable $load): self + { + return new self(Lazy::of($load)); + } + + public static function logger(self $connection, LoggerInterface $logger): self + { + return new self(Logger::psr( + $connection->implementation, + $logger, + )); + } } diff --git a/src/Connection/Implementation.php b/src/Connection/Implementation.php new file mode 100644 index 0000000..f392746 --- /dev/null +++ b/src/Connection/Implementation.php @@ -0,0 +1,21 @@ + + */ + public function __invoke(Query $query): Sequence; +} diff --git a/src/Connection/Lazy.php b/src/Connection/Lazy.php index 16070e6..b6a8ecb 100644 --- a/src/Connection/Lazy.php +++ b/src/Connection/Lazy.php @@ -9,7 +9,10 @@ }; use Innmind\Immutable\Sequence; -final class Lazy implements Connection +/** + * @internal + */ +final class Lazy implements Implementation { /** @var callable(): Connection */ private $load; diff --git a/src/Connection/Logger.php b/src/Connection/Logger.php index 6358087..1ae4534 100644 --- a/src/Connection/Logger.php +++ b/src/Connection/Logger.php @@ -11,12 +11,15 @@ use Innmind\Immutable\Sequence; use Psr\Log\LoggerInterface; -final class Logger implements Connection +/** + * @internal + */ +final class Logger implements Implementation { - private Connection $connection; + private Implementation $connection; private LoggerInterface $logger; - private function __construct(Connection $connection, LoggerInterface $logger) + private function __construct(Implementation $connection, LoggerInterface $logger) { $this->connection = $connection; $this->logger = $logger; @@ -62,7 +65,7 @@ public function __invoke(Query $query): Sequence } } - public static function psr(Connection $connection, LoggerInterface $logger): self + public static function psr(Implementation $connection, LoggerInterface $logger): self { return new self($connection, $logger); } diff --git a/src/Connection/PDO.php b/src/Connection/PDO.php index f328117..8dcf3d9 100644 --- a/src/Connection/PDO.php +++ b/src/Connection/PDO.php @@ -4,7 +4,6 @@ namespace Formal\AccessLayer\Connection; use Formal\AccessLayer\{ - Connection, Query, Query\Parameter, Query\Parameter\Type, @@ -20,7 +19,10 @@ }; use Innmind\Immutable\Sequence; -final class PDO implements Connection +/** + * @internal + */ +final class PDO implements Implementation { private \PDO $pdo; private Driver $driver; From 8ad97716acfee2d45fec29685b4a7c69a1b236de Mon Sep 17 00:00:00 2001 From: Baptiste Langlade Date: Mon, 17 Nov 2025 15:24:43 +0100 Subject: [PATCH 06/24] only keep the implementation once loaded --- src/Connection.php | 4 +++- src/Connection/Lazy.php | 15 ++++++--------- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/Connection.php b/src/Connection.php index 2b33b40..28af1f0 100644 --- a/src/Connection.php +++ b/src/Connection.php @@ -41,7 +41,9 @@ public static function new(Url $dsn): self */ public static function lazy(callable $load): self { - return new self(Lazy::of($load)); + return new self(Lazy::of( + static fn() => $load()->implementation, + )); } public static function logger(self $connection, LoggerInterface $logger): self diff --git a/src/Connection/Lazy.php b/src/Connection/Lazy.php index b6a8ecb..994ccd8 100644 --- a/src/Connection/Lazy.php +++ b/src/Connection/Lazy.php @@ -3,10 +3,7 @@ namespace Formal\AccessLayer\Connection; -use Formal\AccessLayer\{ - Connection, - Query, -}; +use Formal\AccessLayer\Query; use Innmind\Immutable\Sequence; /** @@ -14,12 +11,12 @@ */ final class Lazy implements Implementation { - /** @var callable(): Connection */ + /** @var callable(): Implementation */ private $load; - private ?Connection $connection = null; + private ?Implementation $connection = null; /** - * @param callable(): Connection $load + * @param callable(): Implementation $load */ private function __construct(callable $load) { @@ -33,14 +30,14 @@ public function __invoke(Query $query): Sequence } /** - * @param callable(): Connection $load + * @param callable(): Implementation $load */ public static function of(callable $load): self { return new self($load); } - private function connection(): Connection + private function connection(): Implementation { return $this->connection ?? $this->connection = ($this->load)(); } From 6b4a59fb72ca07328c92b8790ecf71e8c9ee4a17 Mon Sep 17 00:00:00 2001 From: Baptiste Langlade Date: Mon, 17 Nov 2025 15:34:21 +0100 Subject: [PATCH 07/24] add Query\Builder interface --- CHANGELOG.md | 4 ++++ src/Connection.php | 6 +++++- src/Query/Builder.php | 14 ++++++++++++++ 3 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 src/Query/Builder.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 5568c83..7a5eb3c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## [Unreleased] +### Added + +- `Formal\AccessLayer\Query\Builder` + ### Changed - Requires PHP `8.4` diff --git a/src/Connection.php b/src/Connection.php index 28af1f0..8615c6b 100644 --- a/src/Connection.php +++ b/src/Connection.php @@ -26,8 +26,12 @@ private function __construct( * * @return Sequence */ - public function __invoke(Query $query): Sequence + public function __invoke(Query|Query\Builder $query): Sequence { + if ($query instanceof Query\Builder) { + $query = $query->normalize(); + } + return ($this->implementation)($query); } diff --git a/src/Query/Builder.php b/src/Query/Builder.php new file mode 100644 index 0000000..57c7da0 --- /dev/null +++ b/src/Query/Builder.php @@ -0,0 +1,14 @@ + Date: Mon, 17 Nov 2025 15:42:11 +0100 Subject: [PATCH 08/24] rename ::onDemand() named constructors as ::lazily() and deprecate old constructors --- CHANGELOG.md | 5 +++++ src/Query/SQL.php | 11 +++++++++++ src/Query/Select.php | 9 +++++++++ 3 files changed, 25 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7a5eb3c..591c031 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,11 @@ - Requires PHP `8.4` - `Formal\AccessLayer\Connection` is now a final class, all previous implementations are now flagged as internal +### Deprecated + +- `Formal\AccessLayer\Query\Select::onDemand()`, use `::lazily()` instead +- `Formal\AccessLayer\Query\SQL::onDemand()`, use `::lazily()` instead + ### Removed - `Formal\AccessLayer\Connection\PDO::persistent()` diff --git a/src/Query/SQL.php b/src/Query/SQL.php index 1de2c6b..6ee78c3 100644 --- a/src/Query/SQL.php +++ b/src/Query/SQL.php @@ -43,10 +43,21 @@ public static function of(string $sql): self /** * @psalm-pure + * @deprecated Use ::lazily() instead * * @param non-empty-string $sql */ public static function onDemand(string $sql): self + { + return self::lazily($sql); + } + + /** + * @psalm-pure + * + * @param non-empty-string $sql + */ + public static function lazily(string $sql): self { return new self($sql, true, Sequence::of()); } diff --git a/src/Query/Select.php b/src/Query/Select.php index 1ed79b1..c17f0cd 100644 --- a/src/Query/Select.php +++ b/src/Query/Select.php @@ -70,8 +70,17 @@ public static function from(Name|Name\Aliased $table): self /** * @psalm-pure + * @deprecated Use ::lazily() instead */ public static function onDemand(Name|Name\Aliased $table): self + { + return self::lazily($table); + } + + /** + * @psalm-pure + */ + public static function lazily(Name|Name\Aliased $table): self { /** @var Maybe */ $count = Maybe::nothing(); From 8d38fa4ed7170860d9882614401431fd01ccae92 Mon Sep 17 00:00:00 2001 From: Baptiste Langlade Date: Mon, 17 Nov 2025 15:42:50 +0100 Subject: [PATCH 09/24] the builder needs the driver in order to know how to format the sql --- src/Connection.php | 4 ---- src/Connection/Implementation.php | 2 +- src/Connection/Lazy.php | 2 +- src/Connection/Logger.php | 14 ++++++++++---- src/Connection/PDO.php | 6 +++++- src/Query/Builder.php | 7 +++++-- 6 files changed, 22 insertions(+), 13 deletions(-) diff --git a/src/Connection.php b/src/Connection.php index 8615c6b..ce98580 100644 --- a/src/Connection.php +++ b/src/Connection.php @@ -28,10 +28,6 @@ private function __construct( */ public function __invoke(Query|Query\Builder $query): Sequence { - if ($query instanceof Query\Builder) { - $query = $query->normalize(); - } - return ($this->implementation)($query); } diff --git a/src/Connection/Implementation.php b/src/Connection/Implementation.php index f392746..79de1b9 100644 --- a/src/Connection/Implementation.php +++ b/src/Connection/Implementation.php @@ -17,5 +17,5 @@ interface Implementation /** * @return Sequence */ - public function __invoke(Query $query): Sequence; + public function __invoke(Query|Query\Builder $query): Sequence; } diff --git a/src/Connection/Lazy.php b/src/Connection/Lazy.php index 994ccd8..0a19716 100644 --- a/src/Connection/Lazy.php +++ b/src/Connection/Lazy.php @@ -24,7 +24,7 @@ private function __construct(callable $load) } #[\Override] - public function __invoke(Query $query): Sequence + public function __invoke(Query|Query\Builder $query): Sequence { return ($this->connection())($query); } diff --git a/src/Connection/Logger.php b/src/Connection/Logger.php index 1ae4534..c519869 100644 --- a/src/Connection/Logger.php +++ b/src/Connection/Logger.php @@ -26,18 +26,24 @@ private function __construct(Implementation $connection, LoggerInterface $logger } #[\Override] - public function __invoke(Query $query): Sequence + public function __invoke(Query|Query\Builder $query): Sequence { // For the sake of simplicity the queries SQL is logged with the MySQL // format. As otherwise it would require this decorator to retrieve the // driver from the underlying connection. + if ($query instanceof Query\Builder) { + $normalized = $query->normalize(Driver::mysql); + } else { + $normalized = $query; + } + try { $this->logger->debug( 'Query {sql} is about to be executed', [ - 'sql' => $query->sql(Driver::mysql), - 'parameters' => $query->parameters()->reduce( + 'sql' => $normalized->sql(Driver::mysql), + 'parameters' => $normalized->parameters()->reduce( [], static fn(array $parameters, $parameter) => \array_merge( $parameters, @@ -55,7 +61,7 @@ public function __invoke(Query $query): Sequence $this->logger->error( 'Query {sql} failed with {kind}({message})', [ - 'sql' => $query->sql(Driver::mysql), + 'sql' => $normalized->sql(Driver::mysql), 'kind' => \get_class($e), 'message' => $e->getMessage(), ], diff --git a/src/Connection/PDO.php b/src/Connection/PDO.php index 8dcf3d9..d639960 100644 --- a/src/Connection/PDO.php +++ b/src/Connection/PDO.php @@ -73,8 +73,12 @@ private function __construct(Url $dsn) } #[\Override] - public function __invoke(Query $query): Sequence + public function __invoke(Query|Query\Builder $query): Sequence { + if ($query instanceof Query\Builder) { + $query = $query->normalize($this->driver); + } + return match (\get_class($query)) { Query\StartTransaction::class => $this->transaction( $query, diff --git a/src/Query/Builder.php b/src/Query/Builder.php index 57c7da0..7528176 100644 --- a/src/Query/Builder.php +++ b/src/Query/Builder.php @@ -3,12 +3,15 @@ namespace Formal\AccessLayer\Query; -use Formal\AccessLayer\Query; +use Formal\AccessLayer\{ + Query, + Driver, +}; /** * @psalm-immutable */ interface Builder { - public function normalize(): Query; + public function normalize(Driver $driver): Query; } From 536426b8491f94a1cd300877d66b7ba83ca55f29 Mon Sep 17 00:00:00 2001 From: Baptiste Langlade Date: Mon, 17 Nov 2025 15:44:29 +0100 Subject: [PATCH 10/24] fix reaching transaction cases --- src/Connection/PDO.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Connection/PDO.php b/src/Connection/PDO.php index d639960..363d985 100644 --- a/src/Connection/PDO.php +++ b/src/Connection/PDO.php @@ -75,10 +75,6 @@ private function __construct(Url $dsn) #[\Override] public function __invoke(Query|Query\Builder $query): Sequence { - if ($query instanceof Query\Builder) { - $query = $query->normalize($this->driver); - } - return match (\get_class($query)) { Query\StartTransaction::class => $this->transaction( $query, @@ -117,8 +113,12 @@ private function transaction(Query $query, callable $action): Sequence /** * @return Sequence */ - private function execute(Query $query): Sequence + private function execute(Query|Query\Builder $query): Sequence { + if ($query instanceof Query\Builder) { + $query = $query->normalize($this->driver); + } + return match ($query->lazy()) { true => $this->lazy($query), false => $this->defer($query), From a5b9e6934177bbdf9fb565061a9e08ef7065c085 Mon Sep 17 00:00:00 2001 From: Baptiste Langlade Date: Mon, 17 Nov 2025 15:54:58 +0100 Subject: [PATCH 11/24] allow to specify all the query parameters at once --- CHANGELOG.md | 1 + src/Query/SQL.php | 13 ++++++++----- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 591c031..37d0e18 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ ### Added - `Formal\AccessLayer\Query\Builder` +- `Formal\AccessLayer\Query\SQL` named constructors allow to pass all the parameters at once via the second argument ### Changed diff --git a/src/Query/SQL.php b/src/Query/SQL.php index 6ee78c3..c706a75 100644 --- a/src/Query/SQL.php +++ b/src/Query/SQL.php @@ -35,10 +35,11 @@ private function __construct(string $sql, bool $lazy, Sequence $parameters) * @psalm-pure * * @param non-empty-string $sql + * @param Sequence $parameters */ - public static function of(string $sql): self + public static function of(string $sql, ?Sequence $parameters = null): self { - return new self($sql, false, Sequence::of()); + return new self($sql, false, $parameters ?? Sequence::of()); } /** @@ -46,8 +47,9 @@ public static function of(string $sql): self * @deprecated Use ::lazily() instead * * @param non-empty-string $sql + * @param Sequence $parameters */ - public static function onDemand(string $sql): self + public static function onDemand(string $sql, ?Sequence $parameters = null): self { return self::lazily($sql); } @@ -56,10 +58,11 @@ public static function onDemand(string $sql): self * @psalm-pure * * @param non-empty-string $sql + * @param Sequence $parameters */ - public static function lazily(string $sql): self + public static function lazily(string $sql, ?Sequence $parameters = null): self { - return new self($sql, true, Sequence::of()); + return new self($sql, true, $parameters ?? Sequence::of()); } public function with(Parameter $parameter): self From 3b04e566944e6e71128cdf942ada9c0ecf29a1cb Mon Sep 17 00:00:00 2001 From: Baptiste Langlade Date: Mon, 17 Nov 2025 16:10:13 +0100 Subject: [PATCH 12/24] make custom queries implement Query\Builder --- src/Connection/PDO.php | 6 ++-- src/Query/Commit.php | 20 ++--------- src/Query/CreateTable.php | 22 +++--------- src/Query/Delete.php | 27 +++++---------- src/Query/DropTable.php | 23 +++---------- src/Query/Insert.php | 62 +++++++++++++--------------------- src/Query/MultipleInsert.php | 25 +++++--------- src/Query/Rollback.php | 20 ++--------- src/Query/Select.php | 36 ++++++++------------ src/Query/StartTransaction.php | 20 ++--------- src/Query/Update.php | 39 ++++++++------------- 11 files changed, 91 insertions(+), 209 deletions(-) diff --git a/src/Connection/PDO.php b/src/Connection/PDO.php index 363d985..a848767 100644 --- a/src/Connection/PDO.php +++ b/src/Connection/PDO.php @@ -77,15 +77,15 @@ public function __invoke(Query|Query\Builder $query): Sequence { return match (\get_class($query)) { Query\StartTransaction::class => $this->transaction( - $query, + $query->normalize($this->driver), fn(): bool => $this->pdo->beginTransaction(), ), Query\Commit::class => $this->transaction( - $query, + $query->normalize($this->driver), fn(): bool => $this->pdo->commit(), ), Query\Rollback::class => $this->transaction( - $query, + $query->normalize($this->driver), fn(): bool => $this->pdo->rollBack(), ), default => $this->execute($query), diff --git a/src/Query/Commit.php b/src/Query/Commit.php index 40ad582..cd0802a 100644 --- a/src/Query/Commit.php +++ b/src/Query/Commit.php @@ -7,29 +7,15 @@ Query, Driver, }; -use Innmind\Immutable\Sequence; /** * @psalm-immutable */ -final class Commit implements Query +final class Commit implements Builder { #[\Override] - public function parameters(): Sequence + public function normalize(Driver $driver): Query { - /** @var Sequence */ - return Sequence::of(); - } - - #[\Override] - public function sql(Driver $driver): string - { - return 'COMMIT'; - } - - #[\Override] - public function lazy(): bool - { - return false; + return SQL::of('COMMIT'); } } diff --git a/src/Query/CreateTable.php b/src/Query/CreateTable.php index 77a7599..a73e313 100644 --- a/src/Query/CreateTable.php +++ b/src/Query/CreateTable.php @@ -20,7 +20,7 @@ /** * @psalm-immutable */ -final class CreateTable implements Query +final class CreateTable implements Builder { private Name $name; /** @var Sequence */ @@ -102,17 +102,9 @@ public function constraint(PrimaryKey|ForeignKey|Unique $constraint): self } #[\Override] - public function parameters(): Sequence + public function normalize(Driver $driver): Query { - /** @var Sequence */ - return Sequence::of(); - } - - #[\Override] - public function sql(Driver $driver): string - { - /** @var non-empty-string */ - return \sprintf( + return SQL::of(\sprintf( 'CREATE TABLE %s %s (%s%s)', $this->ifNotExists ? 'IF NOT EXISTS' : '', $this->name->sql($driver), @@ -129,12 +121,6 @@ public function sql(Driver $driver): string ->toString(), static fn() => '', ), - ); - } - - #[\Override] - public function lazy(): bool - { - return false; + )); } } diff --git a/src/Query/Delete.php b/src/Query/Delete.php index d871d47..fb422cc 100644 --- a/src/Query/Delete.php +++ b/src/Query/Delete.php @@ -19,7 +19,7 @@ /** * @psalm-immutable */ -final class Delete implements Query +final class Delete implements Builder { private Name|Name\Aliased $table; /** @var Sequence */ @@ -66,24 +66,15 @@ public function where(Specification $specification): self } #[\Override] - public function parameters(): Sequence + public function normalize(Driver $driver): Query { - return $this->where->parameters(); - } - - #[\Override] - public function sql(Driver $driver): string - { - return match ($driver) { - Driver::mysql => $this->mysqlSql($driver), - Driver::postgres => $this->postgresSql($driver), - }; - } - - #[\Override] - public function lazy(): bool - { - return false; + return SQL::of( + match ($driver) { + Driver::mysql => $this->mysqlSql($driver), + Driver::postgres => $this->postgresSql($driver), + }, + $this->where->parameters(), + ); } /** diff --git a/src/Query/DropTable.php b/src/Query/DropTable.php index e062ac5..e548ed2 100644 --- a/src/Query/DropTable.php +++ b/src/Query/DropTable.php @@ -8,12 +8,11 @@ Table\Name, Driver, }; -use Innmind\Immutable\Sequence; /** * @psalm-immutable */ -final class DropTable implements Query +final class DropTable implements Builder { private Name $name; private bool $ifExists; @@ -41,26 +40,12 @@ public static function ifExists(Name $name): self } #[\Override] - public function parameters(): Sequence + public function normalize(Driver $driver): Query { - /** @var Sequence */ - return Sequence::of(); - } - - #[\Override] - public function sql(Driver $driver): string - { - /** @var non-empty-string */ - return \sprintf( + return SQL::of(\sprintf( 'DROP TABLE %s %s', $this->ifExists ? 'IF EXISTS' : '', $this->name->sql($driver), - ); - } - - #[\Override] - public function lazy(): bool - { - return false; + )); } } diff --git a/src/Query/Insert.php b/src/Query/Insert.php index f7a4d68..c163c17 100644 --- a/src/Query/Insert.php +++ b/src/Query/Insert.php @@ -17,7 +17,7 @@ /** * @psalm-immutable */ -final class Insert implements Query +final class Insert implements Builder { private function __construct( private Name $table, @@ -34,36 +34,11 @@ public static function into(Name $table, Row|Select $row): self } #[\Override] - public function parameters(): Sequence - { - if ($this->row instanceof Select) { - return $this->row->parameters(); - } - - return $this->row->values()->map( - static fn($value) => Parameter::of($value->value(), $value->type()), - ); - } - - #[\Override] - public function sql(Driver $driver): string - { - return $this->buildInsert($driver); - } - - #[\Override] - public function lazy(): bool - { - return false; - } - - /** - * @return non-empty-string - */ - private function buildInsert(Driver $driver): string + public function normalize(Driver $driver): Query { if ($this->row instanceof Select) { $columns = $this->row->names(); + $query = $this->row->normalize($driver); if ($columns->empty()) { throw new \LogicException('You need to specify the columns to select when inserting'); @@ -71,11 +46,14 @@ private function buildInsert(Driver $driver): string $keys = $columns->map(static fn($column) => $column->sql($driver)); - return \sprintf( - 'INSERT INTO %s (%s) %s', - $this->table->sql($driver), - Str::of(', ')->join($keys)->toString(), - $this->row->sql($driver), + return SQL::of( + \sprintf( + 'INSERT INTO %s (%s) %s', + $this->table->sql($driver), + Str::of(', ')->join($keys)->toString(), + $query->sql($driver), + ), + $query->parameters(), ); } @@ -84,11 +62,19 @@ private function buildInsert(Driver $driver): string /** @var Sequence */ $values = $this->row->values()->map(static fn() => '?'); - return \sprintf( - 'INSERT INTO %s (%s) VALUES (%s)', - $this->table->sql($driver), - Str::of(', ')->join($keys)->toString(), - Str::of(', ')->join($values)->toString(), + return SQL::of( + \sprintf( + 'INSERT INTO %s (%s) VALUES (%s)', + $this->table->sql($driver), + Str::of(', ')->join($keys)->toString(), + Str::of(', ')->join($values)->toString(), + ), + $this->row->values()->map( + static fn($value) => Parameter::of( + $value->value(), + $value->type(), + ), + ), ); } } diff --git a/src/Query/MultipleInsert.php b/src/Query/MultipleInsert.php index 1149283..82418fa 100644 --- a/src/Query/MultipleInsert.php +++ b/src/Query/MultipleInsert.php @@ -18,7 +18,7 @@ /** * @psalm-immutable */ -final class MultipleInsert implements Query +final class MultipleInsert implements Builder { private Name $table; /** @var Sequence */ @@ -62,23 +62,14 @@ public static function into( } #[\Override] - public function parameters(): Sequence + public function normalize(Driver $driver): Query { - return $this->rows->flatMap(static fn($row) => $row->values()->map( - static fn($value) => Parameter::of($value->value(), $value->type()), - )); - } - - #[\Override] - public function sql(Driver $driver): string - { - return $this->buildInsert($driver); - } - - #[\Override] - public function lazy(): bool - { - return false; + return SQL::of( + $this->buildInsert($driver), + $this->rows->flatMap(static fn($row) => $row->values()->map( + static fn($value) => Parameter::of($value->value(), $value->type()), + )), + ); } /** diff --git a/src/Query/Rollback.php b/src/Query/Rollback.php index 1b22d9b..2895a75 100644 --- a/src/Query/Rollback.php +++ b/src/Query/Rollback.php @@ -7,29 +7,15 @@ Query, Driver, }; -use Innmind\Immutable\Sequence; /** * @psalm-immutable */ -final class Rollback implements Query +final class Rollback implements Builder { #[\Override] - public function parameters(): Sequence + public function normalize(Driver $driver): Query { - /** @var Sequence */ - return Sequence::of(); - } - - #[\Override] - public function sql(Driver $driver): string - { - return 'ROLLBACK'; - } - - #[\Override] - public function lazy(): bool - { - return false; + return SQL::of('ROLLBACK'); } } diff --git a/src/Query/Select.php b/src/Query/Select.php index c17f0cd..9eeafad 100644 --- a/src/Query/Select.php +++ b/src/Query/Select.php @@ -24,7 +24,7 @@ /** * @psalm-immutable */ -final class Select implements Query +final class Select implements Builder { /** * @param Sequence $joins @@ -206,23 +206,10 @@ public function limit(int $limit, ?int $offset = null): self } #[\Override] - public function parameters(): Sequence - { - return $this - ->columns - ->keep(Instance::of(Row\Value::class)) - ->map(static fn($value) => Parameter::of( - $value->value(), - $value->type(), - )) - ->append($this->where->parameters()); - } - - #[\Override] - public function sql(Driver $driver): string + public function normalize(Driver $driver): Query { /** @var non-empty-string */ - return \sprintf( + $sql = \sprintf( 'SELECT %s FROM %s%s %s%s%s%s', $this ->count @@ -256,12 +243,19 @@ public function sql(Driver $driver): string default => ' OFFSET '.$this->offset, }, ); - } + $parameters = $this + ->columns + ->keep(Instance::of(Row\Value::class)) + ->map(static fn($value) => Parameter::of( + $value->value(), + $value->type(), + )) + ->append($this->where->parameters()); - #[\Override] - public function lazy(): bool - { - return $this->lazy; + return match ($this->lazy) { + true => SQL::lazily($sql, $parameters), + false => SQL::of($sql, $parameters), + }; } /** diff --git a/src/Query/StartTransaction.php b/src/Query/StartTransaction.php index 5fd1248..83e43e3 100644 --- a/src/Query/StartTransaction.php +++ b/src/Query/StartTransaction.php @@ -7,29 +7,15 @@ Query, Driver, }; -use Innmind\Immutable\Sequence; /** * @psalm-immutable */ -final class StartTransaction implements Query +final class StartTransaction implements Builder { #[\Override] - public function parameters(): Sequence + public function normalize(Driver $driver): Query { - /** @var Sequence */ - return Sequence::of(); - } - - #[\Override] - public function sql(Driver $driver): string - { - return 'START TRANSACTION'; - } - - #[\Override] - public function lazy(): bool - { - return false; + return SQL::of('START TRANSACTION'); } } diff --git a/src/Query/Update.php b/src/Query/Update.php index 75c2043..871c696 100644 --- a/src/Query/Update.php +++ b/src/Query/Update.php @@ -18,7 +18,7 @@ /** * @psalm-immutable */ -final class Update implements Query +final class Update implements Builder { private Name|Name\Aliased $table; private Row $row; @@ -52,36 +52,27 @@ public function where(Specification $specification): self } #[\Override] - public function parameters(): Sequence - { - return $this - ->row - ->values() - ->map(static fn($value) => Parameter::of($value->value(), $value->type())) - ->append($this->where->parameters()); - } - - #[\Override] - public function sql(Driver $driver): string + public function normalize(Driver $driver): Query { /** @var Sequence */ $columns = $this ->row ->values() ->map(static fn($value) => "{$value->columnSql($driver)} = ?"); + $parameters = $this + ->row + ->values() + ->map(static fn($value) => Parameter::of($value->value(), $value->type())) + ->append($this->where->parameters()); - /** @var non-empty-string */ - return \sprintf( - 'UPDATE %s SET %s %s', - $this->table->sql($driver), - Str::of(', ')->join($columns)->toString(), - $this->where->sql($driver), + return SQL::of( + \sprintf( + 'UPDATE %s SET %s %s', + $this->table->sql($driver), + Str::of(', ')->join($columns)->toString(), + $this->where->sql($driver), + ), + $parameters, ); } - - #[\Override] - public function lazy(): bool - { - return false; - } } From 9dc963bdac9d9dbf2d94dbcdc78ac7eb031adeec Mon Sep 17 00:00:00 2001 From: Baptiste Langlade Date: Mon, 17 Nov 2025 16:18:09 +0100 Subject: [PATCH 13/24] make Query a final class --- CHANGELOG.md | 3 +- src/Query.php | 66 ++++++++++++++++++++++++++++-- src/Query/Commit.php | 2 +- src/Query/CreateTable.php | 2 +- src/Query/Delete.php | 2 +- src/Query/DropTable.php | 2 +- src/Query/Insert.php | 4 +- src/Query/MultipleInsert.php | 2 +- src/Query/Rollback.php | 2 +- src/Query/SQL.php | 75 ++++------------------------------ src/Query/Select.php | 4 +- src/Query/StartTransaction.php | 2 +- src/Query/Update.php | 2 +- 13 files changed, 85 insertions(+), 83 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 37d0e18..436704d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,11 +11,12 @@ - Requires PHP `8.4` - `Formal\AccessLayer\Connection` is now a final class, all previous implementations are now flagged as internal +- `Formal\AccessLayer\Query` is now a final class ### Deprecated - `Formal\AccessLayer\Query\Select::onDemand()`, use `::lazily()` instead -- `Formal\AccessLayer\Query\SQL::onDemand()`, use `::lazily()` instead +- `Formal\AccessLayer\Query\SQL`, use `Formal\AccessLayer\Query` instead ### Removed diff --git a/src/Query.php b/src/Query.php index b5f5d3d..7bcb78f 100644 --- a/src/Query.php +++ b/src/Query.php @@ -9,16 +9,74 @@ /** * @psalm-immutable */ -interface Query +final class Query { + /** @var non-empty-string */ + private string $sql; + private bool $lazy; + /** @var Sequence */ + private Sequence $parameters; + + /** + * @param non-empty-string $sql + * @param Sequence $parameters + */ + private function __construct(string $sql, bool $lazy, Sequence $parameters) + { + $this->sql = $sql; + $this->lazy = $lazy; + $this->parameters = $parameters; + } + + /** + * @psalm-pure + * + * @param non-empty-string $sql + * @param Sequence $parameters + */ + public static function of(string $sql, ?Sequence $parameters = null): self + { + return new self($sql, false, $parameters ?? Sequence::of()); + } + + /** + * @psalm-pure + * + * @param non-empty-string $sql + * @param Sequence $parameters + */ + public static function lazily(string $sql, ?Sequence $parameters = null): self + { + return new self($sql, true, $parameters ?? Sequence::of()); + } + + public function with(Parameter $parameter): self + { + return new self( + $this->sql, + $this->lazy, + ($this->parameters)($parameter), + ); + } + /** * @return Sequence */ - public function parameters(): Sequence; + public function parameters(): Sequence + { + return $this->parameters; + } /** * @return non-empty-string */ - public function sql(Driver $driver): string; - public function lazy(): bool; + public function sql(Driver $driver): string + { + return $this->sql; + } + + public function lazy(): bool + { + return $this->lazy; + } } diff --git a/src/Query/Commit.php b/src/Query/Commit.php index cd0802a..12d4ff3 100644 --- a/src/Query/Commit.php +++ b/src/Query/Commit.php @@ -16,6 +16,6 @@ final class Commit implements Builder #[\Override] public function normalize(Driver $driver): Query { - return SQL::of('COMMIT'); + return Query::of('COMMIT'); } } diff --git a/src/Query/CreateTable.php b/src/Query/CreateTable.php index a73e313..283868d 100644 --- a/src/Query/CreateTable.php +++ b/src/Query/CreateTable.php @@ -104,7 +104,7 @@ public function constraint(PrimaryKey|ForeignKey|Unique $constraint): self #[\Override] public function normalize(Driver $driver): Query { - return SQL::of(\sprintf( + return Query::of(\sprintf( 'CREATE TABLE %s %s (%s%s)', $this->ifNotExists ? 'IF NOT EXISTS' : '', $this->name->sql($driver), diff --git a/src/Query/Delete.php b/src/Query/Delete.php index fb422cc..910d4ab 100644 --- a/src/Query/Delete.php +++ b/src/Query/Delete.php @@ -68,7 +68,7 @@ public function where(Specification $specification): self #[\Override] public function normalize(Driver $driver): Query { - return SQL::of( + return Query::of( match ($driver) { Driver::mysql => $this->mysqlSql($driver), Driver::postgres => $this->postgresSql($driver), diff --git a/src/Query/DropTable.php b/src/Query/DropTable.php index e548ed2..484d37f 100644 --- a/src/Query/DropTable.php +++ b/src/Query/DropTable.php @@ -42,7 +42,7 @@ public static function ifExists(Name $name): self #[\Override] public function normalize(Driver $driver): Query { - return SQL::of(\sprintf( + return Query::of(\sprintf( 'DROP TABLE %s %s', $this->ifExists ? 'IF EXISTS' : '', $this->name->sql($driver), diff --git a/src/Query/Insert.php b/src/Query/Insert.php index c163c17..228239a 100644 --- a/src/Query/Insert.php +++ b/src/Query/Insert.php @@ -46,7 +46,7 @@ public function normalize(Driver $driver): Query $keys = $columns->map(static fn($column) => $column->sql($driver)); - return SQL::of( + return Query::of( \sprintf( 'INSERT INTO %s (%s) %s', $this->table->sql($driver), @@ -62,7 +62,7 @@ public function normalize(Driver $driver): Query /** @var Sequence */ $values = $this->row->values()->map(static fn() => '?'); - return SQL::of( + return Query::of( \sprintf( 'INSERT INTO %s (%s) VALUES (%s)', $this->table->sql($driver), diff --git a/src/Query/MultipleInsert.php b/src/Query/MultipleInsert.php index 82418fa..d2a7f2a 100644 --- a/src/Query/MultipleInsert.php +++ b/src/Query/MultipleInsert.php @@ -64,7 +64,7 @@ public static function into( #[\Override] public function normalize(Driver $driver): Query { - return SQL::of( + return Query::of( $this->buildInsert($driver), $this->rows->flatMap(static fn($row) => $row->values()->map( static fn($value) => Parameter::of($value->value(), $value->type()), diff --git a/src/Query/Rollback.php b/src/Query/Rollback.php index 2895a75..5f1d6dc 100644 --- a/src/Query/Rollback.php +++ b/src/Query/Rollback.php @@ -16,6 +16,6 @@ final class Rollback implements Builder #[\Override] public function normalize(Driver $driver): Query { - return SQL::of('ROLLBACK'); + return Query::of('ROLLBACK'); } } diff --git a/src/Query/SQL.php b/src/Query/SQL.php index c706a75..b414361 100644 --- a/src/Query/SQL.php +++ b/src/Query/SQL.php @@ -3,92 +3,35 @@ namespace Formal\AccessLayer\Query; -use Formal\AccessLayer\{ - Query, - Driver, -}; +use Formal\AccessLayer\Query; use Innmind\Immutable\Sequence; /** - * @psalm-immutable + * @deprecated Use Query instead */ -final class SQL implements Query +final class SQL { - /** @var non-empty-string */ - private string $sql; - private bool $lazy; - /** @var Sequence */ - private Sequence $parameters; - - /** - * @param non-empty-string $sql - * @param Sequence $parameters - */ - private function __construct(string $sql, bool $lazy, Sequence $parameters) - { - $this->sql = $sql; - $this->lazy = $lazy; - $this->parameters = $parameters; - } - /** * @psalm-pure + * @deprecated Use Query::of() instead * * @param non-empty-string $sql * @param Sequence $parameters */ - public static function of(string $sql, ?Sequence $parameters = null): self + public static function of(string $sql, ?Sequence $parameters = null): Query { - return new self($sql, false, $parameters ?? Sequence::of()); + return Query::of($sql, $parameters); } /** * @psalm-pure - * @deprecated Use ::lazily() instead + * @deprecated Use Query::lazily() instead * * @param non-empty-string $sql * @param Sequence $parameters */ - public static function onDemand(string $sql, ?Sequence $parameters = null): self - { - return self::lazily($sql); - } - - /** - * @psalm-pure - * - * @param non-empty-string $sql - * @param Sequence $parameters - */ - public static function lazily(string $sql, ?Sequence $parameters = null): self - { - return new self($sql, true, $parameters ?? Sequence::of()); - } - - public function with(Parameter $parameter): self - { - return new self( - $this->sql, - $this->lazy, - ($this->parameters)($parameter), - ); - } - - #[\Override] - public function parameters(): Sequence - { - return $this->parameters; - } - - #[\Override] - public function sql(Driver $driver): string - { - return $this->sql; - } - - #[\Override] - public function lazy(): bool + public static function onDemand(string $sql, ?Sequence $parameters = null): Query { - return $this->lazy; + return Query::lazily($sql, $parameters); } } diff --git a/src/Query/Select.php b/src/Query/Select.php index 9eeafad..4984db1 100644 --- a/src/Query/Select.php +++ b/src/Query/Select.php @@ -253,8 +253,8 @@ public function normalize(Driver $driver): Query ->append($this->where->parameters()); return match ($this->lazy) { - true => SQL::lazily($sql, $parameters), - false => SQL::of($sql, $parameters), + true => Query::lazily($sql, $parameters), + false => Query::of($sql, $parameters), }; } diff --git a/src/Query/StartTransaction.php b/src/Query/StartTransaction.php index 83e43e3..06bda65 100644 --- a/src/Query/StartTransaction.php +++ b/src/Query/StartTransaction.php @@ -16,6 +16,6 @@ final class StartTransaction implements Builder #[\Override] public function normalize(Driver $driver): Query { - return SQL::of('START TRANSACTION'); + return Query::of('START TRANSACTION'); } } diff --git a/src/Query/Update.php b/src/Query/Update.php index 871c696..dde5290 100644 --- a/src/Query/Update.php +++ b/src/Query/Update.php @@ -65,7 +65,7 @@ public function normalize(Driver $driver): Query ->map(static fn($value) => Parameter::of($value->value(), $value->type())) ->append($this->where->parameters()); - return SQL::of( + return Query::of( \sprintf( 'UPDATE %s SET %s %s', $this->table->sql($driver), From 9fc37ce6e3cd26ea4c6cbd13355d5eb130dbb34d Mon Sep 17 00:00:00 2001 From: Baptiste Langlade Date: Mon, 17 Nov 2025 16:26:01 +0100 Subject: [PATCH 14/24] no need to pass the Driver to Query::sql() as the sql is already formatted --- src/Connection/Logger.php | 4 ++-- src/Connection/PDO.php | 4 +--- src/Exception/QueryFailed.php | 8 ++------ src/Query.php | 2 +- src/Query/Delete.php | 2 +- src/Query/Insert.php | 2 +- src/Query/Select.php | 2 +- src/Query/Update.php | 2 +- src/Query/Where.php | 30 ++++++++++++++++++++++++++++-- 9 files changed, 38 insertions(+), 18 deletions(-) diff --git a/src/Connection/Logger.php b/src/Connection/Logger.php index c519869..5d3d3d0 100644 --- a/src/Connection/Logger.php +++ b/src/Connection/Logger.php @@ -42,7 +42,7 @@ public function __invoke(Query|Query\Builder $query): Sequence $this->logger->debug( 'Query {sql} is about to be executed', [ - 'sql' => $normalized->sql(Driver::mysql), + 'sql' => $normalized->sql(), 'parameters' => $normalized->parameters()->reduce( [], static fn(array $parameters, $parameter) => \array_merge( @@ -61,7 +61,7 @@ public function __invoke(Query|Query\Builder $query): Sequence $this->logger->error( 'Query {sql} failed with {kind}({message})', [ - 'sql' => $normalized->sql(Driver::mysql), + 'sql' => $normalized->sql(), 'kind' => \get_class($e), 'message' => $e->getMessage(), ], diff --git a/src/Connection/PDO.php b/src/Connection/PDO.php index a848767..1950da3 100644 --- a/src/Connection/PDO.php +++ b/src/Connection/PDO.php @@ -172,7 +172,7 @@ private function prepare(Query $query): \PDOStatement { $statement = $this->guard( $query, - fn() => $this->pdo->prepare($query->sql($this->driver)), + fn() => $this->pdo->prepare($query->sql()), ); $_ = $query->parameters()->reduce( @@ -226,7 +226,6 @@ private function guard(Query $query, callable $try): \PDOStatement } throw new QueryFailed( - $this->driver, $query, $errorInfo[0], $errorInfo[1], @@ -256,7 +255,6 @@ private function attempt(Query $query, callable $attempt): void } throw new QueryFailed( - $this->driver, $query, $errorInfo[0], $errorInfo[1], diff --git a/src/Exception/QueryFailed.php b/src/Exception/QueryFailed.php index 0a0e72f..5ba669f 100644 --- a/src/Exception/QueryFailed.php +++ b/src/Exception/QueryFailed.php @@ -3,10 +3,7 @@ namespace Formal\AccessLayer\Exception; -use Formal\AccessLayer\{ - Query, - Driver, -}; +use Formal\AccessLayer\Query; final class QueryFailed extends RuntimeException { @@ -16,7 +13,6 @@ final class QueryFailed extends RuntimeException private ?string $driverSpecificMessage; public function __construct( - Driver $driver, Query $query, string $sqlstate, ?int $code, @@ -29,7 +25,7 @@ public function __construct( $this->driverSpecificMessage = $message; parent::__construct(\sprintf( "Query '%s' failed with: [%s] [%s] %s", - $query->sql($driver), + $query->sql(), $sqlstate, (string) $code, (string) $message, diff --git a/src/Query.php b/src/Query.php index 7bcb78f..3e88ba5 100644 --- a/src/Query.php +++ b/src/Query.php @@ -70,7 +70,7 @@ public function parameters(): Sequence /** * @return non-empty-string */ - public function sql(Driver $driver): string + public function sql(): string { return $this->sql; } diff --git a/src/Query/Delete.php b/src/Query/Delete.php index 910d4ab..c0db880 100644 --- a/src/Query/Delete.php +++ b/src/Query/Delete.php @@ -73,7 +73,7 @@ public function normalize(Driver $driver): Query Driver::mysql => $this->mysqlSql($driver), Driver::postgres => $this->postgresSql($driver), }, - $this->where->parameters(), + $this->where->parameters($driver), ); } diff --git a/src/Query/Insert.php b/src/Query/Insert.php index 228239a..412b640 100644 --- a/src/Query/Insert.php +++ b/src/Query/Insert.php @@ -51,7 +51,7 @@ public function normalize(Driver $driver): Query 'INSERT INTO %s (%s) %s', $this->table->sql($driver), Str::of(', ')->join($keys)->toString(), - $query->sql($driver), + $query->sql(), ), $query->parameters(), ); diff --git a/src/Query/Select.php b/src/Query/Select.php index 4984db1..8f7b084 100644 --- a/src/Query/Select.php +++ b/src/Query/Select.php @@ -250,7 +250,7 @@ public function normalize(Driver $driver): Query $value->value(), $value->type(), )) - ->append($this->where->parameters()); + ->append($this->where->parameters($driver)); return match ($this->lazy) { true => Query::lazily($sql, $parameters), diff --git a/src/Query/Update.php b/src/Query/Update.php index dde5290..c8eb88d 100644 --- a/src/Query/Update.php +++ b/src/Query/Update.php @@ -63,7 +63,7 @@ public function normalize(Driver $driver): Query ->row ->values() ->map(static fn($value) => Parameter::of($value->value(), $value->type())) - ->append($this->where->parameters()); + ->append($this->where->parameters($driver)); return Query::of( \sprintf( diff --git a/src/Query/Where.php b/src/Query/Where.php index 06712e8..e14d24a 100644 --- a/src/Query/Where.php +++ b/src/Query/Where.php @@ -55,7 +55,7 @@ public static function everything(): self /** * @return Sequence */ - public function parameters(): Sequence + public function parameters(Driver $driver): Sequence { /** @var Sequence */ $parameters = Sequence::of(); @@ -65,6 +65,7 @@ public function parameters(): Sequence } return $this->findParamaters( + $driver, $parameters, $this->specification, ); @@ -203,7 +204,15 @@ private function buildInSql( return \sprintf( '%s IN (%s)', $this->buildColumn($driver, $specification), - $specification->value()->sql($driver), + $specification->value()->sql(), + ); + } + + if ($specification->value() instanceof Builder) { + return \sprintf( + '%s IN (%s)', + $this->buildColumn($driver, $specification), + $specification->value()->normalize($driver)->sql(), ); } @@ -227,6 +236,7 @@ private function buildInSql( * @return Sequence */ private function findParamaters( + Driver $driver, Sequence $parameters, Specification $specification, ): Sequence { @@ -241,6 +251,7 @@ private function findParamaters( $specification->left()->value() === $specification->right()->value() ) { return $this->findComparatorParameters( + $driver, $parameters, $specification->left(), ); @@ -257,6 +268,7 @@ private function findParamaters( $specification->left()->value() === $specification->right()->value() ) { return $this->findComparatorParameters( + $driver, $parameters, $specification->left(), ); @@ -264,17 +276,21 @@ private function findParamaters( return match (true) { $specification instanceof Not => $this->findParamaters( + $driver, $parameters, $specification->specification(), ), $specification instanceof Composite => $this->findParamaters( + $driver, $parameters, $specification->left(), )->append($this->findParamaters( + $driver, $parameters, $specification->right(), )), $specification instanceof Comparator => $this->findComparatorParameters( + $driver, $parameters, $specification, ), @@ -287,6 +303,7 @@ private function findParamaters( * @return Sequence */ private function findComparatorParameters( + Driver $driver, Sequence $parameters, Comparator $specification, ): Sequence { @@ -306,6 +323,15 @@ private function findComparatorParameters( return $parameters->append($specification->value()->parameters()); } + if ($specification->value() instanceof Builder) { + return $parameters->append( + $specification + ->value() + ->normalize($driver) + ->parameters(), + ); + } + /** * @var mixed $in */ From 7683c555699c62e198b4ce270b6287cc683c7152 Mon Sep 17 00:00:00 2001 From: Baptiste Langlade Date: Mon, 17 Nov 2025 16:34:17 +0100 Subject: [PATCH 15/24] normalize where clauses in a single operation --- CHANGELOG.md | 1 + src/Query/Delete.php | 15 ++++++++------- src/Query/Select.php | 5 +++-- src/Query/Update.php | 5 +++-- src/Query/Where.php | 38 +++++++++++++++----------------------- 5 files changed, 30 insertions(+), 34 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 436704d..a92d168 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ ### Removed - `Formal\AccessLayer\Connection\PDO::persistent()` +- `Formal\AccessLayer\Query\Where::sql()` and `::parameters()`, use `::normalize()` instead ## 4.2.0 - 2025-04-09 diff --git a/src/Query/Delete.php b/src/Query/Delete.php index c0db880..a4af30c 100644 --- a/src/Query/Delete.php +++ b/src/Query/Delete.php @@ -68,19 +68,21 @@ public function where(Specification $specification): self #[\Override] public function normalize(Driver $driver): Query { + [$where, $parameters] = $this->where->normalize($driver); + return Query::of( match ($driver) { - Driver::mysql => $this->mysqlSql($driver), - Driver::postgres => $this->postgresSql($driver), + Driver::mysql => $this->mysqlSql($driver, $where), + Driver::postgres => $this->postgresSql($driver, $where), }, - $this->where->parameters($driver), + $parameters, ); } /** * @return non-empty-string */ - private function mysqlSql(Driver $driver): string + private function mysqlSql(Driver $driver, string $where): string { /** @var non-empty-string */ return \sprintf( @@ -96,17 +98,16 @@ private function mysqlSql(Driver $driver): string ->map(Str::of(...)) ->fold(new Concat) ->toString(), - $this->where->sql($driver), + $where, ); } /** * @return non-empty-string */ - private function postgresSql(Driver $driver): string + private function postgresSql(Driver $driver, string $where): string { $joins = ''; - $where = $this->where->sql($driver); if (!$this->joins->empty()) { $joins = Str::of(', ') diff --git a/src/Query/Select.php b/src/Query/Select.php index 8f7b084..8c362e2 100644 --- a/src/Query/Select.php +++ b/src/Query/Select.php @@ -208,6 +208,7 @@ public function limit(int $limit, ?int $offset = null): self #[\Override] public function normalize(Driver $driver): Query { + [$where, $parameters] = $this->where->normalize($driver); /** @var non-empty-string */ $sql = \sprintf( 'SELECT %s FROM %s%s %s%s%s%s', @@ -225,7 +226,7 @@ public function normalize(Driver $driver): Query ->map(Str::of(...)) ->fold(new Concat) ->toString(), - $this->where->sql($driver), + $where, match ($this->orderBy) { null => '', default => \sprintf( @@ -250,7 +251,7 @@ public function normalize(Driver $driver): Query $value->value(), $value->type(), )) - ->append($this->where->parameters($driver)); + ->append($parameters); return match ($this->lazy) { true => Query::lazily($sql, $parameters), diff --git a/src/Query/Update.php b/src/Query/Update.php index c8eb88d..ba635c5 100644 --- a/src/Query/Update.php +++ b/src/Query/Update.php @@ -54,6 +54,7 @@ public function where(Specification $specification): self #[\Override] public function normalize(Driver $driver): Query { + [$where, $parameters] = $this->where->normalize($driver); /** @var Sequence */ $columns = $this ->row @@ -63,14 +64,14 @@ public function normalize(Driver $driver): Query ->row ->values() ->map(static fn($value) => Parameter::of($value->value(), $value->type())) - ->append($this->where->parameters($driver)); + ->append($parameters); return Query::of( \sprintf( 'UPDATE %s SET %s %s', $this->table->sql($driver), Str::of(', ')->join($columns)->toString(), - $this->where->sql($driver), + $where, ), $parameters, ); diff --git a/src/Query/Where.php b/src/Query/Where.php index e14d24a..47ad4bc 100644 --- a/src/Query/Where.php +++ b/src/Query/Where.php @@ -53,34 +53,26 @@ public static function everything(): self } /** - * @return Sequence + * @return array{string, Sequence} */ - public function parameters(Driver $driver): Sequence + public function normalize(Driver $driver): array { - /** @var Sequence */ - $parameters = Sequence::of(); - if (\is_null($this->specification)) { - return $parameters; + return ['', Sequence::of()]; } - return $this->findParamaters( - $driver, - $parameters, - $this->specification, - ); - } - - public function sql(Driver $driver): string - { - if (\is_null($this->specification)) { - return ''; - } - - return \sprintf( - 'WHERE %s', - $this->buildSql($driver, $this->specification), - ); + // todo optimize this process to avoid traversing everything twice + return [ + \sprintf( + 'WHERE %s', + $this->buildSql($driver, $this->specification), + ), + $this->findParamaters( + $driver, + Sequence::of(), + $this->specification, + ), + ]; } private function buildSql( From 21407f311ab6082de418cf6761aa0348f65121c8 Mon Sep 17 00:00:00 2001 From: Baptiste Langlade Date: Mon, 17 Nov 2025 16:57:24 +0100 Subject: [PATCH 16/24] fix tests --- proofs/connection/lazy.php | 12 +-- proofs/connection/logger.php | 10 +-- proofs/connection/pdo.php | 28 ++----- proofs/query/where.php | 152 +++++++++++++++++++--------------- src/Connection/PDO.php | 78 +++++++++++------ src/Exception/QueryFailed.php | 9 +- 6 files changed, 158 insertions(+), 131 deletions(-) diff --git a/proofs/connection/lazy.php b/proofs/connection/lazy.php index e4d43f0..ce85537 100644 --- a/proofs/connection/lazy.php +++ b/proofs/connection/lazy.php @@ -1,19 +1,15 @@ PDO::of(Url::of("mysql://root:root@127.0.0.1:$port/example")), + $connection = Connection::lazy( + static fn() => Connection::new(Url::of("mysql://root:root@127.0.0.1:$port/example")), ); Properties::seed($connection); $connections = Set\Call::of(static function() use ($connection) { @@ -32,7 +28,7 @@ yield test( 'Lazy connection must not be established at instanciation', static fn($assert) => $assert - ->object(Lazy::of(static fn() => PDO::of(Url::of('mysql://unknown:unknown@127.0.0.1:3306/unknown')))) + ->object(Connection::lazy(static fn() => Connection::new(Url::of('mysql://unknown:unknown@127.0.0.1:3306/unknown')))) ->instance(Connection::class), ); diff --git a/proofs/connection/logger.php b/proofs/connection/logger.php index a6eb4e7..b0a7d43 100644 --- a/proofs/connection/logger.php +++ b/proofs/connection/logger.php @@ -1,11 +1,7 @@ name})", @@ -55,7 +46,7 @@ ->instance(Connection::class), ); - $lazy = static fn($connection) => test( + yield test( "PDO lazy select doesnt load everything in memory({$driver->name})", static function($assert) use ($connection) { $table = Table\Name::of('test_lazy_load'); @@ -99,9 +90,6 @@ static function($assert) use ($connection) { }, ); - yield $lazy($connection); - yield $lazy($persistent); - if ($driver === Driver::mysql) { yield test( "PDO charset({$driver->name})", @@ -125,7 +113,7 @@ static function($assert) use ($connection, $dsn) { $select = Select::from($table); - $ascii = PDO::of($dsn->withQuery(Query::of('charset=ascii'))); + $ascii = Connection::new($dsn->withQuery(Query::of('charset=ascii'))); $assert ->expected('gelé') ->not() @@ -139,7 +127,7 @@ static function($assert) use ($connection, $dsn) { ), ); - $utf8 = PDO::of($dsn->withQuery(Query::of('charset=utf8mb4'))); + $utf8 = Connection::new($dsn->withQuery(Query::of('charset=utf8mb4'))); $assert->same( 'gelé', $utf8($select) diff --git a/proofs/query/where.php b/proofs/query/where.php index 24aa1ea..9707f5c 100644 --- a/proofs/query/where.php +++ b/proofs/query/where.php @@ -23,9 +23,10 @@ static function($assert) { $where = Where::of(null); + [$sql, $parameters] = $where->normalize(Driver::mysql); $assert->object($where)->instance(Where::class); - $assert->same('', $where->sql(Driver::mysql)); - $assert->count(0, $where->parameters()); + $assert->same('', $sql); + $assert->count(0, $parameters); }, ); @@ -43,12 +44,13 @@ static function($assert, $column, $value) { ); $where = Where::of($specification); + [$sql, $parameters] = $where->normalize(Driver::mysql); $assert->same( "WHERE {$column->name()->sql(Driver::mysql)} = ?", - $where->sql(Driver::mysql), + $sql, ); - $assert->count(1, $where->parameters()); - $assert->same($value, $where->parameters()->first()->match( + $assert->count(1, $parameters); + $assert->same($value, $parameters->first()->match( static fn($parameter) => $parameter->value(), static fn() => null, )); @@ -69,12 +71,13 @@ static function($assert, $column, $value) { ); $where = Where::of($specification); + [$sql, $parameters] = $where->normalize(Driver::mysql); $assert->same( "WHERE {$column->name()->sql(Driver::mysql)} < ?", - $where->sql(Driver::mysql), + $sql, ); - $assert->count(1, $where->parameters()); - $assert->same($value, $where->parameters()->first()->match( + $assert->count(1, $parameters); + $assert->same($value, $parameters->first()->match( static fn($parameter) => $parameter->value(), static fn() => null, )); @@ -100,12 +103,13 @@ static function($assert, $column, $value) { ); $where = Where::of($lessThan->or($equal)); + [$sql, $parameters] = $where->normalize(Driver::mysql); $assert->same( "WHERE {$column->name()->sql(Driver::mysql)} <= ?", - $where->sql(Driver::mysql), + $sql, ); - $assert->count(1, $where->parameters()); - $assert->same($value, $where->parameters()->first()->match( + $assert->count(1, $parameters); + $assert->same($value, $parameters->first()->match( static fn($parameter) => $parameter->value(), static fn() => null, )); @@ -131,17 +135,18 @@ static function($assert, $column1, $column2, $value) { $value, ); $where = Where::of($lessThan->or($equal)); + [$sql, $parameters] = $where->normalize(Driver::mysql); $assert->same( "WHERE ({$column1->name()->sql(Driver::mysql)} < ? OR {$column2->name()->sql(Driver::mysql)} = ?)", - $where->sql(Driver::mysql), + $sql, ); - $assert->count(2, $where->parameters()); - $assert->same($value, $where->parameters()->first()->match( + $assert->count(2, $parameters); + $assert->same($value, $parameters->first()->match( static fn($parameter) => $parameter->value(), static fn() => null, )); - $assert->same($value, $where->parameters()->last()->match( + $assert->same($value, $parameters->last()->match( static fn($parameter) => $parameter->value(), static fn() => null, )); @@ -162,12 +167,13 @@ static function($assert, $column, $value) { ); $where = Where::of($specification); + [$sql, $parameters] = $where->normalize(Driver::mysql); $assert->same( "WHERE {$column->name()->sql(Driver::mysql)} > ?", - $where->sql(Driver::mysql), + $sql, ); - $assert->count(1, $where->parameters()); - $assert->same($value, $where->parameters()->first()->match( + $assert->count(1, $parameters); + $assert->same($value, $parameters->first()->match( static fn($parameter) => $parameter->value(), static fn() => null, )); @@ -193,12 +199,13 @@ static function($assert, $column, $value) { ); $where = Where::of($moreThan->or($equal)); + [$sql, $parameters] = $where->normalize(Driver::mysql); $assert->same( "WHERE {$column->name()->sql(Driver::mysql)} >= ?", - $where->sql(Driver::mysql), + $sql, ); - $assert->count(1, $where->parameters()); - $assert->same($value, $where->parameters()->first()->match( + $assert->count(1, $parameters); + $assert->same($value, $parameters->first()->match( static fn($parameter) => $parameter->value(), static fn() => null, )); @@ -225,16 +232,17 @@ static function($assert, $column1, $column2, $value) { ); $where = Where::of($moreThan->or($equal)); + [$sql, $parameters] = $where->normalize(Driver::mysql); $assert->same( "WHERE ({$column1->name()->sql(Driver::mysql)} > ? OR {$column2->name()->sql(Driver::mysql)} = ?)", - $where->sql(Driver::mysql), + $sql, ); - $assert->count(2, $where->parameters()); - $assert->same($value, $where->parameters()->first()->match( + $assert->count(2, $parameters); + $assert->same($value, $parameters->first()->match( static fn($parameter) => $parameter->value(), static fn() => null, )); - $assert->same($value, $where->parameters()->last()->match( + $assert->same($value, $parameters->last()->match( static fn($parameter) => $parameter->value(), static fn() => null, )); @@ -252,11 +260,12 @@ static function($assert, $column) { ); $where = Where::of($specification); + [$sql, $parameters] = $where->normalize(Driver::mysql); $assert->same( "WHERE {$column->name()->sql(Driver::mysql)} IS NULL", - $where->sql(Driver::mysql), + $sql, ); - $assert->count(0, $where->parameters()); + $assert->count(0, $parameters); }, ); @@ -271,11 +280,12 @@ static function($assert, $column) { ); $where = Where::of($specification->not()); + [$sql, $parameters] = $where->normalize(Driver::mysql); $assert->same( "WHERE {$column->name()->sql(Driver::mysql)} IS NOT NULL", - $where->sql(Driver::mysql), + $sql, ); - $assert->count(0, $where->parameters()); + $assert->count(0, $parameters); }, ); @@ -299,20 +309,21 @@ static function($assert, $column, $value1, $value2, $value3, $values) { ); $where = Where::of($specification); + [$sql, $parameters] = $where->normalize(Driver::mysql); $assert->same( "WHERE {$column->name()->sql(Driver::mysql)} IN (?, ?, ?)", - $where->sql(Driver::mysql), + $sql, ); - $assert->count(3, $where->parameters()); - $assert->same($value1, $where->parameters()->get(0)->match( + $assert->count(3, $parameters); + $assert->same($value1, $parameters->get(0)->match( static fn($parameter) => $parameter->value(), static fn() => null, )); - $assert->same($value2, $where->parameters()->get(1)->match( + $assert->same($value2, $parameters->get(1)->match( static fn($parameter) => $parameter->value(), static fn() => null, )); - $assert->same($value3, $where->parameters()->get(2)->match( + $assert->same($value3, $parameters->get(2)->match( static fn($parameter) => $parameter->value(), static fn() => null, )); @@ -324,11 +335,12 @@ static function($assert, $column, $value1, $value2, $value3, $values) { ); $where = Where::of($specification); + [$sql, $parameters] = $where->normalize(Driver::mysql); $assert->same( \count($values), - \count_chars($where->sql(Driver::mysql))[63], // looking for '?' placeholders + \count_chars($sql)[63], // looking for '?' placeholders ); - $assert->count(\count($values), $where->parameters()); + $assert->count(\count($values), $parameters); }, ); @@ -347,12 +359,13 @@ static function($assert, $column, $leftValue, $rightValue){ ); $where = Where::of($specification->not()); + [$sql, $parameters] = $where->normalize(Driver::mysql); $assert->same( "WHERE {$column->name()->sql(Driver::mysql)} <> ?", - $where->sql(Driver::mysql), + $sql, ); - $assert->count(1, $where->parameters()); - $assert->same($leftValue, $where->parameters()->first()->match( + $assert->count(1, $parameters); + $assert->same($leftValue, $parameters->first()->match( static fn($parameter) => $parameter->value(), static fn() => null, )); @@ -370,16 +383,17 @@ static function($assert, $column, $leftValue, $rightValue){ $specification = $left->or($right); $where = Where::of($specification->not()); + [$sql, $parameters] = $where->normalize(Driver::mysql); $assert->same( "WHERE NOT(({$column->name()->sql(Driver::mysql)} = ? OR {$column->name()->sql(Driver::mysql)} = ?))", - $where->sql(Driver::mysql), + $sql, ); - $assert->count(2, $where->parameters()); - $assert->same($leftValue, $where->parameters()->get(0)->match( + $assert->count(2, $parameters); + $assert->same($leftValue, $parameters->get(0)->match( static fn($parameter) => $parameter->value(), static fn() => null, )); - $assert->same($rightValue, $where->parameters()->get(1)->match( + $assert->same($rightValue, $parameters->get(1)->match( static fn($parameter) => $parameter->value(), static fn() => null, )); @@ -408,16 +422,17 @@ static function($assert, $column1, $column2, $value1, $value2) { $specification = $left->and($right->not()); $where = Where::of($specification); + [$sql, $parameters] = $where->normalize(Driver::mysql); $assert->same( "WHERE ({$column1->name()->sql(Driver::mysql)} = ? AND {$column2->name()->sql(Driver::mysql)} <> ?)", - $where->sql(Driver::mysql), + $sql, ); - $assert->count(2, $where->parameters()); - $assert->same($value1, $where->parameters()->first()->match( + $assert->count(2, $parameters); + $assert->same($value1, $parameters->first()->match( static fn($parameter) => $parameter->value(), static fn() => null, )); - $assert->same($value2, $where->parameters()->last()->match( + $assert->same($value2, $parameters->last()->match( static fn($parameter) => $parameter->value(), static fn() => null, )); @@ -435,16 +450,17 @@ static function($assert, $column1, $column2, $value1, $value2) { $specification = $left->not()->and($right); $where = Where::of($specification); + [$sql, $parameters] = $where->normalize(Driver::mysql); $assert->same( "WHERE ({$column1->name()->sql(Driver::mysql)} <> ? AND {$column2->name()->sql(Driver::mysql)} = ?)", - $where->sql(Driver::mysql), + $sql, ); - $assert->count(2, $where->parameters()); - $assert->same($value1, $where->parameters()->first()->match( + $assert->count(2, $parameters); + $assert->same($value1, $parameters->first()->match( static fn($parameter) => $parameter->value(), static fn() => null, )); - $assert->same($value2, $where->parameters()->last()->match( + $assert->same($value2, $parameters->last()->match( static fn($parameter) => $parameter->value(), static fn() => null, )); @@ -473,16 +489,17 @@ static function($assert, $column1, $column2, $value1, $value2) { $specification = $left->or($right->not()); $where = Where::of($specification); + [$sql, $parameters] = $where->normalize(Driver::mysql); $assert->same( "WHERE ({$column1->name()->sql(Driver::mysql)} = ? OR {$column2->name()->sql(Driver::mysql)} <> ?)", - $where->sql(Driver::mysql), + $sql, ); - $assert->count(2, $where->parameters()); - $assert->same($value1, $where->parameters()->first()->match( + $assert->count(2, $parameters); + $assert->same($value1, $parameters->first()->match( static fn($parameter) => $parameter->value(), static fn() => null, )); - $assert->same($value2, $where->parameters()->last()->match( + $assert->same($value2, $parameters->last()->match( static fn($parameter) => $parameter->value(), static fn() => null, )); @@ -500,16 +517,17 @@ static function($assert, $column1, $column2, $value1, $value2) { $specification = $left->not()->or($right); $where = Where::of($specification); + [$sql, $parameters] = $where->normalize(Driver::mysql); $assert->same( "WHERE ({$column1->name()->sql(Driver::mysql)} <> ? OR {$column2->name()->sql(Driver::mysql)} = ?)", - $where->sql(Driver::mysql), + $sql, ); - $assert->count(2, $where->parameters()); - $assert->same($value1, $where->parameters()->first()->match( + $assert->count(2, $parameters); + $assert->same($value1, $parameters->first()->match( static fn($parameter) => $parameter->value(), static fn() => null, )); - $assert->same($value2, $where->parameters()->last()->match( + $assert->same($value2, $parameters->last()->match( static fn($parameter) => $parameter->value(), static fn() => null, )); @@ -538,16 +556,17 @@ static function($assert, $column, $unused, $value, $type) { ); $where = Where::of($specification); + [$sql, $parameters] = $where->normalize(Driver::mysql); $assert->same( "WHERE {$column->name()->sql(Driver::mysql)} = ?", - $where->sql(Driver::mysql), + $sql, ); - $assert->count(1, $where->parameters()); - $assert->same($value, $where->parameters()->first()->match( + $assert->count(1, $parameters); + $assert->same($value, $parameters->first()->match( static fn($parameter) => $parameter->value(), static fn() => null, )); - $assert->same($type, $where->parameters()->first()->match( + $assert->same($type, $parameters->first()->match( static fn($parameter) => $parameter->type(), static fn() => null, )); @@ -569,12 +588,13 @@ static function($assert, $table, $column, $value) { ); $where = Where::of($specification); + [$sql, $parameters] = $where->normalize(Driver::mysql); $assert->same( "WHERE {$table->sql(Driver::mysql)}.{$column->name()->sql(Driver::mysql)} = ?", - $where->sql(Driver::mysql), + $sql, ); - $assert->count(1, $where->parameters()); - $assert->same($value, $where->parameters()->first()->match( + $assert->count(1, $parameters); + $assert->same($value, $parameters->first()->match( static fn($parameter) => $parameter->value(), static fn() => null, )); diff --git a/src/Connection/PDO.php b/src/Connection/PDO.php index 1950da3..2869cb8 100644 --- a/src/Connection/PDO.php +++ b/src/Connection/PDO.php @@ -77,15 +77,15 @@ public function __invoke(Query|Query\Builder $query): Sequence { return match (\get_class($query)) { Query\StartTransaction::class => $this->transaction( - $query->normalize($this->driver), + $query, fn(): bool => $this->pdo->beginTransaction(), ), Query\Commit::class => $this->transaction( - $query->normalize($this->driver), + $query, fn(): bool => $this->pdo->commit(), ), Query\Rollback::class => $this->transaction( - $query->normalize($this->driver), + $query, fn(): bool => $this->pdo->rollBack(), ), default => $this->execute($query), @@ -102,9 +102,13 @@ public static function of(Url $dsn): self * * @return Sequence */ - private function transaction(Query $query, callable $action): Sequence + private function transaction(Query\Builder $query, callable $action): Sequence { - $this->attempt($query, $action); + $this->attempt( + $query, + $query->normalize($this->driver), + $action, + ); /** @var Sequence */ return Sequence::of(); @@ -116,23 +120,27 @@ private function transaction(Query $query, callable $action): Sequence private function execute(Query|Query\Builder $query): Sequence { if ($query instanceof Query\Builder) { - $query = $query->normalize($this->driver); + $normalized = $query->normalize($this->driver); + } else { + $normalized = $query; } - return match ($query->lazy()) { - true => $this->lazy($query), - false => $this->defer($query), + return match ($normalized->lazy()) { + true => $this->lazy($query, $normalized), + false => $this->defer($query, $normalized), }; } /** * @return Sequence */ - private function lazy(Query $query): Sequence - { + private function lazy( + Query|Query\Builder $query, + Query $normalized, + ): Sequence { /** @var Sequence */ - return Sequence::lazy(function() use ($query): \Generator { - $statement = $this->prepare($query); + return Sequence::lazy(function() use ($query, $normalized): \Generator { + $statement = $this->prepare($query, $normalized); /** @psalm-suppress MixedAssignment */ while ($row = $statement->fetch(\PDO::FETCH_ASSOC)) { @@ -147,9 +155,11 @@ private function lazy(Query $query): Sequence /** * @return Sequence */ - private function defer(Query $query): Sequence - { - $statement = $this->prepare($query); + private function defer( + Query|Query\Builder $query, + Query $normalized, + ): Sequence { + $statement = $this->prepare($query, $normalized); /** @var Sequence */ return Sequence::defer( @@ -168,19 +178,23 @@ private function defer(Query $query): Sequence /** * @throws QueryFailed */ - private function prepare(Query $query): \PDOStatement - { + private function prepare( + Query|Query\Builder $query, + Query $normalized, + ): \PDOStatement { $statement = $this->guard( $query, - fn() => $this->pdo->prepare($query->sql()), + $normalized, + fn() => $this->pdo->prepare($normalized->sql()), ); - $_ = $query->parameters()->reduce( + $_ = $normalized->parameters()->reduce( 0, - function(int $index, Parameter $parameter) use ($query, $statement): int { + function(int $index, Parameter $parameter) use ($query, $normalized, $statement): int { ++$index; $this->attempt( $query, + $normalized, fn(): bool => $statement->bindValue( $parameter->name()->match( static fn($name) => $name, @@ -195,7 +209,11 @@ function(int $index, Parameter $parameter) use ($query, $statement): int { }, ); - $this->attempt($query, static fn(): bool => $statement->execute()); + $this->attempt( + $query, + $normalized, + static fn(): bool => $statement->execute(), + ); return $statement; } @@ -205,8 +223,11 @@ function(int $index, Parameter $parameter) use ($query, $statement): int { * * @throws QueryFailed */ - private function guard(Query $query, callable $try): \PDOStatement - { + private function guard( + Query|Query\Builder $query, + Query $normalized, + callable $try, + ): \PDOStatement { try { $statement = $try(); @@ -227,6 +248,7 @@ private function guard(Query $query, callable $try): \PDOStatement throw new QueryFailed( $query, + $normalized, $errorInfo[0], $errorInfo[1], $errorInfo[2], @@ -239,8 +261,11 @@ private function guard(Query $query, callable $try): \PDOStatement * * @throws QueryFailed */ - private function attempt(Query $query, callable $attempt): void - { + private function attempt( + Query|Query\Builder $query, + Query $normalized, + callable $attempt, + ): void { try { if ($attempt()) { return; @@ -256,6 +281,7 @@ private function attempt(Query $query, callable $attempt): void throw new QueryFailed( $query, + $normalized, $errorInfo[0], $errorInfo[1], $errorInfo[2], diff --git a/src/Exception/QueryFailed.php b/src/Exception/QueryFailed.php index 5ba669f..cce24a0 100644 --- a/src/Exception/QueryFailed.php +++ b/src/Exception/QueryFailed.php @@ -7,13 +7,14 @@ final class QueryFailed extends RuntimeException { - private Query $query; + private Query|Query\Builder $query; private string $sqlstate; private ?int $driverSpecificCode; private ?string $driverSpecificMessage; public function __construct( - Query $query, + Query|Query\Builder $query, + Query $normalized, string $sqlstate, ?int $code, ?string $message, @@ -25,14 +26,14 @@ public function __construct( $this->driverSpecificMessage = $message; parent::__construct(\sprintf( "Query '%s' failed with: [%s] [%s] %s", - $query->sql(), + $normalized->sql(), $sqlstate, (string) $code, (string) $message, ), 0, $previous); } - public function query(): Query + public function query(): Query|Query\Builder { return $this->query; } From b210f3104f2407c864bc93284ae21ea763f73c7b Mon Sep 17 00:00:00 2001 From: Baptiste Langlade Date: Mon, 17 Nov 2025 17:24:27 +0100 Subject: [PATCH 17/24] require blackbox 6 --- CHANGELOG.md | 1 + composer.json | 6 +- fixtures/Table/Column.php | 14 +-- fixtures/Table/Column/Name.php | 9 +- fixtures/Table/Column/Type.php | 112 ++++++++++-------- fixtures/Table/Name.php | 22 ++-- proofs/connection/lazy.php | 2 +- proofs/connection/logger.php | 2 +- proofs/connection/pdo.php | 4 +- proofs/query/parameter/type.php | 6 +- proofs/query/where.php | 44 +++---- ...tTheCorrectNumberOfParametersMustThrow.php | 2 +- .../AllowToStartTwoQueriesInParallel.php | 12 +- .../AnInvalidLazyQueryMustThrow.php | 2 +- .../AnInvalidLazySelectMustThrow.php | 2 +- .../Connection/AnInvalidQueryMustThrow.php | 2 +- ...mittingAnUnstartedTransactionMustThrow.php | 2 +- ...tedAfterStartOfTransactionIsAccessible.php | 12 +- .../ContentIsAccessibleAfterCommit.php | 12 +- .../ContentIsNotAccessibleAfterRollback.php | 12 +- properties/Connection/CreateTable.php | 4 +- .../Connection/CreateTableIfNotExists.php | 4 +- .../Connection/CreateTableWithForeignKey.php | 4 +- .../Connection/CreateTableWithPrimaryKey.php | 4 +- .../CreatingSameTableTwiceMustThrow.php | 4 +- properties/Connection/Delete.php | 2 +- properties/Connection/DeleteSpecificRow.php | 8 +- properties/Connection/DeleteWithAlias.php | 2 +- properties/Connection/Insert.php | 2 +- properties/Connection/InsertSelect.php | 16 ++- .../Connection/MultipleInsertsAtOnce.php | 20 ++-- .../MustThrowWhenValueDoesntFitTheSchema.php | 12 +- .../ParameterTypesCanBeSpecified.php | 12 +- .../ParametersCanBeBoundByIndex.php | 12 +- .../Connection/ParametersCanBeBoundByName.php | 12 +- ...backingAnUnstartedTransactionMustThrow.php | 2 +- .../Connection/SelectAliasedColumns.php | 12 +- properties/Connection/SelectColumns.php | 12 +- properties/Connection/SelectCount.php | 12 +- properties/Connection/SelectEverything.php | 12 +- properties/Connection/SelectLimit.php | 14 ++- properties/Connection/SelectOffset.php | 14 ++- properties/Connection/SelectOrder.php | 14 ++- properties/Connection/SelectValues.php | 22 ++-- properties/Connection/SelectWhere.php | 12 +- properties/Connection/SelectWhereContains.php | 20 ++-- properties/Connection/SelectWhereEndsWith.php | 16 ++- properties/Connection/SelectWhereIn.php | 12 +- properties/Connection/SelectWhereInQuery.php | 22 ++-- .../Connection/SelectWhereStartsWith.php | 16 ++- properties/Connection/Update.php | 2 +- properties/Connection/UpdateSpecificRow.php | 8 +- 52 files changed, 353 insertions(+), 267 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a92d168..0544104 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ - Requires PHP `8.4` - `Formal\AccessLayer\Connection` is now a final class, all previous implementations are now flagged as internal - `Formal\AccessLayer\Query` is now a final class +- Requires `innmind/black-box:~6.5` ### Deprecated diff --git a/composer.json b/composer.json index 59f4136..c852494 100644 --- a/composer.json +++ b/composer.json @@ -35,16 +35,16 @@ }, "require-dev": { "innmind/static-analysis": "^1.2.1", - "innmind/black-box": "^5.8|^6.0.2", + "innmind/black-box": "~6.5", "innmind/coding-standard": "~2.0" }, "conflict": { - "innmind/black-box": "<5.0|~7.0" + "innmind/black-box": "<6.0|~7.0" }, "suggest": { "innmind/black-box": "For property based testing" }, "provide": { - "innmind/black-box-sets": "5.0" + "innmind/black-box-sets": "6.0" } } diff --git a/fixtures/Table/Column.php b/fixtures/Table/Column.php index ba81fbb..110ab3d 100644 --- a/fixtures/Table/Column.php +++ b/fixtures/Table/Column.php @@ -13,13 +13,11 @@ final class Column */ public static function any(?Set $type = null, ?int $max = null): Set { - return Set\Randomize::of( // randomize to prevent same name used twice - Set\Composite::immutable( - Model::of(...), - Column\Name::any($max), - $type ?? Column\Type::any(), - ), - ); + return Set::compose( + Model::of(...), + Column\Name::any($max), + $type ?? Column\Type::any(), + )->randomize(); } /** @@ -27,7 +25,7 @@ public static function any(?Set $type = null, ?int $max = null): Set */ public static function list(): Set { - return Set\Sequence::of(self::any()) + return Set::sequence(self::any()) ->between(1, 20) ->map(static function($columns) { $filtered = []; diff --git a/fixtures/Table/Column/Name.php b/fixtures/Table/Column/Name.php index 9aca454..f46223c 100644 --- a/fixtures/Table/Column/Name.php +++ b/fixtures/Table/Column/Name.php @@ -15,10 +15,11 @@ public static function any(?int $max = null): Set { $max ??= 64; - return Set\Strings::madeOf( - Set\Chars::alphanumerical(), - Set\Elements::of('é', 'è', 'ê', 'ë', '_'), - ) + return Set::strings() + ->madeOf( + Set::strings()->chars()->alphanumerical(), + Set::of('é', 'è', 'ê', 'ë', '_'), + ) ->between(1, $max) ->filter(static fn($string) => \mb_strlen($string, 'ascii') < $max) ->map(Model::of(...)); diff --git a/fixtures/Table/Column/Type.php b/fixtures/Table/Column/Type.php index 1f906e0..51d7872 100644 --- a/fixtures/Table/Column/Type.php +++ b/fixtures/Table/Column/Type.php @@ -13,7 +13,7 @@ final class Type */ public static function any(): Set { - return Set\Either::any( + return Set::either( self::bigint(), self::binary(), self::bit(), @@ -28,7 +28,7 @@ public static function any(): Set self::nullable(), self::comment(), self::nullable(self::comment()), - Set\Elements::of(Model::uuid(), Model::bool()), + Set::of(Model::uuid(), Model::bool()), ); } @@ -37,7 +37,7 @@ public static function any(): Set */ public static function constraint(): Set { - return Set\Either::any( + return Set::either( self::bigint(), self::binary(), self::bit(), @@ -48,7 +48,7 @@ public static function constraint(): Set self::smallint(), self::tinyint(), self::varchar(), - Set\Elements::of(Model::uuid(), Model::bool()), + Set::of(Model::uuid(), Model::bool()), ); } @@ -57,7 +57,9 @@ public static function constraint(): Set */ private static function bigint(): Set { - return Set\Integers::between(1, 255)->map(Model::bigint(...)); + return Set::integers() + ->between(1, 255) + ->map(Model::bigint(...)); } /** @@ -65,7 +67,9 @@ private static function bigint(): Set */ private static function binary(): Set { - return Set\Integers::between(1, 255)->map(Model::binary(...)); + return Set::integers() + ->between(1, 255) + ->map(Model::binary(...)); } /** @@ -73,7 +77,9 @@ private static function binary(): Set */ private static function bit(): Set { - return Set\Integers::between(1, 64)->map(Model::bit(...)); + return Set::integers() + ->between(1, 64) + ->map(Model::bit(...)); } /** @@ -81,7 +87,9 @@ private static function bit(): Set */ private static function char(): Set { - return Set\Integers::between(1, 255)->map(Model::char(...)); + return Set::integers() + ->between(1, 255) + ->map(Model::char(...)); } /** @@ -89,12 +97,14 @@ private static function char(): Set */ private static function decimal(): Set { - return Set\Either::any( - Set\Integers::between(1, 65)->map(Model::decimal(...)), - Set\Composite::immutable( + return Set::either( + Set::integers() + ->between(1, 65) + ->map(Model::decimal(...)), + Set::compose( static fn($precision, $scale) => [$precision, $scale], - Set\Integers::between(1, 65), - Set\Integers::between(0, 30), + Set::integers()->between(1, 65), + Set::integers()->between(0, 30), ) ->filter(static fn($precision) => $precision[1] <= $precision[0]) // scale can't be higher than the precision ->map(static fn($precision) => Model::decimal(...$precision)), @@ -106,7 +116,9 @@ private static function decimal(): Set */ private static function int(): Set { - return Set\Integers::between(1, 255)->map(Model::int(...)); + return Set::integers() + ->between(1, 255) + ->map(Model::int(...)); } /** @@ -114,7 +126,9 @@ private static function int(): Set */ private static function mediumint(): Set { - return Set\Integers::between(1, 255)->map(Model::mediumint(...)); + return Set::integers() + ->between(1, 255) + ->map(Model::mediumint(...)); } /** @@ -122,7 +136,9 @@ private static function mediumint(): Set */ private static function smallint(): Set { - return Set\Integers::between(1, 255)->map(Model::smallint(...)); + return Set::integers() + ->between(1, 255) + ->map(Model::smallint(...)); } /** @@ -130,7 +146,9 @@ private static function smallint(): Set */ private static function tinyint(): Set { - return Set\Integers::between(1, 255)->map(Model::tinyint(...)); + return Set::integers() + ->between(1, 255) + ->map(Model::tinyint(...)); } /** @@ -138,7 +156,9 @@ private static function tinyint(): Set */ private static function varchar(): Set { - return Set\Integers::between(1, 255)->map(Model::varchar(...)); + return Set::integers() + ->between(1, 255) + ->map(Model::varchar(...)); } /** @@ -146,30 +166,27 @@ private static function varchar(): Set */ private static function of(): Set { - return Set\Decorate::immutable( - static fn(string $name): Model => Model::$name(), - Set\Elements::of( - 'bigint', - 'binary', - 'bit', - 'blob', - 'char', - 'date', - 'datetime', - 'decimal', - 'double', - 'float', - 'int', - 'json', - 'longtext', - 'mediumint', - 'mediumtext', - 'smallint', - 'text', - 'tinyint', - 'varchar', - ), - ); + return Set::of( + 'bigint', + 'binary', + 'bit', + 'blob', + 'char', + 'date', + 'datetime', + 'decimal', + 'double', + 'float', + 'int', + 'json', + 'longtext', + 'mediumint', + 'mediumtext', + 'smallint', + 'text', + 'tinyint', + 'varchar', + )->map(static fn(string $name): Model => Model::$name()); } /** @@ -177,9 +194,8 @@ private static function of(): Set */ private static function nullable(?Set $set = null): Set { - return Set\Decorate::immutable( + return ($set ?? self::of())->map( static fn(Model $type): Model => $type->nullable(), - $set ?? self::of(), ); } @@ -188,10 +204,12 @@ private static function nullable(?Set $set = null): Set */ private static function comment(): Set { - return Set\Composite::immutable( + return Set::compose( static fn(Model $type, string $comment): Model => $type->comment($comment), self::of(), - Set\Strings::madeOf(Set\Chars::alphanumerical())->atLeast(1), - ); + Set::strings() + ->madeOf(Set::strings()->chars()->alphanumerical()) + ->atLeast(1), + )->toSet(); } } diff --git a/fixtures/Table/Name.php b/fixtures/Table/Name.php index 34dbd46..abe7412 100644 --- a/fixtures/Table/Name.php +++ b/fixtures/Table/Name.php @@ -13,17 +13,19 @@ final class Name */ public static function any(): Set { - return Set\Composite::immutable( + return Set::compose( static fn(string $firstChar, string $name): Model => Model::of($firstChar.$name), - Set\Either::any( // table name can't start with a number - Set\Chars::lowercaseLetter(), - Set\Chars::uppercaseLetter(), + Set::either( // table name can't start with a number + Set::strings()->chars()->lowercaseLetter(), + Set::strings()->chars()->uppercaseLetter(), ), - Set\Strings::madeOf( - Set\Chars::alphanumerical(), - Set\Elements::of('é', 'è', 'ê', 'ë', '_'), - )->between(0, 63), - ); + Set::strings() + ->madeOf( + Set::strings()->chars()->alphanumerical(), + Set::of('é', 'è', 'ê', 'ë', '_'), + ) + ->between(0, 63), + )->toSet(); } /** @@ -31,7 +33,7 @@ public static function any(): Set */ public static function pair(): Set { - return Set\Composite::immutable( + return Set::compose( static fn($a, $b) => [$a, $b], self::any(), self::any(), diff --git a/proofs/connection/lazy.php b/proofs/connection/lazy.php index ce85537..679ae85 100644 --- a/proofs/connection/lazy.php +++ b/proofs/connection/lazy.php @@ -12,7 +12,7 @@ static fn() => Connection::new(Url::of("mysql://root:root@127.0.0.1:$port/example")), ); Properties::seed($connection); - $connections = Set\Call::of(static function() use ($connection) { + $connections = Set::call(static function() use ($connection) { Properties::seed($connection); return $connection; diff --git a/proofs/connection/logger.php b/proofs/connection/logger.php index b0a7d43..f5cfbf1 100644 --- a/proofs/connection/logger.php +++ b/proofs/connection/logger.php @@ -14,7 +14,7 @@ new NullLogger, ); Properties::seed($connection); - $connections = Set\Call::of(static function() use ($connection) { + $connections = Set::call(static function() use ($connection) { Properties::seed($connection); return $connection; diff --git a/proofs/connection/pdo.php b/proofs/connection/pdo.php index d8c3c5e..2b497cf 100644 --- a/proofs/connection/pdo.php +++ b/proofs/connection/pdo.php @@ -33,7 +33,7 @@ $proofs = static function(Url $dsn, Driver $driver) { $connection = Connection::new($dsn); Properties::seed($connection); - $connections = Set\Call::of(static function() use ($connection) { + $connections = Set::call(static function() use ($connection) { Properties::seed($connection); return $connection; @@ -467,7 +467,7 @@ static function($assert) use ($connection) { yield proof( "Unique constraint({$driver->name})", - given(Set\Integers::between(0, 1_000_000)), + given(Set::integers()->between(0, 1_000_000)), static function($assert, $int) use ($connection) { $table = Table\Name::of('test_unique'); $connection(CreateTable::ifNotExists( diff --git a/proofs/query/parameter/type.php b/proofs/query/parameter/type.php index 0546baa..4978230 100644 --- a/proofs/query/parameter/type.php +++ b/proofs/query/parameter/type.php @@ -31,7 +31,7 @@ static function($assert) { yield proof( 'Type::for() int', - given(Set\Integers::any()), + given(Set::integers()), static function($assert, $int) { $assert->same( Type::int, @@ -42,7 +42,7 @@ static function($assert, $int) { yield proof( 'Type::for() string', - given(Set\Unicode::strings()), + given(Set::strings()->unicode()), static function($assert, $string) { $assert->same( Type::string, @@ -54,7 +54,7 @@ static function($assert, $string) { yield proof( 'Type::for() unsupported data', given( - Set\Elements::of( + Set::of( new \stdClass, new class {}, static fn() => null, diff --git a/proofs/query/where.php b/proofs/query/where.php index 9707f5c..bb2557d 100644 --- a/proofs/query/where.php +++ b/proofs/query/where.php @@ -34,7 +34,7 @@ static function($assert) { 'Where equal comparator', given( Column::any(), - Set\Strings::any(), + Set::strings(), ), static function($assert, $column, $value) { $specification = Property::of( @@ -61,7 +61,7 @@ static function($assert, $column, $value) { 'Where less than comparator', given( Column::any(), - Set\Strings::any(), + Set::strings(), ), static function($assert, $column, $value) { $specification = Property::of( @@ -88,7 +88,7 @@ static function($assert, $column, $value) { 'Where less than or equal comparator', given( Column::any(), - Set\Strings::any(), + Set::strings(), ), static function($assert, $column, $value) { $lessThan = Property::of( @@ -121,7 +121,7 @@ static function($assert, $column, $value) { given( Column::any(), Column::any(), - Set\Strings::any(), + Set::strings(), ), static function($assert, $column1, $column2, $value) { $lessThan = Property::of( @@ -157,7 +157,7 @@ static function($assert, $column1, $column2, $value) { 'Where more than comparator', given( Column::any(), - Set\Strings::any(), + Set::strings(), ), static function($assert, $column, $value) { $specification = Property::of( @@ -184,7 +184,7 @@ static function($assert, $column, $value) { 'Where more than or equal comparator', given( Column::any(), - Set\Strings::any(), + Set::strings(), ), static function($assert, $column, $value) { $moreThan = Property::of( @@ -217,7 +217,7 @@ static function($assert, $column, $value) { given( Column::any(), Column::any(), - Set\Strings::any(), + Set::strings(), ), static function($assert, $column1, $column2, $value) { $moreThan = Property::of( @@ -293,12 +293,12 @@ static function($assert, $column) { 'Where in comparator', given( Column::any(), - Set\Strings::any(), - Set\Strings::any(), - Set\Strings::any(), - Set\Sequence::of( - Set\Strings::any(), - Set\Integers::between(1, 5), + Set::strings(), + Set::strings(), + Set::strings(), + Set::sequence( + Set::strings(), + Set::integers()->between(1, 5), ), ), static function($assert, $column, $value1, $value2, $value3, $values) { @@ -348,8 +348,8 @@ static function($assert, $column, $value1, $value2, $value3, $values) { 'Where not', given( Column::any(), - Set\Strings::any(), - Set\Strings::any(), + Set::strings(), + Set::strings(), ), static function($assert, $column, $leftValue, $rightValue){ $specification = Property::of( @@ -405,8 +405,8 @@ static function($assert, $column, $leftValue, $rightValue){ given( Column::any(), Column::any(), - Set\Strings::any(), - Set\Strings::any(), + Set::strings(), + Set::strings(), ), static function($assert, $column1, $column2, $value1, $value2) { $left = Property::of( @@ -472,8 +472,8 @@ static function($assert, $column1, $column2, $value1, $value2) { given( Column::any(), Column::any(), - Set\Strings::any(), - Set\Strings::any(), + Set::strings(), + Set::strings(), ), static function($assert, $column1, $column2, $value1, $value2) { $left = Property::of( @@ -539,8 +539,8 @@ static function($assert, $column1, $column2, $value1, $value2) { given( Column::any(), Column::any(), - Set\Strings::any(), - Set\Elements::of( + Set::strings(), + Set::of( Type::bool, Type::null, Type::int, @@ -578,7 +578,7 @@ static function($assert, $column, $unused, $value, $type) { given( Name::any(), Column::any(), - Set\Strings::any(), + Set::strings(), ), static function($assert, $table, $column, $value) { $specification = Property::of( diff --git a/properties/Connection/AQueryWithoutTheCorrectNumberOfParametersMustThrow.php b/properties/Connection/AQueryWithoutTheCorrectNumberOfParametersMustThrow.php index 4533910..fe24f78 100644 --- a/properties/Connection/AQueryWithoutTheCorrectNumberOfParametersMustThrow.php +++ b/properties/Connection/AQueryWithoutTheCorrectNumberOfParametersMustThrow.php @@ -21,7 +21,7 @@ final class AQueryWithoutTheCorrectNumberOfParametersMustThrow implements Proper { public static function any(): Set { - return Set\Elements::of(new self); + return Set::of(new self); } public function applicableTo(object $connection): bool diff --git a/properties/Connection/AllowToStartTwoQueriesInParallel.php b/properties/Connection/AllowToStartTwoQueriesInParallel.php index 8f5f832..a415fec 100644 --- a/properties/Connection/AllowToStartTwoQueriesInParallel.php +++ b/properties/Connection/AllowToStartTwoQueriesInParallel.php @@ -34,12 +34,14 @@ private function __construct(string $uuid, string $name, int $number) public static function any(): Set { - return Set\Composite::immutable( + return Set::compose( static fn(...$args) => new self(...$args), - Set\Uuid::any(), - Set\Strings::madeOf(Set\Chars::ascii())->between(0, 125), - Set\Integers::any(), - ); + Set::uuid(), + Set::strings() + ->madeOf(Set::strings()->chars()->ascii()) + ->between(0, 125), + Set::integers(), + )->toSet(); } public function applicableTo(object $connection): bool diff --git a/properties/Connection/AnInvalidLazyQueryMustThrow.php b/properties/Connection/AnInvalidLazyQueryMustThrow.php index 48699ff..c83e4f1 100644 --- a/properties/Connection/AnInvalidLazyQueryMustThrow.php +++ b/properties/Connection/AnInvalidLazyQueryMustThrow.php @@ -21,7 +21,7 @@ final class AnInvalidLazyQueryMustThrow implements Property { public static function any(): Set { - return Set\Elements::of(new self); + return Set::of(new self); } public function applicableTo(object $connection): bool diff --git a/properties/Connection/AnInvalidLazySelectMustThrow.php b/properties/Connection/AnInvalidLazySelectMustThrow.php index 08721b7..913d529 100644 --- a/properties/Connection/AnInvalidLazySelectMustThrow.php +++ b/properties/Connection/AnInvalidLazySelectMustThrow.php @@ -22,7 +22,7 @@ final class AnInvalidLazySelectMustThrow implements Property { public static function any(): Set { - return Set\Elements::of(new self); + return Set::of(new self); } public function applicableTo(object $connection): bool diff --git a/properties/Connection/AnInvalidQueryMustThrow.php b/properties/Connection/AnInvalidQueryMustThrow.php index 3b264d1..516adfa 100644 --- a/properties/Connection/AnInvalidQueryMustThrow.php +++ b/properties/Connection/AnInvalidQueryMustThrow.php @@ -21,7 +21,7 @@ final class AnInvalidQueryMustThrow implements Property { public static function any(): Set { - return Set\Elements::of(new self); + return Set::of(new self); } public function applicableTo(object $connection): bool diff --git a/properties/Connection/CommittingAnUnstartedTransactionMustThrow.php b/properties/Connection/CommittingAnUnstartedTransactionMustThrow.php index cde0025..7f25911 100644 --- a/properties/Connection/CommittingAnUnstartedTransactionMustThrow.php +++ b/properties/Connection/CommittingAnUnstartedTransactionMustThrow.php @@ -21,7 +21,7 @@ final class CommittingAnUnstartedTransactionMustThrow implements Property { public static function any(): Set { - return Set\Elements::of(new self); + return Set::of(new self); } public function applicableTo(object $connection): bool diff --git a/properties/Connection/ContentInsertedAfterStartOfTransactionIsAccessible.php b/properties/Connection/ContentInsertedAfterStartOfTransactionIsAccessible.php index 4f55d39..fdf7e09 100644 --- a/properties/Connection/ContentInsertedAfterStartOfTransactionIsAccessible.php +++ b/properties/Connection/ContentInsertedAfterStartOfTransactionIsAccessible.php @@ -36,12 +36,14 @@ public function __construct(string $uuid, string $username, int $number) public static function any(): Set { - return Set\Composite::immutable( + return Set::compose( static fn(...$args) => new self(...$args), - Set\Uuid::any(), - Set\Strings::madeOf(Set\Chars::ascii())->between(0, 255), - Set\Integers::any(), - ); + Set::uuid(), + Set::strings() + ->madeOf(Set::strings()->chars()->ascii()) + ->between(0, 255), + Set::integers(), + )->toSet(); } public function applicableTo(object $connection): bool diff --git a/properties/Connection/ContentIsAccessibleAfterCommit.php b/properties/Connection/ContentIsAccessibleAfterCommit.php index b86e4d1..22ccfa6 100644 --- a/properties/Connection/ContentIsAccessibleAfterCommit.php +++ b/properties/Connection/ContentIsAccessibleAfterCommit.php @@ -36,12 +36,14 @@ public function __construct(string $uuid, string $username, int $number) public static function any(): Set { - return Set\Composite::immutable( + return Set::compose( static fn(...$args) => new self(...$args), - Set\Uuid::any(), - Set\Strings::madeOf(Set\Chars::ascii())->between(0, 255), - Set\Integers::any(), - ); + Set::uuid(), + Set::strings() + ->madeOf(Set::strings()->chars()->ascii()) + ->between(0, 255), + Set::integers(), + )->toSet(); } public function applicableTo(object $connection): bool diff --git a/properties/Connection/ContentIsNotAccessibleAfterRollback.php b/properties/Connection/ContentIsNotAccessibleAfterRollback.php index a936fbe..b4f68ac 100644 --- a/properties/Connection/ContentIsNotAccessibleAfterRollback.php +++ b/properties/Connection/ContentIsNotAccessibleAfterRollback.php @@ -36,12 +36,14 @@ public function __construct(string $uuid, string $username, int $number) public static function any(): Set { - return Set\Composite::immutable( + return Set::compose( static fn(...$args) => new self(...$args), - Set\Uuid::any(), - Set\Strings::madeOf(Set\Chars::ascii())->between(0, 255), - Set\Integers::any(), - ); + Set::uuid(), + Set::strings() + ->madeOf(Set::strings()->chars()->ascii()) + ->between(0, 255), + Set::integers(), + )->toSet(); } public function applicableTo(object $connection): bool diff --git a/properties/Connection/CreateTable.php b/properties/Connection/CreateTable.php index a9b7862..0761e51 100644 --- a/properties/Connection/CreateTable.php +++ b/properties/Connection/CreateTable.php @@ -33,11 +33,11 @@ public function __construct($name, array $columns) public static function any(): Set { - return Set\Composite::immutable( + return Set::compose( static fn(...$args) => new self(...$args), Name::any(), Column::list(), - ); + )->toSet(); } public function applicableTo(object $connection): bool diff --git a/properties/Connection/CreateTableIfNotExists.php b/properties/Connection/CreateTableIfNotExists.php index 756d142..709ab1a 100644 --- a/properties/Connection/CreateTableIfNotExists.php +++ b/properties/Connection/CreateTableIfNotExists.php @@ -33,11 +33,11 @@ public function __construct($name, array $columns) public static function any(): Set { - return Set\Composite::immutable( + return Set::compose( static fn(...$args) => new self(...$args), Name::any(), Column::list(), - ); + )->toSet(); } public function applicableTo(object $connection): bool diff --git a/properties/Connection/CreateTableWithForeignKey.php b/properties/Connection/CreateTableWithForeignKey.php index d4ef7ac..3834083 100644 --- a/properties/Connection/CreateTableWithForeignKey.php +++ b/properties/Connection/CreateTableWithForeignKey.php @@ -40,12 +40,12 @@ public static function any(): Set { // max length of 30 for column names as combined can't be higher than 64 // as it's the limit of the created constraint name - return Set\Composite::immutable( + return Set::compose( static fn(...$args) => new self(...$args), Name::pair(), Column::any(Column\Type::constraint(), 30), Column::any(null, 30), - ); + )->toSet(); } public function applicableTo(object $connection): bool diff --git a/properties/Connection/CreateTableWithPrimaryKey.php b/properties/Connection/CreateTableWithPrimaryKey.php index 4919b1a..00c5f83 100644 --- a/properties/Connection/CreateTableWithPrimaryKey.php +++ b/properties/Connection/CreateTableWithPrimaryKey.php @@ -38,12 +38,12 @@ public function __construct($name, $primaryKey, array $columns) public static function any(): Set { - return Set\Composite::immutable( + return Set::compose( static fn(...$args) => new self(...$args), Name::any(), Column::any(Column\Type::constraint()), Column::list(), - ); + )->toSet(); } public function applicableTo(object $connection): bool diff --git a/properties/Connection/CreatingSameTableTwiceMustThrow.php b/properties/Connection/CreatingSameTableTwiceMustThrow.php index 00ad807..1e5ead6 100644 --- a/properties/Connection/CreatingSameTableTwiceMustThrow.php +++ b/properties/Connection/CreatingSameTableTwiceMustThrow.php @@ -34,11 +34,11 @@ public function __construct($name, array $columns) public static function any(): Set { - return Set\Composite::immutable( + return Set::compose( static fn(...$args) => new self(...$args), Name::any(), Column::list(), - ); + )->toSet(); } public function applicableTo(object $connection): bool diff --git a/properties/Connection/Delete.php b/properties/Connection/Delete.php index 664b4ae..72e8316 100644 --- a/properties/Connection/Delete.php +++ b/properties/Connection/Delete.php @@ -30,7 +30,7 @@ public function __construct(string $uuid) public static function any(): Set { - return Set\Uuid::any()->map(static fn($uuid) => new self($uuid)); + return Set::uuid()->map(static fn($uuid) => new self($uuid)); } public function applicableTo(object $connection): bool diff --git a/properties/Connection/DeleteSpecificRow.php b/properties/Connection/DeleteSpecificRow.php index 48732cd..754e27b 100644 --- a/properties/Connection/DeleteSpecificRow.php +++ b/properties/Connection/DeleteSpecificRow.php @@ -38,11 +38,11 @@ public function __construct(string $uuid1, string $uuid2) public static function any(): Set { - return Set\Composite::immutable( + return Set::compose( static fn(...$args) => new self(...$args), - Set\Uuid::any(), - Set\Uuid::any(), - ); + Set::uuid(), + Set::uuid(), + )->toSet(); } public function applicableTo(object $connection): bool diff --git a/properties/Connection/DeleteWithAlias.php b/properties/Connection/DeleteWithAlias.php index 0097450..ad5214b 100644 --- a/properties/Connection/DeleteWithAlias.php +++ b/properties/Connection/DeleteWithAlias.php @@ -30,7 +30,7 @@ public function __construct(string $uuid) public static function any(): Set { - return Set\Uuid::any()->map(static fn($uuid) => new self($uuid)); + return Set::uuid()->map(static fn($uuid) => new self($uuid)); } public function applicableTo(object $connection): bool diff --git a/properties/Connection/Insert.php b/properties/Connection/Insert.php index 55a434b..0f6f58b 100644 --- a/properties/Connection/Insert.php +++ b/properties/Connection/Insert.php @@ -30,7 +30,7 @@ public function __construct(string $uuid) public static function any(): Set { - return Set\Uuid::any()->map(static fn($uuid) => new self($uuid)); + return Set::uuid()->map(static fn($uuid) => new self($uuid)); } public function applicableTo(object $connection): bool diff --git a/properties/Connection/InsertSelect.php b/properties/Connection/InsertSelect.php index 73de92f..5b42fe9 100644 --- a/properties/Connection/InsertSelect.php +++ b/properties/Connection/InsertSelect.php @@ -35,13 +35,17 @@ private function __construct( public static function any(): Set { - return Set\Composite::immutable( + return Set::compose( static fn(...$args) => new self(...$args), - Set\Uuid::any(), - Set\Strings::madeOf(Set\Chars::alphanumerical())->between(0, 100), - Set\Integers::any(), - Set\Strings::madeOf(Set\Chars::alphanumerical())->between(10, 100), // to avoid collisions - ); + Set::uuid(), + Set::strings() + ->madeOf(Set::strings()->chars()->alphanumerical()) + ->between(0, 100), + Set::integers(), + Set::strings() + ->madeOf(Set::strings()->chars()->alphanumerical()) + ->between(10, 100), // to avoid collisions + )->toSet(); } public function applicableTo(object $connection): bool diff --git a/properties/Connection/MultipleInsertsAtOnce.php b/properties/Connection/MultipleInsertsAtOnce.php index a89c9e1..84dab94 100644 --- a/properties/Connection/MultipleInsertsAtOnce.php +++ b/properties/Connection/MultipleInsertsAtOnce.php @@ -48,15 +48,19 @@ public function __construct( public static function any(): Set { - return Set\Composite::immutable( + return Set::compose( static fn(...$args) => new self(...$args), - Set\Uuid::any(), - Set\Strings::madeOf(Set\Chars::ascii())->between(0, 255), - Set\Integers::any(), - Set\Uuid::any(), - Set\Strings::madeOf(Set\Chars::ascii())->between(0, 255), - Set\Integers::any(), - ); + Set::uuid(), + Set::strings() + ->madeOf(Set::strings()->chars()->ascii()) + ->between(0, 255), + Set::integers(), + Set::uuid(), + Set::strings() + ->madeOf(Set::strings()->chars()->ascii()) + ->between(0, 255), + Set::integers(), + )->toSet(); } public function applicableTo(object $connection): bool diff --git a/properties/Connection/MustThrowWhenValueDoesntFitTheSchema.php b/properties/Connection/MustThrowWhenValueDoesntFitTheSchema.php index f46d7a2..76548ef 100644 --- a/properties/Connection/MustThrowWhenValueDoesntFitTheSchema.php +++ b/properties/Connection/MustThrowWhenValueDoesntFitTheSchema.php @@ -33,12 +33,14 @@ public function __construct(string $uuid, string $username, int $number) public static function any(): Set { - return Set\Composite::immutable( + return Set::compose( static fn(...$args) => new self(...$args), - Set\Uuid::any(), - Set\Strings::madeOf(Set\Chars::ascii())->between(0, 255), - Set\Integers::any(), - ); + Set::uuid(), + Set::strings() + ->madeOf(Set::strings()->chars()->ascii()) + ->between(0, 255), + Set::integers(), + )->toSet(); } public function applicableTo(object $connection): bool diff --git a/properties/Connection/ParameterTypesCanBeSpecified.php b/properties/Connection/ParameterTypesCanBeSpecified.php index be8e5e6..1483d47 100644 --- a/properties/Connection/ParameterTypesCanBeSpecified.php +++ b/properties/Connection/ParameterTypesCanBeSpecified.php @@ -33,12 +33,14 @@ public function __construct(string $uuid, string $username, int $number) public static function any(): Set { - return Set\Composite::immutable( + return Set::compose( static fn(...$args) => new self(...$args), - Set\Uuid::any(), - Set\Strings::madeOf(Set\Chars::ascii())->between(0, 255), - Set\Integers::any(), - ); + Set::uuid(), + Set::strings() + ->madeOf(Set::strings()->chars()->ascii()) + ->between(0, 255), + Set::integers(), + )->toSet(); } public function applicableTo(object $connection): bool diff --git a/properties/Connection/ParametersCanBeBoundByIndex.php b/properties/Connection/ParametersCanBeBoundByIndex.php index 1c011a5..9683b0f 100644 --- a/properties/Connection/ParametersCanBeBoundByIndex.php +++ b/properties/Connection/ParametersCanBeBoundByIndex.php @@ -32,12 +32,14 @@ public function __construct(string $uuid, string $username, int $number) public static function any(): Set { - return Set\Composite::immutable( + return Set::compose( static fn(...$args) => new self(...$args), - Set\Uuid::any(), - Set\Strings::madeOf(Set\Chars::ascii())->between(0, 255), - Set\Integers::any(), - ); + Set::uuid(), + Set::strings() + ->madeOf(Set::strings()->chars()->ascii()) + ->between(0, 255), + Set::integers(), + )->toSet(); } public function applicableTo(object $connection): bool diff --git a/properties/Connection/ParametersCanBeBoundByName.php b/properties/Connection/ParametersCanBeBoundByName.php index 933eaa3..b549c95 100644 --- a/properties/Connection/ParametersCanBeBoundByName.php +++ b/properties/Connection/ParametersCanBeBoundByName.php @@ -32,12 +32,14 @@ public function __construct(string $uuid, string $username, int $number) public static function any(): Set { - return Set\Composite::immutable( + return Set::compose( static fn(...$args) => new self(...$args), - Set\Uuid::any(), - Set\Strings::madeOf(Set\Chars::ascii())->between(0, 255), - Set\Integers::any(), - ); + Set::uuid(), + Set::strings() + ->madeOf(Set::strings()->chars()->ascii()) + ->between(0, 255), + Set::integers(), + )->toSet(); } public function applicableTo(object $connection): bool diff --git a/properties/Connection/RollbackingAnUnstartedTransactionMustThrow.php b/properties/Connection/RollbackingAnUnstartedTransactionMustThrow.php index 309a4d1..54c53e7 100644 --- a/properties/Connection/RollbackingAnUnstartedTransactionMustThrow.php +++ b/properties/Connection/RollbackingAnUnstartedTransactionMustThrow.php @@ -21,7 +21,7 @@ final class RollbackingAnUnstartedTransactionMustThrow implements Property { public static function any(): Set { - return Set\Elements::of(new self); + return Set::of(new self); } public function applicableTo(object $connection): bool diff --git a/properties/Connection/SelectAliasedColumns.php b/properties/Connection/SelectAliasedColumns.php index f147fbf..430dba8 100644 --- a/properties/Connection/SelectAliasedColumns.php +++ b/properties/Connection/SelectAliasedColumns.php @@ -35,12 +35,14 @@ public function __construct(string $uuid, string $username, int $number) public static function any(): Set { - return Set\Composite::immutable( + return Set::compose( static fn(...$args) => new self(...$args), - Set\Uuid::any(), - Set\Strings::madeOf(Set\Chars::ascii())->between(0, 255), - Set\Integers::any(), - ); + Set::uuid(), + Set::strings() + ->madeOf(Set::strings()->chars()->ascii()) + ->between(0, 255), + Set::integers(), + )->toSet(); } public function applicableTo(object $connection): bool diff --git a/properties/Connection/SelectColumns.php b/properties/Connection/SelectColumns.php index 05e51c4..159beb1 100644 --- a/properties/Connection/SelectColumns.php +++ b/properties/Connection/SelectColumns.php @@ -35,12 +35,14 @@ public function __construct(string $uuid, string $username, int $number) public static function any(): Set { - return Set\Composite::immutable( + return Set::compose( static fn(...$args) => new self(...$args), - Set\Uuid::any(), - Set\Strings::madeOf(Set\Chars::ascii())->between(0, 255), - Set\Integers::any(), - ); + Set::uuid(), + Set::strings() + ->madeOf(Set::strings()->chars()->ascii()) + ->between(0, 255), + Set::integers(), + )->toSet(); } public function applicableTo(object $connection): bool diff --git a/properties/Connection/SelectCount.php b/properties/Connection/SelectCount.php index b0e302b..282a7c7 100644 --- a/properties/Connection/SelectCount.php +++ b/properties/Connection/SelectCount.php @@ -37,13 +37,15 @@ public function __construct($name, string $uuid, string $username, int $number) public static function any(): Set { - return Set\Composite::immutable( + return Set::compose( static fn(...$args) => new self(...$args), FName::any(), - Set\Uuid::any(), - Set\Strings::madeOf(Set\Chars::ascii())->between(0, 255), - Set\Integers::any(), - ); + Set::uuid(), + Set::strings() + ->madeOf(Set::strings()->chars()->ascii()) + ->between(0, 255), + Set::integers(), + )->toSet(); } public function applicableTo(object $connection): bool diff --git a/properties/Connection/SelectEverything.php b/properties/Connection/SelectEverything.php index d7851c4..3273b5f 100644 --- a/properties/Connection/SelectEverything.php +++ b/properties/Connection/SelectEverything.php @@ -34,12 +34,14 @@ public function __construct(string $uuid, string $username, int $number) public static function any(): Set { - return Set\Composite::immutable( + return Set::compose( static fn(...$args) => new self(...$args), - Set\Uuid::any(), - Set\Strings::madeOf(Set\Chars::ascii())->between(0, 255), - Set\Integers::any(), - ); + Set::uuid(), + Set::strings() + ->madeOf(Set::strings()->chars()->ascii()) + ->between(0, 255), + Set::integers(), + )->toSet(); } public function applicableTo(object $connection): bool diff --git a/properties/Connection/SelectLimit.php b/properties/Connection/SelectLimit.php index 6296c91..c17b7ff 100644 --- a/properties/Connection/SelectLimit.php +++ b/properties/Connection/SelectLimit.php @@ -40,13 +40,15 @@ public function __construct( public static function any(): Set { - return Set\Composite::immutable( + return Set::compose( static fn(...$args) => new self(...$args), - Set\Uuid::any(), - Set\Strings::madeOf(Set\Chars::ascii())->between(0, 255), - Set\Integers::any(), - Set\Integers::above(1), - ); + Set::uuid(), + Set::strings() + ->madeOf(Set::strings()->chars()->ascii()) + ->between(0, 255), + Set::integers(), + Set::integers()->above(1), + )->toSet(); } public function applicableTo(object $connection): bool diff --git a/properties/Connection/SelectOffset.php b/properties/Connection/SelectOffset.php index 2cc242a..493ae42 100644 --- a/properties/Connection/SelectOffset.php +++ b/properties/Connection/SelectOffset.php @@ -40,13 +40,15 @@ public function __construct( public static function any(): Set { - return Set\Composite::immutable( + return Set::compose( static fn(...$args) => new self(...$args), - Set\Uuid::any(), - Set\Strings::madeOf(Set\Chars::ascii())->between(0, 255), - Set\Integers::any(), - Set\Integers::above(1), - ); + Set::uuid(), + Set::strings() + ->madeOf(Set::strings()->chars()->ascii()) + ->between(0, 255), + Set::integers(), + Set::integers()->above(1), + )->toSet(); } public function applicableTo(object $connection): bool diff --git a/properties/Connection/SelectOrder.php b/properties/Connection/SelectOrder.php index 52b72d7..026c29f 100644 --- a/properties/Connection/SelectOrder.php +++ b/properties/Connection/SelectOrder.php @@ -47,13 +47,15 @@ public function __construct( public static function any(): Set { - return Set\Composite::immutable( + return Set::compose( static fn(...$args) => new self(...$args), - Set\Uuid::any(), - Set\Uuid::any(), - Set\Strings::madeOf(Set\Chars::ascii())->between(0, 254), - Set\Integers::any(), - ); + Set::uuid(), + Set::uuid(), + Set::strings() + ->madeOf(Set::strings()->chars()->ascii()) + ->between(0, 254), + Set::integers(), + )->toSet(); } public function applicableTo(object $connection): bool diff --git a/properties/Connection/SelectValues.php b/properties/Connection/SelectValues.php index 19d7d54..4b6718d 100644 --- a/properties/Connection/SelectValues.php +++ b/properties/Connection/SelectValues.php @@ -38,18 +38,22 @@ private function __construct( public static function any(): Set { - return Set\Composite::immutable( + return Set::compose( static fn(...$args) => new self(...$args), - Set\Uuid::any(), - Set\Strings::madeOf(Set\Chars::ascii())->between(0, 255), - Set\Integers::any(), + Set::uuid(), + Set::strings() + ->madeOf(Set::strings()->chars()->ascii()) + ->between(0, 255), + Set::integers(), FName::any(), - Set\Either::any( - Set\Integers::any(), - Set\Strings::madeOf(Set\Chars::alphanumerical()), - Set\Elements::of(null, true, false), + Set::either( + Set::integers(), + Set::strings()->madeOf( + Set::strings()->chars()->alphanumerical(), + ), + Set::of(null, true, false), ), - ); + )->toSet(); } public function applicableTo(object $connection): bool diff --git a/properties/Connection/SelectWhere.php b/properties/Connection/SelectWhere.php index 07dde12..b9ffc81 100644 --- a/properties/Connection/SelectWhere.php +++ b/properties/Connection/SelectWhere.php @@ -38,12 +38,14 @@ public function __construct(string $uuid, string $username, int $number) public static function any(): Set { - return Set\Composite::immutable( + return Set::compose( static fn(...$args) => new self(...$args), - Set\Uuid::any(), - Set\Strings::madeOf(Set\Chars::ascii())->between(0, 255), - Set\Integers::any(), - ); + Set::uuid(), + Set::strings() + ->madeOf(Set::strings()->chars()->ascii()) + ->between(0, 255), + Set::integers(), + )->toSet(); } public function applicableTo(object $connection): bool diff --git a/properties/Connection/SelectWhereContains.php b/properties/Connection/SelectWhereContains.php index 88fa80a..1289f2a 100644 --- a/properties/Connection/SelectWhereContains.php +++ b/properties/Connection/SelectWhereContains.php @@ -47,14 +47,20 @@ public function __construct( public static function any(): Set { - return Set\Composite::immutable( + return Set::compose( static fn(...$args) => new self(...$args), - Set\Uuid::any(), - Set\Strings::madeOf(Set\Chars::ascii())->between(0, 100), - Set\Strings::madeOf(Set\Chars::ascii())->between(0, 100), - Set\Strings::madeOf(Set\Chars::ascii())->between(10, 55), // 10 to avoid collisions with possible other values - Set\Integers::any(), - ); + Set::uuid(), + Set::strings() + ->madeOf(Set::strings()->chars()->ascii()) + ->between(0, 100), + Set::strings() + ->madeOf(Set::strings()->chars()->ascii()) + ->between(0, 100), + Set::strings() + ->madeOf(Set::strings()->chars()->ascii()) + ->between(10, 55), // 10 to avoid collisions with possible other values + Set::integers(), + )->toSet(); } public function applicableTo(object $connection): bool diff --git a/properties/Connection/SelectWhereEndsWith.php b/properties/Connection/SelectWhereEndsWith.php index dd4fea5..f003df2 100644 --- a/properties/Connection/SelectWhereEndsWith.php +++ b/properties/Connection/SelectWhereEndsWith.php @@ -44,13 +44,17 @@ public function __construct( public static function any(): Set { - return Set\Composite::immutable( + return Set::compose( static fn(...$args) => new self(...$args), - Set\Uuid::any(), - Set\Strings::madeOf(Set\Chars::ascii())->between(10, 125), // 10 to avoid collisions with possible other values - Set\Strings::madeOf(Set\Chars::ascii())->between(0, 125), - Set\Integers::any(), - ); + Set::uuid(), + Set::strings() + ->madeOf(Set::strings()->chars()->ascii()) + ->between(10, 125), // 10 to avoid collisions with possible other values + Set::strings() + ->madeOf(Set::strings()->chars()->ascii()) + ->between(0, 125), + Set::integers(), + )->toSet(); } public function applicableTo(object $connection): bool diff --git a/properties/Connection/SelectWhereIn.php b/properties/Connection/SelectWhereIn.php index 1e335aa..5f8e0b6 100644 --- a/properties/Connection/SelectWhereIn.php +++ b/properties/Connection/SelectWhereIn.php @@ -38,12 +38,14 @@ public function __construct(string $uuid, string $username, int $number) public static function any(): Set { - return Set\Composite::immutable( + return Set::compose( static fn(...$args) => new self(...$args), - Set\Uuid::any(), - Set\Strings::madeOf(Set\Chars::ascii())->between(0, 255), - Set\Integers::any(), - ); + Set::uuid(), + Set::strings() + ->madeOf(Set::strings()->chars()->ascii()) + ->between(0, 255), + Set::integers(), + )->toSet(); } public function applicableTo(object $connection): bool diff --git a/properties/Connection/SelectWhereInQuery.php b/properties/Connection/SelectWhereInQuery.php index eb44dc4..f4e2ee0 100644 --- a/properties/Connection/SelectWhereInQuery.php +++ b/properties/Connection/SelectWhereInQuery.php @@ -49,17 +49,23 @@ public function __construct( public static function any(): Set { - return Set\Composite::immutable( + return Set::compose( static fn(...$args) => new self(...$args), - Set\Uuid::any(), - Set\Uuid::any(), - Set\Strings::madeOf(Set\Chars::ascii())->between(0, 255), - Set\Integers::any(), + Set::uuid(), + Set::uuid(), + Set::strings() + ->madeOf(Set::strings()->chars()->ascii()) + ->between(0, 255), + Set::integers(), Set\MutuallyExclusive::of( - Set\Strings::madeOf(Set\Chars::ascii())->between(0, 255), - Set\Strings::madeOf(Set\Chars::ascii())->between(0, 255), + Set::strings() + ->madeOf(Set::strings()->chars()->ascii()) + ->between(0, 255), + Set::strings() + ->madeOf(Set::strings()->chars()->ascii()) + ->between(0, 255), ), - ); + )->toSet(); } public function applicableTo(object $connection): bool diff --git a/properties/Connection/SelectWhereStartsWith.php b/properties/Connection/SelectWhereStartsWith.php index 34eecf7..10fdd34 100644 --- a/properties/Connection/SelectWhereStartsWith.php +++ b/properties/Connection/SelectWhereStartsWith.php @@ -44,13 +44,17 @@ public function __construct( public static function any(): Set { - return Set\Composite::immutable( + return Set::compose( static fn(...$args) => new self(...$args), - Set\Uuid::any(), - Set\Strings::madeOf(Set\Chars::ascii())->between(10, 125), // 10 to avoid collisions with possible other values - Set\Strings::madeOf(Set\Chars::ascii())->between(0, 125), - Set\Integers::any(), - ); + Set::uuid(), + Set::strings() + ->madeOf(Set::strings()->chars()->ascii()) + ->between(10, 125), // 10 to avoid collisions with possible other values + Set::strings() + ->madeOf(Set::strings()->chars()->ascii()) + ->between(0, 125), + Set::integers(), + )->toSet(); } public function applicableTo(object $connection): bool diff --git a/properties/Connection/Update.php b/properties/Connection/Update.php index 2265c00..ed384cc 100644 --- a/properties/Connection/Update.php +++ b/properties/Connection/Update.php @@ -30,7 +30,7 @@ public function __construct(string $uuid) public static function any(): Set { - return Set\Uuid::any()->map(static fn($uuid) => new self($uuid)); + return Set::uuid()->map(static fn($uuid) => new self($uuid)); } public function applicableTo(object $connection): bool diff --git a/properties/Connection/UpdateSpecificRow.php b/properties/Connection/UpdateSpecificRow.php index 3b8bcba..d057224 100644 --- a/properties/Connection/UpdateSpecificRow.php +++ b/properties/Connection/UpdateSpecificRow.php @@ -38,11 +38,11 @@ public function __construct(string $uuid1, string $uuid2) public static function any(): Set { - return Set\Composite::immutable( + return Set::compose( static fn(...$args) => new self(...$args), - Set\Uuid::any(), - Set\Uuid::any(), - ); + Set::uuid(), + Set::uuid(), + )->toSet(); } public function applicableTo(object $connection): bool From 1deee469721deda5ad0b68426c1c3e825e1f5f90 Mon Sep 17 00:00:00 2001 From: Baptiste Langlade Date: Mon, 17 Nov 2025 17:32:25 +0100 Subject: [PATCH 18/24] update dependencies --- composer.json | 4 +- proofs/connection/pdo.php | 6 +-- proofs/query/where.php | 40 +++++++++---------- .../CanDropUnknownDatabaseIfNotExists.php | 2 +- ...tedAfterStartOfTransactionIsAccessible.php | 2 +- .../ContentIsAccessibleAfterCommit.php | 2 +- .../ContentIsNotAccessibleAfterRollback.php | 2 +- properties/Connection/CreateTable.php | 2 +- .../Connection/CreateTableIfNotExists.php | 2 +- .../Connection/CreateTableWithForeignKey.php | 4 +- .../Connection/CreateTableWithPrimaryKey.php | 2 +- properties/Connection/Delete.php | 4 +- properties/Connection/DeleteSpecificRow.php | 6 +-- properties/Connection/DeleteWithAlias.php | 4 +- properties/Connection/Insert.php | 6 +-- properties/Connection/InsertSelect.php | 8 ++-- .../Connection/MultipleInsertsAtOnce.php | 6 +-- .../ParameterTypesCanBeSpecified.php | 2 +- .../ParametersCanBeBoundByIndex.php | 2 +- .../Connection/ParametersCanBeBoundByName.php | 2 +- properties/Connection/SelectValues.php | 2 +- properties/Connection/SelectWhere.php | 2 +- properties/Connection/SelectWhereContains.php | 2 +- properties/Connection/SelectWhereEndsWith.php | 2 +- properties/Connection/SelectWhereIn.php | 2 +- properties/Connection/SelectWhereInQuery.php | 2 +- .../Connection/SelectWhereStartsWith.php | 2 +- properties/Connection/Update.php | 2 +- properties/Connection/UpdateSpecificRow.php | 6 +-- 29 files changed, 65 insertions(+), 65 deletions(-) diff --git a/composer.json b/composer.json index c852494..9542587 100644 --- a/composer.json +++ b/composer.json @@ -16,8 +16,8 @@ }, "require": { "php": "~8.4", - "innmind/immutable": "~4.0|~5.0", - "innmind/url": "~4.0", + "innmind/immutable": "dev-next", + "innmind/url": "dev-next", "innmind/specification": "~4.1", "psr/log": "~3.0" }, diff --git a/proofs/connection/pdo.php b/proofs/connection/pdo.php index 2b497cf..645be71 100644 --- a/proofs/connection/pdo.php +++ b/proofs/connection/pdo.php @@ -302,7 +302,7 @@ static function($assert) use ($connection) { $connection(Delete::from($parent)); $rows = $connection(Select::from($child)); - $assert->count(0, $rows); + $assert->same(0, $rows->size()); $connection(DropTable::named($child)); $connection(DropTable::named($parent)); @@ -455,10 +455,10 @@ static function($assert) use ($connection) { ); $rows = $connection(Select::from($child)); - $assert->count(1, $rows); + $assert->same(1, $rows->size()); $rows = $connection(Select::from($parent)); - $assert->count(0, $rows); + $assert->same(0, $rows->size()); $connection(DropTable::named($parent->name())); $connection(DropTable::named($child->name())); diff --git a/proofs/query/where.php b/proofs/query/where.php index bb2557d..52e6c11 100644 --- a/proofs/query/where.php +++ b/proofs/query/where.php @@ -26,7 +26,7 @@ static function($assert) { [$sql, $parameters] = $where->normalize(Driver::mysql); $assert->object($where)->instance(Where::class); $assert->same('', $sql); - $assert->count(0, $parameters); + $assert->same(0, $parameters->size()); }, ); @@ -49,7 +49,7 @@ static function($assert, $column, $value) { "WHERE {$column->name()->sql(Driver::mysql)} = ?", $sql, ); - $assert->count(1, $parameters); + $assert->same(1, $parameters->size()); $assert->same($value, $parameters->first()->match( static fn($parameter) => $parameter->value(), static fn() => null, @@ -76,7 +76,7 @@ static function($assert, $column, $value) { "WHERE {$column->name()->sql(Driver::mysql)} < ?", $sql, ); - $assert->count(1, $parameters); + $assert->same(1, $parameters->size()); $assert->same($value, $parameters->first()->match( static fn($parameter) => $parameter->value(), static fn() => null, @@ -108,7 +108,7 @@ static function($assert, $column, $value) { "WHERE {$column->name()->sql(Driver::mysql)} <= ?", $sql, ); - $assert->count(1, $parameters); + $assert->same(1, $parameters->size()); $assert->same($value, $parameters->first()->match( static fn($parameter) => $parameter->value(), static fn() => null, @@ -141,7 +141,7 @@ static function($assert, $column1, $column2, $value) { "WHERE ({$column1->name()->sql(Driver::mysql)} < ? OR {$column2->name()->sql(Driver::mysql)} = ?)", $sql, ); - $assert->count(2, $parameters); + $assert->same(2, $parameters->size()); $assert->same($value, $parameters->first()->match( static fn($parameter) => $parameter->value(), static fn() => null, @@ -172,7 +172,7 @@ static function($assert, $column, $value) { "WHERE {$column->name()->sql(Driver::mysql)} > ?", $sql, ); - $assert->count(1, $parameters); + $assert->same(1, $parameters->size()); $assert->same($value, $parameters->first()->match( static fn($parameter) => $parameter->value(), static fn() => null, @@ -204,7 +204,7 @@ static function($assert, $column, $value) { "WHERE {$column->name()->sql(Driver::mysql)} >= ?", $sql, ); - $assert->count(1, $parameters); + $assert->same(1, $parameters->size()); $assert->same($value, $parameters->first()->match( static fn($parameter) => $parameter->value(), static fn() => null, @@ -237,7 +237,7 @@ static function($assert, $column1, $column2, $value) { "WHERE ({$column1->name()->sql(Driver::mysql)} > ? OR {$column2->name()->sql(Driver::mysql)} = ?)", $sql, ); - $assert->count(2, $parameters); + $assert->same(2, $parameters->size()); $assert->same($value, $parameters->first()->match( static fn($parameter) => $parameter->value(), static fn() => null, @@ -265,7 +265,7 @@ static function($assert, $column) { "WHERE {$column->name()->sql(Driver::mysql)} IS NULL", $sql, ); - $assert->count(0, $parameters); + $assert->same(0, $parameters->size()); }, ); @@ -285,7 +285,7 @@ static function($assert, $column) { "WHERE {$column->name()->sql(Driver::mysql)} IS NOT NULL", $sql, ); - $assert->count(0, $parameters); + $assert->same(0, $parameters->size()); }, ); @@ -314,7 +314,7 @@ static function($assert, $column, $value1, $value2, $value3, $values) { "WHERE {$column->name()->sql(Driver::mysql)} IN (?, ?, ?)", $sql, ); - $assert->count(3, $parameters); + $assert->same(3, $parameters->size()); $assert->same($value1, $parameters->get(0)->match( static fn($parameter) => $parameter->value(), static fn() => null, @@ -340,7 +340,7 @@ static function($assert, $column, $value1, $value2, $value3, $values) { \count($values), \count_chars($sql)[63], // looking for '?' placeholders ); - $assert->count(\count($values), $parameters); + $assert->same(\count($values), $parameters->size()); }, ); @@ -364,7 +364,7 @@ static function($assert, $column, $leftValue, $rightValue){ "WHERE {$column->name()->sql(Driver::mysql)} <> ?", $sql, ); - $assert->count(1, $parameters); + $assert->same(1, $parameters->size()); $assert->same($leftValue, $parameters->first()->match( static fn($parameter) => $parameter->value(), static fn() => null, @@ -388,7 +388,7 @@ static function($assert, $column, $leftValue, $rightValue){ "WHERE NOT(({$column->name()->sql(Driver::mysql)} = ? OR {$column->name()->sql(Driver::mysql)} = ?))", $sql, ); - $assert->count(2, $parameters); + $assert->same(2, $parameters->size()); $assert->same($leftValue, $parameters->get(0)->match( static fn($parameter) => $parameter->value(), static fn() => null, @@ -427,7 +427,7 @@ static function($assert, $column1, $column2, $value1, $value2) { "WHERE ({$column1->name()->sql(Driver::mysql)} = ? AND {$column2->name()->sql(Driver::mysql)} <> ?)", $sql, ); - $assert->count(2, $parameters); + $assert->same(2, $parameters->size()); $assert->same($value1, $parameters->first()->match( static fn($parameter) => $parameter->value(), static fn() => null, @@ -455,7 +455,7 @@ static function($assert, $column1, $column2, $value1, $value2) { "WHERE ({$column1->name()->sql(Driver::mysql)} <> ? AND {$column2->name()->sql(Driver::mysql)} = ?)", $sql, ); - $assert->count(2, $parameters); + $assert->same(2, $parameters->size()); $assert->same($value1, $parameters->first()->match( static fn($parameter) => $parameter->value(), static fn() => null, @@ -494,7 +494,7 @@ static function($assert, $column1, $column2, $value1, $value2) { "WHERE ({$column1->name()->sql(Driver::mysql)} = ? OR {$column2->name()->sql(Driver::mysql)} <> ?)", $sql, ); - $assert->count(2, $parameters); + $assert->same(2, $parameters->size()); $assert->same($value1, $parameters->first()->match( static fn($parameter) => $parameter->value(), static fn() => null, @@ -522,7 +522,7 @@ static function($assert, $column1, $column2, $value1, $value2) { "WHERE ({$column1->name()->sql(Driver::mysql)} <> ? OR {$column2->name()->sql(Driver::mysql)} = ?)", $sql, ); - $assert->count(2, $parameters); + $assert->same(2, $parameters->size()); $assert->same($value1, $parameters->first()->match( static fn($parameter) => $parameter->value(), static fn() => null, @@ -561,7 +561,7 @@ static function($assert, $column, $unused, $value, $type) { "WHERE {$column->name()->sql(Driver::mysql)} = ?", $sql, ); - $assert->count(1, $parameters); + $assert->same(1, $parameters->size()); $assert->same($value, $parameters->first()->match( static fn($parameter) => $parameter->value(), static fn() => null, @@ -593,7 +593,7 @@ static function($assert, $table, $column, $value) { "WHERE {$table->sql(Driver::mysql)}.{$column->name()->sql(Driver::mysql)} = ?", $sql, ); - $assert->count(1, $parameters); + $assert->same(1, $parameters->size()); $assert->same($value, $parameters->first()->match( static fn($parameter) => $parameter->value(), static fn() => null, diff --git a/properties/Connection/CanDropUnknownDatabaseIfNotExists.php b/properties/Connection/CanDropUnknownDatabaseIfNotExists.php index e8bbd47..ce916fc 100644 --- a/properties/Connection/CanDropUnknownDatabaseIfNotExists.php +++ b/properties/Connection/CanDropUnknownDatabaseIfNotExists.php @@ -40,7 +40,7 @@ public function ensureHeldBy(Assert $assert, object $connection): object { $rows = $connection(Query\DropTable::ifExists($this->name)); - $assert->count(0, $rows); + $assert->same(0, $rows->size()); return $connection; } diff --git a/properties/Connection/ContentInsertedAfterStartOfTransactionIsAccessible.php b/properties/Connection/ContentInsertedAfterStartOfTransactionIsAccessible.php index fdf7e09..f6ca6d8 100644 --- a/properties/Connection/ContentInsertedAfterStartOfTransactionIsAccessible.php +++ b/properties/Connection/ContentInsertedAfterStartOfTransactionIsAccessible.php @@ -66,7 +66,7 @@ public function ensureHeldBy(Assert $assert, object $connection): object $rows = $connection(SQL::of("SELECT * FROM test WHERE id = '{$this->uuid}'")); - $assert->count(1, $rows); + $assert->same(1, $rows->size()); $assert->same( $this->uuid, $rows diff --git a/properties/Connection/ContentIsAccessibleAfterCommit.php b/properties/Connection/ContentIsAccessibleAfterCommit.php index 22ccfa6..efcd775 100644 --- a/properties/Connection/ContentIsAccessibleAfterCommit.php +++ b/properties/Connection/ContentIsAccessibleAfterCommit.php @@ -68,7 +68,7 @@ public function ensureHeldBy(Assert $assert, object $connection): object $rows = $connection(SQL::of("SELECT * FROM test WHERE id = '{$this->uuid}'")); - $assert->count(1, $rows); + $assert->same(1, $rows->size()); $assert->same( $this->uuid, $rows diff --git a/properties/Connection/ContentIsNotAccessibleAfterRollback.php b/properties/Connection/ContentIsNotAccessibleAfterRollback.php index b4f68ac..3032c84 100644 --- a/properties/Connection/ContentIsNotAccessibleAfterRollback.php +++ b/properties/Connection/ContentIsNotAccessibleAfterRollback.php @@ -68,7 +68,7 @@ public function ensureHeldBy(Assert $assert, object $connection): object $rows = $connection(SQL::of("SELECT * FROM test WHERE id = '{$this->uuid}'")); - $assert->count(0, $rows); + $assert->same(0, $rows->size()); return $connection; } diff --git a/properties/Connection/CreateTable.php b/properties/Connection/CreateTable.php index 0761e51..c4be730 100644 --- a/properties/Connection/CreateTable.php +++ b/properties/Connection/CreateTable.php @@ -50,7 +50,7 @@ public function ensureHeldBy(Assert $assert, object $connection): object try { $rows = $connection(Query\CreateTable::named($this->name, ...$this->columns)); - $assert->count(0, $rows); + $assert->same(0, $rows->size()); } finally { $connection(Query\DropTable::ifExists($this->name)); } diff --git a/properties/Connection/CreateTableIfNotExists.php b/properties/Connection/CreateTableIfNotExists.php index 709ab1a..702b1a2 100644 --- a/properties/Connection/CreateTableIfNotExists.php +++ b/properties/Connection/CreateTableIfNotExists.php @@ -51,7 +51,7 @@ public function ensureHeldBy(Assert $assert, object $connection): object $connection(Query\CreateTable::named($this->name, ...$this->columns)); $rows = $connection(Query\CreateTable::ifNotExists($this->name, ...$this->columns)); - $assert->count(0, $rows); + $assert->same(0, $rows->size()); } finally { $connection(Query\DropTable::ifExists($this->name)); } diff --git a/properties/Connection/CreateTableWithForeignKey.php b/properties/Connection/CreateTableWithForeignKey.php index 3834083..a6dcf5a 100644 --- a/properties/Connection/CreateTableWithForeignKey.php +++ b/properties/Connection/CreateTableWithForeignKey.php @@ -60,7 +60,7 @@ public function ensureHeldBy(Assert $assert, object $connection): object $create = $create->primaryKey($this->primaryKey->name()); $rows = $connection($create); - $assert->count(0, $rows); + $assert->same(0, $rows->size()); $create = Query\CreateTable::named($this->name2, ConcreteColumn::of( $this->foreignKey->name(), @@ -73,7 +73,7 @@ public function ensureHeldBy(Assert $assert, object $connection): object ); $rows = $connection($create); - $assert->count(0, $rows); + $assert->same(0, $rows->size()); } finally { $connection(Query\DropTable::ifExists($this->name2)); $connection(Query\DropTable::ifExists($this->name1)); diff --git a/properties/Connection/CreateTableWithPrimaryKey.php b/properties/Connection/CreateTableWithPrimaryKey.php index 00c5f83..4a34e3c 100644 --- a/properties/Connection/CreateTableWithPrimaryKey.php +++ b/properties/Connection/CreateTableWithPrimaryKey.php @@ -58,7 +58,7 @@ public function ensureHeldBy(Assert $assert, object $connection): object $create = $create->primaryKey($this->primaryKey->name()); $rows = $connection($create); - $assert->count(0, $rows); + $assert->same(0, $rows->size()); } finally { $connection(Query\DropTable::ifExists($this->name)); } diff --git a/properties/Connection/Delete.php b/properties/Connection/Delete.php index 72e8316..3aa09fa 100644 --- a/properties/Connection/Delete.php +++ b/properties/Connection/Delete.php @@ -52,11 +52,11 @@ public function ensureHeldBy(Assert $assert, object $connection): object $sequence = $connection(Query\Delete::from(Table\Name::of('test'))); - $assert->count(0, $sequence); + $assert->same(0, $sequence->size()); $rows = $connection($select); - $assert->count(0, $rows); + $assert->same(0, $rows->size()); return $connection; } diff --git a/properties/Connection/DeleteSpecificRow.php b/properties/Connection/DeleteSpecificRow.php index 754e27b..ef89ffa 100644 --- a/properties/Connection/DeleteSpecificRow.php +++ b/properties/Connection/DeleteSpecificRow.php @@ -80,15 +80,15 @@ public function ensureHeldBy(Assert $assert, object $connection): object ); $sequence = $connection($delete); - $assert->count(0, $sequence); + $assert->same(0, $sequence->size()); $rows = $connection(SQL::of("SELECT * FROM test WHERE id = '{$this->uuid1}'")); - $assert->count(0, $rows); + $assert->same(0, $rows->size()); $rows = $connection(SQL::of("SELECT * FROM test WHERE id = '{$this->uuid2}'")); - $assert->count(1, $rows); + $assert->same(1, $rows->size()); return $connection; } diff --git a/properties/Connection/DeleteWithAlias.php b/properties/Connection/DeleteWithAlias.php index ad5214b..69bbfdc 100644 --- a/properties/Connection/DeleteWithAlias.php +++ b/properties/Connection/DeleteWithAlias.php @@ -54,11 +54,11 @@ public function ensureHeldBy(Assert $assert, object $connection): object Table\Name::of('test')->as('alias'), )); - $assert->count(0, $sequence); + $assert->same(0, $sequence->size()); $rows = $connection($select); - $assert->count(0, $rows); + $assert->same(0, $rows->size()); return $connection; } diff --git a/properties/Connection/Insert.php b/properties/Connection/Insert.php index 0f6f58b..ad54efa 100644 --- a/properties/Connection/Insert.php +++ b/properties/Connection/Insert.php @@ -43,7 +43,7 @@ public function ensureHeldBy(Assert $assert, object $connection): object $select = SQL::of("SELECT * FROM test WHERE id = '{$this->uuid}'"); $rows = $connection($select); - $assert->count(0, $rows); + $assert->same(0, $rows->size()); $sequence = $connection(Query\Insert::into( Table\Name::of('test'), @@ -54,11 +54,11 @@ public function ensureHeldBy(Assert $assert, object $connection): object ]), )); - $assert->count(0, $sequence); + $assert->same(0, $sequence->size()); $rows = $connection($select); - $assert->count(1, $rows); + $assert->same(1, $rows->size()); $assert->same( $this->uuid, $rows diff --git a/properties/Connection/InsertSelect.php b/properties/Connection/InsertSelect.php index 5b42fe9..004724f 100644 --- a/properties/Connection/InsertSelect.php +++ b/properties/Connection/InsertSelect.php @@ -58,7 +58,7 @@ public function ensureHeldBy(Assert $assert, object $connection): object $select = SQL::of("SELECT * FROM test_values WHERE id = '{$this->uuid}'"); $rows = $connection($select); - $assert->count(0, $rows); + $assert->same(0, $rows->size()); $sequence = $connection(Query\Insert::into( Table\Name::of('test'), @@ -69,7 +69,7 @@ public function ensureHeldBy(Assert $assert, object $connection): object ]), )); - $assert->count(0, $sequence); + $assert->same(0, $sequence->size()); $sequence = $connection(Query\Insert::into( Table\Name::of('test_values'), @@ -88,11 +88,11 @@ public function ensureHeldBy(Assert $assert, object $connection): object )), )); - $assert->count(0, $sequence); + $assert->same(0, $sequence->size()); $rows = $connection($select); - $assert->count(1, $rows); + $assert->same(1, $rows->size()); $assert->same( $this->uuid, $rows diff --git a/properties/Connection/MultipleInsertsAtOnce.php b/properties/Connection/MultipleInsertsAtOnce.php index 84dab94..0c77929 100644 --- a/properties/Connection/MultipleInsertsAtOnce.php +++ b/properties/Connection/MultipleInsertsAtOnce.php @@ -73,7 +73,7 @@ public function ensureHeldBy(Assert $assert, object $connection): object $select = SQL::of("SELECT * FROM test WHERE id IN ('{$this->uuid1}', '{$this->uuid2}')"); $rows = $connection($select); - $assert->count(0, $rows); + $assert->same(0, $rows->size()); $insert = Query\MultipleInsert::into( Table\Name::of('test'), @@ -94,11 +94,11 @@ public function ensureHeldBy(Assert $assert, object $connection): object ]), ))); - $assert->count(0, $sequence); + $assert->same(0, $sequence->size()); $rows = $connection($select); - $assert->count(2, $rows); + $assert->same(2, $rows->size()); $assert ->expected( $rows diff --git a/properties/Connection/ParameterTypesCanBeSpecified.php b/properties/Connection/ParameterTypesCanBeSpecified.php index 1483d47..36d8833 100644 --- a/properties/Connection/ParameterTypesCanBeSpecified.php +++ b/properties/Connection/ParameterTypesCanBeSpecified.php @@ -58,7 +58,7 @@ public function ensureHeldBy(Assert $assert, object $connection): object $rows = $connection(SQL::of("SELECT * FROM test WHERE id = '{$this->uuid}'")); - $assert->count(1, $rows); + $assert->same(1, $rows->size()); $assert->same( $this->uuid, $rows diff --git a/properties/Connection/ParametersCanBeBoundByIndex.php b/properties/Connection/ParametersCanBeBoundByIndex.php index 9683b0f..048ad5b 100644 --- a/properties/Connection/ParametersCanBeBoundByIndex.php +++ b/properties/Connection/ParametersCanBeBoundByIndex.php @@ -57,7 +57,7 @@ public function ensureHeldBy(Assert $assert, object $connection): object $rows = $connection(SQL::of("SELECT * FROM test WHERE id = '{$this->uuid}'")); - $assert->count(1, $rows); + $assert->same(1, $rows->size()); $assert->same( $this->uuid, $rows diff --git a/properties/Connection/ParametersCanBeBoundByName.php b/properties/Connection/ParametersCanBeBoundByName.php index b549c95..688e53d 100644 --- a/properties/Connection/ParametersCanBeBoundByName.php +++ b/properties/Connection/ParametersCanBeBoundByName.php @@ -57,7 +57,7 @@ public function ensureHeldBy(Assert $assert, object $connection): object $rows = $connection(SQL::of("SELECT * FROM test WHERE id = '{$this->uuid}'")); - $assert->count(1, $rows); + $assert->same(1, $rows->size()); $assert->same( $this->uuid, $rows diff --git a/properties/Connection/SelectValues.php b/properties/Connection/SelectValues.php index 4b6718d..3d79902 100644 --- a/properties/Connection/SelectValues.php +++ b/properties/Connection/SelectValues.php @@ -89,7 +89,7 @@ public function ensureHeldBy(Assert $assert, object $connection): object )); $rows = $connection($select); - $assert->count(1, $rows); + $assert->same(1, $rows->size()); $assert->same( $this->uuid, $rows diff --git a/properties/Connection/SelectWhere.php b/properties/Connection/SelectWhere.php index b9ffc81..663d2b9 100644 --- a/properties/Connection/SelectWhere.php +++ b/properties/Connection/SelectWhere.php @@ -72,7 +72,7 @@ public function ensureHeldBy(Assert $assert, object $connection): object )); $rows = $connection($select); - $assert->count(1, $rows); + $assert->same(1, $rows->size()); $assert->same( $this->uuid, $rows diff --git a/properties/Connection/SelectWhereContains.php b/properties/Connection/SelectWhereContains.php index 1289f2a..ce6f0ad 100644 --- a/properties/Connection/SelectWhereContains.php +++ b/properties/Connection/SelectWhereContains.php @@ -87,7 +87,7 @@ public function ensureHeldBy(Assert $assert, object $connection): object )); $rows = $connection($select); - $assert->count(1, $rows); + $assert->same(1, $rows->size()); $assert->same( $this->uuid, $rows diff --git a/properties/Connection/SelectWhereEndsWith.php b/properties/Connection/SelectWhereEndsWith.php index f003df2..3eff0f6 100644 --- a/properties/Connection/SelectWhereEndsWith.php +++ b/properties/Connection/SelectWhereEndsWith.php @@ -81,7 +81,7 @@ public function ensureHeldBy(Assert $assert, object $connection): object )); $rows = $connection($select); - $assert->count(1, $rows); + $assert->same(1, $rows->size()); $assert->same( $this->uuid, $rows diff --git a/properties/Connection/SelectWhereIn.php b/properties/Connection/SelectWhereIn.php index 5f8e0b6..c5d9cb8 100644 --- a/properties/Connection/SelectWhereIn.php +++ b/properties/Connection/SelectWhereIn.php @@ -72,7 +72,7 @@ public function ensureHeldBy(Assert $assert, object $connection): object )); $rows = $connection($select); - $assert->count(1, $rows); + $assert->same(1, $rows->size()); $assert->same( $this->uuid, $rows diff --git a/properties/Connection/SelectWhereInQuery.php b/properties/Connection/SelectWhereInQuery.php index f4e2ee0..1b76e5c 100644 --- a/properties/Connection/SelectWhereInQuery.php +++ b/properties/Connection/SelectWhereInQuery.php @@ -106,7 +106,7 @@ public function ensureHeldBy(Assert $assert, object $connection): object )); $rows = $connection($select); - $assert->count(1, $rows); + $assert->same(1, $rows->size()); $assert->same( $this->uuid1, $rows diff --git a/properties/Connection/SelectWhereStartsWith.php b/properties/Connection/SelectWhereStartsWith.php index 10fdd34..64d4724 100644 --- a/properties/Connection/SelectWhereStartsWith.php +++ b/properties/Connection/SelectWhereStartsWith.php @@ -81,7 +81,7 @@ public function ensureHeldBy(Assert $assert, object $connection): object )); $rows = $connection($select); - $assert->count(1, $rows); + $assert->same(1, $rows->size()); $assert->same( $this->uuid, $rows diff --git a/properties/Connection/Update.php b/properties/Connection/Update.php index ed384cc..e4da382 100644 --- a/properties/Connection/Update.php +++ b/properties/Connection/Update.php @@ -55,7 +55,7 @@ public function ensureHeldBy(Assert $assert, object $connection): object Row::of(['registerNumber' => 24]), )); - $assert->count(0, $sequence); + $assert->same(0, $sequence->size()); $rows = $connection($select); diff --git a/properties/Connection/UpdateSpecificRow.php b/properties/Connection/UpdateSpecificRow.php index d057224..00f1c87 100644 --- a/properties/Connection/UpdateSpecificRow.php +++ b/properties/Connection/UpdateSpecificRow.php @@ -82,11 +82,11 @@ public function ensureHeldBy(Assert $assert, object $connection): object )); $sequence = $connection($update); - $assert->count(0, $sequence); + $assert->same(0, $sequence->size()); $rows = $connection(SQL::of("SELECT * FROM test WHERE id = '{$this->uuid1}'")); - $assert->count(1, $rows); + $assert->same(1, $rows->size()); $assert->same( 24, $rows @@ -100,7 +100,7 @@ public function ensureHeldBy(Assert $assert, object $connection): object $rows = $connection(SQL::of("SELECT * FROM test WHERE id = '{$this->uuid2}'")); - $assert->count(1, $rows); + $assert->same(1, $rows->size()); $assert->same( 42, $rows From 8295c74ac458b1ce8f1fafc37e4cc48356adb163 Mon Sep 17 00:00:00 2001 From: Baptiste Langlade Date: Tue, 18 Nov 2025 11:15:34 +0100 Subject: [PATCH 19/24] capture connection failures in an attempt --- proofs/connection/lazy.php | 4 ++-- proofs/connection/logger.php | 2 +- proofs/connection/pdo.php | 6 +++--- src/Connection.php | 14 +++++++++++--- src/Connection/PDO.php | 12 +++++++++--- 5 files changed, 26 insertions(+), 12 deletions(-) diff --git a/proofs/connection/lazy.php b/proofs/connection/lazy.php index 679ae85..47f35c7 100644 --- a/proofs/connection/lazy.php +++ b/proofs/connection/lazy.php @@ -9,7 +9,7 @@ return static function() { $port = \getenv('DB_PORT') ?: '3306'; $connection = Connection::lazy( - static fn() => Connection::new(Url::of("mysql://root:root@127.0.0.1:$port/example")), + static fn() => Connection::new(Url::of("mysql://root:root@127.0.0.1:$port/example"))->unwrap(), ); Properties::seed($connection); $connections = Set::call(static function() use ($connection) { @@ -28,7 +28,7 @@ yield test( 'Lazy connection must not be established at instanciation', static fn($assert) => $assert - ->object(Connection::lazy(static fn() => Connection::new(Url::of('mysql://unknown:unknown@127.0.0.1:3306/unknown')))) + ->object(Connection::lazy(static fn() => Connection::new(Url::of('mysql://unknown:unknown@127.0.0.1:3306/unknown'))->unwrap())) ->instance(Connection::class), ); diff --git a/proofs/connection/logger.php b/proofs/connection/logger.php index f5cfbf1..5fcb12d 100644 --- a/proofs/connection/logger.php +++ b/proofs/connection/logger.php @@ -10,7 +10,7 @@ return static function() { $port = \getenv('DB_PORT') ?: '3306'; $connection = Connection::logger( - Connection::new(Url::of("mysql://root:root@127.0.0.1:$port/example")), + Connection::new(Url::of("mysql://root:root@127.0.0.1:$port/example"))->unwrap(), new NullLogger, ); Properties::seed($connection); diff --git a/proofs/connection/pdo.php b/proofs/connection/pdo.php index 645be71..673e2fb 100644 --- a/proofs/connection/pdo.php +++ b/proofs/connection/pdo.php @@ -31,7 +31,7 @@ use Innmind\BlackBox\Set; $proofs = static function(Url $dsn, Driver $driver) { - $connection = Connection::new($dsn); + $connection = Connection::new($dsn)->unwrap(); Properties::seed($connection); $connections = Set::call(static function() use ($connection) { Properties::seed($connection); @@ -113,7 +113,7 @@ static function($assert) use ($connection, $dsn) { $select = Select::from($table); - $ascii = Connection::new($dsn->withQuery(Query::of('charset=ascii'))); + $ascii = Connection::new($dsn->withQuery(Query::of('charset=ascii')))->unwrap(); $assert ->expected('gelé') ->not() @@ -127,7 +127,7 @@ static function($assert) use ($connection, $dsn) { ), ); - $utf8 = Connection::new($dsn->withQuery(Query::of('charset=utf8mb4'))); + $utf8 = Connection::new($dsn->withQuery(Query::of('charset=utf8mb4')))->unwrap(); $assert->same( 'gelé', $utf8($select) diff --git a/src/Connection.php b/src/Connection.php index ce98580..5557f35 100644 --- a/src/Connection.php +++ b/src/Connection.php @@ -11,7 +11,10 @@ Exception\QueryFailed, }; use Innmind\Url\Url; -use Innmind\Immutable\Sequence; +use Innmind\Immutable\{ + Sequence, + Attempt, +}; use Psr\Log\LoggerInterface; final class Connection @@ -31,9 +34,14 @@ public function __invoke(Query|Query\Builder $query): Sequence return ($this->implementation)($query); } - public static function new(Url $dsn): self + /** + * @return Attempt + */ + public static function new(Url $dsn): Attempt { - return new self(PDO::of($dsn)); + return PDO::of($dsn)->map( + static fn($implementation) => new self($implementation), + ); } /** diff --git a/src/Connection/PDO.php b/src/Connection/PDO.php index 2869cb8..941fc1b 100644 --- a/src/Connection/PDO.php +++ b/src/Connection/PDO.php @@ -17,7 +17,10 @@ Authority\UserInformation\User, Authority\UserInformation\Password, }; -use Innmind\Immutable\Sequence; +use Innmind\Immutable\{ + Sequence, + Attempt, +}; /** * @internal @@ -92,9 +95,12 @@ public function __invoke(Query|Query\Builder $query): Sequence }; } - public static function of(Url $dsn): self + /** + * @return Attempt + */ + public static function of(Url $dsn): Attempt { - return new self($dsn); + return Attempt::of(static fn() => new self($dsn)); } /** From 7cc0a17fa0f5297a03aa6fa1d2275ce804189ead Mon Sep 17 00:00:00 2001 From: Baptiste Langlade Date: Tue, 18 Nov 2025 11:26:55 +0100 Subject: [PATCH 20/24] defer the loading of pdo connections --- src/Connection/PDO.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Connection/PDO.php b/src/Connection/PDO.php index 941fc1b..7ed6179 100644 --- a/src/Connection/PDO.php +++ b/src/Connection/PDO.php @@ -100,7 +100,9 @@ public function __invoke(Query|Query\Builder $query): Sequence */ public static function of(Url $dsn): Attempt { - return Attempt::of(static fn() => new self($dsn)); + return Attempt::defer( + static fn() => Attempt::of(static fn() => new self($dsn)), + ); } /** From 4a97c0647a75b8d4e843d74ff379fac4b1b3321e Mon Sep 17 00:00:00 2001 From: Baptiste Langlade Date: Tue, 18 Nov 2025 11:30:47 +0100 Subject: [PATCH 21/24] remove no longer needed lazy connections --- proofs/connection/lazy.php | 7 ++---- src/Connection.php | 11 ---------- src/Connection/Lazy.php | 44 -------------------------------------- 3 files changed, 2 insertions(+), 60 deletions(-) delete mode 100644 src/Connection/Lazy.php diff --git a/proofs/connection/lazy.php b/proofs/connection/lazy.php index 47f35c7..de2dbe4 100644 --- a/proofs/connection/lazy.php +++ b/proofs/connection/lazy.php @@ -8,9 +8,7 @@ return static function() { $port = \getenv('DB_PORT') ?: '3306'; - $connection = Connection::lazy( - static fn() => Connection::new(Url::of("mysql://root:root@127.0.0.1:$port/example"))->unwrap(), - ); + $connection = Connection::new(Url::of("mysql://root:root@127.0.0.1:$port/example"))->unwrap(); Properties::seed($connection); $connections = Set::call(static function() use ($connection) { Properties::seed($connection); @@ -28,8 +26,7 @@ yield test( 'Lazy connection must not be established at instanciation', static fn($assert) => $assert - ->object(Connection::lazy(static fn() => Connection::new(Url::of('mysql://unknown:unknown@127.0.0.1:3306/unknown'))->unwrap())) - ->instance(Connection::class), + ->object(Connection::new(Url::of('mysql://unknown:unknown@127.0.0.1:3306/unknown'))), ); yield properties( diff --git a/src/Connection.php b/src/Connection.php index 5557f35..d777920 100644 --- a/src/Connection.php +++ b/src/Connection.php @@ -6,7 +6,6 @@ use Formal\AccessLayer\{ Connection\Implementation, Connection\PDO, - Connection\Lazy, Connection\Logger, Exception\QueryFailed, }; @@ -44,16 +43,6 @@ public static function new(Url $dsn): Attempt ); } - /** - * @param callable(): Connection $load - */ - public static function lazy(callable $load): self - { - return new self(Lazy::of( - static fn() => $load()->implementation, - )); - } - public static function logger(self $connection, LoggerInterface $logger): self { return new self(Logger::psr( diff --git a/src/Connection/Lazy.php b/src/Connection/Lazy.php deleted file mode 100644 index 0a19716..0000000 --- a/src/Connection/Lazy.php +++ /dev/null @@ -1,44 +0,0 @@ -load = $load; - } - - #[\Override] - public function __invoke(Query|Query\Builder $query): Sequence - { - return ($this->connection())($query); - } - - /** - * @param callable(): Implementation $load - */ - public static function of(callable $load): self - { - return new self($load); - } - - private function connection(): Implementation - { - return $this->connection ?? $this->connection = ($this->load)(); - } -} From 431f22f3d43675681e49ee2e8eeabf4ea338f922 Mon Sep 17 00:00:00 2001 From: Baptiste Langlade Date: Tue, 18 Nov 2025 11:33:51 +0100 Subject: [PATCH 22/24] fix logging queries with the incorrect driver --- src/Connection/Implementation.php | 2 ++ src/Connection/Logger.php | 11 ++++++----- src/Connection/PDO.php | 5 +++++ 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/Connection/Implementation.php b/src/Connection/Implementation.php index 79de1b9..d8bf06e 100644 --- a/src/Connection/Implementation.php +++ b/src/Connection/Implementation.php @@ -6,6 +6,7 @@ use Formal\AccessLayer\{ Query, Row, + Driver, }; use Innmind\Immutable\Sequence; @@ -18,4 +19,5 @@ interface Implementation * @return Sequence */ public function __invoke(Query|Query\Builder $query): Sequence; + public function driver(): Driver; } diff --git a/src/Connection/Logger.php b/src/Connection/Logger.php index 5d3d3d0..271c093 100644 --- a/src/Connection/Logger.php +++ b/src/Connection/Logger.php @@ -28,12 +28,8 @@ private function __construct(Implementation $connection, LoggerInterface $logger #[\Override] public function __invoke(Query|Query\Builder $query): Sequence { - // For the sake of simplicity the queries SQL is logged with the MySQL - // format. As otherwise it would require this decorator to retrieve the - // driver from the underlying connection. - if ($query instanceof Query\Builder) { - $normalized = $query->normalize(Driver::mysql); + $normalized = $query->normalize($this->driver()); } else { $normalized = $query; } @@ -75,4 +71,9 @@ public static function psr(Implementation $connection, LoggerInterface $logger): { return new self($connection, $logger); } + + public function driver(): Driver + { + return $this->connection->driver(); + } } diff --git a/src/Connection/PDO.php b/src/Connection/PDO.php index 7ed6179..2b5ba4e 100644 --- a/src/Connection/PDO.php +++ b/src/Connection/PDO.php @@ -105,6 +105,11 @@ public static function of(Url $dsn): Attempt ); } + public function driver(): Driver + { + return $this->driver; + } + /** * @param callable(): bool $action * From 5bd2a9c1614d26ab15d567195f630a83c1a83ef9 Mon Sep 17 00:00:00 2001 From: Baptiste Langlade Date: Tue, 18 Nov 2025 11:33:58 +0100 Subject: [PATCH 23/24] CS --- src/Connection/Logger.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Connection/Logger.php b/src/Connection/Logger.php index 271c093..c9d69ef 100644 --- a/src/Connection/Logger.php +++ b/src/Connection/Logger.php @@ -4,7 +4,6 @@ namespace Formal\AccessLayer\Connection; use Formal\AccessLayer\{ - Connection, Query, Driver, }; From 579884ab7a0fb9b60bfa3247bc945278945f7e13 Mon Sep 17 00:00:00 2001 From: Baptiste Langlade Date: Tue, 18 Nov 2025 11:34:29 +0100 Subject: [PATCH 24/24] add missing attributes --- src/Connection/Logger.php | 1 + src/Connection/PDO.php | 1 + 2 files changed, 2 insertions(+) diff --git a/src/Connection/Logger.php b/src/Connection/Logger.php index c9d69ef..459a7e4 100644 --- a/src/Connection/Logger.php +++ b/src/Connection/Logger.php @@ -71,6 +71,7 @@ public static function psr(Implementation $connection, LoggerInterface $logger): return new self($connection, $logger); } + #[\Override] public function driver(): Driver { return $this->connection->driver(); diff --git a/src/Connection/PDO.php b/src/Connection/PDO.php index 2b5ba4e..ff1ad2b 100644 --- a/src/Connection/PDO.php +++ b/src/Connection/PDO.php @@ -105,6 +105,7 @@ public static function of(Url $dsn): Attempt ); } + #[\Override] public function driver(): Driver { return $this->driver;