How to initialize static variables
Asked Answered
S

10

220

I have this code:

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

Which gives me the following error:

Parse error: syntax error, unexpected '(', expecting ')' in /home/user/Sites/site/registration/inc/registration.class.inc on line 19

So, I guess I am doing something wrong... but how can I do this if not like that? If I change the mktime stuff with regular strings, it works. So I know that I can do it sort of like that..

Anyone have some pointers?

Spoliation answered 28/3, 2009 at 22:50 Comment(4)
php.benscom.com/manual/en/language.oop5.static.php#51627Peisch
The first answer is over voted. See https://mcmap.net/q/118497/-how-to-initialize-static-variablesAllseed
@Allseed I don't think so. Answer #2 has a lot of overhead compared to Answer #1Huberthuberto
@Allseed ask 10 people, none of them would prefer that.Heiser
P
358

PHP can't parse non-trivial expressions in initializers.

I prefer to work around this by adding code right after definition of the class:

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

or

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

PHP 5.6 can handle some expressions now.

/* 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();
}
Pogonia answered 28/3, 2009 at 23:50 Comment(8)
I love PHP, but it's really odd sometimes.Nicky
I know this is old, but I too use this method. However, I found that sometimes the Foo::init() is not called. I was never able to track down why, but just wanted to make all aware.Mimimimic
@porneL, the first method wouldn't work because you have no access to private variables. The second method works but it forces us to make init public which is ugly. What's a better solution?Allseed
@Allseed the first method uses public property for a reason. AFAIK there is no better solution in PHP at this time (Tjeerd Visser's answer is not bad though). Hiding the hack in class loader doesn't make it go away, forces false inheritance, and it's a bit of cleverness that could unexpectedly break (e.g. when file is require()d explicitly).Pogonia
But let's say I have class with multiple static methods. I call them manytimes, they call each other. How can i have a varible on class (not instance) level, so that variable will be executed only once per request lifecyle. Let's say i want to use like this: static myOneTimeInitVar = Singleton::Instance::getSomeShit().Zinazinah
@porneL Simple array works for me in PHP 5.6.x, although not mentioned in RFC. Example: class Foo {public static $bar = array(3 * 4, "b" => 7 + 8);} var_dump(Foo::$bar);Anetteaneurin
I sometimes also use this method. It can mess with unit tests, though, if anything non-trivial is added to the init method.Wakerobin
Man, you'd think this is something that would be cooked into the language by default. Annoying to have to do thisApiarist
S
34

If you have control over class loading, you can do static initializing from there.

Example:

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

in your class loader, do the following:

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

A more heavy weight solution would be to use an interface with ReflectionClass:

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

in your class loader, do the following:

$rc = new ReflectionClass($klass);
if(in_array('StaticInit', $rc->getInterfaceNames())) { $klass::staticInit() }
Schwinn answered 17/12, 2010 at 11:24 Comment(4)
This is more than somewhat similar to static constructors in c#, I've been using something quite similar for ages and it works great.Volkslied
@EmanuelLandeholm, so is method one faster or method two faster?Allseed
@Volkslied That is no coincidence. I was inspired by c# at the time of answering.Schwinn
@Allseed I have no proof but I suspect that ReflectionClass() may incur more overhead. OTOH, the first method makes the somewhat dangerous assumption that any method called "static_init" in any class loaded by the class loader is a static initializer. This could lead to some hard to track down bugs, eg. with third party classes.Schwinn
L
23

Instead of finding a way to get static variables working, I prefer to simply create a getter function. Also helpful if you need arrays belonging to a specific class, and a lot simpler to implement.

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

Wherever you need the list, simply call the getter method. For example:

if (array_key_exists($type, MyClass::getTypeList()) {
     // do something important...
}
Latoria answered 16/10, 2011 at 15:20 Comment(5)
While this is an elegant solution, I wouldn't say it's ideal for performance reasons, primarily because of the amount of times the array could potentially be initialized - i.e., lots of heap allocation. Since php is written in C, I'd imagine the translation would resolve to a function returning a pointer to an Array per call... Just my two cents.Mechanical
Furthermore function calls are expensive in PHP, so it's best to avoid them if they're not necessary.Yakka
"best to avoid them when not necessary" - not really. Avoid them if they (might) become bottlenecks. Otherwise it's premature optimization.Consternation
@blissfreak - one can avoid reallcating, IF we create a static property in the class, and check in getTypeList() if it's been initialized already and return that. If not initialized yet, initialize it and return that value.Aara
I seriously don't get trying to avoid function calls. Functions are the basis of structured programming.Aara
A
12

I use a combination of Tjeerd Visser's and porneL's answer.

class Something
{
    private static $foo;

    private static getFoo()
    {
        if ($foo === null)
            $foo = [[ complicated initializer ]]
        return $foo;
    }

    public static bar()
    {
        [[ do something with self::getFoo() ]]
    }
}

But an even better solution is to do away with the static methods and use the Singleton pattern. Then you just do the complicated initialization in the constructor. Or make it a "service" and use DI to inject it into any class that needs it.

Antechamber answered 21/10, 2013 at 18:41 Comment(1)
You can't use a singleton if your class uses both static and nonstatic properties.Jobber
G
10

That's too complex to set in the definition. You can set the definition to null though, and then in the constructor, check it, and if it has not been changed - set it:

private static $dates = null;
public function __construct()
{
    if (is_null(self::$dates)) {  // OR if (!is_array(self::$date))
         self::$dates = array( /* .... */);
    }
}
Gharry answered 28/3, 2009 at 23:13 Comment(3)
but will the constructor be of any help in an abstract class that never is instantiated?Spoliation
An abstract class can't be usefully used unless it's been completed and instantiated. The setup above doesn't have to be done specifically in a constructor, as long as it's called somewhere before the variable is going to be used.Gharry
Its not a great idea to assume that constructor is called before static variable is needed. Often one needs a static value before creating an instance.Inexcusable
B
4

