在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
1. 引言 2. 适用范围 3. 标准化的重要性和好处 1. 程序员可以了解任何代码,弄清程序的状况 2. 新人可以很快的适应环境 3. 防止新接触PHP的人出于节省时间的需要,自创一套风格并养成终生的习惯 4. 防止新接触PHP的人一次次的犯同样的错误 5. 在一致的环境下,人们可以减少犯错的机会 6. 程序员们有了一致的敌人 4. PHP编码规范与原则 4.1.代码标记 4.2.注释 程序开发中难免留下一些临时代码和调试代码,此类代码必须添加注释,以免日后遗忘。所有临时性、调试性、试验性的代码,必须添加统一的注释标记“//debug”并后跟完整的注释信息,这样可以方便在程序发布和最终调试前批量检查程序中是否还存在有疑问的代码。例如: 4.3.书写规则 4.3.1. 缩进 4.3.2. 大括号{}、if和switch 4.3.3. 运算符、小括号、空格、关键词和函数 根据上述原则,以下举例说明正确的书写格式: 4.3.4. 函数定义 · 参数的名字和变量的命名规范一致; · 函数定义中的左小括号,与函数名紧挨,中间无需空格; · 开始的左大括号另起一行; · 具有默认值的参数应该位于参数列表的后面; · 函数调用与定义的时候参数与参数之间加入一个空格; · 必须仔细检查并切实杜绝函数起始缩进位置与结束缩进位置不同的现象。 例如,符合标准的定义: 4.3.5. 引号 · 字符串为固定值,不包含“\t”等特殊转义字符; · 数组的固定下标,例如$array[‘key’]; · 表达式中不需要带入变量,例如$string = ‘test’;,而非$string = “test$var”; 例外的,在正则表达式(用于preg_系列函数和ereg系列函数)中,phpcms全部使用双引号,这是为了人工分析和编写的方便,并保持正则表达式的统一,减少不必要的分析混淆。 所有数据在插入数据库之前,均需要进行addslashes()处理,以免特殊字符未经转义在插入数据库的时候出现错误。phpcms中如果已经引入了文件 common.inc.php,则所有通过 GET, POST, FILE,取得的变量默认情况下已经使用了addslashes()进行了转义,不必重复进行。如果数据处理必要(例如用于直接显示),可以使用 stripslashes() 恢复,但数据在插入数据库之前必须再次进行转义。 4.4.命名原则 4.4.1. 变量、对象、函数名 4.4.2. 常量 4.5.变量的初始化与逻辑检查 判断一个无法确定(不知道是否已被赋值)的变量时,可用empty()或isset(),而不要直接使用if($switch)的形式,除非你确切的知道此变量一定已经被初始化并赋值。 empty()和isset()的区别为: bool empty(mixed var) 如果 var 是非空或非零的值,则 empty() 返回 FALSE。换句话说,”"、0、”0″、NULL、FALSE、array()、var $var; 以及没有任何属性的对象都将被认为是空的,如果 var 为空,则返回 TRUE。 bool isset(mixed var[, mixed var[, ...]]) 如果 var 存在则返回 TRUE,否则返回 FALSE。如果已经使用 unset() 释放了一个变量之后,它将不再是 isset()。若使用 isset() 测试一个被设置成 NULL 的变量,将返回 FALSE。同时要注意的是一个 NULL 字节(”\0″)并不等同于 PHP 的 NULL 常数。 判断一个变量是否为数组,请使用is_array(),这种判断尤其适用于对数组进行遍历的操作,例如foreach(),因为如果不事先判断,foreach()会对非数组类型的变量报错; 判断一个数组元素是否存在,可使用isset($array[‘key’]),也可使用empty(),两者异同见上。 4.6.安全性 PHP中的变量不并不像C语言那样需要事先声明,解释器会在第一次使用时自动创建他们,同样类型也不需要指定,解释器会根据上下文环境自动确定。从开发人员的角度来看,这无疑是一种极其方便的处理方法。一个变量被创建了,就可以在程序中的任何地方使用。这导致的结果就是开发人员工经常不注意初始化变量。因此,为了提高程序的安全性,我们不能相信任何没有明确定义的变量。所有的变量在定义使用前要初使化以防止恶意构造提交的变量覆盖程序中使用的变量。 细节可以阅读(http://www.securereality.com.au/studyinscarlet.txt)这篇文档,该文档里罗列了PHP常见的安全问题,阅读该文档是非常有必要的! 4.7.兼容性 代码设计应当兼顾PHP 高低版本的特性,当前,应仍然以PHP 4.3.0作为最低通过平台,尽量不使用高版本PHP 新增的函数、常数或者常量。如果使用只在高版本才具备的函数,必须对其进行二次封装,自动判断当前PHP版本,并自行编写低版本下的兼容代码; 对于个别函数,参数要求或者代码要求应当以较为严格的PHP版本为准; 除非必要,不要使用PHP扩展模块中的函数。使用时应当加入必要的判断,当服务器环境不支持此函数的时候,进行必要的处理。文档和程序中的功能说明中,也应加上兼容性说明。 4.8.代码重用 代码的有效重用可以减少效率的损失与资源的浪费。在开发软件项目时为了避免重复劳动和浪费时间。开发人员应尽量提高现有代码的重用率,同时将更多的精力用在新技术的应用和新功能的创新开发上面。 · 在需要多次使用代码,并且对于您希望实现的任务没有可用的内置 PHP 函数时,不吝啬定义函数或类。开发者须根据功能、调用情况,将函数放置于include目录并以.func.php作为函数文件后缀,将类放置于include/class目录。超过3行,实现相同功能的程序切勿在不同程序中多次出现,这是无法容忍和回避的问题; · 在任何时候都不要出现同一个程序中出现两段或更多的相似代码或相同代码,即便在不同程序中,也应尽力避免。开发者应当总是有能力找到避免代码大段(超过10行)重复或类似的情况。 需要强调的是,本部分虽然篇幅较短,但却是十分需要经验,并将花费开发者大量时间和精力去进行优化的部分,任何产品开发者必须时刻清楚和理解代码重用的重要性和必要性,切实在增强产品效率、逻辑性和可读性上下功夫,这是一名优秀软件开发者所必须具备的基本素质。 4.9.其他细节问题 4.9.1. 包含调用 包含调用程序文件,请全部使用require_once,以避免可能的重复包含问题; 包含调用缓存文件,由于缓存文件无法保证100%正确打开,请使用include_once或include。在必要时,可以使用@include_once或@include的方式,以忽略错误提示; 包含和调用代码中,须以PHPCMS_ROOT.’/’开头,应避免直接写程序文件名(例如:require_once ‘x.php’;)的做法; 所有被包含和调用的程序文件,包括但不限于程序、缓存或模板,通常其不能被直接URL请求。phpcms通过在./include/common.inc.php中定义一个标记性常量IN_PHPCMS,来判断程序是否被合法调用。因此,在除了./include/common.inc.php以外的任何一个被包含和调用的程序文件中,需要包含以下内容,以使得访问者无法直接通过URL请求该文件: 4.9.2. 错误报告级别 在软件开发和调试阶段,请使用error_reporting(E_ALL);作为默认的错误报告级别,此级别最为严格,能够报告程序中所有的错误、警告和提示信息,以帮助开发者检查和核对代码,避免大多数安全性问题和逻辑错误、拼写错误。error_reporting()可以在./include/common.inc.php的头几行进行设置。 在软件发布时,请使用error_reporting(E_ERROR | E_WARNING | E_PARSE);作为默认的错误报告级别,以利于用户使用并将无谓错误提示信息降至最低。 5.数据库设计 5.1.字段 5.1.1. 表和字段命名 表和字段的命名以前面《4.4命名原则》的约定为基本准则。 所有数据表名称,只要其名称是可数名词,则必须以复数方式命名,例如:phpcms_member(用户表);存储多项内容的字段,或代表数量的字段,也应当以复数方式命名,例如:hits(查看次数)、items(内容数量)。 当几个表间的字段有关连时,要注意表与表之间关联字段命名的统一,如phpcms_article_1表中的articleid与phpcms_article_data_1表中的articleid。 代表id自增量的字段,通常用以下几种形式: · 一般情况下,使用全称的形式,例如userid、articleid; · 没有功能性作用,只为管理和维护方便而设的id,可以使用全称的形式,也可只将其命名为id。 篇幅所限,无法一一赘述,但所有与表、字段相关的命名,请务必大量参考phpcms现有字段的命名方式,以保证命名的系统性和统一性。 5.1.2. 字段结构 允许NULL值的字段,数据库在进行比较操作时,会先判断其是否为NULL,非NULL时才进行值的必对。因此基于效率的考虑,所有字段均不能为空,即全部NOT NULL; 预计不会存储非负数的字段,例如各项id、发帖数等,必须设置为UNSIGNED类型。UNSIGNED类型比非UNSIGNED类型所能存储的正整数范围大一倍,因此能获得更大的数值存储空间; 存储开关、选项数据的字段,通常使用tinyint(1)非UNSIGNED类型,少数情况也可能使用enum()结果集的方式。tinyint作为开关字段时,通常1为打开;0为关闭;-1为特殊数据,例如N/A(不可用);高于1的为特殊结果或开关二进制数组合(详见phpcms中相关代码); MEMORY/HEAP类型的表中,要尤其注意规划节约使用存储空间,这将节约更多内存。例如cdb_sessions表中,就将IP地址的存储拆分为4个tinyint(3) UNSIGNED类型的字段,而没有采用char(15)的方式; 任何类型的数据表,字段空间应当本着足够用,不浪费的原则,数值类型的字段取值范围见下表: 5.2.SQL语句 所有SQL语句中,除了表名、字段名称以外,全部语句和函数均需大写,应当杜绝小写方式或大小写混杂的写法。例如select * from phpcms_member;是不符合规范的写法。 很长的SQL语句应当有适当的断行,依据JOIN、FROM、ORDER BY等关键字进行界定。 通常情况下,在对多表进行操作时,要根据不同表名称,对每个表指定一个1~2个字母的缩写,以利于语句简洁和可读性。 如下的语句范例,是符合规范的: 5.3.性能与效率 5.3.1. 定长与变长表 包含任何varchar、text等变长字段的数据表,即为变长表,反之则为定长表。 · 对于变长表,由于记录大小不同,在其上进行许多删除和更改将会使表中的碎片更多。需要定期运行OPTIMIZE TABLE以保持性能。而定长表就没有这个问题; · 如果表中有可变长的字段,将它们转换为定长字段能够改进性能,因为定长记录易于处理。但在试图这样做之前,应该考虑下列问题: · 使用定长列涉及某种折衷。它们更快,但占用的空间更多。char(n) 类型列的每个值总要占用n 个字节(即使空串也是如此),因为在表中存储时,值的长度不够将在右边补空格; · 而varchar(n)类型的列所占空间较少,因为只给它们分配存储每个值所需要的空间,每个值再加一个字节用于记录其长度。因此,如果在char和varchar类型之间进行选择,需要对时间与空间作出折衷; · 变长表到定长表的转换,不能只转换一个可变长字段,必须对它们全部进行转换。而且必须使用一个ALTER TABLE语句同时全部转换,否则转换将不起作用; · 有时不能使用定长类型,即使想这样做也不行。例如对于比255字符更长的串,没有定长类型; · 在设计表结构时如果能够使用定长数据类型尽量用定长的,因为定长表的查询、检索、更新速度都很快。必要时可以把部分关键的、承担频繁访问的表拆分,例如定长数据一个表,非定长数据一个表。例如phpcms的phpcms_member表等。因此规划数据结构时需要进行全局考虑; 进行表结构设计时,应当做到恰到好处,反复推敲,从而实现最优的数据存储体系。 5.3.2. 运算与检索 数值运算一般比字符串运算更快。例如比较运算,可在单一运算中对数进行比较。而串运算涉及几个逐字节的比较,如果串更长的话,这种比较还要多。 如果串列的值数目有限,应该利用普通整型或emum类型来获得数值运算的优越性。 更小的字段类型永远比更大的字段类型处理要快得多。对于字符串,其处理时间与串长度直接相关。一般情况下,较小的表处理更快。对于定长表,应该选择最小的类型,只要能存储所需范围的值即可。例如,如果mediumint够用,就不要选择bigint。对于可变长类型,也仍然能够节省空间。一个TEXT 类型的值用2 字节记录值的长度,而一个LONGTEXT 则用4字节记录其值的长度。如果存储的值长度永远不会超过64KB,使用TEXT 将使每个值节省2字节。 5.3.3. 结构优化与索引优化 索引能加快查询速度,而索引优化和查询优化是相辅相成的,既可以依据查询对索引进行优化,也可以依据现有索引对查询进行优化,这取决于修改查询或索引,哪个对现有产品架构和效率的影响最小。 索引优化与查询优化是多年经验积累的结晶,在此无法详述,但仍然给出几条最基本的准则。 首先,根据产品的实际运行和被访问情况,找出哪些SQL语句是最常被执行的。最常被执行和最常出现在程序中是完全不同的概念。最常被执行的SQL语句,又可被划分为对大表(数据条目多的)和对小表(数据条目少的)的操作。无论大表或小表,有可分为读(SELECT)多、写(UPDATE/INSERT)多或读写都多的操作。 对常被执行的SQL语句而言,对大表操作需要尤其注意: · 写操作多的,通常可使用写入缓存的方法,先将需要写或需要更新的数据缓存至文件或其他表,定期对大表进行批量写操作。同时,应尽量使得常被读写的大表为定长类型,即便原本的结构中大表并非定长。大表定长化,可以通过改变数据存储结构和数据读取方式,将一个大表拆成一个读写多的定长表,和一个读多写少的变长表来实现; · 读操作多的,需要依据SQL查询频率设置专门针对高频SQL语句的索引和联合索引。 而小表就相对简单,加入符合查询要求的特定索引,通常效果比较明显。同时,定长化小表也有益于效率和负载能力的提高。字段比较少的小定长表,甚至可以不需要索引。 其次,看SQL语句的条件和排序字段是否动态性很高(即根据不同功能开关或属性,SQL查询条件和排序字段的变化很大的情况),动态性过高的SQL语句是无法通过索引进行优化的。惟一的办法只有将数据缓存起来,定期更新,适用于结果对实效性要求不高的场合。 MySQL索引,常用的有PRIMARY KEY、INDEX、UNIQUE几种,详情请查阅MySQL文档。通常,在单表数据值不重复的情况下,PRIMARY KEY和UNIQUE索引比INDEX更快,请酌情使用。 事实上,索引是将条件查询、排序的读操作资源消耗,分布到了写操作中,索引越多,耗费磁盘空间越大,写操作越慢。因此,索引决不能盲目添加。对字段索引与否,最根本的出发点,依次仍然是SQL语句执行的概率、表的大小和写操作的频繁程度。 5.3.4. 查询优化 MySQL中并没有提供针对查询条件的优化功能,因此需要开发者在程序中对查询条件的先后顺序人工进行优化。例如如下的SQL语句: 事实上无论a>’0’还是b<’1’哪个条件在前,得到的结果都是一样的,但查询速度就大不相同,尤其在对大表进行操作时。 开发者需要牢记这个原则:最先出现的条件,一定是过滤和排除掉更多结果的条件;第二出现的次之;以此类推。因而,表中不同字段的值的分布,对查询速度有着很大影响。而ORDER BY中的条件,只与索引有关,与条件顺序无关。 除了条件顺序优化以外,针对固定或相对固定的SQL查询语句,还可以通过对索引结构进行优化,进而实现相当高的查询速度。原则是:在大多数情况下,根据WHERE条件的先后顺序和ORDER BY的排序字段的先后顺序而建立的联合索引,就是与这条SQL语句匹配的最优索引结构。尽管,事实的产品中不能只考虑一条SQL语句,也不能不考虑空间占用而建立太多的索引。 同样以上面的SQL语句为例,最优的当table表的记录达到百万甚至千万级后,可以明显的看到索引优化带来的速度提升。 依据上面条件优化和索引优化的两个原则,当table表的值为如下方案时,可以得出最优的条件顺序方案: EXPLAIN语句是检测索引和查询能否良好匹配的简便方法。在phpMyAdmin或其他MySQL客户端中运行EXPLAIN+查询语句,例如EXPLAIN select * FROM table WHERE a>’0’ AND b<’1’ ORDER BY c;这种形式,即使得开发者无需模拟上百万条数据,也可以验证索引是否合理,相关细节请参考MySQL说明。 值得提出的是,Using filesort是最不应当出现的情况,如果EXPLAIN得出此结果,说明数据库为这个查询专门建立了一个用以缓存结果的临时表文件,并在查询结束后删除。众所周知,硬盘I/O速度始终是计算机存储的瓶颈,因此,查询中应当尽全力避免高执行频率的SQL语句使用filesort。尽管,开发者永远都不可能保证产品中的全部SQL语句都不会使用filesort。 限于篇幅,本文档远远没有涵盖数据库优化的方方面面,例如:联合索引与普通索引的可重用性、JOIN连接的索引设计、MEMORY/HEAP表等。数据库优化实际上就是在很多因素和利弊间不断权衡、修改,惟有在成功与失败经验中反复推敲才能得出的经验,这种经验往往就是最难能可贵和价值连城的。 5.3.5. 兼容性问题 由于MySQL 3.23至5.0的变化很大,因此程序中尽量不使用特殊的SQL语句,以免带来兼容性问题,并给数据库移植造成困难。 通常在MySQL 4.1以上版本,phpcms应使用相当的字符集来存储,例如GBK/BIG5/UTF-8。传统的latin1编码虽然有一定的兼容性,但仍然不是推荐的选择。使用相应非默认字符集时,程序每次运行时需要使用SET NAMES ‘character_set’;来规定连接、传输和结果的字符集。 Mysql 5.0以上新增了数种SQL_MODE,默认的SQL_MODE依服务器安装设置不同而不同,因此程序每次运行时需要使用SET SQL_MODE=’’;来规定当前的SQL模式。 6. 模板设计 6.1.代码标记 HTML代码标记一律采用小写字母形式,杜绝任何使用大写字母的方式 模板中所有的逻辑体,如{if}、{loop}等,必须前后使用HTML注释(<!– –>),即类似<!–{if expr}–>的形式。事实上,phpcms模板编译器是支持不加HTML注释的逻辑体写法的,但加入注释可以使得模板可读性更好,同时方便用户使用DreamWeaver或FrontPage等对模板进行修改。 6.2.书写规则 6.2.1. HTML 所有HTML标记参数赋值需使用双引号包含,例如,应当使用<input type=”text” name=”test” value=”ok”>,而绝对不能使用<input value=ok>。 在任何情况下,产品中的模板文件必须采用手写HTML代码的方式,而绝对不能使用DreamWeaver、FrontPage等自动网页制作工具进行撰写或修改。 6.2.2. 变量 模板中使用的变量,依据作用和出现位置不同,分为几种方式: 逻辑体中,即被<!–{ }–>包围起来的部分,例如<!–{if isset($array[‘key’])}–>这种形式,其中的变量书写规范与PHP程序中完全一致; 开发者需要使用{}将变量括起来,以免出现模板编译错误,可能的情况如下: · 变量前后含有中括号的或其他敏感字符的(包括但不限于“$”、“’”等),正确的写法为descriptionnew[{$buddy[buddyid]}]; · 数组的下标为变量的,正确的写法为{$extcredits[$creditstrans][title]}; · 其他变量十分复杂的情况。 6.2.3. 语言元素 6.2.4. 缩进 在phpcms的*.html模板文件中,由于具备逻辑结构,故不考虑任何HTML本身的缩进,所有缩进均意为着逻辑上的缩进结构。缩进采用TAB方式,不使用空格作为缩进符号,仅需适当断行即可。例如 7.文件与目录 7.1.文件命名 所有包含PHP代码的程序文件或半程序文件,应以小写.php作为扩展名,而不要使用.phtml、.php3、.inc、.class等作为扩展名。 · 普通程序 能够被URL直接调用的程序,例如list.php、index.php,直接使用程序名+.php的方式命名 · 函数库和类库程序 分别以小写.func.php和.class.php作为扩展名。函数库和类库程序只能被其他程序引用,而不能独立运行。其中不能包含任何流程性的、不属于任何函数或类的程序代码。 · 流程性程序 以小写.inc.php作为扩展名。只能被其他程序引用,而不能独立运行。其中不能包含任何函数或类代码的程序代码。 · 模板源文件 以小写.html作为扩展名。模板源文件按照phpcms模板编码规则进行编写,不是可以执行的程序,而只能被phpcms模板编译器所解析,放置于./templates/default或./templates下的其他模板目录下。 · 模板目标文件 模板文件被编译后自动生成的目标程序,以小写. php作为扩展名,存放于./data/templates目录下。 · 语言包文件 以小写.lang.php作为扩展名,只能存放模板或程序使用的语言包信息。 · 缓存文件 此类文件为系统自动生成,以cache_xxx.php、usergroup_xxx.php、style_xxx.php等类似形式命名,存放于./data/cache目录下。 7.2.目录命名 phpcms目录命名以前面《4.4命名原则》的约定为基本准则。在可能的情况下,多以复数形式出现,如./templates、./images等。 由于目录数量较少,因此目录命名大多是一些习惯和约定俗成,开发人员如需新建目录,应与项目组成员进行磋商,达成一致后方可实施。 7.3.空目录索引 请在所有不包含普通程序(即能够被URL直接调用的程序)的目录中放置一个1字节的index.htm文件,内容为一个空格。几乎除phpcms根目录以外,所有目录都属于这一类型,因此开发者需要在这些目录全部放入空index.htm文件,以避免当http服务器的Directory Listing打开时,服务器文件被索引和列表。 附件目录等敏感目录,要在程序中实现相应功能,当新建下级目录时,必须自动写入一个空的index.htm文件,以避免新建目录被索引的问题。 |
2022-07-29
2022-08-17
2022-11-06
2022-07-30
2022-08-17
请发表评论