面试问题之PHP

博客内容为PHP-Interview-QA读后笔记

变量

  • PHP中的八大数据类型:整型、字符串、浮点型、布尔、数组、对象、NULL、资源。
  • 字符串的定义:单引号、双引号、NowDoc及HereDoc。双引号包裹的字符串会被解析,单引号的不会。
  • 判断语句中被判定为false的情况:0、0.0、'0'、''、array()、null、false

引用

  • 引用计数、写时复制(对象除外)
  • unset不销毁内存空间、只是取消引用

echo、print、print_r、var_dump、printf、sprintf

echo、print属于语言结构,print_r、var_dump、printf、sprintf是普通函数

  • echo:输出一个或多个字符串(echo '123', '345', '567';)
  • print:输出字符串
  • print_r:可输出复杂数据类型Array、Object,布尔类型true输出为1,false输出为0
  • var_dump:可输出复杂数据类型Array、Object,布尔输出正常
  • printf:格式化输出
  • sprintf:格式化返回

isset、is_null、empty

  • isset:当变量为NULL、声明后未赋值或者变量未声明时,返回false
  • is_null:当变量为NULL、声明后未赋值或者变量未声明时,返回true(当变量未声明或未赋值时,PHP输出警告)
  • empty:当变量为空字符串、false、array()、null、"0"、0、0.0或变量未声明时,返回true

超全局变量

  • $GLOBALS: 包含以下8个
  • $_GET: 请求的GET参数
  • $_POST: 请求的POST参数
  • $_FILES: 提交的文件信息
  • $_REQUEST: 请求参数,包含GET及POST的参数
  • $_COOKIE: 请求中携带的cookie数据
  • $_SESSION: 服务器中的session数据
  • $_ENV: 服务器系统的环境变量
  • $_SERVER: 服务器脚本信息、客户端信息、请求信息

常量

  • const:语言结构,可以在类中定义常量,数据更快
  • define:函数

PHP中预定义的常量(魔术常量):DIRFILELINENAMESPACECLASSTRAITFUNCTIONMETHOD

static、self、$this、parent

  • static表示当前实例化的类,不能用于访问非静态属性
  • self表示当前语句所在的类,也不能用于访问非静态属性,
  • $this指的是实际实例化的对象,其不能访问静态属性和常量,且不能出现于静态方法中
  • parent指向父类,用于访问父类public的方法及静态常量

require、include、require_once、include_once

require引用文件出错时报致命错误,并终止脚本执行,include引用文件出错时报警告,但继续执行后续脚本。

require_once、include_once与require、include类似,但文件只会被包含一次。因为包含前存在判断,所以性能比require、include较差。

应尽量不使用include_once、require_once

常见的数组处理函数

  • array_count_values: 统计数组中各个value的个数
  • array_diff: 判断一个数组与其他数组的差异,返回在第一个数组中但不在其他数组中的元素,例如array_duff([1,2,3,4], [1,2], [4])返回[2 => 3]
  • array_key_exists: 判断key是否存在
  • in_array: 判断值是否存在于数组中
  • array_keys: 获取数组的所有键
  • array_values: 获取数组的所有值
  • array_merge: 合并数组,后者覆盖前者。使用“+”则是前者覆盖后者
  • sort: 数组排序
  • count: 数组长度,等同于sizeof
  • array_shift: 删除数组第一个元素,并返回
  • array_unshift: 在数组头部加入一个元素
  • array_pop: 删除数组末尾最后一个元素,并返回
  • array_push: 向数组末尾添加一个元素
  • array_reverse: 翻转数组
  • array_rand: 从数组中取随机值
  • array_flip: 交换数组的键值
  • array_search: 搜索数组的值,返回第一个匹配到的元素的键
  • array_sum: 计算数组所有值的和
  • array_slice: 根据开始位置和长度复制数组元素,返回复制的元素(即原数组不改变)
  • array_splice: 根据开始位置和长度剪切数组元素,并返回剪切的元素(即原数组中匹配到的元素被删除)
  • array_map: 对数组进行遍历,并对每个元素使用提供的回调函数

常见的字符串处理函数

  • chr: ASCII码转字符
  • ord: 字符转ASCII码
  • count_chars: 统计字符串中的字符数
  • str_word_count: 统计字符串中的单词数
  • explode: 按指定字符将字符串分解为数组
  • implode: 按指定字符将数组合并为字符串
  • htmlentities: 转义处理字符串中的HTML实体。html_entity_decode用于反向解码
  • htmlspecialchars: 转义处理字符串中的特殊字符,包括&、"、'、<、>。htmlspecialchars_decode用于反向解码
  • trim: 去除字符串两端的空格,ltrim、rtrim分别用于去除左边界的空格和右边界的空格
  • str_len: 计算字符串长度
  • strrev: 翻转字符串
  • substr: 根据起始位置和长度获取子字符串,返回获取的子字符串
  • preg_replace: 正则表达式替换
  • preg_match: 正则表达式匹配
  • strstr: 判断字符串2是否为字符串1的子字符串,如果是,返回匹配到的开始位置到父字符串末尾的字符串,如果不是,返回null
  • strrchr: 与strstr一样,只是该函数从父字符串的末尾开始匹配
  • strcmp: 字符串比较函数,如果字符串1>字符串2,返回1;如果字符串1<字符串2,返回-1;如果字符串1等于字符串2,返回0;如果两个字符串部分内容不一致,返回两个字符串中第一个不同的字符的差值
  • str_replace: 搜索并替换字符串的内容

多台服务器session共享的解决方案

  • 将session数据存储到MySQL数据库中
  • 将session数据存储到Redis、MemCache这些内存型数据库中
  • 使用NFS共享session文件
  • 使用cookie保存session信息

魔术方法

  • __construct()
  • __destruct()
  • __call(): 当调用不存在的方法时
  • __callStatic(): 当调用不存在的惊天方法时
  • __get(): 获取不存在的属性时
  • __set(): 写入不存在的属性时
  • __sleep(): 当对象被序列化时
  • __wakeup(): 反序列化时
  • __toString(): 对象作为字符串使用时
  • __invoke(): 当对象被作为函数调用时
  • __isset(): 当使用isset函数探测对象的私有属性时
  • __unset(): 当使用unset函数删除对象的私有属性时
  • __clone(): 当对象被克隆

public、protected、private、final

  • public可被外部访问,protected与private不可被外部访问
  • public、protected可被子类继承,private不可被继承
  • final描述的方法,不可被子类重写;final描述的类,不可被继承

SERVER_ADDR、REMOTE_ADDR、HTTP_X_FORWARDED_FOR、HTTP_CLIENT_IP

  • SERVER_ADDR获取服务器IP
  • REMOTE_ADDR获取客户端IP,但当用户使用代理时,获取到的就是代理服务器的IP
  • HTTP_X_FORWARDED_FOR、HTTP_CLIENT_IP可以获取到用户真实IP及使用的各个代理的IP,但该字段可能被伪造

当后端架构中有使用到代理服务器,可以使用HTTP_X_FORWARDED_FOR、HTTP_CLIENT_IP来进行获取用户IP。如果后端服务器直接暴露给用户,只需要使用REMOTE_ADDR获取客户端IP即可。

php.ini配置

配置项 默认值 说明
short_open_tag On 是否开启缩写模式
precision 14 浮点数显示有效数字的位数
disable_functions 禁用的函数
disable_classes 禁用的类
expose_php On 是否暴露PHP被安装在服务器上
max_execution_time 30 最大执行时间
memory_limit 128M 每个脚本执行时内存限制
error_reporting E_ALL & ~E_DEPRECATED & ~E_STRICT 设置错误报告级别
display_errors On 显示错误
log_errors On 设置是否将错误日志记录到error_log
error_log PHP错误日志文件
upload_max_filesize 2M 上传文件的最大限制
post_max_size 8M 设置POST最大数据限制

php-fpm.conf配置信息

配置项 默认 备注
pid pid文件
error_log 错误日志的位置
log_level notice 错误级别(alert: 必须立即处理;error: 错误信息;warning: 警告信息;notice: 一般重要信息;debug: 调试信息)
daemonize yes 设置FPM后台运行
listen FPM监听的地址及端口
request_slowlog_timeout 0 慢日志记录阈值
slowlog 慢日志记录文件

进程通讯方式

  • 消息队列
  • socket
  • 信号量
  • 共享内存
  • 管道

发起HTTP请求的方式

  • curl: curl_init -> curl_setopt -> curl_exec -> curl_close
  • file_get_contents: http_build_query -> stream_context_create -> file_get_content(url, false, context)
  • fopen: fopen -> fgets -> fclose
  • fsockopen: fsocketopen -> fputs -> fgets -> fclose

生成器yield的基本原理

当一个生成器被调用的时候,它返回一个可以被遍历的对象。
当你遍历这个对象的时候 (例如通过一个 foreach 循环),PHP 将会在每次需要值的时候调用生成器函数,并在产生一个值之后保存生成器的状态,这样它就可以在需要产生下一个值的时候恢复调用状态。

一旦不再需要产生更多的值,生成器函数可以简单退出,而调用生成器的代码还可以继续执行,就像一个数组已经被遍历完了。

此外,yield还可以接受值:

function printer()
{
    while (true) {
        printf("receive: %s\n", yield);
    }
}

$printer = printer();

$printer->send('hello');
$printer->send('world');

PHP的生成器还实现了一个send方法,该方法向yield处传递一个值,同时从 yied 语句处继续执行,直至再次遇到 yield 后控制权回到外部。

Generator生成器提供的方法有:

  • current: 获取生成器当前的值
  • getReturn: 获取生成器返回的值
  • key: 获取生成器当前的key
  • next: 继续执行生成器
  • rewind: 重置生成器
  • send: 发送值给生成器
  • valid: 验证生成器是否关闭
  • throw: 抛出一个生成器异常

防止内存泄露的解决方法

  • 过大的数据分段读取
  • 尽可能减少静态变量的使用,可以考虑使用引用代替
  • 数据库、文件操作完后,及时释放连接
  • 对象、变量使用完后,及时unset释放
  • unset()函数只能在变量值占用内存空间超过256字节时才会释放内存空间
  • 当指向该变量的所有变量(如引用变量)都被销毁后,才会释放内存

模板引擎的原理

  • 通过视图名找到相应的视图文件
  • 通过正则表达式或其它方式将模板引擎的定界符及其他符号修改为PHP的定界符和符号
  • 将转化好的文件内容存为缓存文件,并以视图文件的上一次修改时间作为缓存文件的命名依据,保证视图文件更新后,缓存文件也进行更新

反射类

  • ReflectionClass
  • ReflectionFunction
  • ReflectionMethod
  • ReflectionObject
  • ReflectionGenerator
  • ...

正则表达式

  • \d: 数字
  • \D: 非数字
  • \w: 字母、数字、下划线
  • \W: 非字母、数字、下划线
  • \s: 空白符,空格、制表、换行
  • \S: 非空白符
  • .: 除换行外的任意字符
  • *: 0~n个
  • ?: 0或1个
  • +: 1~n个
  • {n}: n个
  • {n,}: 大于等于n个
  • {n,m}: n~m个
  • ^: 开始
  • $: 结束
  • []: 匹配中括号中任意一个
  • (): 整体或后文引用

修正符:
i: 区分大小写
S: .包括换行
m: 每一行分别进行匹配
U: 取消贪婪模式,另一种方法就是在量词后面加问号,如*?
u: utf8中文匹配

CGI、FastCGI、PHP-FPM

  • CGI: 通用网关接口,是Web服务器与服务端语言交互的标准接口。在一个请求到来时,服务器创建一个CGI进程用来执行程序,等待程序执行完成,该CGI进程被杀死。

  • FastCGI: 是用于提升CGI性能的标准,FastCGI要求CGI进程在处理完请求后不被立刻杀死,而是保留下来,等待处理下一次请求。

  • PHP-FPM: FastCGI Processor Manager,实现FastCGI的管理器,使用FastCGI标准管理CGI程序

PHP操作MySQL数据库

mysqli面向过程

// 使用prepare(推荐)
$conn = mysqli_connect('127.0.0.1', 'root', '', 'test');
mysqli_set_charset($conn, 'utf8');
$stmt = mysqli_prepare($conn, 'SELECT `name`, `phone` FROM `aihailin` LIMIT ?');
mysqli_stmt_bind_param($stmt, 'i', $limit);
$limit = 2;
mysqli_execute($stmt);
$result = mysqli_stmt_get_result($stmt);
foreach ($result as $row) {
  var_dump($row);
}
// 或不使用prepare
$conn = mysqli_connect('127.0.0.1', 'root', '', 'test');
mysqli_set_charset($conn, 'utf8');
$res = mysqli_query($conn, 'SELECT * FROM `aihailin` LIMIT 2');
foreach ($res as $row) {
  var_dump($row);
}

mysqli面向对象

$mysqli = new mysqli('127.0.0.1', 'root', '', 'test');
$mysqli->set_charset('utf8');
$stmt = $mysqli->prepare('SELECT `name`, `phone` FROM `aihailin` LIMIT ?');
$stmt->bind_param('i', $limit);
$limit = 2;
$stmt->execute();
$result = $stmt->get_result();
foreach ($result as $row) {
  var_dump($row);
}

PDO

$dsn = 'mysql:host=127.0.0.1;dbname=test;charset=utf8';
$pdo = new PDO($dsn, 'root', '');
$stmt = $pdo->prepare('SELECT * FROM `aihailin` LIMIT :limit');
$stmt->bindParam(':limit', $limit);
$limit = 5;
if ($stmt->execute()) {
    $result = $stmt->fetchAll(PDO::FETCH_ASSOC);
    var_dump($result);
}