PHP
基础
PHP 的生命周期/启动流程
完整的生命周期为
- 模块初始化
- 请求初始化
- 请求处理
- 请求关闭
- 模块关闭。
- cli 模式下,每个脚本都会完整的执行上面的五大阶段;
- 对于 fastcgi 模式而言,只在启动时会执行模块初始化,之后的请求都走了请求初始化、处理请求、请求关闭三大阶段,在 fastcgi 关闭时执行模块关闭阶段。各个扩展的加载也是在模块初始化阶段完成的。
PHP 8+新特性
- 联合类型:允许变量、参数或返回值声明多种可能类型(如string|int)。
- 命名参数:调用函数时可指定参数名,提升代码可读性。
- 属性(Attributes):替代PHPDoc注释的元数据声明方式。
- match表达式:更强大的switch替代方案,可直接返回值。
- nullsafe运算符:?->在链式调用中遇到null时停止执行而不报错。
- 构造函数属性提升:简化类属性声明和构造函数赋值。
- JIT编译器:显著提升计算密集型任务性能。
Opcache设置问题
- 缓存不更新:开发环境未关闭opcache.validate_timestamps,导致代码更改不生效。
- 内存不足:opcache.memory_consumption设置过小,导致缓存频繁失效。
- 脚本限制:opcache.max_accelerated_files不足,导致部分文件未被缓存。
- 权限问题:Opcache缓存目录不可写。
- 符号链接问题:使用符号链接时路径解析错误。
- 预加载冲突:opcache.preload配置错误,导致类加载问题。
- 与Xdebug冲突:同时启用可能导致性能下降或调试问题。
PHP 的(内存)垃圾回收机制
每一个变量对应一个 zval 数据结构,在该结构内还有一个 val 结构体,该结构体内有一个引用计数(php7 而言,对于 php5,这个引用计数是保存在 zval 结构中的),标识该对象的引用数,当对象的引用计数为 0 时代表这个对象可被回收。
对象的 refcount 减少的时机: 修改变量、函数返回(释放局部变量)、unset 变量
对于数组和对象而言,可能存在变量中的成员引用变量本身的情况,也就是循环引用,这样会造成这个变量永远不会被内存回收,而成为垃圾。
PHP 里对于这种情况给出了垃圾回收机制 如果数组、对象的引用计数减少而且不为零,则认为他们可能是垃圾,把他们放到垃圾收集器里。
等垃圾收集器到了一定的数量之后,进行垃圾处理:对所有可能的垃圾 refcount 减 1,如果为 1,说明是垃圾,则进行内存回收;如果不为 1,说明还有其他变量在使用,refcount 重新加 1;
这种对象复用以及垃圾回收机制在其他语言中也有体现:redis 中也使用了引用计数表示每个对象的引用数量。
如果我的网站用的utf-8编码,为防止乱码出现,都需要注意哪些地方?
从以下几个方面考虑:
- 数据库中库和表都用utf8编码php连接mysql,指定数据库编码为utf8 mysql_query("set names utf8");
- php文件指定头部编码为utf-8 header("content-type:text/html;charset=utf-8");
- 网站下所有文件的编码为utf8,html文件指定编码为utf-8;
什么是单点入口呢?
所谓单点入口就是整个应用程序只有一个入口,所有的实现都通过这个入口来转发。比如Thinkphp使用index.php作为程序的单点入口,当然这个是可以由你自己任意控制的。
作用:
- 定义框架路径、项目路径和项目名称(可选)
- 定义调试模式和运行模式的相关常量(可选)
- 载入框架入口文件(必须)
单点入口有几大好处:
- 一些系统全局处理的变量,类,方法都可以在这里进行处理。比如说你要对数据进行初步的过滤,你要模拟session处理,你要定义一些全局变量,甚至你要注册一些对象或者变量到注册器里面。
- 程序的架构更加清晰明了。
PHP 的控制反转
一种设计思想,用于降低代码之间的耦合度,增强代码的灵活性和可维护性。将组件间的依赖关系从程序内部提到外部来管理。在PHP中,控制反转通常通过依赖注入(Dependency Injection,DI)来实现。
- 构造器注入
- Setter方法注入
- 接口注入(最建议)
合并数组
功能 | array_merge | array_merge_recursive | array_combine | + |
---|---|---|---|---|
合并方式 | 合并数组,覆盖重复键名 | 递归合并数组,合并重复键名的值 | 组合两个数组,第一个数组的值作为键名 | 合并数组,保留第一个数组的重复键名 |
适用场景 | 一般数组合并 | 多维数组合并 | 创建键值对数组 | 保留第一个数组的键值 |
键名重复处理 | 覆盖 | 合并为数组 | 无重复键名,否则报错 | 保留第一个数组的值 |
常用的魔术方法有哪些?
这些方法在特定的情况下会被自动调用,从而提供一些强大的功能,例如重载、序列化、自动加载等。
- __construct() 实例化类时自动调用。
- __destruct() 类对象使用结束时自动调用。
- __set() 在给未定义的属性赋值的时候调用。
- __get() 调用未定义的属性时候调用。
- __isset() 使用isset()或empty()函数时候会调用。
- __unset() 使用unset()时候会调用。
- __sleep() 使用serialize序列化时候调用。
- __wakeup() 使用unserialize反序列化的时候调用。
- __call() 调用一个不存在的方法的时候调用。
- __callStatic()调用一个不存在的静态方法调用。
- __toString() 把对象转换成字符串的时候会调用。比如 echo。
- __invoke() 当尝试把对象当方法调用时调用。
- __set_state() 当使用var_export()函数时候调用。接受一个数组参数。
- __clone() 当使用clone复制一个对象时候调用。
$this和self、parent这三个关键词
分别代表什么?在哪些场合下使用?
- $this 当前对象:$this在当前类中使用,使用->调用属性和方法。
- self 当前类:self也在当前类中使用,不过需要使用::调用。
- parent 当前类的父类:parent在类中使用。
常量
类中如何定义常量、如何类中调用常量、如何在类外调用常量。
类中的常量也就是成员常量,常量就是不会改变的量,是一个恒值。 定义常量使用关键字const.例如:const PI = 3.1415326; 无论是类内还是类外,常量的访问和变量是不一样的,常量不需要实例化对象,访问常量的格式都是类名加作用域操作符号(双冒号)来调用。即:类名 :: 类常量名;
__autoload()方法的工作原理是什么?
使用这个魔术函数的基本条件是类文件的文件名要和类的名字保持一致。
- 当程序执行到实例化某个类的时候,如果在实例化前没有引入这个类文件,那么就自动执行__autoload()函数。
- 这个函数会根据实例化的类的名称来查找这个类文件的路径,当判断这个类文件路径下确实存在这个类文件后就执行include或者require来载入该类,然后程序继续执行,如果这个路径下不存在该文件时就提示错误。
使用自动载入的魔术函数可以不必要写很多个include或者require函数。
trait 和 interface
trait 特质是一种代码复用机制,允许在类中插入一段预定义的代码块。特质可以包含方法和属性。
trait Greeting {
public function sayHello() {
echo "Hello!\n";
}
}
class User {
use Greeting;
}
$user = new User();
$user->sayHello(); // 输出:Hello!
interface 接口中的方法不能有具体实现,只能定义方法的签名(方法名、参数和返回值类型)。
interface Animal {
public function makeSound(): string;
}
class Dog implements Animal {
public function makeSound(): string {
return "Bark";
}
}
class Cat implements Animal {
public function makeSound(): string {
return "Meow";
}
}
PHP超全局变量的列表
- $_GET:用于获取通过URL参数传递的GET请求数据。
- $_POST:用于获取通过表单提交的POST请求数据。
- $_REQUEST:包含通过GET、POST或COOKIE传递的请求数据。
- $_COOKIE:用于获取客户端发送的Cookie数据。
- $_SESSION:用于存储会话数据,这些数据在用户访问期间会持续存在。
- $_SERVER:包含服务器环境信息,例如请求方法、脚本路径、客户端IP等。
- $_ENV:用于访问环境变量。
- $_FILES:用于处理文件上传。
- $GLOBALS:包含当前脚本中所有全局变量的数组。
PHP如何实现页面跳转
使用header()函数 通过header()函数发送HTTP头信息,实现页面跳转。
header("Location: https://www.example.com");
exit; // 跳转后停止脚本执行
使用HTML的<meta>标签 在HTML中使用<meta>标签实现页面跳转。
echo '<meta http-equiv="refresh" content="0;url=https://www.example.com">';
防止脚本攻击函数
htmlspecialchars 主要用于处理用户输入或动态生成的HTML内容,防止XSS攻击。
htmlentities 适用于需要将整个字符串完全转换为HTML实体的场景,例如显示用户输入的HTML代码。
is_writeable()函数存在Bug
无法准确判断一个目录/文件是否可写,请写一个函数来判断目录/文件是否绝对可写
在windows中,当文件只有只读属性时,is_writeable()函数才返回false,当返回true时,该文件不一定是可写的。 如果是目录,在目录中新建文件并通过打开文件来判断; 如果是文件,可以通过打开文件(fopen),来测试文件是否可写。
在Unix中,当php配置文件中开启safe_mode时(safe_mode=on),is_writeable()同样不可用。 读取配置文件是否safe_mode是否开启。
创建Composer包
- 初始化项目:
mkdir my-package && cd my-package
,然后运行composer init。 - 配置composer.json:指定name、description、type、license、autoload等信息。
- 设置命名空间和自动加载:如"psr-4":
{"MyVendor\\MyPackage\\": "src/"}
。 - 编写包代码:在src/目录下创建PHP类,遵循PSR-4标准。
- 添加测试:使用PHPUnit或其他测试框架编写测试用例。
- 发布到Packagist:将代码推送到GitHub/GitLab等平台,在Packagist.org提交仓库URL,设置GitHub服务钩子自动更新。
- 版本控制:使用语义化版本控制(SemVer),通过git tag标记版本。
- 文档编写:添加README.md,说明包用途、安装方法和使用示例。