Discuz中的编码规范很值得PHP开发人员借鉴。里面既介绍了编码时代码标记,注释,书写规则,命名原则等方面基础的内容,对代码的安全性,性能,兼容性,代码重用,数据库设计,数据库性能及优化做了阐述,这个是开发人员必须掌握的技巧。同时也对模板的规范进行了介绍。php
前言
-
本规范由编程原则组成,融合并提炼了开发人员长时间积累下来的成熟经验,意在帮助造成良好一致的编程风格。
适用范围
-
如无特殊说明,如下规则要求彻底适用于Discuz!项目,同时也可大部分适用于COMSENZ旗下其余PHP项目。
标准化的重要性和好处
-
当一个软件项目尝试着遵照公共一致的标准时,可使参与项目的开发人员更容易了解项目中的代码、弄清程序的情况。使新的参与者能够很快的适应环境,防止部分参与者出于节省时间的须要,自创一套风格并养成终生的习惯,致使其它人在阅读时浪费过多的时间和精力。并且在一致的环境下,也能够减小编码出错的机会。缺陷是因为每一个人的标准不一样,因此须要一段时间来适应和改变本身的编码风格,暂时性的降底了工做效率。从使项目长远健康的发展以及后期更高的团队工做效率来考虑暂时的工做效率下降是值得的,也是必需要通过的一个过程。标准不是项目成功的关键,但能够帮助咱们在团队协做中有更高的效率而且更加顺利的完成既定的任务。
-
- 程序员能够了解任何代码,弄清程序的情况
- 新人能够很快的适应环境
- 防止新接触PHP的人出于节省时间的须要,自创一套风格并养成终生的习惯
- 防止新接触PHP的人一次次的犯一样的错误
- 在一致的环境下,人们能够减小犯错的机会
- 程序员们有了一致的敌人
PHP编码规范与原则
代码标记
-
PHP程序可使用
<?php ?>来界定 PHP 代码,在HTML页面中嵌入纯变量时,可使用
<?php echo $variablename;?>这样的形式。
-
注意:为了使代码进一步规范化和标准化,从Discuz! X2版本起开始禁用
<? ?>和
<?=$variablename?>这种速记形式。
注释
-
注释是对于那些容易忘记做用的代码添加简短的介绍性内容。请使用 C 样式的注释“/* */”和标准 C++ 注释“//”。
-
在Discuz!每个程序文件头部中,应放入相应SVN控制字串,以方便SVN提交时自动更新:
例如:程序员
/**
* [Discuz!] (C)2001-2099 Comsenz Inc.
* This is NOT a freeware, use is subject to license terms
*
* $Id$
*/
-
程序开发中不免留下一些临时代码和调试代码,此类代码必须添加注释,以避免往后遗忘。全部临时性、调试性、试验性的代码,必须添加统一的注释标记“//debug”并后跟完整的注释信息,这样能够方便在程序发布和最终调试前批量检查程序中是否还存在有疑问的代码。例如:
$num = 1;
$flag = TRUE; //debug 这里不能肯定是否须要对$flag进行赋值
if(empty($flag)) {
//Statements
}
书写规则
缩进
-
每一个缩进的单位约定是一个TAB(4个空白字符宽度),需每一个参与项目的开发人员在编辑器(UltraEdit、EditPlus、Zend Studio等)中进行强制设定,以防在编写代码时遗忘而形成格式上的不规范。
-
本缩进规范适用于PHP、JavaScript中的函数、类、逻辑结构、循环等。
大括号{}、if和switch
-
- 首括号与关键词同行,尾括号与关键字同列;
- if结构中,else和elseif与先后两个大括号同行,左右各一个空格。另外,即使if后只有一行语句,仍然须要加入大括号,以保证结构清晰;
- switch结构中,一般当一个case块处理后,将跳过以后的case块处理,所以大多数状况下须要添加break。break的位置视程序逻辑,与case同在一行,或新起一行都可,但同一switch体中,break的位置格式应当保持一致。
如下是符合上述规范的例子:正则表达式
if($condition) {
switch($var) {
case 1: echo ‘var is 1’; break;
case 2: echo ‘var is 2’; break;
default: echo ‘var is neither 1 or 2’; break;
}
} else {
switch($str) {
case ‘abc’:
$result = ‘abc’;
break;
default:
$result = ‘unknown’;
break;
}
}
运算符、小括号、空格、关键词和函数
-
- 每一个运算符与两边参与运算的值或表达式中间要有一个空格,惟一的特例是字符链接运算符号两边不加空格;
- 左括号“(” 应和函数关键词紧贴在一块儿,除此之外应当使用空格将“(”同前面内容分开;
- 右括号“)”除后面是“)”或者“.”之外,其余一概用空格隔开它们;
- 除字符串中特地须要,通常状况下,在程序以及HTML中不出现两个连续的空格;
- 任何状况下,PHP程序中不能出现空白的带有TAB或空格的行,即:这类空白行应当不包含任何TAB或空格。同时,任何程序行尾也不能出现多余的TAB或空格。多数编辑器具备自动去除行尾空格的功能,若是习惯养成很差,可临时使用它,避免多余空格产生;
- 每段较大的程序体,上、下应当加入空白行,两个程序块之间只使用1个空行,禁止使用多行。
- 程序块划分尽可能合理,过大或者太小的分割都会影响他人对代码的阅读和理解。通常能够以较大函数定义、逻辑结构、功能结构来进行划分。少于15行的程序块,可不加上下空白行;
- 说明或显示部分中,内容如含有中文、数字、英文单词混杂,应当在数字或者英文单词的先后加入空格。
-
根据上述原则,如下举例说明正确的书写格式:
$result = (($a + 1) * 3 / 2 + $num)).’Test’;
$condition ? func1($var) : func2($var);
$condition ? $long_statement : $another_long_statement;
if($flag) {
//Statements
//More than 15 lines
}
showmessage(‘请使用 restore.php 工具恢复数据。’);
函数定义
-
- 参数的名字和变量的命名规范一致;
- 函数定义中的左小括号,与函数名紧挨,中间无需空格;
- 开始的左大括号与函数定义为同一行,中间加一个空格,不要另起一行;
- 具备默认值的参数应该位于参数列表的后面;
- 函数调用与定义的时候参数与参数之间加入一个空格;
- 必须仔细检查并切实杜绝函数起始缩进位置与结束缩进位置不一样的现象;
-
例如,符合标准的定义:
function authcode($string, $operation, $key = '') {
if($flag) {
//Statement
}
//函数体
}
-
不符合标准的定义:
function authcode($string,$operation,$key = '')
{
//函数体
}
引号
-
PHP中单引号和双引号具备不一样的含义,最大的几项区别以下:
- 单引号中,任何变量($var)、特殊转义字符(如“\t \r \n”等)不会被解析,所以PHP的解析速度更快,转义字符仅仅支持“\’”和“\\”这样对单引号和反斜杠自己的转义;
- 双引号中,变量($var)值会代入字符串中,特殊转义字符也会被解析成特定的单个字符,还有一些专门针对上述两项特性的特殊功能性转义,例如“\$”和“{$array[‘key’]}。这样虽然程序编写更加方便,但同时PHP的解析也很慢;
- 数组中,若是下标不是整型,而是字符串类型,请务必用单引号将下标括起,正确的写法为$array[‘key’],而不是$array[key],由于不正确的写法会使PHP解析器认为key是一个常量,进而先判断常量是否存在,不存在时才以“key”做为下标带入表达式中,同时出发错误事件,产生一条Notice级错误。
- 所以,在绝大多数可使用单引号的场合,禁止使用双引号。依据上述分析,能够或必须使用单引号的状况包括但不限于下述:
- 字符串为固定值,不包含“\t”等特殊转义字符;
- 数组的固定下标,例如$array[‘key’];
- 表达式中不须要带入变量,例如$string = ‘test’;,而非$string = “test$var”;
- 例外的,在正则表达式(用于preg_系列函数和ereg系列函数)中,Discuz!所有使用双引号,这是为了人工分析和编写的方便,并保持正则表达式的统一,减小没必要要的分析混淆。
- 数据库SQL语句中,全部数据必须加单引号,不管数值仍是字串,以免可能的注入漏洞和SQL错误。正确的写法为:
UPDATE cdb_members SET adminid=’1’ WHERE username=’$admin’ AND adminid=’2’;
-
全部数据在插入数据库以前,均须要进行addslashes()处理,以避免特殊字符未经转义在插入数据库的时候出现错误。Discuz!中全部经过 GET, POST, FILE,取得的变量默认状况下已经使用了addslashes()进行了转义,没必要重复进行。若是数据处理必要(例如用于直接显示),可使用 stripslashes() 恢复,但数据在插入数据库以前必须再次进行转义。缓存文件中,通常对缓存数据的值采用 addcslashes($string, '\'\\')进行转义。
Discuz!语言问题
-
在功能设计阶段,当须要使用中文或者给出用户中文提示的时候能够直接在程序中插入简体中文文字,待程序整理和测试阶段由专人进行语言分离工做;
-
功能说明性语言、短语通常不使用“。”或者其余标点符号做为结束,文字中的标点符号,应使用全角。但注意:因为中英文模板与语言包问题,功能说明性的语言中,冒号使用半角”:”,而不使用全角;
-
提示信息(用showmessage()和cpmsg()显示的)和使用技巧(如后台管理界面中的tips)等大篇幅文字中,当中的标点符号应当遵循中文语法规则,以使用全角中文标点为主,结尾应当加入全角句号。
-
尽管程序语言包是在最后整理阶段才进行提取,但程序中直接写出的中文内容,也应充分保证书面语的特征:语言通顺、简洁、得体、无歧义。应完全杜绝认为直接写提示语言是临时性操做的想法,反复推敲,并总结以前提示语言的特征规范,加以应用。良好的语言文字表达能力,是每一个优秀程序员必须具有的基本素质之一。
命名原则
-
命名是程序规划的核心。古人相信只要知道一我的真正的名字就会得到凌驾于那我的之上的难以想象的力量。只要你给事物想到正确的名字,就会给你以及后来的人带来比代码更强的力量。
-
名字就是事物在它所处的生态环境中一个长久而深远的结果。总的来讲,只有了解系统的程序员才能为系统取出最合适的名字。若是全部的命名都与其天然相适合,则关系清晰,含义能够推导得出,通常人的推想也能在乎料之中。
-
就通常约定而言,类、函数和变量的名字应该老是可以描述让代码阅读者可以容易的知道这些代码的做用。形式越简单、越有规则,就越容易让人感知和理解。应该避免使用模棱两可,晦涩不标准的命名。
变量、对象、函数名
-
- 变量、对象、函数名一概为小写格式,除非必要,单词之间通常不使用下划线“_”进行分割;
- 以标准计算机英文为蓝本,杜绝一切拼音、或拼音英文混杂的命名方式;
- 变量命名只能使用项目中有据可查的英文缩写方式,例如可使用$data而不可以使用$data一、$data2这样容易产生混淆的形式,应当使用$threaddata、$postdata这样一目了然容易理解的形式;
- 能够合理的对过长的命名进行缩写,例如$bio($biography),$tpp($threadsPerPage),前提是英文中有这样既有的缩写形式,或字母符合英文缩写规范;
- 必须清楚所使用英文单词的词性,在权限相关的范围内,大多使用$allow***或$is***的形式,前者后面接动词,后者后面接形容词。
常量
-
- 常量应该老是所有使用大写字母命名,少数特别必要的状况下,可以使用划线来分隔单词;
- PHP 的内建值 TRUE、FALSE 和NULL必须所有采用大写字母书写。
变量的初始化与逻辑检查
任何变量在进行累加、直接显示或存储前必需进行初使化,例如:sql
$number = 0; //数值型初始化
$string = ‘’; //字符串初始化
$array = array(); //数组初始化
- 判断一个没法肯定(不知道是否已被赋值)的变量时,可用empty()或isset(),而不要直接使用if($switch)的形式,除非你确切的知道此变量必定已经被初始化并赋值。
-
empty()和isset()的区别为:
-
若是 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(),二者异同见上。
安全性
-
PHP中的变量不并不像C语言那样须要事先声明,解释器会在第一次使用时自动建立他们,一样类型也不须要指定,解释器会根据上下文环境自动肯定。从开发人员的角度来看,这无疑是一种极其方便的处理方法。一个变量被建立了,就能够在程序中的任何地方使用。这致使的结果就是开发人员工常常不注意初始化变量。所以,为了提升程序的安全性,咱们不能相信任何没有明肯定义的变量。全部的变量在定义使用前要初使化以防止恶意构造提交的变量覆盖程序中使用的变量。
兼容性
-
- 代码设计应当兼顾PHP 高低版本的特性,当前,应仍然以PHP 4.0.6做为最低经过平台,尽可能不使用高版本PHP 新增的函数、常数或者常量。若是使用只在高版本才具有的函数,必须对其进行二次封装,自动判断当前PHP版本,并自行编写低版本下的兼容代码;
- 对于个别函数,参数要求或者代码要求应当以较为严格的PHP版本为准;
- 除非必要,不要使用PHP扩展模块中的函数。使用时应当加入必要的判断,当服务器环境不支持此函数的时候,进行必要的处理。文档和程序中的功能说明中,也应加上兼容性说明。
代码重用
-
代码的有效重用能够减小效率的损失与资源的浪费。在开发软件项目时为了不重复劳动和浪费时间。开发人员应尽可能提升现有代码的重用率,同时将更多的精力用在新技术的应用和新功能的创新开发上面。
-
-
- 在须要屡次使用代码,而且对于您但愿实现的任务没有可用的内置 PHP 函数时,不吝啬定义函数或类。开发者须根据功能、调用状况,将函数和类放置于相应的function或class中。超过3行,实现相同功能的程序切勿在不一样程序中屡次出现,这是没法容忍和回避的问题;
- 在任什么时候候都不要出现同一个程序中出现两段或更多的类似代码或相同代码,即使在不一样程序中,也应尽力避免。开发者应当老是有能力找到避免代码大段(超过10行)重复或相似的状况。
-
须要强调的是,本部分虽然篇幅较短,但倒是十分须要经验,并将花费开发者大量时间和精力去进行优化的部分,任何产品开发者必须时刻清楚和理解代码重用的重要性和必要性,切实在加强产品效率、逻辑性和可读性上下功夫,这是一名优秀软件开发者所必须具有的基本素质。
其余细节问题
包含调用
-
- 包含调用程序文件,请所有使用require_once,以免可能的重复包含问题;
- 包含调用缓存文件,因为缓存文件没法保证100%正确打开,请使用include_once或include。在必要时,可使用@include_once或@include的方式,以忽略错误提示;
- 包含和调用代码中,须以“./”或DISCUZ_ROOT.’./’开头,应避免直接写程序文件名(例如:require_once ‘x.php’;)的作法;
- 全部被包含和调用的程序文件,包括但不限于程序、缓存或模板,一般其不能被直接URL请求。Discuz!经过在./source/class/class_core.php中定义一个标记性常量IN_DISCUZ,来判断程序是否被合法调用。所以,在除了./source/class/class_core.php之外的任何一个被包含和调用的程序文件中,须要包含如下内容,以使得访问者没法直接经过URL请求该文件:
if(!defined('IN_DISCUZ')) {
exit('Access Denied');
}
错误报告级别
-
- 在软件开发和调试阶段,请使用error_reporting(E_ALL);做为默认的错误报告级别,此级别最为严格,可以报告程序中全部的错误、警告和提示信息,以帮助开发者检查和核对代码,避免大多数安全性问题和逻辑错误、拼写错误。error_reporting()能够在config/config_global.php中添加一行$_config['debug'] = 1;debug值能够在0~2以前取值,数值越大报错等级越高。
- 在软件发布时,请使用error_reporting(E_ERROR | E_WARNING | E_PARSE);做为默认的错误报告级别,以利于用户使用并将无谓错误提示信息降至最低。
数据库设计
字段
表和字段命名
-
表和字段的命名之前面《4.4命名原则》的约定为基本准则。
-
全部数据表名称,只要其名称是可数名词,则必须以复数方式命名,例如:cdb_members(用户表)、cdb_posts(帖子表);存储多项内容的字段,或表明数量的字段,也应当以复数方式命名,例如:params(parameters,自定义Discuz!代码的参数个数)、views(查看次数)、replies(回复次数)。
-
当几个表间的字段有关连时,要注意表与表之间关联字段命名的统一,如cdb_threads表中的tid与cdb_posts表中的tid。
-
表明id自增量的字段,一般用如下几种形式:
- 最经常使用的核心id,或常常在URL中进行调用的,尽可能用简写的形式,例如tid、pid、uid;
- 有功能性做用,URL中偶尔用到的id,使用全称的形式,例如pluginid;
- 没有功能性做用,只为管理和维护方便而设的id,可使用全称的形式,也可只将其命名为id。
全部与表、字段相关的命名,请务必大量参考Discuz!现有字段的命名方式,以保证命名的系统性和统一性。数据库
字段结构
-
- 容许NULL值的字段,数据库在进行比较操做时,会先判断其是否为NULL,非NULL时才进行值的必对。所以基于效率的考虑,全部字段均不能为空,即所有NOT NULL;
- 预计不会存储非负数的字段,例如各项id、发帖数等,必须设置为UNSIGNED类型。UNSIGNED类型比非UNSIGNED类型所能存储的正整数范围大一倍,所以能得到更大的数值存储空间;
- 存储开关、选项数据的字段,一般使用tinyint(1)非UNSIGNED类型,少数状况也可能使用enum()结果集的方式。tinyint做为开关字段时,一般1为打开;0为关闭;-1为特殊数据,例如N/A(不可用);高于1的为特殊结果或开关二进制数组合(详见Discuz!中相关代码);
- MEMORY/HEAP类型的表中,要尤为注意规划节约使用存储空间,这将节约更多内存。例如cdb_sessions表中,就将IP地址的存储拆分为4个tinyint(3) UNSIGNED类型的字段,而没有采用char(15)的方式;
- 任何类型的数据表,字段空间应当本着足够用,不浪费的原则,数值类型的字段取值范围见下表:
字段类型 |
存储空间(b) |
UNSIGNED |
取值范围 |
tinyint |
1 |
否 |
-128~127 |
是 |
0~255 |
smallint |
2 |
否 |
-32768~32767 |
是 |
0~65535 |
mediumint |
3 |
否 |
-8388608~8388607 |
是 |
0~16777215 |
int |
4 |
否 |
-2147483648~2147483647 |
是 |
0~4294967295 |
bigint |
8 |
否 |
-9223372036854775808~9223372036854775807 |
是 |
0~18446744073709551615 |
SQL语句
-
- 全部SQL语句中,除了表名、字段名称之外,所有语句和函数均需大写,应当杜绝小写方式或大小写混杂的写法。例如select * from cdb_members;是不符合规范的写法。
- 很长的SQL语句应当有适当的断行,依据JOIN、FROM、ORDER BY等关键字进行界定。
- 一般状况下,在对多表进行操做时,要根据不一样表名称,对每一个表指定一个1~2个字母的缩写,以利于语句简洁和可读性。
-
以下的语句范例,是符合规范的:
$query = $db->query("SELECT s.*, m.* FROM {$tablepre}sessions s, {$tablepre}members m WHERE m.uid=s.uid AND s.sid='$sid');
性能与效率
定长与变长表
-
包含任何varchar、text等变长字段的数据表,即为变长表,反之则为定长表。
- 对于变长表,因为记录大小不一样,在其上进行许多删除和更改将会使表中的碎片更多。须要按期运行OPTIMIZE TABLE以保持性能。而定长表就没有这个问题;
- 若是表中有可变长的字段,将它们转换为定长字段可以改进性能,由于定长记录易于处理。但在试图这样作以前,应该考虑下列问题:
- 使用定长列涉及某种折衷。它们更快,但占用的空间更多。char(n) 类型列的每一个值总要占用n 个字节(即便空串也是如此),由于在表中存储时,值的长度不够将在右边补空格;
- 而varchar(n)类型的列所占空间较少,由于只给它们分配存储每一个值所须要的空间,每一个值再加一个字节用于记录其长度。所以,若是在char和varchar类型之间进行选择,须要对时间与空间做出折衷;
- 变长表到定长表的转换,不能只转换一个可变长字段,必须对它们所有进行转换。并且必须使用一个ALTER TABLE语句同时所有转换,不然转换将不起做用;
- 有时不能使用定长类型,即便想这样作也不行。例如对于比255字符更长的串,没有定长类型;
- 在设计表结构时若是可以使用定长数据类型尽可能用定长的,由于定长表的查询、检索、更新速度都很快。必要时能够把部分关键的、承担频繁访问的表拆分,例如定长数据一个表,非定长数据一个表。例如Discuz!的cdb_members和cdb_memberfields表、cdb_forums和cdb_forumfields表等。所以规划数据结构时须要进行全局考虑;
进行表结构设计时,应当作到恰到好处,反复推敲,从而实现最优的数据存储体系。编程
运算与检索
-
- 数值运算通常比字符串运算更快。例如比较运算,可在单一运算中对数进行比较。而串运算涉及几个逐字节的比较,若是串更长的话,这种比较还要多。
- 若是串列的值数目有限,应该利用普通整型或emum类型来得到数值运算的优越性。
- 更小的字段类型永远比更大的字段类型处理要快得多。对于字符串,其处理时间与串长度直接相关。通常状况下,较小的表处理更快。对于定长表,应该选择最小的类型,只要能存储所需范围的值便可。例如,若是mediumint够用,就不要选择bigint。对于可变长类型,也仍然可以节省空间。一个TEXT 类型的值用2 字节记录值的长度,而一个LONGTEXT 则用4字节记录其值的长度。若是存储的值长度永远不会超过64KB,使用TEXT 将使每一个值节省2字节。
结构优化与索引优化
-
索引能加快查询速度,而索引优化和查询优化是相辅相成的,既能够依据查询对索引进行优化,也能够依据现有索引对查询进行优化,这取决于修改查询或索引,哪一个对现有产品架构和效率的影响最小。
-
索引优化与查询优化是多年经验积累的结晶,在此没法详述,但仍然给出几条最基本的准则。
-
首先,根据产品的实际运行和被访问状况,找出哪些SQL语句是最常被执行的。最常被执行和最常出如今程序中是彻底不一样的概念。最常被执行的SQL语句,又可被划分为对大表(数据条目多的)和对小表(数据条目少的)的操做。不管大表或小表,有可分为读(SELECT)多、写(UPDATE/INSERT)多或读写都多的操做。
-
对常被执行的SQL语句而言,对大表操做须要尤为注意:
-
- 写操做多的,一般可以使用写入缓存的方法,先将须要写或须要更新的数据缓存至文件或其余表,按期对大表进行批量写操做,例如Discuz!中点击数延迟更新机制,就是依据此原理实现。同时,应尽可能使得常被读写的大表为定长类型,即使本来的结构中大表并不是定长。大表定长化,能够经过改变数据存储结构和数据读取方式,将一个大表拆成一个读写多的定长表,和一个读多写少的变长表来实现;
- 读操做多的,须要依据SQL查询频率设置专门针对高频SQL语句的索引和联合索引。
-
而小表就相对简单,加入符合查询要求的特定索引,一般效果比较明显。同时,定长化小表也有益于效率和负载能力的提升。字段比较少的小定长表,甚至能够不须要索引。
-
其次,看SQL语句的条件和排序字段是否动态性很高(即根据不一样功能开关或属性,SQL查询条件和排序字段的变化很大的状况),动态性太高的SQL语句是没法经过索引进行优化的。唯一的办法只有将数据缓存起来,按期更新,适用于结果对实效性要求不高的场合。
-
MySQL索引,经常使用的有PRIMARY KEY、INDEX、UNIQUE几种,详情请查阅MySQL文档。一般,在单表数据值不重复的状况下,PRIMARY KEY和UNIQUE索引比INDEX更快,请酌情使用。
-
事实上,索引是将条件查询、排序的读操做资源消耗,分布到了写操做中,索引越多,耗费磁盘空间越大,写操做越慢。所以,索引决不能盲目添加。对字段索引与否,最根本的出发点,依次仍然是SQL语句执行的几率、表的大小和写操做的频繁程度。
查询优化
-
MySQL中并无提供针对查询条件的优化功能,所以须要开发者在程序中对查询条件的前后顺序人工进行优化。例如以下的SQL语句:
SELECT * FROM table WHERE a>’0’ AND b<’1’ ORDER BY c LIMIT 10;
-
事实上不管a>’0’仍是b<’1’哪一个条件在前,获得的结果都是同样的,但查询速度就大不相同,尤为在对大表进行操做时。
-
开发者须要牢记这个原则:最早出现的条件,必定是过滤和排除掉更多结果的条件;第二出现的次之;以此类推。于是,表中不一样字段的值的分布,对查询速度有着很大影响。而ORDER BY中的条件,只与索引有关,与条件顺序无关。
-
除了条件顺序优化之外,针对固定或相对固定的SQL查询语句,还能够经过对索引结构进行优化,进而实现至关高的查询速度。原则是:在大多数状况下,根据WHERE条件的前后顺序和ORDER BY的排序字段的前后顺序而创建的联合索引,就是与这条SQL语句匹配的最优索引结构。尽管,事实的产品中不能只考虑一条SQL语句,也不能不考虑空间占用而创建太多的索引。
-
一样以上面的SQL语句为例,最优的当table表的记录达到百万甚至千万级后,能够明显的看到索引优化带来的速度提高。
-
依据上面条件优化和索引优化的两个原则,当table表的值为以下方案时,能够得出最优的条件顺序方案:
字段a |
字段b |
字段c |
1 |
7 |
11 |
2 |
8 |
10 |
3 |
9 |
13 |
最优条件:b<’1’ AND a>’0’ 最优索引:INDEX abc (b, a, c) 缘由:b<’1’做为第一条件能够先过滤掉75%的结果。若是以a>’0’做为第一条件,则只能先过滤掉25%的结果 注意:数组
-
- 字段c因为未出现于条件中,故条件顺序优化与其无关
- 最优索引由最优条件顺序得来,而非由例子中的SQL语句得来
- 索引并不是修改数据存储的物理顺序,而是经过对应特定偏移量的物理数据而实现的虚拟指针
|
-
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表等。数据库优化实际上就是在不少因素和利弊间不断权衡、修改,唯有在成功与失败经验中反复推敲才能得出的经验,这种经验每每就是最难能难得和价值连城的。
兼容性问题
-
- 因为MySQL 3.23至5.0的变化很大,所以程序中尽可能不使用特殊的SQL语句,以避免带来兼容性问题,并给数据库移植形成困难。
- 一般在MySQL 4.1以上版本,Discuz!应使用至关的字符集来存储,例如GBK/BIG5/UTF-8。传统的latin1编码虽然有必定的兼容性,但仍然不是推荐的选择。使用相应非默认字符集时,程序每次运行时须要使用SET NAMES ‘character_set’;来规定链接、传输和结果的字符集。
- Mysql 5.0以上新增了数种SQL_MODE,默认的SQL_MODE依服务器安装设置不一样而不一样,所以程序每次运行时须要使用SET SQL_MODE=’’;来规定当前的SQL模式。
模板设计
代码标记
-
HTML代码标记一概采用小写字母形式,杜绝任何使用大写字母的方式
-
模板中全部的逻辑体,如{if}、{loop}等,必须先后使用HTML注释(),即相似的形式。事实上,Discuz!模板编译器是支持不加HTML注释的逻辑体写法的,但加入注释可使得模板可读性更好,同时方便用户使用DreamWeaver或FrontPage等对模板进行修改。
-
在HTML标记中使用的逻辑体无需使用HTML注释(),即<input type=”text”{if xxx} value=”1”{/if} />
书写规则
HTML
-
全部HTML标记参数赋值需使用双引号包含,例如,应当使用<input type=”text” name=”test” value=”ok” />,而绝对不能使用<input type=text name=test value=ok />。
-
在任何状况下,产品中的模板文件必须采用手写HTML代码的方式,而绝对不能使用DreamWeaver、FrontPage等自动网页制做工具进行撰写或修改。
-
非成对标记必须以“/>”结尾,如
、<input …/>,<input /> 标记的属性必须按照如下顺序书写:<input type="" name="" id="" class="" … />
变量
-
模板中使用的变量,依据做用和出现位置不一样,分为几种方式:
- 逻辑体中,即被包围起来的部分,例如这种形式,其中的变量书写规范与PHP程序中彻底一致;
- 模板内容中,即未被包围起来的部分,与PHP程序中最大的不一样是,对数组字符串类型下标无需加入单引号,由于Discuz!模板编译器会在对模板进行编译时将这部分的单引号自动加入。
-
偶尔的,开发者须要使用{}将变量括起来,以避免出现模板编译错误,可能的状况以下:
- 变量先后含有中括号的或其余敏感字符的(包括但不限于“$”、“’”等),正确的写法为descriptionnew[{$buddy[buddyid]}];
- 数组的下标为变量的,正确的写法为{$extcredits[$creditstrans][title]};
- 其余变量十分复杂的状况。
语言元素
-
Discuz!模板编译器能够解析对应于./templates/default/templates.lang.php(以默认模板为例)的语言元素。在模板中调用相应语言元素的代码为{lang item},其中item为在templates.lang.php中定义的语言元素名。
缩进
-
在Discuz!的*.htm模板文件中,因为具有逻辑结构,故不考虑任何HTML自己的缩进,全部缩进均意为着逻辑上的缩进结构。缩进采用TAB方式,不使用空格做为缩进符号,仅需适当断行便可。例如:
<!--{loop $threadlist $thread}-->
<table cellspacing=”0” cellspadding=”0” border=”0”>
<tr><td>$thread[message]</tr></td>
</table>
<!--{/loop}-->
文件与目录
文件命名
-
全部包含PHP代码的程序文件或半程序文件,应以小写.php做为扩展名,而不要使用.phtml、.php三、.inc、.class等做为扩展名。
-
普通程序
-
可以被URL直接调用的程序,例如home.php、index.php、forum.php,直接使用程序名+.php的方式命名
-
函数库和类库程序
-
分别以小写function_xxxx.php和class_xxxx.php的格式命名书写。函数库和类库程序只能被其余程序引用,而不能独立运行。其中不能包含任何流程性的、不属于任何函数或类的程序代码。
-
流程性程序
-
以小写.inc.php做为扩展名。只能被其余程序引用,而不能独立运行。其中不能包含任何函数或类代码的程序代码。
-
模板源文件
-
以小写.htm做为扩展名。模板源文件按照Discuz!模板编码规则进行编写,不是能够执行的程序,而只能被Discuz!模板编译器所解析,放置于./templates/default或./templates下的其余模板目录下。
-
模板目标文件
-
模板文件被编译后自动生成的目标程序,以小写.tpl.php做为扩展名,存放于./data/template目录下。
-
语言包文件
-
- 以小写lang_xxxx.php格式命名,只能存放模板或程序使用的语言包信息。
- 模板语言包存放在对应模块名下的lang_template.php,例如:家园的模板语言包source\language\home\lang_template.php
-
缓存文件
-
此类文件为系统自动生成,以cache_xxx.php、usergroup_xxx.php、style_xxx.php等相似形式命名,存放于./data/cache目录下。
目录命名
-
- Discuz!目录命名之前面《4.4命名原则》的约定为基本准则。在可能的状况下,多以复数形式出现,如./templates、./images等。
- 因为目录数量较少,所以目录命名大可能是一些习惯和约定俗成,开发人员如需新建目录,应与项目组成员进行磋商,达成一致后方可实施。
空目录索引
-
- 请在全部不包含普通程序(即可以被URL直接调用的程序)的目录中放置一个1字节的index.htm文件,内容为一个空格。几乎除Discuz!根目录之外,全部目录都属于这一类型,所以开发者须要在这些目录所有放入空index.htm文件,以免当http服务器的Directory Listing打开时,服务器文件被索引和列表。
- 附件目录等敏感目录,要在程序中实现相应功能,当新建下级目录时,必须自动写入一个空的index.htm文件,以免新建目录被索引的问题。
更新时间:2012-5-3缓存