singleton, multitonパターンの実装でみるPHPの進化

Singleton、Multitonパターンを実装することでPHPの進化を体感してみる。

## PHP5.3

### 影響のあった新機能

+ 遅延静的束縛 のサポートが追加されました。  
[PHP: 遅延静的束縛 (Late Static Bindings) - Manual](http://php.net/manual/ja/language.oop5.late-static-bindings.php)  
[PHP: get_called_class - Manual](http://php.net/manual/ja/function.get-called-class.php)  
[PHP: static キーワード - Manual](http://php.net/manual/ja/language.oop5.static.php)

### コードと解説

遅延静的束縛の実装により、継承が行われたとしても親クラスとは別のインスタンスとしてSingletonパターンを構築することが可能となりました。
PHP5.2までで自クラスを表していたself::はself::を記述したクラスとして振る舞うため、継承先でも継承元のクラスを返す問題を抱えていました。

ここで着目すべきは次の2つのコードです。

```php
static::$instance = new static();
```
この通り、staticキーワードのみで適切にインスタンスを作成できるようになりました。

```php
$class_path = get_called_class();
static::$instance[$class_path] = new static();
```
ですが、multitonインスタンスでは、自クラスを表現するためにget_called_class()を呼び出す必要があります。
これは::classといった形でクラス名およびクラスのフルパスを得る事が出来ないためです。

```php:php5.3
<?php
/**
 * Singleton
 */
class Singleton {
    static $instance = null;

    private final function __construct()
    {}

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

/**
 * Multiton
 */
class Multiton {
    static $instance = array();

    private final function __construct()
    {}

    public static function getInstance()
    {
        $class_path = get_called_class();
        if (!isset(static::$instance[$class_path])) {
            static::$instance[$class_path] = new static();
        }
        return static::$instance[$class_path];
    }
}

/**
 * Use Singleton
 */
class SingleInstance extends Singleton {}

/**
 * Use Multiton
 */
class MultiInstance extends Multiton {}
```
## PHP5.4

### 影響のあった新機能

+ [PHP: トレイト - Manual](http://php.net/manual/ja/language.oop5.traits.php)
+ [PHP: 新機能 - Manual](http://php.net/manual/ja/migration54.new-features.php)  
配列の短縮構文が追加されました。$a = [1, 2, 3, 4]; や $a = ['one' => 1, 'two' => 2, 'three' => 3, 'four' => 4]; のように使えます。

### コードと解説

mix inが可能となるtraitが実装されました。
SingletonやMultitonパターンを"特性"として切り出すことにより、継承ツリーにあまり影響を与えない形で柔軟に再利用を行うことができるようになりました。

デザインパターン自体には影響を与えてはいませんが、配列の省略表記もできるようになりました。
僅かですが、記述がスムーズになりました。

```php:php5.4
<?php
/**
 * Singleton
 */
trait SingletonTrait {
    static $instance = null;

    private final function __construct()
    {}

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

/**
 * Multiton
 */
trait MultitonTrait {
    static $instance = [];

    private final function __construct()
    {}

    public static function getInstance()
    {
        $class_path = get_called_class();
        if (!isset(static::$instance[$class_path])) {
            static::$instance[$class_path] = new static();
        }
        return static::$instance[$class_path];
    }
}

/**
 * Use Singleton
 */
class SingleInstance {
    use SingletonTrait;
}

/**
 * Use Multiton
 */
class MultiInstance {
    use MultitonTrait;
}
```

## PHP5.5

### 影響のあった新機能

+ [PHP: 新機能 - Manual](http://php.net/manual/ja/migration55.new-features.php#migration55.new-features.class-name)
  ::class によるクラス名の解決

### コードと解説

::class によるクラス名の解決が可能になったため、ついにPHP5.3から続いていた、get_called_classを使用しなくても済むようになりました。

```php:php5.4
$class_path = get_called_class();
static::$instance[$class_path] = new static();
```
PHP5.4までは上記のように記述していたコードが、PHP5.5からは次のようにシンプルになります。

```php:php5.5
if (!isset(static::$instance[static::class])) {
    static::$instance[static::class] = new static();
}
```

```php:php5.5
/**
 * Singleton
 */
trait Singleton {
    static $instance = null;

    private final function __construct()
    {}

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

/**
 * Multiton
 */
trait Multiton {
    static $instance = [];

    private final function __construct()
    {}

    public static function getInstance()
    {
        if (!isset(static::$instance[static::class])) {
            static::$instance[static::class] = new static();
        }
        return static::$instance[static::class];
    }
}

/**
 * Use Singleton
 */
class SingleInstance {
    use SingletonTrait;
}

/**
 * Use Multiton
 */
class MultiInstance {
    use MultitonTrait;
}
```


## PHP7.0

### 影響のあった新機能

+ [PHP: 新機能 - Manual](http://php.net/manual/ja/migration70.new-features.php#migration70.new-features.null-coalesce-op)  
Null 合体演算子

### コードと解説

これもデザインパターンには影響はありませんが、記述が劇的に容易になる演算子が追加されました。

```php:php5.5
if (!isset(static::$instance[static::class])) {
    static::$instance[static::class] = new static();
}
return static::$instance[static::class];
```

PHP5.6までは上記のように4行で記述していたコードが、PHP7.0からは次のように1行で完結します。

```php7.0
return static::$instance[static::class] ?? static::$instance[static::class] = new static();
```

```php:php7.0
/**
 * Singleton
 */
trait Singleton {
    static $instance = null;

    private final function __construct()
    {}

    public static function getInstance()
    {
        return static::$instance ?? static::$instance = new static();
    }
}

/**
 * Multiton
 */
trait Multiton {
    static $instance = [];

    private final function __construct()
    {}

    public static function getInstance()
    {
        return static::$instance[static::class] ?? static::$instance[static::class] = new static();
    }
}

/**
 * Use Singleton
 */
class SingleInstance {
    use SingletonTrait;
}

/**
 * Use Multiton
 */
class MultiInstance {
    use MultitonTrait;
}
```

## 終わりに

このようにPHPはより問題の解決を容易にするよう飽くなき進化を続けています。
この記事を読まれて、PHPの進化に興味を持たれた方はぜひ、[PHP: 付録 - Manual](http://php.net/manual/ja/appendices.php)から、各バージョンごとのPHPの新機能を読んでみてください。

最新のPHPについての記述はPHP 7.0.x から PHP 7.1.x への移行（2017/03/19時点）にあります。
このページにある、新機能を読めば新しいPHPの事をより一層理解することができるでしょう。