ChatGPT解决这个技术问题 Extra ChatGPT

Best way to do multiple constructors in PHP

You can't put two __construct functions with unique argument signatures in a PHP class. I'd like to do this:

class Student 
{
   protected $id;
   protected $name;
   // etc.

   public function __construct($id){
       $this->id = $id;
      // other members are still uninitialized
   }

   public function __construct($row_from_database){
       $this->id = $row_from_database->id;
       $this->name = $row_from_database->name;
       // etc.
   }
}

What is the best way to do this in PHP?

I dream of named constructors and method overload too +1
In my case, I want to have a protected constructor that has one less required argument than the public one - for the sake of standardizing its factory method. I need a class to be able to create copies of itself, and the factory is in an abstract class, but the concrete classes may have constructors that require a second argument - which the abstract class has no idea of.
Not really something of value but something I stumbled upon some time ago: the class DatePeriod in date_c.php has multiple constructors. But I do not know what PHP does internally with it.

K
Kris

I'd probably do something like this:

<?php

class Student
{
    public function __construct() {
        // allocate your stuff
    }

    public static function withID( $id ) {
        $instance = new self();
        $instance->loadByID( $id );
        return $instance;
    }

    public static function withRow( array $row ) {
        $instance = new self();
        $instance->fill( $row );
        return $instance;
    }

    protected function loadByID( $id ) {
        // do query
        $row = my_awesome_db_access_stuff( $id );
        $this->fill( $row );
    }

    protected function fill( array $row ) {
        // fill all properties from array
    }
}

?>

Then if i want a Student where i know the ID:

$student = Student::withID( $id );

Or if i have an array of the db row:

$student = Student::withRow( $row );

Technically you're not building multiple constructors, just static helper methods, but you get to avoid a lot of spaghetti code in the constructor this way.


Looks like you've just answered the question I asked gpilotino. Thanks! Very clear.
@gpilotino, overkill because you'd need yet another class, (or method) that would basically just consist of a switch/case decision tree, in the end just doing what I already did in two methods. factories are more useful in situations where you can't easily define the exact constraints of a problem, like creating form elements. but then, that's just my opinion and for the record; I don't claim it to be fact.
And could not we also make __construct() private, to prevent someone from ocassionally allocating a "non-ininitialized" instance?
@mlvljr: you could, but i'd suggest making it protected instead of private. otherwise you'll most likely run into trouble if you're ever going to extend your class.
Note from PHP 5.3 on you should probably use new static() rather than new self(), since new static() will work more sanely in child classes.
t
timaschew

The solution of Kris is really nice, but I prefer a mix of factory and fluent style:

<?php

class Student
{

    protected $firstName;
    protected $lastName;
    // etc.

    /**
     * Constructor
     */
    public function __construct() {
        // allocate your stuff
    }

    /**
     * Static constructor / factory
     */
    public static function create() {
        return new self();
    }

    /**
     * FirstName setter - fluent style
     */
    public function setFirstName($firstName) {
        $this->firstName = $firstName;
        return $this;
    }

    /**
     * LastName setter - fluent style
     */
    public function setLastName($lastName) {
        $this->lastName = $lastName;
        return $this;
    }

}

// create instance
$student= Student::create()->setFirstName("John")->setLastName("Doe");

// see result
var_dump($student);
?>

