আগামী ১৫ ডিসেম্বর -২০১৭ তারিখ থেকে শুরু হচ্ছে পাঁচ মাস ব্যাপী Professional Web Design and Development with HTML, CSS PHP,MySQL,JavaScript, AJAX, JQUERY, Bootstrap and Live Project কোর্সের ৮৭ তম ব্যাচ এবং ২৬ ডিসেম্বর-২০১৭ তারিখ থেকে শুরু হচ্ছে চার মাস ব্যাপী Zend PHP-7 Certified PHP Engineering (Advance PHP) কোর্সের ৩৫ তম ব্যাচ। প্রত্যেকটি কোর্স এর ফী নির্ধারণ করা হয়েছে ৩০,০০০/= আগ্রহীদেরকে অতিসত্বর মাসুদ আলম স্যার এর সাথে যোগাযোগ করতে অনুরোধ করা যাচ্ছে। স্যার এর মোবাইল: 01722 81 75 91

Introduction to Standard PHP Library -SPL Part-2

Introduction to Standard PHP Library -SPL Part-2

After Completing Introduction to Standard PHP Library (SPL) Part-1 Today I’ll look at another spl iterating patterns: Simple XML Iterator and LimitIterator . Hopefully you’ll get a idea of what these are capable of and that you can get some new ideas for your day-to-day tasks

Simple XML Iterator

The SimpleXMLIterator is, as the name suggests, quite simple in its implementation. This iterator takes a well formedxml structure (document) and can be traversed as with any aggregate structure. The SimpleXMLIterator extends SimpleXMLElement and is a recursive iterator. Lets begin with looking at the available class methods to the SimpleXMLIterator.

File: get_simple_xml_iterator_methods.php

<?php
/*** list all class methods ***/
foreach( get_class_methods(SimpleXMLIterator) as $methodName)
{
echo $methodName.'<br />';
}
?>

The above code will produce a list of class methods like this:

valid
current
key
next
hasChildren
getChildren
count
__construct
asXML
saveXML
xpath
registerXPathNamespace
attributes
children
getNamespaces
getDocNamespaces
getName
addChild
addAttribute

To get an idea of how the SimpleXmlIterator is implemented a few scripts are provided, however, for the sake of sanity, only the first script will contain the XML tree for use in the other scripts.

File: simple_xml_iterator1.php

<?php

/*** a simple xml tree ***/
$xmlstring = <<<XML
<?xml version = "1.0" encoding="UTF-8" standalone="yes"?>
<document>
<animal>
<category id="26">
<species>Phascolarctidae</species>
<type>koala</type>
<name>Bruce</name>
</category>
</animal>
<animal>
<category id="27">
<species>macropod</species>
<type>kangaroo</type>
<name>Bruce</name>
</category>
</animal>
<animal>
<category id="28">
<species>diprotodon</species>
<type>wombat</type>
<name>Bruce</name>
</category>
</animal>
<animal>
<category id="31">
<species>macropod</species>
<type>wallaby</type>
<name>Bruce</name>
</category>
</animal>
<animal>
<category id="21">
<species>dromaius</species>
<type>emu</type>
<name>Bruce</name>
</category>
</animal>
<animal>
<category id="22">
<species>Apteryx</species>
<type>kiwi</type>
<name>Troy</name>
</category>
</animal>
<animal>
<category id="23">
<species>kingfisher</species>
<type>kookaburra</type>
<name>Bruce</name>
</category>
</animal>
<animal>
<category id="48">
<species>monotremes</species>
<type>platypus</type>
<name>Bruce</name>
</category>
</animal>
<animal>
<category id="4">
<species>arachnid</species>
<type>funnel web</type>
<name>Bruce</name>
<legs>8</legs>
</category>
</animal>
</document>
XML;

/*** a new simpleXML iterator object ***/
try{
/*** a new simple xml iterator ***/
$it = simplexml_load_string($xmlstring, 'SimpleXMLIterator');
/*** a new limitIterator object ***/
foreach(new RecursiveIteratorIterator($it, 1) as $name => $data)
{
echo $name.' -- '.$data.'<br />';
}
}
catch(Exception $e)
{
echo $e->getMessage();
}
?>

