Конструкторы и деструкторы

Constructor

PHP 5 позволяет объявлять методы-конструкторы. Классы, в которых объявлен метод-констуктор, будут вызывать этот метод при каждом создании нового объекта, так что это может оказаться полезным, чтобы, например, инициализировать какое-либо состояние объекта перед его использованием.

Замечание: Конструкторы в классах-родителях не вызываются автоматически. Чтобы вызвать конструктор, объявленный в родительском классе, следует обратиться к методу parent::__construct().

Пример 19-7. Использование унифицированных конструкторов

<?php
class BaseClass {
   function
__construct() {
       print
"Конструктор класса BaseClass\n";
   }
}

class
SubClass extends BaseClass {
   function
__construct() {
      
parent::__construct();
       print
"Конструктор класса SubClass\n";
   }
}

$obj = new BaseClass();
$obj = new SubClass();
?>

Если PHP 5 не может обнаружить объявленный метод __construct(), вызов конструктора произойдет по прежней схеме, через обращение к методу, имя которого соответствует имени класса. Может возникнуть только одна проблема совместимости старого кода, если в нём присутствуют классы с методами __construct().

Деструкторы

PHP 5 предоставляет концепцию деструкторов, сходную с теми, что применяются в других ОО языках, таких, как Java: когда освобождается последняя ссылка на объект, перед высвобождением памяти, занимаемой этим объектом, вызывается метод __destruct(), не принимающий параметров.

Пример 19-8. Пример использования деструктора

<?php
class MyDestructableClass {
   function
__construct() {
       print
"Конструктор\n";
      
$this->name = "MyDestructableClass";
   }

   function
__destruct() {
       print
"Уничтожается " . $this->name . "\n";
   }
}

$obj = new MyDestructableClass();
?>

Как и в случае с конструкторами, деструкторы, объявленные в родительском классе, не будут вызваны автоматически. Для вызова деструктора, объявленном в классе-родителе, следует обратиться к методу parent::__destruct().



Конструкторы и деструкторы
Reza Mahjourian
10-Jul-2006 02:18
Peter has suggested using static methods to compensate for unavailability of multiple constructors in PHP.  This works fine for most purposes, but if you have a class hierarchy and want to delegate parts of initialization to the parent class, you can no longer use this scheme.  It is because unlike constructors, in a static method you need to do the instantiation yourself.  So if you call the parent static method, you will get an object of parent type which you can't continue to initialize with derived class fields.

Imagine you have an Employee class and a derived HourlyEmployee class and you want to be able to construct these objects out of some XML input too.

<?php
class Employee {
   public function
__construct($inName) {
      
$this->name = $inName;
   }

   public static function
constructFromDom($inDom)
   {
      
$name = $inDom->name;
       return new
Employee($name);
   }

   private
$name;
}

class
HourlyEmployee extends Employee {
   public function
__construct($inName, $inHourlyRate) {
      
parent::__construct($inName);
      
$this->hourlyRate = $inHourlyRate;
   }

   public static function
constructFromDom($inDom)
   {
      
// can't call parent::constructFromDom($inDom)
       // need to do all the work here again
      
$name = $inDom->name// increased coupling
      
$hourlyRate = $inDom->hourlyrate;
       return new
EmployeeHourly($name, $hourlyRate);
   }

   private
$hourlyRate;
}
?>

The only solution is to merge the two constructors in one by adding an optional $inDom parameter to every constructor.
Peter Molnar
18-May-2006 06:24
There were many notes about the inability of defining multiple constructors for the class.

My solution is to define separate static methods for each type of constructor.
<?php
class Vector {
   private
$x;
   private
$y;

   public function
__construct() {
      
$this->x = 0;
      
$this->y = 0;
   }

   public static function
createXY($x, $y) {
      
$v = new Vector();
      
$v->x = $x;
      
$v->y = $y;
       return
$v;
   }
}
?>
ckoschmied at web dot de
07-Apr-2006 07:58
Be aware of the fact that if you create a new instance of a class like this:

