Some improvements in configure the application. Code style. Start SettingsService and VisitorService.

This commit is contained in:
Vasily Komrakov 2016-05-28 22:42:51 +03:00
commit fcfe441ca5
27 changed files with 447 additions and 124 deletions

24
app.php Normal file
View file

@ -0,0 +1,24 @@
<?php
require_once __DIR__ . '/bootstrap.php';
/** @var \TorrentPier\View $view */
$view = $di->view;
/** @var \TorrentPier\Db\Adapter $db */
$db = $di->db;
$view->addGlobal('title', 'Title Page Simple');
$categories = $db->select('bb_categories', function (\Zend\Db\Sql\Select $query) {
$query->join('bb_forums', 'bb_categories.cat_id = bb_forums.cat_id');
})->all();
echo $view->make('app', [
'data' => 'Hello world',
'categories' => $categories
]);

30
bootstrap.php Normal file
View file

@ -0,0 +1,30 @@
<?php
require_once __DIR__ . '/vendor/autoload.php';
use TorrentPier\Di,
TorrentPier\ServiceProviders;
$di = new Di();
$di->register(new ServiceProviders\ConfigServiceProvider, [
'file.system.main' => __DIR__ . '/configs/main.php',
'file.local.main' => __DIR__ . '/configs/local.php',
]);
//// Application
$di->register(new ServiceProviders\LogServiceProvider());
$di->register(new ServiceProviders\CacheServiceProvider());
$di->register(new ServiceProviders\DbServiceProvider());
$di->register(new ServiceProviders\SettingsServiceProvider());
$di->register(new ServiceProviders\VisitorServiceProvider());
$di->register(new ServiceProviders\RequestServiceProvider());
// Services
//$di->register(new \TorrentPier\ServiceProviders\SphinxServiceProvider());
// View and Templates
$di->register(new ServiceProviders\TranslationServiceProvider());
$di->register(new ServiceProviders\CaptchaServiceProvider());
$di->register(new ServiceProviders\TwigServiceProvider());
$di->register(new ServiceProviders\ViewServiceProvider());

View file

