php高级教程

本文由码农网 –
小峰原创翻译,转载请看清文末的转载要求,欢迎参与我们的付费投稿计划!

1. 不要使用相对路径,要定义一个根路径

这样的代码行很常见:

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

这种方法有很多缺点:

  • 它首先搜索 php
    包括路径中的指定目录,然后查看当前目录。因此,会检查许多目录。
  • 当一个脚本被包含在另一个脚本的不同目录中时,它的基本目录变为包含脚本的目录。
  • 另一个问题是,当一个脚本从 cron
    运行时,它可能不会将它的父目录作为工作目录。

所以使用绝对路径便成为了一个好方法:

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

所以现在,即使你将项目转移到一个不同的目录,例如将其移动到一个在线的服务器上,这些代码不需要更改就可以运行。

PHP – 多维数组

多维数组指的是包含一个或多个数组的数组。

注释:数组的维度指示您需要选择元素的索引数。

  • 对于二维数组,您需要两个索引来选取元素
  • 对于三维数组,您需要三个索引来选取元素

接上一篇:40+个对初学者非常有用的PHP技巧(一)

2. 不使用 require,包括 require_once 或 include_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 内容等的文件。

PHP – 两维数组

$cars = array
  (
  array("Volvo",22,18),
  array("BMW",15,13),
  array("Saab",5,2),
  array("Land Rover",17,15)
  );

如需访问
$cars 数组中的元素,我们必须使用两个索引(行和列):

<?php
echo $cars[0][0].": 库存:".$cars[0][1].", 销量:".$cars[0][2].".<br>";
echo $cars[1][0].": 库存:".$cars[1][1].", 销量:".$cars[1][2].".<br>";
echo $cars[2][0].": 库存:".$cars[2][1].", 销量:".$cars[2][2].".<br>";
echo $cars[3][0].": 库存:".$cars[3][1].", 销量:".$cars[3][2].".<br>";
?>

或者:

<?php
for ($row = 0; $row < 4; $row++) {
  echo "<p><b>Row number $row</b></p>";
  echo "<ul>";
  for ($col = 0; $col < 3; $col++) {
    echo "<li>".$cars[$row][$col]."</li>";
  }
  echo "</ul>";
}
?>

PHP
date() 函数用于对日期或时间进行格式化。

11.不要在你的应用程序中gzip输出,让apache来做

考虑使用ob_gzhandler?不,别这样做。它没有任何意义。PHP应该是来写应用程序的。不要担心PHP中有关如何优化在服务器和浏览器之间传输的数据。

使用apache mod_gzip/mod_deflate通过.htaccess文件压缩内容。

3. 在应用程序中维护调试环境

在开发过程中,我们 echo
数据库查询,转储创造问题的变量,然后一旦问题被解决,我们注释它们或删除它们。但让一切留在原地可提供长效帮助。

在开发计算机上,你可以这样做:

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";
    }    
}

语法

date(format,timestamp)
参数 描述
format 必需。规定时间戳的格式。
timestamp 可选。规定时间戳。默认是当前时间和日期。
<?php
echo "今天是 " . date("Y/m/d") . "<br>";
echo "今天是 " . date("Y.m.d") . "<br>";
echo "今天是 " . date("Y-m-d") . "<br>";
echo "今天是 " . date("l");
?>

12.从php echo javascript代码时使用json_encode

有些时候一些JavaScript代码是从php动态生成的。

$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"]

这不是很整洁?

4. 通过会话传播状态消息

状态消息是那些执行任务后生成的消息。

<?php
if($wrong_username || $wrong_password)
{
    $msg = 'Invalid username or password';
}
?>
<html>
<body>

<?php echo $msg; ?>

<form>
...
</form>
</body>
</html>

这样的代码很常见。使用变量来显示状态信息有一定的局限性。因为它们无法通过重定向发送(除非你将它们作为
GET
变量传播给下一个脚本,但这非常愚蠢)。而且在大型脚本中可能会有多个消息等。

最好的办法是使用会话来传播(即使是在同一页面上)。想要这样做的话在每个页面上必须得有一个
session_start。

function set_flash($msg)
{
    $_SESSION['message'] = $msg;
}

function get_flash()
{
    $msg = $_SESSION['message'];
    unset($_SESSION['message']);
    return $msg;
}

在你的脚本中:

<?php
if($wrong_username || $wrong_password)
{
    set_flash('Invalid username or password');
}
?>
<html>
<body>

Status is : <?php echo get_flash(); ?>
<form>
...
</form>
</body>
</html>

PHP 提示 – 自动版权年份

使用 date()
函数在您的网站上自动更新版本年份:

版权所有 2010-<?php echo date("Y")?>

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");
}

通过这样做,你就能得到哪里文件写入失败以及为什么失败的准确信息。

5. 让函数变得灵活

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

add_to_cart( 'IPHONE3' , 2 );

当添加单一条目时,使用上面的函数。那么当添加多个条目时,就得创建另一个函数吗?NO。只要让函数变得灵活起来使之能够接受不同的参数即可。请看:

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) );

好了,现在同样的函数就可以接受不同类型的输出了。以上代码可以应用到很多地方让你的代码更加灵活。

获得简单的时间

  • h – 带有首位零的 12
    小时小时格式
  • i – 带有首位零的分钟
  • s – 带有首位零的秒(00 -59)
  • a – 小写的午前和午后(am 或
    pm)

14.改变应用程序创建的文件的权限

当在Linux环境下工作时,权限处理会浪费你很多时间。因此,只要你的php应用程序创建了一些文件,那就应该修改它们的权限以确保它们在外面“平易近人”。否则,例如,文件是由“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);

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

你会得到发送错误的 Header。为什么呢?因为
“超级多余字符”,所有标题都去处理这个去了。于是你得开始调试。你可能需要浪费很多时间来寻找超级额外的空间。

因此要养成省略结束标签的习惯:

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

//No closing tag

这样更好。

获得时区

<?php
date_default_timezone_set("Asia/Shanghai");
echo "当前时间是 " . date("h:i:sa");
?>

15.不要检查提交按钮值来检查表单提交

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

以上代码在大多数时候是正确的,除了应用程序使用多语言的情况。然后“Save”可以是很多不同的东西。那么你该如何再做比较?所以不能依靠提交按钮的值。相反,使用这个:

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

现在你就可以摆脱提交按钮的值了。

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,例如 profiler/debugger 输出。
  • 发送输出给浏览器,并在同一时间做 php
    处理并不是好主意。你见过这样的网站,它有一个 Fatal error
    在侧边栏或在屏幕中间的方框中吗?你知道为什么会出现这种情况吗?因为处理过程和输出被混合在了一起。

