1. sfConfigCache.class.php
  2. /** * sfConfigCache 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 symfony * @subpackage config * @author Fabien Potencier * @author Sean Kerr * @version SVN: $Id: sfConfigCache.class.php 23810 2009-11-12 11:07:44Z Kris.Wallsmith $ */
  3. class sfConfigCache
  4. {
  5. protected
  6. $configuration = null,
  7. $handlers = array(),
  8. $userHandlers = array();
  9. /**
  10. * Constructor
  11. *
  12. * @param sfApplicationConfiguration $configuration A sfApplicationConfiguration instance
  13. */
  14. public function __construct(sfApplicationConfiguration $configuration)
  15. {
  16. $this->configuration = $configuration;
  17. }
  18. /**
  19. * Loads a configuration handler.
  20. *
  21. * @param string $handler The handler to use when parsing a configuration file
  22. * @param array $configs An array of absolute filesystem paths to configuration files
  23. * @param string $cache An absolute filesystem path to the cache file that will be written
  24. *
  25. * @throws <b>sfConfigurationException</b> If a requested configuration file does not have an associated configuration handler
  26. */
  27. protected function callHandler($handler, $configs, $cache)
  28. {
  29. if (count($this->handlers) == 0)
  30. {
  31. // we need to load the handlers first
  32. $this->loadConfigHandlers();
  33. }
  34. if (count($this->userHandlers) != 0)
  35. {
  36. // we load user defined handlers
  37. $this->mergeUserConfigHandlers();
  38. }
  39. // handler instance to call for this configuration file
  40. $handlerInstance = null;
  41. $handler = str_replace(DIRECTORY_SEPARATOR, '/', $handler);
  42. // grab the base name of the handler
  43. $basename = basename($handler);
  44. if (isset($this->handlers[$handler]))
  45. {
  46. // we have a handler associated with the full configuration path
  47. $handlerInstance = $this->getHandler($handler);
  48. }
  49. else if (isset($this->handlers[$basename]))
  50. {
  51. // we have a handler associated with the configuration base name
  52. $handlerInstance = $this->getHandler($basename);
  53. }
  54. else
  55. {
  56. // let's see if we have any wildcard handlers registered that match this basename
  57. foreach (array_keys($this->handlers) as $key)
  58. {
  59. // replace wildcard chars in the configuration
  60. $pattern = strtr($key, array('.' => '\.', '*' => '(.*?)'));
  61. $matches = array();
  62. // create pattern from config
  63. if (preg_match('#'.$pattern.'$#', $handler, $matches))
  64. {
  65. $handlerInstance = $this->getHandler($key);
  66. array_shift($matches);
  67. $handlerInstance->getParameterHolder()->set('wildcardValues', $matches);
  68. break;
  69. }
  70. }
  71. }
  72. if (!$handlerInstance)
  73. {
  74. // we do not have a registered handler for this file
  75. throw new sfConfigurationException(sprintf('Configuration file "%s" does not have a registered handler.', implode(', ', $configs)));
  76. }
  77. // call the handler and retrieve the cache data
  78. $data = $handlerInstance->execute($configs);
  79. $this->writeCacheFile($handler, $cache, $data);
  80. }
  81. /**
  82. * Returns the config handler configured for the given name
  83. *
  84. * @param string $name The config handler name
  85. *
  86. * @return sfConfigHandler A sfConfigHandler instance
  87. */
  88. protected function getHandler($name)
  89. {
  90. if (is_array($this->handlers[$name]))
  91. {
  92. $class = $this->handlers[$name][0];
  93. $this->handlers[$name] = new $class($this->handlers[$name][1]);
  94. }
  95. return $this->handlers[$name];
  96. }
  97. /**
  98. * Checks to see if a configuration file has been modified and if so
  99. * recompile the cache file associated with it.
  100. *
  101. * The recompilation only occurs in a non debug environment.
  102. *
  103. * If the configuration file path is relative, symfony will look in directories
  104. * defined in the sfConfiguration::getConfigPaths() method.
  105. *
  106. * @param string $configPath A filesystem path to a configuration file
  107. * @param boolean $optional If true, config path does not need to exist
  108. *
  109. * @return string An absolute filesystem path to the cache filename associated with this specified configuration file
  110. *
  111. * @throws <b>sfConfigurationException</b> If a requested configuration file does not exist
  112. *
  113. * @see sfConfiguration::getConfigPaths()
  114. */
  115. public function checkConfig($configPath, $optional = false)
  116. {
  117. if (sfConfig::get('sf_debug') && sfConfig::get('sf_logging_enabled'))
  118. {
  119. $timer = sfTimerManager::getTimer('Configuration');
  120. }
  121. // the cache filename we'll be using
  122. $cache = $this->getCacheName($configPath);
  123. if (!sfConfig::get('sf_debug') && !sfConfig::get('sf_test') && is_readable($cache))
  124. {
  125. return $cache;
  126. }
  127. if (!sfToolkit::isPathAbsolute($configPath))
  128. {
  129. $files = $this->configuration->getConfigPaths($configPath);
  130. }
  131. else
  132. {
  133. $files = is_readable($configPath) ? array($configPath) : array();
  134. }
  135. if (!isset($files[0]))
  136. {
  137. if ($optional)
  138. {
  139. return null;
  140. }
  141. // configuration does not exist
  142. throw new sfConfigurationException(sprintf('Configuration "%s" does not exist or is unreadable.', $configPath));
  143. }
  144. // find the more recent configuration file last modification time
  145. $mtime = 0;
  146. foreach ($files as $file)
  147. {
  148. if (filemtime($file) > $mtime)
  149. {
  150. $mtime = filemtime($file);
  151. }
  152. }
  153. if (!is_readable($cache) || $mtime > filemtime($cache))
  154. {
  155. // configuration has changed so we need to reparse it
  156. $this->callHandler($configPath, $files, $cache);
  157. }
  158. if (sfConfig::get('sf_debug') && sfConfig::get('sf_logging_enabled'))
  159. {
  160. $timer->addTime();
  161. }
  162. return $cache;
  163. }
  164. /**
  165. * Clears all configuration cache files.
  166. */
  167. public function clear()
  168. {
  169. sfToolkit::clearDirectory(sfConfig::get('sf_config_cache_dir'));
  170. }
  171. /**
  172. * Converts a normal filename into a cache filename.
  173. *
  174. * @param string $config A normal filename
  175. *
  176. * @return string An absolute filesystem path to a cache filename
  177. */
  178. public function getCacheName($config)
  179. {
  180. if (strlen($config) > 3 && ctype_alpha($config[0]) && $config[1] == ':' && ($config[2] == '\\' || $config[2] == '/'))
  181. {
  182. // file is a windows absolute path, strip off the drive letter
  183. $config = substr($config, 3);
  184. }
  185. // replace unfriendly filename characters with an underscore
  186. $config = str_replace(array('\\', '/', ' '), '_', $config);
  187. $config .= '.php';
  188. return sfConfig::get('sf_config_cache_dir').'/'.$config;
  189. }
  190. /**
  191. * Imports a configuration file.
  192. *
  193. * @param string $config A filesystem path to a configuration file
  194. * @param bool $once Only allow this configuration file to be included once per request?
  195. * @param bool $optional Only include if true
  196. *
  197. * @see checkConfig()
  198. */
  199. public function import($config, $once = true, $optional = false)
  200. {
  201. $cache = $this->checkConfig($config, $optional);
  202. if ($optional && !$cache)
  203. {
  204. return;
  205. }
  206. // include cache file
  207. if ($once)
  208. {
  209. include_once($cache);
  210. }
  211. else
  212. {
  213. include($cache);
  214. }
  215. }
  216. /**
  217. * Loads all configuration application and module level handlers.
  218. *
  219. * @throws <b>sfConfigurationException</b> If a configuration related error occurs.
  220. */
  221. protected function loadConfigHandlers()
  222. {
  223. // manually create our config_handlers.yml handler
  224. $this->handlers['config_handlers.yml'] = new sfRootConfigHandler();
  225. // application configuration handlers
  226. require $this->checkConfig('config/config_handlers.yml');
  227. // module level configuration handlers
  228. // checks modules directory exists
  229. if (!is_readable($sf_app_modules_dir = sfConfig::get('sf_app_modules_dir')))
  230. {
  231. return;
  232. }
  233. // ignore names
  234. $ignore = array('.', '..', 'CVS', '.svn');
  235. // create a file pointer to the module dir
  236. $fp = opendir($sf_app_modules_dir);
  237. // loop through the directory and grab the modules
  238. while (($directory = readdir($fp)) !== false)
  239. {
  240. if (in_array($directory, $ignore))
  241. {
  242. continue;
  243. }
  244. $configPath = $sf_app_modules_dir.'/'.$directory.'/config/config_handlers.yml';
  245. if (is_readable($configPath))
  246. {
  247. // initialize the root configuration handler with this module name
  248. $params = array('module_level' => true, 'module_name' => $directory);
  249. $this->handlers['config_handlers.yml']->initialize($params);
  250. // replace module dir path with a special keyword that
  251. // checkConfig knows how to use
  252. $configPath = 'modules/'.$directory.'/config/config_handlers.yml';
  253. require $this->checkConfig($configPath);
  254. }
  255. }
  256. // close file pointer
  257. closedir($fp);
  258. }
  259. /**
  260. * Writes a cache file.
  261. *
  262. * @param string $config An absolute filesystem path to a configuration file
  263. * @param string $cache An absolute filesystem path to the cache file that will be written
  264. * @param string $data Data to be written to the cache file
  265. *
  266. * @throws sfCacheException If the cache file cannot be written
  267. */
  268. protected function writeCacheFile($config, $cache, $data)
  269. {
  270. $current_umask = umask(0000);
  271. if (!is_dir(dirname($cache)))
  272. {
  273. if (false === @mkdir(dirname($cache), 0777, true))
  274. {
  275. throw new sfCacheException(sprintf('Failed to make cache directory "%s" while generating cache for configuration file "%s".', dirname($cache), $config));
  276. }
  277. }
  278. $tmpFile = tempnam(dirname($cache), basename($cache));
  279. if (!$fp = @fopen($tmpFile, 'wb'))
  280. {
  281. throw new sfCacheException(sprintf('Failed to write cache file "%s" generated from configuration file "%s".', $tmpFile, $config));
  282. }
  283. @fwrite($fp, $data);
  284. @fclose($fp);
  285. // Hack from Agavi (http://trac.agavi.org/changeset/3979)
  286. // With php < 5.2.6 on win32, renaming to an already existing file doesn't work, but copy does,
  287. // so we simply assume that when rename() fails that we are on win32 and try to use copy()
  288. if (!@rename($tmpFile, $cache))
  289. {
  290. if (copy($tmpFile, $cache))
  291. {
  292. unlink($tmpFile);
  293. }
  294. }
  295. chmod($cache, 0666);
  296. umask($current_umask);
  297. }
  298. /**
  299. * Registers a configuration handler.
  300. *
  301. * @param string $handler The handler to use when parsing a configuration file
  302. * @param class $class A configuration handler class
  303. * @param string $params An array of options for the handler class initialization
  304. */
  305. public function registerConfigHandler($handler, $class, $params = array())
  306. {
  307. $this->userHandlers[$handler] = new $class($params);
  308. }
  309. /**
  310. * Merges configuration handlers from the config_handlers.yml
  311. * and the ones defined with registerConfigHandler()
  312. *
  313. */
  314. protected function mergeUserConfigHandlers()
  315. {
  316. // user defined configuration handlers
  317. $this->handlers = array_merge($this->handlers, $this->userHandlers);
  318. $this->userHandlers = array();
  319. }
  320. }

Debug toolbar