function/function,使您的应用程序更专业? 编写业余爱好
在PHP Web应用程序中实现了哪些function (如何),因为您认为它在某种程度上“ 更专业 ”? 或者你有自己的挑剔和代码的癖好,特别是可能会计数的小事情? 你花费了过多的时间来获取正确的代码或次要function?
。
Q&A插图的编码技巧示例:
configuration数据不在数据库中 :应用程序数据!=configuration数据,这也是必要和高效的问题。
修正url :通过添加尾部的斜线来标准化所有url,即使在技术上不需要。
人类可读的cookie :为了保护数据,我尝试避免不透明的会话/数据库句柄(用于用户选项,而不是授权使用)。
内容协商 :对于RSS和Atom格式之间的简单差异是有意义的。 但我看到它很less使用。
UI中没有数据库ID :避免将数据库内部代理键泄漏到URL中。 使用ORM,db内部的密钥甚至不必泄漏到业务逻辑中。
。
提示(不是规则)
- 那么,你认为哪个function会让你的web应用程序高于平均水平?
- 为什么不常见?
- 它是否有益于用户,但同样容易忽略?
- 更专业和安全的编码build议是非常多的话题。 他们总是。
- 但是这个问答的预期范围实际上是不常见/独特的function ,可能是非标准和有争议的function。 迷人的大奖金。
- 这也是关于恰好在PHP中实现的编码偏好和nitpicks。
- 不要想太大或太高。 小function也很重要。
- 显示代码如果可行。
- 然而,语法和编码风格/范例大多是无关紧要的。
- 我们不要争论有用性或代码质量。 这完全是一个function性和代码调查。
第一个关于性function的研究赏金 :很难确定许多好的想法之一。 说实话,我只能把它缩小到五个最喜欢的,并把决定留在rand()
。 而这个话题肯定是有趣的,需要第二轮赏金。 rest一下。 也许有人接手来完善范围。
文档 。
想象一下任何开源项目,你可以find没有最新的文档,并感受这对你认为它是多么专业的影响。
关心安全性 ,特别是用户的私人数据。
使用像bcrypt这样的强大散列来存储密码(参见crypt
文档):
// generating hash // read up how $costParameter and $salt look like in the crypt() docs $hash = crypt($password, '$2a$' . $costParameter . '$' . $salt . '$'); // checking if password is correct if (crypt($whatTheUserEntered, $hash) == $hash) { // correct password }
$salt
不应该是一个静态值。 每个用户都应该有所不同。
对于专业的外观,非常重要的是清洁和美观的graphicsdevise 。 因为封面是现在卖的东西。 伤心,但真实。 一个美丽的代码和丑陋的外观的Web应用程序将不会引起太多的关注。
健康检查
一系列预先configuration的testing,可以随时查看(pipe理员/网站所有者)可以查看的Web应用程序或网站的基本“健康状况”,显示如下内容:
- 数据库服务器是否可达?
- 必要的文件和目录是否可写?
- 所有的数据结构都是完整的吗?
- 所有页面/对话框/查询都显示正确吗?
- 有足够的磁盘空间可用吗?
- 等等
一个简单的状态页面以绿色,橙色或红色背景显示系统当前的“健康状况”。
这与unit testing不同,它处理“真实世界”环境的健康,而不是抽象的代码完整性。
运行状况检查还可以使应用程序轻松迁移到其他服务器。
我已经在几个需要不断监测的项目中实施这个项目。 我计划在今年的某个时候把它变成一个小的,可扩展的“放下”的开源应用程序,可以用在各种networking应用程序甚至是网站上。 参与和投入非常受欢迎 – 有关更多详细信息,请参阅相关的SO问题 。
源代码pipe理。 特别是Git(虽然不是特别的GitHub)
好的,这并不罕见。
但是,当我外包代码,并得到一个整洁的回购,所有的历史和曲折,我更深刻的印象比一个文件夹充满了PHP文件。
而且它显示了多less额外的工作投入到没有看到的项目中。
我使用GitFlow ,在使用过程中需要更长的时间,但是在完成的项目中更加令人印象深刻。 确保客户端看到GitHub图(或等同)所有这些分支看起来整洁和令人印象深刻。 (除了真正有用!)
有关:
问题跟踪系统。
在代码完成之前(当客户端正在审阅时)和后缀都允许他们添加新的任务。
这不仅是一种划分工作和获得更多工作的方式,而且让客户感觉到项目仍然在我心中。 即使我继续前进
我使用的是Collabtive ,这些系统绝对是可怕的,但是看起来更令人印象深刻,然后我尝试了其他的东西,看起来令人印象深刻的东西被认为是专业的。
密码强度/匹配
在注册或更改密码时,以交互方式通知用户密码的强度弱点。 当他们的确认符合他们打字时也通知他们。
实时表单validation
与密码一样,表单条目的实时validation比让用户在知道自己犯了错误之前完成整个表单更为刺激,或者省略了他们跳过的必填字段。
友好的exception/error handling的防御性编程
永远不要使用神秘的错误信息,并且通常会引用最好的例子,你可以find友好的错误信息。 在最好的情况下,这通常需要一个有才华的复制作者负责保持良好的声音等。
这与强大的exception处理和良好的防御性编程息息相关。
防御性devise是关于这个话题的一本相当不错的书。
编写易于维护的代码。 在开始写作之前,请多思考一下对象的界面。 添加许多事件触发器 ,即使您现在不需要它们。 他们可能在这个function中很有用。
编写代码就好像它是一个外部库。 即使您正在处理商业封闭的应用程序,也将其视为开源项目。 想想其他用户可能想更改现有代码的行为而不改变它。 当你使用types提示时,你可能应该检查接口而不是类,所以:
public function add(Storable $resource); # Good public function add(SessionStore $session); # Bad
不要使用全局名称空间和“全局特性”。 定义全局常量或使用__autoload()
(而不是使用spl_autoload_register()
注册另一个自动加载器)是一个不好的做法。
使用第三方代码。 那里有很多图书馆。 大多数时候开发自己的ORM或邮件程序是没有意义的。 它减less了开发应用程序所需的时间 – >它降低了成本。
创buildAPI文档。 我不认为需要任何解释。
让你的代码看起来干净,并保持一些编码约定。
不要创build无用的页面。 没有什么比404 Not Found页面更让人讨厌的了,仅仅是一个很大的404 NOT FOUND信息。 您应该为每个资源提供几个未find的页面。 所以,如果我访问/video/123/how-to-swim
,这种资源不存在,除非我们很抱歉,该video不存在或已被删除。 添加如下内容: 最近的video , 也许你正在寻找:“如何跳跃”,“游泳:ABC” (标题forms类似于URL(游泳)如题)的video等等。
允许用户自定义网站。 想象一下,你有一个有几个“盒子”的主页:最近的video,最近的照片,最近活跃的主题,新闻,宣传画廊等。允许用户隐藏其中一些,改变他们的顺序 – 有人可能更感兴趣的照片和video而不是新闻和线索,所以他/她可能希望把它们放在页面顶部,其他人可能会得到不同的偏好,并可能想要有不同的顺序。 为每个框添加首选项,例如:“显示:5,10,20个最近的video”。
要添加更多(更less的PHP导向)点,这将提高用户体验,使您的应用程序更专业,但处理后端:
良好的压缩/缩小
通常,Web应用程序在加载和通过使用过程中都会发送大量请求。 缩小可以像使用CSS精灵和GZipping内容一样帮助减less初始负载重量。 雅虎的Firefox的Firebug扩展的YSlow插件是帮助您简化并尽可能快地创build应用程序的一个好方法:
Firebug: http : //getfirebug.com/
YSlow: http : //developer.yahoo.com/yslow/
另外 – 一些好的做法: http : //developer.yahoo.com/performance/rules.html
YSlow将帮助确定如何真正使您的应用程序整洁,快速,干净。 我也推荐PHP Minify来处理大部分的文件压缩,它是一个非常强大的工具:
PHP Minify: http : //code.google.com/p/minify/
获得Firebug的另一个原因是它在开发任何Web应用程序时所带来的巨大好处,其中最重要的就是能够确定应用程序的安全程度,因为在操作过程中您可以跟踪所创build的数据stream。
困惑
—分解到下面更详细的答案
很好的使用Apache重写
除了提供干净url以增强浏览体验以及给予逻辑上存档内容印象的基本好处之外,一个好的.htaccess文件可以为您的网站添加额外的安全层,压缩内容服务器端并添加额外的标识符(E-标签,例如,虽然他们的好处是有争议的)。 每个Web应用程序也应该有一个写得很好的.htaccess。
兼容性/validation
我总是强烈要求任何开发人员去最广泛(合理)的长度,以确保他们所有的输出代码是有效的,评论,逻辑和清洁。 W3C在明确指出不仅仅是HTML,而且Web应该如何运作方面做得非常出色。 按照他们的指导,以增加你写的任何东西的兼容性,以确保它适用于你所设想的任何人。
一些伟大的工具:
HTMLvalidation器: http : //validator.w3.org/
其他validation: http : //validator.w3.org/unicorn/
有些规格要注意:
W3C Web应用程序(WebApps)工作组: http : //www.w3.org/2008/webapps/
W3C可访问性指南: http : //www.w3.org/WAI/guid-tech.html
W3C WAI-ARIA计划: http : //www.w3.org/WAI/intro/aria.php
在Javascript方面,JS lint是一个很好的工具,可以确保JS中的任何错误(当您检查时可能不会影响性能)被压扁:
JSLint: http : //www.jslint.com/
并通过代理,以帮助发展,美化的JS可以帮助您更好地构build您的代码的发展,预缩小: http : //jsbeautifier.org/
或者美化PHP当然… http://beta.phpformatter.com/
最后 – 有点less黑与白
人源化
也许生产基于networking的应用程序的最大好处之一是它们不仅可以在用户之间(为了鼓励协作工作)提供连接性,还可以在用户与应用程序本身之间以及负责正在进行的开发和维护。
如果您想到37个信号这样的项目,其中一个吸引人的因素是他们向用户传达这样的感觉,即项目(代码)本身已经被人性化并且具有个性,帮助吸引用户并与应用程序关联,鼓励享受使用和交stream给他人。 我不是说应用程序似乎“活着”,但更多的是感觉更“平易近人”,用户可以“识别”它。
然而,这只是人性化硬币的一个方面,几乎使用户对应用程序/代码/框架/体验几乎感同身受,所以被鼓励使用它。 另一方面是打破应用程序和它之后的边界。
我们都喜欢了解我们使用的背后的人,人等等,因为我们传递了很多信息,往往不合逻辑地将其应用到应用程序本身,而且确实是我们的“喜欢”(即像史蒂夫·乔布斯,苹果等)。虽然承认是一个任性的例子)。 在发生困难时,我们可以通过一条路线与真人联系,只需一个简单的步骤,就可以拥有一个开发人员博客,新闻stream等,在应用程序周围构build一个人性化的元数据,使其具有可信性,并且可能比其部分的总和。
诚然,人性化可能既不是在这里,也不是在那里,而且build立起来肯定是非常困难的(同样的,不同的人有不同的朋友),但是开发人员可以通过任何方式在一个更临床的手术应用程序的边缘,平易近人的乐趣,每天用户都是我书中的赢家。
没有强制登记
这是日常Web应用程序的常见问题。 强制帐户注册不方便用户使用。 现在有必要防止垃圾邮件和滥用。 但这阻碍了采用。
我想大家都听说过Stackoverflow。 他们不强制注册。 你可以没有帐户出资。 它不是强制注册,而是系统地鼓励它。 然而,主要的一点是,至less有一个select是在作出承诺之前逐渐习惯。
这是一个简单的概念。 但同样难以实施。 很less可以使用临时和可选用户帐户,必须为其准备基本的应用程序逻辑和数据库结构。 不pipe实施的努力如何,我认为这是一个重要的和被强调的特点。
经常被忽视的一个: 可打印的CSS 。 这是一个写作的痛苦,但是对于想要在应用程序中打印出一些数据的用户来说,却有很大的不同。
<link href="print.css" rel="stylesheet" type="text/css" media="print">
延续点
例如:您提交表单,您将被redirect,现在login页面应该包含通知。 大多数网站只是在会话中存储该通知。 这意味着:任何页面,将被访问接下来将显示该消息。 在大多数情况下没有问题,但是我的头脑真的很混乱。 我认为会话是用于会话数据,而不是下一个请求数据。
我的解决scheme:创build一个数组,写入数据(通知,也许更多),使用sessionid +一个唯一的标识符保存在memcache和redirect添加参数__continuation = {唯一标识符}。 然后下一页再次加载数据并处理它。
当你想获得更多的数据而不仅仅是一个简短的消息,这变得更加方便。
有问题吗?
一致的编程风格,variables命名,支撑等。遵守编码标准(任何编码标准)。确保由不同人编写的文件看起来相同,以便维护代码不需要知道是谁编写的。
例如: URL“修复”
对于http-URL,我会像每个浏览器一样强制使用path片段。 因此,我要说明“修复”用户input和导入的数据,并且在显示或存储这些值之前,总是将最后的斜杠添加到例如http://example.org 。 可能不是“超级专业”,但是规范化URL通常会简化后面的处理。 不知道,这似乎是“更好”。
$url = preg_replace("/^(http:..[-\w.\d]+)$/", "$1/", $url);
所以我总是会在http://example.org/
的值中,不pipe是什么。 这不会让你的HTML更符合标准,但它只是我的宠儿之一。
UI中没有数据库ID
大多数数据库对象都使用一些代理键枚举。 我认为看起来不太明显(有时偶尔会成为一个安全隐患)正在将这些漏洞泄露到用户界面中,就像在URL中一样。 把user.php?id=37310843
改成user.php?name=title
是没有什么大不了的。 数据库查找ID或名称几乎没有什么区别。 所以这不是关于search引擎优化的愚蠢,而是关于一个云杉的外观
- 使用适当的ORM,那些数据库内部的ID甚至不必泄漏到应用程序逻辑中。
- 展览维基百科:想象他们使用替代键而不是页面标题。 (见CoWiki)
- 由于外部链接总是指向标识内容,所以应避免使用代理键。 如果足够重要,那么应该使用GUID。
任何2.0的networking应用程序预计将有一个重要的事情是 –
- search – 无论是全文search,关键字search,是否有自动build议或(最新的一个)实时search。 没有这个function,当我访问任何网站(尤其是search引擎closures的网站)时,我感到窒息。 对于任何现代2.0网站PHP都有一定的必要性。
- MVC框架 –因为这是与web-dev(不绑定到PHP),模型 – 视图 – 控制器框架是必须的。 由于代码基础过多,迭代周期更快,所以正确的结构化是防止未来麻烦和难以追踪的错误的必要条件。
- AJAX –需要我说这个:)它改变了我们大多数浏览网站的方式。 一个AJAX使网站是一个很酷的function。
- debugging信息 –这是你的应用程序崩溃的情况。 你怎么find出了什么问题? 你维护日志吗? 你能追溯到它的来源日志(发生几个小时)? 如何让日志成为冗长?
- 精简的JavaScript和CSS –减less加载时间,使网站更活泼,从而使用户在浏览网站时感觉更舒适。
- caching –再次极大地增加了用户的感知响应时间,从而使网站更“专业”。
综合应用程序
问问自己 – 如果您运行的是在线商务系统,并且您的信用卡支付供应商处于离线状态,您的客户是否仍然可以浏览您的产品,甚至是使用不同的付款方式付款?
应用程序应该以可以切换“开关”的方式构build,而不会将整个站点置于“维护模式”,而只是使应用程序中的页面或部分以优雅的方式不可用,而应用程序的其余部分stil可供其他用户使用。
综合应用程序可能是一个俗气的名字,但我完全肯定,有更好的东西,但它肯定是许多人在开发可用的应用程序时往往忘记的方法之一。
聪明的蛮力保护
假设你的应用程序中有一个pipe理部分,它包含了大量的客户信息。 绝对数据,你不希望任何人得到他们的手。 即使你有一个很好的盐和很好的algorithm来散列你的密码,一个邪恶的用户仍然可以对你的login表单执行一个DOS /彩虹表(蛮力?)攻击。
第一种select显然是使用自动化软件来执行这些攻击,次要手段是通过对密码进行合格的猜测来手动执行。 考虑到用户猜测他或她的密码的信息是非常容易的,考虑到仅仅使用Google就可以获得多less信息。 另外,如果你生活在瑞典这样的国家,那么你就无法了解到一个人只有一个电话给当局的信息 – 社会安全号码,伴侣姓名,婚姻状况,孩子名字,宠物和更多的信息,所有这些都集中在普通用户的密码猜测上。
如果我们看一下普通人如何select自己的密码 ,安全性应该很容易妥协。 当然,我们可以设置限制长度的用户密码,特殊字符等等,但是这不会影响情况。 蛮力攻击仍然可以进行。
如果用户尝试login并且未能通过未知IP进行第二次尝试,则应该显示一个恢复码以防止自动login。 经过另一次(n)失败尝试后,帐户应该完全locking,需要重新激活才能login。
为了防止进食的自动攻击,一个好主意可能是完全阻止一个在设定的时间内失败的login尝试失败的IP,无法访问login页面,直到它被再次手动列入白名单。
WRT Config not in database , use memcache with, eg, a five-minute expiry. A more sophisticated option is to touch a "reload config" page whenever the config changes; the trick is to make sure you touch the page on every app server instance in the farm (which is why I prefer memcache).
The delve function in PHP, allows you to:
$a = delve($array,'abcd','default'); // $a->b['c']->a $b = delve($object,'abcd','default'); // $a->b['c']->a
Without this, you would have to do painful issets and empties and datatype checks all the bloody time. This is by far my most favourite function.
In regards to what makes more professional code is unit tests, documentation version control and do first (then if you fail to do, then revert to stack overflow).
/** * Delve into an array or object to return the value of a set of keys * @version 1, December 24, 2009 * @param mixed $holder * @param mixed $keys * @param mixed $default * @return mixed * @package balphp * @author Benjamin "balupton" Lupton <contact@balupton.com> - {@link http://www.balupton.com/} * @copyright Copyright (c) 2009-2010, Benjamin Arthur Lupton - {@link http://www.balupton.com/} * @license http://www.gnu.org/licenses/agpl.html GNU Affero General Public License */ function delve ( $holder, $keys, $default = null) { # Prepare $result = null; $end = false; # Prepare Keys ensure_keys($keys, $holder); # Handle $key = array_shift($keys); if ( $key === null ) { # Reched the end of our key array, so holder must be what we want $result = $holder; $end = true; } else { switch ( gettype($holder) ) { case 'array': if ( array_key_exists($key, $holder) ) { # We exist, so recurse $result = delve($holder[$key], $keys, $default); } else { $end = true; } break; case 'object': if ( /* Already accessible via normal object means */ isset($holder->$key) /* Is Doctrine Record */ || ( ($holder instanceOf Doctrine_Record) && ($holder->hasAccessor($key) || $holder->getTable()->hasField($key) || ($holder->hasRelation($key) && (!empty($holder->$key) || $holder->refreshRelated($key) /* < returns null, hence the OR and extra check > */ || isset($holder->$key)) ) // && $holder->$key->exists()) ) ) /* Is normal object */ || ( !($holder instanceOf Doctrine_Record) && method_exists($holder, 'get') && $holder->get($key) !== null ) ) { # We exist, so recurse $result = delve($holder->$key, $keys, $default); } else { $end = true; } break; default: $end = true; break; } } # Check Default if ( $end && $result === null ) { $result = $default; } # Done return $result; }
Full file (and library) available here: http://github.com/balupton/balphp/blob/master/lib/core/functions/_arrays.funcs.php
Automated Unit tests
Important, much in the way a healthcheck page is important. Once my code grows past 800 lines of code, I can't remember what I was doing at line 100 and I will likely break it without realizing it.
Internationalization/Locale Support
Provide application translations for different languages, and serve the appropriate language based on a user's locale settings.
Mobile Version
Detect a user-agent string and serve a mobile/touch-friendly version of your app. This is pretty easy to do using xhtml mobile profiles .
Use an ORM that supports multiple backends
Both as a developer and a user, its nice to be able to swap out database backends by editing a config file. That way, you can run on something like SQLite for dev/single-user uses, and something like PostgreSQL or MySQL for production.
(unobtrusive) Notification if Javascript is disabled
Ever been to Stack Overflow with JS turned off or disabled? Sometimes I forget that noscript is turned on, and SO helpfully tells me. Then I can make a decision about whether or not I want to turn it back on; but at least I know that I'm missing functionality, rather than just thinking the app is lame.
Firebug/Javascript logging
Providing lots of logging helps debug ajax issues. just doing console.log(message) in javascript will log the message to firebug. Have a Production/Development switch in your application config file that turns this on or off (have the php not generate the log messages in production mode)
Whole-site SSL/TLS encryption
As the recent release of Firesheep shows, it's trivial to steal session cookies in open WiFi environments (such as offered by many libraries, hotels, and other venues) and any un-switched Ethernet segments still kicking around. (In theory, on switched ones too )
With Firesheep now making it a two-click operation for even the average Joe, whole-site encryption is no longer something to be treated as a luxury. (Along with setting the secure flag on session cookies to ensure compatible browsers won't accidentally leak them into unsecured requests) Neither the extension nor the problem are going away.
According to Adam Langly's blog, Google found that SSL/TLS isn't as bad as people tend to guess and, with a little tweaking (which they're building on in Chrome), even the latency component of setting up a connection with extra round-trips can be effectively eliminated.
Even certificate costs aren't as big an issue as many people think, given that StartCom offers free SSL certs and their root cert is already present in all major browsers.
A Registry System.
I have spoken many times before in regards to storing objects and scope issues and the best way I know of to over come this is by using a Static / Abstract class with getters and setters that can help you transport objects around your application and save you having to modify classes so that you can specifically import objects for usage.
the class can be as simple as an abstract static class with 2 methods and 1 array, these are the basic ingredients to over come the great scope issue without effecting anything else within the oven.
Here's a small example of what i am talking about:
abstract class Registry { private static $objects = array(); public static function set($name,$object) { self::$objects[$name] = $object; } public static function get($name) { return self::$objects[$name]; } }
looking at the simplicity of that class there's nothing to be afraid of, nothing to make you modify your current framework / application to adopt this method, but if your unsure how it works let me give you some examples
Firsty lets say we have index.php
that includes startup.php
, and within startup.php
your including your libraries and core framework code, but in startup your loading the following
- 数据库
- 会议
- FileWriter
- input
- 产量
now if you wanted to use the FileWriter
object as a tool for logging within Database
object you would usually have a variable called $FileWriter
and you would use the global keyword in Database
object
But by doing the following:
Registry::set('Database', new Database()) Registry::set('Session', new Session()) Registry::set('FileWriter', new FileWriter()) Registry::set('Input', new Input()) Registry::set('Output', new Output())
Your storing everything within your Registry
object so lets take a look at our Database object / class
class Database { //.. public function connect() { if(!$this->connected()) { try { $this->connect(); }catch(Exception $e) { Registry::get('FileWriter')->Write('db_logs',array($this,$e),'unable to connect to database'); } } } //.. }
Now as you can see that Registry is available within the scope of the class, and its clean, safe, your not taking up extra variables or using more likes of code with globalization
, just clean simple and safe
Hope you enjoyed the read.
I know this is frightful code and deserves to be downvoted. Just wanted to give a example that Content Negotiation is actually easy to achieve.
function q_order($src) { $src = str_replace(",", ";q=0.99,", "$src;last;q=0.975"); // inject placeholder values foreach (explode(",", $src) as $prt) { // split entries $q[trim(substr($prt, 0, strpos($prt, ";")))] // append TOKEN until ";" = floatval(substr($prt, strpos($prt, "q=") + 2)); // and first float after "q=" } arsort($q); return($q); }
This allows quick sorting of HTTP_ACCEPT headers, which is useful for automatic alternating between RSS and Atom feed formats. Or just for setting the default language. (You'll often need language flags/links anyway, but it's a better default than just defaulting to english.)
$lang = q_order($_SERVER["HTTP_ACCEPT_LANGUAGE"]); if ($lang["de"] >= $lang["en"]) { ...
Configuration data not in database : Application data belongs into the database. But configuration settings do not. 为什么?
- It's not efficient to query the database for runtime options on every PHP request.
- Settings aren't changed on a daily basis, a static store is sufficient for most use cases.
- The disparity between the a database configuration file, yet having the remainder of the runtime options in the database seems silly.
- IMO it's often done due to lazyness, because setting up securely and modifying a file (ini) store is more involving than just another SQL table.
- As exception, user settings and per-domain configurations are unmanageable without a database store.
While seldomly needed in practice, I do in fact want my users to be able to edit the runtime configuration (once per year). So in effect I spend more time on config file modification functions than it would require with a database. Pros: easier backup and versioning than with SQL config table.
Cons : If access/archiving/versioning is not an issue, then SQL makes a good config store if combined with APC or memcached (for efficient access). Cache files are also a possible solution.
do you have personal nitpicks and code hobbyhorses, specifically small things that might count? Which unsavoured code or minor functionality do you spend an inordinate amount of time on to get right?
Correctly Reference Array Keys
Any non numeric Array Key (ie not an index) should be enclosed in single quotation marks to ensure it is handled by PHP correctly under all circumstances, and syntax is detected in most good code editors. 例如:
$array['key'];
不
$array[key];
Require/Include Once
Always ask yourself why you are using require_once and include_once. Most of the time it is to prevent duplicating code that you may or may not have already pulled- if this is a possiblity, have you really written your PHP in the most efficient and effective way?
Buffer output for Clean Loading and Improved Speed
Adding:
ob_start('ob_gzhandler');
To the beggining of scripts and:
ob_end_flush();
To the end will compress and buffer the output of your scripts so they are more rapidly loaded and not displayed incrementally as content is served.
Predefined Loops
Every time a loop is run the maximum value should be set before it initiates. I see too many incidences where functions are referenced (despite this being fairly widely covered) ie:
for($=0;$i<count($x);$i++){ // code }
This means the count function is run each time the code loops, which is inefficient. It is much better to do the following:
$stop=count($x); for($=0;$i<$stop);$i++){ // code }
RegEx vs PHP String/Numeric Handlers
Reduce reliance on RegEx's if you are handling strings and there is a PHP equivalent, stripos, strncasecmp and strpbrk are faster. strtr is faster than preg_replace by around 5x. ctype_alnum,ctype_alpha and ctype_digit can be used to help form validation in place of a RegExp.
Clean your Workspace
Always close your DB connections when you are finished with them, and always unset your variables. Its good, if perfectionistic, practice.
Consistent use of Quotes
Use single and double quotes CONSISTENTLY. ie if you use:
echo "this is a 'string' not a number";
Dont then use:
echo 'this is a "string" not a number';
If you need to reverse the order, dont- keep it the same and escape the recursive quotation mark. Also, PHP works with strings encapsulated with double quotation marks far better, you can encapsulate a variable without the need for concatination. 例如:
echo "this and $thisvariable here"; // instead of echo 'this and '.$thisvariable.' here';
Auto Permalinks
With this function anyone can set it as the href of a link and it will automatically get the url of the page for easy permalinking.
//Gets the URL of the current page //set $p to yes to echo the urlturn the url or no to re function page_url($p) { $s = empty($_SERVER["HTTPS"]) ? '' : ($_SERVER["HTTPS"] == "on") ? "s" : ""; $protocol = strleft(strtolower($_SERVER["SERVER_PROTOCOL"]), "/").$s; $port = ($_SERVER["SERVER_PORT"] == "80") ? "" : (":".$_SERVER["SERVER_PORT"]); switch ($p) { case 'yes': echo ($protocol."://".$_SERVER['SERVER_NAME'].$port.$_SERVER['REQUEST_URI']); break; case 'no': return $protocol."://".$_SERVER['SERVER_NAME'].$port.$_SERVER['REQUEST_URI']; break; default: echo('javascript:alert(\'invalid Argument in function page_url($p)\')'); } } function strleft($s1, $s2) { return substr($s1, 0, strpos($s1, $s2)); }
Human-readable cookies
I personally believe it's a bit artless to always resort to SESSIONs just because variables are easier to store there with PHP.
Session IDs are technically often cookies too, but not human-readable and opaque storage handles. So instead, if it's simple user-preferences (not autorization stuff), I like to send readable cookie names and values, eg product_order=desc
, or fav_background=orange
.
Usually I resort to sessions too. But for personal projects where I care about data privacy (meaning user rights ) I put up with all the downsides:
- More micromanaging overhead for multiple cookies.
- Validity checking on each cookie separately, because it all becomes user input.
- Might need to define an additional expiry cookie or otherwise random renewal. (Due to the fact that I find 2038 cookie expiry times likewise dissocial, and only use appropriate times.)
- Cannot be used for anything authorization-related, but just user and display options.
- I believe it's important to recognize a semantic difference between real cookies and session cookies. But is it important to take care here, if hardly anyone sees it?
However it makes my data privacy brain part happy. And for some use cases it's a simple no-brainer.
Testability
I don't mean a quick script that "health checks" the application, those are mostly worthless. If most or all of your code is covered by strong tests, you and your customer are more likely to benefit from well designed, agile code.
Readability
Others will be using your code, and you will be updating it later on. If you can't read your code, it's worthless (comments don't count by the way). Variables should be named properly and the flow of context to context should be easy to identify.
for ( $i=0; $i < count($myList); $i++) // obviously an index foreach ( $k as $a => $b ) // wtf? foreach ( $definitions as $word => $definition ) // better
Obfuscation (extended comment)
This is to extend on a comment I made previously which has attracted a number of comments from users wishing to know more. As such I've split it out from the previous answer.
A large number of Web Apps are Javascript based (centric) and as such the ability for reverse engineering is huge as the client has access to the really important part of the code, which typically handles the UI and directs the backend in most of its tasks. At some point the script is likely to handle, or fire, traffic containing much of the data the App deals with, and as such it is easy to expose a lot of the underlying structure of the wider application. One often overlooked technique to protect your own IP is to obfuscate the code, hiding the true purpose of the variables, functions and objects you've written.
Obfuscation is a technique used to complicate code. Obfuscation makes code harder to understand when it is de-compiled, but it typically has no affect on the functionality of the code. Obfuscation programs can be used to protect Java programs by making them harder to reverse-engineer.
Obfuscation: http://en.wikipedia.org/wiki/Obfuscated_code
Using PHP to obfuscate your Javascript (JS) is relatively easy, you can simply create an index of terms to replace, use PHP to open your script replace those terms with their obfuscated counterparts and write the result.
Obfuscating code on the fly in PHP is also easy, though it creates some delay (depending on the size of your files/index). Obfuscating your JS on the fly means values change between visits/page views, adding an additional layer of noise, as the underlying code appears to alter every time the page is loaded.
To illustrate how one may introduce obfuscation into their app, I will examine one possible implementation, this utilises a great great PHP script, PHP Minify, which offers further benefits that will be explained.
Say we have 3 Javascript files, JS1, JS2 and JS3. These scripts have to appear in the order 1, 2, 3- and at present are linked seperately in the header of the master HTML (or PHP) file which delivers our application to the user. JS1 and JS2 are off the shelf external frameworks, such as (eg) jQuery and Prototype (ignore the fact you likely wouldnt have both in your App). JS3 is YOUR javascript, where you utilise the functionality in JS1 and JS2 using your own functions, variables, objects, classes etc. It is your intellectual property (IP) and/or must be obfuscated for legal, financial or selfish(!) reasons.
Obviously, it isnt helpful for us to obfuscate our JS, then have to develop it further. There is a reason we call our functions things like 'function_to_save_secret_info', it makes it easier when we are writing our code (though this highlights why one may want to obfuscate). The ideal would therefore be to keep our JS file that we develop, with the common sense naming, then every time a user requests the master HTML/PHP page, an obfuscated version of this is created on the fly and served. But how do we do this?
One of the best ways is using PHP Minify (http://code.google.com/p/minify/). PHP Minify has a number of advantages. One of these is the ability to group and serve scripts as a single bundle. So, instead of linking to JS1, JS2 and JS3 in our header, we use PHP Minify to set up a group which comprises these three, we link to this group in the header so only have a single reference. Advantages of this include:
- Less HTTP requests, faster loading and better caching (one not three)
- PHP Minify automatically YUI compresses scripts within groups, making load times far faster (also a type of obfuscation in its way)
- Easier management of our JS, altering groups is a breeze
- Less straight forward to inspect our code.
Once we do this, our master page links to our PHP Minify group, which is great- but we effectively are still serving the same content (all be it in a better way).
However, what we can do now is build our obfuscation routine for JS3. In the groupsConfig.php script where we define our script groups in PHP Minify, we change the group, replacing JS3.js with JS3.obfuscated.js. However, we still have the plain english version of JS3.js, JS3.obfuscated.js does not exist.
So basically our main/master HTML/PHP page links to our JS group in its header, which is a reference to PHP Minify where the group is defined as :
JS1.js JS2.js JS3.obfuscated.js
What we now need to do is build a key table/function to obfuscate JS3 and outputs JS3.obfuscated.js. At the simplest level, what we do is write PHP which contains the following (for example, this is not the best code):
$terms_to_obfuscate=array( 'my_secret_function', 'my_secret_object', 'my_secret_variable' ) foreach ($terms_to_obfuscate as $key => $value) { //replace base64_encode(md5(rand(0,999))) with whatever you want to produce an obfuscated term $obfuscated_terms[]=base64_encode(md5(rand(0,999))); } $source_js=file_get_contents('JS3.js'); $fh = fopen('JS3.obfuscated.js', 'w+') or die("can't open file"); $obfuscated_js = str_replace($terms_to_obfuscate, $obfuscated_terms, $source_js); fwrite($fh, $obfuscated_js); fclose($fh);
What this script will do, is open our english language version of JS3, look for the terms we want to obfuscate and replace with randomised content, writing the output to JS3.obfuscated.js. If we place this code ABOVE the group definition in PHP Minify, it will run each time the JS group is called…so each time the underlying master HTML/PHP page for our application loads, our script is randomly obfuscated. Note, this may not be the best implementation in all cases, this is illustrative only.
This is only one possible way of accomplishing obfuscation, but what we now have is compressed JS served faster to our users, randomly obfuscated, and we can still develop our script in 'plain english'. All we have to do is update our list of terms to obfuscate. There are better ways of doing this, this is meant only as an illustration and not a definite, of the general ideas behind the concept.
It is important to note, that obfuscation may only be appropriate for certain projects (it is not particularly open source by any means), and is not a total protection against reverse engineering, but should be viewed as more of a stumbling block for those less determined.
Human-UNREADABLE and very secure session cookies
I believe that human-readable cookies, when they refer to session things (ie autologin) are a security threat. From the experience with PHP-Nuke, which was full (years ago, around version 7) of admin login exploits, I became to encrypt cookies. Also, since cookies are almost all transmitted in clear, I became to bind them to the IP , or better the subnet.
I18n, l10n
Localization is important. I never write human-readable text in pages, I prefer making the website in English and Italian using a shared string library. Usually I override with English words those strings that are not yet translated to avoid displaying bad string IDs
Theming support
A web application looks very professional to my eyes if user can change theme during their browsing. Theming means not only CSS, but also all the graphics (icons, buttons) must be changeable without touching the core . When I wrote phpMyBitTorrent up to 1.2 version I paid lots and lots of attention to theming, which unfortunately resulted in the same exact layout being exposed with different colours/fonts/images. The next-generation theming is templating , so you can completely change the appearence of your website. I believe it's easier to template your website when you use MVC pattern .
Cross-database support
Or, better, DB idependence. I don't like calling explicitly mysql_query() in my code. I prefer running queries onto an abstraction layer that allows me to change DBMS (ie. to SQLite or Oracle) without rewriting the code of the core components.
Logging
Logging is the best way to ease debugging and to collect useful information for FFDA (field failure data analysis). Apache log4php does the trick but you must properly instrument your code with adequate logging statements to get usable information. Lots of academic studies demonstrate that developers never achieve an adequate level of logging: they log too much or not enough, and often faults remain unclear because of missing or unclear logging statements. Unfortunately, logs grow fat with time, so you must be able to keep only those records that may help you find issues about your applications. Logs can also be used for performance purposes, but never forget the overhead logging introduces. Finally, my current senior grade thesis is about a scientifically-proven set of logging rules that are suitable for FFDA in complex applications, to which every developer should take a look.
ORM
Well, this final point is more about cross-database support. To tell you the truth, I became to use NHibernate ORM when I abandoned PHP for ASP.NET. If I had to resume PHP, I would first find a suitable ORM to avoid queries in my code.
These are my smart ideas