Yesterday, I hit a wall in PHP that took a little brain power to solve. I posted the solution on the web for everybody to see. A little back-story:
Constants in PHP (the const keyword) can not have dynamic values. Defines can. In other words, only ONE of the two following lines is valid PHP code:
define(‘THE_CURRENT_TIME’, time()); // OKAY
const THE_CURRENT_TIME = time(); // FAIL
This gets annoying when you are building constants that are pieced together from other constants. Constants are useful because they are, essentially, a way to name two constants the same thing. For example, two classes can both have a constant called NAME, and you don’t have to worry. This is better because separate developers don’t have to know what defines are already being used.
But PHP doesn’t allow constants to have dynamic values. This is annoying. Why not? Probably so that two separate instances of a class don’t have differing values for a given constant (imagine a const with the definition equal to rand()). Sure, it makes sense, but then why aren’t there read-only variables in classes!?
Scraping my post:
In realizing it is impossible to create dynamic constants, I opted for a “read only” constants class.
<?php
abstract class aClassConstant {/**
* Setting is not permitted.
*
* @param string constant name
* @param mixed new value
* @return void
* @throws Exception
*/
final function __set($member, $value) {
throw new Exception(‘You cannot set a constant.’);
}/**
* Get the value of the constant
*
* @param string constant name
* @return void
*/
final function __get($member) {
return $this->$member;
}
}
?>The class would be extended by another class that would compartmentalize the purpose of the constants. Thus, for example, you would extend the class with a DbConstant class for managing database related constants, that might look like this:
<?php
/**
* Constants that deal only with the database
*/
class DbConstant extends aClassConstant {
protected $host = ‘localhost’;
protected $user = ‘user’;
protected $password = ‘pass’;
protected $database = ‘db’;
protected $time;
/**
* Constructor. This is so fully dynamic values can be
* set. This can be skipped and the values can be
* directly assigned for non dynamic values as shown
* above.
*
* @return void
*/
function __construct() {
$this->time = time() + 1; // dynamic assignment
}
}
?>You would use the class like thus:
<?php
$dbConstant = new DbConstant();
echo $dbConstant->host;
?>The following would cause an exception:
<?php
$dbConstant = new DbConstant();
$dbConstant->host = ‘127.0.0.1’; // EXCEPTION
?>It’s not pretty, nor ideal, but at least you don’t pollute the global name space with long winded global names and it is relatively elegant.
Variables must be *protected*, not public. Public variables will bypass the __get and __set methods!! This class is, by design, not meant to be extended much further than one level, as it is really meant to only contain constants. By keeping the constant definition class separate from the rest of your classes (if you are calling this from a class), you minimize the possibility of accidental variable assignment.
Managing this instance may be a slight pain that requires either caching a copy of the instance in a class variable, or using the factory pattern. Unfortunately, static methods can’t detect the correct class name when the parent name is used during the call (e.g., DbConstant::instance()). Thus there is no elegant, inheriting solution to that problem. Thus, it is easier to simply manage a single instance that is declared using conventional notation (e.g., new DbConstant…).
It’s not the best solution I’ve ever came up with, but it’s a hell of a lot better than having giant config files with 50 character long defines in it.
Why an abstract class? Well, the whole point of constants, as I mentioned, is so that you don’t have a big pile of names in one namespace (so you can have two constants called “NAME,” for example). Any benefit of constants would be lost if I made a single unified “constants class.” Thus, I made it abstract. An abstract class can not be used without first being extended. This forces a developer who might use my code to properly separate out the various constants he/she may have into their own class groupings.