Application bootstrap refactor

- Extract environment properties in Environment class
- Replaces initialisation closure by ApplicationLoader class
- Removes undesirable error handling/PHP settings modifications
This commit is contained in:
Thibaud Fabre
2016-01-21 13:28:17 +01:00
parent 7dc776d7ab
commit 450adb0847
11 changed files with 522 additions and 246 deletions

View File

@@ -14,10 +14,13 @@ namespace Alchemy\Phrasea;
use Alchemy\Cors\Options\DefaultProvider; use Alchemy\Cors\Options\DefaultProvider;
use Alchemy\CorsProvider\CorsServiceProvider; use Alchemy\CorsProvider\CorsServiceProvider;
use Alchemy\Geonames\GeonamesServiceProvider; use Alchemy\Geonames\GeonamesServiceProvider;
use Alchemy\Phrasea\Application\Environment;
use Alchemy\Phrasea\Application\Helper\AclAware; use Alchemy\Phrasea\Application\Helper\AclAware;
use Alchemy\Phrasea\Application\Helper\ApplicationBoxAware; use Alchemy\Phrasea\Application\Helper\ApplicationBoxAware;
use Alchemy\Phrasea\Application\Helper\AuthenticatorAware; use Alchemy\Phrasea\Application\Helper\AuthenticatorAware;
use Alchemy\Phrasea\Application\RouteLoader;
use Alchemy\Phrasea\Authorization\AuthorizationServiceProvider; use Alchemy\Phrasea\Authorization\AuthorizationServiceProvider;
use Alchemy\Phrasea\ControllerProvider\ControllerProviderServiceProvider;
use Alchemy\Phrasea\Core\Event\Subscriber\BasketSubscriber; use Alchemy\Phrasea\Core\Event\Subscriber\BasketSubscriber;
use Alchemy\Phrasea\Core\Event\Subscriber\BridgeSubscriber; use Alchemy\Phrasea\Core\Event\Subscriber\BridgeSubscriber;
use Alchemy\Phrasea\Core\Event\Subscriber\ExportSubscriber; use Alchemy\Phrasea\Core\Event\Subscriber\ExportSubscriber;
@@ -142,6 +145,10 @@ class Application extends SilexApplication
use UrlGeneratorTrait; use UrlGeneratorTrait;
use TranslationTrait; use TranslationTrait;
const ENV_DEV = 'dev';
const ENV_PROD = 'prod';
const ENV_TEST = 'test';
protected static $availableLanguages = [ protected static $availableLanguages = [
'de' => 'Deutsch', 'de' => 'Deutsch',
'en' => 'English', 'en' => 'English',
@@ -150,38 +157,31 @@ class Application extends SilexApplication
]; ];
private static $flashTypes = ['warning', 'info', 'success', 'error']; private static $flashTypes = ['warning', 'info', 'success', 'error'];
/**
* @var Environment
*/
private $environment; private $environment;
const ENV_DEV = 'dev'; /**
const ENV_PROD = 'prod'; * @param Environment|string $environment
const ENV_TEST = 'test'; */
public function __construct($environment = null)
public function getEnvironment()
{ {
return $this->environment; if (is_string($environment)) {
} $environment = new Environment($environment, false);
}
public function __construct($environment = self::ENV_PROD) $this->environment = $environment ?: new Environment(self::ENV_PROD, false);
{
parent::__construct();
error_reporting(-1); parent::__construct([
'debug' => $this->environment->isDebug()
$this->environment = $environment; ]);
$this->setupCharset(); $this->setupCharset();
$this->setupApplicationPaths(); $this->setupApplicationPaths();
$this->setupConstants(); $this->setupConstants();
$this['debug'] = !in_array($environment, [
Application::ENV_PROD,
Application::ENV_TEST,
]);
if ($this['debug']) {
ini_set('log_errors', 'on');
ini_set('error_log', $this['root.path'].'/logs/php_error.log');
}
if ('allowed' == getenv('APP_CONTAINER_DUMP')) { if ('allowed' == getenv('APP_CONTAINER_DUMP')) {
$this->register(new PimpleDumpProvider()); $this->register(new PimpleDumpProvider());
} }
@@ -287,6 +287,7 @@ class Application extends SilexApplication
return rtrim($app['cache.path'], '/\\') . '/alchemy_cors.cache.php'; return rtrim($app['cache.path'], '/\\') . '/alchemy_cors.cache.php';
}, },
]); ]);
$this['phraseanet.api_cors.options_provider'] = function (Application $app) { $this['phraseanet.api_cors.options_provider'] = function (Application $app) {
$paths = []; $paths = [];
@@ -307,78 +308,16 @@ class Application extends SilexApplication
$this->register(new LocaleServiceProvider()); $this->register(new LocaleServiceProvider());
$this->setupEventDispatcher(); $this->setupEventDispatcher();
$this['phraseanet.exception_handler'] = $this->share(function ($app) { $this['phraseanet.exception_handler'] = $this->share(function ($app) {
/** @var PhraseaExceptionHandler $handler */
$handler = PhraseaExceptionHandler::register($app['debug']); $handler = PhraseaExceptionHandler::register($app['debug']);
$handler->setTranslator($app['translator']); $handler->setTranslator($app['translator']);
$handler->setLogger($app['monolog']); $handler->setLogger($app['monolog']);
return $handler; return $handler;
}); });
$providers = [ $this->register(new ControllerProviderServiceProvider());
'Alchemy\Phrasea\ControllerProvider\Admin\Collection' => [],
'Alchemy\Phrasea\ControllerProvider\Admin\ConnectedUsers' => [],
'Alchemy\Phrasea\ControllerProvider\Admin\Dashboard' => [],
'Alchemy\Phrasea\ControllerProvider\Admin\Databox' => [],
'Alchemy\Phrasea\ControllerProvider\Admin\Databoxes' => [],
'Alchemy\Phrasea\ControllerProvider\Admin\Feeds' => [],
'Alchemy\Phrasea\ControllerProvider\Admin\Fields' => [],
'Alchemy\Phrasea\ControllerProvider\Admin\Plugins' => [],
'Alchemy\Phrasea\ControllerProvider\Admin\Root' => [],
'Alchemy\Phrasea\ControllerProvider\Admin\SearchEngine' => [],
'Alchemy\Phrasea\ControllerProvider\Admin\Setup' => [],
'Alchemy\Phrasea\ControllerProvider\Admin\Subdefs' => [],
'Alchemy\Phrasea\ControllerProvider\Admin\TaskManager' => [],
'Alchemy\Phrasea\ControllerProvider\Admin\Users' => [],
'Alchemy\Phrasea\ControllerProvider\Client\Root' => [],
'Alchemy\Phrasea\ControllerProvider\Datafiles' => [],
'Alchemy\Phrasea\ControllerProvider\Lightbox' => [],
'Alchemy\Phrasea\ControllerProvider\MediaAccessor' => [],
'Alchemy\Phrasea\ControllerProvider\Minifier' => [],
'Alchemy\Phrasea\ControllerProvider\Permalink' => [],
'Alchemy\Phrasea\ControllerProvider\Prod\BasketProvider' => [],
'Alchemy\Phrasea\ControllerProvider\Prod\Bridge' => [],
'Alchemy\Phrasea\ControllerProvider\Prod\DoDownload' => [],
'Alchemy\Phrasea\ControllerProvider\Prod\Download' => [],
'Alchemy\Phrasea\ControllerProvider\Prod\Edit' => [],
'Alchemy\Phrasea\ControllerProvider\Prod\Export' => [],
'Alchemy\Phrasea\ControllerProvider\Prod\Feed' => [],
'Alchemy\Phrasea\ControllerProvider\Prod\Language' => [],
'Alchemy\Phrasea\ControllerProvider\Prod\Lazaret' => [],
'Alchemy\Phrasea\ControllerProvider\Prod\MoveCollection' => [],
'Alchemy\Phrasea\ControllerProvider\Prod\Order' => [],
'Alchemy\Phrasea\ControllerProvider\Prod\Printer' => [],
'Alchemy\Phrasea\ControllerProvider\Prod\Property' => [],
'Alchemy\Phrasea\ControllerProvider\Prod\Push' => [],
'Alchemy\Phrasea\ControllerProvider\Prod\Query' => [],
'Alchemy\Phrasea\ControllerProvider\Prod\Record' => [],
'Alchemy\Phrasea\ControllerProvider\Prod\Root' => [],
'Alchemy\Phrasea\ControllerProvider\Prod\Share' => [],
'Alchemy\Phrasea\ControllerProvider\Prod\Story' => [],
'Alchemy\Phrasea\ControllerProvider\Prod\Tools' => [],
'Alchemy\Phrasea\ControllerProvider\Prod\Tooltip' => [],
'Alchemy\Phrasea\ControllerProvider\Prod\TOU' => [],
'Alchemy\Phrasea\ControllerProvider\Prod\Upload' => [],
'Alchemy\Phrasea\ControllerProvider\Prod\UsrLists' => [],
'Alchemy\Phrasea\ControllerProvider\Prod\WorkZone' => [],
'Alchemy\Phrasea\ControllerProvider\Report\Activity' => [],
'Alchemy\Phrasea\ControllerProvider\Report\Information' => [],
'Alchemy\Phrasea\ControllerProvider\Report\Root' => [],
'Alchemy\Phrasea\ControllerProvider\Root\Account' => [],
'Alchemy\Phrasea\ControllerProvider\Root\Developers' => [],
'Alchemy\Phrasea\ControllerProvider\Root\Login' => [],
'Alchemy\Phrasea\ControllerProvider\Root\Root' => [],
'Alchemy\Phrasea\ControllerProvider\Root\RSSFeeds' => [],
'Alchemy\Phrasea\ControllerProvider\Root\Session' => [],
'Alchemy\Phrasea\ControllerProvider\Setup' => [],
'Alchemy\Phrasea\ControllerProvider\Thesaurus\Thesaurus' => [],
'Alchemy\Phrasea\ControllerProvider\Thesaurus\Xmlhttp' => [],
'Alchemy\Phrasea\ControllerProvider\User\Notifications' => [],
'Alchemy\Phrasea\ControllerProvider\User\Preferences' => [],
'Alchemy\EmbedProvider\EmbedServiceProvider' => [],
];
foreach ($providers as $class => $values) {
$this->register(new $class, $values);
}
$resolvers = $this['alchemy_embed.resource_resolvers']; $resolvers = $this['alchemy_embed.resource_resolvers'];
$resolvers['datafile'] = $resolvers->share(function () { $resolvers['datafile'] = $resolvers->share(function () {
@@ -411,6 +350,11 @@ class Application extends SilexApplication
} }
} }
public function getEnvironment()
{
return $this->environment->getName();
}
/** /**
* Loads Phraseanet plugins * Loads Phraseanet plugins
*/ */
@@ -475,6 +419,7 @@ class Application extends SilexApplication
$twig->setCache($app['cache.path'].'/twig'); $twig->setCache($app['cache.path'].'/twig');
$paths = []; $paths = [];
if (file_exists($app['plugin.path'] . '/twig-paths.php')) { if (file_exists($app['plugin.path'] . '/twig-paths.php')) {
$paths = require $app['plugin.path'] . '/twig-paths.php'; $paths = require $app['plugin.path'] . '/twig-paths.php';
} }
@@ -651,6 +596,14 @@ class Application extends SilexApplication
return $this; return $this;
} }
/**
* @return bool
*/
public function isDebug()
{
return $this->environment->isDebug();
}
/** /**
* Returns true if a captcha is required for next authentication * Returns true if a captcha is required for next authentication
* *
@@ -706,75 +659,12 @@ class Application extends SilexApplication
*/ */
public function bindRoutes() public function bindRoutes()
{ {
$providers = [ $loader = new RouteLoader();
'/account/' => 'Alchemy\Phrasea\ControllerProvider\Root\Account',
'/admin/' => 'Alchemy\Phrasea\ControllerProvider\Admin\Root',
'/admin/collection' => 'Alchemy\Phrasea\ControllerProvider\Admin\Collection',
'/admin/connected-users' => 'Alchemy\Phrasea\ControllerProvider\Admin\ConnectedUsers',
'/admin/dashboard' => 'Alchemy\Phrasea\ControllerProvider\Admin\Dashboard',
'/admin/databox' => 'Alchemy\Phrasea\ControllerProvider\Admin\Databox',
'/admin/databoxes' => 'Alchemy\Phrasea\ControllerProvider\Admin\Databoxes',
'/admin/fields' => 'Alchemy\Phrasea\ControllerProvider\Admin\Fields',
'/admin/publications' => 'Alchemy\Phrasea\ControllerProvider\Admin\Feeds',
'/admin/plugins' => 'Alchemy\Phrasea\ControllerProvider\Admin\Plugins',
'/admin/search-engine' => 'Alchemy\Phrasea\ControllerProvider\Admin\SearchEngine',
'/admin/setup' => 'Alchemy\Phrasea\ControllerProvider\Admin\Setup',
'/admin/subdefs' => 'Alchemy\Phrasea\ControllerProvider\Admin\Subdefs',
'/admin/task-manager' => 'Alchemy\Phrasea\ControllerProvider\Admin\TaskManager',
'/admin/users' => 'Alchemy\Phrasea\ControllerProvider\Admin\Users',
'/client/' => 'Alchemy\Phrasea\ControllerProvider\Client\Root',
'/datafiles' => 'Alchemy\Phrasea\ControllerProvider\Datafiles',
'/developers/' => 'Alchemy\Phrasea\ControllerProvider\Root\Developers',
'/download/' => 'Alchemy\Phrasea\ControllerProvider\Prod\DoDownload',
'/embed/' => 'Alchemy\EmbedProvider\EmbedServiceProvider',
'/feeds/' => 'Alchemy\Phrasea\ControllerProvider\Root\RSSFeeds',
'/include/minify' => 'Alchemy\Phrasea\ControllerProvider\Minifier',
'/login/' => 'Alchemy\Phrasea\ControllerProvider\Root\Login',
'/lightbox' => 'Alchemy\Phrasea\ControllerProvider\Lightbox',
'/permalink' => 'Alchemy\Phrasea\ControllerProvider\Permalink',
'/prod/baskets' => 'Alchemy\Phrasea\ControllerProvider\Prod\BasketProvider',
'/prod/bridge/' => 'Alchemy\Phrasea\ControllerProvider\Prod\Bridge',
'/prod/download' => 'Alchemy\Phrasea\ControllerProvider\Prod\Download',
'/prod/export/' => 'Alchemy\Phrasea\ControllerProvider\Prod\Export',
'/prod/feeds' => 'Alchemy\Phrasea\ControllerProvider\Prod\Feed',
'/prod/language' => 'Alchemy\Phrasea\ControllerProvider\Prod\Language',
'/prod/lazaret/' => 'Alchemy\Phrasea\ControllerProvider\Prod\Lazaret',
'/prod/lists' => 'Alchemy\Phrasea\ControllerProvider\Prod\UsrLists',
'/prod/order/' => 'Alchemy\Phrasea\ControllerProvider\Prod\Order',
'/prod/printer/' => 'Alchemy\Phrasea\ControllerProvider\Prod\Printer',
'/prod/push/' => 'Alchemy\Phrasea\ControllerProvider\Prod\Push',
'/prod/query/' => 'Alchemy\Phrasea\ControllerProvider\Prod\Query',
'/prod/records/' => 'Alchemy\Phrasea\ControllerProvider\Prod\Record',
'/prod/records/edit' => 'Alchemy\Phrasea\ControllerProvider\Prod\Edit',
'/prod/records/movecollection' => 'Alchemy\Phrasea\ControllerProvider\Prod\MoveCollection',
'/prod/records/property' => 'Alchemy\Phrasea\ControllerProvider\Prod\Property',
'/prod/share/' => 'Alchemy\Phrasea\ControllerProvider\Prod\Share',
'/prod/story' => 'Alchemy\Phrasea\ControllerProvider\Prod\Story',
'/prod/tools/' => 'Alchemy\Phrasea\ControllerProvider\Prod\Tools',
'/prod/tooltip' => 'Alchemy\Phrasea\ControllerProvider\Prod\Tooltip',
'/prod/TOU/' => 'Alchemy\Phrasea\ControllerProvider\Prod\TOU',
'/prod/upload/' => 'Alchemy\Phrasea\ControllerProvider\Prod\Upload',
'/prod/WorkZone' => 'Alchemy\Phrasea\ControllerProvider\Prod\WorkZone',
'/prod/' => 'Alchemy\Phrasea\ControllerProvider\Prod\Root',
'/report/activity' => 'Alchemy\Phrasea\ControllerProvider\Report\Activity',
'/report/informations' => 'Alchemy\Phrasea\ControllerProvider\Report\Information',
'/report/' => 'Alchemy\Phrasea\ControllerProvider\Report\Root',
'/session/' => 'Alchemy\Phrasea\ControllerProvider\Root\Session',
'/setup' => 'Alchemy\Phrasea\ControllerProvider\Setup',
'/thesaurus' => 'Alchemy\Phrasea\ControllerProvider\Thesaurus\Thesaurus',
'/user/notifications/' => 'Alchemy\Phrasea\ControllerProvider\User\Notifications',
'/user/preferences/' => 'Alchemy\Phrasea\ControllerProvider\User\Preferences',
'/xmlhttp' => 'Alchemy\Phrasea\ControllerProvider\Thesaurus\Xmlhttp',
'/' => 'Alchemy\Phrasea\ControllerProvider\Root\Root',
];
// controllers with routes referenced by api $loader->registerProviders(RouteLoader::$defaultProviders);
$providers[$this['controller.media_accessor.route_prefix']] = 'Alchemy\Phrasea\ControllerProvider\MediaAccessor';
foreach ($providers as $prefix => $class) {
$this->mount($prefix, new $class);
}
$this->bindPluginRoutes('plugin.controller_providers.root'); $loader->bindRoutes($this);
$loader->bindPluginRoutes($this, 'plugin.controller_providers.root');
} }
/** /**
@@ -928,12 +818,15 @@ class Application extends SilexApplication
if ($app['conf']->get(['registry', 'executables', 'imagine-driver']) != '') { if ($app['conf']->get(['registry', 'executables', 'imagine-driver']) != '') {
return $app['conf']->get(['registry', 'executables', 'imagine-driver']); return $app['conf']->get(['registry', 'executables', 'imagine-driver']);
} }
if (class_exists('\Gmagick')) { if (class_exists('\Gmagick')) {
return 'gmagick'; return 'gmagick';
} }
if (class_exists('\Imagick')) { if (class_exists('\Imagick')) {
return 'imagick'; return 'imagick';
} }
if (extension_loaded('gd')) { if (extension_loaded('gd')) {
return 'gd'; return 'gd';
} }
@@ -980,6 +873,7 @@ class Application extends SilexApplication
$service->factory('ORM_hydration', $app['orm.cache.driver'], $app['orm.cache.options']) $service->factory('ORM_hydration', $app['orm.cache.driver'], $app['orm.cache.options'])
); );
}); });
$app['orm.proxies_dir'] = $app['root.path'].'/resources/proxies'; $app['orm.proxies_dir'] = $app['root.path'].'/resources/proxies';
$app['orm.auto_generate_proxies'] = $app['debug']; $app['orm.auto_generate_proxies'] = $app['debug'];
$app['orm.proxies_namespace'] = 'Alchemy\Phrasea\Model\Proxies'; $app['orm.proxies_namespace'] = 'Alchemy\Phrasea\Model\Proxies';
@@ -1014,6 +908,7 @@ class Application extends SilexApplication
return $this['session.storage.handler.factory']->create($app['conf']); return $this['session.storage.handler.factory']->create($app['conf']);
}); });
} }
private function setupRecaptacha() private function setupRecaptacha()
{ {
$this['recaptcha.public-key'] = $this->share(function (Application $app) { $this['recaptcha.public-key'] = $this->share(function (Application $app) {
@@ -1105,6 +1000,7 @@ class Application extends SilexApplication
return $configuration; return $configuration;
}); });
$this['media-alchemyst.logger'] = $this->share(function (Application $app) { $this['media-alchemyst.logger'] = $this->share(function (Application $app) {
return $app['monolog']; return $app['monolog'];
}); });
@@ -1220,12 +1116,15 @@ class Application extends SilexApplication
if (!defined('JETON_MAKE_SUBDEF')) { if (!defined('JETON_MAKE_SUBDEF')) {
define('JETON_MAKE_SUBDEF', 0x01); define('JETON_MAKE_SUBDEF', 0x01);
} }
if (!defined('JETON_WRITE_META_DOC')) { if (!defined('JETON_WRITE_META_DOC')) {
define('JETON_WRITE_META_DOC', 0x02); define('JETON_WRITE_META_DOC', 0x02);
} }
if (!defined('JETON_WRITE_META_SUBDEF')) { if (!defined('JETON_WRITE_META_SUBDEF')) {
define('JETON_WRITE_META_SUBDEF', 0x04); define('JETON_WRITE_META_SUBDEF', 0x04);
} }
if (!defined('JETON_WRITE_META')) { if (!defined('JETON_WRITE_META')) {
define('JETON_WRITE_META', 0x06); define('JETON_WRITE_META', 0x06);
} }
@@ -1242,30 +1141,6 @@ class Application extends SilexApplication
*/ */
public function bindPluginRoutes($routeParameter) public function bindPluginRoutes($routeParameter)
{ {
foreach ($this[$routeParameter] as $provider) {
$prefix = '';
if (is_array($provider)) {
$providerDefinition = $provider;
list($prefix, $provider) = $providerDefinition;
}
if (!is_string($prefix) || !is_string($provider)) {
continue;
}
$prefix = '/' . ltrim($prefix, '/');
if (!isset($this[$provider])) {
continue;
}
$provider = $this[$provider];
if (!$provider instanceof ControllerProviderInterface) {
continue;
}
$this->mount($prefix, $provider);
}
} }
} }

