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.