diff --git a/config/app.php b/config/app.php index fe0a97c1..8037479b 100644 --- a/config/app.php +++ b/config/app.php @@ -5,5 +5,11 @@ return [ // Service providers 'providers' => [ + \Engelsystem\Logger\LoggerServiceProvider::class, + \Engelsystem\Exceptions\ExceptionsServiceProvider::class, + \Engelsystem\Config\ConfigServiceProvider::class, + \Engelsystem\Routing\RoutingServiceProvider::class, + \Engelsystem\Renderer\RendererServiceProvider::class, + \Engelsystem\Database\DatabaseServiceProvider::class, ], ]; diff --git a/includes/autoload.php b/includes/autoload.php index f51f89e4..0cd9d355 100644 --- a/includes/autoload.php +++ b/includes/autoload.php @@ -6,4 +6,4 @@ if (!is_readable(__DIR__ . '/../vendor/autoload.php')) { } // Include composer autoloader -require_once __DIR__ . '/../vendor/autoload.php'; +$loader = require __DIR__ . '/../vendor/autoload.php'; diff --git a/includes/engelsystem_provider.php b/includes/engelsystem_provider.php index 3067ab62..48206cb6 100644 --- a/includes/engelsystem_provider.php +++ b/includes/engelsystem_provider.php @@ -2,14 +2,8 @@ use Engelsystem\Application; use Engelsystem\Config\Config; -use Engelsystem\Database\Db; use Engelsystem\Exceptions\Handler as ExceptionHandler; use Engelsystem\Http\Request; -use Engelsystem\Logger\EngelsystemLogger; -use Engelsystem\Renderer\HtmlEngine; -use Engelsystem\Renderer\Renderer; -use Engelsystem\Routing\UrlGenerator; -use Psr\Log\LoggerInterface; use Symfony\Component\HttpFoundation\Session\Session; use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage; use Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage; @@ -21,78 +15,21 @@ require_once __DIR__ . '/autoload.php'; /** - * Initialize the application + * Initialize and bootstrap the application */ $app = new Application(realpath(__DIR__ . DIRECTORY_SEPARATOR . '..')); - - -/** - * Bootstrap application - */ $appConfig = $app->make(Config::class); -$appConfig->set(app('path.config') . '/app.php'); +$appConfig->set(require config_path('app.php')); $app->bootstrap($appConfig); -/** - * Load configuration - */ -$config = new Config(); -$app->instance('config', $config); -$config->set(require __DIR__ . '/../config/config.default.php'); - -if (file_exists(__DIR__ . '/../config/config.php')) { - $config->set(array_replace_recursive( - $config->get(null), - require __DIR__ . '/../config/config.php' - )); -} - /** * Configure application */ -date_default_timezone_set($config->get('timezone')); - - -/** - * Initialize Request - * - * @var Request $request - */ -$request = Request::createFromGlobals(); -$app->instance('request', $request); - - -/** - * Check for maintenance - */ -if ($app->get('config')->get('maintenance')) { - echo file_get_contents(__DIR__ . '/../templates/maintenance.html'); - die(); -} - - -/** - * Register UrlGenerator - */ -$urlGenerator = new UrlGenerator(); -$app->instance('routing.urlGenerator', $urlGenerator); +date_default_timezone_set($app->get('config')->get('timezone')); - -/** - * Initialize renderer - */ -$renderer = new Renderer(); -$app->instance('renderer', $renderer); -$renderer->addRenderer(new HtmlEngine()); - - -/** - * Register error handler - */ -$errorHandler = new ExceptionHandler(); -$app->instance('error.handler', $errorHandler); if (config('environment') == 'development') { + $errorHandler = $app->get('error.handler'); $errorHandler->setEnvironment(ExceptionHandler::ENV_DEVELOPMENT); ini_set('display_errors', true); error_reporting(E_ALL); @@ -102,23 +39,21 @@ if (config('environment') == 'development') { /** - * Connect to database + * Check for maintenance */ -Db::connect( - 'mysql:host=' . config('database')['host'] . ';dbname=' . config('database')['db'] . ';charset=utf8', - config('database')['user'], - config('database')['pw'] -) || die('Error: Unable to connect to database'); -Db::getPdo()->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); -Db::getPdo()->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); +if ($app->get('config')->get('maintenance')) { + echo file_get_contents(__DIR__ . '/../templates/maintenance.html'); + die(); +} + /** - * Init logger + * Initialize Request + * + * @var Request $request */ -$logger = new EngelsystemLogger(); -$app->instance('logger', $logger); -$app->bind(LoggerInterface::class, 'logger'); -$app->bind(EngelsystemLogger::class, 'logger'); +$request = Request::createFromGlobals(); +$app->instance('request', $request); /** diff --git a/src/Application.php b/src/Application.php index b62b28a9..c9023c7b 100644 --- a/src/Application.php +++ b/src/Application.php @@ -54,7 +54,7 @@ class Application extends Container public function register($provider) { if (is_string($provider)) { - $provider = $this->get($provider); + $provider = $this->make($provider); } $this->serviceProviders[] = $provider; diff --git a/src/Config/ConfigServiceProvider.php b/src/Config/ConfigServiceProvider.php new file mode 100644 index 00000000..01b648df --- /dev/null +++ b/src/Config/ConfigServiceProvider.php @@ -0,0 +1,26 @@ +app->make(Config::class); + $this->app->instance('config', $config); + + $config->set(require $defaultConfigFile); + + if (file_exists($configFile)) { + $config->set(array_replace_recursive( + $config->get(null), + require $configFile + )); + } + } +} diff --git a/src/Database/DatabaseServiceProvider.php b/src/Database/DatabaseServiceProvider.php new file mode 100644 index 00000000..364816cc --- /dev/null +++ b/src/Database/DatabaseServiceProvider.php @@ -0,0 +1,31 @@ +app->get('config'); + Db::connect( + 'mysql:host=' . $config->get('database')['host'] . ';dbname=' . $config->get('database')['db'] . ';charset=utf8', + $config->get('database')['user'], + $config->get('database')['pw'] + ) || $this->exitOnError(); + + Db::getPdo()->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + Db::getPdo()->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); + } + + /** + * @throws Exception + */ + protected function exitOnError() + { + throw new Exception('Error: Unable to connect to database'); + } +} diff --git a/src/Exceptions/ExceptionsServiceProvider.php b/src/Exceptions/ExceptionsServiceProvider.php new file mode 100644 index 00000000..7755e1e7 --- /dev/null +++ b/src/Exceptions/ExceptionsServiceProvider.php @@ -0,0 +1,15 @@ +app->make(ExceptionHandler::class); + $this->app->instance('error.handler', $errorHandler); + } +} diff --git a/src/Logger/LoggerServiceProvider.php b/src/Logger/LoggerServiceProvider.php new file mode 100644 index 00000000..cf22f383 --- /dev/null +++ b/src/Logger/LoggerServiceProvider.php @@ -0,0 +1,18 @@ +app->make(EngelsystemLogger::class); + $this->app->instance('logger', $logger); + + $this->app->bind(LoggerInterface::class, 'logger'); + $this->app->bind(EngelsystemLogger::class, 'logger'); + } +} diff --git a/src/Renderer/RendererServiceProvider.php b/src/Renderer/RendererServiceProvider.php new file mode 100644 index 00000000..3e8d69bc --- /dev/null +++ b/src/Renderer/RendererServiceProvider.php @@ -0,0 +1,36 @@ +registerRenderer(); + $this->registerHtmlEngine(); + } + + public function boot() + { + $renderer = $this->app->get('renderer'); + + foreach ($this->app->tagged('renderer.engine') as $engine) { + $renderer->addRenderer($engine); + } + } + + protected function registerRenderer() + { + $renderer = $this->app->make(Renderer::class); + $this->app->instance('renderer', $renderer); + } + + protected function registerHtmlEngine() + { + $htmlEngine = $this->app->make(HtmlEngine::class); + $this->app->instance('renderer.htmlEngine', $htmlEngine); + $this->app->tag('renderer.htmlEngine', ['renderer.engine']); + } +} diff --git a/src/Routing/RoutingServiceProvider.php b/src/Routing/RoutingServiceProvider.php new file mode 100644 index 00000000..b7db1383 --- /dev/null +++ b/src/Routing/RoutingServiceProvider.php @@ -0,0 +1,14 @@ +app->make(UrlGenerator::class); + $this->app->instance('routing.urlGenerator', $urlGenerator); + } +} diff --git a/tests/Feature/Database/DatabaseServiceProviderConnectionTest.php b/tests/Feature/Database/DatabaseServiceProviderConnectionTest.php new file mode 100644 index 00000000..dd1ce729 --- /dev/null +++ b/tests/Feature/Database/DatabaseServiceProviderConnectionTest.php @@ -0,0 +1,53 @@ +getMockBuilder(Config::class) + ->getMock(); + + /** @var PHPUnit_Framework_MockObject_MockObject|Application $app */ + $app = $this->getMockBuilder(Application::class) + ->setMethods(['get']) + ->getMock(); + Application::setInstance($app); + + $app->expects($this->once()) + ->method('get') + ->with('config') + ->willReturn($config); + + $config->expects($this->atLeastOnce()) + ->method('get') + ->with('database') + ->willReturn($this->getDbConfig()); + + $serviceProvider = new DatabaseServiceProvider($app); + $serviceProvider->register(); + } + + private function getDbConfig() + { + $configValues = require __DIR__ . '/../../../config/config.default.php'; + $configFile = __DIR__ . '/../../../config/config.php'; + + if (file_exists($configFile)) { + $configValues = array_replace_recursive($configValues, require $configFile); + } + + return $configValues['database']; + } +} diff --git a/tests/Unit/Config/ConfigServiceProviderTest.php b/tests/Unit/Config/ConfigServiceProviderTest.php new file mode 100644 index 00000000..26128e79 --- /dev/null +++ b/tests/Unit/Config/ConfigServiceProviderTest.php @@ -0,0 +1,54 @@ +getMockBuilder(Config::class) + ->getMock(); + + /** @var PHPUnit_Framework_MockObject_MockObject|Application $app */ + $app = $this->getMockBuilder(Application::class) + ->setMethods(['make', 'instance', 'get']) + ->getMock(); + Application::setInstance($app); + + $app->expects($this->once()) + ->method('make') + ->with(Config::class) + ->willReturn($config); + + $app->expects($this->once()) + ->method('instance') + ->with('config', $config); + + $app->expects($this->atLeastOnce()) + ->method('get') + ->with('path.config') + ->willReturn(__DIR__ . '/../../../config'); + + $config->expects($this->exactly(2)) + ->method('set') + ->withAnyParameters(); + + $config->expects($this->once()) + ->method('get') + ->with(null) + ->willReturn([]); + + $serviceProvider = new ConfigServiceProvider($app); + $serviceProvider->register(); + } +} diff --git a/tests/Unit/Database/DatabaseServiceProviderTest.php b/tests/Unit/Database/DatabaseServiceProviderTest.php new file mode 100644 index 00000000..d0e3e164 --- /dev/null +++ b/tests/Unit/Database/DatabaseServiceProviderTest.php @@ -0,0 +1,49 @@ +getMockBuilder(Config::class) + ->getMock(); + + /** @var PHPUnit_Framework_MockObject_MockObject|Application $app */ + $app = $this->getMockBuilder(Application::class) + ->setMethods(['get']) + ->getMock(); + + $app->expects($this->once()) + ->method('get') + ->with('config') + ->willReturn($config); + + $config->expects($this->atLeastOnce()) + ->method('get') + ->with('database') + ->willReturn([ + 'host' => 'localhost', + 'db' => 'database', + 'user' => 'user', + 'pw' => 'password', + ]); + + $serviceProvider = new DatabaseServiceProvider($app); + $this->expectException(Exception::class); + + $serviceProvider->register(); + } +} diff --git a/tests/Unit/Exceptions/ExceptionsServiceProviderTest.php b/tests/Unit/Exceptions/ExceptionsServiceProviderTest.php new file mode 100644 index 00000000..26eddb75 --- /dev/null +++ b/tests/Unit/Exceptions/ExceptionsServiceProviderTest.php @@ -0,0 +1,39 @@ +getMockBuilder(ExceptionHandler::class) + ->getMock(); + + /** @var PHPUnit_Framework_MockObject_MockObject|Application $app */ + $app = $this->getMockBuilder(Application::class) + ->setMethods(['make', 'instance']) + ->getMock(); + + $app->expects($this->once()) + ->method('make') + ->with(ExceptionHandler::class) + ->willReturn($exceptionHandler); + + $app->expects($this->once()) + ->method('instance') + ->with('error.handler', $exceptionHandler); + + $serviceProvider = new ExceptionsServiceProvider($app); + $serviceProvider->register(); + } +} diff --git a/tests/Unit/Logger/LoggerServiceProviderTest.php b/tests/Unit/Logger/LoggerServiceProviderTest.php new file mode 100644 index 00000000..5143d236 --- /dev/null +++ b/tests/Unit/Logger/LoggerServiceProviderTest.php @@ -0,0 +1,47 @@ +getMockBuilder(EngelsystemLogger::class) + ->getMock(); + + /** @var PHPUnit_Framework_MockObject_MockObject|Application $app */ + $app = $this->getMockBuilder(Application::class) + ->setMethods(['make', 'instance', 'bind']) + ->getMock(); + + $app->expects($this->once()) + ->method('make') + ->with(EngelsystemLogger::class) + ->willReturn($logger); + + $app->expects($this->once()) + ->method('instance') + ->with('logger', $logger); + + $app->expects($this->atLeastOnce()) + ->method('bind') + ->withConsecutive( + [LoggerInterface::class, 'logger'], + [EngelsystemLogger::class, 'logger'] + ); + + $serviceProvider = new LoggerServiceProvider($app); + $serviceProvider->register(); + } +} diff --git a/tests/Unit/Renderer/RendererServiceProviderTest.php b/tests/Unit/Renderer/RendererServiceProviderTest.php new file mode 100644 index 00000000..f9044d8b --- /dev/null +++ b/tests/Unit/Renderer/RendererServiceProviderTest.php @@ -0,0 +1,81 @@ +getMockBuilder(Renderer::class) + ->getMock(); + /** @var PHPUnit_Framework_MockObject_MockObject|HtmlEngine $htmlEngine */ + $htmlEngine = $this->getMockBuilder(HtmlEngine::class) + ->getMock(); + + $app = $this->getApp(['make', 'instance', 'tag']); + + $app->expects($this->exactly(2)) + ->method('make') + ->withConsecutive( + [Renderer::class], + [HtmlEngine::class] + )->willReturnOnConsecutiveCalls( + $renderer, + $htmlEngine + ); + + $app->expects($this->exactly(2)) + ->method('instance') + ->withConsecutive( + ['renderer', $renderer], + ['renderer.htmlEngine', $htmlEngine] + ); + + $this->setExpects($app, 'tag', ['renderer.htmlEngine', ['renderer.engine']]); + + $serviceProvider = new RendererServiceProvider($app); + $serviceProvider->register(); + } + + /** + * @covers \Engelsystem\Renderer\RendererServiceProvider::boot() + */ + public function testBoot() + { + /** @var PHPUnit_Framework_MockObject_MockObject|Renderer $renderer */ + $renderer = $this->getMockBuilder(Renderer::class) + ->getMock(); + /** @var PHPUnit_Framework_MockObject_MockObject|EngineInterface $engine1 */ + $engine1 = $this->getMockForAbstractClass(EngineInterface::class); + /** @var PHPUnit_Framework_MockObject_MockObject|EngineInterface $engine2 */ + $engine2 = $this->getMockForAbstractClass(EngineInterface::class); + + $app = $this->getApp(['get', 'tagged']); + + $engines = [$engine1, $engine2]; + + $this->setExpects($app, 'get', ['renderer'], $renderer); + $this->setExpects($app, 'tagged', ['renderer.engine'], $engines); + + $invocation = $renderer + ->expects($this->exactly(count($engines))) + ->method('addRenderer'); + call_user_func_array([$invocation, 'withConsecutive'], $engines); + + $serviceProvider = new RendererServiceProvider($app); + $serviceProvider->boot(); + } +} diff --git a/tests/Unit/Routing/RoutingServiceProviderTest.php b/tests/Unit/Routing/RoutingServiceProviderTest.php new file mode 100644 index 00000000..4f1cd5fc --- /dev/null +++ b/tests/Unit/Routing/RoutingServiceProviderTest.php @@ -0,0 +1,39 @@ +getMockBuilder(UrlGenerator::class) + ->getMock(); + + /** @var PHPUnit_Framework_MockObject_MockObject|Application $app */ + $app = $this->getMockBuilder(Application::class) + ->setMethods(['make', 'instance']) + ->getMock(); + + $app->expects($this->once()) + ->method('make') + ->with(UrlGenerator::class) + ->willReturn($urlGenerator); + + $app->expects($this->once()) + ->method('instance') + ->with('routing.urlGenerator', $urlGenerator); + + $serviceProvider = new RoutingServiceProvider($app); + $serviceProvider->register(); + } +} diff --git a/tests/Unit/ServiceProviderTest.php b/tests/Unit/ServiceProviderTest.php new file mode 100644 index 00000000..be843742 --- /dev/null +++ b/tests/Unit/ServiceProviderTest.php @@ -0,0 +1,39 @@ +getMockBuilder(Application::class) + ->setMethods($methods) + ->getMock(); + } + + /** + * @param PHPUnit_Framework_MockObject_MockObject $object + * @param string $method + * @param array $arguments + * @param mixed $return + */ + protected function setExpects($object, $method, $arguments, $return = null) + { + $invocation = $object->expects($this->once()) + ->method($method); + call_user_func_array([$invocation, 'with'], $arguments); + + if (!is_null($return)) { + $invocation->willReturn($return); + } + } +} diff --git a/tests/autoload.php b/tests/autoload.php new file mode 100644 index 00000000..3168ce3d --- /dev/null +++ b/tests/autoload.php @@ -0,0 +1,8 @@ +addPsr4('Engelsystem\\Test\\', __DIR__ . '/');