PHP 基础知识

释放双眼,带上耳机,听听看~!

本篇概要:

    1. 引用变量;
    1. 常量及数据类型;
    1. 运算符;
    1. 流程控制;
    1. 自定义函数及内部函数 ;
    1. 正则表达式;
    1. 文件及目录处理;
    1. 会话控制技术;
    1. 面向对象;
    1. 网络协议;
    1. 开发环境及配置。

1. 引用变量;

问题:什么是引用变量?在 PHP 中,用什么符号定义引用变量?

PHP 的引用变量的概念及定义方式
概念:在 PHP 中引用意味着用不同的名字访问同一个变量内容
定义方式:使用 & 符号

  • 延伸:PHP 引用变量的工作原理


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
68
69
70
71
72
73
1<?php
2// 场景 1:
3// 定义一个变量
4// 内存开辟一块空间,$a 指向这个内存空间
5$a = range(0, 1000);
6var_dump(memory_get_usage());  // 查看内存空间
7// 定义一个变量 b,将 a 变量的值赋值给 b
8// 内存里不开辟空间,$b 指向 $a 指向的内存空间
9// COW 机制:Copy On Write,表示有修改操作,才会复制内存
10$b = $a;
11var_dump(memory_get_usage());
12// 对 $a 进行修改
13// 虽然值没有发生改变,但是对它进行写操作,会开辟新内存空间
14$a = range(0, 1000);
15// 此时内存使用会翻倍
16var_dump(memory_get_usage());
17
18// 场景 2:
19$a = range(0, 1000);
20var_dump(memory_get_usage());
21// 使用了“&”引用,表示 $a 和 $b 永远会指向同一块内存空间,而不会去复制,所以没有 COW 机制
22$b = &$a;
23var_dump(memory_get_usage());
24$a = range(0, 1000);
25// 此时内存使用不会有太大的变化
26var_dump(memory_get_usage());
27
28// 关于 zval 结构体
29// 在 PHP 中,变量都是通过 zend 引擎处理,zend 引擎里有一个 zval 结构体
30// 这个 zval 结构体就是变量容器,负责去管理变量的一些内容
31// 场景 3:
32$a = range(0, 3);
33xdebug_debug_zval('a');     // a:refcount=1 表示 1 个变量指向这个内存空间, is_ref=0 表示引用 0
34// 定义变量 $b,把 $a 的值赋值给 $b
35$b = $a;
36xdebug_debug_zval('a');     // a:refcount=2 表示 2 个变量指向这个内存空间, is_ref=0
37// 修改 $a,$a 执行了写操作,不再指向之前的空间,复制了一块空间出来
38$a = range(0, 3);
39xdebug_debug_zval('a');     // a:refcount=1, is_ref=0
40
41// 场景 4:
42$a = range(0, 3);
43xdebug_debug_zval('a');     // a:refcount=1, is_ref=0
44$b = &$a;
45xdebug_debug_zval('a');     // a:refcount=2, is_ref=1
46$a = range(0, 3);
47xdebug_debug_zval('a');     // a:refcount=2, is_ref=1
48
49// 场景 5:
50// 关于 unset(),只会取消引用,不会销毁内存空间
51$a = 1;
52$b = &$a;
53unset($b);            // 取消 $b 引用
54echo $a . "\n";     // 输出 1
55
56// 场景 6:
57// PHP 里对象本身就是引用传值,不需要加引用符号,它也是引用
58class Person{
59  public $name = "zhangsan";
60}
61$p1 = new Person();
62xdebug_debug_zval('p1');        // p1:refcount=1, is_ref=0
63$p2 = $p1;    // 指向同一个内存空间
64xdebug_debug_zval('p1');        // p1:refcount=2, is_ref=0
65$p2->name = "lisi";             
66xdebug_debug_zval('p1');        // p1:refcount=2, is_ref=0
67// 按道理应该执行 COW 机制复制出一块空间才对,
68// 但是 p1 的内存空间还是 2,p1 和 p2 还是指向一个空间,
69// 同时 p1->name 也被改成“lisi”,说明 p1 和 p2 现在改的都是同一个空间
70// 对象有对象的特殊性,本身不会进行空间的复制
71// 如果要复制,要用到 clone
72
73
  • 同类问题


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
1// 程序执行时,每一次循环后变量 $data 的值是什么?
2// 程序执行完成后,$data 的值是什么?
3<?php
4$data = ['a', 'b', 'c'];
5foreach($data as $key => $val){
6   $val = &$data[$key];
7   var_dump($data);
8}
9var_dump($data);
10// 思路,第 1 次循环:
11// $key = 0; $val = 'a'; $val = &$data[0] = 'a'; 结果:abc
12// 第 2 次循环:$key = 1; $val = 'b'; => $data[0] = 'b'; $val = &$data[1] = 'b'; 结果:bbc
13// 第 3 次循环:$key = 2; $val = 'c'; => $data[1] = 'c'; $val = &$data[2] = 'c'; 结果:bcc
14
15// array(3) { [0]=> &string(1) "a" [1]=> string(1) "b" [2]=> string(1) "c" }
16// array(3) { [0]=> string(1) "b" [1]=> &string(1) "b" [2]=> string(1) "c" }
17// array(3) { [0]=> string(1) "b" [1]=> string(1) "c" [2]=> &string(1) "c" }
18// array(3) { [0]=> string(1) "b" [1]=> string(1) "c" [2]=> string(1) "c" }
19
20

