Adding data to a reference to change the referenced array in PHP - php

I have built a function to retrieve the last specified key of an array, it references the array and displays it fine, however, when I try to add to the reference variable, it does not affect the referenced array.
Here is my code:
class test {
public function __construct() {
// For testing
$this->keys[] = 'key1';
$this->keys[] = 'key2';
$this->array['key1']['key2'] = 'Hello World';
}
protected function &getArray() {
$array = &$this->array;
foreach($this->keys as $key) {
$array = &$array[$key];
}
return $array;
}
function add() {
$tmpArray = $this->getArray();
print_r($tmpArray);
echo "<br>\n";
$tmpArray = 'Goodbye World';
print_r($tmpArray);
echo "<br>\n";
print_r($this->array);
}
}
$test = new test;
$test->add();
To sum it up, add() and __construct() are for testing. I am trying to add to $this->array with add(). However when I specify $tmpArray = 'Goodbye World' the referenced array $this->array['key1']['key2'] is still Hello World.
Could someone help point me in the right direction?

In order to return references in PHP, you need to use & twice, once in the definition and once more in assignment. You're missing the one in assignment:
$tmpArray = &$this->getArray();
See PHP: Returning References for details and please don't ask why as I'm incapable of generating rationale for PHP behaviour.

Related

Return reference to parameter

is any way to return reference at class parameter or global variable? I try this
class test{
public static $var;
public static function get(&$ref){
$ref = self::$var;
}
}
test::get($ref);
$ref = 'test';
var_dump(test::$var);
it's a basic example, i know, then this example can be use another way, but i need to keep principle
this is my function, where is problem with reference to variable
class mySession{
public static function issetKeys(){
$keys = func_get_args();
$session = &$_SESSION;
$c = 0;
if (is_array($keys)){
foreach ($keys as $val){
if (isset($session[$val])){
$session = &$session[$val];
$c++;
}
else break;
}
}
return $c == count($keys);
}
public static function &get(){
$keys = func_get_args();
$session = &$_SESSION;
if (is_array($keys)){
foreach ($keys as $val){
if (!isset($session[$val])) $session[$val] = Array();
$session = &$session[$val];
}
}
return $session;
}
}
function getValue(){
if (!mySession::issetKeys('p1', 'p2')){
$session = mySession::get('p1', 'p2');
$session = 'string';
}
return mySession::get('p1', 'p2');
}
print_r($_SESSION);
but no variable save in to $_SESSION
No, and why would you ever do such a thing?
If you want to access a public static variable, you'll just write
test::$var = "Hello there handsome";
In the case above you're not passing the reference of this::$var onto $ref, you're letting a reference to $ref contain the value of this::$var. PHP is not C, and references should generally be avoided when not necessary in PHP.
In the general case, a parameter only exists for the duration of the function to which it was passed; when the function returns, the call stack is unwound and the parameter disappears, which means that any reference to a variable that lived in the call stack will no longer be valid (since it points to memory which has been freed and now quite possibly contains something else entirely.)
That said, it is possible to return a reference to a parameter, although this will not work if you try to return a reference to a variable that actually lives in the stack (that is, one that was not passed by reference):
$myGlobal = 10;
// Note the & before the function name
function &returnReference(&$x) {
return $x;
}
// Note the & before the function call
$someValue = &derp($myGlobal);
echo $myGlobal;
$someValue = 0;
echo $myGlobal;

php magic method to catch array_push

