Issues (1131)

Security Analysis    not enabled

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

src/config/ConfigCache.class.php (8 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
// +---------------------------------------------------------------------------+
4
// | This file is part of the Agavi package.                                   |
5
// | Copyright (c) 2005-2011 the Agavi Project.                                |
6
// | Based on the Mojavi3 MVC Framework, Copyright (c) 2003-2005 Sean Kerr.    |
7
// |                                                                           |
8
// | For the full copyright and license information, please view the LICENSE   |
9
// | file that was distributed with this source code. You can also view the    |
10
// | LICENSE file online at http://www.agavi.org/LICENSE.txt                   |
11
// |   vi: set noexpandtab:                                                    |
12
// |   Local Variables:                                                        |
13
// |   indent-tabs-mode: t                                                     |
14
// |   End:                                                                    |
15
// +---------------------------------------------------------------------------+
16
17
namespace Agavi\Config;
18
19
use Agavi\Exception\AgaviException;
20
use Agavi\Exception\CacheException;
21
use Agavi\Util\Toolkit;
22
use Agavi\Core\Context;
23
use Agavi\Exception\UnreadableException;
24
use Agavi\Exception\ConfigurationException;
25
26
/**
27
 * ConfigCache allows you to customize the format of a configuration
28
 * file to make it easy-to-use, yet still provide a PHP formatted result
29
 * for direct inclusion into your modules.
30
 *
31
 * @package    agavi
32
 * @subpackage config
33
 *
34
 * @author     Sean Kerr <[email protected]>
35
 * @copyright  Authors
36
 * @copyright  The Agavi Project
37
 *
38
 * @since      0.9.0
39
 *
40
 * @version    $Id$
41
 */
42
class ConfigCache
43
{
44
    const CACHE_SUBDIR = 'config';
45
46
    /**
47
     * @var        array An array of config handler instructions.
48
     */
49
    protected static $handlers = null;
50
51
    /**
52
     * @var        array A string=>bool array containing config handler files and
53
     *                   their loaded status.
54
     */
55
    protected static $handlerFiles = array();
56
57
    /**
58
     * @var        bool Whether there is an entry in self::$handlerFiles that
59
     *                  needs processing.
60
     */
61
    protected static $handlersDirty = true;
62
    
63
    /**
64
     * @var        bool Whether the config handler files have been required.
65
     */
66
    protected static $filesIncluded = false;
67
68
    /**
69
     * Load a configuration handler.
70
     *
71
     * @param      string $name        The path of the originally requested configuration file.
72
     * @param      string $config      An absolute filesystem path to a configuration file.
73
     * @param      string $cache       An absolute filesystem path to the cache file that
74
     *                                 will be written.
75
     * @param      string $context     The context which we're currently running.
76
     * @param      array  $handlerInfo Optional config handler info array.
77
     *
78
     * @throws     <b>ConfigurationException</b> If a requested configuration
79
     *                                           file does not have an
80
     *                                           associated config handler.
81
     *
82
     * @author     David Zülke <[email protected]>
83
     * @author     Dominik del Bondio <[email protected]>
84
     * @author     Felix Gilcher <[email protected]>
85
     * @since      0.9.0
86
     */
87
    protected static function callHandler($name, $config, $cache, $context, array $handlerInfo = null)
88
    {
89
        self::setupHandlers();
90
91
        if (null === $handlerInfo) {
92
            // we need to load the handlers first
93
            $handlerInfo = self::getHandlerInfo($name);
94
        }
95
96
        if ($handlerInfo === null) {
97
            // we do not have a registered handler for this file
98
            $error = 'Configuration file "%s" does not have a registered handler';
99
            $error = sprintf($error, $name);
100
            throw new ConfigurationException($error);
101
        }
102
        
103
        $data = self::executeHandler($config, $context, $handlerInfo);
104
        self::writeCacheFile($config, $cache, $data, false);
105
    }
106
107
    /**
108
     * Set up all config handler definitions.
109
     *
110
     * Checks whether the handlers have been loaded or the dirtyHandlers flat is
111
     * set, and loads any handler that has not been loaded.
112
     *
113
     * @author       Felix Gilcher <[email protected]>
114
     * @since        1.0.0
115
     */
116
    protected static function setupHandlers()
117
    {
118
        self::loadConfigHandlers();
119
        
120
        if (self::$handlersDirty) {
121
            // set handlersdirty to false, prevent an infinite loop
122
            self::$handlersDirty = false;
123
            // load additional config handlers
124
            foreach (self::$handlerFiles as $filename => &$loaded) {
125
                if (!$loaded) {
126
                    self::loadConfigHandlersFile($filename);
127
                    $loaded = true;
128
                }
129
            }
130
        }
131
    }
132
    
133
    /**
134
     * Fetch the handler information for the given filename.
135
     *
136
     * @param        string $name The name of the config file (partial path).
137
     *
138
     * @return       array  The handler info.
139
     *
140
     * @author       Felix Gilcher <[email protected]>
141
     * @since        1.0.0
142
     */
143
    protected static function getHandlerInfo($name)
144
    {
145
        // grab the base name of the originally requested config path
146
        $basename = basename($name);
147
148
        $handlerInfo = null;
149
150
        if (isset(self::$handlers[$name])) {
151
            // we have a handler associated with the full configuration path
152
            $handlerInfo = self::$handlers[$name];
153
        } elseif (isset(self::$handlers[$basename])) {
154
            // we have a handler associated with the configuration base name
155
            $handlerInfo = self::$handlers[$basename];
156
        } else {
157
            // let's see if we have any wildcard handlers registered that match
158
            // this basename
159
            foreach (self::$handlers as $key => $value) {
160
                // replace wildcard chars in the configuration and create the pattern
161
                $pattern = sprintf('#%s#', str_replace('\*', '.*?', preg_quote($key, '#')));
162
163
                if (preg_match($pattern, $name)) {
164
                    $handlerInfo = $value;
165
                    break;
166
                }
167
            }
168
        }
169
        
170
        return $handlerInfo;
171
    }
172
    
173
    /**
174
     * Execute the config handler for the given file.
175
     *
176
     * @param        string $config      The path to the config file (full path).
177
     * @param        string $context     The context which we're currently running.
178
     * @param        array  $handlerInfo The config handler info.
179
     *
180
     * @return       string The compiled data.
181
     *
182
     * @author       Felix Gilcher <[email protected]>
183
     * @since        1.0.0
184
     */
185
    protected static function executeHandler($config, $context, array $handlerInfo)
186
    {
187
        // call the handler and retrieve the cache data
188
        /** @var ConfigHandler $handler */
189
        $handler = new $handlerInfo['class'];
190
        if ($handler instanceof XmlConfigHandlerInterface) {
191
            // a new-style config handler
192
            // it does not parse the config itself; instead, it is given a complete and merged DOM document
193
            $doc = XmlConfigParser::run($config, Config::get('core.environment'), $context, $handlerInfo['transformations'], $handlerInfo['validations']);
194
195
            if ($context !== null) {
196
                $context = Context::getInstance($context);
197
            }
198
199
            $handler->initialize($context, $handlerInfo['parameters']);
200
201
            try {
202
                $data = $handler->execute($doc);
203
            } catch (AgaviException $e) {
204
                throw new $e(sprintf("Compilation of configuration file '%s' failed for the following reason(s):\n\n%s", $config, $e->getMessage()), 0, $e);
205
            }
206
        } else {
207
            $validationFile = null;
208
            if (isset($handlerInfo['validations'][XmlConfigParser::STAGE_SINGLE][XmlConfigParser::STEP_TRANSFORMATIONS_AFTER][XmlConfigParser::VALIDATION_TYPE_XMLSCHEMA][0])) {
209
                $validationFile = $handlerInfo['validations'][XmlConfigParser::STAGE_SINGLE][XmlConfigParser::STEP_TRANSFORMATIONS_AFTER][XmlConfigParser::VALIDATION_TYPE_XMLSCHEMA][0];
210
            }
211
            $handler->initialize($validationFile, null, $handlerInfo['parameters']);
212
            $data = $handler->execute($config, $context);
213
        }
214
        
215
        return $data;
216
    }
217
    
218
    /**
219
     * Check to see if a configuration file has been modified and if so
220
     * recompile the cache file associated with it.
221
     *
222
     * If the configuration file path is relative, the path itself is relative
223
     * to the Agavi "core.app_dir" application setting.
224
     *
225
     * @param      string $config  A filesystem path to a configuration file.
226
     * @param      string $context An optional context name for which the config should be read.
227
     *
228
     * @return     string An absolute filesystem path to the cache filename
229
     *                    associated with this specified configuration file.
230
     *
231
     * @throws     <b>UnreadableException</b> If a requested configuration
232
     *                                             file does not exist.
233
     *
234
     * @author     Sean Kerr <[email protected]>
235
     * @since      0.9.0
236
     */
237
    public static function checkConfig($config, $context = null)
238
    {
239
        $config = Toolkit::normalizePath($config);
240
        // the full filename path to the config, which might not be what we were given.
241
        $filename = Toolkit::isPathAbsolute($config) ? $config : Toolkit::normalizePath(Config::get('core.app_dir')) . '/' . $config;
242
243
        $filename = realpath($filename);
244
        if (!is_readable($filename)) {
245
            throw new UnreadableException('Configuration file "' . $filename . '" does not exist or is unreadable.');
246
        }
247
248
        // the cache filename we'll be using
249
        $cache = self::getCacheName($config, $context);
250
251
        if (self::isModified($filename, $cache)) {
252
            // configuration file has changed so we need to reparse it
253
            self::callHandler($config, $filename, $cache, $context);
254
        }
255
256
        return $cache;
257
    }
258
259
    /**
260
     * Check if the cached version of a file is up to date.
261
     *
262
     * @param      string $filename  The source file.
263
     * @param      string $cachename The name of the cached version.
264
     *
265
     * @return     bool Whether or not the cached file must be updated.
266
     *
267
     * @author     David Zülke <[email protected]>
268
     * @since      0.11.0
269
     */
270
    public static function isModified($filename, $cachename)
271
    {
272
        return (!is_readable($cachename) || filemtime($filename) > filemtime($cachename));
273
    }
274
275
    /**
276
     * Clear all configuration cache files.
277
     *
278
     * @author     Sean Kerr <[email protected]>
279
     * @since      0.9.0
280
     */
281
    public static function clear()
282
    {
283
        Toolkit::clearCache(self::CACHE_SUBDIR);
284
    }
285
286
    /**
287
     * Convert a normal filename into a cache filename.
288
     *
289
     * @param      string $config  A normal filename.
290
     * @param      string $context A context name.
291
     *
292
     * @return     string An absolute filesystem path to a cache filename.
293
     *
294
     * @author     Sean Kerr <[email protected]>
295
     * @since      0.9.0
296
     */
297
    public static function getCacheName($config, $context = null)
298
    {
299
        $environment = Config::get('core.environment');
300
301
        if (strlen($config) > 3 && ctype_alpha($config[0]) && $config[1] == ':' && ($config[2] == '\\' || $config[2] == '/')) {
302
            // file is a windows absolute path, strip off the drive letter
303
            $config = substr($config, 3);
304
        }
305
306
        // replace unfriendly filename characters with an underscore and postfix the name with a php extension
307
        // see http://trac.agavi.org/wiki/RFCs/Ticket932 for an explanation how cache names are constructed
308
        $cacheName = sprintf(
309
            '%1$s_%2$s.php',
310
            preg_replace(
311
                '/[^\w-_.]/i',
312
                '_',
313
                sprintf(
314
                    '%1$s_%2$s_%3$s',
315
                    basename($config),
316
                    $environment,
317
                    $context
318
                )
319
            ),
320
            sha1(
321
                sprintf(
322
                    '%1$s_%2$s_%3$s',
323
                    $config,
324
                    $environment,
325
                    $context
326
                )
327
            )
328
        );
329
        
330
        return Config::get('core.cache_dir') . DIRECTORY_SEPARATOR . self::CACHE_SUBDIR . DIRECTORY_SEPARATOR . $cacheName;
331
    }
332
333
    /**
334
     * Import a configuration file.
335
     *
336
     * If the configuration file path is relative, the path itself is relative
337
     * to the Agavi "core.app_dir" application setting.
338
     *
339
     * @param      string $config  A filesystem path to a configuration file.
340
     * @param      string $context A context name.
341
     * @param      bool   $once    Only allow this configuration file to be included once
342
     *                             per request?
343
     *
344
     * @author     Sean Kerr <[email protected]>
345
     * @since      0.9.0
346
     */
347
    public static function load($config, $context = null, $once = true)
348
    {
349
        $cache = self::checkConfig($config, $context);
350
351
        if ($once) {
352
            include_once($cache);
353
        } else {
354
            include($cache);
355
        }
356
    }
357
358
    /**
359
     * Load all configuration application and module level handlers.
360
     *
361
     * @throws     ConfigurationException If a configuration related
362
     *                                                error occurs.
363
     *
364
     * @author     Sean Kerr <[email protected]>
365
     * @since      0.9.0
366
     */
367
    protected static function loadConfigHandlers()
368
    {
369
        if (self::$handlers !== null) {
370
            return;
371
        } else {
372
            self::$handlers = array();
373
        }
374
        
375
        // some checks first
376
        if (!defined('LIBXML_DOTTED_VERSION') || (!Config::get('core.ignore_broken_libxml', false) && !version_compare(LIBXML_DOTTED_VERSION, '2.6.16', 'gt'))) {
0 ignored issues
show
false is of type boolean, but the function expects a string|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
377
            throw new \Exception("A libxml version greater than 2.6.16 is highly recommended. With version 2.6.16 and possibly later releases, validation of XML configuration files will not work and Form Population Filter will eventually fail randomly on some documents due to *severe bugs* in older libxml releases (2.6.16 was released in November 2004, so it is really getting time to update).\n\nIf you still would like to try your luck, disable this message by doing\nAgaviConfig::set('core.ignore_broken_libxml', true);\nand\nAgaviConfig::set('core.skip_config_validation', true);\nbefore calling\nAgavi::bootstrap();\nin index.php (app/config.php is not the right place for this).\n\nBut be advised that you *will* run into segfaults and other sad situations eventually, so what you should really do is upgrade your libxml install.");
378
        }
379
        
380
        $agaviDir = Config::get('core.agavi_dir');
381
        
382
        // :NOTE: fgilcher, 2008-12-03
0 ignored issues
show
Unused Code Comprehensibility introduced by
42% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
383
        // we need this method reentry safe for unit testing
384
        // sorry for the testing code in the class, but I don't have
385
        // any other idea to solve the issue
386
        if (!self::$filesIncluded) {
387
            // since we only need the parser and handlers when the config is not cached
388
            // it is sufficient to include them at this stage
389
            require_once($agaviDir . '/config/LegacyConfigHandler.interface.php');
390
            require_once($agaviDir . '/config/XmlConfigHandler.interface.php');
391
            require_once($agaviDir . '/config/BaseConfigHandler.class.php');
392
            require_once($agaviDir . '/config/ConfigHandler.class.php');
393
            require_once($agaviDir . '/config/XmlConfigHandler.class.php');
394
            require_once($agaviDir . '/config/AutoloadConfigHandler.class.php');
395
            require_once($agaviDir . '/config/ConfigHandlersConfigHandler.class.php');
396
            require_once($agaviDir . '/config/ConfigValueHolder.class.php');
397
            require_once($agaviDir . '/config/ConfigParser.class.php');
398
            require_once($agaviDir . '/config/XmlConfigParser.class.php');
399
            // extended DOM* classes
400
            require_once($agaviDir . '/config/util/dom/XmlConfigDomAttr.class.php');
401
            require_once($agaviDir . '/config/util/dom/XmlConfigDomCharacterData.class.php');
402
            require_once($agaviDir . '/config/util/dom/XmlConfigDomComment.class.php');
403
            require_once($agaviDir . '/config/util/dom/XmlConfigDomDocument.class.php');
404
            require_once($agaviDir . '/config/util/dom/XmlConfigDomDocumentFragment.class.php');
405
            require_once($agaviDir . '/config/util/dom/XmlConfigDomDocumentType.class.php');
406
            require_once($agaviDir . '/config/util/dom/XmlConfigDomElement.class.php');
407
            require_once($agaviDir . '/config/util/dom/XmlConfigDomEntity.class.php');
408
            require_once($agaviDir . '/config/util/dom/XmlConfigDomEntityReference.class.php');
409
            require_once($agaviDir . '/config/util/dom/XmlConfigDomNode.class.php');
410
            require_once($agaviDir . '/config/util/dom/XmlConfigDomNotation.class.php');
411
            require_once($agaviDir . '/config/util/dom/XmlConfigDomProcessingInstruction.class.php');
412
            require_once($agaviDir . '/config/util/dom/XmlConfigDomText.class.php');
413
            // schematron processor
414
            require_once($agaviDir . '/util/SchematronProcessor.class.php');
415
            // extended XSL* classes
416
            if (!Config::get('core.skip_config_transformations', false)) {
0 ignored issues
show
false is of type boolean, but the function expects a string|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
417
                if (!extension_loaded('xsl')) {
418
                    throw new ConfigurationException("You do not have the XSL extension for PHP (ext/xsl) installed or enabled. The extension is used by Agavi to perform XSL transformations in the configuration system to guarantee forwards compatibility of applications.\n\nIf you do not want to or can not install ext/xsl, you may disable all transformations by setting\nAgaviConfig::set('core.skip_config_transformations', true);\nbefore calling\nAgavi::bootstrap();\nin index.php (app/config.php is not the right place for this because this is a setting that's specific to your environment or machine).\n\nKeep in mind that disabling transformations mean you *have* to use the latest configuration file formats and namespace versions. Also, certain additional configuration file validations implemented via Schematron will not be performed.");
419
                }
420
                require($agaviDir . '/util/XsltProcessor.class.php');
421
            }
422
            self::$filesIncluded = true;
423
        }
424
        
425
        // manually create our config_handlers.xml handler
426
        self::$handlers['config_handlers.xml'] = array(
427
            'class' => '\\Agavi\\Config\\ConfigHandlersConfigHandler',
428
            'parameters' => array(
429
            ),
430
            'transformations' => array(
431
                XmlConfigParser::STAGE_SINGLE => array(
432
                    // 0.11 -> 1.0
0 ignored issues
show
Unused Code Comprehensibility introduced by
50% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
433
                    $agaviDir . '/config/xsl/config_handlers.xsl',
434
                    // 1.0 -> 1.0 with AgaviReturnArrayConfigHandler <transformation> for Agavi 1.1
435
                    $agaviDir . '/config/xsl/config_handlers.xsl',
436
                ),
437
                XmlConfigParser::STAGE_COMPILATION => array(
438
                ),
439
            ),
440
            'validations' => array(
441
                XmlConfigParser::STAGE_SINGLE => array(
442
                    XmlConfigParser::STEP_TRANSFORMATIONS_BEFORE => array(
443
                    ),
444
                    XmlConfigParser::STEP_TRANSFORMATIONS_AFTER => array(
445
                        XmlConfigParser::VALIDATION_TYPE_XMLSCHEMA => array(
446
                            $agaviDir . '/config/xsd/config_handlers.xsd',
447
                        ),
448
                        XmlConfigParser::VALIDATION_TYPE_SCHEMATRON => array(
449
                            $agaviDir . '/config/sch/config_handlers.sch',
450
                        ),
451
                    ),
452
                ),
453
                XmlConfigParser::STAGE_COMPILATION => array(
454
                    XmlConfigParser::STEP_TRANSFORMATIONS_BEFORE => array(),
455
                    XmlConfigParser::STEP_TRANSFORMATIONS_AFTER => array()
456
                ),
457
            ),
458
        );
459
460
        $cfg = Config::get('core.config_dir') . '/config_handlers.xml';
461
        if (!is_readable($cfg)) {
462
            $cfg = Config::get('core.system_config_dir') . '/config_handlers.xml';
463
        }
464
        // application configuration handlers
465
        self::loadConfigHandlersFile($cfg);
466
    }
467
    
468
    /**
469
     * Load the config handlers from the given config file.
470
     * Existing handlers will not be overwritten.
471
     *
472
     * @param      string $cfg The path to a config_handlers.xml file.
473
     *
474
     * @author     Felix Gilcher <[email protected]>
475
     * @since      1.0.0
476
     */
477
    protected static function loadConfigHandlersFile($cfg)
478
    {
479
        self::$handlers = (array)self::$handlers + include(ConfigCache::checkConfig($cfg));
480
    }
481
482
    /**
483
     * Schedules a config handlers file to be loaded.
484
     *
485
     * @param      string $filename The path to a config_handlers.xml file.
486
     *
487
     * @author     Dominik del Bondio <[email protected]>
488
     * @since      1.0.0
489
     */
490
    public static function addConfigHandlersFile($filename)
491
    {
492
        if (!isset(self::$handlerFiles[$filename])) {
493
            if (!is_readable($filename)) {
494
                throw new UnreadableException('Configuration file "' . $filename . '" does not exist or is unreadable.');
495
            }
496
            
497
            self::$handlerFiles[$filename] = false;
498
            self::$handlersDirty = true;
499
        }
500
    }
501
502
    /**
503
     * Write a cache file.
504
     *
505
     * @param      string $config An absolute filesystem path to a configuration file.
506
     * @param      string $cache  An absolute filesystem path to the cache file that
507
     *                            will be written.
508
     * @param      string $data   Data to be written to the cache file.
509
     * @param      bool   $append Should we append the data?
510
     *
511
     * @throws     <b>CacheException</b> If the cache file cannot be written.
512
     *
513
     * @author     Sean Kerr <[email protected]>
514
     * @since      0.9.0
515
     */
516
    public static function writeCacheFile($config, $cache, $data, $append = false)
517
    {
518
        $perms = fileperms(Config::get('core.cache_dir')) ^ 0x4000;
519
520
        $cacheDir = Config::get('core.cache_dir') . DIRECTORY_SEPARATOR . self::CACHE_SUBDIR;
521
522
        Toolkit::mkdir($cacheDir, $perms);
523
524
        if ($append && is_readable($cache)) {
525
            $data = file_get_contents($cache) . $data;
526
        }
527
528
        $tmpName = @tempnam($cacheDir, basename($cache));
529
        if (@file_put_contents($tmpName, $data) !== false) {
530
            // that worked, but that doesn't mean we're safe yet
531
            // first, we cannot know if the destination directory really was writeable, as tempnam() falls back to the system temp dir
532
            // second, with php < 5.2.6 on win32 renaming to an already existing file doesn't work, but copy does
533
            // so we simply assume that when rename() fails that we are on win32 and try to use copy() followed by unlink()
534
            // if that also fails, we know something's odd
535
            if (@rename($tmpName, $cache) || (@copy($tmpName, $cache) && unlink($tmpName))) {
536
                // alright, it did work after all. chmod() and bail out.
537
                chmod($cache, $perms);
538
                return;
539
            }
540
        }
541
542
        // still here?
543
        // that means we could not write the cache file
544
        $error = 'Failed to write cache file "%s" generated from ' . 'configuration file "%s".';
545
        $error .= "\n\n";
546
        $error .= 'Please make sure you have set correct write permissions for directory "%s".';
547
        $error = sprintf($error, $cache, $config, Config::get('core.cache_dir'));
548
        throw new CacheException($error);
549
    }
550
551
    /**
552
     * Parses a config file with the ConfigParser for the extension of the given
553
     * file.
554
     *
555
     * @param      string $config         An absolute filesystem path to a configuration file.
556
     * @param      bool   $autoloadParser Whether the config parser class should be autoloaded if
557
     *                                    the class doesn't exist.
558
     * @param      string $validationFile A path to a validation file for this config file.
559
     * @param      string $parserClass    A class name which specifies an parser to be used.
560
     *
561
     * @return     ConfigValueHolder An abstract representation of the
562
     *                                    config file.
563
     *
564
     * @throws     <b>AgaviConfigurationException</b> If the parser for the
565
     *             extension couldn't be found.
566
     *
567
     * @author     Dominik del Bondio <[email protected]>
568
     * @author     David Zülke <[email protected]>
569
     * @since      0.11.0
570
     *
571
     * @deprecated New-style config handlers don't call this method anymore. To be
572
     *             removed in Agavi 1.1
573
     */
574
    public static function parseConfig($config, $autoloadParser = true, $validationFile = null, $parserClass = null)
0 ignored issues
show
The parameter $autoloadParser is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
The parameter $parserClass is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
575
    {
576
        $parser = new ConfigParser();
0 ignored issues
show
Deprecated Code introduced by
The class Agavi\Config\ConfigParser has been deprecated with message: Superseded by XmlConfigParser, will be removed in Agavi 1.1

This class, trait or interface has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the type will be removed from the class and what other constant to use instead.

Loading history...
577
578
        return $parser->parse($config, $validationFile);
0 ignored issues
show
It seems like $validationFile defined by parameter $validationFile on line 574 can also be of type string; however, Agavi\Config\ConfigParser::parse() does only seem to accept array|null, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
579
    }
580
}
581