2.3 Basic Classes

To define a class, use the class keyword followed by the class name:

class Person {



}

This code creates a Person class. This is not a very exciting class, because it lacks methods or properties. Class names in PHP are case-insensitive, so you cannot define both a Person and a PERSON class.

Use this class name to instantiate a new instance of your object:

$rasmus = new Person;

Alternatively, to determine a class from an object instance, use get_class( ):

$person = new Person;

print get_class($person)

Person

Even though class names are case-insensitive, PHP 5 preserves their capitalization. This is different from PHP 4, where PHP converts the name to lowercase. In PHP 4, calling get_class( ) on an instance of the Person class produced person. PHP 5 returns the correct class name.

2.3.1 Properties

List class properties at the top of the class:

class Person {

    public $name;

}

This creates a public property named name. When a property is public, it can be read from and written to anywhere in the program:

$rasmus = new Person;

$rasmus->name = 'Rasmus Lerdorf';

Properties in PHP 4 are declared using a different syntax: var. This syntax is deprecated in favor of public, but for backward compatibility, var is still legal. The behavior of a property declared using public and var is identical.

Never use public properties. Doing so makes it easy to violate encapsulation. Always use accessor methods instead.

You don't need to predeclare a property inside the class to use it. For instance:

$rasmus = new Person;

$rasmus->email = 'rasmus@php.net';

This assigns rasmus@php.net to the email property of $rasmus. This is valid, even though email was not mentioned in the class definition.

Even though you don't have to, always predeclare your properties. First, these properties are implicitly public, so they're already bad. Second, predeclaring properties forces you to think about the best way to handle data. It also makes it easier for anyone reading the class (including yourself two months later) to see all of the class's properties without wading through the entire code of the class.

2.3.2 Methods

Define methods underneath properties. They're declared using the standard function syntax:

class Person {

    public $name;

    

    public function setName($name) {

        $this->name = $name;

    }

}

The public keyword indicates that anyone can call the setName( ) method. In PHP 4, methods are not preceded by a visibility identifier such as public. For backward compatibility, these methods are assumed to be public.

Unlike properties, public methods are okay to use. Accessor methods, for instance, are frequently declared as public.

To refer to an object instance inside of a class, use the special variable $this. For example:

    public function setName($name) {

        $this->name = $name;

    }

In this code, the setName( ) method sets the name property of the current object to the value of the $name variable passed into the method.

Be careful not to place a dollar sign before the property name, such as $this->$name. This causes PHP to access the property with the value stored in the $name variable. This is occasionally the desired result, but often is not.

PHP 4 doesn't prevent you from assigning a new object to $this:

    public function load($object) {

        $this = $object;

    }

but this is illegal in PHP 5. You can only alter object properties. Trying to set $this to a new value produces an error:

PHP Fatal error:  Cannot re-assign $this

2.3.3 Access Restrictions

To prevent a property or method from being accessed from outside a class, use the private keyword:

class Person {

    private $name;

    

    public function setName($name) {

        $this->name = $name;

    }

}



$rasmus = new Person;

$rasmus->setName('Rasmus Lerdorf');

print $rasmus->name;

PHP Fatal error:  Cannot access private property Person::$name... on line 12

When name is declared as private, it cannot be accessed outside of the class. It's still safe to manipulate it inside of setName( ) because that's an internal method. What you cannot do is something like:

print $rasmus->name;

This causes a fatal error, which is why you need to implement a getName( ) method:

class Person {

    private $name;

    

    public function setName($name) {

        $this->name = $name;

    }



    public function getName( ) {

        return $this->name;

    }

}



$rasmus = new Person;

$rasmus->setName('Rasmus Lerdorf');

print $rasmus->getName( );

Rasmus Lerdorf

This code works as expected and prints Rasmus Lerdorf.

Declare private methods by placing the word private in front of function:

class Person {

    private $email;

    

    public function setEmail($email) {

        if ($this->validateEmail($email)) {

            $this->email = $email;

        }

    }



    private function validateEmail($email) {

        // email address validation 

        // regular expression

        // omitted for clarity

    }

}



$rasmus = new Person;

$rasmus->setEmail('rasmus@php.net');

This code declares two methods, one public and one private. The public method, setEmail( ), lets you set a person's email address. The private method, validateEmail( ), is used internally by the class to check whether the address is valid. Since that method is not relevant to a user, it is declared as private.

This example also shows how to access a method from within a class. The syntax is akin to accessing a class's property. Use $this to represent the object, as is done inside setEmail( ):

    public function setEmail($email) {

        if ($this->validateEmail($email)) {

            $this->email = $email;

        }

    }

This code calls the validateEmail( ) method of the Person class and passes it the $email variable. Since this call occurs in a method defined in the same class, it works even though validateEmail( ) is declared as private.