View File

@@ -0,0 +1,56 @@
<?php
namespace Alchemy\Phrasea\Application;
use Alchemy\Phrasea\Application;
use Alchemy\Phrasea\Core\Event\Subscriber\BridgeExceptionSubscriber;
use Alchemy\Phrasea\Core\Event\Subscriber\DebuggerSubscriber;
use Alchemy\Phrasea\Core\Event\Subscriber\FirewallSubscriber;
use Alchemy\Phrasea\Core\Event\Subscriber\JsonRequestSubscriber;
use Alchemy\Phrasea\Core\Event\Subscriber\PhraseaExceptionHandlerSubscriber;
use Alchemy\Phrasea\Core\Middleware\SetupMiddlewareProvider;
use Monolog\Logger;
use Symfony\Bridge\Monolog\Processor\WebProcessor;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
class ApplicationLoader
{
public function buildWebApplication($environment = Application::ENV_PROD, $forceDebug = false)
{
$env = new Environment($environment, false);
$app = new Application($env);
$app->register(new SetupMiddlewareProvider());
$app->loadPlugins();
$app['exception_handler'] = $app->share(function ($app) {
return new PhraseaExceptionHandlerSubscriber($app['phraseanet.exception_handler']);
});
$app['monolog'] = $app->share($app->extend('monolog', function (Logger $monolog) {
$monolog->pushProcessor(new WebProcessor());
return $monolog;
}));
$app->before($app['setup.validate-config'], Application::EARLY_EVENT);
$app->bindRoutes();
$app['dispatcher'] = $app->share(
$app->extend('dispatcher', function (EventDispatcherInterface $dispatcher, Application $app) {
$dispatcher->addSubscriber(new BridgeExceptionSubscriber($app));
$dispatcher->addSubscriber(new FirewallSubscriber());
$dispatcher->addSubscriber(new JsonRequestSubscriber());
if ($app->isDebug()){
$dispatcher->addSubscriber(new DebuggerSubscriber($app));
}
return $dispatcher;
})
);
return $app;
}
}

