三陸牡蠣復興支援プロジェクト「SAVE SANRIKU OYSTER」



制御構造2

ここでは、「制御構造」のページで解説し切れなかった、制御構造の残りの部分、「declare文」と「return文」、その他諸々について解説します。

制御構造の残党(オイ)は以下の通りです。

  • 「include文」
  • 「require文」
  • 「include_once文」
  • 「require_once文」
  • 「return文」
  • 「declare文」

ファイル(PHPスクリプト)を読み込む

スクリプトの規模が大きくなってくると、ファイル1つに全部バーッと書いてしまっては見渡しが悪くなってきます。また、共通する部分や、はたまた必要の無い処理も出てきます。そんな時、機能(処理)別にファイルを作成して、それらを必要に応じて読み込めれば楽が出来そうです。

この手抜きを実現するのが、「include」、「require」、「include_once」、「require_once」の4つです。

これら4つの機能は「パラメータに指定したファイルを読み込む」というもので、基本的に同じですが、以下の様な差異があります。

記述読み込み失敗時と返り値再度読み込みと返り値
include $file処理続行、「FALSE」可、「TRUE」
require $file処理停止可、「TRUE」
include_once $file処理続行、「FALSE」不可、「TRUE」
require_once $file処理停止不可、「TRUE」

名前の通り、「include」は単に「追記」であるのに対して「require」は「必須」であり、「xxx_once」は「一度だけ」となっています。

また、これらはファイルの読込(初回のみ)に成功した場合に整数の「1」を返します。

以下にサンプルと結果を。

まず、「funcs.php」という名前のファイルを作成して、次のように記述します。

<?php
    function func1(){
        echo "HODENASU!! 1<br />\n";
    }
    function func2(){
        echo "HODENASU!! 2<br />\n";
    }
    function func3(){
        echo "HODENASU!! 3<br />\n";
    }
    echo ++$i;
    echo "\n<hr />\n";
?>

つぎに、「hode.php」という名前のファイルを作成して、次のように記述します。

<html>
<body>
<?php
    $i = 0;
    
    require "funcs.php";
    $inc = include "fancy.php";
    
    if($inc){
        echo "Included!<br />\n";
    }else{
        echo "UnIncluded...<br />\n";
    }
    
    func1();
    
    include_once "funcs.php";
    require_once "funcs.php";
    
    func2();
    
    require "fancy.php";
    
    func3();
?>
</body>
</html>
1

(「fancy.php」が不明という警告メッセージ) (「fancy.php」の読み込み失敗という警告メッセージ) UnIncluded... HODENASU!! 1 HODENASU!! 2 (「fancy.php」が不明という警告メッセージ) (「fancy.php」の読み込み失敗という致命的エラーメッセージ) 【処理停止】

今回のサンプルは、以下の様な処理をした末、果てました。

  1. require "funcs.php";」でファイル読込。
  2. 「funcs.php」内で関数定義と文字列出力。
  3. include "fancy.php";」でファイル読込を試みるが失敗。読み込みが成功したか否かを示す真偽値を「変数$inc」に格納。
  4. 「変数$inc」が「false」なので「UnIncluded...」を出力。
  5. func1()」を実行。
  6. include_once "fancy.php";」でファイル読込を試みるが、既に読み込み済みなのでスキップ。
  7. require_once "fancy.php";」でファイル読込を試みるが、既に読み込み済みなのでスキップ。
  8. func2()」を実行。
  9. require "fancy.php";」でファイル読込を試みるが失敗。この時点で処理停止。

ちなみに、HTMLのソースは下記の様になっています。

<html>
<body>
<p>
1
<hr />
</p>
UnIncluded...<br />
HODENASU!! 1<br />
HODENASU!! 2<br />

ファイルを関数内で読み込む

関数内でファイルを読み込んだ場合、ファイルの中身はそのまんま関数内に展開されるので、関数内にあってはならないものがあればエラーになるし(※)、変数は関数内のローカル変数に成り下がりますので注意が必要です。

※以下、補足。

