3
\$\begingroup\$

I made this small class the allows me to navigate large arrays using something like: key1/key2/key3

I made this because I was creating large arrays to hold my classes configs and needed a simpler way of accessing the data.

Here is the project: - https://github.com/AntonioCS/settingsManager

The class is here:

<?php

namespace SettingsManager;

/**
 * To ease access of config data
 *  
 */
class settingsManager {

    /**
     * Where that data will be held
     * @var array
     */
    private $_data = array();

    /**
     * To speed up access
     * @var array
     */
    private $_cache = array();

    /**
     * To use or not to use the cache (that is the question!!)     
     * @var bool
     */
    private $_useCache = true;

    /**
     * Allow the settings to be changed 
     * @var bool
     */
    private $_allowChange = false;    

    /**
     * Initialize the class and set the settings data
     * 
     * @param array $data
     * @param bool $allowChange - Default false
     * @param bool $useCache - Default true
     */
    public function __construct($data, $allowChange = false, $useCache = true) {
        $this->_data = $data;      
        $this->_allowChange = $allowChange; 
        $this->_useCache = $useCache;
    }

    /**
    * Access the _config property and return specified section
    * 
    * @param string $section - Example: section/config
    * @return mixed
    * 
    * @throws OutOfBoundsException 
    */
    public function get($section) {
        if ($this->_useCache && isset($this->_cache[$section]))
            return $this->_cache[$section];

        return $this->_engine($section);    
    }

    /**
     * If allowed this will set the given section to the given value
     * 
     * @param string $section
     * @param mixed $value
     * @return mixed 
     * 
     * @throws OutOfBoundsException
     * @throws TryToChangeImmutableObjectException
     */
    public function set($section, $value) {
        return $this->_engine($section, $value);
    }

    /**
     * Check and see if section exists
     * 
     * @param string $section 
     * @return bool
     */
    public function exists($section) {       
        try {
            $this->get($section);
        }
        catch (\OutOfBoundsException $e) {
            return false;
        }

        return true;
    }

    /**
     * Clear cache 
     */
    public function clearCache() {
        $this->_cache = array();
    }

    /**
     * Get/Set a value from the settings property
     * 
     * @param string $section
     * @param mixed $value
     * @return mixed
     * 
     * @throws \OutOfBoundsException
     * @throws TryToChangeImmutableObjectException
     */
    private function _engine($section, $value = null) {

        if ($value && !$this->_allowChange)
            throw new TryToChangeImmutableObjectException();

        $sections = explode('/',$section);
        $tempSectionData = &$this->_data; 

        foreach ($sections as $currentSection) {        
            if (!isset($tempSectionData[$currentSection])) {
                throw new \OutOfBoundsException($section . ' - ' . $currentSection);
            }       

            $tempSectionData = &$tempSectionData[$currentSection];
        }

        if ($value) {
            $tempSectionData = $value;            
            if ($this->_useCache && isset($this->_cache[$section]))
                unset($this->_cache[$section]);
        }   

        return $tempSectionData;
    }            
}

class TryToChangeImmutableObjectException extends \Exception {}

So a simple use case would be:

<?php

$_testData = array(
        'data' => 1,
        'data2' => array(
            'date3' => 4
            )        
    );

$object = new SettingsManager\settingsManager($_testData);

echo $object->get('data2/date3'); //Should echo 4

Any comments or suggestions?

\$\endgroup\$
1
\$\begingroup\$

I swear I've seen this approach somewhere before, I'll have to see if I can't find that link again. Anyways, a couple of drawbacks of this approach is lack of portability and lack of familiarity. Portability meaning no one can move that code without also taking that class with it. Familiarity meaning not well known. Though I do have to admit, I would love to see PHP natively adopt an xpath navigation style for arrays. Would make life so much easier.

I would steer clear of using method names that are similar to PHP's reserved words. In other words get() would be better as fetch() or query() or something similar. Same for set() and exists(). This helps avoid confusion. The only time "get" or "set" is generally used is when defining a specific getter or setter such as getPath() or setPath(). _engine() is also oddly named, would expect _parse() or _evaluate() or something similar. But this is all stylistic.

The only "real" criticism I have for this bit of code is that occasionally you neglect your braces. These are always important. Please remember them.

if ($this->_useCache && isset($this->_cache[$section]))
    return $this->_cache[$section];

The only improvement I can suggest is to abstract your caching to a different class in case you ever decide to change it.

Have you thought about creating relative pathing support? In other words, using your example, you could get the same results by using just "//date3"? The previous project I was thinking about did this and I was quite impressed. If there was more than a single match it would return a set of results. It also knew only to look one level deep relative to the array passed to it.

Overall, very impressive though. Sorry I couldn't add more to it.

\$\endgroup\$
1
  • \$\begingroup\$ Thanks for this. I will try to improve where you suggested. \$\endgroup\$ – AntonioCS Jul 20 '12 at 8:33

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.