2

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

user115561
  • 3,631
  • 2
  • 14
  • 8
  • 1
    Hmm, I have problems to understand what your issue is. Are you concerned that your code actually works? Because this does not look wrong and you don't need to intercept anything. – hakre Apr 10 '12 at 18:07
  • @hakre: He's trying to add a prefix to all elements added to the `$test` property of `ClassName`. The code he has does not work. – gen_Eric Apr 10 '12 at 18:14
  • 1
    `test` is public therefore accessible so `__set()` won't be called. I'm not 100% certain but internally PHP might call get on your `test` property first.. append the value with `array_push()` then call set with the new value. Not sure if that helps.. just a hidden behavior of php. – Mike B Apr 10 '12 at 18:15
  • This code doesnt throw the array as i expected, i will acomplish the explination – user115561 Apr 10 '12 at 18:17
  • I think @mike is on the money: change `var $test` to `private $test` – Frank Farmer Apr 10 '12 at 18:23
  • 1
    sorry Mike B, i dont understand your suggest :S, i have made `test` private ( `private $test` ), but still, php doesnt get in to the __set function – user115561 Apr 10 '12 at 18:23
  • private properties are still accessible within the same class. You won't be able to trigger `__set()` like that. The only way to trigger it within the same class is to call it explicitly `$this->__set('test', 'my values');` or remove/rename the property declaration making it inaccessible. A lot of times these kinds of variables will be created as protected properties prefixed with an underscore so `__set()` will be triggered. i.e. have a property `protected $_test` then when you go to `$this->test = 'my values'` have `__set()` catch that and add an underscore to the name and set the value. – Mike B Apr 10 '12 at 18:30
  • `__set()` only triggers when setting a value of an undefined property in a class. You're modifying a value, not setting one. – gen_Eric Apr 10 '12 at 18:36
  • I think it cant be resolve as i was thinking as Rocket said, thanks every body for your help – user115561 Apr 10 '12 at 18:55

5 Answers5

2

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;
   }
}
dqhendricks
  • 19,030
  • 11
  • 50
  • 83
0

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};
    }
}
JREAM
  • 5,741
  • 11
  • 46
  • 84
  • He's trying to add a prefix to all elements added to a class property (using `__set`). – gen_Eric Apr 10 '12 at 18:14
  • As Rocket said, but the prefix its an example, in fact is gonna be other thing but for the help propouses is the same, The problem is that there is a program already made, that uses a lot the array push method, and i want to append some thing else, but __get and __set doesnt work for array push, event when a variable of the class its been affected – user115561 Apr 10 '12 at 18:14
  • Can be using some thing else than __set or __get, but make the prefix thing – user115561 Apr 10 '12 at 18:15
0

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);
}
hakre
  • 193,403
  • 52
  • 435
  • 836
0

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());
SteveM
  • 19
  • 2
0

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.

Moshe Katz
  • 15,992
  • 7
  • 69
  • 116