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 01/11] 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 02/11] 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 03/11] 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 04/11] 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 05/11] 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 06/11] 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 07/11] 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 08/11] 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 09/11] 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 10/11] 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 11/11] 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",