通过 PHP mktime() 创建日期

mktime()
函数返回日期的 Unix 时间戳。Unix 时间戳包含 Unix 纪元(1970 年 1 月 1 日
00:00:00 GMT)与指定时间之间的秒数。

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 />";
}

8. 当输出非 HTML 内容时,通过 header 发送正确的 mime 类型

请看一些 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 库也都依赖于 header
信息。

JavaScript,css,jpg 图片,png 图像也是一样:

JavaScript

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

CSS

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

语法

mktime(hour,minute,second,month,day,year)

图片 1

创建日期是
2015-06-10 09:12:31am

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

一些简单的例子是:

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

但是这有一个问题。如果你正在相同域中运行多个应用程序,会话变量会发生冲突。2个不同的应用程序在会话变量中可能会设置相同的键名。举个例子,一个相同域的前端门户和后台管理应用程序。

因此,用包装函数使用应用程序特定键:

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;
}

9. 为 MySQL 连接设置正确的字符编码

曾碰到过 unicode/utf-8 字符被正确地存储在 mysql 表的问题,phpmyadmin
也显示它们是正确的,但是当你使用的时候,你的网页上却并不能正确地显示。里面的奥妙在于
MySQL 连接校对。

$host = 'localhost';
$username = 'root';
$password = 'super_secret';

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

//Check connection validity
if (!$c) 
{
    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');
}

一旦你连接到数据库,不妨设置连接字符集。当你在你的应用程序中使用多种语言时,这绝对有必要。

否则会发生什么呢?你会在非英文文本中看到很多的方框和????????。

通过 PHP strtotime() 用字符串来创建日期

PHP strtotime() 函数用于把人类可读的字符串转换为 Unix 时间。

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有相似名称的内置函数,那么名称不会发生冲突。

从另一个角度看,你可以在相同的应用程序中保持多个版本的相同类,而不会发生任何冲突。因为它被封装了,就是这样。

10. 使用带有正确字符集选项的 htmlentities

PHP 5.4 之前,使用的默认字符编码是 ISO-8859-1,这不能显示例如 À â
这样的字符。

$value = htmlentities($this->value , ENT_QUOTES , 'UTF-8');

从 PHP 5.4 起,默认编码成了
UTF-8,这解决了大部分的问题,但你最好还是知道这件事,如果你的应用程序使用多种语言的话。

语法

strtotime(time,now)

PHP 在将字符串转换为日期这方面非常聪明,所以您能够使用各种值:

19.一些傻瓜式技巧

  • 使用echo代替print
  • 使用str_replace代替preg_replace,除非你确定需要它
  • 不要使用short tags
  • 对于简单的字符串使用单引号代替双引号
  • 在header重定向之后要记得做一个exit
  • 千万不要把函数调用放到for循环控制行中。
  • isset比strlen快
  • 正确和一致地格式化你的代码
  • 不要丢失循环或if-else块的括号。

不要写这样的代码:

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

这绝对是一种浪费。

这样写

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

不要通过吃掉语法缩短你的代码。而是要让你的逻辑更简短。

  • 使用具有代码高亮功能的文本编辑器。代码高亮有助于减少错误。

11. 不要在你的应用程序中 gzip 输出,让 apache 来做

考虑使用 ob_gzhandler?不,别这样做。它没有任何意义。PHP
应该是来写应用程序的。不要担心 PHP
中有关如何优化在服务器和浏览器之间传输的数据。

使用 apache mod_gzip/mod_deflate 通过. htaccess 文件压缩内容。

实例

<?php
$d=strtotime("tomorrow");
echo date("Y-m-d h:i:sa", $d) . "<br>";
$d=strtotime("next Saturday");
echo date("Y-m-d h:i:sa", $d) . "<br>";
$d=strtotime("+3 Months");
echo date("Y-m-d h:i:sa", $d) . "<br>";
?>

<?php
$d1=strtotime("December 31");
$d2=ceil(($d1-time())/60/60/24);
echo "距离十二月三十一日还有:" . $d2 ." 天。";
?>

服务器端包含
(SSI) 用于创建可在多个页面重复使用的函数、页眉、页脚或元素。

20. 使用array_map快速处理数组

比方说,你要trim一个数组的所有元素。新手会这样做:

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

但它可以使用array_map变得更整洁:

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

这适用于trim数组$arr的所有元素。另一个类似的函数是array_walk。

12. 从 php echo javascript 代码时使用 json_encode

有些时候一些 JavaScript 代码是从 php 动态生成的。

$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"]

这不是很整洁?

PHP include 和 require 语句

通过 include 或 require 语句,可以将 PHP 文件的内容插入另一个 PHP
文件(在服务器执行它之前)。

include 和 require
语句是相同的,除了错误处理方面:

  • require
    会生成致命错误(E_COMPILE_ERROR)并停止脚本
  • include
    只生成警告(E_WARNING),并且脚本会继续

假设我们有一个名为 “footer.php” 的标准的页脚文件,就像这样:

<?php
echo "<p>Copyright © 2006-" . date("Y") . " W3School.com.cn</p>";
?>

如需在一张页面中引用这个页脚文件,请使用 include 语句:

<html>
<body>
<h1>欢迎访问我们的首页!</h1>
<p>一段文本。</p>
<p>一段文本。</p>
<?php include 'footer.php';?>
</body>
</html>

21.使用php过滤器验证数据

你是不是使用正则表达式来验证如电子邮件,IP地址等值?是的,每个人都是这样做的。现在,让我们试试一个不同的东西,那就是过滤器。

php过滤器扩展程序将提供简单的方法来有效验证或校验值。

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");
}

通过这样做,你就能得到哪里文件写入失败以及为什么失败的准确信息。

PHP 操作文件

PHP
拥有的多种函数可供创建、读取、上传以及编辑文件。

注意:请谨慎操作文件!

当您操作文件时必须非常小心。如果您操作失误,可能会造成非常严重的破坏。常见的错误是:

  • 编辑错误的文件
  • 被垃圾数据填满硬盘
  • 意外删除文件内容

22.强制类型检查

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

这是一种好习惯。

14. 改变应用程序创建的文件的权限

当在 Linux 环境下工作时,权限处理会浪费你很多时间。因此,只要你的 php
应用程序创建了一些文件,那就应该修改它们的权限以确保它们在外面
“平易近人”。否则,例如,文件是由 “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);

PHP readfile() 函数

readfile() 函数读取文件,并把它写入输出缓冲。

假设我们有一个名为 “webdictionary.txt”
的文本文件,存放在服务器上,就像这样:

AJAX = Asynchronous JavaScript and XML
CSS = Cascading Style Sheets
HTML = Hyper Text Markup Language
PHP = PHP Hypertext Preprocessor
SQL = Structured Query Language
SVG = Scalable Vector Graphics
XML = EXtensible Markup Language

读取此文件并写到输出流的 PHP 代码如下(如读取成功则 readfile()
函数返回字节数):

23.使用set_error_handler()将Php错误写入到文件

set_error_handler()可以用来设置自定义的错误处理程序。在文件中编写一些重要的错误用于日志是个好主意。

15. 不要检查提交按钮值来检查表单提交

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

以上代码在大多数时候是正确的,除了应用程序使用多语言的情况。然后 “Save”
可以是很多不同的东西。那么你该如何再做比较?所以不能依靠提交按钮的值。相反,使用这个:

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

现在你就可以摆脱提交按钮的值了。

实例

<?php
echo readfile("webdictionary.txt");
?>

24.小心处理大型数组

大型的数组或字符串,如果一个变量保存了一些规模非常大的东西,那么要小心处理。常见错误是创建副本,然后耗尽内存,并得到内存溢出的致命错误:

$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文件或导出表到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
    }
}

尽快复原它们,这样内存就能被释放,并且脚本的其余部分就能放松。

下面是关于如何通过引用来赋值从而节省内存的一个简单示例。

<?php

ini_set('display_errors' , true);
error_reporting(E_ALL);

$a = array();

for($i = 0; $i < 100000 ; $i++)
{
    $a[$i] = 'A'.$i;
}

echo 'Memory usage in MB : '. memory_get_usage() / 1000000 . '<br />';

$b = $a;
$b[0] = 'B';

echo 'Memory usage in MB after 1st copy : '. memory_get_usage() / 1000000 . '<br />';

$c = $a;
$c[0] = 'B';

echo 'Memory usage in MB after 2st copy : '. memory_get_usage() / 1000000 . '<br />';

$d =& $a;
$d[0] = 'B';

echo 'Memory usage in MB after 3st copy (reference) : '. memory_get_usage() / 1000000 . '<br />';

一个典型php 5.4机器上的输出是:

Memory usage in MB : 18.08208
Memory usage in MB after 1st copy : 27.930944
Memory usage in MB after 2st copy : 37.779808
Memory usage in MB after 3st copy (reference) : 37.779864

因此可以看出,内存被保存在第3份通过引用的副本中。否则,在所有普通副本中内存将被越来越多地使用。

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 />";
}

PHP Open File – fopen()

fopen()
的第一个参数包含被打开的文件名,第二个参数规定打开文件的模式。如果
fopen() 函数未能打开指定的文件,下面的例子会生成一段消息:

<?php
$myfile = fopen("webdictionary.txt", "r") or die("Unable to open file!");
echo fread($myfile,filesize("webdictionary.txt"));
fclose($myfile);
?>

文件会以如下模式之一打开:

模式 描述
r 打开文件为只读。文件指针在文件的开头开始。
w 打开文件为只写。删除文件的内容或创建一个新的文件,如果它不存在。文件指针在文件的开头开始。
a 打开文件为只写。文件中的现有数据会被保留。文件指针在文件结尾开始。创建新的文件,如果文件不存在。
x 创建新文件为只写。返回 FALSE 和错误,如果文件已存在。
r+ 打开文件为读/写、文件指针在文件开头开始。
w+ 打开文件为读/写。删除文件内容或创建新文件,如果它不存在。文件指针在文件开头开始。
a+ 打开文件为读/写。文件中已有的数据会被保留。文件指针在文件结尾开始。创建新文件,如果它不存在。
x+ 创建新文件为读/写。返回 FALSE 和错误,如果文件已存在。

25.在整个脚本中使用单一的数据库连接

请确保你在整个脚本使用单一的数据库连接。从一开始就打开连接,使用至结束,并在结束时关闭它。不要像这样在函数内打开连接:

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

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

有多个连接也不好,会因为每个连接都需要时间来创建和使用更多的内存,而导致执行减缓。

在特殊情况下。例如数据库连接,可以使用单例模式。

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

一些简单的例子是:

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

但是这有一个问题。如果你正在相同域中运行多个应用程序,会话变量会发生冲突。2
个不同的应用程序在会话变量中可能会设置相同的键名。举个例子,一个相同域的前端门户和后台管理应用程序。

因此,用包装函数使用应用程序特定键:

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;
}

PHP 读取文件 – fread()

fread()
的第一个参数包含待读取文件的文件名,第二个参数规定待读取的最大字节数。

如下 PHP 代码把 “webdictionary.txt”
文件读至结尾:

fread($myfile,filesize("webdictionary.txt"));

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
有相似名称的内置函数,那么名称不会发生冲突。

从另一个角度看,你可以在相同的应用程序中保持多个版本的相同类,而不会发生任何冲突。因为它被封装了,就是这样。

PHP 关闭文件 – fclose()

注释:用完文件后把它们全部关闭是一个良好的编程习惯。您并不想打开的文件占用您的服务器资源。

fclose()
需要待关闭文件的名称(或者存有文件名的变量):

<?php
$myfile = fopen("webdictionary.txt", "r");
// some code to be executed....
fclose($myfile);
?>

19. 一些傻瓜式技巧

  • 使用 echo 代替 print
  • 使用 str_replace 代替 preg_replace,除非你确定需要它
  • 不要使用 short tags
  • 对于简单的字符串使用单引号代替双引号
  • 在 header 重定向之后要记得做一个 exit
  • 千万不要把函数调用放到 for 循环控制行中。
  • isset 比 strlen 快
  • 正确和一致地格式化你的代码
  • 不要丢失循环或 if-else 块的括号。

不要写这样的代码:

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

这绝对是一种浪费。

这样写

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

不要通过吃掉语法缩短你的代码。而是要让你的逻辑更简短。

  • 使用具有代码高亮功能的文本编辑器。代码高亮有助于减少错误。

PHP 读取单行文件 – fgets()

下例输出 “webdictionary.txt”
文件的首行:

<?php
$myfile = fopen("webdictionary.txt", "r") or die("Unable to open file!");
echo fgets($myfile);
fclose($myfile);
?>

注释:调用 fgets()
函数之后,文件指针会移动到下一行。

20. 使用 array_map 快速处理数组

比方说,你要 trim 一个数组的所有元素。新手会这样做:

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

但它可以使用 array_map 变得更整洁:

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

这适用于 trim 数组 $arr 的所有元素。另一个类似的函数是 array_walk。

PHP 检查 End-Of-File – feof()

feof() 对于遍历未知长度的数据很有用。

下例逐行读取 “webdictionary.txt” 文件,直到 end-of-file:

