|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
Note: This is an unedited contribution. If this article is inappropriate,
needs attention or copies someone else's work without reference then please
Report This Article
The Standard PHP Library (SPL) is where PHP 5's object-oriented capabilities truly shine. It improves the language in five key ways: iterators, exceptions, array overloading, XML, and file and data handling. It also provides a few other useful items, such as the observer pattern, counting, helper functions for object identification, and iterator processing. Additionally, it offers advanced functionality for autoloading classes and interfaces. This chapter introduces you to this very important library, and in the following chapters, you will learn more about some of the advanced SPL classes. SPL is enabled by default and is available on most PHP 5 systems. However, because SPL's features were dramatically expanded with the PHP 5.2 release, I recommend using that version or newer if you want to truly take advantage of this library. SPL FundamentalsThe SPL is a series of Zend Engine 2 additions, internal classes, and a set of PHP examples. At the engine level, the SPL implements a set of six classes and interfaces that provide all the magic.These interfaces and the Exception class are special in that they are not really like a traditional interface. They have extra powers and allow the engine to hook into your code in a specific and special way. Here are brief descriptions of these elements: In the rest of this chapter, we'll take a closer look at some of the SPL features, beginning with iterators. IteratorsIterators are classes that implement the Iterator InterfaceThe Iterator interface is defined internally in C code, but if it were represented in PHP, it would look something like Listing 9-1. Listing 9-1. The Iterator Interface interface Iterator { public function current(); public function key(); public function next(); public function rewind(); public function valid(); } Note You do not need to declare Iterator or any other SPL interface yourself. These interfaces are automatically provided by PHP. All iterable objects are responsible for keeping track of their current state. In a normal array, this would be called the array pointer. In your object, any type of variable could be used to track the current element. It is very important to remember the position of elements, as iteration requires stepping through elements one at a time. Table 9-1 lists the methods in the Table 9-1. Iterator Interface Methods
Uses for iterators range from looping over objects to looping over database result sets, and even looping around files. In the next chapter, you will learn about the types of built-in iterator classes and their uses. Iterator Helper FunctionsSeveral useful convenience functions can be used with iterators:
Caution The iterator_to_array($iterator) and iterator_count($iterator) functions can cause some spooky action if you call them on an iterator that does not have a defined ending point. This is because they require an internal exercise of the entire iterator. So if your iterator's valid() method will never return false, do not use these functions, or you will create an infinite loop.
Listing 9-2. Using iterator_apply function print_entry($iterator) {
print( $iterator->current() );
return true;
}
$array = array(1,2,3);
$iterator = new ArrayIterator($array);
iterator_apply($iterator, 'print_entry', array($iterator));
This code outputs the following: 123
While the callback function returns Array OverloadingArray overloading is the process of using an object as an array. This means allowing data access through the [] array syntax. The ArrayAccess InterfaceThe ArrayAccess interface is described in Listing 9-3. Listing 9-3. The ArrayAccess Interface interface ArrayAccess { public function offsetExists($offset); public function offsetSet($offset, $value); public function offsetGet($offset); public function offsetUnset($offset); } Table 9-2 lists the methods in the Table 9-2. ArrayAccess Interface Methods
In the following chapters, you will be introduced to some of the advanced uses for array overloading. Counting and ArrayAccessWhen working with objects acting as arrays, it is often advantageous to allow them to be used exactly as an array would be used. However, by itself, an Fortunately, there is a solution: the Listing 9-4. The Countable Interface interface Countable { public function count(); } When implemented, the The Observer PatternThe observer pattern is a very simple event system that includes two or more interacting classes. This pattern allows classes to observe the state of another class and to be notified when the observed class's state has changed. In the observer pattern, the class that is being observed is called a subject, and the classes that are doing the observing are called observers. To represent these, SPL provides the Listing 9-5. The SplSubject Interface interface SplSubject { public function attach(SplObserver $observer); public function detach(SplObserver $observer); public function notify(); } Listing 9-6. The SplObserver Interface interface SplObserver { public function update(SplSubject $subject); } The idea is that the Listing 9-7 shows an example of using SplSubject and SplObserver. Listing 9-7. The Observer Pattern class DemoSubject implements SplSubject { private $observers, $value; public function __construct() { $this->observers = array(); } public function attach(SplObserver $observer) { $this->observers[] = $observer; } public function detach(SplObserver $observer) { if($idx = array_search($observer,$this->observers,true)) { unset($this->observers[$idx]); } } public function notify() { foreach($this->observers as $observer) { $observer->update($this); } } public function setValue($value) { $this->value = $value; $this->notify(); } public function getValue() { return $this->value; } } class DemoObserver implements SplObserver { public function update(SplSubject $subject) { echo 'The new value is '. $subject->getValue(); } } $subject = new DemoSubject(); $observer = new DemoObserver(); $subject->attach($observer); $subject->setValue(5); Listing 9-7 generates the following output: The new value is 5 The benefits of the observer pattern are that there may be many or no observers attached to the subscriber and you don't need to have prior knowledge of which classes will consume events from your subject class. PHP 6 introduces the This class is similar to an array, except that it can store only unique objects and store only a reference to those objects. It offers a few benefits. One is that you cannot attach a class twice, as you can with the example in Listing 9-7, and because of this, you can prevent Since Listing 9-8. SplObjectStorage and the Observer Pattern class DemoSubject implements SplSubject { private $observers, $value; public function __construct() { $this->observers = new SplObjectStorage(); } public function attach(SplObserver $observer) { $this->observers->attach($observer); } public function detach(SplObserver $observer) { $this->observers->detach($observer); } public function notify() { foreach($this->observers as $observer) { $observer->update($this); } } public function setValue($value) { $this->value = $value; $this->notify(); } public function getValue() { return $this->value; } } class DemoObserver implements SplObserver { public function update(SplSubject $subject) { echo 'The new value is '. $subject->getValue(); } } $subject = new DemoSubject(); $observer = new DemoObserver(); $subject->attach($observer); $subject->setValue(5); Listing 9-8 generates the following output: The new value is 5 SerializationThe SPL's The magic methods cannot serialize private variables from a base class. The
Listing 9-9 demonstrates a scenario that magic methods cannot handle. Listing 9-9. Magic Method Serialization error_reporting(E_ALL); //Ensure notices show class Base { private $baseVar; public function __construct() { $this->baseVar = 'foo'; } } class Extender extends Base { private $extenderVar; public function __construct() { parent::__construct(); $this->extenderVar = 'bar'; } public function __sleep() { return array('extenderVar', 'baseVar'); } } $instance = new Extender(); $serialized = serialize($instance); echo $serialized . "\n"; $restored = unserialize($serialized); Running the code in Listing 9-9 results in the following notice: Notice: serialize(): "baseVar" returned as member variable from __sleep() but does not exist ... O:8:"Extender":2:{s:21:"ExtenderextenderVar";s:3:"bar"; s:7:"baseVar";N;} To solve this problem and properly serialize the Listing 9-10. The Serializable Interface interface Serializable { public function serialize(); public function unserialize( $serialized ); } The The Listing 9-11 shows the serialization of a private member of a base class. Listing 9-11. Serializing a Private Member in a Base Class error_reporting(E_ALL); class Base implements Serializable { private $baseVar; public function __construct() { $this->baseVar = 'foo'; } public function serialize() { return serialize($this->baseVar); } public function unserialize($serialized) { $this->baseVar = unserialize($serialized); } public function printMe() { echo $this->baseVar . "\n"; } } class Extender extends Base { private $extenderVar; public function __construct() { parent::__construct(); $this->extenderVar = 'bar'; } public function serialize() { $baseSerialized = parent::serialize(); return serialize(array($this->extenderVar, $baseSerialized)); } public function unserialize( $serialized ) { $temp = unserialize($serialized); $this->extenderVar = $temp[0]; parent::unserialize($temp[1]); } } $instance = new Extender(); $serialized = serialize($instance); echo $serialized . "\n"; $restored = unserialize($serialized); $restored->printMe(); Listing 9-11 has the following output: C:8:"Extender":42:{a:2:{i:0;s:3:"bar";i:1;s:10:"s:3:"foo";";}} foo As you can see, the foo value of the base class was properly remembered and restored. The code in Listing 9-11 is very simple, but you can combine the code with functions like The The SPL AutoloadingThe Listing 9-12. The __autoload Magic Method function __autoload($class) {
require_once($class . '.inc');
}
$test = new SomeClass(); //Calls autoload to find SomeClass
Now, this isn't SPL. However, SPL does take this concept to the next level, introducing the ability to declare multiple autoload functions. If you have a large application consisting of several different smaller applications or libraries, each application may wish to declare an The The Listing 9-13 shows the registration of the default methods, the configuration of file extensions for the default Listing 9-13. SPL Autoload spl_autoload_register(null,false); spl_autoload_extensions('.php,.inc,.class,.interface'); function myLoader1($class) { //Do something to try to load the $class } function myLoader2($class) { //Maybe load the class from another path } spl_autoload_register('myLoader1',false); spl_autoload_register('myLoader2',false); $test = new SomeClass(); In Listing 9-13, the It is critical to remember that as soon as Listing 9-14. Safe spl_autoload_register Call if(false === spl_autoload_functions()) { if(function_exists('__autoload')) { spl_autoload_register('__autoload',false); } } //Continue to register autoload functions The initialization in Listing 9-14 first calls the You can also call Next, you can manually invoke the loader without actually attempting to utilize the class, by calling the Note The second parameter to class_exists() controls whether or not it attempts to invoke the autoloading mechanism. The function spl_autoload_call() is already integrated with class_exists() when used in autoloading mode. Listing 9-15 shows an example of a clean-failure load attempt using both Listing 9-15. Clean Loading //Try to load className.php if(spl_autoload_call('className') && class_exists('className',false) ) { echo 'className was loaded'; //Safe to instantiate className $instance = new className(); } else { //Not safe to instantiate className echo 'className was not found'; } Object IdentificationSometimes it is advantageous to have a unique code for each instance of a class. For this purpose, SPL provides the Listing 9-16. Invoking spl_object_hash class a {} $instance = new a(); echo spl_object_hash($instance); This code generates the following output: c5e62b9f928ed0ca74013d3e85bbf0e9 Each hash is guaranteed to be unique for every object within the context of a single call. Repeated execution will likely result in the same hashes being generated but is not guaranteed to produce duplicate hashes. References to the same object in the same call are guaranteed to be identical, as shown in Listing 9-17. Listing 9-17. spl_object_hash and References class a {} $instance = new a(); $reference = $instance; echo spl_object_hash($instance) . "\n"; echo spl_object_hash($reference) . "\n"; Listing 9-17 generates the following output: c5e62b9f928ed0ca74013d3e85bbf0e9 c5e62b9f928ed0ca74013d3e85bbf0e9 This data is similar to the comparison === operator; however, some uses may benefit from a hash code approach. For example, when registering objects in an array, the hash code may be used as the key for easier access. Just the FactsIn this chapter, you were introduced to the SPL. The following chapters will build on this introduction. Iterators can be used in looping structures. SPL provides the SPL includes the Using the SPL observer pattern and the PHP 6-specific SPL autoloading is provided by the Object identification is provided by the In the following chapter, you will be introduced to some of the more advanced iterator patterns, so be sure to keep the lessons learned about iterators and their helper functions in mind. This sample content is an excerpt from the book, Pro PHP: Patterns, Frameworks, Testing and More, by Kevin McArthur, Copyright 2008, Apress, Inc. The source code for this book is available to readers at http://apress.com/book/downloadfile/3934. KEVIN MCARTHUR is an open source developer, residing in British Columbia, Canada. He is a self-taught entrepreneur and has been running a very successful PHP application development studio for more than eight years. His company, StormTide Digital Studios, has worked with industry in the United States and Canada to provide scaling solutions for web statistics, VoIP, and print automation. An avid IRC user, Kevin helps to administer one of the largest PHP support organizations, PHP EFnet. Kevin's contributions to open source projects, including the Zend Framework, have made him a well-known authority in the industry. He has written several articles for PHPRiot.com on topics such as reflection, the Standard PHP Library, object-oriented programming, and PostgreSQL.
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||