I'm trying to get the value of if condition that make the condition TRUE, I have the following example:
(code.php)
<?php
$a = 'USERNAME';
$b = 'LASTNAME';
if(substr($a, 0, strlen("<SCRIPT"))=== "<SCRIPT Abdalla $a " ) {
}
?>
in the above example, I'm trying to get the value <SCRIPT without double quotations. I tried some steps, but it still get the double quotation with the value (<SCRIPT). I plan then to assign the value <SCRIPT to another variable. Next code shows my code to get the result, where it still not working properly:
(test.php)
<?php
$file='';
$handle = fopen('code.php','r+');
if ($handle) {
while (($buffer=fgets($handle, 4096)) !== false) {
$file.=$buffer."\n";
}
if (!feof($handle)) {
die('error');
}
fclose($handle);
}
//take note of the use of single quotes to wrap this regex, as we do not want PHP parser to eval the $ in the regex string
preg_match_all('/\s*?(\$[\S]+?)\s*?\=(\"?[\S]+?\"?);/',$file,$matches, PREG_SET_ORDER);
$varval=array();
foreach($matches as $match){
$tmp=$match[2];
if(substr($tmp,0,1)=='"' && substr($tmp,-1,1)=='"'){
//evaluate variables in string. we can evaluate with the current object as the variable should be declared first as of proper PHP syntax
$tmp=substr($tmp, 1,-1); //remove quotes
$tmp=replaceFromObject($tmp, $varval);
}
$varval[$match[1]]=$tmp; //we do not need to check if exists, because we should replace with the latest value.
}
/*
The below stores all quoted text and replace them with our own special variable %var0%, %var1%, %var2%.... %varN%.
This is because there could be cases like if("test == 3" == $a) or even worse if("if(test=3)" == $a), which will make regex/parsing tricky if we do not substitute these quoted text out just like a parser/compiler would.
Note: Something I didn't do. We should really first scan the whole text using preg_match to find if there are any text using this format of variables as well, to prevent collisions!
If there are, we can set a loop to check and append some character to our own special variable like %varrN%, %varrrN%, %varrrrN%...
until we find no collisions and it is safe to use. I will leave this simple regex exercise for you to do on your own. Create the regex to find the file for %varN%...
*/
preg_match_all("/\"([\s\S]*?)\"/", $file, $matches, PREG_SET_ORDER);
$stringvars=array();
$key="%var";
$count=0;
foreach($matches as $match){
if(!in_array($match[1], $stringvars)){
$stringvars[$key.$count.'%']=$match[1];
$file=preg_replace("/\"".preg_quote($match[1])."\"/", $key.$count.'%', $file); //take note of the change to preg_quote
$count++;
}
}
// now we parse the whole text for if(subject anycomparator value)
preg_match_all("/if\s*?\(([\s\S]*?)([\=|\>|\<][\=]{0,2})\s*?([\S\s]*?)\s*?\)/", $file, $matches,PREG_SET_ORDER);
$conditionals=array();
foreach($matches as $match){
$conditionals[]=array(
'subject'=>replaceFromObject(replaceFromObject(trim($match[1]),$stringvars),$varval),
//the order does matter, we replace the string first, then the variables, as there might be variables in the double quoted strings which we should evaluate
'comparator'=>$match[2],
'value'=>replaceFromObject(replaceFromObject(trim($match[3]),$stringvars),$varval),
);
}
foreach ($conditionals as $c){
// echo htmlspecialchars($c['subject']).' ';
// echo htmlspecialchars($c['comparator']).' ';
echo htmlspecialchars($c['value']);
echo "<br/>";
}
/* now this function can be used to replace both the quoted strings AND the variables */
function replaceFromObject($s, $obj){
foreach($obj as $key=>$value){
$s=preg_replace('/'.preg_quote($key).'/', $value, $s); //take note of the preg_quote as the string may contain regex keywords
}
return $s;
}
?>
I want a way to print the value that make the if condition be TRUE, which is (
Note: I know the condition here is not TRUE, so that I want to get the value that make the condition TRUE which is <SCRIPT
The purpose is I want to know what is the value that make the condition TRUE, then later will use this value to do test cases on the code based on this value.
Your goal seems oddly specific, but here's a go at addressing your question.
You should not be using file_get_contents to read the actual source code of the php file. You will be getting the generated html equivalent instead. If you are getting the source code, it means you have your allow_url_fopen set to true. This opens your web application up to potential pretty damaging script injection possibilities, unless you know what you are doing and are careful and consistent with your code. You should try using fgets instead. This code will basically do the same as your file_get_contents.
$file='';
$handle = fopen('code.php','r+');
if ($handle) {
while (($buffer=fgets($handle, 4096)) !== false) {
$file.=$buffer."\n";
}
if (!feof($handle)) {
die('error');
}
fclose($handle);
}
There is a bug in your code. if(strpos($value, 'if') != false) will render position 0 as false, which should be true in your case as if is at position 0. The comparator should be strict if(strpos($value, 'if') !== false).
It is returning the quotation because that is what your code is telling it to. $pos1 = strpos($value, '==') + 2 is pointing to 2 characters after the first =, which is technically your last = in your ===. In fact, with your current code, you would be getting = "<SCRIPT" as the result.
Now for the Solution
To solve it just for your case, you can just adjust the position to substr with. After making the amendments mentioned above (especially 2), you will get your condition without the quotes by changing these 2 lines
$pos1 = strpos($value, '===') + 5; // 5 includes the 3 equal signs, a space and the quote
$pos2 = strrpos($value, ')') - 2; // 2 includes the space and the quote
Although this will get the result you want, it might not work for all use cases, as sometimes you might add a space, sometimes you might not. A more robust solution will be to use regex. In your case, it will be something along this line:
foreach ($lines as $line) {
if(preg_match("/if\s*?\([\s\S]*?\=\=\s*?[\"|']([\S\s]*?)[\"|']\s*?\)/", $line, $matches)){
echo htmlspecialchars($matches[1]);
}
}
This regex string /if\s*?\([\s\S]*?\=\=\s*?[\"|']([\S\s]*?)[\"|']\s*?\)/ does what your code aims to do, but in a more robust manner = finds a string with if possibly a space, a (, some text, a comparator == and whatever is in between the quotes - either ' or ".
To have it even more robust to pick up other conditions, strict equal, < or > or <=, >=, you can do something like this.
$conditionals=array();
foreach ($lines as $line) {
if(preg_match("/if\s*?\([\s\S]*?([\=|\>|\<][\=]{0,2})\s*?[\"|']([\S\s]*?)[\"|']\s*?\)/", $line, $matches)){
$conditionals[]=array(
'comparator'=>$matches[1],
'value'=>$matches[2]
);
}
}
foreach ($conditionals as $c){
echo htmlspecialchars($c['comparator']).' ';
echo htmlspecialchars($c['value']);
echo "<br/>";
}
This will work for a file of code.php that might look like this:
//code.php
<?php
$a = 'Data';
if(substr($a, 0, strlen("<SCRIPT"))=== "<SCRIPT" ) {
echo TRUE;
}
if(substr($a, 0, strlen("<SCRIPT"))== "equal" ) {
echo TRUE;
}
if(substr($a, 0, strlen("<SCRIPT"))<= "lesser or equal" ) {
echo TRUE;
}
if(substr($a, 0, strlen("<SCRIPT"))>= "greater or equal" ) {
echo TRUE;
}
if(substr($a, 0, strlen("<SCRIPT"))< "lesser" ) {
echo TRUE;
}
if(substr($a, 0, strlen("<SCRIPT"))>"greater" ) {
echo TRUE;
}
and will return
=== <SCRIPT
== equal
<= lesser or equal
>= greater or equal
< lesser
> greater
Edit to provide even more robust code for non-quoted values...
Another edit to capture variable values and dump them back in.
Another edit for single quotes variables and space between variables
Note:
The order of capturing strings or variables first matters, as if we are not careful we might be caught in a viscous cycle -> there is string in the variable, there is variable in the string, there is a string in the variable in the string of a variable, etc...
The idea is we should capture the variable first, evaluate whatever variables within these variables (if they are a double quoted string), and we do not need to worry about that string-var inception problem.
We then capture the strings -> and then replace the strings THEN replace the variables.
More Notes
What we should really also check for:
Since PHP only evaluates variables in double quotes, we should check if the string was enclosed in double and single quotes before making the decision to evaluate. I have done it for the variables. It could be easily translated to the strings as well - by capturing the quotes as well, then testing the quotes if it is single or double (or any at all). I will leave this to you as a regex exercise.
The other note is that I did this on purpose. :p The current regex for capturing variables works for $a=3;, $a =3; but not $a = 3; or $a = 3 ; or $a= 3 ;, so on. It is easy to add it in, I left it this way so you have a chance to practice your regex skills by adding this simple conditions in. (Edited to add this in) Hope this helps..
$file='';
$handle = fopen('code.php','r+');
if ($handle) {
while (($buffer=fgets($handle, 4096)) !== false) {
$file.=$buffer."\n";
}
if (!feof($handle)) {
die('error');
}
fclose($handle);
}
//take note of the use of single quotes to wrap this regex, as we do not want PHP parser to eval the $ in the regex string
preg_match_all('/\s*?(\$[\S]+?)\s*?\=\s*?(\"?[\S]+?\"?);/',$file,$matches, PREG_SET_ORDER);
$varval=array();
foreach($matches as $match){
$tmp=trim($match[2]);
if(substr($tmp,0,1)=='"' && substr($tmp,-1,1)=='"'){
//evaluate variables in string. we can evaluate with the current object as the variable should be declared first as of proper PHP syntax
$tmp=substr($tmp, 1,-1); //remove quotes
$tmp=replaceFromObject($tmp, $varval);
}else if(substr($tmp,0,1)=='\'' && substr($tmp,-1,1)=='\''){ // remove single quotes
$tmp=substr($tmp, 1,-1);
//no substitution of variables in single quotes just as PHP syntax
}
$varval[$match[1]]=$tmp; //we do not need to check if exists, because we should replace with the latest value.
}
/*
The below stores all quoted text and replace them with our own special variable %var0%, %var1%, %var2%.... %varN%.
This is because there could be cases like if("test == 3" == $a) or even worse if("if(test=3)" == $a), which will make regex/parsing tricky if we do not substitute these quoted text out just like a parser/compiler would.
Note: Something I didn't do. We should really first scan the whole text using preg_match to find if there are any text using this format of variables as well, to prevent collisions!
If there are, we can set a loop to check and append some character to our own special variable like %varrN%, %varrrN%, %varrrrN%...
until we find no collisions and it is safe to use. I will leave this simple regex exercise for you to do on your own. Create the regex to find the file for %varN%...
*/
preg_match_all("/\"([\s\S]*?)\"/", $file, $matches, PREG_SET_ORDER);
$stringvars=array();
$key="%var";
$count=0;
foreach($matches as $match){
if(!in_array($match[1], $stringvars)){
$stringvars[$key.$count.'%']=$match[1];
$file=preg_replace("/\"".preg_quote($match[1])."\"/", $key.$count.'%', $file); //take note of the change to preg_quote
$count++;
}
}
// now we parse the whole text for if(subject anycomparator value)
preg_match_all("/if\s*?\(([\s\S]*?)([\=|\>|\<][\=]{0,2})\s*?([\S\s]*?)\s*?\)/", $file, $matches,PREG_SET_ORDER);
$conditionals=array();
foreach($matches as $match){
$conditionals[]=array(
'subject'=>replaceFromObject(replaceFromObject(trim($match[1]),$stringvars),$varval),
//the order does matter, we replace the string first, then the variables, as there might be variables in the double quoted strings which we should evaluate
'comparator'=>$match[2],
'value'=>replaceFromObject(replaceFromObject(trim($match[3]),$stringvars),$varval),
);
}
foreach ($conditionals as $c){
echo htmlspecialchars($c['subject']).' ';
echo htmlspecialchars($c['comparator']).' ';
echo htmlspecialchars($c['value']);
echo "<br/>";
}
/* now this function can be used to replace both the quoted strings AND the variables */
function replaceFromObject($s, $obj){
foreach($obj as $key=>$value){
$s=preg_replace('/'.preg_quote($key).'/', $value, $s); //take note of the preg_quote as the string may contain regex keywords
}
return $s;
}
So actually your code doesn't output anything (test.php) on my server. Therefore I've tried another approach which at least works for your code.php, but however, it is not very stable like your own code since it doesn't take a few things into account which would be the following.
Logical operators which weren't thought of:
<, <=, >, >=, ===
No multiline if support
My approch actually doesn't split the string nor does it strip anything, the only thing which it is doing however, is to replace the string literal.
<?php
$file = file_get_contents("test.php");
$curIdx = 0;
while($curIdx < strlen($file))
{
$ifIndex = strpos($file,"if", $curIdx);
if($ifIndex === false)
{
break;
}
$curIdx = $ifIndex+1;
$equalsIdx = strpos($file,"=",$curIdx);
if($equalsIdx === false)
{
break;
}
$curIdx = $equalsIdx+1;
for($i = $curIdx; $i<strlen($file); $i++)
{
if($file[$i] !== "=" && $file[$i] !== " ")
{
$curIdx = $i;
break;
}
}
$condition = substr($file,$curIdx,strpos($file,")",$curIdx)-$curIdx);
$condition = str_replace('"','',$condition);
echo htmlspecialchars($condition);
}
This outputs: <SCRIPT
For your script, I found several issues:
if(strpos($value, 'if') !== false)
has to have a double ==
with that out of the way, you're getting the following output:
= "<SCRIPT"
To get rid of this, simply check if the char after pos1 is another =
if($value[$pos1] === "=")
{
$pos1++;
}
Then we can actually remove the call to sanitize_recursive completely, because substr never returns an array.
Now to get rid, simply call str_replace on the between string:
$between = substr($value, $startIndex, $length);
$between = str_replace('"','',$between);
echo htmlspecialchars($between);
And we're having an output of <SCRIPT- the call to htmlspecialchars is needed because otherwise your browser would start to interpret this tag.
This leaves us with the following file:
<?php
$file = file_get_contents("test.php"); // Code.php the page that include the if condition
$lines = explode("\n", $file); // get each line of source code and store it in array ($lines)
foreach ($lines as $key => &$value) {
if(strpos($value, 'if') !== false) // check if the line have if statement
{
if(strpos($value, '==') !== false ) // check if the line compare two values
{
$pos1 = strpos($value, '==') + 2; // get the existence position of '==' + 2
if($value[$pos1] === "=")
{
$pos1++;
}
$pos2 = strrpos($value, ')'); // get the position of last ) in the line
$startIndex = min($pos1, $pos2);
$length = abs($pos1 - $pos2);
$between = substr($value, $startIndex, $length);
$between = str_replace('"','',$between);
echo htmlspecialchars($between); // will print: "<SCRIPT" with double quotation
}
}
}
I am trying to read a number from a database and write it to an excel file, but I am having trouble keeping leading and trailing zeros. When I checked it, I was able to see that while reading the data, PHP is treating it as a number instead of a string. I have tried type casting and concatenation of the value with an empty string. However, the following code
<?php
$a = (string) 180961.65000;
echo $a;
?>
gets the below output
180961.65
How would I preserve the zeroes?
you can try this.
$number = 180961.65000;
$string = sprintf("%f",$number);
echo $string;
//result:180961.650000
Please use quotes to identify $a as string (also use quotes with echo if possible):
$a = "180961.65000";
echo "$a";
try putting
<?php
$a = (string) "180961.65000";
echo $a;
?>
You need number_format for this
Reference - http://php.net/number_format
$a = "180961.650000000";
$total_len = strlen($a);
$dec_pos = strpos($a, ".");
$diff = $total_len - ($dec_pos +1);
$a = number_format($a,$diff);
$a = str_replace(",", "", $a);
echo $a;
The data type is changing automatically in an unclear way somewhere, while working with the variable.
Therefore I suggest to preserve the leading and trailing digits explicitly while the variable is still a string. Then add them later when the variable has become a number. It might look ugly, but it is flexible for any number of zeroes.
Like this:
// Example number, assume it was received by database
$input = "00018096.16500000";
// Get the leading and trailing zeros as string.
preg_match("/^0+/", $input, $leading);
preg_match("/0+$/", $input, $trailing);
// Mark if there is a decimal point.
$hasDigit = strpos($input, ".") !== false;
// Only for here: force the input to become a number, thus removing all trailing and leading zeros.
$input = 1 * $input;
// Now add the leading and trailing zeroes again and turn the variable back to a string.
$saveOutput = $input . "";
if (count($leading) > 0) {
$saveOutput = $leading[0] . $saveOutput;
}
// If the number is not a float with decimal point, don't add the trailing zeros as they are still there in the integer number.
if (count($trailing) > 0 && $hasDigit) {
$saveOutput = $saveOutput . $trailing[0];
}
// Show result
echo $saveOutput;
// --> Result:
// 00018096.16500000
N.B., I call the variable saveOutput because you are sure it is a string and it won't change, like the input.
$val represents 1,949.58 from my sql
$sold = 50;
if ($val>$sold){
echo "true";
}
else
{
echo "false";
}
I get false. somehow 50 is bigger than 1,949.58 and this because of the ',' sign. I need some advices on how to manage this right. Thx
$val is interpreted by php to be a string. When doing the comparison, it's doing a string compare so you aren't going to get the results you expect.
You need to force them to be a floating point type. Look at http://php.net/manual/en/function.floatval.php Specifically the comments on that function.
Something like this should work:
function floatvalue($value) {
return floatval(preg_replace('#^([-]*[0-9\.,\' ]+?)((\.|,){1}([0-9-]{1,2}))*$#e', "str_replace(array('.', ',', \"'\", ' '), '', '\\1') . '.\\4'", $value));
}
You need to convert these values to floats instead of strings. Right now you're doing string comparisons and not numerical comparisons. The following converts those strings to floating values.
$val = parseFloat('1,949.58'.replace(/\,/g,""))
$sold = parseFloat('50')
The .replace removes the "," from your original string before it's parsed to a Float.
I have tried many different combinations of code using the eval command. However my answer is always blank. Here is a simple one i tried.
$equation = "18 - (18 * 2) - 1";
$var = eval($equation);
echo "answer: " . $var;
I understand this does not need the eval code, but the real code I am working on does. It just always shows up blank.
Solution
Add return inside the evaluated statement, otherwise the eval() won't know what to return:
$equation = '2 + 2 * 2';
$var = eval("return $equation;");
Proof, that the above works: http://ideone.com/bTtIH
About superiority of return vs. setting variable
More from the documentation of eval():
eval() returns NULL unless return is called in the evaluated code, in which case the value passed to return is returned.
In other words: there is no need to pollute the namespace, you can get what you want directly from what eval() returns.
You need to set the variable in the equation when you pass it to eval() to make it a valid and complete PHP expression:
$equation = "18 - (18 * 2) - 1";
eval( '$var = ' . $equation . ';');
echo "answer: " . $var;
Or, you can return the value:
$var = eval( 'return ' . $equation . ';');
Now you can do echo $var;, as seen in this demo.
That being said, eval() use is usually discouraged, as the underlying problem is typically solved with other means.