开发者

What is the best way to save config variables in a PHP web app?

I often switch between .NET and PHP development. With ASP.NET sites I save configuration information (e.g. connection strings, directories, application setting) in the web.config file which is appropriately protected and 开发者_Go百科easy to access the values, etc.

In PHP, I solve this with a class that has static methods for each variable:

class webconfig {
    public static function defaultPageIdCode() {
        return 'welcome';
    }
}

The file is included by the app variables are accessed with a one-line:

$dp = webconfig::defaultPageIdCode();

And since PHP isn't compiled, it is easy to telnet in and change a value for a website anyway, so this solution works fairly well and gives me these two advantages:

  • I can add logic to a config variable without breaking its interface with the application
  • these config variables appear as intellisense in my e.g. Eclipse, NetBeans, etc.

But I can imagine there are other ways people solve saving web config settings in PHP which may have other advantages.

Especially those who have experience with a number of PHP frameworks, what are other ways of saving config variables and their advantages and disadvantages?


I've decided to list all known methods along with their advantages and disadvantages.

I've marked this answer as a community wiki so collaboration is easier.


Global Constants

Assigning:

  • define('CONFIG_DIRECTIVE', 'value');

Accessing:

  • $object = new MyObject(CONFIG_DIRECTIVE);

Advantages:

  • Has global scope.
  • Autocompleted by most IDEs.
  • Has an agreed upon naming convention (UPPERCASE_UNDERSCORE_SEPARATED).

Disadvantages:

  • Directives cannot contain arrays (prior to v7.0.0).

Special Notes:

  • Cannot be reassigned.

Alternate Syntax Files

For example: XML, INI, YAML, etc.

Assigning:

  • Simply edit the file in it's specific language. (For example, for INI files: config_directive = value.)

