Reference to static method in PHP? - php

In PHP, I am able to use a normal function as a variable without problem, but I haven't figured out how to use a static method. Am I just missing the right syntax, or is this not possible?
(EDIT: the first suggested answer does not seem to work. I've extended my example to show the errors returned.)
function foo1($a,$b) { return $a/$b; }
class Bar
{
static function foo2($a,$b) { return $a/$b; }
public function UseReferences()
{
// WORKS FINE:
$fn = foo1;
print $fn(1,1);
// WORKS FINE:
print self::foo2(2,1);
print Bar::foo2(3,1);
// DOES NOT WORK ... error: Undefined class constant 'foo2'
//$fn = self::foo2;
//print $fn(4,1);
// DOES NOT WORK ... error: Call to undefined function self::foo2()
//$fn = 'self::foo2';
//print $fn(5,1);
// DOES NOT WORK ... error: Call to undefined function Bar::foo2()
//$fn = 'Bar::foo2';
//print $fn(5,1);
}
}
$x = new Bar();
$x->UseReferences();
(I am using PHP v5.2.6 -- does the answer change depending on version too?)

PHP handles callbacks as strings, not function pointers. The reason your first test works is because the PHP interpreter assumes foo1 as a string. If you have E_NOTICE level error enabled, you should see proof of that.
"Use of undefined constant foo1 - assumed 'foo1'"
You can't call static methods this way, unfortunately. The scope (class) is relevant so you need to use call_user_func instead.
<?php
function foo1($a,$b) { return $a/$b; }
class Bar
{
public static function foo2($a,$b) { return $a/$b; }
public function UseReferences()
{
$fn = 'foo1';
echo $fn(6,3);
$fn = array( 'self', 'foo2' );
print call_user_func( $fn, 6, 2 );
}
}
$b = new Bar;
$b->UseReferences();

In php 5.2, you can use a variable as the method name in a static call, but to use a variable as the class name, you'll have to use callbacks as described by BaileyP.
However, from php 5.3, you can use a variable as the class name in a static call. So:
class Bar
{
public static function foo2($a,$b) { return $a/$b; }
public function UseReferences()
{
$method = 'foo2';
print Bar::$method(6,2); // works in php 5.2.6
$class = 'Bar';
print $class::$method(6,2); // works in php 5.3
}
}
$b = new Bar;
$b->UseReferences();
?>

You could use the full name of static method, including the namespace.
<?php
function foo($method)
{
return $method('argument');
}
foo('YourClass::staticMethod');
foo('Namespace\YourClass::staticMethod');
The name array array('YourClass', 'staticMethod') is equal to it. But I think the string may be more clear for reading.

In PHP 5.3.0, you could also do the following:
<?php
class Foo {
static function Bar($a, $b) {
if ($a == $b)
return 0;
return ($a < $b) ? -1 : 1;
}
function RBar($a, $b) {
if ($a == $b)
return 0;
return ($a < $b) ? 1 : -1;
}
}
$vals = array(3,2,6,4,1);
$cmpFunc = array('Foo', 'Bar');
usort($vals, $cmpFunc);
// This would also work:
$fooInstance = new Foo();
$cmpFunc = array('fooInstance', 'RBar');
// Or
// $cmpFunc = array('fooInstance', 'Bar');
usort($vals, $cmpFunc);
?>

Coming from a javascript background and being spoiled by it, I just coded this:
function staticFunctionReference($name)
{
return function() use ($name)
{
$className = strstr($name, '::', true);
if (class_exists(__NAMESPACE__."\\$className")) $name = __NAMESPACE__."\\$name";
return call_user_func_array($name, func_get_args());
};
}
To use it:
$foo = staticFunctionReference('Foo::bar');
$foo('some', 'parameters');
It's a function that returns a function that calls the function you wanted to call. Sounds fancy but as you can see in practice it's piece of cake.
Works with namespaces and the returned function should work just like the static method - parameters work the same.

This seems to work for me:
<?php
class Foo{
static function Calc($x,$y){
return $x + $y;
}
public function Test(){
$z = self::Calc(3,4);
echo("z = ".$z);
}
}
$foo = new Foo();
$foo->Test();
?>

In addition to what was said you can also use PHP's reflection capabilities:
class Bar {
public static function foo($foo, $bar) {
return $foo . ' ' . $bar;
}
public function useReferences () {
$method = new ReflectionMethod($this, 'foo');
// Note NULL as the first argument for a static call
$result = $method->invoke(NULL, '123', 'xyz');
}
}

"A member or method declared with static can not be accessed with a variable that is an instance of the object and cannot be re-defined in an extending class"
(http://theserverpages.com/php/manual/en/language.oop5.static.php)

Related

Is it possible to use class as variable in PHP?

I have a class as follows:
class Integer {
private $variable;
public function __construct($variable) {
$this->varaible = $variable;
}
// Works with string only
public function __isString() {
return $this->variable;
}
// Works only, If Im using the class as a function (i must use parenthesis)
public function __invoke() {
return $this->variable;
}
}
$int = new Integer($variable);
I would like work with class as with variable like:
$result = $int + 10;
I donĀ“t known, how can I return $int; ?
PHP does not support overloading operators (which is the technical thing you're looking for). It doesn't know what to do with + when one of the operands is a class Integer, and there's no way to teach PHP what to do. The best you can do is implement appropriate methods:
class Integer {
..
public function add(Integer $int) {
return new Integer($this->variable + $int->variable);
}
}
$a = new Integer(1);
$b = new Integer(2);
echo $a->add($b);
Yes, see Example 4 of php's call_users_func() page;
<?php
class myclass {
static function say_hello()
{
echo "Hello!\n";
}
}
$classname = "myclass";
call_user_func(array($classname, 'say_hello'));
call_user_func($classname .'::say_hello'); // As of 5.2.3
$myobject = new myclass();
call_user_func(array($myobject, 'say_hello'));
?>
public function __construct($variable) {
$this->varaible = $variable;
}
are this typo or not? on $this->varaible ?

PHP - Can you assign a member function to a variable? [duplicate]

This question already has answers here:
php call class function by string name
(6 answers)
Closed 8 years ago.
In PHP5, variables can be evaluated as functions1 such as:
function myFunc() {
echo "whatever";
}
$callableFunction = 'myFunc';
$callableFunction(); // executes myFunc()
Is there any syntax for assigning object member functions to a variable such as:
class MyClass {
function someCall() {
echo "yay";
}
}
$class = new MyClass();
// what I would like:
$assignedFunction = $class->someCall; // but I tried and it returns an error
$memberFunc = 'someCall';
$class->$memberFunc(); // I know this is valid, but I want a single variable to be able to be used to call different functions - I don't want to have to know whether it is part of a class or not.
// my current implementation because I don't know how to do it with anonymous functions:
$assignedFunction = function() { return $class->someCall(); } // <- seems lengthy; would be more efficient if I can just assign $class->someCall to the variable somehow?
$assignedFunction(); // I would like this to execute $class->someCall()
There is a way, but for php 5.4 and above...
class MyClass {
function someCall() {
echo "yay";
}
}
$obj = new Myclass();
$ref = array($obj, 'someCall');
$ref();
Hm.. actually it works for static too, just use the reference by name..
class MyClass {
static function someCall2() {
echo "yay2";
}
}
$ref = array('MyClass', 'someCall2');
$ref();
And for nonstatic this notation works as well. It creates a temporary instance of the class. So, this is what you need, only you need php 5.4 and above )
The PHP 5.4 solution above is good. If you need PHP 5.3, I don't think you can do much better than the anonymous function approach, but you could wrap that into a function that acts very similar to the PHP 5.4 method:
function buildCallable($obj, $function)
{
return function () use ($obj, $function) {
$args = func_get_args();
return call_user_func_array(array($obj, $function), $args);
};
}
//example
class MyClass
{
public function add($x, $y)
{
return $x + $y;
}
public static function multiply($x, $y)
{
return $x * $y;
}
}
//non-static methods
$callable = buildCallable(new MyClass(), 'add');
echo $callable(32, 10);
//static methods
$callable = buildCallable('MyClass', 'multiply');
echo $callable(21, 2);
This should work for any number of arguments to any (publicly visible) method.

Class variables holding a function in PHP

PHP allows for variables to hold functions like so:
$f = function($a,$b) {
print "$a $b";
};
$f("Hello","World!"); //prints 'Hello World!'
This works just fine for me. I'm trying to pass a function into a class and set an instance variable to hold that function but with little luck:
class Clusterer {
private $distanceFunc;
public function __construct($f) {
$this->distanceFunc = $f;
print $f(1,7); //works
print $this->distanceFunc(1,7); //exceptions and errors abound
}
}
$func = function($a,$b) {
return abs($a-$b);
}
$c = new Clusterer($func);
Am I doing something wrong here? The error is that the function doesn't exist so my guess currently is that it looks for a class function with that name (which there isn't one) and then gives up rather than looking for variables as well... how can I make it view the $this->distanceFunc as a variable?
EDIT:
So after the advice from the answers below, I found a solution which was the make a function to wrap the invocation. For example my class is now:
class Clusterer {
private $distanceFunc;
public function __construct($f) {
$this->distanceFunc = $f;
print $f(1,7); //works
print $this->distanceFunc(1,7); //exceptions and errors abound
}
private function distanceFunc($a,$b) {
$holder = $this->distanceFunc;
return $holder($a,$b);
}
}
$func = function($a,$b) {
return abs($a-$b);
}
$c = new Clusterer($func);
and this works great. Php looks for functions first and can only tell if it is a variable by context I guess is the moral of this story.
Your code doesn't work because PHP interprets $this->distanceFunc(1,7) as a class method, but you can do the following:
class Clusterer {
private $distanceFunc;
public function __construct($f) {
$this->distanceFunc = $f;
print $f(1,7); //works
print call_user_func_array($this->distanceFunc, array(1, 7));
// print $this->distanceFunc(1,7); //exceptions and errors abound
}
}
$func = function($a,$b) {
return abs($a-$b);
};
$c = new Clusterer($func);
http://sandbox.onlinephpfunctions.com/code/cdc1bd6bd50f62d5c88631387ac9543368069310
In PHP, methods and properties of an object occupy separate namespaces. This is different from JavaScript, for example, where foo.bar = function() {} is a perfectly valid way of defining a method.
Consequently, $this->distanceFunc(1,7); looks for a method named distanceFunc on the current class, and the classes it inherits from, but never looks for the property which you happen to have given the same name.
One solution is to force PHP to look up a property, then execute it, e.g. $foo = $this->distanceFunc; $foo(1,7) or call_user_func($this->distanceFunc, 1, 7)
Another would be to define the magic method __call on your class, which gets run whenever a non-existent method is referenced. Something like this ought to work (I don't have an easy way to testright now):
function __call($func, $args) {
if ( property_exists($this, $func) && is_callable($this->$func) ) {
return call_user_func_array($this->$func, $args);
}
}
Note that this still isn't the same as a real method, for instance in terms of access to private properties.
It looks like you're going for a strategy pattern here. IE you want to be able to inject different methods for calculating distance? If so there is a more "sane" way to do it.
You can define an interface to the classes you will use to store the strategy method ensuring that the class will always have the method calculate() for example which would be your distance calculation function. Then in the constructor of your Clusterer class, type check against the interface in the parameter and call calculate() on the object passed in.
Looks like this:
interface Calculateable
{
public function calculate();
}
class MyDistanceCalculator implements Calculateable
{
public function calculate()
{
// Your function here
}
}
class Clusterer
{
protected $calc;
public function __construct(Calculateable $calc)
{
$this->calc = $calc;
$this->calc->calculate();
}
}
$myClusterer = new Clusterer(new MyDistanceCalculator());
Because you defined an interface, any object you pass in will have the calculate() function
In HHVM, you can do this:
<?php
class Foo
{
public function __construct()
{
$this->bar = function() { echo "Here\n"; };
($this->bar)();
}
}
new Foo();
But it's not yet supported in PHP. But, it will be in PHP 7 (there will be no release named PHP 6).
PHP doesn't have first class functions. In JavaScript if you returned a function you could do this: myFunctionThatReturnsAFunction()(1,2), but not in PHP.
<?php
class Clusterer {
private $distanceFunc;
public function __construct(Closure $f) {
$this->distanceFunc = $f;
}
public function getDistFunc()
{
return $this->distanceFunc;
}
}
$func = function($a,$b) {
return abs($a-$b);
};
$c = new Clusterer($func);
$a = $c->getDistFunc();
echo $a(1,2);
Take a look at call_user_func
class Clusterer {
private $distanceFunc;
public function __construct($f) {
$this->distanceFunc = $f;
print $f(1,7); //works
print call_user_func($this->distanceFunc, 1, 7); //works too ;)
}
}
$func = function($a,$b) {
return abs($a-$b);
};
$c = new Clusterer($func);
Don't ask me what is the difference, but it works the way you want (One of the reasons i hate this language)

how to call anonymous functions stored in a static array?

I'm trying to store anonymous functions in a static array property of my class. These functions should be invoked later by their index, but calling
self::$arr['index']()
just doesn't work, while
$a = self::$arr['index'];
$a();
does!
This doesn't work:
class A {
private static $func = array('a' => '');
public function __construct() {
self::$func['a'] = create_function('$str', 'echo "$str";');
}
public function go($str) {
self::$func['a']($str); // Call the function directly
}
}
$a = new A();
$a->go("hooray"); // Outputs "Undefined variable: func"
But this does:
class A {
private static $func = array('a' => '');
public function __construct() {
self::$func['a'] = create_function('$str', 'echo "$str";');
}
public function go($str) {
$a = self::$func['a']; // Pass the function name to a variable
$a($str); // Call the function via the variable
}
}
$a = new A();
$a->go("hooray"); // Outputs "hooray"
Why?
I'm using PHP Version 5.4.3
this is the behavior of php's parser
$functionName['a'] = "hello";
self::$functionName['a']();
calls
self::hello();
... the very sad thing is that in php you can't do this:
(self::$functionName['a'])(); // doesn't work in php :(
as you can do in javascript, for example
what you can do is... use a temporary variable like you said
$a = self::$func['a'];
$a($parameter);
or
call_user_func(self::$func['a'], $parameter);
hope this helps...
in latest phps these features were added
$a['sss'] = function(){ echo 'bla'; };
$a['sss']();
class Bla
{
private $a;
function test()
{
$this->a['sss'] = function(){ echo 'bla2'; };
$this->a['sss']();
}
}
$c = new Bla();
$c->test();
and they work properly... so for some reason, this syntax doesn't work only when using the scope resolution operator ( ClassName:: self:: etc)
Well, in php you simply can not do that, it is a php feature. But you can use call_user_func or its relatives:
return call_user_func(self::$func['$a'], $str);
This is a consequence of how the PHP parser currently works. Since the function call () is evaluated before the static operator ::, you end up with the parser attempting to reference the local variable $func instead, and then giving you the error about $func being undefined (which it is, since there is no variable named $func in the method).
As you've discovered, you can solve this by doing two separate statements.

Restricting variable access

I need to have a variable that only one function can write (let's call that function a) and that only one other function can read (let's call that function b). Is that possible?
You could use a static variable:
function foo($val=null) {
static $var = null;
if (!is_null($var)) $var = $val;
return $val;
}
Here $var is only visible inside the function foo and is maintained throughout multiple calls:
foo(123);
echo foo(); // 123
foo(456);
echo foo(); // 456
Or use a class with a private member and access/modify it with public methods:
class A {
private $var;
public function setVar($val) {
$this->var = $val;
}
public function getVar() {
return $this->var;
}
}
With this the private member var is only visible to a particular instance of this class:
$obj1 = new A();
$obj1->setVar(123);
$obj2 = new A();
$obj2->setVar(456);
echo $obj1->getVar(); // 123
echo $obj2->getVar(); // 456
If you make the member static, then there is just one for the class instead of for each instance:
class A {
private static $var;
public function setVar($val) {
self::$var = $val;
}
public function getVar() {
return self::$var;
}
}
$obj1 = new A();
$obj1->setVar(123);
$obj2 = new A();
$obj2->setVar(456);
echo $obj1->getVar(); // 456
echo $obj2->getVar(); // 456
You can use a static abstract class.
abstract class Settings
{
private static var $_settings = array();
public static function get($key,$default = false)
{
return isset(self::$_settings[$key]) ? self::$_settings[$key] : $default;
}
public static function set($key,$value)
{
self::$_settings[$key] = $value;
}
}
Example Usage:
Settings::set('SiteName',`SomeResult`);
echo Settings::get('SiteName');
Since 5.3.0, you can use anonymous functions as closures. The advantage here, is that you can hold on to b... which is returned by a... and fire it off when you're ready:
<?php
function a()
{
// Only a() can write to $myVar
$myVar = 42;
$b = function() use ($myVar)
{
// $b can read $myVar
// no one else can
return $myVar;
};
return $b;
}
// get $b
$test = a();
// use $b
echo $test();
?>
Another solution before 5.3.0, but here a has to fire b which may not be that practical:
You can simply create an internal variable and pass it as an argument. You can do this inside a class, or just inside simple functions:
function a()
{
// ...
// Write the variable that
// only this function can write to
$thisVar = 1;
b($thisVar);
//...
}
function b($myVar)
{
// ...
// Do stuff w $myVar, a copy of $thisVar
// Changing $myVar has no effect on $thisVar
//
}
Do you mean friend functions? Because I'd love to be able to do that. So far I haven't found an easy way though (although you could try using Reflection, but that seems like way to much effort).
For me, it usually hasn't been an issue of maintaining data integrity / encapsulation, but of keeping the list of public methods (which is kinda like a class's API) free of clutter. A perfect framework should be easy to use, have obvious function names etc etc etc. Methods intended for use by a single other method really mess things up. The "solution" I've taken to is prefixing those function names by one or two underscores and writing "intended for internal use only" or something to that extent in the comments.

Categories