views:

128

answers:

2

DISCLAIMER: Please read carefully as this is NOT a question about storing arrays in constants or simple eval() or serialize() techniques. This IS a question primarily about how constants work in PHP and why the constant() function is not working to convert a constant name into a constant value. Thanks.

BACKGROUND: For various reasons, I started out with a flat config file for a homebrewed LAMP(PHP) CMS (in private development). Looking back this may have been misguided, and I have transitioned my variable storage into a DB table. However, the bulk of the code still depends on the CONSTs, so I use eval("define(A, B...);") to load the DB values A and B into constants. This part works fine. But it gets a bit more complicated.

PROBLEM: The problem I'm having now is with constants in arrays (NB. NOT arrays in constants). I have a big, GLOBAL array called defaults that contains config settings in the format shown below.

Initially, I declare: <?php define('THIS_IS_A_CONSTANT', 'abcdefg'); ?> (And THIS WORKS...)

Next, I define $GLOBALS['defaults'] as the following nested array:

Array 
(
   'var_name' => Array 
        (
            'display' => THIS_IS_A_CONSTANT,
            'value'   => 12,
            'type'    => 'int',
            'params'  => Array ( ... )
        ),

    ...
    Lots more variables...
    ...

)

To prevent the client (who has file system access but no direct DB access but can change certain values, including most constants, via the CMS's administrative backend) from mucking up this array structure, I serialize the array structure and store that string in the DB. On each page request, I first define all the constants (stored in the DB) using the eval(define(A,B...)), then I unserialize the array above (which was serialized and stored in the DB). However, no matter what I try I cannot get the values at $GLOBALS['defaults']['var_name']['display'] to be recognized as the values that the constants contain. Instead, the constant name shows up and NOT the constant value (in other words, my output contains THIS_IS_A_CONSTANT instead of 'abcdefg'). Very frustrating, right?

I've tried something like the following (where $arr contains the unserialized array that I fetch from the DB):

    foreach ($arr as $item => $contents) {
            $display = isset($contents['display']) ? $contents['display'] : 1;
            $value = constant("$display");

            // This doesn't work, though it seems like it should            
            $contents['display'] = $value;
            // Neither does this, no matter how much I juggle the quotation marks and backslashes
            eval("\$contents['display'] = constant(\"$value\");");
            // or this ...
            eval("\$contents['display'] = $value;");
            // or this ...
            eval("\$contents['display'] = \$value;");
            // or a number of other things...

    }
    $GLOBALS['defaults'] = $arr;

QUESTIONS: Has anyone dealt with this kind of situation before? Can anyone advise me how to force my constants to be recognized as CONSTANTS and not strings. Do I need to serialize my array differently? Or maybe process the unserialized array differently (after retrieving it from the DB)? Is these some combination of eval() and constant() that will allow me to do this? Why are the constants within my array behaving badly while the constants I define normally are working without problem? Any help at all would be greatly appreciated, as I've been puzzling over this for a few days now and haven't come to any solutions.

All the best, Dakota.

A: 

First, why are you evaling? From what you are saying you want the value of $GLOBALS['defaults']['var_name']['display'] to be the value of the constant THIS_IS_A_CONSTANT. You have de-serialized the string from your database and shoved in in $GLOBALS['defaults'] and the string stored the value as the constant name, not the value of the constant.

Have you tried:

<?php 
define('THIS_IS_A_CONSTANT', 'abcdefg');
$value = constant($GLOBALS['defaults']['var_name']['display']);
//$value should now be abcdefg not THIS_IS_A_CONSTANT
$GLOBALS['defaults']['var_name']['display'] = $value;
?>

If "$GLOBALS['defaults']['var_name']['display']" does contain the string THIS_IS_A_CONSTANT then all you should need to do is pass that string to the constant funct.

Am I missing something?

tvanover
A: 

Although eval does have its uses, this is not one of those cases. You have no idea what is being submitted to the eval at run time - and by the sounds of things you are storing something which you are then treating as code in a user-data storage location.

If the constant was defined before the array was declared then the serialized version would contain the value instead of the label. I can only assume that the value may differ depending on the context at run-time.

I would suggest that a better solution would be to roll your own macro language in PHP, e.g. something like:

<?php
global $defs;

$defs=array(
  'THIS_IS_A_CONSTANT' => 'abcdefg',
  'SO_IS_THIS' => 23
);

// for inline constants  
foreach ($defs as $label =>$val) {
  define($label, $key);
}
replacer($defs,true);

function replacer($in, $init=false;)
{
   static $defs;
   static $vals;
   if ($init) {
      $defs=array_keys($in); 
      $vals=array_values($in);
      return count($in);
   }
   return str_replace($defs, $vals, $in);
  // you might want to use preg_replace() with a pattern based on $defs for a neater solution
}

function fix_var(&$in) 
{
   if (is_array($in)) {
      foreach ($in as $key=>$dummy) {
        fix_var($in[$key]);
      }
   } else {
      $in=replacer($in);
   }
}

?>

C.

symcbean
This is essentially the same answer as @tvanover, but is quite a bit more comprehensive, and though the code is not well documented, it provides decent logic that can be reused. I've worked around this problem in other ways, but I think this is a good resource for other travelers struggling with storing constants in php. Thanks for your help.
Dakota R.