221

I have this code:

private static $dates = [
  'start' => mktime( 0,  0,  0,  7, 30, 2009),  // Start date
  'end'   => mktime( 0,  0,  0,  8,  2, 2009),  // End date
];

I get the following error:

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

If I change mktime to a string, it works.

Anyone have any pointers?

4

11 Answers 11

360

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();
}
Sign up to request clarification or add additional context in comments.

8 Comments

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.
@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?
@Pacerier 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).
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().
@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);
|
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() }

4 Comments

This is more than somewhat similar to static constructors in c#, I've been using something quite similar for ages and it works great.
@EmanuelLandeholm, so is method one faster or method two faster?
@Kris That is no coincidence. I was inspired by c# at the time of answering.
@Pacerier 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.
24

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...
}

5 Comments

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.
Furthermore function calls are expensive in PHP, so it's best to avoid them if they're not necessary.
"best to avoid them when not necessary" - not really. Avoid them if they (might) become bottlenecks. Otherwise it's premature optimization.
@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.
I seriously don't get trying to avoid function calls. Functions are the basis of structured programming.
13

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 dependency injection to inject it into any class that needs it.

1 Comment

You can't use a singleton if your class uses both static and nonstatic properties.
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( /* .... */);
    }
}

3 Comments

but will the constructor be of any help in an abstract class that never is instantiated?
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.
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.
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.

Comments

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

1 Comment

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.
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.

2 Comments

init() type method? could you give an example? is that kind of like a static constructor in C#?
@Svish: No. It should be called just below class definition as a regular static method.
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

Comments

0

If the static property is only used after an instance of the class has been constructed, it can be done in the constructor. For example

class Test
{
    public static \WeakMap $list ;
    function __construct() {
        self::$list ??= new \WeakMap ; // only done once
        self::$list[ $this ] = 0 ;
    }
}

$t = new Test ;
var_dump( Test::$list ) ;

Comments

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

1 Comment

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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.