このレッスンはただ今 無料公開中 です。

初学者向け マンツーマン プログラミング・レッスン

PHPのユーザー定義関数

前章ではPHPがはじめから持っている「組み込み関数」という機能について見てきました。
組み込み関数の引数に何かの値を渡すことで、さまざまな処理を関数がしてくれましたね。文字列の一部を置換えたり、配列を並べ替えるなど。

そのような元々用意された関数だけではなく、ユーザーが独自に関数を作って使用することもできます。これをユーザー定義関数といいます。

関数の定義

PHPのユーザー定義関数は次のような書き方で定義します。

function 関数名(引数) {
  // この中に処理内容を書いていく
  // 処理1
  // 処理2
  // ...
}

変数には好きな名前をつけられることを学びましたが、それと一緒で関数にも名前を付けることができます。 function というキーワードに続いて関数名を書き、その後ろに ( ) カッコを書きます。カッコの中には「引数」を指定します。そしてさらにその次に {} 波カッコを書きます。波カッコの開始から終了までを「ブロック」といい、ブロック内に処理の内容を書いていきます。

引数とは、この関数の中で使うことができるデータに名前をつけたものです。引数についてはまた後ほど詳しく述べます。

それではさっそく、eat という名前の関数を定義してみましょう。

func_eat.php
<?php
  function eat($food, $count, $unit) {
    $text = "私は{$food}を{$count}{$unit}食べました。";
    print $text. PHP_EOL;
  }

引数には $food $count $unit を指定しました。それぞれ「食べ物」「数量」「単位」という意味のデータを扱っています。

つまりこの関数は「私は(食べ物)を(数量)(単位)食べました」という言葉を出力する機能を果たすことになります。

ところで、見慣れない書式が突然出てきました。

$text = "私は{$food}を{$count}{$unit}食べました。";

今までは、文字列や変数(文字列型)を連結させるのに . ドットを使ってきましたが、文字列の中に変数を埋め込むこのような方法もあります。この書き方を 変数展開 と言います。 { } 波カッコで囲った中に、そのまま変数を書くことができます。ただし、これが使えるのは全体の文字列を " 二重引用符で囲った時だけです。

連結するデータが多い場合は、このような変数展開を使った方法の方が見やすくなります。今回のサンプルコードではこの方が都合が良いので、使ってみました。

関数の実行

さて関数の方に話を戻します。
今はまだ関数を「定義」しただけです。定義とは決まりごとを決めたものに過ぎません。ですのでこのプログラムを実行しても、ブロック内にかかれた処理はまだ実行されません。

それでは、この関数を実際に動かすための命令を一番最後に追加してみて下さい。

func_eat.php
<?php
  function eat($food, $count, $unit) {
    $text = "私は{$food}を{$count}{$unit}食べました。";
    print $text. PHP_EOL;
  }
  eat('みかん', 3, '個'); // 追加

これを実行してみましょう。

私はみかんを3個食べました。

追加したこの1文がeat()関数を呼び出したことで、関数内の処理が実行されたことがわかります。

eat('みかん', 3, '個');

これを関数の「呼び出し元」とも言います。そして関数の定義の方を「呼び出し先」と言います。

// 呼び出し先
function 関数名(引数) {
  // 処理
}

// 呼び出し元
関数名(引数)

関数は定義しただけでは何も起こりません。呼び出すことによって初めて、ブロック内に書かれた内容が実行されることを覚えておいて下さい。

仮引数と実引数

ここで改めて「引数」について見ていきましょう。

呼び出し元の eat() のカッコ内には3つの値をカンマ区切りで順番に指定しました。

eat('みかん', 3, '個');

このようにしてカッコ内に引数を指定して関数を呼び出すと、呼び出し先側でその値を受け取ることができます。

<?php
function eat($food, $count, $unit) {

}

従って、

func_eat.php
<?php
  function eat($food, $count, $unit) {
    $text = "私は{$food}を{$count}{$unit}食べました。";
    print $text. PHP_EOL;
  }
  eat('みかん', 3, '個');

