質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.83%

  • PHP

    18651questions

    PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

  • Twig

    71questions

    Twig は、簡潔で可読性の高いテンプレートを記述することができ、 シンプルに記述することを目的として作られた PHPテンプレートエンジンです。

Twig の registerUndefinedFunctionCallback() の仕様について

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 109

ValveHead

score 1

はじめて質問させていただきます。どうぞよろしくお願いいたします。

テンプレートエンジンの Twig 上で PHP の組み込み関数を使用できるようにするため、
(方法は複数あるようですが)こちらなどを参考に、次のようなコードを書きました。

↓index.twig↓

<html>
<head>
    <meta charset="UTF-8">
    <title>{{ title }}</title>
</head>
<body>
    <h1>welcome to {{ message }}</h1>
    <h2>welcome to {{ substr(message, 0, -6) }}</h2>
    <h2>welcome to {{ str_replace('ground', 'place', message)}}</h2>
</body>
</html>


↓tiwg_test.php↓

<?php
require_once './vendor/autoload.php';

$loader = new Twig_Loader_Filesystem('./views/');
$twig = new Twig_Environment($loader);

$twig->registerUndefinedFunctionCallback(function ($name) {
    if (function_exists($name)) {
        return new Twig_SimpleFunction($name, $name);
    }

    return false;
});

echo $twig->render('index.twig', array(
'title' => 'Twig Test',
'message' => 'underground'
));


これによって、以下の結果を得ることができました。

result

ただ、不明な点があります。

registerUndefinedFunctionCallback() はいったい何をしているのか?

Twig_Environment クラスが書かれている Environment.php を確認すると、次のようになっていました。

public function registerUndefinedFunctionCallback($callable)
{
    $this->functionCallbacks[] = $callable;
}


$functionCallbacks はクラス内で、

protected $functionCallbacks = array();

と宣言されています。

クラス内で $functionCallbacks が他に出てくるのは、以下の関数のみです(一番最後の foreach 文)。

