This repository has been archived by the owner on Jul 21, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 11
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
added brotli compression support (#62)
- Loading branch information
1 parent
e6885d4
commit d62b0ff
Showing
9 changed files
with
223 additions
and
22 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
<?php | ||
/** | ||
* @copyright 2017 Hostnet B.V. | ||
*/ | ||
declare(strict_types=1); | ||
namespace Hostnet\Component\Resolver\EventListener; | ||
|
||
use Hostnet\Component\Resolver\Bundler\ContentItem; | ||
use Hostnet\Component\Resolver\Bundler\Runner\RunnerInterface; | ||
use Hostnet\Component\Resolver\Bundler\Runner\RunnerType; | ||
use Hostnet\Component\Resolver\Event\FileEvent; | ||
use Hostnet\Component\Resolver\File; | ||
use Hostnet\Component\Resolver\FileSystem\FileWriter; | ||
use Hostnet\Component\Resolver\FileSystem\StringReader; | ||
use Symfony\Component\EventDispatcher\EventDispatcher; | ||
|
||
/** | ||
* The brotli listener will output another file with a .br extension contain a brotli compressed asset. | ||
* | ||
* @see https://github.com/devongovett/brotli.js | ||
*/ | ||
class BrotliListener | ||
{ | ||
private $runner; | ||
|
||
private $dispatcher; | ||
|
||
private $project_root; | ||
|
||
public function __construct(RunnerInterface $runner, EventDispatcher $dispatcher, string $project_root) | ||
{ | ||
$this->runner = $runner; | ||
$this->dispatcher = $dispatcher; | ||
$this->project_root = $project_root; | ||
} | ||
|
||
/** | ||
* @param FileEvent $event | ||
*/ | ||
public function onPostWrite(FileEvent $event): void | ||
{ | ||
$file = $event->getFile(); | ||
// if the file is already compressed with brotli/gzip, do not compress it again as we do not serve files | ||
// like .br.gz.br | ||
if (preg_match('/\.(gz|br)$/', $file->path)) { | ||
return; | ||
} | ||
$content = $event->getContent(); | ||
$item = new ContentItem($file, $file->getName(), new StringReader($content)); | ||
$brotli_contents = $this->runner->execute(RunnerType::BROTLI, $item); | ||
// the runner returns an empty string if it could not be brotli compressed. This seems to be the | ||
// case for some of the binary files. Maybe we should blacklist binary files, but in general | ||
// any file could benefit from brotli compression. | ||
if (!empty($brotli_contents) && strlen($brotli_contents) < strlen($content)) { | ||
$writer = new FileWriter($this->dispatcher, $this->project_root); | ||
$writer->write(new File($file->path . '.br'), $brotli_contents); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
<?php | ||
/** | ||
* @copyright 2017 Hostnet B.V. | ||
*/ | ||
declare(strict_types=1); | ||
namespace Hostnet\Component\Resolver\Plugin; | ||
|
||
use Hostnet\Component\Resolver\Event\FileEvents; | ||
use Hostnet\Component\Resolver\EventListener\BrotliListener; | ||
|
||
/** | ||
* This plugin outputs a brotli-compressed file next to the output file. | ||
* Enabling this with the correct web server settings will serve a static brotli compressed file if the browser | ||
* accepts brotli encoding. Brotli is superior in compression size compared to gzip, but it is slower to dynamically | ||
* compress it, so a pre-compressed asset is recommended. | ||
*/ | ||
class BrotliPlugin implements PluginInterface | ||
{ | ||
public function activate(PluginApi $plugin_api): void | ||
{ | ||
$config = $plugin_api->getConfig(); | ||
$dispatcher = $config->getEventDispatcher(); | ||
$brotli_listener = new BrotliListener($plugin_api->getRunner(), $dispatcher, $config->getProjectRoot()); | ||
$dispatcher->addListener(FileEvents::POST_WRITE, [$brotli_listener, 'onPostWrite']); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
<?php | ||
/** | ||
* @copyright 2018 Hostnet B.V. | ||
*/ | ||
declare(strict_types=1); | ||
namespace Hostnet\Component\Resolver\EventListener; | ||
|
||
use Hostnet\Component\Resolver\Bundler\ContentItem; | ||
use Hostnet\Component\Resolver\Bundler\Runner\RunnerInterface; | ||
use Hostnet\Component\Resolver\Bundler\Runner\RunnerType; | ||
use Hostnet\Component\Resolver\Event\FileEvent; | ||
use Hostnet\Component\Resolver\File; | ||
use PHPUnit\Framework\TestCase; | ||
use Prophecy\Argument; | ||
use Symfony\Component\EventDispatcher\EventDispatcher; | ||
|
||
/** | ||
* @covers \Hostnet\Component\Resolver\EventListener\BrotliListener | ||
*/ | ||
class BrotliListenerTest extends TestCase | ||
{ | ||
public function testOnPostWrite() | ||
{ | ||
$file = new File(__FILE__); | ||
$contents = file_get_contents(__FILE__); | ||
|
||
$runner = $this->prophesize(RunnerInterface::class); | ||
$runner->execute( | ||
RunnerType::BROTLI, | ||
Argument::that(function (ContentItem $item) use ($contents, $file) { | ||
self::assertEquals($contents, $item->getContent()); | ||
self::assertEquals($file, $item->file); | ||
return true; | ||
}) | ||
)->willReturn('brotli.js'); | ||
$dispatcher = new EventDispatcher(); | ||
$project_root = __DIR__; | ||
|
||
$brotli_listener = new BrotliListener( | ||
$runner->reveal(), | ||
$dispatcher, | ||
$project_root | ||
); | ||
@unlink(__FILE__ . '.br'); | ||
try { | ||
$brotli_listener->onPostWrite(new FileEvent($file, $contents)); | ||
self::assertTrue(file_exists(__FILE__ . '.br')); | ||
} finally { | ||
@unlink(__FILE__ . '.br'); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
<?php | ||
/** | ||
* @copyright 2018 Hostnet B.V. | ||
*/ | ||
declare(strict_types=1); | ||
namespace Hostnet\Component\Resolver\Plugin; | ||
|
||
use Hostnet\Component\Resolver\Bundler\Runner\RunnerInterface; | ||
use Hostnet\Component\Resolver\Config\ConfigInterface; | ||
use Hostnet\Component\Resolver\Event\FileEvents; | ||
use PHPUnit\Framework\TestCase; | ||
use Symfony\Component\EventDispatcher\EventDispatcher; | ||
|
||
/** | ||
* @covers \Hostnet\Component\Resolver\Plugin\BrotliPlugin | ||
*/ | ||
class BrotliPluginTest extends TestCase | ||
{ | ||
public function testActivate() | ||
{ | ||
$brotli_plugin = new BrotliPlugin(); | ||
|
||
$event_dispatcher = new EventDispatcher(); | ||
|
||
$config = $this->prophesize(ConfigInterface::class); | ||
$config->getEventDispatcher()->willReturn($event_dispatcher); | ||
$config->getProjectRoot()->willReturn(__DIR__); | ||
|
||
$runner = $this->prophesize(RunnerInterface::class); | ||
|
||
$plugin_api = $this->prophesize(PluginApi::class); | ||
$plugin_api->getRunner()->wilLReturn($runner->reveal()); | ||
$plugin_api->getConfig()->willReturn($config->reveal()); | ||
|
||
self::assertFalse($event_dispatcher->hasListeners(FileEvents::POST_WRITE)); | ||
$brotli_plugin->activate($plugin_api->reveal()); | ||
self::assertTrue($event_dispatcher->hasListeners(FileEvents::POST_WRITE)); | ||
} | ||
} |