Tuesday, April 29, 2014

Arrays in PHP - 2

In my previous article Arrays in PHP, I had discussed about creating and printing arrays. In this article, I would discuss some more things we can do with Arrays. But before that, let's check one complex array below :

<?php

$arr = array (
          
'name'      => 
   array('first_name'  => 'Robin', 
         'middle_name' => '', 
         'surname'     => 'Smith'),
'dob'       => '14/Jan/1980',
'lucky_nos' => array( 
         'numerology_based'    => 1, 
         'chinese_astro_based' => 2 ),
'grade_years' => array( 
         '2001' => 'A+', 
         '2002' => 'B+', 
         '2003' => 'C+' ),
'ph_nos'    => array(
         '2202-3256', 
         'father' => '4545-2256')
);

?>

In the above multi-dimensional associative array, a student info has been stored under various "key" => "value" pair combinations.

Now, if we want to add some more information to it, we would do it the following way.

<?php
$arr['favourite_color'] = array('blue','pink') ;
?>

So, a new key 'favourite_color' will be created holding an array of 'blue' and 'pink'. But, if we do it the following way ::

<?php
$arr[] = array( 'favourite_color' => array('blue','pink') ) ;
?>

next available index 0 (Zero) will be created. So all the indices within the array will be 'name', 'dob', 'lucky_nos', 'grade_years', 'ph_nos' and 0 (zero). It is same as writing the following statement (in this context)::

<?php
$arr[0] = array( 'favourite_color' => array('blue','pink') );
?>

In case of Associative arrays, the keys would have automatically casts to Integers as shown below :

<?php
$arr = array (  
        0     => 'Philippe',
        '0'   => 'John',  // String to Integer 0
0.9   => 'Mick',  // Float to Integer 0
false => 'Andrew', // 'false' is cast to Integer 0
NULL  => 'Rahman' // NULL is cast to '' 
       );

print_r( $arr );
?>  

All the index '0' (as string), '0.9' (as float/double), 'NULL' (as NULL), 'false' (as boolean) will be converted to integer 0. In case of floating point number, the fractional part is totally discarded, hence, for example, '1' will be accepted as key for given keys like '1.23', '1.67' or '1.09'. In our example above, first four keys are evaluated to 0 (zero) and hence $arr[0] will have the last value 'Andrew' as every time old value is overwritten. The key 'NULL' is cast to empty string. Check out the output below :

Array
(
    [0] => Andrew
    []  => Rahman
)
  
Copying an array to another is very simple. All the 'key'=>'value' combination are preserved while copying to another array.

<?php
// Simple copy
$arr2 = $arr;
?>

However, if we create a reference to that array, any changes to original array would reflect in the reference. Check the example below.

<?php
// Create a reference
$arr3 = & $arr;
?&gt;

Next, we would work on some array operators. Let's start with '+' operator, which unions two array. Check the example below.

<?php
// First array : index 0,1
$arr1 = array( 1, 2 );

// Second array : index 0,1
$arr2 = array( 2, 3 );

// Add them using '+' operator
// $arr1 if first operand
print_r( $arr1 + $arr2 );

// $arr2 is first operand
print_r( $arr2 + $arr1 );
?>

The output will be

Array ( [0] => 0 [1] => 1 )
Array ( [0] => 2 [1] => 3 )

In case of $arr1 + $arr2, as the $arr2 also has index '0' and '1', $arr1's (former array) values will be preserved. Hence the first print_r() print's $arr1's values.

But in case of $arr2 + $arr1, the second print_r() prints just the opposite as first operand $arr2's values are preserved. Check another example ::

<?php
// both the Arrays have indices 0 and 1
$arr1 = ('apple','banana');
$arr2 = ('mango','orange');

// Outputs array(0=>'apple', 1=>'banana')
print_r( $arr1 + $arr2 );
?>

Here the first array - $arr1's index/value pairs will be preserved as the second array also has the same indices.

If both the arrays have different key=>value combinations, the resultant array can have all the key=>value combinations as shown in the example below ::

<?php
// First array : index 0,1
$arr1 = array( 1, 2 );

// Second array : index 2,3
$arr2 = array( 2=>2, 3=>3 );

