duplicated the code helper code to the Twig bundle
The code has been duplicated and not moved for BC reasons. This code has been duplicated in the Twig bundle to be able to decouple the web profiler and the exception templates.
This commit is contained in:
parent
f3c644061a
commit
56fe8d1ffd
@ -11,26 +11,33 @@
|
||||
|
||||
namespace Symfony\Bundle\TwigBundle\Extension;
|
||||
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
if (!defined('ENT_SUBSTITUTE')) {
|
||||
define('ENT_SUBSTITUTE', 8);
|
||||
}
|
||||
|
||||
/**
|
||||
* Twig extension for Symfony code helper
|
||||
*
|
||||
* Twig extension relate to PHP code and used by the profiler and the default exception templates.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class CodeExtension extends \Twig_Extension
|
||||
{
|
||||
private $container;
|
||||
private $fileLinkFormat;
|
||||
private $rootDir;
|
||||
private $charset;
|
||||
|
||||
/**
|
||||
* Constructor of Twig Extension to provide functions for code formatting
|
||||
* Constructor.
|
||||
*
|
||||
* @param ContainerInterface $container A ContainerInterface instance
|
||||
* @param string $fileLinkFormat The format for links to source files
|
||||
* @param string $rootDir The project root directory
|
||||
* @param string $charset The charset
|
||||
*/
|
||||
public function __construct(ContainerInterface $container)
|
||||
public function __construct($fileLinkFormat, $rootDir, $charset)
|
||||
{
|
||||
$this->container = $container;
|
||||
$this->fileLinkFormat = empty($fileLinkFormat) ? ini_get('xdebug.file_link_format') : $fileLinkFormat;
|
||||
$this->rootDir = str_replace('\\', '/', $rootDir).'/';
|
||||
$this->charset = $charset;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -52,46 +59,174 @@ class CodeExtension extends \Twig_Extension
|
||||
|
||||
public function abbrClass($class)
|
||||
{
|
||||
return $this->container->get('templating.helper.code')->abbrClass($class);
|
||||
$parts = explode('\\', $class);
|
||||
$short = array_pop($parts);
|
||||
|
||||
return sprintf("<abbr title=\"%s\">%s</abbr>", $class, $short);
|
||||
}
|
||||
|
||||
public function abbrMethod($method)
|
||||
{
|
||||
return $this->container->get('templating.helper.code')->abbrMethod($method);
|
||||
if (false !== strpos($method, '::')) {
|
||||
list($class, $method) = explode('::', $method, 2);
|
||||
$result = sprintf("%s::%s()", $this->abbrClass($class), $method);
|
||||
} elseif ('Closure' === $method) {
|
||||
$result = sprintf("<abbr title=\"%s\">%s</abbr>", $method, $method);
|
||||
} else {
|
||||
$result = sprintf("<abbr title=\"%s\">%s</abbr>()", $method, $method);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats an array as a string.
|
||||
*
|
||||
* @param array $args The argument array
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function formatArgs($args)
|
||||
{
|
||||
return $this->container->get('templating.helper.code')->formatArgs($args);
|
||||
$result = array();
|
||||
foreach ($args as $key => $item) {
|
||||
if ('object' === $item[0]) {
|
||||
$parts = explode('\\', $item[1]);
|
||||
$short = array_pop($parts);
|
||||
$formattedValue = sprintf("<em>object</em>(<abbr title=\"%s\">%s</abbr>)", $item[1], $short);
|
||||
} elseif ('array' === $item[0]) {
|
||||
$formattedValue = sprintf("<em>array</em>(%s)", is_array($item[1]) ? $this->formatArgs($item[1]) : $item[1]);
|
||||
} elseif ('string' === $item[0]) {
|
||||
$formattedValue = sprintf("'%s'", htmlspecialchars($item[1], ENT_QUOTES, $this->getCharset()));
|
||||
} elseif ('null' === $item[0]) {
|
||||
$formattedValue = '<em>null</em>';
|
||||
} elseif ('boolean' === $item[0]) {
|
||||
$formattedValue = '<em>'.strtolower(var_export($item[1], true)).'</em>';
|
||||
} elseif ('resource' === $item[0]) {
|
||||
$formattedValue = '<em>resource</em>';
|
||||
} else {
|
||||
$formattedValue = str_replace("\n", '', var_export(htmlspecialchars((string) $item[1], ENT_QUOTES, $this->getCharset()), true));
|
||||
}
|
||||
|
||||
$result[] = is_int($key) ? $formattedValue : sprintf("'%s' => %s", $key, $formattedValue);
|
||||
}
|
||||
|
||||
return implode(', ', $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats an array as a string.
|
||||
*
|
||||
* @param array $args The argument array
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function formatArgsAsText($args)
|
||||
{
|
||||
return $this->container->get('templating.helper.code')->formatArgsAsText($args);
|
||||
return strip_tags($this->formatArgs($args));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an excerpt of a code file around the given line number.
|
||||
*
|
||||
* @param string $file A file path
|
||||
* @param int $line The selected line number
|
||||
*
|
||||
* @return string An HTML string
|
||||
*/
|
||||
public function fileExcerpt($file, $line)
|
||||
{
|
||||
return $this->container->get('templating.helper.code')->fileExcerpt($file, $line);
|
||||
if (is_readable($file)) {
|
||||
$code = highlight_file($file, true);
|
||||
// remove main code/span tags
|
||||
$code = preg_replace('#^<code.*?>\s*<span.*?>(.*)</span>\s*</code>#s', '\\1', $code);
|
||||
$content = preg_split('#<br />#', $code);
|
||||
|
||||
$lines = array();
|
||||
for ($i = max($line - 3, 1), $max = min($line + 3, count($content)); $i <= $max; $i++) {
|
||||
$lines[] = '<li'.($i == $line ? ' class="selected"' : '').'><code>'.self::fixCodeMarkup($content[$i - 1]).'</code></li>';
|
||||
}
|
||||
|
||||
return '<ol start="'.max($line - 3, 1).'">'.implode("\n", $lines).'</ol>';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats a file path.
|
||||
*
|
||||
* @param string $file An absolute file path
|
||||
* @param integer $line The line number
|
||||
* @param string $text Use this text for the link rather than the file path
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function formatFile($file, $line, $text = null)
|
||||
{
|
||||
return $this->container->get('templating.helper.code')->formatFile($file, $line, $text);
|
||||
if (null === $text) {
|
||||
$file = trim($file);
|
||||
$fileStr = $file;
|
||||
if (0 === strpos($fileStr, $this->rootDir)) {
|
||||
$fileStr = str_replace($this->rootDir, '', str_replace('\\', '/', $fileStr));
|
||||
$fileStr = sprintf('<abbr title="%s">kernel.root_dir</abbr>/%s', $this->rootDir, $fileStr);
|
||||
}
|
||||
|
||||
$text = "$fileStr at line $line";
|
||||
}
|
||||
|
||||
if (false !== $link = $this->getFileLink($file, $line)) {
|
||||
return sprintf('<a href="%s" title="Click to open this file" class="file_link">%s</a>', htmlspecialchars($link, ENT_QUOTES | ENT_SUBSTITUTE, $this->charset), $text);
|
||||
}
|
||||
|
||||
return $text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the link for a given file/line pair.
|
||||
*
|
||||
* @param string $file An absolute file path
|
||||
* @param integer $line The line number
|
||||
*
|
||||
* @return string A link of false
|
||||
*/
|
||||
public function getFileLink($file, $line)
|
||||
{
|
||||
return $this->container->get('templating.helper.code')->getFileLink($file, $line);
|
||||
if ($this->fileLinkFormat && is_file($file)) {
|
||||
return strtr($this->fileLinkFormat, array('%f' => $file, '%l' => $line));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function formatFileFromText($text)
|
||||
{
|
||||
return $this->container->get('templating.helper.code')->formatFileFromText($text);
|
||||
$that = $this;
|
||||
|
||||
return preg_replace_callback('/in ("|")?(.+?)\1(?: +(?:on|at))? +line (\d+)/s', function ($match) use ($that) {
|
||||
return 'in '.$that->formatFile($match[2], $match[3]);
|
||||
}, $text);
|
||||
}
|
||||
|
||||
public function getName()
|
||||
{
|
||||
return 'code';
|
||||
}
|
||||
|
||||
protected static function fixCodeMarkup($line)
|
||||
{
|
||||
// </span> ending tag from previous line
|
||||
$opening = strpos($line, '<span');
|
||||
$closing = strpos($line, '</span>');
|
||||
if (false !== $closing && (false === $opening || $closing < $opening)) {
|
||||
$line = substr_replace($line, '', $closing, 7);
|
||||
}
|
||||
|
||||
// missing </span> tag at the end of line
|
||||
$opening = strpos($line, '<span');
|
||||
$closing = strpos($line, '</span>');
|
||||
if (false !== $opening && (false === $closing || $closing > $opening)) {
|
||||
$line .= '</span>';
|
||||
}
|
||||
|
||||
return $line;
|
||||
}
|
||||
}
|
||||
|
@ -66,7 +66,9 @@
|
||||
|
||||
<service id="twig.extension.code" class="%twig.extension.code.class%" public="false">
|
||||
<tag name="twig.extension" />
|
||||
<argument type="service" id="service_container" />
|
||||
<argument>%templating.helper.code.file_link_format%</argument>
|
||||
<argument>%kernel.root_dir%</argument>
|
||||
<argument>%kernel.charset%</argument>
|
||||
</service>
|
||||
|
||||
<service id="twig.extension.routing" class="%twig.extension.routing.class%" public="false">
|
||||
|
@ -9,23 +9,18 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\FrameworkBundle\Tests\Templating\Helper;
|
||||
namespace Symfony\Bundle\TwigBundle\Tests\Extension;
|
||||
|
||||
use Symfony\Bundle\FrameworkBundle\Templating\Helper\CodeHelper;
|
||||
use Symfony\Bundle\TwigBundle\Extension\CodeExtension;
|
||||
|
||||
class CodeHelperTest extends \PHPUnit_Framework_TestCase
|
||||
class CodeExtensionTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
protected static $helper;
|
||||
|
||||
public static function setUpBeforeClass()
|
||||
{
|
||||
self::$helper = new CodeHelper('txmt://open?url=file://%f&line=%l', '/root', 'UTF-8');
|
||||
}
|
||||
protected $helper;
|
||||
|
||||
public function testFormatFile()
|
||||
{
|
||||
$expected = sprintf('<a href="txmt://open?url=file://%s&line=25" title="Click to open this file" class="file_link">%s at line 25</a>', __FILE__, __FILE__);
|
||||
$this->assertEquals($expected, self::$helper->formatFile(__FILE__, 25));
|
||||
$this->assertEquals($expected, $this->getExtension()->formatFile(__FILE__, 25));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -33,7 +28,7 @@ class CodeHelperTest extends \PHPUnit_Framework_TestCase
|
||||
*/
|
||||
public function testGettingClassAbbreviation($class, $abbr)
|
||||
{
|
||||
$this->assertEquals(self::$helper->abbrClass($class), $abbr);
|
||||
$this->assertEquals($this->getExtension()->abbrClass($class), $abbr);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -41,7 +36,7 @@ class CodeHelperTest extends \PHPUnit_Framework_TestCase
|
||||
*/
|
||||
public function testGettingMethodAbbreviation($method, $abbr)
|
||||
{
|
||||
$this->assertEquals(self::$helper->abbrMethod($method), $abbr);
|
||||
$this->assertEquals($this->getExtension()->abbrMethod($method), $abbr);
|
||||
}
|
||||
|
||||
public function getClassNameProvider()
|
||||
@ -64,6 +59,11 @@ class CodeHelperTest extends \PHPUnit_Framework_TestCase
|
||||
|
||||
public function testGetName()
|
||||
{
|
||||
$this->assertEquals('code', self::$helper->getName());
|
||||
$this->assertEquals('code', $this->getExtension()->getName());
|
||||
}
|
||||
|
||||
protected function getExtension()
|
||||
{
|
||||
return new CodeExtension('txmt://open?url=file://%f&line=%l', '/root', 'UTF-8');
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user