ViewRenderer.php 7.62 KB
Newer Older
1 2 3
<?php
/**
 * @link http://www.yiiframework.com/
Carsten Brandt committed
4
 * @copyright Copyright (c) 2008 Yii Software LLC
5 6 7
 * @license http://www.yiiframework.com/license/
 */

Qiang Xue committed
8
namespace yii\twig;
9

Qiang Xue committed
10 11
use Yii;
use yii\base\View;
12
use yii\base\ViewRenderer as BaseViewRenderer;
13 14 15 16

/**
 * TwigViewRenderer allows you to use Twig templates in views.
 *
17 18
 * @property array $lexerOptions @see self::$lexerOptions. This property is write-only.
 *
19 20 21
 * @author Alexander Makarov <sam@rmcreative.ru>
 * @since 2.0
 */
22
class ViewRenderer extends BaseViewRenderer
23
{
24
    /**
Alexander Makarov committed
25 26
     * @var string the directory or path alias pointing to where Twig cache will be stored. Set to false to disable
     * templates cache.
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
     */
    public $cachePath = '@runtime/Twig/cache';
    /**
     * @var array Twig options.
     * @see http://twig.sensiolabs.org/doc/api.html#environment-options
     */
    public $options = [];
    /**
     * @var array Objects or static classes.
     * Keys of the array are names to call in template, values are objects or names of static classes.
     * Example: `['html' => '\yii\helpers\Html']`.
     * In the template you can use it like this: `{{ html.a('Login', 'site/login') | raw }}`.
     */
    public $globals = [];
    /**
     * @var array Custom functions.
     * Keys of the array are names to call in template, values are names of functions or static methods of some class.
     * Example: `['rot13' => 'str_rot13', 'a' => '\yii\helpers\Html::a']`.
     * In the template you can use it like this: `{{ rot13('test') }}` or `{{ a('Login', 'site/login') | raw }}`.
     */
    public $functions = [];
    /**
     * @var array Custom filters.
     * Keys of the array are names to call in template, values are names of functions or static methods of some class.
     * Example: `['rot13' => 'str_rot13', 'jsonEncode' => '\yii\helpers\Json::encode']`.
     * In the template you can use it like this: `{{ 'test'|rot13 }}` or `{{ model|jsonEncode }}`.
     */
    public $filters = [];
    /**
     * @var array Custom extensions.
57
     * Example: `['Twig_Extension_Sandbox', new \Twig_Extension_Text()]`
58 59 60 61
     */
    public $extensions = [];
    /**
     * @var array Twig lexer options.
62
     *
63 64 65 66 67 68 69 70 71 72 73 74
     * Example: Smarty-like syntax:
     * ```php
     * [
     *     'tag_comment'  => ['{*', '*}'],
     *     'tag_block'    => ['{', '}'],
     *     'tag_variable' => ['{$', '}']
     * ]
     * ```
     * @see http://twig.sensiolabs.org/doc/recipes.html#customizing-the-syntax
     */
    public $lexerOptions = [];
    /**
75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
     * @var array namespaces and classes to import.
     *
     * Example:
     *
     * ```php
     * [
     *     'yii\bootstrap',
     *     'app\assets',
     *     \yii\bootstrap\NavBar::className(),
     * ]
     * ```
     */
    public $uses = [];
    /**
     * @var \Twig_Environment twig environment object that renders twig templates
90 91 92
     */
    public $twig;

93

94 95 96 97 98 99 100
    public function init()
    {
        $this->twig = new \Twig_Environment(null, array_merge([
            'cache' => Yii::getAlias($this->cachePath),
            'charset' => Yii::$app->charset,
        ], $this->options));

101 102
        $this->twig->setBaseTemplateClass('yii\twig\Template');

103 104 105 106 107 108 109 110 111 112 113 114 115 116 117
        // Adding custom globals (objects or static classes)
        if (!empty($this->globals)) {
            $this->addGlobals($this->globals);
        }

        // Adding custom functions
        if (!empty($this->functions)) {
            $this->addFunctions($this->functions);
        }

        // Adding custom filters
        if (!empty($this->filters)) {
            $this->addFilters($this->filters);
        }

118 119
        $this->addExtensions([new Extension($this->uses)]);

120 121 122 123 124 125
        // Adding custom extensions
        if (!empty($this->extensions)) {
            $this->addExtensions($this->extensions);
        }

        $this->twig->addGlobal('app', \Yii::$app);
126 127 128 129 130

        // Change lexer syntax (must be set after other settings)
        if (!empty($this->lexerOptions)) {
            $this->setLexerOptions($this->lexerOptions);
        }
131 132 133 134 135 136 137 138
    }

    /**
     * Renders a view file.
     *
     * This method is invoked by [[View]] whenever it tries to render a view.
     * Child classes must implement this method to render the given view file.
     *
139 140 141
     * @param View $view the view object used for rendering the file.
     * @param string $file the view file.
     * @param array $params the parameters to be passed to the view file.
142 143 144 145 146 147
     *
     * @return string the rendering result
     */
    public function render($view, $file, $params)
    {
        $this->twig->addGlobal('this', $view);
148
        $loader = new \Twig_Loader_Filesystem(dirname($file));
149 150 151 152 153
        $this->addAliases($loader, Yii::$aliases);
        $this->twig->setLoader($loader);

        return $this->twig->render(pathinfo($file, PATHINFO_BASENAME), $params);
    }
154

155 156 157 158 159 160 161 162 163 164 165 166
    /**
     * Adds aliases
     *
     * @param \Twig_Loader_Filesystem $loader
     * @param array $aliases
     */
    protected function addAliases($loader, $aliases)
    {
        foreach ($aliases as $alias => $path) {
            if (is_array($path)) {
                $this->addAliases($loader, $path);
            } elseif (is_string($path) && is_dir($path)) {
167 168
                $loader->addPath($path, substr($alias, 1));
            }
169
        }
170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210
    }

    /**
     * Adds global objects or static classes
     * @param array $globals @see self::$globals
     */
    public function addGlobals($globals)
    {
        foreach ($globals as $name => $value) {
            if (!is_object($value)) {
                $value = new ViewRendererStaticClassProxy($value);
            }
            $this->twig->addGlobal($name, $value);
        }
    }

    /**
     * Adds custom functions
     * @param array $functions @see self::$functions
     */
    public function addFunctions($functions)
    {
        $this->_addCustom('Function', $functions);
    }

    /**
     * Adds custom filters
     * @param array $filters @see self::$filters
     */
    public function addFilters($filters)
    {
        $this->_addCustom('Filter', $filters);
    }

    /**
     * Adds custom extensions
     * @param array $extensions @see self::$extensions
     */
    public function addExtensions($extensions)
    {
        foreach ($extensions as $extName) {
211
            $this->twig->addExtension(is_object($extName) ? $extName : new $extName());
212 213 214 215 216 217 218 219 220 221 222 223 224 225 226
        }
    }

    /**
     * Sets Twig lexer options to change templates syntax
     * @param array $options @see self::$lexerOptions
     */
    public function setLexerOptions($options)
    {
        $lexer = new \Twig_Lexer($this->twig, $options);
        $this->twig->setLexer($lexer);
    }

    /**
     * Adds custom function or filter
227 228
     * @param string $classType 'Function' or 'Filter'
     * @param array $elements Parameters of elements to add
229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255
     * @throws \Exception
     */
    private function _addCustom($classType, $elements)
    {
        $classFunction = 'Twig_' . $classType . '_Function';

        foreach ($elements as $name => $func) {
            $twigElement = null;

            switch ($func) {
                // Just a name of function
                case is_string($func):
                    $twigElement = new $classFunction($func);
                    break;
                // Name of function + options array
                case is_array($func) && is_string($func[0]) && isset($func[1]) && is_array($func[1]):
                    $twigElement = new $classFunction($func[0], $func[1]);
                    break;
            }

            if ($twigElement !== null) {
                $this->twig->{'add'.$classType}($name, $twigElement);
            } else {
                throw new \Exception("Incorrect options for \"$classType\" $name.");
            }
        }
    }
256
}