Cách khởi tạo biến tĩnh


207

Tôi có mã này:

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

Điều này cho tôi lỗi sau:

Lỗi phân tích cú pháp: lỗi cú pháp, không mong muốn '(', mong đợi ')' trong /home/user/Sites/site/registration/inc/registration. Class.inc trên dòng 19

Vì vậy, tôi đoán tôi đang làm gì đó sai ... nhưng làm thế nào tôi có thể làm điều này nếu không như thế? Nếu tôi thay đổi công cụ mktime bằng các chuỗi thông thường, nó sẽ hoạt động. Vì vậy, tôi biết rằng tôi có thể làm điều đó loại như thế ..

Bất cứ ai cũng có một số gợi ý?



2
Câu trả lời đầu tiên là hơn bình chọn. Xem stackoverflow.com/a/4470002/632951
Pacerier

1
@Pacerier Tôi không nghĩ vậy. Câu trả lời số 2 có rất nhiều chi phí so với Câu trả lời số 1
Kontrollfreak

2
@Pacerier hỏi 10 người, không ai trong số họ thích điều đó.
Trâu

Câu trả lời:


345

PHP không thể phân tích các biểu thức không tầm thường trong các bộ khởi tạo.

Tôi thích giải quyết vấn đề này bằng cách thêm mã ngay sau khi định nghĩa lớp:

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

hoặc là

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

PHP 5.6 có thể xử lý một số biểu thức bây giờ.

