PHPにはローカルな定数がない？

再代入不可なローカル変数は作れないのか、というお話です。
最近[オブジェクト定数]を知り、[オブジェクト定数]を使いながらつらつら書いていたのですが、一つのメソッドにしか使わない[定数]が出てきました。
いったいどうすればいいんだ。

# <i class="fa fa-cubes"></i> 概要

[定数]をメソッド内のスコープで使用できない。

* [定数]はグローバルスコープ
* ~~[オブジェクト定数]はクラス内スコープ~~  
[オブジェクト定数]もグローバルスコープ
* ベストな案が思いつかない

# <i class="fa fa-tag"></i> [定数]

>
superglobalsと同様に定数のスコープはグローバルです。 つまり、スコープによらずスクリプトの中ではどこでも定数に アクセスできます。スコープの詳細についてはマニュアルの 変数のスコープ をご覧ください。 

[定数]は手動では[define()]あるいは[const]で定義できます。

>
```php
define("CONSTANT", "Hello world.");
```
```php
const CONSTANT = 'Hello World';
```

[定数]->[構文]

>
定数は、定義することができ、変数のスコープ規則に関係なく、あら ゆる場所からアクセス可能です。 



# <i class="fa fa-tags"></i> [オブジェクト定数]
[オブジェクト定数]はクラス内で[const]を使い宣言できます。
[定数]の[const]はPHP5.3から使えるようになり、初出はオブジェクト用のようです。

>
値が変更できない定数をクラス内に定義することができます。

>
```php
class MyClass
{
    const CONSTANT = 'constant value';
    function showConstant() {
        echo  self::CONSTANT . "\n";
    }
}
```

個人的には[オブジェクト定数]は馴染み薄く、クラス定数といった方がしっくりきます。

## [オブジェクト定数]は[定数]とそんなに違いがない

記事を書いている途中まで、[オブジェクト定数]はクラス内でのみ使えると勘違いしていました。繰り返しますが、勘違いです。
[オブジェクト定数]のページでもクラス宣言後に

>
```php
class MyClass
{
    const CONSTANT = 'constant value';
    function showConstant() {
        echo  self::CONSTANT . "\n";
    }
}
echo MyClass::CONSTANT . "\n";
```

とグローバルから(インスタンスも必要なく！)[オブジェクト定数]を呼び出せます。

詳しくは[PHPのオブジェクト指向の基礎編。クラス定数について。]がわかりやすいです。

>
見たとおり、クラス定数はクラスのインスタンスを生成せずに、直接参照できます。理屈的には静的メンバと同様です。ていうか、クラス定数も静的メンバです。
静的フィールドとの違いは値の変更が出来るか出来ないかだけのものです。
>
で、必ずpublicということは、結局クラス定数も通常の定数と同じくグローバルという事になります。 

じゃあどう使い分けるのか、の解説もあります。

# <i class="fa fa-search"></i> `final`修飾子、ないの？

たしか`Java`では`final`で変更不可にできたと思います(うろ覚え)
PHPにはないのでしょうか

## <i class="fa fa-exclamation"></i> あったよ、`final`修飾子が！

[finalキーワード]

>
PHP 5 ではキーワード final が導入され、 final を前に付けて定義されたメソッドは子クラスから上書きできません。 クラス自体がfinalと定義された場合には、このクラスを拡張することはできません。 

変数は？

>
    注意: プロパティを final として宣言することはできません。 final として宣言できるのはクラスとメソッドのみです。

プロパティは(publicになりますが)[const] 使えばいいんですが、変数は？

# <i class="fa fa-stack-overflow"></i> どうすればいいの？

現在の知識ではベストな案がない。
代替、というか普通の使い方が3点。

* メソッドの中で[定数]を定義する
* [オブジェクト定数]を使う
* 普通の変数を使う

## 1 メソッドの中で[定数]を定義する

### <i class="fa fa-smile-o"></i> メリット

* 値の再代入が無い
* [定数]が再利用される可能性が低そう
 * [定数]がメソッド内で宣言しても常にグローバルという知識が無ければ再利用されない
 * [定数]がメソッド内で宣言しても常にグローバルという知識があれば、察してそっとしておいてくれる（希望）

### <i class="fa fa-frown-o"></i> デメリット

* [定数]の再利用可能性が全領域
* 一般的な名前をつけにくい

## 2 [オブジェクト定数]を使う

### <i class="fa fa-smile-o"></i> メリット

* 値の再代入が無い
* そのクラスで使用する[定数]とわかりやすい
* `クラス::定数名`となるので、一般的な名前をつけやすい

### <i class="fa fa-frown-o"></i> デメリット

* [定数]の再利用可能性が全領域
* [定数]の再利用に躊躇がなさそう  
自分なら「[オブジェクト定数]にしているならどこでつかってもいいよね？」って思いそう

## 3 普通の変数を使う

### <i class="fa fa-smile-o"></i> メリット

* スコープがメソッド内
* 大文字で宣言すれば定数として扱うというローカル規約が多少は認知されている(と思う)

### <i class="fa fa-frown-o"></i> デメリット

* 値が再代入される可能性がある  
= 常に値を意識しなければならない  
(多分、変数を見たら値が変化していないかが一瞬でもよぎると思います)
* [定数](扱い)であることを周知し難い(変数大文字・コメントなど)

# <i class="fa fa-flag"></i> 結論 : 普通に変数を使うと思います

結局のところ

1. 値を変更されたくない
2. 他の箇所で値を自由に使われたくない

の両立ができないことが問題なのですが、1に関しては
「俯瞰して再代入が無いことがわかるほどメソッドを簡潔に作る」
ことができれば無問題だと思います。

仮にメソッドが自分の手を離れて、誰かが途中で値を変える変更をしたとしても、それはそれで一瞬で目につくようになるはずですし。[^1]
全体に注意を払うよりもメソッド内にのみ注意を払うほうが楽なのは当然です。

でも、値が変わらないことをはっきり表現できないのはちょっとムズムズします[^2]

***

悩むべき問題じゃない
とか
実はあるよ
とかあると小躍りします

# 余談 : [定数]は不変という幻想をぶち殺す

ありえないだろうと思いながらも「php 定数 削除」で検索したら、ありました。

[runkit_constant_remove]

>
runkit_constant_remove — 定義済みの定数を削除する

メソッドの頭で[定数]宣言、終わりで[runkit_constant_remove]とすれば、一応ローカルな[定数]は実現出来そうです…
が、[定数]を自由に削除・再定義できる関数を組み込むことの方が混乱を招きそうです…

# 追記

PHP7.1から[オブジェクト定数]にアクセス修飾子が追加される予定のようです。
* [PHP7.1の新機能 - Qiita]


[^1]: なぜ定数扱いの値を変更するのかに関してはわかりませんが
[^2]: そんなことを言い出したら型の不変も指定したくなってJavaとかにしなさいってなるでしょうが

[runkit_constant_remove]:http://php.net/manual/ja/function.runkit-constant-remove.php
[PHPのオブジェクト指向の基礎編。クラス定数について。]:http://www.objective-php.net/basic/const
[finalキーワード]:http://php.net/manual/ja/language.oop5.final.php
[構文]:http://php.net/manual/ja/language.constants.syntax.php
[const]:http://php.net/manual/ja/language.constants.syntax.php
[define()]:http://php.net/manual/ja/function.define.php
[定数]:http://php.net/manual/ja/language.constants.php
[オブジェクト定数]:http://php.net/manual/ja/language.oop5.constants.php
[PHP7.1の新機能 - Qiita]:http://qiita.com/rana_kualu/items/476385b674d27339f911#support-class-constant-visibility