PHPにおける関数:同じような処理をまとめて扱うしくみ

2015年3月11日(水)
野田 貴子

関数の使い道(2)

先ほど、関数を使う利点を2つ挙げました。覚えていますか?

(1)同じ処理を書いたはずが間違ってしまうという可能性を防ぐことができる
(2)修正するときに一ヶ所を修正するだけで済む

関数を使う利点は、これだけではありません。

(3)プログラムの全体的な流れが見やすくなる

1回しか行わない処理であっても、関数にまとめることがよくあります。それは、プログラム全体の流れを把握しやすくするためです。少し長いですが、次のプログラムを見てください。何が目的のプログラムなのか判断しにくいと思います(分かりやすくするために日本語で書いていますが、このままではもちろん動きませんよ!)。

if (ユーザー名が入力されていない場合) {
    エラー処理をする;
    プログラムを終了する;
}
if (ユーザー名が短すぎる場合) {
    エラー処理をする;
    プログラムを終了する;
}
if (ユーザー名が長すぎる場合) {
    エラー処理をする;
    プログラムを終了する;
}
if (ユーザー名に英数字以外の文字が含まれている場合) {
    エラー処理をする;
    プログラムを終了する;
}
if (ユーザー名に不適切な文字列が含まれている場合) {
    エラー処理をする;
    プログラムを終了する;
}
if (パスワードが入力されていない場合) {
    エラー処理をする;
    プログラムを終了する;
}
if (パスワードが短すぎる場合) {
    エラー処理をする;
    プログラムを終了する;
}
if (パスワードが長すぎる場合) {
    エラー処理をする;
    プログラムを終了する;
}
if (パスワードに英数字以外の文字が含まれている場合) {
    エラー処理をする;
    プログラムを終了する;
}
if (確認用パスワードが入力されていない場合) {
    エラー処理をする;
    プログラムを終了する;
}
if (確認用パスワードがパスワードと一致しない場合) {
    エラー処理をする;
    プログラムを終了する;
}
データベースへ接続する;
if (データベースへ接続できなかった場合) {
    エラー処理をする;
    プログラムを終了する;
}
データベースのトランザクション(邪魔が入らないようにする仕組み)を開始する;
ユーザー名とパスワードをデータベースに保存する;
if (データベースに保存できなかった場合) {
    エラー処理をする;
    プログラムを終了する;
}
データベースのトランザクションを終了する;
ユーザー登録完了画面を表示する;

読みにくいので、これを最後までまじめに読んでくれた方は、おそらくほとんどいないでしょう。これを関連する処理ごとに関数にまとめてみたものが以下になります。ぐっと読みやすくなりましたよね。関数の宣言を読まなくても、この処理の流れを理解することができます。

// 新規ユーザーの入力値をチェックする
$result = check_input_values(); //関数(1)
if ($result === false) {
    エラー処理をする;
    プログラムを終了する;
}
// データベースに新規ユーザーの情報を保存する
$result = save_userdata(); //関数(4)
if ($result === false) {
    エラー処理をする;
    プログラムを終了する;
}
ユーザー登録完了画面を表示する;
// 処理終了

// 関数(1):入力値をチェックする
function check_input_values() {
    // ユーザー名をチェックする
    $result = check_input_username(); //関数(2)
if ($result === false) {
        return false;
}
    // パスワードをチェックする
    $result = check_input_password(); //関数(3)
if ($result === false) {
        return false;
}
return true;
}

