PHPの変数展開について

採用はこちら

PHPの変数展開とは、文字列の中に記述した変数を、その変数が持つ値に置き換える仕組みです。

$name = '山田';

echo "こんにちは、{$name}さん";

出力結果は次のとおりです。

こんにちは、山田さん

PHPでは、主にダブルクォート文字列とヒアドキュメントで変数展開が行われます。

一方、シングルクォート文字列とNowdocでは、基本的に変数展開は行われません。

目次

ダブルクォートでは変数展開される

基本的な書き方

ダブルクォートで囲まれた文字列では、変数が自動的に展開されます。

$name = '佐藤';

echo "私の名前は{$name}です";

出力結果は次のとおりです。

私の名前は佐藤です

単純な変数であれば、波括弧を省略することもできます。

echo "私の名前は$nameです";

ただし、変数の直後に文字が続く場合は、波括弧を使ったほうが安全です。

シングルクォートでは変数展開されない

変数名がそのまま出力される

シングルクォートで囲まれた文字列では、変数展開は行われません。

$name = '佐藤';

echo '私の名前は{$name}です';

出力結果は次のとおりです。

私の名前は{$name}です

固定文字列を扱う場合や、変数展開を必要としない場合は、シングルクォートが適しています。

変数名の境界には波括弧を使う

日本語が直後に続く場合

次の書き方は、意図したとおりに動作しない可能性があります。

$name = '佐藤';

echo "$nameさん";

PHPは、$nameさんを分けず、$nameさんという変数名として解釈しようとします。

そのため、変数名の境界を波括弧で明示します。

echo "{$name}さん";

出力結果は次のとおりです。

佐藤さん

英数字が直後に続く場合

日本語だけでなく、英数字が続く場合も同様です。

$color = 'red';

echo "$colorBox";

この場合、PHPは$colorBoxという変数を参照します。

意図した出力にするには、次のように記述します。

echo "{$color}Box";

変数の直後に文字が続く場合は、{$変数名}の形式を使うと安全です。

配列要素を変数展開する

数値添字配列

配列の要素も文字列内で展開できます。

$colors = ['赤', '青', '緑'];

echo "選択された色は{$colors[0]}です";

出力結果は次のとおりです。

選択された色は赤です

連想配列

連想配列の値も展開できます。

$user = [
    'name' => '山田',
    'age' => 30,
];

echo "名前は{$user['name']}です";
echo "年齢は{$user['age']}歳です";

出力結果は次のとおりです。

名前は山田です
年齢は30歳です

基本構文による配列アクセス

PHPでは、次のような書き方も可能です。

echo "名前は$user[name]です";

ただし、通常の配列アクセス構文とは見た目が異なります。

実務では、次の書き方のほうが分かりやすく、安全です。

echo "名前は{$user['name']}です";

オブジェクトのプロパティを変数展開する

プロパティの展開

オブジェクトのプロパティも文字列内で展開できます。

class User
{
    public string $name = '高橋';
}

$user = new User();

echo "ユーザー名は{$user->name}です";

出力結果は次のとおりです。

ユーザー名は高橋です

単純なプロパティであれば、波括弧なしでも動作する場合があります。

echo "ユーザー名は$user->nameです";

ただし、可読性と構文の明確さを考えると、波括弧を付ける書き方が適しています。

echo "ユーザー名は{$user->name}です";

オブジェクトのメソッドを変数展開する

メソッドの戻り値を展開する

オブジェクトのメソッドは、波括弧内で呼び出せます。

class User
{
    public function getName(): string
    {
        return '伊藤';
    }
}

$user = new User();

echo "名前は{$user->getName()}です";

出力結果は次のとおりです。

名前は伊藤です

メソッドチェーンも使用できる

メソッドの戻り値がオブジェクトであれば、メソッドチェーンも使用できます。

class Profile
{
    public function getDisplayName(): string
    {
        return '山田太郎';
    }
}

class User
{
    public function getProfile(): Profile
    {
        return new Profile();
    }
}

$user = new User();

echo "表示名は{$user->getProfile()->getDisplayName()}です";

出力結果は次のとおりです。

表示名は山田太郎です

ただし、長いメソッドチェーンを文字列内に記述すると読みにくくなることがあります。

その場合は、先に変数へ代入します。

$profile = $user->getProfile();
$displayName = $profile->getDisplayName();

