Skip to content

Instantly share code, notes, and snippets.

@furkanmustafa
Last active December 17, 2015 12:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save furkanmustafa/5607227 to your computer and use it in GitHub Desktop.
Save furkanmustafa/5607227 to your computer and use it in GitHub Desktop.
Simple JSON Configuration Manager
<?php
/**
* A Simple JSON Site-wide Configuration manager with key-path access, overriding, inline variables, etc..
* It might look ugly for some people, most important goal with this is simplicity in the code that uses this.
*
* You can find usage sample at the end of the file
*
* @author Furkan Mustafa <furkan@fume.jp>
* @version 0.1.3
* @copyright no, licensed by GPL, please read http://www.gnu.org/licenses/gpl.html
*/
namespace FM;
class ConfigurationManager {
public static $SharedInstance = null;
public static $Instances = array();
private $instanceNumber = -1;
private $___conf = array();
function __construct($configFile = false, $shared = true) {
if ($configFile) {
$this->overrideWithContentsOfFile($configFile);
}
self::$Instances[] = &$this;
$this->instanceNumber = count(self::$Instances) - 1;
if ($shared)
$this->setShared();
}
function overrideWithData($data, $relativeToKeyPath = false, $depth = 0) {
$depth++;
if ($depth==1 && !is_array($data))
return false; // you shouldn't
$keyPath = array();
if ($relativeToKeyPath)
$keyPath = !is_array($relativeToKeyPath) ? explode('.', $relativeToKeyPath) : $relativeToKeyPath;
if (is_array($data)) {
foreach ($data as $key => $value) {
if ($key=='@global') {
$this->overrideWithData($value, false, $depth);
continue;
}
$subKeyPath = $keyPath;
array_push($subKeyPath, $key);
$this->overrideWithData($value, $subKeyPath, $depth);
}
return;
}
$this->setConf($keyPath, $data);
}
function overrideWithContentsOfFile($file, $relativeToKeyPath = false) {
if (!file_exists($file))
throw new \Exception('Config file '.$file.' doesn\'t exist.');
$this->overrideWithData($this->decode(file_get_contents($file)), $relativeToKeyPath);
}
function setShared() {
self::$SharedInstance = &$this;
}
private function decode($configFileContents) {
// in future this might be pluggable
return json_decode($configFileContents, true);
}
private function filter($data) {
if (is_string($data)) {
while (preg_match('/\$\{([^\}]+)\}/', $data, $m)) {
$data = str_replace($m[0], $this->getConf($m[1]), $data);
}
}
else if (is_array($data)) {
foreach ($data as $n => $i) {
$data[$n] = $this->filter($i);
}
}
return $data;
}
public function getConf($itemPath) {
return $this->conf($itemPath);
}
public function setConf($itemPath, $value) {
return $this->conf($itemPath, $value);
}
public function conf($keyPath, $newValue = null) {
if (!$keyPath)
return $this->filter($this->___conf);
$value = &self::GetKeyPath($this->___conf, $keyPath, $newValue!==null);
if ($newValue!==null) {
$value = $newValue;
return;
}
// filters for values maybe
return $this->filter($value);
}
private static function &GetKeyPath(&$arr, $keyPath, $create = false) {
$keyPathParts = !is_array($keyPath) ? explode('.', $keyPath) : $keyPath;
$subjectKey = array_shift($keyPathParts);
$array_append = false;
if (preg_match('/^([^\[]+)\[([^\]]*)\]$/', $subjectKey, $match)) {
if (isset($match[2]) && strlen(trim($match[2]))>0) {
array_unshift($keyPathParts, $match[2]);
} else if ($create) {
// .. this is for setting/appending
$array_append = true;
}
$subjectKey = $match[1];
}
if (!isset($arr[$subjectKey])) {
if (!$create) {
$rv = false;
return $rv;
}
$arr[$subjectKey] = null;
if ($array_append) {
$arr[$subjectKey] = array();
array_unshift($keyPathParts, '0'); // first element of array
}
if (count($keyPathParts)==0) {
return $arr[$subjectKey];
}
return self::GetKeyPath($arr[$subjectKey], $keyPathParts, true);
}
if ($array_append) {
$count = count($arr[$subjectKey]);
$arr[$subjectKey][$count] = null;
$item = &$arr[$subjectKey][$count];
} else {
$item = &$arr[$subjectKey];
}
if (count($keyPathParts)==0)
return $item;
return self::GetKeyPath($item, $keyPathParts, $create);
}
public function registerGlobalFunction($name) {
$number = $this->instanceNumber;
$code = 'function '.$name.'($keypath = false, $value = null) { return \FM\ConfigurationManager::$Instances['.$number.']->conf($keypath, $value); }';
eval($code);
}
}
/*
// Sample Usage:
$configurationManager = new \FM\ConfigurationManager(__DIR__.'/../config.json');
$configurationManager->registerGlobalFunction('conf');
//$configurationManager->overrideWithContentsOfFile(__DIR__.'/../database.json', 'database');
// detected configuration
conf('path.root', realpath(__DIR__.'/..'));
conf('naber.iyilik', 5);
conf('naber.senden', 'Benden en az ${naber.iyilik} kadar iyiyim be abi');
conf('naber.iyilik', 15);
conf('path.include[].path', 'hey');
conf('path.include[].path', 'mey');
print_r(conf());
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment