Appendix B. Object-Oriented PHP

 

This appendix covers

  • The PHP object model
  • An introduction to the iterators, arrays, and counting interfaces provided by the Standard PHP Library
  • The basics of software design patterns, including the Singleton and Registry patterns

 

This appendix will refresh your knowledge of those bits of PHP that you haven’t had a chance to deal with until now. Zend Framework is written in PHP5 and is fully object oriented, and you’ll need to have a reasonable amount of knowledge about OOP in PHP to make sense of it. This chapter is no substitute for a full book, such as PHP in Action by Reiersøl, Baker, and Shiflett, but it should help you enough to get going with Zend Framework.

We’ll look at what makes a class, including the visibility specifiers of public, protected, and private. And because the full power of object orientation is realized when classes are extended, we’ll also investigate the use of abstract classes and interfaces to help document the API for a component.

PHP5 introduced the SPL, a library that allows you to make your classes easier to use. Zend Framework uses SPL internally, so we’ll look at some of the powerful things that are possible with it.

We’ll also look at software design patterns. A design pattern is an approach to a given problem used by many people. I use the word “approach” deliberately, as the actual code used to solve the problem is usually different, but all solutions share the same design. Design patterns give us a vocabulary we can use to share solutions and to also understand each other. They also have the benefit of having been tested in real applications, so you can be sure that the design pattern is a good solution to the class of problems that it helps to solve.

Let’s dive right in and look at object orientation.

B.1. Object Orientation in PHP

OOP is a way to group related functions and variables together. The fundamental goal is to produce more maintainable code—that is, code that doesn’t accidentally break when you’re working on something else.

In this section, we’ll look at the basics of classes and objects, and how they can be related using inheritance. We’ll then look at how interfaces and abstract classes can help to control how classes are used. Finally, we’ll look at PHP5’s so-called “magic” methods.

B.1.1. Classes, Objects, and Inheritance

In order to understand OOP, you need to understand classes and objects. While we’re here, we’ll also look at interfaces, which are closely related to classes.

A class is the code that groups methods and variables together. That’s all. It’s not scary! Listing B.1 shows the most simple class.

Listing B.1. The most basic implementation of a class

As you can see, the class keyword is used to declare the class, and it’s followed by the class’s name, ZFiA_Person in this case . The name of the class has to be unique across all your files, so it’s a good idea to prefix every class name with a unique identifier. In our case, we’ll use the prefix “ZFiA”. While this is a bit more work when writing the code, the benefits down the line, when you need to integrate your code with someone else’s, far outweigh any initial pain and using a prefix is recommended by the php.net manual.

An object is a runtime instantiation of a class. This means that it has a name and it’s created using the new keyword:

  $rob = new ZFiA_Person();

The variable $rob is an object, and it follows that you can have many objects of the same type. For instance, I can create a $nick object using the same syntax:

  $nick = new ZFiA_Person();

I now have two objects that are of the same class. That is, there’s only one class but two instance objects of it. You can create any number of objects from a particular class.

Let’s look at how we can set up the object using a constructor so that it’s ready to use immediately after creation.

Constructing and Destructing

Usually an object needs to set up its internal state; that is, it must set the initial values of its member variables. To do this, a special method, called a constructor, is used, which has the name __construct(). This method is called automatically when a new object is instantiated. It’s very common for a class to have a constructor, and usually one or more function parameters are used to set the object’s internal state. This is shown in listing B.2.

At the opposite end of the object’s lifetime, another method is called, __destruct(), just as the object is about to be deleted. This method is called the destructor, and it’s rarely used in PHP scripts intended for the web because of the “setup and tear-down every request” nature of PHP. In PHP, all objects are destroyed automatically at the end of the request and recreated at the start of the next request.

Listing B.2. Adding a constructor to our class from listing B.1

Listing B.2 introduces the private and public keywords that control the visibility of methods and variables within a class. These are discussed in more depth in the next section. As you can see in listing B.2, we’ve created two member variables , which are set to their initial values using the constructor .

Public, Protected, and Private Visibility

As we’ve noted, a class can have methods and variables. This grouping is useful in its own right, to help organize code, but it really comes into its own when we add information hiding to the mix.

 

Note

Information hiding is the ability to mark class methods and variables as invisible outside the class boundary. This leads to more maintainable code, because each class “publishes” an API for other classes to use, but is able to implement that API however it would like to.

 

There are three keywords involved: public, protected, and private. They follow the same usage as in other object-oriented languages, such as C++, Java, or C#:

  • public—Available from any scope
  • protected—Only available from within the class or any of its children
  • private—Only available within the defining class itself

