Posts tagged ‘function’

Getting Directory Tree – Recursive VS Non-Recursive

I took some time to examine between the performance of using a recursive and non recursive method of retrieving a directory tree. Of-course retrieving a directory tree is possible with out using any functions and still be able to get the full tree with unlimited depth possibilities. I made the non recursive piece of code that outputs an array of each file and folder/subfolder of a given directory and a recursive function that calls it self on each new folder found. And then before an after I took a memory usage survey and time lapse of code execution. The folder that i used for testnig had some random folders and files and a freshly downloaded codeigniter frame work. So there were about 360 items in the generated array of files and folders.

Non-Recursive Code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$tree=array('folder/');
$folders_to_do = array('folder/');
$current_folder = '';
while(!empty($folders_to_do)){
	$key_of_current_folder = key($folders_to_do);
	$current_folder = $folders_to_do[$key_of_current_folder];
	$folder = scandir($current_folder);
	foreach($folder as $item){
		if(is_dir($current_folder.$item) and $item != '.' and $item != '..'){
			$folders_to_do[] = $current_folder.$item.'/';
			$tree[] = $current_folder.$item.'/';
		}
		if(is_file($current_folder.$item))
			$tree[] = $current_folder.$item;
	}
	unset($folders_to_do[$key_of_current_folder]);
}
sort($tree);

The way it works is very simple. It runs only on 2 loops (while & foreach), 1 array (folders_to_do) and 1 string variable (current_folder) to control what we are scanning. The outer loop will always loop it self as long as it has something to scan, that means if the to-do array is not empty it will loop again. Now inside the while loop we have the regular scandir() function and foreach loop. in the foreach loop when a file is found it is added to the main tree array but if a folder is found then it is not only added to the tree array but is also added to the to-do list. Since the to-do list has something new to scan, the while loop will not brake. At the end when there is no more directories to scan the tree array will contain every folder and file found in the initial directory passed. All that is left to do is to sort the array and you get a neat list of each files and folders of a passed directory.

Recursive:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$tree2 = recursive('folder/');
function recursive($current_folder){
	static $tree = array();
 
	$folder = scandir($current_folder);
	foreach($folder as $item){
		if(is_dir($current_folder.$item) and $item != '.' and $item != '..'){
			$tree[] = $current_folder.$item.'/';
			recursive($current_folder.$item.'/');
		}
		if(is_file($current_folder.$item))
			$tree[] = $current_folder.$item;
	}
	return $tree;
}

This method is also straight forward. It uses a static array inside the function to hold the tree, optionally you can keep passing the tree as an argument if you do not wish to use a static array or if you simple going to use the function multiple times on the same script. The function it self takes in only 1 string variable which is the folder it is going to process. The foreach loop does same thing basically; if it is a file it only adds to the tree array and if it is a folder it adds to the tree array and calls the function with the folder path as teh argument. The recursion concept is no different then any other. It was only simplified ridiculously.

Result Array:

