Breaking the constructor

2019-03-28 12:20发布

问题:

Is there a way to prevent object creation within its constructor, so that:

$object = new Foo();
echo $object; // outputs: NULL

回答1:

Nope, not possible; the new keyword always returns an instance of the class specified, no matter what you attempt to return from the constructor. This is because by the time your constructor is called, PHP has already finished allocating memory for the new object. In other words, the object already exists at that point (otherwise, there's no object on which to call the constructor at all).

You could raise errors or throw exceptions from the constructor instead, and handle those accordingly.

class Foo {
    public function __construct() {
        throw new Exception('Could not finish constructing');
    }
}

try {
    $object = new Foo();
} catch (Exception $e) {
    echo $e->getMessage();
}


回答2:

Impossible, but you can proxify the creation.

<?php

class Foo {
    private function __construct() {}

    static function factory($create=False) {
        if ($create) {
            return new Foo;
        }
    }
}


回答3:

Simply throw an exception:

throw new Exception('thou shalt not be born!');


回答4:

You could implement a factory to do the object creation for you. If you are using PHP 5.3.0+ then you will have access to a feature called Late Static Binding that makes this easy.

<?php
class MyObject
{
    protected $loadSuccess = false;

    protected function __construct ($foo, $bar, $baz)
    {
        // Just a random 50/50 success probability.  Put real logic for testing success here
        $this -> loadSuccess    = (mt_rand (0, 99) > 49);
    }

    static public function factory ($foo, $bar, $baz)
    {
        $objClass   = get_called_class ();
        $obj = new $objClass ($foo, $bar, $baz);
        if ($obj -> loadSuccess)
        {
            return ($obj);
        }
    }
}

class MySubClass extends MyObject
{
    protected function __construct ($foo, $bar, $baz)
    {
        parent::__construct ($foo, $bar, $baz);
        if ($this -> loadSuccess)
        {
            echo ('Hello, I\'m a ' . get_called_class () . '<br>');
        }
    }
}

class MySubSubClass extends MySubClass
{
    protected function __construct ($foo, $bar, $baz)
    {
        parent::__construct ($foo, $bar, $baz);
        if ($this -> loadSuccess)
        {
            echo ($foo * $bar * $baz . '<br>');
        }
    }
}

$o1 = MyObject::factory (1, 2, 3); // Base class
$o2 = MySubClass::factory (4, 5, 6); // Child class
$o3 = MySubSubClass::factory(7, 8, 9); // Descendent class

var_dump ($o1);
var_dump ($o2);
var_dump ($o3);
?>

If you are using a version of PHP prior to 5.3.0 then things get a bit more complicated as late static binding and get_called_class () are not available. However, PHP 5 does support reflection and you can use it to emulate late static binding. Alternatively you can implement a separate factory class and pass it an argument telling it what kind of object you want to initialize.

Another approach is to get your constructor to throw an exception if it fails and handle it with a catch block. However this method can get kind of hairy if you are doing it a lot and it should really only be used when you normally expect object creation to succeed (in other words, if creation failure is an exceptional circumstance).

The factory approach has additional advantages, for example it can hold a cache of objects that you've already created and if you attempt to create the same object twice it will simply pass you a reference the already-initialized object instead of creating a new one. This eliminates memory and processing overhead inherent in object creation, especially if creation involves a time-intensive task such as a complex SQL query



回答5:

If you are try to stop an object from instantiating depending on a few conditions, then why not create a condition before you even hit the PHP new and instantiate the class?

What I mean is, say in your example:

$object = new Foo();
echo $object; // outputs: NULL

You could wrap it in a function that does the validation work for you or just simply an if statement if you are only gonna instantiate the class once.

Like this:

if(conditions_to_meet){
    $object = new Foo();
}
else $object = NULL;

if you need to instantiate that multiple times then wrap it in a function and pass the parameters to your class.

Hope this helps.



标签: php oop