初心者のなぜを一挙解決!今さら聞けないPHPwhileの基礎と本質

while、for、foreach、同じようなループなのにいくつもある意味がよくわからない。ひとつだけでいいのでは?

はじめまして、フリーランスエンジニアの平山です。私は過去プログラミング学習に挫折すること7回、はじめて心の底から理解できた言語がPHPでした。PHPを習得したことで、CakePHPを使ったWebサービスや、サーバサイドと連携したハイブリッドアプリの作成をしてきました。

また、1つの言語の本質を押さえたことで、他の言語の習得がかなり容易にできるようにもなりました。現在は、Pythonを使った人工知能で、ひとつ面白いプロジェクトを進めています。さてwhileに関して、公式マニュアルには次のような一文があります。

whileループは、PHPで最も簡単なタイプのループです。

引用元:PHP: while – Manual http://php.net/manual/ja/control-structures.while.php

いくら簡単と言われても、初心者にとって新しく触れる概念はつまづくポイントになるもの。もしかしたら、あなたは以下のような悩みをお持ちかもしれません。

  • forとwhileの使い分けがわからない
  • 無限ループを解決したい
  • ネストを使ったループ処理がわからないので、具体例を見てみたい

また、whileについて網羅的に知りたい、悩みが明確にできないから知識を深めて理解したいという方もいることでしょう。この記事では挫折経験7回の筆者が見出した、初心者が引っかかりやすいポイントを徹底的に解決します。

以下のポイントは特に力を入れて解説しています。

  • whileの基礎と本質の理解
  • whileとforの使い分け、具体的な使い方
  • 無限ループへの対処方法

最後まで読んでいただくことで、PHPのwhileについて自信を持って使いこなすことができるようになります。ひとりでも多くの方がPHPの本質を理解して、夢の実現にプログラミングを活用できたなら筆者としてこれほどうれしいことはありません。さっそくいってみましょう!

目次

記事を読む前の確認とすり合わせ

whileの学習に取り組む前に、まずは環境の確認とすり合わせをしていきましょう。

PHPの基礎が不安…という方はこちら!

まず、while以前の部分に不安が残る、とか、PHPの基礎についてもう一度確認しておきたい!という方はこちらの記事に一度目を通して見てください。学習方法なども紹介されており、すでに学習が進んでいる方にも新しい発見があるかもしれませんよ。

PHPの実行環境を整えたい方はこちら!

PHPの実行環境が準備できていない方、OSを乗り換えたので実行環境を再度整備する必要がある方はこちらをご覧ください。ちなみに、この記事も上で紹介されているXAMPPを使った環境をベースに動作確認をしています。

PHP実行のためのHTML

本記事で扱うコードのひな形を紹介します。まずは下のコードをコピーしてテキストエディタを開き、コードを貼り付けます。その後、適当な名前を付けてphpファイルとして保存してください。この記事ではsample_while.phpというファイル名で進めていきます。

sample_while.php

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>PHP whileのテスト</title>
</head>
<body>

<!--ここにPHPのコードを貼り付け-->

</body>
</html>

PHPファイルの使い方ですが、本記事内にあるPHPのサンプルプログラムを「<!–ここにPHPのコードを貼り付け–>」の部分に上書きすることで動作確認ができます。例えば、次のように使います。

コード例

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>PHP whileのテスト</title>
</head>
<body>

<?php
echo "PHPの実行テスト";
?>

</body>
</html>

sample_while.phpをXAMPPのhtdocsフォルダなど、サーバのドキュメントルートに置いて、実行が確認できたら準備完了です!

PHPファイルの詳しい実行方法については上でも紹介したPHPをインストールする記事の中で紹介されています。不安のある方はぜひこちらもご覧ください。この記事はWindows10,XAMPP7.3.0,PHP7.3.0の環境で記載・動作確認をしています。

whileの使い方

それではさっそくwhileについてみていきましょう。この記事では、PHP公式マニュアルを読み解く形で話を進めていくので、ぜひ公式マニュアルを別タブで開いて参照しつつこの記事を読み進めてください。

PHP公式マニュアル:while http://php.net/manual/ja/control-structures.while.php

そもそもwhileとは

whileはPHPにおける基本のループの1つです。ループとはプログラムに同じ処理を繰り返させる「繰り返し処理」のことです。たとえば、変数に1をどんどん加えて、それを表示していくような処理がありますね。
PHPのループは用途に応じて4種類あります。

  • while
  • for
  • do-while
  • foreach

それぞれの使い分けはあとの章でじっくりと解説していきます。ところで、公式マニュアルに「CのWHILEループと同様の動作をします。」という謎の一文があります。

