From 92b7dd3335a6572debeacfb5faa82c63a5e67888 Mon Sep 17 00:00:00 2001 From: Marvin Borner Date: Fri, 8 Jun 2018 20:03:25 +0200 Subject: Some minor fixes --- .../core/src/ServicesProvider/ServicesProvider.php | 1242 ++++++++++---------- 1 file changed, 621 insertions(+), 621 deletions(-) (limited to 'main/app/sprinkles/core/src/ServicesProvider') diff --git a/main/app/sprinkles/core/src/ServicesProvider/ServicesProvider.php b/main/app/sprinkles/core/src/ServicesProvider/ServicesProvider.php index 6ac8c41..0adc75a 100644 --- a/main/app/sprinkles/core/src/ServicesProvider/ServicesProvider.php +++ b/main/app/sprinkles/core/src/ServicesProvider/ServicesProvider.php @@ -1,621 +1,621 @@ -config; - - if ($config['alert.storage'] == 'cache') { - return new CacheAlertStream($config['alert.key'], $c->translator, $c->cache, $c->config); - } else if ($config['alert.storage'] == 'session') { - return new SessionAlertStream($config['alert.key'], $c->translator, $c->session); - } else { - throw new \Exception("Bad alert storage handler type '{$config['alert.storage']}' specified in configuration file."); - } - }; - - /** - * Asset loader service. - * - * Loads assets from a specified relative location. - * Assets are Javascript, CSS, image, and other files used by your site. - */ - $container['assetLoader'] = function ($c) { - $basePath = \UserFrosting\SPRINKLES_DIR; - $pattern = "/^[A-Za-z0-9_\-]+\/assets\//"; - - $al = new AssetLoader($basePath, $pattern); - return $al; - }; - - /** - * Asset manager service. - * - * Loads raw or compiled asset information from your bundle.config.json schema file. - * Assets are Javascript, CSS, image, and other files used by your site. - */ - $container['assets'] = function ($c) { - $config = $c->config; - $locator = $c->locator; - - // Load asset schema - if ($config['assets.use_raw']) { - $baseUrl = $config['site.uri.public'] . '/' . $config['assets.raw.path']; - $removePrefix = \UserFrosting\APP_DIR_NAME . \UserFrosting\DS . \UserFrosting\SPRINKLES_DIR_NAME; - $aub = new AssetUrlBuilder($locator, $baseUrl, $removePrefix, 'assets'); - - $as = new AssetBundleSchema($aub); - - // Load Sprinkle assets - $sprinkles = $c->sprinkleManager->getSprinkleNames(); - - // move this out into PathBuilder and Loader classes in userfrosting/assets - // This would also allow us to define and load bundles in themes - $bundleSchemas = array_reverse($locator->findResources('sprinkles://' . $config['assets.raw.schema'], TRUE, TRUE)); - - foreach ($bundleSchemas as $schema) { - if (file_exists($schema)) { - $as->loadRawSchemaFile($schema); - } - } - } else { - $baseUrl = $config['site.uri.public'] . '/' . $config['assets.compiled.path']; - $aub = new CompiledAssetUrlBuilder($baseUrl); - - $as = new AssetBundleSchema($aub); - $as->loadCompiledSchemaFile($locator->findResource("build://" . $config['assets.compiled.schema'], TRUE, TRUE)); - } - - $am = new AssetManager($aub, $as); - - return $am; - }; - - /** - * Cache service. - * - * @return \Illuminate\Cache\Repository - */ - $container['cache'] = function ($c) { - - $config = $c->config; - - if ($config['cache.driver'] == 'file') { - $path = $c->locator->findResource('cache://', TRUE, TRUE); - $cacheStore = new TaggableFileStore($path); - } else if ($config['cache.driver'] == 'memcached') { - // We need to inject the prefix in the memcached config - $config = array_merge($config['cache.memcached'], ['prefix' => $config['cache.prefix']]); - $cacheStore = new MemcachedStore($config); - } else if ($config['cache.driver'] == 'redis') { - // We need to inject the prefix in the redis config - $config = array_merge($config['cache.redis'], ['prefix' => $config['cache.prefix']]); - $cacheStore = new RedisStore($config); - } else { - throw new \Exception("Bad cache store type '{$config['cache.driver']}' specified in configuration file."); - } - - return $cacheStore->instance(); - }; - - /** - * Middleware to check environment. - * - * We should cache the results of this, the first time that it succeeds. - */ - $container['checkEnvironment'] = function ($c) { - $checkEnvironment = new CheckEnvironment($c->view, $c->locator, $c->cache); - return $checkEnvironment; - }; - - /** - * Class mapper. - * - * Creates an abstraction on top of class names to allow extending them in sprinkles. - */ - $container['classMapper'] = function ($c) { - $classMapper = new ClassMapper(); - $classMapper->setClassMapping('query_builder', 'UserFrosting\Sprinkle\Core\Database\Builder'); - $classMapper->setClassMapping('throttle', 'UserFrosting\Sprinkle\Core\Database\Models\Throttle'); - return $classMapper; - }; - - /** - * Site config service (separate from Slim settings). - * - * Will attempt to automatically determine which config file(s) to use based on the value of the UF_MODE environment variable. - */ - $container['config'] = function ($c) { - // Grab any relevant dotenv variables from the .env file - try { - $dotenv = new Dotenv(\UserFrosting\APP_DIR); - $dotenv->load(); - } catch (InvalidPathException $e) { - // Skip loading the environment config file if it doesn't exist. - } - - // Get configuration mode from environment - $mode = getenv('UF_MODE') ?: ''; - - // Construct and load config repository - $builder = new ConfigPathBuilder($c->locator, 'config://'); - $loader = new ArrayFileLoader($builder->buildPaths($mode)); - $config = new Repository($loader->load()); - - // Construct base url from components, if not explicitly specified - if (!isset($config['site.uri.public'])) { - $base_uri = $config['site.uri.base']; - - $public = new Uri( - $base_uri['scheme'], - $base_uri['host'], - $base_uri['port'], - $base_uri['path'] - ); - - // Slim\Http\Uri likes to add trailing slashes when the path is empty, so this fixes that. - $config['site.uri.public'] = trim($public, '/'); - } - - // Hacky fix to prevent sessions from being hit too much: ignore CSRF middleware for requests for raw assets ;-) - // See https://github.com/laravel/framework/issues/8172#issuecomment-99112012 for more information on why it's bad to hit Laravel sessions multiple times in rapid succession. - $csrfBlacklist = $config['csrf.blacklist']; - $csrfBlacklist['^/' . $config['assets.raw.path']] = [ - 'GET' - ]; - $csrfBlacklist['^/wormhole'] = [ - 'POST' - ]; - - $config->set('csrf.blacklist', $csrfBlacklist); - - return $config; - }; - - /** - * Initialize CSRF guard middleware. - * - * @see https://github.com/slimphp/Slim-Csrf - */ - $container['csrf'] = function ($c) { - $csrfKey = $c->config['session.keys.csrf']; - - // Workaround so that we can pass storage into CSRF guard. - // If we tried to directly pass the indexed portion of `session` (for example, $c->session['site.csrf']), - // we would get an 'Indirect modification of overloaded element of UserFrosting\Session\Session' error. - // If we tried to assign an array and use that, PHP would only modify the local variable, and not the session. - // Since ArrayObject is an object, PHP will modify the object itself, allowing it to persist in the session. - if (!$c->session->has($csrfKey)) { - $c->session[$csrfKey] = new \ArrayObject(); - } - $csrfStorage = $c->session[$csrfKey]; - - $onFailure = function ($request, $response, $next) { - $e = new BadRequestException("The CSRF code was invalid or not provided."); - $e->addUserMessage('CSRF_MISSING'); - throw $e; - - return $next($request, $response); - }; - - return new Guard($c->config['csrf.name'], $csrfStorage, $onFailure, $c->config['csrf.storage_limit'], $c->config['csrf.strength'], $c->config['csrf.persistent_token']); - }; - - /** - * Initialize Eloquent Capsule, which provides the database layer for UF. - * - * construct the individual objects rather than using the facade - */ - $container['db'] = function ($c) { - $config = $c->config; - - $capsule = new Capsule; - - foreach ($config['db'] as $name => $dbConfig) { - $capsule->addConnection($dbConfig, $name); - } - - $queryEventDispatcher = new Dispatcher(new Container); - - $capsule->setEventDispatcher($queryEventDispatcher); - - // Register as global connection - $capsule->setAsGlobal(); - - // Start Eloquent - $capsule->bootEloquent(); - - if ($config['debug.queries']) { - $logger = $c->queryLogger; - - foreach ($config['db'] as $name => $dbConfig) { - $capsule->connection($name)->enableQueryLog(); - } - - // Register listener - $queryEventDispatcher->listen(QueryExecuted::class, function ($query) use ($logger) { - $logger->debug("Query executed on database [{$query->connectionName}]:", [ - 'query' => $query->sql, - 'bindings' => $query->bindings, - 'time' => $query->time . ' ms' - ]); - }); - } - - return $capsule; - }; - - /** - * Debug logging with Monolog. - * - * Extend this service to push additional handlers onto the 'debug' log stack. - */ - $container['debugLogger'] = function ($c) { - $logger = new Logger('debug'); - - $logFile = $c->locator->findResource('log://userfrosting.log', TRUE, TRUE); - - $handler = new StreamHandler($logFile); - - $formatter = new MixedFormatter(NULL, NULL, TRUE); - - $handler->setFormatter($formatter); - $logger->pushHandler($handler); - - return $logger; - }; - - /** - * Custom error-handler for recoverable errors. - */ - $container['errorHandler'] = function ($c) { - $settings = $c->settings; - - $handler = new ExceptionHandlerManager($c, $settings['displayErrorDetails']); - - // Register the base HttpExceptionHandler. - $handler->registerHandler('\UserFrosting\Support\Exception\HttpException', '\UserFrosting\Sprinkle\Core\Error\Handler\HttpExceptionHandler'); - - // Register the NotFoundExceptionHandler. - $handler->registerHandler('\UserFrosting\Support\Exception\NotFoundException', '\UserFrosting\Sprinkle\Core\Error\Handler\NotFoundExceptionHandler'); - - // Register the PhpMailerExceptionHandler. - $handler->registerHandler('\phpmailerException', '\UserFrosting\Sprinkle\Core\Error\Handler\PhpMailerExceptionHandler'); - - return $handler; - }; - - /** - * Error logging with Monolog. - * - * Extend this service to push additional handlers onto the 'error' log stack. - */ - $container['errorLogger'] = function ($c) { - $log = new Logger('errors'); - - $logFile = $c->locator->findResource('log://userfrosting.log', TRUE, TRUE); - - $handler = new StreamHandler($logFile, Logger::WARNING); - - $formatter = new LineFormatter(NULL, NULL, TRUE); - - $handler->setFormatter($formatter); - $log->pushHandler($handler); - - return $log; - }; - - /** - * Factory service with FactoryMuffin. - * - * Provide access to factories for the rapid creation of objects for the purpose of testing - */ - $container['factory'] = function ($c) { - - // Get the path of all of the sprinkle's factories - $factoriesPath = $c->locator->findResources('factories://', TRUE, TRUE); - - // Create a new Factory Muffin instance - $fm = new FactoryMuffin(); - - // Load all of the model definitions - $fm->loadFactories($factoriesPath); - - // Set the locale. Could be the config one, but for testing English should do - Faker::setLocale('en_EN'); - - return $fm; - }; - - /** - * Builds search paths for locales in all Sprinkles. - */ - $container['localePathBuilder'] = function ($c) { - $config = $c->config; - - // Make sure the locale config is a valid string - if (!is_string($config['site.locales.default']) || $config['site.locales.default'] == '') { - throw new \UnexpectedValueException('The locale config is not a valid string.'); - } - - // Load the base locale file(s) as specified in the configuration - $locales = explode(',', $config['site.locales.default']); - - return new LocalePathBuilder($c->locator, 'locale://', $locales); - }; - - /** - * Mail service. - */ - $container['mailer'] = function ($c) { - $mailer = new Mailer($c->mailLogger, $c->config['mail']); - - // Use UF debug settings to override any service-specific log settings. - if (!$c->config['debug.smtp']) { - $mailer->getPhpMailer()->SMTPDebug = 0; - } - - return $mailer; - }; - - /** - * Mail logging service. - * - * PHPMailer will use this to log SMTP activity. - * Extend this service to push additional handlers onto the 'mail' log stack. - */ - $container['mailLogger'] = function ($c) { - $log = new Logger('mail'); - - $logFile = $c->locator->findResource('log://userfrosting.log', TRUE, TRUE); - - $handler = new StreamHandler($logFile); - $formatter = new LineFormatter(NULL, NULL, TRUE); - - $handler->setFormatter($formatter); - $log->pushHandler($handler); - - return $log; - }; - - /** - * Error-handler for 404 errors. Notice that we manually create a UserFrosting NotFoundException, - * and a NotFoundExceptionHandler. This lets us pass through to the UF error handling system. - */ - $container['notFoundHandler'] = function ($c) { - return function ($request, $response) use ($c) { - $exception = new NotFoundException; - $handler = new NotFoundExceptionHandler($c, $request, $response, $exception, $c->settings['displayErrorDetails']); - return $handler->handle(); - }; - }; - - /** - * Error-handler for PHP runtime errors. Notice that we just pass this through to our general-purpose - * error-handling service. - */ - $container['phpErrorHandler'] = function ($c) { - return $c->errorHandler; - }; - - /** - * Laravel query logging with Monolog. - * - * Extend this service to push additional handlers onto the 'query' log stack. - */ - $container['queryLogger'] = function ($c) { - $logger = new Logger('query'); - - $logFile = $c->locator->findResource('log://userfrosting.log', TRUE, TRUE); - - $handler = new StreamHandler($logFile); - - $formatter = new MixedFormatter(NULL, NULL, TRUE); - - $handler->setFormatter($formatter); - $logger->pushHandler($handler); - - return $logger; - }; - - /** - * Override Slim's default router with the UF router. - */ - $container['router'] = function ($c) { - $routerCacheFile = FALSE; - if (isset($c->config['settings.routerCacheFile'])) { - $routerCacheFile = $c->config['settings.routerCacheFile']; - } - - return (new Router)->setCacheFile($routerCacheFile); - }; - - /** - * Start the PHP session, with the name and parameters specified in the configuration file. - */ - $container['session'] = function ($c) { - $config = $c->config; - - // Create appropriate handler based on config - if ($config['session.handler'] == 'file') { - $fs = new FileSystem; - $handler = new FileSessionHandler($fs, $c->locator->findResource('session://'), $config['session.minutes']); - } else if ($config['session.handler'] == 'database') { - $connection = $c->db->connection(); - // Table must exist, otherwise an exception will be thrown - $handler = new DatabaseSessionHandler($connection, $config['session.database.table'], $config['session.minutes']); - } else { - throw new \Exception("Bad session handler type '{$config['session.handler']}' specified in configuration file."); - } - - // Create, start and return a new wrapper for $_SESSION - $session = new Session($handler, $config['session']); - $session->start(); - - return $session; - }; - - /** - * Request throttler. - * - * Throttles (rate-limits) requests of a predefined type, with rules defined in site config. - */ - $container['throttler'] = function ($c) { - $throttler = new Throttler($c->classMapper); - - $config = $c->config; - - if ($config->has('throttles') && ($config['throttles'] !== NULL)) { - foreach ($config['throttles'] as $type => $rule) { - if ($rule) { - $throttleRule = new ThrottleRule($rule['method'], $rule['interval'], $rule['delays']); - $throttler->addThrottleRule($type, $throttleRule); - } else { - $throttler->addThrottleRule($type, NULL); - } - } - } - - return $throttler; - }; - - /** - * Translation service, for translating message tokens. - */ - $container['translator'] = function ($c) { - // Load the translations - $paths = $c->localePathBuilder->buildPaths(); - $loader = new ArrayFileLoader($paths); - - // Create the $translator object - $translator = new MessageTranslator($loader->load()); - - return $translator; - }; - - /** - * Set up Twig as the view, adding template paths for all sprinkles and the Slim Twig extension. - * - * Also adds the UserFrosting core Twig extension, which provides additional functions, filters, global variables, etc. - */ - $container['view'] = function ($c) { - $templatePaths = $c->locator->findResources('templates://', TRUE, TRUE); - - $view = new Twig($templatePaths); - - $loader = $view->getLoader(); - - $sprinkles = $c->sprinkleManager->getSprinkleNames(); - - // Add Sprinkles' templates namespaces - foreach ($sprinkles as $sprinkle) { - $path = \UserFrosting\SPRINKLES_DIR . \UserFrosting\DS . - $sprinkle . \UserFrosting\DS . - \UserFrosting\TEMPLATE_DIR_NAME . \UserFrosting\DS; - - if (is_dir($path)) { - $loader->addPath($path, $sprinkle); - } - } - - $twig = $view->getEnvironment(); - - if ($c->config['cache.twig']) { - $twig->setCache($c->locator->findResource('cache://twig', TRUE, TRUE)); - } - - if ($c->config['debug.twig']) { - $twig->enableDebug(); - $view->addExtension(new \Twig_Extension_Debug()); - } - - // Register the Slim extension with Twig - $slimExtension = new TwigExtension( - $c->router, - $c->request->getUri() - ); - $view->addExtension($slimExtension); - - // Register the core UF extension with Twig - $coreExtension = new CoreExtension($c); - $view->addExtension($coreExtension); - - return $view; - }; - } -} +config; + + if ($config['alert.storage'] == 'cache') { + return new CacheAlertStream($config['alert.key'], $c->translator, $c->cache, $c->config); + } else if ($config['alert.storage'] == 'session') { + return new SessionAlertStream($config['alert.key'], $c->translator, $c->session); + } else { + throw new \Exception("Bad alert storage handler type '{$config['alert.storage']}' specified in configuration file."); + } + }; + + /** + * Asset loader service. + * + * Loads assets from a specified relative location. + * Assets are Javascript, CSS, image, and other files used by your site. + */ + $container['assetLoader'] = function ($c) { + $basePath = \UserFrosting\SPRINKLES_DIR; + $pattern = "/^[A-Za-z0-9_\-]+\/assets\//"; + + $al = new AssetLoader($basePath, $pattern); + return $al; + }; + + /** + * Asset manager service. + * + * Loads raw or compiled asset information from your bundle.config.json schema file. + * Assets are Javascript, CSS, image, and other files used by your site. + */ + $container['assets'] = function ($c) { + $config = $c->config; + $locator = $c->locator; + + // Load asset schema + if ($config['assets.use_raw']) { + $baseUrl = $config['site.uri.public'] . '/' . $config['assets.raw.path']; + $removePrefix = \UserFrosting\APP_DIR_NAME . \UserFrosting\DS . \UserFrosting\SPRINKLES_DIR_NAME; + $aub = new AssetUrlBuilder($locator, $baseUrl, $removePrefix, 'assets'); + + $as = new AssetBundleSchema($aub); + + // Load Sprinkle assets + $sprinkles = $c->sprinkleManager->getSprinkleNames(); + + // move this out into PathBuilder and Loader classes in userfrosting/assets + // This would also allow us to define and load bundles in themes + $bundleSchemas = array_reverse($locator->findResources('sprinkles://' . $config['assets.raw.schema'], TRUE, TRUE)); + + foreach ($bundleSchemas as $schema) { + if (file_exists($schema)) { + $as->loadRawSchemaFile($schema); + } + } + } else { + $baseUrl = $config['site.uri.public'] . '/' . $config['assets.compiled.path']; + $aub = new CompiledAssetUrlBuilder($baseUrl); + + $as = new AssetBundleSchema($aub); + $as->loadCompiledSchemaFile($locator->findResource("build://" . $config['assets.compiled.schema'], TRUE, TRUE)); + } + + $am = new AssetManager($aub, $as); + + return $am; + }; + + /** + * Cache service. + * + * @return \Illuminate\Cache\Repository + */ + $container['cache'] = function ($c) { + + $config = $c->config; + + if ($config['cache.driver'] == 'file') { + $path = $c->locator->findResource('cache://', TRUE, TRUE); + $cacheStore = new TaggableFileStore($path); + } else if ($config['cache.driver'] == 'memcached') { + // We need to inject the prefix in the memcached config + $config = array_merge($config['cache.memcached'], ['prefix' => $config['cache.prefix']]); + $cacheStore = new MemcachedStore($config); + } else if ($config['cache.driver'] == 'redis') { + // We need to inject the prefix in the redis config + $config = array_merge($config['cache.redis'], ['prefix' => $config['cache.prefix']]); + $cacheStore = new RedisStore($config); + } else { + throw new \Exception("Bad cache store type '{$config['cache.driver']}' specified in configuration file."); + } + + return $cacheStore->instance(); + }; + + /** + * Middleware to check environment. + * + * We should cache the results of this, the first time that it succeeds. + */ + $container['checkEnvironment'] = function ($c) { + $checkEnvironment = new CheckEnvironment($c->view, $c->locator, $c->cache); + return $checkEnvironment; + }; + + /** + * Class mapper. + * + * Creates an abstraction on top of class names to allow extending them in sprinkles. + */ + $container['classMapper'] = function ($c) { + $classMapper = new ClassMapper(); + $classMapper->setClassMapping('query_builder', 'UserFrosting\Sprinkle\Core\Database\Builder'); + $classMapper->setClassMapping('throttle', 'UserFrosting\Sprinkle\Core\Database\Models\Throttle'); + return $classMapper; + }; + + /** + * Site config service (separate from Slim settings). + * + * Will attempt to automatically determine which config file(s) to use based on the value of the UF_MODE environment variable. + */ + $container['config'] = function ($c) { + // Grab any relevant dotenv variables from the .env file + try { + $dotenv = new Dotenv(\UserFrosting\APP_DIR); + $dotenv->load(); + } catch (InvalidPathException $e) { + // Skip loading the environment config file if it doesn't exist. + } + + // Get configuration mode from environment + $mode = getenv('UF_MODE') ?: ''; + + // Construct and load config repository + $builder = new ConfigPathBuilder($c->locator, 'config://'); + $loader = new ArrayFileLoader($builder->buildPaths($mode)); + $config = new Repository($loader->load()); + + // Construct base url from components, if not explicitly specified + if (!isset($config['site.uri.public'])) { + $base_uri = $config['site.uri.base']; + + $public = new Uri( + $base_uri['scheme'], + $base_uri['host'], + $base_uri['port'], + $base_uri['path'] + ); + + // Slim\Http\Uri likes to add trailing slashes when the path is empty, so this fixes that. + $config['site.uri.public'] = trim($public, '/'); + } + + // Hacky fix to prevent sessions from being hit too much: ignore CSRF middleware for requests for raw assets ;-) + // See https://github.com/laravel/framework/issues/8172#issuecomment-99112012 for more information on why it's bad to hit Laravel sessions multiple times in rapid succession. + $csrfBlacklist = $config['csrf.blacklist']; + $csrfBlacklist['^/' . $config['assets.raw.path']] = [ + 'GET' + ]; + $csrfBlacklist['^/wormhole'] = [ + 'POST' + ]; + + $config->set('csrf.blacklist', $csrfBlacklist); + + return $config; + }; + + /** + * Initialize CSRF guard middleware. + * + * @see https://github.com/slimphp/Slim-Csrf + */ + $container['csrf'] = function ($c) { + $csrfKey = $c->config['session.keys.csrf']; + + // Workaround so that we can pass storage into CSRF guard. + // If we tried to directly pass the indexed portion of `session` (for example, $c->session['site.csrf']), + // we would get an 'Indirect modification of overloaded element of UserFrosting\Session\Session' error. + // If we tried to assign an array and use that, PHP would only modify the local variable, and not the session. + // Since ArrayObject is an object, PHP will modify the object itself, allowing it to persist in the session. + if (!$c->session->has($csrfKey)) { + $c->session[$csrfKey] = new \ArrayObject(); + } + $csrfStorage = $c->session[$csrfKey]; + + $onFailure = function ($request, $response, $next) { + $e = new BadRequestException("The CSRF code was invalid or not provided."); + $e->addUserMessage('CSRF_MISSING'); + throw $e; + + return $next($request, $response); + }; + + return new Guard($c->config['csrf.name'], $csrfStorage, $onFailure, $c->config['csrf.storage_limit'], $c->config['csrf.strength'], $c->config['csrf.persistent_token']); + }; + + /** + * Initialize Eloquent Capsule, which provides the database layer for UF. + * + * construct the individual objects rather than using the facade + */ + $container['db'] = function ($c) { + $config = $c->config; + + $capsule = new Capsule; + + foreach ($config['db'] as $name => $dbConfig) { + $capsule->addConnection($dbConfig, $name); + } + + $queryEventDispatcher = new Dispatcher(new Container); + + $capsule->setEventDispatcher($queryEventDispatcher); + + // Register as global connection + $capsule->setAsGlobal(); + + // Start Eloquent + $capsule->bootEloquent(); + + if ($config['debug.queries']) { + $logger = $c->queryLogger; + + foreach ($config['db'] as $name => $dbConfig) { + $capsule->connection($name)->enableQueryLog(); + } + + // Register listener + $queryEventDispatcher->listen(QueryExecuted::class, function ($query) use ($logger) { + $logger->debug("Query executed on database [{$query->connectionName}]:", [ + 'query' => $query->sql, + 'bindings' => $query->bindings, + 'time' => $query->time . ' ms' + ]); + }); + } + + return $capsule; + }; + + /** + * Debug logging with Monolog. + * + * Extend this service to push additional handlers onto the 'debug' log stack. + */ + $container['debugLogger'] = function ($c) { + $logger = new Logger('debug'); + + $logFile = $c->locator->findResource('log://userfrosting.log', TRUE, TRUE); + + $handler = new StreamHandler($logFile); + + $formatter = new MixedFormatter(NULL, NULL, TRUE); + + $handler->setFormatter($formatter); + $logger->pushHandler($handler); + + return $logger; + }; + + /** + * Custom error-handler for recoverable errors. + */ + $container['errorHandler'] = function ($c) { + $settings = $c->settings; + + $handler = new ExceptionHandlerManager($c, $settings['displayErrorDetails']); + + // Register the base HttpExceptionHandler. + $handler->registerHandler('\UserFrosting\Support\Exception\HttpException', '\UserFrosting\Sprinkle\Core\Error\Handler\HttpExceptionHandler'); + + // Register the NotFoundExceptionHandler. + $handler->registerHandler('\UserFrosting\Support\Exception\NotFoundException', '\UserFrosting\Sprinkle\Core\Error\Handler\NotFoundExceptionHandler'); + + // Register the PhpMailerExceptionHandler. + $handler->registerHandler('\phpmailerException', '\UserFrosting\Sprinkle\Core\Error\Handler\PhpMailerExceptionHandler'); + + return $handler; + }; + + /** + * Error logging with Monolog. + * + * Extend this service to push additional handlers onto the 'error' log stack. + */ + $container['errorLogger'] = function ($c) { + $log = new Logger('errors'); + + $logFile = $c->locator->findResource('log://userfrosting.log', TRUE, TRUE); + + $handler = new StreamHandler($logFile, Logger::WARNING); + + $formatter = new LineFormatter(NULL, NULL, TRUE); + + $handler->setFormatter($formatter); + $log->pushHandler($handler); + + return $log; + }; + + /** + * Factory service with FactoryMuffin. + * + * Provide access to factories for the rapid creation of objects for the purpose of testing + */ + $container['factory'] = function ($c) { + + // Get the path of all of the sprinkle's factories + $factoriesPath = $c->locator->findResources('factories://', TRUE, TRUE); + + // Create a new Factory Muffin instance + $fm = new FactoryMuffin(); + + // Load all of the model definitions + $fm->loadFactories($factoriesPath); + + // Set the locale. Could be the config one, but for testing English should do + Faker::setLocale('en_EN'); + + return $fm; + }; + + /** + * Builds search paths for locales in all Sprinkles. + */ + $container['localePathBuilder'] = function ($c) { + $config = $c->config; + + // Make sure the locale config is a valid string + if (!is_string($config['site.locales.default']) || $config['site.locales.default'] == '') { + throw new \UnexpectedValueException('The locale config is not a valid string.'); + } + + // Load the base locale file(s) as specified in the configuration + $locales = explode(',', $config['site.locales.default']); + + return new LocalePathBuilder($c->locator, 'locale://', $locales); + }; + + /** + * Mail service. + */ + $container['mailer'] = function ($c) { + $mailer = new Mailer($c->mailLogger, $c->config['mail']); + + // Use UF debug settings to override any service-specific log settings. + if (!$c->config['debug.smtp']) { + $mailer->getPhpMailer()->SMTPDebug = 0; + } + + return $mailer; + }; + + /** + * Mail logging service. + * + * PHPMailer will use this to log SMTP activity. + * Extend this service to push additional handlers onto the 'mail' log stack. + */ + $container['mailLogger'] = function ($c) { + $log = new Logger('mail'); + + $logFile = $c->locator->findResource('log://userfrosting.log', TRUE, TRUE); + + $handler = new StreamHandler($logFile); + $formatter = new LineFormatter(NULL, NULL, TRUE); + + $handler->setFormatter($formatter); + $log->pushHandler($handler); + + return $log; + }; + + /** + * Error-handler for 404 errors. Notice that we manually create a UserFrosting NotFoundException, + * and a NotFoundExceptionHandler. This lets us pass through to the UF error handling system. + */ + $container['notFoundHandler'] = function ($c) { + return function ($request, $response) use ($c) { + $exception = new NotFoundException; + $handler = new NotFoundExceptionHandler($c, $request, $response, $exception, $c->settings['displayErrorDetails']); + return $handler->handle(); + }; + }; + + /** + * Error-handler for PHP runtime errors. Notice that we just pass this through to our general-purpose + * error-handling service. + */ + $container['phpErrorHandler'] = function ($c) { + return $c->errorHandler; + }; + + /** + * Laravel query logging with Monolog. + * + * Extend this service to push additional handlers onto the 'query' log stack. + */ + $container['queryLogger'] = function ($c) { + $logger = new Logger('query'); + + $logFile = $c->locator->findResource('log://userfrosting.log', TRUE, TRUE); + + $handler = new StreamHandler($logFile); + + $formatter = new MixedFormatter(NULL, NULL, TRUE); + + $handler->setFormatter($formatter); + $logger->pushHandler($handler); + + return $logger; + }; + + /** + * Override Slim's default router with the UF router. + */ + $container['router'] = function ($c) { + $routerCacheFile = FALSE; + if (isset($c->config['settings.routerCacheFile'])) { + $routerCacheFile = $c->config['settings.routerCacheFile']; + } + + return (new Router)->setCacheFile($routerCacheFile); + }; + + /** + * Start the PHP session, with the name and parameters specified in the configuration file. + */ + $container['session'] = function ($c) { + $config = $c->config; + + // Create appropriate handler based on config + if ($config['session.handler'] == 'file') { + $fs = new FileSystem; + $handler = new FileSessionHandler($fs, $c->locator->findResource('session://'), $config['session.minutes']); + } else if ($config['session.handler'] == 'database') { + $connection = $c->db->connection(); + // Table must exist, otherwise an exception will be thrown + $handler = new DatabaseSessionHandler($connection, $config['session.database.table'], $config['session.minutes']); + } else { + throw new \Exception("Bad session handler type '{$config['session.handler']}' specified in configuration file."); + } + + // Create, start and return a new wrapper for $_SESSION + $session = new Session($handler, $config['session']); + $session->start(); + + return $session; + }; + + /** + * Request throttler. + * + * Throttles (rate-limits) requests of a predefined type, with rules defined in site config. + */ + $container['throttler'] = function ($c) { + $throttler = new Throttler($c->classMapper); + + $config = $c->config; + + if ($config->has('throttles') && ($config['throttles'] !== NULL)) { + foreach ($config['throttles'] as $type => $rule) { + if ($rule) { + $throttleRule = new ThrottleRule($rule['method'], $rule['interval'], $rule['delays']); + $throttler->addThrottleRule($type, $throttleRule); + } else { + $throttler->addThrottleRule($type, NULL); + } + } + } + + return $throttler; + }; + + /** + * Translation service, for translating message tokens. + */ + $container['translator'] = function ($c) { + // Load the translations + $paths = $c->localePathBuilder->buildPaths(); + $loader = new ArrayFileLoader($paths); + + // Create the $translator object + $translator = new MessageTranslator($loader->load()); + + return $translator; + }; + + /** + * Set up Twig as the view, adding template paths for all sprinkles and the Slim Twig extension. + * + * Also adds the UserFrosting core Twig extension, which provides additional functions, filters, global variables, etc. + */ + $container['view'] = function ($c) { + $templatePaths = $c->locator->findResources('templates://', TRUE, TRUE); + + $view = new Twig($templatePaths); + + $loader = $view->getLoader(); + + $sprinkles = $c->sprinkleManager->getSprinkleNames(); + + // Add Sprinkles' templates namespaces + foreach ($sprinkles as $sprinkle) { + $path = \UserFrosting\SPRINKLES_DIR . \UserFrosting\DS . + $sprinkle . \UserFrosting\DS . + \UserFrosting\TEMPLATE_DIR_NAME . \UserFrosting\DS; + + if (is_dir($path)) { + $loader->addPath($path, $sprinkle); + } + } + + $twig = $view->getEnvironment(); + + if ($c->config['cache.twig']) { + $twig->setCache($c->locator->findResource('cache://twig', TRUE, TRUE)); + } + + if ($c->config['debug.twig']) { + $twig->enableDebug(); + $view->addExtension(new \Twig_Extension_Debug()); + } + + // Register the Slim extension with Twig + $slimExtension = new TwigExtension( + $c->router, + $c->request->getUri() + ); + $view->addExtension($slimExtension); + + // Register the core UF extension with Twig + $coreExtension = new CoreExtension($c); + $view->addExtension($coreExtension); + + return $view; + }; + } +} -- cgit v1.2.3