protected function render($template, $params)
{
- return new IPF_HTTP_Response(IPF_Twig::render($template, $params, $this->request));
+ $body = $this->container['template']->render($template, $params, $this->request);
+ return new IPF_HTTP_Response($body);
}
/**
{
function process($action, $request, $matches)
{
- return IPF::callFunction($action, array($request, $matches));
+ $get_callable = $this->container['get_callable'];
+ $callable = call_user_func($get_callable, $action);
+
+ if ($callable !== null)
+ return call_user_func_array($callable, array($request, $matches));
+ else
+ throw new \Exception('Impossible to load view function: ' . $action . '.');
}
}
-
--- /dev/null
+<?php
+
+namespace IPF;
+
+use Pimple\Container;
+use Pimple\ServiceProviderInterface;
+
+class CoreServicesProvider implements ServiceProviderInterface
+{
+ public function register(Container $container)
+ {
+ $container['get_callable'] = function ($c) {
+ $project_root = $c['settings']->get('project_root');
+ return function ($function) use ($project_root) {
+ return self::detectCallable($project_root, $function);
+ };
+ };
+ }
+
+ private static function detectCallable($project_root, $function)
+ {
+ if (is_array($function) && count($function) === 2) {
+ // object/class method
+ return $function;
+ }
+
+ if (is_string($function) && preg_match('/^([\\\\\w]+)::(\w+)$/i', $function, $m)) {
+ // static method
+ return array($m[1], $m[2]);
+ }
+
+ if (is_string($function) && preg_match('/^\w+_\w+$/i', $function)) {
+ // plain function
+
+ if (!function_exists($function)) {
+ $parts = explode('_', $function);
+ array_pop($parts);
+ $file = DIRECTORY_SEPARATOR . strtolower(implode(DIRECTORY_SEPARATOR, $parts)) . '.php';
+
+ $filename = $project_root . DIRECTORY_SEPARATOR . 'project' . $file;
+ if (is_file($filename)) {
+ include_once $filename;
+ if (function_exists($function)) {
+ return $function;
+ }
+ }
+ } else {
+ return $function;
+ }
+ }
+
+ return null;
+ }
+}
<?php
+use IPF\CoreServicesProvider;
+use IPF\Template\TemplateProvider;
use Pimple\Container;
final class IPF_Project
public function run()
{
+ $this->container->register(new CoreServicesProvider());
+ $this->container->register(new TemplateProvider());
+
$this->container['databaseConnection'] = function ($c) {
return IPF_Database::connect();
};
+++ /dev/null
-<?php
-
-final class IPF_Project_Template
-{
- public static function templateDirs()
- {
- $dirs = array();
-
- $projectTemplates = IPF::get('project_root') . '/project/templates';
- if (is_dir($projectTemplates))
- $dirs[] = $projectTemplates;
-
- foreach (IPF_Project::getInstance()->appList() as $app) {
- $applicationTemplates = $app->getPath() . 'templates';
- if (is_dir($applicationTemplates))
- $dirs[] = $applicationTemplates;
- }
-
- return $dirs;
- }
-
- public static function context($params=array(), $request=null)
- {
- $params = array_merge(array(
- 'indexpage_url' => IPF::get('indexpage_url'),
- 'STATIC_URL' => IPF::get('static_url'),
- 'MEDIA_URL' => IPF::get('media_url'),
- 'UPLOAD_URL' => IPF::getUploadUrl(),
- ), $params);
-
- if ($request) {
- $params = array_merge(array(
- 'request' => $request,
- 'user' => $request->user,
- 'CURRENT_URL' => $request->query,
- ), $params);
-
- foreach (IPF::get('template_context_processors', array()) as $proc) {
- $c = IPF::callFunction($proc, array($request));
- $params = array_merge($c, $params);
- }
-
- foreach (IPF_Project::getInstance()->appList() as $app) {
- $params = array_merge($app->templateContext($request), $params);
- }
- }
- return $params;
- }
-
- public static function urlTag($args)
- {
- $count = count($args);
- if ($count === 0)
- throw new IPF_Exception('No view specified');
-
- $view = array_shift($args);
-
- if ($count === 2 && is_array($args[0])) {
- return IPF_HTTP_URL::urlForView($view, $args[0]);
- } elseif ($count === 3 && is_array($args[0]) && is_array($args[1])) {
- return IPF_HTTP_URL::urlForView($view, $args[0], $args[1]);
- } else {
- return IPF_HTTP_URL::urlForView($view, $args);
- }
- }
-
- public static function paramsTag($args)
- {
- $params = array();
-
- $count = count($args);
- for ($i = 0; $i < $count; ) {
- if (is_array($args[$i])) {
- foreach ($args[$i] as $key => $value) {
- if ($value === null)
- unset($params[$key]);
- else
- $params[$key] = $value;
- }
-
- ++$i;
- } else {
- $key = $args[$i];
- $value = $args[$i+1];
- if ($value === null)
- unset($params[$key]);
- else
- $params[$key] = $value;
-
- $i += 2;
- }
- }
-
- return IPF_HTTP_URL::generateParams($params);
- }
-}
--- /dev/null
+<?php
+
+namespace IPF\Template;
+
+use Exception;
+use IPF_Application;
+use IPF_Router;
+use IPF_Settings;
+use Pimple\Container;
+use Pimple\ServiceProviderInterface;
+use Twig\Environment;
+use Twig\Loader\FilesystemLoader;
+use Twig\Loader\LoaderInterface;
+use Twig\TwigFunction;
+
+interface TemplateEnvironment
+{
+ function render($template, $context = [], $request = null);
+}
+
+class TemplateEnvironmentTwig implements TemplateEnvironment
+{
+ /** @var IPF_Settings */
+ private $settings;
+ /** @var array */
+ private $processors;
+ /** @var IPF_Application[] */
+ private $apps;
+ /** @var LoaderInterface */
+ private $loader;
+ /** @var IPF_Router */
+ private $router;
+
+ public function __construct(IPF_Settings $settings, array $processors, array $apps, LoaderInterface $loader, IPF_Router $router)
+ {
+ $this->settings = $settings;
+ $this->processors = $processors;
+ $this->apps = $apps;
+ $this->loader = $loader;
+ $this->router = $router;
+ }
+
+ public function render($template, $context = [], $request = null)
+ {
+ $twig = $this->createEnvironment();
+ $this->addCommonGlobals($twig);
+ $this->addRequestGlobals($twig, $request);
+ $this->addFunctions($twig);
+
+ $template = $twig->load($template);
+
+ return $template->render($context);
+ }
+
+ private function createEnvironment()
+ {
+ $options = array(
+ 'cache' => $this->settings->get('tmp'),
+ );
+ if ($this->settings->get('debug')) {
+ $options['debug'] = true;
+ $options['auto_reload'] = true;
+ }
+
+ return new Environment($this->loader, $options);
+ }
+
+ private function addCommonGlobals(Environment $twig)
+ {
+ $twig->addGlobal('indexpage_url', $this->settings->get('indexpage_url'));
+ $twig->addGlobal('STATIC_URL', $this->settings->get('static_url'));
+ $twig->addGlobal('MEDIA_URL', $this->settings->get('media_url'));
+ $twig->addGlobal('UPLOAD_URL', $this->settings->get('upload_url'));
+ }
+
+ private function addRequestGlobals(Environment $twig, $request)
+ {
+ if (!$request)
+ return;
+
+ $twig->addGlobal('request', $request);
+ $twig->addGlobal('user', $request->user);
+ $twig->addGlobal('CURRENT_URL', $request->query);
+
+ foreach ($this->processors as $proc) {
+ $context = call_user_func_array($proc, array($request));
+ foreach ($context as $key => $value) {
+ $twig->addGlobal($key, $value);
+ }
+ }
+
+ foreach ($this->apps as $app) {
+ $context = $app->templateContext($request);
+ foreach ($context as $key => $value) {
+ $twig->addGlobal($key, $value);
+ }
+ }
+ }
+
+ private function addFunctions(Environment $twig)
+ {
+ $twig->addFunction(new TwigFunction('url', new UrlTag($this->router)));
+ $twig->addFunction(new TwigFunction('params', function (...$args) {
+ return paramsTag(...$args);
+ }));
+ $twig->addFunction(new TwigFunction('trans', function ($str) {
+ return __($str);
+ }));
+ }
+}
+
+final class TemplateProvider implements ServiceProviderInterface
+{
+ public function register(Container $container)
+ {
+ $container['template_dirs'] = function ($c) {
+ $project_root = $c['settings']->get('project_root');
+ $apps = $c['apps'];
+
+ $dirs = array();
+
+ $projectTemplates = $project_root . '/project/templates';
+ if (is_dir($projectTemplates))
+ $dirs[] = $projectTemplates;
+
+ foreach ($apps as $app) {
+ $applicationTemplates = $app->getPath() . 'templates';
+ if (is_dir($applicationTemplates))
+ $dirs[] = $applicationTemplates;
+ }
+
+ return $dirs;
+ };
+
+ $container['template_loader'] = function ($c) {
+ $templateDirs = $c['template_dirs'];
+
+ $loader = new FilesystemLoader(array());
+ foreach ($templateDirs as $dir) {
+ $loader->addPath($dir);
+ }
+ return $loader;
+ };
+
+ $container['template_context_processors'] = function ($c) {
+ $get_callable = $c['get_callable'];
+ $processors = [];
+ foreach ($c['settings']->get('template_context_processors', array()) as $proc) {
+ $callable = call_user_func($get_callable, $proc);
+ if ($callable !== null)
+ $processors[] = $callable;
+ else
+ throw new Exception('Impossible to load template context processor: ' . $proc . '.');
+ }
+ return $processors;
+ };
+
+ $container['template'] = function ($c) {
+ return new TemplateEnvironmentTwig($c['settings'], $c['template_context_processors'], $c['apps'], $c['template_loader'], $c['router']);
+ };
+ }
+}
+
+class UrlTag
+{
+ /** @var IPF_Router */
+ private $router;
+
+ public function __construct(IPF_Router $router)
+ {
+ $this->router = $router;
+ }
+
+ public function __invoke($view, ...$args)
+ {
+ $count = count($args);
+
+ if ($count === 1 && is_array($args[0])) {
+ return $this->router->reverse($view, $args[0]);
+ } elseif ($count === 2 && is_array($args[0]) && is_array($args[1])) {
+ $query = count($args[1]) > 0 ? '?' . paramsTag(...$args[1]) : '';
+ return $this->router->reverse($view, $args[0]) . $query;
+ } else {
+ return $this->router->reverse($view, $args);
+ }
+ }
+}
+
+function paramsTag(...$args)
+{
+ $params = array();
+ $count = count($args);
+ for ($i = 0; $i < $count;) {
+ if (is_array($args[$i])) {
+ foreach ($args[$i] as $key => $value) {
+ if ($value === null)
+ unset($params[$key]);
+ else
+ $params[$key] = $value;
+ }
+
+ ++$i;
+ } else {
+ $key = $args[$i];
+ $value = $args[$i + 1];
+ if ($value === null)
+ unset($params[$key]);
+ else
+ $params[$key] = $value;
+
+ $i += 2;
+ }
+ }
+
+ $params_list = array();
+ foreach ($params as $key => $value)
+ $params_list[] = urlencode($key) . '=' . urlencode($value);
+
+ return implode('&', $params_list);
+}
+++ /dev/null
-<?php
-
-final class IPF_Twig
-{
- public static function render($tplfile, $params=array(), $request=null)
- {
- $context = IPF_Project_Template::context($params, $request);
- $twig = self::createEnvironment();
- $template = $twig->loadTemplate($tplfile);
- return $template->render($context);
- }
-
- private static function createEnvironment()
- {
- $options = array(
- 'cache' => IPF::get('tmp'),
- );
- if (IPF::get('debug')) {
- $options['debug'] = true;
- $options['auto_reload'] = true;
- }
-
- $twig = new Twig_Environment(self::createLoader(), $options);
-
- $twig->addFunction(new Twig_SimpleFunction('url', function() {
- return IPF_Project_Template::urlTag(func_get_args());
- }));
- $twig->addFunction(new Twig_SimpleFunction('params', function() {
- return IPF_Project_Template::paramsTag(func_get_args());
- }));
- $twig->addFunction(new Twig_SimpleFunction('trans', function($str) {
- return __($str);
- }));
-
- return $twig;
- }
-
- private static function createLoader()
- {
- $loader = new Twig_Loader_Filesystem(array());
- foreach (IPF_Project_Template::templateDirs() as $dir) {
- $loader->addPath($dir);
- }
- return $loader;
- }
-}