制御構造とは、プログラムの実行順序を変更するための構造のことを指します。PHPには、様々な制御構造が用意されており、プログラムを効率的に制御することができます。
この章では、制御構造の中でも代表的な「条件分岐」と「繰り返し」について解説します。
条件分岐には「if文」「switch文」「match文」があり、繰り返しには「for文」「while文」「do-while文」があります。
それぞれの文法や使い方を理解し、プログラムの制御に活用していきましょう。
__TOC__
条件分岐は、プログラムにとって非常に重要な要素であり、実行する命令文の流れを切り替えるために使用されます。
条件分岐を使用することで、特定の条件が満たされた場合にのみ、特定の命令文が実行されるようにすることができます。
PHPには、if, switch, match の３つの分岐構文があります。
以下に、if文、switch文、match式の利点と欠点を表にまとめました。
それぞれの構文には利点と欠点があり、使用する際にはその目的に応じた最適な構文を選択する必要があります。
if文は、複雑な条件分岐をカバーすることができますが、ネストが深くなりやすいため見づらくなることがあります。
switch文は、複数の分岐を一つの構文で書けるため、見やすいという利点がありますが、比較演算子が単純な場合にしか使えず、フォールスルーに注意が必要です。
match式は、値に基づいて分岐でき、見やすく分かりやすいという利点がありますが、正規表現を使う場合には不向きです。
PHPには、C風のifがあります。else ifの短縮構文があるほか、テキストに埋込むのに適した代替構文があります。
[https://paiza.io/projects/nkPCU7ofu6F1MVSsMZQvEg?language=perl 例]:
 0) {
    echo '$n > 0', PHP_EOL;
} elseif ($n == 0) {
    echo '$n < 0', PHP_EOL;
} else {
    echo $n, PHP_EOL;
}
echo PHP_EOL;
// 代替構文
if ($n < 0):
    echo '$n < 0', PHP_EOL;
elseif ($n > 0):
    echo '$n > 0', PHP_EOL;
elseif ($n == 0):
    echo '$n < 0', PHP_EOL;
else:
    echo $n, PHP_EOL;
endif;
?>
 0): ?>
基本構文:
if ( 条件１ ) 文１ else if ( 条件式２ ) 文２ elseif ( 条件式３ ) 文３ … else 文
else ifはelse ifのシノニムでそれぞれ０回以上繰り返せる。
else 文は省略可能。
代替構文:
if ( 条件式１ ):
    文１
elseif ( 条件式２ ):
    文２
    ︙
else:
    文
endif;
埋込構文:
テキスト１
テキスト２
テキスト３
        ︙
テキストｎ
テキスト
PHPには、C風のswitchがあり、フォールスルーの挙動などほとんど同じですが、case の式に定数式以外の式が使える他、文字列などの比較も「緩やかな比較」で行えます。
[https://paiza.io/projects/5pSKdJt_kRFiA7GMULV_6g?language=php 例]:
実行結果:
C風のswitchで、もしbreakがないと次のcase以降も実行します。
基本構文:
switch ( 式 ) {
  case 式１ : 文１
  case 式２ : 文２
      ︙
  case 式ｎ : 文ｎ
  default   : 文
}
代替構文:
switch ( 式 ) :
  case 式１ : 文１
  case 式２ : 文２
      ︙
  case 式ｎ : 文ｎ
  default   : 文
endswitch ;
埋込構文:
テキスト１
テキスト２
        ︙
テキストｎ
テキスト
PHPのmatchは、switchと似ていますが、文ではなく式なので値が取れるほか、比較には「厳密な比較」が使われます。
matchには、代替構文も埋込み構文もありません。
[https://paiza.io/projects/hNIrTpNJmW_EozfBVP4HJQ?language=php 例]:
 "KW_abstract",
    "and" => "KW_and",
    "as" => "KW_as",
    default => "Unknown",
},
    PHP_EOL;
?>
 "KW_abstract",
    "and" => "KW_and",
    "as" => "KW_as",
][$s] ?? "Unknown",
    PHP_EOL;
?>
と書いたほうがより簡素だと考えるひともいるでしょう。
また、=>の右には「式」が要求されるので、echoのようなコマンドは使えません。
 "乳児",
        $age < 6 => "幼児",
        $age < 18 => "少年",
        default => "成人",
    },
    "です。",
    PHP_EOL;
