PHPでうるう年(閏年)かどうか判定する

(1/1)
PHPでうるう年(閏年)かどうか判定する
PHPが活躍するのは、ブラウザからの入力に対して何か反応を返すような処理である。
このような処理は、
  1. ブラウザからの入力
  2. PHPによる処理、出力
の2段階になる。一般的に、1番目はHTMLのFORMタグで、2番目がPHPで処理する。そこで今回は、ブラウザから西暦年号を入力し、それがうるう年かどうか判定して結果を出力するページをPHPで作ってみることにする。
(2024年2月12日)記事「早期リターン」「グループ化」を追記した。
(2023年3月4日)フロー図を訂正した。
(2023年1月4日)関数名、変数名を読みやすくした。

download プログラムを実行する

目次

サンプル・プログラム

上のボタンをクリックして、PHPのソースプログラムをダウンロードしてほしい。
解凍できたら、isleap.php というファイル名でWebサーバ Apache の仮想ディレクトリが通っているディレクトリにセーブすること。
なお、画面に表示している行番号は説明の便宜上のものであり、ソースプログラムには含まれていない。
isleap.php 更新履歴
バージョン 更新日 内容
1.4.0 2024/02/12 早期リターン版 isLeapYear2() 追加
1.3.0 2020/12/05 関数名、変数名を読みやすくした
1.2.1 2020/12/05 PHP8動作確認
1.2.0 2017/03/18 bug-fix
1.1.0 2014/07/06 HTML5対応

サンプル・プログラムの実行方法

ブラウザから https://localhost/***/isleap.php (*** はセーブした仮想ディレクトリ名)と入力すると、年を入力するテキストボックスがあらわれるので、適当な西暦年を入力して[調べる]ボタンを押す。すると、同じページにうるう年かどうか判定結果が表示される。なお、初回起動時は現在の西暦年が代入される。

HTMLとPHPが混在するページ

   1: <!DOCTYPE html>
   2: <html lang="ja">
   3: <head>
   4: <meta charset="UTF-8">
   5: <title>閏年かどうか判定する</title>
   6: </head>
   7: 
   8: <?php
   9: // ここからPHPプログラム ===========================================

isleap.php の特徴は、HTMLタグとPHPプログラムが混在していることである。PHPでは、<?php ... ?>で囲まれた部分のみPHPプログラムとして解釈され、それ以外の部分はHTMLとして処理される。
冒頭はHTML 5の宣言部分である。PHPプログラムは、そのあとから始まる。

なお、HTMLタグとPHPプログラムを分ける方法として、これ以外に、「PHPでCSVファイルを読み込む」で取り上げる「ヒアドキュメント」を使った方法などがある。

if文

  18: /**
  19:  * 指定した年がうるう年かどうか判定する.
  20:  * グレゴリを暦の定義に忠実に記述した.
  21:  * @param   int  $year  西暦年
  22:  * @return  bool TRUE:うるう年/FALSE:平年
  23: */
  24: function isLeapYear($year) {
  25:     $result = FALSE;
  26:     if ($year % 4 == 0)     $result = TRUE;
  27:     if ($year % 100 == 0)   $result = FALSE;
  28:     if ($year % 400 == 0)   $result = TRUE;
  29:     return $result;
  30: }

与えられた年がうるう年かどうか調べるユーザー定義関数が isLeapYear である。
われわれが日常使っているグレゴリオ暦では、うるう年のルールが次のように定められている。
  1. 4年で割り切れる年はうるう年である。
  2. ただし、100で割り切れる年は平年である。
  3. ただし、400で割り切れる年はうるう年である。
PHPでうるう年(閏年)かどうか判定する
これをプログラムに忠実に作り込んだものがユーザー関数が isLeapYear である。

まず、変数 $year が平年であれば変数 $resultFALSE を、うるう年なら TRUE を代入する。(TRUE, FALSE は PHP があらかじめ用意している定数で、論理値が真なら TRUE、偽なら FALSE のようにして使う)

最初、変数 $resultFALSE である。次に、変数 $year が4で割り切れる場合は $resultTRUE を代入する。
割り算の余りは剰余演算子 % で得られる。剰余が0に等しいなら $resultTRUE を代入することを表すのが、if文 である。if文 の使い方は、CやJavaなど、他の手続き型言語と同じである。
次に、変数 $year が100で割り切れる場合は変数 $resultFALSEを代入する。
次に、変数 $year が400で割り切れる場合は変数 $resultTRUE を代入する。
最後に、変数 $result を戻り値として返す。

早期リターン

  32: /**
  33:  * 指定した年がうるう年かどうか判定する.
  34:  * 早期リターン版.
  35:  * @param   int  $year  西暦年
  36:  * @return  bool TRUE:うるう年/FALSE:平年
  37: */
  38: function isLeapYear2($year) {
  39:     if ($year % 400 == 0)   return TRUE;
  40:     if ($year % 100 == 0)   return FALSE;
  41:     if ($year % 4 == 0)     return TRUE;
  42:     return FALSE;
  43: }

PHPでうるう年(閏年)かどうか判定する
うるう年の判定だが、先ほどの定義を逆転して、400→100→4の順に割り算をしていくと if文の評価回数が少し減る。
これをフロー図にしたものが左図で、プログラムに実装したものがユーザー関数 isLeapYear2 である。

このように、途中で関数から return することを早期リターンと呼ぶ。
以前は構造化プログラミング手法では好ましくないとされていた書き方だが、最近では受け入れられている。

$_GET変数

  45: //メインプログラム
  46: if (isset($_GET['year']) == FALSE) {
  47:     $year = date('Y');          //初回起動時は現在年を代入
  48: else {
  49:     $year = $_GET['year'];
  50: }

前のページから、FORM タグの GET METHOD で渡されたデータを受け取るのが $_GET変数である。$_GET[名前] のようにして使う。名前の部分は、定数でも変数でも良い。ここでは、FORM のテキストボックス名 year で西暦年を渡すことを想定しているので、$_GET["year"] として受け取る。

ところが、最初にこのプログラムを起動した時には GET で渡されるデータはないので、$_GET['year'] は存在しない。このままでは実行時エラーになってしまう。
PHPでは、変数の存在を調べる関数  isset  が用意されている。そこで、 $_GET['year'] が存在しなければ、関数  date  を使って、プログラム実行時の西暦年を変数 $year に代入する。
もしGET渡しされたデータがあれば、$yearには $_GET['year'] の内容を代入する。
ここでは if~else文 が登場する。

グループ化

if (isLeapYear($year) == TRUE) {
    printf('%s年はうるう年です.', $year);
}
else {
    printf('%s年は平年です.', $year);
}
前述の if~else文だが、上図のように、ブレース {...} で囲まれた領域が1つのブロックになっている。これをグループ化と呼ぶ。説明の都合上、ブロックの範囲を赤枠で示している。
PHPでは、セミコロン ; で1つの文(式)の終わりを意味する。if~else文の中に複数の文(式)を記述したいときは、このようにグループ化する(この例では1文しか入っていない)。
PHPは Python と異なり、グループの中にインデントを置くかどうかは自由である(Python は、ブレース {...}の代わりにインデントによってブロックを識別しているため、インデントは必須)。
したがって、下記のような書き方をしてもいい。
if (isLeapYear($year) == TRUE) { printf('%s年はうるう年です.', $year); }
else { printf('%s年は平年です.', $year); }
また、文(式)が1つしかない場合にはグループ化する必要がないので、下記のような書き方ができる。
if (isLeapYear($year) == TRUE)
    printf('%s年はうるう年です.', $year);
else
    printf('%s年は平年です.', $year);
あまり使われることはないが、歴史的経緯から、下記のような書き方をすることもできる。この記述法では、ブロックをブレース {...} で囲む必要はないし、インデントも必須ではない。
if (isLeapYear($year) == TRUE):
    printf('%s年はうるう年です.', $year);
else:
    printf('%s年は平年です.', $year);
endif;
追い追い取り上げる for文while文でも同じようにグループ化ができる。

HTMLのFORMタグ部分

  56: <form action="isleap.php" method="get">
  57: 西暦年号=
  58: <input type="text" name="year" id="year" size="10" value="<?= $year ?>">
  59: <input type="submit" name="exec" id="exec" value="調べる">
  60: </form>

再びHTMLになっている部分である。
ここで、FORM タグを使って西暦年を入力している。西暦年は、テキストボックス year に代入され、POST METHOD によってユーザー関数 isleap.php に渡される。自分自身に渡していることがミソである。これを変数 $year に代入するのである。
なお、"<?= $year ?>" のように記述することで、HTML上に変数 $year の値を展開することができる。

printf関数

  64: if (isLeapYear($year) == TRUE) {
  65:     printf('%s年はうるう年です.', $year);
  66: else {
  67:     printf('%s年は平年です.', $year);
  68: }

再びPHPプログラムになった。見た目は分断されているが、PHPの処理的には連続している。
変数 $yearうるう年かどうか判定して、画面に判定結果を表示する。
関数  printf  は、指定された書式に応じて引き数の内容を画面に表示する。詳細はリンク先のPHPマニュアルをご覧いただきたい。基本的にはC/C++の printf 関数と同じで、数字や文字列を整形出力するのに重宝する。

補足

今回は isLeapYear をユーザー関数として作ったが、実はPHPには与えられた年月日の妥当性を検証する関数  checkdate  が用意されている。これを使えば、ある年の2月29日が存在するかどうか調べることによって、うるう年かどうかの判定ができる。

関数  printf  はC/C++のものと同様と書いたが、価格などを表す際に整数をカンマ区切りすることができない。こんな場合には関数  number-format  を使えばよい。

質疑応答

【質問】
フローチャートの時点で間違ってます。
閏年の定義は
年が4で割り切れたら閏年、
年が4で割り切れるが100で割り切れたら平年、
年が4で割り切れ、100で割り切れるが、400で割り切れたら閏年です。
西暦2000年(平成12年)は閏年、西暦2100年(令和82年)は平年です。
【回答】
フロー図が誤っていました。訂正します。
その他、Twitterでご指摘いただいた皆様、ありがとうございます。

参考サイト

(この項おわり)
header