Bagaimana cara membuat kelas Singleton menggunakan kelas PHP5?
Bagaimana cara membuat kelas Singleton menggunakan kelas PHP5?
Jawaban:
/**
* 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()
{
}
}
Menggunakan:
$fact = UserFactory::Instance();
$fact2 = UserFactory::Instance();
$fact == $fact2;
Tapi:
$fact = new UserFactory()
Melempar kesalahan
Lihat http://php.net/manual/en/language.variables.scope.php#language.variables.scope.static untuk memahami cakupan variabel statis dan mengapa pengaturan static $inst = null;
berfungsi.
PHP 5.3 memungkinkan pembuatan kelas Singleton yang dapat diwariskan melalui pengikatan statis akhir:
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;
}
}
Ini memecahkan masalah, bahwa sebelum PHP 5.3 setiap kelas yang memperpanjang Singleton akan menghasilkan turunan dari kelas induknya bukan miliknya sendiri.
Sekarang Anda bisa melakukannya:
class Foobar extends Singleton {};
$foo = Foobar::getInstance();
Dan $ foo akan menjadi turunan dari Foobar bukan turunan dari Singleton.
"subclass should own its own static var. check this: echo get_class(Foobar::getInstance());echo get_class(Singleton::getInstance());"
.
$instance
berada di Singleton, bukan subclass. Setelah beberapa subclass di-instantiated, getInstance () akan mengembalikan instance itu untuk semua subclass.
Sayangnya jawaban Inwdr terputus ketika ada beberapa subclass.
Ini adalah kelas dasar Singleton bawaan yang benar.
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];
}
}
Kode uji:
class Foo extends Singleton {}
class Bar extends Singleton {}
echo get_class(Foo::getInstance()) . "\n";
echo get_class(Bar::getInstance()) . "\n";
Cara Nyata dan Modern untuk membuat Pola Singleton adalah:
<?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() {}
}
Jadi sekarang Anda bisa menggunakannya seperti.
<?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
Seperti yang Anda lihat, realisasi ini jauh lebih fleksibel.
instance
fungsi $instance
seharusnya null
tidakfalse
Anda mungkin harus menambahkan metode __clone () pribadi untuk melarang kloning instance.
private function __clone() {}
Jika Anda tidak memasukkan metode ini, berikut ini menjadi mungkin
$inst1=UserFactory::Instance(); // to stick with the example provided above
$inst2=clone $inst1;
sekarang $inst1
! == $inst2
- mereka bukan contoh yang sama lagi.
<?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(){
}
}
menggunakan:
/**
* 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) {
}
Jika Anda menggunakan PHP 5.4: trait nya pilihan, sehingga Anda tidak perlu membuang hirarki warisan untuk memiliki pola Singleton
dan perhatikan juga apakah Anda menggunakan ciri atau memperluas kelas Singleton pada akhirnya adalah membuat singleton dari kelas anak jika Anda tidak menambahkan baris kode berikut:
protected static $inst = null;
di kelas anak
hasil yang tak terduga adalah:
object(DBFactoryChild)#1 (0) {
}
object(DBFactoryChild)#1 (0) {
}
Metode ini akan memberlakukan lajang pada setiap kelas yang Anda inginkan, yang harus Anda lakukan adalah menambahkan 1 metode ke kelas yang ingin Anda buat lajang dan ini akan melakukannya untuk Anda.
Ini juga menyimpan objek dalam kelas "SingleTonBase" sehingga Anda dapat men-debug semua objek yang telah Anda gunakan dalam sistem Anda dengan mengulangi SingleTonBase
objek.
Buat file bernama SingletonBase.php dan sertakan di root skrip Anda!
Kodenya adalah
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;
}
}
Kemudian untuk kelas mana pun yang Anda ingin membuat singleton cukup tambahkan metode tunggal kecil ini.
public static function Singleton()
{
return SingletonBase::Singleton(get_class());
}
Ini adalah contoh kecil:
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();
Dan Anda bisa menambahkan fungsi singleton ini di kelas yang Anda miliki dan itu hanya akan membuat 1 instance per kelas.
CATATAN: Anda harus selalu menjadikan __construct pribadi untuk menghilangkan penggunaan Kelas baru (); Instansiasi.
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();
Anda tidak benar-benar perlu menggunakan pola Singleton karena dianggap sebagai antipattern. Pada dasarnya ada banyak alasan untuk tidak menerapkan pola ini sama sekali. Baca ini untuk mulai dengan: Praktik terbaik di kelas PHP tunggal .
Jika bagaimanapun Anda masih berpikir Anda perlu menggunakan pola Singleton maka kita bisa menulis kelas yang memungkinkan kita untuk mendapatkan fungsionalitas Singleton dengan memperluas kelas abstrak SingletonClassVendor kami.
Inilah yang saya datangi untuk menyelesaikan masalah ini.
<?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() {}
}
Gunakan contoh:
/**
* 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
}
}
Hanya untuk membuktikan bahwa itu berfungsi seperti yang diharapkan:
/**
* 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
Semua kerumitan ini ("keterlambatan pengikatan statis" ... harumph), bagi saya, hanyalah tanda dari objek PHP rusak / model kelas. Jika objek kelas adalah objek kelas satu (lihat Python), maka "$ _instance" akan menjadi instance kelas variabel - anggota objek kelas, yang bertentangan dengan anggota / properti dari instansnya, dan juga sebagai lawan untuk dibagikan oleh keturunannya. Di dunia Smalltalk, ini adalah perbedaan antara "variabel kelas" dan "variabel instance kelas".
Dalam PHP, saya merasa seolah-olah kita perlu memperhatikan pedoman bahwa pola adalah panduan untuk menulis kode - kita mungkin berpikir tentang template Singleton, tetapi mencoba untuk menulis kode yang mewarisi dari kelas "Singleton" yang sebenarnya tampak sesat untuk PHP (meskipun saya kira beberapa jiwa giat dapat membuat kata kunci SVN yang cocok).
Saya akan terus hanya kode setiap singleton secara terpisah, menggunakan templat bersama.
Perhatikan bahwa saya benar-benar keluar dari diskusi lajang-jahat, hidup ini terlalu singkat.
Saya tahu ini mungkin akan menyebabkan perang api yang tidak perlu, tetapi saya dapat melihat bagaimana Anda mungkin menginginkan lebih dari satu koneksi basis data, jadi saya akan mengakui bahwa singleton mungkin bukan solusi terbaik untuk itu ... namun, ada kegunaan lain dari pola singleton yang menurut saya sangat berguna.
Berikut ini contohnya: Saya memutuskan untuk menggulung MVC dan mesin templating saya sendiri karena saya menginginkan sesuatu yang sangat ringan. Namun, data yang ingin saya tampilkan berisi banyak karakter matematika khusus seperti ≥ dan μ dan apa pun yang Anda miliki ... Data disimpan sebagai karakter UTF-8 yang sebenarnya dalam basis data saya alih-alih pra-HTML-disandikan karena aplikasi saya dapat memberikan format lain seperti PDF dan CSV selain HTML. Tempat yang tepat untuk memformat HTML ada di dalam templat ("view" jika Anda mau) yang bertanggung jawab untuk merender bagian halaman itu (snippet). Saya ingin mengonversikannya ke entitas HTML yang sesuai, tetapi fungsi PHP get_html_translation_table () tidak super cepat. Masuk akal untuk mengambil data satu kali dan menyimpannya sebagai sebuah array, membuatnya tersedia untuk semua orang. Sini' sa sampel saya mengetuk bersama untuk menguji kecepatan. Agaknya, ini akan berhasil terlepas dari apakah metode lain yang Anda gunakan (setelah mendapatkan contoh) statis atau tidak.
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";
Pada dasarnya, saya melihat hasil khas seperti ini:
php test.php Durasi: 27.842966794968 detik menggunakan singleton Durasi: 237.78191494942 detik tanpa menggunakan singleton
Jadi, walaupun saya jelas bukan pakar, saya tidak melihat cara yang lebih nyaman dan dapat diandalkan untuk mengurangi overhead panggilan lambat untuk beberapa jenis data, sambil membuatnya super sederhana (satu baris kode untuk melakukan apa yang Anda butuhkan). Diberikan contoh saya hanya memiliki satu metode yang bermanfaat, dan karena itu tidak lebih baik dari fungsi yang didefinisikan secara global, tetapi begitu Anda memiliki dua metode, Anda akan ingin mengelompokkannya bersama, bukan? Apakah saya jauh dari markas?
Juga, saya lebih suka contoh yang benar-benar MELAKUKAN sesuatu, karena kadang-kadang sulit untuk memvisualisasikan ketika contoh menyertakan pernyataan seperti "// lakukan sesuatu yang berguna di sini" yang saya lihat sepanjang waktu ketika mencari tutorial.
Ngomong-ngomong, saya suka umpan balik atau komentar tentang mengapa menggunakan singleton untuk hal semacam ini merugikan (atau terlalu rumit).
Artikel ini membahas topik cukup luas: http://www.phptherightway.com/pages/Design-Patterns.html#singleton
Perhatikan yang berikut ini:
- Konstruktor
__construct()
dinyatakanprotected
untuk mencegah pembuatan instance baru di luar kelas melaluinew
operator.- Metode ajaib
__clone()
dinyatakanprivate
untuk mencegah kloning instance kelas melaluiclone
operator.- Metode ajaib
__wakeup()
dinyatakanprivate
untuk mencegah penghapusan kelas instance melalui fungsi globalunserialize()
.- Mesin virtual baru dibuat melalui pengikatan statis lanjut dalam metode pembuatan statis
getInstance()
dengan kata kuncistatic
. Ini memungkinkan subklasifikasiclass Singleton
dalam contoh.
Saya telah menulis pemikiran lama untuk dibagikan di sini
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();
Saya setuju dengan jawaban pertama tetapi saya juga akan menyatakan kelas sebagai final sehingga tidak dapat diperpanjang karena memperluas singleton melanggar pola singleton. Juga variabel instan harus pribadi sehingga tidak dapat diakses secara langsung. Jadikan juga metode __clone pribadi sehingga Anda tidak dapat mengkloning objek singleton.
Di bawah ini adalah beberapa contoh kode.
/**
* 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;
}
}
Contoh Penggunaan
$user_factory = UserFactory::getInstance();
Apa ini menghentikan Anda dari melakukan (yang akan melanggar pola singleton ..
ANDA TIDAK BISA MELAKUKANNYA!
$user_factory = UserFactory::$_instance;
class SecondUserFactory extends UserFactory { }
Ini harus menjadi cara yang benar dari 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(){
}
}
Saya suka metode @ jose-segura menggunakan ciri-ciri tetapi tidak suka kebutuhan untuk mendefinisikan variabel statis pada sub-kelas. Di bawah ini adalah solusi yang menghindarinya dengan melakukan caching instance dalam variabel lokal statis ke metode pabrik yang diindeks oleh nama kelas:
<?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() {}
}
Penggunaannya sama dengan @ jose-segura hanya tidak perlu untuk variabel statis di sub-kelas.
Kelas database yang memeriksa jika ada instance database yang ada, ia akan mengembalikan instance sebelumnya.
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
Ini adalah contoh dari membuat singleton pada kelas Database
pola desain 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);
maka put put adalah -
object(Database)[1]
object(Database)[1]
object(Database)[1]
gunakan hanya satu instance tidak membuat 3 instance
Contoh cepat:
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;
}
}
Semoga bantuan.
Inilah contoh saya yang memberikan kemampuan untuk memanggil $ var = new Singleton () dan juga membuat 3 variabel untuk menguji apakah ia menciptakan objek baru:
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();