Help us understand the problem. What is going on with this article?

PHPで文字列リテラルに式展開

More than 1 year has passed since last update.

Rubyでは文字列リテラルに任意の式を書いて展開できますが、PHPには単なる変数展開の機能しか存在しないので、同じことを実現するためには利用できないような気がしますが、別にそんなことはなかったぜ。

Rubyでは

#{}で囲むことで任意の式1が書けます。

Rubyの場合
hello = "hello"
world = "world"
puts "#{hello.capitalize}, #{world.upcase}!"
#=> "Hello, WORLD!"

PHPでは

ふつうのPHPの(関数呼び出しを含まない)変数展開は、こうです。

$hello = "hello";
$world = "world";

echo "{$hello}, {$world}!", PHP_EOL;
#=> "hello, world!"

PHPでは、以下のように変数埋め込みができます。

echo "{$hello}, {$world}!", PHP_EOL;
#=> "hello, world!"

式展開を期待して以下のように書いても、何も起きません。

echo "{ucfirst($hello)}, {mb_strtoupper($world)}!", PHP_EOL;
#=> {ucfirst(hello)}, {mb_strtoupper(world)}!

しかし、不思議なことに次のようにすれば式展開として動作します。

$i = function ($v) { return $v; };
echo "{$i(ucfirst($hello))}, {$i(mb_strtoupper($world))}!", PHP_EOL;
#=> "Hello, WORLD!"

$iidentity function恒等関数(引数と同じものをそのまま返す函数)です。

あるいは、次のようにファイルを分けて書くこともできます。

variable.php
namespace variable
{
    const embed = __NAMESPACE__ . '\embed';
    /**
     * @param  mixed $i
     * @return mixed
     */
    function embed($i) {
        return $i;
    }
}
$_ = \variable\embed;
echo "{$_(ucfirst($hello))}, {$_(mb_strtoupper($world))}!", PHP_EOL;

こうすると、ちょっとおもしろ感が出てきますね。

どうしてこんなことができるのか

実はこの種類の変数展開構文には、インスタンスのメソッド呼び出しやプロパティ、配列や文字列のデリファレンス、可変関数可変クラスを利用した静的メソッド・静的プロパティなど、$を使った式はほとんど2書けます

<?php

$d = '2112-09-03';
$DateTime = DateTime::class;
var_dump("ドラえもんが生まれたのは{$DateTime::createFromFormat('Y-m-d', $d)->format('Y年n月j日')}です");
#=> string(57) "ドラえもんが生まれたのは2112年9月3日です"

この仕様の詳細は@do-akiさんのPHP 文字列リテラルにおける変数展開ノ全テ - do_akiの徒然想記で網羅されてるのでお読みください。

可変関数やメソッド呼び出しが可能ってことは何ができるか? そうです。引数にはあらゆるどんな式でも書けます。

echo "{$i((function($s){return $s;})("a"))}";

引数に式が書けるってことはクロージャを悪用して式と文のロンダリングもできます。……つまり、文字列の変数展開の式では事実上かなりいろんなことができます。

これは実用になるのか……?

一般的な方法ではないので、ふつうにprintf()とかsprintf()とか使った方がいいんじゃないですかね…?

$hello = "hello";
$world = "world";
printf('%1$s, %2$s!'.PHP_EOL, ucfirst($hello), mb_strtoupper($world));

まとめ

今回紹介した文法は私が「Rubyの持つ108つのエディタ殺し文法」と呼ぶ式展開です。 PHPでも同等のめんどくささがあることは既知だったのですが、 今回紹介したテクニックを駆使してどこまで複雑な式を書いてエディタのシンタックスハイライト機能をぶっ壊せるかは読者への課題とします。

脚注


  1. 任意の式が書ける… つまり文字列リテラルの中に再帰的に 

  2. なぜかクラス定数は呼び出せません 

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away