BackTraceExplain.php
Go to the documentation of this file.00001 <?php
00026 class BackTraceExplain
00027 {
00041 const CONTEXT_LINES = 0;
00042
00053 const SHOW_ARGUMENT_VALUE = false;
00054
00063 protected static $aFileContent = array();
00064
00072 protected static $iExplainId = 0;
00073
00081 protected $iStartLevel = 0;
00082
00090 protected $oBackTrace = null;
00091
00099 protected $sScriptFunctionName = "";
00100
00104 protected function __construct(){}
00105
00118 protected function fetchArguments()
00119 {
00120 $aArgument = $this->oBackTrace->getArguments();
00121 if ( count( $aArgument ) == 0 )
00122 {
00123 return array();
00124 }
00125 $aReturn = array();
00126 foreach ( $aArgument as $mArgument )
00127 {
00128 switch( gettype( $mArgument ) )
00129 {
00130 case "array":
00131 $sSummary = "array( " . count( $mArgument ) . " )";
00132 break;
00133 case "boolean":
00134 $sSummary = $mArgument == true ? "true" : "false";
00135 break;
00136 case "double":
00137 case "integer":
00138 $sSummary = self::SHOW_ARGUMENT_VALUE == true ? (string) $mArgument : "integer";
00139 break;
00140 case "NULL":
00141 $sSummary = "null";
00142 break;
00143 case "object":
00144 $sSummary = "object( " . htmlentities( get_class( $mArgument ) ) . " )";
00145 break;
00146 case "resource":
00147 $sSummary = "resource( " . htmlentities( strstr( $mArgument, "#" ) . " " . get_resource_type( $mArgument ) ) . " )";
00148 break;
00149 case "string":
00150 if ( self::SHOW_ARGUMENT_VALUE == true )
00151 {
00152 if ( strlen( $mArgument ) > 15 )
00153 {
00154 $mArgument = substr( $mArgument, 0, 12 ) . "...";
00155 }
00156 $sSummary = htmlentities( "\"" . $mArgument . "\"" );
00157 }
00158 else
00159 {
00160 $sSummary = "string( " . strlen( $mArgument ) . " )";
00161 }
00162 break;
00163 default:
00164 $sSummary = "unknown type " . htmlentities( gettype( $mArgument ) );
00165 break;
00166 }
00167 array_push( $aReturn, $sSummary );
00168 }
00169 return $aReturn;
00170 }
00171
00179 protected function fetchCall()
00180 {
00181 if ( $this->oBackTrace->getScope() == BackTrace::SCOPE_FILE )
00182 {
00183 return "";
00184 }
00185 $iLevel = self::$iExplainId + $this->oBackTrace->getLevel();
00186 $sAnchor = "";
00187 $sArgument = "";
00188 foreach ( $this->fetchArguments() as $sSummary )
00189 {
00190 $sArgument .= ", " . $sSummary;
00191 }
00192 if ( $sArgument != "" )
00193 {
00194 $sArgument = substr( $sArgument, 1 ) . " ";
00195 }
00196 $sClass = $this->oBackTrace->getClass();
00197 $sType = $this->oBackTrace->getType();
00198 $sFunction = $this->oBackTrace->getFunction();
00199 return htmlentities( $sClass . $sType . $sFunction ) . "(" . $sArgument . ")";
00200 }
00201
00209 protected function fetchLevel()
00210 {
00211 $sLocation = htmlentities( $this->oBackTrace->getFile() ) . " on line " . $this->oBackTrace->getLine();
00212 $sSource = $this->fetchSource();
00213 $sSourceId = "BackTraceExplainSource" . ( self::$iExplainId + $this->oBackTrace->getLevel() );
00214 if ( $sSource != "" )
00215 {
00216 $sLocation =
00217 "<a href=\"\" onclick=\"this.blur(); " . $this->sScriptFunctionName . "( '" . $sSourceId . "' ); return false;\">" .
00218 $sLocation .
00219 "</a>";
00220 }
00221 return
00222 "<div class=\"traceCall\">" .
00223 $this->fetchCall() .
00224 "</div>" .
00225 "<div class=\"traceLocation\">" .
00226 $sLocation .
00227 "</div>" .
00228 "<div class=\"traceSource\" id=\"" . $sSourceId . "\">" .
00229 $sSource .
00230 "</div>";
00231 }
00232
00247 protected function fetchSource()
00248 {
00249 $sFile = $this->oBackTrace->getFile();
00250 if ( self::CONTEXT_LINES == 0 || file_exists( $sFile ) == false || is_readable( $sFile ) == false )
00251 {
00252 return "";
00253 }
00254 # verifica se existe cache
00255 if ( array_key_exists( $sFile, self::$aFileContent ) == false )
00256 {
00257 self::$aFileContent[$sFile] = substr( highlight_string( file_get_contents( $sFile ), true ), 6, -7 );
00258 }
00259 $sSource = "";
00260 $aLine = explode( "<br />", self::$aFileContent[$sFile] );
00261 $iLine = $this->oBackTrace->getLine();
00262 # define linha inicial e final a ser exibida
00263 if ( self::CONTEXT_LINES < 0 )
00264 {
00265 $iLineStart = 1;
00266 $iLineEnd = count( $aLine );
00267 }
00268 else
00269 {
00270 $fEdge = ( self::CONTEXT_LINES - 1 ) / 3;
00271 $iLineStart = $iLine - ceil( $fEdge * 2 );
00272 $iLineEnd = $iLine + floor( $fEdge );
00273 if ( $iLineStart < 1 )
00274 {
00275 $iLineEnd += abs( $iLineStart ) + 1;
00276 $iLineStart = 1;
00277 }
00278 $iSourceLength = count( $aLine );
00279 if ( $iLineEnd > $iSourceLength )
00280 {
00281 $iLineStart -= $iLineEnd - $iSourceLength;
00282 if ( $iLineStart < 1 )
00283 {
00284 $iLineStart = 1;
00285 }
00286 $iLineEnd = $iSourceLength;
00287 }
00288 }
00289 # monta texto de deteção da primeira linha, que precisa de tratamento especial
00290 $sFirstLineDetect = "<span style=\"color: #000000\">\n<span style=\"color: #0000BB\">";
00291 $iFirstLineDetectLength = strlen( $sFirstLineDetect );
00292 # percorre linhas de código
00293 foreach ( $aLine as $iCurrentLine => $sLineContent )
00294 {
00295 # trata linha corrente para contagem iniciar no número 1
00296 $iCurrentLine++;
00297 # verifica se linha deve estar visível
00298 if ( $iCurrentLine < $iLineStart || $iCurrentLine > $iLineEnd )
00299 {
00300 # remove todo conteúdo que existir entre duas marcações HTML
00301 $aMatch = array();
00302 preg_match_all( "(<[^>]+>)", $sLineContent, $aMatch );
00303 if ( count( $aMatch ) > 0 && is_array( $aMatch[0] ) === true )
00304 {
00305 $sLineContent = str_replace( array( "<br/>", " ", "\n" ), "", implode( "", $aMatch[0] ) );
00306 }
00307 }
00308 else
00309 {
00310 # verifica se é a primeira linha
00311 if ( substr( $sLineContent, 0, $iFirstLineDetectLength ) === $sFirstLineDetect )
00312 {
00313 # retira o prefixo para evitar erros de tratamento do código
00314 # o prefixo é adiciona de volta a linha ao final do tratamento da linha atual
00315 $sLineContent = substr( $sLineContent, $iFirstLineDetectLength );
00316 $sLinePrefix = $sFirstLineDetect;
00317 }
00318 else
00319 {
00320 $sLinePrefix = "";
00321 }
00322 # prepara linha para que ela receba a numeração com uma classe de estilo
00323 # adiciona uma quebra de linha visível ao final da linha
00324 $sLineContent = "</span>" . $sLineContent . "<br/>";
00325 # verifica se linha deve ser destacada
00326 if ( $iCurrentLine !== $iLine )
00327 {
00328 $sClass = "traceNumberLineOff";
00329 }
00330 else
00331 {
00332 # destaca número da linha
00333 # adiciona classe de estilo para destaque de todo conteúdo da linha
00334 $sClass = "traceNumberLineOn";
00335 $sLineContent = preg_replace( "/>([^<]*)</", "><span class=\"traceLineActive\">$1</span><", $sLineContent );
00336 }
00337 $sLineContent =
00338 $sLinePrefix .
00339 "<span class=\"" . $sClass . "\">" .
00340 sprintf( "%0" . strlen( $iLineEnd ) . "d", $iCurrentLine ) .
00341 $sLineContent;
00342 }
00343 $sSource .= $sLineContent;
00344 }
00345 return $sSource;
00346 }
00347
00359 public static function perform( BackTrace $oBackTrace = null )
00360 {
00361 if ( $oBackTrace == null )
00362 {
00363 $oBackTrace = new BackTrace();
00364 $oBackTrace->levelDown();
00365 }
00366 $iOriginalLevel = $oBackTrace->getLevel();
00367 $oExplain = new self();
00368 $oExplain->oBackTrace = $oBackTrace;
00369 $oExplain->iStartLevel = $oBackTrace->getLevel();
00370 $oExplain->sScriptFunctionName = "BackTraceExplainFunction" . self::$iExplainId;
00371 $sTrace = "";
00372 do
00373 {
00374 $sTrace .=
00375 "<li class=\"traceLevel\">" .
00376 $oExplain->fetchLevel() .
00377 "</li>";
00378 }
00379 while ( $oBackTrace->levelDown() == true );
00380 self::$iExplainId += $oBackTrace->getLevel() - $iOriginalLevel + 1;
00381 $oBackTrace->setLevel( $iOriginalLevel );
00382 $sDir = dirname( __FILE__ ) . "/resource/";
00383 return
00384 "<style type=\"text/css\">" .
00385 file_get_contents( $sDir . "style.css" ) .
00386 "</style>" .
00387 "<script type=\"text/javascript\">" .
00388 str_replace( "_TRACE_FUNCTION_", $oExplain->sScriptFunctionName, file_get_contents( $sDir . "script.js" ) ) .
00389 "</script>" .
00390 "<ol class=\"trace\">" .
00391 $sTrace .
00392 "</ol>";
00393 }
00394 }