How can i fix the output of this recursion function - php

function my_recurse($id,$tree=array())
{
$hols = array();
$overall = array();
$asd = $this->db->get_where('story', array('story_id'=>$id))->row_array();
if(isset($asd['story_id'])){
$preds = explode(',',$asd['story_pred']);
if($preds[0] != 0)
{
$hols[] = $preds[0];
$hols = array_merge($tree, $hols);
$this->my_recurse($preds[0],$hols);
}
}
print_r($hols);
}
say for example i have this tree
story1 NULL
story2 story1
story3 story2
story4 story3
and when i enter story4 as my id in the function it always returns the story3 and not story1,story2 and story3. dont know why it reverses the output after the recursion happens. any suggestions would be appreciated

It's hard to say if this will be easy or possible without knowing your database structure, but you don't need recursion to find the path of a tree node to the root - you can do this with a self join. Also, you should avoid making a query in a recursive function. If you can describe your table structure I can attempt to show you how to get a result set that is the tree path in order.

The problem with your code is that its passing the current preds up to the parent hence the reverse tree. Instead it should be getting the preds from the parent
This should work if I understood your requirements clearly
<?php
function my_recurse($id) {
$hols = array();
$overall = array();
$asd = getFromDB($id);
if(isset($asd['story_id'])){
$preds = explode(',',$asd['story_pred']);
if($preds[0] != 0) {
$hols[] = $preds[0];
$hols = array_merge(my_recurse($preds[0]), $hols);
} else {
return $hols;
}
print "preds of {$id} : ";
print implode(', ', $hols) . "\n";
return $hols;
}
}
function getFromDB($id) {
$data = array(1 => array('story_id'=>1, 'story_pred' => '0'),
2 => array('story_id'=>2, 'story_pred' => '1'),
3 => array('story_id'=>3, 'story_pred' => '2'),
4 => array('story_id'=>4, 'story_pred' => '3'),
);
return $data[$id];
}
my_recurse(4);
Running the script above..
$ /usr/bin/php recurse.php
preds of 2 : 1
preds of 3 : 1, 2
preds of 4 : 1, 2, 3
PS: Please add your sample input, output and expected output. It took me 15 minutes to try understanding your problem.

Related

code needs to loop over minimum 2000 times in php foreach

I am having the foreach loop that will run minimum 2000 loops
foreach ($giftCardSchemeData as $keypreload => $preload) {
for ($i=0; $i <$preload['quantity'] ; $i++) {
$cardid = new CarddetailsId($uuidGenerator->generate());
$cardnumber = self::getCardNumber();
$cardexistencetype = ($key == "giftCardSchemeData") ? "Physical" : "E-Card" ;
$giftCardSchemeDataDb = array('preload' => array('value' => $preload['value'], 'expirymonths' => $preload['expiryMonths']));
$otherdata = array('cardnumber' => $cardnumber, 'cardexistencetype' => $cardexistencetype, 'isgiftcard' => true , 'giftcardamount' => $preload['value'],'giftCardSchemeData' => json_encode($giftCardSchemeDataDb), 'expirymonths' => $preload['expiryMonths'], 'isloyaltycard' => false, 'loyaltypoints' => null,'loyaltyCardSchemeData' => null, 'loyaltyRedeemAmount' => null, 'pinnumber' => mt_rand(100000,999999));
$output = array_merge($data, $otherdata);
// var_dump($output);
$carddetailsRepository = $this->get('oloy.carddetails.repository');
$carddetails = $carddetailsRepository->findByCardnumber($cardnumber);
if (!$carddetails) {
$commandBus->dispatch(
new CreateCarddetails($cardid, $output)
);
} else {
self::generateCardFunctionForErrorException($cardid, $output, $commandBus);
}
}
}
Like above foreach I am having totally 5 of them. When I call the function each time the 5 foreach runs and then return the response. It take more time and the php maximum execution time occurs.
Is there a any way to send the response and then we could run the foreach in server side and not creating the maximum execution time issue.Also need an optimization for the foreach.
Also In symfony I have tried the try catch method for the existence check in the above code it return the Entity closed Error. I have teprorily used the existence check in Db but need an optimization
There seems to be a lot wrong (or to be optimized) with this code, but let's focus on your questions:
First I think this code shouldn't be in code that will be triggered by a visitor.
You should seperate 2 processes:
1. A cronjob that runs that will generate everything that must be generated and saved that generated info to a database. The cronjob can take as much time as it needs. Look at Symfony's console components
2. A page that displays only the generated info by fetching it from the database and passing it to a Twig template.
However, looking at the code you posted I think it can be greatly optimized as is. You seem to have a foreach loop that fetches variable data, and in that you have a for-loop that does not seem to generate much variability at all.
So most of the code inside the for loop is now being executed over and over again without making any actual changes.
Here is a concept that would give much higher performance. Ofcourse since I don't know the actual context of your code you will have to "fix it".
$carddetailsRepository = $this->get('oloy.carddetails.repository');
$cardexistencetype = ($key == "giftCardSchemeData") ? "Physical" : "E-Card";
foreach ($giftCardSchemeData as $keypreload => $preload) {
$cardnumber = self::getCardNumber();
$carddetails = $carddetailsRepository->findByCardnumber($cardnumber);
$giftCardSchemeDataDb = array('preload' => array('value' =>
$preload['value'], 'expirymonths' => $preload['expiryMonths']));
$otherdata = array('cardnumber' => $cardnumber, 'cardexistencetype' =>
$cardexistencetype, 'isgiftcard' => true , 'giftcardamount' =>
$preload['value'],'giftCardSchemeData' =>
json_encode($giftCardSchemeDataDb), 'expirymonths' =>
$preload['expiryMonths'], 'isloyaltycard' => false, 'loyaltypoints' =>
null,'loyaltyCardSchemeData' => null, 'loyaltyRedeemAmount' => null,
'pinnumber' => 0);
$output = array_merge($data, $otherdata);
for ($i=0; $i <$preload['quantity'] ; $i++) {
$cardid = new CarddetailsId($uuidGenerator->generate());
$output['pinnumber'] = mt_rand(100000,999999);
if (!$carddetails) {
$commandBus->dispatch(
new CreateCarddetails($cardid, $output)
);
} else {
self::generateCardFunctionForErrorException($cardid, $output, $commandBus);
}
}
}
Also: if in this code you are triggering any database inserts or updates, you don't want to trigger them each iteration. You will want to start some kind of database transaction and flush the queries each X iterations instead.