関数内でも関数の定義とクラスの定義が出来ます。コールされて初めて関数やクラスを定義するという関数を作る事が出来、“定義用関数”によって定義された関数やクラスは、関数外で定義されたものと同様に扱えます。しかしながら、クラス関数内では関数の定義は出来てもクラスの定義は出来ず(クラスのネストは出来ません云々というエラーになる)、定義出来た関数も扱う術がありません(つまり無意味な定義になる)。

ところが、「include」等を使った場合、エラーにはならず、クラス関数外つまりグローバルで定義されたかのように処理されます。以下の2つのサンプルをご覧下さい。

<html>
<body>
<?php
    class TEST{
        function TEST(){
            $hode = "HODENASU!!!<br />";
            function func(){
                echo "I am func()<br />";
            }
            class TEST2{
                function func2(){
                    echo "I am func2()<br />";
                }
            }
            echo $hode;
        }
    }
?>
</body>
</html>

「クラスのネストが云々」というエラーが発生して終了。

次に、「inc.php」という名前で以下の様なファイルを作成し、その下のサンプルでこのファイルを読み込みます。

<?php
    $hode = "HODENASU!!!<br />";
    function func(){
        echo "I am func()<br />";
    }
    class TEST2{
        function func2(){
            echo "I am func2()<br />";
        }
    }
?>
<html>
<body>
<?php
    class TEST{
        function TEST(){
            require_once "inc.php";
            echo $hode;
        }
    }
    
    new TEST;
    $test2 = new TEST2;
    func();
    $test2->func2();
    echo $hode;
?>
</body>
</html>

以下の様な結果になります。

HODENASU!!!
I am func()
I am func2()
(「$hode」は未定義で何も出力されず。)

1つ目のサンプルで「TEST」クラスのコンストラクタ内に記述していたものを、2つ目のサンプルでは外部ファイルに記述してそれを読み込んでいます。

2つ目のサンプルも1つ目のサンプルと同じ末路を辿るかと思いきや、意外な結果になりました。関数の定義もクラスの定義も、さもグローバルで定義されたかのように処理されています。また、変数だけはローカル変数になっています。

このような仕様のおかげで、使いたい時だけクラスを定義する事が出来ます。また、この方法でファイルを読み込む時は必ず「require_once」を使うようにします。

ファイルを条件構造内で読み込む

条件構造(つまり「if文」等の制御文)内でファイルを読み込む場合は、命令ブロック(「ブロック」については「その他(補足)」のページで解説)として括っておく必要があります(本家のマニュアルによると)。

インクルードするファイル内の処理が単文ならいいですが、複数行だった場合は、ブロックが無ければいけないからだと思われます。と思って実験したら、別に問題が起きませんでした(謎です)。問題は無いようですが、括れと言ってるんで一応括っておいた方が良さそうです。以下にサンプルのみ。

<html>
<body>
<?php
    if(true)
        include "inc_a.php";
    else
        include "inc_b.php";

    if(true){
        include "inc_a.php";
    }else{
        include "inc_b.php";
    }
?>
</body>
</html>

1つめの「if文」では処理部を「{}(中括弧、波括弧)」で括っていませんが、2つめでは処理部を「{}(中括弧、波括弧)」で括ってブロックにしています。

読み込まれるファイル内での「return

読み込まれるファイル内に「return $var;」のように「return」があると、その時点でそのファイルの読込は終了して、その値(「$var」)を例えば「include」の返り値として返します。以下にサンプルと結果を。

まず、「return.php」という名前のファイルを作成して、次のように記述します。

<?php
    echo "HODENASU!! 1<br />";
    echo "HODENASU!! 2<br />";
    echo "HODENASU!! 3<br />";
    
    return "NDA!<br />";
    
    echo "HODENASU!! 4<br />";
?>

つぎに、「hode.php」という名前のファイルを作成して、次のように記述します。

<html>
<body>
<?php
    $inc = include "return.php";
    
    if($inc){
        echo $inc;
    }else{
        echo "UnIncluded...<br />";
    }
?>
</body>
</html>
HODENASU!! 1
HODENASU!! 2
HODENASU!! 3
NDA!

