《细说PHP》学习笔记
PHP的基本语法
PHP第一个程序
1 | <html> |
PHP语言标记
PHP语言嵌入HTML中的位置
可以将PHP嵌入到以后缀名为.php的HTML文件中的任何地方,只要在文件中使用<?php起始和终止标识符?>就会开启PHP模式
使用四对不同的开始和结束标记
- 1.xml风格(标准风格推荐使用)
1
2
3<?php
echo"这是xml风格的标记";
?>
xml风格的标记是常用的标记,也是推荐使用的标记,服务器不能禁用,该风格的标记在xml,xhtml中都可以使用。
2.脚本风格
1
2
3<script languange="php">
echo'这是脚本风格的标记';
</script>3.简短风格
1
<?这是简短风格的标记;?>
注:需要在php.ini中设置short _open_tag=on,默认是on,或者在 PHP 编译时加入了 Cenable-short-tags 选项。(PHP 3版本还可以通过 short_tags() 函数激活使用短标记。)
- 4.asp风格
1
2
3<%
echo'这是asp风格的标记';
%>
注:需要在 php.ini 配置文件中开启 asp_tags = on;
上面asp风格与简短风格需要在php.ini中设置下。默认是不支持的。
指令分隔符 “分号”
1 | <?php |
程序注释
PHP支持C,C++和Unix shell风格的注释,PHP的注释符号有三种:以“/”和“/”闭合的多行注释符,以及用“//”和“#”开始的单行注释符。注释一定要卸载被注释代码的上面或是右面,不要写到代码的下面。
1 | <?php |
注:多行注释是无法嵌套的,但是在多行注释里可以包含单行注释,在单行注释中也可以包括多行注释。
变量
变量的声明
在PHP中的声明变量必须是使用“$”后面跟上变量名表示,使用赋值操作符(=)给变量赋值。
1 | <?php |
在变量的使用范围周期内,我们可以通过借助unset()函数释放指定的变量,用isset()函数检测变量是否设置和使用empty()函数检查一个变量是否为空。
变量的命名
变量名是严格区分大小写的,相同单词组成的变量,但大小写不同就是不同变量。
一个有效的变量名由字母或者下划线开头,后面跟上任意数量的字母,数字,或者下划线。
正则表达式为:‘[a-zA-Z\x7f-\xff][a-zA-Z0-9\x7f-\xff]*’1
2
3
4
5
6
7
8
9<?php
$name="a";
$Name="b";
$NAME="c";
echo name; //输出 a
echo Name; //输出 b
echo NAME; //输出 c
?>
可变变量
一个变量的变量名可以动态地设置和使用,一个普通的变量通过声明来设置,而一个可变变量获取一个普通变量的值作为这个可变变量的变量名。
1 | <?php |
变量的引用赋值
新的变量简单的引用了原始变量,改动新的变量将影响到原始变量,反之亦然。使用引用复制,简单地将一个“&”符号加到将要赋值的变量前。
1 | <?php |
变量的类型
数据类型
可以使用函数var_dump()查看某表达式的值和类型。
布尔型(boolean)
以下值被认为是FALSE,所有其他值都被认为是TRUE(包括任何资源)。
- 布尔值FALSE
- 整型值0为假,-1和其他非零值一样,被认为TRUE
- 浮点型值0.0
- 空白字符串和字符串“0”
- 没有成员变量的数组
- 没有单元的对象(仅适用与PHP4)
- 特殊类型NULL(包括尚未设定的变量)
整型(integer)
整数数值有最大的使用范围,整型数的字长和平台有关,如果给定的一个数超出了integer的这个范围,将会被解释为float。同时如果执行的运算结果超出了integer这个范围,也会返回float.
浮点型(float)
浮点数的字长也是和平台相关,允许表示范围在1.7E-308-1.7E+38之间,精确到小数点后15位。不要比较两个浮点数是否相等。如果确实需要更高的精度,应该是使用任意精度数学函数或者gmp()函数。
字符串(string)
字符串可以使用单引号、双引号、界定符三种字面上的方法定义。
- 1.单引号:指定一个简单字符串的最简单的方法就是用单引号括起来。
- 2.双引号:更多的特殊字符的转义,可以解析双引号里面的包含变量。
- 3.界定符(<<<):在<<<之后提供一个标识符开始,然后是包含的字符串,最后是同样的标识符结束。结束标识符必须从行的第一列开始,并且后面除了分号以外不能包含任何其他的字符,空格以及空白制表符都不可以。
数组(array)
用array()语言结构来新建一个array,它接受一定数量用逗号分隔的key=>value参数对。
key 可以是integer或者string value可以是任何值1
2
3
4
5
6<?php
$arr=array("foo"=>"bar",12=>true);
print_r($arr); //使用print_r()函数查看数组中的全部内容
echo $arr["foo"]; //通过数组下标访问数组中的单个数据
echo $arr[12];
?>
对象(object)
一个对象类型的变量,是由一组属性值和一组方法构成的。要初始化一个对象,用new语句将对象实例到一个变量中。
1 | <?php |
NULL类型
特殊的NULL值表示一个变量没有值,NULL类型唯一可能的值就是NULL。NULL不表示空格,也不表示零,也不是空字符串,而是表示一个变量的值为空。NULL不区分大小写,在下列情况下一个变量被认为是NULL:
- 将变量直接赋值为NULL
- 声明的变量尚未被赋值
- 被unset()函数销毁的变量
伪类型介绍
- mixed:说明一个参数可以接受多种不同的类型。
- number:说明一个参数可以是intger或者是float
- callback:
常量
常量的定义和使用
在PHP中是通过使用defin()函数来定义常量的。大小写敏感.遵循PHP标识符的命名规则。1
2
3<?php
define("name",value)
?>
预定义常量
PHP预定于了一系列常量,可以在程序中直接使用来完成一些特殊的功能。
1 | <?php |
PHP中的运算符
类型
- 算术运算符
- 字符串运算符
- 赋值运算符
- 比较运算符
- 逻辑运算符
- 位运算符
- 其他运算符
字符串运算符
在PHP中字符串运算符只有一个,是英文的句号(“.”),也叫做连接运算符
其他运算符
- 三元运算符(?:)提供简单的逻辑判断
- 执行判断符(``)执行操作系统命令,并将其输出信息返回
- 错误控制运算符(@)当将其放置在一个PHP表达式之前,该表达式可能产生的任何警告信息都会被忽略。
PHP语言结构
分支结构
在程序中使用分支结构可以有以下几种形式:
- 单一条件分支结构
- 双向条件分支结构
- 多向条件分支结构
- 巢状条件分支结构
单一条件分支结构(if)
1 | if(表达式) |
双向条件分支结构(else子句)
1 | if(表达式) |
多向条件分支结构(elseif子句)
1 | if(表达式1) |
多向条件分支结构(switch语句)
1 | switch(表达式) |
巢状条件分支结构
1 | if(表达式1){ |
循环结构
PHP中提供三种循环
- while循环
- do-while循环
- for循环
while语句
1 | while(表达式) |
do…while循环
1 | do{ |
for语句
1 | for(初始化:条件表达式; 增量){ |
PHP中的函数
函数声明
1 | function 函数名 ([参数1, 参数2,... 参数n]) |
参数的传递
按值传递参数
通过参数列表传递信息到函数按引用传递参数
在参数的面前预先加上符号“&”即可函数的默认参数
可变长度参数列表
使用func_get_args()函数将所有传递给脚本函数的参数当做一个数组返回
也可以使用func_num_args()函数返回参数的总数,和使用func_get_arg()函数接收一个数字参数,返回指定的函数。
1 | <?php |
变量函数
变量函数也称作可变函数。如果一个变量名后有圆括号,PHP将寻找与变量的值同名的函数,并且将尝试执行它。
1 | <?php |
PHP中的数组与数据结构
数组的概述
数组的分类
在PHP中,根据数组提供下标的不同方式,将数组分为索引数组和关联数组
- 索引数组的索引值是整数
- 关联数组的索引值是字符串
数组的定义
直接复制的方式声明数组
1 | $数组变量名[索引值]=资料内容 |
可以使用print_r()或者var_dump()函数打印数组中所有元素的内容。
1 | print_r($contact1); //输出数组$contact1中所有元素的下标和值 |
在声明数组变量时,还可以在下标中使用数字和字符串混合。
在PHP中,索引数组的下标可以是非连续的值,只要在初始化时指定非连续的下标即可。如果指定的下标志已经声明过,则属于对变量重新赋值。如果没有指定索引值的元素与指定索引值的元素混在一起赋值时,没有指定索引值的元素的默认索引值,将紧跟指定索引值元素中的最高的索引值递增,
使用array()语言结构新建数组
语法格式:1
$数组变量名=array(key1=>value,key2=>value, ... ... keyN=>valueN);
多维数组的声明
1 | //使用array()语句结构将联系人列表中所有数据声明为一个二维数组,默认下标是顺序数字索引 |
数组的遍历
使用for语句循环遍历数组
在PHP中,不仅可以指定非连续的数字索引值,而且还存在以字符串为下标的关联数组。所以在PHP中很少使用for语句循环遍历数组。
使用foreach语句遍历数组
- 第一种语法格式:
每次循环中,当前元素的值被赋给变量$value($value可以是自定义的任意变量),并且把数组内部的指针向后移动一步,因此下一次循环中将会得到该数组的下一个元素,只要数组的结尾停止循环,结束数组的遍历。
1 | foreach(array_expression as $value){ |
- 第二种语法格式
当前元素的键名和值都会被赋给变量$key和$value1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28foreach(array_expression as $key=>$value){
循环体
}
<?php
//声明一个一维的关联数组$contact, 使用“=>”运算符指定了每个元素的字符串下标
$contact = array("ID" => 1,
"姓名" => "高某",
"公司" => "A公司",
"地址" => "北京市",
"电话" => "(010)98765432",
"EMAIL" => "gao@php.com"
);
//以HTML列表的方式输出数组中每个元素的信息
echo '<dl>一个联系人信息:';
foreach($contact as $key => $value){ //使用foreach的第二种格式,可以获取数组元素的键/值
echo "<dd> $key : $value </dd>"; //输出每个元素的键/值对
}
echo '</dl>';
?>
一个联系人信息:
ID : 1
姓名 : 高某
公司 : A公司
地址 : 北京市
电话 : (010)98765432
EMAIL : gao@php.com
联合使用list()、each()和while()循环遍历数组
each()函数
each()函数需要传递一个数组作为参数,返回数组中当前元素的键/值对,并向后移动数组指针到下一个元素的位置。list()函数
list()用一步操作给一组变量进行赋值,即把数组中的值赋给一些变量。list()仅能用与数字索引从0开始。
1 | list(mixed varname,mixed ...)=array_expression //list()语句的语法格式 |
list()是通过“=”运算符以赋值的方式,将数组中每个元素的值,对应的赋给list()函数中的每个参数。list()函数又将它中的每个参数转换成直接可以在脚本中使用的比变量。
- each()和list()结合
1
2
3
4
5<?php
$contact=array("ID"=>1,"名字"=>"高某","公司"=>"A");
list($key=>$value)=each($contact);
echo "$key=>$value";
?>
使用数组的内部指针控制函数遍历数组
数组的内部指针是数组内部的组织机制,指向一个数组中的某个元素,默认是指向数组中第一个元素,通过移动或改变指针的位置,可以访问数组中的任意元素。
对于数组指针的控制PHP提供以下几个内建函数可以利用。
- current():是取得目前指针位置的内容资料
- key():用来读取目前指针所指向资料的索引值
- next():将数组中的内部指针移动到下一个单元
- prev():将数组的内部指针倒回一位
- end():将数组的内部指针指向最后一个元素
- rest():将目前指针无条件移至第一个索引位置
预定义数组
预定义数组 | 说明 |
---|---|
$_SERVER | 变量由Web服务器设定或者直接与当前脚本的执行环境相关联 |
$_ENV | 执行环境提交至脚本的变量 |
$_GET | 经由URL请求提交至脚本的变量 |
$_POST | 经由HTTP POST方法提交至脚本的变量 |
$_REQUEST | 经由GET,POST和COOKIE机制提交至脚本的变量,因此该数组并不值得信任 |
$_FILES | 经由HTTP POST文件上传而提交至脚本的变量 |
$_COOKIE | 经由HTTP Cookies方法提交至脚本的变量 |
$_SESSION | 当前注册给脚本会话的变量 |
$GLOBALS | 包含一个引用指向每个当前脚本的全局范围内有效的变量。该数组的键名为全局变量的名称 |
服务器变量:$_SERVER
$_SERVER是一个包含诸如头信息,路径和脚本位置的数组。这是自动全局变量,在所有的脚本中都有效,在函数或对象的方法中不需要使用global关键字访问它。
1 | <?php |
环境变量:$_ENV
$_ENV数组中的内容是在PHP解析器运行是,从PHP所在服务器中的环境变量转变为PHP全局变量的。
1 | <?php |
### HTTP GET变量:$_GET
$_GET数组也是超全局变量数组,是通过HTTP GET方法传递的变量组成的数组。它属于外部变量,即在服务器页面中通过$_GET超全局数组获取URL或表单的GET方式传递过来的参数。
HTTP POST变量:$_POST
$_POST数组也是超全局变量数组,通过HTTP POST方法传递的变量组成的数组,是自动全局变量,在所有的脚本中都有效,在函数或对象的方法也不需要使用global关键字访问它。
1 | <html> |
request变量:$_REQUEST
此关联数组包含\$_GET,\$_POST和\$_COOKIE中的全部内容。不管是POST还是GET方法提交的所有数据都可以通过\$_REQUEST获得。但是\$_REQUEST的速度比较慢。
HTTP文件上传变量:$_FILES
使用表单的file输入域上传文件时,必须使用POST提交。
$_COOKIE超全局数组是经由HTTP Cookies方法提交至脚本的变量。
Session变量:$_SESSION
在PHP5中,会话控制是在服务器端使用session跟踪用户。
Global变量:$_GLOBALS
GLOBALS是由所有已定义的全局变量组成的数组,变量名就是该数组的索引。
数组的相关处理函数
数组的键/值操作函数
在PHP中,数组的每个元素都是由键/值对组成,通过元素的键来访问对应键的值。
函数array_value()
array_value()函数的作用是返回数组中所有元素的值。使用非常容易,只有一个必选参数,规定传入给定的数组,返回一个包含给定数组中所有值的数组。
1 | <?php |
函数array_key()
array_key()函数的作用是返回数组所有的键名。本函数中有一个必需参数和两个可选参数,其中函数的原型如下:1
array array_key(array input[,mixed search_value[,bool strict]])
如果指定了可选参数search_value,则只返回指定该值的键名,否则input数组中的所有键名都会被返回,自PHP5起,可以用strict参数来进行全等比较。需要传入一个布尔型的值,FALSE为默认不依赖类型。如果传入TRUE值则根据类型返回带有指定值的键名。
1 | <?php |
函数in_array()
in_array()函数的作用是检查数组中是否存在某个值,即在数组中搜索给定的值。本函数中有三个参数,前两个参数为必需的,最后一个参数为可选的。1
bool in_array(mixed needle,array haystack[,bool strict])
第一个参数needle为规定要在数组中搜索的值,第二个参数haystack是规定要被搜索的数组,如果给定的值needle存在与数组haystack中则返回TRUE,函数只有在元素存在于数组且数据类型与给定相同时才返回TRUE。如果没有在数组中找到参数,函数返回FALSE。要注意如果needle参数是字符串,且strict参数设置为TRUE,则搜索区分大小写。
1 | <?php |
也可以使用array_search()函数进行检索。该函数与in_array()的参数相同,搜索给定的值存在则返回相应的键名,也支持对数据类型的严格判断。1
2
3
4
5
6
7<?php
$lamp=array("a"=>"Linux","b"=>"Apache","c"=>"MySQL","d"=>"PHP");
echo array_search("PHP",$lamp); //输出:d (在数组$lamp中,存在字符串"php"则输出下标d)
$a=array("a"=>"8","b"=>8,"c"=>"8");
echo array_search(8,$a,true); //输出:b (严格按类型检索,整型8对应的下标为b)
?>
使用array_key_exists()函数还可以检查给定的键名或者索引是否存在于数组中。因为一个数组中键名必须是唯一的,所以不需要对其数据类型进行判断。也可以使用isset()函数完成对数组中的键名或者索引进行检查,但isset()对数组中为NULL的值不会返回TRUE,而array_key_exists()会。
1 | <?php |
函数array_flip()
array_flip()的作用是交换数组中的键和值。返回一个反转后的数组,如果同一值出现了多次,则最后一个键名将作为它的值,覆盖前面出现的元素。如果原数组中的值的数据类型不是字符串或者整数,函数将报错。该函数只有一个参数。1
array array_flip(array trans)
参数是必需的,要求输入一个要处理的数组,返回该数组中每个元素的键和值交换后的数组。
1 | <?php |
函数array_reverse()
array_reverse()作用是将原数组中的元素顺序翻转,创建新的数组并返回。该函数有两个参数,1
array array_reverse(array array[,bool preserve_keys])
第一个参数是必需项,接受一个数组作为输入。第二个参数是可选项。如果指定为TURE,则元素的键名保持不变,否则键名将丢失。
1 | <?php |
统计数组元素的个数和唯一性
函数count()
函数count()的作用是计算数组中的元素数目或对象中的属性个数。对于数组,返回其元素的个数,对于其他值则返回1。如果参数是变量而变量没有定义或是变量包含一个空的数组,该函数会返回0.1
int count(mixed var[,int mode])
其中第一个参数是必需的,传入要计数的数组或对象。第二个参数是可选的,规定函数的模式是否递归地计算多维数组中的数组的元素个数。可能的值是0和1,0是默认值,不检测多维数组,1则检测多维数组。1
2
3
4
5
6
7
8
9
10
11<?php
$lamp = array("Linux", "Apache", "MySQL", "PHP");
echo count($lamp); //输出数组的个数为:4
//声明一个二维数组,统计数组中元素的个数
$web= array('lamp' => array('Linux', 'Apache', 'MySQL','PHP'),
'j2ee' => array('Unix', 'Tomcat','Oracle','JSP'));
echo count($web, 1); //第二个参数的模式为1则计算多维数组的个数,输出10
echo count($web); //默认模式为0,不计算多维数组的个数,输出2
?>
函数array_count_values()
array_count_values()函数用于统计数组中所有值出现的次数。该函数只有一个参数。1
array array_count_values(array input)
参数规定输入一个数组,返回一个数组,其元素的键名是原数组的值,键值是该值在原数组中出现的次数。
1 | <?php |
函数array_unique()
array_unique()函数用于删除数组中重复的值,并返回没有重复值的新数组。该函数只有一个参数。1
array array_unique(array array)
参数需要接收一个数组,数组中几个元素的值相等时,只保留第一个元素,其他的元素被删除,并返回的新数组中键名不变。array_unique()先将值作为字符串排序,然后对每个值只保留第一个遇到的键名,接着忽略所有后面的键名。这并不意味着在未排序的array中同一个值的出现的键名会被保留。1
2
3
4<?php
$a=array("a"=>"php","b"=>"mysql","c"=>"php"); //声明一个带有重复值的数组
print_r(array_unique($a)); //删除重复值后输出:Array ([a] => php [b] => mysql)
?>
使用回调函数处理数组的函数
函数array_filter()
array_filter()函数用回调函数过滤数组中的元素,返回按用户自定义函数过滤后的新数组。
该函数的第一个参数是必选项,要求输入一个被过滤的数组。第二个参数是可选项,将用户自定义的函数名以字符串形式传入。如果自定义过滤函数返回true,则被操作的数组的当前值就会被包含在返回的结果数值中,并将结果组成一个新的数组。
1 | <?php |
函数array_walk()
array_walk()函数对数组中的每个元素应用回调函数处理。如果成功则返回TRUE,否则返回FLASE。
该函数的第一个参数是必选项,要求输入一个指定的回调函数处理的数组。第二个参数也是必选项,传入用户定义的回调函数,用于操作传入第一个参数的数组。array_walk()函数依次将第一个参数的数组中的每一个值传递到这个自定义的函数中。自定义的这个回调函数中应该接收两个参数,一次传入进来元素的值作为第一个参数,键名作为第二个参数。
1 | <?php |
函数array_map()
array_map()函数可以处理多个数组,将回调函数作用到给定数组的元素上,返回用户自定义函数作用后的数组,array_map()是任意参数列表函数,回调函数接受的参数数目应该和传递给array_map()函数的数组数目一致。
该函数中的第一个参数是必选项,是用户自定义的回调函数的名称,或者是Null。第二个参数也是必选项,输入要处理的函数。也可以接着输入多个数组作为可选参数。
1 | <?php |
数组的排序函数
排序函数 | 说明 |
---|---|
sort() | 按由小到大的升序对给定数组的值排序 |
rsort() | 对数组的元素按照键值进行由大到小的逆向排序 |
usort() | 使用用户自定义的回调函数对数组排序 |
asort() | 对数组进行由小到大排序并保持索引关系 |
uaort() | 使用用户自定义的比较回调函数对数组中的值进行排序并保持索引关联 |
ksort() | 按照键名对数组进行由小到大的排序,为数组值保留原来的键 |
krsort() | 将数组按照由大到小的键逆向排序,为数组值保留原来的键 |
ukrsort() | 使用用户自定义的比较回调函数对数组中的键名进行排序 |
natsort() | 用自然顺序算法对给定数组中的元素排序 |
natcasesort() | 用不区分大小写的自然顺序算法对给定数组中的元素排序 |
array_multisort() | 对多个数组或多维数组进行排序 |
简单的数组排序函数
这两个函数既可以按数字大小排列也可以按字母顺序排列,并具有相同的参数列表。其函数的原型分别如下:1
2bool sort(array $array[,int sort_flags])
bool rsort(array $array[,int sort_flags])
第一个参数是必需的,指定需要排序的数组。后一个参数是可选的,给出了排序的方式,可以用以下值改变排序的行为:
- SORT_REGULAR-是默认值,将自动识别数组元素的类型进行排序
- SORT_NUMERIC-用于数字元素的排序
- SORT_STRING-用于字符串元素的排序
- SORT_LOCALE_STRING-根据当前的locale设置来把元素当做字符串比较
sort()函数对数组中的元素值按照由小到大顺序进行排序,rsort()函数则按照由大到小的顺序对元素的值进行排序。1
2
3
4
5
6
7
8
9<?php
$data = array(5,8,1,7,2); //声明一个数组$data, 存放5个整数元素
sort($data); //使用sort()函数将数组$data中的元素值按照由小到大顺序进行排序
print_r($data); //输出:Array ( [0] => 1 [1] => 2 [2] => 5 [3] => 7 [4] => 8 )
rsort($data); //使用rsort()函数将数组$data按照由大到小的顺序对元素的值进行排序
print_r($data); //输出:Array ( [0] => 8 [1] => 7 [2] => 5 [3] => 2 [4] => 1 )
?>
根据键名对数组排序
根据键名对数组重新排序,ksort()和krsort()函数可以实现。ksort()函数按照键名对数组进行由小到大的排序,krsort()函数和ksort()相反,排序后为数组值保留原来的键。使用的格式与sort()和rsort()相同。1
2
3
4
5
6
7
8
9<?php
$data = array(5=>"five",8=>"eight",1=>"one",7=>"seven",2=>"two"); //声明一个键值混乱的数组
ksort($data); //使用ksort()函数按照键名对数组$data进行由小到大的排序
print_r($data); //输出:Array ( [1] => one [2] => two [5] => five [7] => seven [8] => eight )
krsort($data); //使用krsort()函数按照键名对数组$data进行由大到小的排序
print_r($data); //输出:Array ( [8] => eight [7] => seven [5] => five [2] => two [1] => one )
?>
根据元素的值对数组排序
使用数组中元素的值进行排序来取代键值排序。asort()和arsort()函数将保留原有键名和值的关系。1
2
3
4
5
6
7
8
9
10
11
12<?php
$data = array("l"=>"Linux", "a"=>"Apache","m"=>"MySQL","p"=>"PHP");
asort($data); //使用asort()函数将数组$data按元素的值升序排序,并保留原有的键名和值
print_r($data); //输出:Array ( [a] => Apache [l] => Linux [m] => MySQL [p] => PHP )
arsort($data); //使用arsort()函数将数组$data按元素的值降序排序,并保留原有的键名和值
print_r($data); //输出:Array ( [p] => PHP [m] => MySQL [l] => Linux [a] => Apache )
rsort($data); //使用asort()函数将数组$data按元素的值降序排序,但原始键名被忽略
print_r($data); //输出:Array ( [0] => PHP [1] => MySQL [2] => Linux [3] => Apache )
?>
根据“自然排序”法对数组排序
“自然排序”法,即数字从1到9的排序方法,字母从a到z的排序方法,短者优先。使用natsort()进行“自然排序”法的数组排序,该函数的排序结果是忽略键名的。函数netcasesort()是用“自然排序”算法对数组尽心不区分大小写的排序。
1 | <?php |
根据用户自定的规则对数组排序
usort()、uasort()和uksort()等函数提供了可以通过创建你自己的比较函数作为回调函数的数组排序函数。它们的使用格式一样,并具有相同的参数列表,区别在于对键还是值进行排序。
这三个函数将用户自定义的比较函数对一个数组中的值进行排序。如果要排序的数组需要用一种不寻常的标准进行排序,那么应该使用这几个函数。在自定义的回调函数中,需要两个参数,分别依次传入数组中连续的两个元素。比较函数比较在第一个参数被认为小于、等于或大于第二个参数时分别返回一个小于,等于或大于零的整数。
1 | <?php |
多维数组的排序
php允许在多维数组上执行一些比较复杂的排序。可以使用array_multisort()函数对多个数组或多维数组进行排序,或者根据某一维或多维对多维数组进行排序。1
bool array_multisort(array ar1[,mixed arg[,mixed ...[,array ...]]])
该函数如果成功则返回TRUE,失败则返回FLASE。第一个参数是要排序的主要数组。数组中的值比较为相同的话,就按照下一个输入数组中相应值的大小来排序,以此类推。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16<?php
$data = array( //声明一个$data数组,模拟了一个行和列数组
array("id" => 1, "soft" => "Linux", "rating" => 3),
array("id" => 2, "soft" => "Apache", "rating" => 1),
array("id" => 3, "soft" => "MySQL", "rating" => 4),
array("id" => 4, "soft" => "PHP", "rating" => 2),
);
//使用foreach遍历创建两个数组作为array_multisort的参数
foreach ($data as $key => $value) {
$soft[$key] = $value["soft"]; //将$data中的每个数组元素中键值为soft的值形成数组$soft
$rating[$key] = $value["rating"]; //将每个数组元素中键值为rating的值形成数组$rating
}
array_multisort($rating, $soft, $data); //使用array_multisort()函数传入三个数组进行排序
print_r($data); //输出排序后的二维数组
?>
拆分、合并、分解和接合数组
函数array_slice()
array_slice()函数的作用是在数组中根据条件取出一段值并返回。如果数组有字符串键,所返回的数组将保留键名。该函数可以设置四个参数。第一个参数array是必选项,调用时输入要处理的数组。第二个参数offset也是必须的参数,需要传入一个数组,规定取出元素的开始位置。如果是正数,则从前往后开始取,如果是负值,从后向前取offset绝对值数目的元素。
第三个参数是可选的,需要传入一个数值,规定被返回数组的长度,如果是负数则从后往前,选取该值绝对值数目的元素。如果未设置该值,则返回所有元素。第四个参数也是可选的,需要一个布尔类型的值,如果为TRUE值则所返回的数组将保留键名。设置为FLASE值,也是默认值将重新设置索引键值。注意的是,如果数组有字符串键,所返回的数组将保留键名。
1 | <?php |
函数array_splice()
array_splice()和array_slice()函数类似,选择数组中的一系列元素,但不返回,而是删除他们并用其他值代理。如果提供了第四个参数,则之前选中的那些元素将被第四个参数指定的数组取代,最后生成的数组将会返回。
第一个参数array为必选项,规定要处理的数组。第二个参数offset也是必选项,调用时传入数值。如果offset为正数,则输入数组中该值指定的偏移量开始移除,如果offset为负,则从输入数组末尾倒数该值指定的偏移量开始移除。第三个参数length是可选的,也需要一个数值,如果省略该参数,则移除数组中从offset到结尾的所有部分。如果指定了length并且为正值,则移除这么多元素。如果指定了length且为负值,则移除从offset到数组末尾倒数length为止中间所有的元素。第四个参数array也是可选的,被移除的元素由此数组中的元素替代。如果没有移除任何值,则此数组中的元素将插入到指定位置。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17<?php
$input = array("Linux", "Apache", "MySQL", "PHP");
array_splice($input, 2); //原数组中的第二个元素后到数组结尾都被删除
print_r($input); //输出:Array ( [0] => Linux [1] => Apache )
$input = array("Linux", "Apache", "MySQL", "PHP");
array_splice($input, 1, -1); //从第二个开始移除直到数组末尾倒数1个为止中间所有的元素
print_r($input); //输出:Array ( [0] => Linux [1] => PHP )
$input = array("Linux", "Apache", "MySQL", "PHP");
array_splice($input, 1, count($input), "web"); //从第二个元素到数组结尾都被第4个参数替代
print_r($input); //输出:Array ( [0] => Linux [1] => web )
$input = array("Linux", "Apache", "MySQL", "PHP");
array_splice($input, -1, 1, array("web", "www")); //最后一个元素被第4个参数数组替代
print_r($input); //输出:Array ( [0] => Linux [1] => Apache [2] => MySQL [3] => web [4] => www )
?>
函数array_combine()
array_combine()函数的作用是通过合并两个数组来创建一个新数组。其中的一个数组是键名,另一个数组的值为键值。如果其中一个数组为空,或者两个数组的元素个数不同,则该函数返回false。
该函数有两个参数且都是必选项,两个参数必须有相同数目的元素。1
2
3
4
5
6<?php
$a1=array("OS","WebServer","DataBase","Language"); //声明第一个数组作为参数1
$a2=array("Linux","Apache","MySQL","PHP"); //声明第二个数组作为参数2
print_r(array_combine($a1,$a2)); //使用arrray_combine()将两个数组合并
//输出:Array ( [OS] => Linux [WebServer] => Apache [DataBase] => MySQL [Language] => PHP )
?>
函数array_merge()
array_merge()函数的作用是把一个或多个数组合并为一个数组,如果键名有重复,该键的键值为最后一个键名的值(后面的覆盖前面的)。如果数组是数字索引的,则键名会以连续方式重新索引。注意如果仅仅向array_merge()函数输入了一个数组,且键名是整数,则该函数将返回带有整数键名的新数组,其键名以0开始进行重新索引。
该函数第一个参数是必选项,需要传入一个数组。可以有多个可选参数。但必须都是数组类型的数据。返回将多个数组合并后的新数组。1
2
3
4
5
6
7
8
9<?php
$a1=array("a"=>"Linux","b"=>"Apache");
$a2=array("c"=>"MySQL","b"=>"PHP");
print_r(array_merge($a1,$a2)); //输出: Array ( [a] => Linux [b] => PHP [c] => MySQL )
//仅使用一个数组参数则键名以0开始进行重新索引
$a=array(3=>"PHP",4=>"MySQL");
print_r(array_merge($a)); //输出:Array ( [0] => PHP [1] => MySQL )
?>
函数array_intersect()
array_intersect()函数的作用是返回两个数组的差集数组。该数组包括了所有在被比较的数组中,但是不在任何其他参数数组中的元素值。返回的数组中,键名保持不变。
第一个参数是必选项,传入与其他数组进行比较的数组。第二个参数也是必选项,传入与第一个数组进行比较的数组。可以有多个可选参数作为以后的参数,与第一个数组进行比较的数组。
1 | <?php |
函数array_diff()
array_diff()函数的作用是返回两个数组的差集数组。该数组包括了所有在被比较的数组中,但是不在任何其他参数数组中的元素值。在返回到数组中,键名保持不变。
第一个参数是必选项,传入与其他数组进行比较的数组。第二个参数也是必选项,传入与第一个数组进行比较的数组。第三个参数以后都是可选项,可用一个或任意多个数组与第一个数组进行比较,本函数仅有值用于比较。1
2
3
4
5<?php
$a1=array("Linux", "Apache", "MySQL", "PHP"); //声明第一个数组,作为比较的第一个参数
$a2=array("Linux", "Tomcat", "MySQL", "JSP"); //声明第二个数组,作为比较的第二个参数
print_r(array_diff($a1,$a2)); //输出:Array ( [1] => Apache [3] => PHP )
?>
数组与数据结构
使用数组实现堆栈
堆栈是数据结构的一种实现形式,是使用非常广泛的存储数据的一种容器。在堆栈这种容器中,最后压入的数据(进栈),将会被最先弹出(出栈)。即在数据存储事采用“先进后出”的数据结构。在PHP中,将数组当做一个栈,使用array_push()和array_pop()两个系统函数即可完成数据的进栈和出栈操作。
array_push()函数向第一个参数的数组尾部添加一个或者多个元素(入栈),然后返回新数组的长度。该函数等于多次调用$array[]=$value。
该函数的第一个参数是必选的,作为栈容器的第一个数组。第二个参数也是必选的,在第一个参数中的数组尾部添加的一个数据。还可以有多个可选参数,都可以添加到第一个参数的数组中的尾部,即入栈。但要注意计时数组中有字符串键名,添加的元素也始终是数字键,1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16<?php
$lamp=array("Web"); //声明一个数当作为栈,数为数组array_push()函数第一个参数
echo array_push($lamp,"Linux"); //将字符串”Linux”压入数组$lamp中,返回数组中元素个数2
print_r($lamp); //输出数组中(栈)成员:Array ( [0] => Web [1] => Linux )
//又连续压入三个数据到数组$lamp的尾部
echo array_push($lamp,"Apache", "MySQL", "PHP"); //输出栈中元素的长度为5
print_r($lamp); //输出: Array ( [0] => Web [1] => Linux [2] => Apache [3] => MySQL [4] => PHP )
$lamp=array("a"=>"Linux","b"=>"Apache"); //带有字符串键的数组
echo array_push($lamp,"MySQL","PHP"); //压入两个元素到数组的尾部, 输出栈长度为4
print_r($lamp); // Array ( [a] => Linux [b] => Apache [0] => MySQL [1] => PHP ) //也是数字键
$lamp["web"]="www"; //使用array_push()函数和使用这种直接赋值初使化数组的方式是一样的
print_r($lamp); //Array ( [a] => Linux [b] => Apache [0] => MySQL [1] => PHP [web] => www )
?>
array_pop()函数删除数组中的最后一个元素,即将数组最后一个单元弹出(出栈),并将数组的长度减1,如果数组为空(或者不是数组)将返回NULL。
该函数只有一个参数,即作为栈的数组。返回弹出的数组中最后一个元素的值。1
2
3
4
5
6
7
8
9<?php
$lamp=array("Linux","Apache","MySQL", "PHP"); //声明一个数组作为栈
echo array_pop($lamp); //弹出数组中最后的元素并返回被删除的值,输出PHP
print_r($lamp); //被弹出后的结果:Array ( [0] => Linux [1] => Apache [2] => MySQL )
echo array_pop($lamp); //再弹出数组中最后的元素并返回被删除的值,输出MySQL
print_r($lamp); //被弹出后的结果:Array ( [0] => Linux [1] => Apache )
?>
使用数组实现队列
PHP中的数组处理函数还可以将数组实现队列的操作。堆栈是“后劲先出”,而一个队列则允许在一段插入数据,在另一端删除数据,也就是实现最先进入队列的数据最先退出队列,即队列是“先进先出”的原则。
函数array_shift()可以实现删除数组中的第一个元素,并返回被删除元素的值。
该函数只有一个必选参数,其参数为实现队列的数组。将数组中的第一个单元一处并作为结构返回,并将数组的长度减1,还将所有其他的元素向前移动一位。所有的数字键名将改为0开始计数,字符串键名将保持不变。如果数组为空(或不是数组),则返回NULL1
2
3
4
5
6
7
8
9
10
11<?
//带有字符串键值的关联数组
$lamp=array("a"=>"Linux", "b"=>"Apache", "c"=>"MySQL", "d"=>"PHP");
echo array_shift($lamp); //删除数组第一个元素并返回,输出Linux
print_r ($lamp); //字符串键值保持不变:Array ( [b] => Apache [c] => MySQL [d] => PHP )
//带有数字键的索引数组
$lamp=array("Linux", "Apache", "MySQL", "PHP");
echo array_shift($lamp); //删除数组第一个元素并返回,输出Linux
print_r ($lamp); //数字下标重新索引Array ( [0] => Apache [1] => MySQL [2] => PHP )
?>
在PHP中还可以使用array_unshift()函数在队列数组的开头插入一个或者多个元素,该函数执行成功将返回插入元素个数,使用格式和函数array_push()是一样的。
其他有用的数组处理函数
函数array_rand()
array_rand()函数从数组中随机选出一个或多个元素,并返回。该函数有两个参数,第一个参数为必选项,它接受一个数组作为输入数组,从这个数组中随机选出一个或多个元素。第二个参数是一个可选的参数,指明了你想取出多少个元素,如果没有指定,默认从数组中取出一个元素。如果只取出一个,array_rand()函数返回一个随机元素的键名,否则就返回一个包含随机键名的数组。这样就可以随机从数组中取出键名和值。1
2
3
4
5
6
7
8<?php
$lamp=array("a"=>"Linux", "b"=>"Apache", "c"=>"MySQL", "d"=>"PHP");
echo array_rand($lamp,1); //随机从数组$lamp中取1个元素的键值,例如b
echo $lamp[array_rand($lamp)]."<br>"; //通过随机的一个元素的键值获取数组中一个元素的值
$key=array_rand($lamp,2); //随机从数组$lamp中取2个元素的键值赋给数组$key
echo $lamp[$key[0]]."<br>"; //通过数组$key中第一个值获取数组$lamp中一个元素的值
echo $lamp[$key[1]]."<br>"; //通过数组$key中第二个值获取数组$lamp中另个元素的值
?>
函数shuffle()
shuffle()函数把数组中的元素按随机顺序重新排列,即将数组中的顺序打乱。若成功则返回TRUE,否则返回FALSE。只需要一个数组作为参数,每执行一次则返回不同顺序的数组。1
2
3
4
5<?php
$lamp=array("a"=>"Linux", "b"=>"Apache", "c"=>"MySQL", "d"=>"PHP");
shuffle($lamp); //将传入的数组$lamp按随机顺序重新排列
print_r($lamp); //每执行一次shuffle()函数则返回不同顺序的数组
?>
函数array_sum()
array_sum()函数返回数组中所有值的总和。只需要换入一个数组作为必选参数即可。如果所有值都是整数,则返回一个整数值,如果其中有一个或多个值是浮点数,则返回浮点数。1
2
3
4<?php
$a=array(0=>"5",1=>"15",2=>"25");
echo array_sum($a); //使用array_sum()函数返回数组中元素的总各,输出:45
?>
函数range()
range()函数创建并返回一个包含指定范围的元素的数组。该函数需要三个参数,第一个参数first为必选项,规定数组元素的最小值。第二个参数second也是必选项,规定数组中元素的最大值。第三个参数step是可选的,规定元素之间的步进制,默认为1。该函数创建一个数组,包含从first到second(包含)之间的整数或字符。如果second比first小,则返回反序的数组。1
2
3
4
5
6
7
8
9
10<?php
$number = range(0,5); //使用range()函数声明元素值为0-5的数组
print_r ($number); //获得的数组输出Array ( [0] => 0 [1] => 1 [2] => 2 [3] => 3 [4] => 4 [5] => 5 )
$number = range(0,50,10); //使用range()函数声明元素值为0-50的数组,每个元素之间的步长为10
print_r ($number); //输出Array ( [0] => 0 [1] => 10 [2] => 20 [3] => 30 [4] => 40 [5] => 50 )
$letter = range("a","d"); //还可以使用range()函数声明元素连续的字母数组,声明字母a-d的数组
print_r ($letter); //获得的数组输出Array ( [0] => a [1] => b [2] => c [3] => d )
?>
PHP面向对象的程序设计
如何抽象一个类
类的声明
使用一个关键词class后面加上一个自定义的类别名称,以及加上一对花括号就可以了。有时也需要在class关键字的前面加一些修饰类的关键字。
1 | [一些修饰类的关键字]class 类名{ //使用class关键字加空格再加上类名,后面加上一对花括号 |
类名和变量名还有函数名命名规则相似,都需要遵守PHP中自定义名称的命名规则,如果由多个单词组成,习惯上每个单词的首字母要大写。另外类名的定义也要具有一定的意义,不要随便由几个字母组成。
成员属性
在类中直接声明变量就称为成员属性,可以在类中声明多个变量,即对象中有多个成员属性,每个变量都存储对象不同的属性信息。成员属性可以使用PHP中的标量类型和复合类型,所以也可以是其他类实例化的对象,但在类中使用资源和空类型没有意义。另外,虽然在声明成员属性时可以给变量赋予初值,但是在声明类时没有必要的。
1 | class Person{ |
在成员属性前可以加public,private,static等关键字来修饰,一旦成员属性有其他的关键字修饰就需要去掉”var”。
成员方法
在对象中需要声明一些可以操作本对象成员属性的一些方法,来完成对象的一些行为。在类中直接声明的函数就称为成员方法,可以在类中声明多个函数,即对象中就有多个成员方法。成员方法的声明和函数的声明完全一样,只不过可以加上一些关键字的修饰来控制成员方法的一些权限。
1 | <?php |
通过类实例化对象
创建对象
将类实例化成对象使用new关键字并在后面加上一个和类名同名的方法即可。1
$变量名=new 类名称([参数数列表]); //对象实例化格式
其中,“$变量名”是通过类所创建的一个对象的引用名称,将来通过这个引用来访问对象中的成员。new表明要创建一个新的对象,类名表示新对象的类型,而参数指定了类的构造方法用于初始化对象值。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19<?php
class Phone { //声明一个电话类Phone
//类中成员同上(略)
}
class Person { //声明一个人类Person
//类中成员同上(略)
}
//通过Person类实例化三个对象$person1、$person2、$person3
$person1=new Person(); //创建第一个Person类对象,引用名为$person1
$person2=new Person(); //创建第二个Person类对象,引用名为$person2
$person3=new Person(); //创建第三个Person类对象,引用名为$person3
//通过Phone类实例化三个对象$phone1、$phone2、$phone3
$phone1=new Phone(); //创建第一个Phone类对象,引用名为$phone1
$phone2=new Phone(); //创建第二个Phone类对象,引用名为$phone2
$phone3=new Phone(); //创建第三个Phone类对象,引用名为$phone3
?>
对象类型在内存中的分配
对象类型和整型、字符串等类型一样,也是PHP中的一种数据类型。逻辑上内存大体上被分为四段,分别为栈空间段、堆空间段、初始化数据段和代码段,程序中不同类型数据的声明将会被存放在不同的内存段里面。
栈空间段
栈的特点是空间小但被CPU访问的速度快,是用户存放程序中临时创建的变量。由于栈的后进先出特点,所以栈特别方便用来保存和恢复调用现场。
堆空间段
堆是用于存放进程运行中被动态分配的内存段,它大小并不固定,可动态扩张或所建。用于存储数据长度可变或占用内存比较大的数据。
数据段
数据段用来存放可执行文件中已初始化全局变量,就是存放程序静态分配的变量。
代码段
代码段是用来存放可执行文件的操作指令,也就是说它是可执行程序在内存中的镜像。代码段需要防止在运行时被非法修改,所以只准读取操作,不允许写入操作。
对象中成员的访问
对象中包含成员属性和成员方法,访问对象中的成员则包括成员属性的访问和成员方法的访问。而对成员属性的访问则又包括赋值操作和获取成员属性值的操作。访问对象中的成员和访问数组中的元素类似,只能通过对象的引用来访问对象中的每个成员。但还要使用一个特殊的运算符号“->”来完成对象成员的访问。1
2
3
4$引用名=new 类名称([参数数列表]); //对象实例化格式
$引用名->成员属性=值; //对象成员属性赋值的操作
echo $引用名->成员属性; //获取成员属性的值
$引用名->成员方法; //访问对象中的成员方法
只要是对象中的成员都要使用“对象引用名->属性”或“对象引用名->方法”形式访问。如果对象中的成员不是静态的,这是唯一的访问形式。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67<?php
class Person //声明一个人类Person,其中包含三个成员属性和两个成员方法
{
//下面是声明人的三个成员属性
var $name; //第一个成员属性$name定义人的名子
var $sex; //第二个成员性性$sex定义人的性别
var $age; //第三个成员性成定义人的年龄
//下面是声明人的两个成员方法
function say() //这个人可以说话的方法
{
echo "这个人在说话<br>"; //在方法体中可以有更多内容
}
function run() //这个人可以走路的方法
{
echo "这个人在走路<br>"; //在方法体中可以有更多内容
}
}
//下面三行通过new关键字实例化person类的三个实例对象
$person1=new Person(); //通过类Person创建第一个实例对象$person1
$person2=new Person(); //通过类person创建第二个实例对象$person2
$person3=new Person(); //通过类person创建第三个实例对象$person3
//下面三行是给$person1对象中属性初使化赋值
$person1->name="张三"; //将对象person1中的$name属性赋值为张三
$person1->sex="男"; //将对象person1中的$sex属性赋值为男
$person1->age=20; //将对象person1中的$age属性赋值为20
//下面三行是给$person2对象中属性初使化赋值
$person2->name="李四"; //将对象person2中的$name属性赋值为李四
$person2->sex="女"; //将对象person2中的$sex属性赋值为女
$person2->age=30; //将对象person2中的$age属性赋值为30
//下面三行是给$person3对象中属性初使化赋值
$person3->name="王五"; //将对象person3中的$name属性赋值为王五
$person3->sex="男"; //将对象person3中的$sex属性赋值为男
$person3->age=40; //将对象person3中的$age属性赋值为40
//下面三行是访问$person1对象中的成员属性
echo "person1对象的名子是:".$person1->name."<br>"; //输出对象$person1中的成员属性$name的值
echo "person1对象的性别是:".$person1->sex."<br>"; //输出对象$person1中的成员属性$sex的值
echo "person1对象的年龄是:".$person1->age."<br>"; //输出对象$person1中的成员属性$age的值
//下面两行访问$person1对象中的方法
$person1->say(); //访问第一个对象$person1中的成员方法say(),让第一个人说话
$person1->run(); //访问第一个对象$person1中的成员方法run(),让第一个人走路
//下面三行是访问$person2对象中的成员属性
echo "person2对象的名子是:".$person2->name."<br>"; //输出对象$person2中的成员属性$name的值
echo "person2对象的性别是:".$person2->sex."<br>"; //输出对象$person2中的成员属性$sex的值
echo "person2对象的年龄是:".$person2->age."<br>"; //输出对象$person2中的成员属性$age的值
//下面两行访问$person2对象中的方法
$person2->say(); //访问第二个对象$person2中的成员方法say(),让第二个人说话
$person2->run(); //访问第二个对象$person2中的成员方法run(),让第二个人走路
//下面三行是访问$person3对象中的成员属性
echo "person3对象的名子是:".$person3->name."<br>"; //输出对象$person3中的成员属性$name的值
echo "person3对象的性别是:".$person3->sex."<br>"; //输出对象$person3中的成员属性$sex的值
echo "person3对象的年龄是:".$person3->age."<br>"; //输出对象$person3中的成员属性$age的值
//下面两行访问$person3对象中的方法
$person3->say(); //访问第三个对象$person3中的成员方法say(),让第三个人说话
$person3->run(); //访问第三个对象$person3中的成员方法run(),让第三个人走路
?>
构造方法与析构方法
构造方法是对象创建完成后第一个被对象自动调用的方法,而析构方法是对象在销毁之前最后一个被对象自动调用的方法。
构造方法
当创建一个对象时,构造方法就会被自动调用一次,即每次使用关键字new来实例化对象时都会自动调用构造方法,不能主动通过对象的引用调用构造方法。所以通常使用构造方法执行一些有用的初始化任务。
在类中声明构造方法和声明其他的成员方法相似,但是构造方法的方法名称必须是以两个下划线开始的“__construct()”,这是PHP5中的变化,在PHP5之前的版本中,构造方法是方法名称必须与类名相同,这种方式在PHP5中仍然可以用。但在PHP5中很少声明和类名同名的构造方法了,这样做的好处是可以使构造函数独立于类名,当类名发生变化时不需要更改相应的构造函数名称
1 | function __construct([参数列表]){ //构造方法名称是以两个下划线开始的__construct() |
在PHP中,同一个类中只能声明一个构造方法。原因是构造方法名称是固定的,在PHP中不能声明同名的两个函数,所以也就没有构造方法重载。但可以在声明构造方法时使用默认参数,实现其他面向对象的编程语言中构造方法重载的功能。这样在创建对象时,如果在构造方法中没有传入参数则使用默认参数为成员属性进行初始化。
1 | <?php |
析构方法
PHP将在对象被销毁前自动调用析构方法。析构方法是PHP中新添加的内容,在PHP4中并没有提供。析构方法允许在销毁一个对象之前执行一些特定操作。
当堆内存段中的对象失去访问它的引用时,它就不能被访问了,也就称为垃圾对象了。通常对象的引用被赋予其他的值或者是在页面运行结束时,对象都会失去引用。
析构方法的声明格式与构造方法相似,在类中声明的析构方法名称也是固定的,也是以两个下划线开头的方法名“__destruct()”,而且析构函数不能带有任何参数。1
2function __destruct() //析构方法名称是以两个下划线开始的__destruct()
//方法体,通常用来完成一些在对象销毁前的清理任务
1 | <?php |
封装性
封装性就是把对象的成员属性和成员方法结合成一个独立的相同单位,并尽可能隐蔽对象的内部细节,包含两个含义:
- 把对象的全部成员属性和全部成员方法结合在一起,形参一个不可分隔的独立单位(即对象)。
- 信息隐蔽,即尽可能隐蔽对象的内部细节,对外形成一个边界,只保留有限的对外接口使之与外部发生联系。
设置私有成员
只要在声明成员属性或成员方法时,使用private关键字修饰就实现了对成员的封装。封装后的成员在对象的外部不能被访问,但在对象内部的成员方法中可以访问到自己对象内部被封装的成员属性和被封装的成员方法。达到了对对象成员保护的目的,只能是对象自己使用,其他人不可以访问自己的私有成员。
1 | <? |
私有成员的访问
对象中的成员属性一旦被private关键字封装成私有以后,就只能在对象内部的成员方法中使用。不能被对象外部直接赋值,也不能在对象外部直接获取私有属性的值。如果不让用户在对象外部设置私有属性的值,但可以获取私有属性的值。或者允许用户对私有属性赋值,但需要限制一些赋值的条件。解决方法就是在对象的内部声明一些操作私有属性的方法,再把这个方法通过public关键字设置为共有的访问权限。如果成员方法没有加任何访问控制修饰,默认就是public的,在任何地方都可以访问。这样,在对象外部就可以通过公有的方法作为访问接口,间接地访问对象内部私有成员。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49<?
class Person { //声明一个人类Person,其中包含的三个成员属性被封装起来
//下面是声明人的成员属性,全都使用了private关键字封装
private $name; //第一个成员属性$name定义人的名子,此属性被封装
private $sex; //第二个成员属性$sex定义人的性别,此属性被封装
private $age; //第三个成员属性$age定义人的年龄,此属性被封装
//声明一个构造方法,将来创建对象时,为对象的成员私有属性赋予初值
function __construct($name="", $sex="男", $age=1) {
$this->name = $name; //使用传入的参数$name为成员属性$this->name赋初值
$this->sex = $sex; //使用传入的参数$sex为成员属性$this->sex赋初值
$this->age = $age; //使用传入的参数$age为成员属性$this->age赋初值
}
public function getName() { //通过这个公有方法可以在对象外部获取私有属性$name的值
return $this->name; //返回对象的私有属性的值
}
public function setSex($sex) { //通过这个公有方法在对象外部为私有属性$sex设置值,但限制条件
if($sex=="男" || $sex=="女") //如果传入合法的值才为私有的属性赋值
$this->sex=$sex; //条成立则将参数传入的值赋给私有属性
}
public function setAge($age) { //通过这个公有方法在对象外部为私有属性$age设置值,但限制条件
if($age > 150 || $age <0) //如果设置不合理的年龄则函数不往下执行
return; //返回空值,退出函数
$this->age=$age; //执行此语句则重新为私有属性赋值
}
public function getAge(){ //通过这个公有方法可以在对象外部获取私有属性$name的值
if($this->age > 30) //如果年龄的成员属性大于30则返回虚假的年龄
return $this->age-10; //返回当前的年龄减去10岁
else //如果年龄在30岁以下则返回真实年龄
return $this->age; //返回当前的私有年龄属
}
//下面是声明人的成员公有方法,说出自己所有的私有属性
function say(){ //在类中声明说话的方法,使用$this访问自已对象内部的成员属性
echo "我的名子叫:".$this->name.", 性别:".$this->sex.", 我的年龄是:".$this->age."。<br>";
}
}
$person1=new Person("王五", "男", 40); //创建对象$person1
echo $person1->getName()."<br>"; //访问对象中的公有方法,获取对象中私有属性$name输出
$person1->setSex("女"); //通过公有的方法为私有属性$sex设置合法的值
$person1->setAge(200); //通过公有的方法为私有属性$age设置非法的值,赋值失败
echo $person1->getAge()."<br>"; //访问对象中的公有方法,获取对象中私有属性$age输出
$person1->say(); //访问对象中的公有方法,获取对象中所有的私有属性并输出
?>
set()、get()、isset()和unset()四种方法
在PHP5.1.0以后的版本中,预定义了两个方法“get()”和“set()”,用来完成对所有私有属性都能获取和赋值的操作,以及用来检查私有属性是否存在的方法“isset”和用来删除对象中私有属性的方法“unset()”.(以两个下划线开始)
魔术方法__set()
类中声明格式:1
void __set(string name,mixed value) //是以两个下划线开始的方法名,方法体的内容需要自定义
该方法的作用是在程序运行过程中为私有的成员属性设置值,它不需要有任何返回值。但它需要两个参数,第一个参数需要传入在为私有属性设置值时的属性名,第二个参数则需要传入为属性设置的值。而且这个方法不需要我们主动调用,可以在方法前面也加上private关键字修饰,防止用户直接去调用它。这个方法是在用户值为私用属性设置值时自动调用的。如果不在类中添加这个方法而直接为私有属性赋值,则会出现“不能访问某个私有属性”的错误。
1 | <?php |
魔法方法__get()
在类中声明的格式1
mixed __get(string name)
该方法的作用是在程序运行过程中,通过它可以在对象的外部获取私有成员后苏醒的值。它有一个必选的参数,需要传入在获取私有属性值时的属性名,并返回一个值,是在这个方法处理后的允许对象外部使用的值。而且这个方法也不需要我们主动调用,也可以在方法前面加上private关键字修饰,防止用户直接去调用它。如果不在类中添加这个方法而直接获取私有属性的值,也会出现”不能访问某个私有属性”的错误。
1 | <?php |
魔法方法isset()和unset()
isset()函数是用来测定变量是否存在的函数。传入一个变量作为参数,如果传入的变量存在则传回true,否则传回false。
1 | bool __isset(string name) //传入对象中的成员属性名作为参数,返回测定后的结果 |
unset()的作用是删除指定的变量,参数为要删除的变量名称。也可以使用这个函数在对象外部删除对象中的成员属性,但这个对象中的成员属性必须是公有的才可以直接删除。如果对象中的成员属性被封装,就需要在类中添加“unset()”方法,才可以在对象的外部使用“unset()”方法帮助我们间接地将私有的成员属性删除。也可以在“unset()”方法中限制一些条件,删除一些重要的属性。1
void __unset(string name)
1 | ?php |
继承性
类继承的应用
使用“extends”关键字实现多个类的单继承关系1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46<?php
class Person { //声明一个人类,定义人所具有的一些其本的属性和功能成员,做为父类
var $name; //声明一个存储人的名子的成员
var $sex; //声明一个存储人的性别的成员
var $age; //声明一个存储人的年龄的成员
function __construct($name="", $sex="男", $age=1) { //构造方法用来创建对象并初使化成员属性
$this->name = $name; //为成员属性name在创建对象时赋初值
$this->sex = $sex; //为成员属性sex在创建对象时赋初值
$this->age = $age; //为成员属性age在创建对象时赋初值
}
function say(){ //在人类在声明一个通用的说话方法,介绍一下自己
echo "我的名子叫:".$this->name.", 性别:".$this->sex.", 我的年龄是:".$this->age."。<br>";
}
function run() { //在人类是声明一个人的通用的走路方法
echo $this->name."正在走路。<br>";
}
}
class Student extends Person { //声明一个学生类,使用extends关键字扩展(继承)Person类
var $school; //在学生类中声明一个所在学校school的成员属性
function study() { //在学生类中声明一个学生可以学习的方法
echo $this->name."正在".$this->school."学习<br>";
}
}
class Teacher extends Student { //再声明一个教师类,使用extends关键字扩展(继承)Student类
var $wage; //在教师类中声明一个教师工资wage的成员属性
function teaching() { //在教师类中声明一个教师可以教学的方法
echo $this->name."正在".$this->school."教学,每月工资为".$this->wage."。<br>";
}
}
$teacher1=new Teacher("张三", "男", 40); //使用继承过来的构造方法创建一个教师对象
$teacher1->school="edu"; //将一个教师对象中的所在学校的成员属性school赋值
$teacher1->wage=3000; //将一个教师对象中的成员属性工资赋值
$teacher1->say(); //调用教师对象中的说话方法
$teacher1->study(); //调用教师对象中的学习方法
$teacher1->teaching(); //调用教师对象中的教学方法
?>
访问类型控制
PHP5支持如下三种访问修饰符,包括public、private和protected三种
公有的访问修饰符public
类中的成员将没有访问限制,所有的外部成员都可以访问这个类中的成员,在PHP 5之前的所有版本中,PHP中类的成员都是public的,而且在PHP5中如果类的成员没有指定成员访问修饰符,将被视为public。1
2
3
4var $property //声明成员属性时,没有使用访问控制的修饰符,默认就是public的成员
public $property //使用public修饰符,控制此成员属性为公有的
function fun(){...} //声明成员方法时,没有使用访问控制的修饰符,默认就是public的成员
public function fun(){...} //使用public修饰符,控制此成员方法为公有的
私有的访问修饰符private
当类中的成员被定义为private,对于同一类里的所有成员都没有访问限制,但对于该类的外部代码是不允许改变甚至操作的,对于该类的子类,也不能访问private修饰的成员。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20<?php
class MyClass { //声明一个类作为父类使用,将它的成员都声明为私有的
private $var1=100; //声明一个私有的成员属性并给初值为100
private function printHello() { //声明一个成员方法使用private关键字设置为私有的
echo "hello<br>"; //在方法中只有一条输出语句作为测试使用
}
}
class MyClass2 extends MyClass { //声明一个MyClass类的子类试图访问父类中的私有成员
function useProperty() //在类中声明一个公有方法,访问父类中的私有成员
{
echo "输出从父类继承过来的成员属性值".$this->var1."<br>"; //访问父类中的私有属性
$this->printHello(); //访问父类中的私有方法
}
}
$subObj=new MyClass2(); //初例化出子类对象
$subObj->useProperty(); //调用子类对象中的方法实现对父类私有成员的访问
?>
保护的访问修饰符protected
被修饰为protected的成员,对于该类的子类以及子类都有访问权限,可以进行属性、方法的读及写操作。但不能被该类的外部代码访问,以及该子类的外部代码也不具有访问其属性和方法的权限。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21<?php
class MyClass { //声明一个类作为父类使用,将它的成员都声明为保护的
protected $var1=100; //声明一个保护的成员属性并给初值为100
protected function printHello() { //声明一个成员方法使用protected关键字设置为保护的
echo "hello<br>"; //在方法中只有一条输出语句作为测试使用
}
}
class MyClass2 extends MyClass { //声明一个MyClass类的子类试图访问父类中的保护成员
function useProperty() //在类中声明一个公有方法,访问父类中的保护成员
{
echo "输出从父类继承过来的成员属性值".$this->var1."<br>"; //访问父类中受保护的属性
$this->printHello(); //访问父类中受保护的方法
}
}
$subObj=new MyClass2(); //初例化出子类对象
$subObj->useProperty(); //调用子类对象中的方法实现对父类保护的成员访问
echo $subObj->var1; //试图访问类中受保护的成员,结果出错
?>
常见的关键字和魔术方法
final关键字的应用
在PHP5中新增加了final关键字,它可以夹在类或类中方法前。但不能使用final标识成员属性,虽然final有常量的意思,但是PHP中定义常量是使用define()函数来完成。
final关键字的作用
- 使用final标识的类,不能被继承
- 在类中使用final标识的成员方法,在子类中不能被覆盖
1 | <?php |
static和const关键字的使用
static关键字
使用static关键字可以将类中的成员标识为静态的,即可以用来标识成员属性也可以标识成员方法。
类的静态属性非常类似于函数的全局变量。类中的静态成员是不需要对象而使用类名来直接访问的。1
2类名::静态成员属性名; //在类的外部和成员方法中都可以使用这种方法访问静态成员属性
类名::静态成员方法名(); //在类的外部的成员方法中都可以使用这个方法访问静态成员方法
在类中声明的成员方法中,也可以使用关键字“self”来访问其他静态成员。因为静态成员是属于类的,而不属于任何对象,所以不能用$this来引用,可以使用self关键词。
如果在类的外部访问类中的静态成员,可以使用对象引用和使用类名访问,但通常选择使用类名来访问。如果在类内部的成员方法中访问其他的静态成员,通常使用self的形式去访问,最好不要直接用类名称。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22<?php
class MyClass { //声明一个MyClass类,用来演示如何使用静态成员
static $count; //在类中声明一个静态成员属性count,用来统计对象被创建的次数
function __construct() { //每次创建一个对象就会自动调用一次这个构造方法
self::$count++; //使用self访问静态成员count,使其自增1
}
static function getCount() { //声明一个静态方法,在类外面直接使用类名就可以调用
return self::$count; //在方法中使用self访问静态成员并返回
}
}
MyClass::$count=0; //在类外面使用类名访问类中的静态成员,为其初使化赋值0
$myc1=new MyClass(); //通过MyClass类创建第一个对象,在构造方法中将count累加1
$myc2=new MyClass(); //通过MyClass类创建第二个对象,在构造方法中又为count累加1
$myc3=new MyClass(); //通过MyClass类创建第三个对象,在构造方法中再次为count累加1
echo MyClass::getCount(); //在类外面使用类名访问类中的静态成员方法,获取静态属性的值 3
echo $myc3->getCount(); //通过对象也可以访问类中的静态成员方法,获取静态属性的值 3
?>
const关键字
使用const关键字将类中的成员属性定义为常量,其访问的方式和静态成员一样,都是通过类名或者在成员方法中使用self关键字访问,也不能用对象来访问。标识为常量的属性是只读的,不能重新赋值,如果在程序中试图改变它的值,则会出现错误。所以在声明常量时一定要给初值,因为没有其他方式后期为常量赋值。注意,使用const是声明的常量名称前不要使用“$”符号,而且常量名称通常都是大写的。1
2
3
4
5
6
7
8
9
10
11
12
13
14<?php
class MyClass { //声明一个MyClass类,在类中声明一个常量,和一个成员方法
const CONSTANT = 'CONSTANT value'; //使用const声明一个常量,并直接赋上初使值
function showConstant() { //声明一个成员方法并在其内部访问本类中的常量
echo self::CONSTANT."<br>"; //使用self访问常量,注意常量前不要加“$”
}
}
echo MyClass::CONSTANT . "<br>"; //在类外部使用类名称访问常量,也不要加”$”
$class = new MyClass(); //通过类MyClass创建一个对象引用$class
$class->showConstant(); //调用对象中的方法
// echo $class::CONSTANT; //通过对象名称访问常量是不允许的
?>
克隆对象
在PHP中使用“clone”关键词克隆对象,克隆出一个完全一样的对象,克隆以后,原来和副本两个对象完全独立互不干扰。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23<?php
class Person { //声明类Person,并在其中声明了三个成员属性,一个构造方法以及一个成员方法
private $name; //第一个私有成员属性$name用于存储人的名子
private $sex; //第二个私有成员属性$sex用于存储人的性别
private $age; //第三个私有成员属性$age用于存储人的age
function __construct($name="", $sex="", $age=1) { //构造方法在对象诞生时为成员属性赋初值
$this->name=$name;
$this->sex=$sex;
$this->age=$age;
}
function say() { //一个成员方法用于打印出自己对象中全部的成员属性值
echo "我的名子叫:".$this->name." 性别:".$this->sex." 我的年龄是:".$this->age."<br>";
}
}
$p1=new Person("张三", "男", 20); //创建一个对象并通过构造方法为对象中所有成员属性赋初值
$p2=clone $p1; //使用clone关键字克隆(复制)对象,创建一个对象的副本
// $p3=$p1 //这不是复制对象,而是为对象多复制出一个访问该对象的引用
$p1->say(); //调用原对象中的说话方法,打印原对象中的全部属性值
$p2->say(); //调用副本对象中的说话方法,打印出克隆对象的全部属性值
?>
如果需要为克隆后的副本对象在克隆是重新为成员属性赋初值,则可以在类中声明一个魔术方法clone()。该方法是在对象克隆时自动调用的,所以就可以通过此方法为克隆后的副本重新初始化。__clone()方法不需要任何参数,该方法中自动包含$this和$that两个对象的引用,$this是副本对象的引用,而$that则是原本对象的引用。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27<?php
class Person { //声明类Person,并在其中声明了三个成员属性,一个构造方法以及一个成员方法
private $name; //第一个私有成员属性$name用于存储人的名子
private $sex; //第二个私有成员属性$sex用于存储人的性别
private $age; //第三个私有成员属性$age用于存储人的age
function __construct($name="", $sex="", $age=1) { //构造方法在对象诞生时为成员属性赋初值
$this->name=$name;
$this->sex=$sex;
$this->age=$age;
}
function __clone() { //声明此方法则在对象克隆时自动调用,用来为新对象重新赋值
$this->name="我是".$that->name."的副本"; //为副本对象中的name属性重新赋值
$this->age=10; //为副本对象中的age属性重新赋值
}
function say() { //一个成员方法用于打印出自己对象中全部的成员属性值
echo "我的名子叫:".$this->name." 性别:".$this->sex." 我的年龄是:".$this->age."<br>";
}
}
$p1=new Person("张三", "男", 20); //创建一个对象并通过构造方法为对象中所有成员属性赋初值
$p2=clone $p1; //使用clone克隆(复制)对象,并自动调用类中的__clone()方法
$p1->say(); //调用原对象中的说话方法,打印原对象中的全部属性值
$p2->say(); //调用副本对象中的说话方法,打印出克隆对象的全部属性值
?>
类中通用的方法__toString()
魔法方法__toString()是快速获取对象的字符串表示的最便捷的方式,是在直接输出对象引用时自动调用的方法。通常在此方法中返回的字符串是使用对象中多个属性值连接而成的1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16<?php
class TestClass { //声明一个测试类,在类中声明一个成员属性和一个__toString()方法
private $foo; //在类中声明的一个成员方法
function __construct($foo) { //通过构造方法传值为成员属性赋初值
$this->foo = $foo; //为成员属性赋值
}
public function __toString() { //在类中定义一个__toString方法
return $this->foo; //返回一个成员属性$foo的值
}
}
$obj = new TestClass('Hello'); //创建一个对象并赋值给对象引用$obj
echo $obj; //直接输出对象引用则自动调用了对象中的__toString()方法输出Hello
?>
通过__call()方法处理错误调用
在调用对象中不存在的方法时就会出现系统出错,然后程序退出不能继续执行。如果在类中添加一个魔术方法call(),则调用对象中不存在的方法时就会自动调用该方法,并且程序可以继续向下执行。可以通过在call()方法中的设置,提示用户调用的方法及需要的参数列表内容不存在。call()方法需要两个参数,第一个参数是调用不存在的方法时,接受这个不存在的方法的方法名,并将这个不存在的方法中,使用的参数列表形成数组传给call()方法中的第二个参数。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18<?php
class TestClass { //声明一个测试类,在类中声明printHello()和__call()方法
function printHello() { //声明一个方法,可以让对象成功能调用
echo "Hello<br>"; //执行时输出一条语句
}
function __call($functionName, $args) { //声明此方法用来处理调用对象中不存在的方法
echo "你所调用的函数:".$functionName."(参数:"; //输出调用不存在的方法名
print_r($args); //输出调用不存在的方法时的参数列表
echo ")不存在!<br>\n"; //输出符加的一些提示信息
}
}
$obj=new TestClass(); //通过类TestClass实例化一个对象
$obj->myFun("one", 2, "three"); //调用对象中不存在的方法,则自动调用了对象中的__call()方法
$obj->otherFun(8,9); //调用对象中不存在的方法,则自动调用了对象中的__call()方法
$obj->printHello(); //调用对象中存在的方法,可以成功调用
?>
对象串行化
对象通过写出描述自己状态的数值来记录自己,这个过程叫对象的串行化。串行化就是把整个对象转化为二进制字符串,有两种情况我们必须把对象串行化。
- 对象需要在网络中传输时,将对象串行化化成二进制串后在网络中传输。
- 对象需要持久保存时,将对象串行化后写入文件或是数据库中。
使用serialize()函数来串行化一个对象,把对象转化为二进制的字符串。serialize()函数需要一个参数就是对象的引用名,返回值为一个对象被串行化后的字符串。serialize()返回的字符串含义模糊,一般我们不会解析这个串来得到对象的信息。
另一个是反串行化后转化的二进制字符串再转化为对象,我们使用unserialize()函数来反串行化一个对象。这个函数的参数即为serialize()函数的返回值,返回值当然是重新组织好的对象。
class_Person.php1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17<?php
class Person { //声明一个Person类,包含三个成员属性和一个成员方法
private $name; //人的名子
private $sex; //人的性别
private $age; //人的年龄
function __construct($name="", $sex="", $age="") { //构造方法为成员属性赋初值
$this->name=$name;
$this->sex=$sex;
$this->age=$age;
}
function say() { //这个人可以说话的方法, 说出自己的成员属性
echo "我的名字叫:".$this->name." 性别:".$this->sex." 我的年龄是:".$this->age."<br>";
}
}
?>
serialize.php1
2
3
4
5
6<?php
require "class_Person.php"; //在本文件中包含Person类所在的脚本文件
$person=new Person("张三", "男", 20); //能过Person类创建一个对象,对象的引用名为$person
$person_string=serialize($person); //通过serialize函数将对象串行化,返一个字符串
file_put_contents("file.txt", $person_string); //将对象串行化后返回的字符串保存到file.txt文件中
?>
unserialize.php1
2
3
4
5
6<?php
require "class_Person.php"; //在本文件中包含Person类所在的脚本文件
$person_string=file_get_contents("file.txt"); //将file.txt文件中的字符串读出来并赋给变量$person_string
$person=unserialize($person_string); //进行反串行化操作,形成对象$person。
$person->say(); //调用对象中的say()方法,用来测试反串行化对象是否成功
?>
在PHP5中还有两个魔术方法sleep()和wakeup()可以使用。在调用serialize()函数将对象串行化时,会自动调用对象中的sleep()方法,用来将对象中的部分成员串行化。在调用unserialize()函数反串行化对象时,则会自动调用对象中的__wakeup()方法,用来在二进制串重新组成一个对象时,为新对象中的成员属性重新初始化。
sleep()函数不需要接受任何参数,但需要返回一个数组,在数组中包含需要串行化的属性。未被包含在数组中的属性将在串行化被忽略。如果没有在类中声明sleep()方法,对象中的所有属性都将被串行化。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35<?php
class Person { //声明一个Person类,包含三个成员属性和一个成员方法
private $name; //人的名子
private $sex; //人的性别
private $age; //人的年龄
function __construct($name="", $sex="", $age="") { //构造方法为成员属性赋初值
$this->name=$name;
$this->sex=$sex;
$this->age=$age;
}
function say() { //这个人可以说话的方法, 说出自己的成员属性
echo "我的名子叫:".$this->name." 性别:".$this->sex." 我的年龄是:".$this->age."<br>";
}
function __sleep() { //在类中添加此方法,在串行化时自动调用并返回数组
$arr=array("name", "age"); //数组中的成员$name和$age将被串行化,成员$sex则被忽略
return($arr); //返回一个数组
}
function __wakeup() { //在反串行化对象时自动调用该方法,没有参数也没有返回值
$this->age = 40; //在重新组织对象时,为新对象中的$age属性重新赋值
}
}
$person1=new Person("张三", "男", 20); //通过Person类实例化对象,对象引用名为$person1
//把一个对象串行化,返一个字符串,调用了__sleep()方法,忽略没在数组中的属性$sex
$person_string=serialize($person1);
echo $person_string."<br>"; //输出对象串行化的字符串
//反串行化对象,并自动调用了__wakup()方法重新为新对象中的$age属性赋值
$person2=unserialize($person_string); //反串行化对象形成对象$p2重新赋值$age为40
$person2->say(); //调用新对象中say()方法输出的成员中已没有$sex属性了
?>
抽象类与接口
抽象方法和抽象类
抽象方法就是没有方法体的方法,所谓的没有方法体是指在方法声明时没有花括号以及其中的内容,而是在声明方法时直接在方法后加上分号结束。另外在声明抽象方法时,还要使用关键字abstract来修饰。1
2abstract function fun1() //不能有花括号,就更不能有方法体中的内容了
abstract function fun2() //直接在方法名的括号后面加上分号结束,还要使用abstract修饰
抽象类使用abstract关键字来修饰。在抽象类中可以有不是抽象的成员方法和成员属性,但访问权限不能使用private关键字修饰为私有的。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19<?php
abstract class Person { //声明一个抽象类,要使用abstract关键字标识
protected $name; //声明一个存储人的名子的成员
protected $country; //声明一个存储人的国家的成员
function __construct($name="", $country="china") { //构造方法用来创建对象并初使化成员属性
$this->name = $name; //为成员属性name在创建对象时赋初值
$this->country = $country; //为成员属性country在创建对象时赋初值
}
abstract function say(); //在抽象类中声明一个没有方法体的抽象方法,使用abstract关键字标识
abstract function eat(); //在抽象类中声明另一个没有方法体的抽象方法,使用abstract关键字标识
function run(){ //在抽象类中可以声明正常的非抽象的方法
echo "使用两条腿走路<br>"; //有方法体,输出一条语句
}
}
?>
在抽象类中没有被实现的抽象方法,所以抽象类是不能被实例化的,即创建不了对象,也就是不能直接使用它。使用抽象类就包含了继承关系,它是为它的子类定义公共接口,将它的曹交给子类去遵守。当子类继承抽象类以后,就必须把抽象类中的抽象方法按照子类自己的需要去实现。子类必须把父类中的抽象方法全部都实现,否则子类中还存在抽象方法,所以还是抽象类,也不能实例化对象。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30<?php
class ChineseMan extends Person { //声明一个类去继承抽象类Person
function say() { //将父类中的抽象方法覆盖,按自已的需求去实现
echo $this->name."是".$this->country."人,讲汉语<br>"; //实现的内容
}
function eat() { //将父类中的抽象方法覆盖,按自已的需求去实现
echo $this->name."使用筷子吃饭<br>"; //实现的内容
}
}
class Americans extends Person { //声明另一个类去继承抽象类Person
function say() { //将父类中的抽象方法覆盖,按自已的需求去实现
echo $this->name."是".$this->country."人,讲英语<br>"; //实现的内容
}
function eat() { //将父类中的抽象方法覆盖,按自已的需求去实现
echo $this->name."使用刀子和叉子吃饭<br>"; //实现的内容
}
}
$chineseMan = new ChineseMan("高洛峰", "中国"); //将第一个Person的子类实例化对象
$americans =new Americans("alex", "美国"); //将第二个Person的子类实例化对象
$chineseMan->say(); //通过第一个对象调用子类中已经实例父类中抽象方法的say()方法
$chineseMan->eat(); //通过第一个对象调用子类中已经实例父类中抽象方法的eat()方法
$americans->say(); //通过第二个对象调用子类中已经实例父类中抽象方法的say()方法
$americans->eat(); //通过第二个对象调用子类中已经实例父类中抽象方法的eat()方法
?>
接口技术
接口是一种特殊的抽象类,而抽象类又是一种特殊的类,所以接口也是一种特殊的类。接口中声明的方法必须都是抽象方法,另外不能在接口中声明变量,只能使用const关键字声明为常量的成员属性,而且接口中所有成员都必须有public的访问权限。
类的声明是使用“class”关键字标识的,而接口的声明则使用“interface”关键字表示。1
2
3
4interface 接口名称{ //使用interface关键字声明接口
//常量成员 //接口中的成员属性只能是常量,不能是变量
//抽象方法 //接口中的所有方法必须是抽象方法,不能有非抽象的方法存在
} //接口中的成员也需要用花括号包含起来
如果需要使用接口中的成员,则需要通过子类去实现接口中的全部抽象方法,然后创建子类的对象去调用在子类中实现后的方法。但通过类去继承接口时需要使用implements关键字来实现,而并不是是哟个extends关键字完成。如果需要使用抽象类去实现接口中的部分方法,也需要使用implements关键字实现。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23<?php
interface One { //声明一个接口使用interface关键字,One为接口名称
const CONSTANT = 'CONSTANT value'; //在接口中声明一个常量成员属性,和在类中声明一样
function fun1(); //在接口中声明一个抽象方法“fun1()”
function fun2(); //在接口中声明另一个抽象方法“fun2()”
}
abstract class Three implements One { //声明一个抽象类去实现接口One中的第二个方法
function fun2() { //只实现接口中的一个抽象方法
//具体的实现内容由子类自决定
}
}
class Four implements One { //声明一个类实现接口One中的全部抽象方法
function fun1() { //实现接口中第一个方法
//具体的实现内容由子类自决定
}
function fun2() { //实现接口中的第二个方法
//具体的实现内容由子类自决定
}
}
?>
PHP是单继承的,一个类只能有一个父类,但是一个类可以实现多个接口。将要实现的多个接口之间使用逗号分隔开,而且在子类中要将所有接口中的抽象方法全部实现才可以创建对象。1
2
3class 类名 implements 接口一,接口二...接口n{ //一个类实现多个接口
//实现所有接口中的抽象类
}
实现多个接口是使用“implements”关键字,同时还可以使用“extends”关键字继承一个类。即在继承一个类的同时实现多个接口,但一定要先使用extends继承一类再去使用imlements实现多个接口。1
2
3class 类名 extends 父类名 implements 接口一,接口二...接口n{ //继承一个类的同时实现多个接口
//实现所有接口中的抽象类
}
字符串处理与正则表达式
字符串的处理介绍
字符串的处理方式
在PHP中,提供了大量的字符串操作函数,功能强大,使用也比较简单。但对一些比较复杂的字符串操作,则需要借助PHP所支持的正则表达式来实现。如果字符串处理函数和正则表达式都可以实现字符串操作,建议使用字符串处理函数来完成,因为字符串的处理函数要比正则表达式处理字符串的效率高。
字符串类型的特点
因为PHP是弱类型语言,所以其他类型的数据一般都可以直接应用于字符串操作函数里,而自动转化为字符串进行处理。1
2
3
4<?php
echo substr("1234567",2,4); //将字符串用于字符串函数substr()处理,输出子字符串345
echo substr(123456,2,4); //将整型用于字符串substr()处理,输出同样是字符串345
?>
还可以将字符串视为数组,当做字符集合看待1
2
3
4
5
6
7
8
9<?php
$str="lamp"; //声明一个字符串$str,值为lamp
echo $str."<br>"; //将字符串看作是一个连续的实体,一起输出lamp
//以下将字符串看作字符集合,按数组方式一个一个输出
echo $str[0]; //输出字符串$str中第一个字符l
echo $str[1]; //输出字符串$str中第一个字符a
echo $str[2]; //输出字符串$str中第一个字符m
echo $str[3]; //输出字符串$str中第一个字符p
?>
但将字符串看做字符集合时,PHP脚本引擎无法区分是字符还是数组,会带来二义性。所以中括号的语法已不再使用,替代它的是使用花括号。1
2
3
4
5
6
7<?php
$str="lamp"; //声明一个字符串$str,值为lamp
echo $str{0}; //输出字符串$str中第一个字符l
echo $str{1}; //输出字符串$str中第一个字符a
echo $str{2}; //输出字符串$str中第一个字符m
echo $str{3}; //输出字符串$str中第一个字符p
?>
常用的字符串输出函数
函数名 | 功能描述 |
---|---|
echo() | 输出字符串 |
print() | 输出一个或多个字符串 |
die() | 输出一条消息,并退出当前脚本 |
printf() | 输出格式化字符串 |
sprintf() | 把格式化的字符串写入一个变量中 |
函数echo()
该函数用于输出一个或多个字符串,是在PHP中使用最多的函数,因为使用它的效率要比其他字符串输出函数高。echo()实际上不是一个函数,因此您不需对其使用括号。1
2
3
4
5
6
7
8
9
10
11
12
13
14<?php
$str = "What's LAMP?"; //定义一个字符串$str
echo $str; //可以直接输出字符串变量
echo "<br>"; //也可以直接输出字符串
echo $str."<br>Linux+Apache+MySQL+PHP<br>"; //还可以使用点运算符号连接多个字符串输出
echo "This
text
spans
multiple
lines.<br>"; //可以将一行文本换成多行输出
echo 'This ','string ','was ','made ','with multiple parameters<br>'; //可以输出用逗号隔开的多个参数
?>
函数print()
该函数的功能和echo()的一样,它有返回值,若成功则返回1,失败则返回0。它的执行效率没有echo()函数高。
函数die()
该函数是exit()函数的别名。如果参数是一个字符串,则该函数会在退出前输出它。如果参数是一个整数,这个值会被用做退出状态。退出状态的值在0至254之间。退出状态255有PHP保留,不会被使用。状态0用于成功地终止程序。1
2
3
4<?php
$url = "http://www.lampbrother.net"; //定义一个网络文件的位置
fopen($url,"r") or die("Unable to connect to $url"); //如果打开失败则输出一条消息并退出程序
?>
函数printf()
该函数用于输出格式化的字符串,和C语言的同名函数用法一样。第一个参数为必选项,是规定的字符串以及如何格式化其中的变量。还可以有多个可选参数,是规定插到第一个参数的格式化字符串中对应%符号处的参数。1
printf(format,arg1,arg2,...,argn)
第一个参数中使用的转换格式,是以百分比符号(“%”)开始到转换字符结束。
格式 | 功能描述 |
---|---|
%% | 返回百分比符号 |
%b | 二进制数 |
%c | 按照ASCII值的字符 |
%d | 带符号十进制数 |
%e | 科学计数法 |
%u | 无符号十进制数 |
%f | 浮点数(local setting aware) |
%F | 浮点数(not local setting aware) |
%o | 八进制数 |
%s | 字符串 |
%x | 十六进制数(小写字母) |
%X | 十六进制数(大写字母) |
arg1,arg2,argn等参数将插入到主字符串中的百分号(%)符号处。该函数是逐步执行的。在第一个%符号中,插入arg1,在第二个%符号处,插入arg2,依次类推。如果%符号多于arg参数,则必须使用占位符。占位符被插入%符号之后,由数组和“\$”组成。1
2
3
4
5
6
7
8
9
10
11
12<?php
$str = "LAMP"; //声明一个字符串数据
$number = 789; //声明一个整型数据
//将字符串$str在第一个参数中的%处输出,按%s的字符串输出,整型$number按%u输出
printf("%s book. page number %u <br>",$str,$number);
printf("%0.3f <br>",$number); //将整型$number按浮点数输出,并在小数点后保留3位
$format = "The %2\$s book contains %1\$d pages.
That's a nice %2\$s full of %1\$d pages. <br>"; //定义一个格式并在其中使用占位符
printf($format, $number, $str); //按格式的占位符号输出多次变量,%2$s位置处是第三个参数
?>
函数sprintf()
该函数的用法和printf()的格式相似,但它并不是输出字符串,而是把格式化的字符串以返回值的形式写入到一个变量中。这样就可以将格式化后的字符串在需要时使用。1
2
3
4
5<?php
$num= 12345; //声明一个整数12345
$txt = sprintf("%0.2f",$num); //转换为保留两位小数的浮点数,并赋值给变量$text
echo $txt; //在需要的地方就可以使用格式化后的文本$txt
?>
常用的字符串格式化函数
格式 | 功能描述 |
---|---|
ltrim() | 从字符串左侧删除空格或者其他预定义字符 |
rtrim() | 从字符串的末端开始删除空白字符或其他预定义字符 |
trim() | 从字符串的两端删除空白字符和其他预定义字符 |
str_pad() | 把字符串填充为新的长度 |
strtolower() | 把字符串转换为小写 |
strtoupper() | 把字符串转化为大写 |
ucfirst() | 把字符串中的首字符转换为大写 |
ucwords() | 把字符串中每个单词的首字符转换为大写 |
nl2br() | 在字符串中的每行之间插入HTML换行符 |
htmlentities() | 把字符转化为HTML实体 |
htmlspecialchears() | 把一些预定义的字符转换为HTML实体 |
Stripslashes() | 删除由addcslashes()函数添加的反斜杠 |
strip_tags() | 剥去HTML、XML以及PHP的标签 |
number_format() | 通过千位分组来格式化数字 |
strrev() | 反转字符串 |
md5() | 将一个字符串进行MD5计算 |
取出空格和字符串填补函数
在PHP中可以通过ltrim()、rtrim()和trim()函数利用处理字符串中多余的空格,或者一些其他没有意义的符号。1
2
3string ltrim(string str[,string charlist]) //从字符串左侧删除空格或者其他预定义字符
string rtrim(string str[,string charlist]) //从字符串右侧删除空白字符或其他预定义字符
string trim(string str[,string charlist]) //从字符串的两端删除空白字符和其他预定义字符
这三个函数分别用于从字符串的左、右和两端删除空白字符或其他预定义字符。处理后的结果都会以新字符串的形式返回,不会在原字符串上修改。其中第一个参数str是待处理的字符串,为必选项。第二个参数charlist是过滤字符串,用于指定希望去除的特殊符号,该参数为可选。如果不指定过滤字符串,默认情况下会去掉下列字符:
- “ ”:ASCII为32的字符(0x20),即空格
- “\0”:ASCII为0的字符(0x00),即NULL
- “\t”:ASCII为9的字符(0x99),即制表符
- “\n”:ASCII为10的字符(0x0A),即新行
- “\r”:ASCII为13的字符(0x0D),即回车
此外还可以使用“..”符号指定需要取出的一个范围,例如“0..9”或“a..z”表示去掉ASCII码值中的数字和小写字母。1
2
3
4
5
6
7
8
9
10
11
12<?php
$str=" lamp "; //声明一个字符串,其中左侧有3个空格,右侧2个空格,总长度为9个字符
echo strlen($str); //输出字符串的总长度 9
echo strlen(ltrim($str)); //去掉左侧空格后的长度输出为 6
echo strlen(rtrim($str)); //去掉右侧空格后的长度输出为 7
echo strlen(trim($str)); //去掉两侧空格后的长度输出为 4
$str="123 This is a test ..."; //声明一个测试字符串,左侧为数字开头,右侧为省略号“…”
echo ltrim($str, "0..9"); //过滤掉字符串左侧的数字,输出:This is a test ...
echo rtrim($str, "."); //过滤掉字符串右侧的所有“.”,输出:123 This is a test
echo trim($str, "0..9 A..Z ."); //过滤掉字符串两端的数字和大写字母还有“.”,输出:his is a test
?>
不仅可以按需求过滤字符串中的内容,还可以使用str_pad()函数按需求对字符串进行填补。可以用于对一些敏感信息的保护。1
string str_pad(string input,int pad_length[,string pad_string[,int pad_type]])
该函数有四个参数,第一个参数是必选项,指明要处理的字符串。第二个参数也是必选项,给丁处理后字符串的长度,如果该值小于原始字符串的长度,则不进行任何操作。第三个参数指定填补时所用的字符串,它为可选参数。如果没有指定则默认使用空格填补。最后一个参数指定填补的方向,它有三个可选值:STR_PAD_BOTH、STR_PAD_LEFT和STR_PAD_RIGHT,分别代表在字符串两端、左和右进行填补。也是一个可选参数,如果没有指定则默认值是STR_PAD_RIGHT。1
2
3
4
5
6
7<?php
$str= "LAMP";
echo str_pad($str, 10); //指定长度为10,默认使用空格在右边填补"LAMP "
echo str_pad($str, 10, "-=", STR_PAD_LEFT); //指定长度为10,指定在左边填补" -=-=-=LAMP"
echo str_pad($str, 10, "_", STR_PAD_BOTH); //指定长度为10,指定两端填补 " ___LAMP___"
echo str_pad($str, 6 , "___"); //指定长度为6, 默认在右边填补" LAMP__"
?>
字符串大小写转换
在PHP中提供了四个字符串大小写的转换函数,它们都只有一个可选参数,即传入要进行转换的字符串。利用直接使用这些函数完成大小写转换的操作。函数strtoupper()用于将给定的字符串全部转换为大写字母;函数strtolower()用于将给定的字符串全部转换为小写字母;函数ucfirst()用于将给定的字符串中首字母转换为大写,其余字符不变;函数ucwords()用于将给定的字符串中全部以空格分隔的单词首字母转换为大写。1
2
3
4
5
6
7<?php
$lamp= "lamp is composed of Linux、Apache、MySQL and PHP";
echo strtolower($lamp); //输出lamp is composed of linux、apache、mysql and php
echo strtoupper($lamp); //输出LAMP IS COMPOSED OF LINUX、APACHE、MYSQL AND PHP
echo ucfirst($lamp); //输出Lamp is composed of Linux、Apache、MySQL and PHP
echo ucwords($lamp); //输出Lamp Is Composed Of Linux、Apache、MySQL And PHP
?>
和HTML标签相关的字符串格式化
函数nl2br()
nl2br()函数是在字符串中的每个新行“\n”之前插入HTML换行符“
”。1
2
3<?php
echo nl2br("One line.\nAnother line."); //在“\n”前加上“<br />”标记
?>
函数htmlspecialchars()
htmlspecialchars()函数可以将一些预定义的字符转换为HTML实体。此函数用在预防使用者提供的文字中包含了HTML的标记。
以下是该函数可以转化的字符
- “&”(和号)转换为“&”。
- “””(双引号)转换为“"”。
- “‘“(单引号)转换为“'”。
- “<”(小于)转换为“<”。
- “>”(小于)转换为“>”。
1 | string htmlspecialchars(string string[,int quote_style[,string charset]]) |
该函数中第一个参数是带有HTML标记待处理的字符串,为必选参数。第二个参数为可选参数,用来决定引号的转换方式。默认值为ENT_COMPAT将只转换双引号,而保留单引号;NET_QUOTES将同时转换这两种引号;而RNT_NOQUOTES将不对引号进行转换。第三个参数也是可选的值,用于指定所处理字符串的字符集,默认的字符集是“ISO8859-1”.
1 | <html> |
在PHP中还提供了htmllentities()函数,可以将所有的非ASCII码字符转换为对应的实体代码。该函数与htmlspecialchars()函数的使用语法格式一致,但该函数可以转义更多的HTML字符。1
2
3
4
5
6
7
8
9<?php
$str = "一个 'quote' 是 <b>bold</b>";
// 输出: Ò»¸ö 'quote' ÊÇ <b>bold</b>
echo htmlentities($str);
// 输出: 一个 'quote' 是 <b>bold</b>
echo htmlentities($str, ENT_QUOTES,gb2312);
?>
其他字符串格式化函数
函数strrev()
该函数的作用是将输入的字符反转,只提供一个要处理的字符串作为参数,返回反转后的字符串。1
2
3<?php
echo strrev("you are sb"); //反转输出:bs era uoy
?>
函数number_format()
number_format()函数通过千位分组来格式化数字1
string number_format(float number[,int decimals[,string dec_point,string thousands_sep]])
该函数返回格式化后的数组,该函数支持一个、两个或四个参数(不是三个)。第一个参数为必选项,提供要被格式化的数字。如果未设置其他参数,则数字会被格式化为不带小数点且以逗号(,)作为分隔符的数字。第二个参数是可选项,规定使用多少个小数位。如果设置了该参数,则使用点号(.)作为小数点来格式化数字。第三个参数也是可选的参数,规定用什么字符串作为小数点。第四个参数也为可选参数,规定用做千位分隔符的字符串。如果设置了该参数,那么所有其他参数都是必需的。1
2
3
4
5
6<?php
$number=123456789; //声明一个数字
echo number_format($number); //输出123,456,789千位分隔的字符串
echo number_format($number, 2); //输出123,456,789.00小数点后保留两数小数
echo number_format($number, 2, ",", "."); //输出123.456.789,00 千位使用(,)分隔并保留两位小数
?>
函数md5()
md5()函数的作用就是讲一个字符串进行MD5算法加密,默认返回一个32位的十六进制字符串。1
string md5(string str[,bool raw_output]) //进行MD5算法加密演算
其中第一个参数标识待处理的字符串,是必选项。第二个参数需要一个布尔型数值,是可选项。默认为FALSE,返回一个32位十六进制字符串。如果设置为TURE,将返回一个16位的二进制数。1
2
3
4
5
6
7
8<?php
$password= "lampbrother"; //定义一个字符串作为密码,加密后保存到数据库中
echo md5($password)."<br>"; //输出MD5加密后的值:5f1ba7d4b4bf96fb8e7ae52fc6297aee
if (md5($password) == '5f1ba7d4b4bf96fb8e7ae52fc6297aee') { //将输入的密码和数据库保存的匹配
echo "密码一致,登录成功"; //如果相同则会输出这条信息
}
?>
在PHP中提供了一个对文件进行MD5加密的函数md5_file(),使用的方式和md5()函数相似。
字符串比较函数
按字节顺序进行字符串比较
按字节顺序进行字符串的比较可以使用strcmp()和strcasecmp()两个函数,其中函数strcasecmp()可以忽略字符串中字母的大小写进行比较。1
2int strcmp(string str1,string str2) //区分字符串中字母大小写地比较
int strcasecmp(string str1,string str2) //忽略字符串字母大小写地比较
这两个函数的用法相似,都需要传入进行比较的两个字符串参数。可以对输入的str1和str2两字符串,按照字节的ASCII值从两个字符串的首字节开始比较,如果相等则进入下一个字节的比较,直至结束比较。返回以下三个值之一:
- 如果str1等于str2则返回0
- 如果str1大于str2则返回1
- 如果str1小于str2则返回-1
1 | <?php |
按自然排序进行字符串比较
strnatcmp()函数按自然排序法比较两个字符串,该函数对大小写敏感,其使用格式与strcmp()函数相似。
在PHP中也提供了忽略大小写版本的函数strnatcasecmp(),用法和strnatcmp()函数相同。
正则表达式在字符串处理中的应用
与Perl兼容的正则表达式函数
PHP常用功能模块
日期和时间
UNIX时间戳
UNIX时间戳是保存日期和时间的一种紧凑简洁的方法,是大多数UNIX系统中保存当前日期和事件的一种方法,也是在大多数计算机语言标识日期和时间的一种标准格式。以32位的整数标识格林威治标准时间,例如整数11230499325表示当前事件的时间戳。UNIX时间戳是从1970年1月1日零点开始起到当前事件所经过的秒数。1970年1月1日零点作为所有日期计算的基础,这个日期通常称为UNIX纪元。
将日期和时间转变成UNIX时间戳
在PHP中,如果需要将日期和事件转变成UNIX时间戳,可以调用mktime()函数。该函数的原型如下所示:1
int mktime([int hour[,int minute[,int second[,int month[,int day[,int year]]]]]])
该函数中所有参数都是可选的,如果参数为空,默认将当前时间转变成UNIX时间戳。这样,和直接调用time()函数获取当前的UNIX时间戳功能相同。参数也可以从右向左省略,任何省略的参数会被设置成本地日期和事件的当前值。如果只想转变日期,对具体的时间不在乎,可以将前三个转变事件的参数都设置为0.mktime()函数对于日期运算和验证非常有用,它可以自动校正越界的输入。1
2
3
4
5
6<?php
echo date("M-d-Y", mktime(0, 0, 0, 12, 36, 2007))."\n"; //日期超过31天,计算后输出Jan-05-2008
echo date("M-d-Y", mktime(0, 0, 0, 14, 1, 2008))."\n"; //月份超过12月,计算后输出Feb-01-2009
echo date("M-d-Y", mktime(0, 0, 0, 1, 1, 2009))."\n"; //没有问题的转变,输出结果 Jan-01-2009
echo date("M-d-Y", mktime(0, 0, 0, 1, 1, 99))."\n"; //会将99年转变为1999年, Jan-01-1999
?>
日期计算
1 | <?php |
在以上脚本中,调用mktime()函数将从用户出生日期转变为UNIX时间戳,再调用time()函数获取当前事件的UNIX时间戳。因为这个日期的格式都是使用整数表示的,所以可以将它们相减。又将计算后获取的UNIX时间戳除以一年的秒数,将UNIX时间戳转变为以年度量的单位。
在PHP中获取日期和时间
PHP中提供了多种获取事件和日期的函数,除了通过time()函数获取当前的UNIX时间戳外,调用getdate()函数确定当前时间,通过gettimeofday()函数获取某一天中的具体时间。此外,在PHP中还可以通过date_sunrise()和date_sunset()两个函数,获取某地点某天的日出时间和日落时间。
日期和时间格式化输出
在PHP中可以调用date()函数格式化一个本地事件和日期,该函数的原型如下所示:1
string date(string format[,int timestamp]) //格式化一个本地时间和日期
该函数有两个参数,第一个参数是必需的,规定时间戳的转换格式。第二个参数是可选的,需要提供一个UNIX时间戳,如果没有指定这个时间戳,默认值为time()将返回当前的日期和时间。该函数返回一个格式化后表示适当日期的字符串。date()函数的常见调用方式如下所示:1
echo date("Y-m-d H:i:s"); //输出当前的时间格式:xxxx-xx-xx xx:xx:xx
文件系统处理
文件系统概述
文件类型
PHP是以UNIX的文件系统为模型的,因此在Windows系统中我们只能获得“file”、“dir”或者“unknown”三种文件类型。而在UNIX系统中,我们可以获得“block”、“char”、“dir”、“fifo”、“file”、“link”和“unknown”七种类型。
文件类型 | 描述 |
---|---|
block | 块设备文件,如某个磁盘分区、软驱、光驱CD-ROM等 |
char | 字符设备是指在I/O传输过程中以字符为单位进行传输的设备,例如键盘,打印机等 |
dir | 目录类型,目录也是文件的一种 |
fifo | 命名管道,常用与将信息从一个进程传递到另一个进程 |
file | 普通文件类型,如文本文件或可执行文件等 |
link | 符号链接,是指向文件指针的指针,类似Windows中的快捷键 |
unkonwn | 未知类型 |
在PHP中可以使用filetype()函数获取文件的上述类型,该函数接受一个文件名作为参数,如果文件不存在将返回FALSE。1
2
3
4
5
6
7
8
9
10
11
12<?php
//获取Linux系统下文件类型
echo filetype('/etc/passwd'); //输出file,/etc/passwd为普通文件
echo filetype('/etc/grub.conf'); //输出link,/etc/grub.conf为链接文件,链接到/boot/grub/grub.conf
echo filetype('/etc/'); //输出dir,/etc/为一个目录,即文件夹
echo filetype('/dev/sda1'); //输出block,/dev/sda1为块设备,它是一个分区
echo filetype('/dev/tty01'); //输出char,为字符设备,它是一个字符终端
//获取Windows系统下文件类型
echo filetype("C:\\WINDOWS\\php.ini"); //输出file,C:\WINDOWS\php.ini为一个普通文件
echo filetype("C:\\WINDOWS"); //输出dir,C:\WINDOWS为一个文件夹(目录)
?>
对于一个已知的文件,还可以使用is_file()函数判断给定的文件名是否为一个正常的文件。和它类似的,使用is_dir()函数判断给定的文件名是否是一个目录,使用is_link()函数判断给定的文件名是否为一个符号链接。
文件属性
PHP中提供用来获取文件的一些常见属性的内置函数。
函数名 | 作用 | 参数 | 描述 |
---|---|---|---|
file_exists() | 检查文件或目录是否存在 | 文件名 | 文件存在返回TRUE,不存在则返回FALSE |
filesize() | 取得文件大小 | 文件名 | 返回文件大小的字节数,出错返回FALSE |
is_readable() | 判断给定文件名是否可读 | 文件名 | 如果文件存在且可读则返回TRUE |
is_writable() | 判断给定文件名是否可写 | 文件名 | 如果文件存在且可写则返回TRUE |
is_executable() | 判断给定文件名是否可执行 | 文件名 | 如果文件存在且可执行则返回TRUE |
filectime() | 获取文件的创建时间 | 文件名 | 返回UNIX时间戳格式 |
filemtime() | 获取文件的修改时间 | 文件名 | 返回UNIX时间戳格式 |
fileatime() | 获取文件的访问时间 | 文件名 | 返回UNIX时间戳格式 |
stat() | 获取文件大部分属性值 | 文件名 | 返回关于给定文件有用信息的数组 |
1 | <?php |
目录的基本操作
解析目录路径
要描述一个文件的位置,可以使用绝对路径和相对路径。绝对路径是从根开始一级一级地进入各个子目录,最后指定该文件名或目录名。而相对路径是从当前目录进入某目录,最后指定该文件名或目录名。在系统的每个目录下都有两个特殊的目录“.”和“..”,分别指示当前目录和当前目录的父目录。1
2
3
4
5
6$unixPath="/var/www/html/index.php"; //在UNIX系统中的绝对路径,必须使用“/”作为路径分隔符
$winPath="C:\\Appserv\\www\\index.php"; //在Windows系统中的绝对路径,默认使用“\”作为路径分隔符
$winPath="C:\\Appserv\\www\\index.php"; //在Windows系统中也接受使用“/”作为路径分隔符
$fileName1="file.txt"; //相对路径,当前目录下的file.txt文件
$fileName2="javascript/common.js"; //相对路径。当前路径中javascript子目录下的common.js文件
$fileName3="../images/logo.gif"; //相对路径。上一级目录中images子目录下的logo.gif文件
为了程序可以有很好的移植性,建议都使用“/”作为文件的路径分隔符。
函数basename()
该函数给出一个包含有指向一个文件的全部路径的字符串,本函数返回基本的文件名。第二个参数是可选参数,规定文件的扩展名。如果提供了则不会输出这个扩展名。1
2
3
4
5
6<?php
$path = "/var/www/html/page.php"; //包含有指向一个文件的全路径的字符串
echo basename($path); //显示带有文件扩展名的文件名,输出page.php
echo basename($path,".php"); //显示不带有文件扩展名的文件名,输出page
?>
函数dirname()
该函数只需要一个参数,给出一个包含有指向一个文件的全路径的字符串,本函数返回去掉文件名后的目录名。1
2
3
4
5
6<?php
$path = "/var/www/html/page.php"; //包含有指向一个文件的全路径的字符串
echo dirname($path); //返回目录名/var/www/html
echo dirname('c:/'); //返回目录名c:/
?>
函数pathinfo()
函数pathinfo()返回一个关联数组,其中包括指定路径中的目录名、基本名和扩展名三个部分。分别通过数组键dirname、basename和extension来引用。1
2
3
4
5
6
7<?php
$path = "/var/www/html/page.php"; //包含有指向一个文件的全路径的字符串
$path_parts=pathinfo($path); //返回包括指定路径中的目录名、基本名和扩展名关联数组
echo $path_parts["dirname"]; //输出目录名/var/www/html
echo $path_parts["basename"]; //输出基本名page.php
echo $path_parts["extension"]; //输出扩展名.php
?>
遍历目录
在进行PHP编程的时候,需要对服务器某个目录下面的文件进行浏览,通常称为遍历目录。取得一个目录下的文件和子目录,就需要用到opendir()函数、readdir()函数、closedir()函数和rewinddir()函数。
- 函数opendir()用于打开指定目录,接受一个目录的路径及目录名作为参数,函数返回值为可供其他目录函数使用的目录句柄。如果该目录不存在或没有访问权限,则返回FALSE。
- 函数readdir()用于读取指定目录,接受已经用opendir()函数打开的可操作目录句柄作为参数,函数返回当前目录指针位置的一个文件名,并将目录指针向后移动一位。当指针位于目录的结尾时,因为没有文件存在则返回FALSE。
- 函数closedir()关闭指定目录,接受已经用opendir()函数打开的可操作目录句柄作为参数。函数无返回值,运行后将关闭打开的目录。
- 函数rewinddir()倒回目录句柄,接收已经用opendir()函数打开的可操作目录句柄作为参数。将目录指针重置目录到开始处,即倒回目录开头。
1 | <?php |
统计目录大小
计算文件的大小可以通过filesize()函数完成,统计磁盘大小也可以使用disk_free_space()和disk_total_space()两个函数实现。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22<?php
function dirSize($directory) { //自定义一个函数dirSize(),统计传入参数的目录大小
$dir_size=0; //整型变量初值为0,用来累加各个文件大小从而计算目录大小
if($dir_handle=@opendir($directory)) { //打开目录,并判断是否能成功打开
while($filename=readdir($dir_handle)) { //循环遍历目录下的所有文件
if($filename!="." && $filename!="..") { //一定要排除两个特殊的目录
$subFile=$directory."/".$filename; //将目录下的子文件和当前目录相连
if(is_dir($subFile)) //如果为目录
$dir_size+=dirSize($subFile); //递归地调用自身函数,求子目录的大小
if(is_file($subFile)) //如果是文件
$dir_size+=filesize($subFile); //求出文件的大小并累加
}
}
closedir($dir_handle); //关闭文件资源
return $dir_size; //返回计算后的目录大小
}
}
$dir_size=dirSize("phpMyAdmin"); //调用该函数计算目录大小,返回目录大小的字节数
echo round($dir_size/pow(1024,1),2)."KB"; //将获取的目录字节数转换为“KB”单位并输出
?>
建立和删除目录
在PHP中,使用mkdir()函数只需要传入一个目录名即可很容易地建立一个新目录。但删除目录所用的函数rmdir(),只能删除一个空目录并且目录必须存在。如果是非空的目录就需要先进入到路中,使用unlink()函数将目录中的每个文件都删除掉,再回来将这个空目录删除。如果目录中还存在子目录,而且子目录也非空,就要使用递归的方法了。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20<?php
function delDir($directory) { //自定义函数递归的删除整个目录
if(file_exists($directory)) { //判断目录是否存在,如果不存在rmdir()函数会出错
if($dir_handle=@opendir($directory)) { //打开目录返回目录资源,并判断是否成功
while($filename=readdir($dir_handle)) { //遍历目录,读出目录中的文件或文件夹
if($filename!="." && $filename!="..") { //一定要排除两个特殊的目录
$subFile=$directory."/".$filename; //将目录下的文件和当前目录相连
if(is_dir($subFile)) //如果是目录条件则成立
delDir($subFile); //递归调用自己删除子目录
if(is_file($subFile)) //如果是文件条件则成立
unlink($subFile); //直接删除这个文件
}
}
closedir($dir_handle); //关闭目录资源
rmdir($directory); //删除空目录
}
}
}
delDir("phpMyAdmin"); //调用delDir()函数,将程序所在目录中的“phpMyAdmin”文件夹删除
?>
复制或移动目录
要赋值一个包含多级子目录的目录,将涉及文件的赋值、目录创建等操作。赋值一个文件可以通过PHP提供的copy()函数完成,创建目录可以使用mkdir()函数,定义函数时,首先对原木了进行遍历,如果遇到的是普通文件,直接使用copy()函数进行复制。如果遍历时遇到一个目录,则必须建立该目录,然后再对该目录下的文件进行复制操作,如果还有子目录,则使用递归重复操作,最终将整个目录复制完成。如果是移动目录,只需在遍历时将赋值后的文件或目录删除即可。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26<?php
function copyDir($dirSrc, $dirTo) { //自定义函数递归的复制带有多级子目录的目录
if(is_file($dirTo)) { //如果目标不是一个目录,是一个已存在的文件则退出
echo "目标不是目录不能创建!!";
return; //退出函数
}
if(!file_exists($dirTo)) { //如果目标目录不存在则创建,存在则不变
mkdir($dirTo); //创建目录目录
}
if($dir_handle=@opendir($dirSrc)) { //打开目录返回目录资源,并判断是否成功
while($filename=readdir($dir_handle)) { //遍历目录,读出目录中的文件或文件夹
if($filename!="." && $filename!="..") { //一定要排除两个特殊的目录
$subSrcFile=$dirSrc."/".$filename; //将源目录的多级子目录连接
$subToFile=$dirTo."/".$filename; //将目标目录的多级子目录连接
if(is_dir($subSrcFile)) //如果源文件是一个目录
copyDir($subSrcFile, $subToFile); //递归调用自己复制子目录
if(is_file($subSrcFile)) //如果源文件是一个普通文件
copy($subSrcFile, $subToFile); //直接复制到目标位置
}
}
closedir($dir_handle); //关闭目录资源
}
}
copyDir("phpMyAdmin", "D:/admin"); //测试函数,将目录"phpMyAdmin"复制到"D:/admin"
?>
文件的基本操作
文件的打开与关闭
在PHP中可以通过标准函数fopen()建立与文件资源的连接,使用fclose()函数关闭通过fopen()函数打开的文件资源。
函数fopen()
该函数用来打开一个文件,并在打开一个文件时,还需要指定如何使用它。也就是以哪种文件模式打开文件资源。服务器上的操作系统文件必须知道对打开文件进行什么操作。操作系统需要了解在打开这个文件之后,这个文件是否允许其他的程序脚本再打开,还需要了解脚本的属主用户是否具有在这种方式下使用该文件的权限。该函数的原型如下所示:1
resource fopen(string filename,string mode[,bool use_include_path[,resource zcontext]]) //打开文件
第一个参数需要提供要被打开文件的URL。这个URL可以是脚本所在的服务器中的绝对路径,也可以是相对路径,还可以是网络资源中的文件。
第二个参数需要提供文件模式,文件模式可以告诉操作系统如何处理来自其他人或脚本的访问请求,以及一种用来检查你是否有权访问这个特定文件的方法。当在打开文件时有三种选择:
- 打开一个文件为了只读、只写或者是读和写。
- 如果要写一个文件,可以覆盖所有已有的文件内容,或者需要将新数据追加到文件末尾。
- 如果在一个区分二进制文件和纯文本文件的系统上写一个文件,还必须指定采用的方式。
函数fopen()也支持以上三种方式的组合,只需要在第二个参数中提供一个字符串,指定将对文件进行的操作即可。
模式字符 | 描述 |
---|---|
r | 只读方式打开文件,从文件开头开始读 |
r+ | 读写方式打开文件,从文件开头开始读写 |
w | 只写方式打开文件,从文件开头开始写。如果文件已经存在,将文件指针指向文件头并将文件大小截为零,即删除所有文件已有的内容。如果该文件不存在,函数将创建这个文件 |
w+ | 读写方式打开文件,从文件开头开始读写。如果文件已经存在,将文件指针指向文件头并将文件大小截为零,即删除所有文件已有的内容。如果文件不存在,函数将创建这个文件 |
x | 创建并以写入方式打开,将文件指针指向文件头。如果文件已存在,则fopen()调用失败并返回FALSE,并生成一条E_WARNING级别的错误信息。如果文件不存在则尝试创建之。仅能用于本地文件 |
x+ | 创建并以读写方式打开,将文件指针指向文件头。如果文件已存在,则fopen()调用失败并返回FALSE,并生成一条E_WARNING级别的错误信息。如果文件不存在则尝试创建之。仅能用于本地文件 |
a | 写入方式打开,将文件指针指向文件末尾。如果该文件已有内容,将从该文件末尾开始追加。如果该文件不存在,函数将创建这个文件。 |
a+ | 写入方式打开,将文件指针指向文件末尾。如果该文件已有内容,将从该文件末尾开始追加或读。如果该文件不存在,函数将创建这个文件。 |
b | 以二进制模式打开文件,用于与其他模式进行连接。如果文件系统能够区分二进制文件和文本文件,你可能会使用它。例如在Windows系统中可以区分,而UNIX系统则不区分。这个模式是默认的模式 |
t | 以文本模式打开文件,这个模式也只是windows系统下一个选项,不推荐使用 |
第三个参数是可选的,如果资源位于本地文件系统,PHP则认为可以使用本地路径或相对路径来访问资源。如果这个参数设置为1,这样就会使PHP考虑配置指令include_path中指定的路径。
第四个参数也是可选的,fopen()函数允许文件名称以协议名称开始,例如“http://”,并且在一个远程位置打开该文件。通过设置这个参数,还可以支持一些其他的协议。
如果fopen()函数成功打开一个文件,该函数将返回一个指向这个文件的文件指针。对该文件进行操作所使用的读、写以及其他的文件操作函数,都要使用这个资源来访问该文件。如果打开文件失败,则返回FALSE。1
2
3
4
5
6
7
8
9
10
11
12
13
14<?php
//使用绝对路径打开file.txt文件,选择只读模式,并返回资源$handle
$handle = fopen("/home/rasmus/file.txt","r");
//访问文档根目录下的文件,也以只读模式打开
$handle = fopen("$_SERVER['DOCUMENT_ROOT']/data/info.txt","r");
//在Windows平台上,转义文件路径中的每个反斜线,或者用斜线,以二进制和只写模式组合
$handle = fopen("c:\\data\\file.gif","wb");
//使用相对路径打开file.txt文件,选择只读模式,并返回资源$handle
$handle = fopen("../data/info.txt","r");
//打开远程文件,使用HTTP协议只能以只读的模式打开
$handle = fopen("http://www.example.com/","r");
//使用HTTP协议打开远程文件,如果FTP服务器可写,则可以以写的模式打开
$handle = fopen("ftp://user:password@example.com/somefile.txt","w");
?>
函数fclose()
函数fclose()就会撤销fopen()打开的资源类型,成功时返回TRUE,否则返回FALSE。参数必须使用fopen()或fsockopen()函数打开的已存的文件指针。在目录操作中opendir()函数也是开启一个资源,使用closedir()将其关闭。
写入文件
将程序中的数据保存到文件中比较容易,使用fwrite()函数就可以将字符串内容写入文件中。在文件中通过字符序列\n表示换行符,标识文件中一行的末尾。当需要一次输入或输出一行信息时,不同的操作系统具有不同的结束符号,基于UNIX的系统使用“\n”作为行结束字符,基于Windows的系统使用“\n\r”作为行结束字符,基于Macintosh的系统使用“\r”作为行结束字符。当要写入一个文本文件并想插入一个新行时,需要使用相应操作系统的行结束符号。函数fwrite()的原型如下所示:1
int fwrite(resource handle,string string [,int length]) //写入文件
第一个参数需要提供fopen()函数打开的文件资源,该函数将第二个参数提供的字符串内容输出到由第一个参数指定的资源中。如果给出了第三个可选参数length,fwrite()将在写入了length个字符时停止。否则将一直写入,直到到达内容结束时才停止。如果写入的内容少于length个字节,该函数也会在写完全部内容后停止。函数fwrite()执行完成以后会返回写入的字符数,出现错误时则返回FALSE。1
2
3
4
5
6
7
8
9
10<?php
$fileName="data.txt"; //声明一个变量用来保存文件名
//使用fopen()函数以只写的模式打开文件,如果不存在则创建它,打开失败则通过程序
$handle = fopen($fileName, 'w') or die('打开<b>'.$fileName.'</b>文件失败!!');
for($row=0; $row<10; $row++) //循环10次写入10行数据到文件中
fwrite($handle, $row.": www.lampbrother.net\n"); //写入文件
fclose($handle); //关闭由fopen()打开的文件指针资源
?>
另外,写入文件还可以使用fputs()函数,该函数是fwrite()函数的别名函数。如果需要快速写入文件还可以使用file_put_contents()函数,和依次调用fopen(),fwrite()以及fclose()函数的功能一样。1
2
3
4
5
6
7
8
9<?php
$fileName="data.txt"; //声明一个变量用来保存文件名
$data="共10行数据\n"; //声明一个变量用来保存被写入文件中的数据
for($row=0; $row<10; $row++) //使用循环形成10行数据
$data.=$row.": www.lampbrother.net\n"; //将10数据都存放到一个字符串变量中
file_put_contents($fileName, $data); //一次将所有数据写入到指定的文件中
?>
该函数可以将数据直接写入到指定的文件中。如果同时调用多次时,并向同一个文件中写入数据,则文件中只保存了最后一次调用该函数写入的数据。因为在每次调用时都会重新打开文件并将文件中原有的数据清空,所以不能连续写入多行数据。
读取文件内容
函数 | 描述 |
---|---|
fread() | 读取打开的文件 |
file_get_contents() | 将文件读入字符串 |
fgets() | 从打开的文件中返回一行 |
fgetc() | 从打开的文件中返回字符 |
file() | 把文件读入一个数组中 |
readfile() | 读取一个文件,并输出到输出缓冲 |
函数fread()
该函数用来在打开的文件中读取指定长度的字符串。也可以安全用于二进制文件,在区分二进制文件和文本文件的系统(如Windows)打开文件时,fopen()函数的mode参数要加上’b’。函数fwrite()的原型如下所示:1
string fread(int handle,int length) //读取打开的文件
该函数从文件指针资源handle中读取最多length个字节。在读取完length个字节数,或到达EOF的时候,或当一个包可用都会停止读取文件,就看先碰到哪种情况了。该函数返回读取的内容字符串,如果失败则返回FALSE。
1 | <?php |
如果你只是想将一个文件的内容读入到一个字符串中,可以用file_get_contents()函数,它的性能比上面的代码好得多。file_get_contents()函数用来将文件的内容读入到一个字符串中的首选方法,如果操作系统支持还会使用内存映射技术来增强性能。1
2
3
4<?php
echo file_get_contents("data.txt"); //读取文本文件中的内容并输出
echo file_get_contents("c:\\files\\somepic.gif"); //读取二进制文件中的内容并输出
?>
函数fgets()、fgetc()
该函数一次至多从打开的文件资源中读取一行内容。函数fgets()的原型如下所示:1
string fgets(int handle[,int length) //从打开的文件中返回一行
第一个参数提供使用fopen()函数打开的资源。如果提供了第二个可选参数length,该函数返回length-1个字节。或者返回遇到换行或EOF之前读取的所以内容。如果忽略可选的length参数,默认为1024个字符前遇到换行符号,因此每次成功调用都会返回下一行。如果读取失败则返回FALSE。1
2
3
4
5
6
7
8<?php
$handle = fopen("data.txt", "r") or die("文件打开失败"); //以只读模式打开文件
while (!feof($handle)) { //循环读取第一行,使用feof判断读取文件结尾
$buffer = fgets($handle, 4096); //一次读取打开文件中的一行内容
echo $buffer."<br>"; //输出每一行,并加上HTML的换行标记
}
fclose($handle); //关闭打开的文件资源
?>
函数fgetc()在打开的文件资源中只读取当前指针位置处的一个节符。如果遇到文件结束标志EOF时,将返回FALSE值。1
2
3
4
5
6
7<?php
$fp = fopen('data.txt', 'r') or die("文件打开失败"); //以只读模式打开文件
while (false !== ($char = fgetc($fp))) { //循环文件中读取一个节符碰到 EOF 标记停止
echo $char."<br>"; //输出单个字符
}
?>
函数file()
该函数非常有用,与file_get_contents()类似,不需要使用fopen()函数打开文件,不同的是file()函数可以把整个文件读入到一个数组中。数组中的每个元素对应文件中的行,各元素由换行符分隔,同时换行符仍附加在每个元素的末尾。这样,就可以使用数组的相关函数对文件内容进行处理。1
2
3<?php
print_r(file("test.txt"); //将文件test.txt中的内容读入到一个数组中,并输出。
?>
函数readfile()
该函数可以读取指定的整个文件,立即输出到输出缓冲区,并返回读取的字节数。该函数也不需要使用fopen()函数打开文件。1
2
3<?php
readfile("data.txt"); //直接将文件data.txt中的数据独处并输出到浏览器
?>
访问远程文件
使用PHP不仅可以让用户通过浏览器访问服务器端的文件,还可以通过HTTP或FTP等协议访问其他服务器中的文件,可以在大多数需要文件名作为参数的函数中使用HTTP和FTP URL来代替文件名。使用fopen()函数将指定的文件名与资源绑定到一个流上,如果文件名是“scheme://..”的格式,则被当成一个URL,PHP将搜索协议处理器来处理此模式。
如果需要访问远程文件,必须在PHP的配置文件中激活“allow_url_fopen”选项,才能使用fopen()函数打开远程文件。而且还要确定其他服务器中的文件是否有访问权限,如果使用HTTP协议对远程文件进行连接,只能以“只读”模式打开。如果需要访问的远程FTP服务器中,对所提供的用户开启了“可写”权限,则使用FTP协议连接远程的文件时,就可以使用“可写”或“可读”模式打开文件。但不可以使用“可读可写”的模式。
使用PHP访问远程文件就向访问本地文件一样,都是使用相同的读写函数处理。1
2
3
4
5
6
7
8
9
10
11
12
13
14<?php
$file = fopen ("http://www.lampbrother.com/", "r") or die("打开远程文件失败!!"); //打开远程文件
while (!feof ($file)) { //循环从文件中读取内容
$line = fgets ($file, 1024); //每读取一行
//如果找到远程文件中的标题标记则取出标题,并退出循环,不在读取文件
if (preg_match("/<title>(.*)<\/title>/", $line, $out)) { //使用正则匹配标题标记
$title = $out[1]; //将标题标记中的标题字符取出
break; //退出循环,结束远程文件读取
}
}
fclose($file); //关闭文件资源
echo $title; //输出获取到的远程网页的标题
?>
1 | <?php |
移动文件指针
在对文件进行读写过程中,有时需要在文件中跳转、从不同位置读取,以及将数据写入到不同的位置。指针的位置是以从文件头开始的字节数度量的,默认以不同模式打开文件时,文件指针通常在文件的开头或者结尾处,可以通过ftell()、fseek()和rewind()三个函数对文件指针进行操作。1
2
3int ftell(resource handle) //返回文件指针的当前位置
int fseek(resource handle,int offset[,int whence]) //移动文件指针到指定位置
bool rewind(resource handle) //移动文件指针到文件的开头
使用这些函数时,必须提供一个用fopen()函数打开的、合法的文件指针。函数ftell()获取由指定的资源中的文件指针当前位置的偏移量;函数rewind()将文件指针移回到指定资源的开头;而函数fseek()则将指针移动到第二个参数offset指定的位置,如果没有提供第三个可选参数whence,则位置将设置为从文件开头的offset字节处。否则,第三个参数whence可以设置为三个可能的值,它将影响指针的位置。
- SEEK_CUR:设置指针位置为当前位置加上第二个参数所提供的offset字节
- SEEK_END:设置指针位置为EOF加上offset字节。在这里,offset必须设置为负值
- SEEK_SET:设置指针位置为offset字节处。这与忽略第三个参数whence效果相同。
如果fseek()函数执行成功,将返回0,失败则返回-1.如果将文件以追加模式“a”或者“a+”打开,写入文件的任何数据总是会被附加在后面,不会管文件指针的位置。
1 | <?php |
文件的一些基本操作函数
函数 | 语法结构 | 描述 |
---|---|---|
copy() | copy(来源文件,目的文件) | 复制文件 |
unlike() | unlike(目标文件) | 删除文件 |
ftruncate() | ftruncate(目标文件资源,截取长度) | 将文件截断到指定长度 |
rename() | rename(旧文件名,新文件名) | 重命名文件或目录 |
1 | <?php |
文件的上传和下载
文件上传
客户端上传设置
文件上传的最基础方法,是使用HTML表单选择本地文件进行提交,在form表单中可以通过标记选择本地文件。如果支持文件上传操作,必须在
- enctype=”munltipart/form-data”用来指定表单编码数据方式,让服务器知道,我们要传递一个文件,并带有常规的表单信息
- method=”POST”用来指明发送数据的方法。
另外,还需要在form表单中设置一个hidden类型的input框。其中name值为MAX_FILL_SIZE的隐藏值域,并通过设置其VALUE的值限制上传文件的大小(单位字节),但这个值不能超过PHP的配置文件中upload_max_filesize值设置的大小。1
2
3
4
5
6
7
8
9
10<html>
<head><title>文件上传</title></head>
<body>
<form action="upload.php" method="post" enctype="multipart/form-data">
<input type="hidden" name="MAX_FILE_SIZE" value="1000000">
选择文件:<input type="file" name="myfile">
<input type="submit" value="上传文件">
</form>
</body>
</html>
在服务器端通过PHP处理上传
客户端的上传表单只能提供本地文件选择,以及提供将文件发送给服务器的标准化方式,但并没有提供相关功能来确定文件到达目的地后发生了什么。所以上传文件的接收和后续处理就要通过PHP脚本来处理。要通过PHP成功地管理文件上传,需要通过以下三方面信息。
- 设置PHP配置文件中的指令:用于精细地调节PHP的文件上传功能
- $_FILES多维数组:用于存储各种与上传文件有关的信息。
- PHP的文件上传处理函数:用于上传文件的后续处理。
PHP配置文件中与文件上传有关的选项
指令名 | 默认值 | 功能描述 |
---|---|---|
file_uploads | ON | 确定服务器上的PHP脚本是否可以接受HTTP文件上传 |
memory_limit | 8M | 设置脚本可以分配的最大内存量,防止失控的脚本独占服务器内存 |
upload_max_filesize | 2M | 限制PHP处理上传文件大小的最大值,此值必须小于post_max_size值 |
post_max_size | 8M | 限制通过POST方法可以接受信息的最大值,此值应当大于配置指令upload_max_file的值,因为除了上传的文件之外,还可能传递其他的表单域 |
upload_tmp_dir | NULL | 上传文件存放的临时路径,可以是一个绝对路径。这个目录对于拥有此服务器进程用户必须是可写的。上传的文件在处理之前必须成功传输到服务器,所以必须指定一个位置,可以临时放置这些文件,知道文件移到最终目的地为止。例如:upload_tmp_dir=/tmp/.uploads/。默认值为NULL则为操作系统的临时文件夹 |
表单提交给服务器的数据,可以通过在PHP脚本中使用全局数组 $_GET、 $_POST或 $_REQUEST 接收。而通过POST方法上传的文件有关信息都被存储在多维数组 $_FILES中,这些信息对于通过PHP脚本上传到服务器的文件至关重要。因为文件上传后,首先存储于服务器的临时目录中,同时在PHP脚本中就获取一个$_FILES全局数组。$_FILES数组的第二维中共有五项。
全局数组$_FILES中的元素说明
数组 | 描述 |
---|---|
$_FILES[“myfile”][“name”] | 客户端机器文件的原名称,包括扩展名 |
$_FILES[“myfile”][“size”] | 已上传文件的大小,单位为字节 |
$_FILES[“myfile”][“tmp_name”] | 文件被上传后,在服务器端存储的临时文件名。这是存储在临时目录(由PHP指令upload_tmp_dir指定)中所指定的文件名 |
$_FILES[“myfile”][“error”] | 伴随文件上传时产生的错误信息,有五个可能的值。 0:表示没有发生任何错误,文件上传成功 1:表示上传文件的大小超出了在PHP配置文件中指令upload_max_filesize选项限制的值 2:表示上传文件大小超出了HTML表单中MAX_FILE_SIZE选项指定的值 3:表示文件只被上载* 4:标识没有上载任何文件 |
$_FILES[“myfile”][“type”] | 获取从客户端上传文件MIME类型,MIME类型规定了各种文件格式的类型,每种MIME类型都是由“/”分隔的主类型和子类型组成,如“image/gif”,主类型为“图像”,子类型为GIF格式的文件,“text/html”代表文本的HTML文件,还有很多其他不同类型的文件。 |
- 函数is_upload_file()
该函数判断指定的文件是否通过HTTP POST上传的,如果是则返回TRUE。用于防止潜在的攻击者对原本不能通过脚本交互的文件进行非法管理,这可以用来确保恶意的用户无法欺骗脚本去访问不能访问的文件,例如/etc/passwd。1
Bool is_uploaded_file( string filename) //判断指定的文件是否通过HTTP POST上传的
为了能使此函数正常工作,唯一的参数必须指定类似于$_FILES[‘userfile’][‘tmp_name’]的变量,才能判断指定的文件确定是上传文件。如果使用从客户端上传的文件名$_FILES[‘userfile’][‘name’]则不能正常运作。
- 函数move_uploaded_file()
文件上传后,首先会存储于服务器的临时目录中,可以使用该函数将上传的文件移动到新位置。此函数的原型如下所示1
Bool move_uploaded_file(string filename,string destination) //将上传的文件移动到新位置
虽然函数copy()和函数move()也同样好用,但函数move_uploaded_file()还提供了一种额外的功能,检查并确保由第一个参数filename指定的文件,是否是合法的上传文件。如果文件合法,则将其移到为由第二个参数destination指定的文件。如果filename不是合法的上传文件,不会出现任何操作,将返回FALSE。如果filename是合法的上传文件,但处于某些原因无法移动,不会出现任何操作,也将返回FALSE。此外还会发出一条警告。若成功则返回TRUE。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40<?php
if($_FILES['myfile']['error'] > 0) { //判断文件是否可以成功上传到服务器,0表示上传成功
echo '上传错误: ';
switch ($_FILES['myfile']['error']) {
case 1:
echo '上传文件大小超出了PHP配置文件中的约定值:upload_max_filesize';
break;
case 2:
echo '上传文件大小超出了表单中的约定值:MAX_FILE_SIZE';
break;
case 3:
echo '文件只被部分上载';
break;
case 4:
echo '没有上传任何文件';
break;
}
exit; //如果$_FILES['myfile']['error']大于0都是有错误,输出错误信息并退出程序
}
//获取上传文件的MIME类型中的主类型和子类型
list($maintype,$subtype)=explode("/",$_FILES['myfile']['type']);
if ($maintype=="text") { //通过主类型限制不能上传文本文件,例如.txt .html .php等文件文件
echo '问题: 不能上传文本文件。';
exit; //如果用户上传文本文件则退出程序
}
$upfile = './uploads/'.time().$_FILES['myfile']['name']; //定义上传后的位置和新文件名
if (is_uploaded_file($_FILES['myfile']['tmp_name'])) { //判断是否为上传文件
if (!move_uploaded_file($_FILES['myfile']['tmp_name'], $upfile)) { //从移动文件
echo '问题: 不能将文件移动到指定目录。';
exit;
}
}else{
echo '问题: 上传文件不是一个合法文件: ';
echo $_FILES['myfile']['name'];
exit;
}
echo '文件'.$upfile.'上传成功,大小为'.$_FILES['myfile']['size'].'!<br>'; //如果文件上传成功则输出
?>
处理多个文件上传
多个文件上传和单独文件上传的处理方式是一样的,只需要在客户端多提供几个类型为“file”的输入表单,并指定不同的“name”属性值。1
2
3
4
5
6
7
8
9
10
11
12<html>
<head><title>文件上传</title></head>
<body>
<form action="mul_upload.php" method="post" enctype="multipart/form-data">
<input type="hidden" name="MAX_FILE_SIZE" value="1000000">
选择文件1:<input type="file" name="myfile[]"><br>
选择文件2:<input type="file" name="myfile[]"><br>
选择文件3:<input type="file" name="myfile[]"><br>
<input type="submit" value="上传文件">
</form>
</body>
</html>
在上面的代码中,将三个文件类型的表单以数组的形式组织在一起。当上面的表单提交给PHP的脚本文件mul_upload.php时,在服务器端同时使用全局数组$_FILES存储所有上传文件的信息,但$_FILES由二维数组已经转变为三维数组,这样就可以存储多个上传文件的信息。在脚本文件mul_upload.php中,使用print_r()函数将$_FILES数组中的内容输出。1
2
3<?php
print_r($_FILES); //打印三维数组$_FILES中的内容,查看一下存储上传文件的结构
?>
文件下载
简单的文件下载只需要使用HTML的链接标记a,并将属性href的URL值指定下载的文件即可1
<a href="http://www.example.com/download/example.rar">下载文件</a>
如果通过上面的代码实现文件下载,只能处理一些浏览器不能默认识别的MIME类型文件,例如,当访问example.rar文件时,浏览器并没有直接打开,而是淡出一个下载提示框,提示用户“下载”还是“打开”等处理方式。但如果需要下载后缀名为.html的网页文件、图片文件以及PHP程序脚本文件等,用这种链接形式,则会将文件内容直接输出到浏览器中,并不会提示用户下载。
为了提供文件的安全性,不希望在a标签中给出文件的链接,则必须想浏览器发送必要的头信息,以通知浏览器将要进行下载文件的处理。PHP使用header()函数发送网页的头部信息给浏览器,该函数接受一个头信息的字符串作为参数。文件下载需要发送的头信息包括以下三部分,通过调用三次header()函数完成。以下载图片test.gif为例,需要发送的头信息的代码如下所示:1
2
3header('Content-Type:image/gif'); //发送指定文件MIME类型的头信息
header('Content-Disposition:attachment;filename="test.gif"'); //发送描述文件的头信息,附件和文件名
header('Content-Length:3390'); //发送指定文件大小的信息,单位字节
如果使用header()函数向浏览器发送了这三行头信息,图片test.gif就不会直接在浏览器中显示,而是让浏览器将该文件形式下载的形式。在函数header()中,“Content-Type”指定了文件的MIME类型,“Content-Disposition”用于文件的描述,值“attachment;filename=”test.gif””说明这是一个附件,并且指定了下载后的文件名,“Content-Length”则给出被下载文件的大小。1
2
3
4
5
6
7<?php
$filename="test.gif"; //指定文件名
header('Content-Type: image/gif'); //指定下载文件类型
header('Content-Disposition: attachment; filename="'.$filename.'"'); //指定下载文件的描述
header('Content-Length: '.filesize($filename)); //指定下载文件的大小
readfile($filename); //将文件内容读取出来并直接输出,以便下载
?>
MySQL数据库设计
MySQL数据库管理
结构化查询语句SQL
SQL语句包含四个部分。
- 数据定义语言(DDL):用于定义和管理数据对象,包括数据库、数据表等。例如:CREATE、DROP、ALTER等语句
- 数据操作语言(DML):用于操作数据库对象中所包含的数据。例如:INSERT、UPDATE、DELETE语句。
- 数据查询语言(DQL):用于查询数据库对象 中所包含的数据,能够进行单表查询、连接查询、嵌套查询,以及集合查询等各种复杂程度不同的数据库查询,并将数据返回到客户机中显示。例如:SELECT语句。
- 数据控制语言(DCL):是用来管理数据库的语言,包含管理权限及数据更改。例如:GRANT、REVOKE、COMMIT、ROLLBACK等语句。
MySQL数据库的连接与关闭
1 | mysql -h 服务器主机地址 -u 用户名 -p 用户密码 |
- -h:指定所连接的数据库服务器位置,可以是IP地址,也可以是服务器域名
- -u:指定连接数据库服务器使用的用户名,例如root为管理员用户具有所有权限
- -p:连接数据库服务器使用的密码,但-p和其后的参数之间不要有空格。最后是在该参数后直接回车,然后以密文的形式输入密码
如果需要退出客户机,可以任何输入exit或quit命令结束会话
创建、选择及查看数据库
1 | CREATE DATABASE [IF NOT EXISTS]bookstore; #创建一个名为bookstore的数据库 |
1 | DROP DATABASE [IF EXISTS]bookstore; #删除一个名为bookstore的数据库 |
1 | SHOW DATABASES; #显示所有已建立的数据库名称列表 |
1 | USE bookstore; #打开bookstore数据库为当前数据库使用 |
MySQL数据库中数据表的设计
数据表(Table)
数据表是数据库中基本对象元素,以记录(行)和字段(列)组成的二维结构用于存储数据。数据表由表结构和表内容两部分组成,先建立表结构,然后才能输入数据。数据表结构设计主要包括:字段名称、字段类型和字段属性的设置。
数据值和列类型
对MySQL中数据值的分类,有数值型、字符型、日期型和空值等,MySQL数据库的表是一个二维表,由一个或多个数据列构成。MySQL中的列类型有三种:数值类、字符串类和日期/时间类。
数组类的数据列类型
MySQL中的数值分整型和浮点型两种。而整型中又分为五种整形数据列类型,即TINYINT,SMALLINT,MEDIUMINT,INT和BIGINT。MySQL也有三种浮点型数据列类型,分别是:FLOAT,BOUBLE和DECIMAL。
数据列类型 | 存储空间 | 说明 | 取值范围 |
---|---|---|---|
TINYINT | 1字节 | 非常小的整数 | 带符号值:-128-127 无符号:0-255 |
SMALLINT | 2字节 | 较小的整数 | 带符号值:-32768-32767 无符号:0-65535 |
MEDIUMINT | 3字节 | 中等大小的整数 | 带符号值:-8388608-8388607 无符号:0-16777215 |
INT | 4字节 | 标准整数 | 带符号值:-2147483648-2147483647 无符号:0-4294967295 |
BIGINT | 8字节 | 大整数 | 带符号值:-9223372036854775808-9233372036854775807 无符号:0-18446744073709551615 |
FLOAT | 4或8字节 | 单精度浮点数 | 最小非零值:+-1.175494351E-38 最大非零值:+-3.402823466E+38 |
DOUBLE | 8字节 | 双精度浮点数 | 最小非零值:+-2.2250738585072014E-308 最大非零值:+-1.7976931348623157E+308 |
DECIMAL | 自定义 | 以字符串形式表示的浮点数 | 取决于存储单位字节数 |
字符串类数据列类型
数据列类型 | 存储空间 | 说明 | 最大长度 |
---|---|---|---|
CHAR[(M)] | M字节 | 定长字符串 | M字节 |
VARCHAR[(M)] | L+1字节 | 可变字符串 | M字节 |
TINYBLOD,TINYTEXT | L+1字节 | 非常小的BLOB(二进行大对象)和文本串 | 2e+8-1字节 |
BLOB,TEXT | L+2字节 | 小BLOB和文本串 | 2e+16-4字节 |
MEDIUMBLOB,MEDIUMTEXT | L+3字节 | 中等的BLOB和文本串 | 2e24-1字节 |
LONGBLOB,LONGTEXT | L+4字节 | 大BLOB和文本串 | 2e32-1字节 |
ENUM(‘value1’,’value2’,…) | 1或2字节 | 枚举:可赋予某个枚举成员 | 65535个成员 |
SET(‘value1’,’value2’,…) | 1,2,3,4或8字节 | 集合:可赋予多个集合成员 | 64个成员 |
对于可变长的字符串类型,其长度取决于实际存放在劣种的值的长度。此长度用L来表示。
日期和时间型数据列类型
数据列类型 | 存储空间 | 说明 | 最大长度 |
---|---|---|---|
DATE | 3字节 | “YYYY-MM-DD”格式表示的日期值 | 1000-01-01~9999-12-31 |
TIME | 3字节 | “hh:mm:ss”格式表示的时间值 | -838:59:59~838:59:59 |
DATETIME | 8字节 | “YYYY-MM-DD hh:mm:ss”格式 | 1000-01-01 00:00:00到9999-12-31 23:59:59 |
TIMESTAMP | 4字节 | “YYYYMMDDhhmmss”格式表示的时间戳 | 19700101000000~2037年的某个时刻 |
YEAR | 1字节 | “YYYY”格式的年份量 | 1901~2155 |
NULL值
NULL意味着”没有值”或”未知值”,MySQL中,0或NULL都意味着假而其他值意味着真。布尔运算的默认真值是1.
数据字段属性
UNSIGNED
该属性也只能用于设置数值类型,在数值之前自动用0补齐数据列出现负数。
ZEROFILL
该属性也只能用于设置数值类型,在数值之前自动用0补充不足的位数。
AUTO_INCREMENT
该属性用于设置字段的自动增量属性,当数值类型的字段设置为自动增量时,每增加一条新纪录,该字段的值就自动加1,而且此字段的值不允许重复。此修饰符只能修饰整数类型的字段。
NULL和NOT NULL
默认为NULL,即插入值时没有在此字段插入值,默认为NULL值,如果指定了NOT NULL,则必须在插入值时在此字段添入值
DEFAULT
可以通过此属性来指定一个默认值,如果没有在此列添加值,那么默认添加此值。
创建、修改及删除表
创建表(CREATE TABLE)
1 | CREATE TABLE [IF NOT EXISTS] 表名称( #创建带给定名称的表,必须拥有表CREATE权限 |
其中[]中的为可选的内容,一个表可以由一个或多个字段(列)组成,在字段名后面一定要注明该字段的数据类型。每个字段也可以使用属性对其进行限制说明,但属性是可选的,根据表的需要进行声明。
修改表(ALTER TABLE)
可以用ALTER TABLE语句来修改表的结构,包括添加新的字段、删除原有的字段、修改列的类型、属性及索引,甚至可以修改表的名称。1
ALTER TABLE 表名 ACTION; #修改表的语法格式
其中ACTION是ALTER TABLE的从句,包括为指定的表添加一新列、为表添加一个索引、更改指定列默认值、更改列类型、删除一列、删除索引、更改表名等语句。
- 为指定的数据表添加一新字段,可以在ACTION从句中使用ADD关键字实现
1
ALTER TABLE 表名 ADD 字段名 <建表语句>[FIRST|AFTER 列名] #为指定的表添加新列
如果没指定可选的FIRST或AFTER,则在列尾添加一列,否则在指定列添加新列。
- 为指定的数据表为了更改原有字段的类型,可使用CHANGE或MODIFY子句。如果原列的名字和新列的名字相同,则change或Modify的作用相同。
1
ALTER TABLE 表名 CHANGE(MODIFY) 列表 <建表语句> #为指定的表修改列类型
CHANGE除了更改类型外还能更改列名,而MODIFY不能实现这个功能。
- 如果需要为指定的数据表重新命名,可使用RENAME AS子句,给出旧表名和新表名即可。
1
ALTER TABLE 旧表名 RENAME AS 新表名 #为 指定的数据表重新命名
删除表(DROP TABLE)
可以使用SQL的DROP TABLE语句删除。删除表要比创建和修改表要容易得多,只需要指定表名即可。1
DROP TABLE[IF EXISTS] 表名
当不能确定数据表是否存在,如果存在就删除它,如果存在就删除它,不存在则删除时也不行出现错误,就可在DROPTABLE语句中增加IF EXISTS。同CREATE TABLE一样,IF EXISTS语句在含有DROP TABLE的SQL脚本中很常用,如果不存在待删除的表,脚本会继续向下执行而不会跑出错误。
数据表的类型及存储位置
MySQL支持MyISAM、InnoDB、HEAP、BOB、ARCHIVE、CSV等多种数据表类型,在创建一个新MySQL数据库表时,可以为它设置一个类型。
MyISAM数据表
MyISAM数据表类型的特点是成熟、稳定和易于管理。
InnoDB数据表
InnoDB给MySQL提供了具有提交,回滚和崩溃恢复能力的事务安全存储引擎。
MyISAM和InnoDB两个表的功能简单对比
表类型功能对比 | MyISAM表 | InnoDB |
---|---|---|
事务处理 | 不支持 | 支持 |
数据行锁定 | 不支持,只有表锁定 | 支持 |
外键约束 | 不支持 | 支持 |
表空间大小 | 相对小 | 相对大,最大是2倍 |
全文索引 | 支持 | 不支持 |
COUNT问题 | 无 | 执行COUNT(*)查询,速度慢 |
1 | CREATE TABLE t(i INT) ENGINE = INNODB; #新建表t时指定表类型为INNODB |
数据表的默认字符集
使用CREATE TABLE命令创建数据表时,如果没有明确地指定任何字符集,则新创建数据库的字符集将由MySQL配置文件里character-set-server选项的设置决定。在MySQL配置文件(Linux系统为/etc/my.cnf文件,Windows系统则是my.ini文件)里设置数据表的字符集。1
2character-set-server = gbk #设置MySQL服务器的字符集
collation-server = gbk_chinese_ci #设置排序方法
1 | CREATE DATABASE IF NOT EXISTS mydb DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci; |
创建索引
在MySQL中主要有四类索引:主键索引(PRIMARY KEY)、唯一索引(UNIQUE)、常规索引(INDEX)和全文索引(FULLTEXT)。
主键索引(PRIMARY KEY)
主键索引是关系数据库中最常见的索引类型,主要作用是确定数据表里一条特定的数据记录的位置。数据表会根据主键的唯一性来唯一标识每条记录,使用任意两条记录里的主键字段不允许是同样的内容,这样可以加快寻址定位时的速度。最好为每张数据表指定一个主键,但一个表只能指定一个主键,而且主键的值不能为空,不过可以有多个候选索引。
唯一索引(UNIQUE)
唯一索引与主键索引一样,都可以防止创建重复的值。但是,不同之处在于,每个数据表中只能有一个主键索引,但可以有多个唯一索引。如果能确定某个数据列将只包含彼此各不相同的值,在为这个数据列创建索引的时候就应该使用关键字UNIQUE把它定义为一个唯一索引。
常规索引(INDEX)
创建常规索引可以使用关键字KEY或INDEX随表一同创建,KEY通常是INDEX同义词。也可以在创建表之后CREATE INDEX或ALTER TABLE命令来创建。
如果在创建表时没有创建索引,就需要使用CREATE INDEX命令来创建同样的常规索引。1
CREATE INDEX ind ON carts(userId,bookId); #创建名称为ind的索引为carts表的两个列
全文索引(FULLTEXT)
全文索引在MySQL中是一个FULLTEXT类型索引,但FULLTEXT索引只能用于MyISAM表,并且只可以在CHAR、VARCHAR或TEXT类型的列上创建,也允许创建在一个或多个数据列上。
SQL语言设计
使用DML命令操作数据表中的数据记录
为了修改数据库中的数据,SQL的数据操纵语言(DML)提供了增(INSERT)、删(DELETE)、改(UPDATE)语句。
使用INSERT语句想数据表中添加数据
插入数据是向已经存在的数据表中添加一条新的记录,应该使用INSERT INTO语句。1
INSERT INTO 表名 [(字段名1,字段名2,...,字段名n] VALUES('值1','值2',...,'值n');
在表名后面的括号中是该表中定义的字段名称列表,它们与VALUES子句后面的表达式列表的值是一一对应的,个数也要相等,并且表达式值的类型必须与字段的类型一致。需要用逗号将各个数据分开,字符型数据要用单引号括起来。INSERT语句也可以省略字段列表,但必须插入一行完整的数据,而且必须按表中定义的字段顺序为全部字段提供值。
使用UPDATE语句更新数据表中已存在的数据
SQL语句可以使用UPDATE语句对表中的一列或多列数据进行修改,必须指定需要修改的字段,以及需要赋予的新值。1
2
3UPDATE 表名 #需要给出被修改的表格
SET 字段名=表达式[, .....] #可以对表中一列或多列数据进行修改
[WHERE 条件]; #给出必要的WHERE子名指定要更新的数据行
其中的WHERE子句是必需的,如果不使用WHERE检索条件,则UPDATE语句会将数据表中全部数据行都修改。
使用DELETE语句删除数据表中不需要的数据记录
使用DELETE语句用来删除数据表中的一条或多条数据记录。1
DELETE FROM 表名 [WHERE 条件]; #删除表中记录行的DELETE语法格式
如果没有指定WHERE子句的检索条件,DELETE语句将会删除数据表中的全部数据记录,使用数据库中只剩下数据表结构。
通过DQL命令查询数据表中的数据
1 | SELECT [ALL|DISTINCT] #使用SELECT语句查询检索 |
用中括号“[]”括起来的部分表示是可选的,用大括号“{}”括起来的部分是标识必须从中选择其中的一个。FROM子句指定了SELECT语句中字段的来源,FROM子句后面是包含一个或多个的表达式(由逗号分开),其中的表达式可为单一表名称、已保存的查询或由INNER JOIN、LEFT JOIN或RIGHT JOIN得到的复合结果。
选择特定的字段
使用SELECT语句检索记录的特定字段,多个字段可以用逗号分隔。1
SELECT field1,field2 FROM table1;
可以使用“”从表中检索出所有字段,使用“SELECT”主要是针对用户的书写方便而言。对于不同的数据表,这个操作可将表中每一行、列的数据全部检索出来。
使用AS子句为字段取别名
如果想为返回的列取一个新的标题,以及经过对字段的计算或总结之后,产生了一个新的值,希望把它放到一个新的列里显示,则用AS保留。1
SELECT Name as '名称',author as '作者',price as '价格'FROM tables;
定义别名时一定要使用单引号引起来。其中AS关键字是可选的,在原字段名和别名之间使用一个空格即可。
在有多个表关联查询的情况下,如果表中有同名的字段时,则必须使用别名加以区分。
DISTINCT关键字的使用
在使用SELECT语句返回的记录结构中包含重复的记录,可以使用DISTINCT关键字取消重复的数据,只返回一个。DISTINCT关键字的作用范围是整个查询的列表,而不是单独的一列。同时对两列数据进行查询时,使用了DISTINCT关键字,将返回这两列数据的唯一组合。1
SELECT DISTINCT idFROM table; #取消id列中重复的数据。
使用DISTINCT关键字可以返回简单、明了的数据,但服务器必须话费更多的时间去执行对查询结果的分类和整理。与DISTINCT相对应的是ALL关键字,用于返回满足SELECT语句条件的所有记录。
在SELECT语句中使用表达式的列
在SQL语句中,表达式可用于一些诸如SELECT语句的ORDER BY或HAVING子句、SELECT、DELETE或UPDATE语句的WHERE子句或SET语句之类的地方。使用文本值、column值、NULL值、函数、操作符来书写表达式。主要包括算术表达式、逻辑表达式,以及使用SQL函数表达式。
在SELECT语句中使用表达式重新对数据列进行计算。
使用WHERE子句按条件检索
在SELECT语句中,可以使用WHERE子句指定搜索条件,实现从数据表中检索出符合条件的记录。其中,搜索条件可以由一个或多个逻辑表达式组成,这些表达式指定关于某一记录是真或假的条件。在WHERE子句中,可以通过逻辑操作符和比较操作符和比较操作符指定基本的表达式条件。
逻辑操作符
操作符 | 语法 | 描述 |
---|---|---|
AND或&& | a AND b或a && b | 逻辑与 |
OR或// | a OR b或a // b | 逻辑或 |
XOR | a XOR b | 逻辑异或 |
NOT或! | NOT a或!a | 逻辑非 |
比较操作符
操作符 | 语法 | 描述 |
---|---|---|
IS NULL | a IS NULL | 若操作数a为NULL,则为真 |
IS NOT NULL | a IS NOT NULL | 若操作数a不为NULL,则为真 |
BETWEEN | a BETWEEN b AND c | 若a在b和c之间(包括b和c),则为真 |
NOT BETWEEN | a NOT BETWEEN b AND c | 若a不在b和c之间(包括b和c),则为真 |
LIKE | a LIKE b | SQL模式匹配,若a匹配b,则为真 |
NOT LIKE | a NOT LIKE b | SQL模式匹配,若a不匹配b,则为真 |
IN | a IN(b1,b2,b3,…) | 若a等于b1,b2,b3,…中的某一个,则为真 |
根据空值(NULL)确定检索条件
空值只能定义在允许NULL字段中出现,NULL值是特殊的值,代表“无值”,与0和空字符串(”)都不相同。当在不支持默认值的字段中未输入值,或在字段中显式的设置为空,就会出现空值,但不能用处理已知值的方式来出来NULL。1
SELECT*FROM books WHERE detail IS NOT NULL; #查找不为空的数据
使用BETWEEN AND进行范围比较查询
需要对某个字段通过范围的值进行比较查询,可以使用BETWEEN AND关键字实现,其中AND是多重条件符号,比较时也包括边界条件。也可以使用”>=”和”=<”完成相同的功能。1
2SELECT name,price FROM books WHERE price BETWEEN 30 AND 80;
SELECT name,price FROM books WHERE price >= 30 AND price <=80;
使用IN进行范围比较查询
在WHERE子句中,使用IN关键字并在后面的括号”()”中提供一个值的列表,以供与相应的字段进行比较。该列表中至少应该存在一个值,如果有多个值可以使用逗号”,”分隔。
使用LIKE进行模糊查询
在SELECT语句的WHERE子句中,可以使用LIKE关键字对数据表中的记录进行模糊查询,将查询结果锁定在一个范围内。在查询条件中通常会与”_”和”%”两个通配符一起使用,可以实现复制的检索查询。
- 百分号”%”:表示0个或任意多个字符。
- 下划线”_”:表示单个的任意一个字符。
多表查询(连接查询)
- 非等值和等值的多表查询
多表查询和普通的单表查询相似,都是使用SELECT语句。只不过在多表查询时需要把多张表的名字,全部填写在FROM子句中,并用逗号”,”将表名分开。同时,也可以对数据表使用别名进行引用。为了在查询时区分多个表中出现的重复字段名,可以在字段列表中使用”表名.列名”的形式,如果不存在重名的列,可以省略表名。 - 自身连接查询
连接查询操作不仅可以用于多个表之间,也可以是一个表与其自己进行拼接,称为自身连接查询。当一个表所代表的实体之间有关系时,就可以使用自身连接查询。 - 复合连接查询
在FROM子句后面有n个表需要查询,则在WHERE子句中就需要有多个连接条件。至少要比出现的表格数量少一个,也就是不能少于n-1个查询条件,多个条件使用”AND”关键词连接即可。
嵌套查询(子查询)
嵌套查询是在一个SELECT语句的WHERE子句中,包含另一个SELECT语句,也可以称为子查询。在子查询中只能返回一列,并将形成的结果又作为父查询的条件,在主句中进行一布查询。SQL允许多层嵌套查询,嵌套查询的求解方法是由里向外处理,即每个子查询都是在上一级查询处理之前求解,子查询的结果用于建立其父查询的查找条件。
使用ORDER BY对查询结果排序
在SELECT语句中使用ORDER BY子句,对检索的结果进行排序。1
SELECT id,name FROM books ORDER BY id DESC;
ORDER BY后面可以接一列或多列用于排序的字段,并且使用DESC或ASC关键字设计字段排序的方式。默认情况下按照升序排列,即使用ASC关键字。否则要按照降序排列,必须使用DESC关键字。ORDER BY子句可以和SELECT语句中的其他子句一起使用,但在子查询中不能哟ORDER BY子句,因为ORDER BY子句只能对最终查询结果排序。
使用LIMIT限定结构行数
LIMIT子句可以和其他的SELECT子句一起使用,它可以指定两个参数,分别用以设置返回记录的起始位置,和返回记录的数量。
使用统计函数
统计函数 | 描述 |
---|---|
COUNT() | 返回满足SELECT语句中指定条件的记录数,例如,COUNT(*)返回找到的记录行数 |
SUM() | 通常为数值字段或表达列作统计,返回一列的总和 |
AVG() | 通常为数值字段或表达列作统计,返回一列的平均值 |
MAX() | 可以为数值字段、字符字段或表达列作统计,返回一列中最大的值 |
MIN() | 可以为数值字段、字符字段或表达列统计,返回一列中最小的值 |
1 | SELECT 函数名 (列名1或*), ...函数名 (列名n) FROM 表名; #使用统计函数 |
使用GROUP BY对查询结构分组
GROUP BY子句将表按列值分组,列的值相同的分为一组。如果GROUP BY后面有多个列名,则先按第一个列名分组,再在每组中按第二个列名分组。
使用PHP脚本向MySQL服务器发送SQL
在PHP脚本中连接MySQL服务器
PHP可以通过MySQL功能模块去连接MySQL服务器,办法是调用mysql_connect()函数,和使用MySQL客户机程序连接MySQL服务器类似。1
resource mysql_connect([string server[,string username[,string password[,bool new_link[,int client_flags]]]]])
通常只要提供前三个参数即可,包括MySQL服务器的主机名、MySQL用户名和密码。1
2
3<?php
mysql_connect("localhost","root","mysql_pass"); //使用PHP程序连接MySQL服务器
?>
1 | <?php |
在PHP程序中选择已创建的数据库
为了避免每次调用PHP的mysql扩展函数时都指定目录数据库,最好先用mysql_select_db()函数为后续操作选定一个默认数据库,这个函数和SQL命令“USE bookstore”功能相似。1
2
3
4
5<?php
$link=mysql_connect("localhost", "mysql_user", "mysql_password") or die("连接失败: ".mysql_error());
mysql_select_db('bookstore',$link)or die('不能选定数据库:'.mysql_error());
mysql_close($link);
?>
执行SQL命令
在PHP脚本中,只要把SQL命令作为一个字符串传递给mysql_query函数,就会将其发送到MySQL服务器中并执行。如果想访问的不是当前数据库,就需要调用mysql_db_query()函数来执行SQL命令并明确地给出数据库的名字。这两个函数的最后一个参数都是可选的,即mysql_connect()函数返回的连接标识号,只要在与同一个MySQL服务器建立多条连接的时候才必须给出这个参数。
mysql_query()函数可以用来执行DDL、DML、DQL及DCL等任何一种SQL命令,如果想执行一条以上的SQL命令,就需要为它们分别调用一次mysql_query()函数。如果SQL命令执行成功,mysql_query()函数将返回一个非0值。如果没有执行不成功,该函数将返回FALSE(即数值0),并会生成一条出错信息,出错原因可以利用mysql_errno()和mysql_error()函数来确定。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36<?php
$link=mysql_connect("localhost", "mysql_user", "mysql_pass") or die("连接失败: ".mysql_error());
//为后续的mysql扩展函数的操作选定一个默认的数据库,它相当于SQL命令use bookstore
mysql_select_db('bookstore') or die ('不能选定数据库 bookstore : ' . mysql_error());
//将插入3条的INSERT语句声明为一个字符串
$insert="INSERT INTO books(bookName, publisher, author, price, detail) VALUES
('PHP', '电子工业', '高某某', '80.00', '与PHP相关的图书'),
('JSP', '人民邮电', '洛某某', '50.00', '与JSP相关的图书'),
('ASP', '电子工业', '峰某某', '30.00', '与ASP相关的图书')";
//使用mysql_query()函数发送INSERT语句,如果成功返回TRUE,失败则返回FALSE
$result=mysql_query($insert);
if($result && mysql_affected_rows()>0){
echo "数据记录插入成功,最后一条插入的数据记录ID为:".mysql_insert_id()."<br>";
}else{
echo "插入记录失败,错误号:".mysql_errno().",错误原因:".mysql_error()."<br>";
}
//执行UPDATE命令修改表books中的一条记录,将图书名为PHP的记录价格修改为79.90
$result1=mysql_query("UPDATE books SET price='79.9' WHERE bookName='PHP'");
if($result1 && mysql_affected_rows()>0){
echo "数据记录修改成功<br>";
}else{
echo "修改数据失败,错误号:".mysql_errno().",错误原因:".mysql_error()."<br>";
}
//执行DELETE命令删除表books中图书名为JSP的记录
$result2=mysql_query("DELETE FROM books WHERE bookName='JSP'");
if($result2 && mysql_affected_rows()>0){
echo "数据记录删除成功<br>";
}else{
echo "删除数据失败,错误号:".mysql_errno().",错误原因:".mysql_error()."<br>";
}
mysql_close($link); //关闭与MySQL服务器建立的连接
?>
在PHP脚本中处理SELECT查询结果集
在PHP脚本中执行SELECT查询命令,也是调用mysql_query()函数,但和执行DML不同的是,执行SELECT命令之后mysql_query()函数的返回值是一个PHP资源的引用指针(结果集)。这个返回值可以用在各种结果集处理函数中,对结果数据包的各个字段进行处理。1
2
3$result=mysql_query("SELECT * FROM books"); //执行SELECT语句返回结果集资源$result
$rows=mysql_num_rows($result); //从结果几种获得数据记录行的个数
$cols=mysql_num_fields($result); //从结果中获得数据记录列的个数
- mysql_fetch_row():该函数将一条结构记录返回并以一个普通索引数组的形式保存。
- mysql_fetch_assoc():该函数将一条结果记录返回并以一个普通关联数组的形式保存。
- mysql_fetch_array():该函数可以将结果数据表中的每一行获取为一个关联数组或索引数组,或者同时获取为关联和索引数组。可以通过为该函数传递MYSQL_ASSOC、MYSQL_NUM或MYSQL_BOTH中的一个常量返回不同的数组形态,默认使用MYSQL_BOTH常量将两种数组一起返回。
- mysql_fetch_object():该函数将以一个对象的形式返回一条结果记录,它的各个字段需要以对象的方式进行访问。
1 | <?php |
PHP的mysqli扩展
启用mysqli扩展模块
1 | extension=php_mysqli.dll //在php.ini文件中启动这一行 |
mysqli扩展不仅提供了mysql模块的所有功能,也相应地增加了一些新特征。mysqli扩展模块包括mysqli、mysqli_result和mysqli_stmt三个类,通过这三个类的使用,就可以连接MySQL数据库服务器和选择数据库、查询和获取数据,以及使用预处理语句简化了重复执行的查询语句。
使用mysqli类
mysqli类中的成员方法
成员方法名 | 描述 |
---|---|
_construct() | 构造方法,用于创建一个新的mysqli对象,也可以建立一个连接 |
autocommit() | 开启或关闭数据库修改自动提交 |
change_user | 改变了数据库连接所指定的用户 |
caracter_set_name() | 返回数据库连接默认的字符集 |
close() | 关闭先前打开连接 |
commit() | 提交当前的事务 |
connect() | 打开一个新的连接到MySQL数据库服务器 |
debug() | 执行调试操作 |
dump_debug_info() | 转储调试信息 |
get_client_info() | 返回客户端版本 |
get_host_info() | 返回一个字符串代表的连接使用类型,如:Localhost via UNIX socket |
get_server_info() | 返回MySQL服务器的版本 |
get_server_version() | 返回整数形式的MySQL服务器版本 |
init() | 初始化MySQLi并返回一个资源 |
info() | 检索有关最近执行的查询 |
kill() | 要求服务器去杀死一个MySQL线程 |
multi_query() | 执行多个查询语句 |
more_results() | 从多查询语句中检查是否有任何更多的查询结果 |
Next_result() | 从当前执行的多查询中读取下一个结果 |
options() | 设置选项 |
ping() | 如果没有连接,ping一台服务器连接或重新连接 |
prepare() | 准备一个SQL语句的执行,返回mysqli_stmt对象 |
query() | 与数据库的任何交互都是通过查询进行的,该方法向数据库发送查询来执行 |
real_connect() | 试图打开一个连接到MySQL数据库服务器 |
escape_string() | 转义特殊字符的字符串,用于在一个SQL语句,并考虑到当前的字符集的连接 |
rollback() | 回滚当前的事务 |
select_db() | 为数据库查询选择默认的数据库 |
set_charset() | 设置默认客户端字符集 |
ssl_set() | 使用SSL用于建立安全连接 |
stat() | 获取当前的系统状态 |
stmt_init() | 初始化一个声明,并返回一个mysqli_stmt对象 |
store_result() | 从最后查询中转让结果集 |
thread_safe() | 是否考虑返回安全的线程 |
mysqli类中的成员属性
成员属性名 | 描述 |
---|---|
$affect_rows | 在前一个MySQL操作中获取影响的行数 |
$client_info | MySQL客户端版本为一个字符串返回 |
$client_version | MySQL客户端版本为一个整数返回 |
$errno | 返回最近函数调用的错误代码 |
$error | 返回最近函数调用的错误信息字符串 |
$field_count | 传回最近查询获取的列数 |
$host_info | 返回一个字符串的连接类型使用 |
$info | 检索有关最近执行的查询 |
$insert_id | 返回使用最后查询自动生成的编号 |
$protocal_version | 返回MySQL协议使用的版本 |
$sqlstate | 返回一个字符串包含SQLSTATE错误码的最后一个错 |
$thread_id | 为当前连接返回线程ID |
$warning_count | 返回前一个SQL语句执行过程中产生的警告数量 |
连接MySQL服务器
通过mysqli类的构造方法实例化对象,其构造方法的原型如下所示:1
2
3class mysqli{
__construct ([string host[,string username[,string passwd[,string dbname[,int port[,string socket]]]]]])
}
这个构造方法还有两个可选的参数:MySQL服务器的端口号、一个套接字文件或命名管道。但是这两个可选参数很少使用。
如果连接成功,该构造方法将返回一个mysqli对象。1
$mysqli=new mysqli("localhost","mysql_user","mysql_pwd","mylib"); //连接MySQL数据库服务器
如果在创建mysqli对象没有想构造方法传入连接参数,就需要多写几行代码,调用mysqli对象中的connect()方法连接MySQL数据库服务器,还可以使用select_db()方法特别指定数据库。1
2
3
4
5<?php
$mysqli=new mysqli(); //创建mysqli对象
$mysqli->connect("localhost","mysql_user","mysql_pwd"); //连接指定的MySQL数据库服务器
$mysqli->select_db("mylib"); //选择特定的数据库
?>
处理连接错误报告
在连接过程中难免会出现错误,应该及时让用户得到通知。在连接出错时mysqli对象并没有创建成功,所以不能调用mysqli对象中的成员获取这些错误信息,要通过mysqli扩展中的过程方式获取。使用mysqli_connect_errno()函数测试在建立连接的过程中是否发生错误,相关的出错信息由mysqli_connect_error()函数负责返回。1
2
3
4
5
6
7
8<?php
$mysqli=new mysqli("localhost","mysql_user","mysql_pwd","dbname");
/* 检查连接,如果连接出错输出错误信息并退出程序 */
if(mysqli_connect_errno()){
printf("连接失败:%s\n",mysqli_connect_error());
exit();
}
?>
关闭与MySQL服务器连接
完成数据库访问工作,如果不再需要连接到数据库,应该明确地释放有关的mysqli对象。随意脚本执行结束后,所有打开的数据库连接都将自动关闭,资源被回收。但是,在执行过程中,又肯页面需要多个数据库连接,各个连接要在适当的时候将其关闭。mysqli对象中的close()方法负责关闭打开数据库连接,成功时返回TRUE,否则返回FALSE。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21<?php
/* 连接MySQL数据库并,成功则返回mysqli 对象*/
$mysqli = new mysqli("localhost", "mysql_user", "mysql_pwd", "mylib");
/* 检查连接,如果连接出错输出错误信息并退出程序 */
if (mysqli_connect_errno()) {
printf("连接失败: %s<br>", mysqli_connect_error());
exit();
}
/* 打印当前数据库使用字符集字符串 */
printf ("当前数据库的字符集: %s<br>", $mysqli->character_set_name());
/* 打印客户端版本 */
printf("客户端库版本: %s<br>", $mysqli->get_client_info());
/* 打印服务器主机信息 */
printf("主机信息: %s<br>", $mysqli->host_info);
/* 打印字符串形式MySQL服务器版本 */
printf("服务器版本: %s<br>", $mysqli->server_info);
/*打印整数形式MySQL服务器版本*/
printf("服务器版本:%d<br>", $mysqli->server_version);
/* 关闭打开的数据库连接 */
$mysqli->close();
?>
执行SQL命令
1 | <?php |
使用mysqli_result类
成员方法名 | 描述 |
---|---|
close() | 释放内存并关闭结果集 |
data_seek() | 明确改变当前结果记录顺序 |
fetch_field() | 从结果集中获得某一个字段的信息 |
fetch_fields() | 从结果集中获得全部字段的信息 |
fetch_field_direct() | 从一个指定的列中获得类的详细信息,返回一个包含列信息的对象 |
fetch_array() | 将以一个普通索引数组和关联数组两种形式返回一条结果记录 |
fetch_assoc() | 将以一个普通关联数组的形式返回一条结果记录 |
fetch_object() | 将以一个对象的形式返回一条结果记录 |
fetch_seek() | 设置结果集中字段的偏移位置 |
成员属性名 | 描述 |
---|---|
$current_field | 获取当前结果中指向的字段偏移位置,是一个整数 |
$field_count | 从查询的结果中获取列的个数 |
$lengths | 返回一个数组,保存在结果集中获取当前行的每一列的长度 |
$num_rows | 返回结果集中包含记录的行数 |
从结果集中解析数据
$result->fetch_row()
该方法从结果集中获取一条结果记录,将值存放在一个索引数组中,与其他三个方法相比是最方便的方法。它的各个字段需要以$row[$n]的方式访问,其中$row是从结果集中获取的一行记录返回的数据,$n为连续的整数下标。因为返回的是索引数组,所以还可以和list()函数结合在一起使用。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18<?php
$mysqli = new mysqli("localhost", "root", "123456", "demo"); //连接本地demo数据库
if (mysqli_connect_errno()) {
printf("连接失败: %s<br>", mysqli_connect_error());
exit();
}
$mysqli->query("set names gb2312"); //设置字符集为国标2312码
/* 将部门编号为D01的联系人姓名和电子邮件全部取出存入到结果集中 */
$result = $mysqli->query("SELECT name, email FROM contactInfo WHERE departmentId='D01'");
echo 'D01部门的联系人姓名和电子邮件:';
echo '<ol>';
while(list($name, $email)=$result->fetch_row()){ //从结果集中遍历每条数据
echo '<li>'.$name.' : '.$email.'</li>'; //以列表形式输出每条记录
}
echo '</ol>';
$result->close(); //关闭结果集
$mysqli->close(); //关闭与数据库的连接
?>
$result->fetch_assoc()
该方法将以一个关联数组的形式返回一条结果记录,数据表的字段名表示键,字段内容表示值。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27<?php
$mysqli = new mysqli("localhost", "mysql_user", "mysql_pwd", "demo"); //连接MySQL数据库
if (mysqli_connect_errno()) { //检查连接错误
printf("连接失败: %s<br>", mysqli_connect_error());
exit();
}
$mysqli->query("set names gb2312"); //设置查询字符集
$result = $mysqli->query("SELECT * FROM contactInfo"); //执行查询语句获取结果集
echo '<table width="90%" border="1" align="center">'; //打印HTML表格
echo '<caption><h1>联系人信息表</h1></caption>'; //输出表名
echo '<th>用户ID</th><th>姓名</th><th>部门编号</th>'; //输出字段名
echo '<th>联系地址</th><th>联系电话</th><th>电子邮件</th>';
while($row=$result->fetch_assoc()){ //循环从结果集中遍历记录
echo '<tr align="center">'; //输出行标记
echo '<td>'.$row["uid"].'</td>'; //输出用户ID
echo '<td>'.$row["name"].'</td>'; //输出用户姓名
echo '<td>'.$row["departmentId"].'</td>'; //输出部门编号
echo '<td>'.$row["address"].'</td>'; //输出联系地址
echo '<td>'.$row["phone"].'</td>'; //输出联系电话
echo '<td>'.$row["email"].'</td>'; //输出电子邮件
echo '</tr>';
}
echo '</table>';
$result->close(); //关闭结果集释放内存
$mysqli->close(); //关闭与数据库服务器的连接
?>
$result->fetch_array()
该方法可以说是fetch_row()和fetch_assoc()两个方法的结合版本,可以将结果集的各条记录获取为一个关联数组或数值索引数组,或者同时获取为关联数组和索引数组。默认情况下,会同时获取这两种数组。可以通过在该方法的参数在该方法的参数中传入如下不同的值来修改这种默认行为。
- MYSQLI_ASSOC:记录被作为关联数组返回,字段名为键,字段内容为值。
- MYSQLI_NUM:记录被作为索引数组返回,按查询中指定的字段名顺序排序。
- MYSQLI_BOTH:这是默认值,记录即未做关联数组又作为索引数组返回。因此,每个字段可以根据其索引偏移引用,也可以根据字段名来引用。
$result->fetch_object()
该方法与前面三个方法不同,它将以一个对象的形式返回一条结果记录,而不是数组。它的各个字段需要以对象的方式进行访问,数据列的名字区分字母大小写情况。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27<?php
$mysqli = new mysqli("localhost", "mysql_user", "mysql_pwd", "demo"); //连接MySQL数据库
if (mysqli_connect_errno()) { //检查连接错误
printf("连接失败: %s<br>", mysqli_connect_error());
exit();
}
$mysqli->query("set names gb2312"); //设置查询字符集
$result = $mysqli->query("SELECT * FROM contactInfo"); //执行查询语句获取结果集
echo '<table width="90%" border="1" align="center">'; //打印HTML表格
echo '<caption><h1>联系人信息表</h1></caption>'; //输出表名
echo '<th>用户ID</th><th>姓名</th><th>部门编号</th>'; //输出字段名
echo '<th>联系地址</th><th>联系电话</th><th>电子邮件</th>';
while($rowObj=$result->fetch_object()){ //循环从结果集中遍历记录
echo '<tr align="center">'; //输出行标记
echo '<td>'.$rowObj->uid.'</td>'; //输出用户ID
echo '<td>'.$rowObj->name.'</td>'; //输出用户姓名
echo '<td>'.$rowObj->departmentId.'</td>'; //输出部门编号
echo '<td>'.$rowObj->address.'</td>'; //输出联系地址
echo '<td>'.$rowObj->phone.'</td>'; //输出联系电话
echo '<td>'.$rowObj->email.'</td>'; //输出电子邮件
echo '</tr>';
}
echo '</table>';
$result->close(); //关闭结果集释放内存
$mysqli->close(); //关闭与数据库服务器的连接
?>
从结果集中获取数据列的信息
在解析结果集时,不仅需要从中遍历数据,有时也需要获取数据表的属性和各个字段的信息。可以通过结果集对象中的field_count属性给出结果数据表里的数据列的个数、使用current_field属性获取指向当前列的位置、使用field_seek()方法改变指向当前列的偏移位置,以及通过fetch_field()方法返回的对象中获取当前列的信息。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23<?php
$mysqli = new mysqli("localhost", "root", "123456", "demo"); //连接MySQL数据库
if (mysqli_connect_errno()) { //检查连接错误
printf("连接失败: %s<br>", mysqli_connect_error());
exit();
}
$mysqli->query("set names gb2312"); //设置查询字符集
$result = $mysqli->query("SELECT * FROM contactInfo"); //执行查询语句获取结果集
echo "结果数据表里数据列个数为:".$result->field_count."列<br>"; //从查询结果中获取列数
echo "默认当前列的指针位置为第:".$result->current_field."列<br>"; //打印默认列的指针位置
echo "将指向当前列的指针移动到第二列;<br>";
$result->field_seek(1); //将当前列指针移至第二列(默认0代表第一列)
echo "指向当前列的指针位置为第:".$result->current_field."列<br>"; //打印当前列的指针位置
echo "第二列的信息如下所示:<br>";
$finfo = $result->fetch_field(); //获取当前列的对象
echo "列的名称:".$finfo->name."<br>"; //打印列的名称
echo "数据列来自数据表:".$finfo->table."<br>"; //打印本列来自哪个数据表
echo "本列最长字符串的长度".$finfo->max_length."<br>"; //打印本列中最长字符串长度
$result->close(); //关闭结果集释放内存
$mysqli->close(); //关闭与数据库服务器的连接
?>
数据库抽象层PDO
PDO所支持的数据库
PDO就是一个“数据库访问抽象层”,作用是统一各种数据库的访问接口,能够轻松地在不同数据库之间进行切换,使得数据库间的移植容易实现。
驱动名 | 对应访问的数据库 |
---|---|
PDO_DBLIB | FreeTDS/Microsoft SQL Server/Sybase |
PDO_FIREBIED | Firebird/Interbase 6 |
PDO_MYSQL | MySQL 3.x/4.x/5.x |
PDO_OCI | Oracle |
PDO_ODBC | ODBC v3 |
PDO_PGSQL | PostgreSQL |
PDO_SQLITE | SQLite 2.x/3.x |
PDO的安装
在Linux环境下为启用对MySQL的PDO驱动程序支持,需要在安装PHP5.1以上版本的源代码包环境时,向configure命令添加如下标志:1
--with-pdo-mysql=/usr/local/mysql //其中“/usr/local/mysql”为MySQL服务器安装目录
在Windows环境下PHP5.1以上版本中,PDO和主要数据库的驱动同PHP一样作为扩展发布,要激活它们只需要简单地编辑php.ini文件。下面都是用分号在前面注释掉了支持的扩展模块,我们在后面追加下面的一行代码:1
extension=php_pdo.dll //所以PDO驱动程序共享的扩展,必须有
上面一行是所以PDO驱动程序共享必须要有的扩展。然后,就看使用什么数据库,如果使用MySQL,那么添加下面的一行:1
extension=php_pdo_mysql.dll //如果使用MySQL,那么添加这一行
如果要激活其他一种数据库的PDO驱动程序,添加下面其中的一行,如果要激活多个数据库的PDO驱动程序,添加下面的多行:1
2
3extension=php_pdo_mssql.dll //如果要使用SQL Server,那么添加这一行
extension=php_pdo_odbc.dll //如果要使用ODBC驱动程序,那么添加这一行
extension=php_pdo_oci.dll //如果要使用Oracle驱动程序,那么添加这一行
保存修改的php.ini文件变化,重启Apache服务器,查看phpinfo()函数PDO是否开启。
创建PDO对象
使用PDO在与不同数据库管理系统之间交互时,PDO对象中的成员方法是统一各种数据库的访问接口,所以在使用PDO与数据库交互之间,首先要创建一个PDO对象。在通过构造方法创建对象的同时,需要建立一个与数据库服务器的连接,并选择一个数据库。PDO的构造方法原型如下:1
__construct(string dsn[,string username[,string password[,array driver_options]]]) //PDO的构造方法
在构造方法中,第一个必选的参数是数据源名(DSN),用来定义一个确定的数据库和必须用到的驱动程序。DSN的PDO命名惯例为PDO驱动程序的名称,后面一个冒号,再后面是可选的驱动程序的数据库连接变量信息,如主机名、端口和数据库名。1
2oci:dbname=//localhost:1521/mydb //连接Oracle服务器的数据源名(DSN)
mysql:host=localhost;dbname=testdb //连接MySQL服务器的数据源名(DSN)
构造方法中的第二参数username和第三个参数password分别指定用于连接数据库的用户名和密码,是可选参数。最后一个参数driver_options需要一个数组,用来指定连接所需的所有额外选项,传递附加的调优参数到PDO或底层驱动程序。
以多种方法调用构造方法
将参数嵌入到构造函数
在DSN字符串中加载OCI驱动程序并指定了两个可选参数:第一个是数据库名称,第二个是字符集。使用了特定的字符集连接一个特定的数据库,如果不指定任何信息就会使用默认的数据库。1
2
3
4
5
6
7<?php
try{
$dbh = new PDO("OCI:dbname=accounts;charset=UTF-8","scott","tiger");
}catch(PDOException $e){
echo "数据库连接失败: ".$e->getMessage();
}
?>
OCI:dbname=accounts告诉PDO它应该使用OCI驱动程序,并且应该使用“accounts”数据库。对于MySQL驱动程序,第一个冒号后面的所有内容都将用做MySQL的DSN。1
2
3
4
5
6
7
8
9
10<?php
$dsn='mysql:dbname=testdb;host=127.0.0.1';
$user='dbuser';
$password='dbpass';
try{
$dbh = new PDO($dsn,$user,$password);
}catch(PDOException $e){
echo '数据库连接失败: '.$e->getMessage();
}
?>
将参数存放在文件中
在创建PDO对象时,可以把DSN字符串放在另一个本地或远程文件中,并在构造函数中引用这个文件。1
2
3
4
5
6
7
8<?php
try{
$dbh = new PDO('uri:file://usr/local/dbconnect','webuser','password');
}catch(PDOException $e){
ehco'连接失败: '.$e->getMessage();
}
?>
只要将文件/usr/local/dbconnect中的DSN驱动改变,就可以在多种数据库系统之间切换。但要确保该文件由负责执行PHP脚本的用户拥有,而且此用户拥有必要的权限。
引用php.ini
也可以在PHP服务器的配置文件中维护DSN信息,只要在php.ini文件中把DSN信息赋给一个名为pdo.dsn.aliasname的配置参数,这里aliasname是后面将提供给构造函数的DSN别名。1
2[PDO]
pdo.dsn.orcalepdo="OCI:dbname=//localhost:1521/mydb;charset=UTF-8";
重启启动Apache服务器后,就可以在PHP程序中,调用PDO构造方法时,在第一个参数中使用这个别名。1
2
3
4
5
6
7<?php
try{
$dbh=new PDO("orcalepdo","scott","tiger"); //使用php.ini文件中的oraclepdo别名
}catch(PDOException $e){
echo "数据库连接失败: ".$->getMessage();
}
?>
PDO与连接有关选项
在创建PDO对象时,有一些与数据库连接有关的选项,可以将必要的几个选项组成数组传递给构造方法的第四个参数driver_opts中,用来传递附加的调优参数到PDO或底层驱动程序。
选项名 | 描述 |
---|---|
PDO::ATTR_AUTOCOMMIT | 确定PDO是否关闭自动提交功能,设置FALSE值时关闭 |
PDO::ATTR_CASE | 强制PDO获取的表字符的大小写转换,或原样使用列信息 |
PDO::ATTR_ERRMODE | 设置错误处理的模式 |
PDO::ATTR_PERSISTENT | 确定连接是否为持久连接,默认值为FALSE |
PDO::ATTR_ORACLE_NULLS | 将返回的空字符串转换为SQL的NULL |
PDO::ATTR_PREFETCH | 设置应用程序提前获取的数据大小,以K字节为单位 |
PDO::ATTR_TIMEOUT | 设置超时之前等待的时间 |
PDO::ATTR_SERVER_INFO | 包含与数据库特有的服务器信息 |
PDO::ATTR_SERVER_VERSION | 包含与数据库服务器版本号有关的信息 |
PDO::ATTR_CLIENT_VERSION | 包含与数据库客户端版本号有关的信息 |
PDO::ATTR_CONNECTION_STATUS | 包含与数据库特有的与连接状态有关的信息 |
PDO对象中的成员方法
成员方法名 | 描述 |
---|---|
getAttribute() | 获取一个“数据库连接对象”的属性 |
setAttribute() | 为一个“数据库连接对象”设定属性 |
errorCode() | 获取错误码 |
errorInfo() | 获取错误的信息 |
exec() | 处理一条SQL语句,并返回所影响的条目数 |
query() | 处理一条SQL语句,并返回一个“PDOStatement”对象 |
lastInsertId() | 获取插入到表中的最后一条数据的主键值 |
prepare() | 负责准备要执行的SQL语句 |
getAvailableDrivers() | 获取有效的PDO驱动器名称 |
beginTransaction() | 开始一个事务,表明回滚起始点 |
commit() | 提交一个事务,并执行SQL |
rollback() | 回滚一个事务 |
使用PDO对象
调整PDO的行为属性
getAttribute()
该方法只需要提供一个参数,传递一个特定属性名称,执行成功返回该属性所指定的值,否则返回NULL。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18<?php
$opt=array(PDO::ATTR_PERSISTENT => TRUE);
try {
$dbh = new PDO('mysql:dbname=testdb;host=localhost', 'mysql_user', 'mysql_pwd', $opt);
} catch (PDOException $e) {
echo '数据库连接失败:'.$e->getMessage();
exit;
}
echo "\nPDO是否关闭自动提交功能:".$dbh->getAttribute(PDO::ATTR_AUTOCOMMIT);
echo "\n当前PDO的错误处理的模式:".$dbh->getAttribute(PDO::ATTR_ERRMODE);
echo "\n表字段字符的大小写转换: ".$dbh->getAttribute(PDO::ATTR_CASE);
echo "\n与连接状态相关特有信息: ".$dbh->getAttribute(PDO::ATTR_CONNECTION_STATUS);
echo "\n空字符串转换为SQL的null:".$dbh->getAttribute(PDO::ATTR_ORACLE_NULLS);
echo "\n应用程序提前获取数据大小:".$dbh->getAttribute(PDO::ATTR_PERSISTENT);
echo "\n与数据库特有的服务器信息:".$dbh->getAttribute(PDO::ATTR_SERVER_INFO);
echo "\n数据库服务器版本号信息:".$dbh->getAttribute(PDO::ATTR_SERVER_VERSION);
echo "\n数据库客户端版本号信息:".$dbh->getAttribute(PDO::ATTR_CLIENT_VERSION);
?>
setAttribute()
这个方法需要两个参数,第一个参数提供PDO对象特定的属性名,第二个参数则是为这个指定的属性赋一个值。1
$dbh->setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_EXCEPTION); //设置抛出异常处理错误
使用PDO执行SQL语句
使用PDO::exec()方法
当执行INSERT、UPDATE和DELETE等没有结果集的查询时,使用PDO对象中的exec()方法去执行。该方法成功执行后,将返回受影响的行数。该方法不能用于SELECT查询。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15<?php
try{
$dbh=new PDO('mysql:dbname=testdb;host=localhost','mysql_user','mysql_pwd');
}catch(PDOException $e){
echo '数据库连接失败:'.$e->getMessage();
exit;
}
$query="UPDATE contactInfo SET phone='15801680168' where name='高某某'";
$affected=$dbh->exec($query);
if($affected){
echo '数据表contactInfo中受影响的行数为:'.$affected;
}else{
print_r($dbh->errorInfo());
}
?>
使用PDO::query()方法
当执行返回结果集的SELECT查询时,或者所影响的行数无关紧要时,应当使用PDO对象中的query()方法。如果该方法成功执行指定的查询,返回一个PDOStatement对象。如果使用了query()方法,并想了解影响的行总数,可以使用PDO对象中的rowCount()方法获取。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22<?php
try{
$dbh=new PDO('mysql:dbname=testdb;host=localhost','mysql_user','mysql_pwd');
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}catch(PDOException $e){
echo '数据库连接失败:'.$e->getMessage();
exit;
}
$query="SELECT name, phone, email FROM contactInfo WHERE departmentId='D01'";
try {
$pdostatement=$dbh->query($query);
echo "一共从表中获取到".$pdostatement->rowCount()."条记录:\n";
foreach ($pdostatement as $row) {
echo $row['name'] . "\t";
echo $row['phone'] . "\t";
echo $row['email'] . "\n";
}
} catch (PDOException $e) {
echo $e->getMessage();
print_r($dbh->errorInfo());
}
?>
PDO对预处理语句的支持
了解PDOStatement对象
方法名 | 描述 |
---|---|
bindColumn() | 用来匹配列名和一个指定的变量名,这样每次获取各行记录时,会自动将相应的列值赋给该变量 |
bindParam() | 将参数绑定到相应的查询占位符上 |
bindValue() | 将一值绑定到对应的一个参数中 |
closeCursor() | 关闭游标,使该声明再次被执行 |
columnCount() | 在结果集中返回列的数目 |
errorCode() | 获取错误码 |
errorInfo() | 获取错误的信息 |
execute() | 负责执行一个准备好的预处理查询 |
fetch() | 返回结果集的下一行,当到达结果集末尾时返回FALSE |
fetchAll() | 通过一次调用就可以获取结果集中的所有行,并赋给返回的数组 |
fetchColumn() | 返回结果集中下一行某个列的值 |
fetchObject() | 获取下一行记录并返回它作为一个对象 |
getAttribute() | 获取一个声明属性 |
getColumnMeta() | 在结果集中返回某一列的属性信息 |
nextRowset() | 检索下一行集(结果集) |
rowCount() | 返回使用query()方法执行的SELECT语句后受影响的行总数 |
setAttribute() | 为一个预处理语句设置属性 |
setFetchMode() | 设置需要结果集合的类型 |
准备语句
PDO中有两种使用占位符的语法:“命名参数”和“问号参数”。
使用命名参数作为占位符的INSERT查询
1
$dbh->prepare("INSERT INTO contactInfo(name,address,phone) VALUES(:name,:address,:phone)");
使用问号(?)参数作为占位符的INSERT查询
1
$dbh->prepare("INSERT INTO contactInfo(name,address,phone) VALUES(?,?,?)");
绑定参数
当查询准备好之后,需要在每次执行时替换输入的参数。可以通过PDOStatement对象中的bindParam()方法,把参数绑定到准备好的查询中相应的占位符。方法bindParame()的原型如下所示:1
bindParam(mixed parameter,mixed&variable[,int data_type[,int length[,mixed driver_options]]])
第一个参数parameter是必选项,如果在准备好的查询中,占位符语法使用名字参数时,将名字参数字符串作为bindParam()方法的第一个参数提供。如果占位符语法使用问号参数时,将准备好的查询中列值占位符的索引偏移量,作为该方法的第一个参数提供。
第二个参数variable也是必选项,提供赋给第一个参数所指定占位符的值。它需要按引用传递,在结合准备存储过程使用此方时,可以根据存储过程的某个动作修改这个值。因为该参数是按引用传递,所以只能提供变量作为参数,不能直接提供数值。
第三个参数data_type是可选项,显示地为当前被绑定的参数设置数据类型。
- PDO::PARAM_BOOL:代表boolean数据类型
- PDO::PARAM_NULL:代表SQL中NULL类型
- PDO::PARAM_INT:代表SQL中INTEGER数据类型
- PDO::PARAM_STR:代表SQL中CHAR、VARCHAR和其他字符串数据类型
- PDO::PARAM_LOB:代表SQL中大对象数据类型
- PDO::PARAM_STMT:代表PDOStatement对象类型
- PDO::PARAM_INPUT_OUTPUT:专为存储过程使用的数据类型,可以在过程执行后修改
第四个参数length是可选项,用于指定数据类型的长度,当在第三个参数中使用PDO_PARAM_INPUT_OUTPUT数据类型时必须使用这个参数。
第五个参数driver_options是可选项,通过该参数提供任何数据库驱动程序特定的选项。
执行准备好的查询
当准备好查询并绑定了相应的参数,就可以通过调用PDOStatement类对象中的execute()方法反复执行了。
execute()方法中提供了一个可选参数,该参数由准备查询中的命名参数占位符组成的数组,这是第二种为预处理查询在执行中替换输入参数的方式。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15<?php
try{
$dbh=new PDO('mysql:dbname=testdb;host=localhost','mysql_user','mysql_pwd');
}catch(PDOException $e){
echo '数据库连接失败:'.$e->getMessage();
exit;
}
$query="INSERT INTO contactInfo (name, address, phone) VALUES (:name, :address, :phone)";
$stmt=$dbh->prepare($query);
$stmt->execute(array(":name"=>"赵某某",":address"=>"海淀区", ":phone"=>"15801688348"));
$stmt->execute(array(":name"=>"孙某某",":address"=>"宣武区", ":phone"=>"15801688698"));
?>
获取数据
fetch()方法
DOStatement类中的fetch()方法可以将结果集中当前行的记录以某种方法返回,并将结果集指针移至下一行,当到达结果集末尾时返回FALSE。该方法的原型如下:1
fetch([int fetch_style[,int cursor_orientation[,int cursor_offset]]]) //返回结果集的下一行
第一个参数fetch_style是必选项,获取的一行数据记录中,各列的引用方式取决于这个参数如何设置。可以使用的设置以下六种。
- PDO::FETCH_ASSOC:从结果集中获取按列名为索引的关联数组
- PDO::FETCH_NUM:从结果集中获取一个按列在航中的数值偏移为索引的值数组
- PDO::FETCH_BOTH:这是默认值,包含上面两种数组
- PDO::FETCH_OBJ:从结果集当前行的记录中获取其属性对应各个列名的一个对象
- PDO::FETCH_BOUND:使用fetch()返回TRUE,并将获取的列值赋给通过bindParam()方法中指定的相应变量
- PDO::FETCH_LAZY:创建关联数组和索引数组,以及包含列属性的一个对象,从而可以在这三种接口中任选一种
第二个参数curosr_orientation是可选项,用来确定当对象是一个可滚动的游标时应当获取哪一行。
第三个参数cursor_offset也是可选项,需要提供一个整数值,表示要获取的行相对于当前游标位置的偏移。
1 | <?php |
fetchAll()方法
fetchAll()方法与上一个方法fetch()类似,但是该方法只需要调用一次就可以获取结果集中的所有行,并赋给返回的数组。该方法的原型如下:1
fetchAll([int fetch_style[,int column_index]]) //一次调用返回结果集中所有行
第一个参数fetch_style是必选项,以何种方式引用所获取的列取决于该参数。默认值为PDO::FETCH_BOTH,还可以指定PDO::FETCH_COLUMN值,从结果集中返回一个包含单列的所有值。
第二个参数column_index是可选项,需要提供一个整数索引,当在fetchAll()方法的第一个参数中指定PDO::FETCH_COLUMN值时,从结果集中返回通过该参数提供的索引所指定列的所有值。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29<?php
try{
$dbh=new PDO('mysql:dbname=testdb;host=localhost','mysql_user','mysql_pwd');
}catch(PDOException $e){
echo '数据库连接失败:'.$e->getMessage();
exit;
}
echo '<table border="1" align="center" width=90%>';
echo '<caption><h1>联系人信息表</h1></caption>';
echo '<tr bgcolor="#cccccc">';
echo '<th>UID</th><th>姓名</th><th>联系地址</th><th>联系电话</th><th>电子邮件</th></tr>'; $stmt=$dbh->prepare("select uid,name,address,phone,email from contactInfo");
$stmt->execute();
$allRows=$stmt->fetchAll(PDO::FETCH_NUM);
foreach($allRows as $row){
echo '<tr>';
echo '<td>'.$row[0].'</td>';
echo '<td>'.$row[1].'</td>';
echo '<td>'.$row[2].'</td>';
echo '<td>'.$row[3].'</td>';
echo '<td>'.$row[4].'</td>';
echo '</tr>';
}
echo '</table>';
$stmt->execute();
$row=$stmt->fetchAll(PDO::FETCH_COLUMN, 1);
echo '所有联系人的姓名:';
print_r($row);
?>
bindColumn()方法
使用该方法可以将一个列和一个指定的变量名绑定,这样在每次使用fetch()方法获取各行记录时,会自动将相应的列值赋给该变量,但必须是在fetch()方法第一个参数设置为PDO::FETCH_BOTH值时。bindColumn()方法的原型如下所示:1
bindColumn(mixed coumn,mixed¶m[,int type]) //设置绑定列值到变量上
第一个参数column为必选项,可以使用整数的列偏移位置索引(索引值从1开始),或是列的名称字符串。第二个参数param也是必选项,需要传递一个引用,所以必须提供一个相应的变量名。第三个参数type是可选项,通过设置变量的类型来限制变量值。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25<?php
try{
$dbh=new PDO('mysql:dbname=testdb;host=localhost','mysql_user','mysql_pwd');
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}catch(PDOException $e){
echo '数据库连接失败:'.$e->getMessage();
exit;
}
$query="SELECT uid, name, phone, email FROM contactInfo WHERE departmentId='D01'";
try {
$stmt=$dbh->prepare($query);
$stmt->execute();
$stmt->bindColumn(1, $uid);
$stmt->bindColumn(2, $name);
$stmt->bindColumn('phone', $phone);
$stmt->bindColumn('email', $email);
while ($row = $stmt->fetch(PDO::FETCH_BOUND)) {
echo $uid."\t".$name."\t".$phone."\t".$email."\n";
}
} catch (PDOException $e) {
echo $e->getMessage();
}
?>
大数据对象的存取
PDO允许在bindParam()或bindColumn()调用中通过使用PDO::PARAM_LOB类型代码来使用大型数据库类型。PDO::PARAM_LOB告诉PDO将数据映射为流,所以可以使用PHP中文件处理函数来操纵这样的数据。1
2
3
4
5
6
7
8<?php
$dbh=new PDO('mysql:dbname=testdb;host=localhost','mysql_user','mysql_pwd'); //连接数据库
$stmt=$dbh->prepare("inset into images(contenttype,imagedata)values(?,?)"); //准备插入查询
$fp=fopen($_FILES['file']['tmp_name'],'rb'); //使用fopen()函数打开上传的文件
$stmt->bindParam(1,$_FILES['flie']['type']); //将上传的MIME类型绑定到第一个参数中
$stmt->bindParam(2,$fp,PDO_PARAM_LOB); //将上传文件的二进制数据和第二个参数绑定
$stmt->execute(); //执行准备好的并绑定了参数的查询
?>
会话控制
Cookie是用来将使用者资料记录在客户端的技术,这种技术让Web服务器能将一些只需存放于客户端,或者可以在客户端进行运算的资料,存放于用户的电脑系统之中。
使用PHP内建的setCookie()函数来新建立一个Cookie。Cookie是HTTP标头的一部分,因此setCookie()函数必须在其他信息被输出到浏览器前调用,所以即使是空格或空行都不要在调用setCookie()函数之前输出。setCookie()函数的语法格式如下所示:1
bool setcookie(string $name[,string $value[,int $expire[,string $path[,string $domain[,bool $secure]]]]])
setcookie()函数定义一个和其余的HTTP标头一起发送的Cookie,它的所有参数是对应HTTP标头Cookie资料的属性。
参数 | 描述 | 示例 |
---|---|---|
$name | Cookie的识别名称 | 使用$_COOKIE[‘cookiename’]调用名为cookiename的Cookie |
$value | Cookie的值,可以为数值或字符串形态,此值保存在客户端,不要用来保存敏感数据 | 假定第一个参数为‘cookiename’,可以通过$_COOKIE[‘cookiename’]取得其值 |
$expire | Cookie的生成期限,这是个Unix时间戳,即从Unix纪元开始的秒数 | 如time()+606024*7将设定Cookie在一周后失效,如果未设定cookie,则会在会话结束后就立即失效 |
$panth | Cookie在服务器端的指定路径,当设定此值时,服务器中只有指定路径下的网页或程序可以存取此Cookie | 如果该参数设为“/”的话,cookie就在整个domain内有效,如果设为‘/foo’,cookie就只在domain下的/foo/目录及其子目录内有效,默认值为设定cookie的当前目录 |
$domain | 指定此Cookie所属服务器的网址名称,预设是建立此Cookie服务器的网址 | 要使cookie能在如example.com域名下所有子域名都有效的话,该参数应该设为“.example.com”。虽然“.”并不是必须的,但加上它会兼容更多浏览。如果该参数设置为www.example.com的话,就只在www子域内有效 |
$secure | 指明Cookie是否仅通过安全的HTTPS连接传送中Cookie的安全识别常数,如果设定此值代表只有在某种情况下,才能在客户端与服务器之间传递 | 当设成TRUE时,Cookie仅在安全的连接中被设置。默认值为FALSE |
如果只有name这一个参数,则原有此名称的Cookie选项将会被删除,可以使用空字符串(“ ”)来略过此参数。参数expire和secure是个整数,可以使用0来略过参数,而不是使用空字符串。但参数expire是一个正规的Unix时间整数,由time()或mktime()函数传回。参数secure指出此Cookie将只有在安全的HTTPS连接时传送。在实际建立Cookie是通常仅使用前三项参数。1
2
3<?php
setcookie("username","test",time()+60*60*24*7); //向客户端发送一个Cookie
?>
全部参数1
2
3<?php
setcookie("username","test",time()+60*60*24*7,"/test",".example.com",1); //使用全部参数设置
?>
在设置Cookie的脚本中,第一次读取它的信息并不会生效,必须刷新或到下一个页面才可以看到Cookie值,因为Cookie要先被设置到客户端,再次访问时才能被发送回来,这时才能被获取。所以要测试一个Cookie是否被成功的设定,可以在其到期之前通过另一个页面来访问其值。可以简单地使用print_r(_COOKIE)指令来调试现有的Cookies。1
2
3<?php
print_r($_COOKIE); //输出Cookie中保存的所有用户信息
?>
Cookie也可以利用多维数组的形式,将多个内容值存储在相同Cookie名称标识符下。但不能直接使用setCookie()函数,将数组变量插入到第二个参数作为Cookie的值,因为setCookie()函数的第二个参数必须传一个字符串的值。如果需要将数组变量设置到Cookie中,可以在setCookie()函数的第二个参数中,通过在Cookie标识名称中指定数组下标的形式设置。1
2
3
4
5<?php
setcookie("user[username]","username"); //设置为$_COOKIE["user"]["username"]
setcookie("user[password]","md5("123456")"); //设置为$_COOKIE["user"]["password"]
setcookie("user[email]","test@example.com"); //设置为$_COOKIE["user"]["email"]
?>
用forecho()函数遍历Cookie1
2
3
4
5<?php
forecho($_COOKIE["user"]as $key=>$value){
echo $key.":".$value."\n";
}
?>
删除保存在客户端的Cookie,可以使用两种方法。第一种方法,省略setcookie()函数所有参数列,仅导入第一个参数cookie识别名称参数,来删除指定名称的Cookie资料;第二种方式,利用setcookie()函数,把目标Cookie设定为“已过期”状态。1
2
3
4
5
6<?php
//只指定Cookie识别名称一个参数,即删除客户端中这个指定名称的Cookie资料
setCookie("Account"); //第一种方法
//设置Cookie在当前时间之前已经过期,因此系统会自动删除识别名称isLogin的Cookie
setCookie("isLogin","",time()-1); //第二种方法
?>
Session的应用
Session
Session技术是将使用者相关的资料存放在服务器的系统之下,所以使用者无法停止Session的使用。
在客户端仅需要保存由服务器为用户创建的一个Session标识符,称为Session ID;而在服务器端保存Session变量的值。Session ID是一个既不会重复,又不容易被找到规律的,由32位十六进制数组成的字符串。Session ID会保存在客户端的Cookie里,如果用户阻止Cookie的使用,则可以将Session ID保存在用户浏览器地址栏的URL中。当用户请求Web服务器时,就会把Session ID发送给服务器,再通过Session ID提取保存在服务器中的Seesion变量。可以把Session中保存的变量,当做是这个用户的全局变量,同一个用户对每个脚本的访问都共享这些变量。
当某个用户向Web服务器发出请求时,服务器首先会检查这个客户端的请求里是否已经包含了一个Session ID。如果包含,说明之前已经为此用户创建过Session,服务器则按该Session ID把Session检索出来使用。如果客户端请求不包含Session ID,则为该用户创建一个Session,并且生成一个与此Session关联的Session ID,在本次响应中被传送给客户端保存。
Session是存放与服务器之中,为了避免对服务器系统造成过大的负荷,因此Session并不像Cookie是一种半永久性的存在。Session会因为下面两种状况而自然消失。
- 第一个状况,当使用者关闭浏览器,失去与服务器之间的连接之后,Session即会自动消失。而当使用者下次登入网址时,再另行配置一个Session使用。
- 第二种状况,Session指定的有效期限到期。一般而言PHP系统中对于Session的生存时间并无定义,也就是说预设值为零。可以通过修改php.ini配置文件中有关“session.cookie.lift_time”项目,来设定Session的有效期限,以秒为单位指定了发送给浏览器的Cookie的生命周期。值为0表示“直到关闭浏览器”,默认为0.当系统赋予Session有效期限后,不管浏览器是否开启,Session都会自动消失。
配置Session
选项名 | 描述 | 默认值 |
---|---|---|
session.auto_start | 自动启动会话,0标识禁用,1表示开启 | 0 |
session.cache_expire | 为缓存中的会话页设置当前时间,单位分钟 | 180 |
session.cookie_domain | 指定会话Cookie中的域 | none |
session.cookie_lifetime | Cookie中的Session ID在客户机上保存的时间,0标识延续到浏览器关闭时 | 0 |
session.cookie_path | 在会话Cookie中要设置的路径 | / |
seesion.name | 会话的名称,在客户端用做Cookie的标识名称 | PHPSESSID |
session.save_path | 会话在服务器存储的路径 | /tmp |
seesion.use_cookies | 配置在客户端使用Cookie的会话,1表示允许 | 1 |
Session的声明与使用
Session的设置不同于Cookie,必须先启动,在PHP中必须调用session_start()函数,以便让PHP核心程序,将和Session相关的内建环境变量预先载入至内存中。seesion_star()函数的语法格式如下所示:1
bool session_start(void) //创建Session,开始一个会话,进行Session初始化
函数Session_start()有两个作用,一是开始一个会话,二是返回已经存在的会话。这个函数没有参数,且返回值均为TRUE。
注册一个会话变量和读取Session
注册和读取Session变量,都要通过访问$_SESSION数组完成。在$_SESSION关联数组中农工的键名具有和PHP中普通变量名相同的命名规则。1
2
3
4
5<?php
session_start(); //启动Session的初始化
$_SESSION["username"]="admin"; //注册Session变量,赋值为一个用户的名称
$_SESSION["password"]="123456"; //注册Session变量,赋值为一个用户的密码
?>
Seesion变量的文件的内容结构:
变量名|类型:长度:值; //每个变量都使用相同的结构保存
注销变量与销毁Session
销毁和当前Session有关的所以资料,可以调用session_destroy()函数结束当前的会话,并清空会话中的所有资源。该函数的语法格式如下所示:1
bool session_destroy(void) //销毁和当前Session有关的所有资料
该函数用来关闭Session的运作,如果成功则传回TRUE,销毁Session资料失败则返回false.但该函数并不会释放和当前的Session相关的变量,也不会删除保存在客户端Cookie中的Session ID.因为$_SESSION数组和自定义的数组在使用上是相同的,所以我们可以使用unset()函数释放在Session中注册的单个变量。1
2unset($_SESSION["username"]); //删除在Sesssion中注册的用户名变量
unset($_SESSION["password"]); //删除在Session中注册的用户密码变量
不要使用unset($_SESSION)删除整个$_SESSION数组,这样将不能再通过$_SESSION超全局数组注册变量了。但如果想把某个用户在Session中注册的所有变量都删除,可以直接将数组变量$_SESSION赋上一个空数组。1
$_SESSION=array(); //将某个用户在Session中注册的变量全部清除
在PHP脚本中,可以通过调用session_name()函数获取Session名称。删除保存在客户端Cookie中的Session ID.1
2
3
4
5<?php
if(isset($_COOKIE[session_name()])){ //判断Cookie中是否保存Session ID
setcookie(session_name(),"",time()-3600,"/"); //删除包含Session ID的Cookie
}
?>
Session的注销过程共需要四个步骤。1
2
3
4
5
6
7
8
9
10
11
12<?php
//第一步:开启Session并初使化
session_start();
//第二步:删除所有Session的变量,也可用unset($_SESSION[xxx])逐个删除
$_SESSION = array();
//第三步:如果使用基于Cookie的Session,使用setCooike()删除包含Session Id的Cookie
if (isset($_COOKIE[session_name()])) {
setcookie(session_name(), '', time()-42000, '/');
}
//第四步:最后彻底销毁Session
session_destroy();
?>
传递Session ID
使用Session跟踪一个用户,是通过在各个方面之间传递唯一的Session ID,并通过Session ID提取这个用户在服务器中保存的Session。常见的Session ID传送方法有以下两种。
- 第一种方法是基于Cookie的方式传递Session ID,这种方法更优化,但由于不总是可用,因为用户在客户端可以屏蔽Cookie。
- 第二种方法则是通过URL参数进行传递,直接将会话ID嵌入到URL中。
如果客户端没有禁用Cookie,则在PHP脚本中通过session_start()函数进行初始化后,服务器会自动发送HTTP标头将Session ID保存到客户端电脑的Cookie中。1
setCookie(session_name(),session_id(),0,'/') //虚拟向Cookie中设置Session ID的过程
- 在第一个参数中调用session_name()函数,返回当前Session的名称做为Cookie的标识名称。Session名称的默认值为PHPSESSID,是在php.ini文件中由session.name选项指定的值。也可以调用session_name()函数时提供参数改变当前Session名称。
- 在第二个参数中调用session_id()函数,返回当前Session ID作为Cookie的值。也可以通过调用session_id()函数时提供参数设定当前Session ID
- 第三个参数的值0,是通过在php.ini文件中由session.cookie_lifetime选项设置的值。默认值为0,表示Session ID将在客户机的Cookie中延续到浏览器关闭
- 最后一个参数“/”,表示在Cookie中要设置的路径在整个域内都有效
通过URL传递Session ID
PHP可以重写客户请求的URL,把SessionID添加到URL信息中。可以手动地在每个超链接的URL中都添加一个SessionID,但工作量比较大,不建议使用这种方式。1
2
3
4<?php
session_start();
echo'<a href="demo.php?'.session_name().'='.session_id().'">链接演示</a>;
>