stillat/primitives

将原始值字符串解析成PHP数组。

资助包维护!
JohnathonKoster

v2.0.0 2024-08-15 14:45 UTC

README

Primitives

这个库提供了一种简单的方法,将简单值字符串转换为PHP运行时的等效值。

此库将解析以下类型的值

  • 数字
  • 字符串
  • 数组
  • 关联数组
  • true, false, null
  • 内置PHP常量

未知类型将返回它们的值为null

安装

可以使用composer安装此库

composer require stillat/primitives

示例用法

要使用此库,创建一个Parser类的新实例,并调用parseString方法

<?php

use Stillat\Primitives\Parser;

$parser = new Parser();

$result = $parser->parseString('[1, 2, 3], "some-string", "another", ["one" => 1, "two" => 2]');

将产生以下运行时结果

array(4) {
  [0] =>
  array(3) {
    [0] =>
    int(1)
    [1] =>
    int(2)
    [2] =>
    int(3)
  }
  [1] =>
  string(11) "some-string"
  [2] =>
  string(7) "another"
  [3] =>
  array(2) {
    'one' =>
    int(1)
    'two' =>
    int(2)
  }
}

此库还可以使用parseMethod方法解析基本方法详情

<?php

use Stillat\Primitives\Parser;

$parser = new Parser();

$result = $parser->parseMethod('methodName([1, 2, 3])');

将产生以下运行时结果

array(2) {
  [0] =>
  string(10) "methodName"
  [1] =>
  array(1) {
    [0] =>
    array(3) {
      [0] =>
      int(1)
      [1] =>
      int(2)
      [2] =>
      int(3)
    }
  }
}

无效输入将产生一个null值。

解析嵌套方法

parseMethod的一个更高级的替代方法是parseMethods方法

use Stillat\Primitives\Parser;

$parser = new Parser();

$result = $parser->parseMethods("randomElements(['a', 'b', 'c', 'd', 'e'], rand(1, 5))"); 

检测到的方法调用将作为Stillat\Primitives\MethodCall的实例返回。此类的每个实例将包含原始方法名称以及解析(和评估)后的运行时参数。parseMethods不会为您运行任何方法。

执行运行时方法

Primitives提供了一个名为MethodRunner的实用工具类,可用于在任意目标类上执行parseMethods的结果

<?php

use Stillat\Primitives\Parser;
use Stillat\Primitives\MethodRunner;

$parser = new Parser();
$runner = new MethodRunner();

class MyClass {

    public function sayHello($name)
    {
        return 'Hello, '.$name;
    }

}

$myClassInstance = new MyClass();

$methods = $parser->parseMethods("sayHello('Dave')");
$result = $runner->run($methods, $myClassInstance);

执行上述代码后,$result将包含值Hello, Dave

使用MethodRunner时的重要注意事项

  • 必须只有一个根方法调用
  • 如果有多个根元素,则run方法返回null
  • MethodRunner不会检查方法是否存在,允许调用__call

调用原生PHP函数

内部方法运行器不支持调用原生PHP函数。然而,我们可以创建一个实例,该实例可以(并利用适用于当前项目的逻辑来确定调用哪些函数是“安全”的)

<?php

use Stillat\Primitives\Parser;
use Stillat\Primitives\MethodRunner;

$parser = new Parser();
$runner = new MethodRunner();

class Greeter {

    public function sayHello($name)
    {
        return 'Hello, '.$name;
    }

}

class MethodTarget
{

    protected $instance;
    protected $safePhpFunctions = [
        'strtoupper'
    ];

    public function __construct()
    {
        $this->instance = new Greeter();
    }

    public function __call($name, $arguments)
    {
        // Replace with whatever logic makes sense. This approach
        // utilizes an allowed list of functions, but using
        // something like function_exists also works.
        if (in_array($name, $this->safePhpFunctions)) {
            return call_user_func($name, ...$arguments);
        }

        return call_user_func([$this->instance, $name], ...$arguments);
    }

}

$instance = new MethodTarget();

$result = $parser->parseMethods('sayHello(strtoupper("this is lowercase"))');

$methodResult = $runner->run($result, $instance);

执行上述代码后,$methodResult将包含值Hello, THIS IS LOWERCASE。这种方法之所以有效,是因为我们使用了PHP的__call魔法方法进行方法重载。当我们尝试调用类实例上不存在的方法时,__call方法将接收到方法名称和参数。如果安全函数列表包含传入的方法名称,我们将调用它并返回使用原始参数的结果。如果我们的安全列表不包含该函数,我们默认在目标类实例上调用它。

上下文变量

您还可以提供一个上下文数据数组,该数据可用于评估输入字符串。上下文变量使用$语法。一旦评估,输入字符串中的变量名称将被它们的实际值替换

<?php

use Stillat\Primitives\Parser;

$parser = new Parser();

$context = [
    'name' => 'Dave',
    'city' => 'Anywhere'
];

$result = $parser->parseString('[$name, $city]', $context);

执行前面的示例后,$result将包含类似以下值

array(1) {
  [0] =>
  array(2) {
    [0] =>
    string(4) "Dave"
    [1] =>
    string(8) "Anywhere"
  }
}

可以使用PHP的属性获取器语法(不支持数组访问器语法)利用嵌套变量路径

<?php

use Stillat\Primitives\Parser;

$parser = new Parser();

$context = [
    'nested' => [
        'arrays' => [
            'test' => [
                'name' => 'Dave',
                'city' => 'Anywhere'
            ]
        ]
    ]
];

$result = $parser->parseString('[$nested->arrays->test->name,' .
    '$nested->arrays->test->city]', $context);

像之前一样,$result变量将包含类似以下值

array(1) {
  [0] =>
  array(2) {
    [0] =>
    string(4) "Dave"
    [1] =>
    string(8) "Anywhere"
  }
}

许可证

MIT许可证。请参阅LICENSE.MD文件