PHP名称空间的删除/映射和重写标识符 [英] PHP namespace removal / mapping and rewriting identifiers
问题描述
我正在尝试自动从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
}
将在找到的每个标识符(无论是类,函数还是const)上调用该函数.它将接收一些上下文信息,以将本地标识符转换为绝对/全局$ 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
参数可以是本地,不合格, \ absolute 或 name \ spaced 标识符(但只是标识符,没有表达式). $identifiers2
列表对于解析不合格标识符至关重要,该标识符可以引用当前名称空间中的内容,或者可以引用全局名称中的内容.
,除了名称空间解析和优先级规则外,还必须考虑各种use namespace
别名,并增加一些复杂性.
那么,您将如何/按什么顺序转换类/函数名称的变体?
精神懒惰赏金.
要使这成为一个不太明显的plzsendtehcodez问题:说明性的指令列表或伪代码答案也将是合格的.如果其他方法更适合该任务,请对此进行详细说明. (但不可以,不能选择升级PHP或更改主机程序.)
我想我已经想出了办法,但问题尚待解答/实施建议. (否则赏金显然会归于尼克.)
在引入了一种转换工具,这是我在一个较大的项目中编写的.从那时起,我再也没有维护过这个项目,但是据我所记得,替换命名空间确实有效. (我有时可能会使用适当的解析器重新实现该项目.事实证明,使用普通令牌是可行的相当繁琐.)
您会在伪名称空间解析="nofollow noreferrer"> namespace.php .我基于命名空间解析规则进行了实现,该规则将也许对您也有帮助.
要使这成为一个不太明显的readmycodez答案,请在此处执行代码的基本步骤:
- 获取要解析的标识符,并确保它不是类,接口,函数或常量声明(这些标识符在注册其他,只需在当前名称空间前添加ns分隔符(用下划线代替).
- 确定标识符的类型:类,函数或常量. (因为这些需要不同的分辨率.)
- 确保不解析
self
和parent
类,也不解析true
,false
和null
常量. - 解析别名(
use
列表):- 如果标识符合格,则在第一个名称空间分隔符之前获取零件,并检查是否存在具有该名称的别名.如果是这样,请用别名命名空间替换第一部分(现在标识符将完全合格).否则,将当前名称空间放在前面.
- 如果标识符不合格且标识符类型为
class
,请检查标识符是否为别名,如果是,则将其替换为别名类.
- 如果标识符完全合格,则删除开头的名称空间分隔符,并用下划线替换所有其他名称空间分隔符,然后结束该算法.
- 否则:
- 如果我们位于全局名称空间中,则不需要进一步的解析,因此请结束此算法.
- 如果标识符类型为
class
,则在当前名称空间之前,将所有NS分隔符替换为下划线,并结束该算法. - 否则:
- 如果全局定义了函数/常量,则保留标识符不变并结束该算法. (这假定没有在名称空间中重新定义全局函数!在我的代码中,我没有做这个假设,因此我插入了动态解析代码.)
- 否则,将当前名称空间放在前面,并用下划线替换所有名称空间分隔符. (似乎我的代码在这里出错:即使设置了
assumeGlobal
标志,我也不会这样做.相反,我总是插入动态调度代码.)
附加说明:不要忘记,也可以写namespace\some\ns
.我在 NS
函数中解析这些结构(也负责查找名称空间声明).
I'm attempting to automate the removal of namespaces from a PHP class collection to make them PHP 5.2 compatible. (Shared hosting providers do not fancy rogue PHP 5.3 installations. No idea why. Also the code in question doesn't use any 5.3 feature additions, just that syntax. Autoconversion seems easier than doing it by hand or reimplementing the codebase.)
For rewriting the *.php scripts I'm basically running over a tokenizer list. The identifier searching+merging is already complete. But I'm a bit confused now how to accomplish the actual rewriting.
function rewrite($name, $namespace, $use) {
global $identifiers2; // list of known/existing classes
/*
bounty on missing code here
*/
return strtr($name, "\\", "_"); // goal: backslash to underscore
}
That function is going to be invoked on each found identifier (whether class, function or const). It will receive some context information to transform a local identifier into an absolute/global $name:
$name =
rewrite(
"classfuncconst", # <-- foreach ($names as $name)
"current\name\space",
array(
'namespc' => 'use\this\namespc',
'alias' => 'from\name\too',
...
)
);
At this stage I've already prepared an $identifiers2
list. It contains a list of all known classes, functions and constant names (merged for simplicity here).
$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)
The $name
parameter as received by the rewrite()
function can be a local, unqualified, \absolute or name\spaced identifier (but just identifers, no expressions). The $identifiers2
list is crucial to resolve unqualified identifiers, which can refer to things in the current namespace, or if not found there, global stuff.
And the various use namespace
aliases have to be taken into account and add some complication besides the namespace resolving and precedence rules.
So, how / in which order would you attempt to convert the variations of class/function names here?
Mental Laziness Bounty.
To make this a less blatant plzsendtehcodez question: an explainative instruction list or pseudo-code answer would be eligible too. And if another approach would be more suitable for the task, please elaborate on that rather. (But no, upgrading PHP or changing the hoster is not an option.)
I think I've figured it out meanwhile, but the question is still open for answers / implementation proposals. (Otherwise the bounty will obviously go to nikic.)
In an existing question on migration of namespaces to pseudo namespaced code I already introduced a conversion tool I have written as part of a larger project. I haven't maintained this project anymore since that point, but as far as I remember the namespace replacements did work. (I may reimplement this project using a proper parser at some point. Working with plain tokens has proven to be quite a tedious task.)
You will find my implementation of namespace -> pseudo-namespace resolution in the namespace.php. I based the implementation on the namespace resolution rules, which will probably be of help for you, too.
To make this a less blatant readmycodez answer, here the basic steps the code does:
- Get the identifier to be resolved and ensure that it is not a class, interface, function or constant declaration (these are resolved in registerClass and registerOther by simply prepending the current namespace with ns separators replaced by underscores).
- Determine what type of identifier it is: A class, a function or a constant. (As these need different resolution.)
- Make sure we do not resolve the
self
andparent
classes, nor thetrue
,false
andnull
constants. - Resolve aliases (
use
list):- If the identifier is qualified get the part before the first namespace separator and check whether there exists an alias with that name. If it does, replace the first part with the aliased namespace (now the identifier will be fully qualified). Otherwise prepend the current namespace.
- If identifier is unqualified and the identifier type is
class
, check whether the identifier is an alias and if it is, replace it with the aliased class.
- If the identifier is fully qualified now drop the leading namespace separator and replace all other namespace separators with underscores and end this algorithm.
- Otherwise:
- If we are in the global namespace no further resolution required, thus end this algorithm.
- If the identifier type is
class
prepend the current namespace, replace all NS separators with underscores and end this algorithm. - Otherwise:
- If the function / constant is defined globally leave the identifier as is and end this algorithm. (This assumes that no global functions are redefined in a namespace! In my code I don't make this assumption, thus I insert dynamic resolution code.)
- Otherwise prepend the current namespace and replace all namespace separators with underscores. (Seems like I got a fault in my code here: I don't do this even if the
assumeGlobal
flag is set. Instead I always insert the dynamic dispatch code.)
Additional note: Don't forget that one can also write namespace\some\ns
. I resolve these constructs in the NS
function (which is also responsible for finding namespace declarations).
这篇关于PHP名称空间的删除/映射和重写标识符的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!