echo "表示名は{$displayName}です";

通常の関数呼び出しは直接展開できない

関数名をそのまま書く方法は使えない

次の書き方では、strtoupper()は関数として実行されません。

$name = 'yamada';

echo "大文字:{strtoupper($name)}";

波括弧を使った変数展開では、通常の関数名をそのまま記述することはできません。

先に関数を実行する

関数の戻り値を文字列内に含める場合は、先に変数へ代入します。

$name = 'yamada';
$upperName = strtoupper($name);

echo "大文字:{$upperName}";

または、文字列連結を使います。

echo '大文字:' . strtoupper($name);

可変関数は変数展開内で呼び出せる

関数名を変数に格納する

関数名を変数へ格納した可変関数であれば、文字列内から呼び出せます。

$formatter = 'strtoupper';
$name = 'yamada';

echo "大文字:{$formatter($name)}";

出力結果は次のとおりです。

大文字:YAMADA

無名関数を呼び出す

無名関数を変数へ代入した場合も同様です。

$formatter = function (string $value): string {
    return strtoupper($value);
};

$name = 'yamada';

echo "大文字:{$formatter($name)}";

ただし、可読性を優先する場合は、先に戻り値を変数へ代入する方法が適しています。

$upperName = strtoupper($name);

echo "大文字:{$upperName}";

計算式は直接展開できない

四則演算は波括弧内に書けない

次のように、計算式を直接記述することはできません。

$price = 1000;
$quantity = 3;

echo "合計は{$price * $quantity}円です";

波括弧構文は、任意のPHP式を評価する仕組みではありません。

先に計算する

計算結果を文字列へ含める場合は、先に計算します。

$total = $price * $quantity;

echo "合計は{$total}円です";

文字列連結を使うこともできます。

echo '合計は' . ($price * $quantity) . '円です';

実務では、先に計算結果を変数へ代入したほうが、コードを理解しやすくなります。

定数はそのまま変数展開できない

定数名は文字列として扱われる

次のコードでは、SITE_NAMEは定数として評価されません。

const SITE_NAME = 'サンプルサイト';

echo "サイト名はSITE_NAMEです";

出力結果は次のとおりです。

サイト名はSITE_NAMEです

定数を文字列内で使用する場合は、文字列連結を使います。

echo 'サイト名は' . SITE_NAME . 'です';

または、一度変数へ代入します。

$siteName = SITE_NAME;

echo "サイト名は{$siteName}です";

配列キーとして定数を使う

定数そのものを変数のように展開することはできませんが、配列キーとして使用できます。

const DATA_KEY = 'name';

$user = [
    'name' => '山田',
];

echo "名前は{$user[DATA_KEY]}です";

出力結果は次のとおりです。

名前は山田です

${name}形式はPHP 8.2以降で非推奨

非推奨の書き方

次の形式は、PHP 8.2以降で非推奨です。

$name = '山田';

echo "こんにちは、${name}さん";

推奨される書き方

単純な変数であれば、次のように記述します。

echo "こんにちは、{$name}さん";

変数名の境界が明確な場合は、次の書き方も可能です。

echo "こんにちは、$name";

新しいコードでは、${name}ではなく、{$name}を使用するのが安全です。

可変変数の場合

可変変数でも、古い形式は避けます。

echo "${$variableName}";

推奨される書き方は次のとおりです。

echo "{${$variableName}}";

ヒアドキュメントでは変数展開される

複数行の文字列を扱う

ヒアドキュメントでは、ダブルクォート文字列と同様に変数展開が行われます。

$name = '山田';
$age = 30;

$message = <<<TEXT
ユーザー情報
名前:{$name}
年齢:{$age}歳
TEXT;

echo $message;

出力結果は次のとおりです。

ユーザー情報
名前:山田
年齢:30歳

HTMLメールや複数行のテンプレートなどで便利です。

Nowdocでは変数展開されない

変数をそのまま文字として扱う

Nowdocでは、シングルクォート文字列と同様に変数展開されません。

$name = '山田';

$message = <<<'TEXT'
こんにちは、{$name}さん
TEXT;

echo $message;

出力結果は次のとおりです。

こんにちは、{$name}さん

変数展開したくない複数行文字列を扱う場合に適しています。

エスケープシーケンスの扱い

ダブルクォートで使用できる主な記述

ダブルクォート文字列では、変数展開だけでなく、エスケープシーケンスも解釈されます。