2. 常量及数据类型;

问题:PHP 中字符串可以使用哪三种定义方法以及各自的区别是什么?

涉及 PHP 的字符串的定义方式以及各自区别
定义方式:单引号、双引号、heredoc 和 newdoc (定界符)
单引号区别:
‐ 单引号不解析变量
‐ 单引号不能解析转义字符,只能解析单引号和反斜线本身
‐ 变量和变量、变量和字符串、字符串和字符串之间可以用“.”连接
单引号效率更高
双引号区别:
‐ 双引号可以解析变量,变量可以使用特殊字符和 {} 包含
‐ 双引号可以解析所有转义字符
‐ 也可以使用“.”连接
heredoc 和 newdoc 区别:
‐ heredoc 类似于双引号
‐ newdoc 类似于单引号
‐ 两者都是用来处理大文本


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
1$str = 'a b c d e f $a g';
2$str = "a b c d e f '{$a}' g h";   // “{}”不会返回,返回 a b c d e f '$a的值' g h
3$str = "a b c d e f '$a' g h";     // 同上
4$str = "a b c d e f & $a & g h"; // “&&”会返回,返回 a b c d e f & $a的值 & g h
5
6$sql = "SELECT * FROM user WHERE name = '$name'";
7// 最外层双引号,里面单引号包含了一个变量,变量原样输出,就是单引号变量的值
8// 但是双引号效率低,建议如下写法
9$sql = 'SELECT * FROM user WHERE name = \''.$name.'\'';
10
11// 关于 heredoc 和 newdoc
12// 定义一个字符串,非常长,使用 heredoc
13$str = <<< EoT
14.
15. // 字符串内容
16.
17EoT   // 开头和结尾保持一致
18
19// newdoc,类似单引号,里面的内容不会被解析
20$str = <<< 'EoT'
21.
22.
23.
24EoT  
25
26