<?php
$myfile = fopen("webdictionary.txt", "r") or die("Unable to open file!");
// 输出单行直到 end-of-file
while(!feof($myfile)) {
  echo fgets($myfile) . "<br>";
}
fclose($myfile);
?>

21. 使用 php 过滤器验证数据

你是不是使用正则表达式来验证如电子邮件,IP
地址等值?是的,每个人都是这样做的。现在,让我们试试一个不同的东西,那就是过滤器。

php 过滤器扩展程序将提供简单的方法来有效验证或校验值。

PHP 读取单字符 – fgetc()

下例逐字符读取 “webdictionary.txt”
文件,直到 end-of-file:

<?php
$myfile = fopen("webdictionary.txt", "r") or die("Unable to open file!");
// 输出单字符直到 end-of-file
while(!feof($myfile)) {
  echo fgetc($myfile);
}
fclose($myfile);
?>

注释:在调用 fgetc()
函数之后,文件指针会移动到下一个字符。

22. 强制类型检查

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

这是一种好习惯。

PHP 创建文件 – fopen()

如果您用 fopen()
打开并不存在的文件,此函数会创建文件,假定文件被打开为写入(w)或增加(a)。

下面的例子创建名为 “testfile.txt” 的新文件。此文件将被创建于 PHP
代码所在的相同目录中:

$myfile = fopen("testfile.txt", "w")

23. 使用 set_error_handler()将 Php 错误写入到文件

set_error_handler()可以用来设置自定义的错误处理程序。在文件中编写一些重要的错误用于日志是个好主意。

PHP 文件权限

如果您试图运行这段代码时发生错误,请检查您是否有向硬盘写入信息的 PHP
文件访问权限。

24. 小心处理大型数组

大型的数组或字符串,如果一个变量保存了一些规模非常大的东西,那么要小心处理。常见错误是创建副本,然后耗尽内存,并得到内存溢出的致命错误:

$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 文件或导出表到 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
    }
}

尽快复原它们,这样内存就能被释放,并且脚本的其余部分就能放松。

下面是关于如何通过引用来赋值从而节省内存的一个简单示例。

<?php

ini_set('display_errors' , true);
error_reporting(E_ALL);

$a = array();

for($i = 0; $i < 100000 ; $i++)
{
    $a[$i] = 'A'.$i;
}

echo 'Memory usage in MB : '. memory_get_usage() / 1000000 . '<br />';

$b = $a;
$b[0] = 'B';

echo 'Memory usage in MB after 1st copy : '. memory_get_usage() / 1000000 . '<br />';

$c = $a;
$c[0] = 'B';

echo 'Memory usage in MB after 2st copy : '. memory_get_usage() / 1000000 . '<br />';

$d =& $a;
$d[0] = 'B';

echo 'Memory usage in MB after 3st copy (reference) : '. memory_get_usage() / 1000000 . '<br />';

一个典型 php 5.4 机器上的输出是:

Memory usage in MB : 18.08208
Memory usage in MB after 1st copy : 27.930944
Memory usage in MB after 2st copy : 37.779808
Memory usage in MB after 3st copy (reference) : 37.779864

因此可以看出,内存被保存在第 3
份通过引用的副本中。否则,在所有普通副本中内存将被越来越多地使用。

PHP 写入文件 – fwrite()

fwrite()
的第一个参数包含要写入的文件的文件名,第二个参数是被写的字符串。

<?php
$myfile = fopen("newfile.txt", "w") or die("Unable to open file!");
$txt = "Bill Gatesn";
fwrite($myfile, $txt);
$txt = "Steve Jobsn";
fwrite($myfile, $txt);
fclose($myfile);
?>

25. 在整个脚本中使用单一的数据库连接

请确保你在整个脚本使用单一的数据库连接。从一开始就打开连接,使用至结束,并在结束时关闭它。不要像这样在函数内打开连接:

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

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

有多个连接也不好,会因为每个连接都需要时间来创建和使用更多的内存,而导致执行减缓。

在特殊情况下。例如数据库连接,可以使用单例模式。


原文:http://www.codeceo.com/article/40-php-tips-part-1.html

PHP 覆盖(Overwriting)

如果现在 “newfile.txt”
包含了一些数据,我们可以展示在写入已有文件时发生的的事情。所有已存在的数据会被擦除并以一个新文件开始。

<?php
$myfile = fopen("newfile.txt", "w") or die("Unable to open file!");
$txt = "Mickey Mousen";
fwrite($myfile, $txt);
$txt = "Minnie Mousen";
fwrite($myfile, $txt);
fclose($myfile);
?>

创建上传脚本

“upload_file.php” 文件含有供上传文件的代码:

<?php
if ($_FILES["file"]["error"] > 0)
  {
  echo "Error: " . $_FILES["file"]["error"] . "<br />";
  }
else
  {
  echo "Upload: " . $_FILES["file"]["name"] . "<br />";
  echo "Type: " . $_FILES["file"]["type"] . "<br />";
  echo "Size: " . ($_FILES["file"]["size"] / 1024) . " Kb<br />";
  echo "Stored in: " . $_FILES["file"]["tmp_name"];
  }
?>
  • $_FILES[“file”][“name”] –
    被上传文件的名称
  • $_FILES[“file”][“type”] –
    被上传文件的类型
  • $_FILES[“file”][“size”] –
    被上传文件的大小,以字节计
  • $_FILES[“file”][“tmp_name”] –
    存储在服务器的文件的临时副本的名称
  • $_FILES[“file”][“error”] –
    由文件上传导致的错误代码

上传限制

在这个脚本中,我们增加了对文件上传的限制。用户只能上传 .gif 或 .jpeg
文件,文件大小必须小于 20 kb:

<?php
if ((($_FILES["file"]["type"] == "image/gif")
|| ($_FILES["file"]["type"] == "image/jpeg")
|| ($_FILES["file"]["type"] == "image/pjpeg"))
&& ($_FILES["file"]["size"] < 20000))
  {
  if ($_FILES["file"]["error"] > 0)
    {
    echo "Error: " . $_FILES["file"]["error"] . "<br />";
    }
  else
    {
    echo "Upload: " . $_FILES["file"]["name"] . "<br />";
    echo "Type: " . $_FILES["file"]["type"] . "<br />";
    echo "Size: " . ($_FILES["file"]["size"] / 1024) . " Kb<br />";
    echo "Stored in: " . $_FILES["file"]["tmp_name"];
    }
  }
else
  {
  echo "Invalid file";
  }
?>

注释:对于 IE,识别 jpg 文件的类型必须是 pjpeg,对于 FireFox,必须是
jpeg。

保存被上传的文件

上面的例子在服务器的 PHP 临时文件夹创建了一个被上传文件的临时副本。

