Refactor Blade implementation (again), closes #1769 (#1777)

* Squash bugs, reorganize, etc.
* Use `get_body_class()` to apply filters on template data
* Use `PHP_INT_MAX` as priority for `template_include` filter
This commit is contained in:
QWp6t
2016-12-18 15:50:08 -08:00
committed by GitHub
parent 258d454bec
commit a3141c569e
8 changed files with 267 additions and 182 deletions

View File

@@ -0,0 +1,129 @@
<?php
namespace Roots\Sage\Template;
use Illuminate\Contracts\Container\Container as ContainerContract;
use Illuminate\Contracts\View\Factory as FactoryContract;
use Illuminate\View\Engines\CompilerEngine;
use Illuminate\View\Engines\EngineInterface;
use Illuminate\View\ViewFinderInterface;
/**
* Class BladeProvider
* @method \Illuminate\View\View file($file, $data = [], $mergeData = [])
* @method \Illuminate\View\View make($file, $data = [], $mergeData = [])
*/
class Blade
{
/** @var ContainerContract */
protected $app;
public function __construct(FactoryContract $env, ContainerContract $app)
{
$this->env = $env;
$this->app = $app;
}
/**
* Get the compiler
*
* @return \Illuminate\View\Compilers\BladeCompiler
*/
public function compiler()
{
static $engineResolver;
if (!$engineResolver) {
$engineResolver = $this->app->make('view.engine.resolver');
}
return $engineResolver->resolve('blade')->getCompiler();
}
/**
* @param string $view
* @param array $data
* @param array $mergeData
* @return string
*/
public function render($view, $data = [], $mergeData = [])
{
/** @var \Illuminate\Contracts\Filesystem\Filesystem $filesystem */
$filesystem = $this->app['files'];
return $this->{$filesystem->exists($view) ? 'file' : 'make'}($view, $data, $mergeData)->render();
}
/**
* @param string $file
* @param array $data
* @param array $mergeData
* @return string
*/
public function compiledPath($file, $data = [], $mergeData = [])
{
$rendered = $this->file($file, $data, $mergeData);
/** @var EngineInterface $engine */
$engine = $rendered->getEngine();
if (!($engine instanceof CompilerEngine)) {
// Using PhpEngine, so just return the file
return $file;
}
$compiler = $engine->getCompiler();
$compiledPath = $compiler->getCompiledPath($rendered->getPath());
if ($compiler->isExpired($compiledPath)) {
$compiler->compile($file);
}
return $compiledPath;
}
/**
* @param string $file
* @return string
*/
public function normalizeViewPath($file)
{
// Convert `\` to `/`
$view = str_replace('\\', '/', $file);
// Add namespace to path if necessary
$view = $this->applyNamespaceToPath($view);
// Remove unnecessary parts of the path
$view = str_replace(array_merge($this->app['config']['view.paths'], ['.blade.php', '.php']), '', $view);
// Remove superfluous and leading slashes
return ltrim(preg_replace('%//+%', '/', $view), '/');
}
/**
* Convert path to view namespace
* @param string $path
* @return string
*/
public function applyNamespaceToPath($path)
{
/** @var ViewFinderInterface $finder */
$finder = $this->app['view.finder'];
if (!method_exists($finder, 'getHints')) {
return $path;
}
$delimiter = $finder::HINT_PATH_DELIMITER;
$hints = $finder->getHints();
$view = array_reduce(array_keys($hints), function ($view, $namespace) use ($delimiter, $hints) {
return str_replace($hints[$namespace], $namespace.$delimiter, $view);
}, $path);
return preg_replace("%{$delimiter}[\\/]*%", $delimiter, $view);
}
/**
* Pass any method to the view Factory instance.
*
* @param string $method
* @param array $params
* @return mixed
*/
public function __call($method, $params)
{
return call_user_func_array([$this->env, $method], $params);
}
}

View File

