Team LiB
Previous Section Next Section

Creating Basic Classes

Although we call this style of programming object-oriented programming, the great majority of all the programming is done developing classes. A class can be thought of as a blueprint of an object and defines all the actions that object is capable of. Class definitions thus contain variables, functions (called methods), and even constants that are specific to that class or its instances only. In PHP 4, a basic class was defined as shown in Listing 13.1:

Listing 13.1. A Basic PHP 4 Class
<?php
    class myPHP4Class {
        var $my_variable;
        function my_method($param) {
            echo "Hello, you called my_method($param)!\n";
            echo "The value of my variable is: ";
            echo "{$this->my_variable}\n";
        }
    }
?>

After a class is defined, an instance of that class can be created. A instantiated class is an object and represents a working copy of the previously-defined class. To create an instance of the myPHP4Class class, the new statement is used:

<?php
     include_once ("myPHP4Class_def.php");
     $myinstance = new myPHP4Class();
     $anotherinstance = new myPHP4Class();
?>

In this case, the $myinstance and $anotherinstance variables both represent objects of type myPHP4Class. Although they were created from the same class definition, they are completely independent of each other.

After an instance of a class has been created, the properties and methods defined by that class may be accessed for that instance by using the -> operator. For instance, continuing from the previous example, Listing 13.2 sets the $my_variable property of each instance:

Listing 13.2. Basic Object Accessing
<?php
     $myinstance = new myPHP4Class();
     $anotherinstance = new myPHP4class();
     $myinstance->my_variable = 10;
     $anotherinstance->my_variable = 20;
     $myinstance->my_method("MyParam");
?>

When executed, the $my_variable property of the $myinstance object will be set to 10, and the $anotherinstance $my_variable property will be set to 20. Because this script calls the my_method() method of the class, the following output will be generated as well:

Hello, you called my_method(MyParam)!
The value of my variable is 10

NOTE

$this is a special variable within a class that represents the instance of the object itself. It is used to access both methods and properties internally within the object.


Private, Protected, and Public

In PHP 5, defining and using classes have not changed a great deal. In fact, it is no mistake that the code in Listing 13.1 will still work as expected in PHP 5. This is, however, a deprecated method of defining a class. The new method of defining the preceding class is shown in Listing 13.3:

Listing 13.3. A Basic PHP 5 Class
<?php
     class myPHP5Class {
          public $my_variable;
          public function my_method($param) {
               echo "Hello, you called my_method($param)!\n";
               echo "The value of my variable is: ";
               echo "{$this->my_variable}\n";
          }
     }
?>

This difference brings us to an important new feature in the OO model of PHP 5access controls.

In PHP 4 there was no concept of access control within objects. As an outside developer using the myPHP4Class class, there is nothing stopping me from changing or reading the value of the $my_variable variable. In PHP 5, however, the object model now provides three access levels for class members, which restrict what data can be accessed from where in your scripts. These three access methods are public, private, and protected and can be applied both to methods and properties of the class as shown in Listing 13.3.

NOTE

Not only does PHP provide private, public, and protected when defining class members, but it is also deprecated not to specify an access level in PHP 5.


Class members that are declared public can be accessed from anywhere within a script. They can be called or modified internally by the object or from outside the object. This is not true for class members declared using private, which will allow access to the class member only from within an instance of that class through the $this variable. In Listing 13.4, consider the changes to the example in Listing 13.3:

Listing 13.4. Using private and public in Classes
<?php
     class myPHP5Class {
          private $my_variable;
          public function my_method($param) {
               echo "Hello, you called my_method($param)!\n";
               echo "The value of my variable is: ";
               echo "{$this->my_variable}\n";
          }
     }
?>

When an instance of myPHP5Class is created, attempting to access the $my_variable property from outside the object will cause an error in PHP:

<?php
     $myobject = new myPHP5Class();
     /* This is allowed, as my_method is declared public */
     $myobject->my_method("MyParam");
     /* This will cause an error, $my_variable is private */
     $myobject->my_variable = 10;
