Using $this when not in object context ... stack issue? - php

I'm getting the error Using $this when not in object context when using the code below. Is this because the function is on the stack and has no access to the object?
array_walk($pins, function (&$array) {
$array->timestamp = $this->convertTime(strtotime($array->timestamp));
});
What's the best way to get around this? I was thinking of using a foreach, but wanted to learn more of PHP's lesser used functions that suit the purpose.
Solved it with
foreach($pins as $pin) {
$pin->timestamp = $this->convertTime(strtotime($pin->timestamp));
}
But I would still like to know how to get around the issue with array_walk.

try this:
$that =& $this;
array_walk($pins, function (&$array) use ($that) {
$array->timestamp = $that->convertTime(strtotime($array->timestamp));
});
Also, if you are actually walking an array, the correct syntax will probably be:
$that =& $this;
array_walk($pins, function (&$array) use ($that) {
$array['timestamp'] = $that->convertTime(strtotime($array['timestamp']));
});

What's the best way to get around this? I was thinking of using a foreach, but wanted to learn more of PHP's lesser used functions that suit the purpose.
I would say for your case, foreach is a very good solution because it's clear what it does. You might want to mimic the aliasing array_walk offers with your example:
foreach($pins as &$pin) {
$pin->timestamp = $this->convertTime(strtotime($pin->timestamp));
}
unset($pin);
This should come more closely to array_walk. Probably you want to reference the array itself as well (probably):
$pinsRef = &$pins;
foreach($pinsRef as &$pin) {
$pin->timestamp = $this->convertTime(strtotime($pin->timestamp));
}
unset($pin);
If you want to encapsulate the object itself ($this) inside an anonymous function with private access, this is not possible. Because there does not exists any $this in the context of the anonymous function (yet, a later PHP version might have that).
However one workaround is to pass the object instance into the local scope of the anonymous function by making use of the use clause.
$_this = $this;
$callback = function (&$array) use ($_this) {
$array->timestamp = $_this->convertTime(strtotime($array->timestamp));
});
array_walk($pins, $callback);
But this workaround is limited, as private members of $this are not accessible. Instead you can create the callback function as part of your object and specify it, private access is possible then and you can invoke it like any other object callback:
array_walk($pins, array($this, 'callback'));
Which brings me back to the beginning saying that foreach is probably the better weapon of choice than array_walk in your case.
I hope this is helpful to show you the different alternatives.

The reason is that you're passing a plain function which is not part of an object. As there is no object, there's nothing $thiscould point to. If convertTime is in global scope, simply omit $this->. Othwewise, consider passing an object as a callback to array_walk.

As of PHP 5.4.0, you can access $this from inside of anonymous functions.

Related

Closure::fromCallable for imported function

I'm playing with PHP and some functional style programming.
I'm using the Functional-PHP library but question is generic to PHP (I'm using 7.2).
I try to create a callable from an imported function but what I get is
TypeError: Failed to create closure from callable: function 'pick' not found or invalid function name
Sample code:
use function Functional\pick;
class A
{
public function execute()
{
$pick1 = \Closure::fromCallable('pick');
}
}
PHP use statements define an alias for the rest of the file, but they won't affect a string referencing an imported function or class.
When you say
use function Functional\pick;
it means that in that file, you can call the Functional\pick function just using pick(...). But if you're using a string to reference it then PHP doesn't know to expand the alias.
The quickest way to resolve this is just to use the fully qualified function name when calling fromCallable:
$pick1 = \Closure::fromCallable('Functional\pick');
echo get_class($pick1);
Closure
Alternatively, if you really wanted to use the alias, you could wrap the call a level deeper with another anonymous function:
use function Functional\pick;
$pick1 = \Closure::fromCallable(function (...$args) { return pick(...$args); });
But that's a lot messier, in my opinion at least.
Edit: There's some decent discussion around this in this recent thread in php-externals

How to code/reference to a PHP callable functions easy to manage for my IDE

When I have to write a reference to a callable function I use the standard syntax of PHP defined as:
A PHP function is passed by its name as a string. Any built-in or user-defined function can be used [... omitted...].
A method of an instantiated object is passed as an array containing an object at index 0 and the method name (aka string)
at index 1.
Static class methods can also be passed without instantiating an object of that class by passing the class name (still a string)
instead of an object at index 0.
As of PHP 5.2.3, it is also possible to pass (the string) 'ClassName::methodName'.
Apart from common user-defined function, anonymous functions can also be passed to a callback parameter.
All of these ways are not "IDE friendly" for operations like function name refactor or find usage of.
In my answer I propose a solution, but there are other approaches that can be applied, even totally different, that allow to IDE to "find" the invocation of the methods?
You already are next to the shortest thing you can do
You can perfectly call your anonymous function directly in your function call without using a variable
For instance, you can replace:
$callable=function($param) use ($object){
return $object->myMethod($param);
}
call_user_func($callable, $param);
by:
call_user_func(function($param) use ($object){
return $object->myMethod($param);
}, $param);
You will have to wait for arrow functions in future PHP versions, and you should be able to use something like:
call_user_func(fn($a) => $object->myMethod($a), $param);
I became to a solution, always based on the anonymous-functions that solve the problem but leave me not full satisfied:
Static method of a class
$callable = function($param){
return \my\namespace\myClass::myMethod($param);
}
method of a object
$callable = function($param) use ($object){
return $object->myMethod($param);
}
method of $this object
$callable = function($param){
return self::myMethod($param);
}
Alternatives for oldest php versions
Inside the all classess you gonna call (or in their parents) define the function classname() as follow:
public static function className()
{
return get_called_class();
}
or for very old PHP:
public static function className()
{
return "MyClass";
}
Then
call_user_func(array(MyClass::className(), 'myCallbackMethod'));