Let’s look at this in use, and flesh out the ZFiA_Person class in listing B.3.

Listing B.3. A simple class

As you can see, we’ve marked the $_firstName and $_lastName member variables as protected. Because they’re inaccessible to users of this class, we’ve provided a method, fullName(), to allow access to the data. We’re now free to change how we store the first name and last name information, without affecting any code that uses this class.

We also may want to avoid having to repeat this code for a class that is conceptually similar. This is handled by the concept of class extension.

B.1.2. Extending Classes

Extending classes is extremely powerful and can save you a lot of time by facilitating code reuse. When one class extends another, it will inherit the properties and methods of the class it extends, provided they’re not private. The class being extended is called the parent, and the class that is extending the parent is called the child.

When you extend another class, you’re creating an “is a” relationship. That is, your child class “is a” parent class with additional functionality. For example, we could create a ZFiA_Author class that extends ZFiA_Person, which means that an Author is a Person. This is shown in listing B.4.

Listing B.4. Extending ZFiA_Person to create ZFiA_Author

The ZFiA_Author class inherits all the properties and methods of the ZFiA_Person class. It can also have its own properties and methods . You can override the methods by creating new methods with the same name in the child class, or you can use the methods of the parent class simply by calling them. You don’t need to redefine them. Also, the final keyword can be prepended to the declaration of a function next to the visibility keyword to prevent any child classes from overriding the method.

Another thing we may wish to do is create a class that implements common functionality for a series of child classes, but this class should not be instantiated itself. Abstract classes are designed to solve this problem.

B.1.3. Abstract Classes and Interfaces

Two new features of PHP5 are interfaces and abstract classes. These are special constructs that are used with classes to allow a library designer to specify how consumers of the library should use the provided code.

Abstract Classes

Some classes are designed purely to be extended by other classes; these are known as abstract classes and act as a skeleton implementation of a concept that is then fleshed out by child classes. Abstract classes contain their own member variables and methods along with declarations of methods that must be defined by the concrete child classes.

For instance, we could make ZFiA_Person an abstract class, where we determine that all people will have a first name and surname, but leave the definition of how to present their formal name to be determined by the child classes, as shown in listing B.5.

Listing B.5. An abstract class contains undefined functions

Because ZFiA_Person is now declared as an abstract class, it can no longer be instantiated directly using new; it must be extended by the child class instantiated. The fullName() method is defined as abstract, so all child classes (that aren’t themselves declared as abstract) must provide an implementation of this method. We can provide an implementation of ZFiA_Author as shown in listing B.6.

Listing B.6. Creating a concrete class from an abstract one

As you can see, the ZFiA_Author class provides a specific implementation of fullName() that isn’t appropriate to any other child class of ZFiA_Person, because it appends the word “author” to the end of the person’s name.

One limitation of the PHP object model is that a class may only inherit from one parent class. There are lots of good reasons for this, but it does mean that another mechanism is required to enable a class to conform to more than one “template.” This is achieved using interfaces.

Interfaces

Interfaces define a template for classes that implement the interface. The interface is an API or contract that the class must satisfy. In practical terms, this means that an interface is a list of methods that must be defined by implementing classes. Interfaces allow a class to be used by a method that depends upon the class implementing certain functionality.

If we were to make ZFiA_Person an interface, it would be declared as shown in listing B.7.

Listing B.7. An interface, specifying methods that must be defined in the class

If the fullName method isn’t defined in ZFiA_Author, it will result in a fatal error. Because PHP isn’t a statically typed language, interfaces are a convenience mechanism for the programmer to help prevent logical errors, as they may be specified in type hints.

Type hinting tells the PHP interpreter information about the parameters passed to a function or method. Consider the function in listing B.8.

Listing B.8. Providing information about function parameters with type hinting

To create a type hint, all we need to do is include the type of the function parameter, ZFiA_Person in this case . If we try to pass any other kind of object to this function, we’ll get this error message:

  Catchable fatal error: Argument 1 passed to displayPerson() must be an
  instance of ZFiA_Person, string given, called in type_hinting.php on line
  18 and defined in type_hinting.php on line 11

As should be obvious, interfaces aren’t required for PHP coding because PHP isn’t a statically typed language and will do the right thing in the majority of cases. The main benefit of interfaces is for the programmer, because it helps make code self-documenting.

Another feature of PHP5 that is used by Zend Framework to make programmers’ lives easier is overloading, via so-called “magic” methods. These are special methods that PHP calls automatically, when required, allowing the programmer to provide a cleaner interface to the class.

