Как инициализировать статические переменные - PullRequest
200 голосов
/ 29 марта 2009

У меня есть этот код:

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
);

Что дает мне следующую ошибку:

Ошибка разбора: синтаксическая ошибка, неожиданное '(', ожидание ')' в /home/user/Sites/site/registration/inc/registration.class.inc строке 19

Итак, я думаю, что я делаю что-то не так ... но как я могу это сделать, если не так? Если я изменю mktime с помощью обычных строк, это сработает. Так что я знаю, что могу сделать это вроде вот так ..

У кого-нибудь есть указатели?

Ответы [ 9 ]

336 голосов
/ 29 марта 2009

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();
}
32 голосов
/ 17 декабря 2010

Если у вас есть контроль над загрузкой классов, вы можете выполнить статическую инициализацию оттуда.

Пример:

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() }
23 голосов
/ 16 октября 2011

Вместо того, чтобы искать способ заставить работать статические переменные, я предпочитаю просто создать функцию получения. Также полезно, если вам нужны массивы, принадлежащие определенному классу, и намного проще в реализации.

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...
}
10 голосов
/ 21 октября 2013

Я использую комбинацию ответа 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 голосов
/ 29 марта 2009

Это слишком сложно установить в определении. Вы можете установить для определения значение null, а затем в конструкторе проверить его, и если оно не было изменено - установить его:

private static $dates = null;
public function __construct()
{
    if (is_null(self::$dates)) {  // OR if (!is_array(self::$date))
         self::$dates = array( /* .... */);
    }
}
4 голосов
/ 29 марта 2009

Вы не можете выполнять вызовы функций в этой части кода. Если вы создадите метод типа init (), который выполняется раньше, чем любой другой код, тогда вы сможете заполнить переменную.

3 голосов
/ 26 января 2017

В PHP 7.0.1 я смог определить это:

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

А затем используйте это так:

MyClass::$kIdsByActions[$this->mAction];
3 голосов
/ 03 сентября 2013

Лучший способ - создать такой метод доступа:

/**
* @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 (); откуда угодно.

0 голосов
/ 11 июля 2013

Вот, надеюсь, полезный указатель в примере кода. Обратите внимание, что функция инициализатора вызывается только один раз.

Кроме того, если вы измените вызовы на 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
)
...