diff --git a/.gitignore b/.gitignore
index 7166c792c..c7c94da5a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,5 +3,5 @@
/tests/Bridge/Symfony/cache
/tests/Bridge/Symfony/logs
/.bref/
-/.php_cs.cache
+/.phpcs-cache
/composer.lock
diff --git a/.php_cs b/.php_cs
deleted file mode 100644
index 2e0b42b84..000000000
--- a/.php_cs
+++ /dev/null
@@ -1,17 +0,0 @@
-in(__DIR__)
- ->exclude([
- '.bref',
- 'vendor',
- 'tests/Bridge/Symfony/cache',
- 'tests/Bridge/Symfony/logs',
- ]);
-
-return PhpCsFixer\Config::create()
- ->setRules([
- '@PSR2' => true,
- '@PHP70Migration' => true,
- ])
- ->setFinder($finder);
diff --git a/.phpcs.xml.dist b/.phpcs.xml.dist
new file mode 100644
index 000000000..b90a62cce
--- /dev/null
+++ b/.phpcs.xml.dist
@@ -0,0 +1,146 @@
+
+
+
+
+
+
+
+
+
+
+ src
+ tests
+ tests/Bridge/Symfony/cache
+ tests/Bridge/Symfony/logs
+ tests/Bridge/Laravel/bootstrap/cache
+
+
+
+
+
+
+ 0
+
+
+
+
+ 0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0
+
+
+
+
+ 0
+
+
+
+
+
+ 0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0
+
+
+
+
+ 0
+
+
+
+
+ 0
+
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+
+
+
+
+
+
+ tests/*
+
+
+
+
+ tests/*
+
+
+
diff --git a/.prettyci.composer.json b/.prettyci.composer.json
new file mode 100644
index 000000000..5ae6e7a5a
--- /dev/null
+++ b/.prettyci.composer.json
@@ -0,0 +1,5 @@
+{
+ "require": {
+ "doctrine/coding-standard": "^5.0"
+ }
+}
diff --git a/.travis.yml b/.travis.yml
index c926d335c..59880043b 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -5,7 +5,6 @@ notifications:
on_success: never
php:
- - 7.1
- 7.2
- nightly
@@ -14,7 +13,7 @@ matrix:
allow_failures:
- php: nightly
include:
- - php: 7.1
+ - php: 7.2
env: dependencies=lowest
cache:
@@ -27,3 +26,4 @@ before_script:
script:
- vendor/bin/phpunit
+ - vendor/bin/phpstan analyse
diff --git a/bref b/bref
index 9e6d1bc47..1f8256873 100755
--- a/bref
+++ b/bref
@@ -8,8 +8,6 @@ use Symfony\Component\Console\Style\SymfonyStyle;
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\Process\Process;
-const DEFAULT_PHP_TARGET_VERSION = '7.2.5';
-
if (file_exists(__DIR__ . '/vendor/autoload.php')) {
require_once __DIR__ . '/vendor/autoload.php';
} elseif (file_exists(__DIR__ . '/../autoload.php')) {
diff --git a/composer.json b/composer.json
index 097c63175..16ecfc5dd 100644
--- a/composer.json
+++ b/composer.json
@@ -10,11 +10,16 @@
"src/functions.php"
]
},
+ "autoload-dev": {
+ "psr-4": {
+ "Bref\\Test\\": "tests"
+ }
+ },
"bin": [
"bref"
],
"require": {
- "php": "^7.1.0",
+ "php": "^7.2.0",
"ext-json": "*",
"mnapoli/silly": "^1.7",
"symfony/filesystem": "^3.1|^4.0",
@@ -28,16 +33,18 @@
"zendframework/zend-diactoros": "^1.6",
"jolicode/jolinotif": "^2.0",
"matomo/ini": "^2.0",
- "riverline/multipart-parser": "^1.2"
+ "riverline/multipart-parser": "^1.2",
+ "innmind/json": "^1.0"
},
"require-dev": {
"phpunit/phpunit": "^6.5",
"symfony/symfony": "^3.4|^4.0",
"symfony/debug": "^3.1|^4.0",
"slim/slim": "^3.9",
- "friendsofphp/php-cs-fixer": "^2.4",
"illuminate/contracts": "^5.0",
"illuminate/http": "^5.0",
- "laravel/framework": "^5.6"
+ "laravel/framework": "^5.6",
+ "doctrine/coding-standard": "^5.0",
+ "phpstan/phpstan": "^0.10.5"
}
}
diff --git a/phpstan.neon.dist b/phpstan.neon.dist
new file mode 100644
index 000000000..e1608abc4
--- /dev/null
+++ b/phpstan.neon.dist
@@ -0,0 +1,10 @@
+parameters:
+ level: 5
+ paths:
+ - src
+ - tests
+ excludes_analyse:
+ - %rootDir%/../../../tests/Bridge/Laravel/bootstrap/cache/*
+ - %rootDir%/../../../tests/Bridge/Laravel/storage/*
+ - %rootDir%/../../../tests/Bridge/Symfony/cache/*
+ - %rootDir%/../../../tests/Bridge/Symfony/logs/*
diff --git a/src/Application.php b/src/Application.php
index 1ac589cff..00cff101a 100644
--- a/src/Application.php
+++ b/src/Application.php
@@ -1,5 +1,4 @@
-
- */
class Application
{
/**
@@ -27,19 +24,13 @@ class Application
private const BREF_DIRECTORY = '/tmp/.bref';
private const OUTPUT_FILE_NAME = self::BREF_DIRECTORY . '/output.json';
- /**
- * @var callable
- */
+ /** @var callable */
private $simpleHandler;
- /**
- * @var RequestHandlerInterface
- */
+ /** @var RequestHandlerInterface */
private $httpHandler;
- /**
- * @var \Symfony\Component\Console\Application
- */
+ /** @var \Symfony\Component\Console\Application */
private $cliHandler;
public function __construct()
@@ -58,7 +49,7 @@ public function __construct()
*
* @param callable $handler This callable takes a $event parameter (array) and must return anything serializable to JSON.
*/
- public function simpleHandler(callable $handler) : void
+ public function simpleHandler(callable $handler): void
{
$this->simpleHandler = $handler;
}
@@ -69,7 +60,7 @@ public function simpleHandler(callable $handler) : void
* The handler must be a PSR-15 request handler, it can be any
* framework that is compatible with PSR-15 for example.
*/
- public function httpHandler(RequestHandlerInterface $handler) : void
+ public function httpHandler(RequestHandlerInterface $handler): void
{
$this->httpHandler = $handler;
}
@@ -84,7 +75,7 @@ public function httpHandler(RequestHandlerInterface $handler) : void
* That can also be a Silly (https://github.com/mnapoli/silly) application
* since Silly is based on the Symfony Console.
*/
- public function cliHandler(\Symfony\Component\Console\Application $console) : void
+ public function cliHandler(\Symfony\Component\Console\Application $console): void
{
// Necessary to avoid any `exit()` call :)
$console->setAutoExit(false);
@@ -103,10 +94,10 @@ public function cliHandler(\Symfony\Component\Console\Application $console) : vo
* The application will detect how the lambda is being invoked (HTTP,
* CLI, direct invocation, etc.) and execute the proper handler.
*/
- public function run() : void
+ public function run(): void
{
- if (!$this->isRunningInAwsLambda()) {
- if (php_sapi_name() == "cli") {
+ if (! $this->isRunningInAwsLambda()) {
+ if (PHP_SAPI === 'cli') {
$this->cliHandler->setAutoExit(true);
$this->cliHandler->run();
} else {
@@ -132,20 +123,20 @@ public function run() : void
$cliInput = new StringInput($event['cli']);
$cliOutput = new BufferedOutput;
$exitCode = $this->cliHandler->run($cliInput, $cliOutput);
- $output = json_encode([
+ $output = Json::encode([
'exitCode' => $exitCode,
'output' => $cliOutput->fetch(),
]);
} else {
// Simple invocation
$output = ($this->simpleHandler)($event);
- $output = json_encode($output);
+ $output = Json::encode($output);
}
$this->writeLambdaOutput($output);
}
- private function ensureTempDirectoryExists() : void
+ private function ensureTempDirectoryExists(): void
{
$filesystem = new Filesystem;
if (! $filesystem->exists(self::BREF_DIRECTORY)) {
@@ -153,19 +144,19 @@ private function ensureTempDirectoryExists() : void
}
}
- private function readLambdaEvent() : array
+ private function readLambdaEvent(): array
{
// The lambda event is passed as JSON by `handler.js` as a CLI argument
- global $argv;
- return json_decode($argv[1], true) ?: [];
+ $argv = $_SERVER['argv'];
+ return Json::decode($argv[1]) ?: [];
}
- private function writeLambdaOutput(string $json) : void
+ private function writeLambdaOutput(string $json): void
{
file_put_contents(self::OUTPUT_FILE_NAME, $json);
}
- private function isRunningInAwsLambda() : bool
+ private function isRunningInAwsLambda(): bool
{
// LAMBDA_TASK_ROOT is a constant defined by AWS
// TODO: use a solution that would work with other hosts?
diff --git a/src/Bridge/Laravel/Application.php b/src/Bridge/Laravel/Application.php
index 4393b8080..915de48ba 100644
--- a/src/Bridge/Laravel/Application.php
+++ b/src/Bridge/Laravel/Application.php
@@ -1,5 +1,4 @@
-isArtisanConsole !== null) {
@@ -55,7 +55,7 @@ public function runningInConsole()
return parent::runningInConsole();
}
- public function overrideRunningInConsole(bool $value)
+ public function overrideRunningInConsole(bool $value): void
{
$this->isArtisanConsole = $value;
}
diff --git a/src/Bridge/Laravel/LaravelAdapter.php b/src/Bridge/Laravel/LaravelAdapter.php
index 9c2e4106c..991fcab11 100644
--- a/src/Bridge/Laravel/LaravelAdapter.php
+++ b/src/Bridge/Laravel/LaravelAdapter.php
@@ -1,9 +1,10 @@
-
*/
class LaravelAdapter implements RequestHandlerInterface
{
- /**
- * @var Kernel
- */
+ /** @var Kernel */
private $kernel;
public function __construct(Kernel $kernel)
@@ -27,7 +24,7 @@ public function __construct(Kernel $kernel)
$this->kernel = $kernel;
}
- public function handle(ServerRequestInterface $request) : ResponseInterface
+ public function handle(ServerRequestInterface $request): ResponseInterface
{
// Create a Symfony request that will be used by Laravel
$httpFoundationFactory = new HttpFoundationFactory;
@@ -36,19 +33,17 @@ public function handle(ServerRequestInterface $request) : ResponseInterface
// We create Laravel's HTTP request from the Symfony request
// We cannot use Symfony's request directly because the Kernel's implementation
// expects a `Illuminate\Http\Request` implementation.
- $laravelRequest = \Illuminate\Http\Request::createFromBase($symfonyRequest);
+ $laravelRequest = Request::createFromBase($symfonyRequest);
// Laravel does not forward the headers from the Symfony request
// we need to do that explicitly :'(
$laravelRequest->headers->replace($symfonyRequest->headers->all());
- /** @var \Illuminate\Http\Response $laravelResponse */
+ /** @var Response $laravelResponse */
$laravelResponse = $this->kernel->handle($laravelRequest);
$this->kernel->terminate($laravelRequest, $laravelResponse);
$psr7Factory = new DiactorosFactory;
// The Laravel response extends Symfony so this works fine here
- $response = $psr7Factory->createResponse($laravelResponse);
-
- return $response;
+ return $psr7Factory->createResponse($laravelResponse);
}
}
diff --git a/src/Bridge/Psr7/RequestFactory.php b/src/Bridge/Psr7/RequestFactory.php
index 5a69a6c35..04b315dee 100644
--- a/src/Bridge/Psr7/RequestFactory.php
+++ b/src/Bridge/Psr7/RequestFactory.php
@@ -1,5 +1,4 @@
-
*/
class RequestFactory
{
/**
* Create a PSR-7 server request from an AWS Lambda HTTP event.
*/
- public static function fromLambdaEvent(array $event) : ServerRequestInterface
+ public static function fromLambdaEvent(array $event): ServerRequestInterface
{
$method = $event['httpMethod'] ?? 'GET';
$query = [];
@@ -63,13 +60,16 @@ public static function fromLambdaEvent(array $event) : ServerRequestInterface
if ($contentType === 'application/x-www-form-urlencoded') {
parse_str($bodyString, $parsedBody);
} else {
- $document = new Part("Content-type: $contentType\r\n\r\n".$bodyString);
+ $document = new Part("Content-type: $contentType\r\n\r\n" . $bodyString);
if ($document->isMultiPart()) {
$parsedBody = [];
foreach ($document->getParts() as $part) {
if ($part->isFile()) {
$tmpPath = tempnam(sys_get_temp_dir(), 'bref_upload_');
+ if ($tmpPath === false) {
+ throw new \RuntimeException('Unable to create a temporary directory');
+ }
file_put_contents($tmpPath, $part->getBody());
$file = new UploadedFile($tmpPath, filesize($tmpPath), UPLOAD_ERR_OK, $part->getFileName(), $part->getMimeType());
@@ -110,7 +110,7 @@ public static function fromLambdaEvent(array $event) : ServerRequestInterface
);
}
- private static function createBodyStream(string $body) : StreamInterface
+ private static function createBodyStream(string $body): StreamInterface
{
$stream = fopen('php://memory', 'r+');
fwrite($stream, $body);
@@ -121,9 +121,10 @@ private static function createBodyStream(string $body) : StreamInterface
/**
* Parse a string key like "files[id_cards][jpg][]" and do $array['files']['id_cards']['jpg'][] = $value
+ *
* @param mixed $value
*/
- private static function parseKeyAndInsertValueInArray(array &$array, string $key, $value) : void
+ private static function parseKeyAndInsertValueInArray(array &$array, string $key, $value): void
{
if (strpos($key, '[') === false) {
$array[$key] = $value;
diff --git a/src/Bridge/Slim/SlimAdapter.php b/src/Bridge/Slim/SlimAdapter.php
index 11e88b448..bbd045331 100644
--- a/src/Bridge/Slim/SlimAdapter.php
+++ b/src/Bridge/Slim/SlimAdapter.php
@@ -1,5 +1,4 @@
-
*/
class SlimAdapter implements RequestHandlerInterface
{
- /**
- * @var App
- */
+ /** @var App */
private $slim;
public function __construct(App $slim)
@@ -25,7 +20,7 @@ public function __construct(App $slim)
$this->slim = $slim;
}
- public function handle(ServerRequestInterface $request) : ResponseInterface
+ public function handle(ServerRequestInterface $request): ResponseInterface
{
$response = $this->slim->getContainer()->get('response');
diff --git a/src/Bridge/Symfony/SymfonyAdapter.php b/src/Bridge/Symfony/SymfonyAdapter.php
index a8a43a28d..d37c3b23b 100644
--- a/src/Bridge/Symfony/SymfonyAdapter.php
+++ b/src/Bridge/Symfony/SymfonyAdapter.php
@@ -1,5 +1,4 @@
-
*/
class SymfonyAdapter implements RequestHandlerInterface
{
- /**
- * @var HttpKernelInterface
- */
+ /** @var KernelInterface */
private $httpKernel;
- public function __construct(HttpKernelInterface $httpKernel)
+ public function __construct(KernelInterface $httpKernel)
{
$this->httpKernel = $httpKernel;
}
@@ -54,9 +50,11 @@ private function loadSessionFromRequest(Request $symfonyRequest): string
return '';
}
- $this->httpKernel->getContainer()->get('session')->setId(
- $sessionId = $symfonyRequest->cookies->get(session_name(), '')
- );
+ $sessionId = $symfonyRequest->cookies->get(session_name(), '');
+
+ /** @var SessionInterface $session */
+ $session = $this->httpKernel->getContainer()->get('session');
+ $session->setId($sessionId);
return $sessionId;
}
@@ -67,7 +65,9 @@ private function addSessionCookieToResponseIfChanged(?string $requestSessionId,
return;
}
- $responseSessionId = $this->httpKernel->getContainer()->get('session')->getId();
+ /** @var SessionInterface $session */
+ $session = $this->httpKernel->getContainer()->get('session');
+ $responseSessionId = $session->getId();
if ($requestSessionId === $responseSessionId) {
return;
@@ -92,6 +92,6 @@ private function addSessionCookieToResponseIfChanged(?string $requestSessionId,
private function hasSessionsDisabled(): bool
{
- return false === $this->httpKernel->getContainer()->has('session');
+ return $this->httpKernel->getContainer()->has('session') === false;
}
}
diff --git a/src/Cli/InvokeCommand.php b/src/Cli/InvokeCommand.php
index 1e3dc8faf..3dae4329a 100644
--- a/src/Cli/InvokeCommand.php
+++ b/src/Cli/InvokeCommand.php
@@ -1,8 +1,9 @@
-
*/
class InvokeCommand extends Command
{
- /**
- * @var callable
- */
+ /** @var callable */
private $invokerLocator;
public function __construct(callable $invokerLocator)
@@ -27,46 +24,66 @@ public function __construct(callable $invokerLocator)
parent::__construct();
}
- protected function configure()
+ protected function configure(): void
{
$this
->setName('bref:invoke')
->setDescription('Invoke the lambda locally when testing it in a development environment.')
->setHelp('This command does NOT run the lambda on a serverless provider. It can be used to test the lambda in a "direct invocation" mode on a development machine.')
->addOption('event', 'e', InputOption::VALUE_REQUIRED, 'Event data as JSON')
- ->addOption('path', 'p', InputOption::VALUE_REQUIRED, 'Event data as file')
- ;
+ ->addOption('path', 'p', InputOption::VALUE_REQUIRED, 'Event data as file');
}
- protected function execute(InputInterface $input, OutputInterface $output)
+ protected function execute(InputInterface $input, OutputInterface $output): void
{
$simpleHandler = ($this->invokerLocator)();
$event = [];
- if ($option = $input->getOption('event')) {
- if (null === $event = json_decode($option, true)) {
- throw new \RuntimeException('The `--event` option provided contains invalid JSON: ' . $option);
+ $eventOption = $input->getOption('event');
+ if ($eventOption) {
+ $eventOption = (string) $eventOption;
+ try {
+ $event = Json::decode($eventOption);
+ } catch (RuntimeException $e) {
+ throw new \RuntimeException("The `--event` option provided contains invalid JSON: $eventOption", 0, $e);
}
}
- if ($option = $input->getOption('path')) {
- if (!$path = realpath($option)) {
- throw new \RuntimeException('The `--path` option is an invalid path: ' . $option);
- }
- if (!is_readable($path)) {
- throw new \RuntimeException('The `--path` option reference an invalid file path or misses permission: ' . $option);
- }
- if (!$fileContent = file_get_contents($path)) {
- throw new \RuntimeException('Unable to get file content:' . $option);
- }
- if (null === $event = json_decode($fileContent, true)) {
- throw new \RuntimeException('The `--path` option provided an file with invalid JSON content: ' . $option);
- }
+ $pathOption = $input->getOption('path');
+ if ($pathOption) {
+ $event = $this->readPathOption($pathOption);
}
$payload = $simpleHandler($event);
$output->writeln(json_encode($payload, JSON_PRETTY_PRINT));
}
+
+ /**
+ * @return mixed
+ */
+ private function readPathOption(string $option)
+ {
+ $path = realpath($option);
+ if (! $path) {
+ throw new \RuntimeException('The `--path` option is an invalid path: ' . $option);
+ }
+
+ if (! is_readable($path)) {
+ throw new \RuntimeException('The `--path` option reference an invalid file path or misses permission: ' . $option);
+ }
+
+ $fileContent = file_get_contents($path);
+ if (! $fileContent) {
+ throw new \RuntimeException('Unable to get file content:' . $option);
+ }
+
+ $event = json_decode($fileContent, true);
+ if ($event === null) {
+ throw new \RuntimeException('The `--path` option provided an file with invalid JSON content: ' . $option);
+ }
+
+ return $event;
+ }
}
diff --git a/src/Cli/WelcomeApplication.php b/src/Cli/WelcomeApplication.php
index 34bde3826..c858b6ff7 100644
--- a/src/Cli/WelcomeApplication.php
+++ b/src/Cli/WelcomeApplication.php
@@ -1,5 +1,4 @@
-
*/
class WelcomeApplication extends Application
{
@@ -17,7 +14,7 @@ public function __construct()
{
parent::__construct();
- $this->command('hello', function (SymfonyStyle $io) {
+ $this->command('hello', function (SymfonyStyle $io): void {
$io->writeln('Welcome! This CLI application is working but has no commands.');
$io->writeln([
'Add your own CLI application by registering a Symfony Console application'
@@ -31,7 +28,7 @@ public function __construct()
/**
* Disable the default commands (help and list).
*/
- protected function getDefaultCommands()
+ protected function getDefaultCommands(): array
{
return [];
}
diff --git a/src/Console/Deployer.php b/src/Console/Deployer.php
index 03f070860..b234b7179 100644
--- a/src/Console/Deployer.php
+++ b/src/Console/Deployer.php
@@ -1,5 +1,4 @@
-createProgressBar($io, 7);
@@ -46,7 +47,7 @@ public function invoke(SymfonyStyle $io, string $function, ?string $data, bool $
'--raw' => $raw,
]);
- $p = join(' ', array_map(
+ $p = implode(' ', array_map(
function ($value, $key) {
if ($value === true) {
// Support for "flag" arguments
@@ -67,23 +68,23 @@ function ($value, $key) {
return $process->getOutput();
}
- public function deploy(SymfonyStyle $io, bool $dryRun, ?string $stage) : void
+ public function deploy(SymfonyStyle $io, bool $dryRun, ?string $stage): void
{
$progress = $this->createProgressBar($io, 8);
$this->generateArchive($progress);
- if (!$dryRun) {
+ if (! $dryRun) {
$progress->setMessage('Uploading the lambda');
$progress->display();
$serverlessCommand = 'serverless deploy';
- if (null !== $stage) {
+ if ($stage !== null) {
$serverlessCommand .= ' --stage ' . escapeshellarg($stage);
}
$process = new Process($serverlessCommand, '.bref/output');
$process->setTimeout(null);
$completeDeployOutput = '';
- $process->mustRun(function ($type, $buffer) use ($io, $progress, &$completeDeployOutput) {
+ $process->mustRun(function ($type, $buffer) use ($io, $progress, &$completeDeployOutput): void {
$completeDeployOutput .= $buffer;
$progress->clear();
$io->writeln($buffer);
@@ -109,9 +110,9 @@ public function deploy(SymfonyStyle $io, bool $dryRun, ?string $stage) : void
* @param ProgressBar $progress The progress bar will advance of 7 steps.
* @throws \Exception
*/
- private function generateArchive(ProgressBar $progress) : void
+ private function generateArchive(ProgressBar $progress): void
{
- if (!$this->fs->exists('serverless.yml') || !$this->fs->exists('bref.php')) {
+ if (! $this->fs->exists('serverless.yml') || ! $this->fs->exists('bref.php')) {
throw new \Exception('The files `bref.php` and `serverless.yml` are required to deploy, run `bref init` to create them');
}
@@ -125,7 +126,7 @@ private function generateArchive(ProgressBar $progress) : void
* error if there are unknown keys. Using the Symfony Config component
* for that could make sense.
*/
- $projectConfig = Yaml::parse(file_get_contents('.bref.yml'));
+ $projectConfig = Yaml::parse($this->readContent('.bref.yml'));
}
$progress->advance();
@@ -136,11 +137,11 @@ private function generateArchive(ProgressBar $progress) : void
// Cache PHP's binary in `.bref/bin/php` to avoid downloading it
// on every deploy.
- $phpVersion = $projectConfig['php']['version'] ?? DEFAULT_PHP_TARGET_VERSION;
+ $phpVersion = $projectConfig['php']['version'] ?? self::DEFAULT_PHP_TARGET_VERSION;
$progress->setMessage('Downloading PHP in the `.bref/bin/` directory');
$progress->display();
- if (!$this->fs->exists('.bref/bin/php/php-' . $phpVersion . '.tar.gz')) {
+ if (! $this->fs->exists('.bref/bin/php/php-' . $phpVersion . '.tar.gz')) {
$this->fs->mkdir('.bref/bin/php');
/*
* TODO This option allows to customize the PHP binary used. It should be documented
@@ -150,7 +151,7 @@ private function generateArchive(ProgressBar $progress) : void
*/
$defaultUrl = 'https://s3.amazonaws.com/bref-php/bin/php-' . $phpVersion . '.tar.gz';
$url = $projectConfig['php']['url'] ?? $defaultUrl;
- (new Process("curl -sSL $url -o .bref/bin/php/php-" . $phpVersion . ".tar.gz"))
+ (new Process("curl -sSL $url -o .bref/bin/php/php-$phpVersion.tar.gz"))
->setTimeout(null)
->mustRun();
}
@@ -159,13 +160,13 @@ private function generateArchive(ProgressBar $progress) : void
$progress->setMessage('Installing the PHP binary');
$progress->display();
$this->fs->mkdir('.bref/output/.bref/bin');
- (new Process('tar -xzf .bref/bin/php/php-' . $phpVersion . '.tar.gz -C .bref/output/.bref/bin'))
+ (new Process("tar -xzf .bref/bin/php/php-$phpVersion.tar.gz -C .bref/output/.bref/bin"))
->mustRun();
// Set correct permissions on the file
$this->fs->chmod('.bref/output/.bref/bin', 0755);
// Install our custom php.ini and merge it with user configuration
$phpConfig = $this->buildPhpConfig(
- __DIR__ . '/../../template/php.ini',
+ __DIR__ . '/../../template/php.ini',
'.bref/output/.bref/php.ini',
$projectConfig['php']['configuration'] ?? [],
$projectConfig['php']['extensions'] ?? []
@@ -200,14 +201,14 @@ private function generateArchive(ProgressBar $progress) : void
$progress->advance();
}
- private function runLocally(string $command) : void
+ private function runLocally(string $command): void
{
$process = new Process($command, '.bref/output');
$process->setTimeout(null);
$process->mustRun();
}
- private function createProgressBar(SymfonyStyle $io, int $max) : ProgressBar
+ private function createProgressBar(SymfonyStyle $io, int $max): ProgressBar
{
ProgressBar::setFormatDefinition('bref', "%message%\n %current%/%max% [%bar%] %elapsed:6s%");
@@ -223,9 +224,9 @@ private function createProgressBar(SymfonyStyle $io, int $max) : ProgressBar
/**
* Pre-process the `serverless.yml` file and copy it in the lambda directory.
*/
- private function copyServerlessYml() : void
+ private function copyServerlessYml(): void
{
- $serverlessYml = Yaml::parse(file_get_contents('serverless.yml'));
+ $serverlessYml = Yaml::parse($this->readContent('serverless.yml'));
// Force deploying the files used by Bref without having the user know about them
$serverlessYml['package']['include'][] = 'handler.js';
@@ -234,9 +235,9 @@ private function copyServerlessYml() : void
file_put_contents('.bref/output/serverless.yml', Yaml::dump($serverlessYml, 10));
}
- private function copyProjectToOutputDirectory() : void
+ private function copyProjectToOutputDirectory(): void
{
- if (!$this->fs->exists('.bref/output')) {
+ if (! $this->fs->exists('.bref/output')) {
$this->fs->mkdir('.bref/output');
}
@@ -261,14 +262,13 @@ private function copyProjectToOutputDirectory() : void
private function buildPhpConfig(string $sourceFile, string $targetFile, array $flags, array $extensions): array
{
$config = array_merge(
- ['flags' => array_merge(
- (new IniReader())->readFile($sourceFile),
- $flags
- )],
+ [
+ 'flags' => array_merge((new IniReader)->readFile($sourceFile), $flags),
+ ],
array_combine(
$extensions,
array_map(function ($extension) {
- if (!$this->fs->exists('.bref/output/.bref/bin/ext/' . $extension . '.so')) {
+ if (! $this->fs->exists(".bref/output/.bref/bin/ext/$extension.so")) {
throw new \Exception("The PHP extension '$extension' is not available yet in Bref, please open an issue or a pull request on GitHub to add that extension");
}
@@ -277,30 +277,31 @@ private function buildPhpConfig(string $sourceFile, string $targetFile, array $f
)
);
- (new IniWriter())->writeToFile($targetFile, $config);
+ (new IniWriter)->writeToFile($targetFile, $config);
return $config;
}
- private function removeUnusedExtensions(array $phpConfig)
+ private function removeUnusedExtensions(array $phpConfig): void
{
foreach (glob('.bref/output/.bref/bin/ext/*.so') as $extensionFile) {
if ($extensionFile === '.bref/output/.bref/bin/ext/opcache.so') {
continue;
}
- if (!array_key_exists(basename($extensionFile, '.so'), $phpConfig)) {
+ if (! array_key_exists(basename($extensionFile, '.so'), $phpConfig)) {
$this->fs->remove($extensionFile);
}
}
}
- private function removeUnusedLibraries(array $extensions)
+ private function removeUnusedLibraries(array $extensions): void
{
$dependencies = [];
$dependenciesFile = '.bref/output/.bref/bin/dependencies.yml';
if ($this->fs->exists($dependenciesFile)) {
- $dependencies = Yaml::parse(file_get_contents($dependenciesFile))['extensions'] ?? [];
+ $dependenciesConfig = Yaml::parse($this->readContent($dependenciesFile));
+ $dependencies = $dependenciesConfig['extensions'] ?? [];
$this->fs->remove($dependenciesFile);
}
@@ -309,9 +310,19 @@ private function removeUnusedLibraries(array $extensions)
));
foreach (glob('.bref/output/.bref/bin/lib/**') as $library) {
- if (!in_array(basename($library), $requiredLibraries)) {
+ if (! in_array(basename($library), $requiredLibraries)) {
$this->fs->remove($library);
}
}
}
+
+ private function readContent(string $file): string
+ {
+ $content = file_get_contents($file);
+ if ($content === false) {
+ throw new \RuntimeException("Unable to read the `$file` file");
+ }
+
+ return $content;
+ }
}
diff --git a/src/Filesystem/DirectoryMirror.php b/src/Filesystem/DirectoryMirror.php
index 1988c081d..a0bbec3b0 100644
--- a/src/Filesystem/DirectoryMirror.php
+++ b/src/Filesystem/DirectoryMirror.php
@@ -1,5 +1,4 @@
-
+ * Mirrors a directory into another on the filesystem.
*/
class DirectoryMirror
{
- /**
- * @var Filesystem
- */
+ /** @var Filesystem */
private $fs;
public function __construct(Filesystem $fs)
@@ -30,14 +27,14 @@ public function __construct(Filesystem $fs)
*
* @throws IOException
*/
- public function mirror(Finder $source, Finder $target) : void
+ public function mirror(Finder $source, Finder $target): void
{
[$sourceFiles, $targetFiles] = $this->indexArraysByRelativePath($source, $target);
$filesToCreate = array_diff_key($sourceFiles, $targetFiles);
$filesToDelete = array_diff_key($targetFiles, $sourceFiles);
$filesToVerify = array_intersect_key($sourceFiles, $targetFiles);
- $filesToUpdate = array_filter($filesToVerify, function (int $sourceCTime, string $sourceRelativePath) use ($targetFiles) : bool {
+ $filesToUpdate = array_filter($filesToVerify, function (int $sourceCTime, string $sourceRelativePath) use ($targetFiles): bool {
assert(isset($targetFiles[$sourceRelativePath]));
$targetCTime = $targetFiles[$sourceRelativePath];
/*
@@ -57,9 +54,9 @@ public function mirror(Finder $source, Finder $target) : void
}
/**
- * @param SplFileInfo[] $filesToCreate
+ * @param int[] $filesToCreate
*/
- private function createMissingFiles(array $filesToCreate) : void
+ private function createMissingFiles(array $filesToCreate): void
{
foreach ($filesToCreate as $relativePath => $cTime) {
$targetPath = '.bref/output/' . $relativePath;
@@ -73,9 +70,9 @@ private function createMissingFiles(array $filesToCreate) : void
}
/**
- * @param SplFileInfo[] $filesToDelete
+ * @param int[] $filesToDelete
*/
- private function deleteExtraFiles(array $filesToDelete) : void
+ private function deleteExtraFiles(array $filesToDelete): void
{
foreach ($filesToDelete as $relativePath => $cTime) {
$targetPath = '.bref/output/' . $relativePath;
@@ -85,9 +82,9 @@ private function deleteExtraFiles(array $filesToDelete) : void
}
/**
- * @param SplFileInfo[] $filesToUpdate
+ * @param int[] $filesToUpdate
*/
- private function updateChangedFiles(array $filesToUpdate) : void
+ private function updateChangedFiles(array $filesToUpdate): void
{
foreach ($filesToUpdate as $relativePath => $cTime) {
$targetPath = '.bref/output/' . $relativePath;
@@ -96,11 +93,10 @@ private function updateChangedFiles(array $filesToUpdate) : void
$this->fs->remove($targetPath);
$this->fs->copy($relativePath, $targetPath);
} else {
+ // TODO sync permissions if $targetPath is a directory?
if (is_file($targetPath)) {
$this->fs->remove($targetPath);
$this->fs->mkdir($targetPath);
- } else {
- // TODO sync permissions?
}
}
}
@@ -109,7 +105,7 @@ private function updateChangedFiles(array $filesToUpdate) : void
/**
* @return int[][]
*/
- private function indexArraysByRelativePath(Finder $source, Finder $target) : array
+ private function indexArraysByRelativePath(Finder $source, Finder $target): array
{
$sourceFiles = iterator_to_array($this->indexFilesCTimeByRelativePath($source));
$targetFiles = iterator_to_array($this->indexFilesCTimeByRelativePath($target));
@@ -117,7 +113,7 @@ private function indexArraysByRelativePath(Finder $source, Finder $target) : arr
return [$sourceFiles, $targetFiles];
}
- private function indexFilesCTimeByRelativePath(iterable $files) : \Traversable
+ private function indexFilesCTimeByRelativePath(iterable $files): \Traversable
{
foreach ($files as $file) {
/** @var SplFileInfo $file */
diff --git a/src/Http/LambdaResponse.php b/src/Http/LambdaResponse.php
index 170b63f83..8a5087563 100644
--- a/src/Http/LambdaResponse.php
+++ b/src/Http/LambdaResponse.php
@@ -1,30 +1,22 @@
-
*/
class LambdaResponse
{
- /**
- * @var int
- */
+ /** @var int */
private $statusCode = 200;
- /**
- * @var array
- */
+ /** @var array */
private $headers;
- /**
- * @var string
- */
+ /** @var string */
private $body;
public function __construct(int $statusCode, array $headers, string $body)
@@ -34,7 +26,7 @@ public function __construct(int $statusCode, array $headers, string $body)
$this->body = $body;
}
- public static function fromPsr7Response(ResponseInterface $response) : self
+ public static function fromPsr7Response(ResponseInterface $response): self
{
// The lambda proxy integration does not support arrays in headers
$headers = [];
@@ -54,7 +46,7 @@ public static function fromPsr7Response(ResponseInterface $response) : self
return new self($response->getStatusCode(), $headers, $body);
}
- public static function fromHtml(string $html) : self
+ public static function fromHtml(string $html): self
{
return new self(
200,
@@ -65,7 +57,7 @@ public static function fromHtml(string $html) : self
);
}
- public function toJson() : string
+ public function toJson(): string
{
// The headers must be a JSON object. If the PHP array is empty it is
// serialized to `[]` (we want `{}`) so we force it to an empty object.
@@ -73,7 +65,7 @@ public function toJson() : string
// This is the format required by the AWS_PROXY lambda integration
// See https://stackoverflow.com/questions/43708017/aws-lambda-api-gateway-error-malformed-lambda-proxy-response
- return json_encode([
+ return Json::encode([
'isBase64Encoded' => false,
'statusCode' => $this->statusCode,
'headers' => $headers,
diff --git a/src/Http/WelcomeHandler.php b/src/Http/WelcomeHandler.php
index 241375096..08d2ede80 100644
--- a/src/Http/WelcomeHandler.php
+++ b/src/Http/WelcomeHandler.php
@@ -1,5 +1,4 @@
-
*/
class WelcomeHandler implements RequestHandlerInterface
{
- public function handle(ServerRequestInterface $request) : ResponseInterface
+ public function handle(ServerRequestInterface $request): ResponseInterface
{
$html = file_get_contents(__DIR__ . '/welcome.html');
+ if ($html === false) {
+ throw new \RuntimeException('Unable to read the `welcome.html` template');
+ }
return new HtmlResponse($html);
}
diff --git a/src/functions.php b/src/functions.php
index 3f2413455..4dbacd30f 100644
--- a/src/functions.php
+++ b/src/functions.php
@@ -1,5 +1,6 @@
-simpleHandler($handler);
$app->run();
}
diff --git a/template/bref.php b/template/bref.php
index c720a1a26..984753c1c 100644
--- a/template/bref.php
+++ b/template/bref.php
@@ -1,4 +1,4 @@
-getBody());
}
- private function createLaravel() : Application
+ private function createLaravel(): Application
{
$app = new Application(__DIR__);
$app->singleton(Kernel::class, \Illuminate\Foundation\Http\Kernel::class);
diff --git a/tests/Bridge/Laravel/config/app.php b/tests/Bridge/Laravel/config/app.php
index b62512838..f0b60b479 100644
--- a/tests/Bridge/Laravel/config/app.php
+++ b/tests/Bridge/Laravel/config/app.php
@@ -1,4 +1,3 @@
- '/test',
'requestTimeEpoch' => $currentTimestamp,
],
- 'headers' => [
- ],
+ 'headers' => [],
]);
self::assertEquals('GET', $request->getMethod());
@@ -108,7 +106,7 @@ public function test multipart form data is supported()
'Content-Type' => 'multipart/form-data; boundary=testBoundary',
],
'body' =>
-"--testBoundary\r
+ "--testBoundary\r
Content-Disposition: form-data; name=\"foo\"\r
\r
bar\r
@@ -134,7 +132,7 @@ public function test cookies are supported()
self::assertEquals([
'tz' => 'Europe/Paris',
'four' => 'two + 2',
- 'theme' => 'light'
+ 'theme' => 'light',
], $request->getCookieParams());
}
@@ -151,10 +149,8 @@ public function test arrays in query string are supported()
self::assertEquals([
'vars' => [
'val1' => 'foo',
- 'val2' => [
- 'bar',
- ]
- ]
+ 'val2' => ['bar'],
+ ],
], $request->getQueryParams());
}
@@ -166,7 +162,7 @@ public function test arrays in name are supported with multipart form d
'Content-Type' => 'multipart/form-data; boundary=testBoundary',
],
'body' =>
-"--testBoundary\r
+ "--testBoundary\r
Content-Disposition: form-data; name=\"delete[categories][]\"\r
\r
123\r
@@ -180,10 +176,10 @@ public function test arrays in name are supported with multipart form d
self::assertEquals('POST', $request->getMethod());
self::assertEquals(
[
- 'delete' => [
- 'categories' => [
- '123',
- '456',
+ 'delete' => [
+ 'categories' => [
+ '123',
+ '456',
],
],
],
@@ -199,7 +195,7 @@ public function test files are supported with multipart form data()
'Content-Type' => 'multipart/form-data; boundary=testBoundary',
],
'body' =>
-"--testBoundary\r
+ "--testBoundary\r
Content-Disposition: form-data; name=\"foo\"; filename=\"lorem.txt\"\r
Content-Type: text/plain\r
\r
diff --git a/tests/Bridge/Slim/SlimAdapterTest.php b/tests/Bridge/Slim/SlimAdapterTest.php
index 515037e17..86d0692e1 100644
--- a/tests/Bridge/Slim/SlimAdapterTest.php
+++ b/tests/Bridge/Slim/SlimAdapterTest.php
@@ -1,5 +1,4 @@
-handle(new ServerRequest([], [], self::ROUTE_WITH_SESSION));
- $symfonySessionId = $kernel->getContainer()->get('session')->getId();
+ /** @var SessionInterface $session */
+ $session = $kernel->getContainer()->get('session');
+ $symfonySessionId = $session->getId();
+
self::assertEquals($symfonySessionId, (string) $response->getBody());
self::assertEquals(
- sprintf("%s=%s; path=/", \session_name(), $symfonySessionId),
+ sprintf('%s=%s; path=/', \session_name(), $symfonySessionId),
$response->getHeaders()['Set-Cookie'][0]
);
}
@@ -98,47 +101,47 @@ public function test an existing session is used when session provided()
self::assertEquals('SESSIONID', (string) $response->getBody());
}
- private function createKernel(): HttpKernelInterface
+ private function createKernel(): KernelInterface
{
$kernel = new class('dev', false) extends Kernel implements EventSubscriberInterface {
use MicroKernelTrait;
- public function registerBundles()
+ public function registerBundles(): array
{
return [new FrameworkBundle];
}
- protected function configureContainer(ContainerBuilder $c)
+ protected function configureContainer(ContainerBuilder $c): void
{
$c->register('session_storage', MockArraySessionStorage::class);
$c->loadFromExtension('framework', [
'secret' => 'foo',
'session' => [
- 'storage_id' => 'session_storage'
- ]
+ 'storage_id' => 'session_storage',
+ ],
]);
}
- protected function configureRoutes(RouteCollectionBuilder $routes)
+ protected function configureRoutes(RouteCollectionBuilder $routes): void
{
$routes->add('/', 'kernel:testActionWithoutSession');
$routes->add('/session', 'kernel:testActionWithSession');
}
- public function testActionWithoutSession()
+ public function testActionWithoutSession(): Response
{
return new Response('Hello world!');
}
- public function testActionWithSession(Session $session)
+ public function testActionWithSession(Session $session): Response
{
$session->set('ACTIVATE', 'SESSIONS'); // ensure that Symfony starts/uses sessions
return new Response($session->getId());
}
- public static function getSubscribedEvents()
+ public static function getSubscribedEvents(): array
{
return [KernelEvents::EXCEPTION => 'onKernelException'];
}
@@ -146,7 +149,7 @@ public static function getSubscribedEvents()
/**
* We have to handle NotFound exceptions ourselves because they are not handled by the micro-kernel
*/
- public function onKernelException(GetResponseForExceptionEvent $event)
+ public function onKernelException(GetResponseForExceptionEvent $event): void
{
if ($event->getException() instanceof NotFoundHttpException) {
$event->setResponse(new Response('Not found', 404));
diff --git a/tests/Cli/InvokeCommandTest.php b/tests/Cli/InvokeCommandTest.php
index 56a9bb33a..ef2ad5697 100644
--- a/tests/Cli/InvokeCommandTest.php
+++ b/tests/Cli/InvokeCommandTest.php
@@ -1,58 +1,47 @@
-command = new InvokeCommand(function () {
+ return function (array $event): string {
+ return 'Hello ' . $event['foo'];
};
});
}
- public function setUp()
- {
- $this->inputInterface = $this->prophesize(InputInterface::class);
- $this->inputInterface->bind(Argument::any())->willReturn(null);
- $this->inputInterface->isInteractive()->willReturn(false);
- $this->inputInterface->hasArgument(Argument::any())->willReturn(false);
- $this->inputInterface->validate()->willReturn(null);
- $this->outputInterface = $this->prophesize(OutputInterface::class);
- }
-
+ /**
+ * @expectedException \RuntimeException
+ * @expectedExceptionMessage The `--event` option provided contains invalid JSON: {fooo
+ */
public function test event json_decode fail()
{
- $this->expectException(\RuntimeException::class);
- $this->inputInterface->getOption(Argument::exact('event'))->willReturn(self::INVALID_JSON);
- self::$invokeCommand->run($this->inputInterface->reveal(), $this->outputInterface->reveal());
+ $this->command->run(new StringInput('--event {fooo'), new NullOutput);
}
public function test event json_decode success()
{
- $this->inputInterface->getOption(Argument::exact('event'))->willReturn(self::VALID_JSON);
- $this->inputInterface->getOption(Argument::exact('path'))->willReturn(null);
- $this->outputInterface->writeln(Argument::any())->shouldBeCalled(1);
- self::$invokeCommand->run($this->inputInterface->reveal(), $this->outputInterface->reveal());
+ $output = new BufferedOutput;
+
+ $this->command->run(new ArrayInput([
+ '--event' => '{"foo": "bar"}',
+ ]), $output);
+
+ self::assertEquals('"Hello bar"', trim($output->fetch()));
}
}
diff --git a/tests/CliTest.php b/tests/CliTest.php
index 0d4c76e7f..56d6566fb 100644
--- a/tests/CliTest.php
+++ b/tests/CliTest.php
@@ -1,5 +1,4 @@
-assertJsonPayload($response, [
'isBase64Encoded' => false,
'statusCode' => 204,
- 'headers' => [
- 'Foo' => 'baz',
- ],
+ 'headers' => ['Foo' => 'baz'],
'body' => '',
]);
}
diff --git a/tests/JsHandler/JsHandlerTest.php b/tests/JsHandler/JsHandlerTest.php
index 39ce98573..fe72ff0fa 100644
--- a/tests/JsHandler/JsHandlerTest.php
+++ b/tests/JsHandler/JsHandlerTest.php
@@ -1,5 +1,4 @@
-setEnv([
@@ -77,7 +76,7 @@ private static function assertProcessResult(
string $stdout,
string $stderr = '',
int $exitCode = 0
- ) {
+ ): void {
$fullOutput = $process->getOutput() . $process->getErrorOutput();
self::assertEquals($exitCode, $process->getExitCode(), $fullOutput);
@@ -85,13 +84,19 @@ private static function assertProcessResult(
self::assertEquals($stderr, $process->getErrorOutput());
}
- private static function assertLambdaResponse($expected) : void
+ /**
+ * @param mixed $expected
+ */
+ private static function assertLambdaResponse($expected): void
{
$response = json_decode(file_get_contents(__DIR__ . '/tmp/testResponse.json'), true);
self::assertEquals($expected, $response);
}
- private static function assertLambdaError($expected) : void
+ /**
+ * @param mixed $expected
+ */
+ private static function assertLambdaError($expected): void
{
$error = json_decode(file_get_contents(__DIR__ . '/tmp/testError.json'), true);
self::assertEquals($expected, $error);
diff --git a/tests/JsHandler/bref.array-response.php b/tests/JsHandler/bref.array-response.php
index 6a7b722c1..b4380034e 100644
--- a/tests/JsHandler/bref.array-response.php
+++ b/tests/JsHandler/bref.array-response.php
@@ -1,4 +1,4 @@
-