B.1.4. Magic Methods

Magic methods are special methods used by PHP in certain circumstances. Any method whose name begins with “__” (double underscore) is reserved by the PHP language, so all magic methods are prefixed with __. The most common method is the constructor, __construct, which we looked at in section B.1.1. Table B.1 shows all the magic methods you can use in your class.

Table B.1. PHP5’s magic methods are automatically called by PHP when required

Method name

Prototype

When called

__construct() void __construct() When the object is instantiated.
__destruct() void __destruct() When the object is cleaned up and removed from memory.
__call() mixed __call(string $name, array $arguments) When the member function doesn’t exist; used to implement method handling that depends on the function name.
__get() mixed __get (string $name) When the member variable doesn’t exist when retrieving.
__set() void __set (string $name, mixed $value) When the member variable doesn’t exist when setting.
__isset() bool __isset (string $name) When the member variable doesn’t exist when testing if it is set.
__unset() void __unset (string $name) When the member variable doesn’t exist when unsetting.
__sleep() void __sleep() When the object is about to be serialized; used to commit any pending data that the object may be holding.
__wakeup() void __wakeup() When the object has been unserialized; used to reconnect to external resources (such as a database).
__toString() void __toString() When the object is converted to a string; used to output something sensible on echo().
__set_state() static void __set_state(array $properties) When var_export() has been called on the object; used to enable recreation of an object using eval().
__clone() void __clone() When the object is being copied; usually used to ensure that the copy of the object handles references.
__autoload() void __autoload($class_name) When the class can’t be found when instantiating; used to load the class using include() by mapping the class’s name to a file on disk.

Let’s look at one use of __set() and __get() within Zend_Config. Zend_Config stores all of its variables within a protected member variable called $data, and uses the magic __set() and __get() methods to provide public access to the information, as shown in listing B.9.

Listing B.9. Zend_Config’s use of __get() and __set()

As you can see in listing B.9, Zend_Config has a public method, get(), that is used to retrieve a variable, and it provides for a default if the variable isn’t set . The magic method __get() uses the get() method that’s already written , and so allows for accessing a variable as if it were a native member variable. For example, calling this,

  $adminEmail = $config->adminEmail

is identical to calling this,

  $adminEmail = $config->get('adminEmail'),

except that the first way is cleaner and easier to remember!

Similarly, __set() is used for setting a variable . This method implements private business logic to only allow a variable to be set if the object has its __allowModifications variable set. If the object has been created as read-only, an exception is thrown instead.

This concludes our whistle-stop tour through the highlights of objects in PHP. For more in-depth information, I recommend reading Chapters 2 through 6 of PHP in Action by Reiersøl, Baker, and Shiflett.

Another area of PHP5 that is used by Zend Framework is the Standard PHP Library, known as the SPL.

B.2. The SPL

The SPL is a library of useful interfaces designed to make data access easier. It provides the following categories of interfaces:

  • Iterators— Allows you to loop over elements in collections, files, directories, or XML
  • Array access— Makes objects act like arrays
  • Counting— Allows objects to work with count()
  • Observer— Implements the Observer software design pattern

As this is a tour of the SPL, we shall only look at iteration, array access, and counting because we looked at the Observer design pattern in Chapter 9. We’ll start with iterators, because these are the most common use of the SPL.

B.2.1. Using Iterators

Iterators are objects that loop (or traverse) a structure such as an array, a database result set, or a directory listing. You need an iterator for each type of structure that you wish to iterate over, and the most common (arrays, directories) are built into PHP and the SPL directly.

A common situation is reading a directory, and this is done using the SPL’s DirectoryIterator object, as shown in listing B.10.

Listing B.10. Iterating over a directory listing using DirectoryIterator

As you can see, using an iterator is as easy as using a foreach() loop, and that’s the whole point! Iterators make accessing collections of data very easy, and they come into their own when used with custom classes. This can be seen within Zend Framework in the Zend_Db_Table_Rowset_Abstract class, where iterators allow you to traverse all the results returned from a query.

Let’s look at some highlights of the implementation. First, we use Zend_Db_ Table_Rowset as shown in listing B.11.

Listing B.11. Using iteration with Zend_Db_Table_Rowset

As we’d expect, using a rowset is as easy as using an array. This is possible because Zend_Db_Table_Rowset_Abstract implements the Iterator SPL interface, as shown in listing B.12.

