mirror of
https://github.com/php/php-src.git
synced 2024-12-25 01:40:50 +08:00
553 lines
19 KiB
Plaintext
553 lines
19 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.
|
|
|
|
[Not sure if the following will be implemented after all]
|
|
To simplify migration, the Zend Engine 2.0 supports an optional
|
|
'auto-clone' feature, which performs a cloning of the object
|
|
whenever it would have been copied in the Zend Engine 1.0.
|
|
Optionally, it emits an E_NOTICE message whenever such an
|
|
automatic clone occurs, in order to allow developers to
|
|
gradually migrate to the behavior of the Zend Engine 2 (without
|
|
automatic clones).
|
|
|
|
* 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 calling the object's __clone()
|
|
method.
|
|
|
|
Example:
|
|
|
|
<?php
|
|
$copy_of_object = $object->__clone();
|
|
?>
|
|
|
|
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
|
|
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. [The function hasn't been implemented yet]
|
|
|
|
Example:
|
|
|
|
<?php
|
|
class MyCloneable {
|
|
static $id = 0;
|
|
|
|
function MyCloneable() {
|
|
$this->id = self::$id++;
|
|
}
|
|
|
|
function __clone() {
|
|
$this->name = $clone->name;
|
|
$this->address = "New York";
|
|
$this->id = self::$id++;
|
|
}
|
|
}
|
|
|
|
$obj = new MyCloneable();
|
|
|
|
$obj->name = "Hello";
|
|
$obj->address = "Tel-Aviv";
|
|
|
|
print $obj->id . "\n";
|
|
|
|
$obj = $obj->__clone();
|
|
|
|
print $obj->id . "\n";
|
|
print $obj->name . "\n";
|
|
print $obj->address . "\n";
|
|
?>
|
|
|
|
* Forced deletion of objects.
|
|
|
|
The Zend Engine 1.0 had no means to force deletion of an object
|
|
if there are still references to it. The newly introduced delete
|
|
statement calls the object's destructor and frees it even if the
|
|
object is referenced by some other places in the engine. Other
|
|
references to the deleted object become stale and trying to
|
|
access them results in a fatal error.
|
|
|
|
Note that if you have a user-defined function delete() in an old
|
|
script, this script will yield a parser error with the Zend
|
|
Engine 2.0, since 'delete' is now a reserved word.
|
|
|
|
* Namespaces.
|
|
|
|
The Zend Engine 1.0 provided only three scopes: the global
|
|
scope, the class scope and the function scope. All scopes but
|
|
classes could contain variables, only the class and global
|
|
scopes could contain functions, while only the global scope
|
|
could contain constants and classes. This means that all of the
|
|
Zend Engine 1.0's scoping methods were inherently limited for
|
|
solving symbol name collision problems.
|
|
|
|
The Zend Engine 2.0 introduces the concept of namespaces to
|
|
manage the symbol collision problem by making it possible to
|
|
define multiple symbol tables able to contain all types of
|
|
symbols. The Zend Engine is aware of a current namespace,
|
|
defaulting to the current global one. The current namespace may
|
|
be changed on a file-by-file basis. Symbols in other namespaces
|
|
than the current one may be referenced using a new namespace
|
|
operator. It is possible to "import" symbols from one namespace
|
|
into another.
|
|
|
|
Namespaces and classes are the same with the Zend Engine 2.0,
|
|
except that you can't instantiate a namespace with "new". This
|
|
essentially also makes a class a namespace, so the scoping rules
|
|
for namespaces apply for classes. Some of the consequences of
|
|
this are: [Not finalized. Right now we basically have nested
|
|
classes so you can instantiate any nested class]
|
|
|
|
* Classes may contain classes.
|
|
|
|
Example:
|
|
|
|
<?php
|
|
class DB::MySQL {
|
|
var $host = "";
|
|
|
|
function db_connect($user) {
|
|
print "Connecting to MySQL database '$this->host' as $user\n";
|
|
}
|
|
}
|
|
|
|
class DB::Oracle {
|
|
var $host = "localhost";
|
|
|
|
function db_connect($user) {
|
|
print "Connecting to Oracle database '$this->host' as $user\n";
|
|
}
|
|
}
|
|
|
|
$MySQL_obj = new DB::MySQL();
|
|
$MySQL_obj->db_connect("Susan");
|
|
|
|
$Oracle_obj = new DB::Oracle();
|
|
$Oracle_obj->db_connect("Barbara");
|
|
?>
|
|
|
|
* Classes may contain constants.
|
|
|
|
Example:
|
|
|
|
<?php
|
|
class foo {
|
|
const hey = "hello";
|
|
}
|
|
|
|
print foo::hey;
|
|
?>
|
|
|
|
* Current namespace's symbol tables are searched first for
|
|
constants and functions.
|
|
|
|
Example:
|
|
|
|
The following code prints "foobar", not "foo", because
|
|
the class constant overrides the "global" constant of
|
|
the same name.
|
|
|
|
<?php
|
|
define("foo", "bar");
|
|
|
|
class FooClass {
|
|
const foo = "foobar";
|
|
|
|
function printFoo() {
|
|
print foo;
|
|
}
|
|
}
|
|
?>
|
|
|
|
* In the scope of a function, the current namespace is that
|
|
of the containing class/namespace.
|
|
|
|
Example:
|
|
|
|
<?php
|
|
class FooClass {
|
|
function foo() {
|
|
$this->bar();
|
|
bar();
|
|
}
|
|
|
|
function bar() {
|
|
print "foobar\n";
|
|
}
|
|
}
|
|
|
|
$obj = new FooClass;
|
|
$obj->foo();
|
|
$obj->foo();
|
|
?>
|
|
|
|
This prints "foobar" two times, since a bar() method exists
|
|
in the current namespace.
|
|
|
|
Old code that does not take advantage of namespaces will run
|
|
without modifications.
|
|
|
|
* 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
|
|
recieves 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.
|
|
|
|
* 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.
|
|
|
|
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();
|
|
}
|
|
?>
|
|
|
|
Old code that has no user-defined functions 'catch', 'throw' and
|
|
'try' will run without modifications.
|
|
|
|
* Derefencing 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();
|
|
?>
|
|
|
|
* Static member variables of static classes can now be
|
|
initialized.
|
|
|
|
Example:
|
|
|
|
<?php
|
|
class foo {
|
|
static $my_static = 5;
|
|
}
|
|
|
|
print foo::$my_static;
|
|
?>
|
|
|
|
* Supporting default values for by-reference function parameters.
|
|
|
|
Example:
|
|
|
|
<?php
|
|
function my_function(&$var = null) {
|
|
if ($var === null) {
|
|
die('$var needs to have a value');
|
|
}
|
|
}
|
|
?>
|
|
|
|
|
|
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 retreive 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% compatability 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 incompatability is due to the full variable
|
|
reference within quoted strings feature added in the Zend
|
|
Engine.
|