$instance = new Class();

$instance will not contain a valid reference to the newly created object until the constructor is finished. So don't use $instance while the constructor is still running.

Well, on the other side, why would you want to do it? I wanted to, and it took me some hours to figure out.
Even though it's quite obvious if you think about it :-)
jcaplan at bogus dot amazon dot com
24-Mar-2006 09:52
__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;
  
   public final function
__construct( Parameter $param )
   {
      
$this->param = $param;
      
$this->pseudoConstruct();
   }
  
   protected abstract function
pseudoConstruct();
}

class
ConcreteClass extends BaseClass
{
   protected function
pseudoConstruct()
   {
       echo
__CLASS__.' constructor';
   }
}

$refl = new ReflectionClass( 'ConcreteClass' );
if( !
$refl->isSubclassOf( 'BaseClass' ) ) throw new Exception( 'Invalid base class!' );
$refl->newInstance( new Parameter() );
?>
jochem AT mondrian-it d_o_t nl
30-Jan-2006 04:07
at: derk AT oneindig DOT com

You can achieve identical functionality by doing this:
<?php
class Parent {
   function
__construct()
   {
       echo
"Parent constructor called\\n";
   }
}

class
Child extends Parent {
   function
__construct()
   {
      
parent::__construct();
       echo
" Child 'contructor' called";
   }
}

$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.
developit at mail dot ru
25-Jan-2006 02:32
as [kida at keymail dot it] said you can't weaken a visibility of constructor when extending some class. but suggested trick that uses both old and new constructor namimg syntaxes to weaken visibility from 'protected' to 'public' seems a little bit odd. allthough it works allright. declaring extended class as 'abstract' with 'public' constructor will do quite the same thing in a more elegant manner and without any syntax mess.

<?php
class A
{
  public function
__construct()
  {
  
//do smth
 
}
}

abstract class
B extends A
{
  public function
__construct()
  {
  
parent::__construct();
  }
}
?>

thus, you avoid instanciating class B as if it had a protected contructor
aya at eh dot org
01-Dec-2005 02:20
For those who aren't already aware, PHP5 currently suffers from the classic reference counting leak. See http://en.wikipedia.org/wiki/Reference_counting for more info.

Example code:

<?php

  
class Noisy
  
{
       private
$name;

       public function
__construct($name)
       {
          
$this->name = $name;
           echo
"Noisy::__construct($this->name)\n";
       }

       public function
__destruct()
       {
           echo
"Noisy::__destruct($this->name)\n";
       }
   }
          
   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!
derk AT oneindig DOT com
03-Nov-2005 02:02
If a constructor is not present in a child class, php5 will try to call a constructor from the parent class. This behaviour can be used to somewhat simulate constructor chaining.

<?php
abstract class Parent {
   function
__construct()
   {
       echo
"Parent constructor called\n";
      
$this->myConstruct();
   }
}

class
Child extends Parent {
   function
myConstruct()
   {
       echo
" Child 'contructor' called";
   }
}

$c = new Child();
?>

will output:
Parent constructor called
Child 'constructor' called
timothyrhodes at gmail dot com
17-Oct-2005 09:46
You are receiving that error because you are calling a function outside of your class that requires the instantiated (object) variable "example" which doesn't exist until your class has been constructed into an object and assigned to "example. Here is the proper solution.

<?php

function foo($bar)
{
  
$bar->friend();
}

class
example_class
{
   function
example_class()
   {
      
foo($this);
   }

   function
friend()
   {
      
// Uhhh :)
  
}
}

$example = new example_class;

