Refactored application component composition into dedicated files per component to improve separation of concerns

This commit is contained in:
Chris Kankiewicz
2019-12-15 22:16:23 -07:00
parent 9de11b5c21
commit b1d404c2c6
5 changed files with 105 additions and 63 deletions

View File

@@ -0,0 +1,32 @@
<?php
namespace App\Bootstrap;
use DI\Container;
use PHLAK\Config\Config;
class ConfigComposer
{
/** @var Container The applicaiton container */
protected $container;
/**
* Create a new ConfigComposer object.
*
* @param \DI\Container $container
*/
public function __construct(Container $container)
{
$this->container = $container;
}
/**
* Set up the Config component.
*
* @return void
*/
public function __invoke(): void
{
$this->container->set(Config::class, new Config('app/config'));
}
}

View File

@@ -3,13 +3,14 @@
namespace App\Bootstrap;
use Closure;
use DI\Container;
use PHLAK\Config\Config;
use RuntimeException;
use Symfony\Component\Finder\Finder;
use Symfony\Component\Finder\SplFileInfo;
use Tightenco\Collect\Support\Collection;
class FilesComposer
class FinderComposer
{
/** @const Application paths to be hidden */
protected const APP_FILES = ['app', 'vendor', 'index.php'];
@@ -17,16 +18,22 @@ class FilesComposer
/** @var Config Application config */
protected $config;
/** @var Finder Symfony finder component */
protected $finder;
/** @var Container The application container */
protected $container;
/** @var Collection Collection of hidden file paths */
protected $hiddenFiles;
public function __construct(Config $config, Finder $finder)
/**
* Create a new FilesComposer object.
*
* @param \PHLAK\Config\Config $config
* @param \Symfony\Component\Finder\Finder $finder
*/
public function __construct(Container $container, Config $config)
{
$this->container = $container;
$this->config = $config;
$this->finder = $finder;
$this->hiddenFiles = Collection::make(
$this->config->get('hidden_files', [])
@@ -46,34 +53,34 @@ class FilesComposer
*/
public function __invoke(): void
{
$this->finder->depth(0)->followLinks();
$this->finder->ignoreVCS($this->config->get('ignore_vcs_files', false));
$this->finder->filter(function (SplFileInfo $file) {
$finder = Finder::create()->depth(0)->followLinks();
$finder->ignoreVCS($this->config->get('ignore_vcs_files', false));
$finder->filter(function (SplFileInfo $file) {
return ! $this->hiddenFiles->contains($file->getRealPath());
});
$sortOrder = $this->config->get('sort_order', 'name');
if ($sortOrder instanceof Closure) {
$this->finder->sort($sortOrder);
$finder->sort($sortOrder);
} else {
switch ($sortOrder) {
case 'accessed':
$this->finder->sortByAccessedTime();
$finder->sortByAccessedTime();
break;
case 'changed':
$this->finder->sortByChangedTime();
$finder->sortByChangedTime();
break;
case 'modified':
$this->finder->sortByModifiedTime();
$finder->sortByModifiedTime();
break;
case 'name':
$this->finder->sortByName();
$finder->sortByName();
break;
case 'natural':
$this->finder->sortByName(true);
$finder->sortByName(true);
break;
case 'type':
$this->finder->sortByType();
$finder->sortByType();
break;
default:
throw new RuntimeException("Invalid sort option '{$sortOrder}'");
@@ -81,7 +88,9 @@ class FilesComposer
}
if ($this->config->get('reverse_sort', false)) {
$this->finder->reverseSorting();
$finder->reverseSorting();
}
$this->container->set(Finder::class, $finder);
}
}

View File

@@ -2,6 +2,7 @@
namespace App\Bootstrap;
use DI\Container;
use PHLAK\Config\Config;
use Slim\Views\Twig;
use Twig\Extension\CoreExtension;
@@ -9,26 +10,22 @@ use Twig\TwigFunction;
class ViewComposer
{
/** @var Container The application container */
protected $container;
/** @var Config Application config */
protected $config;
/** @var Twig Twig instance */
protected $twig;
/** @var string Path to theme */
protected $themePath;
/**
* Create a new ViewComposer object.
*
* @param \DI\Container $container
* @param \PHLAK\Config\Config $config
* @param \Slim\Views\Twig $twig
*/
public function __construct(Config $config, Twig $twig)
public function __construct(Container $container, Config $config)
{
$this->container = $container;
$this->config = $config;
$this->twig = $twig;
$this->themePath = $twig->getLoader()->getPaths()[0];
}
/**
@@ -38,32 +35,38 @@ class ViewComposer
*/
public function __invoke(): void
{
$this->twig->getEnvironment()->setCache(
$twig = new Twig("app/themes/{$this->config->get('theme', 'default')}");
$twig->getEnvironment()->setCache(
$this->config->get('view_cache', 'app/cache/views')
);
$this->twig->getEnvironment()->getExtension(CoreExtension::class)->setDateFormat(
$twig->getEnvironment()->getExtension(CoreExtension::class)->setDateFormat(
$this->config->get('date_format', 'Y-m-d H:i:s'), '%d days'
);
$this->registerGlobalFunctions();
$this->registerThemeFunctions();
$this->registerGlobalFunctions($twig);
$this->registerThemeFunctions($twig);
$this->container->set(Twig::class, $twig);
}
/**
* Register global Twig functions.
*
* @param \Slim\Views\Twig $twig
*
* @return void
*/
public function registerGlobalFunctions(): void
public function registerGlobalFunctions(Twig $twig): void
{
$this->twig->getEnvironment()->addFunction(
new TwigFunction('asset', function (string $path) {
return "/{$this->themePath}/{$path}";
$twig->getEnvironment()->addFunction(
new TwigFunction('asset', function (string $path) use ($twig) {
return "/{$twig->getLoader()->getPaths()[0]}/{$path}";
})
);
$this->twig->getEnvironment()->addFunction(
$twig->getEnvironment()->addFunction(
new TwigFunction('sizeForHumans', function (int $bytes) {
$sizes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
$factor = (int) floor((strlen((string) $bytes) - 1) / 3);
@@ -76,18 +79,20 @@ class ViewComposer
/**
* Register theme specific Twig functions.
*
* @param \Slim\Views\Twig $twig
*
* @return void
*/
public function registerThemeFunctions(): void
public function registerThemeFunctions(Twig $twig): void
{
$themeFunctionsFile = "{$this->themePath}/functions.php";
$themeFunctionsFile = "{$twig->getLoader()->getPaths()[0]}/functions.php";
if (file_exists($themeFunctionsFile)) {
$themeConfig = include $themeFunctionsFile;
}
foreach ($themeConfig['functions'] ?? [] as $function) {
$this->twig->getEnvironment()->addFunction($function);
$twig->getEnvironment()->addFunction($function);
}
}
}

View File

@@ -2,6 +2,7 @@
namespace App\Controllers;
use DI\Container;
use PHLAK\Config\Config;
use Slim\Psr7\Response;
use Slim\Views\Twig;
@@ -13,17 +14,22 @@ class DirectoryController
/** @var Config App configuration component */
protected $config;
/** @var Container Application container */
protected $container;
/** @var Twig Twig templating component */
protected $view;
/**
* Create a new DirectoryController object.
*
* @param \DI\Container $container
* @param \PHLAK\Config\Config $config
* @param \Slim\Views\Twig $view
*/
public function __construct(Config $config, Twig $view)
public function __construct(Container $container, Config $config, Twig $view)
{
$this->container = $container;
$this->config = $config;
$this->view = $view;
}
@@ -67,7 +73,7 @@ class DirectoryController
}
/**
* Undocumented function.
* Determine if a provided path is the root path.
*
* @param string $path
*
@@ -75,6 +81,6 @@ class DirectoryController
*/
protected function isRoot(string $path): bool
{
return realpath($path) === realpath($this->config->get('app.root'));
return realpath($path) === realpath($this->container->get('app.root'));
}
}

View File

@@ -1,45 +1,35 @@
<?php
use App\Bootstrap\FilesComposer;
use App\Bootstrap\ConfigComposer;
use App\Bootstrap\FinderComposer;
use App\Bootstrap\ViewComposer;
use App\Controllers;
use DI\Bridge\Slim\Bridge;
use DI\Container;
use Dotenv\Dotenv;
use PHLAK\Config\Config;
use Slim\Views\Twig;
use Symfony\Component\Finder\Finder;
require __DIR__ . '/vendor/autoload.php';
/** Set file access restrictions */
// Set file access restrictions
ini_set('open_basedir', __DIR__);
/** Initialize environment variable handler */
// Initialize environment variable handler
Dotenv::createImmutable(__DIR__)->load();
/** Create the container */
// Initialize the container
$container = new Container();
$container->set('app.root', __DIR__);
/** Register dependencies */
$container->set(Config::class, new Config('app/config'));
$container->set(Finder::class, new Finder());
$container->set(Twig::class, function (Config $config) {
return new Twig("app/themes/{$config->get('theme', 'default')}");
});
/** Configure the application components */
$container->call(function (Config $config) {
$config->set('app.root', __DIR__);
});
$container->call(FilesComposer::class);
// Configure the application componentes
$container->call(ConfigComposer::class);
$container->call(FinderComposer::class);
$container->call(ViewComposer::class);
/** Create the application */
// Create the application
$app = Bridge::create($container);
/** Register routes */
// Register routes
$app->get('/[{path:.*}]', Controllers\DirectoryController::class);
/** Enagage! */
// Enagage!
$app->run();