return」で読み込み終了、返り値として文字列の「"NDA!"」を返し、それを出力しました。

include_path

ファイルを読み込む際、読み込むファイルを指定するわけですが、HTML等では、ファイルの指定に使う記号は次のように定義されています。

記述意味
.自分自身
File同じディレクトリ内のファイル
./File同じディレクトリ内のファイル
./dir1/File同じディレクトリ内にある「dir1」ディレクトリ内のファイル
/Fileドキュメントルート内のファイル
../File親ディレクトリ内のファイル

PHPでも然りなのですが、「include」、「require」、「include_once」、「require_once」の4つの場合は、上の表で言えば2番目の、直接ファイル名で指定(ディレクトリ名も含めたファイル名の先頭に「/(スラッシュ)」を置かないで指定、例えば「"./hodenasu/test.php"」ではなく「"hodenasu/test.php"」という指定)した場合は意味が変わります。

include」、「require」、「include_once」、「require_once」の4つでファイルを読み込む際に指定したパラメータが、直接ファイル名で指定したものである場合、読み込まれるファイルは『PHPの設定ファイル内の「include_path」に指定されたディレクトリ内にあるファイル』です(設定ファイル及び「include_path」と設定方法については、セクションを改めて解説します)。

簡単に言うと、「include_path(インクルードパス)」は、“読み込むファイルが格納されているディレクトリ”です。

このインクルードパスは、「get_include_path()」という関数で取得出来、また、「set_include_path()」という関数で設定出来ます。

以下のサンプルと結果をご覧下さい。

<html>
<body>
<?php
    $path = ".:".$_SERVER["DOCUMENT_ROOT"]."/_incs_";
    
    echo set_include_path($path), "<br />";
    
    echo get_include_path();
    
    echo "<hr />";
    
    include "hodenasu.php";
?>
</body>
</html>
.:/usr/local/lib/php
.:/server/hodenasu/public_html/_incs_

(「hodenasu.php」が不明という警告メッセージ) (「hodenasu.php」の読み込み失敗という警告メッセージ)

インクルードパスを「".:".$_SERVER["DOCUMENT_ROOT"]."/_incs_"」に設定しています。「$_SERVER["DOCUMENT_ROOT"]」は「ドキュメントルート」のパスが格納されているスーパーグローバル変数です。「/server/hodenasu/public_html」の様な値がセットされます。

ドキュメントルートっていうのは、例えばドメイン名が「www.shigeweb.jp」であった場合、このアドレスにアクセスした時に表示されるページ(ファイル)が格納されているディレクトリの事です。「www.shigeweb.jp/php」にアクセスして表示されるページは、ドキュメントルート内にある「php」というディレクトリ内にあるファイルです。

インクルードパスを設定する場合、OSによって、UNIX(Linux)の場合は「:(コロン)」で、Windowsの場合は「;(セミコロン)」で区切って複数設定できます。今回のサンプルでは、「.」と「$_SERVER["DOCUMENT_ROOT"]."/_incs_"」の2つを設定しました。1つめは“同じディレクトリ内の”という意味で、2つめはドキュメントルートディレクトリ内の「_incs_」ディレクトリです。

で、「include "hodenasu.php";」の部分で「hodenasu.php」というファイルの読み込みを試みています。読み込むファイルの指定が、インクルードパスを使用する指定方法なので、設定したインクルードパスからの読み込みを試みます。まず、このサンプルファイルと同じディレクトリ内を探し、無い場合に「ドキュメントルートディレクトリ/_incs_」ディレクトリを探します。結果は何処にも無かったので読み込みに失敗しました。

読み込まれるファイル内で読み込まれるファイルはどれ?

まず、下記の様な構成でファイルを作成して下さい。

ドキュメントルート(「public_html」等)/
    |
    +--- hode/
    |      |
    |      +--- index.php
    |      |
    |      +--- hode_a.php
    |
    +--- _incs_/
           |
           +--- hode_a.php
           |
           +--- hode_b.php
           |
           +--- nasu/
                  |
                  +--- include.php
                  |
                  +--- hode_a.php
                  |
                  +--- hode_b.php
                  |
                  +--- hode_c.php
                  |
                  +--- nasu/
                         |
                         +---  hode_c.php
                         |
                         +---  hode_d.php

