How to stop php script execution on php-fpm/nginx? - php

Unfortunately calling php exit() on php-fpm/nginx configuration does not stop the script immediately while file handles might not be closed.
Some developers suggest calling fastcgi_finish_request() but also this does not stop the script.
Others suggest wrapping all code in a catch block:
<?php
class SystemExit extends Exception {}
try {
/* PUT ALL THE CODE HERE */
} catch (SystemExit $e) { /* do nothing */ }
?>
and throwing an exception where code stop is needed:
if (SOME_EXIT_CONDITION)
throw new SystemExit(); // instead of exit()
This would mean editing all php files to include try/catch block and seems tedious to me.
Are there any other clean solutions?

So we found out that it's a register_shutdown_function callback that prevents your script from exiting immediately.
PHP shutdown function is designed to be called on any script shutdown when possible. But it has a feature: if one of shutdown callbacks calls exit — script is exiting without calling any other callbacks.
So if you really want to skip a shutdown function in some cases, you should register some killer-function as a very first shutdown callback. Inside that killer-function you will check a kind of singleton for state: do we want to exit? — call exit(), otherwise — return.
<?php
function killer_function() {
if ($someGlobalThing->needToExitRightNow()) {
exit();
}
}
register_shutdown_function('killer_function');
// ...
function exit_now() {
$someGlobalThing->exitNow();
exit();
}
($someGlobalThing can be a singleton or some super-global variable or a global registry or whatever you like)
Then calling exit_now() will do the trick.

Related

How to call error_handler automatically?

From my previous question I understand that isn't a good practice manage each exception with try/catch block, 'cause if I've hundred functions I should put hundred try/catch block.
Now I noticed the set_error_handler function, if I've understand correctly (never used it), allow me to swith in a file or function all the error generated in the whole scripts. So instead of put try catch block this function should automatically intercept the error and call a function, is right?
Now I already have a Log class that help me to write a stack trace in a file. This file is daily so I can see all system transaction in separated file.
My Log class is a SingleTon, so in each classes if I want write some trace in the log file I just need to do this:
Log::warning('some parameter here');
My goal is create an error.php file where all the error are switched in the Log::warning('...');. I think that this set_error_handler should be placed in the system core. As I said I never worked with it, someone could help me to achieve this with a bit example? I'll be glad.
set_error_handler is used to handle errors in a script not exceptions.
If you want to catch all exceptions from your application to apply the same process you have to call set_exception_handler PHPDoc.
This function takes a callable in argument, so your handler must be defined in another function.
The main difference between this function and a try catch block is that
Execution will stop after the exception_handler is called.
It's a also good practice to keep existing exception handlers possibly introduce by an included lib.
You can create a class to do this
class ErrorHandler
{
private $previousExceptionHandler;
public function registerExceptionHandler($callPrevious = true)
{
$prev = set_exception_handler(array($this, 'handleException'));
if ($callPrevious && $prev) {
$this->previousExceptionHandler = $prev;
}
}
public function handleException(\Exception $e)
{
// DO YOUR STUFF
if ($this->previousExceptionHandler) {
call_user_func($this->previousExceptionHandler, $e);
}
}
}
And to use it
$errorHandler = new ErrorHandler();
$errorHandler->registerExceptionHandler();
There are some good libs to do that and more, especially if you want to catch your exceptions for logging purpose. You can try the excellent Monolog lib wihich is widely used, and its ErrorHandler class

How to return false from a PHP script from inside a class/function?

