Simple basic class which uses to get_called_class() to create singleton instances. A previous post by php at mikebird dot co dot uk explain how to do this, but the extended static variables require you to define them in child classes before they work.
<?php
abstract class Singleton {
private static $instances = array();
public function __construct() {
$class = get_called_class();
if (array_key_exists($class, self::$instances))
trigger_error("Tried to construct a second instance of class \"$class\"", E_USER_WARNING);
}
public static function getInstance() {
$class = get_called_class();
if (array_key_exists($class, self::$instances) === false)
self::$instances[$class] = new $class();
return self::$instances[$class];
}
}
class A extends Singleton {
}
class B extends Singleton {
}
$a1 = A::getInstance();
$a2 = A::getInstance();
$b1 = B::getInstance();
$b2 = B::getInstance();
if (get_class($a1) == "A" &&
get_class($a2) == "A" &&
get_class($b1) == "B" &&
get_class($b2) == "B" &&
$a1 === $a2 &&
$b1 === $b2)
echo "All good\n";
else
echo "FAIL!\n";
?>
You probably noticed the use of self:: rather than static::, this is because we want the static variable to be private, and using static:: will not allow us to do that.
Late Static Bindings
No PHP 5.3.0, o PHP implementa um recurso chamado late static bindings que pode ser usado para referenciar a classe chamada no contexto de herança estática.
Este recurso foi nomeado "late static bindings" com um pespectiva interna em mente. "Late binding" vem do fato que static:: não ser mais resolvido usando a classe onde o mesmo é definido mas ele será avaliado usando informação em tempo de execução. Ele foi também chamado "static binding" como pode ser usado para (mas não é limitado para) chamada de métodos estáticos.
Limitações do self::
Referências estáticas para a atual classe como self:: ou __CLASS__ são resolvidas usando a classe na qual a função pertence, como onde ele foi definido:
Exemplo #1 Uso do self::
<?php
class A {
public static function who() {
echo __CLASS__;
}
public static function test() {
self::who();
}
}
class B extends A {
public static function who() {
echo __CLASS__;
}
}
B::test();
?>
O exemplo acima irá imprimir:
A
Uso de Late Static Bindings
Late static bindings tenta resolver a limitação introduzindo uma palavra-chave que referencia a classe que foi inicialiamente chamada em runtime. Basicamente, uma palavra-chave que permite você referenciar B em test() no exemplo anterior. Foi decidido não introduzir uma nova palavra-chave, mas usar static que já foi reservado.
Exemplo #2 Simples uso do static::
<?php
class A {
public static function who() {
echo __CLASS__;
}
public static function test() {
static::who(); // Here comes Late Static Bindings
}
}
class B extends A {
public static function who() {
echo __CLASS__;
}
}
B::test();
?>
O exemplo acima irá imprimir:
B
Nota:
static:: não funciona como $this para métodos estáticos! $this-> segue a regra de herança quando static:: não o faz. Esta diferença é detalhada depois nesta página.
Exemplo #3 Uso do static:: em um contexto não-estático
<?php
class TestChild extends TestParent {
public function __construct() {
static::who();
}
public function test() {
$o = new TestParent();
}
public static function who() {
echo __CLASS__."\n";
}
}
class TestParent {
public function __construct() {
static::who();
}
public static function who() {
echo __CLASS__."\n";
}
}
$o = new TestChild;
$o->test();
?>
O exemplo acima irá imprimir:
TestChild TestParent
Nota:
A resolução do Late static bindings termina em uma chamada estática inteiramente resolvida sem volta. Por outro lado, chamadas estáticas usando palavras-chave como parent:: ou self:: encaminharão a informação.
Exemplo #4 Chamadas encaminhadas e não-encaminhadas
<?php
class A {
public static function foo() {
static::who();
}
public static function who() {
echo __CLASS__."\n";
}
}
class B extends A {
public static function test() {
A::foo();
parent::foo();
self::foo();
}
public static function who() {
echo __CLASS__."\n";
}
}
class C extends B {
public static function who() {
echo __CLASS__."\n";
}
}
C::test();
?>O exemplo acima irá imprimir:
A C C
Edge cases
Há várias diferentes formas de ser chamado um método no PHP, como callbacks ou métodos mágicos. Como late static bindings baseia sua resolução em informação em runtine, ele pode dar resultados inesperados em então chamados edge cases.
Exemplo #5 Late static bindings em métodos mágicos
<?php
class A {
protected static function who() {
echo __CLASS__."\n";
}
public function __get($var) {
return static::who();
}
}
class B extends A {
protected static function who() {
echo __CLASS__."\n";
}
}
$b = new B;
$b->foo;
?>
O exemplo acima irá imprimir:
B
This function can be used as a workaround for late static binding in PHP >= 5.1.0. There was another similar version of this function elsewhere, but used eval.
<?php
function & static_var($class, $name)
{
if (is_object($class))
{
$class = get_class($class);
}
elseif ( ! is_string($class))
{
throw new Exception('Must be given an object or a class name', NULL);
}
$class = new ReflectionClass($class);
return $class->getStaticPropertyValue($name);
}
?>
WHOA... KEEP IT SIMPLE!
Remember, when you write a class definition, you are creating a new "type" of object. And when you extend those classes, you are creating a heirarchy. To get to the point... all the class defs below work together to provide a solid organization of data.
<?php
abstract class Animal {
protected $type, $name;
public function __construct($aType, $aName) {
$this->type = $aType;
$this->name = $aName;
}
public function __toString() {
return "Animal [type=$this->type, name=$this->name]";
}
}
class Dog extends Animal {
public function __construct($aName) {
parent::__construct("Dog", $aName);
}
}
class Cat extends Animal {
public function __construct($aName) {
parent::__construct("Cat", $aName);
}
}
echo 'My dog: ' . (new Dog('Sam')) . '<br>';
echo 'My cat: ' . (new Cat('Fluffy')) . '<br>';
echo 'Your dog: ' . (new Dog('Walter')) . '<br>';
echo 'Yout cat: ' . (new Cat('Ginger'));
?>
My dog: Animal[type=Dog, name=Sam]
My cat: Animal[type=Cat, name=Fluffy]
Your dog: Animal[type=Dog, name=Walter]
Yout cat: Animal[type=Cat, name=Ginger]
... and notice the property called $type, is the same as using __CLASS__ in most of the previous posts, but without the complexities of the PHP language.
I discovered an interesting thing. The class name string must be accessed directly from "flat" variable. Late static binding code that get's it's variable from array that is passed by class instance, throws an syntax error. Bug?
<?php
class A {
public $metadata = array('class' => 'A');
public static function numbers()
{
return 123;
}
}
$instance = new A();
// This throws an error
// Parse error: syntax error, unexpected '::' (T_PAAMAYIM_NEKUDOTAYIM)
var_dump( $instance->metadata['class']::numbers() );
// Get the class name and store it in "flat" variable and now it's ok
$class_name = $instance->metadata['class'];
var_dump( $class_name::numbers() );
// Other tests -------------------------------------------
$arr = array('class' => 'A');
// This works too.
var_dump( $arr['class']::numbers() );
?>
example for static binding on 5.2 and real enumaration
<?php
/**
* Static Binding On PHP 5.2
* @author Tufan Baris YILDIRIM
* @since 26.10.2010
*/
abstract class EnumBase
{
protected $num = 0;
public $toString;
public $toInt;
public function __construct($enumKeyOrVal)
{
unset($this->toString,$this->toInt);
$enums = $this->enums();
if(
empty($enumKeyOrVal)
||
!(isset($enums[$this->num = $enumKeyOrVal])
||
($this->num = array_search($enumKeyOrVal,$enums)) !== false)
)
$this->num = 0;
/**
* 5.3 Version
*/
/*
* if(
empty($enumKeyOrVal)
||
!(isset(static::$enums[$this->num = $enumKeyOrVal])
||
($this->num = array_search($enumKeyOrVal,static::$enums)) !== false)
)
$this->num = 0;
*/
}
#5.3 e geçilirse gerek kalmaz.
public function vars()
{
return get_class_vars(get_class($this));
}
public function enums()
{
$vars = $this->vars();
return $vars['enums'];
}
public function __get($property)
{
if(method_exists($this,'__'.$property))
return $this->{'__'.$property}();
else
return $this->__toString();
}
public function __toInt()
{
return $this->num;
}
public function __toString()
{
$enums = $this->enums();
if(isset($enums[$this->num]))
{
return $enums[$this->num];
}
else
{
return $enums[0];
}
/**
* 5.3 Version
*/
/*
if(isset(static::$enums[$this->num]))
{
return static::$enums[$this->num];
}
else
{
return static::$enums[0];
}
*/
}
}
class Positions extends EnumBase
{
public static $enums = array(
0 => 'Bilinmiyor',
1 => 'Kale',
2 => 'Defans',
3 => 'Orta Saha',
4 => 'Forvet'
);
}
$a = new Positions('Orta Saha');
$b = new Positions(4);
$c = (string)$a; // Orta Saha
$d = (string)$b; // Forvet
?>
On about the fourth try, I finally found a usable LSB emultation for php < 5.3, with minimum extra typing.
<?php
// HELPER CLASS - use as-is
class StaticWrapper {
function __construct($className) { $this->class = $className; }
function __get($name) {
$class = new ReflectionClass($this->class);
try {
return $class->getStaticPropertyValue($name);
}
catch (ReflectionException $e) {
return $class->getConstant($name);
}
}
function __call($name, $args) {
return call_user_func_array(array($this->class, $name),
array_merge(array($this), $args));
}
}
// USAGE EXAMPLE
class A {
const C = "CA";
public static function S() { return new StaticWrapper(__CLASS__); } // must be in every inherited class
static function f($S) { $S->g(); echo "C = " . $S->C . "\n"; }
static function g($S) { echo "A\n"; }
function nonstatic() { $this->S()->f(); }
}
class B extends A {
const C = "CB";
public static function S() { return new StaticWrapper(__CLASS__); } // must be in every inherited class
static function g($S) { echo "B\n"; parent::g($S); }
}
B::S()->f();
$b = new B; $b->nonstatic();
// Both print:
// B
// A
// C = CB
?>
The semantics are exactly the same as in PHP 5.3 for static functions and constants, only a little extra typing. It also supports static member variables, however, that is useless because of another bug/misfeature of PHP < 5.3:
<?php
class A { public static $v = 5; }
class B { public static $v = 42; }
echo A::$v; //prints 42
?>
I.e., (emulated) LSB has no use here as PHP forces the variable in the parent and child class to have the same value, therefore you can just as well use self::.
A tricky behaviour of LSB:
If you forget a "static" keywork in the method definition you can get very unexpected results.
<?php
class BaseClass {
public function callTest() { // static accidentally omitted
static::test();
}
protected static function test() {
echo 'BaseClass::test<br />';
}
}
class ExtendedClass extends BaseClass {
protected static function test() {
echo 'ExtendedClass::test<br />';
}
}
class UserClass {
public function useExtendedClass() {
ExtendedClass::callTest(); // Static call of the *acidentally* non static method
}
public function test() {
echo 'UserClass::test<br />';
}
}
BaseClass::callTest(); // BaseClass::test, as expected
ExtendedClass::callTest(); // ExtendedClass::test, as expected
$userClass = new UserClass();
$userClass->useExtendedClass(); // ExtendedClass::test is expected, but displays UserClass::test!!!
?>
In my opinion late static binding is an anti pattern but there are some benefits though:
<?php
class MyClass extends ClassReference
{
}
?>
MyClass::getQualifiedName() returns the full class name including namespace information. This prevents you from passing class names as plain strings and still lets you take advantage of code completion and type browsing.
MyClass::cast() ensures the type of an object to be of a certain class. This is a kind of type hinting.
<?php
abstract class ClassReference
{
final public static function getQualifiedName()
{
return get_called_class();
}
final public static function cast($object)
{
$className = get_called_class();
if (!is_object($var) || !($object instanceof $className))
{
throw new \UnexpectedValueException("Cast failed. Argument is not of type " . $className . ".");
}
return $object;
}
}
?>
also works the same way with static variables and constants
consider this:
<?php
class A
{
// some stuff....
public static function getInstance()
{
return new self();
}
}
class B extends A
{
//stuff...
}
$obj = B::getInstance();
//versus
class A
{
// some stuff....
public static function getInstance()
{
return new static();
}
}
class B extends A
{
//stuff...
}
$obj = B::getInstance();
?>
also works the same way with static variables and constants
THIS WORKED GREAT FOR ME:
<?php
abstract class ParentClass
{
static function parent_method()
{
$child_class_str = self::get_child_class();
eval("\$r = ".$child_class_str."::abstract_static();");
return $r;
}// CHILD MUST OVERRIDE TO PUT ITSELF INTO TRACE
protected abstract static function abstract_static();
private static function get_child_class()
{
$backtrace = debug_backtrace();
$num = count($backtrace);
for($i = 0; $i < $num; $i++)
{
if($backtrace[$i]["class"] !== __CLASS__)
return $backtrace[$i]["class"];
}
return null;
}
}
class ChildClass extends ParentClass
{
static function parent_method(){ return parent::parent_method(); }
protected static function abstract_static()
{
return __METHOD__."()";
}// From ParentClass
}
print "The call was: ". ChildClass::parent_method();
?>
Suprisingly consts are also lazy bound even though you use self instead of static:
<?php
class A{
const X=1;
const Y=self::X;
}
class B extends A{
const X=1.0;
}
var_dump(B::Y); // float(1.0)
?>
Just a quick reminder to always check your syntax. While I love LSB, I thought it wasn't working:
static::$sKey = not set
…until I realized that I’d completely forgotten to make it a variable variable:
$sKey = 'testStaticClassVarNameThatExistsInThisClassesScope';
static::$$sKey = is set
…of course this applies anywhere in PHP, but because of the (current) newness late static bindings, I’ve seen lots of code with this particular snafu in it from others.
PHP5.3 unavailable, yet in the need for 'static', I did the following.
Any objections? Personally I hate using the the eval() statement...
<?php
class mother
{
function setStatic( $prop, $val ) {
// After this, self:: refers to mother, yet next $class refers to...
//
$class = get_class( $this );
eval( "$class::\$$prop = \$$val;" );
}
}
class child extends mother
{
protected static $sProp;
function writer( $value ) {
parent::setStatic( 'sProp', $value );
}
function reader()
{
return self::$sProp;
}
}
$c = new child();
$c->writer( 3 );
echo $c->reader(); // 3
?>
Something you may find useful for passive code-testing:
<?php
class BaseClass {
function __get($id) {
throw new Exception("Trying to access undefined property '$id'.");
}
function __set($id) {
throw new Exception("Trying to access undefined property '$id'.");
}
}
class MyClass extends BaseClass {
// my implementation
}
?>
Using these magic function as described above will help you to find classes that try to access an undefined (and undocumented) class-member. In most cases: this is an error based on misspelled member names.
At least as of PHP 5.3.0a2 there's a function get_called_class(), which returns the class on which the static method is called.
<?php
class a {
static public function test() {
print get_called_class();
}
}
class b extends a {
}
a::test(); // "a"
b::test(); // "b"
?>
I have been dying to see this issue resolved. I'm very much looking forward to the production release of PHP 5.3...
In my case I have been trying to do the following:
class A {
function __construct() {
echo "I was called by " . static::__CLASS__;
}
}
class B extends A {
function Foo() {
echo "I am class " . __CLASS__;
}
}
$b = new B; // Should echo "I was called by B"
$b->Foo(); // Should echo "I am class B"
At the moment I do the following workaround:
class A {
function __construct($child) {
echo "I was called by " . $child;
}
}
class B extends A {
function __construct() {
parent::__construct(__CLASS__);
}
function Foo() {
echo "I am class " . __CLASS__;
}
}
$b = new B; // Echos "I was called by B"
$b->Foo(); // Echo "I am class B"
As you can see, my current workaround has some overhead and is not as water-tight as the late static binding method.
Here is a small workaround I made for the static inheritance issue. It's not perfect, but it works.
<?php
// BaseClass class will be extended by any class needing static inheritance workaroud
class BaseClass {
// Temporarily stores class name for Entry::getStatic() and Entry::setNextStatic()
protected static $nextStatic = false;
// Returns the real name of the class calling the method, not the one in which it was declared.
protected static function getStatic() {
// If already stored
if (self::$nextStatic) {
// Clean and return
$class = self::$nextStatic;
self::$nextStatic = false;
return $class;
}
// Init
$backTrace = debug_backtrace();
$class = false;
// Walk through
for ($i=0; $i<count($backTrace); $i++) {
// If a class is defined
if (isset($backTrace[$i]['class'])) {
// Check if it is not a basic class
if (!in_array($backTrace[$i]['class'], array('BaseClass', 'GenericClass'))) {
return $backTrace[$i]['class'];
} else {
$class = $backTrace[$i]['class'];
}
} else {
// Returns last known class
return $class;
}
}
// Default
return $class;
}
// If a static method is called within global env, the previous method won't work, so we need to tell BaseClass which
public static function setNextStatic($class) {
// Save value
self::$nextStatic = $class;
}
}
// Generic class declaring various static methods
class GenericClass extends BaseClass {
public static $name = 'Generic';
public function getName() {
$static = get_class_vars(get_class($this));
return $static['name'];
}
public static function basicClassName() {
return self::$name;
}
public static function staticClassName() {
// Get real name
$staticName = self::getStatic();
// Return final class name
$static = get_class_vars($staticName);
return $static['name'];
}
}
// Final class
class SomeClass extends GenericClass {
public static $name = 'Some';
public static function returnClassNameWith($string) {
return $string.' : '.self::staticClassName();
}
}
// Instance call
// Will print 'Some'
$a = new SomeClass();
echo 'Name of $a : '.$a->getName().'<br />';
// Static calls
// Will print 'Generic'
echo 'Basic call to SomeClass::$name : '.SomeClass::basicClassName().'<br />';
// Will print 'Generic'
echo 'Global call to SomeClass::$name : '.SomeClass::staticClassName().'<br />';
// Will print 'Some'
BaseClass::setNextStatic('SomeClass');
echo 'Global call to SomeClass::$name with pre-set : '.SomeClass::staticClassName().'<br />';
// Will print 'Some'
echo 'Internal call to SomeClass::$name : '.SomeClass::returnClassNameWith('This is a ').'<br />';
?>
There are two issues with this workaround :
- if you call a static method from global env, you need to declare the name of the class BEFORE calling the method, otherwise the workaround won't work (see 3rd and 4th examples). But I assume good programming makes few calls to static methods from global scope, so this shouldn't be long to fix if you use it.
- the workaround fails to access to private or protected static vars, as it uses get_class_vars(). If you find any better solution, let us know.
With Php 5.3.0, upgrading will be easy : just delete the methods from the basic class, and search/replace any call to getStatic() and setNextStatic() by static:: - or one could use a selector on PHP_VERSION value to include either the BaseClass file with workaround or a BaseClass file using static::
Trying to recreate an inheritable static part for an object through a singleton pattern.
<?php
/**
* "Inheritable static" for PHP < 5.3
* << Library/Inheritable.php >>
*/
abstract class Inheritable_Static extends Singleton
{
}
abstract class Inheritable
{
public static function getStatic($className)
{
// Use an abstract Singleton
return Singleton::getInstance($className . '_Static') ;
}
public function goStatic()
{
return self::getStatic(get_class($this)) ;
}
}
/**
* Abstract
* << Library/SayIt/Abstract.php >>
*/
abstract class SayIt_Abstract_Static extends Inheritable_Static
{
public $format ;
}
abstract class SayIt_Abstract extends Inheritable
{
protected $_name ;
public function __construct($name)
{
$this->_name = $name ;
}
final public function sayIt()
{
echo sprintf($this->goStatic()->format, $this->_name) . "\n" ;
}
}
/**
* Concrete
* << Library/SayIt/Hello.php >>
*/
class SayIt_Hello_Static extends SayIt_Abstract_Static
{
}
class SayIt_Hello extends SayIt_Abstract
{
public static function getStatic() { return parent::getStatic(__CLASS__) ; }
}
/**
* Test
*/
SayIt_Hello::getStatic()->format = 'Hello %s' ;
$w = new SayIt_Hello('World') ;
$j = new SayIt_Hello('Joe') ;
echo $w->sayIt() ; // Hello World
echo $j->sayIt() ; // Hello Joe
About static parameters, these work as expected.
<?php
class A {
protected static $__CLASS__ = __CLASS__;
public static function constructor(){
return static::$__CLASS__;
}
}
class B extends A {
protected static $__CLASS__ = __CLASS__;
}
echo B::constructor(); // B
?>
will this work for variables as well?
it would be great, if the following worked:
<?php
class A {
protected static $table = "table";
public static function connect(){
//do some stuff here
echo static::$table;
return static::getInstance(); //function getInstance() now can return classes A or B depending on the context it was called
}
...
}
class B extends A {
protected static $table = "subtable";
...
}
$table = B::connect(); //hopefully the output will be: subtable
?>
I think this will be pretty helpful too.
My question is, can just 'static' by itself resolve to the late static class?
I ask this because it could help in making new instances of the derived class, from a base class, by calling a derived class's static method instead of having to create a new instance of the derived class - or explicitly defining a 'getClass' method for each derived class.
Example:
<?php
//There isn't really any purpose for this example I posted
//Just a random implementation
class Base {
static function useful() {
//Create a list of instances of the derived class
$list=array();
for ($i=0;$i<10;$i++) $list[]=new static(); //Here's the point in question
return $list;
}
}
class Derived extends Base {
static function somethingElse() {
//...
$list=static::useful();
}
}
?>
I'm not sure what kind of lexical / whatever-it's-called problems this would make with parsing. I don't think it could really collide with any contexts where you would use static otherwise - variable / method declaration.
Even more so, is there a way to get the class's name to which the keywords 'self', 'parent', or 'static' refer?
Example:
<?php
class Base {
static function stuff() {
echo "Self: ".get_class(self);
echo "Parent: ".get_class(parent);
echo "Derived: ".get_class(static);
}
}
class Derived extends Base {
static function stuff() {
static::stuff();
}
}
?>
I don't think there should be a massive bloat in the PHP core to support all of this, but it would be nice to take advantage of the dynamic nature of PHP.
And yet another side note:
If you're in the instance-level scope in a method of a base, and you want to get a top-level static, here's an ugly workaround (from Thacmus /lib/core.php - see SVN repo):
<?php
//Get reference [?] to static from class
//$class - Class name OR object (uses get_class())
//$var - Not gonna say
function& get_static($class,$var) { //'static_get'?
if (!is_string($class)) $class=get_class($class);
if (!@property_exists($class,$var)) {
trigger_error("Static property does not exist: $class::\$$var");
//debug_callstack(); //This is just a wrapper for debug_backtrace() for HTML
return null;
}
//Store a reference so that the base data can be referred to
//The code [[ return eval('return &'.$class.'::$'.$var.';') ]] does not work - can not return references...
//To establish the reference, use [[ $ref=&get_static(...) ]]
eval('$temp=&'.$class.'::$'.$var.';'); //using
return $temp;
}
?>
@ php at mikebird
You can pass arguments to your constructor through your getInstance method, assuming you are running php5.
public static function getInstance($params = null) {
if (self::$objInstance == null) {
$strClass = static::getClass();
self::$objInstance = new $strClass($params);
}
return self::$objInstance;
}
This would pass the params to your constructor. Love for php.
Finally we can implement some ActiveRecord methods:
<?php
class Model
{
public static function find()
{
echo static::$name;
}
}
class Product extends Model
{
protected static $name = 'Product';
}
Product::find();
?>
Output: 'Product'
This should make life easier and neater if you have a project with a lot of singleton classes e.g.
<?php
class Singleton {
public static $objInstance;
public static function &getInstance() {
if (self::$objInstance == null) {
$strClass = static::getClass();
self::$objInstance = new $strClass;
}
return self::$objInstance;
}
public static function getClass() {
return __CLASS__;
}
}
class Foo extends Singleton {
public $intBar;
public function __construct() {
$this->intBar = 1;
}
public static function getClass() {
return __CLASS__;
}
}
$objFooTwo = Foo::getInstance();
$objFooTwo->intBar = 2;
$objFooOne = Foo::getInstance();
if ($objFooOne->intBar == $objFooTwo->intBar) {
echo 'it is a singleton';
} else {
echo 'it is not a singleton';
}
?>
The above will output 'it is a singleton'. The obvious downfall to this method is not being able to give arguments to the constructor.
If you are using PHP < 5.3.0 you might be interested in the following workaround for late static binding: http://de2.php.net/manual/de/function.get-class.php#77698
For static properties declared in parent class and not redeclared in sub-classes, static:: in parent class would hit parent's properies. (class a and b below)
If those were declared in sub-classes, static:: in parent class will hit sub-classes's own. (class c and d below).
e.g.
<?php
$_CONFIG['a'] = '1';
$_CONFIG['b'] = '2';
$_CONFIG['c'] = '3';
$_CONFIG['d'] = '4';
class static_base
{
static protected $config;
static protected $module_name = '';
static public function load()
{
global $_CONFIG;
static::$config = $_CONFIG[static::$module_name];
}
static public function dump()
{
var_dump(static::$config);
}
}
class a extends static_base
{
static protected $module_name = 'a';
}
class b extends static_base
{
static protected $module_name = 'b';
}
class c extends static_base
{
static protected $module_name = 'c';
static protected $config;
}
class d extends static_base
{
static protected $module_name = 'd';
static protected $config;
}
a::load();
b::load();
a::dump(); // 2
b::dump(); // 2
c::load();
d::load();
c::dump(); // 3
d::dump(); // 4
?>
<?php
// tricky implementation of singleton
// creates a function handle to work with the child singleton
// so you can get the object doing: ClassName() instead of ClassName::getInstance()
// or get some property rightaway: ClassName('nameOfPublicProperty');
// enjoy!!
Abstract class SingletonBase
{
protected function __clone(){}
public function getInstance()
{
static $instance = null;
if($instance==null)
{
$instance = new static;
if(!function_exists($name=get_called_class()))
{
$strCode = "function {$name}(\$attribute=null){
static \$obj = null;
!\$obj AND \$obj = {$name}::getInstance();
return \$attribute ? \$obj->\$attribute : \$obj;
}";
// creates the getInstance handle function
eval($strCode);
}
}
return $instance;
}
}
// EXAMPLE
class MySingletonClass extends SingletonBase
{
var $varTest = "somevalue";
}
$obj = MySingletonClass::getInstance();
echo $obj->varTest; // somevalue
echo MySingletonClass('varTest'); // somevalue
$obj = MySingletonClass();
echo $obj->varTest; // somevalue
?>
If you're still stuck on a pre-5.3 codebase then you can fake a singleton superclass like this. It ain't pretty, though. To do it you pass the type info down the stack manually and then abuse call_user_func to pass the value back to the originating class so that you can access it's static properties.
<?php
abstract class Singleton {
protected function __construct() {}
protected function __clone() {}
public function instance($class) {
$member = 'instance';
$instance = self::getStaticMember($class, $member);
if (!$instance instanceof $class) {
$instance = new $class();
self::setStaticMember($class, $member, $instance);
}
return $instance;
}
protected function setStaticMember($class, $member, $value) { call_user_func(array($class, __FUNCTION__), $member, $value); }
protected function getStaticMember($class, $member) { return call_user_func(array($class, __FUNCTION__), $member); }
}
class MySingleton extends Singleton {
protected static $instance = null;
public function instance() { return parent::instance(__CLASS__); }
protected function setStaticMember($member, $value) { self::$$member = $value; }
protected function getStaticMember($member) { return self::$$member; }
}
?>
get_called_class for PHP < 5.3
<?php
/**
* Return called class name
*
* @author Michael Grenier
* @param int $i_level optional
* @return string
*/
function get_called_class ($i_level = 1)
{
$a_debug = debug_backtrace();
$a_called = array();
$a_called_function = $a_debug[$i_level]['function'];
for ($i = 1, $n = sizeof($a_debug); $i < $n; $i++)
{
if (in_array($a_debug[$i]['function'], array('eval')) ||
strpos($a_debug[$i]['function'], 'eval()') !== false)
continue;
if (in_array($a_debug[$i]['function'], array('__call', '__callStatic')))
$a_called_function = $a_debug[$i]['args'][0];
if ($a_debug[$i]['function'] == $a_called_function)
$a_called = $a_debug[$i];
}
if (isset($a_called['object']) && isset($a_called['class']))
return (string)$a_called['class'];
$i_line = (int)$a_called['line'] - 1;
$a_lines = explode("\n", file_get_contents($a_called['file']));
preg_match("#([a-zA-Z0-9_]+){$a_called['type']}
{$a_called['function']}( )*\(#", $a_lines[$i_line], $a_match);
unset($a_debug, $a_called, $a_called_function, $i_line, $a_lines);
if (sizeof($a_match) > 0)
$s_class = (string)trim($a_match[1]);
else
$s_class = (string)$a_called['class'];
if ($s_class == 'self')
return get_called_class($i_level + 2);
return $s_class;
}
?>
