Fixes #254 add Laravel helper functions to Twig environment

This commit is contained in:
Sam Georges 2014-07-16 18:28:15 +10:00
parent 54cf043dea
commit 3c7c87b338
7 changed files with 293 additions and 64 deletions

View File

@ -4,7 +4,6 @@ use Lang;
use Backend;
use BackendMenu;
use BackendAuth;
use System\Classes\MarkupManager;
use Backend\Classes\WidgetManager;
use October\Rain\Support\ModuleServiceProvider;
@ -93,48 +92,6 @@ class ServiceProvider extends ModuleServiceProvider
]);
});
/*
* Register markup tags
*/
MarkupManager::instance()->registerCallback(function($manager){
$manager->registerFunctions([
// Global helpers
'post' => 'post',
// Form helpers
'form_ajax' => ['Form', 'ajax'],
'form_open' => ['Form', 'open'],
'form_close' => ['Form', 'close'],
'form_token' => ['Form', 'token'],
'form_session_key' => ['Form', 'sessionKey'],
'form_token' => ['Form', 'token'],
'form_model' => ['Form', 'model'],
'form_label' => ['Form', 'label'],
'form_text' => ['Form', 'text'],
'form_password' => ['Form', 'password'],
'form_checkbox' => ['Form', 'checkbox'],
'form_radio' => ['Form', 'radio'],
'form_file' => ['Form', 'file'],
'form_select' => ['Form', 'select'],
'form_select_range' => ['Form', 'selectRange'],
'form_select_month' => ['Form', 'selectMonth'],
'form_submit' => ['Form', 'submit'],
'form_macro' => ['Form', '__call'],
'form_value' => ['Form', 'value'],
]);
$manager->registerFilters([
// String helpers
'slug' => ['Str', 'slug'],
'plural' => ['Str', 'plural'],
'singular' => ['Str', 'singular'],
'finish' => ['Str', 'finish'],
'snake' => ['Str', 'snake'],
'camel' => ['Str', 'camel'],
'studly' => ['Str', 'studly'],
]);
});
/*
* Register widgets
*/

View File

@ -4,6 +4,7 @@ use URL;
use File;
use Lang;
use Cache;
use Route;
use Config;
use Request;
use Response;
@ -193,7 +194,13 @@ class CombineAssets
*/
protected function getCombinedUrl($outputFilename = 'undefined.css')
{
return URL::action('Cms\Classes\Controller@combine', [$outputFilename], false);
$combineAction = 'Cms\Classes\Controller@combine';
$actionExists = Route::getRoutes()->getByAction($combineAction) !== null;
if ($actionExists)
return URL::action($combineAction, [$outputFilename], false);
else
return Request::getBasePath().'/combine/'.$outputFilename;
}
/**

View File

@ -10,6 +10,7 @@ use BackendAuth;
use Twig_Environment;
use Twig_Loader_String;
use System\Classes\ErrorHandler;
use System\Classes\MarkupManager;
use System\Classes\PluginManager;
use System\Classes\SettingsManager;
use System\Twig\Engine as TwigEngine;
@ -179,6 +180,44 @@ class ServiceProvider extends ModuleServiceProvider
]);
});
/*
* Register markup tags
*/
MarkupManager::instance()->registerCallback(function($manager){
$manager->registerFunctions([
// Functions
'post' => 'post',
'link_to' => 'link_to',
'link_to_asset' => 'link_to_asset',
'link_to_route' => 'link_to_route',
'link_to_action' => 'link_to_action',
'asset' => 'asset',
'action' => 'action',
'url' => 'url',
'route' => 'route',
'secure_url' => 'secure_url',
'secure_asset' => 'secure_asset',
// Classes
'str_*' => ['Str', '*'],
'url_*' => ['URL', '*'],
'html_*' => ['HTML', '*'],
'form_*' => ['Form', '*'],
'form_macro' => ['Form', '__call'],
]);
$manager->registerFilters([
// Classes
'slug' => ['Str', 'slug'],
'plural' => ['Str', 'plural'],
'singular' => ['Str', 'singular'],
'finish' => ['Str', 'finish'],
'snake' => ['Str', 'snake'],
'camel' => ['Str', 'camel'],
'studly' => ['Str', 'studly'],
]);
});
/*
* Register settings
*/