// 関数(2):ユーザー名をチェックする
function check_input_username() {
    if (ユーザー名が入力されていない場合) {
    else if (ユーザー名が短すぎる場合) {
    else if (ユーザー名が長すぎる場合) {
    else if (ユーザー名に英数字以外の文字が含まれている場合) {
    else if (ユーザー名に不適切な文字列が含まれている場合) {
        return false;
    }
    return true;
}

// 関数(3):パスワードをチェックする
function check_input_password() {
    if (パスワードが入力されていない場合) {
    else if (パスワードが短すぎる場合) {
    else if (パスワードが長すぎる場合) {
    else if (パスワードに英数字以外の文字が含まれている場合) {
    else if (確認用パスワードが入力されていない場合) {
    else if (確認用パスワードがパスワードと一致しない場合) {
        return false;
    }
    return true;
}

// 関数(4):データベースに新規ユーザーのデータを保存する
function save_userdata() {
    データベースへ接続する;
    if (データベースへ接続できなかった場合) {
        return false;
    }
    データベースのトランザクション(邪魔が入らないようにする仕組み)を開始する;
    ユーザー名とパスワードをデータベースに保存する;
    if (データベースに保存できなかった場合) {
        return false;
    }
    データベースのトランザクションを終了する;
    return true;
}

(4)関数の中身を別の人に書いてもらうことができる

ユーザー定義関数は、別のファイルに分けて書いておき、使うときに読み込むことができます。つまり引数に何を渡すのかと、関数が何を返すのかさえ決めておけば、その関数は自分で書かずに別の人に書いてもらうことができます。うまく分業できるということですね。

関数内での変数のスコープ

どのプログラミング言語でも、その言語の「スコープ」のルールは絶対に押さえておくべきです。スコープとは変数などの影響範囲のことです。これが分からないと、思うようにコードが書けません。

まずこのコードを見て、最後の行で何が出力されるか当ててみてください。

$hello = "こんにちは";
function hello_in_english() {
    $hello = "hello";
}
echo $hello;

正解は「こんにちは」です。関数「hello_in_english」はただそこに宣言されているだけで、実行されていません。存在しないのと同じです。

ではこれはどうでしょう。

function hello_in_english () {
    $hello = "hello";
}
$hello = "こんにちは";
hello_in_english ();
echo $hello;

これも「こんにちは」と出力されます。関数「hello_in_english」が実行されていますが、関数の中の$helloと関数の外の$helloは、名前は同じでも別物です。PHPのスコープでは、関数の中で同じ名前の変数をいじくり回しても、関数の外の同じ名前の変数に影響を与えません。このおかげで、他の人が作った関数の中にどんな名前の変数があるのかを気にせずに、安心してその関数を使うことができるのです。

少し難易度を上げます。これは何が出力されるでしょうか。

function hello_in_english ($hello) {
    $hello = "hello";
}
$hello = "こんにちは";
hello_in_english ($hello);
    echo $hello;

これも答えは「こんにちは」です。関数「hello_in_english」の引数の$helloを指定していますが、これは変数$helloを関数に渡しているのではなくて、変数の中身の値「こんにちは」を渡しています。そのため、関数「hello_in_english」の中では最初$helloに「こんにちは」が入りますが、すぐに「hello」で上書きされます。しかしその$helloは何かに使われることもなく、関数が終了しています。つまりこの関数は、実質何もしていません。

それでは、次の処理の結果はどうなるでしょうか。

function hello_in_english () {
    echo $hello;
}
$hello = "こんにちは";
hello_in_english ();

関数の中から関数の外の変数は扱えないので、echoの対象は空っぽになります。よって、何も出力されません。

以下のようにすれば、関数「hello_in_english」に「こんにちは」が渡されるので、「こんにちは」と出力されます。

function hello_in_english ($hello) {
    echo $hello;
}
$hello = "こんにちは";
hello_in_english ($hello);

くどいようですが、渡しているのは変数$helloではなく、変数の中身の「こんにちは」です。

実は変数自体を渡す方法もあります。少し高度な使い方であり、関数の外の変数に影響を与えてしまうので、注意して使ってください。

function hello_in_english (&$hello) {
    $hello = "hello";
}
$hello = "こんにちは";
hello_in_english ($hello);
echo $hello; // →hello

関数の宣言時に引数に「&」を付けると、変数の中身ではなく変数そのものを渡すようになります。

これまでの例を見て、「いちいち引数で渡すのは面倒だ」と思いましたか。使用目的は限られますが、引数で渡さない方法もあります。それは、グローバル変数を使う方法です。スコープの一番外側を「グローバルスコープ」と言いますが、そこで宣言された変数は以下のように指定すると、関数の中からも参照や変更ができます。

<?php
// ここがグローバルスコープ
$hello = "こんにちは";
function hello_in_english() {
    // 関数の中はまた別のスコープ
    // 関数の外の変数は参照できない
    echo $hello; // →出力なし
    // グローバル変数を通して参照できる
    echo $GLOBALS["hello"]; // →こんにちは
    // 変更もできる
    $GLOBALS["hello"] = "hello";
}
hello_in_english();
echo $hello; // →hello

$GLOBALSを使わず、グローバル変数に「global」を付けて宣言する方法もあります。最初に宣言が必要ですが、$GLOBALSよりも見た目がすっきりします。

<?php
$hello = "こんにちは";
function hello_in_english() {
    // グローバル変数の$helloを使うと宣言する
    global $hello;
    // グローバルスコープにある$helloを参照できるようになる
    echo $hello; // →こんにちは
    // 変更もできる
    $hello = "hello";
}
hello_in_english();
echo $hello; // →hello

おわりに

いかがでしたか。今回までの内容で、基本的なPHPの操作を覚えることができました。次回からは、Webアプリケーションに特有の処理が増えてきますよ。お楽しみに。それではまた。

1983年生まれ。大学卒業後、ソフトウェア開発の営業を経て、ソフトウェア開発業務に転向。現在は自社パッケージのフロントエンド開発のほか、PHPでの受託開発案件、日→英のローカライズ案件などを担当。

連載バックナンバー

Think ITメルマガ会員登録受付中

Think ITでは、技術情報が詰まったメールマガジン「Think IT Weekly」の配信サービスを提供しています。メルマガ会員登録を済ませれば、メルマガだけでなく、さまざまな限定特典を入手できるようになります。

Think ITメルマガ会員のサービス内容を見る

他にもこの記事が読まれています