一:引用变量
概念:
用不同的名字访问同一个变量内容
定义方式:&
工作原理:
PHP有一个机制,叫做cow (Copy On Write),写时拷贝
$b = $a;此时还是指向的一个内存空间,当我们对$a和$b其中一个变量进行修改操作的时候,才会发生copy操作,即给$b也复制一块内存空间 。
$b=&$a;就是公用一个内存空间。如果这个内存存储的数据变了,那么两个变量的值都会发生变化。
PHP内置内存函数:
memory_get_usage($real_usage) 返回当前分配给PHP脚本的内存量,单位是字节(byte)
$real_usage参数,值为布尔值。如果设置为 TRUE,获取系统分配的真实内存尺寸。如果未设置或者设置为 FALSE,将是 emalloc() 报告使用的内存量。
在实际WEB开发中,可以用 memory_get_usage()比较各个方法占用内存的高低
下面这个自定义函数将字节数转换成MB
代码如下:
<?php
function memory_usage() {
$memory = ( ! function_exists('memory_get_usage')) ? '0' : round(memory_get_usage()/1024/1024, 2).'MB';
return $memory;
}
验证写时拷贝:
实例:1
<?php
// 定义一个变量
$a = range(0, 1000);
var_dump(memory_get_usage());
// 定义变量b,将a变量的值赋值给b
// COW Copy On Write
$b = $a;
var_dump(memory_get_usage());
// 对a进行修改
$a = range(0, 1000);
var_dump(memory_get_usage());
结论
/Users/zhaochun/www/imooc/demo1.php:5:int 399880
/Users/zhaochun/www/imooc/demo1.php:10:int 399880
/Users/zhaochun/www/imooc/demo1.php:14:int 436800
可以看出$b = $a;之后并没有马上给$b也申请一块内存空间,还是共用一个内存空间,所以值赋值前后内存使用量大致相同(会有一点偏差),当修改$a的值的时候,才会重新给$b开辟新的内存空间,所以此时内存使用量增大。
实例:2
将实例1的$b = $a;改为$b = &$a,打印结果为:
/Users/zhaochun/www/imooc/demo2.php:5:int 399880
/Users/zhaochun/www/imooc/demo2.php:9:int 399904
/Users/zhaochun/www/imooc/demo2.php:13:int 399904
释放内存
要想减少内存的占用,可以使用unset() 函数把不再需要使用的变量删除。虽然不会删除变量指向的内存空间,但是如果这个内存空间的引用计数为0的时候。php会执行回收机制,释放该变量(zval)所占的内存空间。否则会在php程序运行完之后才会启动回收机制。
对于不需要的大的变量,如果没有及时清理,这些变量实际上会一直存在内存中,直到请求结束(参考SAPI的生命周期)。在此之前,这些变量占据的内存不能被使用,便白白浪费了,换句话说,无法释放的内存导致了内存泄露。
如果这种内存泄露仅仅发生了一次或者少数几次,倒也还好,但如果是成千上万次的内存泄露,便是很大的问题了。尤其在长时间运行的脚本中(例如守护程序,一直在后台执行不会中断),由于无法回收内存,最终会导致系统“再无内存可用”。
循环中,同名变量下次赋值的时候, 原来的内存会写入新的值,所以内存也不会一直累积。
类似还有mysql_free_result() 函数,当我们不再需要查询数据得到的结果集时,可以使用释放查询占用的内存。
实例:
echo '开始内存:'.memory_get_usage(), '';
$tmp = str_repeat('hello', 1000);
echo '运行后内存:'.memory_get_usage(), '';
unset($tmp);
echo '回到正常内存:'.memory_get_usage();
打印结果:
开始内存:147296
运行后内存:152456
回到正常内存:147296
调试检测PHP代码性能的方法有:
<?php
memory_get_usage():分析内存占用空间。
microtime():分析程序执行时间。
memory_get_peak_usage():返回内存使用峰值,
getrusage():返回CUP使用情况。
memory_get_peak_usage() — 返回分配给你的 PHP 脚本的内存峰值字节数。
memory_get_usage() —返回当前分配给你的 PHP 脚本的内存量,单位是字节(byte)
在使用linux命令 ps 或 top 命令查看进程时, 能看到内存消耗的百分比和大小, 此处的大小是与 memory_get_peak_usage() 相一致的
memory_get_peak_usage()是系统分配的内存, 在一个请求中,unset掉的数据仍然会被统计。
memory_get_usage() 是当前脚本正在使用的内存,unset掉的数据不记录在内
举例说明:
1, 数据库读出来千万条数据,假如说需要消耗100MB,那么系统会分配给进程 100MB
2, 当处理完数据后 unset 掉, 其实当前进程的消耗的内存并不会变小, 即不会释放100MB空间
3, 内存被划分为, “已使用” 和 “空闲”, unset 只会把 “已使用” 变为 “空闲”, 下次内存请求时会先去”空闲”里取
4, 程序结束, GC 才会释放全部内存
unset后 memory_get_peak_usage 仍然会不继续升高, 程序执行被分配的内存(进程里的内存)仍然要以 memory_get_peak_usage 为准
zval容器
1 概念:
每个php变量存在一个叫”zval”的变量容器中
一个zval变量容器,除了包含变量的类型和值,还包括两个字节的额外信息。
第一个是”is_ref”,是个bool值,用来标识这个变量是否是属于引用集合(reference set)。通过这个字节,php引擎才能把普通变量和引用变量区分开来,
第二个额外字节是引用次数”refcount”,用以表示指向这个zval变量容器的变量(也称符号即symbol)个数。所有的符号存在一个符号表中,其中每个符号都有作用域(scope)。refcount“变成0时就被销毁. 当任何关联到某个变量容器的变量离开它的作用域(比如:函数执行结束),或者对变量调用了函数 unset()时,”refcount“就会减1
注意:PHP7的zval结构已经改变,简单数据类型已经不使用引用计数,他们的生存周期存在于整个请求期间,request 完成后会统一销毁释放,自然也就无需通过引用计数进行内存管理。
PHP7 中全新的 zval 容器和引用计数机制
2 验证写时拷贝
<?php
// zval变量容器
//生成一个新的zval容器
$a = range(0, 3);
// 显示zval信息(此函数需要安装xdebug扩展)
xdebug_debug_zval('a');//指向a的zval容器的引用次数"refcount"为1
// 把一个变量赋值给另一变量将增加引用次数(refcount)
$b = $a;
xdebug_debug_zval('a');//指向a的zval容器的引用次数"refcount"为2,此时b也指向这个zval容器
// 修改a
$a = range(0, 3);
xdebug_debug_zval('a');//指向a的zval容器的引用次数"refcount"为1,
// 给a重新赋值的时候,发生了写时拷贝,会给a重新开辟一块内存空间,b还是原先的内存空间
打印结果:
改为引用之后 $b = &$a;,当修改$a的值,refcount仍然为2,is_ref为1,因为是引用方式
3 复合类型
当考虑像 array和object这样的复合类型时,事情就稍微有点复杂. 与 标量(scalar)类型的值不同,array和 object类型的变量把它们的成员或属性存在自己的符号表中。这意味着下面的例子将生成三个zval变量容器。
<?php
$a = array( 'meaning' => 'life', 'number' => 42 );
xdebug_debug_zval( 'a' );
以上例程的输出类似于:
a: (refcount=1, is_ref=0)=array (
'meaning' => (refcount=1, is_ref=0)='life',
'number' => (refcount=1, is_ref=0)=42
)
这三个zval变量容器是: a,meaning和 number。
取消引用
unset 一个引用,只是断开了变量名和变量内容之间的绑定。这并不意味着变量内容被销毁了,只会删除这个变量
对象的引用
默认情况下对象是通过引用传递的。(引用的是对象的标识符(一个ID),这个标识符指向对象本身)
详见:PHP中的对象复制及clone() 函数.note
但有时你可能想建立一个对象的副本,并希望原来的对象的改变不影响到副本 . 为了这样的目的,PHP定义了一个特殊的方法,称为clone()
<?php
// 对象本身就是引用传递
class Person
{
public $name = "zhangsan";
}
$p1 = new Person;
xdebug_debug_zval('p1');//指向p1的zval容器的引用次数"refcount"为1
$p2 = $p1;
xdebug_debug_zval('p1');//指向p1的zval容器的引用次数"refcount"为2,此时p2也指向这个zval容器
$p2->name = "lisi";
xdebug_debug_zval('p1');//指向p1的zval容器的引用次数"refcount"为2,普通方式赋值的时候,发生了写时拷贝,会给$p2重新开辟一块内存空间,此时指向p1的zval容器的引用次数"refcount"变为1,
//但是对象的调用以及赋值,默认是通过引用的方式,所以此时只是修改了原先的内存空间的值,p1和p2两个变量指向一个内存空间。
引用题
<?php
$data = ['a', 'b', 'c'];
foreach ($data as $key=>$val)
{
$val = &$data[$key];
var_dump($data);
}
var_dump($data);
- 程序运行时,每一次循环结束后变量$data的值是什么?请解释
程序执行完成后,变量$data的值是什么?请解释
解题:
*第一次循环:
第二次循环$key = 0; $val = 'a'; $val = &$data[$key]; 即$val=&$data[0]; 此时$data = ['a', 'b', 'c'];
第三次循环$key = 1; $val = b;因为$val是引用的值,所以第二部循环$val是有值的,此时循环体为: foreach ($data as $key=>$val=&$data[0]),也就是foreach ($data as $key=>b=&$data[0]), b=&$data[0]引用的关系,所以会改变$data[0]的值为b,(非引用的关系如:$val = $data[$key];在foreach中也是$key=>$val=$data[0],$val刚开始也是等于上一次的值,只不过foreach体中会对$val重新赋值) 接下来才是括号中的:$val = &$data[$key];b=&$data[1],这一步是给$data[1]引用赋值,值为b, 此时$data = ['b', 'b', 'c'];
循环结束$key = 2; $val = c; foreach ($data as $key=>$val=&$data[1]),也就是foreach ($data as $key=>c=&$data[1]), 所以$data[1]的值为c 此时$data = ['b', 'c', 'c'];
PHP有函数作用域,但是没有块级作用域,条件语句和循环中的变量对外部可见,而且内外部可以相互改变。PHP没有C/C++、java等语言的块级作用域概念
所以最后的结果就是最后一次循环的结果:$data = [‘b’, ‘c’, ‘c’];
二:常量和数据类型
变量名的命名规则
1、由字母、下划线“_” 、数字 组成,不能以数字开头,不允许包含空格
八大数据类型:
四种标量类型:
boolean (布尔型)
一个是TRUE,另一个FALSE,可以理解为是或否。它不区分大小写,用”echo”输出“true”是“1”,“false”则什么也不输出。
占用字节:通过查看PHP源目录Zend / zend_types.h找到bool的大小:
typedef unsigned char zend_bool;
PHP的bool类型底层是用c的char类型表示的,char只需要一个字节就能表示1或者0;(一个字节是8比特,8个0和1,有256个组合)
<?php
// 数组中的bool型,输出“true”是“1”,“false”则什么也不输出;
// 使用implode分割数组的时候,会将bool的true转为1,bool的false转换为空。使用for循环将数组拼接为字符串的时候,也是会进行转换
// 目的:使用逗号拼接字符串,true,false原样显示:
$ar = ['aaa',true,'bbb',false];
$a = '';
foreach ($ar as $key => $value) {
if($value===true){
$value = 'true';
}else if($value===false){
$value = 'false';
}
$a.=$value.',';
}
echo '<pre>';
print_r($a);
echo '</pre>';
die;
integer (整型)
集合 ℤ = {…, -2, -1, 0, 1, 2, …} 中的一个数。有长度限制,32位平台和64位不同,超出了integer范围,会返回 float。
PHP 不支持无符号的int,PHP整型是有符号的,有负数。
PHP_INT_SIZE:表示整数integer值的字长(字节)
PHP_INT_MAX:表示整数integer值的最大值
注:
输出32位系统中PHP_INT_SIZE:4字节,PHP_INT_MAX:2147483647 (10位)
输出64位系统中PHP_INT_SIZE:8字节,PHP_INT_MAX:9223372036854775807 (19位)
$a = 1234567890123456789;//19位
$b = 12345678901234567890; //20位,超出64位平台int范围,PHP会使用浮点型表示
echo '<pre>';
var_dump($a); //int(1234567890123456789)
echo '</pre>';
echo '<pre>';
var_dump($b); //float(1.2345678901235E+19)
echo '</pre>';
die;
为什么在不同的平台上int类型所占字节数有差异呢
不管什么类型,编译型语言都会把源码编译成机器码,由于不同平台寄存器位宽不一样, 所以有了自己对int的规定, 导致int 这个数据类型比较特殊,具体分配的字节数与机器字长和编译器有关。但一般等于机器寄存器位宽(64位平台除外,默认为4字节),如在32位平台上(所谓32位平台是指通用寄存器的数据宽度是32)编写代码,int 类型分配4个字节,而在16位平台是则分配2个字节。编译器是把代码转换为机器码的软件,如果他愿意,可以把int转换为256位的,只不过会增加机器的复杂度,降低可移植性。
float 浮点型
也就是通常说的小数,可以用小数点或者科学计数法表示。科学计数法可以使用小写的e,也可以使用大写的E。
$num_float1 = 1.234; //1.234
$num_float2 = 1.2e3; //1200
$num_float3 = 7.0E-3; //0.007
float: 4个字节
double: 双精度 占用8个字节
PHP没有double的写法,应该是自动判断是float还是double,占用4或者8字节
PHP中浮点数的字长和平台相关,通常最大值是 1.8e308 并具有 14 位十进制数字的精度(64 位 IEEE 格式)。
浮点数的精度
浮点数的精度有限。尽管取决于系统,PHP 通常使用 IEEE 754 双精度格式,则由于取整而导致的最大相对误差为 1.11e-16。非基本数学运算可能会给出更大误差,并且要考虑到进行复合运算时的误差传递。
此外,以十进制能够精确表示的有理数如 0.1 或 0.7,无论有多少尾数都不能被内部所使用的二进制精确表示,因此不能在不丢失一点点精度的情况下转换为二进制的格式。这就会造成混乱的结果:例如,floor((0.1+0.7)*10) 通常会返回 7 而不是预期中的 8,因为该结果内部的表示其实是类似 7.9999999999999991118…。
所以永远不要相信浮点数结果精确到了最后一位,也永远不要比较两个浮点数是否相等。如果确实需要更高的精度,应该使用任意精度数学函数或者 gmp 函数。
BCMath 数学函数
对于任意精度的数学, 如果有足够多的内存,PHP 提供的 BCMath 支持用字符串的形式表示任意大小和精度的数字,最大尺寸为 2147483647(即 0x7FFFFFFF)。
比较浮点数
要测试浮点数是否相等,要使用一个仅比该数值大一丁点的最小误差值。该值也被称为机器极小值(epsilon)或最小单元取整数,是计算中所能接受的最小的差别值。
$a 和 $b 在小数点后五位精度内都是相等的。
<?php
$a = 1.23456789;
$b = 1.23456780;
$epsilon = 0.00001;
if(abs($a-$b) < $epsilon) {
echo "true";
}
NaN
某些数学运算会产生一个由常量 NAN 所代表的结果。此结果代表着一个在浮点数运算中未定义或不可表述的值。任何拿此值与其它任何值(除了 true)进行的松散或严格比较的结果都是 false。
由于 NAN 代表着任何不同值,不应拿 NAN 去和其它值进行比较,包括其自身,应该用 is_nan() 来检查。
为什么浮点型不精确
因为十进制小数转换为二进制的过程中,使用的方法是乘 2 取整,小数继续乘2取整,直到积中的小数部分为零,最后顺序排列;有的数会出现无限循环的问题,所以浮点数是不精确的。
二进制 字符编码
string (字符串)
数字字母占用一个字节,汉字大部分占用三个字节
两种复合类型:
array (数组)
PHP 中的数组实际上是一个有序映射。映射是一种把 values 关联到 keys 的类型
索引数组赋值:
$arr[0]='苹果';
$array = array('苹果');
$array = array("foo", "bar", "hallo", "world");
关联数组赋值:
$arr['apple']='苹果';
$array = array('one'=>'苹果','two'=>'red');
$array = ['one'=>'apple','two'=>'red'];
object (对象)
要创建一个新的对象 object,使用 new 语句实例化一个类
两种特殊类型:
resource (资源)
一种特殊变量,保存了到外部资源的一个引用。资源是通过专门的函数来建立和使用的,资源类型变量为:打开文件、数据库连接、图形画布区域等的特殊句柄,因此将其它类型的值转换为资源没有意义。
NULL
表示一个变量没有值。NULL 唯一可能的值就是 不区分大小写的null
PHP数据类型底层
在c语言中,32位int都是占用4个字节,long占用4个字节;64位系统下,int占用4个字节,long占用8个字节
而PHP中只有int类型,int类型也要代替long的范围,所以在64位系统下,要占用8字节。
PHP语法简单,并没有规定很多细粒度的数据类型,比如用一个int就代替了short int,int,long 等;所以就会浪费空间,用short就能做的事,实际需要使用long来代替。
1、16位编译器
char :1个字节
char*(即指针变量): 2个字节
short int : 2个字节
int: 2个字节
unsigned int : 2个字节
float: 4个字节
double: 8个字节
long: 4个字节
long long: 8个字节
unsigned long: 4个字节
2、32位编译器
char :1个字节
char*(即指针变量): 4个字节(32位的寻址空间是2^32, 即32个bit,也就是4个字节。同理64位编译器)
short int : 2个字节
int: 4个字节
unsigned int : 4个字节
float: 4个字节
double: 8个字节
long: 4个字节
long long: 8个字节
unsigned long: 4个字节
3、64位编译器
char :1个字节
char*(即指针变量): 8个字节
short int : 2个字节
int: 4个字节
unsigned int : 4个字节
float: 4个字节
double: 8个字节
long: 8个字节
long long: 8个字节
unsigned long: 8个字节
伪类型:
混合型(mixed) 说明一个参数可以接受多种不同的类型。<br /> 数字型(number) 说明一个参数可以是 [int](http://php.net/manual/zh/language.types.integer.php) 或者 [float](http://php.net/manual/zh/language.types.float.php)。<br /> 回调(callback) [callback](http://php.net/manual/zh/language.pseudo-types.php#language.types.callback)回调类型。<br /> void(),无效的, 返回值无意义、作为入参表示不接受任何参数
注意:
整型表示方法:
它可以用十进制、八进制、十六进制指定。十进制就是日常使用的数字;八进制,数字前必须加上“0”;十六进制,数字前必须加“0x”
$data_int = 1234; // 十进制数
$data_int = -567; // 一个负数
$data_int = 0123; // 八进制数(等于十进制的 83)
$data_int = 0x1a; // 十六进制数(等于十进制的 26)
浮点类型不能用到比较运算当中:
$a = 0.1;
$b = 0.7;
$c = $a + $b;
if ($c == 0.8) {
echo 1;
}else{
echo 2;
}
echo $c;//0.8
此时输出2,说明$c不等于0.8,虽然直接输出$c的值为0.8,是PHP程序进行四舍五入了,
0.7+0.8进行运算的时候,交给cpu进行运算,cpu在运算的时候会转成二进制,就会有一定的损耗,最后的值是0.79999....
解决办法:将浮点型都扩大倍数,在进行运算,最后将结果缩小倍数。如$c = (($a*100)+($b*100))/100
布尔等于false的七种情况:
整型0,包括000
浮点0.0,包括0.000
0字符串 '0' ,注意:字符串 "00" "0.0"为true,
空字符串,单引号双引号,heredoc和newdoc
布尔false,
NULL
空数组array(),
为NULL值的情况:
被赋值为 NULL
尚未被赋值(未定义的变量)
被 unset()。unset($var)将会删除$var变量,但变量指向的内存空间的值不会删除)
使用 $b = (unset)$var ,会将$var转换为null,但不会删除$var变量和$var的值,仅是返回 NULL 值而已。没有实际意义,直接给$b=NULL即可。
字符串的定义方式以及区别:
转义字符:
转义字符 | 意义 | ASCII码值(十进制) |
---|---|---|
\a | 响铃(BEL) | 007 |
\b | 退格(BS) ,将当前位置移到前一列 | 008 |
\f | 换页(FF),将当前位置移到下页开头 | 012 |
\n | 换行(LF) ,将当前位置移到下一行开头 | 010 |
\r | 回车(CR) ,将当前位置移到本行开头 | 013 |
\t | 水平制表(HT) (跳到下一个TAB位置) | 009 |
\v | 垂直制表(VT) | 011 |
\\ | 代表一个反斜线字符’’\‘ | 092 |
\‘ | 代表一个单引号(撇号)字符 | 039 |
\“ | 代表一个双引号字符 | 034 |
\? | 代表一个问号 | 063 |
\0 | 空字符(NULL) | 000 |
\ooo | 1到3位八进制数所代表的任意字符 | 三位八进制 |
\xhh | 1到2位十六进制所代表的任意字符 | 二位十六进制 |
不能解析 就会原样输出;
能解析就是会 将其代表的实际意思输出;
\转义符号,可以转义某些场景下具有特殊含义的字符,使其失去特殊含义,如 ‘ \’ ‘ 会输出’ ,将单引号转义
或者和其他字符组合成为特定含义的符号:如\n表示换行
单引号:
单引号不能解析变量和转义字符,但是可以转义\和单引号本身,单引号中出现\和’会报错,因为转义了,需要前面加个\再次进行转义,使其失去特殊含义
‘$a’;会输出$a;’{$a}’会输出{$a}
‘\n’输出\n ‘ \’ ‘ 输出’ ‘\‘ 输出\ ‘ \” ‘输出 \” (单引号中的双引号没有特殊含义,所以\”没有特殊含义,原样输出)
双引号:
双引号可以解析变量
$a=1;
"$a"会输出对应的值1;"{$a}"也会输出1
"abc$ab" 会输出abc,因为程序找不到$ab变量,为了清楚的判断变量名是$a还是$ab,变量可以使用特殊字符和{}包含
"abc$a-b" 会输出:abc1-b,因为变量由数字字母下划线组成,不能由数字开头,所以-之前的是变量名,如果这里是$a_b就会输出abc,提示找不到变量$a_b
双引号可以解析所有转义字符
“\” “ “ “,双引号中出现一个\或者”,也不会原样输出这两个字符,会进行解析,所以会报错,可以使用转义符号\将\或者”进行转义,使其没有特殊含义,作为普通字符输出
“ \n “会被解析成换行 “ \” “输出” ‘\“输出\ “\’”输出\’(双引号中的单引号没有特殊含义,所以\”没有特殊含义,原样输出)
heredoc和newdoc(用来处理大文本)
heredoc类似于双引号
$str = <<<EoT
可以解析变量,变量不需要用连接符.或,来拼接,可以直接写html代码,里面带变量。
EoT;
newdoc类似于单引号
$str = <<<’EoT’
不可以解析变量
EoT;
9大超全局变量
超全局变量是在全部作用域中始终可用的内置变量,这意味着它们在一个脚本的全部作用域中都可用。在函数或方法中无需执行 global $variable; 就可以访问它们。
<?php
echo '<pre>';
print_r($GLOBALS);
echo '</pre>';
Array
(
[_GET] => Array()
[_POST] => Array()
[_COOKIE] => Array
(
[remember_82e5d2c56bdd0811318f0cf078b78bfc] => eyJpdiI6ImtmVTJxUWpZdGpXakxtYzd0XC9TRU5BPT0iLCJ2YWx1ZSI6InRiQnMzb1wvcE44U0pUTUhHbnkyaXNNMVk2NG9JN0Nhb0dpTlhHY1ZWd1lmWFFwUzFkcEdTeDE5NVBBRGpZR1lncHlyNVZXcHVEKzJBekRzK25uQU8xRVo3ZHdRaWhaMFEyclU1UGdzQ0xJYz0iLCJtYWMiOiJkMmE1Y2NhNTM2NDAzYTU0MjMzNTU4ZWE1M2FhZjc2N2IwODVkMzRmYjk1YmFmOWMzNjA1MmJkNWEzNTBiNWU2In0=
)
[_FILES] => Array()
[_ENV] => Array()
[_REQUEST] => Array()
[_SERVER] => Array
(
[HTTP_HOST] => localhost
[HTTP_CONNECTION] => keep-alive
[HTTP_CACHE_CONTROL] => max-age=0
[HTTP_UPGRADE_INSECURE_REQUESTS] => 1
[HTTP_USER_AGENT] => Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36
[HTTP_SEC_FETCH_USER] => ?1
[HTTP_ACCEPT] => text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
[HTTP_SEC_FETCH_SITE] => same-origin
[HTTP_SEC_FETCH_MODE] => navigate
[HTTP_REFERER] => http://localhost/demo/
[HTTP_ACCEPT_ENCODING] => gzip, deflate, br
[HTTP_ACCEPT_LANGUAGE] => zh-CN,zh;q=0.9
[HTTP_COOKIE] => remember_82e5d2c56bdd0811318f0cf078b78bfc=eyJpdiI6ImtmVTJxUWpZdGpXakxtYzd0XC9TRU5BPT0iLCJ2YWx1ZSI6InRiQnMzb1wvcE44U0pUTUhHbnkyaXNNMVk2NG9JN0Nhb0dpTlhHY1ZWd1lmWFFwUzFkcEdTeDE5NVBBRGpZR1lncHlyNVZXcHVEKzJBekRzK25uQU8xRVo3ZHdRaWhaMFEyclU1UGdzQ0xJYz0iLCJtYWMiOiJkMmE1Y2NhNTM2NDAzYTU0MjMzNTU4ZWE1M2FhZjc2N2IwODVkMzRmYjk1YmFmOWMzNjA1MmJkNWEzNTBiNWU2In0%3D
[PATH] => /usr/bin:/bin:/usr/sbin:/sbin
[SERVER_SIGNATURE] =>
[SERVER_SOFTWARE] => Apache/2.4.41 (Unix) PHP/7.2.25
[SERVER_NAME] => localhost
[SERVER_ADDR] => ::1
[SERVER_PORT] => 80
[REMOTE_ADDR] => ::1
[DOCUMENT_ROOT] => /Users/zhaochun/www
[REQUEST_SCHEME] => http
[CONTEXT_PREFIX] =>
[CONTEXT_DOCUMENT_ROOT] => /Users/zhaochun/www
[SERVER_ADMIN] => webmaster@dummy-host2.example.com
[SCRIPT_FILENAME] => /Users/zhaochun/www/demo/a.php
[REMOTE_PORT] => 64211
[GATEWAY_INTERFACE] => CGI/1.1
[SERVER_PROTOCOL] => HTTP/1.1
[REQUEST_METHOD] => GET
[QUERY_STRING] =>
[REQUEST_URI] => /demo/a.php
[SCRIPT_NAME] => /demo/a.php
[PHP_SELF] => /demo/a.php
[REQUEST_TIME_FLOAT] => 1581412374.152
[REQUEST_TIME] => 1581412374
)
[GLOBALS] => Array
*RECURSION*
)
$_SERVER:服务器和执行环境信息。包含了诸如头信息(header)、路径(path)、以及脚本位置(script locations)等等信息的数组。这个数组中的项目由 Web 服务器创建。
http://localhost/imooc/ceshi.php/user/login?id=123
[SERVER_ADDR] => 192.168.0.109,服务器ip地址
[REMOTE_ADDR] => 192.168.0.103,客户端的ip地址
[SERVER_NAME] => localhost,域名
[REQUEST_URI] => /imooc/ceshi.php/user/login?id=123,完整路径
[SCRIPT_NAME] => /imooc/ceshi.php,脚本路径
[PATH_INFO] => /user/login,路由
[QUERY_STRING] => id=123,url参数
[HTTP_REFERER] => http://localhost/imooc/demo.html,上级跳转过来的URL
$_GET:通过 URL 参数传递给当前脚本的变量的数组。(form表单get方式也是通过URL传递参数的)
$_POST:用POST方法传递的参数的有关信息。当 HTTP POST 请求的 Content-Type 是 application/x-www-form-urlencoded 或 multipart/form-data 时,会将变量以关联数组形式传入当前脚本。
$_FILES:包含所有上传的文件信息。
$_COOKIE:通过HTTP cookie传递到脚本的信息
$_SESSION:所有会话有关的信息
$_REQUEST:默认情况下包含了 $_GET,$_POST 和 $_COOKIE 的数组。不建议使用,比较慢
$_ENV:是一个包含服务器端环境变量的数组,为空的话可以在php.ini中开启,生产服务器建议关闭。
常量
常量一经定义,不能被修改,不能被删除。
通常常量都全用大写
常量类似变量,不同之处在于:
在设定以后,常量的值无法更改
常量名不需要开头的美元符号 ($)
PHP7数组常量可以使用 define() 函数定义。 在PHP5.6,它们只能使用 const 关键字定义。
define('language', ['php', 'java', 'jsp', 'asp']);
const language = ['php', 'java', 'jsp', 'asp'];
echo language[1];//java
define()函数申明常量:
define(name,value,case_insensitive)
第三个参数如果设置为true,对大小写敏感
const也可以声明常量:
const FOO= “abcdef”;
define(‘FOO’, ‘BAR’);
echo FOO;
const和define()区别:类中使用const,条件语句使用define()
const本身就是一个语言结构。而define是一个函数。所以使用const速度要快的多。
const是在编译时定义,因此必须处于最顶端的作用区域,可用于全局变量,可用于类成员的定义。不能在函数,循环及if条件中使用;
define是函数,也就是能调用函数的地方都可以使用
<?php
class ClassName1
{
const A= "a"; //常量可定义类成员
function __construct()
{
define('FOO', 'b');//可在函数中使用
// const FOO= "abcdef";//报错,不可在函数中使用
echo FOO;//b
echo self::A; //a
}
}
echo ClassName1::A; //a,类似静态变量,可以直接访问
new ClassName1();
<br /> <br />**获取常量的值:**
constant(string $name),当常量名储存在一个变量里,或者由函数返回常量名。当常量为字符串类型时候可以使用constant获取常量的值,
echo constant('PAI'); 效果等于:echo PAI;
判定常量是否被定义
defined(string constants_name)
系统常量
PHP_VERSION:当前解析器的版本号。它可以告诉我们当前PHP解析器的版本号
PHP_OS:执行当前PHP版本的操作系统名称
__FILE__ :php程序完整文件名。当前文件在服务器的物理位置。
__DIR__:PHP程序位置
__LINE__ :PHP程序文件行数。它可以告诉我们,当前代码在第几行。
__CLASS__: 类名
__FUNCTION__:只是返回方法的名字:doitAgain
__METHOD__:返回类的名字和方法的名字:Test::doitAgain
__NAMESPACE__:命名空间名称:
__TRAIT__:Trait 是为类似 PHP 的单继承语言而准备的一种代码复用机制,通过trait申明的代码,可以直接使用use在类中使用。但是trait中的代码有外部依赖的话,需要通过引入或者传递参数的形式写入trait中。
<?php
trait Dog{
public $name="dog";
public function drive(){
echo "This is dog drive";
}
public function eat(){
echo "This is dog eat";
}
}
class Animal{
public function drive(){
echo "This is animal drive";
}
public function eat(){
echo "This is animal eat";
}
}
class Cat extends Animal{
use Dog; //使用trait类,必须在函数外引入
// 可以重写trait的方法
public function drive(){
echo "This is cat drive";
}
}
$cat = new Cat();
$cat->drive(); // This is cat drive
echo "<br/>";
$cat->eat(); // This is dog eat
同名方法优先级:本类,trait类中,继承的基类(extends继承的类就是基类);
优先顺序是来自当前类的成员覆盖了 trait 的方法,而 trait 则覆盖了被继承的方法
一个类可以组合多个Trait,通过逗号隔开,如下
use trait1,trait2
当不同的trait中,有着同名的方法或属性,会产生冲突,可以使用insteadof或 as进行解决,insteadof 是进行替代,而as是给它取别名,as 还可以修改方法的访问控制
class dog{
use trait1,trait2{
trait1::eat insteadof trait2;
trait2::drive as driven;
//trait2::drive as private driven;
}
}
三:运算符
算术运算符:
+ - / %
% 模数 $x % $y $x 除 $y 的余数
赋值运算符
1,“=”:把右边表达式的值赋给左边的运算数。它将右边表达式值复制一份,交给左边的运算数。换而言之,首先给左边的运算数申请了一块内存,然后把复制的值放到这个内存中(写时拷贝:其中一个值发生改变的时候,才会重新申请内存空间)。
2,“&”:引用赋值,意味着两个变量都指向同一个数据。它将使两个变量共享一块内存,如果这个内存存储的数据变了,那么两个变量的值都会发生变化。
3,+= -= = /= %=
$a = 5;
$a+1;
echo $a;//5
echo '<hr>';
$a+=1;//相当于 $a=$a+1;
echo $a;//6
位运算
位运算
比较运算符
== 等于
=== 全等
!= 不等
<> 不等
!== 非全等
<
>
<=
>=
$a??$b??$c NULL合并运算符:从左往右第一个存在且不为 NULL,将返回它的第一个操作数;否则返回第二个操作数,以此类推。代替三元运算并与 isset()函数功能结合一起使用,php7提供
三元运算符
<?php
对于表达式(expr1)?(expr2):(expr3),如果expr1的值为true,则此表达式的值为expr2,否则为expr3。
$a >= 60 ? "及格": "不及格";
// 算考生在第几排第几个位置,
$maxLine = 4; //每排人数
$no = 18;//学生编号
$line = ceil($no/$maxLine);//计算在第几排。除不尽就进一,下一排
//计算第几排第几个位置。$no%$maxLine余数为0说明能除进,那么位置就是每排人数。除不尽就取余数,余数是几就在第几个座位
$row = $no%$maxLine ? $no%$maxLine : $maxLine;
echo "编号<b>".$no."</b>的座位在第<b>".$line."</b>排第<b>".$row."</b>个位置";
执行运算符
反引号``表示,PHP 将尝试将反引号中的内容作为 shell 命令来执行
$output = `ls -al`;
echo '<pre>';
print_r($output);
echo '</pre>';
会在浏览器中输出和终端中运行ls -al相同的效果
逻辑运算符
and 与 $x and $y 如果 $x 和 $y 都为 true,则返回 true。
or 或 $x or $y 如果 $x 和 $y 至少有一个为 true,则返回 true。前一个成立则不执行后一个
xor 异或 $x xor $y 如果 $x 和 $y 有且仅有一个为 true,则返回 true。
&& 与 $x && $y 如果 $x 和 $y 都为 true,则返回 true。
|| 或 $x || $y 如果 $x 和 $y 至少有一个为 true,则返回 true。前一个成立则不执行后一个
! 非 !$x 如果 $x 不为 true,则返回 true。
字符串运算符
. 连接运算符 :它返回其左右参数连接后的字符串
$txt1 = “Hello” $txt2 = $txt1 . “ world!” 现在 $txt2 包含 “Hello world!”
.= 连接赋值运算符 :它将右边参数附加到左边的参数之后
$txt1 = “Hello” $txt1 .= “ world!” 现在 $txt1 包含 “Hello world!”
递增/递减运算符
运算符 名称 描述
++$x 前递增 $x 加一递增,然后返回 $x
$x++ 后递增 返回 $x,然后 $x 加一递增
—$x 前递减 $x 减一递减,然后返回 $x
$x— 后递减 返回 $x,然后 $x 减一递减
++ — 不影响布尔值
递减NULL没效果,仍为NULL
递增NULL值为1
数组运算符
运算符 名称 例子 结果
+ 联合 $x + $y $x 和 $y 的联合(但不覆盖重复的键)
== 相等 $x == $y 如果 $x 和 $y 拥有相同的键/值对,则返回 true。
=== 全等 $x === $y 如果 $x 和 $y 拥有相同的键/值对,且顺序相同类型相同,则返回 true。
!= 不相等 $x != $y 如果 $x 不等于 $y,则返回 true。
<> 不相等 $x <> $y 如果 $x 不等于 $y,则返回 true。
!== 不全等 $x !== $y 如果 $x 与 $y 完全不同,则返回 true。
类型运算符
instanceof 用于确定一个 PHP 变量是否属于某一类 class 的实例:也可用来确定一个变量是不是继承自某一父类的子类的实例:
返回bool型
<?php
class MyClass{}
$a = new MyClass;
var_dump($a instanceof MyClass);//bool(true)
错误控制运算符 “@”
将@放置在一个PHP表达式之前,该表达式可能产生的任何错误信息都被忽略掉;
如果激活了track_error(在php.ini中设置)特性,表达式所产生的任何错误信息都被存放在变量$php_errormsg中,此变量在每次出错时都会被覆盖,所以如果想用它的话必须尽早检查。
$conn = @mysql_connect(“localhost”,”username”,”password”);
echo “出错了,错误原因是:”.$php_errormsg;
需要注意的是:错误控制前缀“@”不会屏蔽解析错误的信息,不能把它放在函数或类的定义之前,也不能用于条件结构例如if和foreach等。
运算符优先级
经常用到优先级顺序:算数运算 比较运算 符号逻辑运算&& || 赋值运算 字母逻辑运算and or :
优先级从上到下:(开发当中应该使用括号标明运算顺序增加代码可读性)
递增++,递减—,右边的优先级高
! 逻辑运算符 取反
/ % + - . 算数运算符 乘除取余加减
< <= > >= == != === !== <> <=> 比较运算符
& 位运算和引用
&& || 符合逻辑运算符,&&比 || 优先级高,
_= += -= = /= 赋值运算符,右边优先级高:$a = $b = $c 等同$a = ($b = $c)
_and xor or 字母逻辑运算符
<?php
/**
>优先级高于||高于=
先执行比较运算符:前一个3>0成立,为true,然后执行逻辑或运算1 || ,逻辑或成立,立即返回true,且不再执行逻辑或后面的。
以此时将true赋值给$a,$b为默认设置的0
递增不影响布尔值,true++仍为true。
int 0++为1.
*/
$a = 0;
$b = 0;
if ($a = 3 > 0 || $b = 3>0)
{
var_dump($a);//boolean true
var_dump($b);//int 0
$a++;
$b++;
var_dump($a);//boolean true
var_dump($b);//int 1
}
// 把上面的if改为:
// if ($a = 3 < 0 || $b = 3>0) ,$a $b 前后四个值都为 :boolean true
//先执行比较运算符: 前一个3<0不成立,然后执行后一个3>0,成立,
//然后执行逻辑运算符:$a = false || $b = true,逻辑或有一个为true,返回true
//最后执行赋值运算符:逻辑或前一个不成立,会执行后面的。赋值运算符右边优先级高,先给$b赋值,3>0成立返回true ,所以$b = true。然后 将false||true的结果返回给$a,为true
// 所以$a=true;$b=true;递增不影响布尔型,所以结果都是true;
短路效果:如果 || 前面的成立,立即返回true,|| 后面的优先级高也不会执行,实例:
if( true || $a==w){
echo 1;
}else{
echo 2;
}
此时会输出1,说明$a == w没有执行。如果执行的话会提示错误:使用未定义的常量w
改为:if( false || $a==w),给出错误:使用未定义常量,然后输出2
四 流程控制
数组指针操作相关函数:
reset() 函数将内部指针指向数组中的第一个元素,并输出。
list () 把数组中的值赋给一组变量,这不是真正的函数,而是语言结构。仅能用于数字索引的数组,并假定数字索引从 0 开始。
<?php
$my_array = array("Dog","Cat","Horse");
list($a, $b, $c) = $my_array;
current() - 返回数组中的当前元素的值
end() - 将内部指针指向数组中的最后一个元素,并输出
next() - 将内部指针指向数组中的下一个元素,并输出
prev() - 将内部指针指向数组中的上一个元素,并输出
each() - 返回当前元素的键名和键值,并将内部指针向前移动。键/值对被返回带有4个元素的关联和索引混合的数组,键名分别为0、1、key和value。其中键名0和key对应的值是一样的,是数组元素的键名,1和value则包含有数组元素的值。正因为each返回的当前元素的键名中有数字索引,且是0,所以可以将键值赋值给list,结合while循环,可以遍历数组
遍历数组的方式
for循环:只能遍历索引数组,且数组下标需要连续。(for循环遍历数组需要计算数组的长度,来决定循环的次数)
<?php
$arr = array('http://www.jb51.net','脚本之家','PHP教程');
$num = count($arr); //先计算出数组$arr中元素的个数
for($i=0;$i<$num;++$i){
echo $arr[$i].'<br />';
}
foreach遍历数组:可遍历索引和关联数组,foreach 仅能够应用于数组和对象;
无需计算数组长度;foreach首先会复制一份数组进行遍历,对数组进行改写的时候才会发生拷贝,foreach对原数组的改写并不会影响原数组;可以通过在 $value 之前加上 & 来修改数组的元素。此方法将以引用赋值而不是拷贝一个值:
<?php
$arr = array(1, 2, 3, 4);
foreach ($arr as &$value) {
$value = $value * 2;
}
// 现在 $arr 是 array(2, 4, 6, 8)
// 数组最后一个元素的 $value 引用在 foreach 循环之后仍会保留。建议使用 unset() 来将其销毁
unset($value); // 最后取消掉引用
// 未使用 unset($value) 时,$value 仍然引用到最后一项,也就是上面的$arr[3]的值;$arr[3]的值会被之后的$value变量更新掉;导致后续的$value变量的值改变了原arr数组。
foreach ($arr as $key => $value) {
// $arr[3] 会被每一个$value更新掉
echo "{$key} => {$value} ";
print_r($arr);
}
// 直到最终倒数第二个值被复制到最后一个值
// output:
// 0 => 2 Array ( [0] => 2, [1] => 4, [2] => 6, [3] => 2 ) // 第1次遍历的value是2,2替换到arr[3]
// 1 => 4 Array ( [0] => 2, [1] => 4, [2] => 6, [3] => 4 ) // 第2次遍历的value是4,4替换到arr[3]
// 2 => 6 Array ( [0] => 2, [1] => 4, [2] => 6, [3] => 6 ) // 第3次遍历的value是6,6替换到arr[3]
// 第四次遍历的是arr[3],此时arr[3]的值已经是6了
// 3 => 6 Array ( [0] => 2, [1] => 4, [2] => 6, [3] => 6 ) // 第4次遍历的value是6,6替换到arr[3]
foreach会对数组进行重置数组指针操作,即:reset()操作,如果一个数组之前进行了数组指针的操作,如next()将数组指针进行了移动,使用foreach遍历该数组会重置数组,即从数组的第一个元素遍历
<?php
$arr = array('http://www.jb51.net','脚本之家','PHP教程');
foreach($arr as $value){
echo $value.'<br />';
}
foreach($arr as $k=>$v){
echo $k."=>".$v."<br />";
}
foreach 这种迭代的方法,是如何移动指针,如何防止迭代越界?
foreach 的时候, 是先将指针初始化到0的位置, 然后进行 valid 越界判定, 如果没有越界,那么打印当前的 值 和 key,这算是完成了第一次迭代, 然后调用 next 的方法,挪动指针到下一个位置, 判断越界,然后打印键值对, 一直到数据打印完, 依然让指针 +1,这时候发现 指针指向了一个空节点,然后 valid 方法判断返回 false,才结束了打印
while循环,只能遍历索引数组;和for循环类似,需要计算数组长度。while可以方便的设置多条件过滤,比for灵活
<?php
$arr = array('http://www.jb51.net','脚本之家','PHP教程');
$num = count($arr); //先计算出数组$arr中元素的个数
$i=0;
while ( $i<$num ) {
echo $arr[$i].'<br />';
$i++;
}
do{} while 循环,先执行一次,然后进行while条件判断,为true再进行循环。也只能遍历索引数组
<?php
$arr = array('http://www.jb51.net','脚本之家','PHP教程');
$num = count($arr); //先计算出数组$arr中元素的个数
$i=0;
do{
echo $arr[$i].'<br />';
$i++;
}while($i<$num);
while list() each() 组合循环:可遍历索引和关联数组,该组合循环不会重置数组指针,如果之前对改数组进行了指针操作,使用组合循环的时候,接着上次的数组指针继续循环。
PHP7.2已经废弃,不建议使用:因为each()函数在几乎所有可以想象到的方面都不如foreach,包括超过10倍慢
<?php
$arr = array('http://www.jb51.net','脚本之家','PHP教程');
while(list($k,$v) = each($arr)){
echo $k.'=>'.$v.'<br />';
}
continue; // 跳出本次循环,可选数字参数来决定跳过几重循环到循环结尾。默认值是 1,即跳到当前循环末尾。如if里面包含if,在里层if里写continue 2,就会直接跳出外层循环中的下一轮循环
break; //终止循环,如果要跳出多重循环的话,可以用n来表示层数,如果不带参数默认是终止本次循环。
各种循环应用场景:
for循环一般用于计数循环
while循环一般用于多条件循环,也可以用于计数循环;比for灵活
foreach用于遍历关联和索引数组
遍历对象
PHP 提供了一种定义对象的方法使其可以通过单元列表来遍历,例如用 foreach 语句。默认情况下,所有可见属性都将被遍历。
<?php
class MyClass
{
public $var1 = 'value 1';
public $var2 = 'value 2';
public $var3 = 'value 3';
protected $protected = 'protected var';
private $private = 'private var';
function iterateVisible() {
echo "MyClass::iterateVisible:\n";
foreach ($this as $key => $value) {
print "$key => $value\n";
}
}
}
$class = new MyClass();
foreach($class as $key => $value) {
print "$key => $value\n";
}
echo "\n";
$class->iterateVisible();
//输出
// 首选会将public的属性遍历出来
var1 => value 1
var2 => value 2
var3 => value 3
//然后遍历可见方法
MyClass::iterateVisible:
// iterateVisible方法内进行遍历,私有遍历和受保护的变量对本类都是可见的,所以都可以遍历出来。
var1 => value 1
var2 => value 2
var3 => value 3
protected => protected var
private => private var
分支
if elseif else
在if elseif中只能有一个语句块被执行
使用if基本原则:把可能性大的条件放在前面处理,减少流程判断,因为if会从前往后依次进行判断
switch case
<?php
switch (variable) {//参数只能是整形,浮点型,字符串。
case 'value':
# code...
break;
default:
# code...
break;
}
continue 语句作用到 switch 上的作用类似于 break
switch 语句一行接一行地执行,当一个 case 语句中的值和 switch 表达式的值匹配时 PHP 才开始执行语句,直到 switch 的程序段结束或者遇到第一个 break 语句为止。如果不在 case 的语句段最后写上 break 的话,PHP 将继续执行下一个 case 中的语句段。例如:
<?php
switch ($i) {
case 0:
echo "i equals 0";
case 1:
echo "i equals 1";
case 2:
echo "i equals 2";
}
如果 $i 等于 0,PHP 将执行所有的 echo 语句!如果 $i 等于 1,PHP 将执行后面两条 echo 语句。只有当$i 等于 2 时,才会得到“预期”的结果——只显示“i equals 2”。
if和switch比较:
在 switch 语句中条件只求值一次并用来和每个 case 语句比较(而且switch会生成跳转表,直接跳到对应的case)。
在 else if 语句的条件会再次求值。
如判断参数$a的值在一到五之间,switch会先求值一次,如得到$a等于2,就会找case为2的,求值只运算了一次,而if条件中,if会进行一次求值,判断$a是否等于1,然后else if判断是否等于2…会求值多次。
if else 支持复杂的条件判断,而switch只适用于明确值的条件判断:如字典值,数字比较等;
优化多个if elseif?
1,把可能性大的条件放在前面处理,减少流程判断,因为if会从前往后依次进行判断
2,如果参数是整形,浮点型,字符串,使用switch代替if
3,混合开发中,前端模版中的判断一般用if else
替代写法
if,while,for,foreach,switch有替代写法:使用:冒号、endif、endwhile、endfor
替代语法的基本形式:
把左花括号 { ,换成冒号 : ,把右花括号 } 分别换成 endif;,endwhile;,endfor;,endforeach; 以及 endswitch;
例子:
正常写法:
while ( $i <= 10) {
# code...
}
替代写法:更加简洁,一般用于PHP和HTML混编中,更加清晰
while ($i <= 10):
# code...
endwhile;
五 自定义函数和内部函数
可变函数
通过变量的值来调用函数,因为变量的值是可变的,所以可以通过改变一个变量的值来实现调用不同的函数。
经常用在 回调函数、函数列表,或者根据动态参数来调用不同的函数。可变函数的调用方法为变量名加括号。
<?php
function name() {
echo 'jobs';
}
$func = 'name';
$func(); //调用可变函数
可变函数也可以用在对象的方法调用上。
<?php
class book {
function getName() {
return 'bookname';
}
}
$func = 'getName';//方法名
$book = new book();
$book->$func();
静态变量:static关键字
1 仅初始化一次
2 初始化时需要赋值
3 每次执行函数,该值会保留
4 static变量是局部的,仅在函数内部有效,当程序执行离开此作用域时,其值并不会消失
5 可以记录函数的调用次数,从而可以在某些条件下终止递归
第一次调用函数,函数内部定义了static关键字,但是没有赋值,所以此时$count=NULL,NULl递增为1,递减没效果,由于是后递增,先返回NULL,然后递增为1,第一次结果为NULL
第二次调用函数,由于静态变量在一次程序运行中只初始化一次,所以static $count,这一步初始化操作不执行,在内存中找到$count的值为1,return $count++ , 所以此时值为1
最终的值是5 NULL 1 ,但是 echo NULL不会输出,所以最终的值是5 1
require incluce
require,需要必须的意思。在一开始就加载,放在脚本程序的最前面,文件出错时,脚本终止,致命错误
incluce,包含的意思,在用到时加载,文件中出错了,主程序继续往下执行,只有一个警告错误
在流程控制的处理区段中,require是无条件包含也就是如果一个流程里加入require,无论条件成立与否都会先执行require所在的这个区段中
_once后缀表示已加载的不加载,先检查要导入的档案是不是已经在该程序中的其它地方被导入过了,如果有的话就不会再次重复导入
内置函数
另外一些函数是通过其他扩展来支持的,比如mysql数据库处理函数,GD图像处理函数,邮件处理函数等,PHP默认加载了一些常用的扩展库,我们可以安装或者加载其他扩展库来增加PHP的处理函数。
时间日期函数
date_default_timezone_set(‘PRC’);//设置东8区时区
time()返回当前的unix时间戳
自 Unix 纪元(0:00:00 January 1, 1970 GMT)起到现在的秒数,值与micrtime()的sec值一致。
date()格式化一个本地时间/日期,第二个参数默认为time(),
echo date(“Y-m-d H:i:s”).”
“;//2017-02-28 06:56:42
strtotime()将英文文本日期时间解析为 Unix 时间戳:
指定日期的时间戳:
<?php
$t ="1989-03-01 01:00:00";
echo strtotime($t);//604688400
strtotime('now')和time();一样,都是当前时间戳
echo date('Y-m-d H:i:s',strtotime('now'));//当前时间戳 2017-01-09 21:04:11
echo date('Y-m-d H:i:s',strtotime('+1second'));//当前时间戳+1秒 2017-01-09 21:04:12
echo date('Y-m-d H:i:s',strtotime('+1minute'));//当前时间戳+1分 2017-01-09 21:05:11
echo date('Y-m-d H:i:s',strtotime('+1hour'));//当前时间戳+1小时 2017-01-09 22:04:11
echo date('Y-m-d H:i:s',strtotime('+1day'));//当前时间戳+1天 2017-01-10 21:04:11
echo date('Y-m-d H:i:s',strtotime('+1week'));//当前时间戳+1周 2017-01-16 21:04:11
echo date('Y-m-d H:i:s',strtotime('+1month'));//当前时间戳+1月 2017-02-09 21:04:11
echo date('Y-m-d H:i:s',strtotime('+1year'));//当前时间戳+1年 2018-01-09 21:04:11
echo date('Y-m-d H:i:s',strtotime('+12year 12month 12day 12hour 12minute 12second'));//当前时间戳+12年,12月,12天,12小时,12分,12秒 2030-01-22 09:16:23
mktime — 取得一个日期的 Unix 时间戳
mktime(hour,minute,second,month,day,year)
参数可以从右向左省略,任何省略的参数会被设置成本地日期和时间的当前值
获取今日、昨日、上周、本月的起始时间戳和结束时间戳
<?php
//获取今日开始时间戳和结束时间戳
$start = mktime(0,0,0,date('m'),date('d'),date('Y'));
$end = mktime( 0,0,0,date('m'),date('d')+1,date('Y') ) -1;//明天开始的时间戳减一就是今天结束的时间戳
//获取昨日起始时间戳和结束时间戳
$beginYesterday = mktime(0,0,0,date('m'),date('d')-1,date('Y'));
$endYesterday = mktime(0,0,0,date('m'),date('d'),date('Y')) -1;
//获取上周起始时间戳和结束时间戳
$beginLastweek = mktime(0,0,0,date('m'),date('d')-date('w')+1-7,date('Y'));
$endLastweek = mktime(23,59,59,date('m'),date('d')-date('w')+7-7,date('Y'));
//获取本月起始时间戳和结束时间戳
$beginThismonth=mktime(0,0,0,date('m'),1,date('Y'));
$endThismonth=mktime(23,59,59,date('m'),date('t'),date('Y'));
microtime()返回当前unix时间戳和微秒数:
如果调用时不带可选参数,本函数以 “msec sec” 的格式返回一个字符串,其中 msec 是微秒部分, sec 是自 Unix 纪元(0:00:00 January 1, 1970 GMT)起到现在的秒数。字符串的两部分都是以秒为单位返回的。
<?php
echo microtime();//0.64255200 1524652129 返回值类型是string(21),前面是微妙,后面是秒。
//微妙部分,最后两位都是0,一秒等于1百万微妙,所以999999,99万9千9百99就是微妙能表示的最大值,所以微妙部分有效值只有6位。
echo microtime(true); // 1524652129.6426,返回值类型是float,是sec+msec的和,超过四位只保留四位小数
1秒(s)=1000毫秒(ms)
1 毫秒=1000 微秒
1秒(s)=1百万微秒(μs)
microtime()函数常用于性能分析
<?php
date_default_timezone_set("PRC");
$start = microtime(true);
for($i=0; $i<100000; $i++);//执行一段循环操作,模拟耗时
$end = microtime(true);
echo $end-$start;//0.0067892074584961
获取毫秒
microtime() 函数返回当前 Unix 时间戳的微秒数。
因为1秒(s)=1000毫秒(ms)= 1,000,000 微秒(μs),所以将秒乘以1000就是毫秒,但是microtime(true)保留了四位小数,需要扩大一万倍才会没有小数部分。可以扩大一千倍就是毫妙,然后使用使用四舍五入
$msectime= round(microtime(true)1000);
如果要将毫秒级存入数据库,需要将类型改为BIGINT类型
*获取微妙:
list($usec, $sec) = explode(" ", microtime());
$usec就是微妙部分,单位是秒,乘以100万倍后单位就是微秒
getdate — 取得日期/时间信息
date_default_timezone_set('PRC');
获取当前日期的详细信息
echo '<pre>';
print_r(getdate());
echo '</pre>';
// 格式如下:
/Array
(
[seconds] => 41 //秒
[minutes] => 33 //分
[hours] => 15 //小时
[mday] => 11 //号
[wday] => 4 //星期几
[mon] => 1 //月
[year] => 2018 //年
[yday] => 10 //一年中的某天
[weekday] => Thursday
[month] => January
[0] => 1515656021 //自 Unix 纪元以来经过的秒数
)/
// 获取特定日期的详细信息
echo '<pre>';
print_r(getdate(strtotime('2000-1-1 0:0:0')));
echo '</pre>';
die;
ip处理函数
使用编程语言的函数,将IP转换为整型,ip4 使用int类型就可以
ip2long 将 IPV4 的字符串互联网协议转换成长整型数字
long2ip 将长整型转化为字符串形式的互联网标准格式地址(IPV4)
用处:php中向数据库存储IP地址时可以使用 ip2long 函数将ip地址从字符串转换成整型,从而节省了在数据库的存储空间,取出时再用long2ip 即可还原IP地址:
因为PHP的 integer 类型是有符号,所有会导致许多的IP地址在32位系统下为负数, 你需要使用 “%u” 进行转换,通过 sprintf() 或 printf() 得到的字符串来表示无符号的IP地址。
<?php
$ip = '255.255.255.255';
$ip_long = ip2long($ip);
echo $ip_long.PHP_EOL; // 4294967295
echo long2ip($ip_long); // 255.255.255.255
255十进制转换为二进制是11111111,所以四个255就是 4组 8个1,共32个1,也就是32位系统下int无符号的最大值
但是PHPint是有符号的,所以可以把超出有符号int范围的用负数的int表示,PHP提供了转换函数:其实就是将每一项也就是255转换为2进制11111111,然后共32个1,将32个1转换为十进制就可以,最多是由10位组成。如果只是将每组的11111111转换成十进制,就是255255255255,是12位了。所以将每项转换为二进制,合并起来一起转换为整型是最节省空间的。
打印处理
echo 输出内容,不是一个函数,是语言结构,可以不加括号,echo可输出多个内容
echo 1,2,’hi’; // 1 2 hi
echo (1); //使用括号的话只能输出一个内容。
print — 输出字符串: 不是一个函数,是语言结构,可以不加括号,和 echo 最主要的区别: print 仅支持一个参数,并总是返回 1。
print_r — 打印关于变量的易于理解的信息。false不显示
var_dump — 打印变量的相关信息,var_dump还会输出类型和长度,false打印出false
var_export — 输出或返回一个变量的字符串表示,它和 var_dump() 类似,不同的是其返回的表示是合法的 PHP 代码。如var_dump返回的数组格式是便于理解数组,但是不是PHP的数组格式,而var_export返回的是PHP认识的数组格式,可以直接被PHP执行,所以是很为危险的函数。可用来生成PHP配置文件。
上面这三个函数的第二个参数设置为true的话,不会输出结果,会返回给一个变量。如将结果写入laravel日志中。
Log::info(var_export($data,true));
Log::info(print_r($data,true));
三个打印函数图解:
序列化和反序列化函数
serialize() — 产生一个可存储的值的表示,这有利于存储或传递 PHP 的值,同时不丢失其类型和结构。serialize() 可处理除了 resource 之外的任何类型。如可以将对象序列化之后存入session中
unserialize — 反序列化,恢复对象
当序列化对象时,PHP 将试图在序列动作之前调用该对象的成员函数 sleep()。这样就允许对象在被序列化之前做任何清除操作。类似的,当使用 unserialize() 恢复对象时, 将调用 wakeup() 成员函数。
字符串处理函数
字符串函数:http://php.net/manual/zh/ref.strings.php
获取字符串的长度
strlen()函数对于计算英文字符是非常的擅长,一个空格算一个长度,多个空格算多个长度,一个UTF8的中文字符是3个长度
<?php
$str = 'hello我';
$len = strlen($str);//8
// mb_strlen() 会将一个中文字符当作长度1来计算,英文任然为一个长度
echo mb_strlen($str);// 6
字符串的截取
1、英文字符串的截取函数substr()
substr(字符串变量,开始截取的位置从0开始,截取个数)
echo substr("Hello world",6);//world 多个连续空格只算一位
2、中文字符串的截取函数mb_substr()<br /> mb_substr(字符串变量,开始截取的位置,截取个数)<br /> <br />**查找字符串**<br /> 查找字符串在另一字符串中第一次出现的位置(区分大小写)<br /> strpos(要处理的字符串,要查找的字符, 规定开始搜索的位置[可选])<br /> <br />**替换字符串**
<?php
str_replace(要查找的字符串, 要替换的字符串, 被搜索的字符串, 替换进行计数[可选])
$str = 'I want to learn js';
$replace = str_replace('js', 'php', $str);
格式化字符串
printf() 函数把格式化的字符串写入变量并输出。
vprintf() 中的参数位于数组中
<?php
$str = "北京";
$number = 9;
printf("在%s有 %u 百万辆自行车。",$str,$number);//在北京有 9 百万辆自行车。
vprintf("在%s有 %u 百万辆自行车。",array($str,$number));
sprintf () 函数把格式化的字符串写入变量中,但不输出。
vsprintf() 中的参数位于数组中,
<?php
$str = "北京";
$number = 9;
$a = sprintf("在%s有 %u 百万辆自行车。",$str,$number);//不会直接输出,需要打印出来
echo $a;//在北京有 9 百万辆自行车。
例如:
<?php
$str = '99.9';
$result = sprintf('%01.2f', $str);
echo $result;//结果显示99.90
%01.2f 是什么意思呢?
1、% 符号是开始的意思,写在最前面表示指定格式开始了。 也就是 “起始字符”
2、跟在 % 符号后面的是 0, 是 “填空字元” ,表示如果位置空着就用0来填满。
3、在 0 后面的是1,这个 1 是规定整个所有的字符串占位要有1位以上(小数点也算一个占位)。
如果把 1 改成 6,则 $result的值将为 099.90
因为,在小数点后面必须是两位,99.90 一共5个占位,现在需要6个占位,所以用0来填满。
4、在 %01 后面的 .2 (点2) 就很好理解了,它的意思是,小数点后的数字必须占2位。 如果这时候,$str 的值为9.234,则 $result的值将为9.23.
为什么4 不见了呢? 因为在小数点后面,按照上面的规定,必须且仅能占2位。 可是 $str 的值中,小数点后面占了3位,所以,尾数4被去掉了,只剩下 23。
5、f 表示转换成浮点数,最后以 “转换字符” 结尾。
常见的转换字符有:
%s - 字符串
%f - 浮点数(本地设置)
%u - 不包含正负号的十进制数(大于等于 0)
字符串的合并与分割
explode() — 使用一个字符串分割另一个字符串,返回一维数组
$str = "www.runoob.com";
print_r (explode(".",$str));//Array([0] => www [1] => runoob [2] => com )
implode() — 将一维数组的值合并为字符串,别名 join() 函数;
$arr = array('Hello','World!','Beautiful','Day!');
echo implode(" ",$arr);//Hello World! Beautiful Day!
字符串的转义
php字符串转义函数 addslashes()
函数说明:用于对特殊字符加上转义字符,返回一个字符串
返回值:一个经过转义后的字符串
$str = "what's your name?";
echo addslashes($str);//输出:what\'s your name?
其余函数
strrev — 反转字符串(从右往左)
strstr — 查找字符串的首次出现,并返回字符串的剩余部分:
echo strstr("I love Shanghai!","love");//love Shanghai!
strtr() — 转换指定字符,适合替换单个或者多个不连贯的字符为指定的字符。在php7之前速度要比str_replace快4倍甚至十多倍。php7速度相近
echo strtr("Hello world!","el","-*");//H-**o wor*d!
echo strtr("Hello world!","Hello","wo");//wollo world!
preg_replace() 执行一个正则表达式的搜索和替换
substr_replace() 把字符串的一部分替换为另一个字符串。通过指定下标开始替换,下标从0开始
数组处理函数
数组函数:http://php.net/manual/zh/ref.array.php
<?Php
返回键或者值
array_keys() 返回包含数组中所有键名的一个新数组。
array_values() 返回一个包含给定数组中所有键值的数组,但不保留键名。
交集,差集
array_diff() 返回两个数组的差集数组。
array_intersect() 用于比较两个(或更多个)数组的键值,并返回交集。
合并
array_merge() 把一个或多个数组合并为一个数组。
增删,可以模拟:队列(通道)先进先出,栈(桶)先进后出
array_shift() 删除数组中第一个元素,并返回被删除后元素的值。
array_unshift() 数组头部添加值
array_pop() 删除数组中的最后一个元素。并返回被删除后元素的值。
array_push()在数组尾部添加一个或多个元素
排序
sort() 函对索引数组进行升序排序。
真题答疑
<?php
$var1 = 5;
$var2 = 10;
function foo(&$my_var)
{
global $var1;
$var1 += 2;
$var2 = 4;
$my_var += 3;
return $var2;
}
$my_var = 5;
$bar = 'foo';//可变变量,
echo $bar($my_var). "\n";// (可变函数)4,$var2是函数内的局部变量,作为返回值,所以为4
echo $my_var. "\n";//8,my_var作为引用参数传递给函数,函数内部对其修改+3,影响外部的值,所以为8
echo $var1;//7,函数内使用global申明的变量是全局变量,所以函数内对其修改影响外部的值(除了引用赋值),所以为7
echo $var2;//10,var2是函数内的局部变量,函数内修改不影响函数外部的值,仍为10