@ -38,44 +38,19 @@ $di['settings.locale'] = function($di) {
};
$di->register(new \TorrentPier\ServiceProviders\ConfigServiceProvider, [
'config.file.system.main' => __DIR__ . '/library/config.php',
'config.file.local.main' => __DIR__ . '/library/config.local.php',
]);
$di->register(new \TorrentPier\ServiceProviders\LogServiceProvider(), [
'config.log.handlers' => $di->config->log->handlers
]);
$di->register(new \TorrentPier\ServiceProviders\CacheServiceProvider(), [
'config.services.cache' => $di->config->services->cache->toArray()
]);
$di->register(new \TorrentPier\ServiceProviders\DbServiceProvider, [
'config.db' => $di->config->db->toArray()
]);
$di->register(new \TorrentPier\ServiceProviders\SphinxServiceProvider, [
'config.sphinx' => $di->config->sphinx->toArray()
'file.system.main' => __DIR__ . '/library/config.php',
'file.local.main' => __DIR__ . '/library/config.local.php',
]);
$di->register(new \TorrentPier\ServiceProviders\LogServiceProvider());
$di->register(new \TorrentPier\ServiceProviders\CacheServiceProvider());
$di->register(new \TorrentPier\ServiceProviders\DbServiceProvider());
$di->register(new \TorrentPier\ServiceProviders\SphinxServiceProvider());
$di->register(new \TorrentPier\ServiceProviders\RequestServiceProvider());
$di->register(new \TorrentPier\ServiceProviders\ViewServiceProvider());
$di->register(new \TorrentPier\ServiceProviders\TranslationServiceProvider(), [
'config.debug' => $di->config->debug,
'config.translator.dir_cache' => $di->config->translator->dir_cache,
'config.translator.resources' => $di->config->translator->resources->toArray()
]);
$di->register(new \TorrentPier\ServiceProviders\TwigServiceProvider, [
'config.debug' => $di->config->debug,
'config.twig.dir_templates' => $di->config->twig->dir_templates,
'config.twig.dir_cache' => $di->config->twig->dir_cache
]);
$di->register(new \TorrentPier\ServiceProviders\CaptchaServiceProvider, [
'config.captcha.secret_key' => $di->config->captcha->secret_key
]);
$di->register(new \TorrentPier\ServiceProviders\TranslationServiceProvider());
$di->register(new \TorrentPier\ServiceProviders\TwigServiceProvider());
$di->register(new \TorrentPier\ServiceProviders\CaptchaServiceProvider());
$bb_cfg = $di->config->toArray();
$page_cfg = $di->config->page->toArray();
@ -132,11 +107,11 @@ require(CORE_DIR . 'dbs.php');
$DBS = new DBS([
'db' => [
'db' => [
$di->config->db->hostname,
$di->config->db->database,
$di->config->db->username,
$di->config->db->password,
$di->config->db->charset,
$di->config->services->db->hostname,
$di->config->services->db->database,
$di->config->services->db->username,
$di->config->services->db->password,
$di->config->services->db->charset,
false
]
],

79
configs/main.php Normal file
View file

@ -0,0 +1,79 @@
<?php
return [
'debug' => false,
'services' => [
// Database
'db' => [
'debug' => '{self.debug}',
'driver' => 'Pdo_Mysql',
'hostname' => '127.0.0.1',
'database' => 'tp_220',
'username' => 'user',
'password' => 'pass',
'charset' => 'utf8'
],
// Cache
'cache' => [
'adapter' => \TorrentPier\Cache\FileAdapter::class,
'options' => [
'directory' => __DIR__ . '/../internal_data/cache',
],
],
// Translation
'translator' => [
'dir_cache' => __DIR__ . '/../internal_data/cache',
'resources' => [
[
'resource' => __DIR__ . '/../messages/ru.php',
'locale' => 'ru',
],
[
'resource' => __DIR__ . '/../messages/en.php',
'locale' => 'en',
]
]
],
// Twig
'twig' => [
'dir_templates' => __DIR__ . '/../templates/default',
'dir_cache' => __DIR__ . '/../internal_data/cache',
],
// Sphinx
'sphinx' => [
'debug' => '{self.debug}',
'driver' => '{self.db.driver}',
'hostname' => '{self.db.hostname}',
'username' => 'user',
'password' => 'pass',
'port' => 9306,
'charset' => 'utf8'
],
// Logger
'log' => [
'handlers' => [
function () {
return new \Monolog\Handler\StreamHandler(
__DIR__.'/../internal_data/log/app.log',
\Monolog\Logger::DEBUG
);
}
]
],
// Captcha
// Get a Google reCAPTCHA API Key: https://www.google.com/recaptcha/admin
'captcha' => [
'disabled' => false,
'public_key' => '', // your public key
'secret_key' => '', // your secret key
'theme' => 'light', // light or dark
],
]
];

View file

@ -37,62 +37,62 @@ $config = [
],
],
],*/
],
// Database
'db' => [
'debug' => '{self.debug}',
'driver' => 'Pdo_Mysql',
'hostname' => '127.0.0.1',
'database' => 'tp_220',
'username' => 'user',
'password' => 'pass',
'charset' => 'utf8'
],
// Database
'db' => [
'debug' => '{self.debug}',
'driver' => 'Pdo_Mysql',
'hostname' => '127.0.0.1',
'database' => 'tp_220',
'username' => 'user',
'password' => 'pass',
'charset' => 'utf8'
],
// Sphinx
'sphinx' => [
'debug' => '{self.debug}',
'driver' => '{self.db.driver}',
'hostname' => '{self.db.hostname}',
'username' => 'user',
'password' => 'pass',
'port' => 9306,
'charset' => 'utf8'
],
// Sphinx
'sphinx' => [
'debug' => '{self.debug}',
'driver' => '{self.db.driver}',
'hostname' => '{self.db.hostname}',
'username' => 'user',
'password' => 'pass',
'port' => 9306,
'charset' => 'utf8'
],
// Twig
'twig' => [
'dir_templates' => __DIR__ . '/../templates/default',
'dir_cache' => __DIR__ . '/../internal_data/cache',
],
// Twig
'twig' => [
'dir_templates' => __DIR__ . '/../templates/default',
'dir_cache' => __DIR__ . '/../internal_data/cache',
],
// Translation
'translator' => [
'dir_cache' => __DIR__ . '/../internal_data/cache',
'resources' => [
[
'resource' => __DIR__ . '/../messages/ru.php',
'locale' => 'ru',
],
[
'resource' => __DIR__ . '/../messages/en.php',
'locale' => 'en',
// Translation
'translator' => [
'dir_cache' => __DIR__ . '/../internal_data/cache',
'resources' => [
[
'resource' => __DIR__ . '/../messages/ru.php',
'locale' => 'ru',
],
[
'resource' => __DIR__ . '/../messages/en.php',
'locale' => 'en',
]
]
]
],
],
// Log
'log' => [
'handlers' => [
function () {
return new \Monolog\Handler\StreamHandler(
__DIR__.'/../internal_data/log/app.log',
\Monolog\Logger::DEBUG
);
}
]
],
// Log
'log' => [
'handlers' => [
function () {
return new \Monolog\Handler\StreamHandler(
__DIR__.'/../internal_data/log/app.log',
\Monolog\Logger::DEBUG
);
}
]
],
],
// Aliases
// TODO: удалить

