Does this code create multiple instances of the function? - php

I am learning how to build a parent/child category list. I found a great tutorial and have implemented the following code:
while($row = $tree_sth->fetch()){
$rows[$row['id']] = array('name'=>$row['name'], 'on'=>$row['on'], 'parent'=>$row['parent']);
}
function btree($parent){
$has_childs = false;
global $rows;
foreach ($rows as $key => $value){
if($value['parent'] == $parent){
if ($has_childs === false){
$has_childs = true;
echo '<ul>';
}
echo '<li>'.$value['name'];
btree($key);
echo '</li>';
}
}
if($has_childs === true){
echo'</ul>';
}
}
What I am having trouble understanding is how the foreach and recursive function is handled by PHP.
It appears that this causes multiple "instances??" of the function and foreach loop to run simultaneously... Is that correct?
If this is what is happening, it seems this may slow down as my list grows and child relationships get deeper. Is this true?

The short, short version is that the function is working this way:
Begin-function (first instance):
Begin-loop:
Loop...
Begin-func-again?
Begin-function (second instance):
Begin-loop:
Loop...
Begin-func-again?
Begin-function (third instance):
Begin-loop:
Loop...
Begin-func-again? (NO)
// termination point reached
End-loop
End-function (third instance)
End-loop (from second instance)
End-function (second instance)
End-loop (from first instance)
End-function(first instance)
It isn't that there are multiple versions of the function being created simultaneously, they are an expansion and contraction done in order, but they all do stem from the original function call.

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

Using queries as data for loop in Laravel (or PHP in general)

it's my first time asking a question here, please bear with me as i'm just started coding not too long ago.
A while ago, my colleague saw my code
$roles = new Roles();
foreach($roles->get as $role)
{
...irrelevant operation here
}
He commented that the way i put my query inside the iteration is wrong, i should change it to
$roles = $roles->get();
foreach($roles as $role)
He told me that if i put the query as an array expression in foreach loop, it would reference the database each loop, ultimately slowdown the whole site. I'd like to know whether it's true or not, and the logic behind it.
The $roles->get(); opens the db connection, makes the query, closes the connection.
Every connection to DB (often) is a TCP connection to another host, if you put inside a loop it is called (n) times.
You are slowing the loop by making each iteration wait for DB connection.
If you get all data at once (like with $roles->all()) it puts the data into the RAM and doesn't need to call the DB foreach item.
Hope it helped
In foreach loops PHP takes the iterable variable once and uses an internal iterator to process on it. Your colleague's objection applies to simple for loops where the continuation condition is executed each time before every iteration.
You can easily construct a test case:
<?php
class testClass
{
private
$_iteratable = ['a', 'b', 'c'];
public function get()
{
echo "get called<br>\n";
return $this->_iteratable;
}
}
echo "foreach<br>\n";
$obj = new testClass();
foreach($obj->get() as $key => $value)
{
echo "$key: $value<br>\n";
}
/* OUTPUT:
foreach
get called
0: a
1: b
2: c
*/
echo "<p>for<br>\n";
$obj = new testClass();
for($i = 0; $i < count($obj->get()); $i++)
{
echo "$i: {$obj->get()[$i]}<br>\n";
}
/* OUTPUT:
for
get called
get called
0: a
get called
get called
1: b
get called
get called
2: c
get called
?>
To enhance performance you might use foreach($iteratable as &$reference) since only a reference to the data instead of a probably bigger data value is copied each iteration. Doing so, you will be processing on the original data passed to the foreach loop wich means: You can even manipulate that data. If you use foreach by value you will work on a copy and you can modify your work value without affacting the original data.
There is no significant difference between
$roles = $roles->get();
foreach($roles as $role)
and
foreach($roles->get() as $role)
in both cases $roles->get() will be called just once. Which means that database connection will be executed just once.

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

Building a tree from transversing an array in PHP

How to build a tree from an array in PHP knowing the rules that need to be followed to build the tree?
I don't have any particular place in the code where I can find what's really wrong
Anyway, the problems are probably related with the passing by reference I'm repeatedly using.
I did not use recursion because speed is really really important here. From the rules that the input array has (I have full control over them), this is possible, and faster, without recursion.
How it works:
As you transverse the array, each element's ['start_order'] is a bigger number than the previous ['start_order'].Every time that the next element's ['start_order'] is bigger than this element's ['start_order'] and the next element's ['end_order'] is smaller than this element's ['end_order'], then that element is a child of this element. After that step, I must find all the children of that element (the one I just found that it it this element's child).
Here's my code.
<?php
ksort($treeArray);
$tree = array();
$stack = array();
reset($treeArray);
$currentParent = &$treeArray[key($treeArray)];
$tree[] = &$treeArray[key($treeArray)];
while(next($treeArray) !== false){
if(current($treeArray)['start_order'] <= $currentParent['end_order']){
if(current($treeArray)['end_order'] <= $currentParent['end_order']){
// There's a child of the previous
// push the new parent
$stack[] = $currentParent;
if(!isset($currentParent['children'])){
$currentParent['children'] = array();
}
$currentParent['children'][] = &$treeArray[key($treeArray)];
$currentParent = current($treeArray);
}else{
// Supposed not to happen. Log the problem.
}
}else /* if(current($treeArray)['start_order'] > $currentParent['end_order']) */{
// Pop the child here, there are no more children.
if(($popedElement = array_pop($stack)) === NULL){
$tree[] = $currentParent;
}else{
$popedElement['children'][] = &$treeArray[key($treeArray)];
$stack[] = $popedElement;
$currentParent = current($treeArray);
}
}
}
?>
Example:
The input array of this can be something that structurally looks like this:
[1][child1][child1child1][child2][child2child1][child2child2][child2child3][2][child2child1]
which resolves into this tree:
[1]
[child1]
[child1child1]
[child2]
[child2child1]
[child2child2]
[child2child3]
[2]
[child2child1]
And don't forget that this order maintains. [child2child3] never appears before [child2child2], [2] never appears before [1]. In this analogy, is almost like when dealing with XML.
Found the problem here. The problem is related on how I treat the pass-by-reference while trying to solve this problem.
Here's the solution:
$tree[] = &$treeArray[key($treeArray)];
$currentParent = &$treeArray[key($treeArray)];
next($treeArray);
while(current($treeArray) !== false){
if(current($treeArray)['start_order']['start_position'] <= $currentParent['end_order']['end_order']){
if(current($treeArray)['end_order']['end_order'] <= $currentParent['end_order']['end_order']){
// There's a child of the previous
// push the new parent
$stack[] = &$currentParent;
$currentParent['children'][] = &$treeArray[key($treeArray)];
$currentParent = &$treeArray[key($treeArray)];
}else{
// Supposed not to happen. Log the problem.
}
next($treeArray);
}else /* if(current($treeArray)['start_order']['start_position'] > $currentParent['end_order']['end_order']) */{
// Close previous element here. There are no more children.
if(end($stack) === false){
$currentParent = &$treeArray[key($treeArray)];
$tree[] = &$treeArray[key($treeArray)];
next($treeArray);
}else{
$currentParent = &$stack[key($stack)];
unset($stack[key($stack)]);
}
}
}
The main problem was actually the pass-by-reference which is different in PHP than it is in C.
To solve this problem, I'm unable to use both push or pop php functions:
The push because the $var[] operator is faster and using that function.
The pop, because its return is a copy of what I had pushed before instead of the actual element I had pushed.
Additionally, all variable assignments have to be explicitly made by reference (using the & character), so using current() for assignment is out of the question, instead I need to use the key() function like I did.
I also had a small but important bug that forced me to change the "while" instruction. It now contains current() instead of next(). That's only because after the pop of the stack I mustn't move to the next element. I need to do another iteration before I move to the next one. This solves the bad nesting generated when there are multiple tags closing in the same place.
Please note that this code is not optimized for speed.

