views:

243

answers:

7

I am trying to figure out how I want to handle settings in my PHP app. I have pretty much decide that I would like to use a Confg class file so it will be autoloaded and flexible in the future. Below is some stuff I was playing with.

I know you cannot set a variable to popluate a Constant so I then try to use a public static property.

Why can I not set public static $ip = $_SERVER['REMOTE_ADDR']; ??

<?php
// config.class.php
class Config
{
    const URL = 'http://www.foo.com';
    const DB_User = 'dbname';

    public static $test = 'test string';
    public static $ip = $_SERVER['REMOTE_ADDR'];
}

///////////////////////////////////////////////////////
//index.php

// works
echo Config::URL;

// works
echo Config::$test;

// DOES NOT WORK
echo Config::$ip;

?>
+3  A: 

members must be initialized with a constant expression (like a string constant, numeric literal, etc). php will give a parse error if you try to initialize a member with a dynamic expression (like the value of a variable or a function call)...

this is unlike some other langs like python, or javascript which consider class definitions to be on par with executable expressions... so your idea here is very functional, but php doesn't support it now at least...

but there are some alternatives to deal with this:

add the initialization after the class definition:

class C {...}
C::$var = $_SERVER['REMOTE_ADDR'];

or have an init function:

function init()
{
  if (self::$init === false) {
    self::$var = $_SERVER['REMOTE_ADDR'];
    self::$init = true;
  }
}

C::init();

or with newer php you can use the __autoload() hook to do some static initializations...

jspcal
Do you know any good alternatives to get a similar result?
jasondavis
A: 

It won't work because PHP just doesn't allow it.

Generally I wouldn't suggest putting your app's config into a class (at least not in PHP), there are no real advantages in my opinion - better put it into a global array or so :)

lamas
I am thinking of making a class that will pull in an array file so I can just pass in the file path to the array file or something along those lines.
jasondavis
A: 

PHP simply doesn't allow that. If you think about it, it doesn't make sense to have the REMOTE_ADDR as part of your configuration data anyway. You can consider it to be part of the request data, and not the application configuration.

As for your configuration, I would suggest using an .ini file for your configuration because PHP has a built-in INI parser (see the parse_ini_file function). This would be consistent with PHP's choice of INI file format for its own configuration (i.e., php.ini).

Check out this question, for example: http://stackoverflow.com/questions/476892/whats-is-the-best-file-format-for-configuration-files/476908#476908

Neville Flynn
At least tell me why you downvoted. I answered his question and even made a suggestion...
Neville Flynn
+1  A: 

Although this does not answer your question (jspcal answers it correctly), a quick solution that might fit your needs would to use the Singleton design pattern. Here is an alternative:

<?php

// config.class.php
class Config
{
    /**
     * Instance container
     * @var Config
     */
    private static $instance = null;

    /**
     * Constants container
     * @var array
     */
    private $constants = array(
        'url'     => 'http://www.foo.com/',
        'db_user' => 'dbname'
    );

    /**
     * Options container
     * @var array
     */
    private $options = array();

    /**
     * Don't allow outside initialization
     */
    private function __construct()
    {
        // Set options (could load from external source)
        $this->options = array(
            'test' => 'test string',
            'ip'   => $_SERVER['REMOTE_ADDR']
        );
    }

    /**
     * Disable cloning
     */
    private function __clone() { }

    /**
     * Get new instance of class
     */
    public function getInstance()
    {
        if ( null === self::$instance ) {
            self::$instance = new self();
        }
        return self::$instance;
    }

    /**
     * Retrieve value with constants being a higher priority
     * @param $key Key to get
     */
    public function __get( $key )
    {
        if ( isset( $this->constants[$key] ) ) {
            return $this->constants[$key];
        } elseif ( isset( $this->options[$key] ) ) {
            return $this->options[$key];
        }
    }

    /**
     * Set a new or update a key / value pair
     * @param $key Key to set
     * @param $value Value to set
     */
    public function __set( $key, $value )
    {
        $this->options[$key] = $value;
    }
}

///////////////////////////////////////////////////////
//index.php

// works
$config = Config::getInstance();
echo $config->url;
echo $config->test;
echo $config->ip;

Updated: Not sure if you want the constants / options with that kind of priority. It's just an example.

William
Although that is an alternative to what you're looking for, I'd recommend possibly having an array set as private to store the values and using the PHP magic variables __get __set and retrieve values of the array.
William
actually this is kinda what I was thinking of how it would have to be done so thats good +1
jasondavis
Updated with a better example.
William
I will probably take this and build on it to allow for it to read an array file or an ini file, maybe xml file too. On the php ini manual page there is an example class that shows how to do config where it can read like 3-4 different types of files too
jasondavis
+1  A: 

Not a direct answer to your question, but why don't you use a less hardcoded approach, e.g. a generic Config class you can reuse in your apps

// MyConfig.php
class MyConfig {
    protected $_data;

    public function __construct($path)
    {
        $config = include $path;
        $this->_data = $config;
    }

    public function __get($val)
    {
        if(array_key_exists($val, $this->_data)) {
            return $this->_data['val'];
        } else {
            trigger_error("Key $val does not exist", E_USER_NOTICE);
        }
    }
}

that you can fill from an array for a specific app

// app-config.php
return array(
    'ip' => $_SERVER['REMOTE_ADDR'],
    'url' => 'http://www.foo.com';
    'db'  => array(
        'host' => 'foo.com',
        'port' =>  3306
    ),
    'caching' => array(
        'enabled' => false
    )
);

and then use in your bootstrap like

$config = new MyConfig('/path/to/app-config.php');
Gordon
If I understand him correctly, he wants the config to be in the global scope. Although he could easily solve this by changing it to a Singleton pattern or using a Registry pattern.
William
This is probably the route I will go, and yes I do need to make it globaly accessible
jasondavis
I'd prefer a Dependency Injection Service Container for this, but since I have suggested this to Jason on a number of occasions now, I guess he has his reasons for not using one. Without one, I'd go with a registry over a Singleton, just to be able to have multiple Config objects and just in case I need to have something else I want to have globally available that is not a Config. Registry is more dedicated for this.
Gordon
I will probably have a Config object that will be stored in a regisrty object that is injected into all the other objects
jasondavis
+1  A: 

This is not answering your question, but in my opinion, a better way to deal with configurations is actually to use a real configuration file, like an INI or XML file.

You could use e.g. the Zend Config class to read and write such files (and this class can even deal with a plain PHP array as configuration.

In the end this will make your code easier to maintain.

After reading other answers and comments you might also be interested in the Zend Registry class.

In general I would advice to use a framework or readymade components for such stuff. You don't need to reinvent the wheel and you can profit from the other's experience.

Felix Kling
The main reason for building myself is really for the learning experience right now. I love to study code from such things like you posted though
jasondavis
+1  A: 

try to use define() to do that (give a constant!):

// config.class.php
define(REMOTE_ADDR, $_SERVER['REMOTE_ADDR']);

    class Config
    {
        const URL = 'http://www.foo.com';
        const DB_User = 'dbname';

        public static $test = 'test string';
        public static $ip = REMOTE_ADDR;
    }
neobie