Приглашаем посетить
Чарская (charskaya.lit-info.ru)

Introduction to OO Programming

Previous
Table of Contents
Next

Introduction to OO Programming

It is important to note that in procedural programming, the functions and the data are separated from one another. In OO programming, data and the functions to manipulate the data are tied together in objects. Objects contain both data (called attributes or properties) and functions to manipulate that data (called methods).

An object is defined by the class of which it is an instance. A class defines the attributes that an object has, as well as the methods it may employ. You create an object by instantiating a class. Instantiation creates a new object, initializes all its attributes, and calls its constructor, which is a function that performs any setup operations. A class constructor in PHP5 should be named __construct() so that the engine knows how to identify it. The following example creates a simple class named User, instantiates it, and calls its two methods:

<?php
class User {
  public $name;
  public $birthday;
  public function __construct($name, $birthday)
  {
    $this->name = $name;
    $this->birthday = $birthday;
  }
  public function hello()
  {
    return "Hello $this->name!\n";
  }
  public function goodbye()
  {
    return "Goodbye $this->name!\n";
  }
  public function age() {
    $ts = strtotime($this->birthday);
    if($ts === -1) {
      return "Unknown";
    }
    else {
      $diff = time() - $ts;
      return floor($diff/(24*60*60*365)) ;
    }
  }
}
$user = new User('george', '10 Oct 1973');
print $user->hello();
print "You are ".$user->age()." years old.\n";
print $user->goodbye();
?>

Running this causes the following to appear:

Hello george!
You are 29 years old.
Goodbye george!

The constructor in this example is extremely basic; it only initializes two attributes, name and birthday. The methods are also simple. Notice that $this is automatically created inside the class methods, and it represents the User object. To access a property or method, you use the -> notation.

On the surface, an object doesn't seem too different from an associative array and a collection of functions that act on it. There are some important additional properties, though, as described in the following sections:

  • Inheritance Inheritance is the ability to derive new classes from existing ones and inherit or override their attributes and methods.

  • Encapsulation Encapsulation is the ability to hide data from users of the class.

  • Special Methods As shown earlier in this section, classes allow for constructors that can perform setup work (such as initializing attributes) whenever a new object is created. They have other event callbacks that are triggered on other common events as well: on copy, on destruction, and so on.

  • Polymorphism When two classes implement the same external methods, they should be able to be used interchangeably in functions. Because fully understanding polymorphism requires a larger knowledge base than you currently have, we'll put off discussion of it until later in this chapter, in the section "Polymorphism."

Inheritance

You use inheritance when you want to create a new class that has properties or behaviors similar to those of an existing class. To provide inheritance, PHP supports the ability for a class to extend an existing class. When you extend a class, the new class inherits all the properties and methods of the parent (with a couple exceptions, as described later in this chapter). You can both add new methods and properties and override the exiting ones. An inheritance relationship is defined with the word extends. Let's extend User to make a new class representing users with administrative privileges. We will augment the class by selecting the user's password from an NDBM file and providing a comparison function to compare the user's password with the password the user supplies:

class AdminUser extends User{
  public $password;
  public function _ _construct($name, $birthday)
  {
    parent::_ _construct($name, $birthday);
    $db = dba_popen("/data/etc/auth.pw", "r", "ndbm");
    $this->password = dba_fetch($db, $name);
    dba_close($db);
  }
  public function authenticate($suppliedPassword)
  {
    if($this->password === $suppliedPassword) {
      return true;
    }
    else {
      return false;
    }
  }
}

Although it is quite short, AdminUser automatically inherits all the methods from User, so you can call hello(), goodbye(), and age(). Notice that you must manually call the constructor of the parent class as parent::_ _constructor(); PHP5 does not automatically call parent constructors. parent is as keyword that resolves to a class's parent class.

Encapsulation

Users coming from a procedural language or PHP4 might wonder what all the public stuff floating around is. Version 5 of PHP provides data-hiding capabilities with public, protected, and private data attributes and methods. These are commonly referred to as PPP (for public, protected, private) and carry the standard semantics:

  • Public A public variable or method can be accessed directly by any user of the class.

  • Protected A protected variable or method cannot be accessed by users of the class but can be accessed inside a subclass that inherits from the class.

  • Private A private variable or method can only be accessed internally from the class in which it is defined. This means that a private variable or method cannot be called from a child that extends the class.