/* 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
Tôi yêu PHP, nhưng đôi khi nó thực sự kỳ quặc.
Marco Demaio

6
Tôi biết điều này là cũ, nhưng tôi cũng sử dụng phương pháp này. Tuy nhiên, tôi thấy rằng đôi khi Foo :: init () không được gọi. Tôi không bao giờ có thể theo dõi lý do tại sao, nhưng chỉ muốn làm cho tất cả nhận thức được.
sáng suốt

1
@yheL, phương thức đầu tiên sẽ không hoạt động vì bạn không có quyền truy cập vào các biến riêng tư. Phương pháp thứ hai hoạt động nhưng nó buộc chúng ta phải initcông khai cái xấu. Giải pháp nào tốt hơn?
Pacerier

2
@Pacerier phương pháp đầu tiên sử dụng tài sản công cộng vì một lý do. AFAIK không có giải pháp nào tốt hơn trong PHP tại thời điểm này (câu trả lời của Tjeerd Visser không phải là xấu). Việc che giấu hack trong trình nạp lớp sẽ không biến mất, buộc thừa kế sai và đó là một chút thông minh có thể phá vỡ bất ngờ (ví dụ: khi tệp được yêu cầu () d rõ ràng).
Kornel

1
@yheL Mảng đơn giản hoạt động với tôi trong PHP 5.6.x, mặc dù không được đề cập trong RFC. Ví dụ:class Foo {public static $bar = array(3 * 4, "b" => 7 + 8);} var_dump(Foo::$bar);
Pang

32

Nếu bạn có quyền kiểm soát tải lớp, bạn có thể thực hiện khởi tạo tĩnh từ đó.

Thí dụ:

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

trong trình nạp lớp của bạn, hãy làm như sau:

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

Một giải pháp nặng hơn sẽ là sử dụng giao diện với ReflectionClass:

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

trong trình nạp lớp của bạn, hãy làm như sau:

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

Điều này gần giống với các nhà xây dựng tĩnh trong c #, tôi đã sử dụng một cái gì đó khá giống nhau từ lâu và nó hoạt động rất tốt.
Kris

@EmanuelLandeholm, vậy phương pháp một nhanh hơn hay phương pháp hai nhanh hơn?
Pacerier

@Kris Đó không phải là sự trùng hợp. Tôi đã được truyền cảm hứng bởi c # tại thời điểm trả lời.
Emanuel Landeholm

1
@Pacerier Tôi không có bằng chứng nhưng tôi nghi ngờ rằng ReflectionClass () có thể phải chịu thêm chi phí. OTOH, phương thức đầu tiên đưa ra giả định hơi nguy hiểm rằng bất kỳ phương thức nào được gọi là "static_init" trong bất kỳ lớp nào được tải bởi trình nạp lớp đều là trình khởi tạo tĩnh. Điều này có thể dẫn đến một số khó theo dõi lỗi, ví dụ. với các lớp bên thứ ba.
Emanuel Landeholm

23

Thay vì tìm cách để các biến tĩnh hoạt động, tôi thích chỉ đơn giản là tạo một hàm getter. Cũng hữu ích nếu bạn cần các mảng thuộc về một lớp cụ thể và đơn giản hơn rất nhiều để thực hiện.

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

Bất cứ nơi nào bạn cần danh sách, chỉ cần gọi phương thức getter. Ví dụ:

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

11
Mặc dù đây là một giải pháp tao nhã, tôi không nói rằng nó lý tưởng vì lý do hiệu suất, chủ yếu là do số lần mảng có khả năng được khởi tạo - tức là, rất nhiều phân bổ heap. Vì php được viết bằng C, tôi tưởng tượng bản dịch sẽ phân giải thành hàm trả về một con trỏ cho Mảng trên mỗi cuộc gọi ... Chỉ hai xu của tôi.
zeboidlund

Hơn nữa, các lệnh gọi hàm rất tốn kém trong PHP, vì vậy tốt nhất nên tránh chúng nếu không cần thiết.
Mark Rose

14
"tốt nhất để tránh chúng khi không cần thiết" - không thực sự. Tránh chúng nếu chúng (có thể) trở thành nút cổ chai. Nếu không, đó là tối ưu hóa sớm.
tâm lý brm

2
@blissfreak - người ta có thể tránh tái phân bổ, NẾU chúng ta tạo một thuộc tính tĩnh trong lớp và kiểm tra getTypeList () nếu nó đã được khởi tạo và trả về nó. Nếu chưa được khởi tạo, hãy khởi tạo nó và trả về giá trị đó.
Grantwparks

12
Tôi thực sự không cố gắng tránh các cuộc gọi chức năng. Các chức năng là cơ sở của lập trình có cấu trúc.
Grantwparks

11

Tôi sử dụng kết hợp câu trả lời của Tjeerd Visser's và 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() ]]
    }
}

Nhưng một giải pháp thậm chí tốt hơn là loại bỏ các phương thức tĩnh và sử dụng mẫu Singleton. Sau đó, bạn chỉ cần thực hiện khởi tạo phức tạp trong hàm tạo. Hoặc biến nó thành một "dịch vụ" và sử dụng DI để đưa nó vào bất kỳ lớp nào cần nó.


10

Điều đó quá phức tạp để định nghĩa. Bạn có thể đặt định nghĩa thành null mặc dù, sau đó trong hàm tạo, kiểm tra nó và nếu nó không bị thay đổi - hãy đặt nó:

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

10
nhưng hàm tạo sẽ có ích gì trong một lớp trừu tượng không bao giờ được khởi tạo không?
Svish

Một lớp trừu tượng không thể được sử dụng một cách hữu ích trừ khi nó được hoàn thành và khởi tạo. Việc thiết lập ở trên không phải được thực hiện cụ thể trong một hàm tạo, miễn là nó được gọi ở đâu đó trước khi biến sẽ được sử dụng.
Alister Bulman

Nó không phải là một ý tưởng tuyệt vời để giả định rằng hàm tạo được gọi trước khi biến tĩnh là cần thiết. Thường thì người ta cần một giá trị tĩnh trước khi tạo một thể hiện.
ToolmakerSteve

4

Bạn không thể thực hiện các cuộc gọi hàm trong phần này của mã. Nếu bạn thực hiện một phương thức kiểu init () được thực thi trước bất kỳ mã nào khác thì bạn sẽ có thể nhập biến đó.


phương thức kiểu init ()? bạn có thể cho một ví dụ? đó có giống như một hàm tạo tĩnh trong C # không?
Svish

@Svish: Không. Nó nên được gọi ngay bên dưới định nghĩa lớp như một phương thức tĩnh thông thường.
Vladislav Rastrusny

4

Trong PHP 7.0.1, tôi đã có thể định nghĩa điều này:

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

Và sau đó sử dụng nó như thế này:

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

FWIW: Những gì bạn thể hiện không yêu cầu PHP 7; nó hoạt động tốt tại thời điểm câu hỏi được hỏi: "Nếu tôi thay đổi công cụ mktime bằng chuỗi thông thường, nó sẽ hoạt động." Những gì chủ đề này đang tìm kiếm, là các kỹ thuật để khởi tạo một tĩnh, khi khởi tạo cần gọi một hoặc nhiều hàm .
ToolmakerSteve

3

cách tốt nhất là tạo một trình truy cập như thế này:

/**
* @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;
}

sau đó bạn có thể làm static :: db (); hoặc tự :: db (); từ bất cứ đâu.


-1

Đây là một con trỏ hy vọng hữu ích, trong một ví dụ mã. Lưu ý cách hàm khởi tạo chỉ được gọi một lần.

Ngoài ra, nếu bạn đảo ngược các cuộc gọi đến StaticClass::initializeStStateArr()$st = new StaticClass()bạn sẽ nhận được kết quả tương tự.

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

Sản lượng nào:

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

2
Nhưng xin lưu ý rằng bạn đã tạo một thể hiện của lớp (đối tượng), bởi vì hàm tạo là một hàm NONSTATIC công khai. Câu hỏi là: PHP chỉ hỗ trợ các hàm tạo tĩnh (không tạo cá thể. Ví dụ như trong Javastatic { /* some code accessing static members*/ }
Mitja Gustin
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.