Why the following
class AClass
{
public function __construct ()
{
$this->prop = "Hello";
}
public function &get ()
{
return $this->prop;
}
protected $prop;
}
function func (&$ref)
{
$ref = &$ref->get();
}
$value = new AClass();
func($value);
var_dump( $value );
outputs
object(AClass)#2 (1) {
["prop":protected]=>
string(5) "Hello"
}
Shouldn't the $value variable become a reference to $prop and be of type string instead of staying of type AClass?
http://ideone.com/g1hTNV
Consider this piece of code (It's the same as your code, just without everything else):
$value = new stdClass;
$ref = &$value;
$var = "Hello";
$ref = &$var; // this is where you write $ref = &$ref->get();
var_dump($value);
This gives as expected an empty object and not string(5) Hello.
Why?
We're in line 4 overwriting the reference to $value with a reference to $var.
$ref now holds a reference to $var; the value of $value remains unaffected.
What we're not doing
We don't assign the value of $var to $value.
We don't assign to $value a reference to $var.
Conclusion
Assigning references to a variable via another referencing variable is just not possible in PHP.
bwoebi is totally right about how PHP references work. Without a dereference operator it would become impossible to know exactly what you mean when using pointers, so PHP has used another approach. This does not, however, mean that what you want is impossible, you just can't do it all inside a function:
class AClass
{
public function __construct ()
{
$this->prop = "Hello";
}
public function &get()
{
return $this->prop;
}
public $prop;
}
function &func($ref)
{
return $ref->get();
}
$root = new AClass();
$value = &func( $root );
var_dump( $value );
// string(5) "Hello"
$value = "World";
var_dump( $root->get() );
// string(5) "World"
http://codepad.org/gU6pfzUO
You should remove the ampersand in your func function. Then it will return you the string.
function func (&$ref)
{
$ref = $ref->get();
}
what you want can be acheived by this-
<?php
class AClass
{
public function __construct ()
{
$this->prop = "Hello";
}
public function &get ()
{
return $this->prop;
}
protected $prop;
}
function func (&$ref)
{
$ref= $ref->get();
}
$value = new AClass();
func($value);
print_r( $value );
?>
class AClass
{
public function __construct ()
{
$this->prop = "Hello";
}
public function &get ()
{
return $this->prop;
}
protected $prop;
}
function func (&$ref)
{
$ref = $ref->get(); // You don't need the ampersand here
}
$value = new AClass();
func($value);
var_dump( $value ); // outputs: string(5) "Hello"
Just change protected into public for the sake of testing.
$value = new AClass();
$myValue = &$value->get();
var_dump($myValue );
var_dump($value->prop);
$value->prop = 'test';
var_dump($value->prop);
var_dump($myValue );
Output :
string 'Hello' (length=5)
string 'Hello' (length=5)
string 'test' (length=4)
string 'test' (length=4)
incase you think that function is necessary you can use global variable.
Your function func() needs to return a value and then it needs to assign to a variable what func() returned. See modified code below:
function func (&$ref) {
$ref = &$ref->get();
return $ref;
}
$value = new AClass();
$new_value = func($value);
var_dump( $new_value );
Related
Is it possible to pass an anonymous function as a parameter in PHP? And if yes - how?
I am trying to pass an anonymous function to a setter which will fill an array with values returned from that function.
class MyClass
{
private $arr = array();
public function __construct()
{
$this->setArrElm('New', function(){return 123;});
}
private function setArrElm($name, $val)
{
// here: gettype($val) == object
$this->arr[$name] = $val;
}
}
Please note the comment - the type of val is object and I expect an int.
In PHP 7 you can self execute the closure
class MyClass
{
private $arr = array();
public function __construct()
{
$this->setArrElm('New', (function(){return 123;})()); //<-- self execute
}
private function setArrElm($name, int $val) //<-- added typehint
{
// here: gettype($val) == object
$this->arr[$name] = $val;
print_r($val);
}
}
new MyClass;
Output
123
Sandbox
This takes a form similar to JS (probably other languages too):
(function(){return 123;})()
It's important to know that it's executing the function, then passing the result. You can pass the closure (which is an object) and then execute it, too. But if you have strict types and need an int, you can self execute the closure too.
It really only makes sense to do this if you need an int as the argument. Even in that case you can execute it beforehand and then pass the result. This just saves you a local variable.
For < PHP7 or just because
Alt1
class MyClass
{
private $arr = array();
public function __construct()
{
$var = function(){return 123;};
$this->setArrElm('New', $var()); //<-- execute
}
private function setArrElm($name, $val) //<-- added typehint
{
// here: gettype($val) == object
$this->arr[$name] = $val;
print_r($val);
}
}
new MyClass;
Alt2
class MyClass
{
private $arr = array();
public function __construct()
{
$var = function(){return 123;};
$this->setArrElm('New', $var);
}
private function setArrElm($name, $val) //<-- mixed
{
if(gettype($val) == 'object' && is_a($val, '\Closure')){
//is a closure, you could use is_callable etc. too. see __invoke()
$val = $val();
}
$this->arr[$name] = $val;
print_r($val);
}
}
new MyClass;
Alt3
class MyClass
{
private $arr = array();
public function __construct()
{
$var = function(){return 123;};
$this->setArrElm('New', $var);
}
private function setArrElm($name, $val) //<-- mixed
{
if(is_callable($val)){
//pass functions (as a string) or arrays or closures(executable classes with __invoke)
$val = call_user_func($val);
}
$this->arr[$name] = $val;
print_r($val);
}
}
new MyClass;
Cheers
Here's a little mock-up to describe my predicament:
<?php
$var = "Before";
function getVar(){
global $var;
return $var;
}
$array = Array(
"variable" => "Var = " . getVar()
);
$var = "After";
echo $array['variable'];
?>
That code would echo 'Before', I'm aiming for it to echo 'after'. I realize that this is how PHP is supposed to work however it's crucial for the array to execute getVar() only when it's called.
How would I go about doing this?
You can not do this since array declaration will initialize it - so you're mixing function calling at array's 'usage' and at it's definition. There's no 'usage': array is already defined to that moment.
However, an answer could be using ArrayAccess, like this:
class XArray implements ArrayAccess
{
private $storage = [];
public function __construct()
{
$this->storage = func_get_args();
}
public function offsetSet($offset, $value)
{
if(is_null($offset))
{
$this->storage[] = $value;
}
else
{
$this->storage[$offset] = $value;
}
}
public function offsetExists($offset)
{
return isset($this->storage[$offset]);
}
public function offsetUnset($offset)
{
unset($this->storage[$offset]);
}
public function offsetGet($offset)
{
if(!isset($this->storage[$offset]))
{
return null;
}
return is_callable($this->storage[$offset])?
call_user_func($this->storage[$offset]):
$this->storage[$offset];
}
}
function getVar()
{
global $var;
return $var;
}
$var = 'Before Init';
$array = new XArray('foo', 'getVar', 'bar');
$var = 'After Init';
var_dump($array[1]);//'After Init'
-i.e. try to call data, which is inside element, when actual get happened. You may want to have different constructor (for associative arrays) - but the general idea was shown.
Editing my answer after the question was edited.
No, what you are trying to achieve isn't possible because when you call the function it returns and it's done at that point. But you could achieve something similar with object oriented coding. I'll create something for you, please wait.
<?php
class Foo {
public function __toString() {
global $var;
return "Var = {$var}";
}
}
$var = "Before";
$array = array( "variable" => new Foo() );
$var = "After";
echo $array['variable'];
?>
PS: Sorry for the late answer, but there was a blackout in Salzburg. :(
It occurred to me that you could also use anonymous functions and invoke/execute those
Proof of concept:
$var = "Before";
function getVar(){
global $var;
return $var;
}
$array = Array(
"variable" => create_function(null, "return 'Var = ' . getVar();")
);
$var = "After";
echo $array['variable']();
returns
Var = After
I have a PHP class like so:
<?php
class MyClass {
public $my_variable = array();
public function func1() {
$var1 = $this->my_variable;
array_push($var1, 'var1');
return $this->my_variable;
}
public function func2() {
$var2 = $this->my_variable;
array_push($var2, 'var2');
return $this->my_variable;
}
}
$my_class = new MyClass;
print_r($my_class->func1());
print_r($my_class->func2());
?>
The two print_r functions return an empty array, and there are no errors displayed.
How can I get the "var1" and "var2" strings added to the $my_variable array? I'm not sure where I am going wrong here...!
Thanks.
$var1 = $this->my_variable actually creates a copy of the array, which you then push a value onto.
Instead, you can do this: $var1 = &$this->my_variable to create a reference instead, but it would just be better to not have the pointless variable at all:
public function func1() {
$this->my_variable[] = 'var1';
return $this->my_variable;
}
public function func2() {
$this->my_variable[] = 'var2';
return $this->my_variable;
}
Or, more appropriately:
public function add($value) {
$this->my_variable[] = $value;
return $this->my_variable;
}
// call with `$my_class->add('var1'); $my_class->add('var2');
You have to assign the $var's by reference. You copy the array and then add to the copy some entry and then return the initial array.
$var2 = &$this->my_variable;
would be right. The & is marking here a reference.
I'm trying to hold onto a variable reference for later use.
Not certain this is even possible, but I'm hoping I can initialize an array element, and reference it with a variable. Then, set the value of said array element to something, therefore making the value accessible from the referenced variable.
For example, this works:
class Test{
private $_vars = array();
public function bind($key, &$var){
$this->_vars[$key] = &$var;
return $this;
}
public function fetch($key, &$var){
$var = $this->_vars[$key];
return $this;
}
}
$test = new Test();
$string_set = 'This is a string';
$test->bind('string', $string_set)
->fetch('string', $string_get);
var_dump($string_get);
// expected: string(16) "This is a string"
// actual: string(16) "This is a string"
Now here's the problem; the ordering of method calls. I can't have the call() function returning a reference to $this, as the call() function needs to pass up the return value of the stored anonymous function (otherwise I'd reorder the calls to be ->call()->fetch() instead of ->fetch()->call())
Anyways, the fetch() method should be setting the appropriate element by key in $_vars to NULL (to empty any existing value, or initialize it, whichever) and then referencing that element to the passed $var.
When the anonymous function is called (after the fetch() binding is done), it calls bind(), now binding the element in $_vars to whatever (a $string_set containing This is a string in this case) If my logic is correct, the fetch() bound variable ($string_get in this case) should now reference the array element in $_vars which is referencing $string_set which contains This is a string.
Doesn't seem that way though. Here's the code that's failing (stripped down for brevity, but all the important parts are there)
class Test{
private $_vars = array();
private $_function;
public static function factory(){
return $test = new self(function() use(&$test){
$string_set = 'This is a string';
$test->bind('string', $string_set);
return true;
});
}
private function __construct($function){
$this->_function = $function;
}
public function bind($key, &$var){
$this->_vars[$key] = &$var;
return $this;
}
public function fetch($key, &$var){
$this->_vars[$key] = null;
$var = &$this->_vars[$key]; // edited; was not assigning by reference
return $this;
}
public function call(){
return (bool) call_user_func($this->_function);
}
}
$return = Test::factory()
->fetch('string', $string_get)
->call();
var_dump($return, $string_get);
// expected: bool(TRUE), string(16) "This is a string"
// actual: bool(TRUE), NULL
Am I chasing daisies here, is this even possible? Either way, I appreciate and thank you in advance for even glancing at this problem, any insight is really appreciated.
Edit: the line in fetch() - $var = $this->_vars[$key]; wasn't assigning the array element by reference. I've edited it now to $var = &$this->_vars[$key];, though it seemingly has no effect.
Bonus: If this problem is solvable, that's obviously great; I'm actually hoping that bind() can take $var by value, rather than by reference. The method signature would be changed to something like set($key, $value). Anyways, thanks again in advance.
To elaborate for the seemingly curious (looking in your direction #Tomalak) I'll provide the more complete class, and usage scenario:
class Action{
private static $_cache = array();
private static $_basePath;
private $_vars = array();
private $_function;
public static function setBasePath($basePath){
$basePath = rtrim($basePath, '/') . '/';
if(!is_dir($basePath)){
// throw exception, $basePath not found
}
self::$_basePath = $basePath;
}
public static function load($actionPath){
$actionPath = self::$_basePath . $actionPath;
if(array_key_exists($actionPath, self::$_cache)){
return self::$_cache[$actionPath];
}
if(!is_file($actionPath)){
// throw exception, $actionPath not found
}
$action = call_user_func(function() use(&$action, $actionPath){
return require($actionPath);
});
if(!($action instanceof self)){
// throw exception, $action of invalid type
}
self::$_cache[$actionPath] = $action;
return $action;
}
public function __construct($function){
if(!is_callable($function)){
// throw exception, $function not callable
}
$this->_function = $function;
}
public function bindReturn($key, &$var){
$this->_vars[$key] = &$var;
return $this;
}
public function fetchInto($key, &$var){
$this->_vars[$key] = null;
$var = &$this->_vars[$key];
return $this;
}
public function run(){
return (bool) call_user_func_array($this->_function, func_get_args());
}
}
############################################################################
// actions/test.php
return new Action(function($name)
use(&$action){
if($name == 'Alice'){
return false;
}
$data = "Hi, my name is {$name}.";
$action->bindReturn('data', $data);
return true;
});
############################################################################
// index.php (or whatever)
$result = Action::load('actions/test.php') // loaded
->fetchInto('data', $data)
->run('Alice');
// Failed
echo $result
? 'Success - ' . $data
: 'Failed';
$result = Action::load('actions/test.php') // called from cache
->fetchInto('data', $data)
->run('Bob');
// Success - Hi, my name is Bob
echo $result
? 'Success - ' . $data
: 'Failed';
What you want do is simply not possible (at least with referencces), because you cannot "redirect" a reference. Here's what happens:
$instance->fetch('foo', $myVar);
public function fetch($key, &$var){
// Here $var is a reference to $myVar.
$var = &$this->_vars[$key]; // now $var is a reference to $this->_vars[$key]
// it is not connected to $myVar anymore.
}
Here's what you can do: You can pass fetch() a reference to an array and set an element in that array to be a reference to $this->_vars[$key] or you can pass fetch() an object and set a member variable to be the reference.
Oh, sry missed the obvious: You can of course just use your bindReturn() function in the use-case you presented. That would work without problems.
Looks like you have problem with
public function fetch($key, &$var){
$this->_vars[$key] = null;
$var = $this->_vars[$key];
return $this;
}
If you want to remove the key, don't set it to null, unset it:
Edit: changed the code to avoid uninitialized variable exception.
public function fetch($key, &$var){
if(isset($this->_vars[$key]))
{
$var = $this->_vars[$key];
unset($this->_vars[$key]);
}
else
{
$var = null;
}
return $this;
}
I'm trying to reference a private variable of an object from within a closure. The code below would seem to work, but it complains Fatal error: Cannot access self:: when no class scope is active in test.php on line 12 and Fatal error: Using $this when not in object context in test.php on line 20.
Any ideas how to accomplish the same results using a closure while keeping the variables private and without making helper functions (defeating the whole idea of a private variable).
class MyClass
{
static private $_var1;
private $_var2;
static function setVar1( $value )
{
$closure = function () use ( $value ) {
self::$_var1 = $value;
};
$closure();
}
function setVar2( $value )
{
$closure = function () use ( $value ) {
$this->_var2 = $value;
};
$closure();
}
}
MyClass::setVar1( "hello" ); //doesn't work
$myclass = new MyClass;
$myclass->setVar2( "hello" ); //doesn't work
Edit to note, this answer was originally meant for PHP5.3 and earlier, it's possible now. For current information, see this answer.
This is not directly possible. In particularly, closures have no associated scope, so they cannot access private and protected members.
You can, however, use references:
<?php
class MyClass
{
static private $_var1;
private $_var2;
static function setVar1( $value )
{
$field =& self::$_var1;
$closure = function () use ( $value, &$field ) {
$field = $value;
};
$closure();
}
function setVar2( $value )
{
$field =& $this->_var2;
$closure = function () use ( $value, &$field ) {
$field = $value;
};
$closure();
}
}
MyClass::setVar1( "hello" );
$myclass = new MyClass;
$myclass->setVar2( "hello" );
This is possible starting in PHP 5.4.0
class test {
function testMe() {
$test = new test;
$func = function() use ($test) {
$test->findMe(); // Can see protected method
$test::findMeStatically(); // Can see static protected method
};
$func();
return $func;
}
protected function findMe() {
echo " [find Me] \n";
}
protected static function findMeStatically() {
echo " [find Me Statically] \n";
}
}
$test = new test;
$func = $test->testMe();
$func(); // Can call from another context as long as
// the closure was created in the proper context.
Closures have no concept of $this or self -- they are not tied to objects in that way. This means that you would have to pass the variables through the use clause... something like:
$_var1 =& self::$_var1;
$closure = function() use ($value, &$_var1) {
$_var1 = $value;
};
$_var2 =& $this->_var2;
$closure = function() use ($value, &$_var2) {
$_var2 = $value;
};
I haven't tested the above code, but I believe it to be correct.