I am looking for a way to intercept the action in array_push, because when it will be retrieve it each value of the array has another info like:
class ClassName {
var $test = array();
function __set($attr, $value) {
$this->$attr = 'My extra value'.$value;
}
function index(){
array_push($this->test, "some val");
array_push($this->test, "some other val");
print_r($this->test);
}
}
$o = new ClassName();
$o->index();
And expected to get something like:
Array
(
[0] => My extra value some val
[1] => My extra value some other val
)
But i get:
Array
(
[0] => some val
[1] => some other val
)
Thanks to all
Instead of using an array, you can use a class that implements the ArrayAccess interface. This way you have full control over what occurs when you append to the array.
http://php.net/manual/en/class.arrayaccess.php
The drawback is that not all array functions will work on the object (ie sorting etc), but you can push like so:
$object[] = 'new value';
The alternative is to simply make a wrapper function for adding to the array.
public function addToArray($key, $value) {
if ($key === null) {
$this->test[] = 'My extra value ' . $value;
} else {
$this->test[$key] = 'My extra value ' . $value;
}
}
I don't totally understand what you're asking, but are you trying to do this:
$this->test['key'] = "some val";
That will allow you to setup your own output nicely. Because array_push() will throw on another nested level.
Update: Maybe something along these lines?
function push($prefix)
{
$refl = new ReflectionClass($this);
$prop = $refl->getProperties();
foreach($prop as $p) {
$this->{$p} = $prefix . $this->{$p};
}
}
To achieve what you're looking for, I suggest you create yourself a function that prefixes any value independent to it's use:
function prefix($value) {
return 'My extra value '.$value;
}
You can then make use of that function inside the index() function:
function index()
{
$test = array("some val", "some other val");
foreach($test as $value)
{
$this->test[] = $this->prefix($value);
}
print_r($this->test);
}
From the PHP manual:
__set() is run when writing data to inaccessible properties.
__get() is utilized for reading data from inaccessible properties.
This is only called on reading/writing inaccessible properties. Your property however is public, which means it is accessible. Changing the access modifier to protected solves the issue.
Try this:
class ClassName {
private $test = array();
public function __set($attr, $value) {
$this->test[$attr] = $value;
}
public function __get($attr) {
return $this->test[$attr];
}
public function index(){
array_push($this->test, "some val");
array_push($this->test, "some other val");
return $this->test;
}
}
$o = new ClassName();
$o->setData = 'My extra value';
print_r($o->index());
The PHP manual says the following about magic methods:
__set() is run when writing data to inaccessible properties. (Emphasis mine)
Because the test property is inside the method and the function that is accessing it is inside the method, the function will see the variable directly and will not use the magic setter.
Additionally, even if you try to use array_push on the magic setter outside the class itself, that still will not work. You will get the error array_push() expects parameter 1 to be array, object given.
If you want to support array_push, you should write your own push($item) method:
function push($item) {
$this->test[] = 'My extra value' . $item
}
or:
function push() {
$items = func_get_args();
// Using array_map is one of the many ways to do this.
// You could also do it with a simpler `foreach` but I like this way.
$prepend = function($item) {
return 'My extra value' . $item;
};
$items = array_map($prepend, $items);
array_push($this->test, $items);
}
if you want to support pushing multiple items at once.

Show all public attributes (name and value) of an object