これはC言語というPHPとは別のプログラミング言語のwhileと、いま学習しているwhileが同じ動作をしますよ、というただし書きです。初学者の方は無視してもらって構いません。続いて、基本的な使い方をみていきましょう。

基本的な使い方

whileの基本的な書き方は次のとおりです。

while(条件式){
    ループさせたい処理;
}

whileは条件式がTRUEの間、ループさせたい処理を繰り返し処理し続けます。公式マニュアルではループさせたい処理のことを「入れ子の文」と表現していますが、意味は同じです。図で表すと次のようになります。

whileのイメージ図

「条件式がTRUE」という表現に引っかかる方もいるかもしれません。TRUEは論理型の定数です。突然難しげな単語が出てきましたが、要するに式が正しいか間違っているかを判断する際に使う定数の話をしています。

たとえば 0 < 1 は数式として正しいですよね。これをプログラミングでは「式 0 < 1 を評価してTRUEを返す」という表現をします。慣れないうちは難しく感じますが、よく使う表現ですので、がんばって慣れてください。

逆に、間違っていることを表現する定数はFALSEを使います。0 > 1を評価すると、FALSEが返されるという具合ですね。TRUEとあわせて覚えてしまいましょう。whileの具体例として、次のようなプログラムを動かすことができます。

<?php
$i = 0;

while($i < 5){
    echo $i;

    $i = $i + 1;
}

?>

実行結果:

01234

このサンプルプログラムでは、変数$iに0を代入し、ループ処理に突入しています。この$iはループ処理を回す際のカウンターのような働きをします。ループが一周回るたびにカウンターの数が1増加していますよね。そのため、カウンタ変数やループ変数と呼ばれています。あわせて覚えておきましょう。

処理の流れは

whileの条件式を確認 → $i = 0 < 5 なのでTRUE
$iを表示
$iに1を追加して$iに代入 ($i = 1)
whileの最後まで来たので条件式に戻る
条件式を確認 → $i = 1 < 5 なのでTRUE

・・・

$iに1を追加して$iに代入 ($i = 5)
whileの最後まで来たので条件式に戻る
条件式を確認 → $i = 5 < 5 なのでFALSE
条件式がFALSEなので、whileを終了
プログラム終了

となっています。なお、条件式の評価が最初からFALSEだった場合、whileの中身は実行されません。たとえばこのようなコードでは何も表示されないのが正解です。

<?php
$i = 10;

while($i < 5){
    echo $i;

    $i = $i + 1;
}

?>

実行結果:


上と同じように流れを文章にしてみます。

  • whileの条件式を確認 → $i = 10 < 5 なのでFALSE
  • 条件式がFALSEなので、whileを終了
  • プログラム終了

このように動作するため、何も表示されないわけです。whileの基礎は以上になります。ループの基本、納得できましたか?

ループ処理にまつわる小ネタ

ここではループ処理でよく出てくる少々細かい話を取り上げます。

インクリメントとデクリメント

ループ処理ではカウンタ変数をひとつ増加させる$i = $i + 1という処理が頻出します。ループが一回転したため、カウンタ変数をひとつ増加させる、という処理ですね。あまりにも何度もでてくるので、この処理はカンタンに記述する方法が作られました。

それが次の書き方です。

$i++;
//$i = $i + 1;と同じ意味

この書き方をインクリメントといい、変数を1増加させる処理を「変数をインクリメントする」、と表現します。逆に変数から1を減らす処理も存在し、デクリメントといいます。書き方は次のとおりです。

$i--;
//$i = $i - 1;と同じ意味

なお、インクリメント・デクリメントは単体で使う分にはそこまで混乱しないものですが、echo $i++;のようにインクリメントと表示を同時に行うと期待とは異なる結果になることが時々あります。

値を返すタイミングである演算子の前置・後置が問題の原因なのですが、初心者のうちはとりあえず無視してしまって大丈夫です。もし、自力で解決したい方は、こちらを参考にチャレンジしてみてください。
PHPマニュアル:加算子/減算子

コロン構文

この項目はwhileの本質というよりは書き方についての話になります。HTML内に書かれるというPHPの特性上、HTMLタグとPHPのコードが混在することがよく起こります。このとき、コロンを使った構文、通称コロン構文を使うことでコード全体の見通しを良くすることができます。

コロン構文の書き方は次のとおりです。

while(条件式):
    ループ処理

endwhile;

比較のために普通のwhileの書式も再掲します。

while(条件式){
    ループ処理

}

比べると、{が:(コロン)に、}がendwhile;に置き換えられているのがわかりますね。初心者のうちは積極的にコロン構文を使うというよりも、他人が書いたコードを読んでいて、突然コロン構文が出てきたときのために覚えておく、という対応で問題ありません。

