3

I've been looking a way to implement ArrayObject Class to store application configs and i found this implementation in the php manual ( one of the comments )

<?php
 use \ArrayObject;


/**
 *  Singleton With Configuration Info
 */
class Config extends ArrayObject
{
    /**
     *
     * Overwrites the ArrayObject Constructor for
     * Iteration throught the "Array". When the item
     * is an array, it creates another static() instead of an array
     */
    public function __construct(Array $array)
    {
        $this->setFlags(ArrayObject::ARRAY_AS_PROPS);
        foreach($array as $key => $value)
        {
            if(is_array($value))
            {
                $value = new static($value);
            }
            $this->offsetSet($key, $value);
        }
    }

    public function __get($key)
    {
        return $this->offsetGet($key);
    }

    public function __set($key, $value)
    {
        $this->offsetSet($key, $value);
    }
    /**
     * Returns Array when printed (like "echo array();")
     * Instead of an Error
     */
    public function __ToString()
    {
        return 'Array';
    }
}

Usage:

$config = new Config\Config($settings);
$config->uri = 'localhost'; // works
$config->url->uri = 'localhost'; // doesn't work
print_r($config);

I've tried adding to this class the __get and __set it works fine for a simple array but when it comes to multidimensional arrays well ... things are different. I'm getting an error saying that the index is not defined. Can someone help me out please i tried everything i knew googled a lot about it and i didn't find the solution.

I have solved the problem with this class. Later on i will post here a fully working example maybe someone will need it. Thank you everyone for taking time and reading this thread

Update: So what do you think guys? What improvements... changes should i make ?

    public function __construct(Array $properties)
    {
        $this->populateArray($properties);
    }

    private function populateArray(Array $array)
    {
        if(is_array($array))
        {
            foreach($array as $key => $value)
            {
                $this->createProperty($key, $value);
            }
        }
        unset($this->properties);
    }

    private function createProperty($key, $value)
    {
        is_array($value) ? 
            $this->offsetSet($key, $this->createComplexProperty($value))
            : $this->offsetSet($key, $value);
    }

    private function createComplexProperty(Array $array)
    {
        return new Config($array);
    }

    private function createPropertyIfNone($key)
    {
        if($this->offsetExists($key))
            return;

        $this->createProperty($name, array()); 
    }

    public function __get($key)
    {
        $this->createPropertyIfNone($key);
        return $this->offsetGet($key);
    }

    public function __set($key, $value)
    {
        $this->createProperty($key, $value);
    }

    public function __ToString()
    {
        return (string) $value;
    }
}
4
  • $config->url is not defined, so you can't modify it's property uri until it has been created. Commented Jun 5, 2013 at 16:01
  • I know. Well erm from my view the problem is in the __set method which doesn't know how to handle properly $config->var->var = value; Commented Jun 5, 2013 at 16:03
  • 1
    $config->url = new StdClass(); $config->url->uri = "foo"; Commented Jun 5, 2013 at 16:08
  • change return $this->offsetGet($key); to return &$this->offsetGet($key); otherwise, looks good. Commented Jun 6, 2013 at 15:01

1 Answer 1

2

If you want to assume a non-existing key is an array, then this should work.

public function __get($key)
{
    if(!$this->offsetExists($key))
    {
         $this->offsetSet($key,new Array());
    }
    return &$this->offsetGet($key);
}

Usage:

$config = new Config\Config($settings);
$config->url['uri'] = 'localhost';
print_r($config);

EDIT:

Not sure if you have to return a reference or not for this to work.

    return &$this->offsetGet($key);

or this

    return $this->offsetGet($key);
Sign up to request clarification or add additional context in comments.

6 Comments

"Not sure if you have to return a reference or not for this to work." - depends if the object being returned is a primitive type or not. Adding the & will work in both cases anyway.
@TimStamp thanks, do you think it matters if it's a new array or a new object? From a serialization point of view?
Thanks. i'll give it a try and i will be back in a couple of hours.
@MathewFoscarini no it shouldn't matter, but the original question was using object->property syntax, not array[$key] syntax, so that's what I used.
You could use $config->url['a']['b']['c'] and it wouldn't complain, but to use $config->url->a->b->c you'd have to create url, then a, then b in order to avoid errors.
|

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.