2.3.4 Constructors and Destructors

Object constructors act the same in PHP 4 and PHP 5, but PHP 5 introduces a new naming convention. In PHP 4, an object's constructor has the same name as its class:

class Database {

    function Database($host, $user, $password) {

        $this->handle = db_connect($host, $user, $password);   

    }

}



$db = new Database('db.example.com', 'web', 'jsd6w@2d');

Creating a new instance of the Database class causes PHP to call the Database( ) method.

To designate an object constructor in PHP 5, name the method _ _construct( ):

class Database {

    function _ _construct($host, $user, $password) {

        $this->handle = db_connect($host, $user, $password);   

    }

}



$db = new Database('db.example.com', 'web', 'jsd6w@2d');

To ease the transition from PHP 4, if PHP 5 cannot find a method named _ _construct( ) within your object hierarchy, it reverts to the PHP 4 constructor naming scheme and searches accordingly. Since a PHP 4 constructor has the same name as its class, unless you have class methods called _ _construct( ) that are serving another purpose, your existing code should not break under PHP 5. (The reason for this name change is discussed in Section 2.5.4, later in this chapter.)

There are no backward compatibility issues with destructors, because they aren't available in PHP 4. However, that doesn't mean people didn't try to recreate them using other language features. If you emulated destructors, you will want to port your code, because PHP 5's destructors are more efficient and easier to use.

In PHP 4, you can mimic destructors by defining a method that you want to act as a destructor and then using register_shutdown_function( ) to make PHP invoke it at the end of the script, as in Example 2-2.

Example 2-2. Mimicking destructors in PHP 4
register_shutdown_function('destruct');

$GLOBALS['objects_to_destroy'] = array( );



function destruct( ) {

    foreach($GLOBALS['objects_to_destroy'] as $obj) {

        $obj->destruct( );

    }

}



class Database {

    function database($host, $user, $password) {

        $this->handle = db_connect($host, $user, $password);   

        $GLOBALS['objects_to_destroy'][  ] = &$this;

    }



    function destruct( ) {

         db_close($this->handle); // close the database connection

    }

}

PHP has a special function, register_shutdown_function( ), that's called by PHP right before the script ends. You can use register_shutdown_function( ) to ensure PHP runs whatever clean-up code you want before it does its own housekeeping.

Example 2-2 sets things up so that PHP calls the destruct( ) function. This function iterates through a list of destroyable objects stored in the global variable $objects_to_destroy and calls each object's destruct( ) method.

When an object needs a destructor, it must add itself to the $objects_to_destroy array and also implement a destruct( ) method. The destruct( ) method should contain any code needed to clean up the resources that were consumed when the object was created and used.

In this example, the Database class adds itself in the constructor by doing $GLOBALS['objects_to_destroy'][ ] = &$this;. This ensures all objects are properly accounted for. Its destruct( ) method calls db_close( ) to shut the database connection.

In many cases, such as closing connections to databases and unlocking files, PHP will do this for you automatically. However, this clean up occurs only when the script is complete. Therefore, it's a good idea to release them yourself in the destructor. It's best to clean up as soon as possible because other programs may want to use your database connection or access the locked file.

When the majority of PHP scripts were short and fast, letting PHP tidy up your mess wasn't a large concern, because the time between the moment you finished using the resource and the end of the script was very short. Now that PHP is used on the command line and for more complex tasks, long-running scripts are commonplace, so it's a practical concern.

You may notice that since this implementation of destructors uses register_shutdown_function( ), there's no time benefit, because the destructor isn't called until the end of the script. This is one of the big differences between destructors in PHP 5 and this PHP 4 emulation.

In PHP 5, objects are destroyed when they're no longer used, so connections are freed earlier. Also, this implementation is far from clean and object-oriented. You need to use global variables and global functions to track your objects, and it's easy to break the scheme by overwriting the array.

Thankfully, PHP 5 implements object destructors in the language itself, so PHP automatically tracks which objects have destructors and calls them as soon as you're finished using the object. This can be much earlier than when your program ends, so resources such as database connections and file locks won't be held open for the entire script; instead, they are released as soon as possible.

Like constructors, destructors in PHP 5 have a fixed name: _ _destruct( ). Since you don't call the destructor manually, you're not allowed to pass it any parameters. If your destructor needs any object-specific information, store it as a property:

// PHP 5 Destructor

class database {

    function _ _destruct( ) {

         db_close($this->handle); // close the database connection

    }

}

Since destructors are now a language-level feature, there's no need for use register_shutdown_function( ). Everything is done for you.

You cannot assume that PHP will destroy objects in any particular order. Therefore, you should not reference another object in your destructor, as PHP may have already destroyed it. Doing so will not cause a crash, but it will cause your code to behave in an unpredictable (and buggy) manner.