【问题标题】:Dynamic constants in PHP?PHP中的动态常量?
【发布时间】:2012-08-24 16:56:58
【问题描述】:

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

  • 我有一个 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. 简单紧凑。在代码中使用时允许使用简单而紧凑的语法。例如,使用常量我可能会写一个条件语句,例如:

    如果($my_var === ENUM_Types::TypeA){}

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

【问题讨论】:

  • 根据定义,常量不能是动态的。为什么不只使用静态变量?
  • 嗯,是的,我知道,这就是为什么我说它“可能看起来有点奇怪”。我会稍微更新一下这个问题,以更清楚地说明我想要实现的目标。
  • 换句话说......你不想要动态常量,你想要动态枚举。
  • 使用一个充当注册表的类怎么样?
  • 我刚看了标题就想“wtf?” O_o

标签: php


【解决方案1】:

将您的“枚举”值包装在一个单例中并实现(非静态)魔术__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!
  }
}

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

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

“DynamicEnums”的消费者看起来像:

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

[编辑] 更像枚举。

【讨论】:

  • 这看起来不错。我相信它符合我的所有要求。非常感谢魔术。 :^)
  • 遗憾的是,我遇到了这种方法的一个很大的限制......也就是说,如果你将一组帮助函数构建到一个基类中,然后为你的各种 Enum 子类化它,你会发现它们都共享来自静态基础的单例。该死,该死,该死。我需要找到一条出路,走出自己所处的这个盒子。
  • 需要帮助动态调用静态方法? stackoverflow.com/questions/2108795/…
【解决方案2】:

问:有没有办法动态创建类的常量?

答案是“是”,但不要那样做 :)

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 ) {}

根据manual,在这种情况下$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());

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

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

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

问:动态枚举。我希望通过前端管理这个枚举并存储在数据库中(我正在使用 wordpress 管理屏幕,以防万一有人关心)。在运行时,应该将这个“列表”从数据库中提取出来,并作为枚举(或实现上述目标的类似结构)提供给代码。

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

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

【讨论】:

  • 非常感谢。很高兴看到如果您愿意走在黑暗的一面(也就是 eval 的一面),如何实现这一目标。
【解决方案3】:

您可以执行类似 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');

【讨论】:

  • 根据定义,常量不能是动态的。它必须设置为文字值。
  • 如何使用受保护的属性并将其用作 Psuedo 常量?
  • 你能描述一下你在回答中的意思吗?
  • 我?我只是说可以静态使用常量,因此您不能在构造函数方法中使用它们。他可能真正想要的只是一个可以设置然后保护的属性。因为在他的使用中,它更像是一个受保护的属性,而不是一个常量。
  • "...你不能在构造方法中执行它们"...self::$var = "value"; 怎么样?这样做可能没有意义,但有可能。
【解决方案4】:

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

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

【讨论】:

    猜你喜欢
    • 2011-04-29
    • 1970-01-01
    • 2018-06-25
    • 2017-07-07
    • 1970-01-01
    • 2022-07-17
    相关资源
    最近更新 更多