再代入不可なローカル変数は作れないのか、というお話です。
最近オブジェクト定数を知り、オブジェクト定数を使いながらつらつら書いていたのですが、一つのメソッドにしか使わない定数が出てきました。
いったいどうすればいいんだ。
概要
定数をメソッド内のスコープで使用できない。
定数
superglobalsと同様に定数のスコープはグローバルです。 つまり、スコープによらずスクリプトの中ではどこでも定数に アクセスできます。スコープの詳細についてはマニュアルの 変数のスコープ をご覧ください。
定数は手動ではdefine()あるいはconstで定義できます。
define("CONSTANT", "Hello world.");
const CONSTANT = 'Hello World';
定数は、定義することができ、変数のスコープ規則に関係なく、あら ゆる場所からアクセス可能です。
オブジェクト定数
オブジェクト定数はクラス内でconstを使い宣言できます。
定数のconstはPHP5.3から使えるようになり、初出はオブジェクト用のようです。
値が変更できない定数をクラス内に定義することができます。
class MyClass
{
const CONSTANT = 'constant value';
function showConstant() {
echo self::CONSTANT . "\n";
}
}
個人的にはオブジェクト定数は馴染み薄く、クラス定数といった方がしっくりきます。
オブジェクト定数は定数とそんなに違いがない
記事を書いている途中まで、オブジェクト定数はクラス内でのみ使えると勘違いしていました。繰り返しますが、勘違いです。
オブジェクト定数のページでもクラス宣言後に
class MyClass
{
const CONSTANT = 'constant value';
function showConstant() {
echo self::CONSTANT . "\n";
}
}
echo MyClass::CONSTANT . "\n";
とグローバルから(インスタンスも必要なく!)オブジェクト定数を呼び出せます。
詳しくはPHPのオブジェクト指向の基礎編。クラス定数について。がわかりやすいです。
見たとおり、クラス定数はクラスのインスタンスを生成せずに、直接参照できます。理屈的には静的メンバと同様です。ていうか、クラス定数も静的メンバです。
静的フィールドとの違いは値の変更が出来るか出来ないかだけのものです。
で、必ずpublicということは、結局クラス定数も通常の定数と同じくグローバルという事になります。
じゃあどう使い分けるのか、の解説もあります。
final
修飾子、ないの?
たしかJava
ではfinal
で変更不可にできたと思います(うろ覚え)
PHPにはないのでしょうか
あったよ、final
修飾子が!
PHP 5 ではキーワード final が導入され、 final を前に付けて定義されたメソッドは子クラスから上書きできません。 クラス自体がfinalと定義された場合には、このクラスを拡張することはできません。
変数は?
注意: プロパティを final として宣言することはできません。 final として宣言できるのはクラスとメソッドのみです。
プロパティは(publicになりますが)const 使えばいいんですが、変数は?
どうすればいいの?
現在の知識ではベストな案がない。
代替、というか普通の使い方が3点。
1 メソッドの中で定数を定義する
メリット
- 値の再代入が無い
- 定数が再利用される可能性が低そう
- 定数がメソッド内で宣言しても常にグローバルという知識が無ければ再利用されない
- 定数がメソッド内で宣言しても常にグローバルという知識があれば、察してそっとしておいてくれる(希望)
デメリット
- 定数の再利用可能性が全領域
- 一般的な名前をつけにくい
2 オブジェクト定数を使う
メリット
- 値の再代入が無い
- そのクラスで使用する定数とわかりやすい
-
クラス::定数名
となるので、一般的な名前をつけやすい
デメリット
3 普通の変数を使う
メリット
- スコープがメソッド内
- 大文字で宣言すれば定数として扱うというローカル規約が多少は認知されている(と思う)
デメリット
- 値が再代入される可能性がある
= 常に値を意識しなければならない
(多分、変数を見たら値が変化していないかが一瞬でもよぎると思います) - 定数であることを周知し難い(変数大文字・コメントなど)
結論 : 普通に変数を使うと思います
結局のところ
- 値を変更されたくない
- 他の箇所で値を自由に使われたくない
の両立ができないことが問題なのですが、1に関しては
「俯瞰して再代入が無いことがわかるほどメソッドを簡潔に作る」
ことができれば無問題だと思います。
仮にメソッドが自分の手を離れて、誰かが途中で値を変える変更をしたとしても、それはそれで一瞬で目につくようになるはずですし。1
全体に注意を払うよりもメソッド内にのみ注意を払うほうが楽なのは当然です。
でも、値が変わらないことをはっきり表現できないのはちょっとムズムズします2
悩むべき問題じゃない
とか
実はあるよ
とかあると小躍りします
余談 : 定数は不変という幻想をぶち殺す
ありえないだろうと思いながらも「php 定数 削除」で検索したら、ありました。
runkit_constant_remove — 定義済みの定数を削除する
メソッドの頭で定数宣言、終わりでrunkit_constant_removeとすれば、一応ローカルな定数は実現出来そうです…
が、定数を自由に削除・再定義できる関数を組み込むことの方が混乱を招きそうです…
追記
PHP7.1からオブジェクト定数にアクセス修飾子が追加される予定のようです。