W
W
wartur2011-12-12 15:29:03
PHP
wartur, 2011-12-12 15:29:03

PHP - How to return managed content on PHP critical error (E_ERROR, E_PARSE)?

Hello inquisitive minds.
In this question, I would like to continue the popular topic of questions about error handling in PHP.

Conditions.

1) Yes, code.
2) In one of the included files, let's say in one of the modules an error E_ERROR crept in (let's say an exception or incompatibility of the code for different OS, server settings) or E_PARSE (the programmer accidentally messed up, and uploaded it to the server without a test, was late for a girl for example).
3) In the case of a working server, where error output is disabled on the corresponding page where the situation happened, it gives out emptiness. Below I will give my developments on this subject.

If you are interested in the conditions, then I ask you to be brave, here it is even more interesting.


Why did I give 2 examples knowing that set_exception_handler and set_error_handler exist ?
Because in the first case, I would like to reduce it to a universal method, and the second option does not handle errors, for example, E_PARSE, E_ERROR, it does not stupidly start the interception function at all. I wanted to kill 2 birds with one stone.

Task.

Give 404 headers, in case of critical code errors + some data, even static ones like “oh, an error, we are actively fixing this now, come back later” (explanations for users).

What for?
Because the search engine should push against them for 404, and not include empty pages c 200 Ok + in the index, the user must be informed that there is an error on the site now. One way or another, it will be written in the logs, but you don’t always look into the logs, if everything looks good, well, in the mail too - we went to the girls.

Solution with headers

<?php
header($_SERVER["SERVER_PROTOCOL"].' '.'404'); // setting headers before generating content

// Generating content, code errors, exceptions, or parse errors are possible.
$data = generate();

header($_SERVER["SERVER_PROTOCOL"].' '.'200'); // setting proper headers

// data output for example, but better via buffered output
echo $data;
?>

Suggested solution to the problem

  1. It's not enough, I'm trying to find something like intercepting error codes at the Apache level, but this is terribly not universal and binds to one type of server. Moreover, I have not yet found a single example of how PHP can send a message “Houston we have problems!” to Apache so that it loads the default error page.
  2. Through a programmatic way, now I am studying the output buffering functions, maybe I can give something.
  3. I can’t imagine any other ways, unless of course there is something like this in PHP , I’ll take it after the first 2 points.
  4. There is an option to write data to the output (as described above in the Solution with headers), then discard them and rewrite from scratch with new headers - but this is very hackish.

For all of the above, I do not know the solutions, these are only assumptions.

Links to current content

httpd.apache.org/docs/2.0/mod/core.html#errordocument
www.php.net/manual/ru/ref.outcontrol.php
php.net/manual/ru/function.set-error-handler.php
php .net/manual/ru/function.set-exception-handler.php
www.php.net/manual/ru/index.php

Gentlemen, your suggestions and most importantly decisions with prooflinks to the

UPD studio:
I am surprised at hamsters, which are rare and thin questions minus karma, learn to code yourself for a start without crutches.

UPD2:
- This question has 2 answers and all are interesting, the solution is the most elegant, but the second solution is also interesting.
- For those who again need to register a class method, I ask here , where I described how this is done.

UPD3:
In the process of debriefing, a discussion appeared, which I advise you to read for general development.

Answer the question

In order to leave comments, you need to log in

7 answer(s)
I
Ivan Shalganov, 2011-12-12
@wartur

register_shutdown_function(function () {
   $error = error_get_last();
   if ($error && ($error['type'] == E_ERROR || $error['type'] == E_PARSE || $error['type'] == E_COMPILE_ERROR)) {
       if (strpos($error['message'], 'Allowed memory size') === 0) { // если кончилась память
           ini_set('memory_limit', (intval(ini_get('memory_limit'))+64)."M"); // выделяем немножко что бы доработать корректно
           Log::error("PHP Fatal: not enough memory in ".$error['file'].":".$error['line']);
  } else {
           Log::error("PHP Fatal: ".$error['message']." in ".$error['file'].":".$error['line']);
        }
        // ... завершаемая корректно ....
    }
})


It also catches crashes in the absence of free memory, parsing errors, and other things.

S
Sergey Beresnev, 2011-12-12
@sectus

What immediately catches the eye: you need a 503 error instead of a 404.

O
Oleg Matrozov, 2011-12-12
@Mear

I solved a similar problem using the standard output buffer interception (ob_start...). The trick is that the buffer handler is called even in case of fatal errors (specified by you), thus by checking in the buffer handler if an error has occurred, we can already send the necessary headers, etc.

ob_start('ob_end');

...

function ob_end($outputData)
{
  $error = error_get_last();
      
  if (is_array($error) && in_array($error['type'], array(E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR)))
  {
    /* Фатальная ошибка, отдаем 500 */
  }

  return $outputData;
}

E
easterism, 2011-12-12
@easterism

Everything is fine, but somehow they forgot to remind everyone that error_get_last () is supported starting from 5.2.0
Crutches for earlier versions, these are just crutches.

X
xaker1, 2011-12-12
@xaker1

Why Q&A?

G
galaxy, 2011-12-12
@galaxy

200 is not always given : stackoverflow.com/a/8295606
it seems impossible to make this work in conjunction with ErrorDocument

P
png, 2011-12-12
@png

Colleagues.
First, I agree with what was said above. This is also correct. He himself did similar things, blocked errors, wrote to the log, sent to the mail, etc.
Secondly, if I was asked to make such a feature in a good solid project, then I would not reinvent the wheel, but use the solution out of the box.
Popular frameworks Symfony2, Yii - already support this out of the box, and for a long time.
I didn’t check the overflow from memory, it catches the rest without problems and returns the 500 error, that is, as it should (the amendment above, it is not necessary to give the 503rd. You can use any 5xx, this gesture will tell the search engine - “Oh, ask me later ...”, 404 - in this case, it is better not to give it back, it can affect the position of the site in the search - if this is important to you.).
The only limitation is to the style of the code, write without warings and notices, they are also caught. Otherwise, you run the risk of making a project that will only work in production mode, that is, with errors turned off. Debugging such things is not the most pleasant experience. In addition, the number of errors in such code is an order of magnitude higher.
Thirdly, the problem needs to be looked at more broadly.
The fact that the php code falls out when a syntax error or lack of memory occurs is mega bad. The solution is to override the error handling functions - Ihmo, a crutch, but sometimes you can’t do without it.
How to make such a crutch is described almost in the question itself. For myself, I understood the essence of the discussion as follows - and how you need to build the application logic (architecture - if you want), so that it is correct from the point of view of error handling.
The example with HTTP errors and statuses is not the only one. Other options are also possible.
Here's the idea. What and how to do depends on your project. This may not be a complex site that gives information, but it may be a rather complex complex in which business logic is embedded, it can work both when receiving pages and when performing background tasks (for example, scripts in CronTab).
In a nutshell, then I think like this:
- break the logic code into blocks (preferably independent, they can be hierarchical).
- run these blocks as if "in a sandbox" ( the question of how depends on the application itself, someone may not succeed, or for this you will have to change the structure of the database, some algorithms inside the application, etc.)
- on successful execution of the block, apply the result. Something broke, we notify the admin about a critical situation.
Critical situations are best caught with Unit tests, and if this is not possible, then with all types of testing.
Something like that.

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question