Its not clear to me if the following would work:
class Sample {
private $value = 10;
public function something() {
return function() {
echo $this->value;
$this->someProtectedMethod();
}
}
protected function someProtectedMethod() {
echo 'hello world';
}
}
I am using PHP 5.6, the environment this would run is 5.6. I am not sure about two things, the scope of this. and if I can call protected methods, private methods and private variables inside of closure functions.
Problem #1 is a simple syntax error:
return function() {
echo $this->value;
$this->someProtectedMethod();
};
(note the semi-colon)
Now this code will return the actual function when you call something().... it will not execute the function, so you'll want to assign that function to a variable. You have to make an explicit call to that variable as a function to execute it.
// Instantiate our Sample object
$x = new Sample();
// Call something() to return the closure, and assign that closure to $g
$g = $x->something();
// Execute $g
$g();
Then you get into issues of scope, because $this isn't in scope of the function when $g is called. You need to bind the Sample object that we've instantiated to the closure to provide scope for $this, so we actually need to use
// Instantiate our Sample object
$x = new Sample();
// Call something() to return the closure, and assign that closure to $g
$g = $x->something();
// Bind our instance $x to the closure $g, providing scope for $this inside the closure
$g = Closure::bind($g, $x)
// Execute $g
$g();
EDIT
Working Demo
Related
I have to classes and I want to pass a method fro classA to classB constructor and store it on a classB instance variable so as to execute it later.
class A
{
public function execute()
{
$ClassB = new ElgEmpAnalyticsImporterControllerImporterEmporioOrder(ConMW::getDB(), $this -> lPointFile, [$this, 'getLastPoints'] );
$ClassB -> import();
}
public function getLastPoints(Array $keys)
{
$res = [];
forEach ( json_decode( file_get_contents($this -> lastPointFile) ) as $key => $value ):
if ( in_array($key, $keys) ):
$res[$key] = $value;
else:
$res[$key] = '';
endif;
endforeach;
unset($key);
unset($value);
return $res;
}
}
classB
{
public $getLastPoints = null;
public function __construct(callable $getLastPoints)
{
$this -> getLastPoints = $getLastPoints;
}
public function import()
{
$lastPoints = $this -> getLastPoints(['idOrder', 'orderLastExport']);
}
}
Trying to execute it like that I get the error "Call to undefined method getLastPoints()"
I think the problem is on storing the function on the instance variable $getLastPoints of classB. I can conclude this because If I execute the function on the constructor it works. That means if I change the constructor of classB like this
classB
{
public $getLastPoints = null;
public function __construct(callable $getLastPoints)
{
$getLastPoints(['idOrder', 'orderLastExport']);
}
}
it works.
But what i need is to execute the external function inside the import function.
Can someone please help me?
thanks for your time,
Edit for clarification: My question is why I can execute the function inside the contructor like this :
$lastPoint(a,b)
but when I assign the callable into an instance variable like this:
$this -> lastPoint(a,b)
it does not work.
I read that php uses different storage for variables and function. PHP probably sees the callable $lastPoints as a variable. So can the callable $lastPoints, be added as dynamic function to my instance of classB?
Christoforos
PHP Callable Object as Object Member
$getlastpoints is a property with an array value stored in it, not a function.
call_user_func_array($this->getlastpoints, ['idOrder', 'orderLastExport']);
public function import()
{
$my_func = $this->getLastPoints;
$lastPoints = $my_func(['idOrder', 'orderLastExport']);
}
In a nutshell, the reason you will have to do this is because you can define properties and methods in a PHP class having the same name. e.g.
class foo {
public $bar
public function bar() {}
}
so in this instance, if allowed to directly access a stored callable on the $bar property... What would the call below reference?
$my_foo_obj->bar()
To avoid the situation, you cannot call it directly.
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.
I've created not very complex test code (tested in PHP 5.5.12):
<?php
class Test
{
private $cached = null;
public function __construct()
{
$this->cached = [];
$this->cached[0] = 12;
}
function wrap($function, $index)
{
if (isset($this->cached[$index])) {
return $this->cached[$index];
}
$result = call_user_func($function);
return $result;
}
}
class B
{
public function run()
{
$x = 6;
$obj = new Test();
$value = $obj->wrap(
function () use ($x) {
return $this->test($x);
},
1
);
echo $value."<br />";
}
protected function test($x)
{
echo "I'm running ";
return $x * $x;
}
}
class C extends B
{
public function run()
{
$x = 6;
$obj = new Test();
$myFunc = function () use ($x) {
return $this->test($x);
};
$value = $obj->wrap($myFunc, 1);
echo $value."<br />";
}
}
class D extends B
{
public function run()
{
$x = 6;
$obj = new Test();
$value = $obj->wrap(array($this, 'test'), 1);
echo $value."<br />";
}
}
$b = new B();
$b->run();
$c = new C();
$c->run();
$d = new D();
$d->run();
Probably there are some parts of the code you could say it could be done better but the main point are closures function and callable. Those classes simulate in a very simple way caching system. If data is in cache it returns data from cache otherwise function that gets data is called (of course this cache system doesn't work because it doesn't have to - it's just a sample code).
Questions:
1) Why when using object $d I get the following warning:
call_user_func() expects parameter 1 to be a valid callback, cannot access protected method D::test()
and is it possible to launch protected method from parent? When I change this method from protected to public it can be launched without a problem
2) As you probably noticed I want to use some arguments for function I call using call_user_sync. Unfortunately I don't know those parameters when I call call_user_func so in class B and C I used closures where I can use/pass extra parameters. I have 2 extra questions connected to this:
is it the way where closures are useful and commonly used?
is it possible using object $d to pass parameters to test method without using closures but not when calling call_user_sync but inside class D?
It is important to note scope at the time of the execution. You are creating the callback in the correct scope, but you are executing the callback in another object with no access to the protected method (the callback get's executed in class Test not in a parent or child of class B.
I ran in to this issue some time ago when writing my own dispatcher class. One option was to set a "parent" on the dispatcher, and pass the dispatcher as one of the parameters on the callback. The callback then checks the "parent" associated with the Dispatcher for === $this, and then knows that it has access and goes to town.
You have to do your own access checking, is the point.
I would like to access a class constant using self from within an anonymous function.
class My_Class {
const CLASS_CONSTANT = 'test value';
private function my_function(){
$lambda_function = function(){
echo self::CLASS_CONSTANT;
};
$lambda_function();
}
}
When I tried this, I get the error:
Fatal error: Cannot access self:: when no class scope is active in ...
Is it possible to pass the parent class into the scope of this anonymous function? Would a use statement work?
>> All versions test of PHP 5.4+ way on 3v4l <<
PHP 5.4+ WAY:
This has become significantly simpler since PHP 5.4, where $this is no longer dirty:
class My_Class {
const CLASS_CONSTANT = 'test value';
private function my_function() {
$lambda_function = function() {
// $this is actually inherited from the parent object, so
// you don't even need a use() statement
echo $this::CLASS_CONSTANT;
// Or just use self, that's inherited too
echo self::CLASS_CONSTANT;
};
$lambda_function();
}
}
PRE 5.4 WAY:
Make the anonymous function a closure -- by introducing scoped variables into the function -- and call the constant from that:
class My_Class {
const CLASS_CONSTANT = 'test value';
private function my_function() {
$self = $this;
$lambda_function = function() use ($self) { // now it's a closure
echo $self::CLASS_CONSTANT;
} // << you forgot a ;
lambda_function(); // << you forgot a $
}
}
Unfortunately you can't use ($this) YET. They're working on it. I expect it to work in PHP >= 5.4.
afaik anonymous functions are just that.. functions. Not class methods, so scope is out. You can pass the constant as an argument or use My_Class::CLASS_CONSTANT.
You're accessing the self inside an anonymous function, this won't work. What you should do is use My_Class::CLASS_CONSTANT instead of the self reference.
No, that's not possible. Similarly, you can not bind $this to an anonymous function. Just passing the necessary values instead should do the trick though?
<?php
class My_Class {
const CLASS_CONSTANT = 'test value';
private function my_function(){
$lambda = function( $yourConstant ){
return $yourConstant;
};
return $lambda( self::CLASS_CONSTANT );
}
public function test( ) {
return $this->my_function( );
}
}
$class = new My_Class( );
echo $class->test( ); // 'test value'
I don't understand what's going on with this. I need to call Func1 from Func2 and parametr for Func1 should be given inside the object.
class MyClass {
function Func1($a) {
return $a;
}
function Func2() {
echo $this->Func1($a);
}
}
$c = new MyClass();
$c->Func1('parametr'); // prints: 1
$c->Func2();
What about setting the parameter as class variable (property)?
class MyClass {
private $a;
function Func1($a) {
$this->a = $a;
return $a;
}
function Func2() {
echo $this->Func1($this->a);
}
}
This sets the parameter first time you call Func1. Then everytime you call Func2, it uses the parameter. You can also skip passing the parameter like this:
class MyClass {
private $a;
function Func1($a = null) {
if ($a === null) {
return $this->a;
} else {
$this->a = $a;
return $a;
}
}
function Func2() {
echo $this->Func1();
}
}
I.e if you call func1 without any parameter, it uses the stored variable (property), otherwise it uses the given parameter. This can be used in various ways depending on your exact needs.
The instruction:
echo $this->Func1($a);
is wrong: the variable $a is out of the scope of Func2. $a is a parameter of Func1 so is only int he scope of Func1.
You should read more about variable scopes at PHP http://php.net/manual/en/language.variables.scope.php
quick glimpse:
1) you can have global variables. to access those, use keyword global in functions that need access to that
2) you can have local variables, available only within a scope of a function
3) you can pass references to variables, so that variable from one scope is made accessible to other function/scope
4) you can have objects's internal variables of different kind (private, public, protected, static)
I suggest you get familiar with this stuff real well.
As for you code, problem is obvious. In Func2 the $a is local variable, thus when passed to $this->Func1($a), it is undefined. As your example code suggests, you might want to introduce class property private $a, and then use that. e.g.:
class X {
private $a;
function set($val){
$this->a = $val;
}
function get(){
return $this->a;
}
function doSomethingWithA(){
$this->set($this->get() * 2);
}
}