This thread didn't helped me.
If I use
$class_vars = get_class_vars(get_class($this));
foreach ($class_vars as $name => $value) {
echo "$name : $value\n";
}
I get
attrib1_name : attrib2_name : attrib3_name
There are no values. Also a private attribute is shown, which I don't want.
If I use
echo "<pre>";
print_r(get_object_vars($this));
echo "</pre>";
I get
Array
(
[atrrib1_name] => attrib1_value
[attrib2_name] => attrib2_value
)
Here again I have a private attribute and all sub attributes. But this time I have the values. How can I constrain this to one level?
Isn't there a possibility to show all public attributes with their values of an object?
You are seeing non-public properties because get_class_vars works according to current scope. Since you are using $this your code is inside the class, so the non-public properties are accessible from the current scope. The same goes for get_object_vars which is probably a better choice here.
In any case, a good solution would be to move the code that retrieves the property values out of the class.
If you do not want to create a free function for that (why? seriously, reconsider!), you can use a trick that involves an anonymous function:
$getter = function($obj) { return get_object_vars($obj); };
$class_vars = $getter($this);
See it in action.
Update: Since you are in PHP < 5.3.0, you can use this equivalent code:
$getter = create_function('$obj', 'return get_object_vars($obj);');
$class_vars = $getter($this);
You can do this easily with php Reflection api
Extending Mr.Coder's answer, here is a snippet to fetch the public attributes of the object (name and value) as an array
public function getPublicProperties()
{
$results = [];
$reflectionObject = (new ReflectionObject($this));
$properties = $reflectionObject->getProperties(ReflectionProperty::IS_PUBLIC);
foreach ($properties as $property) {
$results[$property->getName()] = $property->getValue($this);
}
return $results;
}
Use get_object_vars.
see: http://dk.php.net/manual/en/function.get-object-vars.php
I Fully recognize what you are trying to achieve so why not have something external like this to help out... (pasted from PHPFiddle)
<?php
final class utils {
public static function getProperties(& $what) {
return get_object_vars($what);
}
}
class ball {
var $name;
private $x, $y;
function __construct($name,$x,$y) {
}
function publicPropsToArray() {
return utils::getProperties($this);
}
function allPropsToArray() {
return get_object_vars($this);
}
}
$ball1 = new ball('henry',5,6);
//$ball2 = new ball('henry',3,4);
echo "<pre>";
print_r($ball1->publicPropsToArray());
echo "\r\n\r\n";
print_r($ball1->allPropsToArray());
echo "\r\n\r\n";
?>
This way I can both access all properties of the object or for something such as a database access layer or similarly for a function that send "safe" data to a view or another un-privileged model I can send just the public properties, but have the behaviour defined within the object.
Sure this leads to coupling with a utility class, but to be fair not all couplings are bad, some are nesecarry to achieve an end goal, dont get bogged down by these things

ArrayAccess in PHP -- assigning to offset by reference

