PHP基础知识学习笔记

一、PHP 超级全局变量

超级全局变量在PHP 4.1.0之后被启用, 是PHP系统中自带的变量,在一个脚本的全部作用域中都可用。


PHP 超级全局变量

PHP中预定义了几个超级全局变量(superglobals) ,这意味着它们在一个脚本的全部作用域中都可用。 你不需要特别说明,就可以在函数及类中使用。

PHP 超级全局变量列表:

    - $GLOBALS
    - $_SERVER
    - $_REQUEST
    - $_POST
    - $_GET
    - $_FILES
    - $_ENV
    - $_COOKIE
    - $_SESSION

本章节我们将讲解几个常用的超级全局变量,其余变量我们在接下来几个章节会介绍到。


PHP $GLOBALS

$GLOBALS 是PHP的一个超级全局变量组,在一个PHP脚本的全部作用域中都可以访问。

$GLOBALS 是一个包含了全部变量的全局组合数组。变量的名字就是数组的键。

以下实例介绍了如何使用超级全局变量 $GLOBALS:

实例
<?php 
$x = 75; 
$y = 25;
 
function addition() 

$GLOBALS['z'] = $GLOBALS['x'] + $GLOBALS['y']; 
}
 
addition(); 
echo $z; 
?>

运行实例 »

以上实例中 z 是一个$GLOBALS数组中的超级全局变量,该变量同样可以在函数外访问。


PHP $_SERVER

$_SERVER 是一个包含了诸如头信息(header)、路径(path)、以及脚本位置(script locations)等等信息的数组。这个数组中的项目由 Web 服务器创建。不能保证每个服务器都提供全部项目;服务器可能会忽略一些,或者提供一些没有在这里列举出来的项目。

以下实例中展示了如何使用$_SERVER中的元素:

实例
<?php 
echo $_SERVER['PHP_SELF'];
echo "<br>";
echo $_SERVER['SERVER_NAME'];
echo "<br>";
echo $_SERVER['HTTP_HOST'];
echo "<br>";
echo $_SERVER['HTTP_REFERER'];
echo "<br>";
echo $_SERVER['HTTP_USER_AGENT'];
echo "<br>";
echo $_SERVER['SCRIPT_NAME'];
?>

运行实例 »

下表列出了所有 $_SERVER 变量中的重要元素:

元素/代码 描述
$_SERVER['PHP_SELF'] 当前执行脚本的文件名,与 document root 有关。例如,在地址为 http://example.com/test.php/foo.bar 的脚本中使用 $_SERVER['PHP_SELF'] 将得到 /test.php/foo.bar。__FILE__ 常量包含当前(例如包含)文件的完整路径和文件名。 从 PHP 4.3.0 版本开始,如果 PHP 以命令行模式运行,这个变量将包含脚本名。之前的版本该变量不可用。
$_SERVER['GATEWAY_INTERFACE'] 服务器使用的 CGI 规范的版本;例如,"CGI/1.1"。
$_SERVER['SERVER_ADDR'] 当前运行脚本所在的服务器的 IP 地址。
$_SERVER['SERVER_NAME'] 当前运行脚本所在的服务器的主机名。如果脚本运行于虚拟主机中,该名称是由那个虚拟主机所设置的值决定。(如: www.runoob.com)
$_SERVER['SERVER_SOFTWARE'] 服务器标识字符串,在响应请求时的头信息中给出。 (如:Apache/2.2.24)
$_SERVER['SERVER_PROTOCOL'] 请求页面时通信协议的名称和版本。例如,"HTTP/1.0"。
$_SERVER['REQUEST_METHOD'] 访问页面使用的请求方法;例如,"GET", "HEAD","POST","PUT"。
$_SERVER['REQUEST_TIME'] 请求开始时的时间戳。从 PHP 5.1.0 起可用。 (如:1377687496)
$_SERVER['QUERY_STRING'] query string(查询字符串),如果有的话,通过它进行页面访问。
$_SERVER['HTTP_ACCEPT'] 当前请求头中 Accept: 项的内容,如果存在的话。
$_SERVER['HTTP_ACCEPT_CHARSET'] 当前请求头中 Accept-Charset: 项的内容,如果存在的话。例如:"iso-8859-1,*,utf-8"。
$_SERVER['HTTP_HOST'] 当前请求头中 Host: 项的内容,如果存在的话。
$_SERVER['HTTP_REFERER'] 引导用户代理到当前页的前一页的地址(如果存在)。由 user agent 设置决定。并不是所有的用户代理都会设置该项,有的还提供了修改 HTTP_REFERER 的功能。简言之,该值并不可信。)
$_SERVER['HTTPS'] 如果脚本是通过 HTTPS 协议被访问,则被设为一个非空的值。
$_SERVER['REMOTE_ADDR'] 浏览当前页面的用户的 IP 地址。
$_SERVER['REMOTE_HOST'] 浏览当前页面的用户的主机名。DNS 反向解析不依赖于用户的 REMOTE_ADDR。
$_SERVER['REMOTE_PORT'] 用户机器上连接到 Web 服务器所使用的端口号。
$_SERVER['SCRIPT_FILENAME'] 当前执行脚本的绝对路径。
$_SERVER['SERVER_ADMIN'] 该值指明了 Apache 服务器配置文件中的 SERVER_ADMIN 参数。如果脚本运行在一个虚拟主机上,则该值是那个虚拟主机的值。(如:someone@runoob.com)
$_SERVER['SERVER_PORT'] Web 服务器使用的端口。默认值为 "80"。如果使用 SSL 安全连接,则这个值为用户设置的 HTTP 端口。
$_SERVER['SERVER_SIGNATURE'] 包含了服务器版本和虚拟主机名的字符串。
$_SERVER['PATH_TRANSLATED'] 当前脚本所在文件系统(非文档根目录)的基本路径。这是在服务器进行虚拟到真实路径的映像后的结果。
$_SERVER['SCRIPT_NAME'] 包含当前脚本的路径。这在页面需要指向自己时非常有用。__FILE__ 常量包含当前脚本(例如包含文件)的完整路径和文件名。
$_SERVER['SCRIPT_URI'] URI 用来指定要访问的页面。例如 "/index.html"。


PHP $_REQUEST

PHP $_REQUEST 用于收集HTML表单提交的数据。

以下实例显示了一个输入字段(input)及提交按钮(submit)的表单(form)。 当用户通过点击 "Submit" 按钮提交表单数据时, 表单数据将发送至<form>标签中 action 属性中指定的脚本文件。 在这个实例中,我们指定文件来处理表单数据。如果你希望其他的PHP文件来处理该数据,你可以修改该指定的脚本文件名。 然后,我们可以使用超级全局变量 $_REQUEST 来收集表单中的 input 字段数据:

实例
<html>
<body>

<form method="post" action="<?php echo $_SERVER['PHP_SELF'];?>">
Name: <input type="text" name="fname">
<input type="submit">
</form>

<?php 
$name = $_REQUEST['fname']; 
echo $name; 
?>

</body>
</html>

运行实例 »


PHP $_POST

PHP $_POST 被广泛应用于收集表单数据,在HTML form标签的指定该属性:"method="post"。

以下实例显示了一个输入字段(input)及提交按钮(submit)的表单(form)。 当用户通过点击 "Submit" 按钮提交表单数据时, 表单数据将发送至<form>标签中 action 属性中指定的脚本文件。 在这个实例中,我们指定文件来处理表单数据。如果你希望其他的PHP文件来处理该数据,你可以修改该指定的脚本文件名。 然后,我们可以使用超级全局变量 $_POST 来收集表单中的 input 字段数据:

实例
<html>
<body>

<form method="post" action="<?php echo $_SERVER['PHP_SELF'];?>">
Name: <input type="text" name="fname">
<input type="submit">
</form>

<?php 
$name = $_POST['fname']; 
echo $name; 
?>

</body>
</html>

运行实例 »


PHP $_GET

PHP $_GET 同样被广泛应用于收集表单数据,在HTML form标签的指定该属性:"method="get"。

$_GET 也可以收集URL中发送的数据。

假定我们有一个包含参数的超链接HTML页面:

<html>
<body>

<a href="test_get.php?subject=PHP&web=runoob.com">Test $GET</a>

</body>
</html>

当用户点击链接 "Test $GET", 参数 "subject" 和 "web" 将发送至"test_get.php",你可以在 "test_get.php" 文件中使用 $_GET 变量来获取这些数据。

以下实例显示了 "test_get.php" 文件的代码:

实例
<html>
<body>

<?php 
echo "Study " . $_GET['subject'] . " at " . $_GET['web'];
?>

</body>
</html>

二、PHP 魔术变量

PHP 向它运行的任何脚本提供了大量的预定义常量。

不过很多常量都是由不同的扩展库定义的,只有在加载了这些扩展库时才会出现,或者动态加载后,或者在编译时已经包括进去了。

有八个魔术常量它们的值随着它们在代码中的位置改变而改变。

例如 __LINE__ 的值就依赖于它在脚本中所处的行来决定。这些特殊的常量不区分大小写,如下:


__LINE__

文件中的当前行号。

实例:

<?php
echo '这是第 “ '  . __LINE__ . ' ” 行';
?>

以上实例输出结果为:

