Zend PHP 5 Certification Study Guide (2014)

Object-Oriented Programming in PHP

With the advent of PHP 5, object orientation was subject to significant and far-reaching changes, but that was more than a decade ago now, and PHP hasn’t stood still. With the introduction of new features in each subsequent release, PHP’s object model has become more robust and feature-rich.

OOP Fundamentals

While the goal of this chapter is not to provide a guide to the concepts of object-oriented programming, it’s a good idea to take a quick look at some of the fundamentals.

OOP is based on the concept of grouping code and data together in logical units called classes. This process is usually referred to as encapsulation, or information hiding, since its goal is to divide an application into separate entities whose internal components can change without altering their external interfaces.

Thus, classes are essentially a representation of a set of functions (also called methods) and variables (called properties) designed to work together and to provide a specific interface to the outside world. It is important to understand that classes are just blueprints that cannot be used directly—they must be instantiated into objects, which can then interact with the rest of the application. You can think of classes as the blueprints for building a car, while objects are, in fact, the cars themselves as they come off the production line. Just like a single set of blueprints can be used to produce an arbitrary number of cars, an individual class can normally be instantiated into an arbitrary number of objects.

Declaring a Class

The basic declaration of a class is very simple:

class myClass

{

    // Class contents go here

}

As you have probably guessed, this advises the PHP interpreter that you are declaring a class called myClass whose contents will normally be a combination of constants, variables, and functions or methods.

Instantiating an Object

Once you have declared a class, you need to instantiate it in order to take advantage of the functionality it offers. This is done by using the new construct:

$myClassInstance = new myClass();

It is also possible to dynamically instantiate a class:

$className = "myClass";

$myClassInstance = new $className();

Duplicating Objects

Since PHP 5.0, objects are treated differently from other types of variables. An object is always passed by reference (in reality, it is passed by handle, but for all practical purposes there is no difference), rather than by value. For example:

$myClassInstance = new myClass();

$copyInstance = $myClassInstance;

In this case, both $myInstance and $copyInstance will reference the same object, even though we didn’t specify that we wanted this to happen by means of any special syntax.

If you wish to create an actual second copy of an object, you can use the clone operator:

$myClassInstance = new myClass();

$cloneInstance = clone $myClassInstance;

When you do this, PHP will create an exact duplicate of the object $myClassInstance and assign it to the $cloneInstance variable. This behavior can be modified if the __clone() magic method is defined for the class (see the section on magic methods later in this chapter for more details).

Class Inheritance

One of the key fundamental concepts of OOP is inheritance. This allows a class to extend another class, essentially adding new methods and properties, as well as overriding existing ones as needed. For example:

Listing 9.1: Class inheritance

class a

{

    function test() {

        echo __METHOD__ . " called\n";

    }

    function func() {

        echo __METHOD__ . " called\n";

    }

}

class b extends a

{

    function test() {

        echo __METHOD__ . " called\n";

    }

}

class c extends b

{

    function test() {

        parent::test();

    }

}

class d extends c

{

    function test() {

        b::test();

    }

}

$a = new a();

$b = new b();

$c = new c();

$d = new d();

$a->test(); // Outputs "a::test called"

$b->test(); // Outputs "b::test called"

$b->func(); // Outputs "a::func called"

$c->test(); // Outputs "b::test called"

$d->test(); // Outputs "b::test called"

In this script, we start by declaring a class called a. We then declare the class b, which extends a. As you can see, this class also has a test() method, which overrides the one declared in a, thus outputting b::test called. Note, however, that we can still access a’s other methods, so that calling $b->func() effectively executes the function in the a class. In this way, b has inherited the methods of its parent a.

Naturally, extending objects in this fashion would be very limiting, since you would only be able to override the functionality provided by parent classes, without any opportunity for reuse (unless you implement your methods using different names). Luckily, parent classes can be accessed using the special parent:: identifier, as we did for class c above. You can also access any other ancestor classes by addressing their methods by name—like we did, for example, in class d.

Class Methods and Properties

As we mentioned earlier, classes can contain both methods and variables (properties). Methods are declared within a class just like traditional functions:

class myClass

{

