- PHP
PHP 関数の書き方|引数・デフォルト値・型宣言・戻り値を一覧で解説
PHP の関数は、function 名前(型 $引数 = デフォルト値): 戻り値の型 { return 値; } の形で書きます。 引数の型・デフォルト値・戻り値の型はどれも省略できますが、PHP 8.x では型を書いておくと、渡し間違いをその場でエラーとして知らせてくれるので、読みやすく直しやすいコードになります。
この記事では、関数の定義から、型宣言、戻り値型(void・never・nullable・union)、デフォルト値、可変長引数、参照渡し、名前付き引数、無名関数・アロー関数・第一級 callable 構文までを、PHP 8.4.21 で実際に動かした結果とエラー文つきで順に解説します。冒頭に書き方の早見表と対応バージョン表を置いたので、必要な書き方をすぐにコピーできます。
PHP 関数の書き方 早見表
書き方の一覧
| やりたいこと | 書き方 | 呼び出し例 |
|---|---|---|
| 基本の関数 | function greet($name) { return "Hi, $name"; } |
greet('佐藤') |
| 引数に型を付ける | function subtotal(int $price, int $count) |
subtotal(1200, 3) |
| 戻り値に型を付ける | function subtotal(int $p, int $c): int |
戻り値は必ず int |
| デフォルト値 | function greet(string $name, string $greeting = 'こんにちは') |
greet('佐藤') |
| null も受け取る | function find(?int $id) |
find(null) |
| null を返すことがある | function findName(int $id): ?string |
戻り値は string か null |
| 複数の型を受け取る | function toLabel(int|string $code) |
toLabel(7)、toLabel('A7') |
| 何も返さない | function logLine(string $m): void |
戻り値は NULL |
| 決して戻らない | function stop(string $r): never |
必ず例外か exit で終わる |
| 可変長引数 | function sum(int ...$numbers) |
sum(1, 2, 3) |
| 配列を展開して渡す | (呼び出し側で)sum(...$list) |
$list = [10, 20, 30] |
| 参照渡し | function addTax(int &$price) |
addTax($price) で元の変数が変わる |
| 名前付き引数 | (呼び出し側で)greet(greeting: 'やあ', name: '田中') |
順番を入れ替えられる |
| 無名関数 | $f = function (int $p) use ($rate) { ... }; |
$f(1000) |
| アロー関数 | $f = fn(int $p) => $p * 2; |
$f(1000) |
| 第一級 callable 構文 | $f = strlen(...); |
$f('hello') |
対応バージョン表
| 機能 | 使えるバージョン | 確認方法 |
|---|---|---|
| スカラー型宣言(int・float・string・bool) | PHP 7.0〜 | PHP マニュアル「型宣言」の変更履歴を今回確認 |
| 戻り値の型宣言 | PHP 7.0〜 | 同上 |
nullable 型(?int) |
PHP 7.1〜 | 同上 |
| void | PHP 7.1〜 | 同上 |
union 型(int|string)・mixed |
PHP 8.0〜 | 同上 |
| never | PHP 8.1〜 | 同上 |
| null・false 単独の型、true 型 | PHP 8.2〜 | 同上 |
| 引数なしの呼び出しで ArgumentCountError | PHP 7.1〜 | PHP マニュアル「関数の引数」を今回確認 |
| 呼び出し時の末尾カンマ | PHP 7.3〜 | 同上 |
| 引数リスト(定義側)の末尾カンマ | PHP 8.0〜 | 同上 |
| 名前付き引数 | PHP 8.0〜 | 同上 |
| 任意引数の後に必須引数を置くと非推奨 | PHP 8.0〜 | 同上 |
デフォルト値に new を書ける |
PHP 8.1〜 | 同上 |
| 配列の展開の後に名前付き引数 | PHP 8.1〜 | 同上 |
型 $x = null の暗黙の nullable が非推奨 |
PHP 8.4〜 | 同上 |
可変長引数(...) |
PHP 5.6〜 | 一般に知られた仕様 |
アロー関数(fn) |
PHP 7.4〜 | 一般に知られた仕様 |
第一級 callable 構文(f(...)) |
PHP 8.1〜 | 一般に知られた仕様 |
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「PHP 関数の書き方|引数・デフォルト値・型宣言・戻り値を一覧で解説」
検証した環境と読み方
| 項目 | 内容 |
|---|---|
| PHP | 8.4.21(CLI) |
| 実行方法 | php -d display_errors=1 -d error_reporting=-1 ファイル名.php |
| 表示の調整 | エラー文に含まれるファイルのパスは、ファイル名だけに置き換えています |
この記事のコードは、そのままファイルに保存して php ファイル名.php で実行できる形にしています。実行結果は、すべて上の環境で実際に出力されたものです。バージョンごとの対応表は、PHP マニュアルの「型宣言」と「関数の引数」のページを今回開いて確認した内容で、PHP 8.4 より前のバージョンでの動作そのものは実行していません。
関数の基本の書き方
<?php
function greet(string $name, string $greeting = 'こんにちは'): string
{
return "{$greeting}、{$name}さん";
}
echo greet('佐藤'), PHP_EOL;
echo greet('鈴木', 'おはよう'), PHP_EOL;
echo greet(greeting: 'こんばんは', name: '田中'), PHP_EOL;
こんにちは、佐藤さん
おはよう、鈴木さん
こんばんは、田中さん
| 部品 | 例 | 意味 |
|---|---|---|
| 関数名 | greet |
英字かアンダースコアで始める。大文字と小文字は区別されない |
| 引数の型 | string $name |
受け取れる値の種類 |
| デフォルト値 | $greeting = 'こんにちは' |
省略されたときに使う値 |
| 戻り値の型 | : string |
返す値の種類 |
return |
return "..."; |
値を返して関数を終える |
1つの関数には1つの役割だけを持たせ、名前は「動詞+対象」にすると、呼び出し側が読みやすくなります。
知っておくと役立つ基本の性質
次の性質をまとめて確認しました。
<?php
// return を書かない関数は NULL を返す
function noRet() {}
var_dump(noRet());
// 関数の中から、外の変数は見えない
$site = 'memo';
function showSite() { return isset($site) ? 'あり' : 'なし'; }
echo showSite(), PHP_EOL;
// 定義より前の行からでも呼べる
echo later(), PHP_EOL;
function later(): string { return '後ろで定義しても呼べる'; }
// 関数名は大文字と小文字を区別しない
echo LATER(), PHP_EOL;
// static 変数は呼び出しをまたいで値を保つ
function nextId(): int { static $id = 0; return ++$id; }
echo nextId(), nextId(), nextId(), PHP_EOL;
// 再帰(自分自身を呼ぶ)
function fact(int $n): int { return $n <= 1 ? 1 : $n * fact($n - 1); }
echo fact(5), PHP_EOL;
NULL
なし
後ろで定義しても呼べる
後ろで定義しても呼べる
123
120
| 性質 | 結果 | ポイント |
|---|---|---|
| return なし | NULL |
値を返さないことを示すなら : void を付ける |
| 外の変数 | 見えない(「なし」) | 必要な値は引数で渡す |
| 定義の位置 | 後ろに書いても呼べる | ファイルの読み込み時に関数が登録されるため |
| 大文字と小文字 | 区別しない | 定義したとおりの表記で呼ぶと読みやすい |
| 同じ名前で2回定義 | Fatal error | 下のエラー文を参照 |
同じ名前の関数を2回定義すると、次の表示で止まりました。
Fatal error: Cannot redeclare function dup() (previously declared in 12_dup.php:2) in 12_dup.php on line 3
引数と戻り値に型を付ける(型宣言)
<?php
function subtotal(int $price, int $count = 1): int
{
return $price * $count;
}
function findName(array $members, int $id): ?string
{
return $members[$id] ?? null;
}
function toLabel(int|string $code): string
{
return 'CODE-' . $code;
}
function logLine(string $message): void
{
echo '[log] ', $message, PHP_EOL;
}
function stop(string $reason): never
{
throw new RuntimeException($reason);
}
var_dump(subtotal(1200, 3));
var_dump(subtotal(1200));
var_dump(findName([1 => '佐藤'], 1));
var_dump(findName([1 => '佐藤'], 2));
var_dump(toLabel(7));
var_dump(toLabel('A7'));
var_dump(logLine('保存しました'));
try {
stop('在庫がありません');
} catch (RuntimeException $e) {
echo get_class($e), ': ', $e->getMessage(), PHP_EOL;
}
int(3600)
int(1200)
string(6) "佐藤"
NULL
string(6) "CODE-7"
string(7) "CODE-A7"
[log] 保存しました
NULL
RuntimeException: 在庫がありません
使える型の一覧
| 型 | 書き方 | 受け取れる・返せるもの | 使える場所 |
|---|---|---|---|
| 整数 | int |
整数 | 引数・戻り値 |
| 小数 | float |
浮動小数点数(int も受け取れる) | 引数・戻り値 |
| 文字列 | string |
文字列 | 引数・戻り値 |
| 真偽値 | bool |
true / false | 引数・戻り値 |
| 配列 | array |
配列 | 引数・戻り値 |
| クラス名 | DateTimeImmutable など |
そのクラスのインスタンス | 引数・戻り値 |
| オブジェクト全般 | object |
任意のオブジェクト | 引数・戻り値 |
| 呼び出せるもの | callable |
関数名の文字列、クロージャなど | 引数・戻り値 |
| 繰り返せるもの | iterable |
配列または Traversable | 引数・戻り値 |
| 何でも | mixed |
すべての値(null を含む) | 引数・戻り値 |
| nullable | ?int |
int または null | 引数・戻り値 |
| union | int|string |
どちらかの型 | 引数・戻り値 |
| void | void |
何も返さない | 戻り値のみ |
| never | never |
戻らない(例外か終了) | 戻り値のみ |
| static | static |
呼び出されたクラスのインスタンス | 戻り値のみ(メソッド) |
戻り値型 void・never・nullable・union の違い
| 戻り値型 | 意味 | 関数の中で書けること | 守らないとどうなるか(実行結果) |
|---|---|---|---|
void |
値を返さない | return; または return なし |
Fatal error: A void function must not return a value |
never |
呼び出し元に戻らない | throw や exit で必ず終わる |
TypeError: n(): never-returning function must not implicitly return |
?string |
string か null を返す | return null; も可 |
return を書かないと Return value must be of type ?string, none returned |
string|false |
string か false を返す | 見つからないときに return false; |
既定のモードで int の 1 を返すと、文字列の "1" に変換された |
int |
必ず int を返す | return 整数; |
TypeError: noReturn(): Return value must be of type int, none returned |
void は「返す値がない」、never は「そもそも戻ってこない」という違いです。 void の関数を var_dump すると NULL が表示されますが、never の関数は例外を投げるか処理を終えるので、呼び出しの次の行には進みません。
union 型で「見つからないときは false」を表す例と、mixed の例も確認しました。
<?php
function ratio(int|float $a, int|float $b): float { return $a / $b; }
var_dump(ratio(1, 4));
function find(int $id): string|false { return $id === 1 ? '佐藤' : false; }
var_dump(find(2));
function typeName(mixed $v): string { return get_debug_type($v); }
echo typeName(1), ' ', typeName('a'), ' ', typeName(null), ' ', typeName([1]), ' ', typeName(1.5), PHP_EOL;
float(0.25)
bool(false)
int string null array float
なお、?int|string のように ? と union を組み合わせる書き方はできません。実行すると構文エラーになりました。null を含めたいときは int|string|null と書きます。
Parse error: syntax error, unexpected token "|", expecting variable in 16_nu.php on line 2
strict_typesの有無で結果が変わる例
PHP の型宣言には2つのモードがあります。既定のモード(型の自動変換あり)では、渡された値を宣言した型に変換できるなら変換して受け取ります。 ファイルの先頭に declare(strict_types=1); を書くと、変換をせず、型が合わなければ TypeError になります。
同じ関数に同じ値を渡して、両方のモードで比べました。
<?php
// declare(strict_types=1); ← この行の有無だけを変えて2回実行
function subtotal(int $price, int $count): int
{
return $price * $count;
}
function half(float $n): float { return $n / 2; }
function count2(): int { return '3'; }
// 以下を1つずつ try〜catch (TypeError $e) で囲んで実行
subtotal('1200', 3);
subtotal(1200.0, 3);
subtotal(true, 3);
subtotal(1200.5, 3);
subtotal('12a', 3);
subtotal(null, 3);
half(5);
count2();
| 渡した値 | strict_types なし(既定) | strict_types=1 |
|---|---|---|
subtotal('1200', 3) |
int(3600)(文字列が数値に変換される) |
TypeError(string given) |
subtotal(1200.0, 3) |
int(3600) |
TypeError(float given) |
subtotal(true, 3) |
int(3)(true が 1 になる) |
TypeError(true given) |
subtotal(1200.5, 3) |
Deprecated を出したうえで int(3600) |
TypeError(float given) |
subtotal('12a', 3) |
TypeError(string given) | TypeError(string given) |
subtotal(null, 3) |
TypeError(null given) | TypeError(null given) |
half(5)(float に int を渡す) |
float(2.5) |
float(2.5) |
count2()(int を返す関数が '3' を返す) |
int(3) |
TypeError(string returned) |
実際に表示されたメッセージは次のとおりです。
(strict_types なしで subtotal(1200.5, 3) を実行)
Deprecated: Implicit conversion from float 1200.5 to int loses precision in 03_weak.php on line 2
int(3600)
(strict_types=1 で subtotal('1200', 3) を実行)
TypeError: subtotal(): Argument #1 ($price) must be of type int, string given, called in 04_strict.php on line 7
(strict_types=1 で count2() を実行)
TypeError: count2(): Return value must be of type int, string returned
この結果から読み取れる要点は4つです。
- 既定のモードでは
'1200'やtrueが黙って int に変換される。 画面のフォームから届く値は文字列なので便利な反面、意図しない値が通ることもある - 小数部分のある float を int に渡すと、既定のモードでは非推奨の通知とともに切り捨てられる(1200.5 が 1200 として計算された)
- strict_types=1 では、型が一致しない値はすべて TypeError になる。 例外は「float の引数に int を渡す」場合だけで、これは両方のモードで通る
'12a'や null は、どちらのモードでも TypeError になる(nullable ではない型に null は渡せない)
PHP マニュアルの「型宣言」のページによると、strict_types はその宣言を書いたファイルの中からの関数呼び出しに効きます。関数を定義したファイルではなく、呼び出す側のファイルの宣言で決まる点が要点です。新しく書くファイルでは先頭に declare(strict_types=1); を置き、外から届いた文字列は (int) や filter_var() で明示的に変換してから渡す、という進め方がおすすめです。
デフォルト値の書き方と、置く順番
デフォルト値は $引数 = 値 の形で書き、呼び出し側が省略したときに使われます。
<?php
function createUser(string $name, int $age = 20, bool $admin = false, string $lang = 'ja'): string
{
return sprintf('%s / %d / %s / %s', $name, $age, $admin ? 'admin' : 'user', $lang);
}
echo createUser('佐藤'), PHP_EOL; // 佐藤 / 20 / user / ja
| デフォルト値に書けるもの | 例 |
|---|---|
| 数値・文字列・真偽値・null | $count = 1、$lang = 'ja'、$id = null |
| 配列 | $options = [] |
| 定数・定数式 | $limit = PHP_INT_MAX、$size = 10 * 2 |
new 式(PHP 8.1〜) |
$at = new DateTimeImmutable('2026-10-03') |
new をデフォルト値に書く例も動かしました。
<?php
function stamp(DateTimeImmutable $at = new DateTimeImmutable('2026-10-03')): string
{
return $at->format('Y-m-d');
}
echo stamp(), PHP_EOL; // 2026-10-03
任意引数の後に必須引数を置くと、非推奨の警告が出る
デフォルト値のある引数(任意引数)は、必須引数の後ろに並べます。 逆の順番にするとどうなるかを確認しました。
<?php
function makeLabel($prefix = 'ID', $number)
{
return $prefix . '-' . $number;
}
echo makeLabel('NO', 5), PHP_EOL;
try {
echo makeLabel(number: 5), PHP_EOL;
} catch (Error $e) {
echo get_class($e), ': ', $e->getMessage(), PHP_EOL;
}
Deprecated: makeLabel(): Optional parameter $prefix declared before required parameter $number is implicitly treated as a required parameter in 06_deprecated.php on line 2
NO-5
ArgumentCountError: makeLabel(): Argument #1 ($prefix) not passed
非推奨(Deprecated)の通知が出て、$prefix のデフォルト値は使われず、必須引数として扱われます。 名前付き引数で number だけを渡しても、$prefix が渡されていないという ArgumentCountError になりました。
| 直す前 | 直した後 |
|---|---|
function makeLabel($prefix = 'ID', $number) |
function makeLabel($number, $prefix = 'ID') |
型 $x = null は ?型 と書く
PHP 8.4 では、nullable ではない型に = null を付ける書き方にも通知が出ました。
<?php
function setOwner(string $owner = null) { return var_export($owner, true); }
echo setOwner(), PHP_EOL;
Deprecated: setOwner(): Implicitly marking parameter $owner as nullable is deprecated, the explicit nullable type must be used instead in 06_deprecated.php on line 9
NULL
| 直す前 | 直した後 |
|---|---|
function setOwner(string $owner = null) |
function setOwner(?string $owner = null) |
TypeErrorとArgumentCountErrorを再現する
引数の数や型が合わないときに出るエラーを、まとめて再現しました。
<?php
function subtotal(int $price, int $count): int
{
return $price * $count;
}
try { subtotal(1200); } catch (ArgumentCountError $e) { echo get_class($e), ': ', $e->getMessage(), PHP_EOL; }
try { subtotal('abc', 2); } catch (TypeError $e) { echo get_class($e), ': ', $e->getMessage(), PHP_EOL; }
var_dump(subtotal(1200, 2, 99));
try { strlen(); } catch (ArgumentCountError $e) { echo get_class($e), ': ', $e->getMessage(), PHP_EOL; }
try { strlen('a', 'b'); } catch (ArgumentCountError $e) { echo get_class($e), ': ', $e->getMessage(), PHP_EOL; }
var_dump($e instanceof TypeError);
ArgumentCountError: Too few arguments to function subtotal(), 1 passed in 05_errors.php on line 6 and exactly 2 expected
TypeError: subtotal(): Argument #1 ($price) must be of type int, string given, called in 05_errors.php on line 7
int(2400)
ArgumentCountError: strlen() expects exactly 1 argument, 0 given
ArgumentCountError: strlen() expects exactly 1 argument, 2 given
bool(true)
| 状況 | 自分で定義した関数 | 組み込み関数(strlen など) |
|---|---|---|
| 引数が足りない | ArgumentCountError(Too few arguments) | ArgumentCountError |
| 引数が多い | エラーにならず、余った引数は使われない(int(2400)) |
ArgumentCountError(expects exactly 1 argument, 2 given) |
| 型が合わない | TypeError | TypeError |
最後の bool(true) が示すとおり、ArgumentCountError は TypeError の一種です。 catch (TypeError $e) と書けば両方をまとめて受け取れます。
try〜catch で受け取らない場合は、Fatal error として次のように表示されます。
Fatal error: Uncaught TypeError: subtotal(): Argument #1 ($price) must be of type int, string given, called in 05_errors.php on line 18 and defined in 05_errors.php:2
Stack trace:
#0 05_errors.php(18): subtotal()
#1 {main}
thrown in 05_errors.php on line 2
エラー文の読み方
subtotal(): Argument #1 ($price) must be of type int, string given, called in 05_errors.php on line 18
────┬──── ─────┬───── ───┬─── ─┬─ ─────┬───── ──────────┬───────────
│ │ │ │ │ └ 呼び出した場所
│ │ │ │ └ 実際に渡された型
│ │ │ └ 宣言されている型
│ │ └ 引数の名前
│ └ 何番目の引数か
└ 関数名
「called in」の後ろのファイル名と行番号が、直す場所です。 関数の定義側ではなく、呼び出している行を見にいきます。
可変長引数で、いくつでも受け取る
引数の前に ... を付けると、渡された値をまとめて配列として受け取れます。
<?php
function sum(int ...$numbers): int
{
return array_sum($numbers);
}
var_dump(sum());
var_dump(sum(1, 2, 3));
$list = [10, 20, 30];
var_dump(sum(...$list)); // 配列を展開して渡す
function tagLine(string $sep, string ...$tags): string
{
return implode($sep, $tags);
}
echo tagLine(' / ', 'PHP', '関数', '引数'), PHP_EOL;
try {
sum(1, 'x');
} catch (TypeError $e) {
echo get_class($e), ': ', $e->getMessage(), PHP_EOL;
}
int(0)
int(6)
int(60)
PHP / 関数 / 引数
TypeError: sum(): Argument #2 must be of type int, string given, called in 07_variadic.php on line 21
| 場所 | 書き方 | 意味 |
|---|---|---|
| 定義側 | function sum(int ...$numbers) |
残りの引数をまとめて配列 $numbers で受け取る |
| 呼び出し側 | sum(...$list) |
配列 $list の要素を1つずつの引数として渡す |
可変長引数に付けた型(int)は、渡された値の1つ1つに対して確認されます。また、可変長引数は引数リストの最後にだけ置けます。途中に置くと次の表示になりました。
Fatal error: Only the last parameter can be variadic in 15_var.php on line 2
文字列キーの配列を展開したり、名前付き引数で渡したりすると、可変長引数はキーつきの配列として受け取ります。
<?php
function show(...$args) { var_dump($args); }
show(a: 1, b: 2);
array(2) {
["a"]=>
int(1)
["b"]=>
int(2)
}
型宣言のない時代から使われている func_get_args() も、現在の PHP で動きます(legacy(1, 2, 3) で 3:1,2,3 を確認)。新しく書くコードでは、型を付けられる ... のほうが読みやすくなります。
参照渡しで、呼び出し元の変数を書き換える
通常の引数は値渡しで、関数の中で引数を書き換えても呼び出し元の変数は変わりません。引数の前に & を付けると参照渡しになり、呼び出し元の変数そのものを書き換えます。
<?php
function addTaxByValue(int $price): void { $price = (int)($price * 1.1); }
function addTaxByRef(int &$price): void { $price = (int)($price * 1.1); }
$a = 1000; addTaxByValue($a); var_dump($a);
$b = 1000; addTaxByRef($b); var_dump($b);
function addItem(array &$cart, string $item): void { $cart[] = $item; }
$cart = ['ペン'];
addItem($cart, 'ノート');
print_r($cart);
int(1000)
int(1100)
Array
(
[0] => ペン
[1] => ノート
)
値渡しの $a は 1000 のまま、参照渡しの $b は 1100 に変わりました。配列も既定では値渡しなので、関数の中で要素を追加したいときは & を付けるか、追加後の配列を return で返します。
オブジェクトを渡したときの動き
オブジェクトは & を付けなくても、関数の中でプロパティを変更すると呼び出し元に反映されます。
<?php
function renameItem(object $o): void { $o->name = '変更後'; }
function replaceItem(object $o): void { $o = new stdClass; $o->name = '差し替え'; }
$obj = new stdClass; $obj->name = '変更前';
renameItem($obj); var_dump($obj->name);
replaceItem($obj); var_dump($obj->name);
string(9) "変更後"
string(9) "変更後"
プロパティの変更は反映されますが、引数に別のオブジェクトを代入しても、呼び出し元の変数は元のオブジェクトを指したままです。オブジェクトの場合は「同じオブジェクトを指す値」がコピーされて渡される、と考えるとこの結果を説明できます。
| 渡すもの | & なし |
& あり |
|---|---|---|
| 数値・文字列・配列 | コピーが渡る。元は変わらない | 元の変数が変わる |
| オブジェクト | プロパティの変更は反映される。代入し直しは反映されない | 代入し直しも反映される |
参照渡しには変数を渡す
参照渡しの引数に、変数ではなく値そのものを渡すと、次の表示で止まりました。
$ addTaxByRef(1000);
Fatal error: Uncaught Error: addTaxByRef(): Argument #1 ($price) could not be passed by reference in 08_ref.php:21
| 判断の目安 | おすすめ |
|---|---|
| 計算結果を1つ返したい | return で返す |
| 複数の値を返したい | 配列で返して [$min, $max] = minMax($nums); で受け取る |
| 大きな配列をその場で並べ替えたい・書き換えたい | 参照渡し(sort() などの組み込み関数と同じ形) |
複数の値を返す例は次のとおりです。
<?php
function minMax(array $nums): array { return [min($nums), max($nums)]; }
[$min, $max] = minMax([3, 9, 1]);
echo "$min / $max", PHP_EOL; // 1 / 9
まずは return で返す形を選び、参照渡しは「その場で書き換える」ことが名前から伝わる関数に絞ると、呼び出し側が読みやすくなります。
名前付き引数で、必要な引数だけを渡す
PHP 8.0 から、引数名: 値 の形で引数を渡せます。途中のデフォルト値を飛ばして、後ろの引数だけを指定できるのがいちばんの利点です。
<?php
function createUser(string $name, int $age = 20, bool $admin = false, string $lang = 'ja'): string
{
return sprintf('%s / %d / %s / %s', $name, $age, $admin ? 'admin' : 'user', $lang);
}
echo createUser('佐藤', lang: 'en'), PHP_EOL; // 位置引数 + 名前付き
echo createUser(admin: true, name: '鈴木'), PHP_EOL; // 順番を入れ替え
echo createUser('田中', 30, lang: 'en'), PHP_EOL;
$args = ['name' => '高橋', 'admin' => true];
echo createUser(...$args), PHP_EOL; // 連想配列を展開
echo createUser(...['伊藤', 41], lang: 'en'), PHP_EOL; // 展開の後に名前付き(8.1〜)
echo str_pad(string: '7', length: 3, pad_string: '0', pad_type: STR_PAD_LEFT), PHP_EOL;
佐藤 / 20 / user / en
鈴木 / 20 / admin / ja
田中 / 30 / user / en
高橋 / 20 / admin / ja
伊藤 / 41 / user / en
007
組み込み関数(str_pad)にも使えます。true や false が並ぶ呼び出しでは、何を指定しているのかが読み取りやすくなります。
名前付き引数のルールとエラー文
| 書き方 | 結果(実行して確認) |
|---|---|
createUser(nmae: '佐藤')(名前の打ち間違い) |
Error: Unknown named parameter $nmae |
createUser('佐藤', name: '鈴木')(同じ引数を2回) |
Error: Named parameter $name overwrites previous argument |
createUser(age: 30)(必須引数がない) |
ArgumentCountError: createUser(): Argument #1 ($name) not passed |
createUser(name: '佐藤', 30)(名前付きの後に位置引数) |
Fatal error: Cannot use positional argument after named argument |
位置で渡す引数を先に、名前付き引数を後に書く、という順番だけ覚えておけば十分です。 引数名が呼び出し側のコードに現れるようになるので、公開している関数の引数名を変えるときは、呼び出し側も一緒に直します。
無名関数・アロー関数・第一級callable構文
関数を変数に入れたり、ほかの関数に渡したりする書き方です。
<?php
$rate = 0.1;
// 無名関数(クロージャ):外の変数は use で取り込む
$withTax = function (int $price) use ($rate): int {
return (int) round($price * (1 + $rate));
};
var_dump($withTax(1000));
// アロー関数:外の変数を自動で取り込む
$withTax2 = fn(int $price): int => (int) round($price * (1 + $rate));
var_dump($withTax2(1000));
// 定義した後で外の変数を変えても、取り込んだ値は変わらない
$rate = 0.5;
var_dump($withTax(1000));
var_dump($withTax2(1000));
int(1100)
int(1100)
int(1100)
int(1100)
無名関数の use もアロー関数も、定義した時点の値をコピーして取り込みます。 後から $rate を 0.5 に変えても、結果は 1100 のままでした。
外の変数を書き換えたいとき
<?php
// use (&$var) で参照として取り込むと、外の変数を書き換えられる
$total = 0;
$add = function (int $n) use (&$total): void { $total += $n; };
$add(5); $add(7);
var_dump($total);
// アロー関数の中で外の変数を変更しても、外には反映されない
$count = 0;
$inc = fn() => $count++;
$inc(); $inc();
var_dump($count);
// use を書かずに外の変数を使うと
$rate = 0.1;
$noUse = function () { return $rate; };
var_dump($noUse());
int(12)
int(0)
Warning: Undefined variable $rate in 10_closure.php on line 32
NULL
| 比べる点 | 無名関数 function () use (...) { } |
アロー関数 fn() => 式 |
|---|---|---|
| 書ける処理 | 複数の文 | 式を1つだけ(その値が戻り値になる) |
| 外の変数の取り込み | use ($a, $b) で明示する |
使った変数を自動で取り込む |
| 取り込み方 | 値のコピー(use (&$a) で参照にできる) |
値のコピーのみ |
| 外の変数の書き換え | use (&$a) でできる |
できない(結果は int(0)) |
| use を書き忘れると | Warning: Undefined variable |
起きない(自動で取り込むため) |
| 向いている場面 | 数行の処理、外の変数を更新する処理 | array_map などに渡す短い変換 |
コールバックとして渡す
<?php
$prices = [100, 250, 400];
print_r(array_map(fn($p) => $p * 2, $prices));
print_r(array_filter($prices, fn($p) => $p >= 250));
usort($prices, fn($a, $b) => $b <=> $a);
print_r($prices);
Array ( [0] => 200 [1] => 500 [2] => 800 )
Array ( [1] => 250 [2] => 400 )
Array ( [0] => 400 [1] => 250 [2] => 100 )
(print_r の出力は、横幅を抑えるために1行にまとめて表示しています。)array_filter の結果はキーが元のまま(1 と 2)残ります。0 から振り直したいときは array_values() を通します。
第一級callable構文:既存の関数をそのまま値にする
PHP 8.1 から、関数名の後ろに (...) を付けると、その関数を Closure オブジェクトとして取り出せます。
<?php
$len = strlen(...);
var_dump($len('hello'));
var_dump($len instanceof Closure);
function double(int $n): int { return $n * 2; }
print_r(array_map(double(...), [1, 2, 3]));
class Cart {
public function __construct(private float $rate) {}
public function withTax(int $p): int { return (int) round($p * (1 + $this->rate)); }
public static function label(int $p): string { return $p . '円'; }
}
$cart = new Cart(0.1);
print_r(array_map($cart->withTax(...), [100, 200]));
print_r(array_map(Cart::label(...), [100, 200]));
int(5)
bool(true)
Array ( [0] => 2 [1] => 4 [2] => 6 )
Array ( [0] => 110 [1] => 220 )
Array ( [0] => 100円 [1] => 200円 )
| 対象 | 文字列・配列で渡す書き方 | 第一級 callable 構文 |
|---|---|---|
| 関数 | 'double' |
double(...) |
| インスタンスメソッド | [$cart, 'withTax'] |
$cart->withTax(...) |
| 静的メソッド | 'Cart::label' または ['Cart', 'label'] |
Cart::label(...) |
文字列で渡す書き方(array_map('double', [6]))も引き続き動きます。違いが出るのは、名前を打ち間違えたときです。
(array_map('doubel', [1]) を実行)
TypeError: array_map(): Argument #1 ($callback) must be a valid callback or null, function "doubel" not found or invalid function name
($x = doubel(...); を実行)
Error: Call to undefined function doubel()
文字列は「使われた場所」で初めてエラーになりますが、(...) は書いた行でエラーになるので、原因の場所がすぐにわかります。 エディタの補完や名前の一括変更も効きやすくなります。
関数を書くときのチェックリスト
| 確認すること | よい状態 |
|---|---|
| 引数と戻り値に型が付いているか | すべての引数と戻り値に型がある |
| null を受け取る・返すか | ?型 または 型|null と明示している |
| 引数の並び | 必須引数が先、任意引数が後、可変長引数が最後 |
| ファイルの先頭 | declare(strict_types=1); がある |
| 値を返さない関数 | : void を付けている |
| 参照渡し | その場で書き換えることが関数名から伝わる |
| 真偽値の引数が並ぶ呼び出し | 名前付き引数で意味がわかるようにしている |
| コールバック | 短い変換はアロー関数、既存の関数は (...) で渡している |
よくある質問
Q. 型宣言は必ず書いたほうがよいですか?
A. 書くことをおすすめします。型があると、渡し間違いが TypeError として呼び出した行つきで表示されるので、原因を探す時間が短くなります。どんな値でも受け取る引数には mixed と書いておくと、「型を書き忘れたのではなく、何でも受け取る」という意図が伝わります。
Q. strict_types=1 は、既存のファイルにも入れたほうがよいですか?
A. 入れると、これまで自動変換で通っていた呼び出し('1200' を int の引数に渡すなど)が TypeError になります。今回の比較表のとおり影響する箇所が決まっているので、新しく書くファイルから入れ、既存のファイルはテストを用意したうえで1ファイルずつ進めるのがおすすめです。
Q. 関数の中から外の変数を使うにはどうすればよいですか?
A. 引数として渡すのが基本です。無名関数なら use ($変数)、アロー関数なら自動で取り込まれます。global $変数; という書き方もありますが、どこで値が変わるのかを追いにくくなるので、引数と戻り値でやり取りする形にすると読みやすくなります。
Q. デフォルト値のある引数を飛ばして、その後ろの引数だけ指定できますか?
A. 名前付き引数を使えばできます。createUser('佐藤', lang: 'en') のように書くと、間の $age と $admin はデフォルト値が使われます。
まとめ
- 関数は
function 名前(型 $引数 = デフォルト値): 戻り値の型 { ... }の形で書く。型は省略できるが、書いておくと渡し間違いをその場で知らせてくれる - 戻り値型は、返さないなら void、戻らないなら never、null もあり得るなら
?型、複数の型なら union を使う - strict_types=1 の有無で、
'1200'やtrueを int に渡したときの結果が変わる。float に int を渡す場合だけは両方で通る - 任意引数は必須引数の後ろに置く。逆にすると非推奨の通知が出て、デフォルト値は使われない
- 引数の不足は ArgumentCountError、型の不一致は TypeError。エラー文の「called in」が直す場所
- 可変長引数は
...$args、参照渡しは&$arg、名前付き引数は名前: 値。位置引数を先に書く - 無名関数は
use、アロー関数は自動で外の変数を取り込む。既存の関数はstrlen(...)の形で値として渡せる
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「PHP 関数の書き方|引数・デフォルト値・型宣言・戻り値を一覧で解説」
株式会社RJCでは、PHPの関数の書き方や型宣言の使い方を仲間と学び合っています。こうした知識を共有しながら、日々技術を磨いています。
株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。