次に、それぞれのファイルに以下の様に記述して保存して下さい。

各「hode_a.php」、「hode_b.php」、「hode_c.php」、「hode_d.php」ファイルには次のように記述します。

<?php
    echo "__FILE__ : ".__FILE__;
?>

「ドキュメントルート/_incs_/nasu/include.php」ファイルには次のように記述します。

<?php
    echo get_include_path(), "<br /><br />";
    
    include "hode_a.php";
    
    echo "<hr />";
    
    include "hode_b.php";
    
    echo "<hr />";
    
    include "hode_c.php";
    
    echo "<hr />";
    
    include "nasu/hode_c.php";
    
    echo "<hr />";
    
    include "nasu/hode_d.php";
    
    echo "<hr />";
    
    include "./hode_a.php";
    
    echo "<hr />";
    
    include "./hode_b.php";
    
    echo "<hr />";
    
    include "./hode_c.php";
    
    echo "<hr />";
    
    include "../_incs_/nasu/hode_c.php";
?>

「ドキュメントルート/hode/index.php」ファイルには次のように記述します。

<html>
<body>
<?php
    $path = ".:".$_SERVER["DOCUMENT_ROOT"]."/_incs_";
    
    set_include_path($path);
    
    include "nasu/include.php";
?>
</body>
</html>

で、作成した「index.php」ファイルにアクセスするってーと、以下の様な結果になります。

.:/server/hodenasu/public_html/_incs_

__FILE__ : /server/hodenasu/public_html/hode/hode_a.php

__FILE__ : /server/hodenasu/public_html/_incs_/hode_b.php
__FILE__ : /server/hodenasu/public_html/_incs_/nasu/hode_c.php
__FILE__ : /server/hodenasu/public_html/_incs_/nasu/hode_c.php
__FILE__ : /server/hodenasu/public_html/_incs_/nasu/nasu/hode_d.php
__FILE__ : /server/hodenasu/public_html/hode/hode_a.php
(「hode_b.php」が不明、読み込み失敗という警告メッセージ)
(「hode_c.php」が不明、読み込み失敗という警告メッセージ)
__FILE__ : /server/hodenasu/public_html/_incs_/nasu/hode_c.php

アクセスした「index.php」ファイル内では「nasu/include.php」ファイルを読み込んでいて、更にその「nasu/include.php」ファイル内では、各ディレクトリに設置した「hode_a.php」、「hode_b.php」、「hode_c.php」、「hode_d.php」ファイルの読み込みを試みています。

前半の5つはインクルードパスを使用する指定方法で、後半の4つは相対パス指定、要するにインクルードパスを使用しない指定方法で読み込みを試みています。

「nasu/include.php」ファイル内の各ファイルの読み込み処理がどうなっていたのか追ってみます。

  1. include "hode_a.php";」でファイル読込。
    インクルードパスを使用してファイル検索が行われ、読み込み元の「hode/index.php」と同じディレクトリ内に「hode_a.php」があったのでそれを読込。
  2. include "hode_b.php";」でファイル読込。
    インクルードパスを使用してファイル検索が行われ、読み込み元の「hode/index.php」と同じディレクトリ内に無く、次の検索先の「ドキュメントルート/_incs_」ディレクトリに「hode_a.php」があったのでそれを読込。
  3. include "hode_c.php";」でファイル読込。
    インクルードパスを使用してファイル検索が行われ、読み込み元の「hode/index.php」と同じディレクトリ内に無く、次の検索先の「ドキュメントルート/_incs_」ディレクトリ内にも無かったので、読み込みに失敗するかと思いきや「nasu/include.php」と同じディレクトリ内にあった「hode_c.php」が読み込まれる。
  4. include "nasu/hode_c.php";」でファイル読込。
    インクルードパスを使用してファイル検索が行われ、読み込み元の「hode/index.php」と同じディレクトリ内に無く、次の検索先の「ドキュメントルート/_incs_」ディレクトリ内に「hode/hode_c.php」があったのでそれを読込。
  5. include "nasu/hode_d.php";」でファイル読込。
    インクルードパスを使用してファイル検索が行われ、読み込み元の「hode/index.php」と同じディレクトリ内に無く、次の検索先の「ドキュメントルート/_incs_」ディレクトリ内にも無かったので、読み込みに失敗するかと思いきや「nasu/include.php」と同じディレクトリ内にあった「hode/hode_d.php」が読み込まれる。
  6. include "./hode_a.php";」でファイル読込。
    読み込み元の「hode/index.php」と同じディレクトリ内の「hode_a.php」を読込。
  7. include "./hode_b.php";」でファイル読込。
    読み込み元の「hode/index.php」と同じディレクトリ内に「hode_b.php」は無いので読込失敗。
  8. include "./hode_c.php";」でファイル読込。
    読み込み元の「hode/index.php」と同じディレクトリ内に「hode_c.php」は無いので読込失敗。
  9. include "../_incs_/nasu/hode_c.php";」でファイル読込。
    読み込み元の「hode/index.php」が格納されたディレクトリの親ディレクトリ内の「_incs_/nasu/hode_c.php」を読込。