Encapsulation allows you to define a public interface that regulates the ways in which users can interact with a class. You can refactor, or alter, methods that aren't public, without worrying about breaking code that depends on the class. You can refactor private methods with impunity. The refactoring of protected methods requires more care, to avoid breaking the classes' subclasses.

Encapsulation is not necessary in PHP (if it is omitted, methods and properties are assumed to be public), but it should be used when possible. Even in a single-programmer environment, and especially in team environments, the temptation to avoid the public interface of an object and take a shortcut by using supposedly internal methods is very high. This quickly leads to unmaintainable code, though, because instead of a simple public interface having to be consistent, all the methods in a class are unable to be refactored for fear of causing a bug in a class that uses that method. Using PPP binds you to this agreement and ensures that only public methods are used by external code, regardless of the temptation to shortcut.

Static (or Class) Attributes and Methods

In addition, methods and properties in PHP can also be declared static. A static method is bound to a class, rather than an instance of the class (a.k.a., an object). Static methods are called using the syntax ClassName::method(). Inside static methods, $this is not available.

A static property is a class variable that is associated with the class, rather than with an instance of the class. This means that when it is changed, its change is reflected in all instances of the class. Static properties are declared with the static keyword and are accessed via the syntax ClassName::$property. The following example illustrates how static properties work:

class TestClass {
  public static $counter;
}
$counter = TestClass::$counter;

If you need to access a static property inside a class, you can also use the magic keywords self and parent, which resolve to the current class and the parent of the current class, respectively. Using self and parent allows you to avoid having to explicitly reference the class by name. Here is a simple example that uses a static property to assign a unique integer ID to every instance of the class:

class TestClass {
  public static $counter = 0;
  public $id;

  public function _ _construct()
  {
    $this->id = self::$counter++;
  }
}

Special Methods

Classes in PHP reserve certain method names as special callbacks to handle certain events. You have already seen _ _construct(), which is automatically called when an object is instantiated. Five other special callbacks are used by classes: _ _get(), _ _set(), and _ _call() influence the way that class properties and methods are called, and they are covered later in this chapter. The other two are _ _destruct() and _ _clone().

_ _destruct() is the callback for object destruction. Destructors are useful for closing resources (such as file handles or database connections) that a class creates. In PHP, variables are reference counted. When a variable's reference count drops to 0, the variable is removed from the system by the garbage collector. If this variable is an object, its _ _destruct() method is called.

The following small wrapper of the PHP file utilities showcases destructors:

class IO {
  public $fh = false;
  public function _ _construct($filename, $flags)
  {
    $this->fh = fopen($filename, $flags);
  }
  public function _ _destruct()
  {
    if($this->fh) {
      fclose($this->fh);
    }
  }
  public function read($length)
  {
    if($this->fh) {
      return fread($this->fh, $length);
    }
  }
  /* ... */
}

In most cases, creating a destructor is not necessary because PHP cleans up resources at the end of a request. For long-running scripts or scripts that open a large number of files, aggressive resource cleanup is important.

In PHP4, objects are all passed by value. This meant that if you performed the following in PHP4:

$obj = new TestClass;
$copy = $obj;

you would actually create three copies of the class: one in the constructor, one during the assignment of the return value from the constructor to $copy, and one when you assign $obj to $copy. These semantics are completely different from the semantics in most other OO languages, so they have been abandoned in PHP5.

In PHP5, when you create an object, you are returned a handle to that object, which is similar in concept to a reference in C++. When you execute the preceding code under PHP5, you only create a single instance of the object; no copies are made.

To actually copy an object in PHP5, you need to use the built-in _ _clone() method. In the preceding example, to make $copy an actual copy of $obj (and not just another reference to a single object), you need to do this:

$obj = new TestClass;
$copy = $obj->_ _clone();

For some classes, the built-in deep-copy _ _clone() method may not be adequate for your needs, so PHP allows you to override it. Inside the _ _clone() method, you have $this, which is the new object with all the original object's properties already copied. For example, in the TestClass class defined previously in this chapter, if you use the default _ _clone() method, you will copy its id property. Instead, you should rewrite the class as follows:

class TestClass {
  public static $counter = 0;
  public $id;
  public $other;

  public  function _ _construct()
  {
    $this->id = self::$counter++;
  }
  public function _ _clone()
  {
    $this->id = self::$counter++;
  }
}


Previous
Table of Contents
Next