statement inside php eval() - php

In a PHP framework which provides hookpoints that are implemented using eval() I am trying to interrupt or continue a loop from within an eval() call.
This is what I am trying
The framework loops like
...
for( $i = 0; $i < 10; $i++ ) {
// framweork code here ...
eval( $plugin_code );
// framweork code here ...
}
...
$plugin_code contains PHP-code - in this sample case
if( $i == 5 ) {
continue;
}
It results in this error
PHP Fatal error: 'continue' not in the 'loop' or 'switch' context
If it is true that eval() only evaluates expressions and can NOT evaluate statements - then how can I implement the continue / break statements inside an eval()?

Leaving aside the mechanics of eval and continue for a moment, I think there is a more fundamental point to make: when writing code that "plugs in" to another system, you can only do what that system allows you to do.
If the hook system simply executes the code you give it (whether via eval, or a callback function, or any other mechanism), you cannot use it to control the flow of loops etc in the main framework code.
If the framework wanted you to do that, it would have to provide a mechanism for your plugin to signal back to the framework what you want it to do - you might register the plugin in a particular way, return a particular value from the callback, set a particular variable, etc.
If you don't want to directly modify the framework, your only option is to request such a feature from the framework's author.

You can't use a statement like continue or break in eval() to affect an outside loop.
I suggest using a variable.
$plugin_code = 'if( $i == 5 ) {
$continue = true;
}';
$continue = false;
for( $i = 0; $i < 10; $i++ ) {
eval( $plugin_code );
if ($continue) {
continue;
}
// other code
}
If you also want to skip the rest of the plugin code, it should put that code in an else block.
$plugin_code = 'if( $i == 5 ) {
$continue = true;
} else {
// other code
}';
IMHO, this whole thing smells fishy. The plugin is dependent on the specific variable being used for the loop, which is quite fragile.

Keywords like continue, break and return which affect the control-flow of the program cannot be used directly inside eval to achieve the result you want. eval is a function, and a function cannot change the control-flow of the code which calls it, except by throwing an exception.
Since continue simply means "skip to the next iteration of the loop", and this is equivalent to "don't execute the rest of the code in this block", you could rewrite your code to make the rest of the block conditional on the if statement instead. If the code looks like
for( $i = 0; $i < 10; $i++ ) {
if( $i == 5 ) {
continue;
}
// do more things
}
then this can trivially be rewritten as
for( $i = 0; $i < 10; $i++ ) {
if( $i != 5 ) {
// do more things
}
}
and this is now in a form which can be eval'd:
// your code
$plugin_code = 'if( $i = 5 ) {
// do more things
}';
// framework code
for( $i = 0; $i < 10; $i++ ) {
eval($plugin_code);
}
However this still only affects the control-flow of the code you're passing into the framework. It's not possible to change the control-flow of the framework code itself.

Related

If each function is deprecated, how to recover the beauty that this function provides?

