Cleaning up a very long if statement - php

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.

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);
}

Different implementation of reverse linked list

I have found several reverse linked list implementation in php and most of them are the same with some little differences like this:
public function reverse() {
if ( $this->_firstNode !== NULL ) {
if ( $this->_firstNode->next !== NULL ) {
$reversed = $temp = NULL;
$current = $this->_firstNode;
while ( $current !== NULL ) {
$temp = $current->next;
$current->next = $reversed;
$reversed = $current;
$current = $temp;
}
$this->_firstNode = $reversed;
}
}
}
But I think it could be changed to this:
public function reverse() {
while ( $this->_firstNode->next !== NULL ) {
$oldFirstNode = $this->_firstNode;
$this->_firstNode = $oldFirstNode->next;
$oldFirstNode->next = NULL;
$this->_firstNode->next = $oldFirstNode;
}
}
Am I right?
Your code does not work, for two reasons:
You do not test for empty list. The method does not consider the case in which $this->_firstNode is NULL.
The method does not work if the list contains only one element.
If the list contains two or more elements, the method reverses only the first two elements of the list, then it falls in an endless loop. This is because in the last line of the body of the while you update $this->_firstNode->next with the value of $oldFirstNode, and in the next iteration you check for $this->_firstNode->next !== NULL, which is different from NULL since it is the value of $oldFirstNode, and the function continue looping on those two nodes.
For algorithm like this one, the best approach is to use paper and pencil and sketch the elements of the list and the variables pointing to them, and update them by following the algorithm step by step.
Finally note that if an algorithm is always is used for a certain basic task, it is very difficult to find a new, more efficient, algorithm.

Searching for a list of names in a piece of text

I have a list of 230 names in a database and I'd like to monitor a Twitter feed for mentions of occurrences of those names, particularly when multiple names are used in the one tweet.
I'm not quite sure how to do this.
My initial thought is:
Store names in an array and run every tweet through a function like this:
function contains($str, array $arr)
{
foreach($arr as $a) {
if (stripos($str,$a) !== false) return true;
}
return false;
}
Is there an easier way to do this?
Seems like a fine approach, though I would consider a variation which returns a little more information through the interface than a boolean, since you said you are interested in multiple mentions. It will execute longer when there is a mention but seems worth it:
function referenceCount($str, array $arr)
{
$count = 0;
foreach($arr as $a) {
if (stripos($str,$a) !== false) $count++;
}
return $count;
}

PHP check postcode is in list

