Find directory structure in CodeIgniter - php

This is my CodeIgniter code to find the directory structure of a folder in my own server, but it is only going one level deep. I want to list all the subdirectories in the given $path. What is the error in this code?
function finddir($path)
{
$this->load->helper('directory');
$dir=directory_map($path,1);
//echo"$path";
foreach ($dir as $key => $subdir)
{
//echo $subdir."<br/>";
if(is_dir($subdir))
{
echo "<h3>$subdir</h3>";
$this->finddir($subdir);
}
else
{
echo "$subdir<br>";
}
}
}
The output goes only one level deep. Since I'm using recursion, I want it to go into deeper levels.

Try the RecursiveDirectoryIterator for this
function finddir($path)
{
$objects = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path), RecursiveIteratorIterator::SELF_FIRST);
foreach($objects as $name => $object){
echo "$name\n";
}
}

It makes more sense to make two functions so that you're not regenerating the array within a recursive function. This way it's generated once, and you are recursively getting values from just that one array. Unless the directory class is broken, you shouldn't need to check if it's a directory. If it's an array, it's a directory:
function finddir($path){
$this->load->helper('directory');
$dir=directory_map($path);
$this->recursive($dir);
}
function recursive($arr) {
foreach ($arr as $key => $val) {
if (is_array($val)){
echo "<h3>$key</h3>";
echo "<ul>\n";
$this->recursive($val);
echo "</ul>\n";
} else {
echo "<li>".$val."</li>\n";
}
}
}
Your break tags <br/> don't show the structure well, so I changed it to use nested lists.
I just noticed that you are pasing a value of 1 to the directory_map() function. That limits it to just one level, so you probably want to leave that out if you want to goes all the way with the recursion:
$dir=directory_map($path);

Why are you doint it this way?
The directory_map('source directory') returns you an array with sub-arrays (and sub-sub-arrays if applicable based on path).
You get the complete tree - just loop over array and print/use as needed, Use is_array($subdir) to test if its directory or file leaf.

instead of $dir=directory_map($path,1) remove number 1 so it displays this way $dir=directory_map($path) since that number will only return the first level directory only.

Related

Using scandir on array of directories

I would like to pass in an array that contains a list of directories to scan. I want to iterate over each directory and push its content into another array that I will print out, but for some reason my code is not working. The directories exist and the path is correct. I think it has something to do with how I'm using foreach.
These are the errors I'm getting:
Notice: Undefined index: C:\Users\john\Desktop\files\images\ in
C:\xampp\htdocs\test.php on line 6
Warning: scandir(): Directory name cannot be empty in
C:\xampp\htdocs\test.php on line 6
This is the code:
function test($dir = []) {
foreach($dir as $bar) {
$list = [];
array_push($list, scandir($dir[$bar]));
}
print_r($list);
}
test(["C:\Users\john\Desktop\files\images\\", "C:\Users\john\Desktop\files\images\autumn\\"]);
If anyone can think of a simpler way to do this, please don't hesitate to tell me.
You're on the right track. There are a few changes you need to make though.
function test($dir = []) {
$list = [];
foreach($dir as $bar) {
$list[] = scandir($bar);
}
print_r($list);
}
As noted by #BrianPoole you need to move the $list out of the foreach loop. By having it in the loop, the array is reset with each iteration, resulting in the final array having one element.
In addition, the foreach loop as explained above by #TimCooper does not operate the same as in JavaScript. If you really want to access the keys, you can use the following syntax:
foreach($dir as $key => $bar)
You would then use either $dir[$key] or $bar to access the directory value.
Finally, array_push is an additional function call that in your case is not needed. By simply adding [] PHP will push the new value onto the end of the array.
function test($dir) {
// DEFINE LIST OUT SIDE OF LOOP
$list = array();
// Run checks
if(count($dir) > 0) {
// Loop Through Directory
foreach($dir as $directory) {
// Push into list
array_push($list, array("scanned"=>$directory, "contents" => scandir($directory)));
}
}else {
// If no directories are passed return array with error
$list = array(
"error" => 1,
"message" => "No directories where passed into test()",
);
}
print_r($list);
}
This is how I would do it. It provides a couple checks and sets up the data so you can se it a bit more clear.

Nested foreach loops, PHP, how do I manipulate the values?

foreach ($bing_array as $bing_array_val)
{
foreach ($final_array as $final_array_val)
{
if ($final_array_val["link"] == $bing_array_val["link"])
{
$final_array_val["rank"] += $bing_array_val["rank"];
}
}
}
The above code has two foreach loops, which are nested.
It should test every bing_array["link"] against every final_array["link"] and if they are the same, the final_array["rank"] value should be += bing_array["rank"] but when I echo final_array, the ["rank"] values are unchanged.
I assume this is a syntax problem, where am I going wrong?
Thanks
You need to use the reference syntax (& prefix):
foreach ($final_array as &$final_array_val)
{
}
unset($final_array_val);
Note that the unset is required to break the reference to the last value. Read more here.
Here is the actual code you need :
foreach ($bing_array as &$bing_array_val)
{
foreach ($final_array as &$final_array_val)
{
if ($final_array_val["link"] == $bing_array_val["link"])
{
$final_array_val["rank"] += $bing_array_val["rank"];
}
}
unset(&$final_array_val);
}
unset(&$bing_array_val);
In your initial code, each time you were looping on $final_array, it was creating a temporary value called $final_array_val containing the content. Then, you modified it, and then it was replaced for each occurence of the foreach.
By passing the variables by reference, instead of creating a new temporary variable in the foreach, you use the actual variable which will keep the modifications you have done to it.