View File

@ -1,5 +1,10 @@
<?php namespace System\Classes;
use Str;
use Twig_TokenParser;
use Twig_SimpleFilter;
use Twig_SimpleFunction;
use System\Classes\ApplicationException;
use System\Classes\PluginManager;
/**
@ -189,4 +194,126 @@ class MarkupManager
return $this->listExtensions(self::EXTENSION_TOKEN_PARSER);
}
/**
* Makes a set of Twig functions for use in a twig extension.
* @param array $functions Current collection
* @return array
*/
public function makeTwigFunctions($functions = [])
{
if (!is_array($functions))
$functions = [];
foreach ($this->listFunctions() as $name => $callable) {
/*
* Handle a wildcard function
*/
if (strpos($name, '*') !== false && $this->isWildCallable($callable)) {
$callable = function($name) use ($callable) {
$arguments = array_slice(func_get_args(), 1);
$method = $this->isWildCallable($callable, Str::camel($name));
return call_user_func_array($method, $arguments);
};
}
if (!is_callable($callable))
throw new ApplicationException(sprintf('The markup function for %s is not callable.', $name));
$functions[] = new Twig_SimpleFunction($name, $callable, ['is_safe' => ['html']]);
}
return $functions;
}
/**
* Makes a set of Twig filters for use in a twig extension.
* @param array $filters Current collection
* @return array
*/
public function makeTwigFilters($filters = [])
{
if (!is_array($filters))
$filters = [];
foreach ($this->listFilters() as $name => $callable) {
/*
* Handle a wildcard function
*/
if (strpos($name, '*') !== false && $this->isWildCallable($callable)) {
$callable = function($name) use ($callable) {
$arguments = array_slice(func_get_args(), 1);
$method = $this->isWildCallable($callable, Str::camel($name));
return call_user_func_array($method, $arguments);
};
}
if (!is_callable($callable))
throw new ApplicationException(sprintf('The markup filter for %s is not callable.', $name));
$filters[] = new Twig_SimpleFilter($name, $callable, ['is_safe' => ['html']]);
}
return $filters;
}
/**
* Makes a set of Twig token parsers for use in a twig extension.
* @param array $parsers Current collection
* @return array
*/
public function makeTwigTokenParsers($parsers = [])
{
if (!is_array($parsers))
$parsers = [];
$extraParsers = $this->listTokenParsers();
foreach ($extraParsers as $obj) {
if (!$obj instanceof Twig_TokenParser)
continue;
$parsers[] = $obj;
}
return $parsers;
}
/**
* Tests if a callable type contains a wildcard, also acts as a
* utility to replace the wildcard with a string.
* @param callable $callable
* @param string $replaceWith
* @return mixed
*/
protected function isWildCallable($callable, $replaceWith = false)
{
$isWild = false;
if (is_string($callable) && strpos($callable, '*') !== false)
$isWild = $replaceWith ? str_replace('*', $replaceWith, $callable) : true;
if (is_array($callable)) {
if (is_string($callable[0]) && strpos($callable[0], '*') !== false) {
if ($replaceWith) {
$isWild = $callable;
$isWild[0] = str_replace('*', $replaceWith, $callable[0]);
}
else
$isWild = true;
}
if (!empty($callable[1]) && strpos($callable[1], '*') !== false) {
if ($replaceWith) {
$isWild = $isWild ?: $callable;
$isWild[1] = str_replace('*', $replaceWith, $callable[1]);
}
else
$isWild = true;
}
}
return $isWild;
}
}

View File