    function myFunction() {

        echo "You called myClass::myFunction";

    }

}

From outside the scope of a class, its methods are called using the indirection operator ->:

$obj = new myClass();

$obj->myFunction();

Naturally, the $obj variable is only valid within the scope of our small snippet of code above, which leaves us with a dilemma: how do you reference a class method from within the class itself? Here’s an example:

Listing 9.2: How to reference withing a class?

class myClass

{

    function myFunction() {

        echo "You called myClass::myFunction";

    }

    function callMyFunction() {

        // ???

    }

}

Clearly, callMyFunction() needs a way to call myFunction() from within the object’s scope. In order to enable this to take place, PHP defines a special variable called this. This variable is only defined within an object’s scope, and always points to the object itself:

Listing 9.3: Using $this

class myClass

{

    function myFunction($data) {

        echo "The value is $data";

    }

    function callMyFunction($data) {

        // Call myFunction()

        $this->myFunction($data);

    }

}

$obj = new myClass();

$obj->callMyFunction(123);

This will output The value is 123.

It is also possible to call methods dynamically, using the $obj->$var() or $obj->{expression}() syntax:

$method = 'callMyFunction';

$obj->$method(123);

// or

$obj->{$method}(123);

When using the curly brace syntax, you can use any valid expression, for example:

$method = 'callMy';

$obj->{$method . 'Function'}(123);

From PHP 5.6, you can also use instantiation time access, (new myClass)->callMyFunction(123) but this means the object is temporary, and not reusable. It isn’t recommended.

Constructors

PHP 5.0 introduced the concept of the unified constructor and, along with it, a new destructor for objects. The constructor and destructor are special class methods that are called, as their names suggest, on object creation and destruction, respectively. Constructors are useful for initializing an object’s properties or for performing start-up procedures, such as connecting to a database or opening a remote file.

The concept of the constructor is, of course, not new to PHP. In PHP 4, it was possible to define a class method whose name was the same as the class itself; PHP would then consider this method to be the class’s constructor and call it whenever a new instance of the class was created. This approach had several drawbacks. For example, if you decided to rename your class, you would also have to rename your constructor.

To avoid these problems, PHP now uses the magic __construct() method as the constructor for any class regardless of the class’ name. This greatly simplify things, and provides you with a standard mechanism to recognize and invoke constructors in a consistent manner:

Listing 9.4: Class constructors

class foo

{

    function __construct() {

         echo __METHOD__;

    }

    function foo() {

        // PHP 4 style constructor

    }

}

new foo();

This example will display foo::__construct (the __METHOD__ constant is replaced at compilation time with the name of the current class method). Note that, if the __construct() method is not found, PHP will look for the old PHP 4-style constructor (foo) and call that instead.

As of PHP 5.3.3, the old style constructors are not called for namespaced classes.

Destructors

In addition to the __construct() method, we also have a __destruct() method. This works like a mirror image of __construct(): it is called right before an object is destroyed, and is useful for performing cleanup procedures such as disconnecting from a remote resource or deleting temporary files:

Listing 9.5: Class destructors

class foo

{

    function __construct() {

        echo __METHOD__ . PHP_EOL;

    }

    function __destruct() {

        echo __METHOD__;

    }

}

new foo();

This code will display:

foo::__construct

foo::__destruct

The predefine PHP_EOL constant represents the correct End Of Line symbol for the platform running your code.

Destruction occurs when all references to an object are gone, and this may not necessarily take place when you expect it, or even when you want it to. In fact, while you can unset() a variable that references an object, or overwrite it with another value, the object itself may not be destroyed right away because a reference to it is held elsewhere. For example, in the following script the destructor is not called when calling unset(), because $b still references the object:

$a = new foo();

$b = $a;

unset($a);

Even if an object still has one or more active references, the __destruct() method is called at the end of script execution and, therefore, you are guaranteed that at some point your destructor will be executed. However, there is no way to determine the order in which any two objects in your scripts will be destroyed. This can sometimes cause problems when an object depends on another to perform one or more functions. For example, if one of your classes encapsulates a database connection and another class needs that connection to flush its data to the database, you should not rely on your destructors to perform a transparent flush to the database when the object is deleted: the instance of the first class that provides database connectivity could, in fact, be destroyed before the second, thus making it impossible for the latter to save its data to the database.

