How handle eval errors inside of preg-replace-callback - php

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.

Related

Display the value of variables in PHP, as soon as they are specified

I want to get about 50 variables from several different sites and show them to user, with PHP Simple HTML DOM Parser.
My Code Structure is something like this:
<?php
include('simple_html_dom.php');
$html = file_get_html('https://site1.com/name1/');
foreach ($html->find('span#name1') as $e) {
$name1 = $e->outertext;
echo $name1;
break;
}
$html = file_get_html('https://site2.com/family1/');
foreach ($html->find('span#family1') as $e) {
$family1 = $e->outertext;
echo $family1;
break;
}
$html = file_get_html('https://site3.com/degree1/');
foreach ($html->find('span#degree1') as $e) {
$height1 = $e->outertext;
echo $height1;
break;
}
$html = file_get_html('https://site4.com/height1/');
foreach ($html->find('span#height1') as $e) {
$height1 = $e->outertext;
echo $height1;
break;
}
// About 30 other blocks of code similar to the above code
?>
At the moment, this code works well and displays the variables, but as expected, it takes about two or three minutes to do this process and show all the variables to the user, which is a lot of time.
Now I'm looking for a way to show to the user (send result to browser), each variable that was found and then find the next variable.
For example, when the value of $name1 is specified, show it to the user and then go to specify the value of $family1
Apparently I should use flush() to fix this problem, but unfortunately I could not find a solution to fix this problem.
You can use ob_flush approach like:
<?php
include('simple_html_dom.php');
$html = file_get_html('https://site1.com/name1/');
foreach ($html->find('span#name1') as $e) {
$name1 = $e->outertext;
echo $name1;
break;
}
flush();
ob_flush();
$html = file_get_html('https://site2.com/family1/');
foreach ($html->find('span#family1') as $e) {
$family1 = $e->outertext;
echo $family1;
break;
}
flush();
ob_flush();
$html = file_get_html('https://site3.com/degree1/');
foreach ($html->find('span#degree1') as $e) {
$height1 = $e->outertext;
echo $height1;
break;
}
flush();
ob_flush();
$html = file_get_html('https://site4.com/height1/');
foreach ($html->find('span#height1') as $e) {
$height1 = $e->outertext;
echo $height1;
break;
}
flush();
ob_flush();
// About 30 other blocks of code similar to the above code
?>
This code after each block flush PHP output to browser.
You will most likely need to architect your app slightly differently. A relatively simple approach would be to to an AJAX call for each variable you need to show, and let the client (browser) decide when to make each call so you can parallelize them. Each call would hit one (and only one) remote URL. This way, you can display results as they come in as AJAX responses.
A slightly more complex approach would be to use Websockets to send back new information as it becomes available. But I think this might be overkill.
In any case both solutions require more browser / JavaScript work than PHP.

How can I check if my function print/echo's something?

I am often using echo to debug function code:
public function MyFunc() {
// some code...
echo "OK";
// some code...
}
How can I check that my function print's/echo's something?
(pseudo code):
MyFunc();
if (<when something was printed>){
echo "You forgot to delete echo calls in this function";
}
This should work for you:
Just call your functions, while you have output buffering on and check if the content then is empty, e.g.
ob_start();
//function calls here
MyFunc();
$content = ob_get_contents();
ob_end_clean();
if(!empty($content))
echo "You forgot to delete echos for this function";
You could create a $debug flag and a debuglog() function, which checks for the debug flag and only then echos the message. Then you can toggle your debug messages on and off from one location.
define('DEBUGMODE', true); // somewhere high up in a config
function debuglog($msg){
if( DEBUGMODE ){ echo $msg; }
}
Should you ever want to get rid of your debug echos, you can search for "debuglog(" and delete those lines of code. This way you won't accidentally delete any echo statements that are required in normal execution or miss any debug echo statements that should really have been removed.
It's the bad way checking if something is echoed.
You can set a variable named is_echoed to 1 or you can return the value
public $is_echoed = 0;
//rest
$this->is_echoed = 1;
or
function myFunc()
{
return "OK";
}
if(myFunc() == 'OK')
//rest
You can use var_dump() and die() to debug your code more efficiently.
$test = "debud test";
public function MyFunc($test)
{
// some code...
var_dump($test); die();
// some code...
}
Reference:
http://php.net/manual/en/function.var-dump.php
http://php.net/manual/en/function.die.php
Why do you want to try such an extensive process of seeing if something has been echoed or not?
For debugging you can definitely use echo to see if the particular block is being hit during a particular use-case. But I would suggest you use flags and return the values to the calling function.
function xyz () {
if (something) return some_value;
else return some_other_value;
}
There is no particular need to have variables and use space in storing a 0 or 1 when you can just return a hard-coded literal.
I would suggest to you to use something like log4php [1]
But if not, I use a function like this:
define('DEBUG', true);
function debug($msg){
if(DEBUG){ echo $msg; }
}
Or something like this to see the log in the browser console:
function debug_to_console( $data ) {
if ( is_array( $data ) )
$output = "<script>console.log( 'Debug Objects: " . implode( ',', $data) . "' );</script>";
else
$output = "<script>console.log( 'Debug Objects: " . $data . "' );</script>";
echo $output;
}

Class to manipulate .ini files in php

