欢迎光临杨雨的个人博客站!

杨雨个人网站-杨雨个人博客-杨照佳

杨雨个人博客网站

关注互联网和搜索引擎的个人博客网站

首页 > WEB开发 > PHP >

进步php代码质量36计

发布时间:2016-11-24  编辑:杨雨个人博客网站   点击:   

  进步PHP代码质量的36个能力

前进php代码质量36计

1.不要行使相对路径

经常会看到:

require_once('../../lib/some_class.php');

该要领有许多弱点:

它起首查找指定的php包括路径, 然后查找当前目次.

因此会搜查过多路径.

假如该剧本被另一目次的剧本包括, 它的根基目次酿成了另一剧内地址的目次.

另一题目, 当按时使命运行该剧本, 它的上级目次也许就不是事变目次了.

因此最佳选择是行使绝对路径:

define('ROOT' , '/var/www/project/');
require_once(ROOT . '../../lib/some_class.php');

//rest of the code

 我们界说了一个绝对路径, 值被写死了. 我们还可以改造它. 路径 /var/www/project 也也许会改变, 那么我们每次都要改变它吗? 不是的, 我们可以行使__FILE__常量, 如:

//suppose your script is /var/www/project/index.php
//Then __FILE__ will always have that full path.

define('ROOT' , pathinfo(__FILE__, PATHINFO_DIRNAME));
require_once(ROOT . '../../lib/some_class.php');

//rest of the code

此刻, 无论你移到哪个目次, 如移到一个外网的处事器上, 代码无须变动便可正确运行.

2. 不要直接行使 require, include, include_once, required_once

可以在剧本头部引入多个文件, 像类库, 器材文件和助手函数等, 如:

require_once('lib/Database.php');
require_once('lib/Mail.php');

require_once('helpers/utitlity_functions.php');

这种用法相等原始. 应该更机动点. 应编写个助手函数包括文件. 譬喻:

function load_class($class_name)
{
    //path to the class file
    $path = ROOT . '/lib/' . $class_name . '.php');
    require_once( $path );
}

load_class('Database');
load_class('Mail');

有什么纷歧样吗? 该代码更具可读性.

將来你可以按需扩展该函数, 如:

function load_class($class_name)
{
    //path to the class file
    $path = ROOT . '/lib/' . $class_name . '.php');

    if(file_exists($path))
    {
        require_once( $path );
    }
}

还可做得更多:

为同样文件查找多个目次

能很轻易的改变安排类文件的目次, 无须在代码遍地逐一修改

可行使相同的函数加载文件, 如Html内容.

3. 为应用保存调试代码

在开拓情形中, 我们打印数据库查询语句, 转存有题目的变量值, 而一旦题目办理, 我们注释或删除它们. 然而更好的做法是保存调试代码.

在开拓情形中, 你可以:

define('ENVIRONMENT' , 'development');