$name = '山田';

echo "名前:{$name}\n年齢:30";

主なエスケープシーケンスは次のとおりです。

記述意味
\n改行
\rキャリッジリターン
\tタブ
\\バックスラッシュ
\"ダブルクォート
\$ドル記号

変数名をそのまま表示したい場合は、$をエスケープします。

$name = '山田';

echo "変数名は\$nameです";

出力結果は次のとおりです。

変数名は$nameです

シングルクォートで解釈される記述

シングルクォート文字列では、基本的に次の2つだけが特別に扱われます。

\\
\'

例は次のとおりです。

echo 'It\'s PHP.';

出力結果は次のとおりです。

It's PHP.

シングルクォート内の\nは改行として扱われません。

echo '1行目\n2行目';

出力結果は次のとおりです。

1行目\n2行目

真偽値とnullの変数展開

trueは1として出力される

$value = true;

echo "値:{$value}";

出力結果は次のとおりです。

値:1

falseは空文字として出力される

$value = false;

echo "値:{$value}";

出力結果は次のようになります。

値:

nullも空文字として扱われる

$value = null;

echo "値:{$value}";

出力結果は次のようになります。

値:

真偽値やnullを確認する場合、変数展開では値の違いが分かりにくくなります。

デバッグにはvar_dump()を使用します。

var_dump($value);

表示用に使う場合は、文字列へ明示的に変換すると分かりやすくなります。

$status = $value ? '有効' : '無効';

echo "状態:{$status}";

配列全体はそのまま展開できない

配列を文字列として出力すると警告が発生する

次のように配列全体を展開すると、配列から文字列への変換に関する警告が発生します。

$items = ['apple', 'banana'];

echo "商品:{$items}";

一般的には、次のような警告が表示されます。

Warning: Array to string conversion

出力にはArrayと表示されます。

商品:Array

配列を文字列へ変換する

配列の要素をカンマ区切りで表示する場合は、implode()を使います。

$itemText = implode(', ', $items);

echo "商品:{$itemText}";

出力結果は次のとおりです。

商品:apple, banana

デバッグ目的の場合

配列の内容を確認する場合は、var_dump()print_r()を使用します。

var_dump($items);
print_r($items);

JSON形式に変換する場合

JSONとして出力する場合は、json_encode()を使用します。

$json = json_encode(
    $items,
    JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR
);

echo $json;

オブジェクト全体を展開する

__toString()を実装する

オブジェクト全体を文字列として展開するには、クラスに__toString()メソッドを実装します。

class Product
{
    public function __construct(
        private string $name
    ) {
    }

    public function __toString(): string
    {
        return $this->name;
    }
}

$product = new Product('ノートパソコン');

echo "商品:{$product}";

出力結果は次のとおりです。

商品:ノートパソコン

__toString()がない場合

__toString()を持たないオブジェクトを文字列として出力すると、エラーになります。

その場合は、必要なプロパティやメソッドの戻り値を出力します。

echo "商品:{$product->name}";

または、次のように記述します。

echo "商品:{$product->getName()}";

波括弧構文で使用できる記述

使用できる例

次のような構文は、文字列内で使用できます。

"{$variable}"
"{$array['key']}"
"{$array[$key]}"
"{$object->property}"
"{$object->method()}"
"{$object->method()->property}"
"{$callable($argument)}"

使用できない例

次のような計算式や条件式は、直接記述できません。

"{$a + $b}"
"{$a * $b}"
"{strtoupper($name)}"
"{count($items)}"
"{$condition ? 'yes' : 'no'}"

波括弧構文は、あらゆるPHP式を自由に評価するための仕組みではありません。

文字列連結との使い分け

変数展開を使う例

単純な変数を文章の中に含める場合は、変数展開が読みやすくなります。

echo "こんにちは、{$userName}さん";

文字列連結を使う例

関数呼び出しを含む場合は、文字列連結が自然なことがあります。

echo '価格:' . number_format($price) . '円';

処理結果を先に変数へ入れる方法もあります。

$formattedPrice = number_format($price);

echo "価格:{$formattedPrice}円";

処理速度の差よりも、可読性と保守性を優先して使い分けることが重要です。

HTML出力時はエスケープが必要

変数展開はHTMLエスケープを行わない

次のコードでは、ユーザー入力がそのままHTMLへ出力されます。