今回のサンプルの結果から解かるのは、例えば「hode/index.php」ファイルが「nasu/include.php」ファイルを読み込み、更に「nasu/include.php」ファイルが別のファイルを読み込む場合、「nasu/include.php」ファイルが読み込もうとするファイルは「hode/index.php」ファイルから見て何処にあるかを指定されたファイルが読み込まれるという事です。これが基本ですが、「hode/index.php」ファイルから見た何処にも無く、「nasu/include.php」ファイルから見るとある場合にはそれが読み込まれます(インクルードパスを使用した場合)。

処理を終了して値を返す

return

既に何度も登場している「return」ですが、これは主に関数内で、「加工した値を返す」という事をさせるために使ってきました。

しかし、このページで既に書きましたが、「return」は関数内にしか置けないわけではありません。関数外に置けば、その時点で処理を終了し、読み込まれたファイル内に書いてあれば、そのファイルの返り値として値を返す事も出来ます。

イベントハンドラみたいな

declare

JavaScriptにはイベントハンドラっていうものがありますが、例えば、「マウスオーバーしたらアラートを鳴らす」という事をしたい場合、「onmouseover="alert("HODENASU!!!");"」と書けば出来ます。これは、『記述したエレメント(要素)のイベントハンドラ(イベントを扱うもので、「onmouseover」であればマウスオーバー(イベント)が発生したら起こす処理を持つ(定義される)もの)が定義された処理を実行する』事で実現していますが、PHPでもこんな様な事が出来るでしょうか。「出来るわけねーだろ」こんな様な事は出来ませんが、こんな様な感じの事は出来ます。

「え?なんて?」

論よりコード2001という事で、以下のサンプルと結果をご覧下さい。

<?php
    function func1(){
        static $i = 0;
        echo '   func1 -> ', ++$i, "<br />";
    }
    function func2(){
        static $i = 0;
        echo '   func2 -> ', ++$i, "<br />";
    }
    
    register_tick_function("func1");
    register_tick_function("func2");
    
    declare(ticks = 3){
        echo "HODENASU!!<br />";
        echo "HODENASU!!<br />";
        echo "HODENASU!!<br />";
        echo "HODENASU!!<br />";
        echo "HODENASU!!<br />";
        echo "HODENASU!!<br />";
        echo "HODENASU!!<br />";
    }
    
    echo "<hr />";
    
    unregister_tick_function("func1");
    
    declare(ticks = 1);
    
    echo "HODENASU!!<br />";
    echo "HODENASU!!<br />";
    echo "HODENASU!!<br />";
    echo "HODENASU!!<br />";
?>
HODENASU!!
HODENASU!!
HODENASU!!
   func1 -> 1
   func2 -> 1
HODENASU!!
HODENASU!!
HODENASU!!
   func1 -> 2
   func2 -> 2
HODENASU!!

