How to display the script snippet where the exception was thrown in PHP

1

PHP, when an exception is thrown, the file and the line where the exception was thrown are reported.

Example:

try {

   throw new Exception('Só para mostrar a linha');

} catch (Exception $e) {

    echo $e->getLine(); // 38
    echo $e->getFile(); // 'projeto/index.php'
}

Based on this information, I would like to read the lines of the file where the exception was thrown. I want to open the file and read 5 lines before and 5 lines after the 38 line above.

How could I do this in PHP?

    
asked by anonymous 31.05.2016 / 20:31

3 answers

1

I liked the answer from @GuilhermeNascimento, but I would also like to leave a solution.

In PHP, to do this, we could use the combination of SplFileObject with LimitIterator .

The LimitIterator iterates over Iterator , from an initial position passed to another.

The SplFileObject is also a Iterator . The iteration side, it reads a line (or a certain amount of specific data) from a file.

PHP also has a specific function to handle exceptions. That is, you do not have to be doing try/catch every time you want to apply this functionality. Just use the set_exception_handler function that will do the job.

Come on:

set_exception_handler(function ($e)
{
     $fileObject = new SplFileObject($e->getFile(), 'r');
     // Definimos que vamos ver o trecho 5 linhas antes da exceção lançada e 5 depois

     $lines = new LimitIterator($fileObject, $e->getLine() - 5, $e->getLine() + 5);

   // Para exibir as linhas basta um foreach
   foreach($lines as $key => $line) {
        echo $line;
   }
});
    
09.06.2016 / 21:46
2

I've always had this need, especially if I need to work with more people and the files have constant changes, sometimes we lose some problem, or even have to be locating a file and locating the problem, having this need I ended up creating a library just for this kind of situation.

One that reads a part of the file defined by the user: link

And another to do line counting thus displaying beyond the line that occurs the problem (because as I said in another question, sometimes the problem may have occurred a few lines before):

link

Then you could use the code like this:

function obterTrecho($source, $line)
{
    if ($line <= 0 || is_file($source) === false) {
        return null;
    } elseif ($line >= 5) {
        $init = $line - 5;
        $end  = $line + 5;
        $breakpoint = 5;
    } else {
        $init = 0;
        $end  = 5;
        $breakpoint = $line;
    }
    return array(
        'breakpoint' => $breakpoint,
        'preview' => explode(EOL, trechoArquivo($source, $init, $end, true))
    );
}

function trechoArquivo($path, $init = 0, $end = 1024)
{
    if (false === is_file($path)) {
        return false;
    }

    $i = 1;
    $output = '';
    $handle = fopen($path, 'rb');

    while (false === feof($handle) && $i <= $end) {
        $data = fgets($handle);
        if ($i >= $init) {
            $output .= $data;
        }
        ++$i;
    }

    fclose($handle);
    return $output;
}

Note that I have used rb instead of r because it can occur from (rarely) needing to read something "binary", its use would look like this:

try {

   throw new Exception('Só para mostrar a linha');

} catch (Exception $e) {
    var_dump(obterTrecho($e->getFile(), $e->getLine()));
}

It will return something like:

    array(
        'breakpoint' => 5,
        'preview' => array(
            'foo();',
            'outrocodigo();',
            '',
            'try {',
            '',
            '   throw new Exception('Só para mostrar a linha');',
            '',
            '} catch (Exception $e) {',
            '     var_dump(obterTrecho($e->getFile(), $e->getLine()));',
            '}'
         )
    );

Note that breakpoint refers to the array item of index 5, but not exactly the line, which you can use to highlight the line that PHP accuses of having the problem, so you can treat this data with something like:

function ExibeErro($file, $line)
{
    $source = obterTrecho($file, $line);

    $data = $source['preview'];
    $breakpoint = $source['breakpoint'];

    $lines = count($data);

    for ($i = 0; $i < $lines; $i++) {
        if ($breakpoint === $i) {
            echo '<strong style="color: red;">', htmlspecialchars($data[$i], ENT_QUOTES), '</strong>', EOL;
        } else {
            echo htmlspecialchars($data[$i], ENT_QUOTES), EOL;
        }
    }
}

...

try {

   throw new Exception('Só para mostrar a linha');

} catch (Exception $e) {
    ExibeErro($e->getFile(), $e->getLine()));
}

The interesting thing about it is that you can implement with set_error_handler so too:

function handlerError($type, $message, $file, $line, $details = null)
{
    static $preventDuplicate; //Previne duplicidade dos erros (também é possivel fazer isto pelo php.ini)

    $str  = '?' . $file . ':' . $line . '?';

    if ($preventDuplicate === null) {
        $preventDuplicate = '';
    }

    if (strpos($preventDuplicate, $str) === false) {
        $preventDuplicate .= $str;
        echo '<h1>Erro: '$message, '</h1>';
        ExibeErro($file, $line);
    }

    return false;
}

function shutodownEvent()
{
    //Pega erros de PARSE
    $e = error_get_last();
    if ($e !== null) {
        handlerError($e['type'], $e['message'], $e['file'], $e['line']);
    }
}

register_shutdown_function('shutodownEvent');
set_error_handler('handlerError', E_ALL|E_STRICT);
    
01.06.2016 / 15:59
1

I think you could do something similar to this, I did not test it, I do not know if it does, but try there:

function myFetchContents($fileError, $errorMessage, $errorLine) { 
        $fError = fopen($fileError, 'r');
        if ($fError) {
           $contentError = fread($fError, $errorLine);
           $errors = array( 'errorMessage' => $errorMessage,
                            'contentError' => nl2br($contentError));
          return $errors;
        }
} 

try { 
   //faz algo
} catch (Exception $e) { 
  $error = myFetchContents($e->getFile(), $e->getMessage, $e->getLine());
  var_dump($error); 
}  
    
31.05.2016 / 21:20