View File

@@ -0,0 +1,42 @@
<?php
namespace Alchemy\Phrasea\Application;
use Alchemy\Phrasea\Application;
class Environment
{
/**
* @var string
*/
private $name;
/**
* @var bool
*/
private $debug = false;
public function __construct($name, $debug)
{
$this->name = (string) $name;
$this->debug = ((bool) $debug) || ! in_array($name, [
Application::ENV_PROD, Application::ENV_TEST
]);
}
/**
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* @return bool
*/
public function isDebug()
{
return $this->debug;
}
}

View File

@@ -9,65 +9,9 @@
* file that was distributed with this source code. * file that was distributed with this source code.
*/ */
namespace Alchemy\Phrasea\Application;
use Alchemy\Phrasea\Application as PhraseaApplication;
use Alchemy\Phrasea\Application; use Alchemy\Phrasea\Application;
use Alchemy\Phrasea\Core\Event\Subscriber\PhraseaExceptionHandlerSubscriber;
use Alchemy\Phrasea\Core\Event\Subscriber\BridgeExceptionSubscriber;
use Alchemy\Phrasea\Core\Event\Subscriber\FirewallSubscriber;
use Alchemy\Phrasea\Core\Event\Subscriber\JsonRequestSubscriber;
use Alchemy\Phrasea\Core\Event\Subscriber\DebuggerSubscriber;
use Monolog\Logger;
use Monolog\Processor\WebProcessor;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\HttpFoundation\Request;
return call_user_func(function ($environment = PhraseaApplication::ENV_PROD) { return (new Application\ApplicationLoader())->buildWebApplication(
$app = new PhraseaApplication($environment); isset($environment) ? $environment : Application::ENV_PROD,
$app->loadPlugins(); isset($forceDebug) ? $forceDebug : false
);
$app['exception_handler'] = $app->share(function ($app) {
return new PhraseaExceptionHandlerSubscriber($app['phraseanet.exception_handler']);
});
$app['monolog'] = $app->share($app->extend('monolog', function (Logger $monolog) {
$monolog->pushProcessor(new WebProcessor());
return $monolog;
}));
$app->before(function (Request $request) use ($app) {
if (0 === strpos($request->getPathInfo(), '/setup')) {
if (!$app['phraseanet.configuration-tester']->isInstalled()) {
if (!$app['phraseanet.configuration-tester']->isBlank()) {
if ('setup_upgrade_instructions' !== $app['request']->attributes->get('_route')) {
return $app->redirectPath('setup_upgrade_instructions');
}
}
} elseif (!$app['phraseanet.configuration-tester']->isBlank()) {
return $app->redirectPath('homepage');
}
} else {
if (false === strpos($request->getPathInfo(), '/include/minify')) {
$app['firewall']->requireSetup();
}
}
}, Application::EARLY_EVENT);
$app->bindRoutes();
$app['dispatcher'] = $app->share(
$app->extend('dispatcher', function (EventDispatcherInterface $dispatcher, PhraseaApplication $app) {
$dispatcher->addSubscriber(new BridgeExceptionSubscriber($app));
$dispatcher->addSubscriber(new FirewallSubscriber());
$dispatcher->addSubscriber(new JsonRequestSubscriber());
$dispatcher->addSubscriber(new DebuggerSubscriber($app));
return $dispatcher;
})
);
return $app;
}, isset($environment) ? $environment : PhraseaApplication::ENV_PROD);

