Come inizializzare le variabili statiche


207

Ho questo codice:

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

Il che mi dà il seguente errore:

Errore di analisi: errore di sintassi, inatteso "(", in attesa ")" in /home/user/Sites/site/registration/inc/registration.class.inc alla riga 19

Quindi, immagino di fare qualcosa di sbagliato ... ma come posso farlo se non così? Se cambio le cose di mktime con stringhe regolari, funziona. Quindi so che posso farlo sorta di genere ..

Qualcuno ha dei suggerimenti?



2
La prima risposta è stata superata. Vedere stackoverflow.com/a/4470002/632951
Pacerier

1
@Pacerier Non la penso così. La risposta n. 2 ha molte spese generali rispetto alla risposta n. 1
Kontrollfreak

2
@Pacerier chiede a 10 persone, nessuna di queste preferirebbe.
Buffalo,

Risposte:


345

PHP non può analizzare espressioni non banali negli inizializzatori.

Preferisco aggirare questo aggiungendo il codice subito dopo la definizione della classe:

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

o

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

PHP 5.6 ora può gestire alcune espressioni.

/* 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
Adoro PHP, ma a volte è davvero strano.
Marco Demaio,

6
So che questo è vecchio, ma anch'io uso questo metodo. Tuttavia, ho scoperto che a volte Foo :: init () non viene chiamato. Non sono mai stato in grado di rintracciare il perché, ma volevo solo renderlo consapevole.
lucente

1
@porneL, il primo metodo non funzionerebbe perché non si ha accesso alle variabili private. Il secondo metodo funziona ma ci costringe a rendere initpubblico il che è brutto. Qual è una soluzione migliore?
Pacerier,

2
@Pacerier il primo metodo utilizza la proprietà pubblica per un motivo. AFAIK al momento non esiste una soluzione migliore in PHP (la risposta di Tjeerd Visser non è male però). Nascondere l'hack nel caricatore di classi non lo fa sparire, impone una falsa eredità ed è un po 'di intelligenza che potrebbe interrompersi inaspettatamente (ad esempio quando il file è richiesto () d esplicitamente).
Kornel,

1
@porneL Simple array funziona per me in PHP 5.6.x, sebbene non menzionato in RFC. Esempio:class Foo {public static $bar = array(3 * 4, "b" => 7 + 8);} var_dump(Foo::$bar);
Pang

32

Se hai il controllo sul caricamento della classe, puoi eseguire l'inizializzazione statica da lì.

Esempio:

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

nel caricatore di classi, procedi come segue:

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

Una soluzione più pesante sarebbe quella di utilizzare un'interfaccia con ReflectionClass:

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

nel caricatore di classi, procedi come segue:

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

Questo è più che in qualche modo simile ai costruttori statici in c #, sto usando qualcosa di abbastanza simile da secoli e funziona benissimo.
Kris,

@EmanuelLandeholm, quindi il metodo uno è più veloce o il metodo due più veloce?
Pacerier

@Kris Non è una coincidenza. Sono stato ispirato da c # al momento della risposta.
Emanuel Landeholm,

1
@Pacerier Non ho prove, ma ho il sospetto che ReflectionClass () possa sostenere un sovraccarico. OTOH, il primo metodo fa supporre un po 'pericoloso che qualsiasi metodo chiamato "static_init" in qualsiasi classe caricata dal caricatore di classi sia un inizializzatore statico. Ciò potrebbe comportare alcuni bug difficili da rintracciare, ad es. con classi di terze parti.
Emanuel Landeholm,

23

Invece di trovare un modo per far funzionare le variabili statiche, preferisco semplicemente creare una funzione getter. Utile anche se hai bisogno di array appartenenti a una classe specifica e molto più semplice da implementare.

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

Ovunque sia necessario l'elenco, è sufficiente chiamare il metodo getter. Per esempio:

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

11
Sebbene si tratti di una soluzione elegante, non direi che è l'ideale per motivi di prestazioni, principalmente a causa della quantità di volte in cui l'array potrebbe essere inizializzato, vale a dire un sacco di allocazione di heap. Dato che php è scritto in C, immagino che la traduzione si risolva in una funzione che restituisce un puntatore a un array per chiamata ... Solo i miei due centesimi.
zeboidlund,

Inoltre, le chiamate di funzione sono costose in PHP, quindi è meglio evitarle se non sono necessarie.
Mark Rose,

14
"meglio evitarli quando non è necessario" - non proprio. Evitali se (potrebbero) diventare colli di bottiglia. Altrimenti è ottimizzazione prematura.
psycho brm,

2
@blissfreak - si può evitare la riallineamento, SE creiamo una proprietà statica nella classe e controlliamo getTypeList () se è già stato inizializzato e lo restituisce. Se non ancora inizializzato, inizializzalo e restituisci quel valore.
grantwparks,

12
Sinceramente non cerco di evitare le chiamate di funzione. Le funzioni sono la base della programmazione strutturata.
grantwparks,

11

Uso una combinazione della risposta di Tjeerd Visser e 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() ]]
    }
}

Ma una soluzione ancora migliore è eliminare i metodi statici e utilizzare il modello Singleton. Quindi esegui la complicata inizializzazione nel costruttore. Oppure rendilo un "servizio" e usa DI per iniettarlo in qualsiasi classe che ne abbia bisogno.


10

È troppo complesso per essere impostato nella definizione. È possibile impostare la definizione su null, quindi nel costruttore, controllarla e, se non è stata modificata, impostarla:

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

10
ma il costruttore sarà di qualche aiuto in una classe astratta che non viene mai istanziata?
Svish,

Una classe astratta non può essere utilmente utilizzata a meno che non sia stata completata e istanziata. L'impostazione sopra non deve essere eseguita specificamente in un costruttore, purché venga chiamata da qualche parte prima che la variabile venga utilizzata.
Alister Bulman,

Non è una buona idea presumere che il costruttore sia chiamato prima che sia necessaria la variabile statica. Spesso è necessario un valore statico prima di creare un'istanza.
ToolmakerSteve

4

Non è possibile effettuare chiamate di funzione in questa parte del codice. Se crei un metodo di tipo init () che viene eseguito prima di qualsiasi altro codice, allora sarai in grado di popolare la variabile.


metodo di tipo init ()? potresti fare un esempio? è un po 'come un costruttore statico in C #?
Svish,

@Svish: No. Dovrebbe essere chiamato appena sotto la definizione di classe come un normale metodo statico.
Vladislav Rastrusny,

4

In PHP 7.0.1, sono stato in grado di definire questo:

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

E poi usalo in questo modo:

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

FWIW: Quello che mostri non richiede PHP 7; ha funzionato bene al momento in cui è stata posta la domanda: "Se cambio le cose di mktime con stringhe regolari, funziona". Ciò che questo thread sta cercando, sono le tecniche per inizializzare uno statico, quando l'inizializzazione deve chiamare una o più funzioni .
ToolmakerSteve

3

il modo migliore è creare un accessor come questo

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

allora puoi fare static :: db (); o self :: db (); da qualsiasi luogo.


-1

Ecco un puntatore si spera utile, in un esempio di codice. Notare come la funzione di inizializzazione viene chiamata una sola volta.

Inoltre, se inverti le chiamate StaticClass::initializeStStateArr()e $st = new StaticClass()otterrai lo stesso risultato.

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

Che produce:

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

2
Ma tieni presente che hai creato un'istanza di classe (oggetto), poiché il costruttore è una funzione pubblica NONSTATIC. La domanda è: PHP supporta solo costruttori statici (nessuna creazione di istanze. Ad esempio come in Javastatic { /* some code accessing static members*/ }
Mitja Gustin
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.