条件式とTRUEの話

この話も本質というよりは見た目の問題なのですが、慣れないと違和感がある部分ということで解説していきます。PHPの学習が進み、他人が書いたコードを読むようになると、時々次のような表現に出会うこともあるでしょう。

while(1){
    ループ処理
}

条件式の部分に数字の1が入っているだけで、式とは言いにくい状態ですよね。いままでは条件式がTRUEのとき、whileはループ処理を繰り返す、と説明してきました。

この表現だと、TRUEが入る部分は数式の形をしていなければいけない感じを受けますが、実はこの部分、式である必要はありません。そのため、直接TRUEと記入した以下のループは問題なく回り続けます。

while(TRUE){
    ループ処理
}

この書き方、あとで紹介する無限ループで頻繁に出てくるので今のうちにおさえておいてください。そして、ここからはプログラミング言語ごとのクセが出てくる部分なのですが、TRUEと解釈される書き方はいくつか存在するんです。そのうちの1つがこの節の冒頭出てきた(1)という書き方になります。

つまり、while(TRUE)とwhile(1)は同じ意味、というわけなんですね。他のプログラミング言語を学び始めると気づくポイントではあるのですが、何をTRUEと解釈して、何をFALSEと解釈するかは、言語によって結構違いがあります。

細かいところなので、うっかりハマるポイントでもあります。プログラミングの初学者にはあまり関係ない話ですが、PHPが何をTRUEと解釈するのか興味のある方はこちらを参考にしてみるといいでしょう。

PHPマニュアル:論理型 (boolean)

この項目も、他人が書いたコードを読んでいてビックリしないよう、知識として覚えておいてください。

[sc name="img-caption"](引用元: GagoDesign/shutterstock)[sc name="img-caption-end"]

さて、ここまでがんばって読み進めてきたことで、whileの公式マニュアルは一通り理解できるようになったはずです。ぜひ一度公式マニュアルに目を通して自分の成長を実感してみてください。

PHP公式マニュアル:while

ループ処理をスキップするcontinue

この節ではループの処理を一部スキップするcontinueについて見ていきます。ループ処理を行っていく中で、カウンタ変数が特定の値のときだけ別の処理をしたい、ということがあります。たとえば$i = 3の時だけ「three!」と表示させたい、というような場面です。

このとき利用するのがcontinueになります。continueの動作は、プログラムがcontinueまで到達すると、その下に続くループ処理をすべてスキップしてループの条件式まで戻る、というものです。サンプルプログラムで実際に動作を確認してみましょう。

<?php
$i = 0;

while($i < 5){

    if($i == 3){
        echo "three!";
        $i++;
        continue;
    }
    
    echo $i;
    $i++;
}

?>

実行結果:

012three!4

ループ内の処理の流れは次のとおりです。

whileの条件式を確認 → $i = 0 < 5 なのでTRUE
ifの条件式を確認 →$i = 0 == 3 となっているのでFALSE
ifの中身は実行しない
$iを表示
$iをインクリメント ($i = 1)
whileの最後まで来たので条件式に戻る

・・・

$iに1を追加して$iに代入 ($i = 3)
whileの最後まで来たので条件式に戻る
whileの条件式を確認 → $i = 3 < 5 なのでTRUE
ifの条件式を確認 →$i = 3 == 3 となっているのでTRUE
ifの中身を実行する
three!と表示
$iをインクリメント ($i = 4)
continueに到達したのでwhileの条件式まで戻る
whileの条件式を確認 → $i = 4 < 5 なのでTRUE
ifの条件式を確認 →$i = 4 == 3 となっているのでFALSE
ifの中身は実行しない

・・・

条件式がFALSEなので、whileを終了
プログラム終了

処理を1個1個みて行くとなかなかのボリュームですが、それぞれの処理自体はそこまで難しくはないはずです。今回は基本で紹介したwhileループに加えて、ifの分岐が登場しました。ifの基本的な部分や応用的な使い方を確認しておきたい方はこちらもぜひご覧ください。

さて、ifの中身を見ていきましょう。ifの中身の処理は、カウンタ変数$iが3と等しいときはthree!と表示して、$iをインクリメントして、continueする、という動作です。処理がcontinueに到達したあとはwhileの条件式まで戻っているのが確認できましたか?

continueはwhileに限らず他のループ処理でも使うことができます。レベルを指定するという応用的な使い方もあります。これらがまとまっている記事がありますので、より学びを深めたい方はこちらも目を通しておくと良いでしょう。

ループの処理を終了するbreak

この章では前の章で紹介したcontinueと似た挙動をするbreakについて紹介していきます。continueは下に続く処理をスキップして条件式の評価に飛ぶ処理でした。