The typical algorithm of search until found, in PHP and for some arrays where we can't use the language array search functions (I think), could be implemented in this way:
$found = false;
while (list($key, $alreadyRP) = each($this->relatedProducts) && !$found) {
if ($alreadyRP->getProduct()->getId() === $rp->getProduct()->getId()) {
$found = true;
}
}
if (!$found) {
// Do something here
}
Please, take it as pseudocode, I didn't executed it. What I like about it, is that it gracefully stops if it is found what we are looking for.
Now, due to the "each" function is deprecated, I have to code something like this:
$found = false;
foreach ($this->relatedProducts as $alreadyRP) {
if ($alreadyRP->getProduct()->getId() === $rp->getProduct()->getId()) {
$found = true;
break;
}
}
if (!$found) {
// Do something here
}
To put a "break" statement inside a "for" loop is ugly in structured programming. Yes, it is optional, but if we avoid it, the "foreach" will go through all the array, which is not the most efficient way.
Any idea to recover the efficiency and structure that "each" gives in this case?
Thank you.
The beauty of the each() method is in the eye of the beholder, but there are other reasons to prefer foreach, including this interesting bit of information from the RFC that led to the deprecation of each()
The each() function is inferior to foreach in pretty much every imaginable way, including being more than 10 times slower.
If the purpose of the method is to // Do something here if the $rp is not found in $this->relatedProducts, I think a more "beautiful" way to handle it would be to extract the search through related products into a separate method.
protected function inRelatedProducts($id) {
foreach ($this->relatedProducts as $alreadyRP) {
if ($alreadyRP->getProduct()->getId() === $id) {
return true;
}
}
return false;
}
Moving the related products search into a separate method is advantageous because
It separates that functionality from the original method so that it becomes reusable instead of being tied to whatever // Do something here does.
It simplifies the original method so it can focus on its main task
$id = $rp->getProduct()->getId();
if (!$this->inRelatedProducts($id)) {
// Do something here
}
It simplifies the search code because if it's contained in its own method, you can just return true; as soon as you find a match, so you won't need to break, or to keep track of a $found variable at all.
On the other hand, if this was my project I would be looking for a way to remove the need for this method by populating $this->relatedProducts so that it's indexed by ID (assuming ID is unique there) so the determination could be reduced to
$id = $rp->getProduct()->getId();
if (isset($this->relatedProducts[$id])) { ...
If you're looking for a rewrite that doesn't involve extra variables, you can replace the each call with calls to current and next:
do {
$found = (current($this->relatedProducts)->getProduct()->getId() === $rp->getProduct()->getId());
} while (empty($found) && false !== next($array));
This is a mechanical translation, and it relies merely on the definition of each (emphasis mine):
Return the current key and value pair from an array and advance the array cursor
It also suffers the same deficiency of the original each version: it doesn't handle empty arrays.
That said, please don't use each, or any of its siblings, for new code. This from a guy who voted "No" on the RFC! Why? Because the performance sucks:
1017$ cat trial.php
<?php
$array = range(0, 999);
$begin = -microtime(true);
for ($i = 0; $i < 10000; $i++) {
reset($array);
$target = rand(333, 666);
do {
$found = (current($array) === $target);
} while (empty($found) && false !== next($array));
}
printf("%.5f\n", $begin + microtime(true));
$begin = -microtime(true);
for ($i = 0; $i < 10000; $i++) {
$target = rand(333, 666);
foreach ($array as $current) {
if ($current === $target) break;
}
}
printf("%.5f\n", $begin + microtime(true));
1018$ php -d error_reporting=-1 trial.php
8.87178
0.33585
That's nearly nine seconds for the next/current version while not even half a second for the foreach version. Just don't.
It looks like each is basically a version of current() and next()
http://php.net/manual/en/function.current.php
http://php.net/manual/en/function.next.php
each() gives the current array item, and moves to the next index.
current() gives the current array item, but doen't increment the index.
So, you can replace each() with current(), and inside your foreach use next() to shift the index up
next() gives the next item, and increments the index.
while (list($key, $alreadyRP) = current($this->relatedProducts) && !$found) {
if ($alreadyRP->getProduct()->getId() === $rp->getProduct()->getId()) {
$found = true;
}
next($this->relatedProducts);
}

Cleaning up a very long if statement

I have a very long list of strings called $stringfilter1 $stringfilter2 etc all the way up to $stringfilter50
I have another string $reporteremail and I want to make a conditional statement whereby if any of the $stringfilter strings is present in the $reporteremail, some code is executed. At the moment my code looks like this and it works:
if (stripos($reporteremail, $stringfilter1) !== false || stripos($reporteremail, $stringfilter2) !== false || stripos($reporteremail, $stringfilter3) !== false [...]) {
runcode();
}
This is very very long though. I have cut it short here.
I was wondering if there's a cleaner, more efficient way to do this?
EDIT:
I am writing a plugin for a bug tracker. The strings are entered on another page in text boxes. I access them on this page by running a function that looks like
$t_filter = plugin_config_get( 'filter1' );
$stringfilter1 = string_attribute( $t_filter1 );
I would agree looping through an array would be the best way to do this. How can I push each new string onto the end of an array without having to write that snippet above out 50 times?
How can I push each new string onto the end of an array without having to write that snippet above out 50 times?
Try this:
$needles = [];
for ($i = 0; $i < 50; $i++) {
$t_filter = plugin_config_get("filter$i");
$needles[] = string_attribute($t_filter);
}
I have a very long list of strings called $stringfilter1 $stringfilter2 etc all the way up to $stringfilter50
[...]
This is very very long though. I have cut it short here.
I was wondering if there's a cleaner, more efficient way to do this?
Try this, it should go after the code block above.
$flag = false;
foreach ($needles as $needle) {
if (stripos($reporteremail, $needle) !== false) {
$flag = true;
break;
}
}
if ($flag) {
runcode();
}
The code above works by iterating through the $needles array and sets a flag if stripos doesn't return false. After it's finished iterating, it checks if the flag is true, if so, this means that one of the needles was found in the array.
EDIT
Alternatively, you could do it all in one loop, which is both faster and more efficient.
$flag = false;
for ($i = 0; $i < 50; $i++) {
$t_filter = plugin_config_get("filter$i");
$needle = string_attribute($t_filter);
if (stripos($reporteremail, $needle) !== false) {
// One of the needles was found in $reporteremail.
runcode();
break;
}
}
You don't need a loop. First put all your filters in an array instead of having them in separate variables. I would try to do this by modifying the input source rather than doing it in your PHP script. (Based on your comments I'm not sure if that's possible or not, so maybe you do need a loop like the one in the other answer.) Then you can use str_ireplace to check for your filter strings in the $reporteremail. (This will not modify $reporteremail.)
str_ireplace($filters, '', $reporteremail, $count);
if ($count) {
// run code
}
The $count parameter will contain a count of how many replacements were performed. If it's nonzero, then at least one of the filters was found in $reporteremail.

handle null values to prevent errors while in loop

I am using the following code to return data from a loop and although I am receiving the data it seems there may be null data also which is producing errors.
Here is the code
$extensions = count($cert['tbsCertificate']['extensions']);
for ($j = 0; $j < $extensions; $j++) {
$count = count($cert['tbsCertificate']['extensions'][$j]['extnValue']);
for ($i = 0; $i < $count; $i++) {
if (array_key_exists('dNSName', $cert['tbsCertificate']['extensions'][$j]['extnValue'][$i])) {
$value = $cert['tbsCertificate']['extensions'][$j]['extnValue'][$i]['dNSName'];
$item = $i;
echo 'DNSName',$item,' : ', $value,"\n";
}
}
}
Here is the result of output
Can someone help me update the code to check for null values (i think in the first for loop) and ideally ignore those so the warnings don't appear? I am happy to provide more context if required.
Change your if condition so that it checks if it is set and if it is array first:
if(isset($cert['tbsCertificate']['extensions'][$j]['extnValue'][$i])
&&
is_array($cert['tbsCertificate']['extensions'][$j]['extnValue'][$i])
&&
array_key_exists('dNSName', $cert['tbsCertificate']['extensions'][$j]['extnValue'][$i]))
if it detects in first condition it is not set or is not an array, it will not proceed to array_key_exists function.
The error probably is, because the incoming data has not indexes like 0,1,2,3... - may be it starts from 1.
The easest solution I think is if you replaces FOR structures by FOREACH.

Function calls within a loop when its not necessary PHP

This might be a very dumb question but it still bugs me. I tend to use this:
$variable = $someObject->getSomeValue();
for ($i = 0; $i < $x; $i++) {
performSomeFunction($variable);
}
I have seen people do the following & some of my co-workers are arguing with me that there is no difference in performance if I use function calls within a loop & that it will save 1 line of code.
for ($i = 0; $i < $x; $i++) {
performSomeFunction($someObject->getSomeValue());
}
Now for $x = 10 this might not have impact on performance but what if $x = 10000 ? Which one is the preferred way or best practice in PHP and in Programming general?
Example of function getSomeValue
// just a getter method for the class
function getSomeValue() {
return $this->userFirstName;
}
Highly depends on your function. For example this
function() {
return 2;
}
It won't matter.
For this:
function() {
$pdo->query('SELECT ....');
// more db stuff
return $someDbStuff;
}
It will matter extremely!
Depends on what you do on $someObject->getSomeValue();. If just returns a variable, it doesn't have any impact on performance, if, on the other hand, you are retrieving the data from a database, its very important to avoid it.
However, its always a good policy to avoid unnecessary iterations and do like this
$variable = $someObject->getSomeValue();
for ($i = 0; $i < $x; $i++) {
performSomeFunction($variable);
}
Yeah, sure there's an impact in the performance.
If you are about to use the same value over iterated loops, why would you go get it every time?
It's better getting it before the loop (if there's no chance of changing this value while in the loop) and then reusing it.
Imagine this getSomeValue() needs to access a database or a webservice, would you rather do it 1 time or $x times for the same effect?

