Why can't I use an object when I construct it? - php

I've tried searching for this but frankly I don't know what to search for and unfortunately I imagine this question has been asked before.
In PHP, and possibly other languages, why can't I use an object immediately after I create it?
// This causes an error
$obj = new Object()->myFunction();
Note: I return $this in most of my setter functions so I can chain them together
function myFunction() {
// ... some more code here ...
return $this;
}

It's simply invalid syntax in PHP. You are able to get this to work in PHP 5.4 by wrapping the object constructor expression with parentheses:
$obj = (new Object())->myFunction();
See PHP 5.4 new features:
Class member access on instantiation has been added, e.g. (new Foo)->bar().
If you want $obj to be the value of the new Object, be sure to return $this from Object::myFunction() (this is called method chaining).
An alternative for getting constructor chaining to work is to have a static method in your class which creates the new class instance for you:
class Object {
public function __construct($var) {
$this->var = $var;
}
public static function newObject($var) {
return new Object($var);
}
}
$obj = Object::newObject()->chainMethodX()->chainMethodY()->...

This is invalid syntax.
PHP only supports:
$obj = new Object();
$obj->myFunction();
Keep in mind that, were you code sample to work, $obj would get the return value of myFunction().

Although not documented on the site it would appear as though the object operator -> has a higher precedence then the new keyword. So saying:
$obj = new Object()->someFunction();
is evaluated like you wrote
$obj = new (Object()->someFunction());
instead of the intended
$obj = (new Object())->someFunction();
The real reason it works this way is in the php grammer definition on line 775

Related

Strange Object/Class Issue (stdClass)

I've just recently began messing around with anonymous functions, and I decided to try and put it to actual use in a larger project. After hitting a road block, I tried setting up a small practice script, but the same issue persists. I don't quite understand what's going on, but here's some code,
$a = function($in) {
echo $in;
};
$a('b4ng');
The above works just fine, as does the following,
class Foo {
public $cmd;
public function __construct() {
$this->cmd = new stdClass();
$this->cmd->a = 'b4ng';
}
}
$bar = new Foo();
echo $bar->cmd->a;
That being made clear, the following does not work,
class Foo {
public $cmd;
public function __construct() {
$this->cmd = new stdClass();
$this->cmd->message = function($in) {
echo $in;
};
}
}
$bar = new Foo();
$bar->cmd->message('b4ng');
When attempting to use the snippet above, I'm hit with the following error,
Fatal error: Call to undefined method stdClass::message()
I understand what the error is telling me, I just don't understand why; 'message' obviously isn't a native function/method of stdClass, but it was declared in 'cmd'.
There is another thing you can't do:
$o = new SomeClass();
$m = $o->someMethod;
$m();
The issue here is that PHP has a special syntax for a member function call, which is what matches $o->foo(). In your case though, foo is a closure (i.e. a data member, not a method) so that causes the error. In order to fix this, you first need to retrieve foo from your object and then invoke it:
// use brackets to force evaluation order
($o->foo)(args..);
// use dedicated function
call_user_func($o->foo, args..);
// use two steps
$foo = $o->foo;
$foo(args..);
I'd try the first variant first, but I'm not sure if PHP allows it. The last variant is the most cludgy, but that one surely works.
In PHP, you can't define class methods outside the class itself. So, you can't create an instance of stdClass and then dynamically create methods for it.

php passing an object back from a class function

I have a set of PHP functions that I want to move into a new class. One of the functions is using &$obj in an argument to modify the original object:
function process_new_place_names(&$obj)
any changes made to $obj in this function are passed back to the script.
Question is, can I do this inside a PHP class too? Also, what is the terminology of this approach?
You can absolutely do this inside of classes. It's known as passing by reference.
Further reading:
http://php.net/manual/en/language.references.php
http://php.net/manual/en/language.oop5.references.php
As SomeKittens says, this is perfectly possible inside a class. However, if $obj is itself an instance of a class, and all you need to do is modify its member variables (known as mutating the object) then there's no need to pass it by reference.
For example, the following code will output baz;
class Foo
{
public $bar;
}
function process_new_place_names($obj)
{
$obj->bar = 'baz';
}
$obj = new Foo();
$obj->bar = 'bar';
process_new_place_names($obj);
echo $obj->bar;
Pass-by-reference is only necessary when you want to change the value of the variable itself; for example, re-assigning the object reference:
function process_new_place_names(&$obj)
{
$obj = new Foo();
$obj->bar = 'baz';
}

Combining object operator ( -> ) and "new"

Given a class A with a member function f(), the following apparently reasonable code:
( new A() )->f();
Fails with a syntax error: "unexpected T_OBJECT_OPERATOR".
Is there an explanation for this?
Edit: As Mageek guessed, I'm trying to make sense of this behaviour; I already know how to work around it.
This is only available as of PHP 5.4. Until then you have to assign the instance to a variable and use that.
See: http://www.php.net/manual/en/migration54.new-features.php
The feature you have asked for is available from PHP 5.4. Here is the list of new features in PHP 5.4:
http://docs.php.net/manual/en/migration54.new-features.php
Class member access on instantiation has been added, e.g. (new Foo)->bar().
But you can try this trick:
class TestClass {
protected $_testvar;
public function __construct($param) {
$this->_testvar = $param;
}
public function testMethod() {
return $this->_testvar;
}
}
function TestClass($param) { return new TestClass($param); }
Now you can write:
$a = TestClass(2)->testMethod();

