PHP - member encapsulation returns strange reference - php

I have a class which has a private member $content. This is wrapped by a get-method:
class ContentHolder
{
private $content;
public function __construct()
{
$this->content = "";
}
public function getContent()
{
return $this->content;
}
}
$c = new ContentHolder();
$foo = array();
$foo['c'] = $c->getContent();
Now $foo['c'] is a reference to content, which is what I don't understand. How can I get the value? Thank You in advance.

I just tried your code and $foo['c'] is not a reference to $content. (Assigning a new value to $foo['c'] does not affect $content.)
By default all PHP functions/methods pass arguments by value and return by value. To return by reference you would need to use this syntax for the method definition:
public function &getContent()
{
return $this->content;
}
And this syntax when calling the method:
$foo['c'] = &$c->getContent();
See http://ca.php.net/manual/en/language.references.return.php.

i'm not quite understanding your question. say you changed:
public function __construct() {
$this->content = "test";
}
$c = new ContentHolder();
$foo = array();
$foo['c'] = $c->getContent();
print $foo['c']; // prints "test"
print $c->getContent(); // prints "test"

In PHP, you don't say: "$foo = new array();"
Instead, you simply say: "$foo = array();"
I ran your code (PHP 5.2.6) and it seems to work fine. I tested it by dumping the array:
var_dump($foo);
This outputs:
array(1) {
["c"]=>
string(0) ""
}
I can also simple use echo:
echo "foo[c] = '" . $foo['c'] . "'\n";
This outputs:
foo[c] = ''

Related

PHP's assignment by reference is not working as expected

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 );

Only initialize a variable in an array if it's requested

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

Anonymous functions in a class

is there a better way to call an anonymous function inside a class? Here is a simple example that clearifies what I mean.
class foo
{
private $call_back_1 = null;
private $call_back_2 = null;
function __construct($func1, $func2)
{
$this->call_back_1 = func1;
$this->call_back_2 = func2;
}
function provokeCallBacks()
{
//this does not work, and gives an error
$this->call_back_1();
//I would like to avoid this
$method = $this->call_back_2;
$method();
}
}
$call1 = function(){ echo "inside call 1"};
$call2 = function(){ echo "inside call 2"};
$test = new foo(call1, call2);
$test->provokeCallBacks();
* Update 1: Please ignore any syntax error as I have written this on the fly for demo puposes. *
Inside foo:provokeCallBacks, I am trying to call the anonymous functions how ever the first way does not works and gives an error. The second one works but it's a bit stupid that I have to use a temp variable called "$method" to make the call.
I want to know if there exists a better way to call the anonymous function.
call_user_func($this->call_back_1);
No, it's not possible to call an anonymous function via $this.
Another options is;
call_user_func($this->call_back_1);
Being PHP loosely typed, it can't do like {$this -> callback}(); you have to store it in a temp variable or to use call_user_func() either.
EDIT - consider something like this:
class Lambdas
{
protected $double;
protected $triple;
public function __construct($double, $triple)
{
$this -> double = $double;
$this -> triple = $triple;
}
public function __call($name, $arguments)
{
if( is_callable($this -> {$name}) ){
return call_user_func_array($this -> {$name}, $arguments);
}
}
}
$lambdas = new Lambdas(
function($a){ return $a * 2;},
function($a){ return $a * 3;}
);
echo $lambdas -> double(2); // prints 4
echo $lambdas -> triple(2); // prints 6
Dirty and dangerous, but you might succeed using eval..
class foo
{
private $call_back_1 = null;
private $call_back_2 = null;
function __construct($func1, $func2)
{
$this->call_back_1 = func1;
$this->call_back_2 = func2;
}
function provokeCallBacks()
{
eval($this->call_back_1);
eval($this->call_back_2);
}
}
call1 = 'echo "inside call 1"';
call2 = 'echo "inside call 2"';
$test = foo(call1, call2);
$test->provokeCallBacks();
I know your question has been answered but you can try changing your approch ..
class Foo {
private $calls = array();
function __set($key, $value) {
$this->calls[$key] = $value;
}
function __call($name, $arg) {
if (array_key_exists($name, $this->calls)) {
$this->calls[$name]();
}
}
function __all() {
foreach ( $this->calls as $call ) {
$call();
}
}
}
$test = new Foo();
$test->A = function () {
echo "inside call 1";
};
$test->B = function () {
echo "inside call 2";
};
$test->A(); // inside call 1
$test->B(); // inside call 2
$test->__all(); // inside call 1 & inside call 2

get a list of all variables defined outside a class by user

i have something like this:
class foo
{
//code
}
$var = new foo();
$var->newVariable = 1; // create foo->newVariable
$var->otherVariable = "hello, im a variable"; //create foo->otherVariable
i can get in class foo a list of all variables defined outside by user (newVariable, otherVariable,etc)? Like this:
class foo
{
public function getUserDefined()
{
// code
}
}
$var = new foo();
$var->newVariable = 1; // create foo->newVariable
$var->otherVariable = "hello, im a variable"; //create foo->otherVariable
var_dump($var->getUserDefined()); // returns array ("newVariable","otherVariable");
Thanks!.
Yes, using get_object_vars() and get_class_vars():
class A {
var $hello = 'world';
}
$a = new A();
$a->another = 'variable';
echo var_dump(get_object_vars($a));
echo '<hr />';
// Then, you can strip off default properties using get_class_vars('A');
$b = get_object_vars($a);
$c = get_class_vars('A');
foreach ($b as $key => $value) {
if (!array_key_exists($key,$c)) echo $key . ' => ' . $value . '<br />';
}
What is your goal? Imo it's not very good practice (unless you really know what you are doing). Maybe it's good idea consider create some class property like "$parameters" and then create setter and getter for this and use it in this way:
class foo {
private $variables;
public function addVariable($key, $value) {
$this->variables[$key] = $value;
}
public function getVariable($key) {
return $this->variables[$key];
}
public function hasVariable($key) {
return isset($this->variables[$key]);
}
(...)
}
$var = new foo();
$var->addVariable('newVariable', 1);
$var->addVariable('otherVariable', "hello, im a variable");
And then you can use it whatever you want, for example get defined variable:
$var->getVariable('otherVariable');
To check if some var is already defined:
$var->hasVariable('someVariable')
get_class_vars() http://php.net/manual/en/function.get-class-vars.php
You question is not clear though.
$var->newVariable = 1;
there are two possible contex of above expression
1) you are accessing class public variables.
like
class foo
{
public $foo;
public function method()
{
//code
}
}
$obj_foo = new foo();
$obj_foo->foo = 'class variable';
OR
2) you are defining class variable runtime using _get and _set
class foo
{
public $foo;
public $array = array();
public function method()
{
//code
}
public function __get()
{
//some code
}
public function __set()
{
// some code
}
}
$obj_foo = new foo();
$obj_foo->bar= 'define class variable outside the class';
so in which context your question is talking about?

PHP array references; holding references in an array for later use

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;
}

Categories