Callback function not found while it is defined above - php

I have this code snippet that is supposed to find the differences between two arrays of feed items:
protected function execute()
{
$existingFeedItems = $feed->getItems();
$newFeedItems = $feed->loadItems();
function compareFeedItemIds($feedItem1, $feedItem2)
{
return $feedItem1->getFeedItemId() == $feedItem2->getFeedItemId() ? 0 : -1;
}
$feedItemsAdded = array_udiff($newFeedItems, $existingFeedItems, "compareFeedItemIds");
$feedItemsRemoved = array_udiff($existingFeedItems, $newFeedItems, "compareFeedItemIds");
$unchangedFeedItems = array_uintersect($newFeedItems, $existingFeedItems, "compareFeedItemIds");
}
This will throw the error:
Warning: array_udiff() expects parameter 3 to be a valid callback,
function 'compareFeedItemIds' not found or invalid function name
Even though I have defined that function above. What is the reason for PHP throwing this error? I have to add I am executing this from an object's method context.

If your callback function is defined within a namespace, then you need to indicate that namespace when you make the udiff() call.
$feedItemsAdded = array_udiff($newFeedItems, $existingFeedItems, "namespace\\compareFeedItemIds");
Otherwise PHP will search for the callback function in the global namespace

Let's assume having a local function definition and/or lambda function really is a good idea (it get's way less attractive if you have the same function definiton a couple of times scattered over your project ....).
You can define a function within another function/method and this defintion only takes place when execution of the script(s) reaches this code. But: The function definition isn't local; it bubbles up - outside of the function/class. And because of that you will get a "cannot redeclare function compareFeedItemIds" error if you execute execute() more than once.
There are several options to "fix" that.
You can assign the function to a local variable and then pass that variable as the third parameter to the array_* functions.
$compareFeedItemIds = function($feedItem1, $feedItem2) {
return $feedItem1->getFeedItemId() == $feedItem2->getFeedItemId() ? 0 : -1;
};
$feedItemsAdded = array_udiff($newFeedItems, $existingFeedItems, $compareFeedItemIds);
$feedItemsRemoved = array_diff($existingFeedItems, $newFeedItems, $compareFeedItemIds);
....
You can also store that function in an instance variable ...or a static class member.
Or... to avoid the namspace problem, just create a static method in your class and then reference that method via self::methodname or static::methodname like e.g.
<?php
class Foo {
public function bar() {
$a = [1,2,3];
$b = [2,3,4];
var_export( array_udiff($a, $b, 'self::moo') );
var_export( array_udiff($b, $a, 'self::moo') );
var_export( array_uintersect($a, $b, 'self::moo') );
}
protected static function moo($a,$b) {
return $a<=>$b;
}
}
$foo = new Foo;
$foo->bar();

Related

Calling member function on array

