如何初始化静态变量


207

我有以下代码:

private static $dates = array(
  'start' => mktime( 0,  0,  0,  7, 30, 2009),  // Start date
  'end'   => mktime( 0,  0,  0,  8,  2, 2009),  // End date
  'close' => mktime(23, 59, 59,  7, 20, 2009),  // Date when registration closes
  'early' => mktime( 0,  0,  0,  3, 19, 2009),  // Date when early bird discount ends
);

这给了我以下错误:

解析错误:语法错误,在第19行的/home/user/Sites/site/registration/inc/registration.class.inc中出现意外的'(',期待')'

所以,我想我做错了什么...但是如果不那样做怎么办?如果我用常规字符串更改mktime内容,则可以正常工作。所以,我知道我能做到这一点的那种像..

有人有指针吗?



2
第一个答案是投票过的。请参阅stackoverflow.com/a/4470002/632951
Pacerier,2013年

1
@Pacerier我不这么认为。回答#2有很多的开销相比,来回答#1
Kontrollfreak

2
@Pacerier询问10个人,没有人愿意。
布法罗

Answers:


345

PHP无法在初始化程序中解析非平凡的表达式。

我更喜欢通过在类定义之后添加代码来解决此问题:

class Foo {
  static $bar;
}
Foo::$bar = array(…);

要么

class Foo {
  private static $bar;
  static function init()
  {
    self::$bar = array(…);
  }
}
Foo::init();

PHP 5.6现在可以处理一些表达式。

/* For Abstract classes */
abstract class Foo{
    private static function bar(){
        static $bar = null;
        if ($bar == null)
            bar = array(...);
        return $bar;
    }
    /* use where necessary */
    self::bar();
}

135
我爱PHP,但是有时候真的很奇怪。
Marco Demaio 2011年

6
我知道这很旧,但是我也使用这种方法。但是,我发现有时不调用Foo :: init()。我从来没有找到原因,只是想让所有人知道。
幸运的2012年

1
@porneL,第一种方法无效,因为您无权访问私有变量。第二种方法可行,但是它迫使我们init公开它是丑陋的。有什么更好的解决方案?
Pacerier,2013年

2
@Pacerier第一种方法使用公共财产是有原因的。AFAIK目前在PHP中没有更好的解决方案(尽管Tjeerd Visser的回答还不错)。隐藏在类加载器中的hack不会使其消失,会导致错误的继承,并且它的一些聪明之处可能会意料之外地破坏(例如,当文件需要require()d时)。
Kornel

1
@porneL简单数组在PHP 5.6.x中对我有效,尽管在RFC中未提及。范例:class Foo {public static $bar = array(3 * 4, "b" => 7 + 8);} var_dump(Foo::$bar);
庞(Pang)

32

如果您可以控制类加载,则可以从那里进行静态初始化。

例:

class MyClass { public static function static_init() { } }

在类加载器中,执行以下操作:

include($path . $klass . PHP_EXT);
if(method_exists($klass, 'static_init')) { $klass::staticInit() }

更重的解决方案是使用带有ReflectionClass的接口:

interface StaticInit { public static function staticInit() { } }
class MyClass implements StaticInit { public static function staticInit() { } }

在类加载器中,执行以下操作:

$rc = new ReflectionClass($klass);
if(in_array('StaticInit', $rc->getInterfaceNames())) { $klass::staticInit() }

这在某种程度上类似于c#中的静态构造函数,多年来我一直在使用类似的东西,并且效果很好。
克里斯(Kris)2013年

@EmanuelLandeholm,方法一快还是方法二快?
Pacerier,2013年

@Kris这不是巧合。回答时,我受到c#的启发。
伊曼纽尔·兰德霍姆

1
@Pacerier我没有证据,但我怀疑ReflectionClass()可能会产生更多开销。OTOH,第一种方法做出某种危险的假设,即由类加载器加载的任何类中称为“ static_init”的任何方法都是静态初始化器。这可能会导致难以跟踪的错误,例如。与第三方课程。
伊曼纽尔·兰德霍尔姆

23

与其寻找使静态变量起作用的方法,不如直接创建一个getter函数。如果您需要属于特定类的数组,并且易于实现,也很有用。

class MyClass
{
   public static function getTypeList()
   {
       return array(
           "type_a"=>"Type A",
           "type_b"=>"Type B",
           //... etc.
       );
   }
}

无论您在哪里需要列表,只需调用getter方法。例如:

