はじめに
PHP Manualの static
キーワードに関するページ(日本語版, 英語版)を読んで、どのようなクラスプロパティ/クラスメソッド定義がOKでどのようなのがNGなのか気になりました。
PHPにはクラスの他にもインターフェースやトレイトというクラスっぽいものもあるので、 どこの中で どう定義するか のパターンを考えて実験してみることにしました。
どこの中で
クラス
PHPには ただのクラス の他にも abstractクラス と finalクラス があります。 abstract かつ final なクラス はありません。
// ただのクラス
class SomeClass {}
// abstractクラス
abstract class SomeAbstractClass {}
// finalクラス
final class SomeFinalClass {}
abstract かつ final なクラス を作ろうとするとどうなるかも実験してみました。
// abstractなfinalクラスを作りたい
abstract final class SomeAbstractFinalClass {}
// => PHP Fatal error: Cannot use the final modifier on an abstract class
// finalなabstractクラスを作りたい
final abstract class SomeFinalAbstractClass {}
// => PHP Fatal error: Cannot use the final modifier on an abstract class
脱線します。 abstract かつ final なクラス を作れないことに今までは納得していたのですが、今、納得できなくなりました。
今までは次のように考えて納得していました。
abstractクラス のインスタンスを生成するためには abstractメソッド を全て実装した継承クラスを作らなければならない。一方で finalクラス の継承クラスを作ることはできない。 abstract かつ final なクラス があったとしても、そのクラスのインスタンスを生成することはできないことになるから、言語仕様で abstract かつ final なクラス が許可されていないのは納得できる。
しかし今、次のように考えて納得できなくなりました。
abstract かつ final なクラス はインスタンスを生成して使うことができず、また継承クラスを作って使うこともできない。だが、そのクラスのクラスメソッドを使うことはできる。もし インスタンスを生成して使いたくはないし、継承クラスを作って使いたくもないが、クラスメソッドだけは使いたい ようなクラスを作りたくなったとすると abstract かつ final なクラス が必要になるのではないか。使い道があるのに、理由がなく言語仕様で許可されていないとしたら納得できない。
脱線が長くなりましたが、PHPの仕様や実装上の理由、もしくはPHPの思想、オブジェクト指向プログラミングといった観点から理由がわかるよ、という方がいらっしゃいましたらご教示いただけると喜びます。
インターフェース
インターフェースはただのインターフェースしかありません。
// ただのインターフェース
interface SomeInterface {}
abstractなインターフェース や finalなインターフェース はありません。
// abstractなインターフェースを作りたい
abstract interface SomeAbstractInterface {}
// => PHP Parse error: syntax error, unexpected 'interface' (T_INTERFACE), expecting abstract (T_ABSTRACT) or final (T_FINAL) or class (T_CLASS)
// finalなインターフェースを作りたい
final interface SomeFinalInterface {}
// => PHP Parse error: syntax error, unexpected 'interface' (T_INTERFACE), expecting abstract (T_ABSTRACT) or final (T_FINAL) or class (T_CLASS)
// abstractでfinalなインターフェースを作りたい
abstract final interface SomeAbstractFinalInterface {}
// => PHP Fatal error: Cannot use the final modifier on an abstract class
// finalでabstractなインターフェースを作りたい
final abstract interface SomeFinalAbstractInterface {}
// => PHP Fatal error: Cannot use the final modifier on an abstract class
インターフェースは何もしなくても abstract のようなものなので、abstractなインターフェース が許可されていないのは納得できます。一方、インターフェースを extend
してほしくない場合に finalなインターフェース のような需要があるのではないかと思いました。
トレイト
トレイトもただのトレイトしかありません
// ただのトレイト
trait SomeTrait {}
abstractなトレイト や finalなトレイト はありません。
// abstractなトレイトを作りたい
abstract trait SomeAbstractTrait {}
// => PHP Parse error: syntax error, unexpected 'trait' (T_TRAIT), expecting abstract (T_ABSTRACT) or final (T_FINAL) or class (T_CLASS)
// finalなトレイトを作りたい
final trait SomeFinalTrait {}
// => PHP Parse error: syntax error, unexpected 'trait' (T_TRAIT), expecting abstract (T_ABSTRACT) or final (T_FINAL) or class (T_CLASS)
// abstractでfinalなトレイトを作りたい
abstract final trait SomeAbstractFinalTrait {}
// => PHP Fatal error: Cannot use the final modifier on an abstract class
// finalでabstractトレイトを作りたい
final abstract trait SomeFinalAbstractTrait {}
// => PHP Fatal error: Cannot use the final modifier on an abstract class
トレイトは実装を定義するためのものだと思っているので、 abstractなトレイト が許可されていないのは納得できます。一方、他のクラスやトレイトで use
したときに、 use
されたトレイトの実装を上書いてほしくない場合に finalなトレイト のような需要があるのではないかと思いました。
どう定義するか
クラスプロパティ
アクセス権を3パターン試します。アクセス権を宣言しないパターンと var
を使うパターンは現在は使うべきではないと考えているので試しません。
また、クラスプロパティの宣言時に初期化するかどうかで2パターン試します。
アクセス権で3パターン x 初期化するかで2パターン = 6パターン の宣言方法を試すことになりました。
public static $public_static_property_without_initialization;
public static $public_static_property_with_initialization = null;
protected static $protected_static_property_without_initialization;
protected static $protected_static_property_with_initialization = null;
private static $private_static_property_without_initialization;
private static $private_static_property_with_initialization = null;
クラスメソッド
アクセス権を3パターン試します。アクセス権を宣言しないパターンは現在は使うべきではないと考えているので試しません。
また、本体があるかどうかで2パターン試します。
さらに、 abstract
キーワードと final
キーワードのくっつき方の組み合わせで5パターン試します。
アクセス権で3パターン x 本体があるかどうかで2パターン x 5パターン = 30パターン の宣言方法を試すことになりました。
// public x 10パターン
public static function public_static_method_without_body();
public static function public_static_method_with_body() {}
abstract public static function abstract_public_static_method_without_body();
abstract public static function abstract_public_static_method_with_body() {}
final public static function final_public_static_method_without_body();
final public static function final_public_static_method_with_body() {}
abstract final public static function abstract_final_public_static_method_without_body();
abstract final public static function abstract_final_public_static_method_with_body() {}
final abstract public static function final_abstract_public_static_method_without_body();
final abstract public static function final_abstract_public_static_method_with_body() {}
// protected x 10パターン
protected static function protected_static_method_without_body();
protected static function protected_static_method_with_body() {}
abstract protected static function abstract_protected_static_method_without_body();
abstract protected static function abstract_protected_static_method_with_body() {}
final protected static function final_protected_static_method_without_body();
final protected static function final_protected_static_method_with_body() {}
abstract final protected static function abstract_final_protected_static_method_without_body();
abstract final protected static function abstract_final_protected_static_method_with_body() {}
final abstract protected static function final_abstract_protected_static_method_without_body();
final abstract protected static function final_abstract_protected_static_method_with_body() {}
// private x 10パターン
private static function private_static_method_without_body();
private static function private_static_method_with_body() {}
abstract private static function abstract_private_static_method_without_body();
abstract private static function abstract_private_static_method_with_body() {}
final private static function final_private_static_method_without_body();
final private static function final_private_static_method_with_body() {}
abstract final private static function abstract_final_private_static_method_without_body();
abstract final private static function abstract_final_private_static_method_with_body() {}
final abstract private static function final_abstract_private_static_method_without_body();
final abstract private static function final_abstract_private_static_method_with_body() {}
実験
エラーになるものをコメントアウトして示します。
ただのクラス
class SomeClass
{
public static $public_static_property_without_initialization;
public static $public_static_property_with_initialization = null;
protected static $protected_static_property_without_initialization;
protected static $protected_static_property_with_initialization = null;
private static $private_static_property_without_initialization;
private static $private_static_property_with_initialization = null;
// public x 10パターン
// public static function public_static_method_without_body();
public static function public_static_method_with_body() {}
// abstract public static function abstract_public_static_method_without_body();
// abstract public static function abstract_public_static_method_with_body() {}
// final public static function final_public_static_method_without_body();
final public static function final_public_static_method_with_body() {}
// abstract final public static function abstract_final_public_static_method_without_body();
// abstract final public static function abstract_final_public_static_method_with_body() {}
// final abstract public static function final_abstract_public_static_method_without_body();
// final abstract public static function final_abstract_public_static_method_with_body() {}
// protected x 10パターン
// protected static function protected_static_method_without_body();
protected static function protected_static_method_with_body() {}
// abstract protected static function abstract_protected_static_method_without_body();
// abstract protected static function abstract_protected_static_method_with_body() {}
// final protected static function final_protected_static_method_without_body();
final protected static function final_protected_static_method_with_body() {}
// abstract final protected static function abstract_final_protected_static_method_without_body();
// abstract final protected static function abstract_final_protected_static_method_with_body() {}
// final abstract protected static function final_abstract_protected_static_method_without_body();
// final abstract protected static function final_abstract_protected_static_method_with_body() {}
// private x 10パターン
// private static function private_static_method_without_body();
private static function private_static_method_with_body() {}
// abstract private static function abstract_private_static_method_without_body();
// abstract private static function abstract_private_static_method_with_body() {}
// final private static function final_private_static_method_without_body();
final private static function final_private_static_method_with_body() {}
// abstract final private static function abstract_final_private_static_method_without_body();
// abstract final private static function abstract_final_private_static_method_with_body() {}
// final abstract private static function final_abstract_private_static_method_without_body();
// final abstract private static function final_abstract_private_static_method_with_body() {}
}
コメントを消したものを再掲します。
class SomeClass
{
public static $public_static_property_without_initialization;
public static $public_static_property_with_initialization = null;
protected static $protected_static_property_without_initialization;
protected static $protected_static_property_with_initialization = null;
private static $private_static_property_without_initialization;
private static $private_static_property_with_initialization = null;
public static function public_static_method_with_body() {}
final public static function final_public_static_method_with_body() {}
protected static function protected_static_method_with_body() {}
final protected static function final_protected_static_method_with_body() {}
private static function private_static_method_with_body() {}
final private static function final_private_static_method_with_body() {}
}
見慣れた光景になりましたね。
abstractクラス
abstract class SomeAbstractClass
{
public static $public_static_property_without_initialization;
public static $public_static_property_with_initialization = null;
protected static $protected_static_property_without_initialization;
protected static $protected_static_property_with_initialization = null;
private static $private_static_property_without_initialization;
private static $private_static_property_with_initialization = null;
// public x 10パターン
// public static function public_static_method_without_body();
public static function public_static_method_with_body() {}
abstract public static function abstract_public_static_method_without_body();
// abstract public static function abstract_public_static_method_with_body() {}
// final public static function final_public_static_method_without_body();
final public static function final_public_static_method_with_body() {}
// abstract final public static function abstract_final_public_static_method_without_body();
// abstract final public static function abstract_final_public_static_method_with_body() {}
// final abstract public static function final_abstract_public_static_method_without_body();
// final abstract public static function final_abstract_public_static_method_with_body() {}
// protected x 10パターン
// protected static function protected_static_method_without_body();
protected static function protected_static_method_with_body() {}
abstract protected static function abstract_protected_static_method_without_body();
// abstract protected static function abstract_protected_static_method_with_body() {}
// final protected static function final_protected_static_method_without_body();
final protected static function final_protected_static_method_with_body() {}
// abstract final protected static function abstract_final_protected_static_method_without_body();
// abstract final protected static function abstract_final_protected_static_method_with_body() {}
// final abstract protected static function final_abstract_protected_static_method_without_body();
// final abstract protected static function final_abstract_protected_static_method_with_body() {}
// private x 10パターン
// private static function private_static_method_without_body();
private static function private_static_method_with_body() {}
// abstract private static function abstract_private_static_method_without_body();
// abstract private static function abstract_private_static_method_with_body() {}
// final private static function final_private_static_method_without_body();
final private static function final_private_static_method_with_body() {}
// abstract final private static function abstract_final_private_static_method_without_body();
// abstract final private static function abstract_final_private_static_method_with_body() {}
// final abstract private static function final_abstract_private_static_method_without_body();
// final abstract private static function final_abstract_private_static_method_with_body() {}
}
コメントを消したものを再掲します。
abstract class SomeAbstractClass
{
public static $public_static_property_without_initialization;
public static $public_static_property_with_initialization = null;
protected static $protected_static_property_without_initialization;
protected static $protected_static_property_with_initialization = null;
private static $private_static_property_without_initialization;
private static $private_static_property_with_initialization = null;
public static function public_static_method_with_body() {}
abstract public static function abstract_public_static_method_without_body();
final public static function final_public_static_method_with_body() {}
protected static function protected_static_method_with_body() {}
abstract protected static function abstract_protected_static_method_without_body();
final protected static function final_protected_static_method_with_body() {}
private static function private_static_method_with_body() {}
final private static function final_private_static_method_with_body() {}
}
クラスがabstractになったので abstract public static function
と abstract protected static function
が許可されるようになりました。 abstract private static function
は意味わかんないので相変わらず許可されません。
finalクラス
final class SomeFinalClass
{
public static $public_static_property_without_initialization;
public static $public_static_property_with_initialization = null;
protected static $protected_static_property_without_initialization;
protected static $protected_static_property_with_initialization = null;
private static $private_static_property_without_initialization;
private static $private_static_property_with_initialization = null;
// public x 10パターン
// public static function public_static_method_without_body();
public static function public_static_method_with_body() {}
// abstract public static function abstract_public_static_method_without_body();
// abstract public static function abstract_public_static_method_with_body() {}
// final public static function final_public_static_method_without_body();
final public static function final_public_static_method_with_body() {}
// abstract final public static function abstract_final_public_static_method_without_body();
// abstract final public static function abstract_final_public_static_method_with_body() {}
// final abstract public static function final_abstract_public_static_method_without_body();
// final abstract public static function final_abstract_public_static_method_with_body() {}
// protected x 10パターン
// protected static function protected_static_method_without_body();
protected static function protected_static_method_with_body() {}
// abstract protected static function abstract_protected_static_method_without_body();
// abstract protected static function abstract_protected_static_method_with_body() {}
// final protected static function final_protected_static_method_without_body();
final protected static function final_protected_static_method_with_body() {}
// abstract final protected static function abstract_final_protected_static_method_without_body();
// abstract final protected static function abstract_final_protected_static_method_with_body() {}
// final abstract protected static function final_abstract_protected_static_method_without_body();
// final abstract protected static function final_abstract_protected_static_method_with_body() {}
// private x 10パターン
// private static function private_static_method_without_body();
private static function private_static_method_with_body() {}
// abstract private static function abstract_private_static_method_without_body();
// abstract private static function abstract_private_static_method_with_body() {}
// final private static function final_private_static_method_without_body();
final private static function final_private_static_method_with_body() {}
// abstract final private static function abstract_final_private_static_method_without_body();
// abstract final private static function abstract_final_private_static_method_with_body() {}
// final abstract private static function final_abstract_private_static_method_without_body();
// final abstract private static function final_abstract_private_static_method_with_body() {}
}
コメントを消したものを再掲します。
final class SomeFinalClass
{
public static $public_static_property_without_initialization;
public static $public_static_property_with_initialization = null;
protected static $protected_static_property_without_initialization;
protected static $protected_static_property_with_initialization = null;
private static $private_static_property_without_initialization;
private static $private_static_property_with_initialization = null;
public static function public_static_method_with_body() {}
final public static function final_public_static_method_with_body() {}
protected static function protected_static_method_with_body() {}
final protected static function final_protected_static_method_with_body() {}
private static function private_static_method_with_body() {}
final private static function final_private_static_method_with_body() {}
}
ただのクラスの場合と同じですね。
ただのインターフェース
interface SomeInterface
{
// public static $public_static_property_without_initialization;
// public static $public_static_property_with_initialization = null;
// protected static $protected_static_property_without_initialization;
// protected static $protected_static_property_with_initialization = null;
// private static $private_static_property_without_initialization;
// private static $private_static_property_with_initialization = null;
// public x 10パターン
public static function public_static_method_without_body();
// public static function public_static_method_with_body() {}
// abstract public static function abstract_public_static_method_without_body();
// abstract public static function abstract_public_static_method_with_body() {}
// final public static function final_public_static_method_without_body();
// final public static function final_public_static_method_with_body() {}
// abstract final public static function abstract_final_public_static_method_without_body();
// abstract final public static function abstract_final_public_static_method_with_body() {}
// final abstract public static function final_abstract_public_static_method_without_body();
// final abstract public static function final_abstract_public_static_method_with_body() {}
// protected x 10パターン
// protected static function protected_static_method_without_body();
// protected static function protected_static_method_with_body() {}
// abstract protected static function abstract_protected_static_method_without_body();
// abstract protected static function abstract_protected_static_method_with_body() {}
// final protected static function final_protected_static_method_without_body();
// final protected static function final_protected_static_method_with_body() {}
// abstract final protected static function abstract_final_protected_static_method_without_body();
// abstract final protected static function abstract_final_protected_static_method_with_body() {}
// final abstract protected static function final_abstract_protected_static_method_without_body();
// final abstract protected static function final_abstract_protected_static_method_with_body() {}
// private x 10パターン
// private static function private_static_method_without_body();
// private static function private_static_method_with_body() {}
// abstract private static function abstract_private_static_method_without_body();
// abstract private static function abstract_private_static_method_with_body() {}
// final private static function final_private_static_method_without_body();
// final private static function final_private_static_method_with_body() {}
// abstract final private static function abstract_final_private_static_method_without_body();
// abstract final private static function abstract_final_private_static_method_with_body() {}
// final abstract private static function final_abstract_private_static_method_without_body();
// final abstract private static function final_abstract_private_static_method_with_body() {}
}
コメントを消したものを再掲します。
interface SomeInterface
{
public static function public_static_method_without_body();
}
1つしか残りませんでした。
ただのトレイト
trait SomeTrait
{
public static $public_static_property_without_initialization;
public static $public_static_property_with_initialization = null;
protected static $protected_static_property_without_initialization;
protected static $protected_static_property_with_initialization = null;
private static $private_static_property_without_initialization;
private static $private_static_property_with_initialization = null;
// public x 10パターン
// public static function public_static_method_without_body();
public static function public_static_method_with_body() {}
abstract public static function abstract_public_static_method_without_body();
// abstract public static function abstract_public_static_method_with_body() {}
// final public static function final_public_static_method_without_body();
final public static function final_public_static_method_with_body() {}
// abstract final public static function abstract_final_public_static_method_without_body();
// abstract final public static function abstract_final_public_static_method_with_body() {}
// final abstract public static function final_abstract_public_static_method_without_body();
// final abstract public static function final_abstract_public_static_method_with_body() {}
// protected x 10パターン
// protected static function protected_static_method_without_body();
protected static function protected_static_method_with_body() {}
abstract protected static function abstract_protected_static_method_without_body();
// abstract protected static function abstract_protected_static_method_with_body() {}
// final protected static function final_protected_static_method_without_body();
final protected static function final_protected_static_method_with_body() {}
// abstract final protected static function abstract_final_protected_static_method_without_body();
// abstract final protected static function abstract_final_protected_static_method_with_body() {}
// final abstract protected static function final_abstract_protected_static_method_without_body();
// final abstract protected static function final_abstract_protected_static_method_with_body() {}
// private x 10パターン
// private static function private_static_method_without_body();
private static function private_static_method_with_body() {}
// abstract private static function abstract_private_static_method_without_body();
// abstract private static function abstract_private_static_method_with_body() {}
// final private static function final_private_static_method_without_body();
final private static function final_private_static_method_with_body() {}
// abstract final private static function abstract_final_private_static_method_without_body();
// abstract final private static function abstract_final_private_static_method_with_body() {}
// final abstract private static function final_abstract_private_static_method_without_body();
// final abstract private static function final_abstract_private_static_method_with_body() {}
}
コメントを消したものを再掲します。
trait SomeTrait
{
public static $public_static_property_without_initialization;
public static $public_static_property_with_initialization = null;
protected static $protected_static_property_without_initialization;
protected static $protected_static_property_with_initialization = null;
private static $private_static_property_without_initialization;
private static $private_static_property_with_initialization = null;
public static function public_static_method_with_body() {}
abstract public static function abstract_public_static_method_without_body();
final public static function final_public_static_method_with_body() {}
protected static function protected_static_method_with_body() {}
abstract protected static function abstract_protected_static_method_without_body();
final protected static function final_protected_static_method_with_body() {}
private static function private_static_method_with_body() {}
final private static function final_private_static_method_with_body() {}
}
abstractクラスと同じになるんですか!?