Quantcast
Channel: AF-Design » PHP
Viewing all articles
Browse latest Browse all 10

Accessing PHP Constants In Heredoc Strings and More

$
0
0

PHP’s built in heredoc string syntax is extremely valuable for creating blocks of HTML within a PHP file, however, it’s not currently possible to include a constant value within the block. There are a number of scenarios where you might want to do this, for example, including the base url of an image server as part of a configuration file. This provides flexibility if you want to change the server name for any reason in the future.

Until now you really only had three choices, each of which have their own risks and limitations.

1) Assign the “constant” to a variable, as part of your configuration file. This defeats the purpose of using a constant. It’s vulnerable to being overwritten at any time by some other piece of code.

2) Assign the constant to a variable just before it’s needed. This adds additional code to update and maintain and may clobber other variables – possibly even other “constants”.

3) Create a function which accesses allows access constants, providing variable like syntax. This solves the problem of getting access to the constant in heredoc, but is limited to only accessing the value. An example is provided at the bottom of this post if your curious how it works.

As an alternative, it’s possible to wrap access to the constants in a class file. This example code might be spread over multiple files, but is consolidated here for illustrative purposes.

// a configuration constant
define('IMAGE_URL', 'http://example.com/images/');
 
// setup our new constant class and assign an additional value
require_once('constants.class.php');
$_CONST = new Constants();
 
// assigning a new constant
$_CONST->WEB_URL = 'http://example.com/';
define('AJAX_URL', 'http://example.com/ajax.php');
 
// accessing the value in heredoc
print <<<HTML
<a href="{$_CONST->WEB_URL}page.php">
     <img src="{$_CONST->IMAGE_URL}filename.png" />
</a>
 
Ajax calls will be handled by {$_CONST->AJAX_URL}
\n
HTML;
 
// attempting to overwrite a value 
$_CONST->IMAGE_URL = 'http://cdn.example.com/'; // raises an error
 
// using traditional syntax to access the constant values
print WEB_URL . "\n"; // prints 'http://example.com/'
print IMAGE_URL . "\n"; // prints 'http://example.com/images/'

This approach is better than the third way because a) It provides good error messaging including the originating filename and line number. b) It provides consistent access to set and get values making it easier for developers. However, there are drawbacks as well, this method incurrs additional processing overhead and memory use.

This core of the class can be described as overloading the __get() and __set() methods to access constants. In the case of the __set, it allows us to check that the value doesn’t exist, is of a valid type that won’t throw errors. For __get() we check for the existance of the constant in the internal array – refreshing as needed and returning the appropriate value.

The class raises consistent, meaningful errors, equivalent to the PHP errors raised when abusing constants. This gives developers a resource for tracking down potentially problematic code, including not only the file and line number where the error originated.

<?php
/*
 
	Class to access and set constants. Especially useful for accessing 
	constants using variable notation as in heredoc notation while 
	protecting the actual value.
 
	define('firstconstant', 'hello');
	$_CONST = new Constants();
	$_CONST->secondconstant = 'world';
	print <<<TXT
$_CONST->firstconstant $_CONST->secondconstant
TXT;
 
	// this raises an error and preserves the value in the internal array
	$_CONST->firstconstant = 'goodbye';
	print <<<TXT
$_CONST->firstconstant $_CONST->secondconstant
TXT;
 
 
*/
 
if(!function_exists("get_defined_constants")){ 
	throw new Exception ('Constants class requires function get_defined_constants()');
}
 
class Constants {
 
	const E_NOTICE = E_USER_NOTICE;
	const E_WARNING = E_USER_WARNING;
 
	protected $constants;
 
	// Create a new instance
	public function __construct(){
		$this->get_defined_constants();
		return $this;
	}
 
	// Handle the process for accessing a constant
	public function __get($key){
		if(!is_string($key)){
			$trace = $this->getTrace();
			trigger_error('Constants are defined as strings ' . $trace, self::E_WARNING);
			return;
		}
		if(!array_key_exists($key, $this->constants)){
			$this->get_defined_constants();
		}
		return $this->get($key);
	}
 
	// Define a new constant, raising any appropriate notices or warnings.
	public function __set($key, $value){
		if(!is_string($key)){
			$trace = $this->getTrace();
			trigger_error('Constants must be declared as strings ' . $trace, self::E_WARNING);
			return;
		}
		$this->get_defined_constants();
		if(!array_key_exists($key, $this->constants)){
			if(!is_scalar($value)){
				$trace = $this->getTrace();
				trigger_error('Constants may only evaluate to scalar values ' . $trace, self::E_WARNING);
				$value = null;
			} else {	
				define($key, $value);
			}
		} else {
			$trace = $this->getTrace();
			trigger_error('Constant ' . $key . ' already defined ' . $trace, self::E_NOTICE);
		}
		return;
	}
 
	// Provide an actual value back, raise a notice as necessary.
	private function get($key){
		if(array_key_exists($key, $this->constants)){
			return $this->constants[$key];
		} else {
			$trace = $this->getTrace();
			trigger_error('Use of undefined constant ' . $key . ' - assumed \'' . $key . '\' '  . $trace, self::E_NOTICE);
			return $key;
		}
	}
 
	// Update the internal array of values.
	private function get_defined_constants(){
		$this->constants = get_defined_constants();
	}
 
	// Provides meaningful trace information about the original
	// file and line number that invoked the method throwing 
	// an error.
	private function getTrace(){
		try{
			throw new Exception('test');
		} catch (Exception $e){
			$trace = $e->getTrace();
			$trace = array_pop($trace);
			return "in {$trace['file']} on line {$trace['line']} from class {$trace['class']}";
 
		}
	}
}
?>

The method described in 3) above is to assign a function to a variable as seen here. Notice the syntax differs slightly because you actually call a function. A potential drawback of this approach includes injection vulnerabilities. Because you are actually executing a function, it’s possible to inject malicious code into the function. This risk could be mitigated by validating the value of $key before.

 
function _CONST($key) { return $key; }
$_CONST = '_CONST';
 
define('IMAGE_URL', 'http://example.com/images/');
 
print <<<HTML
<img src="{$_CONST(IMAGE_URL)}filename.png">
HTML;

Viewing all articles
Browse latest Browse all 10

Trending Articles