public function getFunction($name)
{
    if (!$this->extensionInitialized) {
        $this->initExtensions();
    }

    if (isset($this->functions[$name])) {
        return $this->functions[$name];
    }

    foreach ($this->functions as $pattern => $function) {
        $pattern = str_replace('\\*', '(.*?)', preg_quote($pattern, '#'), $count);

        if ($count) {
            if (preg_match('#^'.$pattern.'$#', $name, $matches)) {
                array_shift($matches);
                $function->setArguments($matches);

                return $function;
            }
        }
    }

    foreach ($this->functionCallbacks as $callback) {
        if (false !== $function = call_user_func($callback, $name)) {
            return $function;
        }
    }


この getFunction() はクラス内では呼ばれていません。
コンストラクタ(必要であれば記載します)も確認しましたが、getFunction() や $functionCallbacks に結びつくものは現状見つけることができていません(そもそもコードの読み方がおかしいでしょうか?)。

registerUndefinedFunctionCallback() は、いったいどのようにして index.twig から PHP の組み込み関数を読み取って、Twig で使えるようにしているのでしょうか?

また、初歩的な質問になってしまうかもしれませんが、

$twig->registerUndefinedFunctionCallback(function ($name) {
    if (function_exists($name)) {
        return new Twig_SimpleFunction($name, $name);
    }


上記のコールバック関数の引数となっている $name はどこからやってくるのでしょうか?

以上の 2 点について、ご回答をいただけましたら幸いです。
よろしくお願いいたします。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

checkベストアンサー

+1

えっと、luckerお兄さん、いつもどおり酔っ払ってるから変な事言ったらごめんね。

良い子の知りたいことは
> registerUndefinedFunctionCallback() は、いったいどのようにして index.twig から PHP の組み込み関数を読み取って、Twig で使えるようにしているのでしょうか?
って事だと思うんだけど、これ、すごく簡単。

ここ。

$twig->registerUndefinedFunctionCallback(function ($name) {
    if (function_exists($name)) {
        return new Twig_SimpleFunction($name, $name);
    }

    return false;
});

function_exists()っていうのはTwigではなくPHPの標準の関数で、指定された関数が定義されているか調べる関数ね。
function_exists

Twigでテンプレートをパースしている過程で関数と思われる名前、例えば

<h2>welcome to {{ substr(message, 0, -6) }}</h2>

で言ったら、substrというのが見つかった場合、その関数がまだTwigに登録されていなかったらregisterUndefinedFunctionCallback(callable $callable)で記述されてる$callableが呼ばれる。

$callable として登録されてるのは、こういうラムダ式。

function ($name) {
    if (function_exists($name)) {
        return new Twig_SimpleFunction($name, $name);
    }

    return false;
}

このラムダの$nameに、さっきの例では"substr"が渡される。そうすると

if (function_exists($name)) {
    return new Twig_SimpleFunction($name, $name);
}

が評価されて、"substr" がPHPで定義されている関数なら、Twig_SimpleFunctionのインスタンスを生成して返す。第一引数はTwigに登録する関数名、第二引数はその関数名がテンプレートに出てきた時に処理を任せるcallable。

ここで晴れて"substr"はTwigで認識できる関数として登録されるから、以後、テンプレートの中で"substr()"が見つかったら、今生成したTwig_SimpleFunctionインスタンスに従って、そのcallableに処理を任せる、というわけ。

で、さっきのfunction_exists($name)で$nameがPHPの標準関数ですらなかった場合、falseが返される。

そうするとTwigは、「そんな関数どこにも登録されてねーよ」と言って、エラーメッセージを出すことになるわけ。

Could you understand?

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/06/10 18:29

    んで、 protected $functionCallbacks = array(); になってるから、
    今まで認識されていない関数っぽいものが見つかるたびに、上記で評価して、PHP組み込み関数であったらその配列$functionCallbacksに1つずつ追加していくわけ。

    で、関数っぽいのが見つかるたびに、その配列をチェックして、登録されているか調べ、されていなかったら同じ事の繰り返しになるってことだね。

    キャンセル

  • 2018/06/10 22:27

    lucker さん、ご回答をありがとうございます。
    追加でいくつかお聞きしてもよいでしょうか。

    >で言ったら、substrというのが見つかった場合、その関数がまだTwigに登録されていなかったら
    registerUndefinedFunctionCallback(callable $callable)で記述されてる$callableが呼ばれる。

    この例で言えば「substr がまだ Twig に登録されていない」というのは、どのように判断されるのでしょうか(registerUndefinedFunctionCallback() は 見つけた組み込み関数を $functionCallbacks に代入するだけの存在という理解で問題ないでしょうか。$functionCallbacks の中身を確認するのは誰なのでしょう?)。

    また、substr() の機能そのものを呼び出す仕組みがよくわかりません。Twig_SimpleFunction インスタンスを生成するときに指定した第 2 引数は、コンストラクタによってインスタンス内の $callable に代入されるようですが、なぜそれだけの操作で substr() が使えるようになるのでしょうか。

    何か見当違いなことを言っていたらすみません。
    ご回答のほど、どうぞよろしくお願いいたします。

    キャンセル

  • 2018/06/11 21:26

    >この例で言えば「substr がまだ Twig に登録されていない」というのは、どのように判断されるのでしょうか

    記憶で話すので申し訳ないけど、Twigに独自関数登録するときは、

    $function = new Twig_SimpleFunction('my_function', function () {
    echo "This is my function!";
    });
    $twig->addFunction($function);

    みたいにTwig_SimpleFunctionクラスのインスタンスを登録するんだよね。
    で、addFunction()に渡されたインスタンスは配列に格納されてて、その配列を漁って登録されてないか調べてる感じ。

    キャンセル

  • 2018/06/11 21:30

    >registerUndefinedFunctionCallback() は 見つけた組み込み関数を $functionCallbacks に代入するだけの存在という理解で問題ないでしょうか。

    違うね。registerUndefinedFunctionCallback() は、まだ登録されていない関数っぽいのが見つかった時に、それがPHPの組み込み関数ならTwig_SimpleFunctionクラスのインスタンスを生成して返す関数を、Twigに登録する存在。

    $functionCallbacksに格納してるのは別の人。

    キャンセル

  • 2018/06/11 21:44

    >Twig_SimpleFunction インスタンスを生成するときに指定した第 2 引数は、コンストラクタによってインスタンス内の $callable に代入されるようですが、なぜそれだけの操作で substr() が使えるようになるのでしょうか。

    Twig_SimpleFunction インスタンスを生成する時に、第一引数が見つかった関数名のstring、第二引数がcallableなんだけど、callableって何かって言うと、ラムダ式とか無名関数とか言われてるやつ。

    例えばこんな感じで
    $callable = function($hoge){ echo $hoge; };
    関数そのものを変数に登録して

    $callable("test");

    って呼び出す事ができる。つまり、Twig_SimpleFunctionのインスタンスの関数名として "substr" が登録されいて、callable として PHP の substr 関数そのものを登録してしまう。

    あとは Twig でテンプレートをパースしていく過程で関数っぽい物
    substr(string $string , int $start [, int $length ] )が見つかったら、登録してある関数配列をforeachかなんかで漁って関数名がsubstrとしてマッチしたらそのインスタンスのcallableに引数として(string $string , int $start [, int $length ] )を渡し、結果をテンプレートに埋め込む。

    という感じなんだけど、

    キャンセル

  • 2018/06/11 21:45

    詳しくはコード読まないと正確な記述はできなくて、やるとしたらこの週末くらいにならないと時間取れないな。

    キャンセル

  • 2018/06/11 21:48

    あとひとつだけ

    >上記のコールバック関数の引数となっている $name はどこからやってくるのでしょうか?

    $twig->registerUndefinedFunctionCallback(function ($name) {
    if (function_exists($name)) {
    return new Twig_SimpleFunction($name, $name);
    }

    この$nameね。

    これは、Twigがテンプレートをパースしてる段階で見つかった関数っぽい名前が渡される。

    もし正確に知りたかったら、今週末 lucker お兄さんが暇になることを祈るといいよ。

    キャンセル

  • 2018/06/12 00:52

    lucker さん、詳細にご説明くださり、ありがとうございます。
    十分に理解できているかどうかは怪しいですが、少なくとも流れは把握できました。
    ウキウキでプログラミングを独学していたのに、ふと寄り道したテンプレートエンジンで心を折られるところでした……。

    >詳しくはコード読まないと正確な記述はできなくて、やるとしたらこの週末くらいにならないと時間取れないな。

    さすがに次の週末までお時間を割かせてしまうのは申し訳ないので、
    差し支えなければ、そろそろこの質問を閉じさせていただこうかと思います。

    ただ、最後に 2 点だけ、お付き合い願えたら幸いです。

    >つまり、Twig_SimpleFunctionのインスタンスの関数名として "substr" が登録されていて、callable として PHP の substr 関数そのものを登録してしまう。

    これは、イメージとしては、次のように登録されるということでしょうか。

    $substr = function(string $string , int $start [, int $length ] ) { substr の処理 }

    また「callable として PHP の substr 関数そのものを登録」といっても、登録対象となる substr 関数そのものを Twig はどうやって引っ張ってくるのでしょうか(登録した"後"にどうやって呼び出すのかについては、先のご回答のおかげで把握できました)。
    やはり Twig 本体のコード次第ということでしょうか。

    どうぞよろしくお願いいたします。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.83%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る

  • PHP

    18651questions

    PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

  • Twig

    71questions

    Twig は、簡潔で可読性の高いテンプレートを記述することができ、 シンプルに記述することを目的として作られた PHPテンプレートエンジンです。