Cara menginisialisasi variabel statis

207

Saya punya kode ini:

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

Yang memberi saya kesalahan berikut:

Kesalahan Parse: kesalahan sintaksis, '(', mengharapkan ')' tak terduga 'di /home/user/Sites/site/registration/inc/registration.class.inc on line 19

Jadi, saya kira saya melakukan sesuatu yang salah ... tapi bagaimana saya bisa melakukan ini jika tidak seperti itu? Jika saya mengubah hal-hal mktime dengan string biasa, itu berfungsi. Jadi saya tahu bahwa saya bisa melakukannya semacam seperti itu ..

Adakah yang punya beberapa petunjuk?

Svish
sumber
2
Jawaban pertama lebih dipilih. Lihat stackoverflow.com/a/4470002/632951
Pacerier
1
@Pacerier Saya rasa tidak. Jawaban # 2 memiliki banyak overhead dibandingkan dengan Jawaban # 1
Kontrollfreak
2
@Pacerier bertanya pada 10 orang, tidak ada yang lebih suka itu.
Buffalo

Jawaban:

345

PHP tidak dapat mem-parsing ekspresi non-sepele dalam inisialisasi.

Saya lebih suka mengatasi ini dengan menambahkan kode tepat setelah definisi kelas:

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

atau

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

PHP 5.6 dapat menangani beberapa ekspresi sekarang.

/* 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();
}
Kornel
sumber
135
Saya suka PHP, tapi terkadang aneh.
Marco Demaio
6
Saya tahu ini sudah tua, tetapi saya juga menggunakan metode ini. Namun, saya menemukan bahwa kadang-kadang Foo :: init () tidak dipanggil. Saya tidak pernah bisa melacak mengapa, tetapi hanya ingin membuat semua sadar.
Lucifurious
1
@porneL, metode pertama tidak akan berfungsi karena Anda tidak memiliki akses ke variabel pribadi. Metode kedua berhasil tetapi memaksa kita untuk initmempublikasikan yang jelek. Apa solusi yang lebih baik?
Pacerier
2
@Pacerier metode pertama menggunakan properti publik karena suatu alasan. AFAIK tidak ada solusi yang lebih baik dalam PHP saat ini (jawaban Tjeerd Visser tidak buruk). Menyembunyikan retasan di loader kelas tidak membuatnya hilang, memaksa warisan palsu, dan sedikit kepintaran yang secara tak terduga dapat pecah (misalnya saat file diperlukan () d secara eksplisit).
Kornel
1
@porneL Simple array berfungsi untuk saya di PHP 5.6.x, meskipun tidak disebutkan dalam RFC. Contoh:class Foo {public static $bar = array(3 * 4, "b" => 7 + 8);} var_dump(Foo::$bar);
Pang
32

Jika Anda memiliki kontrol atas pemuatan kelas, Anda dapat melakukan inisialisasi statis dari sana.

Contoh:

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

di pemuat kelas Anda, lakukan hal berikut:

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

Solusi yang lebih berat adalah menggunakan antarmuka dengan ReflectionClass:

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

di pemuat kelas Anda, lakukan hal berikut:

$rc = new ReflectionClass($klass);
if(in_array('StaticInit', $rc->getInterfaceNames())) { $klass::staticInit() }
Emanuel Landeholm
sumber
Ini lebih dari agak mirip dengan konstruktor statis di c #, saya telah menggunakan sesuatu yang sangat mirip untuk waktu yang lama dan itu berfungsi dengan baik.
Kris
@ EmanuelLandeholm, jadi apakah metode satu lebih cepat atau metode dua lebih cepat?
Pacerier
@ Kris Itu bukan kebetulan. Saya terinspirasi oleh c # pada saat menjawab.
Emanuel Landeholm
1
@Pacerier Saya tidak punya bukti tetapi saya curiga bahwa ReflectionClass () dapat menyebabkan lebih banyak overhead. OTOH, metode pertama membuat asumsi yang agak berbahaya bahwa metode apa pun yang disebut "static_init" di kelas apa pun yang dimuat oleh pemuat kelas adalah penginisialisasi statis. Ini dapat menyebabkan beberapa bug sulit dilacak, misalnya. dengan kelas pihak ketiga.
Emanuel Landeholm
23

Daripada menemukan cara untuk membuat variabel statis berfungsi, saya lebih suka hanya membuat fungsi pengambil. Juga membantu jika Anda memerlukan array milik kelas tertentu, dan jauh lebih mudah untuk diterapkan.

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

Di mana pun Anda membutuhkan daftar, cukup panggil metode pengambil. Sebagai contoh:

if (array_key_exists($type, MyClass::getTypeList()) {
     // do something important...
}
diggie
sumber
11
Walaupun ini adalah solusi yang elegan, saya tidak akan mengatakan itu ideal untuk alasan kinerja, terutama karena jumlah kali array berpotensi diinisialisasi - yaitu, banyak alokasi tumpukan. Karena php ditulis dalam C, saya membayangkan terjemahan akan menyelesaikan ke fungsi mengembalikan pointer ke Array per panggilan ... Hanya dua sen saya.
zeboidlund
Selain itu, panggilan fungsi mahal di PHP, jadi sebaiknya hindari jika tidak diperlukan.
Mark Rose
14
"terbaik untuk menghindari mereka ketika tidak perlu" - tidak juga. Hindari mereka jika mereka (mungkin) menjadi hambatan. Kalau tidak, itu optimasi prematur.
psiko brm
2
@blissfreak - kita dapat menghindari realokasi, JIKA kita membuat properti statis di kelas, dan periksa di getTypeList () jika sudah diinisialisasi dan kembalikan itu. Jika belum diinisialisasi, inisialisasi dan kembalikan nilai itu.
Grantwparks
12
Saya benar-benar tidak berusaha menghindari panggilan fungsi. Fungsi adalah dasar dari pemrograman terstruktur.
Grantwparks
11

Saya menggunakan kombinasi jawaban Tjeerd Visser dan 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() ]]
    }
}

Tetapi solusi yang lebih baik adalah menghilangkan metode statis dan menggunakan pola Singleton. Maka Anda cukup melakukan inisialisasi yang rumit di konstruktor. Atau jadikan "layanan" dan gunakan DI untuk menyuntikkannya ke kelas yang membutuhkannya.

Mambazo
sumber
10

Itu terlalu rumit untuk ditetapkan dalam definisi. Anda dapat mengatur definisi ke nol, dan kemudian di konstruktor, periksa, dan jika belum diubah - setel:

private static $dates = null;
public function __construct()
{
    if (is_null(self::$dates)) {  // OR if (!is_array(self::$date))
         self::$dates = array( /* .... */);
    }
}
Alister Bulman
sumber
10
tetapi apakah konstruktor akan membantu dalam kelas abstrak yang tidak pernah dipakai?
Svish
Kelas abstrak tidak dapat digunakan dengan bermanfaat kecuali jika sudah selesai dan instantiated. Penyiapan di atas tidak harus dilakukan secara khusus di konstruktor, selama itu dipanggil di suatu tempat sebelum variabel akan digunakan.
Alister Bulman
Bukan ide bagus untuk mengasumsikan konstruktor dipanggil sebelum variabel statis diperlukan. Seringkali seseorang membutuhkan nilai statis sebelum membuat sebuah instance.
ToolmakerSteve
4