这个临时的复制文件会在脚本结束时消失。要保存被上传的文件,我们需要把它拷贝到另外的位置:

<?php
if ((($_FILES["file"]["type"] == "image/gif")
|| ($_FILES["file"]["type"] == "image/jpeg")
|| ($_FILES["file"]["type"] == "image/pjpeg"))
&& ($_FILES["file"]["size"] < 20000))
  {
  if ($_FILES["file"]["error"] > 0)
    {
    echo "Return Code: " . $_FILES["file"]["error"] . "<br />";
    }
  else
    {
    echo "Upload: " . $_FILES["file"]["name"] . "<br />";
    echo "Type: " . $_FILES["file"]["type"] . "<br />";
    echo "Size: " . ($_FILES["file"]["size"] / 1024) . " Kb<br />";
    echo "Temp file: " . $_FILES["file"]["tmp_name"] . "<br />";

    if (file_exists("upload/" . $_FILES["file"]["name"]))
      {
      echo $_FILES["file"]["name"] . " already exists. ";
      }
    else
      {
      move_uploaded_file($_FILES["file"]["tmp_name"],
      "upload/" . $_FILES["file"]["name"]);
      echo "Stored in: " . "upload/" . $_FILES["file"]["name"];
      }
    }
  }
else
  {
  echo "Invalid file";
  }
?>

上面的脚本检测了是否已存在此文件,如果不存在,则把文件拷贝到指定的文件夹。

注释:这个例子把文件保存到了名为 “upload” 的新文件夹。

cookie
常用于识别用户。

setcookie() 函数用于设置 cookie。

注释:setcookie() 函数必须位于 <html> 标签之前。

语法

setcookie(name, value, expire, path, domain);

在下面的例子中,我们将创建名为 “user” 的 cookie,把为它赋值 “Alex
Porter”。我们也规定了此 cookie 在一小时后过期:

<?php 
setcookie("user", "Alex Porter", time()+3600);
?>

注释:在发送
cookie 时,cookie 的值会自动进行 URL 编码,在取回时进行自动解码(为防止
URL 编码,请使用 setrawcookie() 取而代之)。

PHP
的 $_COOKIE 变量用于取回 cookie 的值。

在下面的例子中,我们取回了名为 “user” 的 cookie
的值,并把它显示在了页面上:

<?php
// Print a cookie
echo $_COOKIE["user"];
// A way to view all cookies
print_r($_COOKIE);
?>

在下面的例子中,我们使用 isset() 函数来确认是否已设置了 cookie:

<html>
<body>
<?php
if (isset($_COOKIE["user"]))
  echo "Welcome " . $_COOKIE["user"] . "!<br />";
else
  echo "Welcome guest!<br />";
?>
</body>
</html>

当删除
cookie 时,您应当使过期日期变更为过去的时间点。

<?php 
// set the expiration date to one hour ago
setcookie("user", "", time()-3600);
?>

PHP
session 变量用于存储有关用户会话的信息,或更改用户会话的设置。Session
变量保存的信息是单一用户的,并且可供应用程序中的所有页面使用。

不过,会话信息是临时的,在用户离开网站后将被删除。如果您需要永久储存信息,可以把数据存储在数据库中。

Session
的工作机制是:为每个访问者创建一个唯一的 id (UID),并基于这个 UID
来存储变量。UID 存储在 cookie 中,亦或通过 URL 进行传导。

在您把用户信息存储到 PHP session 中之前,首先必须启动会话。

注释:session_start() 函数必须位于 <html> 标签之前

存储 Session 变量

存储和取回 session 变量的正确方法是使用 PHP $_SESSION 变量:

<?php
session_start();
// store session data
$_SESSION['views']=1;
?>
<html>
<body>
<?php
//retrieve session data
echo "Pageviews=". $_SESSION['views'];
?>
</body>
</html>

输出:

Pageviews=1

终结 Session

如果您希望删除某些 session 数据,可以使用 unset() 或 session_destroy()
函数。

unset() 函数用于释放指定的 session 变量:

<?php
unset($_SESSION['views']);
?>

您也可以通过 session_destroy() 函数彻底终结 session:

<?php
session_destroy();
?>

注释:session_destroy() 将重置 session,您将失去所有已存储的 session
数据。

PHP mail() 函数

PHP mail()
函数用于从脚本中发送电子邮件。

语法

mail(to,subject,message,headers,parameters)
参数 描述
to 必需。规定 email 接收者。
subject 必需。规定 email 的主题。注释:该参数不能包含任何新行字符。
message 必需。定义要发送的消息。应使用 LF (n) 来分隔各行。
headers

可选。规定附加的标题,比如 From、Cc 以及 Bcc。

应当使用 CRLF (rn) 分隔附加的标题。

parameters 可选。对邮件发送程序规定额外的参数。

注释:PHP
需要一个已安装且正在运行的邮件系统,以便使邮件函数可用。所用的程序通过在
php.ini 文件中的配置设置进行定义。

PHP 简易 E-Mail

通过 PHP 发送电子邮件的最简单的方式是发送一封文本 email。

在下面的例子中,我们首先声明变量($to, $subject, $message, $from,
$headers),然后我们在 mail() 函数中使用这些变量来发送了一封 e-mail:

<?php
$to = "someone@example.com";
$subject = "Test mail";
$message = "Hello! This is a simple email message.";
$from = "someonelse@example.com";
$headers = "From: $from";
mail($to,$subject,$message,$headers);
echo "Mail Sent.";
?>

PHP Mail Form

通过
PHP,您能够在自己的站点制作一个反馈表单。下面的例子向指定的 e-mail
地址发送了一条文本消息:

<html>
<body>

<?php
if (isset($_REQUEST['email']))
//if "email" is filled out, send email
  {
  //send email
  $email = $_REQUEST['email'] ; 
  $subject = $_REQUEST['subject'] ;
  $message = $_REQUEST['message'] ;
  mail( "someone@example.com", "Subject: $subject",
  $message, "From: $email" );
  echo "Thank you for using our mail form";
  }
else
//if "email" is not filled out, display the form
  {
  echo "<form method='post' action='mailform.php'>
  Email: <input name='email' type='text' /><br />
  Subject: <input name='subject' type='text' /><br />
  Message:<br />
  <textarea name='message' rows='15' cols='40'>
  </textarea><br />
  <input type='submit' />
  </form>";
  }
?>

</body>
</html>

例子解释:

  1. 首先,检查是否填写了邮件输入框
  2. 如果未填写(比如在页面被首次访问时),输出
    HTML 表单
  3. 如果已填写(在表单被填写后),从表单发送邮件
  4. 当点击提交按钮后,重新载入页面,显示邮件发送成功的消息

