Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does assigning a dynamic object's member variable in a single statement cause a syntax error in PHP?

I am trying to create a new object, assign one of it's member's a value and then assign that object to an array key in a single statement.
IE:

class MyObj {
  public $member = 'sad';
}

$myArray = array(
  'key' => ((new MyObj())->member = 'happy')
);

The problem is this gives me a syntax error: "syntax error, unexpected '='"

The following code is a work around:

$obj = new MyObj();
$obj->member = 'happy';
$myArray = array(
  'key' => $obj
);

The question is why doesn't the single statement work? Why does it produce the syntax error? Finally, how can I create a single statement that creates a new object, assigns one of the member variables a value, and then associates that object with a key in an array?

like image 750
Drellgor Avatar asked Nov 01 '22 05:11

Drellgor


1 Answers

First of all I want to say that this is a good question. I don't know why it is not working but there is a simple workaround by using a constructor with a parameter to set the member variable. In fact the construct function is made for triggering code when you initialize an object, and this is exactly what you are trying.

class MyObj {
  public $member;
  public function __construct($x){
    $this->member=$x;
  }
}

$myArray = array(
  'key' => new MyObj('happy')
);

print_r($myArray);

EDIT: I found that accessing a class during initialisation was not possible prior php version 5.4.0. I'm using php 5.4.12 and can't make this work either but it should work, because it is a new feature added since version 5.4.0.

http://docs.php.net/manual/en/migration54.new-features.php

Class member access on instantiation has been added, e.g. (new Foo)->bar().

EDIT: After some testing I came up with this example that actually works. When you try to initialize a new object and access its properties at the same time you are -per definition- chaining methods. This was not possible before php version 5.4.0. If you're using php >= 5.4 you can do this. To initialise an object and access its properties at the same time you need to make sure that the previous method call returns the object (new Obj returns the object automatically). PHP doesn't do this automatically so you need to do return $this manually. See next edit to get a workaround for this.

class MyObj {
  public $member;
  public function setMember($x){
    $this->member=$x;
    return $this;
  }
}

$myArray = array(
  'key' => (new MyObj)->setMember('happy')
);

print_r($myArray);

Note that the member variable is set by a method that returns the object.

EDIT: (11/02/14)

I've made a class to make methods from child classes chainable without having to return $this manually. It also can enable and disable method chaining so you can do both without returning $this when not needed (see start() and stop() methods)

Class Chainable{
    private  $chaining;//boolean

    public function __call($method,$arguments) {
        if(method_exists($this, $method)) {
            //$res=call_user_func_array(array($this,$method),$arguments);

            if($this->chaining ){
                if(!isset(call_user_func_array(array($this,$method),$arguments))){
                    return $this;
                }
                else{
                    echo "you can't chain methods that return something";
                            //however you could add the returned values to an array 
                            //(ie $this->returnedvalues)
                }   
            }

        }
    }
    private function setChaining($x){
        $this->chaining=$x;
    }
    public function start(){
        $this->setChaining(1);
        return $this;
    }
    public function stop(){
        $this->setChaining(0);
        return $this;
    }
}

How to use: Chainable methods must be private or protected. Public methods won't be called by the parents __call() method (Chainable class) because they are out of scope.

Class Foo extends Chainable{
    public $par1;
    public $par2;

    protected function setPar1($x){
        $this->par1=$x;
    }
    protected function setPar2($y){
        $this->par2=$y;
    }
    public function getPar1(){//public functions are not visible to the __call method from parent class.
        return $this->par1;
    }
    public function getPar2(){
        return $this->par2;
    }
    protected function printPar1(){
        echo $this->par1;
    }
    protected function printPar2(){
        echo $this->par2;
    }
}

$test=(new Foo())->start()//start chaining, set boolean true
                 ->setPar1('hello')//chained method calls...
                 ->setPar2('world')
                 ->printPar1()
                 ->printPar2()
                 ->stop();//stop chaining

$test->setPar1('hi');//normal method calls...
$test->setPar2('you');
echo '<br>'.$test->getPar1().' '.$test->getPar2();
$test->start()->setPar1('bonjour')->setPar2('soleil');//start chaining again
$test->stop();
like image 138
kasper Taeymans Avatar answered Nov 15 '22 04:11

kasper Taeymans