Listing B.12. Iterator implementation within Zend_Db_Table_Rowset_Abstract

Zend_Db_Table_Rowset_Abstract holds its data in an array called $_rows and holds the count of how many items are in the array in $_count. We need to keep track of where we are in the array as the foreach() is progressing, and the $_pointer member variable is used for this. To implement Iterator, a class needs to provide five methods: current() , key() , next() , rewind() , and valid() . These methods, as you can see in listing B.12, simply manipulate the $_pointer variable as required and return the correct data in current().

One limitation of Iterator is that it is limited to forward traversal of the data and doesn’t allow for random access. That is, you can’t do this with an instance of Zend_Db_Table_Rowset_Abstract:

  $aUser = $userRowset[4];

This is by design, because the underlying technology behind the class is a database that might not allow random access to a dataset. Other types of objects may require this, though, and for those cases the ArrayAccess interface is used.

B.2.2. Using ArrayAccess and Countable

The ArrayAccess interface allows a class to behave like an array. This means that a user of the class can randomly access any element within the collection using the [] notation, like this:

  $element = $objectArray[4];

As with Iterator, this is achieved using a set of methods that must be implemented by the class. In this case, the methods are offsetExists(), offsetGet(), offsetSet(), and offsetUnset(). The names are self-explanatory and, as shown in listing B.13, allow for several array operations to be carried out.

Listing B.13. Array operations with an ArrayAccess-capable class

The only other array-based functionality that is useful to implement in the context of an object is the count() function. This is done via the Countable interface, which contains just one method, count(). As you’d expect, this function needs to return the number of items in the collection, and then we have all the tools to make a class behave like an array.

The SPL has many other iterators and other interfaces available for more complicated requirements, which are all documented in the excellent php.net manual.

B.3. PHP4

By now, everyone should be using PHP5 because it’s over four years old and PHP4 is no longer supported. If your first steps into PHP5 are with Zend Framework, you should read the excellent migration guide available on the PHP web site at http://www.php.net/manual/en/migration5.php.

With the adoption of PHP5, the PHP world has seen the adoption of software design patterns, which describe how to solve common problems in a language-agnostic manner. Patterns are used throughout Zend Framework’s core code, so we’ll introduce some of the more common ones.

B.4. Software Design Patterns

Everyone who produces software soon discovers that they encounter problems similar to ones they’ve encountered before and that require the same approach to solve. This happens across all software development, and there tend to be best practice solutions for certain classes of problems. In order to make it easier to discuss these approaches to solving problems, the term design pattern has been coined, and catalogues of design patterns are available to help you solve software design problems.

The key thing about design patterns is that they’re not about the code; they’re all about guidelines on how to solve the problem. In this section, we’ll look at the Singleton and Registry design patterns. These are both used within Zend Framework’s core code.

B.4.1. The Singleton Pattern

The Singleton design pattern is a very simple pattern intended to ensure that only one instance of an object can exist. This is used by Zend Framework’s Zend_Controller_ Front front controller to ensure that there is only one front controller in existence for the duration of the request.

Listing B.14 shows the relevant code for implementing the Singleton pattern in Zend_Controller_Front.

Listing B.14. The Singleton pattern as implemented by Zend_Controller_Front

There are three pieces in the Singleton jigsaw, as implemented by Zend_Controller_Front, and most other implementations are similar. A static variable is used to store the instance of the front controller . This is protected to prevent anyone outside of this class from accessing it directly. To prevent use of the new keyword, the constructor is protected , so the only way to create an instance of this class from outside its children is to call the static method getInstance() . getInstance() is static because we don’t have an instance of the class when we call it; it returns an instance to us. Within getInstance(), we only instantiate the class once ; otherwise we just return the previously created instance . Note that Zend_Control-ler_Front allows its child classes to implement their own constructor. It is also common to make the constructor private, and this can be seen in Zend_Search_Lucene_ Document_Html and other classes within the Zend Framework.

The net result is that the code to access the front controller is as follows:

  $frontController = Zend_Controller_Front::getInstance();

This code will work wherever it’s required, and it will create a front controller for us if we don’t have one; otherwise, it returns a reference to the one that has already been created.

Whilst the Singleton is easy to implement, there is a rather large caveat with it: it’s a global variable by the back door. As the Singleton has a static method that returns a reference to the object, it can be called anywhere, and if used unwisely, could introduce coupling between disparate sections of a program. Singletons are also difficult to test, because you have to provide special methods to reset the object’s state.

 

Note

Coupling is the term used when two sections of code are linked in some way. More coupling makes it harder to track down bugs because there are more places where the code could have been changed from.

 