?>

When the preceding code is executed, the following error will occur:

Fatal Error: Cannot access private property myPHP5Class::my_variable in ....

The third and final access level PHP provides is the protected level. This level is similar to private, as it prevents access to the class member externally. However, unlike private, which restricts access to only the specific class where it is defined, protected allows access from both itself and any child classes. For more information regarding child classes and inheritance, see "Class Inheritance" later in this chapter.

Type Hinting

Another improvement in PHP 5 to the object model is the concept of type hinting. PHP is, by design, a typeless language. That is, variables are not restricted to what data type they can contain. In fact, the same variable can at one point be treated as an integer and the next as a string. However, because methods within objects often accept parameters that are instances of other objects, PHP 5 allows you to restrict the data types of method parameters. Consider the example in Listing 13.5:

Listing 13.5. Type Hinting in PHP5
<?php
     class Integer {
          private $number;
          public function getInt() {
               return (int)$this->number;
          }
          public function setInt($num) {
               $this->number = (int)$num;
          }
     }
     class Float {
          private $number;
          public function getFloat() {
               return (float)$this->number;
          }
          public function setFloat($num) {
               $this->number = (float)$num;
          }
     }
?>

Listing 13.5 defines two classes, Integer and Float, which implement a simple wrapper for these data types within PHP. What if a class needed to be implemented that added only two floats together? With our current knowledge, the following would be our solution:

<?php
     class Math {
          public function add($op1, $op2) {
               return $op1->getFloat() + $op2->getFloat();
          }
     }
?>

However, because of the nature of PHP, there are no assurances that the $op1 and $op2 parameters will be instances of the Float class. Even if we are sure that they are objects, there is no convenient way to tell if they are of the correct type. One possible solution is to use the new instanceof operator in PHP5, which returns a Boolean value indicating whether a variable is an instance of a particular class (see Listing 13.6):

Listing 13.6. Using the instanceof Operator
<?php
     class Math {
          public function add($op1, $op2) {
               if(($op1 instanceof Float) &&
                  ($op2 instanceof Float)) {
                    return $op1->getFloat() + $op2->getFloat();
               } else {
                    echo "Must pass two Floats!\n";
               }
          }
     }
?>

In Listing 13.6, our Math class now has a method of ensuring that the parameters that were passed are objects of the correct type. However, such a technique can easily make your code bug prone and confusing to read. A much better approach, which accomplishes the same goal, is to specify the exact type you need in the function prototype, as shown in Listing 13.7:

Listing 13.7. Using Object Type Hinting in PHP5
<?php
     class Math {
          public function add(Float $op1, Float $op2) {
               return $op1->getFloat() + $op2->getFloat();
          }
     }
?>

In this fashion, we are able to cleanly identify the specific type required by the add() method and can safely assume the getFloat() method will exist. As you will see later, type hinting can become very useful when used in conjunction with something called Interfaces (see "Interfaces" later in the chapter).

Cloning

In PHP4, objects were not represented by reference. That is, when an object was passed to a function or method call, a copy was made of that object. Not only was this a real hassle, but it could also result in some very difficult to track down bugs. Because a copy of the object was made, any modifications to that object instance made within a method or function affected only the copy within the function. This counterintuitive behavior has been corrected in PHP5; now all objects are represented by reference. Although an important change, this means that making direct copies of an instance of an object is no longer possible:

<?php
     $class_one = new MyClass();
     $class_one_copy = $class_one;
?>

In this example, you would expect that $class_one_copy would indeed be an independent instance of the MyClass class with all the traits of the $class_one instance. Although this would have been true in PHP 4, in PHP 5 both $class_one and $class_one_copy represent the same objectany modifications made to either instance will cause the same change in the other. In PHP 5, rather than directly assigning an instance of an object to a new variable, the clone statement must be used. This statement returns a new instance of the current object, copying the values of any member properties into it and thus creating an independent clone.