这是第 “ 2 ” 行

__FILE__

文件的完整路径和文件名。如果用在被包含文件中,则返回被包含的文件名。

自 PHP 4.0.2 起,__FILE__ 总是包含一个绝对路径(如果是符号连接,则是解析后的绝对路径),而在此之前的版本有时会包含一个相对路径。

实例:

<?php
echo '该文件位于 “ '  . __FILE__ . ' ” ';
?>

以上实例输出结果为:

该文件位于 “ E:\wamp\www\test\index.php ”

__DIR__

文件所在的目录。如果用在被包括文件中,则返回被包括的文件所在的目录。

它等价于 dirname(__FILE__)。除非是根目录,否则目录中名不包括末尾的斜杠。(PHP 5.3.0中新增)

实例:

<?php
echo '该文件位于 “ '  . __DIR__ . ' ” ';
?>

以上实例输出结果为:

该文件位于 “ E:\wamp\www\test ”

__FUNCTION__

函数名称(PHP 4.3.0 新加)。自 PHP 5 起本常量返回该函数被定义时的名字(区分大小写)。在 PHP 4 中该值总是小写字母的。

实例:

<?php
function test() {
	echo  '函数名为:' . __FUNCTION__ ;
}
test();
?>

以上实例输出结果为:

函数名为:test

__CLASS__

类的名称(PHP 4.3.0 新加)。自 PHP 5 起本常量返回该类被定义时的名字(区分大小写)。

在 PHP 4 中该值总是小写字母的。类名包括其被声明的作用区域(例如 Foo\Bar)。注意自 PHP 5.4 起 __CLASS__ 对 trait 也起作用。当用在 trait 方法中时,__CLASS__ 是调用 trait 方法的类的名字。

实例:

<?php
<?php
class test {
	function _print() {
		echo '类名为:'  . __CLASS__ . "<br>";
		echo  '函数名为:' . __FUNCTION__ ;
	}
}
$t = new test();
$t->_print();
?>

以上实例输出结果为:

类名为:test
函数名为:_print

__TRAIT__

Trait 的名字(PHP 5.4.0 新加)。自 PHP 5.4.0 起,PHP 实现了代码复用的一个方法,称为 traits。

Trait 名包括其被声明的作用区域(例如 Foo\Bar)。

从基类继承的成员被插入的 SayWorld Trait 中的 MyHelloWorld 方法所覆盖。其行为 MyHelloWorld 类中定义的方法一致。优先顺序是当前类中的方法会覆盖 trait 方法,而 trait 方法又覆盖了基类中的方法。

<?php
class Base {
    public function sayHello() {
        echo 'Hello ';
    }
}

trait SayWorld {
    public function sayHello() {
        parent::sayHello();
        echo 'World!';
    }
}

class MyHelloWorld extends Base {
    use SayWorld;
}

$o = new MyHelloWorld();
$o->sayHello();
?>

以上例程会输出:

Hello World!

__METHOD__

类的方法名(PHP 5.0.0 新加)。返回该方法被定义时的名字(区分大小写)。

实例:

<?php
function test() {
	echo  '函数名为:' . __METHOD__ ;
}
test();
?>

以上实例输出结果为:

函数名为:test

__NAMESPACE__

当前命名空间的名称(区分大小写)。此常量是在编译时定义的(PHP 5.3.0 新增)。

实例:

<?php
namespace MyProject;

echo '命名空间为:"', __NAMESPACE__, '"'; // 输出 "MyProject"
?>

以上实例输出结果为:

命名空间为:"MyProject"

三、PHP 命名空间(namespace)

PHP 命名空间(namespace)是在PHP 5.3中加入的,如果你学过C#和Java,那命名空间就不算什么新事物。 不过在PHP当中还是有着相当重要的意义。

PHP 命名空间可以解决以下两类问题:

    1. 用户编写的代码与PHP内部的类/函数/常量或第三方类/函数/常量之间的名字冲突。
    2. 为很长的标识符名称(通常是为了缓解第一类问题而定义的)创建一个别名(或简短)的名称,提高源代码的可读性。
定义命名空间

默认情况下,所有常量、类和函数名都放在全局空间下,就和PHP支持命名空间之前一样。

命名空间通过关键字namespace 来声明。如果一个文件中包含命名空间,它必须在其它所有代码之前声明命名空间。语法格式如下;

< ?php  
// 定义代码在 'MyProject' 命名空间中  
namespace MyProject;  
 
// ... 代码 ...  

你也可以在同一个文件中定义不同的命名空间代码,如:

< ?php  
namespace MyProject1;  
// MyProject1 命名空间中的PHP代码  
 
namespace MyProject2;  
// MyProject2 命名空间中的PHP代码    
 
// 另一种语法
namespace MyProject3 {  
 // MyProject3 命名空间中的PHP代码    
}  
?>

在声明命名空间之前唯一合法的代码是用于定义源文件编码方式的 declare 语句。所有非 PHP 代码包括空白符都不能出现在命名空间的声明之前。

<?php
declare(encoding='UTF-8');//定义多个命名空间和不包含在命名空间中的代码namespaceMyProject{const CONNECT_OK =1;classConnection{/* ... */}function connect(){/* ... */}}namespace{// 全局代码
session_start();
$a =MyProject\connect();
echo MyProject\Connection::start();}?>

以下代码会出现语法错误:

<html><?php
namespaceMyProject;// 命名空间前出现了“<html>” 会致命错误 - 命名空间必须是程序脚本的第一条语句?>

子命名空间

与目录和文件的关系很象,PHP 命名空间也允许指定层次化的命名空间的名称。因此,命名空间的名字可以使用分层次的方式定义:

<?php
namespaceMyProject\Sub\Level;//声明分层次的单个命名空间const CONNECT_OK =1;classConnection{/* ... */}functionConnect(){/* ... */}?>

上面的例子创建了常量 MyProject\Sub\Level\CONNECT_OK,类 MyProject\Sub\Level\Connection 和函数 MyProject\Sub\Level\Connect。


命名空间使用

PHP 命名空间中的类名可以通过三种方式引用:

    1. 非限定名称,或不包含前缀的类名称,例如 $a=new foo(); 或 foo::staticmethod();。如果当前命名空间是 currentnamespace,foo 将被解析为 currentnamespace\foo。如果使用 foo 的代码是全局的,不包含在任何命名空间中的代码,则 foo 会被解析为foo。 警告:如果命名空间中的函数或常量未定义,则该非限定的函数名称或常量名称会被解析为全局函数名称或常量名称。
    2. 限定名称,或包含前缀的名称,例如 $a = new subnamespace\foo(); 或 subnamespace\foo::staticmethod();。如果当前的命名空间是 currentnamespace,则 foo 会被解析为 currentnamespace\subnamespace\foo。如果使用 foo 的代码是全局的,不包含在任何命名空间中的代码,foo 会被解析为subnamespace\foo。
    3. 完全限定名称,或包含了全局前缀操作符的名称,例如, $a = new \currentnamespace\foo(); 或 \currentnamespace\foo::staticmethod();。在这种情况下,foo 总是被解析为代码中的文字名(literal name)currentnamespace\foo。

下面是一个使用这三种方式的实例:

file1.php 文件代码

<?php
namespaceFoo\Bar\subnamespace;const FOO =1;function foo(){}class foo
{staticfunction staticmethod(){}}?>

file2.php 文件代码

<?php
namespaceFoo\Bar;
include 'file1.php';const FOO =2;function foo(){}class foo
{staticfunction staticmethod(){}}/* 非限定名称 */
foo();// 解析为 Foo\Bar\foo resolves to function Foo\Bar\foo
foo::staticmethod();// 解析为类 Foo\Bar\foo的静态方法staticmethod。resolves to class Foo\Bar\foo, method staticmethod
echo FOO;// resolves to constant Foo\Bar\FOO/* 限定名称 */
subnamespace\foo();// 解析为函数 Foo\Bar\subnamespace\foo
subnamespace\foo::staticmethod();// 解析为类 Foo\Bar\subnamespace\foo,// 以及类的方法 staticmethod
echo subnamespace\FOO;// 解析为常量 Foo\Bar\subnamespace\FOO/* 完全限定名称 */
\Foo\Bar\foo();// 解析为函数 Foo\Bar\foo
\Foo\Bar\foo::staticmethod();// 解析为类 Foo\Bar\foo, 以及类的方法 staticmethod
echo \Foo\Bar\FOO;// 解析为常量 Foo\Bar\FOO?>

注意访问任意全局类、函数或常量,都可以使用完全限定名称,例如 \strlen() 或 \Exception 或 \INI_ALL。

在命名空间内部访问全局类、函数和常量:

<?php
namespaceFoo;function strlen(){}const INI_ALL =3;classException{}

$a = \strlen('hi');// 调用全局函数strlen
$b = \INI_ALL;// 访问全局常量 INI_ALL
$c =new \Exception('error');// 实例化全局类 Exception?>

命名空间和动态语言特征

PHP 命名空间的实现受到其语言自身的动态特征的影响。因此,如果要将下面的代码转换到命名空间中,动态访问元素。

example1.php 文件代码:

<?php
class classname
{function __construct(){
        echo __METHOD__,"\n";}}function funcname(){
    echo __FUNCTION__,"\n";}const constname ="global";