Issue with PHP recursive function

I need to traverse the following type of structures:
P
/ | \
E1 E2 E3 .....
/ \ / \ |
V1 V2 V1 V2 V3 .....
| | | | / \
T1 T2 T3 T4 T5 T6 .....
In order to form an associative array with the following elements:
V1(key) = [T1(E1), T3(E2), ...]
V2(key) = [T2(E1), T4(E2), ...]
V3(key) = [T5(E3), T6(E3), ...]
.....
Now here comes the tricky part: the structure is actually simplified. I don't know beforehand how many E-level nodes I'll actually need to deal with (3 in the drawing), or how many V-level nodes each of them has (but at least 1 will be there), and furthermore, each V-level node may also have multiple T-nodes.
I tried using a recursion function to do this (in PHP). I'll simplify the code because it has weird methods regarding some objects that don't really matter to the discussion. My current attempt results in:
V1(key) = [T1(E1)]
V2(key) = [T2(E1)]
That I think means that the traversal is only occurring going down the first E-level "branch".
This is my code:
$result = [];
$traverser = function($node) use (&$traverser, &$result) {
$children = $node->getChildrenArray();
foreach($children as $key=>$child){
if ($child->nodeType() == 'v_node') {
$v_node_key = $child->name;
$t_nodes = $child->getChildrenArray();
if ( !array_key_exists($v_node_key, $results) ){
$results[$v_node_key] = [];
}
foreach($t_nodes as $keyt=>$t_node) {
$info_array = $t_node->toArray();
array_push($results[$v_node_key], $info_array);
}
} else if ($child->nodeType() == 'e_node') {
// keep digging
return $traverser($child);
}
}
};
$traverser($p_node);
I think the problem is that once I call the $traverser function within the foreach it won't come back and resume from the previous state.
Can anyone advise on how I should be tackling this to get the result I placed above?
Well, this is a bit awkward and I'm still not entirely sure if this is the right motive, but I solved this by removing the return in my code.
I thought that the return would allow me to exit the nested function call, but rather I think it jumped out of the first function call (the $traverser($p_node); line).
Even so, by changing the return $traverser($child); line to $traverser($child); it did what it had to do.
Hope this helps anyone!
I not show about what error you got but i suggest you to change you function to be this
function traverser($results, $node) {
$children = $node->getChildrenArray();
foreach($children as $key=>$child){
if ($child->nodeType() == 'v_node') {
$v_node_key = $child->name;
$t_nodes = $child->getChildrenArray();
if ( !array_key_exists($v_node_key, $results) ){
$results[$v_node_key] = [];
}
foreach($t_nodes as $keyt=>$t_node) {
$info_array = $t_node->toArray();
array_push($results[$v_node_key], $info_array);
}
} else if ($child->nodeType() == 'e_node') {
// keep digging
return traverser($child);
}
}
return $results;
}
Hope this help
Well, since you know that you'll have P->E->V->T-nodes, you could simply go for multiple foreach-loops, like this
foreach($p_node->getChildren() as $e_node) {
$e_node_key = $e_node->name;
foreach($e_node->getChildren() as $v_node) {
$v_node_key = $v_node->name;
foreach($v_node->getChildren() as $t_node) {
$t_node_key = $t_node->name;
// do whatever it needs to array_push to results
}
}
}