Array
(
    [0] => folder/
    [1] => folder/another folder/
    [2] => folder/another folder/bar file
    [3] => folder/another folder/dddddd/
    [4] => folder/another folder/dddddd/lost track/
    [5] => folder/another folder/dddddd/lost track/last file
    [6] => folder/another folder/hhh/
    [7] => folder/another omg folder/
    [8] => folder/codeigniter omg/
    [9] => folder/codeigniter omg/index.php
...

Both of those codes will produce exactly the same thing but using different amount of memory. Above you can see a steddy array with each folder and file name with full paths.

Performance:
AS I said earlier I had about 360 files and folder combined for testing.

The time in milliseconds took to execute was the same on average of those methods. I refreshed many times and it was always either same or one or the other had few more milliseconds.

The memory usage for the non recursive was 82,512 and for the recursive method it was 116,112. I used the memory_get_usage() function to find those numbers. For each method I took memory usage before the code and after the code. So the results are (after_code – before_code) for first and second methods, just to clarify so no one has questions to how I have come to my conclusion.

82,512 / 116,112 = 0.71 ratio!

All testing was done here: http://www.sandbox.doc776.org/recursion/script.php

Get File Extension

Very handy PHP function to get the file extension of a file name like something.jpg

Method 1: (best way)

1
2
3
4
5
6
7
8
9
10
11
// get_file_extension ( string [the file name] , boolean [lower case] )
function get_file_extension($str,$low = true){
	$i = strrpos($str,'.');
	if (!$i)
		return '';
	$l = strlen($str) - $i;
	$ext = substr($str,$i+1,$l);
	if($low == true)
	$ext = strtolower($ext);
	return $ext;
}

Method 2:

1
2
3
4
5
6
7
8
9
10
// get_file_extension ( string [the file name] , boolean [lower case] )
function get_file_extension($str,$low = true){
	$ar = explode('.',$str);
	if(empty($ar))
		return '';
	$ar = array_reverse($ar);
	if($low == true)
	$ext = strtolower($ar[0]);
	return $ext;
}

Both functions produce the same output but one uses the string functions and the other uses array function. If you think about it, to manipulate and create arrays take more memory then manipulating regular text. (internally arrays are created using those same string manipulation methods) So method one is better because it uses most simple way to get what you need.

Make Code

Very simple handy little function to generate a random code of number and lower and upper case letters.

Straight forward stuff…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function makecode($chars = 6)
{
	for($i=0; $i<=($chars-1); $i++)
	{
		$r0 = rand(0,1);
		$r1 = rand(0,2); // frequency of lower case
		if($r0==0)
			$r .= chr(rand(ord('A'),ord('Z')));
		elseif($r0==1)
			$r .= rand(0,9);
		if($r1==0)
			$r = strtolower($r);
	}
	return $r;
}

$r0 controls if the next digit is a number or a letter
$1 controls if teh letter will be upper or lower. Set it to 0 if you want it to always be lower or change the range to what ever suits you.
Also a number maybe passed to the function to change how many digits in the code to make.

Converting Bytes To Better Format

Every now and then we need a simple function that could take any number in bytes taken from filesize() or other source and convert it for better reading. Here are some functions that do the exact same thing but with different methods.

Method 1: Very simple straight forward stuff. Can be customized to different sizes with different limits.

1
2
3
4
5
6
7
8
function convert_size($num)
{
    if ($num >= 1073741824) $num = round($num / 1073741824 * 100) / 100 .' gb';
    else if ($num >= 1048576) $num = round($num / 1048576 * 100) / 100 .' mb';
    else if ($num >= 1024) $num = round($num / 1024 * 100) / 100 .' kb';
    else $num .= ' b';
    return $num;
}

Method 2: (best way) More advance using a loop to count size depth. Very small and compact all the way to YB. Unlike method 1 it runs by a single constant.

1
2
3
4
5
6
7
8
9
10
function convert_size($size){
  $i=0;
  $iec = array(" B", " KB", " MB", " GB", " TB", " PB", " EB", " ZB", " YB");
  while (($size/1024)>1)
  {
   $size=round($size/1024,2);
   $i++;
  }
  return substr($size,0,strpos($size,'.')+4).$iec[$i];
}

As you see if you wish to have a large range conversion using the first method then you will have lots of IF statements unlike method two. Iif you are using method one you have more simple flexibility to have different off-sets for sizes.

Method two uses a single while loop to take the size to its lowest by every 1024 and each time it loops it reduces a single multiple of 1024 and counts how many times it reduces it, and at the end shows the corresponding unit size from the array using that count. The array must have the unit sizes in correct order. Good thing about this is if you give it a number that is like 7^20 (that’s 20 zeroes), then it still works just fine as long as the array has that many unit sizes.

Displaying Page Load Time

It is very simple to show page load times in PHP.

Method 1:
Place this at the very begin of your PHP code:

1
define('STARTING_MICROTIME', get_microtime());

Depending on how you like to organize your code have those functions some where, they are pretty straight forward:

1
2
3
4
5
6
7
8
9
function execution_time()
{
    return sprintf("%01.4f", get_microtime() - STARTING_MICROTIME);
}
function get_microtime()
{
    $time = explode(' ', microtime());
    return doubleval($time[0]) + $time[1];
}

And finally just use the execution_time() function to show the total time elapsed since the script started.

Method 2:
Place this at very beginning of your code.

1
define('PAGE_LOAD_START', microtime(true));

And this where you want to display the render time.

1
<?php echo round((microtime(true) - PAGE_LOAD_START), 4); ?>

This way is easier IMO. Using constants is not necessary, it just avoids problems in namespaces if your code is OOP like.

Easy Image Class

I needed something simple to grab images from MySQL database and display them. Slowly it grew into a very handy class.

This class can be used to manipulate images stored in files or in a MySQL database. It can read images from files and store them in a MySQL database table, and vice-versa. The class can also convert images between GIF, JPEG and PNG formats, as well resize the images to create thumbnails.

Documentation:
Coming Soon…

Image Thumbnail / Resize Function

Log time ago I wrote this function for my friend’s gallery website. The function is straight forward and uses the GD library of PHP. You may resize the image by giving it the maximum width and height you want the thumbnail to have. The function acts as an extension of the GD library so to speak, so you have to pass the image resource. An additional argument may be passed stating if you want the thumbnail to be square or not, by default it is false. If it is true the width:height ratio will not be change, it only fills the empty space with black color and places the thumbnail image in the center of the square.

imagethumb(image resource, max width, max height, fill to make square)
1 – (GD image resource) Straight forward teh image resource loaded by GD library.
2 – (int) maximum width of thumbnail
3 – (int) maximum width of thumbnail
4 – (true/false) make thumbnail square

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
function imagethumb($old_im, $limit_w = 100, $limit_h = 100, $fill = false)
{
	//get original dimensions
	$old_w = imagesx($old_im);
	$old_h = imagesy($old_im);
 
	//first resize by width if overflow else set old to new
	if($old_w > $limit_w)
	{
		//get reduction percent
		$reduce_percent = ($limit_w / $old_w) * 100;
		//set new w and h
		$new_w = ($old_w / 100) * $reduce_percent;
		$new_h = ($old_h / 100) * $reduce_percent;
	}
	else
	{
		//set as the old dimensions
		$new_w = $old_w;
		$new_h = $old_h;
	}
 
	//second resize by height if still overflows
	if($new_h > $limit_h)
	{
		//get reduction percent
		$reduce_percent = ($limit_h / $new_h) * 100;
		//set new w and h
		$new_w = ($new_w / 100) * $reduce_percent;
		$new_h = ($new_h / 100) * $reduce_percent;
	}
 
	if($fill == true)
	{
		//height offset picture, to keep centered
		if($new_h < $limit_h)
			$h_offset = floor(($limit_h - $new_h)/2);
		else
			$h_offset = 0;
 
		//width offset picture, to keep centered
		if($new_w < $limit_w)
			$w_offset = floor(($limit_w - $new_w)/2);
		else
			$w_offset = 0;
 
		//make new image and copy to it
		$new_im = imagecreatetruecolor($limit_w, $limit_h);
	}
	else
		$new_im = imagecreatetruecolor($new_w, $new_h);
 
	imagecopyresampled($new_im, $old_im, $w_offset, $h_offset, 0, 0, $new_w, $new_h, $old_w, $old_h);
 
	return $new_im;
}