Magic Methods

In addition to __construct() and __destruct(), several other magic methods were added to allow you to hook into certain operations performed on the class, or instances of the class in which they are defined.

Magic Method

Description

__get()

Handle retrieval of nonexistent or inaccessible properties

__set()

Handle setting of nonexistent or inaccessible properties

__isset()

Handle calls to isset() on nonexistent or inaccessible properies. Since PHP 5.1

__empty()

Handle calls to empty() on nonexistent or inaccessible properties. Since PHP 5.1

__call()

Handle calls to nonexistent or inaccessible object methods

__callStatic()

Handle calls to nonexistent or inaccessible static methods

__invoke()

Called when an object is used as a function (e.g. $object()). Since 5.3

__clone()

Intercept cloning of the object

__sleep()

Intercept serializing of the object

__wakeup()

Intercept unserializing of the object

__set_start()

(Static) Intercept output and re-initalizing via var_export(). Since 5.1

__toString()

Called when the object is converted to a string (e.g. using echo or cast via (string)).

__debugInfo()

Intercept the output for var_dump()

Magic methods allow you to do complex things, such as transforming data between structures using property overloading, or creating dynamic APIs using method overloading.

However, they are called magic methods for a reason. The use of these methods injects a certain amount of uncertainty in to the way your object interacts, and in particular can stop IDEs from being able to perform auto-completion.

Visibility

PHP 5 adds the notion of object method and property visibility (often referred to as “PPP”), which enables you to determine the scope from which each component of your class interfaces can be accessed.

There are four levels of visibility:

Visibility

Description

public

The resource can be accessed from any scope.

protected

The resource can only be accessed from within the class where it is defined and its descendants.

private

The resource can only be accessed from within the class where it is defined.

final

The resource is accessible from any scope, but cannot be overridden in descendant classes.

The final visibility level only applies to methods and classes. Classes that are declared as final cannot be extended.

Typically, you should make all API methods and properties public, since you will want them to be accessible from outside of your objects, while you should keep those used for internal operation as helpers to the API calls protected or private. Constructors and destructors—along with all other magic methods (see below)—should be declared as public. In fact, declaring many of the magic methods, like __destruct, __get, and set, as private will result in a warning error and the method will not be called. There are, however, times when you wish to make the constructor private—for example, when using certain design patterns like Singleton or Factory.

Listing 9.6: Visibility example

class foo

{

    public $foo = 'bar';

    protected $baz = 'bat';

    private $qux = 'bingo';

    function __construct() {

        var_dump(get_object_vars($this));

    }

}

class bar extends foo

{

    function __construct() {

        var_dump(get_object_vars($this));

    }

}

class baz

{

    function __construct() {

        $foo = new foo();

        var_dump(get_object_vars($foo));

    }

}

new foo();

new bar();

new baz();

The example above creates three classes:

·        foo,

·        bar, which extends foo and has access to all of the public and protected properties of foo,

·        baz, which creates a new instance of foo and can only access its public properties.

The output will look like this:

// Output from "foo" itself:

array(3) {

  ["foo"]=>

  string(3) "bar"

  ["baz"]=>

  string(3) "bat"

  ["qux"]=>

  string(5) "bingo"

}

// Output from sub-class "bar":

array(2) {

  ["foo"]=>

  string(3) "bar"

  ["baz"]=>

  string(3) "bat"

}

// Output from stand-alone class "baz":

array(1) {

  ["foo"]=>

  string(3) "bar"

}

Declaring and Accessing Properties

Properties are declared in PHP using one of the PPP operators, followed by their name:

Listing 9.7: Using class properties

class foo

{

    public $bar;

    protected $baz;

    private $bas;

    public $var1 = "Test"; // String

    public $var2 = 1.23; // Numeric value

    public $var3 = array(1, 2, 3);

}

Note that, like a normal variable, a class property can be initialized while it is being declared. However, the initialization is limited to assigning values (but not by evaluating expressions). You can’t, for example, initialize a variable by calling a function.