延伸:数据类型以及常量
三大数据类型(标量,复合,特殊)
标量里有浮点、整型、字符串、布尔
复合是数组和对象
特殊数据类型:NULL 和 resource(资源)


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
1// 浮点类型不能运用到比较运算中(不能运用到精确相等判断里)
2$a = 0.1;
3$b = 0.7;
4if($a + $b == 0.8){
5   // 这里不会进入,因为是不相等的
6   // 因为 0.1 和 0.7 在计算的时候是交给 CPU 计算
7   // CPU 在计算的时候只能把它转化为二进制,在二进制的时候就会有一定的损耗
8   // 所以0.1 + 0.7 最终结果是 0.79999999
9}
10
11// 布尔类型需要知道 false 的七种情况(什么时候判断为 false)
12// 整型0,浮点0.0,空字符串,0字符串,布尔false,空数组、NULL
130 , 0.0 , '' , '0' , false , array() , NULL
14
15// 数组类型:超全局数组
16// $GLOBALS,$_GET,$_POST,$_REQUEST,$_SESSION,$_COOKIE,$_SERVER,$_FILES,$_ENV
17// $GLOBALS 包含后面所有的内容
18// $_REQUEST 包含 $_GET,$_POST,$_COOKIE
19// $_REQUEST 少用,因为是万能钥匙所以不安全
20// 重点:$_SERVER
21$_SERVER['SERVER_ADDR']     // 服务器端 ip 地址
22$_SERVER['SERVER_NAME']     // 服务器名称
23$_SERVER['REQUEST_TIME']    // 请求时间    
24$_SERVER['QUERY_STRING']    // 请求地址“?”后面的字符串,有可能为空
25$_SERVER['HTTP_REFERER']    // 上级请求页面,可能为空
26$_SERVER['HTTP_USER_AGENT'] // 返回头信息里的 USER_AGENT 信息
27$_SERVER['REMOTE_ADDR']     // 客户端 ip 地址
28$_SERVER['REQUEST_URI']     // 访问 index.php 则为 /index.php
29$_SERVER['PATH_INFO']       // 处理路由,框架里一些路由的功能,抓 url 地址路径的部分,去掉域名和"?"后面的部分,比如:/user/login
30
31// NULL
32// 三种情况:直接赋值为 NULL、未定义的变量、unset() 销毁的变量
33
34// 常量
35// 定义的方式:const、define()
36// const 更快,是语言结构,define() 是函数
37// define 不能用于类常量的定义,const 可以
38// 常量一经定义,不能被修改,不能被删除
39
40// 预定义常量
41// __FILE__,__LINE__,__DIR__,__FUNCTION__,__CLASS__,__TRAIT__,__METHOD__,__NAMESPACE__
42__FILE__      // 文件的路径名和文件的名称
43__LINE__      // 所在行的行号
44__DIR__       // 所在目录
45__FUNCTION__  // 所在的函数体的函数名称
46__CLASS__         // 类名
47__TRAIT__         // trait 的名称
48__METHOD__        // 类名加方法名
49__NAMESPACE__     // 返回命名空间名称
50
51

3. 运算符;

问题:foo() 和 @foo() 之间的区别

PHP 的运算符的错误控制符 @
PHP支持一个错误控制符:@。当将其放置在一个 PHP 表达式之前,该表达式可能产生的任何错误信息都会被忽略掉。

  • 延伸:PHP 所有运算符

运算符的优先级
官方文档:https://www.php.net/manual/zh/language.operators.precedence.php
递增 / 递减 > ! > 算术运算符 > 大小比较 > (不)相等比较 > 引用 > 位运算符(^) > 位运算符(|) > 逻辑与 > 逻辑或 > 三目 > 赋值 > and > xor > or
括号的适应可以增加代码可读性,推荐使用
比较运算符
“==” 和 “===” 的区别
等值判断(FALSE)的七种情况
递增 / 递减运算符
递增 / 递减运算符不影响布尔值
递减 NULL 值没有效果,递增 NULL 值为 1
递增和递减在前就先运算后返回,反之就先返回后运算
逻辑运算符
短路作用
“||” 和 “&&”与 “or” 和 “and” 的优先级不同

4. 流程控制;

5. 自定义函数及内部函数 ;

6. 正则表达式;

7. 文件及目录处理;

8. 会话控制技术;

简述 cookie 和 session 的区别及各自的工作机制,储存位置等,简述 cookie 的优缺点

  • PHP 的会话控制技术

