Я пишу собственное PHP-приложение с нуля и для некоторых классов использую singleton
шаблон, потому что мне нужна информация, которая будет рассчитана один раз, и я просто использую их. Сегодня я написал большую часть своего Приложения, и когда я тестировал все это, оно выдает мне следующую ошибку: Достигнут максимальный уровень вложенности функции ‘100’, прерывание. Я сделал несколько тестов и обнаружил, что ошибка генерируется чем-то вроде этого:
index.php
class Foo
{
public function __construct()
{
if(!class_exists('Bar', false))
{
require 'Bar.php';
}
$bar = new Bar;
$bar->doSomething();
}
public function showSomeInformation()
{
// information
}
}
function F()
{
static $instance = null;
if(is_null($instance))
{
$instance = new Foo;
}
return $instance;
}
F();
Bar.php
class Bar
{
public function doSomething()
{
F()->showSomeInformation();
}
}
На мой взгляд, это действительно потому, что F()
был вызван ранее, и он должен иметь в статической переменной экземпляр Foo, и я считаю, что он должен как-то работать, но это не так.
Я чувствую себя потерянным сейчас. Как заставить его работать или хотя бы как что-то изменить, чтобы иметь похожее поведение?
Хорошо, я уже упоминал об этом в комментариях, но здесь вы идете.
$instance
ценность остается null
все время. Зачем? Посмотрите, что происходит, прежде чем назначить экземпляр $instance
,
До $instance
имеет другое значение, которое вы называете $bar->doSomething();
снова.
Это означает, что вы бежите F()
опять же, но $instance
все еще null
, Теперь вы создаете экземпляр Foo
еще раз, но угадайте, что $instance
все еще нуль.
РЕДАКТИРОВАТЬ: Попробуйте это:
<?php class Foo
{
static $instance = null;
public function __construct()
{
if(!class_exists('Bar', false))
{
require 'Bar.php';
}
self::$instance = $this;
$bar = new Bar;
$bar->doSomething(self::$instance);
}
public function showSomeInformation()
{
// information
}
}
class Bar
{
public function doSomething($instance)
{
F($instance)->showSomeInformation();
}
}
function F($instance = null)
{
if(is_null($instance))
{
$instance = new Foo;
}
return $instance;
}
F();
Если вы используете шаблон синглтона, убедитесь, что сам класс отслеживает, был ли он инициализирован или нет. Разрешение обработки внешнего источника может вызвать много проблем (как вы только что испытали).
Бесконечная рекурсия происходит в конструкторе foo:
function F()
{
static $instance = null;
if(is_null($instance))
{
$instance = new Foo;
echo("Never reached\n");
if( is_null($instance) )
{
echo("Still null!\n");
}
}
return $instance;
}
Первый F (); Вызов не создаст Foo, он вызовет конструктор для Foo, который вызовет F () перед возвратом объекта Foo, но F () вызовет конструктор для еще одного Foo, который будет делать то же самое, навсегда, поэтому никаких новых Foo будет когда-либо возвращаться из F (), поэтому у нас заканчивается стек.
Вот пример нормального одноэлементного паттерна в PHP.
http://www.phptherightway.com/pages/Design-Patterns.html
Мне не нравится паттерн Синглтона, и я думаю, если у вас есть небольшой опыт и вы начнете с юнит-тестирования, вы его тоже ненавидите. Но шаблон работает не так, как вы пробовали.
class MySingletonClass
{
private static $instance;
//make constructor protected, to prevent direct instantiation
protected function __construct() {}
/**
* @return MySingletonClass
*/
public static function getInstance()
{
if (!self::$instance) {
self::$instance = new self(); //new static() would we valid too, relevant for extending (late state binding)
}
return self::$instance;
}
public function getSomething()
{
if (!$this->calculated) {
$this->calculated = $this->calculateSomething();
}
return $this->calculated;
}
}
echo MySingletonClass::getInstance()->getSomething();