View file

@ -19,7 +19,7 @@ class Config extends ZendConfig
* @param bool $allowModifications
* @param Config|null $root
*/
public function __construct(array $array, $allowModifications = false, Config $root = null)
public function __construct(array $array, $allowModifications = false, Config &$root = null)
{
$this->allowModifications = (bool) $allowModifications;

View file

@ -10,6 +10,8 @@ use Pimple\Container;
*/
class Di extends Container
{
const DELIMITER = '.';
private static $instance;
/**
@ -24,7 +26,9 @@ class Di extends Container
}
/**
* @return Di
* Get instance dependency injection container.
*
* @return self
* @throws \RuntimeException
*/
public static function getInstance()
@ -37,16 +41,27 @@ class Di extends Container
}
/**
* @param $id
* @return mixed
* @throws \RuntimeException
* {@inheritdoc}
*/
public function offsetGet($id)
{
try {
return parent::offsetGet($id);
} catch (\InvalidArgumentException $e) {
if (strpos($id, self::DELIMITER)) {
list($service, $key) = explode(self::DELIMITER, $id, 2);
if ($this->offsetExists($service)) {
return parent::offsetGet($service)->get($key);
}
}
throw new \InvalidArgumentException($e->getMessage(), $e->getCode(), $e->getPrevious());
}
}
public function __get($id)
{
if ($this->offsetExists($id)) {
return $this->offsetGet($id);
}
throw new \RuntimeException("Service '{$id}' is not registered in the container");
return $this->offsetGet($id);
}
}

View file

@ -21,7 +21,7 @@ class CacheServiceProvider implements ServiceProviderInterface
$setting = $container['config.services.cache'];
/** @var Adapter $adapter */
$adapter = new $setting['adapter']();
$adapter->setOptions($setting['options']);
$adapter->setOptions($setting['options']->toArray());
return $adapter;
};
}

View file

@ -18,7 +18,7 @@ class CaptchaServiceProvider implements ServiceProviderInterface
public function register(Container $container)
{
$container['captcha'] = function (Container $container) {
$captcha = new ReCaptcha($container['config.captcha.secret_key']);
$captcha = new ReCaptcha($container['config.services.captcha.secret_key']);
return $captcha;
};
}

