diff --git a/.platform.app.yaml b/.platform.app.yaml
index e6a072d..d415a84 100644
--- a/.platform.app.yaml
+++ b/.platform.app.yaml
@@ -6,6 +6,8 @@ build:
runtime:
extensions:
+ # Enable the event PHP extension, which provides a faster core event loop.
+ - event
- newrelic
-
name: 'blackfire'
@@ -42,16 +44,14 @@ relationships:
database: "database:mysql"
web:
- locations:
- '/':
- # The public directory of the application relative to its root.
- root: 'web'
- # The front-controller script which determines where to send
- # non-static requests.
- passthru: '/index.php'
+ commands:
+ start: ./console serve
+ upstream:
+ socket_family: tcp
+ protocol: http
crons:
# Look for new emails every 5 minutes.
- drush-queue:
+ sync:
spec: '*/5 * * * *'
cmd: './console sync'
diff --git a/Makefile b/Makefile
index 5571c93..0c17caa 100644
--- a/Makefile
+++ b/Makefile
@@ -1,5 +1,5 @@
preview:
- ENV=dev php -S localhost:8000 -t web
+ ENV=dev ./console serve
install:
set -e
diff --git a/composer.json b/composer.json
index 348bff2..43868e7 100644
--- a/composer.json
+++ b/composer.json
@@ -35,7 +35,9 @@
"rvdv/nntp": "dev-preserve-empty-lines@dev",
"zbateson/mail-mime-parser": "^0.4.3",
"vlucas/phpdotenv": "^2.4",
- "sentry/sentry": "^1.7"
+ "sentry/sentry": "^1.7",
+ "react/http": "^0.7.2",
+ "blackfire/php-sdk": "^1.8"
},
"require-dev": {
"phpunit/phpunit": "^6.0"
diff --git a/composer.lock b/composer.lock
index 20e50df..5d6da8c 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
- "content-hash": "773f53ee7f246794653d94abf1981d37",
+ "content-hash": "ae88cc9e5f02c6b3129cd87765a2f1fb",
"packages": [
{
"name": "algolia/algoliasearch-client-php",
@@ -106,6 +106,121 @@
],
"time": "2017-03-02T16:34:47+00:00"
},
+ {
+ "name": "blackfire/php-sdk",
+ "version": "v1.8.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/blackfireio/php-sdk.git",
+ "reference": "0a238130577c9c1b91606cb92795f44b7ec077cc"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/blackfireio/php-sdk/zipball/0a238130577c9c1b91606cb92795f44b7ec077cc",
+ "reference": "0a238130577c9c1b91606cb92795f44b7ec077cc",
+ "shasum": ""
+ },
+ "require": {
+ "composer/ca-bundle": "^1.0",
+ "php": ">=5.2.0"
+ },
+ "suggest": {
+ "ext-blackfire": "The C version of the Blackfire probe",
+ "ext-xhprof": "XHProf is required as a fallback",
+ "ext-zlib": "To push config to remote profiling targets"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.5.x-dev"
+ }
+ },
+ "autoload": {
+ "files": [
+ "src/autostart.php"
+ ],
+ "psr-4": {
+ "Blackfire\\": "src/Blackfire"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Blackfire.io",
+ "email": "support@blackfire.io"
+ }
+ ],
+ "description": "Blackfire.io PHP SDK",
+ "keywords": [
+ "performance",
+ "profiler",
+ "uprofiler",
+ "xhprof"
+ ],
+ "time": "2017-06-15T14:44:12+00:00"
+ },
+ {
+ "name": "composer/ca-bundle",
+ "version": "1.0.7",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/composer/ca-bundle.git",
+ "reference": "b17e6153cb7f33c7e44eb59578dc12eee5dc8e12"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/composer/ca-bundle/zipball/b17e6153cb7f33c7e44eb59578dc12eee5dc8e12",
+ "reference": "b17e6153cb7f33c7e44eb59578dc12eee5dc8e12",
+ "shasum": ""
+ },
+ "require": {
+ "ext-openssl": "*",
+ "ext-pcre": "*",
+ "php": "^5.3.2 || ^7.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^4.5",
+ "psr/log": "^1.0",
+ "symfony/process": "^2.5 || ^3.0"
+ },
+ "suggest": {
+ "symfony/process": "This is necessary to reliably check whether openssl_x509_parse is vulnerable on older php versions, but can be ignored on PHP 5.5.6+"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Composer\\CaBundle\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Jordi Boggiano",
+ "email": "j.boggiano@seld.be",
+ "homepage": "http://seld.be"
+ }
+ ],
+ "description": "Lets you find a path to the system CA bundle, and includes a fallback to the Mozilla CA bundle.",
+ "keywords": [
+ "cabundle",
+ "cacert",
+ "certificate",
+ "ssl",
+ "tls"
+ ],
+ "time": "2017-03-06T11:59:08+00:00"
+ },
{
"name": "dflydev/fig-cookies",
"version": "v1.0.2",
@@ -628,6 +743,52 @@
],
"time": "2014-09-09T13:34:57+00:00"
},
+ {
+ "name": "evenement/evenement",
+ "version": "v2.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/igorw/evenement.git",
+ "reference": "f6e843799fd4f4184d54d8fc7b5b3551c9fa803e"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/igorw/evenement/zipball/f6e843799fd4f4184d54d8fc7b5b3551c9fa803e",
+ "reference": "f6e843799fd4f4184d54d8fc7b5b3551c9fa803e",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.4.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.0-dev"
+ }
+ },
+ "autoload": {
+ "psr-0": {
+ "Evenement": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Igor Wiedler",
+ "email": "igor@wiedler.ch",
+ "homepage": "http://wiedler.ch/igor/"
+ }
+ ],
+ "description": "Événement is a very simple event dispatching library for PHP",
+ "keywords": [
+ "event-dispatcher",
+ "event-emitter"
+ ],
+ "time": "2012-11-02T14:49:47+00:00"
+ },
{
"name": "filp/whoops",
"version": "2.1.9",
@@ -2027,6 +2188,424 @@
],
"time": "2016-05-29T13:39:13+00:00"
},
+ {
+ "name": "react/cache",
+ "version": "v0.4.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/reactphp/cache.git",
+ "reference": "558f614891341b1d817a8cdf9a358948ec49638f"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/reactphp/cache/zipball/558f614891341b1d817a8cdf9a358948ec49638f",
+ "reference": "558f614891341b1d817a8cdf9a358948ec49638f",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.0",
+ "react/promise": "~2.0|~1.1"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "React\\Cache\\": "src\\"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "Async caching.",
+ "keywords": [
+ "cache"
+ ],
+ "time": "2016-02-25T18:17:16+00:00"
+ },
+ {
+ "name": "react/dns",
+ "version": "v0.4.9",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/reactphp/dns.git",
+ "reference": "288b4f36972cdc2f81dae1d1a58a0467e3f625cb"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/reactphp/dns/zipball/288b4f36972cdc2f81dae1d1a58a0467e3f625cb",
+ "reference": "288b4f36972cdc2f81dae1d1a58a0467e3f625cb",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.0",
+ "react/cache": "~0.4.0|~0.3.0",
+ "react/promise": "~2.1|~1.2",
+ "react/promise-timer": "~1.1",
+ "react/socket": "^1.0 || ^0.8 || ^0.7 || ^0.6 || ^0.5 || ^0.4.4",
+ "react/stream": "^1.0 || ^0.7 || ^0.6 || ^0.5 || ^0.4.5"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^5.0 || ^4.8.10"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "React\\Dns\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "Async DNS resolver for ReactPHP",
+ "keywords": [
+ "dns",
+ "dns-resolver"
+ ],
+ "time": "2017-05-01T17:21:03+00:00"
+ },
+ {
+ "name": "react/event-loop",
+ "version": "v0.4.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/reactphp/event-loop.git",
+ "reference": "8bde03488ee897dc6bb3d91e4e17c353f9c5252f"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/reactphp/event-loop/zipball/8bde03488ee897dc6bb3d91e4e17c353f9c5252f",
+ "reference": "8bde03488ee897dc6bb3d91e4e17c353f9c5252f",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.4.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "~4.8"
+ },
+ "suggest": {
+ "ext-event": "~1.0",
+ "ext-libev": "*",
+ "ext-libevent": ">=0.1.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "React\\EventLoop\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "Event loop abstraction layer that libraries can use for evented I/O.",
+ "keywords": [
+ "asynchronous",
+ "event-loop"
+ ],
+ "time": "2017-04-27T10:56:23+00:00"
+ },
+ {
+ "name": "react/http",
+ "version": "v0.7.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/reactphp/http.git",
+ "reference": "32f0eb3d445b1871b2ba859480ee1981977598f4"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/reactphp/http/zipball/32f0eb3d445b1871b2ba859480ee1981977598f4",
+ "reference": "32f0eb3d445b1871b2ba859480ee1981977598f4",
+ "shasum": ""
+ },
+ "require": {
+ "evenement/evenement": "^2.0 || ^1.0",
+ "php": ">=5.3.0",
+ "react/promise": "^2.1 || ^1.2.1",
+ "react/socket": "^1.0 || ^0.8 || ^0.7 || ^0.6 || ^0.5",
+ "react/stream": "^1.0 || ^0.7 || ^0.6 || ^0.5 || ^0.4.6",
+ "ringcentral/psr7": "^1.2"
+ },
+ "require-dev": {
+ "clue/block-react": "^1.1",
+ "phpunit/phpunit": "^4.8.10||^5.0",
+ "react/promise-stream": "^0.1.1",
+ "react/socket": "^1.0 || ^0.8 || ^0.7"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "React\\Http\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "Event-driven, streaming plaintext HTTP and secure HTTPS server for ReactPHP",
+ "keywords": [
+ "event-driven",
+ "http",
+ "https",
+ "reactphp",
+ "server",
+ "streaming"
+ ],
+ "time": "2017-07-04T13:15:44+00:00"
+ },
+ {
+ "name": "react/promise",
+ "version": "v2.5.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/reactphp/promise.git",
+ "reference": "62785ae604c8d69725d693eb370e1d67e94c4053"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/reactphp/promise/zipball/62785ae604c8d69725d693eb370e1d67e94c4053",
+ "reference": "62785ae604c8d69725d693eb370e1d67e94c4053",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.4.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "~4.8"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "React\\Promise\\": "src/"
+ },
+ "files": [
+ "src/functions_include.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Jan Sorgalla",
+ "email": "jsorgalla@gmail.com"
+ }
+ ],
+ "description": "A lightweight implementation of CommonJS Promises/A for PHP",
+ "keywords": [
+ "promise",
+ "promises"
+ ],
+ "time": "2017-03-25T12:08:31+00:00"
+ },
+ {
+ "name": "react/promise-timer",
+ "version": "v1.1.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/reactphp/promise-timer.git",
+ "reference": "ddedc67bfd7f579fc83e66ff67e3564b179297dd"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/reactphp/promise-timer/zipball/ddedc67bfd7f579fc83e66ff67e3564b179297dd",
+ "reference": "ddedc67bfd7f579fc83e66ff67e3564b179297dd",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3",
+ "react/event-loop": "~0.4.0|~0.3.0",
+ "react/promise": "~2.1|~1.2"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "React\\Promise\\Timer\\": "src/"
+ },
+ "files": [
+ "src/functions.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Christian Lück",
+ "email": "christian@lueck.tv"
+ }
+ ],
+ "description": "Trivial timeout implementation for Promises",
+ "homepage": "https://github.com/react/promise-timer",
+ "keywords": [
+ "async",
+ "event-loop",
+ "promise",
+ "reactphp",
+ "timeout",
+ "timer"
+ ],
+ "time": "2016-12-27T08:12:19+00:00"
+ },
+ {
+ "name": "react/socket",
+ "version": "v0.8.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/reactphp/socket.git",
+ "reference": "8f880ab1a9e1b2eb236a836aa5e1326da3647bcd"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/reactphp/socket/zipball/8f880ab1a9e1b2eb236a836aa5e1326da3647bcd",
+ "reference": "8f880ab1a9e1b2eb236a836aa5e1326da3647bcd",
+ "shasum": ""
+ },
+ "require": {
+ "evenement/evenement": "~2.0|~1.0",
+ "php": ">=5.3.0",
+ "react/dns": "0.4.*|0.3.*",
+ "react/event-loop": "0.4.*|0.3.*",
+ "react/promise": "^2.1 || ^1.2",
+ "react/promise-timer": "~1.0",
+ "react/stream": "^1.0 || ^0.7 || ^0.6 || ^0.5 || ^0.4.5"
+ },
+ "require-dev": {
+ "clue/block-react": "^1.1",
+ "phpunit/phpunit": "~4.8",
+ "react/stream": "^1.0 || ^0.7 || ^0.6"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "React\\Socket\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "Async, streaming plaintext TCP/IP and secure TLS socket server and client connections for ReactPHP",
+ "keywords": [
+ "Connection",
+ "Socket",
+ "async",
+ "reactphp",
+ "stream"
+ ],
+ "time": "2017-05-09T11:27:38+00:00"
+ },
+ {
+ "name": "react/stream",
+ "version": "v0.7.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/reactphp/stream.git",
+ "reference": "a7ea0af02c00f1fc004d654f9ee1e2b900e53d5b"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/reactphp/stream/zipball/a7ea0af02c00f1fc004d654f9ee1e2b900e53d5b",
+ "reference": "a7ea0af02c00f1fc004d654f9ee1e2b900e53d5b",
+ "shasum": ""
+ },
+ "require": {
+ "evenement/evenement": "^2.0|^1.0",
+ "php": ">=5.3.8",
+ "react/event-loop": "^1.0 || ^0.5 || ^0.4 || ^0.3"
+ },
+ "require-dev": {
+ "clue/stream-filter": "~1.2",
+ "phpunit/phpunit": "^5.0 || ^4.8.10"
+ },
+ "suggest": {
+ "react/event-loop": "^0.4"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "React\\Stream\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "Event-driven readable and writable streams for non-blocking I/O in ReactPHP",
+ "keywords": [
+ "event-driven",
+ "io",
+ "non-blocking",
+ "pipe",
+ "reactphp",
+ "readable",
+ "stream",
+ "writable"
+ ],
+ "time": "2017-06-15T20:26:53+00:00"
+ },
+ {
+ "name": "ringcentral/psr7",
+ "version": "1.2.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/ringcentral/psr7.git",
+ "reference": "2594fb47cdc659f3fcf0aa1559b7355460555303"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/ringcentral/psr7/zipball/2594fb47cdc659f3fcf0aa1559b7355460555303",
+ "reference": "2594fb47cdc659f3fcf0aa1559b7355460555303",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3",
+ "psr/http-message": "~1.0"
+ },
+ "provide": {
+ "psr/http-message-implementation": "1.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "~4.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "RingCentral\\Psr7\\": "src/"
+ },
+ "files": [
+ "src/functions_include.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Michael Dowling",
+ "email": "mtdowling@gmail.com",
+ "homepage": "https://github.com/mtdowling"
+ }
+ ],
+ "description": "PSR-7 message implementation",
+ "keywords": [
+ "http",
+ "message",
+ "stream",
+ "uri"
+ ],
+ "time": "2016-03-25T17:36:49+00:00"
+ },
{
"name": "rvdv/nntp",
"version": "dev-preserve-empty-lines",
diff --git a/console b/console
index e371f49..800301d 100755
--- a/console
+++ b/console
@@ -2,10 +2,14 @@
cli();
+$cli->command('serve', function (\Stratify\Http\Application $http, ContainerInterface $container, OutputInterface $output) {
+ $loop = React\EventLoop\Factory::create();
+
+ $db = $container->get(Connection::class);
+
+ $server = new Server(function (ServerRequestInterface $request) use ($http, $db) : ResponseInterface {
+ // Reset the DB connection
+ $db->close();
+ return $http->handle($request);
+ });
+ $server->listen(new React\Socket\Server($container->get('http.port'), $loop));
+ $server->on('error', function (Exception $e) use ($output) {
+ $output->writeln("Error: {$e->getMessage()}");
+ if ($e->getPrevious() !== null) {
+ $e = $e->getPrevious();
+ $output->writeln("Caused by: {$e->getMessage()}");
+ }
+ $output->writeln($e->getTraceAsString());
+ });
+
+ $loop->run();
+});
+
$cli->command('db [--force]', [DbCommand::class, 'setup']);
$cli->command('db-purge [--force]', [DbCommand::class, 'purge']);
$cli->command('db-truncate [--force]', [DbCommand::class, 'truncate']);
diff --git a/res/config/config.php b/res/config/config.php
index bd72032..9510f3c 100644
--- a/res/config/config.php
+++ b/res/config/config.php
@@ -11,6 +11,7 @@
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\DriverManager;
use Externals\Application\Database\CustomMySQLPlatform;
+use Externals\Application\Middleware\AssetsMiddleware;
use Externals\Application\Middleware\MaintenanceMiddleware;
use Externals\Search\AlgoliaSearchIndex;
use Externals\Search\SearchIndex;
@@ -34,6 +35,10 @@
'debug' => false,
'path.cache' => __DIR__ . '/../../var/cache',
'path.logs' => __DIR__ . '/../../var/log',
+ 'path.public' => __DIR__ . '/../../web',
+
+ // Platform.sh defines this variable
+ 'http.port' => env('PORT', 8000),
'version' => env('PLATFORM_TREE_ID', factory(function () {
return trim(shell_exec('git rev-parse HEAD'));
@@ -113,6 +118,9 @@
return SessionMiddleware::fromSymmetricKeyDefaults($key, 31536000);
},
+ AssetsMiddleware::class => create()
+ ->constructor(get('path.public')),
+
'sentry.url' => env('SENTRY_URL', null),
MaintenanceMiddleware::class => autowire()
diff --git a/res/http.php b/res/http.php
index 746add6..60129f4 100644
--- a/res/http.php
+++ b/res/http.php
@@ -4,8 +4,11 @@
use Doctrine\DBAL\Connection;
use Externals\Application\Controller\UserController;
use Externals\Application\Controller\NotFoundController;
+use Externals\Application\Middleware\AssetsMiddleware;
use Externals\Application\Middleware\AuthMiddleware;
+use Externals\Application\Middleware\BlackfireMiddleware;
use Externals\Application\Middleware\MaintenanceMiddleware;
+use Externals\Application\Middleware\NewRelicMiddleware;
use Externals\Application\Middleware\NotFoundMiddleware;
use Externals\Application\Middleware\SessionMiddleware;
use Externals\Email\EmailRepository;
@@ -27,8 +30,11 @@
* HTTP stack.
*/
return pipe([
+ BlackfireMiddleware::class,
+ NewRelicMiddleware::class,
MaintenanceMiddleware::class,
ErrorHandlerMiddleware::class,
+ AssetsMiddleware::class,
NotFoundMiddleware::class,
SessionMiddleware::class,
AuthMiddleware::class,
@@ -141,6 +147,7 @@
// Keep backward compatibility with old URLs (old threads)
'/thread/{id}' => route(function (int $id, EmailRepository $emailRepository, Connection $db) {
+ newrelic_name_transaction('thread_legacy');
$threadSubject = $db->fetchColumn('SELECT `subject` FROM threads_old WHERE id = ?', [$id]);
$email = $emailRepository->findBySubject($threadSubject);
// Permanent redirection
diff --git a/src/Application/Middleware/AssetsMiddleware.php b/src/Application/Middleware/AssetsMiddleware.php
new file mode 100644
index 0000000..81e40c6
--- /dev/null
+++ b/src/Application/Middleware/AssetsMiddleware.php
@@ -0,0 +1,104 @@
+
+ */
+class AssetsMiddleware implements Middleware
+{
+ /**
+ * @var string
+ */
+ private $publicPath;
+
+ public function __construct(string $publicPath)
+ {
+ $this->publicPath = rtrim($publicPath, '/') . '/';
+ }
+
+ public function __invoke(ServerRequestInterface $request, callable $next) : ResponseInterface
+ {
+ $path = $request->getUri()->getPath();
+
+ $file = $this->publicPath . $path;
+
+ if (!is_file($file)) {
+ return $next($request);
+ }
+
+ $body = new Stream($file);
+
+ $array = explode('.', $file);
+ $extension = strtolower(array_pop($array));
+ $mimeType = self::MIME_TYPES[$extension] ?? 'application/octet-stream';
+
+ return new Response($body, 200, [
+ 'Content-Type' => $mimeType,
+ ]);
+ }
+
+ private const MIME_TYPES = [
+ 'txt' => 'text/plain',
+ 'htm' => 'text/html',
+ 'html' => 'text/html',
+ 'php' => 'text/html',
+ 'css' => 'text/css',
+ 'js' => 'application/javascript',
+ 'json' => 'application/json',
+ 'xml' => 'application/xml',
+ 'swf' => 'application/x-shockwave-flash',
+ 'flv' => 'video/x-flv',
+
+ // images
+ 'png' => 'image/png',
+ 'jpe' => 'image/jpeg',
+ 'jpeg' => 'image/jpeg',
+ 'jpg' => 'image/jpeg',
+ 'gif' => 'image/gif',
+ 'bmp' => 'image/bmp',
+ 'ico' => 'image/vnd.microsoft.icon',
+ 'tiff' => 'image/tiff',
+ 'tif' => 'image/tiff',
+ 'svg' => 'image/svg+xml',
+ 'svgz' => 'image/svg+xml',
+
+ // archives
+ 'zip' => 'application/zip',
+ 'rar' => 'application/x-rar-compressed',
+ 'exe' => 'application/x-msdownload',
+ 'msi' => 'application/x-msdownload',
+ 'cab' => 'application/vnd.ms-cab-compressed',
+
+ // audio/video
+ 'mp3' => 'audio/mpeg',
+ 'qt' => 'video/quicktime',
+ 'mov' => 'video/quicktime',
+
+ // adobe
+ 'pdf' => 'application/pdf',
+ 'psd' => 'image/vnd.adobe.photoshop',
+ 'ai' => 'application/postscript',
+ 'eps' => 'application/postscript',
+ 'ps' => 'application/postscript',
+
+ // ms office
+ 'doc' => 'application/msword',
+ 'rtf' => 'application/rtf',
+ 'xls' => 'application/vnd.ms-excel',
+ 'ppt' => 'application/vnd.ms-powerpoint',
+
+ // open office
+ 'odt' => 'application/vnd.oasis.opendocument.text',
+ 'ods' => 'application/vnd.oasis.opendocument.spreadsheet',
+ ];
+}
diff --git a/src/Application/Middleware/BlackfireMiddleware.php b/src/Application/Middleware/BlackfireMiddleware.php
new file mode 100644
index 0000000..1d09067
--- /dev/null
+++ b/src/Application/Middleware/BlackfireMiddleware.php
@@ -0,0 +1,41 @@
+
+ */
+class BlackfireMiddleware
+{
+ public function __invoke(ServerRequestInterface $request, callable $next) : ResponseInterface
+ {
+ $query = $request->getHeaderLine('x-blackfire-query');
+
+ // Only enable when the X-Blackfire-Query header is present
+ if (! $query) {
+ return $next($request);
+ }
+
+ $probe = new \BlackfireProbe($query);
+ if (! $probe->enable()) {
+ return $next($request);
+ }
+
+ /** @var ResponseInterface $response */
+ $response = $next($request);
+
+ // Stop profiling once the request ends
+ $probe->close();
+
+ // Return the header
+ $header = explode(':', $probe->getResponseLine(), 2);
+
+ return $response->withHeader('x-' . $header[0], trim($header[1]));
+ }
+}
diff --git a/src/Application/Middleware/NewRelicMiddleware.php b/src/Application/Middleware/NewRelicMiddleware.php
new file mode 100644
index 0000000..c97589b
--- /dev/null
+++ b/src/Application/Middleware/NewRelicMiddleware.php
@@ -0,0 +1,28 @@
+
+ */
+class NewRelicMiddleware
+{
+ public function __invoke(ServerRequestInterface $request, callable $next) : ResponseInterface
+ {
+ newrelic_end_transaction();
+ newrelic_start_transaction(ini_get('newrelic.appname'));
+
+ /** @var ResponseInterface $response */
+ $response = $next($request);
+
+ newrelic_end_transaction(false);
+
+ return $response;
+ }
+}
diff --git a/web/.htaccess b/web/.htaccess
deleted file mode 100644
index eaa46cb..0000000
--- a/web/.htaccess
+++ /dev/null
@@ -1,52 +0,0 @@
-# Use the front controller as index file. It serves as a fallback solution when
-# every other rewrite/redirect fails (e.g. in an aliased environment without
-# mod_rewrite). Additionally, this reduces the matching process for the
-# start page (path "/") because otherwise Apache will apply the rewriting rules
-# to each configured DirectoryIndex file (e.g. index.php, index.html, index.pl).
-DirectoryIndex index.php
-
-
- RewriteEngine On
-
- # Determine the RewriteBase automatically and set it as environment variable.
- # If you are using Apache aliases to do mass virtual hosting or installed the
- # project in a subdirectory, the base path will be prepended to allow proper
- # resolution of the app.php file and to redirect to the correct URI. It will
- # work in environments without path prefix as well, providing a safe, one-size
- # fits all solution. But as you do not need it in this case, you can comment
- # the following 2 lines to eliminate the overhead.
- RewriteCond %{REQUEST_URI}::$1 ^(/.+)/(.*)::\2$
- RewriteRule ^(.*) - [E=BASE:%1]
-
- # Redirect to URI without front controller to prevent duplicate content
- # (with and without `/app.php`). Only do this redirect on the initial
- # rewrite by Apache and not on subsequent cycles. Otherwise we would get an
- # endless redirect loop (request -> rewrite to front controller ->
- # redirect -> request -> ...).
- # So in case you get a "too many redirects" error or you always get redirected
- # to the start page because your Apache does not expose the REDIRECT_STATUS
- # environment variable, you have 2 choices:
- # - disable this feature by commenting the following 2 lines or
- # - use Apache >= 2.3.9 and replace all L flags by END flags and remove the
- # following RewriteCond (best solution)
- RewriteCond %{ENV:REDIRECT_STATUS} ^$
- RewriteRule ^app\.php(/(.*)|$) %{ENV:BASE}/$2 [R=301,L]
-
- # If the requested filename exists, simply serve it.
- # We only want to let Apache serve files and not directories.
- RewriteCond %{REQUEST_FILENAME} -f
- RewriteRule .? - [L]
-
- # Rewrite all other queries to the front controller.
- RewriteRule .? %{ENV:BASE}/index.php [L]
-
-
-
-
- # When mod_rewrite is not available, we instruct a temporary redirect of
- # the start page to the front controller explicitly so that the website
- # and the generated links can still be used.
- RedirectMatch 302 ^/$ /index.php/
- # RedirectTemp cannot be used instead
-
-
diff --git a/web/index.php b/web/index.php
deleted file mode 100644
index 0a6b10a..0000000
--- a/web/index.php
+++ /dev/null
@@ -1,14 +0,0 @@
-http()->run();