From 68ee7c86f0a15c000441a12ac5eedf0c08fd41f7 Mon Sep 17 00:00:00 2001 From: Khushboo Verma <43381712+vermakhushboo@users.noreply.github.com> Date: Wed, 31 Jan 2024 15:41:59 +0530 Subject: [PATCH 001/103] Add 451 error code --- src/Response.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Response.php b/src/Response.php index b9c7ba84..8d19f5a8 100755 --- a/src/Response.php +++ b/src/Response.php @@ -115,6 +115,8 @@ class Response public const STATUS_CODE_TOO_MANY_REQUESTS = 429; + public const STATUS_CODE_UNAVAILABLE_FOR_LEGAL_REASONS = 451; + public const STATUS_CODE_INTERNAL_SERVER_ERROR = 500; public const STATUS_CODE_NOT_IMPLEMENTED = 501; @@ -168,6 +170,7 @@ class Response self::STATUS_CODE_EXPECTATION_FAILED => 'Expectation Failed', self::STATUS_CODE_TOO_EARLY => 'Too Early', self::STATUS_CODE_TOO_MANY_REQUESTS => 'Too Many Requests', + self::STATUS_CODE_UNAVAILABLE_FOR_LEGAL_REASONS => 'Unavailable For Legal Reasons', self::STATUS_CODE_INTERNAL_SERVER_ERROR => 'Internal Server Error', self::STATUS_CODE_NOT_IMPLEMENTED => 'Not Implemented', self::STATUS_CODE_BAD_GATEWAY => 'Bad Gateway', From 87525cb65bf79a363a9744246d1de7f294c96e5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Thu, 4 Jan 2024 13:24:25 +0000 Subject: [PATCH 002/103] Wildcard host validator --- src/Validator/Host.php | 8 +++++--- tests/Validator/HostTest.php | 6 +++++- 2 files changed, 10 insertions(+), 4 deletions(-) mode change 100644 => 100755 src/Validator/Host.php diff --git a/src/Validator/Host.php b/src/Validator/Host.php old mode 100644 new mode 100755 index 1f7fb06c..77078b2c --- a/src/Validator/Host.php +++ b/src/Validator/Host.php @@ -51,11 +51,13 @@ public function isValid($value): bool return false; } - if (\in_array(\parse_url($value, PHP_URL_HOST), $this->whitelist)) { - return true; + $hostnameValidator = new Hostname($this->whitelist); + + if (!$hostnameValidator->isValid(\parse_url($value, PHP_URL_HOST))) { + return false; } - return false; + return true; } /** diff --git a/tests/Validator/HostTest.php b/tests/Validator/HostTest.php index 6ad38299..de62d4bf 100644 --- a/tests/Validator/HostTest.php +++ b/tests/Validator/HostTest.php @@ -21,7 +21,7 @@ class HostTest extends TestCase public function setUp(): void { - $this->host = new Host(['example.io', 'subdomain.example.test', 'localhost']); + $this->host = new Host(['example.io', 'subdomain.example.test', 'localhost', '*.appwrite.io']); } public function testIsValid() @@ -32,6 +32,10 @@ public function testIsValid() $this->assertEquals($this->host->isValid('localhost'), false); $this->assertEquals($this->host->isValid('http://subdomain.example.test/path'), true); $this->assertEquals($this->host->isValid('http://test.subdomain.example.test/path'), false); + $this->assertEquals($this->host->isValid('http://appwrite.io/path'), false); + $this->assertEquals($this->host->isValid('http://me.appwrite.io/path'), true); + $this->assertEquals($this->host->isValid('http://you.appwrite.io/path'), true); + $this->assertEquals($this->host->isValid('http://us.together.appwrite.io/path'), true); $this->assertEquals($this->host->getType(), 'string'); } } From 893d602cd96676810c25fc9b9a2e9360eebb898f Mon Sep 17 00:00:00 2001 From: Steven Nguyen Date: Mon, 11 Mar 2024 14:22:53 +0100 Subject: [PATCH 003/103] refactor(validator): simplify return --- src/Validator/Host.php | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/Validator/Host.php b/src/Validator/Host.php index 77078b2c..80fec5ec 100755 --- a/src/Validator/Host.php +++ b/src/Validator/Host.php @@ -53,11 +53,7 @@ public function isValid($value): bool $hostnameValidator = new Hostname($this->whitelist); - if (!$hostnameValidator->isValid(\parse_url($value, PHP_URL_HOST))) { - return false; - } - - return true; + return $hostnameValidator->isValid(\parse_url($value, PHP_URL_HOST)); } /** From 18fd859dd40d35298397ff72629b8e67c53d35d2 Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Thu, 7 Mar 2024 16:54:04 +0000 Subject: [PATCH 004/103] chore: test default param with function --- composer.json | 3 +- composer.lock | 309 +++++++++++++++++++++++----------------------- src/App.php | 3 + tests/AppTest.php | 43 +++++++ 4 files changed, 201 insertions(+), 157 deletions(-) diff --git a/composer.json b/composer.json index fe84d0d0..125da2e1 100644 --- a/composer.json +++ b/composer.json @@ -11,7 +11,8 @@ "minimum-stability": "stable", "autoload": { "psr-4": { - "Utopia\\": "src/" + "Utopia\\": "src/", + "Tests\\E2E\\": "tests/e2e" } }, "scripts": { diff --git a/composer.lock b/composer.lock index 9bcef239..6ecc6744 100644 --- a/composer.lock +++ b/composer.lock @@ -155,27 +155,27 @@ }, { "name": "doctrine/lexer", - "version": "3.0.0", + "version": "3.0.1", "source": { "type": "git", "url": "https://github.com/doctrine/lexer.git", - "reference": "84a527db05647743d50373e0ec53a152f2cde568" + "reference": "31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/lexer/zipball/84a527db05647743d50373e0ec53a152f2cde568", - "reference": "84a527db05647743d50373e0ec53a152f2cde568", + "url": "https://api.github.com/repos/doctrine/lexer/zipball/31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd", + "reference": "31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd", "shasum": "" }, "require": { "php": "^8.1" }, "require-dev": { - "doctrine/coding-standard": "^10", - "phpstan/phpstan": "^1.9", - "phpunit/phpunit": "^9.5", + "doctrine/coding-standard": "^12", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^10.5", "psalm/plugin-phpunit": "^0.18.3", - "vimeo/psalm": "^5.0" + "vimeo/psalm": "^5.21" }, "type": "library", "autoload": { @@ -212,7 +212,7 @@ ], "support": { "issues": "https://github.com/doctrine/lexer/issues", - "source": "https://github.com/doctrine/lexer/tree/3.0.0" + "source": "https://github.com/doctrine/lexer/tree/3.0.1" }, "funding": [ { @@ -228,20 +228,20 @@ "type": "tidelift" } ], - "time": "2022-12-15T16:57:16+00:00" + "time": "2024-02-05T11:56:58+00:00" }, { "name": "laravel/pint", - "version": "v1.13.7", + "version": "v1.14.0", "source": { "type": "git", "url": "https://github.com/laravel/pint.git", - "reference": "4157768980dbd977f1c4b4cc94997416d8b30ece" + "reference": "6b127276e3f263f7bb17d5077e9e0269e61b2a0e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/pint/zipball/4157768980dbd977f1c4b4cc94997416d8b30ece", - "reference": "4157768980dbd977f1c4b4cc94997416d8b30ece", + "url": "https://api.github.com/repos/laravel/pint/zipball/6b127276e3f263f7bb17d5077e9e0269e61b2a0e", + "reference": "6b127276e3f263f7bb17d5077e9e0269e61b2a0e", "shasum": "" }, "require": { @@ -252,13 +252,13 @@ "php": "^8.1.0" }, "require-dev": { - "friendsofphp/php-cs-fixer": "^3.38.0", - "illuminate/view": "^10.30.1", + "friendsofphp/php-cs-fixer": "^3.49.0", + "illuminate/view": "^10.43.0", + "larastan/larastan": "^2.8.1", "laravel-zero/framework": "^10.3.0", - "mockery/mockery": "^1.6.6", - "nunomaduro/larastan": "^2.6.4", + "mockery/mockery": "^1.6.7", "nunomaduro/termwind": "^1.15.1", - "pestphp/pest": "^2.24.2" + "pestphp/pest": "^2.33.6" }, "bin": [ "builds/pint" @@ -294,7 +294,7 @@ "issues": "https://github.com/laravel/pint/issues", "source": "https://github.com/laravel/pint" }, - "time": "2023-12-05T19:43:12+00:00" + "time": "2024-02-20T17:38:05+00:00" }, { "name": "myclabs/deep-copy", @@ -357,25 +357,27 @@ }, { "name": "nikic/php-parser", - "version": "v4.18.0", + "version": "v5.0.2", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "1bcbb2179f97633e98bbbc87044ee2611c7d7999" + "reference": "139676794dc1e9231bf7bcd123cfc0c99182cb13" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/1bcbb2179f97633e98bbbc87044ee2611c7d7999", - "reference": "1bcbb2179f97633e98bbbc87044ee2611c7d7999", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/139676794dc1e9231bf7bcd123cfc0c99182cb13", + "reference": "139676794dc1e9231bf7bcd123cfc0c99182cb13", "shasum": "" }, "require": { + "ext-ctype": "*", + "ext-json": "*", "ext-tokenizer": "*", - "php": ">=7.0" + "php": ">=7.4" }, "require-dev": { "ircmaxell/php-yacc": "^0.0.7", - "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0" + "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0" }, "bin": [ "bin/php-parse" @@ -383,7 +385,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.9-dev" + "dev-master": "5.0-dev" } }, "autoload": { @@ -407,26 +409,27 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v4.18.0" + "source": "https://github.com/nikic/PHP-Parser/tree/v5.0.2" }, - "time": "2023-12-10T21:03:43+00:00" + "time": "2024-03-05T20:51:40+00:00" }, { "name": "phar-io/manifest", - "version": "2.0.3", + "version": "2.0.4", "source": { "type": "git", "url": "https://github.com/phar-io/manifest.git", - "reference": "97803eca37d319dfa7826cc2437fc020857acb53" + "reference": "54750ef60c58e43759730615a392c31c80e23176" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phar-io/manifest/zipball/97803eca37d319dfa7826cc2437fc020857acb53", - "reference": "97803eca37d319dfa7826cc2437fc020857acb53", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/54750ef60c58e43759730615a392c31c80e23176", + "reference": "54750ef60c58e43759730615a392c31c80e23176", "shasum": "" }, "require": { "ext-dom": "*", + "ext-libxml": "*", "ext-phar": "*", "ext-xmlwriter": "*", "phar-io/version": "^3.0.1", @@ -467,9 +470,15 @@ "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", "support": { "issues": "https://github.com/phar-io/manifest/issues", - "source": "https://github.com/phar-io/manifest/tree/2.0.3" + "source": "https://github.com/phar-io/manifest/tree/2.0.4" }, - "time": "2021-07-20T11:28:43+00:00" + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2024-03-03T12:33:53+00:00" }, { "name": "phar-io/version", @@ -724,16 +733,16 @@ }, { "name": "phpstan/phpstan", - "version": "1.10.50", + "version": "1.10.60", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "06a98513ac72c03e8366b5a0cb00750b487032e4" + "reference": "95dcea7d6c628a3f2f56d091d8a0219485a86bbe" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/06a98513ac72c03e8366b5a0cb00750b487032e4", - "reference": "06a98513ac72c03e8366b5a0cb00750b487032e4", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/95dcea7d6c628a3f2f56d091d8a0219485a86bbe", + "reference": "95dcea7d6c628a3f2f56d091d8a0219485a86bbe", "shasum": "" }, "require": { @@ -782,20 +791,20 @@ "type": "tidelift" } ], - "time": "2023-12-13T10:59:42+00:00" + "time": "2024-03-07T13:30:19+00:00" }, { "name": "phpunit/php-code-coverage", - "version": "9.2.30", + "version": "9.2.31", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "ca2bd87d2f9215904682a9cb9bb37dda98e76089" + "reference": "48c34b5d8d983006bd2adc2d0de92963b9155965" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/ca2bd87d2f9215904682a9cb9bb37dda98e76089", - "reference": "ca2bd87d2f9215904682a9cb9bb37dda98e76089", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/48c34b5d8d983006bd2adc2d0de92963b9155965", + "reference": "48c34b5d8d983006bd2adc2d0de92963b9155965", "shasum": "" }, "require": { @@ -852,7 +861,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.30" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.31" }, "funding": [ { @@ -860,7 +869,7 @@ "type": "github" } ], - "time": "2023-12-22T06:47:57+00:00" + "time": "2024-03-02T06:37:42+00:00" }, { "name": "phpunit/php-file-iterator", @@ -1105,16 +1114,16 @@ }, { "name": "phpunit/phpunit", - "version": "9.6.15", + "version": "9.6.17", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "05017b80304e0eb3f31d90194a563fd53a6021f1" + "reference": "1a156980d78a6666721b7e8e8502fe210b587fcd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/05017b80304e0eb3f31d90194a563fd53a6021f1", - "reference": "05017b80304e0eb3f31d90194a563fd53a6021f1", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/1a156980d78a6666721b7e8e8502fe210b587fcd", + "reference": "1a156980d78a6666721b7e8e8502fe210b587fcd", "shasum": "" }, "require": { @@ -1188,7 +1197,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.15" + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.17" }, "funding": [ { @@ -1204,7 +1213,7 @@ "type": "tidelift" } ], - "time": "2023-12-01T16:55:19+00:00" + "time": "2024-02-23T13:14:51+00:00" }, { "name": "psr/cache", @@ -1360,16 +1369,16 @@ }, { "name": "sebastian/cli-parser", - "version": "1.0.1", + "version": "1.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/cli-parser.git", - "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2" + "reference": "2b56bea83a09de3ac06bb18b92f068e60cc6f50b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/442e7c7e687e42adc03470c7b668bc4b2402c0b2", - "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/2b56bea83a09de3ac06bb18b92f068e60cc6f50b", + "reference": "2b56bea83a09de3ac06bb18b92f068e60cc6f50b", "shasum": "" }, "require": { @@ -1404,7 +1413,7 @@ "homepage": "https://github.com/sebastianbergmann/cli-parser", "support": { "issues": "https://github.com/sebastianbergmann/cli-parser/issues", - "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.1" + "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.2" }, "funding": [ { @@ -1412,7 +1421,7 @@ "type": "github" } ], - "time": "2020-09-28T06:08:49+00:00" + "time": "2024-03-02T06:27:43+00:00" }, { "name": "sebastian/code-unit", @@ -1658,16 +1667,16 @@ }, { "name": "sebastian/diff", - "version": "4.0.5", + "version": "4.0.6", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "74be17022044ebaaecfdf0c5cd504fc9cd5a7131" + "reference": "ba01945089c3a293b01ba9badc29ad55b106b0bc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/74be17022044ebaaecfdf0c5cd504fc9cd5a7131", - "reference": "74be17022044ebaaecfdf0c5cd504fc9cd5a7131", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/ba01945089c3a293b01ba9badc29ad55b106b0bc", + "reference": "ba01945089c3a293b01ba9badc29ad55b106b0bc", "shasum": "" }, "require": { @@ -1712,7 +1721,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/diff/issues", - "source": "https://github.com/sebastianbergmann/diff/tree/4.0.5" + "source": "https://github.com/sebastianbergmann/diff/tree/4.0.6" }, "funding": [ { @@ -1720,7 +1729,7 @@ "type": "github" } ], - "time": "2023-05-07T05:35:17+00:00" + "time": "2024-03-02T06:30:58+00:00" }, { "name": "sebastian/environment", @@ -1787,16 +1796,16 @@ }, { "name": "sebastian/exporter", - "version": "4.0.5", + "version": "4.0.6", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d" + "reference": "78c00df8f170e02473b682df15bfcdacc3d32d72" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d", - "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/78c00df8f170e02473b682df15bfcdacc3d32d72", + "reference": "78c00df8f170e02473b682df15bfcdacc3d32d72", "shasum": "" }, "require": { @@ -1852,7 +1861,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/exporter/issues", - "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.5" + "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.6" }, "funding": [ { @@ -1860,20 +1869,20 @@ "type": "github" } ], - "time": "2022-09-14T06:03:37+00:00" + "time": "2024-03-02T06:33:00+00:00" }, { "name": "sebastian/global-state", - "version": "5.0.6", + "version": "5.0.7", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "bde739e7565280bda77be70044ac1047bc007e34" + "reference": "bca7df1f32ee6fe93b4d4a9abbf69e13a4ada2c9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bde739e7565280bda77be70044ac1047bc007e34", - "reference": "bde739e7565280bda77be70044ac1047bc007e34", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bca7df1f32ee6fe93b4d4a9abbf69e13a4ada2c9", + "reference": "bca7df1f32ee6fe93b4d4a9abbf69e13a4ada2c9", "shasum": "" }, "require": { @@ -1916,7 +1925,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/global-state/issues", - "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.6" + "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.7" }, "funding": [ { @@ -1924,7 +1933,7 @@ "type": "github" } ], - "time": "2023-08-02T09:26:13+00:00" + "time": "2024-03-02T06:35:11+00:00" }, { "name": "sebastian/lines-of-code", @@ -2324,16 +2333,16 @@ }, { "name": "seld/jsonlint", - "version": "1.10.1", + "version": "1.10.2", "source": { "type": "git", "url": "https://github.com/Seldaek/jsonlint.git", - "reference": "76d449a358ece77d6f1d6331c68453e657172202" + "reference": "9bb7db07b5d66d90f6ebf542f09fc67d800e5259" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Seldaek/jsonlint/zipball/76d449a358ece77d6f1d6331c68453e657172202", - "reference": "76d449a358ece77d6f1d6331c68453e657172202", + "url": "https://api.github.com/repos/Seldaek/jsonlint/zipball/9bb7db07b5d66d90f6ebf542f09fc67d800e5259", + "reference": "9bb7db07b5d66d90f6ebf542f09fc67d800e5259", "shasum": "" }, "require": { @@ -2372,7 +2381,7 @@ ], "support": { "issues": "https://github.com/Seldaek/jsonlint/issues", - "source": "https://github.com/Seldaek/jsonlint/tree/1.10.1" + "source": "https://github.com/Seldaek/jsonlint/tree/1.10.2" }, "funding": [ { @@ -2384,20 +2393,20 @@ "type": "tidelift" } ], - "time": "2023-12-18T13:03:25+00:00" + "time": "2024-02-07T12:57:50+00:00" }, { "name": "symfony/console", - "version": "v7.0.1", + "version": "v7.0.4", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "cdce5c684b2f920bb1343deecdfba356ffad83d5" + "reference": "6b099f3306f7c9c2d2786ed736d0026b2903205f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/cdce5c684b2f920bb1343deecdfba356ffad83d5", - "reference": "cdce5c684b2f920bb1343deecdfba356ffad83d5", + "url": "https://api.github.com/repos/symfony/console/zipball/6b099f3306f7c9c2d2786ed736d0026b2903205f", + "reference": "6b099f3306f7c9c2d2786ed736d0026b2903205f", "shasum": "" }, "require": { @@ -2461,7 +2470,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v7.0.1" + "source": "https://github.com/symfony/console/tree/v7.0.4" }, "funding": [ { @@ -2477,7 +2486,7 @@ "type": "tidelift" } ], - "time": "2023-12-01T15:10:06+00:00" + "time": "2024-02-22T20:27:20+00:00" }, { "name": "symfony/deprecation-contracts", @@ -2548,16 +2557,16 @@ }, { "name": "symfony/filesystem", - "version": "v7.0.0", + "version": "v7.0.3", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "7da8ea2362a283771478c5f7729cfcb43a76b8b7" + "reference": "2890e3a825bc0c0558526c04499c13f83e1b6b12" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/7da8ea2362a283771478c5f7729cfcb43a76b8b7", - "reference": "7da8ea2362a283771478c5f7729cfcb43a76b8b7", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/2890e3a825bc0c0558526c04499c13f83e1b6b12", + "reference": "2890e3a825bc0c0558526c04499c13f83e1b6b12", "shasum": "" }, "require": { @@ -2591,7 +2600,7 @@ "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/filesystem/tree/v7.0.0" + "source": "https://github.com/symfony/filesystem/tree/v7.0.3" }, "funding": [ { @@ -2607,7 +2616,7 @@ "type": "tidelift" } ], - "time": "2023-07-27T06:33:22+00:00" + "time": "2024-01-23T15:02:46+00:00" }, { "name": "symfony/finder", @@ -2742,16 +2751,16 @@ }, { "name": "symfony/polyfill-ctype", - "version": "v1.28.0", + "version": "v1.29.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb" + "reference": "ef4d7e442ca910c4764bce785146269b30cb5fc4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb", - "reference": "ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/ef4d7e442ca910c4764bce785146269b30cb5fc4", + "reference": "ef4d7e442ca910c4764bce785146269b30cb5fc4", "shasum": "" }, "require": { @@ -2765,9 +2774,6 @@ }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "1.28-dev" - }, "thanks": { "name": "symfony/polyfill", "url": "https://github.com/symfony/polyfill" @@ -2804,7 +2810,7 @@ "portable" ], "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.28.0" + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.29.0" }, "funding": [ { @@ -2820,20 +2826,20 @@ "type": "tidelift" } ], - "time": "2023-01-26T09:26:14+00:00" + "time": "2024-01-29T20:11:03+00:00" }, { "name": "symfony/polyfill-intl-grapheme", - "version": "v1.28.0", + "version": "v1.29.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "875e90aeea2777b6f135677f618529449334a612" + "reference": "32a9da87d7b3245e09ac426c83d334ae9f06f80f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/875e90aeea2777b6f135677f618529449334a612", - "reference": "875e90aeea2777b6f135677f618529449334a612", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/32a9da87d7b3245e09ac426c83d334ae9f06f80f", + "reference": "32a9da87d7b3245e09ac426c83d334ae9f06f80f", "shasum": "" }, "require": { @@ -2844,9 +2850,6 @@ }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "1.28-dev" - }, "thanks": { "name": "symfony/polyfill", "url": "https://github.com/symfony/polyfill" @@ -2885,7 +2888,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.28.0" + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.29.0" }, "funding": [ { @@ -2901,20 +2904,20 @@ "type": "tidelift" } ], - "time": "2023-01-26T09:26:14+00:00" + "time": "2024-01-29T20:11:03+00:00" }, { "name": "symfony/polyfill-intl-normalizer", - "version": "v1.28.0", + "version": "v1.29.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-normalizer.git", - "reference": "8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92" + "reference": "bc45c394692b948b4d383a08d7753968bed9a83d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92", - "reference": "8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/bc45c394692b948b4d383a08d7753968bed9a83d", + "reference": "bc45c394692b948b4d383a08d7753968bed9a83d", "shasum": "" }, "require": { @@ -2925,9 +2928,6 @@ }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "1.28-dev" - }, "thanks": { "name": "symfony/polyfill", "url": "https://github.com/symfony/polyfill" @@ -2969,7 +2969,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.28.0" + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.29.0" }, "funding": [ { @@ -2985,20 +2985,20 @@ "type": "tidelift" } ], - "time": "2023-01-26T09:26:14+00:00" + "time": "2024-01-29T20:11:03+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.28.0", + "version": "v1.29.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "42292d99c55abe617799667f454222c54c60e229" + "reference": "9773676c8a1bb1f8d4340a62efe641cf76eda7ec" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/42292d99c55abe617799667f454222c54c60e229", - "reference": "42292d99c55abe617799667f454222c54c60e229", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9773676c8a1bb1f8d4340a62efe641cf76eda7ec", + "reference": "9773676c8a1bb1f8d4340a62efe641cf76eda7ec", "shasum": "" }, "require": { @@ -3012,9 +3012,6 @@ }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "1.28-dev" - }, "thanks": { "name": "symfony/polyfill", "url": "https://github.com/symfony/polyfill" @@ -3052,7 +3049,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.28.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.29.0" }, "funding": [ { @@ -3068,20 +3065,20 @@ "type": "tidelift" } ], - "time": "2023-07-28T09:04:16+00:00" + "time": "2024-01-29T20:11:03+00:00" }, { "name": "symfony/process", - "version": "v7.0.0", + "version": "v7.0.4", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "13bdb1670c7f510494e04fcb2bfa29af63db9c0d" + "reference": "0e7727191c3b71ebec6d529fa0e50a01ca5679e9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/13bdb1670c7f510494e04fcb2bfa29af63db9c0d", - "reference": "13bdb1670c7f510494e04fcb2bfa29af63db9c0d", + "url": "https://api.github.com/repos/symfony/process/zipball/0e7727191c3b71ebec6d529fa0e50a01ca5679e9", + "reference": "0e7727191c3b71ebec6d529fa0e50a01ca5679e9", "shasum": "" }, "require": { @@ -3113,7 +3110,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v7.0.0" + "source": "https://github.com/symfony/process/tree/v7.0.4" }, "funding": [ { @@ -3129,25 +3126,25 @@ "type": "tidelift" } ], - "time": "2023-11-20T16:43:42+00:00" + "time": "2024-02-22T20:27:20+00:00" }, { "name": "symfony/service-contracts", - "version": "v3.4.0", + "version": "v3.4.1", "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", - "reference": "b3313c2dbffaf71c8de2934e2ea56ed2291a3838" + "reference": "fe07cbc8d837f60caf7018068e350cc5163681a0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/b3313c2dbffaf71c8de2934e2ea56ed2291a3838", - "reference": "b3313c2dbffaf71c8de2934e2ea56ed2291a3838", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/fe07cbc8d837f60caf7018068e350cc5163681a0", + "reference": "fe07cbc8d837f60caf7018068e350cc5163681a0", "shasum": "" }, "require": { "php": ">=8.1", - "psr/container": "^2.0" + "psr/container": "^1.1|^2.0" }, "conflict": { "ext-psr": "<1.1|>=2" @@ -3195,7 +3192,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/service-contracts/tree/v3.4.0" + "source": "https://github.com/symfony/service-contracts/tree/v3.4.1" }, "funding": [ { @@ -3211,20 +3208,20 @@ "type": "tidelift" } ], - "time": "2023-07-30T20:28:31+00:00" + "time": "2023-12-26T14:02:43+00:00" }, { "name": "symfony/string", - "version": "v7.0.0", + "version": "v7.0.4", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "92bd2bfbba476d4a1838e5e12168bef2fd1e6620" + "reference": "f5832521b998b0bec40bee688ad5de98d4cf111b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/92bd2bfbba476d4a1838e5e12168bef2fd1e6620", - "reference": "92bd2bfbba476d4a1838e5e12168bef2fd1e6620", + "url": "https://api.github.com/repos/symfony/string/zipball/f5832521b998b0bec40bee688ad5de98d4cf111b", + "reference": "f5832521b998b0bec40bee688ad5de98d4cf111b", "shasum": "" }, "require": { @@ -3281,7 +3278,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v7.0.0" + "source": "https://github.com/symfony/string/tree/v7.0.4" }, "funding": [ { @@ -3297,20 +3294,20 @@ "type": "tidelift" } ], - "time": "2023-11-29T08:40:23+00:00" + "time": "2024-02-01T13:17:36+00:00" }, { "name": "theseer/tokenizer", - "version": "1.2.2", + "version": "1.2.3", "source": { "type": "git", "url": "https://github.com/theseer/tokenizer.git", - "reference": "b2ad5003ca10d4ee50a12da31de12a5774ba6b96" + "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/theseer/tokenizer/zipball/b2ad5003ca10d4ee50a12da31de12a5774ba6b96", - "reference": "b2ad5003ca10d4ee50a12da31de12a5774ba6b96", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", + "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", "shasum": "" }, "require": { @@ -3339,7 +3336,7 @@ "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", "support": { "issues": "https://github.com/theseer/tokenizer/issues", - "source": "https://github.com/theseer/tokenizer/tree/1.2.2" + "source": "https://github.com/theseer/tokenizer/tree/1.2.3" }, "funding": [ { @@ -3347,7 +3344,7 @@ "type": "github" } ], - "time": "2023-11-20T00:12:19+00:00" + "time": "2024-03-03T12:36:25+00:00" }, { "name": "webmozart/glob", @@ -3408,5 +3405,5 @@ "php": ">=8.0" }, "platform-dev": [], - "plugin-api-version": "2.3.0" + "plugin-api-version": "2.6.0" } diff --git a/src/App.php b/src/App.php index afb88192..b8ce4184 100755 --- a/src/App.php +++ b/src/App.php @@ -599,6 +599,9 @@ protected function getArguments(Hook $hook, array $values, array $requestParams) $paramExists = $existsInRequest || $existsInValues; $arg = $existsInRequest ? $requestParams[$key] : $param['default']; + if (\is_callable($arg)) { + $arg = \call_user_func_array($arg, $this->getResources($param['injections'])); + } $value = $existsInValues ? $values[$key] : $arg; if (!$param['skipValidation']) { diff --git a/tests/AppTest.php b/tests/AppTest.php index a5fe1a27..2633f984 100755 --- a/tests/AppTest.php +++ b/tests/AppTest.php @@ -115,6 +115,49 @@ public function testCanGetResources(): void $this->assertEquals('x-def-y-def-' . $resource, $result); } + public function testCanGetDefaultValue(): void + { + App::setResource('first', fn ($second) => "first-{$second}", ['second']); + App::setResource('second', fn () => 'second'); + + $second = $this->app->getResource('second'); + $first = $this->app->getResource('first'); + $this->assertEquals('second', $second); + $this->assertEquals('first-second', $first); + + // Default Params + $route = new Route('GET', '/path'); + + $route + ->param('x', 'x-def', new Text(200), 'x param', true) + ->action(function ($x) { + echo $x; + }); + + \ob_start(); + $this->app->execute($route, new Request(), new Response()); + $result = \ob_get_contents(); + \ob_end_clean(); + + // Default Value using function + $route = new Route('GET', '/path'); + + $route + ->param('x', function ($first, $second) { + return $first . '-' . $second; + } , new Text(200), 'x param', true, ['first', 'second']) + ->action(function ($x) { + echo $x; + }); + + \ob_start(); + $this->app->execute($route, new Request(), new Response()); + $result = \ob_get_contents(); + \ob_end_clean(); + + $this->assertEquals('first-second-second', $result); + } + public function testCanExecuteRoute(): void { App::setResource('rand', fn () => rand()); From d74b9c3f449d83b886a30532220b2534078070dc Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Thu, 7 Mar 2024 16:54:34 +0000 Subject: [PATCH 005/103] chore: update test --- tests/AppTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/AppTest.php b/tests/AppTest.php index 2633f984..06fff140 100755 --- a/tests/AppTest.php +++ b/tests/AppTest.php @@ -115,7 +115,7 @@ public function testCanGetResources(): void $this->assertEquals('x-def-y-def-' . $resource, $result); } - public function testCanGetDefaultValue(): void + public function testCanGetDefaultValueWithFunction(): void { App::setResource('first', fn ($second) => "first-{$second}", ['second']); App::setResource('second', fn () => 'second'); From 7009952d26466a2cf21486e55c4a66e91744ad8d Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Thu, 7 Mar 2024 16:57:25 +0000 Subject: [PATCH 006/103] chore: linter --- tests/AppTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/AppTest.php b/tests/AppTest.php index 06fff140..e12eab0a 100755 --- a/tests/AppTest.php +++ b/tests/AppTest.php @@ -125,7 +125,7 @@ public function testCanGetDefaultValueWithFunction(): void $this->assertEquals('second', $second); $this->assertEquals('first-second', $first); - // Default Params + // Default Params $route = new Route('GET', '/path'); $route @@ -145,7 +145,7 @@ public function testCanGetDefaultValueWithFunction(): void $route ->param('x', function ($first, $second) { return $first . '-' . $second; - } , new Text(200), 'x param', true, ['first', 'second']) + }, new Text(200), 'x param', true, ['first', 'second']) ->action(function ($x) { echo $x; }); From 5bcb735fc240282dc6369fa11f0b40b52fff190f Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Thu, 7 Mar 2024 17:02:38 +0000 Subject: [PATCH 007/103] chore: update test --- tests/AppTest.php | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/tests/AppTest.php b/tests/AppTest.php index e12eab0a..c3c2580c 100755 --- a/tests/AppTest.php +++ b/tests/AppTest.php @@ -125,20 +125,6 @@ public function testCanGetDefaultValueWithFunction(): void $this->assertEquals('second', $second); $this->assertEquals('first-second', $first); - // Default Params - $route = new Route('GET', '/path'); - - $route - ->param('x', 'x-def', new Text(200), 'x param', true) - ->action(function ($x) { - echo $x; - }); - - \ob_start(); - $this->app->execute($route, new Request(), new Response()); - $result = \ob_get_contents(); - \ob_end_clean(); - // Default Value using function $route = new Route('GET', '/path'); From 9dd7242718ae450762b3db2bc48c94636e3820ba Mon Sep 17 00:00:00 2001 From: Steven Nguyen Date: Thu, 21 Mar 2024 09:35:25 -0700 Subject: [PATCH 008/103] fix: remove tests from autoload Adding the Tests namespace causes conflicts downstream. --- composer.json | 3 +-- composer.lock | 49 ++++++++++++++++++++++++------------------------- 2 files changed, 25 insertions(+), 27 deletions(-) diff --git a/composer.json b/composer.json index 125da2e1..fe84d0d0 100644 --- a/composer.json +++ b/composer.json @@ -11,8 +11,7 @@ "minimum-stability": "stable", "autoload": { "psr-4": { - "Utopia\\": "src/", - "Tests\\E2E\\": "tests/e2e" + "Utopia\\": "src/" } }, "scripts": { diff --git a/composer.lock b/composer.lock index 6ecc6744..bbff81f8 100644 --- a/composer.lock +++ b/composer.lock @@ -733,16 +733,16 @@ }, { "name": "phpstan/phpstan", - "version": "1.10.60", + "version": "1.10.64", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "95dcea7d6c628a3f2f56d091d8a0219485a86bbe" + "reference": "fb9f270daffedcb5ff46275dcafe92538b1bc4bb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/95dcea7d6c628a3f2f56d091d8a0219485a86bbe", - "reference": "95dcea7d6c628a3f2f56d091d8a0219485a86bbe", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/fb9f270daffedcb5ff46275dcafe92538b1bc4bb", + "reference": "fb9f270daffedcb5ff46275dcafe92538b1bc4bb", "shasum": "" }, "require": { @@ -791,7 +791,7 @@ "type": "tidelift" } ], - "time": "2024-03-07T13:30:19+00:00" + "time": "2024-03-21T09:57:47+00:00" }, { "name": "phpunit/php-code-coverage", @@ -1114,16 +1114,16 @@ }, { "name": "phpunit/phpunit", - "version": "9.6.17", + "version": "9.6.18", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "1a156980d78a6666721b7e8e8502fe210b587fcd" + "reference": "32c2c2d6580b1d8ab3c10b1e9e4dc263cc69bb04" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/1a156980d78a6666721b7e8e8502fe210b587fcd", - "reference": "1a156980d78a6666721b7e8e8502fe210b587fcd", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/32c2c2d6580b1d8ab3c10b1e9e4dc263cc69bb04", + "reference": "32c2c2d6580b1d8ab3c10b1e9e4dc263cc69bb04", "shasum": "" }, "require": { @@ -1197,7 +1197,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.17" + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.18" }, "funding": [ { @@ -1213,7 +1213,7 @@ "type": "tidelift" } ], - "time": "2024-02-23T13:14:51+00:00" + "time": "2024-03-21T12:07:32+00:00" }, { "name": "psr/cache", @@ -2169,16 +2169,16 @@ }, { "name": "sebastian/resource-operations", - "version": "3.0.3", + "version": "3.0.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/resource-operations.git", - "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8" + "reference": "05d5692a7993ecccd56a03e40cd7e5b09b1d404e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", - "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", + "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/05d5692a7993ecccd56a03e40cd7e5b09b1d404e", + "reference": "05d5692a7993ecccd56a03e40cd7e5b09b1d404e", "shasum": "" }, "require": { @@ -2190,7 +2190,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-main": "3.0-dev" } }, "autoload": { @@ -2211,8 +2211,7 @@ "description": "Provides a list of PHP built-in functions that operate on resources", "homepage": "https://www.github.com/sebastianbergmann/resource-operations", "support": { - "issues": "https://github.com/sebastianbergmann/resource-operations/issues", - "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.3" + "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.4" }, "funding": [ { @@ -2220,7 +2219,7 @@ "type": "github" } ], - "time": "2020-09-28T06:45:17+00:00" + "time": "2024-03-14T16:00:52+00:00" }, { "name": "sebastian/type", @@ -3348,16 +3347,16 @@ }, { "name": "webmozart/glob", - "version": "4.6.0", + "version": "4.7.0", "source": { "type": "git", "url": "https://github.com/webmozarts/glob.git", - "reference": "3c17f7dec3d9d0e87b575026011f2e75a56ed655" + "reference": "8a2842112d6916e61e0e15e316465b611f3abc17" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/webmozarts/glob/zipball/3c17f7dec3d9d0e87b575026011f2e75a56ed655", - "reference": "3c17f7dec3d9d0e87b575026011f2e75a56ed655", + "url": "https://api.github.com/repos/webmozarts/glob/zipball/8a2842112d6916e61e0e15e316465b611f3abc17", + "reference": "8a2842112d6916e61e0e15e316465b611f3abc17", "shasum": "" }, "require": { @@ -3391,9 +3390,9 @@ "description": "A PHP implementation of Ant's glob.", "support": { "issues": "https://github.com/webmozarts/glob/issues", - "source": "https://github.com/webmozarts/glob/tree/4.6.0" + "source": "https://github.com/webmozarts/glob/tree/4.7.0" }, - "time": "2022-05-24T19:45:58+00:00" + "time": "2024-03-07T20:33:40+00:00" } ], "aliases": [], From 3068acc87e1407e6e909dcd2c6e3ae78aa9beece Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Thu, 21 Mar 2024 17:59:59 +0000 Subject: [PATCH 009/103] chore: fix default param --- src/App.php | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/App.php b/src/App.php index b8ce4184..dd9cb926 100755 --- a/src/App.php +++ b/src/App.php @@ -598,10 +598,16 @@ protected function getArguments(Hook $hook, array $values, array $requestParams) $existsInValues = \array_key_exists($key, $values); $paramExists = $existsInRequest || $existsInValues; - $arg = $existsInRequest ? $requestParams[$key] : $param['default']; - if (\is_callable($arg)) { - $arg = \call_user_func_array($arg, $this->getResources($param['injections'])); + if ($existsInRequest) { + $arg = $requestParams[$key]; + } else { + if (!is_string($param['default']) && \is_callable($param['default'])) { + $arg = \call_user_func_array($param['default'], $this->getResources($param['injections'])); + } else { + $arg = $param['default']; + } } + $value = $existsInValues ? $values[$key] : $arg; if (!$param['skipValidation']) { From f11f6d56d8a5560a4f2e64198651df909f845b0d Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Thu, 21 Mar 2024 18:06:19 +0000 Subject: [PATCH 010/103] chore: add test for callable params --- composer.lock | 49 +++++++++++++++++++++++------------------------ tests/AppTest.php | 16 ++++++++++++++++ 2 files changed, 40 insertions(+), 25 deletions(-) diff --git a/composer.lock b/composer.lock index 6ecc6744..bbff81f8 100644 --- a/composer.lock +++ b/composer.lock @@ -733,16 +733,16 @@ }, { "name": "phpstan/phpstan", - "version": "1.10.60", + "version": "1.10.64", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "95dcea7d6c628a3f2f56d091d8a0219485a86bbe" + "reference": "fb9f270daffedcb5ff46275dcafe92538b1bc4bb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/95dcea7d6c628a3f2f56d091d8a0219485a86bbe", - "reference": "95dcea7d6c628a3f2f56d091d8a0219485a86bbe", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/fb9f270daffedcb5ff46275dcafe92538b1bc4bb", + "reference": "fb9f270daffedcb5ff46275dcafe92538b1bc4bb", "shasum": "" }, "require": { @@ -791,7 +791,7 @@ "type": "tidelift" } ], - "time": "2024-03-07T13:30:19+00:00" + "time": "2024-03-21T09:57:47+00:00" }, { "name": "phpunit/php-code-coverage", @@ -1114,16 +1114,16 @@ }, { "name": "phpunit/phpunit", - "version": "9.6.17", + "version": "9.6.18", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "1a156980d78a6666721b7e8e8502fe210b587fcd" + "reference": "32c2c2d6580b1d8ab3c10b1e9e4dc263cc69bb04" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/1a156980d78a6666721b7e8e8502fe210b587fcd", - "reference": "1a156980d78a6666721b7e8e8502fe210b587fcd", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/32c2c2d6580b1d8ab3c10b1e9e4dc263cc69bb04", + "reference": "32c2c2d6580b1d8ab3c10b1e9e4dc263cc69bb04", "shasum": "" }, "require": { @@ -1197,7 +1197,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.17" + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.18" }, "funding": [ { @@ -1213,7 +1213,7 @@ "type": "tidelift" } ], - "time": "2024-02-23T13:14:51+00:00" + "time": "2024-03-21T12:07:32+00:00" }, { "name": "psr/cache", @@ -2169,16 +2169,16 @@ }, { "name": "sebastian/resource-operations", - "version": "3.0.3", + "version": "3.0.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/resource-operations.git", - "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8" + "reference": "05d5692a7993ecccd56a03e40cd7e5b09b1d404e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", - "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", + "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/05d5692a7993ecccd56a03e40cd7e5b09b1d404e", + "reference": "05d5692a7993ecccd56a03e40cd7e5b09b1d404e", "shasum": "" }, "require": { @@ -2190,7 +2190,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-main": "3.0-dev" } }, "autoload": { @@ -2211,8 +2211,7 @@ "description": "Provides a list of PHP built-in functions that operate on resources", "homepage": "https://www.github.com/sebastianbergmann/resource-operations", "support": { - "issues": "https://github.com/sebastianbergmann/resource-operations/issues", - "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.3" + "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.4" }, "funding": [ { @@ -2220,7 +2219,7 @@ "type": "github" } ], - "time": "2020-09-28T06:45:17+00:00" + "time": "2024-03-14T16:00:52+00:00" }, { "name": "sebastian/type", @@ -3348,16 +3347,16 @@ }, { "name": "webmozart/glob", - "version": "4.6.0", + "version": "4.7.0", "source": { "type": "git", "url": "https://github.com/webmozarts/glob.git", - "reference": "3c17f7dec3d9d0e87b575026011f2e75a56ed655" + "reference": "8a2842112d6916e61e0e15e316465b611f3abc17" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/webmozarts/glob/zipball/3c17f7dec3d9d0e87b575026011f2e75a56ed655", - "reference": "3c17f7dec3d9d0e87b575026011f2e75a56ed655", + "url": "https://api.github.com/repos/webmozarts/glob/zipball/8a2842112d6916e61e0e15e316465b611f3abc17", + "reference": "8a2842112d6916e61e0e15e316465b611f3abc17", "shasum": "" }, "require": { @@ -3391,9 +3390,9 @@ "description": "A PHP implementation of Ant's glob.", "support": { "issues": "https://github.com/webmozarts/glob/issues", - "source": "https://github.com/webmozarts/glob/tree/4.6.0" + "source": "https://github.com/webmozarts/glob/tree/4.7.0" }, - "time": "2022-05-24T19:45:58+00:00" + "time": "2024-03-07T20:33:40+00:00" } ], "aliases": [], diff --git a/tests/AppTest.php b/tests/AppTest.php index c3c2580c..38f720dd 100755 --- a/tests/AppTest.php +++ b/tests/AppTest.php @@ -142,6 +142,22 @@ public function testCanGetDefaultValueWithFunction(): void \ob_end_clean(); $this->assertEquals('first-second-second', $result); + + /** Try to pass a param that is a PHP callable */ + $route = new Route('GET', '/path'); + $route + ->param('x', '', new Text(200), 'x param', true, ['first', 'second']) + ->action(function ($x) { + echo $x; + }); + + $request = new UtopiaRequestTest(); + $request::_setParams(['x' => 'count']); + $this->app->execute($route, $request, new Response()); + $result = \ob_get_contents(); + \ob_end_clean(); + + $this->assertEquals('count', $result); } public function testCanExecuteRoute(): void From d333880cf4903a121dee316cb0e122b7cd4cfebb Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Thu, 21 Mar 2024 18:07:23 +0000 Subject: [PATCH 011/103] chore: linter --- src/App.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/App.php b/src/App.php index dd9cb926..1458838c 100755 --- a/src/App.php +++ b/src/App.php @@ -607,7 +607,7 @@ protected function getArguments(Hook $hook, array $values, array $requestParams) $arg = $param['default']; } } - + $value = $existsInValues ? $values[$key] : $arg; if (!$param['skipValidation']) { From 9f07728c7f8384c49fddb15ad19fd97cae8b4c3f Mon Sep 17 00:00:00 2001 From: Khushboo Verma <43381712+vermakhushboo@users.noreply.github.com> Date: Thu, 1 Aug 2024 13:10:41 +0530 Subject: [PATCH 012/103] Added AnyOf Validator --- src/Validator/AnyOf.php | 87 ++++++++++++++++++++++++++++++ tests/Validator/MultipleOfTest.php | 27 ++++++++++ tests/Validator/MultipleTest.php | 35 ------------ 3 files changed, 114 insertions(+), 35 deletions(-) create mode 100644 src/Validator/AnyOf.php create mode 100644 tests/Validator/MultipleOfTest.php delete mode 100644 tests/Validator/MultipleTest.php diff --git a/src/Validator/AnyOf.php b/src/Validator/AnyOf.php new file mode 100644 index 00000000..115f0671 --- /dev/null +++ b/src/Validator/AnyOf.php @@ -0,0 +1,87 @@ + $validators + */ + public function __construct(protected array $validators, protected string $type = self::TYPE_MIXED) + { + } + + /** + * Get Description + * + * Returns validator description + * + * @return string + */ + public function getDescription(): string + { + if (!(\is_null($this->failedRule))) { + $description = $this->failedRule->getDescription(); + } else { + $description = $this->validators[0]->getDescription(); + } + + return $description; + } + + /** + * Is valid + * + * Validation will pass when all rules are valid if only one of the rules is invalid validation will fail. + * + * @param mixed $value + * @return bool + */ + public function isValid(mixed $value): bool + { + foreach ($this->validators as $rule) { + $valid = $rule->isValid($value); + + $this->failedRule = $rule; + + if ($valid) { + return true; + } + } + + return false; + } + + /** + * Get Type + * + * Returns validator type. + * + * @return string + */ + public function getType(): string + { + return $this->type; + } + + /** + * Is array + * + * Function will return true if object is array. + * + * @return bool + */ + public function isArray(): bool + { + return true; + } +} diff --git a/tests/Validator/MultipleOfTest.php b/tests/Validator/MultipleOfTest.php new file mode 100644 index 00000000..f5f51d42 --- /dev/null +++ b/tests/Validator/MultipleOfTest.php @@ -0,0 +1,27 @@ +assertTrue($vaidator->isValid($validTextValidUrl)); + $this->assertTrue($vaidator->isValid($validTextInvalidUrl)); + $this->assertTrue($vaidator->isValid($invalidTextValidUrl)); + $this->assertFalse($vaidator->isValid($invalidTextInvalidUrl)); + } +} diff --git a/tests/Validator/MultipleTest.php b/tests/Validator/MultipleTest.php deleted file mode 100644 index 8ef1ee59..00000000 --- a/tests/Validator/MultipleTest.php +++ /dev/null @@ -1,35 +0,0 @@ -validator = new Multiple([new Text(20), new URL()], Multiple::TYPE_STRING); - } - - public function testIsValid() - { - $this->assertEquals('string', $this->validator->getType()); - $this->assertEquals("1. Value must be a valid string and at least 1 chars and no longer than 20 chars \n2. Value must be a valid URL \n", $this->validator->getDescription()); - - // Valid URL but invalid text length - $this->assertFalse($this->validator->isValid('http://example.com/very-long-url')); - - // Valid text within length, but invalid URL - $this->assertFalse($this->validator->isValid('hello world')); - - // Both conditions satisfied - $this->assertTrue($this->validator->isValid('http://example.com')); - $this->assertTrue($this->validator->isValid('https://google.com')); - - // Neither condition satisfied - $this->assertFalse($this->validator->isValid('example.com/hello-world')); - $this->assertFalse($this->validator->isValid('')); - } -} From bfe39bdc95f495517c6fab5b033fcca6bcc4cde5 Mon Sep 17 00:00:00 2001 From: Khushboo Verma <43381712+vermakhushboo@users.noreply.github.com> Date: Thu, 1 Aug 2024 13:13:09 +0530 Subject: [PATCH 013/103] Re-add multiple test --- tests/Validator/MultipleTest.php | 35 ++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 tests/Validator/MultipleTest.php diff --git a/tests/Validator/MultipleTest.php b/tests/Validator/MultipleTest.php new file mode 100644 index 00000000..8ef1ee59 --- /dev/null +++ b/tests/Validator/MultipleTest.php @@ -0,0 +1,35 @@ +validator = new Multiple([new Text(20), new URL()], Multiple::TYPE_STRING); + } + + public function testIsValid() + { + $this->assertEquals('string', $this->validator->getType()); + $this->assertEquals("1. Value must be a valid string and at least 1 chars and no longer than 20 chars \n2. Value must be a valid URL \n", $this->validator->getDescription()); + + // Valid URL but invalid text length + $this->assertFalse($this->validator->isValid('http://example.com/very-long-url')); + + // Valid text within length, but invalid URL + $this->assertFalse($this->validator->isValid('hello world')); + + // Both conditions satisfied + $this->assertTrue($this->validator->isValid('http://example.com')); + $this->assertTrue($this->validator->isValid('https://google.com')); + + // Neither condition satisfied + $this->assertFalse($this->validator->isValid('example.com/hello-world')); + $this->assertFalse($this->validator->isValid('')); + } +} From 7b5a7a8dd4a28752f6d024d7a817af7fc98b5932 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Thu, 15 Aug 2024 12:01:47 +0000 Subject: [PATCH 014/103] Add getValidatosr to AnyOf --- composer.lock | 313 +++++++++++++++-------------- src/Validator/AnyOf.php | 12 ++ tests/Validator/MultipleOfTest.php | 4 + 3 files changed, 174 insertions(+), 155 deletions(-) diff --git a/composer.lock b/composer.lock index bbff81f8..ecf6f2c8 100644 --- a/composer.lock +++ b/composer.lock @@ -232,16 +232,16 @@ }, { "name": "laravel/pint", - "version": "v1.14.0", + "version": "v1.17.2", "source": { "type": "git", "url": "https://github.com/laravel/pint.git", - "reference": "6b127276e3f263f7bb17d5077e9e0269e61b2a0e" + "reference": "e8a88130a25e3f9d4d5785e6a1afca98268ab110" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/pint/zipball/6b127276e3f263f7bb17d5077e9e0269e61b2a0e", - "reference": "6b127276e3f263f7bb17d5077e9e0269e61b2a0e", + "url": "https://api.github.com/repos/laravel/pint/zipball/e8a88130a25e3f9d4d5785e6a1afca98268ab110", + "reference": "e8a88130a25e3f9d4d5785e6a1afca98268ab110", "shasum": "" }, "require": { @@ -252,13 +252,13 @@ "php": "^8.1.0" }, "require-dev": { - "friendsofphp/php-cs-fixer": "^3.49.0", - "illuminate/view": "^10.43.0", - "larastan/larastan": "^2.8.1", - "laravel-zero/framework": "^10.3.0", - "mockery/mockery": "^1.6.7", + "friendsofphp/php-cs-fixer": "^3.61.1", + "illuminate/view": "^10.48.18", + "larastan/larastan": "^2.9.8", + "laravel-zero/framework": "^10.4.0", + "mockery/mockery": "^1.6.12", "nunomaduro/termwind": "^1.15.1", - "pestphp/pest": "^2.33.6" + "pestphp/pest": "^2.35.0" }, "bin": [ "builds/pint" @@ -294,20 +294,20 @@ "issues": "https://github.com/laravel/pint/issues", "source": "https://github.com/laravel/pint" }, - "time": "2024-02-20T17:38:05+00:00" + "time": "2024-08-06T15:11:54+00:00" }, { "name": "myclabs/deep-copy", - "version": "1.11.1", + "version": "1.12.0", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c" + "reference": "3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", - "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c", + "reference": "3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c", "shasum": "" }, "require": { @@ -315,11 +315,12 @@ }, "conflict": { "doctrine/collections": "<1.6.8", - "doctrine/common": "<2.13.3 || >=3,<3.2.2" + "doctrine/common": "<2.13.3 || >=3 <3.2.2" }, "require-dev": { "doctrine/collections": "^1.6.8", "doctrine/common": "^2.13.3 || ^3.2.2", + "phpspec/prophecy": "^1.10", "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" }, "type": "library", @@ -345,7 +346,7 @@ ], "support": { "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.11.1" + "source": "https://github.com/myclabs/DeepCopy/tree/1.12.0" }, "funding": [ { @@ -353,20 +354,20 @@ "type": "tidelift" } ], - "time": "2023-03-08T13:26:56+00:00" + "time": "2024-06-12T14:39:25+00:00" }, { "name": "nikic/php-parser", - "version": "v5.0.2", + "version": "v5.1.0", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "139676794dc1e9231bf7bcd123cfc0c99182cb13" + "reference": "683130c2ff8c2739f4822ff7ac5c873ec529abd1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/139676794dc1e9231bf7bcd123cfc0c99182cb13", - "reference": "139676794dc1e9231bf7bcd123cfc0c99182cb13", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/683130c2ff8c2739f4822ff7ac5c873ec529abd1", + "reference": "683130c2ff8c2739f4822ff7ac5c873ec529abd1", "shasum": "" }, "require": { @@ -377,7 +378,7 @@ }, "require-dev": { "ircmaxell/php-yacc": "^0.0.7", - "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0" + "phpunit/phpunit": "^9.0" }, "bin": [ "bin/php-parse" @@ -409,9 +410,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v5.0.2" + "source": "https://github.com/nikic/PHP-Parser/tree/v5.1.0" }, - "time": "2024-03-05T20:51:40+00:00" + "time": "2024-07-01T20:03:41+00:00" }, { "name": "phar-io/manifest", @@ -635,16 +636,16 @@ }, { "name": "phpbench/phpbench", - "version": "1.2.15", + "version": "1.3.1", "source": { "type": "git", "url": "https://github.com/phpbench/phpbench.git", - "reference": "f7000319695cfad04a57fc64bf7ef7abdf4c437c" + "reference": "a3e1ef08d9d7736d43a7fbd444893d6a073c0ca0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpbench/phpbench/zipball/f7000319695cfad04a57fc64bf7ef7abdf4c437c", - "reference": "f7000319695cfad04a57fc64bf7ef7abdf4c437c", + "url": "https://api.github.com/repos/phpbench/phpbench/zipball/a3e1ef08d9d7736d43a7fbd444893d6a073c0ca0", + "reference": "a3e1ef08d9d7736d43a7fbd444893d6a073c0ca0", "shasum": "" }, "require": { @@ -656,29 +657,30 @@ "ext-spl": "*", "ext-tokenizer": "*", "php": "^8.1", - "phpbench/container": "^2.1", + "phpbench/container": "^2.2", "phpbench/dom": "~0.3.3", "psr/log": "^1.1 || ^2.0 || ^3.0", "seld/jsonlint": "^1.1", - "symfony/console": "^4.2 || ^5.0 || ^6.0 || ^7.0", - "symfony/filesystem": "^4.2 || ^5.0 || ^6.0 || ^7.0", - "symfony/finder": "^4.2 || ^5.0 || ^6.0 || ^7.0", - "symfony/options-resolver": "^4.2 || ^5.0 || ^6.0 || ^7.0", - "symfony/process": "^4.2 || ^5.0 || ^6.0 || ^7.0", + "symfony/console": "^6.1 || ^7.0", + "symfony/filesystem": "^6.1 || ^7.0", + "symfony/finder": "^6.1 || ^7.0", + "symfony/options-resolver": "^6.1 || ^7.0", + "symfony/process": "^6.1 || ^7.0", "webmozart/glob": "^4.6" }, "require-dev": { "dantleech/invoke": "^2.0", + "ergebnis/composer-normalize": "^2.39", "friendsofphp/php-cs-fixer": "^3.0", "jangregor/phpstan-prophecy": "^1.0", "phpspec/prophecy": "dev-master", "phpstan/extension-installer": "^1.1", "phpstan/phpstan": "^1.0", "phpstan/phpstan-phpunit": "^1.0", - "phpunit/phpunit": "^10.0", - "rector/rector": "^0.18.10", - "symfony/error-handler": "^5.2 || ^6.0 || ^7.0", - "symfony/var-dumper": "^4.0 || ^5.0 || ^6.0 || ^7.0" + "phpunit/phpunit": "^10.4", + "rector/rector": "^0.18.11 || ^1.0.0", + "symfony/error-handler": "^6.1 || ^7.0", + "symfony/var-dumper": "^6.1 || ^7.0" }, "suggest": { "ext-xdebug": "For Xdebug profiling extension." @@ -721,7 +723,7 @@ ], "support": { "issues": "https://github.com/phpbench/phpbench/issues", - "source": "https://github.com/phpbench/phpbench/tree/1.2.15" + "source": "https://github.com/phpbench/phpbench/tree/1.3.1" }, "funding": [ { @@ -729,20 +731,20 @@ "type": "github" } ], - "time": "2023-11-29T12:21:11+00:00" + "time": "2024-06-30T11:04:37+00:00" }, { "name": "phpstan/phpstan", - "version": "1.10.64", + "version": "1.11.10", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "fb9f270daffedcb5ff46275dcafe92538b1bc4bb" + "reference": "640410b32995914bde3eed26fa89552f9c2c082f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/fb9f270daffedcb5ff46275dcafe92538b1bc4bb", - "reference": "fb9f270daffedcb5ff46275dcafe92538b1bc4bb", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/640410b32995914bde3eed26fa89552f9c2c082f", + "reference": "640410b32995914bde3eed26fa89552f9c2c082f", "shasum": "" }, "require": { @@ -785,13 +787,9 @@ { "url": "https://github.com/phpstan", "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/phpstan/phpstan", - "type": "tidelift" } ], - "time": "2024-03-21T09:57:47+00:00" + "time": "2024-08-08T09:02:50+00:00" }, { "name": "phpunit/php-code-coverage", @@ -1114,45 +1112,45 @@ }, { "name": "phpunit/phpunit", - "version": "9.6.18", + "version": "9.6.20", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "32c2c2d6580b1d8ab3c10b1e9e4dc263cc69bb04" + "reference": "49d7820565836236411f5dc002d16dd689cde42f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/32c2c2d6580b1d8ab3c10b1e9e4dc263cc69bb04", - "reference": "32c2c2d6580b1d8ab3c10b1e9e4dc263cc69bb04", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/49d7820565836236411f5dc002d16dd689cde42f", + "reference": "49d7820565836236411f5dc002d16dd689cde42f", "shasum": "" }, "require": { - "doctrine/instantiator": "^1.3.1 || ^2", + "doctrine/instantiator": "^1.5.0 || ^2", "ext-dom": "*", "ext-json": "*", "ext-libxml": "*", "ext-mbstring": "*", "ext-xml": "*", "ext-xmlwriter": "*", - "myclabs/deep-copy": "^1.10.1", - "phar-io/manifest": "^2.0.3", - "phar-io/version": "^3.0.2", + "myclabs/deep-copy": "^1.12.0", + "phar-io/manifest": "^2.0.4", + "phar-io/version": "^3.2.1", "php": ">=7.3", - "phpunit/php-code-coverage": "^9.2.28", - "phpunit/php-file-iterator": "^3.0.5", + "phpunit/php-code-coverage": "^9.2.31", + "phpunit/php-file-iterator": "^3.0.6", "phpunit/php-invoker": "^3.1.1", - "phpunit/php-text-template": "^2.0.3", - "phpunit/php-timer": "^5.0.2", - "sebastian/cli-parser": "^1.0.1", - "sebastian/code-unit": "^1.0.6", + "phpunit/php-text-template": "^2.0.4", + "phpunit/php-timer": "^5.0.3", + "sebastian/cli-parser": "^1.0.2", + "sebastian/code-unit": "^1.0.8", "sebastian/comparator": "^4.0.8", - "sebastian/diff": "^4.0.3", - "sebastian/environment": "^5.1.3", - "sebastian/exporter": "^4.0.5", - "sebastian/global-state": "^5.0.1", - "sebastian/object-enumerator": "^4.0.3", - "sebastian/resource-operations": "^3.0.3", - "sebastian/type": "^3.2", + "sebastian/diff": "^4.0.6", + "sebastian/environment": "^5.1.5", + "sebastian/exporter": "^4.0.6", + "sebastian/global-state": "^5.0.7", + "sebastian/object-enumerator": "^4.0.4", + "sebastian/resource-operations": "^3.0.4", + "sebastian/type": "^3.2.1", "sebastian/version": "^3.0.2" }, "suggest": { @@ -1197,7 +1195,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.18" + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.20" }, "funding": [ { @@ -1213,7 +1211,7 @@ "type": "tidelift" } ], - "time": "2024-03-21T12:07:32+00:00" + "time": "2024-07-10T11:45:39+00:00" }, { "name": "psr/cache", @@ -2332,23 +2330,23 @@ }, { "name": "seld/jsonlint", - "version": "1.10.2", + "version": "1.11.0", "source": { "type": "git", "url": "https://github.com/Seldaek/jsonlint.git", - "reference": "9bb7db07b5d66d90f6ebf542f09fc67d800e5259" + "reference": "1748aaf847fc731cfad7725aec413ee46f0cc3a2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Seldaek/jsonlint/zipball/9bb7db07b5d66d90f6ebf542f09fc67d800e5259", - "reference": "9bb7db07b5d66d90f6ebf542f09fc67d800e5259", + "url": "https://api.github.com/repos/Seldaek/jsonlint/zipball/1748aaf847fc731cfad7725aec413ee46f0cc3a2", + "reference": "1748aaf847fc731cfad7725aec413ee46f0cc3a2", "shasum": "" }, "require": { "php": "^5.3 || ^7.0 || ^8.0" }, "require-dev": { - "phpstan/phpstan": "^1.5", + "phpstan/phpstan": "^1.11", "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0 || ^8.5.13" }, "bin": [ @@ -2380,7 +2378,7 @@ ], "support": { "issues": "https://github.com/Seldaek/jsonlint/issues", - "source": "https://github.com/Seldaek/jsonlint/tree/1.10.2" + "source": "https://github.com/Seldaek/jsonlint/tree/1.11.0" }, "funding": [ { @@ -2392,20 +2390,20 @@ "type": "tidelift" } ], - "time": "2024-02-07T12:57:50+00:00" + "time": "2024-07-11T14:55:45+00:00" }, { "name": "symfony/console", - "version": "v7.0.4", + "version": "v7.1.3", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "6b099f3306f7c9c2d2786ed736d0026b2903205f" + "reference": "cb1dcb30ebc7005c29864ee78adb47b5fb7c3cd9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/6b099f3306f7c9c2d2786ed736d0026b2903205f", - "reference": "6b099f3306f7c9c2d2786ed736d0026b2903205f", + "url": "https://api.github.com/repos/symfony/console/zipball/cb1dcb30ebc7005c29864ee78adb47b5fb7c3cd9", + "reference": "cb1dcb30ebc7005c29864ee78adb47b5fb7c3cd9", "shasum": "" }, "require": { @@ -2469,7 +2467,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v7.0.4" + "source": "https://github.com/symfony/console/tree/v7.1.3" }, "funding": [ { @@ -2485,20 +2483,20 @@ "type": "tidelift" } ], - "time": "2024-02-22T20:27:20+00:00" + "time": "2024-07-26T12:41:01+00:00" }, { "name": "symfony/deprecation-contracts", - "version": "v3.4.0", + "version": "v3.5.0", "source": { "type": "git", "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "7c3aff79d10325257a001fcf92d991f24fc967cf" + "reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/7c3aff79d10325257a001fcf92d991f24fc967cf", - "reference": "7c3aff79d10325257a001fcf92d991f24fc967cf", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1", + "reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1", "shasum": "" }, "require": { @@ -2507,7 +2505,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "3.4-dev" + "dev-main": "3.5-dev" }, "thanks": { "name": "symfony/contracts", @@ -2536,7 +2534,7 @@ "description": "A generic function and convention to trigger deprecation notices", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v3.4.0" + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.5.0" }, "funding": [ { @@ -2552,20 +2550,20 @@ "type": "tidelift" } ], - "time": "2023-05-23T14:45:45+00:00" + "time": "2024-04-18T09:32:20+00:00" }, { "name": "symfony/filesystem", - "version": "v7.0.3", + "version": "v7.1.2", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "2890e3a825bc0c0558526c04499c13f83e1b6b12" + "reference": "92a91985250c251de9b947a14bb2c9390b1a562c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/2890e3a825bc0c0558526c04499c13f83e1b6b12", - "reference": "2890e3a825bc0c0558526c04499c13f83e1b6b12", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/92a91985250c251de9b947a14bb2c9390b1a562c", + "reference": "92a91985250c251de9b947a14bb2c9390b1a562c", "shasum": "" }, "require": { @@ -2573,6 +2571,9 @@ "symfony/polyfill-ctype": "~1.8", "symfony/polyfill-mbstring": "~1.8" }, + "require-dev": { + "symfony/process": "^6.4|^7.0" + }, "type": "library", "autoload": { "psr-4": { @@ -2599,7 +2600,7 @@ "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/filesystem/tree/v7.0.3" + "source": "https://github.com/symfony/filesystem/tree/v7.1.2" }, "funding": [ { @@ -2615,20 +2616,20 @@ "type": "tidelift" } ], - "time": "2024-01-23T15:02:46+00:00" + "time": "2024-06-28T10:03:55+00:00" }, { "name": "symfony/finder", - "version": "v7.0.0", + "version": "v7.1.3", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "6e5688d69f7cfc4ed4a511e96007e06c2d34ce56" + "reference": "717c6329886f32dc65e27461f80f2a465412fdca" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/6e5688d69f7cfc4ed4a511e96007e06c2d34ce56", - "reference": "6e5688d69f7cfc4ed4a511e96007e06c2d34ce56", + "url": "https://api.github.com/repos/symfony/finder/zipball/717c6329886f32dc65e27461f80f2a465412fdca", + "reference": "717c6329886f32dc65e27461f80f2a465412fdca", "shasum": "" }, "require": { @@ -2663,7 +2664,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v7.0.0" + "source": "https://github.com/symfony/finder/tree/v7.1.3" }, "funding": [ { @@ -2679,20 +2680,20 @@ "type": "tidelift" } ], - "time": "2023-10-31T17:59:56+00:00" + "time": "2024-07-24T07:08:44+00:00" }, { "name": "symfony/options-resolver", - "version": "v7.0.0", + "version": "v7.1.1", "source": { "type": "git", "url": "https://github.com/symfony/options-resolver.git", - "reference": "700ff4096e346f54cb628ea650767c8130f1001f" + "reference": "47aa818121ed3950acd2b58d1d37d08a94f9bf55" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/options-resolver/zipball/700ff4096e346f54cb628ea650767c8130f1001f", - "reference": "700ff4096e346f54cb628ea650767c8130f1001f", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/47aa818121ed3950acd2b58d1d37d08a94f9bf55", + "reference": "47aa818121ed3950acd2b58d1d37d08a94f9bf55", "shasum": "" }, "require": { @@ -2730,7 +2731,7 @@ "options" ], "support": { - "source": "https://github.com/symfony/options-resolver/tree/v7.0.0" + "source": "https://github.com/symfony/options-resolver/tree/v7.1.1" }, "funding": [ { @@ -2746,20 +2747,20 @@ "type": "tidelift" } ], - "time": "2023-08-08T10:20:21+00:00" + "time": "2024-05-31T14:57:53+00:00" }, { "name": "symfony/polyfill-ctype", - "version": "v1.29.0", + "version": "v1.30.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "ef4d7e442ca910c4764bce785146269b30cb5fc4" + "reference": "0424dff1c58f028c451efff2045f5d92410bd540" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/ef4d7e442ca910c4764bce785146269b30cb5fc4", - "reference": "ef4d7e442ca910c4764bce785146269b30cb5fc4", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/0424dff1c58f028c451efff2045f5d92410bd540", + "reference": "0424dff1c58f028c451efff2045f5d92410bd540", "shasum": "" }, "require": { @@ -2809,7 +2810,7 @@ "portable" ], "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.29.0" + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.30.0" }, "funding": [ { @@ -2825,20 +2826,20 @@ "type": "tidelift" } ], - "time": "2024-01-29T20:11:03+00:00" + "time": "2024-05-31T15:07:36+00:00" }, { "name": "symfony/polyfill-intl-grapheme", - "version": "v1.29.0", + "version": "v1.30.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "32a9da87d7b3245e09ac426c83d334ae9f06f80f" + "reference": "64647a7c30b2283f5d49b874d84a18fc22054b7a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/32a9da87d7b3245e09ac426c83d334ae9f06f80f", - "reference": "32a9da87d7b3245e09ac426c83d334ae9f06f80f", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/64647a7c30b2283f5d49b874d84a18fc22054b7a", + "reference": "64647a7c30b2283f5d49b874d84a18fc22054b7a", "shasum": "" }, "require": { @@ -2887,7 +2888,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.29.0" + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.30.0" }, "funding": [ { @@ -2903,20 +2904,20 @@ "type": "tidelift" } ], - "time": "2024-01-29T20:11:03+00:00" + "time": "2024-05-31T15:07:36+00:00" }, { "name": "symfony/polyfill-intl-normalizer", - "version": "v1.29.0", + "version": "v1.30.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-normalizer.git", - "reference": "bc45c394692b948b4d383a08d7753968bed9a83d" + "reference": "a95281b0be0d9ab48050ebd988b967875cdb9fdb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/bc45c394692b948b4d383a08d7753968bed9a83d", - "reference": "bc45c394692b948b4d383a08d7753968bed9a83d", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/a95281b0be0d9ab48050ebd988b967875cdb9fdb", + "reference": "a95281b0be0d9ab48050ebd988b967875cdb9fdb", "shasum": "" }, "require": { @@ -2968,7 +2969,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.29.0" + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.30.0" }, "funding": [ { @@ -2984,20 +2985,20 @@ "type": "tidelift" } ], - "time": "2024-01-29T20:11:03+00:00" + "time": "2024-05-31T15:07:36+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.29.0", + "version": "v1.30.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "9773676c8a1bb1f8d4340a62efe641cf76eda7ec" + "reference": "fd22ab50000ef01661e2a31d850ebaa297f8e03c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9773676c8a1bb1f8d4340a62efe641cf76eda7ec", - "reference": "9773676c8a1bb1f8d4340a62efe641cf76eda7ec", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/fd22ab50000ef01661e2a31d850ebaa297f8e03c", + "reference": "fd22ab50000ef01661e2a31d850ebaa297f8e03c", "shasum": "" }, "require": { @@ -3048,7 +3049,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.29.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.30.0" }, "funding": [ { @@ -3064,20 +3065,20 @@ "type": "tidelift" } ], - "time": "2024-01-29T20:11:03+00:00" + "time": "2024-06-19T12:30:46+00:00" }, { "name": "symfony/process", - "version": "v7.0.4", + "version": "v7.1.3", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "0e7727191c3b71ebec6d529fa0e50a01ca5679e9" + "reference": "7f2f542c668ad6c313dc4a5e9c3321f733197eca" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/0e7727191c3b71ebec6d529fa0e50a01ca5679e9", - "reference": "0e7727191c3b71ebec6d529fa0e50a01ca5679e9", + "url": "https://api.github.com/repos/symfony/process/zipball/7f2f542c668ad6c313dc4a5e9c3321f733197eca", + "reference": "7f2f542c668ad6c313dc4a5e9c3321f733197eca", "shasum": "" }, "require": { @@ -3109,7 +3110,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v7.0.4" + "source": "https://github.com/symfony/process/tree/v7.1.3" }, "funding": [ { @@ -3125,25 +3126,26 @@ "type": "tidelift" } ], - "time": "2024-02-22T20:27:20+00:00" + "time": "2024-07-26T12:44:47+00:00" }, { "name": "symfony/service-contracts", - "version": "v3.4.1", + "version": "v3.5.0", "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", - "reference": "fe07cbc8d837f60caf7018068e350cc5163681a0" + "reference": "bd1d9e59a81d8fa4acdcea3f617c581f7475a80f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/fe07cbc8d837f60caf7018068e350cc5163681a0", - "reference": "fe07cbc8d837f60caf7018068e350cc5163681a0", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/bd1d9e59a81d8fa4acdcea3f617c581f7475a80f", + "reference": "bd1d9e59a81d8fa4acdcea3f617c581f7475a80f", "shasum": "" }, "require": { "php": ">=8.1", - "psr/container": "^1.1|^2.0" + "psr/container": "^1.1|^2.0", + "symfony/deprecation-contracts": "^2.5|^3" }, "conflict": { "ext-psr": "<1.1|>=2" @@ -3151,7 +3153,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "3.4-dev" + "dev-main": "3.5-dev" }, "thanks": { "name": "symfony/contracts", @@ -3191,7 +3193,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/service-contracts/tree/v3.4.1" + "source": "https://github.com/symfony/service-contracts/tree/v3.5.0" }, "funding": [ { @@ -3207,20 +3209,20 @@ "type": "tidelift" } ], - "time": "2023-12-26T14:02:43+00:00" + "time": "2024-04-18T09:32:20+00:00" }, { "name": "symfony/string", - "version": "v7.0.4", + "version": "v7.1.3", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "f5832521b998b0bec40bee688ad5de98d4cf111b" + "reference": "ea272a882be7f20cad58d5d78c215001617b7f07" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/f5832521b998b0bec40bee688ad5de98d4cf111b", - "reference": "f5832521b998b0bec40bee688ad5de98d4cf111b", + "url": "https://api.github.com/repos/symfony/string/zipball/ea272a882be7f20cad58d5d78c215001617b7f07", + "reference": "ea272a882be7f20cad58d5d78c215001617b7f07", "shasum": "" }, "require": { @@ -3234,6 +3236,7 @@ "symfony/translation-contracts": "<2.5" }, "require-dev": { + "symfony/emoji": "^7.1", "symfony/error-handler": "^6.4|^7.0", "symfony/http-client": "^6.4|^7.0", "symfony/intl": "^6.4|^7.0", @@ -3277,7 +3280,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v7.0.4" + "source": "https://github.com/symfony/string/tree/v7.1.3" }, "funding": [ { @@ -3293,7 +3296,7 @@ "type": "tidelift" } ], - "time": "2024-02-01T13:17:36+00:00" + "time": "2024-07-22T10:25:37+00:00" }, { "name": "theseer/tokenizer", diff --git a/src/Validator/AnyOf.php b/src/Validator/AnyOf.php index 115f0671..35ebefeb 100644 --- a/src/Validator/AnyOf.php +++ b/src/Validator/AnyOf.php @@ -20,6 +20,18 @@ public function __construct(protected array $validators, protected string $type { } + /** + * Get Validators + * + * Returns validators array + * + * @return array + */ + public function getValidators(): array + { + return $this->validators; + } + /** * Get Description * diff --git a/tests/Validator/MultipleOfTest.php b/tests/Validator/MultipleOfTest.php index f5f51d42..9b31b031 100644 --- a/tests/Validator/MultipleOfTest.php +++ b/tests/Validator/MultipleOfTest.php @@ -23,5 +23,9 @@ public function testIsValid() $this->assertTrue($vaidator->isValid($validTextInvalidUrl)); $this->assertTrue($vaidator->isValid($invalidTextValidUrl)); $this->assertFalse($vaidator->isValid($invalidTextInvalidUrl)); + + $this->assertCount(2, $vaidator->getValidators()); + $this->assertEquals("Utopia\Validator\Text", \get_class($vaidator->getValidators()[0])); + $this->assertEquals("Utopia\Validator\URL", \get_class($vaidator->getValidators()[1])); } } From 3cf34e0bf000054b3c2f817c0ebde557a59b9916 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Thu, 15 Aug 2024 12:03:07 +0000 Subject: [PATCH 015/103] Fix CI/CD --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index cc41aa6f..4bc082d1 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -16,7 +16,7 @@ jobs: php-version: '8.0' - name: Setup Docker - run: docker-compose up -d --build + run: docker compose up -d --build - name: Wait for Server to be ready run: sleep 10 From 9644b0c27acfd35dd32898169bf904aeece9c676 Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Wed, 23 Oct 2024 11:44:02 +0100 Subject: [PATCH 016/103] feat: compression --- composer.json | 3 +- composer.lock | 284 ++++++++------ src/App.php | 29 ++ src/Http/Response.php | 837 ++++++++++++++++++++++++++++++++++++++++++ src/Response.php | 98 +++-- 5 files changed, 1110 insertions(+), 141 deletions(-) create mode 100644 src/Http/Response.php diff --git a/composer.json b/composer.json index fe84d0d0..d856b786 100644 --- a/composer.json +++ b/composer.json @@ -22,7 +22,8 @@ "bench": "vendor/bin/phpbench run --report=benchmark" }, "require": { - "php": ">=8.0" + "php": ">=8.0", + "utopia-php/compression": "0.1.*" }, "require-dev": { "phpunit/phpunit": "^9.5.25", diff --git a/composer.lock b/composer.lock index ecf6f2c8..af10d95d 100644 --- a/composer.lock +++ b/composer.lock @@ -4,21 +4,68 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "d41ea47cadd897ad4053410229874733", - "packages": [], + "content-hash": "29435f73c84b4ebe3f18aa8bcd7e008e", + "packages": [ + { + "name": "utopia-php/compression", + "version": "0.1.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/compression.git", + "reference": "8c6d9bcb5b0972faa27e5bf70923c20403aaf25c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/compression/zipball/8c6d9bcb5b0972faa27e5bf70923c20403aaf25c", + "reference": "8c6d9bcb5b0972faa27e5bf70923c20403aaf25c", + "shasum": "" + }, + "require": { + "php": ">=8.0" + }, + "require-dev": { + "laravel/pint": "1.2.*", + "phpunit/phpunit": "^9.3", + "vimeo/psalm": "4.0.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Compression\\": "src/Compression" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A simple Compression library to handle file compression", + "keywords": [ + "compression", + "framework", + "php", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/compression/issues", + "source": "https://github.com/utopia-php/compression/tree/0.1.0" + }, + "time": "2024-10-23T10:17:46+00:00" + } + ], "packages-dev": [ { "name": "doctrine/annotations", - "version": "2.0.1", + "version": "2.0.2", "source": { "type": "git", "url": "https://github.com/doctrine/annotations.git", - "reference": "e157ef3f3124bbf6fe7ce0ffd109e8a8ef284e7f" + "reference": "901c2ee5d26eb64ff43c47976e114bf00843acf7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/annotations/zipball/e157ef3f3124bbf6fe7ce0ffd109e8a8ef284e7f", - "reference": "e157ef3f3124bbf6fe7ce0ffd109e8a8ef284e7f", + "url": "https://api.github.com/repos/doctrine/annotations/zipball/901c2ee5d26eb64ff43c47976e114bf00843acf7", + "reference": "901c2ee5d26eb64ff43c47976e114bf00843acf7", "shasum": "" }, "require": { @@ -30,10 +77,10 @@ "require-dev": { "doctrine/cache": "^2.0", "doctrine/coding-standard": "^10", - "phpstan/phpstan": "^1.8.0", + "phpstan/phpstan": "^1.10.28", "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", - "symfony/cache": "^5.4 || ^6", - "vimeo/psalm": "^4.10" + "symfony/cache": "^5.4 || ^6.4 || ^7", + "vimeo/psalm": "^4.30 || ^5.14" }, "suggest": { "php": "PHP 8.0 or higher comes with attributes, a native replacement for annotations" @@ -79,9 +126,9 @@ ], "support": { "issues": "https://github.com/doctrine/annotations/issues", - "source": "https://github.com/doctrine/annotations/tree/2.0.1" + "source": "https://github.com/doctrine/annotations/tree/2.0.2" }, - "time": "2023-02-02T22:02:53+00:00" + "time": "2024-09-05T10:17:24+00:00" }, { "name": "doctrine/instantiator", @@ -232,16 +279,16 @@ }, { "name": "laravel/pint", - "version": "v1.17.2", + "version": "v1.18.1", "source": { "type": "git", "url": "https://github.com/laravel/pint.git", - "reference": "e8a88130a25e3f9d4d5785e6a1afca98268ab110" + "reference": "35c00c05ec43e6b46d295efc0f4386ceb30d50d9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/pint/zipball/e8a88130a25e3f9d4d5785e6a1afca98268ab110", - "reference": "e8a88130a25e3f9d4d5785e6a1afca98268ab110", + "url": "https://api.github.com/repos/laravel/pint/zipball/35c00c05ec43e6b46d295efc0f4386ceb30d50d9", + "reference": "35c00c05ec43e6b46d295efc0f4386ceb30d50d9", "shasum": "" }, "require": { @@ -252,13 +299,13 @@ "php": "^8.1.0" }, "require-dev": { - "friendsofphp/php-cs-fixer": "^3.61.1", - "illuminate/view": "^10.48.18", + "friendsofphp/php-cs-fixer": "^3.64.0", + "illuminate/view": "^10.48.20", "larastan/larastan": "^2.9.8", "laravel-zero/framework": "^10.4.0", "mockery/mockery": "^1.6.12", "nunomaduro/termwind": "^1.15.1", - "pestphp/pest": "^2.35.0" + "pestphp/pest": "^2.35.1" }, "bin": [ "builds/pint" @@ -294,7 +341,7 @@ "issues": "https://github.com/laravel/pint/issues", "source": "https://github.com/laravel/pint" }, - "time": "2024-08-06T15:11:54+00:00" + "time": "2024-09-24T17:22:50+00:00" }, { "name": "myclabs/deep-copy", @@ -358,16 +405,16 @@ }, { "name": "nikic/php-parser", - "version": "v5.1.0", + "version": "v5.3.1", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "683130c2ff8c2739f4822ff7ac5c873ec529abd1" + "reference": "8eea230464783aa9671db8eea6f8c6ac5285794b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/683130c2ff8c2739f4822ff7ac5c873ec529abd1", - "reference": "683130c2ff8c2739f4822ff7ac5c873ec529abd1", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/8eea230464783aa9671db8eea6f8c6ac5285794b", + "reference": "8eea230464783aa9671db8eea6f8c6ac5285794b", "shasum": "" }, "require": { @@ -410,9 +457,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v5.1.0" + "source": "https://github.com/nikic/PHP-Parser/tree/v5.3.1" }, - "time": "2024-07-01T20:03:41+00:00" + "time": "2024-10-08T18:51:32+00:00" }, { "name": "phar-io/manifest", @@ -632,6 +679,7 @@ "issues": "https://github.com/phpbench/dom/issues", "source": "https://github.com/phpbench/dom/tree/0.3.3" }, + "abandoned": true, "time": "2023-03-06T23:46:57+00:00" }, { @@ -735,16 +783,16 @@ }, { "name": "phpstan/phpstan", - "version": "1.11.10", + "version": "1.12.7", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "640410b32995914bde3eed26fa89552f9c2c082f" + "reference": "dc2b9976bd8b0f84ec9b0e50cc35378551de7af0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/640410b32995914bde3eed26fa89552f9c2c082f", - "reference": "640410b32995914bde3eed26fa89552f9c2c082f", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/dc2b9976bd8b0f84ec9b0e50cc35378551de7af0", + "reference": "dc2b9976bd8b0f84ec9b0e50cc35378551de7af0", "shasum": "" }, "require": { @@ -789,39 +837,39 @@ "type": "github" } ], - "time": "2024-08-08T09:02:50+00:00" + "time": "2024-10-18T11:12:07+00:00" }, { "name": "phpunit/php-code-coverage", - "version": "9.2.31", + "version": "9.2.32", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "48c34b5d8d983006bd2adc2d0de92963b9155965" + "reference": "85402a822d1ecf1db1096959413d35e1c37cf1a5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/48c34b5d8d983006bd2adc2d0de92963b9155965", - "reference": "48c34b5d8d983006bd2adc2d0de92963b9155965", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/85402a822d1ecf1db1096959413d35e1c37cf1a5", + "reference": "85402a822d1ecf1db1096959413d35e1c37cf1a5", "shasum": "" }, "require": { "ext-dom": "*", "ext-libxml": "*", "ext-xmlwriter": "*", - "nikic/php-parser": "^4.18 || ^5.0", + "nikic/php-parser": "^4.19.1 || ^5.1.0", "php": ">=7.3", - "phpunit/php-file-iterator": "^3.0.3", - "phpunit/php-text-template": "^2.0.2", - "sebastian/code-unit-reverse-lookup": "^2.0.2", - "sebastian/complexity": "^2.0", - "sebastian/environment": "^5.1.2", - "sebastian/lines-of-code": "^1.0.3", - "sebastian/version": "^3.0.1", - "theseer/tokenizer": "^1.2.0" + "phpunit/php-file-iterator": "^3.0.6", + "phpunit/php-text-template": "^2.0.4", + "sebastian/code-unit-reverse-lookup": "^2.0.3", + "sebastian/complexity": "^2.0.3", + "sebastian/environment": "^5.1.5", + "sebastian/lines-of-code": "^1.0.4", + "sebastian/version": "^3.0.2", + "theseer/tokenizer": "^1.2.3" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^9.6" }, "suggest": { "ext-pcov": "PHP extension that provides line coverage", @@ -830,7 +878,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "9.2-dev" + "dev-main": "9.2.x-dev" } }, "autoload": { @@ -859,7 +907,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.31" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.32" }, "funding": [ { @@ -867,7 +915,7 @@ "type": "github" } ], - "time": "2024-03-02T06:37:42+00:00" + "time": "2024-08-22T04:23:01+00:00" }, { "name": "phpunit/php-file-iterator", @@ -1112,16 +1160,16 @@ }, { "name": "phpunit/phpunit", - "version": "9.6.20", + "version": "9.6.21", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "49d7820565836236411f5dc002d16dd689cde42f" + "reference": "de6abf3b6f8dd955fac3caad3af7a9504e8c2ffa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/49d7820565836236411f5dc002d16dd689cde42f", - "reference": "49d7820565836236411f5dc002d16dd689cde42f", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/de6abf3b6f8dd955fac3caad3af7a9504e8c2ffa", + "reference": "de6abf3b6f8dd955fac3caad3af7a9504e8c2ffa", "shasum": "" }, "require": { @@ -1136,7 +1184,7 @@ "phar-io/manifest": "^2.0.4", "phar-io/version": "^3.2.1", "php": ">=7.3", - "phpunit/php-code-coverage": "^9.2.31", + "phpunit/php-code-coverage": "^9.2.32", "phpunit/php-file-iterator": "^3.0.6", "phpunit/php-invoker": "^3.1.1", "phpunit/php-text-template": "^2.0.4", @@ -1195,7 +1243,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.20" + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.21" }, "funding": [ { @@ -1211,7 +1259,7 @@ "type": "tidelift" } ], - "time": "2024-07-10T11:45:39+00:00" + "time": "2024-09-19T10:50:18+00:00" }, { "name": "psr/cache", @@ -1317,16 +1365,16 @@ }, { "name": "psr/log", - "version": "3.0.0", + "version": "3.0.2", "source": { "type": "git", "url": "https://github.com/php-fig/log.git", - "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001" + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/fe5ea303b0887d5caefd3d431c3e61ad47037001", - "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001", + "url": "https://api.github.com/repos/php-fig/log/zipball/f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", "shasum": "" }, "require": { @@ -1361,9 +1409,9 @@ "psr-3" ], "support": { - "source": "https://github.com/php-fig/log/tree/3.0.0" + "source": "https://github.com/php-fig/log/tree/3.0.2" }, - "time": "2021-07-14T16:46:02+00:00" + "time": "2024-09-11T13:17:53+00:00" }, { "name": "sebastian/cli-parser", @@ -2394,16 +2442,16 @@ }, { "name": "symfony/console", - "version": "v7.1.3", + "version": "v7.1.5", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "cb1dcb30ebc7005c29864ee78adb47b5fb7c3cd9" + "reference": "0fa539d12b3ccf068a722bbbffa07ca7079af9ee" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/cb1dcb30ebc7005c29864ee78adb47b5fb7c3cd9", - "reference": "cb1dcb30ebc7005c29864ee78adb47b5fb7c3cd9", + "url": "https://api.github.com/repos/symfony/console/zipball/0fa539d12b3ccf068a722bbbffa07ca7079af9ee", + "reference": "0fa539d12b3ccf068a722bbbffa07ca7079af9ee", "shasum": "" }, "require": { @@ -2467,7 +2515,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v7.1.3" + "source": "https://github.com/symfony/console/tree/v7.1.5" }, "funding": [ { @@ -2483,7 +2531,7 @@ "type": "tidelift" } ], - "time": "2024-07-26T12:41:01+00:00" + "time": "2024-09-20T08:28:38+00:00" }, { "name": "symfony/deprecation-contracts", @@ -2554,16 +2602,16 @@ }, { "name": "symfony/filesystem", - "version": "v7.1.2", + "version": "v7.1.5", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "92a91985250c251de9b947a14bb2c9390b1a562c" + "reference": "61fe0566189bf32e8cfee78335d8776f64a66f5a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/92a91985250c251de9b947a14bb2c9390b1a562c", - "reference": "92a91985250c251de9b947a14bb2c9390b1a562c", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/61fe0566189bf32e8cfee78335d8776f64a66f5a", + "reference": "61fe0566189bf32e8cfee78335d8776f64a66f5a", "shasum": "" }, "require": { @@ -2600,7 +2648,7 @@ "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/filesystem/tree/v7.1.2" + "source": "https://github.com/symfony/filesystem/tree/v7.1.5" }, "funding": [ { @@ -2616,20 +2664,20 @@ "type": "tidelift" } ], - "time": "2024-06-28T10:03:55+00:00" + "time": "2024-09-17T09:16:35+00:00" }, { "name": "symfony/finder", - "version": "v7.1.3", + "version": "v7.1.4", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "717c6329886f32dc65e27461f80f2a465412fdca" + "reference": "d95bbf319f7d052082fb7af147e0f835a695e823" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/717c6329886f32dc65e27461f80f2a465412fdca", - "reference": "717c6329886f32dc65e27461f80f2a465412fdca", + "url": "https://api.github.com/repos/symfony/finder/zipball/d95bbf319f7d052082fb7af147e0f835a695e823", + "reference": "d95bbf319f7d052082fb7af147e0f835a695e823", "shasum": "" }, "require": { @@ -2664,7 +2712,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v7.1.3" + "source": "https://github.com/symfony/finder/tree/v7.1.4" }, "funding": [ { @@ -2680,7 +2728,7 @@ "type": "tidelift" } ], - "time": "2024-07-24T07:08:44+00:00" + "time": "2024-08-13T14:28:19+00:00" }, { "name": "symfony/options-resolver", @@ -2751,20 +2799,20 @@ }, { "name": "symfony/polyfill-ctype", - "version": "v1.30.0", + "version": "v1.31.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "0424dff1c58f028c451efff2045f5d92410bd540" + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/0424dff1c58f028c451efff2045f5d92410bd540", - "reference": "0424dff1c58f028c451efff2045f5d92410bd540", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638", + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "provide": { "ext-ctype": "*" @@ -2810,7 +2858,7 @@ "portable" ], "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.30.0" + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.31.0" }, "funding": [ { @@ -2826,24 +2874,24 @@ "type": "tidelift" } ], - "time": "2024-05-31T15:07:36+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/polyfill-intl-grapheme", - "version": "v1.30.0", + "version": "v1.31.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "64647a7c30b2283f5d49b874d84a18fc22054b7a" + "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/64647a7c30b2283f5d49b874d84a18fc22054b7a", - "reference": "64647a7c30b2283f5d49b874d84a18fc22054b7a", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe", + "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "suggest": { "ext-intl": "For best performance" @@ -2888,7 +2936,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.30.0" + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.31.0" }, "funding": [ { @@ -2904,24 +2952,24 @@ "type": "tidelift" } ], - "time": "2024-05-31T15:07:36+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/polyfill-intl-normalizer", - "version": "v1.30.0", + "version": "v1.31.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-normalizer.git", - "reference": "a95281b0be0d9ab48050ebd988b967875cdb9fdb" + "reference": "3833d7255cc303546435cb650316bff708a1c75c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/a95281b0be0d9ab48050ebd988b967875cdb9fdb", - "reference": "a95281b0be0d9ab48050ebd988b967875cdb9fdb", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/3833d7255cc303546435cb650316bff708a1c75c", + "reference": "3833d7255cc303546435cb650316bff708a1c75c", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "suggest": { "ext-intl": "For best performance" @@ -2969,7 +3017,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.30.0" + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.31.0" }, "funding": [ { @@ -2985,24 +3033,24 @@ "type": "tidelift" } ], - "time": "2024-05-31T15:07:36+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.30.0", + "version": "v1.31.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "fd22ab50000ef01661e2a31d850ebaa297f8e03c" + "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/fd22ab50000ef01661e2a31d850ebaa297f8e03c", - "reference": "fd22ab50000ef01661e2a31d850ebaa297f8e03c", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/85181ba99b2345b0ef10ce42ecac37612d9fd341", + "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "provide": { "ext-mbstring": "*" @@ -3049,7 +3097,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.30.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.31.0" }, "funding": [ { @@ -3065,20 +3113,20 @@ "type": "tidelift" } ], - "time": "2024-06-19T12:30:46+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/process", - "version": "v7.1.3", + "version": "v7.1.5", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "7f2f542c668ad6c313dc4a5e9c3321f733197eca" + "reference": "5c03ee6369281177f07f7c68252a280beccba847" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/7f2f542c668ad6c313dc4a5e9c3321f733197eca", - "reference": "7f2f542c668ad6c313dc4a5e9c3321f733197eca", + "url": "https://api.github.com/repos/symfony/process/zipball/5c03ee6369281177f07f7c68252a280beccba847", + "reference": "5c03ee6369281177f07f7c68252a280beccba847", "shasum": "" }, "require": { @@ -3110,7 +3158,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v7.1.3" + "source": "https://github.com/symfony/process/tree/v7.1.5" }, "funding": [ { @@ -3126,7 +3174,7 @@ "type": "tidelift" } ], - "time": "2024-07-26T12:44:47+00:00" + "time": "2024-09-19T21:48:23+00:00" }, { "name": "symfony/service-contracts", @@ -3213,16 +3261,16 @@ }, { "name": "symfony/string", - "version": "v7.1.3", + "version": "v7.1.5", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "ea272a882be7f20cad58d5d78c215001617b7f07" + "reference": "d66f9c343fa894ec2037cc928381df90a7ad4306" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/ea272a882be7f20cad58d5d78c215001617b7f07", - "reference": "ea272a882be7f20cad58d5d78c215001617b7f07", + "url": "https://api.github.com/repos/symfony/string/zipball/d66f9c343fa894ec2037cc928381df90a7ad4306", + "reference": "d66f9c343fa894ec2037cc928381df90a7ad4306", "shasum": "" }, "require": { @@ -3280,7 +3328,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v7.1.3" + "source": "https://github.com/symfony/string/tree/v7.1.5" }, "funding": [ { @@ -3296,7 +3344,7 @@ "type": "tidelift" } ], - "time": "2024-07-22T10:25:37+00:00" + "time": "2024-09-20T08:28:38+00:00" }, { "name": "theseer/tokenizer", diff --git a/src/App.php b/src/App.php index 1458838c..0dd463f7 100755 --- a/src/App.php +++ b/src/App.php @@ -4,6 +4,8 @@ class App { + public const COMPRESSION_MIN_SIZE_DEFAULT = 1024; + /** * Request method constants */ @@ -102,6 +104,12 @@ class App */ protected static ?Route $wildcardRoute = null; + /** + * Compression + */ + protected bool $compression = false; + protected int $compressionMinSize = App::COMPRESSION_MIN_SIZE_DEFAULT; + /** * App * @@ -112,6 +120,22 @@ public function __construct(string $timezone) \date_default_timezone_set($timezone); } + /** + * Set Compression + */ + public function setCompression(bool $compression) + { + $this->compression = $compression; + } + + /** + * Set minimum compression size + */ + public function setCompressionMinSize(int $compressionMinSize) + { + $this->compressionMinSize = $compressionMinSize; + } + /** * GET * @@ -642,6 +666,11 @@ protected function getArguments(Hook $hook, array $values, array $requestParams) */ public function run(Request $request, Response $response): static { + if ($this->compression) { + $response->setAcceptEncoding($request->getHeader('accept-encoding') ?? ''); + $response->setCompressionMinSize($this->compressionMinSize); + } + $this->resources['request'] = $request; $this->resources['response'] = $response; diff --git a/src/Http/Response.php b/src/Http/Response.php new file mode 100644 index 00000000..0f2cab39 --- /dev/null +++ b/src/Http/Response.php @@ -0,0 +1,837 @@ + 'Continue', + self::STATUS_CODE_SWITCHING_PROTOCOLS => 'Switching Protocols', + self::STATUS_CODE_OK => 'OK', + self::STATUS_CODE_CREATED => 'Created', + self::STATUS_CODE_ACCEPTED => 'Accepted', + self::STATUS_CODE_NON_AUTHORITATIVE_INFORMATION => 'Non-Authoritative Information', + self::STATUS_CODE_NOCONTENT => 'No Content', + self::STATUS_CODE_RESETCONTENT => 'Reset Content', + self::STATUS_CODE_PARTIALCONTENT => 'Partial Content', + self::STATUS_CODE_MULTIPLE_CHOICES => 'Multiple Choices', + self::STATUS_CODE_MOVED_PERMANENTLY => 'Moved Permanently', + self::STATUS_CODE_FOUND => 'Found', + self::STATUS_CODE_SEE_OTHER => 'See Other', + self::STATUS_CODE_NOT_MODIFIED => 'Not Modified', + self::STATUS_CODE_USE_PROXY => 'Use Proxy', + self::STATUS_CODE_UNUSED => '(Unused)', + self::STATUS_CODE_TEMPORARY_REDIRECT => 'Temporary Redirect', + self::STATUS_CODE_BAD_REQUEST => 'Bad Request', + self::STATUS_CODE_UNAUTHORIZED => 'Unauthorized', + self::STATUS_CODE_PAYMENT_REQUIRED => 'Payment Required', + self::STATUS_CODE_FORBIDDEN => 'Forbidden', + self::STATUS_CODE_NOT_FOUND => 'Not Found', + self::STATUS_CODE_METHOD_NOT_ALLOWED => 'Method Not Allowed', + self::STATUS_CODE_NOT_ACCEPTABLE => 'Not Acceptable', + self::STATUS_CODE_PROXY_AUTHENTICATION_REQUIRED => 'Proxy Authentication Required', + self::STATUS_CODE_REQUEST_TIMEOUT => 'Request Timeout', + self::STATUS_CODE_CONFLICT => 'Conflict', + self::STATUS_CODE_GONE => 'Gone', + self::STATUS_CODE_LENGTH_REQUIRED => 'Length Required', + self::STATUS_CODE_PRECONDITION_FAILED => 'Precondition Failed', + self::STATUS_CODE_REQUEST_ENTITY_TOO_LARGE => 'Request Entity Too Large', + self::STATUS_CODE_REQUEST_URI_TOO_LONG => 'Request-URI Too Long', + self::STATUS_CODE_UNSUPPORTED_MEDIA_TYPE => 'Unsupported Media Type', + self::STATUS_CODE_REQUESTED_RANGE_NOT_SATISFIABLE => 'Requested Range Not Satisfiable', + self::STATUS_CODE_EXPECTATION_FAILED => 'Expectation Failed', + self::STATUS_CODE_TOO_EARLY => 'Too Early', + self::STATUS_CODE_TOO_MANY_REQUESTS => 'Too Many Requests', + self::STATUS_CODE_UNAVAILABLE_FOR_LEGAL_REASONS => 'Unavailable For Legal Reasons', + self::STATUS_CODE_INTERNAL_SERVER_ERROR => 'Internal Server Error', + self::STATUS_CODE_NOT_IMPLEMENTED => 'Not Implemented', + self::STATUS_CODE_BAD_GATEWAY => 'Bad Gateway', + self::STATUS_CODE_SERVICE_UNAVAILABLE => 'Service Unavailable', + self::STATUS_CODE_GATEWAY_TIMEOUT => 'Gateway Timeout', + self::STATUS_CODE_HTTP_VERSION_NOT_SUPPORTED => 'HTTP Version Not Supported', + ]; + + /** + * Mime Types with compression support + * + * @var array + */ + protected $compressed = [ + 'text/plain' => true, + 'text/css' => true, + 'text/javascript' => true, + 'application/javascript' => true, + 'text/html' => true, + 'text/html; charset=UTF-8' => true, + 'application/json' => true, + 'application/json; charset=UTF-8' => true, + 'image/svg+xml' => true, + 'application/xml+rss' => true, + ]; + + public const COOKIE_SAMESITE_NONE = 'None'; + + public const COOKIE_SAMESITE_STRICT = 'Strict'; + + public const COOKIE_SAMESITE_LAX = 'Lax'; + + public const CHUNK_SIZE = 2000000; //2mb + + /** + * @var int + */ + protected int $statusCode = self::STATUS_CODE_OK; + + /** + * @var string + */ + protected string $contentType = ''; + + /** + * @var bool + */ + protected bool $disablePayload = false; + + /** + * @var bool + */ + protected bool $sent = false; + + /** + * @var array + */ + protected array $headers = []; + + /** + * @var array + */ + protected array $cookies = []; + + /** + * @var float + */ + protected float $startTime = 0; + + /** + * @var int + */ + protected int $size = 0; + + /** + * @var string + */ + protected string $acceptEncoding = ''; + + /** + * @var int + */ + protected int $compressionMinSize = Http::COMPRESSION_MIN_SIZE_DEFAULT; + + /** + * Response constructor. + * + * @param float $time response start time + */ + public function __construct(float $time = 0) + { + $this->startTime = (!empty($time)) ? $time : \microtime(true); + } + + /** + * Set content type + * + * Set HTTP content type header. + * + * @param string $type + * @param string $charset + */ + public function setContentType(string $type, string $charset = ''): static + { + $this->contentType = $type.((!empty($charset) ? '; charset='.$charset : '')); + + return $this; + } + + /** + * Set accept encoding + * + * Set HTTP accept encoding header. + * + * @param string $acceptEncoding + */ + public function setAcceptEncoding(string $acceptEncoding): static + { + $this->acceptEncoding = $acceptEncoding; + return $this; + } + + /** + * Set min compression size + * + * Set minimum size for compression to be applied in bytes. + * + * @param int $compressionMinSize + */ + public function setCompressionMinSize(int $compressionMinSize): static + { + $this->compressionMinSize = $compressionMinSize; + return $this; + } + + /** + * Get content type + * + * Get HTTP content type header. + * + * @return string + */ + public function getContentType(): string + { + return $this->contentType; + } + + /** + * Get if response was already sent + * + * @return bool + */ + public function isSent(): bool + { + return $this->sent; + } + + /** + * Set status code + * + * Set HTTP response status code between available options. if status code is unknown an exception will be thrown + * + * @param int $code + * + * @throws Exception + */ + public function setStatusCode(int $code = 200): static + { + if (!\array_key_exists($code, $this->statusCodes)) { + throw new Exception('Unknown HTTP status code'); + } + + $this->statusCode = $code; + + return $this; + } + + /** + * Get status code + * + * Get HTTP response status code + * + * @return int + **/ + public function getStatusCode(): int + { + return $this->statusCode; + } + + /** + * Get Response Size + * + * Return output response size in bytes + * + * @return int + */ + public function getSize(): int + { + return $this->size; + } + + /** + * Don't allow payload on response output + */ + public function disablePayload(): static + { + $this->disablePayload = true; + + return $this; + } + + /** + * Allow payload on response output + */ + public function enablePayload(): static + { + $this->disablePayload = false; + + return $this; + } + + /** + * Add header + * + * Add an HTTP response header + * + * @param string $key + * @param string $value + */ + public function addHeader(string $key, string $value): static + { + $this->headers[$key] = $value; + + return $this; + } + + /** + * Remove header + * + * Remove HTTP response header + * + * @param string $key + */ + public function removeHeader(string $key): static + { + if (isset($this->headers[$key])) { + unset($this->headers[$key]); + } + + return $this; + } + + /** + * Get Headers + * + * Return array of all response headers + * + * @return array + */ + public function getHeaders(): array + { + return $this->headers; + } + + /** + * Add cookie + * + * Add an HTTP cookie to response header + * + * @param string $name + * @param string $value + * @param int $expire + * @param string $path + * @param string $domain + * @param bool $secure + * @param bool $httponly + * @param string $sameSite + */ + public function addCookie(string $name, string $value = null, int $expire = null, string $path = null, string $domain = null, bool $secure = null, bool $httponly = null, string $sameSite = null): static + { + $name = strtolower($name); + + $this->cookies[] = [ + 'name' => $name, + 'value' => $value, + 'expire' => $expire, + 'path' => $path, + 'domain' => $domain, + 'secure' => $secure, + 'httponly' => $httponly, + 'samesite' => $sameSite, + ]; + + return $this; + } + + /** + * Remove cookie + * + * Remove HTTP response cookie + * + * @param string $name + */ + public function removeCookie(string $name): static + { + $this->cookies = array_filter($this->cookies, function ($cookie) use ($name) { + return $cookie['name'] !== $name; + }); + + return $this; + } + + /** + * Get Cookies + * + * Return array of all response cookies + * + * @return array + */ + public function getCookies(): array + { + return $this->cookies; + } + + /** + * Output response + * + * Generate HTTP response output including the response header (+cookies) and body and prints them. + * + * @param string $body + * @return void + */ + public function send(string $body = ''): void + { + if ($this->sent) { + return; + } + + $this->sent = true; + + $serverHeader = $this->headers['Server'] ?? 'Utopia/Http'; + $this->addHeader('Server', $serverHeader); + $this->addHeader('X-Debug-Speed', (string) (microtime(true) - $this->startTime)); + + $this->appendCookies()->appendHeaders(); + + // Send response + if ($this->disablePayload) { + $this->end(); + return; + } + + // Compress body + if ( + !empty($this->acceptEncoding) && + isset($this->compressed[$this->contentType]) && + strlen($body) > $this->compressionMinSize + ) { + $algorithm = Compression::fromAcceptEncoding($this->acceptEncoding, [ + Compression::BROTLI, + Compression::GZIP, + Compression::DEFLATE, + ]); + + if ($algorithm) { + $body = $algorithm->compress($body); + $this->addHeader('Content-Encoding', $algorithm->getContentEncoding()); + $this->addHeader('Vary', 'Accept-Encoding'); + } + } + + $headerSize = strlen(implode("\n", $this->headers)); + $bodyLength = strlen($body); + $this->size += $headerSize + $bodyLength; + + if ($bodyLength <= self::CHUNK_SIZE) { + $this->end($body); + } else { + $chunks = str_split($body, self::CHUNK_SIZE); + foreach ($chunks as $chunk) { + $this->write($chunk); + } + $this->end(); + } + + $this->disablePayload(); + } + + /** + * Write + * + * Send output + * + * @param string $content + * @return bool False if write cannot complete, such as request ended by client + */ + abstract public function write(string $content): bool; + + /** + * End + * + * Send optional content and end + * + * @param string $content + * @return void + */ + abstract public function end(string $content = ''): void; + + /** + * Output response + * + * Generate HTTP response output including the response header (+cookies) and body and prints them. + * + * @param string $body + * @param bool $end + * + * @return void + */ + public function chunk(string $body = '', bool $end = false): void + { + if ($this->sent) { + return; + } + + if ($end) { + $this->sent = true; + } + + $this->addHeader('X-Debug-Speed', (string) (microtime(true) - $this->startTime)); + + $this + ->appendCookies() + ->appendHeaders(); + + if (!$this->disablePayload) { + $this->write($body); + if ($end) { + $this->disablePayload(); + $this->end(); + } + } else { + $this->end(); + } + } + + /** + * Append headers + * + * Iterating over response headers to generate them using native PHP header function. + * This method is also responsible for generating the response and content type headers. + */ + protected function appendHeaders(): static + { + // Send status code header + $this->sendStatus($this->statusCode, $this->statusCodes[$this->statusCode] ?? 'Unknown HTTP status code'); + + // Send content type header + if (!empty($this->contentType)) { + $this->addHeader('Content-Type', $this->contentType); + } + + // Set application headers + foreach ($this->headers as $key => $value) { + $this->sendHeader($key, $value); + } + + return $this; + } + + /** + * Send Status Code + * + * @param int $statusCode + * @param string $reason + * @return void + */ + abstract protected function sendStatus(int $statusCode, string $reason): void; + + /** + * Send Header + * + * Output Header + * + * @param string $key + * @param string $value + * @return void + */ + abstract public function sendHeader(string $key, string $value): void; + + /** + * Send Cookie + * + * Output Cookie + * + * @param string $name + * @param string $value + * @param array $options + * @return void + */ + abstract protected function sendCookie(string $name, string $value, array $options): void; + + /** + * Append cookies + * + * Iterating over response cookies to generate them using native PHP cookie function. + */ + protected function appendCookies(): static + { + foreach ($this->cookies as $cookie) { + $this->sendCookie($cookie['name'], $cookie['value'], [ + 'expire' => $cookie['expire'], + 'path' => $cookie['path'], + 'domain' => $cookie['domain'], + 'secure' => $cookie['secure'], + 'httponly' => $cookie['httponly'], + 'samesite' => $cookie['samesite'], + ]); + } + + return $this; + } + + /** + * Redirect + * + * This helper is for sending a 30* HTTP response. + * After setting relevant HTTP headers for redirect response this helper stop application native flow what means the shutdown method will not be executed + * + * NOTICE: it seems webkit based browsers have problems redirecting link with 300 status codes. + * + * @see https://code.google.com/p/chromium/issues/detail?id=75540 + * @see https://bugs.webkit.org/show_bug.cgi?id=47425 + * + * @param string $url complete absolute URI for redirection as required by the internet standard RFC 2616 (HTTP 1.1) + * @param int $statusCode valid HTTP status code + * @return void + * + * @throws Exception + * + * @see http://tools.ietf.org/html/rfc2616 + */ + public function redirect(string $url, int $statusCode = 301): void + { + if (300 == $statusCode) { + \trigger_error('It seems webkit based browsers have problems redirecting link with 300 status codes!', E_USER_NOTICE); + } + + $this + ->addHeader('Location', $url) + ->setStatusCode($statusCode) + ->send(''); + } + + /** + * HTML + * + * This helper is for sending an HTML HTTP response and sets relevant content type header ('text/html'). + * + * @see http://en.wikipedia.org/wiki/JSON + * + * @param string $data + * @return void + */ + public function html(string $data): void + { + $this + ->setContentType(self::CONTENT_TYPE_HTML, self::CHARSET_UTF8) + ->send($data); + } + + /** + * Text + * + * This helper is for sending plain text HTTP response and sets relevant content type header ('text/plain'). + * + * @see http://en.wikipedia.org/wiki/JSON + * + * @param string $data + * @return void + */ + public function text(string $data): void + { + $this + ->setContentType(self::CONTENT_TYPE_TEXT, self::CHARSET_UTF8) + ->send($data); + } + + /** + * JSON + * + * This helper is for sending JSON HTTP response. + * It sets relevant content type header ('application/json') and convert a PHP array ($data) to valid JSON using native json_encode + * + * @see http://en.wikipedia.org/wiki/JSON + * + * @param mixed $data + * @return void + */ + public function json($data): void + { + if (!is_array($data) && !$data instanceof \stdClass) { + throw new \Exception('Invalid JSON input var'); + } + + $this + ->setContentType(Response::CONTENT_TYPE_JSON, self::CHARSET_UTF8) + ->send(\json_encode($data, JSON_UNESCAPED_UNICODE)); + } + + /** + * JSON with padding + * + * This helper is for sending JSONP HTTP response. + * It sets relevant content type header ('text/javascript') and convert a PHP array ($data) to valid JSON using native json_encode + * + * @see http://en.wikipedia.org/wiki/JSONP + * + * @param string $callback + * @param array $data + * @return void + */ + public function jsonp(string $callback, array $data): void + { + $this + ->setContentType(self::CONTENT_TYPE_JAVASCRIPT, self::CHARSET_UTF8) + ->send('parent.'.$callback.'('.\json_encode($data).');'); + } + + /** + * Iframe + * + * This helper is for sending iframe HTTP response. + * It sets relevant content type header ('text/html') and convert a PHP array ($data) to valid JSON using native json_encode + * + * @param string $callback + * @param array $data + * @return void + */ + public function iframe(string $callback, array $data): void + { + $this + ->setContentType(self::CONTENT_TYPE_HTML, self::CHARSET_UTF8) + ->send(''); + } + + /** + * No Content + * + * This helper is for sending no content HTTP response. + * + * The server has successfully fulfilled the request + * and that there is no additional content to send in the response payload body. + * + * @return void + */ + public function noContent(): void + { + $this + ->setStatusCode(self::STATUS_CODE_NOCONTENT) + ->send(''); + } +} diff --git a/src/Response.php b/src/Response.php index 8d19f5a8..b39da39c 100755 --- a/src/Response.php +++ b/src/Response.php @@ -2,6 +2,8 @@ namespace Utopia; +use Utopia\Compression\Compression; + class Response { /** @@ -245,6 +247,16 @@ class Response */ protected int $size = 0; + /** + * @var string + */ + protected string $acceptEncoding = ''; + + /** + * @var int + */ + protected int $compressionMinSize = App::COMPRESSION_MIN_SIZE_DEFAULT; + /** * Response constructor. * @@ -270,6 +282,32 @@ public function setContentType(string $type, string $charset = ''): static return $this; } + /** + * Set accept encoding + * + * Set HTTP accept encoding header. + * + * @param string $acceptEncoding + */ + public function setAcceptEncoding(string $acceptEncoding): static + { + $this->acceptEncoding = $acceptEncoding; + return $this; + } + + /** + * Set min compression size + * + * Set minimum size for compression to be applied in bytes. + * + * @param int $compressionMinSize + */ + public function setCompressionMinSize(int $compressionMinSize): static + { + $this->compressionMinSize = $compressionMinSize; + return $this; + } + /** * Get content type * @@ -473,36 +511,52 @@ public function send(string $body = ''): void return; } - $this->sent = true; - - $this->addHeader('X-Debug-Speed', (string) (\microtime(true) - $this->startTime)); - - $this - ->appendCookies() - ->appendHeaders(); - - if (!$this->disablePayload) { - $length = strlen($body); + $serverHeader = $this->headers['Server'] ?? 'Utopia/Http'; + $this->addHeader('Server', $serverHeader); + $this->addHeader('X-Debug-Speed', (string) (microtime(true) - $this->startTime)); - $this->size = $this->size + strlen(implode("\n", $this->headers)) + $length; + $this->appendCookies()->appendHeaders(); + + // Send response + if ($this->disablePayload) { + $this->end(); + return; + } - if (array_key_exists( - $this->contentType, - $this->compressed - ) && ($length <= self::CHUNK_SIZE)) { // Dont compress with GZIP / Brotli if header is not listed and size is bigger than 2mb - $this->end($body); - } else { - for ($i = 0; $i < ceil($length / self::CHUNK_SIZE); $i++) { - $this->write(substr($body, ($i * self::CHUNK_SIZE), min(self::CHUNK_SIZE, $length - ($i * self::CHUNK_SIZE)))); - } + // Compress body + if ( + !empty($this->acceptEncoding) && + isset($this->compressed[$this->contentType]) && + strlen($body) > $this->compressionMinSize + ) { + $algorithm = Compression::fromAcceptEncoding($this->acceptEncoding, [ + Compression::BROTLI, + Compression::GZIP, + Compression::DEFLATE, + ]); - $this->end(); + if ($algorithm) { + $body = $algorithm->compress($body); + $this->addHeader('Content-Encoding', $algorithm->getContentEncoding()); + $this->addHeader('Vary', 'Accept-Encoding'); } + } + + $headerSize = strlen(implode("\n", $this->headers)); + $bodyLength = strlen($body); + $this->size += $headerSize + $bodyLength; - $this->disablePayload(); + if ($bodyLength <= self::CHUNK_SIZE) { + $this->end($body); } else { + $chunks = str_split($body, self::CHUNK_SIZE); + foreach ($chunks as $chunk) { + $this->write($chunk); + } $this->end(); } + + $this->disablePayload(); } /** From eaebc955de32de6458aabcd37246e0638df26191 Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Wed, 23 Oct 2024 11:49:11 +0100 Subject: [PATCH 017/103] test: enable compression --- tests/e2e/server.php | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/e2e/server.php b/tests/e2e/server.php index cef864ee..caa84ebe 100644 --- a/tests/e2e/server.php +++ b/tests/e2e/server.php @@ -50,4 +50,5 @@ $response = new Response(); $app = new App('UTC'); +$app->setCompression(true); $app->run($request, $response); From f7dfee210fc08dc0349adfe0facd9f6fb60728ce Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Wed, 23 Oct 2024 11:54:53 +0100 Subject: [PATCH 018/103] fix: lint --- src/App.php | 2 +- src/Response.php | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/App.php b/src/App.php index 0dd463f7..00bd44a2 100755 --- a/src/App.php +++ b/src/App.php @@ -667,7 +667,7 @@ protected function getArguments(Hook $hook, array $values, array $requestParams) public function run(Request $request, Response $response): static { if ($this->compression) { - $response->setAcceptEncoding($request->getHeader('accept-encoding') ?? ''); + $response->setAcceptEncoding($request->getHeader('accept-encoding', '')); $response->setCompressionMinSize($this->compressionMinSize); } diff --git a/src/Response.php b/src/Response.php index b39da39c..76bf02b4 100755 --- a/src/Response.php +++ b/src/Response.php @@ -295,7 +295,7 @@ public function setAcceptEncoding(string $acceptEncoding): static return $this; } - /** + /** * Set min compression size * * Set minimum size for compression to be applied in bytes. @@ -516,7 +516,7 @@ public function send(string $body = ''): void $this->addHeader('X-Debug-Speed', (string) (microtime(true) - $this->startTime)); $this->appendCookies()->appendHeaders(); - + // Send response if ($this->disablePayload) { $this->end(); From f4f83a364bce9457155fdb9ce4b525138b42b312 Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Tue, 29 Oct 2024 12:53:12 +0000 Subject: [PATCH 019/103] chore: fmt --- composer.lock | 76 ++++++++++++++++++------------------- src/Validator/ArrayList.php | 2 +- tests/ResponseTest.php | 2 +- tests/ViewTest.php | 2 +- 4 files changed, 41 insertions(+), 41 deletions(-) diff --git a/composer.lock b/composer.lock index af10d95d..4677e800 100644 --- a/composer.lock +++ b/composer.lock @@ -2442,16 +2442,16 @@ }, { "name": "symfony/console", - "version": "v7.1.5", + "version": "v7.1.6", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "0fa539d12b3ccf068a722bbbffa07ca7079af9ee" + "reference": "bb5192af6edc797cbab5c8e8ecfea2fe5f421e57" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/0fa539d12b3ccf068a722bbbffa07ca7079af9ee", - "reference": "0fa539d12b3ccf068a722bbbffa07ca7079af9ee", + "url": "https://api.github.com/repos/symfony/console/zipball/bb5192af6edc797cbab5c8e8ecfea2fe5f421e57", + "reference": "bb5192af6edc797cbab5c8e8ecfea2fe5f421e57", "shasum": "" }, "require": { @@ -2515,7 +2515,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v7.1.5" + "source": "https://github.com/symfony/console/tree/v7.1.6" }, "funding": [ { @@ -2531,7 +2531,7 @@ "type": "tidelift" } ], - "time": "2024-09-20T08:28:38+00:00" + "time": "2024-10-09T08:46:59+00:00" }, { "name": "symfony/deprecation-contracts", @@ -2602,16 +2602,16 @@ }, { "name": "symfony/filesystem", - "version": "v7.1.5", + "version": "v7.1.6", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "61fe0566189bf32e8cfee78335d8776f64a66f5a" + "reference": "c835867b3c62bb05c7fe3d637c871c7ae52024d4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/61fe0566189bf32e8cfee78335d8776f64a66f5a", - "reference": "61fe0566189bf32e8cfee78335d8776f64a66f5a", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/c835867b3c62bb05c7fe3d637c871c7ae52024d4", + "reference": "c835867b3c62bb05c7fe3d637c871c7ae52024d4", "shasum": "" }, "require": { @@ -2648,7 +2648,7 @@ "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/filesystem/tree/v7.1.5" + "source": "https://github.com/symfony/filesystem/tree/v7.1.6" }, "funding": [ { @@ -2664,20 +2664,20 @@ "type": "tidelift" } ], - "time": "2024-09-17T09:16:35+00:00" + "time": "2024-10-25T15:11:02+00:00" }, { "name": "symfony/finder", - "version": "v7.1.4", + "version": "v7.1.6", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "d95bbf319f7d052082fb7af147e0f835a695e823" + "reference": "2cb89664897be33f78c65d3d2845954c8d7a43b8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/d95bbf319f7d052082fb7af147e0f835a695e823", - "reference": "d95bbf319f7d052082fb7af147e0f835a695e823", + "url": "https://api.github.com/repos/symfony/finder/zipball/2cb89664897be33f78c65d3d2845954c8d7a43b8", + "reference": "2cb89664897be33f78c65d3d2845954c8d7a43b8", "shasum": "" }, "require": { @@ -2712,7 +2712,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v7.1.4" + "source": "https://github.com/symfony/finder/tree/v7.1.6" }, "funding": [ { @@ -2728,20 +2728,20 @@ "type": "tidelift" } ], - "time": "2024-08-13T14:28:19+00:00" + "time": "2024-10-01T08:31:23+00:00" }, { "name": "symfony/options-resolver", - "version": "v7.1.1", + "version": "v7.1.6", "source": { "type": "git", "url": "https://github.com/symfony/options-resolver.git", - "reference": "47aa818121ed3950acd2b58d1d37d08a94f9bf55" + "reference": "85e95eeede2d41cd146146e98c9c81d9214cae85" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/options-resolver/zipball/47aa818121ed3950acd2b58d1d37d08a94f9bf55", - "reference": "47aa818121ed3950acd2b58d1d37d08a94f9bf55", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/85e95eeede2d41cd146146e98c9c81d9214cae85", + "reference": "85e95eeede2d41cd146146e98c9c81d9214cae85", "shasum": "" }, "require": { @@ -2779,7 +2779,7 @@ "options" ], "support": { - "source": "https://github.com/symfony/options-resolver/tree/v7.1.1" + "source": "https://github.com/symfony/options-resolver/tree/v7.1.6" }, "funding": [ { @@ -2795,7 +2795,7 @@ "type": "tidelift" } ], - "time": "2024-05-31T14:57:53+00:00" + "time": "2024-09-25T14:20:29+00:00" }, { "name": "symfony/polyfill-ctype", @@ -3117,16 +3117,16 @@ }, { "name": "symfony/process", - "version": "v7.1.5", + "version": "v7.1.6", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "5c03ee6369281177f07f7c68252a280beccba847" + "reference": "6aaa189ddb4ff6b5de8fa3210f2fb42c87b4d12e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/5c03ee6369281177f07f7c68252a280beccba847", - "reference": "5c03ee6369281177f07f7c68252a280beccba847", + "url": "https://api.github.com/repos/symfony/process/zipball/6aaa189ddb4ff6b5de8fa3210f2fb42c87b4d12e", + "reference": "6aaa189ddb4ff6b5de8fa3210f2fb42c87b4d12e", "shasum": "" }, "require": { @@ -3158,7 +3158,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v7.1.5" + "source": "https://github.com/symfony/process/tree/v7.1.6" }, "funding": [ { @@ -3174,7 +3174,7 @@ "type": "tidelift" } ], - "time": "2024-09-19T21:48:23+00:00" + "time": "2024-09-25T14:20:29+00:00" }, { "name": "symfony/service-contracts", @@ -3261,16 +3261,16 @@ }, { "name": "symfony/string", - "version": "v7.1.5", + "version": "v7.1.6", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "d66f9c343fa894ec2037cc928381df90a7ad4306" + "reference": "61b72d66bf96c360a727ae6232df5ac83c71f626" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/d66f9c343fa894ec2037cc928381df90a7ad4306", - "reference": "d66f9c343fa894ec2037cc928381df90a7ad4306", + "url": "https://api.github.com/repos/symfony/string/zipball/61b72d66bf96c360a727ae6232df5ac83c71f626", + "reference": "61b72d66bf96c360a727ae6232df5ac83c71f626", "shasum": "" }, "require": { @@ -3328,7 +3328,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v7.1.5" + "source": "https://github.com/symfony/string/tree/v7.1.6" }, "funding": [ { @@ -3344,7 +3344,7 @@ "type": "tidelift" } ], - "time": "2024-09-20T08:28:38+00:00" + "time": "2024-09-25T14:20:29+00:00" }, { "name": "theseer/tokenizer", @@ -3448,12 +3448,12 @@ ], "aliases": [], "minimum-stability": "stable", - "stability-flags": [], + "stability-flags": {}, "prefer-stable": false, "prefer-lowest": false, "platform": { "php": ">=8.0" }, - "platform-dev": [], + "platform-dev": {}, "plugin-api-version": "2.6.0" } diff --git a/src/Validator/ArrayList.php b/src/Validator/ArrayList.php index 2468aed0..f7c38fc6 100644 --- a/src/Validator/ArrayList.php +++ b/src/Validator/ArrayList.php @@ -46,7 +46,7 @@ public function getDescription(): string { $msg = 'Value must a valid array'; - if($this->length > 0) { + if ($this->length > 0) { $msg .= ' no longer than ' . $this->length . ' items'; } diff --git a/tests/ResponseTest.php b/tests/ResponseTest.php index 30b20477..b894556b 100755 --- a/tests/ResponseTest.php +++ b/tests/ResponseTest.php @@ -35,7 +35,7 @@ public function testCanSetStatus() try { $this->response->setStatusCode(0); // Unknown status code - } catch(\Exception $e) { + } catch (\Exception $e) { $this->assertInstanceOf('\Exception', $e); return; diff --git a/tests/ViewTest.php b/tests/ViewTest.php index e21131af..98e427f3 100755 --- a/tests/ViewTest.php +++ b/tests/ViewTest.php @@ -67,7 +67,7 @@ public function testCanRenderHtml() $this->view->setRendered(false); $this->view->setPath('just-a-broken-string.phtml'); $this->view->render(); - } catch(\Exception $e) { + } catch (\Exception $e) { return; } From 10036b0293e1ded7d0ab2154def2e06744bae80b Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Tue, 29 Oct 2024 12:56:10 +0000 Subject: [PATCH 020/103] chore: remove extra file --- src/Http/Response.php | 837 ------------------------------------------ 1 file changed, 837 deletions(-) delete mode 100644 src/Http/Response.php diff --git a/src/Http/Response.php b/src/Http/Response.php deleted file mode 100644 index 0f2cab39..00000000 --- a/src/Http/Response.php +++ /dev/null @@ -1,837 +0,0 @@ - 'Continue', - self::STATUS_CODE_SWITCHING_PROTOCOLS => 'Switching Protocols', - self::STATUS_CODE_OK => 'OK', - self::STATUS_CODE_CREATED => 'Created', - self::STATUS_CODE_ACCEPTED => 'Accepted', - self::STATUS_CODE_NON_AUTHORITATIVE_INFORMATION => 'Non-Authoritative Information', - self::STATUS_CODE_NOCONTENT => 'No Content', - self::STATUS_CODE_RESETCONTENT => 'Reset Content', - self::STATUS_CODE_PARTIALCONTENT => 'Partial Content', - self::STATUS_CODE_MULTIPLE_CHOICES => 'Multiple Choices', - self::STATUS_CODE_MOVED_PERMANENTLY => 'Moved Permanently', - self::STATUS_CODE_FOUND => 'Found', - self::STATUS_CODE_SEE_OTHER => 'See Other', - self::STATUS_CODE_NOT_MODIFIED => 'Not Modified', - self::STATUS_CODE_USE_PROXY => 'Use Proxy', - self::STATUS_CODE_UNUSED => '(Unused)', - self::STATUS_CODE_TEMPORARY_REDIRECT => 'Temporary Redirect', - self::STATUS_CODE_BAD_REQUEST => 'Bad Request', - self::STATUS_CODE_UNAUTHORIZED => 'Unauthorized', - self::STATUS_CODE_PAYMENT_REQUIRED => 'Payment Required', - self::STATUS_CODE_FORBIDDEN => 'Forbidden', - self::STATUS_CODE_NOT_FOUND => 'Not Found', - self::STATUS_CODE_METHOD_NOT_ALLOWED => 'Method Not Allowed', - self::STATUS_CODE_NOT_ACCEPTABLE => 'Not Acceptable', - self::STATUS_CODE_PROXY_AUTHENTICATION_REQUIRED => 'Proxy Authentication Required', - self::STATUS_CODE_REQUEST_TIMEOUT => 'Request Timeout', - self::STATUS_CODE_CONFLICT => 'Conflict', - self::STATUS_CODE_GONE => 'Gone', - self::STATUS_CODE_LENGTH_REQUIRED => 'Length Required', - self::STATUS_CODE_PRECONDITION_FAILED => 'Precondition Failed', - self::STATUS_CODE_REQUEST_ENTITY_TOO_LARGE => 'Request Entity Too Large', - self::STATUS_CODE_REQUEST_URI_TOO_LONG => 'Request-URI Too Long', - self::STATUS_CODE_UNSUPPORTED_MEDIA_TYPE => 'Unsupported Media Type', - self::STATUS_CODE_REQUESTED_RANGE_NOT_SATISFIABLE => 'Requested Range Not Satisfiable', - self::STATUS_CODE_EXPECTATION_FAILED => 'Expectation Failed', - self::STATUS_CODE_TOO_EARLY => 'Too Early', - self::STATUS_CODE_TOO_MANY_REQUESTS => 'Too Many Requests', - self::STATUS_CODE_UNAVAILABLE_FOR_LEGAL_REASONS => 'Unavailable For Legal Reasons', - self::STATUS_CODE_INTERNAL_SERVER_ERROR => 'Internal Server Error', - self::STATUS_CODE_NOT_IMPLEMENTED => 'Not Implemented', - self::STATUS_CODE_BAD_GATEWAY => 'Bad Gateway', - self::STATUS_CODE_SERVICE_UNAVAILABLE => 'Service Unavailable', - self::STATUS_CODE_GATEWAY_TIMEOUT => 'Gateway Timeout', - self::STATUS_CODE_HTTP_VERSION_NOT_SUPPORTED => 'HTTP Version Not Supported', - ]; - - /** - * Mime Types with compression support - * - * @var array - */ - protected $compressed = [ - 'text/plain' => true, - 'text/css' => true, - 'text/javascript' => true, - 'application/javascript' => true, - 'text/html' => true, - 'text/html; charset=UTF-8' => true, - 'application/json' => true, - 'application/json; charset=UTF-8' => true, - 'image/svg+xml' => true, - 'application/xml+rss' => true, - ]; - - public const COOKIE_SAMESITE_NONE = 'None'; - - public const COOKIE_SAMESITE_STRICT = 'Strict'; - - public const COOKIE_SAMESITE_LAX = 'Lax'; - - public const CHUNK_SIZE = 2000000; //2mb - - /** - * @var int - */ - protected int $statusCode = self::STATUS_CODE_OK; - - /** - * @var string - */ - protected string $contentType = ''; - - /** - * @var bool - */ - protected bool $disablePayload = false; - - /** - * @var bool - */ - protected bool $sent = false; - - /** - * @var array - */ - protected array $headers = []; - - /** - * @var array - */ - protected array $cookies = []; - - /** - * @var float - */ - protected float $startTime = 0; - - /** - * @var int - */ - protected int $size = 0; - - /** - * @var string - */ - protected string $acceptEncoding = ''; - - /** - * @var int - */ - protected int $compressionMinSize = Http::COMPRESSION_MIN_SIZE_DEFAULT; - - /** - * Response constructor. - * - * @param float $time response start time - */ - public function __construct(float $time = 0) - { - $this->startTime = (!empty($time)) ? $time : \microtime(true); - } - - /** - * Set content type - * - * Set HTTP content type header. - * - * @param string $type - * @param string $charset - */ - public function setContentType(string $type, string $charset = ''): static - { - $this->contentType = $type.((!empty($charset) ? '; charset='.$charset : '')); - - return $this; - } - - /** - * Set accept encoding - * - * Set HTTP accept encoding header. - * - * @param string $acceptEncoding - */ - public function setAcceptEncoding(string $acceptEncoding): static - { - $this->acceptEncoding = $acceptEncoding; - return $this; - } - - /** - * Set min compression size - * - * Set minimum size for compression to be applied in bytes. - * - * @param int $compressionMinSize - */ - public function setCompressionMinSize(int $compressionMinSize): static - { - $this->compressionMinSize = $compressionMinSize; - return $this; - } - - /** - * Get content type - * - * Get HTTP content type header. - * - * @return string - */ - public function getContentType(): string - { - return $this->contentType; - } - - /** - * Get if response was already sent - * - * @return bool - */ - public function isSent(): bool - { - return $this->sent; - } - - /** - * Set status code - * - * Set HTTP response status code between available options. if status code is unknown an exception will be thrown - * - * @param int $code - * - * @throws Exception - */ - public function setStatusCode(int $code = 200): static - { - if (!\array_key_exists($code, $this->statusCodes)) { - throw new Exception('Unknown HTTP status code'); - } - - $this->statusCode = $code; - - return $this; - } - - /** - * Get status code - * - * Get HTTP response status code - * - * @return int - **/ - public function getStatusCode(): int - { - return $this->statusCode; - } - - /** - * Get Response Size - * - * Return output response size in bytes - * - * @return int - */ - public function getSize(): int - { - return $this->size; - } - - /** - * Don't allow payload on response output - */ - public function disablePayload(): static - { - $this->disablePayload = true; - - return $this; - } - - /** - * Allow payload on response output - */ - public function enablePayload(): static - { - $this->disablePayload = false; - - return $this; - } - - /** - * Add header - * - * Add an HTTP response header - * - * @param string $key - * @param string $value - */ - public function addHeader(string $key, string $value): static - { - $this->headers[$key] = $value; - - return $this; - } - - /** - * Remove header - * - * Remove HTTP response header - * - * @param string $key - */ - public function removeHeader(string $key): static - { - if (isset($this->headers[$key])) { - unset($this->headers[$key]); - } - - return $this; - } - - /** - * Get Headers - * - * Return array of all response headers - * - * @return array - */ - public function getHeaders(): array - { - return $this->headers; - } - - /** - * Add cookie - * - * Add an HTTP cookie to response header - * - * @param string $name - * @param string $value - * @param int $expire - * @param string $path - * @param string $domain - * @param bool $secure - * @param bool $httponly - * @param string $sameSite - */ - public function addCookie(string $name, string $value = null, int $expire = null, string $path = null, string $domain = null, bool $secure = null, bool $httponly = null, string $sameSite = null): static - { - $name = strtolower($name); - - $this->cookies[] = [ - 'name' => $name, - 'value' => $value, - 'expire' => $expire, - 'path' => $path, - 'domain' => $domain, - 'secure' => $secure, - 'httponly' => $httponly, - 'samesite' => $sameSite, - ]; - - return $this; - } - - /** - * Remove cookie - * - * Remove HTTP response cookie - * - * @param string $name - */ - public function removeCookie(string $name): static - { - $this->cookies = array_filter($this->cookies, function ($cookie) use ($name) { - return $cookie['name'] !== $name; - }); - - return $this; - } - - /** - * Get Cookies - * - * Return array of all response cookies - * - * @return array - */ - public function getCookies(): array - { - return $this->cookies; - } - - /** - * Output response - * - * Generate HTTP response output including the response header (+cookies) and body and prints them. - * - * @param string $body - * @return void - */ - public function send(string $body = ''): void - { - if ($this->sent) { - return; - } - - $this->sent = true; - - $serverHeader = $this->headers['Server'] ?? 'Utopia/Http'; - $this->addHeader('Server', $serverHeader); - $this->addHeader('X-Debug-Speed', (string) (microtime(true) - $this->startTime)); - - $this->appendCookies()->appendHeaders(); - - // Send response - if ($this->disablePayload) { - $this->end(); - return; - } - - // Compress body - if ( - !empty($this->acceptEncoding) && - isset($this->compressed[$this->contentType]) && - strlen($body) > $this->compressionMinSize - ) { - $algorithm = Compression::fromAcceptEncoding($this->acceptEncoding, [ - Compression::BROTLI, - Compression::GZIP, - Compression::DEFLATE, - ]); - - if ($algorithm) { - $body = $algorithm->compress($body); - $this->addHeader('Content-Encoding', $algorithm->getContentEncoding()); - $this->addHeader('Vary', 'Accept-Encoding'); - } - } - - $headerSize = strlen(implode("\n", $this->headers)); - $bodyLength = strlen($body); - $this->size += $headerSize + $bodyLength; - - if ($bodyLength <= self::CHUNK_SIZE) { - $this->end($body); - } else { - $chunks = str_split($body, self::CHUNK_SIZE); - foreach ($chunks as $chunk) { - $this->write($chunk); - } - $this->end(); - } - - $this->disablePayload(); - } - - /** - * Write - * - * Send output - * - * @param string $content - * @return bool False if write cannot complete, such as request ended by client - */ - abstract public function write(string $content): bool; - - /** - * End - * - * Send optional content and end - * - * @param string $content - * @return void - */ - abstract public function end(string $content = ''): void; - - /** - * Output response - * - * Generate HTTP response output including the response header (+cookies) and body and prints them. - * - * @param string $body - * @param bool $end - * - * @return void - */ - public function chunk(string $body = '', bool $end = false): void - { - if ($this->sent) { - return; - } - - if ($end) { - $this->sent = true; - } - - $this->addHeader('X-Debug-Speed', (string) (microtime(true) - $this->startTime)); - - $this - ->appendCookies() - ->appendHeaders(); - - if (!$this->disablePayload) { - $this->write($body); - if ($end) { - $this->disablePayload(); - $this->end(); - } - } else { - $this->end(); - } - } - - /** - * Append headers - * - * Iterating over response headers to generate them using native PHP header function. - * This method is also responsible for generating the response and content type headers. - */ - protected function appendHeaders(): static - { - // Send status code header - $this->sendStatus($this->statusCode, $this->statusCodes[$this->statusCode] ?? 'Unknown HTTP status code'); - - // Send content type header - if (!empty($this->contentType)) { - $this->addHeader('Content-Type', $this->contentType); - } - - // Set application headers - foreach ($this->headers as $key => $value) { - $this->sendHeader($key, $value); - } - - return $this; - } - - /** - * Send Status Code - * - * @param int $statusCode - * @param string $reason - * @return void - */ - abstract protected function sendStatus(int $statusCode, string $reason): void; - - /** - * Send Header - * - * Output Header - * - * @param string $key - * @param string $value - * @return void - */ - abstract public function sendHeader(string $key, string $value): void; - - /** - * Send Cookie - * - * Output Cookie - * - * @param string $name - * @param string $value - * @param array $options - * @return void - */ - abstract protected function sendCookie(string $name, string $value, array $options): void; - - /** - * Append cookies - * - * Iterating over response cookies to generate them using native PHP cookie function. - */ - protected function appendCookies(): static - { - foreach ($this->cookies as $cookie) { - $this->sendCookie($cookie['name'], $cookie['value'], [ - 'expire' => $cookie['expire'], - 'path' => $cookie['path'], - 'domain' => $cookie['domain'], - 'secure' => $cookie['secure'], - 'httponly' => $cookie['httponly'], - 'samesite' => $cookie['samesite'], - ]); - } - - return $this; - } - - /** - * Redirect - * - * This helper is for sending a 30* HTTP response. - * After setting relevant HTTP headers for redirect response this helper stop application native flow what means the shutdown method will not be executed - * - * NOTICE: it seems webkit based browsers have problems redirecting link with 300 status codes. - * - * @see https://code.google.com/p/chromium/issues/detail?id=75540 - * @see https://bugs.webkit.org/show_bug.cgi?id=47425 - * - * @param string $url complete absolute URI for redirection as required by the internet standard RFC 2616 (HTTP 1.1) - * @param int $statusCode valid HTTP status code - * @return void - * - * @throws Exception - * - * @see http://tools.ietf.org/html/rfc2616 - */ - public function redirect(string $url, int $statusCode = 301): void - { - if (300 == $statusCode) { - \trigger_error('It seems webkit based browsers have problems redirecting link with 300 status codes!', E_USER_NOTICE); - } - - $this - ->addHeader('Location', $url) - ->setStatusCode($statusCode) - ->send(''); - } - - /** - * HTML - * - * This helper is for sending an HTML HTTP response and sets relevant content type header ('text/html'). - * - * @see http://en.wikipedia.org/wiki/JSON - * - * @param string $data - * @return void - */ - public function html(string $data): void - { - $this - ->setContentType(self::CONTENT_TYPE_HTML, self::CHARSET_UTF8) - ->send($data); - } - - /** - * Text - * - * This helper is for sending plain text HTTP response and sets relevant content type header ('text/plain'). - * - * @see http://en.wikipedia.org/wiki/JSON - * - * @param string $data - * @return void - */ - public function text(string $data): void - { - $this - ->setContentType(self::CONTENT_TYPE_TEXT, self::CHARSET_UTF8) - ->send($data); - } - - /** - * JSON - * - * This helper is for sending JSON HTTP response. - * It sets relevant content type header ('application/json') and convert a PHP array ($data) to valid JSON using native json_encode - * - * @see http://en.wikipedia.org/wiki/JSON - * - * @param mixed $data - * @return void - */ - public function json($data): void - { - if (!is_array($data) && !$data instanceof \stdClass) { - throw new \Exception('Invalid JSON input var'); - } - - $this - ->setContentType(Response::CONTENT_TYPE_JSON, self::CHARSET_UTF8) - ->send(\json_encode($data, JSON_UNESCAPED_UNICODE)); - } - - /** - * JSON with padding - * - * This helper is for sending JSONP HTTP response. - * It sets relevant content type header ('text/javascript') and convert a PHP array ($data) to valid JSON using native json_encode - * - * @see http://en.wikipedia.org/wiki/JSONP - * - * @param string $callback - * @param array $data - * @return void - */ - public function jsonp(string $callback, array $data): void - { - $this - ->setContentType(self::CONTENT_TYPE_JAVASCRIPT, self::CHARSET_UTF8) - ->send('parent.'.$callback.'('.\json_encode($data).');'); - } - - /** - * Iframe - * - * This helper is for sending iframe HTTP response. - * It sets relevant content type header ('text/html') and convert a PHP array ($data) to valid JSON using native json_encode - * - * @param string $callback - * @param array $data - * @return void - */ - public function iframe(string $callback, array $data): void - { - $this - ->setContentType(self::CONTENT_TYPE_HTML, self::CHARSET_UTF8) - ->send(''); - } - - /** - * No Content - * - * This helper is for sending no content HTTP response. - * - * The server has successfully fulfilled the request - * and that there is no additional content to send in the response payload body. - * - * @return void - */ - public function noContent(): void - { - $this - ->setStatusCode(self::STATUS_CODE_NOCONTENT) - ->send(''); - } -} From 9112676ca82cb45e8cf5cea6db5dbbadddd9d8f1 Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Thu, 31 Oct 2024 11:39:41 +0000 Subject: [PATCH 021/103] feat: supported compression algorithms --- src/App.php | 10 ++++++++++ src/Response.php | 22 +++++++++++++++++----- 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/src/App.php b/src/App.php index 00bd44a2..1b8c5788 100755 --- a/src/App.php +++ b/src/App.php @@ -109,6 +109,7 @@ class App */ protected bool $compression = false; protected int $compressionMinSize = App::COMPRESSION_MIN_SIZE_DEFAULT; + protected mixed $compressionSupported; /** * App @@ -136,6 +137,14 @@ public function setCompressionMinSize(int $compressionMinSize) $this->compressionMinSize = $compressionMinSize; } + /** + * Set supported compression algorithms + */ + public function setCompressionSupported(mixed $compressionSupported) + { + $this->compressionSupported = $compressionSupported; + } + /** * GET * @@ -669,6 +678,7 @@ public function run(Request $request, Response $response): static if ($this->compression) { $response->setAcceptEncoding($request->getHeader('accept-encoding', '')); $response->setCompressionMinSize($this->compressionMinSize); + $response->setCompressionSupported($this->compressionSupported); } $this->resources['request'] = $request; diff --git a/src/Response.php b/src/Response.php index 76bf02b4..625db69c 100755 --- a/src/Response.php +++ b/src/Response.php @@ -257,6 +257,11 @@ class Response */ protected int $compressionMinSize = App::COMPRESSION_MIN_SIZE_DEFAULT; + /** + * @var mixed + */ + protected mixed $compressionSupported; + /** * Response constructor. * @@ -308,6 +313,17 @@ public function setCompressionMinSize(int $compressionMinSize): static return $this; } + /** + * Set supported compression algorithms + * + * @param mixed $compressionSupported + */ + public function setCompressionSupported(mixed $compressionSupported): static + { + $this->compressionSupported = $compressionSupported; + return $this; + } + /** * Get content type * @@ -529,11 +545,7 @@ public function send(string $body = ''): void isset($this->compressed[$this->contentType]) && strlen($body) > $this->compressionMinSize ) { - $algorithm = Compression::fromAcceptEncoding($this->acceptEncoding, [ - Compression::BROTLI, - Compression::GZIP, - Compression::DEFLATE, - ]); + $algorithm = Compression::fromAcceptEncoding($this->acceptEncoding, $this->compressionSupported); if ($algorithm) { $body = $algorithm->compress($body); From a501a56f89097f2fd48c13eb2c632aafb1285cff Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Fri, 1 Nov 2024 17:05:05 +0000 Subject: [PATCH 022/103] fix: define compression supported --- src/App.php | 2 +- src/Response.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/App.php b/src/App.php index 1b8c5788..e61d12f4 100755 --- a/src/App.php +++ b/src/App.php @@ -109,7 +109,7 @@ class App */ protected bool $compression = false; protected int $compressionMinSize = App::COMPRESSION_MIN_SIZE_DEFAULT; - protected mixed $compressionSupported; + protected mixed $compressionSupported = []; /** * App diff --git a/src/Response.php b/src/Response.php index 625db69c..3cbd13c9 100755 --- a/src/Response.php +++ b/src/Response.php @@ -260,7 +260,7 @@ class Response /** * @var mixed */ - protected mixed $compressionSupported; + protected mixed $compressionSupported = []; /** * Response constructor. From 72239fb95a65fc65e3a8e435f1645e234b00ea04 Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Thu, 7 Nov 2024 18:00:49 +0100 Subject: [PATCH 023/103] debug: compression --- src/Response.php | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/Response.php b/src/Response.php index 3cbd13c9..4e1c7f5b 100755 --- a/src/Response.php +++ b/src/Response.php @@ -2,6 +2,7 @@ namespace Utopia; +use Utopia\CLI\Console; use Utopia\Compression\Compression; class Response @@ -315,7 +316,7 @@ public function setCompressionMinSize(int $compressionMinSize): static /** * Set supported compression algorithms - * + * * @param mixed $compressionSupported */ public function setCompressionSupported(mixed $compressionSupported): static @@ -540,6 +541,10 @@ public function send(string $body = ''): void } // Compress body + Console::log('Accept-Encoding: '.$this->acceptEncoding . ' - ' . $this->compressionMinSize); + Console::log('Content-Type: '.$this->contentType . ' - ' . $this->compressed[$this->contentType]); + Console::log('Content-Length: '.strlen($body) . ' - ' . $this->compressionMinSize); + if ( !empty($this->acceptEncoding) && isset($this->compressed[$this->contentType]) && @@ -547,8 +552,15 @@ public function send(string $body = ''): void ) { $algorithm = Compression::fromAcceptEncoding($this->acceptEncoding, $this->compressionSupported); + Console::log('Compression Algorithm: '.($algorithm ? $algorithm->getName() : 'none')); + if ($algorithm) { + Console::log('Body before compression: '.strlen($body)); + $body = $algorithm->compress($body); + + Console::log('Body after compression: '.strlen($body)); + $this->addHeader('Content-Encoding', $algorithm->getContentEncoding()); $this->addHeader('Vary', 'Accept-Encoding'); } From 238c9c2040621f0d5286825d2c452310a1c21e2d Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Fri, 8 Nov 2024 13:10:14 +0100 Subject: [PATCH 024/103] fix: send headers after --- src/Response.php | 27 +++++++++------------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/src/Response.php b/src/Response.php index 4e1c7f5b..828eea45 100755 --- a/src/Response.php +++ b/src/Response.php @@ -532,19 +532,9 @@ public function send(string $body = ''): void $this->addHeader('Server', $serverHeader); $this->addHeader('X-Debug-Speed', (string) (microtime(true) - $this->startTime)); - $this->appendCookies()->appendHeaders(); - - // Send response - if ($this->disablePayload) { - $this->end(); - return; - } + $this->appendCookies(); // Compress body - Console::log('Accept-Encoding: '.$this->acceptEncoding . ' - ' . $this->compressionMinSize); - Console::log('Content-Type: '.$this->contentType . ' - ' . $this->compressed[$this->contentType]); - Console::log('Content-Length: '.strlen($body) . ' - ' . $this->compressionMinSize); - if ( !empty($this->acceptEncoding) && isset($this->compressed[$this->contentType]) && @@ -552,20 +542,21 @@ public function send(string $body = ''): void ) { $algorithm = Compression::fromAcceptEncoding($this->acceptEncoding, $this->compressionSupported); - Console::log('Compression Algorithm: '.($algorithm ? $algorithm->getName() : 'none')); - if ($algorithm) { - Console::log('Body before compression: '.strlen($body)); - $body = $algorithm->compress($body); - - Console::log('Body after compression: '.strlen($body)); - $this->addHeader('Content-Encoding', $algorithm->getContentEncoding()); $this->addHeader('Vary', 'Accept-Encoding'); } } + $this->appendHeaders(); + + // Send response + if ($this->disablePayload) { + $this->end(); + return; + } + $headerSize = strlen(implode("\n", $this->headers)); $bodyLength = strlen($body); $this->size += $headerSize + $bodyLength; From c91b5769ee0ccddec011c8ad02090470d2426ef2 Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Fri, 8 Nov 2024 13:18:17 +0100 Subject: [PATCH 025/103] chore: fmt --- src/Response.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Response.php b/src/Response.php index 828eea45..5210ed34 100755 --- a/src/Response.php +++ b/src/Response.php @@ -2,7 +2,6 @@ namespace Utopia; -use Utopia\CLI\Console; use Utopia\Compression\Compression; class Response From 58851dc88c4d8ac32757f30f1508cdf55a26cb4a Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Fri, 8 Nov 2024 13:23:25 +0100 Subject: [PATCH 026/103] chore: composer update --- composer.lock | 46 +++++++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/composer.lock b/composer.lock index 4677e800..efc9715a 100644 --- a/composer.lock +++ b/composer.lock @@ -8,16 +8,16 @@ "packages": [ { "name": "utopia-php/compression", - "version": "0.1.0", + "version": "0.1.1", "source": { "type": "git", "url": "https://github.com/utopia-php/compression.git", - "reference": "8c6d9bcb5b0972faa27e5bf70923c20403aaf25c" + "reference": "2ac5709e39823dbccb9fa66099ccebe809078c2e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/compression/zipball/8c6d9bcb5b0972faa27e5bf70923c20403aaf25c", - "reference": "8c6d9bcb5b0972faa27e5bf70923c20403aaf25c", + "url": "https://api.github.com/repos/utopia-php/compression/zipball/2ac5709e39823dbccb9fa66099ccebe809078c2e", + "reference": "2ac5709e39823dbccb9fa66099ccebe809078c2e", "shasum": "" }, "require": { @@ -48,9 +48,9 @@ ], "support": { "issues": "https://github.com/utopia-php/compression/issues", - "source": "https://github.com/utopia-php/compression/tree/0.1.0" + "source": "https://github.com/utopia-php/compression/tree/0.1.1" }, - "time": "2024-10-23T10:17:46+00:00" + "time": "2024-11-08T12:22:15+00:00" } ], "packages-dev": [ @@ -783,16 +783,16 @@ }, { "name": "phpstan/phpstan", - "version": "1.12.7", + "version": "1.12.8", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "dc2b9976bd8b0f84ec9b0e50cc35378551de7af0" + "reference": "f6a60a4d66142b8156c9da923f1972657bc4748c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/dc2b9976bd8b0f84ec9b0e50cc35378551de7af0", - "reference": "dc2b9976bd8b0f84ec9b0e50cc35378551de7af0", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/f6a60a4d66142b8156c9da923f1972657bc4748c", + "reference": "f6a60a4d66142b8156c9da923f1972657bc4748c", "shasum": "" }, "require": { @@ -837,7 +837,7 @@ "type": "github" } ], - "time": "2024-10-18T11:12:07+00:00" + "time": "2024-11-06T19:06:49+00:00" }, { "name": "phpunit/php-code-coverage", @@ -2442,16 +2442,16 @@ }, { "name": "symfony/console", - "version": "v7.1.6", + "version": "v7.1.7", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "bb5192af6edc797cbab5c8e8ecfea2fe5f421e57" + "reference": "3284aafcac338b6e86fd955ee4d794cbe434151a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/bb5192af6edc797cbab5c8e8ecfea2fe5f421e57", - "reference": "bb5192af6edc797cbab5c8e8ecfea2fe5f421e57", + "url": "https://api.github.com/repos/symfony/console/zipball/3284aafcac338b6e86fd955ee4d794cbe434151a", + "reference": "3284aafcac338b6e86fd955ee4d794cbe434151a", "shasum": "" }, "require": { @@ -2515,7 +2515,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v7.1.6" + "source": "https://github.com/symfony/console/tree/v7.1.7" }, "funding": [ { @@ -2531,7 +2531,7 @@ "type": "tidelift" } ], - "time": "2024-10-09T08:46:59+00:00" + "time": "2024-11-05T15:34:55+00:00" }, { "name": "symfony/deprecation-contracts", @@ -3117,16 +3117,16 @@ }, { "name": "symfony/process", - "version": "v7.1.6", + "version": "v7.1.7", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "6aaa189ddb4ff6b5de8fa3210f2fb42c87b4d12e" + "reference": "9b8a40b7289767aa7117e957573c2a535efe6585" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/6aaa189ddb4ff6b5de8fa3210f2fb42c87b4d12e", - "reference": "6aaa189ddb4ff6b5de8fa3210f2fb42c87b4d12e", + "url": "https://api.github.com/repos/symfony/process/zipball/9b8a40b7289767aa7117e957573c2a535efe6585", + "reference": "9b8a40b7289767aa7117e957573c2a535efe6585", "shasum": "" }, "require": { @@ -3158,7 +3158,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v7.1.6" + "source": "https://github.com/symfony/process/tree/v7.1.7" }, "funding": [ { @@ -3174,7 +3174,7 @@ "type": "tidelift" } ], - "time": "2024-09-25T14:20:29+00:00" + "time": "2024-11-06T09:25:12+00:00" }, { "name": "symfony/service-contracts", From 16b26aa72ab11177d4dcee70de0d5355ed632ea4 Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Fri, 8 Nov 2024 16:02:16 +0100 Subject: [PATCH 027/103] chore: bump compression --- composer.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/composer.lock b/composer.lock index efc9715a..2d7f5195 100644 --- a/composer.lock +++ b/composer.lock @@ -8,16 +8,16 @@ "packages": [ { "name": "utopia-php/compression", - "version": "0.1.1", + "version": "0.1.2", "source": { "type": "git", "url": "https://github.com/utopia-php/compression.git", - "reference": "2ac5709e39823dbccb9fa66099ccebe809078c2e" + "reference": "6062f70596415f8d5de40a589367b0eb2a435f98" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/compression/zipball/2ac5709e39823dbccb9fa66099ccebe809078c2e", - "reference": "2ac5709e39823dbccb9fa66099ccebe809078c2e", + "url": "https://api.github.com/repos/utopia-php/compression/zipball/6062f70596415f8d5de40a589367b0eb2a435f98", + "reference": "6062f70596415f8d5de40a589367b0eb2a435f98", "shasum": "" }, "require": { @@ -48,9 +48,9 @@ ], "support": { "issues": "https://github.com/utopia-php/compression/issues", - "source": "https://github.com/utopia-php/compression/tree/0.1.1" + "source": "https://github.com/utopia-php/compression/tree/0.1.2" }, - "time": "2024-11-08T12:22:15+00:00" + "time": "2024-11-08T14:59:54+00:00" } ], "packages-dev": [ From 00ce83d7e75a06f75380cc5a2130f99a8cb6bd88 Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Fri, 8 Nov 2024 19:30:20 +0100 Subject: [PATCH 028/103] feat: add header --- src/Response.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Response.php b/src/Response.php index 5210ed34..43e1dc74 100755 --- a/src/Response.php +++ b/src/Response.php @@ -529,7 +529,6 @@ public function send(string $body = ''): void $serverHeader = $this->headers['Server'] ?? 'Utopia/Http'; $this->addHeader('Server', $serverHeader); - $this->addHeader('X-Debug-Speed', (string) (microtime(true) - $this->startTime)); $this->appendCookies(); @@ -544,10 +543,12 @@ public function send(string $body = ''): void if ($algorithm) { $body = $algorithm->compress($body); $this->addHeader('Content-Encoding', $algorithm->getContentEncoding()); + $this->addHeader('X-Utopia-Compression', '1'); $this->addHeader('Vary', 'Accept-Encoding'); } } + $this->addHeader('X-Debug-Speed', (string) (microtime(true) - $this->startTime)); $this->appendHeaders(); // Send response From 7c9e4411bb7ae574a2975cc0ed722aa828628b2c Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Fri, 8 Nov 2024 19:45:35 +0100 Subject: [PATCH 029/103] chore: update to true --- src/Response.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Response.php b/src/Response.php index 43e1dc74..eccdb26b 100755 --- a/src/Response.php +++ b/src/Response.php @@ -543,7 +543,7 @@ public function send(string $body = ''): void if ($algorithm) { $body = $algorithm->compress($body); $this->addHeader('Content-Encoding', $algorithm->getContentEncoding()); - $this->addHeader('X-Utopia-Compression', '1'); + $this->addHeader('X-Utopia-Compression', 'true'); $this->addHeader('Vary', 'Accept-Encoding'); } } From 9d4b8f2efd2327086c93f0804a641c510cb08557 Mon Sep 17 00:00:00 2001 From: Fabian Gruber Date: Mon, 11 Nov 2024 16:16:58 +0100 Subject: [PATCH 030/103] feat: telemetry support --- composer.json | 9 +- composer.lock | 2240 +++++++++++++++++++++++++++++++++++++++-------- src/App.php | 38 +- src/Metrics.php | 62 ++ 4 files changed, 1986 insertions(+), 363 deletions(-) create mode 100644 src/Metrics.php diff --git a/composer.json b/composer.json index d856b786..d903e037 100644 --- a/composer.json +++ b/composer.json @@ -23,8 +23,15 @@ }, "require": { "php": ">=8.0", - "utopia-php/compression": "0.1.*" + "utopia-php/compression": "0.1.*", + "utopia-php/telemetry": "dev-initial-commit" }, + "repositories": [ + { + "type": "vcs", + "url": "https://github.com/utopia-php/telemetry" + } + ], "require-dev": { "phpunit/phpunit": "^9.5.25", "laravel/pint": "^1.2", diff --git a/composer.lock b/composer.lock index 2d7f5195..f8eb585a 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,1793 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "29435f73c84b4ebe3f18aa8bcd7e008e", + "content-hash": "28c0e77977607f0de243f49adfe583bc", "packages": [ + { + "name": "brick/math", + "version": "0.12.1", + "source": { + "type": "git", + "url": "https://github.com/brick/math.git", + "reference": "f510c0a40911935b77b86859eb5223d58d660df1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/brick/math/zipball/f510c0a40911935b77b86859eb5223d58d660df1", + "reference": "f510c0a40911935b77b86859eb5223d58d660df1", + "shasum": "" + }, + "require": { + "php": "^8.1" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.2", + "phpunit/phpunit": "^10.1", + "vimeo/psalm": "5.16.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Brick\\Math\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Arbitrary-precision arithmetic library", + "keywords": [ + "Arbitrary-precision", + "BigInteger", + "BigRational", + "arithmetic", + "bigdecimal", + "bignum", + "bignumber", + "brick", + "decimal", + "integer", + "math", + "mathematics", + "rational" + ], + "support": { + "issues": "https://github.com/brick/math/issues", + "source": "https://github.com/brick/math/tree/0.12.1" + }, + "funding": [ + { + "url": "https://github.com/BenMorel", + "type": "github" + } + ], + "time": "2023-11-29T23:19:16+00:00" + }, + { + "name": "composer/semver", + "version": "3.4.3", + "source": { + "type": "git", + "url": "https://github.com/composer/semver.git", + "reference": "4313d26ada5e0c4edfbd1dc481a92ff7bff91f12" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/semver/zipball/4313d26ada5e0c4edfbd1dc481a92ff7bff91f12", + "reference": "4313d26ada5e0c4edfbd1dc481a92ff7bff91f12", + "shasum": "" + }, + "require": { + "php": "^5.3.2 || ^7.0 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^1.11", + "symfony/phpunit-bridge": "^3 || ^7" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\Semver\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nils Adermann", + "email": "naderman@naderman.de", + "homepage": "http://www.naderman.de" + }, + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + }, + { + "name": "Rob Bast", + "email": "rob.bast@gmail.com", + "homepage": "http://robbast.nl" + } + ], + "description": "Semver library that offers utilities, version constraint parsing and validation.", + "keywords": [ + "semantic", + "semver", + "validation", + "versioning" + ], + "support": { + "irc": "ircs://irc.libera.chat:6697/composer", + "issues": "https://github.com/composer/semver/issues", + "source": "https://github.com/composer/semver/tree/3.4.3" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2024-09-19T14:15:21+00:00" + }, + { + "name": "google/protobuf", + "version": "v4.28.3", + "source": { + "type": "git", + "url": "https://github.com/protocolbuffers/protobuf-php.git", + "reference": "c5c311e0f3d89928251ac5a2f0e3db283612c100" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/protocolbuffers/protobuf-php/zipball/c5c311e0f3d89928251ac5a2f0e3db283612c100", + "reference": "c5c311e0f3d89928251ac5a2f0e3db283612c100", + "shasum": "" + }, + "require": { + "php": ">=7.0.0" + }, + "require-dev": { + "phpunit/phpunit": ">=5.0.0" + }, + "suggest": { + "ext-bcmath": "Need to support JSON deserialization" + }, + "type": "library", + "autoload": { + "psr-4": { + "Google\\Protobuf\\": "src/Google/Protobuf", + "GPBMetadata\\Google\\Protobuf\\": "src/GPBMetadata/Google/Protobuf" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "proto library for PHP", + "homepage": "https://developers.google.com/protocol-buffers/", + "keywords": [ + "proto" + ], + "support": { + "source": "https://github.com/protocolbuffers/protobuf-php/tree/v4.28.3" + }, + "time": "2024-10-22T22:27:17+00:00" + }, + { + "name": "nyholm/psr7", + "version": "1.8.2", + "source": { + "type": "git", + "url": "https://github.com/Nyholm/psr7.git", + "reference": "a71f2b11690f4b24d099d6b16690a90ae14fc6f3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Nyholm/psr7/zipball/a71f2b11690f4b24d099d6b16690a90ae14fc6f3", + "reference": "a71f2b11690f4b24d099d6b16690a90ae14fc6f3", + "shasum": "" + }, + "require": { + "php": ">=7.2", + "psr/http-factory": "^1.0", + "psr/http-message": "^1.1 || ^2.0" + }, + "provide": { + "php-http/message-factory-implementation": "1.0", + "psr/http-factory-implementation": "1.0", + "psr/http-message-implementation": "1.0" + }, + "require-dev": { + "http-interop/http-factory-tests": "^0.9", + "php-http/message-factory": "^1.0", + "php-http/psr7-integration-tests": "^1.0", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.4", + "symfony/error-handler": "^4.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.8-dev" + } + }, + "autoload": { + "psr-4": { + "Nyholm\\Psr7\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com" + }, + { + "name": "Martijn van der Ven", + "email": "martijn@vanderven.se" + } + ], + "description": "A fast PHP7 implementation of PSR-7", + "homepage": "https://tnyholm.se", + "keywords": [ + "psr-17", + "psr-7" + ], + "support": { + "issues": "https://github.com/Nyholm/psr7/issues", + "source": "https://github.com/Nyholm/psr7/tree/1.8.2" + }, + "funding": [ + { + "url": "https://github.com/Zegnat", + "type": "github" + }, + { + "url": "https://github.com/nyholm", + "type": "github" + } + ], + "time": "2024-09-09T07:06:30+00:00" + }, + { + "name": "nyholm/psr7-server", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://github.com/Nyholm/psr7-server.git", + "reference": "4335801d851f554ca43fa6e7d2602141538854dc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Nyholm/psr7-server/zipball/4335801d851f554ca43fa6e7d2602141538854dc", + "reference": "4335801d851f554ca43fa6e7d2602141538854dc", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0", + "psr/http-factory": "^1.0", + "psr/http-message": "^1.0 || ^2.0" + }, + "require-dev": { + "nyholm/nsa": "^1.1", + "nyholm/psr7": "^1.3", + "phpunit/phpunit": "^7.0 || ^8.5 || ^9.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Nyholm\\Psr7Server\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com" + }, + { + "name": "Martijn van der Ven", + "email": "martijn@vanderven.se" + } + ], + "description": "Helper classes to handle PSR-7 server requests", + "homepage": "http://tnyholm.se", + "keywords": [ + "psr-17", + "psr-7" + ], + "support": { + "issues": "https://github.com/Nyholm/psr7-server/issues", + "source": "https://github.com/Nyholm/psr7-server/tree/1.1.0" + }, + "funding": [ + { + "url": "https://github.com/Zegnat", + "type": "github" + }, + { + "url": "https://github.com/nyholm", + "type": "github" + } + ], + "time": "2023-11-08T09:30:43+00:00" + }, + { + "name": "open-telemetry/api", + "version": "1.1.1", + "source": { + "type": "git", + "url": "https://github.com/opentelemetry-php/api.git", + "reference": "542064815d38a6df55af7957cd6f1d7d967c99c6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/opentelemetry-php/api/zipball/542064815d38a6df55af7957cd6f1d7d967c99c6", + "reference": "542064815d38a6df55af7957cd6f1d7d967c99c6", + "shasum": "" + }, + "require": { + "open-telemetry/context": "^1.0", + "php": "^8.1", + "psr/log": "^1.1|^2.0|^3.0", + "symfony/polyfill-php82": "^1.26" + }, + "conflict": { + "open-telemetry/sdk": "<=1.0.8" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.1.x-dev" + }, + "spi": { + "OpenTelemetry\\API\\Instrumentation\\AutoInstrumentation\\HookManagerInterface": [ + "OpenTelemetry\\API\\Instrumentation\\AutoInstrumentation\\ExtensionHookManager" + ] + } + }, + "autoload": { + "files": [ + "Trace/functions.php" + ], + "psr-4": { + "OpenTelemetry\\API\\": "." + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "opentelemetry-php contributors", + "homepage": "https://github.com/open-telemetry/opentelemetry-php/graphs/contributors" + } + ], + "description": "API for OpenTelemetry PHP.", + "keywords": [ + "Metrics", + "api", + "apm", + "logging", + "opentelemetry", + "otel", + "tracing" + ], + "support": { + "chat": "https://app.slack.com/client/T08PSQ7BQ/C01NFPCV44V", + "docs": "https://opentelemetry.io/docs/php", + "issues": "https://github.com/open-telemetry/opentelemetry-php/issues", + "source": "https://github.com/open-telemetry/opentelemetry-php" + }, + "time": "2024-10-15T22:42:37+00:00" + }, + { + "name": "open-telemetry/context", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://github.com/opentelemetry-php/context.git", + "reference": "0cba875ea1953435f78aec7f1d75afa87bdbf7f3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/opentelemetry-php/context/zipball/0cba875ea1953435f78aec7f1d75afa87bdbf7f3", + "reference": "0cba875ea1953435f78aec7f1d75afa87bdbf7f3", + "shasum": "" + }, + "require": { + "php": "^8.1", + "symfony/polyfill-php82": "^1.26" + }, + "suggest": { + "ext-ffi": "To allow context switching in Fibers" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.0.x-dev" + } + }, + "autoload": { + "files": [ + "fiber/initialize_fiber_handler.php" + ], + "psr-4": { + "OpenTelemetry\\Context\\": "." + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "opentelemetry-php contributors", + "homepage": "https://github.com/open-telemetry/opentelemetry-php/graphs/contributors" + } + ], + "description": "Context implementation for OpenTelemetry PHP.", + "keywords": [ + "Context", + "opentelemetry", + "otel" + ], + "support": { + "chat": "https://app.slack.com/client/T08PSQ7BQ/C01NFPCV44V", + "docs": "https://opentelemetry.io/docs/php", + "issues": "https://github.com/open-telemetry/opentelemetry-php/issues", + "source": "https://github.com/open-telemetry/opentelemetry-php" + }, + "time": "2024-08-21T00:29:20+00:00" + }, + { + "name": "open-telemetry/exporter-otlp", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://github.com/opentelemetry-php/exporter-otlp.git", + "reference": "9b6de12204f25f8ab9540b46d6e7b5151897ce18" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/opentelemetry-php/exporter-otlp/zipball/9b6de12204f25f8ab9540b46d6e7b5151897ce18", + "reference": "9b6de12204f25f8ab9540b46d6e7b5151897ce18", + "shasum": "" + }, + "require": { + "open-telemetry/api": "^1.0", + "open-telemetry/gen-otlp-protobuf": "^1.1", + "open-telemetry/sdk": "^1.0", + "php": "^8.1", + "php-http/discovery": "^1.14" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.0.x-dev" + } + }, + "autoload": { + "files": [ + "_register.php" + ], + "psr-4": { + "OpenTelemetry\\Contrib\\Otlp\\": "." + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "opentelemetry-php contributors", + "homepage": "https://github.com/open-telemetry/opentelemetry-php/graphs/contributors" + } + ], + "description": "OTLP exporter for OpenTelemetry.", + "keywords": [ + "Metrics", + "exporter", + "gRPC", + "http", + "opentelemetry", + "otel", + "otlp", + "tracing" + ], + "support": { + "chat": "https://app.slack.com/client/T08PSQ7BQ/C01NFPCV44V", + "docs": "https://opentelemetry.io/docs/php", + "issues": "https://github.com/open-telemetry/opentelemetry-php/issues", + "source": "https://github.com/open-telemetry/opentelemetry-php" + }, + "time": "2024-04-30T18:28:30+00:00" + }, + { + "name": "open-telemetry/gen-otlp-protobuf", + "version": "1.2.1", + "source": { + "type": "git", + "url": "https://github.com/opentelemetry-php/gen-otlp-protobuf.git", + "reference": "66c3b98e998a726691c92e6405a82e6e7b8b169d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/opentelemetry-php/gen-otlp-protobuf/zipball/66c3b98e998a726691c92e6405a82e6e7b8b169d", + "reference": "66c3b98e998a726691c92e6405a82e6e7b8b169d", + "shasum": "" + }, + "require": { + "google/protobuf": "^3.22 || ^4.0", + "php": "^8.0" + }, + "suggest": { + "ext-protobuf": "For better performance, when dealing with the protobuf format" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Opentelemetry\\Proto\\": "Opentelemetry/Proto/", + "GPBMetadata\\Opentelemetry\\": "GPBMetadata/Opentelemetry/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "opentelemetry-php contributors", + "homepage": "https://github.com/open-telemetry/opentelemetry-php/graphs/contributors" + } + ], + "description": "PHP protobuf files for communication with OpenTelemetry OTLP collectors/servers.", + "keywords": [ + "Metrics", + "apm", + "gRPC", + "logging", + "opentelemetry", + "otel", + "otlp", + "protobuf", + "tracing" + ], + "support": { + "chat": "https://app.slack.com/client/T08PSQ7BQ/C01NFPCV44V", + "docs": "https://opentelemetry.io/docs/php", + "issues": "https://github.com/open-telemetry/opentelemetry-php/issues", + "source": "https://github.com/open-telemetry/opentelemetry-php" + }, + "time": "2024-10-30T11:49:49+00:00" + }, + { + "name": "open-telemetry/sdk", + "version": "1.1.2", + "source": { + "type": "git", + "url": "https://github.com/opentelemetry-php/sdk.git", + "reference": "fb0ff8d8279a3776bd604791e2531dd0cc147e8b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/opentelemetry-php/sdk/zipball/fb0ff8d8279a3776bd604791e2531dd0cc147e8b", + "reference": "fb0ff8d8279a3776bd604791e2531dd0cc147e8b", + "shasum": "" + }, + "require": { + "ext-json": "*", + "nyholm/psr7-server": "^1.1", + "open-telemetry/api": "~1.0 || ~1.1", + "open-telemetry/context": "^1.0", + "open-telemetry/sem-conv": "^1.0", + "php": "^8.1", + "php-http/discovery": "^1.14", + "psr/http-client": "^1.0", + "psr/http-client-implementation": "^1.0", + "psr/http-factory-implementation": "^1.0", + "psr/http-message": "^1.0.1|^2.0", + "psr/log": "^1.1|^2.0|^3.0", + "ramsey/uuid": "^3.0 || ^4.0", + "symfony/polyfill-mbstring": "^1.23", + "symfony/polyfill-php82": "^1.26", + "tbachert/spi": "^1.0.1" + }, + "suggest": { + "ext-gmp": "To support unlimited number of synchronous metric readers", + "ext-mbstring": "To increase performance of string operations", + "open-telemetry/sdk-configuration": "File-based OpenTelemetry SDK configuration" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.0.x-dev" + }, + "spi": { + "OpenTelemetry\\API\\Instrumentation\\AutoInstrumentation\\HookManagerInterface": [ + "OpenTelemetry\\API\\Instrumentation\\AutoInstrumentation\\ExtensionHookManager" + ] + } + }, + "autoload": { + "files": [ + "Common/Util/functions.php", + "Logs/Exporter/_register.php", + "Metrics/MetricExporter/_register.php", + "Propagation/_register.php", + "Trace/SpanExporter/_register.php", + "Common/Dev/Compatibility/_load.php", + "_autoload.php" + ], + "psr-4": { + "OpenTelemetry\\SDK\\": "." + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "opentelemetry-php contributors", + "homepage": "https://github.com/open-telemetry/opentelemetry-php/graphs/contributors" + } + ], + "description": "SDK for OpenTelemetry PHP.", + "keywords": [ + "Metrics", + "apm", + "logging", + "opentelemetry", + "otel", + "sdk", + "tracing" + ], + "support": { + "chat": "https://app.slack.com/client/T08PSQ7BQ/C01NFPCV44V", + "docs": "https://opentelemetry.io/docs/php", + "issues": "https://github.com/open-telemetry/opentelemetry-php/issues", + "source": "https://github.com/open-telemetry/opentelemetry-php" + }, + "time": "2024-10-18T21:01:35+00:00" + }, + { + "name": "open-telemetry/sem-conv", + "version": "1.27.1", + "source": { + "type": "git", + "url": "https://github.com/opentelemetry-php/sem-conv.git", + "reference": "1dba705fea74bc0718d04be26090e3697e56f4e6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/opentelemetry-php/sem-conv/zipball/1dba705fea74bc0718d04be26090e3697e56f4e6", + "reference": "1dba705fea74bc0718d04be26090e3697e56f4e6", + "shasum": "" + }, + "require": { + "php": "^8.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "OpenTelemetry\\SemConv\\": "." + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "opentelemetry-php contributors", + "homepage": "https://github.com/open-telemetry/opentelemetry-php/graphs/contributors" + } + ], + "description": "Semantic conventions for OpenTelemetry PHP.", + "keywords": [ + "Metrics", + "apm", + "logging", + "opentelemetry", + "otel", + "semantic conventions", + "semconv", + "tracing" + ], + "support": { + "chat": "https://app.slack.com/client/T08PSQ7BQ/C01NFPCV44V", + "docs": "https://opentelemetry.io/docs/php", + "issues": "https://github.com/open-telemetry/opentelemetry-php/issues", + "source": "https://github.com/open-telemetry/opentelemetry-php" + }, + "time": "2024-08-28T09:20:31+00:00" + }, + { + "name": "php-http/discovery", + "version": "1.20.0", + "source": { + "type": "git", + "url": "https://github.com/php-http/discovery.git", + "reference": "82fe4c73ef3363caed49ff8dd1539ba06044910d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-http/discovery/zipball/82fe4c73ef3363caed49ff8dd1539ba06044910d", + "reference": "82fe4c73ef3363caed49ff8dd1539ba06044910d", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^1.0|^2.0", + "php": "^7.1 || ^8.0" + }, + "conflict": { + "nyholm/psr7": "<1.0", + "zendframework/zend-diactoros": "*" + }, + "provide": { + "php-http/async-client-implementation": "*", + "php-http/client-implementation": "*", + "psr/http-client-implementation": "*", + "psr/http-factory-implementation": "*", + "psr/http-message-implementation": "*" + }, + "require-dev": { + "composer/composer": "^1.0.2|^2.0", + "graham-campbell/phpspec-skip-example-extension": "^5.0", + "php-http/httplug": "^1.0 || ^2.0", + "php-http/message-factory": "^1.0", + "phpspec/phpspec": "^5.1 || ^6.1 || ^7.3", + "sebastian/comparator": "^3.0.5 || ^4.0.8", + "symfony/phpunit-bridge": "^6.4.4 || ^7.0.1" + }, + "type": "composer-plugin", + "extra": { + "class": "Http\\Discovery\\Composer\\Plugin", + "plugin-optional": true + }, + "autoload": { + "psr-4": { + "Http\\Discovery\\": "src/" + }, + "exclude-from-classmap": [ + "src/Composer/Plugin.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com" + } + ], + "description": "Finds and installs PSR-7, PSR-17, PSR-18 and HTTPlug implementations", + "homepage": "http://php-http.org", + "keywords": [ + "adapter", + "client", + "discovery", + "factory", + "http", + "message", + "psr17", + "psr7" + ], + "support": { + "issues": "https://github.com/php-http/discovery/issues", + "source": "https://github.com/php-http/discovery/tree/1.20.0" + }, + "time": "2024-10-02T11:20:13+00:00" + }, + { + "name": "psr/container", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "shasum": "" + }, + "require": { + "php": ">=7.4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", + "keywords": [ + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" + ], + "support": { + "issues": "https://github.com/php-fig/container/issues", + "source": "https://github.com/php-fig/container/tree/2.0.2" + }, + "time": "2021-11-05T16:47:00+00:00" + }, + { + "name": "psr/http-client", + "version": "1.0.3", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-client.git", + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-client/zipball/bb5906edc1c324c9a05aa0873d40117941e5fa90", + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0", + "psr/http-message": "^1.0 || ^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Client\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP clients", + "homepage": "https://github.com/php-fig/http-client", + "keywords": [ + "http", + "http-client", + "psr", + "psr-18" + ], + "support": { + "source": "https://github.com/php-fig/http-client" + }, + "time": "2023-09-23T14:17:50+00:00" + }, + { + "name": "psr/http-factory", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-factory.git", + "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-factory/zipball/2b4765fddfe3b508ac62f829e852b1501d3f6e8a", + "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a", + "shasum": "" + }, + "require": { + "php": ">=7.1", + "psr/http-message": "^1.0 || ^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "PSR-17: Common interfaces for PSR-7 HTTP message factories", + "keywords": [ + "factory", + "http", + "message", + "psr", + "psr-17", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-factory" + }, + "time": "2024-04-15T12:06:14+00:00" + }, + { + "name": "psr/http-message", + "version": "2.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-message.git", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP messages", + "homepage": "https://github.com/php-fig/http-message", + "keywords": [ + "http", + "http-message", + "psr", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-message/tree/2.0" + }, + "time": "2023-04-04T09:54:51+00:00" + }, + { + "name": "psr/log", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "support": { + "source": "https://github.com/php-fig/log/tree/3.0.2" + }, + "time": "2024-09-11T13:17:53+00:00" + }, + { + "name": "ramsey/collection", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/ramsey/collection.git", + "reference": "a4b48764bfbb8f3a6a4d1aeb1a35bb5e9ecac4a5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ramsey/collection/zipball/a4b48764bfbb8f3a6a4d1aeb1a35bb5e9ecac4a5", + "reference": "a4b48764bfbb8f3a6a4d1aeb1a35bb5e9ecac4a5", + "shasum": "" + }, + "require": { + "php": "^8.1" + }, + "require-dev": { + "captainhook/plugin-composer": "^5.3", + "ergebnis/composer-normalize": "^2.28.3", + "fakerphp/faker": "^1.21", + "hamcrest/hamcrest-php": "^2.0", + "jangregor/phpstan-prophecy": "^1.0", + "mockery/mockery": "^1.5", + "php-parallel-lint/php-console-highlighter": "^1.0", + "php-parallel-lint/php-parallel-lint": "^1.3", + "phpcsstandards/phpcsutils": "^1.0.0-rc1", + "phpspec/prophecy-phpunit": "^2.0", + "phpstan/extension-installer": "^1.2", + "phpstan/phpstan": "^1.9", + "phpstan/phpstan-mockery": "^1.1", + "phpstan/phpstan-phpunit": "^1.3", + "phpunit/phpunit": "^9.5", + "psalm/plugin-mockery": "^1.1", + "psalm/plugin-phpunit": "^0.18.4", + "ramsey/coding-standard": "^2.0.3", + "ramsey/conventional-commits": "^1.3", + "vimeo/psalm": "^5.4" + }, + "type": "library", + "extra": { + "captainhook": { + "force-install": true + }, + "ramsey/conventional-commits": { + "configFile": "conventional-commits.json" + } + }, + "autoload": { + "psr-4": { + "Ramsey\\Collection\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ben Ramsey", + "email": "ben@benramsey.com", + "homepage": "https://benramsey.com" + } + ], + "description": "A PHP library for representing and manipulating collections.", + "keywords": [ + "array", + "collection", + "hash", + "map", + "queue", + "set" + ], + "support": { + "issues": "https://github.com/ramsey/collection/issues", + "source": "https://github.com/ramsey/collection/tree/2.0.0" + }, + "funding": [ + { + "url": "https://github.com/ramsey", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/ramsey/collection", + "type": "tidelift" + } + ], + "time": "2022-12-31T21:50:55+00:00" + }, + { + "name": "ramsey/uuid", + "version": "4.7.6", + "source": { + "type": "git", + "url": "https://github.com/ramsey/uuid.git", + "reference": "91039bc1faa45ba123c4328958e620d382ec7088" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ramsey/uuid/zipball/91039bc1faa45ba123c4328958e620d382ec7088", + "reference": "91039bc1faa45ba123c4328958e620d382ec7088", + "shasum": "" + }, + "require": { + "brick/math": "^0.8.8 || ^0.9 || ^0.10 || ^0.11 || ^0.12", + "ext-json": "*", + "php": "^8.0", + "ramsey/collection": "^1.2 || ^2.0" + }, + "replace": { + "rhumsaa/uuid": "self.version" + }, + "require-dev": { + "captainhook/captainhook": "^5.10", + "captainhook/plugin-composer": "^5.3", + "dealerdirect/phpcodesniffer-composer-installer": "^0.7.0", + "doctrine/annotations": "^1.8", + "ergebnis/composer-normalize": "^2.15", + "mockery/mockery": "^1.3", + "paragonie/random-lib": "^2", + "php-mock/php-mock": "^2.2", + "php-mock/php-mock-mockery": "^1.3", + "php-parallel-lint/php-parallel-lint": "^1.1", + "phpbench/phpbench": "^1.0", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-mockery": "^1.1", + "phpstan/phpstan-phpunit": "^1.1", + "phpunit/phpunit": "^8.5 || ^9", + "ramsey/composer-repl": "^1.4", + "slevomat/coding-standard": "^8.4", + "squizlabs/php_codesniffer": "^3.5", + "vimeo/psalm": "^4.9" + }, + "suggest": { + "ext-bcmath": "Enables faster math with arbitrary-precision integers using BCMath.", + "ext-gmp": "Enables faster math with arbitrary-precision integers using GMP.", + "ext-uuid": "Enables the use of PeclUuidTimeGenerator and PeclUuidRandomGenerator.", + "paragonie/random-lib": "Provides RandomLib for use with the RandomLibAdapter", + "ramsey/uuid-doctrine": "Allows the use of Ramsey\\Uuid\\Uuid as Doctrine field type." + }, + "type": "library", + "extra": { + "captainhook": { + "force-install": true + } + }, + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "Ramsey\\Uuid\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A PHP library for generating and working with universally unique identifiers (UUIDs).", + "keywords": [ + "guid", + "identifier", + "uuid" + ], + "support": { + "issues": "https://github.com/ramsey/uuid/issues", + "source": "https://github.com/ramsey/uuid/tree/4.7.6" + }, + "funding": [ + { + "url": "https://github.com/ramsey", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/ramsey/uuid", + "type": "tidelift" + } + ], + "time": "2024-04-27T21:32:50+00:00" + }, + { + "name": "symfony/deprecation-contracts", + "version": "v3.5.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1", + "reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.5-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "files": [ + "function.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "A generic function and convention to trigger deprecation notices", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.5.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-04-18T09:32:20+00:00" + }, + { + "name": "symfony/http-client", + "version": "v7.1.7", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-client.git", + "reference": "90ab2a4992dcf5d1f19a9b8737eba36a7c305fd0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-client/zipball/90ab2a4992dcf5d1f19a9b8737eba36a7c305fd0", + "reference": "90ab2a4992dcf5d1f19a9b8737eba36a7c305fd0", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "psr/log": "^1|^2|^3", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/http-client-contracts": "^3.4.1", + "symfony/service-contracts": "^2.5|^3" + }, + "conflict": { + "php-http/discovery": "<1.15", + "symfony/http-foundation": "<6.4" + }, + "provide": { + "php-http/async-client-implementation": "*", + "php-http/client-implementation": "*", + "psr/http-client-implementation": "1.0", + "symfony/http-client-implementation": "3.0" + }, + "require-dev": { + "amphp/amp": "^2.5", + "amphp/http-client": "^4.2.1", + "amphp/http-tunnel": "^1.0", + "amphp/socket": "^1.1", + "guzzlehttp/promises": "^1.4|^2.0", + "nyholm/psr7": "^1.0", + "php-http/httplug": "^1.0|^2.0", + "psr/http-client": "^1.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/messenger": "^6.4|^7.0", + "symfony/process": "^6.4|^7.0", + "symfony/rate-limiter": "^6.4|^7.0", + "symfony/stopwatch": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\HttpClient\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides powerful methods to fetch HTTP resources synchronously or asynchronously", + "homepage": "https://symfony.com", + "keywords": [ + "http" + ], + "support": { + "source": "https://github.com/symfony/http-client/tree/v7.1.7" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-11-05T16:45:54+00:00" + }, + { + "name": "symfony/http-client-contracts", + "version": "v3.5.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-client-contracts.git", + "reference": "20414d96f391677bf80078aa55baece78b82647d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-client-contracts/zipball/20414d96f391677bf80078aa55baece78b82647d", + "reference": "20414d96f391677bf80078aa55baece78b82647d", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.5-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\HttpClient\\": "" + }, + "exclude-from-classmap": [ + "/Test/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to HTTP clients", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/http-client-contracts/tree/v3.5.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-04-18T09:32:20+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.31.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/85181ba99b2345b0ef10ce42ecac37612d9fd341", + "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "provide": { + "ext-mbstring": "*" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.31.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/polyfill-php82", + "version": "v1.31.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php82.git", + "reference": "5d2ed36f7734637dacc025f179698031951b1692" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php82/zipball/5d2ed36f7734637dacc025f179698031951b1692", + "reference": "5d2ed36f7734637dacc025f179698031951b1692", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php82\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.2+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php82/tree/v1.31.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/service-contracts", + "version": "v3.5.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/service-contracts.git", + "reference": "bd1d9e59a81d8fa4acdcea3f617c581f7475a80f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/bd1d9e59a81d8fa4acdcea3f617c581f7475a80f", + "reference": "bd1d9e59a81d8fa4acdcea3f617c581f7475a80f", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/container": "^1.1|^2.0", + "symfony/deprecation-contracts": "^2.5|^3" + }, + "conflict": { + "ext-psr": "<1.1|>=2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.5-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Service\\": "" + }, + "exclude-from-classmap": [ + "/Test/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to writing services", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/service-contracts/tree/v3.5.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-04-18T09:32:20+00:00" + }, + { + "name": "tbachert/spi", + "version": "v1.0.2", + "source": { + "type": "git", + "url": "https://github.com/Nevay/spi.git", + "reference": "2ddfaf815dafb45791a61b08170de8d583c16062" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Nevay/spi/zipball/2ddfaf815dafb45791a61b08170de8d583c16062", + "reference": "2ddfaf815dafb45791a61b08170de8d583c16062", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^2.0", + "composer/semver": "^1.0 || ^2.0 || ^3.0", + "php": "^8.1" + }, + "require-dev": { + "composer/composer": "^2.0", + "infection/infection": "^0.27.9", + "phpunit/phpunit": "^10.5", + "psalm/phar": "^5.18" + }, + "type": "composer-plugin", + "extra": { + "branch-alias": { + "dev-main": "0.2.x-dev" + }, + "class": "Nevay\\SPI\\Composer\\Plugin", + "plugin-optional": true + }, + "autoload": { + "psr-4": { + "Nevay\\SPI\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "description": "Service provider loading facility", + "keywords": [ + "service provider" + ], + "support": { + "issues": "https://github.com/Nevay/spi/issues", + "source": "https://github.com/Nevay/spi/tree/v1.0.2" + }, + "time": "2024-10-04T16:36:12+00:00" + }, { "name": "utopia-php/compression", "version": "0.1.2", @@ -51,6 +1836,72 @@ "source": "https://github.com/utopia-php/compression/tree/0.1.2" }, "time": "2024-11-08T14:59:54+00:00" + }, + { + "name": "utopia-php/telemetry", + "version": "dev-initial-commit", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/telemetry.git", + "reference": "654896edcefa4cc4ce61af0944bf732febd62f95" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/telemetry/zipball/654896edcefa4cc4ce61af0944bf732febd62f95", + "reference": "654896edcefa4cc4ce61af0944bf732febd62f95", + "shasum": "" + }, + "require": { + "ext-opentelemetry": "*", + "ext-protobuf": "*", + "nyholm/psr7": "^1.8", + "open-telemetry/exporter-otlp": "^1.1", + "open-telemetry/sdk": "^1.1", + "php": ">=8.0", + "symfony/http-client": "^7.1" + }, + "require-dev": { + "laravel/pint": "^1.2", + "phpbench/phpbench": "^1.2", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^9.5.25" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\": "src/" + } + }, + "scripts": { + "lint": [ + "vendor/bin/pint --test" + ], + "format": [ + "vendor/bin/pint" + ], + "check": [ + "vendor/bin/phpstan analyse -c phpstan.neon" + ], + "test": [ + "vendor/bin/phpunit --configuration phpunit.xml" + ], + "bench": [ + "vendor/bin/phpbench run --report=benchmark" + ] + }, + "license": [ + "MIT" + ], + "keywords": [ + "framework", + "php", + "upf" + ], + "support": { + "source": "https://github.com/utopia-php/telemetry/tree/initial-commit", + "issues": "https://github.com/utopia-php/telemetry/issues" + }, + "time": "2024-11-11T15:15:39+00:00" } ], "packages-dev": [ @@ -345,16 +2196,16 @@ }, { "name": "myclabs/deep-copy", - "version": "1.12.0", + "version": "1.12.1", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c" + "reference": "123267b2c49fbf30d78a7b2d333f6be754b94845" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c", - "reference": "3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/123267b2c49fbf30d78a7b2d333f6be754b94845", + "reference": "123267b2c49fbf30d78a7b2d333f6be754b94845", "shasum": "" }, "require": { @@ -393,7 +2244,7 @@ ], "support": { "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.12.0" + "source": "https://github.com/myclabs/DeepCopy/tree/1.12.1" }, "funding": [ { @@ -401,7 +2252,7 @@ "type": "tidelift" } ], - "time": "2024-06-12T14:39:25+00:00" + "time": "2024-11-08T17:47:46+00:00" }, { "name": "nikic/php-parser", @@ -783,16 +2634,16 @@ }, { "name": "phpstan/phpstan", - "version": "1.12.8", + "version": "1.12.9", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "f6a60a4d66142b8156c9da923f1972657bc4748c" + "reference": "ceb937fb39a92deabc02d20709cf14b2c452502c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/f6a60a4d66142b8156c9da923f1972657bc4748c", - "reference": "f6a60a4d66142b8156c9da923f1972657bc4748c", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/ceb937fb39a92deabc02d20709cf14b2c452502c", + "reference": "ceb937fb39a92deabc02d20709cf14b2c452502c", "shasum": "" }, "require": { @@ -837,7 +2688,7 @@ "type": "github" } ], - "time": "2024-11-06T19:06:49+00:00" + "time": "2024-11-10T17:10:04+00:00" }, { "name": "phpunit/php-code-coverage", @@ -1262,119 +3113,17 @@ "time": "2024-09-19T10:50:18+00:00" }, { - "name": "psr/cache", - "version": "3.0.0", - "source": { - "type": "git", - "url": "https://github.com/php-fig/cache.git", - "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/cache/zipball/aa5030cfa5405eccfdcb1083ce040c2cb8d253bf", - "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf", - "shasum": "" - }, - "require": { - "php": ">=8.0.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Cache\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" - } - ], - "description": "Common interface for caching libraries", - "keywords": [ - "cache", - "psr", - "psr-6" - ], - "support": { - "source": "https://github.com/php-fig/cache/tree/3.0.0" - }, - "time": "2021-02-03T23:26:27+00:00" - }, - { - "name": "psr/container", - "version": "2.0.2", - "source": { - "type": "git", - "url": "https://github.com/php-fig/container.git", - "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", - "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", - "shasum": "" - }, - "require": { - "php": ">=7.4.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Container\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" - } - ], - "description": "Common Container Interface (PHP FIG PSR-11)", - "homepage": "https://github.com/php-fig/container", - "keywords": [ - "PSR-11", - "container", - "container-interface", - "container-interop", - "psr" - ], - "support": { - "issues": "https://github.com/php-fig/container/issues", - "source": "https://github.com/php-fig/container/tree/2.0.2" - }, - "time": "2021-11-05T16:47:00+00:00" - }, - { - "name": "psr/log", - "version": "3.0.2", + "name": "psr/cache", + "version": "3.0.0", "source": { "type": "git", - "url": "https://github.com/php-fig/log.git", - "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3" + "url": "https://github.com/php-fig/cache.git", + "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", - "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "url": "https://api.github.com/repos/php-fig/cache/zipball/aa5030cfa5405eccfdcb1083ce040c2cb8d253bf", + "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf", "shasum": "" }, "require": { @@ -1383,12 +3132,12 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.x-dev" + "dev-master": "1.0.x-dev" } }, "autoload": { "psr-4": { - "Psr\\Log\\": "src" + "Psr\\Cache\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -1401,17 +3150,16 @@ "homepage": "https://www.php-fig.org/" } ], - "description": "Common interface for logging libraries", - "homepage": "https://github.com/php-fig/log", + "description": "Common interface for caching libraries", "keywords": [ - "log", + "cache", "psr", - "psr-3" + "psr-6" ], "support": { - "source": "https://github.com/php-fig/log/tree/3.0.2" + "source": "https://github.com/php-fig/cache/tree/3.0.0" }, - "time": "2024-09-11T13:17:53+00:00" + "time": "2021-02-03T23:26:27+00:00" }, { "name": "sebastian/cli-parser", @@ -2533,73 +4281,6 @@ ], "time": "2024-11-05T15:34:55+00:00" }, - { - "name": "symfony/deprecation-contracts", - "version": "v3.5.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1", - "reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1", - "shasum": "" - }, - "require": { - "php": ">=8.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "3.5-dev" - }, - "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" - } - }, - "autoload": { - "files": [ - "function.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "A generic function and convention to trigger deprecation notices", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v3.5.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-04-18T09:32:20+00:00" - }, { "name": "symfony/filesystem", "version": "v7.1.6", @@ -3035,86 +4716,6 @@ ], "time": "2024-09-09T11:45:10+00:00" }, - { - "name": "symfony/polyfill-mbstring", - "version": "v1.31.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/85181ba99b2345b0ef10ce42ecac37612d9fd341", - "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341", - "shasum": "" - }, - "require": { - "php": ">=7.2" - }, - "provide": { - "ext-mbstring": "*" - }, - "suggest": { - "ext-mbstring": "For best performance" - }, - "type": "library", - "extra": { - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Mbstring\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill for the Mbstring extension", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "mbstring", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.31.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-09-09T11:45:10+00:00" - }, { "name": "symfony/process", "version": "v7.1.7", @@ -3176,89 +4777,6 @@ ], "time": "2024-11-06T09:25:12+00:00" }, - { - "name": "symfony/service-contracts", - "version": "v3.5.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/service-contracts.git", - "reference": "bd1d9e59a81d8fa4acdcea3f617c581f7475a80f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/bd1d9e59a81d8fa4acdcea3f617c581f7475a80f", - "reference": "bd1d9e59a81d8fa4acdcea3f617c581f7475a80f", - "shasum": "" - }, - "require": { - "php": ">=8.1", - "psr/container": "^1.1|^2.0", - "symfony/deprecation-contracts": "^2.5|^3" - }, - "conflict": { - "ext-psr": "<1.1|>=2" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "3.5-dev" - }, - "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Contracts\\Service\\": "" - }, - "exclude-from-classmap": [ - "/Test/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Generic abstractions related to writing services", - "homepage": "https://symfony.com", - "keywords": [ - "abstractions", - "contracts", - "decoupling", - "interfaces", - "interoperability", - "standards" - ], - "support": { - "source": "https://github.com/symfony/service-contracts/tree/v3.5.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-04-18T09:32:20+00:00" - }, { "name": "symfony/string", "version": "v7.1.6", @@ -3448,12 +4966,14 @@ ], "aliases": [], "minimum-stability": "stable", - "stability-flags": {}, + "stability-flags": { + "utopia-php/telemetry": 20 + }, "prefer-stable": false, "prefer-lowest": false, "platform": { "php": ">=8.0" }, - "platform-dev": {}, + "platform-dev": [], "plugin-api-version": "2.6.0" } diff --git a/src/App.php b/src/App.php index e61d12f4..addac0fb 100755 --- a/src/App.php +++ b/src/App.php @@ -2,6 +2,9 @@ namespace Utopia; +use Utopia\Telemetry\Adapter as Telemetry; +use Utopia\Telemetry\Adapter\Noop as NoopTelemetry; + class App { public const COMPRESSION_MIN_SIZE_DEFAULT = 1024; @@ -111,6 +114,8 @@ class App protected int $compressionMinSize = App::COMPRESSION_MIN_SIZE_DEFAULT; protected mixed $compressionSupported = []; + protected Metrics $metrics; + /** * App * @@ -119,6 +124,7 @@ class App public function __construct(string $timezone) { \date_default_timezone_set($timezone); + $this->metrics = new Metrics(new NoopTelemetry()); } /** @@ -145,6 +151,17 @@ public function setCompressionSupported(mixed $compressionSupported) $this->compressionSupported = $compressionSupported; } + /** + * Set telemetry adapter. + * + * @param Telemetry $telemetry + * @return void + */ + public function setTelemetry(Telemetry $telemetry) + { + $this->metrics = new Metrics($telemetry); + } + /** * GET * @@ -665,7 +682,24 @@ protected function getArguments(Hook $hook, array $values, array $requestParams) } /** - * Run + * Run: wrapper function to record telemetry. All domain logic should happen in `runInternal`. + */ + public function run(Request $request, Response $response): static + { + $this->metrics->increaseActiveRequest($request); + + $start = microtime(true); + $result = $this->runInternal($request, $response); + + $requestDuration = microtime(true) - $start; + $this->metrics->recordMetrics($request, $response, $this->getRoute(), $requestDuration); + $this->metrics->decreaseActiveRequest($request); + + return $result; + } + + /** + * Run internal * * This is the place to initialize any pre routing logic. * This is where you might want to parse the application current URL by any desired logic @@ -673,7 +707,7 @@ protected function getArguments(Hook $hook, array $values, array $requestParams) * @param Request $request * @param Response $response */ - public function run(Request $request, Response $response): static + private function runInternal(Request $request, Response $response): static { if ($this->compression) { $response->setAcceptEncoding($request->getHeader('accept-encoding', '')); diff --git a/src/Metrics.php b/src/Metrics.php new file mode 100644 index 00000000..12a27def --- /dev/null +++ b/src/Metrics.php @@ -0,0 +1,62 @@ +requestDuration = $telemetry->createHistogram( + 'http.server.request.duration', + 's', + null, + ['ExplicitBucketBoundaries' => [0.005, 0.01, 0.025, 0.05, 0.075, 0.1, 0.25, 0.5, 0.75, 1, 2.5, 5, 7.5, 10]] + ); + + // https://opentelemetry.io/docs/specs/semconv/http/http-metrics/#metric-httpserveractive_requests + $this->activeRequests = $telemetry->createUpDownCounter('http.server.active_requests', '{request}'); + // https://opentelemetry.io/docs/specs/semconv/http/http-metrics/#metric-httpserverrequestbodysize + $this->requestBodySize = $telemetry->createHistogram('http.server.request.body.size', 'By'); + // https://opentelemetry.io/docs/specs/semconv/http/http-metrics/#metric-httpserverresponsebodysize + $this->responseBodySize = $telemetry->createHistogram('http.server.response.body.size', 'By'); + } + + public function recordMetrics(Request $request, Response $response, ?Route $route, float $requestDuration): void + { + $attributes = [ + 'url.scheme' => $request->getProtocol(), + 'http.request.method' => $request->getMethod(), + 'http.route' => $route?->getPath(), + 'http.response.status_code' => $response->getStatusCode(), + ]; + $this->requestDuration->record($requestDuration, $attributes); + $this->requestBodySize->record($request->getSize(), $attributes); + $this->responseBodySize->record($response->getSize(), $attributes); + } + + public function increaseActiveRequest(Request $request): void + { + $this->activeRequests->add(1, [ + 'http.request.method' => $request->getMethod(), + 'url.scheme' => $request->getProtocol(), + ]); + } + + public function decreaseActiveRequest(Request $request): void + { + $this->activeRequests->add(-1, [ + 'http.request.method' => $request->getMethod(), + 'url.scheme' => $request->getProtocol(), + ]); + } +} From 3f46ad84cee26b5cdc377c20d0212a8e91f33389 Mon Sep 17 00:00:00 2001 From: Fabian Gruber Date: Wed, 13 Nov 2024 12:53:10 +0100 Subject: [PATCH 031/103] chore: review feedback --- composer.json | 6 +++++ composer.lock | 18 +++++++------- src/App.php | 47 +++++++++++++++++++++++++++++++------ src/Metrics.php | 62 ------------------------------------------------- 4 files changed, 55 insertions(+), 78 deletions(-) delete mode 100644 src/Metrics.php diff --git a/composer.json b/composer.json index d903e037..e280d28c 100644 --- a/composer.json +++ b/composer.json @@ -37,5 +37,11 @@ "laravel/pint": "^1.2", "phpstan/phpstan": "^1.10", "phpbench/phpbench": "^1.2" + }, + "config": { + "allow-plugins": { + "php-http/discovery": false, + "tbachert/spi": false + } } } diff --git a/composer.lock b/composer.lock index f8eb585a..b78ac481 100644 --- a/composer.lock +++ b/composer.lock @@ -1843,12 +1843,12 @@ "source": { "type": "git", "url": "https://github.com/utopia-php/telemetry.git", - "reference": "654896edcefa4cc4ce61af0944bf732febd62f95" + "reference": "5023d4cbcd4691a802df8cd115fd6fb767536c5b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/telemetry/zipball/654896edcefa4cc4ce61af0944bf732febd62f95", - "reference": "654896edcefa4cc4ce61af0944bf732febd62f95", + "url": "https://api.github.com/repos/utopia-php/telemetry/zipball/5023d4cbcd4691a802df8cd115fd6fb767536c5b", + "reference": "5023d4cbcd4691a802df8cd115fd6fb767536c5b", "shasum": "" }, "require": { @@ -1901,7 +1901,7 @@ "source": "https://github.com/utopia-php/telemetry/tree/initial-commit", "issues": "https://github.com/utopia-php/telemetry/issues" }, - "time": "2024-11-11T15:15:39+00:00" + "time": "2024-11-12T10:11:09+00:00" } ], "packages-dev": [ @@ -2634,16 +2634,16 @@ }, { "name": "phpstan/phpstan", - "version": "1.12.9", + "version": "1.12.10", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "ceb937fb39a92deabc02d20709cf14b2c452502c" + "reference": "fc463b5d0fe906dcf19689be692c65c50406a071" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/ceb937fb39a92deabc02d20709cf14b2c452502c", - "reference": "ceb937fb39a92deabc02d20709cf14b2c452502c", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/fc463b5d0fe906dcf19689be692c65c50406a071", + "reference": "fc463b5d0fe906dcf19689be692c65c50406a071", "shasum": "" }, "require": { @@ -2688,7 +2688,7 @@ "type": "github" } ], - "time": "2024-11-10T17:10:04+00:00" + "time": "2024-11-11T15:37:09+00:00" }, { "name": "phpunit/php-code-coverage", diff --git a/src/App.php b/src/App.php index addac0fb..57842bc6 100755 --- a/src/App.php +++ b/src/App.php @@ -3,7 +3,9 @@ namespace Utopia; use Utopia\Telemetry\Adapter as Telemetry; -use Utopia\Telemetry\Adapter\Noop as NoopTelemetry; +use Utopia\Telemetry\Adapter\None as NoTelemetry; +use Utopia\Telemetry\Histogram; +use Utopia\Telemetry\UpDownCounter; class App { @@ -114,7 +116,10 @@ class App protected int $compressionMinSize = App::COMPRESSION_MIN_SIZE_DEFAULT; protected mixed $compressionSupported = []; - protected Metrics $metrics; + private Histogram $requestDuration; + private UpDownCounter $activeRequests; + private Histogram $requestBodySize; + private Histogram $responseBodySize; /** * App @@ -124,7 +129,7 @@ class App public function __construct(string $timezone) { \date_default_timezone_set($timezone); - $this->metrics = new Metrics(new NoopTelemetry()); + $this->setTelemetry(new NoTelemetry()); } /** @@ -159,7 +164,20 @@ public function setCompressionSupported(mixed $compressionSupported) */ public function setTelemetry(Telemetry $telemetry) { - $this->metrics = new Metrics($telemetry); + // https://opentelemetry.io/docs/specs/semconv/http/http-metrics/#metric-httpserverrequestduration + $this->requestDuration = $telemetry->createHistogram( + 'http.server.request.duration', + 's', + null, + ['ExplicitBucketBoundaries' => [0.005, 0.01, 0.025, 0.05, 0.075, 0.1, 0.25, 0.5, 0.75, 1, 2.5, 5, 7.5, 10]] + ); + + // https://opentelemetry.io/docs/specs/semconv/http/http-metrics/#metric-httpserveractive_requests + $this->activeRequests = $telemetry->createUpDownCounter('http.server.active_requests', '{request}'); + // https://opentelemetry.io/docs/specs/semconv/http/http-metrics/#metric-httpserverrequestbodysize + $this->requestBodySize = $telemetry->createHistogram('http.server.request.body.size', 'By'); + // https://opentelemetry.io/docs/specs/semconv/http/http-metrics/#metric-httpserverresponsebodysize + $this->responseBodySize = $telemetry->createHistogram('http.server.response.body.size', 'By'); } /** @@ -686,14 +704,29 @@ protected function getArguments(Hook $hook, array $values, array $requestParams) */ public function run(Request $request, Response $response): static { - $this->metrics->increaseActiveRequest($request); + $this->activeRequests->add(1, [ + 'http.request.method' => $request->getMethod(), + 'url.scheme' => $request->getProtocol(), + ]); $start = microtime(true); $result = $this->runInternal($request, $response); $requestDuration = microtime(true) - $start; - $this->metrics->recordMetrics($request, $response, $this->getRoute(), $requestDuration); - $this->metrics->decreaseActiveRequest($request); + $attributes = [ + 'url.scheme' => $request->getProtocol(), + 'http.request.method' => $request->getMethod(), + // use ->match(fresh: true) to get the matched internal route, not any wildcard route + 'http.route' => $this->match($request, fresh: true)?->getPath(), + 'http.response.status_code' => $response->getStatusCode(), + ]; + $this->requestDuration->record($requestDuration, $attributes); + $this->requestBodySize->record($request->getSize(), $attributes); + $this->responseBodySize->record($response->getSize(), $attributes); + $this->activeRequests->add(-1, [ + 'http.request.method' => $request->getMethod(), + 'url.scheme' => $request->getProtocol(), + ]); return $result; } diff --git a/src/Metrics.php b/src/Metrics.php deleted file mode 100644 index 12a27def..00000000 --- a/src/Metrics.php +++ /dev/null @@ -1,62 +0,0 @@ -requestDuration = $telemetry->createHistogram( - 'http.server.request.duration', - 's', - null, - ['ExplicitBucketBoundaries' => [0.005, 0.01, 0.025, 0.05, 0.075, 0.1, 0.25, 0.5, 0.75, 1, 2.5, 5, 7.5, 10]] - ); - - // https://opentelemetry.io/docs/specs/semconv/http/http-metrics/#metric-httpserveractive_requests - $this->activeRequests = $telemetry->createUpDownCounter('http.server.active_requests', '{request}'); - // https://opentelemetry.io/docs/specs/semconv/http/http-metrics/#metric-httpserverrequestbodysize - $this->requestBodySize = $telemetry->createHistogram('http.server.request.body.size', 'By'); - // https://opentelemetry.io/docs/specs/semconv/http/http-metrics/#metric-httpserverresponsebodysize - $this->responseBodySize = $telemetry->createHistogram('http.server.response.body.size', 'By'); - } - - public function recordMetrics(Request $request, Response $response, ?Route $route, float $requestDuration): void - { - $attributes = [ - 'url.scheme' => $request->getProtocol(), - 'http.request.method' => $request->getMethod(), - 'http.route' => $route?->getPath(), - 'http.response.status_code' => $response->getStatusCode(), - ]; - $this->requestDuration->record($requestDuration, $attributes); - $this->requestBodySize->record($request->getSize(), $attributes); - $this->responseBodySize->record($response->getSize(), $attributes); - } - - public function increaseActiveRequest(Request $request): void - { - $this->activeRequests->add(1, [ - 'http.request.method' => $request->getMethod(), - 'url.scheme' => $request->getProtocol(), - ]); - } - - public function decreaseActiveRequest(Request $request): void - { - $this->activeRequests->add(-1, [ - 'http.request.method' => $request->getMethod(), - 'url.scheme' => $request->getProtocol(), - ]); - } -} From 438f50d9869d7cddbe4196f83cb84a62c7fa1521 Mon Sep 17 00:00:00 2001 From: Fabian Gruber Date: Wed, 13 Nov 2024 12:03:45 +0100 Subject: [PATCH 032/103] chore: use telemetry 0.1.0, php 8.1 --- .github/workflows/test.yml | 2 +- Dockerfile | 4 ++-- README.md | 2 +- composer.json | 10 ++-------- composer.lock | 38 ++++++++++---------------------------- src/App.php | 2 +- 6 files changed, 17 insertions(+), 41 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 4bc082d1..95ff2f27 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -13,7 +13,7 @@ jobs: - name: Setup PHP uses: shivammathur/setup-php@v2 with: - php-version: '8.0' + php-version: '8.1' - name: Setup Docker run: docker compose up -d --build diff --git a/Dockerfile b/Dockerfile index 54c948a6..043a8e04 100644 --- a/Dockerfile +++ b/Dockerfile @@ -13,11 +13,11 @@ RUN composer install --ignore-platform-reqs --optimize-autoloader \ --no-plugins --no-scripts --prefer-dist \ `if [ "$TESTING" != "true" ]; then echo "--no-dev"; fi` -FROM php:8.0-cli-alpine as final +FROM php:8.1-cli-alpine as final LABEL maintainer="team@appwrite.io" ENV DEBIAN_FRONTEND=noninteractive \ - PHP_VERSION=8 + PHP_VERSION=82 RUN \ apk add --no-cache --virtual .deps \ diff --git a/README.md b/README.md index ac3fc084..712c57ba 100644 --- a/README.md +++ b/README.md @@ -97,7 +97,7 @@ $app->run($request, $response); ## System Requirements -Utopia Framework requires PHP 8.0 or later. We recommend using the latest PHP version whenever possible. +Utopia Framework requires PHP 8.1 or later. We recommend using the latest PHP version whenever possible. ## More from Utopia diff --git a/composer.json b/composer.json index e280d28c..eb2e9a9f 100644 --- a/composer.json +++ b/composer.json @@ -22,16 +22,10 @@ "bench": "vendor/bin/phpbench run --report=benchmark" }, "require": { - "php": ">=8.0", + "php": ">=8.1", "utopia-php/compression": "0.1.*", - "utopia-php/telemetry": "dev-initial-commit" + "utopia-php/telemetry": "0.1.*" }, - "repositories": [ - { - "type": "vcs", - "url": "https://github.com/utopia-php/telemetry" - } - ], "require-dev": { "phpunit/phpunit": "^9.5.25", "laravel/pint": "^1.2", diff --git a/composer.lock b/composer.lock index b78ac481..a875dd46 100644 --- a/composer.lock +++ b/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": "28c0e77977607f0de243f49adfe583bc", + "content-hash": "bac3d7f9e6552a0c0742b6b95c94ed76", "packages": [ { "name": "brick/math", @@ -1839,16 +1839,16 @@ }, { "name": "utopia-php/telemetry", - "version": "dev-initial-commit", + "version": "0.1.0", "source": { "type": "git", "url": "https://github.com/utopia-php/telemetry.git", - "reference": "5023d4cbcd4691a802df8cd115fd6fb767536c5b" + "reference": "d35f2f0632f4ee0be63fb7ace6a94a6adda71a80" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/telemetry/zipball/5023d4cbcd4691a802df8cd115fd6fb767536c5b", - "reference": "5023d4cbcd4691a802df8cd115fd6fb767536c5b", + "url": "https://api.github.com/repos/utopia-php/telemetry/zipball/d35f2f0632f4ee0be63fb7ace6a94a6adda71a80", + "reference": "d35f2f0632f4ee0be63fb7ace6a94a6adda71a80", "shasum": "" }, "require": { @@ -1872,23 +1872,7 @@ "Utopia\\": "src/" } }, - "scripts": { - "lint": [ - "vendor/bin/pint --test" - ], - "format": [ - "vendor/bin/pint" - ], - "check": [ - "vendor/bin/phpstan analyse -c phpstan.neon" - ], - "test": [ - "vendor/bin/phpunit --configuration phpunit.xml" - ], - "bench": [ - "vendor/bin/phpbench run --report=benchmark" - ] - }, + "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], @@ -1898,10 +1882,10 @@ "upf" ], "support": { - "source": "https://github.com/utopia-php/telemetry/tree/initial-commit", - "issues": "https://github.com/utopia-php/telemetry/issues" + "issues": "https://github.com/utopia-php/telemetry/issues", + "source": "https://github.com/utopia-php/telemetry/tree/0.1.0" }, - "time": "2024-11-12T10:11:09+00:00" + "time": "2024-11-13T10:29:53+00:00" } ], "packages-dev": [ @@ -4966,9 +4950,7 @@ ], "aliases": [], "minimum-stability": "stable", - "stability-flags": { - "utopia-php/telemetry": 20 - }, + "stability-flags": [], "prefer-stable": false, "prefer-lowest": false, "platform": { diff --git a/src/App.php b/src/App.php index 57842bc6..f4673813 100755 --- a/src/App.php +++ b/src/App.php @@ -162,7 +162,7 @@ public function setCompressionSupported(mixed $compressionSupported) * @param Telemetry $telemetry * @return void */ - public function setTelemetry(Telemetry $telemetry) + public function setTelemetry(Telemetry $telemetry): void { // https://opentelemetry.io/docs/specs/semconv/http/http-metrics/#metric-httpserverrequestduration $this->requestDuration = $telemetry->createHistogram( From 15f5dd17162b166345e0c6598e49670084e7281e Mon Sep 17 00:00:00 2001 From: Fabian Gruber Date: Fri, 15 Nov 2024 09:35:10 +0100 Subject: [PATCH 033/103] fix: keep original matched route for metrics --- src/App.php | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/App.php b/src/App.php index f4673813..b8951282 100755 --- a/src/App.php +++ b/src/App.php @@ -101,6 +101,14 @@ class App */ protected ?Route $route = null; + /** + * Matched Route + * + * During runtime $this->route might be overwritten with the wildcard route to keep custom functions working with + * paths not declared in the Router. Keep a copy of the original matched app route. + */ + protected ?Route $matchedRoute = null; + /** * Wildcard route * If set, this get's executed if no other route is matched @@ -716,8 +724,7 @@ public function run(Request $request, Response $response): static $attributes = [ 'url.scheme' => $request->getProtocol(), 'http.request.method' => $request->getMethod(), - // use ->match(fresh: true) to get the matched internal route, not any wildcard route - 'http.route' => $this->match($request, fresh: true)?->getPath(), + 'http.route' => $this->matchedRoute?->getPath(), 'http.response.status_code' => $response->getStatusCode(), ]; $this->requestDuration->record($requestDuration, $attributes); @@ -761,6 +768,7 @@ private function runInternal(Request $request, Response $response): static $method = $request->getMethod(); $route = $this->match($request); + $this->matchedRoute = $route; $groups = ($route instanceof Route) ? $route->getGroups() : []; if (self::REQUEST_METHOD_HEAD == $method) { From 090fbabc3bbf7aeaf21aa63a079eb63ed617e18b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Wed, 20 Nov 2024 12:33:16 +0000 Subject: [PATCH 034/103] Ensure swoole knows content length when compressed --- src/Response.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Response.php b/src/Response.php index eccdb26b..9f5215fa 100755 --- a/src/Response.php +++ b/src/Response.php @@ -542,6 +542,7 @@ public function send(string $body = ''): void if ($algorithm) { $body = $algorithm->compress($body); + $this->addHeader('Content-Length', \strlen($body)); $this->addHeader('Content-Encoding', $algorithm->getContentEncoding()); $this->addHeader('X-Utopia-Compression', 'true'); $this->addHeader('Vary', 'Accept-Encoding'); From 9138535d2ef594fca0c0ed22815f0be44c1789fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Wed, 20 Nov 2024 12:35:25 +0000 Subject: [PATCH 035/103] CI/CD fix --- src/Response.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Response.php b/src/Response.php index 9f5215fa..b498d983 100755 --- a/src/Response.php +++ b/src/Response.php @@ -542,7 +542,7 @@ public function send(string $body = ''): void if ($algorithm) { $body = $algorithm->compress($body); - $this->addHeader('Content-Length', \strlen($body)); + $this->addHeader('Content-Length', (string) \strlen($body)); $this->addHeader('Content-Encoding', $algorithm->getContentEncoding()); $this->addHeader('X-Utopia-Compression', 'true'); $this->addHeader('Vary', 'Accept-Encoding'); From e454f24f00658fd85b40c37245b07a1c998337f2 Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Mon, 2 Dec 2024 11:22:32 +0000 Subject: [PATCH 036/103] fix: duplicate compression --- src/Response.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Response.php b/src/Response.php index b498d983..ef9150e2 100755 --- a/src/Response.php +++ b/src/Response.php @@ -532,8 +532,9 @@ public function send(string $body = ''): void $this->appendCookies(); - // Compress body + // Compress body only if all conditions are met: if ( + empty($this->headers['Content-Encoding']) && !empty($this->acceptEncoding) && isset($this->compressed[$this->contentType]) && strlen($body) > $this->compressionMinSize From 0d535f3820a0a73b5ba03c5af27b83c0694d8368 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Mon, 2 Dec 2024 17:47:45 +0100 Subject: [PATCH 037/103] Apply suggestions from code review --- src/Response.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Response.php b/src/Response.php index ef9150e2..74ee265c 100755 --- a/src/Response.php +++ b/src/Response.php @@ -534,7 +534,7 @@ public function send(string $body = ''): void // Compress body only if all conditions are met: if ( - empty($this->headers['Content-Encoding']) && + empty($this->headers['content-encoding']) && !empty($this->acceptEncoding) && isset($this->compressed[$this->contentType]) && strlen($body) > $this->compressionMinSize From 80106e39e9e7525c4228e9e3fbab89de7e58c362 Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Tue, 10 Dec 2024 10:43:34 +0000 Subject: [PATCH 038/103] fix: catch error handler --- src/App.php | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/src/App.php b/src/App.php index b8951282..2d188ac6 100755 --- a/src/App.php +++ b/src/App.php @@ -800,7 +800,13 @@ private function runInternal(Request $request, Response $response): static self::setResource('error', function () use ($e) { return $e; }); - \call_user_func_array($error->getAction(), $this->getArguments($error, [], $request->getParams())); + + try { + $arguments = $this->getArguments($error, [], $request->getParams()); + \call_user_func_array($error->getAction(), $arguments); + } catch (\Throwable $e) { + throw new Exception('Error handler had an error: ' . $e->getMessage(), 500, $e); + } } } } @@ -838,7 +844,12 @@ private function runInternal(Request $request, Response $response): static self::setResource('error', function () use ($e) { return $e; }); - \call_user_func_array($error->getAction(), $this->getArguments($error, [], $request->getParams())); + try { + $arguments = $this->getArguments($error, [], $request->getParams()); + \call_user_func_array($error->getAction(), $arguments); + } catch (\Throwable $e) { + throw new Exception('Error handler had an error: ' . $e->getMessage(), 500, $e); + } } } } @@ -848,7 +859,12 @@ private function runInternal(Request $request, Response $response): static self::setResource('error', function () { return new Exception('Not Found', 404); }); - \call_user_func_array($error->getAction(), $this->getArguments($error, [], $request->getParams())); + try { + $arguments = $this->getArguments($error, [], $request->getParams()); + \call_user_func_array($error->getAction(), $arguments); + } catch (\Throwable $e) { + throw new Exception('Error handler had an error: ' . $e->getMessage(), 500, $e); + } } } } From 4d36d9a83d28361df8e2bd7baca07de71b5024c5 Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Tue, 10 Dec 2024 10:44:47 +0000 Subject: [PATCH 039/103] fix: spacing --- src/App.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/App.php b/src/App.php index 2d188ac6..63965ecf 100755 --- a/src/App.php +++ b/src/App.php @@ -800,7 +800,6 @@ private function runInternal(Request $request, Response $response): static self::setResource('error', function () use ($e) { return $e; }); - try { $arguments = $this->getArguments($error, [], $request->getParams()); \call_user_func_array($error->getAction(), $arguments); From b887c605ec58d58c20a4cd94c8c41966c377cec8 Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Tue, 10 Dec 2024 11:39:14 +0000 Subject: [PATCH 040/103] test: error handler scenerios --- tests/AppTest.php | 141 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 141 insertions(+) diff --git a/tests/AppTest.php b/tests/AppTest.php index 38f720dd..3add5186 100755 --- a/tests/AppTest.php +++ b/tests/AppTest.php @@ -659,4 +659,145 @@ public function testWildcardRoute(): void $_SERVER['REQUEST_METHOD'] = $method; $_SERVER['REQUEST_URI'] = $uri; } + + public function testErrorHandlerFailure(): void + { + $this->app + ->error() + ->inject('error') + ->action(function($error) { + throw new \Exception('Error handler failed'); + }); + + $route = new Route('GET', '/path'); + $route + ->action(function() { + throw new \Exception('Route action failed'); + }); + + try { + $this->app->execute($route, new Request(), new Response()); + $this->fail('Should have thrown an exception'); + } catch (\Exception $e) { + $this->assertEquals('Error handler had an error: Error handler failed', $e->getMessage()); + $this->assertEquals(500, $e->getCode()); + $this->assertInstanceOf(\Exception::class, $e->getPrevious()); + $this->assertEquals('Error handler failed', $e->getPrevious()->getMessage()); + } + } + + public function testOptionsHandlerFailure(): void + { + $this->app + ->error() + ->inject('error') + ->action(function($error) { + throw new \Exception('Options error handler failed'); + }); + + // Set up an options handler that throws + App::options() + ->action(function() { + throw new \Exception('Options handler failed'); + }); + + $request = new UtopiaRequestTest(); + $request->setMethod('OPTIONS'); + + try { + $this->app->run($request, new Response()); + $this->fail('Should have thrown an exception'); + } catch (\Exception $e) { + $this->assertEquals('Error handler had an error: Options error handler failed', $e->getMessage()); + $this->assertEquals(500, $e->getCode()); + } + } + + public function testNotFoundErrorHandlerFailure(): void + { + // Set up error handler that throws for 404 cases + $this->app + ->error() + ->action(function() { + throw new \Exception('404 error handler failed'); + }); + + $request = new UtopiaRequestTest(); + $request->setMethod('GET'); + $request->setURI('/nonexistent-path'); + + try { + $this->app->run($request, new Response()); + $this->fail('Should have thrown an exception'); + } catch (\Exception $e) { + $this->assertEquals('Error handler had an error: 404 error handler failed', $e->getMessage()); + $this->assertEquals(500, $e->getCode()); + $this->assertInstanceOf(\Exception::class, $e->getPrevious()); + $this->assertEquals('404 error handler failed', $e->getPrevious()->getMessage()); + } + } + + public function testGroupErrorHandlerFailure(): void + { + // Set up group-specific error handler that throws + $this->app + ->error() + ->groups(['api']) + ->action(function() { + throw new \Exception('Group error handler failed'); + }); + + $route = new Route('GET', '/api/test'); + $route + ->groups(['api']) + ->action(function() { + throw new \Exception('Route action failed'); + }); + + try { + $this->app->execute($route, new Request(), new Response()); + $this->fail('Should have thrown an exception'); + } catch (\Exception $e) { + $this->assertEquals('Error handler had an error: Group error handler failed', $e->getMessage()); + $this->assertEquals(500, $e->getCode()); + $this->assertInstanceOf(\Exception::class, $e->getPrevious()); + $this->assertEquals('Group error handler failed', $e->getPrevious()->getMessage()); + } + } + + public function testErrorHandlerChaining(): void + { + // Set up multiple error handlers to test chaining behavior + $this->app + ->error() + ->groups(['api']) + ->action(function() { + throw new \Exception('First error handler failed'); + }); + + $this->app + ->error() + ->action(function() { + throw new \Exception('Second error handler failed'); + }); + + $route = new Route('GET', '/api/test'); + $route + ->groups(['api']) + ->action(function() { + throw new \Exception('Original error'); + }); + + try { + $this->app->execute($route, new Request(), new Response()); + $this->fail('Should have thrown an exception'); + } catch (\Exception $e) { + $this->assertEquals('Error handler had an error: First error handler failed', $e->getMessage()); + $this->assertEquals(500, $e->getCode()); + + // Verify the error chain + $this->assertInstanceOf(\Exception::class, $e->getPrevious()); + $this->assertEquals('First error handler failed', $e->getPrevious()->getMessage()); + } + } } From c087eb077d9b9d271f8e16af437cc8adcbce1495 Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Tue, 10 Dec 2024 11:42:28 +0000 Subject: [PATCH 041/103] chore: fmt --- tests/AppTest.php | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/tests/AppTest.php b/tests/AppTest.php index 3add5186..5242e838 100755 --- a/tests/AppTest.php +++ b/tests/AppTest.php @@ -665,13 +665,13 @@ public function testErrorHandlerFailure(): void $this->app ->error() ->inject('error') - ->action(function($error) { + ->action(function ($error) { throw new \Exception('Error handler failed'); }); $route = new Route('GET', '/path'); $route - ->action(function() { + ->action(function () { throw new \Exception('Route action failed'); }); @@ -691,13 +691,13 @@ public function testOptionsHandlerFailure(): void $this->app ->error() ->inject('error') - ->action(function($error) { + ->action(function ($error) { throw new \Exception('Options error handler failed'); }); // Set up an options handler that throws App::options() - ->action(function() { + ->action(function () { throw new \Exception('Options handler failed'); }); @@ -718,7 +718,7 @@ public function testNotFoundErrorHandlerFailure(): void // Set up error handler that throws for 404 cases $this->app ->error() - ->action(function() { + ->action(function () { throw new \Exception('404 error handler failed'); }); @@ -743,14 +743,14 @@ public function testGroupErrorHandlerFailure(): void $this->app ->error() ->groups(['api']) - ->action(function() { + ->action(function () { throw new \Exception('Group error handler failed'); }); $route = new Route('GET', '/api/test'); $route ->groups(['api']) - ->action(function() { + ->action(function () { throw new \Exception('Route action failed'); }); @@ -771,20 +771,20 @@ public function testErrorHandlerChaining(): void $this->app ->error() ->groups(['api']) - ->action(function() { + ->action(function () { throw new \Exception('First error handler failed'); }); $this->app ->error() - ->action(function() { + ->action(function () { throw new \Exception('Second error handler failed'); }); $route = new Route('GET', '/api/test'); $route ->groups(['api']) - ->action(function() { + ->action(function () { throw new \Exception('Original error'); }); @@ -794,7 +794,7 @@ public function testErrorHandlerChaining(): void } catch (\Exception $e) { $this->assertEquals('Error handler had an error: First error handler failed', $e->getMessage()); $this->assertEquals(500, $e->getCode()); - + // Verify the error chain $this->assertInstanceOf(\Exception::class, $e->getPrevious()); $this->assertEquals('First error handler failed', $e->getPrevious()->getMessage()); From f642ceef8078c5f1454c4142c41fc860ee337a6d Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Tue, 10 Dec 2024 11:43:28 +0000 Subject: [PATCH 042/103] fix: codeql --- .github/workflows/codeql-analysis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 126ef740..70bba1bd 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -12,6 +12,6 @@ jobs: - name: Run CodeQL run: | - docker run --rm -v $PWD:/app composer sh -c \ + docker run --rm -v $PWD:/app composer:2.6 sh -c \ "composer install --profile --ignore-platform-reqs && composer check" \ No newline at end of file From 68f5fb300ee24aaa2f18deaa67a51b86002437e8 Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Tue, 10 Dec 2024 11:47:14 +0000 Subject: [PATCH 043/103] fix: bench --- .github/workflows/bench.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/bench.yml b/.github/workflows/bench.yml index 0d97b2bc..43625f99 100644 --- a/.github/workflows/bench.yml +++ b/.github/workflows/bench.yml @@ -12,5 +12,5 @@ jobs: - name: Run Linter run: | - docker run --rm -v $PWD:/app composer sh -c \ + docker run --rm -v $PWD:/app composer:2.6 sh -c \ "composer install --profile --ignore-platform-reqs && git config --global --add safe.directory /app && composer bench -- --progress=plain" From 0d18cf4e719c900edbf3155334d9a1da91e999b3 Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Thu, 16 Jan 2025 11:52:19 +0000 Subject: [PATCH 044/103] feat: compress for types --- composer.lock | 317 ++++++++++++++++++++++++----------------------- src/Response.php | 82 ++++++++++-- 2 files changed, 233 insertions(+), 166 deletions(-) diff --git a/composer.lock b/composer.lock index a875dd46..fb5541a1 100644 --- a/composer.lock +++ b/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": "bac3d7f9e6552a0c0742b6b95c94ed76", + "content-hash": "703d47ec02ec2d09dda755ab4924cae6", "packages": [ { "name": "brick/math", @@ -149,16 +149,16 @@ }, { "name": "google/protobuf", - "version": "v4.28.3", + "version": "v4.29.3", "source": { "type": "git", "url": "https://github.com/protocolbuffers/protobuf-php.git", - "reference": "c5c311e0f3d89928251ac5a2f0e3db283612c100" + "reference": "ab5077c2cfdd1f415f42d11fdbdf903ba8e3d9b7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/protocolbuffers/protobuf-php/zipball/c5c311e0f3d89928251ac5a2f0e3db283612c100", - "reference": "c5c311e0f3d89928251ac5a2f0e3db283612c100", + "url": "https://api.github.com/repos/protocolbuffers/protobuf-php/zipball/ab5077c2cfdd1f415f42d11fdbdf903ba8e3d9b7", + "reference": "ab5077c2cfdd1f415f42d11fdbdf903ba8e3d9b7", "shasum": "" }, "require": { @@ -187,9 +187,9 @@ "proto" ], "support": { - "source": "https://github.com/protocolbuffers/protobuf-php/tree/v4.28.3" + "source": "https://github.com/protocolbuffers/protobuf-php/tree/v4.29.3" }, - "time": "2024-10-22T22:27:17+00:00" + "time": "2025-01-08T21:00:13+00:00" }, { "name": "nyholm/psr7", @@ -337,16 +337,16 @@ }, { "name": "open-telemetry/api", - "version": "1.1.1", + "version": "1.2.0", "source": { "type": "git", "url": "https://github.com/opentelemetry-php/api.git", - "reference": "542064815d38a6df55af7957cd6f1d7d967c99c6" + "reference": "351a30baa79699de3de3a814c8ccc7b52ccdfb1d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/opentelemetry-php/api/zipball/542064815d38a6df55af7957cd6f1d7d967c99c6", - "reference": "542064815d38a6df55af7957cd6f1d7d967c99c6", + "url": "https://api.github.com/repos/opentelemetry-php/api/zipball/351a30baa79699de3de3a814c8ccc7b52ccdfb1d", + "reference": "351a30baa79699de3de3a814c8ccc7b52ccdfb1d", "shasum": "" }, "require": { @@ -360,13 +360,13 @@ }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "1.1.x-dev" - }, "spi": { "OpenTelemetry\\API\\Instrumentation\\AutoInstrumentation\\HookManagerInterface": [ "OpenTelemetry\\API\\Instrumentation\\AutoInstrumentation\\ExtensionHookManager" ] + }, + "branch-alias": { + "dev-main": "1.1.x-dev" } }, "autoload": { @@ -403,7 +403,7 @@ "issues": "https://github.com/open-telemetry/opentelemetry-php/issues", "source": "https://github.com/open-telemetry/opentelemetry-php" }, - "time": "2024-10-15T22:42:37+00:00" + "time": "2025-01-08T23:50:34+00:00" }, { "name": "open-telemetry/context", @@ -466,16 +466,16 @@ }, { "name": "open-telemetry/exporter-otlp", - "version": "1.1.0", + "version": "1.2.0", "source": { "type": "git", "url": "https://github.com/opentelemetry-php/exporter-otlp.git", - "reference": "9b6de12204f25f8ab9540b46d6e7b5151897ce18" + "reference": "243d9657c44a06f740cf384f486afe954c2b725f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/opentelemetry-php/exporter-otlp/zipball/9b6de12204f25f8ab9540b46d6e7b5151897ce18", - "reference": "9b6de12204f25f8ab9540b46d6e7b5151897ce18", + "url": "https://api.github.com/repos/opentelemetry-php/exporter-otlp/zipball/243d9657c44a06f740cf384f486afe954c2b725f", + "reference": "243d9657c44a06f740cf384f486afe954c2b725f", "shasum": "" }, "require": { @@ -526,20 +526,20 @@ "issues": "https://github.com/open-telemetry/opentelemetry-php/issues", "source": "https://github.com/open-telemetry/opentelemetry-php" }, - "time": "2024-04-30T18:28:30+00:00" + "time": "2025-01-08T23:50:03+00:00" }, { "name": "open-telemetry/gen-otlp-protobuf", - "version": "1.2.1", + "version": "1.5.0", "source": { "type": "git", "url": "https://github.com/opentelemetry-php/gen-otlp-protobuf.git", - "reference": "66c3b98e998a726691c92e6405a82e6e7b8b169d" + "reference": "585bafddd4ae6565de154610b10a787a455c9ba0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/opentelemetry-php/gen-otlp-protobuf/zipball/66c3b98e998a726691c92e6405a82e6e7b8b169d", - "reference": "66c3b98e998a726691c92e6405a82e6e7b8b169d", + "url": "https://api.github.com/repos/opentelemetry-php/gen-otlp-protobuf/zipball/585bafddd4ae6565de154610b10a787a455c9ba0", + "reference": "585bafddd4ae6565de154610b10a787a455c9ba0", "shasum": "" }, "require": { @@ -589,20 +589,20 @@ "issues": "https://github.com/open-telemetry/opentelemetry-php/issues", "source": "https://github.com/open-telemetry/opentelemetry-php" }, - "time": "2024-10-30T11:49:49+00:00" + "time": "2025-01-15T23:07:07+00:00" }, { "name": "open-telemetry/sdk", - "version": "1.1.2", + "version": "1.2.0", "source": { "type": "git", "url": "https://github.com/opentelemetry-php/sdk.git", - "reference": "fb0ff8d8279a3776bd604791e2531dd0cc147e8b" + "reference": "9a1c3b866239dbff291e5cc555bb7793eab08127" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/opentelemetry-php/sdk/zipball/fb0ff8d8279a3776bd604791e2531dd0cc147e8b", - "reference": "fb0ff8d8279a3776bd604791e2531dd0cc147e8b", + "url": "https://api.github.com/repos/opentelemetry-php/sdk/zipball/9a1c3b866239dbff291e5cc555bb7793eab08127", + "reference": "9a1c3b866239dbff291e5cc555bb7793eab08127", "shasum": "" }, "require": { @@ -630,13 +630,13 @@ }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "1.0.x-dev" - }, "spi": { "OpenTelemetry\\API\\Instrumentation\\AutoInstrumentation\\HookManagerInterface": [ "OpenTelemetry\\API\\Instrumentation\\AutoInstrumentation\\ExtensionHookManager" ] + }, + "branch-alias": { + "dev-main": "1.0.x-dev" } }, "autoload": { @@ -679,7 +679,7 @@ "issues": "https://github.com/open-telemetry/opentelemetry-php/issues", "source": "https://github.com/open-telemetry/opentelemetry-php" }, - "time": "2024-10-18T21:01:35+00:00" + "time": "2025-01-08T23:50:34+00:00" }, { "name": "open-telemetry/sem-conv", @@ -1263,16 +1263,16 @@ }, { "name": "symfony/deprecation-contracts", - "version": "v3.5.0", + "version": "v3.5.1", "source": { "type": "git", "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1" + "reference": "74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1", - "reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6", + "reference": "74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6", "shasum": "" }, "require": { @@ -1280,12 +1280,12 @@ }, "type": "library", "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, "branch-alias": { "dev-main": "3.5-dev" - }, - "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" } }, "autoload": { @@ -1310,7 +1310,7 @@ "description": "A generic function and convention to trigger deprecation notices", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v3.5.0" + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.5.1" }, "funding": [ { @@ -1326,30 +1326,31 @@ "type": "tidelift" } ], - "time": "2024-04-18T09:32:20+00:00" + "time": "2024-09-25T14:20:29+00:00" }, { "name": "symfony/http-client", - "version": "v7.1.7", + "version": "v7.2.2", "source": { "type": "git", "url": "https://github.com/symfony/http-client.git", - "reference": "90ab2a4992dcf5d1f19a9b8737eba36a7c305fd0" + "reference": "339ba21476eb184290361542f732ad12c97591ec" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-client/zipball/90ab2a4992dcf5d1f19a9b8737eba36a7c305fd0", - "reference": "90ab2a4992dcf5d1f19a9b8737eba36a7c305fd0", + "url": "https://api.github.com/repos/symfony/http-client/zipball/339ba21476eb184290361542f732ad12c97591ec", + "reference": "339ba21476eb184290361542f732ad12c97591ec", "shasum": "" }, "require": { "php": ">=8.2", "psr/log": "^1|^2|^3", "symfony/deprecation-contracts": "^2.5|^3", - "symfony/http-client-contracts": "^3.4.1", + "symfony/http-client-contracts": "~3.4.4|^3.5.2", "symfony/service-contracts": "^2.5|^3" }, "conflict": { + "amphp/amp": "<2.5", "php-http/discovery": "<1.15", "symfony/http-foundation": "<6.4" }, @@ -1360,14 +1361,14 @@ "symfony/http-client-implementation": "3.0" }, "require-dev": { - "amphp/amp": "^2.5", - "amphp/http-client": "^4.2.1", - "amphp/http-tunnel": "^1.0", + "amphp/http-client": "^4.2.1|^5.0", + "amphp/http-tunnel": "^1.0|^2.0", "amphp/socket": "^1.1", "guzzlehttp/promises": "^1.4|^2.0", "nyholm/psr7": "^1.0", "php-http/httplug": "^1.0|^2.0", "psr/http-client": "^1.0", + "symfony/amphp-http-client-meta": "^1.0|^2.0", "symfony/dependency-injection": "^6.4|^7.0", "symfony/http-kernel": "^6.4|^7.0", "symfony/messenger": "^6.4|^7.0", @@ -1404,7 +1405,7 @@ "http" ], "support": { - "source": "https://github.com/symfony/http-client/tree/v7.1.7" + "source": "https://github.com/symfony/http-client/tree/v7.2.2" }, "funding": [ { @@ -1420,20 +1421,20 @@ "type": "tidelift" } ], - "time": "2024-11-05T16:45:54+00:00" + "time": "2024-12-30T18:35:15+00:00" }, { "name": "symfony/http-client-contracts", - "version": "v3.5.0", + "version": "v3.5.2", "source": { "type": "git", "url": "https://github.com/symfony/http-client-contracts.git", - "reference": "20414d96f391677bf80078aa55baece78b82647d" + "reference": "ee8d807ab20fcb51267fdace50fbe3494c31e645" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-client-contracts/zipball/20414d96f391677bf80078aa55baece78b82647d", - "reference": "20414d96f391677bf80078aa55baece78b82647d", + "url": "https://api.github.com/repos/symfony/http-client-contracts/zipball/ee8d807ab20fcb51267fdace50fbe3494c31e645", + "reference": "ee8d807ab20fcb51267fdace50fbe3494c31e645", "shasum": "" }, "require": { @@ -1441,12 +1442,12 @@ }, "type": "library", "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, "branch-alias": { "dev-main": "3.5-dev" - }, - "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" } }, "autoload": { @@ -1482,7 +1483,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/http-client-contracts/tree/v3.5.0" + "source": "https://github.com/symfony/http-client-contracts/tree/v3.5.2" }, "funding": [ { @@ -1498,7 +1499,7 @@ "type": "tidelift" } ], - "time": "2024-04-18T09:32:20+00:00" + "time": "2024-12-07T08:49:48+00:00" }, { "name": "symfony/polyfill-mbstring", @@ -1526,8 +1527,8 @@ "type": "library", "extra": { "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -1600,8 +1601,8 @@ "type": "library", "extra": { "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -1658,16 +1659,16 @@ }, { "name": "symfony/service-contracts", - "version": "v3.5.0", + "version": "v3.5.1", "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", - "reference": "bd1d9e59a81d8fa4acdcea3f617c581f7475a80f" + "reference": "e53260aabf78fb3d63f8d79d69ece59f80d5eda0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/bd1d9e59a81d8fa4acdcea3f617c581f7475a80f", - "reference": "bd1d9e59a81d8fa4acdcea3f617c581f7475a80f", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/e53260aabf78fb3d63f8d79d69ece59f80d5eda0", + "reference": "e53260aabf78fb3d63f8d79d69ece59f80d5eda0", "shasum": "" }, "require": { @@ -1680,12 +1681,12 @@ }, "type": "library", "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, "branch-alias": { "dev-main": "3.5-dev" - }, - "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" } }, "autoload": { @@ -1721,7 +1722,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/service-contracts/tree/v3.5.0" + "source": "https://github.com/symfony/service-contracts/tree/v3.5.1" }, "funding": [ { @@ -1737,7 +1738,7 @@ "type": "tidelift" } ], - "time": "2024-04-18T09:32:20+00:00" + "time": "2024-09-25T14:20:29+00:00" }, { "name": "tbachert/spi", @@ -1766,10 +1767,10 @@ }, "type": "composer-plugin", "extra": { + "class": "Nevay\\SPI\\Composer\\Plugin", "branch-alias": { "dev-main": "0.2.x-dev" }, - "class": "Nevay\\SPI\\Composer\\Plugin", "plugin-optional": true }, "autoload": { @@ -1793,16 +1794,16 @@ }, { "name": "utopia-php/compression", - "version": "0.1.2", + "version": "0.1.3", "source": { "type": "git", "url": "https://github.com/utopia-php/compression.git", - "reference": "6062f70596415f8d5de40a589367b0eb2a435f98" + "reference": "66f093557ba66d98245e562036182016c7dcfe8a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/compression/zipball/6062f70596415f8d5de40a589367b0eb2a435f98", - "reference": "6062f70596415f8d5de40a589367b0eb2a435f98", + "url": "https://api.github.com/repos/utopia-php/compression/zipball/66f093557ba66d98245e562036182016c7dcfe8a", + "reference": "66f093557ba66d98245e562036182016c7dcfe8a", "shasum": "" }, "require": { @@ -1833,9 +1834,9 @@ ], "support": { "issues": "https://github.com/utopia-php/compression/issues", - "source": "https://github.com/utopia-php/compression/tree/0.1.2" + "source": "https://github.com/utopia-php/compression/tree/0.1.3" }, - "time": "2024-11-08T14:59:54+00:00" + "time": "2025-01-15T15:15:51+00:00" }, { "name": "utopia-php/telemetry", @@ -2114,16 +2115,16 @@ }, { "name": "laravel/pint", - "version": "v1.18.1", + "version": "v1.20.0", "source": { "type": "git", "url": "https://github.com/laravel/pint.git", - "reference": "35c00c05ec43e6b46d295efc0f4386ceb30d50d9" + "reference": "53072e8ea22213a7ed168a8a15b96fbb8b82d44b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/pint/zipball/35c00c05ec43e6b46d295efc0f4386ceb30d50d9", - "reference": "35c00c05ec43e6b46d295efc0f4386ceb30d50d9", + "url": "https://api.github.com/repos/laravel/pint/zipball/53072e8ea22213a7ed168a8a15b96fbb8b82d44b", + "reference": "53072e8ea22213a7ed168a8a15b96fbb8b82d44b", "shasum": "" }, "require": { @@ -2134,13 +2135,13 @@ "php": "^8.1.0" }, "require-dev": { - "friendsofphp/php-cs-fixer": "^3.64.0", - "illuminate/view": "^10.48.20", - "larastan/larastan": "^2.9.8", - "laravel-zero/framework": "^10.4.0", + "friendsofphp/php-cs-fixer": "^3.66.0", + "illuminate/view": "^10.48.25", + "larastan/larastan": "^2.9.12", + "laravel-zero/framework": "^10.48.25", "mockery/mockery": "^1.6.12", - "nunomaduro/termwind": "^1.15.1", - "pestphp/pest": "^2.35.1" + "nunomaduro/termwind": "^1.17.0", + "pestphp/pest": "^2.36.0" }, "bin": [ "builds/pint" @@ -2176,7 +2177,7 @@ "issues": "https://github.com/laravel/pint/issues", "source": "https://github.com/laravel/pint" }, - "time": "2024-09-24T17:22:50+00:00" + "time": "2025-01-14T16:20:53+00:00" }, { "name": "myclabs/deep-copy", @@ -2240,16 +2241,16 @@ }, { "name": "nikic/php-parser", - "version": "v5.3.1", + "version": "v5.4.0", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "8eea230464783aa9671db8eea6f8c6ac5285794b" + "reference": "447a020a1f875a434d62f2a401f53b82a396e494" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/8eea230464783aa9671db8eea6f8c6ac5285794b", - "reference": "8eea230464783aa9671db8eea6f8c6ac5285794b", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/447a020a1f875a434d62f2a401f53b82a396e494", + "reference": "447a020a1f875a434d62f2a401f53b82a396e494", "shasum": "" }, "require": { @@ -2292,9 +2293,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v5.3.1" + "source": "https://github.com/nikic/PHP-Parser/tree/v5.4.0" }, - "time": "2024-10-08T18:51:32+00:00" + "time": "2024-12-30T11:07:19+00:00" }, { "name": "phar-io/manifest", @@ -2618,16 +2619,16 @@ }, { "name": "phpstan/phpstan", - "version": "1.12.10", + "version": "1.12.15", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "fc463b5d0fe906dcf19689be692c65c50406a071" + "reference": "c91d4e8bc056f46cf653656e6f71004b254574d1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/fc463b5d0fe906dcf19689be692c65c50406a071", - "reference": "fc463b5d0fe906dcf19689be692c65c50406a071", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/c91d4e8bc056f46cf653656e6f71004b254574d1", + "reference": "c91d4e8bc056f46cf653656e6f71004b254574d1", "shasum": "" }, "require": { @@ -2672,7 +2673,7 @@ "type": "github" } ], - "time": "2024-11-11T15:37:09+00:00" + "time": "2025-01-05T16:40:22+00:00" }, { "name": "phpunit/php-code-coverage", @@ -2995,16 +2996,16 @@ }, { "name": "phpunit/phpunit", - "version": "9.6.21", + "version": "9.6.22", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "de6abf3b6f8dd955fac3caad3af7a9504e8c2ffa" + "reference": "f80235cb4d3caa59ae09be3adf1ded27521d1a9c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/de6abf3b6f8dd955fac3caad3af7a9504e8c2ffa", - "reference": "de6abf3b6f8dd955fac3caad3af7a9504e8c2ffa", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/f80235cb4d3caa59ae09be3adf1ded27521d1a9c", + "reference": "f80235cb4d3caa59ae09be3adf1ded27521d1a9c", "shasum": "" }, "require": { @@ -3015,7 +3016,7 @@ "ext-mbstring": "*", "ext-xml": "*", "ext-xmlwriter": "*", - "myclabs/deep-copy": "^1.12.0", + "myclabs/deep-copy": "^1.12.1", "phar-io/manifest": "^2.0.4", "phar-io/version": "^3.2.1", "php": ">=7.3", @@ -3078,7 +3079,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.21" + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.22" }, "funding": [ { @@ -3094,7 +3095,7 @@ "type": "tidelift" } ], - "time": "2024-09-19T10:50:18+00:00" + "time": "2024-12-05T13:48:26+00:00" }, { "name": "psr/cache", @@ -4174,16 +4175,16 @@ }, { "name": "symfony/console", - "version": "v7.1.7", + "version": "v7.2.1", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "3284aafcac338b6e86fd955ee4d794cbe434151a" + "reference": "fefcc18c0f5d0efe3ab3152f15857298868dc2c3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/3284aafcac338b6e86fd955ee4d794cbe434151a", - "reference": "3284aafcac338b6e86fd955ee4d794cbe434151a", + "url": "https://api.github.com/repos/symfony/console/zipball/fefcc18c0f5d0efe3ab3152f15857298868dc2c3", + "reference": "fefcc18c0f5d0efe3ab3152f15857298868dc2c3", "shasum": "" }, "require": { @@ -4247,7 +4248,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v7.1.7" + "source": "https://github.com/symfony/console/tree/v7.2.1" }, "funding": [ { @@ -4263,20 +4264,20 @@ "type": "tidelift" } ], - "time": "2024-11-05T15:34:55+00:00" + "time": "2024-12-11T03:49:26+00:00" }, { "name": "symfony/filesystem", - "version": "v7.1.6", + "version": "v7.2.0", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "c835867b3c62bb05c7fe3d637c871c7ae52024d4" + "reference": "b8dce482de9d7c9fe2891155035a7248ab5c7fdb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/c835867b3c62bb05c7fe3d637c871c7ae52024d4", - "reference": "c835867b3c62bb05c7fe3d637c871c7ae52024d4", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/b8dce482de9d7c9fe2891155035a7248ab5c7fdb", + "reference": "b8dce482de9d7c9fe2891155035a7248ab5c7fdb", "shasum": "" }, "require": { @@ -4313,7 +4314,7 @@ "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/filesystem/tree/v7.1.6" + "source": "https://github.com/symfony/filesystem/tree/v7.2.0" }, "funding": [ { @@ -4329,20 +4330,20 @@ "type": "tidelift" } ], - "time": "2024-10-25T15:11:02+00:00" + "time": "2024-10-25T15:15:23+00:00" }, { "name": "symfony/finder", - "version": "v7.1.6", + "version": "v7.2.2", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "2cb89664897be33f78c65d3d2845954c8d7a43b8" + "reference": "87a71856f2f56e4100373e92529eed3171695cfb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/2cb89664897be33f78c65d3d2845954c8d7a43b8", - "reference": "2cb89664897be33f78c65d3d2845954c8d7a43b8", + "url": "https://api.github.com/repos/symfony/finder/zipball/87a71856f2f56e4100373e92529eed3171695cfb", + "reference": "87a71856f2f56e4100373e92529eed3171695cfb", "shasum": "" }, "require": { @@ -4377,7 +4378,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v7.1.6" + "source": "https://github.com/symfony/finder/tree/v7.2.2" }, "funding": [ { @@ -4393,20 +4394,20 @@ "type": "tidelift" } ], - "time": "2024-10-01T08:31:23+00:00" + "time": "2024-12-30T19:00:17+00:00" }, { "name": "symfony/options-resolver", - "version": "v7.1.6", + "version": "v7.2.0", "source": { "type": "git", "url": "https://github.com/symfony/options-resolver.git", - "reference": "85e95eeede2d41cd146146e98c9c81d9214cae85" + "reference": "7da8fbac9dcfef75ffc212235d76b2754ce0cf50" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/options-resolver/zipball/85e95eeede2d41cd146146e98c9c81d9214cae85", - "reference": "85e95eeede2d41cd146146e98c9c81d9214cae85", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/7da8fbac9dcfef75ffc212235d76b2754ce0cf50", + "reference": "7da8fbac9dcfef75ffc212235d76b2754ce0cf50", "shasum": "" }, "require": { @@ -4444,7 +4445,7 @@ "options" ], "support": { - "source": "https://github.com/symfony/options-resolver/tree/v7.1.6" + "source": "https://github.com/symfony/options-resolver/tree/v7.2.0" }, "funding": [ { @@ -4460,7 +4461,7 @@ "type": "tidelift" } ], - "time": "2024-09-25T14:20:29+00:00" + "time": "2024-11-20T11:17:29+00:00" }, { "name": "symfony/polyfill-ctype", @@ -4488,8 +4489,8 @@ "type": "library", "extra": { "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -4564,8 +4565,8 @@ "type": "library", "extra": { "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -4642,8 +4643,8 @@ "type": "library", "extra": { "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -4702,16 +4703,16 @@ }, { "name": "symfony/process", - "version": "v7.1.7", + "version": "v7.2.0", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "9b8a40b7289767aa7117e957573c2a535efe6585" + "reference": "d34b22ba9390ec19d2dd966c40aa9e8462f27a7e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/9b8a40b7289767aa7117e957573c2a535efe6585", - "reference": "9b8a40b7289767aa7117e957573c2a535efe6585", + "url": "https://api.github.com/repos/symfony/process/zipball/d34b22ba9390ec19d2dd966c40aa9e8462f27a7e", + "reference": "d34b22ba9390ec19d2dd966c40aa9e8462f27a7e", "shasum": "" }, "require": { @@ -4743,7 +4744,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v7.1.7" + "source": "https://github.com/symfony/process/tree/v7.2.0" }, "funding": [ { @@ -4759,20 +4760,20 @@ "type": "tidelift" } ], - "time": "2024-11-06T09:25:12+00:00" + "time": "2024-11-06T14:24:19+00:00" }, { "name": "symfony/string", - "version": "v7.1.6", + "version": "v7.2.0", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "61b72d66bf96c360a727ae6232df5ac83c71f626" + "reference": "446e0d146f991dde3e73f45f2c97a9faad773c82" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/61b72d66bf96c360a727ae6232df5ac83c71f626", - "reference": "61b72d66bf96c360a727ae6232df5ac83c71f626", + "url": "https://api.github.com/repos/symfony/string/zipball/446e0d146f991dde3e73f45f2c97a9faad773c82", + "reference": "446e0d146f991dde3e73f45f2c97a9faad773c82", "shasum": "" }, "require": { @@ -4830,7 +4831,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v7.1.6" + "source": "https://github.com/symfony/string/tree/v7.2.0" }, "funding": [ { @@ -4846,7 +4847,7 @@ "type": "tidelift" } ], - "time": "2024-09-25T14:20:29+00:00" + "time": "2024-11-13T13:31:26+00:00" }, { "name": "theseer/tokenizer", @@ -4950,12 +4951,12 @@ ], "aliases": [], "minimum-stability": "stable", - "stability-flags": [], + "stability-flags": {}, "prefer-stable": false, "prefer-lowest": false, "platform": { - "php": ">=8.0" + "php": ">=8.1" }, - "platform-dev": [], + "platform-dev": {}, "plugin-api-version": "2.6.0" } diff --git a/src/Response.php b/src/Response.php index b498d983..6c054aa8 100755 --- a/src/Response.php +++ b/src/Response.php @@ -182,21 +182,75 @@ class Response ]; /** - * Mime Types with compression support + * Mime Types with compressible content * * @var array */ - protected $compressed = [ + private static $compressible = [ + // Text + 'text/html' => true, + 'text/richtext' => true, 'text/plain' => true, 'text/css' => true, - 'text/javascript' => true, + 'text/x-script' => true, + 'text/x-component' => true, + 'text/x-java-source' => true, + 'text/x-markdown' => true, + + // JavaScript 'application/javascript' => true, - 'text/html' => true, - 'text/html; charset=UTF-8' => true, + 'application/x-javascript' => true, + 'text/javascript' => true, + 'text/js' => true, + + // Icons + 'image/x-icon' => true, + 'image/vnd.microsoft.icon' => true, + + // Scripts + 'application/x-perl' => true, + 'application/x-httpd-cgi' => true, + + // XML and JSON + 'text/xml' => true, + 'application/xml' => true, + 'application/rss+xml' => true, + 'application/vnd.api+json' => true, + 'application/x-protobuf' => true, 'application/json' => true, - 'application/json; charset=UTF-8' => true, + 'application/manifest+json' => true, + 'application/ld+json' => true, + 'application/graphql+json' => true, + 'application/geo+json' => true, + + // Multipart + 'multipart/bag' => true, + 'multipart/mixed' => true, + + // XHTML + 'application/xhtml+xml' => true, + + // Fonts + 'font/ttf' => true, + 'font/otf' => true, + 'font/x-woff' => true, 'image/svg+xml' => true, - 'application/xml+rss' => true, + 'application/vnd.ms-fontobject' => true, + 'application/ttf' => true, + 'application/x-ttf' => true, + 'application/otf' => true, + 'application/x-otf' => true, + 'application/truetype' => true, + 'application/opentype' => true, + 'application/x-opentype' => true, + 'application/font-woff' => true, + 'application/eot' => true, + 'application/font' => true, + 'application/font-sfnt' => true, + + // WebAssembly + 'application/wasm' => true, + 'application/javascript-binast' => true, ]; public const COOKIE_SAMESITE_NONE = 'None'; @@ -272,6 +326,18 @@ public function __construct(float $time = 0) $this->startTime = (!empty($time)) ? $time : \microtime(true); } + private function isCompressible(?string $contentType): bool + { + if (!$contentType) { + return false; + } + + // Strip any parameters (e.g. ;charset=utf-8) + $contentType = strtolower(trim(explode(';', $contentType)[0])); + + return isset(self::$compressible[$contentType]); + } + /** * Set content type * @@ -535,7 +601,7 @@ public function send(string $body = ''): void // Compress body if ( !empty($this->acceptEncoding) && - isset($this->compressed[$this->contentType]) && + $this->isCompressible($this->contentType) && strlen($body) > $this->compressionMinSize ) { $algorithm = Compression::fromAcceptEncoding($this->acceptEncoding, $this->compressionSupported); From a1efe3e10038afe4109af833ce7a25a8ec4b5ed2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Mon, 3 Feb 2025 12:02:35 +0000 Subject: [PATCH 045/103] Fix formatting --- composer.lock | 111 +++++++++------------------------ src/Response.php | 16 ++--- tests/Validator/DomainTest.php | 1 + tests/Validator/HostTest.php | 1 + tests/Validator/IPTest.php | 1 + tests/Validator/URLTest.php | 1 + 6 files changed, 41 insertions(+), 90 deletions(-) diff --git a/composer.lock b/composer.lock index fb5541a1..342f10d4 100644 --- a/composer.lock +++ b/composer.lock @@ -337,16 +337,16 @@ }, { "name": "open-telemetry/api", - "version": "1.2.0", + "version": "1.2.1", "source": { "type": "git", "url": "https://github.com/opentelemetry-php/api.git", - "reference": "351a30baa79699de3de3a814c8ccc7b52ccdfb1d" + "reference": "74b1a03263be8c5acb578f41da054b4bac3af4a0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/opentelemetry-php/api/zipball/351a30baa79699de3de3a814c8ccc7b52ccdfb1d", - "reference": "351a30baa79699de3de3a814c8ccc7b52ccdfb1d", + "url": "https://api.github.com/repos/opentelemetry-php/api/zipball/74b1a03263be8c5acb578f41da054b4bac3af4a0", + "reference": "74b1a03263be8c5acb578f41da054b4bac3af4a0", "shasum": "" }, "require": { @@ -403,7 +403,7 @@ "issues": "https://github.com/open-telemetry/opentelemetry-php/issues", "source": "https://github.com/open-telemetry/opentelemetry-php" }, - "time": "2025-01-08T23:50:34+00:00" + "time": "2025-01-20T23:35:16+00:00" }, { "name": "open-telemetry/context", @@ -593,16 +593,16 @@ }, { "name": "open-telemetry/sdk", - "version": "1.2.0", + "version": "1.2.1", "source": { "type": "git", "url": "https://github.com/opentelemetry-php/sdk.git", - "reference": "9a1c3b866239dbff291e5cc555bb7793eab08127" + "reference": "96aeaee5b7cb8c0bc4af7ff4717b429f2d9f67e1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/opentelemetry-php/sdk/zipball/9a1c3b866239dbff291e5cc555bb7793eab08127", - "reference": "9a1c3b866239dbff291e5cc555bb7793eab08127", + "url": "https://api.github.com/repos/opentelemetry-php/sdk/zipball/96aeaee5b7cb8c0bc4af7ff4717b429f2d9f67e1", + "reference": "96aeaee5b7cb8c0bc4af7ff4717b429f2d9f67e1", "shasum": "" }, "require": { @@ -679,7 +679,7 @@ "issues": "https://github.com/open-telemetry/opentelemetry-php/issues", "source": "https://github.com/open-telemetry/opentelemetry-php" }, - "time": "2025-01-08T23:50:34+00:00" + "time": "2025-01-09T23:17:14+00:00" }, { "name": "open-telemetry/sem-conv", @@ -1330,16 +1330,16 @@ }, { "name": "symfony/http-client", - "version": "v7.2.2", + "version": "v7.2.3", "source": { "type": "git", "url": "https://github.com/symfony/http-client.git", - "reference": "339ba21476eb184290361542f732ad12c97591ec" + "reference": "7ce6078c79a4a7afff931c413d2959d3bffbfb8d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-client/zipball/339ba21476eb184290361542f732ad12c97591ec", - "reference": "339ba21476eb184290361542f732ad12c97591ec", + "url": "https://api.github.com/repos/symfony/http-client/zipball/7ce6078c79a4a7afff931c413d2959d3bffbfb8d", + "reference": "7ce6078c79a4a7afff931c413d2959d3bffbfb8d", "shasum": "" }, "require": { @@ -1405,7 +1405,7 @@ "http" ], "support": { - "source": "https://github.com/symfony/http-client/tree/v7.2.2" + "source": "https://github.com/symfony/http-client/tree/v7.2.3" }, "funding": [ { @@ -1421,7 +1421,7 @@ "type": "tidelift" } ], - "time": "2024-12-30T18:35:15+00:00" + "time": "2025-01-28T15:51:35+00:00" }, { "name": "symfony/http-client-contracts", @@ -2466,70 +2466,18 @@ }, "time": "2023-10-30T13:38:26+00:00" }, - { - "name": "phpbench/dom", - "version": "0.3.3", - "source": { - "type": "git", - "url": "https://github.com/phpbench/dom.git", - "reference": "786a96db538d0def931f5b19225233ec42ec7a72" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpbench/dom/zipball/786a96db538d0def931f5b19225233ec42ec7a72", - "reference": "786a96db538d0def931f5b19225233ec42ec7a72", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "php": "^7.3||^8.0" - }, - "require-dev": { - "friendsofphp/php-cs-fixer": "^3.14", - "phpstan/phpstan": "^1.10", - "phpunit/phpunit": "^8.0||^9.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, - "autoload": { - "psr-4": { - "PhpBench\\Dom\\": "lib/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Daniel Leech", - "email": "daniel@dantleech.com" - } - ], - "description": "DOM wrapper to simplify working with the PHP DOM implementation", - "support": { - "issues": "https://github.com/phpbench/dom/issues", - "source": "https://github.com/phpbench/dom/tree/0.3.3" - }, - "abandoned": true, - "time": "2023-03-06T23:46:57+00:00" - }, { "name": "phpbench/phpbench", - "version": "1.3.1", + "version": "1.4.0", "source": { "type": "git", "url": "https://github.com/phpbench/phpbench.git", - "reference": "a3e1ef08d9d7736d43a7fbd444893d6a073c0ca0" + "reference": "4248817222514421cba466bfa7adc7d8932345d4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpbench/phpbench/zipball/a3e1ef08d9d7736d43a7fbd444893d6a073c0ca0", - "reference": "a3e1ef08d9d7736d43a7fbd444893d6a073c0ca0", + "url": "https://api.github.com/repos/phpbench/phpbench/zipball/4248817222514421cba466bfa7adc7d8932345d4", + "reference": "4248817222514421cba466bfa7adc7d8932345d4", "shasum": "" }, "require": { @@ -2542,7 +2490,6 @@ "ext-tokenizer": "*", "php": "^8.1", "phpbench/container": "^2.2", - "phpbench/dom": "~0.3.3", "psr/log": "^1.1 || ^2.0 || ^3.0", "seld/jsonlint": "^1.1", "symfony/console": "^6.1 || ^7.0", @@ -2561,8 +2508,8 @@ "phpstan/extension-installer": "^1.1", "phpstan/phpstan": "^1.0", "phpstan/phpstan-phpunit": "^1.0", - "phpunit/phpunit": "^10.4", - "rector/rector": "^0.18.11 || ^1.0.0", + "phpunit/phpunit": "^10.4 || ^11.0", + "rector/rector": "^1.2", "symfony/error-handler": "^6.1 || ^7.0", "symfony/var-dumper": "^6.1 || ^7.0" }, @@ -2607,7 +2554,7 @@ ], "support": { "issues": "https://github.com/phpbench/phpbench/issues", - "source": "https://github.com/phpbench/phpbench/tree/1.3.1" + "source": "https://github.com/phpbench/phpbench/tree/1.4.0" }, "funding": [ { @@ -2615,20 +2562,20 @@ "type": "github" } ], - "time": "2024-06-30T11:04:37+00:00" + "time": "2025-01-26T19:54:45+00:00" }, { "name": "phpstan/phpstan", - "version": "1.12.15", + "version": "1.12.16", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "c91d4e8bc056f46cf653656e6f71004b254574d1" + "reference": "e0bb5cb78545aae631220735aa706eac633a6be9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/c91d4e8bc056f46cf653656e6f71004b254574d1", - "reference": "c91d4e8bc056f46cf653656e6f71004b254574d1", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/e0bb5cb78545aae631220735aa706eac633a6be9", + "reference": "e0bb5cb78545aae631220735aa706eac633a6be9", "shasum": "" }, "require": { @@ -2673,7 +2620,7 @@ "type": "github" } ], - "time": "2025-01-05T16:40:22+00:00" + "time": "2025-01-21T14:50:05+00:00" }, { "name": "phpunit/php-code-coverage", diff --git a/src/Response.php b/src/Response.php index 105bcbad..e4abbcfd 100755 --- a/src/Response.php +++ b/src/Response.php @@ -196,21 +196,21 @@ class Response 'text/x-component' => true, 'text/x-java-source' => true, 'text/x-markdown' => true, - + // JavaScript 'application/javascript' => true, 'application/x-javascript' => true, 'text/javascript' => true, 'text/js' => true, - + // Icons 'image/x-icon' => true, 'image/vnd.microsoft.icon' => true, - + // Scripts 'application/x-perl' => true, 'application/x-httpd-cgi' => true, - + // XML and JSON 'text/xml' => true, 'application/xml' => true, @@ -222,14 +222,14 @@ class Response 'application/ld+json' => true, 'application/graphql+json' => true, 'application/geo+json' => true, - + // Multipart 'multipart/bag' => true, 'multipart/mixed' => true, - + // XHTML 'application/xhtml+xml' => true, - + // Fonts 'font/ttf' => true, 'font/otf' => true, @@ -247,7 +247,7 @@ class Response 'application/eot' => true, 'application/font' => true, 'application/font-sfnt' => true, - + // WebAssembly 'application/wasm' => true, 'application/javascript-binast' => true, diff --git a/tests/Validator/DomainTest.php b/tests/Validator/DomainTest.php index ca14a20e..8707bf71 100644 --- a/tests/Validator/DomainTest.php +++ b/tests/Validator/DomainTest.php @@ -1,4 +1,5 @@ Date: Mon, 24 Feb 2025 17:03:42 +0000 Subject: [PATCH 046/103] Fix actions running after response in init --- composer.lock | 76 +++++++++++++++++++------------------- src/Response.php | 2 + tests/e2e/ResponseTest.php | 13 +++++++ tests/e2e/server.php | 19 ++++++++++ 4 files changed, 72 insertions(+), 38 deletions(-) diff --git a/composer.lock b/composer.lock index 342f10d4..b726c908 100644 --- a/composer.lock +++ b/composer.lock @@ -337,16 +337,16 @@ }, { "name": "open-telemetry/api", - "version": "1.2.1", + "version": "1.2.2", "source": { "type": "git", "url": "https://github.com/opentelemetry-php/api.git", - "reference": "74b1a03263be8c5acb578f41da054b4bac3af4a0" + "reference": "8b925df3047628968bc5be722468db1b98b82d51" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/opentelemetry-php/api/zipball/74b1a03263be8c5acb578f41da054b4bac3af4a0", - "reference": "74b1a03263be8c5acb578f41da054b4bac3af4a0", + "url": "https://api.github.com/repos/opentelemetry-php/api/zipball/8b925df3047628968bc5be722468db1b98b82d51", + "reference": "8b925df3047628968bc5be722468db1b98b82d51", "shasum": "" }, "require": { @@ -403,7 +403,7 @@ "issues": "https://github.com/open-telemetry/opentelemetry-php/issues", "source": "https://github.com/open-telemetry/opentelemetry-php" }, - "time": "2025-01-20T23:35:16+00:00" + "time": "2025-02-03T21:49:11+00:00" }, { "name": "open-telemetry/context", @@ -593,16 +593,16 @@ }, { "name": "open-telemetry/sdk", - "version": "1.2.1", + "version": "1.2.2", "source": { "type": "git", "url": "https://github.com/opentelemetry-php/sdk.git", - "reference": "96aeaee5b7cb8c0bc4af7ff4717b429f2d9f67e1" + "reference": "37eec0fe47ddd627911f318f29b6cd48196be0c0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/opentelemetry-php/sdk/zipball/96aeaee5b7cb8c0bc4af7ff4717b429f2d9f67e1", - "reference": "96aeaee5b7cb8c0bc4af7ff4717b429f2d9f67e1", + "url": "https://api.github.com/repos/opentelemetry-php/sdk/zipball/37eec0fe47ddd627911f318f29b6cd48196be0c0", + "reference": "37eec0fe47ddd627911f318f29b6cd48196be0c0", "shasum": "" }, "require": { @@ -679,24 +679,24 @@ "issues": "https://github.com/open-telemetry/opentelemetry-php/issues", "source": "https://github.com/open-telemetry/opentelemetry-php" }, - "time": "2025-01-09T23:17:14+00:00" + "time": "2025-01-29T21:40:28+00:00" }, { "name": "open-telemetry/sem-conv", - "version": "1.27.1", + "version": "1.30.0", "source": { "type": "git", "url": "https://github.com/opentelemetry-php/sem-conv.git", - "reference": "1dba705fea74bc0718d04be26090e3697e56f4e6" + "reference": "4178c9f390da8e4dbca9b181a9d1efd50cf7ee0a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/opentelemetry-php/sem-conv/zipball/1dba705fea74bc0718d04be26090e3697e56f4e6", - "reference": "1dba705fea74bc0718d04be26090e3697e56f4e6", + "url": "https://api.github.com/repos/opentelemetry-php/sem-conv/zipball/4178c9f390da8e4dbca9b181a9d1efd50cf7ee0a", + "reference": "4178c9f390da8e4dbca9b181a9d1efd50cf7ee0a", "shasum": "" }, "require": { - "php": "^8.1" + "php": "^8.0" }, "type": "library", "extra": { @@ -736,7 +736,7 @@ "issues": "https://github.com/open-telemetry/opentelemetry-php/issues", "source": "https://github.com/open-telemetry/opentelemetry-php" }, - "time": "2024-08-28T09:20:31+00:00" + "time": "2025-02-06T00:21:48+00:00" }, { "name": "php-http/discovery", @@ -2115,16 +2115,16 @@ }, { "name": "laravel/pint", - "version": "v1.20.0", + "version": "v1.21.0", "source": { "type": "git", "url": "https://github.com/laravel/pint.git", - "reference": "53072e8ea22213a7ed168a8a15b96fbb8b82d44b" + "reference": "531fa0871fbde719c51b12afa3a443b8f4e4b425" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/pint/zipball/53072e8ea22213a7ed168a8a15b96fbb8b82d44b", - "reference": "53072e8ea22213a7ed168a8a15b96fbb8b82d44b", + "url": "https://api.github.com/repos/laravel/pint/zipball/531fa0871fbde719c51b12afa3a443b8f4e4b425", + "reference": "531fa0871fbde719c51b12afa3a443b8f4e4b425", "shasum": "" }, "require": { @@ -2132,15 +2132,15 @@ "ext-mbstring": "*", "ext-tokenizer": "*", "ext-xml": "*", - "php": "^8.1.0" + "php": "^8.2.0" }, "require-dev": { - "friendsofphp/php-cs-fixer": "^3.66.0", - "illuminate/view": "^10.48.25", - "larastan/larastan": "^2.9.12", - "laravel-zero/framework": "^10.48.25", + "friendsofphp/php-cs-fixer": "^3.68.5", + "illuminate/view": "^11.42.0", + "larastan/larastan": "^3.0.4", + "laravel-zero/framework": "^11.36.1", "mockery/mockery": "^1.6.12", - "nunomaduro/termwind": "^1.17.0", + "nunomaduro/termwind": "^2.3", "pestphp/pest": "^2.36.0" }, "bin": [ @@ -2177,20 +2177,20 @@ "issues": "https://github.com/laravel/pint/issues", "source": "https://github.com/laravel/pint" }, - "time": "2025-01-14T16:20:53+00:00" + "time": "2025-02-18T03:18:57+00:00" }, { "name": "myclabs/deep-copy", - "version": "1.12.1", + "version": "1.13.0", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "123267b2c49fbf30d78a7b2d333f6be754b94845" + "reference": "024473a478be9df5fdaca2c793f2232fe788e414" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/123267b2c49fbf30d78a7b2d333f6be754b94845", - "reference": "123267b2c49fbf30d78a7b2d333f6be754b94845", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/024473a478be9df5fdaca2c793f2232fe788e414", + "reference": "024473a478be9df5fdaca2c793f2232fe788e414", "shasum": "" }, "require": { @@ -2229,7 +2229,7 @@ ], "support": { "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.12.1" + "source": "https://github.com/myclabs/DeepCopy/tree/1.13.0" }, "funding": [ { @@ -2237,7 +2237,7 @@ "type": "tidelift" } ], - "time": "2024-11-08T17:47:46+00:00" + "time": "2025-02-12T12:17:51+00:00" }, { "name": "nikic/php-parser", @@ -2566,16 +2566,16 @@ }, { "name": "phpstan/phpstan", - "version": "1.12.16", + "version": "1.12.19", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "e0bb5cb78545aae631220735aa706eac633a6be9" + "reference": "c42ba9bab7a940ed00092ecb1c77bad98896d789" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/e0bb5cb78545aae631220735aa706eac633a6be9", - "reference": "e0bb5cb78545aae631220735aa706eac633a6be9", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/c42ba9bab7a940ed00092ecb1c77bad98896d789", + "reference": "c42ba9bab7a940ed00092ecb1c77bad98896d789", "shasum": "" }, "require": { @@ -2620,7 +2620,7 @@ "type": "github" } ], - "time": "2025-01-21T14:50:05+00:00" + "time": "2025-02-19T15:42:21+00:00" }, { "name": "phpunit/php-code-coverage", diff --git a/src/Response.php b/src/Response.php index e4abbcfd..470097e2 100755 --- a/src/Response.php +++ b/src/Response.php @@ -639,6 +639,8 @@ public function send(string $body = ''): void $this->end(); } + $this->sent = true; + $this->disablePayload(); } diff --git a/tests/e2e/ResponseTest.php b/tests/e2e/ResponseTest.php index 878cc23f..bff46a55 100644 --- a/tests/e2e/ResponseTest.php +++ b/tests/e2e/ResponseTest.php @@ -43,4 +43,17 @@ public function testFile() $response = $this->client->call(Client::METHOD_GET, '/humans.txt'); $this->assertEquals(204, $response['headers']['status-code']); } + + public function testEarlyResponse() + { + // Ensure response from action is not recieved + $response = $this->client->call(Client::METHOD_GET, '/early-response'); + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertNotEquals('Action response', $response['body']); + + // 2nd request would catch if action from first ran + $response = $this->client->call(Client::METHOD_GET, '/early-response'); + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals('Init response. Actioned before: no', $response['body']); + } } diff --git a/tests/e2e/server.php b/tests/e2e/server.php index caa84ebe..a10d58ba 100644 --- a/tests/e2e/server.php +++ b/tests/e2e/server.php @@ -46,6 +46,25 @@ $response->noContent(); }); + +// Endpoints for early response +// Meant to run twice, so init hook can know if action ran +$earlyResponseAction = 'no'; +App::init() + ->groups(['early-response']) + ->inject('response') + ->action(function (Response $response) use ($earlyResponseAction) { + $response->send('Init response. Actioned before: ' . $earlyResponseAction); + }); + +App::get('/early-response') + ->groups(['early-response']) + ->inject('response') + ->action(function (Response $response) use (&$earlyResponseAction) { + $earlyResponseAction = 'yes'; + $response->send('Action response'); + }); + $request = new Request(); $response = new Response(); From e332b1c9776fc865292e6cb72cc961c54b72999d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Tue, 4 Mar 2025 18:49:49 +0000 Subject: [PATCH 047/103] Proof of content, error in alias --- composer.lock | 91 ++++++++++++++++---------------------- tests/e2e/Client.php | 24 +++++++++- tests/e2e/ResponseTest.php | 22 +++++++++ tests/e2e/server.php | 12 +++++ 4 files changed, 96 insertions(+), 53 deletions(-) diff --git a/composer.lock b/composer.lock index b726c908..0156c108 100644 --- a/composer.lock +++ b/composer.lock @@ -8,16 +8,16 @@ "packages": [ { "name": "brick/math", - "version": "0.12.1", + "version": "0.12.3", "source": { "type": "git", "url": "https://github.com/brick/math.git", - "reference": "f510c0a40911935b77b86859eb5223d58d660df1" + "reference": "866551da34e9a618e64a819ee1e01c20d8a588ba" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/brick/math/zipball/f510c0a40911935b77b86859eb5223d58d660df1", - "reference": "f510c0a40911935b77b86859eb5223d58d660df1", + "url": "https://api.github.com/repos/brick/math/zipball/866551da34e9a618e64a819ee1e01c20d8a588ba", + "reference": "866551da34e9a618e64a819ee1e01c20d8a588ba", "shasum": "" }, "require": { @@ -26,7 +26,7 @@ "require-dev": { "php-coveralls/php-coveralls": "^2.2", "phpunit/phpunit": "^10.1", - "vimeo/psalm": "5.16.0" + "vimeo/psalm": "6.8.8" }, "type": "library", "autoload": { @@ -56,7 +56,7 @@ ], "support": { "issues": "https://github.com/brick/math/issues", - "source": "https://github.com/brick/math/tree/0.12.1" + "source": "https://github.com/brick/math/tree/0.12.3" }, "funding": [ { @@ -64,7 +64,7 @@ "type": "github" } ], - "time": "2023-11-29T23:19:16+00:00" + "time": "2025-02-28T13:11:00+00:00" }, { "name": "composer/semver", @@ -1082,16 +1082,16 @@ }, { "name": "ramsey/collection", - "version": "2.0.0", + "version": "2.1.0", "source": { "type": "git", "url": "https://github.com/ramsey/collection.git", - "reference": "a4b48764bfbb8f3a6a4d1aeb1a35bb5e9ecac4a5" + "reference": "3c5990b8a5e0b79cd1cf11c2dc1229e58e93f109" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ramsey/collection/zipball/a4b48764bfbb8f3a6a4d1aeb1a35bb5e9ecac4a5", - "reference": "a4b48764bfbb8f3a6a4d1aeb1a35bb5e9ecac4a5", + "url": "https://api.github.com/repos/ramsey/collection/zipball/3c5990b8a5e0b79cd1cf11c2dc1229e58e93f109", + "reference": "3c5990b8a5e0b79cd1cf11c2dc1229e58e93f109", "shasum": "" }, "require": { @@ -1099,25 +1099,22 @@ }, "require-dev": { "captainhook/plugin-composer": "^5.3", - "ergebnis/composer-normalize": "^2.28.3", - "fakerphp/faker": "^1.21", + "ergebnis/composer-normalize": "^2.45", + "fakerphp/faker": "^1.24", "hamcrest/hamcrest-php": "^2.0", - "jangregor/phpstan-prophecy": "^1.0", - "mockery/mockery": "^1.5", + "jangregor/phpstan-prophecy": "^2.1", + "mockery/mockery": "^1.6", "php-parallel-lint/php-console-highlighter": "^1.0", - "php-parallel-lint/php-parallel-lint": "^1.3", - "phpcsstandards/phpcsutils": "^1.0.0-rc1", - "phpspec/prophecy-phpunit": "^2.0", - "phpstan/extension-installer": "^1.2", - "phpstan/phpstan": "^1.9", - "phpstan/phpstan-mockery": "^1.1", - "phpstan/phpstan-phpunit": "^1.3", - "phpunit/phpunit": "^9.5", - "psalm/plugin-mockery": "^1.1", - "psalm/plugin-phpunit": "^0.18.4", - "ramsey/coding-standard": "^2.0.3", - "ramsey/conventional-commits": "^1.3", - "vimeo/psalm": "^5.4" + "php-parallel-lint/php-parallel-lint": "^1.4", + "phpspec/prophecy-phpunit": "^2.3", + "phpstan/extension-installer": "^1.4", + "phpstan/phpstan": "^2.1", + "phpstan/phpstan-mockery": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpunit/phpunit": "^10.5", + "ramsey/coding-standard": "^2.3", + "ramsey/conventional-commits": "^1.6", + "roave/security-advisories": "dev-latest" }, "type": "library", "extra": { @@ -1155,19 +1152,9 @@ ], "support": { "issues": "https://github.com/ramsey/collection/issues", - "source": "https://github.com/ramsey/collection/tree/2.0.0" + "source": "https://github.com/ramsey/collection/tree/2.1.0" }, - "funding": [ - { - "url": "https://github.com/ramsey", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/ramsey/collection", - "type": "tidelift" - } - ], - "time": "2022-12-31T21:50:55+00:00" + "time": "2025-03-02T04:48:29+00:00" }, { "name": "ramsey/uuid", @@ -1330,16 +1317,16 @@ }, { "name": "symfony/http-client", - "version": "v7.2.3", + "version": "v7.2.4", "source": { "type": "git", "url": "https://github.com/symfony/http-client.git", - "reference": "7ce6078c79a4a7afff931c413d2959d3bffbfb8d" + "reference": "78981a2ffef6437ed92d4d7e2a86a82f256c6dc6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-client/zipball/7ce6078c79a4a7afff931c413d2959d3bffbfb8d", - "reference": "7ce6078c79a4a7afff931c413d2959d3bffbfb8d", + "url": "https://api.github.com/repos/symfony/http-client/zipball/78981a2ffef6437ed92d4d7e2a86a82f256c6dc6", + "reference": "78981a2ffef6437ed92d4d7e2a86a82f256c6dc6", "shasum": "" }, "require": { @@ -1405,7 +1392,7 @@ "http" ], "support": { - "source": "https://github.com/symfony/http-client/tree/v7.2.3" + "source": "https://github.com/symfony/http-client/tree/v7.2.4" }, "funding": [ { @@ -1421,7 +1408,7 @@ "type": "tidelift" } ], - "time": "2025-01-28T15:51:35+00:00" + "time": "2025-02-13T10:27:23+00:00" }, { "name": "symfony/http-client-contracts", @@ -4650,16 +4637,16 @@ }, { "name": "symfony/process", - "version": "v7.2.0", + "version": "v7.2.4", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "d34b22ba9390ec19d2dd966c40aa9e8462f27a7e" + "reference": "d8f411ff3c7ddc4ae9166fb388d1190a2df5b5cf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/d34b22ba9390ec19d2dd966c40aa9e8462f27a7e", - "reference": "d34b22ba9390ec19d2dd966c40aa9e8462f27a7e", + "url": "https://api.github.com/repos/symfony/process/zipball/d8f411ff3c7ddc4ae9166fb388d1190a2df5b5cf", + "reference": "d8f411ff3c7ddc4ae9166fb388d1190a2df5b5cf", "shasum": "" }, "require": { @@ -4691,7 +4678,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v7.2.0" + "source": "https://github.com/symfony/process/tree/v7.2.4" }, "funding": [ { @@ -4707,7 +4694,7 @@ "type": "tidelift" } ], - "time": "2024-11-06T14:24:19+00:00" + "time": "2025-02-05T08:33:46+00:00" }, { "name": "symfony/string", diff --git a/tests/e2e/Client.php b/tests/e2e/Client.php index 4052689b..86b96a02 100644 --- a/tests/e2e/Client.php +++ b/tests/e2e/Client.php @@ -54,17 +54,35 @@ public function __construct() public function call(string $method, string $path = '', array $headers = [], array $params = []) { usleep(50000); + $url = $this->baseUrl.$path.(($method == self::METHOD_GET && !empty($params)) ? '?'.http_build_query($params) : ''); $ch = curl_init($this->baseUrl.$path.(($method == self::METHOD_GET && !empty($params)) ? '?'.http_build_query($params) : '')); $responseHeaders = []; $responseStatus = -1; $responseType = ''; $responseBody = ''; + $query = match ($headers['content-type']) { + 'application/json' => \json_encode($params), + 'text/plain' => $params, + default => \http_build_query($params), + }; + + $formattedHeaders = []; + foreach ($headers as $key => $value) { + if (strtolower($key) === 'accept-encoding') { + curl_setopt($ch, CURLOPT_ENCODING, $value); + continue; + } else { + $formattedHeaders[] = $key . ': ' . $value; + } + } + + curl_setopt($ch, CURLOPT_PATH_AS_IS, 1); curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36'); - curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); + curl_setopt($ch, CURLOPT_HTTPHEADER, $formattedHeaders); curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 0); curl_setopt($ch, CURLOPT_TIMEOUT, 15); curl_setopt($ch, CURLOPT_HEADERFUNCTION, function ($curl, $header) use (&$responseHeaders) { @@ -80,6 +98,10 @@ public function call(string $method, string $path = '', array $headers = [], arr return $len; }); + if ($method !== self::METHOD_GET) { + curl_setopt($ch, CURLOPT_POSTFIELDS, $query); + } + $responseBody = curl_exec($ch); $responseStatus = curl_getinfo($ch, CURLINFO_HTTP_CODE); diff --git a/tests/e2e/ResponseTest.php b/tests/e2e/ResponseTest.php index bff46a55..87ce8168 100644 --- a/tests/e2e/ResponseTest.php +++ b/tests/e2e/ResponseTest.php @@ -56,4 +56,26 @@ public function testEarlyResponse() $this->assertEquals(200, $response['headers']['status-code']); $this->assertEquals('Init response. Actioned before: no', $response['body']); } + + public function testAliasWithParameter(): void + { + $response = $this->client->call(Client::METHOD_POST, '/functions/deployment', [ + 'content-type' => 'application/json' + ], [ + 'deploymentId' => 'deployment1' + ]); + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals('ID:deployment1', $response['body']); + + $response = $this->client->call(Client::METHOD_POST, '/functions/deployment', [ + 'content-type' => 'application/json' + ]); + $this->assertEquals(204, $response['headers']['status-code']); + + $response = $this->client->call(Client::METHOD_POST, '/functions/deployment/deployment2', [ + 'content-type' => 'application/json' + ]); + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals('ID:deployment2', $response['body']); + } } diff --git a/tests/e2e/server.php b/tests/e2e/server.php index a10d58ba..7a591238 100644 --- a/tests/e2e/server.php +++ b/tests/e2e/server.php @@ -46,6 +46,18 @@ $response->noContent(); }); +App::post('/functions/deployment') + ->alias('/functions/deployment/:deploymentId') + ->param('deploymentId', '', new Text(64, 0), '', true) + ->inject('response') + ->action(function (string $deploymentId, Response $response) { + if (empty($deploymentId)) { + $response->noContent(); + return; + } + + $response->send('ID:' . $deploymentId); + }); // Endpoints for early response // Meant to run twice, so init hook can know if action ran From 5771262c147b5bc5cb1e2a7942abc929c1e09b0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Tue, 4 Mar 2025 20:30:12 +0000 Subject: [PATCH 048/103] Fix edge-case --- src/Router.php | 6 +++++- tests/e2e/Client.php | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/Router.php b/src/Router.php index a2ad9b99..9e5c2772 100644 --- a/src/Router.php +++ b/src/Router.php @@ -101,12 +101,16 @@ public static function addRoute(Route $route): void */ public static function addRouteAlias(string $path, Route $route): void { - [$alias] = self::preparePath($path); + [$alias, $params] = self::preparePath($path); if (array_key_exists($alias, self::$routes[$route->getMethod()]) && !self::$allowOverride) { throw new Exception("Route for ({$route->getMethod()}:{$alias}) already registered."); } + foreach ($params as $key => $index) { + $route->setPathParam($key, $index); + } + self::$routes[$route->getMethod()][$alias] = $route; } diff --git a/tests/e2e/Client.php b/tests/e2e/Client.php index 86b96a02..cedd4cf8 100644 --- a/tests/e2e/Client.php +++ b/tests/e2e/Client.php @@ -61,7 +61,7 @@ public function call(string $method, string $path = '', array $headers = [], arr $responseType = ''; $responseBody = ''; - $query = match ($headers['content-type']) { + $query = match ($headers['content-type'] ?? '') { 'application/json' => \json_encode($params), 'text/plain' => $params, default => \http_build_query($params), From 1a515465257eeaa6c3bff4175f0c790c40387bbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Tue, 4 Mar 2025 22:15:32 +0000 Subject: [PATCH 049/103] Fix new edge case --- src/Route.php | 8 ++++---- src/Router.php | 10 +++++----- tests/e2e/ResponseTest.php | 16 ++++++++++------ tests/e2e/server.php | 9 +++++++++ 4 files changed, 28 insertions(+), 15 deletions(-) diff --git a/src/Route.php b/src/Route.php index fa4b25fa..aa0591be 100755 --- a/src/Route.php +++ b/src/Route.php @@ -28,7 +28,7 @@ class Route extends Hook /** * Path params. * - * @var array + * @var array> */ protected array $pathParams = []; @@ -141,9 +141,9 @@ public function getHook(): bool * @param int $index * @return void */ - public function setPathParam(string $key, int $index): void + public function setPathParam(string $path, string $key, int $index): void { - $this->pathParams[$key] = $index; + $this->pathParams[$path][$key] = $index; } /** @@ -157,7 +157,7 @@ public function getPathValues(Request $request): array $pathValues = []; $parts = explode('/', ltrim($request->getURI(), '/')); - foreach ($this->pathParams as $key => $index) { + foreach (($this->pathParams[$this->getPath()] ?? []) as $key => $index) { if (array_key_exists($index, $parts)) { $pathValues[$key] = $parts[$index]; } diff --git a/src/Router.php b/src/Router.php index 9e5c2772..76662fdb 100644 --- a/src/Router.php +++ b/src/Router.php @@ -86,7 +86,7 @@ public static function addRoute(Route $route): void } foreach ($params as $key => $index) { - $route->setPathParam($key, $index); + $route->setPathParam($path, $key, $index); } self::$routes[$route->getMethod()][$path] = $route; @@ -108,7 +108,7 @@ public static function addRouteAlias(string $path, Route $route): void } foreach ($params as $key => $index) { - $route->setPathParam($key, $index); + $route->setPathParam($alias, $key, $index); } self::$routes[$route->getMethod()][$alias] = $route; @@ -142,7 +142,7 @@ public static function match(string $method, string $path): Route|null ); if (array_key_exists($match, self::$routes[$method])) { - return self::$routes[$method][$match]; + return (self::$routes[$method][$match])->path($match); } } @@ -151,7 +151,7 @@ public static function match(string $method, string $path): Route|null */ $match = self::WILDCARD_TOKEN; if (array_key_exists($match, self::$routes[$method])) { - return self::$routes[$method][$match]; + return (self::$routes[$method][$match])->path($match); } /** @@ -161,7 +161,7 @@ public static function match(string $method, string $path): Route|null $current = ($current ?? '') . "{$part}/"; $match = $current . self::WILDCARD_TOKEN; if (array_key_exists($match, self::$routes[$method])) { - return self::$routes[$method][$match]; + return (self::$routes[$method][$match])->path($match); } } diff --git a/tests/e2e/ResponseTest.php b/tests/e2e/ResponseTest.php index 87ce8168..ecbb9761 100644 --- a/tests/e2e/ResponseTest.php +++ b/tests/e2e/ResponseTest.php @@ -67,15 +67,19 @@ public function testAliasWithParameter(): void $this->assertEquals(200, $response['headers']['status-code']); $this->assertEquals('ID:deployment1', $response['body']); - $response = $this->client->call(Client::METHOD_POST, '/functions/deployment', [ - 'content-type' => 'application/json' - ]); + $response = $this->client->call(Client::METHOD_POST, '/functions/deployment'); $this->assertEquals(204, $response['headers']['status-code']); - $response = $this->client->call(Client::METHOD_POST, '/functions/deployment/deployment2', [ - 'content-type' => 'application/json' - ]); + $response = $this->client->call(Client::METHOD_POST, '/functions/deployment/deployment2'); $this->assertEquals(200, $response['headers']['status-code']); $this->assertEquals('ID:deployment2', $response['body']); + + $response = $this->client->call(Client::METHOD_POST, '/database/collections/col1'); + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals(';col1', $response['body']); + + $response = $this->client->call(Client::METHOD_POST, '/databases/db2/collections/col2'); + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals('db2;col2', $response['body']); } } diff --git a/tests/e2e/server.php b/tests/e2e/server.php index 7a591238..179767cd 100644 --- a/tests/e2e/server.php +++ b/tests/e2e/server.php @@ -59,6 +59,15 @@ $response->send('ID:' . $deploymentId); }); +App::post('/databases/:databaseId/collections/:collectionId') + ->alias('/database/collections/:collectionId') + ->param('databaseId', '', new Text(64, 0), '', true) + ->param('collectionId', '', new Text(64, 0), '', true) + ->inject('response') + ->action(function (string $databaseId, string $collectionId, Response $response) { + $response->send($databaseId . ';' . $collectionId); + }); + // Endpoints for early response // Meant to run twice, so init hook can know if action ran $earlyResponseAction = 'no'; From 1d31f755348f78004f870693b4f072e600da2e5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Tue, 4 Mar 2025 22:31:03 +0000 Subject: [PATCH 050/103] Fix another edge case --- src/App.php | 4 +++- src/Route.php | 17 +++++++++++++++-- src/Router.php | 14 ++++++++++---- 3 files changed, 28 insertions(+), 7 deletions(-) diff --git a/src/App.php b/src/App.php index 63965ecf..aa2249a9 100755 --- a/src/App.php +++ b/src/App.php @@ -579,7 +579,9 @@ public function execute(Route $route, Request $request, Response $response): sta { $arguments = []; $groups = $route->getGroups(); - $pathValues = $route->getPathValues($request); + + $preparedPath = Router::preparePath($route->getMatchedPath()); + $pathValues = $route->getPathValues($request, $preparedPath[0]); try { if ($route->getHook()) { diff --git a/src/Route.php b/src/Route.php index aa0591be..f89139b0 100755 --- a/src/Route.php +++ b/src/Route.php @@ -46,6 +46,8 @@ class Route extends Hook */ protected int $order; + protected string $getMatchedPath = ''; + public function __construct(string $method, string $path) { $this->path($path); @@ -55,6 +57,17 @@ public function __construct(string $method, string $path) }; } + public function setMatchedPath(string $path): self + { + $this->getMatchedPath = $path; + return $this; + } + + public function getMatchedPath(): string + { + return $this->getMatchedPath; + } + /** * Get Route Order ID * @@ -152,12 +165,12 @@ public function setPathParam(string $path, string $key, int $index): void * @param \Utopia\Request $request * @return array */ - public function getPathValues(Request $request): array + public function getPathValues(Request $request, string $path): array { $pathValues = []; $parts = explode('/', ltrim($request->getURI(), '/')); - foreach (($this->pathParams[$this->getPath()] ?? []) as $key => $index) { + foreach (($this->pathParams[$path] ?? []) as $key => $index) { if (array_key_exists($index, $parts)) { $pathValues[$key] = $parts[$index]; } diff --git a/src/Router.php b/src/Router.php index 76662fdb..bc6e63cc 100644 --- a/src/Router.php +++ b/src/Router.php @@ -142,7 +142,9 @@ public static function match(string $method, string $path): Route|null ); if (array_key_exists($match, self::$routes[$method])) { - return (self::$routes[$method][$match])->path($match); + $route = self::$routes[$method][$match]; + $route->setMatchedPath($match); + return $route; } } @@ -151,7 +153,9 @@ public static function match(string $method, string $path): Route|null */ $match = self::WILDCARD_TOKEN; if (array_key_exists($match, self::$routes[$method])) { - return (self::$routes[$method][$match])->path($match); + $route = self::$routes[$method][$match]; + $route->setMatchedPath($match); + return $route; } /** @@ -161,7 +165,9 @@ public static function match(string $method, string $path): Route|null $current = ($current ?? '') . "{$part}/"; $match = $current . self::WILDCARD_TOKEN; if (array_key_exists($match, self::$routes[$method])) { - return (self::$routes[$method][$match])->path($match); + $route = self::$routes[$method][$match]; + $route->setMatchedPath($match); + return $route; } } @@ -196,7 +202,7 @@ protected static function combinations(array $set): iterable * @param string $path * @return array */ - protected static function preparePath(string $path): array + public static function preparePath(string $path): array { $parts = array_values(array_filter(explode('/', $path))); $prepare = ''; From c42db6d059fe10601a743ef8d6ff7141fb627be7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Thu, 6 Mar 2025 08:17:09 +0100 Subject: [PATCH 051/103] Apply suggestions from code review --- src/Route.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Route.php b/src/Route.php index f89139b0..7a64c732 100755 --- a/src/Route.php +++ b/src/Route.php @@ -46,7 +46,7 @@ class Route extends Hook */ protected int $order; - protected string $getMatchedPath = ''; + protected string $matchedPath = ''; public function __construct(string $method, string $path) { @@ -65,7 +65,7 @@ public function setMatchedPath(string $path): self public function getMatchedPath(): string { - return $this->getMatchedPath; + return $this->matchedPath; } /** From 897e9e8e22c34da6fe844a8a323dbdc5ebbf9eff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Thu, 6 Mar 2025 08:17:27 +0100 Subject: [PATCH 052/103] Apply suggestions from code review --- src/Route.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Route.php b/src/Route.php index 7a64c732..aa06a29c 100755 --- a/src/Route.php +++ b/src/Route.php @@ -59,7 +59,7 @@ public function __construct(string $method, string $path) public function setMatchedPath(string $path): self { - $this->getMatchedPath = $path; + $this->matchedPath = $path; return $this; } From f1f293cdfc7b5ba551ab3893ef53c00c3bcd6366 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Thu, 6 Mar 2025 10:49:42 +0000 Subject: [PATCH 053/103] Avoid breaking change --- src/Route.php | 12 +++++++++--- src/Router.php | 4 ++-- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/Route.php b/src/Route.php index aa06a29c..6828cffb 100755 --- a/src/Route.php +++ b/src/Route.php @@ -154,7 +154,7 @@ public function getHook(): bool * @param int $index * @return void */ - public function setPathParam(string $path, string $key, int $index): void + public function setPathParam(string $key, int $index, string $path = ''): void { $this->pathParams[$path][$key] = $index; } @@ -165,12 +165,18 @@ public function setPathParam(string $path, string $key, int $index): void * @param \Utopia\Request $request * @return array */ - public function getPathValues(Request $request, string $path): array + public function getPathValues(Request $request, string $path = ''): array { $pathValues = []; $parts = explode('/', ltrim($request->getURI(), '/')); - foreach (($this->pathParams[$path] ?? []) as $key => $index) { + if(empty($path)) { + $pathParams = $this->pathParams[$path] ?? $this->pathParams[0] ?? []; + } else { + $pathParams = $this->pathParams[$path] ?? []; + } + + foreach ($pathParams as $key => $index) { if (array_key_exists($index, $parts)) { $pathValues[$key] = $parts[$index]; } diff --git a/src/Router.php b/src/Router.php index bc6e63cc..eb2cea84 100644 --- a/src/Router.php +++ b/src/Router.php @@ -86,7 +86,7 @@ public static function addRoute(Route $route): void } foreach ($params as $key => $index) { - $route->setPathParam($path, $key, $index); + $route->setPathParam($key, $index, $path); } self::$routes[$route->getMethod()][$path] = $route; @@ -108,7 +108,7 @@ public static function addRouteAlias(string $path, Route $route): void } foreach ($params as $key => $index) { - $route->setPathParam($alias, $key, $index); + $route->setPathParam($key, $index, $alias); } self::$routes[$route->getMethod()][$alias] = $route; From 3ee429f66353f2b7c635e15405a020d98612093c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Thu, 6 Mar 2025 10:49:47 +0000 Subject: [PATCH 054/103] Update dependencies --- composer.lock | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/composer.lock b/composer.lock index 0156c108..698e26e8 100644 --- a/composer.lock +++ b/composer.lock @@ -149,16 +149,16 @@ }, { "name": "google/protobuf", - "version": "v4.29.3", + "version": "v4.30.0", "source": { "type": "git", "url": "https://github.com/protocolbuffers/protobuf-php.git", - "reference": "ab5077c2cfdd1f415f42d11fdbdf903ba8e3d9b7" + "reference": "e1d66682f6836aa87820400f0aa07d9eb566feb6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/protocolbuffers/protobuf-php/zipball/ab5077c2cfdd1f415f42d11fdbdf903ba8e3d9b7", - "reference": "ab5077c2cfdd1f415f42d11fdbdf903ba8e3d9b7", + "url": "https://api.github.com/repos/protocolbuffers/protobuf-php/zipball/e1d66682f6836aa87820400f0aa07d9eb566feb6", + "reference": "e1d66682f6836aa87820400f0aa07d9eb566feb6", "shasum": "" }, "require": { @@ -187,9 +187,9 @@ "proto" ], "support": { - "source": "https://github.com/protocolbuffers/protobuf-php/tree/v4.29.3" + "source": "https://github.com/protocolbuffers/protobuf-php/tree/v4.30.0" }, - "time": "2025-01-08T21:00:13+00:00" + "time": "2025-03-04T22:54:49+00:00" }, { "name": "nyholm/psr7", @@ -337,16 +337,16 @@ }, { "name": "open-telemetry/api", - "version": "1.2.2", + "version": "1.2.3", "source": { "type": "git", "url": "https://github.com/opentelemetry-php/api.git", - "reference": "8b925df3047628968bc5be722468db1b98b82d51" + "reference": "199d7ddda88f5f5619fa73463f1a5a7149ccd1f1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/opentelemetry-php/api/zipball/8b925df3047628968bc5be722468db1b98b82d51", - "reference": "8b925df3047628968bc5be722468db1b98b82d51", + "url": "https://api.github.com/repos/opentelemetry-php/api/zipball/199d7ddda88f5f5619fa73463f1a5a7149ccd1f1", + "reference": "199d7ddda88f5f5619fa73463f1a5a7149ccd1f1", "shasum": "" }, "require": { @@ -403,7 +403,7 @@ "issues": "https://github.com/open-telemetry/opentelemetry-php/issues", "source": "https://github.com/open-telemetry/opentelemetry-php" }, - "time": "2025-02-03T21:49:11+00:00" + "time": "2025-03-05T21:42:54+00:00" }, { "name": "open-telemetry/context", @@ -2553,16 +2553,16 @@ }, { "name": "phpstan/phpstan", - "version": "1.12.19", + "version": "1.12.20", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "c42ba9bab7a940ed00092ecb1c77bad98896d789" + "reference": "3240b1972042c7f73cf1045e879ea5bd5f761bb7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/c42ba9bab7a940ed00092ecb1c77bad98896d789", - "reference": "c42ba9bab7a940ed00092ecb1c77bad98896d789", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/3240b1972042c7f73cf1045e879ea5bd5f761bb7", + "reference": "3240b1972042c7f73cf1045e879ea5bd5f761bb7", "shasum": "" }, "require": { @@ -2607,7 +2607,7 @@ "type": "github" } ], - "time": "2025-02-19T15:42:21+00:00" + "time": "2025-03-05T13:37:43+00:00" }, { "name": "phpunit/php-code-coverage", From 71d7e75889f8828f82fca029f1f6597048a3d4f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Thu, 6 Mar 2025 11:51:45 +0100 Subject: [PATCH 055/103] Revert "Avoid breaking change" This reverts commit f1f293cdfc7b5ba551ab3893ef53c00c3bcd6366. --- src/Route.php | 12 +++--------- src/Router.php | 4 ++-- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/src/Route.php b/src/Route.php index 6828cffb..aa06a29c 100755 --- a/src/Route.php +++ b/src/Route.php @@ -154,7 +154,7 @@ public function getHook(): bool * @param int $index * @return void */ - public function setPathParam(string $key, int $index, string $path = ''): void + public function setPathParam(string $path, string $key, int $index): void { $this->pathParams[$path][$key] = $index; } @@ -165,18 +165,12 @@ public function setPathParam(string $key, int $index, string $path = ''): void * @param \Utopia\Request $request * @return array */ - public function getPathValues(Request $request, string $path = ''): array + public function getPathValues(Request $request, string $path): array { $pathValues = []; $parts = explode('/', ltrim($request->getURI(), '/')); - if(empty($path)) { - $pathParams = $this->pathParams[$path] ?? $this->pathParams[0] ?? []; - } else { - $pathParams = $this->pathParams[$path] ?? []; - } - - foreach ($pathParams as $key => $index) { + foreach (($this->pathParams[$path] ?? []) as $key => $index) { if (array_key_exists($index, $parts)) { $pathValues[$key] = $parts[$index]; } diff --git a/src/Router.php b/src/Router.php index eb2cea84..bc6e63cc 100644 --- a/src/Router.php +++ b/src/Router.php @@ -86,7 +86,7 @@ public static function addRoute(Route $route): void } foreach ($params as $key => $index) { - $route->setPathParam($key, $index, $path); + $route->setPathParam($path, $key, $index); } self::$routes[$route->getMethod()][$path] = $route; @@ -108,7 +108,7 @@ public static function addRouteAlias(string $path, Route $route): void } foreach ($params as $key => $index) { - $route->setPathParam($key, $index, $alias); + $route->setPathParam($alias, $key, $index); } self::$routes[$route->getMethod()][$alias] = $route; From 719b5dcbaa1892129ca384067c90ba7c1c4eca9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Thu, 6 Mar 2025 11:52:03 +0100 Subject: [PATCH 056/103] Reapply "Avoid breaking change" This reverts commit 71d7e75889f8828f82fca029f1f6597048a3d4f7. --- src/Route.php | 12 +++++++++--- src/Router.php | 4 ++-- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/Route.php b/src/Route.php index aa06a29c..6828cffb 100755 --- a/src/Route.php +++ b/src/Route.php @@ -154,7 +154,7 @@ public function getHook(): bool * @param int $index * @return void */ - public function setPathParam(string $path, string $key, int $index): void + public function setPathParam(string $key, int $index, string $path = ''): void { $this->pathParams[$path][$key] = $index; } @@ -165,12 +165,18 @@ public function setPathParam(string $path, string $key, int $index): void * @param \Utopia\Request $request * @return array */ - public function getPathValues(Request $request, string $path): array + public function getPathValues(Request $request, string $path = ''): array { $pathValues = []; $parts = explode('/', ltrim($request->getURI(), '/')); - foreach (($this->pathParams[$path] ?? []) as $key => $index) { + if(empty($path)) { + $pathParams = $this->pathParams[$path] ?? $this->pathParams[0] ?? []; + } else { + $pathParams = $this->pathParams[$path] ?? []; + } + + foreach ($pathParams as $key => $index) { if (array_key_exists($index, $parts)) { $pathValues[$key] = $parts[$index]; } diff --git a/src/Router.php b/src/Router.php index bc6e63cc..eb2cea84 100644 --- a/src/Router.php +++ b/src/Router.php @@ -86,7 +86,7 @@ public static function addRoute(Route $route): void } foreach ($params as $key => $index) { - $route->setPathParam($path, $key, $index); + $route->setPathParam($key, $index, $path); } self::$routes[$route->getMethod()][$path] = $route; @@ -108,7 +108,7 @@ public static function addRouteAlias(string $path, Route $route): void } foreach ($params as $key => $index) { - $route->setPathParam($alias, $key, $index); + $route->setPathParam($key, $index, $alias); } self::$routes[$route->getMethod()][$alias] = $route; From 916424c2e4a8159333c837af2de7949dfe05b6d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Thu, 6 Mar 2025 10:54:51 +0000 Subject: [PATCH 057/103] quality fix --- src/Route.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Route.php b/src/Route.php index 6828cffb..714b0a79 100755 --- a/src/Route.php +++ b/src/Route.php @@ -170,8 +170,8 @@ public function getPathValues(Request $request, string $path = ''): array $pathValues = []; $parts = explode('/', ltrim($request->getURI(), '/')); - if(empty($path)) { - $pathParams = $this->pathParams[$path] ?? $this->pathParams[0] ?? []; + if (empty($path)) { + $pathParams = $this->pathParams[$path] ?? \array_values($this->pathParams)[0] ?? []; } else { $pathParams = $this->pathParams[$path] ?? []; } From 26f6c5a0651612aca1eece8ddb5e39038aa66bc0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Sun, 18 May 2025 23:45:10 +0000 Subject: [PATCH 058/103] deprecated params --- src/Hook.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Hook.php b/src/Hook.php index 4b933528..029eeb50 100644 --- a/src/Hook.php +++ b/src/Hook.php @@ -197,9 +197,10 @@ public function inject(string $injection): static * @param bool $optional * @param array $injections * @param bool $skipValidation + * @param bool $deprecated * @return static */ - public function param(string $key, mixed $default, Validator|callable $validator, string $description = '', bool $optional = false, array $injections = [], bool $skipValidation = false): static + public function param(string $key, mixed $default, Validator|callable $validator, string $description = '', bool $optional = false, array $injections = [], bool $skipValidation = false, bool $deprecated = false): static { $this->params[$key] = [ 'default' => $default, @@ -208,6 +209,7 @@ public function param(string $key, mixed $default, Validator|callable $validator 'optional' => $optional, 'injections' => $injections, 'skipValidation' => $skipValidation, + 'deprecated' => $deprecated, 'value' => null, 'order' => count($this->params) + count($this->injections), ]; From d3e12307739b7f7930d7a69b1cac332714c385dd Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Thu, 22 May 2025 17:14:56 +0100 Subject: [PATCH 059/103] fix: add missing status codes --- src/Http/Response.php | 77 ++++++++++++++++++++++--------------------- 1 file changed, 39 insertions(+), 38 deletions(-) diff --git a/src/Http/Response.php b/src/Http/Response.php index 8d085d63..8b319679 100755 --- a/src/Http/Response.php +++ b/src/Http/Response.php @@ -42,92 +42,74 @@ abstract class Response * HTTP response status codes */ public const STATUS_CODE_CONTINUE = 100; - public const STATUS_CODE_SWITCHING_PROTOCOLS = 101; + public const STATUS_CODE_PROCESSING = 102; + public const STATUS_CODE_EARLY_HINTS = 103; public const STATUS_CODE_OK = 200; - public const STATUS_CODE_CREATED = 201; - public const STATUS_CODE_ACCEPTED = 202; - public const STATUS_CODE_NON_AUTHORITATIVE_INFORMATION = 203; - public const STATUS_CODE_NOCONTENT = 204; - public const STATUS_CODE_RESETCONTENT = 205; - public const STATUS_CODE_PARTIALCONTENT = 206; + public const STATUS_CODE_MULTI_STATUS = 207; + public const STATUS_CODE_ALREADY_REPORTED = 208; + public const STATUS_CODE_IM_USED = 226; public const STATUS_CODE_MULTIPLE_CHOICES = 300; - public const STATUS_CODE_MOVED_PERMANENTLY = 301; - public const STATUS_CODE_FOUND = 302; - public const STATUS_CODE_SEE_OTHER = 303; - public const STATUS_CODE_NOT_MODIFIED = 304; - public const STATUS_CODE_USE_PROXY = 305; - public const STATUS_CODE_UNUSED = 306; - public const STATUS_CODE_TEMPORARY_REDIRECT = 307; + public const STATUS_CODE_PERMANENT_REDIRECT = 308; public const STATUS_CODE_BAD_REQUEST = 400; - public const STATUS_CODE_UNAUTHORIZED = 401; - public const STATUS_CODE_PAYMENT_REQUIRED = 402; - public const STATUS_CODE_FORBIDDEN = 403; - public const STATUS_CODE_NOT_FOUND = 404; - public const STATUS_CODE_METHOD_NOT_ALLOWED = 405; - public const STATUS_CODE_NOT_ACCEPTABLE = 406; - public const STATUS_CODE_PROXY_AUTHENTICATION_REQUIRED = 407; - public const STATUS_CODE_REQUEST_TIMEOUT = 408; - public const STATUS_CODE_CONFLICT = 409; - public const STATUS_CODE_GONE = 410; - public const STATUS_CODE_LENGTH_REQUIRED = 411; - public const STATUS_CODE_PRECONDITION_FAILED = 412; - public const STATUS_CODE_REQUEST_ENTITY_TOO_LARGE = 413; - public const STATUS_CODE_REQUEST_URI_TOO_LONG = 414; - public const STATUS_CODE_UNSUPPORTED_MEDIA_TYPE = 415; - public const STATUS_CODE_REQUESTED_RANGE_NOT_SATISFIABLE = 416; - public const STATUS_CODE_EXPECTATION_FAILED = 417; - + public const STATUS_CODE_IM_A_TEAPOT = 418; + public const STATUS_CODE_MISDIRECTED_REQUEST = 421; + public const STATUS_CODE_UNPROCESSABLE_ENTITY = 422; + public const STATUS_CODE_LOCKED = 423; + public const STATUS_CODE_FAILED_DEPENDENCY = 424; public const STATUS_CODE_TOO_EARLY = 425; - + public const STATUS_CODE_UPGRADE_REQUIRED = 426; + public const STATUS_CODE_PRECONDITION_REQUIRED = 428; public const STATUS_CODE_TOO_MANY_REQUESTS = 429; + public const STATUS_CODE_REQUEST_HEADER_FIELDS_TOO_LARGE = 431; + public const STATUS_CODE_UNAVAILABLE_FOR_LEGAL_REASONS = 451; public const STATUS_CODE_UNAVAILABLE_FOR_LEGAL_REASONS = 451; public const STATUS_CODE_INTERNAL_SERVER_ERROR = 500; - public const STATUS_CODE_NOT_IMPLEMENTED = 501; - public const STATUS_CODE_BAD_GATEWAY = 502; - public const STATUS_CODE_SERVICE_UNAVAILABLE = 503; - public const STATUS_CODE_GATEWAY_TIMEOUT = 504; - public const STATUS_CODE_HTTP_VERSION_NOT_SUPPORTED = 505; + public const STATUS_CODE_VARIANT_ALSO_NEGOTIATES = 506; + public const STATUS_CODE_INSUFFICIENT_STORAGE = 507; + public const STATUS_CODE_LOOP_DETECTED = 508; + public const STATUS_CODE_NOT_EXTENDED = 510; + public const STATUS_CODE_NETWORK_AUTHENTICATION_REQUIRED = 511; /** * @var array @@ -135,6 +117,8 @@ abstract class Response protected $statusCodes = [ self::STATUS_CODE_CONTINUE => 'Continue', self::STATUS_CODE_SWITCHING_PROTOCOLS => 'Switching Protocols', + self::STATUS_CODE_PROCESSING => 'Processing', + self::STATUS_CODE_EARLY_HINTS => 'Early Hints', self::STATUS_CODE_OK => 'OK', self::STATUS_CODE_CREATED => 'Created', self::STATUS_CODE_ACCEPTED => 'Accepted', @@ -142,6 +126,9 @@ abstract class Response self::STATUS_CODE_NOCONTENT => 'No Content', self::STATUS_CODE_RESETCONTENT => 'Reset Content', self::STATUS_CODE_PARTIALCONTENT => 'Partial Content', + self::STATUS_CODE_MULTI_STATUS => 'Multi-Status', + self::STATUS_CODE_ALREADY_REPORTED => 'Already Reported', + self::STATUS_CODE_IM_USED => 'IM Used', self::STATUS_CODE_MULTIPLE_CHOICES => 'Multiple Choices', self::STATUS_CODE_MOVED_PERMANENTLY => 'Moved Permanently', self::STATUS_CODE_FOUND => 'Found', @@ -150,6 +137,7 @@ abstract class Response self::STATUS_CODE_USE_PROXY => 'Use Proxy', self::STATUS_CODE_UNUSED => '(Unused)', self::STATUS_CODE_TEMPORARY_REDIRECT => 'Temporary Redirect', + self::STATUS_CODE_PERMANENT_REDIRECT => 'Permanent Redirect', self::STATUS_CODE_BAD_REQUEST => 'Bad Request', self::STATUS_CODE_UNAUTHORIZED => 'Unauthorized', self::STATUS_CODE_PAYMENT_REQUIRED => 'Payment Required', @@ -168,8 +156,16 @@ abstract class Response self::STATUS_CODE_UNSUPPORTED_MEDIA_TYPE => 'Unsupported Media Type', self::STATUS_CODE_REQUESTED_RANGE_NOT_SATISFIABLE => 'Requested Range Not Satisfiable', self::STATUS_CODE_EXPECTATION_FAILED => 'Expectation Failed', + self::STATUS_CODE_IM_A_TEAPOT => 'I\'m a teapot', + self::STATUS_CODE_MISDIRECTED_REQUEST => 'Misdirected Request', + self::STATUS_CODE_UNPROCESSABLE_ENTITY => 'Unprocessable Entity', + self::STATUS_CODE_LOCKED => 'Locked', + self::STATUS_CODE_FAILED_DEPENDENCY => 'Failed Dependency', self::STATUS_CODE_TOO_EARLY => 'Too Early', + self::STATUS_CODE_UPGRADE_REQUIRED => 'Upgrade Required', + self::STATUS_CODE_PRECONDITION_REQUIRED => 'Precondition Required', self::STATUS_CODE_TOO_MANY_REQUESTS => 'Too Many Requests', + self::STATUS_CODE_REQUEST_HEADER_FIELDS_TOO_LARGE => 'Request Header Fields Too Large', self::STATUS_CODE_UNAVAILABLE_FOR_LEGAL_REASONS => 'Unavailable For Legal Reasons', self::STATUS_CODE_INTERNAL_SERVER_ERROR => 'Internal Server Error', self::STATUS_CODE_NOT_IMPLEMENTED => 'Not Implemented', @@ -177,6 +173,11 @@ abstract class Response self::STATUS_CODE_SERVICE_UNAVAILABLE => 'Service Unavailable', self::STATUS_CODE_GATEWAY_TIMEOUT => 'Gateway Timeout', self::STATUS_CODE_HTTP_VERSION_NOT_SUPPORTED => 'HTTP Version Not Supported', + self::STATUS_CODE_VARIANT_ALSO_NEGOTIATES => 'Variant Also Negotiates', + self::STATUS_CODE_INSUFFICIENT_STORAGE => 'Insufficient Storage', + self::STATUS_CODE_LOOP_DETECTED => 'Loop Detected', + self::STATUS_CODE_NOT_EXTENDED => 'Not Extended', + self::STATUS_CODE_NETWORK_AUTHENTICATION_REQUIRED => 'Network Authentication Required', ]; /** From 3e8733e0b2f07ce1b8034ced3246b3b08ce9e1be Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Thu, 22 May 2025 17:17:24 +0100 Subject: [PATCH 060/103] ci: fix docker-compose --- .github/workflows/test.yml | 4 ++-- CONTRIBUTING.md | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e17e5507..2c707436 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -19,7 +19,7 @@ jobs: run: composer validate --strict - name: Setup Docker - run: docker-compose up -d --build + run: docker compose up -d --build - name: Wait for Server to be ready run: sleep 10 @@ -28,4 +28,4 @@ jobs: run: docker compose exec fpm vendor/bin/phpunit --configuration phpunit.xml - name: Run Swoole Tests - run: docker compose exec swoole vendor/bin/phpunit --configuration phpunit.xml \ No newline at end of file + run: docker compose exec swoole vendor/bin/phpunit --configuration phpunit.xml diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3ab8893b..361c6a7b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -66,9 +66,9 @@ $ git push origin [name_of_your_new_branch] ### Testing -- `docker-compose up -d` -- `docker-compose exec web vendor/bin/phpunit --configuration phpunit.xml` -- `docker-compose exec web vendor/bin/psalm --show-info=true` +- `docker compose up -d` +- `docker compose exec web vendor/bin/phpunit --configuration phpunit.xml` +- `docker compose exec web vendor/bin/psalm --show-info=true` ## Introducing New Features From 35839e91e9b147da2ddf66f31d02b980b833c3e4 Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Thu, 22 May 2025 19:12:17 +0100 Subject: [PATCH 061/103] fix: duplicate --- src/Http/Response.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Http/Response.php b/src/Http/Response.php index 8b319679..e4545325 100755 --- a/src/Http/Response.php +++ b/src/Http/Response.php @@ -97,8 +97,6 @@ abstract class Response public const STATUS_CODE_REQUEST_HEADER_FIELDS_TOO_LARGE = 431; public const STATUS_CODE_UNAVAILABLE_FOR_LEGAL_REASONS = 451; - public const STATUS_CODE_UNAVAILABLE_FOR_LEGAL_REASONS = 451; - public const STATUS_CODE_INTERNAL_SERVER_ERROR = 500; public const STATUS_CODE_NOT_IMPLEMENTED = 501; public const STATUS_CODE_BAD_GATEWAY = 502; From 0057b7689d6eb887dc1f9050e2065980c24c2afc Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Mon, 30 Jun 2025 09:03:16 +0000 Subject: [PATCH 062/103] Fix: handle null path, default to `/` --- src/App.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/App.php b/src/App.php index aa2249a9..e553bcee 100755 --- a/src/App.php +++ b/src/App.php @@ -561,6 +561,11 @@ public function match(Request $request, bool $fresh = false): ?Route } $url = \parse_url($request->getURI(), PHP_URL_PATH); + + if ($url === null || $url === false) { + $url = '/'; // Default to root path for malformed URLs + } + $method = $request->getMethod(); $method = (self::REQUEST_METHOD_HEAD == $method) ? self::REQUEST_METHOD_GET : $method; From f69f100e42137c9fd1c5945e86d83c83e9210ab4 Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Mon, 30 Jun 2025 09:12:26 +0000 Subject: [PATCH 063/103] Tests --- tests/AppTest.php | 54 +++++++++++++++++++++++++++++++++++++- tests/e2e/ResponseTest.php | 17 ++++++++++++ 2 files changed, 70 insertions(+), 1 deletion(-) diff --git a/tests/AppTest.php b/tests/AppTest.php index 5242e838..3afa8585 100755 --- a/tests/AppTest.php +++ b/tests/AppTest.php @@ -221,7 +221,7 @@ public function testCanExecuteRoute(): void ->param('x', 'x-def', new Text(1, min: 0), 'x param', false) ->param('y', 'y-def', new Text(1, min: 0), 'y param', false) ->action(function ($x, $y) { - echo $x . '-', $y; + echo $x . '-' . $y; }); \ob_start(); @@ -497,6 +497,58 @@ public function testCanMatchRoute(string $method, string $path, string $url = nu $this->assertEquals($expected, $this->app->getRoute()); } + public function testMatchWithNullPath(): void + { + // Create a route for root path + $expected = App::get('/'); + + // Test case where parse_url returns null (malformed URL) + $_SERVER['REQUEST_METHOD'] = 'GET'; + $_SERVER['REQUEST_URI'] = '://invalid-url'; // This will cause parse_url to return null for PATH component + + $matched = $this->app->match(new Request()); + $this->assertEquals($expected, $matched); + } + + public function testMatchWithEmptyPath(): void + { + // Create a route for root path + $expected = App::get('/'); + + // Test case where URI has no path component + $_SERVER['REQUEST_METHOD'] = 'GET'; + $_SERVER['REQUEST_URI'] = 'https://example.com'; // No path component + + $matched = $this->app->match(new Request()); + $this->assertEquals($expected, $matched); + } + + public function testMatchWithMalformedURL(): void + { + // Create a route for root path + $expected = App::get('/'); + + // Test case where parse_url returns false (severely malformed URL) + $_SERVER['REQUEST_METHOD'] = 'GET'; + $_SERVER['REQUEST_URI'] = 'ht!tp://invalid'; // Malformed scheme + + $matched = $this->app->match(new Request()); + $this->assertEquals($expected, $matched); + } + + public function testMatchWithOnlyQueryString(): void + { + // Create a route for root path + $expected = App::get('/'); + + // Test case where URI has only query string (no path) + $_SERVER['REQUEST_METHOD'] = 'GET'; + $_SERVER['REQUEST_URI'] = '?param=value'; // Only query string, no path + + $matched = $this->app->match(new Request()); + $this->assertEquals($expected, $matched); + } + public function testNoMismatchRoute(): void { $requests = [ diff --git a/tests/e2e/ResponseTest.php b/tests/e2e/ResponseTest.php index ecbb9761..64ebfef4 100644 --- a/tests/e2e/ResponseTest.php +++ b/tests/e2e/ResponseTest.php @@ -57,6 +57,23 @@ public function testEarlyResponse() $this->assertEquals('Init response. Actioned before: no', $response['body']); } + public function testNullPathHandling() + { + // Test that malformed URLs default to root path + $response = $this->client->call(Client::METHOD_GET, '/'); + $this->assertEquals('Hello World!', $response['body']); + $this->assertEquals(200, $response['headers']['status-code']); + } + + public function testRootPathFallback() + { + // Test that when path parsing fails, it falls back to root + // This is tested by ensuring the root route works correctly + $response = $this->client->call(Client::METHOD_GET, '/'); + $this->assertEquals('Hello World!', $response['body']); + $this->assertEquals(200, $response['headers']['status-code']); + } + public function testAliasWithParameter(): void { $response = $this->client->call(Client::METHOD_POST, '/functions/deployment', [ From 13d59cb21a0e074864628e1cdb7c67a4ef53ceed Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Mon, 30 Jun 2025 09:24:16 +0000 Subject: [PATCH 064/103] update app test --- tests/AppTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/AppTest.php b/tests/AppTest.php index 3afa8585..371f4a8e 100755 --- a/tests/AppTest.php +++ b/tests/AppTest.php @@ -504,7 +504,7 @@ public function testMatchWithNullPath(): void // Test case where parse_url returns null (malformed URL) $_SERVER['REQUEST_METHOD'] = 'GET'; - $_SERVER['REQUEST_URI'] = '://invalid-url'; // This will cause parse_url to return null for PATH component + $_SERVER['REQUEST_URI'] = '?param=1'; // This will cause parse_url to return null for PATH component $matched = $this->app->match(new Request()); $this->assertEquals($expected, $matched); @@ -530,7 +530,7 @@ public function testMatchWithMalformedURL(): void // Test case where parse_url returns false (severely malformed URL) $_SERVER['REQUEST_METHOD'] = 'GET'; - $_SERVER['REQUEST_URI'] = 'ht!tp://invalid'; // Malformed scheme + $_SERVER['REQUEST_URI'] = '#fragment'; // Malformed scheme $matched = $this->app->match(new Request()); $this->assertEquals($expected, $matched); From 804be9e0810cfd70c1c2ffa3e696e0def49ed9fd Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Mon, 30 Jun 2025 09:30:42 +0000 Subject: [PATCH 065/103] Fix codeql --- src/App.php | 2 +- src/Request.php | 2 +- src/Response.php | 4 ++-- tests/AppTest.php | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/App.php b/src/App.php index e553bcee..d041bc3b 100755 --- a/src/App.php +++ b/src/App.php @@ -344,7 +344,7 @@ public static function error(): Hook * @param string|null $default * @return string|null */ - public static function getEnv(string $key, string $default = null): ?string + public static function getEnv(string $key, ?string $default = null): ?string { return $_SERVER[$key] ?? $default; } diff --git a/src/Request.php b/src/Request.php index b877920a..2cd008e0 100755 --- a/src/Request.php +++ b/src/Request.php @@ -136,7 +136,7 @@ public function getRawPayload(): string * @param string|null $default * @return string|null */ - public function getServer(string $key, string $default = null): ?string + public function getServer(string $key, ?string $default = null): ?string { return $_SERVER[$key] ?? $default; } diff --git a/src/Response.php b/src/Response.php index 470097e2..0bc4d08d 100755 --- a/src/Response.php +++ b/src/Response.php @@ -533,7 +533,7 @@ public function getHeaders(): array * @param bool $httponly * @param string $sameSite */ - public function addCookie(string $name, string $value = null, int $expire = null, string $path = null, string $domain = null, bool $secure = null, bool $httponly = null, string $sameSite = null): static + public function addCookie(string $name, ?string $value = null, ?int $expire = null, ?string $path = null, ?string $domain = null, ?bool $secure = null, ?bool $httponly = null, ?string $sameSite = null): static { $name = strtolower($name); @@ -665,7 +665,7 @@ protected function write(string $content): void * @param string $content * @return void */ - protected function end(string $content = null): void + protected function end(?string $content = null): void { if (!is_null($content)) { echo $content; diff --git a/tests/AppTest.php b/tests/AppTest.php index 371f4a8e..7a3c4f2d 100755 --- a/tests/AppTest.php +++ b/tests/AppTest.php @@ -467,7 +467,7 @@ public function providerRouteMatching(): array /** * @dataProvider providerRouteMatching */ - public function testCanMatchRoute(string $method, string $path, string $url = null): void + public function testCanMatchRoute(string $method, string $path, ?string $url = null): void { $url ??= $path; $expected = null; From 2cad921b3f9256169a4ded84c9c9fa04b9c593d2 Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Mon, 30 Jun 2025 21:36:53 +0100 Subject: [PATCH 066/103] fix: prevent callable strings --- composer.json | 5 ++ phpunit.xml | 23 ++++---- src/Http/Http.php | 2 +- tests/HttpTest.php | 59 ++++++++++++++++++- ...questTest.php => UtopiaFPMRequestTest.php} | 0 5 files changed, 76 insertions(+), 13 deletions(-) rename tests/{UtopiaRequestTest.php => UtopiaFPMRequestTest.php} (100%) diff --git a/composer.json b/composer.json index b5c168bc..06ff7c94 100644 --- a/composer.json +++ b/composer.json @@ -16,6 +16,11 @@ "Tests\\E2E\\": "tests/e2e" } }, + "autoload-dev": { + "psr-4": { + "Utopia\\Http\\Tests\\": "tests/" + } + }, "scripts": { "lint": "vendor/bin/pint --test", "format": "vendor/bin/pint", diff --git a/phpunit.xml b/phpunit.xml index de6deb0b..e33b301e 100755 --- a/phpunit.xml +++ b/phpunit.xml @@ -1,17 +1,18 @@ - + ./tests/e2e/Client.php ./tests/ - \ No newline at end of file + diff --git a/src/Http/Http.php b/src/Http/Http.php index 37638edb..f0ab42d8 100755 --- a/src/Http/Http.php +++ b/src/Http/Http.php @@ -740,7 +740,7 @@ protected function getArguments(Hook $hook, string $context, array $values, arra $paramExists = $existsInRequest || $existsInValues; $arg = $existsInRequest ? $requestParams[$key] : $param['default']; - if (\is_callable($arg)) { + if (\is_callable($arg) && !\is_string($arg)) { $arg = \call_user_func_array($arg, $this->getResources($param['injections'])); } $value = $existsInValues ? $values[$key] : $arg; diff --git a/tests/HttpTest.php b/tests/HttpTest.php index 0d314cd3..ae6c5dd6 100755 --- a/tests/HttpTest.php +++ b/tests/HttpTest.php @@ -454,7 +454,7 @@ public function providerRouteMatching(): array /** * @dataProvider providerRouteMatching */ - public function testCanMatchRoute(string $method, string $path, string $url = null): void + public function testCanMatchRoute(string $method, string $path, ?string $url = null): void { $url ??= $path; $expected = null; @@ -616,4 +616,61 @@ public function testWildcardRoute(): void $_SERVER['REQUEST_METHOD'] = $method; $_SERVER['REQUEST_URI'] = $uri; } + + public function testCallableStringParametersNotExecuted(): void + { + // Test that callable strings (like function names) are not executed + $route = new Route('GET', '/test-callable-string'); + + $route + ->param('callback', 'phpinfo', new Text(200), 'callback param', true) + ->action(function ($callback) { + // If the string 'phpinfo' was executed as a function, + // it would output PHP info. Instead, it should just be the string. + echo 'callback-value: ' . $callback; + }); + + \ob_start(); + $this->http->execute($route, new Request(), '1'); + $result = \ob_get_contents(); + \ob_end_clean(); + + $this->assertEquals('callback-value: phpinfo', $result); + + // Test with request parameter that is a callable string + $route2 = new Route('GET', '/test-callable-string-param'); + + $route2 + ->param('func', 'default', new Text(200), 'func param', false) + ->action(function ($func) { + echo 'func-value: ' . $func; + }); + + \ob_start(); + $request = new UtopiaFPMRequestTest(); + $request::_setParams(['func' => 'system']); + $this->http->execute($route2, $request, '1'); + $result = \ob_get_contents(); + \ob_end_clean(); + + $this->assertEquals('func-value: system', $result); + + // Test callable closure still works + $route3 = new Route('GET', '/test-callable-closure'); + + $route3 + ->param('generated', function () { + return 'generated-value'; + }, new Text(200), 'generated param', true) + ->action(function ($generated) { + echo 'generated: ' . $generated; + }); + + \ob_start(); + $this->http->execute($route3, new Request(), '1'); + $result = \ob_get_contents(); + \ob_end_clean(); + + $this->assertEquals('generated: generated-value', $result); + } } diff --git a/tests/UtopiaRequestTest.php b/tests/UtopiaFPMRequestTest.php similarity index 100% rename from tests/UtopiaRequestTest.php rename to tests/UtopiaFPMRequestTest.php From 08186e660f67f0767be58846a2b9bba3f9ac4e31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Tue, 19 Aug 2025 12:44:56 +0200 Subject: [PATCH 067/103] Improve domain validator; add double slash tests --- src/Validator/Domain.php | 6 +++++- tests/Validator/DomainTest.php | 15 +++++++++++++-- tests/e2e/ResponseTest.php | 23 +++++++++++++++++++++++ 3 files changed, 41 insertions(+), 3 deletions(-) diff --git a/src/Validator/Domain.php b/src/Validator/Domain.php index aea0a21c..db385713 100644 --- a/src/Validator/Domain.php +++ b/src/Validator/Domain.php @@ -45,7 +45,11 @@ public function isValid($value): bool return false; } - if (\filter_var($value, FILTER_VALIDATE_DOMAIN) === false) { + if (\filter_var($value, FILTER_VALIDATE_DOMAIN, FILTER_FLAG_HOSTNAME) === false) { + return false; + } + + if (\str_ends_with($value, '.') || \str_ends_with($value, '-')) { return false; } diff --git a/tests/Validator/DomainTest.php b/tests/Validator/DomainTest.php index 8707bf71..fc8484a1 100644 --- a/tests/Validator/DomainTest.php +++ b/tests/Validator/DomainTest.php @@ -31,14 +31,25 @@ public function testIsValid() $this->assertEquals(true, $this->domain->isValid('example.com')); $this->assertEquals(true, $this->domain->isValid('subdomain.example.com')); $this->assertEquals(true, $this->domain->isValid('subdomain.example-app.com')); - $this->assertEquals(true, $this->domain->isValid('subdomain.example_app.com')); + $this->assertEquals(false, $this->domain->isValid('subdomain.example_app.com')); $this->assertEquals(true, $this->domain->isValid('subdomain-new.example.com')); - $this->assertEquals(true, $this->domain->isValid('subdomain_new.example.com')); + $this->assertEquals(false, $this->domain->isValid('subdomain_new.example.com')); $this->assertEquals(true, $this->domain->isValid('localhost')); $this->assertEquals(true, $this->domain->isValid('example.io')); $this->assertEquals(true, $this->domain->isValid('example.org')); $this->assertEquals(true, $this->domain->isValid('example.org')); $this->assertEquals(false, $this->domain->isValid(false)); + $this->assertEquals(false, $this->domain->isValid('api.appwrite.io.')); + $this->assertEquals(false, $this->domain->isValid('.api.appwrite.io')); + $this->assertEquals(false, $this->domain->isValid('.api.appwrite.io')); + $this->assertEquals(false, $this->domain->isValid('api..appwrite.io')); + $this->assertEquals(false, $this->domain->isValid('api-.appwrite.io')); + $this->assertEquals(false, $this->domain->isValid('api.-appwrite.io')); + $this->assertEquals(false, $this->domain->isValid('app write.io')); + $this->assertEquals(false, $this->domain->isValid(' appwrite.io')); + $this->assertEquals(false, $this->domain->isValid('appwrite.io ')); + $this->assertEquals(false, $this->domain->isValid('-appwrite.io')); + $this->assertEquals(false, $this->domain->isValid('appwrite.io-')); $this->assertEquals(false, $this->domain->isValid('.')); $this->assertEquals(false, $this->domain->isValid('..')); $this->assertEquals(false, $this->domain->isValid('')); diff --git a/tests/e2e/ResponseTest.php b/tests/e2e/ResponseTest.php index 64ebfef4..0a8d70ac 100644 --- a/tests/e2e/ResponseTest.php +++ b/tests/e2e/ResponseTest.php @@ -99,4 +99,27 @@ public function testAliasWithParameter(): void $this->assertEquals(200, $response['headers']['status-code']); $this->assertEquals('db2;col2', $response['body']); } + + public function testDoubleSlash() + { + $response = $this->client->call(Client::METHOD_GET, '//'); + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals('Hello World!', $response['body']); + + $response = $this->client->call(Client::METHOD_GET, '//path-404'); + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals('Hello World!', $response['body']); + + $response = $this->client->call(Client::METHOD_GET, '//value/123'); + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEmpty($response['body']); + + $response = $this->client->call(Client::METHOD_GET, '/value//123'); + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEmpty($response['body']); + + $response = $this->client->call(Client::METHOD_GET, '//value//123'); + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEmpty($response['body']); + } } From 569d423048e323bc7d2e211e6f4dd1fd89a218f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Thu, 21 Aug 2025 14:46:06 +0200 Subject: [PATCH 068/103] trigger ci/cd checks --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 137e384c..5eb2641d 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +. +

Logo

From f23a6e7247208b0f0dfc66b78d3a7ce5cecb5a88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Thu, 21 Aug 2025 16:17:55 +0200 Subject: [PATCH 069/103] Make CI/CD checks pass --- README.md | 2 - composer.json | 4 +- composer.lock | 569 ++++++++++++++------------- src/Http/Adapter/FPM/Request.php | 2 +- src/Http/Adapter/FPM/Response.php | 4 +- src/Http/Adapter/Swoole/Request.php | 2 +- src/Http/Adapter/Swoole/Response.php | 2 +- src/Http/Adapter/Swoole/Server.php | 4 +- src/Http/Files.php | 2 +- src/Http/Http.php | 12 +- src/Http/Request.php | 2 +- src/Http/Response.php | 22 +- src/Http/Validator/AllOf.php | 4 +- src/Http/Validator/AnyOf.php | 4 +- src/Http/Validator/ArrayList.php | 2 +- src/Http/Validator/NoneOf.php | 4 +- tests/ResponseTest.php | 2 +- 17 files changed, 337 insertions(+), 306 deletions(-) diff --git a/README.md b/README.md index 5eb2641d..137e384c 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,3 @@ -. -

Logo

diff --git a/composer.json b/composer.json index 06ff7c94..3b0d7f66 100644 --- a/composer.json +++ b/composer.json @@ -34,9 +34,9 @@ }, "require-dev": { "phpunit/phpunit": "^9.5.25", - "laravel/pint": "^1.2", + "laravel/pint": "1.*", "swoole/ide-helper": "4.8.3", - "phpstan/phpstan": "^1.10", + "phpstan/phpstan": "1.*", "phpbench/phpbench": "^1.2" } } diff --git a/composer.lock b/composer.lock index 67cde3f0..abee3f7b 100644 --- a/composer.lock +++ b/composer.lock @@ -4,21 +4,21 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "33b8cf270cfbd8f86cbd1338d81f5140", + "content-hash": "47587a7a55803f5b0fdf225267c03f03", "packages": [], "packages-dev": [ { "name": "doctrine/annotations", - "version": "2.0.1", + "version": "2.0.2", "source": { "type": "git", "url": "https://github.com/doctrine/annotations.git", - "reference": "e157ef3f3124bbf6fe7ce0ffd109e8a8ef284e7f" + "reference": "901c2ee5d26eb64ff43c47976e114bf00843acf7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/annotations/zipball/e157ef3f3124bbf6fe7ce0ffd109e8a8ef284e7f", - "reference": "e157ef3f3124bbf6fe7ce0ffd109e8a8ef284e7f", + "url": "https://api.github.com/repos/doctrine/annotations/zipball/901c2ee5d26eb64ff43c47976e114bf00843acf7", + "reference": "901c2ee5d26eb64ff43c47976e114bf00843acf7", "shasum": "" }, "require": { @@ -30,10 +30,10 @@ "require-dev": { "doctrine/cache": "^2.0", "doctrine/coding-standard": "^10", - "phpstan/phpstan": "^1.8.0", + "phpstan/phpstan": "^1.10.28", "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", - "symfony/cache": "^5.4 || ^6", - "vimeo/psalm": "^4.10" + "symfony/cache": "^5.4 || ^6.4 || ^7", + "vimeo/psalm": "^4.30 || ^5.14" }, "suggest": { "php": "PHP 8.0 or higher comes with attributes, a native replacement for annotations" @@ -79,9 +79,9 @@ ], "support": { "issues": "https://github.com/doctrine/annotations/issues", - "source": "https://github.com/doctrine/annotations/tree/2.0.1" + "source": "https://github.com/doctrine/annotations/tree/2.0.2" }, - "time": "2023-02-02T22:02:53+00:00" + "time": "2024-09-05T10:17:24+00:00" }, { "name": "doctrine/instantiator", @@ -232,16 +232,16 @@ }, { "name": "laravel/pint", - "version": "v1.16.1", + "version": "v1.24.0", "source": { "type": "git", "url": "https://github.com/laravel/pint.git", - "reference": "9266a47f1b9231b83e0cfd849009547329d871b1" + "reference": "0345f3b05f136801af8c339f9d16ef29e6b4df8a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/pint/zipball/9266a47f1b9231b83e0cfd849009547329d871b1", - "reference": "9266a47f1b9231b83e0cfd849009547329d871b1", + "url": "https://api.github.com/repos/laravel/pint/zipball/0345f3b05f136801af8c339f9d16ef29e6b4df8a", + "reference": "0345f3b05f136801af8c339f9d16ef29e6b4df8a", "shasum": "" }, "require": { @@ -249,22 +249,25 @@ "ext-mbstring": "*", "ext-tokenizer": "*", "ext-xml": "*", - "php": "^8.1.0" + "php": "^8.2.0" }, "require-dev": { - "friendsofphp/php-cs-fixer": "^3.59.3", - "illuminate/view": "^10.48.12", - "larastan/larastan": "^2.9.7", - "laravel-zero/framework": "^10.4.0", + "friendsofphp/php-cs-fixer": "^3.82.2", + "illuminate/view": "^11.45.1", + "larastan/larastan": "^3.5.0", + "laravel-zero/framework": "^11.45.0", "mockery/mockery": "^1.6.12", - "nunomaduro/termwind": "^1.15.1", - "pestphp/pest": "^2.34.8" + "nunomaduro/termwind": "^2.3.1", + "pestphp/pest": "^2.36.0" }, "bin": [ "builds/pint" ], "type": "project", "autoload": { + "files": [ + "overrides/Runner/Parallel/ProcessFactory.php" + ], "psr-4": { "App\\": "app/", "Database\\Seeders\\": "database/seeders/", @@ -294,20 +297,20 @@ "issues": "https://github.com/laravel/pint/issues", "source": "https://github.com/laravel/pint" }, - "time": "2024-06-18T16:50:05+00:00" + "time": "2025-07-10T18:09:32+00:00" }, { "name": "myclabs/deep-copy", - "version": "1.12.0", + "version": "1.13.4", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c" + "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c", - "reference": "3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/07d290f0c47959fd5eed98c95ee5602db07e0b6a", + "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a", "shasum": "" }, "require": { @@ -346,7 +349,7 @@ ], "support": { "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.12.0" + "source": "https://github.com/myclabs/DeepCopy/tree/1.13.4" }, "funding": [ { @@ -354,20 +357,20 @@ "type": "tidelift" } ], - "time": "2024-06-12T14:39:25+00:00" + "time": "2025-08-01T08:46:24+00:00" }, { "name": "nikic/php-parser", - "version": "v5.1.0", + "version": "v5.6.1", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "683130c2ff8c2739f4822ff7ac5c873ec529abd1" + "reference": "f103601b29efebd7ff4a1ca7b3eeea9e3336a2a2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/683130c2ff8c2739f4822ff7ac5c873ec529abd1", - "reference": "683130c2ff8c2739f4822ff7ac5c873ec529abd1", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/f103601b29efebd7ff4a1ca7b3eeea9e3336a2a2", + "reference": "f103601b29efebd7ff4a1ca7b3eeea9e3336a2a2", "shasum": "" }, "require": { @@ -386,7 +389,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "5.0-dev" + "dev-master": "5.x-dev" } }, "autoload": { @@ -410,9 +413,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v5.1.0" + "source": "https://github.com/nikic/PHP-Parser/tree/v5.6.1" }, - "time": "2024-07-01T20:03:41+00:00" + "time": "2025-08-13T20:13:15+00:00" }, { "name": "phar-io/manifest", @@ -583,69 +586,18 @@ }, "time": "2023-10-30T13:38:26+00:00" }, - { - "name": "phpbench/dom", - "version": "0.3.3", - "source": { - "type": "git", - "url": "https://github.com/phpbench/dom.git", - "reference": "786a96db538d0def931f5b19225233ec42ec7a72" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpbench/dom/zipball/786a96db538d0def931f5b19225233ec42ec7a72", - "reference": "786a96db538d0def931f5b19225233ec42ec7a72", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "php": "^7.3||^8.0" - }, - "require-dev": { - "friendsofphp/php-cs-fixer": "^3.14", - "phpstan/phpstan": "^1.10", - "phpunit/phpunit": "^8.0||^9.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, - "autoload": { - "psr-4": { - "PhpBench\\Dom\\": "lib/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Daniel Leech", - "email": "daniel@dantleech.com" - } - ], - "description": "DOM wrapper to simplify working with the PHP DOM implementation", - "support": { - "issues": "https://github.com/phpbench/dom/issues", - "source": "https://github.com/phpbench/dom/tree/0.3.3" - }, - "time": "2023-03-06T23:46:57+00:00" - }, { "name": "phpbench/phpbench", - "version": "1.3.1", + "version": "1.4.1", "source": { "type": "git", "url": "https://github.com/phpbench/phpbench.git", - "reference": "a3e1ef08d9d7736d43a7fbd444893d6a073c0ca0" + "reference": "78cd98a9aa34e0f8f80ca01972a8b88d2c30194b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpbench/phpbench/zipball/a3e1ef08d9d7736d43a7fbd444893d6a073c0ca0", - "reference": "a3e1ef08d9d7736d43a7fbd444893d6a073c0ca0", + "url": "https://api.github.com/repos/phpbench/phpbench/zipball/78cd98a9aa34e0f8f80ca01972a8b88d2c30194b", + "reference": "78cd98a9aa34e0f8f80ca01972a8b88d2c30194b", "shasum": "" }, "require": { @@ -658,7 +610,6 @@ "ext-tokenizer": "*", "php": "^8.1", "phpbench/container": "^2.2", - "phpbench/dom": "~0.3.3", "psr/log": "^1.1 || ^2.0 || ^3.0", "seld/jsonlint": "^1.1", "symfony/console": "^6.1 || ^7.0", @@ -677,8 +628,8 @@ "phpstan/extension-installer": "^1.1", "phpstan/phpstan": "^1.0", "phpstan/phpstan-phpunit": "^1.0", - "phpunit/phpunit": "^10.4", - "rector/rector": "^0.18.11 || ^1.0.0", + "phpunit/phpunit": "^10.4 || ^11.0", + "rector/rector": "^1.2", "symfony/error-handler": "^6.1 || ^7.0", "symfony/var-dumper": "^6.1 || ^7.0" }, @@ -723,7 +674,7 @@ ], "support": { "issues": "https://github.com/phpbench/phpbench/issues", - "source": "https://github.com/phpbench/phpbench/tree/1.3.1" + "source": "https://github.com/phpbench/phpbench/tree/1.4.1" }, "funding": [ { @@ -731,20 +682,20 @@ "type": "github" } ], - "time": "2024-06-30T11:04:37+00:00" + "time": "2025-03-12T08:01:40+00:00" }, { "name": "phpstan/phpstan", - "version": "1.11.6", + "version": "1.12.28", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "6ac78f1165346c83b4a753f7e4186d969c6ad0ee" + "reference": "fcf8b71aeab4e1a1131d1783cef97b23a51b87a9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/6ac78f1165346c83b4a753f7e4186d969c6ad0ee", - "reference": "6ac78f1165346c83b4a753f7e4186d969c6ad0ee", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/fcf8b71aeab4e1a1131d1783cef97b23a51b87a9", + "reference": "fcf8b71aeab4e1a1131d1783cef97b23a51b87a9", "shasum": "" }, "require": { @@ -789,39 +740,39 @@ "type": "github" } ], - "time": "2024-07-01T15:33:06+00:00" + "time": "2025-07-17T17:15:39+00:00" }, { "name": "phpunit/php-code-coverage", - "version": "9.2.31", + "version": "9.2.32", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "48c34b5d8d983006bd2adc2d0de92963b9155965" + "reference": "85402a822d1ecf1db1096959413d35e1c37cf1a5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/48c34b5d8d983006bd2adc2d0de92963b9155965", - "reference": "48c34b5d8d983006bd2adc2d0de92963b9155965", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/85402a822d1ecf1db1096959413d35e1c37cf1a5", + "reference": "85402a822d1ecf1db1096959413d35e1c37cf1a5", "shasum": "" }, "require": { "ext-dom": "*", "ext-libxml": "*", "ext-xmlwriter": "*", - "nikic/php-parser": "^4.18 || ^5.0", + "nikic/php-parser": "^4.19.1 || ^5.1.0", "php": ">=7.3", - "phpunit/php-file-iterator": "^3.0.3", - "phpunit/php-text-template": "^2.0.2", - "sebastian/code-unit-reverse-lookup": "^2.0.2", - "sebastian/complexity": "^2.0", - "sebastian/environment": "^5.1.2", - "sebastian/lines-of-code": "^1.0.3", - "sebastian/version": "^3.0.1", - "theseer/tokenizer": "^1.2.0" + "phpunit/php-file-iterator": "^3.0.6", + "phpunit/php-text-template": "^2.0.4", + "sebastian/code-unit-reverse-lookup": "^2.0.3", + "sebastian/complexity": "^2.0.3", + "sebastian/environment": "^5.1.5", + "sebastian/lines-of-code": "^1.0.4", + "sebastian/version": "^3.0.2", + "theseer/tokenizer": "^1.2.3" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^9.6" }, "suggest": { "ext-pcov": "PHP extension that provides line coverage", @@ -830,7 +781,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "9.2-dev" + "dev-main": "9.2.x-dev" } }, "autoload": { @@ -859,7 +810,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.31" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.32" }, "funding": [ { @@ -867,7 +818,7 @@ "type": "github" } ], - "time": "2024-03-02T06:37:42+00:00" + "time": "2024-08-22T04:23:01+00:00" }, { "name": "phpunit/php-file-iterator", @@ -1112,45 +1063,45 @@ }, { "name": "phpunit/phpunit", - "version": "9.6.19", + "version": "9.6.25", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "a1a54a473501ef4cdeaae4e06891674114d79db8" + "reference": "049c011e01be805202d8eebedef49f769a8ec7b7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/a1a54a473501ef4cdeaae4e06891674114d79db8", - "reference": "a1a54a473501ef4cdeaae4e06891674114d79db8", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/049c011e01be805202d8eebedef49f769a8ec7b7", + "reference": "049c011e01be805202d8eebedef49f769a8ec7b7", "shasum": "" }, "require": { - "doctrine/instantiator": "^1.3.1 || ^2", + "doctrine/instantiator": "^1.5.0 || ^2", "ext-dom": "*", "ext-json": "*", "ext-libxml": "*", "ext-mbstring": "*", "ext-xml": "*", "ext-xmlwriter": "*", - "myclabs/deep-copy": "^1.10.1", - "phar-io/manifest": "^2.0.3", - "phar-io/version": "^3.0.2", + "myclabs/deep-copy": "^1.13.4", + "phar-io/manifest": "^2.0.4", + "phar-io/version": "^3.2.1", "php": ">=7.3", - "phpunit/php-code-coverage": "^9.2.28", - "phpunit/php-file-iterator": "^3.0.5", + "phpunit/php-code-coverage": "^9.2.32", + "phpunit/php-file-iterator": "^3.0.6", "phpunit/php-invoker": "^3.1.1", - "phpunit/php-text-template": "^2.0.3", - "phpunit/php-timer": "^5.0.2", - "sebastian/cli-parser": "^1.0.1", - "sebastian/code-unit": "^1.0.6", - "sebastian/comparator": "^4.0.8", - "sebastian/diff": "^4.0.3", - "sebastian/environment": "^5.1.3", - "sebastian/exporter": "^4.0.5", - "sebastian/global-state": "^5.0.1", - "sebastian/object-enumerator": "^4.0.3", - "sebastian/resource-operations": "^3.0.3", - "sebastian/type": "^3.2", + "phpunit/php-text-template": "^2.0.4", + "phpunit/php-timer": "^5.0.3", + "sebastian/cli-parser": "^1.0.2", + "sebastian/code-unit": "^1.0.8", + "sebastian/comparator": "^4.0.9", + "sebastian/diff": "^4.0.6", + "sebastian/environment": "^5.1.5", + "sebastian/exporter": "^4.0.6", + "sebastian/global-state": "^5.0.8", + "sebastian/object-enumerator": "^4.0.4", + "sebastian/resource-operations": "^3.0.4", + "sebastian/type": "^3.2.1", "sebastian/version": "^3.0.2" }, "suggest": { @@ -1195,7 +1146,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.19" + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.25" }, "funding": [ { @@ -1206,12 +1157,20 @@ "url": "https://github.com/sebastianbergmann", "type": "github" }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, { "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", "type": "tidelift" } ], - "time": "2024-04-05T04:35:58+00:00" + "time": "2025-08-20T14:38:31+00:00" }, { "name": "psr/cache", @@ -1317,16 +1276,16 @@ }, { "name": "psr/log", - "version": "3.0.0", + "version": "3.0.2", "source": { "type": "git", "url": "https://github.com/php-fig/log.git", - "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001" + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/fe5ea303b0887d5caefd3d431c3e61ad47037001", - "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001", + "url": "https://api.github.com/repos/php-fig/log/zipball/f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", "shasum": "" }, "require": { @@ -1361,9 +1320,9 @@ "psr-3" ], "support": { - "source": "https://github.com/php-fig/log/tree/3.0.0" + "source": "https://github.com/php-fig/log/tree/3.0.2" }, - "time": "2021-07-14T16:46:02+00:00" + "time": "2024-09-11T13:17:53+00:00" }, { "name": "sebastian/cli-parser", @@ -1534,16 +1493,16 @@ }, { "name": "sebastian/comparator", - "version": "4.0.8", + "version": "4.0.9", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "fa0f136dd2334583309d32b62544682ee972b51a" + "reference": "67a2df3a62639eab2cc5906065e9805d4fd5dfc5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/fa0f136dd2334583309d32b62544682ee972b51a", - "reference": "fa0f136dd2334583309d32b62544682ee972b51a", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/67a2df3a62639eab2cc5906065e9805d4fd5dfc5", + "reference": "67a2df3a62639eab2cc5906065e9805d4fd5dfc5", "shasum": "" }, "require": { @@ -1596,15 +1555,27 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/comparator/issues", - "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.8" + "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.9" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/comparator", + "type": "tidelift" } ], - "time": "2022-09-14T12:41:17+00:00" + "time": "2025-08-10T06:51:50+00:00" }, { "name": "sebastian/complexity", @@ -1871,16 +1842,16 @@ }, { "name": "sebastian/global-state", - "version": "5.0.7", + "version": "5.0.8", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "bca7df1f32ee6fe93b4d4a9abbf69e13a4ada2c9" + "reference": "b6781316bdcd28260904e7cc18ec983d0d2ef4f6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bca7df1f32ee6fe93b4d4a9abbf69e13a4ada2c9", - "reference": "bca7df1f32ee6fe93b4d4a9abbf69e13a4ada2c9", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/b6781316bdcd28260904e7cc18ec983d0d2ef4f6", + "reference": "b6781316bdcd28260904e7cc18ec983d0d2ef4f6", "shasum": "" }, "require": { @@ -1923,15 +1894,27 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/global-state/issues", - "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.7" + "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.8" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/global-state", + "type": "tidelift" } ], - "time": "2024-03-02T06:35:11+00:00" + "time": "2025-08-10T07:10:35+00:00" }, { "name": "sebastian/lines-of-code", @@ -2104,16 +2087,16 @@ }, { "name": "sebastian/recursion-context", - "version": "4.0.5", + "version": "4.0.6", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1" + "reference": "539c6691e0623af6dc6f9c20384c120f963465a0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", - "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/539c6691e0623af6dc6f9c20384c120f963465a0", + "reference": "539c6691e0623af6dc6f9c20384c120f963465a0", "shasum": "" }, "require": { @@ -2155,15 +2138,27 @@ "homepage": "https://github.com/sebastianbergmann/recursion-context", "support": { "issues": "https://github.com/sebastianbergmann/recursion-context/issues", - "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.5" + "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.6" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/recursion-context", + "type": "tidelift" } ], - "time": "2023-02-03T06:07:39+00:00" + "time": "2025-08-10T06:57:39+00:00" }, { "name": "sebastian/resource-operations", @@ -2330,23 +2325,23 @@ }, { "name": "seld/jsonlint", - "version": "1.10.2", + "version": "1.11.0", "source": { "type": "git", "url": "https://github.com/Seldaek/jsonlint.git", - "reference": "9bb7db07b5d66d90f6ebf542f09fc67d800e5259" + "reference": "1748aaf847fc731cfad7725aec413ee46f0cc3a2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Seldaek/jsonlint/zipball/9bb7db07b5d66d90f6ebf542f09fc67d800e5259", - "reference": "9bb7db07b5d66d90f6ebf542f09fc67d800e5259", + "url": "https://api.github.com/repos/Seldaek/jsonlint/zipball/1748aaf847fc731cfad7725aec413ee46f0cc3a2", + "reference": "1748aaf847fc731cfad7725aec413ee46f0cc3a2", "shasum": "" }, "require": { "php": "^5.3 || ^7.0 || ^8.0" }, "require-dev": { - "phpstan/phpstan": "^1.5", + "phpstan/phpstan": "^1.11", "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0 || ^8.5.13" }, "bin": [ @@ -2378,7 +2373,7 @@ ], "support": { "issues": "https://github.com/Seldaek/jsonlint/issues", - "source": "https://github.com/Seldaek/jsonlint/tree/1.10.2" + "source": "https://github.com/Seldaek/jsonlint/tree/1.11.0" }, "funding": [ { @@ -2390,7 +2385,7 @@ "type": "tidelift" } ], - "time": "2024-02-07T12:57:50+00:00" + "time": "2024-07-11T14:55:45+00:00" }, { "name": "swoole/ide-helper", @@ -2436,23 +2431,24 @@ }, { "name": "symfony/console", - "version": "v7.1.2", + "version": "v7.3.2", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "0aa29ca177f432ab68533432db0de059f39c92ae" + "reference": "5f360ebc65c55265a74d23d7fe27f957870158a1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/0aa29ca177f432ab68533432db0de059f39c92ae", - "reference": "0aa29ca177f432ab68533432db0de059f39c92ae", + "url": "https://api.github.com/repos/symfony/console/zipball/5f360ebc65c55265a74d23d7fe27f957870158a1", + "reference": "5f360ebc65c55265a74d23d7fe27f957870158a1", "shasum": "" }, "require": { "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-mbstring": "~1.0", "symfony/service-contracts": "^2.5|^3", - "symfony/string": "^6.4|^7.0" + "symfony/string": "^7.2" }, "conflict": { "symfony/dependency-injection": "<6.4", @@ -2509,7 +2505,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v7.1.2" + "source": "https://github.com/symfony/console/tree/v7.3.2" }, "funding": [ { @@ -2520,25 +2516,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-06-28T10:03:55+00:00" + "time": "2025-07-30T17:13:41+00:00" }, { "name": "symfony/deprecation-contracts", - "version": "v3.5.0", + "version": "v3.6.0", "source": { "type": "git", "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1" + "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1", - "reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/63afe740e99a13ba87ec199bb07bbdee937a5b62", + "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62", "shasum": "" }, "require": { @@ -2546,12 +2546,12 @@ }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "3.5-dev" - }, "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.6-dev" } }, "autoload": { @@ -2576,7 +2576,7 @@ "description": "A generic function and convention to trigger deprecation notices", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v3.5.0" + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.6.0" }, "funding": [ { @@ -2592,20 +2592,20 @@ "type": "tidelift" } ], - "time": "2024-04-18T09:32:20+00:00" + "time": "2024-09-25T14:21:43+00:00" }, { "name": "symfony/filesystem", - "version": "v7.1.2", + "version": "v7.3.2", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "92a91985250c251de9b947a14bb2c9390b1a562c" + "reference": "edcbb768a186b5c3f25d0643159a787d3e63b7fd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/92a91985250c251de9b947a14bb2c9390b1a562c", - "reference": "92a91985250c251de9b947a14bb2c9390b1a562c", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/edcbb768a186b5c3f25d0643159a787d3e63b7fd", + "reference": "edcbb768a186b5c3f25d0643159a787d3e63b7fd", "shasum": "" }, "require": { @@ -2642,7 +2642,7 @@ "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/filesystem/tree/v7.1.2" + "source": "https://github.com/symfony/filesystem/tree/v7.3.2" }, "funding": [ { @@ -2653,25 +2653,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-06-28T10:03:55+00:00" + "time": "2025-07-07T08:17:47+00:00" }, { "name": "symfony/finder", - "version": "v7.1.1", + "version": "v7.3.2", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "fbb0ba67688b780efbc886c1a0a0948dcf7205d6" + "reference": "2a6614966ba1074fa93dae0bc804227422df4dfe" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/fbb0ba67688b780efbc886c1a0a0948dcf7205d6", - "reference": "fbb0ba67688b780efbc886c1a0a0948dcf7205d6", + "url": "https://api.github.com/repos/symfony/finder/zipball/2a6614966ba1074fa93dae0bc804227422df4dfe", + "reference": "2a6614966ba1074fa93dae0bc804227422df4dfe", "shasum": "" }, "require": { @@ -2706,7 +2710,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v7.1.1" + "source": "https://github.com/symfony/finder/tree/v7.3.2" }, "funding": [ { @@ -2717,25 +2721,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-05-31T14:57:53+00:00" + "time": "2025-07-15T13:41:35+00:00" }, { "name": "symfony/options-resolver", - "version": "v7.1.1", + "version": "v7.3.2", "source": { "type": "git", "url": "https://github.com/symfony/options-resolver.git", - "reference": "47aa818121ed3950acd2b58d1d37d08a94f9bf55" + "reference": "119bcf13e67dbd188e5dbc74228b1686f66acd37" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/options-resolver/zipball/47aa818121ed3950acd2b58d1d37d08a94f9bf55", - "reference": "47aa818121ed3950acd2b58d1d37d08a94f9bf55", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/119bcf13e67dbd188e5dbc74228b1686f66acd37", + "reference": "119bcf13e67dbd188e5dbc74228b1686f66acd37", "shasum": "" }, "require": { @@ -2773,7 +2781,7 @@ "options" ], "support": { - "source": "https://github.com/symfony/options-resolver/tree/v7.1.1" + "source": "https://github.com/symfony/options-resolver/tree/v7.3.2" }, "funding": [ { @@ -2784,29 +2792,33 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-05-31T14:57:53+00:00" + "time": "2025-07-15T11:36:08+00:00" }, { "name": "symfony/polyfill-ctype", - "version": "v1.30.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "0424dff1c58f028c451efff2045f5d92410bd540" + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/0424dff1c58f028c451efff2045f5d92410bd540", - "reference": "0424dff1c58f028c451efff2045f5d92410bd540", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638", + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "provide": { "ext-ctype": "*" @@ -2817,8 +2829,8 @@ "type": "library", "extra": { "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -2852,7 +2864,7 @@ "portable" ], "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.30.0" + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.33.0" }, "funding": [ { @@ -2863,29 +2875,33 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-05-31T15:07:36+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/polyfill-intl-grapheme", - "version": "v1.30.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "64647a7c30b2283f5d49b874d84a18fc22054b7a" + "reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/64647a7c30b2283f5d49b874d84a18fc22054b7a", - "reference": "64647a7c30b2283f5d49b874d84a18fc22054b7a", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/380872130d3a5dd3ace2f4010d95125fde5d5c70", + "reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "suggest": { "ext-intl": "For best performance" @@ -2893,8 +2909,8 @@ "type": "library", "extra": { "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -2930,7 +2946,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.30.0" + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.33.0" }, "funding": [ { @@ -2941,29 +2957,33 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-05-31T15:07:36+00:00" + "time": "2025-06-27T09:58:17+00:00" }, { "name": "symfony/polyfill-intl-normalizer", - "version": "v1.30.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-normalizer.git", - "reference": "a95281b0be0d9ab48050ebd988b967875cdb9fdb" + "reference": "3833d7255cc303546435cb650316bff708a1c75c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/a95281b0be0d9ab48050ebd988b967875cdb9fdb", - "reference": "a95281b0be0d9ab48050ebd988b967875cdb9fdb", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/3833d7255cc303546435cb650316bff708a1c75c", + "reference": "3833d7255cc303546435cb650316bff708a1c75c", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "suggest": { "ext-intl": "For best performance" @@ -2971,8 +2991,8 @@ "type": "library", "extra": { "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -3011,7 +3031,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.30.0" + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.33.0" }, "funding": [ { @@ -3022,29 +3042,34 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-05-31T15:07:36+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.30.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "fd22ab50000ef01661e2a31d850ebaa297f8e03c" + "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/fd22ab50000ef01661e2a31d850ebaa297f8e03c", - "reference": "fd22ab50000ef01661e2a31d850ebaa297f8e03c", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6d857f4d76bd4b343eac26d6b539585d2bc56493", + "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493", "shasum": "" }, "require": { - "php": ">=7.1" + "ext-iconv": "*", + "php": ">=7.2" }, "provide": { "ext-mbstring": "*" @@ -3055,8 +3080,8 @@ "type": "library", "extra": { "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -3091,7 +3116,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.30.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.33.0" }, "funding": [ { @@ -3102,25 +3127,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-06-19T12:30:46+00:00" + "time": "2024-12-23T08:48:59+00:00" }, { "name": "symfony/process", - "version": "v7.1.1", + "version": "v7.3.0", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "febf90124323a093c7ee06fdb30e765ca3c20028" + "reference": "40c295f2deb408d5e9d2d32b8ba1dd61e36f05af" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/febf90124323a093c7ee06fdb30e765ca3c20028", - "reference": "febf90124323a093c7ee06fdb30e765ca3c20028", + "url": "https://api.github.com/repos/symfony/process/zipball/40c295f2deb408d5e9d2d32b8ba1dd61e36f05af", + "reference": "40c295f2deb408d5e9d2d32b8ba1dd61e36f05af", "shasum": "" }, "require": { @@ -3152,7 +3181,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v7.1.1" + "source": "https://github.com/symfony/process/tree/v7.3.0" }, "funding": [ { @@ -3168,20 +3197,20 @@ "type": "tidelift" } ], - "time": "2024-05-31T14:57:53+00:00" + "time": "2025-04-17T09:11:12+00:00" }, { "name": "symfony/service-contracts", - "version": "v3.5.0", + "version": "v3.6.0", "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", - "reference": "bd1d9e59a81d8fa4acdcea3f617c581f7475a80f" + "reference": "f021b05a130d35510bd6b25fe9053c2a8a15d5d4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/bd1d9e59a81d8fa4acdcea3f617c581f7475a80f", - "reference": "bd1d9e59a81d8fa4acdcea3f617c581f7475a80f", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/f021b05a130d35510bd6b25fe9053c2a8a15d5d4", + "reference": "f021b05a130d35510bd6b25fe9053c2a8a15d5d4", "shasum": "" }, "require": { @@ -3194,12 +3223,12 @@ }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "3.5-dev" - }, "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.6-dev" } }, "autoload": { @@ -3235,7 +3264,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/service-contracts/tree/v3.5.0" + "source": "https://github.com/symfony/service-contracts/tree/v3.6.0" }, "funding": [ { @@ -3251,20 +3280,20 @@ "type": "tidelift" } ], - "time": "2024-04-18T09:32:20+00:00" + "time": "2025-04-25T09:37:31+00:00" }, { "name": "symfony/string", - "version": "v7.1.2", + "version": "v7.3.2", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "14221089ac66cf82e3cf3d1c1da65de305587ff8" + "reference": "42f505aff654e62ac7ac2ce21033818297ca89ca" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/14221089ac66cf82e3cf3d1c1da65de305587ff8", - "reference": "14221089ac66cf82e3cf3d1c1da65de305587ff8", + "url": "https://api.github.com/repos/symfony/string/zipball/42f505aff654e62ac7ac2ce21033818297ca89ca", + "reference": "42f505aff654e62ac7ac2ce21033818297ca89ca", "shasum": "" }, "require": { @@ -3322,7 +3351,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v7.1.2" + "source": "https://github.com/symfony/string/tree/v7.3.2" }, "funding": [ { @@ -3333,12 +3362,16 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-06-28T09:27:18+00:00" + "time": "2025-07-10T08:47:49+00:00" }, { "name": "theseer/tokenizer", @@ -3450,5 +3483,5 @@ "ext-swoole": "*" }, "platform-dev": [], - "plugin-api-version": "2.6.0" + "plugin-api-version": "2.3.0" } diff --git a/src/Http/Adapter/FPM/Request.php b/src/Http/Adapter/FPM/Request.php index ecabb457..41452440 100644 --- a/src/Http/Adapter/FPM/Request.php +++ b/src/Http/Adapter/FPM/Request.php @@ -36,7 +36,7 @@ public function getRawPayload(): string * @param string|null $default * @return string|null */ - public function getServer(string $key, string $default = null): ?string + public function getServer(string $key, ?string $default = null): ?string { return $_SERVER[$key] ?? $default; } diff --git a/src/Http/Adapter/FPM/Response.php b/src/Http/Adapter/FPM/Response.php index e58cc3d4..6c877918 100644 --- a/src/Http/Adapter/FPM/Response.php +++ b/src/Http/Adapter/FPM/Response.php @@ -25,10 +25,10 @@ public function write(string $content): bool * * Send optional content and end * - * @param string $content + * @param string|null $content * @return void */ - public function end(string $content = null): void + public function end(?string $content = null): void { if (!is_null($content)) { echo $content; diff --git a/src/Http/Adapter/Swoole/Request.php b/src/Http/Adapter/Swoole/Request.php index 8a717730..164cf81c 100644 --- a/src/Http/Adapter/Swoole/Request.php +++ b/src/Http/Adapter/Swoole/Request.php @@ -43,7 +43,7 @@ public function getRawPayload(): string * @param string|null $default * @return string|null */ - public function getServer(string $key, string $default = null): ?string + public function getServer(string $key, ?string $default = null): ?string { return $this->swoole->server[$key] ?? $default; } diff --git a/src/Http/Adapter/Swoole/Response.php b/src/Http/Adapter/Swoole/Response.php index 4bb03926..b0b99978 100644 --- a/src/Http/Adapter/Swoole/Response.php +++ b/src/Http/Adapter/Swoole/Response.php @@ -45,7 +45,7 @@ public function write(string $content): bool * @param string|null $content * @return void */ - public function end(string $content = null): void + public function end(?string $content = null): void { $this->swoole->end($content); } diff --git a/src/Http/Adapter/Swoole/Server.php b/src/Http/Adapter/Swoole/Server.php index 9fe73e98..fff99f1e 100755 --- a/src/Http/Adapter/Swoole/Server.php +++ b/src/Http/Adapter/Swoole/Server.php @@ -15,7 +15,7 @@ class Server extends Adapter { protected SwooleServer $server; - public function __construct(string $host, string $port = null, array $settings = []) + public function __construct(string $host, ?string $port = null, array $settings = []) { $this->server = new SwooleServer($host, $port); $this->server->set(\array_merge($settings, [ @@ -42,7 +42,7 @@ public function onStart(callable $callback) public function start() { - if(Coroutine::getCid() === -1) { + if (Coroutine::getCid() === -1) { run(fn () => $this->server->start()); } else { $this->server->start(); diff --git a/src/Http/Files.php b/src/Http/Files.php index 5ecd353f..b5fc5199 100644 --- a/src/Http/Files.php +++ b/src/Http/Files.php @@ -83,7 +83,7 @@ public function getCount(): int * * @throws \Exception */ - public function load(string $directory, string $root = null): void + public function load(string $directory, ?string $root = null): void { if (!is_readable($directory)) { throw new Exception("Failed to load directory: {$directory}"); diff --git a/src/Http/Http.php b/src/Http/Http.php index f0ab42d8..8fe02785 100755 --- a/src/Http/Http.php +++ b/src/Http/Http.php @@ -295,7 +295,7 @@ public static function error(): Hook * @param string|null $default * @return string|null */ - public static function getEnv(string $key, string $default = null): ?string + public static function getEnv(string $key, ?string $default = null): ?string { return $_SERVER[$key] ?? $default; } @@ -366,7 +366,7 @@ public function getResource(string $name, string $context = 'utopia', bool $fres $this->resources[$context] ??= []; $resourcesCallback = &self::$resourcesCallbacks[$context] ?? []; - if(empty($resourcesCallback) || !\array_key_exists($name, $resourcesCallback)) { + if (empty($resourcesCallback) || !\array_key_exists($name, $resourcesCallback)) { $resourcesCallback = &self::$resourcesCallbacks['utopia']; } @@ -514,7 +514,7 @@ public static function addRoute(string $method, string $url): Route * * @throws \Exception */ - public function loadFiles(string $directory, string $root = null): void + public function loadFiles(string $directory, ?string $root = null): void { $this->files->load($directory, $root); } @@ -576,7 +576,7 @@ public function start() try { $this->run($request, $response, $context); } finally { - if(isset(self::$resourcesCallbacks[$context])) { + if (isset(self::$resourcesCallbacks[$context])) { unset(self::$resourcesCallbacks[$context]); } } @@ -593,7 +593,7 @@ public function start() $arguments = $this->getArguments($hook, 'utopia', [], []); \call_user_func_array($hook->getAction(), $arguments); } - } catch(\Exception $e) { + } catch (\Exception $e) { self::setResource('error', fn () => $e); foreach (self::$errors as $error) { // Global error hooks @@ -793,7 +793,7 @@ public function run(Request $request, Response $response, string $context): stat $arguments = $this->getArguments($hook, $context, [], []); \call_user_func_array($hook->getAction(), $arguments); } - } catch(\Exception $e) { + } catch (\Exception $e) { self::setResource('error', fn () => $e, [], $context); foreach (self::$errors as $error) { // Global error hooks diff --git a/src/Http/Request.php b/src/Http/Request.php index 65f67b28..8332d99d 100755 --- a/src/Http/Request.php +++ b/src/Http/Request.php @@ -124,7 +124,7 @@ abstract public function getRawPayload(): string; * @param string|null $default * @return string|null */ - abstract public function getServer(string $key, string $default = null): ?string; + abstract public function getServer(string $key, ?string $default = null): ?string; /** * Set server diff --git a/src/Http/Response.php b/src/Http/Response.php index e4545325..d0e6e793 100755 --- a/src/Http/Response.php +++ b/src/Http/Response.php @@ -404,15 +404,15 @@ public function getHeaders(): array * Add an HTTP cookie to response header * * @param string $name - * @param string $value - * @param int $expire - * @param string $path - * @param string $domain - * @param bool $secure - * @param bool $httponly - * @param string $sameSite - */ - public function addCookie(string $name, string $value = null, int $expire = null, string $path = null, string $domain = null, bool $secure = null, bool $httponly = null, string $sameSite = null): static + * @param string|null $value + * @param int|null $expire + * @param string|null $path + * @param string|null $domain + * @param bool|null $secure + * @param bool|null $httponly + * @param string|null $sameSite + */ + public function addCookie(string $name, ?string $value = null, ?int $expire = null, ?string $path = null, ?string $domain = null, ?bool $secure = null, ?bool $httponly = null, ?string $sameSite = null): static { $name = strtolower($name); @@ -519,10 +519,10 @@ abstract public function write(string $content): bool; * * Send optional content and end * - * @param string $content + * @param string|null $content * @return void */ - abstract public function end(string $content = null): void; + abstract public function end(?string $content = null): void; /** * Output response diff --git a/src/Http/Validator/AllOf.php b/src/Http/Validator/AllOf.php index 70b40751..5e52f5ed 100644 --- a/src/Http/Validator/AllOf.php +++ b/src/Http/Validator/AllOf.php @@ -29,7 +29,7 @@ public function __construct(protected array $validators, protected string $type */ public function getDescription(): string { - if(!(\is_null($this->failedRule))) { + if (!(\is_null($this->failedRule))) { $description = $this->failedRule->getDescription(); } else { $description = $this->validators[0]->getDescription(); @@ -51,7 +51,7 @@ public function isValid(mixed $value): bool foreach ($this->validators as $rule) { $valid = $rule->isValid($value); - if(!$valid) { + if (!$valid) { $this->failedRule = $rule; return false; } diff --git a/src/Http/Validator/AnyOf.php b/src/Http/Validator/AnyOf.php index ad2bd921..ff96a54c 100644 --- a/src/Http/Validator/AnyOf.php +++ b/src/Http/Validator/AnyOf.php @@ -29,7 +29,7 @@ public function __construct(protected array $validators, protected string $type */ public function getDescription(): string { - if(!(\is_null($this->failedRule))) { + if (!(\is_null($this->failedRule))) { $description = $this->failedRule->getDescription(); } else { $description = $this->validators[0]->getDescription(); @@ -53,7 +53,7 @@ public function isValid(mixed $value): bool $this->failedRule = $rule; - if($valid) { + if ($valid) { return true; } } diff --git a/src/Http/Validator/ArrayList.php b/src/Http/Validator/ArrayList.php index 79dc4696..c2c7e5ab 100644 --- a/src/Http/Validator/ArrayList.php +++ b/src/Http/Validator/ArrayList.php @@ -46,7 +46,7 @@ public function getDescription(): string { $msg = 'Value must a valid array'; - if($this->length > 0) { + if ($this->length > 0) { $msg .= ' no longer than ' . $this->length . ' items'; } diff --git a/src/Http/Validator/NoneOf.php b/src/Http/Validator/NoneOf.php index 20af51b7..f74762fc 100644 --- a/src/Http/Validator/NoneOf.php +++ b/src/Http/Validator/NoneOf.php @@ -31,7 +31,7 @@ public function getDescription(): string { $description = ''; - if(!(\is_null($this->failedRule))) { + if (!(\is_null($this->failedRule))) { $description = $this->failedRule->getDescription(); } else { $description = $this->validators[0]->getDescription(); @@ -53,7 +53,7 @@ public function isValid(mixed $value): bool foreach ($this->validators as $rule) { $valid = $rule->isValid($value); - if($valid) { + if ($valid) { $this->failedRule = $rule; return false; } diff --git a/tests/ResponseTest.php b/tests/ResponseTest.php index d9d0acc1..8dd8394a 100755 --- a/tests/ResponseTest.php +++ b/tests/ResponseTest.php @@ -36,7 +36,7 @@ public function testCanSetStatus() try { $this->response->setStatusCode(0); // Unknown status code - } catch(\Exception $e) { + } catch (\Exception $e) { $this->assertInstanceOf('\Exception', $e); return; From 9d62aad909b651fd45175644ec0c86c55550e29a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Thu, 21 Aug 2025 16:28:08 +0200 Subject: [PATCH 070/103] Add cookies header support for swoole --- composer.json | 2 +- src/Http/Adapter/Swoole/Request.php | 17 ++++++++++++++++- tests/e2e/BaseTest.php | 10 ++++++++++ tests/e2e/init.php | 9 +++++++++ 4 files changed, 36 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index 3b0d7f66..377787e6 100644 --- a/composer.json +++ b/composer.json @@ -24,7 +24,7 @@ "scripts": { "lint": "vendor/bin/pint --test", "format": "vendor/bin/pint", - "check": "vendor/bin/phpstan analyse -c phpstan.neon", + "check": "vendor/bin/phpstan analyse -c phpstan.neon --memory-limit 512M", "test": "vendor/bin/phpunit --configuration phpunit.xml", "bench": "vendor/bin/phpbench run --report=benchmark" }, diff --git a/src/Http/Adapter/Swoole/Request.php b/src/Http/Adapter/Swoole/Request.php index 164cf81c..0ec31263 100644 --- a/src/Http/Adapter/Swoole/Request.php +++ b/src/Http/Adapter/Swoole/Request.php @@ -359,6 +359,21 @@ protected function generateInput(): array */ protected function generateHeaders(): array { - return $this->swoole->header; + $headers = $this->swoole->header; + + if (empty($this->swoole->cookie)) { + return $headers; + } + + $cookieHeaders = []; + foreach ($this->swoole->cookie as $key => $value) { + $cookieHeaders[] = "{$key}={$value}"; + } + + if (!empty($cookieHeaders)) { + $headers['cookie'] = \implode('; ', $cookieHeaders); + } + + return $headers; } } diff --git a/tests/e2e/BaseTest.php b/tests/e2e/BaseTest.php index 3fbdef51..57d04aa6 100644 --- a/tests/e2e/BaseTest.php +++ b/tests/e2e/BaseTest.php @@ -35,4 +35,14 @@ public function testFile() $response = $this->client->call(Client::METHOD_GET, '/humans.txt'); $this->assertEquals(204, $response['headers']['status-code']); } + + public function testCookie() + { + $response = $this->client->call(Client::METHOD_GET, '/cookies', [ + 'Cookie: cookie1=value1; cookie2=value2' + ]); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals('cookie1=value1; cookie2=value2', $response['body']); + } } diff --git a/tests/e2e/init.php b/tests/e2e/init.php index 64bb3cfa..2c85bd13 100644 --- a/tests/e2e/init.php +++ b/tests/e2e/init.php @@ -3,6 +3,7 @@ require_once __DIR__.'/../../vendor/autoload.php'; use Utopia\Http\Http; +use Utopia\Http\Request; use Utopia\Http\Response; use Utopia\Http\Validator\Text; @@ -25,6 +26,14 @@ $response->send($value); }); + +Http::get('/cookies') + ->inject('request') + ->inject('response') + ->action(function (Request $request, Response $response) { + $response->send($request->getHeaders()['cookie'] ?? ''); + }); + Http::get('/chunked') ->inject('response') ->action(function (Response $response) { From 51343ac5ef3051e92c898e155365f063fd6e477c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Tue, 26 Aug 2025 09:18:39 +0000 Subject: [PATCH 071/103] Implement domain restrictions --- composer.lock | 589 +++++++++++++++++++-------------- src/Validator/Domain.php | 54 +++ tests/Validator/DomainTest.php | 26 ++ 3 files changed, 417 insertions(+), 252 deletions(-) diff --git a/composer.lock b/composer.lock index 698e26e8..96f94159 100644 --- a/composer.lock +++ b/composer.lock @@ -8,16 +8,16 @@ "packages": [ { "name": "brick/math", - "version": "0.12.3", + "version": "0.13.1", "source": { "type": "git", "url": "https://github.com/brick/math.git", - "reference": "866551da34e9a618e64a819ee1e01c20d8a588ba" + "reference": "fc7ed316430118cc7836bf45faff18d5dfc8de04" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/brick/math/zipball/866551da34e9a618e64a819ee1e01c20d8a588ba", - "reference": "866551da34e9a618e64a819ee1e01c20d8a588ba", + "url": "https://api.github.com/repos/brick/math/zipball/fc7ed316430118cc7836bf45faff18d5dfc8de04", + "reference": "fc7ed316430118cc7836bf45faff18d5dfc8de04", "shasum": "" }, "require": { @@ -56,7 +56,7 @@ ], "support": { "issues": "https://github.com/brick/math/issues", - "source": "https://github.com/brick/math/tree/0.12.3" + "source": "https://github.com/brick/math/tree/0.13.1" }, "funding": [ { @@ -64,20 +64,20 @@ "type": "github" } ], - "time": "2025-02-28T13:11:00+00:00" + "time": "2025-03-29T13:50:30+00:00" }, { "name": "composer/semver", - "version": "3.4.3", + "version": "3.4.4", "source": { "type": "git", "url": "https://github.com/composer/semver.git", - "reference": "4313d26ada5e0c4edfbd1dc481a92ff7bff91f12" + "reference": "198166618906cb2de69b95d7d47e5fa8aa1b2b95" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/semver/zipball/4313d26ada5e0c4edfbd1dc481a92ff7bff91f12", - "reference": "4313d26ada5e0c4edfbd1dc481a92ff7bff91f12", + "url": "https://api.github.com/repos/composer/semver/zipball/198166618906cb2de69b95d7d47e5fa8aa1b2b95", + "reference": "198166618906cb2de69b95d7d47e5fa8aa1b2b95", "shasum": "" }, "require": { @@ -129,7 +129,7 @@ "support": { "irc": "ircs://irc.libera.chat:6697/composer", "issues": "https://github.com/composer/semver/issues", - "source": "https://github.com/composer/semver/tree/3.4.3" + "source": "https://github.com/composer/semver/tree/3.4.4" }, "funding": [ { @@ -139,33 +139,32 @@ { "url": "https://github.com/composer", "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/composer/composer", - "type": "tidelift" } ], - "time": "2024-09-19T14:15:21+00:00" + "time": "2025-08-20T19:15:30+00:00" }, { "name": "google/protobuf", - "version": "v4.30.0", + "version": "v4.32.0", "source": { "type": "git", "url": "https://github.com/protocolbuffers/protobuf-php.git", - "reference": "e1d66682f6836aa87820400f0aa07d9eb566feb6" + "reference": "9a9a92ecbe9c671dc1863f6d4a91ea3ea12c8646" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/protocolbuffers/protobuf-php/zipball/e1d66682f6836aa87820400f0aa07d9eb566feb6", - "reference": "e1d66682f6836aa87820400f0aa07d9eb566feb6", + "url": "https://api.github.com/repos/protocolbuffers/protobuf-php/zipball/9a9a92ecbe9c671dc1863f6d4a91ea3ea12c8646", + "reference": "9a9a92ecbe9c671dc1863f6d4a91ea3ea12c8646", "shasum": "" }, "require": { - "php": ">=7.0.0" + "php": ">=8.1.0" + }, + "provide": { + "ext-protobuf": "*" }, "require-dev": { - "phpunit/phpunit": ">=5.0.0" + "phpunit/phpunit": ">=5.0.0 <8.5.27" }, "suggest": { "ext-bcmath": "Need to support JSON deserialization" @@ -187,9 +186,9 @@ "proto" ], "support": { - "source": "https://github.com/protocolbuffers/protobuf-php/tree/v4.30.0" + "source": "https://github.com/protocolbuffers/protobuf-php/tree/v4.32.0" }, - "time": "2025-03-04T22:54:49+00:00" + "time": "2025-08-14T20:00:33+00:00" }, { "name": "nyholm/psr7", @@ -337,16 +336,16 @@ }, { "name": "open-telemetry/api", - "version": "1.2.3", + "version": "1.4.0", "source": { "type": "git", "url": "https://github.com/opentelemetry-php/api.git", - "reference": "199d7ddda88f5f5619fa73463f1a5a7149ccd1f1" + "reference": "b3a9286f9c1c8247c83493c5b1fa475cd0cec7f7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/opentelemetry-php/api/zipball/199d7ddda88f5f5619fa73463f1a5a7149ccd1f1", - "reference": "199d7ddda88f5f5619fa73463f1a5a7149ccd1f1", + "url": "https://api.github.com/repos/opentelemetry-php/api/zipball/b3a9286f9c1c8247c83493c5b1fa475cd0cec7f7", + "reference": "b3a9286f9c1c8247c83493c5b1fa475cd0cec7f7", "shasum": "" }, "require": { @@ -366,7 +365,7 @@ ] }, "branch-alias": { - "dev-main": "1.1.x-dev" + "dev-main": "1.4.x-dev" } }, "autoload": { @@ -403,20 +402,20 @@ "issues": "https://github.com/open-telemetry/opentelemetry-php/issues", "source": "https://github.com/open-telemetry/opentelemetry-php" }, - "time": "2025-03-05T21:42:54+00:00" + "time": "2025-06-19T23:36:51+00:00" }, { "name": "open-telemetry/context", - "version": "1.1.0", + "version": "1.3.1", "source": { "type": "git", "url": "https://github.com/opentelemetry-php/context.git", - "reference": "0cba875ea1953435f78aec7f1d75afa87bdbf7f3" + "reference": "438f71812242db3f196fb4c717c6f92cbc819be6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/opentelemetry-php/context/zipball/0cba875ea1953435f78aec7f1d75afa87bdbf7f3", - "reference": "0cba875ea1953435f78aec7f1d75afa87bdbf7f3", + "url": "https://api.github.com/repos/opentelemetry-php/context/zipball/438f71812242db3f196fb4c717c6f92cbc819be6", + "reference": "438f71812242db3f196fb4c717c6f92cbc819be6", "shasum": "" }, "require": { @@ -462,20 +461,20 @@ "issues": "https://github.com/open-telemetry/opentelemetry-php/issues", "source": "https://github.com/open-telemetry/opentelemetry-php" }, - "time": "2024-08-21T00:29:20+00:00" + "time": "2025-08-13T01:12:00+00:00" }, { "name": "open-telemetry/exporter-otlp", - "version": "1.2.0", + "version": "1.3.2", "source": { "type": "git", "url": "https://github.com/opentelemetry-php/exporter-otlp.git", - "reference": "243d9657c44a06f740cf384f486afe954c2b725f" + "reference": "196f3a1dbce3b2c0f8110d164232c11ac00ddbb2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/opentelemetry-php/exporter-otlp/zipball/243d9657c44a06f740cf384f486afe954c2b725f", - "reference": "243d9657c44a06f740cf384f486afe954c2b725f", + "url": "https://api.github.com/repos/opentelemetry-php/exporter-otlp/zipball/196f3a1dbce3b2c0f8110d164232c11ac00ddbb2", + "reference": "196f3a1dbce3b2c0f8110d164232c11ac00ddbb2", "shasum": "" }, "require": { @@ -526,7 +525,7 @@ "issues": "https://github.com/open-telemetry/opentelemetry-php/issues", "source": "https://github.com/open-telemetry/opentelemetry-php" }, - "time": "2025-01-08T23:50:03+00:00" + "time": "2025-06-16T00:24:51+00:00" }, { "name": "open-telemetry/gen-otlp-protobuf", @@ -593,22 +592,22 @@ }, { "name": "open-telemetry/sdk", - "version": "1.2.2", + "version": "1.7.0", "source": { "type": "git", "url": "https://github.com/opentelemetry-php/sdk.git", - "reference": "37eec0fe47ddd627911f318f29b6cd48196be0c0" + "reference": "86287cf30fd6549444d7b8f7d8758d92e24086ac" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/opentelemetry-php/sdk/zipball/37eec0fe47ddd627911f318f29b6cd48196be0c0", - "reference": "37eec0fe47ddd627911f318f29b6cd48196be0c0", + "url": "https://api.github.com/repos/opentelemetry-php/sdk/zipball/86287cf30fd6549444d7b8f7d8758d92e24086ac", + "reference": "86287cf30fd6549444d7b8f7d8758d92e24086ac", "shasum": "" }, "require": { "ext-json": "*", "nyholm/psr7-server": "^1.1", - "open-telemetry/api": "~1.0 || ~1.1", + "open-telemetry/api": "~1.4.0", "open-telemetry/context": "^1.0", "open-telemetry/sem-conv": "^1.0", "php": "^8.1", @@ -621,7 +620,7 @@ "ramsey/uuid": "^3.0 || ^4.0", "symfony/polyfill-mbstring": "^1.23", "symfony/polyfill-php82": "^1.26", - "tbachert/spi": "^1.0.1" + "tbachert/spi": "^1.0.5" }, "suggest": { "ext-gmp": "To support unlimited number of synchronous metric readers", @@ -631,6 +630,13 @@ "type": "library", "extra": { "spi": { + "OpenTelemetry\\API\\Configuration\\ConfigEnv\\EnvComponentLoader": [ + "OpenTelemetry\\API\\Instrumentation\\Configuration\\General\\ConfigEnv\\EnvComponentLoaderHttpConfig", + "OpenTelemetry\\API\\Instrumentation\\Configuration\\General\\ConfigEnv\\EnvComponentLoaderPeerConfig" + ], + "OpenTelemetry\\SDK\\Common\\Configuration\\Resolver\\ResolverInterface": [ + "OpenTelemetry\\SDK\\Common\\Configuration\\Resolver\\SdkConfigurationResolver" + ], "OpenTelemetry\\API\\Instrumentation\\AutoInstrumentation\\HookManagerInterface": [ "OpenTelemetry\\API\\Instrumentation\\AutoInstrumentation\\ExtensionHookManager" ] @@ -679,20 +685,20 @@ "issues": "https://github.com/open-telemetry/opentelemetry-php/issues", "source": "https://github.com/open-telemetry/opentelemetry-php" }, - "time": "2025-01-29T21:40:28+00:00" + "time": "2025-08-06T03:07:06+00:00" }, { "name": "open-telemetry/sem-conv", - "version": "1.30.0", + "version": "1.36.0", "source": { "type": "git", "url": "https://github.com/opentelemetry-php/sem-conv.git", - "reference": "4178c9f390da8e4dbca9b181a9d1efd50cf7ee0a" + "reference": "60dd18fd21d45e6f4234ecab89c14021b6e3de9a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/opentelemetry-php/sem-conv/zipball/4178c9f390da8e4dbca9b181a9d1efd50cf7ee0a", - "reference": "4178c9f390da8e4dbca9b181a9d1efd50cf7ee0a", + "url": "https://api.github.com/repos/opentelemetry-php/sem-conv/zipball/60dd18fd21d45e6f4234ecab89c14021b6e3de9a", + "reference": "60dd18fd21d45e6f4234ecab89c14021b6e3de9a", "shasum": "" }, "require": { @@ -736,7 +742,7 @@ "issues": "https://github.com/open-telemetry/opentelemetry-php/issues", "source": "https://github.com/open-telemetry/opentelemetry-php" }, - "time": "2025-02-06T00:21:48+00:00" + "time": "2025-08-04T03:22:08+00:00" }, { "name": "php-http/discovery", @@ -1082,16 +1088,16 @@ }, { "name": "ramsey/collection", - "version": "2.1.0", + "version": "2.1.1", "source": { "type": "git", "url": "https://github.com/ramsey/collection.git", - "reference": "3c5990b8a5e0b79cd1cf11c2dc1229e58e93f109" + "reference": "344572933ad0181accbf4ba763e85a0306a8c5e2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ramsey/collection/zipball/3c5990b8a5e0b79cd1cf11c2dc1229e58e93f109", - "reference": "3c5990b8a5e0b79cd1cf11c2dc1229e58e93f109", + "url": "https://api.github.com/repos/ramsey/collection/zipball/344572933ad0181accbf4ba763e85a0306a8c5e2", + "reference": "344572933ad0181accbf4ba763e85a0306a8c5e2", "shasum": "" }, "require": { @@ -1152,27 +1158,26 @@ ], "support": { "issues": "https://github.com/ramsey/collection/issues", - "source": "https://github.com/ramsey/collection/tree/2.1.0" + "source": "https://github.com/ramsey/collection/tree/2.1.1" }, - "time": "2025-03-02T04:48:29+00:00" + "time": "2025-03-22T05:38:12+00:00" }, { "name": "ramsey/uuid", - "version": "4.7.6", + "version": "4.9.0", "source": { "type": "git", "url": "https://github.com/ramsey/uuid.git", - "reference": "91039bc1faa45ba123c4328958e620d382ec7088" + "reference": "4e0e23cc785f0724a0e838279a9eb03f28b092a0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ramsey/uuid/zipball/91039bc1faa45ba123c4328958e620d382ec7088", - "reference": "91039bc1faa45ba123c4328958e620d382ec7088", + "url": "https://api.github.com/repos/ramsey/uuid/zipball/4e0e23cc785f0724a0e838279a9eb03f28b092a0", + "reference": "4e0e23cc785f0724a0e838279a9eb03f28b092a0", "shasum": "" }, "require": { - "brick/math": "^0.8.8 || ^0.9 || ^0.10 || ^0.11 || ^0.12", - "ext-json": "*", + "brick/math": "^0.8.8 || ^0.9 || ^0.10 || ^0.11 || ^0.12 || ^0.13", "php": "^8.0", "ramsey/collection": "^1.2 || ^2.0" }, @@ -1180,26 +1185,23 @@ "rhumsaa/uuid": "self.version" }, "require-dev": { - "captainhook/captainhook": "^5.10", + "captainhook/captainhook": "^5.25", "captainhook/plugin-composer": "^5.3", - "dealerdirect/phpcodesniffer-composer-installer": "^0.7.0", - "doctrine/annotations": "^1.8", - "ergebnis/composer-normalize": "^2.15", - "mockery/mockery": "^1.3", + "dealerdirect/phpcodesniffer-composer-installer": "^1.0", + "ergebnis/composer-normalize": "^2.47", + "mockery/mockery": "^1.6", "paragonie/random-lib": "^2", - "php-mock/php-mock": "^2.2", - "php-mock/php-mock-mockery": "^1.3", - "php-parallel-lint/php-parallel-lint": "^1.1", - "phpbench/phpbench": "^1.0", - "phpstan/extension-installer": "^1.1", - "phpstan/phpstan": "^1.8", - "phpstan/phpstan-mockery": "^1.1", - "phpstan/phpstan-phpunit": "^1.1", - "phpunit/phpunit": "^8.5 || ^9", - "ramsey/composer-repl": "^1.4", - "slevomat/coding-standard": "^8.4", - "squizlabs/php_codesniffer": "^3.5", - "vimeo/psalm": "^4.9" + "php-mock/php-mock": "^2.6", + "php-mock/php-mock-mockery": "^1.5", + "php-parallel-lint/php-parallel-lint": "^1.4.0", + "phpbench/phpbench": "^1.2.14", + "phpstan/extension-installer": "^1.4", + "phpstan/phpstan": "^2.1", + "phpstan/phpstan-mockery": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpunit/phpunit": "^9.6", + "slevomat/coding-standard": "^8.18", + "squizlabs/php_codesniffer": "^3.13" }, "suggest": { "ext-bcmath": "Enables faster math with arbitrary-precision integers using BCMath.", @@ -1234,32 +1236,22 @@ ], "support": { "issues": "https://github.com/ramsey/uuid/issues", - "source": "https://github.com/ramsey/uuid/tree/4.7.6" + "source": "https://github.com/ramsey/uuid/tree/4.9.0" }, - "funding": [ - { - "url": "https://github.com/ramsey", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/ramsey/uuid", - "type": "tidelift" - } - ], - "time": "2024-04-27T21:32:50+00:00" + "time": "2025-06-25T14:20:11+00:00" }, { "name": "symfony/deprecation-contracts", - "version": "v3.5.1", + "version": "v3.6.0", "source": { "type": "git", "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6" + "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6", - "reference": "74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/63afe740e99a13ba87ec199bb07bbdee937a5b62", + "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62", "shasum": "" }, "require": { @@ -1272,7 +1264,7 @@ "name": "symfony/contracts" }, "branch-alias": { - "dev-main": "3.5-dev" + "dev-main": "3.6-dev" } }, "autoload": { @@ -1297,7 +1289,7 @@ "description": "A generic function and convention to trigger deprecation notices", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v3.5.1" + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.6.0" }, "funding": [ { @@ -1313,20 +1305,20 @@ "type": "tidelift" } ], - "time": "2024-09-25T14:20:29+00:00" + "time": "2024-09-25T14:21:43+00:00" }, { "name": "symfony/http-client", - "version": "v7.2.4", + "version": "v7.3.2", "source": { "type": "git", "url": "https://github.com/symfony/http-client.git", - "reference": "78981a2ffef6437ed92d4d7e2a86a82f256c6dc6" + "reference": "1c064a0c67749923483216b081066642751cc2c7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-client/zipball/78981a2ffef6437ed92d4d7e2a86a82f256c6dc6", - "reference": "78981a2ffef6437ed92d4d7e2a86a82f256c6dc6", + "url": "https://api.github.com/repos/symfony/http-client/zipball/1c064a0c67749923483216b081066642751cc2c7", + "reference": "1c064a0c67749923483216b081066642751cc2c7", "shasum": "" }, "require": { @@ -1338,6 +1330,7 @@ }, "conflict": { "amphp/amp": "<2.5", + "amphp/socket": "<1.1", "php-http/discovery": "<1.15", "symfony/http-foundation": "<6.4" }, @@ -1350,7 +1343,6 @@ "require-dev": { "amphp/http-client": "^4.2.1|^5.0", "amphp/http-tunnel": "^1.0|^2.0", - "amphp/socket": "^1.1", "guzzlehttp/promises": "^1.4|^2.0", "nyholm/psr7": "^1.0", "php-http/httplug": "^1.0|^2.0", @@ -1392,7 +1384,7 @@ "http" ], "support": { - "source": "https://github.com/symfony/http-client/tree/v7.2.4" + "source": "https://github.com/symfony/http-client/tree/v7.3.2" }, "funding": [ { @@ -1403,25 +1395,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-02-13T10:27:23+00:00" + "time": "2025-07-15T11:36:08+00:00" }, { "name": "symfony/http-client-contracts", - "version": "v3.5.2", + "version": "v3.6.0", "source": { "type": "git", "url": "https://github.com/symfony/http-client-contracts.git", - "reference": "ee8d807ab20fcb51267fdace50fbe3494c31e645" + "reference": "75d7043853a42837e68111812f4d964b01e5101c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-client-contracts/zipball/ee8d807ab20fcb51267fdace50fbe3494c31e645", - "reference": "ee8d807ab20fcb51267fdace50fbe3494c31e645", + "url": "https://api.github.com/repos/symfony/http-client-contracts/zipball/75d7043853a42837e68111812f4d964b01e5101c", + "reference": "75d7043853a42837e68111812f4d964b01e5101c", "shasum": "" }, "require": { @@ -1434,7 +1430,7 @@ "name": "symfony/contracts" }, "branch-alias": { - "dev-main": "3.5-dev" + "dev-main": "3.6-dev" } }, "autoload": { @@ -1470,7 +1466,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/http-client-contracts/tree/v3.5.2" + "source": "https://github.com/symfony/http-client-contracts/tree/v3.6.0" }, "funding": [ { @@ -1486,23 +1482,24 @@ "type": "tidelift" } ], - "time": "2024-12-07T08:49:48+00:00" + "time": "2025-04-29T11:18:49+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.31.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341" + "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/85181ba99b2345b0ef10ce42ecac37612d9fd341", - "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6d857f4d76bd4b343eac26d6b539585d2bc56493", + "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493", "shasum": "" }, "require": { + "ext-iconv": "*", "php": ">=7.2" }, "provide": { @@ -1550,7 +1547,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.31.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.33.0" }, "funding": [ { @@ -1561,16 +1558,20 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-09T11:45:10+00:00" + "time": "2024-12-23T08:48:59+00:00" }, { "name": "symfony/polyfill-php82", - "version": "v1.31.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php82.git", @@ -1626,7 +1627,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php82/tree/v1.31.0" + "source": "https://github.com/symfony/polyfill-php82/tree/v1.33.0" }, "funding": [ { @@ -1637,6 +1638,10 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" @@ -1646,16 +1651,16 @@ }, { "name": "symfony/service-contracts", - "version": "v3.5.1", + "version": "v3.6.0", "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", - "reference": "e53260aabf78fb3d63f8d79d69ece59f80d5eda0" + "reference": "f021b05a130d35510bd6b25fe9053c2a8a15d5d4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/e53260aabf78fb3d63f8d79d69ece59f80d5eda0", - "reference": "e53260aabf78fb3d63f8d79d69ece59f80d5eda0", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/f021b05a130d35510bd6b25fe9053c2a8a15d5d4", + "reference": "f021b05a130d35510bd6b25fe9053c2a8a15d5d4", "shasum": "" }, "require": { @@ -1673,7 +1678,7 @@ "name": "symfony/contracts" }, "branch-alias": { - "dev-main": "3.5-dev" + "dev-main": "3.6-dev" } }, "autoload": { @@ -1709,7 +1714,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/service-contracts/tree/v3.5.1" + "source": "https://github.com/symfony/service-contracts/tree/v3.6.0" }, "funding": [ { @@ -1725,20 +1730,20 @@ "type": "tidelift" } ], - "time": "2024-09-25T14:20:29+00:00" + "time": "2025-04-25T09:37:31+00:00" }, { "name": "tbachert/spi", - "version": "v1.0.2", + "version": "v1.0.5", "source": { "type": "git", "url": "https://github.com/Nevay/spi.git", - "reference": "2ddfaf815dafb45791a61b08170de8d583c16062" + "reference": "e7078767866d0a9e0f91d3f9d42a832df5e39002" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Nevay/spi/zipball/2ddfaf815dafb45791a61b08170de8d583c16062", - "reference": "2ddfaf815dafb45791a61b08170de8d583c16062", + "url": "https://api.github.com/repos/Nevay/spi/zipball/e7078767866d0a9e0f91d3f9d42a832df5e39002", + "reference": "e7078767866d0a9e0f91d3f9d42a832df5e39002", "shasum": "" }, "require": { @@ -1756,7 +1761,7 @@ "extra": { "class": "Nevay\\SPI\\Composer\\Plugin", "branch-alias": { - "dev-main": "0.2.x-dev" + "dev-main": "1.0.x-dev" }, "plugin-optional": true }, @@ -1775,9 +1780,9 @@ ], "support": { "issues": "https://github.com/Nevay/spi/issues", - "source": "https://github.com/Nevay/spi/tree/v1.0.2" + "source": "https://github.com/Nevay/spi/tree/v1.0.5" }, - "time": "2024-10-04T16:36:12+00:00" + "time": "2025-06-29T15:42:06+00:00" }, { "name": "utopia-php/compression", @@ -1827,16 +1832,16 @@ }, { "name": "utopia-php/telemetry", - "version": "0.1.0", + "version": "0.1.1", "source": { "type": "git", "url": "https://github.com/utopia-php/telemetry.git", - "reference": "d35f2f0632f4ee0be63fb7ace6a94a6adda71a80" + "reference": "437f0021777f0e575dfb9e8a1a081b3aed75e33f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/telemetry/zipball/d35f2f0632f4ee0be63fb7ace6a94a6adda71a80", - "reference": "d35f2f0632f4ee0be63fb7ace6a94a6adda71a80", + "url": "https://api.github.com/repos/utopia-php/telemetry/zipball/437f0021777f0e575dfb9e8a1a081b3aed75e33f", + "reference": "437f0021777f0e575dfb9e8a1a081b3aed75e33f", "shasum": "" }, "require": { @@ -1857,7 +1862,7 @@ "type": "library", "autoload": { "psr-4": { - "Utopia\\": "src/" + "Utopia\\Telemetry\\": "src/Telemetry" } }, "notification-url": "https://packagist.org/downloads/", @@ -1871,9 +1876,9 @@ ], "support": { "issues": "https://github.com/utopia-php/telemetry/issues", - "source": "https://github.com/utopia-php/telemetry/tree/0.1.0" + "source": "https://github.com/utopia-php/telemetry/tree/0.1.1" }, - "time": "2024-11-13T10:29:53+00:00" + "time": "2025-03-17T11:57:52+00:00" } ], "packages-dev": [ @@ -2102,16 +2107,16 @@ }, { "name": "laravel/pint", - "version": "v1.21.0", + "version": "v1.24.0", "source": { "type": "git", "url": "https://github.com/laravel/pint.git", - "reference": "531fa0871fbde719c51b12afa3a443b8f4e4b425" + "reference": "0345f3b05f136801af8c339f9d16ef29e6b4df8a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/pint/zipball/531fa0871fbde719c51b12afa3a443b8f4e4b425", - "reference": "531fa0871fbde719c51b12afa3a443b8f4e4b425", + "url": "https://api.github.com/repos/laravel/pint/zipball/0345f3b05f136801af8c339f9d16ef29e6b4df8a", + "reference": "0345f3b05f136801af8c339f9d16ef29e6b4df8a", "shasum": "" }, "require": { @@ -2122,12 +2127,12 @@ "php": "^8.2.0" }, "require-dev": { - "friendsofphp/php-cs-fixer": "^3.68.5", - "illuminate/view": "^11.42.0", - "larastan/larastan": "^3.0.4", - "laravel-zero/framework": "^11.36.1", + "friendsofphp/php-cs-fixer": "^3.82.2", + "illuminate/view": "^11.45.1", + "larastan/larastan": "^3.5.0", + "laravel-zero/framework": "^11.45.0", "mockery/mockery": "^1.6.12", - "nunomaduro/termwind": "^2.3", + "nunomaduro/termwind": "^2.3.1", "pestphp/pest": "^2.36.0" }, "bin": [ @@ -2135,6 +2140,9 @@ ], "type": "project", "autoload": { + "files": [ + "overrides/Runner/Parallel/ProcessFactory.php" + ], "psr-4": { "App\\": "app/", "Database\\Seeders\\": "database/seeders/", @@ -2164,20 +2172,20 @@ "issues": "https://github.com/laravel/pint/issues", "source": "https://github.com/laravel/pint" }, - "time": "2025-02-18T03:18:57+00:00" + "time": "2025-07-10T18:09:32+00:00" }, { "name": "myclabs/deep-copy", - "version": "1.13.0", + "version": "1.13.4", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "024473a478be9df5fdaca2c793f2232fe788e414" + "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/024473a478be9df5fdaca2c793f2232fe788e414", - "reference": "024473a478be9df5fdaca2c793f2232fe788e414", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/07d290f0c47959fd5eed98c95ee5602db07e0b6a", + "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a", "shasum": "" }, "require": { @@ -2216,7 +2224,7 @@ ], "support": { "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.13.0" + "source": "https://github.com/myclabs/DeepCopy/tree/1.13.4" }, "funding": [ { @@ -2224,20 +2232,20 @@ "type": "tidelift" } ], - "time": "2025-02-12T12:17:51+00:00" + "time": "2025-08-01T08:46:24+00:00" }, { "name": "nikic/php-parser", - "version": "v5.4.0", + "version": "v5.6.1", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "447a020a1f875a434d62f2a401f53b82a396e494" + "reference": "f103601b29efebd7ff4a1ca7b3eeea9e3336a2a2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/447a020a1f875a434d62f2a401f53b82a396e494", - "reference": "447a020a1f875a434d62f2a401f53b82a396e494", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/f103601b29efebd7ff4a1ca7b3eeea9e3336a2a2", + "reference": "f103601b29efebd7ff4a1ca7b3eeea9e3336a2a2", "shasum": "" }, "require": { @@ -2256,7 +2264,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "5.0-dev" + "dev-master": "5.x-dev" } }, "autoload": { @@ -2280,9 +2288,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v5.4.0" + "source": "https://github.com/nikic/PHP-Parser/tree/v5.6.1" }, - "time": "2024-12-30T11:07:19+00:00" + "time": "2025-08-13T20:13:15+00:00" }, { "name": "phar-io/manifest", @@ -2455,16 +2463,16 @@ }, { "name": "phpbench/phpbench", - "version": "1.4.0", + "version": "1.4.1", "source": { "type": "git", "url": "https://github.com/phpbench/phpbench.git", - "reference": "4248817222514421cba466bfa7adc7d8932345d4" + "reference": "78cd98a9aa34e0f8f80ca01972a8b88d2c30194b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpbench/phpbench/zipball/4248817222514421cba466bfa7adc7d8932345d4", - "reference": "4248817222514421cba466bfa7adc7d8932345d4", + "url": "https://api.github.com/repos/phpbench/phpbench/zipball/78cd98a9aa34e0f8f80ca01972a8b88d2c30194b", + "reference": "78cd98a9aa34e0f8f80ca01972a8b88d2c30194b", "shasum": "" }, "require": { @@ -2541,7 +2549,7 @@ ], "support": { "issues": "https://github.com/phpbench/phpbench/issues", - "source": "https://github.com/phpbench/phpbench/tree/1.4.0" + "source": "https://github.com/phpbench/phpbench/tree/1.4.1" }, "funding": [ { @@ -2549,20 +2557,20 @@ "type": "github" } ], - "time": "2025-01-26T19:54:45+00:00" + "time": "2025-03-12T08:01:40+00:00" }, { "name": "phpstan/phpstan", - "version": "1.12.20", + "version": "1.12.28", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "3240b1972042c7f73cf1045e879ea5bd5f761bb7" + "reference": "fcf8b71aeab4e1a1131d1783cef97b23a51b87a9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/3240b1972042c7f73cf1045e879ea5bd5f761bb7", - "reference": "3240b1972042c7f73cf1045e879ea5bd5f761bb7", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/fcf8b71aeab4e1a1131d1783cef97b23a51b87a9", + "reference": "fcf8b71aeab4e1a1131d1783cef97b23a51b87a9", "shasum": "" }, "require": { @@ -2607,7 +2615,7 @@ "type": "github" } ], - "time": "2025-03-05T13:37:43+00:00" + "time": "2025-07-17T17:15:39+00:00" }, { "name": "phpunit/php-code-coverage", @@ -2930,16 +2938,16 @@ }, { "name": "phpunit/phpunit", - "version": "9.6.22", + "version": "9.6.25", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "f80235cb4d3caa59ae09be3adf1ded27521d1a9c" + "reference": "049c011e01be805202d8eebedef49f769a8ec7b7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/f80235cb4d3caa59ae09be3adf1ded27521d1a9c", - "reference": "f80235cb4d3caa59ae09be3adf1ded27521d1a9c", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/049c011e01be805202d8eebedef49f769a8ec7b7", + "reference": "049c011e01be805202d8eebedef49f769a8ec7b7", "shasum": "" }, "require": { @@ -2950,7 +2958,7 @@ "ext-mbstring": "*", "ext-xml": "*", "ext-xmlwriter": "*", - "myclabs/deep-copy": "^1.12.1", + "myclabs/deep-copy": "^1.13.4", "phar-io/manifest": "^2.0.4", "phar-io/version": "^3.2.1", "php": ">=7.3", @@ -2961,11 +2969,11 @@ "phpunit/php-timer": "^5.0.3", "sebastian/cli-parser": "^1.0.2", "sebastian/code-unit": "^1.0.8", - "sebastian/comparator": "^4.0.8", + "sebastian/comparator": "^4.0.9", "sebastian/diff": "^4.0.6", "sebastian/environment": "^5.1.5", "sebastian/exporter": "^4.0.6", - "sebastian/global-state": "^5.0.7", + "sebastian/global-state": "^5.0.8", "sebastian/object-enumerator": "^4.0.4", "sebastian/resource-operations": "^3.0.4", "sebastian/type": "^3.2.1", @@ -3013,7 +3021,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.22" + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.25" }, "funding": [ { @@ -3024,12 +3032,20 @@ "url": "https://github.com/sebastianbergmann", "type": "github" }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, { "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", "type": "tidelift" } ], - "time": "2024-12-05T13:48:26+00:00" + "time": "2025-08-20T14:38:31+00:00" }, { "name": "psr/cache", @@ -3249,16 +3265,16 @@ }, { "name": "sebastian/comparator", - "version": "4.0.8", + "version": "4.0.9", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "fa0f136dd2334583309d32b62544682ee972b51a" + "reference": "67a2df3a62639eab2cc5906065e9805d4fd5dfc5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/fa0f136dd2334583309d32b62544682ee972b51a", - "reference": "fa0f136dd2334583309d32b62544682ee972b51a", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/67a2df3a62639eab2cc5906065e9805d4fd5dfc5", + "reference": "67a2df3a62639eab2cc5906065e9805d4fd5dfc5", "shasum": "" }, "require": { @@ -3311,15 +3327,27 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/comparator/issues", - "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.8" + "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.9" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/comparator", + "type": "tidelift" } ], - "time": "2022-09-14T12:41:17+00:00" + "time": "2025-08-10T06:51:50+00:00" }, { "name": "sebastian/complexity", @@ -3586,16 +3614,16 @@ }, { "name": "sebastian/global-state", - "version": "5.0.7", + "version": "5.0.8", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "bca7df1f32ee6fe93b4d4a9abbf69e13a4ada2c9" + "reference": "b6781316bdcd28260904e7cc18ec983d0d2ef4f6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bca7df1f32ee6fe93b4d4a9abbf69e13a4ada2c9", - "reference": "bca7df1f32ee6fe93b4d4a9abbf69e13a4ada2c9", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/b6781316bdcd28260904e7cc18ec983d0d2ef4f6", + "reference": "b6781316bdcd28260904e7cc18ec983d0d2ef4f6", "shasum": "" }, "require": { @@ -3638,15 +3666,27 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/global-state/issues", - "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.7" + "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.8" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/global-state", + "type": "tidelift" } ], - "time": "2024-03-02T06:35:11+00:00" + "time": "2025-08-10T07:10:35+00:00" }, { "name": "sebastian/lines-of-code", @@ -3819,16 +3859,16 @@ }, { "name": "sebastian/recursion-context", - "version": "4.0.5", + "version": "4.0.6", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1" + "reference": "539c6691e0623af6dc6f9c20384c120f963465a0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", - "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/539c6691e0623af6dc6f9c20384c120f963465a0", + "reference": "539c6691e0623af6dc6f9c20384c120f963465a0", "shasum": "" }, "require": { @@ -3870,15 +3910,27 @@ "homepage": "https://github.com/sebastianbergmann/recursion-context", "support": { "issues": "https://github.com/sebastianbergmann/recursion-context/issues", - "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.5" + "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.6" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/recursion-context", + "type": "tidelift" } ], - "time": "2023-02-03T06:07:39+00:00" + "time": "2025-08-10T06:57:39+00:00" }, { "name": "sebastian/resource-operations", @@ -4109,23 +4161,24 @@ }, { "name": "symfony/console", - "version": "v7.2.1", + "version": "v7.3.2", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "fefcc18c0f5d0efe3ab3152f15857298868dc2c3" + "reference": "5f360ebc65c55265a74d23d7fe27f957870158a1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/fefcc18c0f5d0efe3ab3152f15857298868dc2c3", - "reference": "fefcc18c0f5d0efe3ab3152f15857298868dc2c3", + "url": "https://api.github.com/repos/symfony/console/zipball/5f360ebc65c55265a74d23d7fe27f957870158a1", + "reference": "5f360ebc65c55265a74d23d7fe27f957870158a1", "shasum": "" }, "require": { "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-mbstring": "~1.0", "symfony/service-contracts": "^2.5|^3", - "symfony/string": "^6.4|^7.0" + "symfony/string": "^7.2" }, "conflict": { "symfony/dependency-injection": "<6.4", @@ -4182,7 +4235,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v7.2.1" + "source": "https://github.com/symfony/console/tree/v7.3.2" }, "funding": [ { @@ -4193,25 +4246,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-12-11T03:49:26+00:00" + "time": "2025-07-30T17:13:41+00:00" }, { "name": "symfony/filesystem", - "version": "v7.2.0", + "version": "v7.3.2", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "b8dce482de9d7c9fe2891155035a7248ab5c7fdb" + "reference": "edcbb768a186b5c3f25d0643159a787d3e63b7fd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/b8dce482de9d7c9fe2891155035a7248ab5c7fdb", - "reference": "b8dce482de9d7c9fe2891155035a7248ab5c7fdb", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/edcbb768a186b5c3f25d0643159a787d3e63b7fd", + "reference": "edcbb768a186b5c3f25d0643159a787d3e63b7fd", "shasum": "" }, "require": { @@ -4248,7 +4305,7 @@ "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/filesystem/tree/v7.2.0" + "source": "https://github.com/symfony/filesystem/tree/v7.3.2" }, "funding": [ { @@ -4259,25 +4316,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-10-25T15:15:23+00:00" + "time": "2025-07-07T08:17:47+00:00" }, { "name": "symfony/finder", - "version": "v7.2.2", + "version": "v7.3.2", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "87a71856f2f56e4100373e92529eed3171695cfb" + "reference": "2a6614966ba1074fa93dae0bc804227422df4dfe" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/87a71856f2f56e4100373e92529eed3171695cfb", - "reference": "87a71856f2f56e4100373e92529eed3171695cfb", + "url": "https://api.github.com/repos/symfony/finder/zipball/2a6614966ba1074fa93dae0bc804227422df4dfe", + "reference": "2a6614966ba1074fa93dae0bc804227422df4dfe", "shasum": "" }, "require": { @@ -4312,7 +4373,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v7.2.2" + "source": "https://github.com/symfony/finder/tree/v7.3.2" }, "funding": [ { @@ -4323,25 +4384,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-12-30T19:00:17+00:00" + "time": "2025-07-15T13:41:35+00:00" }, { "name": "symfony/options-resolver", - "version": "v7.2.0", + "version": "v7.3.2", "source": { "type": "git", "url": "https://github.com/symfony/options-resolver.git", - "reference": "7da8fbac9dcfef75ffc212235d76b2754ce0cf50" + "reference": "119bcf13e67dbd188e5dbc74228b1686f66acd37" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/options-resolver/zipball/7da8fbac9dcfef75ffc212235d76b2754ce0cf50", - "reference": "7da8fbac9dcfef75ffc212235d76b2754ce0cf50", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/119bcf13e67dbd188e5dbc74228b1686f66acd37", + "reference": "119bcf13e67dbd188e5dbc74228b1686f66acd37", "shasum": "" }, "require": { @@ -4379,7 +4444,7 @@ "options" ], "support": { - "source": "https://github.com/symfony/options-resolver/tree/v7.2.0" + "source": "https://github.com/symfony/options-resolver/tree/v7.3.2" }, "funding": [ { @@ -4390,16 +4455,20 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-11-20T11:17:29+00:00" + "time": "2025-07-15T11:36:08+00:00" }, { "name": "symfony/polyfill-ctype", - "version": "v1.31.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", @@ -4458,7 +4527,7 @@ "portable" ], "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.31.0" + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.33.0" }, "funding": [ { @@ -4469,6 +4538,10 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" @@ -4478,16 +4551,16 @@ }, { "name": "symfony/polyfill-intl-grapheme", - "version": "v1.31.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe" + "reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe", - "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/380872130d3a5dd3ace2f4010d95125fde5d5c70", + "reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70", "shasum": "" }, "require": { @@ -4536,7 +4609,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.31.0" + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.33.0" }, "funding": [ { @@ -4547,16 +4620,20 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-09T11:45:10+00:00" + "time": "2025-06-27T09:58:17+00:00" }, { "name": "symfony/polyfill-intl-normalizer", - "version": "v1.31.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-normalizer.git", @@ -4617,7 +4694,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.31.0" + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.33.0" }, "funding": [ { @@ -4628,6 +4705,10 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" @@ -4637,16 +4718,16 @@ }, { "name": "symfony/process", - "version": "v7.2.4", + "version": "v7.3.0", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "d8f411ff3c7ddc4ae9166fb388d1190a2df5b5cf" + "reference": "40c295f2deb408d5e9d2d32b8ba1dd61e36f05af" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/d8f411ff3c7ddc4ae9166fb388d1190a2df5b5cf", - "reference": "d8f411ff3c7ddc4ae9166fb388d1190a2df5b5cf", + "url": "https://api.github.com/repos/symfony/process/zipball/40c295f2deb408d5e9d2d32b8ba1dd61e36f05af", + "reference": "40c295f2deb408d5e9d2d32b8ba1dd61e36f05af", "shasum": "" }, "require": { @@ -4678,7 +4759,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v7.2.4" + "source": "https://github.com/symfony/process/tree/v7.3.0" }, "funding": [ { @@ -4694,20 +4775,20 @@ "type": "tidelift" } ], - "time": "2025-02-05T08:33:46+00:00" + "time": "2025-04-17T09:11:12+00:00" }, { "name": "symfony/string", - "version": "v7.2.0", + "version": "v7.3.2", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "446e0d146f991dde3e73f45f2c97a9faad773c82" + "reference": "42f505aff654e62ac7ac2ce21033818297ca89ca" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/446e0d146f991dde3e73f45f2c97a9faad773c82", - "reference": "446e0d146f991dde3e73f45f2c97a9faad773c82", + "url": "https://api.github.com/repos/symfony/string/zipball/42f505aff654e62ac7ac2ce21033818297ca89ca", + "reference": "42f505aff654e62ac7ac2ce21033818297ca89ca", "shasum": "" }, "require": { @@ -4765,7 +4846,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v7.2.0" + "source": "https://github.com/symfony/string/tree/v7.3.2" }, "funding": [ { @@ -4776,12 +4857,16 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-11-13T13:31:26+00:00" + "time": "2025-07-10T08:47:49+00:00" }, { "name": "theseer/tokenizer", diff --git a/src/Validator/Domain.php b/src/Validator/Domain.php index db385713..abd5839d 100644 --- a/src/Validator/Domain.php +++ b/src/Validator/Domain.php @@ -13,6 +13,31 @@ */ class Domain extends Validator { + /** + * Helper for creating domain restriction rule. + * Such rules prevent validation from passing, so this behaves as deny-list. + * + * @param string $hostname A domain base, such as top-level domain or subdomain. Restriction is only applied if domain matches this hostname + * @param int $level Specify what level (top-level, subdomain, sub-subdomain, ..) domain must be. Example: "stage.appwrite.io" is level 3 + * @param array $prefixDenyList Disallowed beginning of domain, useful for reserved behaviours, such as prefixing "branch-" for preview domains + * + */ + public static function createRestriction(string $hostname, ?int $level = null, ?array $prefixDenyList = []) + { + return [ + 'hostname' => $hostname, + 'level' => $level, + 'prefixDenyList' => $prefixDenyList, + ]; + } + + /** + * @param array $restrictions Set of conditions that prevent validation from passing + */ + public function __construct(protected array $restrictions = []) + { + } + /** * Get Description * @@ -53,6 +78,35 @@ public function isValid($value): bool return false; } + foreach ($this->restrictions as $restriction) { + $hostname = $restriction['hostname']; + $level = $restriction['level']; + $prefixDenyList = $restriction['prefixDenyList']; + + // Only apply restriction rules to relevant domains + if (!\str_ends_with($value, $hostname)) { + continue; + } + + // Domain-level restriction + if (!is_null($level)) { + $expectedPartsCount = $level; + $partsCount = \count(\explode('.', $value, $expectedPartsCount + 1)); + if ($partsCount !== $expectedPartsCount) { + return false; + } + } + + // Domain prefix (beginning) restriction + if (!is_null($prefixDenyList) && !empty($prefixDenyList)) { + foreach ($prefixDenyList as $deniedPrefix) { + if (\str_starts_with($value, $deniedPrefix)) { + return false; + } + } + } + } + return true; } diff --git a/tests/Validator/DomainTest.php b/tests/Validator/DomainTest.php index fc8484a1..39313a9f 100644 --- a/tests/Validator/DomainTest.php +++ b/tests/Validator/DomainTest.php @@ -57,4 +57,30 @@ public function testIsValid() $this->assertEquals(false, $this->domain->isValid(1)); $this->assertEquals(false, $this->domain->isValid(1.2)); } + + public function testRestrictions() + { + $validator = new Domain([ + Domain::createRestriction('appwrite.network', 3, ['preview-', 'branch-']), + Domain::createRestriction('fra.appwrite.run', 4), + ]); + + $this->assertEquals(true, $validator->isValid('google.com')); + $this->assertEquals(true, $validator->isValid('stage.google.com')); + $this->assertEquals(true, $validator->isValid('shard4.stage.google.com')); + + $this->assertEquals(false, $validator->isValid('appwrite.network')); + $this->assertEquals(false, $validator->isValid('preview-a.appwrite.network')); + $this->assertEquals(false, $validator->isValid('branch-a.appwrite.network')); + $this->assertEquals(true, $validator->isValid('google.appwrite.network')); + $this->assertEquals(false, $validator->isValid('stage.google.appwrite.network')); + $this->assertEquals(false, $validator->isValid('shard4.stage.google.appwrite.network')); + + $this->assertEquals(false, $validator->isValid('fra.appwrite.run')); + $this->assertEquals(true, $validator->isValid('appwrite.run')); + $this->assertEquals(true, $validator->isValid('google.fra.appwrite.run')); + $this->assertEquals(false, $validator->isValid('shard4.google.fra.appwrite.run')); + $this->assertEquals(true, $validator->isValid('branch-google.fra.appwrite.run')); + $this->assertEquals(true, $validator->isValid('preview-google.fra.appwrite.run')); + } } From 4a029c13b374c6daa13cd1cbc043ccd713fc6d79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Tue, 26 Aug 2025 09:28:47 +0000 Subject: [PATCH 072/103] PR review improvements --- src/Validator/Domain.php | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Validator/Domain.php b/src/Validator/Domain.php index abd5839d..83dcdcee 100644 --- a/src/Validator/Domain.php +++ b/src/Validator/Domain.php @@ -18,15 +18,15 @@ class Domain extends Validator * Such rules prevent validation from passing, so this behaves as deny-list. * * @param string $hostname A domain base, such as top-level domain or subdomain. Restriction is only applied if domain matches this hostname - * @param int $level Specify what level (top-level, subdomain, sub-subdomain, ..) domain must be. Example: "stage.appwrite.io" is level 3 + * @param int $levels Specify what level (top-level, subdomain, sub-subdomain, ..) domain must be. Example: "stage.appwrite.io" is level 3 * @param array $prefixDenyList Disallowed beginning of domain, useful for reserved behaviours, such as prefixing "branch-" for preview domains * */ - public static function createRestriction(string $hostname, ?int $level = null, ?array $prefixDenyList = []) + public static function createRestriction(string $hostname, ?int $levels = null, array $prefixDenyList = []) { return [ 'hostname' => $hostname, - 'level' => $level, + 'levels' => $levels, 'prefixDenyList' => $prefixDenyList, ]; } @@ -80,7 +80,7 @@ public function isValid($value): bool foreach ($this->restrictions as $restriction) { $hostname = $restriction['hostname']; - $level = $restriction['level']; + $levels = $restriction['levels']; $prefixDenyList = $restriction['prefixDenyList']; // Only apply restriction rules to relevant domains @@ -89,8 +89,8 @@ public function isValid($value): bool } // Domain-level restriction - if (!is_null($level)) { - $expectedPartsCount = $level; + if (!is_null($levels)) { + $expectedPartsCount = $levels; $partsCount = \count(\explode('.', $value, $expectedPartsCount + 1)); if ($partsCount !== $expectedPartsCount) { return false; @@ -98,7 +98,7 @@ public function isValid($value): bool } // Domain prefix (beginning) restriction - if (!is_null($prefixDenyList) && !empty($prefixDenyList)) { + if (!empty($prefixDenyList)) { foreach ($prefixDenyList as $deniedPrefix) { if (\str_starts_with($value, $deniedPrefix)) { return false; From 4700d389a63eabb1e2accfceaacaf89c73c66079 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Tue, 2 Sep 2025 12:19:35 +0200 Subject: [PATCH 073/103] Improve cookie handling --- src/Http/Adapter/Swoole/Request.php | 29 ++++++++++++++++------------- src/Http/Adapter/Swoole/Server.php | 3 ++- tests/e2e/BaseTest.php | 19 +++++++++++++++++-- 3 files changed, 35 insertions(+), 16 deletions(-) diff --git a/src/Http/Adapter/Swoole/Request.php b/src/Http/Adapter/Swoole/Request.php index 0ec31263..f0dd1d66 100644 --- a/src/Http/Adapter/Swoole/Request.php +++ b/src/Http/Adapter/Swoole/Request.php @@ -253,9 +253,21 @@ public function getFiles($key): array */ public function getCookie(string $key, string $default = ''): string { - $key = strtolower($key); + $key = \strtolower($key); + + $cookies = \explode(';', $this->getHeader('cookie', '')); + foreach ($cookies as $cookie) { + $cookie = \trim($cookie); + [$cookieKey, $cookieValue] = \explode('=', $cookie, 2); + $cookieKey = \trim($cookieKey); + $cookieKey = \strtolower($cookieKey); + $cookieValue = \trim($cookieValue); + if ($cookieValue === $key) { + return $cookieValue; + } + } - return $this->swoole->cookie[$key] ?? $default; + return $default; } /** @@ -361,17 +373,8 @@ protected function generateHeaders(): array { $headers = $this->swoole->header; - if (empty($this->swoole->cookie)) { - return $headers; - } - - $cookieHeaders = []; - foreach ($this->swoole->cookie as $key => $value) { - $cookieHeaders[] = "{$key}={$value}"; - } - - if (!empty($cookieHeaders)) { - $headers['cookie'] = \implode('; ', $cookieHeaders); + foreach ($headers as $key => $value) { + $headers[strtolower($key)] = $value; } return $headers; diff --git a/src/Http/Adapter/Swoole/Server.php b/src/Http/Adapter/Swoole/Server.php index fff99f1e..bb3b87d8 100755 --- a/src/Http/Adapter/Swoole/Server.php +++ b/src/Http/Adapter/Swoole/Server.php @@ -19,7 +19,8 @@ public function __construct(string $host, ?string $port = null, array $settings { $this->server = new SwooleServer($host, $port); $this->server->set(\array_merge($settings, [ - 'enable_coroutine' => true + 'enable_coroutine' => true, + 'http_parse_cookie' => false, ])); } diff --git a/tests/e2e/BaseTest.php b/tests/e2e/BaseTest.php index 57d04aa6..f95e96cd 100644 --- a/tests/e2e/BaseTest.php +++ b/tests/e2e/BaseTest.php @@ -38,11 +38,26 @@ public function testFile() public function testCookie() { + $cookie = 'cookie1=value1'; $response = $this->client->call(Client::METHOD_GET, '/cookies', [ - 'Cookie: cookie1=value1; cookie2=value2' + 'Cookie: ' . $cookie ]); + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals($cookie, $response['body']); + + $cookie = 'cookie1=value1; cookie2=value2'; + $response = $this->client->call(Client::METHOD_GET, '/cookies', [ + 'Cookie: ' . $cookie + ]); + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals($cookie, $response['body']); + + $cookie = 'cookie1=value1;cookie2=value2'; + $response = $this->client->call(Client::METHOD_GET, '/cookies', [ + 'Cookie: ' . $cookie + ]); $this->assertEquals(200, $response['headers']['status-code']); - $this->assertEquals('cookie1=value1; cookie2=value2', $response['body']); + $this->assertEquals($cookie, $response['body']); } } From abe33048a19edde616c444d3d6f0d425c7eb0555 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Tue, 2 Sep 2025 13:16:19 +0200 Subject: [PATCH 074/103] Logical fixes & new tests --- src/Http/Adapter/Swoole/Request.php | 3 +-- tests/e2e/BaseTest.php | 28 ++++++++++++++++++---------- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/src/Http/Adapter/Swoole/Request.php b/src/Http/Adapter/Swoole/Request.php index f0dd1d66..b07a5fdb 100644 --- a/src/Http/Adapter/Swoole/Request.php +++ b/src/Http/Adapter/Swoole/Request.php @@ -260,9 +260,8 @@ public function getCookie(string $key, string $default = ''): string $cookie = \trim($cookie); [$cookieKey, $cookieValue] = \explode('=', $cookie, 2); $cookieKey = \trim($cookieKey); - $cookieKey = \strtolower($cookieKey); $cookieValue = \trim($cookieValue); - if ($cookieValue === $key) { + if ($cookieKey === $key) { return $cookieValue; } } diff --git a/tests/e2e/BaseTest.php b/tests/e2e/BaseTest.php index f95e96cd..7279ee41 100644 --- a/tests/e2e/BaseTest.php +++ b/tests/e2e/BaseTest.php @@ -38,25 +38,33 @@ public function testFile() public function testCookie() { + // One cookie $cookie = 'cookie1=value1'; - $response = $this->client->call(Client::METHOD_GET, '/cookies', [ - 'Cookie: ' . $cookie - ]); + $response = $this->client->call(Client::METHOD_GET, '/cookies', [ 'Cookie: ' . $cookie ]); $this->assertEquals(200, $response['headers']['status-code']); $this->assertEquals($cookie, $response['body']); + // Two cookiees $cookie = 'cookie1=value1; cookie2=value2'; - $response = $this->client->call(Client::METHOD_GET, '/cookies', [ - 'Cookie: ' . $cookie - ]); + $response = $this->client->call(Client::METHOD_GET, '/cookies', [ 'Cookie: ' . $cookie ]); $this->assertEquals(200, $response['headers']['status-code']); $this->assertEquals($cookie, $response['body']); - + // Two cookies without optional space $cookie = 'cookie1=value1;cookie2=value2'; - $response = $this->client->call(Client::METHOD_GET, '/cookies', [ - 'Cookie: ' . $cookie - ]); + $response = $this->client->call(Client::METHOD_GET, '/cookies', [ 'Cookie: ' . $cookie ]); + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals($cookie, $response['body']); + + // Cookie with "=" in value + $cookie = 'cookie1=value1=value2'; + $response = $this->client->call(Client::METHOD_GET, '/cookies', [ 'Cookie: ' . $cookie ]); + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals($cookie, $response['body']); + + // Case sensitivity for cookie names + $cookie = 'cookie1=v1; Cookie1=v2'; + $response = $this->client->call(Client::METHOD_GET, '/cookies', [ 'Cookie: ' . $cookie ]); $this->assertEquals(200, $response['headers']['status-code']); $this->assertEquals($cookie, $response['body']); } From 5a4d186619d525391d90daca23b68fa54ad3240b Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Wed, 3 Sep 2025 23:56:23 +1200 Subject: [PATCH 075/103] Add support for 422 --- src/Response.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Response.php b/src/Response.php index 0bc4d08d..bd946677 100755 --- a/src/Response.php +++ b/src/Response.php @@ -113,6 +113,8 @@ class Response public const STATUS_CODE_EXPECTATION_FAILED = 417; + public const STATUS_CODE_UNPROCESSABLE_ENTITY = 422; + public const STATUS_CODE_TOO_EARLY = 425; public const STATUS_CODE_TOO_MANY_REQUESTS = 429; From d19da136fc560eaf7c2137c4448809b5c7eeebf3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Wed, 3 Sep 2025 19:05:57 +0200 Subject: [PATCH 076/103] Allow array for headers --- src/Http/Adapter/FPM/Response.php | 12 ++++++++--- src/Http/Adapter/Swoole/Response.php | 4 ++-- src/Http/Response.php | 32 ++++++++++++++++++++++------ tests/e2e/BaseTest.php | 8 +++++++ tests/e2e/Client.php | 27 ++++++++++++++++++++++- tests/e2e/init.php | 9 ++++++++ 6 files changed, 80 insertions(+), 12 deletions(-) diff --git a/src/Http/Adapter/FPM/Response.php b/src/Http/Adapter/FPM/Response.php index 6c877918..6a5d4dd4 100644 --- a/src/Http/Adapter/FPM/Response.php +++ b/src/Http/Adapter/FPM/Response.php @@ -53,12 +53,18 @@ protected function sendStatus(int $statusCode): void * Output Header * * @param string $key - * @param string $value + * @param string|array $value * @return void */ - public function sendHeader(string $key, string $value): void + public function sendHeader(string $key, mixed $value): void { - \header($key.': '.$value); + if (\is_array($value)) { + foreach ($value as $v) { + \header($key.': '.$v, false); + } + } else { + \header($key.': '.$value); + } } /** diff --git a/src/Http/Adapter/Swoole/Response.php b/src/Http/Adapter/Swoole/Response.php index b0b99978..be48a027 100644 --- a/src/Http/Adapter/Swoole/Response.php +++ b/src/Http/Adapter/Swoole/Response.php @@ -65,10 +65,10 @@ protected function sendStatus(int $statusCode): void * Send Header * * @param string $key - * @param string $value + * @param string|array $value * @return void */ - public function sendHeader(string $key, string $value): void + public function sendHeader(string $key, mixed $value): void { $this->swoole->header($key, $value); } diff --git a/src/Http/Response.php b/src/Http/Response.php index d0e6e793..2bc4a5f4 100755 --- a/src/Http/Response.php +++ b/src/Http/Response.php @@ -225,7 +225,7 @@ abstract class Response protected bool $sent = false; /** - * @var array + * @var array> */ protected array $headers = []; @@ -365,7 +365,15 @@ public function enablePayload(): static */ public function addHeader(string $key, string $value): static { - $this->headers[$key] = $value; + if (\array_key_exists($key, $this->headers)) { + if (\is_array($this->headers[$key])) { + $this->headers[$key][] = $value; + } else { + $this->headers[$key] = [$this->headers[$key], $value]; + } + } else { + $this->headers[$key] = $value; + } return $this; } @@ -391,7 +399,7 @@ public function removeHeader(string $key): static * * Return array of all response headers * - * @return array + * @return array> */ public function getHeaders(): array { @@ -483,7 +491,19 @@ public function send(string $body = ''): void if (!$this->disablePayload) { $length = strlen($body); - $this->size = $this->size + strlen(implode("\n", $this->headers)) + $length; + $headersSize = 0; + foreach ($this->headers as $name => $values) { + if (\is_array($values)) { + foreach ($values as $value) { + $headersSize += \strlen($name . ': ' . $value); + } + $headersSize += (\count($values) - 1) * 2; // linebreaks + } else { + $headersSize += \strlen($name . ': ' . $values); + } + } + $headersSize += (\count($this->headers) - 1) * 2; // linebreaks + $this->size = $this->size + $headersSize + $length; if (array_key_exists( $this->contentType, @@ -599,10 +619,10 @@ abstract protected function sendStatus(int $statusCode): void; * Output Header * * @param string $key - * @param string $value + * @param string|array $value * @return void */ - abstract public function sendHeader(string $key, string $value): void; + abstract public function sendHeader(string $key, mixed $value): void; /** * Send Cookie diff --git a/tests/e2e/BaseTest.php b/tests/e2e/BaseTest.php index 7279ee41..04b4249d 100644 --- a/tests/e2e/BaseTest.php +++ b/tests/e2e/BaseTest.php @@ -68,4 +68,12 @@ public function testCookie() $this->assertEquals(200, $response['headers']['status-code']); $this->assertEquals($cookie, $response['body']); } + + public function testSetCookie() + { + $response = $this->client->call(Client::METHOD_GET, '/set-cookie'); + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals('value1', $response['cookies']['key1']); + $this->assertEquals('value2', $response['cookies']['key2']); + } } diff --git a/tests/e2e/Client.php b/tests/e2e/Client.php index 5bf15b2e..42fdd5a8 100644 --- a/tests/e2e/Client.php +++ b/tests/e2e/Client.php @@ -61,6 +61,8 @@ public function call(string $method, string $path = '', array $headers = [], arr $responseType = ''; $responseBody = ''; + $cookies = []; + curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); @@ -68,7 +70,7 @@ public function call(string $method, string $path = '', array $headers = [], arr curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 0); curl_setopt($ch, CURLOPT_TIMEOUT, 15); - curl_setopt($ch, CURLOPT_HEADERFUNCTION, function ($curl, $header) use (&$responseHeaders) { + curl_setopt($ch, CURLOPT_HEADERFUNCTION, function ($curl, $header) use (&$responseHeaders, &$cookies) { $len = strlen($header); $header = explode(':', $header, 2); @@ -76,6 +78,12 @@ public function call(string $method, string $path = '', array $headers = [], arr return $len; } + if (strtolower(trim($header[0])) == 'set-cookie') { + $parsed = $this->parseCookie((string)trim($header[1])); + $name = array_key_first($parsed); + $cookies[$name] = $parsed[$name]; + } + $responseHeaders[strtolower(trim($header[0]))] = trim($header[1]); return $len; @@ -99,6 +107,23 @@ public function call(string $method, string $path = '', array $headers = [], arr return [ 'headers' => $responseHeaders, 'body' => $responseBody, + 'cookies' => $cookies, ]; } + + /** + * Parse Cookie String + * + * @param string $cookie + * @return array + */ + public function parseCookie(string $cookie): array + { + $cookies = []; + + parse_str(strtr($cookie, ['&' => '%26', '+' => '%2B', ';' => '&']), $cookies); + + return $cookies; + } + } diff --git a/tests/e2e/init.php b/tests/e2e/init.php index 2c85bd13..54638fd9 100644 --- a/tests/e2e/init.php +++ b/tests/e2e/init.php @@ -34,6 +34,15 @@ $response->send($request->getHeaders()['cookie'] ?? ''); }); +Http::get('/set-cookie') + ->inject('request') + ->inject('response') + ->action(function (Request $request, Response $response) { + $response->addHeader('Set-Cookie', 'key1=value1'); + $response->addHeader('Set-Cookie', 'key2=value2'); + $response->send('OK'); + }); + Http::get('/chunked') ->inject('response') ->action(function (Response $response) { From 94af4fce7f6bc77d9d80884a8e65c8d9cf09d3f9 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Thu, 4 Sep 2025 09:46:44 +0530 Subject: [PATCH 077/103] feat: add example to params --- src/Hook.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Hook.php b/src/Hook.php index 029eeb50..166852f8 100644 --- a/src/Hook.php +++ b/src/Hook.php @@ -198,9 +198,10 @@ public function inject(string $injection): static * @param array $injections * @param bool $skipValidation * @param bool $deprecated + * @param string $example * @return static */ - public function param(string $key, mixed $default, Validator|callable $validator, string $description = '', bool $optional = false, array $injections = [], bool $skipValidation = false, bool $deprecated = false): static + public function param(string $key, mixed $default, Validator|callable $validator, string $description = '', bool $optional = false, array $injections = [], bool $skipValidation = false, bool $deprecated = false, string $example = ''): static { $this->params[$key] = [ 'default' => $default, @@ -210,6 +211,7 @@ public function param(string $key, mixed $default, Validator|callable $validator 'injections' => $injections, 'skipValidation' => $skipValidation, 'deprecated' => $deprecated, + 'example' => $example, 'value' => null, 'order' => count($this->params) + count($this->injections), ]; From e745e7b2dbb78d69a409c8695ec64ed6c0bbd021 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Sun, 7 Sep 2025 10:55:02 +0200 Subject: [PATCH 078/103] Add array-like headers; sync partially with 0.34.x --- composer.json | 2 +- composer.lock | 215 ++++++++++++++++++++++++++----------- src/Response.php | 101 ++++++++++++++--- tests/e2e/ResponseTest.php | 41 +++++++ tests/e2e/server.php | 16 +++ 5 files changed, 296 insertions(+), 79 deletions(-) diff --git a/composer.json b/composer.json index eb2e9a9f..cf4f01e3 100644 --- a/composer.json +++ b/composer.json @@ -17,7 +17,7 @@ "scripts": { "lint": "vendor/bin/pint --test", "format": "vendor/bin/pint", - "check": "vendor/bin/phpstan analyse -c phpstan.neon", + "check": "vendor/bin/phpstan analyse -c phpstan.neon --memory-limit 512M", "test": "vendor/bin/phpunit --configuration phpunit.xml", "bench": "vendor/bin/phpbench run --report=benchmark" }, diff --git a/composer.lock b/composer.lock index 96f94159..1282e5de 100644 --- a/composer.lock +++ b/composer.lock @@ -8,25 +8,25 @@ "packages": [ { "name": "brick/math", - "version": "0.13.1", + "version": "0.14.0", "source": { "type": "git", "url": "https://github.com/brick/math.git", - "reference": "fc7ed316430118cc7836bf45faff18d5dfc8de04" + "reference": "113a8ee2656b882d4c3164fa31aa6e12cbb7aaa2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/brick/math/zipball/fc7ed316430118cc7836bf45faff18d5dfc8de04", - "reference": "fc7ed316430118cc7836bf45faff18d5dfc8de04", + "url": "https://api.github.com/repos/brick/math/zipball/113a8ee2656b882d4c3164fa31aa6e12cbb7aaa2", + "reference": "113a8ee2656b882d4c3164fa31aa6e12cbb7aaa2", "shasum": "" }, "require": { - "php": "^8.1" + "php": "^8.2" }, "require-dev": { "php-coveralls/php-coveralls": "^2.2", - "phpunit/phpunit": "^10.1", - "vimeo/psalm": "6.8.8" + "phpstan/phpstan": "2.1.22", + "phpunit/phpunit": "^11.5" }, "type": "library", "autoload": { @@ -56,7 +56,7 @@ ], "support": { "issues": "https://github.com/brick/math/issues", - "source": "https://github.com/brick/math/tree/0.13.1" + "source": "https://github.com/brick/math/tree/0.14.0" }, "funding": [ { @@ -64,7 +64,7 @@ "type": "github" } ], - "time": "2025-03-29T13:50:30+00:00" + "time": "2025-08-29T12:40:03+00:00" }, { "name": "composer/semver", @@ -336,16 +336,16 @@ }, { "name": "open-telemetry/api", - "version": "1.4.0", + "version": "1.5.0", "source": { "type": "git", "url": "https://github.com/opentelemetry-php/api.git", - "reference": "b3a9286f9c1c8247c83493c5b1fa475cd0cec7f7" + "reference": "7692075f486c14d8cfd37fba98a08a5667f089e5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/opentelemetry-php/api/zipball/b3a9286f9c1c8247c83493c5b1fa475cd0cec7f7", - "reference": "b3a9286f9c1c8247c83493c5b1fa475cd0cec7f7", + "url": "https://api.github.com/repos/opentelemetry-php/api/zipball/7692075f486c14d8cfd37fba98a08a5667f089e5", + "reference": "7692075f486c14d8cfd37fba98a08a5667f089e5", "shasum": "" }, "require": { @@ -402,7 +402,7 @@ "issues": "https://github.com/open-telemetry/opentelemetry-php/issues", "source": "https://github.com/open-telemetry/opentelemetry-php" }, - "time": "2025-06-19T23:36:51+00:00" + "time": "2025-08-07T23:07:38+00:00" }, { "name": "open-telemetry/context", @@ -592,22 +592,22 @@ }, { "name": "open-telemetry/sdk", - "version": "1.7.0", + "version": "1.7.1", "source": { "type": "git", "url": "https://github.com/opentelemetry-php/sdk.git", - "reference": "86287cf30fd6549444d7b8f7d8758d92e24086ac" + "reference": "52690d4b37ae4f091af773eef3c238ed2bc0aa06" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/opentelemetry-php/sdk/zipball/86287cf30fd6549444d7b8f7d8758d92e24086ac", - "reference": "86287cf30fd6549444d7b8f7d8758d92e24086ac", + "url": "https://api.github.com/repos/opentelemetry-php/sdk/zipball/52690d4b37ae4f091af773eef3c238ed2bc0aa06", + "reference": "52690d4b37ae4f091af773eef3c238ed2bc0aa06", "shasum": "" }, "require": { "ext-json": "*", "nyholm/psr7-server": "^1.1", - "open-telemetry/api": "~1.4.0", + "open-telemetry/api": "^1.4", "open-telemetry/context": "^1.0", "open-telemetry/sem-conv": "^1.0", "php": "^8.1", @@ -685,20 +685,20 @@ "issues": "https://github.com/open-telemetry/opentelemetry-php/issues", "source": "https://github.com/open-telemetry/opentelemetry-php" }, - "time": "2025-08-06T03:07:06+00:00" + "time": "2025-09-05T07:17:06+00:00" }, { "name": "open-telemetry/sem-conv", - "version": "1.36.0", + "version": "1.37.0", "source": { "type": "git", "url": "https://github.com/opentelemetry-php/sem-conv.git", - "reference": "60dd18fd21d45e6f4234ecab89c14021b6e3de9a" + "reference": "8da7ec497c881e39afa6657d72586e27efbd29a1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/opentelemetry-php/sem-conv/zipball/60dd18fd21d45e6f4234ecab89c14021b6e3de9a", - "reference": "60dd18fd21d45e6f4234ecab89c14021b6e3de9a", + "url": "https://api.github.com/repos/opentelemetry-php/sem-conv/zipball/8da7ec497c881e39afa6657d72586e27efbd29a1", + "reference": "8da7ec497c881e39afa6657d72586e27efbd29a1", "shasum": "" }, "require": { @@ -742,7 +742,7 @@ "issues": "https://github.com/open-telemetry/opentelemetry-php/issues", "source": "https://github.com/open-telemetry/opentelemetry-php" }, - "time": "2025-08-04T03:22:08+00:00" + "time": "2025-09-03T12:08:10+00:00" }, { "name": "php-http/discovery", @@ -1164,20 +1164,20 @@ }, { "name": "ramsey/uuid", - "version": "4.9.0", + "version": "4.9.1", "source": { "type": "git", "url": "https://github.com/ramsey/uuid.git", - "reference": "4e0e23cc785f0724a0e838279a9eb03f28b092a0" + "reference": "81f941f6f729b1e3ceea61d9d014f8b6c6800440" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ramsey/uuid/zipball/4e0e23cc785f0724a0e838279a9eb03f28b092a0", - "reference": "4e0e23cc785f0724a0e838279a9eb03f28b092a0", + "url": "https://api.github.com/repos/ramsey/uuid/zipball/81f941f6f729b1e3ceea61d9d014f8b6c6800440", + "reference": "81f941f6f729b1e3ceea61d9d014f8b6c6800440", "shasum": "" }, "require": { - "brick/math": "^0.8.8 || ^0.9 || ^0.10 || ^0.11 || ^0.12 || ^0.13", + "brick/math": "^0.8.8 || ^0.9 || ^0.10 || ^0.11 || ^0.12 || ^0.13 || ^0.14", "php": "^8.0", "ramsey/collection": "^1.2 || ^2.0" }, @@ -1236,9 +1236,9 @@ ], "support": { "issues": "https://github.com/ramsey/uuid/issues", - "source": "https://github.com/ramsey/uuid/tree/4.9.0" + "source": "https://github.com/ramsey/uuid/tree/4.9.1" }, - "time": "2025-06-25T14:20:11+00:00" + "time": "2025-09-04T20:59:21+00:00" }, { "name": "symfony/deprecation-contracts", @@ -1309,16 +1309,16 @@ }, { "name": "symfony/http-client", - "version": "v7.3.2", + "version": "v7.3.3", "source": { "type": "git", "url": "https://github.com/symfony/http-client.git", - "reference": "1c064a0c67749923483216b081066642751cc2c7" + "reference": "333b9bd7639cbdaecd25a3a48a9d2dcfaa86e019" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-client/zipball/1c064a0c67749923483216b081066642751cc2c7", - "reference": "1c064a0c67749923483216b081066642751cc2c7", + "url": "https://api.github.com/repos/symfony/http-client/zipball/333b9bd7639cbdaecd25a3a48a9d2dcfaa86e019", + "reference": "333b9bd7639cbdaecd25a3a48a9d2dcfaa86e019", "shasum": "" }, "require": { @@ -1326,6 +1326,7 @@ "psr/log": "^1|^2|^3", "symfony/deprecation-contracts": "^2.5|^3", "symfony/http-client-contracts": "~3.4.4|^3.5.2", + "symfony/polyfill-php83": "^1.29", "symfony/service-contracts": "^2.5|^3" }, "conflict": { @@ -1384,7 +1385,7 @@ "http" ], "support": { - "source": "https://github.com/symfony/http-client/tree/v7.3.2" + "source": "https://github.com/symfony/http-client/tree/v7.3.3" }, "funding": [ { @@ -1404,7 +1405,7 @@ "type": "tidelift" } ], - "time": "2025-07-15T11:36:08+00:00" + "time": "2025-08-27T07:45:05+00:00" }, { "name": "symfony/http-client-contracts", @@ -1649,6 +1650,86 @@ ], "time": "2024-09-09T11:45:10+00:00" }, + { + "name": "symfony/polyfill-php83", + "version": "v1.33.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php83.git", + "reference": "17f6f9a6b1735c0f163024d959f700cfbc5155e5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/17f6f9a6b1735c0f163024d959f700cfbc5155e5", + "reference": "17f6f9a6b1735c0f163024d959f700cfbc5155e5", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php83\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.3+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php83/tree/v1.33.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-07-08T02:45:35+00:00" + }, { "name": "symfony/service-contracts", "version": "v3.6.0", @@ -4161,16 +4242,16 @@ }, { "name": "symfony/console", - "version": "v7.3.2", + "version": "v7.3.3", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "5f360ebc65c55265a74d23d7fe27f957870158a1" + "reference": "cb0102a1c5ac3807cf3fdf8bea96007df7fdbea7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/5f360ebc65c55265a74d23d7fe27f957870158a1", - "reference": "5f360ebc65c55265a74d23d7fe27f957870158a1", + "url": "https://api.github.com/repos/symfony/console/zipball/cb0102a1c5ac3807cf3fdf8bea96007df7fdbea7", + "reference": "cb0102a1c5ac3807cf3fdf8bea96007df7fdbea7", "shasum": "" }, "require": { @@ -4235,7 +4316,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v7.3.2" + "source": "https://github.com/symfony/console/tree/v7.3.3" }, "funding": [ { @@ -4255,7 +4336,7 @@ "type": "tidelift" } ], - "time": "2025-07-30T17:13:41+00:00" + "time": "2025-08-25T06:35:40+00:00" }, { "name": "symfony/filesystem", @@ -4397,16 +4478,16 @@ }, { "name": "symfony/options-resolver", - "version": "v7.3.2", + "version": "v7.3.3", "source": { "type": "git", "url": "https://github.com/symfony/options-resolver.git", - "reference": "119bcf13e67dbd188e5dbc74228b1686f66acd37" + "reference": "0ff2f5c3df08a395232bbc3c2eb7e84912df911d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/options-resolver/zipball/119bcf13e67dbd188e5dbc74228b1686f66acd37", - "reference": "119bcf13e67dbd188e5dbc74228b1686f66acd37", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/0ff2f5c3df08a395232bbc3c2eb7e84912df911d", + "reference": "0ff2f5c3df08a395232bbc3c2eb7e84912df911d", "shasum": "" }, "require": { @@ -4444,7 +4525,7 @@ "options" ], "support": { - "source": "https://github.com/symfony/options-resolver/tree/v7.3.2" + "source": "https://github.com/symfony/options-resolver/tree/v7.3.3" }, "funding": [ { @@ -4464,7 +4545,7 @@ "type": "tidelift" } ], - "time": "2025-07-15T11:36:08+00:00" + "time": "2025-08-05T10:16:07+00:00" }, { "name": "symfony/polyfill-ctype", @@ -4718,16 +4799,16 @@ }, { "name": "symfony/process", - "version": "v7.3.0", + "version": "v7.3.3", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "40c295f2deb408d5e9d2d32b8ba1dd61e36f05af" + "reference": "32241012d521e2e8a9d713adb0812bb773b907f1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/40c295f2deb408d5e9d2d32b8ba1dd61e36f05af", - "reference": "40c295f2deb408d5e9d2d32b8ba1dd61e36f05af", + "url": "https://api.github.com/repos/symfony/process/zipball/32241012d521e2e8a9d713adb0812bb773b907f1", + "reference": "32241012d521e2e8a9d713adb0812bb773b907f1", "shasum": "" }, "require": { @@ -4759,7 +4840,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v7.3.0" + "source": "https://github.com/symfony/process/tree/v7.3.3" }, "funding": [ { @@ -4770,25 +4851,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-04-17T09:11:12+00:00" + "time": "2025-08-18T09:42:54+00:00" }, { "name": "symfony/string", - "version": "v7.3.2", + "version": "v7.3.3", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "42f505aff654e62ac7ac2ce21033818297ca89ca" + "reference": "17a426cce5fd1f0901fefa9b2a490d0038fd3c9c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/42f505aff654e62ac7ac2ce21033818297ca89ca", - "reference": "42f505aff654e62ac7ac2ce21033818297ca89ca", + "url": "https://api.github.com/repos/symfony/string/zipball/17a426cce5fd1f0901fefa9b2a490d0038fd3c9c", + "reference": "17a426cce5fd1f0901fefa9b2a490d0038fd3c9c", "shasum": "" }, "require": { @@ -4846,7 +4931,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v7.3.2" + "source": "https://github.com/symfony/string/tree/v7.3.3" }, "funding": [ { @@ -4866,7 +4951,7 @@ "type": "tidelift" } ], - "time": "2025-07-10T08:47:49+00:00" + "time": "2025-08-25T06:35:40+00:00" }, { "name": "theseer/tokenizer", @@ -4970,12 +5055,12 @@ ], "aliases": [], "minimum-stability": "stable", - "stability-flags": {}, + "stability-flags": [], "prefer-stable": false, "prefer-lowest": false, "platform": { "php": ">=8.1" }, - "platform-dev": {}, - "plugin-api-version": "2.6.0" + "platform-dev": [], + "plugin-api-version": "2.3.0" } diff --git a/src/Response.php b/src/Response.php index bd946677..ee0aad9a 100755 --- a/src/Response.php +++ b/src/Response.php @@ -47,6 +47,10 @@ class Response public const STATUS_CODE_SWITCHING_PROTOCOLS = 101; + public const STATUS_CODE_PROCESSING = 102; + + public const STATUS_CODE_EARLY_HINTS = 103; + public const STATUS_CODE_OK = 200; public const STATUS_CODE_CREATED = 201; @@ -61,6 +65,12 @@ class Response public const STATUS_CODE_PARTIALCONTENT = 206; + public const STATUS_CODE_MULTI_STATUS = 207; + + public const STATUS_CODE_ALREADY_REPORTED = 208; + + public const STATUS_CODE_IM_USED = 226; + public const STATUS_CODE_MULTIPLE_CHOICES = 300; public const STATUS_CODE_MOVED_PERMANENTLY = 301; @@ -77,6 +87,8 @@ class Response public const STATUS_CODE_TEMPORARY_REDIRECT = 307; + public const STATUS_CODE_PERMANENT_REDIRECT = 308; + public const STATUS_CODE_BAD_REQUEST = 400; public const STATUS_CODE_UNAUTHORIZED = 401; @@ -113,12 +125,26 @@ class Response public const STATUS_CODE_EXPECTATION_FAILED = 417; + public const STATUS_CODE_IM_A_TEAPOT = 418; + + public const STATUS_CODE_MISDIRECTED_REQUEST = 421; + public const STATUS_CODE_UNPROCESSABLE_ENTITY = 422; + public const STATUS_CODE_LOCKED = 423; + + public const STATUS_CODE_FAILED_DEPENDENCY = 424; + public const STATUS_CODE_TOO_EARLY = 425; + public const STATUS_CODE_UPGRADE_REQUIRED = 426; + + public const STATUS_CODE_PRECONDITION_REQUIRED = 428; + public const STATUS_CODE_TOO_MANY_REQUESTS = 429; + public const STATUS_CODE_REQUEST_HEADER_FIELDS_TOO_LARGE = 431; + public const STATUS_CODE_UNAVAILABLE_FOR_LEGAL_REASONS = 451; public const STATUS_CODE_INTERNAL_SERVER_ERROR = 500; @@ -133,12 +159,24 @@ class Response public const STATUS_CODE_HTTP_VERSION_NOT_SUPPORTED = 505; + public const STATUS_CODE_VARIANT_ALSO_NEGOTIATES = 506; + + public const STATUS_CODE_INSUFFICIENT_STORAGE = 507; + + public const STATUS_CODE_LOOP_DETECTED = 508; + + public const STATUS_CODE_NOT_EXTENDED = 510; + + public const STATUS_CODE_NETWORK_AUTHENTICATION_REQUIRED = 511; + /** * @var array */ protected $statusCodes = [ self::STATUS_CODE_CONTINUE => 'Continue', self::STATUS_CODE_SWITCHING_PROTOCOLS => 'Switching Protocols', + self::STATUS_CODE_PROCESSING => 'Processing', + self::STATUS_CODE_EARLY_HINTS => 'Early Hints', self::STATUS_CODE_OK => 'OK', self::STATUS_CODE_CREATED => 'Created', self::STATUS_CODE_ACCEPTED => 'Accepted', @@ -146,6 +184,9 @@ class Response self::STATUS_CODE_NOCONTENT => 'No Content', self::STATUS_CODE_RESETCONTENT => 'Reset Content', self::STATUS_CODE_PARTIALCONTENT => 'Partial Content', + self::STATUS_CODE_MULTI_STATUS => 'Multi-Status', + self::STATUS_CODE_ALREADY_REPORTED => 'Already Reported', + self::STATUS_CODE_IM_USED => 'IM Used', self::STATUS_CODE_MULTIPLE_CHOICES => 'Multiple Choices', self::STATUS_CODE_MOVED_PERMANENTLY => 'Moved Permanently', self::STATUS_CODE_FOUND => 'Found', @@ -154,6 +195,7 @@ class Response self::STATUS_CODE_USE_PROXY => 'Use Proxy', self::STATUS_CODE_UNUSED => '(Unused)', self::STATUS_CODE_TEMPORARY_REDIRECT => 'Temporary Redirect', + self::STATUS_CODE_PERMANENT_REDIRECT => 'Permanent Redirect', self::STATUS_CODE_BAD_REQUEST => 'Bad Request', self::STATUS_CODE_UNAUTHORIZED => 'Unauthorized', self::STATUS_CODE_PAYMENT_REQUIRED => 'Payment Required', @@ -172,8 +214,16 @@ class Response self::STATUS_CODE_UNSUPPORTED_MEDIA_TYPE => 'Unsupported Media Type', self::STATUS_CODE_REQUESTED_RANGE_NOT_SATISFIABLE => 'Requested Range Not Satisfiable', self::STATUS_CODE_EXPECTATION_FAILED => 'Expectation Failed', + self::STATUS_CODE_IM_A_TEAPOT => 'I\'m a teapot', + self::STATUS_CODE_MISDIRECTED_REQUEST => 'Misdirected Request', + self::STATUS_CODE_UNPROCESSABLE_ENTITY => 'Unprocessable Entity', + self::STATUS_CODE_LOCKED => 'Locked', + self::STATUS_CODE_FAILED_DEPENDENCY => 'Failed Dependency', self::STATUS_CODE_TOO_EARLY => 'Too Early', + self::STATUS_CODE_UPGRADE_REQUIRED => 'Upgrade Required', + self::STATUS_CODE_PRECONDITION_REQUIRED => 'Precondition Required', self::STATUS_CODE_TOO_MANY_REQUESTS => 'Too Many Requests', + self::STATUS_CODE_REQUEST_HEADER_FIELDS_TOO_LARGE => 'Request Header Fields Too Large', self::STATUS_CODE_UNAVAILABLE_FOR_LEGAL_REASONS => 'Unavailable For Legal Reasons', self::STATUS_CODE_INTERNAL_SERVER_ERROR => 'Internal Server Error', self::STATUS_CODE_NOT_IMPLEMENTED => 'Not Implemented', @@ -181,10 +231,15 @@ class Response self::STATUS_CODE_SERVICE_UNAVAILABLE => 'Service Unavailable', self::STATUS_CODE_GATEWAY_TIMEOUT => 'Gateway Timeout', self::STATUS_CODE_HTTP_VERSION_NOT_SUPPORTED => 'HTTP Version Not Supported', + self::STATUS_CODE_VARIANT_ALSO_NEGOTIATES => 'Variant Also Negotiates', + self::STATUS_CODE_INSUFFICIENT_STORAGE => 'Insufficient Storage', + self::STATUS_CODE_LOOP_DETECTED => 'Loop Detected', + self::STATUS_CODE_NOT_EXTENDED => 'Not Extended', + self::STATUS_CODE_NETWORK_AUTHENTICATION_REQUIRED => 'Network Authentication Required', ]; /** - * Mime Types with compressible content + * Mime Types with compression support * * @var array */ @@ -284,7 +339,7 @@ class Response protected bool $sent = false; /** - * @var array + * @var array> */ protected array $headers = []; @@ -488,7 +543,15 @@ public function enablePayload(): static */ public function addHeader(string $key, string $value): static { - $this->headers[$key] = $value; + if (\array_key_exists($key, $this->headers)) { + if (\is_array($this->headers[$key])) { + $this->headers[$key][] = $value; + } else { + $this->headers[$key] = [$this->headers[$key], $value]; + } + } else { + $this->headers[$key] = $value; + } return $this; } @@ -514,7 +577,7 @@ public function removeHeader(string $key): static * * Return array of all response headers * - * @return array + * @return array> */ public function getHeaders(): array { @@ -527,13 +590,13 @@ public function getHeaders(): array * Add an HTTP cookie to response header * * @param string $name - * @param string $value - * @param int $expire - * @param string $path - * @param string $domain - * @param bool $secure - * @param bool $httponly - * @param string $sameSite + * @param string|null $value + * @param int|null $expire + * @param string|null $path + * @param string|null $domain + * @param bool|null $secure + * @param bool|null $httponly + * @param string|null $sameSite */ public function addCookie(string $name, ?string $value = null, ?int $expire = null, ?string $path = null, ?string $domain = null, ?bool $secure = null, ?bool $httponly = null, ?string $sameSite = null): static { @@ -627,9 +690,21 @@ public function send(string $body = ''): void return; } - $headerSize = strlen(implode("\n", $this->headers)); + $headersSize = 0; + foreach ($this->headers as $name => $values) { + if (\is_array($values)) { + foreach ($values as $value) { + $headersSize += \strlen($name . ': ' . $value); + } + $headersSize += (\count($values) - 1) * 2; // linebreaks + } else { + $headersSize += \strlen($name . ': ' . $values); + } + } + $headersSize += (\count($this->headers) - 1) * 2; // linebreaks + $bodyLength = strlen($body); - $this->size += $headerSize + $bodyLength; + $this->size += $headersSize + $bodyLength; if ($bodyLength <= self::CHUNK_SIZE) { $this->end($body); diff --git a/tests/e2e/ResponseTest.php b/tests/e2e/ResponseTest.php index 0a8d70ac..1b31d374 100644 --- a/tests/e2e/ResponseTest.php +++ b/tests/e2e/ResponseTest.php @@ -122,4 +122,45 @@ public function testDoubleSlash() $this->assertEquals(200, $response['headers']['status-code']); $this->assertEmpty($response['body']); } + + public function testCookie() + { + // One cookie + $cookie = 'cookie1=value1'; + $response = $this->client->call(Client::METHOD_GET, '/cookies', [ 'Cookie: ' . $cookie ]); + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals($cookie, $response['body']); + + // Two cookiees + $cookie = 'cookie1=value1; cookie2=value2'; + $response = $this->client->call(Client::METHOD_GET, '/cookies', [ 'Cookie: ' . $cookie ]); + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals($cookie, $response['body']); + + // Two cookies without optional space + $cookie = 'cookie1=value1;cookie2=value2'; + $response = $this->client->call(Client::METHOD_GET, '/cookies', [ 'Cookie: ' . $cookie ]); + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals($cookie, $response['body']); + + // Cookie with "=" in value + $cookie = 'cookie1=value1=value2'; + $response = $this->client->call(Client::METHOD_GET, '/cookies', [ 'Cookie: ' . $cookie ]); + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals($cookie, $response['body']); + + // Case sensitivity for cookie names + $cookie = 'cookie1=v1; Cookie1=v2'; + $response = $this->client->call(Client::METHOD_GET, '/cookies', [ 'Cookie: ' . $cookie ]); + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals($cookie, $response['body']); + } + + public function testSetCookie() + { + $response = $this->client->call(Client::METHOD_GET, '/set-cookie'); + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals('value1', $response['cookies']['key1']); + $this->assertEquals('value2', $response['cookies']['key2']); + } } diff --git a/tests/e2e/server.php b/tests/e2e/server.php index 179767cd..0a6641ad 100644 --- a/tests/e2e/server.php +++ b/tests/e2e/server.php @@ -26,6 +26,22 @@ $response->send($value); }); +App::get('/cookies') + ->inject('request') + ->inject('response') + ->action(function (Request $request, Response $response) { + $response->send($request->getHeaders()['cookie'] ?? ''); + }); + +App::get('/set-cookie') + ->inject('request') + ->inject('response') + ->action(function (Request $request, Response $response) { + $response->addHeader('Set-Cookie', 'key1=value1'); + $response->addHeader('Set-Cookie', 'key2=value2'); + $response->send('OK'); + }); + App::get('/chunked') ->inject('response') ->action(function (Response $response) { From a772a756f5b385182a2475e2b1aa6be619454dd8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Sun, 7 Sep 2025 10:58:04 +0200 Subject: [PATCH 079/103] more sync with 0.34.x --- src/Response.php | 12 +++++++++--- tests/e2e/Client.php | 26 +++++++++++++++++++++++++- 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/src/Response.php b/src/Response.php index ee0aad9a..adcfc028 100755 --- a/src/Response.php +++ b/src/Response.php @@ -827,12 +827,18 @@ protected function sendStatus(int $statusCode): void * Output Header * * @param string $key - * @param string $value + * @param string|array $value * @return void */ - protected function sendHeader(string $key, string $value): void + protected function sendHeader(string $key, mixed $value): void { - \header($key.': '.$value); + if (\is_array($value)) { + foreach ($value as $v) { + \header($key.': '.$v, false); + } + } else { + \header($key.': '.$value); + } } /** diff --git a/tests/e2e/Client.php b/tests/e2e/Client.php index cedd4cf8..03bc1e29 100644 --- a/tests/e2e/Client.php +++ b/tests/e2e/Client.php @@ -61,6 +61,8 @@ public function call(string $method, string $path = '', array $headers = [], arr $responseType = ''; $responseBody = ''; + $cookies = []; + $query = match ($headers['content-type'] ?? '') { 'application/json' => \json_encode($params), 'text/plain' => $params, @@ -85,7 +87,7 @@ public function call(string $method, string $path = '', array $headers = [], arr curl_setopt($ch, CURLOPT_HTTPHEADER, $formattedHeaders); curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 0); curl_setopt($ch, CURLOPT_TIMEOUT, 15); - curl_setopt($ch, CURLOPT_HEADERFUNCTION, function ($curl, $header) use (&$responseHeaders) { + curl_setopt($ch, CURLOPT_HEADERFUNCTION, function ($curl, $header) use (&$responseHeaders, &$cookies) { $len = strlen($header); $header = explode(':', $header, 2); @@ -93,6 +95,12 @@ public function call(string $method, string $path = '', array $headers = [], arr return $len; } + if (strtolower(trim($header[0])) == 'set-cookie') { + $parsed = $this->parseCookie((string)trim($header[1])); + $name = array_key_first($parsed); + $cookies[$name] = $parsed[$name]; + } + $responseHeaders[strtolower(trim($header[0]))] = trim($header[1]); return $len; @@ -120,6 +128,22 @@ public function call(string $method, string $path = '', array $headers = [], arr return [ 'headers' => $responseHeaders, 'body' => $responseBody, + 'cookies' => $cookies, ]; } + + /** + * Parse Cookie String + * + * @param string $cookie + * @return array + */ + public function parseCookie(string $cookie): array + { + $cookies = []; + + parse_str(strtr($cookie, ['&' => '%26', '+' => '%2B', ';' => '&']), $cookies); + + return $cookies; + } } From 3ef6341ee6376eee1c2be82271349c82beaffbea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Sun, 7 Sep 2025 11:01:36 +0200 Subject: [PATCH 080/103] Fix tests --- tests/e2e/ResponseTest.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/e2e/ResponseTest.php b/tests/e2e/ResponseTest.php index 1b31d374..ccb734b5 100644 --- a/tests/e2e/ResponseTest.php +++ b/tests/e2e/ResponseTest.php @@ -127,31 +127,31 @@ public function testCookie() { // One cookie $cookie = 'cookie1=value1'; - $response = $this->client->call(Client::METHOD_GET, '/cookies', [ 'Cookie: ' . $cookie ]); + $response = $this->client->call(Client::METHOD_GET, '/cookies', [ 'Cookie' => $cookie ]); $this->assertEquals(200, $response['headers']['status-code']); $this->assertEquals($cookie, $response['body']); // Two cookiees $cookie = 'cookie1=value1; cookie2=value2'; - $response = $this->client->call(Client::METHOD_GET, '/cookies', [ 'Cookie: ' . $cookie ]); + $response = $this->client->call(Client::METHOD_GET, '/cookies', [ 'Cookie' => $cookie ]); $this->assertEquals(200, $response['headers']['status-code']); $this->assertEquals($cookie, $response['body']); // Two cookies without optional space $cookie = 'cookie1=value1;cookie2=value2'; - $response = $this->client->call(Client::METHOD_GET, '/cookies', [ 'Cookie: ' . $cookie ]); + $response = $this->client->call(Client::METHOD_GET, '/cookies', [ 'Cookie' => $cookie ]); $this->assertEquals(200, $response['headers']['status-code']); $this->assertEquals($cookie, $response['body']); // Cookie with "=" in value $cookie = 'cookie1=value1=value2'; - $response = $this->client->call(Client::METHOD_GET, '/cookies', [ 'Cookie: ' . $cookie ]); + $response = $this->client->call(Client::METHOD_GET, '/cookies', [ 'Cookie' => $cookie ]); $this->assertEquals(200, $response['headers']['status-code']); $this->assertEquals($cookie, $response['body']); // Case sensitivity for cookie names $cookie = 'cookie1=v1; Cookie1=v2'; - $response = $this->client->call(Client::METHOD_GET, '/cookies', [ 'Cookie: ' . $cookie ]); + $response = $this->client->call(Client::METHOD_GET, '/cookies', [ 'Cookie' => $cookie ]); $this->assertEquals(200, $response['headers']['status-code']); $this->assertEquals($cookie, $response['body']); } From 190d2d2d9b86b3a8213169cb441f2da619cf8c51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Sun, 7 Sep 2025 11:54:27 +0200 Subject: [PATCH 081/103] Fix header override --- src/Response.php | 26 ++++++++++++++++---------- tests/e2e/ResponseTest.php | 5 +++++ tests/e2e/server.php | 9 +++++++++ 3 files changed, 30 insertions(+), 10 deletions(-) diff --git a/src/Response.php b/src/Response.php index adcfc028..6379cdaf 100755 --- a/src/Response.php +++ b/src/Response.php @@ -540,9 +540,15 @@ public function enablePayload(): static * * @param string $key * @param string $value + * @param bool $override */ - public function addHeader(string $key, string $value): static + public function addHeader(string $key, string $value, bool $override = false): static { + if ($override) { + $this->headers[$key] = $value; + return $this; + } + if (\array_key_exists($key, $this->headers)) { if (\is_array($this->headers[$key])) { $this->headers[$key][] = $value; @@ -659,7 +665,7 @@ public function send(string $body = ''): void } $serverHeader = $this->headers['Server'] ?? 'Utopia/Http'; - $this->addHeader('Server', $serverHeader); + $this->addHeader('Server', $serverHeader, override: true); $this->appendCookies(); @@ -674,14 +680,14 @@ public function send(string $body = ''): void if ($algorithm) { $body = $algorithm->compress($body); - $this->addHeader('Content-Length', (string) \strlen($body)); - $this->addHeader('Content-Encoding', $algorithm->getContentEncoding()); - $this->addHeader('X-Utopia-Compression', 'true'); - $this->addHeader('Vary', 'Accept-Encoding'); + $this->addHeader('Content-Length', (string) \strlen($body), override: true); + $this->addHeader('Content-Encoding', $algorithm->getContentEncoding(), override: true); + $this->addHeader('X-Utopia-Compression', 'true', override: true); + $this->addHeader('Vary', 'Accept-Encoding', override: true); } } - $this->addHeader('X-Debug-Speed', (string) (microtime(true) - $this->startTime)); + $this->addHeader('X-Debug-Speed', (string) (microtime(true) - $this->startTime), override: true); $this->appendHeaders(); // Send response @@ -769,7 +775,7 @@ public function chunk(string $body = '', bool $end = false): void $this->sent = true; } - $this->addHeader('X-Debug-Speed', (string) (microtime(true) - $this->startTime)); + $this->addHeader('X-Debug-Speed', (string) (microtime(true) - $this->startTime), override: true); $this ->appendCookies() @@ -799,7 +805,7 @@ protected function appendHeaders(): static // Send content type header if (!empty($this->contentType)) { - $this->addHeader('Content-Type', $this->contentType); + $this->addHeader('Content-Type', $this->contentType, override: true); } // Set application headers @@ -908,7 +914,7 @@ public function redirect(string $url, int $statusCode = 301): void } $this - ->addHeader('Location', $url) + ->addHeader('Location', $url, override: true) ->setStatusCode($statusCode) ->send(''); } diff --git a/tests/e2e/ResponseTest.php b/tests/e2e/ResponseTest.php index ccb734b5..9db080ee 100644 --- a/tests/e2e/ResponseTest.php +++ b/tests/e2e/ResponseTest.php @@ -162,5 +162,10 @@ public function testSetCookie() $this->assertEquals(200, $response['headers']['status-code']); $this->assertEquals('value1', $response['cookies']['key1']); $this->assertEquals('value2', $response['cookies']['key2']); + + $response = $this->client->call(Client::METHOD_GET, '/set-cookie-override'); + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertArrayNotHasKey('key1', $response['cookies']); + $this->assertEquals('value2', $response['cookies']['key2']); } } diff --git a/tests/e2e/server.php b/tests/e2e/server.php index 0a6641ad..3cec25d2 100644 --- a/tests/e2e/server.php +++ b/tests/e2e/server.php @@ -42,6 +42,15 @@ $response->send('OK'); }); +App::get('/set-cookie-override') + ->inject('request') + ->inject('response') + ->action(function (Request $request, Response $response) { + $response->addHeader('Set-Cookie', 'key1=value1', override: true); + $response->addHeader('Set-Cookie', 'key2=value2', override: true); + $response->send('OK'); + }); + App::get('/chunked') ->inject('response') ->action(function (Response $response) { From f055a719b8e43e10f3dca8e876f7d70974846a2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Sun, 7 Sep 2025 12:04:58 +0200 Subject: [PATCH 082/103] Fix header case sensitivity --- src/Response.php | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/Response.php b/src/Response.php index 6379cdaf..3f76a86e 100755 --- a/src/Response.php +++ b/src/Response.php @@ -669,9 +669,17 @@ public function send(string $body = ''): void $this->appendCookies(); + $hasContentEncoding = false; + foreach ($this->headers as $name => $values) { + if (\strtolower($name) === 'content-encoding') { + $hasContentEncoding = true; + break; + } + } + // Compress body only if all conditions are met: if ( - empty($this->headers['content-encoding']) && + !$hasContentEncoding && !empty($this->acceptEncoding) && $this->isCompressible($this->contentType) && strlen($body) > $this->compressionMinSize From f8e8a9d9e5d5f86791d5a75a220717db28a1455b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Sun, 7 Sep 2025 20:36:06 +0200 Subject: [PATCH 083/103] Fix override default (backwards compatibility) --- src/Response.php | 2 +- tests/e2e/ResponseTest.php | 6 +++--- tests/e2e/server.php | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Response.php b/src/Response.php index 3f76a86e..6eb39b11 100755 --- a/src/Response.php +++ b/src/Response.php @@ -542,7 +542,7 @@ public function enablePayload(): static * @param string $value * @param bool $override */ - public function addHeader(string $key, string $value, bool $override = false): static + public function addHeader(string $key, string $value, bool $override = true): static { if ($override) { $this->headers[$key] = $value; diff --git a/tests/e2e/ResponseTest.php b/tests/e2e/ResponseTest.php index 9db080ee..b51755b5 100644 --- a/tests/e2e/ResponseTest.php +++ b/tests/e2e/ResponseTest.php @@ -160,12 +160,12 @@ public function testSetCookie() { $response = $this->client->call(Client::METHOD_GET, '/set-cookie'); $this->assertEquals(200, $response['headers']['status-code']); - $this->assertEquals('value1', $response['cookies']['key1']); + $this->assertArrayNotHasKey('key1', $response['cookies']); $this->assertEquals('value2', $response['cookies']['key2']); - $response = $this->client->call(Client::METHOD_GET, '/set-cookie-override'); + $response = $this->client->call(Client::METHOD_GET, '/set-cookie-no-override'); $this->assertEquals(200, $response['headers']['status-code']); - $this->assertArrayNotHasKey('key1', $response['cookies']); + $this->assertEquals('value1', $response['cookies']['key1']); $this->assertEquals('value2', $response['cookies']['key2']); } } diff --git a/tests/e2e/server.php b/tests/e2e/server.php index 3cec25d2..c7dae347 100644 --- a/tests/e2e/server.php +++ b/tests/e2e/server.php @@ -42,12 +42,12 @@ $response->send('OK'); }); -App::get('/set-cookie-override') +App::get('/set-cookie-no-override') ->inject('request') ->inject('response') ->action(function (Request $request, Response $response) { - $response->addHeader('Set-Cookie', 'key1=value1', override: true); - $response->addHeader('Set-Cookie', 'key2=value2', override: true); + $response->addHeader('Set-Cookie', 'key1=value1', override: false); + $response->addHeader('Set-Cookie', 'key2=value2', override: false); $response->send('OK'); }); From 812c69f994f7376a23adc0a9373d268299343a49 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Thu, 25 Sep 2025 13:55:25 +0530 Subject: [PATCH 084/103] chore: update error handler had an error to print stacktrace --- src/App.php | 10 ++++----- tests/AppTest.php | 53 ++++++++++++++++++++++++++--------------------- 2 files changed, 34 insertions(+), 29 deletions(-) diff --git a/src/App.php b/src/App.php index d041bc3b..1302d35a 100755 --- a/src/App.php +++ b/src/App.php @@ -642,7 +642,7 @@ public function execute(Route $route, Request $request, Response $response): sta $arguments = $this->getArguments($error, $pathValues, $request->getParams()); \call_user_func_array($error->getAction(), $arguments); } catch (\Throwable $e) { - throw new Exception('Error handler had an error: ' . $e->getMessage(), 500, $e); + throw new Exception('Error handler had an error: ' . $e->getMessage() . "\nStack trace: " . $e->getTraceAsString(), 500, $e); } } } @@ -654,7 +654,7 @@ public function execute(Route $route, Request $request, Response $response): sta $arguments = $this->getArguments($error, $pathValues, $request->getParams()); \call_user_func_array($error->getAction(), $arguments); } catch (\Throwable $e) { - throw new Exception('Error handler had an error: ' . $e->getMessage(), 500, $e); + throw new Exception('Error handler had an error: ' . $e->getMessage() . "\nStack trace: " . $e->getTraceAsString(), 500, $e); } } } @@ -811,7 +811,7 @@ private function runInternal(Request $request, Response $response): static $arguments = $this->getArguments($error, [], $request->getParams()); \call_user_func_array($error->getAction(), $arguments); } catch (\Throwable $e) { - throw new Exception('Error handler had an error: ' . $e->getMessage(), 500, $e); + throw new Exception('Error handler had an error: ' . $e->getMessage() . "\nStack trace: " . $e->getTraceAsString(), 500, $e); } } } @@ -854,7 +854,7 @@ private function runInternal(Request $request, Response $response): static $arguments = $this->getArguments($error, [], $request->getParams()); \call_user_func_array($error->getAction(), $arguments); } catch (\Throwable $e) { - throw new Exception('Error handler had an error: ' . $e->getMessage(), 500, $e); + throw new Exception('Error handler had an error: ' . $e->getMessage() . "\nStack trace: " . $e->getTraceAsString(), 500, $e); } } } @@ -869,7 +869,7 @@ private function runInternal(Request $request, Response $response): static $arguments = $this->getArguments($error, [], $request->getParams()); \call_user_func_array($error->getAction(), $arguments); } catch (\Throwable $e) { - throw new Exception('Error handler had an error: ' . $e->getMessage(), 500, $e); + throw new Exception('Error handler had an error: ' . $e->getMessage() . "\nStack trace: " . $e->getTraceAsString(), 500, $e); } } } diff --git a/tests/AppTest.php b/tests/AppTest.php index 7a3c4f2d..2c9d136b 100755 --- a/tests/AppTest.php +++ b/tests/AppTest.php @@ -718,22 +718,23 @@ public function testErrorHandlerFailure(): void ->error() ->inject('error') ->action(function ($error) { - throw new \Exception('Error handler failed'); + throw new Exception('Error handler failed'); }); $route = new Route('GET', '/path'); $route ->action(function () { - throw new \Exception('Route action failed'); + throw new Exception('Route action failed'); }); try { $this->app->execute($route, new Request(), new Response()); $this->fail('Should have thrown an exception'); - } catch (\Exception $e) { - $this->assertEquals('Error handler had an error: Error handler failed', $e->getMessage()); + } catch (Exception $e) { + $this->assertStringStartsWith('Error handler had an error: Error handler failed', $e->getMessage()); + $this->assertStringContainsString('Stack trace:', $e->getMessage()); $this->assertEquals(500, $e->getCode()); - $this->assertInstanceOf(\Exception::class, $e->getPrevious()); + $this->assertInstanceOf(Exception::class, $e->getPrevious()); $this->assertEquals('Error handler failed', $e->getPrevious()->getMessage()); } } @@ -744,13 +745,13 @@ public function testOptionsHandlerFailure(): void ->error() ->inject('error') ->action(function ($error) { - throw new \Exception('Options error handler failed'); + throw new Exception('Options error handler failed'); }); // Set up an options handler that throws App::options() ->action(function () { - throw new \Exception('Options handler failed'); + throw new Exception('Options handler failed'); }); $request = new UtopiaRequestTest(); @@ -759,8 +760,9 @@ public function testOptionsHandlerFailure(): void try { $this->app->run($request, new Response()); $this->fail('Should have thrown an exception'); - } catch (\Exception $e) { - $this->assertEquals('Error handler had an error: Options error handler failed', $e->getMessage()); + } catch (Exception $e) { + $this->assertStringStartsWith('Error handler had an error: Options error handler failed', $e->getMessage()); + $this->assertStringContainsString('Stack trace:', $e->getMessage()); $this->assertEquals(500, $e->getCode()); } } @@ -771,7 +773,7 @@ public function testNotFoundErrorHandlerFailure(): void $this->app ->error() ->action(function () { - throw new \Exception('404 error handler failed'); + throw new Exception('404 error handler failed'); }); $request = new UtopiaRequestTest(); @@ -781,10 +783,11 @@ public function testNotFoundErrorHandlerFailure(): void try { $this->app->run($request, new Response()); $this->fail('Should have thrown an exception'); - } catch (\Exception $e) { - $this->assertEquals('Error handler had an error: 404 error handler failed', $e->getMessage()); + } catch (Exception $e) { + $this->assertStringStartsWith('Error handler had an error: 404 error handler failed', $e->getMessage()); + $this->assertStringContainsString('Stack trace:', $e->getMessage()); $this->assertEquals(500, $e->getCode()); - $this->assertInstanceOf(\Exception::class, $e->getPrevious()); + $this->assertInstanceOf(Exception::class, $e->getPrevious()); $this->assertEquals('404 error handler failed', $e->getPrevious()->getMessage()); } } @@ -796,23 +799,24 @@ public function testGroupErrorHandlerFailure(): void ->error() ->groups(['api']) ->action(function () { - throw new \Exception('Group error handler failed'); + throw new Exception('Group error handler failed'); }); $route = new Route('GET', '/api/test'); $route ->groups(['api']) ->action(function () { - throw new \Exception('Route action failed'); + throw new Exception('Route action failed'); }); try { $this->app->execute($route, new Request(), new Response()); $this->fail('Should have thrown an exception'); - } catch (\Exception $e) { - $this->assertEquals('Error handler had an error: Group error handler failed', $e->getMessage()); + } catch (Exception $e) { + $this->assertStringStartsWith('Error handler had an error: Group error handler failed', $e->getMessage()); + $this->assertStringContainsString('Stack trace:', $e->getMessage()); $this->assertEquals(500, $e->getCode()); - $this->assertInstanceOf(\Exception::class, $e->getPrevious()); + $this->assertInstanceOf(Exception::class, $e->getPrevious()); $this->assertEquals('Group error handler failed', $e->getPrevious()->getMessage()); } } @@ -824,31 +828,32 @@ public function testErrorHandlerChaining(): void ->error() ->groups(['api']) ->action(function () { - throw new \Exception('First error handler failed'); + throw new Exception('First error handler failed'); }); $this->app ->error() ->action(function () { - throw new \Exception('Second error handler failed'); + throw new Exception('Second error handler failed'); }); $route = new Route('GET', '/api/test'); $route ->groups(['api']) ->action(function () { - throw new \Exception('Original error'); + throw new Exception('Original error'); }); try { $this->app->execute($route, new Request(), new Response()); $this->fail('Should have thrown an exception'); - } catch (\Exception $e) { - $this->assertEquals('Error handler had an error: First error handler failed', $e->getMessage()); + } catch (Exception $e) { + $this->assertStringStartsWith('Error handler had an error: First error handler failed', $e->getMessage()); + $this->assertStringContainsString('Stack trace:', $e->getMessage()); $this->assertEquals(500, $e->getCode()); // Verify the error chain - $this->assertInstanceOf(\Exception::class, $e->getPrevious()); + $this->assertInstanceOf(Exception::class, $e->getPrevious()); $this->assertEquals('First error handler failed', $e->getPrevious()->getMessage()); } } From 69d1452314213183ef7b7a98ca9bf178027160a9 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Thu, 25 Sep 2025 16:11:32 +0530 Subject: [PATCH 085/103] Trigger Build From f7d4d16ea60fb1879a7f535e98b535cd1dae67b0 Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Sun, 12 Oct 2025 05:25:43 +0000 Subject: [PATCH 086/103] fix merge --- src/Http/Http.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Http/Http.php b/src/Http/Http.php index 43278800..56d1e187 100755 --- a/src/Http/Http.php +++ b/src/Http/Http.php @@ -584,6 +584,7 @@ public function run(Container $context): static 'http.request.method' => $request->getMethod(), 'url.scheme' => $request->getProtocol(), ]); + return $result; } From 7120fd9e2d1dcdc1d96a8ad0edc7bf33d06d5a1c Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Sun, 12 Oct 2025 05:26:00 +0000 Subject: [PATCH 087/103] fix typo --- src/Http/Http.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Http/Http.php b/src/Http/Http.php index 56d1e187..111eecba 100755 --- a/src/Http/Http.php +++ b/src/Http/Http.php @@ -50,7 +50,7 @@ class Http extends Base * Compression */ protected bool $compression = false; - protected int $compressionMinSize = App::COMPRESSION_MIN_SIZE_DEFAULT; + protected int $compressionMinSize = self::COMPRESSION_MIN_SIZE_DEFAULT; protected mixed $compressionSupported = []; private Histogram $requestDuration; From aedf29bc9c93388c2a1cc68f460c805065fff7d2 Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Sun, 12 Oct 2025 08:23:12 +0000 Subject: [PATCH 088/103] Fix codeql issues --- .github/workflows/codeql-analysis.yml | 2 +- src/Http/Adapter/Swoole/Server.php | 26 ++++----- src/Http/Http.php | 25 -------- src/Http/Response.php | 84 ++------------------------- tests/HttpTest.php | 10 +++- tests/UtopiaFPMRequestTest.php | 77 ++++++++++++++++++++++++ 6 files changed, 101 insertions(+), 123 deletions(-) create mode 100644 tests/UtopiaFPMRequestTest.php diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 70bba1bd..c6e9f877 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -12,6 +12,6 @@ jobs: - name: Run CodeQL run: | - docker run --rm -v $PWD:/app composer:2.6 sh -c \ + docker run --rm -v $PWD:/app composer:2.8 sh -c \ "composer install --profile --ignore-platform-reqs && composer check" \ No newline at end of file diff --git a/src/Http/Adapter/Swoole/Server.php b/src/Http/Adapter/Swoole/Server.php index bfeed443..1c6914ab 100755 --- a/src/Http/Adapter/Swoole/Server.php +++ b/src/Http/Adapter/Swoole/Server.php @@ -2,9 +2,13 @@ namespace Utopia\Http\Adapter\Swoole; +use Swoole\Coroutine; use Utopia\Http\Adapter; -use Swoole\Http\Server as SwooleServer; -use Swoole\Runtime; +use Swoole\Coroutine\Http\Server as SwooleServer; +use Swoole\Http\Request as SwooleRequest; +use Swoole\Http\Response as SwooleResponse; + +use function Swoole\Coroutine\run; class Server extends Adapter { @@ -13,36 +17,28 @@ class Server extends Adapter public function __construct(string $host, ?string $port = null, array $settings = []) { $this->server = new SwooleServer($host, $port); - $this->server->set(\array_merge($settings, [ + $this->server->set(array_merge([ 'open_http2_protocol' => true, 'dispatch_mode' => 2, 'enable_coroutine' => true, 'http_parse_cookie' => false, - ])); + ], $settings)); } public function onRequest(callable $callback) { - $this->server->on('request', function ($request, $response) use ($callback) { - go(function () use ($request, $response, $callback) { - call_user_func($callback, new Request($request), new Response($response)); - }); + $this->server->handle('/', function (SwooleRequest $request, SwooleResponse $response) use ($callback) { + call_user_func($callback, $request, $response); }); } public function onStart(callable $callback) { - $this->server->on('start', function () use ($callback) { - go(function () use ($callback) { - call_user_func($callback); - }); - }); + call_user_func($callback, $this); } public function start() { - Runtime::enableCoroutine(); - return $this->server->start(); if (Coroutine::getCid() === -1) { run(fn () => $this->server->start()); } else { diff --git a/src/Http/Http.php b/src/Http/Http.php index 111eecba..95a4a05b 100755 --- a/src/Http/Http.php +++ b/src/Http/Http.php @@ -252,31 +252,6 @@ public static function options(): Hook return $hook; } - /** - * Get Mode - * - * Get current mode - * - * @return string - */ - public static function getMode(): string - { - return self::$mode; - } - - /** - * Set Mode - * - * Set current mode - * - * @param string $value - * @return void - */ - public static function setMode(string $value): void - { - self::$mode = $value; - } - /** * Get Routes * diff --git a/src/Http/Response.php b/src/Http/Response.php index b2116c39..866e9452 100755 --- a/src/Http/Response.php +++ b/src/Http/Response.php @@ -45,8 +45,6 @@ abstract class Response */ public const STATUS_CODE_CONTINUE = 100; public const STATUS_CODE_SWITCHING_PROTOCOLS = 101; - public const STATUS_CODE_PROCESSING = 102; - public const STATUS_CODE_EARLY_HINTS = 103; public const STATUS_CODE_PROCESSING = 102; @@ -63,12 +61,6 @@ abstract class Response public const STATUS_CODE_ALREADY_REPORTED = 208; public const STATUS_CODE_IM_USED = 226; - public const STATUS_CODE_MULTI_STATUS = 207; - - public const STATUS_CODE_ALREADY_REPORTED = 208; - - public const STATUS_CODE_IM_USED = 226; - public const STATUS_CODE_MULTIPLE_CHOICES = 300; public const STATUS_CODE_MOVED_PERMANENTLY = 301; public const STATUS_CODE_FOUND = 302; @@ -79,8 +71,6 @@ abstract class Response public const STATUS_CODE_TEMPORARY_REDIRECT = 307; public const STATUS_CODE_PERMANENT_REDIRECT = 308; - public const STATUS_CODE_PERMANENT_REDIRECT = 308; - public const STATUS_CODE_BAD_REQUEST = 400; public const STATUS_CODE_UNAUTHORIZED = 401; public const STATUS_CODE_PAYMENT_REQUIRED = 402; @@ -123,16 +113,6 @@ abstract class Response public const STATUS_CODE_NOT_EXTENDED = 510; public const STATUS_CODE_NETWORK_AUTHENTICATION_REQUIRED = 511; - public const STATUS_CODE_VARIANT_ALSO_NEGOTIATES = 506; - - public const STATUS_CODE_INSUFFICIENT_STORAGE = 507; - - public const STATUS_CODE_LOOP_DETECTED = 508; - - public const STATUS_CODE_NOT_EXTENDED = 510; - - public const STATUS_CODE_NETWORK_AUTHENTICATION_REQUIRED = 511; - /** * @var array */ @@ -330,7 +310,7 @@ abstract class Response /** * @var int */ - protected int $compressionMinSize = App::COMPRESSION_MIN_SIZE_DEFAULT; + protected int $compressionMinSize = Http::COMPRESSION_MIN_SIZE_DEFAULT; /** * @var mixed @@ -635,7 +615,9 @@ public function send(string $body = ''): void ->addHeader('Server', array_key_exists('Server', $this->headers) ? $this->headers['Server'] : 'Utopia/Http') ->addHeader('X-Debug-Speed', (string) (\microtime(true) - $this->startTime)) ; - $this->appendCookies(); + $this + ->appendCookies() + ->appendHeaders(); if (!$this->disablePayload) { $length = strlen($body); @@ -654,10 +636,7 @@ public function send(string $body = ''): void $headersSize += (\count($this->headers) - 1) * 2; // linebreaks $this->size = $this->size + $headersSize + $length; - if (array_key_exists( - $this->contentType, - $this->compressed - ) && ($length <= self::CHUNK_SIZE)) { // Dont compress with GZIP / Brotli if header is not listed and size is bigger than 2mb + if ($this->isCompressible($this->contentType) && ($length <= self::CHUNK_SIZE)) { // Dont compress with GZIP / Brotli if header is not listed and size is bigger than 2mb $this->end($body); } else { for ($i = 0; $i < ceil($length / self::CHUNK_SIZE); $i++) { @@ -668,59 +647,6 @@ public function send(string $body = ''): void } } - // Compress body only if all conditions are met: - if ( - !$hasContentEncoding && - !empty($this->acceptEncoding) && - $this->isCompressible($this->contentType) && - strlen($body) > $this->compressionMinSize - ) { - $algorithm = Compression::fromAcceptEncoding($this->acceptEncoding, $this->compressionSupported); - - if ($algorithm) { - $body = $algorithm->compress($body); - $this->addHeader('Content-Length', (string) \strlen($body), override: true); - $this->addHeader('Content-Encoding', $algorithm->getContentEncoding(), override: true); - $this->addHeader('X-Utopia-Compression', 'true', override: true); - $this->addHeader('Vary', 'Accept-Encoding', override: true); - } - } - - $this->addHeader('X-Debug-Speed', (string) (microtime(true) - $this->startTime), override: true); - $this->appendHeaders(); - - // Send response - if ($this->disablePayload) { - $this->end(); - return; - } - - $headersSize = 0; - foreach ($this->headers as $name => $values) { - if (\is_array($values)) { - foreach ($values as $value) { - $headersSize += \strlen($name . ': ' . $value); - } - $headersSize += (\count($values) - 1) * 2; // linebreaks - } else { - $headersSize += \strlen($name . ': ' . $values); - } - } - $headersSize += (\count($this->headers) - 1) * 2; // linebreaks - - $bodyLength = strlen($body); - $this->size += $headersSize + $bodyLength; - - if ($bodyLength <= self::CHUNK_SIZE) { - $this->end($body); - } else { - $chunks = str_split($body, self::CHUNK_SIZE); - foreach ($chunks as $chunk) { - $this->write($chunk); - } - $this->end(); - } - $this->sent = true; $this->disablePayload(); diff --git a/tests/HttpTest.php b/tests/HttpTest.php index d7b5bc86..b42f9ad7 100755 --- a/tests/HttpTest.php +++ b/tests/HttpTest.php @@ -10,6 +10,7 @@ use Utopia\Http\Tests\MockResponse as Response; use Utopia\Http\Validator\Text; use Utopia\Http\Adapter\FPM\Server; +use Utopia\Http\Tests\UtopiaFPMRequestTest; class HttpTest extends TestCase { @@ -804,8 +805,9 @@ public function testCallableStringParametersNotExecuted(): void echo 'callback-value: ' . $callback; }); + $context = clone $this->context; \ob_start(); - $this->http->execute($route, new Request(), '1'); + $this->http->execute($route, new Request(), $context); $result = \ob_get_contents(); \ob_end_clean(); @@ -821,9 +823,10 @@ public function testCallableStringParametersNotExecuted(): void }); \ob_start(); + $context = clone $this->context; $request = new UtopiaFPMRequestTest(); $request::_setParams(['func' => 'system']); - $this->http->execute($route2, $request, '1'); + $this->http->execute($route2, $request, $context); $result = \ob_get_contents(); \ob_end_clean(); @@ -841,7 +844,8 @@ public function testCallableStringParametersNotExecuted(): void }); \ob_start(); - $this->http->execute($route3, new Request(), '1'); + $context = clone $this->context; + $this->http->execute($route3, new Request(), $context); $result = \ob_get_contents(); \ob_end_clean(); diff --git a/tests/UtopiaFPMRequestTest.php b/tests/UtopiaFPMRequestTest.php new file mode 100644 index 00000000..4c78751d --- /dev/null +++ b/tests/UtopiaFPMRequestTest.php @@ -0,0 +1,77 @@ + Date: Mon, 13 Oct 2025 00:48:29 +0000 Subject: [PATCH 089/103] keep original server --- src/Http/Adapter/Swoole/Server.php | 33 +++++++++++++----------------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/src/Http/Adapter/Swoole/Server.php b/src/Http/Adapter/Swoole/Server.php index 1c6914ab..c240eaaa 100755 --- a/src/Http/Adapter/Swoole/Server.php +++ b/src/Http/Adapter/Swoole/Server.php @@ -2,13 +2,8 @@ namespace Utopia\Http\Adapter\Swoole; -use Swoole\Coroutine; use Utopia\Http\Adapter; -use Swoole\Coroutine\Http\Server as SwooleServer; -use Swoole\Http\Request as SwooleRequest; -use Swoole\Http\Response as SwooleResponse; - -use function Swoole\Coroutine\run; +use Swoole\Http\Server as SwooleServer; class Server extends Adapter { @@ -17,32 +12,32 @@ class Server extends Adapter public function __construct(string $host, ?string $port = null, array $settings = []) { $this->server = new SwooleServer($host, $port); - $this->server->set(array_merge([ + $this->server->set(\array_merge($settings, [ 'open_http2_protocol' => true, 'dispatch_mode' => 2, - 'enable_coroutine' => true, - 'http_parse_cookie' => false, - ], $settings)); + ])); } public function onRequest(callable $callback) { - $this->server->handle('/', function (SwooleRequest $request, SwooleResponse $response) use ($callback) { - call_user_func($callback, $request, $response); + $this->server->on('request', function ($request, $response) use ($callback) { + go(function () use ($request, $response, $callback) { + call_user_func($callback, new Request($request), new Response($response)); + }); }); } public function onStart(callable $callback) { - call_user_func($callback, $this); + $this->server->on('start', function () use ($callback) { + go(function () use ($callback) { + call_user_func($callback); + }); + }); } public function start() { - if (Coroutine::getCid() === -1) { - run(fn () => $this->server->start()); - } else { - $this->server->start(); - } + return $this->server->start(); } -} +} \ No newline at end of file From d1bbdacc75bc19c0d29bf512b0de7a1c39ccbc85 Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Mon, 13 Oct 2025 01:07:12 +0000 Subject: [PATCH 090/103] format --- src/Http/Adapter/Swoole/Server.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Http/Adapter/Swoole/Server.php b/src/Http/Adapter/Swoole/Server.php index c240eaaa..1a79e927 100755 --- a/src/Http/Adapter/Swoole/Server.php +++ b/src/Http/Adapter/Swoole/Server.php @@ -40,4 +40,4 @@ public function start() { return $this->server->start(); } -} \ No newline at end of file +} From 3ee6a78d09087083e650be73795b81423b76924a Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Mon, 13 Oct 2025 01:29:58 +0000 Subject: [PATCH 091/103] Fix tests usage --- src/Http/Request.php | 27 +-------------------------- tests/e2e/BaseTest.php | 10 +++++----- tests/e2e/init.php | 4 ++-- 3 files changed, 8 insertions(+), 33 deletions(-) diff --git a/src/Http/Request.php b/src/Http/Request.php index 38151339..a90f86ce 100755 --- a/src/Http/Request.php +++ b/src/Http/Request.php @@ -504,32 +504,7 @@ public function setPayload(array $params): static * * @return array */ - protected function generateHeaders(): array - { - if (null === $this->headers) { - /** - * Fallback for older PHP versions - * that do not support generateHeaders - */ - if (!\function_exists('getallheaders')) { - $headers = []; - - foreach ($_SERVER as $name => $value) { - if (\substr($name, 0, 5) == 'HTTP_') { - $headers[\str_replace(' ', '-', \strtolower(\str_replace('_', ' ', \substr($name, 5))))] = $value; - } - } - - $this->headers = $headers; - - return $this->headers; - } - - $this->headers = array_change_key_case(getallheaders()); - } - - return $this->headers; - } + abstract protected function generateHeaders(): array; /** * Generate input diff --git a/tests/e2e/BaseTest.php b/tests/e2e/BaseTest.php index badb1ab3..47c7679c 100644 --- a/tests/e2e/BaseTest.php +++ b/tests/e2e/BaseTest.php @@ -85,31 +85,31 @@ public function testCookie() { // One cookie $cookie = 'cookie1=value1'; - $response = $this->client->call(Client::METHOD_GET, '/cookies', [ 'Cookie: ' . $cookie ]); + $response = $this->client->call(Client::METHOD_GET, '/cookies', [ 'Cookie' => $cookie ]); $this->assertEquals(200, $response['headers']['status-code']); $this->assertEquals($cookie, $response['body']); // Two cookiees $cookie = 'cookie1=value1; cookie2=value2'; - $response = $this->client->call(Client::METHOD_GET, '/cookies', [ 'Cookie: ' . $cookie ]); + $response = $this->client->call(Client::METHOD_GET, '/cookies', [ 'Cookie' => $cookie ]); $this->assertEquals(200, $response['headers']['status-code']); $this->assertEquals($cookie, $response['body']); // Two cookies without optional space $cookie = 'cookie1=value1;cookie2=value2'; - $response = $this->client->call(Client::METHOD_GET, '/cookies', [ 'Cookie: ' . $cookie ]); + $response = $this->client->call(Client::METHOD_GET, '/cookies', [ 'Cookie' => $cookie ]); $this->assertEquals(200, $response['headers']['status-code']); $this->assertEquals($cookie, $response['body']); // Cookie with "=" in value $cookie = 'cookie1=value1=value2'; - $response = $this->client->call(Client::METHOD_GET, '/cookies', [ 'Cookie: ' . $cookie ]); + $response = $this->client->call(Client::METHOD_GET, '/cookies', [ 'Cookie' => $cookie ]); $this->assertEquals(200, $response['headers']['status-code']); $this->assertEquals($cookie, $response['body']); // Case sensitivity for cookie names $cookie = 'cookie1=v1; Cookie1=v2'; - $response = $this->client->call(Client::METHOD_GET, '/cookies', [ 'Cookie: ' . $cookie ]); + $response = $this->client->call(Client::METHOD_GET, '/cookies', [ 'Cookie' => $cookie ]); $this->assertEquals(200, $response['headers']['status-code']); $this->assertEquals($cookie, $response['body']); } diff --git a/tests/e2e/init.php b/tests/e2e/init.php index c65d2c28..003fb29c 100644 --- a/tests/e2e/init.php +++ b/tests/e2e/init.php @@ -84,8 +84,8 @@ ->inject('request') ->inject('response') ->action(function (Request $request, Response $response) { - $response->addHeader('Set-Cookie', 'key1=value1'); - $response->addHeader('Set-Cookie', 'key2=value2'); + $response->addHeader('Set-Cookie', 'key1=value1', false); + $response->addHeader('Set-Cookie', 'key2=value2', false); $response->send('OK'); }); From 0c62db79ad9a1e4110902518e969ec78702325c5 Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Mon, 13 Oct 2025 02:02:32 +0000 Subject: [PATCH 092/103] Fix fpm cookie tests --- src/Http/Adapter/Swoole/Request.php | 14 ++++++++++- tests/e2e/BaseTest.php | 11 ++++++--- tests/e2e/ResponseFPMTest.php | 37 +++++++++++++++++++++++++++++ 3 files changed, 58 insertions(+), 4 deletions(-) diff --git a/src/Http/Adapter/Swoole/Request.php b/src/Http/Adapter/Swoole/Request.php index 52660e16..e97deff1 100644 --- a/src/Http/Adapter/Swoole/Request.php +++ b/src/Http/Adapter/Swoole/Request.php @@ -392,7 +392,19 @@ protected function generateInput(): array */ protected function generateHeaders(): array { - $headers = $this->swoole->header; + $headers = $this->swoole->header ?? []; + + // Check if cookies are available in a separate property + if (!empty($this->swoole->cookie)) { + // Convert cookies back to Cookie header format + $cookiePairs = []; + foreach ($this->swoole->cookie as $name => $value) { + $cookiePairs[] = $name . '=' . $value; + } + if (!empty($cookiePairs)) { + $headers['cookie'] = implode('; ', $cookiePairs); + } + } foreach ($headers as $key => $value) { $headers[strtolower($key)] = $value; diff --git a/tests/e2e/BaseTest.php b/tests/e2e/BaseTest.php index 47c7679c..03db3319 100644 --- a/tests/e2e/BaseTest.php +++ b/tests/e2e/BaseTest.php @@ -95,11 +95,16 @@ public function testCookie() $this->assertEquals(200, $response['headers']['status-code']); $this->assertEquals($cookie, $response['body']); + /** + * Cookie response always expecting space in multiple cookie + * as RFC 6265 (https://datatracker.ietf.org/doc/html/rfc6265#section-4.2.1) recommends it + */ + // Two cookies without optional space $cookie = 'cookie1=value1;cookie2=value2'; $response = $this->client->call(Client::METHOD_GET, '/cookies', [ 'Cookie' => $cookie ]); $this->assertEquals(200, $response['headers']['status-code']); - $this->assertEquals($cookie, $response['body']); + $this->assertEquals('cookie1=value1; cookie2=value2', $response['body']); // Cookie with "=" in value $cookie = 'cookie1=value1=value2'; @@ -108,10 +113,10 @@ public function testCookie() $this->assertEquals($cookie, $response['body']); // Case sensitivity for cookie names - $cookie = 'cookie1=v1; Cookie1=v2'; + $cookie = 'cookie1=v1;Cookie1=v2'; $response = $this->client->call(Client::METHOD_GET, '/cookies', [ 'Cookie' => $cookie ]); $this->assertEquals(200, $response['headers']['status-code']); - $this->assertEquals($cookie, $response['body']); + $this->assertEquals('cookie1=v1; Cookie1=v2', $response['body']); } public function testSetCookie() diff --git a/tests/e2e/ResponseFPMTest.php b/tests/e2e/ResponseFPMTest.php index 88cff38e..3568891f 100644 --- a/tests/e2e/ResponseFPMTest.php +++ b/tests/e2e/ResponseFPMTest.php @@ -14,4 +14,41 @@ public function setUp(): void { $this->client = new Client('http://fpm'); } + + /** + * Override cookie test for FPM specific behavior + * FPM preserves original cookie format while Swoole normalizes it + */ + public function testCookie() + { + // One cookie + $cookie = 'cookie1=value1'; + $response = $this->client->call(Client::METHOD_GET, '/cookies', [ 'Cookie' => $cookie ]); + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals($cookie, $response['body']); + + // Two cookies with space (FPM preserves original format) + $cookie = 'cookie1=value1; cookie2=value2'; + $response = $this->client->call(Client::METHOD_GET, '/cookies', [ 'Cookie' => $cookie ]); + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals($cookie, $response['body']); + + // Two cookies without space (FPM preserves original format) + $cookie = 'cookie1=value1;cookie2=value2'; + $response = $this->client->call(Client::METHOD_GET, '/cookies', [ 'Cookie' => $cookie ]); + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals($cookie, $response['body']); + + // Cookie with "=" in value + $cookie = 'cookie1=value1=value2'; + $response = $this->client->call(Client::METHOD_GET, '/cookies', [ 'Cookie' => $cookie ]); + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals($cookie, $response['body']); + + // Case sensitivity for cookie names + $cookie = 'cookie1=v1; Cookie1=v2'; + $response = $this->client->call(Client::METHOD_GET, '/cookies', [ 'Cookie' => $cookie ]); + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals($cookie, $response['body']); + } } From 7bfc6c9f3f1d6504c28f066f317a1fdf5bac0383 Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Mon, 13 Oct 2025 02:06:59 +0000 Subject: [PATCH 093/103] update tests separation using groups --- .github/workflows/test.yml | 6 +++--- docker-compose.yml | 2 -- tests/e2e/ResponseFPMTest.php | 4 ++++ tests/e2e/ResponseSwooleCoroutineTest.php | 4 ++++ tests/e2e/ResponseSwooleTest.php | 4 ++++ 5 files changed, 15 insertions(+), 5 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a6f1e642..7d97c19c 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -25,10 +25,10 @@ jobs: run: sleep 10 - name: Run FPM Tests - run: docker compose exec fpm vendor/bin/phpunit --configuration phpunit.xml + run: docker compose exec fpm vendor/bin/phpunit --configuration phpunit.xml --group=fpm - name: Run Swoole Tests - run: docker compose exec swoole vendor/bin/phpunit --configuration phpunit.xml + run: docker compose exec swoole vendor/bin/phpunit --configuration phpunit.xml --group=swoole - name: Run Swoole Corotuine Tests - run: docker compose exec swoole-coroutine vendor/bin/phpunit --configuration phpunit.xml + run: docker compose exec swoole-coroutine vendor/bin/phpunit --configuration phpunit.xml --group=swoole-coroutine diff --git a/docker-compose.yml b/docker-compose.yml index bd2952c7..5e396eb3 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -10,8 +10,6 @@ services: - ./tests:/usr/share/nginx/html/tests networks: - testing - depends_on: - - swoole swoole: build: context: . diff --git a/tests/e2e/ResponseFPMTest.php b/tests/e2e/ResponseFPMTest.php index 3568891f..b0f140c2 100644 --- a/tests/e2e/ResponseFPMTest.php +++ b/tests/e2e/ResponseFPMTest.php @@ -5,6 +5,10 @@ use PHPUnit\Framework\TestCase; use Tests\E2E\Client; +/** + * @group fpm + * @group e2e + */ class ResponseFPMTest extends TestCase { use BaseTest; diff --git a/tests/e2e/ResponseSwooleCoroutineTest.php b/tests/e2e/ResponseSwooleCoroutineTest.php index d1811e37..ad97fa66 100644 --- a/tests/e2e/ResponseSwooleCoroutineTest.php +++ b/tests/e2e/ResponseSwooleCoroutineTest.php @@ -5,6 +5,10 @@ use PHPUnit\Framework\TestCase; use Tests\E2E\Client; +/** + * @group swoole-coroutine + * @group e2e + */ class ResponseSwooleCoroutineTest extends TestCase { use BaseTest; diff --git a/tests/e2e/ResponseSwooleTest.php b/tests/e2e/ResponseSwooleTest.php index 6d793b90..741cc2ba 100755 --- a/tests/e2e/ResponseSwooleTest.php +++ b/tests/e2e/ResponseSwooleTest.php @@ -5,6 +5,10 @@ use PHPUnit\Framework\TestCase; use Tests\E2E\Client; +/** + * @group swoole + * @group e2e + */ class ResponseSwooleTest extends TestCase { use BaseTest; From fc0cea5d6739d3c09822295f2b3dac9b8cf933e3 Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Mon, 13 Oct 2025 02:24:50 +0000 Subject: [PATCH 094/103] test suite --- .github/workflows/test.yml | 3 +++ docker-compose.yml | 3 +++ phpunit.xml | 19 ++++++++++++++++--- 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 7d97c19c..6c84b7f1 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -24,6 +24,9 @@ jobs: - name: Wait for Server to be ready run: sleep 10 + - name: Run unit Tests + run: docker compose exec swoole vendor/bin/phpunit --configuration phpunit.xml --testsuite=unit + - name: Run FPM Tests run: docker compose exec fpm vendor/bin/phpunit --configuration phpunit.xml --group=fpm diff --git a/docker-compose.yml b/docker-compose.yml index 5e396eb3..6122cb80 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -8,6 +8,7 @@ services: volumes: - ./src:/usr/share/nginx/html/src - ./tests:/usr/share/nginx/html/tests + - ./phpunit.xml:/usr/share/nginx/html/phpunit.xml networks: - testing swoole: @@ -21,6 +22,7 @@ services: - ./src:/usr/src/code/src - ./tests:/usr/src/code/tests - ./tmp/xdebug:/tmp/xdebug + - ./phpunit.xml:/usr/share/nginx/html/phpunit.xml networks: - testing swoole-coroutine: @@ -34,6 +36,7 @@ services: - ./src:/usr/src/code/src - ./tests:/usr/src/code/tests - ./tmp/xdebug:/tmp/xdebug + - ./phpunit.xml:/usr/share/nginx/html/phpunit.xml networks: - testing diff --git a/phpunit.xml b/phpunit.xml index a70020e5..c7210e5e 100755 --- a/phpunit.xml +++ b/phpunit.xml @@ -10,11 +10,24 @@ stopOnFailure="false" > - - ./tests/e2e/Client.php + ./tests/MockRequest.php ./tests/MockResponse.php - ./tests/ + ./tests/HookTest.php + ./tests/HttpTest.php + ./tests/RequestTest.php + ./tests/ResponseTest.php + ./tests/RouterTest.php + ./tests/RouteTest.php + ./tests/UtopiaFPMRequestTest.php + ./tests/Validator/ + + + ./tests/e2e/Client.php + ./tests/e2e/BaseTest.php + ./tests/e2e/ResponseFPMTest.php + ./tests/e2e/ResponseSwooleTest.php + ./tests/e2e/ResponseSwooleCoroutineTest.php From b6f497bc839a964d67ae55ddc65fa5c9b2b3fa9a Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Mon, 13 Oct 2025 02:26:36 +0000 Subject: [PATCH 095/103] upgrade docker --- Dockerfile.fpm | 4 ++-- Dockerfile.swoole | 4 ++-- Dockerfile.swoole_coroutines | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Dockerfile.fpm b/Dockerfile.fpm index 043a8e04..c6299534 100644 --- a/Dockerfile.fpm +++ b/Dockerfile.fpm @@ -1,4 +1,4 @@ -FROM composer:2.0 AS step0 +FROM composer:2.8 AS step0 ARG TESTING=true @@ -13,7 +13,7 @@ RUN composer install --ignore-platform-reqs --optimize-autoloader \ --no-plugins --no-scripts --prefer-dist \ `if [ "$TESTING" != "true" ]; then echo "--no-dev"; fi` -FROM php:8.1-cli-alpine as final +FROM php:8.3-cli-alpine AS final LABEL maintainer="team@appwrite.io" ENV DEBIAN_FRONTEND=noninteractive \ diff --git a/Dockerfile.swoole b/Dockerfile.swoole index a930c97a..f4c442b1 100644 --- a/Dockerfile.swoole +++ b/Dockerfile.swoole @@ -1,4 +1,4 @@ -FROM composer:2.0 AS step0 +FROM composer:2.8 AS step0 ARG TESTING=true ARG DEBUG=false @@ -14,7 +14,7 @@ RUN composer install --ignore-platform-reqs --optimize-autoloader \ --no-plugins --no-scripts --prefer-dist \ `if [ "$TESTING" != "true" ]; then echo "--no-dev"; fi` -FROM appwrite/base:0.9.0 as final +FROM appwrite/base:0.11.3 AS final ARG TESTING=true ARG DEBUG=false diff --git a/Dockerfile.swoole_coroutines b/Dockerfile.swoole_coroutines index a02852da..f8b56432 100644 --- a/Dockerfile.swoole_coroutines +++ b/Dockerfile.swoole_coroutines @@ -1,4 +1,4 @@ -FROM composer:2.0 AS step0 +FROM composer:2.8 AS step0 ARG TESTING=true ARG DEBUG=false @@ -14,7 +14,7 @@ RUN composer install --ignore-platform-reqs --optimize-autoloader \ --no-plugins --no-scripts --prefer-dist \ `if [ "$TESTING" != "true" ]; then echo "--no-dev"; fi` -FROM appwrite/base:0.9.0 as final +FROM appwrite/base:0.11.3 AS final ARG TESTING=true ARG DEBUG=false From ff286baefdc2705200eb0ea80a425f90a460208a Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Mon, 13 Oct 2025 08:12:37 +0545 Subject: [PATCH 096/103] Update tests/UtopiaFPMRequestTest.php Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- tests/UtopiaFPMRequestTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/UtopiaFPMRequestTest.php b/tests/UtopiaFPMRequestTest.php index 4c78751d..d15c7a68 100644 --- a/tests/UtopiaFPMRequestTest.php +++ b/tests/UtopiaFPMRequestTest.php @@ -19,7 +19,7 @@ class UtopiaFPMRequestTest extends UtopiaFPMRequest */ public function getParam(string $key, $default = null): mixed { - if ($this::_hasParams() && \in_array($key, $this::_getParams())) { + if ($this::_hasParams() && \array_key_exists($key, $this::_getParams())) { return $this::_getParams()[$key]; } From b5601ea4ad1174d90a487e4a1cb63814561a6d8c Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Mon, 13 Oct 2025 02:32:05 +0000 Subject: [PATCH 097/103] add return type --- src/Http/Http.php | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/Http/Http.php b/src/Http/Http.php index 95a4a05b..675f3626 100755 --- a/src/Http/Http.php +++ b/src/Http/Http.php @@ -92,25 +92,28 @@ public function __construct(Adapter $server, Container $container, string $timez /** * Set Compression */ - public function setCompression(bool $compression) + public function setCompression(bool $compression): static { $this->compression = $compression; + return $this; } /** * Set minimum compression size */ - public function setCompressionMinSize(int $compressionMinSize) + public function setCompressionMinSize(int $compressionMinSize): static { $this->compressionMinSize = $compressionMinSize; + return $this; } /** * Set supported compression algorithms */ - public function setCompressionSupported(mixed $compressionSupported) + public function setCompressionSupported(mixed $compressionSupported): static { $this->compressionSupported = $compressionSupported; + return $this; } /** From ca257c1500b434146beedb189e60277c0abfb4d2 Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Tue, 14 Oct 2025 01:02:14 +0000 Subject: [PATCH 098/103] fix malformed cookie --- src/Http/Adapter/Swoole/Request.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Http/Adapter/Swoole/Request.php b/src/Http/Adapter/Swoole/Request.php index e97deff1..039b7d53 100644 --- a/src/Http/Adapter/Swoole/Request.php +++ b/src/Http/Adapter/Swoole/Request.php @@ -285,6 +285,9 @@ public function getCookie(string $key, string $default = ''): string $cookies = \explode(';', $this->getHeader('cookie', '')); foreach ($cookies as $cookie) { $cookie = \trim($cookie); + if ($cookie === '' || !\str_contains($cookie, '=')) { + continue; + } [$cookieKey, $cookieValue] = \explode('=', $cookie, 2); $cookieKey = \trim($cookieKey); $cookieValue = \trim($cookieValue); @@ -292,7 +295,6 @@ public function getCookie(string $key, string $default = ''): string return $cookieValue; } } - return $default; } From 6250ab58a196507ef01caa1f6222a03f29a8c3d5 Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Tue, 14 Oct 2025 01:06:28 +0000 Subject: [PATCH 099/103] fixes --- docker-compose.yml | 3 --- src/Http/Adapter/Swoole/Request.php | 17 +---------------- 2 files changed, 1 insertion(+), 19 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 6122cb80..5e396eb3 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -8,7 +8,6 @@ services: volumes: - ./src:/usr/share/nginx/html/src - ./tests:/usr/share/nginx/html/tests - - ./phpunit.xml:/usr/share/nginx/html/phpunit.xml networks: - testing swoole: @@ -22,7 +21,6 @@ services: - ./src:/usr/src/code/src - ./tests:/usr/src/code/tests - ./tmp/xdebug:/tmp/xdebug - - ./phpunit.xml:/usr/share/nginx/html/phpunit.xml networks: - testing swoole-coroutine: @@ -36,7 +34,6 @@ services: - ./src:/usr/src/code/src - ./tests:/usr/src/code/tests - ./tmp/xdebug:/tmp/xdebug - - ./phpunit.xml:/usr/share/nginx/html/phpunit.xml networks: - testing diff --git a/src/Http/Adapter/Swoole/Request.php b/src/Http/Adapter/Swoole/Request.php index 039b7d53..1739ac42 100644 --- a/src/Http/Adapter/Swoole/Request.php +++ b/src/Http/Adapter/Swoole/Request.php @@ -280,22 +280,7 @@ public function getFiles($key): array */ public function getCookie(string $key, string $default = ''): string { - $key = \strtolower($key); - - $cookies = \explode(';', $this->getHeader('cookie', '')); - foreach ($cookies as $cookie) { - $cookie = \trim($cookie); - if ($cookie === '' || !\str_contains($cookie, '=')) { - continue; - } - [$cookieKey, $cookieValue] = \explode('=', $cookie, 2); - $cookieKey = \trim($cookieKey); - $cookieValue = \trim($cookieValue); - if ($cookieKey === $key) { - return $cookieValue; - } - } - return $default; + return $this->swoole->cookie[$key] ?? $this->swoole->cookie[strtolower($key)] ?? $default; } /** From 0f3b1f415396c019b092ab59e752b5405e403f00 Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Tue, 14 Oct 2025 01:11:08 +0000 Subject: [PATCH 100/103] fix telemetry matching --- src/Http/Http.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Http/Http.php b/src/Http/Http.php index 675f3626..ac3dcdaf 100755 --- a/src/Http/Http.php +++ b/src/Http/Http.php @@ -552,7 +552,7 @@ public function run(Container $context): static $attributes = [ 'url.scheme' => $request->getProtocol(), 'http.request.method' => $request->getMethod(), - 'http.route' => $this->matchedRoute?->getPath(), + 'http.route' => $this->match($request)?->getPath() ?? '', 'http.response.status_code' => $response->getStatusCode(), ]; $this->requestDuration->record($requestDuration, $attributes); From 2cc1ac6dcc9e3324d647b8f0b020610a87c5fab3 Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Tue, 14 Oct 2025 01:12:51 +0000 Subject: [PATCH 101/103] fix route get from context --- src/Http/Http.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Http/Http.php b/src/Http/Http.php index ac3dcdaf..1aa861ee 100755 --- a/src/Http/Http.php +++ b/src/Http/Http.php @@ -540,6 +540,8 @@ public function run(Container $context): static /** @var Request $request */ $response = $context->get('response'); /** @var Response $response */ + $route = $context->get('route'); + /** @var ?Route $route */ $this->activeRequests->add(1, [ 'http.request.method' => $request->getMethod(), @@ -552,7 +554,7 @@ public function run(Container $context): static $attributes = [ 'url.scheme' => $request->getProtocol(), 'http.request.method' => $request->getMethod(), - 'http.route' => $this->match($request)?->getPath() ?? '', + 'http.route' => $route?->getPath() ?? '', 'http.response.status_code' => $response->getStatusCode(), ]; $this->requestDuration->record($requestDuration, $attributes); From 383cfc7cb621638088fbd76738d4f1fd37c26cb6 Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Tue, 14 Oct 2025 01:16:30 +0000 Subject: [PATCH 102/103] Fix route for telementry --- src/Http/Http.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Http/Http.php b/src/Http/Http.php index 1aa861ee..8c5480de 100755 --- a/src/Http/Http.php +++ b/src/Http/Http.php @@ -540,7 +540,7 @@ public function run(Container $context): static /** @var Request $request */ $response = $context->get('response'); /** @var Response $response */ - $route = $context->get('route'); + $route = $this->match($request); /** @var ?Route $route */ $this->activeRequests->add(1, [ From 5ed7634326fd8361740b0ff0045f8904eb522767 Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Wed, 15 Oct 2025 07:40:39 +0000 Subject: [PATCH 103/103] fix: remove duplicate matching --- src/Http/Http.php | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/Http/Http.php b/src/Http/Http.php index 8c5480de..cc248cde 100755 --- a/src/Http/Http.php +++ b/src/Http/Http.php @@ -542,13 +542,14 @@ public function run(Container $context): static /** @var Response $response */ $route = $this->match($request); /** @var ?Route $route */ + $this->matchedRoute = $route; $this->activeRequests->add(1, [ 'http.request.method' => $request->getMethod(), 'url.scheme' => $request->getProtocol(), ]); $start = microtime(true); - $result = $this->runInternal($context); + $result = $this->runInternal($context, $route); $requestDuration = microtime(true) - $start; $attributes = [ @@ -576,7 +577,7 @@ public function run(Container $context): static * * @param Container $context */ - protected function runInternal(Container $context): static + protected function runInternal(Container $context, ?Route $route): static { $request = $context->get('request'); /** @var Request $request */ @@ -602,8 +603,6 @@ protected function runInternal(Container $context): static } $method = $request->getMethod(); - $route = $this->match($request); - $this->matchedRoute = $route; $groups = ($route instanceof Route) ? $route->getGroups() : []; if (null === $route && null !== self::$wildcardRoute) {