PHP - is there a way to save an already defind function in a variable?

I am interested in something google couldn't really help me with...
I know that its possible to use anonymous functions and also store functions in a variable in PHP like that
$function = function myFoo() { echo "bar"; }
and call it using the variable: $function();
So far so good but what if I have a function or method declared somewhere but not saved on intializing?
I have a function that shall expect a callable, of course call_user_func() can help me here but then I need to pass the method name in my callback handler which I think is pretty unsafe because in the moment I add the callback I cant say if it really is a function and exists when I store it.
Thatswhy I would like to realize the following szenario:
This function:
function anyFunction() {
echo "doWhatever...!";
}
should be saved in a variable at a later point in time:
$myOtherFunction = get_registered_func("anyFunction");
I know get_registered_func() doesnt exist but I want to know if this is possible somehow!
With this I could now have another function
function setCallback(callable $theCallbackFunction) { }
And use it like this:
setCallback($myOtherFunction);
This would have a great advantage that an exception / a fatal is thrown when the parameter is no function or does not exist.
So in short, is there a way to store a previously defined, already existing function or method in a variable?
PHP's callable meta type can be:
a string
an array
an anonymous function
Only the latter one is somewhat "type safe", i.e. if you get an anonymous function you know it's something you can call. The other two options are merely formalised informal standards (if that makes sense) which are supported by a few functions that accept callbacks; they're not actually a type in PHP's type system. Therefore there's basically no guarantee you can make about them.
You can only work around this by checking whether the callable you got is, well, callable using is_callable before you execute them. You could wrap this into your own class, for example, to actually create a real callable type.
I see no reason why this shouldn't be possible, other than there not being a PHP function to do it. The anonymous function syntax is newly introduced in PHP, I wouldn'be surprised if it was still a little rough around the edges.
You can always wrap it:
function wrap_function ($callback) {
if (is_callable($callback) === false) {
throw new Exception("nope");
}
return function () {
call_user_func($callback, func_get_args());
}
}
$foo = new Foo();
$ref = wrap_function(array($foo, "bar"));
Not a good way, but maybe this helps you:
function foo($a) {
echo $a;
}
$callFoo = 'foo';
$callFoo('Hello World!'); // prints Hello

Have $this in PHP implicit function before PHP 5.4.0

According to http://php.net/manual/en/functions.anonymous.php, in PHP 5.3 $this is not accessible from inside an implicit function, even if the function is defined in a context where $this exists. Is there any way to work around this limitation? (by the way, upgrading the PHP installation on the webserver is not possible)
The way I would like to use the implicit function is to define a callback which is a member function of some object. More precisely, I would like to do something like
$callback = function() { return $this->my_callback(); }
Actually, an event better syntax would be
$callback = $this->my_callback
but I cannot make it work (PHP dies with "Fatal error: Function name must be a string" when I try to execute the callback).
Should do the work:
$object = $this ;
$callback = function() use ($object) { return $object->my_callback(); } ;
use will bring an accessible variable (in our case the reference of the object) upon its declaration to the function scope, so you will not have to send it as a parameter.
Sometimes it is even better to use such a varname as $self or $that so to be more clear.
$function = array($this, 'my_callback');
(perhaps combined with call_user_func() )
It looks like you can pass variables to the callback function. I haven't worked with closures in PHP, but I think this would work for you:
$callback = function($instance) { return $instance->my_callback(); }
$callback($this);
Or if the callback is triggered outside the current class.
$callback($myClassInstance);

Would it be better to use closures for event handling instead of eval()?

I'm making an event handler for a class, but I was wondering if it would be better to use closures instead of evaluation of code?
My only reason for using eval() is simply because it is able to access everything within the class (and it's really, really unsafe :D), but I don't know if closures can.
if I did something like this:
<?php
class SomethingCool {
protected $handlers;
public function addHandler($cmd, closure $func) {
$this->handlers[$cmd][] = $func;
}
public function handle($cmd) {
if(!isset($this->handlers[$cmd]))
return false;
foreach($this->handlers[$cmd] as $func)
$func();
}
}
?>
<?php
$wut = new SomethingCool();
$wut->addHandler('lol', function() use($wut) {
$wut->handle('lol');
}
);
?>
Would it execute without error?
I would test it myself, but I'm unable to at the moment.
If you write handlers using eval, you'll end up writing code like this:
$wut->addHandler('lol', '$this->handle(\'lol\');');
Apart from the obviously terrible problems with escaping quotes and broken syntax highlighting in your editor, this introduces the problem of ambiguous dependencies. What does $this refer to in your code? It does not work literally as is in the code, it depends on being evaluated within a certain context. This makes code a real mess.
The alternative is dependency injection:
$wut->addHandler('lol', function (SomethingCool $sc) {
$sc->handle('lol');
});
When calling this handler, SomethingCool will inject itself as a function argument. That's a lot more robust. It means you can hand this callback around to other contexts and do whatever you want behind the scenes, the callback does not depend on being evaluated within a certain context anymore.
Alternatively, use a closure:
$wut->addHandler('lol', function () use ($wut) {
$wut->handle('lol');
});
This has the same benefit of you being sure where your dependency comes from and knowing that you can depend on it.
So yes, anything is better than eval.
Why not just pass the instance of your SomethingCool to each handler?
public function handle($cmd)
{
if (!isset($this->handlers[$cmd])) {
return;
}
foreach ($this->handlers[$cmd] as $func)
$func($this); // pass ourself to each handler
}
}
$wut->addHandler('lol', function(SomethingCool $obj) {
// $obj refers to the SomethingCool instance
});
$wut->handle('lol');
Btw, if you also want to be able to remove handlers, you could also use SplObjectStorage for each command category.

Categories