TomWright/PHPConfig

View on GitHub
src/Config.php

Summary

Maintainability
A
1 hr
Test Coverage
<?php


namespace TomWright\PHPConfig;

use TomWright\PHPConfig\ConfigReader\ConfigReader;

class Config
{

    /**
     * @var array|object
     */
    protected $store;

    /**
     * @var string
     */
    protected $separator;


    /**
     * Config constructor.
     * @param array $options
     */
    public function __construct(array $options = [])
    {
        $this->setSeparator('.');
        $this->store = [];

        if (array_key_exists('separator', $options) && is_string($options['separator'])) {
            $this->setSeparator($options['separator']);
        }
        if (array_key_exists('store', $options) && is_array($options['store'])) {
            $this->store = $options['store'];
        }
        if (array_key_exists('readers', $options) && is_array($options['readers'])) {
            foreach ($options['readers'] as $reader) {
                $this->read($reader);
            }
        }
    }


    /**
     * Processes the specified ConfigReader.
     * @param ConfigReader $reader
     */
    public function read(ConfigReader $reader)
    {
        $reader->process($this);
    }


    /**
     * @return array
     */
    public function getAll(): array
    {
        return $this->store;
    }


    /**
     * @param string $key
     * @return string
     */
    private function stripMultipleSeparatorsFromKey(string $key): string
    {
        $separator = $this->getSeparator();
        $safeSeparator = preg_quote($separator);
        return preg_replace('/' . $safeSeparator . '{2,}/', $this->getSeparator(), $key);
    }


    /**
     * @param array $data
     * @return Config
     */
    public function putAll(array $data): Config
    {
        foreach ($data as $key => $value) {
            $this->put($key, $value);
        }
        return $this;
    }


    /**
     * @param string $key
     * @param mixed $value
     * @return $this
     */
    public function put(string $key, $value): Config
    {
        $key = $this->stripMultipleSeparatorsFromKey($key);
        $keys = explode($this->getSeparator(), $key);
        $totalKeys = count($keys);

        $store =& $this->store;

        $currentKeyIndex = 0;
        foreach ($keys as $currentKey) {
            $currentKeyIndex++;
            $isLastKey = ($currentKeyIndex === $totalKeys);
            if ($isLastKey) {
                $store[$currentKey] = $value;
                break;
            }

            $subItem = $this->getFromStore($currentKey, $store, null);
            if ($subItem === null || (! is_array($subItem) && ! is_object($subItem))) {
                $store[$currentKey] = [];
            }

            $store =& $store[$currentKey];
        }

        return $this;
    }


    /**
     * Returns whether or not the key contains the separator.
     * @param string $key
     * @return bool
     */
    private function keyContainsSeparator(string $key): bool
    {
        return strpos($key, $this->getSeparator()) !== false;
    }


    /**
     * Returns the part of the key before the first separator.
     * @param string $key
     * @return string
     */
    private function getFirstKeyFromKey(string $key): string
    {
        $key = $this->stripMultipleSeparatorsFromKey($key);
        $separatorPos = strpos($key, $this->getSeparator());
        if ($separatorPos === false) {
            return $key;
        }
        return substr($key, 0, $separatorPos);
    }


    /**
     * Returns the remaining part of the key after the first separator.
     * @param string $key
     * @return string
     */
    private function getSubsequentKeyFromKey(string $key): string
    {
        $key = $this->stripMultipleSeparatorsFromKey($key);
        $separatorPos = strpos($key, $this->getSeparator());
        if ($separatorPos === false) {
            return $key;
        }
        return substr($key, $separatorPos + 1);
    }


    /**
     * @param string $key
     * @param mixed $default
     * @param array|object $store
     * @return mixed|null
     */
    public function get(string $key, $default = null, $store = null)
    {
        if ($store === null) {
            $store = $this->getAll();
        }

        if (! $this->keyContainsSeparator($key)) {
            return $this->getFromStore($key, $store);
        }

        $firstKey = $this->getFirstKeyFromKey($key);

        return $this->get(
            $this->getSubsequentKeyFromKey($key),
            $default,
            $this->getFromStore($firstKey, $store)
        );
    }


    /**
     * @param string $key
     * @param array|object $store
     * @param null $default
     * @return mixed|null
     */
    private function getFromStore(string $key, $store, $default = null)
    {
        if (is_object($store) && isset($store->{$key})) {
            return $store->{$key};
        }
        if (is_array($store) && array_key_exists($key, $store)) {
            return $store[$key];
        }
        return $default;
    }


    /**
     * @return string
     */
    public function getSeparator(): string
    {
        return $this->separator;
    }


    /**
     * @param string $separator
     * @return $this
     */
    public function setSeparator(string $separator): Config
    {
        $this->separator = $separator;
        return $this;
    }

}