├── .gitignore ├── .php_cs ├── LICENSE ├── README.md ├── composer.json └── src ├── Autoloader.php ├── Compilers ├── BladeCompiler.php ├── Compiler.php ├── CompilerInterface.php └── Concerns │ ├── CompilesComments.php │ ├── CompilesComponents.php │ ├── CompilesConditionals.php │ ├── CompilesEchos.php │ ├── CompilesIncludes.php │ ├── CompilesInjections.php │ ├── CompilesLayouts.php │ ├── CompilesLoops.php │ ├── CompilesRawPhp.php │ ├── CompilesStacks.php │ └── CompilesTranslations.php ├── Concerns ├── ManagesComponents.php ├── ManagesLayouts.php ├── ManagesLoops.php └── ManagesStacks.php ├── Contracts ├── Arrayable.php └── Renderable.php ├── Engines ├── CompilerEngine.php ├── Engine.php ├── EngineInterface.php ├── EngineResolver.php ├── FileEngine.php └── PhpEngine.php ├── Factory.php ├── FileViewFinder.php ├── Filesystem.php ├── Support ├── Arr.php ├── HtmlString.php └── Str.php ├── View.php ├── ViewFinderInterface.php ├── ViewName.php └── helpers.php /.gitignore: -------------------------------------------------------------------------------- 1 | tests 2 | -------------------------------------------------------------------------------- /.php_cs: -------------------------------------------------------------------------------- 1 | files() 5 | ->in(__DIR__) 6 | ->ignoreDotFiles(true) 7 | ->ignoreVCS(true); 8 | 9 | $fixers = [ 10 | '-psr0', 11 | '-php_closing_tag', 12 | 'blankline_after_open_tag', 13 | 'concat_without_spaces', 14 | 'double_arrow_multiline_whitespaces', 15 | 'duplicate_semicolon', 16 | 'empty_return', 17 | 'extra_empty_lines', 18 | 'include', 19 | 'join_function', 20 | 'list_commas', 21 | 'multiline_array_trailing_comma', 22 | 'namespace_no_leading_whitespace', 23 | 'no_blank_lines_after_class_opening', 24 | 'no_empty_lines_after_phpdocs', 25 | 'object_operator', 26 | 'operators_spaces', 27 | 'phpdoc_indent', 28 | 'phpdoc_no_access', 29 | 'phpdoc_no_package', 30 | 'phpdoc_scalar', 31 | 'phpdoc_short_description', 32 | 'phpdoc_to_comment', 33 | 'phpdoc_trim', 34 | 'phpdoc_type_to_var', 35 | 'phpdoc_var_without_name', 36 | 'remove_leading_slash_use', 37 | 'remove_lines_between_uses', 38 | 'return', 39 | 'self_accessor', 40 | 'single_array_no_trailing_comma', 41 | 'single_blank_line_before_namespace', 42 | 'single_quote', 43 | 'spaces_before_semicolon', 44 | 'spaces_cast', 45 | 'standardize_not_equal', 46 | 'ternary_spaces', 47 | 'trim_array_spaces', 48 | 'unalign_equals', 49 | 'unary_operators_spaces', 50 | 'whitespacy_lines', 51 | 'multiline_spaces_before_semicolon', 52 | 'short_array_syntax', 53 | 'short_echo_tag', 54 | ]; 55 | 56 | return Symfony\CS\Config\Config::create() 57 | ->level(Symfony\CS\FixerInterface::PSR2_LEVEL) 58 | ->fixers($fixers) 59 | ->finder($finder) 60 | ->setUsingCache(true); 61 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015-2016 刘小乐 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Blade 2 | 3 | [【简体中文】](https://github.com/XiaoLer/blade/wiki) 4 | 5 | This is a view templating engine which is extracted from Laravel. It's independent without relying on Laravel's Container or any others. 6 | 7 | 8 | ### Installation 9 | 10 | With Composer, you just need to run 11 | 12 | ``` sh 13 | composer require xiaoler/blade 14 | ``` 15 | 16 | If you haven't use composer, you should add all the files in folder `src` to your project folder, 17 | and then `require` them in your code. 18 | 19 | 20 | ### Usage 21 | 22 | ```php 23 | directive('datetime', function($timestamp) { 44 | return preg_replace('/(\(\d+\))/', '', $timestamp); 45 | }); 46 | 47 | $resolver = new EngineResolver; 48 | $resolver->register('blade', function () use ($compiler) { 49 | return new CompilerEngine($compiler); 50 | }); 51 | 52 | // get an instance of factory 53 | $factory = new Factory($resolver, new FileViewFinder($file, $path)); 54 | 55 | // if your view file extension is not php or blade.php, use this to add it 56 | $factory->addExtension('tpl', 'blade'); 57 | 58 | // render the template file and echo it 59 | echo $factory->make('hello', ['a' => 1, 'b' => 2])->render(); 60 | ``` 61 | 62 | You can enjoy almost all the features of blade with this extension. 63 | However, remember that some of exclusive features are removed. 64 | 65 | You can't: 66 | 67 | - use `@inject` `@can` `@cannot` `@lang` in a template file 68 | - add any events or middleawares 69 | 70 | Documentation: [http://laravel.com/docs/5.4/blade](http://laravel.com/docs/5.3/blade) 71 | 72 | Thanks for Laravel and it authors. That is a great project. 73 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "xiaoler/blade", 3 | "description": "an independent version of laravel view template - blade", 4 | "license": "MIT", 5 | "homepage": "http://0x1.im", 6 | "support": { 7 | "issues": "https://github.com/XiaoLer/blade/issues", 8 | "source": "https://github.com/XiaoLer/blade" 9 | }, 10 | "authors": [ 11 | { 12 | "name": "Scholer Liu", 13 | "email": "scholer_l@live.com" 14 | } 15 | ], 16 | "require": { 17 | "php": ">=5.4.0" 18 | }, 19 | "autoload": { 20 | "files": [ 21 | "src/helpers.php" 22 | ], 23 | "psr-4": { 24 | "Xiaoler\\Blade\\": "src/" 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Autoloader.php: -------------------------------------------------------------------------------- 1 | setPath($path); 116 | } 117 | 118 | if (! is_null($this->cachePath)) { 119 | $contents = $this->compileString($this->files->get($this->getPath())); 120 | 121 | $this->files->put($this->getCompiledPath($this->getPath()), $contents); 122 | } 123 | } 124 | 125 | /** 126 | * Get the path currently being compiled. 127 | * 128 | * @return string 129 | */ 130 | public function getPath() 131 | { 132 | return $this->path; 133 | } 134 | 135 | /** 136 | * Set the path currently being compiled. 137 | * 138 | * @param string $path 139 | * @return void 140 | */ 141 | public function setPath($path) 142 | { 143 | $this->path = $path; 144 | } 145 | 146 | /** 147 | * Compile the given Blade template contents. 148 | * 149 | * @param string $value 150 | * @return string 151 | */ 152 | public function compileString($value) 153 | { 154 | $result = ''; 155 | 156 | if (strpos($value, '@verbatim') !== false) { 157 | $value = $this->storeVerbatimBlocks($value); 158 | } 159 | 160 | $this->footer = []; 161 | 162 | // Here we will loop through all of the tokens returned by the Zend lexer and 163 | // parse each one into the corresponding valid PHP. We will then have this 164 | // template as the correctly rendered PHP that can be rendered natively. 165 | foreach (token_get_all($value) as $token) { 166 | $result .= is_array($token) ? $this->parseToken($token) : $token; 167 | } 168 | 169 | if (! empty($this->verbatimBlocks)) { 170 | $result = $this->restoreVerbatimBlocks($result); 171 | } 172 | 173 | // If there are any footer lines that need to get added to a template we will 174 | // add them here at the end of the template. This gets used mainly for the 175 | // template inheritance via the extends keyword that should be appended. 176 | if (count($this->footer) > 0) { 177 | $result = $this->addFooters($result); 178 | } 179 | 180 | return $result; 181 | } 182 | 183 | /** 184 | * Store the verbatim blocks and replace them with a temporary placeholder. 185 | * 186 | * @param string $value 187 | * @return string 188 | */ 189 | protected function storeVerbatimBlocks($value) 190 | { 191 | return preg_replace_callback('/(?verbatimBlocks[] = $matches[1]; 193 | 194 | return $this->verbatimPlaceholder; 195 | }, $value); 196 | } 197 | 198 | /** 199 | * Replace the raw placeholders with the original code stored in the raw blocks. 200 | * 201 | * @param string $result 202 | * @return string 203 | */ 204 | protected function restoreVerbatimBlocks($result) 205 | { 206 | $result = preg_replace_callback('/'.preg_quote($this->verbatimPlaceholder).'/', function () { 207 | return array_shift($this->verbatimBlocks); 208 | }, $result); 209 | 210 | $this->verbatimBlocks = []; 211 | 212 | return $result; 213 | } 214 | 215 | /** 216 | * Add the stored footers onto the given content. 217 | * 218 | * @param string $result 219 | * @return string 220 | */ 221 | protected function addFooters($result) 222 | { 223 | return ltrim($result, PHP_EOL) 224 | .PHP_EOL.implode(PHP_EOL, array_reverse($this->footer)); 225 | } 226 | 227 | /** 228 | * Parse the tokens from the template. 229 | * 230 | * @param array $token 231 | * @return string 232 | */ 233 | protected function parseToken($token) 234 | { 235 | list($id, $content) = $token; 236 | 237 | if ($id == T_INLINE_HTML) { 238 | foreach ($this->compilers as $type) { 239 | $content = $this->{"compile{$type}"}($content); 240 | } 241 | } 242 | 243 | return $content; 244 | } 245 | 246 | /** 247 | * Execute the user defined extensions. 248 | * 249 | * @param string $value 250 | * @return string 251 | */ 252 | protected function compileExtensions($value) 253 | { 254 | foreach ($this->extensions as $compiler) { 255 | $value = call_user_func($compiler, $value, $this); 256 | } 257 | 258 | return $value; 259 | } 260 | 261 | /** 262 | * Compile Blade statements that start with "@". 263 | * 264 | * @param string $value 265 | * @return string 266 | */ 267 | protected function compileStatements($value) 268 | { 269 | return preg_replace_callback( 270 | '/\B@(@?\w+(?:::\w+)?)([ \t]*)(\( ( (?>[^()]+) | (?3) )* \))?/x', function ($match) { 271 | return $this->compileStatement($match); 272 | }, $value 273 | ); 274 | } 275 | 276 | /** 277 | * Compile a single Blade @ statement. 278 | * 279 | * @param array $match 280 | * @return string 281 | */ 282 | protected function compileStatement($match) 283 | { 284 | if (Str::contains($match[1], '@')) { 285 | $match[0] = isset($match[3]) ? $match[1].$match[3] : $match[1]; 286 | } elseif (isset($this->customDirectives[$match[1]])) { 287 | $match[0] = $this->callCustomDirective($match[1], Arr::get($match, 3)); 288 | } elseif (method_exists($this, $method = 'compile'.ucfirst($match[1]))) { 289 | $match[0] = $this->$method(Arr::get($match, 3)); 290 | } 291 | 292 | return isset($match[3]) ? $match[0] : $match[0].$match[2]; 293 | } 294 | 295 | /** 296 | * Call the given directive with the given value. 297 | * 298 | * @param string $name 299 | * @param string|null $value 300 | * @return string 301 | */ 302 | protected function callCustomDirective($name, $value) 303 | { 304 | if (Str::startsWith($value, '(') && Str::endsWith($value, ')')) { 305 | $value = Str::substr($value, 1, -1); 306 | } 307 | 308 | return call_user_func($this->customDirectives[$name], trim($value)); 309 | } 310 | 311 | /** 312 | * Strip the parentheses from the given expression. 313 | * 314 | * @param string $expression 315 | * @return string 316 | */ 317 | public function stripParentheses($expression) 318 | { 319 | if (Str::startsWith($expression, '(')) { 320 | $expression = substr($expression, 1, -1); 321 | } 322 | 323 | return $expression; 324 | } 325 | 326 | /** 327 | * Register a custom Blade compiler. 328 | * 329 | * @param callable $compiler 330 | * @return void 331 | */ 332 | public function extend(callable $compiler) 333 | { 334 | $this->extensions[] = $compiler; 335 | } 336 | 337 | /** 338 | * Get the extensions used by the compiler. 339 | * 340 | * @return array 341 | */ 342 | public function getExtensions() 343 | { 344 | return $this->extensions; 345 | } 346 | 347 | /** 348 | * Register a handler for custom directives. 349 | * 350 | * @param string $name 351 | * @param callable $handler 352 | * @return void 353 | */ 354 | public function directive($name, callable $handler) 355 | { 356 | $this->customDirectives[$name] = $handler; 357 | } 358 | 359 | /** 360 | * Get the list of custom directives. 361 | * 362 | * @return array 363 | */ 364 | public function getCustomDirectives() 365 | { 366 | return $this->customDirectives; 367 | } 368 | 369 | /** 370 | * Set the echo format to be used by the compiler. 371 | * 372 | * @param string $format 373 | * @return void 374 | */ 375 | public function setEchoFormat($format) 376 | { 377 | $this->echoFormat = $format; 378 | } 379 | } 380 | -------------------------------------------------------------------------------- /src/Compilers/Compiler.php: -------------------------------------------------------------------------------- 1 | files = $files; 40 | $this->cachePath = $cachePath; 41 | } 42 | 43 | /** 44 | * Get the path to the compiled version of a view. 45 | * 46 | * @param string $path 47 | * @return string 48 | */ 49 | public function getCompiledPath($path) 50 | { 51 | return $this->cachePath.'/'.sha1($path).'.php'; 52 | } 53 | 54 | /** 55 | * Determine if the view at the given path is expired. 56 | * 57 | * @param string $path 58 | * @return bool 59 | */ 60 | public function isExpired($path) 61 | { 62 | $compiled = $this->getCompiledPath($path); 63 | 64 | // If the compiled file doesn't exist we will indicate that the view is expired 65 | // so that it can be re-compiled. Else, we will verify the last modification 66 | // of the views is less than the modification times of the compiled views. 67 | if (! $this->files->exists($compiled)) { 68 | return true; 69 | } 70 | 71 | return $this->files->lastModified($path) >= 72 | $this->files->lastModified($compiled); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/Compilers/CompilerInterface.php: -------------------------------------------------------------------------------- 1 | contentTags[0], $this->contentTags[1]); 16 | 17 | return preg_replace($pattern, '', $value); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/Compilers/Concerns/CompilesComponents.php: -------------------------------------------------------------------------------- 1 | startComponent{$expression}; ?>"; 16 | } 17 | 18 | /** 19 | * Compile the end-component statements into valid PHP. 20 | * 21 | * @return string 22 | */ 23 | protected function compileEndComponent() 24 | { 25 | return 'renderComponent(); ?>'; 26 | } 27 | 28 | /** 29 | * Compile the slot statements into valid PHP. 30 | * 31 | * @param string $expression 32 | * @return string 33 | */ 34 | protected function compileSlot($expression) 35 | { 36 | return "slot{$expression}; ?>"; 37 | } 38 | 39 | /** 40 | * Compile the end-slot statements into valid PHP. 41 | * 42 | * @return string 43 | */ 44 | protected function compileEndSlot() 45 | { 46 | return 'endSlot(); ?>'; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/Compilers/Concerns/CompilesConditionals.php: -------------------------------------------------------------------------------- 1 | yieldContent{$expression}))): ?>"; 16 | } 17 | 18 | /** 19 | * Compile the if statements into valid PHP. 20 | * 21 | * @param string $expression 22 | * @return string 23 | */ 24 | protected function compileIf($expression) 25 | { 26 | return ""; 27 | } 28 | 29 | /** 30 | * Compile the unless statements into valid PHP. 31 | * 32 | * @param string $expression 33 | * @return string 34 | */ 35 | protected function compileUnless($expression) 36 | { 37 | return ""; 38 | } 39 | 40 | /** 41 | * Compile the else-if statements into valid PHP. 42 | * 43 | * @param string $expression 44 | * @return string 45 | */ 46 | protected function compileElseif($expression) 47 | { 48 | return ""; 49 | } 50 | 51 | /** 52 | * Compile the else statements into valid PHP. 53 | * 54 | * @return string 55 | */ 56 | protected function compileElse() 57 | { 58 | return ''; 59 | } 60 | 61 | /** 62 | * Compile the end-if statements into valid PHP. 63 | * 64 | * @return string 65 | */ 66 | protected function compileEndif() 67 | { 68 | return ''; 69 | } 70 | 71 | /** 72 | * Compile the end-unless statements into valid PHP. 73 | * 74 | * @return string 75 | */ 76 | protected function compileEndunless() 77 | { 78 | return ''; 79 | } 80 | 81 | /** 82 | * Compile the if-isset statements into valid PHP. 83 | * 84 | * @param string $expression 85 | * @return string 86 | */ 87 | protected function compileIsset($expression) 88 | { 89 | return ""; 90 | } 91 | 92 | /** 93 | * Compile the end-isset statements into valid PHP. 94 | * 95 | * @return string 96 | */ 97 | protected function compileEndIsset() 98 | { 99 | return ''; 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/Compilers/Concerns/CompilesEchos.php: -------------------------------------------------------------------------------- 1 | getEchoMethods() as $method) { 16 | $value = $this->$method($value); 17 | } 18 | 19 | return $value; 20 | } 21 | 22 | /** 23 | * Get the echo methods in the proper order for compilation. 24 | * 25 | * @return array 26 | */ 27 | protected function getEchoMethods() 28 | { 29 | return [ 30 | 'compileRawEchos', 31 | 'compileEscapedEchos', 32 | 'compileRegularEchos', 33 | ]; 34 | } 35 | 36 | /** 37 | * Compile the "raw" echo statements. 38 | * 39 | * @param string $value 40 | * @return string 41 | */ 42 | protected function compileRawEchos($value) 43 | { 44 | $pattern = sprintf('/(@)?%s\s*(.+?)\s*%s(\r?\n)?/s', $this->rawTags[0], $this->rawTags[1]); 45 | 46 | $callback = function ($matches) { 47 | $whitespace = empty($matches[3]) ? '' : $matches[3].$matches[3]; 48 | 49 | return $matches[1] ? substr($matches[0], 1) : "compileEchoDefaults($matches[2])}; ?>{$whitespace}"; 50 | }; 51 | 52 | return preg_replace_callback($pattern, $callback, $value); 53 | } 54 | 55 | /** 56 | * Compile the "regular" echo statements. 57 | * 58 | * @param string $value 59 | * @return string 60 | */ 61 | protected function compileRegularEchos($value) 62 | { 63 | $pattern = sprintf('/(@)?%s\s*(.+?)\s*%s(\r?\n)?/s', $this->contentTags[0], $this->contentTags[1]); 64 | 65 | $callback = function ($matches) { 66 | $whitespace = empty($matches[3]) ? '' : $matches[3].$matches[3]; 67 | 68 | $wrapped = sprintf($this->echoFormat, $this->compileEchoDefaults($matches[2])); 69 | 70 | return $matches[1] ? substr($matches[0], 1) : "{$whitespace}"; 71 | }; 72 | 73 | return preg_replace_callback($pattern, $callback, $value); 74 | } 75 | 76 | /** 77 | * Compile the escaped echo statements. 78 | * 79 | * @param string $value 80 | * @return string 81 | */ 82 | protected function compileEscapedEchos($value) 83 | { 84 | $pattern = sprintf('/(@)?%s\s*(.+?)\s*%s(\r?\n)?/s', $this->escapedTags[0], $this->escapedTags[1]); 85 | 86 | $callback = function ($matches) { 87 | $whitespace = empty($matches[3]) ? '' : $matches[3].$matches[3]; 88 | 89 | return $matches[1] ? $matches[0] : "compileEchoDefaults($matches[2])}); ?>{$whitespace}"; 90 | }; 91 | 92 | return preg_replace_callback($pattern, $callback, $value); 93 | } 94 | 95 | /** 96 | * Compile the default values for the echo statement. 97 | * 98 | * @param string $value 99 | * @return string 100 | */ 101 | public function compileEchoDefaults($value) 102 | { 103 | return preg_replace('/^(?=\$)(.+?)(?:\s+or\s+)(.+?)$/s', 'isset($1) ? $1 : $2', $value); 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/Compilers/Concerns/CompilesIncludes.php: -------------------------------------------------------------------------------- 1 | renderEach{$expression}; ?>"; 16 | } 17 | 18 | /** 19 | * Compile the include statements into valid PHP. 20 | * 21 | * @param string $expression 22 | * @return string 23 | */ 24 | protected function compileInclude($expression) 25 | { 26 | $expression = $this->stripParentheses($expression); 27 | 28 | return "make({$expression}, array_except(get_defined_vars(), array('__data', '__path')))->render(); ?>"; 29 | } 30 | 31 | /** 32 | * Compile the include-if statements into valid PHP. 33 | * 34 | * @param string $expression 35 | * @return string 36 | */ 37 | protected function compileIncludeIf($expression) 38 | { 39 | $expression = $this->stripParentheses($expression); 40 | 41 | return "exists({$expression})) echo \$__env->make({$expression}, array_except(get_defined_vars(), array('__data', '__path')))->render(); ?>"; 42 | } 43 | 44 | /** 45 | * Compile the include-when statements into valid PHP. 46 | * 47 | * @param string $expression 48 | * @return string 49 | */ 50 | protected function compileIncludeWhen($expression) 51 | { 52 | $expression = $this->stripParentheses($expression); 53 | 54 | return "renderWhen($expression, array_except(get_defined_vars(), array('__data', '__path'))); ?>"; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/Compilers/Concerns/CompilesInjections.php: -------------------------------------------------------------------------------- 1 | "; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Compilers/Concerns/CompilesLayouts.php: -------------------------------------------------------------------------------- 1 | stripParentheses($expression); 25 | 26 | $echo = "make({$expression}, array_except(get_defined_vars(), array('__data', '__path')))->render(); ?>"; 27 | 28 | $this->footer[] = $echo; 29 | 30 | return ''; 31 | } 32 | 33 | /** 34 | * Compile the section statements into valid PHP. 35 | * 36 | * @param string $expression 37 | * @return string 38 | */ 39 | protected function compileSection($expression) 40 | { 41 | $this->lastSection = trim($expression, "()'\" "); 42 | 43 | return "startSection{$expression}; ?>"; 44 | } 45 | 46 | /** 47 | * Replace the @parent directive to a placeholder. 48 | * 49 | * @return string 50 | */ 51 | protected function compileParent() 52 | { 53 | return ViewFactory::parentPlaceholder($this->lastSection ?: ''); 54 | } 55 | 56 | /** 57 | * Compile the yield statements into valid PHP. 58 | * 59 | * @param string $expression 60 | * @return string 61 | */ 62 | protected function compileYield($expression) 63 | { 64 | return "yieldContent{$expression}; ?>"; 65 | } 66 | 67 | /** 68 | * Compile the show statements into valid PHP. 69 | * 70 | * @return string 71 | */ 72 | protected function compileShow() 73 | { 74 | return 'yieldSection(); ?>'; 75 | } 76 | 77 | /** 78 | * Compile the append statements into valid PHP. 79 | * 80 | * @return string 81 | */ 82 | protected function compileAppend() 83 | { 84 | return 'appendSection(); ?>'; 85 | } 86 | 87 | /** 88 | * Compile the overwrite statements into valid PHP. 89 | * 90 | * @return string 91 | */ 92 | protected function compileOverwrite() 93 | { 94 | return 'stopSection(true); ?>'; 95 | } 96 | 97 | /** 98 | * Compile the stop statements into valid PHP. 99 | * 100 | * @return string 101 | */ 102 | protected function compileStop() 103 | { 104 | return 'stopSection(); ?>'; 105 | } 106 | 107 | /** 108 | * Compile the end-section statements into valid PHP. 109 | * 110 | * @return string 111 | */ 112 | protected function compileEndsection() 113 | { 114 | return 'stopSection(); ?>'; 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/Compilers/Concerns/CompilesLoops.php: -------------------------------------------------------------------------------- 1 | forElseCounter; 23 | 24 | preg_match('/\( *(.*) +as *(.*)\)$/is', $expression, $matches); 25 | 26 | $iteratee = trim($matches[1]); 27 | 28 | $iteration = trim($matches[2]); 29 | 30 | $initLoop = "\$__currentLoopData = {$iteratee}; \$__env->addLoop(\$__currentLoopData);"; 31 | 32 | $iterateLoop = '$__env->incrementLoopIndices(); $loop = $__env->getLastLoop();'; 33 | 34 | return ""; 35 | } 36 | 37 | /** 38 | * Compile the for-else-empty and empty statements into valid PHP. 39 | * 40 | * @param string $expression 41 | * @return string 42 | */ 43 | protected function compileEmpty($expression) 44 | { 45 | if ($expression) { 46 | return ""; 47 | } 48 | 49 | $empty = '$__empty_'.$this->forElseCounter--; 50 | 51 | return "popLoop(); \$loop = \$__env->getLastLoop(); if ({$empty}): ?>"; 52 | } 53 | 54 | /** 55 | * Compile the end-for-else statements into valid PHP. 56 | * 57 | * @return string 58 | */ 59 | protected function compileEndforelse() 60 | { 61 | return ''; 62 | } 63 | 64 | /** 65 | * Compile the end-empty statements into valid PHP. 66 | * 67 | * @return string 68 | */ 69 | protected function compileEndEmpty() 70 | { 71 | return ''; 72 | } 73 | 74 | /** 75 | * Compile the for statements into valid PHP. 76 | * 77 | * @param string $expression 78 | * @return string 79 | */ 80 | protected function compileFor($expression) 81 | { 82 | return ""; 83 | } 84 | 85 | /** 86 | * Compile the for-each statements into valid PHP. 87 | * 88 | * @param string $expression 89 | * @return string 90 | */ 91 | protected function compileForeach($expression) 92 | { 93 | preg_match('/\( *(.*) +as *(.*)\)$/is', $expression, $matches); 94 | 95 | $iteratee = trim($matches[1]); 96 | 97 | $iteration = trim($matches[2]); 98 | 99 | $initLoop = "\$__currentLoopData = {$iteratee}; \$__env->addLoop(\$__currentLoopData);"; 100 | 101 | $iterateLoop = '$__env->incrementLoopIndices(); $loop = $__env->getLastLoop();'; 102 | 103 | return ""; 104 | } 105 | 106 | /** 107 | * Compile the break statements into valid PHP. 108 | * 109 | * @param string $expression 110 | * @return string 111 | */ 112 | protected function compileBreak($expression) 113 | { 114 | if ($expression) { 115 | preg_match('/\(\s*(-?\d+)\s*\)$/', $expression, $matches); 116 | 117 | return $matches ? '' : ""; 118 | } 119 | 120 | return ''; 121 | } 122 | 123 | /** 124 | * Compile the continue statements into valid PHP. 125 | * 126 | * @param string $expression 127 | * @return string 128 | */ 129 | protected function compileContinue($expression) 130 | { 131 | if ($expression) { 132 | preg_match('/\(\s*(-?\d+)\s*\)$/', $expression, $matches); 133 | 134 | return $matches ? '' : ""; 135 | } 136 | 137 | return ''; 138 | } 139 | 140 | /** 141 | * Compile the end-for statements into valid PHP. 142 | * 143 | * @return string 144 | */ 145 | protected function compileEndfor() 146 | { 147 | return ''; 148 | } 149 | 150 | /** 151 | * Compile the end-for-each statements into valid PHP. 152 | * 153 | * @return string 154 | */ 155 | protected function compileEndforeach() 156 | { 157 | return 'popLoop(); $loop = $__env->getLastLoop(); ?>'; 158 | } 159 | 160 | /** 161 | * Compile the while statements into valid PHP. 162 | * 163 | * @param string $expression 164 | * @return string 165 | */ 166 | protected function compileWhile($expression) 167 | { 168 | return ""; 169 | } 170 | 171 | /** 172 | * Compile the end-while statements into valid PHP. 173 | * 174 | * @return string 175 | */ 176 | protected function compileEndwhile() 177 | { 178 | return ''; 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /src/Compilers/Concerns/CompilesRawPhp.php: -------------------------------------------------------------------------------- 1 | " : ''; 26 | } 27 | 28 | /** 29 | * Compile the unset statements into valid PHP. 30 | * 31 | * @param string $expression 32 | * @return string 33 | */ 34 | protected function compileUnset($expression) 35 | { 36 | return ""; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/Compilers/Concerns/CompilesStacks.php: -------------------------------------------------------------------------------- 1 | yieldPushContent{$expression}; ?>"; 16 | } 17 | 18 | /** 19 | * Compile the push statements into valid PHP. 20 | * 21 | * @param string $expression 22 | * @return string 23 | */ 24 | protected function compilePush($expression) 25 | { 26 | return "startPush{$expression}; ?>"; 27 | } 28 | 29 | /** 30 | * Compile the end-push statements into valid PHP. 31 | * 32 | * @return string 33 | */ 34 | protected function compileEndpush() 35 | { 36 | return 'stopPush(); ?>'; 37 | } 38 | 39 | /** 40 | * Compile the prepend statements into valid PHP. 41 | * 42 | * @param string $expression 43 | * @return string 44 | */ 45 | protected function compilePrepend($expression) 46 | { 47 | return "startPrepend{$expression}; ?>"; 48 | } 49 | 50 | /** 51 | * Compile the end-prepend statements into valid PHP. 52 | * 53 | * @return string 54 | */ 55 | protected function compileEndprepend() 56 | { 57 | return 'stopPrepend(); ?>'; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/Compilers/Concerns/CompilesTranslations.php: -------------------------------------------------------------------------------- 1 | startTranslation(); ?>'; 17 | } elseif ($expression[1] === '[') { 18 | return "startTranslation{$expression}; ?>"; 19 | } else { 20 | return "getFromJson{$expression}; ?>"; 21 | } 22 | } 23 | 24 | /** 25 | * Compile the end-lang statements into valid PHP. 26 | * 27 | * @return string 28 | */ 29 | protected function compileEndlang() 30 | { 31 | return 'renderTranslation(); ?>'; 32 | } 33 | 34 | /** 35 | * Compile the choice statements into valid PHP. 36 | * 37 | * @param string $expression 38 | * @return string 39 | */ 40 | protected function compileChoice($expression) 41 | { 42 | return "choice{$expression}; ?>"; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/Concerns/ManagesComponents.php: -------------------------------------------------------------------------------- 1 | componentStack[] = $name; 49 | 50 | $this->componentData[$this->currentComponent()] = $data; 51 | 52 | $this->slots[$this->currentComponent()] = []; 53 | } 54 | } 55 | 56 | /** 57 | * Render the current component. 58 | * 59 | * @return string 60 | */ 61 | public function renderComponent() 62 | { 63 | $name = array_pop($this->componentStack); 64 | 65 | return $this->make($name, $this->componentData($name))->render(); 66 | } 67 | 68 | /** 69 | * Get the data for the given component. 70 | * 71 | * @param string $name 72 | * @return array 73 | */ 74 | protected function componentData($name) 75 | { 76 | return array_merge( 77 | $this->componentData[count($this->componentStack)], 78 | ['slot' => new HtmlString(trim(ob_get_clean()))], 79 | $this->slots[count($this->componentStack)] 80 | ); 81 | } 82 | 83 | /** 84 | * Start the slot rendering process. 85 | * 86 | * @param string $name 87 | * @param string|null $content 88 | * @return void 89 | */ 90 | public function slot($name, $content = null) 91 | { 92 | if (count(func_get_args()) == 2) { 93 | $this->slots[$this->currentComponent()][$name] = $content; 94 | } else { 95 | if (ob_start()) { 96 | $this->slots[$this->currentComponent()][$name] = ''; 97 | 98 | $this->slotStack[$this->currentComponent()][] = $name; 99 | } 100 | } 101 | } 102 | 103 | /** 104 | * Save the slot content for rendering. 105 | * 106 | * @return void 107 | */ 108 | public function endSlot() 109 | { 110 | Arr::last($this->componentStack); 111 | 112 | $currentSlot = array_pop( 113 | $this->slotStack[$this->currentComponent()] 114 | ); 115 | 116 | $this->slots[$this->currentComponent()] 117 | [$currentSlot] = new HtmlString(trim(ob_get_clean())); 118 | } 119 | 120 | /** 121 | * Get the index for the current component. 122 | * 123 | * @return int 124 | */ 125 | protected function currentComponent() 126 | { 127 | return count($this->componentStack) - 1; 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /src/Concerns/ManagesLayouts.php: -------------------------------------------------------------------------------- 1 | sectionStack[] = $section; 42 | } 43 | } else { 44 | $this->extendSection($section, e($content)); 45 | } 46 | } 47 | 48 | /** 49 | * Inject inline content into a section. 50 | * 51 | * @param string $section 52 | * @param string $content 53 | * @return void 54 | */ 55 | public function inject($section, $content) 56 | { 57 | return $this->startSection($section, $content); 58 | } 59 | 60 | /** 61 | * Stop injecting content into a section and return its contents. 62 | * 63 | * @return string 64 | */ 65 | public function yieldSection() 66 | { 67 | if (empty($this->sectionStack)) { 68 | return ''; 69 | } 70 | 71 | return $this->yieldContent($this->stopSection()); 72 | } 73 | 74 | /** 75 | * Stop injecting content into a section. 76 | * 77 | * @param bool $overwrite 78 | * @return string 79 | * @throws \InvalidArgumentException 80 | */ 81 | public function stopSection($overwrite = false) 82 | { 83 | if (empty($this->sectionStack)) { 84 | throw new InvalidArgumentException('Cannot end a section without first starting one.'); 85 | } 86 | 87 | $last = array_pop($this->sectionStack); 88 | 89 | if ($overwrite) { 90 | $this->sections[$last] = ob_get_clean(); 91 | } else { 92 | $this->extendSection($last, ob_get_clean()); 93 | } 94 | 95 | return $last; 96 | } 97 | 98 | /** 99 | * Stop injecting content into a section and append it. 100 | * 101 | * @return string 102 | * @throws \InvalidArgumentException 103 | */ 104 | public function appendSection() 105 | { 106 | if (empty($this->sectionStack)) { 107 | throw new InvalidArgumentException('Cannot end a section without first starting one.'); 108 | } 109 | 110 | $last = array_pop($this->sectionStack); 111 | 112 | if (isset($this->sections[$last])) { 113 | $this->sections[$last] .= ob_get_clean(); 114 | } else { 115 | $this->sections[$last] = ob_get_clean(); 116 | } 117 | 118 | return $last; 119 | } 120 | 121 | /** 122 | * Append content to a given section. 123 | * 124 | * @param string $section 125 | * @param string $content 126 | * @return void 127 | */ 128 | protected function extendSection($section, $content) 129 | { 130 | if (isset($this->sections[$section])) { 131 | $content = str_replace(static::parentPlaceholder($section), $content, $this->sections[$section]); 132 | } 133 | 134 | $this->sections[$section] = $content; 135 | } 136 | 137 | /** 138 | * Get the string contents of a section. 139 | * 140 | * @param string $section 141 | * @param string $default 142 | * @return string 143 | */ 144 | public function yieldContent($section, $default = '') 145 | { 146 | $sectionContent = e($default); 147 | 148 | if (isset($this->sections[$section])) { 149 | $sectionContent = $this->sections[$section]; 150 | } 151 | 152 | $sectionContent = str_replace('@@parent', '--parent--holder--', $sectionContent); 153 | 154 | return str_replace( 155 | '--parent--holder--', '@parent', str_replace(static::parentPlaceholder($section), '', $sectionContent) 156 | ); 157 | } 158 | 159 | /** 160 | * Get the parent placeholder for the current request. 161 | * 162 | * @param string $section 163 | * @return string 164 | */ 165 | public static function parentPlaceholder($section = '') 166 | { 167 | if (! isset(static::$parentPlaceholder[$section])) { 168 | static::$parentPlaceholder[$section] = '##parent-placeholder-'.sha1($section).'##'; 169 | } 170 | 171 | return static::$parentPlaceholder[$section]; 172 | } 173 | 174 | /** 175 | * Check if section exists. 176 | * 177 | * @param string $name 178 | * @return bool 179 | */ 180 | public function hasSection($name) 181 | { 182 | return array_key_exists($name, $this->sections); 183 | } 184 | 185 | /** 186 | * Get the contents of a section. 187 | * 188 | * @param string $name 189 | * @param string $default 190 | * @return mixed 191 | */ 192 | public function getSection($name, $default = null) 193 | { 194 | return isset($this->getSections()[$name]) ? $this->getSections()[$name] : $default; 195 | } 196 | 197 | /** 198 | * Get the entire array of sections. 199 | * 200 | * @return array 201 | */ 202 | public function getSections() 203 | { 204 | return $this->sections; 205 | } 206 | 207 | /** 208 | * Flush all of the sections. 209 | * 210 | * @return void 211 | */ 212 | public function flushSections() 213 | { 214 | $this->sections = []; 215 | $this->sectionStack = []; 216 | } 217 | } 218 | -------------------------------------------------------------------------------- /src/Concerns/ManagesLoops.php: -------------------------------------------------------------------------------- 1 | loopsStack); 28 | 29 | $this->loopsStack[] = [ 30 | 'iteration' => 0, 31 | 'index' => 0, 32 | 'remaining' => isset($length) ? $length : null, 33 | 'count' => $length, 34 | 'first' => true, 35 | 'last' => isset($length) ? $length == 1 : null, 36 | 'depth' => count($this->loopsStack) + 1, 37 | 'parent' => $parent ? (object) $parent : null, 38 | ]; 39 | } 40 | 41 | /** 42 | * Increment the top loop's indices. 43 | * 44 | * @return void 45 | */ 46 | public function incrementLoopIndices() 47 | { 48 | $loop = $this->loopsStack[$index = count($this->loopsStack) - 1]; 49 | 50 | $this->loopsStack[$index] = array_merge($this->loopsStack[$index], [ 51 | 'iteration' => $loop['iteration'] + 1, 52 | 'index' => $loop['iteration'], 53 | 'first' => $loop['iteration'] == 0, 54 | 'remaining' => isset($loop['count']) ? $loop['remaining'] - 1 : null, 55 | 'last' => isset($loop['count']) ? $loop['iteration'] == $loop['count'] - 1 : null, 56 | ]); 57 | } 58 | 59 | /** 60 | * Pop a loop from the top of the loop stack. 61 | * 62 | * @return void 63 | */ 64 | public function popLoop() 65 | { 66 | array_pop($this->loopsStack); 67 | } 68 | 69 | /** 70 | * Get an instance of the last loop in the stack. 71 | * 72 | * @return \stdClass|null 73 | */ 74 | public function getLastLoop() 75 | { 76 | if ($last = Arr::last($this->loopsStack)) { 77 | return (object) $last; 78 | } 79 | } 80 | 81 | /** 82 | * Get the entire loop stack. 83 | * 84 | * @return array 85 | */ 86 | public function getLoopStack() 87 | { 88 | return $this->loopsStack; 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/Concerns/ManagesStacks.php: -------------------------------------------------------------------------------- 1 | pushStack[] = $section; 42 | } 43 | } else { 44 | $this->extendPush($section, $content); 45 | } 46 | } 47 | 48 | /** 49 | * Stop injecting content into a push section. 50 | * 51 | * @return string 52 | * @throws \InvalidArgumentException 53 | */ 54 | public function stopPush() 55 | { 56 | if (empty($this->pushStack)) { 57 | throw new InvalidArgumentException('Cannot end a push stack without first starting one.'); 58 | } 59 | 60 | return tap(array_pop($this->pushStack), function ($last) { 61 | $this->extendPush($last, ob_get_clean()); 62 | }); 63 | } 64 | 65 | /** 66 | * Append content to a given push section. 67 | * 68 | * @param string $section 69 | * @param string $content 70 | * @return void 71 | */ 72 | protected function extendPush($section, $content) 73 | { 74 | if (! isset($this->pushes[$section])) { 75 | $this->pushes[$section] = []; 76 | } 77 | 78 | if (! isset($this->pushes[$section][$this->renderCount])) { 79 | $this->pushes[$section][$this->renderCount] = $content; 80 | } else { 81 | $this->pushes[$section][$this->renderCount] .= $content; 82 | } 83 | } 84 | 85 | /** 86 | * Start prepending content into a push section. 87 | * 88 | * @param string $section 89 | * @param string $content 90 | * @return void 91 | */ 92 | public function startPrepend($section, $content = '') 93 | { 94 | if ($content === '') { 95 | if (ob_start()) { 96 | $this->pushStack[] = $section; 97 | } 98 | } else { 99 | $this->extendPrepend($section, $content); 100 | } 101 | } 102 | 103 | /** 104 | * Stop prepending content into a push section. 105 | * 106 | * @return string 107 | * @throws \InvalidArgumentException 108 | */ 109 | public function stopPrepend() 110 | { 111 | if (empty($this->pushStack)) { 112 | throw new InvalidArgumentException('Cannot end a prepend operation without first starting one.'); 113 | } 114 | 115 | return tap(array_pop($this->pushStack), function ($last) { 116 | $this->extendPrepend($last, ob_get_clean()); 117 | }); 118 | } 119 | 120 | /** 121 | * Prepend content to a given stack. 122 | * 123 | * @param string $section 124 | * @param string $content 125 | * @return void 126 | */ 127 | protected function extendPrepend($section, $content) 128 | { 129 | if (! isset($this->prepends[$section])) { 130 | $this->prepends[$section] = []; 131 | } 132 | 133 | if (! isset($this->prepends[$section][$this->renderCount])) { 134 | $this->prepends[$section][$this->renderCount] = $content; 135 | } else { 136 | $this->prepends[$section][$this->renderCount] = $content.$this->prepends[$section][$this->renderCount]; 137 | } 138 | } 139 | 140 | /** 141 | * Get the string contents of a push section. 142 | * 143 | * @param string $section 144 | * @param string $default 145 | * @return string 146 | */ 147 | public function yieldPushContent($section, $default = '') 148 | { 149 | if (! isset($this->pushes[$section]) && ! isset($this->prepends[$section])) { 150 | return $default; 151 | } 152 | 153 | $output = ''; 154 | 155 | if (isset($this->prepends[$section])) { 156 | $output .= implode(array_reverse($this->prepends[$section])); 157 | } 158 | 159 | if (isset($this->pushes[$section])) { 160 | $output .= implode($this->pushes[$section]); 161 | } 162 | 163 | return $output; 164 | } 165 | 166 | /** 167 | * Flush all of the stacks. 168 | * 169 | * @return void 170 | */ 171 | public function flushStacks() 172 | { 173 | $this->pushes = []; 174 | $this->prepends = []; 175 | $this->pushStack = []; 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /src/Contracts/Arrayable.php: -------------------------------------------------------------------------------- 1 | compiler = $compiler; 35 | } 36 | 37 | /** 38 | * Get the evaluated contents of the view. 39 | * 40 | * @param string $path 41 | * @param array $data 42 | * @return string 43 | */ 44 | public function get($path, array $data = []) 45 | { 46 | $this->lastCompiled[] = $path; 47 | 48 | // If this given view has expired, which means it has simply been edited since 49 | // it was last compiled, we will re-compile the views so we can evaluate a 50 | // fresh copy of the view. We'll pass the compiler the path of the view. 51 | if ($this->compiler->isExpired($path)) { 52 | $this->compiler->compile($path); 53 | } 54 | 55 | $compiled = $this->compiler->getCompiledPath($path); 56 | 57 | // Once we have the path to the compiled file, we will evaluate the paths with 58 | // typical PHP just like any other templates. We also keep a stack of views 59 | // which have been rendered for right exception messages to be generated. 60 | $results = $this->evaluatePath($compiled, $data); 61 | 62 | array_pop($this->lastCompiled); 63 | 64 | return $results; 65 | } 66 | 67 | /** 68 | * Handle a view exception. 69 | * 70 | * @param \Exception $e 71 | * @param int $obLevel 72 | * @return void 73 | * 74 | * @throws \Exception 75 | */ 76 | protected function handleViewException(Exception $e, $obLevel) 77 | { 78 | $e = new ErrorException($this->getMessage($e), 0, 1, $e->getFile(), $e->getLine(), $e); 79 | 80 | parent::handleViewException($e, $obLevel); 81 | } 82 | 83 | /** 84 | * Get the exception message for an exception. 85 | * 86 | * @param \Exception $e 87 | * @return string 88 | */ 89 | protected function getMessage(Exception $e) 90 | { 91 | return $e->getMessage().' (View: '.realpath(Arr::last($this->lastCompiled)).')'; 92 | } 93 | 94 | /** 95 | * Get the compiler implementation. 96 | * 97 | * @return \Xiaoler\Blade\Compilers\CompilerInterface 98 | */ 99 | public function getCompiler() 100 | { 101 | return $this->compiler; 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/Engines/Engine.php: -------------------------------------------------------------------------------- 1 | lastRendered; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Engines/EngineInterface.php: -------------------------------------------------------------------------------- 1 | resolved[$engine]); 36 | 37 | $this->resolvers[$engine] = $resolver; 38 | } 39 | 40 | /** 41 | * Resolver an engine instance by name. 42 | * 43 | * @param string $engine 44 | * @return \Xiaoler\Blade\Engines\EngineInterface 45 | * @throws \InvalidArgumentException 46 | */ 47 | public function resolve($engine) 48 | { 49 | if (isset($this->resolved[$engine])) { 50 | return $this->resolved[$engine]; 51 | } 52 | 53 | if (isset($this->resolvers[$engine])) { 54 | return $this->resolved[$engine] = call_user_func($this->resolvers[$engine]); 55 | } 56 | 57 | throw new InvalidArgumentException("Engine $engine not found."); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/Engines/FileEngine.php: -------------------------------------------------------------------------------- 1 | evaluatePath($path, $data); 21 | } 22 | 23 | /** 24 | * Get the evaluated contents of the view at the given path. 25 | * 26 | * @param string $__path 27 | * @param array $__data 28 | * @return string 29 | */ 30 | protected function evaluatePath($__path, $__data) 31 | { 32 | $obLevel = ob_get_level(); 33 | 34 | ob_start(); 35 | 36 | extract($__data, EXTR_SKIP); 37 | 38 | // We'll evaluate the contents of the view inside a try/catch block so we can 39 | // flush out any stray output that might get out before an error occurs or 40 | // an exception is thrown. This prevents any partial views from leaking. 41 | try { 42 | include $__path; 43 | } catch (Exception $e) { 44 | $this->handleViewException($e, $obLevel); 45 | } catch (Throwable $e) { 46 | $this->handleViewException(new FatalThrowableError($e), $obLevel); 47 | } 48 | 49 | return ltrim(ob_get_clean()); 50 | } 51 | 52 | /** 53 | * Handle a view exception. 54 | * 55 | * @param \Exception $e 56 | * @param int $obLevel 57 | * @return void 58 | * 59 | * @throws \Exception 60 | */ 61 | protected function handleViewException(Exception $e, $obLevel) 62 | { 63 | while (ob_get_level() > $obLevel) { 64 | ob_end_clean(); 65 | } 66 | 67 | throw $e; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/Factory.php: -------------------------------------------------------------------------------- 1 | 'blade', 46 | 'php' => 'php', 47 | 'css' => 'file', 48 | ]; 49 | 50 | /** 51 | * The number of active rendering operations. 52 | * 53 | * @var int 54 | */ 55 | protected $renderCount = 0; 56 | 57 | /** 58 | * Create a new view factory instance. 59 | * 60 | * @param \Xiaoler\Blade\Engines\EngineResolver $engines 61 | * @param \Xiaoler\Blade\ViewFinderInterface $finder 62 | * @return void 63 | */ 64 | public function __construct(EngineResolver $engines, ViewFinderInterface $finder) 65 | { 66 | $this->finder = $finder; 67 | $this->engines = $engines; 68 | 69 | $this->share('__env', $this); 70 | } 71 | 72 | /** 73 | * Get the evaluated view contents for the given view. 74 | * 75 | * @param string $path 76 | * @param array $data 77 | * @param array $mergeData 78 | * @return \Xiaoler\Blade\View 79 | */ 80 | public function file($path, $data = [], $mergeData = []) 81 | { 82 | $data = array_merge($mergeData, $this->parseData($data)); 83 | 84 | return $this->viewInstance($path, $path, $data); 85 | } 86 | 87 | /** 88 | * Get the evaluated view contents for the given view. 89 | * 90 | * @param string $view 91 | * @param array $data 92 | * @param array $mergeData 93 | * @return \Xiaoler\Blade\View 94 | */ 95 | public function make($view, $data = [], $mergeData = []) 96 | { 97 | $path = $this->finder->find( 98 | $view = $this->normalizeName($view) 99 | ); 100 | 101 | // Next, we will create the view instance and call the view creator for the view 102 | // which can set any data, etc. Then we will return the view instance back to 103 | // the caller for rendering or performing other view manipulations on this. 104 | $data = array_merge($mergeData, $this->parseData($data)); 105 | 106 | return $this->viewInstance($view, $path, $data); 107 | } 108 | 109 | /** 110 | * Get the rendered content of the view based on a given condition. 111 | * 112 | * @param bool $condition 113 | * @param string $view 114 | * @param array $data 115 | * @param array $mergeData 116 | * @return string 117 | */ 118 | public function renderWhen($condition, $view, $data = [], $mergeData = []) 119 | { 120 | if (! $condition) { 121 | return ''; 122 | } 123 | 124 | return $this->make($view, $this->parseData($data), $mergeData)->render(); 125 | } 126 | 127 | /** 128 | * Get the rendered contents of a partial from a loop. 129 | * 130 | * @param string $view 131 | * @param array $data 132 | * @param string $iterator 133 | * @param string $empty 134 | * @return string 135 | */ 136 | public function renderEach($view, $data, $iterator, $empty = 'raw|') 137 | { 138 | $result = ''; 139 | 140 | // If is actually data in the array, we will loop through the data and append 141 | // an instance of the partial view to the final result HTML passing in the 142 | // iterated value of this data array, allowing the views to access them. 143 | if (count($data) > 0) { 144 | foreach ($data as $key => $value) { 145 | $result .= $this->make( 146 | $view, ['key' => $key, $iterator => $value] 147 | )->render(); 148 | } 149 | } 150 | 151 | // If there is no data in the array, we will render the contents of the empty 152 | // view. Alternatively, the "empty view" could be a raw string that begins 153 | // with "raw|" for convenience and to let this know that it is a string. 154 | else { 155 | $result = Str::startsWith($empty, 'raw|') 156 | ? substr($empty, 4) 157 | : $this->make($empty)->render(); 158 | } 159 | 160 | return $result; 161 | } 162 | 163 | /** 164 | * Normalize a view name. 165 | * 166 | * @param string $name 167 | * @return string 168 | */ 169 | protected function normalizeName($name) 170 | { 171 | return ViewName::normalize($name); 172 | } 173 | 174 | /** 175 | * Parse the given data into a raw array. 176 | * 177 | * @param mixed $data 178 | * @return array 179 | */ 180 | protected function parseData($data) 181 | { 182 | return $data instanceof Arrayable ? $data->toArray() : $data; 183 | } 184 | 185 | /** 186 | * Create a new view instance from the given arguments. 187 | * 188 | * @param string $view 189 | * @param string $path 190 | * @param array $data 191 | * @return \Xiaoler\Blade\View 192 | */ 193 | protected function viewInstance($view, $path, $data) 194 | { 195 | return new View($this, $this->getEngineFromPath($path), $view, $path, $data); 196 | } 197 | 198 | /** 199 | * Determine if a given view exists. 200 | * 201 | * @param string $view 202 | * @return bool 203 | */ 204 | public function exists($view) 205 | { 206 | try { 207 | $this->finder->find($view); 208 | } catch (InvalidArgumentException $e) { 209 | return false; 210 | } 211 | 212 | return true; 213 | } 214 | 215 | /** 216 | * Get the appropriate view engine for the given path. 217 | * 218 | * @param string $path 219 | * @return \Xiaoler\Blade\Engines\EngineInterface 220 | * 221 | * @throws \InvalidArgumentException 222 | */ 223 | public function getEngineFromPath($path) 224 | { 225 | if (! $extension = $this->getExtension($path)) { 226 | throw new InvalidArgumentException("Unrecognized extension in file: $path"); 227 | } 228 | 229 | $engine = $this->extensions[$extension]; 230 | 231 | return $this->engines->resolve($engine); 232 | } 233 | 234 | /** 235 | * Get the extension used by the view file. 236 | * 237 | * @param string $path 238 | * @return string 239 | */ 240 | protected function getExtension($path) 241 | { 242 | $extensions = array_keys($this->extensions); 243 | 244 | return Arr::first($extensions, function ($value) use ($path) { 245 | return Str::endsWith($path, '.'.$value); 246 | }); 247 | } 248 | 249 | /** 250 | * Add a piece of shared data to the environment. 251 | * 252 | * @param array|string $key 253 | * @param mixed $value 254 | * @return mixed 255 | */ 256 | public function share($key, $value = null) 257 | { 258 | $keys = is_array($key) ? $key : [$key => $value]; 259 | 260 | foreach ($keys as $key => $value) { 261 | $this->shared[$key] = $value; 262 | } 263 | 264 | return $value; 265 | } 266 | 267 | /** 268 | * Increment the rendering counter. 269 | * 270 | * @return void 271 | */ 272 | public function incrementRender() 273 | { 274 | $this->renderCount++; 275 | } 276 | 277 | /** 278 | * Decrement the rendering counter. 279 | * 280 | * @return void 281 | */ 282 | public function decrementRender() 283 | { 284 | $this->renderCount--; 285 | } 286 | 287 | /** 288 | * Check if there are no active render operations. 289 | * 290 | * @return bool 291 | */ 292 | public function doneRendering() 293 | { 294 | return $this->renderCount == 0; 295 | } 296 | 297 | /** 298 | * Add a location to the array of view locations. 299 | * 300 | * @param string $location 301 | * @return void 302 | */ 303 | public function addLocation($location) 304 | { 305 | $this->finder->addLocation($location); 306 | } 307 | 308 | /** 309 | * Add a new namespace to the loader. 310 | * 311 | * @param string $namespace 312 | * @param string|array $hints 313 | * @return $this 314 | */ 315 | public function addNamespace($namespace, $hints) 316 | { 317 | $this->finder->addNamespace($namespace, $hints); 318 | 319 | return $this; 320 | } 321 | 322 | /** 323 | * Prepend a new namespace to the loader. 324 | * 325 | * @param string $namespace 326 | * @param string|array $hints 327 | * @return $this 328 | */ 329 | public function prependNamespace($namespace, $hints) 330 | { 331 | $this->finder->prependNamespace($namespace, $hints); 332 | 333 | return $this; 334 | } 335 | 336 | /** 337 | * Replace the namespace hints for the given namespace. 338 | * 339 | * @param string $namespace 340 | * @param string|array $hints 341 | * @return $this 342 | */ 343 | public function replaceNamespace($namespace, $hints) 344 | { 345 | $this->finder->replaceNamespace($namespace, $hints); 346 | 347 | return $this; 348 | } 349 | 350 | /** 351 | * Register a valid view extension and its engine. 352 | * 353 | * @param string $extension 354 | * @param string $engine 355 | * @param \Closure $resolver 356 | * @return void 357 | */ 358 | public function addExtension($extension, $engine, $resolver = null) 359 | { 360 | $this->finder->addExtension($extension); 361 | 362 | if (isset($resolver)) { 363 | $this->engines->register($engine, $resolver); 364 | } 365 | 366 | unset($this->extensions[$extension]); 367 | 368 | $this->extensions = array_merge([$extension => $engine], $this->extensions); 369 | } 370 | 371 | /** 372 | * Flush all of the factory state like sections and stacks. 373 | * 374 | * @return void 375 | */ 376 | public function flushState() 377 | { 378 | $this->renderCount = 0; 379 | 380 | $this->flushSections(); 381 | $this->flushStacks(); 382 | } 383 | 384 | /** 385 | * Flush all of the section contents if done rendering. 386 | * 387 | * @return void 388 | */ 389 | public function flushStateIfDoneRendering() 390 | { 391 | if ($this->doneRendering()) { 392 | $this->flushState(); 393 | } 394 | } 395 | 396 | /** 397 | * Get the extension to engine bindings. 398 | * 399 | * @return array 400 | */ 401 | public function getExtensions() 402 | { 403 | return $this->extensions; 404 | } 405 | 406 | /** 407 | * Get the engine resolver instance. 408 | * 409 | * @return \Xiaoler\Blade\Engines\EngineResolver 410 | */ 411 | public function getEngineResolver() 412 | { 413 | return $this->engines; 414 | } 415 | 416 | /** 417 | * Get the view finder instance. 418 | * 419 | * @return \Xiaoler\Blade\ViewFinderInterface 420 | */ 421 | public function getFinder() 422 | { 423 | return $this->finder; 424 | } 425 | 426 | /** 427 | * Set the view finder instance. 428 | * 429 | * @param \Xiaoler\Blade\ViewFinderInterface $finder 430 | * @return void 431 | */ 432 | public function setFinder(ViewFinderInterface $finder) 433 | { 434 | $this->finder = $finder; 435 | } 436 | 437 | /** 438 | * Flush the cache of views located by the finder. 439 | * 440 | * @return void 441 | */ 442 | public function flushFinderCache() 443 | { 444 | $this->getFinder()->flush(); 445 | } 446 | 447 | /** 448 | * Get an item from the shared data. 449 | * 450 | * @param string $key 451 | * @param mixed $default 452 | * @return mixed 453 | */ 454 | public function shared($key, $default = null) 455 | { 456 | return Arr::get($this->shared, $key, $default); 457 | } 458 | 459 | /** 460 | * Get all of the shared data for the environment. 461 | * 462 | * @return array 463 | */ 464 | public function getShared() 465 | { 466 | return $this->shared; 467 | } 468 | } 469 | -------------------------------------------------------------------------------- /src/FileViewFinder.php: -------------------------------------------------------------------------------- 1 | files = $files; 56 | $this->paths = $paths; 57 | 58 | if (isset($extensions)) { 59 | $this->extensions = $extensions; 60 | } 61 | } 62 | 63 | /** 64 | * Get the fully qualified location of the view. 65 | * 66 | * @param string $name 67 | * @return string 68 | */ 69 | public function find($name) 70 | { 71 | if (isset($this->views[$name])) { 72 | return $this->views[$name]; 73 | } 74 | 75 | if ($this->hasHintInformation($name = trim($name))) { 76 | return $this->views[$name] = $this->findNamespacedView($name); 77 | } 78 | 79 | return $this->views[$name] = $this->findInPaths($name, $this->paths); 80 | } 81 | 82 | /** 83 | * Get the path to a template with a named path. 84 | * 85 | * @param string $name 86 | * @return string 87 | */ 88 | protected function findNamespacedView($name) 89 | { 90 | list($namespace, $view) = $this->parseNamespaceSegments($name); 91 | 92 | return $this->findInPaths($view, $this->hints[$namespace]); 93 | } 94 | 95 | /** 96 | * Get the segments of a template with a named path. 97 | * 98 | * @param string $name 99 | * @return array 100 | * 101 | * @throws \InvalidArgumentException 102 | */ 103 | protected function parseNamespaceSegments($name) 104 | { 105 | $segments = explode(static::HINT_PATH_DELIMITER, $name); 106 | 107 | if (count($segments) != 2) { 108 | throw new InvalidArgumentException("View [$name] has an invalid name."); 109 | } 110 | 111 | if (! isset($this->hints[$segments[0]])) { 112 | throw new InvalidArgumentException("No hint path defined for [{$segments[0]}]."); 113 | } 114 | 115 | return $segments; 116 | } 117 | 118 | /** 119 | * Find the given view in the list of paths. 120 | * 121 | * @param string $name 122 | * @param array $paths 123 | * @return string 124 | * 125 | * @throws \InvalidArgumentException 126 | */ 127 | protected function findInPaths($name, $paths) 128 | { 129 | foreach ((array) $paths as $path) { 130 | foreach ($this->getPossibleViewFiles($name) as $file) { 131 | if ($this->files->exists($viewPath = $path.'/'.$file)) { 132 | return $viewPath; 133 | } 134 | } 135 | } 136 | 137 | throw new InvalidArgumentException("View [$name] not found."); 138 | } 139 | 140 | /** 141 | * Get an array of possible view files. 142 | * 143 | * @param string $name 144 | * @return array 145 | */ 146 | protected function getPossibleViewFiles($name) 147 | { 148 | return array_map(function ($extension) use ($name) { 149 | return str_replace('.', '/', $name).'.'.$extension; 150 | }, $this->extensions); 151 | } 152 | 153 | /** 154 | * Add a location to the finder. 155 | * 156 | * @param string $location 157 | * @return void 158 | */ 159 | public function addLocation($location) 160 | { 161 | $this->paths[] = $location; 162 | } 163 | 164 | /** 165 | * Prepend a location to the finder. 166 | * 167 | * @param string $location 168 | * @return void 169 | */ 170 | public function prependLocation($location) 171 | { 172 | array_unshift($this->paths, $location); 173 | } 174 | 175 | /** 176 | * Add a namespace hint to the finder. 177 | * 178 | * @param string $namespace 179 | * @param string|array $hints 180 | * @return void 181 | */ 182 | public function addNamespace($namespace, $hints) 183 | { 184 | $hints = (array) $hints; 185 | 186 | if (isset($this->hints[$namespace])) { 187 | $hints = array_merge($this->hints[$namespace], $hints); 188 | } 189 | 190 | $this->hints[$namespace] = $hints; 191 | } 192 | 193 | /** 194 | * Prepend a namespace hint to the finder. 195 | * 196 | * @param string $namespace 197 | * @param string|array $hints 198 | * @return void 199 | */ 200 | public function prependNamespace($namespace, $hints) 201 | { 202 | $hints = (array) $hints; 203 | 204 | if (isset($this->hints[$namespace])) { 205 | $hints = array_merge($hints, $this->hints[$namespace]); 206 | } 207 | 208 | $this->hints[$namespace] = $hints; 209 | } 210 | 211 | /** 212 | * Replace the namespace hints for the given namespace. 213 | * 214 | * @param string $namespace 215 | * @param string|array $hints 216 | * @return void 217 | */ 218 | public function replaceNamespace($namespace, $hints) 219 | { 220 | $this->hints[$namespace] = (array) $hints; 221 | } 222 | 223 | /** 224 | * Register an extension with the view finder. 225 | * 226 | * @param string $extension 227 | * @return void 228 | */ 229 | public function addExtension($extension) 230 | { 231 | if (($index = array_search($extension, $this->extensions)) !== false) { 232 | unset($this->extensions[$index]); 233 | } 234 | 235 | array_unshift($this->extensions, $extension); 236 | } 237 | 238 | /** 239 | * Returns whether or not the view name has any hint information. 240 | * 241 | * @param string $name 242 | * @return bool 243 | */ 244 | public function hasHintInformation($name) 245 | { 246 | return strpos($name, static::HINT_PATH_DELIMITER) > 0; 247 | } 248 | 249 | /** 250 | * Flush the cache of located views. 251 | * 252 | * @return void 253 | */ 254 | public function flush() 255 | { 256 | $this->views = []; 257 | } 258 | 259 | /** 260 | * Get the filesystem instance. 261 | * 262 | * @return \Xiaoler\Blade\Filesystem 263 | */ 264 | public function getFilesystem() 265 | { 266 | return $this->files; 267 | } 268 | 269 | /** 270 | * Get the active view paths. 271 | * 272 | * @return array 273 | */ 274 | public function getPaths() 275 | { 276 | return $this->paths; 277 | } 278 | 279 | /** 280 | * Get the namespace to file path hints. 281 | * 282 | * @return array 283 | */ 284 | public function getHints() 285 | { 286 | return $this->hints; 287 | } 288 | 289 | /** 290 | * Get registered extensions. 291 | * 292 | * @return array 293 | */ 294 | public function getExtensions() 295 | { 296 | return $this->extensions; 297 | } 298 | } 299 | -------------------------------------------------------------------------------- /src/Filesystem.php: -------------------------------------------------------------------------------- 1 | isFile($path)) { 31 | return file_get_contents($path); 32 | } 33 | 34 | throw new Exception("File does not exist at path {$path}"); 35 | } 36 | 37 | /** 38 | * Write the contents of a file. 39 | * 40 | * @param string $path 41 | * @param string $contents 42 | * @param bool $lock 43 | * @return int 44 | */ 45 | public function put($path, $contents, $lock = false) 46 | { 47 | return file_put_contents($path, $contents, $lock ? LOCK_EX : 0); 48 | } 49 | 50 | /** 51 | * Get the file's last modification time. 52 | * 53 | * @param string $path 54 | * @return int 55 | */ 56 | public function lastModified($path) 57 | { 58 | return filemtime($path); 59 | } 60 | 61 | /** 62 | * Determine if the given path is a file. 63 | * 64 | * @param string $file 65 | * @return bool 66 | */ 67 | public function isFile($file) 68 | { 69 | return is_file($file); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/Support/Arr.php: -------------------------------------------------------------------------------- 1 | offsetExists($key); 32 | } 33 | 34 | return array_key_exists($key, $array); 35 | } 36 | 37 | /** 38 | * Return the first element in an array passing a given truth test. 39 | * 40 | * @param array $array 41 | * @param callable|null $callback 42 | * @param mixed $default 43 | * @return mixed 44 | */ 45 | public static function first($array, callable $callback = null, $default = null) 46 | { 47 | if (is_null($callback)) { 48 | if (empty($array)) { 49 | return value($default); 50 | } 51 | 52 | foreach ($array as $item) { 53 | return $item; 54 | } 55 | } 56 | 57 | foreach ($array as $key => $value) { 58 | if (call_user_func($callback, $value, $key)) { 59 | return $value; 60 | } 61 | } 62 | 63 | return value($default); 64 | } 65 | 66 | /** 67 | * Return the last element in an array passing a given truth test. 68 | * 69 | * @param array $array 70 | * @param callable|null $callback 71 | * @param mixed $default 72 | * @return mixed 73 | */ 74 | public static function last($array, callable $callback = null, $default = null) 75 | { 76 | if (is_null($callback)) { 77 | return empty($array) ? value($default) : end($array); 78 | } 79 | 80 | return static::first(array_reverse($array, true), $callback, $default); 81 | } 82 | 83 | /** 84 | * Get an item from an array using "dot" notation. 85 | * 86 | * @param \ArrayAccess|array $array 87 | * @param string $key 88 | * @param mixed $default 89 | * @return mixed 90 | */ 91 | public static function get($array, $key, $default = null) 92 | { 93 | if (! static::accessible($array)) { 94 | return value($default); 95 | } 96 | 97 | if (is_null($key)) { 98 | return $array; 99 | } 100 | 101 | if (static::exists($array, $key)) { 102 | return $array[$key]; 103 | } 104 | 105 | foreach (explode('.', $key) as $segment) { 106 | if (static::accessible($array) && static::exists($array, $segment)) { 107 | $array = $array[$segment]; 108 | } else { 109 | return value($default); 110 | } 111 | } 112 | 113 | return $array; 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /src/Support/HtmlString.php: -------------------------------------------------------------------------------- 1 | html = $html; 23 | } 24 | 25 | /** 26 | * Get the HTML string. 27 | * 28 | * @return string 29 | */ 30 | public function toHtml() 31 | { 32 | return $this->html; 33 | } 34 | 35 | /** 36 | * Get the HTML string. 37 | * 38 | * @return string 39 | */ 40 | public function __toString() 41 | { 42 | return $this->toHtml(); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/Support/Str.php: -------------------------------------------------------------------------------- 1 | view = $view; 64 | $this->path = $path; 65 | $this->engine = $engine; 66 | $this->factory = $factory; 67 | 68 | $this->data = $data instanceof Arrayable ? $data->toArray() : (array) $data; 69 | } 70 | 71 | /** 72 | * Get the string contents of the view. 73 | * 74 | * @param callable|null $callback 75 | * @return string 76 | * 77 | * @throws \Throwable 78 | */ 79 | public function render(callable $callback = null) 80 | { 81 | try { 82 | $contents = $this->renderContents(); 83 | 84 | $response = isset($callback) ? call_user_func($callback, $this, $contents) : null; 85 | 86 | // Once we have the contents of the view, we will flush the sections if we are 87 | // done rendering all views so that there is nothing left hanging over when 88 | // another view gets rendered in the future by the application developer. 89 | $this->factory->flushStateIfDoneRendering(); 90 | 91 | return ! is_null($response) ? $response : $contents; 92 | } catch (Exception $e) { 93 | $this->factory->flushState(); 94 | 95 | throw $e; 96 | } catch (Throwable $e) { 97 | $this->factory->flushState(); 98 | 99 | throw $e; 100 | } 101 | } 102 | 103 | /** 104 | * Get the contents of the view instance. 105 | * 106 | * @return string 107 | */ 108 | protected function renderContents() 109 | { 110 | // We will keep track of the amount of views being rendered so we can flush 111 | // the section after the complete rendering operation is done. This will 112 | // clear out the sections for any separate views that may be rendered. 113 | $this->factory->incrementRender(); 114 | 115 | $contents = $this->getContents(); 116 | 117 | // Once we've finished rendering the view, we'll decrement the render count 118 | // so that each sections get flushed out next time a view is created and 119 | // no old sections are staying around in the memory of an environment. 120 | $this->factory->decrementRender(); 121 | 122 | return $contents; 123 | } 124 | 125 | /** 126 | * Get the evaluated contents of the view. 127 | * 128 | * @return string 129 | */ 130 | protected function getContents() 131 | { 132 | return $this->engine->get($this->path, $this->gatherData()); 133 | } 134 | 135 | /** 136 | * Get the data bound to the view instance. 137 | * 138 | * @return array 139 | */ 140 | protected function gatherData() 141 | { 142 | $data = array_merge($this->factory->getShared(), $this->data); 143 | 144 | foreach ($data as $key => $value) { 145 | if ($value instanceof Renderable) { 146 | $data[$key] = $value->render(); 147 | } 148 | } 149 | 150 | return $data; 151 | } 152 | 153 | /** 154 | * Get the sections of the rendered view. 155 | * 156 | * @return array 157 | */ 158 | public function renderSections() 159 | { 160 | return $this->render(function () { 161 | return $this->factory->getSections(); 162 | }); 163 | } 164 | 165 | /** 166 | * Add a piece of data to the view. 167 | * 168 | * @param string|array $key 169 | * @param mixed $value 170 | * @return $this 171 | */ 172 | public function with($key, $value = null) 173 | { 174 | if (is_array($key)) { 175 | $this->data = array_merge($this->data, $key); 176 | } else { 177 | $this->data[$key] = $value; 178 | } 179 | 180 | return $this; 181 | } 182 | 183 | /** 184 | * Add a view instance to the view data. 185 | * 186 | * @param string $key 187 | * @param string $view 188 | * @param array $data 189 | * @return $this 190 | */ 191 | public function nest($key, $view, array $data = []) 192 | { 193 | return $this->with($key, $this->factory->make($view, $data)); 194 | } 195 | 196 | /** 197 | * Get the name of the view. 198 | * 199 | * @return string 200 | */ 201 | public function name() 202 | { 203 | return $this->getName(); 204 | } 205 | 206 | /** 207 | * Get the name of the view. 208 | * 209 | * @return string 210 | */ 211 | public function getName() 212 | { 213 | return $this->view; 214 | } 215 | 216 | /** 217 | * Get the array of view data. 218 | * 219 | * @return array 220 | */ 221 | public function getData() 222 | { 223 | return $this->data; 224 | } 225 | 226 | /** 227 | * Get the path to the view file. 228 | * 229 | * @return string 230 | */ 231 | public function getPath() 232 | { 233 | return $this->path; 234 | } 235 | 236 | /** 237 | * Set the path to the view. 238 | * 239 | * @param string $path 240 | * @return void 241 | */ 242 | public function setPath($path) 243 | { 244 | $this->path = $path; 245 | } 246 | 247 | /** 248 | * Get the view factory instance. 249 | * 250 | * @return \Xiaoler\Blade\Factory 251 | */ 252 | public function getFactory() 253 | { 254 | return $this->factory; 255 | } 256 | 257 | /** 258 | * Get the view's rendering engine. 259 | * 260 | * @return \Xiaoler\Blade\Engines\EngineInterface 261 | */ 262 | public function getEngine() 263 | { 264 | return $this->engine; 265 | } 266 | 267 | /** 268 | * Determine if a piece of data is bound. 269 | * 270 | * @param string $key 271 | * @return bool 272 | */ 273 | public function offsetExists($key) 274 | { 275 | return array_key_exists($key, $this->data); 276 | } 277 | 278 | /** 279 | * Get a piece of bound data to the view. 280 | * 281 | * @param string $key 282 | * @return mixed 283 | */ 284 | public function offsetGet($key) 285 | { 286 | return $this->data[$key]; 287 | } 288 | 289 | /** 290 | * Set a piece of data on the view. 291 | * 292 | * @param string $key 293 | * @param mixed $value 294 | * @return void 295 | */ 296 | public function offsetSet($key, $value) 297 | { 298 | $this->with($key, $value); 299 | } 300 | 301 | /** 302 | * Unset a piece of data from the view. 303 | * 304 | * @param string $key 305 | * @return void 306 | */ 307 | public function offsetUnset($key) 308 | { 309 | unset($this->data[$key]); 310 | } 311 | 312 | /** 313 | * Get a piece of data from the view. 314 | * 315 | * @param string $key 316 | * @return mixed 317 | */ 318 | public function &__get($key) 319 | { 320 | return $this->data[$key]; 321 | } 322 | 323 | /** 324 | * Set a piece of data on the view. 325 | * 326 | * @param string $key 327 | * @param mixed $value 328 | * @return void 329 | */ 330 | public function __set($key, $value) 331 | { 332 | $this->with($key, $value); 333 | } 334 | 335 | /** 336 | * Check if a piece of data is bound to the view. 337 | * 338 | * @param string $key 339 | * @return bool 340 | */ 341 | public function __isset($key) 342 | { 343 | return isset($this->data[$key]); 344 | } 345 | 346 | /** 347 | * Remove a piece of bound data from the view. 348 | * 349 | * @param string $key 350 | * @return bool 351 | */ 352 | public function __unset($key) 353 | { 354 | unset($this->data[$key]); 355 | } 356 | 357 | /** 358 | * Dynamically bind parameters to the view. 359 | * 360 | * @param string $method 361 | * @param array $parameters 362 | * @return \Xiaoler\Blade\View 363 | * 364 | * @throws \BadMethodCallException 365 | */ 366 | public function __call($method, $parameters) 367 | { 368 | if (! Str::startsWith($method, 'with')) { 369 | throw new BadMethodCallException("Method [$method] does not exist on view."); 370 | } 371 | 372 | return $this->with(Str::snake(substr($method, 4)), $parameters[0]); 373 | } 374 | 375 | /** 376 | * Get the string contents of the view. 377 | * 378 | * @return string 379 | */ 380 | public function __toString() 381 | { 382 | return $this->render(); 383 | } 384 | } 385 | -------------------------------------------------------------------------------- /src/ViewFinderInterface.php: -------------------------------------------------------------------------------- 1 |