How to display the excerpt from the script where the exception was released in PHP

Asked

Viewed 109 times

1

PHP, when an exception is released, is informed the file and the line where the exception was launched.

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 released. I want to open the file and read 5 lines before and 5 lines after line 38 above.

How could I do this in PHP?

  • still exist $e->getMessage(); , $e->getTraceAsString();, $e->getCode(), $e->getPrevious () and $e->getTrace() the ideal is you save a log, and then open it.

  • No, but I want to do as it is in the same question. I want to display the snippet of the script where the error occurred, like the Whoops library does.

  • 1

    using file_get_contents() does not solve? or set_error_handler()

  • file_get_contents will pick it all up. I actually have a solution, but I’d like to learn a better way :D. But that’s it, the set_error_handler is the way

  • See if here has the expected path.

  • 1

    It would be more or less the same mechanism that Whoops uses?

  • 1

    Yes @rray, that’s right

  • 2

    You’ll show it on the page?

  • I want to make an Exception Handler that will display the mistakes. I have an idea of how to do, but I want to learn how to do better

Show 5 more comments

3 answers

1

Also I 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 we have to keep 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: https://github.com/inphinit/framework/blob/master/src/Experimental/File.php#L16

And another to do the count of lines so 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):

https://github.com/inphinit/framework/blob/master/src/Experimental/Debug.php#L172

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 used rb instead of r because it can occur sometimes (rarely) need to read something "binary", the use of it would be 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 item of the index array 5, but not exactly the line, which you can use to highlight the line that PHP accuses is with 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 it with set_error_handler so also:

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);

1


I liked the reply of @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 iterate on a Iterator, from one initial position passed to another.

The SplFileObject is also a Iterator. The expensive iteration, 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, there is no need to keep doing try/catch every time you want to apply this feature. 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;
   }
});

1

I think you could do something similar to this, I didn’t test it, I don’t know if it fits, but try it 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); 
}  
  • fread will read a certain size in the buffer (the second argument), not the specific line. I believe in this case it is the fseek who does this job.

  • Another alternative is to use the function var_dump(ini_get('display_errors')), - read more here or var_dump(error_get_last()); - read more here

Browser other questions tagged

You are not signed in. Login or sign up in order to post.