开发者

PHP robust include to handle errors?

I have a PHP app "index.php" that, for various reasons, needs to run other PHP scripts by using include_once on that other script. That other script isn't very stable, so is there some way to do a safe include_once that won't halt the caller?

i.e.:

<?php

safely_include_once('badfile.php'); // MAY throw syntax error, parse error, other badness
echo "I can continue execution after error";

?&开发者_C百科gt;

(I know what a bad idea this can be, etc., rest assured this isn't production-environment stuff.)


None of these answers will work. The top-voted one, which says to @include(), will still terminate the first script if there is a parse error in the included file.

You can't eval() it, you can't try/catch it, and every way of calling include or require will terminate all execution in the script.

This question remains OPEN and UNSOLVED.

http://bugs.php.net/bug.php?id=41810

This is a bug in PHP, and this functionality's been missing since at least 2006. They classified the bug as "bogus" because, they claim, includes() and requires() happen at compile-time.

This goes out the window if you are generating the string arguments to include() and/or require() at RUNTIME, or doing an eval() over a string containing code that runs an include().


The only real solution, thought not the most elegant one but works is to call in another php interpretor that parses and execute the script for nothing else than checking if it yields a parse error or a No errors detected message to filter error messages like PHP Parse Error blablabla. like this code does :

<?php
function ChkInc($file){
   return file_exists($file) && (substr(exec("php -l $file"), 0, 28) == "No syntax errors detected in");
}
?>

This idea came from gillis' comment to include function manual page at PHP doc.


You can just

@include "fileWithBadSyntax.php";

Which, from my quick tests, works for both parse errors or errors thrown with trigger_error().

EDIT: This is completely wrong. See sneak's answer, which is correct, if somewhat unhelpful.


Would it be possible to change your architecture and turn "badfile.php" into a web service? Instead of including it directly into your codebase, you would call it over the network and parse or include its output. This will get you around parse errors, you could also avoid potentially malicious code if you have badfile.php's environment limited appropriately (using safe_mode, or running a separate web server process with limited privileges).


You could do a try/catch block

<?php
try {
    include("someFile.php");
} catch (Exception $e) {
    // Should probably write it to a log file, but... for brevity's sake:
    echo 'Caught exception: ',  $e->getMessage(), "\n";
}
?>

Now, it will only include the file if there is no error. If there is an error, it will just skip this stuff and write the exception (like, in the comments, preferably to a log file or something).

For more info: http://us2.php.net/manual/en/language.exceptions.php


Graceful workaround for include-file-not-found ....

function safe_require( $incfile, $showfn=1 ) {
  $a = explode( ":", get_include_path() ) ;
  $a[] = "";
  $b = 0;
  foreach( $a as $p ) {
    if( !empty( $p )) $p .= "/";
    $f = $p.$incfile;
    if( file_exists( $f )) {
      $b = 1;
      break;
    }
  }
  if( !$b ) 
    exit( "Cannot proceed, required file " . 
          (($showfn) ? $incfile : "" ) . " is unavailable or server has timed out." 
        );
  require_once( "$f" );
}


See the second answer, @sneak is right, you cant catch requires / includes.

My solution for autoload require was to use error_get_last, so just add this in the base file (this will then handle parse errors from requires/include included by things such as auto-load / child classes:

<?php 
    register_shutdown_function( "fatal_handler" );
    function fatal_handler() 
    {
        $error = error_get_last();  
        if($error !== NULL) 
        {
            if(isset($error['type']) && ($error['type'] === E_ERROR  || $error['type'] === 1 || $error['type'] === 2 || $error['type'] === 3 || $error['type'] === 4))
            {
                // do what you need to do here to make safe, I just log and use that log (to make sure other tests dont run in my case)
                Some_Class::logErrorToFile('fatal_error:' . json_encode($error));
                file_put_contents('somp_file_path_that_all_other_classes_check', 'fatal_error:' . json_encode($error));
            }
            else
            {
                Some_Class::logErrorToFile('error:' . json_encode($error)); 
            }
        }
        return;
    }

spl_autoload_register(function ($classname) {
    $classPath = str_replace("_", "/", $classname);
    $path = dirname(__FILE__) . '/../'.$classPath.'.php';
    require_once($path);  // fatal errors are logged for this runtime require, will not actually stop execution for this case, but we can handle how we deal with that now      
});


In badfile.php you could make it return a value:

<?php
//inestable instructions.


return true; ?>

then on main file you could do:

 <?php

 if (require($badFile)) {
     //if it was true continue with normal execution
 } else {
     echo "Error: ". error_get_last();
 }


Here's the only real solution I've been able to find:

function safe_include($fn) {
        $fc = file_get_contents($fn);
        if(!eval($fc)) {
                return 0;
        }
        return 1;
}

Note that the above code means you have to take out the opening statements from your to-be-included files, or do the following:

eval("?>" . $fc)

The problem is that you can't call require() or include() or their _once() variants at any point and expect them not to terminate everything, including any error handlers. PHP will completely stop processing everything when it encounters a parse error.

The only exception to this is the string inside of an eval(). The problem is, though, that you can't really do this:

eval('require($fn);'); 

...because the require() inside the eval'd string will still stop. You have to read in the contents, pray that the file in question doesn't include() or require() further, and eval() it.

The real solution? Skip PHP. :/

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