當前位置: 妍妍網 > 碼農

PHP語言運算式庫ExpressionLanguage

2024-02-21碼農

symfony/expression-language 是 Symfony 框架中的一個元件,它允許你解析和執行運算式。這個元件非常有用,特別是當你需要動態地計算或評估某些條件或運算式時。

安裝

composer require symfony/expression-language

如果您在Symfony應用程式之外安裝此元件,則必須在程式碼中要求 vendor/autoload.php 檔以啟用Composer提供的類自動載入機制。

表達語言如何幫助我?

該元件的目的是允許使用者在配置中使用運算式來實作更復雜的邏輯。例如: Symfony Framework 在安全性、驗證規則和路由匹配中使用運算式。

除了在框架本身中使用元件之外,ExpressionLanguage 元件是業務規則引擎基礎的理想候選者。這個想法是讓網站的網站管理員在不使用PHP的情況下以動態的方式配置東西,並且不會引入安全問題:

# Get the special price if
user.getGroup() in ['good_customers''collaborator']
# Promote article to the homepage when
article.commentCount > 100 and article.category not in ["misc"]
# Send an alert when
product.stock < 15

運算式可以被看作是一個非常受限制的PHP沙箱,並且不太容易受到外部註入的影響,因為您必須顯式聲明運算式中哪些變量可用(但您仍然應該清理終端使用者提供並傳遞給運算式的任何數據)。

使用

ExpressionLanguage 元件可以編譯和計算運算式。運算式是一行程式,通常返回布爾值,可供程式碼在 if 語句中執行運算式時使用。一個簡單的運算式例子是 1 + 2 。您也可以使用更復雜的運算式,例如 someArray[3].someMethod('bar')

