IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> PHP知识库 -> php学习 -> 正文阅读

[PHP知识库]php学习

PHP 简介

PHP 是服务器端脚本语言。

PHP 是什么?

  • PHP(全称:PHP:Hypertext Preprocessor,即"PHP:超文本预处理器")是一种通用开源脚本语言。
  • PHP 脚本在服务器上执行。
  • PHP 可免费下载使用。

PHP 文件是什么?

  • PHP 文件可包含文本、HTML、JavaScript代码和 PHP 代码
  • PHP 代码在服务器上执行,结果以纯 HTML 形式返回给浏览器
  • PHP 文件的默认文件扩展名是 “.php”

PHP 能做什么?

  • PHP 可以生成动态页面内容
  • PHP 可以创建、打开、读取、写入、关闭服务器上的文件
  • PHP 可以收集表单数据
  • PHP 可以发送和接收 cookies
  • PHP 可以添加、删除、修改您的数据库中的数据
  • PHP 可以限制用户访问您的网站上的一些页面
  • PHP 可以加密数据

通过 PHP,您不再限于输出 HTML。您可以输出图像、PDF 文件,甚至 Flash 电影。您还可以输出任意的文本,比如 XHTML 和 XML。


为什么使用 PHP?

  • PHP 可在不同的平台上运行(Windows、Linux、Unix、Mac OS X 等)
  • PHP 与目前几乎所有的正在被使用的服务器相兼容(Apache、IIS 等)
  • PHP 提供了广泛的数据库支持
  • PHP 是免费的,可从官方的 PHP 资源下载它: www.php.net
  • PHP 易于学习,并可高效地运行在服务器端

PHP基本语法

代码标记

在PHP历史发展中,可以使用多种标记来区分PHP脚本。

ASP标记:<% php代码 %>

短标记:<?php php代码 ?> ,以上两种基本启用,如果要使用需要在配置文件中开启

脚本标记:<scritp language="php">php代码\</script>

标准标记(常用):<?php php代码 ?>

PHP注释

习惯:所有的代码在写的过程中都必须进行注释,对于初学者而言注释就是个人学习和写代码的一个思路说明。

PHP中注释分为两种:行注释和块注释

行注释:一次注释一行

//:后面跟的所有内容都是注释

#:同上

块注释:一次注释一段