The above code will produce a dump of all the elements within the XML tree and if they have a value, that will also be shown. What we have done is used the simplexml_load_stirng() function, which returns an object, to load its return value directly into the SimpoeXmlIterator. Remember that the SimpleXmlIterator is a recursive iterator, so we need an iterator to iterate over a RecursiveIterator. The correct tool for this is the RecursiveIteratorIterator which will, as the name suggests, iterate over a recursive iterator. But, we usually need a little more control than that. Here we will select the species node of each animal. Only the code is shown, not the XML.

File: simple_xml_iterator2.php

<?php

/*** a simple xml tree ***/
$xmlstring = <<<XML
<?xml version = "1.0" encoding="UTF-8" standalone="yes"?>
<document>
<animal>
<category id="26">
<species>Phascolarctidae</species>
<type>koala</type>
<name>Bruce</name>
</category>
</animal>
<animal>
<category id="27">
<species>macropod</species>
<type>kangaroo</type>
<name>Bruce</name>
</category>
</animal>
<animal>
<category id="28">
<species>diprotodon</species>
<type>wombat</type>
<name>Bruce</name>
</category>
</animal>
<animal>
<category id="31">
<species>macropod</species>
<type>wallaby</type>
<name>Bruce</name>
</category>
</animal>
<animal>
<category id="21">
<species>dromaius</species>
<type>emu</type>
<name>Bruce</name>
</category>
</animal>
<animal>
<category id="22">
<species>Apteryx</species>
<type>kiwi</type>
<name>Troy</name>
</category>
</animal>
<animal>
<category id="23">
<species>kingfisher</species>
<type>kookaburra</type>
<name>Bruce</name>
</category>
</animal>
<animal>
<category id="48">
<species>monotremes</species>
<type>platypus</type>
<name>Bruce</name>
</category>
</animal>
<animal>
<category id="4">
<species>arachnid</species>
<type>funnel web</type>
<name>Bruce</name>
<legs>8</legs>
</category>
</animal>
</document>
XML;

try {
/*** a new simpleXML iterator object ***/
$sxi =new SimpleXMLIterator($xmlstring);

foreach ( $sxi as $node )
{
foreach($node as $k=>$v)
{
echo $v->species.'<br />';
}
}
}
catch(Exception $e)
{
echo $e->getMessage();
}
?>

So now we simply have a list like this:

Phascolarctidae
macropod
diprotodon
macropod
dromaius
Apteryx
kingfisher
monotremes
arachnid

What has been done in the above script is to instantiate a new SimpleXmlIterator object. Then we have needed two foreach loops to correctly return the species values. This to me looks rather cumbersome, so lets try another way.

File: simple_xml_iterator3.php

<?php

try {
$sxe = simplexml_load_string($xmlstring, 'SimpleXMLIterator');

for ($sxe->rewind(); $sxe->valid(); $sxe->next())
{
if($sxe->hasChildren())
{
foreach($sxe->getChildren() as $element=>$value)
{
echo $value->species.'<br />';
}
}
}
}
catch(Exception $e)
{
echo $e->getMessage();
}
?>

In the above code we have loaded the SimpleXmlIterator the simplexml_load_string() function and still have needed two loops, a for and a foreach, to iterate over the object. Here we have also used a conditional statement with SimpleXmlIterator::hasChildren() to check for the existence of child nodes. If a child node is found, the SimpleXmlIterator::getChildren() method can the node values and we can then view the results. Is this a better way? Is it faster? Lets look at a quick test with AB (Apache Bench) to see what results we can get.

The first method using two foreach statements and AB returned these results with 10,000 requests with a concurrency of 20.