+1; This type of solution can yield really nice code. Although I would opt for setLastName (or rather all setters) in this solution to return $this instead of having effectively two setters on the same property.
As someone used to compiled, statically typed languages like C# this way of doing things just sits nicely with me.
How does providing a static create method differ from just using the constructor in the same way? $student = new Student()->setFirstName("John")->setLastName("Doe");
There's an important problem with that code: you can't ensure that the instance is valid (that's why there are constructors) and usually immutable classes are preferable.
This is most clean code which is seen for such question, and it can cleaner by use return new self() in the create() method
D
Daff

PHP is a dynamic language, so you can't overload methods. You have to check the types of your argument like this:

class Student 
{
   protected $id;
   protected $name;
   // etc.

   public function __construct($idOrRow){
    if(is_int($idOrRow))
    {
        $this->id = $idOrRow;
        // other members are still uninitialized
    }
    else if(is_array($idOrRow))
    {
       $this->id = $idOrRow->id;
       $this->name = $idOrRow->name;
       // etc.  
    }
}

All that leads to is awesome spaghetti code. But it is indeed probably the easiest way to do it.
If you create your constructors as you would in a statically typed language it will become spaghetti code. But you don't. Creating two constructors with one parameter and no type (no type == any type) for that parameter will not work in any language, anyway (e.g. it won't work to have two Java constructors with one Object parameter each in one class, either).
What i meant is that you're doing different things in the same scope based on outside influence, It's not a bad solution (since it will work), just not the one I would choose.
A language being "dynamic" does not exclude the possibility for function/constructor overloads. It does not even exclude static typing. And even if, there would still be the possibility of allowing overloads based purely on argument-count. Please don't use "dynamic" as an excuse for things.
I like this as a way of simplifying the code for the user of the class, and it accomplishes what the OP wanted. It won't be spaghetti code if you make two functions (like Kris' answer) and just call the functions appropriately in the constructor. The code for checking arguments is likely not that complicated. This assumes of course that there is some way to distinguish the arguments from each other as in this case.
B
Björn
public function __construct() {
    $parameters = func_get_args();
    ...
}

$o = new MyClass('One', 'Two', 3);

Now $paramters will be an array with the values 'One', 'Two', 3.

Edit,

I can add that

func_num_args()

will give you the number of parameters to the function.


How does this solve the problem of knowing what was passed? I think it complicates the issue as instead of having to check the type of the parameter, you have to check if x parameter is set and then the type of it.
It doesn't solve the problem to know what type was passed, but it's the way to go for "multiple constructors" in PHP. Type checking is up to OP to do.
i wonder what happens when a new developer is added to a project with lots of code like this
N
Nasif Md. Tanjim

As has already been shown here, there are many ways of declaring multiple constructors in PHP, but none of them are the correct way of doing so (since PHP technically doesn't allow it). But it doesn't stop us from hacking this functionality... Here's another example:

<?php

class myClass {
    public function __construct() {
        $get_arguments       = func_get_args();
        $number_of_arguments = func_num_args();

        if (method_exists($this, $method_name = '__construct'.$number_of_arguments)) {
            call_user_func_array(array($this, $method_name), $get_arguments);
        }
    }

    public function __construct1($argument1) {
        echo 'constructor with 1 parameter ' . $argument1 . "\n";
    }

    public function __construct2($argument1, $argument2) {
        echo 'constructor with 2 parameter ' . $argument1 . ' ' . $argument2 . "\n";
    }

    public function __construct3($argument1, $argument2, $argument3) {
        echo 'constructor with 3 parameter ' . $argument1 . ' ' . $argument2 . ' ' . $argument3 . "\n";
    }
}

$object1 = new myClass('BUET');
$object2 = new myClass('BUET', 'is');
$object3 = new myClass('BUET', 'is', 'Best.');

Source: The easiest way to use and understand multiple constructors:

Hope this helps. :)


This is the best solution. Can be even more elegant if using PHP 5.6+ with the new ... operator.
Of course this won't work with JannieT's original question since her desired constructors were __construct($id) and __construct($row_from_database). Both have one argument, presumably either an int for the first and an array or object for the second. The appending of a number could, of course, be extended to be some sort of argument signature in C++ style (i.e., __construct_i($intArg) and __construct_a($arrayArg)).
+1: I kinda like this, but extended with type information and without the double underscore prefixes in the nested ctors. Thanks for the inspiration!
You even could add reflection to your example code to apply type checks on parameters of each function of the class that starts with __construct and match the appropriate constructor that way
A
Andrei Serdeliuc ॐ

You could do something like this:

public function __construct($param)
{
    if(is_int($param)) {
         $this->id = $param;
    } elseif(is_object($param)) {
     // do something else
    }
 }

+1 for a very workable solution. However, for the class in mind I'll use @Kris' method.
y
yannis

As of version 5.4, PHP supports traits. This is not exactly what you are looking for, but a simplistic trait based approach would be:

trait StudentTrait {
    protected $id;
    protected $name;

    final public function setId($id) {
        $this->id = $id;
        return $this;
    }

    final public function getId() { return $this->id; }

    final public function setName($name) {
        $this->name = $name; 
        return $this;
    }

    final public function getName() { return $this->name; }

}

class Student1 {
    use StudentTrait;

    final public function __construct($id) { $this->setId($id); }
}

class Student2 {
    use StudentTrait;

    final public function __construct($id, $name) { $this->setId($id)->setName($name); }
}

We end up with two classes, one for each constructor, which is a bit counter-productive. To maintain some sanity, I'll throw in a factory:

class StudentFactory {
    static public function getStudent($id, $name = null) {
        return 
            is_null($name)
                ? new Student1($id)
                : new Student2($id, $name)
    }
}

So, it all comes down to this:

$student1 = StudentFactory::getStudent(1);
$student2 = StudentFactory::getStudent(1, "yannis");

It's a horribly verbose approach, but it can be extremely convenient.


J
Jed Lynch

Here is an elegant way to do it. Create trait that will enable multiple constructors given the number of parameters. You would simply add the number of parameters to the function name "__construct". So one parameter will be "__construct1", two "__construct2"... etc.

trait constructable
{
    public function __construct() 
    { 
        $a = func_get_args(); 
        $i = func_num_args(); 
        if (method_exists($this,$f='__construct'.$i)) { 
            call_user_func_array([$this,$f],$a); 
        } 
    } 
}

class a{
    use constructable;

    public $result;

    public function __construct1($a){
        $this->result = $a;
    }

    public function __construct2($a, $b){
        $this->result =  $a + $b;
    }
}

echo (new a(1))->result;    // 1
echo (new a(1,2))->result;  // 3

Very clever, elegant and reusable. :clap:
Parse error: syntax error, unexpected ',' on line 8
This is such an awesome way to do it :)
r
rojoca

Another option is to use default arguments in the constructor like this

class Student {

    private $id;
    private $name;
    //...

    public function __construct($id, $row=array()) {
        $this->id = $id;
        foreach($row as $key => $value) $this->$key = $value;
    }
}

This means you'll need to instantiate with a row like this: $student = new Student($row['id'], $row) but keeps your constructor nice and clean.

On the other hand, if you want to make use of polymorphism then you can create two classes like so:

class Student {

    public function __construct($row) {
         foreach($row as $key => $value) $this->$key = $value;
    }
}

class EmptyStudent extends Student {

    public function __construct($id) {
        parent::__construct(array('id' => $id));
    }
}

now you have two classes with different names but the same functionality just a different signature on the constructor, sounds like a pretty bad idea to me.
Sounds like classic polymorphism to me, otherwise known as object oriented programming.
Creating multiple classes to provide different constructors is indeed a bad idea. Classes that extends other classes should extend, meaning they should have added functionality, thats the point of OOP, not this.
g
gpilotino

as stated in the other comments, as php does not support overloading, usually the "type checking tricks" in constructor are avoided and the factory pattern is used intead

ie.

$myObj = MyClass::factory('fromInteger', $params);
$myObj = MyClass::factory('fromRow', $params);

Looks neat. I'm not familiar with factories. In your example, would $myObj be of the type MyClass? What would the two static functions look like that return a constructed instance of $myObj?
I would use seperate methods like Kris did to prevent one big factory method.
indeed, @Kris solution is the best.
this looks much like C++ tags
C
Carrie Kendall

You could do something like the following which is really easy and very clean:

