diff --git a/modules/cms/classes/Controller.php b/modules/cms/classes/Controller.php index d9c0b873a..6edf16b05 100644 --- a/modules/cms/classes/Controller.php +++ b/modules/cms/classes/Controller.php @@ -15,6 +15,7 @@ use BackendAuth; use Twig_Environment; use Controller as BaseController; use Cms\Twig\Loader as TwigLoader; +use Cms\Twig\DebugExtension; use Cms\Twig\Extension as CmsTwigExtension; use Cms\Classes\FileHelper as CmsFileHelper; use System\Models\RequestLog; @@ -262,11 +263,13 @@ class Controller extends BaseController */ protected function initTwigEnvironment() { - $this->loader = new TwigLoader(); + $this->loader = new TwigLoader; + + $isDebugMode = Config::get('app.debug', false); $options = [ 'auto_reload' => true, - 'debug' => Config::get('app.debug', false), + 'debug' => $isDebugMode, ]; if (!Config::get('cms.twigNoCache')) $options['cache'] = storage_path().'/twig'; @@ -274,6 +277,9 @@ class Controller extends BaseController $this->twig = new Twig_Environment($this->loader, $options); $this->twig->addExtension(new CmsTwigExtension($this)); $this->twig->addExtension(new SystemTwigExtension); + + if ($isDebugMode) + $this->twig->addExtension(new DebugExtension($this)); } /** diff --git a/modules/cms/twig/DebugExtension.php b/modules/cms/twig/DebugExtension.php new file mode 100644 index 000000000..7bcf4fb38 --- /dev/null +++ b/modules/cms/twig/DebugExtension.php @@ -0,0 +1,235 @@ +controller = $controller; + } + + /** + * Returns a list of global functions to add to the existing list. + * + * @return array An array of global functions + */ + public function getFunctions() + { + return array( + new Twig_SimpleFunction('dump', [$this, 'runDump'], array('is_safe' => ['html'], 'needs_context' => true, 'needs_environment' => true)), + ); + } + + public function runDump(Twig_Environment $env, $context) + { + if (!$env->isDebug()) { + return; + } + + ob_start(); + + $count = func_num_args(); + if (2 === $count) { + $this->controllerMode = true; + $vars = []; + foreach ($context as $key => $value) { + if (!$value instanceof Twig_Template) { + $vars[$key] = $value; + } + } + + $this->dump($vars); + } else { + for ($i = 2; $i < $count; $i++) { + $this->dump(func_get_arg($i)); + } + } + + return ob_get_clean(); + } + + /** + * Returns the name of the extension. + * + * @return string The extension name + */ + public function getName() + { + return 'debug'; + } + + /** + * Dump information about a variable + * + * @param mixed $variable Variable to dump + * @param string $caption Caption of the dump + * @return void + */ + public function dump($variables = null, $caption = 'Variable Inspector') + { + $info = []; + + if (!is_array($variables)) + $variables = [$variables]; + + $output = []; + $output[] = ''; + $output[] = $this->makeTableHeader($caption); + foreach ($variables as $key => $item) { + $output[] = $this->makeTableRow($key, $item); + } + $output[] = '
'; + + $html = implode(PHP_EOL, $output); + + print '
' . $html . '
'; + } + + protected function makeTableHeader($caption) + { + $output = []; + $output[] = ''; + $output[] = ''.$caption.''; + $output[] = ''; + return implode(PHP_EOL, $output); + } + + protected function makeTableRow($key, $variable) + { + $this->zebra = $this->zebra ? 0 : 1; + $css = $this->getDataCss(); + $output = []; + $output[] = ''; + + if ($this->controllerMode) + $output[] = '{{ '.$key.' }}'; + else + $output[] = ''.$key.''; + + $output[] = ''.gettype($variable).''; + $output[] = ''.$this->evalVarDesc($variable).''; + $output[] = ''; + return implode(PHP_EOL, $output); + } + + protected function evalVarDesc($variable) + { + switch (gettype($variable)) { + case 'object': + return $this->evalObjDesc($variable); + + case 'array': + return $this->evalArrDesc($variable); + + default: + return ''; + } + } + + protected function evalObjDesc($variable) + { + return ''.Str::getRealClass($variable).''; + } + + protected function evalArrDesc($variable) + { + $output = []; + foreach ($variable as $key => $value) { + $output[] = ''.$key.''; + } + + return implode(', ', $output); + } + + /** + * Get the CSS string for the output data + * + * @return string + */ + protected function getDataCss() + { + return $this->arrayToCss([ + 'padding' => '7px', + 'background-color' => $this->zebra ? '#D8D9DB' : '#FFF', + 'color' => '#405261', + ]); + } + + /** + * Get the CSS string for the output container + * + * @return string + */ + protected function getContainerCss() + { + return $this->arrayToCss([ + 'background-color' => '#F3F3F3', + 'border' => '1px solid #bbb', + 'border-radius' => '4px', + 'font-size' => '12px', + 'line-height' => '1.4em', + 'margin' => '30px', + 'padding' => '7px', + 'display' => 'inline-block', + ]); + } + + /** + * Get the CSS string for the output header + * + * @return string + */ + protected function getHeaderCss() + { + return $this->arrayToCss([ + 'font-size' => '18px', + 'font-weight' => 'normal', + 'margin' => '0', + 'padding' => '10px', + 'background-color' => '#7B8892', + 'color' => '#FFF', + ]); + } + + /** + * Convert a key/value pair array into a CSS string + * + * @param array $rules List of rules to process + * @return string + */ + protected function arrayToCss(array $rules) + { + $strings = []; + + foreach ($rules as $key => $value) { + $strings[] = $key . ': ' . $value; + } + + return join('; ', $strings); + } + +}