PHP 防止 E-mail 注入

防止 e-mail 注入的最好方法是对输入进行验证。

<html>
<body>
<?php
function spamcheck($field)
  {
  //filter_var() sanitizes the e-mail 
  //address using FILTER_SANITIZE_EMAIL
  $field=filter_var($field, FILTER_SANITIZE_EMAIL);

  //filter_var() validates the e-mail
  //address using FILTER_VALIDATE_EMAIL
  if(filter_var($field, FILTER_VALIDATE_EMAIL))
    {
    return TRUE;
    }
  else
    {
    return FALSE;
    }
  }

if (isset($_REQUEST['email']))
  {//if "email" is filled out, proceed

  //check if the email address is invalid
  $mailcheck = spamcheck($_REQUEST['email']);
  if ($mailcheck==FALSE)
    {
    echo "Invalid input";
    }
  else
    {//send email
    $email = $_REQUEST['email'] ; 
    $subject = $_REQUEST['subject'] ;
    $message = $_REQUEST['message'] ;
    mail("someone@example.com", "Subject: $subject",
    $message, "From: $email" );
    echo "Thank you for using our mail form";
    }
  }
else
  {//if "email" is not filled out, display the form
  echo "<form method='post' action='mailform.php'>
  Email: <input name='email' type='text' /><br />
  Subject: <input name='subject' type='text' /><br />
  Message:<br />
  <textarea name='message' rows='15' cols='40'>
  </textarea><br />
  <input type='submit' />
  </form>";
  }
?>

</body>
</html>

在上面的代码中,我们使用了 PHP
过滤器来对输入进行验证:

  • FILTER_SANITIZE_EMAIL
    从字符串中删除电子邮件的非法字符
  • FILTER_VALIDATE_EMAIL
    验证电子邮件地址


PHP
中,默认的错误处理很简单。一条消息会被发送到浏览器,这条消息带有文件名、行号以及一条描述错误的消息。

基本的错误处理:使用 die() 函数    

die("File not found");

创建自定义错误处理器

创建一个自定义的错误处理器非常简单。我们很简单地创建了一个专用函数,可以在
PHP 中发生错误时调用该函数。

该函数必须有能力处理至少两个参数 (error
level 和 error message),但是可以接受最多五个参数(可选的:file,
line-number 以及 error context):

语法

error_function(error_level,error_message,
error_file,error_line,error_context)
参数 描述
error_level

必需。为用户定义的错误规定错误报告级别。必须是一个值数。

参见下面的表格:错误报告级别。

error_message 必需。为用户定义的错误规定错误消息。
error_file 可选。规定错误在其中发生的文件名。
error_line 可选。规定错误发生的行号。
error_context 可选。规定一个数组,包含了当错误发生时在用的每个变量以及它们的值。

错误报告级别

常量 描述
2 E_WARNING 非致命的 run-time 错误。不暂停脚本执行。
8 E_NOTICE

Run-time 通知。

脚本发现可能有错误发生,但也可能在脚本正常运行时发生。

256 E_USER_ERROR 致命的用户生成的错误。这类似于程序员使用 PHP 函数 trigger_error() 设置的 E_ERROR。
512 E_USER_WARNING 非致命的用户生成的警告。这类似于程序员使用 PHP 函数 trigger_error() 设置的 E_WARNING。
1024 E_USER_NOTICE 用户生成的通知。这类似于程序员使用 PHP 函数 trigger_error() 设置的 E_NOTICE。
4096 E_RECOVERABLE_ERROR 可捕获的致命错误。类似 E_ERROR,但可被用户定义的处理程序捕获。(参见 set_error_handler())
8191 E_ALL

所有错误和警告,除级别 E_STRICT 以外。

(在 PHP 6.0,E_STRICT 是 E_ALL 的一部分)

function customError($errno, $errstr)
 { 
 echo "<b>Error:</b> [$errno] $errstr<br />";
 echo "Ending Script";
 die();
 }

上面的代码是一个简单的错误处理函数。当它被触发时,它会取得错误级别和错误消息。然后它会输出错误级别和消息,并终止脚本。

Set Error Handler

PHP
的默认错误处理程序是内建的错误处理程序。我们打算把上面的函数改造为脚本运行期间的默认错误处理程序。

可以修改错误处理程序,使其仅应用到某些错误,这样脚本就可以不同的方式来处理不同的错误。不过,在本例中,我们打算针对所有错误来使用我们的自定义错误处理程序:

set_error_handler("customError");

由于我们希望我们的自定义函数来处理所有错误,set_error_handler()
仅需要一个参数,可以添加第二个参数来规定错误级别。

触发错误

在脚本中用户输入数据的位置,当用户的输入无效时触发错误的很有用的。在
PHP 中,这个任务由 trigger_error() 完成。

您可以在脚本中任何位置触发错误,通过添加的第二个参数,您能够规定所触发的错误级别。

可能的错误类型:

  • E_USER_ERROR – 致命的用户生成的
    run-time 错误。错误无法恢复。脚本执行被中断。
  • E_USER_WARNING –
    非致命的用户生成的 run-time 警告。脚本执行不被中断。
  • E_USER_NOTICE – 默认。用户生成的
    run-time
    通知。脚本发现了可能的错误,也有可能在脚本运行正常时发生。

错误记录

默认地,根据在 php.ini 中的 error_log 配置,PHP
向服务器的错误记录系统或文件发送错误记录。通过使用 error_log()
函数,您可以向指定的文件或远程目的地发送错误记录。

通过电子邮件向您自己发送错误消息,是一种获得指定错误的通知的好办法。

通过 E-Mail 发送错误消息

在下面的例子中,如果特定的错误发生,我们将发送带有错误消息的电子邮件,并结束脚本:

<?php
//error handler function
function customError($errno, $errstr)
 { 
 echo "<b>Error:</b> [$errno] $errstr<br />";
 echo "Webmaster has been notified";
 error_log("Error: [$errno] $errstr",1,
 "someone@example.com","From: webmaster@example.com");
}

//set error handler
set_error_handler("customError",E_USER_WARNING);

//trigger error
$test=2;
if ($test>1)
 {
 trigger_error("Value must be 1 or below",E_USER_WARNING);
 }
?>

以上代码的输出应该类似这样:

Error: [512] Value must be 1 or below
Webmaster has been notified

接收自以上代码的邮件类似这样:

Error: [512] Value must be 1 or below

这个方法不适合所有的错误。常规错误应当通过使用默认的 PHP
记录系统在服务器上进行记录。

异常(Exception)用于在指定的错误发生时改变脚本的正常流程。

