Merge pull request #347 from MyIgel/master

Implemented Container, closes #342
This commit is contained in:
msquare 2017-09-30 11:25:58 +02:00 committed by GitHub
commit 801c17aa6c
39 changed files with 1154 additions and 178 deletions

4
.gitignore vendored
View File

@ -14,12 +14,14 @@ Thumbs.db
_vimrc_local.vim
.sass-cache
# PHPstorm config
# PHPstorm files
/.idea/
/.phpstorm.meta.php
# Project files
/config/config.php
/test/coverage
/public/coverage
# Composer files
/vendor/

View File

@ -17,7 +17,9 @@
"php": ">=7.0.0",
"erusev/parsedown": "1.6.*",
"twbs/bootstrap": "^3.3",
"symfony/http-foundation": "^3.3"
"symfony/http-foundation": "^3.3",
"psr/container": "^1.0",
"psr/log": "^1.0"
},
"require-dev": {
"phpunit/phpunit": "^6.3"

View File

@ -44,6 +44,9 @@ return [
// Number of News shown on one site
'display_news' => 6,
// Users are able to sign up
'registration_enabled' => true,
// Only arrived angels can sign up for shifts
'signup_requires_arrival' => false,

View File

@ -28,3 +28,6 @@ UPDATE `Groups` SET UID = UID * 10;
INSERT INTO `Groups` (Name, UID) VALUES ('News Admin', -65);
INSERT INTO `Privileges` (id, name, `desc`) VALUES (42, 'admin_news_html', 'Use HTML in news');
INSERT INTO `GroupPrivileges` (group_id, privilege_id) VALUES (-65, 14), (-65, 42);
-- Add log level to LogEntries
ALTER TABLE `LogEntries` CHANGE COLUMN `nick` `level` VARCHAR(20) NOT NULL;

View File

@ -1,24 +1,36 @@
<?php
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;
/**
* This file includes all needed functions, connects to the db etc.
*/
require_once __DIR__ . '/autoload.php';
/**
* Initialize the application
*/
$app = Application::getInstance();
/**
* Load configuration
*/
$config = new Config();
Config::setInstance($config);
$app->instance('config', $config);
$config->set(require __DIR__ . '/../config/config.default.php');
if (file_exists(__DIR__ . '/../config/config.php')) {
@ -37,7 +49,8 @@ date_default_timezone_set($config->get('timezone'));
* @var Request $request
*/
$request = Request::createFromGlobals();
$request::setInstance($request);
$app->instance('request', $request);
/**
* Check for maintenance
@ -48,18 +61,26 @@ if ($config->get('maintenance')) {
}
/**
* Register UrlGenerator
*/
$urlGenerator = new UrlGenerator();
$app->instance('routing.urlGenerator', $urlGenerator);
/**
* Initialize renderer
*/
$renderer = new Renderer();
$app->instance('renderer', $renderer);
$renderer->addRenderer(new HtmlEngine());
Renderer::setInstance($renderer);
/**
* Register error handler
*/
$errorHandler = new ExceptionHandler();
$app->instance('error.handler', $errorHandler);
if (config('environment') == 'development') {
$errorHandler->setEnvironment(ExceptionHandler::ENV_DEVELOPMENT);
ini_set('display_errors', true);
@ -80,6 +101,14 @@ Db::connect(
Db::getPdo()->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
Db::getPdo()->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
/**
* Init logger
*/
$logger = new EngelsystemLogger();
$app->instance('logger', $logger);
$app->instance(LoggerInterface::class, $logger);
$app->instance(EngelsystemLogger::class, $logger);
/**
* Include legacy code
@ -170,7 +199,9 @@ foreach ($includeFiles as $file) {
/**
* Init application
*/
$session = new Session();
$sessionStorage = (PHP_SAPI != 'cli' ? new NativeSessionStorage(['cookie_httponly' => true]) : new MockArraySessionStorage());
$session = new Session($sessionStorage);
$app->instance('session', $session);
$session->start();
$request->setSession($session);

View File

@ -1,7 +1,5 @@
<?php
use Engelsystem\Http\Request;
/**
* Return currently active locale
*
@ -65,7 +63,7 @@ function gettext_locale($locale = null)
*/
function make_langselect()
{
$request = Request::getInstance();
$request = app('request');
$items = [];
foreach (config('locales') as $locale => $name) {

View File

@ -5,16 +5,16 @@ use Engelsystem\Database\DB;
/**
* Creates a log entry.
*
* @param string $nick Username
* @param string $message Log Message
* @param string $logLevel Log level
* @param string $message Log Message
* @return bool
*/
function LogEntry_create($nick, $message)
function LogEntry_create($logLevel, $message)
{
return DB::insert('
INSERT INTO `LogEntries` (`timestamp`, `nick`, `message`)
INSERT INTO `LogEntries` (`timestamp`, `level`, `message`)
VALUES(?, ?, ?)
', [time(), $nick, $message]);
', [time(), $logLevel, $message]);
}
/**
@ -43,7 +43,7 @@ function LogEntries_filter($keyword)
return DB::select('
SELECT *
FROM `LogEntries`
WHERE `nick` LIKE ?
WHERE `level` LIKE ?
OR `message` LIKE ?
ORDER BY `timestamp` DESC
',

View File

@ -17,12 +17,10 @@ function admin_log()
if (request()->has('keyword')) {
$filter = strip_request_item('keyword');
}
$log_entries_source = LogEntries_filter($filter);
$log_entries = LogEntries_filter($filter);
$log_entries = [];
foreach ($log_entries_source as $log_entry) {
foreach ($log_entries as &$log_entry) {
$log_entry['date'] = date('d.m.Y H:i', $log_entry['timestamp']);
$log_entries[] = $log_entry;
}
return page_with_title(admin_log_title(), [
@ -33,7 +31,7 @@ function admin_log()
]),
table([
'date' => 'Time',
'nick' => 'Angel',
'level' => 'Type',
'message' => 'Log Entry'
], $log_entries)
]);

View File

@ -33,7 +33,7 @@ function logout_title()
*/
function guest_register()
{
global $user;
global $user, $privileges;
$tshirt_sizes = config('tshirt_sizes');
$enable_tshirt_size = config('enable_tshirt_size');
$min_password_length = config('min_password_length');
@ -75,6 +75,14 @@ function guest_register()
}
}
if (!in_array('register', $privileges) || (!isset($user) && !config('registration_enabled'))) {
error(_('Registration is disabled.'));
return page_with_title(register_title(), [
msg(),
]);
}
if ($request->has('submit')) {
$valid = true;
@ -496,7 +504,7 @@ function get_register_hint()
{
global $privileges;
if (in_array('register', $privileges)) {
if (in_array('register', $privileges) && config('registration_enabled')) {
return join('', [
'<p>' . _('Please sign up, if you want to help us!') . '</p>',
buttons([

View File

@ -1,7 +1,6 @@
<?php
use Engelsystem\Database\DB;
use Engelsystem\Http\Request;
/**
* Publically available page to feed the news to feed readers
@ -45,7 +44,7 @@ function user_atom()
*/
function make_atom_entries_from_news($news_entries)
{
$request = Request::getInstance();
$request = app('request');
$html = '<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>Engelsystem</title>

View File

@ -9,10 +9,12 @@
function engelsystem_log($message)
{
global $user;
$nick = "Guest";
$logger = app('logger');
if (isset($user)) {
$nick = User_Nick_render($user);
}
LogEntry_create($nick, $message);
$logger->info('{nick}: {message}', ['nick' => $nick, 'message' => $message]);
}

View File

@ -59,7 +59,7 @@ function header_toolbar()
);
}
if (!isset($user) && in_array('register', $privileges)) {
if (!isset($user) && in_array('register', $privileges) && config('registration_enabled')) {
$toolbar_items[] = toolbar_item_link(page_link_to('register'), 'plus', register_title(), $page == 'register');
}

View File

@ -475,13 +475,27 @@ function AngelTypes_about_view_angeltype($angeltype)
*/
function AngelTypes_about_view($angeltypes, $user_logged_in)
{
global $privileges;
$buttons = [];
if ($user_logged_in) {
$buttons[] = button(page_link_to('angeltypes'), angeltypes_title(), 'back');
} else {
if (in_array('register', $privileges) && config('registration_enabled')) {
$buttons[] = button(page_link_to('register'), register_title());
}
$buttons[] = button(page_link_to('login'), login_title());
}
$faqUrl = config('faq_url');
if (!empty($faqUrl)) {
$buttons[] = button($faqUrl, _('FAQ'), 'btn-primary');
}
$content = [
buttons([
!$user_logged_in ? button(page_link_to('register'), register_title()) : '',
!$user_logged_in ? button(page_link_to('login'), login_title()) : '',
$user_logged_in ? button(page_link_to('angeltypes'), angeltypes_title(), 'back') : '',
button(config('faq_url'), _('FAQ'), 'btn-primary')
]),
buttons($buttons),
'<p>' . _('Here is the list of teams and their tasks. If you have questions, read the FAQ.') . '</p>',
'<hr />'
];

View File

@ -1,17 +1,19 @@
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
bootstrap="./includes/engelsystem_provider.php"
bootstrap="./includes/autoload.php"
xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/6.3/phpunit.xsd"
colors="true"
>
<testsuites>
<testsuite name="Models">
<directory>./test/model/</directory>
<testsuite name="Feature">
<directory>./tests/Feature</directory>
</testsuite>
<testsuite name="Unit">
<directory>./tests/Unit</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory>./include/</directory>
<directory>./public/</directory>
<directory>./src/</directory>
</whitelist>
</filter>

View File

@ -1,3 +1,4 @@
<IfModule mod_rewrite.c>
RewriteEngine on

View File

@ -1,7 +1,5 @@
<?php
use Engelsystem\Http\Request;
require_once realpath(__DIR__ . '/../includes/engelsystem_provider.php');
$free_pages = [
@ -27,7 +25,6 @@ $page = '';
$title = '';
$content = '';
/** @var Request $request */
$page = $request->query->get('p');
if (empty($page)) {
$page = $request->path();

25
src/Application.php Normal file
View File

@ -0,0 +1,25 @@
<?php
namespace Engelsystem;
use Engelsystem\Container\Container;
use Psr\Container\ContainerInterface;
class Application extends Container
{
public function __construct()
{
$this->registerBaseBindings();
}
protected function registerBaseBindings()
{
self::setInstance($this);
Container::setInstance($this);
$this->instance('app', $this);
$this->instance('container', $this);
$this->instance(Container::class, $this);
$this->instance(Application::class, $this);
$this->instance(ContainerInterface::class, $this);
}
}

View File

@ -2,15 +2,8 @@
namespace Engelsystem\Config;
use ErrorException;
class Config
{
/**
* @var self
*/
protected static $instance;
/**
* The config values
*
@ -104,25 +97,4 @@ class Config
{
$this->remove($key);
}
/**
* @return Config
* @throws ErrorException
*/
public static function getInstance()
{
if (!self::$instance instanceof self) {
throw new ErrorException('Config not initialized');
}
return self::$instance;
}
/**
* @param self $instance
*/
public static function setInstance($instance)
{
self::$instance = $instance;
}
}

116
src/Container/Container.php Normal file
View File

@ -0,0 +1,116 @@
<?php
namespace Engelsystem\Container;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\ContainerInterface;
use Psr\Container\NotFoundExceptionInterface;
class Container implements ContainerInterface
{
/**
* The globally available container
*
* @var static
*/
protected static $instance;
/**
* Contains the shared instances
*
* @var mixed[]
*/
protected $instances = [];
/**
* Finds an entry of the container by its identifier and returns it
*
* @param string $id Identifier of the entry to look for
*
* @throws NotFoundExceptionInterface No entry was found for **this** identifier
* @throws ContainerExceptionInterface Error while retrieving the entry
*
* @return mixed Entry
*/
public function get($id)
{
if ($this->has($id)) {
return $this->resolve($id);
}
throw new NotFoundException(sprintf('The entry with the id "%s" could not be found', $id));
}
/**
* Register a shared entry in the container
*
* @param string $abstract Identifier of the entry to set
* @param mixed $instance Entry
*/
public function instance($abstract, $instance)
{
$this->singleton($abstract, $instance);
}
/**
* Register a shared entry as singleton in the container
*
* @param string $abstract
* @param mixed $instance
*/
public function singleton($abstract, $instance)
{
$this->instances[$abstract] = $instance;
}
/**
* Returns true if the container can return an entry for the given identifier
* Returns false otherwise
*
* `has($id)` returning true does not mean that `get($id)` will not throw an exception
* It does however mean that `get($id)` will not throw a `NotFoundExceptionInterface`
*
* @param string $id Identifier of the entry to look for
*
* @return bool
*/
public function has($id)
{
return isset($this->instances[$id]);
}
/**
* Resolve the requested object
*
* @param string $abstract
* @return mixed
*/
protected function resolve($abstract)
{
return $this->instances[$abstract];
}
/**
* Get the globally available instance of the container
*
* @return self
*/
public static function getInstance()
{
if (is_null(static::$instance)) {
static::$instance = new static;
}
return static::$instance;
}
/**
* Set the globally available instance of the container
*
* @param Container $container
*/
public static function setInstance(Container $container)
{
static::$instance = $container;
}
}

View File

@ -0,0 +1,11 @@
<?php
namespace Engelsystem\Container;
use Exception;
use Psr\Container\ContainerExceptionInterface;
class ContainerException extends Exception implements ContainerExceptionInterface
{
}

View File

@ -0,0 +1,10 @@
<?php
namespace Engelsystem\Container;
use Psr\Container\NotFoundExceptionInterface;
class NotFoundException extends ContainerException implements NotFoundExceptionInterface
{
}

View File

@ -42,7 +42,7 @@ class Handler
/**
* @param Throwable $e
*/
public function exceptionHandler(Throwable $e)
public function exceptionHandler($e)
{
$this->handle(
'exception',

View File

@ -2,14 +2,10 @@
namespace Engelsystem\Http;
use ErrorException;
use Symfony\Component\HttpFoundation\Request as SymfonyRequest;
class Request extends SymfonyRequest
{
/** @var self */
protected static $instance;
/**
* Get POST input
*
@ -68,25 +64,4 @@ class Request extends SymfonyRequest
{
return rtrim(preg_replace('/\?.*/', '', $this->getUri()), '/');
}
/**
* @return self
* @throws ErrorException
*/
public static function getInstance()
{
if (!self::$instance instanceof self) {
throw new ErrorException('Request not initialized');
}
return self::$instance;
}
/**
* @param self $instance
*/
public static function setInstance($instance)
{
self::$instance = $instance;
}
}

View File

@ -0,0 +1,74 @@
<?php
namespace Engelsystem\Logger;
use Psr\Log\AbstractLogger;
use Psr\Log\InvalidArgumentException;
use Psr\Log\LogLevel;
class EngelsystemLogger extends AbstractLogger
{
protected $allowedLevels = [
LogLevel::ALERT,
LogLevel::CRITICAL,
LogLevel::DEBUG,
LogLevel::EMERGENCY,
LogLevel::ERROR,
LogLevel::INFO,
LogLevel::NOTICE,
LogLevel::WARNING,
];
/**
* Logs with an arbitrary level.
*
* @TODO: Implement $context['exception']
*
* @param mixed $level
* @param string $message
* @param array $context
*
* @throws InvalidArgumentException
*/
public function log($level, $message, array $context = [])
{
if (!$this->checkLevel($level)) {
throw new InvalidArgumentException();
}
$message = $this->interpolate($message, $context);
LogEntry_create($level, $message);
}
/**
* Interpolates context values into the message placeholders.
*
* @param string $message
* @param array $context
* @return string
*/
protected function interpolate($message, array $context = [])
{
foreach ($context as $key => $val) {
// check that the value can be casted to string
if (is_array($val) || (is_object($val) && !method_exists($val, '__toString'))) {
continue;
}
// replace the values of the message
$message = str_replace('{' . $key . '}', $val, $message);
}
return $message;
}
/**
* @param string $level
* @return bool
*/
protected function checkLevel($level)
{
return in_array($level, $this->allowedLevels);
}
}

View File

@ -29,6 +29,6 @@ class HtmlEngine implements EngineInterface
*/
public function canRender($path)
{
return strpos($path, '.html') && file_exists($path);
return strpos($path, '.htm') && file_exists($path);
}
}

View File

@ -2,12 +2,11 @@
namespace Engelsystem\Renderer;
use ErrorException;
use Psr\Log\LoggerAwareTrait;
class Renderer
{
/** @var self */
protected static $instance;
use LoggerAwareTrait;
/** @var EngineInterface[] */
protected $renderer = [];
@ -29,7 +28,10 @@ class Renderer
return $renderer->get($template, $data);
}
engelsystem_error('Unable to find a renderer for template file &laquo;' . $template . '&raquo;.');
if ($this->logger) {
$this->logger->error('Unable to find a renderer for template file "{file}"', ['file' => $template]);
}
return '';
}
@ -42,21 +44,4 @@ class Renderer
{
$this->renderer[] = $renderer;
}
/**
* @return self
* @throws ErrorException
*/
public static function getInstance()
{
return self::$instance;
}
/**
* @param self $instance
*/
public static function setInstance($instance)
{
self::$instance = $instance;
}
}

View File

@ -2,8 +2,6 @@
namespace Engelsystem\Routing;
use Engelsystem\Http\Request;
class UrlGenerator
{
/**
@ -11,10 +9,10 @@ class UrlGenerator
* @param array $parameters
* @return string
*/
public static function to($path, $parameters = [])
public function to($path, $parameters = [])
{
$path = '/' . ltrim($path, '/');
$request = Request::getInstance();
$request = app('request');
$uri = $request->getUriForPath($path);
if (!empty($parameters) && is_array($parameters)) {

View File

@ -1,12 +1,28 @@
<?php
// Some useful functions
use Engelsystem\Application;
use Engelsystem\Config\Config;
use Engelsystem\Http\Request;
use Engelsystem\Renderer\Renderer;
use Engelsystem\Routing\UrlGenerator;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
/**
* Get the global app instance
*
* @param string $id
* @return mixed
*/
function app($id = null)
{
if (is_null($id)) {
return Application::getInstance();
}
return Application::getInstance()->get($id);
}
/**
* Get or set config values
*
@ -16,15 +32,18 @@ use Symfony\Component\HttpFoundation\Session\SessionInterface;
*/
function config($key = null, $default = null)
{
$config = app('config');
if (empty($key)) {
return Config::getInstance();
return $config;
}
if (is_array($key)) {
Config::getInstance()->set($key);
$config->set($key);
return true;
}
return Config::getInstance()->get($key, $default);
return $config->get($key, $default);
}
/**
@ -34,7 +53,7 @@ function config($key = null, $default = null)
*/
function request($key = null, $default = null)
{
$request = Request::getInstance();
$request = app('request');
if (is_null($key)) {
return $request;
@ -50,7 +69,7 @@ function request($key = null, $default = null)
*/
function session($key = null, $default = null)
{
$session = request()->getSession();
$session = app('session');
if (is_null($key)) {
return $session;
@ -66,7 +85,7 @@ function session($key = null, $default = null)
*/
function view($template = null, $data = null)
{
$renderer = Renderer::getInstance();
$renderer = app('renderer');
if (is_null($template)) {
return $renderer;
@ -78,9 +97,15 @@ function view($template = null, $data = null)
/**
* @param string $path
* @param array $parameters
* @return string
* @return UrlGenerator|string
*/
function url($path, $parameters = [])
function url($path = null, $parameters = [])
{
return UrlGenerator::to($path, $parameters);
$urlGenerator = app('routing.urlGenerator');
if (is_null($path)) {
return $urlGenerator;
}
return $urlGenerator->to($path, $parameters);
}

View File

@ -1,38 +0,0 @@
<?php
namespace Engelsystem\Test;
use \PHPUnit\Framework\TestCase;
class LogEntriesModelTest extends TestCase
{
public function create_LogEntry()
{
LogEntry_create('test', 'test');
}
public function test_LogEntry_create()
{
$count = count(LogEntries());
$this->assertNotFalse(LogEntry_create('test', 'test_LogEntry_create'));
// There should be one more log entry now
$this->assertEquals(count(LogEntries()), $count + 1);
}
public function test_LogEntries_clear_all()
{
$this->create_LogEntry();
$this->assertTrue(count(LogEntries()) > 0);
$this->assertNotFalse(LogEntries_clear_all());
$this->assertEquals(count(LogEntries()), 0);
}
/**
* @after
*/
public function teardown()
{
LogEntries_clear_all();
}
}

View File

@ -0,0 +1,150 @@
<?php
namespace Engelsystem\Test\Logger;
use Engelsystem\Logger\EngelsystemLogger;
use PHPUnit\Framework\TestCase;
use Psr\Log\InvalidArgumentException;
use Psr\Log\LoggerInterface;
use Psr\Log\LogLevel;
class EngelsystemLoggerTest extends TestCase
{
public static function setUpBeforeClass()
{
require_once __DIR__ . '/../../../includes/engelsystem_provider.php';
}
/**
* @return LoggerInterface
*/
public function getLogger()
{
return new EngelsystemLogger();
}
public function testImplements()
{
$this->assertInstanceOf('Psr\Log\LoggerInterface', $this->getLogger());
}
/**
* @return string[]
*/
public function provideLogLevels()
{
return [
[LogLevel::ALERT],
[LogLevel::CRITICAL],
[LogLevel::DEBUG],
[LogLevel::EMERGENCY],
[LogLevel::ERROR],
[LogLevel::INFO],
[LogLevel::NOTICE],
[LogLevel::WARNING],
];
}
/**
* @dataProvider provideLogLevels
* @param string $level
*/
public function testAllLevels($level)
{
$logger = $this->getLogger();
LogEntries_clear_all();
$logger->log($level, 'First log message');
$logger->{$level}('Second log message');
$entries = LogEntries();
$this->assertCount(2, $entries);
}
public function testContextReplacement()
{
$logger = $this->getLogger();
LogEntries_clear_all();
$logger->log(LogLevel::INFO, 'My username is {username}', ['username' => 'Foo']);
$entry = $this->getLastEntry();
$this->assertEquals('My username is Foo', $entry['message']);
$this->assertEquals(LogLevel::INFO, $entry['level']);
}
/**
* @return string[]
*/
public function provideContextReplaceValues()
{
return [
['Data and {context}', [], 'Data and {context}'],
['Data and {context}', ['context' => null], 'Data and '],
['Data and {context}', ['context' => new \stdClass()], 'Data and {context}'],
['Some user asked: {question}', ['question' => 'Foo?'], 'Some user asked: Foo?'],
];
}
/**
* @dataProvider provideContextReplaceValues
*
* @param string $message
* @param string[] $context
* @param string $expected
*/
public function testContextReplaceValues($message, $context, $expected)
{
$logger = $this->getLogger();
$logger->log(LogLevel::INFO, $message, $context);
$entry = $this->getLastEntry();
$this->assertEquals($expected, $entry['message']);
}
public function testContextToString()
{
$logger = $this->getLogger();
LogEntries_clear_all();
$mock = $this->getMockBuilder('someDataProvider')
->setMethods(['__toString'])
->getMock();
$mock->expects($this->atLeastOnce())
->method('__toString')
->will($this->returnValue('FooBar'));
$logger->log(LogLevel::INFO, 'Some data and {context}', ['context' => $mock]);
$entry = $this->getLastEntry();
$this->assertEquals('Some data and FooBar', $entry['message']);
}
/**
* @expectedException InvalidArgumentException
*/
public function testThrowExceptionOnInvalidLevel()
{
$logger = $this->getLogger();
$logger->log('This log level should never be defined', 'Some message');
}
/**
* @return array
*/
public function getLastEntry()
{
$entries = LogEntries();
$entry = array_pop($entries);
return $entry;
}
public function tearDown()
{
LogEntries_clear_all();
}
}

View File

@ -0,0 +1,38 @@
<?php
namespace Engelsystem\Test;
use PHPUnit\Framework\TestCase;
use Psr\Log\LogLevel;
class LogEntriesModelTest extends TestCase
{
public static function setUpBeforeClass()
{
require_once __DIR__ . '/../../../includes/engelsystem_provider.php';
}
public function testCreateLogEntry()
{
LogEntries_clear_all();
$count = count(LogEntries());
$this->assertNotFalse(LogEntry_create(LogLevel::WARNING, 'test_LogEntry_create'));
// There should be one more log entry now
$this->assertEquals(count(LogEntries()), $count + 1);
}
public function testClearAllLogEntries()
{
LogEntry_create(LogLevel::WARNING, 'test');
$this->assertTrue(count(LogEntries()) > 0);
$this->assertNotFalse(LogEntries_clear_all());
$this->assertCount(0, LogEntries());
}
public function tearDown()
{
LogEntries_clear_all();
}
}

View File

@ -1,13 +1,18 @@
<?php
namespace Engelsystem\Test;
use \PHPUnit\Framework\TestCase;
use PHPUnit\Framework\TestCase;
class RoomModelTest extends TestCase
{
private $room_id = null;
public static function setUpBeforeClass()
{
require_once __DIR__ . '/../../../includes/engelsystem_provider.php';
}
public function create_Room()
{
$this->room_id = Room_create('test', false, true, '');
@ -16,20 +21,17 @@ class RoomModelTest extends TestCase
public function test_Room()
{
$this->create_Room();
$room = Room($this->room_id);
$this->assertNotFalse($room);
$this->assertNotNull($room);
$this->assertEquals($room['Name'], 'test');
$this->assertNull(Room(- 1));
$this->assertNull(Room(-1));
}
/**
* @after
*/
public function teardown()
public function tearDown()
{
if ($this->room_id != null) {
Room_delete($this->room_id);

View File

@ -0,0 +1,29 @@
<?php
namespace Engelsystem\Test\Config;
use Engelsystem\Application;
use Engelsystem\Container\Container;
use PHPUnit\Framework\TestCase;
use Psr\Container\ContainerInterface;
class ApplicationTest extends TestCase
{
/**
* @covers \Engelsystem\Application::__construct
* @covers \Engelsystem\Application::registerBaseBindings
*/
public function testConstructor()
{
$app = new Application();
$this->assertInstanceOf(Container::class, $app);
$this->assertInstanceOf(ContainerInterface::class, $app);
$this->assertSame($app, $app->get('app'));
$this->assertSame($app, $app->get('container'));
$this->assertSame($app, $app->get(Container::class));
$this->assertSame($app, $app->get(Application::class));
$this->assertSame($app, $app->get(ContainerInterface::class));
$this->assertSame($app, Container::getInstance());
}
}

View File

@ -0,0 +1,115 @@
<?php
namespace Engelsystem\Test\Config;
use Engelsystem\Config\Config;
use PHPUnit\Framework\TestCase;
class ConfigTest extends TestCase
{
/**
* @covers \Engelsystem\Config\Config::get
*/
public function testGet()
{
$config = new Config();
$config->set('test', 'FooBar');
$this->assertEquals(['test' => 'FooBar'], $config->get(null));
$this->assertEquals('FooBar', $config->get('test'));
$this->assertEquals('defaultValue', $config->get('notExisting', 'defaultValue'));
$this->assertNull($config->get('notExisting'));
}
/**
* @covers \Engelsystem\Config\Config::set
*/
public function testSet()
{
$config = new Config();
$config->set('test', 'FooBar');
$this->assertEquals('FooBar', $config->get('test'));
$config->set([
'name' => 'Engelsystem',
'mail' => ['user' => 'test'],
]);
$this->assertEquals('Engelsystem', $config->get('name'));
$this->assertEquals(['user' => 'test'], $config->get('mail'));
}
/**
* @covers \Engelsystem\Config\Config::has
*/
public function testHas()
{
$config = new Config();
$this->assertFalse($config->has('test'));
$config->set('test', 'FooBar');
$this->assertTrue($config->has('test'));
}
/**
* @covers \Engelsystem\Config\Config::remove
*/
public function testRemove()
{
$config = new Config();
$config->set(['foo' => 'bar', 'test' => '123']);
$config->remove('foo');
$this->assertEquals(['test' => '123'], $config->get(null));
}
/**
* @covers \Engelsystem\Config\Config::__get
*/
public function testMagicGet()
{
$config = new Config();
$config->set('test', 'FooBar');
$this->assertEquals('FooBar', $config->test);
}
/**
* @covers \Engelsystem\Config\Config::__set
*/
public function testMagicSet()
{
$config = new Config();
$config->test = 'FooBar';
$this->assertEquals('FooBar', $config->get('test'));
}
/**
* @covers \Engelsystem\Config\Config::__isset
*/
public function testMagicIsset()
{
$config = new Config();
$this->assertFalse(isset($config->test));
$config->set('test', 'FooBar');
$this->assertTrue(isset($config->test));
}
/**
* @covers \Engelsystem\Config\Config::__unset
*/
public function testMagicUnset()
{
$config = new Config();
$config->set(['foo' => 'bar', 'test' => '123']);
unset($config->foo);
$this->assertEquals(['test' => '123'], $config->get(null));
}
}

View File

@ -0,0 +1,104 @@
<?php
namespace Engelsystem\Test\Config;
use Engelsystem\Container\Container;
use PHPUnit\Framework\TestCase;
class ContainerTest extends TestCase
{
/**
* @covers \Engelsystem\Container\Container::get
*/
public function testGet()
{
$container = new Container();
$class = new class
{
};
$container->instance('foo', $class);
$this->assertSame($class, $container->get('foo'));
}
/**
* @covers \Engelsystem\Container\Container::get
* @expectedException \Engelsystem\Container\NotFoundException
*/
public function testGetException()
{
$container = new Container();
$container->get('not.registered.service');
}
/**
* @covers \Engelsystem\Container\Container::instance
* @covers \Engelsystem\Container\Container::resolve
*/
public function testInstance()
{
$container = new Container();
$class = new class
{
};
$container->instance('foo', $class);
$this->assertSame($class, $container->get('foo'));
}
/**
* @covers \Engelsystem\Container\Container::has
*/
public function testHas()
{
$container = new Container();
$this->assertFalse($container->has('test'));
$class = new class
{
};
$container->instance('test', $class);
$this->assertTrue($container->has('test'));
}
/**
* @covers \Engelsystem\Container\Container::singleton
*/
public function testSingleton()
{
$container = new Container();
$class = new class
{
};
$container->singleton('foo', $class);
$this->assertSame($class, $container->get('foo'));
$this->assertSame($class, $container->get('foo'));
}
/**
* @covers \Engelsystem\Container\Container::setInstance
* @covers \Engelsystem\Container\Container::getInstance
*/
public function testContainerSingleton()
{
// Ensure that no container has been initialized
$reflection = new \ReflectionProperty(Container::class, 'instance');
$reflection->setAccessible(true);
$reflection->setValue(null, null);
$reflection->setAccessible(false);
$container0 = new Container();
$container = Container::getInstance();
$this->assertNotSame($container0, $container);
$container1 = new Container;
Container::setInstance($container1);
$this->assertSame($container1, Container::getInstance());
}
}

152
tests/Unit/HelpersTest.php Normal file
View File

@ -0,0 +1,152 @@
<?php
namespace Engelsystem\Test\Config;
use Engelsystem\Application;
use Engelsystem\Config\Config;
use Engelsystem\Container\Container;
use Engelsystem\Http\Request;
use Engelsystem\Renderer\Renderer;
use Engelsystem\Routing\UrlGenerator;
use PHPUnit\Framework\TestCase;
use Symfony\Component\HttpFoundation\Session\Session;
class HelpersTest extends TestCase
{
/**
* @covers \app
*/
public function testApp()
{
$class = new class
{
};
$appMock = $this->getAppMock('some.name', $class);
$this->assertEquals($appMock, app());
$this->assertEquals($class, app('some.name'));
}
/**
* @covers \config
*/
public function testConfig()
{
$configMock = $this->getMockBuilder(Config::class)
->getMock();
$this->getAppMock('config', $configMock);
$this->assertEquals($configMock, config());
$configMock->expects($this->once())
->method('set')
->with(['foo' => 'bar']);
$this->assertTrue(config(['foo' => 'bar']));
$configMock->expects($this->once())
->method('get')
->with('mail')
->willReturn(['user' => 'FooBar']);
$this->assertEquals(['user' => 'FooBar'], config('mail'));
}
/**
* @covers \request
*/
public function testRequest()
{
$requestMock = $this->getMockBuilder(Request::class)
->getMock();
$this->getAppMock('request', $requestMock);
$this->assertEquals($requestMock, request());
$requestMock->expects($this->once())
->method('input')
->with('requestKey')
->willReturn('requestValue');
$this->assertEquals('requestValue', request('requestKey'));
}
/**
* @covers \session
*/
public function testSession()
{
$sessionMock = $this->getMockBuilder(Session::class)
->getMock();
$this->getAppMock('session', $sessionMock);
$this->assertEquals($sessionMock, session());
$sessionMock->expects($this->once())
->method('get')
->with('someKey')
->willReturn('someValue');
$this->assertEquals('someValue', session('someKey'));
}
/**
* @covers \view
*/
public function testView()
{
$rendererMock = $this->getMockBuilder(Renderer::class)
->getMock();
$this->getAppMock('renderer', $rendererMock);
$this->assertEquals($rendererMock, view());
$rendererMock->expects($this->once())
->method('render')
->with('template.name', ['template' => 'data'])
->willReturn('rendered template');
$this->assertEquals('rendered template', view('template.name', ['template' => 'data']));
}
/**
* @covers \url
*/
public function testUrl()
{
$urlGeneratorMock = $this->getMockBuilder(UrlGenerator::class)
->getMock();
$this->getAppMock('routing.urlGenerator', $urlGeneratorMock);
$this->assertEquals($urlGeneratorMock, url());
$urlGeneratorMock->expects($this->once())
->method('to')
->with('foo/bar', ['param' => 'value'])
->willReturn('http://lorem.ipsum/foo/bar?param=value');
$this->assertEquals('http://lorem.ipsum/foo/bar?param=value', url('foo/bar', ['param' => 'value']));
}
/**
* @param string $alias
* @param object $object
* @return Application|\PHPUnit_Framework_MockObject_MockObject
*/
protected function getAppMock($alias, $object)
{
$appMock = $this->getMockBuilder(Container::class)
->getMock();
$appMock->expects($this->atLeastOnce())
->method('get')
->with($alias)
->willReturn($object);
/** @var $appMock Application */
Application::setInstance($appMock);
return $appMock;
}
}

View File

@ -0,0 +1,67 @@
<?php
namespace Engelsystem\Test\Config;
use Engelsystem\Renderer\HtmlEngine;
use PHPUnit\Framework\TestCase;
class HtmlEngineTest extends TestCase
{
/** @var string[] */
protected $tmpFileNames = [];
/**
* @covers \Engelsystem\Renderer\HtmlEngine::get
*/
public function testGet()
{
$engine = new HtmlEngine();
$file = $this->createTempFile('<div>%main_content%</div>');
$data = $engine->get($file, ['main_content' => 'Lorem ipsum dolor sit']);
$this->assertEquals('<div>Lorem ipsum dolor sit</div>', $data);
}
/**
* @covers \Engelsystem\Renderer\HtmlEngine::canRender
*/
public function testCanRender()
{
$engine = new HtmlEngine();
$this->assertFalse($engine->canRender('/dev/null'));
$file = $this->createTempFile();
$this->assertTrue($engine->canRender($file));
$htmFile = $this->createTempFile('', '.htm');
$this->assertTrue($engine->canRender($htmFile));
}
/**
* @param string $content
* @param string $extension
* @return string
*/
protected function createTempFile($content = '', $extension = '.html')
{
$tmpFileName = tempnam(sys_get_temp_dir(), 'EngelsystemUnitTest');
$fileName = $tmpFileName . $extension;
rename($tmpFileName, $fileName);
file_put_contents($fileName, $content);
$this->tmpFileNames[] = $fileName;
return $fileName;
}
public function tearDown()
{
foreach ($this->tmpFileNames as $fileName) {
unlink($fileName);
}
}
}

View File

@ -0,0 +1,55 @@
<?php
namespace Engelsystem\Test\Config;
use Engelsystem\Renderer\EngineInterface;
use Engelsystem\Renderer\Renderer;
use PHPUnit\Framework\TestCase;
use Psr\Log\LoggerInterface;
class RendererTest extends TestCase
{
public function testGet()
{
$renderer = new Renderer();
$nullRenderer = $this->getMockForAbstractClass(EngineInterface::class);
$nullRenderer->expects($this->atLeastOnce())
->method('canRender')
->willReturn(false);
$renderer->addRenderer($nullRenderer);
$mockRenderer = $this->getMockForAbstractClass(EngineInterface::class);
$mockRenderer->expects($this->atLeastOnce())
->method('canRender')
->with('foo.template')
->willReturn(true);
$mockRenderer->expects($this->atLeastOnce())
->method('get')
->with('foo.template', ['lorem' => 'ipsum'])
->willReturn('Rendered content');
$renderer->addRenderer($mockRenderer);
$data = $renderer->render('foo.template', ['lorem' => 'ipsum']);
$this->assertEquals('Rendered content', $data);
}
public function testError()
{
$renderer = new Renderer();
$loggerMock = $this->getMockForAbstractClass(LoggerInterface::class);
$loggerMock
->expects($this->once())
->method('error');
$renderer->setLogger($loggerMock);
$data = $renderer->render('testing.template');
$this->assertEquals('', $data);
}
}

View File

@ -0,0 +1,51 @@
<?php
namespace Engelsystem\Test\Config;
use Engelsystem\Application;
use Engelsystem\Container\Container;
use Engelsystem\Http\Request;
use Engelsystem\Routing\UrlGenerator;
use PHPUnit\Framework\TestCase;
class UrlGeneratorTest extends TestCase
{
public function provideLinksTo()
{
return [
['/foo/path', '/foo/path', 'http://foo.bar/foo/path', [], 'http://foo.bar/foo/path'],
['foo', '/foo', 'https://foo.bar/foo', [], 'https://foo.bar/foo'],
['foo', '/foo', 'http://f.b/foo', ['test' => 'abc', 'bla' => 'foo'], 'http://f.b/foo?test=abc&bla=foo'],
];
}
/**
* @dataProvider provideLinksTo
* @covers \Engelsystem\Routing\UrlGenerator::to
*
* @param string $path
* @param string $willReturn
* @param string $urlToPath
* @param string[] $arguments
* @param string $expectedUrl
*/
public function testTo($urlToPath, $path, $willReturn, $arguments, $expectedUrl)
{
$app = new Container();
$urlGenerator = new UrlGenerator();
Application::setInstance($app);
$request = $this->getMockBuilder(Request::class)
->getMock();
$request->expects($this->once())
->method('getUriForPath')
->with($path)
->willReturn($willReturn);
$app->instance('request', $request);
$url = $urlGenerator->to($urlToPath, $arguments);
$this->assertEquals($expectedUrl, $url);
}
}