best way is to create an accessor like this:

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

then you can do static::db(); or self::db(); from anywhere.

Basin answered 3/9, 2013 at 14:52 Comment(0)
H
4

In PHP 7.0.1, I was able to define this:

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

And then use it like this:

MyClass::$kIdsByActions[$this->mAction];
Heiser answered 26/1, 2017 at 10:18 Comment(1)
FWIW: What you show doesn't require PHP 7; it worked fine at the time the question was asked: "If I change the mktime stuff with regular strings, it works." What this thread is looking for, is techniques for initializing a static, when the initialization needs to call one or more functions.Inexcusable
V
3

You can't make function calls in this part of the code. If you make an init() type method that gets executed before any other code does then you will be able to populate the variable then.

Vivyan answered 28/3, 2009 at 22:53 Comment(2)
init() type method? could you give an example? is that kind of like a static constructor in C#?Spoliation
@Svish: No. It should be called just below class definition as a regular static method.Horton
J
0

In my case, I'm using both static and nonstatic class properties, and I might even have main program code referencing the static part of the class before defining the class. Since static portions of classes don't have constructors, just add a manual constructor to initialize any variables requiring nontrivial calculation:

class A
   {
   static $a; // Initialized by Init()
   static function Init()
      {
      A::$a=nontrivial();
      {
   }
...
A::Init();    // Initialize static part of class
...
$obj=new A(); // Using initialized class as an object
Jobber answered 18/1, 2023 at 15:55 Comment(0)
D
-1

Here is a hopefully helpful pointer, in a code example. Note how the initializer function is only called once.

Also, if you invert the calls to StaticClass::initializeStStateArr() and $st = new StaticClass() you'll get the same result.

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

Which yields :

$ php static.php
Starting...
In initializeStStateArr
Array
(
    [CA] => California
    [CO] => Colorado
)
Delmadelmar answered 11/7, 2013 at 18:23 Comment(1)
But please note, that you created a instance of class (object), because constructor is a public NONSTATIC function. Question is: does PHP support static constructors only (no instance creation. For example like in Java static { /* some code accessing static members*/ }Zinazinah

© 2022 - 2025 — McMap. All rights reserved.