Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move logic to a trait #44

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
145 changes: 1 addition & 144 deletions src/BrefKernel.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,152 +2,9 @@

namespace Bref\SymfonyBridge;

use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\HttpKernel\Kernel;

abstract class BrefKernel extends Kernel
{
public function isLambda(): bool
{
return getenv('LAMBDA_TASK_ROOT') !== false;
}

/**
* {@inheritDoc}
*/
public function getCacheDir()
{
if ($this->isLambda()) {
return '/tmp/cache/' . $this->environment;
}

return parent::getCacheDir();
}

/**
* {@inheritDoc}
*/
public function getLogDir()
{
if ($this->isLambda()) {
return '/tmp/log/';
}

return parent::getLogDir();
}

/**
* {@inheritDoc}
*
* When on the lambda, copy the cache dir over to /tmp where it is writable
* We have to do this before Symfony does anything else with the Kernel
* otherwise it might prematurely warm the cache before we can copy it
*
* @see https://github.com/brefphp/symfony-bridge/pull/37
*/
public function handle($request, $type = HttpKernelInterface::MASTER_REQUEST, $catch = true)
{
$this->prepareCacheDir(parent::getCacheDir(), $this->getCacheDir());

return parent::handle($request, $type, $catch);
}

/**
* {@inheritdoc}
*
* We also need to prepare the Cache dir in Kernel::boot in case we are in a Console or worker context
* in which Kernel::handle is not called.
*/
public function boot()
{
$this->prepareCacheDir(parent::getCacheDir(), $this->getCacheDir());
$this->ensureLogDir($this->getLogDir());

parent::boot();
}

/**
* Return the pre-warmed directories in var/cache/[env] that should be copied to
* a writable directory in the Lambda environment.
*
* For optimal performance one should prewarm the cache folder and override this
* function to return an empty array.
*/
protected function getWritableCacheDirectories(): array
{
return ['pools'];
}

protected function prepareCacheDir(string $readOnlyDir, string $writeDir): void
{
if (! $this->isLambda() || is_dir($writeDir) || ! is_dir($readOnlyDir)) {
return;
}

$startTime = microtime(true);
$cacheDirectoriesToCopy = $this->getWritableCacheDirectories();
$filesystem = new Filesystem;
$filesystem->mkdir($writeDir);

$scandir = scandir($readOnlyDir, SCANDIR_SORT_NONE);
if ($scandir === false) {
return;
}

foreach ($scandir as $item) {
if (in_array($item, ['.', '..'])) {
continue;
}

// Copy directories to a writable space on Lambda.
if (in_array($item, $cacheDirectoriesToCopy)) {
$filesystem->mirror("$readOnlyDir/$item", "$writeDir/$item");
continue;
}

// Symlink all other directories
// This is especially important with the Container* directories since it uses require_once statements
if (is_dir("$readOnlyDir/$item")) {
$filesystem->symlink("$readOnlyDir/$item", "$writeDir/$item");
continue;
}

// Copy all other files.
$filesystem->copy("$readOnlyDir/$item", "$writeDir/$item");
}

$this->logToStderr(sprintf(
'Symfony cache directory prepared in %s ms.',
number_format((microtime(true) - $startTime) * 1000, 2)
));
}

/**
* Even though applications should never write into it on Lambda, there are parts of Symfony
* (like "about" CLI command) that expect the log dir exists, so we have to make sure of it.
*
* @see https://github.com/brefphp/symfony-bridge/issues/42
*/
private function ensureLogDir(string $writeLogDir): void
{
if (! $this->isLambda() || is_dir($writeLogDir)) {
return;
}

$filesystem = new Filesystem;
$filesystem->mkdir($writeLogDir);
}

/**
* This method logs to stderr.
*
* It must only be used in a lambda environment since all error output will be logged.
*
* @param string $message The message to log
*/
protected function logToStderr(string $message): void
{
file_put_contents('php://stderr', date('[c] ') . $message . PHP_EOL, FILE_APPEND);
}
use BrefKernelTrait;
}
152 changes: 152 additions & 0 deletions src/BrefKernelTrait.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
<?php declare(strict_types=1);

