I'm trying to instantiate a class from PHP from another class, as well as calling methods of said class, but no matter what I do, I recieve errors.
Why won't the following work:
class Example {
public function sayHi () {
echo "hi";
}
}
class Caller {
private $_controller,
$_action;
public function __construct ($cont, $action) {
$this->_controller = $cont;
$this->_action = $action;
}
public function __toString() {
return (string)$this->_action;
}
public function run () {
$controller = new $this->_controller;
if (is_callable(array($controller, $this->_action))) {
$controller->$this->_action;
}
}
}
$caller = new Caller ('Example', 'sayHi');
$caller->run();
When changing the method run to the following works?
public function run () {
$controller = new $this->_controller;
if (is_callable(array($controller, $this->_action))) {
call_user_func(array($this->_controller, $this->_action));
}
}
The reason why I don't want call_user_func, is because it calls the controller statically.
Removing the magic method __toString gives me:
Catchable fatal error: Object of class Caller could not be converted to string
Adding it again gives me the following:
Undefined property: Example::$sayHi (same line, on method run() from Caller)
Trying to get property of non-object (same line, on method run() from Caller)
This line is your problem:
$controller->$this->_action;
Couple issues here. For one, you don't have parens at the end to indicate you are calling a method. PHP thinks you are trying to access a property.
Secondly, you want to get the value first for $this->_action and then use that value dynamically as the method name. Use curly braces to separate this out.
Change that line to this:
$controller->{$this->_action}();
And it works: http://3v4l.org/2B0qg
You're partially right. If you simply pass a class name and function to call_user_func it will call it statically. But if you pass an instance to it, it will call it within that instance. That's what callables are.
Related
I have a class with the structure below. And Im trying to call a method of the class with "$this->getTopProds($prodsInfo)" however Im getting an error:
"Cannot use '$this' in non-object context.intelephense(1030)"
And on the page the error is "Non-static method App\JsonResponse\Prod\ProdRender:: getTopProds() cannot be called statically".
Do you know what can be the issue?
class ProdRender
{
public static function hotel(array $prodsInfo): array
{
dd($this->getTopProds($prodsInfo));
}
private function getTopProds(array $prodsInfo)
{
//
}
}
No, we can't use this keyword inside a static method. “this” refers to the current instance of the class. But if we define a method as static, the class instance will not have access to it
To keep using the other method call inside the static method you need to change the second method getTopProds to be static too, and call it with self
self::getTopProds($prodsInfo)
In case you need it try this:
<?php
class ProdRender
{
public function hotel(array $prodsInfo)
{
return $this->getTopProds($prodsInfo);
}
private function getTopProds(array $prodsInfo)
{
return $prodsInfo;
}
}
$ho = new ProdRender();
$response = $ho->hotel(["fadf","ceec"]);
var_dump($response);
class MyClass {
var $lambda;
function __construct() {
$this->lambda = function() {echo 'hello world';};
// no errors here, so I assume that this is legal
}
}
$myInstance = new MyClass();
$myInstance->lambda();
//Fatal error: Call to undefined method MyClass::lambda()
So what is the correct syntax for reaching class variables ?
In PHP, methods and properties are in a separate namespace (you can have a method and a property with the same name), and whether you are accessing a property or a method depends of the syntax you are using to do so.
$expr->something() is a method call, so PHP will search something in the class' list of methods.
$expr->something is a property fetch, so PHP will search something in the class' list of properties.
$myInstance->lambda(); is parsed as a method call, so PHP searches for a method named lambda in your class, but there is no such method (hence the Call to undefined method error).
So you have to use the fetch property syntax to fetch the lambda, and then call it.
Since PHP 7.0, you can do this with ($obj->lambda)():
($obj->lambda)();
The parentheses make sure that PHP parses ($obj->lambda) as fetch the property named lambda. Then, () calls the result of fetching the property.
or you can do this with ->lambda->__invoke():
$myInstance = new MyClass();
$myInstance->lambda->__invoke();
__invoke is one of PHP's magic methods. When an object implements this method, it becomes invokable: it can be called using the $var() syntax. Anonymous functions are instances of Closure, which implements __invoke.
Or assign it to a local variable:
$lambda = $myInstance->lambda;
$lambda();
Or call it using call_user_func:
call_user_func($myInstance->lambda);
call_user_func can call any callable, including anonymous functions.
Alternatively, if this is a common pattern in your code, you can setup a __call method to forward calls to your lambda:
class MyClass
{
private $lambda;
public function __construct()
{
$this->lambda = function() {
echo "Hello world!\n";
};
}
public function __call($name, $args)
{
return call_user_func_array($this->$name, $args);
}
}
Now this works:
$myInstance = new MyClass();
$myInstance->lambda();
Since PHP 5.4 you can even do that in a trait:
trait LambdasAsMethods
{
public function __call($name, $args)
{
return call_user_func_array($this->$name, $args);
}
}
class MyClass
{
use LambdasAsMethods;
private $lambda;
public function __construct()
{
$this->lambda = function() {
echo "Hello World!\n";
};
}
}
$myInstance = new MyClass();
$myInstance->lambda();
You can also call your lambda function without change something in your class, using ReflectionFunction.
$myInstance = new MyClass();
$lambda = new ReflectionFunction($myInstance->lambda);
$lambda->invoke();
or if you have to pass arguments then
$args = array('arg'=>'value');
$lambda->invokeArgs($args);
class MyClass {
var $lambda;
function __construct() {
$this->lambda = function() {echo 'hello world';};
// no errors here, so I assume that this is legal
}
}
$myInstance = new MyClass();
$myInstance->lambda();
//Fatal error: Call to undefined method MyClass::lambda()
So what is the correct syntax for reaching class variables ?
In PHP, methods and properties are in a separate namespace (you can have a method and a property with the same name), and whether you are accessing a property or a method depends of the syntax you are using to do so.
$expr->something() is a method call, so PHP will search something in the class' list of methods.
$expr->something is a property fetch, so PHP will search something in the class' list of properties.
$myInstance->lambda(); is parsed as a method call, so PHP searches for a method named lambda in your class, but there is no such method (hence the Call to undefined method error).
So you have to use the fetch property syntax to fetch the lambda, and then call it.
Since PHP 7.0, you can do this with ($obj->lambda)():
($obj->lambda)();
The parentheses make sure that PHP parses ($obj->lambda) as fetch the property named lambda. Then, () calls the result of fetching the property.
or you can do this with ->lambda->__invoke():
$myInstance = new MyClass();
$myInstance->lambda->__invoke();
__invoke is one of PHP's magic methods. When an object implements this method, it becomes invokable: it can be called using the $var() syntax. Anonymous functions are instances of Closure, which implements __invoke.
Or assign it to a local variable:
$lambda = $myInstance->lambda;
$lambda();
Or call it using call_user_func:
call_user_func($myInstance->lambda);
call_user_func can call any callable, including anonymous functions.
Alternatively, if this is a common pattern in your code, you can setup a __call method to forward calls to your lambda:
class MyClass
{
private $lambda;
public function __construct()
{
$this->lambda = function() {
echo "Hello world!\n";
};
}
public function __call($name, $args)
{
return call_user_func_array($this->$name, $args);
}
}
Now this works:
$myInstance = new MyClass();
$myInstance->lambda();
Since PHP 5.4 you can even do that in a trait:
trait LambdasAsMethods
{
public function __call($name, $args)
{
return call_user_func_array($this->$name, $args);
}
}
class MyClass
{
use LambdasAsMethods;
private $lambda;
public function __construct()
{
$this->lambda = function() {
echo "Hello World!\n";
};
}
}
$myInstance = new MyClass();
$myInstance->lambda();
You can also call your lambda function without change something in your class, using ReflectionFunction.
$myInstance = new MyClass();
$lambda = new ReflectionFunction($myInstance->lambda);
$lambda->invoke();
or if you have to pass arguments then
$args = array('arg'=>'value');
$lambda->invokeArgs($args);
Take the following example of two classes:
class Yolo {
public function __invoke() {
echo 'YOLO';
}
}
class Swag {
public $yolo;
public function __construct() {
$this->yolo = new Yolo();
}
}
Is it possible to invoke the Yolo object via an instance of Swag?
(new Swag())->yolo(); throws a warning and doesn't call __invoke:
PHP Warning: Uncaught Error: Call to undefined method Swag::yolo()
In PHP 7 you can directly call it (just need extra braces):
((new Swag())->yolo)();
In PHP 5 you need a temp variable:
$y=(new Swag())->yolo;
$y();
The problem is that you try to call the function yolo() on the Swag class. In your case you have to use the public class variable and call the subclass over that.
var_dump((new Swag())->yolo);
that is your object. When you use () you try to call the class not the class variable.
You do not even have to make it a public method.
But you might have to reroute the call manually since the callable method is created at runtime:
class Yolo {
public function __invoke() {
echo 'YOLO';
}
}
class Swag {
private $yolo;
public function __construct() {
$this->yolo = new Yolo();
}
function __call($method, $args) {
if(is_callable($this->$method))
{
return call_user_func_array($this->$method, $args);
}
}
}
(new Swag())->yolo();
It would probably a good idea to check first, if the called method exists and if it is actually callable.
But the example works as a proof of concept.
class MyClass {
var $lambda;
function __construct() {
$this->lambda = function() {echo 'hello world';};
// no errors here, so I assume that this is legal
}
}
$myInstance = new MyClass();
$myInstance->lambda();
//Fatal error: Call to undefined method MyClass::lambda()
So what is the correct syntax for reaching class variables ?
In PHP, methods and properties are in a separate namespace (you can have a method and a property with the same name), and whether you are accessing a property or a method depends of the syntax you are using to do so.
$expr->something() is a method call, so PHP will search something in the class' list of methods.
$expr->something is a property fetch, so PHP will search something in the class' list of properties.
$myInstance->lambda(); is parsed as a method call, so PHP searches for a method named lambda in your class, but there is no such method (hence the Call to undefined method error).
So you have to use the fetch property syntax to fetch the lambda, and then call it.
Since PHP 7.0, you can do this with ($obj->lambda)():
($obj->lambda)();
The parentheses make sure that PHP parses ($obj->lambda) as fetch the property named lambda. Then, () calls the result of fetching the property.
or you can do this with ->lambda->__invoke():
$myInstance = new MyClass();
$myInstance->lambda->__invoke();
__invoke is one of PHP's magic methods. When an object implements this method, it becomes invokable: it can be called using the $var() syntax. Anonymous functions are instances of Closure, which implements __invoke.
Or assign it to a local variable:
$lambda = $myInstance->lambda;
$lambda();
Or call it using call_user_func:
call_user_func($myInstance->lambda);
call_user_func can call any callable, including anonymous functions.
Alternatively, if this is a common pattern in your code, you can setup a __call method to forward calls to your lambda:
class MyClass
{
private $lambda;
public function __construct()
{
$this->lambda = function() {
echo "Hello world!\n";
};
}
public function __call($name, $args)
{
return call_user_func_array($this->$name, $args);
}
}
Now this works:
$myInstance = new MyClass();
$myInstance->lambda();
Since PHP 5.4 you can even do that in a trait:
trait LambdasAsMethods
{
public function __call($name, $args)
{
return call_user_func_array($this->$name, $args);
}
}
class MyClass
{
use LambdasAsMethods;
private $lambda;
public function __construct()
{
$this->lambda = function() {
echo "Hello World!\n";
};
}
}
$myInstance = new MyClass();
$myInstance->lambda();
You can also call your lambda function without change something in your class, using ReflectionFunction.
$myInstance = new MyClass();
$lambda = new ReflectionFunction($myInstance->lambda);
$lambda->invoke();
or if you have to pass arguments then
$args = array('arg'=>'value');
$lambda->invokeArgs($args);