Stop processing and evaluate next value in foreach loop

I am trying to search and check for the duplicate details that is available in the database using php.The user enters several names and then a phone number to check for the duplicates. Below is my function. I just cropped out some parts because it was too long.
function gtc($names,$phone)
{
$pageNumb=20;
$position = array(5);
$sepname=explode(",","$names");
foreach ($sepname as $sepname1)
{
for ($page=0;$page<=$pageNumb;$page=$page + 1)
{
$turl="http://192.168.111.2119/search.php?qry=$sepname1&page=$page";
$search=curl_init();
curl_setopt($search,CURLOPT_URL,$turl);
curl_setopt($search,CURLOPT_RETURNTRANSFER,1);
curl_setopt($search,CURLOPT_FAILONERROR,true);
curl_setopt($search,CURLOPT_AUTOREFERER,true);
$result=curl_exec($search);
$dom = new DOMDocument();
#$dom->loadHTML($result);
$xpath=new DOMXPath($dom);
$elements = $xpath->evaluate("//div[#id='inf']");
foreach ($elements as $element)
{
$position[$sepname1] = $position[$sepname1] + 1;
foreach($position as $key=>$val)
$contct = $element->getElementsByTagName("contact")->item(0)->nodeValue;
if (preg_match("/$phone/i",$contct)) {
echo "Found In Database>";
}
}
ob_flush();
flush();
}
}
}
?>
The function works perfectly but the only problem that I am having is although a match is found it goes until the last page which is given in the for loop and then goes through the next name. I would like to know is it possible to make when a match is found stop processing and then search for the next name?
I don’t want to use exit(); because it completely stops the execution
The input format is as below
User inputs names: john, Sam, Michael, Steve
Phone number:0084554741
Any help will be appreciated
You are likely looking for break
break ends execution of the current for, foreach, while, do-while or switch structure.
// output numbers 1,2,3,4
foreach( range(1,10) as $number) {
if($number === 5) break;
echo $number, PHP_EOL;
}
and/or continue
continue is used within looping structures to skip the rest of the current loop iteration and continue execution at the condition evaluation and then the beginning of the next iteration.
// output only even numbers
foreach( range(1,10) as $number) {
if($number % 2) continue;
echo $number, PHP_EOL;
}
#Gordon offered the formally correct answer, a "best practices" answer would be to split your function into logical parts (especially as you mention it's too long), for example:
function search($term) {
$xml = loadXmlFromRemoteHost();
if(!$xml)
echo "An error occured";
$found = findTermInXml($xml, $term);
if($found)
echo "Found In Database>";
The search function contains the loop in question and uses "return" to exit the loop (and the function) when it finds something:
function findTermInXml($xml, $term) {
foreach(...whatever...) {
if(...some condition...)
return true;
}
return false; // nothing found
}
Take a look at:
http://pl.php.net/break
http://pl.php.net/continue

Categories