php可变参数可以取得具体的参数名称吗

水纹
  • 393

比如:

function test($data,...$params){
    var_dump($params);
}
$data = ['test'=>'test'];
$a = 1;
$b = 2;
$c = 3
test($data,$a,$b,$c);

现在打印出来的是

array(3) { [0]=> int(1) [1]=> int(2) [2]=> int(3) }

有什么办法可以取得参数名吗?变成下面这种

array(3) { ['a']=> int(1) ['b']=> int(2) ['c']=> int(3) }
回复
阅读 2.2k
4 个回答

php 层面上 应该是不可以的,将对应值赋值给变量,然后变量传递给形参,此时和变量名是没有关系的

Tuesday
  • 138
function fun_info($funName=null){ 
    static $func = array(); 
    !$func && $func = get_defined_functions(); 
    if(!$funName){ 
       $flist = array(); 
       $flist['internal'] = fun_info('internal'); 
       $flist['user'] = fun_info('user'); 
       return $flist; 
    }else{ 
        if($func[$funName]){ 
            $funcs = $func[$funName]; 
        }else{ 
            $funName = strtolower($funName); 
            $funcs = array($funName); // 换算成数组. 
        } 
    } 
    foreach ($funcs as $func_val){ 
        $f = new ReflectionFunction($func_val); 
        $args = array(); 
        // $f->isUserDefined(); // 检测是否为用户自定义函数. 
        // $f->export('error_reporting'); // 导出函数详情. 
        foreach ($f->getParameters() as $param) { 
                $tmparg = ''; 
                if ($param->isPassedByReference()) $tmparg = '&'; 
                if ($param->isOptional()) { 
                    try { 
                        $DefaultValue = $param->getDefaultValue(); 
                        if(is_null($DefaultValue)) 
                            $DefaultValue = 'null'; 
                        $tmparg = '[' . $tmparg . '$' . $param->getName() . ' = ' . $DefaultValue . ']'; 
                    } catch( Exception $e ) { 
                        $tmparg = '['.$tmparg . '$' . $param->getName().']'; 
                    } 
                } else { 
                    $tmparg .= '$'.$param->getName(); 
                } 
                $args[] = $tmparg; 
                unset ($tmparg); 
        } 
        $functions_list[$func_val] = 'function ' . $func_val . '(' . strtr(implode(', ', $args),array('], ['=>', ')). ')' . PHP_EOL; 
    } 
    return $functions_list; 
} 

fun_info('strlen'); //支持对某个函数进行分析.

简单
  • 839

给出的代码,其实只是表示了位置参数
需要参数名称的需求,实质上是要非数字类型键的关联数组
python 中称之为关键字参数,用字典/映射 dict类型表示

php 函数体内查看与之相关的参数方法有
func_num_args( void ) 返回传递给当前函数的参数个数
mixed func_get_arg ( int $arg_num ) 返回指定的参数
array func_get_args ( void ) 返回参数列表

如果你想获取参数名,传由变量名和变量值组成的关联数组就是了,像下面这样

function test($data,...$params){
    var_dump(func_get_arg(1));
    // ["a"=>1,"b"=>2]
    var_dump(func_num_args());
    // 2
}
$data = ['test'=>'test'];
$a = 1;
$b = 2;
test($data, compact("a","b"));

像上面这种参数操作,Yii在构造函数中配置行为,事件操作也是给数组,结合参数个数感知,位置灵活变动。至于反射,在函数级别没必要,看看 laravel的助手函数,你会发现似曾相识。

如果你一定要这样做的话,也不是不可以。

首先使用 debug_backtrace() 拿到调用栈,array_shift() 取出来,拿到文件,然后把文件交给 php-parser 找到调用的位置,就可以通过 php-parser 拿到了。

<?php

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

use PhpParser\ParserFactory;

/**
 * 获取调用函数的参数
 *
 * @param array  $backtrace debug_backtrace
 * @param string $func      要查找的函数名字
 *
 * @return array 返回调用的参数,如果是变量,就返回变量名,如果不是变量是值,就返回序列号,从参数索引 0 开始编号
 */
function getCallFuncArgs(array $backtrace, string $func): array
{
    // 获取调用栈中第一个
    $caller = array_shift($backtrace);
    // 取出文件
    $file = $caller['file'];
    // 创建解析器
    $parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7);
    $code = file_get_contents($file);
    // 解析代码
    $ast = $parser->parse($code);
    $nodeFinder = new \PhpParser\NodeFinder();
    // 查找所有的函数调用
    $nodes = $nodeFinder->findInstanceOf($ast, \PhpParser\Node\Expr\FuncCall::class);
    $argList = [];
    foreach ($nodes as $node) {
        // 找到函数名跟要查找的一致的
        if (($node->name->parts[0] ?? null) === $func) {
            // 创建索引值
            $i = 0;
            // 遍历所有参数
            foreach ($node->args as $arg) {
                // 如果是变量,就取变量名
                if ($arg->value instanceof \PhpParser\Node\Expr\Variable) {
                    $argList[] = $arg->value->name;
                } else {
                    // 否则就编号
                    $argList[] = '#' . $i;
                }

                $i++;
            }
        }
    }
    return $argList;
}

function testA()
{
    // 调用
    $keys = getCallFuncArgs(debug_backtrace(), __FUNCTION__);
    // 填充一个关联数组,创建关系
    $combine = array_combine($keys, func_get_args());
    var_dump($combine);
}

$a = 1;
$b = 2;
$c = 2;
testA($a, $b, 1, 2, 3, 4, $c);

上面的代码输出就像这样:
图片.png

当然,这只是实现的一种思路,如果要用到业务中,还是做好多方面的测试工作,以免出现意外情况。

宣传栏