Skip to content

Commit 82c7252

Browse files
Merge pull request #147 from utopia-php/feat-compression-0.33.x
feat: utopia compression 0.33.x
2 parents a7f5775 + 58851dc commit 82c7252

File tree

8 files changed

+307
-150
lines changed

8 files changed

+307
-150
lines changed

composer.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@
2222
"bench": "vendor/bin/phpbench run --report=benchmark"
2323
},
2424
"require": {
25-
"php": ">=8.0"
25+
"php": ">=8.0",
26+
"utopia-php/compression": "0.1.*"
2627
},
2728
"require-dev": {
2829
"phpunit/phpunit": "^9.5.25",

composer.lock

Lines changed: 174 additions & 126 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/App.php

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
class App
66
{
7+
public const COMPRESSION_MIN_SIZE_DEFAULT = 1024;
8+
79
/**
810
* Request method constants
911
*/
@@ -102,6 +104,13 @@ class App
102104
*/
103105
protected static ?Route $wildcardRoute = null;
104106

107+
/**
108+
* Compression
109+
*/
110+
protected bool $compression = false;
111+
protected int $compressionMinSize = App::COMPRESSION_MIN_SIZE_DEFAULT;
112+
protected mixed $compressionSupported = [];
113+
105114
/**
106115
* App
107116
*
@@ -112,6 +121,30 @@ public function __construct(string $timezone)
112121
\date_default_timezone_set($timezone);
113122
}
114123

124+
/**
125+
* Set Compression
126+
*/
127+
public function setCompression(bool $compression)
128+
{
129+
$this->compression = $compression;
130+
}
131+
132+
/**
133+
* Set minimum compression size
134+
*/
135+
public function setCompressionMinSize(int $compressionMinSize)
136+
{
137+
$this->compressionMinSize = $compressionMinSize;
138+
}
139+
140+
/**
141+
* Set supported compression algorithms
142+
*/
143+
public function setCompressionSupported(mixed $compressionSupported)
144+
{
145+
$this->compressionSupported = $compressionSupported;
146+
}
147+
115148
/**
116149
* GET
117150
*
@@ -642,6 +675,12 @@ protected function getArguments(Hook $hook, array $values, array $requestParams)
642675
*/
643676
public function run(Request $request, Response $response): static
644677
{
678+
if ($this->compression) {
679+
$response->setAcceptEncoding($request->getHeader('accept-encoding', ''));
680+
$response->setCompressionMinSize($this->compressionMinSize);
681+
$response->setCompressionSupported($this->compressionSupported);
682+
}
683+
645684
$this->resources['request'] = $request;
646685
$this->resources['response'] = $response;
647686

src/Response.php

Lines changed: 88 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
namespace Utopia;
44

5+
use Utopia\Compression\Compression;
6+
57
class Response
68
{
79
/**
@@ -245,6 +247,21 @@ class Response
245247
*/
246248
protected int $size = 0;
247249

250+
/**
251+
* @var string
252+
*/
253+
protected string $acceptEncoding = '';
254+
255+
/**
256+
* @var int
257+
*/
258+
protected int $compressionMinSize = App::COMPRESSION_MIN_SIZE_DEFAULT;
259+
260+
/**
261+
* @var mixed
262+
*/
263+
protected mixed $compressionSupported = [];
264+
248265
/**
249266
* Response constructor.
250267
*
@@ -270,6 +287,43 @@ public function setContentType(string $type, string $charset = ''): static
270287
return $this;
271288
}
272289

290+
/**
291+
* Set accept encoding
292+
*
293+
* Set HTTP accept encoding header.
294+
*
295+
* @param string $acceptEncoding
296+
*/
297+
public function setAcceptEncoding(string $acceptEncoding): static
298+
{
299+
$this->acceptEncoding = $acceptEncoding;
300+
return $this;
301+
}
302+
303+
/**
304+
* Set min compression size
305+
*
306+
* Set minimum size for compression to be applied in bytes.
307+
*
308+
* @param int $compressionMinSize
309+
*/
310+
public function setCompressionMinSize(int $compressionMinSize): static
311+
{
312+
$this->compressionMinSize = $compressionMinSize;
313+
return $this;
314+
}
315+
316+
/**
317+
* Set supported compression algorithms
318+
*
319+
* @param mixed $compressionSupported
320+
*/
321+
public function setCompressionSupported(mixed $compressionSupported): static
322+
{
323+
$this->compressionSupported = $compressionSupported;
324+
return $this;
325+
}
326+
273327
/**
274328
* Get content type
275329
*
@@ -473,36 +527,50 @@ public function send(string $body = ''): void
473527
return;
474528
}
475529

476-
$this->sent = true;
530+
$serverHeader = $this->headers['Server'] ?? 'Utopia/Http';
531+
$this->addHeader('Server', $serverHeader);
532+
$this->addHeader('X-Debug-Speed', (string) (microtime(true) - $this->startTime));
477533

478-
$this->addHeader('X-Debug-Speed', (string) (\microtime(true) - $this->startTime));
534+
$this->appendCookies();
479535

480-
$this
481-
->appendCookies()
482-
->appendHeaders();
536+
// Compress body
537+
if (
538+
!empty($this->acceptEncoding) &&
539+
isset($this->compressed[$this->contentType]) &&
540+
strlen($body) > $this->compressionMinSize
541+
) {
542+
$algorithm = Compression::fromAcceptEncoding($this->acceptEncoding, $this->compressionSupported);
483543

484-
if (!$this->disablePayload) {
485-
$length = strlen($body);
544+
if ($algorithm) {
545+
$body = $algorithm->compress($body);
546+
$this->addHeader('Content-Encoding', $algorithm->getContentEncoding());
547+
$this->addHeader('Vary', 'Accept-Encoding');
548+
}
549+
}
486550

487-
$this->size = $this->size + strlen(implode("\n", $this->headers)) + $length;
551+
$this->appendHeaders();
488552

489-
if (array_key_exists(
490-
$this->contentType,
491-
$this->compressed
492-
) && ($length <= self::CHUNK_SIZE)) { // Dont compress with GZIP / Brotli if header is not listed and size is bigger than 2mb
493-
$this->end($body);
494-
} else {
495-
for ($i = 0; $i < ceil($length / self::CHUNK_SIZE); $i++) {
496-
$this->write(substr($body, ($i * self::CHUNK_SIZE), min(self::CHUNK_SIZE, $length - ($i * self::CHUNK_SIZE))));
497-
}
553+
// Send response
554+
if ($this->disablePayload) {
555+
$this->end();
556+
return;
557+
}
498558

499-
$this->end();
500-
}
559+
$headerSize = strlen(implode("\n", $this->headers));
560+
$bodyLength = strlen($body);
561+
$this->size += $headerSize + $bodyLength;
501562

502-
$this->disablePayload();
563+
if ($bodyLength <= self::CHUNK_SIZE) {
564+
$this->end($body);
503565
} else {
566+
$chunks = str_split($body, self::CHUNK_SIZE);
567+
foreach ($chunks as $chunk) {
568+
$this->write($chunk);
569+
}
504570
$this->end();
505571
}
572+
573+
$this->disablePayload();
506574
}
507575

508576
/**

src/Validator/ArrayList.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ public function getDescription(): string
4646
{
4747
$msg = 'Value must a valid array';
4848

49-
if($this->length > 0) {
49+
if ($this->length > 0) {
5050
$msg .= ' no longer than ' . $this->length . ' items';
5151
}
5252

tests/ResponseTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ public function testCanSetStatus()
3535

3636
try {
3737
$this->response->setStatusCode(0); // Unknown status code
38-
} catch(\Exception $e) {
38+
} catch (\Exception $e) {
3939
$this->assertInstanceOf('\Exception', $e);
4040

4141
return;

tests/ViewTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ public function testCanRenderHtml()
6767
$this->view->setRendered(false);
6868
$this->view->setPath('just-a-broken-string.phtml');
6969
$this->view->render();
70-
} catch(\Exception $e) {
70+
} catch (\Exception $e) {
7171
return;
7272
}
7373

tests/e2e/server.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,4 +50,5 @@
5050
$response = new Response();
5151

5252
$app = new App('UTC');
53+
$app->setCompression(true);
5354
$app->run($request, $response);

0 commit comments

Comments
 (0)