Friday, July 26, 2013

Handling XML in PHP - I

XML documents are much stricter than HTML. XML is case sensitive whereas HTML tags can be in any case. Multiple spaces are ignored in HTML whereas XML preserves whitespaces. In HTML, tags may not be closed, fo example <br> tag does not need to have a closing </br> tag. But in XML, tags must be properly closed. Attribute quoting is essential in XML; which means the value for an element attribute must be wrapped in a quote. But this is not mandatory in HTML. Lastly, In XML, all data inside tag must be escaped. Ampersand (&) must be escaped to &amp; We need htmlspecialchars() or htmlentities() functions to do this for us.

There are various XML handling extension in PHP for reading, manipulating XML document. Here we would check out SimpleXML extension which is quite capable of handling general XML document.

XML document must start with an <xml> tag with a version number as shown below.

<?xml version="1.0"?>

To generate an XML document in PHP requires correct Content-Type header for the document. A simple example to generate XML document is given below.

<?php
header('Content-type:text/xml');
echo "<?xml version='1.0'?>";
echo <<<XML
 <student>
  <fname>John</fname>
  <lname>Smith</lname>
  <roll_no>109</roll_no>
  <class>VII</class>
 </student>
XML;
?>


The XML declaration statement <?xml version='1.0'?> may cause a problem. It purely matches with any PHP code block with short_open_tag directive set to on. In that case PHP processing might start thinking that it is a PHP statement. To prevent this, this XML declaration is printed with echo or print.

For parsing XML document, we would use simplexml_load_file() function. Check the example below. Here we assume that an xml file test.xml has the following content.

<?xml version='1.0'?>
<students>
 <student id='1' section='A'>
  <!-- Teacher : Niel Hertz -->
  <fname>John</fname>
  <lname>Smith</lname>
  <roll_no>109</roll_no>
  <class>VII</class>
 </student>
 <student id='2' section='B'>
  <!-- Teacher : Jim Cartel -->
  <fname>Jeff</fname>
  <lname>Smith</lname>
  <roll_no>110</roll_no>
  <class>VIII</class>
 </student>
</students>


Notice that XML elements can have attributes as HTML elements have. But in XML, the attribute values need to be quoted. The XML document can have comments (Similar to HTML comments).

Now our PHP code loads this file and parses it.

<?php
// Load the XML file in an Object
$all_students = simplexml_load_file('test.xml');

// This prints an array-like structure
// which is very easier to browse
// print_r( $all_students );



// Browse each node
foreach( $all_students->student as $st )
{
  echo "<br>ID :: {$st['id']}, Student Name : {$st->fname} {$st->lname}, ";
  echo "Roll : {$st->roll_no}, Class : {$st->class}";
}
?>


Output ::
ID :: 1, Student Name : John Smith, Roll : 109, Class : VII
ID :: 2, Student Name : Jeff Smith, Roll : 110, Class : VIII


The simplexml_load_file() function reads the XML document, puts the root element 'students' in an SimpleXMLElement object variable $all_students. The variable '$all_students' now have all the <student> nodes in array format where each student detail is again stored in SimpleXMLElement object. So, in the foreach loop, the $st variable points to each student node (now converted to an object) and all the child nodes become properties of that object. Hence $st->fname prints the <fname> node content appearing under the <student> node. So, basically SimpleXML converts all XML elements into object properties which
then becomes easier to handle.

Also notice, how the 'id' attribute of each <student> element is accessed through the array construct $st['id']. All node attributes are stored in an array. However comments are not captured by SimpleXML extension.

SimpleXML extension comes with another function called simplexml_load_string() which loads the XML from a string. Check the example below.

<?php
$str = <<<XML
<?xml version='1.0'?>
<students>
 <student id='1' section='A'>
  <!-- Teacher : Niel Hertz -->
  <fname>John</fname>
  <lname>Smith</lname>
  <roll_no>109</roll_no>
  <class>VII</class>
 </student>
 <student id='2' section='B'>
  <!-- Teacher : Jim Cartel -->
  <fname>Jeff</fname>
  <lname>Smith</lname>
  <roll_no>110</roll_no>
  <class>VIII</class>
 </student>
</students>
XML;

// Now load the XML and turn it into an object
$all_students = simplexml_load_string($str);
?>


The same effect can be achieved by using SimpleXMLIterator class object. This is shown below ..

<?php
// $str variable is defined above
// Load the XML and turn it into an Iterator object

$all_students = new SimpleXmlIterator($str);

// Rewind is necessary to move the pointer
// to the first element

$all_students->rewind();

// Now start Looping; The key() function
// returns key in each iteration

while( $all_students->key() )
{
  // Get the current item
  $st = $all_students->current();
 
  // Print
  echo "<br>ID :: {$st['id']}, Student Name : {$st->fname} {$st->lname}, Roll : {$st->roll_no}, Class : {$st->class}";
 
  // Move the pointer to next item
  $all_students->next();
}
?>


The code above is quite self-explanatory. The functions rewind(), next() are used to move the iterator pointer to at the beginning and next item respectively. The  current() function points to current item.

Below, we are using objects of another class called SimpleXMLElement to convert an XML string to an object.

<?php
// Convert to SimpleXMLElement object
$all_students = new SimpleXMLElement($str);

// Show count of total immediate children under root
echo $all_students->count() . "<br>";