@@ -2,108 +2,93 @@
namespace Roots\Sage\Template;
use Jenssegers\Blade\Blade;
use Illuminate\View\Engines\CompilerEngine;
use Illuminate\Container\Container;
use Illuminate\Contracts\Container\Container as ContainerContract;
use Illuminate\Events\Dispatcher;
use Illuminate\Filesystem\Filesystem;
use Illuminate\View\ViewServiceProvider;
class BladeProvider extends Blade
/**
* Class BladeProvider
*/
class BladeProvider extends ViewServiceProvider
{
/** @var Blade */
public $blade;
/** @var string */
protected $cachePath;
/**
* Constructor.
*
* @param array $viewPaths
* @param string $cachePath
* @param ContainerContract $container
* @param array $config
*/
public function __construct($viewPaths, $cachePath, ContainerContract $container = null)
public function __construct(ContainerContract $container = null, $config = [])
{
parent::__construct((array) $viewPaths, $cachePath, $container);
/** @noinspection PhpParamsInspection */
parent::__construct($container ?: new Container);
$this->app->bindIf('config', function () use ($config) {
return $config;
}, true);
}
/**
* Bind required instances for the service provider.
*/
public function register()
{
$this->registerFilesystem();
$this->registerEvents();
$this->registerEngineResolver();
$this->registerViewFinder();
$this->registerFactory();
return $this;
}
/**
* @param string $view
* @param array $data
* @param array $mergeData
* @return \Illuminate\View\View
* Register Filesystem
*/
public function make($view, $data = [], $mergeData = [])
public function registerFilesystem()
{
return $this->container['view']->make($this->normalizeViewPath($view), $data, $mergeData);
$this->app->bindIf('files', function () {
return new Filesystem;
}, true);
return $this;
}
/**
* @param string $view
* @param array $data
* @param array $mergeData
* @return string
* Register the events dispatcher
*/
public function render($view, $data = [], $mergeData = [])
public function registerEvents()
{
return $this->make($view, $data, $mergeData)->render();
$this->app->bindIf('events', function () {
return new Dispatcher;
}, true);
return $this;
}
/**
* @param string $file
* @param array $data
* @param array $mergeData
* @return string
*/
public function compiledPath($file, $data = [], $mergeData = [])
/** @inheritdoc */
public function registerEngineResolver()
{
$rendered = $this->make($file, $data, $mergeData);
$engine = $rendered->getEngine();
parent::registerEngineResolver();
return $this;
}
if (!($engine instanceof CompilerEngine)) {
// Using PhpEngine, so just return the file
return $file;
}
$compiler = $engine->getCompiler();
$compiledPath = $compiler->getCompiledPath($rendered->getPath());
if ($compiler->isExpired($compiledPath)) {
$compiler->compile($file);
}
return $compiledPath;
/** @inheritdoc */
public function registerFactory()
{
parent::registerFactory();
return $this;
}
/**
* Register the view finder implementation.
*
* @return void
*/
public function registerViewFinder()
{
$this->container->bind('view.finder', function ($app) {
$paths = $app['config']['view.paths'];
return new FileViewFinder($app['files'], $paths);
});
}
/**
* @param string $file
* @return string
*/
public function normalizeViewPath($file)
{
// Convert `\` to `/`
$view = str_replace('\\', '/', $file);
// Remove unnecessary parts of the path
$remove = array_merge($this->viewPaths, array_map('basename', $this->viewPaths), ['.blade.php', '.php']);
$view = str_replace($remove, '', $view);
// Remove leading slashes
$view = ltrim($view, '/');
// Convert `/` to `.`
return str_replace('/', '.', $view);
$this->app->bindIf('view.finder', function ($app) {
$config = $this->app['config'];
$paths = $config['view.paths'];
$namespaces = $config['view.namespaces'];
$finder = new FileViewFinder($app['files'], $paths);
array_map([$finder, 'addNamespace'], array_keys($namespaces), $namespaces);
return $finder;
}, true);
return $this;
}
}

View File

@@ -12,7 +12,7 @@ class FileViewFinder extends \Illuminate\View\FileViewFinder
* @param string $name
* @return array
*/
protected function getPossibleViewFiles($name)
public function getPossibleViewFiles($name)
{
$parts = explode(self::FALLBACK_PARTS_DELIMITER, $name);
$templates[] = array_shift($parts);