$userName = $_GET['name'] ?? '';

echo "<p>こんにちは、{$userName}さん</p>";

ユーザー入力にHTMLやJavaScriptが含まれていた場合、クロスサイトスクリプティングの原因になる可能性があります。

htmlspecialchars()を使用する

HTML本文へ値を出力する場合は、htmlspecialchars()を使用します。

$escapedUserName = htmlspecialchars(
    $userName,
    ENT_QUOTES | ENT_SUBSTITUTE,
    'UTF-8'
);

echo "<p>こんにちは、{$escapedUserName}さん</p>";

変数展開できることと、安全に出力できることは別の問題です。

出力先に応じて処理を変える

HTML本文では、次のように処理します。

htmlspecialchars(
    $value,
    ENT_QUOTES | ENT_SUBSTITUTE,
    'UTF-8'
);

URLの一部として使用する場合は、次のように処理します。

rawurlencode($value);

JSONとして使用する場合は、次のように処理します。

json_encode(
    $value,
    JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR
);

JavaScriptへPHPの値を渡す場合は、文字列を直接組み立てず、json_encode()を利用します。

<script>
const value = <?= json_encode(
    $value,
    JSON_HEX_TAG
    | JSON_HEX_AMP
    | JSON_HEX_APOS
    | JSON_HEX_QUOT
    | JSON_THROW_ON_ERROR
) ?>;
</script>

SQLには値を直接展開しない

危険な書き方

次のように、ユーザー入力をSQL文へ直接展開する方法は危険です。

$email = $_POST['email'] ?? '';

$sql = "SELECT * FROM users WHERE email = '{$email}'";

SQLインジェクションの原因になる可能性があります。

プリペアドステートメントを使用する

PDOを利用する場合は、プレースホルダーを使用します。

$stmt = $pdo->prepare(
    'SELECT * FROM users WHERE email = :email'
);

$stmt->execute([
    'email' => $email,
]);

文字列連結でも安全にはならない

次の2つは、どちらも危険です。

$sql = "SELECT * FROM users WHERE email = '{$email}'";
$sql = 'SELECT * FROM users WHERE email = "' . $email . '"';

変数展開を文字列連結へ変更しても、SQLインジェクション対策にはなりません。

値をSQL文字列へ直接組み込まず、必ずプレースホルダーを使用します。

実務で推奨される書き方

単純な変数

echo "こんにちは、{$name}さん";

配列要素

echo "商品名:{$product['name']}";

オブジェクトのプロパティ

echo "担当者:{$user->name}";

オブジェクトのメソッド

短い処理であれば、直接記述できます。

echo "担当者:{$user->getName()}";

処理が長い場合は、先に変数へ代入します。

$displayName = $user->getProfile()->getDisplayName();

echo "担当者:{$displayName}";

計算結果

$total = $price * $quantity;

echo "合計:{$total}円";

関数の戻り値

$formattedPrice = number_format($price);

echo "価格:{$formattedPrice}円";

HTML出力

$escapedName = htmlspecialchars(
    $name,
    ENT_QUOTES | ENT_SUBSTITUTE,
    'UTF-8'
);

echo "<p>{$escapedName}</p>";

SQL処理

$stmt = $pdo->prepare(
    'SELECT * FROM users WHERE id = :id'
);

$stmt->execute([
    'id' => $userId,
]);

まとめ

PHPの変数展開では、次のポイントを押さえることが重要です。

  • ダブルクォートとヒアドキュメントでは変数展開される
  • シングルクォートとNowdocでは変数展開されない
  • 変数の直後に文字が続く場合は波括弧を使う
  • 配列要素やオブジェクトのプロパティを展開できる
  • オブジェクトのメソッドも呼び出せる
  • 可変関数も一定の形式で呼び出せる
  • 通常の関数名をそのまま波括弧内に書くことはできない
  • 四則演算や三項演算子などの任意の式は直接展開できない
  • ${name}形式はPHP 8.2以降で非推奨
  • HTML出力では適切なエスケープが必要
  • SQLでは値を直接展開せず、プレースホルダーを使用する

単純な変数や配列要素、短いメソッド呼び出しは文字列内で展開できます。

一方、計算や複雑な処理は先に変数へ代入したほうが、可読性と保守性に優れたコードになります。

以上、PHPの変数展開についてでした。

最後までお読みいただき、ありがとうございました。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!
目次