Therefore, in PHP 5 the code shown in Listing 13.8 would be used to create an independent copy of an instantiated object:

Listing 13.8. Using the clone Statement
<?php
     $class_one = new MyClass();
     $class_one_copy = clone $class_one;
?>

When you use the clone statement, by default an exact copy of the object being cloned is returned. However, a class can also implement the special __clone() method, which enables you to control what elements will be copied from one instance to another. In this special method, the $this variable references the new copy of the object, complete with all the values from the original. See Listing 13.9 below for using the __clone() method to control the values of the cloned object:

Listing 13.9. Using the __clone() Method
<?php
     class myObject {
          public $var_one = 10;
          public $var_two = 20;
          function __clone() {
               /* Set $var_two in the clone to 0 */
                    $this->var_two = 0;
          }
     }
     $inst_one = new myObject();
     $inst_two = clone $inst_one;
     var_dump($inst_one);
     var_dump($inst_two);
?>

In this example, the output will be as follows:

object(myObject)#1 (2) {
  ["var_one"]=>
  int(10)
  ["var_two"]=>
  int(20)
}
object(myObject)#2 (2) {
  ["var_one"]=>
  int(10)
  ["var_two"]=>
  int(0)
}

The __clone() method can be useful in any number of situations, the most likely is when the object being cloned contains information that is truly specific to that instance alone (such as a unique object identifier). In these cases, __clone() can be used to copy only that information that makes sense to copy.

Constructors and Destructors

Constructors and destructors are functions that are called when a new instance of an object is created (constructors) and/or destroyed (destructors). Their primary purpose is to allow for a means to initialize and clean up after an object during its use. In PHP 4, only constructors were available and were created by defining a function whose name was the same as the class itself:

<?php
     class SimpleClass {
          function SimpleClass($param) {
               echo "Created a new instance of SimpleClass!";
          }
     }
     $myinstance = new SimpleClass;
?>

In PHP 5, this concept has been improved considerably. To begin, PHP 5 now uses a unified constructor function named __construct(). PHP 5 also uses a unified __destruct() method for its destructors. Thus, in PHP 5 a reimplementation of the preceding SimpleClass example would look something like the code shown in Listing 13.10:

Listing 13.10. Using Unified Constructors and Destructors
<?php
     class SimpleClass {
          function __construct($param) {
               echo "Created a new instance of SimpleClass!";
          }
          function __destruct() {
               echo "Destroyed this instance of SimpleClass";
          }
     }
     $myinstance = new SimpleClass("value");
     unset($myinstance);
?>

Although constructors are intuitively useful for initializing class properties, the combination of constructors and destructors is equally useful in other ways. One classic example is a class to access a database back end, where a constructor could make the connection to the database while the destructor closes it.

Class Constants

Class constants are a new feature to PHP 5 that provide, as the name implies, a facility to define constant values within a class definition. To define a constant within a class, the const keyword is used, followed by the name of the constant to define and its value, as shown in Listing 13.11:

Listing 13.11. Using Class Constants in PHP5
<?php
     class ConstExample {
          private $myvar;
          public $readme;
          const MY_CONSTANT = 10;

          public function showConstant() {
               echo "The value is: ".MY_CONSTANT;
          }
     }
     $inst = new ConstExample;
     $inst->showConstant();
     echo "The value: ".ConstExample::MY_CONSTANT;
?>

Listing 13.11 illustrates the use of class constants in both the class itself and from outside the class. In this example, a single constant MY_CONSTANT has been defined in the class with an integer value of 10. This constant can be accessed anywhere within the class itself directly, just as with any constant created using the define() function. However, to access the constant from outside the class, it must be referenced along with the class name that defines the constant in <CLASSNAME>::<CONSTANT> format. Class constants, like all other class members, are inherited from parent classes and can be overridden by child classes (for more information on inheritance, see "Class Inheritance" later in this chapter).

Static Methods

