Задача
В общем, сложилась такая ситуация, что на предоставленном для проекта хостинге, версия PHP была 5.2, а сам проект написан под 5.3. Наверняка все, кто работает с PHP, знают, что в версии 5.3 появилась возможность доступа к имени класса, полученного с помощью позднего статического связывания.
К примеру:
<?php
class Singleton {
static $instances = Array();
private function __construct() {}
private function __clone() {}
private function __wakeup() {}
static function model(){
$class = get_called_class();
if (!isset(self::$instances[$class])) {
self::$instances[$class] = new $class();
}
return self::$instances[$class];
}
}
И теперь любой наследник этого класса будет Singleton`ом.
class Test extends Singleton {
public function say(){
return 'Hi Habr!';
}
}
echo Test::model()->say();
Такой приём возможен из-за появления новой функции get_called_class в версии 5.3. Понятно, что в ранних версиях такое работать не будет. Но что делать, если код уже написан, а последняя версия PHP недоступна?
Решение
Решением является смена хостинга или установка последнее версии PHP :). Но к примеру у меня сложилась ситуация, что сейчас такое невозможно, а проект должен работать, так что пришлось выкручиваться.
<?php
if(!function_exists('get_called_class')) {
function get_called_class() {
$obj = false;
$backtrace = debug_backtrace();
foreach($backtrace as $row){
if($row['function'] == 'call_user_func'){
$obj = explode('::', $backtrace[2]['args'][0]);
$obj = $obj[0];
break;
}
}
if(!$obj){
$backtrace = $backtrace[1];
$file = file_get_contents($backtrace["file"]);
$file = explode("\n", $file);
for($line = $backtrace["line"] - 1; $line > 0; $line--) {
preg_match("/(?<class>\w+)::(.*)/", trim($file[$line]), $matches);
if (isset($matches["class"])){
return $matches["class"];
}
}
throw new Exception("Could not find");
}
return $obj;
}
}
class Singleton {
static $instances = Array();
private function __construct() {}
private function __clone() {}
private function __wakeup() {}
static function model(){
$class = get_called_class();
if (!isset(self::$instances[$class])) {
self::$instances[$class] = new $class();
}
return self::$instances[$class];
}
}
Как временное решение проблемы вполне подойдет. Ну и для решения проблем, связанных с возможностью ссылаться на класс используя переменную:
$class_name::model();
Заменяется на:
call_user_func($class_name. '::model');
Вот собственно и все. Спасибо за внимание.