// this will not work

class foo

{

    public $created = time();

}

That’s something you can only do within one of the class’s methods (typically, the constructor).

Listing 9.8: Initializing properties with functions

class foo

{

    public $created;

    public function __construct() {

        $this->created = time();

    }

}

As of PHP 5.6, you can use contents scalar expressions, discussed in the PHP Basics Chapter, to initialize a property.

Constants, Static Methods and Properties

Along with PPP, PHP 5 also implements static methods and properties. Unlike regular methods and properties, their static counterparts exist and are accessible as part of a class itself, as opposed to existing only within the scope of one of its instances. This allows you to treat classes as true containers of interrelated functions and data elements, which, in turn, is a very handy expedient to avoid naming conflicts.

While PHP 4 allowed you to call any method of a class statically using the scope resolution operator :: (officially known as Paamayim Nekudotayim—Hebrew for “Double Colon”), PHP 5 introduces a stricter syntax that calls for the use of the static keyword to convey the use of properties and methods as such.

PHP is very strict about the use of static properties; calling static properties using object notation (i.e. $obj->property) will result in both a “strict standards” message and a notice. This is not the case with static methods, however calling a non-static method statically will also emit a “strict standards” message.

Listing 9.9: Static properties

class foo

{

    static $bar = "bat";

    public static function baz() {

        echo "Hello World";

    }

}

$foo = new foo();

$foo->baz();

echo $foo->bar;

This example will display:

Hello WorldPHP Strict Standards:  Accessing static property

   foo::$bar as non static in PHPDocument1 on line 17

Strict Standards: Accessing static property foo::$bar as

   non static in PHPDocument1 on line 1

The correct way to call static methods and properties is like so:

foo::baz();

echo foo::$bar;

In addition to declaring a method static, you can set the visibility with the regular methods, using public, protected, and private.

The order of the static keyword and a visibility keyword does not matter. If no visibility definition is declared, the static method or property is considered public.

Dynamic Calling

It is possible to use variable variables to access static methods:

$var = 'bar';

echo foo::$$var;

PHP 5.3 added the ability to use a variable class name for static access:

$className = 'foo';

$className::baz();

echo $className::$bar;

Additionally, PHP 5.4 added support for dynamic static method access using the ClassName::$var or ClassName::{expression}() syntax:

$method = 'baz';

foo::$method();

// or

foo::{$method}();

As with dynamic object method calls, you can use any expression inside the curly braces.

Self and Late Static Binding

In addition to the parent:: identifier for calling parent methods, which works the same way as in the object context, there is also self:: which is often used similarly to $this in static contexts, although it doesn’t quite work the same way.

Unlike $this, which refers to the current instance and has the scope of the class that was instantiated, self::—like the magic constant __CLASS__—refers to the class in which the call is defined. This means that if you have a parent class with a method that references self:: to call a method that is overridden in a child class, when you call the method inherited from the parent, it will call the parent’s version of the method, not the child’s, and in the parent’s context, meaning that visibility has no impact.

Listing 9.10: Static binding using self keyword

class a

{

    public static function test() {

        self::foo();

        self::bar();

    }

    public static function foo() {

        echo __METHOD__ . " called\n";

    }

    private static function bar() {

        echo __METHOD__ . " called\n";

    }

}

class b extends a { }

class c extends a

{

    public static function foo() {

        echo __METHOD__ . " called\n";

    }

    private static function bar() {

        echo __METHOD__ . " called\n";

    }

}

a::test();  // a::foo called

            // a::bar called

b::test();  // a::foo called

            // a::bar called

c::test();  // a::foo called

            // a::bar called

Notice how b::test(), which inherits the a class’s static methods, is still calling a::foo() and a::bar(), and c::test(), which overrides the methods in a, still calls them from the context of a.

PHP 5.3.0 introduced late static binding (also known as LSB), which will determine the current class at runtime. It has similar semantics to $this and refers to wherever the call happens, not the definition, but with the context of wherever it is defined. This is achieved by using the static::identifier:

Listing 9.11: Late static binding using static keyword

class a

{

    public static function test() {

        static::foo();

        static::bar();

    }