// Browse thru children for Printing
foreach($all_students->children() as $st)
{
 echo "<br>ID:{$st['id']}, ". $st->getName()." Name:{$st->fname} {$st->lname}, ";
 echo "Roll:{$st->roll_no}, Class:{$st->class}, Section:{$st['section']}";
}
?>


The count() method of SimpleXMLElement object counts the children of an element. At the beginning, the variable $all_students holds all the XML document including the root element. Hence calling count() function just reports "2" (i.e 2 <student> tags under the root element <students>). The children() method of the SimpleXMLElement class object finds children of given node. In our cases, 2 <student> nodes are listed as children of the root element <students>. So, a foreach() loop prints the details of each child node or <student> tag.

Let's add attributes and child nodes to the above XML structure.

<?php
// Now load the XML and turn it into an object
$all_students = new SimpleXMLElement($str);

// Get All the children
$child = $all_students->children();

// Browse thru children for adding
// Attributes and Child elements

for($i=0; $i<count($child); $i++ )
{
  // Get each child
  $st = $child[$i];
 
  // Create a username text
  $username = strtolower($st->fname . "_" . $st->lname);
  $username = str_replace(" ", "", $username );
 
  // Add a new Attibute called 'username' to each <student> node
  $st->addAttribute('username', $username );
 
  // Add a child Node called nickname inside each <student> node
  $st->addChild('nickname', $username );
}
// Now print the XML on browser
// SET the header

header('Content-Type:text/xml');
// Print XML
echo $all_students->asXML();
?>


The above code is quite self-explanatory. We are just looping through each child element occurs under the root element 'students'. We can add any attribute to any tag or node using addAttribute() method. This method takes attribute name and value as first and second parameters respectively. The addChild() method adds child under any node and takes new node's name and content as first & second parameters respectively. Finally, the asXML() method prints the new XML, but for the browser to print the XML in correct format, we must setup the Content-Type in the header() call. The output is shown below.

<students>
 <student id="1" section="A" username="john_smith">
  <!-- Teacher : Niel Hertz -->
  <fname>John</fname>
  <lname>Smith</lname>
  <roll_no>109</roll_no>
  <class>VII</class>
  <nickname>john_smith</nickname>
 </student>
 <student id="2" section="B" username="jeff_smith">
  <!-- Teacher : Jim Cartel -->
  <fname>Jeff</fname>
  <lname>Smith</lname>
  <roll_no>110</roll_no>
  <class>VIII</class>
  <nickname>jeff_smith</nickname>
 </student>
</students>


Notice the new attributes and child elements '<nickname>' have been added (marked in orange) in the XML structure.

XPath is used to navigate through elements and attributes in an XML document. XPath support is available in SimpleXML. Which means we can run XPath query on any XML data. The xpath() method of SimpleXML extension searches for any SimpleXML node matching the path provided as XPath. Check one example below.

<?php
// Load the XML and turn it into an object
$all_students = new SimpleXMLElement($str);

// Get all first Names only
$first_names = $all_students->xpath('/students/student/fname');
foreach ($first_names as $fname)
{
  echo " $fname";
}
?>


The above code displays content of all <fname> tags appearing in XPath 'students/student'. The method xpath() takes the path of the node as argument and returns all the nodes that appear in that path specified. If error, then FALSE is returned by xpath() method, otherwise an array of SimpleXML nodes are returned. If no matching node is found, then an empty array is returned.


Check out the 2nd part of this article - Handling XML in PHP - II

To see Complex XML parsing using SimpleXMLElement, check article Parsing Complex XML with SimpleXML in PHP

Tuesday, July 23, 2013

DirectoryIterator in PHP

The DirectoryIterator is an Iterator class which comes with PHP5 and it provides another way for viewing contents of any directory. It follows the object-oriented approach. Let's check out one example..

<?php
// Create DirectoryIterator Object with path as parameter
$folder = new DirectoryIterator("joomla2.5.9");

// Browse Folder
foreach( $folder as $f )
 echo ( $f->getFilename() . "<br>");
?>


The above code displays all sub-folders (including '.' and '..') and files under the path provided as argument while calling the DirectoryIterator constructor. We can identify if an entry is a file or folder using various methods of the class. Check the example below.

<?php
// Create DirectoryIterator Object with path as parameter
$folder = new DirectoryIterator("joomla2.5.9");

// Browse Folder
foreach( $folder as $f )
{
   // If special '.' or '..' folder, ignore
   if( !$f->isDot() )
   {
     // Directory
     if( $f->isDir() )
     {
       echo ( $f->getFilename() . " [Directory] <br>");
     }
     else // File
     {
       // Permission returns an integer, convert it to Octal
       $octal_perms = substr(sprintf('%o', $f->getPerms()), -4);
       // Print File Details
       echo "FileName : {$f->getBasename()}, Extension : [{$f->getExtension()}], ";
       echo "FileSize : {$f->getSize()} Bytes, Permissions : [$octal_perms] ";
       echo "Last Modified : ".date('Y-m-d',$f->getMTime())."<br>";
     }
   }
}
?>


The above code is pretty self-explaining. Now, let is recursively use the above method to fetch all the sub-folder contents.

<?php
// Create DirectoryIterator Object with path as parameter
$folder = "joomla2.5.9";

// Call the function
get_folder_contents( $folder );