// Add them using '+' operator
// $arr1 if first operand
print_r( $arr1 + $arr2 );

// $arr2 is first operand
print_r( $arr2 + $arr1 );
?>

Here is the output

Array ( [0] => 1 [1] => 2 [2] => 2 [3] => 3 )
Array ( [2] => 2 [3] => 3 [0] => 1 [1] => 2 )

In the first case, $arr2's index and value combinations are appended to $arr1's same combination whereas in the second case, $arr1's key value pairs are appended to $arr2's same pairs.

Next, we can use operators '==', '<=', '>=' for array comparisons. Keys should match on both the arrays for such comparison. Array with lesser number of values are smaller in case of comparison. If keys are same, then values are checked. Check the example below :

<?php
$arr1 = array ( 1=>1, 2=>2 );
$arr2 = array ( 2=>2, 3=>3 );

// Uncomparable as index don't match
echo $arr1 == $arr2;

$arr1 = array ( 1=>1, 2=>2 );
$arr2 = array ( 1=>2, 2=>4 );

// $arr2 has larger values
echo $arr1 <= $arr2; // Outputs 1
?>

Let's see how to deal with situations where a function returns an array.

<?php
// Define a Function which returns array
function return_array(){ return array(1,2,3,4); }

// As of PHP 5.4, access the 3rd element
$fourth_elem = return_array()[3];

// Otherwise, store it in some
// other variable
$arr = return_array();

// Access Array Items
$fourth_elem = $arr[3];
?>


Objects and other data types can be typecast to array. Check out some examples below : 

<?php
class a
{
  private $name = 'Robin';
  private $age  = 15;
  public  $roll = 1;
  protected $ph = 909090;
}

// Create Object of class a
$obj = new a();

// Object to Array
$arr = (array) $obj ;

// Member names will be the 
// KEYS in the resultant array
print_r( $arr );

// String to Array
$name = 'Robin' ;
$arr = (array) $name;
print_r( $arr );
?>

While typecasting from Object, the member variables' names become the keys of the resultant array. For private variables, class is prepended to the key, for public variables the key is same as the variable name, for protected variable a '*' is prepended to the key. The characters/strings which are prepended to the key are wrapped by NULL (\0)  characters. These NULL characters can be seen if we var_dump() the array.

Check the output of the above code :

Array
(
    [aname] => Robin 
    [aage] => 15
    [roll] => 1
    [*ph] => 909090
)
Array
(
    [0] => Robin

)

Arrays types are very flexible in PHP. We would discuss more on it in later articles.

Thursday, April 03, 2014

Creating Image Upload Previewer with Javascript/PHP

This is a very common scenario on Web Applications that an user selects an image for uploading and he is shown the preview of that image immediately before the user clicks on the "Submit" button. 

How is it possible when the image file is not even uploaded to Server? From where the <img> element is showing the right image? How is it happening without refreshing the page?

Actually this involves both Server and Client side interactions which is the subject matter for this discussion. 

We have the following screen, 





and it changes to this immediately after the user selects an image from his Hard Disk.




To achieve our goal, let's chalk out the essential points : 

1. We need an <input type="file"> element within a <form> element.
2. The <form> element must be submitted for uploading/sending a file to Server. As form submission refreshes the page, we'll be using a little trick here.. we'll take help of an <iframe> element and submit our form onto this <iframe>. This will not refresh the page, but upload the file to the server.
3. When the image file is successfully uploaded, we just change the "src" property of an <img> element on our page to load the image and show the preview.

Let's check out the HTML we need.

<html>
<head>
<script src="jquery.min.js" ></script>
<script src="imageupload.js"></script>
</head>
<body>

<!-- FORM starts here -->
<form name="f" enctype="multipart/form-data" method="post" target="myiframe" action="imageupload.php">

Select Image files :: <br> <br>

<div style='padding-bottom:10px'>

<!-- FILE INPUT element -->
<input type="file" name = "file1" onchange="upload_and_show_image(this)"> 

<!-- <img> Tag to display the Preview -->
<img id='image1' border='0' src='' width="100" style='display:none'/> 

<!-- We have a SPAN element to show messages -->
<span id='message1'></span>

</div>

<!-- An Hidden Field -->
<input type='hidden' name='form_submit' value='1'>