当异常被触发时,通常会发生:

  • 当前代码状态被保存
  • 代码执行被切换到预定义的异常处理器函数
  • 根据情况,处理器也许会从保存的代码状态重新开始执行代码,终止脚本执行,或从代码中另外的位置继续执行脚本

异常的基本使用

当异常被抛出时,其后的代码不会继续执行,PHP 会尝试查找匹配的 “catch”
代码块。

如果异常没有被捕获,而且又没用使用 set_exception_handler()
作相应的处理的话,那么将发生一个严重的错误(致命错误),并且输出
“Uncaught Exception” (未捕获异常)的错误消息。

正确的处理程序应当包括:

  1. Try – 使用异常的函数应该位于 “try”
    代码块内。如果没有触发异常,则代码将照常继续执行。但是如果异常被触发,会抛出一个异常。
  2. Throw – 这里规定如何触发异常。每一个
    “throw” 必须对应至少一个 “catch”
  3. Catch – “catch”
    代码块会捕获异常,并创建一个包含异常信息的对象

创建一个自定义的 Exception 类

这个自定义的
exception 类继承了 PHP 的 exception
类的所有属性,您可向其添加自定义的函数。

<?php
class customException extends Exception
 {
 public function errorMessage()
  {
  //error message
  $errorMsg = 'Error on line '.$this->getLine().' in '.$this->getFile()
  .': <b>'.$this->getMessage().'</b> is not a valid E-Mail address';
  return $errorMsg;
  }
 }

$email = "someone@example...com";

try
 {
 //check if 
 if(filter_var($email, FILTER_VALIDATE_EMAIL) === FALSE)
  {
  //throw exception if email is not valid
  throw new customException($email);
  }
 }

catch (customException $e)
 {
 //display custom message
 echo $e->errorMessage();
 }
?>

例子解释:

上面的代码抛出了一个异常,并通过一个自定义的
exception 类来捕获它:

  1. customException() 类是作为旧的
    exception
    类的一个扩展来创建的。这样它就继承了旧类的所有属性和方法。
  2. 创建 errorMessage() 函数。如果
    e-mail 地址不合法,则该函数返回一条错误消息
  3. 把 $email 变量设置为不合法的 e-mail
    地址字符串
  4. 执行 “try” 代码块,由于 e-mail
    地址不合法,因此抛出一个异常
  5. “catch”
    代码块捕获异常,并显示错误消息

多个异常

可以使用多个
if..else 代码块,或一个 switch
代码块,或者嵌套多个异常。这些异常能够使用不同的 exception
类,并返回不同的错误消息:

<?php
class customException extends Exception
{
public function errorMessage()
{
//error message
$errorMsg = 'Error on line '.$this->getLine().' in '.$this->getFile()
.': <b>'.$this->getMessage().'</b> is not a valid E-Mail address';
return $errorMsg;
}
}

$email = "someone@example.com";

try
 {
 //check if 
 if(filter_var($email, FILTER_VALIDATE_EMAIL) === FALSE)
  {
  //throw exception if email is not valid
  throw new customException($email);
  }
 //check for "example" in mail address
 if(strpos($email, "example") !== FALSE)
  {
  throw new Exception("$email is an example e-mail");
  }
 }

catch (customException $e)
 {
 echo $e->errorMessage();
 }

catch(Exception $e)
 {
 echo $e->getMessage();
 }
?>

例子解释:

上面的代码测试了两种条件,如何任何条件不成立,则抛出一个异常:

  1. customException() 类是作为旧的
    exception
    类的一个扩展来创建的。这样它就继承了旧类的所有属性和方法。
  2. 创建 errorMessage() 函数。如果
    e-mail 地址不合法,则该函数返回一个错误消息。
  3. 执行 “try”
    代码块,在第一个条件下,不会抛出异常。
  4. 由于 e-mail 含有字符串
    “example”,第二个条件会触发异常。
  5. “catch”
    代码块会捕获异常,并显示恰当的错误消息

如果没有捕获 customException,紧紧捕获了
base exception,则在那里处理异常。

重新抛出异常

有时,当异常被抛出时,您也许希望以不同于标准的方式对它进行处理。可以在一个
“catch” 代码块中再次抛出异常。

脚本应该对用户隐藏系统错误。对程序员来说,系统错误也许很重要,但是用户对它们并不感兴趣。为了让用户更容易使用,您可以再次抛出带有对用户比较友好的消息的异常:

<?php
class customException extends Exception
 {
 public function errorMessage()
  {
  //error message
  $errorMsg = $this->getMessage().' is not a valid E-Mail address.';
  return $errorMsg;
  }
 }

$email = "someone@example.com";

try
 {
 try
  {
  //check for "example" in mail address
  if(strpos($email, "example") !== FALSE)
   {
   //throw exception if email is not valid
   throw new Exception($email);
   }
  }
 catch(Exception $e)
  {
  //re-throw exception
  throw new customException($email);
  }
 }

catch (customException $e)
 {
 //display custom message
 echo $e->errorMessage();
 }
?>

例子解释:

上面的代码检测在邮件地址中是否含有字符串
“example”。如果有,则再次抛出异常:

  1. customException() 类是作为旧的
    exception
    类的一个扩展来创建的。这样它就继承了旧类的所有属性和方法。
  2. 创建 errorMessage() 函数。如果
    e-mail 地址不合法,则该函数返回一个错误消息。
  3. 把 $email
    变量设置为一个有效的邮件地址,但含有字符串 “example”。
  4. “try” 代码块包含另一个 “try”
    代码块,这样就可以再次抛出异常。
  5. 由于 e-mail 包含字符串
    “example”,因此触发异常。
  6. “catch” 捕获到该异常,并重新抛出
    “customException”。
  7. 捕获到
    “customException”,并显示一条错误消息。

如果在其目前的 “try”
代码块中异常没有被捕获,则它将在更高层级上查找 catch 代码块。

设置顶层异常处理器 (Top Level Exception Handler)

set_exception_handler()
函数可设置处理所有未捕获异常的用户定义函数。

<?php
function myException($exception)
{
echo "<b>Exception:</b> " , $exception->getMessage();
}

set_exception_handler('myException');

throw new Exception('Uncaught Exception occurred');
?>

以上代码的输出应该类似这样:

Exception: Uncaught Exception occurred

在上面的代码中,不存在 “catch”
代码块,而是触发顶层的异常处理程序。应该使用此函数来捕获所有未被捕获的异常。

异常的规则

  • 需要进行异常处理的代码应该放入 try
    代码块内,以便捕获潜在的异常。
  • 每个 try 或 throw
    代码块必须至少拥有一个对应的 catch 代码块。
  • 使用多个 catch
    代码块可以捕获不同种类的异常。
  • 可以在 try 代码块内的 catch
    代码块中再次抛出(re-thrown)异常。

