diff --git a/app/.gitignore b/app/.gitignore index a20bb1f33..6dfef167d 100644 --- a/app/.gitignore +++ b/app/.gitignore @@ -4,7 +4,6 @@ /config/.env /logs/* /tmp/* -/vendor/* # OS generated files # ###################### diff --git a/app/composer.json b/app/composer.json index 6cfb6b0ef..3bea99d9d 100644 --- a/app/composer.json +++ b/app/composer.json @@ -6,11 +6,13 @@ "license": "MIT", "require": { "php": ">=8.0", - "cakephp/cakephp": "^4.3", + "cakephp/cakephp": "4.3.9", "cakephp/migrations": "^3.2", "cakephp/plugin-installer": "^1.3", "doctrine/dbal": "^3.3", - "mobiledetect/mobiledetectlib": "^2.8" + "league/container": "^4.2.0", + "mobiledetect/mobiledetectlib": "^2.8", + "psr/log": "^2.0" }, "require-dev": { "cakephp/bake": "^2.6", diff --git a/app/composer.lock b/app/composer.lock index 481ace0d0..ff0065eed 100644 --- a/app/composer.lock +++ b/app/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "60f77f61fc803e4b2b9295e4ab5bfa10", + "content-hash": "b5b872b4fb7ca7b66353dd22890a8ee6", "packages": [ { "name": "cakephp/cakephp", diff --git a/app/src/Controller/AppController.php b/app/src/Controller/AppController.php index 0ff98d5c1..48cc8b1ef 100644 --- a/app/src/Controller/AppController.php +++ b/app/src/Controller/AppController.php @@ -220,9 +220,9 @@ public function calculatePermissions(?int $id): array { foreach($permissions['entity'] as $action => $roles) { $ok = false; - if(($action != 'delete' || $canDelete) - && - !$readOnly || in_array($action, $readOnlyActions)) { + if((($action != 'delete' || $canDelete) + && + !$readOnly) || in_array($action, $readOnlyActions)) { if(is_array($roles)) { foreach($roles as $role) { // eg: $role = "platformAdmin", which corresponds to the variables set, above diff --git a/app/vendor/cakephp/bake/src/Command/CommandHelperCommand.php b/app/vendor/cakephp/bake/src/Command/CommandHelperCommand.php new file mode 100644 index 000000000..fba31ed10 --- /dev/null +++ b/app/vendor/cakephp/bake/src/Command/CommandHelperCommand.php @@ -0,0 +1,54 @@ +_io`. + * + * @param array $args The arguments for the helper. + * @return void + */ + public function output(array $args): void + { + } +} diff --git a/app/vendor/cakephp/bake/templates/bake/Plugin/tests/schema.sql.twig b/app/vendor/cakephp/bake/templates/bake/Plugin/tests/schema.sql.twig new file mode 100644 index 000000000..cdf0465f7 --- /dev/null +++ b/app/vendor/cakephp/bake/templates/bake/Plugin/tests/schema.sql.twig @@ -0,0 +1 @@ +-- Test database schema for {{ plugin }} diff --git a/app/vendor/cakephp/bake/tests/Fixture/ArticlesFixture.php b/app/vendor/cakephp/bake/tests/Fixture/ArticlesFixture.php new file mode 100644 index 000000000..7a8637cf5 --- /dev/null +++ b/app/vendor/cakephp/bake/tests/Fixture/ArticlesFixture.php @@ -0,0 +1,26 @@ + 1, 'title' => 'First Article', 'body' => 'First Article Body', 'published' => 'Y'], + ['author_id' => 3, 'title' => 'Second Article', 'body' => 'Second Article Body', 'published' => 'Y'], + ['author_id' => 1, 'title' => 'Third Article', 'body' => 'Third Article Body', 'published' => 'Y'], + ]; +} diff --git a/app/vendor/cakephp/bake/tests/Fixture/ArticlesTagsFixture.php b/app/vendor/cakephp/bake/tests/Fixture/ArticlesTagsFixture.php new file mode 100644 index 000000000..41ab1499d --- /dev/null +++ b/app/vendor/cakephp/bake/tests/Fixture/ArticlesTagsFixture.php @@ -0,0 +1,21 @@ + 'mariano'], + ['name' => 'nate'], + ['name' => 'larry'], + ['name' => 'garrett'], + ]; +} diff --git a/app/vendor/cakephp/bake/tests/Fixture/CommentsFixture.php b/app/vendor/cakephp/bake/tests/Fixture/CommentsFixture.php new file mode 100644 index 000000000..eecb7608e --- /dev/null +++ b/app/vendor/cakephp/bake/tests/Fixture/CommentsFixture.php @@ -0,0 +1,29 @@ + 1, 'user_id' => 2, 'comment' => 'First Comment for First Article', 'published' => 'Y', 'created' => '2007-03-18 10:45:23', 'updated' => '2007-03-18 10:47:31'], + ['article_id' => 1, 'user_id' => 4, 'comment' => 'Second Comment for First Article', 'published' => 'Y', 'created' => '2007-03-18 10:47:23', 'updated' => '2007-03-18 10:49:31'], + ['article_id' => 1, 'user_id' => 1, 'comment' => 'Third Comment for First Article', 'published' => 'Y', 'created' => '2007-03-18 10:49:23', 'updated' => '2007-03-18 10:51:31'], + ['article_id' => 1, 'user_id' => 1, 'comment' => 'Fourth Comment for First Article', 'published' => 'N', 'created' => '2007-03-18 10:51:23', 'updated' => '2007-03-18 10:53:31'], + ['article_id' => 2, 'user_id' => 1, 'comment' => 'First Comment for Second Article', 'published' => 'Y', 'created' => '2007-03-18 10:53:23', 'updated' => '2007-03-18 10:55:31'], + ['article_id' => 2, 'user_id' => 2, 'comment' => 'Second Comment for Second Article', 'published' => 'Y', 'created' => '2007-03-18 10:55:23', 'updated' => '2007-03-18 10:57:31'], + ]; +} diff --git a/app/vendor/cakephp/bake/tests/Fixture/HiddenFieldsFixture.php b/app/vendor/cakephp/bake/tests/Fixture/HiddenFieldsFixture.php new file mode 100644 index 000000000..f1db98741 --- /dev/null +++ b/app/vendor/cakephp/bake/tests/Fixture/HiddenFieldsFixture.php @@ -0,0 +1,47 @@ + + */ + public $fields = [ + 'id' => ['type' => 'integer'], + 'password' => ['type' => 'string', 'null' => true, 'length' => 255], + 'auth_token' => ['type' => 'string', 'null' => true, 'length' => 255], + '_constraints' => [ + 'primary' => ['type' => 'primary', 'columns' => ['id']], + ], + ]; + + /** + * records property + * + * @var array + */ + public $records = [ + ['password' => '$2a$10$u05j8FjsvLBNdfhBhc21LOuVMpzpabVXQ9OpC2wO3pSO0q6t7HHMO', 'auth_token' => '12345'], + ['password' => '$2a$10$u05j8FjsvLBNdfhBhc21LOuVMpzpabVXQ9OpC2wO3pSO0q6t7HHMO', 'auth_token' => '23456'], + ]; +} diff --git a/app/vendor/cakephp/bake/tests/Fixture/PostsFixture.php b/app/vendor/cakephp/bake/tests/Fixture/PostsFixture.php new file mode 100644 index 000000000..85478bb3b --- /dev/null +++ b/app/vendor/cakephp/bake/tests/Fixture/PostsFixture.php @@ -0,0 +1,26 @@ + 1, 'title' => 'First Post', 'body' => 'First Post Body', 'published' => 'Y'], + ['author_id' => 3, 'title' => 'Second Post', 'body' => 'Second Post Body', 'published' => 'Y'], + ['author_id' => 1, 'title' => 'Third Post', 'body' => 'Third Post Body', 'published' => 'Y'], + ]; +} diff --git a/app/vendor/cakephp/bake/tests/Fixture/TagsFixture.php b/app/vendor/cakephp/bake/tests/Fixture/TagsFixture.php new file mode 100644 index 000000000..6d904d111 --- /dev/null +++ b/app/vendor/cakephp/bake/tests/Fixture/TagsFixture.php @@ -0,0 +1,26 @@ + 'tag1', 'description' => 'A big description', 'created' => '2016-01-01 00:00'], + ['name' => 'tag2', 'description' => 'Another big description', 'created' => '2016-01-01 00:00'], + ['name' => 'tag3', 'description' => 'Yet another one', 'created' => '2016-01-01 00:00'], + ]; +} diff --git a/app/vendor/cakephp/bake/tests/Fixture/UniqueFieldsFixture.php b/app/vendor/cakephp/bake/tests/Fixture/UniqueFieldsFixture.php new file mode 100644 index 000000000..2d64967de --- /dev/null +++ b/app/vendor/cakephp/bake/tests/Fixture/UniqueFieldsFixture.php @@ -0,0 +1,59 @@ + + */ + public $fields = [ + 'id' => ['type' => 'integer'], + 'username' => ['type' => 'string', 'null' => true, 'length' => 255], + 'email' => ['type' => 'string', 'null' => true, 'length' => 255], + 'field_1' => ['type' => 'string', 'null' => true, 'length' => 255], + 'field_2' => ['type' => 'string', 'null' => true,'length' => 255], + '_constraints' => [ + 'primary' => [ + 'type' => 'primary', + 'columns' => ['id'], + ], + 'multiple_fields_unique' => [ + 'type' => 'unique', + 'columns' => [ + 'field_1', + 'field_2', + ], + ], + ], + ]; + + /** + * records property + * + * @var array + */ + public $records = [ + ['field_1' => 'unique_value_1', 'field_2' => 'unique_value_2'], + ['field_1' => 'unique_value_2', 'field_2' => 'unique_value_3'], + ]; +} diff --git a/app/vendor/cakephp/bake/tests/schema.php b/app/vendor/cakephp/bake/tests/schema.php new file mode 100644 index 000000000..c0891568f --- /dev/null +++ b/app/vendor/cakephp/bake/tests/schema.php @@ -0,0 +1,514 @@ + 'authors', + 'columns' => [ + 'id' => [ + 'type' => 'integer', + ], + 'name' => [ + 'type' => 'string', + 'default' => null, + ], + ], + 'constraints' => [ + 'primary' => [ + 'type' => 'primary', + 'columns' => [ + 'id', + ], + ], + ], + ], + [ + 'table' => 'tags', + 'columns' => [ + 'id' => [ + 'type' => 'integer', + 'null' => false, + ], + 'name' => [ + 'type' => 'string', + 'null' => false, + ], + 'description' => [ + 'type' => 'text', + 'length' => 16777215, + ], + 'created' => [ + 'type' => 'datetime', + 'null' => true, + 'default' => null, + ], + ], + 'constraints' => [ + 'primary' => [ + 'type' => 'primary', + 'columns' => [ + 'id', + ], + ], + ], + ], + [ + 'table' => 'articles', + 'columns' => [ + 'id' => [ + 'type' => 'integer', + ], + 'author_id' => [ + 'type' => 'integer', + 'null' => true, + ], + 'title' => [ + 'type' => 'string', + 'null' => true, + ], + 'body' => 'text', + 'published' => [ + 'type' => 'string', + 'length' => 1, + 'default' => 'N', + ], + ], + 'constraints' => [ + 'primary' => [ + 'type' => 'primary', + 'columns' => [ + 'id', + ], + ], + ], + ], + [ + 'table' => 'articles_tags', + 'columns' => [ + 'article_id' => [ + 'type' => 'integer', + 'null' => false, + ], + 'tag_id' => [ + 'type' => 'integer', + 'null' => false, + ], + ], + 'constraints' => [ + 'unique_tag' => [ + 'type' => 'primary', + 'columns' => [ + 'article_id', + 'tag_id', + ], + ], + 'tag_id_fk' => [ + 'type' => 'foreign', + 'columns' => [ + 'tag_id', + ], + 'references' => [ + 'tags', + 'id', + ], + 'update' => 'cascade', + 'delete' => 'cascade', + ], + ], + ], + [ + 'table' => 'posts', + 'columns' => [ + 'id' => [ + 'type' => 'integer', + ], + 'author_id' => [ + 'type' => 'integer', + 'null' => false, + ], + 'title' => [ + 'type' => 'string', + 'null' => false, + ], + 'body' => 'text', + 'published' => [ + 'type' => 'string', + 'length' => 1, + 'default' => 'N', + ], + ], + 'constraints' => [ + 'primary' => [ + 'type' => 'primary', + 'columns' => [ + 'id', + ], + ], + ], + ], + [ + 'table' => 'comments', + 'columns' => [ + 'id' => [ + 'type' => 'integer', + ], + 'article_id' => [ + 'type' => 'integer', + 'null' => false, + ], + 'user_id' => [ + 'type' => 'integer', + 'null' => false, + ], + 'comment' => [ + 'type' => 'text', + ], + 'published' => [ + 'type' => 'string', + 'length' => 1, + 'default' => 'N', + ], + 'created' => [ + 'type' => 'datetime', + ], + 'updated' => [ + 'type' => 'datetime', + ], + ], + 'constraints' => [ + 'primary' => [ + 'type' => 'primary', + 'columns' => [ + 'id', + ], + ], + ], + ], + [ + 'table' => 'bake_articles_bake_tags', + 'columns' => [ + 'bake_article_id' => ['type' => 'integer', 'null' => false], + 'bake_tag_id' => ['type' => 'integer', 'null' => false], + ], + 'constraints' => ['UNIQUE_TAG' => ['type' => 'unique', 'columns' => ['bake_article_id', 'bake_tag_id']]], + ], + [ + 'table' => 'bake_articles', + 'columns' => [ + 'id' => ['type' => 'integer'], + 'bake_user_id' => ['type' => 'integer', 'null' => false], + 'title' => ['type' => 'string', 'length' => 50, 'null' => false], + 'body' => 'text', + 'rating' => ['type' => 'float', 'unsigned' => true, 'default' => 0.0, 'null' => false], + 'score' => ['type' => 'decimal', 'unsigned' => true, 'default' => 0.0, 'null' => false], + 'published' => ['type' => 'boolean', 'length' => 1, 'default' => false, 'null' => false], + 'created' => 'datetime', + 'updated' => 'datetime', + ], + 'constraints' => ['primary' => ['type' => 'primary', 'columns' => ['id']]], + ], + [ + 'table' => 'car', + 'columns' => [ + 'id' => ['type' => 'integer'], + 'bake_user_id' => ['type' => 'integer', 'null' => false], + 'title' => ['type' => 'string', 'null' => false], + 'body' => 'text', + 'published' => ['type' => 'boolean', 'length' => 1, 'default' => false], + 'created' => 'datetime', + 'updated' => 'datetime', + ], + 'constraints' => ['primary' => ['type' => 'primary', 'columns' => ['id']]], + ], + [ + 'table' => 'bake_comments', + 'columns' => [ + 'otherid' => ['type' => 'integer'], + 'bake_article_id' => ['type' => 'integer', 'null' => false], + 'bake_user_id' => ['type' => 'integer', 'null' => false], + 'comment' => 'text', + 'published' => ['type' => 'string', 'length' => 1, 'default' => 'N'], + 'created' => ['type' => 'datetime', 'length' => null, 'null' => false, 'default' => null, 'comment' => '', 'precision' => null], + 'updated' => ['type' => 'datetime', 'length' => null, 'null' => true, 'default' => null, 'comment' => '', 'precision' => null], + ], + 'constraints' => ['primary' => ['type' => 'primary', 'columns' => ['otherid']]], + ], + [ + 'table' => 'bake_tags', + 'columns' => [ + 'id' => ['type' => 'integer'], + 'tag' => ['type' => 'string', 'null' => false], + 'created' => 'datetime', + 'updated' => 'datetime', + ], + 'constraints' => ['primary' => ['type' => 'primary', 'columns' => ['id']]], + ], + [ + 'table' => 'bake_authors', + 'columns' => [ + 'id' => ['type' => 'integer'], + 'role_id' => ['type' => 'integer', 'null' => false], + 'name' => ['type' => 'string', 'default' => null], + 'description' => ['type' => 'text', 'default' => null], + 'member' => ['type' => 'boolean'], + 'member_number' => ['type' => 'integer', 'null' => true], + 'account_balance' => ['type' => 'decimal', 'null' => true, 'precision' => 2, 'length' => 12], + 'created' => 'datetime', + 'modified' => 'datetime', + ], + 'constraints' => ['primary' => ['type' => 'primary', 'columns' => ['id']]], + ], + [ + 'table' => 'profiles', + 'columns' => [ + 'id' => ['type' => 'integer'], + 'author_id' => ['type' => 'integer', 'null' => false], + 'nick' => ['type' => 'string', 'null' => false], + 'avatar' => ['type' => 'string', 'default' => null], + ], + 'constraints' => ['primary' => ['type' => 'primary', 'columns' => ['id']]], + ], + [ + 'table' => 'roles', + 'columns' => [ + 'id' => ['type' => 'integer'], + 'name' => ['type' => 'string', 'null' => false], + ], + 'constraints' => ['primary' => ['type' => 'primary', 'columns' => ['id']]], + ], + [ + 'table' => 'binary_tests', + 'columns' => [ + 'id' => ['type' => 'integer'], + 'byte' => ['type' => 'binary', 'length' => 1], + 'data' => ['type' => 'binary', 'length' => 300], + ], + 'constraints' => ['primary' => ['type' => 'primary', 'columns' => ['id']]], + ], + [ + 'table' => 'categories', + 'columns' => [ + 'id' => ['type' => 'integer', 'length' => 11, 'unsigned' => true, 'null' => false, 'default' => null, 'comment' => '', 'autoIncrement' => true, 'precision' => null], + 'created' => ['type' => 'datetime', 'length' => null, 'null' => false, 'default' => null, 'comment' => '', 'precision' => null], + 'modified' => ['type' => 'datetime', 'length' => null, 'null' => false, 'default' => null, 'comment' => '', 'precision' => null], + 'name' => ['type' => 'string', 'length' => 100, 'null' => false, 'default' => '', 'comment' => '', 'precision' => null, 'fixed' => null], + ], + 'constraints' => ['primary' => ['type' => 'primary', 'columns' => ['id']]], + ], + [ + 'table' => 'categories_products', + 'columns' => [ + 'category_id' => ['type' => 'integer', 'length' => 11, 'unsigned' => true, 'null' => false, 'default' => null, 'comment' => '', 'precision' => null, 'autoIncrement' => null], + 'product_id' => ['type' => 'integer', 'length' => 11, 'unsigned' => true, 'null' => false, 'default' => null, 'comment' => '', 'precision' => null, 'autoIncrement' => null], + ], + 'constraints' => ['primary' => ['type' => 'primary', 'columns' => ['category_id', 'product_id'], 'length' => []]], + ], + [ + 'table' => 'category_threads', + 'columns' => [ + 'id' => ['type' => 'integer'], + 'parent_id' => ['type' => 'integer'], + 'name' => ['type' => 'string', 'null' => false], + 'lft' => ['type' => 'integer', 'unsigned' => true], + 'rght' => ['type' => 'integer', 'unsigned' => true], + 'created' => 'datetime', + 'updated' => 'datetime', + ], + 'constraints' => ['primary' => ['type' => 'primary', 'columns' => ['id']]], + ], + [ + 'table' => 'datatypes', + 'columns' => [ + 'id' => ['type' => 'integer', 'null' => false], + 'decimal_field' => ['type' => 'decimal', 'length' => '6', 'precision' => 3, 'default' => '0.000'], + 'float_field' => ['type' => 'float', 'length' => '5,2', 'null' => false, 'default' => null], + 'huge_int' => ['type' => 'biginteger'], + 'small_int' => ['type' => 'smallinteger'], + 'tiny_int' => ['type' => 'tinyinteger'], + 'bool' => ['type' => 'boolean', 'null' => false, 'default' => false], + 'uuid' => ['type' => 'uuid'], + 'timestamp_field' => ['type' => 'timestamp'], + ], + 'constraints' => ['primary' => ['type' => 'primary', 'columns' => ['id']]], + ], + [ + 'table' => 'hidden_fields', + 'columns' => [ + 'id' => ['type' => 'integer'], + 'password' => ['type' => 'string', 'null' => true, 'length' => 255], + 'auth_token' => ['type' => 'string', 'null' => true, 'length' => 255], + ], + 'constraints' => ['primary' => ['type' => 'primary', 'columns' => ['id']]], + ], + [ + 'table' => 'users', + 'columns' => [ + 'id' => ['type' => 'integer'], + 'username' => ['type' => 'string', 'null' => true, 'length' => 255], + 'password' => ['type' => 'string', 'null' => true, 'length' => 255], + 'created' => ['type' => 'timestamp', 'null' => true], + 'updated' => ['type' => 'timestamp', 'null' => true], + ], + 'constraints' => ['primary' => ['type' => 'primary', 'columns' => ['id']]], + ], + [ + 'table' => 'invitations', + 'columns' => [ + 'id' => ['type' => 'integer'], + 'sender_id' => ['type' => 'integer', 'null' => false], + 'receiver_id' => ['type' => 'integer', 'null' => false], + 'body' => 'text', + 'created' => 'datetime', + 'updated' => 'datetime', + ], + 'constraints' => [ + 'primary' => ['type' => 'primary', 'columns' => ['id']], + 'sender_idx' => [ + 'type' => 'foreign', + 'columns' => ['sender_id'], + 'references' => ['users', 'id'], + 'update' => 'noAction', + 'delete' => 'noAction', + ], + 'receiver_idx' => [ + 'type' => 'foreign', + 'columns' => ['receiver_id'], + 'references' => ['users', 'id'], + 'update' => 'noAction', + 'delete' => 'noAction', + ], + ], + ], + [ + 'table' => 'number_trees', + 'columns' => [ + 'id' => ['type' => 'integer'], + 'name' => ['type' => 'string', 'length' => 50, 'null' => false], + 'parent_id' => 'integer', + 'lft' => ['type' => 'integer', 'unsigned' => true], + 'rght' => ['type' => 'integer', 'unsigned' => true], + 'depth' => ['type' => 'integer', 'unsigned' => true], + ], + 'constraints' => ['primary' => ['type' => 'primary', 'columns' => ['id']]], + ], + [ + 'table' => 'old_products', + 'columns' => [ + 'id' => ['type' => 'integer', 'length' => 11, 'unsigned' => true, 'null' => false, 'default' => null, 'comment' => '', 'autoIncrement' => true, 'precision' => null], + 'created' => ['type' => 'datetime', 'length' => null, 'null' => false, 'default' => null, 'comment' => '', 'precision' => null], + 'modified' => ['type' => 'datetime', 'length' => null, 'null' => true, 'default' => null, 'comment' => '', 'precision' => null], + 'name' => ['type' => 'string', 'length' => 100, 'null' => false, 'default' => '', 'comment' => '', 'precision' => null, 'fixed' => null], + ], + 'constraints' => ['primary' => ['type' => 'primary', 'columns' => ['id'], 'length' => []]], + ], + [ + 'table' => 'products', + 'columns' => [ + 'id' => ['type' => 'integer', 'length' => 11, 'unsigned' => true, 'null' => false, 'default' => null, 'comment' => '', 'autoIncrement' => true, 'precision' => null], + 'created' => ['type' => 'datetime', 'length' => null, 'null' => false, 'default' => null, 'comment' => '', 'precision' => null], + 'modified' => ['type' => 'datetime', 'length' => null, 'null' => false, 'default' => null, 'comment' => '', 'precision' => null], + 'name' => ['type' => 'string', 'length' => 100, 'null' => false, 'default' => '', 'comment' => '', 'precision' => null, 'fixed' => null], + ], + 'constraints' => ['primary' => ['type' => 'primary', 'columns' => ['id'], 'length' => []]], + ], + [ + 'table' => 'product_versions', + 'columns' => [ + 'id' => ['type' => 'integer', 'length' => 11, 'unsigned' => true, 'null' => false, 'default' => null, 'comment' => '', 'autoIncrement' => true, 'precision' => null], + 'product_id' => ['type' => 'integer', 'length' => 11, 'unsigned' => true, 'null' => false, 'default' => null, 'comment' => '', 'precision' => null, 'autoIncrement' => null], + 'version' => ['type' => 'datetime', 'length' => null, 'null' => false, 'default' => null, 'comment' => '', 'precision' => null], + ], + 'constraints' => ['primary' => ['type' => 'primary', 'columns' => ['id'], 'length' => []]], + ], + [ + 'table' => 'todo_items', + 'columns' => [ + 'id' => ['type' => 'integer', 'null' => false], + 'user_id' => ['type' => 'integer', 'null' => false], + 'title' => ['type' => 'string', 'length' => 50, 'null' => false], + 'body' => ['type' => 'text'], + 'effort' => ['type' => 'decimal', 'default' => 0, 'null' => false], + 'completed' => ['type' => 'boolean', 'default' => false, 'null' => false], + 'todo_task_count' => ['type' => 'integer', 'default' => 0, 'null' => false], + 'created' => ['type' => 'datetime'], + 'updated' => ['type' => 'datetime'], + ], + 'constraints' => ['primary' => ['type' => 'primary', 'columns' => ['id']]], + ], + [ + 'table' => 'todo_labels', + 'columns' => [ + 'id' => ['type' => 'integer'], + 'label' => ['type' => 'string', 'null' => false], + ], + 'constraints' => ['primary' => ['type' => 'primary', 'columns' => ['id']]], + ], + [ + 'table' => 'todo_items_todo_labels', + 'columns' => [ + 'todo_item_id' => ['type' => 'integer', 'null' => false], + 'todo_label_id' => ['type' => 'integer', 'null' => false], + ], + 'constraints' => [ + 'primary' => ['type' => 'primary', 'columns' => ['todo_item_id', 'todo_label_id']], + 'item_fk' => [ + 'type' => 'foreign', + 'columns' => ['todo_item_id'], + 'references' => ['todo_items', 'id'], + 'update' => 'cascade', + 'delete' => 'cascade', + ], + 'label_fk' => [ + 'type' => 'foreign', + 'columns' => ['todo_label_id'], + 'references' => ['todo_labels', 'id'], + 'update' => 'cascade', + 'delete' => 'cascade', + ], + ], + ], + [ + 'table' => 'todo_tasks', + 'columns' => [ + 'uid' => ['type' => 'integer'], + 'todo_item_id' => ['type' => 'integer', 'null' => false], + 'title' => ['type' => 'string', 'length' => 50, 'null' => false], + 'body' => 'text', + 'completed' => ['type' => 'boolean', 'default' => false, 'null' => false], + 'effort' => ['type' => 'decimal', 'default' => 0.0, 'null' => false, 'unsigned' => true], + 'created' => 'datetime', + 'updated' => 'datetime', + ], + 'constraints' => ['primary' => ['type' => 'primary', 'columns' => ['uid']]], + ], + [ + 'table' => 'unique_fields', + 'columns' => [ + 'id' => ['type' => 'integer'], + 'username' => ['type' => 'string', 'null' => true, 'length' => 255], + 'email' => ['type' => 'string', 'null' => true, 'length' => 255], + 'field_1' => ['type' => 'string', 'null' => true, 'length' => 255], + 'field_2' => ['type' => 'string', 'null' => true,'length' => 255], + ], + 'constraints' => [ + 'primary' => [ + 'type' => 'primary', + 'columns' => ['id'], + ], + 'multiple_fields_unique' => [ + 'type' => 'unique', + 'columns' => [ + 'field_1', + 'field_2', + ], + ], + ], + ], +]; diff --git a/app/vendor/cakephp/cakephp/src/Controller/Exception/InvalidParameterException.php b/app/vendor/cakephp/cakephp/src/Controller/Exception/InvalidParameterException.php new file mode 100644 index 000000000..b7d6fc8e2 --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/Controller/Exception/InvalidParameterException.php @@ -0,0 +1,52 @@ + + */ + protected $templates = [ + 'failed_coercion' => 'Unable to coerce "%s" to `%s` for `%s` in action %s::%s().', + 'missing_dependency' => 'Failed to inject dependency from service container for parameter `%s` ' . + 'with type `%s` in action %s::%s().', + 'missing_parameter' => 'Missing passed parameter for `%s` in action %s::%s().', + 'unsupported_type' => 'Type declaration for `%s` in action %s::%s() is unsupported.', + ]; + + /** + * Switches message template based on `template` key in message array. + * + * @param string|array $message Either the string of the error message, or an array of attributes + * that are made available in the view, and sprintf()'d into Exception::$_messageTemplate + * @param int|null $code The error code + * @param \Throwable|null $previous the previous exception. + */ + public function __construct($message = '', ?int $code = null, ?Throwable $previous = null) + { + if (is_array($message)) { + $this->_messageTemplate = $this->templates[$message['template']] ?? ''; + unset($message['template']); + } + parent::__construct($message, $code, $previous); + } +} diff --git a/app/vendor/cakephp/cakephp/src/Database/Expression/CaseExpressionTrait.php b/app/vendor/cakephp/cakephp/src/Database/Expression/CaseExpressionTrait.php new file mode 100644 index 000000000..e486a193f --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/Database/Expression/CaseExpressionTrait.php @@ -0,0 +1,106 @@ +_typeMap !== null && + $value instanceof IdentifierExpression + ) { + $type = $this->_typeMap->type($value->getIdentifier()); + } + + return $type; + } + + /** + * Compiles a nullable value to SQL. + * + * @param \Cake\Database\ValueBinder $binder The value binder to use. + * @param \Cake\Database\ExpressionInterface|object|scalar|null $value The value to compile. + * @param string|null $type The value type. + * @return string + */ + protected function compileNullableValue(ValueBinder $binder, $value, ?string $type = null): string + { + if ( + $type !== null && + !($value instanceof ExpressionInterface) + ) { + $value = $this->_castToExpression($value, $type); + } + + if ($value === null) { + $value = 'NULL'; + } elseif ($value instanceof Query) { + $value = sprintf('(%s)', $value->sql($binder)); + } elseif ($value instanceof ExpressionInterface) { + $value = $value->sql($binder); + } else { + $placeholder = $binder->placeholder('c'); + $binder->bind($placeholder, $value, $type); + $value = $placeholder; + } + + return $value; + } +} diff --git a/app/vendor/cakephp/cakephp/src/Database/Expression/CaseStatementExpression.php b/app/vendor/cakephp/cakephp/src/Database/Expression/CaseStatementExpression.php new file mode 100644 index 000000000..40bacf94b --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/Database/Expression/CaseStatementExpression.php @@ -0,0 +1,596 @@ + + */ + protected $validClauseNames = [ + 'value', + 'when', + 'else', + ]; + + /** + * Whether this is a simple case expression. + * + * @var bool + */ + protected $isSimpleVariant = false; + + /** + * The case value. + * + * @var \Cake\Database\ExpressionInterface|object|scalar|null + */ + protected $value = null; + + /** + * The case value type. + * + * @var string|null + */ + protected $valueType = null; + + /** + * The `WHEN ... THEN ...` expressions. + * + * @var array<\Cake\Database\Expression\WhenThenExpression> + */ + protected $when = []; + + /** + * Buffer that holds values and types for use with `then()`. + * + * @var array|null + */ + protected $whenBuffer = null; + + /** + * The else part result value. + * + * @var \Cake\Database\ExpressionInterface|object|scalar|null + */ + protected $else = null; + + /** + * The else part result type. + * + * @var string|null + */ + protected $elseType = null; + + /** + * The return type. + * + * @var string|null + */ + protected $returnType = null; + + /** + * Constructor. + * + * When a value is set, the syntax generated is + * `CASE case_value WHEN when_value ... END` (simple case), + * where the `when_value`'s are compared against the + * `case_value`. + * + * When no value is set, the syntax generated is + * `CASE WHEN when_conditions ... END` (searched case), + * where the conditions hold the comparisons. + * + * Note that `null` is a valid case value, and thus should + * only be passed if you actually want to create the simple + * case expression variant! + * + * @param \Cake\Database\ExpressionInterface|object|scalar|null $value The case value. + * @param string|null $type The case value type. If no type is provided, the type will be tried to be inferred + * from the value. + */ + public function __construct($value = null, ?string $type = null) + { + if (func_num_args() > 0) { + if ( + $value !== null && + !is_scalar($value) && + !(is_object($value) && !($value instanceof Closure)) + ) { + throw new InvalidArgumentException(sprintf( + 'The `$value` argument must be either `null`, a scalar value, an object, ' . + 'or an instance of `\%s`, `%s` given.', + ExpressionInterface::class, + getTypeName($value) + )); + } + + $this->value = $value; + + if ( + $value !== null && + $type === null && + !($value instanceof ExpressionInterface) + ) { + $type = $this->inferType($value); + } + $this->valueType = $type; + + $this->isSimpleVariant = true; + } + } + + /** + * Sets the `WHEN` value for a `WHEN ... THEN ...` expression, or a + * self-contained expression that holds both the value for `WHEN` + * and the value for `THEN`. + * + * ### Order based syntax + * + * When passing a value other than a self-contained + * `\Cake\Database\Expression\WhenThenExpression`, + * instance, the `WHEN ... THEN ...` statement must be closed off with + * a call to `then()` before invoking `when()` again or `else()`: + * + * ``` + * $queryExpression + * ->case($query->identifier('Table.column')) + * ->when(true) + * ->then('Yes') + * ->when(false) + * ->then('No') + * ->else('Maybe'); + * ``` + * + * ### Self-contained expressions + * + * When passing an instance of `\Cake\Database\Expression\WhenThenExpression`, + * being it directly, or via a callable, then there is no need to close + * using `then()` on this object, instead the statement will be closed + * on the `\Cake\Database\Expression\WhenThenExpression` + * object using + * `\Cake\Database\Expression\WhenThenExpression::then()`. + * + * Callables will receive an instance of `\Cake\Database\Expression\WhenThenExpression`, + * and must return one, being it the same object, or a custom one: + * + * ``` + * $queryExpression + * ->case() + * ->when(function (\Cake\Database\Expression\WhenThenExpression $whenThen) { + * return $whenThen + * ->when(['Table.column' => true]) + * ->then('Yes'); + * }) + * ->when(function (\Cake\Database\Expression\WhenThenExpression $whenThen) { + * return $whenThen + * ->when(['Table.column' => false]) + * ->then('No'); + * }) + * ->else('Maybe'); + * ``` + * + * ### Type handling + * + * The types provided via the `$type` argument will be merged with the + * type map set for this expression. When using callables for `$when`, + * the `\Cake\Database\Expression\WhenThenExpression` + * instance received by the callables will inherit that type map, however + * the types passed here will _not_ be merged in case of using callables, + * instead the types must be passed in + * `\Cake\Database\Expression\WhenThenExpression::when()`: + * + * ``` + * $queryExpression + * ->case() + * ->when(function (\Cake\Database\Expression\WhenThenExpression $whenThen) { + * return $whenThen + * ->when(['unmapped_column' => true], ['unmapped_column' => 'bool']) + * ->then('Yes'); + * }) + * ->when(function (\Cake\Database\Expression\WhenThenExpression $whenThen) { + * return $whenThen + * ->when(['unmapped_column' => false], ['unmapped_column' => 'bool']) + * ->then('No'); + * }) + * ->else('Maybe'); + * ``` + * + * ### User data safety + * + * When passing user data, be aware that allowing a user defined array + * to be passed, is a potential SQL injection vulnerability, as it + * allows for raw SQL to slip in! + * + * The following is _unsafe_ usage that must be avoided: + * + * ``` + * $case + * ->when($userData) + * ``` + * + * A safe variant for the above would be to define a single type for + * the value: + * + * ``` + * $case + * ->when($userData, 'integer') + * ``` + * + * This way an exception would be triggered when an array is passed for + * the value, thus preventing raw SQL from slipping in, and all other + * types of values would be forced to be bound as an integer. + * + * Another way to safely pass user data is when using a conditions + * array, and passing user data only on the value side of the array + * entries, which will cause them to be bound: + * + * ``` + * $case + * ->when([ + * 'Table.column' => $userData, + * ]) + * ``` + * + * Lastly, data can also be bound manually: + * + * ``` + * $query + * ->select([ + * 'val' => $query->newExpr() + * ->case() + * ->when($query->newExpr(':userData')) + * ->then(123) + * ]) + * ->bind(':userData', $userData, 'integer') + * ``` + * + * @param \Cake\Database\ExpressionInterface|\Closure|object|array|scalar $when The `WHEN` value. When using an + * array of conditions, it must be compatible with `\Cake\Database\Query::where()`. Note that this argument is + * _not_ completely safe for use with user data, as a user supplied array would allow for raw SQL to slip in! If + * you plan to use user data, either pass a single type for the `$type` argument (which forces the `$when` value to + * be a non-array, and then always binds the data), use a conditions array where the user data is only passed on + * the value side of the array entries, or custom bindings! + * @param array|string|null $type The when value type. Either an associative array when using array style + * conditions, or else a string. If no type is provided, the type will be tried to be inferred from the value. + * @return $this + * @throws \LogicException In case this a closing `then()` call is required before calling this method. + * @throws \LogicException In case the callable doesn't return an instance of + * `\Cake\Database\Expression\WhenThenExpression`. + */ + public function when($when, $type = null) + { + if ($this->whenBuffer !== null) { + throw new LogicException('Cannot call `when()` between `when()` and `then()`.'); + } + + if ($when instanceof Closure) { + $when = $when(new WhenThenExpression($this->getTypeMap())); + if (!($when instanceof WhenThenExpression)) { + throw new LogicException(sprintf( + '`when()` callables must return an instance of `\%s`, `%s` given.', + WhenThenExpression::class, + getTypeName($when) + )); + } + } + + if ($when instanceof WhenThenExpression) { + $this->when[] = $when; + } else { + $this->whenBuffer = ['when' => $when, 'type' => $type]; + } + + return $this; + } + + /** + * Sets the `THEN` result value for the last `WHEN ... THEN ...` + * statement that was opened using `when()`. + * + * ### Order based syntax + * + * This method can only be invoked in case `when()` was previously + * used with a value other than a closure or an instance of + * `\Cake\Database\Expression\WhenThenExpression`: + * + * ``` + * $case + * ->when(['Table.column' => true]) + * ->then('Yes') + * ->when(['Table.column' => false]) + * ->then('No') + * ->else('Maybe'); + * ``` + * + * The following would all fail with an exception: + * + * ``` + * $case + * ->when(['Table.column' => true]) + * ->when(['Table.column' => false]) + * // ... + * ``` + * + * ``` + * $case + * ->when(['Table.column' => true]) + * ->else('Maybe') + * // ... + * ``` + * + * ``` + * $case + * ->then('Yes') + * // ... + * ``` + * + * ``` + * $case + * ->when(['Table.column' => true]) + * ->then('Yes') + * ->then('No') + * // ... + * ``` + * + * @param \Cake\Database\ExpressionInterface|object|scalar|null $result The result value. + * @param string|null $type The result type. If no type is provided, the type will be tried to be inferred from the + * value. + * @return $this + * @throws \LogicException In case `when()` wasn't previously called with a value other than a closure or an + * instance of `\Cake\Database\Expression\WhenThenExpression`. + */ + public function then($result, ?string $type = null) + { + if ($this->whenBuffer === null) { + throw new LogicException('Cannot call `then()` before `when()`.'); + } + + $whenThen = (new WhenThenExpression($this->getTypeMap())) + ->when($this->whenBuffer['when'], $this->whenBuffer['type']) + ->then($result, $type); + + $this->whenBuffer = null; + + $this->when[] = $whenThen; + + return $this; + } + + /** + * Sets the `ELSE` result value. + * + * @param \Cake\Database\ExpressionInterface|object|scalar|null $result The result value. + * @param string|null $type The result type. If no type is provided, the type will be tried to be inferred from the + * value. + * @return $this + * @throws \LogicException In case a closing `then()` call is required before calling this method. + * @throws \InvalidArgumentException In case the `$result` argument is neither a scalar value, nor an object, an + * instance of `\Cake\Database\ExpressionInterface`, or `null`. + */ + public function else($result, ?string $type = null) + { + if ($this->whenBuffer !== null) { + throw new LogicException('Cannot call `else()` between `when()` and `then()`.'); + } + + if ( + $result !== null && + !is_scalar($result) && + !(is_object($result) && !($result instanceof Closure)) + ) { + throw new InvalidArgumentException(sprintf( + 'The `$result` argument must be either `null`, a scalar value, an object, ' . + 'or an instance of `\%s`, `%s` given.', + ExpressionInterface::class, + getTypeName($result) + )); + } + + if ($type === null) { + $type = $this->inferType($result); + } + + $this->else = $result; + $this->elseType = $type; + + return $this; + } + + /** + * Returns the abstract type that this expression will return. + * + * If no type has been explicitly set via `setReturnType()`, this + * method will try to obtain the type from the result types of the + * `then()` and `else() `calls. All types must be identical in order + * for this to work, otherwise the type will default to `string`. + * + * @return string + * @see CaseStatementExpression::then() + */ + public function getReturnType(): string + { + if ($this->returnType !== null) { + return $this->returnType; + } + + $types = []; + foreach ($this->when as $when) { + $type = $when->getResultType(); + if ($type !== null) { + $types[] = $type; + } + } + + if ($this->elseType !== null) { + $types[] = $this->elseType; + } + + $types = array_unique($types); + if (count($types) === 1) { + return $types[0]; + } + + return 'string'; + } + + /** + * Sets the abstract type that this expression will return. + * + * If no type is being explicitly set via this method, then the + * `getReturnType()` method will try to infer the type from the + * result types of the `then()` and `else() `calls. + * + * @param string $type The type name to use. + * @return $this + */ + public function setReturnType(string $type) + { + $this->returnType = $type; + + return $this; + } + + /** + * Returns the available data for the given clause. + * + * ### Available clauses + * + * The following clause names are available: + * + * * `value`: The case value for a `CASE case_value WHEN ...` expression. + * * `when`: An array of `WHEN ... THEN ...` expressions. + * * `else`: The `ELSE` result value. + * + * @param string $clause The name of the clause to obtain. + * @return \Cake\Database\ExpressionInterface|object|array<\Cake\Database\Expression\WhenThenExpression>|scalar|null + * @throws \InvalidArgumentException In case the given clause name is invalid. + */ + public function clause(string $clause) + { + if (!in_array($clause, $this->validClauseNames, true)) { + throw new InvalidArgumentException( + sprintf( + 'The `$clause` argument must be one of `%s`, the given value `%s` is invalid.', + implode('`, `', $this->validClauseNames), + $clause + ) + ); + } + + return $this->{$clause}; + } + + /** + * @inheritDoc + */ + public function sql(ValueBinder $binder): string + { + if ($this->whenBuffer !== null) { + throw new LogicException('Case expression has incomplete when clause. Missing `then()` after `when()`.'); + } + + if (empty($this->when)) { + throw new LogicException('Case expression must have at least one when statement.'); + } + + $value = ''; + if ($this->isSimpleVariant) { + $value = $this->compileNullableValue($binder, $this->value, $this->valueType) . ' '; + } + + $whenThenExpressions = []; + foreach ($this->when as $whenThen) { + $whenThenExpressions[] = $whenThen->sql($binder); + } + $whenThen = implode(' ', $whenThenExpressions); + + $else = $this->compileNullableValue($binder, $this->else, $this->elseType); + + return "CASE {$value}{$whenThen} ELSE $else END"; + } + + /** + * @inheritDoc + */ + public function traverse(Closure $callback) + { + if ($this->whenBuffer !== null) { + throw new LogicException('Case expression has incomplete when clause. Missing `then()` after `when()`.'); + } + + if ($this->value instanceof ExpressionInterface) { + $callback($this->value); + $this->value->traverse($callback); + } + + foreach ($this->when as $when) { + $callback($when); + $when->traverse($callback); + } + + if ($this->else instanceof ExpressionInterface) { + $callback($this->else); + $this->else->traverse($callback); + } + + return $this; + } + + /** + * Clones the inner expression objects. + * + * @return void + */ + public function __clone() + { + if ($this->whenBuffer !== null) { + throw new LogicException('Case expression has incomplete when clause. Missing `then()` after `when()`.'); + } + + if ($this->value instanceof ExpressionInterface) { + $this->value = clone $this->value; + } + + foreach ($this->when as $key => $when) { + $this->when[$key] = clone $this->when[$key]; + } + + if ($this->else instanceof ExpressionInterface) { + $this->else = clone $this->else; + } + } +} diff --git a/app/vendor/cakephp/cakephp/src/Database/Expression/WhenThenExpression.php b/app/vendor/cakephp/cakephp/src/Database/Expression/WhenThenExpression.php new file mode 100644 index 000000000..bf51eaf19 --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/Database/Expression/WhenThenExpression.php @@ -0,0 +1,349 @@ + + */ + protected $validClauseNames = [ + 'when', + 'then', + ]; + + /** + * The type map to use when using an array of conditions for the + * `WHEN` value. + * + * @var \Cake\Database\TypeMap + */ + protected $_typeMap; + + /** + * Then `WHEN` value. + * + * @var \Cake\Database\ExpressionInterface|object|scalar|null + */ + protected $when = null; + + /** + * The `WHEN` value type. + * + * @var array|string|null + */ + protected $whenType = null; + + /** + * The `THEN` value. + * + * @var \Cake\Database\ExpressionInterface|object|scalar|null + */ + protected $then = null; + + /** + * Whether the `THEN` value has been defined, eg whether `then()` + * has been invoked. + * + * @var bool + */ + protected $hasThenBeenDefined = false; + + /** + * The `THEN` result type. + * + * @var string|null + */ + protected $thenType = null; + + /** + * Constructor. + * + * @param \Cake\Database\TypeMap|null $typeMap The type map to use when using an array of conditions for the `WHEN` + * value. + */ + public function __construct(?TypeMap $typeMap = null) + { + if ($typeMap === null) { + $typeMap = new TypeMap(); + } + $this->_typeMap = $typeMap; + } + + /** + * Sets the `WHEN` value. + * + * @param \Cake\Database\ExpressionInterface|object|array|scalar $when The `WHEN` value. When using an array of + * conditions, it must be compatible with `\Cake\Database\Query::where()`. Note that this argument is _not_ + * completely safe for use with user data, as a user supplied array would allow for raw SQL to slip in! If you + * plan to use user data, either pass a single type for the `$type` argument (which forces the `$when` value to be + * a non-array, and then always binds the data), use a conditions array where the user data is only passed on the + * value side of the array entries, or custom bindings! + * @param array|string|null $type The when value type. Either an associative array when using array style + * conditions, or else a string. If no type is provided, the type will be tried to be inferred from the value. + * @return $this + * @throws \InvalidArgumentException In case the `$when` argument is neither a non-empty array, nor a scalar value, + * an object, or an instance of `\Cake\Database\ExpressionInterface`. + * @throws \InvalidArgumentException In case the `$type` argument is neither an array, a string, nor null. + * @throws \InvalidArgumentException In case the `$when` argument is an array, and the `$type` argument is neither + * an array, nor null. + * @throws \InvalidArgumentException In case the `$when` argument is a non-array value, and the `$type` argument is + * neither a string, nor null. + * @see CaseStatementExpression::when() for a more detailed usage explanation. + */ + public function when($when, $type = null) + { + if ( + !(is_array($when) && !empty($when)) && + !is_scalar($when) && + !is_object($when) + ) { + throw new InvalidArgumentException(sprintf( + 'The `$when` argument must be either a non-empty array, a scalar value, an object, ' . + 'or an instance of `\%s`, `%s` given.', + ExpressionInterface::class, + is_array($when) ? '[]' : getTypeName($when) + )); + } + + if ( + $type !== null && + !is_array($type) && + !is_string($type) + ) { + throw new InvalidArgumentException(sprintf( + 'The `$type` argument must be either an array, a string, or `null`, `%s` given.', + getTypeName($type) + )); + } + + if (is_array($when)) { + if ( + $type !== null && + !is_array($type) + ) { + throw new InvalidArgumentException(sprintf( + 'When using an array for the `$when` argument, the `$type` argument must be an ' . + 'array too, `%s` given.', + getTypeName($type) + )); + } + + // avoid dirtying the type map for possible consecutive `when()` calls + $typeMap = clone $this->_typeMap; + if ( + is_array($type) && + count($type) > 0 + ) { + $typeMap = $typeMap->setTypes($type); + } + + $when = new QueryExpression($when, $typeMap); + } else { + if ( + $type !== null && + !is_string($type) + ) { + throw new InvalidArgumentException(sprintf( + 'When using a non-array value for the `$when` argument, the `$type` argument must ' . + 'be a string, `%s` given.', + getTypeName($type) + )); + } + + if ( + $type === null && + !($when instanceof ExpressionInterface) + ) { + $type = $this->inferType($when); + } + } + + $this->when = $when; + $this->whenType = $type; + + return $this; + } + + /** + * Sets the `THEN` result value. + * + * @param \Cake\Database\ExpressionInterface|object|scalar|null $result The result value. + * @param string|null $type The result type. If no type is provided, the type will be inferred from the given + * result value. + * @return $this + */ + public function then($result, ?string $type = null) + { + if ( + $result !== null && + !is_scalar($result) && + !(is_object($result) && !($result instanceof Closure)) + ) { + throw new InvalidArgumentException(sprintf( + 'The `$result` argument must be either `null`, a scalar value, an object, ' . + 'or an instance of `\%s`, `%s` given.', + ExpressionInterface::class, + getTypeName($result) + )); + } + + $this->then = $result; + + if ($type === null) { + $type = $this->inferType($result); + } + + $this->thenType = $type; + + $this->hasThenBeenDefined = true; + + return $this; + } + + /** + * Returns the expression's result value type. + * + * @return string|null + * @see WhenThenExpression::then() + */ + public function getResultType(): ?string + { + return $this->thenType; + } + + /** + * Returns the available data for the given clause. + * + * ### Available clauses + * + * The following clause names are available: + * + * * `when`: The `WHEN` value. + * * `then`: The `THEN` result value. + * + * @param string $clause The name of the clause to obtain. + * @return \Cake\Database\ExpressionInterface|object|scalar|null + * @throws \InvalidArgumentException In case the given clause name is invalid. + */ + public function clause(string $clause) + { + if (!in_array($clause, $this->validClauseNames, true)) { + throw new InvalidArgumentException( + sprintf( + 'The `$clause` argument must be one of `%s`, the given value `%s` is invalid.', + implode('`, `', $this->validClauseNames), + $clause + ) + ); + } + + return $this->{$clause}; + } + + /** + * @inheritDoc + */ + public function sql(ValueBinder $binder): string + { + if ($this->when === null) { + throw new LogicException('Case expression has incomplete when clause. Missing `when()`.'); + } + + if (!$this->hasThenBeenDefined) { + throw new LogicException('Case expression has incomplete when clause. Missing `then()` after `when()`.'); + } + + $when = $this->when; + if ( + is_string($this->whenType) && + !($when instanceof ExpressionInterface) + ) { + $when = $this->_castToExpression($when, $this->whenType); + } + if ($when instanceof Query) { + $when = sprintf('(%s)', $when->sql($binder)); + } elseif ($when instanceof ExpressionInterface) { + $when = $when->sql($binder); + } else { + $placeholder = $binder->placeholder('c'); + if (is_string($this->whenType)) { + $whenType = $this->whenType; + } else { + $whenType = null; + } + $binder->bind($placeholder, $when, $whenType); + $when = $placeholder; + } + + $then = $this->compileNullableValue($binder, $this->then, $this->thenType); + + return "WHEN $when THEN $then"; + } + + /** + * @inheritDoc + */ + public function traverse(Closure $callback) + { + if ($this->when instanceof ExpressionInterface) { + $callback($this->when); + $this->when->traverse($callback); + } + + if ($this->then instanceof ExpressionInterface) { + $callback($this->then); + $this->then->traverse($callback); + } + + return $this; + } + + /** + * Clones the inner expression objects. + * + * @return void + */ + public function __clone() + { + if ($this->when instanceof ExpressionInterface) { + $this->when = clone $this->when; + } + + if ($this->then instanceof ExpressionInterface) { + $this->then = clone $this->then; + } + } +} diff --git a/app/vendor/cakephp/cakephp/src/Database/Type/ColumnSchemaAwareInterface.php b/app/vendor/cakephp/cakephp/src/Database/Type/ColumnSchemaAwareInterface.php new file mode 100644 index 000000000..1c7bdac45 --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/Database/Type/ColumnSchemaAwareInterface.php @@ -0,0 +1,29 @@ +|null Array of column information, or `null` in case the column isn't processed by this type. + */ + public function convertColumnDefinition(array $definition, DriverInterface $driver): ?array; +} diff --git a/app/vendor/cakephp/cakephp/src/Http/Client/Adapter/Mock.php b/app/vendor/cakephp/cakephp/src/Http/Client/Adapter/Mock.php new file mode 100644 index 000000000..15ee3bf21 --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/Http/Client/Adapter/Mock.php @@ -0,0 +1,134 @@ + $options See above. + * @return void + */ + public function addResponse(RequestInterface $request, Response $response, array $options): void + { + if (isset($options['match']) && !($options['match'] instanceof Closure)) { + $type = getTypeName($options['match']); + throw new InvalidArgumentException("The `match` option must be a `Closure`. Got `{$type}`."); + } + $this->responses[] = [ + 'request' => $request, + 'response' => $response, + 'options' => $options, + ]; + } + + /** + * Find a response if one exists. + * + * @param \Psr\Http\Message\RequestInterface $request The request to match + * @param array $options Unused. + * @return \Cake\Http\Client\Response[] The matched response or an empty array for no matches. + */ + public function send(RequestInterface $request, array $options): array + { + $found = null; + $method = $request->getMethod(); + $requestUri = (string)$request->getUri(); + + foreach ($this->responses as $index => $mock) { + if ($method !== $mock['request']->getMethod()) { + continue; + } + if (!$this->urlMatches($requestUri, $mock['request'])) { + continue; + } + if (isset($mock['options']['match'])) { + $match = $mock['options']['match']($request); + if (!is_bool($match)) { + throw new InvalidArgumentException('Match callback must return a boolean value.'); + } + if (!$match) { + continue; + } + } + $found = $index; + break; + } + if ($found !== null) { + // Move the current mock to the end so that when there are multiple + // matches for a URL the next match is used on subsequent requests. + $mock = $this->responses[$found]; + unset($this->responses[$found]); + $this->responses[] = $mock; + + return [$mock['response']]; + } + + throw new MissingResponseException(['method' => $method, 'url' => $requestUri]); + } + + /** + * Check if the request URI matches the mock URI. + * + * @param string $requestUri The request being sent. + * @param \Psr\Http\Message\RequestInterface $mock The request being mocked. + * @return bool + */ + protected function urlMatches(string $requestUri, RequestInterface $mock): bool + { + $mockUri = (string)$mock->getUri(); + if ($requestUri === $mockUri) { + return true; + } + $starPosition = strrpos($mockUri, '/%2A'); + if ($starPosition === strlen($mockUri) - 4) { + $mockUri = substr($mockUri, 0, $starPosition); + + return strpos($requestUri, $mockUri) === 0; + } + + return false; + } +} diff --git a/app/vendor/cakephp/cakephp/src/Http/Client/Exception/MissingResponseException.php b/app/vendor/cakephp/cakephp/src/Http/Client/Exception/MissingResponseException.php new file mode 100644 index 000000000..8f80cfdbc --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/Http/Client/Exception/MissingResponseException.php @@ -0,0 +1,29 @@ + + */ + protected $_defaultConfig = [ + ]; + + /** + * @param array $config Config options + */ + public function __construct(array $config = []) + { + $this->setConfig($config); + } + + /** + * Formats message. + * + * @param mixed $level Logging level + * @param string $message Message string + * @param array $context Mesage context + * @return string Formatted message + */ + abstract public function format($level, string $message, array $context = []): string; +} diff --git a/app/vendor/cakephp/cakephp/src/Log/Formatter/DefaultFormatter.php b/app/vendor/cakephp/cakephp/src/Log/Formatter/DefaultFormatter.php new file mode 100644 index 000000000..b4fc4d4a1 --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/Log/Formatter/DefaultFormatter.php @@ -0,0 +1,58 @@ + + */ + protected $_defaultConfig = [ + 'dateFormat' => 'Y-m-d H:i:s', + 'includeTags' => false, + 'includeDate' => true, + ]; + + /** + * @param array $config Formatter config + */ + public function __construct(array $config = []) + { + $this->setConfig($config); + } + + /** + * @inheritDoc + */ + public function format($level, string $message, array $context = []): string + { + if ($this->_config['includeDate']) { + $message = sprintf('%s %s: %s', (new DateTime())->format($this->_config['dateFormat']), $level, $message); + } else { + $message = sprintf('%s: %s', $level, $message); + } + if ($this->_config['includeTags']) { + $message = sprintf('<%s>%s', $level, $message, $level); + } + + return $message; + } +} diff --git a/app/vendor/cakephp/cakephp/src/Log/Formatter/JsonFormatter.php b/app/vendor/cakephp/cakephp/src/Log/Formatter/JsonFormatter.php new file mode 100644 index 000000000..430df4933 --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/Log/Formatter/JsonFormatter.php @@ -0,0 +1,50 @@ + + */ + protected $_defaultConfig = [ + 'dateFormat' => DATE_ATOM, + 'flags' => JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES, + 'appendNewline' => true, + ]; + + /** + * @param array $config Formatter config + */ + public function __construct(array $config = []) + { + $this->setConfig($config); + } + + /** + * @inheritDoc + */ + public function format($level, string $message, array $context = []): string + { + $log = ['date' => date($this->_config['dateFormat']), 'level' => (string)$level, 'message' => $message]; + $json = json_encode($log, $this->_config['flags']); + + return $this->_config['appendNewline'] ? $json . "\n" : $json; + } +} diff --git a/app/vendor/cakephp/cakephp/src/Log/Formatter/LegacySyslogFormatter.php b/app/vendor/cakephp/cakephp/src/Log/Formatter/LegacySyslogFormatter.php new file mode 100644 index 000000000..7c03cc6b6 --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/Log/Formatter/LegacySyslogFormatter.php @@ -0,0 +1,48 @@ + + */ + protected $_defaultConfig = [ + 'format' => '%s: %s', + ]; + + /** + * @param array $config Formatter config + */ + public function __construct(array $config = []) + { + $this->setConfig($config); + } + + /** + * @inheritDoc + */ + public function format($level, string $message, array $context = []): string + { + return sprintf($this->getConfig('format'), $level, $message); + } +} diff --git a/app/vendor/cakephp/cakephp/src/TestSuite/ConnectionHelper.php b/app/vendor/cakephp/cakephp/src/TestSuite/ConnectionHelper.php new file mode 100644 index 000000000..f696d6d12 --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/TestSuite/ConnectionHelper.php @@ -0,0 +1,166 @@ +` aliases for all non-test connections. + * + * This forces all models to use the test connection instead. For example, + * if a model is confused to use connection `files` then it will be aliased + * to `test_files`. + * + * The `default` connection is aliased to `test`. + * + * @return void + */ + public function addTestAliases(): void + { + ConnectionManager::alias('test', 'default'); + foreach (ConnectionManager::configured() as $connection) { + if ($connection === 'test' || $connection === 'default') { + continue; + } + + if (strpos($connection, 'test_') === 0) { + $original = substr($connection, 5); + ConnectionManager::alias($connection, $original); + } else { + $test = 'test_' . $connection; + ConnectionManager::alias($test, $connection); + } + } + } + + /** + * Enables query logging for all database connections. + * + * @param array|null $connections Connection names or null for all. + * @return void + */ + public function enableQueryLogging(?array $connections = null): void + { + $connections = $connections ?? ConnectionManager::configured(); + foreach ($connections as $connection) { + $connection = ConnectionManager::get($connection); + if ($connection instanceof Connection) { + $connection->enableQueryLogging(); + } + } + } + + /** + * Drops all tables. + * + * @param string $connectionName Connection name + * @param array|null $tables List of tables names or null for all. + * @return void + */ + public function dropTables(string $connectionName, ?array $tables = null): void + { + /** @var \Cake\Database\Connection $connection */ + $connection = ConnectionManager::get($connectionName); + $collection = $connection->getSchemaCollection(); + + if (method_exists($collection, 'listTablesWithoutViews')) { + $allTables = $collection->listTablesWithoutViews(); + } else { + $allTables = $collection->listTables(); + } + + $tables = $tables !== null ? array_intersect($tables, $allTables) : $allTables; + $schemas = array_map(function ($table) use ($collection) { + return $collection->describe($table); + }, $tables); + + $dialect = $connection->getDriver()->schemaDialect(); + /** @var \Cake\Database\Schema\TableSchema $schema */ + foreach ($schemas as $schema) { + foreach ($dialect->dropConstraintSql($schema) as $statement) { + $connection->execute($statement)->closeCursor(); + } + } + /** @var \Cake\Database\Schema\TableSchema $schema */ + foreach ($schemas as $schema) { + foreach ($dialect->dropTableSql($schema) as $statement) { + $connection->execute($statement)->closeCursor(); + } + } + } + + /** + * Truncates all tables. + * + * @param string $connectionName Connection name + * @param array|null $tables List of tables names or null for all. + * @return void + */ + public function truncateTables(string $connectionName, ?array $tables = null): void + { + /** @var \Cake\Database\Connection $connection */ + $connection = ConnectionManager::get($connectionName); + $collection = $connection->getSchemaCollection(); + + $allTables = $collection->listTablesWithoutViews(); + $tables = $tables !== null ? array_intersect($tables, $allTables) : $allTables; + $schemas = array_map(function ($table) use ($collection) { + return $collection->describe($table); + }, $tables); + + $this->runWithoutConstraints($connection, function (Connection $connection) use ($schemas): void { + $dialect = $connection->getDriver()->schemaDialect(); + /** @var \Cake\Database\Schema\TableSchema $schema */ + foreach ($schemas as $schema) { + foreach ($dialect->truncateTableSql($schema) as $statement) { + $connection->execute($statement)->closeCursor(); + } + } + }); + } + + /** + * Runs callback with constraints disabled correctly per-database + * + * @param \Cake\Database\Connection $connection Database connection + * @param \Closure $callback callback + * @return void + */ + public function runWithoutConstraints(Connection $connection, Closure $callback): void + { + if ($connection->getDriver()->supports(DriverInterface::FEATURE_DISABLE_CONSTRAINT_WITHOUT_TRANSACTION)) { + $connection->disableConstraints(function (Connection $connection) use ($callback): void { + $callback($connection); + }); + } else { + $connection->transactional(function (Connection $connection) use ($callback): void { + $connection->disableConstraints(function (Connection $connection) use ($callback): void { + $callback($connection); + }); + }); + } + } +} diff --git a/app/vendor/cakephp/cakephp/src/TestSuite/Fixture/FixtureHelper.php b/app/vendor/cakephp/cakephp/src/TestSuite/Fixture/FixtureHelper.php new file mode 100644 index 000000000..ce8210e60 --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/TestSuite/Fixture/FixtureHelper.php @@ -0,0 +1,299 @@ + $fixtureNames Fixture names from test case + * @return array<\Cake\Datasource\FixtureInterface> + */ + public function loadFixtures(array $fixtureNames): array + { + static $cachedFixtures = []; + + $fixtures = []; + foreach ($fixtureNames as $fixtureName) { + if (strpos($fixtureName, '.')) { + [$type, $pathName] = explode('.', $fixtureName, 2); + $path = explode('/', $pathName); + $name = array_pop($path); + $additionalPath = implode('\\', $path); + + if ($type === 'core') { + $baseNamespace = 'Cake'; + } elseif ($type === 'app') { + $baseNamespace = Configure::read('App.namespace'); + } elseif ($type === 'plugin') { + [$plugin, $name] = explode('.', $pathName); + $baseNamespace = str_replace('/', '\\', $plugin); + $additionalPath = null; + } else { + $baseNamespace = ''; + $name = $fixtureName; + } + + if (strpos($name, '/') > 0) { + $name = str_replace('/', '\\', $name); + } + + $nameSegments = [ + $baseNamespace, + 'Test\Fixture', + $additionalPath, + $name . 'Fixture', + ]; + /** @psalm-var class-string<\Cake\Datasource\FixtureInterface> */ + $className = implode('\\', array_filter($nameSegments)); + } else { + /** @psalm-var class-string<\Cake\Datasource\FixtureInterface> */ + $className = $fixtureName; + } + + if (isset($fixtures[$className])) { + throw new UnexpectedValueException("Found duplicate fixture `$fixtureName`."); + } + + if (!class_exists($className)) { + throw new UnexpectedValueException("Could not find fixture `$fixtureName`."); + } + + if (!isset($cachedFixtures[$className])) { + $cachedFixtures[$className] = new $className(); + } + + $fixtures[$className] = $cachedFixtures[$className]; + } + + return $fixtures; + } + + /** + * Runs the callback once per connection. + * + * The callback signature: + * ``` + * function callback(ConnectionInterface $connection, array $fixtures) + * ``` + * + * @param \Closure $callback Callback run per connection + * @param array<\Cake\Datasource\FixtureInterface> $fixtures Test fixtures + * @return void + */ + public function runPerConnection(Closure $callback, array $fixtures): void + { + $groups = []; + foreach ($fixtures as $fixture) { + $groups[$fixture->connection()][] = $fixture; + } + + foreach ($groups as $connectionName => $fixtures) { + $callback(ConnectionManager::get($connectionName), $fixtures); + } + } + + /** + * Inserts fixture data. + * + * @param array<\Cake\Datasource\FixtureInterface> $fixtures Test fixtures + * @return void + * @internal + */ + public function insert(array $fixtures): void + { + $this->runPerConnection(function (ConnectionInterface $connection, array $groupFixtures): void { + if ($connection instanceof Connection) { + $sortedFixtures = $this->sortByConstraint($connection, $groupFixtures); + if ($sortedFixtures) { + $this->insertConnection($connection, $sortedFixtures); + } else { + $helper = new ConnectionHelper(); + $helper->runWithoutConstraints( + $connection, + function (Connection $connection) use ($groupFixtures): void { + $this->insertConnection($connection, $groupFixtures); + } + ); + } + } else { + $this->insertConnection($connection, $groupFixtures); + } + }, $fixtures); + } + + /** + * Inserts all fixtures for a connection and provides friendly errors for bad data. + * + * @param \Cake\Datasource\ConnectionInterface $connection Fixture connection + * @param array<\Cake\Datasource\FixtureInterface> $fixtures Connection fixtures + * @return void + */ + protected function insertConnection(ConnectionInterface $connection, array $fixtures): void + { + foreach ($fixtures as $fixture) { + try { + $fixture->insert($connection); + } catch (PDOException $exception) { + $message = sprintf( + 'Unable to insert rows for table `%s`.' + . " Fixture records might have invalid data or unknown contraints.\n%s", + $fixture->sourceName(), + $exception->getMessage() + ); + throw new CakeException($message); + } + } + } + + /** + * Truncates fixture tables. + * + * @param array<\Cake\Datasource\FixtureInterface> $fixtures Test fixtures + * @return void + * @internal + */ + public function truncate(array $fixtures): void + { + $this->runPerConnection(function (ConnectionInterface $connection, array $groupFixtures): void { + if ($connection instanceof Connection) { + $sortedFixtures = null; + if ($connection->getDriver()->supports(DriverInterface::FEATURE_TRUNCATE_WITH_CONSTRAINTS)) { + $sortedFixtures = $this->sortByConstraint($connection, $groupFixtures); + } + + if ($sortedFixtures !== null) { + $this->truncateConnection($connection, array_reverse($sortedFixtures)); + } else { + $helper = new ConnectionHelper(); + $helper->runWithoutConstraints( + $connection, + function (Connection $connection) use ($groupFixtures): void { + $this->truncateConnection($connection, $groupFixtures); + } + ); + } + } else { + $this->truncateConnection($connection, $groupFixtures); + } + }, $fixtures); + } + + /** + * Truncates all fixtures for a connection and provides friendly errors for bad data. + * + * @param \Cake\Datasource\ConnectionInterface $connection Fixture connection + * @param array<\Cake\Datasource\FixtureInterface> $fixtures Connection fixtures + * @return void + */ + protected function truncateConnection(ConnectionInterface $connection, array $fixtures): void + { + foreach ($fixtures as $fixture) { + try { + $fixture->truncate($connection); + } catch (PDOException $exception) { + $message = sprintf( + 'Unable to truncate table `%s`.' + . " Fixture records might have invalid data or unknown contraints.\n%s", + $fixture->sourceName(), + $exception->getMessage() + ); + throw new CakeException($message); + } + } + } + + /** + * Sort fixtures with foreign constraints last if possible, otherwise returns null. + * + * @param \Cake\Database\Connection $connection Database connection + * @param array<\Cake\Datasource\FixtureInterface> $fixtures Database fixtures + * @return array|null + */ + protected function sortByConstraint(Connection $connection, array $fixtures): ?array + { + $constrained = []; + $unconstrained = []; + foreach ($fixtures as $fixture) { + $references = $this->getForeignReferences($connection, $fixture); + if ($references) { + $constrained[$fixture->sourceName()] = ['references' => $references, 'fixture' => $fixture]; + } else { + $unconstrained[] = $fixture; + } + } + + // Check if any fixtures reference another fixture with constrants + // If they do, then there might be cross-dependencies which we don't support sorting + foreach ($constrained as ['references' => $references]) { + foreach ($references as $reference) { + if (isset($constrained[$reference])) { + return null; + } + } + } + + return array_merge($unconstrained, array_column($constrained, 'fixture')); + } + + /** + * Gets array of foreign references for fixtures table. + * + * @param \Cake\Database\Connection $connection Database connection + * @param \Cake\Datasource\FixtureInterface $fixture Database fixture + * @return array + */ + protected function getForeignReferences(Connection $connection, FixtureInterface $fixture): array + { + static $schemas = []; + + // Get and cache off the schema since TestFixture generates a fake schema based on $fields + $tableName = $fixture->sourceName(); + if (!isset($schemas[$tableName])) { + $schemas[$tableName] = $connection->getSchemaCollection()->describe($tableName); + } + $schema = $schemas[$tableName]; + + $references = []; + foreach ($schema->constraints() as $constraintName) { + $constraint = $schema->getConstraint($constraintName); + + if ($constraint && $constraint['type'] === TableSchema::CONSTRAINT_FOREIGN) { + $references[] = $constraint['references'][0]; + } + } + + return $references; + } +} diff --git a/app/vendor/cakephp/cakephp/src/TestSuite/Fixture/FixtureStrategyInterface.php b/app/vendor/cakephp/cakephp/src/TestSuite/Fixture/FixtureStrategyInterface.php new file mode 100644 index 000000000..09706c395 --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/TestSuite/Fixture/FixtureStrategyInterface.php @@ -0,0 +1,38 @@ + $fixtureNames Name of fixtures used by test. + * @return void + */ + public function setupTest(array $fixtureNames): void; + + /** + * Called after each test run in each TestCase. + * + * @return void + */ + public function teardownTest(): void; +} diff --git a/app/vendor/cakephp/cakephp/src/TestSuite/Fixture/PHPUnitExtension.php b/app/vendor/cakephp/cakephp/src/TestSuite/Fixture/PHPUnitExtension.php new file mode 100644 index 000000000..00eb4887f --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/TestSuite/Fixture/PHPUnitExtension.php @@ -0,0 +1,49 @@ +addTestAliases(); + + $enableLogging = in_array('--debug', $_SERVER['argv'] ?? [], true); + if ($enableLogging) { + $helper->enableQueryLogging(); + Log::drop('queries'); + Log::setConfig('queries', [ + 'className' => 'Console', + 'stream' => 'php://stderr', + 'scopes' => ['queriesLog'], + ]); + } + } +} diff --git a/app/vendor/cakephp/cakephp/src/TestSuite/Fixture/SchemaLoader.php b/app/vendor/cakephp/cakephp/src/TestSuite/Fixture/SchemaLoader.php new file mode 100644 index 000000000..3a97b1bd6 --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/TestSuite/Fixture/SchemaLoader.php @@ -0,0 +1,135 @@ +helper = new ConnectionHelper(); + } + + /** + * Load and apply schema sql file, or an array of files. + * + * @param array|string $paths Schema files to load + * @param string $connectionName Connection name + * @param bool $dropTables Drop all tables prior to loading schema files + * @param bool $truncateTables Truncate all tables after loading schema files + * @return void + */ + public function loadSqlFiles( + $paths, + string $connectionName = 'test', + bool $dropTables = true, + bool $truncateTables = false + ): void { + $files = (array)$paths; + + // Don't create schema if we are in a phpunit separate process test method. + if (isset($GLOBALS['__PHPUNIT_BOOTSTRAP'])) { + return; + } + + if ($dropTables) { + $this->helper->dropTables($connectionName); + } + + /** @var \Cake\Database\Connection $connection */ + $connection = ConnectionManager::get($connectionName); + foreach ($files as $file) { + if (!file_exists($file)) { + throw new InvalidArgumentException("Unable to load SQL file `$file`."); + } + $sql = file_get_contents($file); + + // Use the underlying PDO connection so we can avoid prepared statements + // which don't support multiple queries in postgres. + $driver = $connection->getDriver(); + $driver->getConnection()->exec($sql); + } + + if ($truncateTables) { + $this->helper->truncateTables($connectionName); + } + } + + /** + * Load and apply CakePHP-specific schema file. + * + * @param string $file Schema file + * @param string $connectionName Connection name + * @return void + * @internal + */ + public function loadInternalFile(string $file, string $connectionName = 'test'): void + { + // Don't reload schema when we are in a separate process state. + if (isset($GLOBALS['__PHPUNIT_BOOTSTRAP'])) { + return; + } + + $this->helper->dropTables($connectionName); + + $tables = include $file; + + $connection = ConnectionManager::get($connectionName); + $connection->disableConstraints(function ($connection) use ($tables) { + foreach ($tables as $table) { + $schema = new TableSchema($table['table'], $table['columns']); + if (isset($table['indexes'])) { + foreach ($table['indexes'] as $key => $index) { + $schema->addIndex($key, $index); + } + } + if (isset($table['constraints'])) { + foreach ($table['constraints'] as $key => $index) { + $schema->addConstraint($key, $index); + } + } + + // Generate SQL for each table. + foreach ($schema->createSql($connection) as $sql) { + $connection->execute($sql); + } + } + }); + } +} diff --git a/app/vendor/cakephp/cakephp/src/TestSuite/Fixture/TransactionStrategy.php b/app/vendor/cakephp/cakephp/src/TestSuite/Fixture/TransactionStrategy.php new file mode 100644 index 000000000..88cb75747 --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/TestSuite/Fixture/TransactionStrategy.php @@ -0,0 +1,94 @@ + + */ + protected $fixtures = []; + + /** + * Initialize strategy. + */ + public function __construct() + { + $this->helper = new FixtureHelper(); + } + + /** + * @inheritDoc + */ + public function setupTest(array $fixtureNames): void + { + if (empty($fixtureNames)) { + return; + } + + $this->fixtures = $this->helper->loadFixtures($fixtureNames); + + $this->helper->runPerConnection(function ($connection) { + if ($connection instanceof Connection) { + assert( + $connection->inTransaction() === false, + 'Cannot start transaction strategy inside a transaction. ' . + 'Ensure you have closed all open transactions.' + ); + $connection->enableSavePoints(); + if (!$connection->isSavePointsEnabled()) { + throw new RuntimeException( + "Could not enable save points for the `{$connection->configName()}` connection. " . + 'Your database needs to support savepoints in order to use ' . + 'TransactionStrategy.' + ); + } + + $connection->begin(); + $connection->createSavePoint('__fixtures__'); + } + }, $this->fixtures); + + $this->helper->insert($this->fixtures); + } + + /** + * @inheritDoc + */ + public function teardownTest(): void + { + $this->helper->runPerConnection(function ($connection) { + if ($connection->inTransaction()) { + $connection->rollback(true); + } + }, $this->fixtures); + } +} diff --git a/app/vendor/cakephp/cakephp/src/TestSuite/Fixture/TruncateStrategy.php b/app/vendor/cakephp/cakephp/src/TestSuite/Fixture/TruncateStrategy.php new file mode 100644 index 000000000..218c2ca7e --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/TestSuite/Fixture/TruncateStrategy.php @@ -0,0 +1,62 @@ + + */ + protected $fixtures = []; + + /** + * Initialize strategy. + */ + public function __construct() + { + $this->helper = new FixtureHelper(); + } + + /** + * @inheritDoc + */ + public function setupTest(array $fixtureNames): void + { + if (empty($fixtureNames)) { + return; + } + + $this->fixtures = $this->helper->loadFixtures($fixtureNames); + $this->helper->insert($this->fixtures); + } + + /** + * @inheritDoc + */ + public function teardownTest(): void + { + $this->helper->truncate($this->fixtures); + } +} diff --git a/app/vendor/cakephp/cakephp/src/TestSuite/HttpClientTrait.php b/app/vendor/cakephp/cakephp/src/TestSuite/HttpClientTrait.php new file mode 100644 index 000000000..7f78675a2 --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/TestSuite/HttpClientTrait.php @@ -0,0 +1,117 @@ + $headers A list of headers for the response. Example `Content-Type: application/json` + * @param string $body The body for the response. + * @return \Cake\Http\Client\Response + */ + public function newClientResponse(int $code = 200, array $headers = [], string $body = ''): Response + { + $headers = array_merge(["HTTP/1.1 {$code}"], $headers); + + return new Response($headers, $body); + } + + /** + * Add a mock response for a POST request. + * + * @param string $url The URL to mock + * @param \Cake\Http\Client\Response $response The response for the mock. + * @param array $options Additional options. See Client::addMockResponse() + * @return void + */ + public function mockClientPost(string $url, Response $response, array $options = []): void + { + Client::addMockResponse('POST', $url, $response, $options); + } + + /** + * Add a mock response for a GET request. + * + * @param string $url The URL to mock + * @param \Cake\Http\Client\Response $response The response for the mock. + * @param array $options Additional options. See Client::addMockResponse() + * @return void + */ + public function mockClientGet(string $url, Response $response, array $options = []): void + { + Client::addMockResponse('GET', $url, $response, $options); + } + + /** + * Add a mock response for a PATCH request. + * + * @param string $url The URL to mock + * @param \Cake\Http\Client\Response $response The response for the mock. + * @param array $options Additional options. See Client::addMockResponse() + * @return void + */ + public function mockClientPatch(string $url, Response $response, array $options = []): void + { + Client::addMockResponse('PATCH', $url, $response, $options); + } + + /** + * Add a mock response for a PUT request. + * + * @param string $url The URL to mock + * @param \Cake\Http\Client\Response $response The response for the mock. + * @param array $options Additional options. See Client::addMockResponse() + * @return void + */ + public function mockClientPut(string $url, Response $response, array $options = []): void + { + Client::addMockResponse('PUT', $url, $response, $options); + } + + /** + * Add a mock response for a DELETE request. + * + * @param string $url The URL to mock + * @param \Cake\Http\Client\Response $response The response for the mock. + * @param array $options Additional options. See Client::addMockResponse() + * @return void + */ + public function mockClientDelete(string $url, Response $response, array $options = []): void + { + Client::addMockResponse('DELETE', $url, $response, $options); + } +} diff --git a/app/vendor/cakephp/cakephp/templates/Error/invalid_parameter.php b/app/vendor/cakephp/cakephp/templates/Error/invalid_parameter.php new file mode 100644 index 000000000..eaca47f8c --- /dev/null +++ b/app/vendor/cakephp/cakephp/templates/Error/invalid_parameter.php @@ -0,0 +1,58 @@ +layout = 'dev_error'; + +$this->assign('title', sprintf('Invalid Parameter', h($class))); +$this->assign( + 'subheading', + sprintf('Error The passed parameter or parameter type is invalid in %s::%s()', h($class), h($action)) +); +$this->assign('templateName', 'invalid_parameter.php'); + +$this->start('file'); +?> +

+ Error + +

+ +
+end() ?> diff --git a/app/vendor/cakephp/cakephp/tests/Fixture/ArticlesTagsBindingKeysFixture.php b/app/vendor/cakephp/cakephp/tests/Fixture/ArticlesTagsBindingKeysFixture.php new file mode 100644 index 000000000..893a42187 --- /dev/null +++ b/app/vendor/cakephp/cakephp/tests/Fixture/ArticlesTagsBindingKeysFixture.php @@ -0,0 +1,35 @@ + 1, 'tagname' => 'tag1'], + ['article_id' => 1, 'tagname' => 'tag2'], + ['article_id' => 2, 'tagname' => 'tag1'], + ['article_id' => 2, 'tagname' => 'tag3'], + ]; +} diff --git a/app/vendor/cakephp/cakephp/tests/Fixture/ColumnSchemaAwareTypeValuesFixture.php b/app/vendor/cakephp/cakephp/tests/Fixture/ColumnSchemaAwareTypeValuesFixture.php new file mode 100644 index 000000000..b8da77f47 --- /dev/null +++ b/app/vendor/cakephp/cakephp/tests/Fixture/ColumnSchemaAwareTypeValuesFixture.php @@ -0,0 +1,32 @@ + ['type' => 'integer'], + 'val' => ['type' => 'text', 'null' => false, 'comment' => 'Fixture comment'], + '_constraints' => [ + 'primary' => ['type' => 'primary', 'columns' => ['id']], + ], + ]; + + public function init(): void + { + parent::init(); + + $this->records = [ + [ + 'val' => new ColumnSchemaAwareTypeValueObject('THIS TEXT SHOULD BE PROCESSED VIA A CUSTOM TYPE'), + ], + [ + 'val' => 'THIS TEXT ALSO SHOULD BE PROCESSED VIA A CUSTOM TYPE', + ], + ]; + } +} diff --git a/app/vendor/cakephp/cakephp/tests/Fixture/NullableAuthorsFixture.php b/app/vendor/cakephp/cakephp/tests/Fixture/NullableAuthorsFixture.php new file mode 100644 index 000000000..050cfb631 --- /dev/null +++ b/app/vendor/cakephp/cakephp/tests/Fixture/NullableAuthorsFixture.php @@ -0,0 +1,41 @@ + ['type' => 'integer'], + 'author_id' => ['type' => 'integer', 'null' => true], + '_constraints' => ['primary' => ['type' => 'primary', 'columns' => ['id']]], + ]; + + /** + * records property + * + * @var array + */ + public $records = [ + ['author_id' => 3], + ['author_id' => null], + ]; +} diff --git a/app/vendor/cakephp/cakephp/tests/schema.php b/app/vendor/cakephp/cakephp/tests/schema.php new file mode 100644 index 000000000..18ea93a15 --- /dev/null +++ b/app/vendor/cakephp/cakephp/tests/schema.php @@ -0,0 +1,1557 @@ + 'binary_uuid_items', + 'columns' => [ + 'id' => [ + 'type' => 'binaryuuid', + ], + 'name' => [ + 'type' => 'string', + 'null' => false, + ], + 'published' => [ + 'type' => 'boolean', + 'null' => false, + ], + ], + 'constraints' => [ + 'primary' => [ + 'type' => 'primary', + 'columns' => [ + 'id', + ], + ], + ], + ], + [ + 'table' => 'unique_authors', + 'columns' => [ + 'id' => [ + 'type' => 'integer', + ], + 'first_author_id' => [ + 'type' => 'integer', + 'null' => true, + ], + 'second_author_id' => [ + 'type' => 'integer', + 'null' => false, + ], + ], + 'constraints' => [ + 'primary' => [ + 'type' => 'primary', + 'columns' => [ + 'id', + ], + ], + 'nullable_non_nullable_unique' => [ + 'type' => 'unique', + 'columns' => [ + 'first_author_id', + 'second_author_id', + ], + ], + ], + ], + [ + 'table' => 'articles_more_translations', + 'columns' => [ + 'id' => [ + 'type' => 'integer', + ], + 'locale' => [ + 'type' => 'string', + 'null' => false, + ], + 'title' => [ + 'type' => 'string', + 'null' => false, + ], + 'subtitle' => [ + 'type' => 'string', + 'null' => false, + ], + 'body' => 'text', + ], + 'constraints' => [ + 'primary' => [ + 'type' => 'primary', + 'columns' => [ + 'id', + 'locale', + ], + ], + ], + ], + [ + 'table' => 'users', + 'columns' => [ + 'id' => [ + 'type' => 'integer', + ], + 'username' => [ + 'type' => 'string', + 'null' => true, + ], + 'password' => [ + 'type' => 'string', + 'null' => true, + ], + 'created' => [ + 'type' => 'timestamp', + 'null' => true, + ], + 'updated' => [ + 'type' => 'timestamp', + 'null' => true, + ], + ], + 'constraints' => [ + 'primary' => [ + 'type' => 'primary', + 'columns' => [ + 'id', + ], + ], + ], + ], + [ + 'table' => 'featured_tags', + 'columns' => [ + 'tag_id' => [ + 'type' => 'integer', + 'null' => false, + ], + 'priority' => [ + 'type' => 'integer', + 'null' => false, + ], + ], + 'constraints' => [ + 'primary' => [ + 'type' => 'primary', + 'columns' => [ + 'tag_id', + ], + ], + ], + ], + [ + 'table' => 'column_schema_aware_type_values', + 'columns' => [ + 'id' => [ + 'type' => 'integer', + ], + 'val' => [ + 'type' => 'text', + 'null' => false, + 'comment' => 'Fixture comment', + ], + ], + 'constraints' => [ + 'primary' => [ + 'type' => 'primary', + 'columns' => [ + 'id', + ], + ], + ], + ], + [ + 'table' => 'sections_members', + 'columns' => [ + 'id' => [ + 'type' => 'integer', + ], + 'section_id' => [ + 'type' => 'integer', + ], + 'member_id' => [ + 'type' => 'integer', + ], + ], + 'constraints' => [ + 'primary' => [ + 'type' => 'primary', + 'columns' => [ + 'id', + ], + ], + ], + ], + [ + 'table' => 'site_articles_tags', + 'columns' => [ + 'article_id' => [ + 'type' => 'integer', + 'null' => false, + ], + 'tag_id' => [ + 'type' => 'integer', + 'null' => false, + ], + 'site_id' => [ + 'type' => 'integer', + 'null' => false, + ], + ], + 'constraints' => [ + 'UNIQUE_TAG2' => [ + 'type' => 'primary', + 'columns' => [ + 'article_id', + 'tag_id', + 'site_id', + ], + ], + ], + ], + [ + 'table' => 'authors_translations', + 'columns' => [ + 'id' => [ + 'type' => 'integer', + ], + 'locale' => [ + 'type' => 'string', + 'null' => false, + ], + 'name' => [ + 'type' => 'string', + 'null' => false, + ], + ], + 'constraints' => [ + 'primary' => [ + 'type' => 'primary', + 'columns' => [ + 'id', + 'locale', + ], + ], + ], + ], + [ + 'table' => 'binary_uuid_items_binary_uuid_tags', + 'columns' => [ + 'id' => [ + 'type' => 'integer', + ], + 'binary_uuid_item_id' => [ + 'type' => 'binaryuuid', + 'null' => false, + ], + 'binary_uuid_tag_id' => [ + 'type' => 'binaryuuid', + 'null' => false, + ], + ], + 'constraints' => [ + 'primary' => [ + 'type' => 'primary', + 'columns' => [ + 'id', + ], + ], + 'unique_item_tag' => [ + 'type' => 'unique', + 'columns' => [ + 'binary_uuid_item_id', + 'binary_uuid_tag_id', + ], + ], + ], + ], + [ + 'table' => 'auth_users', + 'columns' => [ + 'id' => [ + 'type' => 'integer', + ], + 'username' => [ + 'type' => 'string', + 'null' => false, + ], + 'password' => [ + 'type' => 'string', + 'null' => false, + ], + 'created' => 'datetime', + 'updated' => 'datetime', + ], + 'constraints' => [ + 'primary' => [ + 'type' => 'primary', + 'columns' => [ + 'id', + ], + ], + ], + ], + [ + 'table' => 'counter_cache_categories', + 'columns' => [ + 'id' => [ + 'type' => 'integer', + ], + 'name' => [ + 'type' => 'string', + 'length' => 255, + 'null' => false, + ], + 'post_count' => [ + 'type' => 'integer', + 'null' => true, + ], + ], + 'constraints' => [ + 'primary' => [ + 'type' => 'primary', + 'columns' => [ + 'id', + ], + ], + ], + ], + [ + 'table' => 'date_keys', + 'columns' => [ + 'id' => [ + 'type' => 'date', + ], + 'title' => [ + 'type' => 'string', + 'null' => true, + ], + ], + 'constraints' => [ + 'primary' => [ + 'type' => 'primary', + 'columns' => [ + 'id', + ], + ], + ], + ], + [ + 'table' => 'counter_cache_posts', + 'columns' => [ + 'id' => [ + 'type' => 'integer', + ], + 'title' => [ + 'type' => 'string', + 'length' => 255, + ], + 'user_id' => [ + 'type' => 'integer', + 'null' => true, + ], + 'category_id' => [ + 'type' => 'integer', + 'null' => true, + ], + 'published' => [ + 'type' => 'boolean', + 'null' => false, + 'default' => false, + ], + ], + 'constraints' => [ + 'primary' => [ + 'type' => 'primary', + 'columns' => [ + 'id', + ], + ], + ], + ], + [ + 'table' => 'test_plugin_comments', + 'columns' => [ + 'id' => [ + 'type' => 'integer', + ], + 'article_id' => [ + 'type' => 'integer', + 'null' => false, + ], + 'user_id' => [ + 'type' => 'integer', + 'null' => false, + ], + 'comment' => 'text', + 'published' => [ + 'type' => 'string', + 'length' => 1, + 'default' => 'N', + ], + 'created' => 'datetime', + 'updated' => 'datetime', + ], + 'constraints' => [ + 'primary' => [ + 'type' => 'primary', + 'columns' => [ + 'id', + ], + ], + ], + ], + [ + 'table' => 'members', + 'columns' => [ + 'id' => [ + 'type' => 'integer', + ], + 'section_count' => [ + 'type' => 'integer', + ], + ], + 'constraints' => [ + 'primary' => [ + 'type' => 'primary', + 'columns' => [ + 'id', + ], + ], + ], + ], + [ + 'table' => 'uuid_items', + 'columns' => [ + 'id' => [ + 'type' => 'uuid', + ], + 'published' => [ + 'type' => 'boolean', + 'null' => false, + ], + 'name' => [ + 'type' => 'string', + 'null' => false, + ], + ], + 'constraints' => [ + 'primary' => [ + 'type' => 'primary', + 'columns' => [ + 'id', + ], + ], + ], + ], + [ + 'table' => 'special_tags_translations', + 'columns' => [ + 'id' => [ + 'type' => 'integer', + ], + 'locale' => [ + 'type' => 'string', + 'null' => false, + ], + 'extra_info' => [ + 'type' => 'string', + ], + ], + 'constraints' => [ + 'primary' => [ + 'type' => 'primary', + 'columns' => [ + 'id', + 'locale', + ], + ], + ], + ], + [ + 'table' => 'articles', + 'columns' => [ + 'id' => [ + 'type' => 'integer', + ], + 'author_id' => [ + 'type' => 'integer', + 'null' => true, + ], + 'title' => [ + 'type' => 'string', + 'null' => true, + ], + 'body' => 'text', + 'published' => [ + 'type' => 'string', + 'length' => 1, + 'default' => 'N', + ], + ], + 'constraints' => [ + 'primary' => [ + 'type' => 'primary', + 'columns' => [ + 'id', + ], + ], + ], + ], + [ + 'table' => 'articles_translations', + 'columns' => [ + 'id' => [ + 'type' => 'integer', + ], + 'locale' => [ + 'type' => 'string', + 'null' => false, + ], + 'title' => [ + 'type' => 'string', + 'null' => true, + ], + 'body' => 'text', + ], + 'constraints' => [ + 'primary' => [ + 'type' => 'primary', + 'columns' => [ + 'id', + 'locale', + ], + ], + ], + ], + [ + 'table' => 'products', + 'columns' => [ + 'id' => [ + 'type' => 'integer', + ], + 'category' => [ + 'type' => 'integer', + 'null' => false, + ], + 'name' => [ + 'type' => 'string', + 'null' => false, + ], + 'price' => [ + 'type' => 'integer', + ], + ], + 'constraints' => [ + 'primary' => [ + 'type' => 'primary', + 'columns' => [ + 'category', + 'id', + ], + ], + ], + ], + [ + 'table' => 'orders', + 'columns' => [ + 'id' => [ + 'type' => 'integer', + ], + 'product_category' => [ + 'type' => 'integer', + 'null' => false, + ], + 'product_id' => [ + 'type' => 'integer', + 'null' => false, + ], + ], + 'constraints' => [ + 'primary' => [ + 'type' => 'primary', + 'columns' => [ + 'id', + ], + ], + 'product_category_fk' => [ + 'type' => 'foreign', + 'columns' => [ + 'product_category', + 'product_id', + ], + 'references' => [ + 'products', + [ + 'category', + 'id', + ], + ], + 'update' => 'cascade', + 'delete' => 'cascade', + ], + ], + 'indexes' => [ + 'product_category' => [ + 'type' => 'index', + 'columns' => [ + 'product_category', + 'product_id', + ], + ], + ], + ], + [ + 'table' => 'comments', + 'columns' => [ + 'id' => [ + 'type' => 'integer', + ], + 'article_id' => [ + 'type' => 'integer', + 'null' => false, + ], + 'user_id' => [ + 'type' => 'integer', + 'null' => false, + ], + 'comment' => [ + 'type' => 'text', + ], + 'published' => [ + 'type' => 'string', + 'length' => 1, + 'default' => 'N', + ], + 'created' => [ + 'type' => 'datetime', + ], + 'updated' => [ + 'type' => 'datetime', + ], + ], + 'constraints' => [ + 'primary' => [ + 'type' => 'primary', + 'columns' => [ + 'id', + ], + ], + ], + ], + [ + 'table' => 'datatypes', + 'columns' => [ + 'id' => [ + 'type' => 'biginteger', + ], + 'cost' => [ + 'type' => 'decimal', + 'length' => 20, + 'precision' => 1, + 'null' => true, + ], + 'fraction' => [ + 'type' => 'decimal', + 'length' => 20, + 'precision' => 19, + 'null' => true, + ], + 'floaty' => [ + 'type' => 'float', + 'null' => true, + ], + 'small' => [ + 'type' => 'smallinteger', + 'null' => true, + ], + 'tiny' => [ + 'type' => 'tinyinteger', + 'null' => true, + ], + ], + 'constraints' => [ + 'primary' => [ + 'type' => 'primary', + 'columns' => [ + 'id', + ], + ], + ], + ], + [ + 'table' => 'authors', + 'columns' => [ + 'id' => [ + 'type' => 'integer', + ], + 'name' => [ + 'type' => 'string', + 'default' => null, + ], + ], + 'constraints' => [ + 'primary' => [ + 'type' => 'primary', + 'columns' => [ + 'id', + ], + ], + ], + ], + [ + 'table' => 'counter_cache_comments', + 'columns' => [ + 'id' => [ + 'type' => 'integer', + ], + 'title' => [ + 'type' => 'string', + 'length' => 255, + ], + 'user_id' => [ + 'type' => 'integer', + 'null' => true, + ], + ], + 'constraints' => [ + 'primary' => [ + 'type' => 'primary', + 'columns' => [ + 'id', + ], + ], + ], + ], + [ + 'table' => 'special_tags', + 'columns' => [ + 'id' => [ + 'type' => 'integer', + ], + 'article_id' => [ + 'type' => 'integer', + 'null' => false, + ], + 'tag_id' => [ + 'type' => 'integer', + 'null' => false, + ], + 'highlighted' => [ + 'type' => 'boolean', + 'null' => true, + ], + 'highlighted_time' => [ + 'type' => 'timestamp', + 'null' => true, + ], + 'extra_info' => [ + 'type' => 'string', + ], + 'author_id' => [ + 'type' => 'integer', + 'null' => true, + ], + ], + 'constraints' => [ + 'primary' => [ + 'type' => 'primary', + 'columns' => [ + 'id', + ], + ], + 'UNIQUE_TAG2' => [ + 'type' => 'unique', + 'columns' => [ + 'article_id', + 'tag_id', + ], + ], + ], + ], + [ + 'table' => 'ordered_uuid_items', + 'columns' => [ + 'id' => [ + 'type' => 'string', + 'length' => 32, + ], + 'published' => [ + 'type' => 'boolean', + 'null' => false, + ], + 'name' => [ + 'type' => 'string', + 'null' => false, + ], + ], + 'constraints' => [ + 'primary' => [ + 'type' => 'primary', + 'columns' => [ + 'id', + ], + ], + ], + ], + [ + 'table' => 'counter_cache_users', + 'columns' => [ + 'id' => [ + 'type' => 'integer', + ], + 'name' => [ + 'type' => 'string', + 'length' => 255, + 'null' => false, + ], + 'post_count' => [ + 'type' => 'integer', + 'null' => true, + ], + 'comment_count' => [ + 'type' => 'integer', + 'null' => true, + ], + 'posts_published' => [ + 'type' => 'integer', + 'null' => true, + ], + ], + 'constraints' => [ + 'primary' => [ + 'type' => 'primary', + 'columns' => [ + 'id', + ], + ], + ], + ], + [ + 'table' => 'tags', + 'columns' => [ + 'id' => [ + 'type' => 'integer', + 'null' => false, + ], + 'name' => [ + 'type' => 'string', + 'null' => false, + ], + 'description' => [ + 'type' => 'text', + 'length' => 16777215, + ], + 'created' => [ + 'type' => 'datetime', + 'null' => true, + 'default' => null, + ], + ], + 'constraints' => [ + 'primary' => [ + 'type' => 'primary', + 'columns' => [ + 'id', + ], + ], + ], + ], + [ + 'table' => 'articles_tags', + 'columns' => [ + 'article_id' => [ + 'type' => 'integer', + 'null' => false, + ], + 'tag_id' => [ + 'type' => 'integer', + 'null' => false, + ], + ], + 'constraints' => [ + 'unique_tag' => [ + 'type' => 'primary', + 'columns' => [ + 'article_id', + 'tag_id', + ], + ], + 'tag_id_fk' => [ + 'type' => 'foreign', + 'columns' => [ + 'tag_id', + ], + 'references' => [ + 'tags', + 'id', + ], + 'update' => 'cascade', + 'delete' => 'cascade', + ], + ], + ], + [ + 'table' => 'articles_tags_binding_keys', + 'columns' => [ + 'article_id' => [ + 'type' => 'integer', + 'null' => false, + ], + 'tagname' => [ + 'type' => 'string', + 'null' => false, + ], + ], + 'constraints' => [ + 'unique_tag' => [ + 'type' => 'primary', + 'columns' => [ + 'article_id', + 'tagname', + ], + ], + ], + ], + [ + 'table' => 'profiles', + 'columns' => [ + 'id' => [ + 'type' => 'integer', + 'null' => false, + 'autoIncrement' => true, + ], + 'user_id' => [ + 'type' => 'integer', + 'null' => false, + ], + 'first_name' => [ + 'type' => 'string', + 'null' => true, + ], + 'last_name' => [ + 'type' => 'string', + 'null' => true, + ], + 'is_active' => [ + 'type' => 'boolean', + 'null' => false, + 'default' => true, + ], + ], + 'constraints' => [ + 'primary' => [ + 'type' => 'primary', + 'columns' => [ + 'id', + ], + ], + ], + ], + [ + 'table' => 'sessions', + 'columns' => [ + 'id' => [ + 'type' => 'string', + 'length' => 128, + ], + 'data' => [ + 'type' => 'binary', + 'length' => 16777215, + 'null' => true, + ], + 'expires' => [ + 'type' => 'integer', + 'length' => 11, + 'null' => true, + ], + ], + 'constraints' => [ + 'primary' => [ + 'type' => 'primary', + 'columns' => [ + 'id', + ], + ], + ], + ], + [ + 'table' => 'comments_translations', + 'columns' => [ + 'id' => [ + 'type' => 'integer', + ], + 'locale' => [ + 'type' => 'string', + 'null' => false, + ], + 'comment' => 'text', + ], + 'constraints' => [ + 'primary' => [ + 'type' => 'primary', + 'columns' => [ + 'id', + 'locale', + ], + ], + ], + ], + [ + 'table' => 'menu_link_trees', + 'columns' => [ + 'id' => [ + 'type' => 'integer', + ], + 'menu' => [ + 'type' => 'string', + 'null' => false, + ], + 'lft' => [ + 'type' => 'integer', + ], + 'rght' => [ + 'type' => 'integer', + ], + 'parent_id' => 'integer', + 'url' => [ + 'type' => 'string', + 'null' => false, + ], + 'title' => [ + 'type' => 'string', + 'null' => false, + ], + ], + 'constraints' => [ + 'primary' => [ + 'type' => 'primary', + 'columns' => [ + 'id', + ], + ], + ], + ], + [ + 'table' => 'polymorphic_tagged', + 'columns' => [ + 'id' => [ + 'type' => 'integer', + ], + 'tag_id' => [ + 'type' => 'integer', + ], + 'foreign_key' => [ + 'type' => 'integer', + ], + 'foreign_model' => [ + 'type' => 'string', + ], + 'position' => [ + 'type' => 'integer', + 'null' => true, + ], + ], + 'constraints' => [ + 'primary' => [ + 'type' => 'primary', + 'columns' => [ + 'id', + ], + ], + ], + ], + [ + 'table' => 'things', + 'columns' => [ + 'id' => [ + 'type' => 'integer', + ], + 'title' => [ + 'type' => 'string', + 'length' => 20, + ], + 'body' => [ + 'type' => 'string', + 'length' => 50, + ], + ], + ], + [ + 'table' => 'site_articles', + 'columns' => [ + 'id' => [ + 'type' => 'integer', + ], + 'author_id' => [ + 'type' => 'integer', + 'null' => true, + ], + 'site_id' => [ + 'type' => 'integer', + 'null' => false, + ], + 'title' => [ + 'type' => 'string', + 'null' => true, + ], + 'body' => 'text', + ], + 'constraints' => [ + 'primary' => [ + 'type' => 'primary', + 'columns' => [ + 'id', + 'site_id', + ], + ], + ], + ], + [ + 'table' => 'sections_translations', + 'columns' => [ + 'id' => [ + 'type' => 'integer', + ], + 'locale' => [ + 'type' => 'string', + 'null' => false, + ], + 'title' => [ + 'type' => 'string', + ], + ], + 'constraints' => [ + 'primary' => [ + 'type' => 'primary', + 'columns' => [ + 'id', + 'locale', + ], + ], + ], + ], + [ + 'table' => 'authors_tags', + 'columns' => [ + 'author_id' => [ + 'type' => 'integer', + 'null' => false, + ], + 'tag_id' => [ + 'type' => 'integer', + 'null' => false, + ], + ], + 'constraints' => [ + 'unique_tag' => [ + 'type' => 'primary', + 'columns' => [ + 'author_id', + 'tag_id', + ], + ], + 'author_id_fk' => [ + 'type' => 'foreign', + 'columns' => ['author_id'], + 'references' => ['authors', 'id'], + 'update' => 'cascade', + 'delete' => 'cascade', + ], + ], + ], + [ + 'table' => 'site_authors', + 'columns' => [ + 'id' => [ + 'type' => 'integer', + ], + 'name' => [ + 'type' => 'string', + 'default' => null, + ], + 'site_id' => [ + 'type' => 'integer', + 'null' => false, + ], + ], + 'constraints' => [ + 'primary' => [ + 'type' => 'primary', + 'columns' => [ + 'id', + 'site_id', + ], + ], + ], + ], + [ + 'table' => 'i18n', + 'columns' => [ + 'id' => [ + 'type' => 'integer', + ], + 'locale' => [ + 'type' => 'string', + 'length' => 6, + 'null' => false, + ], + 'model' => [ + 'type' => 'string', + 'null' => false, + ], + 'foreign_key' => [ + 'type' => 'integer', + 'null' => false, + ], + 'field' => [ + 'type' => 'string', + 'null' => false, + ], + 'content' => [ + 'type' => 'text', + ], + ], + 'constraints' => [ + 'primary' => [ + 'type' => 'primary', + 'columns' => [ + 'id', + ], + ], + ], + ], + [ + 'table' => 'number_trees', + 'columns' => [ + 'id' => [ + 'type' => 'integer', + ], + 'name' => [ + 'type' => 'string', + 'null' => false, + ], + 'parent_id' => 'integer', + 'lft' => [ + 'type' => 'integer', + ], + 'rght' => [ + 'type' => 'integer', + ], + 'depth' => [ + 'type' => 'integer', + ], + ], + 'constraints' => [ + 'primary' => [ + 'type' => 'primary', + 'columns' => [ + 'id', + ], + ], + ], + ], + [ + 'table' => 'composite_increments', + 'columns' => [ + 'id' => [ + 'type' => 'integer', + 'null' => false, + 'autoIncrement' => true, + ], + 'account_id' => [ + 'type' => 'integer', + 'null' => false, + ], + 'name' => [ + 'type' => 'string', + 'default' => null, + ], + ], + 'constraints' => [ + 'primary' => [ + 'type' => 'primary', + 'columns' => [ + 'id', + 'account_id', + ], + ], + ], + ], + [ + 'table' => 'tags_shadow_translations', + 'columns' => [ + 'id' => [ + 'type' => 'integer', + ], + 'locale' => [ + 'type' => 'string', + 'null' => false, + ], + 'name' => [ + 'type' => 'string', + 'null' => false, + ], + ], + 'constraints' => [ + 'primary' => [ + 'type' => 'primary', + 'columns' => [ + 'id', + 'locale', + ], + ], + ], + ], + [ + 'table' => 'posts', + 'columns' => [ + 'id' => [ + 'type' => 'integer', + ], + 'author_id' => [ + 'type' => 'integer', + 'null' => false, + ], + 'title' => [ + 'type' => 'string', + 'null' => false, + ], + 'body' => 'text', + 'published' => [ + 'type' => 'string', + 'length' => 1, + 'default' => 'N', + ], + ], + 'constraints' => [ + 'primary' => [ + 'type' => 'primary', + 'columns' => [ + 'id', + ], + ], + ], + ], + [ + 'table' => 'binary_uuid_tags', + 'columns' => [ + 'id' => [ + 'type' => 'binaryuuid', + ], + 'name' => [ + 'type' => 'string', + 'null' => false, + ], + ], + 'constraints' => [ + 'primary' => [ + 'type' => 'primary', + 'columns' => [ + 'id', + ], + ], + ], + ], + [ + 'table' => 'tags_translations', + 'columns' => [ + 'id' => [ + 'type' => 'integer', + 'null' => false, + 'autoIncrement' => true, + ], + 'locale' => [ + 'type' => 'string', + 'null' => false, + ], + 'name' => [ + 'type' => 'string', + 'null' => false, + ], + ], + 'constraints' => [ + 'primary' => [ + 'type' => 'primary', + 'columns' => [ + 'id', + ], + ], + ], + ], + [ + 'table' => 'cake_sessions', + 'columns' => [ + 'id' => [ + 'type' => 'string', + 'length' => 128, + ], + 'data' => [ + 'type' => 'text', + 'null' => true, + ], + 'expires' => [ + 'type' => 'integer', + 'length' => 11, + 'null' => true, + ], + ], + 'constraints' => [ + 'primary' => [ + 'type' => 'primary', + 'columns' => [ + 'id', + ], + ], + ], + ], + [ + 'table' => 'attachments', + 'columns' => [ + 'id' => [ + 'type' => 'integer', + ], + 'comment_id' => [ + 'type' => 'integer', + 'null' => false, + ], + 'attachment' => [ + 'type' => 'string', + 'null' => false, + ], + 'created' => 'datetime', + 'updated' => 'datetime', + ], + 'constraints' => [ + 'primary' => [ + 'type' => 'primary', + 'columns' => [ + 'id', + ], + ], + ], + ], + [ + 'table' => 'categories', + 'columns' => [ + 'id' => [ + 'type' => 'integer', + ], + 'parent_id' => [ + 'type' => 'integer', + 'null' => false, + ], + 'name' => [ + 'type' => 'string', + 'null' => false, + ], + 'created' => 'datetime', + 'updated' => 'datetime', + ], + 'constraints' => [ + 'primary' => [ + 'type' => 'primary', + 'columns' => [ + 'id', + ], + ], + ], + ], + [ + 'table' => 'sections', + 'columns' => [ + 'id' => [ + 'type' => 'integer', + ], + 'title' => [ + 'type' => 'string', + ], + ], + 'constraints' => [ + 'primary' => [ + 'type' => 'primary', + 'columns' => [ + 'id', + ], + ], + ], + ], + [ + 'table' => 'counter_cache_user_category_posts', + 'columns' => [ + 'id' => [ + 'type' => 'integer', + ], + 'category_id' => [ + 'type' => 'integer', + ], + 'user_id' => [ + 'type' => 'integer', + ], + 'post_count' => [ + 'type' => 'integer', + 'null' => true, + ], + ], + 'constraints' => [ + 'primary' => [ + 'type' => 'primary', + 'columns' => [ + 'id', + ], + ], + ], + ], + [ + 'table' => 'site_tags', + 'columns' => [ + 'id' => [ + 'type' => 'integer', + ], + 'site_id' => [ + 'type' => 'integer', + ], + 'name' => [ + 'type' => 'string', + 'null' => false, + ], + ], + 'constraints' => [ + 'primary' => [ + 'type' => 'primary', + 'columns' => [ + 'id', + 'site_id', + ], + ], + ], + ], + [ + 'table' => 'nullable_authors', + 'columns' => [ + 'id' => [ + 'type' => 'integer', + ], + 'author_id' => [ + 'type' => 'integer', + 'null' => true, + ], + ], + 'constraints' => [ + 'primary' => [ + 'type' => 'primary', + 'columns' => [ + 'id', + ], + ], + ], + ], +]; diff --git a/app/vendor/cakephp/debug_kit/tests/schema.php b/app/vendor/cakephp/debug_kit/tests/schema.php new file mode 100644 index 000000000..dbe289eef --- /dev/null +++ b/app/vendor/cakephp/debug_kit/tests/schema.php @@ -0,0 +1,61 @@ + 'requests', + 'columns' => [ + 'id' => ['type' => 'uuid', 'null' => false], + 'url' => ['type' => 'text', 'null' => false], + 'content_type' => ['type' => 'string'], + 'status_code' => ['type' => 'integer'], + 'method' => ['type' => 'string'], + 'requested_at' => ['type' => 'datetime', 'null' => false], + ], + 'constraints' => [ + 'primary' => ['type' => 'primary', 'columns' => ['id']], + ], + ], + [ + 'table' => 'panels', + 'columns' => [ + 'id' => ['type' => 'uuid'], + 'request_id' => ['type' => 'uuid', 'null' => false], + 'panel' => ['type' => 'string'], + 'title' => ['type' => 'string'], + 'element' => ['type' => 'string'], + 'summary' => ['type' => 'string'], + 'content' => ['type' => 'binary', 'length' => TableSchema::LENGTH_LONG], + ], + 'constraints' => [ + 'primary' => ['type' => 'primary', 'columns' => ['id']], + 'unique_panel' => ['type' => 'unique', 'columns' => ['request_id', 'panel']], + 'request_id_fk' => [ + 'type' => 'foreign', + 'columns' => ['request_id'], + 'references' => ['requests', 'id'], + ], + ], + ], + [ + 'table' => 'articles', + 'columns' => [ + 'id' => ['type' => 'integer'], + 'author_id' => ['type' => 'integer', 'null' => true], + 'title' => ['type' => 'string', 'null' => true], + 'body' => 'text', + 'published' => ['type' => 'string', 'length' => 1, 'default' => 'N'], + ], + 'constraints' => [ + 'primary' => [ + 'type' => 'primary', + 'columns' => ['id'], + ], + ], + ], +]; diff --git a/app/vendor/cakephp/migrations/src/TestSuite/Migrator.php b/app/vendor/cakephp/migrations/src/TestSuite/Migrator.php new file mode 100644 index 000000000..b9d2f0325 --- /dev/null +++ b/app/vendor/cakephp/migrations/src/TestSuite/Migrator.php @@ -0,0 +1,260 @@ +helper = new ConnectionHelper(); + } + + /** + * Runs one set of migrations. + * This is useful if all your migrations are located in config/Migrations, + * or in a single directory, or in a single plugin. + * + * ## Options + * + * - `skip` A list of `fnmatch` compatible table names that should be ignored. + * + * For additional options {@see \Migrations\Migrations::migrate()}. + * + * @param array $options Migrate options. Connection defaults to `test`. + * @param bool $truncateTables Truncate all tables after running migrations. Defaults to true. + * @return void + */ + public function run( + array $options = [], + bool $truncateTables = true + ): void { + $this->runMany([$options], $truncateTables); + } + + /** + * Runs multiple sets of migrations. + * This is useful if your migrations are located in multiple sources, plugins or connections. + * + * For options, {@see \Migrations\Migrator::run()}. + * + * Example: + * + * $this->runMany([ + * ['connection' => 'some-connection', 'source' => 'some/directory'], + * ['plugin' => 'PluginA'] + * ]); + * + * @param array> $options Array of option arrays. + * @param bool $truncateTables Truncate all tables after running migrations. Defaults to true. + * @return void + */ + public function runMany( + array $options = [], + bool $truncateTables = true + ): void { + // Don't recreate schema if we are in a phpunit separate process test. + if (isset($GLOBALS['__PHPUNIT_BOOTSTRAP'])) { + return; + } + + // Detect all connections involved, and mark those with changed status. + $connectionsToDrop = []; + $connectionsList = []; + foreach ($options as $i => $migrationSet) { + $migrationSet += ['connection' => 'test']; + $skip = $migrationSet['skip'] ?? []; + unset($migrationSet['skip']); + + $options[$i] = $migrationSet; + $connectionName = $migrationSet['connection']; + if (!in_array($connectionName, $connectionsList)) { + $connectionsList[] = ['name' => $connectionName, 'skip' => $skip]; + } + + $migrations = new Migrations(); + if (!in_array($connectionName, $connectionsToDrop) && $this->shouldDropTables($migrations, $migrationSet)) { + $connectionsToDrop[] = ['name' => $connectionName, 'skip' => $skip]; + } + } + + foreach ($connectionsToDrop as $item) { + $this->dropTables($item['name'], $item['skip']); + } + + // Run all sets of migrations + foreach ($options as $migrationSet) { + $migrations = new Migrations(); + + if (!$migrations->migrate($migrationSet)) { + throw new RuntimeException( + "Unable to migrate fixtures for `{$migrationSet['connection']}`." + ); + } + } + + // Truncate all connections if required in parameters + if ($truncateTables) { + foreach ($connectionsList as $item) { + $this->truncate($item['name'], $item['skip']); + } + } + } + + /** + * Truncate tables after calling run([], false) + * + * For options, {@see \Migrations\Migrations::migrate()}. + * + * @param string $connection Connection name to truncate all non-phinx tables + * @param string[] $skip A fnmatch compatible list of table names to skip. + * @return void + */ + public function truncate(string $connection, array $skip = []): void + { + // Don't recreate schema if we are in a phpunit separate process test. + if (isset($GLOBALS['__PHPUNIT_BOOTSTRAP'])) { + return; + } + + $tables = $this->getNonPhinxTables($connection, $skip); + if ($tables) { + $this->helper->truncateTables($connection, $tables); + } + } + + /** + * Detect if migrations have changed and the database needs to be wiped. + * + * @param \Migrations\Migrations $migrations The migrations service. + * @param array $options The connection options. + * @return bool + */ + protected function shouldDropTables(Migrations $migrations, array $options): bool + { + Log::write('debug', "Reading migrations status for {$options['connection']}..."); + + $messages = [ + 'down' => [], + 'missing' => [], + ]; + foreach ($migrations->status($options) as $migration) { + if ($migration['status'] === 'up' && ($migration['missing'] ?? false)) { + $messages['missing'][] = 'Applied but, missing Migration ' . + "source={$migration['name']} id={$migration['id']}"; + } + if ($migration['status'] === 'down') { + $messages['down'][] = "Migration to reverse. source={$migration['name']} id={$migration['id']}"; + } + } + $output = []; + $hasProblems = false; + $itemize = function ($item) { + return '- ' . $item; + }; + if (!empty($messages['down'])) { + $hasProblems = true; + $output[] = 'Migrations needing to be reversed:'; + $output = array_merge($output, array_map($itemize, $messages['down'])); + $output[] = ''; + } + if (!empty($messages['missing'])) { + $hasProblems = true; + $output[] = 'Applied but missing migrations:'; + $output = array_merge($output, array_map($itemize, $messages['down'])); + $output[] = ''; + } + if ($output) { + $output = array_merge( + ['Your migration status some differences with the expected state.', ''], + $output, + ['Going to drop all tables in this source, and re-apply migrations.'] + ); + Log::write('debug', implode("\n", $output)); + } + + return $hasProblems; + } + + /** + * Drops the regular tables of the provided connection + * and truncates the phinx tables. + * + * @param string $connection Connection on which tables are dropped. + * @param string[] $skip A fnmatch compatible list of tables to skip. + * @return void + */ + protected function dropTables(string $connection, array $skip = []): void + { + $dropTables = $this->getNonPhinxTables($connection, $skip); + if (count($dropTables)) { + $this->helper->dropTables($connection, $dropTables); + } + $phinxTables = $this->getPhinxTables($connection); + if (count($phinxTables)) { + $this->helper->truncateTables($connection, $phinxTables); + } + } + + /** + * Get the list of tables that are phinxlog + * + * @param string $connection The connection name to operate on. + * @return string[] The list of tables that are not related to phinx in the provided connection. + */ + protected function getPhinxTables(string $connection): array + { + $tables = ConnectionManager::get($connection)->getSchemaCollection()->listTables(); + + return array_filter($tables, function ($table) { + return strpos($table, 'phinxlog') !== false; + }); + } + + /** + * Get the list of tables that are not phinxlog related. + * + * @param string $connection The connection name to operate on. + * @param string[] $skip A fnmatch compatible list of tables to skip. + * @return string[] The list of tables that are not related to phinx in the provided connection. + */ + protected function getNonPhinxTables(string $connection, array $skip): array + { + $tables = ConnectionManager::get($connection)->getSchemaCollection()->listTables(); + $skip[] = '*phinxlog*'; + + return array_filter($tables, function ($table) use ($skip) { + foreach ($skip as $pattern) { + if (fnmatch($pattern, $table) === true) { + return false; + } + } + + return true; + }); + } +} diff --git a/app/vendor/composer/autoload_classmap.php b/app/vendor/composer/autoload_classmap.php index f592b89e2..2ee20fe07 100644 --- a/app/vendor/composer/autoload_classmap.php +++ b/app/vendor/composer/autoload_classmap.php @@ -409,13 +409,11 @@ 'PharIo\\Version\\AbstractVersionConstraint' => $vendorDir . '/phar-io/version/src/constraints/AbstractVersionConstraint.php', 'PharIo\\Version\\AndVersionConstraintGroup' => $vendorDir . '/phar-io/version/src/constraints/AndVersionConstraintGroup.php', 'PharIo\\Version\\AnyVersionConstraint' => $vendorDir . '/phar-io/version/src/constraints/AnyVersionConstraint.php', - 'PharIo\\Version\\BuildMetaData' => $vendorDir . '/phar-io/version/src/BuildMetaData.php', 'PharIo\\Version\\ExactVersionConstraint' => $vendorDir . '/phar-io/version/src/constraints/ExactVersionConstraint.php', 'PharIo\\Version\\Exception' => $vendorDir . '/phar-io/version/src/exceptions/Exception.php', 'PharIo\\Version\\GreaterThanOrEqualToVersionConstraint' => $vendorDir . '/phar-io/version/src/constraints/GreaterThanOrEqualToVersionConstraint.php', 'PharIo\\Version\\InvalidPreReleaseSuffixException' => $vendorDir . '/phar-io/version/src/exceptions/InvalidPreReleaseSuffixException.php', 'PharIo\\Version\\InvalidVersionException' => $vendorDir . '/phar-io/version/src/exceptions/InvalidVersionException.php', - 'PharIo\\Version\\NoBuildMetaDataException' => $vendorDir . '/phar-io/version/src/exceptions/NoBuildMetaDataException.php', 'PharIo\\Version\\NoPreReleaseSuffixException' => $vendorDir . '/phar-io/version/src/exceptions/NoPreReleaseSuffixException.php', 'PharIo\\Version\\OrVersionConstraintGroup' => $vendorDir . '/phar-io/version/src/constraints/OrVersionConstraintGroup.php', 'PharIo\\Version\\PreReleaseSuffix' => $vendorDir . '/phar-io/version/src/PreReleaseSuffix.php', @@ -427,7 +425,6 @@ 'PharIo\\Version\\VersionConstraintParser' => $vendorDir . '/phar-io/version/src/VersionConstraintParser.php', 'PharIo\\Version\\VersionConstraintValue' => $vendorDir . '/phar-io/version/src/VersionConstraintValue.php', 'PharIo\\Version\\VersionNumber' => $vendorDir . '/phar-io/version/src/VersionNumber.php', - 'PhpToken' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/PhpToken.php', 'ReturnTypeWillChange' => $vendorDir . '/symfony/polyfill-php81/Resources/stubs/ReturnTypeWillChange.php', 'SebastianBergmann\\CliParser\\AmbiguousOptionException' => $vendorDir . '/sebastian/cli-parser/src/exceptions/AmbiguousOptionException.php', 'SebastianBergmann\\CliParser\\Exception' => $vendorDir . '/sebastian/cli-parser/src/exceptions/Exception.php', @@ -459,7 +456,6 @@ 'SebastianBergmann\\CodeCoverage\\NoCodeCoverageDriverWithPathCoverageSupportAvailableException' => $vendorDir . '/phpunit/php-code-coverage/src/Exception/NoCodeCoverageDriverWithPathCoverageSupportAvailableException.php', 'SebastianBergmann\\CodeCoverage\\Node\\AbstractNode' => $vendorDir . '/phpunit/php-code-coverage/src/Node/AbstractNode.php', 'SebastianBergmann\\CodeCoverage\\Node\\Builder' => $vendorDir . '/phpunit/php-code-coverage/src/Node/Builder.php', - 'SebastianBergmann\\CodeCoverage\\Node\\CrapIndex' => $vendorDir . '/phpunit/php-code-coverage/src/Node/CrapIndex.php', 'SebastianBergmann\\CodeCoverage\\Node\\Directory' => $vendorDir . '/phpunit/php-code-coverage/src/Node/Directory.php', 'SebastianBergmann\\CodeCoverage\\Node\\File' => $vendorDir . '/phpunit/php-code-coverage/src/Node/File.php', 'SebastianBergmann\\CodeCoverage\\Node\\Iterator' => $vendorDir . '/phpunit/php-code-coverage/src/Node/Iterator.php', @@ -493,17 +489,12 @@ 'SebastianBergmann\\CodeCoverage\\Report\\Xml\\Unit' => $vendorDir . '/phpunit/php-code-coverage/src/Report/Xml/Unit.php', 'SebastianBergmann\\CodeCoverage\\StaticAnalysisCacheNotConfiguredException' => $vendorDir . '/phpunit/php-code-coverage/src/Exception/StaticAnalysisCacheNotConfiguredException.php', 'SebastianBergmann\\CodeCoverage\\StaticAnalysis\\CacheWarmer' => $vendorDir . '/phpunit/php-code-coverage/src/StaticAnalysis/CacheWarmer.php', - 'SebastianBergmann\\CodeCoverage\\StaticAnalysis\\CachingFileAnalyser' => $vendorDir . '/phpunit/php-code-coverage/src/StaticAnalysis/CachingFileAnalyser.php', 'SebastianBergmann\\CodeCoverage\\StaticAnalysis\\CodeUnitFindingVisitor' => $vendorDir . '/phpunit/php-code-coverage/src/StaticAnalysis/CodeUnitFindingVisitor.php', 'SebastianBergmann\\CodeCoverage\\StaticAnalysis\\ExecutableLinesFindingVisitor' => $vendorDir . '/phpunit/php-code-coverage/src/StaticAnalysis/ExecutableLinesFindingVisitor.php', - 'SebastianBergmann\\CodeCoverage\\StaticAnalysis\\FileAnalyser' => $vendorDir . '/phpunit/php-code-coverage/src/StaticAnalysis/FileAnalyser.php', 'SebastianBergmann\\CodeCoverage\\StaticAnalysis\\IgnoredLinesFindingVisitor' => $vendorDir . '/phpunit/php-code-coverage/src/StaticAnalysis/IgnoredLinesFindingVisitor.php', - 'SebastianBergmann\\CodeCoverage\\StaticAnalysis\\ParsingFileAnalyser' => $vendorDir . '/phpunit/php-code-coverage/src/StaticAnalysis/ParsingFileAnalyser.php', 'SebastianBergmann\\CodeCoverage\\TestIdMissingException' => $vendorDir . '/phpunit/php-code-coverage/src/Exception/TestIdMissingException.php', 'SebastianBergmann\\CodeCoverage\\UnintentionallyCoveredCodeException' => $vendorDir . '/phpunit/php-code-coverage/src/Exception/UnintentionallyCoveredCodeException.php', 'SebastianBergmann\\CodeCoverage\\Util\\DirectoryCouldNotBeCreatedException' => $vendorDir . '/phpunit/php-code-coverage/src/Exception/DirectoryCouldNotBeCreatedException.php', - 'SebastianBergmann\\CodeCoverage\\Util\\Filesystem' => $vendorDir . '/phpunit/php-code-coverage/src/Util/Filesystem.php', - 'SebastianBergmann\\CodeCoverage\\Util\\Percentage' => $vendorDir . '/phpunit/php-code-coverage/src/Util/Percentage.php', 'SebastianBergmann\\CodeCoverage\\Version' => $vendorDir . '/phpunit/php-code-coverage/src/Version.php', 'SebastianBergmann\\CodeCoverage\\XmlException' => $vendorDir . '/phpunit/php-code-coverage/src/Exception/XmlException.php', 'SebastianBergmann\\CodeUnitReverseLookup\\Wizard' => $vendorDir . '/sebastian/code-unit-reverse-lookup/src/Wizard.php', @@ -607,25 +598,10 @@ 'SebastianBergmann\\Timer\\ResourceUsageFormatter' => $vendorDir . '/phpunit/php-timer/src/ResourceUsageFormatter.php', 'SebastianBergmann\\Timer\\TimeSinceStartOfRequestNotAvailableException' => $vendorDir . '/phpunit/php-timer/src/exceptions/TimeSinceStartOfRequestNotAvailableException.php', 'SebastianBergmann\\Timer\\Timer' => $vendorDir . '/phpunit/php-timer/src/Timer.php', - 'SebastianBergmann\\Type\\CallableType' => $vendorDir . '/sebastian/type/src/type/CallableType.php', 'SebastianBergmann\\Type\\Exception' => $vendorDir . '/sebastian/type/src/exception/Exception.php', - 'SebastianBergmann\\Type\\FalseType' => $vendorDir . '/sebastian/type/src/type/FalseType.php', - 'SebastianBergmann\\Type\\GenericObjectType' => $vendorDir . '/sebastian/type/src/type/GenericObjectType.php', - 'SebastianBergmann\\Type\\IntersectionType' => $vendorDir . '/sebastian/type/src/type/IntersectionType.php', - 'SebastianBergmann\\Type\\IterableType' => $vendorDir . '/sebastian/type/src/type/IterableType.php', - 'SebastianBergmann\\Type\\MixedType' => $vendorDir . '/sebastian/type/src/type/MixedType.php', - 'SebastianBergmann\\Type\\NeverType' => $vendorDir . '/sebastian/type/src/type/NeverType.php', - 'SebastianBergmann\\Type\\NullType' => $vendorDir . '/sebastian/type/src/type/NullType.php', - 'SebastianBergmann\\Type\\ObjectType' => $vendorDir . '/sebastian/type/src/type/ObjectType.php', 'SebastianBergmann\\Type\\ReflectionMapper' => $vendorDir . '/sebastian/type/src/ReflectionMapper.php', 'SebastianBergmann\\Type\\RuntimeException' => $vendorDir . '/sebastian/type/src/exception/RuntimeException.php', - 'SebastianBergmann\\Type\\SimpleType' => $vendorDir . '/sebastian/type/src/type/SimpleType.php', - 'SebastianBergmann\\Type\\StaticType' => $vendorDir . '/sebastian/type/src/type/StaticType.php', - 'SebastianBergmann\\Type\\Type' => $vendorDir . '/sebastian/type/src/type/Type.php', 'SebastianBergmann\\Type\\TypeName' => $vendorDir . '/sebastian/type/src/TypeName.php', - 'SebastianBergmann\\Type\\UnionType' => $vendorDir . '/sebastian/type/src/type/UnionType.php', - 'SebastianBergmann\\Type\\UnknownType' => $vendorDir . '/sebastian/type/src/type/UnknownType.php', - 'SebastianBergmann\\Type\\VoidType' => $vendorDir . '/sebastian/type/src/type/VoidType.php', 'SebastianBergmann\\Version' => $vendorDir . '/sebastian/version/src/Version.php', 'SqlFormatter' => $vendorDir . '/jdorn/sql-formatter/lib/SqlFormatter.php', 'Stringable' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/Stringable.php', diff --git a/app/vendor/composer/autoload_psr4.php b/app/vendor/composer/autoload_psr4.php index 7fc2f0c35..0956dccba 100644 --- a/app/vendor/composer/autoload_psr4.php +++ b/app/vendor/composer/autoload_psr4.php @@ -6,7 +6,7 @@ $baseDir = dirname($vendorDir); return array( - 'phpDocumentor\\Reflection\\' => array($vendorDir . '/phpdocumentor/reflection-common/src', $vendorDir . '/phpdocumentor/type-resolver/src', $vendorDir . '/phpdocumentor/reflection-docblock/src'), + 'phpDocumentor\\Reflection\\' => array($vendorDir . '/phpdocumentor/reflection-common/src', $vendorDir . '/phpdocumentor/reflection-docblock/src', $vendorDir . '/phpdocumentor/type-resolver/src'), 'Webmozart\\Assert\\' => array($vendorDir . '/webmozart/assert/src'), 'Twig\\Extra\\Markdown\\' => array($vendorDir . '/twig/markdown-extra'), 'Twig\\' => array($vendorDir . '/twig/twig/src'), diff --git a/app/vendor/composer/autoload_static.php b/app/vendor/composer/autoload_static.php index f3e4ccdf3..ebaf164f2 100644 --- a/app/vendor/composer/autoload_static.php +++ b/app/vendor/composer/autoload_static.php @@ -161,8 +161,8 @@ class ComposerStaticInitb25f76eec921984aa94dcf4015a4846e 'phpDocumentor\\Reflection\\' => array ( 0 => __DIR__ . '/..' . '/phpdocumentor/reflection-common/src', - 1 => __DIR__ . '/..' . '/phpdocumentor/type-resolver/src', - 2 => __DIR__ . '/..' . '/phpdocumentor/reflection-docblock/src', + 1 => __DIR__ . '/..' . '/phpdocumentor/reflection-docblock/src', + 2 => __DIR__ . '/..' . '/phpdocumentor/type-resolver/src', ), 'Webmozart\\Assert\\' => array ( @@ -862,13 +862,11 @@ class ComposerStaticInitb25f76eec921984aa94dcf4015a4846e 'PharIo\\Version\\AbstractVersionConstraint' => __DIR__ . '/..' . '/phar-io/version/src/constraints/AbstractVersionConstraint.php', 'PharIo\\Version\\AndVersionConstraintGroup' => __DIR__ . '/..' . '/phar-io/version/src/constraints/AndVersionConstraintGroup.php', 'PharIo\\Version\\AnyVersionConstraint' => __DIR__ . '/..' . '/phar-io/version/src/constraints/AnyVersionConstraint.php', - 'PharIo\\Version\\BuildMetaData' => __DIR__ . '/..' . '/phar-io/version/src/BuildMetaData.php', 'PharIo\\Version\\ExactVersionConstraint' => __DIR__ . '/..' . '/phar-io/version/src/constraints/ExactVersionConstraint.php', 'PharIo\\Version\\Exception' => __DIR__ . '/..' . '/phar-io/version/src/exceptions/Exception.php', 'PharIo\\Version\\GreaterThanOrEqualToVersionConstraint' => __DIR__ . '/..' . '/phar-io/version/src/constraints/GreaterThanOrEqualToVersionConstraint.php', 'PharIo\\Version\\InvalidPreReleaseSuffixException' => __DIR__ . '/..' . '/phar-io/version/src/exceptions/InvalidPreReleaseSuffixException.php', 'PharIo\\Version\\InvalidVersionException' => __DIR__ . '/..' . '/phar-io/version/src/exceptions/InvalidVersionException.php', - 'PharIo\\Version\\NoBuildMetaDataException' => __DIR__ . '/..' . '/phar-io/version/src/exceptions/NoBuildMetaDataException.php', 'PharIo\\Version\\NoPreReleaseSuffixException' => __DIR__ . '/..' . '/phar-io/version/src/exceptions/NoPreReleaseSuffixException.php', 'PharIo\\Version\\OrVersionConstraintGroup' => __DIR__ . '/..' . '/phar-io/version/src/constraints/OrVersionConstraintGroup.php', 'PharIo\\Version\\PreReleaseSuffix' => __DIR__ . '/..' . '/phar-io/version/src/PreReleaseSuffix.php', @@ -880,7 +878,6 @@ class ComposerStaticInitb25f76eec921984aa94dcf4015a4846e 'PharIo\\Version\\VersionConstraintParser' => __DIR__ . '/..' . '/phar-io/version/src/VersionConstraintParser.php', 'PharIo\\Version\\VersionConstraintValue' => __DIR__ . '/..' . '/phar-io/version/src/VersionConstraintValue.php', 'PharIo\\Version\\VersionNumber' => __DIR__ . '/..' . '/phar-io/version/src/VersionNumber.php', - 'PhpToken' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/PhpToken.php', 'ReturnTypeWillChange' => __DIR__ . '/..' . '/symfony/polyfill-php81/Resources/stubs/ReturnTypeWillChange.php', 'SebastianBergmann\\CliParser\\AmbiguousOptionException' => __DIR__ . '/..' . '/sebastian/cli-parser/src/exceptions/AmbiguousOptionException.php', 'SebastianBergmann\\CliParser\\Exception' => __DIR__ . '/..' . '/sebastian/cli-parser/src/exceptions/Exception.php', @@ -912,7 +909,6 @@ class ComposerStaticInitb25f76eec921984aa94dcf4015a4846e 'SebastianBergmann\\CodeCoverage\\NoCodeCoverageDriverWithPathCoverageSupportAvailableException' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Exception/NoCodeCoverageDriverWithPathCoverageSupportAvailableException.php', 'SebastianBergmann\\CodeCoverage\\Node\\AbstractNode' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Node/AbstractNode.php', 'SebastianBergmann\\CodeCoverage\\Node\\Builder' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Node/Builder.php', - 'SebastianBergmann\\CodeCoverage\\Node\\CrapIndex' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Node/CrapIndex.php', 'SebastianBergmann\\CodeCoverage\\Node\\Directory' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Node/Directory.php', 'SebastianBergmann\\CodeCoverage\\Node\\File' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Node/File.php', 'SebastianBergmann\\CodeCoverage\\Node\\Iterator' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Node/Iterator.php', @@ -946,17 +942,12 @@ class ComposerStaticInitb25f76eec921984aa94dcf4015a4846e 'SebastianBergmann\\CodeCoverage\\Report\\Xml\\Unit' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Report/Xml/Unit.php', 'SebastianBergmann\\CodeCoverage\\StaticAnalysisCacheNotConfiguredException' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Exception/StaticAnalysisCacheNotConfiguredException.php', 'SebastianBergmann\\CodeCoverage\\StaticAnalysis\\CacheWarmer' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/StaticAnalysis/CacheWarmer.php', - 'SebastianBergmann\\CodeCoverage\\StaticAnalysis\\CachingFileAnalyser' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/StaticAnalysis/CachingFileAnalyser.php', 'SebastianBergmann\\CodeCoverage\\StaticAnalysis\\CodeUnitFindingVisitor' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/StaticAnalysis/CodeUnitFindingVisitor.php', 'SebastianBergmann\\CodeCoverage\\StaticAnalysis\\ExecutableLinesFindingVisitor' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/StaticAnalysis/ExecutableLinesFindingVisitor.php', - 'SebastianBergmann\\CodeCoverage\\StaticAnalysis\\FileAnalyser' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/StaticAnalysis/FileAnalyser.php', 'SebastianBergmann\\CodeCoverage\\StaticAnalysis\\IgnoredLinesFindingVisitor' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/StaticAnalysis/IgnoredLinesFindingVisitor.php', - 'SebastianBergmann\\CodeCoverage\\StaticAnalysis\\ParsingFileAnalyser' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/StaticAnalysis/ParsingFileAnalyser.php', 'SebastianBergmann\\CodeCoverage\\TestIdMissingException' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Exception/TestIdMissingException.php', 'SebastianBergmann\\CodeCoverage\\UnintentionallyCoveredCodeException' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Exception/UnintentionallyCoveredCodeException.php', 'SebastianBergmann\\CodeCoverage\\Util\\DirectoryCouldNotBeCreatedException' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Exception/DirectoryCouldNotBeCreatedException.php', - 'SebastianBergmann\\CodeCoverage\\Util\\Filesystem' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Util/Filesystem.php', - 'SebastianBergmann\\CodeCoverage\\Util\\Percentage' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Util/Percentage.php', 'SebastianBergmann\\CodeCoverage\\Version' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Version.php', 'SebastianBergmann\\CodeCoverage\\XmlException' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Exception/XmlException.php', 'SebastianBergmann\\CodeUnitReverseLookup\\Wizard' => __DIR__ . '/..' . '/sebastian/code-unit-reverse-lookup/src/Wizard.php', @@ -1060,25 +1051,10 @@ class ComposerStaticInitb25f76eec921984aa94dcf4015a4846e 'SebastianBergmann\\Timer\\ResourceUsageFormatter' => __DIR__ . '/..' . '/phpunit/php-timer/src/ResourceUsageFormatter.php', 'SebastianBergmann\\Timer\\TimeSinceStartOfRequestNotAvailableException' => __DIR__ . '/..' . '/phpunit/php-timer/src/exceptions/TimeSinceStartOfRequestNotAvailableException.php', 'SebastianBergmann\\Timer\\Timer' => __DIR__ . '/..' . '/phpunit/php-timer/src/Timer.php', - 'SebastianBergmann\\Type\\CallableType' => __DIR__ . '/..' . '/sebastian/type/src/type/CallableType.php', 'SebastianBergmann\\Type\\Exception' => __DIR__ . '/..' . '/sebastian/type/src/exception/Exception.php', - 'SebastianBergmann\\Type\\FalseType' => __DIR__ . '/..' . '/sebastian/type/src/type/FalseType.php', - 'SebastianBergmann\\Type\\GenericObjectType' => __DIR__ . '/..' . '/sebastian/type/src/type/GenericObjectType.php', - 'SebastianBergmann\\Type\\IntersectionType' => __DIR__ . '/..' . '/sebastian/type/src/type/IntersectionType.php', - 'SebastianBergmann\\Type\\IterableType' => __DIR__ . '/..' . '/sebastian/type/src/type/IterableType.php', - 'SebastianBergmann\\Type\\MixedType' => __DIR__ . '/..' . '/sebastian/type/src/type/MixedType.php', - 'SebastianBergmann\\Type\\NeverType' => __DIR__ . '/..' . '/sebastian/type/src/type/NeverType.php', - 'SebastianBergmann\\Type\\NullType' => __DIR__ . '/..' . '/sebastian/type/src/type/NullType.php', - 'SebastianBergmann\\Type\\ObjectType' => __DIR__ . '/..' . '/sebastian/type/src/type/ObjectType.php', 'SebastianBergmann\\Type\\ReflectionMapper' => __DIR__ . '/..' . '/sebastian/type/src/ReflectionMapper.php', 'SebastianBergmann\\Type\\RuntimeException' => __DIR__ . '/..' . '/sebastian/type/src/exception/RuntimeException.php', - 'SebastianBergmann\\Type\\SimpleType' => __DIR__ . '/..' . '/sebastian/type/src/type/SimpleType.php', - 'SebastianBergmann\\Type\\StaticType' => __DIR__ . '/..' . '/sebastian/type/src/type/StaticType.php', - 'SebastianBergmann\\Type\\Type' => __DIR__ . '/..' . '/sebastian/type/src/type/Type.php', 'SebastianBergmann\\Type\\TypeName' => __DIR__ . '/..' . '/sebastian/type/src/TypeName.php', - 'SebastianBergmann\\Type\\UnionType' => __DIR__ . '/..' . '/sebastian/type/src/type/UnionType.php', - 'SebastianBergmann\\Type\\UnknownType' => __DIR__ . '/..' . '/sebastian/type/src/type/UnknownType.php', - 'SebastianBergmann\\Type\\VoidType' => __DIR__ . '/..' . '/sebastian/type/src/type/VoidType.php', 'SebastianBergmann\\Version' => __DIR__ . '/..' . '/sebastian/version/src/Version.php', 'SqlFormatter' => __DIR__ . '/..' . '/jdorn/sql-formatter/lib/SqlFormatter.php', 'Stringable' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/Stringable.php', diff --git a/app/vendor/composer/installed.php b/app/vendor/composer/installed.php index a1c165d2c..ba144fc0d 100644 --- a/app/vendor/composer/installed.php +++ b/app/vendor/composer/installed.php @@ -5,7 +5,7 @@ 'type' => 'project', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), - 'reference' => '93bba0447bb8054b40e23c848b1daf270ef7b68f', + 'reference' => '20a630fdda018e7cfdfe697ae7c25d9ac5d9bb20', 'name' => 'cakephp/app', 'dev' => true, ), @@ -25,7 +25,7 @@ 'type' => 'project', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), - 'reference' => '93bba0447bb8054b40e23c848b1daf270ef7b68f', + 'reference' => '20a630fdda018e7cfdfe697ae7c25d9ac5d9bb20', 'dev_requirement' => false, ), 'cakephp/bake' => array( diff --git a/app/vendor/composer/pcre/LICENSE b/app/vendor/composer/pcre/LICENSE new file mode 100644 index 000000000..c5a282ff4 --- /dev/null +++ b/app/vendor/composer/pcre/LICENSE @@ -0,0 +1,19 @@ +Copyright (C) 2021 Composer + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/app/vendor/composer/pcre/README.md b/app/vendor/composer/pcre/README.md new file mode 100644 index 000000000..6cfdec174 --- /dev/null +++ b/app/vendor/composer/pcre/README.md @@ -0,0 +1,164 @@ +composer/pcre +============= + +PCRE wrapping library that offers type-safe `preg_*` replacements. + +This library gives you a way to ensure `preg_*` functions do not fail silently, returning +unexpected `null`s that may not be handled. + +As of 3.0 this library enforces [`PREG_UNMATCHED_AS_NULL`](#preg_unmatched_as_null) usage +for all matching and replaceCallback functions, [read more below](#preg_unmatched_as_null) +to understand the implications. + +It thus makes it easier to work with static analysis tools like PHPStan or Psalm as it +simplifies and reduces the possible return values from all the `preg_*` functions which +are quite packed with edge cases. + +This library is a thin wrapper around `preg_*` functions with [some limitations](#restrictions--limitations). +If you are looking for a richer API to handle regular expressions have a look at +[rawr/t-regx](https://packagist.org/packages/rawr/t-regx) instead. + +[![Continuous Integration](https://github.com/composer/pcre/workflows/Continuous%20Integration/badge.svg?branch=main)](https://github.com/composer/pcre/actions) + + +Installation +------------ + +Install the latest version with: + +```bash +$ composer require composer/pcre +``` + + +Requirements +------------ + +* PHP 7.4.0 is required for 3.x versions +* PHP 7.2.0 is required for 2.x versions +* PHP 5.3.2 is required for 1.x versions + + +Basic usage +----------- + +Instead of: + +```php +if (preg_match('{fo+}', $string, $matches)) { ... } +if (preg_match('{fo+}', $string, $matches, PREG_OFFSET_CAPTURE)) { ... } +if (preg_match_all('{fo+}', $string, $matches)) { ... } +$newString = preg_replace('{fo+}', 'bar', $string); +$newString = preg_replace_callback('{fo+}', function ($match) { return strtoupper($match[0]); }, $string); +$newString = preg_replace_callback_array(['{fo+}' => fn ($match) => strtoupper($match[0])], $string); +$filtered = preg_grep('{[a-z]}', $elements); +$array = preg_split('{[a-z]+}', $string); +``` + +You can now call these on the `Preg` class: + +```php +use Composer\Pcre\Preg; + +if (Preg::match('{fo+}', $string, $matches)) { ... } +if (Preg::matchWithOffsets('{fo+}', $string, $matches)) { ... } +if (Preg::matchAll('{fo+}', $string, $matches)) { ... } +$newString = Preg::replace('{fo+}', 'bar', $string); +$newString = Preg::replaceCallback('{fo+}', function ($match) { return strtoupper($match[0]); }, $string); +$newString = Preg::replaceCallbackArray(['{fo+}' => fn ($match) => strtoupper($match[0])], $string); +$filtered = Preg::grep('{[a-z]}', $elements); +$array = Preg::split('{[a-z]+}', $string); +``` + +The main difference is if anything fails to match/replace/.., it will throw a `Composer\Pcre\PcreException` +instead of returning `null` (or false in some cases), so you can now use the return values safely relying on +the fact that they can only be strings (for replace), ints (for match) or arrays (for grep/split). + +Additionally the `Preg` class provides match methods that return `bool` rather than `int`, for stricter type safety +when the number of pattern matches is not useful: + +```php +use Composer\Pcre\Preg; + +if (Preg::isMatch('{fo+}', $string, $matches)) // bool +if (Preg::isMatchAll('{fo+}', $string, $matches)) // bool +``` + +If you would prefer a slightly more verbose usage, replacing by-ref arguments by result objects, you can use the `Regex` class: + +```php +use Composer\Pcre\Regex; + +// this is useful when you are just interested in knowing if something matched +// as it returns a bool instead of int(1/0) for match +$bool = Regex::isMatch('{fo+}', $string); + +$result = Regex::match('{fo+}', $string); +if ($result->matched) { something($result->matches); } + +$result = Regex::matchWithOffsets('{fo+}', $string); +if ($result->matched) { something($result->matches); } + +$result = Regex::matchAll('{fo+}', $string); +if ($result->matched && $result->count > 3) { something($result->matches); } + +$newString = Regex::replace('{fo+}', 'bar', $string)->result; +$newString = Regex::replaceCallback('{fo+}', function ($match) { return strtoupper($match[0]); }, $string)->result; +$newString = Regex::replaceCallbackArray(['{fo+}' => fn ($match) => strtoupper($match[0])], $string)->result; +``` + +Note that `preg_grep` and `preg_split` are only callable via the `Preg` class as they do not have +complex return types warranting a specific result object. + +See the [MatchResult](src/MatchResult.php), [MatchWithOffsetsResult](src/MatchWithOffsetsResult.php), [MatchAllResult](src/MatchAllResult.php), +[MatchAllWithOffsetsResult](src/MatchAllWithOffsetsResult.php), and [ReplaceResult](src/ReplaceResult.php) class sources for more details. + +Restrictions / Limitations +-------------------------- + +Due to type safety requirements a few restrictions are in place. + +- matching using `PREG_OFFSET_CAPTURE` is made available via `matchWithOffsets` and `matchAllWithOffsets`. + You cannot pass the flag to `match`/`matchAll`. +- `Preg::split` will also reject `PREG_SPLIT_OFFSET_CAPTURE` and you should use `splitWithOffsets` + instead. +- `matchAll` rejects `PREG_SET_ORDER` as it also changes the shape of the returned matches. There + is no alternative provided as you can fairly easily code around it. +- `preg_filter` is not supported as it has a rather crazy API, most likely you should rather + use `Preg::grep` in combination with some loop and `Preg::replace`. +- `replace`, `replaceCallback` and `replaceCallbackArray` do not support an array `$subject`, + only simple strings. +- As of 2.0, the library always uses `PREG_UNMATCHED_AS_NULL` for matching, which offers [much + saner/more predictable results](#preg_unmatched_as_null). As of 3.0 the flag is also set for + `replaceCallback` and `replaceCallbackArray`. + +#### PREG_UNMATCHED_AS_NULL + +As of 2.0, this library always uses PREG_UNMATCHED_AS_NULL for all `match*` and `isMatch*` +functions. As of 3.0 it is also done for `replaceCallback` and `replaceCallbackArray`. + +This means your matches will always contain all matching groups, either as null if unmatched +or as string if it matched. + +The advantages in clarity and predictability are clearer if you compare the two outputs of +running this with and without PREG_UNMATCHED_AS_NULL in $flags: + +```php +preg_match('/(a)(b)*(c)(d)*/', 'ac', $matches, $flags); +``` + +| no flag | PREG_UNMATCHED_AS_NULL | +| --- | --- | +| array (size=4) | array (size=5) | +| 0 => string 'ac' (length=2) | 0 => string 'ac' (length=2) | +| 1 => string 'a' (length=1) | 1 => string 'a' (length=1) | +| 2 => string '' (length=0) | 2 => null | +| 3 => string 'c' (length=1) | 3 => string 'c' (length=1) | +| | 4 => null | +| group 2 (any unmatched group preceding one that matched) is set to `''`. You cannot tell if it matched an empty string or did not match at all | group 2 is `null` when unmatched and a string if it matched, easy to check for | +| group 4 (any optional group without a matching one following) is missing altogether. So you have to check with `isset()`, but really you want `isset($m[4]) && $m[4] !== ''` for safety unless you are very careful to check that a non-optional group follows it | group 4 is always set, and null in this case as there was no match, easy to check for with `$m[4] !== null` | + +License +------- + +composer/pcre is licensed under the MIT License, see the LICENSE file for details. diff --git a/app/vendor/composer/pcre/composer.json b/app/vendor/composer/pcre/composer.json new file mode 100644 index 000000000..40477ff4e --- /dev/null +++ b/app/vendor/composer/pcre/composer.json @@ -0,0 +1,46 @@ +{ + "name": "composer/pcre", + "description": "PCRE wrapping library that offers type-safe preg_* replacements.", + "type": "library", + "license": "MIT", + "keywords": [ + "pcre", + "regex", + "preg", + "regular expression" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } + ], + "require": { + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "symfony/phpunit-bridge": "^5", + "phpstan/phpstan": "^1.3", + "phpstan/phpstan-strict-rules": "^1.1" + }, + "autoload": { + "psr-4": { + "Composer\\Pcre\\": "src" + } + }, + "autoload-dev": { + "psr-4": { + "Composer\\Pcre\\": "tests" + } + }, + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "scripts": { + "test": "vendor/bin/simple-phpunit", + "phpstan": "phpstan analyse" + } +} diff --git a/app/vendor/composer/pcre/phpstan-baseline.neon b/app/vendor/composer/pcre/phpstan-baseline.neon new file mode 100644 index 000000000..df994246b --- /dev/null +++ b/app/vendor/composer/pcre/phpstan-baseline.neon @@ -0,0 +1,17 @@ +parameters: + ignoreErrors: + - + message: "#^Method Composer\\\\Pcre\\\\Preg\\:\\:matchAll\\(\\) should return int\\<0, max\\> but returns int\\.$#" + count: 1 + path: src/Preg.php + + - + message: "#^Method Composer\\\\Pcre\\\\Preg\\:\\:matchAllWithOffsets\\(\\) should return int\\<0, max\\> but returns int\\.$#" + count: 1 + path: src/Preg.php + + - + message: "#^Strict comparison using \\=\\=\\= between int and null will always evaluate to false\\.$#" + count: 2 + path: src/Preg.php + diff --git a/app/vendor/composer/pcre/src/MatchAllResult.php b/app/vendor/composer/pcre/src/MatchAllResult.php new file mode 100644 index 000000000..4c367b036 --- /dev/null +++ b/app/vendor/composer/pcre/src/MatchAllResult.php @@ -0,0 +1,46 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace Composer\Pcre; + +final class MatchAllResult +{ + /** + * An array of match group => list of matched strings + * + * @readonly + * @var array> + */ + public $matches; + + /** + * @readonly + * @var 0|positive-int + */ + public $count; + + /** + * @readonly + * @var bool + */ + public $matched; + + /** + * @param 0|positive-int $count + * @param array> $matches + */ + public function __construct($count, array $matches) + { + $this->matches = $matches; + $this->matched = (bool) $count; + $this->count = $count; + } +} diff --git a/app/vendor/composer/pcre/src/MatchAllWithOffsetsResult.php b/app/vendor/composer/pcre/src/MatchAllWithOffsetsResult.php new file mode 100644 index 000000000..4e893fece --- /dev/null +++ b/app/vendor/composer/pcre/src/MatchAllWithOffsetsResult.php @@ -0,0 +1,48 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace Composer\Pcre; + +final class MatchAllWithOffsetsResult +{ + /** + * An array of match group => list of matches, every match being a pair of string matched + offset in bytes (or -1 if no match) + * + * @readonly + * @var array> + * @phpstan-var array}>> + */ + public $matches; + + /** + * @readonly + * @var 0|positive-int + */ + public $count; + + /** + * @readonly + * @var bool + */ + public $matched; + + /** + * @param 0|positive-int $count + * @param array> $matches + * @phpstan-param array}>> $matches + */ + public function __construct($count, array $matches) + { + $this->matches = $matches; + $this->matched = (bool) $count; + $this->count = $count; + } +} diff --git a/app/vendor/composer/pcre/src/MatchResult.php b/app/vendor/composer/pcre/src/MatchResult.php new file mode 100644 index 000000000..d8e34606a --- /dev/null +++ b/app/vendor/composer/pcre/src/MatchResult.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace Composer\Pcre; + +final class MatchResult +{ + /** + * An array of match group => string matched + * + * @readonly + * @var array + */ + public $matches; + + /** + * @readonly + * @var bool + */ + public $matched; + + /** + * @param 0|positive-int $count + * @param array $matches + */ + public function __construct($count, array $matches) + { + $this->matches = $matches; + $this->matched = (bool) $count; + } +} diff --git a/app/vendor/composer/pcre/src/MatchWithOffsetsResult.php b/app/vendor/composer/pcre/src/MatchWithOffsetsResult.php new file mode 100644 index 000000000..9bd813bbb --- /dev/null +++ b/app/vendor/composer/pcre/src/MatchWithOffsetsResult.php @@ -0,0 +1,41 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace Composer\Pcre; + +final class MatchWithOffsetsResult +{ + /** + * An array of match group => pair of string matched + offset in bytes (or -1 if no match) + * + * @readonly + * @var array + * @phpstan-var array}> + */ + public $matches; + + /** + * @readonly + * @var bool + */ + public $matched; + + /** + * @param 0|positive-int $count + * @param array $matches + * @phpstan-param array}> $matches + */ + public function __construct($count, array $matches) + { + $this->matches = $matches; + $this->matched = (bool) $count; + } +} diff --git a/app/vendor/composer/pcre/src/PcreException.php b/app/vendor/composer/pcre/src/PcreException.php new file mode 100644 index 000000000..218b2f2d8 --- /dev/null +++ b/app/vendor/composer/pcre/src/PcreException.php @@ -0,0 +1,60 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace Composer\Pcre; + +class PcreException extends \RuntimeException +{ + /** + * @param string $function + * @param string|string[] $pattern + * @return self + */ + public static function fromFunction($function, $pattern) + { + $code = preg_last_error(); + + if (is_array($pattern)) { + $pattern = implode(', ', $pattern); + } + + return new PcreException($function.'(): failed executing "'.$pattern.'": '.self::pcreLastErrorMessage($code), $code); + } + + /** + * @param int $code + * @return string + */ + private static function pcreLastErrorMessage($code) + { + if (function_exists('preg_last_error_msg')) { + return preg_last_error_msg(); + } + + // older php versions did not set the code properly in all cases + if (PHP_VERSION_ID < 70201 && $code === 0) { + return 'UNDEFINED_ERROR'; + } + + $constants = get_defined_constants(true); + if (!isset($constants['pcre'])) { + return 'UNDEFINED_ERROR'; + } + + foreach ($constants['pcre'] as $const => $val) { + if ($val === $code && substr($const, -6) === '_ERROR') { + return $const; + } + } + + return 'UNDEFINED_ERROR'; + } +} diff --git a/app/vendor/composer/pcre/src/Preg.php b/app/vendor/composer/pcre/src/Preg.php new file mode 100644 index 000000000..af215779c --- /dev/null +++ b/app/vendor/composer/pcre/src/Preg.php @@ -0,0 +1,275 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace Composer\Pcre; + +class Preg +{ + /** @internal */ + public const ARRAY_MSG = '$subject as an array is not supported. You can use \'foreach\' instead.'; + /** @internal */ + public const INVALID_TYPE_MSG = '$subject must be a string, %s given.'; + + /** + * @param non-empty-string $pattern + * @param array $matches Set by method + * @param int $flags PREG_UNMATCHED_AS_NULL is always set, no other flags are supported + * @return 0|1 + */ + public static function match(string $pattern, string $subject, ?array &$matches = null, int $flags = 0, int $offset = 0): int + { + if (($flags & PREG_OFFSET_CAPTURE) !== 0) { + throw new \InvalidArgumentException('PREG_OFFSET_CAPTURE is not supported as it changes the type of $matches, use matchWithOffsets() instead'); + } + + $result = preg_match($pattern, $subject, $matches, $flags | PREG_UNMATCHED_AS_NULL, $offset); + if ($result === false) { + throw PcreException::fromFunction('preg_match', $pattern); + } + + return $result; + } + + /** + * Runs preg_match with PREG_OFFSET_CAPTURE + * + * @param non-empty-string $pattern + * @param array $matches Set by method + * @param int $flags PREG_UNMATCHED_AS_NULL and PREG_MATCH_OFFSET are always set, no other flags are supported + * @return 0|1 + * + * @phpstan-param array}> $matches + */ + public static function matchWithOffsets(string $pattern, string $subject, ?array &$matches, int $flags = 0, int $offset = 0): int + { + $result = preg_match($pattern, $subject, $matches, $flags | PREG_UNMATCHED_AS_NULL | PREG_OFFSET_CAPTURE, $offset); + if ($result === false) { + throw PcreException::fromFunction('preg_match', $pattern); + } + + return $result; + } + + /** + * @param non-empty-string $pattern + * @param array> $matches Set by method + * @param int $flags PREG_UNMATCHED_AS_NULL is always set, no other flags are supported + * @return 0|positive-int + */ + public static function matchAll(string $pattern, string $subject, ?array &$matches = null, int $flags = 0, int $offset = 0): int + { + if (($flags & PREG_OFFSET_CAPTURE) !== 0) { + throw new \InvalidArgumentException('PREG_OFFSET_CAPTURE is not supported as it changes the type of $matches, use matchAllWithOffsets() instead'); + } + + if (($flags & PREG_SET_ORDER) !== 0) { + throw new \InvalidArgumentException('PREG_SET_ORDER is not supported as it changes the type of $matches'); + } + + $result = preg_match_all($pattern, $subject, $matches, $flags | PREG_UNMATCHED_AS_NULL, $offset); + if ($result === false || /* PHP < 8 may return null */ $result === null) { + throw PcreException::fromFunction('preg_match_all', $pattern); + } + + return $result; + } + + /** + * Runs preg_match_all with PREG_OFFSET_CAPTURE + * + * @param non-empty-string $pattern + * @param array> $matches Set by method + * @param int $flags PREG_UNMATCHED_AS_NULL and PREG_MATCH_OFFSET are always set, no other flags are supported + * @return 0|positive-int + * + * @phpstan-param array}>> $matches + */ + public static function matchAllWithOffsets(string $pattern, string $subject, ?array &$matches, int $flags = 0, int $offset = 0): int + { + $result = preg_match_all($pattern, $subject, $matches, $flags | PREG_UNMATCHED_AS_NULL | PREG_OFFSET_CAPTURE, $offset); + if ($result === false || /* PHP < 8 may return null */ $result === null) { + throw PcreException::fromFunction('preg_match_all', $pattern); + } + + return $result; + } + + /** + * @param string|string[] $pattern + * @param string|string[] $replacement + * @param string $subject + * @param int $count Set by method + */ + public static function replace($pattern, $replacement, $subject, int $limit = -1, int &$count = null): string + { + if (!is_scalar($subject)) { + if (is_array($subject)) { + throw new \InvalidArgumentException(static::ARRAY_MSG); + } + + throw new \TypeError(sprintf(static::INVALID_TYPE_MSG, gettype($subject))); + } + + $result = preg_replace($pattern, $replacement, $subject, $limit, $count); + if ($result === null) { + throw PcreException::fromFunction('preg_replace', $pattern); + } + + return $result; + } + + /** + * @param string|string[] $pattern + * @param string $subject + * @param int $count Set by method + * @param int $flags PREG_OFFSET_CAPTURE is supported, PREG_UNMATCHED_AS_NULL is always set + */ + public static function replaceCallback($pattern, callable $replacement, $subject, int $limit = -1, int &$count = null, int $flags = 0): string + { + if (!is_scalar($subject)) { + if (is_array($subject)) { + throw new \InvalidArgumentException(static::ARRAY_MSG); + } + + throw new \TypeError(sprintf(static::INVALID_TYPE_MSG, gettype($subject))); + } + + $result = preg_replace_callback($pattern, $replacement, $subject, $limit, $count, $flags | PREG_UNMATCHED_AS_NULL); + if ($result === null) { + throw PcreException::fromFunction('preg_replace_callback', $pattern); + } + + return $result; + } + + /** + * @param array $pattern + * @param string $subject + * @param int $count Set by method + * @param int $flags PREG_OFFSET_CAPTURE is supported, PREG_UNMATCHED_AS_NULL is always set + */ + public static function replaceCallbackArray(array $pattern, $subject, int $limit = -1, int &$count = null, int $flags = 0): string + { + if (!is_scalar($subject)) { + if (is_array($subject)) { + throw new \InvalidArgumentException(static::ARRAY_MSG); + } + + throw new \TypeError(sprintf(static::INVALID_TYPE_MSG, gettype($subject))); + } + + $result = preg_replace_callback_array($pattern, $subject, $limit, $count, $flags | PREG_UNMATCHED_AS_NULL); + if ($result === null) { + $pattern = array_keys($pattern); + throw PcreException::fromFunction('preg_replace_callback_array', $pattern); + } + + return $result; + } + + /** + * @param int $flags PREG_SPLIT_NO_EMPTY or PREG_SPLIT_DELIM_CAPTURE + * @return list + */ + public static function split(string $pattern, string $subject, int $limit = -1, int $flags = 0): array + { + if (($flags & PREG_SPLIT_OFFSET_CAPTURE) !== 0) { + throw new \InvalidArgumentException('PREG_SPLIT_OFFSET_CAPTURE is not supported as it changes the type of $matches, use splitWithOffsets() instead'); + } + + $result = preg_split($pattern, $subject, $limit, $flags); + if ($result === false) { + throw PcreException::fromFunction('preg_split', $pattern); + } + + return $result; + } + + /** + * @param int $flags PREG_SPLIT_NO_EMPTY or PREG_SPLIT_DELIM_CAPTURE, PREG_SPLIT_OFFSET_CAPTURE is always set + * @return list + * @phpstan-return list}> + */ + public static function splitWithOffsets(string $pattern, string $subject, int $limit = -1, int $flags = 0): array + { + $result = preg_split($pattern, $subject, $limit, $flags | PREG_SPLIT_OFFSET_CAPTURE); + if ($result === false) { + throw PcreException::fromFunction('preg_split', $pattern); + } + + return $result; + } + + /** + * @template T of string|\Stringable + * @param string $pattern + * @param array $array + * @param int $flags PREG_GREP_INVERT + * @return array + */ + public static function grep(string $pattern, array $array, int $flags = 0): array + { + $result = preg_grep($pattern, $array, $flags); + if ($result === false) { + throw PcreException::fromFunction('preg_grep', $pattern); + } + + return $result; + } + + /** + * @param non-empty-string $pattern + * @param array $matches Set by method + * @param int $flags PREG_UNMATCHED_AS_NULL is always set, no other flags are supported + */ + public static function isMatch(string $pattern, string $subject, ?array &$matches = null, int $flags = 0, int $offset = 0): bool + { + return (bool) static::match($pattern, $subject, $matches, $flags, $offset); + } + + /** + * @param non-empty-string $pattern + * @param array> $matches Set by method + * @param int $flags PREG_UNMATCHED_AS_NULL is always set, no other flags are supported + */ + public static function isMatchAll(string $pattern, string $subject, ?array &$matches = null, int $flags = 0, int $offset = 0): bool + { + return (bool) static::matchAll($pattern, $subject, $matches, $flags, $offset); + } + + /** + * Runs preg_match with PREG_OFFSET_CAPTURE + * + * @param non-empty-string $pattern + * @param array $matches Set by method + * @param int $flags PREG_UNMATCHED_AS_NULL is always set, no other flags are supported + * + * @phpstan-param array}> $matches + */ + public static function isMatchWithOffsets(string $pattern, string $subject, ?array &$matches, int $flags = 0, int $offset = 0): bool + { + return (bool) static::matchWithOffsets($pattern, $subject, $matches, $flags, $offset); + } + + /** + * Runs preg_match_all with PREG_OFFSET_CAPTURE + * + * @param non-empty-string $pattern + * @param array> $matches Set by method + * @param int $flags PREG_UNMATCHED_AS_NULL is always set, no other flags are supported + * + * @phpstan-param array}>> $matches + */ + public static function isMatchAllWithOffsets(string $pattern, string $subject, ?array &$matches, int $flags = 0, int $offset = 0): bool + { + return (bool) static::matchAllWithOffsets($pattern, $subject, $matches, $flags, $offset); + } +} diff --git a/app/vendor/composer/pcre/src/Regex.php b/app/vendor/composer/pcre/src/Regex.php new file mode 100644 index 000000000..9e8e7b817 --- /dev/null +++ b/app/vendor/composer/pcre/src/Regex.php @@ -0,0 +1,118 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace Composer\Pcre; + +class Regex +{ + /** + * @param non-empty-string $pattern + */ + public static function isMatch(string $pattern, string $subject, int $offset = 0): bool + { + return (bool) Preg::match($pattern, $subject, $matches, 0, $offset); + } + + /** + * @param non-empty-string $pattern + * @param int $flags PREG_UNMATCHED_AS_NULL is always set, no other flags are supported + */ + public static function match(string $pattern, string $subject, int $flags = 0, int $offset = 0): MatchResult + { + if (($flags & PREG_OFFSET_CAPTURE) !== 0) { + throw new \InvalidArgumentException('PREG_OFFSET_CAPTURE is not supported as it changes the return type, use matchWithOffsets() instead'); + } + + $count = Preg::match($pattern, $subject, $matches, $flags, $offset); + + return new MatchResult($count, $matches); + } + + /** + * Runs preg_match with PREG_OFFSET_CAPTURE + * + * @param non-empty-string $pattern + * @param int $flags PREG_UNMATCHED_AS_NULL and PREG_MATCH_OFFSET are always set, no other flags are supported + */ + public static function matchWithOffsets(string $pattern, string $subject, int $flags = 0, int $offset = 0): MatchWithOffsetsResult + { + $count = Preg::matchWithOffsets($pattern, $subject, $matches, $flags, $offset); + + return new MatchWithOffsetsResult($count, $matches); + } + + /** + * @param non-empty-string $pattern + * @param int $flags PREG_UNMATCHED_AS_NULL is always set, no other flags are supported + */ + public static function matchAll(string $pattern, string $subject, int $flags = 0, int $offset = 0): MatchAllResult + { + if (($flags & PREG_OFFSET_CAPTURE) !== 0) { + throw new \InvalidArgumentException('PREG_OFFSET_CAPTURE is not supported as it changes the return type, use matchAllWithOffsets() instead'); + } + + if (($flags & PREG_SET_ORDER) !== 0) { + throw new \InvalidArgumentException('PREG_SET_ORDER is not supported as it changes the return type'); + } + + $count = Preg::matchAll($pattern, $subject, $matches, $flags, $offset); + + return new MatchAllResult($count, $matches); + } + + /** + * Runs preg_match_all with PREG_OFFSET_CAPTURE + * + * @param non-empty-string $pattern + * @param int $flags PREG_UNMATCHED_AS_NULL and PREG_MATCH_OFFSET are always set, no other flags are supported + */ + public static function matchAllWithOffsets(string $pattern, string $subject, int $flags = 0, int $offset = 0): MatchAllWithOffsetsResult + { + $count = Preg::matchAllWithOffsets($pattern, $subject, $matches, $flags, $offset); + + return new MatchAllWithOffsetsResult($count, $matches); + } + /** + * @param string|string[] $pattern + * @param string|string[] $replacement + * @param string $subject + */ + public static function replace($pattern, $replacement, $subject, int $limit = -1): ReplaceResult + { + $result = Preg::replace($pattern, $replacement, $subject, $limit, $count); + + return new ReplaceResult($count, $result); + } + + /** + * @param string|string[] $pattern + * @param string $subject + * @param int $flags PREG_OFFSET_CAPTURE is supported, PREG_UNMATCHED_AS_NULL is always set + */ + public static function replaceCallback($pattern, callable $replacement, $subject, int $limit = -1, int $flags = 0): ReplaceResult + { + $result = Preg::replaceCallback($pattern, $replacement, $subject, $limit, $count, $flags); + + return new ReplaceResult($count, $result); + } + + /** + * @param array $pattern + * @param string $subject + * @param int $flags PREG_OFFSET_CAPTURE is supported, PREG_UNMATCHED_AS_NULL is always set + */ + public static function replaceCallbackArray(array $pattern, $subject, int $limit = -1, int $flags = 0): ReplaceResult + { + $result = Preg::replaceCallbackArray($pattern, $subject, $limit, $count, $flags); + + return new ReplaceResult($count, $result); + } +} diff --git a/app/vendor/composer/pcre/src/ReplaceResult.php b/app/vendor/composer/pcre/src/ReplaceResult.php new file mode 100644 index 000000000..0ac08401d --- /dev/null +++ b/app/vendor/composer/pcre/src/ReplaceResult.php @@ -0,0 +1,44 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace Composer\Pcre; + +final class ReplaceResult +{ + /** + * @readonly + * @var string + */ + public $result; + + /** + * @readonly + * @var 0|positive-int + */ + public $count; + + /** + * @readonly + * @var bool + */ + public $matched; + + /** + * @param 0|positive-int $count + * @param string $result + */ + public function __construct($count, $result) + { + $this->count = $count; + $this->matched = (bool) $count; + $this->result = $result; + } +} diff --git a/app/vendor/league/container/src/Argument/ArgumentInterface.php b/app/vendor/league/container/src/Argument/ArgumentInterface.php new file mode 100644 index 000000000..5763c26fa --- /dev/null +++ b/app/vendor/league/container/src/Argument/ArgumentInterface.php @@ -0,0 +1,13 @@ +defaultValue = $defaultValue; + parent::__construct($value); + } + + /** + * @return mixed|null + */ + public function getDefaultValue() + { + return $this->defaultValue; + } +} diff --git a/app/vendor/league/container/src/Argument/DefaultValueInterface.php b/app/vendor/league/container/src/Argument/DefaultValueInterface.php new file mode 100644 index 000000000..18483506f --- /dev/null +++ b/app/vendor/league/container/src/Argument/DefaultValueInterface.php @@ -0,0 +1,13 @@ +value = $value; + } else { + throw new InvalidArgumentException('Incorrect type for value.'); + } + } + + /** + * {@inheritdoc} + */ + public function getValue() + { + return $this->value; + } +} diff --git a/app/vendor/league/container/src/Argument/LiteralArgumentInterface.php b/app/vendor/league/container/src/Argument/LiteralArgumentInterface.php new file mode 100644 index 000000000..2c5a2658b --- /dev/null +++ b/app/vendor/league/container/src/Argument/LiteralArgumentInterface.php @@ -0,0 +1,9 @@ +value = $value; + } + + public function getValue(): string + { + return $this->value; + } +} diff --git a/app/vendor/league/container/src/Argument/ResolvableArgumentInterface.php b/app/vendor/league/container/src/Argument/ResolvableArgumentInterface.php new file mode 100644 index 000000000..9fa9167c9 --- /dev/null +++ b/app/vendor/league/container/src/Argument/ResolvableArgumentInterface.php @@ -0,0 +1,10 @@ +=8.0.0" + }, + "autoload": { + "psr-4": { + "Psr\\Cache\\": "src/" + } + }, + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + } +} diff --git a/app/vendor/psr/cache/src/CacheException.php b/app/vendor/psr/cache/src/CacheException.php new file mode 100644 index 000000000..bb785f46c --- /dev/null +++ b/app/vendor/psr/cache/src/CacheException.php @@ -0,0 +1,10 @@ +logger = $logger; + } +} diff --git a/app/vendor/psr/log/src/LoggerInterface.php b/app/vendor/psr/log/src/LoggerInterface.php new file mode 100644 index 000000000..b4d062b9b --- /dev/null +++ b/app/vendor/psr/log/src/LoggerInterface.php @@ -0,0 +1,125 @@ +log(LogLevel::EMERGENCY, $message, $context); + } + + /** + * Action must be taken immediately. + * + * Example: Entire website down, database unavailable, etc. This should + * trigger the SMS alerts and wake you up. + * + * @param string|\Stringable $message + * @param array $context + * + * @return void + */ + public function alert(string|\Stringable $message, array $context = []) + { + $this->log(LogLevel::ALERT, $message, $context); + } + + /** + * Critical conditions. + * + * Example: Application component unavailable, unexpected exception. + * + * @param string|\Stringable $message + * @param array $context + * + * @return void + */ + public function critical(string|\Stringable $message, array $context = []) + { + $this->log(LogLevel::CRITICAL, $message, $context); + } + + /** + * Runtime errors that do not require immediate action but should typically + * be logged and monitored. + * + * @param string|\Stringable $message + * @param array $context + * + * @return void + */ + public function error(string|\Stringable $message, array $context = []) + { + $this->log(LogLevel::ERROR, $message, $context); + } + + /** + * Exceptional occurrences that are not errors. + * + * Example: Use of deprecated APIs, poor use of an API, undesirable things + * that are not necessarily wrong. + * + * @param string|\Stringable $message + * @param array $context + * + * @return void + */ + public function warning(string|\Stringable $message, array $context = []) + { + $this->log(LogLevel::WARNING, $message, $context); + } + + /** + * Normal but significant events. + * + * @param string|\Stringable $message + * @param array $context + * + * @return void + */ + public function notice(string|\Stringable $message, array $context = []) + { + $this->log(LogLevel::NOTICE, $message, $context); + } + + /** + * Interesting events. + * + * Example: User logs in, SQL logs. + * + * @param string|\Stringable $message + * @param array $context + * + * @return void + */ + public function info(string|\Stringable $message, array $context = []) + { + $this->log(LogLevel::INFO, $message, $context); + } + + /** + * Detailed debug information. + * + * @param string|\Stringable $message + * @param array $context + * + * @return void + */ + public function debug(string|\Stringable $message, array $context = []) + { + $this->log(LogLevel::DEBUG, $message, $context); + } + + /** + * Logs with an arbitrary level. + * + * @param mixed $level + * @param string|\Stringable $message + * @param array $context + * + * @return void + * + * @throws \Psr\Log\InvalidArgumentException + */ + abstract public function log($level, string|\Stringable $message, array $context = []); +} diff --git a/app/vendor/psr/log/src/NullLogger.php b/app/vendor/psr/log/src/NullLogger.php new file mode 100644 index 000000000..560770571 --- /dev/null +++ b/app/vendor/psr/log/src/NullLogger.php @@ -0,0 +1,30 @@ +logger) { }` + * blocks. + */ +class NullLogger extends AbstractLogger +{ + /** + * Logs with an arbitrary level. + * + * @param mixed $level + * @param string|\Stringable $message + * @param array $context + * + * @return void + * + * @throws \Psr\Log\InvalidArgumentException + */ + public function log($level, string|\Stringable $message, array $context = []) + { + // noop + } +} diff --git a/app/vendor/squizlabs/php_codesniffer/CodeSniffer.conf b/app/vendor/squizlabs/php_codesniffer/CodeSniffer.conf new file mode 100644 index 000000000..d9872f8d6 --- /dev/null +++ b/app/vendor/squizlabs/php_codesniffer/CodeSniffer.conf @@ -0,0 +1,5 @@ + '../../cakephp/cakephp-codesniffer,../../slevomat/coding-standard', +) +?> \ No newline at end of file