</form>

<!-- THE IFRAME which is kept Hidden -->
<iframe name="myiframe" style="display:none"></iframe>

</body>
</html>

Notes : 
1. We have included JQuery Library for all our client-side working.

2. We have put all our client-side logic in imageupload.js file, which we'll be dissecting soon.

3. We have a Server Side PHP file imageupload.php where the form is actually submitted. This Server Side file handles the uploaded image and renames/stores it.

4. Check the declaration of <input> element :
   <input type="file" name = "file1" onchange="upload_and_show_image(this)"> 
   
   The javascript function "upload_and_show_image()" is called when user selects a new Image. We are capturing the "change" event on this element. The included script "imageupload.js" contents the whole upload_and_show_image() function definition.
   
5. Check the declaration of <iframe> element :
   <iframe name="myiframe" style="display:none"></iframe>
   
   We have given a name "myiframe" to it and it is kept hidden on our page.
   
6. Check the declaration of <form> element :
   <form name="f" enctype="multipart/form-data" method="post" target="myiframe" action="imageupload.php">
   
   For uploading a file, "enctype" property should have a value "multipart/form-data", "method" should be "post". The <form> will be submitted to a PHP page "imageupload.php". Check out its "target" property which holds "myiframe" i.e the <form> will be subitted to "imageupload.php" within an <iframe> having a name "myiframe". This way, ONLY the said <iframe> will be refreshed, not other parts of our page.
   
7. We have the <img> element which is kept hidden initially. When an Image is fully uploaded and available to browser for displaying, we change the "src" property of this element to load it on the browser.
   
   <img id='image1' border='0' src='' width="100" style='display:none'/> 
   
8.  We have a SPAN element where we can show error messages etc. 
    <span id='message1'></span>  

9. We also have a hidden value within the form. With this, we can track the form submission at the server end.
   
   <input type='hidden' name='form_submit' value='1'>
   

Let's check out the JS file "imageupload.js". What it does is shown point wise below :

1. Get the fileName. On some browser, we get whole path of the image like "c:\fakepath\sampleImage.jpg", we need to extract the name of only the image.

2. We would replace all the spaces with hyphens ("-") within the file name.

3. We also check for valid image extensions. If wrong files are selected, no preview will be available.

4. After we submit the form using Javascript, we create an Image object, load the image with it and when loading is completed, we change the "src" property of the <img> element on our page to show the preview.

Now the content ...

function upload_and_show_image(t)
{
  // Initially Hide the <img> element
  jQuery("#image1").hide();
  
  // get the Selected Image File's name 
  var filename = jQuery(t).val() ;
  filename = filename.toLowerCase()
  
  /// Take the actual File name
  /// Remove the path information 
  var pos = filename.lastIndexOf("\\");
  if(pos!= -1)
  {
    filename = filename.substr(pos+1);
  }
    
  // GET Extension
  var extn = filename.split('.').pop();
  
  // Remove all spaces with hyphens
  var fname = filename.split('.')[0].replace(/\s/g,"-");
  var filename_final = (fname + "." + extn);
  
  // Locate to the SPAN for a message
  var sp = jQuery("#message1");
  
  // Check for valid Image extension
  var valid_extn = ['jpg','jpeg','png','bmp','gif','tiff','wmf'];
  var matched = 0;
  for(var i in valid_extn) 
   if(valid_extn[i] == extn) matched = 1;
  
  if( !matched )
  {
     // Show an Error message in the SPAN element
     jQuery(sp).html("Please select a valid Image file").css("color","red");
     
     // RETURN
     return;
  }
  
  // Proceed, Clear the SPAN content
  jQuery(sp).html("");
  
  // Submit the form
  jQuery("form[name='f']").submit();
  
  // Create New Image  
  var i = new Image();
  i.src = filename;
  
  // Do some tasks when Image is loaded
  i.onload = function()
  { 
   // Change the 'src' property
   jQuery("#image1").attr("src", i.src);
 
    // Show the preview <img>
   jQuery("#image1").show();   
  }
  
}

The above JS code is quite self-explanatory. The input (type 'file') gets full path name usually like "c:\abcd.jpg" (On windows system) etc. So, we need to extract only the file name out of it. And here, the lastIndexOf() and substr() function helps us to achieve it.