このコードの実行結果が

私はみかんを3個食べました。

となったのです。

ところで、呼び出し元・呼び出し先のいずれも「引数」という言葉で言い表していますが、一方は「渡す側」、もう一方は「受け取る側」と、お互い異なる立場にありますね。なので、次のように両者を区別して呼ぶこともあります。

  • 呼び出し元の引数・・・実引数
  • 呼び出し先の引数・・・仮引数

関数の定義であらかじめ「受け皿」としての引数を仮に用意しておき、呼び出し元から「実際の値」が渡されます。
実際の値が入って方が実引数で、予め用意された受け皿が仮引数と覚えておくと良いですね。

// 呼び出し先(定義)
function 関数名(仮引数1, 仮引数2, ...) {

}

// 呼び出し元
関数名(実引数1, 実引数2, ...);

引数は何個まで?

さて、引数の「個数」についても触れておきましょう。今回のサンプルコードでは $food count unit という3つの引数を使いました。いったい引数はいくつまで使用できるのでしょうか?

正解はいくつでも構いません。必要な分だけ10個でも20個でも指定できます。(とはいえ、普通はそのようなわかりにくい使い方はしませんが)

指定した順番に「第1引数」「第2引数」「第3引数」... という風に呼びます。

ただし、仮引数と実引数の数は一致させなければなりません。

例えば次のように実引数を2個にしてしまうと、このプログラムはエラーを起こします。

func_eat.php
<?php
  function eat($food, $count, $unit) {
    $text = "私は{$food}を{$count}{$unit}食べました。";
    print $text. PHP_EOL;
  }
  eat('みかん', 3); // 2個に減らした

PHP Fatal error:  Uncaught ArgumentCountError: Too few arguments to function eat(), 2 passed in 〜(略)

訳)引数の数に関するエラー:eat()関数に対して引数が少なすぎます。2つしか引数が与えられていません。該当箇所は 〜(略)

このような結果になります。なので、仮引数側で指定された個数の値を実引数側で指定するようにしましょう。

引数のデフォルト値

実は仮引数と実引数の数が異なる場合でもエラーにさせない方法があります。仮引数にデフォルト値(初期値)を与えておくことで、実引数が省略された場合はデフォルト値を使用するように設計しておくことができます。

func_eat.php
<?php
  function eat($food, $count, $unit = '個') { // 第3引数にデフォルト値を与えた
    $text = "私は{$food}を{$count}{$unit}食べました。";
    print $text. PHP_EOL;
  }
  eat('みかん', 3); // 実引数は2個

第3引数 $unit というデフォルト値を設定しました。これにより、実引数の数が2個しかなくてもプログラムはエラーを吐くことなく、正常に動作するようになりました。呼び出し元で省略された第三引数は、呼び出し先ではデフォルト値を使って処理が進んでいくことになります。

実行結果

私はみかんを3個食べました。

あくまでも「デフォルト値」なので、第三引数に任意の値を指定すれば、そちらを使用してくれます。

eat('みかん', 3, 'つ');

私はみかんを3つ食べました。

関数のメリットとは?

さてこれまでのサンプルコードでは、1つの関数を定義して、その関数を1回だけ呼び出していました。

これではまだ、関数を使う理由やメリットがわかりにくいですね。

実は関数は、1度定義しておくとあとから何度でも使うことができるのです。この「使い回し」こそが、関数を利用する一番の利点です。

変数に似ていますね。変数は「値」を一度代入しておくとあとから使いまわしが出来るようになりました。関数では「処理」を定義しておき、後から何度でも呼び出すことが出来るようになります。

func_eat.php
<?php
  function eat($food, $count, $unit) {
    $text = "私は{$food}を{$count}{$unit}食べました。";
    print $text. PHP_EOL;
  }
  eat('みかん', 3, '個');
  eat('プリン', 2, '個');
  eat('バナナ', 1, '本');

