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.
Related
I'm using PHP 7.1.11
I've tried following code for an Anonymous Class which is declared as a parameter to a class method. I want to call the method present in an anonymous class. But I'm getting a fatal error. How should I call the anonymous class method successfully?
<?php
class Util {
private $logger;
public function __construct(){}
public function getLogger($logger) {
$this->logger = $logger;
}
public function setLogger($logger) {
$this->logger = $logger;
}
}
$util = new Util();
$util->setLogger(new class {
public function log($msg) {
echo $msg;
}
});
$util->setLogger()->log('Phil runs very fast'); // Ioutput should be : Phil runs very fast
?>
Output I'm currently getting :
Fatal error: Uncaught ArgumentCountError: Too few arguments to function Util::setLogger(), 0 passed in anonymous_class_ex.php on line 27 and exactly 1 expected in anonymous_class_ex.php:13 Stack trace: #0 anonymous_class_ex.php(https://stackoverflow.com/posts/47605127/edit27): Util->setLogger() #1 {main} thrown in anonymous_class_ex.php on line 13
You'd run it like you would any dynamic reference to a callable, as a two-element array: [object, 'methodName']. Here the object is your anonymous class definition:
$util->setLogger([new class {
public function log($msg) {
echo $msg;
}
}, 'log']);
Here's a stand-alone runnable example for you to look at:
class Outer {
function runner(){
$this->runCallback([new class {
function anonymousClassMethod(){
echo "hi from anonymous class method";
}
}, 'anonymousClassMethod']);
}
function runCallback(callable $f){
$f();
}
}
$o = new Outer();
$o->runner();
I see 2 issues with your code.
You are not using the right method on $util
Instead of
$util->setLogger()->log('Phil runs very fast');
your code should be $util->getLogger()->log('Phil runs very fast');.
You want to getLogger since it was already set before.
Your implementation of getLogger() might be flawed
In my opinion, this method should not have any parameter and it should return something, which is actually missing in your code.
So instead of
public function getLogger($logger) {
$this->logger = $logger;
}
you should have something like
public function getLogger() {
return $this->logger;
}
With those 2 points addressed, your code works as expected. See here for yourself https://ideone.com/V2slho.
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.
If I have a class like:
class MyClass
{
public function foo()
{
echo "foo";
}
}
And then outside of the class instantiate it and try to create an anonymous function in it:
$mine = new MyClass();
$mine->bar = function() {
echo "bar";
}
And then try to call it like $mine->bar(), I get:
Fatal error: Call to undefined method MyClass::bar() in ...
How can I create an anonymous function / closure on a class instance?
Aside: Before you tell me I should rethink my logic or use interfaces and OOP properly, in my case, it's a convenience method that applies to this specific instance of a bastardized class in an attempt to clean-up a legacy procedural application. And yes, I'm using PHP 5.3+
See my blog article here: http://blog.flowl.info/2013/php-container-class-anonymous-function-lambda-support/
You need to add a magic __call function:
public function __call($func, $args) {
return call_user_func($this->$func, $args);
}
The problem is that within this construct you can call private methods from public scope.
I suggest not to simply add new variables to a class that are not defined. You can avoid this using magic __set functions and catch all undefined variables in a container (= array, like in my blog post) and change the call_user_func behaviour to call only inside the array:
// inside class:
public $members = array();
public function __call($func, $args) {
// note the difference of calling only inside members:
return call_user_func($this->members[$func], $args);
}
__call
This will work.
class Foo {
public $bar;
public function __construct()
{
$this->bar = function()
{
echo 'closure called';
};
$this->bar();
}
public function __call($method, $args) {
return call_user_func($this->$method, $args);
}
}
new Foo();
The function IS being created.
PHP has a problem with calling it.
Dirty, but works:
$f = $mine->bar;
$f();
I have a problem which is probably not for most of you.
Sorry if it is obvious for you...
This is my code :
class Bat
{
public function test()
{
echo"ici";
exit();
}
public function test2()
{
$this->test();
}
}
In my controller:
bat::test2();
i have an error:
Exception information: Message: Method "test" does not exist and was
not trapped in __call()
Bat::test2 refers to a static function. So you have to declare it static.
class Bat
{
public static function test()
{
echo"ici";
exit();
}
// You can call me from outside using 'Bar::test2()'
public static function test2()
{
// Call the static function 'test' in our own class
// $this is not defined as we are not in an instance context, but in a class context
self::test();
}
}
Bat::test2();
Else, you need an instance of Bat and call the function on that instance:
$myBat = new Bat();
$myBat->test2();
I have a PHP class that looks like this:
class userAuthentication
{
public static function Authenticate()
{
if (isset($_COOKIE['email']))
{
verify($someVar, getPass($_COOKIE['email']);
}
}
public static function getPass($email)
{
}
public static function verify()
{
}
}
At times (I can't pin-point exactly when), I get a fatal error :
Call to undefined function getPass()
The error is thrown at the line where I call the function in the above code sample. Why is this happening when the function clearly exists.
It's a static function in a class. Use self::getPass() or static::getPass() if you want to take advantage of Late Static Binding. Same goes for verify().
verify is not a global function, but only valid in the scope of your class. You want
self::verify($someVar, getPass($_COOKIE['email']);
I would assume the error occurs when you try to run getPass($_COOKIE... since you're calling it wrong. Since the function is a class method, you have to run it like this:
$this->getPass(...);
or if you're calling it statically:
self::getPass(...);
Making your code:
class userAuthentication
{
public static function Authenticate()
{
if (isset($_COOKIE['email']))
{
self::verify($someVar, self::getPass($_COOKIE['email']);
// Or...
$this->verify($someVar, $this->getPass($_COOKIE['email']);
}
}
public static function getPass($email)
{
}
public static function verify()
{
}
}
You're not calling it as a static function.
From within the class use either:
self::getPass($email);
or (for late static binding):
static::getPass($email);
and from outside the class:
userAuthentication::getPass($email);
The line should probably be:
self::verify($someVar, self::getPass($_COOKIE['email']);