[ホーム] -> [Aache + PHP + PostgreSQL 実験室] |
他の言語と同じように、一連の処理を、関数を作ってまとめることができます。次のように宣言します。
function 関数名(引数のリスト) { 行いたい処理; 行いたい処理; return 戻したい値; }
引数が複数ある場合は、カンマ「,
」で区切って指定します。例えば、指定された二つの引数のうち、大きい方の値を返す m_max
と言う関数を作ってみます。
function m_max($a, $b) { if ($a > $b) { $large = $a; } else { $large = $b; } return $large; }
これで、関数が定義されました。呼び出すときは、他の関数と同じように、次のようにします。
$x = 10; $y = 3; $max = m_max($x, $y); echo("$x と $y の大きい方の値は $max です。");
このユーザ定義関数は、非常に便利ですので、良く使うようにしておきましょう。ちょっとした一連の処理でも、関数としてしておくと、たいへん便利です。まあ、ほかの言語を知っている人は当然分かってますよね。もちろん、必ずしも値を戻す必要はありませんし、引数のある関数を作る必要もありません。例えば、呼び出すと、今日の日付を出力する関数を定義します(まあ、値を戻さないから関数というのは不自然かな?)。
function m_PrintDate() { $t = time(); $s = date("Y/m/d", $t); echo($s); }
この場合は、ただ「m_PrintDate();
」と呼び出すだけです。すると、画面上に今日の日付が、表示されます。この様に、良く使う一連の作業を関数としてまとめておくと、あとで、新しくプログラムを作ったときに、再利用しやすくなります。
再利用性が、もっと高いのが、クラスという考え方です。これを理解するには、「オブジェクト指向」という考え方をする必要があります。で、本屋さんに行って、オブジェクト指向についての本を買うと、火星人が、地底人に向けて書いたような意味不明なことが書かれています。でも、実際使ってみると非常に簡単なので安心してください。複数の関数と、複数の変数を一まとめにしたのがクラスです。ただそれだけです。もちろん、オブジェクト指向の概念は、知識として知っておくと便利ですが、とりあえず使ってみるのが先だと思います。「結構すごいじゃないか」とか、「何でこんなことする必要があるの?」といったことを思ったら、はじめて本を買って読んでみても遅くないと思います。と、言うわけで早速クラスを作ってみましょう。関数のところで紹介した、一番大きい値を持ってくる関数の発展形を考えてみます。
class Compare { var $all_value; // 保持している値 // 値を追加する function add($v) { $this->all_value[] = $v; } // 最大の値を取得する function max() { $max = $this->all_value[0]; for ($i = 1; $i < count($this->all_value); $i++) { if ($max < $this->all_value[$i]) { $max = $this->all_value[$i]; } } return $max; } // 最小の値を取得する function min() { $min = $this->all_value[0]; for ($i = 1; $i < count($this->all_value); $i++) { if ($min > $this->all_value[$i]) { $min = $this->all_value[$i]; } } return $min; } }
このクラスは、add
によって追加された値の中から、max
を呼び出されたら最大の値、min
を呼び出されたら最小の値を返すようなクラスです。注意する点は、クラス内全体で使う変数(ここでは all_value
)は、var
を使って宣言する必要があります。そして、クラスの持っている変数にアクセスするときは、$this->変数名 としている点です。ちなみに、このクラスを使うには次のようにします。
$comp = new Compare; $comp->add(10); $comp->add( 5); $comp->add(20); $max = $comp->max(); $min = $comp->min(); echo("一番大きいのは $max です。\n"); echo("一番小さいのは $min です。\n");
ポイントは、new
演算子です。これにより、そのクラスの「インスタンス」が作成されます。作成された Compare
クラスのインスタンスを、変数 comp
に格納して、以降の Compare
クラスの関数には、-> を使ってアクセスします。
大まかな使い方はこんなところです。うーん、ちょっと、ピント来ないかな?
ところで、プログラムを作っているとよくあることですが、このクラスに、新しい機能を追加したくなったとします。指定された値より大きい値すべてを返す関数が欲しくなりました。クラスを利用している場合、これを実現するには二つの方法があります。一つは、Compare
クラスを変更して関数を追加する方法。もう一つは、Compare
クラスを継承して、新しいクラスを作る方法です。ここでは、2番目の継承を使う方法を説明しましょう。なんたってオブジェクト指向ですから。Compare
クラスを継承して、MoreCompare
クラスを作ります。
class MoreCompare extends Compare { // 指定された引数より大きい値すべてを返す function over($v) { for ($i = 0; $i < count($this->all_value); $i++) { if ($v < $this->all_value[$i]) { $ret[] = $this->all_value[$i]; } } return $ret; } } // MoreCompare を使う $comp = new MoreCompare; $comp->add(10); $comp->add( 5); $comp->add(20); $max = $comp->max(); $min = $comp->min(); $r = $comp->over(7); echo("一番大きいのは $max です。\n"); echo("一番小さいのは $min です。\n"); echo("7 より大きいのは ".count($r)." 個あります。\n");
この様に継承したクラスは、新しい関数と、継承した元のクラスの両方の関数が使えます。これのメリットは、元のクラスを修正せずに、機能追加を行える点にあります。この程度の大きさのクラスであれば、修正も容易ですが、もっと大きなクラスになると、修正もたいへんになります。また、修正した場合、それらのクラスを使っているプログラムを、テストする必要がありますが、継承して新たに定義するのであれば、元のクラスは変更されていないので、テストする必要もありません。つまり、元のプログラムの信頼性を損なうこと無く、新たな機能の追加が行えるのです。
もう一つ、クラスには「コンストラクタ」と呼ばれる特別な関数があります。コンストラクタは、クラス名と同じ名前の関数 です。そして、コンストラクタは、クラスのインスタンスが作成されるとき、自動的に呼び出されます。ここでは、Compare
クラスを改造して、コンストラクタを追加します。
class Compare { // 追加部分 - はじめ function Compare($v = NULL) { // $v の初期値を NULL(「何もない」値) if (is_null($v)) { // NULL は引数が省略された場合なので、何もしない } elseif (is_array($v)) { // 配列を指定した場合は、そのまま代入 $this->all_value = $v; } else { // 普通の変数の場合は、配列の要素として代入 $this->all_value[] = $v; } } // 追加部分 - 終わり // 後は同じ } // Compare を使う(コンストラクタを利用) $comp = new Compare(array(10, 5, 20)); $max = $comp->max(); $min = $comp->min(); echo("一番大きいのは $max です。\n"); echo("一番小さいのは $min です。\n"); // Compare を使う(コンストラクタを省略) $comp = new Compare; $comp->add(10); $comp->add( 5); $comp->add(20); $max = $comp->max(); $min = $comp->min(); echo("一番大きいのは $max です。\n"); echo("一番小さいのは $min です。\n");
これらの例だけでは、クラスのメリットがあまり見えてこなかったかも知れませんが、クラスを使うことにより、機能追加などの保守性、またコードの再利用がしやすくなるのです。ただの関数より、クラスの方が、すべてにおいて勝っているとは思っていませんが、使いこなせば非常に強力だと思います。ぜひ、使ってみてください。
変数には、その変数が使える範囲があります。このことを、スコープと言ったりします。PHP のスコープは、大きく2種類あります。一つは、グローバルスコープで、PHP の中全体で使えるものです。もう一つはローカルスコープで、これは、ユーザ定義関数の中のスコープです。例えば次の例を見てみます。
1 function test() { 2 $a = 1; 3 } 4 test(); 5 echo($a);
test
という関数を定義して、その中で「a
」という変数に「1
」を代入します。そして、最後に変数 a
の値を表示しています。この時何が表示されるでしょうか? 答えは、「何も表示されない」です。これは、2 行めが、test
の中(ローカルスコープ)で、その外側(グローバルスコープ)からは参照できないからです。もちろん、それぞれのローカルスコープは、独立しています。
1 function test1() { 2 $a = 1; 3 } 4 function test2() { 5 echo $a; 6 } 7 test1(); 8 test2();
という例も、test1
と test2
はそれぞれ別のスコープなので、5 行めが実行されても何も表示されません。では、つぎの場合はどうでしょう。
1 $a = 1; 2 function test() { 3 $a = 2; 4 } 5 test(); 6 echo $a;
この場合は、いくつが表示されるでしょうか? 答えは、「1」が表示されます。これは、1 行めの変数 a
は、グローバルスコープで定義されているのに対し、3 行めの a
は、ローカルスコープで定義されているので、名前は同じでも、値の格納先が違います。ここら辺は、C や perl と違うところですね。では、もし関数の中(ローカルスコープ)から、1 行めの変数 a
の値(グローバルスコープにある)を変更したい場合は、どうしたらいいでしょうか。次の例を見てください。
1 $a = 1; 2 function test() { 3 global $a; 4 $a = 2; 5 } 6 test(); 7 echo $a;
これで、うまくいきます。ポイントは 3 行めの global
です。これで、「変数 a
はグローバルスコープのものだよ」と宣言することにより、関数の外側(グローバルスコープ)の値をアクセスできるようになります。したがって、この例では 7 行めが実行されると 2 が表示されます。ちなみに、その逆、つまりローカルスコープの値を、グローバルスコープからアクセスすることはできません。まあ、当然と言えば当然ですけどね。
ちなみに、このスコープという概念は、ほとんどの言語で存在します。この目的は、何と言っても、変数の値の保護です。プログラムが大きくなればなるほど、変数の値が、思いもよらないところで書き換えられてしまうものです。似たような名前の変数の場合、ちょっとしたタイプミスで、違う変数の値を書き換えてしまったりします。いろいろプログラムを作っていくと分かりますが、x
や、i
と言った変数は、プログラム中、いたるところで使用します。なるべく思うように動かすためには、これらの変数の有効範囲を狭めて、安全性を保つ必要があります。関数を呼び出したら、勝手に変数 i
の値が書き変わったら大変ですよね。だから、なるべくこのスコープを有効に使うために、グローバルスコープの変数はなるべく使わないようにしたり、なるべく ユーザ定義関数をつくって、スコープを分割したりする努力をおすすめします。まあ、ここら辺は、各人のプログラミングのスタイルや好みによって、変わってきますけど。プログラミングになれている人は、スタイルを確立しているでしょうけど、初心者の方は、なるべくこういったことを心掛けてください。後から、こういうクセを直すのって大変です・・・。
PHP の変数の型は、他の言語とは違い、その変数が使用される文に応じて自動的に決定されます。内部的には、論理値、整数、浮動少数点数、文字列、配列、オブジェクト、リソース、ヌル(NULL
)の8つの種類があります。変数に格納するときに、ダブルクォーテーション「"
」(シングルクォーテーション「'
」でもいいですが)によって囲んだ文字列を代入した場合は文字列型に、数値の間にピリオド「.
」がある値を代入した場合は浮動少数点数、数値のみの場合は整数として格納されますが、変数を参照する場合には別の型で取り出すことも可能です。
1 $foo = "1"; // $foo は文字列 "0" です。 2 $foo += 2; // $foo は整数 3 です。 3 $foo = $foo + 1.3; // $foo は浮動少数点数 3.3 です。 4 $foo = 5 + "10 Little Piggies"; // $foo は整数 15 です。
1行めはいいとして、2行めは、値は整数の 3 になります。これは、変数 foo
が文字列ですが、+= 2
した結果の数値に変換され、格納されるのでしょう。それに対して、3行めは、整数と浮動少数点数の演算を行った場合は、浮動少数点数として格納されています。4行めは、整数と文字列の演算ですが、文字列の方が整数に変換され、整数同士の足し算の結果が整数として代入されています。
上の例から考えると、優先順位は、浮動少数点数、整数、文字列の順で、それぞれ優先順位が違う型の演算の場合は、優先順位の高い方の型に変換されて演算されるようです。
論理値(boolean
)についてですが、これは PHP 4 から導入された型で、TRUE
と FALSE
という値を持ちます。if
文などで使う ==
演算子などの結果が、この型になります。
$s1 = 10; $s2 = 10; $ret = ($s1 == $s2); // $ret は論理値(ここでは TRUE) if ($ret == TRUE) { // 冗長 ($ret == TRUE) == TRUE ... きりがない echo "TRUE\n"; } if ($ret) { // 上の $ret == TRUE と同じ echo "TRUE\n"; }
また、他の型から、論理値に変換されるときは、次のような規則で変換されます。
$v = 0; // 整数の 0 if (! $v) { echo("`$v' is FALSE\n"); } // 整数の 0 は FALSE です $v = 0.0; // 浮動小数点数の 0.0 if (! $v) { echo("`$v' is FALSE\n"); } // 浮動小数点数の 0.0 は FALSE です $v = ""; // 空の文字列 if (! $v) { echo("`$v' is FALSE\n"); } // 空の文字列は FALSE です $v = "0"; // 文字列 "0" if (! $v) { echo("`$v' is FALSE\n"); } // なんと文字列 "0" も FALSE です $v = array(); // 空の配列 if (! $v) { echo("`$v' is FALSE\n"); } // 空の配列は FALSE です $v = NULL; // NULL 値 if (! $v) { echo("`$v' is FALSE\n"); } // NULL 値は FALSE です class A { function x() { echo("A.x()"); } } $v = new A; // 変数を持っていないクラス if (! $v) { echo("`$v' is FALSE\n"); } // 変数を持っていないクラスは FALSE です
ここであげたもの以外は、TRUE
として変換されます。
PHP も UNIX 生まれの他のソフトと同じように、正規表現を使用することができます。正規表現とは何かを説明するのは、気力がないので許してください(うーん)。文字を検索したりするときの、ワイルドカードが強力になったものだと思ってください。PHP では、正規表現をサポートしている関数群が2種類あります。一つは POSIX 1003.2 に準拠しているereg, eregi, ereg_replace, eregi_replace, split, spliti
などで、ほとんどの関数は頭が ereg
で始まっています。もう一つは Perl の正規表現に準拠している、preg_match, preg_match_all, preg_replace, preg_split, preg_quote, preg_grep
などの頭が preg
で始まっている関数群です。なお、国際版の場合、日本語を扱える正規表現を実装した関数は、mb_ereg, mb_eregi, mb_ereg_replace, mb_eregi_replace
などの、頭に mb_ereg
がついているものとなります。以下に、PHP で使える POSIX 系の正規表現の一覧をあげます。抜けがあるかも知れないけど・・・。
文字 | 意味 |
---|---|
. | 何か 1 文字にマッチします。 |
? | 直前の文字の、0 個か 1 個にマッチします。 |
+ | 直前の文字の、1 個以上にマッチします。 |
* | 直前の文字の、0 個以上にマッチします。 |
^ | 行の先頭にマッチします。 |
$ | 行の末尾にマッチします。 |
[文字のリスト] |
文字のリストに指定されたいずれかにマッチします。先頭がカレット「^ 」の場合は、文字のリスト以外にマッチします。文字のリスト内に、「A-Z 」とかハイフンで指定されていた場合、範囲指定となります。つまり「A-Z 」の場合、大文字の A から Z 全てが指定されたのと同じ意味になります。ちなみに、閉じ括弧「] 」を指定したい場合は、文字のリストの先頭(あるいは先頭のカレット「^ 」の直後)に指定すれば大丈夫です。また、ハイフン「- 」を指定したい場合は、文字のリストの最初か最後の文字とすれば大丈夫です。
|
(文字列1|文字列2) |
文字列1か、文字列2にマッチします。パイプ「| 」で区切れば、文字列をもっとたくさん指定できます。
|
{数値1,数値2} |
直前の文字の、「数値1」以上「数値2」以下の繰り返しにマッチします。カンマ「, 」に続く「数値2」は省略できます。その場合は、「数値1」以上の繰り返しにマッチします。「数値1」、「数値2」共に、0 から 255 まで指定できます。両方指定した場合、数値1は、数値2より大きくてはいけません。ちなみに、「? = {0,1} 」「+ = {1} 」「* = {0} 」となります。
|
[[:クラス名:]] |
クラス名の部分には、alnum (英字または数字)、digit (数字)、punct (スペースと英数字を除く表示可能な文字)、alpha (アルファベット)、graph (スペース以外の表示可能な文字)、space (スペース、フォームフィード、改行、復帰、水平タブ、垂直タブ)、blank (スペースかタブ)、lower (小文字)、upper (大文字)、cntrl (制御文字)、print (スペースを含む表示可能な文字)、xdigit (16 進数での数字)を指定できます。C 言語の、isalnum とかと、同じです。これは例えば、「[[:alpha:]] 」を指定すると、アルファベット全てにマッチします。この表現と、「[A-z] 」の意味はほぼ同じですが、英語以外の言語(英語の26種類以外の文字がある言語の場合)の場合は、少し結果が違ってきます。
|
[[:<:]] |
単語の先頭にマッチします。例えば「[[:<:]]play 」という正規表現は、「play 」にはマッチしますが、「replay 」にはマッチしません。
|
[[:>:]] |
単語の最後にマッチします。例えば「document[[:>:]] 」の場合、「document 」にはマッチしますが、「documentary 」にはマッチしません。
|
\ | この文字の後ろに、正規表現の特殊文字を置くと、それらは、通常の文字として解釈されます。 |