「PHP/Webアプリケーション向けの機能」の版間の差分

削除された内容 追加された内容
Ef3 (トーク | 投稿記録)
→‎match: && 演算子を使って書き換え。
タグ: 2017年版ソースエディター
Ef3 (トーク | 投稿記録)
→‎列挙型: 詳細は「PHP/クラス#Enum」を参照
タグ: 2017年版ソースエディター
408 行
 
== 列挙型 ==
{{Main|PHP/クラス#Enum}}
=== 概要 ===
PHP8.1から列挙型(enum)が使えます。
 
enum とは何かの説明が難しいのでコードと実行例を先に示します。
 
;例
:<syntaxhighlight lang=php>
<?php
 
enum gameMode
{
case map;
case menu;
case battle;
}
 
 
$a = gameMode::map ;
$b = gameMode::menu ; // このサンプルコードでは$bは以降は未使用。参考のため書いている。
 
if ($a == gameMode::map ){ echo "今マップ画面を操作中です。" , PHP_EOL ; }
if ($a == gameMode::menu ){ echo "今メニュー画面を操作中です。" , PHP_EOL ; }
 
?>
</syntaxhighlight>
 
 
;実行結果
<pre>
今マップ画面を操作中です。
</pre>
 
PHP以外のどの言語でも、enumは条件分岐以外にも使えますが、条件分岐の応用が比較的に有名です。
 
なお、C言語などのよその言語ではswitch文のラベル番号の省略などの応用にenumを使うのが有名なのですが、しかしPHPでは現状、整数の自動的な割り当ての機能の仕様が不透明なので、switch文への応用の可能性も不明です。
 
そもそも何のためにenumを使うのか、enumのどこが便利かというと、enumを使わずに代わりに場合分けの各条件に対応する数値で場合分けをすると、どの数値が、どの条件だったかを、第三者が確認するのが面倒になります。(こういう、プログラムした本人でないと分からない番号のことを「マジックナンバー」と言います。コメントなどで意図を説明する方法もありますが、どちらにせよ手間です。)
 
また、気が変わって条件の順番を書き換えるとき、もしenumが無いと、順番の入れ替えのたびに番号を振り直しになるので、とても面倒です。
 
PHPのenumの場合、特に数値を割り当てしないかぎりは、そもそもenum定数に数値が格納されません。より確実にマジックナンバー問題が防げます。
 
なお、おおむね用語として、
:<syntaxhighlight lang=php>
enum 列挙体名
{
case 列挙定数名1;
case 列挙定数名2;
case 列挙定数名3;
}
</syntaxhighlight>
 
のような呼び方をします。「列挙定数」という代わりに「列挙子」と言う場合もあります。
 
「列挙体名」という代わりに「列挙型名」という場合もあります。
 
 
;ネストは不明
なお、enumの中にenumを入れるという、enumの入れ子(ネスト)については、現状のところは動作例は不明ですし仕様も不明です。おそらくenumのネストについては非サポートです。そもそもC言語自体に現状、enumのネストがない状態です(2021年に記述)。
 
 
=== enum型と変数の同時宣言の実装状況 ===
enum を宣言する際、形式的にはenum型の変数を下記コードのように同時に宣言できますが、果たして本当にenum型変数として認識しているかは不明です(enum の宣言とは独立して、単に独立の「$a;」という命令文だと認識している可能性がある。クラス class を転用して enum を実装しているようだが(エラー文などで "class" を見かけるので)、そもそもPHPのクラスの宣言では、クラスの型とアクセス用変数とは同時宣言できない(インスタンス化が必要なので) )。しかも2個以上のアクセス用変数の宣言には対応しておらず1個しか宣言できません。
 
 
;例
:<syntaxhighlight lang=php>
<?php
 
enum gameMode
{
case map;
case menu;
case battle;
} $a;
// 最後の行では $a,$b; と2個以上で宣言するとコンパイルエラー
 
$a = gameMode::battle ;
$b = gameMode::menu ; // このサンプルコードでは$bは以降は未使用。参考のため書いている。
 
if ($a == gameMode::battle ){ echo "今、戦闘画面を操作中です。" , PHP_EOL ; }
if ($a == gameMode::menu ){ echo "今メニュー画面を操作中です。" , PHP_EOL ; }
 
?>
</syntaxhighlight>
 
 
;実行結果
<pre>
今、戦闘画面を操作中です。
</pre>
 
なお、PHP7以降の近年のPHPは、限定的ですが型の機能を導入することで安全な動作を保障できるように開発していく方針になっています。関数など一部の機能では、最新版のPHPでは、すでに型が使えます。なのでもしかたらenumについても型が使えるようになるのかもしれない可能性もありますが、現状ではまだ可能性です。enumの型を使えない可能性も充分にありますので、早合点しないようにしましょう。
 
 
そのほか、PHPのenumはクラスを流用して構築されているものの、コンストラクタやデストラクタなどクラスの要素の一部は定義できない仕様であり、継承も不可能です。[https://www.php.net/manual/ja/language.enumerations.object-differences.php 『オブジェクトとの違い ¶』 ]2022年6月24日に確認.
 
=== スカラ値つきのenum ===
enum に整数型(int)または文字列型(string)の値をつけることが可能です。
 
enumの要素に関連づけられた数値にアクセスする場合、下記コードのように <code>->value</code> が必要です。これは整数型でも文字列型でも、ともに <code>->value</code> です。なお int も string もつけず <code>->value</code>でアクセスするのは非推奨であり、警告されます。(<code>Warning: Undefined property:</code> などの文字列。またintを宣言せずにenumを使用した場合に各要素の代入される数値は現状、0,1,2,3・・・'''ではない'''です。なので、値つき宣言して無い場合には、代入値の順番や大小などは一切当てにしないのが安全でしょう。)
 
 
そのほか、enum要素名(下記コード例でなら"buki"や"tate"の部分のこと)にアクセスする場合には <code>->name</code> になります。現状、<code>->name</code> プロパティはint宣言やstring宣言とは無関係に使えます。
 
つまり valueというプロパティは、enumをintまたはstringに関連づけて宣言しおえた段階までに自動で生成されます。よって、プログラマー側でvalueプロパティを宣言する必要はありません。nameプロパティも宣言の必要は無いです。
 
また、<code>value</code>以外のデタラメな文字列(たとえば「vabqe」)とかのプロパティを書いてもエラーになります。
このように、あらかじめ value および name というプロパティがenumでは決まっています。
 
;例
:<syntaxhighlight lang=php>
<?php
// 装備画面
enum soubiMode: int // : int を忘れないように
{
case buki = 1; // 武器
case tate = 2 ; // 盾
case kabuto = 3 ; // かぶと
}
 
 
$a = soubiMode::tate->value ;
 
if ($a == 2 ){
echo "{$a}番データベースを編集。" , PHP_EOL ;
}
if ($a == 3 ){
echo "こっちには来てない。" , PHP_EOL ;
}
 
?>
</syntaxhighlight>
 
 
 
;実行結果
<pre>
2番データベースを編集。
</pre>
 
なお、C言語やPythonなど他の一部の言語では自動的にスカラー値を付ける機能があるのですが、しかしPHPでは現状ではまだそのような機能や仕様は知られていません(2021年に本文を記述)。なので、上記コードで「case buki = 1」の「=1」を消すとエラーになります。
 
なおC言語ではこのような機能があるので、switch文のラベルの番号付けの省略としてenumを応用する例が有名です。
 
さて、enumで定義した整数値を使って計算したい場合、下記のように<code>->value</code>プロパティで代入値にアクセスする必要があります。
 
;例
:<syntaxhighlight lang=php>
<?php
// 装備画面
enum soubiMode: int // : int を忘れないように
{
case buki = 5; // 武器
case tate = 6 ; // 盾
case kabuto = 7 ; // かぶと
}
 
$a = soubiMode::kabuto->value - 4 ; // 7-4 = 3
 
// 現状では if 文で事前にenum格納した変数を呼び出さないとエラーになる。
if ($a == 3 ){
echo "{$a}番データベースを編集。" , PHP_EOL ;
}
if ($a == 4 ){
echo "こっちには来てない。" , PHP_EOL ;
}
 
?>
</syntaxhighlight>
;実行結果
<pre>
3番データベースを編集。
</pre>
 
 
説明の都合上、上記ではif文を書いてからブロック中でprintを使いましたが、別に事前に if文が無くてもprinttでenum変数のvalue値は表示できます。
 
:<syntaxhighlight lang=php>
<?php
// 装備画面
enum soubiMode: int // : int を忘れないように
{
case buki = 1; // 武器
case tate = 2 ; // 盾
case kabuto = 3 ; // かぶと
}
 
echo "valueごと定義\n");
$a = soubiMode::tate->value ;
echo $a);
echo "\n");
 
echo "print側でvalue指定\n");
$b = soubiMode::buki ;
echo $b->value);
echo "\n");
 
?>
</syntaxhighlight>
 
 
;実行結果
<pre>
valueごと定義
2
print側でvalue指定
1
</pre>
 
=== enum と match 式 ===
match 式の変数と条件式には、下記のように enum変数を入れることも出来ます。このため、下記のように書けます。
 
:<syntaxhighlight lang=php>
<?php
// 装備画面
enum soubiMode
{
case buki ; // 武器
case tate ; // 盾
case kabuto ; // かぶと
}
 
 
$a = soubiMode::buki ; // 区別のため buki に変更
 
match ($a) {
soubiMode::buki, soubiMode::tate => echo "{$a->name}データベースを編集。" , PHP_EOL,
soubiMode::kabuto => echo "カブトのデータベースは未作成。" , PHP_EOL,
default => echo "宣言されてない装備にアクセスしました。" , PHP_EOL ,
 
};
 
?>
</syntaxhighlight>
 
;実行結果
<pre>
bukiデータベースを編集。
</pre>
 
 
その他、matchの各条件(「soubiMode::kabuto」の部分)をもっと短く書きたい場合、下記コードのように、enum要素名をつかって条件分岐する方法もあります。ただし、文字列は内部的なデータ数が長くなるので、処理速度に多少の欠点が考えられる。よって可能なら、なるべく上述のようなenum変数を直接に条件にするコードのほうが良いだろう。
 