if(! $db->query( $query )
{
    if(ENVIRONMENT == 'development')
    {
        echo "$query failed";
    }
    else
    {
        echo "Database error. Please contact administrator";
    }
}

在处事器中, 你可以:

define('ENVIRONMENT' , 'production');

if(! $db->query( $query )
{
    if(ENVIRONMENT == 'development')
    {
        echo "$query failed";
    }
    else
    {
        echo "Database error. Please contact administrator";
    }
}

4. 行使可跨平台的函数执行呼吁

system, exec, passthru, shell_exec 这4个函数可用于执行体系呼吁. 每个的举动都有渺小不同. 题目在于, 当在共享主机中, 某些函数也许被选择性的禁用. 大大都新手趋于每次起首搜查哪个函数可用, 然而再行使它.

更好的方案是封成函数一个可跨平台的函数. 

/**
	Method to execute a command in the terminal
	Uses :

	1. system
	2. passthru
	3. exec
	4. shell_exec

*/
function terminal($command)
{
	//system
	if(function_exists('system'))
	{
		ob_start();
		system($command , $return_var);
		$output = ob_get_contents();
		ob_end_clean();
	}
	//passthru
	else if(function_exists('passthru'))
	{
		ob_start();
		passthru($command , $return_var);
		$output = ob_get_contents();
		ob_end_clean();
	}

	//exec
	else if(function_exists('exec'))
	{
		exec($command , $output , $return_var);
		$output = implode("\n" , $output);
	}

	//shell_exec
	else if(function_exists('shell_exec'))
	{
		$output = shell_exec($command) ;
	}

	else
	{
		$output = 'Command execution not possible on this system';
		$return_var = 1;
	}

	return array('output' => $output , 'status' => $return_var);
}

terminal('ls');

上面的函数將运行shell呼吁, 只要有一个体系函数可用, 这保持了代码的同等性. 

5. 机动编写函数

function add_to_cart($item_id , $qty)
{
    $_SESSION['cart']['item_id'] = $qty;
}

add_to_cart( 'IPHONE3' , 2 );

行使上面的函数添加单个项目. 而当添加项列表的时辰,你要建设另一个函数吗? 不消, 只要稍加寄望差异范例的参数, 就会更机动. 如:

function add_to_cart($item_id , $qty)
{
    if(!is_array($item_id))
    {
        $_SESSION['cart']['item_id'] = $qty;
    }

    else
    {
        foreach($item_id as $i_id => $qty)
        {
            $_SESSION['cart']['i_id'] = $qty;
        }
    }
}

add_to_cart( 'IPHONE3' , 2 );
add_to_cart( array('IPHONE3' => 2 , 'IPAD' => 5) );

此刻, 同个函数可以处理赏罚差异范例的输入参数了. 可以参照上面的例子重构你的多处代码, 使其更智能.

6. 故意忽略php封锁标签

我很想知道为什么这么多关于php提议的博客文章都没提到这点.

<?php

echo "Hello";

//Now dont close this tag

这將节省你许多时刻. 我们举个例子:

一个 super_class.php 文件

<?php
class super_class
{
    function super_function()
    {
        //super code
    }
}
?>
//super extra character after the closing tag

index.php

require_once('super_class.php');

//echo an image or pdf , or set the cookies or session data

这样, 你將会获得一个 Headers already send error. 为什么? 由于 “super extra character” 已经被输出了. 此刻你得开始调试啦. 这会耗费大量时刻探求 super extra 的位置.

因此, 养成省略封锁符的风俗:

<?php
class super_class
{
    function super_function()
    {
        //super code
    }
}

//No closing tag

这会更好. 

7. 在某处所网络全部输入, 一次输出给赏识器

这称为输出缓冲, 若是说你已在差异的函数输出内容:

function print_header()
{
    echo "<div id='header'>Site Log and Login links</div>";
}

function print_footer()
{
    echo "<div id='footer'>Site was made by me</div>";
}

print_header();
for($i = 0 ; $i < 100; $i++)
{
    echo "I is : $i <br />';
}
print_footer();

更换方案, 在某处所齐集网络输出. 你可以存储在函数的局部变量中, 也可以行使ob_start和ob_end_clean. 如下:

function print_header()
{
    $o = "<div id='header'>Site Log and Login links</div>";
    return $o;
}

function print_footer()
{
    $o = "<div id='footer'>Site was made by me</div>";
    return $o;
}

echo print_header();
for($i = 0 ; $i < 100; $i++)
{
    echo "I is : $i <br />';
}
echo print_footer();

为什么必要输出缓冲:

>>可以在发送给赏识器前变动输出. 如 str_replaces 函数或也许是 preg_replaces 或添加些监控/调试的Html内容.

>>输出给赏识器的同时又做php的处理赏罚很糟糕. 你应该看到过有些站点的侧边栏或中间呈现错误信息. 知道为什么会产生吗? 由于处理赏罚和输出殽杂了.

8. 发送正确的mime范例头信息, 假如输出非Html内容的话.

输出一些xml.

$xml = '<?xml version="1.0" encoding="utf-8" standalone="yes"?>';
$xml = "<response>
  <code>0</code>
</response>";

//Send xml data
echo $xml;

事变得不错. 但必要一些改造.

$xml = '<?xml version="1.0" encoding="utf-8" standalone="yes"?>';
$xml = "<response>
  <code>0</code>
</response>";

//Send xml data
header("content-type: text/xml");
echo $xml;

留意header行. 该行奉告赏识器发送的是xml范例的内容. 以是赏识器能正确的处理赏罚. 许多的javascript库也依靠头信息.

相同的有 javascript , Css, jpg image, png image:

JavaScript

header("content-type: application/x-javascript");
echo "var a = 10";

CSS

header("content-type: text/Css");
echo "#div id { background:#000; }";

9. 为mysql毗连配置正确的字符编码

曾经碰着过在mysql表中配置了unicode/utf-8编码,  phpadmin也能正确表现, 但当你获取内容并在页面输出的时辰,会呈现乱码. 这里的题目出在mysql毗连的字符编码.

//Attempt to connect to database
$c = mysqli_connect($this->host , $this->username, $this->password);

//Check connection validity
if (!$c)&nbsp;
{
	die ("Could not connect to the database host: <br />". mysqli_connect_error());
}

//Set the character set of the connection
if(!mysqli_set_charset ( $c , 'UTF8' ))
{
	die('mysqli_set_charset() failed');
}

一旦毗连数据库, 最好配置毗连的 characterset. 你的应用假如要支持多说话, 这么做是必需的.

10. 行使 Htmlentities 配置正确的编码选项

php5.4前, 字符的默认编码是ISO-8859-1, 不能直接输出如À â等.

$value = Htmlentities($this->value , ENT_QUOTES , CHARSET);

php5.4往后, 默认编码为UTF-8, 这將办理许多题目. 但假如你的应用是多说话的, 如故要寄望编码题目,.

11. 不要在应用中行使gzip压缩输出, 让apache处理赏罚

思量过行使 ob_gzhandler 吗? 不要那样做. 毫有时义. php只应用来编写应用. 不该劳神处事器和赏识器的数据传输优化题目.

行使apache的mod_gzip/mod_deflate 模块压缩内容.

12. 行使json_encode输出动态javascript内容

时常会用php输出动态javascript内容:

$images = array(
 'myself.png' , 'friends.png' , 'colleagues.png'
);

$js_code = '';

foreach($images as $image)
{
$js_code .= "'$image' ,";
}

$js_code = 'var images = [' . $js_code . ']; ';

echo $js_code;

//Output is var images = ['myself.png' ,'friends.png' ,'colleagues.png' ,];

更智慧的做法, 行使 json_encode:

$images = array(
 'myself.png' , 'friends.png' , 'colleagues.png'
);

$js_code = 'var images = ' . json_encode($images);

echo $js_code;

//Output is : var images = ["myself.png","friends.png","colleagues.png"]

优雅乎?

13. 写文件前, 搜查目次写权限

写或生涯文件前, 确保目次是可写的, 若是不行写, 输堕落误信息. 这会节省你许多调试时刻. linux体系中, 必要处理赏罚权限, 目次权限不妥会导致许多许多的题目, 文件也有也许无法读取等等.

确保你的应用足够智能, 输出某些重要信息.

$contents = "All the content";
$file_path = "/var/www/project/content.txt";

file_put_contents($file_path , $contents);

这概略上正确. 但有些间接的题目. file_put_contents 也许会因为几个缘故起因失败:

>>父目次不存在

>>目次存在, 但不行写

>>文件被写锁住?

以是写文件前做明晰的搜查更好.

$contents = "All the content";
$dir = '/var/www/project';
$file_path = $dir . "/content.txt";

if(is_writable($dir))
{
    file_put_contents($file_path , $contents);
}
else
{
    die("Directory $dir is not writable, or does not exist. Please check");
}

这么做后, 你会获得一个文件在那里写及为什么失败的明晰信息.

14. 变动应用建设的文件权限

在linux情形中, 权限题目也许会挥霍你许多时刻. 从今今后, 无论何时, 当你建设一些文件后, 确保行使chmod配置正确权限. 不然的话, 也许文件先是由"php"用户建设, 但你用其余的用户登录事变, 体系將会拒绝会见或打开文件, 你不得不奋力获取root权限,  变动文件的权限等等.

// Read and write for owner, read for everybody else
chmod("/somedir/somefile", 0644);

// Everything for owner, read and execute for others
chmod("/somedir/somefile", 0755);

15. 不要依靠submit按钮值来搜查表单提交举动

if($_POST['submit'] == 'Save')
{
    //Save the things
}

上面大大都环境正确, 除了应用是多说话的. 'Save' 也许代表其余寄义. 你怎么区分它们呢. 因此, 不要依靠于submit按钮的值.

if( $_SERVER['REQUEST_METHOD'] == 'POST' and isset($_POST['submit']) )
{
    //Save the things
}

此刻你从submit按钮值中脱节出来了.

16. 为函数内总具有沟通值的变量界说成静态变量

//Delay for some time
function delay()
{
    $sync_delay = get_option('sync_delay');

    echo "<br />Delaying for $sync_delay seconds...";
    sleep($sync_delay);
    echo "Done <br />";
}

用静态变量代替:

//Delay for some time
function delay()
{
    static $sync_delay = null;

    if($sync_delay == null)
    {
	$sync_delay = get_option('sync_delay');
    }

    echo "<br />Delaying for $sync_delay seconds...";
    sleep($sync_delay);
    echo "Done <br />";
}

17. 不要直接行使 $_SESSION 变量

某些简朴例子:

$_SESSION['username'] = $username;
$username = $_SESSION['username'];

这会导致某些题目. 假如在同个域名中运行了多个应用, session 变量也许会斗嘴. 两个差异的应用也许行使统一个session key. 譬喻, 一个前端派别, 和一个靠山打点体系行使统一域名.

以后刻开始, 行使应用相干的key和一个包装函数:

define('APP_ID' , 'abc_corp_ecommerce');

//Function to get a session variable
function session_get($key)
{
    $k = APP_ID . '.' . $key;

    if(isset($_SESSION[$k]))
    {
        return $_SESSION[$k];
    }

    return false;
}

//Function set the session variable
function session_set($key , $value)
{
    $k = APP_ID . '.' . $key;
    $_SESSION[$k] = $value;

    return true;
}

18. 將器材函数封装到类中

若是你在某文件中界说了许多器材函数:

function utility_a()
{
    //This function does a utility thing like string processing
}

function utility_b()
{
    //This function does nother utility thing like database processing
}

function utility_c()
{
    //This function is ...
}

这些函数的行使分手到应用遍地. 你也许想將他们封装到某个类中:

class Utility
{
    public static function utility_a()
    {

    }

    public static function utility_b()
    {

    }

    public static function utility_c()
    {

    }
}

//and call them as 

$a = Utility::utility_a();
$b = Utility::utility_b();

显而易见的甜头是, 假如php内建有同名的函数, 这样可以停止斗嘴.

另一种观点是, 你可以在同个应用中为同个类维护多个版本, 而不导致斗嘴. 这是封装的根基甜头, 无它.

19. Bunch of silly tips 

>>行使echo代替print

>>行使str_replace代替preg_replace, 除非你绝对必要

>>不要行使 short tag

>>简朴字符串用单引号代替双引号

>>head重定向跋文得行使exit

>>不要在轮回中挪用函数

>>isset比strlen快

>>始中如一的名目化代码

>>不要删除轮回可能if-else的括号

不要这样写代码:

if($a == true) $a_count++;

这绝对WASTE.

写成:

if($a == true)
{
    $a_count++;
}

不要实行省略一些语法来收缩代码. 而是让你的逻辑简短.

>>行使有高亮语法表现的文本编辑器. 高亮语法能让你镌汰错误.

20. 行使array_map快速处理赏罚数组

好比说你想 trim 数组中的全部元素. 新手也许会:

foreach($arr as $c => $v)
{
	$arr[$c] = trim($v);
}

但行使 array_map 更简朴:

$arr = array_map('trim' , $arr);

这会为$arr数组的每个元素都申请挪用trim. 另一个相同的函数是 array_walk. 请查阅文档进修更多能力.

21. 行使 php filter 验证数据

你必定曾行使过正则表达式验证 email , ip地点等. 是的,每小我私人都这么行使. 此刻, 我们想做差异的实行, 称为filter.

php的filter扩展提供了简朴的方法验证和搜查输入.

22. 逼迫范例搜查

$amount = intval( $_GET['amount'] );
$rate = (int) $_GET['rate'];

这是个好风俗.

23. 假如必要,行使profiler如xdebug

假如你行使php开拓大型的应用, php包袱了许多运算量, 速率会是一个很重要的指标. 行使profile辅佐优化代码. 可行使

xdebug和webgrid.

24. 警惕处理赏罚大数组

对付大的数组和字符串, 必需警惕处理赏罚. 常见错误是产生数组拷贝导致内存溢出,抛出Fatal Error of Memory size 信息:

$db_records_in_array_format; //This is a big array holding 1000 rows from a table each having 20 columns , every row is atleast 100 bytes , so total 1000 * 20 * 100 = 2MB

$cc = $db_records_in_array_format; //2MB more

some_function($cc); //Another 2MB ?

当导入或导出csv文件时, 经常会这么做.

不要以为上面的代码会常常因内存限定导致剧本瓦解. 对付小的变量是没题目的, 但处理赏罚大数组的时辰就必需停止.

确保通过引用转达, 或存储在类变量中:

$a = get_large_array();
pass_to_function(&$a);

这么做后, 向函数转达变量引用(而不是拷贝数组). 查察文档.

class A
{
    function first()
    {
        $this->a = get_large_array();
        $this->pass_to_function();
    }

    function pass_to_function()
    {
        //process $this->a
    }
}

尽快的 unset 它们, 让内存得以开释,减轻剧本承担.

25.  由始至终行使单一数据库毗连

确保你的剧本由始至终都行使单一的数据库毗连. 在开始处正确的打开毗连, 行使它直到竣事, 最后封锁它. 不要像下面这样在函数中打开毗连:

function add_to_cart()
{
    $db = new Database();
    $db->query("INSERT INTO cart .....");
}

function empty_cart()
{
    $db = new Database();
    $db->query("DELETE FROM cart .....");
}

行使多个毗连是个糟糕的, 它们会拖慢应用, 由于建设毗连必要时刻和占用内存.

特定环境行使单例模式, 如数据库毗连.

26. 停止直接写SQL, 抽象之

不厌其烦的写了太多如下的语句:

$query = "INSERT INTO users(name , email , address , phone) VALUES('$name' , '$email' , '$address' , '$phone')";
$db->query($query); //call to mysqli_query()

这不是个建壮的方案. 它有些弱点:

>>每次都手动转义值

>>验证查询是否正确

>>查询的错误会花很长时刻辨认(除非每次都用if-else搜查)

>>很难维护伟大的查询

因此行使函数封装:

function insert_record($table_name , $data)
{
    foreach($data as $key => $value)
    {
	//mysqli_real_escape_string
        $data[$key] = $db->mres($value);
    }

    $fields = implode(',' , array_keys($data));
    $values = "'" . implode("','" , array_values($data)) . "'";

    //Final query
    $query = "INSERT INTO {$table}($fields) VALUES($values)";

    return $db->query($query);
}

$data = array('name' => $name , 'email' => $email  , 'address' => $address , 'phone' => $phone);

insert_record('users' , $data);

看到了吗? 这样会更易读和扩展. record_data 函数警惕的处理赏罚了转义. 

最大的利益是数据被预处理赏罚为一个数组, 任何语法错误城市被捕捉.

该函数应该界说在某个database类中, 你可以像 $db->insert_record这样挪用.

查察本文, 看看奈何让你处理赏罚数据库更轻易.

相同的也可以编写update,select,delete要领. 试试吧.

27. 將数据库天生的内容缓存到静态文件中

假如全部的内容都是从数据库获取的, 它们应该被缓存. 一旦天生了, 就將它们生涯在姑且文件中. 下次哀求该页面时, 可直接从缓存中取, 不消再查数据库.

甜头:

>>节省php处理赏罚页面的时刻, 执行更快

>>更少的数据库查询意味着更少的mysql毗连开销

28. 在数据库中生涯session

基于文件的session计策会有许多限定. 行使基于文件的session不能扩展到集群中, 由于session生涯在单个处事器中. 但数据库可被多个处事器会见, 这样就可以办理题目.

在数据库中生涯session数据, 尚有更多甜头:

>>处理赏罚username一再登录题目. 同个username不能在两个处所同时登录.

>>能更筹备的查询在线用户状态.

29. 停止行使全局变量

>>行使 defines/constants

>>行使函数获取值

>>行使类并通过$this会见

30. 在head中行使base标签

没传闻过? 请看下面:

<head>
<base href="http://www.domain.com/store/">
</head>
<body>
<img src="happy.jpg" />
</body>
</Html>

base 标签很是有效. 假设你的应用分成几个子目次, 它们都要包罗沟通的导航菜单.

www.domain.com/store/home.php

www.domain.com/store/products/ipad.php

在首页中, 可以写:

<a href="home.php">Home</a>
<a href="products/ipad.php">Ipad</a>

但在你的ipad.php不得不写成:

<a href="../home.php">Home</a>
<a href="ipad.php">Ipad</a>

由于目次纷歧样. 有这么多差异版本的导航菜单要维护, 很糟糕啊. 

因此, 请行使base标签.

<head>
<base href="http://www.domain.com/store/">
</head>
<body>
<a href="home.php">Home</a>
<a href="products/ipad.php">Ipad</a>
</body>
</Html>

此刻, 这段代码放在应用的各个目次文件中举动都同等. 

31. 永久不要將 error_reporting 设为 0

封锁不相的错误陈诉. E_FATAL 错误是很重要的. 

ini_set('display_errors', 1);
error_reporting(~E_WARNING & ~E_NOTICE & ~E_STRICT);

32. 留意平台系统布局

integer在32位和64位系统布局中长度是差异的. 因此某些函数如 strtotime 的举动会差异.

在64位的呆板中, 你会看到如下的输出.

$ php -a
Interactive shell

php > echo strtotime("0000-00-00 00:00:00");
-62170005200
php > echo strtotime('1000-01-30');
-30607739600
php > echo strtotime('2100-01-30');
4104930600

但在32位呆板中, 它们將是bool(false). 查察这里, 相识更多.

33. 不要过度依靠 set_time_limit

假如你想限定最小时刻, 可以行使下面的剧本:

set_time_limit(30);

//Rest of the code

安枕无忧吗?  留意任何外部的执行, 如体系挪用,socket操纵, 数据库操纵等, 就不在set_time_limits的节制之下.

 因此, 就算数据库耗费了许多时刻查询, 剧本也不会遏制执行. 视环境而定.

34. 行使扩展库

一些例子:

>>mPDF -- 能通过Html天生pdf文档

>>PHPExcel -- 读写excel

>>PhpMailer -- 轻松处理赏罚发送包括四面的邮件

>>pChart -- 行使php天生报表

行使开源库完成伟大使命, 如天生pdf, ms-excel文件, 报表等.

35. 行使MVC框架

是时辰行使像 codeigniter 这样的MVC框架了. MVC框架并不强制你写面向工具的代码. 它们仅將php代码与Html疏散.

>>明晰区分php和Html代码. 在团队协作中有甜头, 计划师和措施员可以同事势情.

>>面向工具计划的函数能让你更轻易维护

>>内建函数完成了许多事变, 你不必要一再编写

>>开拓大的应用是必需的

>>许多提议, 能力和hack已被框架实现了

36. 时常看看 phpbench 

phpbench 提供了些php根基操纵的基准测试功效, 它展示了一些徽小的语法变革是奈何导致庞大差此外.

查察php站点的评述, 有题目到IRC提问, 时常阅读开源代码, 行使Linux开拓. 

英文原文

本文地址:http://itbyc.com/php/13193.html
转载请注明出处。
分享是一种快乐,也是一种美德:
评论列表(网友评论仅供网友表达个人看法,并不表明本站同意其观点或证实其描述)
博客首页 | WEB开发 | 网站运营 | CMS使用教程 滇ICP备14002061号-1