Anda tidak dapat melakukan panggilan fungsi di bagian kode ini. Jika Anda membuat metode tipe init () yang dijalankan sebelum kode lain melakukannya, Anda akan dapat mengisi variabel itu.

alxp
sumber
metode tipe init ()? bisakah kamu memberikan contoh? adalah semacam konstruktor statis di C #?
Svish
@Vish: Tidak. Ini harus disebut tepat di bawah definisi kelas sebagai metode statis biasa.
Vladislav Rastrusny
4

Di PHP 7.0.1, saya dapat mendefinisikan ini:

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

Dan kemudian gunakan seperti ini:

MyClass::$kIdsByActions[$this->mAction];
Kerbau
sumber
FWIW: Apa yang Anda tampilkan tidak memerlukan PHP 7; itu berfungsi dengan baik pada saat pertanyaan diajukan: "Jika saya mengubah hal-hal mktime dengan string biasa, itu berfungsi." Apa yang dicari thread ini, adalah teknik untuk menginisialisasi statis, ketika inisialisasi perlu memanggil satu atau lebih fungsi .
ToolmakerSteve
3

Cara terbaik adalah membuat accessor seperti ini:

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

maka Anda dapat melakukan static :: db (); atau self :: db (); dari mana-mana.

espaciomore
sumber
-1

Berikut ini adalah penunjuk yang mudah-mudahan bermanfaat, dalam contoh kode. Perhatikan bagaimana fungsi penginisialisasi hanya dipanggil sekali.

Juga, jika Anda membalikkan panggilan ke StaticClass::initializeStStateArr()dan $st = new StaticClass()Anda akan mendapatkan hasil yang sama.

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

Yang menghasilkan:

$ php static.php
Starting...
In initializeStStateArr
Array
(
    [CA] => California
    [CO] => Colorado
)
David Luhman
sumber
2
Tetapi harap dicatat, bahwa Anda membuat turunan kelas (objek), karena konstruktor adalah fungsi NONSTATIK publik. Pertanyaannya adalah: apakah PHP mendukung konstruktor statis saja (tidak ada pembuatan contoh. Misalnya seperti di Jawastatic { /* some code accessing static members*/ }
Mitja Gustin