Friday, November 15, 2013

Handling XML in PHP - II

If we want to generate XML in an organized way, we better use DOM extensions that comes with PHP. This creates DOMDocument object and the corresponding methods follow a pattern of adding, removing or manipulating elements and text nodes or adding attributes to DOM elements. The methods are quite similar to JavaScript DOM handling functions. 

Our first example loads an XML document using PHP DOM extension, then renders it on browser.

<?php
// Create new object
$xml_doc = new DOMDocument();
// Use load() method to load a document
$xml_doc->load('test.xml');
//Render it on screen
echo $xml_doc->saveXML();
?>

The content of test.xml is shown as output but only the text nodes. Tags are not shown. We need to view the source to get the actual XML structure. To render the whole XML structure on browser (without viewing the source), we need to include the header call : header('Content-type:text/xml'); before calling the saveXML() method.

Our next example, creates an XML document on the fly. Let's check it out.

<?php
header('Content-type:text/xml');

// Create new DOM object
$xml_doc = new DOMDocument();

// Add the root element 'students'
$students = $xml_doc->createElement('students');

// Append the new node to the doc
$xml_doc->appendChild($students);

// Now create First student Node
$student1 = $xml_doc->createElement('student');

// Create some attributes for student node
$attr1 = $xml_doc->createAttribute('id');
$attr1->value = "1";
$attr2 = $xml_doc->createAttribute('section');
$attr2->value = "A";
$attr3 = $xml_doc->createAttribute('username');
$attr3->value = "john_smith";

// Append the new attributes to the created element 'student'
$student1->appendChild($attr1);
$student1->appendChild($attr2);
$student1->appendChild($attr3);

// Now Insert a comment Node
$comm1 = $xml_doc->createComment('Teacher : Niel Hertz');
$student1->appendChild($comm1);

// Now Insert other child elements under "student" node
$fname = $xml_doc->createElement('fname');
$textNode = $xml_doc->createTextNode('John');
$fname->appendChild($textNode);

$lname = $xml_doc->createElement('lname');
$textNode = $xml_doc->createTextNode('Smith');
$lname->appendChild($textNode);

$roll_no = $xml_doc->createElement('roll_no');
$textNode = $xml_doc->createTextNode('109');
$roll_no->appendChild($textNode);

// Append all child nodes to 'student' node
$student1->appendChild($fname);
$student1->appendChild($lname);
$student1->appendChild($roll_no);

// Finally Append the 'student' node to 'students'
$students->appendChild($student1);

// Render it on screen
echo $xml_doc->saveXML();
?>

The above code generates and renders the following XML on the browser : 

<students>
 <student id="1" section="A" username="john_smith">
   <!--Teacher : Niel Hertz-->
   <fname>John109</fname>
   <lname>Smith</lname>
   <roll_no>109</roll_no>
 </student>
</students>

In the above code, we are using a DOMDocument object first, then using functions like createElement(), createAttribute(), createComment(), createTextNode() etc to create a DOM structure under it. All the created elements, attributes, comments, text nodes do not get into picture until we give calls to appendChild(). This function adds the element [or attribute etc] to its parent.

Next, we would parse an XML structure using the DOMDocument objects. Let's check out a program which loads an XML file and prints every elements and its contents on the screen.

<?php

// Create new DOM object
$xml_doc = new DOMDocument();

// Load external XML file

//$xml_doc->load('test.xml');  /// This is also OK
$xml_doc->loadXML(file_get_contents('test.xml'));

/// Get all the student element
$students = $xml_doc->getElementsByTagName('student');

//// Iterate thru each student node
foreach( $students as $stu )
{
  // Get Attributes of an element
  $id = $stu->getAttribute('id');
  $section = $stu->getAttribute('section');
  $username = $stu->getAttribute('username');
  
  // GET Child Elements/nodes and their values
  // A NodeList is returned by getElementsByTagName
  $nodes = $stu->getElementsByTagName('fname');
  $fname= $nodes->item(0)->nodeValue;

  $nodes = $stu->getElementsByTagName('lname');
  $lname= $nodes->item(0)->nodeValue;
  
  $nodes = $stu->getElementsByTagName('roll_no');
  $roll_no= $nodes->item(0)->nodeValue;
  
  $nodes = $stu->getElementsByTagName('class');
  $class= $nodes->item(0)->nodeValue;

  $nodes = $stu->getElementsByTagName('nickname');
  $nickname= $nodes->item(0)->nodeValue;


  /// Print on screen
  echo "<b>Student Details ::: </b><br>";
  echo "Name : $fname $lname [NickName : $nickname]<br>";
  echo "ID : $id, Section : $section, Class : $class<br>";
  echo "Username : $username<br><br>";
  
}