I don't truly understand how chaining functions work on the values that are returned.
Let's say I have a function that returns a string or array
public static $query;
public static function getArray($arr) {
Database::$query = $arr;
return Database::$query;
}
public function single() {
return Database::$query[0];
}
Why, when I call it can I then not chain a function onto this to affect the string (In this example I was to append ' test' and how would I go about doing this?
Why can I simply not call
Database::getArray(array("test","test2"))->single();
Without getting a Call to a member function single() on array error. But instead, make it return only the first value of the array.
How would I go append doing what I'm trying to achieve here? Why is my logic wrong?
When you call a method, the return value is whatever that method decides to return; the return value doesn't have any automatic relationship with the object you called the method on. For instance:
class A {
public function foo() {
return 'Hello, World!';
}
}
$a = new A;
echo $a->foo();
The value returned is just an ordinary string, just as if foo was a global function not attached to any object.
In PHP, strings (and other "basic types" like arrays) are not objects, so you can't call any methods on them. Even if you could, those methods would be built into the language, and you couldn't just decide that ->single() could be called on any array.
What may be confusing is that some people write methods with the convention that they return an object, known as a "fluent interface", or more generally "chained methods". This is not a feature of the language, just a natural consequence of returning an object from a method:
class A {
public function foo() {
return new B;
}
}
class B {
public function bar() {
return 'Hello, World!';
}
}
$a = new A;
$b = $a->foo(); // $b is a B object
echo $b->bar();
// We can combine this into one line:
echo (new A)->foo()->bar();
There is nothing special about this chaining; it's just that wherever you have an object, you can call appropriate methods on it, just as wherever you have a number, you can do maths with it. Compare with a simple addition:
function foo() {
return 1;
}
$a = foo();
$a = $a + 2;
echo $a;
// We can combine this into one line:
echo foo() + 2;
// Or keep the assignment:
$a = foo() + 2;
echo $a;
The object doesn't know it's being chained - in fact, it shouldn't need to know anything about the code around it, and that's an important part of structured programming.
A common pattern is then to have modifying methods which return the object they just modified, so you can make a series of modifications in one go:
class A {
private $words = [];
public function addWord($word) {
$this->words[] = $word;
// $this is the current object, which is an instance of class A
return $this;
}
public function getString() {
return implode(' ', $this->words);
}
}
$a = new A;
// Calling $a->addWord(...) gives us back the same object
$a = $a->addWord('Hello');
$a = $a->addWord('World');
// Calling $a->getString() gives us back a string
echo $a->getString();
// We can combine this into one line:
echo (new A)->addWord('Hello')->addWord('World')->getString();
Note that you can only refer to $this if you have created an instance of the object (with the new keyword), not in a method declared as static. A static method can still have this kind of pattern, but it will need to return some other object, like new self (a new instance of the current class) or self::$foo (an object created earlier).
it's called fluent interface, if you want to chain methods from same class you have to return this from each of them which you want to call fluently, so your code should look like:
public static $query;
public function getArray($arr) {
Database::$query = $arr;
return $this;
}
public function single() {
return Database::$query[0];
}
after applying changes, the construct Database::getArray(array("test","test2"))->single(); will work, however you may consider renaming method getArray, because as its name suggests, it shouldn't be returning $this, but array
#EDIT
you should change the type of function getArray from public static function to public function to make it work, also your final statement will change to something like:
(new Database())->getArray(array("test","test2"))->single();
however, in this case, I would consider redesigning your class and creating some kind of singleton so that you instantiate Database class only once and store the object somewhere

use() statement on anonymous functions

Is it a bad practice to use it?
Because people say that global variables are bad practice and the use thing brings variables from outside into the functions, so it's like global
This is how it looks
$a = 1;
$func = function() use($a){
print $a;
};
Any arguments defined in the "use" arguments for an anonymous function use the value at the time when the anonymous function is defined; so they must exist at that point; but they don't need to be passed (or even exist in the caller scope) when the function is called.
function myFunctionCreator() {
$a = 1; // Must exist for the `use` clause
$func = function() use($a){
echo $a, PHP_EOL;
};
return $func;
}
$myFunc = myFunctionCreator();
$a = 2;
$myFunc(); // echoes 1 (value of $a at the point where the function was created)
As you can see from the above example, $a has a value of 1 at the point where the function is defined, and even though a variable with the same name exists at the point when the function called, it is the original $a (with the value 1) that is used in the function call.
Arguments defined in the main argument definition need not exist when the function is defined, but the values must be passed as arguments to the function at the point when it is called.
function myFunctionCreator() {
$a = 1; // Need not exist, and will be ignored
$func = function($a) {
echo $a, PHP_EOL;
};
return $func;
}
$myFunc = myFunctionCreator();
$value = 2;
$myFunc($value); // echoes 2 (value of $a explicitly passed to the function call
// at the time it is executed)
So the behaviour of the two types is quite different, and their purpose when combined provides a degree of flexibility that is quite different
As Rizier123 has mentioned in his comment, arguments passed to an anonymous function as "standard" can have defaults, typehints, etc, whereas "use" arguments cannot.
function myFunctionCreator() {
$func = function(array $dataset = [1,2,3]) {
foreach($dataset as $value) {
echo $value, PHP_EOL;
}
};
return $func;
}
$myFunc = myFunctionCreator();
$value = ['a','b','c'];
$myFunc($value);
$myFunc();
$myFunc(['x','y','z']);
Or (as the third call shows, arguments can be passed directly.
Andy of these applied to a "use" argument will result in a parse error

php Slim framework - passing parameter to closure

I am trying to study the code for Slim framework. In the constructor for Slim class, $c is passed to the closure (for instance, when storing/setting the request/response object in the container):
public function __construct(array $userSettings = array())
{
// Setup IoC container
$this->container = new \Slim\Helper\Set();
$this->container['settings'] = array_merge(static::getDefaultSettings(), $userSettings);
// Default request
$this->container->singleton('request', function ($c) {
return new \Slim\Http\Request($c['environment']);
});
// Default response
$this->container->singleton('response', function ($c) {
return new \Slim\Http\Response();
});
But $c is not defined/declared anywhere prior to this statement, so how does it work? I started to trace everything from the beginning and I can't find $c anywhere prior to it being used in this manner.
$c is a parameter of the closure function. Imagine you had a function by itself:
function myFunction($c) {
echo $c;
}
In the case of a closure, you can store an anonymous function in a variable:
$someFunction = function ($c) {
echo $c;
}
$someFunction("hello world");
So instead of directly storing the closure into the variable, the code above is passing the anonymous function as a parameter to $this->container->singleton(). So $c is not created until the closure is called. The singleton method stores this in a variable called $value, so if that function ran:
$value(array('environment'=>'test'));
$c would now contain array('environment'=>'test')
Slim also uses the __get() __set() magic methods quite a bit, so from the example code you set, within the Slim class, one could call:
$request = $this->container->request(array('environment'=>'test'));
The container is of class Slim\Helper\Set. Since this doesn't have a request method, this would call the container's __get() method. It would look up the stored method configured above for 'request' and pass the array in as $c
I think khartnett gave a perfect answer. To make it clearer for you, an example.
When you define a function, you are only describing how it works. You are not setting any specific values. For example, when I write:
function sum($a, $b) {
return $a + $b;
}
I am not saying what the values of $a and $b are here. I am just describing what I am doing with these variables to calculate a result. It is not until I call this function that I'm working with actual values:
sum(3, 4); // returns 7
In your question, the $c variable is like the $a and $b variable.
Like khartnett showed in his answer, it works like this:
// Definition time
$someFunction = function ($c) {
echo $c;
}
// Calling time
$someFunction("hello world");
It is not until calling time that $c gets its value (in this example, the value is "hello world").
The $c is a reference to the container itself - so that any dependencies will be automatically resolved when invoked.
So using for example the request object:
// Default request
$this->container->singleton('request', function ($c) {
return new \Slim\Http\Request($c['environment']);
});
Looking at the Request constructor you will see that it expects an instance of Environment, which we just told the container should be available already using the key 'environment'.
The answer will lay in $app->run():
$this->container->get("service_name") then in get() method offsetGet($id)
public function offsetGet($id)
{
if (!isset($this->keys[$id])) {
throw new UnknownIdentifierException($id);
}
if (
isset($this->raw[$id])
|| !\is_object($this->values[$id])
|| isset($this->protected[$this->values[$id]])
|| !\method_exists($this->values[$id], '__invoke')
) {
return $this->values[$id];
}
if (isset($this->factories[$this->values[$id]])) {
return $this->values[$id]($this);
}
$raw = $this->values[$id];
$val = $this->values[$id] = $raw($this); // THIS LINE CALLS THE CONTAINER ITSELF
$this->raw[$id] = $raw;
$this->frozen[$id] = true;
return $val;
}
Pimple Container functioning it's a bit more complicated due to the fact:
Allowing any PHP callable leads to difficult to debug problems
as function names (strings) are callable (creating a function with
the same name as an existing parameter would break your container)
So unique identifiers are introduced.
Here is a simple implementation of ServiceContainer get() method inside the ServiceContainer class:
public function get($serviceName)
{
if (!array_key_exists($serviceName, $this->container)) {
throw new \http\Exception\InvalidArgumentException("Service not found!");
}
$service = $this->container[$serviceName];
if (is_callable($service)) {
$this->container[$serviceName] = $service = $service($this);
// $this (aka ServiceContainer) will be passed as parameter to Closures
}
return $service;
}
Hope it clarifies the question, have a nice day!

How can I use a callback function which is stored as a class member in PHP?

Consider the following code, which is a scheme of storing a callback function as a member, and then using it:
class MyClass {
function __construct($callback) {
$this->callback = $callback;
}
function makeCall() {
return $this->callback();
}
}
function myFunc() {
return 'myFunc was here';
}
$o = new MyClass(myFunc);
echo $o->makeCall();
I would expect myFunc was here to be echoed, but instead I get:
Call to undefined method MyClass::callback()
Can anyone explain what's wrong here, and what I can do in order to get the desired behaviour?
In case it matters, I am using PHP 5.3.13.
You can change your makeCall method to this:
function makeCall() {
$func = $this->callback;
return $func();
}
Pass it as a string and call it by call_user_func.
class MyClass {
function __construct($callback) {
$this->callback = $callback;
}
function makeCall() {
return call_user_func($this->callback);
}
}
function myFunc() {
return 'myFunc was here';
}
$o = new MyClass("myFunc");
echo $o->makeCall();
One important thing about PHP is that it recognises the type of a symbol with the syntax rather than the contents of it, so you need to state explicitly what you refer to.
In many languages you just write:
myVariable
myFunction
myConstant
myClass
myClass.myStaticMethod
myObject.myMethod
And the parser/compiler knows what each of the symbols means, because it's aware of what they refer to simply by knowing what's assigned to them.
In PHP, however, you need to use the syntax to let the parser know what "symbol namespace" you refer to, so normally you write:
$myVariable
myFunction()
myConstant
new myClass
myClass::myStaticMethod()
$myObject->method()
However, as you can see these are calls rather than references. To pass a reference to a function, class or method in PHP, combined string and array syntax is used:
'myFunction'
array('myClass', 'myStaticMethod')
array($myObject, 'myMethod')
In your case, you need to use 'myFunc' in place of myFunc to let PHP know that you're passing a reference to a function and not retrieving the value the myFunc constant.
Another ramification is that when you write $myObject->callback(), PHP assumes callback is a method because of the parentheses and it does not attempt to loop up a property.
To achieve the expected result, you need to either store a copy of/reference to the property callback in a local variable and use the following syntax:
$callback = $this->callback;
return $callback();
which identifies it as a closure, because of the dollar sign and the parentheses; or call it with the call_user_func function:
call_user_func($this->callback);
which, on the other hand, is a built-in function that expects callback.

PHP5.3: "Call to undefined method" error when calling invoke from class variable

I have been doing some tests (to replace old code) with the __invoke magic method and I'm not sure this is a bug or not:
Lets suppose we have a class:
class Calc {
function __invoke($a,$b){
return $a*$b;
}
}
The following is possible and works without any problem:
$c = new Calc;
$k = $c;
echo $k(4,5); //outputs 20
However if I want to have another class to store an instance of that object,
this doesn't work:
class Test {
public $k;
function __construct() {
$c = new Calc;
$this->k = $c; //Just to show a similar situation than before
// $this-k = new Calc; produces the same error.
}
}
The error occurs when we try to call it like:
$t = new Test;
echo $t->k(4,5); //Error: Call to undefined method Test::k()
I know that a "solution" could be to have a function inside the class Test (named k) to "forward" the call using call_user_func_array but that is not elegant.
I need to keep that instance inside a common class (for design purposes) and be able to call it as function from other classes... any suggestion?
Update:
I found something interesting (at least for my purposes):
If we assign the "class variable" into a local variable it works:
$t = new Test;
$m = $t->k;
echo $m(4,5);
PHP thinks you want to call a method k on instance $t when you do:
$t->k(4, 5)
which is perfectly reasonable. You can use an intermediate variable to call the object:
$b = $t->k;
$b(4, 5);
See also bug #50029, which describes your issue.
When you do $test->k(), PHP thinks you are calling a method on the $test instance. Since there is no method named k(), PHP throws an exception. What you are trying to do is make PHP return the public property k and invoke that, but to do so you have to assign k to a variable first. It's a matter of dereferencing.
You could add the magic __call method to your Test class to check if there is a property with the called method name and invoke that instead though:
public function __call($method, $args) {
if(property_exists($this, $method)) {
$prop = $this->$method;
return $prop();
}
}
I leave adding the arguments to the invocation to you.
You might also want to check if the property is_callable.
But anyway, then you can do
$test->k();
You can not use method syntax (like $foo->bar() ) to call closures or objects with __invoke, since the engine always thinks this is a method call. You could simulate it through __call:
function __call($name, $params) {
if(is_callable($this->$name)) {
call_user_func_array($this->$name, $params);
}
}
but it would not work as-is.
If you call $test->k() PHP will search for a method called "k" on the $test instance and obviously it will throws an Exception.
To resolve this problem you can create a getter of the property "k"
class Test {
public $k;
function __construct() {
$c = new Calc;
$this->k = $c; //Just to show a similar situation than before
// $this-k = new Calc; produces the same error.
}
public function getK() {
return $this->k;
}
}
So now you can use the functor in this way:
$t = new Test();
echo $t->getK()(4,5);

Categories