PHP 面试题目
一、PHP 基础语法(初级)
- PHP 中有哪些数据类型?请举例说明。
# 标量类型
int // 整形
string //字符串
float //浮点型
bool //布尔型
# 复合类型
array // 数组
object //对象
# 特殊类型
null // 空值
resource //资源,例如数据库连接等$handle = fopen("a.txt", "r")
# 伪类型
mixed // 可接收多重类型(int,string等)
number // 整形或浮点型
callback //可调用函数(匿名函数)
void // 无返回值 ==和===的区别是什么?== // 比较值
=== // 比较值和类型,比较严谨- 如何判断一个变量是否已定义且不为 null?
isset($var):会返回true,当变量存在且不为null,如果未定义,isset()也不会报错 - include、require、include_once、require_once 有何区别?
# 都是用于引入文件, 建议使用*_once
include // 警告,继续执行,会重复引入,脚本不会终止
require // 致命错误,中止执行,会重复引入
include_once // 警告,继续执行,只会引入一次
require_once // 致命错误,中止执行,引入一次 - isset() 和 empty() 的区别?
isset // 判断变量存在且不为空
empty // 判断变为0,null,空字符串 - PHP 中如何处理错误与异常?try-catch 和 set_error_handler 的使用场景?
set_error_handler // 处理非异常的传统运行时错误
try-catch // 捕获异常(如PDO异常) 继承自Throwable - 常见的字符串操作函数有哪些?如 substr, strpos 等。
# 查找与匹配
strpos // 查询子串首次出现的位置
strrpos // 查询子串最后一次出现的位置
stripos // 不区分大小写查找
# 截取
substr // 截取字符串一部分
str_split // 按字符拆分成数组
explode // 按分隔符拆分为数组
implode // 将数组合并为字符串
# 替换
str_replace // 替换指定内容
str_ireplace //不区分大小写替换
substr_replace //替换指定位置的内容
# 大小写转换
strtolower // 转小写
strtoupper // 转大写
ucfirst // 首字母大写
lcfirst // 首字母小写 - 如何获取当前脚本的文件路径?
# 当前文件的完整路径包括文件名
echo __FILE__
# 当前脚本所在的目录路径(不包含文件名)
echo __DIR__ - 变量和常量的定义方式及区别?
- 变量: $var = value, 可变,局部/全局可见
- 常量: const = value和define("name","value"), 不可变,全局可见
- 引号(单引号、双引号、heredoc、nowdoc)之间的区别?
- 单引号:
- 不会解析变量
- 除了\和',不会解析转移字符
- 双引号:
- 解析变量
- 常见字符串,如\n,\t,"
- heredoc类似双引号
- newdoc类似单引号
- 单引号:
- 什么是类型自动转换?什么时候会触发?
- 指当前运算或比较中涉及不同类型的值时,php自动将变量转换为适当的数据类型行为。
- 什么是可变函数?什么是匿名函数?
- 默认参数一定要在非默认参数之后
- 默认参数可以是表达式
- 避免默认值时可变数据类型
use在闭包中的作用? use 从外部作用域引入变量,使得闭包可以访问其他外部的变量值$outside = 'world';
$greet = function () use ($outside) {
return "Hello, $outside!";
};
echo $greet(); // 输出:Hello, world!
二、PHP 面向对象编程(初中级)
- public、protected、private 的作用及区别。
# 访问修饰符
public // 任何地方都可以访问(类外、子类、类内部)
protected // 类内部或者子类中访问
private // 私有,类自身内部访问 - 接口(interface)和抽象类(abstract class)的区别.
interface abstract 8.0以上可以实现default 可以实现包含方法 不能有属性 有属性,修饰符 无构造方法 可以有 多实现 单继承 都必须为public public/protected - 魔术方法
__construct()、__get()、__set()、__call()有何用途?__construct // 构造函数,创建对象时自动调用,初始化对象属性
__get // 访问未定义或不可访问属性时触发
__set // 未定义或不可访问属性赋值时触发
__call // 调用未定义的方法时触发 - 什么是依赖注入?如何实现?
用于将对象所依赖的其他对象(或资源)从外部传入,而不是在类内部创建依赖对象,提高解耦性、可测试性和可维护性 - PHP 如何实现多态?举一个接口或继承的例子
不同的类对相同的方法做出不同的响应
1、通过接口
2、通过继承,重写方法 - traits 是什么?解决了哪些问题?
由于类不能多继承,又希望多个类共享一组方法,类似模块引入,使用use ExampleTrait - 设计模式:
# Singleton数据库连接、缓存服务、日志管理。
class DB {
private static $instance;
private function __construct{}
public static function getInstance(){
if (!self::$instance){
self::$instance = new DB();
}
return self::$instance;
}
}
# Factory 工厂模式 据条件返回不同的对象,比如支付系统(支付宝/微信)
interface Payment {
public function pay($amount);
}
class Alipay implements Payment{
public function pay($amount){echo "Alipay: $amount";}
}
class PaymentFactory{
public static function create($type):Payment{
if ($type === 'alipay') return new Alipay();
}
}
# Adapter适配器模式,老接口适配新系统,比如不同邮件服务类统一成一个接口。
class OldMailer {
public function sendMail($to,$msg){echo "Sent to $to";}
}
class MailAdapter {
private $mailer;
public function __construct(OldMailer $mailer){
$this->mailer = $mailer;
}
public function send($recipient,$message){
$this->mailer->sendMail($recipient,$message)
}
}
# Decorator 装饰器模式,
✅ 作用:在不修改原类的前提下,动态扩展其功能。
✅ 场景:为日志、缓存、权限等功能增加“包裹层”。
class UserService {
public function login($user){
echo "User login\n";
}
}
class LogDecorator {
private $service;
public function __construct(UserService $service){
$this->service = $service
}
public function login($user){
echo "Log start\n";
$this->service->login($user);
echo "Log end\n";
}
}
# Observer观察者模式
✅ 作用:对象之间建立发布/订阅关系,一方变更,通知所有监听者。
✅ 场景:用户注册后发送邮件/短信通知。
interface Observer{
public function update($event);
}
class MailService implements Observer{
public function update($event){
echo "发送邮件:$event"
}
}
class EventManager{
private $observers = []
public function attach(Observer $observer){
this->observers[] = $observer
}
public function trigger($event){
foreach($this->observers as $obs){
$obs->update($event)
}
}
}
# Strategy(策略模式)
✅ 作用:封装多个算法,互相可以替换。
✅ 场景:计算折扣策略、支付策略、日志策略等。
interface DiscountStrategy{
public function getPrice($price)
}
class VipDiscount implements DiscountStrategy{
public function getPrice($price){return $price * 0.8}
}
class Cart{
public function checkout($price,DiscountStrategy $strategy){
return $strategy->getPrice($price);
}
}模式名 用于何时? 关键词 单例 只要一个对象 全局唯一 工厂 隐藏创建细节 创建对象 策略 运行时替换行为 多种算法 装饰器 增加功能 包裹增强 适配器 改接口名或结构 转接口 观察者 通知一批监听者 发布/订阅 责任链 多个处理器顺序尝试 依次处理 代理 控制访问(懒加载、安全) 代替访问
三、PHP 进阶与常用功能(中级)
- 使用 PDO 操作数据库的流程?如何防止 SQL 注入?
1.预处理语句和绑定参数,避免直接拼接字符串形成SQL
2.PDO会自动将参数转义,防止恶意注入
3.关闭PDO::ATT_EMULATE_PREPARES(模拟预处理)可以增强安全性。 - 文件上传的处理流程?如何限制类型和大小?
- 处理流程:
- 表单准备,使用form标签
- 用户选择文件并提交表单
- 服务器接收文件
- PHP通过$_FILES超全局变量获取上传信息
- $_FILE['file']['tmp_name']是文件的临时路径
- $_FILE['file']['name']是原始文件名
- 验证文件大小
- 检查上传错误码
- 检查文件大小
- 检查文件MIME类型或扩展名
- 移动文件
- 用 move_uploaded_file() 将临时文件移动到目标目录
- 返回结果
- 类型和大小限制
- 通过new fileinfo(FILEINFO_MIME_TYPE);
- 通过$_FILE['size']来判断大小
- 处理流程:
- 使用 curl 发送 POST 请求的方式?
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL,$url);
curl_setopt($ch, CURLOPT_POST, true); // 启动POST请求
# application/x-www-form-urlencoded使用
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data)); // 编码为query string
# 发送json使用
curl_setopt($ch, CURLOPT_HTPHEADER, ["Content-Type:application/json"])
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data)); // json格式
# 发送multipart/form-data使用
curl_setopt($ch, CURLOPT_POSTFIELDS, $data)
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);// 返回结果而不是输出
$response = curl_exec($ch);
curl_close($ch);
echo $response; - 如何设置和读取 cookie / session?
setcookie(name, value, expire, path, domain, secure, httponly);
params:
name: cookie 名称
value: 值
expire: 到期时间(时间戳)
path: 有效路径(/代表整个站点)
httponly: 方式javascript访问,提高安全性
#读取cookie
$_COOKIE['username']
# 启用session
session_start() // 必须写到最顶部
$_SESSION['user_id'] = 123
unset($_SESSION['user_id']) // 删除某个session值
session_destory() // 销毁session - Composer 是什么?如何创建自己的 Composer 包?
# 安装依赖包(根据composer.json)
composer install
# 添加新依赖包
composer require monolog/monolog
# 自动加载类
require 'vendor/autoload.php' - PHP 性能优化建议有哪些?如 opcache、output buffer?
- 启用OPcache
- 缓存php脚本的编译结果(字节码),避免每次请求都重复解析
# php.ini 中启用
opcache.enable=1
opcache.memory_consumption=128
opcache.interned_strings_buffer=8
opcache.max_accelerated_files=10000
opcache.validate_timestamps=1
- 缓存php脚本的编译结果(字节码),避免每次请求都重复解析
- 启用Output Buffering(输出缓冲)
- 避免频繁I/O写入,提高传输效率.
- 启用OPcache
- 如何读取 JSON 并将其转为 PHP 数组?
$json = '{"name": "Alice", "age": 25, "email": "alice@example.com"}';
$data = json_decode($json, true); // 第二个参数为 true 表示转为数组
print_r($data);
四、Laravel 核心知识(中高频)
- Laravel 的请求生命周期简述.
- 入口文件(public/index.php)
- 所有请求都首先经过(public/index.php),laravel应用入口.
- 它会加载自动加载器和启动laravel应用实例:
require __DIR__.'/../vendor/autoload.php';
$app = require_once __DIR__.'/../bootstrap/app.php';
- 生成内核(Kernel)
- Laravel 会创建一个HTTP Kernel实例(App\Http\Kernel),它定义了中间件和请求流程.
- 核心职责是处理请求并返回响应:
$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);
$response = $kernel->handle($request);
- 处理中间件
- 请求依次经过全局中间件和路由中间件
- 用户认证
- CSFR验证
- 请求日志记录
- 内容预处理等
- 请求依次经过全局中间件和路由中间件
- 路由分发
- 到达路由系统,根据URI和请求方法(GET/POST等)匹配定义的路由。
- laravel会调用对应的Controller或闭包函数来处理
- 控制器处理
- 执行据具体逻辑,例如:读取数据,调用服务类,返回视图等
- 生成响应
- 控制器会返回一个响应对象(Illuminate\Httpp\Response),可能是:
- 页面HTML
- JSON数据
- 文件下载
- 重定向等
- 控制器会返回一个响应对象(Illuminate\Httpp\Response),可能是:
- 发送响应给浏览器
$response->send() - 中止中间件执行
- Laravel 执行中间件的terminate()方法
Browser Request
↓
public/index.php (入口)
↓
Bootstrap Laravel
↓
HTTP Kernel (App\Http\Kernel)
↓
[全局中间件处理]
↓
路由匹配(RouteServiceProvider)
↓
[路由中间件处理]
↓
控制器/闭包执行
↓
生成响应 (Response)
↓
发送响应 (send)
↓
终止中间件(terminate) - 入口文件(public/index.php)
- Route::get() 和 Route::resource() 的区别及使用场景。
get() 定义一个响应GET方法的简单路由,比如:首页、详情页等
resource() 定义一组restful路由,资源管理,不使用可以用only限制 - 如何创建中间件?如何进行路由分组?
路由分组分为带中间件和带前缀
1. 使用命令artisan来创建
php artisan make:middleware checkAge
2. 在中间件中编写逻辑
3. 注册中间件
4. 路由中使用中间件# 带中间件
Route:middleware(['auth'])->group(function(){
Route::get('/', function(){
return view('dashboard')
})
})
# 带前缀
Route::prefix('admin')->group(function(){
Route::get('/user', [UserController:class, 'index']);
}) - 什么是服务容器?如何绑定并解析服务?
- 容器的作用:
- 自动解析类的依赖关系
- 将接口绑定到具体实现
- 实现控制反转(IOC)和依赖注入
- 场景:
- 控制器中自动注入依赖类
- 接口绑定到某个实现
- 绑定单例、闭包、类等
- 绑定解析
- 通常在AppServiceProvider中register()方法中内定义
- 自动注入解析
- 手动解析
- 容器的作用:
- Laravel 的依赖注入有哪几种方式?构造函数、服务提供者、闭包绑定。
- 构造函数注入
- 方法注入
- 服务提供者绑定,配置接口和实现绑定
- 闭包绑定:复杂实例化逻辑或参数传递实现。
- Eloquent ORM 的基本查询写法(where、orWhere、with、has)。
- where基本条件
$users = User::where('status', 'active')->get();select * from users where status ='active'; - orWhere 或条件查询
$users = User::where('status', 'active')->orWhere('is_admin', true)->get();SELECT * FROM users WHERE status = 'active' OR is_admin = true; - with 预加载关联模型
$users = User::with('posts')->get();-- 第一个查询:获取用户
SELECT * FROM users;
-- 第二个查询:获取这些用户的关联文章
SELECT * FROM posts WHERE user_id IN (1, 2, 3, ...);# 嵌套关联预加载
$users = User::with('posts.comments')->get();
$users = User::with(['posts'=>function($query){
$query->where('status', 'published');
}]) - has 过滤"有某个关联"的模型
$users = User::has('posts')->get();
# sql
SELECT * FROM users WHERE EXISTS (
SELECT * FROM posts WHERE posts.user_id = users.id
);
# 限制关联记录数量:
$users = User::has('posts', '>=', 5)->get();
- where基本条件
- Laravel 如何避免 N+1 查询问题?(with vs load)
- with 主动预加载,解决n+1
- load是临时操作,模型已经存在,相比较with是额外的sql。
- Laravel 的事件与监听器如何工作?
Event: 表示某个事件发生
Listener: 表示当前发生时执行的逻辑
1. 创建事件和监听器
php artisan make:event UserRegister
php artisan make:listener SendEmail --event=UserRegister
# 生成两个文件
app/Events/UserRegistered.php
app/Listeners/SendWelcomeEmail.php
2. 定义事件类
3. 定义监听器类
4. 注册事件和监听器 在 app/Providers/EventServiceProvider.php 中
5. 触发事件在控制器中 添加比如:event(new \App\Events\UserRegistered($user)); - 队列如何配置与使用?有哪些驱动?
- Laravel队列基础概念
- Job: 表示一个任务
- Queue: 任务排队通道,可按优先级区分不同队列
- Worker: 后台"工人"进程, 持续监听并处理队列任务.
- Queue Driver: 底层存储队列任务的系统(数据库、redis)
- 步骤:
- 选择队列驱动,在.env中配置QUEUE_CONNECTION=database
驱动 描述 sync 同步执行 database 存储在数据库 redis redis beanstalkd 高性能队列 sqs 亚马逊sqs null 丢弃所有队列任务(测试) - 创建Job表
php artisan queue:table
php artisan migrate - 创建一个Job
php artisan make:job SendEmail
class SendWelcomeEmail implements ShouldQueue
{
use Queueable;
protected $user;
public function __construct($user)
{
$this->user = $user;
}
public function handle()
{
Mail::to($this->user->email)->send(new WelcomeMail($this->user));
}
} - 分发Job到队列
# 在控制器或者服务中调用
dispatch(new \App\Jobs\SendWelcomeEmail($user)); - 启动队列监听Worker
php artisan queue:work
# 或使用自动重启的守护进程
php artisan queue:listen
- 选择队列驱动,在.env中配置QUEUE_CONNECTION=database
- Laravel队列基础概念
- Laravel 的缓存系统有哪些?如何使用 file/redis 缓存?
驱动 简介 file 缓存存储到文件系统 redis 支持高并发 database 数据库作为缓存存储 memcached 使用Memcached服务 array 临时缓存,仅限当前生命周期 dynamodb AWS DynamoDB 配置文件在:config/cache.php,默认驱动在 'default' => env('CACHE_DRIVER', 'file'),
六、安全与认证(通用 + 框架)
-
CSRF/XSS 是什么?Laravel/Yii2 如何防御?
XSS: 注入恶意脚本, 窃取Cookie或控制前端页面
Laravel: 使用 {{$variable}}转义
Yii2: Html::encode($content)
CSRF:
Laravel: @csrf / VerifyCsrfToken
Yii2: enableCsrfValidation = true -
Laravel 的 csrf_token 如何嵌入表单?
Blade模版在form标签内加一行
手动插入token使用csfr_token()函数手动添加。
所有POST、PUT、PATCH、DELETE都应该加 -
如何使用 bcrypt 或 password_hash 加密密码?
- password_hash 是原生php
- btypt laravel辅助函数,
- 推荐方法使用Hash::make('123456') laravel 推荐
-
Laravel 和 Yii2 如何实现 RBAC 权限控制?
- Laravel使用官方推荐包spatie/laravel-permission
- 安装扩展
composer require spatie/laravel-permission - 发布并迁移
php artisan vendor:publish --provider="Spatie\Permission\PermissionServiceProvider"
php artisan migrate - 在模型中引入Trait
- 分配角色权限
- 验证权限
- 中间件控制访问
- 安装扩展
- yii2自带完善的rbac
- 基于数据库的
- 配置组件authManager:
'components' => [
'authManager' => [
'class' => 'yii\rbac\DbManager',
],
], - 创建RBAC表结构
php yii migrate --migrationPath=@yii/rbac/migrations/ - 添加角色/权限并赋权
- 验证权限
- 配置组件authManager:
- 基于代码的RBAC 'authManager' => ['class' => 'yii\rbac\PhpManager']
- 基于数据库的
- Laravel使用官方推荐包spatie/laravel-permission
-
如何防止 SQL 注入(框架/原生)?
做法 说明 ✅ 输入验证与过滤 用 filter_var()、htmlspecialchars()等✅ 最小权限原则 数据库账号仅授予必要权限 ✅ 日志记录与监控 审查异常访问行为 ✅ 关闭错误信息输出 禁止生产环境显示 SQL 报错信息 技术 防注入推荐写法 原生 PHP PDO + Prepared Statements Laravel 使用 Eloquent / Query Builder,避免拼接 SQL Yii2 使用 ActiveRecord / 参数绑定的原生 SQL -
如何防止session被劫持
- 使用https
- 设置HttpOnly & Secure Cookie
- 绑定Session到IP/User-Agent
- 定期更新SessionID
七、数据库 & SQL 优化
-
编写 SQL:查询 orders 表中金额 >100 的 paid 状态订单总数。
select * from orders where amount>100 and status = 'paid' -
写一条 SQL:查询每个用户的订单总金额
select sum(amount) from orders group by userid; -
什么是索引?哪些字段适合建立索引?哪些不适合?
- 索引通常以B+树(默认的Innodb引擎),对某些字段进行排序
- 提高查询效率(SELECT)
- 提高JOIN查询性能
- 提高Where/Order By/Group By等操作
- 适合索引
- 经常做为查询条件WHERE的自带
- 经常用于排序Order By的字段
- 经常用于连接JOIN ON的字段
- 经常用于分组GROUP BY的字段
- 唯一性要求高的字段
- 外键字段,经常需要连表
- 不适合
- 频繁更新的字段
- 重复值比较多的字段
- 查询量较少的字段
- 大文本字段
- 数据量极少的表
- 索引通常以B+树(默认的Innodb引擎),对某些字段进行排序
-
GROUP BY 与 HAVING 的区别?
- GROUP BY是分组依据,结果集按一个或多个字段进行分组
- having 筛选分组后的结果
-
如何使用 EXPLAIN 分析 SQL 执行计划?
Explain select * orders from where user_id=101;字段 说明 id查询中执行顺序的标识 select_type查询类型,如 SIMPLE(简单查询)、PRIMARY、SUBQUERY 等 table正在访问的表名 type访问类型,性能指标(最好是 const、eq_ref,差的是ALL)possible_keys可能使用的索引 key实际使用的索引 key_len使用索引的长度 ref哪个列或常量被用于查找索引 rows估算需要扫描的行数 Extra额外信息,如是否使用文件排序、临时表等 -
Laravel 的事务处理方法?
- 使用 DB::transaction() 方法(推荐)
use Illuminate\Support\Facades\DB;
DB::transaction(function () {
// 这里的数据库操作将作为一个事务执行
DB::table('users')->update(['votes' => 1]);
DB::table('posts')->delete();
}); - 手动开启、提交和回滚事务
use Illuminate\Support\Facades\DB;
DB::beginTransaction();
try {
DB::table('users')->update(['votes' => 1]);
DB::table('posts')->delete();
DB::commit(); // 提交事务
} catch (\Exception $e) {
DB::rollBack(); // 发生异常回滚
throw $e;
}
- 使用 DB::transaction() 方法(推荐)
-
Yii2 中如何进行批量插入? 在 Yii2 中,批量插入(bulk insert)可以使用 yii\db\Command 的 batchInsert() 方法,效率比循环逐条插入更高,适合一次插入多条数据。
注意事项:
- batchInsert() 只做写操作,无法触发 AR 的事件和行为(如自动时间戳等),也不会自动验证模型数据
- 如果需要批量插入且触发事件,需循环调用 $model->save(),性能较低
- 适合大批量导入或初始化数据时使用
-
Yii2 事务如何处理
$transaction = Yii::$app->db->beginTransaction();
$transaction->commit();
八、部署与运维
- Laravel 项目的部署步骤(Nginx + PHP-FPM)
server {
listen 80;
server_name yourdomain.com; # 替换为你的域名或IP
root /var/www/laravel-app/public;
index index.php index.html;
access_log /var/log/nginx/laravel_access.log;
error_log /var/log/nginx/laravel_error.log;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.1-fpm.sock; # 根据 PHP 版本调整
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
location ~ /\.(?!well-known).* {
deny all;
}
} .env文件的作用?Laravel 如何区分开发/生产环境?- .env是一个环境配置文件,存放应用的环境变量
- 用于数据库配置、缓存驱动、邮件服务、API秘钥等敏感信息或环境相关配置
- 避免将敏感信息写到代码中,方便不同环境使用不同配置
- Laravel 启动时通过 vlucas/phpdotenv 读取 .env 并加载到 PHP 的环境变量中($_ENV、$_SERVER
- Laravel 通过 .env 文件中的 APP_ENV 变量区分环境
APP_ENV=local # 本地开发环境
APP_ENV=production # 生产环境 - 在代码中可以通过 app()->environment() 或 env('APP_ENV') 获取当前环境,做不同处理:
if (app()->environment('production')) {
// 生产环境逻辑
}
if (app()->environment(['local', 'testing'])) {
// 开发或测试环境逻辑
}
- Laravel 通过 .env 文件中的 APP_ENV 变量区分环境
- Laravel 中如何使用 config:cache、route:cache 等命令?
- php artisan config:cache
- 作用:将所有配置文件(config/*.php)合并并缓存成一个文件,避免每次请求都加载多个配置文件,提升性能。
- 生产环境部署时,一般都会执行此命令。
- php artisan route:cache
- 作用:将所有路由定义缓存为一个文件,提高路由注册速度。
- 使用场景:路由定义较多,且路由不会频繁修改时使用。适合生产环境。
- php artisan config:cache
- Laravel 的 storage 目录要赋予什么权限?
storage目录用于保存缓存文件、日志、session文件、编译后的视图等。
- 赋予写权限
# 给 storage 和 bootstrap/cache 目录赋予 755 权限(推荐)
chmod -R 755 storage bootstrap/cache
# 将 Web 服务器用户加入文件夹所属用户组
chown -R www-data:www-data storage bootstrap/cache
- 赋予写权限
- Yii2 如何部署?入口文件在哪?
-
上传项目
-
安装依赖
-
设置目录权限
-
配置nginx
-
设置数据库连接
-
初始化init
入口文件为web/index.php
-
- Laravel 队列如何守护运行?如何在服务器中执行 supervisor?
- linux 安装supervisor
- 配置laravel worker
- 如何排查 Laravel 项目的 500 错误?日志路径在哪里?
- 查看laravel错误日志
storage/logs/laravel.log - .env开启调试
APP_DEBUG=true
APP_ENV=local - 出现问题原因:
原因 描述 权限问题 storage/、bootstrap/cache/不可写.env 错误 配置语法错误或缺少密钥 缓存问题 运行 php artisan config:clear和route:clear清除缓存Composer 问题 未正确安装依赖或自动加载器异常 PHP 扩展缺失 缺少 mbstring、openssl等扩展数据库连接失败 .env中数据库配置错误
- 查看laravel错误日志
九、 PHP Swoole
-
Swoole 核心
核心能力 说明 🌐 异步网络通信 内建 TCP/UDP/HTTP/WebSocket 服务器,无需 Apache/Nginx 🧵 协程 类似 Go 的协程调度,可轻松处理 10w+ 并发请求 🪝 常驻内存 脚本常驻、性能飞跃、避免重复加载 📤 异步任务 支持任务下发、异步处理,适合重操作或 IO 密集型任务 💼 多进程 主进程 + Worker + Task + 自定义子进程 -
Swoole 电商场景
场景 Swoole 用法 说明 秒杀接口 高并发协程 + Redis 限流 + 内存排队 降低 DB 压力、实现“抢单”快速响应 订单处理 异步任务投递处理库存/支付回调 避免用户等待,后台慢慢处理 WebSocket 消息 实时推送订单状态、客服消息 消息系统:客服、物流、系统通知 接口聚合服务 多个服务间 HTTP 异步请求合并返回 比如结算页汇总商品/库存/运费 异步任务队列 比如注册送券、下单发邮件、日志投递 使用 TaskWorker 或自定义进程 接口网关 基于 Swoole 搭建 API 网关服务 集中限流/鉴权/转发,替代传统 Nginx 网关
综合性问题
-
Laravel 和 Yii2 在架构设计上的优劣对比?
方面 Laravel Yii2 模块支持 支持包管理(Composer 包),无模块系统但支持模块包 原生支持模块(Module)概念,方便大型应用拆分 扩展包生态 极其丰富,社区活跃,几乎所有功能都有成熟包 生态较 Laravel 小,但官方和社区扩展稳定,适合企业级开发 事件与中间件机制 事件驱动,支持事件监听与广播,中间件链灵活 事件驱动,行为系统灵活,中间件支持较晚加入,功能较 Laravel 简单 方面 Laravel Yii2 性能 性能一般,依赖许多组件,启动稍慢,适合中大型应用 性能优越,框架轻量,启动快,适合对性能要求较高的应用 缓存支持 多种缓存驱动,内置支持 Redis、Memcached 等 多缓存驱动,支持 Redis、Memcached,缓存机制高效 数据库查询 Eloquent 方便,复杂查询语法直观,但性能稍逊 Query Builder 性能优秀,支持复杂 SQL 优化,适合大数据量操作 -
你如何设计一个高并发接口?有哪些防护机制?
- 接口性能优化
- 轻量设计: 接口只返回必要数据,避免复杂业务逻辑和荣誉数据处理
- 异步处理:耗时操作(发送邮件、日志记录等)放到异步队列,快速返回
- 数据库优化:
- 避免全表扫描,合理使用索引。
- 避免N+1查询.
- 使用缓存减少压力
- 缓存机制
- 数据缓存热点数据
- 页面缓存或接口缓存,降低重复计算
- 架构设计
- 负载均衡
- 水平扩展: 多台服务器部署接口服务
- 限流与降级
- 限流策略
- 全局限流: 对整个接口的访问做限制,最大请求数
- 用户限流: 基于用户身份(IP, token)限流,防止恶意刷接口
- 漏桶/令牌桶算法: 常用的平滑限流算法
- 降级策略:
- 当系统压力过大时,主动拒绝低优先级请求或返回降级数据
- 预设降级接口,保证核心业务有限处理
- 限流策略
- 接口性能优化
-
描述一次线上事故处理流程,如何快速定位问题?
- 查看异常
- 紧急快速响应,确定事故等级
- 日志排查与问题定位确定定位角度
- 最近部署?
- 接口响应慢?数据库锁?
- 单一用户/全体用户
- 是否影响所有服务还是单个节点
- 问题修复与验证
- 通知与总结
-
如果让你重构一个大型遗留系统,你的步骤是什么?
- 采用分段式渐进式重构
- 重构的8个核心
- 业务与代码调研
- 建立测试
- 代码结构重构为可维护的层级结构
- 重构配置与依赖
- 数据库结构审查与优化
- 核心模块重构(优先顺序由简到复杂)
-
如何对 API 添加版本控制?
考虑使用路径版本控制
PHP WSL2 安装步骤
- 安装WSL2
# 安装wsl命令
wsl --install
# 查看可用发行版列表
wsl --list --online
# 安装发行版
wsl --install -d <DistroName>
# 更改安装的默认Linux分发版
wsl --install -d <Distribution Name>
# 列出Linux发行版
wsl -l -v
# 版本设置为 WSL 1 或 WSL 2 例如:wsl --set-version Ubuntu-20.04 2
wsl --set-default-version <Version#>
# 更新和升级软件包
sudo apt update && sudo apt upgrade - 安装PHP环境
#添加 ondrej/php PPA(提供多版本 PHP)
sudo apt install software-properties-common -y
sudo add-apt-repository ppa:ondrej/php
sudo apt update
# 安装php7.4和常用扩展
sudo apt install php7.4 php7.4-cli php7.4-mysql php7.4-curl php7.4-mbstring php7.4-xml php7.4-zip php7.4-redis php7.4-fpm -y
# 验证版本
php -v
# 验证fpm是否启动
sudo service php7.4-fpm status #output: /run/php/php7.4-fpm.sock
# 可选: 固定PHP版本
sudo update-alternatives --install /usr/bin/php php /usr/bin/php7.4 74
sudo update-alternatives --config php
# 补充建议(可选)
curl -sS https://getcomposer.org/installer | php
sudo mv composer.phar /usr/local/bin/composer
# 安装phpmyadmin
sudo apt install phpmyadmin - 安装Mysql环境
# 升级依赖包
sudo apt update
# 安装必要依赖
sudo apt install libaio1 libncurses5 -y
# 安装mysql5.7tar包
https://downloads.mysql.com/archives/get/p/23/file/mysql-server_5.7.42-1ubuntu18.04_amd64.deb-bundle.tar
# 解压包
tar -xvf mysql-server_5.7.42-1ubuntu18.04_amd64.deb-bundle.tar
# 安装包
sudo dpkg -i *.deb
# 强力修复并按照
sudo apt install -f -y
# 再次尝试安装
sudo dpkg -i *.deb
# 启动 MySQL 服务
sudo service mysql start - 安装Redis数据
# 安装默认版本
sudo apt install redis-server -y
sudo service redis-server start - 安装nginx服务
sudo apt update
# 安装nginx服务
sudo apt install nginx -y
# 启动服务
sudo service nginx startserver {
listen 8088;
server_name localhost;
root /home/<your_user>/yii2-app/web; # <-- 替换为 Yii2 项目 web 目录
index index.php index.html;
access_log /var/log/nginx/yii2_access.log;
error_log /var/log/nginx/yii2_error.log;
charset utf-8;
location / {
try_files $uri $uri/ /index.php?$args;
}
location ~ \.php$ {
include fastcgi_params;
fastcgi_pass unix:/run/php/php7.4-fpm.sock; # 或 127.0.0.1:9000
fastcgi_index index.php;
# 关键参数:告诉 PHP 要执行哪个脚本文件
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
# 禁止访问隐藏文件 (.git/.env 等)
location ~ /\.(?!well-known).* {
deny all;
}
# 禁止直接执行 /assets 下的 .php 文件
location ~ ^/assets/.*\.php$ {
deny all;
}
# 静态文件优化(缓存)
location ~* \.(jpg|jpeg|png|gif|css|js|ico|woff|woff2|ttf|svg|eot)$ {
expires 7d;
access_log off;
log_not_found off;
}
}
# nginx配置详解
# 表示匹配所有以.php结尾的URL. ~表示使用正则匹配,\.是转义的点,\.php$匹配字符串以.php结尾
location ~ \.php$ {
# 包含Nginx提供的一组标准FastCGI参数,是必须得信息比如:
# QUERY_STRING
# REQUEST_METHOD
# CONTENT_TYPE
# SCRIPT_NAME
include fastcgi_params;
# 告诉Ningx把PHP请求发送给谁处理,表示Unix套接字与PHP-FPM通信
fastcgi_pass unix:/run/php/php7.4-fpm.sock;
# 这行高度php-fpm要运行哪个php文件
# $doucment_root是上面配置的root路径
# $fastcgi_script_name 是当前请求的路径,比如/index.php
# 命令路径会传给PHP-FPM去执行这个文件
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}