为什么要使用会话控制技术?
WEB 是通过 HTTP 协议来实现的,而 HTTP 协议又是无状态的协议。也就是说 HTTP 协议没有内建机制来维护两个事物之间的状态。所以同一个用户在请求相同界面两次的时候,HTTP 协议不会认为这两次请求都来自同一个用户,会把它当成是两次独立请求隔离开,认为是不同的两个人来请求的。如果用户执行了登录操作,再次请求页面,HTTP 协议不会认为该用户之前做过登录,因为它没办法保持该用户之前的登录状态,没办法在不同页面之间进行用户的跟踪和用户状态的保持。
对于用户会话控制技术来说就是为了解决这样一个问题。就是允许服务器跟踪同一个客户端做出的连续请求。这样就可以保持用户的状态,从而完成登录状态的一个保持
会话控制技术实现的方式
通过 GET 参数传递:但是信息不是特别安全,其次会参数丢失,不建议使用
Cookie:它是一种由服务器发送给客户端的片段信息,存储在客户端浏览器的内存或者是硬盘当中的技术。就是存储在客户端(用户浏览器)里的一个文件,这个文件包含了客户端的一些片段信息。
‐ 比如用户去超市购买完东西办理了会员卡。超市就是服务器,用户就是客户端,会员卡就是 Cookie。Cookie 存储了用户的基本信息。基本信息就是用户的状态信息。用户登录就会把登录状态保存在 Cookie 文件里。
Cookie 的操作:见下
Cookie 的优点和缺点:
‐ 将信息存储在客户端,不会占用服务器资源,效率高。
‐ 缺点也是将信息存储在客户端,所以不建议将一些敏感的信息保存在 Cookie 中。
‐ 而且用户有权限禁止浏览器 Cookie 的使用,一旦用户禁止,就无法保存用户信息。
Session:是将使用者的资料存储在服务器中。这样用户无法禁用 Session 的使用。但是 Session 并不是完全脱离 Cookie 的,而是基于 Cookie 的。
比如说用户还是去超市。用户也办了会员卡,但是会员卡是一张电子卡,每次要说明会员的身份证号,就可以给该会员进行打折优惠。这里超市还是服务器,用户还是客户端,但是对于会员卡来说,因为没有实质的会员卡,拿到的是一个电子卡,会员卡信息依然是用户状态的信息,但是会员卡信息是存储在服务器的,与 Cookie 不同的是,电子卡是由服务器端进行保存的,是由超市进行保存的。用户每次过来的时候只需要报身份证号,这个身份证号就是 SessionID,这个 SessionID 是存储在 Cookie 里的,如果 Cookie 被禁用,可以通过 URL 来传递 SessionID,来保存一个 Session 的状态。所以 Session 是基于 Cookie 的。Session 是存在服务器端的一个文件里。每当用户想要读取 Session 内容的时候,首先他会拿浏览器中携带的 SessionID 的 Cookie 文件,根据 SessionID 找到对应的 Session 文件,然后讲内容读取出来。这就是 Session 的工作原理。
Session 的操作:见下
Session 的优点和缺点:
‐ 信息非常安全,都是存储在服务器端,客户端拿不到信息
‐ 缺点就是消耗服务器资源
传递 SessionID 的问题:Session 是基于 Cookie 的,Cookie 里面存储了 SessionID,如果把浏览器 Cookie 禁用,那 SessionID 没有办法传递了。那怎么办?session_name() 和 session_id() 去进行传递,见下。
Session 存储:在服务器配置集群的情况下,建议不要以文件的形式存储 Session,把 Session 存储到内存服务器里。
‐ session_set_save_handler(),存 MySQL,Memcached,Redis 等。


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
1// cookie 的操作:
2//创建 cookie
3setcookie($name, $value, $expire, $path,  $domain, $secure);
4setcookie('a[b]', 'val');  // 存数组
5$_COOKIE                   // 读取 cookie(只读)
6setcookie($name, '', time()-1000);   // 删除 cookie
7
8// Session 的操作
9session_start();   // 创建 Session
10$_SESSION;            // 操作 Session
11$_SESSION = [];       // 清空 Session
12session_destroy();    // 删除 Session 以及对应的 SessionID 的 Cookie
13
14// 关于 Session 的配置信息(php.ini 中)
15session.auto_start        // 是否自动开启 session_start();
16session.cookie_domain // 存储 SessionID 的 Cookie 的有效域名
17session.cookie_lifetime
18session.cookie_path
19session.name          // 默认是 PHPSESSID,可以改
20session.save_path     // Session 在服务器中存储的路径
21session.use_cookies       // 是否用 Cookie 传递 SessionID
22session.use_trans_sid
23// Session 垃圾回收机制,三者配合使用
24// 如果用户直接关闭浏览器不退出,Session 会永久保存在服务器里
25// 这个时候就会配置一些垃圾回收,把 Session 文件定时清理掉
26// 每 100 次调用 session_start() 的时候,会有 1 次去清理文件
27// 什么样的文件?就是当前时间的时间戳减去最后文件修改的时间超过 1440 秒的,
28// 说明这个文件已经过期了,要清楚掉
29// 如果 session.gc_divisor=1,就是每次 session_start() 都会清理过期文件
30// 不建议把 session.gc_divisor 配置的特别小,会消耗服务器资源
31session.gc_probability=1
32session.gc_divisor=100
33session.gc_maxlifetime=1440   // Session 最大生命周期,超过清理
34
35session.save_handler  // Session 存储的句柄(也可以存储到 Memcached,Redis 等)
36
37// 传递 SessionID 的问题
38// session_name() 和 session_id()
39// <a href="1.php?PHPSESSID=sessionID的值">下一个页面</a>
40// sessionID的值就是文件名称:sess_[sessionID的值]
41<a href="1.php?<?php echo session_name().'='.session_id(); ?>">下一个页面</a>
42// session_name() 就是配置里 session.name 的值,默认的 PHPSESSID
43// session_id() 就是登录以后生成的新的 Session 文件的 ID 的值
44
45<a href="1.php?<?php echo SID; ?>">下一个页面</a>
46// SID 就是 session_name() 和 session_id() 的拼接
47// 但是 SID 有一个特性,就是如果开启了 Cookie,SID 就是空;如果 Cookie 禁用才会有值,很智能。
48
49