I've been playing around with a php class I found on the net called Config Magik that I use to store some info in a INI file but have recently run into some problems when using removeKey. I wanted to know if someone can point me to a similar class that would work as well or better. Or if there is a better way to go about this.
This is my function right now, after playing around with it like crazy, so it is probably very faulty.
require_once('class.ConfigMagik.php');
$config = new ConfigMagik('config.ini', true, true);
if(!empty($_GET)){
if(!is_writeable('config.ini')){
echo 'Could not write to config.ini';
return false;
}
//if there is no section parameter, we will not do anything.
if(!isset($_GET['section'])){
echo false; return false;
} else {
$section_name = $_GET['section'];
unset($_GET['section']); //Unset section so that we can use the GET variable to manipulate the other parameters in a foreach loop.
if (!empty($_GET)){
foreach ($_GET as $var => $value){
echo $var.'='.$_GET[$var].'<br />';
//Check if said variable $var exists in the section.
if($config->get($var, $section_name) !== NULL){
//Set variable value.
try{
$config->set($var, $value, $section_name);
echo 'Setting variable '. $var.' to '.$value.' on section '.$section_name;
} catch(Exception $e) {
echo 'Could not set variable '.$var;
echo $e;
return false;
}
} else {
echo $var.' does not exist <br />';
}
}
}
try{
$section = $config->get($section_name); //Get the entire section so that we can manipulate it.
echo '<pre>';print_r($section);echo '</pre>';
foreach ($section as $title=>$value){
if(!isset($_GET[$title]) && isset($section[$title])){
try{
$config->removeKey($title, $section_name);
echo '<b>'.$title.'</b>: removed <br />';
} catch(Exception $e){
echo $e;
}
}
}
} catch(Exception $e){
echo $e;
}
$config->save();
//echo $config->toString('HTML');
echo true;
return true;
}
} else { RUN SOME HTML }
It basically saves the settings I pass on from the GET parameters and if the parameters are not there it is supposed to delete it. When I get to $config->removeKey($title, $section_name); in the last try catch statement it won't save automatically (as it should), so I tried running $config->save() and I ended up with a ini file that had section = array everywhere. Any advice will be appreciated as I've been learning PHP on the web for the last few weeks so I believe I've got a ways to go.
I have definitely isolated the problem to the $config->save() part, just don't know how to solve it.
Thanks in advance.
I have been using Zend_Config_Ini and Zend_Config_Writer_Ini in the past and was satisfied with the features. You will have extract the whole library/Zend/Config folder from Zend Framework and make Zend_Exception available though.

PHP eval and capturing errors (as much as possible)

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.

Handle error when getimagesize can't find a file

when I'm trying to getimagesize($img) and the image doesn't exist, I get an error. I don't want to first check whether the file exists, just handle the error.
I'm not sure how try catch works, but I want to do something like:
try: getimagesize($img) $works = true
catch: $works = flase
Like you said, if used on a non-existing file, getimagesize generates a warning :
This code :
if ($data = getimagesize('not-existing.png')) {
echo "OK";
} else {
echo "NOT OK";
}
will get you a
Warning: getimagesize(not-existing.png) [function.getimagesize]:
failed to open stream: No such file or directory
A solution would be to use the # operator, to mask that error :
if ($data = #getimagesize('not-existing.png')) {
echo "OK";
} else {
echo "NOT OK";
}
As the file doesn't exist, $data will still be false ; but no warning will be displayed.
Another solution would be to check if the file exists, before using getimagesize ; something like this would do :
if (file_exists('not-existing.png') &&
($data = getimagesize('not-existing.png'))
) {
echo "OK";
} else {
echo "NOT OK";
}
If the file doesn't exist, getimagesize is not called -- which means no warning
Still, this solution is not the one you should use for images that are on another server, and accessed via HTTP (if you are in this case), as it'll mean two requests to the remote server.
For local images, that would be quite OK, I suppose ; only problem I see is the notice generated when there is a read error not being masked.
Finally :
I would allow errors to be displayed on your developpement server,
And would not display those on your production server -- see display_errors, about that ;-)
Call me a dirty hacker zombie who will be going to hell, but I usually get around this problem by catching the warning output into an output buffer, and then checking the buffer. Try this:
ob_start();
$data = getimagesize('not-existing.png');
$resize_warning = ob_get_clean();
if(!empty($resize_warning)) {
print "NOT OK";
# We could even print out the warning here, just as PHP would do
print "$resize_warning";
} else {
print "OK"
}
Like I said, not the way to get a cozy place in programmer's heaven, but when it comes to dysfunctional error handling, a man has to do what a man has to do.
I'm sorry that raise such old topic. Recently encountered a similar problem and found this topic instead a solution. For religious reasons I think that '#' is bad decision. And then I found another solution, it looks something like this:
function exception_error_handler( $errno, $errstr, $errfile, $errline ) {
throw new Exception($errstr);
}
set_error_handler("exception_error_handler");
try {
$imageinfo = getimagesize($image_url);
} catch (Exception $e) {
$imageinfo = false;
}
This solution has worked for me.
try {
if (url_exists ($photoUrl) && is_array (getimagesize ($photoUrl)))
{
return $photoUrl;
}
} catch (\Exception $e) { return ''; }
Simple and working solution based on other answers:
$img_url = "not-existing.jpg";
if ( is_file($img_url) && is_array($img_size = getimagesize($img_url)) ) {
print_r($img_size);
echo "OK";
} else {
echo "NOT OK";
}

Categories