/// FUnction Definition
function get_folder_contents( $folder )
{
  // Create Iterator Object
  $folder_name = new DirectoryIterator($folder);

  // Browse Folder
  foreach( $folder_name as $f )
  {
       // If special '.' or '..' folder, ignore
       if( !$f->isDot() )
       {
         // Directory
         if( $f->isDir() )
         {
           // Print Directory Name
           echo (" ---- Showing Contents of $folder/{$f->getFilename()} ---- <br>");
          
           // RECURSIVE CALL
           get_folder_contents( $folder . "/" . $f->getFilename() );
         }
         else // File
         {
           // Permission returns an integer, convert it to Octal
           $octal_perms = substr(sprintf('%o', $f->getPerms()), -4);
           // Print File Details
           echo "FileName : {$f->getBasename()}, Extension : [{$f->getExtension()}], <br>";
         }
       }
  }
}
?>


The DirectoryIterator class also provides some methods like current(), next(), rewind() which moves Iterator cursor. Check one example below..

<?php
// Get the Current directory
$folder = new DirectoryIterator( dirname(__FILE__) );

// Check if the pointer position is a valid entry
// We are browsing for FILES first
while($folder->valid())
{
    // GET the Current file/folder
    // iteator pointer is pointing to.
    $file = $folder->current();
   
    // Check if it is a FILE
    if( !$file->isDir() )
    {
       // Echo Key :: FileName
       echo $folder->key() . " :: " . $file->getFilename() . "<br>";
    }
   
    // Move the pointer pointing to next item
    $folder->next();
}

// Let's search for Directories
// Hence move the pointer to the beginning
$folder->rewind();

// Again start the loop
while($folder->valid() )
{
  // GET the Current file/folder
  $dir = $folder->current();
 
  // Check if it is a Directory
  if( $dir->isDir() )
      echo $folder->key() . " :: " . $dir->getFilename() . "<br>";

  // Move the pointer to the next item
  $folder->next();   
}
?>


We have used rewind() function to move the iterator pointer pointing to the beginning of the file/folder listing. The next() function moves the pointer to the next item in the list. The key() function returns a key value (integer) which I think the sorted index for each item.

Check previous articles on file/folder browsing in PHP : File and folder Iterators in PHP, Using Glob in PHP

Using Glob in PHP

In the previous article "Files and Folder Iterator in PHP", we have discussed how we can iterate through files and folders with functions like dir(), opendir(), readdir() etc. In this section, we would see how glob() function can be used to find and iterate through files and folders under current directory. Check the code snippet below :

<?php
// Change to Folder
chdir('joomla2.5.9');

// Iterate thru all files pattern *.*
foreach( glob("*.*") as $filename )
{
  echo "<br> File Name : $filename, Size ::". filesize($filename);
}
?>


The above code lists all the files matching the pattern "*.*" in the subfolder 'joomla2.5.9'. This pattern can be anything like "*.txt", "*.php", "conf*.*" etc. We can use the path information in the pattern as shown in the code below..

<?php
$patt = "text_files/*a.*";

foreach( glob($patt) as $filename )
{
  echo "<br> File Name : $filename, Size ::". filesize($filename);
}
?>


The above code lists all files whose name ends with 'a' in the folder named "text_files". We we want to list all files and folders then we can use the following pattern :

<?php
foreach( glob("text_files/*") as $filename )
{
    echo "<br> Folder Name :: $filename";
}
?>


If we need to show only list of folders then we can use a flag with glob as shown below :

<?php
foreach( glob("text_files/*", GLOB_ONLYDIR | GLOB_NOSORT  ) as $filename )
{
    echo "<br> Folder Name :: $filename";
}
?>


The flag GLOB_ONLYDIR returns only folders in the path specified. The flag GLOB_NOSORT does not sort the result listing.

We can also use is_dir() to check if this is a folder or not, as shown in the code below :

<?php
foreach( glob("text_files/*") as $filename )
{
  if( is_dir($filename ) )
    echo "<br> Folder Name :: $filename";
}
?>


My current directory is c:\xampp\htdocs, say I would like to see all the folders whose names start with "p" and only 3 letters long in the parent folder c:\xampp. I need to change the code a bit.

<?php
foreach( glob("../p??", GLOB_ONLYDIR | GLOB_NOSORT ) as $filename )
{
    echo "<br> Folder Name :: $filename";
}
?>


The output is ::

Folder Name :: ../php

Next, we build a function which would scan the current directory and show us listing of all the files and sub-folders under it. Check the code below...

<?php
// Set a path
$dir = "text_files";
$space = "-";

// Call the function
get_listing( $dir, "" );

// Function definition
function get_listing($dir,$space)
{
  // Build Path Pattern
  $path = "$dir/*";
 
  /// Loop through each item
  foreach( glob( $path ) as $filename )
  {
    echo "{$space}{$filename}<br>";
  
    // If sub-folder, then call recursively
    if( is_dir($filename) )
    {  get_listing("$filename", $space . "-" );
     
    }
  }
}
?>


The code above is quite self-explanatory. We have created a function called 'get_listing'. Inside it, we find files and folders using a pattern stored in variable "$path". If any item found is a directory ( is_dir() returns TRUE for such entry ), we recursively call the same function to get inside the folder and scan its contents.

Using pattern like "*" does not return special files like .htaccess.

