[ホーム] -> [Aache + PHP + PostgreSQL 実験室]

PHP を便利に使うために

ユーザ定義関数

他の言語と同じように、一連の処理を、関数を作ってまとめることができます。次のように宣言します。

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();

という例も、test1test2 はそれぞれ別のスコープなので、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 から導入された型で、TRUEFALSE という値を持ちます。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 系の正規表現の一覧をあげます。抜けがあるかも知れないけど・・・。

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