mirror of
https://github.com/php/php-src.git
synced 2024-11-30 13:25:43 +08:00
1167 lines
36 KiB
Plaintext
1167 lines
36 KiB
Plaintext
Changes in the Zend Engine 2.0
|
|
|
|
* New Object Model.
|
|
|
|
The Zend Engine's handling of objects has been completely
|
|
changed in order to allow for new features, but also to increase
|
|
its performance.
|
|
|
|
Objects were handled in previous versions like primitive types
|
|
(for instance integers and strings). The drawback of this method
|
|
is, that semantically the whole object was copied when a
|
|
variable was assigned or parameters were passed to a method. The
|
|
new approach refers to objects by handle and not by value (one
|
|
can think of a handle as an object's ID).
|
|
|
|
Many PHP programmers aren't even aware of the copying quirks of
|
|
the old object model and, therefore, there is a relatively good
|
|
chance that the amount of PHP applications that will work out of
|
|
the box or after a very small amount of modifications would be
|
|
high.
|
|
|
|
* $this
|
|
|
|
Unlike in Zend Engine 1 the pseudo variable $this cannot be
|
|
exchanged in Zend Engine 2. You can of course modify or work with
|
|
an object by using $this but you cannot replace $this with another
|
|
object to change the original object.
|
|
|
|
Example:
|
|
|
|
<?php
|
|
class Foo {
|
|
function replace($other)
|
|
{
|
|
$this = $other;
|
|
}
|
|
}
|
|
|
|
$object = new Foo;
|
|
$object->prop = 'Hello';
|
|
|
|
$other = new Foo;
|
|
$other->prop = 'Bye';
|
|
|
|
$object->replace($other);
|
|
|
|
print $object->prop; // still shows 'Hello'
|
|
|
|
?>
|
|
|
|
Zend Engine 2.0 will issue a compile error, if an assignment
|
|
to $this is found.
|
|
|
|
* Private and Protected Members.
|
|
|
|
The Zend Engine 2.0 introduces private and protected member
|
|
variables. Note that for performance reasons no error message is
|
|
emitted in case of an illegal access to a private or protectecd
|
|
member variable.
|
|
|
|
Example:
|
|
|
|
<?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(); /* Should print */
|
|
print "MyClass2::printHello() " . $this->Hello; /* Shouldn't print out anything */
|
|
print "MyClass2::printHello() " . $this->Bar; /* Shouldn't print (not declared)*/
|
|
print "MyClass2::printHello() " . $this->Foo; /* Should print */
|
|
}
|
|
}
|
|
|
|
$obj = new MyClass();
|
|
print $obj->Hello; /* Shouldn't print out anything */
|
|
print $obj->Bar; /* Shouldn't print out anything */
|
|
print $obj->Foo; /* Shouldn't print out anything */
|
|
$obj->printHello(); /* Should print */
|
|
|
|
$obj = new MyClass2();
|
|
print $obj->Hello; /* Shouldn't print out anything */
|
|
print $obj->Bar; /* Shouldn't print out anything */
|
|
print $obj->Foo; /* Shouldn't print out anything */
|
|
$obj->printHello();
|
|
?>
|
|
|
|
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.
|
|
|
|
* Private and protected methods.
|
|
|
|
The Zend Engine 2.0 introduces private and 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.
|
|
|
|
* Abstract Classes and Methods.
|
|
|
|
The Zend Engine 2.0 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:
|
|
|
|
<?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();
|
|
?>
|
|
|
|
Classes that do not have abstract methods can be declared abstract
|
|
to prevent them from being instantiated.
|
|
|
|
Old code that has no user-defined classes or functions named
|
|
'abstract' should run without modifications.
|
|
|
|
* Interfaces.
|
|
|
|
The Zend Engine 2.0 introduces interfaces. A class may implement
|
|
an arbitrary list of interfaces.
|
|
|
|
Example:
|
|
|
|
<?php
|
|
interface Printable {
|
|
public function dump();
|
|
}
|
|
|
|
class PrintableExample implements Printable {
|
|
public function dump() {
|
|
// ...
|
|
}
|
|
}
|
|
?>
|
|
|
|
Old code that has no user-defined classes or functions named
|
|
'interface' or 'implements' should run without modifications.
|
|
|
|
An interface may extend one or more base interfaces (but not
|
|
implement them). Neither a class nor an interface can inherit
|
|
methods of the same name from different root interfaces.
|
|
|
|
Interfaces may contain abstract static methods.
|
|
|
|
Example:
|
|
|
|
<?php
|
|
interface Printable {
|
|
function dump();
|
|
}
|
|
|
|
interface Streamable extends Printable {
|
|
function writeToStream();
|
|
static function readFromStream();
|
|
}
|
|
|
|
class PrintableExample implements Streamable {
|
|
public function dump() {
|
|
// ...
|
|
}
|
|
function writeToStream() {
|
|
// ...
|
|
}
|
|
static function readFromStream() {
|
|
// ...
|
|
}
|
|
}
|
|
?>
|
|
|
|
A class that does not implement all interface methods must be
|
|
declared as an abstract class.
|
|
|
|
* Class Type Hints.
|
|
|
|
While remaining loosely typed the Zend Engine 2.0 introduces the
|
|
ability to use class type hints to declare the expected class of
|
|
objects that are passed as parameters to a method.
|
|
|
|
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
|
|
|
|
function foo(ClassName $object) {
|
|
// ...
|
|
}
|
|
|
|
is equivalent to
|
|
|
|
function foo($object) {
|
|
if (!($object instanceof ClassName)) {
|
|
die('Argument 1 must be an instance of ClassName');
|
|
}
|
|
}
|
|
|
|
This syntax only applies to objects/classes, not built-in types.
|
|
|
|
* Final methods and classes.
|
|
|
|
The Zend Engine 2.0 introduces the "final" keyword to declare
|
|
final methods. Those cannot be overridden by sub-classes.
|
|
|
|
Example:
|
|
|
|
<?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:
|
|
|
|
<?php
|
|
final class Foo {
|
|
// class definition
|
|
}
|
|
// the next line is impossible
|
|
// class Bork extends Foo {}
|
|
?>
|
|
|
|
Properties cannot be final. See per-class constants below.
|
|
|
|
Old code that has no user-defined classes or functions named
|
|
'final' should run without modifications.
|
|
|
|
* Object Cloning.
|
|
|
|
The Zend Engine 1.0 offered no way a user could decide what copy
|
|
constructor to run when an object is duplicated. During
|
|
duplication, the Zend Engine 1.0 did a bitwise 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 operator.
|
|
|
|
Example:
|
|
|
|
<?php
|
|
$copy_of_object = clone $object;
|
|
?>
|
|
|
|
When the developer asks to create a new copy of an object, the
|
|
Zend Engine 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
|
|
ensures, that the clone will be initialized with all of the
|
|
properties from the source object, so that developers can start
|
|
with a by-value replica of the source object, and only override
|
|
properties that need to be changed.
|
|
|
|
Example:
|
|
<?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';
|
|
|
|
$obj_clone = clone $obj;
|
|
|
|
print $obj->id . "\n";
|
|
print $obj->name . "\n";
|
|
print $obj->address . "\n";
|
|
|
|
print $obj_clone->id . "\n";
|
|
print $obj_clone->name . "\n";
|
|
print $obj_clone->address . "\n";
|
|
?>
|
|
|
|
* Unified Constructors.
|
|
|
|
The Zend Engine 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 can be used.
|
|
|
|
With the Zend Engine 1.0, 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
|
|
the Zend Engine 1.0 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.
|
|
|
|
The Zend Engine 2.0 introduces a standard way of declaring
|
|
constructor methods by calling them by the name __construct().
|
|
|
|
Example:
|
|
|
|
<?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 the Zend Engine 2.0 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.
|
|
|
|
* Destructors.
|
|
|
|
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 the Zend Engine
|
|
1.0, although PHP had already support for registering functions
|
|
which should be run on request shutdown.
|
|
|
|
The Zend Engine 2.0 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 name __destruct() that
|
|
receives no parameters, is called before the object is freed
|
|
from memory.
|
|
|
|
Example:
|
|
|
|
<?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.
|
|
|
|
* Constants.
|
|
|
|
The Zend Engine 2.0 introduces per-class constants.
|
|
|
|
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.
|
|
|
|
* Exceptions.
|
|
|
|
The Zend Engine 1.0 had no exception handling. The Zend Engine 2.0
|
|
introduces a exception model similar to that of other programming
|
|
languages. But there is no catch all and no finally clause.
|
|
|
|
Old code that has no user-defined classes or functions 'catch',
|
|
'throw' and 'try' will run without modifications.
|
|
|
|
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 a 'instanceof' match gets executed.
|
|
When the catch block finishes execution continues at the end of
|
|
the last catch block. If no catch block has a '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:
|
|
|
|
<?php
|
|
class MyException {
|
|
function __construct($exception) {
|
|
$this->exception = $exception;
|
|
}
|
|
|
|
function Display() {
|
|
print "MyException: $this->exception\n";
|
|
}
|
|
}
|
|
|
|
class MyExceptionFoo extends MyException {
|
|
function __construct($exception) {
|
|
$this->exception = $exception;
|
|
}
|
|
|
|
function Display() {
|
|
print "MyException: $this->exception\n";
|
|
}
|
|
}
|
|
|
|
try {
|
|
throw new MyExceptionFoo('Hello');
|
|
}
|
|
catch (MyException $exception) {
|
|
$exception->Display();
|
|
}
|
|
catch (Exception $exception) {
|
|
echo $exception;
|
|
}
|
|
?>
|
|
|
|
Even though the above example shows that it is possible to define
|
|
exception classes that don't inherit from Exception it is best to
|
|
do so. This is because the internal Exception class can gather a
|
|
lot of information otherwise not available. The PHP code emulation
|
|
code would look something like shown below. The comments show the
|
|
meaning of each property. As the code shows it is possible to read
|
|
any available information by using the getter methods. But since
|
|
some of the methods are used internally they are marked final. All
|
|
in all the class is very restrictive because it must be ensured
|
|
that anything used internally always works as expected.
|
|
|
|
Emulating class Exception:
|
|
|
|
<?php
|
|
class Exception {
|
|
function __construct(/*string*/ $message=NULL, /*int*/ $code=0) {
|
|
if (func_num_args()) {
|
|
$this->message = $message;
|
|
}
|
|
$this->code = $code;
|
|
$this->file = __FILE__; // of throw clause
|
|
$this->line = __LINE__; // of throw clause
|
|
$this->trace = debug_backtrace();
|
|
$this->string = StringFormat($this);
|
|
}
|
|
|
|
protected $message = 'Unknown exception'; // exception message
|
|
protected $code = 0; // user defined exception code
|
|
protected $file; // source filename of exception
|
|
protected $line; // source line of exception
|
|
|
|
private $trace; // backtrace of exception
|
|
private $string; // internal only!!
|
|
|
|
final function getMessage() {
|
|
return $this->message;
|
|
}
|
|
final function getCode() {
|
|
return $this->code;
|
|
}
|
|
final function getFile() {
|
|
return $this->file;
|
|
}
|
|
final function getTrace() {
|
|
return $this->trace;
|
|
}
|
|
final function getTraceAsString() {
|
|
return self::TraceFormat($this);
|
|
}
|
|
function _toString() {
|
|
return $this->string;
|
|
}
|
|
static private function StringFormat(Exception $exception) {
|
|
// ... a function not available in PHP scripts
|
|
// that returns all relevant information as a string
|
|
}
|
|
static private function TraceFormat(Exception $exception) {
|
|
// ... a function not available in PHP scripts
|
|
// that returns the backtrace as a string
|
|
}
|
|
}
|
|
?>
|
|
|
|
If you derive your exception classes from this Exception base class
|
|
your exceptions will be nicely shown in the builtin handler for
|
|
uncaught exceptions.
|
|
|
|
Note: The method getMessage() is a final read only access method to
|
|
the private proeprty message that is set in the constructor. If you
|
|
feel a need to overwrite the exception display then overload method
|
|
__toString() in your derived class or implement your own extneral
|
|
exception display function to accomplish your desired formatting.
|
|
|
|
Example:
|
|
|
|
<?php
|
|
function display_exception(Exception $ex)
|
|
{
|
|
echo 'Exception: ' . $ex->getMessage() . 'with backtrace: <pre>';
|
|
echo $ex->getTrace();
|
|
echo '</pre>';
|
|
}
|
|
|
|
try
|
|
{
|
|
// your code here
|
|
}
|
|
catch (Exception $ex)
|
|
{
|
|
display_exeption($ex);
|
|
}
|
|
?>
|
|
|
|
* Dereferencing objects returned from functions.
|
|
|
|
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();
|
|
?>
|
|
|
|
* Member variables of classes can now be initialized.
|
|
|
|
Example:
|
|
|
|
<?php
|
|
class foo {
|
|
static $my_static = 5;
|
|
public $my_prop = 'bla';
|
|
}
|
|
|
|
print foo::$my_static;
|
|
|
|
$obj = foo;
|
|
|
|
print $obj->my_prop;
|
|
?>
|
|
|
|
* Static Methods.
|
|
|
|
The Zend Engine 2.0 introduces the 'static' keyword to declare
|
|
a method static, thus callable from outside the object context.
|
|
|
|
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.
|
|
|
|
* instanceof.
|
|
New support for an instanceof operator which checks if an object
|
|
is of a certain class or interface type.
|
|
|
|
Example:
|
|
|
|
<?php
|
|
|
|
class Foo {
|
|
}
|
|
|
|
$obj = new Foo();
|
|
if ($obj instanceof Foo) {
|
|
print "Yay!\n";
|
|
}
|
|
?>
|
|
|
|
* Parameters that are passed by reference to a function
|
|
may now have default values.
|
|
|
|
Example:
|
|
|
|
<?php
|
|
function my_function(&$var = null) {
|
|
if ($var === null) {
|
|
die('$var needs to have a value');
|
|
}
|
|
}
|
|
?>
|
|
|
|
* __autoload().
|
|
|
|
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. __autoload() must succeed in loading the class. If it
|
|
doesn't then an E_ERROR is emitted.
|
|
|
|
Example:
|
|
|
|
<?php
|
|
function __autoload($className) {
|
|
include_once $className . '.php';
|
|
}
|
|
|
|
$object = new ClassName;
|
|
?>
|
|
|
|
* Method calls and property accesses can be overloaded
|
|
by class methods __call(), __get() and __set().
|
|
|
|
__get() and __set() Example:
|
|
|
|
<?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);
|
|
?>
|
|
|
|
__call() Example:
|
|
|
|
<?php
|
|
class Caller {
|
|
var $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);
|
|
?>
|
|
|
|
* Iteration
|
|
|
|
Objects may be iterated in an overloaded way when used with
|
|
foreach. The default behavior is to iterate over all properties
|
|
with respect to property visibility.
|
|
|
|
Example:
|
|
|
|
<?php
|
|
class Foo {
|
|
var $x = 1;
|
|
var $y = 2;
|
|
}
|
|
|
|
$obj = new Foo;
|
|
|
|
foreach ($obj as $prp_name => $prop_value) {
|
|
// using the property
|
|
}
|
|
?>
|
|
|
|
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' allow to specify
|
|
how class objects are iterated in PHP code. The first of them simply
|
|
has a method 'getIterator' which must return an object that either
|
|
implements the interface 'Iterator' or is instantiated from an
|
|
internal class that can be iterated.
|
|
|
|
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;
|
|
|
|
// this foreach ...
|
|
foreach($obj as $key => $val) {
|
|
echo "$key = $val\n";
|
|
}
|
|
|
|
// matches the following 7 lines with the for directive.
|
|
$it = $obj->getIterator();
|
|
for($it->rewind(); $it->valid(); $it->next()) {
|
|
$key = $it->key();
|
|
$val = $it->current();
|
|
echo "$key = $val\n";
|
|
}
|
|
unset($it);
|
|
?>
|
|
|
|
The matching for directive is very intersting here since it shows
|
|
the use of all abstract methods declared in the interfaces Iterator
|
|
and IteratorAggregate respectively.
|
|
|
|
* Array overloading
|
|
|
|
Objects can be used with Array notation when they implement the
|
|
interface ArrayAccess. You cannot use such objects in standard
|
|
array functions, however you have full control over the array
|
|
notation. This allows lazy initialization or read only array.
|
|
|
|
Note that setting [] results in a call to offsetSet() with
|
|
index being NULL. That means that as with standard arrays you
|
|
cannot store NULL keys.
|
|
|
|
Example:
|
|
|
|
<?php
|
|
class ArrayClass implements ArrayAccess {
|
|
public $a = array();
|
|
|
|
function offsetExists($index) {
|
|
return array_key_exists($index, $this->a);
|
|
}
|
|
function offsetGet($index) {
|
|
return $this->a[$index];
|
|
}
|
|
function offsetSet($index, $newval) {
|
|
return $this->a[$index] = $newval;
|
|
}
|
|
function offsetUnset($index) {
|
|
unset($this->a[$index]);
|
|
}
|
|
}
|
|
|
|
$obj = new ArrayClass;
|
|
|
|
$obj[0] = 'bla'; // calls offsetSet(0,'bla')
|
|
$obj[] = 42; // calls offsetSet(NULL, 42)
|
|
$x = $obj[0]; // calls offsetGet(0)
|
|
$b = isset($obj[0]); // calls offsetExists(0)
|
|
unset($obj[0]); // calls offsetUnset(0)
|
|
?>
|
|
|
|
|
|
* __METHOD__
|
|
|
|
The pseudo constant __METHOD__ shows the current class and method
|
|
when used inside a method and the function when used outside of a
|
|
class.
|
|
|
|
Example:
|
|
|
|
<?php
|
|
class Foo {
|
|
function Show() {
|
|
echo __FILE__ . '(' . __LINE__ . ')' . __METHOD__;
|
|
}
|
|
}
|
|
function Test() {
|
|
echo __FILE__ . '(' . __LINE__ . ')' . __METHOD__;
|
|
}
|
|
?>
|
|
|
|
* __toString()
|
|
|
|
The magic method __toString() allows to overload the object to
|
|
string conversion. This conversion is only done automatically for
|
|
the printing functions (echo, print) but not for other functions
|
|
that expect strings. Also the function __toString is not used in
|
|
places where objects are not allowed but strings are like array
|
|
indices. Note that specialized objects may be converted to a string
|
|
in any place but without calling __toString().
|
|
|
|
Example:
|
|
|
|
<?php
|
|
class Foo {
|
|
function __toString() {
|
|
return "What ever";
|
|
}
|
|
}
|
|
|
|
$obj = new Foo;
|
|
|
|
$str = (string) $obj; // call __toString()
|
|
|
|
echo $obj; // call __toString()
|
|
|
|
$ar = array();
|
|
$ar[(string)$obj]; // this works
|
|
$ar[$obj]; // this is not allowed
|
|
?>
|
|
|
|
* Reflection API
|
|
|
|
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:
|
|
http://sitten-polizei.de/php/reflection_api/docs/language.reflection.html
|
|
|
|
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');
|
|
?>
|
|
|
|
* New memory manager
|
|
The Zend Engine has a new memory manager which allows it to run efficiently
|
|
in multi-threaded environments as it doesn't need to use mutexes to lock
|
|
and unlock during allocation/deallocation.
|
|
|
|
* Others
|
|
Probably other changes which we forgot to list. This list will be kept up-to-date
|
|
as much as possible.
|
|
|
|
|
|
Changes in the Zend Engine 1.0
|
|
|
|
The Zend Engine was designed from the ground up for increased speed,
|
|
reduced memory consumption and more reliable execution. We dare say
|
|
it meets all of these goals and does so pretty well. Beyond that,
|
|
there are several improvements in the language engine features:
|
|
|
|
* References support.
|
|
|
|
$foo = &$a; would make $foo and $a be two names to the same
|
|
variable. This works with arrays as well, on either side; e.g.,
|
|
$foo = &$a[7]; would make $foo and $a[7] be two names to the
|
|
same variable. Changing one would change the other and vice
|
|
versa.
|
|
|
|
* Object overloading support.
|
|
|
|
This feature allows various OO libraries to use the OO notation
|
|
of PHP to access their functionality. Right now, no use is made
|
|
of that feature, but we'd have a COM module ready by the time
|
|
PHP 4.0 is released. A CORBA module would probably follow.
|
|
|
|
* include() and eval() are now functions, and not statements.
|
|
|
|
That means they return a value. The default return value from
|
|
include() and eval() is 1, so that you can do if (include())
|
|
without further coding. The return value may be changed by
|
|
returning a value from the global scope of the included file or
|
|
the evaluated string. For example, if 'return 7;' is executed in
|
|
the global scope of foo.inc, include('foo.inc') would evaluate
|
|
to 7.
|
|
|
|
* Automatic resource deallocation.
|
|
|
|
Several people have been bitten by the fact that PHP 3.0 had no
|
|
concept of reference counting. The Zend Engine adds full
|
|
reference counting for every value in the system, including
|
|
resources. As soon as a resource is no longer referenced from
|
|
any variable, it is automatically destroyed to save memory and
|
|
resources. The most obvious example for the advantage in this is
|
|
a loop that has an SQL query inside it, something like '$result
|
|
= sql_query(...);'. In PHP 3.0, every iteration resulted in
|
|
another SQL result-set allocated in the memory, and all of the
|
|
result sets weren't destroyed until the end of the script's
|
|
execution. With the Zend Engine, as soon as we overwrite an old
|
|
result set with a new one, the old result set which is no longer
|
|
referenced, is destroyed.
|
|
|
|
* Full support for nesting arrays and objects within each other,
|
|
in as many levels as you want.
|
|
|
|
* true and false are now constants of type boolean.
|
|
|
|
Comparing any other value to them would convert that value to a
|
|
boolean first, and conduct the comparison later. That means, for
|
|
example, that 5==true would evaluate to true (in PHP 3.0, true
|
|
was nothing but a constant for the integer value of 1, so
|
|
5==true was identical to 5==1, which was false).
|
|
|
|
* Runtime binding of function names.
|
|
|
|
This complex name has a simple explanation - you can now call
|
|
functions before they're declared!
|
|
|
|
* Added here-docs support.
|
|
|
|
* Added foreach.
|
|
|
|
Two syntaxes supported:
|
|
|
|
foreach(array_expr as $val) statement
|
|
foreach(array_expr as $key => $val) statement
|
|
|
|
* A true unset() implementation.
|
|
|
|
A variable or element that is unset(), is now sent to oblivion
|
|
in its entirely, no trace remains from it.
|
|
|
|
* Output buffering support.
|
|
|
|
Use ob_start() to begin output buffering, ob_end_flush() to end
|
|
buffering and send out the buffered contents, ob_end_clean() to
|
|
end buffering without sending the buffered contents, and
|
|
ob_get_contents() to retrieve the current contents of the output
|
|
buffer. Header information (header(), content type, cookies) are
|
|
not buffered. By turning on output buffering, you can
|
|
effectively send header information all throughout your file,
|
|
regardless of whether you've emitted body output or not.
|
|
|
|
* Full variable reference within quoted strings:
|
|
|
|
${expr} - full indirect reference support for scalar
|
|
variables
|
|
{variable} - full variable support
|
|
|
|
For example:
|
|
|
|
$foo[5]['bar'] = 'foobar';
|
|
print "{$foo[5]["bar"]}"; // would print "foobar"
|
|
|
|
* Ability to call member functions of other classes from within
|
|
member functions or from the global scope.
|
|
|
|
You can now, for example, override a parent function with a
|
|
child function, and call the parent function from it.
|
|
|
|
* Runtime information for classes (class name, parent, available
|
|
functions, etc.).
|
|
|
|
* Much more efficient syntax highlighter - runs much quicker,
|
|
performs more reliably, and generates much tighter HTML.
|
|
|
|
* A full-featured debugger has been integrated with the language
|
|
(supports breakpoints, expression evaluation, step-in/over,
|
|
function call backtrace, and more).
|
|
|
|
The Zend Engine claims 100% compatibility with the engine of PHP
|
|
3.0, and is shamelessly lying about it. Here's why:
|
|
|
|
* Static variable initializers only accept scalar values
|
|
(in PHP 3.0 they accepted any valid expression). The impact
|
|
should be somewhere in between void and non existent, since
|
|
initializing a static variable with anything but a simple
|
|
static value makes no sense at all.
|
|
|
|
* The scope of break and continue is local to that of an
|
|
include()'d file or an eval()'d string. The impact should
|
|
be somewhat smaller of the one above.
|
|
|
|
* The return statement no longer works from a require()'d file. It
|
|
hardly worked in PHP 3.0, so the impact should be fairly small. If
|
|
you want this functionality - use include() instead.
|
|
|
|
* unset() is no longer a function, but a statement.
|
|
|
|
* The following letter combination is not supported within
|
|
encapsulated strings: "{$". If you have a string that includes
|
|
this letter combination, for example, print "{$somevar"; (which
|
|
printed the letter { and the contents of the variable $somevar in
|
|
PHP 3.0), it will result in a parse error with the Zend Engine.
|
|
In this case, you would have to change the code to print
|
|
"\{$somevar"; This incompatibility is due to the full variable
|
|
reference within quoted strings feature added in the Zend
|
|
Engine.
|