| [ホーム] -> [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」にはマッチしません。
|
| \ | この文字の後ろに、正規表現の特殊文字を置くと、それらは、通常の文字として解釈されます。 |