Have fun with PHP: how to bypass an object constructor

Submitted by Frederic Marand on

Some of the features in PHP may be surprising. Think for instance of the way PDO is able to create classed results, place the query results into them, and only then invoke the constructor when using PDO::FETCH_CLASS fetch mode without the additional PDO::FETCH_PROPS_LATE. Every wished you could do this in userland code ? Turns out this has long been possible, and is even simpler since PHP 5.4.

Creating objects without __construct() in PHP 5.0, 5.1, 5.2, 5.3 and later

<?php
class Foo {
  public
$property = "Not constructed";

  public function
__construct() {
   
$this->property = "Constructed";
  }

  public function
talk() {
    echo
"I was {$this->property}.\n";
  }

  public static function
frankenstein() {
   
$rc = new ReflectionClass(__CLASS__);
   
$properties = $rc->getDefaultProperties();
   
$prefix = 'O:' . strlen(__CLASS__) . ':"' . __CLASS__ . '":' . count($properties) . ':{';
   
$suffix = '}';

   
$hash = array();
    foreach (
$properties as $name => $value) {
     
$hash[] = serialize($name) . serialize($value);
    }
   
$serialized = $prefix . implode($hash) . $suffix;
    return
unserialize($serialized);
  }
}

// Normal instantiation.
$x = new Foo();
var_dump($x);
$x->talk();

// Paranormal instantiation.
$y = Foo::frankenstein();
var_dump($y);
$y->talk();
$y->__construct();
$y->talk();
?>

Creating objects without __construct() in PHP 5.4, 5.5 and later

PHP 5.4 introduces a new, much simpler and more reliable way to achieve the same result.

<?php
class Foo {
  public
$property = "Not constructed";

  public function
__construct() {
   
$this->property = "Constructed";
  }

  public function
talk() {
    echo
"I was {$this->property}.\n";
  }

  public static function
fronkonstine() {
   
$rc = new ReflectionClass(__CLASS__);
    return
$rc->newInstanceWithoutConstructor();
  }
}

// Normal instantiation.
$x = new Foo();
var_dump($x);
$x->talk();

// Paranormal instantiation.
$y = Foo::fronkonstine();
var_dump($y);
$y->talk();
echo
"But now...\n";
$y->__construct();
$y->talk();
?>

Bypassing __construct() in real life ?

Just because these mechanisms have been available for a long time does not mean they are good for general use. Nonetheless, there is a very much used package using this technique: PHPUnit, which was the inspiration for this story. You can find code like it in PHPUnit_Framework_MockObject_Generator::getObject(), where it is used to create mocks bypassing the original constructor:

<?php
class PHPUnit_Framework_MockObject_Generator
/* ... */
   
protected static function getObject($code, $className, $originalClassName = '', $callOriginalConstructor = FALSE, $callAutoload = FALSE, array $arguments = array())
    {
   
/* ... */
            // Use a trick to create a new object of a class
            // without invoking its constructor.
           
$object = unserialize(
             
sprintf('O:%d:"%s":0:{}', strlen($className), $className)
            );
   
/* ... */
   
}
?>

Invoking __construct after construction

The most disturbing in all of this might well be the last two lines in each example:

<?php
$y
->__construct();
$y->talk();
?>

As these examples show, it is indeed possible to invoke the constructor after the actual object creation, just like any other method. Which completes the ability to create / set properties and only then invoke the constructor, just like PDO in non-late-props mode. Can you think of meaningful use cases for it all, beyond building test doubles (mocks) ? If so, by all means comment on this article !

Rajapriya (not verified)

Mon, 2017-07-03 12:06

Interesting article to read.. After reading this article i got clear information about php constructors which helpful to make a constructors program easily..