View file

@ -19,10 +19,10 @@ class ConfigServiceProvider implements ServiceProviderInterface
public function register(Container $container)
{
$container['config'] = function ($container) {
$config = new Config(Factory::fromFile($container['config.file.system.main']));
$config = new Config(Factory::fromFile($container['file.system.main']));
if (isset($container['config.file.local.main']) && file_exists($container['config.file.local.main'])) {
$config->merge(new Config(Factory::fromFile($container['config.file.local.main'])));
if (isset($container['file.local.main']) && file_exists($container['file.local.main'])) {
$config->merge(new Config(Factory::fromFile($container['file.local.main'])));
}
return $config;

View file

@ -19,8 +19,8 @@ class DbServiceProvider implements ServiceProviderInterface
public function register(Container $container)
{
$container['db'] = function ($container) {
$adapter = new Adapter($container['config.db']);
unset($container['config.db']);
$adapter = new Adapter($container['config.services.db']);
unset($container['config.services.db']);
return $adapter;
};
}

View file

@ -26,7 +26,7 @@ class LogServiceProvider implements ServiceProviderInterface
$container['log'] = function ($container) {
/** @var Logger $logger */
$logger = $container['logger']('app');
foreach ($container['config.log.handlers'] as $logHandler) {
foreach ($container['config.services.log.handlers'] as $logHandler) {
$logger->pushHandler(call_user_func($logHandler));
}

View file

@ -0,0 +1,17 @@
<?php
namespace TorrentPier\ServiceProviders;
use Pimple\Container;
use Pimple\ServiceProviderInterface;
use TorrentPier\Settings;
class SettingsServiceProvider implements ServiceProviderInterface
{
public function register(Container $container)
{
$container['settings'] = function (Container $container) {
return new Settings($container['db'], $container['cache']);
};
}
}

View file

@ -24,7 +24,7 @@ class SphinxServiceProvider implements ServiceProviderInterface
{
$container['sphinx'] = function ($container) {
$platform = new SphinxQL();
$adapter = new Adapter($container['config.sphinx']);
$adapter = new Adapter($container['config.services.sphinx']);
$driver = $adapter->getDriver();
// Check driver
@ -38,7 +38,7 @@ class SphinxServiceProvider implements ServiceProviderInterface
}
$platform->setDriver($adapter->getDriver());
unset($container['config.sphinx']);
unset($container['config.services.sphinx']);
return $adapter;
};

View file

@ -13,19 +13,23 @@ use Symfony\Component\Translation\Translator;
*/
class TranslationServiceProvider implements ServiceProviderInterface
{
/**
* {@inheritdoc}
*/
public function register(Container $container)
{
$container['translator'] = function (Container $container) {
$translator = new Translator(
$container['settings.locale'],
$container['visitor.settings.base.locale'] ?: $container['settings.base.locale'],
null,
null, // $container['config.translator.dir_cache'],
null, // $container['config.services.translator.dir_cache'],
$container['config.debug']
);
$translator->addLoader('php', new PhpFileLoader());
foreach ($container['config.translator.resources'] as $item) {
$resources = $container['config.services.translator.resources']->toArray();
foreach ($resources as $item) {
$translator->addResource('php', $item['resource'], $item['locale']);
}

View file

@ -5,6 +5,7 @@ namespace TorrentPier\ServiceProviders;
use Pimple\Container;
use Pimple\ServiceProviderInterface;
use Symfony\Bridge\Twig\Extension\TranslationExtension;
use TorrentPier\Twig\Loader\Filesystem;
/**
* Class TwigServiceProvider
@ -12,21 +13,20 @@ use Symfony\Bridge\Twig\Extension\TranslationExtension;
*/
class TwigServiceProvider implements ServiceProviderInterface
{
/**
* {@inheritdoc}
*/
public function register(Container $container)
{
$container['twig'] = function (Container $container) {
$loader = new \Twig_Loader_Filesystem($container['config.twig.dir_templates']);
$loader = new Filesystem($container['config.services.twig.dir_templates']);
$twig = new \Twig_Environment($loader, [
'debug' => $container['config.debug'],
'cache' => $container['config.twig.dir_cache'],
'cache' => $container['config.services.twig.dir_cache'],
]);
$twig->addExtension(new \Twig_Extension_Core());
$twig->addExtension(new \Twig_Extension_Escaper());
$twig->addExtension(new \Twig_Extension_Optimizer());
$twig->addExtension(new \Twig_Extension_Debug());
$twig->addExtension(new TranslationExtension($container['translator']));
$twig->addExtension(new \Twig_Extension_Debug());
return $twig;
};

View file

@ -0,0 +1,17 @@
<?php
namespace TorrentPier\ServiceProviders;
use Pimple\Container;
use Pimple\ServiceProviderInterface;
use TorrentPier\Visitor;
class VisitorServiceProvider implements ServiceProviderInterface
{
public function register(Container $container)
{
$container['visitor'] = function (Container $container) {
return new Visitor();
};
}
}

26
src/Settings.php Normal file
View file

@ -0,0 +1,26 @@
<?php
namespace TorrentPier;
use TorrentPier\Db\Adapter as DbAdapter;
use TorrentPier\Cache\Adapter as CacheAdapter;
class Settings
{
/** @var DbAdapter */
protected $db;
/** @var CacheAdapter */
protected $cache;
public function __construct(DbAdapter $db, CacheAdapter $cache)
{
$this->db = $db;
$this->cache = $cache;
}
public function get($key)
{
return null;
}
}

View file

@ -0,0 +1,20 @@
<?php
namespace TorrentPier\Twig\Loader;
/**
* Class Filesystem
* @package TorrentPier\Twig\Loader
*/
class Filesystem extends \Twig_Loader_Filesystem
{
const FILE_EXTENSION = '.twig';
/**
* {@inheritdoc}
*/
protected function normalizeName($name)
{
return parent::normalizeName($name) . self::FILE_EXTENSION;
}
}

View file

@ -19,6 +19,11 @@ class View
$this->twig = $twig;
}
public function addGlobal($name, $value)
{
$this->twig->addGlobal($name, $value);
}
/**
* @param $template
* @param array $params
@ -26,6 +31,6 @@ class View
*/
public function make($template, $params = [])
{
return $this->twig->render($template . '.twig', $params);
return $this->twig->render($template, $params);
}
}

11
src/Visitor.php Normal file
View file

@ -0,0 +1,11 @@
<?php
namespace TorrentPier;
class Visitor
{
public function get()
{
return null;
}
}

View file

@ -0,0 +1,26 @@
{% extends "layouts/main" %}
{% block content %}
<h1>{{ data }}</h1>
<ul>
{% for category in categories %}
<li>
<h3>{{ category.forum_name|e }} - {{ category.cat_title|e }}</h3>
<p>{{ category.forum_desc }}</p>
</li>
{% endfor %}
</ul>
{% endblock %}

View file

@ -0,0 +1,33 @@
<!-- Fixed navbar -->
<nav class="navbar navbar-inverse navbar-fixed-top">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">Bootstrap theme</a>
</div>
<div id="navbar" class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li class="active"><a href="#">Home</a></li>
<li><a href="#about">About</a></li>
<li><a href="#contact">Contact</a></li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Dropdown <span class="caret"></span></a>
<ul class="dropdown-menu">
<li><a href="#">Action</a></li>
<li><a href="#">Another action</a></li>
<li><a href="#">Something else here</a></li>
<li role="separator" class="divider"></li>
<li class="dropdown-header">Nav header</li>
<li><a href="#">Separated link</a></li>
<li><a href="#">One more separated link</a></li>
</ul>
</li>
</ul>
</div><!--/.nav-collapse -->
</div>
</nav>

View file

@ -0,0 +1,41 @@
<!doctype html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>{{ title }}</title>
<!-- Latest compiled and minified CSS -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" integrity="sha384-1q8mTJOASx8j1Au+a5WDVnPi2lkFfwwEAa8hDDdjZlpLegxhjVME1fgjWPGmkzs7" crossorigin="anonymous">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.6.3/css/font-awesome.min.css" integrity="sha384-T8Gy5hrqNKT+hzMclPo118YTQO6cYprQmhrYwIiQ/3axmI1hQomh7Ud2hPOy8SP1" crossorigin="anonymous">
<!-- Optional theme -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap-theme.min.css" integrity="sha384-fLW2N01lMqjakBkx3l/M9EahuwpSfeNvV63J5ezn3uZzapT0u7EYsXMjQV+0En5r" crossorigin="anonymous">
{#<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootswatch/3.3.6/slate/bootstrap.min.css" integrity="sha384-X9JiR5BtXUXiV6R3XuMyVGefFyy+18PHpBwaMfteb/vd2RrK6Gt4KPenkQyWLxCC" crossorigin="anonymous">#}
<style>
body{padding-top:40px;padding-bottom:40px;background-color:#eee}.form-signin{max-width:330px;padding:15px;margin:0 auto}.form-signin .form-signin-heading,.form-signin .checkbox{margin-bottom:10px}.form-signin .checkbox{font-weight:normal}.form-signin .form-control{position:relative;height:auto;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:10px;font-size:16px}.form-signin .form-control:focus{z-index:2}.form-signin input[type="email"]{margin-bottom:-1px;border-bottom-right-radius:0;border-bottom-left-radius:0}.form-signin input[type="password"]{margin-bottom:10px;border-top-left-radius:0;border-top-right-radius:0}
body{padding-top:70px;padding-bottom:30px}.theme-dropdown .dropdown-menu{position:static;display:block;margin-bottom:20px}.theme-showcase>p>.btn{margin:5px 0}.theme-showcase .navbar .container{width:auto}
</style>
</head>
<body>
{% include 'blocks/menu' %}
<div class="container theme-showcase" role="main">
{% block content %}
Content of the page...
{% endblock %}
</div>
<!-- Latest compiled and minified JavaScript -->
<script src="https://code.jquery.com/jquery-2.2.4.min.js" integrity="sha256-BbhdlvQf/xTY9gja0Dq3HiwQF8LaCRTXxZKRutelT44=" crossorigin="anonymous"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js" integrity="sha384-0mSbJDEHialfmuBBQP6A4Qrprq5OVfW37PRR3j5ELqxss1yVqOtnepnHVP9aJ7xS" crossorigin="anonymous"></script>
</body>
</html>

View file

@ -29,8 +29,8 @@ class DiTest extends \PHPUnit_Framework_TestCase
/**
* @depends testGetInstanceIsNotInitialized
* @expectedException \RuntimeException
* @expectedExceptionMessage Service 'test' is not registered in the container
* @expectedException \InvalidArgumentException
* @expectedExceptionMessage Identifier "test" is not defined.
*/
public function testGetByPropertyIsNotExists()
{

View file

@ -14,7 +14,7 @@ class CaptchaServiceProviderTest extends \PHPUnit_Framework_TestCase
{
$di = new Di();
$di->register(new CaptchaServiceProvider, [
'config.captcha.secret_key' => 'secret key'
'config.services.captcha.secret_key' => 'secret key'
]);
static::assertInstanceOf(ReCaptcha::class, $di->captcha);

View file

@ -25,7 +25,7 @@ class ViewTest extends \PHPUnit_Framework_TestCase
$mockTwig
->expects(static::once())
->method('render')
->with(static::equalTo($templateFileName . '.twig'), static::equalTo($templateParam))
->with(static::equalTo($templateFileName), static::equalTo($templateParam))
->willReturnCallback(function () {
return 'test render';
});