add configuration systeme

This commit is contained in:
Nicolas Le Goff
2011-12-26 22:21:32 +01:00
parent 02c9d77af6
commit c5a6d65fd1
7 changed files with 674 additions and 4 deletions

View File

@@ -24,7 +24,13 @@ require_once __DIR__ . '/../../vendor/Silex/vendor/pimple/lib/Pimple.php';
class Core extends \Pimple
{
public function __construct($isDev = false)
/**
*
* @var Core\Configuration
*/
private $conf;
public function __construct($env)
{
/**
@@ -32,14 +38,18 @@ class Core extends \Pimple
*/
static::initAutoloads();
$this->conf = new Core\Configuration($env);
$this['Version'] = $this->share(function()
{
return new Core\Version();
});
$this['EM'] = $this->share(function()
$conf = $this->conf;
$this['EM'] = $this->share(function() use ($conf)
{
$doctrine = new Core\Service\Doctrine();
$doctrine = new Core\Service\Doctrine($conf->getDoctrineConf());
return $doctrine->getEntityManager();
});
@@ -91,6 +101,20 @@ class Core extends \Pimple
return;
}
private function init()
{
if ($this->conf->debug)
{
ini_set('display_errors', 1);
error_reporting(E_ALL);
\Symfony\Component\HttpKernel\Debug\ErrorHandler::register();
}
else
{
ini_set('display_errors', 0);
}
}
/**
*
* @return Request
@@ -291,4 +315,14 @@ class Core extends \Pimple
return;
}
public function getEnv()
{
return $this->env;
}
public function isDebug()
{
return $this->conf->debug;
}
}

View File

@@ -0,0 +1,203 @@
<?php
/*
* This file is part of Phraseanet
*
* (c) 2005-2010 Alchemy
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\Phrasea\Core;
/**
*
* @package
* @license http://opensource.org/licenses/gpl-3.0 GPLv3
* @link www.phraseanet.com
*/
class Configuration
{
public $debug = false;
protected $dbConf;
protected $installed;
public function __construct($envName)
{
$specifications = new Configuration\PhraseaConfiguration();
$parser = new Configuration\Parser\Yaml();
$handler = new Configuration\EnvironnementHandler($specifications, $parser);
$configuration = $handler->handle($envName);
var_dump($configuration);
exit;
}
public function getMainConfigFileName()
{
return 'config';
}
/**
*
* @return \SplFileObject
*/
public function getMainConfigFile()
{
return $this->mainConfigFile;
}
public function getDoctrineConf()
{
return (array) $this->dbConf;
}
public function isInstalled()
{
return $this->installed;
}
private function loadEnvironnments($env)
{
$filename = $this->loadFileName($env);
if (!is_file($filename))
throw new \InvalidArgumentException(sprintf('Config file %s do not exist', $filename));
$envConf = \Symfony\Component\Yaml\Yaml::parse($filename);
$this->envsConf[] = $envConf;
if (isset($envConf["extends"]))
{
$this->loadEnvironnments($envConf["extends"]);
}
else
{
return;
}
}
private function substituteEnvConfToMainConf(Array $mainConf)
{
foreach (array_reverse($this->envsConf) as $env)
{
$this->conf = array_replace_recursive($this->conf, $env);
}
$this->conf = array_replace_recursive($mainConf, $this->conf);
}
private function loadFileName($env = 'main')
{
if ('main' === $env)
{
return sprintf('%s/%s.yml', $this->configFilePath, $this->getMainConfigFileName());
}
else
{
return sprintf('%s/%s_%s.yml', $this->configFilePath, $this->getMainConfigFileName(), $env);
}
}
private function getNoneReplacedPath()
{
return array(
array('doctrine', 'dbal')
);
}
private function process($env)
{
$mainConf = \Symfony\Component\Yaml\Yaml::parse($this->loadFileName());
$this->loadEnvironnments($env);
$this->substituteEnvConfToMainConf($mainConf);
$path = array();
$excludedPath = $pathToProcess = $this->getNoneReplacedPath();
$confToChange = $this->conf;
foreach (array_reverse($this->envsConf) as $conf)
{
foreach ($pathToProcess as $key => $thePath)
{
$replaceValue = $this->getDataPath($conf, $thePath);
if (null !== $replaceValue)
{
$map = function($item, $key) use (&$confToChange, $replaceValue, &$map, &$path, $thePath)
{
if (count(array_diff($path, $thePath) === 0))
{
$path[] = $key;
$replace = function(&$searchArray, $path, $value, $depth = 0) use (&$replace)
{
foreach ($searchArray as $k => $v)
{
if ($k === $path[$depth])
{
array_shift($path);
if (is_array($v) && count($path) !== 0)
{
$replace(&$searchArray[$k], $path, $value, $depth++);
}
else
{
$searchArray[$k] = $value;
}
}
}
};
$replace($confToChange, $path, $replaceValue);
}
elseif (is_array($item))
{
$path[] = $key;
array_walk($item, $map);
}
};
array_walk($this->conf, $map);
unset($pathToProcess[$key]);
}
}
}
}
public function getDataPath(Array $data, Array $path)
{
$found = true;
for ($x = 0; ($x < count($path) && $found); $x++)
{
$key = $path[$x];
if (isset($data[$key]))
{
$data = $data[$key];
}
else
{
$found = false;
}
}
if ($found)
return $data;
else
return null;
}
}

View File

@@ -0,0 +1,79 @@
<?php
/*
* This file is part of Phraseanet
*
* (c) 2005-2010 Alchemy
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\Phrasea\Core\Configuration;
/**
*
* @package
* @license http://opensource.org/licenses/gpl-3.0 GPLv3
* @link www.phraseanet.com
*/
interface ConfigurationSpecification
{
/**
* Keywords to detect extended file
*/
const EXTENDED_KEYWORD = 'extends';
/**
* Return an array of paths that CAN'T be extended by ONLY one or more of their value
* but must be fully replaced with new values
*
* example:
* array(array('PATH', 'TO', 'MY', 'SCOPE1', array('SCOPE2')
*
* So $extendedConf['PATH']['TO']['MY']['SCOPE'] will fully replace
* $mainConf['PATH']['TO']['MY']['SCOPE'];
*
* @return Array
*/
public function getNonExtendablePath();
/**
* Return the configuration file from an environnment name
*
* @return \SplFileObject
*/
public function getConfFileFromEnvName($name);
/**
* Return the path to the configuration file
*
* @return string
*/
public function getConfigurationFilePath();
/**
* Return the configurationFile extension
*
* @return string
*/
public function getConfFileExtension();
/**
* Check wether the environnement $env extends another one
*
* @param type $env
* @return boolean
*/
public function isExtended(Array $env);
/**
* Return the extends environnement name null if extends nothing
*
* @param Array an environnement
* @return mixed|null
*/
public function getExtendedEnvName(Array $env);
}

View File

@@ -0,0 +1,204 @@
<?php
/*
* This file is part of Phraseanet
*
* (c) 2005-2010 Alchemy
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\Phrasea\Core\Configuration;
use \Symfony\Component\Yaml\Yaml;
/**
*
* @package
* @license http://opensource.org/licenses/gpl-3.0 GPLv3
* @link www.phraseanet.com
*/
class EnvironnementHandler
{
/**
* Configuration file specification interface
* @var ConfigurationSpecification
*/
protected $confSpecification;
/**
* A file parser interface
* @var Parser\ParserInterface
*/
protected $parser;
public function __construct(ConfigurationSpecification $configSpec, Parser\ParserInterface $parser)
{
$this->confSpecification = $configSpec;
$this->parser = $parser;
}
/**
* Stacks all envrironnement in $env that extends the loaded configuration file
*
* @param SplFileObject $file File of the current loaded config file
* @param array $envs A stack of conf environnments
* @return array
*/
private function retrieveExtendedEnvFromFile(\SplFileObject $file, Array $envs = array())
{
$env = $this->parser->parse($file);
//stack current env to allEnvs
$allEnvs[] = $env;
//check if the loaded environnement extends another configuration file
if ($this->confSpecification->isExtended($env))
{
try
{
//get extended environnement name
$envName = $this->confSpecification->getExtendedEnvName($env);
//get extended configuration file
$file = $this->confSpecification->getConfFileFromEnvName($envName);
//recurse
$this->retrieveExtendedEnvFromFile($file, $allEnvs);
}
catch (\Exception $e)
{
throw \Exception(sprintf("filename %s not found", $file->getPathname()));
}
}
return $allEnvs;
}
/**
* Get the value of a specified data path
*
* @param array $data The array where the data are stored
* @param array $path The Path as an array example : array('path', 'to', 'my', 'value')
* @return mixed
*/
private function getDataPath(Array $data, Array $path)
{
$found = true;
for ($x = 0; ($x < count($path) && $found); $x++)
{
$key = $path[$x];
if (isset($data[$key]))
{
$data = $data[$key];
}
else
{
$found = false;
}
}
return $found ? $data : null;
}
/**
* Handle the configuration process and return the final configuration
*
* @param strinig $name the name of the loaded environnement
* @return Array
*/
public function handle($name)
{
//get the corresepondant file
$file = $this->confSpecification->getConfFileFromEnvName($name);
//get all extended configuration from current env
$allEnvs = $this->retrieveExtendedEnvFromFile($file);
//Last env is the main one
$mainEnv = array_pop($allEnvs);
$excludedPath = $pathToprocess = $this->confSpecification->getNonExtendablePath();
//at least 2 envs and one path to process
if (count($allEnvs) >= 1 && count($excludedPath) >= 1)
{
foreach ($allEnvs as $currentEnv) // run trought environnements
{
foreach ($pathToprocess as $kpath => $processedPath) //run throught path
{
$valueToReplace = $this->getDataPath($currentEnv, $processedPath); //retrive the value to replace
if (null !== $valueToReplace)
{
// reset current path
$currentPath = array();
//callback to iterate over the main conf environnement and replace value from extended file
$map = function($item, $key) use (&$mainEnv, $valueToReplace, &$map, &$currentPath, $processedPath)
{
if (count(array_diff($processedPath, $currentPath)) === 0) // current path and processed path match
{
/**
* Replace current value of the $currentpath in $searchArray by $value
*/
$replace = function(&$searchArray, $currentPath, $value) use (&$replace)
{
foreach ($searchArray as $k => $v)
{
if ($k === $currentPath[0])
{
array_shift($currentPath);
if (is_array($v) && count($currentPath) !== 0)
{
$replace(&$searchArray[$k], $currentPath, $value);
}
elseif (count($currentPath) === 0)
{
$searchArray[$k] = $value;
}
}
}
};
$replace($mainEnv, $currentPath, $valueToReplace);
}
elseif (is_array($item)) // if current item is an array
{
$currentPath[] = $key; // add item's key to current path
array_walk($item, $map); // and dig into the current item
}
else //wrong path
{
$currentPath = array(); //reset
}
};
//run trough the main conf environnement
array_walk($mainEnv, $map);
//once done
//reduce the paths to process
unset($pathToprocess[$kpath]);
break;
}
}
}
}
if(count($allEnvs) >= 1)
{
foreach($allEnvs as $extendedEnv)
$mainEnv = array_replace_recursive($mainEnv, $extendedEnv);
}
return $mainEnv;
}
}

View File

@@ -0,0 +1,30 @@
<?php
/*
* This file is part of Phraseanet
*
* (c) 2005-2010 Alchemy
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\Phrasea\Core\Configuration\Parser;
/**
*
* @package
* @license http://opensource.org/licenses/gpl-3.0 GPLv3
* @link www.phraseanet.com
*/
interface ParserInterface
{
/**
* Parse the configuration file $file to an array
*
* @param \SplFileObject $file the file to parse
* @return Array
*/
public function parse(\SplFileObject $file);
}

View File

@@ -0,0 +1,39 @@
<?php
/*
* This file is part of Phraseanet
*
* (c) 2005-2010 Alchemy
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\Phrasea\Core\Configuration\Parser;
use Symfony\Component\Yaml\Yaml as SfYaml;
/**
*
* @package
* @license http://opensource.org/licenses/gpl-3.0 GPLv3
* @link www.phraseanet.com
*/
class Yaml implements ParserInterface
{
/**
*
* @Override
*/
public function parse(\SplFileObject $file)
{
try
{
return SfYaml::parse($file->getPathname());
}
catch(\Exception $e)
{
throw new \Exception(sprintf('Failed to parse the configuration file %s', $e->getMessage()));
}
}
}

View File

@@ -0,0 +1,81 @@
<?php
/*
* This file is part of Phraseanet
*
* (c) 2005-2010 Alchemy
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\Phrasea\Core\Configuration;
/**
*
* @package
* @license http://opensource.org/licenses/gpl-3.0 GPLv3
* @link www.phraseanet.com
*/
class PhraseaConfiguration implements ConfigurationSpecification
{
/**
*
* @Override
*/
public function getNonExtendablePath()
{
return array('doctrine', 'dbal');
}
/**
*
* @Override
*/
public function getConfFileFromEnvName($name)
{
return new \SplFileObject(sprintf("/%s/config_%s.%s"
, $this->getConfigurationFilePath()
, $name
, $this->getConfFileExtension())
);
}
/**
*
* @Override
*/
public function getConfigurationFilePath()
{
return __DIR__ . '/../../../../../config';
}
/**
*
* @Override
*/
public function getConfFileExtension()
{
return 'yml';
}
/**
*
* @Override
*/
public function isExtended(Array $env)
{
return isset($env[self::EXTENDED_KEYWORD]);
}
/**
*
* @Override
*/
public function getExtendedEnvName(Array $env)
{
return $this->isExtended($env) ? $env[self::EXTENDED_KEYWORD] : null;
}
}