※当サイトの記事には、広告・プロモーションが含まれます。

静的(スタティック)メンバと「シングルトン」と呼ばれる実装方法

スタティックとは?

⇒  static キーワード(クラスとオブジェクト)- PHPマニュアル

 

オブジェクト指向は「クラスを作って、それをオブジェクトとして呼び出す」ということです。

一般的に、new演算子でクラスのインスタンス(オブジェクト)を作ってクラスの中のプロパティやメソッドにアクセスしたりします。

ですが、クラスの中のプロパティやメソッドにstatic修飾子をつけると、クラスをインスタンス化せずに、アクセス可能です。

ただし、

  • static なプロパティは、インスタンス化されたクラスオブジェクトから アクセスはできません。
  • つまり、static プロパティは、矢印演算子 -> によるオブジェクトからのアクセスはできません。
  • staticなプロパティは、クラス特有の値をもち、オブジェクト毎に値を持ちません。(この値がすべてのオブジェクトで共通ということを示す。)
  • static なメソッドは、インスタンス化されたクラスオブジェクトからもアクセスができます。
  • 疑似変数 $this は、 static として宣言されたメソッドの内部から利用することはできません。
  • static でないメソッドを静的にコールすると、E_STRICT レベルの 警告が発生します。

 など、注意点があります。

 

メンバ(プロパティ、メソッド)にアクセスする方法として、

  1. インスタンス(オブジェクト)を生成して「->(アロー演算子)」を用いてオブジェクト経由でアクセスする方法
  2. インスタンス(オブジェクト)を生成せず、「::演算子」を用いて静的に(スタティックに)アクセスする方法

の2パターンあるようです。

1.はインスタンスのメンバ(「インスタンスメンバ」と呼ぶ事にする)にアクセスし、
2.はクラスのメンバ(「クラスメンバ」と呼ぶ事にする)にアクセスします。

 

なお、「スタティックメンバ」は「クラスメンバ」ですが、「クラスメンバ」は必ずしも「スタティックメンバ」ではありません(「スタティック」でないメンバ関数をクラス関数としてコール出来るので)。

 

「オブジェクト」はオブジェクトの構造を定義した「クラス」の“実例(インスタンス)”で、「クラス」は「オブジェクト」のいわば“雛型”です。

「オブジェクト」は幾つでも生成できますが、「クラス」はひとつしかありません。

 

インスタンスメンバ」にアクセスするという事は“実例(インスタンス)”のメンバにアクセスするという事で、「クラスメンバ」にアクセスするということは“雛型(クラス)”のメンバにアクセスするという事です。

 

「クラスメンバ」にクラス外からアクセスするには、「クラス名::$static_var」や「クラス名::static_method( )」のようにしてアクセスします。

「クラスメンバ」にクラス内からアクセスするには、「self::」を使って「self::$static_var」や「self::static_method( )」のようにしてアクセスします。

ちなみに、「スタティックメソッド」でない「通常メソッド」もこれでコール出来ます。

しかし、この場合にコールされるのは「インスタンスメソッド」です(当然、「クラスメソッド」からのアクセスの場合は「クラスメソッド」としてコールされます)。

 

“派生クラス(子)でメソッドをオーバーライドしたが、基底クラス(親)の方のメソッドをコールしたい時に使う”「parent::」も「self::」と同様に「クラスメンバ」にアクセス(「インスタンスメソッド」から「通常メソッド」への場合は「インスタンスメソッド」にアクセス)します。

 

⇩   スコープ定義演算子(::)につては下記サイトへ

PHPを愛する試み 〜self:: parent:: static:: および遅延静的束縛〜

⇩ 静的メンバなどに関して詳しくは下記サイトへ。

PHPのオブジェクト指向入門 | Objective-PHP.net

【 ほでなすPHP 】 PHP5の基本 -> スタティックメンバ/クラス定数

 

 

 ⇩  PHPマニュアル(変数のスコープ)によると、staticなプロパテイ(メンバ変数)は、

静的変数の使用

変数のスコープに関する別の重要な機能は、静的 (static) 変数です。静的変数はローカル関数スコープのみに 存在しますが、プログラム実行がこのスコープの外で行われるようになっ てもその値を失わないません。次の例を見てください。

例4 静的変数が必要な場面の例

<?php
function test()
{
    
$a 0;
    echo 
$a;
    
$a++;
}
?>

この関数は、コールされる度に$a0にセットし、0 を出力するのでほとん ど役にたちません。変数を1増やす $a++ は、関数から外に出ると変数 $aが消えてしまうために目的を達成しません。現在 のカウントの追跡ができるようにカウント関数を使用できるようにするた めには、変数$aをstaticとして宣言します。

例5 静的変数の使用例

<?php
function test()
{
    static 
$a 0;
    echo 
$a;
    
$a++;
}
?>
 

こうすると、$a は関数が最初にコールされたときにのみ初期化され、 test() 関数がコールされるたびに $a の値を出力してその値を増加させます。

