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
参数可以是 local、unqualified、\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 分隔符替换为下划线来解析)。
确定它是什么类型的标识符:类、函数或常量。 (因为这些需要不同的分辨率。)
确保我们不解析
self
和 parent
类,也不解析 true
、false
和 null
常量。
解析别名(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 命名空间移除/映射和重写标识符的主要内容,如果未能解决你的问题,请参考以下文章