Practical example

Late static binding is often used when dealing with data models. Say you have a base Model class with methods to set/get a 'table' static attribute that will define every sub model's table name. To access your future sub model's $table attribute, you decide to use late static binding and thus, access the variable using the static:: accessor.

class Model {
	protected static $table;
	
	public static function getTable() {
		return static::$table;
	}
	protected static function setTable($t) {
		static::$table = $t;
	}
}

Nothing out of the ordinary here. But now, let's write some sub models and see what happen when we use the getters/setters.

class Post extends Model {

}

class User extends Model {

}

You have just declared two child classes extending the base Model class. These two sub models will inherit attributes and methods from its parent class. That way, you can use the getTable and setTable methods to assign values to their $table attribute.

Post::setTable('post_table');
var_dump(Post::getTable()); // Outputs post_table (OK)
User::setTable('user_table');
var_dump(User::getTable()); // Outputs user_table (OK)

Everything is working like a charm, except for one thing. Add and try out the following line of code after the previous dumps :

var_dump(Post::getTable()); // Outputs user_table (Instead of post_table)

Oops ! As you can see, when you dump the output of Post::getTable(), it actually outputs the table of the User class.

Explanation

Let's take a look back at the sub models definitions :

class Post extends Model {

}

class User extends Model {

}

Because they both extend the Model class, they inherit of the two setTable and getTable static methods and the $table static attribute.

Now what you have to know is that while late static binding using the static:: keyword gives access to child attributes, it does not actually initialize them. So calling static::$table from Post::setTable() will try to access the $table attribute of the Post static class. But since the attribute has not been manually initialized in the class declaration, it will resolve as Model::$table.

So when you called the setTable() and getTable() of the Post and User sub models, it in fact accessed and modified the Model::$table attribute. This is why the last dump of the Post::getTable() method outputs the table set by the User::setTable() method. It was using the Model::$table attribute all the time.

Correction

class Model {
	protected static $table;
	
	public static function getTable() {
		return static::$table;
	}
	protected static function setTable($t) {
		static::$table = $t;
	}
}
class Post extends Model {
	protected static $table;
}

class User extends Model {
	protected static $table;
}

Post::setTable('post_table');
var_dump(Post::getTable()); // Outputs post_table (OK)
User::setTable('user_table');
var_dump(User::getTable()); // Outputs user_table (OK)
var_dump(Post::getTable()); // Outputs post_table (OK)

Conclusion

So beware when using late static binding to access static child attributes. You have to declare the attributes in the child classes if you want to use them or else it will simply fallback to the parent class attributes.

This can be quite painful to debug since the resolving of the attribute access is transparent, so be sure to declare your child attributes when working with late static binding !

More ?