public function __construct()    
{
   $arguments = func_get_args(); 

   switch(sizeof(func_get_args()))      
   {
    case 0: //No arguments
        break; 
    case 1: //One argument
        $this->do_something($arguments[0]); 
        break;              
    case 2:  //Two arguments
        $this->do_something_else($arguments[0], $arguments[1]); 
        break;            
   }
}

why assign func_get_args to a variable and call it again in the next line? would also be better if you only called func_get_args after deciding you need to based on fund_num_args.
Imho this is the opposite of a clean solution
W
Waku-2

This question has already been answered with very smart ways to fulfil the requirement but I am wondering why not take a step back and ask the basic question of why do we need a class with two constructors? If my class needs two constructors then probably the way I am designing my classes needs little more consideration to come up with a design that is cleaner and more testable.

We are trying to mix up how to instantiate a class with the actual class logic.

If a Student object is in a valid state, then does it matter if it was constructed from the row of a DB or data from a web form or a cli request?

Now to answer the question that that may arise here that if we don't add the logic of creating an object from db row, then how do we create an object from the db data, we can simply add another class, call it StudentMapper if you are comfortable with data mapper pattern, in some cases you can use StudentRepository, and if nothing fits your needs you can make a StudentFactory to handle all kinds of object construction tasks.

Bottomline is to keep persistence layer out of our head when we are working on the domain objects.


D
David Culbreth

I know I'm super late to the party here, but I came up with a fairly flexible pattern that should allow some really interesting and versatile implementations.

Set up your class as you normally would, with whatever variables you like.

class MyClass{
    protected $myVar1;
    protected $myVar2;

    public function __construct($obj = null){
        if($obj){
            foreach (((object)$obj) as $key => $value) {
                if(isset($value) && in_array($key, array_keys(get_object_vars($this)))){
                    $this->$key = $value;
                }
            }
        }
    }
}

When you make your object just pass an associative array with the keys of the array the same as the names of your vars, like so...

$sample_variable = new MyClass([
    'myVar2'=>123, 
    'i_dont_want_this_one'=> 'This won\'t make it into the class'
    ]);

print_r($sample_variable);

The print_r($sample_variable); after this instantiation yields the following:

MyClass Object ( [myVar1:protected] => [myVar2:protected] => 123 )

Because we've initialize $group to null in our __construct(...), it is also valid to pass nothing whatsoever into the constructor as well, like so...

$sample_variable = new MyClass();

print_r($sample_variable);

Now the output is exactly as expected:

MyClass Object ( [myVar1:protected] => [myVar2:protected] => )

The reason I wrote this was so that I could directly pass the output of json_decode(...) to my constructor, and not worry about it too much.

This was executed in PHP 7.1. Enjoy!


You can do some cool stuff like throwing an exception when an unexpected value is entered in the array. There is an example of this on a gist that I wrote up
s
stanley mbote

I was facing the same issue on creating multiple constructors with different signatures but unfortunately, PHP doesn't offer a direct method to do so. Howerever, I found a trick to overcome that. Hope works for all of you too.

    <?PHP

    class Animal
    {

      public function __construct()
      {
        $arguments = func_get_args();
        $numberOfArguments = func_num_args();

        if (method_exists($this, $function = '__construct'.$numberOfArguments)) {
            call_user_func_array(array($this, $function), $arguments);
        }
    }
   
    public function __construct1($a1)
    {
        echo('__construct with 1 param called: '.$a1.PHP_EOL);
    }
   
    public function __construct2($a1, $a2)
    {
        echo('__construct with 2 params called: '.$a1.','.$a2.PHP_EOL);
    }
   
    public function __construct3($a1, $a2, $a3)
    {
        echo('__construct with 3 params called: '.$a1.','.$a2.','.$a3.PHP_EOL);
    }
}

$o = new Animal('sheep');
$o = new Animal('sheep','cat');
$o = new Animal('sheep','cat','dog');

// __construct with 1 param called: sheep
// __construct with 2 params called: sheep,cat
// __construct with 3 params called: sheep,cat,dog