eat()関数を合計3回呼び出しました。ただし3回とも引数の内容は変えてあります。

実行結果は

私はみかんを3個食べました。
私はプリンを2個食べました。
私はバナナを1本食べました。

となりました。

関数とはこのように「機能」を決めておき、受け取った引数の中身に応じてふるまいを変えていくことができるのです。

2回や3回の呼び出しくらいならば、関数を使わないでも直接書くことはまだ容易いです。しかし、これが 100 回や 1000 回だったらどうでしょう。ソースコードは恐ろしく長くなりますし、例えば最後の「食べました」の部分をあとから「食べたよ!」に変更することになった場合、100箇所、1000箇所を修正しなければなりません。
しかし関数を使っておけば、修正はたったの1箇所だけです。

そしてもうひとつ、関数がもっと便利になる使い方があります。

return文で戻り値を返す

例えば次のような機能を持った関数があったとしましょう。引数で受け取った2つの数値「底辺の長さ」と「高さ」から三角形の面積を求める関数です。

func_triangle.php
<?php
  function triangle_area($bottom, $height) {
    $area = $bottom * $height / 2;
    print "面積は{$area}平方センチメートルです". PHP_EOL;
  }

  triangle_area(15, 7);

↓実行結果

面積は52.5平方センチメートルです。

三角形の面積を求め、それをこのような書式で出力するだけの役割ならば、この関数はこれで十分です。ですが、そのようなケースは少ないのではないでしょうか。求めた面積の値を使ってまた別の計算をしたり、変数に保持しておいて後でまた別のところで使うなどの用途が多いと思います。

つまり、triangle_area()関数には「三角形の面積の計算」部分だけを任せることにして、計算結果を使いまわしたい場合です。

func_triangle.php
<?php
  function triangle_area($bottom, $height) {
    $area = $bottom * $height / 2;
    return $area;
  }

  $area_A = triangle_area(15, 7);
  $area_B = triangle_area(8, 12);

  print "三角形Aの面積は" . $area_A . PHP_EOL;
  print "三角形Bの面積は" . $area_B . PHP_EOL;

  print "2つの面積の合計は";
  print $area_A + $area_B . PHP_EOL;

AとBという2つの三角形の面積を求め、さらにその和を求めました。

このようなふるまいを実現してくれたのが return 文です。

function triangle_area($bottom, $height) {
  $area = $bottom * $height / 2;
  return $area; // ←これ
}

戻り値とは

上記のサンプルコードのように、関数では return文 を用いて処理結果を呼び出し元に返す使い方をすることが多いです。この返却される値のことを「戻り値(または返り値)」と呼びます。

func_triangle.php
<?php
  function triangle_area($bottom, $height) {
    $area = $bottom * $height / 2;
    return $area;
  }

  $area_A = triangle_area(15, 7); // 戻り値「52.5」が変数 $area_A に代入される
  $area_B = triangle_area(8, 12); // 戻り値「48」が変数 $area_B に代入される

  print "三角形Aの面積は" . $area_A . PHP_EOL;
  print "三角形Bの面積は" . $area_B . PHP_EOL;

  print "2つの面積の合計は";
  print $area_A + $area_B . PHP_EOL;

triangle_area()関数は「三角形の面積を計算する」という役割に特化しています。計算結果は呼び出し元へ「戻り値」として返すので、あとはそれを変数に代入するなり、煮るなり焼くなり好きにして下さい、という姿勢です。このように役割を分けることで関数の汎用性は高まり、より一層使い回しがしやすくなります。

練習問題1 BMI計算機

func_bmi.phpファイルを新規作成して「身長と体重からBMIを計算する関数」を作成して下さい。なお、BMIとは Body Mass Index の略で「肥満度」を手軽に測る指標として使われます。計算式は次の通りです。(身長がメートルである点に注意して下さい)

BMIの計算式

BMI = 体重 kg ÷ (身長 m × 身長 m)