View File

@@ -0,0 +1,155 @@
<?php
namespace Alchemy\Phrasea\Application;
use Alchemy\EmbedProvider\EmbedServiceProvider;
use Alchemy\Phrasea\Application;
use Alchemy\Phrasea\ControllerProvider as Providers;
use Assert\Assertion;
use Silex\ControllerProviderInterface;
class RouteLoader
{
public static $defaultProviders = [
'/account/' => Providers\Root\Account::class,
'/admin/' => Providers\Admin\Root::class,
'/admin/collection' => Providers\Admin\Collection::class,
'/admin/connected-users' => Providers\Admin\ConnectedUsers::class,
'/admin/dashboard' => Providers\Admin\Dashboard::class,
'/admin/databox' => Providers\Admin\Databox::class,
'/admin/databoxes' => Providers\Admin\Databoxes::class,
'/admin/fields' => Providers\Admin\Fields::class ,
'/admin/publications' => Providers\Admin\Feeds::class,
'/admin/plugins' => Providers\Admin\Plugins::class,
'/admin/search-engine' => Providers\Admin\SearchEngine::class,
'/admin/setup' => Providers\Admin\Setup::class,
'/admin/subdefs' => Providers\Admin\Subdefs::class,
'/admin/task-manager' => Providers\Admin\TaskManager::class,
'/admin/users' => Providers\Admin\Users::class,
'/client/' => Providers\Client\Root::class,
'/datafiles' => Providers\Datafiles::class,
'/developers/' => Providers\Root\Developers::class,
'/download/' => Providers\Prod\DoDownload::class,
'/embed/' => EmbedServiceProvider::class,
'/feeds/' => Providers\Root\RSSFeeds::class,
'/include/minify' => Providers\Minifier::class,
'/login/' => Providers\Root\Login::class,
'/lightbox' => Providers\Lightbox::class,
'/permalink' => Providers\Permalink::class,
'/prod/baskets' => Providers\Prod\BasketProvider::class,
'/prod/bridge/' => Providers\Prod\Bridge::class,
'/prod/download' => Providers\Prod\Download::class,
'/prod/export/' => Providers\Prod\Export::class,
'/prod/feeds' => Providers\Prod\Feed::class,
'/prod/language' => Providers\Prod\Language::class,
'/prod/lazaret/' => Providers\Prod\Lazaret::class,
'/prod/lists' => Providers\Prod\UsrLists::class,
'/prod/order/' => Providers\Prod\Order::class,
'/prod/printer/' => Providers\Prod\Printer::class,
'/prod/push/' => Providers\Prod\Push::class,
'/prod/query/' => Providers\Prod\Query::class,
'/prod/records/' => Providers\Prod\Record::class,
'/prod/records/edit' => Providers\Prod\Edit::class,
'/prod/records/movecollection' => Providers\Prod\MoveCollection::class,
'/prod/records/property' => Providers\Prod\Property::class,
'/prod/share/' => Providers\Prod\Share::class,
'/prod/story' => Providers\Prod\Story::class,
'/prod/tools/' => Providers\Prod\Tools::class,
'/prod/tooltip' => Providers\Prod\Tooltip::class,
'/prod/TOU/' => Providers\Prod\TOU::class,
'/prod/upload/' => Providers\Prod\Upload::class,
'/prod/WorkZone' => Providers\Prod\WorkZone::class,
'/prod/' => Providers\Prod\Root::class,
'/report/activity' => Providers\Report\Activity::class,
'/report/informations' => Providers\Report\Information::class,
'/report/' => Providers\Report\Root::class,
'/session/' => Providers\Root\Session::class,
'/setup' => Providers\Setup::class,
'/thesaurus' => Providers\Thesaurus\Thesaurus::class,
'/user/notifications/' => Providers\User\Notifications::class,
'/user/preferences/' => Providers\User\Preferences::class,
'/xmlhttp' => Providers\Thesaurus\Xmlhttp::class,
'/' => Providers\Root\Root::class,
];
/**
* @var string[]
*/
private $controllerProviders = [];
/**
* @param string $prefix
* @param string $providerClass
* @throws \InvalidArgumentException
*/
public function registerProvider($prefix, $providerClass)
{
Assertion::classExists($providerClass);
$this->controllerProviders[$prefix] = $providerClass;
}
public function registerProviders(array $providers)
{
foreach ($providers as $prefix => $providerClass) {
$this->registerProvider($prefix, $providerClass);
}
}
/**
* @param Application $app
*/
public function bindRoutes(Application $app)
{
// @todo Move me out of here !
// Controllers with routes referenced by api
$this->controllerProviders[$app['controller.media_accessor.route_prefix']] = Providers\MediaAccessor::class;
foreach ($this->controllerProviders as $prefix => $class) {
$app->mount($prefix, new $class);
}
}
/**
* @param Application $app
* @param $routeParameter
*/
public function bindPluginRoutes(Application $app, $routeParameter)
{
foreach ($app[$routeParameter] as $provider) {
$prefix = '';
$providerKey = $provider;
if (is_array($provider)) {
list($prefix, $providerKey) = $provider;
}
if (! $this->isValidProviderDefinition($app, $prefix, $provider)) {
continue;
}
$prefix = '/' . ltrim($prefix, '/');
$provider = $app[$providerKey];
if (!$provider instanceof ControllerProviderInterface) {
continue;
}
$app->mount($prefix, $provider);
}
}
private function isValidProviderDefinition(Application $app, $prefix, $provider)
{
if (!is_string($prefix) || !is_string($provider)) {
return false;
}
if (!isset($app[$provider])) {
return false;
}
return true;
}
}