9. 面向对象;

问题:写出 PHP 类权限控制修饰符

  • PHP 类权限控制修饰符(参考 访问类型的控制)

public、protected、private
public:修饰的成员有最高权限,可以在类的内部使用、外部使用、子类中使用
protected:类的内部使用、子类中使用(被继承)、不可以在类的外部使用
private:权限最小,只能在类的内部使用,不能被继承、不允许在外部使用

  • 延伸 1:面向对象的封装、继承和多态

成员访问的权限
public、protected、private 分别在类的内部,子类,类的外部的访问权限
单一继承
PHP 是单一继承,同时只能继承一个类。
如果有接口(interface)的话,可以继承一个类,继承一个接口。也可以实现多个接口。
以上参考 抽象类和接口
方法重写
父类中定义一个方法,子类继承父类的时候,如果方法名称相同会覆盖掉
如果不想覆盖的话,可以延伸(parent::父类)
PHP 基础知识
面向对象的多态
抽象类的定义:在类的前面使用 abstract。
如果类当中有抽象方法,类必须定义为抽象类
如果类当中没有抽象方法,类也可以定义为抽象类
接口的定义:里面的方法都是抽象的,没有方法体(大括号)
定义好方法,等着后面来实现。(参考:抽象类和接口)

  • 延伸 2:魔术方法

  • __construct()、__destruct()、__call()、__construct()、__callStatic()、__get()、__set()、__isset()、__sleep()、__wakeup()、__toString()、__clone()

  • 延伸 3:设计模式

  • 常见设计模式:工厂模式、单例模式、注册树模式、适配器模式、观察者模式和策略模式

10. 网络协议;

问题:HTTP/1.1 中,状态码 200、301、304、403、404、500 的含义

HTTP 状态码
HTTP 状态码负责客户端 HTTP 请求的返回结果,标记服务器端的处理是否正常,通知出现的错误。状态码的职责是当客户端向服务器端发送请求的时候,描述返回的一个请求结果。借助状态码,用户可以知道服务器端是否正确的处理了请求。
状态码的五类响应:1XX、2XX、3XX、4XX、5XX
1XX 类的是信息类状态码,它主要接收请求,表示接受请求正在处理。
2XX 类的表示 Success,是成功类状态码。它代表的意思是请求正常处理完毕。
3XX 类的是重定向,redirection。它代表的意思是需要进行附加操作以完成请求。
4XX 类的是 client error,客户端错误,服务器无法完成请求。
5XX 类的是 server error,服务端错误,服务器处理请求出错。

  • 延伸 1:OSI 七层模型
  • 延伸 2:HTTP 协议的工作特点和工作原理
  • 延伸 3:HTTP 协议常见请求 / 响应头和请求方法
  • 延伸 4:HTTPS 协议的工作原理
  • 延伸 5:常见的网络协议和端口

11. 开发环境及配置。

给TA打赏
共{{data.count}}人
人已打赏
安全技术

C++迭代器

2022-1-11 12:36:11

安全技术

使用Rust从零写操作系统 (1) —— 独立式可执行程序

2022-1-12 12:36:11

个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索