S
Salvi Pascual

Let me add my grain of sand here

I personally like adding a constructors as static functions that return an instance of the class (the object). The following code is an example:

 class Person
 {
     private $name;
     private $email;

     public static function withName($name)
     {
         $person = new Person();
         $person->name = $name;

         return $person;
     }

     public static function withEmail($email)
     {
         $person = new Person();
         $person->email = $email;

         return $person;
     }
 }

Note that now you can create instance of the Person class like this:

$person1 = Person::withName('Example');
$person2 = Person::withEmail('yo@mi_email.com');

I took that code from:

http://alfonsojimenez.com/post/30377422731/multiple-constructors-in-php


T
That Realty Programmer Guy

Hmm, surprised I don't see this answer yet, suppose I'll throw my hat in the ring.

class Action {
    const cancelable    =   0;
    const target        =   1
    const type          =   2;

    public $cancelable;
    public $target;
    public $type;


    __construct( $opt = [] ){

        $this->cancelable   = isset($opt[cancelable]) ? $opt[cancelable] : true;
        $this->target       = isset($opt[target]) ?     $opt[target] : NULL;
        $this->type         = isset($opt[type]) ?       $opt[type] : 'action';

    }
}


$myAction = new Action( [
    Action::cancelable => false,
    Action::type => 'spin',
    .
    .
    .
]);

You can optionally separate the options into their own class, such as extending SplEnum.

abstract class ActionOpt extends SplEnum{
    const cancelable    =   0;
    const target        =   1
    const type          =   2;
}

I also thought of this when I had to solve the following problem. My class should get a constructor that can be called either without a parameter or with a defined number of parameters (in this case 3). With the array it is very easy to check using empty and count and take appropriate action. If empty then terminate the function, because there is nothing to assign or if the number of parameters or their value does not fit throw appropriate exceptions. Translated with www.DeepL.com/Translator (free version)
g
galmok

This is my take on it (build for php 5.6).

It will look at constructor parameter types (array, class name, no description) and compare the given arguments. Constructors must be given with least specificity last. With examples:

// demo class
class X {
    public $X;

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

    public function __toString() {
        return 'X'.$this->X;
    }
}

// demo class
class Y {
    public $Y;

    public function __construct($y) {
        $this->Y = $y;
    }
    public function __toString() {
        return 'Y'.$this->Y;
    }
}

// here be magic
abstract class MultipleConstructors {
    function __construct() {
        $__get_arguments       = func_get_args();
        $__number_of_arguments = func_num_args();

        $__reflect = new ReflectionClass($this);
        foreach($__reflect->getMethods() as $__reflectmethod) {
            $__method_name = $__reflectmethod->getName();
            if (substr($__method_name, 0, strlen('__construct')) === '__construct') {
                $__parms = $__reflectmethod->getParameters();
                if (count($__parms) == $__number_of_arguments) {
                    $__argsFit = true;
                    foreach ($__parms as $__argPos => $__param) {
                        $__paramClass= $__param->getClass();
                        $__argVar = func_get_arg($__argPos);
                        $__argVarType = gettype($__argVar);
                        $__paramIsArray = $__param->isArray() == true;
                        $__argVarIsArray = $__argVarType == 'array';
                        // parameter is array and argument isn't, or the other way around.
                        if (($__paramIsArray && !$__argVarIsArray) ||
                            (!$__paramIsArray && $__argVarIsArray)) {
                            $__argsFit = false;
                            continue;
                        }
                        // class check
                        if ((!is_null($__paramClass) && $__argVarType != 'object') ||
                            (is_null($__paramClass) && $__argVarType == 'object')){
                            $__argsFit = false;
                            continue;
                        }
                        if (!is_null($__paramClass) && $__argVarType == 'object') {
                            // class type check
                            $__paramClassName = "N/A";
                            if ($__paramClass)
                                $__paramClassName = $__paramClass->getName();
                            if ($__paramClassName != get_class($__argVar)) {
                                $__argsFit = false;
                            }
                        }
                    }
                    if ($__argsFit) {
                        call_user_func_array(array($this, $__method_name), $__get_arguments);
                        return;
                    }
                }
            }
        }
        throw new Exception("No matching constructors");
    }
}