static変数は、再帰関数を実現する1つの手段としても使用されます。再帰 関数は、自分自身をコールする関数です。再帰関数を書くときには、無限 に再帰を行う可能性があるため、注意する必要があります。適当な方法に より再帰を確実に終了させる必要があります。次の簡単な関数は、中止す るタイミングを知るためにstatic変数$countを用いて、 10 回まで再帰を行います。

例6 再帰関数での静的変数の使用

<?php
function test()
{
    static 
$count 0;

    
$count++;
    echo 
$count;
    if (
$count 10) {
        
test();
    }
    
$count--;
}
?>

注意 : 静的変数は、上の例に見られるような方法で宣言されます。 式の結果を静的変数に代入しようとすると、パースエラーが 発生します。

例7 静的変数の宣言

<?php
function foo(){
    static 
$int 0;          // 正しい
    
static $int 1+2;        // 間違い(式を代入しています)
    
static $int sqrt(121);  // 間違い(同じく式を代入しています)

    
$int++;
    echo 
$int;
}
?>

注意 : 静的な宣言は、コンパイル時に解決されます。

 

グローバル変数と静的変数のリファレンス 

PHP4を駆動するZend Engine 1では、 リファレンス変数の修正子 static および global を実装しています。 例えば、関数スコープ内にglobal 命令により実際にインポートされた真のグローバル変数は、 実際にグローバル変数へのリファレンスを作成します。 これにより、以下の例が示すように予測できない動作を引き起こす可能性 があります。

<?php
function test_global_ref() {
    global 
$obj;
    
$obj = &new stdclass;
}

function 
test_global_noref() {
    global 
$obj;
    
$obj = new stdclass;
}

test_global_ref();
var_dump($obj);
test_global_noref();
var_dump($obj);
?>

上の例の出力は以下となります。

NULL
object(stdClass)(0) {
}

類似の動作がstatic命令にも適用されます。 リファレンスは静的に保存することができません。

<?php
function &get_instance_ref() {
    static 
$obj;

    echo 
'Static object: ';
    
var_dump($obj);
    if (!isset(
$obj)) {
        
// Assign a reference to the static variable
        
$obj = &new stdclass;
    }
    
$obj->property++;
    return 
$obj;
}

function &
get_instance_noref() {
    static 
$obj;

    echo 
'Static object: ';
    
var_dump($obj);
    if (!isset(
$obj)) {
        
// Assign the object to the static variable
        
$obj = new stdclass;
    }
    
$obj->property++;
    return 
$obj;
}

$obj1 get_instance_ref();
$still_obj1 get_instance_ref();
echo 
"\n";
$obj2 get_instance_noref();
$still_obj2 get_instance_noref();
?>

この例を実行すると以下の出力となります。

Static object: NULL
Static object: NULL

Static object: NULL
Static object: object(stdClass)(1) {
["property"]=>
int(1)
}

この例は、static変数にリファレンスを代入した時に &get_instance_ref()関数を2回目に コールした際に保持されていないことを示しています。

 

シングルトン

静的メンバが威力を発揮するのが、「シングルトン」と呼ばれる実装方法です。

「シングルトン」とは、アプリケーションの処理全体を通して、あるクラスのインスタンスは絶対に1つしか存在しない事を保証する仕組みです。

例:

<?php

// クラスSingletonSampleの定義

class SingletonSample
{
    private static $instance;

    private function __construct( )
    {

    }

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

 

$ins1 = SingletonSample::getInstance( );
$ins2 = SingletonSample::getInstance( );
    if ($ins1 === $ins2) {
        echo '同一インスタンスです';
    } else {
        echo '違うインスタンスです';
    }

?>

 

まずこのクラスはコンストラクタがprivateになっています。

つまり、外部からはnewによるインスタンス生成が出来ません。

ではどうするのかというと、静的メソッドであるgetInstanceメソッドです。

このメソッドの中では自分自身のインスタンスをnewで生成しています。

クラス内なのでnewが可能なわけです。

そして自分自身のクラスの静的フィールドである$instanceにそれを格納しています。

以降、getInstanceメソッドを再度呼び出しても、newは通らず、既に生成されて$instanceに格納されているインスタンスを返すだけの仕組みです。

そして外側からのインスタンス生成にはgetInstanceを通すしかありません。

つまり、newは絶対に一度しか行われないわけです。

 

 ⇩    シングルトンの他の実装例は下記サイトへ

PHP - シングルトンの抽象化クラス&トレイトを作成する - Qiita

 

シングルトンパターンは使いどころを見極めるのが難しいようです。

 

⇩    シングルトンパターンの問題点は下記サイトへ

シングルトンパターンの誘惑に負けない - Strategic Choice 

 

Singleton パターン(シングルトン・パターン)とは、

GoF(Gang of Four; 4人のギャングたち)によって定義されたデザインパターンの1つである。

Singleton パターンを用いると、そのクラスインスタンスが1つしか生成されないことを保証することができる。

ロケールやLook&Feelなど、絶対にアプリケーション全体で統一しなければならない仕組みの実装に使用される。