Static methods are methods that are part of a class, but that are designed to be called from outside the context of the instance of an object. They behave identically to normal methods in a class in every way minus one significant detailstatic methods cannot use the $this variable to reference the current instance of the object.

To create a static method, add the static keyword in front of any class method declaration:

static function myMethod() {
...

Because static methods are not associated with a particular instance of an object, they can be called from outside the context of an instantiated object. To call a static method in this fashion, the syntax is as follows:

<CLASSNAME>::<METHOD>

<CLASSNAME> represents the class where the static method resides and <METHOD> is the method to call. Note that static methods can also be called from the context of an instantiated object as well; however, they still do not have access to the $this instance variable.

Class Inheritance

In both PHP 4 and PHP 5, object-oriented programming is designed around a single-inheritance model. Inheritance is, by definition, the capability for one class definition to extend another class definition's functionality. When one class inherits from another, all of the parent's methods, properties, and constants are available from the child class as well. Furthermore, child classes can also reimplement some or all of the methods, properties, and constants of a parent class to provide additional or different functionality. Classes are inherited from one another using the extends keyword in the class definition. Consider the example in Listing 13.12:

Listing 13.12. Class Inheritance
<?php
     class ParentClass {
          public $parentvar;
          public function parentOne() {
               echo "Called parentOne()\n";
          }
          private function parentTwo() {
               echo "Called parentTwo()!\n";
          }
     }

     class ChildClass extends ParentClass {
          public function childOne() {
               echo "Called childOne()!\n";
          }

          /* No need to define the parentOne() method, it was
             already inherited from the ParentClass class */

     }

     $v = new ChildClass();
     $v->parentOne();
?>

In this example I have defined two classes: ParentClass and ChildClass (which extends ParentClass). Although each of these classes implements its own unique set of functions, because Childclass extends the ParentClass class it includes all the properties and methods that it has access to as well. This is where the access-level concepts of private, public, and protected come into play the most. When inheriting methods and properties from another class, only those class members declared public or protected will be available within the child class.

NOTE

When I first introduced public, private, and protected, I omitted describing protected in any great detail. As you can see, the reason is that to understand protected you must first understand inheritance. When a class member is declared as protected, it is only available within the context of the class itself or any child classes that inherit from it.


When considering inheritance, it is also important to understand how class members are bound. Consider Listing 13.13:

Listing 13.13. Class Member Overloading
<?php
     class ParentClass {
          public function callMe() {
               echo "Parent called!\n";
          }
     }
     class ChildClass extends ParentClass {
          public function callMe() {
               echo "Child called!\n";
          }
     }
     $child = new ChildClass;
     $child->callMe();
?>

When the preceding script is executed, what will happen? You have already learned that child classes will inherit class members from a parent, but what if the child also implements the same class member itself? In situations such as when calling the callMe() method in Listing 13.13, the method which is actually executed is the one found in the ChildClass version. This principle of preferring the "local" class member is a critical part of OO programming; in fact, it also applies to parent classes as well. Consider the example in Listing 13.14:

Listing 13.14. Class Member Binding in PHP
<?php
     class ParentClass {
          public function callMe() {
               $this->anotherCall();
          }
          public function anotherCall() {
               echo "Parent called!\n";
          }
     }
     class ChildClass extends ParentClass {

          public function anotherCall() {
               echo "Child called!\n";
          }
     }
     $child = new ChildClass;
     $child->callMe();
?>

In this example, when the callMe() method is called, what will happen? Because the callMe() method is not defined in the ChildClass class, the one from ParentClass will be used. Looking at the callMe() method in ParentClass, note that it calls a second method as well: anotherCall(). Although PHP is executing the callMe() method from the ParentClass class, when executed it will be the ChildClass anotherCall() function that is executed, even though it also exists in the ParentClass class. The reason is that in PHP (as is the case with most languages support OOP), $this will always reference the instance of the class that called the method of the class (in this case ChildClass), regardless of where the code resides.

    Team LiB
    Previous Section Next Section