You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

458 lines
13 KiB

2 years ago
<?php
// +---------------------------------------------------------------------------+
// | This file is part of the core package. |
// | Copyright (c) laiketui.com |
// | |
// | For the full copyright and license information, please view the LICENSE |
// | file that was distributed with this source code. You can also view the |
// | LICENSE file online at http://www.laiketui.com |
// +---------------------------------------------------------------------------+
/**
* ConfigCache allows you to customize the format of a configuration file to
* make it easy-to-use, yet still provide a PHP formatted result for direct
* inclusion into your modules.
*
* @package laiketui
* @subpackage config
*
* @author ketter (ketter@laiketui.com)
* @since 3.0.0
*/
class ConfigCache extends MojaviObject
{
// +-----------------------------------------------------------------------+
// | PRIVATE VARIABLES |
// +-----------------------------------------------------------------------+
private static
$handlers = array();
// +-----------------------------------------------------------------------+
// | METHODS |
// +-----------------------------------------------------------------------+
/**
* Load a configuration handler.
*
* @param string The handler to use when parsing a configuration file.
* @param string An absolute filesystem path to a configuration file.
* @param string An absolute filesystem path to the cache file that will be
* written.
*
* @return void
*
* @throws <b>ConfigurationException</b> If a requested configuration file
* does not have an associated
* configuration handler.
*
* @author ketter (ketter@laiketui.com)
* @since 3.0.0
*/
private static function callHandler ($handler, $config, $cache)
{
if (count(self::$handlers) == 0)
{
// we need to load the handlers first
self::loadConfigHandlers();
}
// grab the base name of the handler
$basename = basename($handler);
if (isset(self::$handlers[$handler]))
{
// we have a handler associated with the full configuration path
// call the handler and retrieve the cache data
$data =& self::$handlers[$handler]->execute($config);
self::writeCacheFile($config, $cache, $data, false);
return;
} else if (isset(self::$handlers[$basename]))
{
// we have a handler associated with the configuration base name
// call the handler and retrieve the cache data
$data =& self::$handlers[$basename]->execute($config);
self::writeCacheFile($config, $cache, $data, false);
return;
} else
{
// let's see if we have any wildcard handlers registered that match
// this basename
foreach (self::$handlers as $key => $handlerInstance)
{
// replace wildcard chars in the configuration
$pattern = str_replace('.', '\.', $key);
$pattern = str_replace('*', '.*?', $pattern);
// create pattern from config
$pattern = '#' . $pattern . '#';
if (preg_match($pattern, $handler))
{
// we found a match!
// call the handler and retrieve the cache data
$data =& self::$handlers[$key]->execute($config);
self::writeCacheFile($config, $cache, $data, false);
return;
}
}
}
// we do not have a registered handler for this file
$error = 'Configuration file "%s" does not have a ' .
'registered handler';
$error = sprintf($error, $config);
throw new ConfigurationException($error);
}
// -------------------------------------------------------------------------
/**
* Check to see if a configuration file has been modified and if so
* recompile the cache file associated with it.
*
* If the configuration file path is relative, the path itself is relative
* to the Mojavi MO_WEBAPP_DIR application setting.
*
* @param string A filesystem path to a configuration file.
*
* @return string An absolute filesystem path to the cache filename
* associated with this specified configuration file.
*
* @throws <b>ConfigurationException</b> If a requested configuration file
* does not exist.
*
* @author ketter (ketter@laiketui.com)
* @since 3.0.0
*/
public static function checkConfig ($config)
{
// the full filename path to the config
$filename = $config;
if (!Toolkit::isPathAbsolute($filename))
{
$filename = MO_WEBAPP_DIR . '/' . $filename;
}
if (!is_readable($filename))
{
// configuration file does not exist
$error = 'Configuration file "%s" does not exist or is unreadable';
$error = sprintf($error, $filename);
throw new ConfigurationException($error);
}
// the cache filename we'll be using
$cache = self::getCacheName($config);
if (!is_readable($cache) || filemtime($filename) > filemtime($cache))
{
// configuration file has changed so we need to reparse it
self::callHandler($config, $filename, $cache);
}
return $cache;
}
// -------------------------------------------------------------------------
/**
* Clear all configuration cache files.
*
* @return void
*
* @author ketter (ketter@laiketui.com)
* @since 3.0.0
*/
public static function clear ()
{
self::clearCache(MO_CACHE_DIR);
}
// -------------------------------------------------------------------------
/**
* Clear all configuration cache files.
*
* This method exists to prevent accidental deletion of non-cache directory
* files.
*
* @param string An absolute filesystem path to a cache directory.
*
* @return void
*
* @author ketter (ketter@laiketui.com)
* @since 3.0.0
*/
private static function clearCache ($directory)
{
// open a file point to the cache dir
$fp = opendir($directory);
// ignore names
$ignore = array('.', '..', 'CVS', '.svn');
while (($file = readdir($fp)) !== false)
{
if (!in_array($file, $ignore))
{
if (is_dir($file))
{
// recurse through directory
self::clearCache($file);
// delete the directory
rmdir($file);
} else
{
// delete the file
unlink($directory . '/' . $file);
}
}
}
// close file pointer
closedir($fp);
}
// -------------------------------------------------------------------------
/**
* Convert a normal filename into a cache filename.
*
* @param string A normal filename.
*
* @return string An absolute filesystem path to a cache filename.
*
* @author ketter (ketter@laiketui.com)
* @since 3.0.0
*/
public static function getCacheName ($config)
{
if (strlen($config) > 3 && ctype_alpha($config{0}) &&
$config{1} == ':' && $config{2} == '\\')
{
// file is a windows absolute path, strip off the drive letter
$config = substr($config, 3);
}
// replace unfriendly filename characters with an underscore
$config = str_replace(array('\\', '/'), '_', $config);
$config .= '.php';
return MO_CACHE_DIR . '/' . $config;
}
// -------------------------------------------------------------------------
/**
* Import a configuration file.
*
* If the configuration file path is relative, the path itself is relative
* to the Mojavi MO_WEBAPP_DIR application setting.
*
* @param string A filesystem path to a configuration file.
* @param bool Only allow this configuration file to be included once per
* request?
*
* @return void
*
* @author ketter (ketter@laiketui.com)
* @since 3.0.0
*/
public static function import ($config, $once = true)
{
// check the config file
$cache = self::checkConfig($config);
// include cache file
if ($once)
{
include_once($cache);
} else
{
include($cache);
}
}
// -------------------------------------------------------------------------
/**
* Load all configuration application and module level handlers.
*
* @return void
*
* @throws <b>ConfigurationException</b> If a configuration related error
* occurs.
*
* @author ketter (ketter@laiketui.com)
* @since 3.0.0
*/
private static function loadConfigHandlers ()
{
// manually create our config_handlers.ini handler
self::$handlers['config_handlers.ini'] = new RootConfigHandler();
self::$handlers['config_handlers.ini']->initialize();
// application configuration handlers
require_once(ConfigCache::checkConfig('config/config_handlers.ini'));
// module level configuration handlers
// make sure our modules directory exists
if (is_readable(MO_MODULE_DIR))
{
// ignore names
$ignore = array('.', '..', 'CVS', '.svn');
// create a file pointer to the module dir
$fp = opendir(MO_MODULE_DIR);
// loop through the directory and grab the modules
while (($directory = readdir($fp)) !== false)
{
if (!in_array($directory, $ignore))
{
$config = MO_MODULE_DIR . '/' . $directory .
'/config/config_handlers.ini';
if (is_readable($config))
{
// initialize the root configuration handler with this
// module name
$params = array('module_level' => true,
'module_name' => $directory);
self::$handlers['config_handlers.ini']->initialize($params);
// replace module dir path with a special keyword that
// checkConfig knows how to use
$config = 'modules/' . $directory .
'/config/config_handlers.ini';
require_once(ConfigCache::checkConfig($config));
}
}
}
// close file pointer
closedir($fp);
} else
{
// module directory doesn't exist or isn't readable
$error = 'Module directory "%s" does not exist or is not readable';
$error = sprintf($error, MO_MODULE_DIR);
throw new ConfigurationException($error);
}
}
// -------------------------------------------------------------------------
/**
* Write a cache file.
*
* @param string An absolute filesystem path to a configuration file.
* @param string An absolute filesystem path to the cache file that will
* be written.
* @param string Data to be written to the cache file.
* @param string Should we append the data?
*
* @throws <b>CacheException</b> If the cache file cannot be written.
*
* @author ketter (ketter@laiketui.com)
* @since 3.0.0
*/
private static function writeCacheFile ($config, $cache, &$data, $append)
{
$flags = ($append) ? FILE_APPEND : 0;
if (@file_put_contents($cache, $data, $flags) === false)
{
// cannot write cache file
$error = 'Failed to write cache file "%s" generated from ' .
'configuration file "%s"';
$error = sprintf($error, $cache, $config);
throw new CacheException($error);
}
}
}
?>