$a ='classname';
$obj =new $a;// prints classname::__construct
$b ='funcname';
$b();// prints funcname
echo constant('constname'),"\n";// prints global?>

必须使用完全限定名称(包括命名空间前缀的类名称)。注意因为在动态的类名称、函数名称或常量名称中,限定名称和完全限定名称没有区别,因此其前导的反斜杠是不必要的。

动态访问命名空间的元素

<?php
namespace namespacename;class classname
{function __construct(){
        echo __METHOD__,"\n";}}function funcname(){
    echo __FUNCTION__,"\n";}const constname ="namespaced";

include 'example1.php';

$a ='classname';
$obj =new $a;// prints classname::__construct
$b ='funcname';
$b();// prints funcname
echo constant('constname'),"\n";// prints global/* note that if using double quotes, "\\namespacename\\classname" must be used */
$a ='\namespacename\classname';
$obj =new $a;// prints namespacename\classname::__construct
$a ='namespacename\classname';
$obj =new $a;// also prints namespacename\classname::__construct
$b ='namespacename\funcname';
$b();// prints namespacename\funcname
$b ='\namespacename\funcname';
$b();// also prints namespacename\funcname
echo constant('\namespacename\constname'),"\n";// prints namespaced
echo constant('namespacename\constname'),"\n";// also prints namespaced?>

namespace关键字和__NAMESPACE__常量

PHP支持两种抽象的访问当前命名空间内部元素的方法,__NAMESPACE__ 魔术常量和namespace关键字。

常量__NAMESPACE__的值是包含当前命名空间名称的字符串。在全局的,不包括在任何命名空间中的代码,它包含一个空的字符串。

__NAMESPACE__ 示例, 在命名空间中的代码

<?php
namespaceMyProject;

echo '"', __NAMESPACE__,'"';// 输出 "MyProject"?>

__NAMESPACE__ 示例,全局代码

<?php

echo '"', __NAMESPACE__,'"';// 输出 ""?>

常量 __NAMESPACE__ 在动态创建名称时很有用,例如:

使用__NAMESPACE__动态创建名称

<?php
namespaceMyProject;functionget($classname){
    $a = __NAMESPACE__ .'\\'. $classname;returnnew $a;}?>

关键字 namespace 可用来显式访问当前命名空间或子命名空间中的元素。它等价于类中的 self 操作符。

amespace操作符,命名空间中的代码

<?php
namespaceMyProject;use blah\blah as mine;// see "Using namespaces: importing/aliasing"

blah\mine();// calls function blah\blah\mine()namespace\blah\mine();// calls function MyProject\blah\mine()namespace\func();// calls function MyProject\func()namespace\sub\func();// calls function MyProject\sub\func()namespace\cname::method();// calls static method "method" of class MyProject\cname
$a =newnamespace\sub\cname();// instantiates object of class MyProject\sub\cname
$b =namespace\CONSTANT;// assigns value of constant MyProject\CONSTANT to $b?>

namespace操作符, 全局代码

<?php

namespace\func();// calls function func()namespace\sub\func();// calls function sub\func()namespace\cname::method();// calls static method "method" of class cname
$a =newnamespace\sub\cname();// instantiates object of class sub\cname
$b =namespace\CONSTANT;// assigns value of constant CONSTANT to $b?>

使用命名空间:别名/导入

PHP 命名空间支持 有两种使用别名或导入方式:为类名称使用别名,或为命名空间名称使用别名。注意PHP不支持导入函数或常量。

在PHP中,别名是通过操作符 use 来实现的. 下面是一个使用所有可能的三种导入方式的例子:

1、使用use操作符导入/使用别名

<?php
namespace foo;useMy\Full\Classname asAnother;// 下面的例子与 use My\Full\NSname as NSname 相同useMy\Full\NSname;// 导入一个全局类use \ArrayObject;

$obj =newnamespace\Another;// 实例化 foo\Another 对象
$obj =newAnother;// 实例化 My\Full\Classname 对象NSname\subns\func();// 调用函数 My\Full\NSname\subns\func
$a =newArrayObject(array(1));// 实例化 ArrayObject 对象// 如果不使用 "use \ArrayObject" ,则实例化一个 foo\ArrayObject 对象?>

2、 一行中包含多个use语句

<?php
useMy\Full\Classname asAnother,My\Full\NSname;

$obj =newAnother;// 实例化 My\Full\Classname 对象NSname\subns\func();// 调用函数 My\Full\NSname\subns\func?>

导入操作是在编译执行的,但动态的类名称、函数名称或常量名称则不是。

3、导入和动态名称

<?php
useMy\Full\Classname asAnother,My\Full\NSname;

$obj =newAnother;// 实例化一个 My\Full\Classname 对象
$a ='Another';
$obj =new $a;// 实际化一个 Another 对象?>

另外,导入操作只影响非限定名称和限定名称。完全限定名称由于是确定的,故不受导入的影响。

4、导入和完全限定名称

<?php
useMy\Full\Classname asAnother,My\Full\NSname;

$obj =newAnother;// instantiates object of class My\Full\Classname
$obj =new \Another;// instantiates object of class Another
$obj =newAnother\thing;// instantiates object of class My\Full\Classname\thing
$obj =new \Another\thing;// instantiates object of class Another\thing?>

使用命名空间:后备全局函数/常量

在一个命名空间中,当 PHP 遇到一个非限定的类、函数或常量名称时,它使用不同的优先策略来解析该名称。类名称总是解析到当前命名空间中的名称。因此在访问系统内部或不包含在命名空间中的类名称时,必须使用完全限定名称,例如:

1、在命名空间中访问全局类

<?php
namespace A\B\C;classExceptionextends \Exception {}

$a =newException('hi');// $a 是类 A\B\C\Exception 的一个对象
$b =new \Exception('hi');// $b 是类 Exception 的一个对象

$c =newArrayObject;// 致命错误, 找不到 A\B\C\ArrayObject 类?>

对于函数和常量来说,如果当前命名空间中不存在该函数或常量,PHP 会退而使用全局空间中的函数或常量。

2、 命名空间中后备的全局函数/常量

<?php
namespace A\B\C;const E_ERROR =45;function strlen($str){return \strlen($str)-1;}

echo E_ERROR,"\n";// 输出 "45"
echo INI_ALL,"\n";// 输出 "7" - 使用全局常量 INI_ALL

echo strlen('hi'),"\n";// 输出 "1"if(is_array('hi')){// 输出 "is not array"
    echo "is array\n";}else{
    echo "is not array\n";}?>

全局空间

如果没有定义任何命名空间,所有的类与函数的定义都是在全局空间,与 PHP 引入命名空间概念前一样。在名称前加上前缀 \ 表示该名称是全局空间中的名称,即使该名称位于其它的命名空间中时也是如此。

使用全局空间说明

<?php
namespace A\B\C;/* 这个函数是 A\B\C\fopen */function fopen(){/* ... */
     $f = \fopen(...);// 调用全局的fopen函数return $f;}?>

命名空间的顺序

自从有了命名空间之后,最容易出错的该是使用类的时候,这个类的寻找路径是什么样的了。

<?php
namespace A;use B\D, C\E as F;// 函数调用

foo();// 首先尝试调用定义在命名空间"A"中的函数foo()// 再尝试调用全局函数 "foo"

\foo();// 调用全局空间函数 "foo" my\foo();// 调用定义在命名空间"A\my"中函数 "foo" 

F();// 首先尝试调用定义在命名空间"A"中的函数 "F" // 再尝试调用全局函数 "F"// 类引用new B();// 创建命名空间 "A" 中定义的类 "B" 的一个对象// 如果未找到,则尝试自动装载类 "A\B"new D();// 使用导入规则,创建命名空间 "B" 中定义的类 "D" 的一个对象// 如果未找到,则尝试自动装载类 "B\D"new F();// 使用导入规则,创建命名空间 "C" 中定义的类 "E" 的一个对象// 如果未找到,则尝试自动装载类 "C\E"new \B();// 创建定义在全局空间中的类 "B" 的一个对象// 如果未发现,则尝试自动装载类 "B"new \D();// 创建定义在全局空间中的类 "D" 的一个对象// 如果未发现,则尝试自动装载类 "D"new \F();// 创建定义在全局空间中的类 "F" 的一个对象// 如果未发现,则尝试自动装载类 "F"// 调用另一个命名空间中的静态方法或命名空间函数

B\foo();// 调用命名空间 "A\B" 中函数 "foo"

B::foo();// 调用命名空间 "A" 中定义的类 "B" 的 "foo" 方法// 如果未找到类 "A\B" ,则尝试自动装载类 "A\B"

D::foo();// 使用导入规则,调用命名空间 "B" 中定义的类 "D" 的 "foo" 方法// 如果类 "B\D" 未找到,则尝试自动装载类 "B\D"

\B\foo();// 调用命名空间 "B" 中的函数 "foo" 

\B::foo();// 调用全局空间中的类 "B" 的 "foo" 方法// 如果类 "B" 未找到,则尝试自动装载类 "B"// 当前命名空间中的静态方法或函数

A\B::foo();// 调用命名空间 "A\A" 中定义的类 "B" 的 "foo" 方法// 如果类 "A\A\B" 未找到,则尝试自动装载类 "A\A\B"

\A\B::foo();// 调用命名空间 "A\B" 中定义的类 "B" 的 "foo" 方法// 如果类 "A\B" 未找到,则尝试自动装载类 "A\B"?>

名称解析遵循下列规则:

    1. 对完全限定名称的函数,类和常量的调用在编译时解析。例如 new \A\B 解析为类 A\B。
    2. 所有的非限定名称和限定名称(非完全限定名称)根据当前的导入规则在编译时进行转换。例如,如果命名空间 A\B\C 被导入为 C,那么对 C\D\e() 的调用就会被转换为 A\B\C\D\e()。
    3. 在命名空间内部,所有的没有根据导入规则转换的限定名称均会在其前面加上当前的命名空间名称。例如,在命名空间 A\B 内部调用C\D\e(),则 C\D\e() 会被转换为 A\B\C\D\e() 。
    4. 非限定类名根据当前的导入规则在编译时转换(用全名代替短的导入名称)。例如,如果命名空间 A\B\C 导入为C,则 new C() 被转换为 new A\B\C() 。
    5. 在命名空间内部(例如A\B),对非限定名称的函数调用是在运行时解析的。例如对函数 foo() 的调用是这样解析的:
        1. 在当前命名空间中查找名为 A\B\foo() 的函数
        2. 尝试查找并调用 全局(global) 空间中的函数 foo()。
    6. 在命名空间(例如A\B)内部对非限定名称或限定名称类(非完全限定名称)的调用是在运行时解析的。下面是调用 new C() 及 new D\E() 的解析过程: new C()的解析:
        1. 在当前命名空间中查找A\B\C类。
        2. 尝试自动装载类A\B\C。
new D\E()的解析:
        1. 在类名称前面加上当前命名空间名称变成:A\B\D\E,然后查找该类。
        2. 尝试自动装载类 A\B\D\E。
为了引用全局命名空间中的全局类,必须使用完全限定名称 new \C()。
四、PHP 表单验证

本章节我们将介绍如何使用PHP验证客户端提交的表单数据。


PHP 表单验证
Note 在处理PHP表单时我们需要考虑安全性。

本章节我们将展示PHP表单数据安全处理,为了防止黑客及垃圾信息我们需要对表单进行数据安全验证。

在本章节介绍的HTML表单中包含以下输入字段: 必须与可选文本字段,单选按钮,及提交按钮:

查看代码 »

<!DOCTYPE HTML> 
<html>
<head>
<meta charset="utf-8">
<title>菜鸟教程(runoob.com)</title>
<style>
.error {color: #FF0000;}
</style>
</head>
<body> 

<?php
// 定义变量并默认设置为空值
$nameErr = $emailErr = $genderErr = $websiteErr = "";
$name = $email = $gender = $comment = $website = "";

if ($_SERVER["REQUEST_METHOD"] == "POST")
{
    if (empty($_POST["name"]))
    {
        $nameErr = "名字是必需的";
    }
    else
    {
        $name = test_input($_POST["name"]);
        // 检测名字是否只包含字母跟空格
        if (!preg_match("/^[a-zA-Z ]*$/",$name))
        {
            $nameErr = "只允许字母和空格"; 
        }
    }
    
    if (empty($_POST["email"]))
    {
      $emailErr = "邮箱是必需的";
    }
    else
    {
        $email = test_input($_POST["email"]);
        // 检测邮箱是否合法
        if (!preg_match("/([\w\-]+\@[\w\-]+\.[\w\-]+)/",$email))
        {
            $emailErr = "非法邮箱格式"; 
        }
    }
    
    if (empty($_POST["website"]))
    {
        $website = "";
    }
    else
    {
        $website = test_input($_POST["website"]);
        // 检测 URL 地址是否合法
        if (!preg_match("/\b(?:(?:https?|ftp):\/\/|www\.)[-a-z0-9+&@#\/%?=~_|!:,.;]*[-a-z0-9+&@#\/%=~_|]/i",$website))
        {
            $websiteErr = "非法的 URL 的地址"; 
        }
    }
    
    if (empty($_POST["comment"]))
    {
        $comment = "";
    }
    else
    {
        $comment = test_input($_POST["comment"]);
    }
    
    if (empty($_POST["gender"]))
    {
        $genderErr = "性别是必需的";
    }
    else
    {
        $gender = test_input($_POST["gender"]);
    }
}

function test_input($data)
{
    $data = trim($data);
    $data = stripslashes($data);
    $data = htmlspecialchars($data);
    return $data;
}
?>

<h2>PHP 表单验证实例</h2>
<p><span class="error">* 必需字段。</span></p>
<form method="post" action="<?php echo htmlspecialchars($_SERVER["PHP_SELF"]);?>"> 
   名字: <input type="text" name="name" value="<?php echo $name;?>">
   <span class="error">* <?php echo $nameErr;?></span>
   <br><br>
   E-mail: <input type="text" name="email" value="<?php echo $email;?>">
   <span class="error">* <?php echo $emailErr;?></span>
   <br><br>
   网址: <input type="text" name="website" value="<?php echo $website;?>">
   <span class="error"><?php echo $websiteErr;?></span>
   <br><br>
   备注: <textarea name="comment" rows="5" cols="40"><?php echo $comment;?></textarea>
   <br><br>
   性别:
   <input type="radio" name="gender" <?php if (isset($gender) && $gender=="female") echo "checked";?>  value="female">女
   <input type="radio" name="gender" <?php if (isset($gender) && $gender=="male") echo "checked";?>  value="male">男
   <span class="error">* <?php echo $genderErr;?></span>
   <br><br>
   <input type="submit" name="submit" value="Submit"> 
</form>

<?php
echo "<h2>您输入的内容是:</h2>";
echo $name;
echo "<br>";
echo $email;
echo "<br>";
echo $website;
echo "<br>";
echo $comment;
echo "<br>";
echo $gender;
?>

</body>
</html>

PHP 表单验证实例

* 必需字段。

名字:  * 

E-mail:  * 

网址:  

备注:  

性别: 女 男 * 

您输入的内容是:




上述表单验证规则如下:

字段 验证规则
名字 必须。 +只能包含字母和空格
E-mail 必须。 + 必须是一个有效的电子邮件地址(包含'@'和'.')
网址 必须。如果存在,它必须包含一个有效的URL
备注 必须。 多行输入字段(文本域)
性别 必须。 必须选择一个

首先让我们先看看纯HTML的表单代码:


文本字段

"名字", "E-mail", 及"网址"字段为文本输入元素,"备注"字段是 textarea。HTML代码如下所示:

“名字”:<input type="text" name="name">
E-mail:<input type="text" name="email">网址:<input type="text" name="website">备注:<textarea name="comment" rows="5" cols="40"></textarea>

单选按钮

"性别"字段是单选按钮,HTML代码如下所示:

性别:<input type="radio" name="gender" value="female">女<input type="radio" name="gender" value="male">男

表单元素

HTML 表单代码如下所示:

<form method="post" action="<?php echo htmlspecialchars($_SERVER["PHP_SELF"]);?>">

该表单使用 method="post" 方法来提交数据。

Note 什么是 $_SERVER["PHP_SELF"] 变量?

$_SERVER["PHP_SELF"]是超级全局变量,返回当前正在执行脚本的文件名,与 document root相关。

所以, $_SERVER["PHP_SELF"] 会发送表单数据到当前页面,而不是跳转到不同的页面。

Note 什么是 htmlspecialchars()方法?

htmlspecialchars() 函数把一些预定义的字符转换为 HTML 实体。

预定义的字符是:

    - & (和号) 成为 &amp;
    - " (双引号) 成为 &quot;
    - ' (单引号) 成为 &#039;
    - < (小于) 成为 &lt;
    - > (大于) 成为 &gt;


PHP表单中需引起注重的地方?

$_SERVER["PHP_SELF"] 变量有可能会被黑客使用!

当黑客使用跨网站脚本的HTTP链接来攻击时,$_SERVER["PHP_SELF"]服务器变量也会被植入脚本。原因就是跨网站脚本是附在执行文件的路径后面的,因此$_SERVER["PHP_SELF"]的字符串就会包含HTTP链接后面的JavaScript程序代码。

Note XSS又叫 CSS (Cross-Site Script) ,跨站脚本攻击。恶意攻击者往Web页面里插入恶意html代码,当用户浏览该页之时,嵌入其中Web里面的html代码会被执行,从而达到恶意用户的特殊目的。

指定以下表单文件名为 "test_form.php":

<form method="post" action="<?php echo $_SERVER["PHP_SELF"];?>">

现在,我们使用URL来指定提交地址 "test_form.php",以上代码修改为如下所示:

<formmethod="post"action="test_form.php">

这样做就很好了。

但是,考虑到用户会在浏览器地址栏中输入以下地址:

http://www.runoob.com/test_form.php/%22%3E%3Cscript%3Ealert('hacked')%3C/script%3E

以上的 URL 中,将被解析为如下代码并执行:

<formmethod="post"action="test_form.php/"><script>alert('hacked')</script>

代码中添加了 script 标签,并添加了alert命令。 当页面载入时会执行该Javascript代码(用户会看到弹出框)。 这仅仅只是一个简单的实例来说明PHP_SELF变量会被黑客利用。

请注意, 任何JavaScript代码可以添加在<script>标签中! 黑客可以利用这点重定向页面到另外一台服务器的页面上,页面 代码文件中可以保护恶意代码,代码可以修改全局变量或者获取用户的表单数据。


如何避免 $_SERVER["PHP_SELF"] 被利用?

$_SERVER["PHP_SELF"] 可以通过 htmlspecialchars() 函数来避免被利用。

form 代码如下所示:

<form method="post" action="<?php echo htmlspecialchars($_SERVER["PHP_SELF"]);?>">

htmlspecialchars() 把一些预定义的字符转换为 HTML 实体。现在如果用户想利用 PHP_SELF 变量, 结果将输出如下所示:

<formmethod="post"action="test_form.php/&quot;&gt;&lt;script&gt;alert('hacked')&lt;/script&gt;">

尝试该漏洞失败!


使用 PHP 验证表单数据

首先我们对用户所有提交的数据都通过 PHP 的 htmlspecialchars() 函数处理。

当我们使用 htmlspecialchars() 函数时,在用户尝试提交以下文本域:

<script>location.href('http://www.runoob.com')</script>

该代码将不会被执行,因为它会被保存为HTML转义代码,如下所示:

&lt;script&gt;location.href('http://www.runoob.com')&lt;/script&gt;

以上代码是安全的,可以正常在页面显示或者插入邮件中。

当用户提交表单时,我们将做以下两件事情,:

    1. 使用 PHP trim() 函数去除用户输入数据中不必要的字符 (如:空格,tab,换行)。
    2. 使用PHP stripslashes()函数去除用户输入数据中的反斜杠 (\)

接下来让我们将这些过滤的函数写在一个我们自己定义的函数中,这样可以大大提高代码的复用性。

将函数命名为 test_input()。

现在,我们可以通过test_input()函数来检测 $_POST 中的所有变量, 脚本代码如下所示:

实例
<?php
// 定义变量并默认设置为空值
$name = $email = $gender = $comment = $website = "";

if ($_SERVER["REQUEST_METHOD"] == "POST")
{
  $name = test_input($_POST["name"]);
  $email = test_input($_POST["email"]);
  $website = test_input($_POST["website"]);
  $comment = test_input($_POST["comment"]);
  $gender = test_input($_POST["gender"]);
}

function test_input($data)
{
  $data = trim($data);
  $data = stripslashes($data);
  $data = htmlspecialchars($data);
  return $data;
}
?>

运行实例 »

注意我们在执行以上脚本时,会通过$_SERVER["REQUEST_METHOD"]来检测表单是否被提交 。如果 REQUEST_METHOD 是 POST, 表单将被提交 - 数据将被验证。如果表单未提交将跳过验证并显示空白。

在以上实例中使用输入项都是可选的,即使用户不输入任何数据也可以正常显示。

在接下来的章节中我们将介绍如何对用户输入的数据进行验证。


五、get和post变量


PHP $_GET 变量

在 PHP 中,预定义的 $_GET 变量用于收集来自 method="get" 的表单中的值。


$_GET 变量

预定义的 $_GET 变量用于收集来自 method="get" 的表单中的值。

从带有 GET 方法的表单发送的信息,对任何人都是可见的(会显示在浏览器的地址栏),并且对发送信息的量也有限制。

实例

form.html 文件代码如下:

<html>
<head>
<meta charset="utf-8">
<title>菜鸟教程(runoob.com)</title>
</head>
<body>

<form action="welcome.php" method="get">
名字: <input type="text" name="fname">
年龄: <input type="text" name="age">
<input type="submit" value="提交">
</form>

</body>
</html>

当用户点击 "Submit" 按钮时,发送到服务器的 URL 如下所示:

http://www.runoob.com/welcome.php?fname=Runoob&age=3

"welcome.php" 文件现在可以通过 $_GET 变量来收集表单数据了(请注意,表单域的名称会自动成为 $_GET 数组中的键):

欢迎 <?php echo $_GET["fname"]; ?>!<br>
你的年龄是 <?php echo $_GET["age"]; ?>  岁。

以上表单执行演示:


何时使用 method="get"?

在 HTML 表单中使用 method="get" 时,所有的变量名和值都会显示在 URL 中。

注释:所以在发送密码或其他敏感信息时,不应该使用这个方法!

然而,正因为变量显示在 URL 中,因此可以在收藏夹中收藏该页面。在某些情况下,这是很有用的。

注释:HTTP GET 方法不适合大型的变量值。它的值是不能超过 2000 个字符的。



PHP $_POST 变量

在 PHP 中,预定义的 $_POST 变量用于收集来自 method="post" 的表单中的值。


$_POST 变量

预定义的 $_POST 变量用于收集来自 method="post" 的表单中的值。

从带有 POST 方法的表单发送的信息,对任何人都是不可见的(不会显示在浏览器的地址栏),并且对发送信息的量也没有限制。

注释:然而,默认情况下,POST 方法的发送信息的量最大值为 8 MB(可通过设置 php.ini 文件中的 post_max_size 进行更改)。

实例

form.html 文件代码如下:

<html><head><metacharset="utf-8"><title>菜鸟教程(runoob.com)</title></head><body><formaction="welcome.php"method="post">
名字: <inputtype="text"name="fname">
年龄: <inputtype="text"name="age"><inputtype="submit"value="提交"></form></body></html>

当用户点击 "提交" 按钮时,URL 类似如下所示:

http://www.runoob.com/welcome.php

"welcome.php" 文件现在可以通过 $_POST 变量来收集表单数据了(请注意,表单域的名称会自动成为 $_POST 数组中的键):

欢迎<?php echo $_POST["fname"];?>!<br>你的年龄是<?php echo $_POST["age"];?>岁。

通过浏览器访问演示如下:



何时使用 method="post"?

从带有 POST 方法的表单发送的信息,对任何人都是不可见的,并且对发送信息的量也没有限制。

然而,由于变量不显示在 URL 中,所以无法把页面加入书签。


PHP $_REQUEST 变量

预定义的 $_REQUEST 变量包含了 $_GET、$_POST 和 $_COOKIE 的内容。

$_REQUEST 变量可用来收集通过 GET 和 POST 方法发送的表单数据。

实例

你可以将 "welcome.php" 文件修改为如下代码,它可以接受 $_GET、$_POST等数据。

欢迎<?php echo $_REQUEST["fname"];?>!<br>你的年龄是<?php echo $_REQUEST["age"];?>岁。

六、PHP date() 函数


PHP date() 函数用于格式化时间/日期。


PHP date() 函数

PHP date() 函数可把时间戳格式化为可读性更好的日期和时间。

Tip时间戳是一个字符序列,表示一定的事件发生的日期/时间。

语法
string date ( string $format [, int $timestamp ] )

参数 描述
format 必需。规定时间戳的格式。
timestamp 可选。规定时间戳。默认是当前的日期和时间。


PHP Date() - 格式化日期

date() 函数的第一个必需参数 format 规定了如何格式化日期/时间。

这里列出了一些可用的字符:

    - d - 代表月中的天 (01 - 31)
    - m - 代表月 (01 - 12)
    - Y - 代表年 (四位数)

如需了解 format 参数中可用的所有字符列表,请查阅我们的 PHP Date 参考手册,date() 函数。

可以在字母之间插入其他字符,比如 "/"、"." 或者 "-",这样就可以增加附加格式了:

<?php
echo date("Y/m/d") . "<br>";
echo date("Y.m.d") . "<br>";
echo date("Y-m-d");
?>

上面代码的输出如下所示:

2016/05/11<br>
2016.05.11<br>
2016-05-11

格式字串可以识别以下 format 参数的字符串

format字符 说明 返回值例子
--- ---
d 月份中的第几天,有前导零的 2 位数字 01 到 31
D 星期中的第几天,文本表示,3 个字母 Mon 到 Sun
j 月份中的第几天,没有前导零 1 到 31
l("L"的小写字母) 星期几,完整的文本格式 Sunday 到 Saturday
N ISO-8601 格式数字表示的星期中的第几天(PHP 5.1.0 新加) 1(表示星期一)到 7(表示星期天)
S 每月天数后面的英文后缀,2 个字符 st,nd,rd 或者 th。可以和 j 一起用
w 星期中的第几天,数字表示 0(表示星期天)到 6(表示星期六)
z 年份中的第几天 0 到 365
星期 --- ---
W ISO-8601 格式年份中的第几周,每周从星期一开始(PHP 4.1.0 新加的) 例如:42(当年的第 42 周)
--- ---
F 月份,完整的文本格式,例如 January 或者 March January 到 December
m 数字表示的月份,有前导零 01 到 12
M 三个字母缩写表示的月份 Jan 到 Dec
n 数字表示的月份,没有前导零 1 到 12
t 给定月份所应有的天数 28 到 31
--- ---
L 是否为闰年 如果是闰年为 1,否则为 0
o ISO-8601 格式年份数字。这和 Y 的值相同,只除了如果 ISO 的星期数(W)属于前一年或下一年,则用那一年。(PHP 5.1.0 新加) Examples: 1999 or 2003
Y 4 位数字完整表示的年份 例如:1999 或 2003
y 2 位数字表示的年份 例如:99 或 03
时间 --- ---
a 小写的上午和下午值 am 或 pm
A 大写的上午和下午值 AM 或 PM
B Swatch Internet 标准时 000 到 999
g 小时,12 小时格式,没有前导零 1 到 12
G 小时,24 小时格式,没有前导零 0 到 23
h 小时,12 小时格式,有前导零 01 到 12
H 小时,24 小时格式,有前导零 00 到 23
i 有前导零的分钟数 00 到 59>
s 秒数,有前导零 00 到 59>
u 毫秒 (PHP 5.2.2 新加)。需要注意的是 date() 函数总是返回000000 因为它只接受 integer 参数, 而 DateTime::format()才支持毫秒。 示例: 654321
时区 --- ---
e 时区标识(PHP 5.1.0 新加) 例如:UTC,GMT,Atlantic/Azores
I 是否为夏令时 如果是夏令时为 1,否则为 0
O 与格林威治时间相差的小时数 例如:+0200
P 与格林威治时间(GMT)的差别,小时和分钟之间有冒号分隔(PHP 5.1.3 新加) 例如:+02:00
T 本机所在的时区 例如:EST,MDT(【译者注】在 Windows 下为完整文本格式,例如"Eastern Standard Time",中文版会显示"中国标准时间")。
Z 时差偏移量的秒数。UTC 西边的时区偏移量总是负的,UTC 东边的时区偏移量总是正的。 -43200 到 43200
完整的日期/时间 --- ---
c ISO 8601 格式的日期(PHP 5 新加) 2004-02-12T15:19:21+00:00
r RFC 822 格式的日期 例如:Thu, 21 Dec 2000 16:01:07 +0200
U 从 Unix 纪元(January 1 1970 00:00:00 GMT)开始至今的秒数 参见 time()

七、PHP 包含文件


PHP include 和 require 语句

在 PHP 中,您可以在服务器执行 PHP 文件之前在该文件中插入一个文件的内容。

include 和 require 语句用于在执行流中插入写在其他文件中的有用的代码。

include 和 require 除了处理错误的方式不同之外,在其他方面都是相同的:

    - require 生成一个致命错误(E_COMPILE_ERROR),在错误发生后脚本会停止执行。
    - include 生成一个警告(E_WARNING),在错误发生后脚本会继续执行。

因此,如果您希望继续执行,并向用户输出结果,即使包含文件已丢失,那么请使用 include。否则,在框架、CMS 或者复杂的 PHP 应用程序编程中,请始终使用 require 向执行流引用关键文件。这有助于提高应用程序的安全性和完整性,在某个关键文件意外丢失的情况下。

包含文件省去了大量的工作。这意味着您可以为所有网页创建标准页头、页脚或者菜单文件。然后,在页头需要更新时,您只需更新这个页头包含文件即可。

语法
include 'filename';或者require'filename';

PHP include 和 require 语句
基础实例

假设您有一个标准的页头文件,名为 "header.php"。如需在页面中引用这个页头文件,请使用 include/require:

<html><head><metacharset="utf-8"><title>菜鸟教程(runoob.com)</title></head><body><?php include 'header.php';?><h1>欢迎来到我的主页!</h1><p>一些文本。</p></body></html>
实例 2

假设我们有一个在所有页面中使用的标准菜单文件。

"menu.php":

echo '<a href="/">主页</a>
<a href="/html">HTML 教程</a>
<a href="/php">PHP 教程</a>';

网站中的所有页面均应引用该菜单文件。以下是具体的做法:

<html><head><metacharset="utf-8"><title>菜鸟教程(runoob.com)</title></head><body><divclass="leftmenu"><?php include 'menu.php';?></div><h1>欢迎来到我的主页!</h1><p>一些文本。</p></body></html>
实例 3

假设我们有一个定义变量的包含文件("vars.php"):

<?php
$color='red';
$car='BMW';?>

这些变量可用在调用文件中:

<html><head><metacharset="utf-8"><title>菜鸟教程(runoob.com)</title></head><body><h1>欢迎来到我的主页!</h1><?php 
include 'vars.php';
echo "I have a $color $car";// I have a red BMW?></body></html>


八、PHP Cookie
  
  

cookie 常用于识别用户。


Cookie 是什么?

cookie 常用于识别用户。cookie 是一种服务器留在用户计算机上的小文件。每当同一台计算机通过浏览器请求页面时,这台计算机将会发送 cookie。通过 PHP,您能够创建并取回 cookie 的值。


如何创建 Cookie?

setcookie() 函数用于设置 cookie。

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

语法
setcookie(name, value, expire, path, domain);
实例 1

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

<?php
setcookie("user","runoob", time()+3600);?><html>
.....

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

实例 2

您还可以通过另一种方式设置 cookie 的过期时间。这也许比使用秒表示的方式简单。

<?php
$expire=time()+60*60*24*30;
setcookie("user","runoob", $expire);?><html>
.....

在上面的实例中,过期时间被设置为一个月(60 秒 * 60 分 * 24 小时 * 30 天)。


如何取回 Cookie 的值?

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

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

<?php
// 输出 cookie 值
echo $_COOKIE["user"];// 查看所有 cookie
print_r($_COOKIE);?>

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

<html><head><metacharset="utf-8"><title>菜鸟教程(runoob.com)</title></head><body><?php
if(isset($_COOKIE["user"]))
	echo "欢迎 ". $_COOKIE["user"]."!<br>";else
	echo "普通访客!<br>";?></body></html>


如何删除 Cookie?

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

删除的实例:

<?php
// 设置 cookie 过期时间为过去 1 小时
setcookie("user","", time()-3600);?>


如果浏览器不支持 Cookie 该怎么办?

如果您的应用程序需要与不支持 cookie 的浏览器打交道,那么您不得不使用其他的办法在您的应用程序中的页面之间传递信息。一种方式是通过表单传递数据(有关表单和用户输入的内容,在本教程的前面章节中我们已经介绍过了)。

下面的表单在用户单点击 "Submit" 按钮时,向 "welcome.php" 提交了用户输入:

<html><head><metacharset="utf-8"><title>菜鸟教程(runoob.com)</title></head><body><formaction="welcome.php"method="post">
名字: <inputtype="text"name="name">
年龄: <inputtype="text"name="age"><inputtype="submit"></form></body></html>

取回 "welcome.php" 文件中的值,如下所示:

<html><head><metacharset="utf-8"><title>菜鸟教程(runoob.com)</title></head><body>

欢迎 <?php echo $_POST["name"];?>.<br>
你 <?php echo $_POST["age"];?> 岁了。

</body></html>
九、PHP Session
   
   

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


PHP Session 变量

您在计算机上操作某个应用程序时,您打开它,做些更改,然后关闭它。这很像一次对话(Session)。计算机知道您是谁。它清楚您在何时打开和关闭应用程序。然而,在因特网上问题出现了:由于 HTTP 地址无法保持状态,Web 服务器并不知道您是谁以及您做了什么。

PHP session 解决了这个问题,它通过在服务器上存储用户信息以便随后使用(比如用户名称、购买商品等)。然而,会话信息是临时的,在用户离开网站后将被删除。如果您需要永久存储信息,可以把数据存储在数据库中。

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


开始 PHP Session

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

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

<?php session_start(); ?>

<html>
<body>

</body>
</html>

上面的代码会向服务器注册用户的会话,以便您可以开始保存用户信息,同时会为用户会话分配一个 UID。


存储 Session 变量

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

<?php
session_start();
// 存储 session 数据
$_SESSION['views']=1;
?>

<html>
<head>
<meta charset="utf-8">
<title>菜鸟教程(runoob.com)</title>
</head>
<body>

<?php
// 检索 session 数据
echo "浏览量:". $_SESSION['views'];
?>

</body>
</html>

输出:

浏览量:1

在下面的实例中,我们创建了一个简单的 page-view 计数器。isset() 函数检测是否已设置 "views" 变量。如果已设置 "views" 变量,我们累加计数器。如果 "views" 不存在,则创建 "views" 变量,并把它设置为 1:

<?php
session_start();

if(isset($_SESSION['views']))
{
	$_SESSION['views']=$_SESSION['views']+1;
}
else
{
	$_SESSION['views']=1;
}
echo "浏览量:". $_SESSION['views'];
?>


销毁 Session

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

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

<?php
session_start();
if(isset($_SESSION['views']))
{
	unset($_SESSION['views']);
}
?>

您也可以通过调用 session_destroy() 函数彻底销毁 session:

<?php
session_destroy();
?>

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

十、PHP 发送电子邮件
   
   

PHP 允许您从脚本直接发送电子邮件。


PHP mail() 函数

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

语法

mail(to,subject,message,headers,parameters)
参数描述
to必需。规定 email 接收者。
subject必需。规定 email 的主题。注释:该参数不能包含任何新行字符。
message必需。定义要发送的消息。应使用 LF (\n) 来分隔各行。每行应该限制在 70 个字符内。
headers可选。规定附加的标题,比如 From、Cc 和 Bcc。应当使用 CRLF (\r\n) 分隔附加的标题。
parameters可选。对邮件发送程序规定额外的参数。

注释:PHP 运行邮件函数需要一个已安装且正在运行的邮件系统(如:sendmail、postfix、qmail等)。所用的程序通过在 php.ini 文件中的配置设置进行定义。请在我们的 PHP Mail 参考手册 阅读更多内容。


PHP 简易 E-Mail

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

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

<?php
$to ="someone@example.com";// 邮件接收者
$subject ="参数邮件";// 邮件标题
$message ="Hello! 这是邮件的内容。";// 邮件正文
$from ="someonelse@example.com";// 邮件发送者
$headers ="From:". $from;// 头部信息设置
mail($to,$subject,$message,$headers);
echo "邮件已发送";?>


PHP Mail 表单

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

<html><head><metacharset="utf-8"><title>菜鸟教程(runoob.com)</title></head><body><?php
if(isset($_REQUEST['email'])){// 如果接收到邮箱参数则发送邮件// 发送邮件
	$email = $_REQUEST['email'];
	$subject = $_REQUEST['subject'];
	$message = $_REQUEST['message'];
	mail("someone@example.com", $subject,
	$message,"From:". $email);
	echo "邮件发送成功";}else{// 如果没有邮箱参数则显示表单
	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>

实例解释:
    - 首先,检查是否填写了邮件输入框
    - 如果未填写(比如在页面被首次访问时),输出 HTML 表单
    - 如果已填写(在表单被填写后),从表单发送电子邮件
    - 当填写完表单点击提交按钮后,页面重新载入,可以看到邮件输入被重置,同时显示邮件发送成功的消息

注释:这个简易发送 e-mail 不安全,在本教程的下一章中,您将阅读到更多关于电子邮件脚本中的安全隐患,我们将为您讲解如何验证用户输入使它更安全。


十一、PHP Secure E-mails
   
   

在上一节中的 PHP e-mail 脚本中,存在着一个漏洞。


PHP E-mail 注入

首先,请看上一章中的 PHP 代码:

<html>
<head>
<meta charset="utf-8">
<title>菜鸟教程(runoob.com)</title>
</head>
<body>

<?php
if (isset($_REQUEST['email'])) { // 如果接收到邮箱参数则发送邮件
	// 发送邮件
	$email = $_REQUEST['email'] ;
	$subject = $_REQUEST['subject'] ;
	$message = $_REQUEST['message'] ;
	mail("someone@example.com", $subject,
	$message, "From:" . $email);
	echo "邮件发送成功";
} else { // 如果没有邮箱参数则显示表单
	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>

以上代码存在的问题是,未经授权的用户可通过输入表单在邮件头部插入数据。

假如用户在表单中的输入框内加入如下文本到电子邮件中,会出现什么情况呢?

someone@example.com%0ACc:person2@example.com
%0ABcc:person3@example.com,person3@example.com,
anotherperson4@example.com,person5@example.com
%0ABTo:person6@example.com

与往常一样,mail() 函数把上面的文本放入邮件头部,那么现在头部有了额外的 Cc:、Bcc: 和 To: 字段。当用户点击提交按钮时,这封 e-mail 会被发送到上面所有的地址!


PHP 防止 E-mail 注入

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

下面的代码与上一章中的类似,不过这里我们已经增加了检测表单中 email 字段的输入验证程序:

<html>
<head>
<meta charset="utf-8">
<title>菜鸟教程(runoob.com)</title>
</head>
<body>
<?php
function spamcheck($field)
{
	// filter_var() 过滤 e-mail
	// 使用 FILTER_SANITIZE_EMAIL
	$field=filter_var($field, FILTER_SANITIZE_EMAIL);

	//filter_var() 过滤 e-mail
	// 使用 FILTER_VALIDATE_EMAIL
	if(filter_var($field, FILTER_VALIDATE_EMAIL))
	{
		return TRUE;
	}
	else
	{
		return FALSE;
	}
}

if (isset($_REQUEST['email']))
{
	// 如果接收到邮箱参数则发送邮件

	// 判断邮箱是否合法
	$mailcheck = spamcheck($_REQUEST['email']);
	if ($mailcheck==FALSE)
	{
		echo "非法输入";
	}
	else
	{	
		// 发送邮件
		$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
{ 
	// 如果没有邮箱参数则显示表单
	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 Filter 中阅读更多关于过滤器的知识。


十二、PHP 错误处理


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


PHP 错误处理

在创建脚本和 Web 应用程序时,错误处理是一个重要的部分。如果您的代码缺少错误检测编码,那么程序看上去很不专业,也为安全风险敞开了大门。

本教程介绍了 PHP 中一些最为重要的错误检测方法。

我们将为您讲解不同的错误处理方法:

  • 简单的 "die()" 语句
  • 自定义错误和错误触发器
  • 错误报告

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

第一个实例展示了一个打开文本文件的简单脚本:

<?php
$file=fopen("welcome.txt","r");?>

如果文件不存在,您会得到类似这样的错误:

Warning: fopen(welcome.txt)[function.fopen]: failed to open stream:No such file or directory in/www/runoob/test/test.php on line 2

为了避免用户得到类似上面的错误消息,我们在访问文件之前检测该文件是否存在:

<?php
if(!file_exists("welcome.txt")){die("文件不存在");}else{
	$file=fopen("welcome.txt","r");}?>

现在,如果文件不存在,您会得到类似这样的错误消息:

文件不存在

相比之前的代码,上面的代码更有效,这是由于它采用了一个简单的错误处理机制在错误之后终止了脚本。

然而,简单地终止脚本并不总是恰当的方式。让我们研究一下用于处理错误的备选的 PHP 函数。


创建自定义错误处理器

创建一个自定义的错误处理器非常简单。我们很简单地创建了一个专用函数,可以在 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可选。规定一个数组,包含了当错误发生时在用的每个变量以及它们的值。

错误报告级别

这些错误报告级别是用户自定义的错误处理程序处理的不同类型的错误:

常量描述
2E_WARNING非致命的 run-time 错误。不暂停脚本执行。
8E_NOTICErun-time 通知。在脚本发现可能有错误时发生,但也可能在脚本正常运行时发生。
256E_USER_ERROR致命的用户生成的错误。这类似于程序员使用 PHP 函数 trigger_error() 设置的 E_ERROR。
512E_USER_WARNING非致命的用户生成的警告。这类似于程序员使用 PHP 函数 trigger_error() 设置的 E_WARNING。
1024E_USER_NOTICE用户生成的通知。这类似于程序员使用 PHP 函数 trigger_error() 设置的 E_NOTICE。
4096E_RECOVERABLE_ERROR可捕获的致命错误。类似 E_ERROR,但可被用户定义的处理程序捕获。(参见 set_error_handler())
8191E_ALL所有错误和警告。(在 PHP 5.4 中,E_STRICT 成为 E_ALL 的一部分)

现在,让我们创建一个处理错误的函数:

function customError($errno, $errstr){
	echo "<b>Error:</b> [$errno] $errstr<br>";
	echo "脚本结束";die();}

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

现在,我们已经创建了一个错误处理函数,我们需要确定在何时触发该函数。


设置错误处理程序

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

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

set_error_handler("customError");

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

实例

通过尝试输出不存在的变量,来测试这个错误处理程序:

<?php
// 错误处理函数function customError($errno, $errstr){
	echo "<b>Error:</b> [$errno] $errstr";}// 设置错误处理函数
set_error_handler("customError");// 触发错误
echo($test);?>

以上代码的输出如下所示:

Error:[8]Undefined variable: test


触发错误

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

实例

在本例中,如果 "test" 变量大于 "1",就会发生错误:

<?php
$test=2;if($test>1){
	trigger_error("变量值必须小于等于 1");}?>

以上代码的输出如下所示:

Notice:变量值必须小于等于1in/www/test/runoob.php on line 5

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

可能的错误类型:

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

实例

在本例中,如果 "test" 变量大于 "1",则发生 E_USER_WARNING 错误。如果发生了 E_USER_WARNING,我们将使用我们自定义的错误处理程序并结束脚本:

<?php
// 错误处理函数function customError($errno, $errstr){
	echo "<b>Error:</b> [$errno] $errstr<br>";
	echo "脚本结束";die();}// 设置错误处理函数
set_error_handler("customError",E_USER_WARNING);// 触发错误
$test=2;if($test>1){
	trigger_error("变量值必须小于等于 1",E_USER_WARNING);}?>

以上代码的输出如下所示:

Error:[512]变量值必须小于等于1脚本结束

现在,我们已经学习了如何创建自己的 error,以及如何触发它们,接下来我们研究一下错误记录。


错误记录

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

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

通过 E-Mail 发送错误消息

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

<?php
// 错误处理函数function customError($errno, $errstr){
	echo "<b>Error:</b> [$errno] $errstr<br>";
	echo "已通知网站管理员";
	error_log("Error: [$errno] $errstr",1,"someone@example.com","From: webmaster@example.com");}// 设置错误处理函数
set_error_handler("customError",E_USER_WARNING);// 触发错误
$test=2;if($test>1){
	trigger_error("变量值必须小于等于 1",E_USER_WARNING);}?>

以上代码的输出如下所示:

Error:[512]变量值必须小于等于1已通知网站管理员

接收自以上代码的邮件如下所示:

Error:[512]变量值必须小于等于1

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

十三、PHP 异常处理


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


异常是什么

PHP 5 提供了一种新的面向对象的错误处理方法。

异常处理用于在指定的错误(异常)情况发生时改变脚本的正常流程。这种情况称为异常。

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

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

我们将展示不同的错误处理方法:

  • 异常的基本使用
  • 创建自定义的异常处理器
  • 多个异常
  • 重新抛出异常
  • 设置顶层异常处理器

注释:异常应该仅仅在错误情况下使用,而不应该用于在一个指定的点跳转到代码的另一个位置。


异常的基本使用

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

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

让我们尝试抛出一个异常,同时不去捕获它:

<?php
// 创建一个有异常处理的函数
function checkNum($number)
{
	if($number>1)
	{
		throw new Exception("Value must be 1 or below");
	}
	return true;
}

// 触发异常
checkNum(2);
?>

上面的代码会得到类似这样的一个错误:

Fatal error: Uncaught exception 'Exception' with message 'Value must be 1 or below' in /www/runoob/test/test.php:7 Stack trace: #0 /www/runoob/test/test.php(13): checkNum(2) #1 {main} thrown in /www/runoob/test/test.php on line 7

Try、throw 和 catch

要避免上面实例中出现的错误,我们需要创建适当的代码来处理异常。

适当的处理异常代码应该包括:

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

让我们触发一个异常:

<?php
// 创建一个有异常处理的函数
function checkNum($number)
{
	if($number>1)
	{
		throw new Exception("变量值必须小于等于 1");
	}
		return true;
}
	
// 在 try 块 触发异常
try
{
	checkNum(2);
	// 如果抛出异常,以下文本不会输出
	echo '如果输出该内容,说明 $number 变量';
}
// 捕获异常
catch(Exception $e)
{
	echo 'Message: ' .$e->getMessage();
}
?>

上面代码将得到类似这样一个错误:

Message: 变量值必须小于等于 1

实例解释:

上面的代码抛出了一个异常,并捕获了它:

  1. 创建 checkNum() 函数。它检测数字是否大于 1。如果是,则抛出一个异常。
  2. 在 "try" 代码块中调用 checkNum() 函数。
  3. checkNum() 函数中的异常被抛出。
  4. "catch" 代码块接收到该异常,并创建一个包含异常信息的对象 ($e)。
  5. 通过从这个 exception 对象调用 $e->getMessage(),输出来自该异常的错误消息。

然而,为了遵循 "每个 throw 必须对应一个 catch" 的原则,可以设置一个顶层的异常处理器来处理漏掉的错误。


创建一个自定义的 Exception 类

创建自定义的异常处理程序非常简单。我们简单地创建了一个专门的类,当 PHP 中发生异常时,可调用其函数。该类必须是 exception 类的一个扩展。

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

我们开始创建 exception 类:

<?php
class customException extends Exception
{
	public function errorMessage()
	{
		// 错误信息
		$errorMsg = '错误行号 '.$this->getLine().' in '.$this->getFile()
		.': <b>'.$this->getMessage().'</b> 不是一个合法的 E-Mail 地址';
		return $errorMsg;
	}
}

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

try
{
	// 检测邮箱
	if(filter_var($email, FILTER_VALIDATE_EMAIL) === FALSE)
	{
		// 如果是个不合法的邮箱地址,抛出异常
		throw new customException($email);
	}
}

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

这个新的类是旧的 exception 类的副本,外加 errorMessage() 函数。正因为它是旧类的副本,因此它从旧类继承了属性和方法,我们可以使用 exception 类的方法,比如 getLine()、getFile() 和 getMessage()。

实例解释:

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

  1. customException() 类是作为旧的 exception 类的一个扩展来创建的。这样它就继承了旧的 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()
	{
		// 错误信息
		$errorMsg = '错误行号 '.$this->getLine().' in '.$this->getFile()
		.': <b>'.$this->getMessage().'</b> 不是一个合法的 E-Mail 地址';
		return $errorMsg;
	}
}

$email = "someone@example.com";

try
{
	// 检测邮箱
	if(filter_var($email, FILTER_VALIDATE_EMAIL) === FALSE)
	{
		// 如果是个不合法的邮箱地址,抛出异常
		throw new customException($email);
	}
	// 检测 "example" 是否在邮箱地址中
	if(strpos($email, "example") !== FALSE)
	{
		throw new Exception("$email 是 example 邮箱");
	}
}
catch (customException $e)
{
	echo $e->errorMessage();
}
catch(Exception $e)
{
	echo $e->getMessage();
}
?>

实例解释:

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

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

如果 customException 类抛出了异常,但没有捕获 customException,仅仅捕获了 base exception,则在那里处理异常。


重新抛出异常

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

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

<?php
class customException extends Exception
{
	public function errorMessage()
	{
		// 错误信息
		$errorMsg = $this->getMessage().' 不是一个合法的 E-Mail 地址。';
		return $errorMsg;
	}
}

$email = "someone@example.com";

try
{
	try
	{
		// 检测 "example" 是否在邮箱地址中
		if(strpos($email, "example") !== FALSE)
		{
			// 如果是个不合法的邮箱地址,抛出异常
			throw new Exception($email);
		}
	}
	catch(Exception $e)
	{
		// 重新抛出异常
		throw new customException($email);
	}
}
catch (customException $e)
{
	// 显示自定义信息
	echo $e->errorMessage();
}
?>

实例解释:

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

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

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


设置顶层异常处理器

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 代码块中抛出(再次抛出)异常。

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


十四、PHP 过滤器


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


什么是 PHP 过滤器?

PHP 过滤器用于验证和过滤来自非安全来源的数据。

测试、验证和过滤用户输入或自定义数据是任何 Web 应用程序的重要组成部分。

PHP 的过滤器扩展的设计目的是使数据过滤更轻松快捷。


为什么使用过滤器?

几乎所有的 Web 应用程序都依赖外部的输入。这些数据通常来自用户或其他应用程序(比如 web 服务)。通过使用过滤器,您能够确保应用程序获得正确的输入类型。

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

输入过滤是最重要的应用程序安全课题之一。

什么是外部数据?

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

函数和过滤器

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

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

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

<?php
$int = 123;

if(!filter_var($int, FILTER_VALIDATE_INT))
{
	echo("不是一个合法的整数");
}
else
{
	echo("是个合法的整数");
}
?>

上面的代码使用了 "FILTER_VALIDATE_INT" 过滤器来过滤变量。由于这个整数是合法的,因此上面的代码将输出:

如果我们尝试使用一个非整数的变量(比如 "123abc"),则将输出:"Integer is not valid"。

如需查看完整的函数和过滤器列表,请访问我们的 PHP Filter 参考手册


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("不是一个合法的整数");
}
else
{
	echo("是个合法的整数");
}
?>

就像上面的代码一样,选项必须放入一个名为 "options" 的相关数组中。如果使用标志,则不需在数组内。

由于整数是 "300",它不在指定的范围内,以上代码的输出将是:

不是一个合法的整数

如需查看完整的函数和过滤器列表,请访问我们的 PHP Filter 参考手册。您可以看到每个过滤器的可用选项和标志。


验证输入

让我们试着验证来自表单的输入。

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

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

在下面的实例中,输入变量 "email" 被传到 PHP 页面:

<?php
if(!filter_has_var(INPUT_GET, "email"))
{
	echo("没有 email 参数");
}
else
{
	if (!filter_input(INPUT_GET, "email", FILTER_VALIDATE_EMAIL))
	{
		echo "不是一个合法的 E-Mail";
	}
	else
	{
		echo "是一个合法的 E-Mail";
	}
}
?>

以上实例测试结果如下:

实例解释

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

  1. 检测是否存在 "GET" 类型的 "email" 输入变量
  2. 如果存在输入变量,检测它是否是有效的 e-mail 地址

净化输入

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

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

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

在下面的实例中,输入变量 "url" 被传到 PHP 页面:

<?php
if(!filter_has_var(INPUT_GET, "url"))
{
	echo("没有 url 参数");
}
else
{
	$url = filter_input(INPUT_GET, 
	"url", FILTER_SANITIZE_URL);
	echo $url;
}
?>

实例解释

上面的实例有一个通过 "GET" 方法传送的输入变量 (url):

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

假如输入变量是一个类似这样的字符串:"http://www.ruåånoøøob.com/",则净化后的 $url 变量如下所示:



过滤多个输入

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

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

<?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("年龄必须在 1 到 120 之间。<br>");
}
elseif(!$result["email"])
{
	echo("E-Mail 不合法<br>");
}
else
{
	echo("输入正确");
}
?>

实例解释

上面的实例有三个通过 "GET" 方法传送的输入变量 (name、age 和 email):

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

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

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

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

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

使用 Filter Callback

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

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

将您准备用到的过滤器的函数,按指定选项的规定方法进行规定。在关联数组中,带有名称 "options"。

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

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

$string = "www_runoob_com!";

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

上面代码的结果如下所示:

实例解释

上面的实例把所有 "_" 转换成 "." :

  1. 创建一个把 "_" 替换为 "." 的函数
  2. 调用 filter_var() 函数,它的参数是 FILTER_CALLBACK 过滤器以及包含我们的函数的数组
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

fullstack_lth

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值