関数の仕様

  • 関数名は calc_bmi とすること
  • 第1引数は身長をセンチメートルで、第2引数は体重をキログラムで受け取ること
  • 計算結果の小数点以下は切り捨て、整数値を返すこと
  • 計算したBMI値は戻り値として呼び出し元に返却すること
  • 返却された戻り値であるBMIをコンソールに出力すること

小数点以下を切り捨てる方法はこのカリキュラムでは学んでいません。自身で検索して調べて下さい

php 切り捨て
func_bmi.php
<?php
  function calc_bmi($height, $weight) {
    // 身長をセンチメートルからメートルに換算

    // BMIを計算

    // BMIの小数点以下を切り捨て

    // BMIを返す

  }

  print calc_bmi(172, 64) . PHP_EOL;

練習問題2 BMI計算機その2

練習問題1のBMI関数をもっと活用しましょう。

  • 田中さん・鈴木さん・佐藤さんの3人分の身長・体重データを連想配列で予め用意してください。
  • ループ処理によって1人づつの身長・体重を関数に渡して、戻り値を受け取って下さい
  • 最後に3人の名前とBMIをコンソールに出力して下さい。
<?php
  function calc_bmi($height, $weight) {
    // 練習問題1の内容
  }

  $members = [
    [
      'name' => '田中',
      'height' => 172,
      'weight' => 69,
      'bmi' => null,
    ],
    [
      'name' => '鈴木',
      'height' => '158',
      'weight' => '46',
      'bmi' => null,
    ],
    [
      'name' => '佐藤',
      'height' => '168',
      'weight' => '72',
      'bmi' => null,
    ],
  ];

  // TODO : foreach文を使って$membersそれぞれのBMIを取得・表示
  foreach () {

  }

実行結果としてコンソールに次の通りに出力されれば正解です。

田中さんのBMIは23です。
鈴木さんのBMIは18です。
佐藤さんのBMIは25です。

練習問題3 FizzBuzzゲーム

6章「分岐処理」及び7章「繰返し処理」の練習問題でやったFizzBuzzゲームを、関数を使った仕様に改良してみましょう。以前の練習問題が正しくできていないと、この問題を解くのは難しいです。クリアしていない人は6章7章を復習しておきましょう。

制約を設けておきます。次の仕様を満たすようにして下さい。

  • 関数名は fizzbuzz とする
  • あらかじめ変数 $max に50を代入しておく
  • $max値の回数分だけ fizzbuzz() 関数を呼び出す
  • fizzbuzz関数は受け取った引数が3の倍数の場合は Fizz を返す
  • 同様に引数が 5 の倍数の場合は Buzz を返す
  • 同様に引数が 3 の倍数であり、かつ 5 の倍数でもある場合は FizzBuzz を返す
  • 3 か 5 の倍数ではない場合はそのまま引数を返す
  • 関数の戻り値を呼び出し元で出力する
func_fizzbuzz.php
<?php
  $max = 50;
  function fizzbuzz() {

  }

  // 関数を $max 回数呼び出し、戻り値を出力する

望まれる出力結果

1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
11
Fizz
13
14
FizzBuzz
16
17
Fizz
19
Buzz
Fizz
22
23
Fizz
Buzz
26
Fizz
28
29
FizzBuzz
31
32
Fizz
34
Buzz
Fizz
37
38
Fizz
Buzz
41
Fizz
43
44
FizzBuzz
46
47
Fizz
49
Buzz

まとめ

長くなりましたが、ユーザー定義関数についてこの章で学んできました。前章までと比べても、だいぶ歯ごたえのある内容だったのではないでしょうか。

いよいよ次章からはPHPの「クラス」について学んでいきます。クラスはこれまで学んできたことの集大成とも言える内容です。特に、関数の使い方について十分に理解していなければ、クラスの理解に至ることも難しいでしょう。

難しいな、と感じたらいつでもこの章に戻ってきて、復習をするようにしましょう。