".*" - This pattern can track files like .htaccess. But this also includes special directory entries "." (current directory) and ".." (parent directory).

"n*" - This pattern would return all files and folders whose names start with "n". This is case-sensitive.

"*p" - This patten would return all files and folders whose names ends with "p". This would match folders like "jump", "pump" and file names like "a.php", "c_d.ctp" etc. This would also match only "p".

"*im*" - This would match those files/folders whose names have "im" inside it. However this would match names like "image", "sikkim" or even "im".

"{,.}*" - This pattern would search for * or .* which would include all files, folders, special directory entries like "." and ".." and special files like .htaccess. The curly braces can hold possible values separated by commas. Check another example of globbing pattern with curly braces below::

"sample.{png,gif,tiff,jpeg,jpg}" - This pattern would search for a file name 'sample' with any extension among the values given within curly braces. If the directory has files "sample.png", "sample.tiff", "sample.jpeg", all of them would be listed.

With square brackets, we can provide a range or various tokens to match. Check examples below. Square brackets can't be used to match more than 1 character.

"m[a,u,o]m" - This would match any of "mam", "mom" or "mum".
"[n,j,f]*"  - This would fetch all files/folders with names start with any of "n", "j" or "f".
"b[a-d]d"   - This would match "bad", "bbd", "bcd" and "bdd" because we have provided a range a to d.
"f[ee,oo]l.txt" - This won't match "feel.txt" and "fool.txt". We can not use multi-charactered token inside square brackets. "ee", "oo" are invalid whereas "a", "o" etc single characters are perfectly ok.

The question mark ("?") represents a single character. So, "ba?" would mean "ba" must be followed by a single character only. So, "ban", "bat", "bad", "bar", "bag" would be matched.

"?.*" - This pattern would fetch all files with names (excluding extension) consisting of single letter like "a.php", "j.mov" etc but not "ab.txt" or "pol.php".
"?" would fetch all files/folders with names (including extension) consisting of a single character.
"????.txt" would fetch those .txt files whose names (excluding extension) are composed of 4 characters.

Exclamation mark inside square brackets would imply "logical NOT" or exclusion. A pattern like "a[!x]e" would match "are" but not "axe".

"[!a].txt" - This pattern would match files like "b.txt", "c.txt" but not "a.txt".

In the next article DirectoryIterator in PHP, we discuss on DirectoryIterator Iterator class which provides another way to browse for files and folders.

Tuesday, July 09, 2013

Arrays in PHP

Let's check out how PHP arrays work. Let's try with a very basic example ::

<?php
$arr = array( 1,2,3,4,5 );
print_r( $arr );
?>


PHP Array Index starts with 0. However we can specify an Index for any item explicitly while defining the array.

<?php
$arr = array( 10,20,30, 0=> 100,40,50 );
print_r( $arr );
?>


The above program puts 10 at index 0, then 20 at index 1, puts 30 at index 2 and then overwrites index 0 with 100. 40 and 50 are placed at index 3 and 4 respectively.

<?php
$arr = array( -9 => 10, 20, 30, 40=> 100, 40, 50 );
print_r( $arr );
?>


The program above starts with putting 10 at index -9. Next it puts 20 at index 0 and increments the index value for next items where the index is not defined explicitly. This way, value 20 and 30 are positioned at index 0 and 1. Next we explicitly define an index 40 and put a value 100 to it. And then we have a value 40 for which we don't define any index explicitly. Hence PHP would take the index of previous item 100 (index is 40 which is also the highest), add 1 to it (41) and set the value to that index. This way, value 40 and 50 are set to index 41 and 42 respectively.

If we want an array to start at index 1 instead of 0, we can write it the following way ::

<?php
$arr = array( 1 => 'Gimmy', 'John', 'Joseph', 'Jeff');

/// Or we can go the below way
$arr = array();
$arr[1] = 'Kim';
$arr [] = 'Tim';  // created index 2
$arr [] = 'Sim';  // 3
$arr [6] = 'Jim'; // 6
$arr [] = 'Lim';  // 7
?>


Iterating through such numerically indexed arrays would be as shown below :

<?php
$arr = array( 1,2,3,4,5,6,7,8,9 );

// Method 1
for($i = 0; $i< count($arr ); $i ++ ) echo $arr[$i];

// Method 2
foreach( $arr as $value ) echo $value;

// Method 2.1, to show the index => value pair
foreach( $arr as $index=>$value ) echo " [$index]=>$value, ";

// Bring the Array pointer at the first position
reset($arr);

// Method 3
while( list($index,$val) = each($arr) ) echo " [$index] => $val, ";

// Another method using next(), prev() etc functions
// At this point, the Array pointer is pointing to
// the end of the array, move it to the 1st position
reset( $arr );

// Move 2 places forward
next( $arr);
next( $arr);

// Print
echo current( $arr ); // 3

// Go 1 place backward
prev( $arr );

// Print
echo current( $arr ); //2
?>


The count() function returns total number of elements in the array. The each()  function returns each item in the array when used inside the loop. The current() function current item which the array pointer is pointing to.

Let's create an array of Odd numbers using range() function.

<?php
// Create array from 1 till 52, step 2
// Index starts at 0
$arr = range(1,52,2) ;
// Print
print_r( $arr );
?>


If we do not specify the step parameters with range() function, default 1 is taken.

