I'm trying to run a method on each element inside a collection. It's an object method residing in the same class:
protected function doSomething()
{
$discoveries = $this->findSomething();
$discoveries->each([$this, 'doSomethingElse']);
}
protected function doSomethingElse($element)
{
$element->bar();
// And some more
}
If I precede the call on Collection::each with the check is_callable([$this, 'doSomethingElse']) it returns true, so apparently it is callable. The call itself however throws an exception:
Type error: Argument 1 passed to Illuminate\Support\Collection::each() must
be callable, array given, called in ---.php on line 46
The method trying to be called can be found here.
I'm bypassing this by just passing a closure that itself simply calls that function, but this would definitely a much cleaner solution and I can't find out why it throws the error.
Change the visibility of your callback method to public.
protected function doSomething()
{
$discoveries = $this->findSomething();
$discoveries->each([$this, 'doSomethingElse']);
}
public function doSomethingElse($element)
{
$element->bar();
// And some more
}
Since PHP 7.1 you can leave your function protected. Now you can write:
protected function doSomething()
{
$discoveries = $this->findSomething();
$discoveries->each(\Closure::fromCallable([$this, 'doSomethingElse']));
}
protected function doSomethingElse($element)
{
$element->bar();
// And some more
}
Source
function with callback
public function foo(callable $callback){
$callback('something')
}
named function for callback
public function do($something) {
//
}
result
foo(function ($something) {
//
})
or call named function
foo(\Closure::fromCallable([$this, 'do']))
or
foo(this->do(...))
PHP >= 5.4
I wasn't able to reproduce your error, but my guess is that you should use $discoveries instead of $this in the callback array, like so:
$discoveries->each([$discoveries, 'doSomethingElse']);
Even though $discoveries and $this are of the same class, and therefore can access each other's protected and private methods, the type-hinting functionality may not check that the object in the callback array is the same class as the current class. However, the is_callable() method will check for this, which may explain why it returns true when you call it from inside the each() method.
PHP < 5.4
There is no type named callable, so when you use it as a type hint, it is referring to a class named callable. See this answer.
Related
I have a class with methods that I want to use as callbacks.
How can I pass them as arguments?
Class MyClass {
public function myMethod() {
// How should these be called?
$this->processSomething(this->myCallback);
$this->processSomething(self::myStaticCallback);
}
private function processSomething(callable $callback) {
// Process something...
$callback();
}
private function myCallback() {
// Do something...
}
private static function myStaticCallback() {
// Do something...
}
}
Check the callable manual to see all the different ways to pass a function as a callback. I copied that manual here and added some examples of each approach based on your scenario.
Callable
A PHP function is passed by its name as a string. Any built-in or user-defined function can be used, except language constructs such as: array(), echo, empty(), eval(), exit(), isset(), list(), print or unset().
// Not applicable in your scenario
$this->processSomething('some_global_php_function');
A method of an instantiated object is passed as an array containing an object at index 0 and the method name at index 1.
// Only from inside the same class
$this->processSomething([$this, 'myCallback']);
$this->processSomething([$this, 'myStaticCallback']);
// From either inside or outside the same class
$myObject->processSomething([new MyClass(), 'myCallback']);
$myObject->processSomething([new MyClass(), 'myStaticCallback']);
Static class methods can also be passed without instantiating an object of that class by passing the class name instead of an object at index 0.
// Only from inside the same class
$this->processSomething([__CLASS__, 'myStaticCallback']);
// From either inside or outside the same class
$myObject->processSomething(['\Namespace\MyClass', 'myStaticCallback']);
$myObject->processSomething(['\Namespace\MyClass::myStaticCallback']); // PHP 5.2.3+
$myObject->processSomething([MyClass::class, 'myStaticCallback']); // PHP 5.5.0+
Apart from common user-defined function, anonymous functions can also be passed to a callback parameter.
// Not applicable in your scenario unless you modify the structure
$this->processSomething(function() {
// process something directly here...
});
As of PHP 8.1, we now have first-class callables. They use the syntax $callable = functionName(...). The three dots are part of the syntax and not an omission.
You can use the new syntax to create callable class methods.
Class MyClass {
public function myMethod() {
// first-class callables
$this->processSomething($this->myCallback(...));
$this->processSomething(self::myStaticCallback(...));
}
private function processSomething(callable $callback) {
// Process something...
$callback();
}
private function myCallback() {
// Do something...
}
private static function myStaticCallback() {
// Do something...
}
}
The three dots are not an omission/placeholder for parameters. They are a special syntax for creating a callable. If the method accepts no parameters, the syntax remains the same.
Since 5.3 there is a more elegant way you can write it, I'm still trying to find out if it can be reduced more
$this->processSomething(function() {
$this->myCallback();
});
You can also to use call_user_func() to specify a callback:
public function myMethod() {
call_user_func(array($this, 'myCallback'));
}
private function myCallback() {
// do something...
}
You can set the method return type to callable. It works for PHP 7.1
protected function myMethod(): callable
{
return function (int $j) {
};
}
Then call it like this:
someFunction($this->myMethod());
I have two class A and B
I want to 'send' a method from A to B via B constructor and then execute it in B. I've been trying to work with anonymous functions just like this :
class A
{
public function __construct()
{
// Send testMethod() to B with an anonymous function
new B(function (string $test) {
$this->testMethod($test);
});
}
protected function testMethod(string $test) {
echo ($test);
}
}
class B
{
protected $testFct;
public function __construct($fctToExecute)
{
// Asign anonymous function to $testFct to be executed in all object
$this->testFct= function (string $test) {
$fctToExecute($test);
};
}
// Want to be able now to call this $testFct function in a method like :
protected function methodWhereICallTestfct() {
$this->testFct("I'm dumb!"); // It has to echo "I'm dumb!"
}
}
But when I try to set it up I get always an error like :
Uncaught Error: Call to undefined method testFct()
Do you have any idea what the problem is? I would like to specify that my php version is PHP 7.1.3.
EDIT :
Here is it possible to see a more similare code as mine which return the error
You have got 2 mistakes in your code.
First mistake is that you ignore the parameter $fctToExecute in B::__construct(). If you would like to save the passed closure into a property of object B then you do not need another closure.
public function __construct(closure $fctToExecute) {
// Asign anonymous function to $testFct to be executed in all object
$this->testFct = $fctToExecute;
}
The second problem is that when you are trying to execute the closure you are actually trying to execute a function called testFct. You should use parentheses to declare precedence of operations.
$this->testFct("I'm dumb!"); // This looks for a function called testFct
($this->testFct)("I'm dumb!"); // This will execute the closure stored in the property called $testFct
The parentheses make a lot of difference here.
I have a class with methods that I want to use as callbacks.
How can I pass them as arguments?
Class MyClass {
public function myMethod() {
// How should these be called?
$this->processSomething(this->myCallback);
$this->processSomething(self::myStaticCallback);
}
private function processSomething(callable $callback) {
// Process something...
$callback();
}
private function myCallback() {
// Do something...
}
private static function myStaticCallback() {
// Do something...
}
}
Check the callable manual to see all the different ways to pass a function as a callback. I copied that manual here and added some examples of each approach based on your scenario.
Callable
A PHP function is passed by its name as a string. Any built-in or user-defined function can be used, except language constructs such as: array(), echo, empty(), eval(), exit(), isset(), list(), print or unset().
// Not applicable in your scenario
$this->processSomething('some_global_php_function');
A method of an instantiated object is passed as an array containing an object at index 0 and the method name at index 1.
// Only from inside the same class
$this->processSomething([$this, 'myCallback']);
$this->processSomething([$this, 'myStaticCallback']);
// From either inside or outside the same class
$myObject->processSomething([new MyClass(), 'myCallback']);
$myObject->processSomething([new MyClass(), 'myStaticCallback']);
Static class methods can also be passed without instantiating an object of that class by passing the class name instead of an object at index 0.
// Only from inside the same class
$this->processSomething([__CLASS__, 'myStaticCallback']);
// From either inside or outside the same class
$myObject->processSomething(['\Namespace\MyClass', 'myStaticCallback']);
$myObject->processSomething(['\Namespace\MyClass::myStaticCallback']); // PHP 5.2.3+
$myObject->processSomething([MyClass::class, 'myStaticCallback']); // PHP 5.5.0+
Apart from common user-defined function, anonymous functions can also be passed to a callback parameter.
// Not applicable in your scenario unless you modify the structure
$this->processSomething(function() {
// process something directly here...
});
As of PHP 8.1, we now have first-class callables. They use the syntax $callable = functionName(...). The three dots are part of the syntax and not an omission.
You can use the new syntax to create callable class methods.
Class MyClass {
public function myMethod() {
// first-class callables
$this->processSomething($this->myCallback(...));
$this->processSomething(self::myStaticCallback(...));
}
private function processSomething(callable $callback) {
// Process something...
$callback();
}
private function myCallback() {
// Do something...
}
private static function myStaticCallback() {
// Do something...
}
}
The three dots are not an omission/placeholder for parameters. They are a special syntax for creating a callable. If the method accepts no parameters, the syntax remains the same.
Since 5.3 there is a more elegant way you can write it, I'm still trying to find out if it can be reduced more
$this->processSomething(function() {
$this->myCallback();
});
You can also to use call_user_func() to specify a callback:
public function myMethod() {
call_user_func(array($this, 'myCallback'));
}
private function myCallback() {
// do something...
}
You can set the method return type to callable. It works for PHP 7.1
protected function myMethod(): callable
{
return function (int $j) {
};
}
Then call it like this:
someFunction($this->myMethod());
Warning: preg_replace_callback() [function.preg-replace-callback]: Requires argument 2, 'info', to be a valid callback in [...]
public function getDisplay(){
$info = array_merge($this->info,array());
return preg_replace_callback('!\{\{(\w+)\}\}!', 'info', $this->display);
}
In a public function from "MyClass", stopped working when I moved from another class to this one. Which was:
public function getEdit( $type){
$all_info = $this->info;
return preg_replace_callback('!\{\{(\w+)\}\}!', 'all_info', $edit_contents);
}
Both are cleaned up, and now I can't retest with previous class because it's already long gone.
I'm sure using variables is not allowed, but it was working, so I'm clueless.
When I do this, as suggested in some stackoverflow thread, but obviously it's not made to be used within objects:
public function getDisplay(){
$display_info = $this->info;
function display_info($matches) {
global $display_info;
return $display_info[$matches[1]];
}
return preg_replace_callback('!\{\{(\w+)\}\}!', 'display_info', $this->display);
}
So I need some love and guidance cuz php is driving me crazy this week...
Instead of using anonymous functions or more complex methods, you can just use one of methods supported for passing callbacks (see the documentation on callbacks):
A method of an instantiated object is passed as an array containing an object at index 0 and the method name at index 1.
To pass "info" method of current object as a callback, just do the following:
array($this, 'info')
and pass it wherever you want to use "info" method as a callback within one of the objects other methods.
The correct way to do this would be with a closure:
public function getDisplay() {
// While $this support in closures has been recently added (>=5.4.0), it's
// not yet widely available, so we'll get another reference to the current
// instance into $that first:
$that = $this;
// Now we'll write that preg... line of which you speak, with a closure:
return preg_replace_callback('!\{\{(\w+)\}\}!', function($matches) use($that) {
return $that->info[$matches[1]];
}, $this->display);
}
This solved it:
public function getDisplay(){
return preg_replace_callback('!\{\{(\w+)\}\}!', array(get_class($this), '_preg_replace_callback'), $this->display);
}
private function _preg_replace_callback($matches){
return $this->info[$matches[1]];
}
I did try this approach before, but didn't use the get_class() function to wrap $this. Oh bother...
I'm a little confused about the situation shown in this code...
class DirEnt
{
public function PopulateDirectory($path)
{
/*... code ...*/
while ($file = readdir($folder))
{
is_dir($file) ? $dtype = DType::Folder : $dtype = Dtype::File;
$this->push_back(new SomeClass($file, $dtype));
}
/*... code ...*/
}
//Element inserter.
public function push_back($element)
{
//Insert the element.
}
}
Why do I need to use either $this->push_back(new SomeClass($file, $dtype)) or self::push_back(new SomeClass($file, $dtype)) to call the member function push_back? I can't seem to access it just by doing push_back(new SomeClass($file, $dtype)) like I would have expected. I read When to use self over $this? but it didn't answer why I need one of them at all (or if I do at all, maybe I messed something else up).
Why is this specification required when the members are both non-static and in the same class? Shouldn't all member functions be visible and known from other member functions in the same class?
PS: It works fine with $this-> and self:: but says the functions unknown when neither is present on the push_back call.
$this->push_back will call the method as part of the CURRENT object.
self::push_back calls the method as a static, which means you can't use $this within push_back.
push_back() by itself will attempt to call a push-back function from the global scope, not the push_back in your object. It is not an "object call", it's just a plain-jane function call, just as calling printf or is_readable() within an object calls the usual core PHP functions.
I cant seem to access it just by doing push_back(new SomeClass($file, $dtype)) like I would have expected.
This way you call push_back() as a function. There is no way around $this (for object methods) or self::/static:: (for class methods), because it would result into ambiguity
Just remember: PHP is not Java ;)
You can access like this
public static function abc($process_id){
return 1;
}
public static function xyz(){
$myflag=self::abc();
return $myflag;
}
output : 1