Como alguém criaria uma classe Singleton usando classes PHP5?
Como alguém criaria uma classe Singleton usando classes PHP5?
Respostas:
/**
* Singleton class
*
*/
final class UserFactory
{
/**
* Call this method to get singleton
*
* @return UserFactory
*/
public static function Instance()
{
static $inst = null;
if ($inst === null) {
$inst = new UserFactory();
}
return $inst;
}
/**
* Private ctor so nobody else can instantiate it
*
*/
private function __construct()
{
}
}
Usar:
$fact = UserFactory::Instance();
$fact2 = UserFactory::Instance();
$fact == $fact2;
Mas:
$fact = new UserFactory()
Lança um erro.
Consulte http://php.net/manual/en/language.variables.scope.php#language.variables.scope.static para entender os escopos de variáveis estáticas e por que a configuração static $inst = null;
funciona.
O PHP 5.3 permite a criação de uma classe Singleton herdável via ligação estática tardia:
class Singleton
{
protected static $instance = null;
protected function __construct()
{
//Thou shalt not construct that which is unconstructable!
}
protected function __clone()
{
//Me not like clones! Me smash clones!
}
public static function getInstance()
{
if (!isset(static::$instance)) {
static::$instance = new static;
}
return static::$instance;
}
}
Isso resolve o problema: antes do PHP 5.3, qualquer classe que estendesse um Singleton produziria uma instância de sua classe pai em vez da sua.
Agora você pode fazer:
class Foobar extends Singleton {};
$foo = Foobar::getInstance();
E $ foo será uma instância do Foobar em vez de uma instância do Singleton.
"subclass should own its own static var. check this: echo get_class(Foobar::getInstance());echo get_class(Singleton::getInstance());"
.
$instance
reside em Singleton, não na subclasse. Depois que alguma subclasse é instanciada, getInstance () retornará essa instância para todas as subclasses.
Infelizmente, a resposta de Inwdr é interrompida quando existem várias subclasses.
Aqui está uma classe base Singleton herdável correta.
class Singleton
{
private static $instances = array();
protected function __construct() {}
protected function __clone() {}
public function __wakeup()
{
throw new Exception("Cannot unserialize singleton");
}
public static function getInstance()
{
$cls = get_called_class(); // late-static-bound class name
if (!isset(self::$instances[$cls])) {
self::$instances[$cls] = new static;
}
return self::$instances[$cls];
}
}
Código do teste:
class Foo extends Singleton {}
class Bar extends Singleton {}
echo get_class(Foo::getInstance()) . "\n";
echo get_class(Bar::getInstance()) . "\n";
A maneira real e moderna de fazer o padrão Singleton é:
<?php
/**
* Singleton Pattern.
*
* Modern implementation.
*/
class Singleton
{
/**
* Call this method to get singleton
*/
public static function instance()
{
static $instance = false;
if( $instance === false )
{
// Late static binding (PHP 5.3+)
$instance = new static();
}
return $instance;
}
/**
* Make constructor private, so nobody can call "new Class".
*/
private function __construct() {}
/**
* Make clone magic method private, so nobody can clone instance.
*/
private function __clone() {}
/**
* Make sleep magic method private, so nobody can serialize instance.
*/
private function __sleep() {}
/**
* Make wakeup magic method private, so nobody can unserialize instance.
*/
private function __wakeup() {}
}
Então agora você pode usá-lo como.
<?php
/**
* Database.
*
* Inherited from Singleton, so it's now got singleton behavior.
*/
class Database extends Singleton {
protected $label;
/**
* Example of that singleton is working correctly.
*/
public function setLabel($label)
{
$this->label = $label;
}
public function getLabel()
{
return $this->label;
}
}
// create first instance
$database = Database::instance();
$database->setLabel('Abraham');
echo $database->getLabel() . PHP_EOL;
// now try to create other instance as well
$other_db = Database::instance();
echo $other_db->getLabel() . PHP_EOL; // Abraham
$other_db->setLabel('Priler');
echo $database->getLabel() . PHP_EOL; // Priler
echo $other_db->getLabel() . PHP_EOL; // Priler
Como você vê, essa percepção é muito mais flexível.
instance
função não $instance
deve sernull
false
Você provavelmente deve adicionar um método __clone () privado para impedir a clonagem de uma instância.
private function __clone() {}
Se você não incluir esse método, será possível o seguinte
$inst1=UserFactory::Instance(); // to stick with the example provided above
$inst2=clone $inst1;
now $inst1
! == $inst2
- eles não são mais a mesma instância.
<?php
/**
* Singleton patter in php
**/
trait SingletonTrait {
protected static $inst = null;
/**
* call this method to get instance
**/
public static function getInstance(){
if (static::$inst === null){
static::$inst = new static();
}
return static::$inst;
}
/**
* protected to prevent clonning
**/
protected function __clone(){
}
/**
* protected so no one else can instance it
**/
protected function __construct(){
}
}
usar:
/**
* example of class definitions using SingletonTrait
*/
class DBFactory {
/**
* we are adding the trait here
**/
use SingletonTrait;
/**
* This class will have a single db connection as an example
**/
protected $db;
/**
* as an example we will create a PDO connection
**/
protected function __construct(){
$this->db =
new PDO('mysql:dbname=foodb;port=3305;host=127.0.0.1','foouser','foopass');
}
}
class DBFactoryChild extends DBFactory {
/**
* we repeating the inst so that it will differentiate it
* from UserFactory singleton
**/
protected static $inst = null;
}
/**
* example of instanciating the classes
*/
$uf0 = DBFactoryChild::getInstance();
var_dump($uf0);
$uf1 = DBFactory::getInstance();
var_dump($uf1);
echo $uf0 === $uf1;
respose:
object(DBFactoryChild)#1 (0) {
}
object(DBFactory)#2 (0) {
}
Se você estiver usando o PHP 5.4: trait é uma opção, para que você não precise desperdiçar a hierarquia de herança para ter o padrão Singleton
e observe também que, se você usa características ou estende o Singleton classe uma extremidade solta era criar um singleton de classes filho, se você não adicionar a seguinte linha de código:
protected static $inst = null;
na classe infantil
o resultado inesperado será:
object(DBFactoryChild)#1 (0) {
}
object(DBFactoryChild)#1 (0) {
}
Esse método aplicará singletons em qualquer classe que você desejar, basta adicionar 1 método à classe que você deseja criar um singleton e isso fará isso por você.
Isso também armazena objetos em uma classe "SingleTonBase" para que você possa depurar todos os objetos que você usou em seu sistema recorrendo novamente aos SingleTonBase
objetos.
Crie um arquivo chamado SingletonBase.php e inclua-o na raiz do seu script!
O código é
abstract class SingletonBase
{
private static $storage = array();
public static function Singleton($class)
{
if(in_array($class,self::$storage))
{
return self::$storage[$class];
}
return self::$storage[$class] = new $class();
}
public static function storage()
{
return self::$storage;
}
}
Então, para qualquer classe que você deseja criar um singleton, basta adicionar este pequeno método único.
public static function Singleton()
{
return SingletonBase::Singleton(get_class());
}
Aqui está um pequeno exemplo:
include 'libraries/SingletonBase.resource.php';
class Database
{
//Add that singleton function.
public static function Singleton()
{
return SingletonBase::Singleton(get_class());
}
public function run()
{
echo 'running...';
}
}
$Database = Database::Singleton();
$Database->run();
E você pode simplesmente adicionar esta função singleton em qualquer classe que você possui e ela criará apenas 1 instância por classe.
NOTA: Você deve sempre tornar o __construct privado para eliminar o uso da nova Class (); instanciações.
class Database{
//variable to hold db connection
private $db;
//note we used static variable,beacuse an instance cannot be used to refer this
public static $instance;
//note constructor is private so that classcannot be instantiated
private function __construct(){
//code connect to database
}
//to prevent loop hole in PHP so that the class cannot be cloned
private function __clone() {}
//used static function so that, this can be called from other classes
public static function getInstance(){
if( !(self::$instance instanceof self) ){
self::$instance = new self();
}
return self::$instance;
}
public function query($sql){
//code to run the query
}
}
Access the method getInstance using
$db = Singleton::getInstance();
$db->query();
Você realmente não precisa usar o padrão Singleton porque é considerado um antipadrão. Basicamente, existem muitas razões para não implementar esse padrão. Leia isto para começar: Prática recomendada em classes singleton PHP .
Se, afinal, você ainda acha que precisa usar o padrão Singleton, poderíamos escrever uma classe que nos permita obter a funcionalidade Singleton estendendo nossa classe abstrata SingletonClassVendor.
Foi com isso que eu vim para resolver esse problema.
<?php
namespace wl;
/**
* @author DevWL
* @dosc allows only one instance for each extending class.
* it acts a litle bit as registry from the SingletonClassVendor abstract class point of view
* but it provides a valid singleton behaviour for its children classes
* Be aware, the singleton pattern is consider to be an anti-pattern
* mostly because it can be hard to debug and it comes with some limitations.
* In most cases you do not need to use singleton pattern
* so take a longer moment to think about it before you use it.
*/
abstract class SingletonClassVendor
{
/**
* holds an single instance of the child class
*
* @var array of objects
*/
protected static $instance = [];
/**
* @desc provides a single slot to hold an instance interchanble between all child classes.
* @return object
*/
public static final function getInstance(){
$class = get_called_class(); // or get_class(new static());
if(!isset(self::$instance[$class]) || !self::$instance[$class] instanceof $class){
self::$instance[$class] = new static(); // create and instance of child class which extends Singleton super class
echo "new ". $class . PHP_EOL; // remove this line after testing
return self::$instance[$class]; // remove this line after testing
}
echo "old ". $class . PHP_EOL; // remove this line after testing
return static::$instance[$class];
}
/**
* Make constructor abstract to force protected implementation of the __constructor() method, so that nobody can call directly "new Class()".
*/
abstract protected function __construct();
/**
* Make clone magic method private, so nobody can clone instance.
*/
private function __clone() {}
/**
* Make sleep magic method private, so nobody can serialize instance.
*/
private function __sleep() {}
/**
* Make wakeup magic method private, so nobody can unserialize instance.
*/
private function __wakeup() {}
}
Use exemplo:
/**
* EXAMPLE
*/
/**
* @example 1 - Database class by extending SingletonClassVendor abstract class becomes fully functional singleton
* __constructor must be set to protected becaouse:
* 1 to allow instansiation from parent class
* 2 to prevent direct instanciation of object with "new" keword.
* 3 to meet requierments of SingletonClassVendor abstract class
*/
class Database extends SingletonClassVendor
{
public $type = "SomeClass";
protected function __construct(){
echo "DDDDDDDDD". PHP_EOL; // remove this line after testing
}
}
/**
* @example 2 - Config ...
*/
class Config extends SingletonClassVendor
{
public $name = "Config";
protected function __construct(){
echo "CCCCCCCCCC" . PHP_EOL; // remove this line after testing
}
}
Apenas para provar que funciona como esperado:
/**
* TESTING
*/
$bd1 = Database::getInstance(); // new
$bd2 = Database::getInstance(); // old
$bd3 = Config::getInstance(); // new
$bd4 = Config::getInstance(); // old
$bd5 = Config::getInstance(); // old
$bd6 = Database::getInstance(); // old
$bd7 = Database::getInstance(); // old
$bd8 = Config::getInstance(); // old
echo PHP_EOL."COMPARE ALL DATABASE INSTANCES".PHP_EOL;
var_dump($bd1);
echo '$bd1 === $bd2' . ($bd1 === $bd2)? ' TRUE' . PHP_EOL: ' FALSE' . PHP_EOL; // TRUE
echo '$bd2 === $bd6' . ($bd2 === $bd6)? ' TRUE' . PHP_EOL: ' FALSE' . PHP_EOL; // TRUE
echo '$bd6 === $bd7' . ($bd6 === $bd7)? ' TRUE' . PHP_EOL: ' FALSE' . PHP_EOL; // TRUE
echo PHP_EOL;
echo PHP_EOL."COMPARE ALL CONFIG INSTANCES". PHP_EOL;
var_dump($bd3);
echo '$bd3 === $bd4' . ($bd3 === $bd4)? ' TRUE' . PHP_EOL: ' FALSE' . PHP_EOL; // TRUE
echo '$bd4 === $bd5' . ($bd4 === $bd5)? ' TRUE' . PHP_EOL: ' FALSE' . PHP_EOL; // TRUE
echo '$bd5 === $bd8' . ($bd5 === $bd8)? ' TRUE' . PHP_EOL: ' FALSE' . PHP_EOL; // TRUE
Toda essa complexidade ("ligação estática tardia" ... harumph) é, para mim, simplesmente um sinal do modelo de objeto / classe quebrado do PHP. Se os objetos de classe fossem objetos de primeira classe (consulte Python), "$ _instance" seria uma instância de classe variável de - um membro do objeto de classe, em oposição a um membro / propriedade de suas instâncias, e também em oposição a compartilhado por seus descendentes. No mundo do Smalltalk, essa é a diferença entre uma "variável de classe" e uma "variável de instância de classe".
No PHP, parece-me que precisamos levar a sério a orientação de que os padrões são um guia para escrever código - talvez pensemos em um modelo Singleton, mas tentando escrever código que herda de uma classe "Singleton" real parece mal orientado para o PHP (embora eu suponha que uma alma empreendedora possa criar uma palavra-chave SVN adequada).
Continuarei a codificar cada singleton separadamente, usando um modelo compartilhado.
Observe que eu estou absolutamente fora da discussão sobre singletons-are-evil, a vida é muito curta.
Sei que isso provavelmente causará uma guerra de chamas desnecessária, mas posso ver como você pode querer mais de uma conexão com o banco de dados, por isso admito que o singleton pode não ser a melhor solução para isso ... no entanto, existem outros usos do padrão singleton que considero extremamente úteis.
Aqui está um exemplo: eu decidi rodar meu próprio MVC e mecanismo de modelagem porque queria algo realmente leve. No entanto, os dados que eu quero exibir contêm muitos caracteres matemáticos especiais, como ≥ e μ, e o que você tem ... Os dados são armazenados como o caractere UTF-8 real no meu banco de dados, em vez de pré-codificado em HTML, porque meu aplicativo pode fornecer outros formatos, como PDF e CSV, além do HTML. O local apropriado para formatar para HTML está dentro do modelo ("visualização", se desejar), responsável por renderizar a seção da página (snippet). Eu quero convertê-los em suas entidades HTML apropriadas, mas a função get_html_translation_table () do PHP não é super rápida. Faz mais sentido recuperar os dados uma vez e armazenar como uma matriz, disponibilizando-os para uso de todos. Aqui' uma amostra bati juntos para testar a velocidade. Presumivelmente, isso funcionaria independentemente de os outros métodos que você usar (depois de obter a instância) serem estáticos ou não.
class EncodeHTMLEntities {
private static $instance = null;//stores the instance of self
private $r = null;//array of chars elligalbe for replacement
private function __clone(){
}//disable cloning, no reason to clone
private function __construct()
{
$allEntities = get_html_translation_table(HTML_ENTITIES, ENT_NOQUOTES);
$specialEntities = get_html_translation_table(HTML_SPECIALCHARS, ENT_NOQUOTES);
$this->r = array_diff($allEntities, $specialEntities);
}
public static function replace($string)
{
if(!(self::$instance instanceof self) ){
self::$instance = new self();
}
return strtr($string, self::$instance->r);
}
}
//test one million encodings of a string
$start = microtime(true);
for($x=0; $x<1000000; $x++){
$dump = EncodeHTMLEntities::replace("Reference method for diagnosis of CDAD, but clinical usefulness limited due to extended turnaround time (≥96 hrs)");
}
$end = microtime(true);
echo "Run time: ".($end-$start)." seconds using singleton\n";
//now repeat the same without using singleton
$start = microtime(true);
for($x=0; $x<1000000; $x++){
$allEntities = get_html_translation_table(HTML_ENTITIES, ENT_NOQUOTES);
$specialEntities = get_html_translation_table(HTML_SPECIALCHARS, ENT_NOQUOTES);
$r = array_diff($allEntities, $specialEntities);
$dump = strtr("Reference method for diagnosis of CDAD, but clinical usefulness limited due to extended turnaround time (≥96 hrs)", $r);
}
$end = microtime(true);
echo "Run time: ".($end-$start)." seconds without using singleton";
Basicamente, vi resultados típicos como este:
php test.php Tempo de execução: 27,842966794968 segundos usando singleton Tempo de execução: 237.78191494942 segundos sem usar o singleton
Portanto, embora eu certamente não seja especialista, não vejo uma maneira mais conveniente e confiável de reduzir a sobrecarga de chamadas lentas para algum tipo de dados, ao mesmo tempo em que é super simples (linha de código única para fazer o que você precisa). Meu exemplo tem apenas um método útil e, portanto, não é melhor do que uma função definida globalmente, mas assim que você tiver dois métodos, você desejará agrupá-los, certo? Estou longe da base?
Além disso, prefiro exemplos que realmente fazem alguma coisa, pois às vezes é difícil visualizar quando um exemplo inclui instruções como "// faça algo útil aqui", que vejo o tempo todo ao procurar por tutoriais.
De qualquer forma, eu adoraria comentários ou comentários sobre por que usar um singleton para esse tipo de coisa é prejudicial (ou excessivamente complicado).
Este artigo aborda o tópico amplamente: http://www.phptherightway.com/pages/Design-Patterns.html#singleton
Observe o seguinte:
- O construtor
__construct()
é declaradoprotected
para impedir a criação de uma nova instância fora da classe através donew
operador.- O método mágico
__clone()
é declaradoprivate
para impedir a clonagem de uma instância da classe através doclone
operador.- O método mágico
__wakeup()
é declaradoprivate
para impedir a desserialização de uma instância da classe através da função globalunserialize()
.- Uma nova instância é criada através de ligação tardia no método de criação estática estática
getInstance()
com a palavra chavestatic
. Isso permite a subclasse declass Singleton
no exemplo.
Eu escrevi há muito tempo pensado para compartilhar aqui
class SingletonDesignPattern {
//just for demo there will be only one instance
private static $instanceCount =0;
//create the private instance variable
private static $myInstance=null;
//make constructor private so no one create object using new Keyword
private function __construct(){}
//no one clone the object
private function __clone(){}
//avoid serialazation
public function __wakeup(){}
//ony one way to create object
public static function getInstance(){
if(self::$myInstance==null){
self::$myInstance=new SingletonDesignPattern();
self::$instanceCount++;
}
return self::$myInstance;
}
public static function getInstanceCount(){
return self::$instanceCount;
}
}
//now lets play with singleton design pattern
$instance = SingletonDesignPattern::getInstance();
$instance = SingletonDesignPattern::getInstance();
$instance = SingletonDesignPattern::getInstance();
$instance = SingletonDesignPattern::getInstance();
echo "number of instances: ".SingletonDesignPattern::getInstanceCount();
Concordo com a primeira resposta, mas também declararia a classe como final, para que não possa ser estendida, pois estender um singleton viola o padrão de singleton. Além disso, a variável de instância deve ser privada para que não possa ser acessada diretamente. Também torne o método __clone privado, para que você não possa clonar o objeto singleton.
Abaixo está um código de exemplo.
/**
* Singleton class
*
*/
final class UserFactory
{
private static $_instance = null;
/**
* Private constructor
*
*/
private function __construct() {}
/**
* Private clone method
*
*/
private function __clone() {}
/**
* Call this method to get singleton
*
* @return UserFactory
*/
public static function getInstance()
{
if (self::$_instance === null) {
self::$_instance = new UserFactory();
}
return self::$_instance;
}
}
Exemplo de uso
$user_factory = UserFactory::getInstance();
O que isso impede você de fazer (o que violaria o padrão singleton ..
VOCÊ NÃO PODE FAZER ISSO!
$user_factory = UserFactory::$_instance;
class SecondUserFactory extends UserFactory { }
Esse deve ser o caminho certo de Singleton.
class Singleton {
private static $instance;
private $count = 0;
protected function __construct(){
}
public static function singleton(){
if (!isset(self::$instance)) {
self::$instance = new Singleton;
}
return self::$instance;
}
public function increment()
{
return $this->count++;
}
protected function __clone(){
}
protected function __wakeup(){
}
}
Eu gostei do método @ jose-segura de usar características, mas não gostei da necessidade de definir uma variável estática nas subclasses. Abaixo está uma solução que a evita armazenando em cache as instâncias em uma variável local estática no método de fábrica indexado pelo nome da classe:
<?php
trait Singleton {
# Single point of entry for creating a new instance. For a given
# class always returns the same instance.
public static function instance(){
static $instances = array();
$class = get_called_class();
if( !isset($instances[$class]) ) $instances[$class] = new $class();
return $instances[$class];
}
# Kill traditional methods of creating new instances
protected function __clone() {}
protected function __construct() {}
}
O uso é o mesmo que @ jose-segura, apenas não é necessária a variável estática nas subclasses.
Classe de banco de dados que verifica se existe alguma instância de banco de dados existente, ela retornará a instância anterior.
class Database {
public static $instance;
public static function getInstance(){
if(!isset(Database::$instance) ) {
Database::$instance = new Database();
}
return Database::$instance;
}
private function __cunstruct() {
/* private and cant create multiple objects */
}
public function getQuery(){
return "Test Query Data";
}
}
$dbObj = Database::getInstance();
$dbObj2 = Database::getInstance();
var_dump($dbObj);
var_dump($dbObj2);
/*
After execution you will get following output:
object(Database)[1]
object(Database)[1]
*/
Ref http://www.phptechi.com/php-singleton-design-patterns-example.html
Este é o exemplo de create singleton na classe Database
padrões de design 1) singleton
class Database{
public static $instance;
public static function getInstance(){
if(!isset(Database::$instance)){
Database::$instance=new Database();
return Database::$instance;
}
}
$db=Database::getInstance();
$db2=Database::getInstance();
$db3=Database::getInstance();
var_dump($db);
var_dump($db2);
var_dump($db3);
então colocar fora é -
object(Database)[1]
object(Database)[1]
object(Database)[1]
use apenas uma instância única e não crie 3 instâncias
Exemplo rápido:
final class Singleton
{
private static $instance = null;
private function __construct(){}
private function __clone(){}
private function __wakeup(){}
public static function get_instance()
{
if ( static::$instance === null ) {
static::$instance = new static();
}
return static::$instance;
}
}
Espero ajuda.
Aqui está o meu exemplo que fornece a capacidade de chamar como $ var = new Singleton () e também criar 3 variáveis para testar se ele cria um novo objeto:
class Singleton{
private static $data;
function __construct(){
if ($this::$data == null){
$this->makeSingleton();
}
echo "<br/>".$this::$data;
}
private function makeSingleton(){
$this::$data = rand(0, 100);
}
public function change($new_val){
$this::$data = $new_val;
}
public function printme(){
echo "<br/>".$this::$data;
}
}
$a = new Singleton();
$b = new Singleton();
$c = new Singleton();
$a->change(-2);
$a->printme();
$b->printme();
$d = new Singleton();
$d->printme();