PHP中的动态常量


Dynamic constants in PHP?

有没有一种方法可以动态创建类的常量?我知道这听起来有点奇怪,但让我解释一下我想做什么:

  • 我有一个Enum类,它的属性由静态const定义定义
  • 这个类扩展了PHP SplEnum类
  • 与其在代码中键入这些定义中的每一个,我更希望有一个静态初始化程序去数据库并提取枚举值

也许像这样的东西:

class myEnum extends SplEnum {
    public static function init () {
        $myNameValuePair = DB_Functions::get_enum_list();
        foreach ( $myNameValuePair as $name => $value) {
            $const = array ( self , $name );
            $const = $value;
        }
    }
}

我认识到这实际上不会起作用,因为它没有设置CONST,而是设置静态变量。也许我的整个想法都是头脑发热的,还有更好的技巧。无论如何,任何实现最终目标的方法都是非常值得赞赏的。

更新

我认为更加明确我的目标可能会有所帮助,因为我认为我对常数的使用完全可能不好。基本上我想实现的是列举列表的典型要求:

  1. 约束函数签名。我希望能够请求一组值作为函数的输入。例如:

    公共函数do_something(ENUM_Types$type){}

  2. 简单紧凑。在代码中使用时,允许使用简单紧凑的语法。例如,使用常量,我可能会写一个条件语句,类似于:

    if($my_var==ENUM_Types::TypeA){}

  3. 动态枚举。我希望这个枚举通过前端进行管理,并存储在数据库中(如果有人关心的话,我会使用wordpress管理屏幕)。在运行时,这个"列表"应该从DB中取出,并作为枚举(或实现上述目标的类似结构)提供给代码。

将"enum"值封装在一个单例中,并实现(非静态)神奇的__get方法:

<?php
class DynamicEnums {
  private static $singleton;
  private $enum_values;
  public static function singleton() {
    if (!self::$singleton) {
        self::$singleton = new DynamicEnums();
    }
    return self::$singleton;
  }
  function __construct() {
    $this->enum_values = array( //fetch from somewhere
        'one' => 'two',
        'buckle' => 'my shoe!',
    );
  }
  function __get($name) {
    return $this->enum_values[$name]; //or throw Exception?
  }
  public static function values() {
    return self::singleton()->enum_values; //warning... mutable!
  }
}

对于奖励积分,创建一个返回singleton的(非OO)函数:

function DynamicEnums() {
    return DynamicEnums::singleton();
}

"DynamicEnums"的使用者看起来像:

echo DynamicEnums::singleton()->one;
echo DynamicEnums()->one;            //can you feel the magic?
print_r(DynamicEnums::values());

[edit]更像枚举。

Q: 有没有一种方法可以动态地创建类的常量?

答案是肯定的,但不要这么做:)

class EnumFactory {
   public static function create($class, array $constants) {
       $declaration = '';
       foreach($constants as $name => $value) {
           $declaration .= 'const ' . $name . ' = ' . $value . ';';
       }
       eval("class $class { $declaration }");
   }
}
EnumFactory::create('darkSide', array('FOO' => 1, 'BAR' => 2));
echo darkSide::FOO . ' ' . darkSide::BAR;

下一个问题。。。

Q: 约束函数签名。我希望能够请求一组值作为函数的输入。例如:public function do_something ( ENUM_Types $type ) {}

根据手册,在这种情况下,$type必须是ENUM_Types类的实例。但对于常量,这是不可能的(它们不能包含对象)。

但是等等。。。我们可以使用这样的技巧:

class Enum {
    protected static $_constantToClassMap = array();
    protected static function who() { return __CLASS__; }
    public static function registerConstants($constants) {
        $class = static::who();
        foreach ($constants as $name => $value) {
            self::$_constantToClassMap[$class . '_' . $name] = new $class();
        }
    }
    public static function __callStatic($name, $arguments) {
        return self::$_constantToClassMap[static::who() . '_' . $name];
    }
}
class EnumFactory {
    public static function create($class, $constants) {
        $declaration = '';
        foreach($constants as $name => $value) {
            $declaration .= 'const ' . $name . ' = ' . $value . ';';
        }
        eval("class $class extends Enum { $declaration protected static function who() { return __CLASS__; } }");
        $class::registerConstants($constants);
    }
}
EnumFactory::create('darkSide', array('FOO' => 1, 'BAR' => 2));
EnumFactory::create('aaa', array('FOO' => 1, 'BAR' => 2));
echo (aaa::BAR() instanceof aaa) ? 'Yes' : 'No'; // Yes
echo (aaa::BAR() instanceof darkSide) ? 'Yes' : 'No'; // No

之后我们可以使用"类型提示":

function doSomething(darkSide $var) {
    echo 'Bu!';
}
doSomething(darkSide::BAR());
doSomething(aaa::BAR());

Q: 简洁紧凑。在代码中使用时,允许使用简单紧凑的语法。例如,通过使用常量,我可能会编写一个条件语句,类似于:if ( $my_var === ENUM_Types::TypeA ) {}

您可以使用以下形式的伪常量值:

if (darkSide::FOO === 1) {}

Q: 动态枚举。我希望这个枚举通过前端进行管理,并存储在数据库中(如果有人关心的话,我会使用wordpress管理屏幕)。在运行时,这个"列表"应该从DB中取出,并作为枚举(或实现上述目标的类似结构)提供给代码。

您可以通过将数组传递到EnumFactory::create($class, $constants):来初始化枚举

EnumFactory::create('darkSide', array('FOO' => 1, 'BAR' => 2));

您可以执行类似Const=$$constant的操作。然后你可以设置$contant=什么。或者,您可以使用受保护的属性,因为您希望它是动态的,而常量不是。示例:

class Foo {
protected $test = '';
function set($bar){
    $this->test = $bar;
}
function get($bar){
    return $this->test;
}

}
$foobar = new Foo();
$foobar->set('test');
echo $foobar->get('test');

我不推荐它,但eval()。。。请不要。

我已经修改了自动加载器,以自动定义丢失或拼写错误的异常类型。原因:您可以捕获未捕获的异常,但在实例化异常类中的拼写错误时,无法从PHP_FATAL中恢复。