function rseo_get_seo($check, $post){
//code breaks somewhere in here. or in the rseo_doTheParse function.
switch ($check)
{
case "h1": return rseo_doTheParse('h1', $post);
case "h2": return rseo_doTheParse('h2', $post);
case "h3": return rseo_doTheParse('h3', $post);
case "img-alt": return rseo_doTheParse('img-alt', $post);
}
}
function rseo_doTheParse($heading, $post){
try { //I get a FATAL error here. unexpected '{'
$content = $post->post_content;
if ($content == "") return false;
$keyword = trim(strtolower(rseo_getKeyword($post)));
#$dom = new DOMDocument;
#$dom->loadHTML(strtolower($post->post_content));
$xPath = new DOMXPath(#$dom);
switch ($heading)
{
case "img-alt": return $xPath->evaluate('boolean(//img[contains(#alt, "'.$keyword.'")])');
default: return $xPath->evaluate('boolean(/html/body//'.$heading.'[contains(.,"'.$keyword.'")])');
}
}
catch (Exception $e)
{
echo 'Exception caught: ', $e->getMessage(), "\n";
}
}
The only thing I can think of is that you're on PHP 4, which doesn't support exception handling. So it thinks try is some kind of constant, but doesn't expect a { to be there.
You should have gotten a parse error, not a fatal error.
That code is 100% valid. Perhaps the error is elsewhere. On a side note, DOM functions don't throw exceptions--you might want to look into libxml_use_internal_errorsand set it up to throw exceptions.
I've pasted the code in a new file and ran it: no error. The problem might be above your code?
Line 14 after the switch block. Remove the second } just before the catch block
Related
I have an HTML string mixed with PHP codes. so i want to just do evaluation of the codes that it possible and replace it back there. My idea is something like this :
$html='Hi it <b>PAPION</b>. Now timestamp is <?php echo time(); ?>. have a good time.';
$html = preg_replace_callback('/(<\?php)(.*?)(\?>)/ims',function($matches){
try {
ob_start();
eval($matches[2]);
return ob_get_clean();
} catch(Exception $e) {
return "";
}
}, $html);
and it works fine.
But if my code have an error, like this:
$html='Hi it <b>PAPION</b>. Now timestamp is <?php echo xxtime(); ?>. have a good time.';
$html = preg_replace_callback('/(<\?php)(.*?)(\?>)/ims',function($matches){
try {
ob_start();
eval($matches[2]);
return ob_get_clean();
} catch(Exception $e) {
return "";
}
}, $html);
instead of just leaving the place blank, it will make the $html string blank.
PHP >5.4
any way to handle it?
Best regards!
you set $html to "" in catch . must change your code like follow :
$html='Hi it <b>PAPION</b>. Now timestamp is <?php echo xxtime(); ?>. have a good time.';
preg_match('/(<\?php)(.*?)(\?>)/ims', $html, $matches);
$html = str_replace($matches[2], getEvalOutput($matches[2]), html);
echo "html is ".$html;
function getEvalOutput($matche){
try {
ob_start();
eval($matche);
return ob_get_clean();
} catch(Exception $e) {
return " ERROR happened";
}
}
you get fatal error and you must handle somehow that; this is just a test code for you to understand the way:
<?php
$phpCode = "
<?php
set_error_handler('myErrorHandler');
register_shutdown_function('fatalErrorShutdownHandler');
function myErrorHandler(\$code, \$message, \$file, \$line) {
echo \$message;
}
function fatalErrorShutdownHandler()
{
\$last_error = error_get_last();
if (\$last_error['type'] === E_ERROR) {
// fatal error
myErrorHandler(E_ERROR, \$last_error['message'], \$last_error['file'], \$last_error['line']);
}
}
\$html='Hi it <b>PAPION</b>. Now timestamp is <?php echo xxtime(); ?>. have a good time.';
\$html = preg_replace_callback('/(<\?php)(.*?)(\?>)/ims',function(\$matches){
try {
ob_start();
eval(\$matches[2]);
return ob_get_clean();
} catch(Exception \$e) {
}
}, \$html);
echo \$html;";
file_put_contents('a.php', $phpCode);
$x = exec( 'php a.php');
echo $x;
/* delete the file */
unlink('a.php');
As #user122293 said, the problem is about difference between fatal errors and other errors.
In PHP <7 fatal errors are not catch-able.
Also, there is no any Exception for fatal errors even in PHP >=7
Now we have some ways (from best to worst):
A) Making sure we use PHP 7 or higher and change the code to something like this :
$html='Hi it is <b>PAPION</b>. Now timestamp is <?php echo time(); ?>. have a good time. a can be: <?php a=b*2 ?>. and wrong timestamp is <?php xxxxtime(); ?>.';
$html = preg_replace_callback('/(<\?php)(.*?)(\?>)/ims',function($matches){
try {
ob_start();
eval($matches[2]);
return ob_get_clean();
} catch(Throwable $e) {
return "";
}
}, $html);
echo $html;
to output will be:
Hi it is PAPION. Now timestamp is 1570282086. have a good time. a can be: . and wrong timestamp is .
the important part in my case was using Throwable / Error in catch. as the explanation here: https://stackoverflow.com/a/48381661/7514010
B) & C) If we have access to exec function. we try the code first, then use it. or even save the file temporary and execute it for test. (Look crazy!)
or
Send the code as a query to a tester and get the result and then run it. (It can not work because some PHP code tags can depend to each other and should evaluate in same script.)
Both explained here : https://stackoverflow.com/a/33531067/7514010
D) Using register_shutdown_function + crazy way to force PHP 5 to at last have get the html again or even run next lines.
Like this example :
https://3v4l.org/0R7dq
+E) Make a function for parsing manually before eval or include using regex parsing and function_exists or method_exists or etc... or any library.
I'm parsing RSS feeds and have a (sub)function that looks like this:
function nSpace($nSp, $type, $i){
$namespaces = $i->getNameSpaces(true);
$exp = explode(':', $nSp);
try{
$nameSp = $i->children($namespaces[$exp[0]]);
$item[$type] = (string)$nameSp->$exp[1];
return $item[$type];
}
catch (Exception $e) { }
}
I'm trying to retrieve the namespace value, and will pass common RSS feed namespaces like "dc:date" or "content:encoded" for example as $nSp. The function works fine should the namespace exist in the XML, however the try{} is producing an 'Undefined Index' error in the first line when it doesn't.
Personally I'd rather run an isset($i->children($namespaces[$exp[0]])){} check instead of the try/catch, as I'm more familiar with that workflow, however this doesn't work (get a 'Can't use return value' error).
Few questions:
Shouldn't the try{} not produce error messages?
Is try/catch the best way?
Is there a way to do it with an if() instead?
Thanks.
Update:
Here is the (abreviated) call/usage for this function:
$rawFeed = file_get_contents($url);
try { $rss = new SimpleXMLElement($rawFeed); } catch (Exception $e) { }
foreach ($rss->channel->item as $i) {
$item['link'] = isset($i->link) ? (string)$i->link : nSpace('dc:link', 'link', $i);
$item['dateRaw'] = isset($i->pubDate) ? (string)$i->pubDate : nSpace('dc:date', 'date', $i);
// etc...
}
I want to replace the list of warnings and errors with a simple error banner. I'm trying to check if this code produce errors and if so output a custom error
$sxml = simplexml_load_file($yurl)
I played around with the try catch block but I just can't seem to get it right, any help will be appreciated.
Use libxml_use_internal_errors()
<?php
libxml_use_internal_errors(true);
$sxml = simplexml_load_file($yurl);
if (!$sxml) {
foreach (libxml_get_errors() as $error) {
// Custom error banner here
switch ($error->level) {
case LIBXML_ERR_WARNING:
$return .= "Warning $error->code: ";
break;
case LIBXML_ERR_ERROR:
$return .= "Error $error->code: ";
break;
case LIBXML_ERR_FATAL:
$return .= "Fatal Error $error->code: ";
break;
}
}
//clears libxml error buffer
libxml_clear_errors();
}
?>
libxml_get_errors() returns an array of libXMLError objects.
You can only catch exceptions, not errors.
Use set_error_handler() to replace PHP's default error handler with your own function.
I'd like to be able to catch die() and exit() messages. Is this possible? I'm hoping for something similar to set_error_handler and set_exception_handler. I've looked at register_shutdown_function() but it seems to contain no context for the offending die() and exit() calls.
I realize that die() and exit() are bad ways to handle errors. I am not looking to be told not to do this. :) I am creating a generic system and want to be able to gracefully log exit() and die() if for some reason someone (not me) decides this is a good idea to do.
Yes you can, but you need ob_start, ob_get_contents, ob_end_clean and register_shutdown_function
function onDie(){
$message = ob_get_contents(); // Capture 'Doh'
ob_end_clean(); // Cleans output buffer
callWhateverYouWant();
}
register_shutdown_function('onDie');
//...
ob_start(); // You need this to turn on output buffering before using die/exit
#$dumbVar = 1000/0 or die('Doh'); // "#" prevent warning/error from php
//...
ob_end_clean(); // Remember clean your buffer before you need to use echo/print
According to the PHP manual, shutdown functions should still be notified when die() or exit() is called.
Shutdown functions and object destructors will always be executed even if exit() is called.
It doesn't seem to be possible to get the status sent in exit($status). Unless you can use output buffering to capture it, but I'm not sure how you'd know when to call ob_start().
Maybe override_function() could be interesting, if APD is available
As best as I can tell this is not really possible. Some of the solutions posted here may work but they require a lot of additional work or many dependencies. There is no way to easily and reliable trap the die() and exit() messages.
Why do not use custom error handling instead? If not, you could always use LD_PRELOAD and C Code injection to catch it :) Or recompile php with your customizations :P
If you use the single point of entry method. (index.php) I can recommend this for your error handling:
Short version:
ob_start();
register_shutdown_function('shutdownHandler');
include('something');
define(CLEAN_EXIT, true);
function shutdownHandler() {
if(!defined("CLEAN_EXIT") || !CLEAN_EXIT) {
$msg = "Script stopped unexpectedly: ".ob_get_contents();
//Handle premature die()/exit() here
}
}
Additional steps and more detailed:
Roughly my way of doing it. I have even more going on than I show here (handling database transactions/rollback/sending e-mails/writing logs/displaying friendly error messages/user error reporting/etc), but this is the basic idea behind all of it).
Hope it helps someone.
<?php
//Some initialization
//starting output buffering. (fatalErrorHandler is optional, but I recommend using it)
ob_start('fatalErrorHandler');
//Execute code right at the end. Catch exit() and die() here. But also all other terminations inside PHPs control
register_shutdown_function('shutdownHandler');
//handling other errors: Also optional
set_error_handler('errorHandler');
try {
//Include of offensive code
include(...);
}
catch (Exception $ex) {
//Handling exception. Be careful to not raise exceptions here again. As you can end up in a cycle.
}
//Code reached this point, so it was a clean exit.
define(CLEAN_EXIT, true);
//Gets called when the script engine shuts down.
function shutdownHandler() {
$status = connection_status();
$statusText = "";
switch ($status) {
case 0:
if (!defined("CLEAN_EXIT") || !CLEAN_EXIT) {
$msg = "Script stopped unexpectedly: ".ob_get_contents();
//Handle premature die()/exit() here
}
else {
//Clean exit. Just return
return;
}
case 1: $statusText = "ABORTED (1)"; break;
case 2: $statusText = "TIMEOUT (2)"; break;
case 3: $statusText = "ABORTED & TIMEOUT (3)"; break;
default : $statusText = "UNKNOWN ($status)"; break;
}
//Handle other exit variants saved in $statusText here ob_get_contents() can have additional useful information here
}
// error handler function (This is optional in your case)
function errorHandler($errno, $errstr, $errfile, $errline) {
$msg = "[$errno] $errstr\nOn line $errline in file $errfile";
switch ($errno) {
case E_ERROR: $msg = "[E_ERROR] ".$msg; break;
case E_WARNING: $msg = "[E_WARNING] ".$msg; break;
case E_PARSE: $msg = "[E_PARSE] ".$msg; break;
case E_NOTICE: $msg = "[E_NOTICE] ".$msg; break;
case E_CORE_ERROR: $msg = "[E_CORE_ERROR] ".$msg; break;
case E_CORE_WARNING: $msg = "[E_CORE_WARNING] ".$msg; break;
case E_COMPILE_ERROR: $msg = "[E_COMPILE_ERROR] ".$msg; break;
case E_COMPILE_WARNING: $msg = "[E_COMPILE_WARNING] ".$msg; break;
case E_USER_ERROR: $msg = "[E_USER_ERROR] ".$msg; break;
case E_USER_WARNING: $msg = "[E_USER_WARNING] ".$msg; break;
case E_USER_NOTICE: $msg = "[E_USER_NOTICE] ".$msg; break;
case E_STRICT: $msg = "[E_STRICT] ".$msg; break;
case E_RECOVERABLE_ERROR: $msg = "[E_RECOVERABLE_ERROR] ".$msg; break;
case E_DEPRECATED: $msg = "[E_DEPRECIATED] ".$msg; break;
case E_USER_DEPRICIATED: $msg = "[E_USER_DEPRICIATED] ".$msg; break;
default: $msg = "[UNKNOWN] ".$msg; break;
}
//Handle Normal error/notice/warning here.
$handled = ...
if ($handled)
return true; //handled. Proceed execution
else
throw Exception($msg); //Be careful. this might quickly become cyclic. Be sure to have code that catches and handles exceptions. Else die() here after logging/reporting the error.
}
function fatalErrorHandler(&$buffer) {
$matches = null;
//Checking if the output contains a fatal error
if (preg_match('/<br \/>\s*<b>([^<>].*)error<\/b>:(.*)<br \/>$/', $buffer, $matches) ) {
$msg = preg_replace('/<.*?>/','',$matches[2]);
//Handle Fatal error here
return "There was an unexpected situation that resulted in an error. We have been informed and will look into it."
}
//No fatal exception. Return buffer and continue
return $buffer;
}
Catching exits is useful in automated tests. The way I do it is I throw a special runtime exception instead of calling exit directly.
<?php
class CatchableExit extends RuntimeException
{
}
class Quitter
{
public function run($i)
{
echo "Quitter called with \$i = $i \n";
throw new CatchableExit();
}
}
class Runner
{
public function run()
{
trigger_error('I am a harmless warning', E_USER_WARNING);
trigger_error('And I am a notice', E_USER_NOTICE);
for ($i = 0; $i < 10; $i++) {
$q = new Quitter();
try {
$q->run($i);
} catch (CatchableExit $e) {
}
}
}
}
function exception_handler(Throwable $exception)
{
if ($exception instanceof CatchableExit) {
exit();
}
}
set_exception_handler('exception_handler');
$runner = new Runner();
$runner->run();
yes: write a function and use that instead.
function kill($msg){
// Do your logging..
exit($msg);
}
Disclaimer; I'm fully aware of the pitfalls and "evils" of eval, including but not limited to: performance issues, security, portability etc.
The problem
Reading the PHP manual on eval...
eval() returns NULL unless return is
called in the evaluated code, in which
case the value passed to return is
returned. If there is a parse error in
the evaluated code, eval() returns
FALSE and execution of the following
code continues normally. It is not
possible to catch a parse error in
eval() using set_error_handler().
In short, no error capture except returning false which is very helpful, but I'm sur eI could do way better!
The reason
A part of the site's functionality I'm working on relies on executing expressions. I'd like not to pass through the path of sandbox or execution modules, so I've ended using eval. Before you shout "what if the client turned bad?!" know that the client is pretty much trusted; he wouldn't want to break his own site, and anyone getting access to this functionality pretty much owns the server, regardless of eval.
The client knows about expressions like in Excel, and it isn't a problem explaining the little differences, however, having some form of warning is pretty much standard functionality.
This is what I have so far:
define('CR',chr(13));
define('LF',chr(10));
function test($cond=''){
$cond=trim($cond);
if($cond=='')return 'Success (condition was empty).'; $result=false;
$cond='$result = '.str_replace(array(CR,LF),' ',$cond).';';
try {
$success=eval($cond);
if($success===false)return 'Error: could not run expression.';
return 'Success (condition return '.($result?'true':'false').').';
}catch(Exception $e){
return 'Error: exception '.get_class($e).', '.$e->getMessage().'.';
}
}
Notes
The function returns a message string in any event
The code expression should be a single-line piece of PHP, without PHP tags and without an ending semicolon
New lines are converted to spaces
A variable is added to contain the result (expression should return either true or false, and in order not to conflict with eval's return, a temp variable is used.)
So, what would you add to further aide the user? Is there any further parsing functions which might better pinpoint possible errors/issues?
Chris.
Since PHP 7 eval() will generate a ParseError exception for syntax errors:
try {
$result = eval($code);
} catch (ParseError $e) {
// Report error somehow
}
In PHP 5 eval() will generate a parse error, which is special-cased to not abort execution (as parse errors would usually do). However, it also cannot be caught through an error handler. A possibility is to catch the printed error message, assuming that display_errors=1:
ob_start();
$result = eval($code);
if ('' !== $error = ob_get_clean()) {
// Report error somehow
}
I've found a good alternative/answer to my question.
First of, let me start by saying that nikic's suggestion works when I set error_reporting(E_ALL); notices are shown in PHP output, and thanks to OB, they can be captured.
Next, I've found this very useful code:
/**
* Check the syntax of some PHP code.
* #param string $code PHP code to check.
* #return boolean|array If false, then check was successful, otherwise an array(message,line) of errors is returned.
*/
function php_syntax_error($code){
if(!defined("CR"))
define("CR","\r");
if(!defined("LF"))
define("LF","\n") ;
if(!defined("CRLF"))
define("CRLF","\r\n") ;
$braces=0;
$inString=0;
foreach (token_get_all('<?php ' . $code) as $token) {
if (is_array($token)) {
switch ($token[0]) {
case T_CURLY_OPEN:
case T_DOLLAR_OPEN_CURLY_BRACES:
case T_START_HEREDOC: ++$inString; break;
case T_END_HEREDOC: --$inString; break;
}
} else if ($inString & 1) {
switch ($token) {
case '`': case '\'':
case '"': --$inString; break;
}
} else {
switch ($token) {
case '`': case '\'':
case '"': ++$inString; break;
case '{': ++$braces; break;
case '}':
if ($inString) {
--$inString;
} else {
--$braces;
if ($braces < 0) break 2;
}
break;
}
}
}
$inString = #ini_set('log_errors', false);
$token = #ini_set('display_errors', true);
ob_start();
$code = substr($code, strlen('<?php '));
$braces || $code = "if(0){{$code}\n}";
if (eval($code) === false) {
if ($braces) {
$braces = PHP_INT_MAX;
} else {
false !== strpos($code,CR) && $code = strtr(str_replace(CRLF,LF,$code),CR,LF);
$braces = substr_count($code,LF);
}
$code = ob_get_clean();
$code = strip_tags($code);
if (preg_match("'syntax error, (.+) in .+ on line (\d+)$'s", $code, $code)) {
$code[2] = (int) $code[2];
$code = $code[2] <= $braces
? array($code[1], $code[2])
: array('unexpected $end' . substr($code[1], 14), $braces);
} else $code = array('syntax error', 0);
} else {
ob_end_clean();
$code = false;
}
#ini_set('display_errors', $token);
#ini_set('log_errors', $inString);
return $code;
}
Seems it easily does exactly what I need (yay)!
How to test for parse errors inside eval():
$result = #eval($evalcode . "; return true;");
If $result == false, $evalcode has a parse error and does not execute the 'return true' part. Obviously $evalcode must not return itself something, but with this trick you can test for parse errors in expressions effectively...
Good news: As of PHP 7, eval() now* throws a ParseError exception if the evaluated code is invalid:
try
{
eval("Oops :-o");
}
catch (ParseError $err)
{
echo "YAY! ERROR CAPTURED: $err";
}
* Well, for quite a while then... ;)
I think that best solution is
try {
eval(/* ... */);
} catch (Throwable $t) {
//...
}
It catches every error and exception, including Call to undefined function etc
You can also try something like this:
$filePath = '/tmp/tmp_eval'.mt_rand();
file_put_contents($filePath, $evalCode);
register_shutdown_function('unlink', $filePath);
require($filePath);
So any errors in $evalCode will be handled by errors handler.