Accessing:

  • The config file needs to be parsed. (For example, for INI's: parse_ini_file().)

Advantages:

  • Most likely has a syntax more suited for a config file.

Disadvantages:

  • Possible overhead of accessing and parsing the file.

Array

Assigning:

  • $config['directive'] = 'value';

Accessing:

  • The cleanest method of accessing configuration values using this method is to pass the required values to the object that needs them on creation, or pass them to your container object and let it handle passing them out internally.
    • $object = new MyObject($config['directive']);
    • $container = new MyContainer($config);

Advantages:

  • Directives can be arrays.

Disadvantages:

  • No autocompletion.

Special Notes:

  • Variable collisions can occur. If this is a concern, name your array appropriately to avoid them.

Class

Assigning:

  • There are many different class based implementations.
    • Static class.
      • myCfgObj::setDirective('DIRECTIVE', 'value');
    • Instanced class.
      • myCfgObj->setDirective('DIRECTIVE', 'value');

Accessing:

  • Again there are various class based implementations.
    • Static class.
      • $object = new MyObject(myCfgObj::getDirective('DIRECTIVE'));
    • Instanced class.
      • $object = new MyObject(myCfgObj->getDirective('DIRECTIVE'));

Advantages:

  • Can be autoloaded.

Disadvantages:

  • Tends to be a bit verbose.
  • Can be hard to maintain if a container class is not being used.


I tend to use a Settings static class in PHP, this is because

  • It has a global scope.
  • You can enable/disable changes to protected configs.
  • You can add any settings during anywhere within runtime.
  • You can make the class automated to fetch public configs from a file/database.

Example:

abstract class Settings
{
    static private $protected = array(); // For DB / passwords etc
    static private $public = array(); // For all public strings such as meta stuff for site

    public static function getProtected($key)
    {
        return isset(self::$protected[$key]) ? self::$protected[$key] : false;
    }

    public static function getPublic($key)
    {
        return isset(self::$public[$key]) ? self::$public[$key] : false;
    }

    public static function setProtected($key,$value)
    {
        self::$protected[$key] = $value;
    }

    public static function setPublic($key,$value)
    {
        self::$public[$key] = $value;
    }

    public function __get($key)
    {//$this->key // returns public->key
        return isset(self::$public[$key]) ? self::$public[$key] : false;
    }

    public function __isset($key)
    {
        return isset(self::$public[$key]);
    }
}

Then within your runtime, if you loaded this file first, followed by your database config file, your database config file would look like so:

<?php
Settings::setProtected('db_hostname', 'localhost');
Settings::setProtected('db_username', 'root');
Settings::setProtected('db_password', '');
Settings::setProtected('db_database', 'root');
Settings::setProtected('db_charset', 'UTF-8');
//...
echo Settings::getProtected('db_hostname'); // localhost
//...
Settings::setPublic('config_site_title', 'MySiteTitle');
Settings::setPublic('config_site_charset', 'UTF-8');
Settings::setPublic('config_site_root', 'http://localhost/dev/');

As you can see the we have a method __get that should only be allowed to grab public variables, An example of why we have this is as follows:

$template = new Template();
$template->assign('settings', new Settings());

Regardless the fact that we have used this object as a static object, the values should still stand so within the template you can now do, lets say.

<html>
    <head>
        <?php echo isset($settings->config_site_title) ? $settings->config_site_title : 'Fallback Title'; ?>
    </head>
</html>

And this will only allow you to have access to the public data during the initialized period.

This can get a lot more complex but more system friendly, some examples:

  • A loadConfig method to automatically parse a config file, xml, php, yaml.
  • If you register an shutdown_function you can auto update the database with new settings.
  • You can auto-populate the class with config from that database.
  • You can implement iterators to make it compatible with looping.
  • Lots more.

This too me is by far the best methods to complete this job.


The way I do it is directly store them in an array and save the file as config.php

<?php

$config['dbname'] = "mydatabase";
$config['WebsiteName'] = "Fundoo Site";
$config['credits'] = true;
$config['version'] = "4.0.4";

?>

Thi is the way most PHP frameworks like Wordpress, etc do it.


In PHP I always use a ".htaccess" to protect my config file (Double protection)


Note: "Best way" never exists. Every application and framework doing it their own style. While your example is doing the trick i think it's a little bit resource-heavy for a simple config file.

  • You can do it with single variables like Amber pointed out
  • You can do it with arrays this is the most common approach and you can always easily edit your config file.
  • You can do it with .ini files that PHP easily parsing

Edit:

Edward please take a look at parse_ini_file's examples. You can load the .ini file with a simple command then you can use the variables in a class like in your example.


There are pretty much possibilities I think, but the most common methods are storing as plain text in files like .csv, .ini, .xml. With little tricks you can protect these files, so that no one can load the files directly.

an INI-File example:

;<?php die(); ?>
[config1]
var1 = 'value1';
var2 = 'value2';
...
[config2]
...

The ; is considered a comment in ini files. So when you read in the file with an ini-parser, this line will be ignored. If someone accesses the file directly via url the die()-function will be executed. This works only, if the INI-file wears a file-extension like .php so that the server knows that this should be executed and not diplayed as plain text.

Possible disadvantage of most file-base-config-storages are problems with some utf8-characters.

Zend_Config is a component of the Zend-Framework, which provides possibilities for several storage-adapters with an easy to use api.


Since PHP is able to use OO, I like to go with a "Config class":

class Config
{
    /**
     * ---------------------------------
     * Database - Access
     * --------------------------------- 
     */

    /**
     * @var String
     */
    const DB_DRIVER = 'pgsql';

    const DB_USER = 'postgres';

    const DB_PASSWORD = 'postgres';

    const DB_NAME = 'postgres';
}

It is easy accessable with Config::DB_DRIVER. No need to include the file since the application autoloader will do that for you. Of course, protecting the file still needs to be done.


Telnet? OMG I've fallen into a timewarp and arrived in 1992!

But seriously, IIRC there are tools which allow asp.net (and other languages) to parse session data - which is just a serialized php array. I'd have a go at implementing the global settings as a sort of shadow session within PHP. Even if you don't store your config settings as a serialized PHP array, you could map them into the session at runtime using your own session handler.

In terms of where you store the data - that's a more tricky one when you're presumably running on a Microsoft platform. Obviously you don't want the expense of disk access for every request. Although NT does some disk caching it's not (IME) quite as effective as other OS. Memcached appears to be one solution to this. It does appear to be usable from asp.net.

HTH


The usual route is to use define:

define('MYSQL_USER', 'ROOT');

and access it all over the application via MYSQL_USER :

$user = MYSQL_USER;

However arrays are not supported in this way.


There are few possibilities:

  1. You can use config file (ini, json, xml or yaml). For ini you have parse_ini_file, for JSON there is json_decode (+file_get_contents), for YAML you have to use external library (search for sfYaml)

  2. You can have a config file with variables or constants (better for immutable config and accessible in all scopes), which you includes in your script:

    define('ROOT_DIR', '\home\www');

    $sRootDir = '\home\www';

If you are OO-oriented, you can wrap it in class, as properties - you do not have getter method for every property, you can just have:

class Config
{
    public $var1 = 'xxx';
    public $var2 = 'yyy';
}

($c = new Config(); print $c->var1)

or

static class Config
{
    public static $var1 = 'xxx';
    public static $var2 = 'yyy';
}

(print c::$var1)

The best is to have registry-type class, implementing singleton pattern and capable to read config from given file.


In order to be testable, I use a Config class that holds the actual configuration data and an AppConfig static class that holds a reference to a Config object loaded at bootstrap from application wide configuration files (dependency injected at bootstrap). In the tests environment, I only change the Config object. See https://github.com/xprt64/Config

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