정적 변수를 초기화하는 방법


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

1
@Pacerier 나는 그렇게 생각하지 않습니다. 답변 # 2답변 # 1에
Kontrollfreak

2
@Pacerier는 10 명에게 묻습니다.
버팔로

답변:


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

6
나는 이것이 오래된 것을 알고 있지만 나도이 방법을 사용합니다. 그러나 때로는 Foo :: init ()이 호출되지 않는다는 것을 알았습니다. 나는 그 이유를 추적 할 수 없었지만 모든 것을 알고 싶어했습니다.
lucifurious

1
개인 변수에 액세스 할 수 없으므로 @porneL, 첫 번째 방법이 작동하지 않습니다. 두 번째 방법은 효과가 있지만 init추악한 공개 방법을 강요합니다 . 더 나은 해결책은 무엇입니까?
Pacerier

2
@Pacerier 첫 번째 방법은 이유 때문에 공공 재산을 사용합니다. AFAIK 현재 PHP에는 더 나은 솔루션이 없습니다 (Tjeerd Visser의 답변은 나쁘지 않습니다). 클래스 로더에서 해킹을 숨기는 것은 사라지지 않고, 상속을 강제로하며, 예기치 않게 깨질 수있는 영리함입니다 (예 : 파일이 명시 적으로 요구 될 때).
Kornel

1
@porneL 간단한 배열은 RFC에 언급되지 않았지만 PHP 5.6.x에서 작동합니다. 예 :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

@EmanuelLandeholm, 방법 1이 더 빠르거나 2가 더 빠릅니다.
Pacerier

@Kris 그것은 우연의 일치가 아닙니다. 나는 대답 할 때 C #에서 영감을 얻었습니다.
엠마누엘 Landeholm

1
@Pacerier 증거는 없지만 ReflectionClass ()가 더 많은 오버 헤드를 유발할 것으로 의심됩니다. 첫 번째 방법 인 OTOH 는 클래스 로더가로드 한 모든 클래스 에서 "static_init"라는 메소드 가 정적 초기화 프로그램 이라는 다소 위험한 가정을 합니다 . 이로 인해 버그를 추적하기가 어려울 수 있습니다. 타사 수업과 함께.
엠마누엘 Landeholm

23

정적 변수를 작동시키는 방법을 찾는 대신 게터 함수를 만드는 것을 선호합니다. 특정 클래스에 속하는 배열이 필요하고 구현하기가 훨씬 간단한 경우에도 유용합니다.

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로 작성되었으므로 번역이 호출 당 배열에 대한 포인터를 반환하는 함수로 해석 될 것이라고 상상할 것입니다 ... 단지 2 센트.
zeboidlund

또한 함수 호출은 PHP에서 비싸므로 필요하지 않은 경우 피하는 것이 가장 좋습니다.
Mark Rose

14
"필요하지 않을 때 피하는 것이 가장 좋습니다"-실제로는 아닙니다. 병목 현상이 발생하면 피하십시오. 그렇지 않으면 조기 최적화입니다.
psycho brm

2
@blissfreak-클래스에서 정적 속성을 생성하고 getTypeList ()가 이미 초기화되어 있는지 확인하고 반환하면 구현을 피할 수 있습니다. 아직 초기화되지 않은 경우 초기화하고 해당 값을 반환하십시오.
grantwparks 2013

12
나는 함수 호출을 피하려고 노력하지 않는다. 함수는 구조화 된 프로그래밍의 기초입니다.
grantwparks

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() ]]
    }
}

그러나 더 좋은 해결책은 정적 메소드를 없애고 싱글 톤 패턴을 사용하는 것입니다. 그런 다음 생성자에서 복잡한 초기화를 수행합니다. 또는 "서비스"로 만들고 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 2016 년

정적 변수가 필요하기 전에 생성자가 호출되었다고 가정하는 것은 좋지 않습니다. 인스턴스 만들기 전에 정적 값이 필요한 경우가 종종 있습니다.
ToolmakerSteve

4

이 코드 부분에서는 함수를 호출 할 수 없습니다. 다른 코드보다 먼저 init () 유형의 메소드를 실행하면 변수를 채울 수 있습니다.


init () 타입 메소드? 예를 들어 주시겠습니까? C #의 정적 생성자와 같은 종류입니까?
Svish

@Svish : 아니요. 클래스 정의 바로 아래에서 일반 정적 메소드로 호출해야합니다.
Vladislav Rastrusny

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
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.