View File

@@ -0,0 +1,106 @@
<?php
namespace Alchemy\Phrasea\ControllerProvider;
use Alchemy\EmbedProvider\EmbedServiceProvider;
use Silex\Application;
use Silex\ServiceProviderInterface;
class ControllerProviderServiceProvider implements ServiceProviderInterface
{
private $controllerProviders = [];
/**
* Registers services on the given app.
*
* This method should only be used to configure services and parameters.
* It should not get services.
*/
public function register(Application $app)
{
$this->loadProviders();
foreach ($this->controllerProviders as $class => $values) {
$app->register(new $class, $values);
}
}
/**
* Bootstraps the application.
*
* This method is called after all services are registered
* and should be used for "dynamic" configuration (whenever
* a service must be requested).
*/
public function boot(Application $app)
{
// Nothing to do here
}
public function loadProviders()
{
$this->controllerProviders = [
Admin\Collection::class => [],
Admin\ConnectedUsers::class => [],
Admin\Dashboard::class => [],
Admin\Databox::class => [],
Admin\Databoxes::class => [],
Admin\Feeds::class => [],
Admin\Fields::class => [],
Admin\Plugins::class => [],
Admin\Root::class => [],
Admin\SearchEngine::class => [],
Admin\Setup::class => [],
Admin\Subdefs::class => [],
Admin\TaskManager::class => [],
Admin\Users::class => [],
Client\Root::class => [],
Datafiles::class => [],
Lightbox::class => [],
MediaAccessor::class => [],
Minifier::class => [],
Permalink::class => [],
Prod\BasketProvider::class => [],
Prod\Bridge::class => [],
Prod\DoDownload::class => [],
Prod\Download::class => [],
Prod\Edit::class => [],
Prod\Export::class => [],
Prod\Feed::class => [],
Prod\Language::class => [],
Prod\Lazaret::class => [],
Prod\MoveCollection::class => [],
Prod\Order::class => [],
Prod\Printer::class => [],
Prod\Property::class => [],
Prod\Push::class => [],
Prod\Query::class => [],
Prod\Record::class => [],
Prod\Root::class => [],
Prod\Share::class => [],
Prod\Story::class => [],
Prod\Tools::class => [],
Prod\Tooltip::class => [],
Prod\TOU::class => [],
Prod\Upload::class => [],
Prod\UsrLists::class => [],
Prod\WorkZone::class => [],
Report\Activity::class => [],
Report\Information::class => [],
Report\Root::class => [],
Root\Account::class => [],
Root\Developers::class => [],
Root\Login::class => [],
Root\Root::class => [],
Root\RSSFeeds::class => [],
Root\Session::class => [],
Setup::class => [],
Thesaurus\Thesaurus::class => [],
Thesaurus\Xmlhttp::class => [],
User\Notifications::class => [],
User\Preferences::class => [],
EmbedServiceProvider::class => [],
];
}
}

