In PHP 5 there is a new Object Model. PHP's handling of objects has been
completely rewritten, allowing for better performance and more features.
In previous versions of PHP, objects were handled like primitive types
(for instance integers and strings). The drawback of this method was that
semantically the whole object was copied when a variable was assigned, or
pass as a parameter to a method. In the new approach, objects are
referenced by handle, and not by value (one can think of a handle as an
object's identifier).
Many PHP programmers aren't even aware of the copying quirks of the old
object model and, therefore, the majority of PHP applications will work
out of the box, or with very few modifications.
PHP 5 introduces private and protected member variables, they allow you
to define the visibility of class properties.
Example B-4. Private and Protected Members accesibility
Protected member variables can be accessed in classes extending the
class they are declared in, whereas private member variables can only be
accessed by the class they belong to.
|
<?php
class MyClass {
private $Hello = "Hello, World!\n";
protected $Bar = "Hello, Foo!\n";
protected $Foo = "Hello, Bar!\n";
function printHello() {
print "MyClass::printHello() " . $this->Hello;
print "MyClass::printHello() " . $this->Bar;
print "MyClass::printHello() " . $this->Foo;
}
}
class MyClass2 extends MyClass {
protected $Foo;
function printHello() {
MyClass::printHello(); print "MyClass2::printHello() " . $this->Hello; print "MyClass2::printHello() " . $this->Bar; print "MyClass2::printHello() " . $this->Foo; }
}
$obj = new MyClass();
print $obj->Hello; print $obj->Bar; print $obj->Foo; $obj->printHello(); $obj = new MyClass2();
print $obj->Hello; print $obj->Bar; print $obj->Foo; $obj->printHello();
?>
|
|
With PHP 5, private and protected methods are also introduced.
Example B-5. Protected methods example |
<?php
class Foo {
private function aPrivateMethod() {
echo "Foo::aPrivateMethod() called.\n";
}
protected function aProtectedMethod() {
echo "Foo::aProtectedMethod() called.\n";
$this->aPrivateMethod();
}
}
class Bar extends Foo {
public function aPublicMethod() {
echo "Bar::aPublicMethod() called.\n";
$this->aProtectedMethod();
}
}
$o = new Bar;
$o->aPublicMethod();
?>
|
|
Old code that has no user-defined classes or functions named
"public", "protected" or "private" should
run without modifications.
PHP 5 also introduces abstract classes and methods. An abstract method
only declares the method's signature and does not provide an
implementation. A class that contains abstract methods needs to be
declared abstract.
Example B-6. Abstract class example |
<?php
abstract class AbstractClass {
abstract public function test();
}
class ImplementedClass extends AbstractClass {
public function test() {
echo "ImplementedClass::test() called.\n";
}
}
$o = new ImplementedClass;
$o->test();
?>
|
|
Abstract classes cannot be instantiated. Old code that has no
user-defined classes or functions named 'abstract' should run without
modifications.
PHP 5 introduces interfaces. A class may implement an arbitrary list of
interfaces.
Example B-7. Interface example |
<?php
interface Throwable {
public function getMessage();
}
class MyException implements Throwable {
public function getMessage() {
}
}
?>
|
|
Old code that has no user-defined classes or functions named 'interface'
or 'implements' should run without modifications.
While remaining loosely typed PHP 5 introduces the ability to use class
type hints to declare the expected class of objects that are passed as
parameters to a method.
Example B-8. Class type hinting example |
<?php
interface Foo {
function a(Foo $foo);
}
interface Bar {
function b(Bar $bar);
}
class FooBar implements Foo, Bar {
function a(Foo $foo) {
}
function b(Bar $bar) {
}
}
$a = new FooBar;
$b = new FooBar;
$a->a($b);
$a->b($b);
?>
|
|
These class type hints are not checked upon compilation, as would be the
case in a typed language, but during runtime. This means that:
is equivalent to:
PHP 5 introduces the "final" keyword to declare final members and
methods. Methods and members declared final cannot be overridden by
sub-classes.
Example B-9. final method |
<?php
class Foo {
final function bar() {
}
}
?>
|
|
It is furthermore possible to make a class final. Doing this prevents a
class from being specialized (it cannot be inherited by another class).
There's no need to declare the methods of a final class themselves as
final.
Example B-10. final class |
<?php
final class Foo {
}
?>
|
|
Properties can not be final.
Old code that has no user-defined classes or functions named 'final'
should run without modifications.
PHP 4 offered no way a user could decide what copy constructor to run
when an object is duplicated. During duplication, PHP 4 did a bit for bit
copy making an identical replica of all the object's properties.
Creating a copy of an object with fully replicated properties is not
always the wanted behavior. A good example of the need for copy
constructors, is if you have an object which represents a GTK window and
the object holds the resource of this GTK window, when you create a
duplicate you might want to create a new window with the same properties
and have the new object hold the resource of the new window. Another
example is if your object holds a reference to another object which it
uses and when you replicate the parent object you want to create a new
instance of this other object so that the replica has its own separate
copy.
An object copy is created by using the clone keyword (which calls the
object's __clone() method if possible). An object's
__clone() method cannot be called directly.
When the developer asks to create a new copy of an object, PHP 5 will
check if a __clone() method has been defined or not.
If not, it will call a default __clone() which will copy all of the object's
properties. If a __clone() method is defined, then it
will be responsible to set the necessary properties in the created object.
For convenience, the engine will supply a function that imports all of the
properties from the source object, so that they can start with a by-value
replica of the source object, and only override properties that need to be
changed.
Example B-11. Objects cloning |
<?php
class MyCloneable {
static $id = 0;
function MyCloneable() {
$this->id = self::$id++;
}
function __clone() {
$this->address = "New York";
$this->id = self::$id++;
}
}
$obj = new MyCloneable();
$obj->name = "Hello";
$obj->address = "Tel-Aviv";
print $obj->id . "\n";
$obj_cloned = clone $obj;
print $obj_cloned->id . "\n";
print $obj_cloned->name . "\n";
print $obj_cloned->address . "\n";
?>
|
|
PHP 5 allows developers to declare constructor methods for classes.
Classes which have a constructor method call this method on each
newly-created object, so it is suitable for any initialization that the
object may need before it is used.
With PHP 4, constructor methods were class methods that had the same name
as the class itself. Since it is very common to call parent constructors
from derived classes, the way PHP 4 worked made it a bit cumbersome to
move classes around in a large class hierarchy. If a class is moved to
reside under a different parent, the constructor name of that parent
changes as well, and the code in the derived class that calls the parent
constructor has to be modified.
PHP 5 introduces a standard way of declaring constructor methods by
calling them by the name __construct().
Example B-12. using new unified constructors |
<?php
class BaseClass {
function __construct() {
print "In BaseClass constructor\n";
}
}
class SubClass extends BaseClass {
function __construct() {
parent::__construct();
print "In SubClass constructor\n";
}
}
$obj = new BaseClass();
$obj = new SubClass();
?>
|
|
For backwards compatibility, if PHP 5 cannot find a
__construct() function for a given class, it will
search for the old-style constructor function, by the name of the class.
Effectively, it means that the only case that would have compatibility
issues is if the class had a method named
__construct() which was used for different semantics.
Having the ability to define destructors for objects can be very useful.
Destructors can log messages for debugging, close database connections
and do other clean-up work. No mechanism for object destructors existed
in PHP 4, although PHP had already support for registering functions
which should be run on request shutdown.
PHP 5 introduces a destructor concept similar to that of other
object-oriented languages, such as Java: When the last reference to an
object is destroyed the object's destructor, which is a class method
named __destruct() that receives no parameters, is
called before the object is freed from memory.
Example B-13. Destructor |
<?php
class MyDestructableClass {
function __construct() {
print "In constructor\n";
$this->name = "MyDestructableClass";
}
function __destruct() {
print "Destroying " . $this->name . "\n";
}
}
$obj = new MyDestructableClass();
?>
|
|
Like constructors, parent destructors will not be called implicitly by
the engine. In order to run a parent destructor, one would have to
explicitly call parent::__destruct() in the destructor
body.
PHP 5 introduces per-class constants:
Example B-14. Class constant example |
<?php
class Foo {
const constant = "constant";
}
echo "Foo::constant = " . Foo::constant . "\n";
?>
|
|
Old code that has no user-defined classes or functions named 'const'
will run without modifications.
PHP 4 had no exception handling. PHP 5 introduces a exception model
similar to that of other programming languages. Note that there is
support for "catch all" but not for the "finally" clause.
Exceptions can be rethrown in catch blocks. Also it is possible to have
multiple catch blocks. In that case the caught exception is compared with
the classtype of each catch block from top to bottom and the first block
that has an 'instanceof' match gets executed. When the catch block
finishes, execution continues at the end of the last catch block. If no
catch block has an 'instanceof' match then the next try/catch block is
searched until no more try/catch blocks are available. In that case the
exception is an uncaught exception and the program terminates with
showing the exception.
Example B-15. Exception creation example |
<?php
try {
throw new Exception('Hello');
}
catch (Exception $exception) {
echo $exception;
}
?>
|
|
Old code that has no user-defined classes or functions 'catch', 'throw'
and 'try' will run without modifications.
In PHP 4 it wasn't possible to dereference objects returned by functions
and make further method calls on those objects. With PHP 5, the following
is now possible:
Example B-16. Dereferencing example |
<?php
class Circle {
function draw() {
print "Circle\n";
}
}
class Square {
function draw() {
print "Square\n";
}
}
function ShapeFactoryMethod($shape) {
switch ($shape) {
case "Circle":
return new Circle();
case "Square":
return new Square();
}
}
ShapeFactoryMethod("Circle")->draw();
ShapeFactoryMethod("Square")->draw();
?>
|
|
Static member variables of static classes can now be initialized.
Example B-17. Static variable initialization example |
<?php
class foo {
static $my_static = 5;
public $my_prop = 'bla';
}
print foo::$my_static;
$obj = new foo;
print $obj->my_prop;
?>
|
|
PHP 5 introduces the 'static' keyword to declare a method static, thus
callable from outside the object context.
Example B-18. Static Methods example |
<?php
class Foo {
public static function aStaticMethod() {
}
}
Foo::aStaticMethod();
?>
|
|
The pseudo variable $this is not available inside a method that has been
declared static.
PHP 5 introduces the instanceof keyword, that
allows you to ascertain whether or not an object is an instance of
a class, or extends a class, or implements an interface.
Example B-19. instanceof example |
<?php
class baseClass { }
$a = new baseClass;
if ($a instanceof baseClass) {
echo "Hello World";
}
?>
|
|
Statics are now treated at compile-time which allows developers to
assign variables to statics by reference. This change also greatly
improves their performance but means that indirect references to statics
will not work anymore.
Parameters that are passed by reference to a function may now have
default values
Example B-20. |
<?php
function my_function(&$var = null) {
if ($var === null) {
die("$var needs to have a value");
}
}
?>
|
|
The __autoload() interceptor function will be
automatically called when an undeclared class is to be instantiated.
The name of that class will be passed to the __autoload()
interceptor function as its only argument.
Example B-21. __autoload() example |
<?php
function __autoload($className) {
include_once $className . ".php";
}
$object = new ClassName;
?>
|
|
Both method calls and property accesses can be overloaded via the
__call(), __get() and
__set() methods.
Example B-22.
__get() and
__set()
|
<?php
class Setter {
public $n;
public $x = array("a" => 1, "b" => 2, "c" => 3);
function __get($nm) {
print "Getting [$nm]\n";
if (isset($this->x[$nm])) {
$r = $this->x[$nm];
print "Returning: $r\n";
return $r;
} else {
print "Nothing!\n";
}
}
function __set($nm, $val) {
print "Setting [$nm] to $val\n";
if (isset($this->x[$nm])) {
$this->x[$nm] = $val;
print "OK!\n";
} else {
print "Not OK!\n";
}
}
}
$foo = new Setter();
$foo->n = 1;
$foo->a = 100;
$foo->a++;
$foo->z++;
var_dump($foo);
?>
|
|
Example B-23. __get() example |
<?php
class Caller {
private $x = array(1, 2, 3);
function __call($m, $a) {
print "Method $m called:\n";
var_dump($a);
return $this->x;
}
}
$foo = new Caller();
$a = $foo->test(1, "2", 3.4, true);
var_dump($a);
?>
|
|
Objects may be iterated in an overloaded way when used with foreach. The
default behavior is to iterate over all properties.
Example B-24. Object iteration example |
<?php
class Foo {
public $x = 1;
public $y = 2;
}
$obj = new Foo;
foreach ($obj as $prp_name => $prop_value) {
}
?>
|
|
Each class whose instances can be iterated with foreach should implement
the empty interface Traversable. Hence any object that
says it implements Traversable can be used with foreach.
The interfaces IteratorAggregate and Iterator
allows you to specify how class objects are iterated in PHP code. The first
of them simply has a method getIterator() which must
return an array or an object that either implements the interface
Iterator or is instantiated from an internal
class that can be iterated
Example B-25. Iterator creation example |
<?php
class ObjectIterator implements Iterator {
private $obj;
private $num;
function __construct($obj) {
$this->obj = $obj;
}
function rewind() {
$this->num = 0;
}
function valid() {
return $this->num < $this->obj->max;
}
function key() {
return $this->num;
}
function current() {
switch($this->num) {
case 0: return "1st";
case 1: return "2nd";
case 2: return "3rd";
default: return $this->num."th";
}
}
function next() {
$this->num++;
}
}
class Object implements IteratorAggregate {
public $max = 3;
function getIterator() {
return new ObjectIterator($this);
}
}
$obj = new Object;
foreach($obj as $key => $val) {
echo "$key = $val\n";
}
$it = $obj->getIterator();
for($it->rewind(); $it->valid(); $it->next()) {
$key = $it->current();
$val = $it->key();
echo "$key = $val\n";
}
unset($it);
?>
|
|
The new __METHOD__ pseudo constant shows the current
class and method when used inside a method and the function when used outside
of a class.
Example B-26. __METHOD__ use example |
<?php
class Foo {
function show() {
echo __METHOD__;
}
}
class Bar extends Foo {
}
Foo::show(); Bar::show(); function test() {
echo __METHOD__;
}
test(); ?>
|
|
The new __toString() magic method allows you to
overload the object to string conversion.
Example B-27. __toString() example |
<?php
class Foo {
function __toString() {
return "What ever";
}
}
$obj = new Foo;
echo $obj; ?>
|
|
PHP 5 comes with a complete reflection API that adds the ability to
reverse-engineer classes, interfaces, functions and methods as well as
extensions.
The reflection API also offers ways of getting doc comments for
functions, classes and methods.
Nearly all aspects of object oriented code can be reflected by using the
reflection API which is
documented separately.
Example B-28. Reflection API use example |
<?php
class Foo {
public $prop;
function Func($name) {
echo "Hello $name";
}
}
reflectionClass::export('Foo');
reflectionObject::export(new Foo);
reflectionMethod::export('Foo', 'func');
reflectionProperty::export('Foo', 'prop');
reflectionExtension::export('standard');
?>
|
|