Is it bad practice to stuff garbage into $array[-1] to prevent OBOB?

I am trying to write a bunch of nested if statements that are somewhat as follows:
if (strcmp($data['reports'][$i][$j],$data['reports'][$i][$j-1])){
but as you all know, you have to write a separate if statement if it is 0, because 0-1 doesn't exist.
if ($j == 0){
//First report, to prevent OBOB
echo "<br/>".$data['reports'][$i][$j]."";
#-> You would have to write two more statements here to test for province...
}
if ($j >= 1){
//All subsequent reports
if (strcmp($data['reports'][$i][$j],$data['reports'][$i][$j-1])){
echo "<br/>".$data['reports'][$i][$j]."";
#-> You would have to write two more statements here to test for province...
}
Now, imagine if you have to test other dependent values for sequential identicality?
It goes from 4 to 8 to 16 separate statements to just test for 4 of these...
So, I just had the idea to stuff crap into $j = -1.
$data['reports'][$i][-1]='aaaaaaaaaa';
// This would depend on the type of data you are
// comparing I suppose it could be zeros.
$data['provinces'][$i][-1]='aaaaaaaaa';
Is there a better solution?
Why don't you just start your loop with 1 instead of 0? That is, instead of this:
for ($j = 0; $j < count($data['reports'][$i]); $j++ )
do this:
for ($j = 1; $j < count($data['reports'][$i]); $j++ )
It's pointless to compare the first array element to the previous one anyway, so just skip it.
Unless you need to do something with the first element, Vilx's answer is the way to go. By the looks of your code, you always echo the first element, and the others need an if condition. I'm guessing your concerned about duplicate code (good), just move the code that echos a row into a new method and call it conditionally.

Categories