像JS一样将任何PHP函数转换为String()

时间:2014-06-14 23:36:02

标签: javascript php

在javascript中,任何函数基本上都是一个对象,您可以在其上调用(function(){})。toString()以将其底层代码作为字符串。

我正在开发一个旨在用PHP完成工作的函数。 到目前为止看起来像这样:

function fn_to_string($fn, $strip_comments = true) {

    static $contents_cache = array();
    static $nl = "\r\n"; # change this to how you want

    if(!is_callable($fn)) return ''; # it should be a function
    if(!class_exists('ReflectionFunction')) return ''; # PHP 5.1 I think

    # get function info
    $rfn = new ReflectionFunction($fn);
    $file = $rfn->getFileName();
    $start = $rfn->getStartLine();
    $end = $rfn->getEndLine();

    if(!is_readable($file)) return ''; # file should be readable

    # cache file contents for subsequent reads (in case we use multiple fns defined in the same file)
    $md5 = md5($file);
    if(!isset($contents_cache[$md5]))
        $contents_cache[$md5] = file($file, FILE_IGNORE_NEW_LINES);

    if(empty($contents_cache[$md5])) return ''; # there should be stuff in the file
    $file = $contents_cache[$md5];

    # get function code and tokens
    $code = "<?php ". implode($nl, array_slice($file, $start-1, ($end+1)-$start));
    $tokens = token_get_all( $code);

    # now let's parse the code;
    $code = '';
    $function_count = 0;
    $ignore_input = false; # we use this to get rid of "use" or function name
    $got_header = false;
    $in_function = false;
    $braces_level = 0;
    foreach($tokens as $token){

        # get the token name or string
        if(is_string($token)){
            $token_name = $token;
        }elseif(is_array($token) && isset($token[0]) ){
            $token_name = token_name($token[0]);
            $token = isset($token[1]) ? $token[1] : "";
        }else{
            continue;
        }

        # strip comments
        if( 1
            && $strip_comments 
            && ($token_name == "T_COMMENT" || $token_name == "T_DOC_COMMENT" || $token_name == "T_ML_COMMENT")
        ){
            # but put back the new line
            if(substr($token,-1) == "\n")
                $code.=$nl;
            continue;
        }

        # let's decide what to do with it now
        if($in_function){

            # nesting level
            if($token_name == "{"){
                $braces_level++;

                # done ignoring `use`
                $ignore_input = false;
            }

            # append
            if( 1 
                && $function_count==1
                && ( 0
                    # skip function names
                    || ( $ignore_input && $token_name == "(" && !$got_header && (!($ignore_input=false)) ) 

                    # skip function () use (...) in closures functions
                    || ( $braces_level == 0 && !$got_header && $token_name == ")" && ($ignore_input=true) && ($got_header=true) )

                    # this fall-through is intentional
                    || !$ignore_input 
                )
            ) {
                $code .= $token;
            }

            # ending "}"
            if($token_name == "}"){
                $braces_level--;

                # done collecting the function
                if($braces_level == 0)
                    $in_function = false;
            }

        }elseif($token_name == "T_FUNCTION"){
            $function_count++;
            $in_function = true;
            $ignore_input = true;
            $braces_level = 0;
            $code.=$token;

            # we can't detect this properly so bail out
            if($function_count>1){
                $code = '';
                break;
            }
        }
    }

    return $code;
}

该函数使用ReflectionFunction类来确定声明传递函数的位置,并使用token_get_all()来处理声明的不同部分。

这可以按预期工作:

  1. 处理作为字符串传递的函数名称
  2. 处理变量函数
  3. 处理封闭物和lambdas
  4. 甚至可以自己处理
  5. 可以删除评论
  6. 然而,

    1. 它依赖于未记录的类ReflectionFunction
    2. 如果无法读取自己的源文件,则会失败
    3. 如果在声明传递函数的同一行上声明了多个函数,则会失败。例如:function a(){} function b(){} fn_to_string('a'); // fails
    4. 无法确定范围或上下文,因此它会删除函数名称和use关键字以避免将来出现问题
    5. 我正在努力确定这样的事情是否适合现实世界。所以我的问题是:

      1. 使用这种方法有什么理由可能不是一个好主意吗?
      2. 是否存在任何可预见的性能问题?
      3. 还有更好的选择吗?
      4. 是否存在功能未涵盖的任何被忽视的案例?
      5. 是否存在脚本可能无法读取的服务器设置?例如:is_readable(__FILE__)===false

0 个答案:

没有答案