2011-05-19 13:32:24 +01:00
< ? php
/*
* This file is part of the Symfony package .
*
* ( c ) Fabien Potencier < fabien @ symfony . com >
*
* For the full copyright and license information , please view the LICENSE
* file that was distributed with this source code .
*/
2012-12-12 11:25:26 +00:00
namespace Symfony\Bridge\Twig\Extension ;
2011-05-19 13:32:24 +01:00
2012-11-12 14:06:38 +00:00
if ( ! defined ( 'ENT_SUBSTITUTE' )) {
define ( 'ENT_SUBSTITUTE' , 8 );
}
2011-05-19 13:32:24 +01:00
/**
2012-11-12 14:06:38 +00:00
* Twig extension relate to PHP code and used by the profiler and the default exception templates .
2011-05-19 13:32:24 +01:00
*
* @ author Fabien Potencier < fabien @ symfony . com >
*/
class CodeExtension extends \Twig_Extension
{
2012-11-12 14:06:38 +00:00
private $fileLinkFormat ;
private $rootDir ;
private $charset ;
2011-05-19 13:32:24 +01:00
/**
2012-11-12 14:06:38 +00:00
* Constructor .
2011-05-19 13:32:24 +01:00
*
2012-11-12 14:06:38 +00:00
* @ param string $fileLinkFormat The format for links to source files
* @ param string $rootDir The project root directory
* @ param string $charset The charset
2011-05-19 13:32:24 +01:00
*/
2012-11-12 14:06:38 +00:00
public function __construct ( $fileLinkFormat , $rootDir , $charset )
2011-05-19 13:32:24 +01:00
{
2012-11-12 14:06:38 +00:00
$this -> fileLinkFormat = empty ( $fileLinkFormat ) ? ini_get ( 'xdebug.file_link_format' ) : $fileLinkFormat ;
$this -> rootDir = str_replace ( '\\' , '/' , $rootDir ) . '/' ;
$this -> charset = $charset ;
2011-05-19 13:32:24 +01:00
}
/**
* { @ inheritdoc }
*/
public function getFilters ()
{
return array (
'abbr_class' => new \Twig_Filter_Method ( $this , 'abbrClass' , array ( 'is_safe' => array ( 'html' ))),
'abbr_method' => new \Twig_Filter_Method ( $this , 'abbrMethod' , array ( 'is_safe' => array ( 'html' ))),
'format_args' => new \Twig_Filter_Method ( $this , 'formatArgs' , array ( 'is_safe' => array ( 'html' ))),
'format_args_as_text' => new \Twig_Filter_Method ( $this , 'formatArgsAsText' ),
'file_excerpt' => new \Twig_Filter_Method ( $this , 'fileExcerpt' , array ( 'is_safe' => array ( 'html' ))),
'format_file' => new \Twig_Filter_Method ( $this , 'formatFile' , array ( 'is_safe' => array ( 'html' ))),
'format_file_from_text' => new \Twig_Filter_Method ( $this , 'formatFileFromText' , array ( 'is_safe' => array ( 'html' ))),
'file_link' => new \Twig_Filter_Method ( $this , 'getFileLink' , array ( 'is_safe' => array ( 'html' ))),
);
}
public function abbrClass ( $class )
{
2012-11-12 14:06:38 +00:00
$parts = explode ( '\\' , $class );
$short = array_pop ( $parts );
return sprintf ( " <abbr title= \" %s \" >%s</abbr> " , $class , $short );
2011-05-19 13:32:24 +01:00
}
public function abbrMethod ( $method )
{
2012-11-12 14:06:38 +00:00
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 ;
2011-05-19 13:32:24 +01:00
}
2012-11-12 14:06:38 +00:00
/**
* Formats an array as a string .
*
* @ param array $args The argument array
*
* @ return string
*/
2011-05-19 13:32:24 +01:00
public function formatArgs ( $args )
{
2012-11-12 14:06:38 +00:00
$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 ]) {
2012-11-12 17:18:55 +00:00
$formattedValue = sprintf ( " '%s' " , htmlspecialchars ( $item [ 1 ], ENT_QUOTES , $this -> charset ));
2012-11-12 14:06:38 +00:00
} 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 {
2012-11-12 17:18:55 +00:00
$formattedValue = str_replace ( " \n " , '' , var_export ( htmlspecialchars (( string ) $item [ 1 ], ENT_QUOTES , $this -> charset ), true ));
2012-11-12 14:06:38 +00:00
}
$result [] = is_int ( $key ) ? $formattedValue : sprintf ( " '%s' => %s " , $key , $formattedValue );
}
return implode ( ', ' , $result );
2011-05-19 13:32:24 +01:00
}
2012-11-12 14:06:38 +00:00
/**
* Formats an array as a string .
*
* @ param array $args The argument array
*
* @ return string
*/
2011-05-19 13:32:24 +01:00
public function formatArgsAsText ( $args )
{
2012-11-12 14:06:38 +00:00
return strip_tags ( $this -> formatArgs ( $args ));
2011-05-19 13:32:24 +01:00
}
2012-11-12 14:06:38 +00:00
/**
* 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
*/
2011-05-19 13:32:24 +01:00
public function fileExcerpt ( $file , $line )
{
2012-11-12 14:06:38 +00:00
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>' ;
}
2011-05-19 13:32:24 +01:00
}
2012-11-12 14:06:38 +00:00
/**
* 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
*/
2011-05-19 13:32:24 +01:00
public function formatFile ( $file , $line , $text = null )
{
2012-11-12 14:06:38 +00:00
if ( null === $text ) {
$file = trim ( $file );
2013-01-20 10:09:21 +00:00
$text = $file ;
if ( 0 === strpos ( $text , $this -> rootDir )) {
$text = str_replace ( $this -> rootDir , '' , str_replace ( '\\' , '/' , $text ));
$text = sprintf ( '<abbr title="%s">kernel.root_dir</abbr>/%s' , $this -> rootDir , $text );
2012-11-12 14:06:38 +00:00
}
}
2013-01-20 10:09:21 +00:00
$text = " $text at line $line " ;
2012-11-12 14:06:38 +00:00
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 ;
2011-05-19 13:32:24 +01:00
}
2012-11-12 14:06:38 +00:00
/**
* 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
*/
2011-05-19 13:32:24 +01:00
public function getFileLink ( $file , $line )
{
2012-11-12 14:06:38 +00:00
if ( $this -> fileLinkFormat && is_file ( $file )) {
return strtr ( $this -> fileLinkFormat , array ( '%f' => $file , '%l' => $line ));
}
return false ;
2011-05-19 13:32:24 +01:00
}
public function formatFileFromText ( $text )
{
2012-11-12 14:06:38 +00:00
$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 );
2011-05-19 13:32:24 +01:00
}
public function getName ()
{
return 'code' ;
}
2012-11-12 14:06:38 +00:00
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 ;
}
2011-05-19 13:32:24 +01:00
}