// how to use multiple constructors
class A extends MultipleConstructors {
    public $value;

    function __constructB(array $hey) {
        $this->value = 'Array#'.count($hey).'<br/>';
    }
    function __construct1(X $first) {
        $this->value = $first .'<br/>';
    }

    function __construct2(Y $second) {
        $this->value = $second .'<br/>';
    }
    function __constructA($hey) {
        $this->value = $hey.'<br/>';
    }

    function __toString() {
        return $this->value;
    }
}

$x = new X("foo");
$y = new Y("bar");

$aa = new A(array("one", "two", "three"));
echo $aa;

$ar = new A("baz");
echo $ar;

$ax = new A($x);
echo $ax;

$ay = new A($y);
echo $ay;

Result:

Array#3
baz
Xfoo
Ybar

Instead of the terminating exception if no constructor is found, it could be remove and allow for "empty" constructor. Or whatever you like.


S
Serginho

For php7, I compare parameters type as well, you can have two constructors with same number of parameters but different type.

trait GenericConstructorOverloadTrait
{
    /**
     * @var array Constructors metadata
     */
    private static $constructorsCache;
    /**
     * Generic constructor
     * GenericConstructorOverloadTrait constructor.
     */
    public function __construct()
    {
        $params = func_get_args();
        $numParams = func_num_args();

        $finish = false;

        if(!self::$constructorsCache){
            $class = new \ReflectionClass($this);
            $constructors =  array_filter($class->getMethods(),
                function (\ReflectionMethod $method) {
                return preg_match("/\_\_construct[0-9]+/",$method->getName());
            });
            self::$constructorsCache = $constructors;
        }
        else{
            $constructors = self::$constructorsCache;
        }
        foreach($constructors as $constructor){
            $reflectionParams = $constructor->getParameters();
            if(count($reflectionParams) != $numParams){
                continue;
            }
            $matched = true;
            for($i=0; $i< $numParams; $i++){
                if($reflectionParams[$i]->hasType()){
                    $type = $reflectionParams[$i]->getType()->__toString();
                }
                if(
                    !(
                        !$reflectionParams[$i]->hasType() ||
                        ($reflectionParams[$i]->hasType() &&
                            is_object($params[$i]) &&
                            $params[$i] instanceof $type) ||
                        ($reflectionParams[$i]->hasType() &&
                            $reflectionParams[$i]->getType()->__toString() ==
                            gettype($params[$i]))
                    )
                ) {
                    $matched = false;
                    break;
                }

            }

            if($matched){
                call_user_func_array(array($this,$constructor->getName()),
                    $params);
                $finish = true;
                break;
            }
        }

        unset($constructor);

        if(!$finish){
            throw new \InvalidArgumentException("Cannot match construct by params");
        }
    }

}

To use it:

class MultiConstructorClass{

    use GenericConstructorOverloadTrait;

    private $param1;

    private $param2;

    private $param3;

    public function __construct1($param1, array $param2)
    {
        $this->param1 = $param1;
        $this->param2 = $param2;
    }

    public function __construct2($param1, array $param2, \DateTime $param3)
    {
        $this->__construct1($param1, $param2);
        $this->param3 = $param3;
    }

    /**
     * @return \DateTime
     */
    public function getParam3()
    {
        return $this->param3;
    }

    /**
     * @return array
     */
    public function getParam2()
    {
        return $this->param2;
    }

    /**
     * @return mixed
     */
    public function getParam1()
    {
        return $this->param1;
    }
}

Can you show how you new up two instances of your MultiConstructorClass using the two different constructor methods? Thanks.
I thought my answer was slick, but this is clearly better.
Y
YuraV