?>
webmaster at wyrihaximus dot net
17-Oct-2005 06:57
Just a note on the __construct() function (in my example it's the classname but it has the same effect).  When you try call a function outside the class, wich calls a function wich is inside the class in construction you will get a "Call to function on undefined object" error. The first piece of code shows a failing script. The seccond piece of code shows a work-around solution .

Examples:

The wrong code:
<?php

function foo($bar)
{
   global
$example;
  
$example->friend();
}

class
example_class
{
   function
example_class()
   {
      
foo('bar');
   }
  
   function
friend()
   {
      
// Uhhh :)
  
}
}

$example = new example_class;

?>

The working code:
<?php

function foo($bar)
{
   global
$example;
  
$example->friend();
}

class
example_class
{
   function
example_class()
   {
      
// Some other code
  
}
  
   function
example_class_step2()
   {
      
foo('bar');
   }
  
   function
friend()
   {
      
// Uhhh :)
  
}
}

$example = new example_class;
$example->example_class_step2();

?>
contact at tcknetwork dot com
21-Sep-2005 12:54
be careful while trying to access files with __destruct() because the base directory (getcwd()) will be the root of your server and not the path of your script, so add before all your path called in __destruct() :
EITHER  dirname($_SERVER["SCRIPT_FILENAME"])."my/path/"
OR      dirname(__FILE__)."my/path/"
         (be careful with includes, it will give the path of the file processed and not the main file)
php dot net at lk2 dot de
13-Aug-2005 09:50
It looks like `echo()`ed output from the __destructor() function is displayed onto screen _before_ other output that the class may have have already sent before.

This can be misleading if you have debug info printed in the destructor but not a problem if you know it.
stanley dot turnteen at gmail dot com
05-Aug-2005 01:20
Actually a buddy just pointed out a more elegant method:

<?php

 
class Movie
 
{
   public
$title;
   public
$director;
   public
$stars;
   public
$year_released;
  
   public function
__construct()
   { 
     @list(
$this->title,
          
$this->director,
          
$this->stars,
          
$this->year_released) = func_get_args();
   }
  
  }

?>
stanley dot turnteen at gmail dot com
05-Aug-2005 12:43
IMHO using func_get_args() is superior to constructor polymorphism, because you don't have to define constructors for every possible way a class can be initialized.

The pattern I use looks like this;  all you have to do is pass the parameters in the correct order.

<?php

 
class Movie
 
{
   public
$title;
   public
$director;
   public
$stars;
   public
$year_released;
  
   public function
__construct()
   {
    
$args = func_get_args();
    
     foreach(array(
"title", "director", "stars", "year_released") as $i)
     {
       if(empty(
$args))
       {
         break;
       }
      
      
$this->$i = array_shift($args);
     }
   }
  
  }
?>
nayyar at ossp dot com dot pk
14-Jul-2005 02:48
This might help you understanding __construct() function.

<?
  
class submit
  
{
     var
$val1;
     var
$val2;
     function
__construct()
     {
    
$this->val1 = 7;
    
$this->val2 = 9;
     }
     function
sum()
     {
         return
$this->val1 + $this->val2;
     }
    
  
   }

  
$tt = new submit;
  
$tt->val1 = 7;
  
$tt->val2 = 7;
  
$result = $tt->sum();
   echo
$result;
?>
dominics at gmail dot com
10-Jul-2005 07:12
If you're using E_STRICT error reporting, PHP will tell you if you define both __construct() and an old-style constructor (a function with the same name as the class) together in a class. Note that this occurs even if the old constructor function is abstract or final (for instance, if you were intending to only use it in a sub-class). Be wary of this if you're trying to implement the 'command' design pattern.

The solution? Either turn E_STRICT off (and possibly forgo some other important notices), rename your function (and possibly make things a little more complicated), or look at using an interface.
Obeliks
23-Jun-2005 04:43
In reply to "luancarvalho at terra dot com dot br":

You can also use an constructor without parameters and get the parameters via func_get_args();
Then you can check if it is_int or is_whatever, and process it accordingly.
luancarvalho at terra dot com dot br
14-Jun-2005 01:42
Even PHP 5 doesn't allow to redeclare a method in a class. So, it's impossible to create differents constructors to a object with unique parameters, something common JAVA.

To overcome this limitation I've been forced to use something like this:

<?php

class MyClass {

  public
$n1;
  public
$n2;

  function
MyClass ($n1 = FALSE, $n2 = FALSE) {
   if (
$n1) {
    
$this->n1 = $n1;
   } else {
    
$this->n1 = 0;
   }

   if (
$n2) {
    
$this->n2 = $n2;
   } else {
    
$this->n2 = mt_rand();
   }

  }

}

?>
shakeel at nxb dot com dot pk
31-May-2005 10:57
In order to make constructor overloading in PHP5, we have to make a function __construct and inside from that's constructor, use func_num_args to get the total numbers of arguments supplied to the constructor and then do appropriate work or call some other function. The value of any argument can be acceessed by using
func_get_arg($index) function. This is illustrated in the
following Example.

<?php
class Overload
{
 private
$var1;
 private
$var2;
 
# constructor which acts as a overloaded constructors
 
public function __construct()
{
$num_args=func_num_args();
switch (
$num_args)
{
 
case
'0':
      
// Do something
       # set zero in both instance variables. if no argument is
       #supplied
    
$this->var1 = $this->var2 =0;
     break;
case
'1':
      
# get value of the argument
$arg1=func_get_arg(0);
      
// Do something else;
       /* asigned ist arg. to ist instance variable and Zero in the 2nd instance variable or any other initializing value. because only one argument is supplid.    */         

$this->var1 = $arg1;
$this->var2 = 0;
break;

case
'2':
// get value of both arguments
$arg1=func_get_arg(0);
$arg2=func_get_arg(1);   
// Do something else;
        
$this->var1 = $arg1;
$this->var2 = $arg2;
break;             
}
// end of switch statement

 
} // end of function __construct

 
// function to display the values of instance variables
function display()
{
echo
"var1: $this->var1 <br>";
echo
"var2: $this->var2 <br><br>";

}
// end of function display

}// end of class

// create 3 objects with different no. of arguments
$over = new Overload();
$over1 = new Overload(5);
$over2 = new Overload(5,10);

// call display function with all 3 objects.
$over->display();
$over1->display();
$over2->display();

?>

output:

var1: 0
var2: 0

var1: 5
var2: 0

var1: 5
var2: 10
kida at keymail dot it
31-May-2005 12:59
Well:

class A
{
     public function __construct()
     {
             //do something
     }
}

class B extends A
{
     protected function __construct()
     {
         parent::__construct();
     }
}

You can't make this becouse B::__construct() MUST have the same visibility or a weaker visibility of A::__construct() !!??!!
SO I THINK it's better

class A
{
     public function A()
     {
             //do something
     }
}

class B extends A
{
     protected function  B()
     {
         parent::A();
     }
}

Because it works. Instead of what they're saying ;)
rocco at bluora dot com dot au
16-Apr-2005 08:29
Before PHP reaches the point where it calls the __destruct functions, it has already done a session_write_close() so you can no longer write anything to the session.

I wanted it to copy some variables from my class into the session once the script had finished but now having to get the last function to call a SaveToSession() function.

In php versions 5.0.2 and 5.0.4
contact at tcknetwork dot com
15-Apr-2005 08:45
Note that php5 use in priority __construct() instead of [classname](). So you could build a constructed/destructed class for php4/5 very easily using this.
<?
class test {
 function
test() {
 
$this->__construct();
 
register_shutdown_function(array($this,"__destruct"));
 }
 function
__construct() {
  echo
"construct\n";
 }
 function
__destruct() {
  echo
"destruct\n";
 }
};
$t=new test();
?>
In case you use unset($t) in php4, the destructor is not called. so be careful.
apfelsaft
30-Mar-2005 01:59
at the end of a script all remaining objects aren't in fact destructed. it is only their __destruct() method, which will be called. the objects still exist after that.

so, if your database connection object has no __destruct() or at least it doesn't disconnects the database, it will still work.

in general, there is no need to disconnect the database (especially for persistent connections).
miguel dot simoes at newnet dot com dot pt
23-Mar-2005 04:01
In the PHP documentation example you can change the class name under $this->name = "ClassName" with $this->name = __CLASS__;

This way you'll have a starting point for having a dinamic content even inside PHP code and providing a good base for having a "Class template" with __construct and __destruct.
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.
caliban at darklock dot com
22-Feb-2005 02:00
(Referring to the earlier example code and comment)

The "final" keyword forbids the Baz object to define its own constructor; defining an override for a "final" method creates a fatal error. Without the "final" keyword, if the Baz object defines a constructor, the Foo constructor will not be called automatically; instead, the Baz constructor will be called and the Foo constructor will not.

If the Foo constructor SHOULD be called, e.g. to open files or initialise member variables, Baz will need to call parent::__construct() explicitly. In most cases, this is what you will want to do when extending a class, but you can't just do it as a matter of habit... because if Foo does not HAVE a constructor, parent::__construct() is a fatal error. This creates a problem. (The problem can be easily solved with @parent::__construct(), if you don't care about OTHER errors that might arise.)

