打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
Implementing PHP namespaces in an existing project
If you've been developing for a longer time you know the problem. You started coding a project and now PHP comes with this new cool feature. At first you want to stay backwards compatible with older versions but there comes a point where you want to use these new features. Otherwise your code will be outdated and less attractive to new developers.
Such a feature we didn't have were namespaces. Namespaces are very useful to organize code and to avoid naming conflicts when you include a vendor library. For example you can have a function called "sendmail()". Now you'll have a problem when you want to include a 3rd party mail library which uses the exact same function.
With Group-Office we solved this problem before PHP namespaces arrived by prefixing. For example a class in the address book module would be called "GO_Addressbook_Model_Contact". All of our classes where prefixed by "GO" and followed up by the module name. This way we hacked our namespaces in with underscores. This was invented before PHP 5.3 became mainstream. Now almost all servers run PHP 5.3 and higher so it's time to change to namespaces! But this can be a tricky task which such a large project as Group-Office. But because we used the underscore namespace hack it wasn't so difficult at all. I wrote up with a script that could convert our project in one GO! It turns
class GO_Addressbook_Model_Contact{} into:
<?phpnamespace \GO\Addressbook\Model;class Contact{}
Some prerequisites
While writing this script I did face a couple of problems I had to resolve manually. We used some names that where incompatible. For example:
GO_Base_Util_Array would convert into:
<?phpnamespace GO\Base\Util;class Array{}
Using "Array" as a class name is invalid in PHP so I had to refactor these names. I found out that refactoring these names before using the script was the best approach. This way we could use Netbeans to easily refactor these names.
The list of illegal names was:
Function
Interface
Abstract
Switch
List
There are probably more but these where the only ones used in Group-Office.
I also had to make sure all existing PHP classes that were used are prefixed with a backslash. For example "new PDO" had to be written as "new \PDO".
The script
This script assumes "GO" is a global static singleton class and all classes are written in the GO_Module_Type_Class form. It also requires one class per file. You could easily modify it to work for your project if you meet these requirements.
<?php/** * * The MIT License (MIT) * * Copyright (c) 2014 Intermesh BV * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * *///find all PHP files except updates.php and updates.inc.php because we shouldn't touch them$cmd = 'find . -type f \( -iname "*.php" ! -iname "updates*" \);';exec($cmd, $scripts, $return_var);//return var should be 0 otherwise something went wrongif($return_var!=0) exit("Find command did not run successfully.\n");foreach($scripts as $script){ //skip old files. We don't use .inc.php anymore in the new framework if(substr($script,-14)=='.class.inc.php' || in_array(basename($script),array('namespacer.php','action.php','json.php'))) continue; //get the contents of the PHP Script $content = $oldContent = file_get_contents($script); //Our main global static function GO::function() is easiest to identify like this $content = str_replace('GO::', '\\GO::', $content); //All GO classes are build up like GO_Module_SomeClass so we can match them with //a regular expression. $regex = '/[^A-Za-z0-9_-](GO(_[A-Za-z0-9]+)+)\b/'; $classes = array(); if(preg_match_all($regex, $content, $matches)) { //loop through the matched class names and store the old classname as key //and the new classname as value foreach($matches[1] as $className){ //skip all uppercase classnames. They are old eg. GO_USERS, GO_LINKS if($className!=strtoupper($className)){ if(!in_array($className, $classes)){ $classes[$className]='\\'.str_replace('_','\\', $className); } } } //replace all old class names with the new namespaced ones. foreach($classes as $oldClassName=>$newClassName){ $content = str_replace($oldClassName, $newClassName, $content); } //now we have a problem with the class declarations. //we only have one class per file! foreach($classes as $oldClassName=>$newClassName){ $classDeclarationRegex = '/(class|interface)\s('.preg_quote($newClassName,'/').')/'; //Attempt to find a class definition in this file. if(preg_match($classDeclarationRegex,$content, $classDeclarationMatches,PREG_OFFSET_CAPTURE)){ echo "Found ".$newClassName."\n"; //strip last part of the class name to become the namespace. //eg. class; \GO\Email\Model\ImapMessageAttachment will have namespace: //GO\Email\Model $namespace = trim($newClassName,'\\'); $lastBackSlashPos = strrpos($namespace,'\\'); $namespace = substr($namespace,0, $lastBackSlashPos); //find place in the file to enter the "namespace GO\Email\Model;" declaration. //we can do this above the line with declaration "class ImapMessageAttachment" $offset = $classDeclarationMatches[0][1]; $lastLineBreakPos = strrpos(substr($content,0,$offset), "\n"); $declaration = "\n\nnamespace ".$namespace.";\n\n"; //Inset the declaration in the file content $firstPart = substr($content,0,$lastLineBreakPos); $lastPart = substr($content, $lastLineBreakPos); $content = $firstPart.$declaration.$lastPart; //now we must remove the namespace from class usages in this file. //eg. \GO\Base\Db\ActiveRecord becomes ActiveRecord. $content = preg_replace('/([^"\'])\\\\'.preg_quote($namespace,'/').'\\\\/', "$1", $content); } } } //some doubles could have been made $content = str_replace('\\\\GO', '\\GO', $content); //if the contents were modified then write them to the file. if($oldContent != $content){ echo "\nReplacing $script\n"; file_put_contents($script, $content); } echo "All done!\n"; }
本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
Namespace declaration statement has to be the very first statement in the script
PHP命名空间 namespace 如何实现自动加载
PHP中的命名空间与动态类实例化
addLoadEvent函数
PHP自动加载学习记录
Mybatis一对多与一对一
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服