func2 -> 3 func2 -> 4 HODENASU!! func2 -> 5 HODENASU!! func2 -> 6 HODENASU!! func2 -> 7 HODENASU!! func2 -> 8 func2 -> 9

まず、「func1()」と「func2()」の2つの関数を定義しています。

次に、「register_tick_function("func1");」と「register_tick_function("func2");」の部分ですが、ここでは、『イベントハンドラ「tick」に関数「func1()」と関数「func2()」を登録する』という事をしています。

これにより、イベント「tick」が発生したら「func1()」と「func2()」の2つの関数が実行されるようになりました。

次に、「declare(ticks = 3){ 処理 }」の部分ですが、ここで行われる事は、『処理内の命令が「3」回実行される毎にイベントハンドラ「tick」に登録された関数を実行する』です。

命令とは、ここでは「echo」の事です。結果を見ると、命令「echo」が「3」回実行される毎に「func1()」と「func2()」の2つの関数が実行されています。

次に、「unregister_tick_function("func1");」の部分ですが、ここでは、『イベントハンドラ「tick」に登録してあった関数「func1()」を解除する』という事をしています。

これにより、イベント「tick」が発生しても、関数「func1()」は実行されなくなりました。

次に、「declare(ticks = 1);」の部分ですが、ここで行われる事は、『以降の処理内の命令が「1」回実行される毎にイベントハンドラ「tick」に登録された関数を実行する』です。

結果を見ると、命令「echo」が「1」回実行される毎に関数「func2()」のみが実行されています。

また、結果を見ると、関数の実行のされ方にちょっとクセがあるみたいです。2番目の方では最初と最後に必ず1回は関数が実行されています。

ticks

「・・・。アレェ??イベント「tick」が発生する毎に関数実行するって言ってっけどよ、「tick」って一体どういうイベントなんだ?オイ!コノ野郎!「tick」って一体どういうイベントなんだよ!」
「・・・お客様!私、入社したばっかりなので解かりません!」
「仕事を下さい。」

イベントというかなんというか、“イベントハンドラみたいな感じのもの”なので、「tick」がどういうイベントかって説明するのは難しいのですが、強いて言えば「指を鳴らす」でしょうか。指が鳴らされる度に登録された関数がコールされるというイメージで。

declare文」の「()(丸括弧)」内に指定できるイベントは「ticks N(N:自然数)」というものだけです。これは、『命令が「N」回実行される毎に登録された関数をコールする』という“イベントハンドらみたいな感じのもの”です。

この「tick」に関数を定義、解除する関数がそれぞれ「register_tick_function(関数名);」と「unregister_tick_function(関数名);」です

なお、関数は何個でも登録出来ます。また、同じ名前の関数を登録すると、登録した回数分、毎回実行されます。

おさらいと予告

これで、制御構造の解説は終了です。

制御構造とはパート2

  • PHPスクリプトを機能ごとに別々にファイルを作成して、他のファイルから読み込む事が出来る。それを実現するのが「include文」、「require文」、「include_once文」、「require_once文」の4つ。
  • これら4つは、ファイル読み込みに成功したか、読み込もうとしているファイルが既に読み込み済みかによって挙動が変わる。
  • 読み込まれるファイル内に「return」を書くとその時点でそのファイルの処理は終了し、読み込み元のファイルに処理を戻す。また、パラメータを指定すればそれがそのファイルの返り値として受け取れる。指定しなければ成功したか否かを表す真偽値を返す。
  • ファイルを読み込む際の、ファイルの指定の仕方が「File」という形の場合、読み込まれるファイルは「読み込み元のファイルと同じディレクトリ内のファイル」ではなく、「「include_path」に指定されたディレクトリ内のファイル」になる。
  • declare文」では、イベントハンドラ「tick」に登録された関数を、指定した回数、命令が実行される毎にコールする。

感動のフィナーレ!いよいよ最終回です

次回は、解説し損ねた事柄をビラーッと解説して最終回です。

作成日:2004年05月22日 最終更新日:2004年05月24日
【印刷モード風モード で表示】
ホーム > プロジェクトP > PHPの基本 > 制御構造2