/src/View/BakeView.php
PHP | 273 lines | 134 code | 29 blank | 110 comment | 10 complexity | 028d08dd809344488c26ca653134d909 MD5 | raw file
- <?php
- namespace Bake\View;
- use Bit\Core\Configure;
- use Bit\Core\Traits\Conventions;
- use Bit\Core\Traits\InstanceConfig;
- use Bit\Event\EventManager;
- use Bit\Network\Request;
- use Bit\Network\Response;
- use Bit\Utility\Inflector;
- use Bit\View\View;
- class BakeView extends View
- {
- use Conventions;
- use InstanceConfig;
- protected $_viewExt = '.ptp';
- /**
- * Default class config
- *
- * This config is read when evaluating a template file.
- *
- * phpTagReplacements are applied to the contents of a bake template, to allow php tags
- * to be treated as plain text
- *
- * replacements are applied in order on the template contents before the template is evaluated
- * In order these:
- * swallow leading whitespace for <%- tags
- * swallow trailing whitespace for -%> tags
- * Add an extra newline to <%=, to counteract php automatically removing a newline
- * Replace remaining <=% with php short echo tags
- * Replace <% with php open tags
- * Replace %> with php close tags
- *
- * @var array
- */
- protected $_defaultConfig = [
- 'phpTagReplacements' => [
- '<?' => "<BitPHPBakeOpenTag",
- '?>' => "BitPHPBakeCloseTag>"
- ],
- 'replacements' => [
- '/\n[ \t]+<%-( |$)/' => "\n<% ",
- '/-%>/' => "?>",
- '/<%=(.*)\%>\n(.)/' => "<%=$1%>\n\n$2",
- '<%=' => '<?=',
- '<%' => '<?php',
- '%>' => '?>'
- ]
- ];
- /**
- * Path where bake's intermediary files are written.
- * Defaults to `TMP . 'bake' . DS`.
- *
- * @var string
- */
- protected $_tmpLocation;
- /**
- * Upon construction, append the plugin's template paths to the paths to check
- *
- * @param \Bit\Network\Request|null $request Request instance.
- * @param \Bit\Network\Response|null $response Response instance.
- * @param \Bit\Event\EventManager|null $eventManager Event manager instance.
- * @param array $viewOptions View options. See View::$_passedVars for list of
- * options which get set as class properties.
- */
- public function __construct(
- Request $request = null,
- Response $response = null,
- EventManager $eventManager = null,
- array $viewOptions = []
- ) {
- parent::__construct($request, $response, $eventManager, $viewOptions);
- $bakeTemplates = dirname(dirname(__FILE__)) . DS . 'Template' . DS;
- $paths = (array)Configure::read('App.paths.templates');
- if (!in_array($bakeTemplates, $paths)) {
- $paths[] = $bakeTemplates;
- Configure::write('App.paths.templates', $paths);
- }
- $this->_tmpLocation = TMP . 'bake' . DS;
- if (!file_exists($this->_tmpLocation)) {
- mkdir($this->_tmpLocation);
- }
- }
- /**
- * Renders view for given view file and layout.
- *
- * Render triggers helper callbacks, which are fired before and after the view are rendered,
- * as well as before and after the layout. The helper callbacks are called:
- *
- * - `beforeRender`
- * - `afterRender`
- *
- * View names can point to plugin views/layouts. Using the `Plugin.view` syntax
- * a plugin view/layout can be used instead of the app ones. If the chosen plugin is not found
- * the view will be located along the regular view path cascade.
- *
- * View can also be a template string, rather than the name of a view file
- *
- * @param string|null $view Name of view file to use, or a template string to render
- * @param string|null $layout Layout to use. Not used, for consistency with other views only
- * @return string|null Rendered content.
- * @throws \Bit\Core\Exception\Exception If there is an error in the view.
- */
- public function render($view = null, $layout = null)
- {
- $viewFileName = $this->_getViewFileName($view);
- $templateEventName = str_replace(
- ['.ctp', DS],
- ['', '.'],
- explode('Template' . DS . 'Bake' . DS, $viewFileName)[1]
- );
- $this->_currentType = static::TYPE_VIEW;
- $this->dispatchEvent('View.beforeRender', [$viewFileName]);
- $this->dispatchEvent('View.beforeRender.' . $templateEventName, [$viewFileName]);
- $this->Blocks->set('content', $this->_render($viewFileName));
- $this->dispatchEvent('View.afterRender', [$viewFileName]);
- $this->dispatchEvent('View.afterRender.' . $templateEventName, [$viewFileName]);
- if ($layout === null) {
- $layout = $this->layout;
- }
- if ($layout && $this->autoLayout) {
- $this->Blocks->set('content', $this->renderLayout('', $layout));
- }
- return $this->Blocks->get('content');
- }
- /**
- * Wrapper for creating and dispatching events.
- *
- * Use the Bake prefix for bake related view events
- *
- * @param string $name Name of the event.
- * @param array|null $data Any value you wish to be transported with this event to
- * it can be read by listeners.
- *
- * @param object|null $subject The object that this event applies to
- * ($this by default).
- *
- * @return \Bit\Event\Event
- */
- public function dispatchEvent($name, $data = null, $subject = null)
- {
- $name = preg_replace('/^View\./', 'Bake.', $name);
- return parent::dispatchEvent($name, $data, $subject);
- }
- /**
- * Sandbox method to evaluate a template / view script in.
- *
- * @param string $viewFile Filename of the view
- * @param array $dataForView Data to include in rendered view.
- * If empty the current View::$viewVars will be used.
- * @return string Rendered output
- */
- protected function _evaluate($viewFile, $dataForView)
- {
- $viewString = $this->_getViewFileContents($viewFile);
- $replacements = array_merge($this->config('phpTagReplacements') + $this->config('replacements'));
- foreach ($replacements as $find => $replace) {
- if ($this->_isRegex($find)) {
- $viewString = preg_replace($find, $replace, $viewString);
- } else {
- $viewString = str_replace($find, $replace, $viewString);
- }
- }
- $this->__viewFile = $this->_tmpLocation . Inflector::slug(preg_replace('@.*Template[/\\\\]@', '', $viewFile)) . '.php';
- file_put_contents($this->__viewFile, $viewString);
- unset($viewFile, $viewString, $replacements, $find, $replace);
- extract($dataForView);
- ob_start();
- include $this->__viewFile;
- $content = ob_get_clean();
- $unPhp = $this->config('phpTagReplacements');
- return str_replace(array_values($unPhp), array_keys($unPhp), $content);
- }
- /**
- * Get the contents of the template file
- *
- * @param string $filename A template filename
- * @return string Bake template to evaluate
- */
- protected function _getViewFileContents($filename)
- {
- return file_get_contents($filename);
- }
- /**
- * Return all possible paths to find view files in order
- *
- * @param string $plugin Optional plugin name to scan for view files.
- * @param bool $cached Set to false to force a refresh of view paths. Default true.
- * @return array paths
- */
- protected function _paths($plugin = null, $cached = true)
- {
- $paths = parent::_paths($plugin, false);
- foreach ($paths as &$path) {
- $path .= 'Bake' . DS;
- }
- return $paths;
- }
- /**
- * Check if a replacement pattern is a regex
- *
- * Use preg_match to detect invalid regexes
- *
- * @param string $maybeRegex a fixed string or a regex
- * @return bool
- */
- protected function _isRegex($maybeRegex)
- {
- // @codingStandardsIgnoreStart
- $isRegex = @preg_match($maybeRegex, '');
- // @codingStandardsIgnoreEnd
- return $isRegex !== false;
- }
- /**
- * Renders a layout. Returns output from _render(). Returns false on error.
- * Several variables are created for use in layout.
- *
- * @param string $content Content to render in a template, wrapped by the surrounding layout.
- * @param string|null $layout Layout name
- * @return mixed Rendered output, or false on error
- * @throws \Bit\Core\Exception\Exception if there is an error in the view.
- * @triggers View.beforeLayout $this, [$layoutFileName]
- * @triggers View.afterLayout $this, [$layoutFileName]
- */
- public function renderLayout($content, $layout = null)
- {
- $layoutFileName = $this->_getLayoutFileName($layout);
- if (empty($layoutFileName)) {
- return $this->Blocks->get('content');
- }
- if (!empty($content)) {
- $this->Blocks->set('content', $content);
- }
- $this->dispatchEvent('View.beforeLayout', [$layoutFileName]);
- $title = $this->Blocks->get('title');
- if ($title === '') {
- $title = Inflector::humanize($this->templatePath);
- $this->Blocks->set('title', $title);
- }
- $this->_currentType = static::TYPE_LAYOUT;
- $this->Blocks->set('content', $this->_render($layoutFileName));
- $this->dispatchEvent('View.afterLayout', [$layoutFileName]);
- return $this->Blocks->get('content');
- }
- }