web-dev-qa-db-ja.com

PHP5でシングルトンデザインパターンを作成する

PHP5クラスを使用してシングルトンクラスを作成するにはどうすればよいですか?

198
Andrew Moore
/**
 * 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;の設定が機能する理由を理解してください。

263
nlaq

PHP 5.3では、遅延静的バインディングを介して継承可能なシングルトンクラスを作成できます。

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;
    }
}

これにより、PHP 5.3より前のシングルトンを拡張したクラスは、それ自身ではなく親クラスのインスタンスを生成するという問題を解決します。

できるようになりました:

class Foobar extends Singleton {};
$foo = Foobar::getInstance();

また、$ fooは、Singletonのインスタンスではなく、Foobarのインスタンスになります。

116
selfawaresoup

残念ながら 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";
110
mpartel

リアルワンアンドモダンシングルトンパターンの作成方法:

<?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

ご覧のとおり、この実現ははるかに柔軟です。

35
Abraham Tugalov

インスタンスの複製を禁止するには、おそらくプライベート__clone()メソッドを追加する必要があります。

private function __clone() {}

このメソッドを含めない場合、次のことが可能になります

$inst1=UserFactory::Instance(); // to stick with the example provided above
$inst2=clone $inst1;

$inst1!== $inst2-それらはもはや同じインスタンスではありません。

26
Stefan Gehrig
<?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パターンを持つために継承階層を無駄にします

また、traitsまたはextends Singletonクラスのルーズエンドは、次のコード行を追加しない場合、子クラスのシングルトンを作成することでした。

   protected static $inst = null;

子クラスで

予期しない結果は次のようになります。

object(DBFactoryChild)#1 (0) {
}
object(DBFactoryChild)#1 (0) {
}
11
jose segura
protected  static $_instance;

public static function getInstance()
{
    if(is_null(self::$_instance))
    {
        self::$_instance = new self();
    }
    return self::$_instance;
}

このコードは、クラス名を気にせずに任意のクラスに適用できます。

10
hungneox

クラスごとに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つのインスタンスのみが作成されます。

注:常に__constructをprivateにして、新しいClass()の使用を排除する必要があります。インスタンス化。

8
RobertPitt
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();
5
rizon

シングルトンパターンはアンチパターンと見なされるため、実際に使用する必要はありません。基本的に、このパターンをまったく実装しない理由はたくさんあります。はじめにこれを読んでください: 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
5
DevWL

この複雑さ(「遅延静的バインディング」... harumph)はすべて、私にとって、PHPの壊れたオブジェクト/クラスモデルの単なる兆候です。クラスオブジェクトがファーストクラスオブジェクト(Pythonを参照)の場合、「$ _ instance」はクラスinstance変数-インスタンスのメンバー/プロパティではなく、クラスオブジェクトのメンバーになります。 、およびその子孫によって共有されるものとは対照的です。 Smalltalkの世界では、これは「クラス変数」と「クラスインスタンス変数」の違いです。

PHPでは、パターンはコードを書くためのガイドであるというガイダンスを心に留める必要があるかのように見えます。おそらくシングルトンのテンプレートについて考えるかもしれませんが、実際の「シングルトン」クラスを継承するコードを書き込もうとしています。 PHPの見当違いのように見えます(ただし、いくつかの進取の気性のある魂が適切なSVNキーワードを作成できると思っていました)。

共有テンプレートを使用して、各シングルトンを個別にコーディングし続けます。

私は絶対に邪悪な議論から抜け出していることに注意してください。人生は短すぎます。

3
Tom Stambaugh

これはおそらく不必要な火炎戦争を引き起こすだろうことを知っていますが、複数のデータベース接続が必要になる方法を見ることができますので、シングルトンはそのための最良の解決策ではないかもしれないという点を認めます...しかし、私が非常に役立つと思うシングルトンパターンの他の用途。

次に例を示します。本当に軽量なものが欲しかったので、自分の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つしかないため、グローバルに定義された関数よりも優れているわけではありませんが、2つのメソッドができたらすぐにグループ化することになるでしょう。私はベースから外れていますか?

また、チュートリアルを検索するときによく見られる「//ここで何か役に立つこと」などのステートメントがサンプルに含まれていると、視覚化するのが難しい場合があるため、実際に何かをするサンプルを好みます。

とにかく、この種の事柄にシングルトンを使用することが有害である(または過度に複雑である)理由についてのフィードバックやコメントをお待ちしています。

2
user2009125

この記事はトピックを非常に広範囲にカバーしています: http://www.phptherightway.com/pages/Design-Patterns.html#singleton

次のことに注意してください。

  • コンストラクター__construct()protectedとして宣言され、new演算子を介してクラス外で新しいインスタンスが作成されるのを防ぎます。
  • マジックメソッド__clone()privateとして宣言され、clone演算子を介したクラスのインスタンスの複製を防ぎます。
  • マジックメソッド__wakeup()は、privateとして宣言され、グローバル関数unserialize()を介したクラスのインスタンスのシリアル化解除を防ぎます。
  • 新しいインスタンスは、キーワードstaticを使用して、静的作成メソッドgetInstance()の遅延静的バインディングを介して作成されます。これにより、例のclass Singletonのサブクラス化が可能になります。
1

私はここで共有するために長い間考えを書いてきました

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();
1

最初の答えに同意しますが、シングルトンの拡張がシングルトンパターンに違反するため、クラスを拡張できないように、クラスをfinalとして宣言します。また、インスタンス変数は、直接アクセスできないようにプライベートにする必要があります。また、シングルトンオブジェクトを複製できないように、__ 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 { }
0
Joseph Crawford

特性を使用する@ jose-seguraメソッドは好きでしたが、サブクラスで静的変数を定義する必要はありませんでした。以下は、静的ローカル変数のインスタンスを、クラス名でインデックス付けされたファクトリーメソッドにキャッシュすることで回避するソリューションです。

<?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() {}
}

使用法は@ jose-seguraと同じですが、サブクラスの静的変数は不要です。

0
Eric Anderson

既存のデータベースインスタンスがあるかどうかを確認するデータベースクラスは、前のインスタンスを返します。

   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

0
sunil rajput

これはデータベースクラスでシングルトンを作成する例です

設計パターン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つのインスタンスを作成しない

0

これは、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(){

    }

} 
0
Mário Kapusta