How can I make the main PHP script return false from inside a class or a function?
Why: this is because of the built-in webserver:
If a PHP file is given on the command line when the web server is started it is treated as a "router" script. The script is run at the start of each HTTP request. If this script returns FALSE, then the requested resource is returned as-is.
from the documentation about the PHP Built-in webserver
In other words, you return false in your router script so that the built-in webserver can serve static files. Example from the documentation:
if (preg_match('/\.(?:png|jpg|jpeg|gif)$/', $_SERVER["REQUEST_URI"])) {
return false; // serve the requested resource as-is.
} else {
echo "<p>Welcome</p>";
}
The thing is that I'm trying to add that behavior to a web framework: I don't want to write that into index.php. I'd rather encapsulate that logic into a class (middleware) that will halt the script's execution if php_sapi_name() == 'cli-server' and a static asset is asked.
However, I don't know how I can make the whole PHP script return false from a class or a function, since obviously return false will return from the current method/function/file and not from the main file.
Is there a way to achieve the same behavior with exit() for example? I realize I don't even know what return false in the main file actually means (is that a specific exit code?).
You should have the router invoke the class method, and then, if the method returns false, you return false from your router file.
Of course it can turn into a headache. There are basically only two methods to achieve what you want to achieve.
There is a faster way though, you can abuse exceptions and create a specialized exception for the case:
StaticFileException.php
<?php
class StaticFileException extends Exception {}
router.php
<?php
try {
$c = new Controller();
return $c->handleRequest();
} catch (StaticFileException $e) {
return false;
}
Once you have this kind of code in place, just throw new StaticFileException and you're done.
If the method in your class handles static assets by using exit then the solution can be as simple as replacing exit with return false and having the caller of that method simply return the method in the global scope as well.
So if your class looks something like this...
class Router
{
public function handleRequest($uri)
{
if (is_file($this->docRoot . $uri->path)) {
exit; // static file found
} else {
// handle as normal route
}
}
}
Just replace exit there with return false ...
return false; // static file found
Then if your index.php works something like this...
$router = new Router($docRoot);
$router->handleRequest($_SERVER['REQUEST_URI']);
Simply add a return infront of the handleRequest method like so....
return $router->handleRequest($_SERVER['REQUEST_URI']);
This should have minimal side-effects on your framework design and as you can see requires very little code and refactoring because returning from the script's global scope only has a single side-effect in PHP (in that it returns the value to calling script i.e. if you used include/require as an expression in an assignment). In your case if index.php is the calling script then you have nothing to worry about here just by adding return infront of that method.
Of course, once you return the rest of the script will not continue so make sure it is the last statement in your index.php. You can even just assign the return value to a temporary value and return later if you needed for logic....
$router = new Router($docRoot);
if ($router->handleRequest($_SERVER['REQUEST_URI'])) {
/* if you need to do anything else here ... */
} else {
return false; // otherwise you can return false for static here
}
In general I would say that calling exit from inside of a function/method is almost never desirable. It makes your class harder to test and debug and really has no upside from the alternatives like throwing an exception, or just returning from the method, and letting the caller handle the failure scenarios gracefully.
Isn't possible to play with register_shutdown_function or with auto_append_file setting to deal with that? Not really nice but maybe that can do the job.

Interesting behavior of __construct() and __destruct() in PHP

I was testing a class whilst developing my app and just like any other quick-lazy-method-tweak I did an echo in
function __construct(){
parent::__construct();
echo "yaba daba doo";
exit();
}
Now, after few tests and all, I thought of checking if the class has been fully compiled thus I wrote:
function __destruct(){
echo "ends here";
exit();
}
Interestingly, even though there was an exit in __construct , it still executed __destruct!
As per my knowledge exit 'kills' the process, doesn't it?
__destruct is called upon when the object has no more reference for the class.
Does that mean, exit(0) does not kill the process on priority?
Is it a PHP bug? because, IMO the script shouldn't go any further?
It's all in the manual.
The destructor will be called even if script execution is stopped using exit(). Calling exit() in a destructor will prevent the remaining shutdown routines from executing.

Exit PHP script w/o error

I have a PHP script that registers approximately 20 functions. Ideally it'd be loaded with require_once. I want make it so that even if somehow it does get loaded more than once, it wouldn't try to re-register the functions. I could wrap each declaration in !function_exists, but I'm wondering if there's a faster way. (Or do you think 20 calls to !function_exists is negligible?) I tried using exit but that causes an error. Is it possible to exit w/o throwing an error ?
if ( /* already loaded */ ) {
/* leave gracefully */
}
/* declare ~20 functions */
if (!defined('ALREADY_LOADED')) {
define('ALREADY_LOADED', true);
// rest of your code goes here
}
This checks to see if a constant ALREADY_LOADED is defined, and if it is not, it defines the constant and will do whatever else you want.
After it's been run once, the constant will be defined, and it will not run a second time.
I suggest using a more descriptive constant name than ALREADY_LOADED.
I think using OOP is the most practical way
Can be used as a class with static functions
class SimleClass{
public static function func1($arg){...}
}
use it
SimpeClass::func1($arg);
or sigleton
class SimleClass{
protected static $_instance=null;
private function __construct(){...}
public static function getInstance(){
if(is_null(self::$_instance)
self::$_instance=new SimpleClass();
return self::$_instance;
}
public function func1($arg){...}
}
use it
SimpleClass::getInstance()->func1($arg);
and how to connect it think itself
can use include_once
and can implement the autoloader class
function myClassLoader($className){
include_once "/lib/$className.php";
}
spl_autoload_register(myClassLoader);
=============================================================
for more concrete answer requires more data.
Perhaps your application, you can rearrange to get the more logical structure
If you as an author don't require it more then once, (or use require_once() consistantly) there shouldn't be a problem about re-registering functions.
As for exiting without an error, try die().
I would try not to redeclare functions in first step (here require more than once).
Doing 20x function_exists() does not cost anything, only some time writing and wrapping ;)
Perhaps you could try to use classes/methods instead.
function_exists is no bad.
You can write your php script files in the following manner.
/* file1.php */
if(!defined('FILE1')) {
define('FILE1', 1);
/////////////////////////
// PUT file contents here.
/////////////////////////
}// file.php finished.
Now you can include this file as many time as you want here and there. Nothing will be redefined again.