namespace Bref\SymfonyBridge;

use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\HttpKernel\HttpKernelInterface;

trait BrefKernelTrait
{
public function isLambda(): bool
{
return getenv('LAMBDA_TASK_ROOT') !== false;
}

/**
* {@inheritDoc}
*/
public function getCacheDir()
{
if ($this->isLambda()) {
return '/tmp/cache/' . $this->environment;
}

return parent::getCacheDir();
}

/**
* {@inheritDoc}
*/
public function getLogDir()
{
if ($this->isLambda()) {
return '/tmp/log/';
}

return parent::getLogDir();
}

/**
* {@inheritDoc}
*
* When on the lambda, copy the cache dir over to /tmp where it is writable
* We have to do this before Symfony does anything else with the Kernel
* otherwise it might prematurely warm the cache before we can copy it
*
* @see https://github.com/brefphp/symfony-bridge/pull/37
*/
public function handle($request, $type = HttpKernelInterface::MASTER_REQUEST, $catch = true)
{
$this->prepareCacheDir(parent::getCacheDir(), $this->getCacheDir());

return parent::handle($request, $type, $catch);
}

/**
* {@inheritdoc}
*
* We also need to prepare the Cache dir in Kernel::boot in case we are in a Console or worker context
* in which Kernel::handle is not called.
*/
public function boot()
{
$this->prepareCacheDir(parent::getCacheDir(), $this->getCacheDir());
$this->ensureLogDir($this->getLogDir());

parent::boot();
}

/**
* Return the pre-warmed directories in var/cache/[env] that should be copied to
* a writable directory in the Lambda environment.
*
* For optimal performance one should prewarm the cache folder and override this
* function to return an empty array.
*/
protected function getWritableCacheDirectories(): array
{
return ['pools'];
}

protected function prepareCacheDir(string $readOnlyDir, string $writeDir): void
{
if (! $this->isLambda() || is_dir($writeDir) || ! is_dir($readOnlyDir)) {
return;
}

$startTime = microtime(true);
$cacheDirectoriesToCopy = $this->getWritableCacheDirectories();
$filesystem = new Filesystem;
$filesystem->mkdir($writeDir);

$scandir = scandir($readOnlyDir, SCANDIR_SORT_NONE);
if ($scandir === false) {
return;
}

foreach ($scandir as $item) {
if (in_array($item, ['.', '..'])) {
continue;
}

// Copy directories to a writable space on Lambda.
if (in_array($item, $cacheDirectoriesToCopy)) {
$filesystem->mirror("$readOnlyDir/$item", "$writeDir/$item");
continue;
}

// Symlink all other directories
// This is especially important with the Container* directories since it uses require_once statements
if (is_dir("$readOnlyDir/$item")) {
$filesystem->symlink("$readOnlyDir/$item", "$writeDir/$item");
continue;
}

// Copy all other files.
$filesystem->copy("$readOnlyDir/$item", "$writeDir/$item");
}

$this->logToStderr(sprintf(
'Symfony cache directory prepared in %s ms.',
number_format((microtime(true) - $startTime) * 1000, 2)
));
}

/**
* Even though applications should never write into it on Lambda, there are parts of Symfony
* (like "about" CLI command) that expect the log dir exists, so we have to make sure of it.
*
* @see https://github.com/brefphp/symfony-bridge/issues/42
*/
private function ensureLogDir(string $writeLogDir): void
{
if (! $this->isLambda() || is_dir($writeLogDir)) {
return;
}

$filesystem = new Filesystem;
$filesystem->mkdir($writeLogDir);
}

/**
* This method logs to stderr.
*
* It must only be used in a lambda environment since all error output will be logged.
*
* @param string $message The message to log
*/
protected function logToStderr(string $message): void
{
file_put_contents('php://stderr', date('[c] ') . $message . PHP_EOL, FILE_APPEND);
}
}