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

Posted

技术标签:

【中文标题】PHP 命名空间移除/映射和重写标识符【英文标题】:PHP namespace removal / mapping and rewriting identifiers 【发布时间】:2011-08-31 13:14:05 【问题描述】:

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

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

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)

rewrite() 函数接收到的 $name 参数可以是 localunqualified\absolute name\spaced 标识符(但只是标识符,没有表达式)。 $identifiers2 列表对于解析 unqualified 标识符至关重要,这些标识符可以引用当前命名空间中的内容,或者如果在其中找不到,则为全局内容。

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

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

精神懒惰赏金。

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

我想我已经想通了,但问题仍然有待回答/实施建议。 (否则赏金显然会归 nikic。)

【问题讨论】:

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

在existing question on migration of namespaces to pseudo namespaced code 我已经写了introduced a conversion tool 作为一个更大项目的一部分。从那时起我就不再维护这个项目了,但据我所知,命名空间替换确实有效。 (我可能会在某个时候使用proper parser 重新实现这个项目。事实证明,使用普通令牌是一项相当乏味的任务。)

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

为了让这个 readmycodez 答案不那么明显,这里是代码执行的基本步骤:

    获取要解析的标识符并确保它不是类、接口、函数或常量声明(这些在registerClass 和registerOther 中通过简单地在当前命名空间前加上ns 分隔符替换为下划线来解析)。 确定它是什么类型的标识符:类、函数或常量。 (因为这些需要不同的分辨率。) 确保我们不解析 selfparent 类,也不解析 truefalsenull 常量。 解析别名(use 列表):
      如果标识符合格,则获取第一个命名空间分隔符之前的部分,并检查是否存在具有该名称的别名。如果是这样,用别名命名空间替换第一部分(现在标识符将是完全限定的)。否则添加当前命名空间。 如果标识符不合格且标识符类型为class,则检查标识符是否为别名,如果是,则将其替换为别名类。
    如果标识符是完全限定的,现在删除前导命名空间分隔符并用下划线替换所有其他命名空间分隔符并结束此算法。 否则:
      如果我们在全局命名空间中,则不需要进一步解析,因此结束此算法。 如果标识符类型为class,则在当前命名空间前添加,将所有 NS 分隔符替换为下划线并结束此算法。 否则:
        如果函数/常量是全局定义的,则保持标识符不变并结束该算法。 (这假设没有在命名空间中重新定义全局函数!在我的代码中我没有做这个假设,因此我插入了动态解析代码。) 否则,在当前命名空间前面加上下划线替换所有命名空间分隔符。 (好像我的代码有问题:即使设置了assumeGlobal 标志,我也不会这样做。相反,我总是插入动态调度代码。)

补充说明:别忘了也可以写namespace\some\ns。我在the NS function(也负责查找命名空间声明)中解析了这些构造。

【讨论】:

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

以上是关于PHP 命名空间移除/映射和重写标识符的主要内容,如果未能解决你的问题,请参考以下文章

PHP中的命名空间

PHP命名空间

php命名空间

18PHP 命名空间(namespace)

php之快速入门学习-17(PHP 命名空间)

命名空间