trouble with recursion in php - php

I cannot figure out why this function is not working. Evertime I try to make the recursive call all I get is an IE page with a cannot display error message. I left // by the lines that is causing me the trouble. I also tried the call without the $this-> and got an error function not recognized
private function insert($key, $current) {
$newnode=new Node($key);
$parent=$this->root;
if($this->root==null) {
$this->root=$newnode;
return;
} else {
if($newnode->data > $parent->data) {
$parent=$parent->rightChild;
$this->insert($key, $parent);//if I comment this line it
//work, but that make the function useless
} else {
echo "smaller ";
}
}
}

The error is obviously an infinite recursive loop.
This is most probably due to the fact that you never use the $current argument.
You're always comparing the $newnode->data against $this->root->data which if greater once, will always be greater.
Update
Here's how I'd change it
private function insert($key, $current = null)
{
$newnode = new Node($key);
$parent = null === $current ? $this->root : $current;
if (null === $parent) {
$this->root = $newnode;
return;
}
if ($newnode->data > $parent->data) {
// same as before from here

Related

PHP recursive function returns empty JSON object [duplicate]

This question already has answers here:
How to use return inside a recursive function in PHP
(4 answers)
Closed 9 months ago.
I have a problem with a recursive function in PHP which returns a JSON object. When the the condition is met to run the function a second time I always get an empty object as result {}. Everything is executed as it would be in the first run, but I always get an empty result.
Here is my code (very much simplified, yet functioning):
public function run()
{
$result = null;
// .......
// there is alot other stuff here, that all runs
// perfectly through also the second run
// ......
// Conditional Routing
if($this->wfProfile->autoprocess){
// select new wfProfile and go again.
$this->autoprocess(function($data){
if($data['error']==0){
$result = null;
$this->run(); // from here we start over !
}else{
return $data;
}
});
}else{
return ['error'=>0,'message'=>'all good']; // this is where it should go at the end of second loop
}
}
There is no place in the whole class, that would return an empty JSON object. Something must be here, that I'm doing wrong or what I'm overseeing.
Edit (I don't think this helps)
private function autoprocess($callback)
{
if(is_callable($callback)){
$possibleWFprofiles = WfProfile::where('statusNow', $this->wfRequest->status)->where('conditionalRouting', 1)->get();
if($possibleWFprofiles->count() == 0){
// configuration error....
$result = ["error"=>1, 'message'=>"Unable to find Conditional Routing enabled WfProfiles: ".$this->wfRequest->status];
}
foreach($possibleWFprofiles as $possibleWfProfile){
if(array_search($possibleWfProfile->crFieldname, $this->wfRequestFields)===false){
// fieldname wrongly configured
$result = ["error"=>1, 'message'=>"Unable to find field ".$possibleWfProfile->crFieldname];
}
// see if this is the right one
if($this->wfRequest[$possibleWfProfile->crFieldname] == $possibleWfProfile->crValue){
$this->wfProfile = $possibleWfProfile;
$result = ['error'=>0,'message'=>'Off to loop 2'];
}
}
call_user_func($callback, $result);
}
}
When you make a return $data, inside a anonymous function, it will not be a run's return.
You are not doing nothing with this return in your autoprocess function.
You need to return something in autoprocess and then return in your if:
if($this->wfProfile->autoprocess){
// select new wfProfile and go again.
return $this->autoprocess(function($data){
if($data['error']==0){
$result = null;
return $this->run(); // from here we start over !
}else{
return $data;
}
});
}else{
return ['error'=>0,'message'=>'all good']; // this is where it should go at the end of second loop
}
You need to return your value, for example take this:
function callback($func, $val) {
return call_user_func($func, $val);
}
function run($val) {
if ($val < 10) {
callback(function($val) { return run($val + 1); }, $val);
}
return $val;
}
print(run(0));
this will print empty, but if you do:
function callback($func, $val) {
return call_user_func($func, $val);
}
function run($val) {
if ($val < 10) {
return callback(function($val) { return run($val + 1); }, $val);
}
return $val;
}
print(run(0));
it will print 10
Your function:
public function run()
{
$result = null;
// lets say this is true...
if($this->wfProfile->autoprocess){
// now we are here, where does this return a value???
$this->autoprocess(function($data){
// if it goes here, it never returns a value.
if($data['error']==0){
$result = null;
$this->run(); // from here we start over !
}else{ // if it returns here it still just returns to
// $this->autoprocess, which might return to the
// original run function, but you don't seem to be
// returning its return either...
return $data;
}
});
}else{
return ['error'=>0,'message'=>'all good']; // this is where it should go at the end of second loop
}
}
At the end I chose the imho less elegant way to solve this, so I used goto instead of calling the function again. This is easy to read and to debug/extend in future. So here we go:
public function run()
{
startover:
$result = null;
// more stuff going on here
// Conditional Routing
if($this->wfProfile->autoprocess){
// select new wfProfile and go again.
$result = $this->autoprocess();
if($result['error']==0){
goto startover; // easiest way :-)
}else{
return $result;
}
}else{
return ['error'=>0,'message'=>'all good'];
}
}
and here the autoprocess function
private function autoprocess()
{
$possibleWFprofiles = WfProfile::where('statusNow', $this->wfRequest->status)->where('conditionalRouting', 1)->get();
if($possibleWFprofiles->count() == 0){
// configuration error....
return ["error"=>1, 'message'=>"Unable to find Conditional Routing enabled WfProfiles: ".$this->wfRequest->status];
}
foreach($possibleWFprofiles as $possibleWfProfile){
if(array_search($possibleWfProfile->crFieldname, $this->wfRequestFields)===false){
// fieldname wrongly configured
return ["error"=>1, 'message'=>"Unable to find field ".$possibleWfProfile->crFieldname];
}
// see if this is the right one
if($this->wfRequest[$possibleWfProfile->crFieldname] == $possibleWfProfile->crValue){
$this->wfProfile = $possibleWfProfile;
return ['error'=>0,'message'=>'Off to loop 2'];
}
}
}

Check function if event has an accepted offer?

I need to check if an event has an offer and i have made this function
public function hasAcceptedOffer()
{
foreach ($this->offers as $offer) {
if( $offer->accepted == 1 ){
return true;
} else {
return false;
}
}
But i think this can be made better, more optimized. Because if there are a lot of offers i don't want to go through all of them. What i want is that if a function finds an offer that is accepted it should stop further iteration.
I think you did good here.
You can remove the else part by doing this
public function hasAcceptedOffer()
{
foreach ($this->offers as $offer) {
if( $offer->accepted == 1 ){
return true;
}
}
return false;
}
Assuming you only care if any offer was accepted, and you don't need to know which one, I like to do it like this:
public function hasAcceptedOffer()
{
foreach($this->offers as $offer) {
if($offer->accepted !== 1)
continue;
return true;
}
return false;
}
Each iteration of the loop will quickly skip to the next one if it doesn't match your desired criteria, it will return immediately when it finds the first positive result and skip processing the others (since you don't care anyway) and return false if no matches are found.
If you want to collect the offers that were accepted, you can amend it like so:
public function getAcceptedOffers()
{
$results = array();
foreach($this->offers as $offer) {
if($offer->accepted !== 1)
continue;
$results[] = $offer;
}
return $results;
}

Function to check on variables that may or may not exist - PHP

I have a function that is meant to check if two variables match, but with different values. It's a kinda complicated idea... but here's an example of its usage:
match($set1->test,"YES",$set2->test,"ON")
It will return true if $set1->test == "YES" && $set2->test == "ON"
Here's an example of how its implemented:
function match($field1,$val1,$field2,$val2) {
if ((isset($field1) && $field1 == $val1) && (isset($field2) && $field2 == $val2))
{
return true;
}
return false;
}
So the big issue here is you CANNOT do isset inside a function with the function's arguments. It's pointless, because the error gets thrown that $set1->test does not exist when the function is called, and if it isn't an object property then the variable gets initialized in the function scope anyway. It seems that the only way to get around this is to do the isset test on $set1->test and $set2->test before passing them to the function, but I really don't want to. It feels unnecessary.
My question is how can I call match($set1->test,"YES",$set2->test,"ON") when $set1->test or $set2->test has not been set?
ANSWER
I'm going to use a variation on Tamás's answer. I will have a separate function called prop, like this:
function prop($obj, $property) {
if (property_exists($obj,$property)) {
return $obj->$property;
}
return null;
}
Then I'll call match like this:
match(prop($set1,'test'),"YES",prop($set2,'test'),"ON")
Thanks!
Try using property_exists
function match($obj1, $obj2,$property,$val1,$val2) {
if ((property_exists($obj1, $property) && $obj1->$property == $val1) && (property_exists($obj2, $property) && $obj2->$property == $val2))
{
return true;
}
return false;
}
It's not very popular, but this could be a valid use for the # modifier to disable errors:
#match($set1->test, "YES", $set2->test, "ON");
I would do something like this:
function match($field1=NULL,$val1=NULL,$field2=NULL,$val2=NULL) {
$result = false;
if (!is_null($field) && !is_null($val1) && !is_null($field2) && !is_null($val2)) {
if ($field1 == $val1 && $field2 == $val2) {
$result = true;
}
else {
$result = false;
}
}
return $result;
}

PHP: how to resolve a dynamic property of an object that is multiple levels deep

I am have written a helper function to "cleanup" callback variables for input into MySQL. This is the function that I wrote:
public function string($object, $objectPath) {
if (!empty($object->$objectPath) || $object->$objectPath !== '') {
$value = $object->$objectPath;
} else {
return 'NULL';
}
if (!empty($value) || $value != '') {
return "'".str_replace("'","''",$value)."'";
} else {
return 'NULL';
}
}
Now, $object is always an object returned by the call, and $objectPath is always a string to points to a given value. Here's where the problem comes in. This works:
$value = $this->db->string($object, 'foo');
However, this does not work:
$value = $this->db->string($object, 'foo->bar->foo1->bar1');
Whenever $objectPath is more than "one layer" deep, I get the following error from (Amazon's) client library:
Fatal error: Call to undefined method MarketplaceWebServiceOrders_Model_Order::getFoo->Bar() in /path/to/Model.php on line 63
The code block that the error refers to is this:
public function __get($propertyName)
{
$getter = "get$propertyName";
return $this->$getter(); // this is line 63
}
$object is not XML, so I can't use SimpleXMLElement and XPath.
What is the problem with my code? Is it that am I concatenating an object and a string? If so, how can I make that possible? How can I get this function to do what I intended it to do?
By the way, I'm using PHP 5.4.27.
PHP doesn't automatically resolve a string containing multiple path levels to children of an object like you are attempting to do.
This will not work even if $obj contains the child hierarchy you are expecting:
$obj = ...;
$path = 'level1->level2->level3';
echo $obj->$path; // WRONG!
You would need to split up the path and "walk" through the object trying to resolve the final property.
Here is an example based on yours:
<?php
$obj = new stdClass();
$obj->name = 'Fred';
$obj->job = new stdClass();
$obj->job->position = 'Janitor';
$obj->job->years = 4;
print_r($obj);
echo 'Years in current job: '.string($obj, 'job->years').PHP_EOL;
function string($obj, $path_str)
{
$val = null;
$path = preg_split('/->/', $path_str);
$node = $obj;
while (($prop = array_shift($path)) !== null) {
if (!is_object($obj) || !property_exists($node, $prop)) {
$val = null;
break;
}
$val = $node->$prop;
// TODO: Insert any logic here for cleaning up $val
$node = $node->$prop;
}
return $val;
}
Here it is working: http://3v4l.org/9L4gc
With #itsmejodie's help, I finally got a working solution:
public function string($node, $objectPath) {
$value = NULL;
$path = explode('->', $objectPath);
while (($prop = array_shift($path)) !== NULL) {
if (!$node->$prop) {
break;
}
$value = $node->$prop;
$node = $node->$prop;
}
if (is_string($value)) {
return "'".str_replace("'","''",$value)."'";
} else {
return 'NULL';
}
}
The key for me was to see that, as #itsmejodie put it, "PHP doesn't automatically resolve a string containing multiple path levels to children of an object." In a string like, 'foo->bar->foo1->bar2', PHP won't convert the ->'s into the T_OBJECT_OPERATOR, thus appending a string to an object, e.g., $object->foo->bar->foo1->bar2, just won't work.

Does PHP grok null references?

I want to define a PHP function with an reference parameter, by default, a null reference. Not very much unlike what the following C++ code would do:
ReturnType my_function(moar lulz, ParameterType* ptr_to_my_param = 0)
{
// do some processing using lulz only
// ...
if (ptr_to_my_param)
{
// use *ptr_to_my_param
// or ptr_to_my_param->
// ...
}
// do more processing, again using lulz only
// ...
}
How do I do that in PHP?
If I understand you correctly, you want to do something like
class NiceClass {}
function foo($baz, NiceClass $bar = NULL) {
// Do some processing
if(bar !== NULL) {
// Do something with bar
$bar->yay($baz);
}
// Do some more processing
}
function my_function($value, $value = null) {
if(is_null($value)) {
} else {
}
}

Categories