if (array_key_exists($type, MyClass::getTypeList()) {
     // do something important...
}

11
尽管这是一个优雅的解决方案,但出于性能方面的考虑,我不会说它是理想的,主要是因为可能初始化数组的次数(即大量堆分配)。由于php是用C编写的,因此我想翻译会解析为一个函数,该函数每次调用返回指向Array的指针……仅需2美分。
zeboidlund 2012年

此外,函数调用在PHP中非常昂贵,因此,如果不需要,最好避免使用它们。
马克·罗斯

14
“在不必要时最好避免它们”-并非如此。如果它们(可能)成为瓶颈,请避免使用它们。否则,这是过早的优化。
Psycho Brm 2013年

2
@blissfreak-如果我们在类中创建一个静态属性,并检查getTypeList()是否已经初始化并返回它,则可以避免实现。如果尚未初始化,则将其初始化并返回该值。
Grantwparks

12
我很认真地避免尝试避免函数调用。函数是结构化编程的基础。
Grantwparks 2013年

11

我结合使用了Tjeerd Visser和porneL的答案。

class Something
{
    private static $foo;

    private static getFoo()
    {
        if ($foo === null)
            $foo = [[ complicated initializer ]]
        return $foo;
    }

    public static bar()
    {
        [[ do something with self::getFoo() ]]
    }
}

但是,更好的解决方案是取消静态方法并使用Singleton模式。然后,您只需在构造函数中进行复杂的初始化。或者将其设为“服务”,然后使用DI将其注入到需要它的任何类中。


10

太复杂了,无法在定义中设置。您可以将定义设置为null,然后在构造函数中进行检查,如果尚未更改,请进行设置:

private static $dates = null;
public function __construct()
{
    if (is_null(self::$dates)) {  // OR if (!is_array(self::$date))
         self::$dates = array( /* .... */);
    }
}

10
但是构造函数对永远不会实例化的抽象类有什么帮助吗?
Svish

除非已完成并实例化抽象类,否则无法使用它。上面的设置不必专门在构造函数中完成,只要在要使用该变量之前的某个地方调用它即可。
Alister Bulman

假设在需要静态变量之前调用构造函数并不是一个好主意。创建实例之前,通常需要一个静态值。
ToolmakerSteve


4

在PHP 7.0.1中,我能够定义以下内容:

public static $kIdsByActions = array(
  MyClass1::kAction => 0,
  MyClass2::kAction => 1
);

然后像这样使用它:

MyClass::$kIdsByActions[$this->mAction];

FWIW:您显示的内容不需要PHP 7。在被问到这个问题时,它工作得很好:“如果我用常规字符串更改mktime东西,它将起作用。” 当初始化需要调用一个或多个函数时,此线程正在寻找用于初始化静态的技术。
ToolmakerSteve

3

最好的方法是创建这样的访问器:

/**
* @var object $db : map to database connection.
*/
public static $db= null; 

/**
* db Function for initializing variable.   
* @return object
*/
public static function db(){
 if( !isset(static::$db) ){
  static::$db= new \Helpers\MySQL( array(
    "hostname"=> "localhost",
    "username"=> "root",
    "password"=> "password",
    "database"=> "db_name"
    )
  );
 }
 return static::$db;
}

那么你可以做static :: db(); 或self :: db(); 从任何地方。


-1

在代码示例中,这是一个很有帮助的指针。注意初始化函数仅被调用一次。

同样,如果您将呼叫取反StaticClass::initializeStStateArr()$st = new StaticClass()您将获得相同的结果。

$ cat static.php
<?php

class StaticClass {

  public static  $stStateArr = NULL;

  public function __construct() {
    if (!isset(self::$stStateArr)) {
      self::initializeStStateArr();
    }
  }

  public static function initializeStStateArr() {
    if (!isset(self::$stStateArr)) {
      self::$stStateArr = array('CA' => 'California', 'CO' => 'Colorado',);
      echo "In " . __FUNCTION__. "\n";
    }
  }

}

print "Starting...\n";
StaticClass::initializeStStateArr();
$st = new StaticClass();

print_r (StaticClass::$stStateArr);

产生:

$ php static.php
Starting...
In initializeStStateArr
Array
(
    [CA] => California
    [CO] => Colorado
)

2
但请注意,您创建了类(对象)的实例,因为构造函数是公共的NONSTATIC函数。问题是:PHP是否仅支持静态构造函数(不创建实例。例如在Java中static { /* some code accessing static members*/ }
Mitja Gustin,
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.