Another way we can create an array of Odd numbers is shown below :
 

<?php
$arr = array();
for($i=1; $i<=52; $i = $i+2)
 $arr[] = $i; // Push to Array
?>

Notice that counter is increased by 2 in every iteration by the expression $i = $i + 2.

PHP Associative arrays are similar to numerically indexed arrays except the fact "index" and "value" pair are used as "named keys" and "value" pair. This means we can use any string as "index" (or key) of that array.

<?php
$arr = array( 'sky' => 'blue', 'sea' => 'green', 'snow' => 'white', 'tree' => 'green' );

// Print each Key=>Value pair
foreach( $arr as $element => $color )
 echo "<br>$element is $color";

// If we try to print the same array one more time
// we DONT need to move the pointer at the beginning
// But between 2 each($arr) calls, we must call reset()
foreach( $arr as $element => $color )
 echo "<br>$element is $color"; // This is OK

?>


If we try to print an index which is out of range, we get a notice saying "Undefined Index" or "Undefined offset" errcr. Check the example below..

<?php
$arr = array('banana' => 'yellow', 'mango' => 'green', 'strawberry' => 'red');
echo "{$arr['pineapple']}" ; // Undefined Index 'pineapple'
echo $arr[0];       // Undefined Offset 0

$arr = range(1,10); // Index 0 to 9
echo $arr[23];      // Undefined Offset 23
?>


Let's modify an array using loop.

<?php
$arr = range(1,10);

// Loop though, increment each value by 1
for($i=0;$i<count($arr);$i++) 
  $arr[$i]++;

// Foreach works with a copy of the original
// Array, hence it can not modify the content
// However the each() function works with the original
// copy of the array. Hence as a result, all elements
// would be multiplied by 2
while(list($key,$value) = each($arr) )
 $arr[$key] = $value * 2;
?>


Let's try some other type of for and while loop construct to print the elements of various arrays.

<?php
$arr = range( 1, 10 );

// Indexed/Associative Array - While Construct - I
$i = 0;
while( $i++ < count($arr) )
{
  // print
  echo "<br>" . key( $arr ) . " : " . current( $arr );
  // Move to Next Item
  next( $arr );
}

// Indexed Array - While Construct - II
while( 1 )
{
  echo "<br>" . key($arr) . " : " . current( $arr );
  next( $arr );
  if( key($arr) === null ) break;
}

/// Associative Arrays
$arr = array('banana' => 'yellow', 'mango' => 'green', 'strawberry' => 'red');

/// Associative Array - For Construct - I
for( reset($arr); key($arr); next($arr) )
{
  /// print banana : yellow etc
  echo "<br>" . key( $arr ) . " : " . current( $arr );
}

?>

Let's try some functions like isset(), empty() on PHP arrays.

<?php
$arr = "";
$arr2 = array();


// Prints nothing as False
echo is_array( $arr );

// Prints 1 as True
echo empty( $arr2 );

// Prints 1 as True
echo isset($arr2);
?>


The $_GET or $_REQUEST or $_POST variables are associative arrays of variables passed to the current script.

We can test if any variable is set in those arrays. This is shown below ..


<?php
 

if( isset( $_GET['fname'] )  )
{
  //
This part gets executed if URL is like
  // test.php?fname=
}

if( isset( $_GET['fname'] )  && trim( $_GET['fname'] ) != '' )
{
  //
This part gets executed if URL is like
  // test.php?fname=john
}

?>


If 'fname' word is present in the URL, then both the if conditions become true. However, if a valid value for fname is submitted, then only the second If condition satisfies.

To delete any item from an array, we use unset() function as shown in the program below.


<?php
$arr = range(10,20);
unset( $arr[4] ); //
4th Index is deleted
unset( $arr[5] ); // 5th Index is removed
unset( $arr[6] ); // 6th Index is removed

$arr = array( 'sky' => 'blue', 'sea' => 'green', 'snow' => 'white', 'tree' => 'green' );
unset( $arr['sky'], $arr['sea'] ); //
Item with key 'sky', 'sea' removed
?>


The above program removes 3 items from index 4,5 and 6. However, old index => value combination is maintained. That means, after we delete the 4th index from the array, the element at index 5 is not re-positioned at index 4. This behaviour occurs in JavaScript when we try to remove selected items from a selectbox which was discussed in article "HTML Select element manipulation using Javascript - II".

After deleting indexes from an array, holes are actually created and hence the array can not be looped through using for() normal loop construct. We need to use foreach() construct in such cases. 


Another important thing to remember, when we use unset() to remove array items, the index stays at where it was before. It does not decrease automatically. Check the example below : 

<?php
// Create the array
$arr[0] = 0; $arr[1] = 1;

// Unset the elements
unset($arr[0]); unset($arr[1]);

// Add new elements at index 2
$arr[] = 2;
?>

The last statement would insert the value '2' at the index '2'. It does not decrease when we unset elements from the array.

Check more on Arrays in my next article Arrays in PHP - 2

Friday, June 28, 2013

ReflectionClass in PHP

To know internals of any classes or interfaces, we can use ReflectionClass which is offered by PHP5. With this, we can retrieve methods, properties etc from a class. Let's checkOut a simple example ::

<?php
// User defined class a [Line No. 2]
class a
{
  const am = 10;   // Constant
  protected $aa;   // Protected Member
  public $aaa;     // Public Member
  function disp()  {  } // Public method
}

