PHP5クラスを使用してシングルトンクラスを作成するにはどうすればよいですか?
22 に答える
/**
* 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()
{
}
}
使用するには:
$fact = UserFactory::Instance();
$fact2 = UserFactory::Instance();
$fact == $fact2;
しかし:
$fact = new UserFactory()
エラーをスローします。
静的変数のスコープと設定が機能する理由を理解するには、 http://php.net/manual/en/language.variables.scope.php#language.variables.scope.staticを参照してくださいstatic $inst = null;
。
残念ながら、複数のサブクラスがある場合、Inwdr の答えは壊れます。
これは正しい継承可能なシングルトン基本クラスです。
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];
}
}
テストコード:
class Foo extends Singleton {}
class Bar extends Singleton {}
echo get_class(Foo::getInstance()) . "\n";
echo get_class(Bar::getInstance()) . "\n";
シングルトン パターンを作成するリアル ワンかつモダンな方法は次のとおりです。
<?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() {}
}
これで、次のように使用できます。
<?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
ご覧のとおり、この実現ははるかに柔軟です。
インスタンスのクローン作成を禁止するには、おそらくプライベート __clone() メソッドを追加する必要があります。
private function __clone() {}
このメソッドを含めない場合、次のことが可能になります
$inst1=UserFactory::Instance(); // to stick with the example provided above
$inst2=clone $inst1;
今$inst1
!== $inst2
- それらはもはや同じインスタンスではありません。
<?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(){
}
}
使用する:
/**
* 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;
応答:
object(DBFactoryChild)#1 (0) {
}
object(DBFactory)#2 (0) {
}
PHP 5.4 を使用している場合: traitはオプションであるため、 Singleton パターンを使用するために継承階層を無駄にする必要はありません。
また、トレイトを使用するかシングルトンクラスを拡張するかにかかわらず、次のコード行を追加しない場合、子クラスのシングルトンを作成するのがルーズ エンドであることに注意してください。
protected static $inst = null;
子クラスで
予期しない結果は次のようになります。
object(DBFactoryChild)#1 (0) {
}
object(DBFactoryChild)#1 (0) {
}
protected static $_instance;
public static function getInstance()
{
if(is_null(self::$_instance))
{
self::$_instance = new self();
}
return self::$_instance;
}
このコードは、クラス名を気にせずに任意のクラスに適用できます。
クラスごとに 1 行で複数のオブジェクトをサポート:
このメソッドは、任意のクラスにシングルトンを強制します。シングルトンを作成するクラスに 1 つのメソッドを追加するだけで、これが実行されます。
また、オブジェクトを「SingleTonBase」クラスに格納するため、システムで使用したすべてのオブジェクトをオブジェクトを再帰的にデバッグできSingleTonBase
ます。
SingletonBase.php というファイルを作成し、スクリプトのルートに含めます。
コードは
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;
}
}
次に、シングルトンを作成したいクラスに対して、この小さな単一のメソッドを追加するだけです。
public static function Singleton()
{
return SingletonBase::Singleton(get_class());
}
以下に小さな例を示します。
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();
そして、このシングルトン関数を任意のクラスに追加するだけで、クラスごとに 1 つのインスタンスしか作成されません。
注: new Class(); の使用をなくすために、常に __construct を非公開にする必要があります。インスタンス化。
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();
シングルトン パターンはアンチパターンと見なされるため、実際に使用する必要はありません。基本的に、このパターンをまったく実装しない理由はたくさんあります。最初にこれを読んでください: PHP シングルトン クラスのベスト プラクティス。
それでも Singleton パターンを使用する必要があると思われる場合は、SingletonClassVendor 抽象クラスを拡張することで、Singleton 機能を取得できるクラスを作成できます。
これは、この問題を解決するために私が持ってきたものです。
<?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() {}
}
使用例:
/**
* 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
}
}
期待どおりに機能することを証明するためだけに:
/**
* 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
これにより不必要な炎上戦争が発生する可能性があることはわかっていますが、複数のデータベース接続が必要になる可能性があることはわかります。そのため、シングルトンがそのための最善の解決策ではない可能性があることを認めます...ただし、私が非常に役立つと思うシングルトンパターンの他の使用法。
次に例を示します。本当に軽量なものが欲しかったので、自分のMVCとテンプレートエンジンをロールすることにしました。ただし、表示したいデータには、≥やμなどの特殊な数学文字がたくさん含まれています...データは、HTMLで事前にエンコードされているのではなく、実際のUTF-8文字としてデータベースに保存されます。私のアプリは、HTMLに加えて、PDFやCSVなどの他の形式を配信できます。HTMLをフォーマットする適切な場所は、そのページセクション(スニペット)のレンダリングを担当するテンプレート(必要に応じて「ビュー」)内です。それらを適切なHTMLエンティティに変換したいのですが、PHPのget_html_translation_table()関数は超高速ではありません。データを一度取得して配列として保存し、すべてのユーザーが使用できるようにする方が理にかなっています。ここ' サンプル私は速度をテストするために一緒にノックしました。おそらく、これは、(インスタンスを取得した後に)使用する他のメソッドが静的であるかどうかに関係なく機能します。
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";
基本的に、私は次のような典型的な結果を見ました:
php test.php 実行時間:シングルトンを使用して27.842966794968秒 実行時間:シングルトンを使用せずに237.78191494942秒
したがって、私は確かに専門家ではありませんが、ある種のデータの低速呼び出しのオーバーヘッドを削減し、それを非常に単純にする(必要なことを実行するための1行のコード)ためのより便利で信頼できる方法はわかりません。確かに、私の例には1つの便利なメソッドしかないため、グローバルに定義された関数よりも優れていますが、2つのメソッドがあるとすぐに、それらをグループ化する必要があります。私は基地から離れていますか?
また、チュートリアルを検索するときにいつも目にする「//ここで何か役に立つことをする」などのステートメントが例に含まれていると、視覚化が難しい場合があるため、実際に何かを行う例が好きです。
とにかく、このタイプのものにシングルトンを使用することが有害である(または過度に複雑である)理由についてのフィードバックやコメントがあればいいのですが。
このすべての複雑さ (「遅い静的バインディング」... 障害) は、私にとって、PHP の壊れたオブジェクト/クラス モデルの兆候にすぎません。クラス オブジェクトがファースト クラス オブジェクト (Python を参照) である場合、"$_instance" はクラスインスタンス変数になります。つまり、インスタンスのメンバー/プロパティではなく、クラス オブジェクトのメンバーであり、共有でもありません。その子孫によって。Smalltalk の世界では、これが「クラス変数」と「クラス インスタンス変数」の違いです。
PHP では、パターンはコードを記述するためのガイドであるというガイダンスを心に留める必要があるように思えます。おそらく、Singleton テンプレートについて考えるかもしれませんが、実際の「Singleton」クラスから継承するコードを記述しようとしています。 PHP には見当違いのように見えます (進取の気性に富む人なら、適切な SVN キーワードを作成できると思いますが)。
共有テンプレートを使用して、各シングルトンを個別にコーディングし続けます。
私はシングルトンは悪であるという議論には絶対に近づかないことに注意してください。人生は短すぎます。
この記事では、トピックを非常に広範囲にカバーしています: http://www.phptherightway.com/pages/Design-Patterns.html#singleton
次の点に注意してください。
- コンストラクターは、演算子を介してクラスの外部に新しいインスタンスを作成しないように
__construct()
宣言されています。protected
new
- マジック メソッドは、演算子によるクラスのインスタンスの複製を防ぐように
__clone()
宣言されています。private
clone
- マジック メソッドは、グローバル関数によるクラスのインスタンスの非シリアル化を防止するように
__wakeup()
宣言されています。private
unserialize()
- 新しいインスタンスは、静的作成メソッド
getInstance()
でキーワードを使用して遅延静的バインディングを介して作成されますstatic
。これにより、class Singleton
例のサブクラス化が可能になります。
私は最初の答えに同意しますが、シングルトンを拡張するとシングルトンパターンに違反するため、拡張できないように、クラスを最終として宣言します。また、インスタンス変数は直接アクセスできないようにプライベートにする必要があります。また、__clone メソッドをプライベートにして、シングルトン オブジェクトを複製できないようにします。
以下はサンプルコードです。
/**
* 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;
}
}
使用例
$user_factory = UserFactory::getInstance();
これにより何をすることができなくなりますか(シングルトンパターンに違反します..
あなたはこれを行うことはできません!
$user_factory = UserFactory::$_instance;
class SecondUserFactory extends UserFactory { }
これは、データベース クラスでシングルトンを作成する例です。
設計パターン 1) シングルトン
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);
そのときの出力は --
object(Database)[1]
object(Database)[1]
object(Database)[1]
単一のインスタンスのみを使用し、3 つのインスタンスを作成しない
これはシングルトンの正しい方法です。
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(){
}
}
上記の答えは大丈夫ですが、さらに追加します。
2021 年にここに来る人は誰でも、Singleton
Pattern クラスを としてtrait
使用し、これを任意のクラスで再利用する別の例を示します。
<?php
namespace Akash;
trait Singleton
{
/**
* Singleton Instance
*
* @var Singleton
*/
private static $instance;
/**
* Private Constructor
*
* We can't use the constructor to create an instance of the class
*
* @return void
*/
private function __construct()
{
// Don't do anything, we don't want to be initialized
}
/**
* Get the singleton instance
*
* @return Singleton
*/
public static function getInstance()
{
if (!isset(self::$instance)) {
self::$instance = new self();
}
return self::$instance;
}
/**
* Private clone method to prevent cloning of the instance of the
* Singleton instance.
*
* @return void
*/
private function __clone()
{
// Don't do anything, we don't want to be cloned
}
/**
* Private unserialize method to prevent unserializing of the Singleton
* instance.
*
* @return void
*/
private function __wakeup()
{
// Don't do anything, we don't want to be unserialized
}
}
そのため、どのクラスでも簡単に使用できます。UserSeeder
クラスに Singleton パターンを実装したいとします。
<?php
class UserSeeder
{
use Singleton;
/**
* Seed Users
*
* @return void
*/
public function seed()
{
echo 'Seeding...';
}
}
以前のインスタンスを返す既存のデータベース インスタンスがあるかどうかをチェックするデータベース クラス。
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]
*/
参照http://www.phptechi.com/php-singleton-design-patterns-example.html
$var = new Singleton() として呼び出す機能を提供し、新しいオブジェクトを作成するかどうかをテストするための 3 つの変数を作成する私の例を次に示します。
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();