    public static function foo() {

        echo __METHOD__ . " called\n";

    }

    private static function bar() {

        echo __METHOD__ . " called\n";

    }

}

class b extends a { }

class c extends a

{

    public static function foo() {

        echo __METHOD__ . " called\n";

    }

    private static function bar() {

        echo __METHOD__ . " called\n";

    }

}

a::test();  // a::foo called

            // a::bar called

b::test();  // a::foo called

            // a::bar called

c::test();  // c::foo called

            // Fatal error: Call to private method

            //   c::bar() from context 'a'

This time, b::test() doesn’t change its behavior from before, as it simply inherits the methods from a. However, c::test() now shows that it is calling the overriding methods, but still from the context of a, so while we can call the public c::foo(), we get a fatal error calling the private c::bar().

Class Constants

Class constants work in the same way as regular constants, except they are scoped within a class. Class constants are public, and accessible from all scopes. For example, the following script will output Hello World:

class foo

{

    const BAR = "Hello World";

}

echo foo::BAR;

Note that class constants suffer from the same limitations as regular constants and therefore can only contain scalar values.

Class constants have several advantages over traditional constants: since they are encapsulated in a class, they make for much clearer code, and they are significantly faster than those declared with the define() construct.

Interfaces and Abstract Classes

Interfaces and abstract classes are yet another new feature added to PHP 5. Both are used to create a series of constraints on the base design of a group of classes. An abstract class essentially defines the basic skeleton of a specific type of encapsulated entity. For example, you can use an abstract class to define the basic concept of “car” as having two doors, a lock, and a method that locks or unlocks the doors. Abstract classes cannot be used directly; they must be extended so that the descendent class provides a full complement of methods. For example:

Listing 9.12: Using abstract classes

abstract class DataStore_Adapter

{

    private $id;

    abstract function insert();

    abstract function update();

    public function save() {

        if (!is_null($this->id)) {

            $this->update();

        } else {

            $this->insert();

        }

    }

}

class PDO_DataStore_Adapter extends DataStore_Adapter

{

    public __construct($dsn) {

        // ...

    }

    function insert() {

        // ...

    }

    function update() {

        // ...

    }

}

You must declare a class as abstract so long as it has (or inherits without providing a body) at least one abstract method.

As you can see, in this example we define a class called DataStore_Adapter and declare two abstract methods called insert() and update(). Note that these methods don’t actually have a body—that’s one of the requirements of abstract classes—and that the class itself must be declared as abstract in order for the compiler to satisfy the parser’s syntactic requirements. We then extend DataStore_Adapter into PDO_DataStore_Adapter, which is no longer abstract because we have now provided implementations for both insert() and update().

Interfaces

Interfaces, on the other hand, are used to specify an API that a class must implement. This allows you to create a common contract that your classes must implement in order to satisfy certain logical requirements. For example, you could use interfaces to abstract the concept of database provider into a common API that could then be implemented by a series of classes that interface to different DBMSs.

Interface methods contain no body:

Listing 9.13: Defining interfaces

interface DataStore_Adapter {

    public function insert();

    public function update();

    public function save();

    public function newRecord($name = null);

}

class PDO_DataStore_Adapter implements DataStore_Adapter

{

    public function insert() {

        // ...

    }

    public function update() {

        // ...

    }

    public function save() {

        // ...

    }

    public function newRecord($name = null) {

    }

}

In the example above, if you fail to define all of the methods for a particular interface, or all of the arguments for any given interface method, you will see something like this:

Fatal error:  Class PDO_DataStore_Adapter contains 1

  abstract method and must therefore be declared abstract or

  implement the remaining methods (DataStore_Adapter::save)

  in *document* on line 27

or

Fatal error: Declaration of PDO_DataStore_Adapter::newRecord()

  must be compatible with that of DataStore_Adapter::newRecord()

  in *document* on line 12

It is also possible to implement more than one interface in the same class:

class PDO_DataStore_Adapter implements

   DataStore_Adapter, SeekableIterator

{

    // ...

}

In this example, we need to define the methods for both DataStore_Adapter and SeekableIterator. Additionally, a class can extend another class, as well as implement multiple interfaces at the same time:

Remember—a class can only extend one parent class, but it can implement multiple interfaces.

class PDO_DataStore_Adapter extends PDO implements

    DataStore_Adapter, SeekableIterator

{

    // ...

}

Determining an Object’s Class

It is often convenient to be able to determine whether a given object is an instance of a particular class, or whether it implements a specific interface. This can be done by using the instanceof operator:

if ($obj instanceof MyClass) {

    echo "\$obj is an instance of MyClass";

}

Naturally, instanceof allows you to inspect all of the ancestor classes of your object, as well as any interfaces.

Lazy Loading

Prior to PHP 5.0, instantiating an undefined class or using one of its methods in a static way would cause a fatal error. This meant that you needed to include all of the class files that you might need, rather than loading them as they were needed—just so that you wouldn’t forget one—or come up with complicated file inclusion mechanisms to reduce the needless processing of external files.

To solve this problem, PHP 5 features an “autoload” facility that makes it possible to implement “lazy loading”, or loading of classes on demand. When referencing a nonexistent class, be it as a type hint, static call, or attempt to instantiate an object, PHP will try to call the __autoload() global function so that the script may be given an opportunity to load it. If, after the call to autoload(), the class is still not defined, the interpreter gives up and throws a fatal error.

function __autoload($class) {

    // Require PEAR-compatible classes

    require_once str_replace("_", "/", $class . '.php');

}

$obj = new Some_Class();

When instantiating Some_Class, __autoload() is called and passed “Some_Class” as its argument. The function then replaces the underscores with forward slashes, and includes the file using require_once().

Using __autoload() is a great help when you are working with only one naming scheme; it allows lazy-loading of classes, so that classes that are never used are also never loaded. However, once you start mixing code and using different libraries (e.g.: PEAR and some legacy application) you will rapidly run into cases that __autoload() cannot handle without becoming too bulky and slow.

Luckily, the Standard PHP Library (SPL) offers a simpler solution to this problem, by allowing you to stack autoloaders on top of each other. If one fails to load a class, the next one in the chain is called, until either the class has been loaded, or no more autoloaders are part of the chain (in which case, a fatal error occurs).

Since PHP 5.3, the SPL extension is always enabled. The SPL is discussed further in the Elements of Object-Oriented Design chapter.

By default, SPL uses its own autoloader, called spl_autoload(); this built-in function checks all include paths for filenames that match the name of the class that needs loading in lowercase letters, followed by .inc, .php, or the extensions specified using a comma-separated string as the only parameter to a call to spl_autoload_extensions().

Additional autoloaders can be added to the stack by calling spl_autoload_register(). The first call to this function replaces the __autoload() call in the engine with its own implementation. This means that, if you already have a user-defined __autoload(), you will need to register it with SPL in order for it to continue working:

spl_autoload_register('spl_autoload');

if (function_exists('__autoload')) {

    spl_autoload_register('__autoload');

}

Note that spl_autoload_register() has a second argument, throw, that, when set to true, will throw an exception when it is unable to register the autoloader.

Another addition in PHP 5.3, spl_autoload_register() now accepts a third argument, prepend, which, when set to true, will prepend the autoloader on the stack.

Reflection

With the introduction of PHP’s new object model in PHP 5.0 also came the Reflection API, a collection of functions and objects that allows you to examine the contents of a script’s code, such as functions and objects, at runtime.

From PHP 5.3, the reflection extension is always enabled.

Reflection can be very handy in a number of circumstances. For example, it can be used to generate simple documentation, or to determine whether certain functionality is available to a script, and so on. Here’s an example:

Listing 9.14: Using reflection

/**

 * Say Hello

 *

 * @param string $to

 */

function hello($to = "World") {

    echo "Hello $to";

}

$funcs = get_defined_functions();

?>

<h1>Documentation</h1>

<?php

/**

 * Do Foo

 *

 * @param string $bar Some Bar

 * @param array $baz An Array of Baz

 */

function foo($bar, $baz = array()) { }

$funcs = get_defined_functions();