// Inheritance [Line No. 11]
class b extends a
{
  static $vendor = 1; // Static Property
  public $bbb;        // Public property
  const am = 20;      // Constant
 
  public static function get_vendor()  { } // Static Method
  function disp(){  }    // Public Method                 
}

// Print
ReflectionClass::export( 'b' );
?>


Output ::

Class [ <user> class b extends a ] {
  @@ C:\xampp\htdocs\test.php 12-20

  - Constants [1] {
    Constant [ integer am ] { 20 }
  }

  - Static properties [1] {
    Property [ public static $vendor ]
  }

  - Static methods [1] {
    Method [ <user> static public method get_vendor ] {
      @@ C:\xampp\htdocs\test.php 18 - 18
    }
  }

  - Properties [3] {
    Property [ <default> public $bbb ]
    Property [ <default> protected $aa ]
    Property [ <default> public $aaa ]
  }

  - Methods [1] {
    Method [ <user, overwrites a, prototype a> public method disp ] {
      @@ C:\xampp\htdocs\test.php 19 - 19
    }
  }
}


Discussion ::
1. The class whose internals we want to must be passed to ReflectionClass' static method export(). For example if we want to know about a class named 'employee', we would write as this ::
  ReflectionClass::export( 'employee' );
 

2. Notice the output, Constants, Static Properties, Static Methods, Methods and Properties are shown in list. Check that in the properties section, inherited properties (3) are also shown. In the method section, notice it is denoted that class b overwrites a's disp() with its own copy of disp(). ReflectionClass' export static method returns minute details about a class like number of methods and properties, method's parameters, visibility parameters (whether private, protected or public). It also tells us about the line numbers within the source code where a particular method or property was defined.

Let's try some other method ReflectionClass offers. Check another example below ::

<?php
// Class Definition as shown above ...

// Create new ReflectionClass Object
$c = new ReflectionClass('b');

// Use ReflectionClass Methods

// Get value of a Constant 'am'
echo $c->getConstant('am');

// getConstants() returns class
// constants as an array
print_r( $c->getConstants() );

// Get Constructor Name if defined
print_r($c->getConstructor());

// Get all properties including inherited ones
print_r( $c->getDefaultProperties());

// Get the filename, in which the class is defined
echo $c->getFileName();

// GET ALL Interface Names implemented in the CLASS
// getInterfaces() returns all interfaces in
// ReflectionClass objects
print_r($c->getInterfaceNames());

// Lists all local and inherited methods
print_r($c->getMethods());

// Get the class name including NameSpace
echo $c->getName();

// Get the name of Parent Class
print_r($c->getParentClass());

// GET all properties (including inherited)
// We use various Filters with this function
// IS_PROTECTED to get Protected properties
// IS_PUBLIC to get Public properties
// IS_STATIC to get static properties
// IS_PRIVATE to get static properties
// Parent's STATIC properties are also shown
print_r($c->getProperties( ReflectionProperty::IS_STATIC  ));

// Get a Particular property
echo $c->getProperty('vendor');

// List all static Properties
print_r( $c->getStaticProperties());

// GEt value of any static property
var_dump( $c->getStaticPropertyValue('vendor'));

// Check if a specific Constant exists
echo $c->hasConstant('am');

// Check if a specific Property exists
echo $c->hasProperty('aa');

// Check if a specific Method exists
echo $c->hasMethod('disp');

// Check if a Class is Abstract
var_dump( $c->isAbstract());

// Checks if a class is clonable
// A Class is NOT clonable if its magic
// method __clone() is set to private
var_dump($c->isCloneable());

// Checks if a class is FInal
var_dump($c->isFinal());

// Checks if any object is an instance
// of the current class
var_dump( $c->isInstance(new b) );

// The above statement is similar to
echo new b instanceof b;

// Check if a class is instantiable
// Interfaces, Abstract Classes and classes
// with private constructor can not be
// instantiated
var_dump( $c->IsInstantiable() );
?>


Here I repeat some important points mentioned in comments in the code above ::

i. Interfaces, Abstract classes and any class with its constrructor ( __construct() ) set to private are not instantiable.
ii. If any class' magic method __clone() is set to private, the object of such class can not be cloned any more.
iii. Parent class' Static properties can be accessed from children classes.
iv. Parent class' Constant can be accessed from children classes.


ReflectionClass is used to make documentation for a piece of code.

Count() function in PHP

The purpose of this function is to return the total count of elements in an array as shown in the example below.

<?php
 $a = array(12,13,14);
 echo count($a);  // 3
?>

 

It does not matter if the array is associative or its indices are not ordered. Check another example ::

<?php
 $a = array(12,13,14);
 $a[24] = 12;
 $a['my_name'] = 'Chandan';
 $a[134] = 102;
 echo count($a);  // 6
?>


Let's take an Multi-dimensional array and apply count().

<?php
 $a = array(
      "names" => array("John", "Martin", "Teddy"),
      "rolls" => array(10, 23, 3)
      );
 echo count($a);  // 2
?>

 

The above program prints 2 as count of primary indices "names" and "rolls". Let's modify the count() function call to get the correct element count in the array.