var pos = filename.lastIndexOf("\\");
filename = filename.substr(pos+1);

Now check the Server side code for handling the Image upload. Here is the content of "imageupload.php" file. 
  
<?php
// Check whether form is submitted
if(isset($_POST['form_submit']))
{
  // Check if FILE has been uploaded
  if( isset($_FILES['file1']['tmp_name']) && $_FILES['file1']['error'] == 0 )
  {
    
// Get the filename corrected
$file_name = strtolower($_FILES['file1']['name']);
$a = explode(".", $file_name );
$name = str_replace(" ","-",$a[0]);
$extn = $a[1];

// Check for Valid extensions
   $extns = array('jpg','jpeg','png','bmp','gif','tiff','wmf');
    
// Found
if( in_array( $extn, $extns) !== false )
{
     // Upload
    @move_uploaded_file( $_FILES['file1']['tmp_name'], "$name.$extn");
}
  }
}
?>

The above piece of HTML, JavaScript and PHP codes work together to effect this Image preview. 

But there is a problem with the above working model. Check out this section in JS file:

  // Submit the form
 jQuery("form[name='f']").submit();
  
 // Create New Image  
 var i = new Image();
 i.src = filename;

Here, we are submitting the form and then immediately we are creating an Image object in Javascript and changing its "src" property. Problem is, the Server may be quite busy while processing the uploaded file, renaming it and moving it etc or Server is taking time as the image has huge file size. But client side javascript does not wait for the server side PHP to finish its own task. So, sometimes, the Image loading (changing the 'src' property of 'img' tag) is done when the Image was not even created/moved by PHP code. As a result, the  <img> element (meant to show the preview) does not show the image and we get an error message "Network Error : file not found"  in browser console. An broken Image icon is displayed on browser. However if you right click on the broken-image icon and click "Reload Image" for 1 or more times, the image is shown perfectly (by that time the Image is available at the server-end). To work around with this, we may follow the following logic :: 

1. Submit the form
2. Run a timer, which fires a JS function check_for_image_existence() every 100 miliseconds. This function fires an Ajax request to server to query whether the image file is now existing. So, every 100 milliseconds, an Ajax call is fired.
3. If the Ajax response is satisfactory (Image exists), then we change the "src" property of the <img> element on our page. 

To achieve this, we need to change our JS file first as shown below, We are showing only the updated lines: 

     // Submit the form
  jQuery("form[name='f']").submit();
  
  /*  COMMENT OUT OLD CODE
  var i = new Image();
  i.src = filename;
  i.onload = function()
  { 
jQuery("#image1").attr("src", i.src);
jQuery("#image1").show();   
  }
  */
  
  // SET a Timer
  timer = setInterval( function(){ check_for_image_existence(); }, 100);
  
  /// The function which fires AJAX
  function check_for_image_existence()
  {
      
    // Ajax Call
    jQuery.ajax({
url  : "imageupload.php",
data : "check_file_existence=1&filename=" + filename_final,
dataType : "json",
type : "post",
cache : 'false',
success : function(data)
{
      // Success
      if(data.success == 1 )
      {
       // Change the "SRC" property
       jQuery("#image1").attr("src", filename_final );
       jQuery("#image1").show(); 
 
       // Clear the Interval
       clearInterval( timer );
      }
}   // success callBack ends  
     }); // jQuery.ajax ends
  } // Function ends 

The ajax call sends request to a PHP file "imageupload.php" with parameters "check_file_existence" and "filename". When the return response holds a value of "1" in "data.success" variable, we know that the file is available in Server and changing "src" can not just fail. We also remember to clear the timer.

Check the new Server side PHP code which can be appended to the existing code in "imageupload.php".

<?php
if( isset($_REQUEST['check_file_existence']) && $_REQUEST['check_file_existence'] == 1 )
{
  // Extract File name from Ajax request
  $filename = $_REQUEST['filename'];
  
  // Check if file exists
  if($filename && file_exists($filename))
    echo json_encode(array("success" => 1));
  else
    echo json_encode(array("success" => 0));  
}
?>

We receive the ajax response in json format.

The above fixes make the whole solution work perfectly now. It runs on all browsers also. Give it a try.