@ -52,12 +52,7 @@ class Extension extends Twig_Extension
/*
* Include extensions provided by plugins
*/
foreach ($this->markupManager->listFunctions() as $name => $callable) {
if (!is_callable($callable))
throw new ApplicationException(sprintf('The markup function for %s is not callable.', $name));
$functions[] = new Twig_SimpleFunction($name, $callable, ['is_safe' => ['html']]);
}
$functions = $this->markupManager->makeTwigFunctions($functions);
return $functions;
}
@ -76,12 +71,7 @@ class Extension extends Twig_Extension
/*
* Include extensions provided by plugins
*/
foreach ($this->markupManager->listFilters() as $name => $callable) {
if (!is_callable($callable))
throw new ApplicationException(sprintf('The markup filter for %s is not callable.', $name));
$filters[] = new Twig_SimpleFilter($name, $callable, ['is_safe' => ['html']]);
}
$filters = $this->markupManager->makeTwigFilters($filters);
return $filters;
}
@ -95,13 +85,10 @@ class Extension extends Twig_Extension
{
$parsers = [];
$extraParsers = $this->markupManager->listTokenParsers();
foreach ($extraParsers as $obj) {
if (!$obj instanceof Twig_TokenParser)
continue;
$parsers[] = $obj;
}
/*
* Include extensions provided by plugins
*/
$parsers = $this->markupManager->makeTwigTokenParsers($parsers);
return $parsers;
}

View File

@ -5,7 +5,8 @@ use Cms\Classes\Theme;
class ControllerTest extends TestCase
{
public function tearDown() {
public function tearDown()
{
Mockery::close();
}

View File

@ -0,0 +1,111 @@
<?php
use System\Classes\MarkupManager;
class MarkupManagerTest extends TestCase
{
public function setUp()
{
include_once base_path().'/tests/fixtures/system/plugins/october/test/Plugin.php';
}
//
// Helpers
//
protected static function callProtectedMethod($object, $name, $params = [])
{
$className = get_class($object);
$class = new ReflectionClass($className);
$method = $class->getMethod($name);
$method->setAccessible(true);
return $method->invokeArgs($object, $params);
}
public static function getProtectedProperty($object, $name)
{
$className = get_class($object);
$class = new ReflectionClass($className);
$property = $class->getProperty($name);
$property->setAccessible(true);
return $property->getValue($object);
}
public static function setProtectedProperty($object, $name, $value)
{
$className = get_class($object);
$class = new ReflectionClass($className);
$property = $class->getProperty($name);
$property->setAccessible(true);
return $property->setValue($object, $value);
}
//
// Tests
//
public function testIsWildCallable()
{
$manager = MarkupManager::instance();
/*
* Negatives
*/
$callable = 'something';
$result = self::callProtectedMethod($manager, 'isWildCallable', [$callable]);
$this->assertFalse($result);
$callable = ['Form', 'open'];
$result = self::callProtectedMethod($manager, 'isWildCallable', [$callable]);
$this->assertFalse($result);
$callable = function() { return 'O, Hai!'; };
$result = self::callProtectedMethod($manager, 'isWildCallable', [$callable]);
$this->assertFalse($result);
/*
* String
*/
$callable = 'something_*';
$result = self::callProtectedMethod($manager, 'isWildCallable', [$callable]);
$this->assertTrue($result);
$result = self::callProtectedMethod($manager, 'isWildCallable', [$callable, 'delicious']);
$this->assertEquals('something_delicious', $result);
/*
* Array
*/
$callable = ['Class', 'foo_*'];
$result = self::callProtectedMethod($manager, 'isWildCallable', [$callable]);
$this->assertTrue($result);
$result = self::callProtectedMethod($manager, 'isWildCallable', [$callable, 'bar']);
$this->assertTrue(isset($result[0]));
$this->assertTrue(isset($result[1]));
$this->assertEquals('Class', $result[0]);
$this->assertEquals('foo_bar', $result[1]);
$callable = ['My*', 'method'];
$result = self::callProtectedMethod($manager, 'isWildCallable', [$callable]);
$this->assertTrue($result);
$result = self::callProtectedMethod($manager, 'isWildCallable', [$callable, 'Class']);
$this->assertTrue(isset($result[0]));
$this->assertTrue(isset($result[1]));
$this->assertEquals('MyClass', $result[0]);
$this->assertEquals('method', $result[1]);
$callable = ['My*', 'my*'];
$result = self::callProtectedMethod($manager, 'isWildCallable', [$callable]);
$this->assertTrue($result);
$result = self::callProtectedMethod($manager, 'isWildCallable', [$callable, 'Food']);
$this->assertTrue(isset($result[0]));
$this->assertTrue(isset($result[1]));
$this->assertEquals('MyFood', $result[0]);
$this->assertEquals('myFood', $result[1]);
}
}