<?php
 $a = array(
      "names" => array("John", "Martin", "Teddy"),
      "rolls" => array(10, 23, 3)
      );
 echo count($a, COUNT_RECURSIVE);  // 8
?>


The second parameter to count() function forces it to get through each index recursively and search for sub-elements. Each of the 8 elements in the above array can be accesses as shown below :

$a['names']     // Value :: Array
$a['rolls']     // Value :: Array
$a['names'][0]  // Value :: John
$a['names'][1]  // Value :: Martin
$a['names'][2]  // Value :: Teddy
$a['rolls'][0]  // Value :: 10
$a['rolls'][1]  // Value :: 23
$a['rolls'][2]  // Value :: 3


The constant COUNT_RECURSIVE can be replaced with "1". Let's use the count() function with simple/basic type variables.

<?php
 $a = NULL;
 $b = '';
 $c = 'Smith';
 $d = 100;
 echo count($a);  // 0
 echo count($b);  // 1
 echo count($c);  // 1
 echo count($d);  // 1
?>


For any uninitialized simple/basic type variable, count returns zero. Let's use count() with objects.

<?php
class Foo
{
    public $var1 = 100;
    public $var2 = 200;
    public function display()
    {
      echo "$var1, $var2";
    }
}


$foo  = new Foo();
echo count($foo, COUNT_RECURSIVE); // 1
?>


Same behaviour noticed when we use objects instead of basic types. However we can convert an object to an array and then use count() function to know number of properties it has. Check the example below ::

<?php
$b = new stdClass;
$b->name = "John";
$b->roll_no = 100;
$b->age = 35;
$b->grade= 'b';
echo count((array)$b); // 4
?>


Let's use Countable interface which is defined in SPL (Standard PHP Library). By using this interface, we can manipulate the value count($object) returns. Check out the example below ::

<?php
class foo implements Countable
{
  private static $count = 0;
 
  public function __construct()
  {
     // Increment the count
     self::$count++;
  }
  // Our own implementation of count()
  public function count()
  {
    return self::$count;
  }
 
}
$b = new foo(); // count = 1
$c = new foo(); // count = 2

echo count($c); // 2
echo count($b); // 2
?>


Notice that the constructor is incrementing the value of static variable $count every time we create a new object. Our implementation of count() function manipulates the way count() works with our objects and it returns the value of $count (Class Property).

Thursday, June 13, 2013

String manipulation in javascript thorugh RegExp II

Problem 5 :: Swap between 1st and 3rd word in every combination of 3 words in a given string. For example, string like "I am right" would result into "right am I" i.e 1st and 3rd words are swapped.
Solution ::

<script>
// String
var str = "We are the men in red. He is the best boy.";

// Define RegExp
var patt = /(\S+)\s+(\S+)\s+(\S+)/g;

// Apply RegExp
var res = str.replace( patt, "{$3} $2 {$1}" );

// Print in Console
console.log( res );
</script>


Output ::
{the} are {We} {red.} in {men} {the} is {He} best boy.

Explanation ::
i) Regular expression (\S+)\s+(\S+)\s+(\S+) tries to capture 3 adjacent words joind with 2 occurrences of single/multiple spaces.
ii) The replace() function replaces such matched 3 words with "{$3} $2 {$1}" or 1st and 3rd words swapped.

Problem 6 :: I have a string like "I have 10 dollars in my pocket". We need to convert all numeric value ("10") to currency formatted text like "$10.00".
Solution ::

<script>
// String
var str = "0+ 1 + 10 + 10.00 + $5.00 = $26.00, right?";

// Define RegExp
var patt = /(\$*\d+\.*\d*)/g;

// Apply RegExp
var res = str.replace( patt, function($1)

{ var p = $1.replace(".00","").replace("$","");  
  return "$" + p + ".00"; 

);

// Print in Console
console.log( res );
</script>


Output ::
$0.00+ $1.00 + $10.00 + $10.00 + $5.00 = $26.00, right?

Explanation ::
The capturing group (\$*\d+\.*\d*) implies the following :
i) $ may be present at the beginning
ii) Next, series of digits
iii) A dot (.) may appear next
iv) Again a series of digits might appear.

The RegExp above would capture text like "$12" or "12" or "$12.00". Next we have a function which replace all "$" and ".00" characters from the matched word, and finally produces the resultant string by adding "$" and ".00" at the beginning and the end respectively. The replacement is required in case where texts like "$10" or "$20.00" are found.

Problem 7 :: This problem has been featured on MDN page. Converting Celsius to Fahrenheit through RegExp. Check out the code below.
Solution ::

<script>
// String
var str = "Last 7 day's temperature : 14.5C 4C 0C -3C -5.6C";

// Define RegExp
var patt = /([-]*\d+\.*\d*[C|F])/g;

// Apply RegExp
var res = str.replace( patt, function($1)
{
  var v = $1; 
 
  // GET the numeric value
  var t = parseFloat( v.substr(0, v.length - 1 ) );

  if(v.substr(-1) == 'C')
  {
     // Convert to Farenheit
     var result = ( ( t * 1.8 ) + 32 ).toFixed(2) + "F";
  }

  if(v.substr(-1) == 'F')
  {
     // Convert to Celsius
     var result = ( ( t - 32 ) / 1.8 ).toFixed(2) + "C";
  }
  return $1 + "[" + result + "]";

} );

// Print in Console
console.log( res );
</script>


Output ::
 
