53

I have this code. Is it possible for a User object constructor to somehow fail so that $this->LoggedUser is assigned a NULL value and the object is freed after constructor returns?

$this->LoggedUser = NULL;
if ($_SESSION['verbiste_user'] != false)
  $this->LoggedUser = new User($_SESSION['verbiste_user']);    
Trott
  • 66,479
  • 23
  • 173
  • 212
  • 7
    Great first question by the way. – Pekka Feb 06 '10 at 21:20
  • I've seen a certain popular CMS that returns FALSE in a constructor. What's up with that?!?! – loungerdork Oct 31 '11 at 03:17
  • 1
    Just thought I'd chime in here for documentation sake. Since the date is so far back, it is possible that the CMS you are seeing was built for PHP4. PHP4 used to allow a lot of bad things, not the least of which allowed the user to overwrite $this in the named constructor (for example $this = false). – techdude Dec 31 '14 at 22:52

6 Answers6

67

Assuming you're using PHP 5, you can throw an exception in the constructor:

class NotFoundException extends Exception {}

class User {
    public function __construct($id) {
        if (!$this->loadById($id)) {
             throw new NotFoundException();
        }
    }
}

$this->LoggedUser = NULL;
if ($_SESSION['verbiste_user'] != false) {
    try {
        $this->LoggedUser = new User($_SESSION['verbiste_user']);
    } catch (NotFoundException $e) {}
}

For clarity, you could wrap this in a static factory method:

class User {
    public static function load($id) {
        try {
            return new User($id);
        } catch (NotFoundException $unfe) {
            return null;
        }
    }
    // class body here...
}

$this->LoggedUser = NULL;
if ($_SESSION['verbiste_user'] != false)
    $this->LoggedUser = User::load($_SESSION['verbiste_user']);

As an aside, some versions of PHP 4 allowed you to set $this to NULL inside the constructor but I don't think was ever officially sanctioned and the 'feature' was eventually removed.

jaz303
  • 1,136
  • 9
  • 11
  • 4
    +1 IMO, this is the correct OO way of indicating failure to construct an object. – Jon Benedicto Feb 06 '10 at 21:26
  • 2
    It depends on the nature of the failure. If it was an exception, yes. If a parameter to the constructor did not meet a criterion, in my mind no. – Pekka Feb 06 '10 at 21:35
  • 5
    Pekka, I'm not sure what sorts of "bad parameters" would not be an exception? If you can't create a valid object with a certain set of params surely that is exceptional, and should throw an exception? Can you give an example to clarify the distinction you're thinking of? (I see you do in the answer below, that would be useful up here, but I can't edit your comment!) – Peter Bagnall May 14 '12 at 11:01
13

AFAIK this can't be done, new will always return an instance of the object.

What I usually do to work around this is:

  • Adding a ->valid boolean flag to the object that determines whether an object was successfully loaded or not. The constructor will then set the flag

  • Creating a wrapper function that executes the new command, returns the new object on success, or on failure destroys it and returns false

-

function get_car($model)
      {
        $car = new Car($model);
        if ($car->valid === true) return $car; else return false;
     } 

I'd be interested to hear about alternative approaches, but I don't know any.

Pekka
  • 442,112
  • 142
  • 972
  • 1,088
  • Thanks for clarifying. So basically I could justwrap the new command in try...catch and then raise an exception in a constructor? –  Feb 06 '10 at 21:13
  • 1
    Good question! I think you *can*, but it doesn't really sound right to me. If I create an object car with model "Ford" it could simply be that no car of that model is in the database. That is not really what *exceptions* were designed for. It's more of an expected condition. I'd be interested to see what other answers come up, what is seen as the "right" way to handle this. – Pekka Feb 06 '10 at 21:16
  • this is doing thing in the dirty way, check @jaz303's answer – MinhajulAnwar Nov 19 '15 at 12:13
6

Consider it this way. When you use new, you get a new object. Period. What you're doing is you have a function that searches for an existing user, and returns it when found. The best thing to express this is probably a static class function such as User::findUser(). This is also extensible to when you're deriving your classes from a base class.

Wim
  • 11,091
  • 41
  • 58
  • That sounds the most logical, yes. I've only just begun OO programming in PHP so I'm not completely sure how to handle things in a 'proper' way. –  Feb 06 '10 at 21:22
5

A factory might be useful here:

class UserFactory
{
    static public function create( $id )
    {
        return (
            filter_var( 
                $id,
                FILTER_VALIDATE_INT, 
                [ 'options' => [ 'min_range' => 1, ] ]
            )
                ? new User( $id )
                : null
        );
  }
}
kaiser
  • 21,817
  • 17
  • 90
  • 110
4

When a constructor fails for some unknown reason, it won't return a NULL value or FALSE but it throws an exception. As with everything with PHP5. If you don't handle the exception then the script will stop executing with an Uncaught Exception error.

TheGrandWazoo
  • 2,879
  • 1
  • 17
  • 15
  • 1
    When does a constructor throw an exception? When it returns false? Or do you mean, a constructor should throw an exception if a certain condition could not be met`? – Pekka Feb 06 '10 at 21:13
  • 1
    Your constructor should throw the exception. – Tom Oct 04 '12 at 14:30
3

maybe something like this:

class CantCreateException extends Exception{
}

class SomeClass {
    public function __construct() {
       if (something_bad_happens) {
          throw ( new CantCreateException());
       }
    }
}

try{
  $obj = new SomeClass();
}
catch(CantCreateException $e){
   $obj = null;
}
if($obj===null) echo "couldn't create object";
//jaz303 stole my idea an wrap it into a static method
Chris O
  • 689
  • 8
  • 22
useless
  • 1,876
  • 17
  • 18