samsonos/php_config

View on GitHub
src/Manager.php

Summary

Maintainability
A
0 mins
Test Coverage
<?php
/**
 * Created by PhpStorm.
 * User: egorov
 * Date: 06.01.2015
 * Time: 16:59
 */
namespace samsonos\config;

use samson\core\Event;

/**
 * Configuration scheme manager
 * @package samsonos\config
 */
class Manager
{
    /** @var array Collection of file path -> class loaded */
    protected static $classes = array();

    /** @var array Collection of files in current init */
    protected static $files = array();

    /**
     * Load all entity configuration classes in specific location.
     *
     * All module files must match Entity::FILE_PATTERN to be
     * loaded.
     *
     * @param string $path Path for importing classes
     */
    public static function import($path)
    {
        // Read all files in path
        self::$files = glob($path .'/'. Entity::FILE_PATTERN);

        // Fill array of entity files with keys of file names without extension
        foreach (self::$files as $file) {
            $file = realpath($file);
            // If we have not already loaded this class before with other schemes
            if (!isset(self::$classes[$file])) {
                // Store loaded classes
                $classes = get_declared_classes();

                // Load entity configuration file
                require_once($file);

                // Get loaded class - store class to static collection
                $classes = array_diff(get_declared_classes(), $classes);
                self::$classes[$file] = end($classes);
            }
        }
    }

    /** @var Scheme[] Collection of available schemes */
    public $schemes = array();

    /** @var  Scheme Pointer to current active configuration scheme */
    protected $active;

    /**
     * Initialize all configuration logic
     */
    public function __construct()
    {
        // Add class auto loader
        spl_autoload_register(array($this, 'autoload'));

        // Subscribe active configuration scheme to core module configure event
        Event::subscribe('core.environment.change', array($this, 'change'));
    }

    /**
     * Configuration class autoloader.
     * It helps resolve single configuration entity configuration dependencies
     * @param string $className Class name for loading
     */
    public function autoload($className)
    {
        // If namespace is present
        $class = substr($className, strrpos($className, '\\')+1);

        // Try to find class file by class name without namespace
        $matches = preg_grep('/'.$class.'/i', self::$files);
        if (sizeof($matches)) {
            require_once(end($matches));
        }
    }

    /**
     * Switch active environment
     * @param string $environment Configuration environment identifier
     */
    public function change($environment = Scheme::BASE)
    {
        // Switch to configuration environment
        $this->active = & $this->schemes[$environment];

        // Subscribe active configuration scheme to core module configure event
        Event::subscribe('core.module.configure', array($this, 'configure'));

        // If we have successfully changed configuration scheme
        if (!isset($this->active)) {
            // Signal error
            Event::fire(
                'error',
                array(
                    $this,
                    'Cannot change configuration scheme to ['.$environment.'] - Configuration scheme does not exists'
                )
            );

            // Set global scheme as active
            $this->active = & $this->schemes[Scheme::BASE];
        }
    }

    /**
     * Initialize all configuration logic
     * @param string $basePath Path to configuration base folder
     */
    public function init($basePath)
    {
        // Create global scheme instance
        $this->create($basePath, Scheme::BASE);

        // Switch to global environment
        $this->change();

        // Read all directories in base configuration path
        foreach (glob($basePath . '*', GLOB_ONLYDIR) as $path) {
            // Create new configuration scheme
            $this->create($path);
        }
    }

    /**
     * Create configuration scheme
     * @param string $path Path to configuration scheme folder
     * @param string $environment Configuration scheme environment identifier
     */
    public function create($path, $environment = null)
    {
        // Import all valid entity configuration classes from path
        self::import($path);

        // If no environment identifier is passed - use it from path
        $environment = !isset($environment) ? basename($path) : $environment;

        // Pointer to a configuration scheme
        $pointer = & $this->schemes[$environment];

        // Check if have NOT already created configuration for this environment
        if (!isset($pointer)) {
            $pointer = new Scheme(realpath($path . '/'), $environment);
        } else { // Load data to existing configuration scheme
            $pointer->load(realpath($path . '/'));
        }
    }

    /**
     * Configure object with current configuration scheme entity parameters.
     *
     * If now $identifier is passed - automatic identifier generation
     * will take place from object class name.
     *
     * If additional parameters key=>value collection is passed, they
     * will be used to configure object instead of entity configuration
     * class.
     *
     * If current configuration scheme has no entity configuration for
     * passed object - global configuration scheme will be used.
     *
     * @param mixed $object Object for configuration with entity
     * @param string $identifier Configuration entity name
     * @param array|null $params Collection of configuration parameters
     */
    public function configure(& $object, $identifier = null, $params = null)
    {
        // Try to configure using current scheme
        if (!$this->active->configure($object, $identifier, $params)) {
            // Get pointer to global scheme
            $base = & $this->schemes[Scheme::BASE];
            if (isset($base) && $base !== $this->active) {
                // Call global scheme for configuration
                $base->configure($object, $identifier, $params);
            }
        }
    }
}