Concurrency Level:20
Time taken for tests: 55.785945 seconds
Complete requests:10000
Failed requests:0
Write errors: 0
Total transferred:3600720 bytes
HTML transferred: 1380276 bytes
Requests per second:179.26 [#/sec] (mean)
Time per request: 111.572 [ms] (mean)
Time per request: 5.579 [ms] (mean, across all concurrent requests)
Transfer rate:63.03 [Kbytes/sec] receive

The second method with 10,000 requests a concurrency of 20 produced the following results.

Concurrency Level:20
Time taken for tests: 60.696794 seconds
Complete requests:10000
Failed requests:0
Write errors: 0
Total transferred:3600000 bytes
HTML transferred: 1380000 bytes
Requests per second:164.75 [#/sec] (mean)
Time per request: 121.394 [ms] (mean)
Time per request: 6.070 [ms] (mean, across all concurrent requests)
Transfer rate:57.91 [Kbytes/sec] received

The second method comes in at about 5 seconds slower than the first. Showing that while the nested foreach loops may look a little clumsy, but as the iterator uses less memory, it is more efficient. The reality is that both these methods suffer and a better way is to had with xpath.

The SimpleXmlIterator::xpath() method allows direct iteration over an XML tree by specifying the xpath to the node. Similar to a file system path, the xpath is of the species node in our XML document looks like this:

animal/category/species

The ability to specify the xpath to the particular node saves us many iterations of the XML tree which, in turn, saves us CPU cycles, thus allowing for much faster scripts. The code below will give the same listing of species as the two examples above, but this time we will use the xpath to go directly to the node we want to view. Once again, the XML tree has been omitted for the sake of brevity.

File: simple_xml_iterator4.php

<?php
try {
/*** a new simpleXML iterator object ***/
$sxi =new SimpleXMLIterator($xmlstring);

/*** set the xpath ***/
$foo = $sxi->xpath('animal/category/species');

/*** iterate over the xpath ***/
foreach ($foo as $k=>$v)
{
echo $v.'<br />';
}
}
catch(Exception $e)
{
echo $e->getMessage();
}
?>

Once again we produce a list of species from XML tree like this:

  • Phascolarctidae
  • macropod
  • diprotodon
  • macropod
  • dromaius
  • Apteryx
  • kingfisher
  • monotremes
  • arachnid

This is a far more efficient method of retrieving XML data. By simply changing the species path to name we could fetch a list of the animal names. All Australian animals would seem to be called Bruce. We can traverse unlimitted depths with the xpath. Below is smail XML tree with more depth.

File: simple_xml_iterator5.php

<?php

/*** The path to enlightenment ***/
$xmlstring = <<<XML
<?xml version = "1.0" encoding="UTF-8" standalone="yes"?>
<document>
<ignorance>
I dont want to know
<discovery>
Whats this then?
<awareness>
This could be useful
<acceptance>
I must use this
<enlightenment>
SPL
</enlightenment>
</acceptance>
</awareness>
</discovery>
</ignorance>

<ignorance>
knowledge is truth
<discovery>
Seek only the strong
<awareness>
look deeply for insight
<acceptance>
Open your mind
<enlightenment>
Wisdom is yours
</enlightenment>
</acceptance>
</awareness>
</discovery>
</ignorance>
</document>
XML;

try {
/*** a new simpleXML iterator object ***/
$sxi =new SimpleXMLIterator($xmlstring);

/*** set the xpath ***/
$foo = $sxi->xpath('ignorance/discovery/awareness/acceptance/enlightenment');

/*** iterate over the xpath ***/
foreach ($foo as $k=>$v)
{
echo $v.'<br />';
}
}
catch(Exception $e)
{
echo $e->getMessage();
}
?>

The XML tree above has five levels from the document root. Using xpath we have traversed the tree directly to the node we seek. The saving in iterations here is obvious as we no longer need to set up a foreach heirachy to traverse the tree on the. The xpath to enlightenment just got easier.

A problem arises in XML when two documents may have similar names. If we had the xml document of animals, and another of animal owners, a conflict may arise in the name node. Each of the animals, and owners would have a name node and a method is required to distinguish between them. The SimpleXMLIterator provides several methods for the management of namespaces. These include as listed in the top of the this chapter. The script below shows a small change to the xml string that has been used and demonstrates the use of namespaces and how to utilize them within the SimpleXMLIterator.

File: simple_xml_iterator5.php

<?php

/*** a simple xml tree ***/
$xmlstring = <<<XML
<?xml version = "1.0" encoding="UTF-8" standalone="yes"?>
<document xmlns:spec="http://example.org/animal-species">
<animal>
<category id="26">
<species>Phascolarctidae</species>
<spec:name>Speed Hump</spec:name>
<type>koala</type>
<name>Bruce</name>
</category>
</animal>
<animal>
<category id="27">
<species>macropod</species>
<spec:name>Boonga</spec:name>
<type>kangaroo</type>
<name>Bruce</name>
</category>
</animal>
<animal>
<category id="28">
<species>diprotodon</species>
<spec:name>pot holer</spec:name>
<type>wombat</type>
<name>Bruce</name>
</category>
</animal>
<animal>
<category id="31">
<species>macropod</species>
<spec:name>Target</spec:name>
<type>wallaby</type>
<name>Bruce</name>
</category>
</animal>
<animal>
<category id="21">
<species>dromaius</species>
<spec:name>Road Runner</spec:name>
<type>emu</type>
<name>Bruce</name>
</category>
</animal>
<animal>
<category id="22">
<species>Apteryx</species>
<spec:name>Football</spec:name>
<type>kiwi</type>
<name>Troy</name>
</category>
</animal>
<animal>
<category id="23">
<species>kingfisher</species>
<spec:name>snaker</spec:name>
<type>kookaburra</type>
<name>Bruce</name>
</category>
</animal>
<animal>
<category id="48">
<species>monotremes</species>
<spec:name>Swamp Rat</spec:name>
<type>platypus</type>
<name>Bruce</name>
</category>
</animal>
<animal>
<category id="4">
<species>arachnid</species>
<spec:name>Killer</spec:name>
<type>funnel web</type>
<name>Bruce</name>
<legs>8</legs>
</category>
</animal>
</document>
XML;

/*** a new simpleXML iterator object ***/
try {
/*** a new simpleXML iterator object ***/
$sxi =new SimpleXMLIterator($xmlstring);

$sxi-> registerXPathNamespace('spec', 'http://www.exampe.org/species-title');

/*** set the xpath ***/
$result = $sxi->xpath('//spec:name');

/*** get all declared namespaces ***/
foreach($sxi->getDocNamespaces('animal') as $ns)
{
echo $ns.'<br />';
}

/*** iterate over the xpath ***/
foreach ($result as $k=>$v)
{
echo $v.'<br />';
}
}
catch(Exception $e)
{
echo $e->getMessage();
}
?>

In the script above we have declared a namespace in our root as
<document xmlns:spec=”http://example.org/animal-species”>

Also not the addtion to the XML of a second name node. This is prefixed by the spec: prefix to identify the namespace and not confuse it with the regular animal names. The use of the SimpleXMLIterator::registerXPathNamespace() method ensures the document namespace prefixing is sane. To get the namespaces the SimpleXMLIterator::getDocNamespaces() method is used to get the names of all namespaces declared within the document. The SimpleXMLIterator::getNamespaces() method could also have been used. The SimpleXMLIterator::getDocNamespaces() method only gets those namespaces that are actually declared within the document itself.

To add a child to the XML tree is also simple with the SimpleXmlIterator::addChild method. The XML string for this script has been simplified to better demonstrate adding a child.

File: simple_xml_iterator6.php

<?php
$xmlstring = <<<XML
<?xml version = "1.0" encoding="UTF-8" standalone="yes"?>
<document>
<animal>koala</animal>
<animal>kangaroo</animal>
<animal>wombat</animal>
<animal>wallaby</animal>
<animal>emu</animal>
<animal>kiwi</animal>
<animal>kookaburra</animal>
<animal>platypus</animal>
<animal>funnel web</animal>
</document>
XML;

try {
/*** a new simpleXML iterator object ***/
$sxi =new SimpleXMLIterator($xmlstring);

/*** add a child ***/
$sxi->addChild('animal', 'Tiger');

/*** a new simpleXML iterator object ***/
$new = new SimpleXmlIterator($sxi->saveXML());

/*** iterate over the new tree ***/
foreach($new as $val)
{
echo $val.'<br />';
}
}
catch(Exception $e)
{
echo $e->getMessage();
}
?>

The script above produces a list like this:

  • koala
  • kangaroo
  • wombat
  • wallaby
  • emu
  • kiwi
  • kookaburra
  • platypus
  • funnel web
  • Tiger

Note that when the new we call the saveXML() method that the XML is saved as an XML string once more. If we wish to iterate over the new string, we need to use another iterator instance. The saveXML method is an alias of asXML and was added to remove the inconsistency with domXML::saveXML. Adding an attribute is just as easy with the addAttribute method as shown here.

File: simple_xml_iterator7.php

<?php
$xmlstring = <<<XML
<?xml version = "1.0" encoding="UTF-8" standalone="yes"?>
<document>
<animal>koala</animal>
<animal>kangaroo</animal>
<animal>wombat</animal>
<animal>wallaby</animal>
<animal>emu</animal>
<animal>kiwi</animal>
<animal>kookaburra</animal>
<animal>platypus</animal>
<animal>funnel web</animal>
</document>
XML;

try {
/*** a new simpleXML iterator object ***/
$sxi =new SimpleXMLIterator($xmlstring);

/*** add a child ***/
$sxi->addChild('animal', 'Tiger');

/*** a new simpleXML iterator object ***/
$new = new SimpleXmlIterator($sxi->saveXML());

/*** iterate over the new tree ***/
foreach($new as $val)
{
echo $val.'<br />';
}
}
catch(Exception $e)
{
echo $e->getMessage();
}
?>

For demonstration purposes htmlentities() has been used to show the actual XML data and not just the values. The resulting XML string now looks like this:

<?xml version=”1.0″ encoding=”UTF-8″ standalone=”yes”?>

<document xmlns:id=”urn::test-foo” id:att1=”good things” att2=”no-ns”>

<animal>koala</animal>

<animal>kangaroo</animal>

<animal>wombat</animal>

<animal>wallaby</animal>

<animal>emu</animal>

<animal>kiwi</animal>

<animal>kookaburra</animal>

<animal>platypus</animal>

<animal>funnel web</animal>

</document>

LimitIterator

As the name suggests, the LimitIterator allows the coder to limit the return values. Lets see the methods available to the LimitIterator.

File: limit_iterator.php

<?php
/*** list all class methods ***/
foreach( get_class_methods(LimitIterator) as $methodName)
{
echo $methodName.'<br />';
}
?>

he results from above show us the following methods available to the LimitIterator

  • __construct
  • rewind
  • valid
  • key
  • current
  • next
  • seek
  • getPosition
  • getInnerIterator

Once again we see the usual suspects os valid() key() current() next() for iteration. What makes the LimitIterator funky is the seek() method and getPosition() method. The LimitIterator is an outer iterator and takes an iterator as a parameter for the construct. The LimitIterator allows you to specify the offset and the number of results returned. I can already hear the gears grinding as you think how this might be ideal for pagination of an aggregate structure such as database results, or an XML heirachy. This sort of functionality allows us to mimic the behaviour of the SQL LIMIT clause with the use of an offset and limit,except we can use it over any aggregate structure.

File: limit_iterator1.php

<?php

/*** the offset value ***/
$offset = 3;

/*** the limit of records to show ***/
$limit = 2;

/*** a simple xml tree ***/
$xmlstring = <<<XML
<?xml version = "1.0" encoding="UTF-8" standalone="yes"?>
<document>
<animal>koala</animal>
<animal>kangaroo</animal>
<animal>wombat</animal>
<animal>wallaby</animal>
<animal>emu</animal>
<animal>kiwi</animal>
<animal>kookaburra</animal>
<animal>platypus</animal>
</document>
XML;

try{
/*** a new limitIterator object ***/
$it = new LimitIterator(new SimpleXMLIterator($xmlstring), $offset, $limit);
/*** show the results ***/
foreach($it as $r)
{
// output the key and current array value
echo $it->key().' -- '.$it->current().'<br />';
}
}
catch(Exception $e)
{
echo $e->getMessage();
}

The above code will produce a list of like this:

  • animal — wallaby
  • animal — emu

Using the offset and limit variables, we have successfully extracted the XML data by positioning the internal pointer to the third item in the tree (offset)and fetching the next two items (limit). The offset an limit variables could come from POST or GET to aid in your application pagination. We saw here the use of the SimpleXMLiterator as our inner iterator. The SimpleXMLIterator can do much more. You can read more in SimpleXML Iterator chapter. The LimitIterator also allows the checking of the position of the internal pointer to check where you are in your iteration. Here we will switch to an ArrayIterator as the inner iterator to see your we can get the position of the internal pointer with the getPosition() method.

File: limit_iterator2.php

<?php
/*** the offset value ***/
$offset = 3;

/*** the limit of records to show ***/
$limit = 2;

$array = array('koala', 'kangaroo', 'wombat', 'wallaby', 'emu', 'kiwi', 'kookaburra', 'platypus');

$it = new LimitIterator(new ArrayIterator($array), $offset, $limit);

foreach($it as $k=>$v)
{
echo $it->getPosition().'<br />';
}
?>

The code above will give us the results 3 and 4. The LimitIterator::getPosition() method has given the current position within the iterator. We can also use the seek() method to go directly to a position within the iterator. When called, the seek() method implements the SeekableIterator to find the required position and exposing the value to the current() method.

File: limit_iterator3.php

<?php

/*** the offset value ***/
$offset = 4;

/*** the limit of records to show ***/
$limit = 6;

/*** a simple array ***/
$array = array('koala', 'kangaroo', 'wombat', 'wallaby', 'emu', 'kiwi', 'kookaburra', 'platypus');

$it = new LimitIterator(new ArrayIterator($array), $offset, $limit);

try
{
$it->seek(3);
echo $it->current();
}
catch(OutOfBoundsException $e)
{
echo $e->getMessage() . "<br />";
}
?> 

If you run the above code you will get an error message like this:

Cannot seek to 3 which is below the offset 4

This is because we have incorrectly specified the seek parameter and an OutOfBoundsException is thrown. We then catch the exception and many handle the error as we wish. Lets try again with some valid values.

File: limit_iterator4.php

<?php

/*** a simple array ***/
$array = array('koala', 'kangaroo', 'wombat', 'wallaby', 'emu', 'kiwi', 'kookaburra', 'platypus');

$it = new LimitIterator(new ArrayIterator($array));

try
{
$it->seek(5);
echo $it->current();
}
catch(OutOfBoundsException $e)
{
echo $e->getMessage() . "<br />";
}
?>

This time we have removed the offset and the limit and the whole inner iterator is exposed. The seek() method has been used to direct the internal pointer to the offset of 5. It is then a simple matter to echo the current() value, in this case the result is kiwi. Like all outer iterators, the LimitIterator allows us access to the inner iterator with the getInnerIterator() method.

File: limit_iterator5.php

<?php

/*** the offset value ***/
$offset = 2;

/*** a simple array ***/
$array = array('koala', 'kangaroo', 'wombat', 'wallaby', 'emu', 'kiwi', 'kookaburra', 'platypus');

/*** a new LimitIterator object ***/
$it = new LimitIterator(new ArrayIterator($array), $offset);

/*** access the inner iterator method offsetUnset() ***/
$it->getInnerIterator()->offsetUnset(5);

try
{
foreach($it as $value)
{
echo $value.'<br />';
}
}
catch(OutOfBoundsException $e)
{
echo $e->getMessage() . "<br />";
}
?>

This time we have omitted the limit parameter and used only the offset. This will allow the LimitIterator to begin at the offset and traverse to the end of the structure. We have also accessed the inner iterator (ArrayIterator) with the getInnerIterator() method and called ArrayIterator::offsetUnset() method to remove the kiwi from our lives.

Hi, My name is Masud Alam, love to work with Open Source Technologies, living in Dhaka, Bangladesh. I’m a Certified Engineer on ZEND PHP 5.3, I served my first five years a number of leadership positions at Winux Soft Ltd, SSL Wireless Ltd, Canadian International Development Agency (CIDA), World Vision, Care Bangladesh, Helen Keller, US AID and MAX Group where I worked on ERP software and web development., but now i’m a founder and CEO of TechBeeo Software Company Ltd. I’m also a Course Instructor of ZCPE PHP 7 Certification and professional web development course at w3programmers Training Institute – a leading Training Institute in the country.

One comment on “Introduction to Standard PHP Library -SPL Part-2

Leave a Reply

Your email address will not be published. Required fields are marked *