対するbreakは到達した時点でループを終了させる処理になります。たとえば、カウンタ変数が3になった時点でループ処理を終わらせたいような場面ですね。こちらもサンプルプログラムで動作を確認してみましょう。

<?php
$i = 0;

while($i < 5){

    if($i == 3){
        echo "three!";
        
        break;
    }
    
    echo $i;
    $i++;
}

?>

実行結果:

012three!

continueのサンプルプログラムとほぼ同じものを用意しました。違いはcontinueがbreakに変わったことと、カウンタ変数のインクリメントを削除したことです。breakに到達した時点でループ処理が終了するため、カウンタ変数をインクリメントさせる必要はありません。

実行結果はcontinueのときと違いthree!で打ち切られ、4が登場しません。カウンタ変数$iが3でループ処理を終えている、というのが実感できたのではないでしょうか。こちらもループ内の流れを文章で書いてみましょう。

whileの条件式を確認 → $i = 0 < 5 なのでTRUE
ifの条件式を確認 →$i = 0 == 3 となっているのでFALSE
ifの中身は実行しない
$iを表示
$iをインクリメント ($i = 1)
whileの最後まで来たので条件式に戻る

・・・

$iに1を追加して$iに代入 ($i = 3)
whileの最後まで来たので条件式に戻る
whileの条件式を確認 → $i = 3 < 5 なのでTRUE
ifの条件式を確認 →$i = 3 == 3 となっているのでTRUE
ifの中身を実行する
three!と表示
breakに到達したのでwhileループを終了
プログラム終了

continueと違い、breakに到達した時点でループ処理が終了したのが実感できましたか?

breakもcontinueと同様に、while以外のループ処理でも利用できます。レベル指定も使うことができ、これらについてまとめてある記事がこちらになります。理解を深めたい方はぜひご覧ください。

この章で学んだこと・まとめ

大変長い章になってしまったので、この章で学んだことをもう一度まとめておきます。

whileの基本的な使い方

まずはじめにwhileの基本的な使い方を学びました。基本的な書式は以下の通りです。

while(条件式){
    ループさせたい処理;
}

条件式は式が正しいか間違っているか(真か偽か)を判断して、TRUEかFALSEを返す式でした。whileは条件式がTRUEの間ループし続けます。

また、カウンタ変数について学びました。カウンタ変数はループごとに数値が変化する変数で、カウンタ変数が特定の値になったらループを終了する、という方法で利用するものでした。

カウンタ変数を変化させるための便利な方法として、インクリメント・デクリメントも学びました。インクリメントは$i++、デクリメントは$i–のように書き、変数の値を1だけ増加・減少させるものでした。

コロン構文

コロン構文についても学びました。コロン構文は、HTMLの内部に書くことになるPHPので、見通しを良くするために使う書き方でした。

continueとbreak

continueとbreakについても学習しました。これらはループ内部に記述することで次のように動作します

  • continue:以下のループ処理を飛ばして条件式に戻る
  • break:以下のループ処理を飛ばしてループ処理を終了させる

continueとbreakはifなどの条件分岐と合わせることで、ループ処理を柔軟に変化させることができます。

do-whileの使い方

この章ではwhileループの仲間であるdo-whileについて詳しく説明していきます。今回も公式マニュアルをひもとく形で進めていくので、ぜひ別タブでマニュアルを開きつつご覧ください。

PHP公式マニュアル:while

do-whileとは

do-whileはwhileとほぼ同じ働きをするループ処理です。whileループとの唯一の違いは、ループ内部の処理が最低1回は動作することが保証されていること。文章だけでは少々わかりにくいので、書式とサンプルプログラムで見ていきましょう。

まずは書式から。

do{
    ループ処理
}while(条件式);

whileと同様に図で表すと下のようになります。

do-whileのイメージ図

サンプルプログラムは次のとおりです。

<?php
$i = 0;

do{
    echo $i;

    $i = $i + 1;

}while($i < 5);

?>

実行結果:

01234

実行結果を見て、「whileのときと同じじゃん!」というツッコミが浮かんだ方はなかなか鋭いです。たしかに、実行結果はwhileとdo-whileで全く同じなのです。では、どうやって使い分けをすればいいのでしょうか?それは次の節で見ていきましょう。

do-whilw文の使い方

前の節のサンプルプログラムは、whileとdo-whileの実行結果が同じになってしまいました。では、どんな場面であればdo-whileを活用できるのでしょうか。ポイントはループ内部の処理が最低1回は動作するという部分です。試しに以下のサンプルプログラムを実行してみてください。

<?php
$i = 10;

do{
    echo $i;

    $i = $i + 1;
}while($i < 5);

?>