First, a quote from the ole' manual on ArrayAccess::offsetSet():
This function is not called in assignments by reference and otherwise indirect changes to array dimensions overloaded with ArrayAccess (indirect in the sense they are made not by changing the dimension directly, but by changing a sub-dimension or sub-property or assigning the array dimension by reference to another variable). Instead, ArrayAccess::offsetGet() is called. The operation will only be successful if that method returns by reference, which is only possible since PHP 5.3.4.
I'm a bit confused by this. It appears that this suggests that (as of 5.3.4) one can define offsetGet() to return by reference in an implementing class, thus handling assignments by reference.
So, now a test snippet:
(Disregard the absence of validation and isset() checking)
class Test implements ArrayAccess
{
protected $data = array();
public function &offsetGet($key)
{
return $this->data[$key];
}
public function offsetSet($key, $value)
{
$this->data[$key] = $value;
}
public function offsetExists($key) { /* ... */ }
public function offsetUnset($key) { /* ... */ }
}
$test = new Test();
$test['foo'] = 'bar';
$test['foo'] = &$bar; // Fatal error: Cannot assign by reference to
// overloaded object in
var_dump($test, $bar);
Ok, so that doesn't work. Then what does this manual note refer to?
Reason
I'd like to permit assignment by reference via the array operator to an object implementing ArrayAccess, as the example snippet shows. I've investigated this before, and I didn't think it was possible, but having come back to this due to uncertainty, I (re-)discovered this mention in the manual. Now I'm just confused.
Update: As I hit Post Your Question, I realized that this is likely just referring to assignment by reference to another variable, such as $bar = &$test['foo'];. If that's the case, then apologies; though knowing how, if it is at all possible, to assign by reference to the overloaded object would be great.
Further elaboration: What it all comes down to, is I would like to have the following method aliases:
isset($obj[$key]); // $obj->has_data($key);
$value = $obj[$key]; // $obj->get_data($key);
$obj[$key] = $value; // $obj->set_data($key, $value);
$obj[$key] = &$variable; // $obj->bind_data($key, $variable);
// also, flipping the operands is a syntactic alternative
$variable = &$obj[$key]; // $obj->bind_data($key, $variable);
unset($obj[$key]); // $obj->remove_data($key);
As far as has, get, set, and remove go, they're no problem with the supported methods of ArrayAccess. The binding functionality is where I'm at a loss, and am beginning to accept that the limitations of ArrayAccess and PHP are simply prohibitive of this.
What the manual is referring to are so called "indirect modifications". Consider the following script:
$array = new ArrayObject;
$array['foo'] = array();
$array['foo']['bar'] = 'foobar';
In the above script $array['foo'] = array(); will trigger a offsetSet('foo', array()). $array['foo']['bar'] = 'foobar'; on the other hand will trigger a offsetGet('foo'). Why so? The last line will be evaluated roughly like this under the hood:
$tmp =& $array['foo'];
$tmp['bar'] = 'foobar';
So $array['foo'] is first fetched by ref and then modified. If your offsetGet returns by ref this will succeed. If not you'll get some indirect modification error.
What you want on the other hand is the exact opposite: Not fetch a value by reference, but assign it. This would theoretically require a signature of offsetSet($key, &$value), but practically this is just not possible.
By the way, references are hard to grasp. You'll get lots of non-obvious behavior and this is especially true for array item references (those have some special rules). I'd recommend you to just avoid them altogether.
This does not work with ArrayAccess, you could add yourself a public function that allows you to set a reference to an offset (sure, this looks different to using array syntax, so it's not really a sufficient answer):
class Test implements ArrayAccess{
protected $_data = array();
public function &offsetGet($key){
return $this->_data[$key];
}
...
public function offsetSetReference($key, &$value)
{
$this->_data[$key] = &$value;
}
}
$test = new Test();
$test['foo'] = $var = 'bar';
$test->offsetSetReference('bar', $var);
$var = 'foo';
echo $test['bar']; # foo
$alias = &$test['bar'];
$alias = 'hello :)';
echo $var; # hello :)
Probably such a function was forgotten when ArrayAccess was first implemented.
Edit: Pass it as "a reference assignment":
class ArrayAccessReferenceAssignment
{
private $reference;
public function __construct(&$reference)
{
$this->reference = &$reference;
}
public function &getReference()
{
$reference = &$this->reference;
return $reference;
}
}
class Test implements ArrayAccess{
...
public function offsetSet($key, $value){
if ($value instanceof ArrayAccessReferenceAssignment)
{
$this->offsetSetReference($key, $value->getReference());
}
else
{
$this->_data[$key] = $value;
}
}
Which then works flawlessly because you implemented it. That's probably more nicely interfacing than the more explicit offsetSetReference variant above:
$test = new Test();
$test['foo'] = $var = 'bar';
$test['bar'] = new ArrayAccessReferenceAssignment($var);
$var = 'foo';
echo $test['bar']; # foo
$alias = &$test['bar'];
$alias = 'hello :)';
echo $var; # hello :)

php reference versus value

class Test {
private $arr;
function __construct() {
$this->arr = array('test');
}
function getArr() {
return $this->arr;
}
}
$a = new Test();
$b = $a->getArr();
$b[0][0] = 'a';
$s = $a->getArr();
echo $s[0]
Why does this echo test instead of aest? Does PHP copy the array and the contents of the array when returning it? How do I get an array in which I can change the strings and have it reflected in the object?
By returning and assigning by reference:
class Test {
//...
function &getArr() {
return $this->arr;
}
}
$a = new Test();
$b =& $a->getArr();
$b[0][0] = 'a';
$s = $a->getArr();
echo $s[0];
Does PHP copy the array and the contents of the array when returning it?
From the point of view of the programmer, it works as if returning would copy the value, except when returning by reference. In terms of implementation, there are optimizations that avoid this happens, as long as it has no impact in the behavior of the script.

Categories