More modern aproach: You are mixing seperate classes into one, entity & data hydration. So for your case you should have 2 classes:

class Student 
{
   protected $id;
   protected $name;
   // etc.
}
class StudentHydrator
{
   public function hydrate(Student $student, array $data){
      $student->setId($data['id']);
      if(isset($data['name')){
        $student->setName($data['name']);
      }
      // etc. Can be replaced with foreach
      return $student;
   }
}

//usage
$hydrator = new StudentHydrator();
$student = $hydrator->hydrate(new Student(), ['id'=>4]);
$student2 = $hydrator->hydrate(new Student(), $rowFromDB);

Also please note that you should use doctrine or other ORM that already provides automatic entity hydration. And you should use dependency injection in order to skip mannualy creating objects like StudentHydrator.


l
lukas.j

Starting with PHP 8 we can use named arguments:

class Student {

  protected int $id;
  protected string $name;

  public function __construct(int $id = null, string $name = null, array $row_from_database = null) {
    if ($id !== null && $name !== null && $row_from_database === null) {
      $this->id = $id;
      $this->name = $name;
    } elseif ($id === null && $name === null
        && $row_from_database !== null
        && array_keys($row_from_database) === [ 'id', 'name' ]
        && is_int($row_from_database['id'])
        && is_string($row_from_database['name'])) {
      $this->id = $row_from_database['id'];
      $this->name = $row_from_database['name'];
    } else {
      throw new InvalidArgumentException('Invalid arguments');
    }
  }

}

$student1 = new Student(id: 3, name: 'abc');
$student2 = new Student(row_from_database: [ 'id' => 4, 'name' => 'def' ]);

With proper checking it is possible to rule out invalid combinations of arguments, so that the created instance is a valid one at the end of the constructor (but errors will only be detected at runtime).


佚名

In response to the best answer by Kris (which amazingly helped design my own class btw), here is a modified version for those that might find it useful. Includes methods for selecting from any column and dumping object data from array. Cheers!

public function __construct() {
    $this -> id = 0;
    //...
}

public static function Exists($id) {
    if (!$id) return false;
    $id = (int)$id;
    if ($id <= 0) return false;
    $mysqli = Mysql::Connect();
    if (mysqli_num_rows(mysqli_query($mysqli, "SELECT id FROM users WHERE id = " . $id)) == 1) return true;
    return false;
}

public static function FromId($id) {
    $u = new self();
    if (!$u -> FillFromColumn("id", $id)) return false;
    return $u;
}

public static function FromColumn($column, $value) {
    $u = new self();
    if (!$u -> FillFromColumn($column, $value)) return false;
    return $u;
}

public static function FromArray($row = array()) {
    if (!is_array($row) || $row == array()) return false;
    $u = new self();
    $u -> FillFromArray($row);
    return $u;
}

protected function FillFromColumn($column, $value) {
    $mysqli = Mysql::Connect();
    //Assuming we're only allowed to specified EXISTENT columns
    $result = mysqli_query($mysqli, "SELECT * FROM users WHERE " . $column . " = '" . $value . "'");
    $count = mysqli_num_rows($result);
    if ($count == 0) return false;
    $row = mysqli_fetch_assoc($result);
    $this -> FillFromArray($row);
}

protected function FillFromArray(array $row) {
    foreach($row as $i => $v) {
        if (isset($this -> $i)) {
            $this -> $i = $v;
        }
    }
}

public function ToArray() {
    $m = array();
    foreach ($this as $i => $v) {
        $m[$i] = $v;    
    }
    return $m;
}

public function Dump() {
    print_r("<PRE>");
    print_r($this -> ToArray());
    print_r("</PRE>");  
}

v
viral

Call constructors by data type:

class A 
{ 
    function __construct($argument)
    { 
       $type = gettype($argument);

       if($type == 'unknown type')
       {
            // type unknown
       }

       $this->{'__construct_'.$type}($argument);
    } 