?>

The above program loads content of an XML file, then parses it and prints the student details. The functions like getElementsByTagName(), getElementById() locates elements within the XML document. The function getElementsByTagName() returns DOMNodeList class object holding all the elements found. Then we can simply use the item() method to access each element. Attributes of elements can be retrieved using getAttribute() method.

Check out the first part - Handling XML in PHP - I

Wednesday, November 13, 2013

How to check whether a remote file exists in PHP

Sometimes, we may need to execute remote php files and work on the resultant output. For example, very recently I have worked on a project in which file-checking PHP scripts on various remote servers are executed at regular intervals by a cron set on a single server at my disposal [I can control this server]. Those file-checking PHP scripts check if any file has been modified recently on its local server. This facility helps in tracking if any file has been modified by recent hacking attack.

In this situation, I needed to use CURL methods to execute the remote scripts, then get and parse the output. But I got error messages when the remote file-checking scripts were removed/deleted/renamed by hacking or any other ways. So, before executing remote scripts, we needed to check if the script existed at all.

There are many ways we can determine whether a remote script exists or not.

1. The first method is using functions like fopen()/file_get_contents()/file_exists() method. Check the code below..

<?php

/// We are trying to open the remote file in READ-ONLY mode
/// Using fopen() function 
$fp = fopen("http://example.org/file_check.php","r") or die("Could not open remote file");

/// Using file_get_contents() function
$str = file_get_contents("http://example.org/file_check.php") or die("Could not open remote file");

/// Using file_exists() function
echo  file_exists("http://example.org/file_check.php") or die("ERROR");

?>

2. The second method is using the function get_headers(). This function returns all the header sent by the server. Check the code below.

<?php

// GET the URL status
$status = get_headers( 'http://example.org/file_check.php' , 1) ;

// Check the status CODE
if( $status[0] == 'HTTP/1.0 404 Not Found' )
{
  // ... 
}

?>

The get_headers() function returns the headers in an array format and that array's zeroth position always hold the response code. 

All the above methods would require the allow_url_fopen settings in php.ini file to be set to 'On'. Otherwise they would fail.

3. The third method is to use CURL library. Check the code below.

<?php

// Init CURL
$ch = curl_init();

// set URL and other appropriate options
$curl_url = "http://example.org/file_check.php";
curl_setopt($ch, CURLOPT_URL, $curl_url);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE );

// grab URL and pass it to the browser
$str = curl_exec($ch);

/// Check if the file_check.php exists
$http_status = curl_getinfo($ch, CURLINFO_HTTP_CODE);

/// HTTP Status 200 means file exists
if( $http_status == "200" )
{
   //// The result/Output is already stored in $str
   //// We will parse the value of $str
}

?>

The above program is quite self-explanatory.  The curl_getinfo() function returns the response code which is 200 in case the page exists. The CURL method does not care about the value set to allow_url_fopen directive, hence very convenient and powerful tool to use.

Wednesday, August 21, 2013

Basic Authentication with .htpasswd in Xampp (Windows)

The basic http authentication created with .htaccess and .htpasswd files works pretty well on online real server. However implementing them locally on our Xampp server (on Windows) may be a little problematic. Here I am going to discuss the steps required to achieve this.

First of all, we need 2 files need to be created, .htaccess and .htpasswd. All the user passwords are stored inside the .htpasswd file. Windows does not allow to create such nameless files, however we can create them with Command Prompt. Check the picture below.



We have used the DOS command "copy con <filename>". Pressing CTRL + Z would end the process and creates a new file with the name provided. This way, we created the required .htaccess file. Our working folder is "htpassword". We created the .htaccess inside that only.

Next we would create the .htpasswd file with the help of command 'htpasswd', the corresponding windows executable file 'htpasswd.exe' resides in xampp\apache\bin folder. So, check the next screenshot to understand how we create the password file required for authentication.



We gave the command "htpasswd -c -m -b .htpasswd admin admin_pass". "admin" is going to be the username and "admin_pass" would be our password.

-c : create a new file
-m : MD5 encryption is enforced
-b : Use the password given at the command prompt

So, our .htpasswd file is successfully created. Our working folder "htpassword" also has an index.php file which contains a one-liner welcome message.

Now, open the .htaccess file, we need to write the following lines inside it.

AuthName "My Protected Area"
AuthType Basic
AuthUserFile c:\xampp\htdocs\htpassword\.htpasswd
require valid-user


Check the third line where the path of the password file is clearly mentioned. Now when we try to go to http://localhost/htpassword, a prompt appears asking for user id and password ( this is shown in screenshot below ). If valid id and password given, then we are redirected to index.php or other file set to be the default file.
 


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