該元件提供了兩種使用運算式的方法:

  • evaluation :運算式在沒有編譯成PHP的情況下被求值;

  • compile :運算式被編譯為PHP,因此可以緩存和計算。

  • 簡單入門

    <?php
    /**
     * @desc PHP語言運算式引擎庫ExpressionLanguage
     * @author Tinywan(ShaoBo Wan)
     * @date 2024/2/20 9:44
     */

    declare(strict_types=1);
    require'../vendor/autoload.php';
    $expressionLanguage = new \Symfony\Component\ExpressionLanguage\ExpressionLanguage();
    var_dump($expressionLanguage->evaluate('1 + 2')); // displays 3
    var_dump($expressionLanguage->compile('1 + 2')); // displays (1 + 2)


    Parsing and Linting 運算式

    ExpressionLanguage 元件提供了一種解析和lint運算式的方法。 parse() 方法返回一個ParsedExpression例項,該例項可用於檢查和操作運算式。另一方面, lint() 返回一個布爾值,指示運算式是否有效。

    Parse(解析)

    symfony/expression-language 中,解析(parse)是指將字串形式的運算式轉換成內部可執行的運算式結構。這通常是透過元件的 parse 方法來實作的。

    <?php
    require '../vendor/autoload.php';
    $expressionLanguage = new \Symfony\Component\ExpressionLanguage\ExpressionLanguage();
    // 解析運算式
    $parsedExpression = $expressionLanguage->parse('1 + 2 * (3 - 4)', []);
    // $parsedExpression 是一個 ParsedExpression 物件,代表解析後的運算式結構
    var_dump($parsedExpression);

    需要註意的是, parse 方法在這裏並不執行運算式,只是將其轉換成一種可以在之後執行的形式。

    Lint(語法檢查)

    symfony/expression-language 中,並沒有直接提供名為 lint 的方法來進行語法檢查。但是,你可以透過嘗試解析運算式並捕獲可能丟擲的異常來檢查運算式的語法是否正確。

    <?php
    require '../vendor/autoload.php';
    use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
    $expressionLanguage = new ExpressionLanguage();
    try {
    $expressionString = '1 + 2 * (3 - 4';
    // 嘗試解析運算式,如果解析失敗,則捕獲異常
    $expressionLanguage->lint($expressionString, null);
    echo'運算式語法正確' . "\n";
    } catch (\Symfony\Component\ExpressionLanguage\SyntaxError $e) {
    // 如果捕獲到 SyntaxError 異常,說明運算式語法有誤
    echo'運算式語法錯誤:' . $e->getMessage() . "\n";
    }

    運算式語法錯誤:Unclosed "(" around position 8 for expression 1 + 2 * (3 - 4 .

    註冊函式

    函式在每個特定的 ExpressionLanguage 例項上註冊。這意味著函式可以在該例項執行的任何運算式中使用。

    要註冊函式,請使用 register() 。此方法有3個參數:

  • name 運算式中函式的名稱;

  • compiler 當使用函式編譯運算式時執行的函式;

  • evaluator 當運算式被求值時執行的函式。

  • Example

    <?php
    /**
     * @desc 註冊函式
     * @author Tinywan(ShaoBo Wan)
     * @date 2024/2/20 9:44
     */

    declare(strict_types=1);
    require'../vendor/autoload.php';
    $expressionLanguage = new \Symfony\Component\ExpressionLanguage\ExpressionLanguage();
    $expressionLanguage->register('custom_function_tinywan'function($str)string{
    return sprintf('(is_string(%1$s) ? strtolower(%1$s) : %1$s)', $str);
    }, function($arguments, $str)string{
    if (!is_string($str)) {
    return $str;
    }
    return strtolower($str);
    });
    var_dump($expressionLanguage->evaluate('custom_function_tinywan("開源技術小棧,HELLO")'));
    // this will print: 開源技術小棧,hello



    除了自訂函式參數之外,求值器還將傳遞一個 arguments 變量作為其第一個參數,該變量等於 evaluate() 的第二個參數(例如,在求值運算式時的「值」)。

    物件及其內容

    Symfony運算式非常強大,它可以在運算式語言中攔截PHP物件及其內容。

    <?php
    /**
     * @desc 物件及其內容
     * @author Tinywan(ShaoBo Wan)
     * @date 2024/2/20 9:44
     */

    declare(strict_types=1);
    require'../vendor/autoload.php';
    useSymfony\Component\ExpressionLanguage\ExpressionLanguage;
    $expressionLanguage = new ExpressionLanguage();
    classProduct{
    public string $name;
    public float $price;
    }
    $product = new Product();
    $product->name = 'Tinywan';
    $product->price = 2024;
    echo'Product price is ' .
    $expressionLanguage->evaluate('product.price', ['product' => $product]) . "\r\n";
    echo'Is Product price higher than 2022: ' .
    $expressionLanguage->evaluate('product.price > 2022', ['product' => $product]) . "\r\n";



    打印輸出結果

    Product price is 2024
    Is Product price higher than 20221

    其他套用

    PHP-Casbin 是一個強大的、高效的開源存取控制框架套用,其許可權管理機制支持多種存取控制模型。

    擴充套件庫:https://github.com/php-casbin/php-casbin

    Casbin 套用案例

    /**
     * @param array $functions
     *
     * @return ExpressionLanguage
     */
    protected function getExpressionLanguage(array $functions): ExpressionLanguage
    {
    $expressionLanguage = new ExpressionLanguage();
    foreach ($functions as $key => $func) {
    $expressionLanguage->register($keyfunction (...$args) use ($key) {
    return sprintf($key.'(%1$s)', implode(','$args));
    }, function ($arguments, ...$args) use ($func) {
    return$func(...$args);
    });
    }
    return$expressionLanguage;
    }
    $expressionLanguage = $this->getExpressionLanguage($functions);
    $expression = $expressionLanguage->parse($expWithRule, array_merge($rTokens$pTokens));
    $result = $expressionLanguage->evaluate($expression$parameters);
    if ($result) {
    $policyEffects[0] = Effector::ALLOW;
    else {
    $policyEffects[0] = Effector::INDETERMINATE;
    }