View File

@@ -37,10 +37,6 @@ class DebuggerSubscriber implements EventSubscriberInterface
public function checkIp(GetResponseEvent $event) public function checkIp(GetResponseEvent $event)
{ {
if (Application::ENV_DEV !== $this->app->getEnvironment()) {
return;
}
if ($this->app['configuration.store']->isSetup() && $this->app['conf']->has(['debugger', 'allowed-ips'])) { if ($this->app['configuration.store']->isSetup() && $this->app['conf']->has(['debugger', 'allowed-ips'])) {
$allowedIps = $this->app['conf']->get(['debugger', 'allowed-ips']); $allowedIps = $this->app['conf']->get(['debugger', 'allowed-ips']);
$allowedIps = is_array($allowedIps) ? $allowedIps : [$allowedIps]; $allowedIps = is_array($allowedIps) ? $allowedIps : [$allowedIps];

View File

@@ -0,0 +1,53 @@
<?php
namespace Alchemy\Phrasea\Core\Middleware;
use Assert\Assertion;
use Silex\Application;
use Silex\ServiceProviderInterface;
use Symfony\Component\HttpFoundation\Request;
class SetupMiddlewareProvider implements ServiceProviderInterface
{
/**
* Registers services on the given app.
*
* This method should only be used to configure services and parameters.
* It should not get services.
* @param Application $app
*/
public function register(Application $app)
{
Assertion::isInstanceOf($app, \Alchemy\Phrasea\Application::class);
$app['setup.validate-config'] = $app->share($app->protect(function (Request $request) use ($app) {
if (0 === strpos($request->getPathInfo(), '/setup')) {
if (!$app['phraseanet.configuration-tester']->isInstalled()) {
if (!$app['phraseanet.configuration-tester']->isBlank()) {
if ('setup_upgrade_instructions' !== $app['request']->attributes->get('_route')) {
return $app->redirectPath('setup_upgrade_instructions');
}
}
} elseif (!$app['phraseanet.configuration-tester']->isBlank()) {
return $app->redirectPath('homepage');
}
} else {
if (false === strpos($request->getPathInfo(), '/include/minify')) {
$app['firewall']->requireSetup();
}
}
}));
}
/**
* Bootstraps the application.
*
* This method is called after all services are registered
* and should be used for "dynamic" configuration (whenever
* a service must be requested).
*/
public function boot(Application $app)
{
// no-op
}
}

View File

@@ -0,0 +1,48 @@
<?php
namespace Alchemy\Tests\Phrasea\Application;
use Alchemy\Phrasea\Application;
use Alchemy\Phrasea\Application\RouteLoader;
use Prophecy\Argument;
use Silex\ControllerCollection;
use Silex\ControllerProviderInterface;
use Silex\Route;
class RouteLoaderTest extends \PHPUnit_Framework_TestCase
{
/**
* @expectedException \InvalidArgumentException
*/
public function testRegisterProviderWithInvalidClassFails()
{
$routeLoader = new RouteLoader();
$routeLoader->registerProvider('test_invalid_class', '\Alchemy\Tests\Phrasea\Application\UndefinedClass');
}
public function testRegisteredProvidersAreMountedInApplication()
{
$application = $this->prophesize(Application::class);
$application->offsetGet(Argument::any())
->shouldBeCalled();
$application->mount(Argument::any(), Argument::type(ControllerProviderInterface::class))
->shouldBeCalled();
$application->mount(Argument::exact('mount_prefix'), Argument::type(MockControllerProvider::class))
->shouldBeCalled();
$routeLoader = new RouteLoader();
$routeLoader->registerProvider('mount_prefix', MockControllerProvider::class);
$routeLoader->bindRoutes($application->reveal());
}
}
class MockControllerProvider implements ControllerProviderInterface
{
public function connect(\Silex\Application $app)
{
return new ControllerCollection(new Route('/'));
}
}

View File

@@ -37,9 +37,11 @@ class DebuggerSubscriberTest extends \PhraseanetTestCase
$app['conf']->set(['debugger', 'allowed-ips'], $authorized); $app['conf']->set(['debugger', 'allowed-ips'], $authorized);
$app['dispatcher']->addSubscriber(new DebuggerSubscriber($app)); $app['dispatcher']->addSubscriber(new DebuggerSubscriber($app));
$app->get('/', function () { $app->get('/', function () {
return 'success'; return 'success';
}); });
$app->boot(); $app->boot();
if ($exceptionThrown) { if ($exceptionThrown) {
@@ -53,12 +55,12 @@ class DebuggerSubscriberTest extends \PhraseanetTestCase
{ {
return [ return [
[false, Application::ENV_PROD, '127.0.0.1', []], [false, Application::ENV_PROD, '127.0.0.1', []],
[false, Application::ENV_PROD, '192.168.0.1', []], [true, Application::ENV_PROD, '192.168.0.1', []],
[false, Application::ENV_DEV, '127.0.0.1', []], [false, Application::ENV_DEV, '127.0.0.1', []],
[true, Application::ENV_DEV, '192.168.0.1', []], [true, Application::ENV_DEV, '192.168.0.1', []],
[false, Application::ENV_DEV, '192.168.0.1', ['192.168.0.1']], [false, Application::ENV_DEV, '192.168.0.1', ['192.168.0.1']],
[false, Application::ENV_TEST, '127.0.0.1', []], [false, Application::ENV_TEST, '127.0.0.1', []],
[false, Application::ENV_TEST, '192.168.0.1', []], [true, Application::ENV_TEST, '192.168.0.1', []],
]; ];
} }
} }

View File

@@ -12,9 +12,8 @@
use Alchemy\Phrasea\Application; use Alchemy\Phrasea\Application;
use Symfony\Component\Debug\ErrorHandler; use Symfony\Component\Debug\ErrorHandler;
require_once __DIR__ . "/../lib/autoload.php";
error_reporting(0); require_once __DIR__ . "/../lib/autoload.php";
ErrorHandler::register(); ErrorHandler::register();