📝

Symfony ExpressionLanguage #1 概要と使い方

2022/10/02に公開約5,100字

PHP のフレームワークとして有名な Symfony には ExpressionLanguage というコンポート (ライブラリ) があります。

このコンポーネントはとても興味深く使い勝手の良いので、僕がシステムを作るときはさまざまな場面で使用しているのですが、日本語の記事がほとんどありません。

そこで ExpressionLanguage を紹介してみようと思います

ExpressionLanguage とは?

ExpressionLanguage はその名のとおり「式言語」と呼ばれるもので、Symfony 固有のものではありません。

「コードをがっつり書くほどでもないが、ちょっと計算したり、書式を整えたりしたい」という場面で使用します。

HTML を出力するのにテンプレートエンジンを使われている方は、テンプレートに埋め込まれるコードを思い浮かべていただければイメージしやすいと思います。

(以後、ExpressionLanguage は SymfonyExpressionLanguage のこととして説明していきます。)

SymfonyExpressionLanguage には以下のような特徴があります。

  • 式の評価とコンパイルを行うエンジンを提供しています。
  • 式は通常 1 行 で表現されます。真偽値を返すのが一般的ですが、他の値を返すことも可能です。
  • 独自のシンタックス を持っていて、シンプルに記述できます。
  • 式に対して、変数や関数を渡すこともでき、機能に合わせて柔軟に実装することができます。

eval vs ExpressionLanguage

PHP には文字列で渡された PHP コードを実行する eval関数 があり、これを使えば ExpressionLanguage 以上のことができます。

しかし、eval関数 は任意の PHP コードを実行できるため非常に危険であることがマニュアルにも記載されています。

ExpressionLanguage では提供する機能を限定することができるので、安全に使用することができます。

ExpressionLanguage を使ってみる

さっそく ExpressionLanguage を使ってみましょう。まず簡易のプロジェクトのためのディレクトリを作成します。

mkdir el-example
cd el-example

次に こちら を参考に Composer をダウンロードしてください。

symfony/expression-language をインストールします。

php composer.phar require symfony/expression-language

これで準備完了です。

動かしてみる

以下のコードを入力してみましょう。

<?php

require __DIR__ . '/vendor/autoload.php';

use Symfony\Component\ExpressionLanguage\ExpressionLanguage;

$expressionLanguage = new ExpressionLanguage;

var_dump($expressionLanguage->evaluate('1 + 2'));

1 + 2 という文字列が式として評価 (計算) され、3 が出力されます。

このように 構文に沿って記述された文字列を評価 (計算) するのが ExpressionLanguage です。

変数を渡す

次に変数を渡してみましょう。

ここでは User クラスのインスタンスを渡し、名前と年齢を出力しています。

<?php

require __DIR__ . '/vendor/autoload.php';

use Symfony\Component\ExpressionLanguage\ExpressionLanguage;

class User {
    public function __construct(private string $name, private int $age)
    {}

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

    public function getAge(): int {
        return $this->age;
    }
}

$expressionLanguage = new ExpressionLanguage;

$user = new User('ヤマダタロウ', 20);
var_dump($expressionLanguage->evaluate(
    'user.getName() ~ " (" ~ user.getAge() ~ "歳)"',
    [
        'user' => $user
    ]
));

ヤマダタロウ (20歳) と出力されます。

このように数値や文字列だけでなく、インスタンスを渡し、そのメソッドを実行することもできます。

関数を使う

独自の関数を作って使用することもできます。

ここでは my_implode 関数を作ってみます。

<?php

require __DIR__ . '/vendor/autoload.php';

use Symfony\Component\ExpressionLanguage\ExpressionLanguage;

$expressionLanguage = new ExpressionLanguage;

// 第1引数: 関数名
// 第2引数: コンパイル時に実行される関数
// 第3引数: 評価時に実行される関数
$expressionLanguage->register(
    'my_implode',
    function($value) {
        return sprintf('implode(",", %s)', $value);
    },
    function($arguments, $values) {
        return implode(',', $values);
    }
);

var_dump($expressionLanguage->compile(
    'my_implode(["A", "B", "C"])'
));

var_dump($expressionLanguage->evaluate(
    'my_implode(["A", "B", "C"])'
));

compile メソッドでは implode(",", [0 => "A", 1 => "B", 2 => "C"]) が出力されます。

evaluate メソッドでは A,B,C が出力されます。


ExpressionLanguage を使うと機能を限定しながら安全に式を評価することができます。

何がうれしいのか

ここまで ExpressionLanguage の使い方を説明してきましたが、ExpressionLanguage の何がうれしいのでしょうか?

中には「ふつうに PHP のコードを書けばよいのでは?」と思われた方もおられるかもしれません。

ExpressionLanguage を使うメリットは システムの挙動の一部をユーザーが制御できるようになる ことにあります。

すべてがプログラムで書かれているとユーザーが少し動きを変更したいと思っても、プログラマーに依頼しなければなりません。

社内で変更できればよいですが、プログラマーがいなければお金がかかることもあります。

これではユーザーのものであるべきシステムが融通のきかない不便なものになってしまいます。

ExpressionLanguage をうまく使うことができば、こういった問題を解決し、ユーザーがシステムを制御するというより良い状態にすることができます。

まとめ

ExpressionLanguage は少し練習すればノンプログラマーでも使える便利なもので、システムの自由度を向上させることができます。

ExpressionLanguage を使えばなんでも解決dけいるわけではありませんが、ユーザーのために構築されたシステムの制御をユーザーに渡す助けにはなる と僕は考えています。

難しいのは使いどころです。

どの部分を ExpressionLanguage として切り出し、ユーザーに公開するのか。

その設計上の判断に検討の余地があります。

僕が設計したシステムでは以下のようなところで ExpressionLanguage を使用しています。

  • CSV でエクスポートするときのデータフォーマットの定義
  • 取得した数値データの変換
  • 機能の有効・無効の切り替え

これらの使用例は別の記事にして紹介したいと思います。

Discussion

ログインするとコメントできます