As a result, care should be taken when designing a class to be a Singleton and also when choosing to use the getInstance() method elsewhere in the code. Usually, it’s better to pass an object around so that it’s easier to control access to it than to access the Singleton directly. An example of where we can do this is within the action controller class: Zend_Controller_Action provides a getRequest() method, which returns the Request object, and this method should be used in preference to Zend_Controller_Front::getInstance()->getRequest().

Having said that, it isn’t uncommon to need to access commonly used information from multiple places, such as configuration data. We can use the Registry pattern to ensure that managing such data works well.

B.4.2. The Registry pattern

The Registry pattern allows us to take any object and treat it as a Singleton, and it allows us to centrally manage these objects. The biggest improvement over the Singleton is that it is possible to have two instances of an object if required.

A typical scenario is a database connection object. Usually, you connect to one database for the duration of the request, so it’s tempting to make the object a Singleton. However, you may need to connect to a second database every so often (for export or import, for example) and for those times you need two instances of the database connection object. A Singleton won’t allow this, but using a Registry will give you the benefits of the easily accessible object without the drawbacks.

This problem is so common that Zend Framework provides the Zend_Registry class, which implements the Registry pattern. For simple access, there are two main functions that form the basic API of Zend_Registry, and their usage is shown in listings B.15 and B.16.

Listing B.15. Storing an object to the Zend_Registry

To store an object into the registry, the set() method is used. Predictably, the get() method is used to retrieve an object at another place in the code.

Listing B.16. Retrieving an object from the Zend_Registry

The only complication to be aware of with Zend_Registry is that you must ensure that you provide a unique key when you store the object into the registry; otherwise you’ll overwrite the previously stored object of the same name. Other than that, it’s easier to use a Registry than modifying a class to make it a Singleton.

The key components of the implementation are shown in listing B.17. Let’s examine it.

Listing B.17. The Registry pattern as implemented by Zend_Registry

Internally, Zend_Registry uses a Singleton-like method, getInstance(), to retrieve an instance of itself . This is used in both get() and set(), and it ensures that both methods are using the same registry when setting and retrieving items. Zend_Registry extends ArrayObject, which means that you can treat an instance of it as if it were an array and so iterate over it or directly set and get elements as if they were array keys. ArrayObject is provided by the SPL and implements the methods required by the ArrayAccess interface, including offsetGet(), offsetSet(), and offsetExists(). This saves having to write these methods yourself, though they can be overridden if custom functionality is required. The static methods set() and get() need to perform the same actions, so set() calls offsetSet() to set an item into the registry , and offsetGet() is used to retrieve an item from the registry within get() .

Note that by using offsetGet() and offsetSet(), the code is oblivious of the underlying storage mechanism used to hold the items. This future-proofing allows for the storage mechanism to be changed if required, and this code won’t need to be updated.

We’ve looked at two of the common design patterns used in web applications and, as noted earlier, there are many others. Zend Framework implements more too, and so is a good code base for studying them. These are some of the other patterns in use:

  • MVC in the Zend_Controller family of functions
  • Table Data Gateway in Zend_Db_Table
  • Row Data Gateway in Zend_Db_Table_Row
  • Strategy in Zend_Layout_Controller_Action_Helper_Layout
  • Observer in Zend_XmlRpc_Server_Fault

There are a lot of patterns out there, and we recommend you read Patterns of Enterprise Application Architecture by Martin Fowler and php|architect’s Guide to PHP Design Patterns by Jason Sweat for further information on many of the common Web-oriented patterns. Also, PHP in Action, by Reiersøl, Baker, and Shiflett, has a very accessible introduction to the finer points of using design patterns with PHP.

B.5. Summary

Zend Framework is a modern PHP5 framework and, as such, takes advantage of all the new goodies that PHP5 provides. Understanding of the key features of PHP5’s object model is vital for writing applications with Zend framework. Every part of the Framework is written in Object Oriented PHP with careful use of the SPL interfaces such as ArrayAccess, its sibling ArrayObject, Countable, and Iterator. The SPL can be used to provide interfaces to classes that make them behave more like native PHP arrays. Software design patterns are an important part of modern web development and we’ve covered what they are and looked at how Zend Framework uses the Singleton and Registry patterns.

This chapter has provided an overview and for more information. We strongly recommend looking at other books that cover these topics in depth. Having said that, armed with the knowledge gleaned here, you’re well placed to dive head first into Zend Framework and the main chapters of this book.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.128.199.210