Subversion-Projekte lars-tiefland.php_share

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

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