So given an object Foo with no constructor, any extension objects MUST NOT call parent::__construct() in their own constructors. If Foo later *adds* a constructor, the extension objects SHOULD call parent::__construct() if they have constructors themselves. (If they don't, Foo's constructor will be called automatically.) A whole slew of problems pop up as a result, so what we need is a way to say "call the parent class constructor if there is one".

An immediate avenue presents itself with is_callable(array("parent","__construct")), but this isn't as useful as you might expect because it doesn't handle old-style constructors. Basically, if Foo has a constructor named Foo(), parent::__construct() will call parent::Foo(), but is_callable(array("parent","__construct")) returns FALSE.

Fortunately, this is easily resolved with a call to is_callable(array("parent",get_parent_class($this))). Adding that bit of code into the constructor of your derived classes will achieve exactly what we want, namely to call the parent's constructor if there is one.

But there always seems to be a catch... this only works one level down. If Baz extends Bar extends Foo, this code will get Baz to call Bar's constructor, but Bar can't call Foo's constructor with it -- because $this is an instance of Baz, not Bar, so Bar cannot determine its parent class name correctly. (It is noteworthy that if Bar does not *have* a constructor, Baz will still happily pass a request through to Foo's constructor.) The reflection interface is the most obvious way to go about fixing this, but that gets a little too complex to be called a "note".
donpro at lclnav dot com
07-Jan-2005 06:52
I am using PHP 5.0.3 and was playing with the above example.  I changed the code by omitting the 'final' keword from the contructor in the Foo class and still got the same result, i.e., the constructor was called when only this code was run:

$Obj = new Baz( "Passed to new Baz( )" );
lenix
27-Nov-2004 08:38
i just found out that, together with the 'final' keyword, it gets pretty easy to write a parent-class with a constructor always called and not overwriteable by any childs extending that class.

<?php
  
abstract class Foo {
     final function
__construct( $Bar ) {
         echo
"Foo::__construct( $Bar );<br />\n";
     } 
   }
   class
Baz extends Foo {
     public function
printBaz( $Val = "" ) {
         echo
"Baz::printBaz( $Val );<br />\n";
     }
   }
  
   try {
    
$Obj =& new Baz( "Passed to new Baz( )" );
    
$Obj->printBaz( "method of Baz" );
   } catch (
Exception $E ) {
     echo
'Caught exception: '$E->getMessage(), "<\n";
   }
?>

of course subclasses are forced that way to have no contructor on their own.

<Autoloading ObjectsVisibility>
 Last updated: Tue, 15 Nov 2005