<?php
/**
 * Link         :   http://www.phpcorner.net
 * User         :   qingbing<780042175@qq.com>
 * Date         :   2018-12-05
 * Version      :   1.0
 */

// 定义环境变量
defined("PHP_ENV") or define("PHP_ENV", "product");
// 定义配置存放目录
defined("CONFIG_PATH") or define("CONFIG_PATH", dirname(realpath(".")) . "/conf");
// 定义配置缓存的存放目录
defined("RUNTIME_PATH") or define("RUNTIME_PATH", dirname(realpath(".")) . "/runtime");

/**
 * Class Config
 */
class Config
{
    /* @var Config[] */
    private static $instances = [];

    /**
     * 配置获取实例
     * @param string $filename 配置分组名
     * @param string $type 分类类型
     * @param bool $cache 是否缓存
     * @return Config|mixed
     */
    public static function getInstance($filename = null, $type = null, $cache = true)
    {
        if (null === $filename) {
            $filename = 'application';
        }
        $uniqid = md5($filename . $type);
        if (!isset(self::$instances[$uniqid])) {
            return self::$instances[$uniqid] = new self($uniqid, $filename, $type, $cache);
        }
        return self::$instances[$uniqid];
    }

    /* @var string filename和type组合的唯一键 */
    private $_uniqid;
    /* @var string 文件名 */
    private $_filename;
    /* @var string 只需要的键值 */
    private $_type;
    /* @var array 最终数据 */
    private $_data;

    /**
     * 构造函数
     * Config constructor.
     * @param string $filename 分组名
     * @param string $type 类别
     * @param bool $cache 是否缓存
     */
    private function __construct($uniqid, $filename, $type = null, $cache = true)
    {
        $this->_uniqid = $uniqid;
        $this->_filename = $filename;
        $this->_type = $type;
        if ($cache && file_exists($file = $this->cacheFile())) {
            $this->_data = require($file);
        } else {
            $mData = $this->fileData($this->configFile());
            $eData = $this->fileData($this->envFile());
            try {
                if (empty($this->_type)) {
                    $R = $mData;
                    if (isset($eData[$this->_filename])) {
                        $R = array_cover($R, $eData[$this->_filename]);
                    }
                } else if (isset($mData[$this->_type]) && !empty($mData[$this->_type])) {
                    $R = $mData[$this->_type];
                    if (isset($eData[$this->_filename][$this->_type])) {
                        $R = array_cover($R, $eData[$this->_filename][$this->_type]);
                    }
                } else {
                    $R = [];
                }
                $this->_data = $R;
                if ($file) {
                    file_put_contents($file, '<?php return ' . var_export($R, true) . ';');
                }
            } catch (Exception $e) {

            }
        }
    }

    /**
     * 设置并获取配置文件的缓存目录
     * @return string
     */
    protected function cachePath()
    {
        $path = RUNTIME_PATH . '/config';
        if (!is_dir($path)) {
            mkdir($path, 0777);
        }
        return $path;
    }

    /**
     * 获取分组名对应的缓存文件
     * @return string
     */
    protected function cacheFile()
    {
        return $this->cachePath() . '/' . $this->_uniqid . '.inc';
    }

    /**
     * 获取配置组文件地址
     * @return string
     */
    protected function configFile()
    {
        return CONFIG_PATH . "/{$this->_filename}.php";
    }

    /**
     * 获取环境变量文件地址
     * @return string
     */
    protected function envFile()
    {
        return CONFIG_PATH . "/env/php-" . PHP_ENV . ".php";
    }

    /**
     * 获取文件中返回的数组数据
     * @param string $file
     * @return array|mixed
     */
    protected function fileData($file)
    {
        if (!file_exists($file)) {
            return [];
        }
        $data = require $file;
        if (!is_array($data)) {
            return [];
        }
        return $data;
    }

    /**
     * 返回所有配置
     * @return array
     */
    public function getAll()
    {
        return $this->_data;
    }

    /**
     * 返回具体id的配置信息
     * @param string $id
     * @return string
     */
    public function getById($id)
    {
        return isset($this->_data[$id]) ? $this->_data[$id] : null;
    }
}