/* xxx /:中间直到/出现之前,全部都是注释。

PHP变量

预定义变量

预定义变量:提前定义的变量,系统定义的变量,存储许多需要用到的数据(预定义变量都是数组)

$_GRT:获取所有表单以get方法提交的数据

$_POST:POST提交的数据都会保存在此

$_REQUEST:GET和POST提交的都会保存

$_GLOBALS:PHP中所有全局变量

$_SERVER:服务器信息

$_SESSION:session会话数据

$_COOKIE:cookie 会话数据

$_NEV:环境信息

$_FILES:用户上传的文件信息

PHP的八种数据类型

在PHP中将数据分为三大类八小类:

简单(基本)数据类型:4个小类

整形:int/integer,系统分配4个字节存储,表示整数类型(有前提)

浮点型:float/double,系统分配8个字节存储,表示小数或者整型存不下的整数

字符串型:string,系统根据实际长度分配,表示字符串(引号)

布尔类型:bool/boolean,表示布尔类型,只有两个值:true和false

复合数据类型:2个小类

对象类型:object,存放对象(面向对象)

数组类型:array,存储多个数据(一次性)

特殊数据类型:2个小类

资源类型:resource,存放资源数据(PHP外部数据,如数据库、文件)

空类型:NULL,只有一个值就是NULL(不能运算)

类型转换

类型转换:在很多的条件下,需要指定的数据类型,需要外部数据(当前PHP取得的数据),转换成目标数据类型

在PHP中有两种类型转换方式:

1、自动转换:系统根据需求自己判定,自己转换(用得比较多,效率偏低)

2、强制(手动)转换:认为根据需要的目标类型转换

强制转换规则:在变量之前增加一个括号(),然后在里面写上对应类型:int/integer…其中NULL类型用到unset()

在转换过程中,用得比较多的就是转布尔类型(判断)和转数值类型(算数运算)

其他类型转布尔类型:true或者false,在PHP中比较少类型转换变成false

其他类型转数值的说明:

1、布尔true为1,false为0;

2、字符串转数值有自己的规则

2.1、以字母开头的字符串,永远为0;

2.2、以数字开头的字符串,取到碰到字符串为止(不会同时包含两个小数点 )

$a = 'abc1.1.1';
$b = '1.1.1abc';
//自动转换
echo $a + $b;    //算数+运算,系统先转换为数值类型(整形或浮点型),然后运算
//强制转换
echo (float)$a,(flaot)$b;

类型判断

通过一组类型判断函数,来判断变量,最终返回这个变量所保存数据的数据类型(相同结果为true,不同为false):是一组以is_开头后跟乐星名字的函数:is_XXX(变量名)

Bool类型不能用echo来查看,可以使用var_dump结构来查看

var_dump(变量1,变量2…)

var_dump(is_int($a));    //bool(false)
var_dump(is_string($a));    //bool(true)

还有一组函数可以用来获取以及设定数据(变量)的类型

gettype(变量名):获取类型,得到的是该类型对应的字符串

settype(变量名,类型):设定数据类型:与强制转换不同

1、强制转换(类型)变量名,是对数据值复制的内容进行处理(不会处理实际存储的内容)

2、settype会直接改变数据本身

$a = 'abc1.1.1';
echo (float)$a;
echo gettype($a);        //string,强制转换不会改变数据类型
var_dump(settype($a,'int'));    //bool(true),会改变数据本身
echo gettype($a),$a;        //int,a-->1

字符串转义

转义:在计算机通用协议中,有以下特定的方式定义的字母,系统会特定处理:通常这种方式都是使用反斜杠+字母(单词)的特性:

\r\n:回车换行

PHP在识别转义字符的时候也是使用同样的模式:反斜杠+字母

在PHP中系统常用的转义符号:

\’:在单引号字符串中显示单引号

\”:在双引号字符串中显示双引号

\r:代表回车(理论上是回到当前行的首位置)

\n:代表新一行

\t:类似tab键,输出4个空格

$:在PHP中使用$符号作为变量符号,因此需要特定识别

单引号和双引号的区别:

1、其中单引号中能够识别\’,而双引号中就不能识别\’

//定义字符串识别转义符号
$str1 = 'abc\r\ndef\t\'\"\$fg';
$str2 = "abc\r\ndef\t\'\"\$fg";
echo $str1,'<br/>',$str2;        //abc\r\ndef\t'\"\$fg
                                //abc def \'"$fg

2、双引号总因为能够识别$符号,索引双引号中可以解析变量,而单引号不能

$a = 'hello';
//变量识别
$str1 = 'abc $a def';
$str2 = "abc $a def";
$str3 = 'abc$adef';
$str4 = "abc{$a}def";
echo $str1,'<br/>',$str2;        //abc $a def
                                //abc hello def
echo $str3,'<br/>',$str4;        //abc$adef
                                //abchellodef

双引号中变量识别的规则

1、变量本身系统能够与后面的内容区分:应该保证变量的独立性,不要让系统难以区分

2、使用变量专业标识符(区分),给变量加上一组大括号{}

字符串长度问题

1、基本函数strlen():得到字符串的长度(字节为单位)

//定义字符串
$str1 = 'abcdefj';
$str2 = '你好中国123';            //中文在utf-8字符集中占3个字节
echo strlen($str1),'<br/>',strlen($str2);        //7,15

2、多字节字符串的长度问题:包括中文的长度

3、多自己字符串扩展模块:mbstring扩展(mb:Multi Bytes)

首先要加载PHP的mbstring扩展(在配置文件中)

可以使用mb扩展带来的很多函数

//定义字符串
$str1 = 'abcdefj';
$str2 = '你好中国123';            //中文在utf-8字符集中占3个字节
echo mb_strlen($str1),'<br/>',mb_strlen($str2);        //7,7

mbstring扩展针对的是一些关于字符统计:strlen只是针对标准交换码ASCⅡ,mbstring会针对不同字符集

错误抑制符

在PHP中有一些错误可以提前预知,但是这些错误可能无法避免,但是又不希望报错为用户看,可以使用错误抑制符处理。

@:在可能出错的表达式前面使用“@”符号即可

$a = 10;
$b = 0;
$a / $b//报错
@($a / $b);        //不会报错

错误抑制符通常在生产环境(上线)会用到,在开发的时候不会用:系统本身最好没有任何错误。

PHP global 关键字

global 关键字用于函数内访问全局变量。

在函数内调用函数外定义的全局变量,我们需要在函数中的变量前加上 global 关键字:

<?php
$x=5;
$y=10;
 
function myTest()
{
    global $x,$y;
    $y=$x+$y;
}
 
myTest();
echo $y; // 输出 15
?>

PHP 将所有全局变量存储在一个名为 $GLOBALS[index] 的数组中。 index 保存变量的名称。这个数组可以在函数内部访问,也可以直接用来更新全局变量。

上面的实例可以写成这样:

<?php
$x=5;
$y=10;
 
function myTest()
{
    $GLOBALS['y']=$GLOBALS['x']+$GLOBALS['y'];
} 
 
myTest();
echo $y;
?>

Static 作用域

当一个函数完成时,它的所有变量通常都会被删除。然而,有时候您希望某个局部变量不要被删除。

要做到这一点,请在您第一次声明变量时使用 static 关键字:

<?php
function myTest()
{
    static $x=0;
    echo $x;
    $x++;
    echo PHP_EOL;    // 换行符
}
 
myTest();
myTest();
myTest();
?>

然后,每次调用该函数时,该变量将会保留着函数前一次被调用时的值。

**注释:**该变量仍然是函数的局部变量。

PHP echo 和 print 语句

echo 和 print 区别:

  • echo - 可以输出一个或多个字符串
  • print - 只允许输出一个字符串,返回值总为 1

**提示:**echo 输出的速度比 print 快, echo 没有返回值,print有返回值1。

PHP EOF(heredoc) 使用说明

PHP EOF(heredoc)是一种在命令行shell(如sh、csh、ksh、bash、PowerShell和zsh)和程序语言(像Perl、PHP、Python和Ruby)里定义一个字符串的方法。

使用概述:

  • \1. 必须后接分号,否则编译通不过。
  • \2. EOF 可以用任意其它字符代替,只需保证结束标识与开始标识一致。
  • 3. 结束标识必须顶格独自占一行(即必须从行首开始,前后不能衔接任何空白和字符)。
  • \4. 开始标识可以不带引号或带单双引号,不带引号与带双引号效果一致,解释内嵌的变量和转义符号,带单引号则不解释内嵌的变量和转义符号。
  • \5. 当内容需要内嵌引号(单引号或双引号)时,不需要加转义符,本身对单双引号转义,此处相当与q和qq的用法。
<?php
$name="runoob";
$a= <<<EOF
        "abc"$name
        "123"
EOF;
// 结束需要独立一行且前后不能空格
echo $a;
?>
 
输出:
    "abc"runoob
     "123"
  • 1.PHP 定界符 EOF 的作用就是按照原样,包括换行格式什么的,输出在其内部的东西;
  • 2.在 PHP 定界符 EOF 中的任何特殊字符都不需要转义;
  • 3.PHP 定界符 EOF

PHP 超级全局变量

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

PHP 超级全局变量列表:

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

PHP $GLOBALS

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

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

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

下表列出了所有 $_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” 按钮提交表单数据时, 表单数据将发送至标签中 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” 按钮提交表单数据时, 表单数据将发送至标签中 action 属性中指定的脚本文件。 在这个实例中,我们指定文件来处理表单数据。如果你希望其他的PHP文件来处理该数据,你可以修改该指定的脚本文件名。 然后,我们可以使用超级全局变量 $_POST 来收集表单中的 input 字段数据:

PHP $_GET

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

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

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

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>test</title>
</head>
<body>
<a href="test01.php?subject=php&web=123.qq.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"]."@".$_GET["web"];
?>

</body>
</html>

PHP 魔术常量

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

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

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

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

LINE

文件中的当前行号。

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

FILE

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

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

<html>
<body>

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

</body>
</html>

//输出
//E:\Code\code-phpstorm\TestProj\src\test01.php

DIR

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

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

<html>
<body>

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

</body>
</html>

//输出
//E:\Code\code-phpstorm\TestProj\src

FUNCTION

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

CLASS

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

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

<?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

文件包含

文件包含:在一个PHP脚本中,去将另外一个文件(PHP)包含进来,去合作完成一件事情。

文件包含的作用

文件包含的意义:

1、要么使用被包含文件中的内容,实现代码的共享(重用):向上包含(索要)

向上包含:在当前脚本要用某个代码之前包含别的文件

2、要么自己有东西可以给别的文件使用,实现代码的共享(重用):向下包含(给与)

向下包含:在自己又某个东西的时候,需要别的脚本来显示(自己代码写完之后包含其他文件)

最大的作用:分工写作,每个脚本做的事情不一样,因此可以使用写作方式,让多个脚本共同完成意见事情。

文件包含四种形式

在PHP中文件的包含有四种形式(两种大形式)

include:包含文件

include_once:系统会自动判断文件包含过程中,是否已经包含过(一个文件最多被包含一次)

require:与include相同

require_once:与include_once相同

包含基本语法:

include ‘文件名字’;

include(‘文件名字’); //文件名字:路径问题

//include1.php
<?php
    //被包含文件
    //定义数据
    $a = 1;
    define('PI',3.14);
    ?>
//include2.php
<?php
    //包含文件:使用数据
    //包含文件
    include 'include1.php';        //包含当前文件include2.php所在文件夹下的include1.php
    echo $a,PI;
    ?>

以上方式:是先包含文件,后使用文件中的内容(向上包含)

向下包含:先准备内容,然后包含另外的文件,在另外的文件中,使用当前的内容

//include3.php
<?php
    //定义数据
    $a = 1;
    define('PI',3.14);
    //包含文件:为了显示以上数据
    include_once 'include4.php';
    ?>
//include4.php
<table>
    <tr>
        <td><?php echo $a;?></td>
        <td><?php echo PI;?></td>
</table>

文件加载原理

PHP代码的执行流程

1、读取代码文件(PHP文件)

2、编译:将PHP代码转换成字节码(生成opcode)

3、zendengine来计息opcode,按照字节码去进行逻辑运算

4、转换成对应的HTML代码

文件加载原理:

1、在文件加载(include或者require)的时候,系统会自动的将将被包含文件中的代码相当于嵌入到当前文件中

2、加载位置:在哪加载,对应的文件中的代码嵌入的位置就是对应的include位置

3、在PHP中被包含的文件是单独进行编译的

PHP文件在编译的过程中如果出现了语法错误,那么会失败(不会执行);但是如果被包含文件有错误的时候,系统会在执行到包含include这条语句的时候才会报错。

inclde和require区别

include和include_once的区别:

include系统会碰到一次,执行一次;如果对同一个文件进行多次加载,那么系统会执行多次;

include_once:系统碰到多次,也只会执行一次

require和include的区别:本质都是包含文件,唯一的区别在于包含不到文件的时候,报错的形式不一样

include的错误级别较轻:不会阻止代码执行

require要求较高:如果包含出错代码不再执行(require后面的代码)

文件加载路径

文件在加载的收需要指定文件路径才能保证PHP正确的找到对应的文件。

文件的加载路径包含两大类:

1、绝对路径

从磁盘的根目录开始(本地绝对路径)

windows:盘符c:/路径/PHP文件

linux:/路径/PHP文件

从网站根目录开始(网络绝对路径)

/:相对于网站主机名字对应的路径

2、相对路径:从当前文件所在目录开始的路径

.|./:表示当前文件夹

…/:上级目录(当前文件夹的上一层文件夹)

绝对路径和相对路径的加载区别

1、绝对路径相对相率偏低,但是相对安全(路径不会出问题)

2、相对路径相对效率较高,但是容易出错(相对路径会发生改变)

//PHP文件加载路径
<?php
    //相对路径加载
    include_once 'include1.php';    //默认当前文件本身
    include_once './include1.php';  
    //复杂相对路径
    include_once '../www/include1.php';
 
    //绝对路径
    include_once 'D:\Service\phpstudy_pro\WWW\include1.php';
?>

文件嵌套包含

文件嵌套包含:一个文件包含另外一个文件,同时被包含的文件又包含了另外一个文件

//文件嵌套包含
<?php
    //包含include3.php//文件本身包含了Include4.php
    include 'include3.php';
    ?>

嵌套包含的时候就很容易出现相对路径出错的问题:相对路径会因为文件的包含而改变(./和…/):windows下面,每一个文件夹下都有.和…的文件夹。

举例:张三左边是李四,李四左边是王五

张三把李四叫到自己的位置:李四与王五之间又两个位置,李四如果还按照左边伸手找王五就找不到

字符串相关函数

1、转换函数:Implode(),explode(),str_split()

implode(连接方式,数组):将数组中的元素按照某个规则连接成一个字符串

explode(分隔字符,目标字符串):将字符串按照某个格式进行分隔,变成一个数组

中国|北京|顺义 == array(“中国”,”北京”,”顺义”)

str_split(字符串,字符长度):按照指定长度拆分字符串得到数组

2、截取函数:trim(),ltrim(),rtrim()

trim(字符串[,指定字符]):本身默认是用来去除两边的空格(中间不行),但是也可以指定要去除的内容,是按照指定的内容循环取出两边有的内容:直到碰到一个不是目标字符为止

ltrim():去除左边的

rtrim():去除右边的

$str1 = ' ab cd ef ';
$str2 = trim($str1);
var_dump($str2);        //string(8) "ab cd ef"

3、截取函数:substr(),strstr()

substr(字符串,起始位置从0开始[,长度]):指定位置开始截取字符串,可以截取指定长度(不指定到最后)

strstr(字符串,匹配字符):从指定位置开始,截取到最后(可以用来取文件后缀名)

$str1 = ' ab cd ef ';
$str2 = substr($str1,4);
$str3 = strstr($str1,'c');
var_dump($str2);        //string(6) "cd ef "
echo '<br/>';
var_dump($str3);        //string(6) "cd ef "

4、大小写转换函数:strtolower(),strtoupper(),ucfirst()

strtolower():全部小写

strtoupper():全部大写

ucfirst():首字母大写

$str = 'abcd e f '
echo strtoupper($str);        //ABCD E F
echo ucfirst($str);            //Abcd e f

5、查找函数:strpos(),strrpos()

strpos():查找字符在目标字符串中首次出现的位置,没找到则返回false

strrpos():查找字符在目标字符串中最后次出现的位置,没找到则返回false

$str = '123a123a123a123';
echo strpos($str,'a'),'<br/>';        //3
echo strrpos($str,'a');                //11

6、替换函数:str_replace()

str_replace(匹配目标,替换的内容,字符串本身):将目标字符串中部分字符串进行替换

$str = '123a123a123a123';
echo str_replace('a','b',$str);        //123b123b123b123

7、格式化函数:printf(),sprintf()

printf/sprintf(输出字符串有占位符,顺序占位内容…):格式化输出数据

$age = 8;
$name = 'tony';
echo sprintf('你好,今年我%d岁,我叫%s',$age,$name);        //你好,今年我8岁,我叫tony

8、其他:str_repeat(),str_shuffle()

str_repeat():重复某个字符串N次

str_shuffle():随机打乱字符串

$str = 'abcdefg';
echo str_repeat($str,5),'<br/>';        //abcdefgabcdefg
echo str_shuffle($str);            //fdbgeca

数组详解

数组的概念

数组:array,数据的组合,值将一组数据(多个)存储到一个指定的容器中,用变量指向该容器,然后可以通过变量一次性得到该容器中的所有数据。

数组定义语法

在PHP中系统提供了多种定义数组的方式:

1、使用array关键字:最常用的

$变量 = array(元素1,元素2,元素3…);

2、可以使用中括号来包裹数据:

$变量 = [元素1,元素2…];

3、隐形定义数组:给变量增加一个中括号,系统自动变成数组

$变量[] = 值1; //如果不提供下标也可以,系统自动生成(数字:从0开始,且从当前前面最大的开始)

$变量[下标] = 值; //中括号里面的内容称之为下标key,该下标可以是字母(单词)或者数字,与变量命名的规则相似

//定义数组:array
$arr1 = array('1',2,'hello');
var_dump($arr1);    //array(3) { [0]=> string(1) "1" [1]=> int(2) [2]=> string(5) "hello" }
//定义数组:[]
$arr2 = ['1',2,'hello'];
var_dump($arr2);    //同上
//隐形数组:
$arr3[] = 1;
$arr3[10] = 100;
$arr3[] = '2';
$arr3[3] = 55;
$arr3['key'] = 'key';
var_dump($arr3);    //array(5) { [0]=> int(1) [10]=> int(100) [11]=> string(1) "2" [3]=> int(55) ["key"]=> string(3) "key" }

PHP数组特点

1、可以整数下标或者字符串下标

如果数组下标都为整数:索引数组

如果数组下标都为字符串:关联数组

2、不同下标可以混合存在:混合数组

3、数组元素的顺序以放入顺序为准,跟下标无关

4、数字下标的自增长特性:从0开始自动增长,如果中间手动出现较大的,那么后面的自增长元素从最大的值+1开始

5、特殊值下标的自动转换

布尔值:true和false

空:NULL

$arr4[false] = false;
$arr4[true] = true;
$arr4[NULL] = NULL;
var_dump($arr4);        //array(3) { [0]=> bool(false) [1]=> bool(true) [""]=> NULL }

6、PHP中数组元素没有类型限制

7、PHP中数组元素没有长度限制

补充:PHP中的数组是很大的数据,所以存储是堆区,为当前数组分配一块连续的内存。

多维数组

多维数组:数组里面的元素又是数组

二维数组

二维数组:数组中所有的元素都是一维数组

$info = array(
    array('name' => 'Dim','age' => 30),
    array('name' => 'Tom','age' => 28),
    array('name' => 'Lily','age' => 20)        //最后一个元素,后面可以跟逗号不影响(不建议)
);
echo '<br/>';
print_r($info);

多维数组

在第二维的数组元素中可以继续时数组,在PHP中没有维度限制(PHP本质并没有二维数组)

但是:不建议使用超过三维以上的数组,会增加访问复杂度,降低访问效率。

异形数组(不规则数组)

异形数组:数组中的元素不规则,有普通基本变量也有数组

在实际开发中,并不常用,尽量让数组袁术规则化(便于进行访问)

遍历数组

遍历的基本含义

数组遍历:普通数组数据的访问都是通过数组元素的下标来实现访问,如果说数组中所有的数据都需要一次输出出来,就需要我们使用到一些简化的规则来实现自动获取下标以及输出数组元素。

$arr = arry(0 => array('name' => 'Tom'),1 => array('name' => 'Jim'));    //二维数组
//访问一维元素:$arr[一维下标]
$arr[0];        //结果:array('name' => 'Tom')
//访问二维元素:$arr[一维下标][二维下标]
$arr[1]['name']//Jim

foreach遍历语法

基本语法:

foreach($数组变量 as[$下标 =>]$值){
    //通过$下标访问元素的下标;通过$值访问元素的值
}

通常:如果时关联数组(字母下标),就需要下标,如果是数字下标就直接访问值

//PHP数组遍历:foreach
//定义数组
$arr = array(1,2,3,4,5,6,7,8,9,10);
//foreach循环
foreach($arr as $v){
    //$v随意命名
    echo $v,'<br/>';        //1,2,3,4,5,6,7,8,9,10
}
foreach($arr as $k => $v){
    //$v随意命名
    echo 'key:',$k,' == value:',$v,'<br/>';        //key:0 == value:1
                                                //key:1 == value:2
                                                //key:2 == value:3
                                                //key:3 == value:4
                                                //key:4 == value:5
                                                //key:5 == value:6
                                                //key:6 == value:7
                                                //key:7 == value:8
                                                //key:8 == value:9
                                                //key:9 == value:10
}

在进行数据存储定义的时候,通常二维数组不会两个维度的key下标都为数字,一般时一维为数字(无意义),二维为字符串(数据库表字段),所以在进行遍历的时候,通常是只需要针对一维进行遍历,取得二维数组元素,然后二维数组元素通过下标去访问。

$arr = array(
    0 => array('name' => 'Dim','age' => 30),
    1 => array('name' => 'Tom','age' => 28)
);
//通过foreach遍历一维元素
foreach($arr as $value){
    //1、可以继续遍历:增加foreach遍历$value
    //2、可以使用下标访问
    echo 'name is:',$value['name'],' and age is:',$value['age'],'<br/>';            //name is:Dim and age is:30
        //name is:Tom and age is:28
}

foreach遍历原理

foreach遍历的原理:本质时数组的内部有一颗指针,默认时指向数组元素的第一个元素,foreach就是利用指针去获取数据,同时移动指针。

foreach($arr as $k => $v){
    //循环体
}

1、foreach会重置指针:让指针指向第一个元素;

2、进入foreach循环:通过指针取得当前第一个元素,然后将下标取出放到对应的下标变量 k 中 ( 如 果 存 在 ) , 将 值 取 出 来 放 到 对 应 的 值 变 量 k中(如果存在),将值取出来放到对应的值变量 kv中;(指针下移)

3、进入到循环内部(循环体),开始执行;

4、重复2和3,直到在2的时候遇到指针取不到内容(指针指向数组最后)

for循环遍历数组

for循环:基于已知边界条件(起始和结束)然后有条件的变化(规律)

因此:for循环遍历数组有对应条件

1、获取数组长度:count(数组)的阿斗数组元素的长度

2、要求数组元素的下标时规律的数字

//数组特点:索引数组,下标规律
$arr = array(1,2,3,4,5,6,7,10);
for($i = 0,$len = count($arr);$i < $len;$i++){
    echo 'key is:',$i,' and value is:',$arr[$i],'<br/>';
                //key is:0 and value is:1
                //key is:1 and value is:2
                //key is:2 and value is:3
                //key is:3 and value is:4
                //key is:4 and value is:5
                //key is:5 and value is:6
                //key is:6 and value is:7
                //key is:7 and value is:10
}

while配合each和list遍历数组

while实在外部定义边界条件,如果要实现可以和for循环实现

each函数使用:each能够从一个数组中获取当前数组指针所指向的元素的下标和值,拿到之后将数组指针下移,同时将拿到的元素的下标和值以一个四个元素的数组返回:

0下标 –> 取得元素的下标值

1下标 –> 取得元素的值

key下标 –> 取得元素的下标值

value下标 –> 取得元素的值

$arr = array(1,'name' => 'Tom',3,'age' => 30);
echo '<pre/>';
//each函数指针操作
print_r(each($arr));    //Array([1] => 1 [value] => 1 [0] => 0 [key] => 0)
print_r(each($arr));    //Array( [1] => Tom [value] => Tom [0] => name [key] => name)

如果each取不到结果(数组指针移到最后),返回false

list函数使用:list是一种结构,不是一种函数(没有返回值),是list提供一堆变量去从一个数组中取得元素值,然后依次存放到对应的变量当中(批量为变量赋值:值来源于数组):list必须从索引数组中去获取数据,而且必须从0开始。

$arr = array(1,2 => 1);
list($first) = $arr;
list($first,$second) = $arr;    //错误:second变量对应的小标为元素小标1的,但是数组没有
var_dump($first);        //int(1)

list和each配合特别好:each一定有两个元素就是0和1下标元素

list(变量1,变量2) = each(数组);    //是一种赋值运算,但是可以得到false结果(each取不到正确的结果),整个表达式为false

 
 
 
$arr = array(1,'name' => 'Tom',3,'age' => 30);
while(list($key,$value) = each($arr)){
    //输出
    echo 'key is :',$key,' value is:',$value,'<br/>';
}

数组的相关函数

1、排序函数:对数组元素进行排序,都是按照ASCII码进行比较,可以进行英文比较

sort():顺序排序(下标重排)

//排序函数
$arr = array(3,1,5,2,0);
echo '<pre>';
$sort_arr = sort($arr);
print_r($arr);        //Array ([0] => 0 [1] => 1 [2] => 2 [3] => 3 [4] => 5 )

rsort():逆向排序(下标重排)

asor():顺序排序(下标保留)

$asort_arr = asort($arr);
print_r($arr);        //Array ( [4] => 0 [1] => 1 [3] => 2 [0] => 3 [2] => 5 )

arsor():逆序排序(下标保留)

ksort():顺序排序:按照键名(下标)

krsort():逆序排序:按照键名(下标)

$krsort_arr = krsort($arr);
print_r($arr);        //Array ( [4] => 0 [3] => 2 [2] => 5 [1] => 1 [0] => 3 )

shuffle():随机打乱数组元素

$sort_arr = sort($arr);
$shuffle_arr = shuffle($arr);
print_r($arr);        //Array ( [0] => 3 [1] => 2 [2] => 0 [3] => 5 [4] => 1 )

2、指针函数

reset():重置指针,将数组指针回到首位

end():重置指针,将数组指针指到最后一个元素

next():指针下移,取得下一个元素的值

prev():指针上移,取得上一个元素的值

current():获得当前指针的元素值

key():获得当前指针的下标值

$arr = array(3,1,5,2,0);
//指针函数
echo key($arr),'<br/>';            //0
echo current($arr),'<br/>';        //3
echo next($arr),next($arr),'<br/>';        //1,5
echo prev($arr),'<br/>';        //1
echo end($arr),'<br/>';            //0
echo reset($arr),'<br/>';        //3

注意事项:next和prev会移动指针,有可能会导致指针移动到最前或者最后(离开数组),导致数组不能使用,通过next和prev不能回到正确的位置。只能通过end或者reset进行指针重置。

3、其他函数

count():统计数组中元素的数量

array_push():往数组中加入一个元素(数组后面)

array_pop():从数组中取出一个元素(数组后面)

array_shift():从数组中取出一个元素(数组前面)

array_unshift():往数组中加入一个元素(数组前面)

PHP模拟数据结构:

栈:压栈,先进去后出来(FILO)

队列:排队,先进去的先出去(FIFO)

$arr = array();
//栈:先压栈后出栈:都是从一端出来
//前面:array_shift/array_unshift
//后面:array_push/array_pop
//压栈
array_push($arr,3);
array_push($arr,2);
array_push($arr,1);
print_r($arr);        //Array ( [0] => 3 [1] => 2 [2] => 1 )
//出栈
echo array_pop($arr),array_pop($arr),array_pop($arr),'<br/>';        //1,2,3
$arr = array();
//队列:先排队,先出来,一端进,另外一端出
//后进前出:array_push/array_shfit
//前进后出:array_unshift/array_pop
//入队
array_unshift($arr,3);
array_unshift($arr,2);
array_unshift($arr,1);
print_r($arr);            //Array ( [0] => 1 [1] => 2 [2] => 3 )
//出队
echo array_pop($arr),array_pop($arr),array_pop($arr),'<br/>';        //3,2,1

array_reverse():把数组元素反过来

in_array():判断一个元素在数组中是否存在

array_keys():获取一个数组的所有下标,返回一个索引数组

array_values():获取一个数组的所有值,返回一个索引数组

$arr = array(1,2,3,4,5);
print_r(array_reverse($arr));    //Array ( [0] => 5 [1] => 4 [2] => 3 [3] => 2 [4] => 1 )
var_dump(in_array(4,$arr));        //bool(true)
var_dump(in_array(8,$arr));        //bool(false)
print_r(array_keys($arr));        //Array ( [0] => 0 [1] => 1 [2] => 2 [3] => 3 [4] => 4 )
print_r(array_values($arr));    //Array ( [0] => 1 [1] => 2 [2] => 3 [3] => 4 [4] => 5 )
//使用指针遍历数组
<?php
$arr = array(5,4,3,2,1);
while(key($arr)!==null){
    echo key($arr)."=>".current($arr)."\n";
    next($arr);
}
    ?>

编程思想

编程思想:如何利用数学模式来解决对应的需求问题;然后利用代码实现对应的数据模型(逻辑)。

算法:使用代码实现对应的数学模型,从而解决对应的业务问题。

递推算法

递推算法是一种简单的算法,即通过已知条件,利用特定关系得出中间推论,直至得到结果的算法。递推算法分为顺推和逆推两种。

顺推:通过最简单的条件(已知),然后逐步推演结果

逆推:通过结果找到规律,然后推到已知条件

斐波那契数列:1 1 2 3 5 8 13 …,通常需求:请求得指定位置N所对应的值是多少

找规律:

1、第一个数是1

2、第二个数也是1

3、从第三位开始:属于前两个数的和

代码解决思路:

1、如果数字位置为1和2,结果都是1

2、从第三个开始,想办法得到前两个结果,就可以得到

终极解决办法:想办法要求的位置之前的所有的值都列出来,那么要求的数就可以通过前两个数之和计算出来:使用数组存储所有结果即可。

递推算法求斐波那契序列:

//需求:规律1  1  2  3  5...
//求出指定位数对应的值
//已知条件:第一个和第二个数都为1,第三个开始为前两个之和
function my_recursive($dse){
    if($des == 1 || $des == 2) return 1;
    //开始计算
    $f[1] = 1;
    $f[2] = 1;     //如果想要第一个或者第二个结果,那么可以直接给出
    $des = 15;
    for($i = 3;$i <= $des;$i++){
        $f[$i] = $f[$i-1] + $f[$i-2];
    }
    //查看
    return $f[$des];
}
echo my_recursive(15);        //610

递归算法

递推算法是把问题转化为规模小了的同类问题的子问题。然后递归调用函数(或过程)来表示问题的解。

1、简化问题:找到最优子问题(不能再小)

2、函数自己调用自己

斐波那契数列:1 1 2 3 5 8 13 …

需求:请求得指定位置N所对应的值是多少

规律:第一和第二个数为1,从第三个开始为前两个数之和

F(N) = F(N-1) + F(N-2);

F(N-1) = F(N-2) + F(N-3);

F(2) = F(1) = 1;

递归思想中:量两个非常重要的点

递归点:发现当前问题又解决当前问题的函数,取解决规模比当前小一点的问题来解决F(N) = F(N-1) + F(N-2)

递归出口:当问题解决的时候,已经到达(必须有)最优子问题,不能再次调用函数

如果一个函数递归调用自己而没有递归出口:就是死循环

递归的本质是函数调用函数:一个函数需要开辟一块内存空间,递归会出现同时调用N多个函数(自己):递归的本质是利用空间换时间。

//递归一定有函数
function recursion($n){
    //递归出口
    if($n == 1 || $n == 2) return 1;
    //递归点:求N的值,与求N-1的值一模一样,只是N-1的规模比N小
    return recursion($n-1) + recursion($n-2);
}
echo recursion(15);        //610

数组排序算法

冒泡排序

冒泡排序(bubble sort),是一种计算机科学领域的比较简单的排序算法。

他重复地走访过要排序的序列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。

走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。

冒泡排序的算法思路:

1、比较相邻的元素。如果第一个比第二个大,就交换他们两个。

2、对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应该回事最大的数。

3、针对所有元素重复以上步骤,除了最后一个。

4、持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。

//数组排序算法:冒泡排序
$arr = array(1,4,2,9,7,5,8);
//2、想办法让下面可以每次找出最大值的代码重复执行
for($i = 0,$len = count($arr);$i < $len - 1;$i++,$len--){
    //1、想办法将最大的值放到最右边去
    for($j = 0;$j < $len - 1;$j++){
        //判断:两两相比
        if($arr[$j] > $arr[$j+1]){
            //左边比右边大:交换
            $temp = $arr[$j];
            $arr[$j] = $arr[$j+1];
            $arr[$j+1] = $temp;
        }
    }
}
echo '<pre>';
print_r($arr);        //Array ( [0] => 1 [1] => 2 [2] => 4 [3] => 5 [4] => 7 [5] => 8 [6] => 9 )

选择排序

选择排序(selection sort)是一种简单直观的排序算法。他的工作原理是每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排完。选择排序是不稳定的排序方法(比如序列[5,5,3]第一次就将第一个[5]与[3]交换,导致第一个5挪动到第二个5后面)。

选择排序的思路:

1、假设第一个元素为最小元素,记下下标。

2、寻找右侧剩余的元素,如果有更小的,重新记下最新的下标。

3、如果又新的最小的,交换两个元素。

4、往右重复以上步骤,直到元素本身为最后一个。

//数组排序算法:选择排序
$arr = array(1,5,2,9,6,3,4);
//1、确定要交换多少次:一次只能找到最小的,需要找到数组长度对应的次数
for($i = 1,$len = count($arr);$i < $len;$i++){
    //2、假设当前第一个已经排好序了
    $min = $i;
    //3、拿该最小的取比较剩余的其他
    for($j = $i+1;$j < $len;$j++){
        //4、比较:比较当前元素与选定的最小元素
        if($arr[$j] < $arr[$min]){
            //说明当前指定的$min不合适
            $min = $j;
        }
    }
    //5、交换当前选定的值与实际最小的元素值
    if($min != $i){
        $temp = $arr[$i];
        $arr[$i] = $arr[$min];
        $arr[$min] = $temp;        
    }
}
echo '<pre>';
print_r($arr);        //Array ( [0] => 1 [1] => 2 [2] => 3 [3] => 4 [4] => 5 [5] => 6 [6] => 9 )

插入排序

插入排序(insert sort),插入排序的基本操作就是将一个数据插入到已经排好序的有序数据中,从而得到一个新的、个数加一的有序数据,算法适用于少量数据的排序,是稳定的排序方法。插入算法把要排序的数组分为两部分:第一部分包含了这个数组的所有元素,但将最后一个元素除外(让数组多一个空间才有插入的位置),而第二部分就只包含这一个元素(即待插入元素)。在第一部分排序完成后,再将这个最后元素插入到已排好序的第一部分中。

插入排序的基本思想是:每步将一个待排序的记录,按其关键码值的大小插入前面已经排序的文件中适当的位置上,直到全部插入完为止。

插入排序的算法思路:

1、设置监视哨r[0],将待插入记录的值赋值给r[0];

2、设置开始查找的位置j

3、再数组中进行搜索,搜索中将第j个记录后移,直至r[0].key >= r[j].key为止;

4、讲r[0]插入r[j+1]的位置上

通俗来讲:

1、认定第一个元素已经排好序

2、取出第二个元素,作为待插入数据

3、与已经排好序的数组的最右侧元素开始比较

4、如果后面的小于前面的:说明前面已经排好序的那个数组元素不再对的位置(向后移一个),然后让新的元素填充进去(继续向前比:高级)

5、重复前面的步骤:直到当前元素插入到对的位置

6、重复以上步骤,直到所有的数组元素都插入到对的位置

//数组排序:插入排序
$arr = array(4,2,6,8,9,5);
//1、确定要插入多少回(假设一个数字一次性插入到对的位置,同时第一个位置是假设对的)
for($i = 1,$len = count($arr);$i < $len;$i++){
    //2、取出当前要插入的元素的值
    $temp = $arr[$i];
    //3、让该数据与前面已经拍好序的数组元素重复比较(挨个比较),直到对的位置(交换)
    for($j = $i - 1;$j >= 0;$j--){
        //4、比较
        if($arr[$j] > $temp){
            //说明当前要插入的元素,比前面的已经排好序的元素的值要小:交换位置
            $arr[$j+1] = $arr[$j];
            $arr[$j] = $temp;
        }else{
            //说明当前待插入元素,比前面的元素要大:说明位置正确
            break;
        }
    }
}
echo '<pre>';
print_r($arr);        //Array ( [0] => 2 [1] => 4 [2] => 5 [3] => 6 [4] => 8 [5] => 9 )

优化代码:找到对的位置交换一次即可

//数组排序:插入排序(优化)
$arr = array(4,2,6,8,9,5);
//1、确定要插入多少回(假设一个数字一次性插入到对的位置,同时第一个位置是假设对的)
for($i = 1,$len = count($arr);$i < $len;$i++){
    //2、取出当前要插入的元素的值
    $temp = $arr[$i];
    $change = false;
    //3、让该数据与前面已经拍好序的数组元素重复比较(挨个比较),直到对的位置(交换)
    for($j = $i - 1;$j >= 0;$j--){
        //4、比较
        if($arr[$j] > $temp){
            //说明当前要插入的元素,比前面的已经排好序的元素的值要小:交换位置
            $arr[$j+1] = $arr[$j];
            //说明前面要插入的元素,比前面已经排好序的元素的值要小:交换位置
            $change = true;
        }else{
            //说明当前待插入元素,比前面的元素要大:说明位置正确
            break;
        }
    }
    //判断位置需要变动
    if($change){
        //有数据移动:占错位置了
        $arr[$j+1] = $temp;
    }
}
echo '<pre>';
print_r($arr);        //Array ( [0] => 2 [1] => 4 [2] => 5 [3] => 6 [4] => 8 [5] => 9 )

快速排序

快速排序(quick sort)是对冒泡排序的一种改进。通过一趟排序将要排序的数据分隔成独立的两部分,其中一部分的所有数据都比另一部分的所有数据都要小,然后再按次方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。(递归)

设要排序的数组是A[0]……A[N-1],首先任意选取一个数据(通常选用数组的第一个数)作为关键数据,然后将所有比它小的数据都放到它前面,所有比它大的数据都放到它后面,这个过程称为一趟快速排序。值得注意的是,快速排序不是一种稳定的排序算法,也就是说,多个相同的值的相对位置也许会再算法结束时产生变动。

快速排序的算法是:

1、从数组中选出一个元素(通常第一个),作为参照对象。

2、定义两个数组,将目标数组中剩余的元素与参照元素挨个比较:小的放到一个数组,大的放到另外一个数组。

3、第二步执行完之后,前后的数组顺序不确定,但是确定了自己的位置。

4、将得到的小数组按照第1到第3步重复操作(子问题)。

5、回溯最小数组(一个元素)

//数组排序:快速排序
$arr = array(1,6,3,4,9,2,7,8);
function quick_sort($arr){
    //递归出口
    $len = count($arr);
    if($len <= 1) return $arr;
    //取出某个元素,然后将剩余的数组元素,分散到两个不同的数组中
    $left = $right = array();
    for($i = 1;$i < $len;$i++){
        //第一个元素作为比较元素
        //比较:小的放left中,大的放right中
        if($arr[$i] < $arr[0]){
            $left[] = $arr[$i];
        }else{
            $right[] = $arr[$i];
        }
    }
    //$left和$right数组元素没有排好序:递归点
    $left = quick_sort($left);
    $right = quick_sort($right);
    //合并三个“数”组
    return array_merge($left,(array)$arr[0],$right);
}
echo '<pre>';
print_r(quick_sort($arr));        //Array ( [0] => 2 [1] => 3 [2] => 4 [3] => 5 [4] => 6 [5] => 7 [6] => 8 [7] => 9 )

归并排序

归并排序(merge sort)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。

//二路归并
$arr1 = array(1,3,5);
$arr2 = array(2,4,6);
//取出一个空数组用于归并空间
$arr3 = array();
while(count($arr1) && count($arr2)){
    //只要$arr1和$arr2里面还有元素,就进行循环
    //取出每个数组的第一个元素:进行比较
    $arr3[] = $arr1[0] < $arr2[0] ? array_shift($arr1) :array_shift($arr2);    }
//合并结果
print_r(array_merge($arr3,$arr1,$arr2));

归并排序的算法是:

1、将数组拆分成两个数组

2、重复步骤1将数组拆分成最小单元

3、申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列。

4、设定两个指针,最初位置分别为两个已经排序序列的起始位置。

5、比较两个指针所所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置

6、重复步骤3直到某一指针超出序列尾

7、将另一序列所剩下的所有元素直接赋值到合并序列尾

$arr = array(4,7,2,1,5,9,3);
//数组排序:归并排序
function merge_sort($arr){
    //递归出口
    $len = count($arr);
    if($len <= 1) return $arr;
    //拆分
    $middle = floor($len / 2);
    $left = array_slice($arr,0,$middle);
    $right = array_slice($arr,$middle);
    //递归点:$left和$right都没有排好序:而且可能是多个元素的数组
    $left = merge_sort($left);
    $right = merge_sort($right);
    //假设左边和右边都已经排好序:二路归并
    $m = array();
    while(count($left) && count($right)){
        //只要$arr1和$arr2里面还有元素,就进行循环
        //取出每个数组的第一个元素:进行比较
        $m[] = $left[0] < $right[0] ? array_shift($left) :array_shift($right);    
    }
    //返回结果
    return array_merge($m,$left,$right);        //Array ( [0] => 1 [1] => 2 [2] => 3 [3] => 4 [4] => 5 [5] => 7 [6] => 9 )
}

查找算法

查找算法含义

查找实在大量的信息中寻找一个特定的信息元素,再计算机应用中,查找是基本运算。查找算法是指实现查找过程对应的代码结。就是在大型数组中取快速定位到想要的元素。

顺序查找算法

顺序查找也称为线形查找,从数据结构线形表的一端开始,顺序扫描,一次将扫描到的结点关键字与给定值k相比较,若相等则表示查找成功,若扫描结束仍然没有找到关键字等于k的结点,表示查找失败。

//查找算法:顺序查找
$arr = array(1,3,6,8,23,68,100);
//顺序查找:从数组第一个元素开始,挨个匹配
function check_order($arr,$num){
    //全部匹配
    for($i = 0,$len = count($arr);$i < $len;$i++){
        //判断
        if($arr[$i] == $num){
            return $i;
        }
    }
    return false;
}
var_dump(check_order($arr,5));        //bool(false)

二分查找算法

二分查找算法要求线形表中的结点按关键字值升序或者降序排列,用给定值k与中间结点的关键字比较,中间结点把线形表分成两个子表,若相等则查找成功;若不相等,再根据k与该中间结点关键字的比较结果确定下一步查找哪个子表,这样递归进行,直到查找到或查找结束。

折半算法思路:

1、计算数组长度

2、确定左右两边的指针位置

3、找到中间位置

4、匹配

5、然后根据大小重定边界

//查找算法:二分查找算法
$arr = array(1,3,6,8,23,68,100);
    function check_break($arr,$res){
    //1、得到数组的边界
    $right = count($arr);
    $left = 0;
    //循环匹配
    while($left <= $right){
        //3、得到中间位置
        $middle = floor(($left + $right) / 2);
        //4、匹配数据
        if($arr[$middle] == $res){
            return $middle;
        }
        //5、没有找到
        if($arr[$middle] <= $res){
            //值在右边
            $left = $middle + 1;
        }else{
            //值在左边
            $right = $middle - 1;
        }
    }
    return false;
}
var_dump(check_break($arr,100));        //float(6)

PHP面向对象

  • ? 定义了一件事物的抽象特点。类的定义包含了数据的形式以及对数据的操作。
  • 对象 ? 是类的实例。
  • 成员变量 ? 定义在类内部的变量。该变量的值对外是不可见的,但是可以通过成员函数访问,在类被实例化为对象后,该变量即可成为对象的属性。
  • 成员函数 ? 定义在类的内部,可用于访问对象的数据。
  • 继承 ? 继承性是子类自动共享父类数据结构和方法的机制,这是类之间的一种关系。在定义和实现一个类的时候,可以在一个已经存在的类的基础之上来进行,把这个已经存在的类所定义的内容作为自己的内容,并加入若干新的内容。
  • 父类 ? 一个类被其他类继承,可将该类称为父类,或基类,或超类。
  • 子类 ? 一个类继承其他类称为子类,也可称为派生类。
  • 多态 ? 多态性是指相同的函数或方法可作用于多种类型的对象上并获得不同的结果。不同的对象,收到同一消息可以产生不同的结果,这种现象称为多态性。
  • 重载 ? 简单说,就是函数或者方法有同样的名称,但是参数列表不相同的情形,这样的同名不同参数的函数或者方法之间,互相称之为重载函数或者方法。
  • 抽象性 ? 抽象性是指将具有一致的数据结构(属性)和行为(操作)的对象抽象成类。一个类就是这样一种抽象,它反映了与应用有关的重要性质,而忽略其他一些无关内容。任何类的划分都是主观的,但必须与具体的应用有关。
  • 封装 ? 封装是指将现实世界中存在的某个客体的属性与行为绑定在一起,并放置在一个逻辑单元内。
  • 构造函数 ? 主要用来在创建对象时初始化对象, 即为对象成员变量赋初始值,总与new运算符一起使用在创建对象的语句中。
  • 析构函数 ? 析构函数(destructor) 与构造函数相反,当对象结束其生命周期时(例如对象所在的函数已调用完毕),系统自动执行析构函数。析构函数往往用来做"清理善后" 的工作(例如在建立对象时用new开辟了一片内存空间,应在退出前在析构函数中用delete释放)。

构造函数

PHP 5 允许开发者在一个类中定义一个方法作为构造函数,语法格式如下:

void __construct ([ mixed $args [, $... ]] )

析构函数

析构函数(destructor) 与构造函数相反,当对象结束其生命周期时(例如对象所在的函数已调用完毕),系统自动执行析构函数。

PHP 5 引入了析构函数的概念,这类似于其它面向对象的语言,其语法格式如下:

void __destruct ( void )

继承

PHP 使用关键字 extends 来继承一个类,PHP 不支持多继承,格式如下:

class Child extends Parent {
   // 代码部分
}

子类继承父类的属性和方法,也就是说子类拥有父类所有的东西,包括 public(公有)、protected(保护)和private(私有)的。但是父类私有的属性和方法不能让子类直接调用,并不是说没有继承过来,而是继承过来了,但是要调用父类的私有属性和方法的话还是需要通过父类的 public、protected 方法调用。

方法重写

如果从父类继承的方法不能满足子类的需求,可以对其进行改写,这个过程叫方法的覆盖(override),也称为方法的重写。

实例中重写了 getUrl 与 getTitle 方法:

访问控制

PHP 对属性或方法的访问控制,是通过在前面添加关键字 public(公有),protected(受保护)或 private(私有)来实现的。

  • **public(公有):**公有的类成员可以在任何地方被访问。
  • **protected(受保护):**受保护的类成员则可以被其自身以及其子类和父类访问。
  • **private(私有):**私有的类成员则只能被其定义所在的类访问。

接口

使用接口(interface),可以指定某个类必须实现哪些方法,但不需要定义这些方法的具体内容。

接口是通过 interface 关键字来定义的,就像定义一个标准的类一样,但其中定义所有的方法都是空的。

接口中定义的所有方法都必须是公有,这是接口的特性。

要实现一个接口,使用 implements 操作符。类中必须实现接口中定义的所有方法,否则会报一个致命错误。类可以实现多个接口,用逗号来分隔多个接口的名称。

<?php

// 声明一个'iTemplate'接口
interface iTemplate
{
    public function setVariable($name, $var);
    public function getHtml($template);
}


// 实现接口
class Template implements iTemplate
{
    private $vars = array();
  
    public function setVariable($name, $var)
    {
        $this->vars[$name] = $var;
    }
  
    public function getHtml($template)
    {
        foreach($this->vars as $name => $value) {
            $template = str_replace('{' . $name . '}', $value, $template);
        }
 
        return $template;
    }
}

常量

可以把在类中始终保持不变的值定义为常量。在定义和使用常量的时候不需要使用 $ 符号。

常量的值必须是一个定值,不能是变量,类属性,数学运算的结果或函数调用。

自 PHP 5.3.0 起,可以用一个变量来动态调用类。但该变量的值不能为关键字(如 self,parent 或 static)。

<?php
class MyClass
{
    const constant = '常量值';

    function showConstant() {
        echo  self::constant . PHP_EOL;
    }
}

echo MyClass::constant . PHP_EOL;

$classname = "MyClass";
echo $classname::constant . PHP_EOL; // 自 5.3.0 起

$class = new MyClass();
$class->showConstant();

echo $class::constant . PHP_EOL; // 自 PHP 5.3.0 起
?>

抽象类

任何一个类,如果它里面至少有一个方法是被声明为抽象的,那么这个类就必须被声明为抽象的。

定义为抽象的类不能被实例化。

被定义为抽象的方法只是声明了其调用方式(参数),不能定义其具体的功能实现。

继承一个抽象类的时候,子类必须定义父类中的所有抽象方法;另外,这些方法的访问控制必须和父类中一样(或者更为宽松)。例如某个抽象方法被声明为受保护的,那么子类中实现的方法就应该声明为受保护的或者公有的,而不能定义为私有的。

<?php
abstract class AbstractClass
{
 // 强制要求子类定义这些方法
    abstract protected function getValue();
    abstract protected function prefixValue($prefix);

    // 普通方法(非抽象方法)
    public function printOut() {
        print $this->getValue() . PHP_EOL;
    }
}

class ConcreteClass1 extends AbstractClass
{
    protected function getValue() {
        return "ConcreteClass1";
    }

    public function prefixValue($prefix) {
        return "{$prefix}ConcreteClass1";
    }
}

class ConcreteClass2 extends AbstractClass
{
    public function getValue() {
        return "ConcreteClass2";
    }

    public function prefixValue($prefix) {
        return "{$prefix}ConcreteClass2";
    }
}

$class1 = new ConcreteClass1;
$class1->printOut();
echo $class1->prefixValue('FOO_') . PHP_EOL;

$class2 = new ConcreteClass2;
$class2->printOut();
echo $class2->prefixValue('FOO_') . PHP_EOL;
?>

//输出
ConcreteClass1
FOO_ConcreteClass1
ConcreteClass2
FOO_ConcreteClass2

此外,子类方法可以包含父类抽象方法中不存在的可选参数。

例如,子类定义了一个可选参数,而父类抽象方法的声明里没有,则也是可以正常运行的。

Static 关键字

声明类属性或方法为 static(静态),就可以不实例化类而直接访问。

静态属性不能通过一个类已实例化的对象来访问(但静态方法可以)。

由于静态方法不需要通过对象即可调用,所以伪变量 $this 在静态方法中不可用。

静态属性不可以由对象通过 -> 操作符来访问。

自 PHP 5.3.0 起,可以用一个变量来动态调用类。但该变量的值不能为关键字 self,parent 或 static

<?php
class Foo {
  public static $my_static = 'foo';
  
  public function staticValue() {
     return self::$my_static;
  }
}

print Foo::$my_static . PHP_EOL;
$foo = new Foo();

print $foo->staticValue() . PHP_EOL;
?>    

  //输出
  //foo
  //foo

Final 关键字

PHP 5 新增了一个 final 关键字。如果父类中的方法被声明为 final,则子类无法覆盖该方法。如果一个类被声明为 final,则不能被继承。

以下代码执行会报错:

<?php
class BaseClass {
   public function test() {
       echo "BaseClass::test() called" . PHP_EOL;
   }
   
   final public function moreTesting() {
       echo "BaseClass::moreTesting() called"  . PHP_EOL;
   }
}

class ChildClass extends BaseClass {
   public function moreTesting() {
       echo "ChildClass::moreTesting() called"  . PHP_EOL;
   }
}
// 报错信息 Fatal error: Cannot override final method BaseClass::moreTesting()
?>

调用父类构造方法

PHP 不会在子类的构造方法中自动的调用父类的构造方法。要执行父类的构造方法,需要在子类的构造方法中调用 parent::__construct()

<?php
class BaseClass {
   function __construct() {
       print "BaseClass 类中构造方法" . PHP_EOL;
   }
}
class SubClass extends BaseClass {
   function __construct() {
       parent::__construct();  // 子类构造方法不能自动调用父类的构造方法
       print "SubClass 类中构造方法" . PHP_EOL;
   }
}
class OtherSubClass extends BaseClass {
    // 继承 BaseClass 的构造方法
}

// 调用 BaseClass 构造方法
$obj = new BaseClass();

// 调用 BaseClass、SubClass 构造方法
$obj = new SubClass();

// 调用 BaseClass 构造方法
$obj = new OtherSubClass();
?>
 
//输出
BaseClass 类中构造方法
BaseClass 类中构造方法
SubClass 类中构造方法
BaseClass 类中构造方法

PHP表单

PHP 中的 $_GET 和 $_POST 变量用于检索表单中的信息,比如用户输入。

PHP 表单处理

有一点很重要的事情值得注意,当处理 HTML 表单时,PHP 能把来自 HTML 页面中的表单元素自动变成可供 PHP 脚本使用。

PHP 获取下拉菜单的数据

PHP 下拉菜单单选

以下实例我们设置了下拉菜单三个选项,表单使用 GET 方式获取数据,action 属性值为空表示提交到当前脚本,我们可以通过 select 的 name 属性获取下拉菜单的值:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>test</title>
</head>
<body>
<form action="test01.php" method="get">
    <select name ="q">
        <option value="">选择一个国家</option>
        <option value="CHINA">China</option>
        <option value="USA">USA</option>
        <option value="UK">UK</option>
    </select>
    <input type="submit" value="提交">
</form>
</body>
</html>
//test01.php
<?php
$q = isset($_GET['q'])?htmlspecialchars($_GET['q']):'';
if ($q){
    if ($q == 'CHINA'){
        echo "你的国家选择了中国";
    }elseif ($q == 'USA'){
        echo "你的国家选择了美国";
    }elseif ($q == 'UK'){
        echo "你的国家选择了英国";
    }
}else{
    echo "选择有误!";
}
?>

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

PHP 下拉菜单多选

如果下拉菜单是多选的( multiple=“multiple”),我们可以通过将设置 select name=“q[]” 以数组的方式获取,以下使用 POST 方式提交,代码如下所示:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>test</title>
</head>
<body>
<form action="test01.php" method="post">
    <select multiple="multiple" name ="q[]">
        <option value="">选择一个国家</option>
        <option value="CHINA">China</option>
        <option value="USA">USA</option>
        <option value="UK">UK</option>
    </select>
    <input type="submit" value="提交">
</form>
</body>
</html>
<?php
$q = isset($_POST['q'])?$_POST['q']:'';
if (is_array($q)){
    $sites = array(
        'CHINA'=>'你选择了中国',
        'USA'=>'你选择了美国',
        'UK'=>'你选择了英国'
    );
    foreach ($q as $key){
        echo $sites[$key].PHP_EOL;
    }
}else{
    echo "选择有误!";
}
?>

G E T 、 _GET、 G?ET_POST 和 $_REQUEST 的区别?

$_GET 变量接受所有以 get 方式发送的请求,及浏览器地址栏中的 ? 之后的内容。

$_POST 变量接受所有以 post 方式发送的请求,例如,一个 form 以 method=post 提交,提交后 php 会处理 post 过来的全部变量。

$_REQUEST 支持两种方式发送过来的请求,即 postget 它都可以接受,显示不显示要看传递方法,get 会显示在 url 中(有字符数限制),post 不会在 url 中显示,可以传递任意多的数据(只要服务器支持)。

PHP表单验证

我们应该尽可能的对用户的输入进行验证(通过客户端脚本)。浏览器验证速度更快,并且可以减轻服务器的压力。

如果用户输入需要插入数据库,您应该考虑使用服务器验证。在服务器验证表单的一种好的方式是,把表单的数据传给当前页面(异步提交的方式更好),而不是跳转到不同的页面。这样用户就可以在同一张表单页面得到错误信息。用户也就更容易发现错误了。

下面贴一段常规的表单验证代码

<!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>

image-20220128193031841

文本字段

“名字”, “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"]);?>">
Note什么是 $_SERVER[“PHP_SELF”] 变量? $_SERVER[“PHP_SELF”]是超级全局变量,返回当前正在执行脚本的文件名,与 document root相关。

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

Note什么是 htmlspecialchars()方法? htmlspecialchars() 函数把一些预定义的字符转换为 HTML 实体。预定义的字符是:& (和号) 成为 &" (双引号) 成为 "’ (单引号) 成为 '< (小于) 成为 <> (大于) 成为 >

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

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

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

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

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

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

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

<form method="post" action="test_form.php">

这样做就很好了。

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

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

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

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

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

请注意, 任何JavaScript代码可以添加在

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

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

form 代码如下所示:

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

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

<form method="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 变量

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

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

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

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

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

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

$_POST 变量

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

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

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

$_REQUEST 变量

预定义的 $_REQUEST 变量包含了 G E T 、 _GET、 G?ET_POST 和 $_COOKIE 的内容。

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

PHP - 多维数组

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

在多维数组中,主数组中的每一个元素也可以是一个数组,子数组中的每一个元素也可以是一个数组。

<?php
$sites = array
(
    "runoob"=>array
    (
        "菜鸟教程",
        "http://www.runoob.com"
    ),
    "google"=>array
    (
        "Google 搜索",
        "http://www.google.com"
    ),
    "taobao"=>array
    (
        "淘宝",
        "http://www.taobao.com"
    )
);
print("<pre>"); // 格式化输出数组
print_r($sites);
print("</pre>");
?>

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/10/21
2016.10.21
2016-10-21
format 字符说明返回值例子
d月份中的第几天,有前导零的 2 位数字0131
D星期中的第几天,文本表示,3 个字母MonSun
j月份中的第几天,没有前导零131
l("L"的小写字母)星期几,完整的文本格式SundaySaturday
NISO-8601 格式数字表示的星期中的第几天(PHP 5.1.0 新加)1(表示星期一)到 7(表示星期天)
S每月天数后面的英文后缀,2 个字符stndrd 或者 th。可以和 j 一起用
w星期中的第几天,数字表示0(表示星期天)到 6(表示星期六)
z年份中的第几天0365
星期
WISO-8601 格式年份中的第几周,每周从星期一开始(PHP 4.1.0 新加的)例如:42(当年的第 42 周)
F月份,完整的文本格式,例如 January 或者 MarchJanuaryDecember
m数字表示的月份,有前导零0112
M三个字母缩写表示的月份JanDec
n数字表示的月份,没有前导零112
t给定月份所应有的天数2831
L是否为闰年如果是闰年为 1,否则为 0
oISO-8601 格式年份数字。这和 Y 的值相同,只除了如果 ISO 的星期数(W)属于前一年或下一年,则用那一年。(PHP 5.1.0 新加)Examples: 1999 or 2003
Y4 位数字完整表示的年份例如:19992003
y2 位数字表示的年份例如:9903
时间
a小写的上午和下午值ampm
A大写的上午和下午值AMPM
BSwatch Internet 标准时000999
g小时,12 小时格式,没有前导零112
G小时,24 小时格式,没有前导零023
h小时,12 小时格式,有前导零0112
H小时,24 小时格式,有前导零0023
i有前导零的分钟数0059>
s秒数,有前导零0059>
u毫秒 (PHP 5.2.2 新加)。需要注意的是 date() 函数总是返回 000000 因为它只接受 integer 参数, 而 DateTime::format() 才支持毫秒。示例: 654321
时区
e时区标识(PHP 5.1.0 新加)例如:UTCGMTAtlantic/Azores
I是否为夏令时如果是夏令时为 1,否则为 0
O与格林威治时间相差的小时数例如:+0200
P与格林威治时间(GMT)的差别,小时和分钟之间有冒号分隔(PHP 5.1.3 新加)例如:+02:00
T本机所在的时区例如:ESTMDT(【译者注】在 Windows 下为完整文本格式,例如"Eastern Standard Time",中文版会显示"中国标准时间")。
Z时差偏移量的秒数。UTC 西边的时区偏移量总是负的,UTC 东边的时区偏移量总是正的。-4320043200
完整的日期/时间
cISO 8601 格式的日期(PHP 5 新加)2004-02-12T15:19:21+00:00
rRFC 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 向执行流引用关键文件。这有助于提高应用程序的安全性和完整性,在某个关键文件意外丢失的情况下。

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

PHP 文件处理


fopen() 函数用于在 PHP 中打开文件。


打开文件

fopen() 函数用于在 PHP 中打开文件。

此函数的第一个参数含有要打开的文件的名称,第二个参数规定了使用哪种模式来打开文件:

<html>
<body>

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

</body>
</html>

文件可能通过下列模式来打开:

模式描述
r只读。在文件的开头开始。
r+读/写。在文件的开头开始。
w只写。打开并清空文件的内容;如果文件不存在,则创建新文件。
w+读/写。打开并清空文件的内容;如果文件不存在,则创建新文件。
a追加。打开并向文件末尾进行写操作,如果文件不存在,则创建新文件。
a+读/追加。通过向文件末尾写内容,来保持文件内容。
x只写。创建新文件。如果文件已存在,则返回 FALSE 和一个错误。
x+读/写。创建新文件。如果文件已存在,则返回 FALSE 和一个错误。

**注释:**如果 fopen() 函数无法打开指定文件,则返回 0 (false)。

关闭文件

fclose() 函数用于关闭打开的文件:

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

//执行一些代码

fclose($file);
?>

检测文件末尾(EOF)

feof() 函数检测是否已到达文件末尾(EOF)。

在循环遍历未知长度的数据时,feof() 函数很有用。

**注释:**在 w 、a 和 x 模式下,您无法读取打开的文件!

if (feof($file)) echo "文件结尾";

逐行读取文件

fgets() 函数用于从文件中逐行读取文件。

**注释:**在调用该函数之后,文件指针会移动到下一行。

实例

下面的实例逐行读取文件,直到文件末尾为止:

<?php
$file = fopen("welcome.txt", "r") or exit("无法打开文件!");
// 读取文件每一行,直到文件结尾
while(!feof($file))
{
  echo fgets($file). "<br>";
}
fclose($file);
?>

逐字符读取文件

fgetc() 函数用于从文件中逐字符地读取文件。

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

实例

下面的实例逐字符地读取文件,直到文件末尾为止:

<?php
$file=fopen("welcome.txt","r") or exit("无法打开文件!");
while (!feof($file))
{
  echo fgetc($file);
}
fclose($file);
?>

PHP 文件上传


通过 PHP,可以把文件上传到服务器。

本章节实例在 test 项目下完成,目录结构为:

test
|-----upload             # 文件上传的目录
|-----form.html          # 表单文件
|-----upload_file.php    # php 上传代码

创建一个文件上传表单

允许用户从表单上传文件是非常有用的。

请看下面这个供上传文件的 HTML 表单:

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

<form action="upload_file.php" method="post" enctype="multipart/form-data">
    <label for="file">文件名:</label>
    <input type="file" name="file" id="file"><br>
    <input type="submit" name="submit" value="提交">
</form>

</body>
</html>

将以上代码保存到 form.html 文件中。

有关上面的 HTML 表单的一些注意项列举如下:

  • 标签的 enctype 属性规定了在提交表单时要使用哪种内容类型。在表单需要二进制数据时,比如文件内容,请使用 “multipart/form-data”。
  • 标签的 type=“file” 属性规定了应该把输入作为文件来处理。举例来说,当在浏览器中预览时,会看到输入框旁边有一个浏览按钮。

**注释:**允许用户上传文件是一个巨大的安全风险。请仅仅允许可信的用户执行文件上传操作。


创建上传脚本

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

<?php
if ($_FILES["file"]["error"] > 0)
{
    echo "错误:" . $_FILES["file"]["error"] . "<br>";
}
else
{
    echo "上传文件名: " . $_FILES["file"]["name"] . "<br>";
    echo "文件类型: " . $_FILES["file"]["type"] . "<br>";
    echo "文件大小: " . ($_FILES["file"]["size"] / 1024) . " kB<br>";
    echo "文件临时存储的位置: " . $_FILES["file"]["tmp_name"];
}
?>

通过使用 PHP 的全局数组 $_FILES,你可以从客户计算机向远程服务器上传文件。

第一个参数是表单的 input name,第二个下标可以是 “name”、“type”、“size”、“tmp_name” 或 “error”。如下所示:

  • $_FILES[“file”][“name”] - 上传文件的名称
  • $_FILES[“file”][“type”] - 上传文件的类型
  • $_FILES[“file”][“size”] - 上传文件的大小,以字节计
  • $_FILES[“file”][“tmp_name”] - 存储在服务器的文件的临时副本的名称
  • $_FILES[“file”][“error”] - 由文件上传导致的错误代码

这是一种非常简单文件上传方式。基于安全方面的考虑,您应当增加有关允许哪些用户上传文件的限制。

上传限制

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

<?php
// 允许上传的图片后缀
$allowedExts = array("gif", "jpeg", "jpg", "png");
$temp = explode(".", $_FILES["file"]["name"]);
$extension = end($temp);        // 获取文件后缀名
if ((($_FILES["file"]["type"] == "image/gif")
|| ($_FILES["file"]["type"] == "image/jpeg")
|| ($_FILES["file"]["type"] == "image/jpg")
|| ($_FILES["file"]["type"] == "image/pjpeg")
|| ($_FILES["file"]["type"] == "image/x-png")
|| ($_FILES["file"]["type"] == "image/png"))
&& ($_FILES["file"]["size"] < 204800)    // 小于 200 kb
&& in_array($extension, $allowedExts))
{
    if ($_FILES["file"]["error"] > 0)
    {
        echo "错误:: " . $_FILES["file"]["error"] . "<br>";
    }
    else
    {
        echo "上传文件名: " . $_FILES["file"]["name"] . "<br>";
        echo "文件类型: " . $_FILES["file"]["type"] . "<br>";
        echo "文件大小: " . ($_FILES["file"]["size"] / 1024) . " kB<br>";
        echo "文件临时存储的位置: " . $_FILES["file"]["tmp_name"];
    }
}
else
{
    echo "非法的文件格式";
}
?>

保存被上传的文件

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

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

<?php
//1、提取文件后缀名
$allowExts = array('gif','jpg','png','jpeg');   //允许上传的文件后缀
$temp = explode('.',$_FILES['file']['name']);
$extension = end($temp);

//2、判断文件是否符合上传
if ((($_FILES['file']['type'] == 'image/gif')
    || ($_FILES['file']['type'] == 'image/jpg')
    ||($_FILES['file']['type'] == 'image/jpeg')
    ||($_FILES['file']['type'] == 'image/png')
    ||($_FILES['file']['type'] == 'image/x-png')
    ||($_FILES['file']['type'] == 'image/pjpeg'))
    && ($_FILES['file']['size'] < 4092000) //小于4MB
    && in_array($extension,$allowExts))
{
    //3、符合:
    //  判断文件有错误
    if ($_FILES['file']['error'] > 0)
    {
        echo '文件错误:'.$_FILES['file']['error'].'<br>';
    }
    //  打印文件信息
    else
    {
        echo '上传文件名:'.$_FILES['file']['name'].'<br>';
        echo '文件类型:'.$_FILES['file']['type'].'<br>';
        echo '文件大小:'.$_FILES['file']['size'].'B'.'<br>';
        echo '文件临时存储的位置:'.$_FILES['file']['tmp_name'].'<br>';
    }
    //  复制文件到upload目录下
    //先判断当前目录下的upload目录是否存在该文件
    //若不存在upload目录,则需创建upload目录,并赋予权限为777
    if (file_exists('../upload'.$_FILES['file']['name']))
    {
        echo $_FILES['file']['name'].'文件已经存在'.'<br>';
    }
    else
    {
        //如果upload目录不存在该文件,则将该文件上传到upload目录下
        move_uploaded_file($_FILES['file']['tmp_name'],'../upload/'.$_FILES['file']['name']);
        echo '文件存储于'.'../upload/'.$_FILES['file']['name'];
    }
}

//4、不符合则打印非法文件格式
else
{
    echo '文件格式错误!';
}

image-20220130133348305

PHP Cookie


cookie 常用于识别用户。


Cookie 是什么?

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


如何创建 Cookie?

setcookie() 函数用于设置 cookie。

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

语法

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>
<meta charset="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>
<meta charset="utf-8">
<title>菜鸟教程(runoob.com)</title>
</head>
<body>

<form action="welcome.php" method="post">
名字: <input type="text" name="name">
年龄: <input type="text" name="age">
<input type="submit">
</form>

</body>
</html>

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

<html>
<head>
<meta charset="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() 函数必须位于 标签之前:

实例

<?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>

销毁 Session

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

unset() 函数用于释放指定的 session 变量,session_destroy() 函数彻底销毁 session::

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

关于验证登陆用户是否有 admin 权限的例子。

如果用户成功登陆的话,储存一个登陆成功的凭证的 session,即admin=true

<?php
//  表单提交后...
$posts = $_POST;
//  清除一些空白符号
foreach ($posts as $key => $value) {
    $posts[$key] = trim($value);
}
$password = md5($posts["password"]);
$username = $posts["username"]; 

$query = "SELECT `username` FROM `user` WHERE `password` = '$password' AND `username` = '$username'";
//  取得查询结果
$userInfo = $DB->getRow($query); 

if (!empty($userInfo)) {
    //  当验证通过后,启动 Se
index.html

> 验证 session里的`admin`是否为 `true`

<?php
//  防止全局变量造成安全隐患
$admin = false;
//  启动会话,这步必不可少
session_start();
//  判断是否登陆
if (isset($_SESSION["admin"]) && $_SESSION["admin"] === true) {
    echo "您已经成功登陆";
} else {
    //  验证失败,将 $_SESSION["admin"] 置为 false
    $_SESSION["admin"] = false;
    die("您无权访问");
}
?>

PHP Secure E-mails

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 错误处理


在 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 中发生错误时调用该函数。

该函数必须有能力处理至少两个参数 (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: 变量值必须小于等于 1
in /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 异常处理


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


异常是什么

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 类的一个扩展。

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

我们开始创建 customException 类:

<?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” 过滤器来过滤变量。

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”,它不在指定的范围内,以上代码的输出将是:

不是一个合法的整数

验证输入

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

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

然后我们用 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 变量如下所示:

img


过滤多个输入

表单通常由多个输入字段组成。为了避免对 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"));
?>

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

img

实例解释

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

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

PHP 高级过滤器


检测一个数字是否在一个范围内

以下实例使用了 filter_var() 函数来检测一个 INT 型的变量是否在 1 到 200 内:

实例

<?php
$int = 122;
$min = 1;
$max = 200;

if (filter_var($int, FILTER_VALIDATE_INT, array("options" => array("min_range"=>$min, "max_range"=>$max))) === false) {
  echo("变量值不在合法范围内");
} else {
  echo("变量值在合法范围内");
}
?>

检测 IPv6 地址

以下实例使用了 filter_var() 函数来检测一个 $ip 变量是否是 IPv6 地址:

<?php
$ip = "2001:0db8:85a3:08d3:1319:8a2e:0370:7334";

if (!filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) === false) {
    echo("$ip 是一个 IPv6 地址");
} else {
    echo("$ip 不是一个 IPv6 地址");
}
?>

检测 URL - 必须包含QUERY_STRING(查询字符串)

以下实例使用了 filter_var() 函数来检测 $url 是否包含查询字符串:

<?php
$url = "http://www.runoob.com";

if (!filter_var($url, FILTER_VALIDATE_URL, FILTER_FLAG_QUERY_REQUIRED) === false) {
    echo("$url 是一个合法的 URL");
} else {
    echo("$url 不是一个合法的 URL");
}
?>

移除 ASCII 值大于 127 的字符

以下实例使用了 filter_var() 函数来移除字符串中 ASCII 值大于 127 的字符,同样它也能移除 HTML 标签:

<?php
$str = "<h1>Hello World???!</h1>";

$newstr = filter_var($str, FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_HIGH);
echo $newstr;
?>

PHP JSON

本章节我们将为大家介绍如何使用 PHP 语言来编码和解码 JSON 对象。


环境配置

在 php5.2.0 及以上版本已经内置 JSON 扩展。

JSON 函数

函数描述
json_encode对变量进行 JSON 编码
json_decode对 JSON 格式的字符串进行解码,转换为 PHP 变量
json_last_error返回最后发生的错误

json_encode

PHP json_encode() 用于对变量进行 JSON 编码,该函数如果执行成功返回 JSON 数据,否则返回 FALSE 。

语法

string json_encode ( $value [, $options = 0 ] )

参数

  • value: 要编码的值。该函数只对 UTF-8 编码的数据有效。

  • options:由以下常量组成的二进制掩码 JSON_HEX_QUOT, JSON_HEX_TAG, JSON_HEX_AMP, JSON_HEX_APOS, JSON_NUMERIC_CHECK, JSON_PRETTY_PRINT, JSON_UNESCAPED_SLASHES, JSON_FORCE_OBJECT, JSON_PRESERVE_ZERO_FRACTION, JSON_UNESCAPED_UNICODE, JSON_PARTIAL_OUTPUT_ON_ERROR。

    要注意的是 JSON_UNESCAPED_UNICODE 选项,如果我们不希望中文被编码,可以添加该选项。

实例

以下实例演示了如何将 PHP 数组转换为 JSON 格式数据:

<?php
   $arr = array('a' => 1, 'b' => 2, 'c' => 3, 'd' => 4, 'e' => 5);
   echo json_encode($arr);
?>

以上代码执行结果为:

{"a":1,"b":2,"c":3,"d":4,"e":5}

以下实例演示了如何将 PHP 对象转换为 JSON 格式数据:

实例

<?php
  class Emp {
    public $name = "";
    public $hobbies = "";
    public $birthdate = "";
  }
  $e = new Emp();
  $e->name = "sachin";
  $e->hobbies = "sports";
  $e->birthdate = date('m/d/Y h:i:s a', "8/5/1974 12:20:03 p");
  $e->birthdate = date('m/d/Y h:i:s a', strtotime("8/5/1974 12:20:03"));

  echo json_encode($e);
?>

以上代码执行结果为:

{"name":"sachin","hobbies":"sports","birthdate":"08\/05\/1974 12:20:03 pm"}

使用 JSON_UNESCAPED_UNICODE 选项

<?php
   $arr = array('runoob' => '菜鸟教程', 'taobao' => '淘宝网');
   echo json_encode($arr); // 编码中文
   echo PHP_EOL;  // 换行符
   echo json_encode($arr, JSON_UNESCAPED_UNICODE);  // 不编码中文
?>

以上代码执行结果为:

{"runoob":"\u83dc\u9e1f\u6559\u7a0b","taobao":"\u6dd8\u5b9d\u7f51"}
{"runoob":"菜鸟教程","taobao":"淘宝网"}

json_decode

PHP json_decode() 函数用于对 JSON 格式的字符串进行解码,并转换为 PHP 变量。

语法

mixed json_decode ($json_string [,$assoc = false [, $depth = 512 [, $options = 0 ]]])

参数

  • json_string: 待解码的 JSON 字符串,必须是 UTF-8 编码数据
  • assoc: 当该参数为 TRUE 时,将返回数组,FALSE 时返回对象。
  • depth: 整数类型的参数,它指定递归深度
  • options: 二进制掩码,目前只支持 JSON_BIGINT_AS_STRING 。

实例

以下实例演示了如何解码 JSON 数据:

实例

<?php
  $json = '{"a":1,"b":2,"c":3,"d":4,"e":5}';

  var_dump(json_decode($json));
  var_dump(json_decode($json, **true**));
?>

以上代码执行结果为:

object(stdClass)#1 (5) {
    ["a"] => int(1)
    ["b"] => int(2)
    ["c"] => int(3)
    ["d"] => int(4)
    ["e"] => int(5)
}

array(5) {
    ["a"] => int(1)
    ["b"] => int(2)
    ["c"] => int(3)
    ["d"] => int(4)
    ["e"] => int(5)
}

PHP7新特性

PHP 标量类型与返回值类型声明

标量类型声明

默认情况下,所有的PHP文件都处于弱类型校验模式。

PHP 7 增加了标量类型声明的特性,标量类型声明有两种模式:

  • 强制模式 (默认)
  • 严格模式

标量类型声明语法格式:

declare(strict_types=1); 

代码中通过指定 strict_types的值(1或者0),1表示严格类型校验模式,作用于函数调用和返回语句;0表示弱类型校验模式。

可以使用的类型参数有:

  • int
  • float
  • bool
  • string
  • interfaces
  • array
  • callable

强制模式实例

实例

<?php
// 强制模式
function sum(int ...$ints)
{
  return array_sum($ints);
}

print(sum(2, '3', 4.1));
?>

以上程序执行输出结果为:

9

实例汇总将参数 4.1 转换为整数 4 后再相加。

严格模式实例

<?php
// 严格模式
declare(strict_types=1);

function sum(int ...$ints)
{
  return array_sum($ints);
}

print(sum(2, '3', 4.1));
?>

以上程序由于采用了严格模式,所以如果参数中出现不适整数的类型会报错,执行输出结果为:

PHP Fatal error:  Uncaught TypeError: Argument 2 passed to sum() must be of the type integer, string given, called in……

返回类型声明

PHP 7 增加了对返回类型声明的支持,返回类型声明指明了函数返回值的类型。

可以声明的返回类型有:

  • int
  • float
  • bool
  • string
  • interfaces
  • array
  • callable

返回类型声明实例

实例中,要求返回结果为整数:

<?php
declare(strict_types=1);

function returnIntValue(int $value): int
{
   return $value;
}

print(returnIntValue(5));
?>

以上程序执行输出结果为:

5

特殊情况

对于标量类型声明:在严格模式下,有一种例外的情况是:当函数参数为float时,传入int型变量不会跑出typeerror,而是正常执行,在返回类型声明中,也是同样的:

<?php
declare(strict_types = 1);
function test (float $inter) {
    return $inter;
}

echo test(2); // 结果为2

function test1(int $inte) : float{
    return $inte;
}
echo test1(1); // 结果为1
?>

PHP 常量数组

在 PHP 5.6 中仅能通过 const 定义常量数组,PHP 7 可以通过 define() 来定义。

实例

<?php
// 使用 define 函数来定义数组
define('sites', [
   'Google',
   'Runoob',
   'Taobao'
]);

print(sites[1]);
?>

以上程序执行输出结果为:

Runoob

PHP Closure::call()

PHP 7 的 Closure::call() 有着更好的性能,将一个闭包函数动态绑定到一个新的对象实例并调用执行该函数。

<?php
class A {
  private $x = 1;
}

// PHP 7 之前版本定义闭包函数代码
$getXCB = function() {
  return $this->x;
};

// 闭包函数绑定到类 A 上
$getX = $getXCB->bindTo(new A, 'A'); 

echo $getX();
print(PHP_EOL);

// PHP 7+ 代码
$getX = function() {
  return $this->x;
};
echo $getX->call(new A);
?>

以上程序执行输出结果为:

1
1

PHP 过滤 unserialize()

PHP 7 增加了可以为 unserialize() 提供过滤的特性,可以防止非法数据进行代码注入,提供了更安全的反序列化数据。

<?php
class MyClass1 { 
   public $obj1prop;   
}
class MyClass2 {
   public $obj2prop;
}


$obj1 = new MyClass1();
$obj1->obj1prop = 1;
$obj2 = new MyClass2();
$obj2->obj2prop = 2;

$serializedObj1 = serialize($obj1);
$serializedObj2 = serialize($obj2);

// 默认行为是接收所有类
// 第二个参数可以忽略
// 如果 allowed_classes 设置为 false, unserialize 会将所有对象转换为 __PHP_Incomplete_Class 对象
$data = unserialize($serializedObj1 , ["allowed_classes" => true]);

// 转换所有对象到 __PHP_Incomplete_Class 对象,只允许 MyClass1 和 MyClass2 转换到 __PHP_Incomplete_Class
$data2 = unserialize($serializedObj2 , ["allowed_classes" => ["MyClass1", "MyClass2"]]);

print($data->obj1prop);
print(PHP_EOL);
print($data2->obj2prop);
?>

以上程序执行输出结果为:

1
2

PHP 7 异常

PHP 7 异常用于向下兼容及增强旧的assert()函数。它能在生产环境中实现零成本的断言,并且提供抛出自定义异常及错误的能力。

老版本的API出于兼容目的将继续被维护,assert()现在是一个语言结构,它允许第一个参数是一个表达式,而不仅仅是一个待计算的 string或一个待测试的boolean。


assert() 配置

配置项默认值可选值
zend.assertions11 - 生成和执行代码 (开发模式)0 - 生成代码,但在执行时跳过它**-1** - 不生成代码 (生产环境)
assert.exception01 - 断言失败时抛出,可以抛出异常对象,如果没有提供异常,则抛出 AssertionError 对象实例。0 - 使用或生成 Throwable, 仅仅是基于对象生成的警告而不是抛出对象(与 PHP 5 兼容)

参数

  • assertion

    断言。在 PHP 5 中,是一个用于执行的字符串或者用于测试的布尔值。在 PHP 7 中,可以是一个返回任何值的表达式, 它将被执行结果用于指明断言是否成功。

  • description

    如果 assertion 失败了,选项 description 将会包括在失败信息里。

  • exception

    在 PHP 7 中,第二个参数可以是一个 Throwable 对象,而不是一个字符串,如果断言失败且启用了 assert.exception 该对象将被抛出。

实例

将 zend.assertions 设置为 0:

<?php
ini_set('zend.assertions', 0);

assert(true == false);
echo 'Hi!';
?>

以上程序执行输出结果为:

Hi!

将 zend.assertions 设置为 1,assert.exception 设置为 1:

<?php
ini_set('zend.assertions', 1);
ini_set('assert.exception', 1);

assert(true == false);
echo 'Hi!';
?>

以上程序执行输出结果为:

Fatal error: Uncaught AssertionError: assert(true == false) in -:2
Stack trace:
#0 -(2): assert(false, 'assert(true == ...')
#1 {main}
  thrown in - on line 2

PHP 7 Session 选项

PHP 7 session_start() 函数可以接收一个数组作为参数,可以覆盖 php.ini 中 session 的配置项。

这个特性也引入了一个新的 php.ini 设置(session.lazy_write), 默认情况下设置为 true,意味着 session 数据只在发生变化时才写入。

除了常规的会话配置指示项, 还可以在此数组中包含 read_and_close 选项。如果将此选项的值设置为 TRUE, 那么会话文件会在读取完毕之后马上关闭, 因此,可以在会话数据没有变动的时候,避免不必要的文件锁。

实例

把 cache_limiter 设置为私有的,同时在阅读完 session 后立即关闭。

<?php
session_start([
   'cache_limiter' => 'private',
   'read_and_close' => true,
]);
?>
  • bool
  • string
  • interfaces
  • array
  • callable

强制模式实例

实例

<?php
// 强制模式
function sum(int ...$ints)
{
  return array_sum($ints);
}

print(sum(2, '3', 4.1));
?>

以上程序执行输出结果为:

9

实例汇总将参数 4.1 转换为整数 4 后再相加。

严格模式实例

<?php
// 严格模式
declare(strict_types=1);

function sum(int ...$ints)
{
  return array_sum($ints);
}

print(sum(2, '3', 4.1));
?>

以上程序由于采用了严格模式,所以如果参数中出现不适整数的类型会报错,执行输出结果为:

PHP Fatal error:  Uncaught TypeError: Argument 2 passed to sum() must be of the type integer, string given, called in……

返回类型声明

PHP 7 增加了对返回类型声明的支持,返回类型声明指明了函数返回值的类型。

可以声明的返回类型有:

  • int
  • float
  • bool
  • string
  • interfaces
  • array
  • callable

返回类型声明实例

实例中,要求返回结果为整数:

<?php
declare(strict_types=1);

function returnIntValue(int $value): int
{
   return $value;
}

print(returnIntValue(5));
?>

以上程序执行输出结果为:

5

特殊情况

对于标量类型声明:在严格模式下,有一种例外的情况是:当函数参数为float时,传入int型变量不会跑出typeerror,而是正常执行,在返回类型声明中,也是同样的:

<?php
declare(strict_types = 1);
function test (float $inter) {
    return $inter;
}

echo test(2); // 结果为2

function test1(int $inte) : float{
    return $inte;
}
echo test1(1); // 结果为1
?>

PHP 常量数组

在 PHP 5.6 中仅能通过 const 定义常量数组,PHP 7 可以通过 define() 来定义。

实例

<?php
// 使用 define 函数来定义数组
define('sites', [
   'Google',
   'Runoob',
   'Taobao'
]);

print(sites[1]);
?>

以上程序执行输出结果为:

Runoob

PHP Closure::call()

PHP 7 的 Closure::call() 有着更好的性能,将一个闭包函数动态绑定到一个新的对象实例并调用执行该函数。

<?php
class A {
  private $x = 1;
}

// PHP 7 之前版本定义闭包函数代码
$getXCB = function() {
  return $this->x;
};

// 闭包函数绑定到类 A 上
$getX = $getXCB->bindTo(new A, 'A'); 

echo $getX();
print(PHP_EOL);

// PHP 7+ 代码
$getX = function() {
  return $this->x;
};
echo $getX->call(new A);
?>

以上程序执行输出结果为:

1
1

PHP 过滤 unserialize()

PHP 7 增加了可以为 unserialize() 提供过滤的特性,可以防止非法数据进行代码注入,提供了更安全的反序列化数据。

<?php
class MyClass1 { 
   public $obj1prop;   
}
class MyClass2 {
   public $obj2prop;
}


$obj1 = new MyClass1();
$obj1->obj1prop = 1;
$obj2 = new MyClass2();
$obj2->obj2prop = 2;

$serializedObj1 = serialize($obj1);
$serializedObj2 = serialize($obj2);

// 默认行为是接收所有类
// 第二个参数可以忽略
// 如果 allowed_classes 设置为 false, unserialize 会将所有对象转换为 __PHP_Incomplete_Class 对象
$data = unserialize($serializedObj1 , ["allowed_classes" => true]);

// 转换所有对象到 __PHP_Incomplete_Class 对象,只允许 MyClass1 和 MyClass2 转换到 __PHP_Incomplete_Class
$data2 = unserialize($serializedObj2 , ["allowed_classes" => ["MyClass1", "MyClass2"]]);

print($data->obj1prop);
print(PHP_EOL);
print($data2->obj2prop);
?>

以上程序执行输出结果为:

1
2

PHP 7 异常

PHP 7 异常用于向下兼容及增强旧的assert()函数。它能在生产环境中实现零成本的断言,并且提供抛出自定义异常及错误的能力。

老版本的API出于兼容目的将继续被维护,assert()现在是一个语言结构,它允许第一个参数是一个表达式,而不仅仅是一个待计算的 string或一个待测试的boolean。


assert() 配置

配置项默认值可选值
zend.assertions11 - 生成和执行代码 (开发模式)0 - 生成代码,但在执行时跳过它**-1** - 不生成代码 (生产环境)
assert.exception01 - 断言失败时抛出,可以抛出异常对象,如果没有提供异常,则抛出 AssertionError 对象实例。0 - 使用或生成 Throwable, 仅仅是基于对象生成的警告而不是抛出对象(与 PHP 5 兼容)

参数

  • assertion

    断言。在 PHP 5 中,是一个用于执行的字符串或者用于测试的布尔值。在 PHP 7 中,可以是一个返回任何值的表达式, 它将被执行结果用于指明断言是否成功。

  • description

    如果 assertion 失败了,选项 description 将会包括在失败信息里。

  • exception

    在 PHP 7 中,第二个参数可以是一个 Throwable 对象,而不是一个字符串,如果断言失败且启用了 assert.exception 该对象将被抛出。

实例

将 zend.assertions 设置为 0:

<?php
ini_set('zend.assertions', 0);

assert(true == false);
echo 'Hi!';
?>

以上程序执行输出结果为:

Hi!

将 zend.assertions 设置为 1,assert.exception 设置为 1:

<?php
ini_set('zend.assertions', 1);
ini_set('assert.exception', 1);

assert(true == false);
echo 'Hi!';
?>

以上程序执行输出结果为:

Fatal error: Uncaught AssertionError: assert(true == false) in -:2
Stack trace:
#0 -(2): assert(false, 'assert(true == ...')
#1 {main}
  thrown in - on line 2

PHP 7 Session 选项

PHP 7 session_start() 函数可以接收一个数组作为参数,可以覆盖 php.ini 中 session 的配置项。

这个特性也引入了一个新的 php.ini 设置(session.lazy_write), 默认情况下设置为 true,意味着 session 数据只在发生变化时才写入。

除了常规的会话配置指示项, 还可以在此数组中包含 read_and_close 选项。如果将此选项的值设置为 TRUE, 那么会话文件会在读取完毕之后马上关闭, 因此,可以在会话数据没有变动的时候,避免不必要的文件锁。

实例

把 cache_limiter 设置为私有的,同时在阅读完 session 后立即关闭。

<?php
session_start([
   'cache_limiter' => 'private',
   'read_and_close' => true,
]);
?>
  PHP知识库 最新文章
Laravel 下实现 Google 2fa 验证
UUCTF WP
DASCTF10月 web
XAMPP任意命令执行提升权限漏洞(CVE-2020-
[GYCTF2020]Easyphp
iwebsec靶场 代码执行关卡通关笔记
多个线程同步执行,多个线程依次执行,多个
php 没事记录下常用方法 (TP5.1)
php之jwt
2021-09-18
上一篇文章      下一篇文章      查看所有文章
加:2022-04-18 17:20:14  更:2022-04-18 17:21:33 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/18 15:01:40-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码