    function __construct_boolean($argument) 
    { 
        // do something
    }
    function __construct_integer($argument) 
    { 
        // do something
    }
    function __construct_double($argument) 
    { 
        // do something
    }
    function __construct_string($argument) 
    { 
        // do something
    }
    function __construct_array($argument) 
    { 
        // do something
    }
    function __construct_object($argument) 
    { 
        // do something
    }
    function __construct_resource($argument) 
    { 
        // do something
    }

    // other functions

} 

You should mention that you got this code snippet from here --> php.net/manual/en/language.oop5.decon.php#99903.
That was about 6 months ago, check my update @LavaSlider
@iRuth I have completely changed it now
T
TheKLF99

You could always add an extra parameter to the constructor called something like mode and then perform a switch statement on it...

class myClass 
{
    var $error ;
    function __construct ( $data, $mode )
    {
        $this->error = false
        switch ( $mode )
        {
            'id' : processId ( $data ) ; break ;
            'row' : processRow ( $data ); break ;
            default : $this->error = true ; break ;
         }
     }

     function processId ( $data ) { /* code */ }
     function processRow ( $data ) { /* code */ }
}

$a = new myClass ( $data, 'id' ) ;
$b = new myClass ( $data, 'row' ) ;
$c = new myClass ( $data, 'something' ) ;

if ( $a->error )
   exit ( 'invalid mode' ) ;
if ( $b->error )
   exit ('invalid mode' ) ;
if ( $c->error )
   exit ('invalid mode' ) ;

Also with that method at any time if you wanted to add more functionality you can just add another case to the switch statement, and you can also check to make sure someone has sent the right thing through - in the above example all the data is ok except for C as that is set to "something" and so the error flag in the class is set and control is returned back to the main program for it to decide what to do next (in the example I just told it to exit with an error message "invalid mode" - but alternatively you could loop it back round until valid data is found).


j
jechaviz

I created this method to let use it not only on constructors but in methods:

My constructor:

function __construct() {
    $paramsNumber=func_num_args();
    if($paramsNumber==0){
        //do something
    }else{
        $this->overload('__construct',func_get_args());
    }
}

My doSomething method:

public function doSomething() {
    $paramsNumber=func_num_args();
    if($paramsNumber==0){
        //do something
    }else{
        $this->overload('doSomething',func_get_args());
    }
}

Both works with this simple method:

public function overloadMethod($methodName,$params){
    $paramsNumber=sizeof($params);
    //methodName1(), methodName2()...
    $methodNameNumber =$methodName.$paramsNumber;
    if (method_exists($this,$methodNameNumber)) {
        call_user_func_array(array($this,$methodNameNumber),$params);
    }
}

So you can declare

__construct1($arg1), __construct2($arg1,$arg2)...

or

methodName1($arg1), methodName2($arg1,$arg2)...

and so on :)

And when using:

$myObject =  new MyClass($arg1, $arg2,..., $argN);

it will call __constructN, where you defined N args

then $myObject -> doSomething($arg1, $arg2,..., $argM)

it will call doSomethingM, , where you defined M args;


S
Sophie Su

Kris's answer is great, but as Buttle Butku commented, new static() would be preferred in PHP 5.3+.

So I'd do it like this (modified from Kris's answer):

<?php

class Student
{
    public function __construct() {
        // allocate your stuff
    }

    public static function withID( $id ) {
        $instance = new static();
        $instance->loadByID( $id );
        return $instance;
    }

    public static function withRow( array $row ) {
        $instance = new static();
        $instance->fill( $row );
        return $instance;
    }

    protected function loadByID( $id ) {
        // do query
        $row = my_awesome_db_access_stuff( $id );
        $this->fill( $row );
    }

    protected function fill( array $row ) {
        // fill all properties from array
    }
}

?>

Usage:

<?php

$student1 = Student::withID($id);
$student2 = Student::withRow($row);

?>

I also found an useful example in php.net OOP document.