PHP 命名空间删除/映射和重写标识符

PHP namespace removal / mapping and rewriting identifiers

提问人:mario 提问时间:5/24/2011 最后编辑:mario 更新时间:8/4/2011 访问量:4360

问:

我正在尝试从 PHP 类集合中自动删除命名空间,以使它们与 PHP 5.2 兼容。(共享主机提供商不喜欢流氓 PHP 5.3 安装。不知道为什么。此外,有问题的代码没有使用任何 5.3 功能添加,只是使用该语法。自动转换似乎比手动操作或重新实现代码库更容易。

为了重写 *.php 脚本,我基本上是在运行一个分词器列表。标识符搜索+合并已经完成。但是我现在有点困惑如何完成实际的重写。

function rewrite($name, $namespace, $use) {

    global $identifiers2;            // list of known/existing classes

    /*
        bounty on missing code here
    */

    return strtr($name, "\\", "_");  // goal: backslash to underscore
}

该函数将在每个找到的标识符(无论是类、函数还是常量)上调用。它将接收一些上下文信息,以将本地标识符转换为绝对/全局$name:

$name =
    rewrite(
        "classfuncconst",      # <-- foreach ($names as $name)
        "current\name\space",
        array(
           'namespc' => 'use\this\namespc',
           'alias' => 'from\name\too',
           ...
        )
    );

在这个阶段,我已经准备了一个清单。它包含所有已知类、函数和常量名称的列表(为简单起见,此处合并)。$identifiers2

$identifiers2 = array(             // Alternative suggestions welcome.
   "name\space\Class" => "Class",  // - list structure usable for task?
   "other\ns\func1" => "func1",    // - local name aliases helpful?
   "blip\CONST" => "CONST",        // - (ignore case-insensitivity)

函数接收的参数可以是本地的、非限定、\绝对的或名称\间隔的标识符(但只是标识符,没有表达式)。该列表对于解析非限定标识符至关重要,这些标识符可以引用当前命名空间中的内容,或者如果找不到,则引用全局内容。$namerewrite()$identifiers2

并且必须考虑各种别名,除了命名空间解析和优先级规则之外,还增加了一些复杂性。use namespace

那么,您将如何/以何种顺序尝试在此处转换类/函数名称的变体?

精神懒惰赏金。

为了使这个问题不那么明目张胆:解释性指令列表或伪代码答案也符合条件。如果另一种方法更适合这项任务,请详细说明。(但是不可以,升级 PHP 或更改主机不是一种选择。

我想我已经想通了,但这个问题仍然有待回答/实施建议。(否则赏金显然会归尼基奇所有。

PHP 命名空间 tokenize

评论

4赞 jwueller 5/24/2011
这个转换器对很多人(包括我)都非常有用。我很想看到结果!
0赞 jwueller 5/24/2011
哦,包含一些关于命名空间解析的信息。
0赞 OZ_ 5/24/2011
我敢肯定这种转换将导致很多错误。而且我认为最好更改托管,而不是使用代码进行这种冒险操作。也许是为了某些库发行版,但即使在这种情况下,如果这个库成为安装 PHP 5.3 的理由也会更好。
0赞 jwueller 5/24/2011
@OZ_:如果所有内容都以一致为前缀,则不会发生冲突(这是该工具应该做的),因此可能会有一些特殊情况,例如动态函数/方法调用,但这些都是可以避免的。您并不总是可以选择托管商。一些客户可能希望在自己的机器上使用它,等等。为这些情况提供这样的工具真是太好了。
3赞 mario 5/25/2011
让它在 php 5.2 上运行是问题主题和约束。

答:

18赞 NikiC 5/27/2011 #1

关于将命名空间迁移到伪命名空间代码的现有问题中,我已经介绍了一个转换工具,该工具是作为更大项目的一部分编写的。从那时起,我就不再维护这个项目了,但据我所知,命名空间替换确实有效。(我可能会在某个时候使用适当的解析器重新实现这个项目。事实证明,使用普通令牌是一项相当繁琐的任务。

您将在 namespace.php中找到我的 namespace -> 伪命名空间解析的实现。我基于命名空间解析规则实现,这可能也会对您有所帮助。

为了使这成为一个不那么明目张胆的 readmycodez 答案,以下是代码执行的基本步骤:

  1. 获取要解析的标识符,并确保它不是类、接口、函数或常量声明(这些在 registerClassregisterOther 中解析,只需在当前命名空间前面加上 ns 分隔符替换为下划线)。
  2. 确定标识符的类型:类、函数或常量。(因为这些需要不同的分辨率。
  3. 确保我们不解析 和 类,也不解析 、 和 常量。selfparenttruefalsenull
  4. 解析别名(列表):use
    1. 如果标识符是限定的,请获取第一个命名空间分隔符之前的部分,并检查是否存在具有该名称的别名。如果是这样,请将第一部分替换为别名命名空间(现在标识符将完全限定)。否则,在当前命名空间前面添加。
    2. 如果 identifier 为非限定且标识符类型为 ,请检查标识符是否为别名,如果是,则将其替换为别名类。class
  5. 如果标识符是完全限定的,现在删除前导命名空间分隔符,并将所有其他命名空间分隔符替换为下划线,然后结束此算法。
  6. 否则:
    1. 如果我们在全局命名空间中,则不需要进一步的解析,因此结束此算法。
    2. 如果标识符类型在当前命名空间前面加上,请将所有 NS 分隔符替换为下划线并结束此算法。class
    3. 否则:
      1. 如果函数/常量是全局定义的,则保持标识符不变并结束此算法。(这假设在命名空间中没有重新定义全局函数!在我的代码中,我没有做出这个假设,因此我插入了动态解析代码。
      2. 否则,在当前命名空间前面加上所有命名空间分隔符,并将所有命名空间分隔符替换为下划线。(似乎我在这里的代码中出现了错误:即使设置了标志,我也不会这样做。相反,我总是插入动态调度代码。assumeGlobal

附加说明:别忘了也可以写 .我在 NS 函数(该函数还负责查找命名空间声明)中解析这些构造。namespace\some\ns

评论

2赞 mario 5/27/2011
很有意思。看起来这几乎是一个已解决的任务。由于您的实现非常干净,如果我无法让我的实现工作,我会使用它。而且您已经解决了 lambda 函数问题等。(在代币列表上工作确实只是使事情复杂化,但至少在一些让步的情况下它是可行的。-- 全局函数/类的歧义可能是一个问题,这也是我尝试 $identifiers 2 列表解决方法的原因。
0赞 NikiC 5/27/2011
@mario:我添加了一个描述,说明如果你想实现自己的版本,resolve 函数的作用(这可能并非不合理,因为我的代码更多地用于 5.3 -> 5.2 的一般转换,而不是移植特定的项目。在后一种情况下,您可以结合自己对项目的了解,使转换更加漂亮)。
0赞 mario 5/27/2011
解决了带有隐式别名的 namespace\ 前缀,以及您偶然提到的其他一些陷阱(parent、self)。虽然我已经分离了类、func、const 声明,但我并没有真正通过令牌流中的上下文来区分它们(为了简单起见)。但我认为,如果我知道声明的标识符,那就不够区别了。- 我不确定你的第一点:你最后不是也重写了名称声明吗?(我将更彻底地阅读侦听器代码..)$use["namespace"] = $namespace;
0赞 NikiC 5/27/2011
@mario:我改写了我的第一点。声明是单独重写的,而不是在此函数中重写的,只需在当前命名空间前面加上前面(并替换下划线)。这就是我所说的这一点的意思;)