実行結果:

10

このサンプル、実はwhileの基本的な使い方で紹介したものとほぼ同じです。違いはwhileとdo-whileを取り替えただけ。

whileのときは最初の条件式でFALSEになるため、何も表示されないプログラムでした。ですが、今回はdo-whileの最低1回は動作するという仕組みにより、最初のechoが実行されて$iの中身が表示されているわけです。

処理の流れは次のとおりです。

  • $iに10を代入
  • $i = 10 を表示
  • $iに1を加えて$iに代入
  • whileの条件式を判断 → 10 < 5 なのでFALSE、whileを終了
  • プログラム終了

「1回だけ動作することが保証されているのがなんの役に立つの?」という疑問を持つ方もいるでしょう。たしかに、是が非でもdo-whileを使わなければいけない場面というのは存在しません。

ほとんどの場合、ループ処理はforとwhileで事足ります。do-whileがソースの見通しを悪くするということで、使用を禁止している現場もあるくらいですし…。ですが、他人が書いたコードを読むときなどで知らないとどうしようもない知識でもあるため、ここで紹介しました。

[sc name="img-caption"](引用元:GagoDesign/shutterstock)[sc name="img-caption-end"]

whileと同じく、ここまで通読いただけると公式マニュアルが無事読み解けるようになっているはずです。

PHP公式マニュアル:do-while

なお、後ろの段落で紹介されているサンプルプログラムは初心者段階では無視してOKです。というか、かなりテクニカルな使い方なので、むしろ無視推奨です。

擬似的にgoto的なことができる書き方なのですが、goto同様人によってはかなり嫌われている書き方でもあります。興味のある方は頭の体操だと思って処理の流れを考えてみてください。

whileとforの使い分け

この章では初学者の方が一度は引っかかるであろう、whileとforの使い分けを見ていきます。

基本かつ柔軟なループが組みやすいwhile

まずは今まで説明してきたwhileからです。whileはループ処理の基礎とも言える単純な構造をしています。条件式がTRUEのあいだループ処理を続けるというのは非常にシンプルで、その分柔軟な運用が可能です。

たとえば以下のように書くことで、カンタンに終わらないループ、つまり無限ループを作ることもできます。

while(TRUE){
    ループ処理
}

無限ループについてはこの次の章で詳しく説明していきます。さわりの部分だけ説明すると、無限ループにcontinueやbreakを組み合わせることで、他のループ処理よりもカンタンに柔軟な条件設定・分岐を作ることができます。

このようにループの回数を指定せず、何らかのアクションが起こるまでループさせることがwhileは得意なので、回数不定のループはwhile、などとよく説明されます。

1回だけは確実に実行させたいときのdo-while

do-whileの使い方でも出てきましたが、do-whileでなければ表現できないループというのは基本的に存在しません。なので、「ループ処理の最低1回の実行を保証したい」というピンポイントなケース以外で使うことはほとんどないでしょう。

回数指定がカンタンにできるfor

PHPの基本的なループはwhileの他にforがあります。whileとの使い分けで一番悩むのがforではないでしょうか。悩まずに使い分けられるようになるためには、それぞれの特徴を理解することが重要です。

そこでまずは、whileとforの違いを見ていきましょう。forは引数にカウンタ変数の初期値、条件式、増減式をとります。whileでは初期値はループの外で定義し、増減式はループ内部でインクリメントを使うことで実現していました。forの場合はこれらが引数としてセット可能になっているのが大きな違いです。

初期値、条件式、増減式の3点セットが引数に使えるため、forは回数を指定したループを作るのが得意です。whileとは逆に回数が特定できるループはfor、といった説明がよくなされています。

whileは初期値や増減式を書く場所にある程度の自由があり、書かないことすら可能です。逆に初期値を設定し忘れたり、増減式を書き忘れたりすることもあるため、回数指定にまつわるうっかりミスが起こりにくいのがforのメリットであり使う理由となるでしょう。

forについてさらに詳しく知りたい方はこちらの記事もご覧ください。

配列の反復処理に便利なforeach

foreachは配列の処理に特化したループです。まだ配列まで学習が進んでいない方向けにざっくりと説明すると、配列は複数の値を保持できる変数のようなものです。内部に複数の値を持つため、一個ずつ取り出して処理を行いたい、という需要があるわけですね。

このような事情があるため、foreachは配列から値を取りだすのに非常に便利にできています。配列から値を取り出すときはぜひforeachを使いましょう。foreachの使い方についてはこちらの記事を参照してください。

実はforeachはループ処理の中では比較的新しいもので、昔はforeachそのものが存在しませんでした。そのころはforやwhileを使って配列から要素を取り出す方法を使っていたんです。

今でもプログラミングの練習ということでwhileを使って配列から値を取り出す機会もあるかもしれません。次の節ではwhileをつかって配列から値をすべて取り出す方法を見ていきます。

whileで配列の値を取得する方法

前節でも書きましたが、配列から値を取得したい場合、PHPではforeachを使うのが最適です。ですが、どうしてもwhileで処理したい場合もあるかもしれないので、ここで紹介していきます。

whileで配列の値を取得する場合、count関数も一緒に使用します。count関数は配列の要素数をカウントしてくれる関数です。whileで配列の値を1つずつ取得するサンプルプログラムはこのようになります。

<?php

$array = [
         'value1',
         'value2',
         'value3',
         'value4',
         'value5'
         ];

//countで配列の要素数を取得し、値を$numberに代入

$number = count($array);

$i = 0;  

while ($i < $number){
  echo $array[$i];

  echo '<br>';
  
  $i++;
}

?>

実行結果:

value1
value2
value3
value4
value5

ちなみに、先ほどから何度も言っていますが、この処理自体はforeachを使うことでよりスマートに書くことができます。foreachのサンプルは次のとおりです。

<?php

$array = [
         'value1',
         'value2',
         'value3',
         'value4',
         'value5'
         ];

foreach ($array as $value){
  echo $value;

  echo '<br>';
}

?>

whileを使うよりもずっとスッキリ記述できていますね!

無限ループとwhile

この章では無限ループについて詳しく説明していきます。まずは、そもそも論から見ていきましょう。

[sc name="img-caption"](引用元: Marish/shutterstock)[sc name="img-caption-end"]

そもそも無限ループとは

無限ループは言葉の通り、無限に続いてしまうループのことです。意図的に無限ループを作る場合もあるにはありますが、多くの場合、意図せずに作ってしまうことがほとんどでしょう。意図せずに無限ループを作ってしまった場合、コンピュータに大きな負荷がかかってしまうため、注意が必要です。

ただ、多くのプログラミング言語では無限ループが発生した場合、強制的に終了する機能がありますのでそこまで過剰に恐れることもありません。

なぜ無限ループは必要なのか?

だいたい、コンピュータに大きな負荷を掛けるような処理がなぜ必要なのでしょうか?たしかに、無限ループだけだとループ処理が終わらないため、ほぼ意味はありません。

ですが、無限ループにいままで学んできたcontinueやbreakを組み合わせることで柔軟なループ処理を実現することができるんです。たとえば次のようなループ処理です。

while(TRUE){
    ・・・

    if(条件式){
        特定の条件下での処理
        break
    }

    ・・・

}

このような構造にすることで、特定の条件が発生するまでループ処理を回し続け、処理を待機するいわゆる処理待ち状態を作ることができます。条件がそろうまでループ処理を使って待機する、という形で無限ループは役割があるわけです。

さて、処理待ちのプログラムを実際に組んでみようという方向けに注意点を1つ。処理待ちを組む際はsleep等でプログラムの実行を遅延させる方法を学んでからにしてください。何度も同じことを言っていますが、無限ループは処理をする間コンピュータに大きな負荷を掛け続けます。

そのため、一定時間処理を休止させることで、負荷を軽減する手段が必要になるんです。sleepの使い方についてはこちらの記事をご覧ください。

次節では意図せず無限ループができてしまった時、どのように対応すればいいのかを紹介します。

意図せず無限ループができてしまったときの対処法

この節で無限ループが起きてしまった場合の対処法を覚えていきましょう。PHPの動作確認はブラウザ上で行っている方が多いのではないでしょうか。すべてのブラウザには、下の画像のようにWebページの読み込みを停止させるためのボタンがあります。

例:Google Chromeの読み込み停止ボタン

この読み込み停止ボタンを押すことで、PHPファイルの実行を強制的に止めることができます。緊急時の対処法として、ぜひ覚えておいてください。

なお、Google Chromeは再読込みボタンと読み込み停止ボタンが一体となっており、ファイルを読み込み中のみ読み込み停止ボタンが現れます。いざというときのためにも、自分の環境での強制停止方法は覚えておきましょう。

また、PHPは標準の仕様として、30秒以上同じプログラムを実行しようとすると、強制的に止まる仕組みが用意されています。そのため、停止方法がわからなくても、最悪30秒待てば強制的に止まるので、そこまで無限ループを恐れることもないでしょう。

無限ループを解消するためのチェックポイント

この節では意図せず無限ループができてしまった際に、無限ループを解消するために確認すべきポイントを紹介します。

カウンタ変数の$をチェック

初心者で一番やりがちなのがこのミスです。たとえば下のサンプルプログラムは無限ループになっています。