PHP create_function result stored as instance variable, and called as $object->func()?

I'm using PHPs create_function($args, $code) function to dynamically load a function definition from a database.
The way I'm attempting to implement it is as follows:
I have a class MyClass which has an instance variable myFunction. The constructor populates that instance variable with the result of a call to create_function. I'm hoping to dynamically create a function for the specific object (once instantiated) of this class, that can be called as $object->myFunction(arg1, arg2);
So my class looks like:
class MyClass {
public $myFunction = '';
public function __construct() {
$this->myFunction = //return function body from DB call.
}
}
I'm then trying to call this dynamic function from elsewhere in my program on the instantiated "MyClass" object by doing something like...
$object = new MyClass();
$object->myFunction(args..);
However I keep getting errors such as:
MyClass and its behaviors do not have a method or closure named myFunction.
When I run var_dump($object->myFunction) I get back "lambda_xx", which is a good sign meaning create_function is at least working.
Interesting Update on Works vs. Doesn't Work cases
It turns out that in my "other file" where I am doing the following:
$pm = Yii::app()->user->postMatching; //This is a PostMatching object made elsewhere
$c = $pm->findRelated;
foreach ($posts as $post) {
var_dump($c);
$postIds = $c($post, $limit);
//post to related mapping
$specificRelatedPostIds[$post->postId] = $postIds;
}
exit; // exiting for testing
This doesn't work, but if instead of pulling the object $pm from Yii::app()->user->postMatching I just create a new one:
$pm = new PostMatching();
$c = $pm->findRelated; //the anon function instance variable
$c(); // THIS WORKS NOW!
So naturally I var_dumped $pm and $c in both the "newly created" case and the case where I get it from Yii::app()->user->postMatching, and they are identical. The only thing that is different is the name of the anonymous function (as expected).
Does anyone have any idea why this might be the case? In both cases $pm IS an instantiated PostMatching object with that instance variable, I'm just unable to use the syntax to invoke it!
Just updated the above with newly discovered "Twists", thanks guys!
Maybe something along these lines can be useful:
class MyClass {
private $myFunction = '';
public function __construct() {
$this->myFunction = //return function body from DB call.
}
public function myFunction() {
$args = func_get_args();
return call_user_func_array($this->myFunction, $args);
}
}
That's due to parsing-related troubles that PHP has. This version should work:
$object = new MyClass();
$method = $object->myFunction;
$method(args..);
See it in action.
You can call the method like this:
call_user_func($object->myFunction, args..);

PHP stdClass() with __get() Magic Method

Take the following code as an example:
class xpto
{
public function __get($key)
{
return $key;
}
}
function xpto()
{
static $instance = null;
if (is_null($instance) === true)
{
$instance = new xpto();
}
return $instance;
}
echo xpto()->haha; // returns "haha"
Now, I'm trying to archive the same result but without have to write the xpto class. My guess is I should have to write something like this:
function xpto()
{
static $instance = null;
if (is_null($instance) === true)
{
$instance = new stdClass();
}
return $instance;
}
echo xpto()->haha; // doesn't work - obviously
Now, is it possible to add __get() magic functionality to the stdClass object? I guess not, but I'm not sure.
No, it is not possible. You cannot add anything to stdClass. Also, unlike Java, where every object is a direct or indirect subclass of Object, this is not the case in PHP.
class A {};
$a = new A();
var_dump($a instanceof stdClass); // will return false
What are you really trying to achieve? Your question sounds a bit like "I want to close the door of my car, but without having a car" :-).
The OP looks like they are trying to achieve a singleton pattern using a function in the global scope which is probably not the correct way to go, but anyway, regarding Cassy's answer, "You cannot add anything to stdClass" - this is not true.
You can add properties to the stdClass simply by assigning a value to them:
$obj = new stdClass();
$obj->myProp = 'Hello Property'; // Adds the public property 'myProp'
echo $obj->myProp;
However, I think you need PHP 5.3+ in order to add methods (anonymous functions / closures), in which case you might be able to do something like the following. However, I've not tried this. But if this does work, can you do the same with the magic __get() method?
UPDATE: As noted in the comments, you cannot dynamically add methods in this way. Assigning an anonymous function (PHP 5.3+) does just that and simply assigns a function (strictly a closure object) to a public property.
$obj = new stdClass();
$obj->myMethod = function($name) {echo 'Hello '.$name;};
// Fatal error: Call to undefined method stdClass::myMethod()
//$obj->myMethod('World');
$m = $obj->myMethod;
$m('World'); // Output: Hello World
call_user_func($obj->myMethod,'Foo'); // Output: Hello Foo

Categories