Last active
December 17, 2015 12:09
-
-
Save furkanmustafa/5607227 to your computer and use it in GitHub Desktop.
Simple JSON Configuration Manager
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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