2

I've been using __set magic method with protected properties to monitor changes so that my classes know if they have something to save. Is there any way to monitor an array type property for changes? I understand that normally you access the array via a reference and functions like array_push won't trigger the __set method, they'll use a reference to the array.

What I want is basically this:

class Skill{ public $Player, $Name, $Level;}
class Player {
    protected $Name, /*Other properties*/, $Skills /*Array*/
}

I then do tracking on all of the properties in Player to tell me if the persistence needs updated. (Skill would also have this function, but this shows the basic example). Also, I want to force them to remain synchronized (it's a bidirectional relationship).

Is there any way to do this that allows it to behave like an array (don't want to go through making a class just to synchronize those if I don't have to).

2 Answers 2

4

You could extend ArrayObject and proxy append:

class Skills extends ArrayObject
{
    public function append($value)
    {
        // track changes
        parent::append($value);
    }
}
Sign up to request clarification or add additional context in comments.

3 Comments

This looks like exactly what I want. I'll test it's applicability later. Didn't know about ArrayObject. That's really cool. I'd have to alter more than that, and would call the object simply change tracking array and give the constructor a field name to keep synchronized (including removing from the array). In short, this lets me make a simple class to act as a shim which is 98% what I was looking for. (I was hoping for no class at all, do the tracking in the class the array is part of, but the best solution isn't always the one that fits your original pattern).
Glad this helps you, it's a bit sideways from your current implementation but the fact that by definition it ArrayObject implements IteratorAggregate , ArrayAccess , Serializable , Countable means you can use the object like an array (php.net/manual/en/class.arrayaccess.php)
Note that PHP does not treat ArrayObject (or ArrayAccess etc) as an array. array_* methods will fail, the + operator will fail etc.
1

You could look into something like runkit_function_redifine(), but is it really too cumbersome to make helper methods for what you want? e.g.

class Player
{
    private $skills = array();

    protected function addSkill($skill)
    {
        // Do something.
        //

        $this->skills[] = $skill;
    }
}

Or even a wrapper for an array to make it cleaner:

class FancyArray
{
    private $content = array();
    
    public function add($value)
    {
        // Do something.
        //

        $this->content[] = $value;
    }

    public function remove($value){ /* blah */ }

    public function getContent(){ return $this->content; }
}

class Player
{
    protected $skills;

    public function __construct()
    {
        $this->skills = new FancyArray();
        $this->skills->add("Ninjitsu");
    }
}

6 Comments

It's not that it's too cumbersome (annoying, but doable) but that I was really hoping for a better solution. I also don't want to have to write special code for each such item (I'm trying to make this project as reusable as possible because I have two more in the works which will need almost identical architecture). This solution really does have to be developed individually for EACH such array (of which there will be many).
@lassombra I'm not sure why you consider the latter example (FancyArray) as needing to be individualized for each time it is implemented. It is one class that you can recycle.
Missed that part. That (like the answer below) is pretty much what I'm after. As I said there what I was hoping for was a way that player class could monitor (while allowing array functionality to remain) changes to the array without writing a separate class. Fortunately that separate class doesn't have a lot of change required. Now the challenge is that I can only accept one answer...
@lassombra In my opinion the other answer is closer to what you were after, considering you specifically mentioned that you didn't want to set up an intermediary class to manage the state changes; I just wanted to put forward that it's not actually as tedious as you may have thought.
what I'm curious is could I use with the "fancy array" array_push($this->skills, $newskill) and get it to work? If so, then yours is actually more precise to what I'm trying to accomplish.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.