push into array while it is being looped

Here's a snippet of my code. I understand that it is generally advised against to modify an array whilst it is being looped, however in this scenario I think it is the right thing to do and saves resources.
$levels = array($post);
foreach ($levels as $level) {
$next_sibling = get_next_sibling_page($level);
if ($next_sibling) {
$next_page = $next_sibling;
break 1;
} else if ($level->post_parent) {
array_push($levels, get_page($level->post_parent));
}
}
The idea is that the foreach would reiterate with the pushed value if ($level->post_parent), I can see that the if statement is resolving to true and the array is being pushed to however the foreach does not reiterate and only runs the one time.
Does anyone know how I can recursively continue my foreach until $level->post_parent resolves to false?
To modify array which is being used in foreach pass array by referance.
foreach (&$levels as $level) {
.....
}

Better method to drill through array than nested loop?

I have a menu structure (Drupal) that contains elements representing a menu link. If the menu has a child menu (is the parent of a submenu) it has an array key 'below' with a menu item inside with the same structure. In theory this menu could be infinitely deep and the only way that I know how to go through each level is by create a new loop on the item if 'below' has anything in it. I know there has to be a more elegant and dynamic way to deal with this. I'm not really looking for a Drupal specific answer as this problem has come up before and I've just hacked it together. Thanks for reading!
Without an example of the kind of data you're working with it's difficult to give an exact implementation, but the general class of problems you describe is one that can usually be solved with recursion - a function that calls itself.
<?php
function deepPrintArr (array $arr)
{
$output = '';
foreach ($arr as $elem)
{
if (is_array $elem)
{
$output .= deepPrintArr ($elem);
}
else
{
$output .= "<p>$elem</p>\n";
}
}
return ($output);
}
echo (deepPrintArr ($some_deeply_nested_array));
?>
Please take time to "read" /inc/menu.inc in your drupal code.
This file contains a host of very usefull functions, some are not mentioned in any documentation.
menu_get_active_trail() for example gives you the current active path for the active page. This is a lot easier than looping recursive through all menu items in your own code.
For this site: Qrios I wrote some code to build a two level menu in Drupal:
<?php $base_tree = menu_tree_page_data($use_menu);
//Debug
//print_r($tree);
foreach ($base_tree as $item) {
if ($item['link']['in_active_trail'] == 1){
$tree = $item['below'];
}else{
$tree = array(); //empty array
}
} ?>
Not sure if this is what you mean, but you get the idea of using Drupals functions. You just have to find them.

How do you remove an array element in a foreach loop?

I want to loop through an array with foreach to check if a value exists. If the value does exist, I want to delete the element which contains it.
I have the following code:
foreach($display_related_tags as $tag_name) {
if($tag_name == $found_tag['name']) {
// Delete element
}
}
I don't know how to delete the element once the value is found. How do I delete it?
I have to use foreach for this problem. There are probably alternatives to foreach, and you are welcome to share them.
If you also get the key, you can delete that item like this:
foreach ($display_related_tags as $key => $tag_name) {
if($tag_name == $found_tag['name']) {
unset($display_related_tags[$key]);
}
}
A better solution is to use the array_filter function:
$display_related_tags =
array_filter($display_related_tags, function($e) use($found_tag){
return $e != $found_tag['name'];
});
As the php documentation reads:
As foreach relies on the internal array pointer in PHP 5, changing it within the loop may lead to unexpected behavior.
In PHP 7, foreach does not use the internal array pointer.
foreach($display_related_tags as $key => $tag_name)
{
if($tag_name == $found_tag['name'])
unset($display_related_tags[$key];
}
Instead of doing foreach() loop on the array, it would be faster to use array_search() to find the proper key. On small arrays, I would go with foreach for better readibility, but for bigger arrays, or often executed code, this should be a bit more optimal:
$result=array_search($unwantedValue,$array,true);
if($result !== false) {
unset($array[$result]);
}
The strict comparsion operator !== is needed, because array_search() can return 0 as the index of the $unwantedValue.
Also, the above example will remove just the first value $unwantedValue, if the $unwantedValue can occur more then once in the $array, You should use array_keys(), to find all of them:
$result=array_keys($array,$unwantedValue,true)
foreach($result as $key) {
unset($array[$key]);
}
Check http://php.net/manual/en/function.array-search.php for more information.
if you have scenario in which you have to remove more then one values from the foreach array in this case you have to pass value by reference in for each:
I try to explain this scenario:
foreach ($manSkuQty as $man_sku => &$man_qty) {
foreach ($manufacturerSkus as $key1 => $val1) {
// some processing here and unset first loops entries
// here dont include again for next iterations
if(some condition)
unset($manSkuQty[$key1]);
}
}
}
in second loop you want to unset first loops entries dont come again in the iteration for performance purpose or else then unset from memory as well because in memory they present and will come in iterations.
There are already answers which are giving light on how to unset. Rather than repeating code in all your classes make function like below and use it in code whenever required. In business logic, sometimes you don't want to expose some properties. Please see below one liner call to remove
public static function removeKeysFromAssociativeArray($associativeArray, $keysToUnset)
{
if (empty($associativeArray) || empty($keysToUnset))
return array();
foreach ($associativeArray as $key => $arr) {
if (!is_array($arr)) {
continue;
}
foreach ($keysToUnset as $keyToUnset) {
if (array_key_exists($keyToUnset, $arr)) {
unset($arr[$keyToUnset]);
}
}
$associativeArray[$key] = $arr;
}
return $associativeArray;
}
Call like:
removeKeysFromAssociativeArray($arrValues, $keysToRemove);

Categories