How do you use PHPUnit to test a function if that function is supposed to kill PHP?

Essentially I have a method of a class called killProgram, which is intended to send a hTTP redirect and then kill PHP.
How am I supposed to test this? When I run phpunit it doesn't return anything for that test, and closes completely.
Right now I'm considering having the killProgram function throw an exception which shouldn't get handled, which would allow me to assert that an exception was thrown.
Is there a better way?
It's obviously an old question but my suggestion would be to move the code that die()'s into a separate method that you can then mock.
As an example, instead of having this:
class SomeClass
{
public function do()
{
exit(1);
// or
die('Message');
}
}
do this:
class SomeClass
{
public function do()
{
$this->terminate(123);
// or
$this->terminate('Message');
}
protected function terminate($code = 0)
{
exit($code);
}
// or
protected function terminate($message = '')
{
die($message);
}
}
That way you can easily mock the terminate method and you don't have to worry about the script terminating without you being able to catch it.
Your test would look something like this:
class SomeClassTest extends \PHPUnit_Framework_TestCase
{
/**
* #expectedExceptionCode 123
*/
public function testDoFail()
{
$mock = $this->getMock('SomeClass');
$mock->expects($this->any())
->method('terminate')
->will($this->returnCallback(function($code) {
throw new \Exception($code);
}));
// run to fail
$mock->do();
}
}
I haven't tested the code but should be pretty close to a working state.
As every tests are run by the same PHPUnit process, if you use exit/die in your PHP code, you will kill everything -- as you noticed ^^
So, you have to find another solution, yes -- like returning instead of dying ; or throwing an exception (you can test if some tested code has thrown an expected exception).
Maybe PHPUnit 3.4 and it's --process-isolation switch (see Optionally execute each test using a separate PHP process) might help (by not having everything dying), but you still wouldn't be able to get the result of the test, if PHPUnit doesn't get the control back.
I've had this problem a couple of times ; solved it by returning instead of dying -- even returning several times, if needed, to go back "high enough" in the call stack ^^
In the end, I suppose I don't have any "die" anymore in my application... It's probably better, when thinking about MVC, btw.
There's no need to change the code just to be able to test it, you can simply use set_exit_overload() (provided by test_helpers from same author as PHPUnit).
I realise you've already accepted an answer for this and it's an old question, but I figure this might be useful for someone, so here goes:
Instead of using die(), you could use throw new RuntimeException() (or an exception class of your own), which will also halt program execution (albeit in a different fashion) and use PHPUnit's setExpectedException() to catch it. If you want your script to die() when that exception is encountered, printing absolutely nothing up at level of the user, take a look at set_exception_handler().
Specifically, I'm thinking of a scenario in which you'd place the set_exception_handler()-call into a bootstrap file that the tests don't use, so the handler won't fire there regardless of scenario, so nothing interferes with PHPUnit's native exception handling.
This relates to set of issues I've been having getting some legacy code to pass a test. So I've come up with a Testable class like this...
class Testable {
static function exitphp() {
if (defined('UNIT_TESTING')) {
throw new TestingPhpExitException();
} else {
exit();
}
}
}
Now I simply replace calls to exit() with Testable::exitphp().
If it's under test I just define UNIT_TESTING, in production I don't. Seems like a simple Mock.
You can kill the script or throw an exception, depending on the value of an environmental variable...
So you kill in production or throw an exception in test environment.
Any call to die or exit, Kills the whole process...
This was supposed to be a comment but I can't comment with the level of my reputation points.

Categories