?>
こんな事もできます。
基本構文:
代替構文:（ありません）
埋込構文:（ありません）
分岐処理を多重にネストすると、可読性が低下し、コードが複雑になります。また、ネストされた分岐により、バグを見つけることがより困難になる場合があります。
そのため、分岐処理をシンプルに保つために、以下の点に注意することが重要です。
ネストを減らす
ネストを深くすると、可読性が低下するため、できる限り浅くするように心がけます。
分岐処理を多重に行わないようにすることで、コードの複雑性を減らすことができます。
論理演算子を活用する
複雑な条件式を書く場合は、論理演算子を使うことで、簡潔に書くことができます。
ガード節を利用する
条件に合わない場合は、その時点で処理を中断する「ガード節」を利用することで、コードの見通しをよくすることができます。
分岐処理の抽象化
分岐処理が複雑になる場合は、別の関数やクラスに分岐処理をまとめることで、コードの可読性を向上させることができます。
以上のようなポイントに注意することで、分岐処理をシンプルに保つことができます。
ガード節は、プログラムにおいて条件式が満たされない場合に、関数の実行を早期に終了させる方法です。通常、条件式が満たされなかった場合には関数内で複数のif文が使われている場合がありますが、ガード節を使うことで、コードを簡潔に保ち、可読性を高めることができます。
例えば、次のような関数があったとします。
function calcPrice($price, $tax) {
  if (!is_numeric($price)) {
    return 0;
  }
  if (!is_numeric($tax)) {
    return 0;
  }
  $result = $price * (1 + $tax);
  return $result;
}
この関数では、引数が数値でない場合に、0を返しています。しかし、この関数は、引数が数値でない場合はすぐに処理を終了させる必要があります。この場合、ガード節を使うことで、以下のように書き換えることができます。
function calcPrice($price, $tax) {
  if (!is_numeric($price) || !is_numeric($tax)) {
    return 0;
  }
  $result = $price * (1 + $tax);
  return $result;
}
このように、ガード節を使うことで、コードの可読性を高めることができます。
省略表記や三項演算子を使用すると、コードの可読性を向上させることができます。以下は、その書き方の例です。
【省略表記】
// 通常のif文
if ($condition) {
  $result = 'true';
} else {
  $result = 'false';
}
// 三項演算子
$result = ($condition) ? 'true' : 'false';
 
【null合体演算子】 null合体演算子??を使用することで、変数のnullチェックを簡潔に書くことができます。
// 通常のif文
if (isset($name)) {
  $userName = $name;
} else {
  $userName = 'Guest';
}
// null合体演算子
$userName = $name ?? 'Guest';
 
【switch文の省略表記】 switch文の省略表記を使用することで、複数の条件に対して同じ処理を行うことができます。
// 通常のswitch文
switch ($value) {
  case 1:
    $result = 'A';
    break;
  case 2:
    $result = 'B';
    break;
  case 3:
  case 4:
  case 5:
    $result = 'C';
    break;
  default:
    $result = 'D';
    break;
}
// 省略表記
switch ($value):
  case 1: $result = 'A'; break;
  case 2: $result = 'B'; break;
  case 3:
  case 4:
  case 5: $result = 'C'; break;
  default: $result = 'D'; break;
endswitch;
 
ただし、省略表記や三項演算子を多用しすぎると可読性が低下するため、適切な場面で使用するようにしましょう。また、複雑な条件分岐には適していないため、その場合は通常のif文やswitch文を使用することが望ましいです。
if や while なでの条件式で、使われる比較演算子には２種類あります。
==:型を厳格には区別しない「緩やかな比較」
===:型を厳格に区別する「厳密な比較」
なお、=等号1個は単なる代入命令ですが条件式で使われると代入された値を真理値として評価されます。
==は、型を厳格には区別しない「緩やかな比較」演算子です。
実行結果
整数：
int(1)
int(1)
浮動小数点数：
int(1)
float(1)
文字列：
int(1) 
string(1) "1"
すべて一致します。
===は、型まで一致していることを要求する「厳密な比較」演算子です。
実行結果
整数：
int(1) 
int(1)
型の一致は、数値であるだけでは不十分で、整数か浮動小数点数かは区別しています。
!==は型まで一致していない、あるいは値が一致していないと真をかえす演算子です。
繰り返しは、同じ処理を複数回繰り返すことができる制御構造の一つです。ループとも呼ばれ、同じ処理を繰り返し実行することで、膨大な量の処理を自動化することができます。繰り返し処理は、プログラミングにおいて非常に重要な役割を果たしており、Webアプリケーションの開発でも頻繁に利用されます。
ここでは、PHPで繰り返し処理を行うための方法を学びます。まずは、基本的な「for文」、「while文」、「do-while文」の使い方を解説します。
次に、より高度な繰り返し処理に必要な概念である「配列」や「連想配列」、そして「foreach文」の使い方についても学びます。
PHPには、while, do-while, for, foreach の４つの繰り返し文と、break, continue の２つの繰り返し制御文があります。
また、goto 演算子はありますが、ループからの大域脱出はbreak, continue の引数（オプション；ディフォルトは 1）でループのレベルを変えるなど、goto が使われがちな状況に代替手段を提供しているので、ほかのプログラミング言語ほどは goto の出番はありません。
加えて、PHPの繰り返し文や複文は、スコープを持ちません。ループを抜けてもループ変数は「生きている」ので、ループ変数は参照可能です。
whileは、まず条件式を評価し、真なら続く文（単文あるいは複文）を実行し条件式を再び評価します。もし、偽ならばループの次に移ります。
[https://paiza.io/projects/wKZ1iYmSF1snIyaXvt3L0g?language=php whileの例]:
実行結果:
$i = 1
$i = 2
$i = 6
$i = 42
Done!(1806)
$j = 1
$j = 2
$j = 6
$j = 42
Done!(1806)
$j = 1
$j = 2
$j = 6
$j = 42
Done!(1806)
基本構文:
while ( 条件式 ) 文
while では、条件式を省略できません。
代替構文:
while ( 条件式 ) :
  文
endwhile ;
埋込構文:
テキスト
do-whileは、文（単文あるいは複文）を実行し条件式を評価します。真なら文を再び評価します。もし、偽ならばループの次に移ります。
[https://paiza.io/projects/gf0BFOd3LIjhl8Q27HN8Sg?language=php do-whileの例]:
実行結果:
$i = 1
Done!(2)
基本構文:
do 文 while ( 条件式 ) ;
代替構文:（ありません）
埋込構文:（ありません）
forは、C風の３つの項を持つfor文です。
whileの例と同じロジックを for(;;) で書き直しました（結果は同じなので省略します）。
forの例:
基本構文:
for ( 初期化文 ; 条件式 : 繰り返し文 ) 文
forの最初に一回だけ実行されます。
条件式
真なら文を実行します。
省略されると永久ループになります。
繰り返し文
文を実行ししたあとに実行されます。
文
単文あるいは複文あるいは他の制御構造。
代替構文:
for ( 初期化文 ; 条件式 : 繰り返し文 ) :
  文
endfor ;
埋込構文:
テキスト
foreachは、配列に代表される「繰り返し可能オブジェクト」(iterable object)を繰り返しします。
foreachには、配列版と連想配列版があります。
[https://paiza.io/projects/JJx-4z9VbnvJGcKboyavGQ?language=php froeachの例]:
 "Spring",
    "夏" => "Summer",
    "秋" => "Fall / Autumn",
    "冬" => "Winter",
];
foreach ($seasonsMap as $ja => $en) {
    echo "基本: $ja --> $en", PHP_EOL;
}
echo PHP_EOL;
while ($ja = key($seasonsMap)) {
    $en = $seasonsMap[$ja];
    echo "等価: $ja --> $en", PHP_EOL;
    next($seasonsMap);
}
echo PHP_EOL;
foreach ($seasonsMap as $ja => $en):
    echo "代替: $ja --> $en", PHP_EOL;
endforeach;
?>
 $en) : ?>
 $en" ?> 
実行結果:
基本: 春
基本: 夏
基本: 秋
基本: 冬
等価: 春
等価: 夏
等価: 秋
等価: 冬
代替: 春
代替: 夏
代替: 秋
代替: 冬
埋込: 春 
埋込: 夏 
埋込: 秋 
埋込: 冬 
基本: 春 --> Spring
基本: 夏 --> Summer
基本: 秋 --> Fall / Autumn
基本: 冬 --> Winter
等価: 春 => Spring
等価: 夏 => Summer
等価: 秋 => Fall / Autumn
等価: 冬 => Winter
代替: 春 --> Spring
代替: 夏 --> Summer
代替: 秋 --> Fall / Autumn
代替: 冬 --> Winter
埋込: 春 --> Spring 
埋込: 夏 --> Summer 
埋込: 秋 --> Fall / Autumn 
埋込: 冬 --> Winter 
基本構文（配列版）:
foreach ( 繰り返し可能オブジェクト as $ループ変数 ) 文
代替構文（配列版）:
foreach ( 繰り返し可能オブジェクト as $ループ変数 ) :
  文
endwhile ;
埋込構文（配列版）:
テキスト
基本構文（連想配列版）:
foreach ( 繰り返し可能オブジェクト as $キー変数 => $値変数 ) 文
代替構文（連想配列版）:
foreach ( 繰り返し可能オブジェクト as $キー変数 => $値変数 ) :
  文
endwhile ;
埋込構文（連想配列版）:
 $値変数 ) : ?>
テキスト
key()/next() で「while による等価なコード」を書きましたが、foreach()は内部繰り返し子を変更しません。
ループ変数を、参照にすることで配列の要素を変更できます。
[https://paiza.io/projects/QMKf9FKRXTOvRQgCk9Oe_A?language=php ループ変数を参照にしたforeach()の例]:
実行結果:
array (
  0 => 1,
  1 => 2,
  2 => 3,
  3 => 4,
)
array (
  0 => 2,
  1 => 4,
  2 => 6,
  3 => 8,
)
array (
  0 => 2,
  1 => 4,
  2 => 6,
  3 => 0,
)
このコードでは、まず配列 $ary を定義しています。その後、foreachループを使って配列 $ary の要素を 2 倍に更新し、変数 $value に参照をとることで配列の値が更新されるようにしています。つまり、foreachループが終了した時点で、配列 $ary の中身は、すべて 2 倍になっています。
その後、$value 変数に 0 を代入していますが、foreachループ内で参照していたのは $ary 配列の要素の参照であり、変数 $value 自体は配列要素への参照ではありません。そのため、$value に 0 を代入しても、配列 $ary の最後の要素だけが 0 に更新されることになります。
このように、ループ変数は、foreach()を抜けても参照は生きているので、代入するとループの外からも配列を壊すことができてしまいます。
 unset($value)のように参照関係を明示的に破壊する必要があります。
break文は、ループ内で使用され、ループ処理を中断するために使用されます。break文はif文内に配置されることができます。ループ内でbreak文が実行されると、ループは即座に終了し、次の処理に進みます。
以下は、ループ処理中にbreak文を使用する例です。
";
}
?>
この例では、1から10までの数字を表示しますが、6の前に処理が停止します。
continue文は、ループ内で使用され、現在のループの反復をスキップして、次の反復に進むために使用されます。
以下は、ループ処理中にcontinue文を使用する例です。
";
}
?>
この例では、1から10までの数字を表示しますが、6だけをスキップします。
goto文は、指定されたラベルにジャンプするために使用されます。goto文は、ループ、スイッチ、または関数の中で使用されます。ただし、goto文を使用することは、プログラムの可読性を損なうことがあり、使用は推奨されません。
以下は、goto文を使用する例です。
この例では、goto文が使用され、aというラベルにジャンプします。これにより、2番目のecho文が表示されず、3番目のecho文だけが表示されます。
例外処理（れいがいしょり、''exception handling''）とは、プログラム実行中に予期せぬ異常が発生した場合に、通常の実行フローを中断し、エラーメッセージの表示や適切な処理を行うプログラムの手法です。
PHPでの例外処理は、try, catch, throw, finally キーワードを使って行われます。例外が発生する可能性のあるコードを try ブロック内に記述し、それに対する例外処理を catch ブロックで行います。
以下は基本的な例です:
try {
    // 例外が発生する可能性のあるコード
    // 例外を発生させる場合は、throwキーワードを使って例外オブジェクトを投げることができます
    if ($somethingUnexpected) {
        throw new Exception("Something unexpected occurred!");
    }
    // 例外が発生しなかった場合の処理
} catch (Exception $e) {
    // 例外が発生した場合の処理
    echo "Caught exception: " . $e->getMessage();
} finally {
    // finallyブロックは例外の有無に関わらず、最終的なクリーンアップを行うためのブロックです
    // このブロックは省略可能です
    echo "Finally block";
}
 
上記の例では、try ブロック内で例外が発生する可能性があります。もし例外が発生した場合、catch ブロックが実行されて例外オブジェクトが $e としてキャッチされ、その後の処理が実行されます。finally ブロックは例外の有無にかかわらず、最終的な処理を実行するために使用されます。
以下はゼロ除算の例外処理を含んだPHPコードです。
[https://paiza.io/projects/jgl-WuB6hmFyeoaAnLL1vw?language=php コード例]:
getMessage(), PHP_EOL;
    } finally {
        echo " Finally block", PHP_EOL;
    }
}
// 例外が発生する可能性のある関数を呼び出す
divide(10, 2); // 正常なケース：10を2で割る
divide(8, 0);  // 例外ケース：ゼロ除算
?>
この例では、divide() 関数を定義し、引数 $numerator を $denominator で割る処理を行います。try ブロック内で $denominator がゼロの場合に例外を投げます。catch ブロックでは例外が発生した場合にそのメッセージを表示し、finally ブロックでは最終的なクリーンアップを行います。
PHPの例外処理に関連する主なクラスは次のとおりです。
Exception: これは PHP の標準的な例外クラスです。このクラスを継承してカスタム例外クラスを作成することもできます。getMessage()、getCode()、getFile()、getLine()、getTrace()などのメソッドが利用可能で、これらは例外の詳細情報を取得するために使用されます。
Error: これは例外ではなくエラーを表すためのクラスです。このクラスも例外クラスを継承しており、エラーの詳細情報を取得するためのメソッドがあります。
カスタム例外を作成することもできます。例えば、特定の種類のエラーやアプリケーション固有の例外を処理するために、以下のようにしてカスタム例外を作成することができます:
class CustomException extends Exception {
    // カスタム例外クラスの実装
}
// 使用例
try {
    // 何かしらの処理
    if ($somethingUnexpected) {
        throw new CustomException("Something unexpected occurred!");
    }
} catch (CustomException $e) {
    echo "Caught custom exception: ", $e->getMessage(), PHP_EOL;
} catch (Exception $e) {
    // 他の例外をキャッチする
    echo "Caught exception: ", $e->getMessage(), PHP_EOL;
}
 
上記の例では、CustomException という独自の例外クラスを作成しています。そして try ブロック内で、何かしらの条件でその例外を発生させ、catch ブロックでその例外をキャッチしています。
このようにして、標準の Exception クラスを継承してカスタム例外を作成し、それを使用して特定の種類の例外を処理することができます。
PHPにおける例外処理のベストプラクティスにはいくつかあります。その中でも重要なものをいくつか挙げてみましょう。
適切な例外クラスの使用: 標準の Exception クラスを適切に使用することは重要です。また、必要に応じてカスタム例外クラスを作成することで、異なる種類のエラーに対処できます。
エラーメッセージの適切な管理: 例外が発生した際には、適切なエラーメッセージを提供し、デバッグやエラーの追跡を容易にすることが大切です。getMessage() メソッドを使って適切な情報を取得し、ログに記録するなどして詳細を把握しやすくします。
適切なエラーハンドリング: try, catch, finally ブロックを使ってエラーをキャッチし、適切なハンドリングを行います。エラーをキャッチした際に適切な対処を行い、コードの安全性を確保します。
例外の階層構造の活用: PHPには様々な種類の例外があり、これらを階層的に管理することで、特定の例外に対する処理をより細かく制御できます。
適切なロギング: 例外が発生したときには、それをログに記録することが重要です。これにより、エラーが発生した際に問題をより迅速に特定し、解決することができます。
ユーザーフレンドリーなエラー表示: ユーザーにはわかりやすく、安心感を与えるエラーメッセージを提供することが重要です。しかし、セキュリティ上のリスクを避けるために、エラーの詳細はログなどに記録し、ユーザーにはそれを表示しないようにします。
これらのベストプラクティスを遵守することで、PHPアプリケーションのエラー処理を効果的に行うことができます。
代替構文は、通常の制御構造の代わりに、より読みやすいコードを書くために使用される構文です。
代替構文は、制御構造の一部に波括弧 { } を含めずに書くことができ、それによってコードをよりシンプルにすることができます。
例えば、次のif文の代替構文は：
if ($a == 1) {
    echo "a is 1";
}
以下のように書くことができます：
if ($a == 1):
    echo "a is 1";
