PHP - Classes and Objects - Manual
PHP - Classes and Objects - Manual
$type = 'cc';
$obj = new $type; // outputs "hi!"
class cc {
function __construct() {
echo 'hi!';
}
}
?>
You can also conditionally define them by wrapping them in if/else blocks etc, like so:
<?php
if (expr) {
class cc {
// version 1
}
} else {
class cc {
// version 2
}
}
?>
It makes up for PHP's lack of preprocessor directives. The caveat is that the if/else code body must have been executed before
you can use the class, so you need to pay attention to the order of the code, and not use things before they're defined.
}
}
class FlagsEnum extends Enum {
public function __construct( /*...*/ ) {
$args = func_get_args();
for( $i=0, $n=count($args), $f=0x1; $i<$n; $i++, $f *= 0x2 )
$this->add($args[$i], $f);
}
}
?>
Example usage:
<?php
$eFruits = new Enum("APPLE", "ORANGE", "PEACH");
echo $eFruits->APPLE . ",";
echo $eFruits->ORANGE . ",";
echo $eFruits->PEACH . "\n";
$eBeers = new DefinedEnum("GUINESS" => 25, "MIRROR_POND" => 49);
echo $eBeers->GUINESS . ",";
echo $eBeers->MIRROR_POND . "\n";
$eFlags = new FlagsEnum("HAS_ADMIN", "HAS_SUPER", "HAS_POWER", "HAS_GUEST");
echo $eFlags->HAS_ADMIN . ",";
echo $eFlags->HAS_SUPER . ",";
echo $eFlags->HAS_POWER . ",";
echo $eFlags->HAS_GUEST . "\n";
?>
Will output:
1, 2, 3
25, 49
1,2,4,8 (or 1, 10, 100, 1000 in binary)
$this->price = $iPrice;
$this->qty = $iQty;
$this->calculate();
}
protected function calculate() {
$this->price = number_format($this->price, 2);
$this->total = number_format(($this->price * $this->qty), 2);
}
public function __toString() {
return "You ordered ($this->qty) '$this->name'" . ($this->qty == 1 ? "" : "s") .
" at \$$this->price, for a total of: \$$this->total.";
}
}
echo (new Item("Widget 22", 4.90, 2));
?>
You ordered (2) 'Widget 22's at $4.90, for a total of: $9.80.
By loading class Item (which houses all the improvements we made over the first script) into PHP first, we went from having to
write 5 statements in the first script, to writing only 1 statement "echo new Item" in the second.
S b. 27-May-2005 09:50
We can't create easily anonymous objects like in JavaScript.
JS example :
var o = {
aProperty : "value",
anotherProperty : [ "element 1", "element 2" ] } ;
alert(o.anotherProperty[1]) ; // "element 2"
So I have created a class Object :
<?php
class Object {
function __construct( ) {
$n = func_num_args( ) ;
for ( $i = 0 ; $i < $n ; $i += 2 ) {
$this->{func_get_arg($i)} = func_get_arg($i + 1) ;
}
}
}
$o = new Object(
'aProperty', 'value',
'anotherProperty', array('element 1', 'element 2')) ;
echo $o->anotherProperty[1] ; // "element 2"
?>
You must feel free to make it better :)
function p() {
print "Parent's constructor\n";
}
function p_test() {
print "p_test()\n";
$this->c_test();
}
}
class c extends p {
function c() {
print "Child's constructor\n";
parent::p();
}
function c_test() {
print "c_test()\n";
}
}
$obj = new c;
$obj->p_test();
?>
Outputs:
Child's constructor
Parent's constructor
p_test()
c_test()
function aMemberFunc() {
print 'Inside `aMemberFunc()`';
}
}
$foo = new Foo;
?>
You can access member variables in an object using another variable as name:
<?php
$element = 'aMemberVar';
print $foo->$element; // prints "aMemberVar Member Variable"
?>
or use functions:
<?php
function getVarName()
{ return 'aMemberVar'; }
print $foo->{getVarName()}; // prints "aMemberVar Member Variable"
?>
Important Note: You must surround function name with { and } or PHP would think you are calling a member function of object
"foo".
you can use a constant or literal as well:
<?php
define(MY_CONSTANT, 'aMemberVar');
print $foo->{MY_CONSTANT}; // Prints "aMemberVar Member Variable"
print $foo->{'aMemberVar'}; // Prints "aMemberVar Member Variable"
?>
Introduction
Starting with PHP 5, the object model was rewritten to allow for better performance and more features. This was
a major change from PHP 4. PHP 5 has a full object model.
Among the features in PHP 5 are the inclusions of visibility, abstract and final classes and methods, additional
magic methods, interfaces, cloning and typehinting.
PHP treats objects in the same way as references or handles, meaning that each variable contains an object
reference rather than a copy of the entire object. See Objects and References
Tip
See also the Userland Naming Guide.
Introduction
The Basics
class
Basic class definitions begin with the keyword class, followed by a class name, followed by a pair of curly
braces which enclose the definitions of the properties and methods belonging to the class.
The class name can be any valid label which is a not a PHP reserved word. A valid class name starts with a
letter or underscore, followed by any number of letters, numbers, or underscores. As a regular expression, it
would be expressed thus: [a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*.
A class may contain its own constants, variables (called "properties"), and functions (called "methods").
Example #1 Simple Class definition
<?php
classSimpleClass
{
//propertydeclaration
public$var='adefaultvalue';
//methoddeclaration
publicfunctiondisplayVar(){
echo$this->var;
}
}
?>
The pseudo-variable $this is available when a method is called from within an object context. $this is a
reference to the calling object (usually the object to which the method belongs, but possibly another object, if
the method is called statically from the context of a secondary object).
Example #2 Some examples of the $this pseudo-variable
<?php
classA
{
functionfoo()
{
if(isset($this)){
echo'$thisisdefined(';
echoget_class($this);
echo")\n";
}else{
echo"\$thisisnotdefined.\n";
}
}
}
classB
{
functionbar()
{
//Note:thenextlinewillissueawarningifE_STRICTisenabled.
A::foo();
}
}
$a=newA();
$a->foo();
//Note:thenextlinewillissueawarningifE_STRICTisenabled.
A::foo();
$b=newB();
$b->bar();
//Note:thenextlinewillissueawarningifE_STRICTisenabled.
B::bar();
?>
is
is
is
is
defined (A)
not defined.
defined (B)
not defined.
new
To create an instance of a class, the new keyword must be used. An object will always be created unless the
object has a constructor defined that throws an exception on error. Classes should be defined before
instantiation (and in some cases this is a requirement).
If a string containing the name of a class is used with new, a new instance of that class will be created. If the
class is in a namespace, its fully qualified name must be used when doing this.
Example #3 Creating an instance
<?php
$instance=newSimpleClass();
//Thiscanalsobedonewithavariable:
$className='Foo';
$instance=new$className();//Foo()
?>
In the class context, it is possible to create a new object by new self and new parent.
When assigning an already created instance of a class to a new variable, the new variable will access the same
instance as the object that was assigned. This behaviour is the same when passing instances to a function. A
copy of an already created object can be made by cloning it.
Example #4 Object Assignment
<?php
$instance=newSimpleClass();
$assigned=$instance;
$reference=&$instance;
$instance->var='$assignedwillhavethisvalue';
$instance=null;//$instanceand$referencebecomenull
var_dump($instance);
var_dump($reference);
var_dump($assigned);
?>
extends
A class can inherit the methods and properties of another class by using the keyword extends in the class
declaration. It is not possible to extend multiple classes; a class can only inherit from one base class.
The inherited methods and properties can be overridden by redeclaring them with the same name defined in the
parent class. However, if the parent class has defined a method as final, that method may not be overridden. It
is possible to access the overridden methods or static properties by referencing them with parent::.
When overriding methods, the parameter signature should remain the same or PHP will generate an
error. This does not apply to the constructor, which allows overriding with different parameters.
E_STRICT
level
The Basics
{
echo "i am dead<br>
";
}
//////////////////
static function getNewInstance( $have_girlfren)
{
try{
$new_obj=new TestClass( $have_girlfren);
return $new_obj;
}
catch(Exception $e)
{
unset($new_obj);
echo "rest in peace<br>
";
return false;
}
}
//
function gotGirlFren()
{
return $this->have_girlfren;
}
}
$test_obj=TestClass::getNewInstance( false );
if( $test_obj )
echo "bye test oby closing<br>
";
else
echo "instance commit suicide<br>
";
echo "--------------<br>
";
echo "-the end--";
?>
2. Define the SYSTEM we want to use -- laws, rules, policies, structure, properties etc. -- with valid PHP class definitions.
3. Instantiate objects with the 'new' operator so we can poke it for a while.
4. Output some data to see if they obey the rules we wrote.
5. Here we go!
<?php
// 1. check, ENVIRONMENT created.
class Wheel
{
public $cir = 3.0; // circumference in feet.
public $rpm = 0.0; // rotations (spins) per minute.
public function __toString()
{
return 'Wheel [' .
'cir=' . number_format($this->cir, 2) . ' ft, ' .
'rpm=' . $this->rpm .
']';
}
}
// Wheel defined...
class Speedometer
{
public $wheel;
public function __construct(Wheel $wheel)
{
$this->wheel = $wheel;
}
public function getSpeed()
{
$speed = 0.0;
$wheel = $this->wheel;
if($wheel->rpm != 0.0)
{
$fpm = $wheel->cir * $wheel->rpm; // feet per minute
$fph = $fpm * 60; // feet per hour
$speed = $fph / 5280; // MPH = $fph / feet per mile
}
return $speed;
}
public function __toString()
{
return 'Speedometer [' .
'speed=' . number_format($this->getSpeed(), 2) . ' MPH, ' .
'wheel=' . $this->wheel .
']';
}
}
// Speedometer defined.
// 2. check, SYSTEM created.
$wheel = new Wheel();
$speedometer = new Speedometer($wheel);
// class definition
class Bear {
// define properties
public $name;
public $weight;
public $age;
public $sex;
public $colour;
// constructor
public function __construct() {
$this->age = 0;
$this->weight = 100;
}
// define methods
public function eat($units) {
echo $this->name." is eating ".$units." units of food... ";
$this->weight += $units;
}
public function run() {
echo $this->name." is running... ";
}
public function kill() {
echo $this->name." is killing prey... ";
}
public function sleep() {
echo $this->name." is sleeping... ";
}
}
// extended class definition
class PolarBear extends Bear {
// constructor
public function __construct() {
parent::__construct();
$this->colour = "white";
$this->weight = 600;
}
// define methods
public function swim() {
echo $this->name." is swimming... ";
}
}
?>
class Bar {
public function hello() {
echo "bar\n";
}
};
$foo = new Foo();
$bar = $foo->make();
echo "expecting 'bar': ";
$bar->hello();
echo "expecting 'foo': ";
$foo->callClassName();
?>
even tough $this->className() is written two times in exactly the same way, the one contained in a new statement gets the
className field and the other performs the actual method.
?>
Regards
Motaz Abuthiab
class B extends A {
public function func3() {
echo("I'm func3 in B!<br/>");
$this->func1();
$this->func2(); // Call to private function from extended class results in a fatal error
}
}
$b = new B;
$b->func3(); // Ends in a fatal error
// OR
$b->func1(); // Call to protected function from outside world results in a fatal error
?>
If you want a function to be accessible in class B but not to the outside world, it must be declared as protected.
You could define a class that extends stdClass, but you would get no benefit, as stdClass does nothing.
(tested on PHP 5.2.8)
function setData(&$data) {
$this->data =& $data;
}
function hasData(&$data) {
$saved = $data;
$data = true;
$result = $this->data === true;
$data = $saved;
return $result;
}
function isDataOf(&$object) {
return $object->hasData($this);
}
}
$o1 = new TestClass;
$o2 = new TestClass;
$o1->setData($o2);
$o2->setData($o1);
var_dump($o1->hasData($o2)); // true as expected
var_dump($o2->hasData($o1)); // true as expected
var_dump($o1->isDataOf($o2)); // false even though $o1 is in fact the data of $o2
var_dump($o2->isDataOf($o1)); // false even though $o2 is in fact the data of $o1
?>
You can make this example work by replacing the hasData method with:
<?php
function hasData(&$data) {
return $data === $this->data;
}
?>
However, although I've not tested this, I've been told that Zend Engine 1, i.e. PHP 4, will choke on the === parameter when
comparing recursing objects.
<?php
class MyClass {
public $message = 'Hello';
public function MyClassFunction() {
function innerFunction(&$this_thing, $message = null) {
$this_thing->message = (!is_null($message)) ? $message : $this_thing->message;
$this_thing->echo_something();
}
innerFunction($this); // echoes 'Hello'
innerFunction($this, '<br/>New Message'); // echoes '<br/>New Message'
}
<?php
class MyClass {
public $message = 'Hello';
public function MyClassFunction() {
function InnerFunction() {
echo $this->message; // Reports a fatal error
}
innerFunction();
}
}
$class = new MyClass;
$class->MyClassFunction();
?>
This issue cannot be solved by using the Scope Resolution Operator if you're trying to access a variable:
<?php
class MyClass {
public $message = 'Hello';
public function MyClassFunction() {
function InnerFunction() {
echo MyClass::message; // Reports a fatal error
}
innerFunction();
}
}
$class = new MyClass;
$class->MyClassFunction();
?>
Additionally, you can NOT create a public function to access that variable:
<?php
class MyClass {
public $message = 'Hello';
public function MyClassFunction() {
function InnerFunction() {
MyClass::echoSomething();
}
innerFunction();
}
public function echoSomething() {
echo $this->message; // Reports a fatal error
}
}
$class = new MyClass;
$class->MyClassFunction();
?>
Note that in this last case, the error is generated on the line below echoSomething function declaration, not at
MyClass::echoSomething();
However, it is worth noting that when called directly, echoSomething works fine:
<?php
class MyClass {
public $message = 'Hello';
public function MyClassFunction() {
function InnerFunction() {
MyClass::echoSomething();
}
innerFunction();
}
public function echoSomething() {
echo $this->message; // Echoes 'Hello'
}
}
$class = new MyClass;
$class->echoSomething();
?>
}
function basedeclare() {
self::base1declare()
}
}
class base2
public $base2var;
public $basevar;
function based2declare() {
echo 'I am base 2';
}
function basedeclare() {
self::base2declare
}
}
?>
Multiple inheritance is achieved by extending the inheritance class, and then in the __construct function placing calls to the
"inherits" method of the inheritance class. Each call pushes an instance of the inherited class into an array var which
functions as a LIFO stack. Using the magic methods, any failed method call, property set, get, isset or unset is intercepted by
the inheritance base class which then attempts to resolve the reference. Object method name conflicts are resolved simply by
the later inheritance masking the scope of the earlier inherited method. I recognize there are shortcomings to the approach I
offer here, but it works for all my current multiple inheritance needs and offers simplicity and ease of understanding as
benefits.
<?php
class base_test extends inheritance { // multiple inheritance
function __construct() {
$this->inherits( 'base1' );
$this->inherits( 'base2' );
}
} ?>
Here are some code fragments you can try out to test things.
<?php
$testobj = new base_test();
var_dump( $testobj );
$testobj->base2declare();
$testobj->base1declare();
$testobj->base0declare();
$testobj->basedeclare();
$testobj->base2var = 27;
echo $testobj->base2var;
?>
I'd be interested in hearing any comments.
<?php
class A {
public $x = 'A';
public function foo() {
$b = new B;
$b->bar();
return $this->x;
}
}
class B extends A {
public function bar() {
$this->x = 'B';
}
}
$a = new A
echo $a->foo(); //A
?>
I was doing something similar to this (example is greatly simplified to show logic) and spent a long while trying to work out
why I would always get 'A' and never get 'B'. Now, after a few weeks, I have revisited the problem and have worked out why:
The code 'new B' creates a new instance of class B. While class B extends class A, it is a new object and not an extension of
the object created by 'new A'
The value of $x is set to 'B' within the object $b, but not in object $a.
If within A::foo(), one was to access $b->x then one would obtain the vale 'B', for example
<?php
class C {
public $x = 'C';
public function foo() {
$c = new C;
$c->bar();
$this->x = $c->$x
return $this->x;
}
}
class D extends C {
public function bar() {
$this->x = 'D';
}
}
$c = new C
echo $c->foo(); //D
?>
while other functions may only access one piece of data. If each function revolves around the data inside, then you have
created a good class.
This means that $reference, which points at the same data slot, will also be NULL. But $assignment, which is a different data
slot, will still hold its copy of the handle to the Object instance, so it will not be NULL.
<?php
$objectVar = null;
print_r($objectVar);
print_r($reference);
print_r($assignment);
//
// $objectVar --->+---------+
// | NULL |
// $reference --->+---------+
//
// +---------+
// $assignment -->|(handle1)----+
// +---------+ |
// |
// v
// Object(1):foo="qux"
?>
{
...
Properties
Class member variables are called "properties". You may also see them referred to using other terms such as
"attributes" or "fields", but for the purposes of this reference we will use "properties". They are defined by using
one of the keywords public, protected, or private, followed by a normal variable declaration. This declaration
may include an initialization, but this initialization must be a constant value--that is, it must be able to be
evaluated at compile time and must not depend on run-time information in order to be evaluated.
See Visibility for more information on the meanings of public, protected, and private.
Note:
In order to maintain backward compatibility with PHP 4, PHP 5 will still accept the use of the keyword var in
property declarations instead of (or in addition to) public, protected, or private. However, var is no
longer required. In versions of PHP from 5.0 to 5.1.3, the use of var was considered deprecated and would
issue an E_STRICT warning, but since PHP 5.1.3 it is no longer deprecated and does not issue the warning.
If you declare a property using var instead of one of public, protected, or private, then PHP 5 will treat
the property as if it had been declared as public.
Within class methods the properties, constants, and methods may be accessed by using the form $this>property (where property is the name of the property) unless the access is to a static property within the
context of a static class method, in which case it is accessed using the form self::$property. See Static Keyword
for more information.
The pseudo-variable $this is available inside any class method when that method is called from within an object
context. $this is a reference to the calling object (usually the object to which the method belongs, but possibly
another object, if the method is called statically from the context of a secondary object).
Example #1 property declarations
<?php
classSimpleClass
{
//invalidpropertydeclarations:
public$var1='hello'.'world';
public$var2=<<<EOD
helloworld
EOD;
public$var3=1+2;
public$var4=self::myStaticMethod();
public$var5=$myVar;
//validpropertydeclarations:
public$var6=myConstant;
public$var7=array(true,false);
//ThisisallowedonlyinPHP5.3.0andlater.
public$var8=<<<'EOD'
helloworld
EOD;
}
?>
Note:
There are some nice functions to handle classes and objects. You might want to take a look at the
Class/Object Functions.
Unlike heredocs, nowdocs can be used in any static data context, including property declarations.
Example #2 Example of using a nowdoc to initialize a property
<?php
classfoo{
//AsofPHP5.3.0
public$bar=<<<'EOT'
bar
EOT;
}
?>
Note:
Nowdoc support was added in PHP 5.3.0.
Properties
}
}
$bar = new MyClass();
I guess because it's treating $foo like a variable in the first example, so trying to call $this->FALSE (or something along
those lines) which makes no sense. It's obvious once you've realised, but there aren't any examples of accessing on this page
that show that.
Properties have implicit accessor and mutator functionality. I've created an abstract class that allows implicit property
functionality.
<?php
abstract class PropertyObject
{
public function __get($name)
{
if (method_exists($this, ($method = 'get_'.$name)))
{
return $this->$method();
}
else return;
}
Class Constants
It is possible to define constant values on a per-class basis remaining the same and unchangeable. Constants
differ from normal variables in that you don't use the $ symbol to declare or use them.
The value must be a constant expression, not (for example) a variable, a property, a result of a mathematical
operation, or a function call.
It's also possible for interfaces to have constants. Look at the interface documentation for examples.
As of PHP 5.3.0, it's possible to reference the class using a variable. The variable's value can not be a keyword
(e.g. self, parent and static).
Example #1 Defining and using a constant
<?php
classMyClass
{
constconstant='constantvalue';
functionshowConstant(){
echoself::constant."\n";
}
}
echoMyClass::constant."\n";
$classname="MyClass";
echo$classname::constant."\n";//AsofPHP5.3.0
$class=newMyClass();
$class->showConstant();
echo$class::constant."\n";//AsofPHP5.3.0
?>
Note:
Nowdoc support was added in PHP 5.3.0.
Class Constants
}
$obj = new Example();
$obj->test(); // outputs "Hello, world."
?>
I realize the same could be done simply by defining the constant in a class and accessing it via "class_name::const_name", but
I find this a little nicer in that the class declaration makes it immediately obvious that you accessing values from the
implemented interface.
/**
* Get the value of the constant
*
* @param string constant name
* @return void
*/
final function __get($member) {
return $this->$member;
}
}
?>
The class would be extended by another class that would compartmentalize the purpose of the constants. Thus, for example, you
would extend the class with a DbConstant class for managing database related constants, that might look like this:
<?php
/**
* Constants that deal only with the database
*/
class DbConstant extends aClassConstant {
/**
* Constructor. This is so fully dynamic values can be set. This can be skipped and the values can be directly assigned for
non dynamic values as shown above.
*
* @return void
*/
function __construct() {
$this->time = time() + 1; // dynamic assignment
}
}
?>
You would use the class like thus:
<?php
$dbConstant = new DbConstant();
echo $dbConstant->host;
?>
The following would cause an exception:
<?php
$dbConstant = new DbConstant();
$dbConstant->host = '127.0.0.1'; // EXCEPTION
?>
It's not pretty, nor ideal, but at least you don't pollute the global name space with long winded global names and it is
relatively elegant.
Variables must be *protected*, not public. Public variables will bypass the __get and __set methods!! This class is, by design,
not meant to be extended much further than one level, as it is really meant to only contain constants. By keeping the constant
definition class seperate from the rest of your classes (if you are calling this from a class), you minimize the possibility of
accidental variable assignment.
Managing this instance may be a slight pain that requires either caching a copy of the instance in a class variable, or using
the factory pattern. Unfortunately, static methods can't detect the correct class name when the parent name is used during the
call (e.g., DbConstant::instance()). Thus there is no elegant, inheriting solution to that problem. Thus, it is easier to
simply manage a single instance that is declared using conventional notation (e.g., new DbConstant...).
- Michi Kono
class AClass {
const an_array = Array (1,2,3,4);
//this WILL NOT work
// and will throw Fatal Error:
//Fatal error: Arrays are not allowed in class constants in...
public static $an_array = Array (1,2,3,4);
//this WILL work
//however, you have no guarantee that it will not be modified outside your class
}
?>
17-Jun-2005 09:29
It's important to note that constants cannot be overridden by an extended class, if you with to use them in virtual functions.
For example :
<?php
class abc
{
const avar = "abc's";
function show()
{
echo self::avar . "\r\n";
}
};
class def extends abc
{
const avar = "def's";
function showmore ()
{
echo self::avar . "\r\n";
$this->show();
}
};
$bob = new def();
$bob->showmore();
?>
Will display:
def's
abc's
However, if you use variables instead the output is different, such as:
<?php
class abc
{
protected $avar = "abc's";
function show()
{
echo $this->avar . "\r\n";
}
};
class def extends abc
{
protected $avar = "def's";
function showmore ()
{
echo $this->avar . "\r\n";
$this->show();
}
};
$bob = new def();
$bob->showmore();
?>
Will output:
def's
def's
Autoloading Classes
Many developers writing object-oriented applications create one PHP source file per-class definition. One of the
biggest annoyances is having to write a long list of needed includes at the beginning of each script (one for each
class).
In PHP 5, this is no longer necessary. You may define an __autoload() function which is automatically called in
case you are trying to use a class/interface which hasn't been defined yet. By calling this function the scripting
engine is given a last chance to load the class before PHP fails with an error.
Tip
spl_autoload_register() provides a more flexible alternative for autoloading classes. For
this reason, using __autoload() is discouraged and may be deprecated or removed in the
future.
Note:
Prior to 5.3.0, exceptions thrown in the __autoload function could not be caught in the catch block and
would result in a fatal error. From 5.3.0+ exceptions thrown in the __autoload function can be caught in the
catch block, with 1 provision. If throwing a custom exception, then the custom exception class must be
available. The __autoload function may be used recursively to autoload the custom exception class.
Note:
Autoloading is not available if using PHP in CLI interactive mode.
Note:
If the class name is used e.g. in call_user_func() then it can contain some dangerous characters such as ../.
It is recommended to not use the user-input in such functions or at least verify the input in __autoload().
Example #1 Autoload example
This example attempts to load the classes MyClass1 and MyClass2 from the files MyClass1.php and
MyClass2.php respectively.
<?php
function__autoload($class_name){
include$class_name.'.php';
}
$obj=newMyClass1();
$obj2=newMyClass2();
?>
<?php
function__autoload($name){
var_dump($name);
}
classFooimplementsITest{
}
/*
string(5)"ITest"
Fatalerror:Interface'ITest'notfoundin...
*/
?>
Example #4 Autoloading with exception handling for 5.3.0+ - Missing custom exception
This example throws an exception for a non-loadable, custom exception.
<?php
function__autoload($name){
echo"Wanttoload$name.\n";
thrownewMissingException("Unabletoload$name.");
}
try{
$obj=newNonLoadableClass();
}catch(Exception$e){
echo$e->getMessage(),"\n";
}
?>
See Also
unserialize()
unserialize_callback_func
spl_autoload()
spl_autoload_register()
Autoloading Classes
namespace coreControllerHello;
class World
{
function __construct()
{
echo "Helloworld";
}
}
?>
config.php:
<?php
function __autoload($classname) {
include_once("classfolder/" . $classname . ".php");
}
?>
ClassFolder: with SampleClass.php
<?php
class SampleClass {
public function hello_rahj() {
echo "Hello rahj!";
}
}
?>
In your Typical Php File: e.g. Index.php
<?php
include("config.php")
$hello = new SampleClass();
$hello->hello_rahj(); // outputs Hello rahj!
?>
When using the __autoload functions, Make Sure you use a Proper naming Convention.
Hope this Helps!
if (file_exists($file)) {
require_once($file);
return true;
}
}
return false;
});
?>
/* put the custom functions in the autoload register when the class is initialized */
private function __construct(){
spl_autoload_register(array($this, 'clean'));
spl_autoload_register(array($this, 'dirty'));
}
/* the clean method to autoload the class without any includes, works in most cases */
private function clean($class){
global $docroot;
$class=str_replace('_', '/', $class);
spl_autoload_extensions(implode(',', $this->_ext));
foreach($this->_src as $resource){
set_include_path($docroot . $resource);
spl_autoload($class);
}
}
/* the dirty method to autoload the class after including the php file containing the class */
private function dirty($class){
global $docroot;
$class=str_replace('_', '/', $class);
foreach($this->_src as $resource){
foreach($this->_ext as $ext){
@include($docroot . $resource . $class . $ext);
}
}
spl_autoload($class);
}
}
?>
Obviously you still have to include this class the dirty way, for example:
<?php
$path='../';
$docroot=$_SERVER['DOCUMENT_ROOT'] . implode('/',array_slice(explode('/',$_SERVER['PHP_SELF']),0,-2)) . '/';
include($path . 'library/autoload/autoloader.php');
autoload_autoloader::init();
?>
please note that this autoloader class does require the $docroot variable which is the absolute path of the root of your
application, you can set it manually or copy the code i used.
my directory structure looks like this:
- www/
+ myapp/
+ application/
+ controllers/
+ models/
+ views/
+ helpers/
+ library/
+ autoload/
- autoloader.php
class SuperClass {
}
}
class DerivedClass extends SuperClass {
}
class AnotherDerivedClass extends SuperClass {
}
?>
The scripting engine will found the SuperClass class.
Example 2.:
You also can do it with the eval function, and if you dinamycally declare the class, you don't get a Fatal Error, and you can
do many interesting things with the eval function;)
<?php
function __autoload($className){
echo "Now loading: $className<br />";
if (function_exists($func))
{
$returnvalue = call_user_func_array($func,$funcargs);
}
else
{
$funcpath = "scripts/".$func.".php";
require_once($funcpath);
if (function_exists($func))
{
$returnvalue = call_user_func_array($func,$funcargs);
}
else
{
die "SORRY $func IS NOT USABLE";
}
}
foreach($directories as $directory){
foreach($fileNameFormats as $fileNameFormat){
$path = $directory.sprintf($fileNameFormat, $className);
if(file_exists($path)){
include_once $path;
return;
}
}
}
}
spl_autoload_register('autoLoader');
?>
<?php
function __autoload($className) {
$fullclasspath="";
// get separated directories
$pathchunks=explode("_",$className);
require_once '../../'.$fullclasspath.$className.'.class.php';
}
?>*
bye
CasualWeb
if(method_exists($class,'__construct')){
eval("class al_$class extends $class{function __construct(){parent::__construct();}}new al_$class;");
}
return true;
}
?>
}
?>
I use it for example to establish the mysql-connection on demand.
It is also possilbe do add a destructor by adding this lines to the function:
<?php
if(method_exists($class_name,'destruct'))
register_shutdown_function(array($class_name,'destruct'));
?>
if(file_exists($path)) {
require_once($path);
return true;
} else {
clearstatcache();
$classes = $settings->ReLoadSettings();
$path = $classes['classes'][$class_name];
}
if(file_exists($path)) {
require_once($path);
return true;
} else {
die("The requested library,".$class_name.", could not be found at ".$classes[$class_name][$i].". Please check your ini
file");
}
}
?>
james dot dot dot dunmore at gmail dot com 18-Apr-2008 04:43
Andrew: 03-Nov-2006 12:26
That seems a bit messy to me, this is a bit neater:
<?php
function __autoload($class_name)
{
//class directories
$directorys = array(
'classes/',
'classes/otherclasses/',
'classes2/',
'module1/classes/'
);
//for each directory
foreach($directorys as $directory)
{
//see if the file exsists
if(file_exists($directory.$class_name . '.php'))
{
require_once($directory.$class_name . '.php');
//only require the class once, so quit after to save effort (if you got more, then name them something else
return;
}
}
}
Seems like a strange layer of obscurity to me (to be honest). It would be really handy if that could be implemented and
remain in memory though, enabling pages to instantiate classes without having to repeatedly include documents. Being able to
construct an object without having to include the document that contained it (AND without having to include that function in
some form on each document) would be very nice.
while($entryName = readdir($myDirectory)) {
$dirArray[] = $entryName;
}
// close directory
closedir($myDirectory);
// count elements in array
$indexCount = count($dirArray);
sort($dirArray);
for($index=0; $index < $indexCount; $index++) {
if($dirArray[$index] != '.' AND $dirArray[$index] != '..') {
include("required/classes/$dirArray[$index]");
$classname = strtolower($dirArray[$index]);
$classname = str_replace('.php','',$classname);
$classinit = str_replace('.php','',$dirArray[$index]);
$$classname = new $classinit;
}
}
function __autoload($class_name) {
find_file($class_name); //your own routine to locate the class file
if (!class_exists($class_name, false)) {
eval("class $class_name {}");
throw new Exception('Class not found');
}
}
?>
tested... oO) and search as deep as possible. Now it will find CLASS_DIR/foo/bar.class.php also like
CLASS_DIR/foo/bar/baz/buz/fii/and/so/on/class.php
Warning: This code will check ALL dirs who're "deeper" / "lower" than the class dir, so prevent deeply hidden files (or use
just a few folders).
Improved Version:
<?php
// change this, if this code isn't "higher" than ALL classfiles
define("CLASS_DIR", dirname(__FILE__));
/**
* autoload classes (no need to include them one by one)
*
* @uses classFolder()
* @param $className string
*/
function __autoload($className) {
$folder = classFolder($className);
if($folder)
require_once($folder.$className.".class.php");
}
/**
* search for folders and subfolders with classes
*
* @param $className string
* @param $sub string[optional]
* @return string
*/
function classFolder($className, $sub = "/") {
$dir = dir(CLASS_DIR.$sub);
if(file_exists(CLASS_DIR.$sub.$className.".class.php"))
return CLASS_DIR.$sub;
while(false !== ($folder = $dir->read())) {
if($folder != "." && $folder != "..") {
if(is_dir(CLASS_DIR.$sub.$folder)) {
$subFolder = classFolder($className, $sub.$folder."/");
if($subFolder)
return $subFolder;
}
}
}
$dir->close();
return false;
}
?>
Requirements:
- the subfolders must be at least 3 letters long
- the filenames must be in the form CLASSNAME.class.php
Note:
- in this example the main class folder is 'lib'
define('ROOT_DIR', dirname(__FILE__).'/');
function __autoload($className) {
$folder=classFolder($className);
if($folder) require_once($folder.'/'.$className.'.class.php');
}
function classFolder($className,$folder='lib') {
$dir=dir(ROOT_DIR.$folder);
if($folder=='lib' && file_exists(ROOT_DIR.$folder.'/'.$className.'.class.php')) return $folder;
else {
while (false!==($entry=$dir->read())) {
$checkFolder=$folder.'/'.$entry;
if(strlen($entry)>2) {
if(is_dir(ROOT_DIR.$checkFolder)) {
if(file_exists(ROOT_DIR.$checkFolder.'/'.$className.'.class.php')) return $checkFolder;
else {
$subFolder=classFolder($className,$checkFolder);
if($subFolder) return $subFolder;
}
}
}
}
}
$dir->close();
return 0;
}
{
function __construct(\$a=0, \$b=0, \$c=0, \$d=0, \$e=0, \$f=0, \$g=0, \$h=0, \$i=0)
{
throw new AutoloadExceptionRetranslator('$autoloadException');
}
}
");
}
else
{
require_once $classname;
}
}
try
{
$anyObject = new AnyNonExistantClass();
}
catch (AutoloadException $e)
{
print_r($e->getTrace());
}
?>
andrew dot delete dot cornes at gmail dot delete dot com 03-Nov-2006 04:26
If you'd like '__autoload()' to support multiple class folders, each containing multiple class files (one per class), you may
want to try something like this (file '__autoload.php'):
<?php
define('CLASS_FILENAME_SUFFIX', '.class.php');
function __autoload($className)
{
$__autoloadAbsolutePath = dirname(__FILE__);
// 'pathStart' is your web application root folder.
// (This may or may not be where '__autoload.php'
// resides; let's assume here that it resides one
// level 'below' the web app root.)
$pathStart = $__autoloadAbsolutePath .
DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR;
// 'classPath' is a list of class folders to look in.
// (In this example, there's just one: 'classlibs/lib1'.
// To add more, simply append them; start with
// 'PATH_SEPARATOR . $pathStart .', and off you go...)
$classPath = PATH_SEPARATOR . $pathStart .
'classlibs' . DIRECTORY_SEPARATOR . 'lib1';
// Add list of class folders to 'include_path' for the
// forthcoming 'require()' (or similar directive).
$oldIncludePath = get_include_path();
set_include_path($oldIncludePath . $classPath);
require_once($className . CLASS_FILENAME_SUFFIX);
// Reinstate initial 'include_path'.
set_include_path($oldIncludePath);
}
?>
As your web application develops, new paths containing class files can be added into the '$classPath' variable within
'__autoload()'. If hard-coding the '$classPath' variable isn't to your taste, you could arrange for its value to come from
'outside' in whatever way you like.
Any comments gratefully received.
/**
* Attach a new type of locator
* @param object Locator
* @param string key
*/
public static function attachLocator(Locator $locator, $key)
{
self::$locators[$key] = $locator;
}
/**
* Remove a locator that's been added
* @param string key
* @return bool
*/
public static function dropLocator($key)
{
if (self::isActiveLocator($key))
{
unset(self::$locators[$key]);
return true;
}
else return false;
}
/**
* Check if a locator is currently loaded
* @param string key
* @return bool
*/
public static function isActiveLocator($key)
{
return array_key_exists($key, self::$locators);
}
/**
* Load in the required service by asking all service locators
* @param string class
*/
public function load($class)
{
foreach (self::$locators as $key => $obj)
{
if ($obj->canLocate($class))
{
require_once $obj->getPath($class);
if (class_exists($class)) return;
}
}
}
}
/**
* PHPs default __autload
* Grabs an instance of ServiceLocator then runs it
* @package ServiceLocator
* @author Chris Corbyn
* @param string class
*/
function __autoload($class)
{
$locator = new ServiceLocator();
$locator->load($class);
}
?>
An example Use Case:
<?php
require 'ServiceLocator.php';
//Define some sort of service locator to attach...
class PearLocator implements Locator
{
protected $base = '.';
if ( isset($somedata) )
{
var_dump($somedata);
}
else
{
die('No data!');
}
}
}
?>
'common way':
<?php
require 'bar.class.php';
$foo = new bar();
?>
'__autoload way':
<?php
function __autoload($classname)
{
require $classname . '.class.php';
}
$foo = new bar();
?>
Both 'comon way' and '__autoload way' should give same result:
string(9) "Some data"
else
{
die('No data!');
}
}
}
?>
Attempt to load this file in common way:
<?php
require 'bar.class.php';
$foo = new bar();
?>
this will output (as expected):
string(9) "Some data"
But in case of __autoload:
<?php
function __autoload($classname)
{
require $classname . '.class.php';
}
$foo = new bar();
?>
you could expect that this script will return the same but no, it will return "No data!", because defenition of $somedata after
requiring treats as local within user-defined function __autoload().
?>
Content of index.php5:
<?php
// declare handler for any unknown class request
function __autoload($aClassName)
{
// load the class
require_once ($aClassName . '.class.php5');
// create a reference to the static constructor's operation
$staticConstructorReference = array($aClassName, '_construct');
page2.php
<?php
require_once('classLoader.php');
session_start();
echo $_SESSION['testObj']->showProp(); // displays foo
?>
Works with multiple session objects as well. Tested on a Win2k/IIS machine.
The following might provide a good work-around for throwing exceptions from the __autoload function when a file containing the
correct class doesn't exists.
function __autoload ($class_name) {
$file = 'system/objects/' . $class_name . '.inc.php';
if (!file_exists ($file)) {
return eval ("class $class_name {" .
" function $class_name () {" .
" throw new Exception ();" .
" }" .
"}");
}
require_once ($file);
}
Cheers,
Nolan
$args
[,
$...
]] )
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.
Note: Parent constructors are not called implicitly if the child class defines a constructor. In order to run a
parent constructor, a call to parent::__construct() within the child constructor is required.
Example #1 using new unified constructors
<?php
classBaseClass{
function__construct(){
print"InBaseClassconstructor\n";
}
}
classSubClassextendsBaseClass{
function__construct(){
parent::__construct();
print"InSubClassconstructor\n";
}
}
$obj=newBaseClass();
$obj=newSubClass();
?>
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.
Unlike with other methods, PHP will not generate an E_STRICT level error message when __construct() is
overridden with different parameters than the parent __construct() method has.
As of PHP 5.3.3, methods with the same name as the last element of a namespaced class name will no longer
be treated as constructor. This change doesn't affect non-namespaced classes.
Example #2 Constructors in namespaced classes
<?php
namespaceFoo;
classBar{
publicfunctionBar(){
//treatedasconstructorinPHP5.3.0-5.3.2
//treatedasregularmethodasofPHP5.3.3
}
}
?>
Destructor
void __destruct ( void )
PHP 5 introduces a destructor concept similar to that of other object-oriented languages, such as C++. The
destructor method will be called as soon as there are no other references to a particular object, or in any order
during the shutdown sequence.
Example #3 Destructor Example
<?php
classMyDestructableClass{
function__construct(){
print"Inconstructor\n";
$this->name="MyDestructableClass";
}
function__destruct(){
print"Destroying".$this->name."\n";
}
}
$obj=newMyDestructableClass();
?>
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.
The destructor will be called even if script execution is stopped using exit(). Calling exit() in a destructor will
prevent the remaining shutdown routines from executing.
Note:
Destructors called during the script shutdown have HTTP headers already sent. The working directory in the
script shutdown phase can be different with some SAPIs (e.g. Apache).
Note:
Attempting to throw an exception from a destructor (called in the time of script termination) causes a fatal
error.
{
$this->save();
}
public function log($row)
{
$this->rows[] = $row;
}
public function save()
{
echo '<ul>';
foreach ($this->rows as $row)
{
echo '<li>', $row, '</li>';
}
echo '</ul>';
}
}
$logger = new Logger;
$logger->log('Before');
$nonset->foo();
$logger->log('After');
?>
Without the $nonset->foo(); line, Before and After will both be printed, but with the line neither will be printed.
One can however register the destructor or another method as a shutdown function:
<?php
class Logger
{
protected $rows = array();
public function __construct()
{
register_shutdown_function(array($this, '__destruct'));
}
}
$logger = new Logger;
$logger->log('Before');
$nonset->foo();
$logger->log('After');
?>
Now Before will be printed, but not After, so you can see that a shutdown occurred after Before.
/**
* An indentifier
* @var string
*/
private $name;
/**
* A reference to another Foo object
* @var Foo
*/
private $link;
public function __construct($name) {
$this->name = $name;
}
public function setLink(Foo $link){
$this->link = $link;
}
public function __destruct() {
echo 'Destroying: ', $this->name, PHP_EOL;
}
}
// create two Foo objects:
$foo = new Foo('Foo 1');
$bar = new Foo('Foo 2');
// make them point to each other
$foo->setLink($bar);
$bar->setLink($foo);
// destroy the global references to them
$foo = null;
$bar = null;
// we now have no way to access Foo 1 or Foo 2, so they OUGHT to be __destruct()ed
// but they are not, so we get a memory leak as they are still in memory.
//
// Uncomment the next line to see the difference when explicitly calling the GC:
// gc_collect_cycles();
//
// see also: http://www.php.net/manual/en/features.gc.php
//
// create two more Foo objects, but DO NOT set their internal Foo references
// so nothing except the vars $foo and $bar point to them:
$foo = new Foo('Foo 3');
$bar = new Foo('Foo 4');
// destroy the global references to them
$foo = null;
$bar = null;
// we now have no way to access Foo 3 or Foo 4 and as there are no more references
// to them anywhere, their __destruct() methods are automatically called here,
// BEFORE the next line is executed:
echo 'End of script', PHP_EOL;
?>
This will output:
Destroying: Foo 3
Destroying: Foo 4
End of script
Destroying: Foo 1
Destroying: Foo 2
But if we uncomment the gc_collect_cycles(); function call in the middle of the script, we get:
Destroying: Foo 2
Destroying: Foo 1
Destroying: Foo 3
Destroying: Foo 4
End of script
As may be desired.
NOTE: calling gc_collect_cycles() does have a speed overhead, so only use it if you feel you need to.
}
$foobar = new Foobar();
$foobar->baz = new Foobar();
try {
fail($foobar); // Send foobar to func that throws an Exception
} catch( Exception $e ) {
print $e->getMessage(); // Exception A will be caught and printed, as expected.
}
$foobar = null; // clearing foobar, and its property $baz
try {
print 'Exception B:';// this will be printed
// output stops here.
throw new Exception("Exception B!");
} catch( Exception $e ) {
print $e->getMessage(); // doesn't happen
}
print 'End'; // this won't be printed
?>
function __construct() {
parent:: __construct(); // use parent method to call parent class
echo 'bar';
}
}
class foobar extends bar {
function __construct() {
parent:: __construct();
echo 'foobar';
}
}
$show = new foobar();
/* result is foobarfoobar */
?>
function __construct1($a1)
{
echo('__construct with 1 param called: '.$a1.PHP_EOL);
}
function __construct2($a1,$a2)
{
echo('__construct with 2 params called: '.$a1.','.$a2.PHP_EOL);
}
function __construct3($a1,$a2,$a3)
{
echo('__construct with 3 params called: '.$a1.','.$a2.','.$a3.PHP_EOL);
}
}
$o = new A('sheep');
$o = new A('sheep','cat');
$o = new A('sheep','cat','dog');
// results:
// __construct with 1 param called: sheep
// __construct with 2 params called: sheep,cat
// __construct with 3 params called: sheep,cat,dog
?>
function __cons_fullname($args=null) {
if (! is_array($args) || count($args) < 2) {
echo "Error: Not enough parameters (Constructor fullname(string $firstname, string $lastname)) !!!\n";
}
else {
$a = (string) $args["firstname"];
$b = (string) $args["lastname"];
$c = $a . " " . $b;
echo "Constructor fullname(): Result = $c\n";
}
}
} // class
// --> these two lines are the equivalent
$obj1 = new TestClass();
$obj2 = new TestClass("default");
// --> these two are specialized
// should be read as "$obj3 = new TestClass, min(99.7, 99.83);"
$obj3 = new TestClass("min", array(99.7, 99.83));
// should be read as "$obj4 = new TestClass, fullname("John", "Doe");"
$obj4 = new TestClass("fullname", array("firstname" => "John", "lastname" => "Doe"));
// --> these two lack parameters
$obj5 = new TestClass("min", array());
$obj6 = new TestClass("fullname");
?>
?>
If you are not aware of the parameter list, you could use the following in __construct to call up the chain:
<?php
$args = func_get_args();
call_user_func_array(array($this, 'parent::__construct'), $args);
?>
{
static $instance =null;
if (is_null($instance))
{
//requiring the class file
require_once 'bookingSession.php';
//request the instance
$instance = new bookingSession();
}
return $instance;
}
?>
The __destruct was actually being called each time I grabbed this class with
<?php $session =& Factory::getBookingSession(); ?>
Only after the destruct ran once or twice the construct would run. Obviously causing some major problems.
MY SOLUTION: was to call the register shutdown function in my fake session construct, like so:
<?php
class bookingSession {
function __construct(){
$session =& Factory::getSession();
$this->setProperties( unserialize(base64_decode( $session->get('bookingSession') )) );
$this->setProperties(JRequest::get('post'));
register_shutdown_function(array($this,"destruct"));
}
*/
function destruct() {
$session =& JFactory::getSession();
$session->set( 'bookingSession', base64_encode (serialize ($this)) );
}
}
?>
}
}
function __destruct(){
echo "Ack! I'm being destroyed!";
}
}
?>
This makes it much easier to use your script with fewer compatibility issues.
<?php
class baseclass
{
function baseclass()
{
echo "constructor is called"."<br>" ;
}
}
class deriviedclass extends baseclass
{
function deriviedclass()
{
parent::baseclass();
}
}
$object_base=new baseclass();
$object_derive=new deriviedclass();
?>
Out put is
constructor is called
constructor is called
<?php
class db {
//...
private static $instance;
//...
public static function singleton() {
if(!isset(self::$instance)) {
$c = __CLASS__;
self::$instance = new $c();
}
return self::$instance;
}
//...
}
?>
}
}
$obj1 = new MyClassA("Hello", "World!"); // INSTANTIATE MyClassA
$d1 = $obj1->data1;
$d2 = $obj1->data2;
?>
If your class DOES NOT CONTAIN instance members or you DO NOT want to instantiate it, then there is no reason to initialize it
or use __construct().
<?php
class MyClassB {
const DATA1 = "Hello";
public static $data2 = "World!";
}
$obj1 = new MyClassB(); // INSTANTIATE MyClassB - NO error.
$d1 = $obj1::DATA1; // ERROR
$d2 = $obj1::data2; // ERROR
$d1 = MyClassB::DATA1; // ok
$d2 = MyClassB::$data2; // ok
?>
The fact that $obj1 is useless and cannot be used as a reference, is further evidence that MyClassB objects should not be
instantiated. NOTICE that MyClassB does not use private members or functions to make it behave that way. Rather, it is the
collective nature of all the class members + what ISN'T there.
$this->type = $aType;
$this->name = $aName;
$this->sound = $aSound;
}
/*
* define the sorting rules - we will sort all Animals by name.
*/
public static function compare($a, $b)
{
if($a->name < $b->name) return -1;
else if($a->name == $b->name) return 0;
else return 1;
}
/*
* a String representation for all Animals.
*/
public function __toString()
{
return "$this->name the $this->type goes $this->sound";
}
}
?>
Trying to instantiate an object of type Animal will not work...
$myPet = new Animal("Parrot", "Captain Jack", "Kaaawww!"); // throws Fatal Error: cannot instantiate abstract class Animal.
Declaring Animal as abstract is like killing two birds with one stone. 1. We stop it from being instantiated - which means we
do not need a private __construct() or a static getInstance() method, and 2. We can use it for polymorphic behavior. In our
case here, that means "__construct", "__toString" and "compare" will be called for all subclasses of Animal that have not
defined their own implementations.
The following subclasses use parent::__construct(), which sends all new data to Animal. Our Animal class stores this data and
defines functions for polymorphism to work... and the best part is, it keeps our subclass defs super short and even sweeter.
<?php
class Dog extends Animal{
public function __construct($name){
parent::__construct("Dog", $name, "woof!");
}
}
class Cat extends Animal{
public function __construct($name){
parent::__construct("Cat", $name, "meeoow!");
}
}
class Bird extends Animal{
public function __construct($name){
parent::__construct("Bird", $name, "chirp chirp!!");
}
}
# create a PHP Array and initialize it with Animal objects
$animals = array(
new Dog("Fido"),
new Bird("Celeste"),
new Cat("Pussy"),
new Dog("Brad"),
new Bird("Kiki"),
new Cat("Abraham"),
new Dog("Jawbone")
);
# sort $animals with PHP's usort - calls Animal::compare() many many times.
usort($animals, array("Animal", "compare"));
# print out the sorted results - calls Animal->__toString().
foreach($animals as $animal) echo "$animal<br>\n";
?>
The results are "sorted by name" and "printed" by the Animal class:
Abraham the Cat goes meeoow!
Brad the Dog goes woof!
Celeste the Bird goes chirp chirp!!
Fido the Dog goes woof!
Jawbone the Dog goes woof!
Kiki the Bird goes chirp chirp!!
Pussy the Cat goes meeoow!
Using parent::__construct() in a subclass and a super smart base class, gives your child objects a headstart in life, by
alleviating them from having to define or handle several error and exception routines that they have no control over.
Notice how subclass definitions are really short - no variables or functions at all, and there is no private __construct()
method anywhere? Notice how objects of type Dog, Cat, and Bird are all sorted by our base class Animal? All the class
definitions above address several issues (keeping objects from being instantiated) and enforces the desired, consistent, and
reliable behavior everytime... with the least amount of code. In addition, new extenstions can easily be created. Each subclass
is now super easy to redefine or even extend... now that you can see a way to do it.
KK 16-May-2008 01:57
I ran into an interesting (and subtle) code error while porting some code to PHP 5.2.5 from PHP 4.4.8 that I think illustrates
a noteworthy semantic.
I have a hierarchy of classes with both styles of constructors but where one in the middle was missing the __construct()
function (it just had the old-style one that called the (nonexistent) __construct()). It worked fine in PHP4 but caused an
endless loop (and stack overflow) in PHP5. I believe what happened is that in PHP4 the old-style constructor was not called,
but in PHP5 it was (due to the "emulation" of PHP4), and since _construct() wasn't defined for that class, the call to $this>__construct() caused a looping call to the original (lowest child) constructor.
}
}
One would think that the references to the objects contained in the singleton would be destroyed first, but this is not the
case. In fact, this won't work even if you reverse the paradigm and store a reference to EncryptedComms in every object you'd
like to be destroyed before it.
In short, when a script die()s, there doesn't seem to be any way to predict the order in which the destructors will fire.
// Class Function.
// Class Function.
public function functionB( ) {
}
}
class C extends A {
// Class Function.
protected, I can easily do it! Notice that an extra Method was added to Class B ... this is because Class B has extra
functionality specific to itself. Same applies to Class C.
Why don't Class B and Class C extend from Class A? Because Class A has a public Constructor, which pretty much defies the
point of this solution.
This solution is not perfect however and has some flaws.
Good Luck,
}
?>
You could then also use register_shutdown_function(...) to call a static class method, which loops through ALL the objects and
calls the $obj->close routine.
<?php
# register a shutdown function which triggers before destructors (hence when such results are desired for a Session class, etc)
register_shutdown_function("example::destructors");
class example {
}
This will probably give some people new ideas, and other will probably be confused. Happy coding...
NOTE: I didnt test the code(s) above. They were a rewritten example from my frameworks.
?>
class Example
{
function __construct()
{
echo "do some basic stuff here";
}
function Example($arg)
{
echo $arg;
}
}
You then can call with or without arguments without having notices and/or warnings thrown at you... Of course this is limited
but if you don't need something complex this can help to get the job done in some situations. I believe you could also add
arguments to the __construct() function and as long as it is different than Example() 's args you would be fine. Although I
have yet to test this.
destruct: c: func 2
destruct: d: global 2
destruct: a: global 1
conclusions:
destructors are always called on script end.
destructors are called in order of their "context": first functions, then global objects
objects in function context are deleted in order as they are set (older objects first).
objects in global context are deleted in reverse order (older objects last)
shutdown functions are called before the destructors.
shutdown functions are called in there "register" order. ;)
regards, J
/**
* Prevent an object from being constructed.
*/
private function __construct( ) {}
/**
* Function to return the instance of this class.
*/
public static function getInstance( ) {
if (is_null(self::$oInst)) {
self::$oInst = new A( );
}
return self::$oInst;
}
}
?>
*/
private function __construct( ) {
}
/**
* Function To Return An Instance Of This Class.
*/
public static function getNewInstance( ) {
return new A( );
}
}
?>
In some code, if I try to do the following:
<?php
$Obj = A::getNewInstance( );
?>
PHP in this case will not fail with a fatal error and instead the variable '$Obj' becomes an Object with an instance of Class
'A'.
This is a simple example and pretty advanced things can be done using this method. I am sure advanced developers are aware of
this so this is just a little note going out to new PHP developers.
#
class ParentDestructClass extends AncestorClass {
function __destruct() {
echo 'ParentDestructClass: destructing itself';
parent::__destruct();
}
}
#
class EmptyDestructClass extends AncestorClass {
function __destruct() {
echo 'EmptyDestructClass: destructing itself';
}
}
#
class NoDestructClass extends AncestorClass {
}
#--echo '<hr>';
$p=new ParentDestructClass();
unset($p);
echo '<hr>';
$e=new EmptyDestructClass();
unset($e);
echo '<hr>';
$n=new NoDestructClass();
unset($n);
echo '<hr>';
?>
which displays:
--ParentDestructClass: destructing itself
AncestorClass: destructing ParentDestructClass
--EmptyDestructClass: destructing itself
--AncestorClass: destructing NoDestructClass
---
}
public function __destruct()
{
chdir($this->_cwd);
}
}
?>
Be aware the booty trap!
http://bugs.php.net/bug.php?id=34206
01-Oct-2006 11:03
This is a simple thing to bear in mind but it's also easy to forget it. When chaining object constructors and destructors,
always remember to call the superclass __construct() method in the subclass __construct() so that all superclass members are
properly initialized before you start initializing the ones belonging to your subclass.
Also, you will usually want to do your own cleanup first in your subclass __destruct() method so you will probably want to call
the superclass __destruct() as the last thing in your subclass so that you can use resources defined in the superclass during
the cleanup phase.
For example, if your superclass includes a database connection and your subclass __destruct method commits things to the
database then if you call the superclass destruct before doing so then the database connection will no longer be valid and you
will be unable to commit your changes.
protected $parent;
public function __construct(&$p) {
# $this->parent=&$p;
}
class containing {
protected $contained;
public function __construct() {
$this->contained=new contained($this);
}
public function __destruct() {
// unset($this->contained);
print 'containing ';
}
}
new containing();
?>
Will output
containing contained
After uncommenting the // comment, the output will change to
contained containing
Adding a reference from the contained class to the containing one (the # comment) will not change that, but beware, because it
can cause random errors in other destructors in the parts of the script which seem unrelated! (PHP Version 5.1.2)
__construct and __destruct must be declared public in any class that you intend to instantiate with new. However, in an
abstract (or never-instantiated base) class you can declare them private or protected, and subclasses can still refer to them
via parent::__construct (!) (tested in PHP 5.1.2).
09-Feb-2006 10:55
(Refering to: caliban at darklock dot com)
To force a constructor always to be called, and still be able to define a constructor on a derived class use the model below.
Ideal for module architectures, because you only have to know the file and classname to construct an object.
<?php
class Parameter {}
abstract class BaseClass
{
protected $param;
}
}
$c = new Child();
?>
Added advantage is that Parent doesn't need to have the method myConstruct(), and that you're using constructors like they were
intended.
}
function foo($num)
{
$noisy = new Noisy($num);
//$noisy->me = $noisy; // Uncomment this line to create a cyclic reference
}
for ($i = 0; $i < 10; ++$i)
foo($i);
?>
As it stands, the destructor of class 'Noisy' will be called on '$noisy' when it goes out of scope in function 'foo', but
uncommenting the second line in function 'foo' will prevent this, and cause a memory leak.
See http://bugs.php.net/bug.php?id=33595 for a bug report, which reads as if this is not likely to get fixed in the near
future, so watch out!
}
?>
04-Mar-2005 03:48
> To caliban at darklock dot com: Why not just define
> a dummy constructor
Because you don't always get to modify your base classes. Once you get beyond the "build to suit" range of software
development, you end up having to work with other people's code, and sometimes you just plain can't change it. When Bob is in
charge of making changes to that object, you can't add a dummy constructor. You have to tell Bob to do it, and until Bob does
it, you don't get it. So if you want to hit your deadlines, you don't count on Bob caring enough about your job to make the
changes you want... you work around it. It might be convenient for *you* to have a constructor on that object, but when you're
only one of several thousand people that are using it, your convenience isn't generally among the design criteria.
Smaller projects where you can add whatever you want wherever you want will not have this problem, in which case the dummy
constructor is indeed a better solution.
24-Feb-2005 01:08
To caliban at darklock dot com: Why not just define a dummy constructor <?PHP function __construct() {} ?> in the base class?
This adds little overhead, and allows you to both extend the class worry-free and later add construct functionality to the base
class.
And now, about destructors: I haven't seen this clarified anywhere in the manual, but object destructors are called implicitly
at script shutdown for all objects that still exist at that Tpoint. his happens *after* any shutdown functions set with <?PHP
register_shutdown_function() ?> have been called.
Objects appear to be destructed in the order they were defined, which means you have to be careful with destruct methods that
rely on the functionality of other objects (e.g. on a database-handler) as they will have shut down already.
Visibility
The visibility of a property or method can be defined by prefixing the declaration with the keywords public,
protected or private. Class members declared public can be accessed everywhere. Members declared protected
can be accessed only within the class itself and by inherited and parent classes. Members declared as private
may only be accessed by the class that defines the member.
Property Visibility
Class properties must be defined as public, private, or protected. If declared using var, the property will be
defined as public.
Example #1 Property declaration
<?php
/**
*DefineMyClass
*/
classMyClass
{
public$public='Public';
protected$protected='Protected';
private$private='Private';
functionprintHello()
{
echo$this->public;
echo$this->protected;
echo$this->private;
}
}
$obj=newMyClass();
echo$obj->public;//Works
echo$obj->protected;//FatalError
echo$obj->private;//FatalError
$obj->printHello();//ShowsPublic,ProtectedandPrivate
/**
*DefineMyClass2
*/
classMyClass2extendsMyClass
{
//Wecanredeclarethepublicandprotectedmethod,butnotprivate
protected$protected='Protected2';
functionprintHello()
{
echo$this->public;
echo$this->protected;
echo$this->private;
}
}
$obj2=newMyClass2();
echo$obj2->public;//Works
echo$obj2->private;//Undefined
echo$obj2->protected;//FatalError
$obj2->printHello();//ShowsPublic,Protected2,Undefined
?>
Note: The PHP 4 method of declaring a variable with the var keyword is still supported for compatibility
reasons (as a synonym for the public keyword). In PHP 5 before 5.1.3, its usage would generate an E_STRICT
warning.
Method Visibility
Class methods may be defined as public, private, or protected. Methods declared without any explicit visibility
keyword are defined as public.
Example #2 Method Declaration
<?php
/**
*DefineMyClass
*/
classMyClass
{
//Declareapublicconstructor
publicfunction__construct(){}
//Declareapublicmethod
publicfunctionMyPublic(){}
//Declareaprotectedmethod
protectedfunctionMyProtected(){}
//Declareaprivatemethod
privatefunctionMyPrivate(){}
//Thisispublic
functionFoo()
{
$this->MyPublic();
$this->MyProtected();
$this->MyPrivate();
}
}
$myclass=newMyClass;
$myclass->MyPublic();//Works
$myclass->MyProtected();//FatalError
$myclass->MyPrivate();//FatalError
$myclass->Foo();//Public,ProtectedandPrivatework
/**
*DefineMyClass2
*/
classMyClass2extendsMyClass
{
//Thisispublic
functionFoo2()
{
$this->MyPublic();
$this->MyProtected();
$this->MyPrivate();//FatalError
}
}
$myclass2=newMyClass2;
$myclass2->MyPublic();//Works
$myclass2->Foo2();//PublicandProtectedwork,notPrivate
classBar
{
publicfunctiontest(){
$this->testPrivate();
$this->testPublic();
}
publicfunctiontestPublic(){
echo"Bar::testPublic\n";
}
privatefunctiontestPrivate(){
echo"Bar::testPrivate\n";
}
}
classFooextendsBar
{
publicfunctiontestPublic(){
echo"Foo::testPublic\n";
}
privatefunctiontestPrivate(){
echo"Foo::testPrivate\n";
}
}
$myFoo=newfoo();
$myFoo->test();//Bar::testPrivate
//Foo::testPublic
?>
}
privatefunctionbar()
{
echo'Accessedtheprivatemethod.';
}
publicfunctionbaz(Test$other)
{
//Wecanchangetheprivateproperty:
$other->foo='hello';
var_dump($other->foo);
//Wecanalsocalltheprivatemethod:
$other->bar();
}
}
$test=newTest('test');
$test->baz(newTest('other'));
?>
Visibility
<?php
class Item
{
/**
* This is INSIDE CODE because it is written INSIDE the class.
*/
public $label;
public $price;
}
/**
* This is OUTSIDE CODE because it is written OUTSIDE the class.
*/
$item = new Item();
$item->label = 'Ink-Jet Tatoo Gun';
$item->price = 49.99;
?>
Ok, that's simple enough... I got it inside and out. The big problem with this is that the Item class is COMPLETELY IGNORANT
in the following ways:
* It REQUIRES OUTSIDE CODE to do all the work AND to know what and how to do it -- huge mistake.
* OUTSIDE CODE can cast Item properties to any other PHP types (booleans, integers, floats, strings, arrays, and objects etc.)
-- another huge mistake.
Note: we did it correctly above, but what if someone made an array for $price? FYI: PHP has no clue what we mean by an Item,
especially by the terms of our class definition above. To PHP, our Item is something with two properties (mutable in every way)
and that's it. As far as PHP is concerned, we can pack the entire set of Britannica Encyclopedias into the price slot. When
that happens, we no longer have what we expect an Item to be.
INSIDE CODE should keep the integrity of the object. For example, our class definition should keep $label a string and $price a
float -- which means only strings can come IN and OUT of the class for label, and only floats can come IN and OUT of the class
for price.
<?php
class Item
{
/**
* Here's the new INSIDE CODE and the Rules to follow:
*
* 1. STOP ACCESS to properties via $item->label and $item->price,
* by using the protected keyword.
* 2. FORCE the use of public functions.
* 3. ONLY strings are allowed IN & OUT of this class for $label
* via the getLabel and setLabel functions.
* 4. ONLY floats are allowed IN & OUT of this class for $price
* via the getPrice and setPrice functions.
*/
protected $label = 'Unknown Item'; // Rule 1 - protected.
protected $price = 0.0; // Rule 1 - protected.
public function getLabel() { // Rule 2 - public function.
return $this->label; // Rule 3 - string OUT for $label.
}
public function getPrice() { // Rule 2 - public function.
function __construct () {
$this->reallyPrivate = 'secret';
$this->justReadOnly = 'read only';
}
function __get ($what) {
switch ($what) {
case 'justReadOnly':
return $this->$what;
default:
# Generate an error, throw an exception, or ...
return null;
}
}
function __isset ($what) {
$val = $this->__get($what);
return isset($val);
}
}
$rom = new ReadOnlyMembers();
var_dump($rom->justReadOnly); // string(9) "read only"
$rom->justReadOnly = 'new value'; // Fatal error
var_dump($rom->reallyPrivate); // Fatal error
?>
class b extends a
{
function getFoo($ainstance)
{
return $ainstance->foo;
}
}
$aainstance=someexternalfunction();
$binstance=new b;
$aafoo=$binstance->getFoo($aainstance);
?>
In the below example Bar knows about the existence of _test() in Foo because they inherited this method from the same parent.
It does not matter that it was abstract in the parent.
<?php
abstract class Base {
abstract protected function _test();
}
3. private methods never participate in the in the overriding because these methods are not visible in the child classes.
Example:
class a {
private function my(){
print "parent:my";
}
public function getmy(){
$this->my();
}
}
class b extends a{
private function my(){
print "base:my";
}
}
$x = new b();
$x->getmy(); // parent:my
4. While overriding decreasing access specifier is not allowed
class a {
public function my(){
print "parent:my";
}
}
class b extends a{
private function my(){
print "base:my";
}
}
//Fatal error: Access level to b::my() must be public (as in class a)
do not see the private methods of parent class and vice versa also.
As a result, parents and children can have different implementations of the "same" private methods, depending on where you call
them (e.g. parent or child class instance). Why? Because private methods are visible only for the class that defines them and
the child class does not see the parent's private methods. If the child doesn't see the parent's private methods, the child
can't override them. Scopes are different. In other words -- each class has a private set of private variables that no-one
else has access to.
A sample demonstrating the percularities of private methods when extending classes:
<?php
abstract class base {
public function inherited() {
$this->overridden();
}
private function overridden() {
echo 'base';
}
}
class child extends base {
private function overridden() {
echo 'child';
}
}
$test = new child();
$test->inherited();
?>
Output will be "base".
If you want the inherited methods to use overridden functionality in extended classes but public sounds too loose, use
protected. That's what it is for:)
A sample that works as intended:
<?php
abstract class base {
public function inherited() {
$this->overridden();
}
protected function overridden() {
echo 'base';
}
}
class child extends base {
protected function overridden() {
echo 'child';
}
}
$test = new child();
$test->inherited();
?>
Output will be "child".
WHEN do I use public, protected or private keyword? Here's the default behavior.
<?php
class Example
{
/* use PUBLIC on variables and functions when:
* 1. outside-code SHOULD access this property or function.
* 2. extending classes SHOULD inherit this property or function.
*/
public $var1;
public function someFunction_1() { }
/* use PROTECTED on variables and functions when:
* 1. outside-code SHOULD NOT access this property or function.
* 2. extending classes SHOULD inherit this property or function.
*/
protected $var2;
protected function someFunction_2() { }
/* use PRIVATE on variables and functions when:
* 1. outside-code SHOULD NOT access this property or function.
* 2. extending classes SHOULD NOT inherit this property or function.
*/
private $var3;
private function someFunction_3() { }
}
# these are the only valid calls outside-code can make on Example objects:
$obj1 = new Example(); // instantiate
$var1 = $obj1->var1; // get public data
$obj1->var1 = 35; // set public data
$obj1->someFunction_1(); // call public function
?>
Now try extending the class...
<?php
class Example_2 extends Example
{
// this class inherits the following properties and functions.
public $var1;
public function someFunction_1() { }
protected $var2;
protected function someFunction_2() { }
}
# these are the only valid calls outside-code can make on Example_2 objects:
$obj2 = new Example_2(); // instantiate
$var2 = $obj2->var1; // get public data
$obj2->var1 = 45; // set public data
$obj2->someFunction_1(); // call public function
?>
<?php
class MyClass_1{
public $pubVal = 'Hello World!';
protected $proVal = 'Hello FROM MyClass_1';
private $priVal = 'Hello TO MyClass_1';
public function __toString(){
return "MyClass_1[public=$this->pubVal, protected=$this->proVal, private=$this->priVal]";
}
}
class MyClass_2 extends MyClass_1{
public function __toString(){
return "MyClass_2[public=$this->pubVal, protected=$this->proVal, private=$this->priVal]";
}
}
class MyClass_3 extends MyClass_2{
private $priVal = 'Hello TO MyClass_3';
public function __toString(){
return "MyClass_3[public=$this->pubVal, protected=$this->proVal, private=$this->priVal]";
}
}
class MyClass_4 extends MyClass_3{
protected $proVal = 'Hello FROM MyClass_4';
public function __toString(){
return "MyClass_4[public=$this->pubVal, protected=$this->proVal, private=$this->priVal]";
}
}
class MyClass_5 extends MyClass_4{
public function __toString(){
return "MyClass_5[public=$this->pubVal, protected=$this->proVal, private=$this->priVal]";
}
}
class MyClass_6 extends MyClass_5{
private $priVal = 'Hello TO MyClass_6';
public function __toString(){
return "MyClass_6[public=$this->pubVal, protected=$this->proVal, private=$this->priVal]";
}
}
echo (new MyClass_1()) . '<br>';
echo (new MyClass_2()) . '<br>';
echo (new MyClass_3()) . '<br>';
echo (new MyClass_4()) . '<br>';
echo (new MyClass_5()) . '<br>';
echo (new MyClass_6()) . '<br>';
?>
The list of extended objects:
MyClass_1[public=Hello World!, protected=Hello FROM MyClass_1, private=Hello TO MyClass_1]
MyClass_2[public=Hello World!, protected=Hello FROM MyClass_1, private=]
MyClass_3[public=Hello World!, protected=Hello FROM MyClass_1, private=Hello TO MyClass_3]
class B extends A
{
}
$a = new A("a_protected", "a_private");
$other_a = new A("other_a_protected", "other_a_private");
$b = new B("b_protected", "ba_private");
$other_a->print_other($a); //echoes a_protected and a_private
$other_a->print_other($b); //echoes b_protected and ba_private
$b->print_other($a); //echoes a_protected and a_private
?>
class Bar {
private $a;
public function __construct() {
$foo = new Foo();
var_dump($foo->get('a')); // results in: string(11) "Value of $a"
}
}
$bar = new Bar();
?>
}
$a = new A();
A::bar($a);
?>
It's working.
Object Inheritance
Inheritance is a well-established programming principle, and PHP makes use of this principle in its object model.
This principle will affect the way many classes and objects relate to one another.
For example, when you extend a class, the subclass inherits all of the public and protected methods from the
parent class. Unless a class overrides those methods, they will retain their original functionality.
This is useful for defining and abstracting functionality, and permits the implementation of additional
functionality in similar objects without the need to reimplement all of the shared functionality.
Note:
Unless autoloading is used, then classes must be defined before they are used. If a class extends another,
then the parent class must be declared before the child class structure. This rule applies to classes that
inherit other classes and interfaces.
Example #1 Inheritance Example
<?php
classfoo
{
publicfunctionprintItem($string)
{
echo'Foo:'.$string.PHP_EOL;
}
publicfunctionprintPHP()
{
echo'PHPisgreat.'.PHP_EOL;
}
}
classbarextendsfoo
{
publicfunctionprintItem($string)
{
echo'Bar:'.$string.PHP_EOL;
}
}
$foo=newfoo();
$bar=newbar();
$foo->printItem('baz');//Output:'Foo:baz'
$foo->printPHP();//Output:'PHPisgreat'
$bar->printItem('baz');//Output:'Bar:baz'
$bar->printPHP();//Output:'PHPisgreat'
?>
Object Inheritance
MULTI INHERITANCE
<?php
class A
{
public function meth1()
{
echo "meth1";
}
public function meth2()
{
B::meth2();
}
}
class B
{
public function meth2()
{
echo "<br/>meth2 ".get_class($this);
}
}
$a = new A();
$a->meth2();
$b = new B();
$b->meth2();
?>
OZ 14-Nov-2010 03:24
Model for Mixins pattern:
<?php
interface IMixinsCaller
{
public function __mixin_get_property($property);
public function __mixin_set_property($property, $value);
public function __mixin_call($method, $value);
}
abstract class MixinsCaller implements IMixinsCaller
{
protected $mixins = array();
public function __call($name, $arguments)
{
if (!empty($this->mixins))
{
foreach ($this->mixins as $mixin)
{
if (method_exists($mixin, $name))
{
return call_user_func_array(array($mixin, $name), $arguments);
}
}
}
trigger_error('Non-existent method was called in class '.__CLASS__.': '.$name, E_USER_WARNING);
}
public function __mixin_get_property($property)
{
if (property_exists($this, $property))
{
return $this->$property;
}
trigger_error('Non-existent property was get in class '.__CLASS__.': '.$property, E_USER_WARNING);
}
public function __mixin_set_property($property, $value)
{
if (property_exists($this, $property))
{
return $this->$property = $value;
}
trigger_error('Non-existent property was set in class '.__CLASS__.': '.$property, E_USER_WARNING);
}
public function __mixin_call($method, $value)
{
if (method_exists($this, $method))
{
return call_user_func_array(array($this, $method), $value);
}
trigger_error('Non-existent method was called in class '.__CLASS__.': '.$method, E_USER_WARNING);
}
public function AddMixin($mixin)
{
$this->mixins[] = $mixin;
}
}
abstract class Mixin
{
/** @var IMixinsCaller $parent_object */
private $parent_object;
public function __construct(IMixinsCaller $parent_object)
{
$this->parent_object = $parent_object;
}
public function __get($property)
{
return $this->parent_object->__mixin_get_property($property);
}
public function __set($property, $value)
{
// object(foo_bar_baz)[1]
// foo_bar
// object(foo_bar_baz)[1]
// foo
// object(foo_bar_baz)[1]
?>
This means that you cannot have one class extend 2 other classes (see the extends keyword). However, you can have one class
extend another, which extends another, and so on.
Example:
<?php
class A {
// more code here
}
class B extends A {
// more code here
}
class C extends B {
// more code here
}
?>
// Error: Given object is not an instance of the class this method was declared in
//$method = new ReflectionMethod($className, $method);
//return $method->invokeArgs($this, $args);
$payload = $this->buildMixinPayload($this->_mixMap, $method, $args);
if(!$payload) throw new Exception('Method ' . $method . ' not found');
list($mixinMethod, list($method, $args)) = $payload;
return $this->$mixinMethod($method, $args);
}
}
}
return false;
}
}
?>
/*
* Circular references are, of course, illegal and will be detected
*/
/*
public function mixinSteakAndKidneyPie($method, $args){
return SteakAndKidneyPie::$method(@$args[0], @$args[1], @$args[2], @$args[3], @$args[4], @$args[5]);
}
//*/
/*
* class tokens are bound at compile time so need to be explicitly declared
* Need to make sure there are enough argument placeholders to cover all mixed in methods of Lunch
* Late static binding may improve this situation
*/
public function mixinLunch($method, $args){
return Lunch::$method(@$args[0], @$args[1], @$args[2], @$args[3], @$args[4], @$args[5]);
}
/*
* everything to be mixed in must be public
* protected/private methods called from within mixed in methods will fail
*/
public $filling = 'Steak and Kidney';
/*
* order of mixin declaration significant
* later declarations override earlier ones
*/
public function mixinSteakAndKidney($method, $args){
return SteakAndKidney::$method(@$args[0], @$args[1], @$args[2], @$args[3], @$args[4], @$args[5]);
}
/*
* Pick specific methods like so:
*/
//*
public function getFilling(){
return SteakAndKidney::getFilling();
}
//*/
}
$pie = new SteakAndKidneyPie();
echo $pie->buildPie(' | ');
echo '<br/>Pie ' . $pie->isEdible('is', 'is not') . ' Edible';
/*
OUTPUTS:
Crust | Steak and Kidney | More Crust
Pie is Edible
*/
?>
<?php
abstract class Cheese
{
//can ONLY be inherited by another class
}
class Cheddar extends Cheese
{
}
$dinner = new Cheese; //fatal error
$lunch = new Cheddar; //works!
?>
Three special keywords self, parent and static are used to access properties or methods from inside the class
definition.
Example #2 :: from inside the class definition
<?php
classOtherClassextendsMyClass
{
publicstatic$my_static='staticvar';
publicstaticfunctiondoubleColon(){
echoparent::CONST_VALUE."\n";
echoself::$my_static."\n";
}
}
$classname='OtherClass';
echo$classname::doubleColon();//AsofPHP5.3.0
OtherClass::doubleColon();
?>
When an extending class overrides the parents definition of a method, PHP will not call the parent's method. It's
up to the extended class on whether or not the parent's method is called. This also applies to Constructors and
Destructors, Overloading, and Magic method definitions.
Example #3 Calling a parent's method
<?php
classMyClass
{
protectedfunctionmyFunc(){
echo"MyClass::myFunc()\n";
}
}
classOtherClassextendsMyClass
{
//Overrideparent'sdefinition
publicfunctionmyFunc()
{
//Butstillcalltheparentfunction
parent::myFunc();
echo"OtherClass::myFunc()\n";
}
}
$class=newOtherClass();
$class->myFunc();
?>
}
echo A::$B . A::B . A::B(); # Outputs: 123
?>
$args=func_get_args();
/* $args will contain any argument passed to __construct.
* Your formal argument doesnt influence the way func_get_args() works
*/
call_user_func_array(array('parent',__FUNCTION__),$args);
}
// but this is not for __construct only
function anyMethod() {
$args=func_get_args();
call_user_func_array(array('parent',__FUNCTION__),$args);
}
// Note: php 5.3.0 will even let you do
function anyMethod() {
//Needs php >=5.3.x
call_user_func_array(array('parent',__FUNCTION__),func_get_args());
}
}
?>
$sFuncName = $bt["function"];
// constructor or destructor called (old fashion way)?
if( ( $bIsConstruct = ( $sFuncName == $sClassName )) or ( $sFuncName == "_".$sClassName ) )
{
// get parent constructor/destructor
$sFuncName = (( !$bIsConstruct ) ? "_" : "" ).$sParentClassName;
if( $sFuncName == (( !$bIsConstruct ) ? "_" : "" ).$sClassName )
{ return; }
}
if( method_exists( $sParentClassName, $sFuncName ))
{
// If there are parameters specified, use these
$args = func_get_args();
if( !suIsValidArray( $args ))
{ $args = &$bt["args"]; } // otherwise use previous function parameters if any
$iCount = 0;
$sArgs = "";
if( suIsValidArray( $args, $iCount ))
{
for( $i = 0; $i < $iCount; $i++ )
{ $sArgs.="&$"."args[$i]".( $i < ($iCount-1) ? "," : "" ); }
}
// Simple, evaluate it, this is possible because it is done inside the class itself, and
// the parent class is already created because is a part of the class structure, no worry, no scope issues
eval( "$"."result = $sParentClassName::".$sFuncName."($sArgs);" );
return @$result;
}
}
?>
/**
* Test property for using indirect default value
*/
protected static $other = 'cA';
{
self::$other = $val; // Set a value in this scope.
}
}
class cB extends cA
{
/**
* Test property with redefined default value
*/
protected static $item = 'Bar';
/**
* Now we drop redeclaring the setOther() method, use cA with 'self::' just for fun.
*/
}
class cD extends cA
{
/**
* Test property with redefined default value
*/
protected static $item = 'Foxtrot';
/**
* Now we drop redeclaring all methods to complete this issue.
*/
}
cB::setOther('cB'); // It's cB::method()!
cB::method(); // It's cA::method()!
cC::setOther('cC'); // It's cA::method()!
cC::method(); // It's cC::method()!
cD::setOther('cD'); // It's cA::method()!
cD::method(); // It's cA::method()!
/**
* Results: ->
* Foo
* cB
* Tango
* cC
* Foo
* cD
*
* What the hell?! :)
*/
?>
function set_data () {
b::set();
}
}
class b {
static function set () {
// $this->data = 'Data from B !';
// using this in static function throws a warning ...
caller()->data = 'Data from B !';
}
}
$a = new a();
$a->set_data();
echo $a->data;
?>
Outputs: Data from B !
No warnings or errors !
//Static workaround for php4 - even works with arrays - the trick is accessing the arrays.
//I used the format s_varname for my methods that employ this workaround. That keeps it
//similar to working with actual variables as much as possible.
//The s_ prefix immediately identifies it as a static variable workaround method while
//I'm looking thorugh my code.
function &s_foo($value=null, $remove=null)
{
static $s_var; //Declare the static variable. The name here doesn't matter - only the name of the method matters.
if($remove)
{
if(is_array($value))
{
if(is_array($s_var))
{
foreach($value as $key => $data)
{
unset($s_var[$key]);
}
}
}
else
{
//You can't just use unset() here because the static state of the variable will bring back the value next time
you call the method.
$s_var = null;
unset($s_var);
}
//Make sure that you don't set the value over again.
$value = null;
}
if($value)
{
if(is_array($value))
{
if(is_array($s_var))
{
//$s_var = array_merge($s_var, $value); //Doesn't overwrite values. This adds them - a property of
the array_merge() function.
foreach($value as $key => $data)
{
$s_var[$key] = $data; //Overwrites values.
}
}
else
{
$s_var = $value;
}
}
else
{
$s_var = $value;
}
}
return $s_var;
}
}
static $className;
static function showClassName() {
echo self::$className . "\n";
}
}
class ExampleSubclassOne extends ExampleSuperclass
{
static function setClassName()
{
self::$className = "subclassOne";
}
}
class ExampleSubclassTwo extends ExampleSuperClass
{
static function setClassName()
{
self::$className = "subclassTwo";
}
}
// setting variables for each class
ExampleSubclassOne::setClassName();
ExampleSubclassTwo::setClassName();
ExampleSubclassOne::showClassName(); // output is "subclassTwo"!
// more output:
echo ExampleSubclassOne::$className . "\n"; // output is "subclassTwo"!
echo ExampleSubclassTwo::$className . "\n"; // output is "subclassTwo"
echo ExampleSuperclass::$className . "\n"; // output is "subclassTwo"!
?>
appearantly, any static variables defined in a superclass are directly referenced in subclasses,
and all changes are visible throughout the class heirarchy. care must be taken when using static
class variables.
}
class ExampleClass extends ExampleSuperclass
{
static function classType()
{
return "subclass";
}
}
ExampleClass::doSomething();
// output is "doing something with superclass"!
?>
This can be surprising (it surprised me!) when coming from other object-oriented languages, which would output "doing something
with subclass" in this case.
Static Keyword
Declaring class properties or methods as static makes them accessible without needing an instantiation of the
class. A property declared as static can not be accessed with an instantiated class object (though a static
method can).
For compatibility with PHP 4, if no visibility declaration is used, then the property or method will be treated as if
it was declared as public.
Because static methods are callable without an instance of the object created, the pseudo-variable $this is not
available inside the method declared as static.
Static properties cannot be accessed through the object using the arrow operator ->.
Calling non-static methods statically generates an
E_STRICT
level warning.
Like any other PHP static variable, static properties may only be initialized using a literal or constant;
expressions are not allowed. So while you may initialize a static property to an integer or array (for instance),
you may not initialize it to another variable, to a function return value, or to an object.
As of PHP 5.3.0, it's possible to reference the class using a variable. The variable's value can not be a keyword
(e.g. self, parent and static).
Example #1 Static property example
<?php
classFoo
{
publicstatic$my_static='foo';
publicfunctionstaticValue(){
returnself::$my_static;
}
}
classBarextendsFoo
{
publicfunctionfooStatic(){
returnparent::$my_static;
}
}
printFoo::$my_static."\n";
$foo=newFoo();
print$foo->staticValue()."\n";
print$foo->my_static."\n";//Undefined"Property"my_static
print$foo::$my_static."\n";
$classname='Foo';
print$classname::$my_static."\n";//AsofPHP5.3.0
printBar::$my_static."\n";
$bar=newBar();
print$bar->fooStatic()."\n";
?>
<?php
classFoo{
publicstaticfunctionaStaticMethod(){
//...
}
}
Foo::aStaticMethod();
$classname='Foo';
$classname::aStaticMethod();//AsofPHP5.3.0
?>
Static Keyword
}
?>
In order to define a __destruct function (which is definitely called) we create a new instance in the init() function and
define a destruct() function (which is called from the 'real' one):
<?php
class B
{
static private $autoSave;
static public function init($autoSave)
{
/* emulating __construct() */
self::$autoSave = $autoSave;
new B();
}
static public function destruct()
{
if (self::$autoSave)
self::save();
}
public function __destruct()
{
B::destruct();
}
}
?>
}
?>
public static function init($value) { self::$a = $value; }
public static function getA() { return self::$a; }
}
class B extends A {
protected static $a; // redefine $a for own use
class A
{
private $myProp = 'A';
function set() {
self::$variable = 2;
}
}
class Child2 extends MyParent {
function show() {
echo(self::$variable);
}
}
$c1 = new Child1();
$c1->set();
$c2 = new Child2();
$c2->show(); // prints 2
?>
class User{
const GIVEN = 1; // class constants can't be labeled static nor assigned visibility
public $a=2;
public static $b=3;
*/
protected static $count = 0;
protected static $start = 1;
protected static $colors = array('red','yellow','blue','orange', 'green', 'white');
protected static $sizes = array(4, 6, 8, 10, 12, 16);
public $name;
public $color;
public $size;
public function __construct(){
$this->name = 'RB_' . self::$start++;
$this->color = (int) rand(0, 5);
$this->size = self::$sizes[(int) rand(0, 5)];
self::$count++;
}
public static function getCount(){
return self::$count;
}
public static function setStart($val){
self::$start = $val;
}
/*
* Define the sorting rules for RubberBalls - which is to sort by self::$colors.
* PHP's usort() method will call this function many many times.
*/
public static function compare($a, $b){
if($a->color < $b->color) return -1;
else if($a->color == $b->color) return 0;
else return 1;
}
public function __toString(){
return "RubberBall[
name=$this->name,
color=" . self::$colors[$this->color] . ",
size=" . $this->size . "\"]";
}
}
# RubberBall counts the number of objects created, but allows us to
# set the starting count like so:
RubberBall::setStart(100);
# create a PHP Array and initialize it with (12) RubberBall objects
$balls = array();
for($i = 0; $i < 12; $i++) $balls[] = new RubberBall();
# sort the RubberBall objects. PHP's usort() calls RubberBall::compare() to do this.
usort($balls, array("RubberBall", "compare"));
# print the sorted results - uses the static RubberBall::getCount().
echo 'RubberBall count: ' . RubberBall::getCount() . '<br><br>';
foreach($balls as $ball) echo $ball . '<br>';
I'm running out of room so I have not displayed the output, but it is tested and it works great.
}
public static function showAll(){
//echo ("# Logs::show ".self::getLogsCount());
// show the logs from cero to last
// im starting with php so I use for (...) because its my
// way in AS3.0 but you better keep checking that
$count=self::getLogsCount();
for ($i=0; $i<$count;$i++){
$log=self::$logs[$i];
self::message($log);
}
}
// Yup! a new log!
public static function push($msg){
//self::message("# Logs::push ");
array_push(self::$logs, $msg);
}
// Send a message to the html output.
// Now, a little words about this one, you may want to use an
// special button on your html or somewhere else to show or hide the
// logs, because you are in development and want to keep this thing
// little hidden of your clients. Probably may generate a java script
// variable so instead of [echo "<p>...</p>";] is ok to
// use [echo "<script>logs+=$msg</script>"]
// I dont know, figure it out.
public function message($msg){
echo "<p>- Logs > $msg </p>";
}
}
// Ready to use.
// to call a function in an static class, refer to the function as
// TheClass::theFunction
// remember, php uses :: (sorry, I still learning why because I come from the AS3 world)
Logs::push("log1");
Logs::show();
Logs::push("log2");
Logs::show();
Logs::push("log3");
Logs::showAll();
?>
XYZ::$foo = chr(1);
class XYZ
{
static $foo;
}
if(!is_null($vs))
$s = $vs;
return $s;
}
}
class b extends a { }
class c extends b { }
$a = new a();
$a->v('a');
$aa = new a();
$aa->v('last a');
$c = new c();
$c->v('c');
class A {
static function foo () {
echo "A::foo()";
}
function __construct() {
$this->self = &$GLOBALS['static'][get_class($this)];
$this->self['instances'] += 1;
self::$all_instances += 1;
}
}
class child_a extends root {
}
class child_b extends root {
}
$a1 = new child_a();
$a2 = new child_a();
$a3 = new child_a();
$b1 = new child_b();
$b2 = new child_b();
echo "child_a instances: " . $a3->self['instances'] . " | all_instances: " . child_a::$all_instances . "\n";
echo "child_b instances: " . $b2->self['instances'] . " | all_instances: " . child_b::$all_instances . "\n";
echo "\$GLOBALS['static'] = ";
var_dump($GLOBALS['static']);
?>
// Output:
child_a instances: 3 | all_instances: 5
child_b instances: 2 | all_instances: 5
$GLOBALS['static'] = array(2) {
["child_a"]=>
&array(1) {
["instances"]=>
int(3)
}
["child_b"]=>
&array(1) {
["instances"]=>
int(2)
}
}
<?php
class Foo {
static function find($class) {
$obj = new $class();
return $obj;
}
}
class Bar extends Foo {
static function find() {
return parent::find(__CLASS__);
}
function print_hello() {
echo("hello");
}
}
Bar::find()->print_hello();
?>
06-Apr-2005 03:14
You misunderstand the meaning of inheritance : there is no duplication of members when you inherit from a base class. Members
are shared through inheritance, and can be accessed by derived classes according to visibility (public, protected, private).
The difference between static and non static members is only that a non static member is tied to an instance of a class
although a static member is tied to the class, and not to a particular instance.
That is, a static member is shared by all instances of a class although a non static member exists for each instance of
class.
Thus, in your example, the static property has the correct value, according to principles of object oriented conception.
class Base
{
public $a;
public static $b;
}
class Derived extends Base
{
public function __construct()
{
$this->a = 0;
parent::$b = 0;
}
public function f()
{
$this->a++;
parent::$b++;
}
}
$i1 = new Derived;
$i2 = new Derived;
$i1->f();
echo $i1->a, ' ', Derived::$b, "\n";
$i2->f();
echo $i2->a, ' ', Derived::$b, "\n";
outputs
1 1
1 2
<?php
// This will work.
// Moved the static variable's initialization logic outside the class.
XYZ::$ABC = new ABC();
class XYZ
{
// I'm just declaring the static variable here, but I'm not initializing it.
public static $ABC;
}
class ABC
{
}
$myXyz = new XYZ();
var_dump($myXyz);
var_dump(XYZ::$ABC);
?>
the constructor.
Whether this is "pure" OPP, I don't know. But it does work, is worth mentioning, and could be usefull.
class Singleton
{
private static $instance=null;
private $value=null;
private function __construct() {
}
public static function getInstance() {
if ( self::$instance == null ) {
echo "<br>new<br>";
self::$instance = new Singleton("values");
self::$instance->value = "values";
}
else {
echo "<br>old<br>";
}
return self::$instance;
}
}
new bad();
new bar(); // prints bool(false)
?>
A safe implementation requires a little more effort:
<?php
class foo
{
private static $stuff = array('key1' => 1, 'key2' => 2);
public final static function getstuff()
{
return self::$stuff;
}
}
class bar
{
public function __construct()
{
var_dump(foo::getstuff());
}
}
class bad
{
public function __construct()
{
foo::$stuff = FALSE;
}
}
new bar(); // prints array(2) { ["key1"]=> int(1) ["key2"]=> int(2) }
new bad(); // results in a fatal error
?>
function Bar ()
{
echo "Derived::Bar()";
}
}
Derived::Foo();
?>
function Bar ()
{
echo "Derived::Bar()";
}
}
Derived::Foo(); // This time it works.
?>
Note that Base::Foo() may no longer be declared 'static' since static methods cannot be overridden (this means it will trigger
errors if error level includes E_STRICT.)
If Foo() takes parameters then list them before $class=__CLASS__ and in most cases, you can just forget about that parameter
throughout your code.
The major caveat is, of course, that you must override Foo() in every subclass and must always include the $class parameter
when calling parent::Foo().
PHP 5.0.1 doesn't seem to mind if you call a static method in a non-static context, though it might not be the best of style
to do so.
On the other hand, PHP complains if you try to try to call a non-static method in a static context (if your error reporting is
cranked up to E_STRICT).
class Test {
Class Abstraction
PHP 5 introduces abstract classes and methods. Classes defined as abstract may not be instantiated, and any
class that contains at least one abstract method must also be abstract. Methods defined as abstract simply
declare the method's signature - they cannot define the implementation.
When inheriting from an abstract class, all methods marked abstract in the parent's class declaration must be
defined by the child; additionally, these methods must be defined with the same (or a less restricted) visibility.
For example, if the abstract method is defined as protected, the function implementation must be defined as
either protected or public, but not private. Furthermore the signatures of the methods must match, i.e. the type
hints and the number of required arguments must be the same. This also applies to constructors as of PHP 5.4.
Before 5.4 constructor signatures could differ.
Example #1 Abstract class example
<?php
abstractclassAbstractClass
{
//ForceExtendingclasstodefinethismethod
abstractprotectedfunctiongetValue();
abstractprotectedfunctionprefixValue($prefix);
//Commonmethod
publicfunctionprintOut(){
print$this->getValue()."\n";
}
}
classConcreteClass1extendsAbstractClass
{
protectedfunctiongetValue(){
return"ConcreteClass1";
}
publicfunctionprefixValue($prefix){
return"{$prefix}ConcreteClass1";
}
}
classConcreteClass2extendsAbstractClass
{
publicfunctiongetValue(){
return"ConcreteClass2";
}
publicfunctionprefixValue($prefix){
return"{$prefix}ConcreteClass2";
}
}
$class1=newConcreteClass1;
$class1->printOut();
echo$class1->prefixValue('FOO_')."\n";
$class2=newConcreteClass2;
$class2->printOut();
echo$class2->prefixValue('FOO_')."\n";
?>
Old code that has no user-defined classes or functions named 'abstract' should run without modifications.
Class Abstraction
Oliver Anan
On the other hand, an abstract class is a tool aimed at helping the "implementor" of the classes that extend it. It is an
infrastructure that can impose restrictions and guidelines about what the concrete classes should look like. From a class
design perspective, abstract classes are more architecturally important than interfaces. In this case, the implementor sits
between the abstract class and the concrete one, building the latter on top of the former.
?>
Now just think about a Database class where MySQL and PostgreSQL extend it. Also, a note. An abstract class is just like an
interface, but you can define methods in an abstract class whereas in an interface they are all abstract.
nathan dot vorbei dot tech at gmail dot com 16-Nov-2009 10:55
"additionally, these methods must be defined with the same (or a less restricted) visibility."
The words were not restricted in abstract class but also normal classes,
the method in child Class which overwrites the parent Class can also change the the visibility of the method to same or less
restricted.
for example:
<?php
class ClassOne {
protected static $staticone = 'nathan';
protected function changestaticone() {
return self::$staticone = 'john';
}
}
class ClassTwo extends ClassOne {
public function changestaticone() {
return self::$staticone = 'Alexey';
}
}
$classtwo = new ClassTwo();
echo $classtwo->changestaticone();
If you're defining a new class that is abstract, it means that you can make some non-abstract functions that you can use to
define the general underlying behavior of that class along side abstract ones.
In interfaces, you can't do that since functions defined therewithin cannot have a body.
Abstract functions you use for classes that must define more specific behavior when "extending" your class.
So for a crude example - define by your non-abstract functions how that particular object (which may be part of a larger class
hierarchy) would store and process it's data in SQL, XML, etc.
Then define abstract functions which allow someone implementing that class to specifically manipulate the data that is to be
stored. Then require a format which this data must be returned in, and then in your non-abstract functions call those functions
on destruction, in normal runtime, and so on.
Again, non-abstract functions, or even another class could implement the finer points of ensuring that data is in the correct
format, and so on, ad infinitum.
It isn't too much of a reach to say that if you used a normal class instead of an abstract class, then there isn't much
intrinsic requirement between the two classes.
Assuming that you wanted the functions to use each-others functions and you'd need to use them specifically by name, you'd have
to write some code which checked to see -- lamely using function_exists() and other lamery -- if that class has the function
you require for interoperability, when you could avoid all possible confusion and headaches by simply using the right tool for
the job.
And reading a decent OOP book.
public $LastName;
public $FirstName;
public $BirthDate;
might be using English, but that isn't as important as the fact that some of the document is already written.
An abstract class is the foundation for another object. When a class says "I extend abstract class Y", it is saying "I use
some methods or properties already defined in this other class named Y".
So, consider the following PHP:
<?php
class X implements Y { } // this is saying that "X" agrees to speak language "Y" with your code.
class X extends Y { } // this is saying that "X" is going to complete the partial class "Y".
?>
You would have your class implement a particular interface if you were distributing a class to be used by other people. The
interface is an agreement to have a specific set of public methods for your class.
You would have your class extend an abstract class if you (or someone else) wrote a class that already had some methods written
that you want to use in your new class.
These concepts, while easy to confuse, are specifically different and distinct. For all intents and purposes, if you're the
only user of any of your classes, you don't need to implement interfaces.
ASP.NET where you don't have to access the class itself...however in future I hope PHP does give us this functionality.
Interfaces in PHP only define public functions without any basic or core functionality therefore making it lesser a friend to
PROs from OOP && OOD backgrounds.
Instead Abstraction in PHP is perfered as this allows your objects a core with the ability to define different levels of access
to methods | variables | constants. Although publicly allowing access to variables (to me) | (to official \ senior programmers)
defeats the whole idea of OO. Abstraction allows the keywords of final which is very important to me as I can have many child
objects of objects and of child objects.
Thus knowing the difference helps with your choosen style but if your really looking to make an application with Abstraction I
would always suggest using an Abstract Class instead of Interfaces as Interfaces are limited and provides no functionality and
is a waste of bytes on your hard drive.
The below is an example of Abstraction used in an automated system.
<?php
Abstract Class events
{
protected $priority;
protected $message;
protected $environment;
protected $syslog;
protected $config;
protected $global;
protected $classes;
protected $super;
public function __construct(array &$config)
{
$this->config = $config;
}
#This function can be the same for both classes 'Triangle' and 'Rectangle'
public function getValue($base,$height)
{
$this->base = $base;
$this->height = $height;
}
# This might be different for each class of shape, because each Surface is calculated by a different formula ( St = b*h/2
and Sr = b*h)
abstract public function surface();
}
class Triangle extends Shape
{
# s = b*h/2
public function surface(){
return round((($this->base)*($this->height)/2),2);
}
}
class Rectangle extends Shape
{
# s = b*h
public function surface(){
return round((($this->base)*($this->height)),2);
}
}
$r = new Rectangle();
$r->getValue(15,3);
echo $r->surface() ."\n"; # echo 45
$t = new Triangle();
$t->getValue(15,3);
echo $t->surface() ."\n"; # echo 22.5
?>
An Object Interface is essentually nothing but a list of function names that a class must define if the class implements that
interface.
An Abstract Class is essentually a prototype which hints towards what extending classes should be doing.
An Abstract Class can also be thought of as a Base Class that provides some basic functionality, & also defines a built-in
Object Interface that all extending classes will implement.
So, an Object Interface is really a built-in part of an Abstract Class.
Object Interfaces
Object interfaces allow you to create code which specifies which methods a class must implement, without
having to define how these methods are handled.
Interfaces are defined using the interface keyword, in the same way as a standard class, but without any of the
methods having their contents defined.
All methods declared in an interface must be public, this is the nature of an interface.
implements
To implement an interface, the implements operator is used. All methods in the interface must be implemented
within a class; failure to do so will result in a fatal error. Classes may implement more than one interface if
desired by separating each interface with a comma.
Note:
A class cannot implement two interfaces that share function names, since it would cause ambiguity.
Note:
Interfaces can be extended like classes using the extends operator.
Note:
The class implementing the interface must use the exact same method signatures as are defined in the
interface. Not doing so will result in a fatal error.
Constants
It's possible for interfaces to have constants. Interface constants works exactly like class constants except they
cannot be overridden by a class/interface that inherits them.
Examples
Example #1 Interface example
<?php
//Declaretheinterface'iTemplate'
interfaceiTemplate
{
publicfunctionsetVariable($name,$var);
publicfunctiongetHtml($template);
}
//Implementtheinterface
//Thiswillwork
classTemplateimplementsiTemplate
{
private$vars=array();
publicfunctionsetVariable($name,$var)
{
$this->vars[$name]=$var;
}
publicfunctiongetHtml($template)
{
foreach($this->varsas$name=>$value){
$template=str_replace('{'.$name.'}',$value,$template);
}
return$template;
}
}
//Thiswillnotwork
//Fatalerror:ClassBadTemplatecontains1abstractmethods
//andmustthereforebedeclaredabstract(iTemplate::getHtml)
classBadTemplateimplementsiTemplate
{
private$vars=array();
publicfunctionsetVariable($name,$var)
{
$this->vars[$name]=$var;
}
}
?>
publicfunctionbaz(Foo$foo)
{
}
}
?>
//Thiswillhowevernotworkbecauseit'snotallowedto
//overrideconstants.
classbimplementsa
{
constb='Classconstant';
}
?>
An interface, together with type-hinting, provides a good way to make sure that a particular object contains
particular methods. See instanceof operator and type hinting.
Object Interfaces
For example: I have a database. I want to write a class that accesses the data in my database. I define an interface like
this:
interface Database {
function listOrders();
function addOrder();
function removeOrder();
...
}
Then let's say we start out using a MySQL database. So we write a class to access the MySQL database:
class MySqlDatabase implements Database {
function listOrders() {...
}
we write these methods as needed to get to the MySQL database tables. Then you can write your controller to use the interface
as such:
$database = new MySqlDatabase();
foreach ($database->listOrders() as $order) {
Then let's say we decide to migrate to an Oracle database. We could write another class to get to the Oracle database as such:
class OracleDatabase implements Database {
public function listOrders() {...
}
Then - to switch our application to use the Oracle database instead of the MySQL database we only have to change ONE LINE of
code:
$database = new OracleDatabase();
all other lines of code, such as:
foreach ($database->listOrders() as $order) {
will remain unchanged. The point is - the INTERFACE describes the methods that we need to access our database. It does NOT
describe in any way HOW we achieve that. That's what the IMPLEMENTing class does. We can IMPLEMENT this interface as many times
as we need in as many different ways as we need. We can then switch between implementations of the interface without impact to
our code because the interface defines how we will use it regardless of how it actually works.
namespace fruitcitrus;
class orange {
public function squeeze(Foo $foo);
}
#Will throw an exception Fatal error: Declaration of "fruit\citrus\orange::squeeze must be compatible with that of
fruit\squeezable() in fruit/squeezable.php
?>
<?php
#file: orange
namespace fruitcitrus;
use BarFoo; #DO NOT FORGET THIS!
class orange {
public function squeeze (Foo $foo);
}
#Will be correct
?>
}
?>
<?php
$bar = new foo(); // Invalid - throws fatal error
interface foo2
{
}
class bar implements foo2
{
}
$bar = new foo(); // Valid, since it is below the class declaration
?>
Also, @Jeffrey -- Lol? What? Is that a joke? PHP interfaces have nothing to do with connecting to "peripheral devices" or
"cameras", etc. Not even in the least sense. This is a very common miscommunication with the word "interface", as interfaces in
programming are not at all like interfaces in electronics or drivers, etc.
}
}
class T806 extends T805 implements Auxiliary_Platform {
public function Weapon() {
var_dump(__CLASS__);
}
public function Shields() {
var_dump(__CLASS__ . "->" . __FUNCTION__);
}
}
$T805 = new T805();
$T805->Weapon();
$T805->Health();
$T805->Shields();
echo "<hr />";
$T806 = new T806();
$T806->Weapon();
$T806->Health();
$T806->Shields();
/* Output:
string(4) "T805"
string(12) "T805::Health"
string(13) "T805->Shields"
<hr />string(4) "T806"
string(12) "T805::Health"
string(13) "T806->Shields"
*/
?>
Class T805 implements the interface Auxiliary_Platform. T806 does the same thing, but the method Health() is inherited from
T805 (not the exact case, but you get the idea). PHP seems to be fine with this and everything still works fine. Do note that
the rules for class inheritance doesn't change in this scenario.
If the code were to be the same, but instead T805 (or T806) DOES NOT implement Auxiliary_Platform, then it'll still work. Since
T805 already follows the interface, everything that inherits T805 will also be valid. I would be careful about that.
Personally, I don't consider this a bug.
This seems to work in PHP5.2.9-2, PHP5.3 and PHP5.3.1 (my current versions).
We could also do the opposite:
<?php
class T805 {
public function Weapon() {
var_dump(__CLASS__);
}
}
class T806 extends T805 implements Auxiliary_Platform {
public function Health() {
var_dump(__CLASS__ . "::" . __FUNCTION__);
}
public function Shields() {
If you have PHP 5.2 you can still declare static methods in interfaces. While you won't be able to call them via LSB, the
"implements IDoSomething" can serve as a hint/reminder to other developers by saying "this class has a ::doSomething() method".
Besides, you'll be upgrading to 5.3 soon, right? Right?
(Heh. I just realized: "I do something". Unintentional, I swear!)
?>
NB: forgive me for errors syntax. I in the middle of doing Java.
You have noticed that relevant behavious have been added to the relevant objects..
/**
* abstract class implements water but defines makeItWet
* in the most general way to allow child class to
* provide specificity
**/
abstract class weather implements water
{
private $cloudy;
public function makeItWet(){}
abstract public function start();
abstract public function getCloudy();
abstract public function setCloudy();
}
class rain extends weather {
private $cloudy;
public function start() {
return "Here's some weather. ";
}
requirement, all objects that need those methods implemented would have to be descended from a base class that's known to have
those methods. that's clearly not a smart idea if these objects aren't actually related.
one example (that i'm currently working on) is a background service that pulls information down from different content
providers. i have a transport and i have an import. for both, what actually happens in the background is different from
provider to provider, but since i'm implementing a transport & import interface, i only need to write code once, because i know
exactly the what methods will be implemented to get the job done. then, i just have a config file that loads the class
dynamically. i don't need something like
if ( $provider == "some company" )
{
// use this set of code
}
elseif ( $provider == "another company" )
{
// use this other set of code
}
instead, i can do:
foreach ( $providers as $provider => $info )
{
$_transport = $info['transportObject'];
$transport = new $_transport();
$_import = $info['importObject'];
$import = new $_import();
$transport->setImporter( $import );
$transport->retrieve();
}
it is expected behavior that when a class implements two interfaces that share one or more method names, an error is thrown,
because interfaces don't relate to each other. if you want that sort of inferred behavior (i.e. A and B are different except
for these shared methods), stick to [abstract] classes.
it sucks that interface methods might collide for some common types of tasks (get(), set(), etc.), so knowing that, design your
interfaces with more unique method names.
an litte example:
<?php
interface myInterface{
public function setStuff($id, $name);
}
class MyFirstClass implements myInterface{
public function setStuff($id, $name);
}
class MySecondClass implements myInterface{
public function setStuff($id, $name, $type);
}
class myThirdClass implements myInterface{
public function setStuff($id, $name, $type=0);
}
?>
Here mySecondClass will print an fatal error while myThirdClass is just fine because myThirdClass::setStuff($id, $name); is
valid and thus fullfills the interface requirements. an interface declares as set of requirement on how methods can be called
and any class implementing an interface thus agrees that is will provide these methods and that they can be called as in the
interface. adding additional arguments with default values is thus allowed because it does not violate the agreement that the
method can be called as in the interface.
Please note that the sentence "Note: A class cannot implement two interfaces that share function names, since it would cause
ambiguity." _really_ means that it is not possible to do something like:
<?php
interface IA {
public function a();
}
interface IB {
public function a();
}
class Test implements IA, IB {
public function a() {
echo "a";
}
}
$o = new Test();
$o->a();
?>
lead to:
PHP Fatal error: Can't inherit abstract function IB::a() (previously declared abstract in IA)
It's important to note this because it is very unexpected behavior and renders many common Interface completly useless.
?>
By definition, all methods in an interface are abstract. So the above code segment is equivalent to your interface definitions
and results in the same error. Why? Let's have a look at the PHP manual. From the second paragraph on class abstraction:
"When inheriting from an abstract class, all methods marked abstract in the parent's class declaration must be defined by the
child;"
http://www.php.net/manual/en/language.oop5.abstract.php
So by definition, you may only overload non-abstract methods.
For example:
<?php
abstract class IFoo
{
public function Foo()
{
// do something...
}
}
?>
If both methods need the same name, then you'll have to use non-abstract methods. In this case, interfaces aren't the right
tool for the job. You'll want to use abstract classes (or just regular classes).
interface Comparable
{function compare(self $compare);}
?>
Which is then implemented:
<?php
class String implements Comparable
{
private $string;
function __construct($string)
{$this->string = $string;}
function compare(self $compare)
{return $this->string == $compare->string;}
}
class Integer implements Comparable
{
private $integer;
function __construct($int)
{$this->integer = $int;}
function compare(self $compare)
{return $this->integer == $compare->integer;}
}
?>
Comparing Integer with String will result in a fatal error, as it is not an instance of the same class:
<?php
$first_int = new Integer(3);
$second_int = new Integer(3);
$first_string = new String("foo");
$second_string = new String("bar");
var_dump($first_int->compare($second_int)); // bool(true)
var_dump($first_string->compare($second_string)); // bool(false)
var_dump($first_string->compare($second_int)); // Fatal Error
?>
[vbolshov@localhost tmp]$
PHP doesn't generate a Fatal Error in this case, although the method declaration in the class differs from that in the
interface. This situation doesn't seem good to me: I'd prefer classes being strictly bound to their interfaces.
interface Foo {
function bar();
}
abstract class FooBar implements Foo {
abstract function bar(); // just for making clear, that this
// method has to be implemented
}
?>
This will end up with the following error-message:
Fatal error: Can't inherit abstract function Foo::bar() (previously declared abstract in FooBar) in path/to/file on line
anylinenumber
print FooBar::SOME_CONSTANT;
?>
Both of the last print statements will output the same thing: the value of FooBar::SOME_CONSTANT
Traits
As of PHP 5.4.0, PHP implements a method of code reuse called Traits.
Traits is a mechanism for code reuse in single inheritance languages such as PHP. A Trait is intended to reduce
some limitations of single inheritance by enabling a developer to reuse sets of methods freely in several
independent classes living in different class hierarchies. The semantics of the combination of Traits and classes is
defined in a way which reduces complexity, and avoids the typical problems associated with multiple inheritance
and Mixins.
A Trait is similar to a class, but only intended to group functionality in a fine-grained and consistent way. It is
not possible to instantiate a Trait on its own. It is an addition to traditional inheritance and enables horizontal
composition of behavior; that is, the application of class members without requiring inheritance.
Example #1 Trait example
<?php
traitezcReflectionReturnInfo{
functiongetReturnType(){/*1*/}
functiongetReturnDescription(){/*2*/}
}
classezcReflectionMethodextendsReflectionMethod{
useezcReflectionReturnInfo;
/*...*/
}
classezcReflectionFunctionextendsReflectionFunction{
useezcReflectionReturnInfo;
/*...*/
}
?>
Precedence
An inherited member from a base class is overridden by a member inserted by a Trait. The precedence order is
that members from the current class override Trait methods, which in return override inherited methods.
Example #2 Precedence Order Example
An inherited method from a base class is overridden by the method inserted into MyHelloWorld from the
SayWorld Trait. The behavior is the same for methods defined in the MyHelloWorld class. The precedence
order is that methods from the current class override Trait methods, which in turn override methods from
the base class.
<?php
classBase{
publicfunctionsayHello(){
echo'Hello';
}
}
traitSayWorld{
publicfunctionsayHello(){
parent::sayHello();
echo'World!';
}
}
classMyHelloWorldextendsBase{
useSayWorld;
}
$o=newMyHelloWorld();
$o->sayHello();
?>
Multiple Traits
Multiple Traits can be inserted into a class by listing them in the use statement, separated by commas.
Example #4 Multiple Traits Usage
<?php
traitHello{
publicfunctionsayHello(){
echo'Hello';
}
}
traitWorld{
publicfunctionsayWorld(){
echo'World';
}
}
classMyHelloWorld{
useHello,World;
publicfunctionsayExclamationMark(){
echo'!';
}
}
$o=newMyHelloWorld();
$o->sayHello();
$o->sayWorld();
$o->sayExclamationMark();
?>
Conflict Resolution
If two Traits insert a method with the same name, a fatal error is produced, if the conflict is not explicitly
resolved.
To resolve naming conflicts between Traits used in the same class, the insteadof operator needs to be used to
chose exactly one of the conflicting methods.
Since this only allows one to exclude methods, the as operator can be used to allow the inclusion of one of the
conflicting methods under another name.
Example #5 Conflict Resolution
In this example, Talker uses the traits A and B. Since A and B have conflicting methods, it defines to use the
variant of smallTalk from trait B, and the variant of bigTalk from trait A.
The Aliased_Talker makes use of the as operator to be able to use B's bigTalk implementation under an
additional alias talk.
<?php
traitA{
publicfunctionsmallTalk(){
echo'a';
}
publicfunctionbigTalk(){
echo'A';
}
}
traitB{
publicfunctionsmallTalk(){
echo'b';
}
publicfunctionbigTalk(){
echo'B';
}
}
classTalker{
useA,B{
B::smallTalkinsteadofA;
A::bigTalkinsteadofB;
}
}
classAliased_Talker{
useA,B{
B::smallTalkinsteadofA;
A::bigTalkinsteadofB;
B::bigTalkastalk;
}
}
?>
useHello,World;
}
classMyHelloWorld{
useHelloWorld;
}
$o=newMyHelloWorld();
$o->sayHello();
$o->sayWorld();
?>
classC1{
useCounter;
}
classC2{
useCounter;
}
$o=newC1();$o->inc();//echo1
$p=newC2();$p->inc();//echo1
?>
Properties
Traits can also define properties.
Example #11 Defining Properties
<?php
traitPropertiesTrait{
public$x=1;
}
classPropertiesExample{
usePropertiesTrait;
}
$example=newPropertiesExample;
$example->x;
?>
If a trait defines a property then a class can not define a property with the same name, otherwise an error is
issued. It is an E_STRICT if the class definition is compatible (same visibility and initial value) or fatal error
otherwise.
Example #12 Conflict Resolution
<?php
traitPropertiesTrait{
public$same=true;
public$different=false;
}
classPropertiesExample{
usePropertiesTrait;
public$same=true;//StrictStandards
public$different=true;//Fatalerror
}
?>
Traits
/**
* Example Usage
*/
class foo {
use singleton;
use TestTrait;
}
Foo1::$_bar = 'Hello';
Foo2::$_bar = 'World';
echo Foo1::$_bar . ' ' . Foo2::$_bar; // Prints: Hello World
?>
Jason dot Hofer dot deletify dot this dot part at gmail dot com 14-Mar-2012 04:39
A (somewhat) practical example of trait usage.
Without traits:
<?php
class Controller {
/* Controller-specific methods defined here. */
}
class AdminController extends Controller {
/* Controller-specific methods inherited from Controller. */
/* Admin-specific methods defined here. */
}
class CrudController extends Controller {
/* Controller-specific methods inherited from Controller. */
/* CRUD-specific methods defined here. */
}
class AdminCrudController extends CrudController {
/* Controller-specific methods inherited from Controller. */
/* CRUD-specific methods inherited from CrudController. */
/* (!!!) Admin-specific methods copied and pasted from AdminController. */
}
?>
With traits:
<?php
class Controller {
/* Controller-specific methods defined here. */
}
class AdminController extends Controller {
/* Controller-specific methods inherited from Controller. */
/* Admin-specific methods defined here. */
}
trait CrudControllerTrait {
/* CRUD-specific methods defined here. */
}
class AdminCrudController extends AdminController {
use CrudControllerTrait;
/* Controller-specific methods inherited from Controller. */
/* Admin-specific methods inherited from AdminController. */
/* CRUD-specific methods defined by CrudControllerTrait. */
}
?>
trait second_trait
{
function first_function()
{
echo "From Second Trait";
}
}
class first_class
{
use first_trait, second_trait
{
// This class will now call the method
// first function from first_trait only
first_trait::first_function insteadof second_trait;
}
}
trait Call_Helper{
$b->whereAmI(); //World
?>
The magic constant __TRAIT__ will giev you the name of the trait
Overloading
Overloading in PHP provides means to dynamically "create" properties and methods. These dynamic entities are
processed via magic methods one can establish in a class for various action types.
The overloading methods are invoked when interacting with properties or methods that have not been declared
or are not visible in the current scope. The rest of this section will use the terms "inaccessible properties" and
"inaccessible methods" to refer to this combination of declaration and visibility.
All overloading methods must be defined as public.
Note:
None of the arguments of these magic methods can be passed by reference.
Note:
PHP's interpretation of "overloading" is different than most object oriented languages. Overloading
traditionally provides the ability to have multiple methods with the same name but different quantities and
types of arguments.
Changelog
Version
Description
5.3.0
Added __callStatic(). Added warning to enforce public visibility and non-static declaration.
5.1.0
Property overloading
public
public
public
public
$value
$a = $obj->b = 8;
Note:
It is not possible to use overloaded properties in other language constructs than isset(). This means if
empty() is called on an overloaded property, the overloaded method is not called.
To workaround that limitation, the overloaded property must be copied into a local variable in the scope and
then be handed to empty().
Example #1 Overloading properties via the __get(), __set(), __isset() and __unset() methods
<?php
classPropertyTest
{
/**Locationforoverloadeddata.*/
private$data=array();
/**Overloadingnotusedondeclaredproperties.*/
public$declared=1;
/**Overloadingonlyusedonthiswhenaccessedoutsidetheclass.*/
private$hidden=2;
publicfunction__set($name,$value)
{
echo"Setting'$name'to'$value'\n";
$this->data[$name]=$value;
}
publicfunction__get($name)
{
echo"Getting'$name'\n";
if(array_key_exists($name,$this->data)){
return$this->data[$name];
}
$trace=debug_backtrace();
trigger_error(
'Undefinedpropertyvia__get():'.$name.
'in'.$trace[0]['file'].
'online'.$trace[0]['line'],
E_USER_NOTICE);
returnnull;
}
/**AsofPHP5.1.0*/
publicfunction__isset($name)
{
echo"Is'$name'set?\n";
returnisset($this->data[$name]);
}
/**AsofPHP5.1.0*/
publicfunction__unset($name)
{
echo"Unsetting'$name'\n";
unset($this->data[$name]);
}
/**Notamagicmethod,justhereforexample.*/
publicfunctiongetHidden()
{
return$this->hidden;
}
}
echo"<pre>\n";
$obj=newPropertyTest;
$obj->a=1;
echo$obj->a."\n\n";
var_dump(isset($obj->a));
unset($obj->a);
var_dump(isset($obj->a));
echo"\n";
echo$obj->declared."\n\n";
echo"Let'sexperimentwiththeprivatepropertynamed'hidden':\n";
echo"Privatesarevisibleinsidetheclass,so__get()notused...\n";
echo$obj->getHidden()."\n";
echo"Privatesnotvisibleoutsideofclass,so__get()isused...\n";
echo$obj->hidden."\n";
?>
Notice:
Method overloading
public mixed __call ( string $name , array $arguments )
public static mixed __callStatic ( string $name , array
$arguments
Overloading
<?php
class Post {
private $title;
private $content;
private $author;
private $comments;
private $_getters = array('title', 'content', 'author', 'comments');
private $_setters = array('title', 'content', 'author');
<?php
$post->title = 'Hello, World!';
?>
I've tried a couple of different ways of doing this, but this seems to be the fastest (although still around 2 times slower
than using ugly old school getter- and setter-methods).
Functions like property_exists() are unnecessary and only makes thing even slower (my initial attempt at using __set and __get
was up to 9 times slower than traditional getter/setter methods).
Could probably still be improved/changed in different ways depending on context.
return $retval;
}
}
class B
{
protected $val
public function __construct()
{
$this->val = 1;
}
public function __get($variable)
{
echo "Class B::Variable " . $variable . "\n\r";
$retval = $this->{$variable};
return $retval;
}
}
$A = new A();
echo "Final Value: " . $A->B->val;
?>
That will return something like...
Class A::Variable B
Class B::Variable val
Final Value: 1
It seperates the calls into $A->B and $B->val
Hope this helps someone
}
public function setBar($value) {
$this->bar = $value;
}
}
$foo = new foo();
echo $foo->bar; // Ouputs: Hello World!
$db = new mysqli("localhost","root","","tests");
$sql = "INSERT INTO foo SET bar=?";
$res = $db->prepare($sql);
$res->bind_param("s",$foo->bar); // Notice: Indirect modification of overloaded property foo::$bar has no effect in
/var/www/overload.php on line 24
$res->execute(); // Writes "Hello World!" to database
$foo->setBar("Goodbye");
echo $foo->bar; // Outputs: Goodbye
$res->execute(); // Writes "Hello World!" to database
?>
* TestMagicCallMethod::__call
* TestMagicCallMethod::bar
*/
$test->baz();
/**
* Outputs:
* TestMagicCallMethod::__call
* TestMagicCallMethod::baz
*/
?>
..is probably not what you should be doing. Always make sure that the methods you call in __call are allowed as you probably
dont want all the private/protected methods to be accessed by a typo or something.
}
// Example adding a new custom method
$test = new Test;
$test->do_something = function ($var1, $var2) {echo "custom called with $var1 and $var2\n";};
$test->do_something('asdf', 'test');
?>
Unfortunately, without access to $this within the anon functions, it's not really useful. You can "use ($test)" in your anon
function, but that just makes it more complicated.
{
echo "\n", __FILE__, ':', __LINE__, ' ', __METHOD__, '(', $prop, ') instance ', $this->id ;
return new demo( 'autocreated' ) ; // return a class anyway for the demo
}
function __isset( $prop )
{
echo "\n", __FILE__, ':', __LINE__, ' ', __METHOD__, '(', $prop, ') instance ', $this->id ;
return FALSE ;
}
}
$x = new demo( 'demo' ) ;
echo "\n", 'Calls __isset() on demo as expected when executing isset( $x->a )' ;
$ret = isset( $x->a ) ;
echo "\n", 'Calls __get() on demo without call to __isset() when executing isset( $x->a->b )' ;
$ret = isset( $x->a->b ) ;
?>
Outputs
Calls __isset() on demo as expected when executing isset( $x->a )
C:\htdocs\test.php:31 demo::__isset(a) instance demo
Calls __get() on demo without call to __isset() when executing isset( $x->a->b )
C:\htdocs\test.php:26 demo::__get(a) instance demo
C:\htdocs\test.php:31 demo::__isset(b) instance autocreated
class A {
private $fields;
public function __call($func, $args) {
if (empty($args)) {
return $this->fields[$args[0]];
} else {
$this->$fields[$func] = $args[0];
}
}
?>
Or statically:
<?php
class foo {
static $vals;
public static function __callStatic($func, $args) {
if (!empty($args)) {
self::$vals[$func] = $args[0];
} else {
return self::$vals[$func];
}
}
}
?>
Which would allow you to say:
<?php
Foo::username('john');
print Foo::username(); // prints 'john'
?>
}
public function __set($name, $value)
{
$fn_name = 'set_' . $name;
if (method_exists($this, $fn_name))
{
$this->$fn_name($value);
}
}
private function get_degrees()
{
return $this->degrees;
}
}
?>
you can even turn this into a function or something
<?php
$foo->b = $a = 3;
?>
eric dot druid+php dot net at gmail dot com 01-Jul-2009 04:22
I needed to know from where a member variable was set from to determine visibility.
<?php
class foo {
private $data;
public function __set($name, $value) {
$trace = debug_backtrace();
if(!isset($trace[1]) || $trace[1]['object'] != $trace[0]['object']) {
die("Trying to set protected member '$name' from public scope.");
}
$data[$name] = $value;
}
}
?>
$this->$key = $value;
} else {
$this->properties[$key] = $value;
}
}
public function __get($key) {
if (array_key_exists($key, $this->properties)) {
return $this->properties[$key];
}
return null;
}
}
?>
The idea behind this is, that you can register new properties at any time from within a class without getting a warning or
some of that kind. Now, if you try to set a property from outside the class, the __set() method will be called and decides
whether to place the new property in the properties array or declare it as a new "native" property. When trying to get that
property, the __get() method will only be called if the property is not "native", which won't be the case for an array.
HTH
}
just put it in your __get() or __set() like so:
public function __get($property)
{
echo "<p><font color='#ff0000'>Attempted to __get() non-existant property/variable '".$property."' in class
'".$this->get_class_name()."'.</font><p>\n";
$this->suggest_alternative($property);
exit;
}
?>
$this->server->add('personalD',
'Personal Data',
'server/personal.php');
$this->server->add('personalI',
'Personal Interviews',
'server/personalI.php');
$this->server->personalI->add('detailsByIer',
'Detalis by Interviewer',
'server/personalI.php?tab=detailsByIer');
//(...)
return $this;
}
}
//Testing
$meuMenu = new menu;
echo $meuMenu;
/***********************************************
*Will output (to the browser):
*
*<pre>
*1 Server<br>
*1.1 Personal Data<br>
*1.2 Personal Interviews<br>
*1.2.1 Details by Interviewer<br>
*</pre>
*
*Which shows:
*
*1 Server
* 1.1 Personal Data
* 1.2 Personal Interviews
* 1.2.1 Details by Interviewer
************************************************/
?>
public function strPrint($bm = ''){ //Just to show, replace with something better.
$m = '';$r = '';$n = $this->p['n'];
if ($n > 0){switch($n){case 0:case 1: $qs = 0; break;case 2: $qs = 2; break;case 3: $qs = 6; break;case 4: $qs = 12;
break;case 5: $qs = 20; break;case 6: $qs = 30; break;case 7: $qs = 42; break;case 8: $qs = 56; break;}
$tab = str_repeat(' ', $qs);$r .= $tab;
if ($bm <> ''){$m = $bm.'.';}
$im = $this->p['i'] + 1;$m .= $im;$r .= $m.' ';$r .= $this->p['t']."<br>\n";
}
foreach ($this->c as $child){$r .= $child->strPrint($m);}
return $r;
}
if (isset($s->A->B->C))
{
print($s->A->B->C);
}
else
{
print('A->B->C is NOT set');
}
if (isset($s->X->Y->Z))
{
print_r($s->X->Y->Z);
}
else
{
print('X->Y->Z is NOT set');
}
// prints: FOOArray ( [0] => BAR )
?>
... have fun and ...
call_user_func( array($this,'unset_'.(string)$property) );
}
}
?>
}
}
?>
?>
Umang
} else {
// public void whatever() in Java
echo 'Usage: whatever([int], boolean)';
}
}
}
?>
// The Java version:
public class someClass {
public boolean whatever(boolean arg1) {
return arg1;
}
}
}
$this->x[$var] = $value;
}
}
}
$z = new test;
$z->variable = 'abc';
$z->{'somearray[key]'} = 'def';
var_dump($z->x);
var_dump($z->y);
?>
}
public function doSomething($parameterList)
{
//We make actions with the value
}
}
class WantStaticCall {
private static $objectList;
private function __construct()
public static function init()
{
self::$objectList = array();
}
public static function register($alias)
{
self::$objectList[$alias] = new objDriver();
}
public static function __call($method, $arguments)
{
$alias = $arguments[0];
array_shift($arguments);
call_user_method($method, self::$objectList[$alias], $arguments);
}
}
// The deal here is to use following code:
WantStaticCall::register('logger');
WantStaticCall::doSomething('logger', $argumentList);
// and we will make objDriver to call his doSomething function with arguments
// $argumentList. This is not common pattern but very usefull in some cases.
// The problem here is that __call() cannot be static, Is there a way to work it around
?>
?>
Outputs:
I'm Being Set !
I'm Being Got !
I'm Being Got !
I'm Being Set !
Whats happening, is PHP is acquiring a reference to the object, triggering __get; Then applying the changes to the object via
the reference.
Which is the correct behaviour; objects being special creatures, with an aversion to being cloned...
Unfortunately this will never invoke the __set handler, even though it is modifying a property within 'foo', which is slightly
annoying if you wanted to keep track of changes to an objects overloaded properties.
I guess Journaled Objects will have to wait till PHP 6 :)
}
}
$tc = new TestClass();
$tc->foo = 'bar';
$tc->values['foo'] = 'boing';
echo '$tc->foo == ' . $tc->foo . '<br>';
echo '$tc ' . (property_exists($tc, 'foo') ? 'now has' : 'still does not have') . ' a property called "foo"<br>';
/*
OUPUTS:
$tc->foo == bar
$tc now has a property called "foo"
*/
?>
The property must be PRIVATE, otherwise the overload method __get doesn't be called.
<?php
class Session {
private $ro_usrName;
function __construct (){
$this->ro_usrName = "Marcos";
}
function __set($set, $val){
if(property_exists($this,"ro_".$set))
echo "The property '$set' is read only";
else
if(property_exists($this,$set))
$this->{$set}=$val;
else
echo "Property '$set' doesn't exist";
}
function __get{$get}{
if(property_exists($this,"ro_".$get))
return $this->{"ro_".$get};
else
if(property_exists($this,$get))
return $this->{$get};
else
echo "Property '$get' doesn't exist";
}
}
?>
?>
Will output :
Param2(4,5)
Param3(4,5,6)
Fatal error: Uncaught exception 'Exception' with message 'Tried to call unknown method OverloadedClass::ParamX' in
overload.php:7
Stack trace:
#0 [internal function]: OverloadedClass->__call('ParamX', Array)
#1 overload.php(22): OverloadedClass->ParamX(4, 5, 6, 7)
#2 {main}
thrown in overload.php on line 7
<?php
$tmp_array = &$myclass->foo;
$tmp_array['bar'] = 'baz';
unset($tmp_array);
?>
Therefore, the above won't do anything if your __get implementation resembles this:
<?php
function __get($name) {
return array_key_exists($name, $this->values)
? $this->values[$name] : null;
}
?>
You will actually need to set the value in __get and return that, as in the following code:
<?php
function __get($name) {
if (!array_key_exists($name, $this->values))
$this->values[$name] = null;
return $this->values[$name];
}
?>
$name = preg_replace('/set(_)?/','',$name);
if(property_exists(__class__,$name))
{
$this->{$name}=array_pop($params);
return true;
}
else
{
//call to class error handler
return false;
}
return true;
}
elseif(substr($name,0,3)=="get")
{
$name = preg_replace('/get(_)?/','',$name);
if(property_exists(__class__,$name) )
{
return $this->{$name};
}
else
{
//call to class error handler
return false;
}
}
else
{
//call to class error handler
return false;
}
}
else
{
die("method $name dose not exist\n");
}
return false;
}
Yet another way of providing support for read-only properties. Any property that has
"pri_" as a prefix will NOT be returned, period, any other property will be returned
and if it was declared to be "protected" or "private" it will be read-only. (scope dependent of course)
<?php
function __get($var){
if (property_exists($this,$var) && (strpos($var,"pri_") !== 0) )
return $this->{$var};
else
//Do something
}
?>
$GLOBALS['var2']++;
echo "Overloaded function returns: $ref2 which should be equal to " . $GLOBALS['var2'] . "<br/>\n";
?>
Answer: __get()
<?php
class a
{
function __get($v)
{
$this->$v = new $v;
return $this->$v;
}
}
class b
{
function say($word){
echo $word;
}
}
$a = new a();
$a->b->say('hello world');
// echos 'hello world'
?>
26-Aug-2005 07:32
To those who wish for "real" overloading: there's not really any advantage to using __call() for this -- it's easy enough with
func_get_args(). For example:
<?php
class Test
{
public function Blah()
{
$args = func_get_args();
switch (count($args))
{
case 1: /* do something */ break;
case 2: /* do something */ break;
}
}
}
?>
06-May-2005 03:50
Please note that PHP5's overloading behaviour is not compatible at all with PHP4's overloading behaviour.
TestClass::__wakeup().
?>
}
elseif(substr($val, 0, 3) == 'get')
{
$varname = substr($val, 3);
}
else
{
die("method $val does not exist\n");
}
# now see if that variable exists:
foreach($this as $class_var=>$class_var_value)
{
if(strtolower($class_var) == strtolower($varname))
{
return $class_var_value;
}
}
return false;
}
# IMPORTANT: you can keep some things private - or treat
# some vars differently by giving them their own getter method
# See how this function lies about Person's weight.
function getWeight()
{
return intval($this->weight * .8);
}
}
$a = new Person('Miles', 35, 200);
# these all work (case-insensitive):
print $a->get_name() . "\n";
print $a->getName() . "\n";
print $a->get_Name() . "\n";
print $a->getname() . "\n";
print $a->get_age() . "\n";
print $a->getAge() . "\n";
print $a->getage() . "\n";
print $a->get_Age() . "\n";
# defined functions still override the __call
print $a->getWeight() . "\n";
# trying to get something that doesn't exist returns false
print $a->getNothing();
# this still gets error:
print $a->hotdog();
?>
public $objOwner;
function __call($sMethod, $aParams)
{
// Has the Owner been assigned?
if (isset($this->objOwner))
{
return call_user_func_array(array($this->objOwner, $sMethod), $aParams);
}
else
{
echo 'Owner for ' . get_class($this) . ' not assigned.';
}
}
}
class A_WebPageContainer
{
private $sName;
{
// Pseudo function overloading
public function __call($func_name, $argv)
{
$argc = sizeof($argv);
switch ($func_name) {
case 'is_available':
$func_name = ($argc == 2) ? 'is_available_single' : 'is_available_multi';
break;
default: // If no applicable function was found, generate the default PHP error message.
Error('Call to undefined function Framework::'. $func_name .'().');
}
return call_user_func_array(array(&$this, $func_name), $argv);
}
// protected array is_available_multi($mod_name, $mod_addon_0 [, $mod_addon_1 [, ...]])
protected function is_available_multi()
{
if (($argc = func_num_args()) < 2) {
Error('A minimum of two arguments are required for Framework::is_available().');
}
$available_addons = array();
// --snip- return $available_addons;
}
protected function is_available_single($mod_name, $mod_addon)
{
// --snip- return true;
}
}
$fw = new Framework;
$test_one = $fw->is_available('A_Module_Name', 'An_Addon');
var_dump($test_one);
// Test one produces a boolean value, supposedly representing whether 'An_Addon' is available and can be used.
$test_two = $fw->is_available('A_Module_Name', 'Addon_0', 'Addon_1', 'Addon_2');
var_dump($test_two);
// Test two produces an array, supposedly listing any of the three 'Addon_N' modules that are available and can be used.
// Here are the actual results of the above:
// bool(true)
// array(0) {
// }
?>
--By adding additional case statements to Framework::__call(), methods can easily be overloaded as needed. It's also possible to
add any other overloading criteria you require inside the switch statement, allowing for more intricate overloading
functionality.
Object Iteration
PHP 5 provides a way for objects to be defined so it is possible to iterate through a list of items, with, for
example a foreach statement. By default, all visible properties will be used for the iteration.
Example #1 Simple Object Iteration
<?php
classMyClass
{
public$var1='value1';
public$var2='value2';
public$var3='value3';
protected$protected='protectedvar';
private$private='privatevar';
functioniterateVisible(){
echo"MyClass::iterateVisible:\n";
foreach($thisas$key=>$value){
print"$key=>$value\n";
}
}
}
$class=newMyClass();
foreach($classas$key=>$value){
print"$key=>$value\n";
}
echo"\n";
$class->iterateVisible();
?>
As the output shows, the foreach iterated through all of the visible properties that could be accessed.
To take it a step further, the Iterator interface may be implemented. This allows the object to dictate how it will
be iterated and what values will be available on each iteration.
Example #2 Object Iteration implementing Iterator
<?php
classMyIteratorimplementsIterator
{
private$var=array();
publicfunction__construct($array)
{
if(is_array($array)){
$this->var=$array;
}
}
publicfunctionrewind()
{
echo"rewinding\n";
reset($this->var);
}
publicfunctioncurrent()
{
$var=current($this->var);
echo"current:$var\n";
return$var;
}
publicfunctionkey()
{
$var=key($this->var);
echo"key:$var\n";
return$var;
}
publicfunctionnext()
{
$var=next($this->var);
echo"next:$var\n";
return$var;
}
publicfunctionvalid()
{
$key=key($this->var);
$var=($key!==NULL&&$key!==FALSE);
echo"valid:$var\n";
return$var;
}
}
$values=array(1,2,3);
$it=newMyIterator($values);
foreach($itas$a=>$b){
print"$a:$b\n";
}
?>
rewinding
valid: 1
current: 1
key: 0
0: 1
next: 2
valid: 1
current: 2
key: 1
1: 2
next: 3
valid: 1
current: 3
key: 2
2: 3
next:
valid:
The IteratorAggregate interface can be used as an alternative to implementing all of the Iterator methods.
IteratorAggregate only requires the implementation of a single method, IteratorAggregate::getIterator(), which
should return an instance of a class implementing Iterator.
Example #3 Object Iteration implementing IteratorAggregate
<?php
classMyCollectionimplementsIteratorAggregate
{
private$items=array();
private$count=0;
//RequireddefinitionofinterfaceIteratorAggregate
publicfunctiongetIterator(){
returnnewMyIterator($this->items);
}
publicfunctionadd($value){
$this->items[$this->count++]=$value;
}
}
$coll=newMyCollection();
$coll->add('value1');
$coll->add('value2');
$coll->add('value3');
foreach($collas$key=>$val){
echo"key/value:[$key->$val]\n\n";
}
?>
valid: 1
current: value 2
key: 1
key/value: [1 -> value 2]
next: value 3
current: value 3
valid: 1
current: value 3
key: 2
key/value: [2 -> value 3]
next:
current:
valid:
Note:
For more examples of iterators, see the SPL Extension.
Object Iteration
if(isset($data[$index])) {
unset($data[$index]);
}
$u = &$this->data[$index];
if(is_array($value)) {
$u = new ArrayAccessImpl();
foreach($value as $idx=>$e)
$u[$idx]=$e;
} else
$u=$value;
}
public function offsetGet($index) {
// echo ("GET: ".$index."<br>");
if(!isset($this->data[$index]))
$this->data[$index]=new ArrayAccessImpl();
return $this->data[$index];
}
public function offsetExists($index) {
// echo ("EXISTS: ".$index."<br>");
if(isset($this->data[$index])) {
if($this->data[$index] instanceof ArrayAccessImpl) {
if(count($this->data[$index]->data)>0)
return true;
else
return false;
} else
return true;
} else
return false;
}
}
echo "ArrayAccess implementation that behaves like a multi-level array<hr />";
function offsetUnset($offset)
{
unset ($this->objectArray[$offset]);
}
<?php
class Contact
{
protected $name = NULL;
public function set_name($name)
{
$this->name = $name;
}
<?php
foreach($MyObject as $key => &$value)
$value = 'new '.$value;
?>
I think this should be written somewhere in the documentations, but I couldn't find it.
Magic Methods
The function names __construct(), __destruct(), __call(), __callStatic(), __get(), __set(), __isset(), __unset(),
__sleep(), __wakeup(), __toString(), __invoke(), __set_state() and __clone() are magical in PHP classes. You
cannot have functions with these names in any of your classes unless you want the magic functionality
associated with them.
Caution
PHP reserves all function names starting with __ as magical. It is recommended that you do not
use function names with __ in PHP unless you want some documented magic functionality.
publicfunction__construct($server,$username,$password,$db)
{
$this->server=$server;
$this->username=$username;
$this->password=$password;
$this->db=$db;
$this->connect();
}
privatefunctionconnect()
{
$this->link=mysql_connect($this->server,$this->username,$this->password);
mysql_select_db($this->db,$this->link);
}
publicfunction__sleep()
{
returnarray('server','username','password','db');
}
publicfunction__wakeup()
{
$this->connect();
}
}
?>
__toString()
public string __toString ( void )
The __toString() method allows a class to decide how it will react when it is treated like a string. For example,
what echo $obj; will print. This method must return a string, as otherwise a fatal E_RECOVERABLE_ERROR level error is
emitted.
Example #2 Simple example
<?php
//Declareasimpleclass
classTestClass
{
public$foo;
publicfunction__construct($foo)
{
$this->foo=$foo;
}
publicfunction__toString()
{
return$this->foo;
}
}
$class=newTestClass('Hello');
echo$class;
?>
It is worth noting that before PHP 5.2.0 the __toString() method was only called when it was directly combined
with echo or print. Since PHP 5.2.0, it is called in any string context (e.g. in printf() with %s modifier) but not
in other types contexts (e.g. with %d modifier). Since PHP 5.2.0, converting objects without __toString()
method to string would cause E_RECOVERABLE_ERROR.
__invoke()
mixed __invoke ([
$...
] )
The __invoke() method is called when a script tries to call an object as a function.
Note:
This feature is available since PHP 5.3.0.
Example #3 Using __invoke()
<?php
classCallableClass
{
publicfunction__invoke($x)
{
var_dump($x);
}
}
$obj=newCallableClass;
$obj(5);
var_dump(is_callable($obj));
?>
__set_state()
static object __set_state ( array
$properties
This static method is called for classes exported by var_export() since PHP 5.1.0.
The only parameter of this method is an array containing exported properties in the form array('property' =>
value, ...).
Example #4 Using __set_state() (since PHP 5.1.0)
<?php
classA
{
public$var1;
public$var2;
publicstaticfunction__set_state($an_array)//AsofPHP5.1.0
{
$obj=newA;
$obj->var1=$an_array['var1'];
$obj->var2=$an_array['var2'];
return$obj;
}
}
$a=newA;
$a->var1=5;
$a->var2='foo';
eval('$b='.var_export($a,true).';');//$b=A::__set_state(array(
//'var1'=>5,
//'var2'=>'foo',
//));
var_dump($b);
?>
Magic Methods
return $result;
}
}
$obj1=new MyBaseClass();
echo ($s1=serialize($obj1))."<br>"; // only test2 is stored, as it was changed in the constructor
$obj2=new MyBaseClass();
$obj2->name='object 2'; // change default value here
echo ($s2=serialize($obj2))."<br>"; // stored name and test2
$obj3=new MyBaseClass();
$obj3->test2=NULL; // switch back to default value
echo ($s3=serialize($obj3))."<br>"; // nothing is stored but the class name
// let us check if we can retrieve the objects
unset($obj1, $obj2, $obj3);
$obj1=unserialize($s1);
$obj2=unserialize($s2);
$obj3=unserialize($s3);
var_dump($obj1);
var_dump($obj2);
var_dump($obj3);
?>
public static function throwToStringException($exception)
{
// Should not occur with prescribed usage, but in case of recursion: clean out exception, return a valid string, and
weep
if (isset(self::$_toStringException))
{
self::$_toStringException = null;
return '';
}
self::$_toStringException = $exception;
return null;
}
}
class My_Class
{
public function doComplexStuff()
{
throw new Exception('Oh noes!');
}
public function __toString()
{
try
{
// do your complex thing which might trigger an exception
return $this->doComplexStuff();
}
catch (Exception $e)
{
// The 'return' is required to trigger the trick
return My_ToStringFixer::throwToStringException($e);
}
}
}
$x = new My_Class();
try
{
echo $x;
}
catch (Exception $e)
{
echo 'Caught Exception! : '. $e;
}
?>
{
echo "Invoking A() Class";
}
}
class B
{
public $a;
class InvokeSingleParam {
function __invoke($param1)
{
print __METHOD__ . PHP_EOL;
print "Value of \$param1 is: " . $param1 . PHP_EOL . PHP_EOL;
}
}
class InvokeMultiParams {
function __invoke($param1, $param2, $param3) {
print __METHOD__ . PHP_EOL;
print "Value of \$param1 is: " . $param1 . PHP_EOL;
print "Value of \$param2 is: " . $param2 . PHP_EOL;
print "Value of \$param3 is: " . $param3 . PHP_EOL . PHP_EOL;
}
}
$no(1, 2, 3);
$single('one param');
$multi('param 1', 'param 2', 'param 3');
?>
This outputs:
InvokeNoParams::__invoke
The value of $param1 is: 1
The value of $param2 is: 2
The value of $param3 is: 3
InvokeSingleParam::__invoke
Value of $param1 is: one param
InvokeMultiParams::__invoke
Value of $param1 is: param 1
Value of $param2 is: param 2
Value of $param3 is: param 3
Thought I would just explain something about __get and __set here.
Imgine you have a class like so:
<?php
class user_bo{
protected $attributes = Array();
?>
I return null if my variable don't exist.
function __construct() {
$this->test_obj = new stdClass();
}
function __get($prop) {
return $this->$prop;
}
function __set($prop, $val) {
$this->$prop = $val;
}
}
$a = new A();
?>
Combined Operators (.=, +=, *=, etc): you must also define a companion __get() method to grant write -and- read access to the
property. Remember, "$x += $y" is shorthand for "$x = $x + $y". In other words, "__set($x, (__get($x) + $y))".
Properties that are Arrays: attempting to set array values like "$a->test_array[] = 'asdf';" from outside this object will
result in an "Indirect modification of overloaded property" notice and the operation completely ignored. You can't use '[]'
for array value assignment in this context (with the exception only if you made __get() return by reference, in which case, it
would work fine and bypass the __set() method altogether). You can work around this doing something like unioning the array
instead:
<?php
$a->test_array[] = 'asdf'; // notice given and ignored unless __get() was declared to return by reference
$a->test_array += array(1 => 'asdf'); // to add a key/value
$a->test_array = array("key" => 'asdf') + $a->test_array; // to overwrite a key/value.
?>
Properties that are Objects: as long as you have that __get() method, you can freely access and alter that sub object's own
properties, bypassing __set() entirely. Remember, objects are assigned and passed by reference naturally.
<?php
$a->test_obj->prop = 1; // fine if $a did not have a set method declared.
?>
All above tested in 5.3.2.
}
$p1 = new point(1,1);
$p2 = new point(23,-6);
$p3 = new point(15,20);
echo $p1($p2,$p3,$p1); // round trip 73.89
?>
Functionally, __invoke() can also be used to mimic the use of variable functions. Sadly, attempting any calling of __invoke()
on a static level will produce a fatal error.
{
if(method_exists($this,$MethodName='set_'.$varName))
return $this->$MethodName($value);
else
trigger_error($varName.' is not avaliable .',E_USER_ERROR);
}
private function set_Surname($value)
{
// On surname changed Events.
echo "Surname Changed as ".$value."\r\n";
$this->_Surname=$value;
}
private function get_Surname()
{
// On Get Events.
echo "Surname Getted\r\n";
return $this->_Surname;
}
private function set_Name($value)
{
// On Name changed Events.
echo "Name Changed as ".$value."\r\n";
$this->_Name=$value;
}
private function get_Name()
{
// On Get Name Events.
echo "Name getted\r\n";
return $this->_Name;
}
}
?>
Using :
<?php
$get=new GetSet();
$get->Name="Tufan Baris";
$get->Surname="YILDIRIM";
echo $get->Name.' '.$get->Surname;
?>
Output
Name Changed as Tufan Baris
Surname Changed asYILDIRIM
Name getted
Surname Getted
Tufan Baris YILDIRIM
HOW IT HAPPENED:
I use to have a logging function called LogEnter() and another called LogExit() that I use to call at every function when
intensively debugging.
For example all my functions look like:
<?php
function AnyFunction( $a, $b, $c )
{
LogEnter();
// DoSomeStuffHere.
LogExit();
}
?>
Then inside LogEnter(), I use debug_backtrace() to discover the parameters that were used when calling the function (in this
example the values of $a, $b, $c).
The LogEnter() deals without knowing the number of parameters to treat, and formats something like ( 2, 3, 4 ) if those were
the values. That info is printed among other info to a log-file.
Doing this, keeps me with a single-very-simple way to trace all te calls.
The thing is that if the the following are met at the same time, I have a problem:
- The function accepts (and is called with) a class as a parameter.
- The function is being debuggd by calling LogEnter();
- The class does not have __toString() coded.
- For some reason I am not allowed to edit the class nor inherit from it.
Then the debug function performs an "Object of class X could not be converted to string" error when trying to "dump" the
parameter list (containing the object) into the file.
LOG FUNCTION:
This looks for the "parameters array" and then calls BuildArgs to format them. LogWrite finds the function name, file, line
number, and so on, but does not matter here.
<?php
public function LogEnter()
{
$Debug = debug_backtrace();
$DebugLine = $Debug[ 1 ];
// [0] would be this very same function.
// [1] is the caller of this function.
$Args = isset( $DebugLine[ 'args' ] ) ? $DebugLine[ 'args' ] : '[x]';
$Msg = 'Entering with ' . BuildArgs( $Args );
LogWrite( $Msg );
}
// CAUTION, THIS NEXT ONE FAILS
private function BuildArgs( $Args )
{
$Result = '( ' . implode( ', ', $Args ) . ' )';
return $Result;
}
?>
The problem is that if you call a function where a parameter being an object that has not the __toString() defined, then
automatically "imploding" the argument list into a string, fails.
SOLUTION:
The solution is that the __toString is "checkable for its existance". The "tips" are to manually traverse the result of
debug_backtrace()['args'] and then, for each element, check:
- if it is or not an object with is_object( $Value )
- in case it is, check if __toString() is defined with method_exists( $Value, '__toString' )
- in case it is not an object or the method exist, perform a normal assignation.
- in case it is an object without that method, print something else. I choose to output the class name with get_class( $Value
) and put it between square brackets.
The final function becomes like this:
<?php
private function BuildArgs( $Args )
{
// This function gets an 'args' array from debug_backtrace() and formats it in the printable format (a, b, c) including the
parenthesis.
$FormattedParams = '';
$Glue = '';
foreach( $Args as $Value )
{
if( is_object( $Value ) )
{
if( method_exists( $Value, "__toString" ) )
{
$PrintableValue = $Value;
}
else
{
$PrintableValue = '[' . get_class( $Value ) . ']';
}
}
else
{
$PrintableValue = $Value;
}
$FormattedParams .= $Glue . $PrintableValue;
$Glue = ', ';
}
$Result = sprintf( '( %s )', $FormattedParams );
return $Result;
}
?>
give and error in this case as it is evaluated first as a function, not as a class and so __autoload won't be triggered.
public $label='';
$this->label = $label;
}
function nz_arr($attrib_arr){
$s = '';
foreach($attrib_arr as $attrib){
$s .= $this->nz($attrib);
}
return $s;
}
/**
* Print the tag attribute if it is not blank, such as id="$this->id"
* @param string $attrib
* @return string
*/
function nz($attrib){
$s = '';
if($this->$attrib != '') $s = $attrib .' = "' . $this->$attrib . '"';
return $s;
}
//This causes RECURSION because of parsing between double quotes. This is a very UNEXPECTED behaviour!
function nz_($attrib){
$s = '';
if($this->$attrib != '') $s = "$attrib = \"$this->$attrib\"";
return $s;
}
}//end class
//usage
$a = new my_tag_A('abc.php', 'ABC'); $a->target = '_blank';
echo $a;
//prints:
// <a href="abc.php" target="_blank" >ABC</a>
?>
class B extends A {
}
$b = new B;
$b->var1 = 5;
eval('$new_b = ' . var_export($b, true) . ';');
var_dump($new_b);
/*
object(A)#2 (1) {
["var1"]=>
int(5)
}
*/
?>
function __construct()
{
$__imported = Array();
$__imported_functions = Array();
}
function Imports($object)
{
$new_imports = new $object();
$imports_name = get_class($new_imports);
array_push( $__imported, Array($imports_name,$new_imports) );
$imports_function = get_class_methods($new_imports);
foreach ($imports_function as $i=>$function_name)
{
$this->__imported_functions[$function_name] = &$new_imports;
}
}
function Msg()
{
echo "Hello world<br />";
}
}
$b = new B();
$b->Msg();
// or call $b->Imports("ExternalFunc");
$b->TestB();
//$b->TestB(1,3,4);
?>
class foo {
}
class a {
private $var1;
function __construct(foo &$obj = NULL) {
$this->var1 = &$obj;
}
/** Return its variables array, if its parent exists and the __sleep method is accessible, call it and push the result into
the array and return the whole thing. */
public function __sleep() {
$a = array_keys(get_object_vars(&$this));
if (method_exists(parent, '__sleep')) {
$p = parent::__sleep();
array_push($a, $p);
};
return $a;
}
}
class b extends a {
function __construct(foo &$obj = NULL) {
parent::__construct($obj);
}
}
session_start();
$myfoo = &new foo();
$myb = &new b($myfoo);
$myb = unserialize(serialize(&$myb));
?>
This should work, I haven't tested deeper.
Example:
<?php
class user {
/**
* @var int Gets and sets the user ID
*/
public $UserID;
private $_userID;
public function __construct() {
// All the magic is in single line:
// We unset public property, so our setters and getters
// are used and phpDoc and editors with code completition are happy
unset($this->UserID);
}
public function __set($key, $value) {
// assign value for key UserID to _userID property
}
public function __get($key) {
// return value of _userID for UserID property
}
}
?>
}
?>
3. private members should be named using "\0" . class name . "\0" . member name, like so:
<?php
class Foo {
private $bar;
public function __sleep() {
return array("\0Foo\0bar");
}
}
?>
So with this information let us serialize a class hierarchy correctly:
<?php
class Base {
private $foo = "foo_value";
protected $bar = "bar_value";
public function __sleep() {
return array("\0Base\0foo", "\0*\0bar");
}
}
class Derived extends Base {
public $baz = "baz_value";
private $boo = "boo_value";
public function __sleep() {
// we have to merge our members with our parent's
return array_merge(array("baz", "\0Derived\0boo"), parent::__sleep());
}
}
class Leaf extends Derived {
private $qux = "qux_value";
protected $zaz = "zaz_value";
public $blah = "blah_value";
public function __sleep() {
// again, merge our members with our parent's
return array_merge(array("\0Leaf\0qux", "\0*\0zaz", "blah"), parent::__sleep());
}
}
// test it
$test = new Leaf();
$s = serialize($test);
$test2 = unserialize($s);
echo $s;
print_r($test);
print_r($test2);
?>
Now if you comment out all of the __sleep() functions and output the serialized string, you will see that the output doesn't
change. The most important part of course is that with the proper __sleep() functions, we can unserialize the string and get a
properly set up object.
I hope this solves the mystery for everybody. __sleep() does work, if you use it correctly :-)
function __sleep()
{
function __construct($name){
$this->name=$name;
$this->statics=array();
}
function setStatic($k,$v){
if(!is_resource($v))
$this->statics[$k]=$v;
}
function __wakeup(){
foreach($this->statics as $k=>$v)
eval($this->name."::\$".$k."=\$this->statics['".$k."'];");
}
}
function storeStaticAttributes(){
$classes=get_declared_classes();
foreach($classes as $name){
$reflect=new ReflectionClass($name);
if($reflect->isUserDefined()){
$statics=$reflect->getStaticProperties();
if(empty($_SESSION["_classes"]))
$_SESSION["_classes"]=array();
if(empty($_SESSION["_classes"][$name]))
$_SESSION["_classes"][$name]=new Classes($name);
foreach($statics as $k=>$v)
$_SESSION["_classes"][$name]->setStatic($k,$v);
}
}
}
register_shutdown_function('storeStaticAttributes');
?>
} else {
$str = '';
$values = array_values($var);
for ( $i=0; $i<count($values); $i++ ) {
$str .= "'".$values[$i]."' ,";
}
$str = substr($str, 0, -2);
return eval('return $this->classObject->'.$func.'('.$str.');');
}
}
}
?>
So if you'll do a $myClass->unknownMethod() and it is found neither in MyClass nor in SomeAbstractUnknownClass, MyClass will
try to call this method in $classObject.
I use this for 'extending' a UserObject-Class which already extends an other one.
Better solutions are always welcome ;)
whatever was returned by __get. To be more verbose, the above code is essentially identical to:
$tmp_array = &$myclass->foo;
$tmp_array['bar'] = 'baz';
unset($tmp_array);
Therefore, the above won't do anything if your __get implementation resembles this:
function __get($name) {
return array_key_exists($name, $this->values)
? $this->values[$name] : null;
}
You will actually need to set the value in __get and return that, as in the following code:
function __get($name) {
if (!array_key_exists($name, $this->values))
$this->values[$name] = null;
return $this->values[$name];
}
use (method call or property operation), and, if the class exists, magically "complete" itself and turn into the desired
object.
else
trigger_error('Caller is no object!', E_USER_ERROR);
}
function __destruct() {
$this->setModuleInformation();
if (method_exists($this->caller, '__deinit'))
$this->caller->__deinit();
$this->unsetModuleInformation();
}
function __isset($isset) {
$this->setModuleInformation();
if (is_object($this->caller))
$return = isset($this->caller->{$isset});
else
trigger_error('Caller is no object!', E_USER_ERROR);
$this->unsetModuleInformation();
return $return;
}
function __unset($unset) {
$this->setModuleInformation();
if (is_object($this->caller)) {
if (isset($this->caller->{$unset}))
unset($this->caller->{$unset});
}
else
trigger_error('Caller is no object!', E_USER_ERROR);
$this->unsetModuleInformation();
}
function __set($set, $val) {
$this->setModuleInformation();
if (is_object($this->caller))
$this->caller->{$set} = $val;
else
trigger_error('Caller is no object!', E_USER_ERROR);
$this->unsetModuleInformation();
}
function __get($get) {
$this->setModuleInformation();
if (is_object($this->caller)) {
if (isset($this->caller->{$get}))
$return = $this->caller->{$get};
else
$return = false;
}
else
trigger_error('Caller is no object!', E_USER_ERROR);
$this->unsetModuleInformation();
return $return;
}
function setModuleInformation() {
$this->caller->module = $this->module;
}
function unsetModuleInformation() {
$this->caller->module = NULL;
}
}
// Well this can be a Config Class?
class Config {
public $module;
public $test;
function __construct()
{
print('Constructor will have no Module Information... Use __init() instead!<br />');
print('--> '.print_r($this->module, 1).' <--');
print('<br />');
print('<br />');
$this->test = '123';
}
function __init()
{
print('Using of __init()!<br />');
print('--> '.print_r($this->module, 1).' <--');
print('<br />');
print('<br />');
}
456
789
Guestbook
return true;
}
function __sleep()
{
$return_arr[] = "info"
return $return_arr;
}
}
this way subcore is being serialized by core when core is being serialized. subcore handles its own data and core stores it as
a serialize string inside itself. on wakeup core unserializes subcore.
this may have a performance cost, but if you have many objects connected this way this is the best way of serializing them.
you only need to serialize the the main object wich will serialize all those below which will serialize all those below them
again. in effect causing a sort of chainreaction in wich each object takes care of its own info.
offcoarse you always need to store the eventualy serialized string in a safe place. somebody got experience with this way of
__wakeup and __sleep.
works in PHP4&5
__sleep() handles protected/private properties very well. You should never rely on get_class_vars() to retrieve property names
since this function only returns the public properties. Use the Reflection API instead for that purpose. Better yet, if you
know which ones you want to save it is always faster to specify the return array manually.
{
public function getContents($pos, $len) { ...stuff... }
}
class CryptedStorageClass extends SomeStupidStorageClass
{
private $decrypted_block;
public function getContents($pos, $len) { ...decrypt... }
}
?>
If SomeStupidStorageClass decided to serialize its subclasses' data as well as its own, a portion of what was once an encrypted
thingie could be stored, in the clear, wherever the thingie was stored. Obviously, CryptedStorageClass would never have chosen
this...but it had to either know how to serialize its parent class's data without calling parent::_sleep(), or let the base
class do what it wanted to.
Considering encapsulation again, no class should have to know how the parent handles its own private data. And it certainly
shouldn't have to worry that users will find a way to break access controls in the name of convenience.
If a class wants both to have private/protected data and to survive serialization, it should have its own __sleep() method
which asks the parent to report its own fields and then adds to the list if applicable. Like so....
<?php
class BetterClass
{
private $content;
public function __sleep()
{
return array('basedata1', 'basedata2');
}
public function getContents() { ...stuff... }
}
class BetterDerivedClass extends BetterClass
{
private $decrypted_block;
public function __sleep()
{
return parent::__sleep();
}
public function getContents() { ...decrypt... }
}
?>
The derived class has better control over its data, and we don't have to worry about something being stored that shouldn't be.
However, when automating __sleep it becomes necissary to have access to the private/protected subclass variables because their
names have to be returned by __sleep.
So here is the work around:
<?php
public function __sleep()
{
... code ...
$sleepVars = array_keys((array)$this);
return $sleepVars;
}
?>
Even though array_keys includes more information about the variable names than just the variable names -- it still seems to
work appropriately.
Magic Methods
The function names __construct(), __destruct(), __call(), __callStatic(), __get(), __set(), __isset(), __unset(),
__sleep(), __wakeup(), __toString(), __invoke(), __set_state() and __clone() are magical in PHP classes. You
cannot have functions with these names in any of your classes unless you want the magic functionality
associated with them.
Caution
PHP reserves all function names starting with __ as magical. It is recommended that you do not
use function names with __ in PHP unless you want some documented magic functionality.
publicfunction__construct($server,$username,$password,$db)
{
$this->server=$server;
$this->username=$username;
$this->password=$password;
$this->db=$db;
$this->connect();
}
privatefunctionconnect()
{
$this->link=mysql_connect($this->server,$this->username,$this->password);
mysql_select_db($this->db,$this->link);
}
publicfunction__sleep()
{
returnarray('server','username','password','db');
}
publicfunction__wakeup()
{
$this->connect();
}
}
?>
__toString()
public string __toString ( void )
The __toString() method allows a class to decide how it will react when it is treated like a string. For example,
what echo $obj; will print. This method must return a string, as otherwise a fatal E_RECOVERABLE_ERROR level error is
emitted.
Example #2 Simple example
<?php
//Declareasimpleclass
classTestClass
{
public$foo;
publicfunction__construct($foo)
{
$this->foo=$foo;
}
publicfunction__toString()
{
return$this->foo;
}
}
$class=newTestClass('Hello');
echo$class;
?>
It is worth noting that before PHP 5.2.0 the __toString() method was only called when it was directly combined
with echo or print. Since PHP 5.2.0, it is called in any string context (e.g. in printf() with %s modifier) but not
in other types contexts (e.g. with %d modifier). Since PHP 5.2.0, converting objects without __toString()
method to string would cause E_RECOVERABLE_ERROR.
__invoke()
mixed __invoke ([
$...
] )
The __invoke() method is called when a script tries to call an object as a function.
Note:
This feature is available since PHP 5.3.0.
Example #3 Using __invoke()
<?php
classCallableClass
{
publicfunction__invoke($x)
{
var_dump($x);
}
}
$obj=newCallableClass;
$obj(5);
var_dump(is_callable($obj));
?>
__set_state()
static object __set_state ( array
$properties
This static method is called for classes exported by var_export() since PHP 5.1.0.
The only parameter of this method is an array containing exported properties in the form array('property' =>
value, ...).
Example #4 Using __set_state() (since PHP 5.1.0)
<?php
classA
{
public$var1;
public$var2;
publicstaticfunction__set_state($an_array)//AsofPHP5.1.0
{
$obj=newA;
$obj->var1=$an_array['var1'];
$obj->var2=$an_array['var2'];
return$obj;
}
}
$a=newA;
$a->var1=5;
$a->var2='foo';
eval('$b='.var_export($a,true).';');//$b=A::__set_state(array(
//'var1'=>5,
//'var2'=>'foo',
//));
var_dump($b);
?>
Magic Methods
return $result;
}
}
$obj1=new MyBaseClass();
echo ($s1=serialize($obj1))."<br>"; // only test2 is stored, as it was changed in the constructor
$obj2=new MyBaseClass();
$obj2->name='object 2'; // change default value here
echo ($s2=serialize($obj2))."<br>"; // stored name and test2
$obj3=new MyBaseClass();
$obj3->test2=NULL; // switch back to default value
echo ($s3=serialize($obj3))."<br>"; // nothing is stored but the class name
// let us check if we can retrieve the objects
unset($obj1, $obj2, $obj3);
$obj1=unserialize($s1);
$obj2=unserialize($s2);
$obj3=unserialize($s3);
var_dump($obj1);
var_dump($obj2);
var_dump($obj3);
?>
public static function throwToStringException($exception)
{
// Should not occur with prescribed usage, but in case of recursion: clean out exception, return a valid string, and
weep
if (isset(self::$_toStringException))
{
self::$_toStringException = null;
return '';
}
self::$_toStringException = $exception;
return null;
}
}
class My_Class
{
public function doComplexStuff()
{
throw new Exception('Oh noes!');
}
public function __toString()
{
try
{
// do your complex thing which might trigger an exception
return $this->doComplexStuff();
}
catch (Exception $e)
{
// The 'return' is required to trigger the trick
return My_ToStringFixer::throwToStringException($e);
}
}
}
$x = new My_Class();
try
{
echo $x;
}
catch (Exception $e)
{
echo 'Caught Exception! : '. $e;
}
?>
{
echo "Invoking A() Class";
}
}
class B
{
public $a;
class InvokeSingleParam {
function __invoke($param1)
{
print __METHOD__ . PHP_EOL;
print "Value of \$param1 is: " . $param1 . PHP_EOL . PHP_EOL;
}
}
class InvokeMultiParams {
function __invoke($param1, $param2, $param3) {
print __METHOD__ . PHP_EOL;
print "Value of \$param1 is: " . $param1 . PHP_EOL;
print "Value of \$param2 is: " . $param2 . PHP_EOL;
print "Value of \$param3 is: " . $param3 . PHP_EOL . PHP_EOL;
}
}
$no(1, 2, 3);
$single('one param');
$multi('param 1', 'param 2', 'param 3');
?>
This outputs:
InvokeNoParams::__invoke
The value of $param1 is: 1
The value of $param2 is: 2
The value of $param3 is: 3
InvokeSingleParam::__invoke
Value of $param1 is: one param
InvokeMultiParams::__invoke
Value of $param1 is: param 1
Value of $param2 is: param 2
Value of $param3 is: param 3
Thought I would just explain something about __get and __set here.
Imgine you have a class like so:
<?php
class user_bo{
protected $attributes = Array();
?>
I return null if my variable don't exist.
function __construct() {
$this->test_obj = new stdClass();
}
function __get($prop) {
return $this->$prop;
}
function __set($prop, $val) {
$this->$prop = $val;
}
}
$a = new A();
?>
Combined Operators (.=, +=, *=, etc): you must also define a companion __get() method to grant write -and- read access to the
property. Remember, "$x += $y" is shorthand for "$x = $x + $y". In other words, "__set($x, (__get($x) + $y))".
Properties that are Arrays: attempting to set array values like "$a->test_array[] = 'asdf';" from outside this object will
result in an "Indirect modification of overloaded property" notice and the operation completely ignored. You can't use '[]'
for array value assignment in this context (with the exception only if you made __get() return by reference, in which case, it
would work fine and bypass the __set() method altogether). You can work around this doing something like unioning the array
instead:
<?php
$a->test_array[] = 'asdf'; // notice given and ignored unless __get() was declared to return by reference
$a->test_array += array(1 => 'asdf'); // to add a key/value
$a->test_array = array("key" => 'asdf') + $a->test_array; // to overwrite a key/value.
?>
Properties that are Objects: as long as you have that __get() method, you can freely access and alter that sub object's own
properties, bypassing __set() entirely. Remember, objects are assigned and passed by reference naturally.
<?php
$a->test_obj->prop = 1; // fine if $a did not have a set method declared.
?>
All above tested in 5.3.2.
}
$p1 = new point(1,1);
$p2 = new point(23,-6);
$p3 = new point(15,20);
echo $p1($p2,$p3,$p1); // round trip 73.89
?>
Functionally, __invoke() can also be used to mimic the use of variable functions. Sadly, attempting any calling of __invoke()
on a static level will produce a fatal error.
{
if(method_exists($this,$MethodName='set_'.$varName))
return $this->$MethodName($value);
else
trigger_error($varName.' is not avaliable .',E_USER_ERROR);
}
private function set_Surname($value)
{
// On surname changed Events.
echo "Surname Changed as ".$value."\r\n";
$this->_Surname=$value;
}
private function get_Surname()
{
// On Get Events.
echo "Surname Getted\r\n";
return $this->_Surname;
}
private function set_Name($value)
{
// On Name changed Events.
echo "Name Changed as ".$value."\r\n";
$this->_Name=$value;
}
private function get_Name()
{
// On Get Name Events.
echo "Name getted\r\n";
return $this->_Name;
}
}
?>
Using :
<?php
$get=new GetSet();
$get->Name="Tufan Baris";
$get->Surname="YILDIRIM";
echo $get->Name.' '.$get->Surname;
?>
Output
Name Changed as Tufan Baris
Surname Changed asYILDIRIM
Name getted
Surname Getted
Tufan Baris YILDIRIM
HOW IT HAPPENED:
I use to have a logging function called LogEnter() and another called LogExit() that I use to call at every function when
intensively debugging.
For example all my functions look like:
<?php
function AnyFunction( $a, $b, $c )
{
LogEnter();
// DoSomeStuffHere.
LogExit();
}
?>
Then inside LogEnter(), I use debug_backtrace() to discover the parameters that were used when calling the function (in this
example the values of $a, $b, $c).
The LogEnter() deals without knowing the number of parameters to treat, and formats something like ( 2, 3, 4 ) if those were
the values. That info is printed among other info to a log-file.
Doing this, keeps me with a single-very-simple way to trace all te calls.
The thing is that if the the following are met at the same time, I have a problem:
- The function accepts (and is called with) a class as a parameter.
- The function is being debuggd by calling LogEnter();
- The class does not have __toString() coded.
- For some reason I am not allowed to edit the class nor inherit from it.
Then the debug function performs an "Object of class X could not be converted to string" error when trying to "dump" the
parameter list (containing the object) into the file.
LOG FUNCTION:
This looks for the "parameters array" and then calls BuildArgs to format them. LogWrite finds the function name, file, line
number, and so on, but does not matter here.
<?php
public function LogEnter()
{
$Debug = debug_backtrace();
$DebugLine = $Debug[ 1 ];
// [0] would be this very same function.
// [1] is the caller of this function.
$Args = isset( $DebugLine[ 'args' ] ) ? $DebugLine[ 'args' ] : '[x]';
$Msg = 'Entering with ' . BuildArgs( $Args );
LogWrite( $Msg );
}
// CAUTION, THIS NEXT ONE FAILS
private function BuildArgs( $Args )
{
$Result = '( ' . implode( ', ', $Args ) . ' )';
return $Result;
}
?>
The problem is that if you call a function where a parameter being an object that has not the __toString() defined, then
automatically "imploding" the argument list into a string, fails.
SOLUTION:
The solution is that the __toString is "checkable for its existance". The "tips" are to manually traverse the result of
debug_backtrace()['args'] and then, for each element, check:
- if it is or not an object with is_object( $Value )
- in case it is, check if __toString() is defined with method_exists( $Value, '__toString' )
- in case it is not an object or the method exist, perform a normal assignation.
- in case it is an object without that method, print something else. I choose to output the class name with get_class( $Value
) and put it between square brackets.
The final function becomes like this:
<?php
private function BuildArgs( $Args )
{
// This function gets an 'args' array from debug_backtrace() and formats it in the printable format (a, b, c) including the
parenthesis.
$FormattedParams = '';
$Glue = '';
foreach( $Args as $Value )
{
if( is_object( $Value ) )
{
if( method_exists( $Value, "__toString" ) )
{
$PrintableValue = $Value;
}
else
{
$PrintableValue = '[' . get_class( $Value ) . ']';
}
}
else
{
$PrintableValue = $Value;
}
$FormattedParams .= $Glue . $PrintableValue;
$Glue = ', ';
}
$Result = sprintf( '( %s )', $FormattedParams );
return $Result;
}
?>
give and error in this case as it is evaluated first as a function, not as a class and so __autoload won't be triggered.
public $label='';
$this->label = $label;
}
function nz_arr($attrib_arr){
$s = '';
foreach($attrib_arr as $attrib){
$s .= $this->nz($attrib);
}
return $s;
}
/**
* Print the tag attribute if it is not blank, such as id="$this->id"
* @param string $attrib
* @return string
*/
function nz($attrib){
$s = '';
if($this->$attrib != '') $s = $attrib .' = "' . $this->$attrib . '"';
return $s;
}
//This causes RECURSION because of parsing between double quotes. This is a very UNEXPECTED behaviour!
function nz_($attrib){
$s = '';
if($this->$attrib != '') $s = "$attrib = \"$this->$attrib\"";
return $s;
}
}//end class
//usage
$a = new my_tag_A('abc.php', 'ABC'); $a->target = '_blank';
echo $a;
//prints:
// <a href="abc.php" target="_blank" >ABC</a>
?>
class B extends A {
}
$b = new B;
$b->var1 = 5;
eval('$new_b = ' . var_export($b, true) . ';');
var_dump($new_b);
/*
object(A)#2 (1) {
["var1"]=>
int(5)
}
*/
?>
function __construct()
{
$__imported = Array();
$__imported_functions = Array();
}
function Imports($object)
{
$new_imports = new $object();
$imports_name = get_class($new_imports);
array_push( $__imported, Array($imports_name,$new_imports) );
$imports_function = get_class_methods($new_imports);
foreach ($imports_function as $i=>$function_name)
{
$this->__imported_functions[$function_name] = &$new_imports;
}
}
function Msg()
{
echo "Hello world<br />";
}
}
$b = new B();
$b->Msg();
// or call $b->Imports("ExternalFunc");
$b->TestB();
//$b->TestB(1,3,4);
?>
class foo {
}
class a {
private $var1;
function __construct(foo &$obj = NULL) {
$this->var1 = &$obj;
}
/** Return its variables array, if its parent exists and the __sleep method is accessible, call it and push the result into
the array and return the whole thing. */
public function __sleep() {
$a = array_keys(get_object_vars(&$this));
if (method_exists(parent, '__sleep')) {
$p = parent::__sleep();
array_push($a, $p);
};
return $a;
}
}
class b extends a {
function __construct(foo &$obj = NULL) {
parent::__construct($obj);
}
}
session_start();
$myfoo = &new foo();
$myb = &new b($myfoo);
$myb = unserialize(serialize(&$myb));
?>
This should work, I haven't tested deeper.
Example:
<?php
class user {
/**
* @var int Gets and sets the user ID
*/
public $UserID;
private $_userID;
public function __construct() {
// All the magic is in single line:
// We unset public property, so our setters and getters
// are used and phpDoc and editors with code completition are happy
unset($this->UserID);
}
public function __set($key, $value) {
// assign value for key UserID to _userID property
}
public function __get($key) {
// return value of _userID for UserID property
}
}
?>
}
?>
3. private members should be named using "\0" . class name . "\0" . member name, like so:
<?php
class Foo {
private $bar;
public function __sleep() {
return array("\0Foo\0bar");
}
}
?>
So with this information let us serialize a class hierarchy correctly:
<?php
class Base {
private $foo = "foo_value";
protected $bar = "bar_value";
public function __sleep() {
return array("\0Base\0foo", "\0*\0bar");
}
}
class Derived extends Base {
public $baz = "baz_value";
private $boo = "boo_value";
public function __sleep() {
// we have to merge our members with our parent's
return array_merge(array("baz", "\0Derived\0boo"), parent::__sleep());
}
}
class Leaf extends Derived {
private $qux = "qux_value";
protected $zaz = "zaz_value";
public $blah = "blah_value";
public function __sleep() {
// again, merge our members with our parent's
return array_merge(array("\0Leaf\0qux", "\0*\0zaz", "blah"), parent::__sleep());
}
}
// test it
$test = new Leaf();
$s = serialize($test);
$test2 = unserialize($s);
echo $s;
print_r($test);
print_r($test2);
?>
Now if you comment out all of the __sleep() functions and output the serialized string, you will see that the output doesn't
change. The most important part of course is that with the proper __sleep() functions, we can unserialize the string and get a
properly set up object.
I hope this solves the mystery for everybody. __sleep() does work, if you use it correctly :-)
function __sleep()
{
function __construct($name){
$this->name=$name;
$this->statics=array();
}
function setStatic($k,$v){
if(!is_resource($v))
$this->statics[$k]=$v;
}
function __wakeup(){
foreach($this->statics as $k=>$v)
eval($this->name."::\$".$k."=\$this->statics['".$k."'];");
}
}
function storeStaticAttributes(){
$classes=get_declared_classes();
foreach($classes as $name){
$reflect=new ReflectionClass($name);
if($reflect->isUserDefined()){
$statics=$reflect->getStaticProperties();
if(empty($_SESSION["_classes"]))
$_SESSION["_classes"]=array();
if(empty($_SESSION["_classes"][$name]))
$_SESSION["_classes"][$name]=new Classes($name);
foreach($statics as $k=>$v)
$_SESSION["_classes"][$name]->setStatic($k,$v);
}
}
}
register_shutdown_function('storeStaticAttributes');
?>
} else {
$str = '';
$values = array_values($var);
for ( $i=0; $i<count($values); $i++ ) {
$str .= "'".$values[$i]."' ,";
}
$str = substr($str, 0, -2);
return eval('return $this->classObject->'.$func.'('.$str.');');
}
}
}
?>
So if you'll do a $myClass->unknownMethod() and it is found neither in MyClass nor in SomeAbstractUnknownClass, MyClass will
try to call this method in $classObject.
I use this for 'extending' a UserObject-Class which already extends an other one.
Better solutions are always welcome ;)
whatever was returned by __get. To be more verbose, the above code is essentially identical to:
$tmp_array = &$myclass->foo;
$tmp_array['bar'] = 'baz';
unset($tmp_array);
Therefore, the above won't do anything if your __get implementation resembles this:
function __get($name) {
return array_key_exists($name, $this->values)
? $this->values[$name] : null;
}
You will actually need to set the value in __get and return that, as in the following code:
function __get($name) {
if (!array_key_exists($name, $this->values))
$this->values[$name] = null;
return $this->values[$name];
}
use (method call or property operation), and, if the class exists, magically "complete" itself and turn into the desired
object.
else
trigger_error('Caller is no object!', E_USER_ERROR);
}
function __destruct() {
$this->setModuleInformation();
if (method_exists($this->caller, '__deinit'))
$this->caller->__deinit();
$this->unsetModuleInformation();
}
function __isset($isset) {
$this->setModuleInformation();
if (is_object($this->caller))
$return = isset($this->caller->{$isset});
else
trigger_error('Caller is no object!', E_USER_ERROR);
$this->unsetModuleInformation();
return $return;
}
function __unset($unset) {
$this->setModuleInformation();
if (is_object($this->caller)) {
if (isset($this->caller->{$unset}))
unset($this->caller->{$unset});
}
else
trigger_error('Caller is no object!', E_USER_ERROR);
$this->unsetModuleInformation();
}
function __set($set, $val) {
$this->setModuleInformation();
if (is_object($this->caller))
$this->caller->{$set} = $val;
else
trigger_error('Caller is no object!', E_USER_ERROR);
$this->unsetModuleInformation();
}
function __get($get) {
$this->setModuleInformation();
if (is_object($this->caller)) {
if (isset($this->caller->{$get}))
$return = $this->caller->{$get};
else
$return = false;
}
else
trigger_error('Caller is no object!', E_USER_ERROR);
$this->unsetModuleInformation();
return $return;
}
function setModuleInformation() {
$this->caller->module = $this->module;
}
function unsetModuleInformation() {
$this->caller->module = NULL;
}
}
// Well this can be a Config Class?
class Config {
public $module;
public $test;
function __construct()
{
print('Constructor will have no Module Information... Use __init() instead!<br />');
print('--> '.print_r($this->module, 1).' <--');
print('<br />');
print('<br />');
$this->test = '123';
}
function __init()
{
print('Using of __init()!<br />');
print('--> '.print_r($this->module, 1).' <--');
print('<br />');
print('<br />');
}
456
789
Guestbook
return true;
}
function __sleep()
{
$return_arr[] = "info"
return $return_arr;
}
}
this way subcore is being serialized by core when core is being serialized. subcore handles its own data and core stores it as
a serialize string inside itself. on wakeup core unserializes subcore.
this may have a performance cost, but if you have many objects connected this way this is the best way of serializing them.
you only need to serialize the the main object wich will serialize all those below which will serialize all those below them
again. in effect causing a sort of chainreaction in wich each object takes care of its own info.
offcoarse you always need to store the eventualy serialized string in a safe place. somebody got experience with this way of
__wakeup and __sleep.
works in PHP4&5
__sleep() handles protected/private properties very well. You should never rely on get_class_vars() to retrieve property names
since this function only returns the public properties. Use the Reflection API instead for that purpose. Better yet, if you
know which ones you want to save it is always faster to specify the return array manually.
{
public function getContents($pos, $len) { ...stuff... }
}
class CryptedStorageClass extends SomeStupidStorageClass
{
private $decrypted_block;
public function getContents($pos, $len) { ...decrypt... }
}
?>
If SomeStupidStorageClass decided to serialize its subclasses' data as well as its own, a portion of what was once an encrypted
thingie could be stored, in the clear, wherever the thingie was stored. Obviously, CryptedStorageClass would never have chosen
this...but it had to either know how to serialize its parent class's data without calling parent::_sleep(), or let the base
class do what it wanted to.
Considering encapsulation again, no class should have to know how the parent handles its own private data. And it certainly
shouldn't have to worry that users will find a way to break access controls in the name of convenience.
If a class wants both to have private/protected data and to survive serialization, it should have its own __sleep() method
which asks the parent to report its own fields and then adds to the list if applicable. Like so....
<?php
class BetterClass
{
private $content;
public function __sleep()
{
return array('basedata1', 'basedata2');
}
public function getContents() { ...stuff... }
}
class BetterDerivedClass extends BetterClass
{
private $decrypted_block;
public function __sleep()
{
return parent::__sleep();
}
public function getContents() { ...decrypt... }
}
?>
The derived class has better control over its data, and we don't have to worry about something being stored that shouldn't be.
However, when automating __sleep it becomes necissary to have access to the private/protected subclass variables because their
names have to be returned by __sleep.
So here is the work around:
<?php
public function __sleep()
{
... code ...
$sleepVars = array_keys((array)$this);
return $sleepVars;
}
?>
Even though array_keys includes more information about the variable names than just the variable names -- it still seems to
work appropriately.
Final Keyword
PHP 5 introduces the final keyword, which prevents child classes from overriding a method by prefixing the
definition with final. If the class itself is being defined final then it cannot be extended.
Example #1 Final methods example
<?php
classBaseClass{
publicfunctiontest(){
echo"BaseClass::test()called\n";
}
finalpublicfunctionmoreTesting(){
echo"BaseClass::moreTesting()called\n";
}
}
classChildClassextendsBaseClass{
publicfunctionmoreTesting(){
echo"ChildClass::moreTesting()called\n";
}
}
//ResultsinFatalerror:CannotoverridefinalmethodBaseClass::moreTesting()
?>
Note: Properties cannot be declared final, only classes and methods may be declared as final.
Final Keyword
The behaviour of FINAL is not as serious as you may think. A little explample:
<?php
class A {
final private function method(){}
}
class B extends A {
private function method(){}
}
?>
Normally you would expect some of the following will happen:
- An error that final and private keyword cannot be used together
- No error as the private visibility says, that a method/var/etc. is only visible within the same class
But what happens is PHP is a little curios: "Cannot override final method A::method()"
So its possible to deny method names in subclasses! Don't know if this is a good behavior, but maybe its useful for your
purpose.
$m->invoke(null);
return;
}
throw new Exception('Class or interface not found ' . $className);
}
?>
This can only work when one class is defined per file, since we are assured that __autoload() will be called to load the file
containing the class.
eg:
test2.php:
<?php
class B {
public static $X;
private static function __init() {
echo 'B', "\n";
self::$X = array(1, 2);
}
}
class A {
public static $Y;
private static function __init() {
echo 'A', "\n";
self::$Y = array(3, 4);
}
}
?>
test.php:
<?php
function __autoload($n) {
if ($n == 'A' || $n == 'B') require 'test2.php';
... do our __init() trick ...
}
var_dump(B::$X); // shows B, then array(2) (1, 2)
var_dump(A::$Y); // shows NULL.
?>
//will work
public function morningNews(){
print 'good morning world!';
}
//will work
final public function eveningNews(){
print 'good evening world!';
}
//will work
final function nightNews(){
print 'good night world!';
}
}
print news::micCheck; // output: test1
print '<br/>';
print news::$promptCheck; // output: test2
print '<br/>';
print news::morningNews(); // output: good morning world!
print '<br/>';
print news::eveningNews(); // output: good evening world!
print '<br/>';
print news::nightNews(); // output: good night world!
?>
Object Cloning
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.
$copy_of_object = clone $object;
When an object is cloned, PHP 5 will perform a shallow copy of all of the object's properties. Any properties that
are references to other variables, will remain references.
void __clone ( void )
Once the cloning is complete, if a __clone() method is defined, then the newly created object's __clone()
method will be called, to allow any necessary properties that need to be changed.
Example #1 Cloning an object
<?php
classSubObject
{
static$instances=0;
public$instance;
publicfunction__construct(){
$this->instance=++self::$instances;
}
publicfunction__clone(){
$this->instance=++self::$instances;
}
}
classMyCloneable
{
public$object1;
public$object2;
function__clone()
{
//Forceacopyofthis->object,otherwise
//itwillpointtosameobject.
$this->object1=clone$this->object1;
}
}
$obj=newMyCloneable();
$obj->object1=newSubObject();
$obj->object2=newSubObject();
$obj2=clone$obj;
print("OriginalObject:\n");
print_r($obj);
print("ClonedObject:\n");
print_r($obj2);
?>
Object Cloning
</pre>
olivier dot pons at goo dot without dot oo dot mail dot com 18-Mar-2010 10:14
If you think "clone" will create a new instance, thus calling "__constructor", you're wrong. clone seems to only allocate
memory for the object cloned, and simply copies the variables memory from the original to the new one (imagine something alike
memcpy() in C). Nothing more. Keep in mind you'll have to do all the rest by yourself.
<?php
unset($ref);
var_dump($a);
/*
object(A)#1 (1) {
["p"]=>
string(5) "Hello"
}
*/
?>
I interpret this as the reference-count jumping from 2 straight to 0. However...
2. It IS possible to create a reference with a reference count of 1 - i.e. to convert an property from value type to reference
type, without any extra references. All you have to do is declare that it refers to itself. This is HIGHLY idiosyncratic, but
nevertheless it works. This leads to the observation that although the manual states that 'Any properties that are references
to other variables, will remain references,' this is not strictly true. Any variables that are references, even to *themselves*
(not necessarily to other variables), will be copied by reference rather than by value.
Here's an example to demonstrate:
<?php
class ByVal
{
var $prop;
}
class ByRef
{
var $prop;
function __construct() { $this->prop =& $this->prop; }
}
$a = new ByVal;
$a->prop = 1;
$b = clone $a;
$b->prop = 2; // $a->prop remains at 1
$a = new ByRef;
$a->prop = 1;
$b = clone $a;
$b->prop = 2; // $a->prop is now 2
?>
<?php
class Foo
{
function __construct()
{
$this->_myself = $this;
}
function __clone() {
foreach ($this as $key => $val) {
if (is_object($val) || (is_array($val))) {
$this->{$key} = unserialize(serialize($val));
}
}
}
}
// this object references itself
$foo = new Foo();
// create a deep clone
$bar = clone $foo;
// check if we reach this point
echo 'Finished cloning!';
?>
Replacing the __clone() method with the one shown in edit by danbrown at php dot net we run into an infinite loop, and we
never get message 'Finished cloning!'.
[EDIT BY danbrown AT php DOT net: An almost exact function was contributed on 02-DEC-2008-10:18 by (david ashe AT metabin):
<?php
function __clone(){
foreach($this as $name => $value){
if(gettype($value)=='object'){
$this->$name= clone($this->$name);
}
}
}
?>
Giving credit where it's due. ~DPB]
$general->issueCommand('Retreat!');
echo $platoon[26] . '<br>';
echo $platoon[197] . '<br>';
?>
COMMAND ISSUED: Deploy Troops
Soldier[id=1, status=deploying]
Soldier[id=225, status=deploying]
Soldier[id=14, status=deploying]
COMMAND ISSUED: March Forward
Soldier[id=49, status=marching forward]
Soldier[id=165, status=marching forward]
COMMAND ISSUED: Fire!
Soldier[id=250, status=shot fired]
Soldier[id=70, status=shot fired]
COMMAND ISSUED: Retreat!
Soldier[id=28, status=course reversed]
Soldier[id=199, status=course reversed]
$y['bar'] == 10;
Hope this will be useful.
By the way, to determine whether the variable is compatible with ArrayAccess/ArrayObject see
http://php.net/manual/en/function.is-array.php#48083
public $bbb;
function __clone(){
$this->a = clone $this->a;//clone MANUALLY!!!
}
}
$b1 = new B();
$b1->a = new A();
$b1->a->aaa = 111;
$b1->bbb = 1;
$b2 = clone $b1;
$b2->a->aaa = 222;//BEWARE!!
$b2->bbb = 2;//no problem on basic types
var_dump($b1); echo '<br />';
var_dump($b2);
/*
OUTPUT BEFORE implementing the function __clone()
object(B)#2 (3) { ["a"]=> object(A)#3 (1) { ["aaa"]=> int(222) } ["bbb"]=> int(1) }
object(B)#4 (3) { ["a"]=> object(A)#3 (1) { ["aaa"]=> int(222) } ["bbb"]=> int(2) }
OUTPUT AFTER implementing the function __clone()
object(B)#1 (3) { ["a"]=> object(A)#2 (1) { ["aaa"]=> int(111) } ["bbb"]=> int(1) }
object(B)#3 (3) { ["a"]=> object(A)#4 (1) { ["aaa"]=> int(222) } ["bbb"]=> int(2) }
*/
?>
Whenever we use another class inside, we must clone it manually. If you have 10s of classes related, this is rather tedious. I
don't want to even think about classes dynamically populated with other objects. Be careful when designing your classes! You
should look after your objects all the time! This major change on PHP5 vs PHP4 regarding "references" definitely has very good
performance improvements but comes with very dangerous side effects as well..
Note, that I was working with a multi-dimensional array and I was not using the Key=>Value pair system, but basically, the
point is that if you use foreach, you need to specify that the copied data is to be accessed by reference.
Comparing Objects
In PHP 5, object comparison is more complicated than in PHP 4 and more in accordance to what one will expect
from an Object Oriented Language (not that PHP 5 is such a language).
When using the comparison operator (==), object variables are compared in a simple manner, namely: Two
object instances are equal if they have the same attributes and values, and are instances of the same class.
On the other hand, when using the identity operator (===), object variables are identical if and only if they
refer to the same instance of the same class.
An example will clarify these rules.
Example #1 Example of object comparison in PHP 5
<?php
functionbool2str($bool)
{
if($bool===false){
return'FALSE';
}else{
return'TRUE';
}
}
functioncompareObjects(&$o1,&$o2)
{
echo'o1==o2:'.bool2str($o1==$o2)."\n";
echo'o1!=o2:'.bool2str($o1!=$o2)."\n";
echo'o1===o2:'.bool2str($o1===$o2)."\n";
echo'o1!==o2:'.bool2str($o1!==$o2)."\n";
}
classFlag
{
public$flag;
functionFlag($flag=true){
$this->flag=$flag;
}
}
classOtherFlag
{
public$flag;
functionOtherFlag($flag=true){
$this->flag=$flag;
}
}
$o=newFlag();
$p=newFlag();
$q=$o;
$r=newOtherFlag();
echo"Twoinstancesofthesameclass\n";
compareObjects($o,$p);
echo"\nTworeferencestothesameinstance\n";
compareObjects($o,$q);
echo"\nInstancesoftwodifferentclasses\n";
compareObjects($o,$r);
?>
Note:
Extensions can define own rules for their objects comparison.
Comparing Objects
// AN XML STRING
$xml = '<?xml version="1.0" encoding="utf-8"?>
<thing>
<number>123456</number>
<email>user@example.com</email>
<state>CA</state>
</thing>';
// SHOW THE XML STRING
echo htmlentities($xml);
// MAKE TWO OBJECTS
$obj1 = SimpleXML_Load_String($xml);
$obj2 = SimpleXML_Load_String($xml);
// COMPARE OBJECTS AND FIND THAT THIS ECHOS NOTHING AT ALL
if ($obj1 === $obj2) echo "\n\nOBJECTS IDENTICAL ";
if ($obj1 == $obj2) echo "\n\nOBJECTS EQUAL ";
// SHOW THE OBJECTS - NOTE DIFFERENT SimpleXMLElement NUMBERS
echo "\n\n";
var_dump($obj1);
var_dump($obj2);
// ITERATE OVER THE OBJECTS
foreach ($obj1 as $key => $val1)
{
$val2 = $obj2->$key;
var_dump($val1);
var_dump($val2);
if ($val1 == $val2) echo "\n\nOBJECTS EQUAL"; // ECHOS NOTHING
// RECAST AS STRINGS AND COMPARE AGAIN
$val1 = (string)$val1;
$val2 = (string)$val2;
if ($val1 === $val2) echo "STRINGS IDENTICAL: $key => $val1 \n\n"; // CONMPARISON SHOWS STRINGS IDENTICAL
}
?>
$this->emp_id = $emp_ID;
}
/*
* define the rules for sorting this object - using emp_id.
* Make sure this function returns a -1, 0, or 1.
*/
public static function compare($a, $b)
{
if ($a->emp_id < $b->emp_id) return -1;
else if($a->emp_id == $b->emp_id) return 0;
else return 1;
}
public function __toString()
{
return "Employee[first=$this->first, last=$this->last, emp_id=$this->emp_id]";
}
}
# create a PHP array and initialize it with Employee objects.
$employees = array(
new Employee("John", "Smith", 345),
new Employee("Jane", "Doe", 231),
new Employee("Mike", "Barnes", 522),
new Employee("Vicky", "Jones", 107),
new Employee("John", "Doe", 2),
new Employee("Kevin", "Patterson", 89)
);
# sort the $employees array using Employee compare() method.
usort($employees, array("Employee", "compare"));
# print the results
foreach($employees as $employee)
{
echo $employee . '<br>';
}
?>
Results are now sorted by emp_id:
Employee[first=John, last=Doe, emp_id=2]
Employee[first=Kevin, last=Patterson, emp_id=89]
Employee[first=Vicky, last=Jones, emp_id=107]
Employee[first=Jane, last=Doe, emp_id=231]
Employee[first=John, last=Smith, emp_id=345]
Employee[first=Mike, last=Barnes, emp_id=522]
Important Note: Your PHP code will never directly call the Employee's compare() method, but PHP's usort() calls it many many
times. Also, when defining the rules for sorting, make sure to get to a "primitive type" level... that is, down to a number or
string, and that the function returns a -1, 0, or 1, for reliable and consistent results.
Also see: http://www.php.net/manual/en/function.usort.php for more examples of PHP's sorting facilities.
I'm not sure that the PHP Example #1 above is clear enough. In my own experience, I have found there is a distinct difference
between a "comparison" and a "test for equality". The difference is found in the possible return values of the function being
used, for example.
/*
* Test two values for EQUALITY - returns (boolean) TRUE or FALSE.
*/
function equals($a, $b)
{
return ($a == $b);
}
/*
* COMPARE two values - returns (int) -1, 0, or 1.
*/
function compare($a, $b)
{
if($a < $b) return -1;
else if($a == $b) return 0;
else if($a > $b) return 1;
else return -1;
}
My examples clarify the difference between "making a comparison" and "testing for equality". You can substitute any of the "=="
with "===" for example, but the point is on the possible return values of the function. All tests for EQUALITY will return TRUE
or FALSE, and a COMPARISON will give a "<", "==", or ">" answers... which you can then use for sorting.
$this->Mom( $attribute );
}
}
next($a); next($b);
}
return is_null(key($a)) && is_null(key($b));
}
else
return $a===$b;
}
?>
Note that when comparing object attributes, the comparison is recursive (at least, it is with PHP 5.2). That is, if $a->x
contains an object then that will be compared with $b->x in the same manner. Be aware that this can lead to recursion errors:
<?php
class Foo {
public $x;
}
$a = new Foo();
$b = new Foo();
$a->x = $b;
$b->x = $a;
print_r($a == $b);
?>
Results in:
PHP Fatal error: Nesting level too deep - recursive dependency? in test.php on line 11
Type Hinting
PHP 5 introduces type hinting. Functions are now able to force parameters to be objects (by specifying the
name of the class in the function prototype), interfaces, arrays (since PHP 5.1) or callable (since PHP 5.4).
However, if NULL is used as the default parameter value, it will be allowed as an argument for any later call.
If class or interface is specified as type hint then all its children or implementations are allowed too.
Type hints can not be used with scalar types such as int or string. Traits are not allowed either.
Example #1 Type Hinting examples
<?php
//Anexampleclass
classMyClass
{
/**
*Atestfunction
*
*FirstparametermustbeanobjectoftypeOtherClass
*/
publicfunctiontest(OtherClass$otherclass){
echo$otherclass->var;
}
/**
*Anothertestfunction
*
*Firstparametermustbeanarray
*/
publicfunctiontest_array(array$input_array){
print_r($input_array);
}
/**
*Firstparametermustbeiterator
*/
publicfunctiontest_interface(Traversable$iterator){
echoget_class($iterator);
}
/**
*Firstparametermustbecallable
*/
publicfunctiontest_callable(callable$callback,$data){
call_user_func($callback,$data);
}
}
//Anotherexampleclass
classOtherClass{
public$var='HelloWorld';
}
?>
<?php
//Aninstanceofeachclass
$myclass=newMyClass;
$otherclass=newOtherClass;
//FatalError:Argument1mustbeanobjectofclassOtherClass
$myclass->test('hello');
//FatalError:Argument1mustbeaninstanceofOtherClass
$foo=newstdClass;
$myclass->test($foo);
//FatalError:Argument1mustnotbenull
$myclass->test(null);
//Works:PrintsHelloWorld
$myclass->test($otherclass);
//FatalError:Argument1mustbeanarray
$myclass->test_array('astring');
//Works:Printsthearray
$myclass->test_array(array('a','b','c'));
//Works:PrintsArrayObject
$myclass->test_interface(newArrayObject(array()));
//Works:Printsint(1)
$myclass->test_callable('var_dump',1);
?>
}
test(NULL);
test(newstdClass);
?>
Type Hinting
public function getValue() {
return $this->value;
}
public function __toString() {
return (string)$this->value;
}
}
?>
To download the library, just google for 'strong data typing autoboxing' . There is an article describing how to use it, and a
download link to the appropriate PHP library.
return $arg;
}
?>
the results are here :
bench for 100000 iterations, in seconds
avg min max total
test 5.3275489807129E-6 2.8610229492188E-6 0.0033020973205566 0.53275895118713
object 4.9089097976685E-6 3.814697265625E-6 0.0025870800018311 0.49089503288269
hint 3.2338891029358E-5 2.9802322387695E-5 0.0025920867919922 3.2338931560516
As you can see, the method by object is the best
now you know...
set_error_handler(array('Typehint','handleTypehint'));
return TRUE;
}
?>
Hope this helps,
--Dmitriy
Daniel's typehint implementation was just what I was looking for but performance in production wasn't going to cut it. Calling
a backtrace every time hurts performance. For my implementation I didn't use it, after all, PHP tells us what the data type is
in the error message, I don't feel I need to evaluate the argument where I am using typehinting. Here is the cut down version
I use in my error handling class:
<?php
public static function typehint($level, $message)
{
if($level == E_RECOVERABLE_ERROR)
{
if(preg_match('/^Argument (\d)+ passed to (?:(\w+)::)?(\w+)\(\) must be an instance of (\w+), (\w+) given/',
$message, $match))
{
if($match[4] == $match[5])
return true;
}
}
return false;
}
?>
Hope this can be of use to somebody.
}
?>
Bar::baz() will now accept any instance of Baz.
If Bar is not a heir of any class (no 'extends') PHP will raise a fatal error:
'Cannot access parent:: when current class scope has no parent'.
public function __construct($name, DataType $dataType, $mandatory = false, $listField = true, $value = null, $readOnly =
false, BaseCBO $foreignClass = null)
{
$this->Name = $name;
$this->DataType = $dataType;
$this->Mandatory = $mandatory;
$this->ListField = $listField;
$this->Value = $value;
$this->ReadOnly = $readOnly;
$this->ForeignClass = $foreignClass;
}
}
// ....
class DoSomeStuff
{
public function DoGenericThings(Field $field)
{
if ($field->DataType instanceof Integer)
{
// do things for an integer field...
}
}
}
?>
<?php
function notypehinting($x)
{
is_string($x); //checking the type manually instead
}
function typehinting(string $x)
{
}
$test=new timer;
for($i=0;$i<10000;$i++)
{
try{
notypehinting('test');
}
catch(Exception $e){}
}
echo $test.'<br>';
$test2=new timer;
for($i=0;$i<10000;$i++)
{
try{
typehinting('test');
}
catch(Exception $e){}
}
echo $test2.'<br>';
?>
Output:
0.0088460445404053
0.21634602546692
Result:
typehinting() ist more than 20 times slower than notypehinting()
You see: typehinting for scalar types (like suggested by Daniel) is not the best thing for the performance if you use it very
often.
}
public static function handleTypehint($ErrLevel, $ErrMessage)
{
if ($ErrLevel == E_RECOVERABLE_ERROR)
{
if (preg_match(TYPEHINT_PCRE, $ErrMessage, $ErrMatches))
{
list($ErrMatch, $ThArgIndex, $ThClass, $ThFunction, $ThHint, $ThType) = $ErrMatches;
if (isset(self::$Typehints[$ThHint]))
{
$ThBacktrace = debug_backtrace();
$ThArgValue = NULL;
if (self::getTypehintedArgument($ThBacktrace, $ThFunction, $ThArgIndex, $ThArgValue))
{
if (call_user_func(self::$Typehints[$ThHint], $ThArgValue))
{
return TRUE;
}
}
}
}
}
return FALSE;
}
}
Typehint::initializeHandler();
?>
An are some examples of the class in use:
<?php
function teststring(string $string) { echo $string; }
function testinteger(integer $integer) { echo $integer; }
function testfloat(float $float) { echo $float; }
// This will work for class methods as well.
?>
You get the picture..
}
?>
In other words, type hinting allows for descendants, as caliban at darklock dot com has shown, except when you're subclassing.
See http://bugs.php.net/bug.php?id=36601 for a bit more info. Flagged as wontfix, though, so something to keep in mind.
}
class Chair extends Object
{
public function toString( )
{
return 'This is a chair.';
}
}
}
class Table extends Object
{
public function toString( )
{
return 'This is a table.';
}
02-Sep-2006 08:59
The type hinting system can also be used for interfaces. Example:
<?php
interface fooface
{
public function foo ();
}
class fooclass implements fooface
{
public function foo ()
{
echo ('foo<br>');
}
}
class barclass implements fooface
{
public function foo ()
{
echo ('bar<br>');
}
}
class bazclass implements fooface
{
public function foo ()
{
echo ('baz<br>');
}
}
class quuxclass
{
public function foo ()
{
echo ('quux<br>');
}
}
function callfoo (fooface $myClass)
{
$myClass -> foo ();
}
$myfoo = new fooclass;
$mybar = new barclass;
$mybaz = new bazclass;
$myquux = new quuxclass;
callfoo ($myfoo);
callfoo ($mybar);
callfoo ($mybaz);
callfoo ($myquux); // Fails because the quuxclass doesn't implement the fooface interface
?>
Using this syntax you can allow a function to work with different classes as long as they all implement the same interfaces.
An example might be an online shop that implements a plugin system for payment. If the creator of the script provides a
payment module interface then functions can check if it has been implemented in a given payment class. This means that the
details of the class are unimportant, so it doesn't matter if it interfaces with PayPal, HSBC, ProTX or any other payment
system you care to name, but if it doesn't properly provide all the functionality a payment module requires a fatal error is
generated.
Unfortunately, it doesn't seem possible to use type hinting with new. In java you could do a "fooface myfoo = new fooclass"
which would fail if you tried it with quuxclass instead, but as far as I can tell you can't do a similar test on create with
PHP.
else
{
echo $otherclass->var;
}
}
}
// Another example class
class OtherClass
{
public $var = 'Hello World';
}
// Yet another example class
class DerivedClass extends OtherClass
{
function __construct()
{
$this->var="Planet";
}
public function Planet()
{
echo "Hello ".$this->var;
}
}
$myclass = new MyClass;
$otherclass = new OtherClass;
$derivedclass = new DerivedClass;
// Works - prints "Hello World"
$myclass->test($otherclass);
// Works - calls DerivedClass::Planet()
// which prints "Hello Planet"
$myclass->test($derivedclass);
?>
Limitations of self::
Static references to the current class like self:: or __CLASS__ are resolved using the class in which the
function belongs, as in where it was defined:
Example #1 self:: usage
<?php
classA{
publicstaticfunctionwho(){
echo__CLASS__;
}
publicstaticfunctiontest(){
self::who();
}
}
classBextendsA{
publicstaticfunctionwho(){
echo__CLASS__;
}
}
B::test();
?>
publicstaticfunctionwho(){
echo__CLASS__;
}
publicstaticfunctiontest(){
static::who();//HerecomesLateStaticBindings
}
}
classBextendsA{
publicstaticfunctionwho(){
echo__CLASS__;
}
}
B::test();
?>
Note:
In non-static contexts, the called class will be the class of the object instance. Since $this-> will try to call
private methods from the same scope, using static:: may give different results. Another difference is that
static:: can only refer to static properties.
Example #3 static:: usage in a non-static context
<?php
classA{
privatefunctionfoo(){
echo"success!\n";
}
publicfunctiontest(){
$this->foo();
static::foo();
}
}
classBextendsA{
/*foo()willbecopiedtoB,henceitsscopewillstillbeAand
*thecallbesuccessful*/
}
classCextendsA{
privatefunctionfoo(){
/*originalmethodisreplaced;thescopeofthenewoneisC*/
}
}
$b=newB();
$b->test();
$c=newC();
$c->test();//fails
?>
Fatal error:
Note:
Late static bindings' resolution will stop at a fully resolved static call with no fallback. On the other hand,
static calls using keywords like parent:: or self:: will forward the calling information.
Example #4 Forwarding and non-forwarding calls
<?php
classA{
publicstaticfunctionfoo(){
static::who();
}
publicstaticfunctionwho(){
echo__CLASS__."\n";
}
}
classBextendsA{
publicstaticfunctiontest(){
A::foo();
parent::foo();
self::foo();
}
publicstaticfunctionwho(){
echo__CLASS__."\n";
}
}
classCextendsB{
publicstaticfunctionwho(){
echo__CLASS__."\n";
}
}
C::test();
?>
}
?>
Suprisingly consts are also lazy bound even though you use self instead of static:
<?php
class A{
const X=1;
const Y=self::X;
}
class B extends A{
const X=1.0;
}
var_dump(B::Y); // float(1.0)
?>
2 => 'Defans',
3 => 'Orta Saha',
4 => 'Forvet'
);
}
$a = new Positions('Orta Saha');
$b = new Positions(4);
$c = (string)$a; // Orta Saha
$d = (string)$b; // Forvet
?>
?>
}
return $instance;
}
kx 14-Sep-2008 09:39
At least as of PHP 5.3.0a2 there's a function get_called_class(), which returns the class on which the static method is called.
<?php
class a {
static public function test() {
print get_called_class();
}
}
class b extends a {
}
a::test(); // "a"
b::test(); // "b"
?>
// BaseClass class will be extended by any class needing static inheritance workaroud
class BaseClass {
// Temporarily stores class name for Entry::getStatic() and Entry::setNextStatic()
protected static $nextStatic = false;
// Returns the real name of the class calling the method, not the one in which it was declared.
protected static function getStatic() {
// If already stored
if (self::$nextStatic) {
// Clean and return
$class = self::$nextStatic;
self::$nextStatic = false;
return $class;
}
// Init
$backTrace = debug_backtrace();
$class = false;
// Walk through
for ($i=0; $i<count($backTrace); $i++) {
// If a class is defined
if (isset($backTrace[$i]['class'])) {
// Check if it is not a basic class
if (!in_array($backTrace[$i]['class'], array('BaseClass', 'GenericClass'))) {
return $backTrace[$i]['class'];
} else {
$class = $backTrace[$i]['class'];
}
} else {
// Returns last known class
return $class;
}
}
// Default
return $class;
}
// If a static method is called within global env, the previous method won't work, so we need to tell BaseClass which
public static function setNextStatic($class) {
// Save value
self::$nextStatic = $class;
}
}
// Generic class declaring various static methods
class GenericClass extends BaseClass {
public static $name = 'Generic';
}
/**
* Concrete
* << Library/SayIt/Hello.php >>
*/
class SayIt_Hello_Static extends SayIt_Abstract_Static
{
}
class SayIt_Hello extends SayIt_Abstract
{
public static function getStatic() { return parent::getStatic(__CLASS__) ; }
}
/**
* Test
*/
SayIt_Hello::getStatic()->format = 'Hello %s' ;
$w = new SayIt_Hello('World') ;
$j = new SayIt_Hello('Joe') ;
echo $w->sayIt() ; // Hello World
echo $j->sayIt() ; // Hello Joe
class Singleton {
public static $objInstance;
}
class Foo extends Singleton {
public $intBar;
public function __construct() {
$this->intBar = 1;
}
public static function getClass() {
return __CLASS__;
}
}
$objFooTwo = Foo::getInstance();
$objFooTwo->intBar = 2;
$objFooOne = Foo::getInstance();
if ($objFooOne->intBar == $objFooTwo->intBar) {
echo 'it is a singleton';
} else {
echo 'it is not a singleton';
}
?>
The above will output 'it is a singleton'. The obvious downfall to this method is not being able to give arguments to the
constructor.
$c=newA;
$d=&$c;//$cand$darereferences
//($c,$d)=<id>
$d->foo=2;
echo$c->foo."\n";
$e=newA;
functionfoo($obj){
//($obj)=($e)=<id>
$obj->foo=2;
}
foo($e);
echo$e->foo."\n";
?>
?>
The reason for this is simple. In the bar function of the B class, we replace the identifier you passed in, which identified
the same instance of the A class as your $f variable, with a brand new A class identifier. Creating a new instance of A
doesn't mutate $f because $f wasn't passed as a reference.
To get the reference behavior, one would have to enter the following for class B:
<?php
class B {
public function foo(A $bar)
{
$bar->foo = 42;
}
class B
{
function __toString() {
return "Class B";
}
}
?>
In the first test case we make two objects out of the classes A and B, then swap the variables using a temp one and the normal
assignment operator (=).
<?php
$a = new A();
$b = new B();
$temp = $a;
$a = $b;
$b = $temp;
$temp = $a;
$a = &$b;
$b = $temp;
}
}
# create several instances of DataModelControl...
$dmc1 = new DataModelControl('dmc1');
$dmc2 = new DataModelControl('dmc2');
$dmc3 = new DataModelControl('dmc3');
echo $dmc1 . '<br>';
echo $dmc2 . '<br>';
echo $dmc3 . '<br><br>';
# To change data, use any DataModelControl object...
$dmc2->setData(512);
# Or, call setData() directly from the class...
DataModelControl::setData(1024);
echo $dmc1 . '<br>';
echo $dmc2 . '<br>';
echo $dmc3 . '<br><br>';
?>
DataModelControl [name=dmc1, data=256]
DataModelControl [name=dmc2, data=256]
DataModelControl [name=dmc3, data=256]
DataModelControl [name=dmc1, data=1024]
DataModelControl [name=dmc2, data=1024]
DataModelControl [name=dmc3, data=1024]
... even better! Now, PHP creates one copy of $data, that is shared amongst all DataModelControl objects.
}
class DataControl {
protected $name, $model;
public function __construct($dcName, $dcModel) {
$this->name = $dcName;
$this->model = $dcModel;
}
public function setData($dmData) {
$this->model->setData($dmData);
}
public function __toString() {
return "DataController [name=$this->name, model=" . $this->model->__toString() . "]";
}
}
# create one instance of DataModel...
$model = new DataModel('dm1', 128);
echo $model . '<br><br>';
# create several instances of DataControl, passing $model to each one...
$dc1 = new DataControl('dc1', $model);
$dc2 = new DataControl('dc2', $model);
$dc3 = new DataControl('dc3', $model);
echo $dc1 . '<br>';
echo $dc2 . '<br>';
echo $dc3 . '<br><br>';
# To change data, use any $dataControl->setData()...
$dc3->setData(512);
echo $dc1 . '<br>';
echo $dc2 . '<br>';
echo $dc3 . '<br><br>';
?>
* * * output * * *
DataModel [name=dm1, data=128]
DataController [name=dc1, model=DataModel [name=dm1, data=128]]
DataController [name=dc2, model=DataModel [name=dm1, data=128]]
DataController [name=dc3, model=DataModel [name=dm1, data=128]]
DataController [name=dc1, model=DataModel [name=dm1, data=512]]
DataController [name=dc2, model=DataModel [name=dm1, data=512]]
DataController [name=dc3, model=DataModel [name=dm1, data=512]]
Furthermore, in OOP, it is not a good idea for "global functions" to operate on an object's properties... and it is not a good
idea for your class objects to let them. To illustrate the point, the example should be:
<?php
class A {
protected $foo = 1;
public function getFoo() {
return $this->foo;
}
public function setFoo($val) {
if($val > 0 && $val < 10) {
$this->foo = $val;
}
}
public function __toString() {
return "A [foo=$this->foo]";
}
}
$a = new A();
$b = $a; // $a and $b are copies of the same identifier
// ($a) = ($b) = <id>
$b->setFoo(2);
echo $a->getFoo() . '<br>';
$c = new A();
$d = &$c; // $c and $d are references
// ($c,$d) = <id>
$d->setFoo(2);
echo $c . '<br>';
$e = new A();
$e->setFoo(16); // will be ignored
echo $e;
?>
- - 2
A [foo=2]
A [foo=1]
- - Because the global function foo() has been deleted, class A is more defined, robust and will handle all foo operations... and
only for objects of type A. I can now take it for granted and see clearly that your are talking about "A" objects and their
references. But it still reminds me too much of cloning and object comparisons, which to me borders on machine-like programming
and not object-oriented programming, which is a totally different way to think.
Object Serialization
Serializing objects - objects in sessions
serialize() returns a string containing a byte-stream representation of any value that can be stored in PHP.
unserialize() can use this string to recreate the original variable values. Using serialize to save an object will
save all variables in an object. The methods in an object will not be saved, only the name of the class.
In order to be able to unserialize() an object, the class of that object needs to be defined. That is, if you have
an object of class A and serialize this, you'll get a string that refers to class A and contains all values of
variables contained in it. If you want to be able to unserialize this in another file, an object of class A, the
definition of class A must be present in that file first. This can be done for example by storing the class
definition of class A in an include file and including this file or making use of the spl_autoload_register()
function.
<?php
//classa.inc:
classA{
public$one=1;
publicfunctionshow_one(){
echo$this->one;
}
}
//page1.php:
include("classa.inc");
$a=newA;
$s=serialize($a);
//store$ssomewherewherepage2.phpcanfindit.
file_put_contents('store',$s);
//page2.php:
//thisisneededfortheunserializetoworkproperly.
include("classa.inc");
$s=file_get_contents('store');
$a=unserialize($s);
//nowusethefunctionshow_one()ofthe$aobject.
$a->show_one();
?>
If an application is using sessions and uses session_register() to register objects, these objects are serialized
automatically at the end of each PHP page, and are unserialized automatically on each of the following pages.
This means that these objects can show up on any of the application's pages once they become part of the
session. However, the session_register() is removed since PHP 5.4.0.
It is strongly recommended that if an application serializes objects, for use later in the application, that the
application includes the class definition for that object throughout the application. Not doing so might result in
an object being unserialized without a class definition, which will result in PHP giving the object a class of
__PHP_Incomplete_Class_Name, which has no methods and would render the object useless.
So if in the example above $a became part of a session by running session_register("a"), you should include
the file classa.inc on all of your pages, not only page1.php and page2.php.
Object Serialization
OOP Changelog
Changes to the PHP 5 OOP model are logged here. Descriptions and other notes regarding these features are
documented within the OOP 5 documentation.
Version
Description
5.4.0
Changed: If an abstract class defines a signature for the constructor it will now be enforced.
5.3.3
Changed: Methods with the same name as the last element of a namespaced class name will no
longer be treated as constructor. This change doesn't affect non-namespaced classes.
5.3.0
Changed: Classes that implement interfaces with methods that have default values in the prototype
are no longer required to match the interface's default value.
5.3.0
Changed: It's now possible to reference the class using a variable (e.g., echo
$classname::constant;). The variable's value can not be a keyword (e.g., self, parent or static).
5.3.0
Changed: An E_WARNING level error is issued if the magic overloading methods are declared static. It
also enforces the public visibility requirement.
5.3.0
Changed: Prior to 5.3.0, exceptions thrown in the __autoload() function could not be caught in the
catch block, and would result in a fatal error. Exceptions now thrown in the __autoload function can
be caught in the catch block, with one proviso. If throwing a custom exception, then the custom
exception class must be available. The __autoload function may be used recursively to autoload the
custom exception class.
5.3.0
5.3.0
Added: heredoc and nowdoc support for class const and property definitions. Note: heredoc values
must follow the same rules as double-quoted strings, (e.g., no variables within).
5.3.0
5.3.0
5.2.0
Changed: The __toString() method was only called when it was directly combined with echo or
print. But now, it is called in any string context (e.g. in printf() with %s modifier) but not in other
types contexts (e.g. with %d modifier). Since PHP 5.2.0, converting objects without a __toString
method to string emits a E_RECOVERABLE_ERROR level error.
5.1.3
Changed: In previous versions of PHP 5, the use of var was considered deprecated and would issue
an E_STRICT level error. It's no longer deprecated, therefore does not emit the error.
5.1.0
Changed: The __set_state() static method is now called for classes exported by var_export().
5.1.0
OOP Changelog