foreach ($funcs['user'] as $func) {

    try {

        $func = new ReflectionFunction($func);

    } catch (ReflectionException $e) {

        // ...

    }

    $prototype = $func->name . ' ( ';

    $args = array();

    foreach ($func->getParameters() as $param) {

        $arg = "";

        if ($param->isPassedByReference()) {

            $arg = '&';

        }

        if ($param->isOptional()) {

            $arg = '['  . $param->getName()

                 . ' = '

                 . $param->getDefaultValue() . ']';

        } else {

            $arg = $param->getName();

        }

        $args[] = $arg;

    }

    $prototype .= implode(", ", $args) . ' )';

    echo "<h2>$prototype</h2>";

    echo "

<p>

Comment:

</p>

<pre>

" . $func->getDocComment() . "

</pre>

<p>

File: " . $func->getFileName() . "

<br />

Lines: " . $func->getStartLine()

         . " - "

         . $func->getEndLine() . "

</p>";

}

This simple code runs through every single user-defined function in our script and extracts several pieces of information on it; its output will look similar to the following:

<h2>foo ( bar, [baz = Array] )</h2>

<p>

Comment:

</p>

<pre>

/**

 * Do Foo

 *

 * @param string $bar Some Bar

 * @param array $baz An Array of Baz

 */

</pre>

<p>

File: PHPDocument1

<br />

Lines: 8 - 8

</p>

If we wish to expand on this simple script so that it works for classes, we can simply use ReflectionClass and ReflectionMethod:

Listing 9.15: Using reflection with classes

/**

 * Greeting Class

 *

 * Extends a greeting to someone/thing

 */

class Greeting

{

    /**

     * Say Hello

     *

     * @param string $to

     */

    function hello($to = "World") {

        echo "Hello $to";

    }

}

$class = new ReflectionClass("Greeting");

?>

<h1>Documentation</h1>

<h2><?php echo $class->getName(); ?></h2>

<p>

Comment:

</p>

<pre>

<?php echo $class->getDocComment(); ?>

</pre>

<p>

File: <?php echo $class->getFileName(); ?>

<br />

Lines: <?php echo $class->getStartLine(); ?>

  - <?php echo $class->getEndLine(); ?>

</p>

<?php

foreach ($class->getMethods() as $method) {

    $prototype = $method->name . ' ( ';

    $args = array();

    foreach ($method->getParameters() as $param) {

        $arg = "";

        if ($param->isPassedByReference()) {

            $arg = '&';

        }

        if ($param->isOptional()) {

            $arg = '[' . $param->getName()

                 . ' = '

                 . $param->getDefaultValue() . ']';

        } else {

            $arg = $param->getName();

        }

        $args[] = $arg;

    }

    $prototype .= implode(", ", $args) . ' )';

    echo "<h3>$prototype</h3>";

    echo "

<p>

Comment:

</p>

<pre>

" . $method->getDocComment() . "

</pre>

<p>

File: " . $method->getFileName() . "

<br />

Lines: " . $method->getStartLine() . " - "

         . $method->getEndLine() . "

</p>";

}

The output for this example will look similar to the following:

<h1>Documentation</h1>

<h2>Greeting</h2>

<p>

Comment:

</p>

<pre>

/**

 * Greeting Class

 *

 * Extends a greeting to someone/thing

 */

</pre>

<p>

File: PHPDocument2<br />

Lines: 7 - 18</p>

<h3>hello ( [to = World] )</h3>

<p>

Comment:

</p>

<pre>

/**

 * Say Hello

 *

 * @param string $to

 */

</pre>

<p>

File: PHPDocument2

<br />

Lines: 13 - 17

</p>

The Reflection API is extremely powerful, since it allows you to inspect user-defined and internal functions, classes and objects, and extensions. In addition to inspecting them, you can also call functions and methods directly through the API.

Summary

PHP’s object-oriented capabilities have grown considerably from their inception in PHP 4. PHP 5’s new OOP model makes it possible to build significantly more robust and scalable applications, and provides the foundation for creating easy-to-use, encapsulated, re-useable code. While OOP is not the only programming methodology that you can use in your applications, it adds a valuable tool to your bag of tricks as a developer, and its judicious use is sure to improve your code.