简而言之:如果抛出了异常,就必须捕获它。

PHP
过滤器用于验证和过滤来自非安全来源的数据,比如用户的输入。

您应该始终对外部数据进行过滤!

什么是外部数据?

  • 来自表单的输入数据
  • Cookies
  • 服务器变量
  • 数据库查询结果

函数和过滤器

如需过滤变量,请使用下面的过滤器函数之一:

  • filter_var() –
    通过一个指定的过滤器来过滤单一的变量
  • filter_var_array() –
    通过相同的或不同的过滤器来过滤多个变量
  • filter_input –
    获取一个输入变量,并对它进行过滤
  • filter_input_array –
    获取多个输入变量,并通过相同的或不同的过滤器对它们进行过滤

在下面的例子中,我们用 filter_var()
函数验证了一个整数:

<?php
$int = 123;

if(!filter_var($int, FILTER_VALIDATE_INT))
 {
 echo("Integer is not valid");
 }
else
 {
 echo("Integer is valid");
 }
?>

Validating 和 Sanitizing

有两种过滤器:

Validating 过滤器:

  • 用于验证用户输入
  • 严格的格式规则(比如 URL 或 E-Mail
    验证)
  • 如果成功则返回预期的类型,如果失败则返回
    FALSE

Sanitizing 过滤器:

  • 用于允许或禁止字符串中指定的字符
  • 无数据格式规则
  • 始终返回字符串

选项和标志

选项和标志用于向指定的过滤器添加额外的过滤选项。

不同的过滤器有不同的选项和标志。

在下面的例子中,我们用 filter_var() 和 “min_range” 以及 “max_range”
选项验证了一个整数:

<?php
$var=300;

$int_options = array(
"options"=>array
 (
 "min_range"=>0,
 "max_range"=>256
 )
);

if(!filter_var($var, FILTER_VALIDATE_INT, $int_options))
 {
 echo("Integer is not valid");
 }
else
 {
 echo("Integer is valid");
 }
?>

验证输入

我们需要作的第一件事情是确认是否存在我们正在查找的输入数据。

然后我们用 filter_input() 函数过滤输入的数据。

在下面的例子中,输入变量 “email” 被传到 PHP 页面:

<?php
if(!filter_has_var(INPUT_GET, "email"))
 {
 echo("Input type does not exist");
 }
else
 {
 if (!filter_input(INPUT_GET, "email", FILTER_VALIDATE_EMAIL))
  {
  echo "E-Mail is not valid";
  }
 else
  {
  echo "E-Mail is valid";
  }
 }
?>

例子解释:

上面的例子有一个通过 “GET”
方法传送的输入变量 (email):

  1. 检测是否存在 “GET” 类型的 “email”
    输入变量
  2. 如果存在输入变量,检测它是否是有效的邮件地址

净化输入

让我们试着清理一下从表单传来的
URL。

首先,我们要确认是否存在我们正在查找的输入数据。

然后,我们用 filter_input()
函数来净化输入数据。

在下面的例子中,输入变量 “url” 被传到
PHP 页面:

<?php
if(!filter_has_var(INPUT_POST, "url"))
 {
 echo("Input type does not exist");
 }
else
 {
 $url = filter_input(INPUT_POST, "url", FILTER_SANITIZE_URL);
 }
?>

例子解释:

上面的例子有一个通过 “POST”
方法传送的输入变量 (url):

  1. 检测是否存在 “POST” 类型的 “url”
    输入变量
  2. 如果存在此输入变量,对其进行净化(删除非法字符),并将其存储在
    $url 变量中

假如输入变量类似这样:”
$url 变量应该是这样的:

http://www.W3School.com.cn/

过滤多个输入

表单通常由多个输入字段组成。为了避免对
filter_var 或 filter_input 重复调用,我们可以使用 filter_var_array
或 the filter_input_array 函数。

在本例中,我们使用
filter_input_array() 函数来过滤三个 GET 变量。接收到的 GET
变量是一个名字、一个年龄以及一个邮件地址:

<?php
$filters = array
 (
 "name" => array
  (
  "filter"=>FILTER_SANITIZE_STRING
  ),
 "age" => array
  (
  "filter"=>FILTER_VALIDATE_INT,
  "options"=>array
   (
   "min_range"=>1,
   "max_range"=>120
   )
  ),
 "email"=> FILTER_VALIDATE_EMAIL,
 );

$result = filter_input_array(INPUT_GET, $filters);

if (!$result["age"])
 {
 echo("Age must be a number between 1 and 120.<br />");
 }
elseif(!$result["email"])
 {
 echo("E-Mail is not valid.<br />");
 }
else
 {
 echo("User input is valid");
 }
?>

例子解释:

上面的例子有三个通过 “GET”
方法传送的输入变量 (name, age and email)

  1. 设置一个数组,其中包含了输入变量的名称,以及用于指定的输入变量的过滤器
  2. 调用 filter_input_array
    函数,参数包括 GET 输入变量及刚才设置的数组
  3. 检测 $result 变量中的 “age” 和
    “email” 变量是否有非法的输入。(如果存在非法输入,)

filter_input_array()
函数的第二个参数可以是数组或单一过滤器的 ID。

如果该参数是单一过滤器的
ID,那么这个指定的过滤器会过滤输入数组中所有的值。

如果该参数是一个数组,那么此数组必须遵循下面的规则:

  • 必须是一个关联数组,其中包含的输入变量是数组的键(比如
    “age” 输入变量)
  • 此数组的值必须是过滤器的 ID
    ,或者是规定了过滤器、标志以及选项的数组

使用 Filter Callback

通过使用 FILTER_CALLBACK
过滤器,可以调用自定义的函数,把它作为一个过滤器来使用。这样,我们就拥有了数据过滤的完全控制权。

您可以创建自己的自定义函数,也可以使用已有的
PHP 函数。

规定您准备用到过滤器函数的方法,与规定选项的方法相同。

在下面的例子中,我们使用了一个自定义的函数把所有
“_” 转换为空格:

<?php
function convertSpace($string)
{
return str_replace("_", " ", $string);
}

$string = "Peter_is_a_great_guy!";

echo filter_var($string, FILTER_CALLBACK, array("options"=>"convertSpace"));
?>

以上代码的结果是这样的:

Peter is a great guy!

例子解释:

上面的例子把所有 “_”
转换成空格:

  1. 创建一个把 “_”
    替换为空格的函数
  2. 调用 filter_var() 函数,它的参数是
    FILTER_CALLBACK 过滤器以及包含我们的函数的数组

发表评论

电子邮件地址不会被公开。 必填项已用*标注