提问人: 提问时间:9/27/2010 最后编辑:8 revs, 3 users 99%mario 更新时间:2/5/2012 访问量:4049
使您的应用程序更专业的特性/功能?编码爱好马
Features/functions that make your app more professional? Coding hobbyhorses
问:
您在 PHP Web 应用程序中实现(如何)哪些功能,因为 你认为它在某种程度上“更专业”?或者你有个人的吹毛求疵 和代码爱好,特别是可能很重要的小事? 你花了大量的时间在哪些不愉快的代码或次要功能上来做对?
.
Q&A 插图的编码爱好示例:
不在数据库中的配置数据:应用程序数据 != 配置数据,其中 也是一个必要性和效率的问题。
URL 修复:通过附加尾部斜杠来规范化所有网址,即使从技术上讲不是必需的。
人类可读的 cookie:为了数据隐私,我尽量避免使用不透明的会话/数据库句柄(用于用户选项,而不是授权使用)。
内容协商:对简单的变体有意义 例如.RSS和 Atom 格式之间。但我看到它很少使用。
UI 中没有数据库 ID:避免将数据库内部代理项密钥泄漏到 URL 中。使用 ORM,db-internal 密钥甚至不必泄漏到业务逻辑中。
.
提示(非规则)
- 那么,您认为哪些功能使您的 Web 应用程序高于平均水平?
- 为什么它不常见?
- 它是否使用户受益,但同样容易被忽视?
- 更专业、更安全的编码建议非常切合主题。他们总是如此。
- 但这个问答的预期范围实际上是不常见/独特的功能,也可能是非标准和有争议的功能。令人着迷的大奖金。
- 它还与恰好在PHP中实现的编码偏好和吹毛求疵有关。
- 不要想得太大或太高。小功能也很重要。
- 如果可行,请显示代码。
- 然而,语法和编码风格/范式大多是偏离主题的。
- 我们不要争论有用性或代码质量。这纯粹是一个特征和代码调查。
第一轮特征研究赏金:很难从众多好主意中选出一个。说实话,我只能将其缩小到五个最爱,并将决定权留给.这个话题绝对足够有趣,值得第二轮赏金。休息了一会儿。也许其他人会接管来完善范围。rand()
答:
以 Jus 为例: URL“修复”
对于 http-URL,我将路径片段视为强制性的,就像每个浏览器一样
确实如此。因此,我强调要“修复”用户输入和导入的数据,并始终添加
在显示或存储此类值之前,尾部斜杠到例如 http://example.org。
可能不是“超级专业”,但规范化 URL 通常会简化处理
以后和他们在一起。而且不知道,它只是看起来“更好”。
$url = preg_replace("/^(http:..[-\w.\d]+)$/", "$1/", $url);
所以无论如何,我都会一直有价值观。
这并不能使你的HTML更符合标准,但这只是我的烦恼之一。http://example.org/
评论
$url = sprintf('%s/', rtrim($url, '/'));
WRT Config 不在数据库中,请使用 memcache,例如,五分钟的到期时间。一个更复杂的选项是,每当配置发生变化时,都会触摸“重新加载配置”页面;诀窍是确保触摸场中每个应用程序服务器实例上的页面(这就是我更喜欢 memcache 的原因)。
评论
文档。
想象一下,你可以找到任何开源项目,无论有没有最新的文档,并感受到这对你认为它的专业程度的影响。
评论
对于专业的外观,非常重要的是干净和美观的图形设计。因为封面是现在最畅销的东西。可悲,但真实。具有漂亮代码和丑陋外观的 Web 应用程序不会引起太多关注。
评论
一致的编程风格、变量命名、支撑等。遵守编码标准(任何编码标准)。确保由不同人编写的文件看起来相同,这样维护代码就不需要知道是谁编写的。
评论
any coding standard
密码强度/匹配
在注册或更改密码时,以交互方式通知用户其密码的强度弱点。当他们的确认匹配时,还要通知他们。
实时表单验证
与密码一样,表单条目的实时验证比允许用户在知道自己犯了错误或省略他们跳过的必填字段之前完成整个表单要少得多。
具有友好异常/错误处理的防御性编程
永远不要使用隐晦的错误消息,并且通常参考您可以找到的最佳案例示例,以获取友好的错误消息。在最好的情况下,这通常需要一位才华横溢的撰稿人负责保持良好的语气等。
这与可靠的异常处理和良好的防御编程密切相关。
《防御设计》是一本关于这个主题的相当不错的书。
注意安全,尤其是用户的私人数据。
使用强哈希(如 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
不应为静态值。对于每个用户来说,它应该是不同的。
评论
$salt
也应该是用户无法更改的内容,因为这会使他们的密码失效,他们必须更改新密码。所以,如果用户名是永久性的,那就太好了;如果没有,可能是他们的行 ID、创建时间等。
crypt()
延续点
示例:您提交了一个表单,您被重定向,现在登录页面应该保留通知。大多数站点只是将该通知存储在会话中。这意味着:接下来将访问的任何页面都将显示该消息。在大多数情况下没问题,但对我来说真的很混乱。我认为会话是针对会话数据的,而不是下一个请求数据。
我的解决方案:创建一个数组,将数据(通知等)写入其中,使用 sessionid + 唯一标识符将其保存在 memcache 中,然后在重定向上添加参数 __continuation={该唯一标识符}。然后,下一页再次加载该数据并对其进行处理。
当您想要拥有更多数据而不仅仅是一条短消息时,这将变得更加方便。
问题?
评论
?msgid=5
我知道这是可怕的代码,应该被否决。只是想举个例子,内容协商实际上很容易实现。
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);
}
这允许对HTTP_ACCEPT标头进行快速排序,这对于在 RSS 和 Atom 源格式之间自动交替非常有用。或者只是为了设置默认语言。(无论如何,您经常需要语言标志/链接,但这比仅默认为英语要好。
$lang = q_order($_SERVER["HTTP_ACCEPT_LANGUAGE"]);
if ($lang["de"] >= $lang["en"]) { ...
任何 2.0 Web 应用程序都应该具备的重要内容之一是 -
- 搜索 - 无论是全文搜索、关键字搜索、自动建议还是(最新的)实时搜索。如果没有这个功能,当我访问任何网站(尤其是那些对搜索引擎关闭的网站)时,我会感到窒息。对于任何现代 2.0 站点 PHP,无论是否都必须具备。
- MVC 框架 -由于这与 web-dev 有关(不依赖于 PHP),因此 Model-View-Controller 框架是必须的。由于过多的代码库和更快的迭代周期,适当的结构是防止未来麻烦和无法追踪的错误所必需的。
- 阿贾克斯 -需要我谈谈这个:)它改变了我们大多数人浏览网站的方式。AJAX使网站具有很酷的功能。
- 调试信息 -这适用于应用程序崩溃时的情况。你如何找到问题所在?你们维护日志吗?您能否从日志中将问题追溯到其来源(发生数小时后)?您应该让日志有多详细?
- 缩小的 Javascript 和 CSS -减少加载时间,使网站更加活泼,从而使用户在浏览网站时感觉更舒适。
- 缓存-再次极大地增加了用户的感知响应时间,从而使网站更加“专业”。
评论
运行状况检查
一系列预配置的测试,用于确定 Web 应用程序或网站的基本“运行状况”,可以随时(由管理员/网站所有者)查看,显示以下内容:
- 数据库服务器是否可访问?
- 必要的文件和目录是否可写?
- 所有的数据结构都是理智和完整的吗?
- 所有页面/对话框/查询是否正确显示?
- 是否有足够的可用磁盘空间?
- 等等等等。
一个简单的状态页面以绿色、橙色或红色背景显示系统的当前“运行状况”。
这与单元测试的不同之处在于,它处理的是“真实世界”环境的运行状况,而不是抽象的代码完整性。
运行状况检查还使将应用程序迁移到另一台服务器变得容易。
我已经在几个需要持续监控的项目中实现了这一点;我计划在今年的某个时候把它做成一个小型的、可扩展的“插入式”开源应用程序,可以在各种 Web 应用程序甚至网站中使用。非常欢迎参与和投入 - 有关更多详细信息,请参阅相关的 SO 问题。
评论
编写易于维护的代码。在开始编写之前,请多想几遍对象的界面。添加许多事件触发器,即使您现在不需要它们。它们在功能中可能很有用。
编写代码,就好像它是外部库一样。即使您正在处理商业、封闭的应用程序,也应将其视为开源项目。想想其他用户,他们可能希望在不更改现有代码的情况下更改现有代码的行为。当你使用类型提示时,你可能应该检查接口而不是类,所以:
public function add(Storable $resource); # Good
public function add(SessionStore $session); # Bad
不要使用全局命名空间和“全局功能”。定义全局常量或使用(而不是使用 注册另一个自动加载器)是一种不好的做法。__autoload()
spl_autoload_register()
使用第三方代码。那里有很多图书馆。大多数时候,开发自己的ORM或Mailer是没有意义的。它减少了开发应用程序所需的时间,>降低了成本。
创建 API 文档。我认为不需要任何解释。
使代码看起来干净,并保留一些编码约定。
不要创建无用的页面。没有什么比 404 未找到页面更烦人的了,仅此而已,只是一个大的 404 未找到信息。您可能应该为每个资源提供几个未找到的页面。因此,如果我访问并且此类资源不存在,除非我们很抱歉,该视频不存在或已被删除。 添加如下内容: 最近的视频, 也许您正在寻找:“how to dace”、“swimming: abc”(标题类似于标题形式的视频 URL(如何游泳))等。/video/123/how-to-swim
允许用户自定义网站。想象一下,您有一个包含多个“框”的主页:最近的视频、最近的照片、最近的活跃主题、新闻、推广的画廊等。允许用户隐藏其中一些,更改他们的顺序 - 有人可能对照片和视频更感兴趣,而不是新闻和线程,所以他/她可能希望将它们放在页面顶部,其他人可能有不同的偏好,可能希望有不同的顺序。为每个框添加首选项,例如:“显示:5、10、20 个最近的视频”。
评论
配置数据不在数据库中:应用程序数据属于数据库。 但配置设置没有。为什么?
- 在每个 PHP 请求上查询数据库的运行时选项效率不高。
- 设置不会每天更改,静态存储对于大多数用例来说就足够了。
- 数据库配置文件之间的差异,但在数据库中拥有其余的运行时选项似乎很愚蠢。
- IMO 这通常是由于懒惰而完成的,因为安全地设置和修改文件 (ini) 存储比另一个 SQL 表更复杂。
- 例外情况是,如果没有数据库存储,则无法管理用户设置和每个域的配置。
虽然在实践中很少需要,但实际上我确实希望我的用户能够编辑运行时配置(每年一次)。因此,实际上,我在配置文件修改功能上花费的时间比数据库所需的时间还要多。优点:比 SQL 配置表更容易备份和版本控制。
缺点:如果访问/存档/版本控制不是问题,那么如果与 APC 或 memcached(用于高效访问)结合使用,则 SQL 可以很好地存储配置。缓存文件也是一种可能的解决方案。
人类可读的 cookie
我个人认为,仅仅因为变量更容易用 PHP 存储在那里,总是求助于 SESSION 有点不艺术。
从技术上讲,会话 ID 通常也是 Cookie,但不是人类可读和不透明的存储句柄。因此,相反,如果它是简单的用户首选项(不是自动化的东西),我喜欢发送可读的 cookie 名称和值,例如 或 .product_order=desc
fav_background=orange
通常我也会求助于会议。但是对于我关心数据隐私(即用户权利)的个人项目,我忍受了所有的缺点:
- 为多个 Cookie 提供更多的微观管理开销。
- 分别对每个 cookie 进行有效性检查,因为它都成为用户输入。
- 可能需要定义额外的过期 Cookie 或以其他方式随机续订。(由于我发现 2038 cookie 到期时间同样不合群,并且只使用适当的时间。
- 不能用于任何与授权相关的内容,而只能用于用户和显示选项。
- 我认为识别真实 cookie 和会话 cookie 之间的语义差异很重要。但是,如果几乎没有人看到它,那么在这里要小心吗?
然而,这让我的数据隐私大脑部分感到高兴。对于某些用例,这是一个简单而简单的不费吹灰之力的事情。
评论
PHPSESSID
UI
中没有数据库 ID 大多数数据库对象都是使用某些代理键枚举的。我做什么
认为看起来不时尚(有时可能是一个安全陷阱)正在将这些泄漏到 UI 中,就像在 URL 中一样。将任何占用更改为 .对于数据库来说,查找 ID 或名称几乎没有区别。所以这不是关于SEO的愚蠢,而是关于一个华丽的外表user.php?id=37310843
user.php?name=title
- 有了适当的 ORM,这些数据库内部 ID 甚至不必泄漏到应用程序逻辑中。
- 展示维基百科:想象一下他们使用代理键而不是页面标题。(见 CoWiki)
- 由于外部链接始终指向标识内容,因此仍应避免使用代理键。如果它足够重要,可以进行确切的分配,那么无论如何都应该使用 GUID。
添加更多(较少面向 PHP)的点,这将增强用户体验并使您的应用程序更专业,但会处理后端:
良好的压缩/缩小
通常,Web 应用在加载时和使用过程中都会发送大量请求。缩小可以帮助减少初始加载权重,CSS 精灵和 GZipping 内容的使用也是如此。帮助您简化和尽可能快速地制作应用程序的一个好方法是 Yahoo 的 Firefox Firebug 扩展的 YSlow 插件:
YSlow:http://developer.yahoo.com/yslow/
另外,一些好的做法:http://developer.yahoo.com/performance/rules.html
YSlow 将帮助您确定如何真正使您的应用程序整洁、快速和干净。我还推荐 PHP Minify 来处理大部分文件压缩,它是一个非常强大的工具:
PHP缩小:http://code.google.com/p/minify/
此外,获得 Firebug 的另一个原因是它在开发任何 Web 应用程序时提供的巨大好处,尤其是确定应用程序的安全性,因为在操作过程中您可以跟踪创建的数据流。
混淆
---拆分为下面更详细的答案
Apache重写的
好用 除了提供干净的URL以增强浏览体验和给人以逻辑归档内容的印象的基本好处外,一个好的.htaccess文件还可以为您的网站添加额外的安全层,压缩内容服务器端并添加额外的标识符(例如E-Tags,尽管它们的好处是有争议的)。每个 Web 应用程序也应该有一个写得很好的 .htaccess。
兼容性/验证
我总是强烈敦促任何开发人员尽最大努力(合理)确保他们所有的输出代码都是有效的、注释的、逻辑的和干净的。W3C 不仅明确规范了 HTML,还明确规范了 Web 应用程序应该如何运行。遵循他们的指导,以提高你所写任何内容的兼容性,以确保它以你所设想的方式适用于每个人。
一些很棒的工具:
HTML 验证器:http://validator.w3.org/
其他验证: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 可以帮助你更好地构建代码以进行开发、预缩小:http://jsbeautifier.org/
当然是美化的PHP......http://beta.phpformatter.com/
最后 - 少一点黑白
的人性化
也许生产基于 Web 的应用程序的最大好处之一是它们可以提供连接,不仅在用户之间(为了鼓励协作工作),而且在用户和应用程序本身以及负责其持续开发和维护的人之间。
如果你想到像 37 Signals 这样的项目,其中一个吸引人的因素是它们给用户的感觉是项目(代码)本身已经人性化并具有个性,有助于吸引用户并与应用程序相关联,鼓励享受使用和与他人交流。我并不是说这个应用程序看起来“活着”,而是说它感觉更“平易近人”,用户可以“认同”它。
然而,这只是人性化硬币的一面,它几乎要让用户对应用程序/代码/框架/体验产生“共鸣,从而鼓励他们使用它。另一方面是打破应用程序与其背后的人之间的界限。
我们都喜欢了解我们使用的东西背后的人、人等——因为我们从他们那里传授了很多信息,并且经常不合逻辑地将其应用于应用程序本身,实际上是我们对它的“喜欢”(即像史蒂夫乔布斯,购买苹果等。虽然诚然是一个任性的例子)。拥有一条我们可以在遇到困难时联系真人的途径是一个简单的步骤,拥有开发人员博客、新闻流等——围绕应用程序构建一个人性化的元宇宙会给它带来可信度,并感觉它可能大于其各部分的总和。
诚然,人性化可能既不在这里也不在那里——而且它当然很难构建(就像不同的人有不同的朋友一样),但开发人员可以通过任何方式打磨一个更临床的外科手术应用程序的边缘,让它对日常用户来说更舒适、更平易近人、更有趣,这是我书中的赢家。
评论
测试
我不是说“健康检查”应用程序的快速脚本,这些脚本大多毫无价值。如果你的大部分或全部代码都经过了强大的测试,你和你的客户就更有可能从精心设计的敏捷代码中受益。
可读性
其他人将使用您的代码,您稍后将对其进行更新。如果你看不懂你的代码,它就毫无价值(顺便说一句,注释不算在内)。变量应正确命名,上下文到上下文的流程应易于识别。
for ( $i=0; $i < count($myList); $i++) // obviously an index
foreach ( $k as $a => $b ) // wtf?
foreach ( $definitions as $word => $definition ) // better
PHP 中的 delve 函数允许您:
$a = delve($array,'a.b.c.d','default'); // $a->b['c']->a
$b = delve($object,'a.b.c.d','default'); // $a->b['c']->a
如果没有这个,你将不得不一直做痛苦的 issets 和 empty 以及数据类型检查。这是迄今为止我最喜欢的功能。
关于使代码更专业的是单元测试,文档版本控制和先做(然后如果你做不到,那就恢复到堆栈溢出)。
/**
* 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 <[email protected]> - {@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;
}
完整文件(和库)可在此处获得: http://github.com/balupton/balphp/blob/master/lib/core/functions/_arrays.funcs.php
评论
a.b.c
a.b
b.c
$keys
implode()
explode()
你有个人的吹毛求疵和代码吗 爱好马,特别是小东西 这可能算数吗?哪个不咸不香 代码或次要功能 花费过多的时间 做对了吗?
正确引用数组键
任何非数字数组键(即不是索引)都应该用单引号括起来,以确保 PHP 在任何情况下都能正确处理它,并且在大多数好的代码编辑器中都能检测到语法。例如:
$array['key'];
不
$array[key];
需要/包含一次
总是问问自己为什么要使用require_once和include_once。大多数时候,这是为了防止复制您可能已经或可能尚未提取的代码 - 如果这是可能的,您真的以最有效和最有效的方式编写了 PHP 吗?
缓冲输出,用于清洁加载和提高速度
添加:
ob_start('ob_gzhandler');
到脚本的开头,以及:
ob_end_flush();
到最后,将压缩和缓冲脚本的输出,以便更快地加载它们,并且不会在提供内容时以增量方式显示。
预定义循环
每次运行循环时,都应在启动之前设置最大值。我看到太多引用函数的事件(尽管这被相当广泛地覆盖),即:
for($=0;$i<count($x);$i++){
// code
}
这意味着每次代码循环时都会运行 count 函数,效率低下。最好执行以下操作:
$stop=count($x);
for($=0;$i<$stop);$i++){
// code
}
正则表达式与 PHP 字符串/数字处理程序
减少对正则表达式的依赖 如果你正在处理字符串并且有一个 PHP 等价物,stripos、strncasecmp 和 strpbrk 更快。strtr 比 preg_replace快 5 倍左右。 ctype_alnum,ctype_alpha 和 ctype_digit 可用于帮助表单验证代替正则表达式。
清理工作区
完成数据库连接后,请始终关闭它们,并始终取消设置变量。如果完美主义的话,这是很好的实践。
一致使用报价
始终如一地使用单引号和双引号。即,如果您使用:
echo "this is a 'string' not a number";
然后不要使用:
echo 'this is a "string" not a number';
如果您需要颠倒顺序,请不要 - 保持不变并转义递归引号。此外,PHP 可以更好地处理用双引号封装的字符串,您可以在不需要连接的情况下封装变量。例如:
echo "this and $thisvariable here";
// instead of
echo 'this and '.$thisvariable.' here';
评论
'
"
'
"
密码劫持
我们最初在这里得到了一个答案,那就是支持和反用户安全。该建议是关于用户注册的,并使用提供的注册电子邮件与用户 SMTP/POP3(?) 服务器检查提供的密码,如果用户懒惰地重复使用他的电子邮件/POP3 帐户密码,则会给出一条粗鲁的错误消息。
显然,它被否决了,因为闯入用户的电子邮件帐户是值得怀疑的(即使意图显然是为了教育)。所以答案被删除了。但我喜欢对用户粗鲁的元概念,如果它有助于他的安全。
其次,我认为,虽然这个功能对于公共网站来说是一个法律上的错误功能(如果事先没有明确的警告或其他信息),但对于 Intranet Web 应用程序来说,这是一个非常可能的概念。我认为,对于公司内部的IT策略来说,确保密码在更敏感的系统中是唯一的,这甚至是一件很常见的事情。
评论
无强制注册
这是日常 Web 应用程序的常见问题。强制帐户注册对用户不友好。如今,防止垃圾邮件和滥用是必要的。但它阻碍了采用。
我想每个人都听说过 Stackoverflow。它们不强制注册。您可以在没有帐户的情况下参与。它不是强迫注册,而是系统地鼓励注册。然而,主要的一点是,至少可以选择在承诺之前逐渐适应。
这是一个简单的概念。但这同样难以实现。很少可以使用临时和可选用户帐户,必须为此准备基本应用程序逻辑和数据库结构。无论实施工作如何,我相信这是一个重要且未被重视的功能。
评论
自动化单元测试
很重要,就像健康检查页面一样重要。一旦我的代码增长到 800 行以上,我就不记得我在第 100 行做了什么,我可能会在不知不觉中破坏它。
注册表系统。
我之前已经多次谈到存储对象和范围问题,我所知道的克服这个问题的最好方法是使用带有 getter 和 setter 的 Static / Abstract 类,这可以帮助您在应用程序周围传输对象,并节省您修改类的麻烦,以便您可以专门导入对象以供使用。
该类可以像具有 2 个方法和 1 个数组的抽象静态类一样简单,这些是克服大范围问题的基本要素,而不会影响烤箱中的任何其他内容。
这是我所说的一个小例子:
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];
}
}
看看这个类的简单性,没有什么可害怕的,没有什么可以让你修改你当前的框架/应用程序来采用这种方法,但如果你不确定它是如何工作的,让我给你一些例子
首先,假设我们有 ,并在您的包括您的库和核心框架代码中,但在启动时加载以下内容index.php
startup.php
startup.php
- 数据库
- 会期
- 文件编写器
- 输入
- 输出
现在,如果您想将该对象用作在对象中记录的工具,您通常会调用一个变量,并在 Object 中使用 global 关键字FileWriter
Database
$FileWriter
Database
但通过执行以下操作:
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())
您将所有内容存储在您的对象中,因此让我们看一下我们的数据库对象/类Registry
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');
}
}
}
//..
}
现在,正如你所看到的,注册表在类的范围内是可用的,而且它干净、安全,你不占用额外的变量或使用更多喜欢的代码,只是干净、简单和安全globalization
希望您喜欢阅读。
源代码管理。特别是 Git(虽然不是专门的 GitHub)
好吧,这并不少见。
但是,当我外包代码并取回一个整洁的存储库时,其中包含所有的历史和曲折,我印象远比一个装满 php 文件的文件夹更深刻。
它展示了在项目中投入了多少看不见的额外工作。
我使用 GitFlow,它在使用过程中需要更长的时间,但在完成的项目中更令人印象深刻,从而弥补了这一点。确保客户端看到 github 图形(或等效项)所有这些树枝看起来都很整洁,令人印象深刻。(除了实际上有用!
相关新闻:
问题跟踪系统。
在代码完成之前(当客户正在审查时)和之后,以允许他们添加新任务。
这不仅是一种划分任务和获得更多工作的方式,它让客户觉得这个项目仍然在我的脑海中。尽管我继续前进。
我使用 Collabtive,这绝对是可怕的,因为这些系统的发展,但看起来比我尝试过的任何其他东西都更令人印象深刻,看起来令人印象深刻的东西被认为是专业的。
评论
企业集团应用
扪心自问 - 如果您正在运行在线商务系统,而您的卡支付供应商离线,您的客户是否仍然能够浏览您的产品,甚至使用不同的支付方式结账?
应用程序的构建方式应允许切换“开关”,这不会使整个站点进入“维护模式”,而只是以优雅的方式使应用程序中的页面或部分不可用,而应用程序的其余部分可供其他用户使用。
聚合应用程序可能是一个俗气的名字,但我完全确定它有更好的东西,但它绝对是许多人在开发可用应用程序时容易忘记的方法之一。
智能暴力破解保护
假设您的应用程序中有一个管理部件,其中包含相当多的客户信息。绝对是你不希望任何人拿到的数据。即使你有一个很好的盐和好的算法来散列你的密码,一个邪恶的用户仍然可以对你的登录表单进行DOS/彩虹表(蛮力?)攻击。
第一种选择显然是使用自动化软件来执行这些攻击,其次是通过对密码进行合格的猜测来手动执行。找到有关用户的足够信息来猜测他或她的密码真的很容易,考虑到仅使用 Google 就可以获得多少信息。 此外,如果您居住在瑞典这样的国家/地区 - 您只需给当局打一个电话就找不到有关一个人的信息 - 社会安全号码、伴侣姓名、婚姻状况、孩子姓名、宠物以及更多信息,这些信息都汇集在一起,作为普通用户对密码的非常合格的猜测。
如果我们看一下普通人如何选择密码,安全性应该非常容易受到损害。当然,我们可以对用户密码的长度、特殊字符等设置限制,但这不会影响这种情况。仍然可以进行暴力攻击。
如果用户尝试登录,但第二次尝试从未知 IP 登录失败,则应显示验证码以防止自动登录。在另一 (n) 次尝试失败后,该帐户应完全锁定,需要重新激活才能登录。
为了防止自动攻击侵蚀性能,一个好主意可能是完全阻止在设定的时间跨度内进行 (n) 次登录尝试失败的 IP,使其无法访问登录页面,直到再次被手动列入白名单。
混淆(扩展评论)
这是为了扩展我之前发表的评论,该评论吸引了许多希望了解更多信息的用户的评论。因此,我将其从之前的答案中分离出来。
大量的 Web 应用程序都是基于 Javascript(以 Javascript 为中心)的,因此逆向工程的能力是巨大的,因为客户端可以访问代码中真正重要的部分,这些部分通常处理 UI 并指导后端完成其大部分任务。在某些时候,脚本可能会处理或触发包含应用程序处理的大部分数据的流量,因此很容易暴露更广泛应用程序的许多底层结构。保护你自己的IP的一个经常被忽视的技术是混淆代码,隐藏你编写的变量、函数和对象的真正目的。
混淆是一种用于 使代码复杂化。混淆使 代码何时更难理解 已反编译,但通常没有 对 法典。可以使用混淆程序 为了保护 Java 程序,请使 它们更难进行逆向工程。
混淆:http://en.wikipedia.org/wiki/Obfuscated_code
使用 PHP 对 Javascript (JS) 进行混淆相对容易,您可以简单地创建一个要替换的术语索引,使用 PHP 打开脚本,将这些术语替换为经过混淆的对应项并编写结果。
在 PHP 中动态混淆代码也很容易,尽管它会产生一些延迟(取决于文件/索引的大小)。动态混淆 JS 意味着值在访问/页面浏览量之间发生变化,从而增加了额外的噪音层,因为底层代码似乎在每次加载页面时都会发生变化。
为了说明如何将混淆引入他们的应用程序,我将研究一种可能的实现,它利用了一个很棒的 PHP 脚本 PHP Minify,它提供了更多的好处,将进行解释。
假设我们有 3 个 Javascript 文件,JS1、JS2 和 JS3。这些脚本必须按 1、2、3 的顺序出现,目前在主 HTML(或 PHP)文件的标头中单独链接,该文件将我们的应用程序交付给用户。JS1 和 JS2 是现成的外部框架,例如 jQuery 和 Prototype(忽略一个事实,你的应用程序中可能不会同时拥有这两个框架)。JS3 是你的 javascript,您可以使用自己的函数、变量、对象、类等来利用 JS1 和 JS2 中的功能。这是您的知识产权 (IP) 和/或出于法律、财务或自私 (!) 原因必须混淆。
显然,混淆我们的 JS 对我们来说是没有帮助的,然后必须进一步开发它。我们称函数为“function_to_save_secret_info”是有原因的,它使我们在编写代码时更容易(尽管这突出了为什么人们可能想要混淆)。因此,理想的情况是保留我们开发的 JS 文件,使用常识性命名,然后每次用户请求主 HTML/PHP 页面时,都会动态创建并提供其混淆版本。但是我们该怎么做呢?
最好的方法之一是使用 PHP Minify (http://code.google.com/p/minify/)。PHP Minify 具有许多优点。其中之一是能够将脚本分组并作为单个捆绑包提供。因此,我们不是在头文件中链接到 JS1、JS2 和 JS3,而是使用 PHP Minify 来设置一个由这三个组成的组,我们在头文件中链接到这个组,因此只有一个引用。这样做的优点包括:
- 更少的 HTTP 请求、更快的加载速度和更好的缓存(一个而不是三个)
- PHP Minify 自动 YUI 压缩组内的脚本,使加载时间更快(也是一种混淆方式)
- 更轻松地管理我们的 JS,更改组轻而易举
- 检查我们的代码不那么简单。
一旦我们这样做了,我们的母版页就会链接到我们的PHP缩小组,这很好 - 但我们实际上仍然提供相同的内容(所有这些都以更好的方式提供)。
但是,我们现在能做的是为 JS3 构建混淆例程。在 groupsConfig.php 脚本中,我们在 PHP 缩小中定义脚本组,我们更改组,将 JS3.js 替换为 JS3.obfuscated.js。但是,我们仍然有JS3.js的简单英文版本,JS3.obfuscated.js不存在。
所以基本上我们的主/主 HTML/PHP 页面在其标题中链接到我们的 JS 组,这是对 PHP Minify 的引用,其中该组定义为:
JS1.js
JS2.js
JS3.obfuscated.js
我们现在需要做的是构建一个关键表/函数来混淆 JS3 并输出 JS3.obfuscated.js。在最简单的层面上,我们所做的是编写包含以下内容的 PHP(例如,这不是最好的代码):
$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);
这个脚本将要做的是打开我们的英语版本的 JS3,查找我们想要混淆的术语并用随机内容替换,将输出写入 JS3.obfuscated.js。如果我们将这段代码放在 PHP Minify 中的组定义之上,它将在每次调用 JS 组时运行......因此,每次加载我们应用程序的底层主 HTML/PHP 页面时,我们的脚本都会被随机混淆。请注意,这可能不是所有情况下的最佳实现,这只是说明性的。
这只是实现混淆的一种可能方法,但我们现在拥有的是压缩的 JS,可以更快地提供给我们的用户,随机混淆,我们仍然可以用“简单的英语”开发我们的脚本。我们所要做的就是更新我们的术语列表以混淆。有更好的方法可以做到这一点,这只是为了说明而不是确定概念背后的一般思想。
需要注意的是,混淆可能只适用于某些项目(无论如何它都不是特别开源),并不是对逆向工程的全面保护,但对于那些不太坚定的人来说,应该被视为更多的绊脚石。
评论
人类无法读取且非常安全的会话 cookie
我相信人类可读的 cookie,当它们提到会话内容(即自动登录)时是一种安全威胁。根据PHP-Nuke的经验,它充满了(几年前,大约第7版)管理员登录漏洞,我开始加密cookie。 此外,由于 cookie 几乎都是明文传输的,因此我将它们绑定到 IP,或者更好的是子网。
I18n、l10n
本地化很重要。我从不在页面中编写人类可读的文本,我更喜欢使用共享字符串库用英语和意大利语制作网站。通常,我会用英语单词覆盖那些尚未翻译的字符串,以避免显示错误的字符串 ID
主题支持
如果用户可以在浏览过程中更改主题,那么 Web 应用程序在我看来看起来非常专业。主题不仅意味着 CSS,而且所有图形(图标、按钮)都必须在不触及核心的情况下进行更改。当我编写 phpMyBitTorrent 1.2 版本时,我非常关注主题,不幸的是,这导致相同的确切布局被不同的颜色/字体/图像公开。下一代主题是模板化的,因此您可以完全改变网站的外观。我相信当你使用MVC模式时,对你的网站进行模板化会更容易。
跨数据库支持
或者,更好的是,数据库依赖性。我不喜欢在我的代码中显式调用 mysql_query()。我更喜欢在抽象层上运行查询,这样我就可以更改 DBMS(即更改为 SQLite 或 Oracle),而无需重写核心组件的代码。
伐木
日志记录是简化调试和收集 FFDA(现场故障数据分析)有用信息的最佳方式。Apache log4php 可以解决问题,但您必须使用足够的日志记录语句正确检测代码以获取可用信息。许多学术研究表明,开发人员从未达到足够的日志记录级别:他们记录过多或不够,并且由于缺少或不清楚的日志记录语句,错误通常仍然不清楚。不幸的是,日志会随着时间的推移而增加,因此您必须能够仅保留那些可以帮助您查找有关应用程序问题的记录。日志也可用于性能目的,但永远不要忘记日志记录引入的开销。最后,我目前的高年级论文是关于一套经过科学验证的日志记录规则,这些规则适用于复杂应用程序中的 FFDA,每个开发人员都应该看看。
ORM的
好吧,最后一点更多的是关于跨数据库支持。说实话,当我放弃PHP ASP.NET 时,我开始使用NHibernate ORM。如果我必须恢复 PHP,我会首先找到一个合适的 ORM 来避免代码中的查询。
这些是我的聪明主意
评论
可读代码:
源代码必须写得很好,并且易于其他开发人员理解。它们必须具有短函数和离散类。它们必须具有描述性的函数和类名,这将导致较少的注释。函数或类必须具有单一的职责。每个函数的参数数量有限。智能编写的代码需要更少的分解、轻松升级、可扩展性等。
全站SSL/TLS加密
正如最近发布的 Firesheep 所显示的那样,在开放的 WiFi 环境(例如许多图书馆、酒店和其他场所提供)以及任何仍在运行的未交换以太网段中窃取会话 cookie 是微不足道的。(理论上,在切换的上也是如此)
现在,Firesheep 甚至对普通 Joe 来说也只需单击两次即可操作,因此全站加密不再被视为奢侈品。(以及在会话 cookie 上设置安全标志,以确保兼容的浏览器不会意外地将它们泄漏到不安全的请求中)扩展和问题都不会消失。
根据 Adam Langly 的博客,Google 发现 SSL/TLS 并不像人们想象的那么糟糕,并且通过一些调整(他们在 Chrome 中构建),甚至可以有效地消除设置额外往返连接的延迟部分。
甚至证书成本也不像许多人想象的那么大,因为StartCom提供免费的SSL证书,并且其根证书已经存在于所有主要浏览器中。
评论
一个经常被忽视的:可打印的CSS。编写起来很痛苦,但对于想要在应用程序中打印出某些数据的用户来说,这有很大的不同。
<link href="print.css" rel="stylesheet" type="text/css" media="print">
评论
@media print {}
@media screen {}
国际化/语言环境支持
提供不同语言的应用程序翻译,并根据用户的区域设置提供适当的语言。
手机版
检测用户代理字符串,并提供适合移动/触摸的应用版本。使用 xhtml 移动配置文件很容易做到这一点。
使用支持多个后端的 ORM
无论是作为开发人员还是用户,能够通过编辑配置文件来交换数据库后端都很好。这样一来,你就可以在 SQLite 之类的东西上运行,供开发/单用户使用,也可以在 PostgreSQL 或 MySQL 等东西上运行,用于生产。
(不显眼)禁用 Javascript 时的通知
曾经在关闭或禁用 JS 的情况下去过 Stack Overflow 吗?有时我忘记了noscript已打开,SO很有帮助地告诉我。然后我可以决定是否要重新打开它;但至少我知道我缺少功能,而不仅仅是认为该应用程序很蹩脚。
Firebug/Javascript 日志记录
提供大量日志记录有助于调试 ajax 问题。只需在 JavaScript 中执行 console.log(message) 就会将消息记录到 Firebug。在应用程序配置文件中有一个生产/开发开关来打开或关闭它(让 php 不要在生产模式下生成日志消息)
使用线程在后台尽可能多地工作。这样用户界面就永远不会冻结。 当过程记录器花费超过一秒钟才能完成时,会显示不错的进度条。
自动固定链接
使用此功能,任何人都可以将其设置为链接的 href,它会自动获取页面的 url,以便于永久链接。
//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));
}
评论
web-development
best-practices