From 13f078d0055737d6eb9b1e49e2a1e03ee77163a9 Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Tue, 11 Jun 2024 10:02:24 +0900 Subject: [PATCH 01/34] Remove singleton scope from certain classes The previous implementation made use of the singleton scope for the RowInterface, RowListInterface, and InvokeInterface. This change removes this scope, allowing new instances of these classes to be created each time they are needed. This may improve flexibility in certain use cases and can prevent potential issues related to state persistence across multiple uses. --- src/SqlQueryProviderModule.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/SqlQueryProviderModule.php b/src/SqlQueryProviderModule.php index 7b132fe..0cacd9e 100644 --- a/src/SqlQueryProviderModule.php +++ b/src/SqlQueryProviderModule.php @@ -23,8 +23,8 @@ protected function configure() { $this->bind(SqlFinder::class)->in(Scope::SINGLETON); $this->bind(ParamReaderInterface::class)->to(ParamReader::class)->in(Scope::SINGLETON); - $this->bind(RowInterface::class)->toProvider(RowInterfaceProvider::class)->in(Scope::SINGLETON); - $this->bind(RowListInterface::class)->toProvider(RowListInterfaceProvider::class)->in(Scope::SINGLETON); - $this->bind(InvokeInterface::class)->toProvider(RowListInterfaceProvider::class)->in(Scope::SINGLETON); + $this->bind(RowInterface::class)->toProvider(RowInterfaceProvider::class); + $this->bind(RowListInterface::class)->toProvider(RowListInterfaceProvider::class); + $this->bind(InvokeInterface::class)->toProvider(RowListInterfaceProvider::class); } } From 10781c66e914cf4ae510b8b73c52c80a2d42c24e Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Tue, 11 Jun 2024 11:43:55 +0900 Subject: [PATCH 02/34] Enhance exception handling in SQL file search process Added code to handle SqlFileNotFoundException in SqlFinder, RowInterfaceProvider and RowListInterfaceProvider classes. When the SQL file is not found, the code now attempts to get an instance with the specified SQL query instead of immediately throwing an exception. --- src/Exception/SqlFileNotFoundException.php | 9 +++++++ src/RowInterfaceProvider.php | 23 ++++++++++++++++-- src/RowListInterfaceProvider.php | 28 +++++++++++++++++++--- src/SqlFinder.php | 2 +- src/SqlQueryProviderModule.php | 1 + 5 files changed, 57 insertions(+), 6 deletions(-) diff --git a/src/Exception/SqlFileNotFoundException.php b/src/Exception/SqlFileNotFoundException.php index 57dbac7..46c422f 100644 --- a/src/Exception/SqlFileNotFoundException.php +++ b/src/Exception/SqlFileNotFoundException.php @@ -8,4 +8,13 @@ class SqlFileNotFoundException extends LogicException { + /** @var string */ + public $sql; + + public function __construct(string $message, string $sql) + { + $this->sql = $sql; + + parent::__construct($message); + } } diff --git a/src/RowInterfaceProvider.php b/src/RowInterfaceProvider.php index e707e62..11f8ec3 100644 --- a/src/RowInterfaceProvider.php +++ b/src/RowInterfaceProvider.php @@ -5,8 +5,12 @@ namespace Ray\Query; use Aura\Sql\ExtendedPdoInterface; +use Ray\Di\Exception\Unbound; use Ray\Di\InjectionPointInterface; +use Ray\Di\InjectorInterface; use Ray\Di\ProviderInterface; +use Ray\Query\Exception\SqlFileNotFoundException; +use Throwable; /** @implements ProviderInterface */ final class RowInterfaceProvider implements ProviderInterface @@ -19,19 +23,34 @@ final class RowInterfaceProvider implements ProviderInterface /** @var SqlFinder */ private $finder; + private $injector; public function __construct( InjectionPointInterface $ip, ExtendedPdoInterface $pdo, - SqlFinder $finder + SqlFinder $finder, + InjectorInterface $injector ) { $this->ip = $ip; $this->pdo = $pdo; $this->finder = $finder; + $this->injector = $injector; } public function get(): SqlQueryRow { - return new SqlQueryRow($this->pdo, ($this->finder)($this->ip->getParameter())); + try { + $sql = ($this->finder)($this->ip->getParameter()); + } catch (SqlFileNotFoundException | Throwable $e) { + try { + $named = $e->sql; + + return $this->injector->getInstance(RowInterface::class, $named); + } catch (Unbound $unbound) { + throw $e; + } + } + + return new SqlQueryRow($this->pdo, $sql); } } diff --git a/src/RowListInterfaceProvider.php b/src/RowListInterfaceProvider.php index 284f32e..53ded0d 100644 --- a/src/RowListInterfaceProvider.php +++ b/src/RowListInterfaceProvider.php @@ -5,8 +5,11 @@ namespace Ray\Query; use Aura\Sql\ExtendedPdoInterface; +use Ray\Di\Exception\Unbound; use Ray\Di\InjectionPointInterface; +use Ray\Di\InjectorInterface; use Ray\Di\ProviderInterface; +use Ray\Query\Exception\SqlFileNotFoundException; /** @implements ProviderInterface */ final class RowListInterfaceProvider implements ProviderInterface @@ -19,19 +22,38 @@ final class RowListInterfaceProvider implements ProviderInterface /** @var SqlFinder */ private $finder; + private $injector; public function __construct( InjectionPointInterface $ip, ExtendedPdoInterface $pdo, - SqlFinder $finder + SqlFinder $finder, + InjectorInterface $injector ) { $this->ip = $ip; $this->pdo = $pdo; $this->finder = $finder; + $this->injector = $injector; } - public function get(): SqlQueryRowList + public function get(): QueryInterface { - return new SqlQueryRowList($this->pdo, ($this->finder)($this->ip->getParameter())); + try { + $sql = ($this->finder)($this->ip->getParameter()); + } catch (SqlFileNotFoundException $e) { + try { + $named = $e->sql; + + return $this->injector->getInstance(RowListInterface::class, $named); + } catch (Unbound $unbound) { + try { + return $this->injector->getInstance('', $named); + } catch (Unbound $unbound) { + throw $e; + } + } + } + + return new SqlQueryRowList($this->pdo, $sql); } } diff --git a/src/SqlFinder.php b/src/SqlFinder.php index 30d511c..a83c332 100644 --- a/src/SqlFinder.php +++ b/src/SqlFinder.php @@ -43,7 +43,7 @@ public function __invoke(ReflectionParameter $param): string if (! file_exists($file)) { $msg = sprintf('%s:%s', (string) $param, $file); - throw new SqlFileNotFoundException($msg); + throw new SqlFileNotFoundException($msg, $sqlAnnotation->sql); } return (string) file_get_contents($file); diff --git a/src/SqlQueryProviderModule.php b/src/SqlQueryProviderModule.php index 0cacd9e..3ac529a 100644 --- a/src/SqlQueryProviderModule.php +++ b/src/SqlQueryProviderModule.php @@ -26,5 +26,6 @@ protected function configure() $this->bind(RowInterface::class)->toProvider(RowInterfaceProvider::class); $this->bind(RowListInterface::class)->toProvider(RowListInterfaceProvider::class); $this->bind(InvokeInterface::class)->toProvider(RowListInterfaceProvider::class); + $this->bind(QueryInterface::class)->toProvider(RowListInterfaceProvider::class); } } From c45e7c8acd88431d0916b8723e3c0e367b45efa1 Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Tue, 11 Jun 2024 12:15:26 +0900 Subject: [PATCH 03/34] Update interface implementation in providers The providers' RowInterfaceProvider and RowListInterfaceProvider have been updated to implement the QueryInterface instead of RowInterface and RowListInterface respectively. Also, the return type for the get method in RowInterfaceProvider is changed to QueryInterface. --- src/RowInterfaceProvider.php | 4 ++-- src/RowListInterfaceProvider.php | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/RowInterfaceProvider.php b/src/RowInterfaceProvider.php index 11f8ec3..5e56b32 100644 --- a/src/RowInterfaceProvider.php +++ b/src/RowInterfaceProvider.php @@ -12,7 +12,7 @@ use Ray\Query\Exception\SqlFileNotFoundException; use Throwable; -/** @implements ProviderInterface */ +/** @implements ProviderInterface */ final class RowInterfaceProvider implements ProviderInterface { /** @var InjectionPointInterface */ @@ -37,7 +37,7 @@ public function __construct( $this->injector = $injector; } - public function get(): SqlQueryRow + public function get(): QueryInterface { try { $sql = ($this->finder)($this->ip->getParameter()); diff --git a/src/RowListInterfaceProvider.php b/src/RowListInterfaceProvider.php index 53ded0d..11c89f9 100644 --- a/src/RowListInterfaceProvider.php +++ b/src/RowListInterfaceProvider.php @@ -11,7 +11,7 @@ use Ray\Di\ProviderInterface; use Ray\Query\Exception\SqlFileNotFoundException; -/** @implements ProviderInterface */ +/** @implements ProviderInterface */ final class RowListInterfaceProvider implements ProviderInterface { /** @var InjectionPointInterface */ From deef1268acf9812e9f216bc298791763b1ebd7a4 Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Tue, 11 Jun 2024 16:06:20 +0900 Subject: [PATCH 04/34] Refactor error handling in RowInterfaceProvider and RowListInterfaceProvider In this commit, we've improved error handling in two classes - RowInterfaceProvider and RowListInterfaceProvider. If a SQLFileNotFound exception occurs, a warning is logged and the code tries to provide an instance with the given name. This change helps make our exceptions more transparent and our error recovery more robust. --- src/RowInterfaceProvider.php | 15 ++++++++++++--- src/RowListInterfaceProvider.php | 24 ++++++++++++++++++++++-- 2 files changed, 34 insertions(+), 5 deletions(-) diff --git a/src/RowInterfaceProvider.php b/src/RowInterfaceProvider.php index 5e56b32..b6dd759 100644 --- a/src/RowInterfaceProvider.php +++ b/src/RowInterfaceProvider.php @@ -10,7 +10,9 @@ use Ray\Di\InjectorInterface; use Ray\Di\ProviderInterface; use Ray\Query\Exception\SqlFileNotFoundException; -use Throwable; + +use function error_log; +use function sprintf; /** @implements ProviderInterface */ final class RowInterfaceProvider implements ProviderInterface @@ -23,6 +25,8 @@ final class RowInterfaceProvider implements ProviderInterface /** @var SqlFinder */ private $finder; + + /** @var InjectorInterface */ private $injector; public function __construct( @@ -41,14 +45,19 @@ public function get(): QueryInterface { try { $sql = ($this->finder)($this->ip->getParameter()); - } catch (SqlFileNotFoundException | Throwable $e) { + // For development + // @codeCoverageIgnoreStart + } catch (SqlFileNotFoundException $e) { try { $named = $e->sql; + $instance = $this->injector->getInstance(RowInterface::class, $named); + error_log(sprintf('Warning: #[Sql(\'%s\')] is not vald. Change to #[\\Ray\\Di\\Di\\Named(\'%s\')]', $named, $named)); - return $this->injector->getInstance(RowInterface::class, $named); + return $instance; } catch (Unbound $unbound) { throw $e; } + // @codeCoverageIgnoreEnd } return new SqlQueryRow($this->pdo, $sql); diff --git a/src/RowListInterfaceProvider.php b/src/RowListInterfaceProvider.php index 11c89f9..2380e9e 100644 --- a/src/RowListInterfaceProvider.php +++ b/src/RowListInterfaceProvider.php @@ -11,6 +11,10 @@ use Ray\Di\ProviderInterface; use Ray\Query\Exception\SqlFileNotFoundException; +use function assert; +use function error_log; +use function sprintf; + /** @implements ProviderInterface */ final class RowListInterfaceProvider implements ProviderInterface { @@ -22,6 +26,8 @@ final class RowListInterfaceProvider implements ProviderInterface /** @var SqlFinder */ private $finder; + + /** @var InjectorInterface */ private $injector; public function __construct( @@ -43,14 +49,28 @@ public function get(): QueryInterface } catch (SqlFileNotFoundException $e) { try { $named = $e->sql; + // Try "RowListInterface" + $instance = $this->injector->getInstance(RowListInterface::class, $named); + // For development + // @codeCoverageIgnoreStart + assert($instance instanceof QueryInterface); + error_log(sprintf('Warning: #[Sql(\'%s\')] is not vald. Change to #[\\Ray\\Di\\Di\\Named(\'%s\')]', $named, $named)); - return $this->injector->getInstance(RowListInterface::class, $named); + return $instance; } catch (Unbound $unbound) { try { - return $this->injector->getInstance('', $named); + assert(isset($named)); // @phpstan-ignore-line + // try "callable" + /** @var QueryInterface $instance */ + $instance = $this->injector->getInstance('', $named); + assert($instance instanceof QueryInterface); + error_log(sprintf('Warning: #[Sql(\'%s\')] is not vald. Change to #[\\Ray\\Di\\Di\\Named(\'%s\')]', $named, $named)); + + return $instance; } catch (Unbound $unbound) { throw $e; } + // @codeCoverageIgnoreEnd } } From b45cf3e63fae2f44831ed5c2e0b5c72f392bb561 Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Tue, 11 Jun 2024 16:10:40 +0900 Subject: [PATCH 05/34] Refactor code to use attribute Syntax The code was refactored to use PHP 8's attribute Syntax. This affected both the README.md and README.ja.md documentation files. Refactoring involved replacing the older doctrine annotations with the new #[Attribute] syntax. This impacts how constructors are written and how dependencies are injected. --- README.ja.md | 112 +++++++++++++++++++-------------------------------- README.md | 77 ++++++++++++----------------------- 2 files changed, 67 insertions(+), 122 deletions(-) diff --git a/README.ja.md b/README.ja.md index ca03d5a..79cba88 100644 --- a/README.ja.md +++ b/README.ja.md @@ -24,9 +24,9 @@ ### Composerインストール - $ composer require ray/query-module + composer require ray/query-module -### Moduleインストール +### モジュールのインストール ```php use Ray\Di\AbstractModule; @@ -34,8 +34,8 @@ use Ray\Query\SqlQueryModule; class AppModule extends AbstractModule { - protected function configure() - { +protected function configure() +{ // SqlQueryModule インストール $this->install(new SqlQueryModule($sqlDir)); @@ -77,26 +77,10 @@ SELECT * FROM todo WHERE id = :id ```php class Todo { - /** - * @var callable - */ - private $createTodo; - - /** - * @var callable - */ - private $todo; - - /** - * @Named("createTodo=todo_insert, todo=todo_item_by_id") - */ public function __construct( - callable $createTodo, - callable $todo - ){ - $this->createTodo = $createTodo; - $this->todo = $todo; - } + private #[Sql("createTodo") QUereInterface $createTodo、 + private #[Sql("createTodo") QUereInterface $todo + ){} public function get(string $uuid) { @@ -112,26 +96,25 @@ class Todo } } ``` -## 単一行と複数行 +## 行または行リスト -得られる値が`単一行(Row)`か`複数行(Rowのリスト`)に応じて`RowInterface`か`RowListInterface`を指定することができます。 +RowInterface` または `RowListInterface` を使用すると、戻り値の型を `Row` または `RowList` に指定することができます。 +RowInterface` は単一の行を返す SQL を指定するのに便利です。 ```php use Ray\Query\RowInterface; class Todo { - /** - * @Named("todo_item_by_id") - */ - public function __construct(RowInterface $todo) + public function __construct( + #[Sql('todo_item_by_id') RowInterface $todo) { $this->todo = $todo; } - + public function get(string $uuid) { -    $todo = ($this->todo)(['id' => $uuid]); // 単一行 + $todo = ($this->todo)(['id' => $uuid]); // 一行分のデータ } } ``` @@ -141,45 +124,37 @@ use Ray\Query\RowListInterface; class Todos { - /** - * @Named("todos") - */ - public function __construct(RowListInterface $todos) - { - $this->todos = $todos; - } + public function __construct( + #[Sql("todos") private readonly RowListInterface $todos + ){} public function get(string $uuid) { -    $todos = ($this->todos)(); // 複数行 + $todos = ($this->todos)(); // 複数行のデータ } } ``` -## メソッドをオーバーライド +## メソッドをオーバーライドする -`@Query`でメソッド全体をSQLの実行に置き換えることができます。 +メソッド全体を `#[Query]` で指定した callable オブジェクトでオーバーライドすることができます。 ```php -class Foo +use Ray\Query\Annotation\Query;class Foo { - /** - * @Query(id="todo_item_by_id") - */ + #[Query("todo_item_by_id")] public function get(string $id) { } } ``` -メソッド引数とSQLのバインドする変数名が違う時は`templated=true`を指定すると`uri_template`と同じように変数名を変えることができます。 +パラメータ名がメソッドの引数とQueryオブジェクトの引数で異なる場合は、uri_template式で解決できます。 ```php -class FooTempalted +use Ray\Query\Annotation\Query;class FooTempalted { - /** - * @Query(id="todo_item_by_id?id={a}", templated=true) - */ + #[Query(id:"todo_item_by_id?id={a}", templated:true)] public function get(string $a) { } @@ -189,20 +164,18 @@ class FooTempalted 単一行の時は`type='row'`を指定します。 ```php -class FooRow +use Ray\Query\Annotation\Query;class FooRow + +#[Query("ticket_item_by_id", type: "row")] +public function onGet(string $id): static { - /** - * @Query(id="ticket_item_by_id", type="row") - */ - public function onGet(string $id) : ResourceObject - { - } +} } ``` SELETした結果が無い場合にはcode 404が返ります。 -## URIを実行オブジェクトに +## URI を Web リクエストオブジェクトに変換 `WebQueryModule`は設定で束縛したURIをWebアクセスする実行関数がインジェクトされます。 例えば以下の例なら、`https://httpbin.org/todo`を`POST`リクエストする`$createTodo`の実行オブジェクトに変換されインジェクトされます @@ -226,19 +199,16 @@ class AppModule extends AbstractModule } ``` -利用コードは`SqlQueryModule`の時と同じです。 +使い方は `SqlQueryModule` と同じです。 ```php -/** - * @Named("createTodo=todo_post, todo=todo_get") - */ -public function __construct( - callable $createTodo, - callable $todo -){ - $this->createTodo = $createTodo; - $this->todo = $todo; -} + public function __construct( + callable $createTodo, + callable $todo + ){ + $this->createTodo = $createTodo; + $this->todo = $todo; + } ``` ```php @@ -263,13 +233,13 @@ class CreateTodo implements QueryInterface { private $pdo; private $builder; - + public function __construct(PdoInterface $pdo, QueryBuilderInferface $builder) { $this->pdo = $pdo; $this->builder = $builder; } - + public function __invoke(array $query) { // $pdoと$builderを使ったクエリ実行 diff --git a/README.md b/README.md index d99d65e..3d9dacd 100644 --- a/README.md +++ b/README.md @@ -71,28 +71,15 @@ SELECT * FROM todo WHERE id = :id A callable object injected into the constructor. Those object was made in specified sql with `@Named` binding. ```php +use Ray\Query\Annotation\Sql; +use Ray\Query\InvokeInterface; + class Todo { - /** - * @var callable - */ - private $createTodo; - - /** - * @var callable - */ - private $todo; - - /** - * @Named("createTodo=todo_insert, todo=todo_item_by_id") - */ public function __construct( - callable $createTodo, - callable $todo - ){ - $this->createTodo = $createTodo; - $this->todo = $todo; - } + #[Sql('todo_insert') private readonly InvokeInterface $createTodo, + #[Sql('todo_by_id') private readonly InvokeInterface $todo + ){} public function get(string $uuid) { @@ -118,17 +105,13 @@ use Ray\Query\RowInterface; class Todo { - /** - * @Named("todo_item_by_id") - */ - public function __construct(RowInterface $todo) - { - $this->todo = $todo; - } + public function __construct( + #[Sql('todo_by_id') private readonly RowInterface $todo + ){} public function get(string $uuid) { -    $todo = ($this->todo)(['id' => $uuid]); // single row data + $todo = ($this->todo)(['id' => $uuid]); // single row data } } ``` @@ -138,17 +121,13 @@ use Ray\Query\RowListInterface; class Todos { - /** - * @Named("todos") - */ - public function __construct(RowListInterface $todos) - { - $this->todos = $todos; - } + public function __construct( + #[Sql('todos') private readonly RowListInterface $todos + }{} public function get(string $uuid) { -    $todos = ($this->todos)(); // multiple row data + $todos = ($this->todos)(); // multiple row data } } ``` @@ -160,9 +139,7 @@ Entire method invocation can be override with callable object in specified with ```php class Foo { - /** - * @Query(id="todo_item_by_id") - */ + #[Query("todo_item_by_id") public function get(string $id) { } @@ -172,11 +149,9 @@ class Foo When parameter name is different method arguments and Query object arguments, uri_template style expression can solve it. ```php -class FooTempalted +use Ray\Query\Annotation\Query;class FooTempalted { - /** - * @Query(id="todo_item_by_id?id={a}", templated=true) - */ + #[Query("todo_item_by_id?id={a}", templated: true)] public function get(string $a) { } @@ -188,9 +163,7 @@ Specify `type='row'` when single row result is expected to return. ```php class FooRow { - /** - * @Query(id="ticket_item_by_id", type="row") - */ + #[Query("ticket_item_by_id", type: "row")] public function onGet(string $id) : ResourceObject { } @@ -225,14 +198,16 @@ class AppModule extends AbstractModule The usage code is the same as for `SqlQueryModule`. - ```php -/** - * @Named("createTodo=todo_post, todo=todo_get") - */ + +use Ray\Di\Di\Named; + +private $createTodo; +private $todo; + public function __construct( - callable $createTodo, - callable $todo + #[Named('todo_post') callable $createTodo, + #[Named('todo_get') callable $todo, ){ $this->createTodo = $createTodo; $this->todo = $todo; From 32be06ef9d3484c9e87b6c59b2318a0638f88c61 Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Tue, 11 Jun 2024 16:18:01 +0900 Subject: [PATCH 06/34] fixup! Refactor code to use attribute Syntax --- README.ja.md | 15 ++++++--------- README.md | 32 ++++++++++++++++---------------- 2 files changed, 22 insertions(+), 25 deletions(-) diff --git a/README.ja.md b/README.ja.md index 79cba88..69ed987 100644 --- a/README.ja.md +++ b/README.ja.md @@ -231,11 +231,10 @@ class AppModule extends AbstractModule ```php class CreateTodo implements QueryInterface { - private $pdo; - private $builder; - - public function __construct(PdoInterface $pdo, QueryBuilderInferface $builder) - { + public function __construct( + private PdoInterface $pdo, + private QueryBuilderInferface $builder + ) { $this->pdo = $pdo; $this->builder = $builder; } @@ -248,13 +247,11 @@ class CreateTodo implements QueryInterface } ``` -`callable`に束縛します。 - ```php -$this->bind('')->annotatedWith('cretate_todo')->to(CreateTodo::class); // callableはインターフェイスなし +$this->bind(QueryInterface::class)->annotatedWith('cretate_todo')->to(CreateTodo::class); ``` -利用コードは同じです。`@Query`の利用コードも変わりません。 +利用コードは同じです。`#[Query]`の利用コードも変わりません。 ## ISO8601 DateTimeモジュール diff --git a/README.md b/README.md index 3d9dacd..d8c8aed 100644 --- a/README.md +++ b/README.md @@ -10,23 +10,23 @@ `Ray.QueryModule` makes a query to an external media such as a database or Web API with a function object to be injected. - * `SqlQueryModule` is for DB. Convert the SQL file to a simple function object that executes that SQL. - * `WebQueryModule` is for the Web API. Convert the URI and method set into a simple function object that Web requests to that URI. - * `PhpQueryModule` is a generic module. It provides storage access which can not be provided by static conversion by PHP function object. +* `SqlQueryModule` is for DB. Convert the SQL file to a simple function object that executes that SQL. +* `WebQueryModule` is for the Web API. Convert the URI and method set into a simple function object that Web requests to that URI. +* `PhpQueryModule` is a generic module. It provides storage access which can not be provided by static conversion by PHP function object. ## Motivation - * You can have a clear boundary between domain layer (usage code) and infrastructure layer (injected function) in code. - * Execution objects are generated automatically so you do not need to write procedural code for execution. - * Since usage codes are indifferent to the actual state of external media, storage can be changed later. Easy parallel development and stabbing. +* You can have a clear boundary between domain layer (usage code) and infrastructure layer (injected function) in code. +* Execution objects are generated automatically so you do not need to write procedural code for execution. +* Since usage codes are indifferent to the actual state of external media, storage can be changed later. Easy parallel development and stabbing. ## Installation ### Composer install $ composer require ray/query-module - + ### Module install ```php @@ -68,17 +68,17 @@ SELECT * FROM todo WHERE id = :id ## Convert SQL to SQL invocation object -A callable object injected into the constructor. Those object was made in specified sql with `@Named` binding. +A callable object injected into the constructor. Those object was made in specified sql with `#[Sql]` binding. ```php use Ray\Query\Annotation\Sql; -use Ray\Query\InvokeInterface; +use Ray\Query\InvokeInterface;use Ray\Query\QueryInterface; class Todo { public function __construct( - #[Sql('todo_insert') private readonly InvokeInterface $createTodo, - #[Sql('todo_by_id') private readonly InvokeInterface $todo + #[Sql('todo_insert') private readonly QueryInterface $createTodo, + #[Sql('todo_by_id') private readonly QueryInterface $todo ){} public function get(string $uuid) @@ -97,7 +97,7 @@ class Todo ``` ## Row or RowList -You can specify expected return value type is either `Row` or `RowList` with `RowInterface` or `RowListInterface`. +You can specify expected return value type is either `Row` or `RowList` with `RowInterface` or `RowListInterface`. `RowInterface` is handy to specify SQL which return single row. ```php @@ -185,7 +185,7 @@ class AppModule extends AbstractModule { protected function configure() { - // WebQueryModuleインストール + // WebQueryModule install $webQueryConfig = [ 'todo_post' => ['POST', 'https://httpbin.org/todo'], 'todo_get' => ['GET', 'https://httpbin.org/todo'] @@ -225,7 +225,7 @@ public function __construct( ($this->todo)(['id' => $uuid]); ``` -The usage code of `@Query` does not change either. +The usage code of `#[Query]` does not change either. ## Bind to PHP class @@ -254,7 +254,7 @@ class CreateTodo implements QueryInterface Bind to `callable`. ```php -$this->bind('')->annotatedWith('cretate_todo')->to(CreateTodo::class); // callableはインターフェイスなし +$this->bind(QueryInterface::class)->annotatedWith('cretate_todo')->to(CreateTodo::class); ``` The usage codes are the same. The usage code of `@Query` does not change either. @@ -293,5 +293,5 @@ php demo/run.php ## BEAR.Sunday example - * [Koriym.Ticketsan](https://github.com/koriym/Koriym.TicketSan/blob/master/src/Resource/App/Ticket.php) +* [Koriym.Ticketsan](https://github.com/koriym/Koriym.TicketSan/blob/master/src/Resource/App/Ticket.php) From 75602927aa50269018612ec5d0aa6a91e8487976 Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Tue, 11 Jun 2024 16:22:36 +0900 Subject: [PATCH 07/34] Updated vendor dependencies The composer.lock file was updated to include the latest versions of several dependencies, including "amphp/amp", "amphp/byte-stream", "composer/pcre", and "composer/semver" among others. Some metadata and reference details were also updated in the process. --- .github/workflows/continuous-integration.yml | 1 - demo/0-manual-injection.php | 15 +- demo/1-constructor-injection.php | 19 +- demo/Todo.php | 8 +- vendor-bin/tools/composer.lock | 1248 ++++++++++-------- 5 files changed, 675 insertions(+), 616 deletions(-) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 54bf867..b42a071 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -11,4 +11,3 @@ jobs: with: old_stable: '["7.3", "7.4", "8.0", "8.1", "8.2"]' current_stable: 8.3 - script: demo/run.php diff --git a/demo/0-manual-injection.php b/demo/0-manual-injection.php index cd4d10d..bab13a7 100644 --- a/demo/0-manual-injection.php +++ b/demo/0-manual-injection.php @@ -3,25 +3,16 @@ declare(strict_types=1); use Aura\Sql\ExtendedPdo; +use Ray\Query\QueryInterface; use Ray\Query\SqlQueryRowList; require dirname(__DIR__) . '/vendor/autoload.php'; class Todo { - /** - * @var callable - */ - private $createTodo; - - /** - * @var callable - */ - private $todoItem; - public function __construct( - callable $createTodo, - callable $todoItem + private QueryInterface $createTodo, + private QueryInterface $todoItem ) { $this->createTodo = $createTodo; $this->todoItem = $todoItem; diff --git a/demo/1-constructor-injection.php b/demo/1-constructor-injection.php index ee79a4f..a114948 100644 --- a/demo/1-constructor-injection.php +++ b/demo/1-constructor-injection.php @@ -6,28 +6,17 @@ use Ray\Di\AbstractModule; use Ray\Di\Di\Named; use Ray\Di\Injector; +use Ray\Query\Annotation\Sql; +use Ray\Query\QueryInterface; use Ray\Query\SqlQueryModule; require dirname(__DIR__) . '/vendor/autoload.php'; class Todo { - /** - * @var callable - */ - private $createTodo; - - /** - * @var callable - */ - private $todoItem; - - /** - * @Named("createTodo=todo_insert, todoItem=todo_item_by_id") - */ public function __construct( - callable $createTodo, - callable $todoItem + #[Sql('todo_insert') QueryInterface $createTodo, + #[Sql('todo_item_by_id') QueryInterface $todoItem, ) { $this->createTodo = $createTodo; $this->todoItem = $todoItem; diff --git a/demo/Todo.php b/demo/Todo.php index 3df2c48..1d0f905 100644 --- a/demo/Todo.php +++ b/demo/Todo.php @@ -8,16 +8,12 @@ class Todo { - /** - * @Query("todo_item_by_id") - */ + #[Query('todo_item_by_id')] public function get(string $id) { } - /** - * @Query(id="todo_insert?id={uuid}", templated=true) - */ + #[Query('todo_insert?id={uuid}', templated: true)] public function create(string $uuid, string $title) { } diff --git a/vendor-bin/tools/composer.lock b/vendor-bin/tools/composer.lock index 841ce55..606418d 100644 --- a/vendor-bin/tools/composer.lock +++ b/vendor-bin/tools/composer.lock @@ -4,21 +4,21 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "24af3cb796e74e26f4b89886bd7710ef", + "content-hash": "8dc94057e9943e1644b88010beabbbde", "packages": [], "packages-dev": [ { "name": "amphp/amp", - "version": "v2.6.2", + "version": "v2.6.4", "source": { "type": "git", "url": "https://github.com/amphp/amp.git", - "reference": "9d5100cebffa729aaffecd3ad25dc5aeea4f13bb" + "reference": "ded3d9be08f526089eb7ee8d9f16a9768f9dec2d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/amphp/amp/zipball/9d5100cebffa729aaffecd3ad25dc5aeea4f13bb", - "reference": "9d5100cebffa729aaffecd3ad25dc5aeea4f13bb", + "url": "https://api.github.com/repos/amphp/amp/zipball/ded3d9be08f526089eb7ee8d9f16a9768f9dec2d", + "reference": "ded3d9be08f526089eb7ee8d9f16a9768f9dec2d", "shasum": "" }, "require": { @@ -30,8 +30,8 @@ "ext-json": "*", "jetbrains/phpstorm-stubs": "^2019.3", "phpunit/phpunit": "^7 | ^8 | ^9", - "psalm/phar": "^3.11@dev", - "react/promise": "^2" + "react/promise": "^2", + "vimeo/psalm": "^3.12" }, "type": "library", "extra": { @@ -86,7 +86,7 @@ "support": { "irc": "irc://irc.freenode.org/amphp", "issues": "https://github.com/amphp/amp/issues", - "source": "https://github.com/amphp/amp/tree/v2.6.2" + "source": "https://github.com/amphp/amp/tree/v2.6.4" }, "funding": [ { @@ -94,20 +94,20 @@ "type": "github" } ], - "time": "2022-02-20T17:52:18+00:00" + "time": "2024-03-21T18:52:26+00:00" }, { "name": "amphp/byte-stream", - "version": "v1.8.1", + "version": "v1.8.2", "source": { "type": "git", "url": "https://github.com/amphp/byte-stream.git", - "reference": "acbd8002b3536485c997c4e019206b3f10ca15bd" + "reference": "4f0e968ba3798a423730f567b1b50d3441c16ddc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/amphp/byte-stream/zipball/acbd8002b3536485c997c4e019206b3f10ca15bd", - "reference": "acbd8002b3536485c997c4e019206b3f10ca15bd", + "url": "https://api.github.com/repos/amphp/byte-stream/zipball/4f0e968ba3798a423730f567b1b50d3441c16ddc", + "reference": "4f0e968ba3798a423730f567b1b50d3441c16ddc", "shasum": "" }, "require": { @@ -123,11 +123,6 @@ "psalm/phar": "^3.11.4" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - } - }, "autoload": { "files": [ "lib/functions.php" @@ -151,7 +146,7 @@ } ], "description": "A stream abstraction to make working with non-blocking I/O simple.", - "homepage": "http://amphp.org/byte-stream", + "homepage": "https://amphp.org/byte-stream", "keywords": [ "amp", "amphp", @@ -161,9 +156,8 @@ "stream" ], "support": { - "irc": "irc://irc.freenode.org/amphp", "issues": "https://github.com/amphp/byte-stream/issues", - "source": "https://github.com/amphp/byte-stream/tree/v1.8.1" + "source": "https://github.com/amphp/byte-stream/tree/v1.8.2" }, "funding": [ { @@ -171,7 +165,7 @@ "type": "github" } ], - "time": "2021-03-30T17:13:30+00:00" + "time": "2024-04-13T18:00:56+00:00" }, { "name": "composer/package-versions-deprecated", @@ -248,30 +242,30 @@ }, { "name": "composer/pcre", - "version": "1.0.1", + "version": "3.1.4", "source": { "type": "git", "url": "https://github.com/composer/pcre.git", - "reference": "67a32d7d6f9f560b726ab25a061b38ff3a80c560" + "reference": "04229f163664973f68f38f6f73d917799168ef24" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/pcre/zipball/67a32d7d6f9f560b726ab25a061b38ff3a80c560", - "reference": "67a32d7d6f9f560b726ab25a061b38ff3a80c560", + "url": "https://api.github.com/repos/composer/pcre/zipball/04229f163664973f68f38f6f73d917799168ef24", + "reference": "04229f163664973f68f38f6f73d917799168ef24", "shasum": "" }, "require": { - "php": "^5.3.2 || ^7.0 || ^8.0" + "php": "^7.4 || ^8.0" }, "require-dev": { "phpstan/phpstan": "^1.3", "phpstan/phpstan-strict-rules": "^1.1", - "symfony/phpunit-bridge": "^4.2 || ^5" + "symfony/phpunit-bridge": "^5" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "1.x-dev" + "dev-main": "3.x-dev" } }, "autoload": { @@ -299,7 +293,7 @@ ], "support": { "issues": "https://github.com/composer/pcre/issues", - "source": "https://github.com/composer/pcre/tree/1.0.1" + "source": "https://github.com/composer/pcre/tree/3.1.4" }, "funding": [ { @@ -315,20 +309,20 @@ "type": "tidelift" } ], - "time": "2022-01-21T20:24:37+00:00" + "time": "2024-05-27T13:40:54+00:00" }, { "name": "composer/semver", - "version": "3.2.9", + "version": "3.4.0", "source": { "type": "git", "url": "https://github.com/composer/semver.git", - "reference": "a951f614bd64dcd26137bc9b7b2637ddcfc57649" + "reference": "35e8d0af4486141bc745f23a29cc2091eb624a32" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/semver/zipball/a951f614bd64dcd26137bc9b7b2637ddcfc57649", - "reference": "a951f614bd64dcd26137bc9b7b2637ddcfc57649", + "url": "https://api.github.com/repos/composer/semver/zipball/35e8d0af4486141bc745f23a29cc2091eb624a32", + "reference": "35e8d0af4486141bc745f23a29cc2091eb624a32", "shasum": "" }, "require": { @@ -378,9 +372,9 @@ "versioning" ], "support": { - "irc": "irc://irc.freenode.org/composer", + "irc": "ircs://irc.libera.chat:6697/composer", "issues": "https://github.com/composer/semver/issues", - "source": "https://github.com/composer/semver/tree/3.2.9" + "source": "https://github.com/composer/semver/tree/3.4.0" }, "funding": [ { @@ -396,31 +390,31 @@ "type": "tidelift" } ], - "time": "2022-02-04T13:58:43+00:00" + "time": "2023-08-31T09:50:34+00:00" }, { "name": "composer/xdebug-handler", - "version": "2.0.5", + "version": "3.0.5", "source": { "type": "git", "url": "https://github.com/composer/xdebug-handler.git", - "reference": "9e36aeed4616366d2b690bdce11f71e9178c579a" + "reference": "6c1925561632e83d60a44492e0b344cf48ab85ef" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/9e36aeed4616366d2b690bdce11f71e9178c579a", - "reference": "9e36aeed4616366d2b690bdce11f71e9178c579a", + "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/6c1925561632e83d60a44492e0b344cf48ab85ef", + "reference": "6c1925561632e83d60a44492e0b344cf48ab85ef", "shasum": "" }, "require": { - "composer/pcre": "^1", - "php": "^5.3.2 || ^7.0 || ^8.0", + "composer/pcre": "^1 || ^2 || ^3", + "php": "^7.2.5 || ^8.0", "psr/log": "^1 || ^2 || ^3" }, "require-dev": { "phpstan/phpstan": "^1.0", "phpstan/phpstan-strict-rules": "^1.1", - "symfony/phpunit-bridge": "^4.2 || ^5.0 || ^6.0" + "phpunit/phpunit": "^8.5 || ^9.6 || ^10.5" }, "type": "library", "autoload": { @@ -444,9 +438,9 @@ "performance" ], "support": { - "irc": "irc://irc.freenode.org/composer", + "irc": "ircs://irc.libera.chat:6697/composer", "issues": "https://github.com/composer/xdebug-handler/issues", - "source": "https://github.com/composer/xdebug-handler/tree/2.0.5" + "source": "https://github.com/composer/xdebug-handler/tree/3.0.5" }, "funding": [ { @@ -462,39 +456,42 @@ "type": "tidelift" } ], - "time": "2022-02-24T20:20:32+00:00" + "time": "2024-05-06T16:37:16+00:00" }, { "name": "dealerdirect/phpcodesniffer-composer-installer", - "version": "v0.7.2", + "version": "v1.0.0", "source": { "type": "git", - "url": "https://github.com/Dealerdirect/phpcodesniffer-composer-installer.git", - "reference": "1c968e542d8843d7cd71de3c5c9c3ff3ad71a1db" + "url": "https://github.com/PHPCSStandards/composer-installer.git", + "reference": "4be43904336affa5c2f70744a348312336afd0da" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Dealerdirect/phpcodesniffer-composer-installer/zipball/1c968e542d8843d7cd71de3c5c9c3ff3ad71a1db", - "reference": "1c968e542d8843d7cd71de3c5c9c3ff3ad71a1db", + "url": "https://api.github.com/repos/PHPCSStandards/composer-installer/zipball/4be43904336affa5c2f70744a348312336afd0da", + "reference": "4be43904336affa5c2f70744a348312336afd0da", "shasum": "" }, "require": { "composer-plugin-api": "^1.0 || ^2.0", - "php": ">=5.3", + "php": ">=5.4", "squizlabs/php_codesniffer": "^2.0 || ^3.1.0 || ^4.0" }, "require-dev": { "composer/composer": "*", + "ext-json": "*", + "ext-zip": "*", "php-parallel-lint/php-parallel-lint": "^1.3.1", - "phpcompatibility/php-compatibility": "^9.0" + "phpcompatibility/php-compatibility": "^9.0", + "yoast/phpunit-polyfills": "^1.0" }, "type": "composer-plugin", "extra": { - "class": "Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\Plugin" + "class": "PHPCSStandards\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\Plugin" }, "autoload": { "psr-4": { - "Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\": "src/" + "PHPCSStandards\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -510,7 +507,7 @@ }, { "name": "Contributors", - "homepage": "https://github.com/Dealerdirect/phpcodesniffer-composer-installer/graphs/contributors" + "homepage": "https://github.com/PHPCSStandards/composer-installer/graphs/contributors" } ], "description": "PHP_CodeSniffer Standards Composer Installer Plugin", @@ -534,10 +531,10 @@ "tests" ], "support": { - "issues": "https://github.com/dealerdirect/phpcodesniffer-composer-installer/issues", - "source": "https://github.com/dealerdirect/phpcodesniffer-composer-installer" + "issues": "https://github.com/PHPCSStandards/composer-installer/issues", + "source": "https://github.com/PHPCSStandards/composer-installer" }, - "time": "2022-02-04T12:51:07+00:00" + "time": "2023-01-05T11:28:13+00:00" }, { "name": "dnoegel/php-xdg-base-dir", @@ -578,23 +575,23 @@ }, { "name": "doctrine/coding-standard", - "version": "8.2.1", + "version": "11.1.0", "source": { "type": "git", "url": "https://github.com/doctrine/coding-standard.git", - "reference": "f595b060799c1a0d76ead16981804eaa0bbcd8d6" + "reference": "4bcfa9d922e13cb3b8078d4752e3ba276f959c88" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/coding-standard/zipball/f595b060799c1a0d76ead16981804eaa0bbcd8d6", - "reference": "f595b060799c1a0d76ead16981804eaa0bbcd8d6", + "url": "https://api.github.com/repos/doctrine/coding-standard/zipball/4bcfa9d922e13cb3b8078d4752e3ba276f959c88", + "reference": "4bcfa9d922e13cb3b8078d4752e3ba276f959c88", "shasum": "" }, "require": { - "dealerdirect/phpcodesniffer-composer-installer": "^0.6.2 || ^0.7", - "php": "^7.1 || ^8.0", - "slevomat/coding-standard": "^6.4.1", - "squizlabs/php_codesniffer": "^3.5.8" + "dealerdirect/phpcodesniffer-composer-installer": "^0.6.2 || ^0.7 || ^1.0.0", + "php": "^7.2 || ^8.0", + "slevomat/coding-standard": "^8.6.2", + "squizlabs/php_codesniffer": "^3.7" }, "type": "phpcodesniffer-standard", "notification-url": "https://packagist.org/downloads/", @@ -618,6 +615,7 @@ "code", "coding", "cs", + "dev", "doctrine", "rules", "sniffer", @@ -627,22 +625,22 @@ ], "support": { "issues": "https://github.com/doctrine/coding-standard/issues", - "source": "https://github.com/doctrine/coding-standard/tree/8.2.1" + "source": "https://github.com/doctrine/coding-standard/tree/11.1.0" }, - "time": "2021-04-03T10:54:55+00:00" + "time": "2023-01-06T09:12:24+00:00" }, { - "name": "doctrine/instantiator", - "version": "1.4.1", + "name": "doctrine/deprecations", + "version": "1.1.3", "source": { "type": "git", - "url": "https://github.com/doctrine/instantiator.git", - "reference": "10dcfce151b967d20fde1b34ae6640712c3891bc" + "url": "https://github.com/doctrine/deprecations.git", + "reference": "dfbaa3c2d2e9a9df1118213f3b8b0c597bb99fab" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/10dcfce151b967d20fde1b34ae6640712c3891bc", - "reference": "10dcfce151b967d20fde1b34ae6640712c3891bc", + "url": "https://api.github.com/repos/doctrine/deprecations/zipball/dfbaa3c2d2e9a9df1118213f3b8b0c597bb99fab", + "reference": "dfbaa3c2d2e9a9df1118213f3b8b0c597bb99fab", "shasum": "" }, "require": { @@ -650,13 +648,60 @@ }, "require-dev": { "doctrine/coding-standard": "^9", + "phpstan/phpstan": "1.4.10 || 1.10.15", + "phpstan/phpstan-phpunit": "^1.0", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "psalm/plugin-phpunit": "0.18.4", + "psr/log": "^1 || ^2 || ^3", + "vimeo/psalm": "4.30.0 || 5.12.0" + }, + "suggest": { + "psr/log": "Allows logging deprecations via PSR-3 logger implementation" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Deprecations\\": "lib/Doctrine/Deprecations" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.", + "homepage": "https://www.doctrine-project.org/", + "support": { + "issues": "https://github.com/doctrine/deprecations/issues", + "source": "https://github.com/doctrine/deprecations/tree/1.1.3" + }, + "time": "2024-01-30T19:34:25+00:00" + }, + { + "name": "doctrine/instantiator", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/instantiator.git", + "reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/c6222283fa3f4ac679f8b9ced9a4e23f163e80d0", + "reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0", + "shasum": "" + }, + "require": { + "php": "^8.1" + }, + "require-dev": { + "doctrine/coding-standard": "^11", "ext-pdo": "*", "ext-phar": "*", - "phpbench/phpbench": "^0.16 || ^1", - "phpstan/phpstan": "^1.4", - "phpstan/phpstan-phpunit": "^1", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", - "vimeo/psalm": "^4.22" + "phpbench/phpbench": "^1.2", + "phpstan/phpstan": "^1.9.4", + "phpstan/phpstan-phpunit": "^1.3", + "phpunit/phpunit": "^9.5.27", + "vimeo/psalm": "^5.4" }, "type": "library", "autoload": { @@ -683,7 +728,7 @@ ], "support": { "issues": "https://github.com/doctrine/instantiator/issues", - "source": "https://github.com/doctrine/instantiator/tree/1.4.1" + "source": "https://github.com/doctrine/instantiator/tree/2.0.0" }, "funding": [ { @@ -699,7 +744,7 @@ "type": "tidelift" } ], - "time": "2022-03-03T08:28:38+00:00" + "time": "2022-12-30T00:23:10+00:00" }, { "name": "felixfbecker/advanced-json-rpc", @@ -748,16 +793,16 @@ }, { "name": "felixfbecker/language-server-protocol", - "version": "1.5.1", + "version": "v1.5.2", "source": { "type": "git", "url": "https://github.com/felixfbecker/php-language-server-protocol.git", - "reference": "9d846d1f5cf101deee7a61c8ba7caa0a975cd730" + "reference": "6e82196ffd7c62f7794d778ca52b69feec9f2842" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/felixfbecker/php-language-server-protocol/zipball/9d846d1f5cf101deee7a61c8ba7caa0a975cd730", - "reference": "9d846d1f5cf101deee7a61c8ba7caa0a975cd730", + "url": "https://api.github.com/repos/felixfbecker/php-language-server-protocol/zipball/6e82196ffd7c62f7794d778ca52b69feec9f2842", + "reference": "6e82196ffd7c62f7794d778ca52b69feec9f2842", "shasum": "" }, "require": { @@ -798,22 +843,22 @@ ], "support": { "issues": "https://github.com/felixfbecker/php-language-server-protocol/issues", - "source": "https://github.com/felixfbecker/php-language-server-protocol/tree/1.5.1" + "source": "https://github.com/felixfbecker/php-language-server-protocol/tree/v1.5.2" }, - "time": "2021-02-22T14:02:09+00:00" + "time": "2022-03-02T22:36:06+00:00" }, { "name": "myclabs/deep-copy", - "version": "1.11.0", + "version": "1.11.1", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "14daed4296fae74d9e3201d2c4925d1acb7aa614" + "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/14daed4296fae74d9e3201d2c4925d1acb7aa614", - "reference": "14daed4296fae74d9e3201d2c4925d1acb7aa614", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", + "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", "shasum": "" }, "require": { @@ -851,7 +896,7 @@ ], "support": { "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.11.0" + "source": "https://github.com/myclabs/DeepCopy/tree/1.11.1" }, "funding": [ { @@ -859,20 +904,20 @@ "type": "tidelift" } ], - "time": "2022-03-03T13:19:32+00:00" + "time": "2023-03-08T13:26:56+00:00" }, { "name": "netresearch/jsonmapper", - "version": "v4.0.0", + "version": "v4.4.1", "source": { "type": "git", "url": "https://github.com/cweiske/jsonmapper.git", - "reference": "8bbc021a8edb2e4a7ea2f8ad4fa9ec9dce2fcb8d" + "reference": "132c75c7dd83e45353ebb9c6c9f591952995bbf0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/cweiske/jsonmapper/zipball/8bbc021a8edb2e4a7ea2f8ad4fa9ec9dce2fcb8d", - "reference": "8bbc021a8edb2e4a7ea2f8ad4fa9ec9dce2fcb8d", + "url": "https://api.github.com/repos/cweiske/jsonmapper/zipball/132c75c7dd83e45353ebb9c6c9f591952995bbf0", + "reference": "132c75c7dd83e45353ebb9c6c9f591952995bbf0", "shasum": "" }, "require": { @@ -883,7 +928,7 @@ "php": ">=7.1" }, "require-dev": { - "phpunit/phpunit": "~7.5 || ~8.0 || ~9.0", + "phpunit/phpunit": "~7.5 || ~8.0 || ~9.0 || ~10.0", "squizlabs/php_codesniffer": "~3.5" }, "type": "library", @@ -908,27 +953,27 @@ "support": { "email": "cweiske@cweiske.de", "issues": "https://github.com/cweiske/jsonmapper/issues", - "source": "https://github.com/cweiske/jsonmapper/tree/v4.0.0" + "source": "https://github.com/cweiske/jsonmapper/tree/v4.4.1" }, - "time": "2020-12-01T19:48:11+00:00" + "time": "2024-01-31T06:18:54+00:00" }, { "name": "nikic/php-parser", - "version": "v4.13.2", + "version": "v4.19.1", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "210577fe3cf7badcc5814d99455df46564f3c077" + "reference": "4e1b88d21c69391150ace211e9eaf05810858d0b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/210577fe3cf7badcc5814d99455df46564f3c077", - "reference": "210577fe3cf7badcc5814d99455df46564f3c077", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/4e1b88d21c69391150ace211e9eaf05810858d0b", + "reference": "4e1b88d21c69391150ace211e9eaf05810858d0b", "shasum": "" }, "require": { "ext-tokenizer": "*", - "php": ">=7.0" + "php": ">=7.1" }, "require-dev": { "ircmaxell/php-yacc": "^0.0.7", @@ -964,9 +1009,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v4.13.2" + "source": "https://github.com/nikic/PHP-Parser/tree/v4.19.1" }, - "time": "2021-11-30T19:35:32+00:00" + "time": "2024-03-17T08:10:35+00:00" }, { "name": "openlss/lib-array2xml", @@ -1023,28 +1068,28 @@ }, { "name": "pdepend/pdepend", - "version": "2.10.3", + "version": "2.16.2", "source": { "type": "git", "url": "https://github.com/pdepend/pdepend.git", - "reference": "da3166a06b4a89915920a42444f707122a1584c9" + "reference": "f942b208dc2a0868454d01b29f0c75bbcfc6ed58" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pdepend/pdepend/zipball/da3166a06b4a89915920a42444f707122a1584c9", - "reference": "da3166a06b4a89915920a42444f707122a1584c9", + "url": "https://api.github.com/repos/pdepend/pdepend/zipball/f942b208dc2a0868454d01b29f0c75bbcfc6ed58", + "reference": "f942b208dc2a0868454d01b29f0c75bbcfc6ed58", "shasum": "" }, "require": { "php": ">=5.3.7", - "symfony/config": "^2.3.0|^3|^4|^5|^6.0", - "symfony/dependency-injection": "^2.3.0|^3|^4|^5|^6.0", - "symfony/filesystem": "^2.3.0|^3|^4|^5|^6.0" + "symfony/config": "^2.3.0|^3|^4|^5|^6.0|^7.0", + "symfony/dependency-injection": "^2.3.0|^3|^4|^5|^6.0|^7.0", + "symfony/filesystem": "^2.3.0|^3|^4|^5|^6.0|^7.0", + "symfony/polyfill-mbstring": "^1.19" }, "require-dev": { "easy-doc/easy-doc": "0.0.0|^1.2.3", "gregwar/rst": "^1.0", - "phpunit/phpunit": "^4.8.36|^5.7.27", "squizlabs/php_codesniffer": "^2.0.0" }, "bin": [ @@ -1066,9 +1111,15 @@ "BSD-3-Clause" ], "description": "Official version of pdepend to be handled with Composer", + "keywords": [ + "PHP Depend", + "PHP_Depend", + "dev", + "pdepend" + ], "support": { "issues": "https://github.com/pdepend/pdepend/issues", - "source": "https://github.com/pdepend/pdepend/tree/2.10.3" + "source": "https://github.com/pdepend/pdepend/tree/2.16.2" }, "funding": [ { @@ -1076,24 +1127,25 @@ "type": "tidelift" } ], - "time": "2022-02-23T07:53:09+00:00" + "time": "2023-12-17T18:09:59+00:00" }, { "name": "phar-io/manifest", - "version": "2.0.3", + "version": "2.0.4", "source": { "type": "git", "url": "https://github.com/phar-io/manifest.git", - "reference": "97803eca37d319dfa7826cc2437fc020857acb53" + "reference": "54750ef60c58e43759730615a392c31c80e23176" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phar-io/manifest/zipball/97803eca37d319dfa7826cc2437fc020857acb53", - "reference": "97803eca37d319dfa7826cc2437fc020857acb53", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/54750ef60c58e43759730615a392c31c80e23176", + "reference": "54750ef60c58e43759730615a392c31c80e23176", "shasum": "" }, "require": { "ext-dom": "*", + "ext-libxml": "*", "ext-phar": "*", "ext-xmlwriter": "*", "phar-io/version": "^3.0.1", @@ -1134,9 +1186,15 @@ "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", "support": { "issues": "https://github.com/phar-io/manifest/issues", - "source": "https://github.com/phar-io/manifest/tree/2.0.3" + "source": "https://github.com/phar-io/manifest/tree/2.0.4" }, - "time": "2021-07-20T11:28:43+00:00" + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2024-03-03T12:33:53+00:00" }, { "name": "phar-io/version", @@ -1244,28 +1302,35 @@ }, { "name": "phpdocumentor/reflection-docblock", - "version": "5.3.0", + "version": "5.4.1", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "622548b623e81ca6d78b721c5e029f4ce664f170" + "reference": "9d07b3f7fdcf5efec5d1609cba3c19c5ea2bdc9c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/622548b623e81ca6d78b721c5e029f4ce664f170", - "reference": "622548b623e81ca6d78b721c5e029f4ce664f170", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/9d07b3f7fdcf5efec5d1609cba3c19c5ea2bdc9c", + "reference": "9d07b3f7fdcf5efec5d1609cba3c19c5ea2bdc9c", "shasum": "" }, "require": { + "doctrine/deprecations": "^1.1", "ext-filter": "*", - "php": "^7.2 || ^8.0", + "php": "^7.4 || ^8.0", "phpdocumentor/reflection-common": "^2.2", - "phpdocumentor/type-resolver": "^1.3", + "phpdocumentor/type-resolver": "^1.7", + "phpstan/phpdoc-parser": "^1.7", "webmozart/assert": "^1.9.1" }, "require-dev": { - "mockery/mockery": "~1.3.2", - "psalm/phar": "^4.8" + "mockery/mockery": "~1.3.5", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-mockery": "^1.1", + "phpstan/phpstan-webmozart-assert": "^1.2", + "phpunit/phpunit": "^9.5", + "vimeo/psalm": "^5.13" }, "type": "library", "extra": { @@ -1289,37 +1354,45 @@ }, { "name": "Jaap van Otterdijk", - "email": "account@ijaap.nl" + "email": "opensource@ijaap.nl" } ], "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", "support": { "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", - "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.3.0" + "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.4.1" }, - "time": "2021-10-19T17:43:47+00:00" + "time": "2024-05-21T05:55:05+00:00" }, { "name": "phpdocumentor/type-resolver", - "version": "1.6.0", + "version": "1.8.2", "source": { "type": "git", "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "93ebd0014cab80c4ea9f5e297ea48672f1b87706" + "reference": "153ae662783729388a584b4361f2545e4d841e3c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/93ebd0014cab80c4ea9f5e297ea48672f1b87706", - "reference": "93ebd0014cab80c4ea9f5e297ea48672f1b87706", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/153ae662783729388a584b4361f2545e4d841e3c", + "reference": "153ae662783729388a584b4361f2545e4d841e3c", "shasum": "" }, "require": { - "php": "^7.2 || ^8.0", - "phpdocumentor/reflection-common": "^2.0" + "doctrine/deprecations": "^1.0", + "php": "^7.3 || ^8.0", + "phpdocumentor/reflection-common": "^2.0", + "phpstan/phpdoc-parser": "^1.13" }, "require-dev": { "ext-tokenizer": "*", - "psalm/phar": "^4.8" + "phpbench/phpbench": "^1.2", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-phpunit": "^1.1", + "phpunit/phpunit": "^9.5", + "rector/rector": "^0.13.9", + "vimeo/psalm": "^4.25" }, "type": "library", "extra": { @@ -1345,28 +1418,28 @@ "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", "support": { "issues": "https://github.com/phpDocumentor/TypeResolver/issues", - "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.6.0" + "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.8.2" }, - "time": "2022-01-04T19:58:01+00:00" + "time": "2024-02-23T11:10:43+00:00" }, { "name": "phpmd/phpmd", - "version": "2.11.1", + "version": "2.15.0", "source": { "type": "git", "url": "https://github.com/phpmd/phpmd.git", - "reference": "08b60a2eb7e14c23f46ff8865b510ae08b75d0fd" + "reference": "74a1f56e33afad4128b886e334093e98e1b5e7c0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpmd/phpmd/zipball/08b60a2eb7e14c23f46ff8865b510ae08b75d0fd", - "reference": "08b60a2eb7e14c23f46ff8865b510ae08b75d0fd", + "url": "https://api.github.com/repos/phpmd/phpmd/zipball/74a1f56e33afad4128b886e334093e98e1b5e7c0", + "reference": "74a1f56e33afad4128b886e334093e98e1b5e7c0", "shasum": "" }, "require": { - "composer/xdebug-handler": "^1.0 || ^2.0", + "composer/xdebug-handler": "^1.0 || ^2.0 || ^3.0", "ext-xml": "*", - "pdepend/pdepend": "^2.10.2", + "pdepend/pdepend": "^2.16.1", "php": ">=5.3.9" }, "require-dev": { @@ -1375,8 +1448,7 @@ "ext-simplexml": "*", "gregwar/rst": "^1.0", "mikey179/vfsstream": "^1.6.8", - "phpunit/phpunit": "^4.8.36 || ^5.7.27", - "squizlabs/php_codesniffer": "^2.0" + "squizlabs/php_codesniffer": "^2.9.2 || ^3.7.2" }, "bin": [ "src/bin/phpmd" @@ -1413,6 +1485,7 @@ "description": "PHPMD is a spin-off project of PHP Depend and aims to be a PHP equivalent of the well known Java tool PMD.", "homepage": "https://phpmd.org/", "keywords": [ + "dev", "mess detection", "mess detector", "pdepend", @@ -1422,7 +1495,7 @@ "support": { "irc": "irc://irc.freenode.org/phpmd", "issues": "https://github.com/phpmd/phpmd/issues", - "source": "https://github.com/phpmd/phpmd/tree/2.11.1" + "source": "https://github.com/phpmd/phpmd/tree/2.15.0" }, "funding": [ { @@ -1430,20 +1503,20 @@ "type": "tidelift" } ], - "time": "2021-12-17T11:25:43+00:00" + "time": "2023-12-11T08:22:20+00:00" }, { "name": "phpmetrics/phpmetrics", - "version": "v2.7.4", + "version": "v2.8.2", "source": { "type": "git", "url": "https://github.com/phpmetrics/PhpMetrics.git", - "reference": "e6a7aee0e0948e363eb78ce9d58573cd5af2cdec" + "reference": "4b77140a11452e63c7a9b98e0648320bf6710090" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpmetrics/PhpMetrics/zipball/e6a7aee0e0948e363eb78ce9d58573cd5af2cdec", - "reference": "e6a7aee0e0948e363eb78ce9d58573cd5af2cdec", + "url": "https://api.github.com/repos/phpmetrics/PhpMetrics/zipball/4b77140a11452e63c7a9b98e0648320bf6710090", + "reference": "4b77140a11452e63c7a9b98e0648320bf6710090", "shasum": "" }, "require": { @@ -1459,7 +1532,8 @@ "require-dev": { "phpunit/phpunit": "^4.8.36 || ^5.7.27 || ^6.5.14", "sebastian/comparator": ">=1.2.3", - "squizlabs/php_codesniffer": "^3.5" + "squizlabs/php_codesniffer": "^3.5", + "symfony/dom-crawler": "^3.0 || ^4.0 || ^5.0" }, "bin": [ "bin/phpmetrics" @@ -1495,112 +1569,39 @@ ], "support": { "issues": "https://github.com/PhpMetrics/PhpMetrics/issues", - "source": "https://github.com/phpmetrics/PhpMetrics/tree/master" + "source": "https://github.com/phpmetrics/PhpMetrics/tree/v2.8.2" }, - "time": "2020-06-30T20:33:55+00:00" - }, - { - "name": "phpspec/prophecy", - "version": "v1.15.0", - "source": { - "type": "git", - "url": "https://github.com/phpspec/prophecy.git", - "reference": "bbcd7380b0ebf3961ee21409db7b38bc31d69a13" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/bbcd7380b0ebf3961ee21409db7b38bc31d69a13", - "reference": "bbcd7380b0ebf3961ee21409db7b38bc31d69a13", - "shasum": "" - }, - "require": { - "doctrine/instantiator": "^1.2", - "php": "^7.2 || ~8.0, <8.2", - "phpdocumentor/reflection-docblock": "^5.2", - "sebastian/comparator": "^3.0 || ^4.0", - "sebastian/recursion-context": "^3.0 || ^4.0" - }, - "require-dev": { - "phpspec/phpspec": "^6.0 || ^7.0", - "phpunit/phpunit": "^8.0 || ^9.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Prophecy\\": "src/Prophecy" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Konstantin Kudryashov", - "email": "ever.zet@gmail.com", - "homepage": "http://everzet.com" - }, - { - "name": "Marcello Duarte", - "email": "marcello.duarte@gmail.com" - } - ], - "description": "Highly opinionated mocking framework for PHP 5.3+", - "homepage": "https://github.com/phpspec/prophecy", - "keywords": [ - "Double", - "Dummy", - "fake", - "mock", - "spy", - "stub" - ], - "support": { - "issues": "https://github.com/phpspec/prophecy/issues", - "source": "https://github.com/phpspec/prophecy/tree/v1.15.0" - }, - "time": "2021-12-08T12:19:24+00:00" + "time": "2023-03-08T15:03:36+00:00" }, { "name": "phpstan/phpdoc-parser", - "version": "0.4.9", + "version": "1.29.1", "source": { "type": "git", "url": "https://github.com/phpstan/phpdoc-parser.git", - "reference": "98a088b17966bdf6ee25c8a4b634df313d8aa531" + "reference": "fcaefacf2d5c417e928405b71b400d4ce10daaf4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/98a088b17966bdf6ee25c8a4b634df313d8aa531", - "reference": "98a088b17966bdf6ee25c8a4b634df313d8aa531", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/fcaefacf2d5c417e928405b71b400d4ce10daaf4", + "reference": "fcaefacf2d5c417e928405b71b400d4ce10daaf4", "shasum": "" }, "require": { - "php": "^7.1 || ^8.0" + "php": "^7.2 || ^8.0" }, "require-dev": { - "consistence/coding-standard": "^3.5", - "ergebnis/composer-normalize": "^2.0.2", - "jakub-onderka/php-parallel-lint": "^0.9.2", - "phing/phing": "^2.16.0", + "doctrine/annotations": "^2.0", + "nikic/php-parser": "^4.15", + "php-parallel-lint/php-parallel-lint": "^1.2", "phpstan/extension-installer": "^1.0", - "phpstan/phpstan": "^0.12.26", - "phpstan/phpstan-strict-rules": "^0.12", - "phpunit/phpunit": "^6.3", - "slevomat/coding-standard": "^4.7.2", - "symfony/process": "^4.0" + "phpstan/phpstan": "^1.5", + "phpstan/phpstan-phpunit": "^1.1", + "phpstan/phpstan-strict-rules": "^1.0", + "phpunit/phpunit": "^9.5", + "symfony/process": "^5.2" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "0.4-dev" - } - }, "autoload": { "psr-4": { "PHPStan\\PhpDocParser\\": [ @@ -1615,26 +1616,26 @@ "description": "PHPDoc parser with support for nullable, intersection and generic types", "support": { "issues": "https://github.com/phpstan/phpdoc-parser/issues", - "source": "https://github.com/phpstan/phpdoc-parser/tree/master" + "source": "https://github.com/phpstan/phpdoc-parser/tree/1.29.1" }, - "time": "2020-08-03T20:32:43+00:00" + "time": "2024-05-31T08:52:43+00:00" }, { "name": "phpstan/phpstan", - "version": "0.12.99", + "version": "1.11.4", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "b4d40f1d759942f523be267a1bab6884f46ca3f7" + "reference": "9100a76ce8015b9aa7125b9171ae3a76887b6c82" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/b4d40f1d759942f523be267a1bab6884f46ca3f7", - "reference": "b4d40f1d759942f523be267a1bab6884f46ca3f7", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/9100a76ce8015b9aa7125b9171ae3a76887b6c82", + "reference": "9100a76ce8015b9aa7125b9171ae3a76887b6c82", "shasum": "" }, "require": { - "php": "^7.1|^8.0" + "php": "^7.2|^8.0" }, "conflict": { "phpstan/phpstan-shim": "*" @@ -1644,11 +1645,6 @@ "phpstan.phar" ], "type": "library", - "extra": { - "branch-alias": { - "dev-master": "0.12-dev" - } - }, "autoload": { "files": [ "bootstrap.php" @@ -1659,9 +1655,16 @@ "MIT" ], "description": "PHPStan - PHP Static Analysis Tool", + "keywords": [ + "dev", + "static analysis" + ], "support": { + "docs": "https://phpstan.org/user-guide/getting-started", + "forum": "https://github.com/phpstan/phpstan/discussions", "issues": "https://github.com/phpstan/phpstan/issues", - "source": "https://github.com/phpstan/phpstan/tree/0.12.99" + "security": "https://github.com/phpstan/phpstan/security/policy", + "source": "https://github.com/phpstan/phpstan-src" }, "funding": [ { @@ -1671,37 +1674,29 @@ { "url": "https://github.com/phpstan", "type": "github" - }, - { - "url": "https://www.patreon.com/phpstan", - "type": "patreon" - }, - { - "url": "https://tidelift.com/funding/github/packagist/phpstan/phpstan", - "type": "tidelift" } ], - "time": "2021-09-12T20:09:55+00:00" + "time": "2024-06-06T12:19:22+00:00" }, { "name": "phpunit/php-code-coverage", - "version": "9.2.15", + "version": "9.2.31", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "2e9da11878c4202f97915c1cb4bb1ca318a63f5f" + "reference": "48c34b5d8d983006bd2adc2d0de92963b9155965" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/2e9da11878c4202f97915c1cb4bb1ca318a63f5f", - "reference": "2e9da11878c4202f97915c1cb4bb1ca318a63f5f", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/48c34b5d8d983006bd2adc2d0de92963b9155965", + "reference": "48c34b5d8d983006bd2adc2d0de92963b9155965", "shasum": "" }, "require": { "ext-dom": "*", "ext-libxml": "*", "ext-xmlwriter": "*", - "nikic/php-parser": "^4.13.0", + "nikic/php-parser": "^4.18 || ^5.0", "php": ">=7.3", "phpunit/php-file-iterator": "^3.0.3", "phpunit/php-text-template": "^2.0.2", @@ -1716,8 +1711,8 @@ "phpunit/phpunit": "^9.3" }, "suggest": { - "ext-pcov": "*", - "ext-xdebug": "*" + "ext-pcov": "PHP extension that provides line coverage", + "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" }, "type": "library", "extra": { @@ -1750,7 +1745,8 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.15" + "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.31" }, "funding": [ { @@ -1758,7 +1754,7 @@ "type": "github" } ], - "time": "2022-03-07T09:28:20+00:00" + "time": "2024-03-02T06:37:42+00:00" }, { "name": "phpunit/php-file-iterator", @@ -2003,20 +1999,20 @@ }, { "name": "phpunit/phpunit", - "version": "9.5.17", + "version": "9.6.19", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "5c5abcfaa2cbd44b2203995d7a339ef910fe0c8f" + "reference": "a1a54a473501ef4cdeaae4e06891674114d79db8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/5c5abcfaa2cbd44b2203995d7a339ef910fe0c8f", - "reference": "5c5abcfaa2cbd44b2203995d7a339ef910fe0c8f", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/a1a54a473501ef4cdeaae4e06891674114d79db8", + "reference": "a1a54a473501ef4cdeaae4e06891674114d79db8", "shasum": "" }, "require": { - "doctrine/instantiator": "^1.3.1", + "doctrine/instantiator": "^1.3.1 || ^2", "ext-dom": "*", "ext-json": "*", "ext-libxml": "*", @@ -2027,31 +2023,26 @@ "phar-io/manifest": "^2.0.3", "phar-io/version": "^3.0.2", "php": ">=7.3", - "phpspec/prophecy": "^1.12.1", - "phpunit/php-code-coverage": "^9.2.13", + "phpunit/php-code-coverage": "^9.2.28", "phpunit/php-file-iterator": "^3.0.5", "phpunit/php-invoker": "^3.1.1", "phpunit/php-text-template": "^2.0.3", "phpunit/php-timer": "^5.0.2", "sebastian/cli-parser": "^1.0.1", "sebastian/code-unit": "^1.0.6", - "sebastian/comparator": "^4.0.5", + "sebastian/comparator": "^4.0.8", "sebastian/diff": "^4.0.3", "sebastian/environment": "^5.1.3", - "sebastian/exporter": "^4.0.3", + "sebastian/exporter": "^4.0.5", "sebastian/global-state": "^5.0.1", "sebastian/object-enumerator": "^4.0.3", "sebastian/resource-operations": "^3.0.3", - "sebastian/type": "^2.3.4", + "sebastian/type": "^3.2", "sebastian/version": "^3.0.2" }, - "require-dev": { - "ext-pdo": "*", - "phpspec/prophecy-phpunit": "^2.0.1" - }, "suggest": { - "ext-soap": "*", - "ext-xdebug": "*" + "ext-soap": "To be able to generate mocks based on WSDL files", + "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" }, "bin": [ "phpunit" @@ -2059,7 +2050,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "9.5-dev" + "dev-master": "9.6-dev" } }, "autoload": { @@ -2090,7 +2081,8 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", - "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.17" + "security": "https://github.com/sebastianbergmann/phpunit/security/policy", + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.19" }, "funding": [ { @@ -2100,9 +2092,13 @@ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", + "type": "tidelift" } ], - "time": "2022-03-05T16:54:31+00:00" + "time": "2024-04-05T04:35:58+00:00" }, { "name": "psalm/plugin-phpunit", @@ -2265,16 +2261,16 @@ }, { "name": "sebastian/cli-parser", - "version": "1.0.1", + "version": "1.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/cli-parser.git", - "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2" + "reference": "2b56bea83a09de3ac06bb18b92f068e60cc6f50b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/442e7c7e687e42adc03470c7b668bc4b2402c0b2", - "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/2b56bea83a09de3ac06bb18b92f068e60cc6f50b", + "reference": "2b56bea83a09de3ac06bb18b92f068e60cc6f50b", "shasum": "" }, "require": { @@ -2309,7 +2305,7 @@ "homepage": "https://github.com/sebastianbergmann/cli-parser", "support": { "issues": "https://github.com/sebastianbergmann/cli-parser/issues", - "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.1" + "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.2" }, "funding": [ { @@ -2317,7 +2313,7 @@ "type": "github" } ], - "time": "2020-09-28T06:08:49+00:00" + "time": "2024-03-02T06:27:43+00:00" }, { "name": "sebastian/code-unit", @@ -2432,16 +2428,16 @@ }, { "name": "sebastian/comparator", - "version": "4.0.6", + "version": "4.0.8", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "55f4261989e546dc112258c7a75935a81a7ce382" + "reference": "fa0f136dd2334583309d32b62544682ee972b51a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/55f4261989e546dc112258c7a75935a81a7ce382", - "reference": "55f4261989e546dc112258c7a75935a81a7ce382", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/fa0f136dd2334583309d32b62544682ee972b51a", + "reference": "fa0f136dd2334583309d32b62544682ee972b51a", "shasum": "" }, "require": { @@ -2494,7 +2490,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/comparator/issues", - "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.6" + "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.8" }, "funding": [ { @@ -2502,24 +2498,24 @@ "type": "github" } ], - "time": "2020-10-26T15:49:45+00:00" + "time": "2022-09-14T12:41:17+00:00" }, { "name": "sebastian/complexity", - "version": "2.0.2", + "version": "2.0.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/complexity.git", - "reference": "739b35e53379900cc9ac327b2147867b8b6efd88" + "reference": "25f207c40d62b8b7aa32f5ab026c53561964053a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/739b35e53379900cc9ac327b2147867b8b6efd88", - "reference": "739b35e53379900cc9ac327b2147867b8b6efd88", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/25f207c40d62b8b7aa32f5ab026c53561964053a", + "reference": "25f207c40d62b8b7aa32f5ab026c53561964053a", "shasum": "" }, "require": { - "nikic/php-parser": "^4.7", + "nikic/php-parser": "^4.18 || ^5.0", "php": ">=7.3" }, "require-dev": { @@ -2551,7 +2547,7 @@ "homepage": "https://github.com/sebastianbergmann/complexity", "support": { "issues": "https://github.com/sebastianbergmann/complexity/issues", - "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.2" + "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.3" }, "funding": [ { @@ -2559,20 +2555,20 @@ "type": "github" } ], - "time": "2020-10-26T15:52:27+00:00" + "time": "2023-12-22T06:19:30+00:00" }, { "name": "sebastian/diff", - "version": "4.0.4", + "version": "4.0.6", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d" + "reference": "ba01945089c3a293b01ba9badc29ad55b106b0bc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/3461e3fccc7cfdfc2720be910d3bd73c69be590d", - "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/ba01945089c3a293b01ba9badc29ad55b106b0bc", + "reference": "ba01945089c3a293b01ba9badc29ad55b106b0bc", "shasum": "" }, "require": { @@ -2617,7 +2613,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/diff/issues", - "source": "https://github.com/sebastianbergmann/diff/tree/4.0.4" + "source": "https://github.com/sebastianbergmann/diff/tree/4.0.6" }, "funding": [ { @@ -2625,20 +2621,20 @@ "type": "github" } ], - "time": "2020-10-26T13:10:38+00:00" + "time": "2024-03-02T06:30:58+00:00" }, { "name": "sebastian/environment", - "version": "5.1.3", + "version": "5.1.5", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "388b6ced16caa751030f6a69e588299fa09200ac" + "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/388b6ced16caa751030f6a69e588299fa09200ac", - "reference": "388b6ced16caa751030f6a69e588299fa09200ac", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", + "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", "shasum": "" }, "require": { @@ -2680,7 +2676,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/environment/issues", - "source": "https://github.com/sebastianbergmann/environment/tree/5.1.3" + "source": "https://github.com/sebastianbergmann/environment/tree/5.1.5" }, "funding": [ { @@ -2688,20 +2684,20 @@ "type": "github" } ], - "time": "2020-09-28T05:52:38+00:00" + "time": "2023-02-03T06:03:51+00:00" }, { "name": "sebastian/exporter", - "version": "4.0.4", + "version": "4.0.6", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "65e8b7db476c5dd267e65eea9cab77584d3cfff9" + "reference": "78c00df8f170e02473b682df15bfcdacc3d32d72" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/65e8b7db476c5dd267e65eea9cab77584d3cfff9", - "reference": "65e8b7db476c5dd267e65eea9cab77584d3cfff9", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/78c00df8f170e02473b682df15bfcdacc3d32d72", + "reference": "78c00df8f170e02473b682df15bfcdacc3d32d72", "shasum": "" }, "require": { @@ -2757,7 +2753,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/exporter/issues", - "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.4" + "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.6" }, "funding": [ { @@ -2765,20 +2761,20 @@ "type": "github" } ], - "time": "2021-11-11T14:18:36+00:00" + "time": "2024-03-02T06:33:00+00:00" }, { "name": "sebastian/global-state", - "version": "5.0.5", + "version": "5.0.7", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "0ca8db5a5fc9c8646244e629625ac486fa286bf2" + "reference": "bca7df1f32ee6fe93b4d4a9abbf69e13a4ada2c9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/0ca8db5a5fc9c8646244e629625ac486fa286bf2", - "reference": "0ca8db5a5fc9c8646244e629625ac486fa286bf2", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bca7df1f32ee6fe93b4d4a9abbf69e13a4ada2c9", + "reference": "bca7df1f32ee6fe93b4d4a9abbf69e13a4ada2c9", "shasum": "" }, "require": { @@ -2821,7 +2817,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/global-state/issues", - "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.5" + "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.7" }, "funding": [ { @@ -2829,24 +2825,24 @@ "type": "github" } ], - "time": "2022-02-14T08:28:10+00:00" + "time": "2024-03-02T06:35:11+00:00" }, { "name": "sebastian/lines-of-code", - "version": "1.0.3", + "version": "1.0.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/lines-of-code.git", - "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc" + "reference": "e1e4a170560925c26d424b6a03aed157e7dcc5c5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/c1c2e997aa3146983ed888ad08b15470a2e22ecc", - "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/e1e4a170560925c26d424b6a03aed157e7dcc5c5", + "reference": "e1e4a170560925c26d424b6a03aed157e7dcc5c5", "shasum": "" }, "require": { - "nikic/php-parser": "^4.6", + "nikic/php-parser": "^4.18 || ^5.0", "php": ">=7.3" }, "require-dev": { @@ -2878,7 +2874,7 @@ "homepage": "https://github.com/sebastianbergmann/lines-of-code", "support": { "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", - "source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.3" + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.4" }, "funding": [ { @@ -2886,7 +2882,7 @@ "type": "github" } ], - "time": "2020-11-28T06:42:11+00:00" + "time": "2023-12-22T06:20:34+00:00" }, { "name": "sebastian/object-enumerator", @@ -3002,16 +2998,16 @@ }, { "name": "sebastian/recursion-context", - "version": "4.0.4", + "version": "4.0.5", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "cd9d8cf3c5804de4341c283ed787f099f5506172" + "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/cd9d8cf3c5804de4341c283ed787f099f5506172", - "reference": "cd9d8cf3c5804de4341c283ed787f099f5506172", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", + "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", "shasum": "" }, "require": { @@ -3050,10 +3046,10 @@ } ], "description": "Provides functionality to recursively process PHP variables", - "homepage": "http://www.github.com/sebastianbergmann/recursion-context", + "homepage": "https://github.com/sebastianbergmann/recursion-context", "support": { "issues": "https://github.com/sebastianbergmann/recursion-context/issues", - "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.4" + "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.5" }, "funding": [ { @@ -3061,20 +3057,20 @@ "type": "github" } ], - "time": "2020-10-26T13:17:30+00:00" + "time": "2023-02-03T06:07:39+00:00" }, { "name": "sebastian/resource-operations", - "version": "3.0.3", + "version": "3.0.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/resource-operations.git", - "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8" + "reference": "05d5692a7993ecccd56a03e40cd7e5b09b1d404e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", - "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", + "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/05d5692a7993ecccd56a03e40cd7e5b09b1d404e", + "reference": "05d5692a7993ecccd56a03e40cd7e5b09b1d404e", "shasum": "" }, "require": { @@ -3086,7 +3082,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-main": "3.0-dev" } }, "autoload": { @@ -3107,8 +3103,7 @@ "description": "Provides a list of PHP built-in functions that operate on resources", "homepage": "https://www.github.com/sebastianbergmann/resource-operations", "support": { - "issues": "https://github.com/sebastianbergmann/resource-operations/issues", - "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.3" + "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.4" }, "funding": [ { @@ -3116,32 +3111,32 @@ "type": "github" } ], - "time": "2020-09-28T06:45:17+00:00" + "time": "2024-03-14T16:00:52+00:00" }, { "name": "sebastian/type", - "version": "2.3.4", + "version": "3.2.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/type.git", - "reference": "b8cd8a1c753c90bc1a0f5372170e3e489136f914" + "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/b8cd8a1c753c90bc1a0f5372170e3e489136f914", - "reference": "b8cd8a1c753c90bc1a0f5372170e3e489136f914", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", + "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", "shasum": "" }, "require": { "php": ">=7.3" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^9.5" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.3-dev" + "dev-master": "3.2-dev" } }, "autoload": { @@ -3164,7 +3159,7 @@ "homepage": "https://github.com/sebastianbergmann/type", "support": { "issues": "https://github.com/sebastianbergmann/type/issues", - "source": "https://github.com/sebastianbergmann/type/tree/2.3.4" + "source": "https://github.com/sebastianbergmann/type/tree/3.2.1" }, "funding": [ { @@ -3172,7 +3167,7 @@ "type": "github" } ], - "time": "2021-06-15T12:49:02+00:00" + "time": "2023-02-03T06:13:03+00:00" }, { "name": "sebastian/version", @@ -3229,42 +3224,42 @@ }, { "name": "slevomat/coding-standard", - "version": "6.4.1", + "version": "8.15.0", "source": { "type": "git", "url": "https://github.com/slevomat/coding-standard.git", - "reference": "696dcca217d0c9da2c40d02731526c1e25b65346" + "reference": "7d1d957421618a3803b593ec31ace470177d7817" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/slevomat/coding-standard/zipball/696dcca217d0c9da2c40d02731526c1e25b65346", - "reference": "696dcca217d0c9da2c40d02731526c1e25b65346", + "url": "https://api.github.com/repos/slevomat/coding-standard/zipball/7d1d957421618a3803b593ec31ace470177d7817", + "reference": "7d1d957421618a3803b593ec31ace470177d7817", "shasum": "" }, "require": { - "dealerdirect/phpcodesniffer-composer-installer": "^0.6.2 || ^0.7", - "php": "^7.1 || ^8.0", - "phpstan/phpdoc-parser": "0.4.5 - 0.4.9", - "squizlabs/php_codesniffer": "^3.5.6" + "dealerdirect/phpcodesniffer-composer-installer": "^0.6.2 || ^0.7 || ^1.0", + "php": "^7.2 || ^8.0", + "phpstan/phpdoc-parser": "^1.23.1", + "squizlabs/php_codesniffer": "^3.9.0" }, "require-dev": { - "phing/phing": "2.16.3", - "php-parallel-lint/php-parallel-lint": "1.2.0", - "phpstan/phpstan": "0.12.48", - "phpstan/phpstan-deprecation-rules": "0.12.5", - "phpstan/phpstan-phpunit": "0.12.16", - "phpstan/phpstan-strict-rules": "0.12.5", - "phpunit/phpunit": "7.5.20|8.5.5|9.4.0" + "phing/phing": "2.17.4", + "php-parallel-lint/php-parallel-lint": "1.3.2", + "phpstan/phpstan": "1.10.60", + "phpstan/phpstan-deprecation-rules": "1.1.4", + "phpstan/phpstan-phpunit": "1.3.16", + "phpstan/phpstan-strict-rules": "1.5.2", + "phpunit/phpunit": "8.5.21|9.6.8|10.5.11" }, "type": "phpcodesniffer-standard", "extra": { "branch-alias": { - "dev-master": "6.x-dev" + "dev-master": "8.x-dev" } }, "autoload": { "psr-4": { - "SlevomatCodingStandard\\": "SlevomatCodingStandard" + "SlevomatCodingStandard\\": "SlevomatCodingStandard/" } }, "notification-url": "https://packagist.org/downloads/", @@ -3272,9 +3267,13 @@ "MIT" ], "description": "Slevomat Coding Standard for PHP_CodeSniffer complements Consistence Coding Standard by providing sniffs with additional checks.", + "keywords": [ + "dev", + "phpcs" + ], "support": { "issues": "https://github.com/slevomat/coding-standard/issues", - "source": "https://github.com/slevomat/coding-standard/tree/6.4.1" + "source": "https://github.com/slevomat/coding-standard/tree/8.15.0" }, "funding": [ { @@ -3286,20 +3285,20 @@ "type": "tidelift" } ], - "time": "2020-10-05T12:39:37+00:00" + "time": "2024-03-09T15:20:58+00:00" }, { "name": "squizlabs/php_codesniffer", - "version": "3.6.2", + "version": "3.10.1", "source": { "type": "git", - "url": "https://github.com/squizlabs/PHP_CodeSniffer.git", - "reference": "5e4e71592f69da17871dba6e80dd51bce74a351a" + "url": "https://github.com/PHPCSStandards/PHP_CodeSniffer.git", + "reference": "8f90f7a53ce271935282967f53d0894f8f1ff877" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/5e4e71592f69da17871dba6e80dd51bce74a351a", - "reference": "5e4e71592f69da17871dba6e80dd51bce74a351a", + "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/8f90f7a53ce271935282967f53d0894f8f1ff877", + "reference": "8f90f7a53ce271935282967f53d0894f8f1ff877", "shasum": "" }, "require": { @@ -3309,11 +3308,11 @@ "php": ">=5.4.0" }, "require-dev": { - "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0" + "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.3.4" }, "bin": [ - "bin/phpcs", - "bin/phpcbf" + "bin/phpcbf", + "bin/phpcs" ], "type": "library", "extra": { @@ -3328,55 +3327,76 @@ "authors": [ { "name": "Greg Sherwood", - "role": "lead" + "role": "Former lead" + }, + { + "name": "Juliette Reinders Folmer", + "role": "Current lead" + }, + { + "name": "Contributors", + "homepage": "https://github.com/PHPCSStandards/PHP_CodeSniffer/graphs/contributors" } ], "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.", - "homepage": "https://github.com/squizlabs/PHP_CodeSniffer", + "homepage": "https://github.com/PHPCSStandards/PHP_CodeSniffer", "keywords": [ "phpcs", - "standards" + "standards", + "static analysis" ], "support": { - "issues": "https://github.com/squizlabs/PHP_CodeSniffer/issues", - "source": "https://github.com/squizlabs/PHP_CodeSniffer", - "wiki": "https://github.com/squizlabs/PHP_CodeSniffer/wiki" + "issues": "https://github.com/PHPCSStandards/PHP_CodeSniffer/issues", + "security": "https://github.com/PHPCSStandards/PHP_CodeSniffer/security/policy", + "source": "https://github.com/PHPCSStandards/PHP_CodeSniffer", + "wiki": "https://github.com/PHPCSStandards/PHP_CodeSniffer/wiki" }, - "time": "2021-12-12T21:44:58+00:00" + "funding": [ + { + "url": "https://github.com/PHPCSStandards", + "type": "github" + }, + { + "url": "https://github.com/jrfnl", + "type": "github" + }, + { + "url": "https://opencollective.com/php_codesniffer", + "type": "open_collective" + } + ], + "time": "2024-05-22T21:24:41+00:00" }, { "name": "symfony/config", - "version": "v6.0.3", + "version": "v7.1.1", "source": { "type": "git", "url": "https://github.com/symfony/config.git", - "reference": "c14f32ae4cd2a3c29d8825c5093463ac08ade7d8" + "reference": "2210fc99fa42a259eb6c89d1f724ce0c4d62d5d2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/config/zipball/c14f32ae4cd2a3c29d8825c5093463ac08ade7d8", - "reference": "c14f32ae4cd2a3c29d8825c5093463ac08ade7d8", + "url": "https://api.github.com/repos/symfony/config/zipball/2210fc99fa42a259eb6c89d1f724ce0c4d62d5d2", + "reference": "2210fc99fa42a259eb6c89d1f724ce0c4d62d5d2", "shasum": "" }, "require": { - "php": ">=8.0.2", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/filesystem": "^5.4|^6.0", - "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-php81": "^1.22" + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/filesystem": "^7.1", + "symfony/polyfill-ctype": "~1.8" }, "conflict": { - "symfony/finder": "<4.4" + "symfony/finder": "<6.4", + "symfony/service-contracts": "<2.5" }, "require-dev": { - "symfony/event-dispatcher": "^5.4|^6.0", - "symfony/finder": "^5.4|^6.0", - "symfony/messenger": "^5.4|^6.0", - "symfony/service-contracts": "^1.1|^2|^3", - "symfony/yaml": "^5.4|^6.0" - }, - "suggest": { - "symfony/yaml": "To use the yaml reference dumper" + "symfony/event-dispatcher": "^6.4|^7.0", + "symfony/finder": "^6.4|^7.0", + "symfony/messenger": "^6.4|^7.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/yaml": "^6.4|^7.0" }, "type": "library", "autoload": { @@ -3404,7 +3424,7 @@ "description": "Helps you find, load, combine, autofill and validate configuration values of any kind", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/config/tree/v6.0.3" + "source": "https://github.com/symfony/config/tree/v7.1.1" }, "funding": [ { @@ -3420,27 +3440,28 @@ "type": "tidelift" } ], - "time": "2022-01-03T09:53:43+00:00" + "time": "2024-05-31T14:57:53+00:00" }, { "name": "symfony/console", - "version": "v6.0.5", + "version": "v6.4.8", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "3bebf4108b9e07492a2a4057d207aa5a77d146b1" + "reference": "be5854cee0e8c7b110f00d695d11debdfa1a2a91" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/3bebf4108b9e07492a2a4057d207aa5a77d146b1", - "reference": "3bebf4108b9e07492a2a4057d207aa5a77d146b1", + "url": "https://api.github.com/repos/symfony/console/zipball/be5854cee0e8c7b110f00d695d11debdfa1a2a91", + "reference": "be5854cee0e8c7b110f00d695d11debdfa1a2a91", "shasum": "" }, "require": { - "php": ">=8.0.2", + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-mbstring": "~1.0", - "symfony/service-contracts": "^1.1|^2|^3", - "symfony/string": "^5.4|^6.0" + "symfony/service-contracts": "^2.5|^3", + "symfony/string": "^5.4|^6.0|^7.0" }, "conflict": { "symfony/dependency-injection": "<5.4", @@ -3454,18 +3475,16 @@ }, "require-dev": { "psr/log": "^1|^2|^3", - "symfony/config": "^5.4|^6.0", - "symfony/dependency-injection": "^5.4|^6.0", - "symfony/event-dispatcher": "^5.4|^6.0", - "symfony/lock": "^5.4|^6.0", - "symfony/process": "^5.4|^6.0", - "symfony/var-dumper": "^5.4|^6.0" - }, - "suggest": { - "psr/log": "For using the console logger", - "symfony/event-dispatcher": "", - "symfony/lock": "", - "symfony/process": "" + "symfony/config": "^5.4|^6.0|^7.0", + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/event-dispatcher": "^5.4|^6.0|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/lock": "^5.4|^6.0|^7.0", + "symfony/messenger": "^5.4|^6.0|^7.0", + "symfony/process": "^5.4|^6.0|^7.0", + "symfony/stopwatch": "^5.4|^6.0|^7.0", + "symfony/var-dumper": "^5.4|^6.0|^7.0" }, "type": "library", "autoload": { @@ -3494,12 +3513,12 @@ "homepage": "https://symfony.com", "keywords": [ "cli", - "command line", + "command-line", "console", "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v6.0.5" + "source": "https://github.com/symfony/console/tree/v6.4.8" }, "funding": [ { @@ -3515,51 +3534,43 @@ "type": "tidelift" } ], - "time": "2022-02-25T10:48:52+00:00" + "time": "2024-05-31T14:49:08+00:00" }, { "name": "symfony/dependency-injection", - "version": "v6.0.6", + "version": "v7.1.1", "source": { "type": "git", "url": "https://github.com/symfony/dependency-injection.git", - "reference": "a296611f599d0b28e7af88798f830f9cb4d1e8e6" + "reference": "77c636dfd86c0b60c5d184b2fd2ddf8dd11c309c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/a296611f599d0b28e7af88798f830f9cb4d1e8e6", - "reference": "a296611f599d0b28e7af88798f830f9cb4d1e8e6", + "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/77c636dfd86c0b60c5d184b2fd2ddf8dd11c309c", + "reference": "77c636dfd86c0b60c5d184b2fd2ddf8dd11c309c", "shasum": "" }, "require": { - "php": ">=8.0.2", + "php": ">=8.2", "psr/container": "^1.1|^2.0", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/polyfill-php81": "^1.22", - "symfony/service-contracts": "^1.1.6|^2.0|^3.0" + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/service-contracts": "^3.5", + "symfony/var-exporter": "^6.4|^7.0" }, "conflict": { "ext-psr": "<1.1|>=2", - "symfony/config": "<5.4", - "symfony/finder": "<5.4", - "symfony/proxy-manager-bridge": "<5.4", - "symfony/yaml": "<5.4" + "symfony/config": "<6.4", + "symfony/finder": "<6.4", + "symfony/yaml": "<6.4" }, "provide": { "psr/container-implementation": "1.1|2.0", "symfony/service-implementation": "1.1|2.0|3.0" }, "require-dev": { - "symfony/config": "^5.4|^6.0", - "symfony/expression-language": "^5.4|^6.0", - "symfony/yaml": "^5.4|^6.0" - }, - "suggest": { - "symfony/config": "", - "symfony/expression-language": "For using expressions in service container configuration", - "symfony/finder": "For using double-star glob patterns or when GLOB_BRACE portability is required", - "symfony/proxy-manager-bridge": "Generate service proxies to lazy load them", - "symfony/yaml": "" + "symfony/config": "^6.4|^7.0", + "symfony/expression-language": "^6.4|^7.0", + "symfony/yaml": "^6.4|^7.0" }, "type": "library", "autoload": { @@ -3587,7 +3598,7 @@ "description": "Allows you to standardize and centralize the way objects are constructed in your application", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/dependency-injection/tree/v6.0.6" + "source": "https://github.com/symfony/dependency-injection/tree/v7.1.1" }, "funding": [ { @@ -3603,29 +3614,29 @@ "type": "tidelift" } ], - "time": "2022-03-02T12:58:14+00:00" + "time": "2024-05-31T14:57:53+00:00" }, { "name": "symfony/deprecation-contracts", - "version": "v3.0.0", + "version": "v3.5.0", "source": { "type": "git", "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "c726b64c1ccfe2896cb7df2e1331c357ad1c8ced" + "reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/c726b64c1ccfe2896cb7df2e1331c357ad1c8ced", - "reference": "c726b64c1ccfe2896cb7df2e1331c357ad1c8ced", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1", + "reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1", "shasum": "" }, "require": { - "php": ">=8.0.2" + "php": ">=8.1" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "3.0-dev" + "dev-main": "3.5-dev" }, "thanks": { "name": "symfony/contracts", @@ -3654,7 +3665,7 @@ "description": "A generic function and convention to trigger deprecation notices", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v3.0.0" + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.5.0" }, "funding": [ { @@ -3670,27 +3681,30 @@ "type": "tidelift" } ], - "time": "2021-11-01T23:48:49+00:00" + "time": "2024-04-18T09:32:20+00:00" }, { "name": "symfony/filesystem", - "version": "v6.0.6", + "version": "v7.1.1", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "52b888523545b0b4049ab9ce48766802484d7046" + "reference": "802e87002f919296c9f606457d9fa327a0b3d6b2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/52b888523545b0b4049ab9ce48766802484d7046", - "reference": "52b888523545b0b4049ab9ce48766802484d7046", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/802e87002f919296c9f606457d9fa327a0b3d6b2", + "reference": "802e87002f919296c9f606457d9fa327a0b3d6b2", "shasum": "" }, "require": { - "php": ">=8.0.2", + "php": ">=8.2", "symfony/polyfill-ctype": "~1.8", "symfony/polyfill-mbstring": "~1.8" }, + "require-dev": { + "symfony/process": "^6.4|^7.0" + }, "type": "library", "autoload": { "psr-4": { @@ -3717,7 +3731,7 @@ "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/filesystem/tree/v6.0.6" + "source": "https://github.com/symfony/filesystem/tree/v7.1.1" }, "funding": [ { @@ -3733,20 +3747,20 @@ "type": "tidelift" } ], - "time": "2022-03-02T12:58:14+00:00" + "time": "2024-05-31T14:57:53+00:00" }, { "name": "symfony/polyfill-ctype", - "version": "v1.25.0", + "version": "v1.29.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "30885182c981ab175d4d034db0f6f469898070ab" + "reference": "ef4d7e442ca910c4764bce785146269b30cb5fc4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/30885182c981ab175d4d034db0f6f469898070ab", - "reference": "30885182c981ab175d4d034db0f6f469898070ab", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/ef4d7e442ca910c4764bce785146269b30cb5fc4", + "reference": "ef4d7e442ca910c4764bce785146269b30cb5fc4", "shasum": "" }, "require": { @@ -3760,9 +3774,6 @@ }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "1.23-dev" - }, "thanks": { "name": "symfony/polyfill", "url": "https://github.com/symfony/polyfill" @@ -3799,7 +3810,7 @@ "portable" ], "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.25.0" + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.29.0" }, "funding": [ { @@ -3815,20 +3826,20 @@ "type": "tidelift" } ], - "time": "2021-10-20T20:35:02+00:00" + "time": "2024-01-29T20:11:03+00:00" }, { "name": "symfony/polyfill-intl-grapheme", - "version": "v1.25.0", + "version": "v1.29.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "81b86b50cf841a64252b439e738e97f4a34e2783" + "reference": "32a9da87d7b3245e09ac426c83d334ae9f06f80f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/81b86b50cf841a64252b439e738e97f4a34e2783", - "reference": "81b86b50cf841a64252b439e738e97f4a34e2783", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/32a9da87d7b3245e09ac426c83d334ae9f06f80f", + "reference": "32a9da87d7b3245e09ac426c83d334ae9f06f80f", "shasum": "" }, "require": { @@ -3839,9 +3850,6 @@ }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "1.23-dev" - }, "thanks": { "name": "symfony/polyfill", "url": "https://github.com/symfony/polyfill" @@ -3880,7 +3888,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.25.0" + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.29.0" }, "funding": [ { @@ -3896,20 +3904,20 @@ "type": "tidelift" } ], - "time": "2021-11-23T21:10:46+00:00" + "time": "2024-01-29T20:11:03+00:00" }, { "name": "symfony/polyfill-intl-normalizer", - "version": "v1.25.0", + "version": "v1.29.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-normalizer.git", - "reference": "8590a5f561694770bdcd3f9b5c69dde6945028e8" + "reference": "bc45c394692b948b4d383a08d7753968bed9a83d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/8590a5f561694770bdcd3f9b5c69dde6945028e8", - "reference": "8590a5f561694770bdcd3f9b5c69dde6945028e8", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/bc45c394692b948b4d383a08d7753968bed9a83d", + "reference": "bc45c394692b948b4d383a08d7753968bed9a83d", "shasum": "" }, "require": { @@ -3920,9 +3928,6 @@ }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "1.23-dev" - }, "thanks": { "name": "symfony/polyfill", "url": "https://github.com/symfony/polyfill" @@ -3964,7 +3969,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.25.0" + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.29.0" }, "funding": [ { @@ -3980,20 +3985,20 @@ "type": "tidelift" } ], - "time": "2021-02-19T12:13:01+00:00" + "time": "2024-01-29T20:11:03+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.25.0", + "version": "v1.29.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "0abb51d2f102e00a4eefcf46ba7fec406d245825" + "reference": "9773676c8a1bb1f8d4340a62efe641cf76eda7ec" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/0abb51d2f102e00a4eefcf46ba7fec406d245825", - "reference": "0abb51d2f102e00a4eefcf46ba7fec406d245825", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9773676c8a1bb1f8d4340a62efe641cf76eda7ec", + "reference": "9773676c8a1bb1f8d4340a62efe641cf76eda7ec", "shasum": "" }, "require": { @@ -4007,9 +4012,6 @@ }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "1.23-dev" - }, "thanks": { "name": "symfony/polyfill", "url": "https://github.com/symfony/polyfill" @@ -4047,7 +4049,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.25.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.29.0" }, "funding": [ { @@ -4063,20 +4065,20 @@ "type": "tidelift" } ], - "time": "2021-11-30T18:21:41+00:00" + "time": "2024-01-29T20:11:03+00:00" }, { - "name": "symfony/polyfill-php81", - "version": "v1.25.0", + "name": "symfony/polyfill-php80", + "version": "v1.29.0", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-php81.git", - "reference": "5de4ba2d41b15f9bd0e19b2ab9674135813ec98f" + "url": "https://github.com/symfony/polyfill-php80.git", + "reference": "87b68208d5c1188808dd7839ee1e6c8ec3b02f1b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/5de4ba2d41b15f9bd0e19b2ab9674135813ec98f", - "reference": "5de4ba2d41b15f9bd0e19b2ab9674135813ec98f", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/87b68208d5c1188808dd7839ee1e6c8ec3b02f1b", + "reference": "87b68208d5c1188808dd7839ee1e6c8ec3b02f1b", "shasum": "" }, "require": { @@ -4084,9 +4086,6 @@ }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "1.23-dev" - }, "thanks": { "name": "symfony/polyfill", "url": "https://github.com/symfony/polyfill" @@ -4097,7 +4096,7 @@ "bootstrap.php" ], "psr-4": { - "Symfony\\Polyfill\\Php81\\": "" + "Symfony\\Polyfill\\Php80\\": "" }, "classmap": [ "Resources/stubs" @@ -4108,6 +4107,10 @@ "MIT" ], "authors": [ + { + "name": "Ion Bazan", + "email": "ion.bazan@gmail.com" + }, { "name": "Nicolas Grekas", "email": "p@tchwork.com" @@ -4117,7 +4120,7 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill backporting some PHP 8.1+ features to lower PHP versions", + "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", "homepage": "https://symfony.com", "keywords": [ "compatibility", @@ -4126,7 +4129,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php81/tree/v1.25.0" + "source": "https://github.com/symfony/polyfill-php80/tree/v1.29.0" }, "funding": [ { @@ -4142,36 +4145,34 @@ "type": "tidelift" } ], - "time": "2021-09-13T13:58:11+00:00" + "time": "2024-01-29T20:11:03+00:00" }, { "name": "symfony/service-contracts", - "version": "v3.0.0", + "version": "v3.5.0", "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", - "reference": "36715ebf9fb9db73db0cb24263c79077c6fe8603" + "reference": "bd1d9e59a81d8fa4acdcea3f617c581f7475a80f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/36715ebf9fb9db73db0cb24263c79077c6fe8603", - "reference": "36715ebf9fb9db73db0cb24263c79077c6fe8603", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/bd1d9e59a81d8fa4acdcea3f617c581f7475a80f", + "reference": "bd1d9e59a81d8fa4acdcea3f617c581f7475a80f", "shasum": "" }, "require": { - "php": ">=8.0.2", - "psr/container": "^2.0" + "php": ">=8.1", + "psr/container": "^1.1|^2.0", + "symfony/deprecation-contracts": "^2.5|^3" }, "conflict": { "ext-psr": "<1.1|>=2" }, - "suggest": { - "symfony/service-implementation": "" - }, "type": "library", "extra": { "branch-alias": { - "dev-main": "3.0-dev" + "dev-main": "3.5-dev" }, "thanks": { "name": "symfony/contracts", @@ -4181,7 +4182,10 @@ "autoload": { "psr-4": { "Symfony\\Contracts\\Service\\": "" - } + }, + "exclude-from-classmap": [ + "/Test/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -4208,7 +4212,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/service-contracts/tree/v3.0.0" + "source": "https://github.com/symfony/service-contracts/tree/v3.5.0" }, "funding": [ { @@ -4224,37 +4228,39 @@ "type": "tidelift" } ], - "time": "2021-11-04T17:53:12+00:00" + "time": "2024-04-18T09:32:20+00:00" }, { "name": "symfony/string", - "version": "v6.0.3", + "version": "v7.1.1", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "522144f0c4c004c80d56fa47e40e17028e2eefc2" + "reference": "60bc311c74e0af215101235aa6f471bcbc032df2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/522144f0c4c004c80d56fa47e40e17028e2eefc2", - "reference": "522144f0c4c004c80d56fa47e40e17028e2eefc2", + "url": "https://api.github.com/repos/symfony/string/zipball/60bc311c74e0af215101235aa6f471bcbc032df2", + "reference": "60bc311c74e0af215101235aa6f471bcbc032df2", "shasum": "" }, "require": { - "php": ">=8.0.2", + "php": ">=8.2", "symfony/polyfill-ctype": "~1.8", "symfony/polyfill-intl-grapheme": "~1.0", "symfony/polyfill-intl-normalizer": "~1.0", "symfony/polyfill-mbstring": "~1.0" }, "conflict": { - "symfony/translation-contracts": "<2.0" + "symfony/translation-contracts": "<2.5" }, "require-dev": { - "symfony/error-handler": "^5.4|^6.0", - "symfony/http-client": "^5.4|^6.0", - "symfony/translation-contracts": "^2.0|^3.0", - "symfony/var-exporter": "^5.4|^6.0" + "symfony/emoji": "^7.1", + "symfony/error-handler": "^6.4|^7.0", + "symfony/http-client": "^6.4|^7.0", + "symfony/intl": "^6.4|^7.0", + "symfony/translation-contracts": "^2.5|^3.0", + "symfony/var-exporter": "^6.4|^7.0" }, "type": "library", "autoload": { @@ -4293,7 +4299,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v6.0.3" + "source": "https://github.com/symfony/string/tree/v7.1.1" }, "funding": [ { @@ -4309,20 +4315,96 @@ "type": "tidelift" } ], - "time": "2022-01-02T09:55:41+00:00" + "time": "2024-06-04T06:40:14+00:00" + }, + { + "name": "symfony/var-exporter", + "version": "v7.1.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/var-exporter.git", + "reference": "db82c2b73b88734557cfc30e3270d83fa651b712" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/var-exporter/zipball/db82c2b73b88734557cfc30e3270d83fa651b712", + "reference": "db82c2b73b88734557cfc30e3270d83fa651b712", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "symfony/property-access": "^6.4|^7.0", + "symfony/serializer": "^6.4|^7.0", + "symfony/var-dumper": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\VarExporter\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Allows exporting any serializable PHP data structure to plain PHP code", + "homepage": "https://symfony.com", + "keywords": [ + "clone", + "construct", + "export", + "hydrate", + "instantiate", + "lazy-loading", + "proxy", + "serialize" + ], + "support": { + "source": "https://github.com/symfony/var-exporter/tree/v7.1.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-05-31T14:57:53+00:00" }, { "name": "theseer/tokenizer", - "version": "1.2.1", + "version": "1.2.3", "source": { "type": "git", "url": "https://github.com/theseer/tokenizer.git", - "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e" + "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/theseer/tokenizer/zipball/34a41e998c2183e22995f158c581e7b5e755ab9e", - "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", + "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", "shasum": "" }, "require": { @@ -4351,7 +4433,7 @@ "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", "support": { "issues": "https://github.com/theseer/tokenizer/issues", - "source": "https://github.com/theseer/tokenizer/tree/1.2.1" + "source": "https://github.com/theseer/tokenizer/tree/1.2.3" }, "funding": [ { @@ -4359,20 +4441,20 @@ "type": "github" } ], - "time": "2021-07-28T10:34:58+00:00" + "time": "2024-03-03T12:36:25+00:00" }, { "name": "vimeo/psalm", - "version": "4.22.0", + "version": "4.30.0", "source": { "type": "git", "url": "https://github.com/vimeo/psalm.git", - "reference": "fc2c6ab4d5fa5d644d8617089f012f3bb84b8703" + "reference": "d0bc6e25d89f649e4f36a534f330f8bb4643dd69" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/vimeo/psalm/zipball/fc2c6ab4d5fa5d644d8617089f012f3bb84b8703", - "reference": "fc2c6ab4d5fa5d644d8617089f012f3bb84b8703", + "url": "https://api.github.com/repos/vimeo/psalm/zipball/d0bc6e25d89f649e4f36a534f330f8bb4643dd69", + "reference": "d0bc6e25d89f649e4f36a534f330f8bb4643dd69", "shasum": "" }, "require": { @@ -4397,6 +4479,7 @@ "php": "^7.1|^8", "sebastian/diff": "^3.0 || ^4.0", "symfony/console": "^3.4.17 || ^4.1.6 || ^5.0 || ^6.0", + "symfony/polyfill-php80": "^1.25", "webmozart/path-util": "^2.3" }, "provide": { @@ -4410,6 +4493,7 @@ "phpdocumentor/reflection-docblock": "^5", "phpmyadmin/sql-parser": "5.1.0||dev-master", "phpspec/prophecy": ">=1.9.0", + "phpstan/phpdoc-parser": "1.2.* || 1.6.4", "phpunit/phpunit": "^9.0", "psalm/plugin-phpunit": "^0.16", "slevomat/coding-standard": "^7.0", @@ -4463,27 +4547,27 @@ ], "support": { "issues": "https://github.com/vimeo/psalm/issues", - "source": "https://github.com/vimeo/psalm/tree/4.22.0" + "source": "https://github.com/vimeo/psalm/tree/4.30.0" }, - "time": "2022-02-24T20:34:05+00:00" + "time": "2022-11-06T20:37:08+00:00" }, { "name": "webmozart/assert", - "version": "1.10.0", + "version": "1.11.0", "source": { "type": "git", "url": "https://github.com/webmozarts/assert.git", - "reference": "6964c76c7804814a842473e0c8fd15bab0f18e25" + "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/webmozarts/assert/zipball/6964c76c7804814a842473e0c8fd15bab0f18e25", - "reference": "6964c76c7804814a842473e0c8fd15bab0f18e25", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/11cb2199493b2f8a3b53e7f19068fc6aac760991", + "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991", "shasum": "" }, "require": { - "php": "^7.2 || ^8.0", - "symfony/polyfill-ctype": "^1.8" + "ext-ctype": "*", + "php": "^7.2 || ^8.0" }, "conflict": { "phpstan/phpstan": "<0.12.20", @@ -4521,9 +4605,9 @@ ], "support": { "issues": "https://github.com/webmozarts/assert/issues", - "source": "https://github.com/webmozarts/assert/tree/1.10.0" + "source": "https://github.com/webmozarts/assert/tree/1.11.0" }, - "time": "2021-03-09T10:59:23+00:00" + "time": "2022-06-03T18:03:27+00:00" }, { "name": "webmozart/path-util", @@ -4584,5 +4668,5 @@ "prefer-lowest": false, "platform": [], "platform-dev": [], - "plugin-api-version": "2.2.0" + "plugin-api-version": "2.6.0" } From a3d9681859f70943f0e408c577e44758a8ea4bd9 Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Tue, 11 Jun 2024 17:24:57 +0900 Subject: [PATCH 08/34] Add SqlQueryInterceptModule and update SqlQueryModule and SqlQueryProviderModule Added SqlQueryInterceptModule to extend functionality. Modified SqlQueryModule to remove unnecessary interceptor bindings. Refactored SqlQueryProviderModule to include sqlDir in the constructor to handle SQL directory instances more efficiently. --- src/SqlQueryInterceptModule.php | 23 +++++++++++++++++++++++ src/SqlQueryModule.php | 15 --------------- src/SqlQueryProviderModule.php | 8 +++++++- tests/SqlQueryModuleTest.php | 1 + tests/SqlQueryProviderModuleTest.php | 2 +- 5 files changed, 32 insertions(+), 17 deletions(-) create mode 100644 src/SqlQueryInterceptModule.php diff --git a/src/SqlQueryInterceptModule.php b/src/SqlQueryInterceptModule.php new file mode 100644 index 0000000..624a2ca --- /dev/null +++ b/src/SqlQueryInterceptModule.php @@ -0,0 +1,23 @@ +bindInterceptor( + $this->matcher->any(), + $this->matcher->annotatedWith(Query::class), + [QueryInterceptor::class], + ); + } +} diff --git a/src/SqlQueryModule.php b/src/SqlQueryModule.php index 7f0444a..33ee6e1 100644 --- a/src/SqlQueryModule.php +++ b/src/SqlQueryModule.php @@ -6,8 +6,6 @@ use FilesystemIterator; use Ray\Di\AbstractModule; -use Ray\Query\Annotation\AliasQuery; -use Ray\Query\Annotation\Query; use RecursiveDirectoryIterator; use RecursiveIteratorIterator; use RecursiveRegexIterator; @@ -55,19 +53,6 @@ protected function configure() $sql = (string) ($this->getSql)($fileInfo); $this->bind()->annotatedWith($sqlId)->toInstance($sql); } - - $this->bindInterceptor( - $this->matcher->any(), - $this->matcher->annotatedWith(Query::class), - [QueryInterceptor::class], - ); - // <=0.4.0 - /** @psalm-suppress DeprecatedClass */ - $this->bindInterceptor( - $this->matcher->any(), - $this->matcher->annotatedWith(AliasQuery::class), - [SqlAliasInterceptor::class], - ); } protected function bindCallableItem(string $name, string $sqlId): void diff --git a/src/SqlQueryProviderModule.php b/src/SqlQueryProviderModule.php index 3ac529a..ed26b7a 100644 --- a/src/SqlQueryProviderModule.php +++ b/src/SqlQueryProviderModule.php @@ -11,8 +11,13 @@ class SqlQueryProviderModule extends AbstractModule { - public function __construct(?AbstractModule $module = null) + /** @var string */ + private $sqlDir; + + public function __construct(string $sqlDir, ?AbstractModule $module = null) { + $this->sqlDir = $sqlDir; + parent::__construct($module); } @@ -21,6 +26,7 @@ public function __construct(?AbstractModule $module = null) */ protected function configure() { + $this->bind(SqlDir::class)->toInstance(new SqlDir($this->sqlDir)); $this->bind(SqlFinder::class)->in(Scope::SINGLETON); $this->bind(ParamReaderInterface::class)->to(ParamReader::class)->in(Scope::SINGLETON); $this->bind(RowInterface::class)->toProvider(RowInterfaceProvider::class); diff --git a/tests/SqlQueryModuleTest.php b/tests/SqlQueryModuleTest.php index ac3cd32..f77112b 100644 --- a/tests/SqlQueryModuleTest.php +++ b/tests/SqlQueryModuleTest.php @@ -47,6 +47,7 @@ protected function configure() { $this->bind(ExtendedPdoInterface::class)->toInstance($this->pdo); $this->install(new SqlQueryModule(__DIR__ . '/Fake/sql')); + $this->install(new SqlQueryInterceptModule()); } }; } diff --git a/tests/SqlQueryProviderModuleTest.php b/tests/SqlQueryProviderModuleTest.php index 4b4b932..f5f24c0 100644 --- a/tests/SqlQueryProviderModuleTest.php +++ b/tests/SqlQueryProviderModuleTest.php @@ -41,7 +41,7 @@ protected function configure() { $this->bind(ExtendedPdoInterface::class)->toInstance($this->pdo); $this->install(new SqlQueryModule(__DIR__ . '/Fake/sql')); - $this->install(new SqlQueryProviderModule()); + $this->install(new SqlQueryProviderModule(__DIR__ . '/Fake/sql')); } }; } From a4adc51dc1b68175bb2e211ec4c9be9d83f6c3f5 Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Tue, 11 Jun 2024 20:38:01 +0900 Subject: [PATCH 09/34] Refactor and update README.md documentation This commit involves a significant revision of the README.md file. The changes made include the removal of an unnecessary language link, reframing of the sections for better clarity and relevancy, as well as updating code snippets for improved comprehension. Added a new section for 'Usage', explicitly defined function return types, and eliminated redundant code blocks for concise information. The commit also ensures all code blocks are correctly formatted. --- README.md | 60 ++++++++++++++++++++++++++++++------------------------- 1 file changed, 33 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index d8c8aed..ae959df 100644 --- a/README.md +++ b/README.md @@ -4,8 +4,6 @@ [![Type Coverage](https://shepherd.dev/github/ray-di/Ray.QueryModule/coverage.svg)](https://shepherd.dev/github/ray-di/Ray.QueryModule) ![Continuous Integration](https://github.com/ray-di/Ray.QueryModule/workflows/Continuous%20Integration/badge.svg) -[Japanese](README.ja.md) - ## Overview `Ray.QueryModule` makes a query to an external media such as a database or Web API with a function object to be injected. @@ -14,20 +12,20 @@ * `WebQueryModule` is for the Web API. Convert the URI and method set into a simple function object that Web requests to that URI. * `PhpQueryModule` is a generic module. It provides storage access which can not be provided by static conversion by PHP function object. - ## Motivation * You can have a clear boundary between domain layer (usage code) and infrastructure layer (injected function) in code. * Execution objects are generated automatically so you do not need to write procedural code for execution. * Since usage codes are indifferent to the actual state of external media, storage can be changed later. Easy parallel development and stabbing. + ## Installation ### Composer install $ composer require ray/query-module -### Module install +### Module install (SQL) ```php use Ray\Di\AbstractModule; @@ -35,37 +33,31 @@ use Ray\Query\SqlQueryModule; class AppModule extends AbstractModule { - protected function configure() + protected function configure(): void { // SqlQueryModule install - $this->install(new SqlQueryModule($sqlDir)); - - // WebQueryModule install - $webQueryConfig = [ - 'post_todo' => ['POST', 'https://httpbin.org/todo'], // bind-name => [method, uri] - 'get_todo' => ['GET', 'https://httpbin.org/todo'] - ]; - $guzzleConfig = []; // @see http://docs.guzzlephp.org/en/stable/request-options.html - $this->install(new WebQueryModule($webQueryConfig, $guzzleConfig)); + $this->install(new SqlQueryProviderModule($sqlDir)); } } ``` +## Usage -### SQL files +Store SQL files in one directory. -$sqlDir/**todo_insert.sql** + +$sqlDir/todo_insert.sql ```sql INSERT INTO todo (id, title) VALUES (:id, :title) ``` -$sqlDir/**todo_item_by_id.sql** +$sqlDir/todo_item_by_id.sql ```sql SELECT * FROM todo WHERE id = :id ``` -## Convert SQL to SQL invocation object +#### Convert SQL to SQL invocation object A callable object injected into the constructor. Those object was made in specified sql with `#[Sql]` binding. @@ -95,7 +87,8 @@ class Todo } } ``` -## Row or RowList + +### Row or RowList You can specify expected return value type is either `Row` or `RowList` with `RowInterface` or `RowListInterface`. `RowInterface` is handy to specify SQL which return single row. @@ -134,7 +127,24 @@ class Todos ## Override the method with callable object -Entire method invocation can be override with callable object in specified with `@Query`. +Entire method invocation can be override with callable object in specified with `#[Query]`. + + +```php +use Ray\Di\AbstractModule; +use Ray\Query\SqlQueryProviderModule; +use Ray\Query\SqlQueryInterceptModule; + +class AppModule extends AbstractModule +{ + protected function configure(): void + { + $this->install(new SqlQueryProviderModule($sqlDir)); + $this->SqlQueryInterceptModule(); + } +} +``` + ```php class Foo @@ -251,7 +261,7 @@ class CreateTodo implements QueryInterface } ``` -Bind to `callable`. +Bind to `QueryInterface `. ```php $this->bind(QueryInterface::class)->annotatedWith('cretate_todo')->to(CreateTodo::class); @@ -277,13 +287,13 @@ use Ray\Query\SqlFileName; use Ray\Query\SqlQueryModule; $this->install(new SqlQueryModule(__DIR__ . '/Fake/sql', null, new SqlFileName())); -```` +``` Execute SQL ```sql /* todo_item_by_id.sql */ SELECT * FROM todo WHERE id = :id -```` +``` ## Demo @@ -291,7 +301,3 @@ Execute SQL php demo/run.php ``` -## BEAR.Sunday example - -* [Koriym.Ticketsan](https://github.com/koriym/Koriym.TicketSan/blob/master/src/Resource/App/Ticket.php) - From 33c5c8d923b95277347e8a65340bb7af01c4689b Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Tue, 11 Jun 2024 20:38:13 +0900 Subject: [PATCH 10/34] Removed Japanese version of README file --- README.ja.md | 290 --------------------------------------------------- 1 file changed, 290 deletions(-) delete mode 100644 README.ja.md diff --git a/README.ja.md b/README.ja.md deleted file mode 100644 index 69ed987..0000000 --- a/README.ja.md +++ /dev/null @@ -1,290 +0,0 @@ -# Ray.QueryModule -[![codecov](https://codecov.io/gh/ray-di/Ray.QueryModule/branch/1.x/graph/badge.svg?token=60G2MFDOBR)](https://codecov.io/gh/ray-di/Ray.QueryModule) -[![Type Coverage](https://shepherd.dev/github/ray-di/Ray.QueryModule/coverage.svg)](https://shepherd.dev/github/ray-di/Ray.QueryModule) -![Continuous Integration](https://github.com/ray-di/Ray.QueryModule/workflows/Continuous%20Integration/badge.svg) - -[English](README.md) - -## 概要 - -`Ray.QueryModule`はデータベースなど外部メディアへの問い合わせを、インジェクトされる関数オブジェクトで行うようにします。 - - * `SqlQueryModule`はDB用です。SQLファイルをそのSQLを実行する単純な関数オブジェクトに変換します。 - * `WebQueryModule`はWeb API用です。URIをそのURIにWebレクエストする単純な関数オブジェクトに変換します。 - * `PhpQueryModule`は汎用のモジュールです。静的な変換では提供できないストレージアクセスをPHPの関数オブジェクトとして提供します。 - -## モチベーション - - * コードにドメイン層(利用コード)とインフラストラクチャ層(インジェクトされる関数)の明確な境界を持たせることが出来ます。 - * 実行オブジェクトは自動で生成されるので実行のための手続きコードを記述する必要がありません。 - * 利用コードは外部メディアの実態に無関心なので、ストレージを後で変更することができます。平行開発やスタブ化が容易です。 - - -## インストール - -### Composerインストール - - composer require ray/query-module - -### モジュールのインストール - -```php -use Ray\Di\AbstractModule; -use Ray\Query\SqlQueryModule; - -class AppModule extends AbstractModule -{ -protected function configure() -{ - // SqlQueryModule インストール - $this->install(new SqlQueryModule($sqlDir)); - - // WebQueryModuleインストール - $webQueryConfig = [ - 'post_todo' => ['POST', 'https://httpbin.org/todo'], - 'get_todo' => ['GET', 'https://httpbin.org/todo'] - ]; - $guzzleConfig = []; - $this->install(new WebQueryModule($webQueryConfig, $guzzleConfig)); - - // ISO8601 DateTimeフォーマット - $this->>install(new Iso8601FormatModule(['created_at', 'updated_at']); - } -} -``` - -### SQLファイル - -$sqlDir/**todo_insert.sql** - -```sql -INSERT INTO todo (id, title) VALUES (:id, :title) -``` - -$sqlDir/**todo_item_by_id.sql** - -```sql -SELECT * FROM todo WHERE id = :id -``` - -## 利用 - -## SQLを実行オブジェクトに - -`SqlQueryModule`をインストールするとSQLのファイル名によって束縛されたSQL実行関数がインジェクトされます。 -例えば以下の例なら、`todo_insert.sql`ファイルが`$createTodo`の実行オブジェクトに変換されインジェクトされます - -```php -class Todo -{ - public function __construct( - private #[Sql("createTodo") QUereInterface $createTodo、 - private #[Sql("createTodo") QUereInterface $todo - ){} - - public function get(string $uuid) - { - return ($this->todo)(['id' => $uuid]); - } - - public function create(string $uuid, string $title) - { - ($this->createTodo)([ - 'id' => $uuid, - 'title' => $title - ]); - } -} -``` -## 行または行リスト - -RowInterface` または `RowListInterface` を使用すると、戻り値の型を `Row` または `RowList` に指定することができます。 -RowInterface` は単一の行を返す SQL を指定するのに便利です。 - -```php -use Ray\Query\RowInterface; - -class Todo -{ - public function __construct( - #[Sql('todo_item_by_id') RowInterface $todo) - { - $this->todo = $todo; - } - - public function get(string $uuid) - { - $todo = ($this->todo)(['id' => $uuid]); // 一行分のデータ - } -} -``` - -```php -use Ray\Query\RowListInterface; - -class Todos -{ - public function __construct( - #[Sql("todos") private readonly RowListInterface $todos - ){} - - public function get(string $uuid) - { - $todos = ($this->todos)(); // 複数行のデータ - } -} -``` - -## メソッドをオーバーライドする - -メソッド全体を `#[Query]` で指定した callable オブジェクトでオーバーライドすることができます。 - -```php -use Ray\Query\Annotation\Query;class Foo -{ - #[Query("todo_item_by_id")] - public function get(string $id) - { - } -} -``` - -パラメータ名がメソッドの引数とQueryオブジェクトの引数で異なる場合は、uri_template式で解決できます。 - -```php -use Ray\Query\Annotation\Query;class FooTempalted -{ - #[Query(id:"todo_item_by_id?id={a}", templated:true)] - public function get(string $a) - { - } -} -``` - -単一行の時は`type='row'`を指定します。 - -```php -use Ray\Query\Annotation\Query;class FooRow - -#[Query("ticket_item_by_id", type: "row")] -public function onGet(string $id): static -{ -} -} -``` - -SELETした結果が無い場合にはcode 404が返ります。 - -## URI を Web リクエストオブジェクトに変換 - -`WebQueryModule`は設定で束縛したURIをWebアクセスする実行関数がインジェクトされます。 -例えば以下の例なら、`https://httpbin.org/todo`を`POST`リクエストする`$createTodo`の実行オブジェクトに変換されインジェクトされます - -```php -use Ray\Di\AbstractModule; -use Ray\Query\SqlQueryModule; - -class AppModule extends AbstractModule -{ - protected function configure() - { - // WebQueryModuleインストール - $webQueryConfig = [ - 'todo_post' => ['POST', 'https://httpbin.org/todo'], // bind-name => [method, uri] - 'todo_get' => ['GET', 'https://httpbin.org/todo'] - ]; - $guzzleConfig = []; // @see http://docs.guzzlephp.org/en/stable/request-options.html - $this->install(new WebQueryModule($webQueryConfig, $guzzleConfig)); - } -} -``` - -使い方は `SqlQueryModule` と同じです。 - -```php - public function __construct( - callable $createTodo, - callable $todo - ){ - $this->createTodo = $createTodo; - $this->todo = $todo; - } -``` - -```php -// POST -($this->createTodo)([ - 'id' => $uuid, - 'title' => $title -]); - -// GET -($this->todo)(['id' => $uuid]); -``` - -`@Query`の利用コードも変わりません。 - -## PHPクラスを束縛 - -複数のクエリーを実行したり、他の依存が必要な場合にはPHPクラスに束縛し依存を利用します。 - -```php -class CreateTodo implements QueryInterface -{ - public function __construct( - private PdoInterface $pdo, - private QueryBuilderInferface $builder - ) { - $this->pdo = $pdo; - $this->builder = $builder; - } - - public function __invoke(array $query) - { - // $pdoと$builderを使ったクエリ実行 - return $result; - } -} -``` - -```php -$this->bind(QueryInterface::class)->annotatedWith('cretate_todo')->to(CreateTodo::class); -``` - -利用コードは同じです。`#[Query]`の利用コードも変わりません。 - -## ISO8601 DateTimeモジュール - -指定したコラム名の値を[ISO8601](https://www.iso.org/iso-8601-date-and-time-format.html)形式に変換します。PHPでは[DateTime::ATOM](https://www.php.net/manual/ja/class.datetime.php#datetime.constants.atom)の定数で定義されているフォーマットです。 -日付のコラム名を配列にして`Iso8601FormatModule`の引数に渡してインストールします。 - -```php -$this->install(new Iso8601FormatModule(['created_at', 'updated_at'])); -``` -## SQL file name log - -SQLファイル名をコメントとしてSQL文に付加する事ができます。クエリーログに有用です。 - -```php -use Ray\Query\SqlFileName; -use Ray\Query\SqlQueryModule; - -$this->install(new SqlQueryModule(__DIR__ . '/Fake/sql', null, new SqlFileName())); -``` - -実行SQL - -```sql -/* todo_item_by_id.sql */ SELECT * FROM todo WHERE id = :id -``` - -## デモ - -``` -php demo/run.php -``` - -## BEAR.Sunday - - * [Koriym.Ticketsan](https://github.com/koriym/Koriym.TicketSan/blob/master/src/Resource/App/Ticket.php) - From 9970cab700eb037a68405e08e2582e2015eafceb Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Tue, 11 Jun 2024 22:15:32 +0900 Subject: [PATCH 11/34] Remove SqlQueryProviderModule and update tests SqlQueryProviderModule and its corresponding test have been removed. SqlFinderInterface has been created. SqlFinder class now implements SqlFinderInterface. Minor changes to other test files and classes have been made, including adjustments to method parameter types and additions of new classes. The changes are made to improve code structure and readability. --- src/CallableQueryModule.php | 93 +++++++++++++++ src/RowInterfaceProvider.php | 4 +- src/RowListInterfaceProvider.php | 4 +- src/SqlFinder.php | 2 +- src/SqlFinderInterface.php | 12 ++ src/SqlQueryModule.php | 80 +++---------- src/SqlQueryProviderModule.php | 37 ------ tests/Fake/FakeTodoRepository.php | 2 +- tests/Iso8601FormatModuleTest.php | 2 +- tests/SqlQueryModuleTest.php | 168 +++++---------------------- tests/SqlQueryProviderModuleTest.php | 88 -------------- 11 files changed, 151 insertions(+), 341 deletions(-) create mode 100644 src/CallableQueryModule.php create mode 100644 src/SqlFinderInterface.php delete mode 100644 src/SqlQueryProviderModule.php delete mode 100644 tests/SqlQueryProviderModuleTest.php diff --git a/src/CallableQueryModule.php b/src/CallableQueryModule.php new file mode 100644 index 0000000..22ad3e0 --- /dev/null +++ b/src/CallableQueryModule.php @@ -0,0 +1,93 @@ +sqlDir = $sqlDir; + $this->getSql = $getSql ?? static function (SplFileInfo $fileInfo): string { + return (string) file_get_contents($fileInfo->getPathname()); + }; + + parent::__construct($module); + } + + /** + * {@inheritdoc} + */ + protected function configure() + { + $this->bind(SqlDir::class)->toInstance(new SqlDir($this->sqlDir)); + /** @var SplFileInfo $fileInfo */ + foreach ($this->files($this->sqlDir) as $fileInfo) { + $name = pathinfo((string) $fileInfo->getRealPath())['filename']; + $sqlId = 'sql-' . $name; + $this->bind(QueryInterface::class)->annotatedWith($name)->toConstructor( + SqlQueryRowList::class, + "sql=$sqlId", + ); + $this->bindCallableItem($name, $sqlId); + $this->bindCallableList($name, $sqlId); + + $sql = (string) ($this->getSql)($fileInfo); + $this->bind()->annotatedWith($sqlId)->toInstance($sql); + } + } + + protected function bindCallableItem(string $name, string $sqlId): void + { + $this->bind(RowInterface::class)->annotatedWith($name)->toConstructor( + SqlQueryRow::class, + "sql=$sqlId", + ); + } + + protected function bindCallableList(string $name, string $sqlId): void + { + $this->bind()->annotatedWith($name)->toConstructor( + SqlQueryRowList::class, + "sql=$sqlId", + ); + $this->bind(RowListInterface::class)->annotatedWith($name)->toConstructor( + SqlQueryRowList::class, + "sql=$sqlId", + ); + } + + /** @psalm-suppress ArgumentTypeCoercion */ + private function files(string $dir): RegexIterator + { + return new RegexIterator( + new RecursiveIteratorIterator( + new RecursiveDirectoryIterator( + $dir, + FilesystemIterator::CURRENT_AS_FILEINFO | FilesystemIterator::KEY_AS_PATHNAME | FilesystemIterator::SKIP_DOTS, + ), + RecursiveIteratorIterator::LEAVES_ONLY, + ), + '/^.+\.sql$/', + RecursiveRegexIterator::MATCH, + ); + } +} diff --git a/src/RowInterfaceProvider.php b/src/RowInterfaceProvider.php index b6dd759..4eb3387 100644 --- a/src/RowInterfaceProvider.php +++ b/src/RowInterfaceProvider.php @@ -23,7 +23,7 @@ final class RowInterfaceProvider implements ProviderInterface /** @var ExtendedPdoInterface */ private $pdo; - /** @var SqlFinder */ + /** @var SqlFinderInterface */ private $finder; /** @var InjectorInterface */ @@ -32,7 +32,7 @@ final class RowInterfaceProvider implements ProviderInterface public function __construct( InjectionPointInterface $ip, ExtendedPdoInterface $pdo, - SqlFinder $finder, + SqlFinderInterface $finder, InjectorInterface $injector ) { $this->ip = $ip; diff --git a/src/RowListInterfaceProvider.php b/src/RowListInterfaceProvider.php index 2380e9e..d4253f3 100644 --- a/src/RowListInterfaceProvider.php +++ b/src/RowListInterfaceProvider.php @@ -24,7 +24,7 @@ final class RowListInterfaceProvider implements ProviderInterface /** @var ExtendedPdoInterface */ private $pdo; - /** @var SqlFinder */ + /** @var SqlFinderInterface */ private $finder; /** @var InjectorInterface */ @@ -33,7 +33,7 @@ final class RowListInterfaceProvider implements ProviderInterface public function __construct( InjectionPointInterface $ip, ExtendedPdoInterface $pdo, - SqlFinder $finder, + SqlFinderInterface $finder, InjectorInterface $injector ) { $this->ip = $ip; diff --git a/src/SqlFinder.php b/src/SqlFinder.php index a83c332..228bcaf 100644 --- a/src/SqlFinder.php +++ b/src/SqlFinder.php @@ -14,7 +14,7 @@ use function file_get_contents; use function sprintf; -final class SqlFinder +final class SqlFinder implements SqlFinderInterface { /** @var ParamReaderInterface */ private $reader; diff --git a/src/SqlFinderInterface.php b/src/SqlFinderInterface.php new file mode 100644 index 0000000..e11544d --- /dev/null +++ b/src/SqlFinderInterface.php @@ -0,0 +1,12 @@ +sqlDir = $sqlDir; - $this->getSql = $getSql ?? static function (SplFileInfo $fileInfo): string { - return (string) file_get_contents($fileInfo->getPathname()); - }; parent::__construct($module); } @@ -39,55 +27,13 @@ public function __construct(string $sqlDir, ?AbstractModule $module = null, ?cal protected function configure() { $this->bind(SqlDir::class)->toInstance(new SqlDir($this->sqlDir)); - /** @var SplFileInfo $fileInfo */ - foreach ($this->files($this->sqlDir) as $fileInfo) { - $name = pathinfo((string) $fileInfo->getRealPath())['filename']; - $sqlId = 'sql-' . $name; - $this->bind(QueryInterface::class)->annotatedWith($name)->toConstructor( - SqlQueryRowList::class, - "sql=$sqlId", - ); - $this->bindCallableItem($name, $sqlId); - $this->bindCallableList($name, $sqlId); - - $sql = (string) ($this->getSql)($fileInfo); - $this->bind()->annotatedWith($sqlId)->toInstance($sql); - } - } - - protected function bindCallableItem(string $name, string $sqlId): void - { - $this->bind(RowInterface::class)->annotatedWith($name)->toConstructor( - SqlQueryRow::class, - "sql=$sqlId", - ); - } - - protected function bindCallableList(string $name, string $sqlId): void - { - $this->bind()->annotatedWith($name)->toConstructor( - SqlQueryRowList::class, - "sql=$sqlId", - ); - $this->bind(RowListInterface::class)->annotatedWith($name)->toConstructor( - SqlQueryRowList::class, - "sql=$sqlId", - ); - } - - /** @psalm-suppress ArgumentTypeCoercion */ - private function files(string $dir): RegexIterator - { - return new RegexIterator( - new RecursiveIteratorIterator( - new RecursiveDirectoryIterator( - $dir, - FilesystemIterator::CURRENT_AS_FILEINFO | FilesystemIterator::KEY_AS_PATHNAME | FilesystemIterator::SKIP_DOTS, - ), - RecursiveIteratorIterator::LEAVES_ONLY, - ), - '/^.+\.sql$/', - RecursiveRegexIterator::MATCH, - ); + $this->bind(SqlFinderInterface::class)->to(SqlFinder::class)->in(Scope::SINGLETON); + $this->bind(ParamReaderInterface::class)->to(ParamReader::class)->in(Scope::SINGLETON); + $this->bind(RowInterface::class)->toProvider(RowInterfaceProvider::class); + $this->bind(RowListInterface::class)->toProvider(RowListInterfaceProvider::class); + $this->bind(InvokeInterface::class)->toProvider(RowListInterfaceProvider::class); + $this->bind(QueryInterface::class)->toProvider(RowListInterfaceProvider::class); + // AOP + $this->install(new SqlQueryInterceptModule()); } } diff --git a/src/SqlQueryProviderModule.php b/src/SqlQueryProviderModule.php deleted file mode 100644 index ed26b7a..0000000 --- a/src/SqlQueryProviderModule.php +++ /dev/null @@ -1,37 +0,0 @@ -sqlDir = $sqlDir; - - parent::__construct($module); - } - - /** - * {@inheritdoc} - */ - protected function configure() - { - $this->bind(SqlDir::class)->toInstance(new SqlDir($this->sqlDir)); - $this->bind(SqlFinder::class)->in(Scope::SINGLETON); - $this->bind(ParamReaderInterface::class)->to(ParamReader::class)->in(Scope::SINGLETON); - $this->bind(RowInterface::class)->toProvider(RowInterfaceProvider::class); - $this->bind(RowListInterface::class)->toProvider(RowListInterfaceProvider::class); - $this->bind(InvokeInterface::class)->toProvider(RowListInterfaceProvider::class); - $this->bind(QueryInterface::class)->toProvider(RowListInterfaceProvider::class); - } -} diff --git a/tests/Fake/FakeTodoRepository.php b/tests/Fake/FakeTodoRepository.php index daff8a7..cd6432b 100644 --- a/tests/Fake/FakeTodoRepository.php +++ b/tests/Fake/FakeTodoRepository.php @@ -24,7 +24,7 @@ class FakeTodoRepository public $todoList; public function __construct( - InvokeInterface $todoCreate, + QueryInterface $todoCreate, RowInterface $todoItem, RowListInterface $todoList ){ diff --git a/tests/Iso8601FormatModuleTest.php b/tests/Iso8601FormatModuleTest.php index dccd15e..817b143 100644 --- a/tests/Iso8601FormatModuleTest.php +++ b/tests/Iso8601FormatModuleTest.php @@ -42,7 +42,7 @@ public function __construct(ExtendedPdo $pdo) protected function configure() { $this->bind(ExtendedPdoInterface::class)->toInstance($this->pdo); - $this->install(new SqlQueryModule(__DIR__ . '/Fake/sql')); + $this->install(new CallableQueryModule(__DIR__ . '/Fake/sql')); $this->install(new Iso8601FormatModule(['created_at'])); } }; diff --git a/tests/SqlQueryModuleTest.php b/tests/SqlQueryModuleTest.php index f77112b..260fca4 100644 --- a/tests/SqlQueryModuleTest.php +++ b/tests/SqlQueryModuleTest.php @@ -6,24 +6,19 @@ use Aura\Sql\ExtendedPdo; use Aura\Sql\ExtendedPdoInterface; -use Aura\Sql\PdoInterface; -use InvalidArgumentException; use PDO; use PHPUnit\Framework\TestCase; use Ray\Di\AbstractModule; use Ray\Di\Injector; +use Ray\Query\Exception\SqlFileNotFoundException; +use Ray\Query\Exception\SqlNotAnnotatedException; -use function assert; -use function print_r; +use function count; class SqlQueryModuleTest extends TestCase { - /** @var ExtendedPdo */ - protected $pdo; - - /** @var AbstractModule */ - protected $module; - + /** @var AbstractModule */ + private $module; protected function setUp(): void { $pdo = new ExtendedPdo('sqlite::memory:'); @@ -33,7 +28,6 @@ protected function setUp(): void id INTEGER, title TEXT )'); - $pdo->perform('INSERT INTO todo (id, title) VALUES (:id, :title)', ['id' => '1', 'title' => 'run']); $this->module = new class ($pdo) extends AbstractModule { /** @var ExtendedPdo */ private $pdo; @@ -47,157 +41,47 @@ protected function configure() { $this->bind(ExtendedPdoInterface::class)->toInstance($this->pdo); $this->install(new SqlQueryModule(__DIR__ . '/Fake/sql')); - $this->install(new SqlQueryInterceptModule()); } }; } - public function testRowInterfaceInject(): void + public function testProviderInject(): void { $injector = new Injector($this->module, __DIR__ . '/tmp'); - $todo = $injector->getInstance(FakeTodo::class); - /** @var FakeQuery $todo */ - $actual = $todo->create('2', 'think'); - $this->assertSame([], $actual); + $todo = $injector->getInstance(FakeTodoRepository::class); + $this->todoTest($todo); } - public function testCallableInject(): void + /** @requires PHP 8.1 */ + public function testProviderInjectAttr(): void { $injector = new Injector($this->module, __DIR__ . '/tmp'); - $todo = $injector->getInstance(FakeTodo::class); - /** @var FakeQuery $todo */ - $actural = $todo->get('1'); - $expected = [ - 'id' => '1', - 'title' => 'run', - ]; - $this->assertSame($expected, $actural); + $todo = $injector->getInstance(FakeTodoRepositoryAttr::class); + $this->todoTest($todo); } - public function testAssistedQueryInterface(): void + public function todoTest(object $todo): void { - $injector = new Injector($this->module, __DIR__ . '/tmp'); - $todo = $injector->getInstance(FakeQuery::class); - /** @var FakeQuery $todo */ - $actual = $todo->create('2', 'think'); - $this->assertSame([], $actual); - } - - public function testAssistedQuery(): void - { - $injector = new Injector($this->module, __DIR__ . '/tmp'); - $todo = $injector->getInstance(FakeQuery::class); - /** @var FakeQuery $todo */ - $actual = $todo->get('1')[0]['title']; - $this->assertSame('run', $actual); + /** @var FakeTodoRepository $todo */ + ($todo->todoCreate)(['id' => 1, 'title' => 'think']); + ($todo->todoCreate)(['id' => 2, 'title' => 'travel']); + $list = ($todo->todoList)([]); + $this->assertSame(2, count($list)); + $item = ($todo->todoItem)(['id' => 2]); + $this->assertSame('travel', $item['title']); } - public function testRowInterface(): void + public function testSqlFileNotFoundException(): void { + $this->expectException(SqlFileNotFoundException::class); $injector = new Injector($this->module, __DIR__ . '/tmp'); - $item = $injector->getInstance(FakeItem::class); - /** @var FakeItem $item */ - $actual = $item(['id' => '1']); - $this->assertSame(['id' => '1', 'title' => 'run'], $actual); + $injector->getInstance(FakeTodoProviderSqlNotFound::class); } - public function testRowListInterface(): void + public function testSqlNotAnnotated(): void { + $this->expectException(SqlNotAnnotatedException::class); $injector = new Injector($this->module, __DIR__ . '/tmp'); - $item = $injector->getInstance(FakeList::class); - /** @var FakeItem $item */ - $actual = $item(['id' => '1']); - $this->assertSame([['id' => '1', 'title' => 'run']], $actual); - } - - public function testSqlAliasInterceptor(): void - { - $injector = new Injector($this->module, __DIR__ . '/tmp'); - /** @var FakeAlias $fakeAlias */ - $fakeAlias = $injector->getInstance(FakeAlias::class); - $actual = $fakeAlias->get('1'); - $expected = [ - 'id' => '1', - 'title' => 'run', - ]; - $this->assertSame($expected, $actual); - } - - public function testSqlAliasInterceptorWithNamed(): FakeAliasNamed - { - $injector = new Injector($this->module, __DIR__ . '/tmp'); - /** @var FakeAliasNamed $fakeAlias */ - $fakeAlias = $injector->getInstance(FakeAliasNamed::class); - $actual = $fakeAlias->get('1'); - $expected = [ - 'id' => '1', - 'title' => 'run', - ]; - $this->assertSame($expected, $actual); - - return $fakeAlias; - } - - /** @depends testSqlAliasInterceptorWithNamed */ - public function testTempalteError(FakeAliasNamed $ro): void - { - $this->expectException(InvalidArgumentException::class); - $ro->templteError('1'); - } - - public function testResourceObject200(): void - { - $injector = new Injector($this->module, __DIR__ . '/tmp'); - /** @var FakeRo $ro */ - $ro = $injector->getInstance(FakeRo::class); - $response = $ro->onGet('1'); - $this->assertSame(200, $response->code); - $this->assertSame(['id' => '1', 'title' => 'run'], $response->body); - $this->assertSame('{"id":"1","title":"run"}', (string) $response); - } - - public function testResourceObject404(): void - { - $injector = new Injector($this->module, __DIR__ . '/tmp'); - /** @var FakeRo $ro */ - $ro = $injector->getInstance(FakeRo::class); - $response = $ro->onGet('2'); - $this->assertSame(404, $response->code); - $this->assertSame([], $response->body); - $this->assertSame('[]', (string) $response); - } - - public function testDevSqlModule(): void - { - $pdo = new ExtendedPdo('sqlite::memory:'); - $pdo->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, true); - $pdo->query('CREATE TABLE IF NOT EXISTS todo ( - id INTEGER, - title TEXT -)'); - $pdo->perform('INSERT INTO todo (id, title) VALUES (:id, :title)', ['id' => '1', 'title' => 'run']); - $module = new class ($pdo) extends AbstractModule { - /** @var ExtendedPdo */ - private $pdo; - - public function __construct(ExtendedPdo $pdo) - { - $this->pdo = $pdo; - } - - protected function configure() - { - $this->bind(ExtendedPdoInterface::class)->toInstance($this->pdo); - $this->install(new SqlQueryModule(__DIR__ . '/Fake/sql', null, new SqlFileName())); - } - }; - $injector = (new Injector($module)); - $todo = $injector->getInstance(FakeTodo::class); - assert($todo instanceof FakeTodo); - $pdo = $injector->getInstance(ExtendedPdoInterface::class); - assert($pdo instanceof PdoInterface); - $todo->create('1', 'a'); - $todo->get('1'); - $this->assertStringContainsString('/* todo_item_by_id.sql */ SELECT * FROM todo WHERE id = :id', print_r($todo, true)); + $injector->getInstance(FakeTodoProviderSqlNotAnnotated::class); } } diff --git a/tests/SqlQueryProviderModuleTest.php b/tests/SqlQueryProviderModuleTest.php deleted file mode 100644 index f5f24c0..0000000 --- a/tests/SqlQueryProviderModuleTest.php +++ /dev/null @@ -1,88 +0,0 @@ -setAttribute(PDO::ATTR_STRINGIFY_FETCHES, true); - $pdo->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, true); - $pdo->query('CREATE TABLE IF NOT EXISTS todo ( - id INTEGER, - title TEXT -)'); - $this->module = new class ($pdo) extends AbstractModule { - /** @var ExtendedPdo */ - private $pdo; - - public function __construct(ExtendedPdo $pdo) - { - $this->pdo = $pdo; - } - - protected function configure() - { - $this->bind(ExtendedPdoInterface::class)->toInstance($this->pdo); - $this->install(new SqlQueryModule(__DIR__ . '/Fake/sql')); - $this->install(new SqlQueryProviderModule(__DIR__ . '/Fake/sql')); - } - }; - } - - public function testProviderInject(): void - { - $injector = new Injector($this->module, __DIR__ . '/tmp'); - $todo = $injector->getInstance(FakeTodoRepository::class); - $this->todoTest($todo); - } - - /** @requires PHP 8.1 */ - public function testProviderInjectAttr(): void - { - $injector = new Injector($this->module, __DIR__ . '/tmp'); - $todo = $injector->getInstance(FakeTodoRepositoryAttr::class); - $this->todoTest($todo); - } - - public function todoTest(object $todo): void - { - /** @var FakeTodoRepository $todo */ - ($todo->todoCreate)(['id' => 1, 'title' => 'think']); - ($todo->todoCreate)(['id' => 2, 'title' => 'travel']); - $list = ($todo->todoList)([]); - $this->assertSame(2, count($list)); - $item = ($todo->todoItem)(['id' => 2]); - $this->assertSame('travel', $item['title']); - } - - public function testSqlFileNotFoundException(): void - { - $this->expectException(SqlFileNotFoundException::class); - $injector = new Injector($this->module, __DIR__ . '/tmp'); - $injector->getInstance(FakeTodoProviderSqlNotFound::class); - } - - public function testSqlNotAnnotated(): void - { - $this->expectException(SqlNotAnnotatedException::class); - $injector = new Injector($this->module, __DIR__ . '/tmp'); - $injector->getInstance(FakeTodoProviderSqlNotAnnotated::class); - } -} From 0a148d7a1e5e5da16e03f24d720b82a5eed971b1 Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Tue, 11 Jun 2024 22:15:42 +0900 Subject: [PATCH 12/34] Updated references to SqlQueryProviderModule to SqlQueryModule In this commit, references and calls to SqlQueryProviderModule across the project were replaced with SqlQueryModule. Moreover, some of the SqlQueryModule instances have been switched to CallableQueryModule for better compatibility with the Named attribute. --- README.md | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index ae959df..ef83968 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,7 @@ class AppModule extends AbstractModule protected function configure(): void { // SqlQueryModule install - $this->install(new SqlQueryProviderModule($sqlDir)); + $this->install(new SqlQueryModule($sqlDir)); } } ``` @@ -129,17 +129,16 @@ class Todos Entire method invocation can be override with callable object in specified with `#[Query]`. - ```php use Ray\Di\AbstractModule; -use Ray\Query\SqlQueryProviderModule; +use Ray\Query\SqlQueryModule; use Ray\Query\SqlQueryInterceptModule; class AppModule extends AbstractModule { protected function configure(): void { - $this->install(new SqlQueryProviderModule($sqlDir)); + $this->install(new SqlQueryModule($sqlDir)); $this->SqlQueryInterceptModule(); } } @@ -189,7 +188,7 @@ In the following example, an invocation object of `$createTodo` which makes` POS ```php use Ray\Di\AbstractModule; -use Ray\Query\SqlQueryModule; +use Ray\Query\CallableQueryModule; class AppModule extends AbstractModule { @@ -280,13 +279,13 @@ $this->install(new Iso8601FormatModule(['created_at', 'updated_at'])); ## SQL file name log -The SQL file name can be appended to the SQL statement as a comment. This is useful for query logging. +Binding to `#[Named]`, which was supported until `0.9`, is possible with `CallableQueryModule`. ```php use Ray\Query\SqlFileName; -use Ray\Query\SqlQueryModule; +use Ray\Query\CallableQueryModule; -$this->install(new SqlQueryModule(__DIR__ . '/Fake/sql', null, new SqlFileName())); +$this->install(new CallableQueryModule(__DIR__ . '/Fake/sql')); ``` Execute SQL From 375c78eebf1780dc9b577c6d380cd97b7fd780be Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Tue, 11 Jun 2024 22:15:51 +0900 Subject: [PATCH 13/34] Update phpstan.neon configuration file The phpstan.neon configuration file has been updated. The "excludes_analyse" parameter has been replaced with the "excludePaths" parameter to ensure proper paths exclusion during the code analysis. --- phpstan.neon | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/phpstan.neon b/phpstan.neon index 8978572..6e01048 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -3,6 +3,6 @@ parameters: paths: - src - tests - excludes_analyse: + excludePaths: - %currentWorkingDirectory%/tests/tmp/* - %currentWorkingDirectory%/tests/Fake/* From 5f2c975cddcc0bdfbba95284e80d2a4918c48823 Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Tue, 11 Jun 2024 22:16:11 +0900 Subject: [PATCH 14/34] Replace SqlQueryModule with CallableQueryModule Updated the code to replace all instances of SqlQueryModule with CallableQueryModule. In addition, the constructor parameters in Todo class were refactored to use private properties instead --- demo/1-constructor-injection.php | 11 +++++------ demo/2-query-annotation.php | 2 +- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/demo/1-constructor-injection.php b/demo/1-constructor-injection.php index a114948..1efe465 100644 --- a/demo/1-constructor-injection.php +++ b/demo/1-constructor-injection.php @@ -8,6 +8,7 @@ use Ray\Di\Injector; use Ray\Query\Annotation\Sql; use Ray\Query\QueryInterface; +use Ray\Query\CallableQueryModule; use Ray\Query\SqlQueryModule; require dirname(__DIR__) . '/vendor/autoload.php'; @@ -15,12 +16,9 @@ class Todo { public function __construct( - #[Sql('todo_insert') QueryInterface $createTodo, - #[Sql('todo_item_by_id') QueryInterface $todoItem, - ) { - $this->createTodo = $createTodo; - $this->todoItem = $todoItem; - } + #[Sql('todo_insert')] private QueryInterface $createTodo, + #[Sql('todo_item_by_id')] private QueryInterface $todoItem, + ) {} public function get(string $uuid) : array { @@ -40,6 +38,7 @@ public function create(string $uuid, string $title) protected function configure() { $this->install(new Ray\AuraSqlModule\AuraSqlModule('sqlite::memory:')); + $this->install(new CallableQueryModule(dirname(__DIR__ . '/sql'))); $this->install(new SqlQueryModule(dirname(__DIR__ . '/sql'))); } }); diff --git a/demo/2-query-annotation.php b/demo/2-query-annotation.php index a379b0c..2b9516e 100644 --- a/demo/2-query-annotation.php +++ b/demo/2-query-annotation.php @@ -17,7 +17,7 @@ protected function configure() { $this->install(new AuraSqlModule('sqlite::memory:')); - $this->install(new SqlQueryModule(dirname(__DIR__ . '/sql'))); + $this->install(new CallableQueryModule(dirname(__DIR__ . '/sql'))); $this->bind(Todo::class); } }); From b76feb13828b9cb82af58bd55eb3a873c70ef4ad Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Tue, 11 Jun 2024 22:57:54 +0900 Subject: [PATCH 15/34] Refactor QueryInterceptor class and update SqlQueryRow QueryInterceptor class has been refactored to streamline its structure, removing dependency of InjectorInterface and introducing ExtendedPdoInterface and SqlDir. Also, an explicit count equality check was added in SqlQueryRow to improve code readability and maintainability. --- src/QueryInterceptor.php | 48 ++++++++++++++++++++++++++++++---------- src/SqlQueryRow.php | 2 +- 2 files changed, 37 insertions(+), 13 deletions(-) diff --git a/src/QueryInterceptor.php b/src/QueryInterceptor.php index 52944b0..c58072b 100644 --- a/src/QueryInterceptor.php +++ b/src/QueryInterceptor.php @@ -4,26 +4,38 @@ namespace Ray\Query; +use Aura\Sql\ExtendedPdoInterface; use BEAR\Resource\ResourceObject; use InvalidArgumentException; use Ray\Aop\MethodInterceptor; use Ray\Aop\MethodInvocation; -use Ray\Di\InjectorInterface; use Ray\Query\Annotation\Query; +use Ray\Query\Exception\SqlFileNotFoundException; use function assert; +use function file_exists; +use function file_get_contents; use function is_string; use function parse_str; use function parse_url; +use function sprintf; +use function strpos; +use function strstr; class QueryInterceptor implements MethodInterceptor { - /** @var InjectorInterface */ - private $injector; - - public function __construct(InjectorInterface $injector) - { - $this->injector = $injector; + /** @var SqlDir */ + private $sqlDir; + + /** @var ExtendedPdoInterface */ + private $pdo; + + public function __construct( + ExtendedPdoInterface $pdo, + SqlDir $sqlDir + ) { + $this->sqlDir = $sqlDir; + $this->pdo = $pdo; } /** @return ResourceObject|mixed */ @@ -32,17 +44,29 @@ public function invoke(MethodInvocation $invocation) $method = $invocation->getMethod(); /** @var Query $query */ $query = $method->getAnnotation(Query::class); + + $queryId = $query->id; + if (strpos($queryId, '?') !== false) { + $queryId = strstr($queryId, '?', true); + } + + $file = sprintf('%s/%s.sql', $this->sqlDir->value, $queryId); + if (! file_exists($file)) { + throw new SqlFileNotFoundException($query->id, $query->id); + } + + $sql = (string) file_get_contents($file); + $query = $invocation->getMethod()->getAnnotation(Query::class); + assert($query instanceof Query); /** @var array $namedArguments */ $namedArguments = (array) $invocation->getNamedArguments(); [$queryId, $params] = $query->templated ? $this->templated($query, $namedArguments) : [$query->id, $namedArguments]; - $interface = $query->type === 'row' ? RowInterface::class : RowListInterface::class; + $sqlQuery = $query->type === 'row' ? new SqlQueryRow($this->pdo, $sql) : new SqlQueryRowList($this->pdo, $sql); assert(is_string($queryId)); - /** @var RowInterface|RowListInterface|object $query */ - $query = $this->injector->getInstance($interface, $queryId); - assert($query instanceof QueryInterface); + assert($sqlQuery instanceof QueryInterface); /** @var array $params */ - return $this->getQueryResult($invocation, $query, $params); + return $this->getQueryResult($invocation, $sqlQuery, $params); } /** diff --git a/src/SqlQueryRow.php b/src/SqlQueryRow.php index 5b33757..e9ef701 100644 --- a/src/SqlQueryRow.php +++ b/src/SqlQueryRow.php @@ -30,7 +30,7 @@ public function __invoke(array ...$queries): iterable { $query = $queries[0]; $item = $this->pdo->fetchAssoc($this->sql, $query); - if (! count($item)) { + if (count($item) === 0) { return []; } From 98490675ea0ac3c3e53045d5152fe7c6bc99896a Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Tue, 11 Jun 2024 22:58:05 +0900 Subject: [PATCH 16/34] Replace CallableQueryModule with SqlQueryModule In both 1-constructor-injection.php and 2-query-annotation.php files, the CallableQueryModule was replaced with SqlQueryModule. This change simplifies the code by reducing redundancy and using a more appropriate module for handling SQL operations. --- demo/1-constructor-injection.php | 3 +-- demo/2-query-annotation.php | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/demo/1-constructor-injection.php b/demo/1-constructor-injection.php index 1efe465..bca012d 100644 --- a/demo/1-constructor-injection.php +++ b/demo/1-constructor-injection.php @@ -38,8 +38,7 @@ public function create(string $uuid, string $title) protected function configure() { $this->install(new Ray\AuraSqlModule\AuraSqlModule('sqlite::memory:')); - $this->install(new CallableQueryModule(dirname(__DIR__ . '/sql'))); - $this->install(new SqlQueryModule(dirname(__DIR__ . '/sql'))); + $this->install(new SqlQueryModule(__DIR__ . '/sql')); } }); /** @var Todo $todo */ diff --git a/demo/2-query-annotation.php b/demo/2-query-annotation.php index 2b9516e..c0642be 100644 --- a/demo/2-query-annotation.php +++ b/demo/2-query-annotation.php @@ -17,7 +17,7 @@ protected function configure() { $this->install(new AuraSqlModule('sqlite::memory:')); - $this->install(new CallableQueryModule(dirname(__DIR__ . '/sql'))); + $this->install(new SqlQueryModule(__DIR__ . '/sql')); $this->bind(Todo::class); } }); From b264f63a48ec7d202c1a025000247e2012b4e61f Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Wed, 12 Jun 2024 07:45:13 +0900 Subject: [PATCH 17/34] Add SQL query interception tests and refactor interceptor The commit introduces a new SQL Query Interception test case with various scenarios. The interceptor class `QueryInterceptor` is also refactored, simplifying some method calls and optimizing the way the SQL file is loaded. Additionally, Query methods were added to FakeRo, FakeTodo, and the newly added FakeBar class for testing purposes. --- src/QueryInterceptor.php | 39 +++++------- tests/Fake/FakeBar.php | 27 ++++++++ tests/Fake/FakeRo.php | 19 ++++++ tests/Fake/FakeTodo.php | 11 ++++ tests/SqlQueryInterceptModuleTest.php | 88 +++++++++++++++++++++++++++ tests/SqlQueryModuleTest.php | 2 +- 6 files changed, 162 insertions(+), 24 deletions(-) create mode 100644 tests/Fake/FakeBar.php create mode 100644 tests/SqlQueryInterceptModuleTest.php diff --git a/src/QueryInterceptor.php b/src/QueryInterceptor.php index c58072b..ec3d8c9 100644 --- a/src/QueryInterceptor.php +++ b/src/QueryInterceptor.php @@ -6,9 +6,9 @@ use Aura\Sql\ExtendedPdoInterface; use BEAR\Resource\ResourceObject; -use InvalidArgumentException; use Ray\Aop\MethodInterceptor; use Ray\Aop\MethodInvocation; +use Ray\Aop\ReflectionMethod; use Ray\Query\Annotation\Query; use Ray\Query\Exception\SqlFileNotFoundException; @@ -19,8 +19,6 @@ use function parse_str; use function parse_url; use function sprintf; -use function strpos; -use function strstr; class QueryInterceptor implements MethodInterceptor { @@ -44,26 +42,12 @@ public function invoke(MethodInvocation $invocation) $method = $invocation->getMethod(); /** @var Query $query */ $query = $method->getAnnotation(Query::class); - - $queryId = $query->id; - if (strpos($queryId, '?') !== false) { - $queryId = strstr($queryId, '?', true); - } - - $file = sprintf('%s/%s.sql', $this->sqlDir->value, $queryId); - if (! file_exists($file)) { - throw new SqlFileNotFoundException($query->id, $query->id); - } - - $sql = (string) file_get_contents($file); - $query = $invocation->getMethod()->getAnnotation(Query::class); - assert($query instanceof Query); /** @var array $namedArguments */ $namedArguments = (array) $invocation->getNamedArguments(); [$queryId, $params] = $query->templated ? $this->templated($query, $namedArguments) : [$query->id, $namedArguments]; - $sqlQuery = $query->type === 'row' ? new SqlQueryRow($this->pdo, $sql) : new SqlQueryRowList($this->pdo, $sql); assert(is_string($queryId)); - assert($sqlQuery instanceof QueryInterface); + $sql = $this->getsql($queryId, $method); + $sqlQuery = $query->type === 'row' ? new SqlQueryRow($this->pdo, $sql) : new SqlQueryRowList($this->pdo, $sql); /** @var array $params */ return $this->getQueryResult($invocation, $sqlQuery, $params); @@ -79,6 +63,7 @@ private function getQueryResult(MethodInvocation $invocation, QueryInterface $qu /** @psalm-suppress MixedAssignment */ $result = $query($param); $object = $invocation->getThis(); + if ($object instanceof ResourceObject) { return $this->returnRo($object, $invocation, $result); } @@ -111,18 +96,26 @@ private function return404(ResourceObject $ro): ResourceObject /** * @param array $namedArguments * - * @return array + * @return array */ private function templated(Query $query, array $namedArguments): array { $url = parse_url(uri_template($query->id, $namedArguments)); - if (! isset($url['path'])) { - throw new InvalidArgumentException($query->id); - } + assert(isset($url['path'])); $queryId = $url['path']; isset($url['query']) ? parse_str($url['query'], $params) : $params = $namedArguments; return [$queryId, $params + $namedArguments]; } + + private function getsql(string $queryId, ReflectionMethod $method): string + { + $file = sprintf('%s/%s.sql', $this->sqlDir->value, $queryId); + if (! file_exists($file)) { + throw new SqlFileNotFoundException((string) $method, $queryId); + } + + return (string) file_get_contents($file); + } } diff --git a/tests/Fake/FakeBar.php b/tests/Fake/FakeBar.php new file mode 100644 index 0000000..b4aa2f6 --- /dev/null +++ b/tests/Fake/FakeBar.php @@ -0,0 +1,27 @@ + $title ]); } + + /** + * @Query(id="todo_item_by_id", type="row") + */ + #[Query('todo_item_by_id', type: 'row')] + public function getIntercepted(string $id) + { + return $this; + } + } diff --git a/tests/SqlQueryInterceptModuleTest.php b/tests/SqlQueryInterceptModuleTest.php new file mode 100644 index 0000000..27bfca2 --- /dev/null +++ b/tests/SqlQueryInterceptModuleTest.php @@ -0,0 +1,88 @@ +setAttribute(PDO::ATTR_STRINGIFY_FETCHES, true); + $pdo->query('CREATE TABLE IF NOT EXISTS todo ( + id INTEGER, + title TEXT +)'); + $pdo->perform('INSERT INTO todo (id, title) VALUES (:id, :title) ', ['id' => 1, 'title' => 'run']); + $module = new class ($pdo) extends AbstractModule { + /** @var ExtendedPdo */ + private $pdo; + + public function __construct(ExtendedPdo $pdo) + { + $this->pdo = $pdo; + } + + protected function configure() + { + $this->bind(ExtendedPdoInterface::class)->toInstance($this->pdo); + $this->install(new SqlQueryModule(__DIR__ . '/Fake/sql')); + $this->bind(FakeBar::class); + } + }; + $injector = new Injector($module, __DIR__ . '/tmp'); + $this->fakeRo = $injector->getInstance(FakeRo::class); + $this->fakeBar = $injector->getInstance(FakeBar::class); + } + + public function testResourceObject200(): void + { + $response = $this->fakeRo->onGet('1'); + $this->assertSame(200, $response->code); + $this->assertSame(['id' => '1', 'title' => 'run'], $response->body); + $this->assertSame('{"id":"1","title":"run"}', (string) $response); + } + + public function testResourceObject404(): void + { + $response = $this->fakeRo->onGet('2'); + $this->assertSame(404, $response->code); + $this->assertSame([], $response->body); + $this->assertSame('[]', (string) $response); + } + + public function testNoSqlFile(): void + { + $this->expectException(SqlFileNotFoundException::class); + $this->fakeRo->noSql(); + } + + public function testWithQuery(): void + { + $response = $this->fakeRo->withQuery('1'); + $this->assertSame(200, $response->code); + $this->assertSame(['id' => '1', 'title' => 'run'], $response->body); + $this->assertSame('{"id":"1","title":"run"}', (string) $response); + } + + public function testNonResourceObject(): void + { + $response = $this->fakeBar->getIntercepted('1'); + $this->assertSame(['id' => '1', 'title' => 'run'], $response); + } +} diff --git a/tests/SqlQueryModuleTest.php b/tests/SqlQueryModuleTest.php index 260fca4..c24b1bf 100644 --- a/tests/SqlQueryModuleTest.php +++ b/tests/SqlQueryModuleTest.php @@ -23,10 +23,10 @@ protected function setUp(): void { $pdo = new ExtendedPdo('sqlite::memory:'); $pdo->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, true); - $pdo->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, true); $pdo->query('CREATE TABLE IF NOT EXISTS todo ( id INTEGER, title TEXT + )'); $this->module = new class ($pdo) extends AbstractModule { /** @var ExtendedPdo */ From 730573f59cf1c82564e1310cd832779dfe261d5a Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Wed, 12 Jun 2024 08:58:12 +0900 Subject: [PATCH 18/34] Add GetSqlInterface and its implementations New interface GetSqlInterface is introduced along with its two implementations - GetSql and DevGetSql. The SqlQueryModule and CallableQueryModule have been modified to bind GetSqlInterface with GetSql. DevSqlQueryModule and relevant tests are also added. This interface allows for better encapsulation of SQL fetch operations. --- src/CallableQueryModule.php | 2 ++ src/DevGetSql.php | 23 +++++++++++++++++++++++ src/DevSqlQueryModule.php | 19 +++++++++++++++++++ src/GetSql.php | 18 ++++++++++++++++++ src/GetSqlInterface.php | 10 ++++++++++ src/SqlFinder.php | 10 +++++++--- src/SqlQueryModule.php | 1 + tests/SqlQueryModuleTest.php | 34 ++++++++++++++++++++++++++++++++++ 8 files changed, 114 insertions(+), 3 deletions(-) create mode 100644 src/DevGetSql.php create mode 100644 src/DevSqlQueryModule.php create mode 100644 src/GetSql.php create mode 100644 src/GetSqlInterface.php diff --git a/src/CallableQueryModule.php b/src/CallableQueryModule.php index 22ad3e0..48829b1 100644 --- a/src/CallableQueryModule.php +++ b/src/CallableQueryModule.php @@ -6,6 +6,7 @@ use FilesystemIterator; use Ray\Di\AbstractModule; +use Ray\Di\Scope; use RecursiveDirectoryIterator; use RecursiveIteratorIterator; use RecursiveRegexIterator; @@ -38,6 +39,7 @@ public function __construct(string $sqlDir, ?AbstractModule $module = null, ?cal */ protected function configure() { + $this->bind(GetSqlInterface::class)->to(GetSql::class)->in(Scope::SINGLETON); $this->bind(SqlDir::class)->toInstance(new SqlDir($this->sqlDir)); /** @var SplFileInfo $fileInfo */ foreach ($this->files($this->sqlDir) as $fileInfo) { diff --git a/src/DevGetSql.php b/src/DevGetSql.php new file mode 100644 index 0000000..0857f21 --- /dev/null +++ b/src/DevGetSql.php @@ -0,0 +1,23 @@ +getFilename(), (string) file_get_contents($filePath)); + } +} diff --git a/src/DevSqlQueryModule.php b/src/DevSqlQueryModule.php new file mode 100644 index 0000000..b7810f6 --- /dev/null +++ b/src/DevSqlQueryModule.php @@ -0,0 +1,19 @@ +bind(GetSqlInterface::class)->to(DevGetSql::class)->in(Scope::SINGLETON); + } +} diff --git a/src/GetSql.php b/src/GetSql.php new file mode 100644 index 0000000..5ae338e --- /dev/null +++ b/src/GetSql.php @@ -0,0 +1,18 @@ + $reader */ public function __construct( ParamReaderInterface $reader, - SqlDir $sqlDir + SqlDir $sqlDir, + GetSqlInterface $getSql ) { $this->reader = $reader; $this->sqlDir = $sqlDir; + $this->getSql = $getSql; } public function __invoke(ReflectionParameter $param): string @@ -46,6 +50,6 @@ public function __invoke(ReflectionParameter $param): string throw new SqlFileNotFoundException($msg, $sqlAnnotation->sql); } - return (string) file_get_contents($file); + return ($this->getSql)($file); } } diff --git a/src/SqlQueryModule.php b/src/SqlQueryModule.php index aa66e96..ab46b10 100644 --- a/src/SqlQueryModule.php +++ b/src/SqlQueryModule.php @@ -26,6 +26,7 @@ public function __construct(string $sqlDir, ?AbstractModule $module = null) */ protected function configure() { + $this->bind(GetSqlInterface::class)->to(GetSql::class)->in(Scope::SINGLETON); $this->bind(SqlDir::class)->toInstance(new SqlDir($this->sqlDir)); $this->bind(SqlFinderInterface::class)->to(SqlFinder::class)->in(Scope::SINGLETON); $this->bind(ParamReaderInterface::class)->to(ParamReader::class)->in(Scope::SINGLETON); diff --git a/tests/SqlQueryModuleTest.php b/tests/SqlQueryModuleTest.php index c24b1bf..a0be32a 100644 --- a/tests/SqlQueryModuleTest.php +++ b/tests/SqlQueryModuleTest.php @@ -13,7 +13,9 @@ use Ray\Query\Exception\SqlFileNotFoundException; use Ray\Query\Exception\SqlNotAnnotatedException; +use function assert; use function count; +use function print_r; class SqlQueryModuleTest extends TestCase { @@ -84,4 +86,36 @@ public function testSqlNotAnnotated(): void $injector = new Injector($this->module, __DIR__ . '/tmp'); $injector->getInstance(FakeTodoProviderSqlNotAnnotated::class); } + + public function testDevSqlModule(): void + { + $pdo = new ExtendedPdo('sqlite::memory:'); + $pdo->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, true); + $pdo->query('CREATE TABLE IF NOT EXISTS todo ( + id INTEGER, + title TEXT +)'); + $pdo->perform('INSERT INTO todo (id, title) VALUES (:id, :title)', ['id' => '1', 'title' => 'run']); + $module = new class ($pdo) extends AbstractModule { + /** @var ExtendedPdo */ + private $pdo; + + public function __construct(ExtendedPdo $pdo) + { + $this->pdo = $pdo; + } + + protected function configure() + { + $this->bind(ExtendedPdoInterface::class)->toInstance($this->pdo); + $this->install(new SqlQueryModule(__DIR__ . '/Fake/sql', null)); + $this->override(new DevSqlQueryModule()); + } + }; + $injector = (new Injector($module)); + $todo = $injector->getInstance(FakeTodoRepository::class); + assert($todo instanceof FakeTodoRepository); + ($todo->todoCreate)(['id' => 2, 'title' => 'think']); + $this->assertStringContainsString('/* todo_item_by_id.sql */ SELECT * FROM todo WHERE id = :id', print_r($todo, true)); + } } From 2fd987f392d58961ab2f72efc8a477766d622691 Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Wed, 12 Jun 2024 10:33:49 +0900 Subject: [PATCH 19/34] Trim SQL file content and update SQL query handling In this update, the contents of the SQL files are trimmed when loaded for processing. This removes any unnecessary white spaces, hence streamlining the content. The handling of SQL queries was also updated - specifically, leading comments in SQL queries are properly removed. These changes aim to refine the data processing and improve the overall efficiency and accuracy of the script. --- src/CallableQueryModule.php | 5 +++-- src/DevGetSql.php | 3 ++- src/SqlFileName.php | 3 ++- src/SqlQueryRowList.php | 5 ++++- tests/Iso8601FormatModuleTest.php | 2 +- 5 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/CallableQueryModule.php b/src/CallableQueryModule.php index 48829b1..d458c9b 100644 --- a/src/CallableQueryModule.php +++ b/src/CallableQueryModule.php @@ -15,6 +15,7 @@ use function file_get_contents; use function pathinfo; +use function trim; class CallableQueryModule extends AbstractModule { @@ -28,7 +29,7 @@ public function __construct(string $sqlDir, ?AbstractModule $module = null, ?cal { $this->sqlDir = $sqlDir; $this->getSql = $getSql ?? static function (SplFileInfo $fileInfo): string { - return (string) file_get_contents($fileInfo->getPathname()); + return trim((string) file_get_contents($fileInfo->getPathname())); }; parent::__construct($module); @@ -52,7 +53,7 @@ protected function configure() $this->bindCallableItem($name, $sqlId); $this->bindCallableList($name, $sqlId); - $sql = (string) ($this->getSql)($fileInfo); + $sql = trim((string) ($this->getSql)($fileInfo)); $this->bind()->annotatedWith($sqlId)->toInstance($sql); } } diff --git a/src/DevGetSql.php b/src/DevGetSql.php index 0857f21..335c630 100644 --- a/src/DevGetSql.php +++ b/src/DevGetSql.php @@ -8,6 +8,7 @@ use function file_get_contents; use function sprintf; +use function trim; final class DevGetSql implements GetSqlInterface { @@ -18,6 +19,6 @@ public function __invoke(string $filePath): string { $fileInfo = new SplFileInfo($filePath); - return sprintf('/* %s */ %s', $fileInfo->getFilename(), (string) file_get_contents($filePath)); + return sprintf('/* %s */ %s', $fileInfo->getFilename(), trim((string) file_get_contents($filePath))); } } diff --git a/src/SqlFileName.php b/src/SqlFileName.php index 8f2c4e3..ab45591 100644 --- a/src/SqlFileName.php +++ b/src/SqlFileName.php @@ -8,6 +8,7 @@ use function file_get_contents; use function sprintf; +use function trim; final class SqlFileName { @@ -16,6 +17,6 @@ final class SqlFileName */ public function __invoke(SplFileInfo $fileInfo): string { - return sprintf('/* %s */ %s', $fileInfo->getFilename(), (string) file_get_contents($fileInfo->getPathname())); + return sprintf('/* %s */ %s', $fileInfo->getFilename(), trim((string) file_get_contents($fileInfo->getPathname()))); } } diff --git a/src/SqlQueryRowList.php b/src/SqlQueryRowList.php index fa62e10..c79bd7b 100644 --- a/src/SqlQueryRowList.php +++ b/src/SqlQueryRowList.php @@ -12,6 +12,7 @@ use function array_pop; use function count; use function explode; +use function preg_replace; use function strpos; use function strtolower; use function trim; @@ -53,7 +54,9 @@ public function __invoke(array ...$queries): iterable $lastQuery = $result ? strtolower(trim((string) $result->queryString, "\\ \t\n\r\0\x0B")) : ''; - if ($result instanceof PDOStatement && strpos($lastQuery, 'select') === 0) { + // Remove leading comment + $sqlQuery = trim(preg_replace('/^\/\*.*\*\//', '', $lastQuery)); + if ($result instanceof PDOStatement && strpos($sqlQuery, 'select') === 0) { return (array) $result->fetchAll(PDO::FETCH_ASSOC); } diff --git a/tests/Iso8601FormatModuleTest.php b/tests/Iso8601FormatModuleTest.php index 817b143..0e22ae0 100644 --- a/tests/Iso8601FormatModuleTest.php +++ b/tests/Iso8601FormatModuleTest.php @@ -42,7 +42,7 @@ public function __construct(ExtendedPdo $pdo) protected function configure() { $this->bind(ExtendedPdoInterface::class)->toInstance($this->pdo); - $this->install(new CallableQueryModule(__DIR__ . '/Fake/sql')); + $this->install(new CallableQueryModule(__DIR__ . '/Fake/sql', null, new SqlFileName())); $this->install(new Iso8601FormatModule(['created_at'])); } }; From ac7517858c294b521e1ee612dcc91b3f7ed80939 Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Wed, 12 Jun 2024 10:44:19 +0900 Subject: [PATCH 20/34] Rename and refactor SQL module and interface. The DevSqlQueryModule and DevGetSql have been renamed to SqlFileNameModule and GetSqlWithFileName respectively, matching their intended purposes. Additionally, all instances of these objects used within the project have been updated to reference the new names. --- src/{DevGetSql.php => GetSqlWithFileName.php} | 2 +- src/{DevSqlQueryModule.php => SqlFileNameModule.php} | 4 ++-- tests/SqlQueryModuleTest.php | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) rename src/{DevGetSql.php => GetSqlWithFileName.php} (87%) rename src/{DevSqlQueryModule.php => SqlFileNameModule.php} (57%) diff --git a/src/DevGetSql.php b/src/GetSqlWithFileName.php similarity index 87% rename from src/DevGetSql.php rename to src/GetSqlWithFileName.php index 335c630..3848c91 100644 --- a/src/DevGetSql.php +++ b/src/GetSqlWithFileName.php @@ -10,7 +10,7 @@ use function sprintf; use function trim; -final class DevGetSql implements GetSqlInterface +final class GetSqlWithFileName implements GetSqlInterface { /** * {@inheritDoc} diff --git a/src/DevSqlQueryModule.php b/src/SqlFileNameModule.php similarity index 57% rename from src/DevSqlQueryModule.php rename to src/SqlFileNameModule.php index b7810f6..52cc054 100644 --- a/src/DevSqlQueryModule.php +++ b/src/SqlFileNameModule.php @@ -7,13 +7,13 @@ use Ray\Di\AbstractModule; use Ray\Di\Scope; -class DevSqlQueryModule extends AbstractModule +class SqlFileNameModule extends AbstractModule { /** * {@inheritdoc} */ protected function configure(): void { - $this->bind(GetSqlInterface::class)->to(DevGetSql::class)->in(Scope::SINGLETON); + $this->bind(GetSqlInterface::class)->to(GetSqlWithFileName::class)->in(Scope::SINGLETON); } } diff --git a/tests/SqlQueryModuleTest.php b/tests/SqlQueryModuleTest.php index a0be32a..4d01653 100644 --- a/tests/SqlQueryModuleTest.php +++ b/tests/SqlQueryModuleTest.php @@ -109,7 +109,7 @@ protected function configure() { $this->bind(ExtendedPdoInterface::class)->toInstance($this->pdo); $this->install(new SqlQueryModule(__DIR__ . '/Fake/sql', null)); - $this->override(new DevSqlQueryModule()); + $this->override(new SqlFileNameModule()); } }; $injector = (new Injector($module)); From 58ba496434c3df2e55a1168d89766f2f5f0050a7 Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Wed, 12 Jun 2024 10:44:27 +0900 Subject: [PATCH 21/34] Update README with SqlFileNameModule usage The README has been updated to include instructions on how to use the SqlFileNameModule. This allows users to run queries with file name comments, providing clarity on which SQL command has been executed when logging SQL. Additional code snippets have been included to demonstrate the setup and usage. --- README.md | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index ef83968..47aafc3 100644 --- a/README.md +++ b/README.md @@ -276,8 +276,21 @@ Install date column names as an array and pass it as an argument to `Iso8601Form ```php $this->install(new Iso8601FormatModule(['created_at', 'updated_at'])); ``` +## SQL file name -## SQL file name log +Install `SqlFileNameModule` to run queries with file name comments in the query as follows. +Use it to see which SQL was executed when you log SQL. + +```php +$this->install(new SqlFileNameModule()); +``` +The SQL is executed with file name comments as follows. + +```sql +/* todo_list.sql */ SELECT * FROM todo +``` + +## #[Named] bindings Binding to `#[Named]`, which was supported until `0.9`, is possible with `CallableQueryModule`. @@ -286,6 +299,10 @@ use Ray\Query\SqlFileName; use Ray\Query\CallableQueryModule; $this->install(new CallableQueryModule(__DIR__ . '/Fake/sql')); +$this->install(new CallableQueryModule(__DIR__ . '/Fake/sql'), null new SqlFileName()); // query with sql file name +``` + +```php ``` Execute SQL From a329d46bf3807db0007f63391b837be9aaed2a0d Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Wed, 12 Jun 2024 10:46:08 +0900 Subject: [PATCH 22/34] Add 'extra' configuration to composer.json The 'extra' configuration has been added in the composer.json file. This new configuration includes 'bamarni-bin' settings with 'bin-links' and 'forward-command' set to true. These changes enhance the compatibility and functionality of the project's dependencies. --- composer.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/composer.json b/composer.json index c049470..6cc1fc2 100644 --- a/composer.json +++ b/composer.json @@ -62,5 +62,11 @@ "allow-plugins": { "bamarni/composer-bin-plugin": true } + }, + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": true + } } } From 3f96b6066cb01f8a3b59657726a83f23a455b161 Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Wed, 12 Jun 2024 10:50:51 +0900 Subject: [PATCH 23/34] Update project description in README and composer.json The project description has been updated in both the README.md and composer.json files. The new description accurately represents the project's functionality of converting SQL files and Web API URIs into injectable function objects. --- README.md | 2 ++ composer.json | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 47aafc3..9b99bb0 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # Ray.QueryModule +Converts SQL files, Web API URIs into injectable function objects. + [![codecov](https://codecov.io/gh/ray-di/Ray.QueryModule/branch/1.x/graph/badge.svg?token=60G2MFDOBR)](https://codecov.io/gh/ray-di/Ray.QueryModule) [![Type Coverage](https://shepherd.dev/github/ray-di/Ray.QueryModule/coverage.svg)](https://shepherd.dev/github/ray-di/Ray.QueryModule) ![Continuous Integration](https://github.com/ray-di/Ray.QueryModule/workflows/Continuous%20Integration/badge.svg) diff --git a/composer.json b/composer.json index 6cc1fc2..e2d6fb7 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,6 @@ { "name": "ray/query-module", - "description": "An external media access framework", + "description": "Converts SQL files, Web API URIs into injectable function objects.", "keywords": ["repository"], "homepage": "https://github.com/koriym/Koriym.PhpSkeleton", "license": "MIT", From 98977c8f5617425cb606a98565381d61378ed44e Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Wed, 12 Jun 2024 10:54:23 +0900 Subject: [PATCH 24/34] fixup! Trim SQL file content and update SQL query handling --- src/SqlQueryRowList.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/SqlQueryRowList.php b/src/SqlQueryRowList.php index c79bd7b..6c9c79d 100644 --- a/src/SqlQueryRowList.php +++ b/src/SqlQueryRowList.php @@ -55,7 +55,7 @@ public function __invoke(array ...$queries): iterable $lastQuery = $result ? strtolower(trim((string) $result->queryString, "\\ \t\n\r\0\x0B")) : ''; // Remove leading comment - $sqlQuery = trim(preg_replace('/^\/\*.*\*\//', '', $lastQuery)); + $sqlQuery = trim((string) preg_replace('/^\/\*.*\*\//', '', $lastQuery)); if ($result instanceof PDOStatement && strpos($sqlQuery, 'select') === 0) { return (array) $result->fetchAll(PDO::FETCH_ASSOC); } From 02d3a82a2df7b5963165fd6f4d1b3bfeebc04a0a Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Wed, 12 Jun 2024 11:13:27 +0900 Subject: [PATCH 25/34] Keep BC with PHP 7.x Update query annotations in FakeRo test This commit updates and adds query annotations in the FakeRo test. It adds a query annotation to the `noSql` method and modifies the id parameter in the `withQuery` method's `@Query` annotation for enhanced templating. --- tests/Fake/FakeRo.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/Fake/FakeRo.php b/tests/Fake/FakeRo.php index caed1b9..d45f21f 100644 --- a/tests/Fake/FakeRo.php +++ b/tests/Fake/FakeRo.php @@ -22,13 +22,16 @@ public function onGet(string $id) return $this; } + /** + * @Query(id="_non_exists_", type="row") + */ #[Query('_non_exists_')] public function noSql(): void { } /** - * @Query(id="todo_item_by_id?id=num", type="row", templated=true) + * @Query(id="todo_item_by_id?id={num}", type="row", templated=true) */ #[Query('todo_item_by_id?id={num}', type: 'row', templated: true)] public function withQuery(string $num): ResourceObject From 825236406cad5494ec7c1b375c540d89841a0a2b Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Thu, 13 Jun 2024 01:42:01 +0900 Subject: [PATCH 26/34] Refactor GetSql to FileGetContents The GetSql class and interface have been renamed to FileGetContents to better describe the functionality they provide. Associated classes and tests have been updated for this change. A new exception, SqlFileNotReadableException, has been introduced to address issues when files cannot be read. Also, the contents manipulation from GetSqlWithFileName constructor and SqlFileName class have been shifted to the new FileGetContents class. --- src/CallableQueryModule.php | 7 ++-- src/Exception/SqlFileNotReadableException.php | 11 ++++++ src/FileGetContents.php | 27 ++++++++++++++ ...rface.php => FileGetContentsInterface.php} | 2 +- ...me.php => FileGetContentsWithFileName.php} | 15 +++++--- src/GetSql.php | 18 ---------- src/QueryInterceptor.php | 19 ++++++---- src/SqlFileName.php | 9 +++-- src/SqlFileNameModule.php | 2 +- src/SqlFinder.php | 4 +-- src/SqlQueryModule.php | 3 +- tests/Iso8601FormatModuleTest.php | 35 ++++++++++++++++++- tests/SqlQueryModuleTest.php | 2 +- 13 files changed, 110 insertions(+), 44 deletions(-) create mode 100644 src/Exception/SqlFileNotReadableException.php create mode 100644 src/FileGetContents.php rename src/{GetSqlInterface.php => FileGetContentsInterface.php} (76%) rename src/{GetSqlWithFileName.php => FileGetContentsWithFileName.php} (50%) delete mode 100644 src/GetSql.php diff --git a/src/CallableQueryModule.php b/src/CallableQueryModule.php index d458c9b..d3a9b7c 100644 --- a/src/CallableQueryModule.php +++ b/src/CallableQueryModule.php @@ -13,7 +13,6 @@ use RegexIterator; use SplFileInfo; -use function file_get_contents; use function pathinfo; use function trim; @@ -29,7 +28,9 @@ public function __construct(string $sqlDir, ?AbstractModule $module = null, ?cal { $this->sqlDir = $sqlDir; $this->getSql = $getSql ?? static function (SplFileInfo $fileInfo): string { - return trim((string) file_get_contents($fileInfo->getPathname())); + $getContents = new FileGetContents(); + + return $getContents($fileInfo->getPathname()); }; parent::__construct($module); @@ -40,7 +41,7 @@ public function __construct(string $sqlDir, ?AbstractModule $module = null, ?cal */ protected function configure() { - $this->bind(GetSqlInterface::class)->to(GetSql::class)->in(Scope::SINGLETON); + $this->bind(FileGetContentsInterface::class)->to(FileGetContents::class)->in(Scope::SINGLETON); $this->bind(SqlDir::class)->toInstance(new SqlDir($this->sqlDir)); /** @var SplFileInfo $fileInfo */ foreach ($this->files($this->sqlDir) as $fileInfo) { diff --git a/src/Exception/SqlFileNotReadableException.php b/src/Exception/SqlFileNotReadableException.php new file mode 100644 index 0000000..ff02f47 --- /dev/null +++ b/src/Exception/SqlFileNotReadableException.php @@ -0,0 +1,11 @@ +getContents = $getContents; + } + /** * {@inheritDoc} */ public function __invoke(string $filePath): string { $fileInfo = new SplFileInfo($filePath); + $content = ($this->getContents)($filePath); - return sprintf('/* %s */ %s', $fileInfo->getFilename(), trim((string) file_get_contents($filePath))); + return sprintf('/* %s */ %s', $fileInfo->getFilename(), $content); } } diff --git a/src/GetSql.php b/src/GetSql.php deleted file mode 100644 index 5ae338e..0000000 --- a/src/GetSql.php +++ /dev/null @@ -1,18 +0,0 @@ -sqlDir = $sqlDir; $this->pdo = $pdo; + $this->fileGetContents = $fileGetContents; } /** @return ResourceObject|mixed */ @@ -111,11 +115,12 @@ private function templated(Query $query, array $namedArguments): array private function getsql(string $queryId, ReflectionMethod $method): string { - $file = sprintf('%s/%s.sql', $this->sqlDir->value, $queryId); - if (! file_exists($file)) { + $filePath = sprintf('%s/%s.sql', $this->sqlDir->value, $queryId); + + try { + return ($this->fileGetContents)($filePath); + } catch (SqlFileNotReadableException $e) { throw new SqlFileNotFoundException((string) $method, $queryId); } - - return (string) file_get_contents($file); } } diff --git a/src/SqlFileName.php b/src/SqlFileName.php index ab45591..0a016a5 100644 --- a/src/SqlFileName.php +++ b/src/SqlFileName.php @@ -6,10 +6,6 @@ use SplFileInfo; -use function file_get_contents; -use function sprintf; -use function trim; - final class SqlFileName { /** @@ -17,6 +13,9 @@ final class SqlFileName */ public function __invoke(SplFileInfo $fileInfo): string { - return sprintf('/* %s */ %s', $fileInfo->getFilename(), trim((string) file_get_contents($fileInfo->getPathname()))); + $getFileContents = new FileGetContentsWithFileName(new FileGetContents()); + $filePath = $fileInfo->getPathname(); + + return $getFileContents($filePath); } } diff --git a/src/SqlFileNameModule.php b/src/SqlFileNameModule.php index 52cc054..fda2de8 100644 --- a/src/SqlFileNameModule.php +++ b/src/SqlFileNameModule.php @@ -14,6 +14,6 @@ class SqlFileNameModule extends AbstractModule */ protected function configure(): void { - $this->bind(GetSqlInterface::class)->to(GetSqlWithFileName::class)->in(Scope::SINGLETON); + $this->bind(FileGetContentsInterface::class)->to(FileGetContentsWithFileName::class)->in(Scope::SINGLETON); } } diff --git a/src/SqlFinder.php b/src/SqlFinder.php index 8391fae..8527699 100644 --- a/src/SqlFinder.php +++ b/src/SqlFinder.php @@ -21,14 +21,14 @@ final class SqlFinder implements SqlFinderInterface /** @var SqlDir */ private $sqlDir; - /** @var GetSqlInterface */ + /** @var FileGetContentsInterface */ private $getSql; /** @param ParamReaderInterface $reader */ public function __construct( ParamReaderInterface $reader, SqlDir $sqlDir, - GetSqlInterface $getSql + FileGetContentsInterface $getSql ) { $this->reader = $reader; $this->sqlDir = $sqlDir; diff --git a/src/SqlQueryModule.php b/src/SqlQueryModule.php index ab46b10..9a89a9d 100644 --- a/src/SqlQueryModule.php +++ b/src/SqlQueryModule.php @@ -26,7 +26,8 @@ public function __construct(string $sqlDir, ?AbstractModule $module = null) */ protected function configure() { - $this->bind(GetSqlInterface::class)->to(GetSql::class)->in(Scope::SINGLETON); + $this->bind(FileGetContents::class); + $this->bind(FileGetContentsInterface::class)->to(FileGetContents::class)->in(Scope::SINGLETON); $this->bind(SqlDir::class)->toInstance(new SqlDir($this->sqlDir)); $this->bind(SqlFinderInterface::class)->to(SqlFinder::class)->in(Scope::SINGLETON); $this->bind(ParamReaderInterface::class)->to(ParamReader::class)->in(Scope::SINGLETON); diff --git a/tests/Iso8601FormatModuleTest.php b/tests/Iso8601FormatModuleTest.php index 0e22ae0..78434ff 100644 --- a/tests/Iso8601FormatModuleTest.php +++ b/tests/Iso8601FormatModuleTest.php @@ -42,7 +42,7 @@ public function __construct(ExtendedPdo $pdo) protected function configure() { $this->bind(ExtendedPdoInterface::class)->toInstance($this->pdo); - $this->install(new CallableQueryModule(__DIR__ . '/Fake/sql', null, new SqlFileName())); + $this->install(new CallableQueryModule(__DIR__ . '/Fake/sql', null)); $this->install(new Iso8601FormatModule(['created_at'])); } }; @@ -77,4 +77,37 @@ public function testList(): void ]; $this->assertSame($expected, $actural); } + + public function testSqlFileName(): void + { + $pdo = new ExtendedPdo('sqlite::memory:'); + $pdo->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, true); + $pdo->query('CREATE TABLE IF NOT EXISTS todo ( + id INTEGER, + title TEXT, + created_at TIMESTAMP)'); + $pdo->perform('INSERT INTO todo (id, title, created_at) VALUES (:id, :title, :created_at)', ['id' => '1', 'title' => 'run', 'created_at' => '1970-01-01 00:00:00']); + + $module = new class ($pdo) extends AbstractModule { + /** @var ExtendedPdo */ + private $pdo; + + public function __construct(ExtendedPdo $pdo) + { + $this->pdo = $pdo; + + parent::__construct(); + } + + protected function configure() + { + $this->bind(ExtendedPdoInterface::class)->toInstance($this->pdo); + $this->install(new CallableQueryModule(__DIR__ . '/Fake/sql', null, new SqlFileName())); + $this->install(new Iso8601FormatModule(['created_at'])); + } + }; + $injector = new Injector($this->module, __DIR__ . '/tmp'); + $todo = $injector->getInstance(FakeTodo::class); + $this->assertInstanceOf(FakeTodo::class, $todo); + } } diff --git a/tests/SqlQueryModuleTest.php b/tests/SqlQueryModuleTest.php index 4d01653..e93b53c 100644 --- a/tests/SqlQueryModuleTest.php +++ b/tests/SqlQueryModuleTest.php @@ -87,7 +87,7 @@ public function testSqlNotAnnotated(): void $injector->getInstance(FakeTodoProviderSqlNotAnnotated::class); } - public function testDevSqlModule(): void + public function testSqlFileNameModule(): void { $pdo = new ExtendedPdo('sqlite::memory:'); $pdo->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, true); From 438538e5f64eb525bc8c32fb07b8b8abeb93f6a0 Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Thu, 13 Jun 2024 09:55:34 +0900 Subject: [PATCH 27/34] Add file existence check in FileGetContents class A check for file existence has been added to the FileGetContents class. Now, before file content is retrieved, the system will check whether the file exists. This reduces the likelihood of encountering a SqlFileNotReadableException. --- src/FileGetContents.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/FileGetContents.php b/src/FileGetContents.php index 24920ec..49fc1e0 100644 --- a/src/FileGetContents.php +++ b/src/FileGetContents.php @@ -6,6 +6,7 @@ use Ray\Query\Exception\SqlFileNotReadableException; +use function file_exists; use function file_get_contents; final class FileGetContents implements FileGetContentsInterface @@ -15,6 +16,10 @@ final class FileGetContents implements FileGetContentsInterface */ public function __invoke(string $filePath): string { + if (! file_exists($filePath)) { + throw new SqlFileNotReadableException($filePath); + } + $content = file_get_contents($filePath); if ($content === false) { // @codeCoverageIgnoreStart From 96340c242ef281fc622416cf37e868e8200a7690 Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Thu, 13 Jun 2024 09:55:46 +0900 Subject: [PATCH 28/34] Update exception handling and dependencies The code has been updated to throw SqlFileNotReadableException instead of SqlFileNotFoundException. It also includes the addition of InjectorInterface as a dependency in QueryInterceptor. --- src/QueryInterceptor.php | 41 +++++++++++++++++---------- tests/SqlQueryInterceptModuleTest.php | 4 +-- 2 files changed, 28 insertions(+), 17 deletions(-) diff --git a/src/QueryInterceptor.php b/src/QueryInterceptor.php index ba3cc26..5deb598 100644 --- a/src/QueryInterceptor.php +++ b/src/QueryInterceptor.php @@ -8,9 +8,9 @@ use BEAR\Resource\ResourceObject; use Ray\Aop\MethodInterceptor; use Ray\Aop\MethodInvocation; -use Ray\Aop\ReflectionMethod; +use Ray\Di\Exception\Unbound; +use Ray\Di\InjectorInterface; use Ray\Query\Annotation\Query; -use Ray\Query\Exception\SqlFileNotFoundException; use Ray\Query\Exception\SqlFileNotReadableException; use function assert; @@ -30,14 +30,19 @@ class QueryInterceptor implements MethodInterceptor /** @var FileGetContentsInterface */ private $fileGetContents; + /** @var InjectorInterface */ + private $injector; + public function __construct( ExtendedPdoInterface $pdo, SqlDir $sqlDir, - FileGetContentsInterface $fileGetContents + FileGetContentsInterface $fileGetContents, + InjectorInterface $injector ) { $this->sqlDir = $sqlDir; $this->pdo = $pdo; $this->fileGetContents = $fileGetContents; + $this->injector = $injector; } /** @return ResourceObject|mixed */ @@ -50,7 +55,24 @@ public function invoke(MethodInvocation $invocation) $namedArguments = (array) $invocation->getNamedArguments(); [$queryId, $params] = $query->templated ? $this->templated($query, $namedArguments) : [$query->id, $namedArguments]; assert(is_string($queryId)); - $sql = $this->getsql($queryId, $method); + $filePath = sprintf('%s/%s.sql', $this->sqlDir->value, $queryId); + try { + $sql = ($this->fileGetContents)($filePath); + } catch (SqlFileNotReadableException $e) { + // For BC + // @codeCoverageIgnoreStart + try { + $sqlQuery = $this->injector->getInstance(RowListInterface::class, $queryId); + // @codeCoverageIgnoreEnd + assert($sqlQuery instanceof QueryInterface); + } catch (Unbound $e) { + throw new SqlFileNotReadableException($filePath); + } + + /** @var array $params */ + return $this->getQueryResult($invocation, $sqlQuery, $params); + } + $sqlQuery = $query->type === 'row' ? new SqlQueryRow($this->pdo, $sql) : new SqlQueryRowList($this->pdo, $sql); /** @var array $params */ @@ -112,15 +134,4 @@ private function templated(Query $query, array $namedArguments): array return [$queryId, $params + $namedArguments]; } - - private function getsql(string $queryId, ReflectionMethod $method): string - { - $filePath = sprintf('%s/%s.sql', $this->sqlDir->value, $queryId); - - try { - return ($this->fileGetContents)($filePath); - } catch (SqlFileNotReadableException $e) { - throw new SqlFileNotFoundException((string) $method, $queryId); - } - } } diff --git a/tests/SqlQueryInterceptModuleTest.php b/tests/SqlQueryInterceptModuleTest.php index 27bfca2..e4d5a7b 100644 --- a/tests/SqlQueryInterceptModuleTest.php +++ b/tests/SqlQueryInterceptModuleTest.php @@ -10,7 +10,7 @@ use PHPUnit\Framework\TestCase; use Ray\Di\AbstractModule; use Ray\Di\Injector; -use Ray\Query\Exception\SqlFileNotFoundException; +use Ray\Query\Exception\SqlFileNotReadableException; class SqlQueryInterceptModuleTest extends TestCase { @@ -68,7 +68,7 @@ public function testResourceObject404(): void public function testNoSqlFile(): void { - $this->expectException(SqlFileNotFoundException::class); + $this->expectException(SqlFileNotReadableException::class); $this->fakeRo->noSql(); } From 2be10e4197b7bbeb042dd0df702bf694b57ed6e9 Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Thu, 13 Jun 2024 10:04:36 +0900 Subject: [PATCH 29/34] Remove unused BC comment, add PHP class binding docs The BC (backward compatibility) comment in QueryInterceptor.php was removed, as it served no meaningful purpose. Added to README.md, is a section explaining how to bind a PHP class to a query, instead of specifying an SQL file. The usage and behavior are described, with a corresponding PHP annotation example provided. --- README.md | 10 ++++++++++ src/QueryInterceptor.php | 1 - 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 9b99bb0..67004c4 100644 --- a/README.md +++ b/README.md @@ -183,6 +183,16 @@ class FooRow If there is no SELECT result, it returns `404 Not Found`. +### PHP class binding + +This Query can also be a dependency on a PHP class rather than specifying an SQL file. + +```php + #[Query("ticket_item_by_id"] +``` + +If the SQL file `ticket_item_by_id.sql` does not exist with such an attribute, the Query interface bound by `RowListInterfce` and `ticket_item_by_id` is executed with the arguments passed to the method The method is executed with the arguments passed to the method. + ## Convert URI to Web request object With `WebQueryModule`, it converts the URI bound in the configuration into an invocation object for web access and injects it. diff --git a/src/QueryInterceptor.php b/src/QueryInterceptor.php index 5deb598..5201401 100644 --- a/src/QueryInterceptor.php +++ b/src/QueryInterceptor.php @@ -59,7 +59,6 @@ public function invoke(MethodInvocation $invocation) try { $sql = ($this->fileGetContents)($filePath); } catch (SqlFileNotReadableException $e) { - // For BC // @codeCoverageIgnoreStart try { $sqlQuery = $this->injector->getInstance(RowListInterface::class, $queryId); From ac3c54d29a41329cae873bff3e70f94a3d4c2b5b Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Thu, 13 Jun 2024 10:08:05 +0900 Subject: [PATCH 30/34] Update README.md heading content The heading about module installation in the README.md file has been modified. The new heading is more descriptive and improves guidance for the user on how to install the SQL module. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 67004c4..ea57a26 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ Converts SQL files, Web API URIs into injectable function objects. $ composer require ray/query-module -### Module install (SQL) +### How to Install the SQL Module ```php use Ray\Di\AbstractModule; From 8d5d5c6a6ac3039027b876524f7bd74cd54e6417 Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Thu, 13 Jun 2024 10:09:19 +0900 Subject: [PATCH 31/34] Update description in README and composer.json The description in both README.md and composer.json files has been changed. The phrase "Converts SQL files, Web API URIs into injectable function objects" was updated to "Converts SQL files and Web API URIs into inject --- README.md | 2 +- composer.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ea57a26..6c9615e 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Ray.QueryModule -Converts SQL files, Web API URIs into injectable function objects. +Converts SQL files and Web API URIs into injectable function objects [![codecov](https://codecov.io/gh/ray-di/Ray.QueryModule/branch/1.x/graph/badge.svg?token=60G2MFDOBR)](https://codecov.io/gh/ray-di/Ray.QueryModule) [![Type Coverage](https://shepherd.dev/github/ray-di/Ray.QueryModule/coverage.svg)](https://shepherd.dev/github/ray-di/Ray.QueryModule) diff --git a/composer.json b/composer.json index e2d6fb7..2d81349 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,6 @@ { "name": "ray/query-module", - "description": "Converts SQL files, Web API URIs into injectable function objects.", + "description": "Converts SQL files and Web API URIs into injectable function objects", "keywords": ["repository"], "homepage": "https://github.com/koriym/Koriym.PhpSkeleton", "license": "MIT", From ef267e511e2e5581ab8126f3d7f9a909c7299a04 Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Thu, 13 Jun 2024 10:09:57 +0900 Subject: [PATCH 32/34] Update annotation syntax in README.md The commit modifies the documentation to reflect the recent changes in the annotation syntax. Specifically, it updates the code snippet in the README file to use the `#[Query]` syntax instead of `@Query`. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6c9615e..0974923 100644 --- a/README.md +++ b/README.md @@ -278,7 +278,7 @@ Bind to `QueryInterface `. $this->bind(QueryInterface::class)->annotatedWith('cretate_todo')->to(CreateTodo::class); ``` -The usage codes are the same. The usage code of `@Query` does not change either. +The usage codes are the same. The usage code of `#[Query]` does not change either. ## ISO8601 DateTime Module From e70ecfbf56ba905057e4a09c807569b612615490 Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Thu, 13 Jun 2024 10:19:33 +0900 Subject: [PATCH 33/34] Refactor error handling for SqlFileNotFoundException Moved the error handling for SqlFileNotFoundException into its own private method, handleSqlNotFound, for improved readability and simplicity. This change does not alter the functionality but enhances code structure and maintainability. --- src/RowInterfaceProvider.php | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/src/RowInterfaceProvider.php b/src/RowInterfaceProvider.php index 4eb3387..7fbe8a2 100644 --- a/src/RowInterfaceProvider.php +++ b/src/RowInterfaceProvider.php @@ -48,18 +48,23 @@ public function get(): QueryInterface // For development // @codeCoverageIgnoreStart } catch (SqlFileNotFoundException $e) { - try { - $named = $e->sql; - $instance = $this->injector->getInstance(RowInterface::class, $named); - error_log(sprintf('Warning: #[Sql(\'%s\')] is not vald. Change to #[\\Ray\\Di\\Di\\Named(\'%s\')]', $named, $named)); - - return $instance; - } catch (Unbound $unbound) { - throw $e; - } - // @codeCoverageIgnoreEnd + return $this->handleSqlNotFound($e); } return new SqlQueryRow($this->pdo, $sql); } + + private function handleSqlNotFound(SqlFileNotFoundException $e): RowInterface + { + try { + $named = $e->sql; + $instance = $this->injector->getInstance(RowInterface::class, $named); + error_log(sprintf('Warning: #[Sql(\'%s\')] is not vald. Change to #[\\Ray\\Di\\Di\\Named(\'%s\')]', $named, $named)); + + return $instance; + } catch (Unbound $unbound) { + throw $e; + } + // @codeCoverageIgnoreEnd + } } From 114a6a6f0792da043103fef078c938deb0547014 Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Thu, 13 Jun 2024 11:18:03 +0900 Subject: [PATCH 34/34] Add proper cleanup in test teardown Added a tearDown() function to the SqlQueryInterceptModuleTest, SqlQueryModuleTest, and SqlQueryTest classes. This method takes care of appropriately cleaning up the SQLite connection after each test, preventing potential issues related to connection leakage. --- tests/SqlQueryInterceptModuleTest.php | 11 +++++++++++ tests/SqlQueryModuleTest.php | 12 ++++++++++++ tests/SqlQueryTest.php | 7 +++++++ 3 files changed, 30 insertions(+) diff --git a/tests/SqlQueryInterceptModuleTest.php b/tests/SqlQueryInterceptModuleTest.php index e4d5a7b..c05819e 100644 --- a/tests/SqlQueryInterceptModuleTest.php +++ b/tests/SqlQueryInterceptModuleTest.php @@ -14,6 +14,9 @@ class SqlQueryInterceptModuleTest extends TestCase { + /** @var ExtendedPdo */ + private $pdo; + /** @var FakeRo */ private $fakeRo; @@ -45,11 +48,19 @@ protected function configure() $this->bind(FakeBar::class); } }; + $this->pdo = $pdo; $injector = new Injector($module, __DIR__ . '/tmp'); $this->fakeRo = $injector->getInstance(FakeRo::class); $this->fakeBar = $injector->getInstance(FakeBar::class); } + protected function tearDown(): void + { + unset($this->pdo); // This effectively closes the SQLite connection + + parent::tearDown(); + } + public function testResourceObject200(): void { $response = $this->fakeRo->onGet('1'); diff --git a/tests/SqlQueryModuleTest.php b/tests/SqlQueryModuleTest.php index e93b53c..581f504 100644 --- a/tests/SqlQueryModuleTest.php +++ b/tests/SqlQueryModuleTest.php @@ -19,8 +19,12 @@ class SqlQueryModuleTest extends TestCase { + /** @var ExtendedPdo */ + private $pdo; + /** @var AbstractModule */ private $module; + protected function setUp(): void { $pdo = new ExtendedPdo('sqlite::memory:'); @@ -45,6 +49,14 @@ protected function configure() $this->install(new SqlQueryModule(__DIR__ . '/Fake/sql')); } }; + $this->pdo = $pdo; + } + + protected function tearDown(): void + { + unset($this->pdo); // This effectively closes the SQLite connection + + parent::tearDown(); } public function testProviderInject(): void diff --git a/tests/SqlQueryTest.php b/tests/SqlQueryTest.php index 1f41012..19c4360 100644 --- a/tests/SqlQueryTest.php +++ b/tests/SqlQueryTest.php @@ -29,6 +29,13 @@ protected function setUp(): void $this->pdo = $pdo; } + protected function tearDown(): void + { + unset($this->pdo); // This effectively closes the SQLite connection + + parent::tearDown(); + } + public function testInvoke(): void { $sql = (string) file_get_contents(__DIR__ . '/Fake/sql/todo_item_by_id.sql');