Related
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)
Is it possible to add methods to functions?
For example:
<?
function func(){
;
}
//add method
func->test = function(){
;
}
func->test();
func();
I'm coming from a javascript background, and therefore I'm used to 'everything is an object'.
EDIT:
I was just explaining where the misconception may often come from for new phpers. I understand the above code doesn't work.
EDIT 2
Figured it out.
class myfunc_class{
function __invoke(){
//function body
}
function __call($closure, $args)
{
call_user_func_array($this->$closure, $args);
}
}
$func = new myfunc_class;
$func->test = function(){
echo '<br>test<br>';
};
$func->test();
$func();
Even sexier :)
class func{
public $_function;
function __invoke(){
return call_user_func_array($this->_function,func_get_args());
}
function __construct($fun){
$this->_function = $fun;
}
function __call($closure, $args)
{
call_user_func_array($this->$closure, $args);
}
}
$func = new func(function($value){
echo $value;
});
$func->method = function(){
echo '<br>test<br>';
};
$func('someValue');
$func->method();
No.
Not everything is an object in PHP. In fact the only thing that is an object is, well, an object. More specifically, and generally, an instantiation of a class.
Your code converted to PHP
// function_object.php
<?php
class FunctionObject {
public method func() {
// do stuff
}
}
?>
In other code you would use it like this:
<?php
// example.php in same folder as function_object.php
include 'function_object.php';
$FuncObj = new FunctionObject;
$FuncObj->func();
Also: read more about PHP & OOP
No, because an object is a different PHP language construct than a function. Functions do not have properties, but are instead simply execution instructions.
But, if func were instead a pre-defined class, then yes... with a bit of witchcraft, ignoring public outcry, foregoing readability and PHP coding standards, and by using closures with the __call() magic method...
class func
{
function __call($func, $args)
{
return call_user_func_array($this->$func, $args);
}
}
$obj = new func;
$obj->test = function($param1, $param2)
{
return $param1 + $param2;
};
echo $obj->test(1,1);
This won't work as you'd think without __call(), because by $obj->test(1,1), PHP thinks you're trying to call a non-existent method of func when out of object scope. But inside, being that the new "test" property is of a type: closure, the call_user_func_array() just sees the "test" property as just another function, so you can hide this bit of trickery from outside scope.
You would need your function func() to return an object, then you'd be able to do something like: func()->test();
But please note that your way of handling objects is not right in PHP and I suggest that you go read the OO documentations here.
In difference to javacript, in PHP not everything is an object. Therefore you need to differ between function and class.
If you want to create an object, you need to define the class first.
class myClass {
}
You can then add as many functions to the class as you need. But you need to define them first:
class myClass {
function test() {
echo "test!\n";
}
}
When everything is ready, you can bring it to life then:
$class = new myClass;
$class->test();
Checkout the manual for more.
You can't do what you're trying to do, but you can define functions inside of other functions.
This example outputs text:
function a() {
function b() { echo 'Hi'; }
}
a();
b();
Output: HiHi
This example outputs an error:
function a() {
function b() { echo 'Hi'; }
}
b();
Output: ERROR
I've got an Abstract PHP superclass, which contains code that needs to know which subclass its running under.
class Foo {
static function _get_class_name() {
return get_called_class();
//works in PHP 5.3.*, but not in PHP 5.2.*
}
static function other_code() {
//needs to know
echo self::_get_class_name();
}
}
class Bar extends Foo {
}
class FooBar extends Foo {
}
Bar::other_code(); // i need 'Bar'
FooBar::other_code(); // i need 'FooBar'
This would work if I called the function get_called_class() -- however, this code is going to be run in PHP version 5.2.*, so that function is not available.
There's some custom PHP implementations of get_called_class() out there, but they all rely on going thru the debug_backtrack(), parsing a file name & line number, and running a regex (as the coder is not aware that PHP 5.2 has reflection) to find the class name. This code needs to be able to be run with php, ie. not only from a .php file. (It needs to work from a php -a shell, or an eval() statement.)
Ideally, a solution would work without requiring any code to be added to the subclasses… The only potential solution I can see though is adding the following code to each subclass, which is obviously a disgusting hack:
class FooBar extends Foo {
static function _get_class_name() {
return 'FooBar';
}
}
EDIT: Wait, this doesn't even seem to work. It would've been my last resort. Can anybody think of something similar to this solution that'd get me the required functionality. Ie., I'm willing to accept a solution that requires me to add one function or variable to each subclass telling it what its class name is. Unfortunately, it seems that calling self::_get_class_name() from the superclass calls the parent class' implementation, even if the subclass has overridden it.
In reality it is often helpful to know the actual called (sub)class when executing a superclass method, and I disagree that there's anything wrong with wanting to solve this problem.
Example, my objects need to know the class name, but what they do with that information is always the same and could be extracted into a superclass method IF I was able to get the called class name. Even the PHP team thought this was useful enough to include in php 5.3.
The correct and un-preachy answer, as far as I can tell, is that prior to 5.3, you have to either do something heinous (e.g. backtrace,) or just include duplicate code in each of the subclasses.
Working solution:
function getCalledClass(){
$arr = array();
$arrTraces = debug_backtrace();
foreach ($arrTraces as $arrTrace){
if(!array_key_exists("class", $arrTrace)) continue;
if(count($arr)==0) $arr[] = $arrTrace['class'];
else if(get_parent_class($arrTrace['class'])==end($arr)) $arr[] = $arrTrace['class'];
}
return end($arr);
}
This is not possible.
The concept of "called class" was introduced in PHP 5.3. This information was not tracked in previous versions.
As an ugly work-around, you could possibly use debug_backtrace to look into the call stack, but it's not equivalent. For instance, in PHP 5.3, using ClassName::method() doesn't forward the static call; you have no way to tell this with debug_backtrace. Also, debug_backtrace is relatively slow.
The PHP/5.2 alternative to late static binding that keeps duplicate code to the minimum while avoiding weird hacks would be to create one-liners on child classes that pass the class name as argument:
abstract class Transaction{
public $id;
public function __construct($id){
$this->id = $id;
}
protected static function getInstanceHelper($class_name, $id){
return new $class_name($id);
}
}
class Payment extends Transaction{
public static function getInstance($id){
return parent::getInstanceHelper(__CLASS__, $id);
}
}
class Refund extends Transaction{
public static function getInstance($id){
return parent::getInstanceHelper(__CLASS__, $id);
}
}
var_dump( Payment::getInstance(1), Refund::getInstance(2) );
object(Payment)#1 (1) {
["id"]=>
int(1)
}
object(Refund)#2 (1) {
["id"]=>
int(2)
}
The solution is:
get_class($this);
However, I don't know if this sentence works in static functions. Give it a try and tell me your feedback.
This hack includes the heinous use of debug_backtrace... not pretty, but it does the job:
<?php
function callerName($functionName=null)
{
$btArray = debug_backtrace();
$btIndex = count($btArray) - 1;
while($btIndex > -1)
{
if(!isset($btArray[$btIndex]['file']))
{
$btIndex--;
if(isset($matches[1]))
{
if(class_exists($matches[1]))
{
return $matches[1];
}
else
{
continue;
}
}
else
{
continue;
}
}
else
{
$lines = file($btArray[$btIndex]['file']);
$callerLine = $lines[$btArray[$btIndex]['line']-1];
if(!isset($functionName))
{
preg_match('/([a-zA-Z\_]+)::/',
$callerLine,
$matches);
}
else
{
preg_match('/([a-zA-Z\_]+)::'.$functionName.'/',
$callerLine,
$matches);
}
$btIndex--;
if(isset($matches[1]))
{
if(class_exists($matches[1]))
{
return $matches[1];
}
else
{
continue;
}
}
else
{
continue;
}
}
}
return $matches[1];
}
I have asked a question like this before, because I wanted a parent to have a factory method that was something like this
public static function factory() {
return new __CLASS__;
}
But it always returned the parent class, not the inherited one.
I was told that it is not possible without late static binding. It was introduced in PHP 5.3. You can read the documentation.
This function does the same job but works with instances too:
if (!function_exists('get_called_class')) {
function get_called_class() {
$bt = debug_backtrace();
/*
echo '<br><br>';
echo '<pre>';
print_r($bt);
echo '</pre>';
*/
if (self::$fl == $bt[1]['file'] . $bt[1]['line']) {
self::$i++;
} else {
self::$i = 0;
self::$fl = $bt[1]['file'] . $bt[1]['line'];
}
if ($bt[1]['type'] == '::') {
$lines = file($bt[1]['file']);
preg_match_all('/([a-zA-Z0-9\_]+)::' . $bt[1]['function'] . '/', $lines[$bt[1]['line'] - 1], $matches);
$result = $matches[1][self::$i];
} else if ($bt[1]['type'] == '->') {
$result = get_class($bt[1]['object']);
}
return $result;
}
}
<?php
class Foo {
private static $instance;
static function _get_class_name() {
return self::myNameIs();
}
static function other_code() {
//needs to know
echo self::_get_class_name();
}
}
class Bar extends Foo {
public static function myNameIs() {
self::$instance = new Bar();
return get_class(self::$instance);
}
}
class FooBar extends Foo {
public static function myNameIs() {
self::$instance = new FooBar();
return get_class(self::$instance);
}
}
Bar::other_code(); // i need 'Bar'
FooBar::other_code(); // i need 'FooBar'
I want to access private methods and variables from outside the classes in very rare specific cases.
I've seen that this is not be possible although introspection is used.
The specific case is the next one:
I would like to have something like this:
class Console
{
final public static function run() {
while (TRUE != FALSE) {
echo "\n> ";
$command = trim(fgets(STDIN));
switch ($command) {
case 'exit':
case 'q':
case 'quit':
echo "OK+\n";
return;
default:
ob_start();
eval($command);
$out = ob_get_contents();
ob_end_clean();
print("Command: $command");
print("Output:\n$out");
break;
}
}
}
}
This method should be able to be injected in the code like this:
Class Demo
{
private $a;
final public function myMethod()
{
// some code
Console::run();
// some other code
}
final public function myPublicMethod()
{
return "I can run through eval()";
}
private function myPrivateMethod()
{
return "I cannot run through eval()";
}
}
(this is just one simplification. the real one goes through a socket, and implement a bunch of more things...)
So...
If you instantiate the class Demo and you call $demo->myMethod(), you'll get a console: that console can access the first method writing a command like:
> $this->myPublicMethod();
But you cannot run successfully the second one:
> $this->myPrivateMethod();
Do any of you have any idea, or if there is any library for PHP that allows you to do this?
Thanks a lot!
Just make the method public. But if you want to get tricky you can try this (PHP 5.3):
class LockedGate
{
private function open()
{
return 'how did you get in here?!!';
}
}
$object = new LockedGate();
$reflector = new ReflectionObject($object);
$method = $reflector->getMethod('open');
$method->setAccessible(true);
echo $method->invoke($object);
EDIT:
Updated to include examples of private function calls with parameters.
As of PHP 5.4, you can use the predefined Closure class to bind a method/property of a class to a delta functions that has access even to private members.
The Closure class
For example we have a class with a private variable and we want to access it outside the class:
class Foo {
private $bar = "Foo::Bar";
private function add_ab($a, $b) {
return $a + $b;
}
}
PHP 5.4+
$foo = new Foo;
// Single variable example
$getFooBarCallback = function() {
return $this->bar;
};
$getFooBar = $getFooBarCallback->bindTo($foo, 'Foo');
echo $getFooBar(); // Prints Foo::Bar
// Function call with parameters example
$getFooAddABCallback = function() {
// As of PHP 5.6 we can use $this->fn(...func_get_args()) instead of call_user_func_array
return call_user_func_array(array($this, 'add_ab'), func_get_args());
};
$getFooAddAB = $getFooAddABCallback->bindTo($foo, 'Foo');
echo $getFooAddAB(33, 6); // Prints 39
As of PHP 7, you can use the new Closure::call method, to bind any method/property of an obect to a callback function, even for private members:
PHP 7+
$foo = new Foo;
// Single variable example
$getFooBar = function() {
return $this->bar;
};
echo $getFooBar->call($foo); // Prints Foo::Bar
// Function call with parameters example
$getFooAddAB = function() {
return $this->add_ab(...func_get_args());
};
echo $getFooAddAB->call($foo, 33, 6); // Prints 39
The first question you should ask is, if you need to access it from outside the class, why did you declare it private? If it's not your code, the originator probably had a good reason to declare it private, and accessing it directly is a very bad (and largely unmaintainable) practice.
EDIT: As Adam V. points out in the comments, you need to make the private method accessible before invoking it. Code sample updated to include this. I haven't tested it, though - just adding here to keep the answer updated.
That having been said, you can use Reflection to accomplish this. Instantiate ReflectionClass, call getMethod for the method you want to invoke, and then call invoke on the returned ReflectionMethod.
A code sample (though I haven't tested it, so there may be errors) might look like
$demo = new Demo();
$reflection_class = new ReflectionClass("Demo");
$reflection_method = $reflection_class->getMethod("myPrivateMethod");
$reflection_method->setAccessible(true);
$result = $reflection_method->invoke($demo, NULL);
Here's a variation of the other answers that can be used to make such calls one line:
public function callPrivateMethod($object, $methodName)
{
$reflectionClass = new \ReflectionClass($object);
$reflectionMethod = $reflectionClass->getMethod($methodName);
$reflectionMethod->setAccessible(true);
$params = array_slice(func_get_args(), 2); //get all the parameters after $methodName
return $reflectionMethod->invokeArgs($object, $params);
}
I have these problems too sometimes, however I get around it through my coding standards. Private or protected functions are denoted with a prefix underscore ie
private function _myPrivateMethod()
Then i simply make the function public.
public function _myPrivateMethod()
So although the function is public the naming convention gives the notification that whilst public is is private and shouldn't really be used.
If you are able to added a method in the class where the method is defined, you can add method which uses the call_user_method() internally. This works also with PHP 5.2.x
<?php
class SomeClass {
public function callprivate($methodName) {
call_user_method(array($this, $methodName));
}
private function somePrivateMethod() {
echo 'test';
}
}
$object = new SomeClass();
$object->callprivate('somePrivateMethod');
Answer is put public to the method. Whatever trick you are going to do it wouldn't be understandable to fellow developers. For example they do not know that at some other code this function has been accessed as public by looking at the Demo class.
One more thing. that console can access the first method writing a command like:. How can this even be possible? Console can not access demo class functions by using $this.
I guess the reflectionClass is the only alternative if you really want to execute some private methods. Anyhow, if you just need read access to privat or protected properties, you could use this code:
<?php
class Demo
{
private $foo = "bar";
}
$demo = new Demo();
// Will return an object with public, private and protected properties in public scope.
$properties = json_decode(preg_replace('/\\\\u([0-9a-f]{4})|'.get_class($demo).'/i', '', json_encode((array) $demo)));
?>
<?php
$request="email";
$data=[1,2,3,4,5];
$name=new Update($request,$data);
class Update{
private $request;
private $data;
function __construct($request,$data){
$this->request=$request;
$this->data=$data;
if($this->request=='email'){
$this->update_email();
}
else{
echo "Can't do anything";
}
}
private function update_email(){
echo $this->request;
echo '\n';
foreach($this->data as $x){
echo $x."\n";
}
}
}
?>
I am trying to make a class from a member variable like this:
<?
class A{
private $to_construct = 'B';
function make_class(){
// code to make class goes here
}
}
class B{
function __construct(){
echo 'class constructed';
}
}
$myA = new A();
$myA->make_class();
?>
I tried using:
$myClass = new $this->to_construct();
and
$myClass = new {$this->to_construct}();
but neither worked. I ended up having to do this:
$constructor = $this->to_construct;
$myClass = new $constructor();
It seems like there should be a way to do this without storing the class name in a local variable. Am I missing something?
Have you tried this?
$myClass = new $this->to_construct;
Are you using PHP 4 or something? On 5.2.9 $myClass = new $this->to_construct(); works perfectly.
In the end it's what you have to live with, with PHP. PHP syntax and semantics are VERY inconsistent. For example, an array access to the result of a call is a syntax error:
function foo() {
return array("foo","bar");
}
echo $foo()[0];
Any other language could do that but PHP can't. Sometimes you simply need to store values into local variables.
Same is true for func_get_args() in older versions of PHP. If you wanted to pass it to a function, you needed to store it in a local var first.
If I read well between the lines you are trying to do something like this. Right?
class createObject{
function __construct($class){
$this->$class=new $class;
}
}
class B{
function __construct(){
echo 'class B constructed<br>';
}
function sayHi(){
echo 'Hi I am class: '.get_class();
}
}
class C{
function __construct(){
echo 'class C constructed<br>';
}
function sayHi(){
echo 'Hi I am class: '.get_class();
}
}
$wantedClass='B';
$finalObject = new createObject($wantedClass);
$finalObject->$wantedClass->sayHi();
--
Dam