Explode DB record into tabular format

I have a database connected to my site that is recording actions taken by a user on an application we have built. The application holds a number of images, which the user can view and download, and the application tracks, per user, which image they have viewed delimited by a comma
What I want to do is write a PHP script that will allow me to turn this delimited text string into a calculated table format
If the database has example records of:
1 | 1-A, 1-B, 2-A, 2-C
2 | 1-A
3 | 1-B, 2-C
4 |
5 | 1-A, 1-B, 1-C, 2-A
To which I wanted to write a script that would be able to output this as:
1-A = 3
1-B = 3
1-C = 1
2-A = 2
2-C = 2
(I want to point out, I'm not suggesting that I want to have variables named after each entry, with its calculated total as the value, I'm open to however is best to return this value)
My first step was to explode each record into an array, but then I wasn't sure as to my next step to turn this into something I can write into a table.
I realise the database is very poorly structured, but unfortunately I haven't written it, and so I have no ability to re-structure the data is stored.
Kind regards
this should work:
<?php
$values = array (
'1' => '1-A, 1-B, 2-A, 2-C',
'2' => '1-A',
'3' => '1-B, 2-C',
'4' => '',
'5' => '1-A, 1-B, 1-C, 2-A'
);
$valueCounts = array();
foreach($values as $value)
{
foreach(explode(', ', $value) as $val)
{
if(!key_exists($val, $valueCounts))
{
if($val) $valueCounts[$val] = 1;
}
else
{
$valueCounts[$val]++;
}
}
}
ksort($valueCounts);
foreach($valueCounts as $value => $count)
{
echo "$value = $count<br />";
}
?>
result:
1-A = 3
1-B = 3
1-C = 1
2-A = 2
2-C = 2
if you're not sure how to populate the $values array, please paste the database code and I will try and incorporate it into the above.

Access Control Problem

I am developing an access control library for my project and I am looking to the best solution to do this:
I am getting all my access list from database to an array. In result it looks like this:
$array = array(
'*' => array('administrator' => TRUE),
'frontend/*' => array(
'user' => TRUE,
'unregistered' => TRUE
),
'backend/*' => array(
'user' => FALSE,
'unregistered' => FALSE
),
'backend/user/*' => array(
'moderator' => FALSE,
'supermoderator' => TRUE,
),
'backend/article/*' => array(
'supermoderator' => TRUE
),
'backend/article/add/new' => array(
'moderator' => TRUE
)
);
The " * " means this user has access all of that related options backend/article/* means that group have access to all article options (article/add, article/remove, ...).
As you see the there is no item in backend/article/add for supermoderator but it has the master access to all article pages.
What is the best way to check this? I tried array_walk() but I guess it wont help me.
Thank you for advices...
I can share my whole code if you want.
* Edit *
Am I storing wrong? If you have the better solution to store it I will be happy to hear it.
Thank you for any advices
No matter what this is going to be a complex algorithm, a simple array_walk wont do. Unless someone is feeling particularly generous and will write one for you, I suggest you hire a programmer.
Am I storing wrong? If you have the better solution to store it I will be happy to hear it.
It totally depends on your algorithm. You can probably write one that uses your current data format. You can also probably write a simpler one if you change your data format. But what your data format should look like then, well, that's a job for a programmer.
I found the answer myself:
lets say the user trying to access backend/article/add/new and this user in the supermoderator group. So I need to look for backend/*, backend/article/*, backend/article/add/*. array_slice() and for() enough for this:
I am using CodeIgniter by the way. I modified it a little bit to seperate frontend and backend controllers. I am not using application/controller directory. I am using application/backend and application/frontend directories for controllers.
So an uri pattern is this: http://site.com/[backend]*/[directory]*/class/method
// This is the page that user trying to reach
$requested_page = "backend/article/add/new";
// pharsing...
$x = explode('/', $requested_page);
// this is needed to cut last 3, 2, 1 items of $x
$i = count($x) > 3 ? -4 : -count($x);
for (; $i < 0; $i++) {
$resource = implode('/', array_slice($x, 0, $i)) . '/*';
// echoing for debug
echo $resource;
}
// Outputs:
// backend/*
// backend/article/*
// backend/article/add/*
function userHasPermissions($permissionsArray, $user, $path) {
// Check exact
if(isset($permissionsArray[$path]) &&
isset($permissionsArray[$path][$user])) {
return $permissionsArray[$path][$user];
}
// Check lower and lower
$partArr = explode('/', $path);
for($i = substr_count($path, '/'); $i >= 0; $i--) {
if($i > 0) {
$choppedPartArr = array_slice($partArr, 0, $i);
$newPath = implode($choppedPartArr, '/') . '/*';
} else {
$newPath = '*';
}
if(isset($permissionsArray[$newPath]) &&
isset($permissionsArray[$newPath][$user])) {
return $permissionsArray[$newPath][$user];
}
}
return false;
}
echo "Result: " . (userHasPermissions($array, 'supermoderator', 'backend/article/add') ? "true" : "false");
Note that 'backend/article' will return false for 'supermoderator' since 'backend/article/*' does not match it. To change this, simply change $i = substr_count($path, '/'); to $i = substr_count($path, '/')+1;.