Last 7 day's temperature : 14.5C[58.10F] 4C[39.20F] 0C[32.00F] -3C[26.60F] -5.6C[21.92F]

Explanation ::
This is a nice program to convert Celsius to Fahrenheit and vice versa within a string. The output shows that all the temperatures in Celsius are appended with same in Fahrenheit. For example "14.5C" has been appended with "[58.10F]". Let's dissect the RegExp "/([-]*\d+\.*\d*[C|F])/g"..

i) [-]* means the number may start with a negative sign
ii) \d+\.*\d* means any sequence of digits may be followed by a dot or a series of digits. This captures numbers like "1", "12", "1.23" or "13.2".
iii) [C|F] means the number may be followed by either 'C' or 'F'.

The function we used with replace() function is very simple. We use  v.substr(0, v.length - 1) statement to extract "14.5" from "14.5C". And then we put a if-else logic to convert from Celsius to Fahrenheit or vice-versa. We use toFixed() function to round a floating point number to 2 decimal places i.e rounding 58.100000000 off to 58.10. The replace() function is responsible for making the string "14.5C"  converted to "14.5C[58.10F]".


Problem 8 :: Let's find out all words which have comma after them.
Solution ::

<script>
// String
var str = "We didn't do it, even they didn't, who did it then?";

// Define RegExp
var patt = /(\S+)(?=,)/g;

// Apply RegExp
var res = str.replace( patt, function($1){ return  "[" + $1 + "]"; } );

// Print in Console
console.log( res );
</script>


Output ::
We didn't do [it], even they [didn't], who did it then? I [did],

Explanation :: We have actually wrapped all matched words with a square bracket around them. Let's check the RegExp /(\S+)(?=,)/g ...

i) 'g' modifier is used to denote a global search
ii) (\S+) means any series of non-whitespace characters
iii) (?=,) denotes a positive lookahead regexp which starts with ?=. This means 'followed by comma' which would not be included in resultant match.

However, the above RegExp has a problem, it can match "iran,iraq," as a whole from a string like "iran,iraq, and Nepal". To get rid of this, we would change the pattern like this ::

var patt = /(\S[^\s,]+)(?=,)/g;

The inclusion of "[^\s,]" would accept all words containing comma two separate words. The output would be "[iran]" and "[iraq]".

We could have written the regexp pattern without using the positive lookahead as this ::

var patt = /(\S[^,\s]+),/g ;

 
It captures any character excluding comma and whitespace, followed by a comma. Problem is all the matched words are returned with a comma at the end. For this reason, positive lookahead is only solution to this. 


See previous article for more info on JS RegExp.

String manipulation in javascript thorugh RegExp I

Problem 1 :: Find all 3 letter words in a given string
Solution ::

<script>
// String
var str = "We are the men in red. He is the best boy.";

// Define RegExp
var patt = /\b([\w]{3})\b/g;

// Apply RegExp
var res = str.match( patt );

// Print in COnsole
console.log( res );
</script>


Output ::
["are", "the", "men", "red", "the", "boy"]

Explanation ::
i. modifier 'g' is used for global search.
ii. \b is used to denote the word boundaries
iii. ([\w]{3}) means any alphanumeric character 3 times

Problem 2 :: Wrap all 3 letter words with a pair of braces within the string given in problem 1 above.
Solution :: We need to manipulate the replace() function a little to achieve this.

// Apply RegExp
var repl_ace = str.replace( patt, "{$1}" );

console.log( repl_ace );
 

Output ::
We {are} {the} {men} in {red}. He is {the} best {boy}.

Explanation ::
The 2nd argument in replace() function can take RegExp and $1 means every match found i.e if a 3 letter word "are" is found, $1 would refer to the match "are" itself. Hence, "{$1}" would generate {actual_word} for each 3 letter words found in the string.

Problem 3 :: Convert all 3 letter words to Upper Case within the string.
Solution :: We simply change the replace() function to achieve this. Check it out below.

// Apply RegExp
var repl_ace = str.replace( patt, function($1)

   { return $1.toUpperCase();  } ); 
// Print
console.log( repl_ace );

Output ::
We ARE THE MEN in RED. He is THE best BOY.

Explanation :: The second argument to replace() function should be a string, however we used a function which returns a string. That string takes the "$1" i.e the matched word and returns it in Upper case format which the replace() function uses.

Problem 4 :: Find every 2 adjacent words in a String and swap them. Means, it would match words like "We are", "the men" etc and swap them to "are We" and "men the".
 

Solution ::
<script>
// String
var str = "We are the men in red. He is the best boy.";

// Define RegExp
var patt = /(\S+)\s+(\S+)/g;

// Apply RegExp
var res = str.replace( patt, "$2 $1" );

// Print in Console
console.log( res );
</script>


Output ::
are We men the red. in is He best the boy.

Explanation ::
i) (\S+) means any series of non-whitespace i.e "We"
ii) \s+ means any series of whitespaces.
iii) (\S+) again means non-whitespace after the whitespaces i.e "are". So, we are trying to match adjacent words like "(We) (are)" or "(the) (men)". So, for every such match $1 would hold the 1st word, $2 holds the 2nd word i.e "We" & "are" for matched word pair "(We) (are)".
iv) The replace() function replaces with "$2 $1" resulting to "are We".

More such examples can be found in my next article.