endif;
同様に、以下のfor文の代替構文は：
for ($i = 0; $i < 10; $i++) {
    echo $i;
}
以下のように書くことができます：
for ($i = 0; $i < 10; $i++):
    echo $i;
endfor;
代替構文は、コードの可読性を向上させることができますが、適切に使用しない場合はコードの可読性を損なうこともあります。したがって、代替構文を使用する際には、適切に判断することが重要です。
代替構文は、コードの見た目をより読みやすくするために導入された構文です。
代替構文を使用する場合、制御構造の終了を示すためにそれぞれ endif、endfor、endforeach、endwhile を使用します。
しかし、代替構文を使っても制御構造自体の動作には変わりはありません。
// 条件分岐
// if文
if (条件式1) {
    // 条件式1が true の場合の処理
} elseif (条件式2) {
    // 条件式1が false で条件式2が true の場合の処理
} else {
    // 上記の条件式がすべて false の場合の処理
}
// switch文
switch (値) {
    case 値1:
        // 値が値1と一致する場合の処理
        break;
    case 値2:
        // 値が値2と一致する場合の処理
        break;
    // ...
    default:
        // 値がどの case にも一致しない場合の処理
        break;
}
// match式（PHP 8.0以降）
match (値) {
    値1 => // 値が値1と一致する場合の処理,
    値2 => // 値が値2と一致する場合の処理,
    // ...
    default => // 値がどの case にも一致しない場合の処理,
}
// 繰り返し
// for文
for (初期化式; 条件式; 変化式) {
    // 条件式が true の場合に繰り返す処理
}
// while文
while (条件式) {
    // 条件式が true の場合に繰り返す処理
}
// do-while文
do {
    // 最初に必ず一度実行される処理
    // 条件式が true の場合に繰り返す処理
} while (条件式);
// foreach文
foreach ($配列 as $キー => $値) {
    // 配列の要素数分繰り返す処理
}
// 制御文
// break文
break; // 現在のループまたはswitch文から抜ける
// continue文
continue; // 現在のループの次の繰り返しに移る
// goto文
goto ラベル名; // 指定されたラベルにジャンプする
// ...
ラベル名:
// ...
条件式（Condition）：条件分岐やループ処理において、判定に使用する式のことです。真偽値を返す必要があります。
if文（if statement）：条件分岐のための制御構造の一つで、指定された条件式が true の場合に実行するブロックと、 false の場合に実行するブロックを分けて指定します。
elseif文（elseif statement）：複数の条件分岐を実行する場合に使用される制御構造の一つで、直前の if 文の条件式が false で、自身の条件式が true の場合に実行するブロックを指定します。
else文（else statement）：if文の条件式が false の場合に実行するブロックを指定する制御構造の一つです。
switch文（switch statement）：複数の条件分岐を実行する場合に使用される制御構造の一つで、指定された変数の値によって実行するブロックを選択します。
case文（case statement）：switch 文で使用され、値に応じて実行するブロックを選択します。
default文（default statement）：switch 文で使用され、どの case 文にも当てはまらない場合に実行するブロックを指定します。
while文（while statement）：指定された条件式が true の場合に、実行するブロックを繰り返し実行する制御構造の一つです。
do-while文（do-while statement）：一度は必ず実行し、指定された条件式が true の場合に実行するブロックを繰り返し実行する制御構造の一つです。
for文（for statement）：指定された回数分、実行するブロックを繰り返し実行する制御構造の一つです。
foreach文（foreach statement）：配列やオブジェクトのプロパティを1つずつ処理するために使用される制御構造。
break文（break statement）：繰り返し処理を強制的に終了するための制御構造の一つです。
continue文（continue statement）：繰り返し処理の一部をスキップし、次のループに進むための制御構造の一つです。
goto文（goto statement）：指定されたラベルにジャンプするための制御構造の一つです。PHPではほとんど使用されません。