LinkedIn type friends connection required in php

I am creating a custom social network for one of my clients.
In this I am storing the friends of a user in the form of CSV as shown below in the user table
uid user_name friends
1 John 2
2 Jack 3,1
3 Gary 2,4
4 Joey 3
In the above scenario if the logged in user is John and if he visits the profile page of Joey, the connection between them should appear as
John->Jack->Gary->Joey
I am able to establish the connection at level 1 i.e
If Jack visits Joey's profile I am able to establish the following :
Jack->Gary->Joey
But for the 2nd level I need to get into the same routine of for loops which I know is not the right solution + I am not able to implement that as well.
So, can someone please help me with this?
Thanks in Advance,
Akash
P:S I am not in a position to change the db architecture :(
Here's some bfs code I had written in ruby; it should give you a good enough idea of how things work to translate it to php. the other change you'll need to make is to replace graph[current] with a db query to get the current user's friends.
def bfs(graph, start, stop)
queue = [start]
visited = {}
parents = {}
current = nil
while true
if queue.empty?
return nil
end
current = queue.shift
if current == stop
return read_path(current, parents)
end
visited[current] = true
graph[current].each do |i|
if not visited[i] and not queue.index(i)
parents[i] = current
queue.push(i)
end
end
end
end
def read_path(node, parents)
a = [node]
while parents[node]
a.push(parents[node])
node = parents[node]
end
return a.reverse
end
GRAPH = {
"a" => ["b", "c"],
"b" => ["c", "d"],
"c" => ["a", "e"],
"d" => ["b", "c", "f"],
"e" => ["c", "f"]
}
path = bfs(GRAPH, "a", "f")
p path
Here's some sample code:
<?php
$currentUID = 1; // The logged in user
$pageUID = 4; // The user whose page is being visited
// Parse the CSV
$csv = explode("\n", $csvData);
$csvlen = count($csv);
for($i=0;$i<$csvlen;$i++) {
$csv[$i] = explode(",", $csv[$i]);
}
function getFriends($csv, $uid) {
foreach($csv as $user)
if($user[0] == $uid)
return explode(',', $user[2]);
}
$userFriends = getFriends($csv, $currentUID);
$pageFriends = getFriends($csv, $pageUID);
$friendPool = array();
foreach($userFriends as $friend) {
$hisFriends = getFriends($friend);
foreach($hisFriends as $subFriend) {
if(in_array($subFriend, $pageFriends)) {
if(isset($friendPool[$friend]))
$friendPool[$friend][] = $subFriend;
else
$friendPool[$friend] = array( $subFriend );
}
}
}
foreach($friendPool as $friend=>$subFriends)
foreach($subFriends as $subFriend)
echo "$currentUID -> $friend -> $subFriend -> $pageUID\n";

Categories