<?php
$i = 0;

while(i < 5){
    echo $i;

    $i = $i + 1;
}

?>

正確に言うと、次のエラーが無限に出続けるサンプルプログラムです。

Warning: Use of undefined constant i - assumed 'i' (this will throw an Error in a future version of PHP) in C:xampphtdocssample_while.php on line XX
0

初めてこれを見ると、なかなかギョッっとしますよね。これが原因でループにトラウマを持ってしまう方もいるかもしれません。さて、これは何が間違っていたのかというと、タイトルの通りカウンタ変数に$が付いていないんです。whileの条件式の部分ですね。

while(i < 5){

iに$が付いていないため、このiは変数ではなく、未定義の定数とみなされてしまいます。iは未定義の定数→文字列→数値と変換されていき、最終的に0と評価されてしまいます。0 < 5はTRUEであり、値が変化しないため、無限ループが発生していたわけです。

解決方法はカンタンで、iに$をつければ完了です。無限ループでエラーが大量に出力されたときはカウンタ変数の$を確認しましょう。

カウンタ変数がwhile内部で変化してるかチェック

次に確認してほしいのが、カウンタ変数が内部で変化しているかチェックすることです。たとえば次の無限ループを起こすサンプルプログラムがあったとします。

<?php
$i = 0;

while($i < 5){
    echo $i;
}

?>

先程の例と違い、今回はエラーが出力されず、延々と0が表示されます。この無限ループの原因は、カウンタ変数の増加を記述し忘れたことにあります。今回の例ではサンプルなのでわりとあからさまに書いてないのがわかります。

ですが、whileは構造上ループの最後にカウンタ変数の増減処理を書くことが多いため、書き忘れることが結構あります。このパターンで無限ループにハマってしまった場合は、まずはループ末尾にカウンタ変数の増減式があるのかしっかり確認しましょう。

うっかりミスとして、増減式をループの外に書いてあるケースも時々見かけます。あわせて気をつけてください。今回のサンプルプログラムの修正方法は次のとおりです。

<?php
$i = 0;

while($i < 5){
    echo $i;

    $i++;
    //$iのインクリメントを追加
}

?>

breakは機能しているかチェック

最後に確認しておきたいのが、breakが機能しているかチェックすることです。今回のサンプルプログラムは次のとおりです。

<?php
$i = 0;

while(TRUE){

    if($i == -3){
        echo "three!";
        
        break;
    }
    
    echo $i;
    $i++;
}

?>

whileの条件式をTRUEにしてループ処理を行う場合、breakを含むブロックが無限ループ解消の鍵になります。そもそもbreakが設定されていなかったり、breakのブロックに入れないと無限ループになってしまうのはある種当然のこと。breakブロックの設定と入るための条件式をしっかり確認しましょう。

今回はカウンタ変数が絶対に取らない値をbreakに入るための条件式に設定していたため、無限ループが発生していたのです。そのため、修正方法はこのようになります。

<?php
$i = 0;

while(TRUE){

    if($i == 3){
        //-3を3に修正
        echo "three!";
        
        break;
    }
    
    echo $i;
    $i++;
}

?>

whileを使った応用処理

さて、最後にwhileを使ったループ処理をいくつか紹介していきます。応用処理ということで、ここではネスト(入れ子)という技術もあわせて紹介します。ネストとはwhileループの中にwhileループを差し込む使い方です。

書式としては下のようになります。

while(条件式1){

    ループ処理

    while(条件式2){
        ループ処理
    }

    ループ処理
}

見て分かる通り、ネストを行うことでループの構造は複雑になります。また、ネストの中にさらにネストを作ることができ、原理上いくらでもネストを深くすることは可能です。

ただ、深いネストは処理がわかりにくく、エラーが発生した際の対応も面倒になってくるので、必要がない限りは2から3重のネストで止めるのが一般的です。

ネストを学ぶことでできることが飛躍的に広がります。そのため、ついつい深いネストを作りがちですが、ぜひ読みやすいコードを書くよう心がけましょう。

ネストで九九の表を作ろう

まずはループを使って九九の表を作ってみましょう。内側のループで掛け算の右側の数字を変化させつつ計算結果を表示し、外側のループで掛け算の左側の数字を変化させる、という流れで進めていきます。

まずは内側のループについて見ていきます。whileを使って1 ~ 9を表示させる、という処理ですね。計算結果は次のように表示させましょう。

echo $x. "×". $y. "=". $x*$y. " ";

これは次のように表示されます

$x×$y=$x*$y (スペース)

そして、段と段の間に1行改行を入れたいので、次のように改行タグを表示させます。

echo "<br>";

ここまでをまとめると次のようになります。

$y = 1;
    while($y <= 9){
        echo $x. "×". $y. "=". $x*$y. " ";

        $y++;
    }
echo "<br>";

これだけで$xの初期値を設定してあげればxの段の結果が表示されます。あとは、xを1 ~ 9まで変化させることで1の段から9の段までの九九の表を得ることができます。サンプルプログラムは以下のようになります。

<?php
$x = 1;

while($x <= 9){

    $y = 1;

    while($y <= 9){
        echo $x. "×". $y. "=". $x*$y. " ";

        $y++;
    }

    echo "<br>";

    $x++;
}
?>

実行結果:

1×1=1 1×2=2 1×3=3 1×4=4 1×5=5 1×6=6 1×7=7 1×8=8 1×9=9 
2×1=2 2×2=4 2×3=6 2×4=8 2×5=10 2×6=12 2×7=14 2×8=16 2×9=18 
3×1=3 3×2=6 3×3=9 3×4=12 3×5=15 3×6=18 3×7=21 3×8=24 3×9=27 
4×1=4 4×2=8 4×3=12 4×4=16 4×5=20 4×6=24 4×7=28 4×8=32 4×9=36 
5×1=5 5×2=10 5×3=15 5×4=20 5×5=25 5×6=30 5×7=35 5×8=40 5×9=45 
6×1=6 6×2=12 6×3=18 6×4=24 6×5=30 6×6=36 6×7=42 6×8=48 6×9=54 
7×1=7 7×2=14 7×3=21 7×4=28 7×5=35 7×6=42 7×7=49 7×8=56 7×9=63 
8×1=8 8×2=16 8×3=24 8×4=32 8×5=40 8×6=48 8×7=56 8×8=64 8×9=72 
9×1=9 9×2=18 9×3=27 9×4=36 9×5=45 9×6=54 9×7=63 9×8=72 9×9=81

数字をピラミッド型に表示してみよう

次に紹介するのが、数字をピラミッド型にならべる方法です。この手の問題はプログラミングの練習問題で時々見かけますね。イメージとしては下のようなものです。

1
22
333
4444
・・・

ここでは考え方の一例を紹介します。外側のカウンタ変数を$x、内側のカウンタ変数を$yとします。外側のループで行の数を扱い、内側のループでその行に必要な個数を表示させましょう。

今回も内側のループから組み立てていきます。内側のループを組み立てる際は外側のループのカウンタ変数を固定すると考えやすくなります。ここでは$x = 3として内側のループを考えましょう。

内側のループの仕事は$xの数を$x個並べることです。具体例を使うと、3を3個並べればいいわけですね。3を3回連続で表示させるプログラムは次のようになります。

<?php

$y = 0;

while($y < 3){
    echo "3";

    $y++;
}

?>

ここで、3を$xに置き換えることで外側のループをどう組めばいいのかが見えてきます。

$y = 0;

while($y < $x){
    echo $x;

    $y++;
}

$xごとに行を変えたいため、内側のループの後ろに改行タグが必要ですね。さらに、$xを変化させるためのインクリメントも追加します。以上をまとめると、次のようなサンプルプログラムが出来上がります。

<?php
$x = 1;

while($x < 10){
    $y = 0;

    while($y < $x){
        echo $x;

        $y++;
    }

    echo "<br>";
    $x++;
}

?>

実行結果:

1
22
333
4444
55555
666666
7777777
88888888
999999999

今回は数字を使ったので10以降はピラミッド型が若干崩れて汚くなってしまいますが、*(アスタリスク)などの記号を使うときれいなピラミッド型が延々と描けますよ。この手の問題は内側のループとスペースの使い方を工夫することでいろいろな形が描けます。興味のある方はいろいろと試してみるといいでしょう。

まとめ

いかがでしたか? この記事ではPHPのwhileについて

  • 基本的な構文からはじまり
  • 他のループ文との使い分け
  • 無限ループの対処法
  • 応用的な使い方をまとめてきました

最大限スモールステップにしてつまずきを減らそうとした結果、かなりのボリュームになってしまった感はあります。すべてを一気に理解するのはなかなか大変でしょう。

ぜひブックマークやお気に入りに登録していただき、わからないことがあったら見返すような、辞書的な使い方をしてもらえると、筆者としてとても嬉しいです。お相手はPHPが心の師匠なテックライターの平山でした。

この記事を書いた人

フリーのエンジニア・ライター。
プログラミング、ライティング、マーケティングなど、あらゆる手段を駆使して、
ハッピーなフルリモートワーカーを目指し中。

最近興味がある分野は深層強化学習。
積みゲー、積ん読がどんどん増加しているのがここ数年の悩み。
実は侍エンジニア塾の卒業生だったりします。

目次