I have a list of postcode areas (left hand part only) as below:
IV1-28,IV30-32,IV36,IV40-49,IV52-56,IV63,KW1-3,KW5-14,PA21-38,PH1-26,PH30-41,PH49-50,LD1-99,SY16-20,SY23-25
My input is a UK Postcode eg. IV21
I need a PHP function to check if the input postcode (eg IV21) is in the list.
This would be simple enough, but the list is in the form IV1-28 as opposed to being a 'normal' list such as IV1,IV2,IV3,IV4,IV5,IV6...IV26,IV27,IV28 etc.
Help is much appreciated. Thanks in advance.
loops through an array of codes and splits them into the number then checks if that number is within the range
$list = array("IV1-28","AB1-10");
$found = false;
$input = "AB10";
$inputCode = substr($input,0,2);
$inputNumber = substr($input,2);
foreach ($list as $l)
{
$lCode = substr($l,0,2);
$lNumber = substr($l,2);
$listNumbers = explode("-",$lNumber);
if (($inputNumber >= $listNumbers[0]) && ($inputNumber <= $listNumbers[1]) && ($inputCode==$lCode))
{
$found = true;
break;
}
}
var_dump($found);
http://php.net/manual/en/function.explode.php (to split your list into only the left hand side), add the left hand side into an array, and use http://php.net/manual/en/function.in-array.php - should get you somewhere close to what you need
use strpos function. returns int if found or else false. note that matching IV1 would give true even of IV11 is found. so use the following script
$code="IV21";
$list="IV1-..........etc";
if(strpos($list,$code."-") || strpos($list,$code.",") {
//true
}
else {
//false
}
the "-" and "," is to ensure that the string ends so that IV11 won't give true if we search for IV1

should this method be decomposed into separate methods?

This method takes search a search keyword and parsed mysql query, and rewrites the where expression to include LIKE %keyword%.
It works well, but I dont know if its good or bad practice to have a method with this many loops...
private function build_where($query_array, $options)
{
//add WHERE starting point
$where = '';
if(!empty($query_array['WHERE']))
{
//build where array
$where_array = $query_array['WHERE'];
//start the where
$where .= 'WHERE ';
//get columns array
$columns_array = $this->build_columns_array($query_array);
//if there is a search string
if(!empty($options['sSearch']))
{
//check for enabled columns
$i = 0;
$columns_length = count($columns_array);
for($i; $i < intval($columns_length); $i++)
{
//create the options boolean array
$searchable_columns['bSearchable_'.$i] = $options['bSearchable_'.$i];
}
//loop through searchable_columns for true values
foreach($searchable_columns as $searchable_column_key => $searchable_column_val)
{
if($searchable_column_val == true)
{
//get an integer from the searchable_column key
$column_id = preg_replace("/[^0-9]/", '', $searchable_column_key);
//lookup column name by index
foreach($columns_array as $columns_array_key => $columns_array_val)
{
//if the $columns_array_key matches the $column_id
if($columns_array_key == $column_id)
{
//loop to build where foreach base expression
$i = 0;
$where_length = count($where_array);
for($i; $i < intval($where_length); $i++)
{
//append the existing WHERE Expressions
$where .= $where_array[$i]['base_expr'];
}
//append the LIKE '%$options['sSearch'])%'
$where .= ' AND '.$columns_array_val." LIKE '%".$options['sSearch']."%' OR ";
}
}
}
}
//remove the last OR
$where = substr_replace($where, "", -3);
}
else
{
//loop to build where
$i = 0;
$where_length = count($where_array);
for($i; $i < intval($where_length); $i++)
{
$where .= $where_array[$i]['base_expr'];
}
}
}
//print_r($where_length);
return $where;
}
The school of thought of Kent Beck or Martin Fowler would actually advise you to refactor this large methods down to many small methods. It's not easily read in my opinion, which would be the main reason to refactor.
Breaking up methods is not primarily about reuse. Doing so can make code easier to read, test, and maintain. Clear method names can also substitute for inline comments. This method does two high-level things which could be separated: building a where clause with options and without options. Another hint for me is that the logic that builds the where clause with options looks meaty enough to warrant its own method.
private function build_where($query_array, $options) {
if(!empty($query_array['WHERE'])) {
$where_array = $query_array['WHERE'];
$columns_array = $this->build_columns_array($query_array);
if (empty($options['sSearch'])) {
return $this->build_where_with_options($where_array, $columns_array, $options);
}
else {
return $this->build_where_without_options($where_array, $columns_array);
}
}
else {
return '';
}
}
Now you can quickly scan build_where() to see that there are three possible forms the where clause may take and when along with the input each form needs to produce its result.
Here are some minor improvements you can make throughout your code:
count() returns an integer and doesn't need the intval() calls in your for loops. Even if you left those in, it would be better to apply the call outside the loop so its done only once as it yields the same value each time.
if($searchable_column_val == true) is equivalent to if($searchable_column_val) since both cast $searchable_column_val to a boolean value and the latter passes when that casted boolean value equals true.
$where = substr_replace($where, "", -3) can be replace with $where = substr($where, 0, -3) and is a little clearer.
Instead of looping through an array looking for a specific key you can take advantage of PHP's arrays by simply grabbing the value with that key.
For the last one, this code
foreach($columns_array as $columns_array_key => $columns_array_val)
{
//if the $columns_array_key matches the $column_id
if($columns_array_key == $column_id)
{ ... }
}
can be replaced by this
$columns_array_val = $columns_array[$column_id];
...
Personal preference really. Some programmers would chop this up into several functions. Personally, I think it's fine the way you have it. If I saw something that I thought might be reusable, I'd refactor it out into a separate file that could be included.
In my opinion, some programmers are too quick to make things "reuesable" before they even have something to reuse it with.

Categories