:<syntaxhighlight lang=php>
<?php
// 装備画面
enum soubiMode
{
case buki ; // 武器
case tate ; // 盾
case kabuto ; // かぶと
}
 
$a = soubiMode::tate ;
 
match ($a->name) {
"buki", "tate" => echo "{$a->name}データベースを編集。" , PHP_EOL,
"kabuto" => echo "カブトのデータベースは未作成。" , PHP_EOL,
default => echo "宣言されてない装備にアクセスしました。" , PHP_EOL ,
 
};
 
?>
</syntaxhighlight>
 
;実行結果
<pre>
tateデータベースを編集。
</pre>
 
=== enum の関数 ===
enum は関数の引数にすることもできる。(関数について詳しくは[[PHP/入門/関数とは]]で扱う。)
 
:<syntaxhighlight lang=php>
<?php
// 装備画面
enum soubiMode: int // : int を忘れないように
{
case buki = 1; // 武器
case tate = 2 ; // 盾
case kabuto = 3 ; // かぶと
}
 
// $a = soubiMode::tate->value ;
 
echo "enum関数実験" , PHP_EOL ;
function f($mode){
print "$mode->name \n";
print "$mode->value \n";
}
 
f(soubiMode::tate);
f(soubiMode::kabuto);
 
?>
</syntaxhighlight>
 
 
;実行結果
<pre>
enum関数実験
tate
2
kabuto
3
</pre>
 
 
なお、引用符中の <code>$mode->value</code> は、下記のように <code>{$mode->value}</code> と書いてもいい(2022年6月のPHP8.1の時点)。
 
:<syntaxhighlight lang=php>
<?php
// 装備画面
enum soubiMode: int // : int を忘れないように
{
case buki = 1; // 武器
case tate = 2 ; // 盾
case kabuto = 3 ; // かぶと
}
 
// $a = soubiMode::tate->value ;
 
echo "enum関数実験" , PHP_EOL ;
function f($mode){
print "{$mode->name} \n";
print "{$mode->value} \n";
}
 
f(soubiMode::tate);
f(soubiMode::kabuto);
 
?>
</syntaxhighlight>
 
 
なお、もし、たまたま文字列 "$mode->value" を表示したい場合は、単にエスケープシーケンス「\」を使って<code>print "\$mode->value \n";</code>とすれば済む。
 
 
;余談
実は2021年にPHP本体にenumの機能が使われるよりもずっと前から、
コミュニティ外部のライブラリである Laravel や Symphony などの「フレームワーク」に前々からenumが存在していました(ただし書式は違っていたが)。
 
つまり結果的には、フレームワークで有用性の実証された機能が、2021年にPHP本体にも取り入れられたことになります。
 
 
 
PHP8では関数の引数に型をつけることができるが、下記のように引数としてenum変数を使う際には型の代わりにenumグループ名をつけることもできる。
 
:<syntaxhighlight lang=php>
<?php
// 装備画面
enum soubiMode // : int を忘れないように
{
case buki ; // 武器
case tate ; // 盾
case kabuto ; // かぶと
}
 
function f(soubiMode $mode){ // 型の部分がenumグループ名
print "{$mode->name} \n";
}
 
f(soubiMode::tate);
f(soubiMode::kabuto);
 
?>
</syntaxhighlight>
 
;実行結果
<pre>
tate
kabuto
</pre>
 
[[カテゴリ:PHP]]