PHP面试题

758 阅读11分钟

其他问题

a=[0,1,2,3];a=[0,1,2,3]; b=[1,2,3,4,5]; a+=a+=b;$a值是多少?


Array

(

[0] => 0

[1] => 1

[2] => 2

[3] => 3

[4] => 5

)

a数组的值对a数组的值对b进行覆盖和合并

HTTP 状态中302、403、 500代码含义?

302: 重定向

403: 禁止访问

500: 内部错误

echo、print_r、print、var_dump区别

echo 和 print 的区别

共同点

echo 和 print都不是严格意义上的函数,它们都是语言结构。只能输出字符串,整型和浮点型数据

不能打印复合型和资源型数据

区别点

echo 可以连续输出多个变量,而print只能一次输出一个变量

print打印的值能直接复制给一个变量,如 $a = print “123”

在使用时,echo()函数比print()速度稍快

var_dump()和print_r()的区别

共同点

两者都可以打印数组,对象之类的复合型变量

区别

print_r()只能打印一些易于理解的信息,且print_r()在打印数组时,会把数组的指针移到最后边,使用reset()可让指针回到开始处

而var_dump()不但能打印复合类型的数据,还能打印资源类型的变量,且var_dump() 输出的信息比较详细

语句include和require的区别是什么?重复包含同一个文件分别会有什么提示?

include 和 require

对 include()语句来说,在执行文件时每次都要进行读取

而对于reqire()来说,文件只处理一次(实际上,文件内容替换require()语句)

报错

include 引入文件的时候,如果碰到错误,会给出提示,并继续运行下边的代码

require 引入文件的时候,如果碰到错误,会给出提示,并停止运行下边的代码

参考资料

PHP7 与 PHP5 的区别

区别

  • 性能提升:PHP7比PHP5.0性能提升了两倍

  • 以前的许多致命错误,现在改成抛出异常

  • PHP 7.0比PHP5.0移除了一些老的不在支持的SAPI(服务器端应用编程端口)和扩展

  • PHP 7.0比PHP5.0新增了空接合操作符

  • PHP 7.0比PHP5.0新增加了结合比较运算符

  • PHP 7.0比PHP5.0新增加了函数的返回类型声明

  • PHP 7.0比PHP5.0新增加了标量类型声明

  • PHP 7.0比PHP5.0新增加匿名类

  • 错误处理和64位支持

  • 声明返回类型

  • PHP 7允许程序员根据期望的返回值声明函数的返回类型

参考资料

php中字符串处理函数列举5个,简述用途

str_split: 将字符串转换为数组

substr: 字符串截取

substr_replace: 字符串截取替换

strstr: 查找字符串首次出现

strpos: 查找字符串首次出现的位置

列举下使用过的框架,及其优缺点

Laravel

优点

Laravel 是一个现代化的PHP开发框架,代码优雅,使用 composer 方式扩展功能,社区活跃,

且功能强大

缺点

缺点是比较重,比较适合做后台管理或者应用型WEB系统

而且,内部代码常使用匿名回调,什么难以追踪和调试代码

swoft

优点

首个基于 Swoole 原生协程的新时代 PHP 高性能协程全栈框架

各种服务齐全

缺点

要会网络编程和最新的设计模式

有两个数组[1,2,5,11,32,15,77]和[99,32,15,5,1,77]两个数组,写段程序找出它们共同都拥有的数

题解


<?php

$a = [1 , 2, 5, 11, 32, 15, 77];

$b = [99, 32, 15, 5, 1, 77];

$c = [];

$tmpA = 0;

$tmpB = 0;

$aLen = count($a);

// O(n)

foreach ($a as $v) {

$c[$v] = 0;

}

// O(n)

foreach($b as $v) {

if (isset($c[$v])) {

$c[$v]++;

}

}

// O(n)

foreach($c as $key => $v) {

if ($v == 0) {

unset($c[$key]);

}

}

// 时间复杂度: O(n), 空间复杂度:O(n)

print_r($c);

参考资料

写条语句从user 表随机调取 1 条数据?

select * from user order by rand() limit 1;

设计一个商品分类表,写出sql语句查询某个分类的所有上级?

表结构和数据

0102c33323679150e701f7486ce45fdd.png@w=300

展示某个分类的所有上级

select s.type_id,s.type_name, p.type_name as parent_name from tdb_goods_types as s join tdb_goods_types as p on s.type_id = p.parent_id;

参考资料

写出一个类,包含面向对象三大特征和至少三个魔术方法?


<?php

class BasePay

{

protected $total;

};

interface PlayMode

{

public function incrMoney(int $num);

};

class AliPay extends BasePay implements PlayMode

{

public function __construct()

{

$this->total = 0;

}

public function incrMoney(int $num): void

{

$this->total += $num;

}

public function __set(string $key, int $value)

{

if (isset($this->$key)) {

$this->$key = $value;

}

}

public function __get(string $key): int

{

if (isset($this->$key)) {

return $this->$key;

} else {

return 0;

}

}

};

$alipy = new AliPay();

$alipy->incrMoney(10);

echo $alipy->total . PHP_EOL;

$alipy->total = 100;

echo $alipy->total . PHP_EOL;

php中数组处理函数列举5个,简述用途

array_chunk: 把一个数据拆分成多个

array_shift: 把数组开头的单元移出数组

array_pop: 弹出数组最后一个单元(出栈)

array_keys: 返回数组中部分的或所有的键名

array_values: 返回含所有值的索引数组

array_diff: 计算数组的差集

array_map: 为数组的每个元素应用回调函数

array_merge: 合并一个或多个数组

array_search: 在数组中搜索给定的值,如果成功则返回首个相应的键名

array_unique: 移除数组中重复的值

array_push: 将一个或多个单元压入数组的末尾(入栈)

SQL语言数据定义(DDL)语句中有哪些操作关键字?

create、alter、drop、truncate、comment, rename

php7有哪些特点?描述下Trait的继承优先级?

自身方法 > Trait 方法 > 父类方法

php缓存可以使用哪些软件,各自特点是什么?

多线程:memcache支持多线程,Redis支持单线程

持久化:Redis支持持久化,memcache不支持持久化

分布式:Redis做主从结构,memcache服务器需要通过hash一致化来支撑主从结构

如何排查和优化查询比较慢的sql语句?

  1. 通过explain 可以查看一条SQL使用哪些索引和有什么使用临时表

| EXPLAIN | 输出字段 |

| --- | --- |

| id | 执行编号,标志select所属的行 |

| select_type | 显示本行是简单或复杂select |

| table | 访问引用哪个表 |

| type |数据访问/读取操作类型 |

| possible_keys |解释哪一些索引可能有利用高效的查询 |

| key | 显示mysql决定采用哪个索引来优化查询 |

| key_len | 显示mysql在索引里使用的字节数 |

| ref | 为了之前的表在key列记录的索引中查找值所用的列或常量 |

| rows | 为了找到所需的行而需要读取的行数,估算值,不精确 |

| Extra | 额外信息,如using index、filesort 等 |

  1. 如果语句执行的时候被卡住,通过show processlist,查看语句是否在等MDL锁

参考资料

mysql数据库索引有哪些?什么情况下不适合建立索引?

主键索引、二级索引。就算自己不建索引,系统也会创建一个row_id

如果创建的InnoDB表没有指定主键,那么InnoDB会给你创建一个不可见的,长度为6个字节的row_id

InnoDB 维护了一个全局的dict_sys.row_id值,所有无主键的InnoDB表,每插入一行数据,都将当前的dict_sys.row_id值作为要插入数据的row_id,然后把dict_sys.row_id的值加1

是否用过mysql分库分表?使用了哪种策略, 如何解决增表,减表问题

垂直分库

垂直分库就是根据业务耦合性,将关联度低的不同表存储在不同的数据库

做法与大系统拆分为多个小系统类似,按业务分类进行独立划分

与"微服务治理"的做法相似,每个微服务使用单独的一个数据库

垂直分表

垂直分表是基于数据库中的"列"进行,某个表字段较多,可以新建一张扩展表,将不经常用或字段长度较大的字段拆分出去到扩展表中

在字段很多的情况下(例如一个大表有100多个字段),通过"大表拆小表",更便于开发与维护,也能避免跨页问题,MySQL底层是通过数据页存储的,一条记录占用空间过大会导致跨页,造成额外的性能开销

水平分表

当一个应用难以再细粒度的垂直切分,或切分后数据量行数巨大,存在单库读写、存储性能瓶颈,这时候就需要进行水平切分了。

水平切分分为库内分表和分库分表,是根据表内数据内在的逻辑关系,将同一个表按不同的条件分散到多个数据库或多个表中,每个表中只包含一部分数据,从而使得单个表的数据量变小

库内分表只解决了单一表数据量过大的问题,但没有将表分布到不同机器的库上,因此对于减轻MySQL数据库的压力来说,帮助不是很大,大家还是竞争同一个物理机的CPU、内存、网络IO,最好通过分库分表来解决

水平切分后同一张表会出现在多个数据库/表中,每个库/表的内容不同

以字段为依据,按照一定策略(hash、range等),将一个表中的数据拆分到多个表中

水平分表 - 范围法

以用户中心的业务主键uid为划分依据,将数据水平切分到两个数据库实例上去

  • user-db1:存储0到1千万的uid数据

  • user-db2:存储1到2千万的uid数据

范围法的优点是

  • 切分策略简单,根据uid,按照范围,user- center很快能够定位到数据在哪个库上

  • 扩容简单,如果容量不够,只要增加user-db3即可

缺点

  • uid必须要满足递增的特性

  • 数据量不均,新增的user-db3,在初期的数据会比较少

  • 请求量不均,一般来说,新注册的用户活跃度会比较高,故user-db2往往会比user-db1负载要高,导致服务器利用率不平衡

水平分表 - 哈希法

也是以用户中心的业务主键uid为划分依据,将数据水平切分到两个数据库实例上去

  • user-db1:存储uid取模得1的uid数据

  • user-db2:存储uid取模得0的uid数据

哈希法的优点是

  • 切分策略简单,根据uid,按照hash,user-center很快能够定位到数据在哪个库上

  • 数据量均衡,只要uid是均匀的,数据在各个库上的分布一定是均衡的

  • 请求量均衡,只要uid是均匀的,负载在各个库上的分布一定是均衡的

哈希法的不足是

  • 扩容麻烦,如果容量不够,要增加一个库,重新hash可能会导致数据迁移

水平分表 - 基因法

image.png

  • 在用户注册时,设计函数login_name生成3bit基因,login_name_gene=f(login_name),如上图粉色部分

  • 同时,生成61bit的全局唯一id,作为用户的标识,如上图绿色部分

  • 接着把3bit的login_name_gene也作为uid的一部分,如上图屎黄色部分

  • 生成64bit的uid,由id和login_name_gene拼装而成,并按照uid分库插入数据

  • 用login_name来访问时,先通过函数由login_name再次复原3bit基因,login_name_gene=f(login_name),通过login_name_gene%8直接定位到库

参考资料

现需要实现高可用高性能架构,列出你需要用到的软件及其作用?

正向代理和反向代理的区别

虽然正向代理服务器和反向代理服务器所处的位置都是客户端和真实服务器之间,所做的事情也都是把客户端的请求转发给服务器,再把服务器的响应转发给客户端,但是二者之间还是有一定的差异的

  1. 正向代理其实是客户端的代理, 帮助客户端访问其无法访问的服务器资源。反向代理则是服务器的代理, 帮助服务器做负载均衡,安全防护等。

  2. 正向代理一般是客户端架设的, 比如在自己的机器上安装一个代理软件。 反向代理一般是服务器架设的, 比如在自己的机器集群中部署一个反向代理服务器

  3. 正向代理中,服务器不知道真正的客户端到底是谁, 以为访问自己的就是真实的客户端. 反向代理中,客户端不知道真正的服务器是谁, 以为自己访问的就是真实的服务器

  4. 正向代理和反向代理的作用和目的不同. 正向代理主要是用来解决访问限制问题, 而反向代理则是提供负载均衡、安全防护等作用。二者均能提高访问速度

反向代理层

反向代理层的水平扩展,是通过"DNS轮询"实现的:dns-server对于一个域名配置了多个解析IP,每次DNS解析请求来访问dns-server,会轮询返回这些IP

image.png

当nginx成为瓶颈的时候,只要增加服务器数量,新政nginx服务器的部署,增加一个外网IP,就能扩展反向代理层的性能,做到理论上的无限高并发

站点的水平扩展

通过nginx 实现,修改nginx.conf,可以设置多个web后端


upstream backend {

server backend1.example.com;

server backend2.example.com;

server backend3.example.com;

}

当web后端成为瓶颈的时候,只要增加服务器数量,新增web服务的部署,在nginx配置中配置新的后端,就能扩展站点层的性能

Nginx支持多种负载均衡算法

  • 轮询(Round Robin):Nginx将请求依次分配给每个后端服务器,实现请求的平均分配。缺点是当某台服务器响应变慢或者宕机时,仍然会接收到请求

  • IP Hash:Nginx根据客户端IP地址的Hash值,将请求分配给后端服务器。这种算法可以使得同一客户端的请求总是发送到同一台服务器上,保证会话的一致性

  • 最少连接(Least Connections):Nginx将请求分配给连接数最少的后端服务器,实现负载均衡的动态调整。这种算法适合处理长连接请求

  • 加权轮询(Weighted Round Robin):Nginx根据后端服务器的权重值,将请求分配给不同权重的服务器。权重值越高的服务器接收到的请求越多,可以实现负载均衡的动态调整。

  • 加权最少连接(Weighted Least Connections):Nginx根据后端服务器的权重值和连接数,将请求分配给连接数最少且权重值最高的服务器。可以实现负载均衡的动态调整和负载均衡的均衡分配。

数据缓存层

redis 主从,哨兵集群、分片集群,memcached,一致性hash算法

数据层

mysql 主从,读写分离

mysql router,主要用以解决MySQL主从库集群的高可用、负载均衡、易扩展等问题

参考资料

事务的其特性有哪些?mysql是否支持嵌套事务?

事务的特性

A (Atomicity) 原子性

  • 一个事务中的操作,要么全部完成,要么全部不完成

C (Consistency) 一致性

  • 事务开始之前和事务结束后,数据库的完整性没有被破坏

I (Isolation) 隔离性

  • 数据库允许多个并发事务同时对数据进行读写和修改的能力

D (Durability) 持久性

  • 事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失

隔离级别

读未提交:一个事务还没提交时,它做的变更就能被别的事务看到

读提交:一个事务提交之后,它做的变更才会被其他事务看到

可重复读:一个事务执行过程中看到的数据,总是跟这个事务在启动时看到的数据是一致的

串行化: “写”会加“写锁”,“读”会加“读锁”

隔离性弱可能出现的问题

脏读:读到其他事务未提交的数据

不可重复读:前后读取的记录内容不一致

幻读:前后读取的记录数量不一致

mysql是否支持嵌套事务?

所以 MySQL 本身是不支持事务嵌套的

但 MySQL 也给我们提供了一个 SAVEPOINT 来做出类似事务嵌套的动作,我们将运用 SAVEPOINT 来帮助我们实现事务嵌套

参考资料

缓存穿透、缓存雪崩、缓存击穿的区别以及对应的解决方案?

缓存穿透

概念

缓存穿透是指恶意请求一个不存在的数据,导致请求穿透到数据库,压垮数据库

解决方案

  1. 对请求参数进行校验,例如对 ID 进行格式校验或进行 BloomFilter 过滤

  2. 缓存空对象,如果大量请求查询同一个不存在的数据,则可以在缓存中缓存空对象,避免频繁地查询数据库

  3. 限制短时间内的请求次数,例如对同一个 key 的请求在一定时间内只允许请求一次

  4. 使用缓存预热,提前将热点数据加载到缓存中

缓存雪崩

概念

缓存雪崩是指因为缓存服务器宕机、缓存键值过期或缓存大量键值同时失效等原因,导致大量请求直接打到数据库上,引起数据库宕机

解决方案

  1. 使用分布式锁,避免缓存失效时大量的请求同时访问数据库

  2. 使用多级缓存,比如本地缓存 + 分布式缓存,避免缓存失效时大量请求直接打到数据库上

  3. 对缓存的过期时间进行随机化,避免缓存同时失效

  4. 缓存数据时,使用不同的过期时间,避免在同一时间大量缓存数据失效

缓存击穿

概念

缓存击穿是指某个热点数据过期或被删除,在数据未来得及被缓存之前,大量请求直接打到数据库上,引起数据库宕机

解决方案

  1. 对于热点数据,不设置过期时间,始终存储在缓存中,避免缓存失效时大量请求直接打到数据库上

  2. 在缓存数据时,使用分布式锁,避免缓存失效时大量的请求同时访问数据库

  3. 双key: 要缓存的key过期时间是t,key1没有过期时间。每次缓存读取不到key时就返回key1的内容,然后触发一个事件。这个事件会同时更新key和key1

redis缓存库崩了,然后这些请求瞬间落在了mysql数据库上

解决方法

  • 事前:提高缓存库的高可用, 使用主从结构加哨兵 cluster集群

  • 事中:提供熔断机制

  • 事后: 做持久化,尽快恢复缓存集群,一旦恢复,自动从磁盘上读取数据,恢复内存中的数据

用php实现冒泡排序算法,使用php实现快速排序算法?

冒泡排序


<?php

function bubbleSort(&$arr, $len) {

$flag = 1;

for ($i = 0; $flag; $i++) {

$flag = 0;

for ($j = 0; $j < $len - $i - 1; ++$j) {

if ($arr[$j] > $arr[$j + 1]) {

$tmp = $arr[$j];

$arr[$j] = $arr[$j + 1];

$arr[$j + 1] = $tmp;

$flag = 1;

}

}

}

}

$arr = [4, 5, 6, 3, 2, 1];

$len = count($arr);

bubbleSort($arr, $len);

foreach($arr as $v) {

echo $v . "\t";

}

快速排序


<?php

function swap(&$arr, $a, $b) {

$tmp = $arr[$a];

$arr[$a] = $arr[$b];

$arr[$b] = $tmp;

}

function partition(&$arr, $p, $r): int {

// $i 是用于提交值的下标

// $j 是用于遍历$arr

$i = $j = $p;

for (; $j < $r; $j++) {

if ($arr[$j] < $arr[$r]) {

if ($i != $j) {

swap($arr, $i, $j);

}

$i++;

}

}

swap($arr, $i, $j);

return $i;

}

function _quickSort(&$arr, $p, $r): void {

if ($p > $r) {

return ;

}

$q = partition($arr, $p, $r);

_quickSort($arr, $p, $q - 1);

_quickSort($arr, $q + 1, $r);

}

function quickSort(&$arr, $len): void {

_quickSort($arr, 0, $len - 1);

}

$arr = [5, 8, 9, 23, 67, 1, 3, 7, 31, 56];

$len = count($arr);

quickSort($arr, $len);

foreach($arr as $v) {

echo $v . "\t";

}

设置php错误级别,除通知错误都可以显示

错误设置

int error_reporting ( [int level] )

  • E_ERROR: 报告运行时的致命错误

  • E_WARNING: 报告运行的非致命错误

  • E_NOTICE

默认的报告是除了通知之外的所有错误

error_reporting = E_ALL & ~ (E_NOTICE)

参考资料

安全对一套程序来说至关重要,请说说在开发中应该注意哪些安全机制?

使用验证码防止注册机灌水

使用预处理,绑定参数,参数过滤转义 防止sql注入

使用token防止远程提交,使用token验证登录状态

作用域操作符::如何使用?都在哪些场合下使用?

调用类常量

调用静态方法

$this和self、parent这三个关键词分别代表什么?在哪些场合下使用?

$this: 当前对象

self: 当前类

parent: 当前类的父类

$this在当前类中使用,使用->调用属性和方法

self也在当前类中使用,不过需要使用::调用

parent在类中使用

PHP的 数组底层实现原理

PHP5 数组底层实现原理

PHP5使用HashTable,通过某种哈希函数将特定的键映射到特定值的一种数据结构

哈希函数拆分成hash1 和 hash2。hash1将key映射为h值,hash将h值映射成slot(简称: hashTable的下标)

而且还保存两种链表

  • 全局链表:按插入顺序将所有的bucket全部串联起来

  • 局部链表: 为了解决哈希冲突

PHP7 数组底层实现原理

PHP7则改成了逻辑上的链表,所有bucket都分配在连续的数组内存中,不再用指针维护上下游关系

连续内存前,还存在一个索引表,用于存储slot(hashTable下标),通过slot指向bucket数组该slot链表的第一个bucket,然后通过zval.u2.next指向下一个bucket

hashTable的运行过程

  • 通过hash1把key化成hash

  • 通过于nTableMask得到slot下标

  • 通过nNumUsed++ 得到bucket的插入位置Idx

  • 把Idx存储solt下标的索引表,永远存储的是最新的那个,因为zval.u2.next会形成链表

PHP的变量底层实现原理

PHP5变量底层结构

image.png

zval_struct 用48字节

PHP7变量底层结构

image.png

zval_struct 用来16字节

zval_value 结构体,可以存储整型、浮点型、引用计数、字符串类型、数组类型、对象类型、资源类型、引用类型、抽象语法树等等

PHP的GC机制原理

引用计数 + 四色标记法

普通变量的垃圾回收,引用计数为0时,直接设置成为分配

数组和对象的垃圾回收,当它们进行unset时,如果他们的引用计数>0,就把它们加入到垃圾缓冲区,并标记为紫色

回收流程

  • 对gc root环中的每个元素进行深度优先遍历,将每个元素中gc_info为紫色的标记元素置为灰色,且引用计数减1

  • 扫描root环中gc_info为灰色的元素,如果发现引用计数仍大于0,说明这个元素还在使用,标记为黑色,引用计数加1.如果为0,则标记为白色

  • 扫描root环,将黑色的元素从root移除

  • 将root中颜色为白色的元素进行深度优先遍历,将其引用计数加1,然后将roots链表移动到待释放的列表中(to_free)

Laravel的依赖注入实现原理

什么是依赖注入

不通过 new() 的方式在类内部创建依赖类对象,而是将依赖的类对象在外部创建好之后,通过构造函数、函数参数等方式传递(或注入)给类使用

Laravel的依赖注入实现原理是什么

laravel容器包含控制反转和依赖注入,使用起来就是,先把对象bind好,需要时可以直接使用make来取就好

参考资料

MYSQL B+TREE底层原理

B+TREE 属性

  • 根节点有两个值

  • 中间节点和子节点可以k个元素 (k/2 < M < )

  • 当前节点存在上级的最大或最小值

  • 节点存储的不是数据,而是索引

  • 叶子节点存储最终的数据,并按从小到大的顺序以链表的形式连接起来

一条SQL语句在MySQL中如何执行的?

select 语句

update 语句

什么是静态延迟绑定

定义

静态延迟绑定,即在类内部用来代表类本身的关键字部分不是在类编译时固定好,而是当方法被访问时动态的选择来访者所属的类

静态延迟绑定就是利用static 关键字代替静态绑定self,静态延迟绑定需要使用到静态成员的重写

参考资料

判断一个数是否为2的整数次幂

题目


class Solution {

function isPowerOfTwo($n) {

return $n > 0 && ($n & $n - 1) == 0;

}

}

题解

linux 中存在一日志文件非常大,打开速度很慢,如何查找其中部分指定内容

命令

  • tailf xxxx | grep 'keyword' -C10

  • less

参考资料

负载均衡方式

轮询

负载均衡系统收到请求后,按照顺序轮流分配到服务器上

加权轮询

负载均衡系统根据服务器权重进行任务分配,这里的权重一般是根据硬件配置进行静态配置的,采用动态的方式计算会更加契合业务,但复杂度也会更高

负载最低优先

负载均衡系统将任务分配给当前负载最低的服务器,这里的负载根据不同的任务类型和业务场景,可以用不同的指标来衡量

性能最优类

负载最低优先类算法是站在服务器的角度来进行分配的,而性能最优优先类算法则是站在客户端的角度来进行分配的,优先将任务分配给处理速度最快的服务器,通过这种方式达到最快响应客户端的目的

两数组直接相加,array_merge 与直接相加的区别

代码


<?php

$arr1 = ["a", "b", "c"];

$arr2 = ["d", "e", "f"];

print_r(array_merge($arr1, $arr2));

print_r($arr1 + $arr1);

结果


array_merge,两个数组合并一起

(

[0] => a

[1] => b

[2] => c

[3] => d

[4] => e

[5] => f

)

+,只保留前一个数组

(

[0] => a

[1] => b

[2] => c

)

参考

session与cookie区别

  1. cookie数据存放在客户的浏览器上,session数据放在服务器上

  2. cookie不是很安全,别人可以分析存放在本地的COOKIE并进行COOKIE欺骗,如果主要考虑到安全应当使用session

  3. session会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能,如果主要考虑到减轻服务器性能方面,应当使用COOKIE

  4. 单个cookie在客户端的限制是3K,就是说一个站点在客户端存放的COOKIE不能3K

遍历两个一个数组最后的结果

代码


最大公约数的递归:

1、若a可以整除b,则最大公约数是b

2、如果步骤1不成立,最大公约数便是ba%b的最大公约数

示例:求(140,21)

140%21 = 14

21%14 = 7

14%7 = 0

返回7


/**

* 求最大公约数 Greatest Common Divisor(GCD)

* @param $a

* @param $b

* @return mixed

*/

function gcd($a, $b)

{

// 防止因除数为0而崩溃

if ($a == 0 || $b == 0) {

return 1;

}

if ($a % $b == 0) {

return $b;

}

return gcd($b, $a % $b);

}

参考资料

mysql 性能调优

laravel队列的延迟分发(delay)

原理

如果你想延迟执行一个队列中的任务,你可以用任务实例的 delay 方法

QUEUE_DRIVER=sync 默认的,使用delay延时队列 不能使用同步的,否则不起作用

参考资料

TCP长连接与短连接的区别,各自的优点与缺点,以及其使用场景

描述 PHP autoload 的机制

laravel autoload实现原理

PHP的性能优化

PHP性能优化

Innodb下创建表,表中不创建主键索引,创建一般索引后,查询逻辑是怎样

php 进程模型,php 怎么支持多个并发

概念

PHP-FPM 是多进程服务,其中有一个master进程(做管理工作)和多个worker进程(处理数据请求)

进程创建

static 模式

static 模式始终会保持一个固定数量的子进程,这个数量由pm.max_children定义

可以将其设置为512个worker

dynamic 模式

子进程的数量是动态变化的

启动,会生成固定数量的子进程,可以理解成最小子进程数,通过pm.start_server控制。而最大子进程数则由pm.max_children控制,子进程数会在 pm.start_servers ~ pm.max_children范围内变化

ondemand 模式

这种模式和dynamic模式正好相反,把内存放在第一位,每个闲置进程在持续闲置了pm.process_idle_timeout秒后就会被杀掉

什么是协程

协程是比线程更轻量的执行单元。进程和线程的调度是由操作系统负责的,而协程则是由执行单元相互协商进行调度的,所以它的切换发生在用户态

只有前一个协程主动地执行 yield 函数,让出 CPU 的使用权,下一个协程才能得到调度

协程实现


/**

* @brief 协程切换

* 1. 把当前rsp寄存器的值存储到old_co的stack_pointer属性

* 2. 并且把新的协程的 stack_pointer 属性更新到 rsp 寄存器

* 2. 最后,通过retq指令将会从栈上取出调用者地址,并跳转回调用者继续执行

*

* @param old_co 老协程

* @param co 新协程

*/

void yield_to(coroutine *old_co, coroutine *co) {

__asm__(

"movq %%rsp, %0\n\t"

"movq %%rax, %%rsp\n\t"

:"=m"(old_co->stack_pointer):"a"(co->stack_pointer):);

}

参考资料

找出数组中不重复的值 [1,2,3,3,2,1,5]


// 时间复杂度: O(n)

function findUnique($arr) {

$hash = array();

foreach($arr as $val) {

if(!isset($hash[$val])) {

$hash[$val] = 1;

} else {

unset($hash[$val]);

}

}

return array_keys($hash);

}

调用示例:

$arr = [1,2,3,3,2,1,5];

$result = findUnique($arr);

print_r($result); // 输出 [3, 5]

有两个文件文件,大小都超过了 1G,一行一条数据,每行数据不超过 500 字节,两文件中有一部分内容是完全相同的,请写代码找到相同的行,并写到新文件中。PHP 最大允许内内为 255M

对于这个问题,我们可以将两个文件分别进行 hash 算法的处理,然后将 hash 值相同的行记录下来,再进行比较得到相同的行。由于文件大小都超过了1G,因此需要逐行读取文件,处理时需要注意内存的使用。在 PHP 中,可以使用 fopen() 和 fgets() 函数进行逐行读取,同时可以使用 hash() 函数进行 hash 值的计算


<?php

// 打开原始文件和目标文件

$source = fopen('source.txt', 'r');

$target = fopen('target.txt', 'w');

// 定义 hash 表

$hash_table = array();

// 逐行读取原始文件

while (!feof($source)) {

$line = fgets($source);

// 计算当前行的 hash 值

$hash = hash('md5', $line);

// 判断 hash 值是否已存在

if (isset($hash_table[$hash])) {

// 如果已存在,则说明当前行与之前的某一行相同,将其写入目标文件中

fwrite($target, $line);

} else {

// 如果不存在,则将当前行的 hash 值存入 hash 表中

$hash_table[$hash] = true;

}

}

// 关闭文件句柄

fclose($source);

fclose($target);

?>

参考资料

PHP 如何实现不用自带的 cookie 函数为客户端下发 cookie。对于分布式系统,如何来保存 session 值

PHP 如何实现不用自带的 cookie 函数为客户端下发 cookie?

PHP可以通过设置HTTP响应头的方式来为客户端下发cookie,具体步骤如下

  • 使用PHP的header函数设置HTTP响应头中的Set-Cookie字段,设置cookie的键值对和其他属性,如过期时间、路径、域名等

  • 发送HTTP响应,浏览器会自动保存cookie,并在下一次请求时自动发送给服务器


header("Set-Cookie: name=value; expires=Wed, 21 Oct 2020 07:28:00 GMT; path=/; domain=example.com; secure; HttpOnly");

对于分布式系统,如何来保存 session 值?

在分布式系统中,为了保持session的一致性,需要使用共享存储来保存session值。常用的共享存储包括

  • 数据库:将session值存储在数据库中,可以使用MySQL、Redis等数据库实现

  • 文件系统:将session值存储在共享的文件系统中,如NFS、GlusterFS等

  • 缓存系统:将session值存储在缓存系统中,如Memcached、Redis等

在PHP中,通过设置session存储引擎的方式来实现分布式系统中的session共享

  • Files:使用文件系统存储session值

  • Memcached:使用Memcached存储session值

  • Redis:使用Redis存储session值

  • MySQL:使用MySQL存储session值

请用 SHELL 统计 5 分钟内,nginx 日志里访问最多的 URL 地址,对应的 IP 是哪些?


#!/bin/bash

# 设置时间范围

start_time=$(date -d "5 minutes ago" "+%d/%b/%Y:%H:%M:%S")

end_time=$(date "+%d/%b/%Y:%H:%M:%S")

# 统计访问最多的URL地址和对应的IP地址

awk -v start_time="$start_time" -v end_time="$end_time" '$4 > "["start_time && $4 < "["end_time {print $1,$7}' access.log | sort | uniq -c | sort -rn | head -10

解释一下脚本的每个部分

  • start_time 和 end_time 分别表示统计的时间范围,这里设置为5分钟前至当前时间

  • awk 命令用于从日志文件中提取时间、IP地址和URL地址。其中,4表示日志中的时间字段,4 表示日志中的时间字段,1 表示日志中的IP地址字段,$7 表示日志中的URL地址字段。

  • sort 命令用于将提取出来的IP地址和URL地址进行排序

  • uniq -c 命令用于统计每个IP地址和URL地址的出现次数

  • sort -rn 命令用于将统计结果按照出现次数从大到小排序

  • head -10 命令用于只显示出现次数最多的前10个IP地址和URL地址

写一段 shell 脚本实现备份 mysql 指定库(如 test) 到指定文件夹并打包,并删除 30 天前的备份,然后将新的备份推送到远端服务器,完成后送邮件通知


#!/bin/bash

# 备份文件名

BACKUP_NAME="test_$(date +%Y%m%d_%H%M%S).sql"

# 备份目录

BACKUP_DIR="/path/to/backups"

# 本地备份文件路径

BACKUP_FILE="$BACKUP_DIR/$BACKUP_NAME"

# 远端服务器信息

REMOTE_USER="user"

REMOTE_HOST="remote_host"

REMOTE_DIR="/path/to/remote/dir"

# 邮件信息

MAIL_TO="user@example.com"

MAIL_SUBJECT="MySQL Backup Report"

MAIL_BODY="MySQL backup completed successfully."

# 备份 MySQL 数据库

mysqldump -u root -pPASSWORD test > "$BACKUP_FILE"

# 压缩备份文件

tar -czvf "$BACKUP_FILE.tar.gz" "$BACKUP_FILE"

# 删除 30 天前的备份文件

find "$BACKUP_DIR" -type f -name "*.tar.gz" -mtime +30 -delete

# 推送备份文件到远端服务器

scp "$BACKUP_FILE.tar.gz" "$REMOTE_USER@$REMOTE_HOST:$REMOTE_DIR"

# 发送邮件通知

echo "$MAIL_BODY" | mail -s "$MAIL_SUBJECT" "$MAIL_TO"

a=[1,2,3];foreach(a=[1,2,3]; foreach (a as &v)foreach(v){} foreach (a as v)vardump(v){} var_dump (a) 等于多少;

执行完上述代码后,$a的值为array(1,2,2)

这是因为在第一个foreach循环中,使用了引用变量&$v,修改了数组元素的值为2

在第二个foreach循环中,没有使用引用变量,所以没有修改数组元素的值,但是$v的值仍然是2,所以在第二次循环中,又将第二个元素的值改为了2

最终,$a的值变成了array(1,2,2)

参考资料

数据库中的存放了用户 ID, 扣费很多行,redis 中存放的是用户的钱包,现在要写一个脚本,将数据库中的扣费记录同步到 redis 中,每 5 分钟执行一次。请问要考虑哪些问题?

并发问题

在多个线程同时执行同步操作时,可能会导致 Redis 中的钱包余额出现错误,因此需要使用 Redis 的事务机制或者 Lua 脚本来保证操作的原子性

数据库和 Redis 数据不一致问题

在同步时需要保证在同一时刻数据库和 Redis 中的数据是一致的

否则可能会导致数据不一致的情况。可以使用分布式锁或者其他同步机制来解决这个问题

数据量大问题

如果数据库中的扣费记录比较多,每 5 分钟执行一次同步可能会导致 Redis 中的钱包数据过大,影响 Redis 的性能,因此可以考虑将记录分批同步

性能问题

同步操作可能会对数据库和 Redis 的性能造成影响,需要评估同步操作的性能和影响,并进行优化

简单的同步脚本示例(使用 Python 和 Redis)


import redis

import pymysql

r = redis.Redis(host='localhost', port=6379, db=0)

conn = pymysql.connect(host='localhost', port=3306, user='root', password='password', db='test')

def sync():

cursor = conn.cursor()

cursor.execute("SELECT user_id, amount FROM payment_log WHERE synced = 0")

rows = cursor.fetchall()

for row in rows:

user_id, amount = row

pipe = r.pipeline()

pipe.hincrby("wallets", user_id, -amount)

pipe.execute()

cursor.execute("UPDATE payment_log SET synced = 1 WHERE user_id = %s", user_id)

conn.commit()

sync()

这个脚本从数据库中获取未同步的扣费记录,然后将对应的用户钱包中的余额减去扣费金额,并将记录标记为已同步

同步时使用了 Redis 的 pipeline 来保证操作的原子性。在实际使用时需要根据具体情况进行修改和优化

有 10 亿条订单数据,属于 1000 个司机的,请取出订单量前 20 的司机

可以采用MapReduce框架进行处理。首先,将订单数据按照司机ID进行分组,然后统计每个司机的订单量

接着,使用MapReduce框架进行计算,将每个司机的订单量作为key,司机ID作为value,进行倒排。最后,取前20个即为订单量前20的司机

php遍历一个多维数组

遍历多维数组可以使用循环嵌套的方式,每一层循环处理数组的每一维度


function traverseArray($arr) {

foreach ($arr as $key => $value) {

if (is_array($value)) {

traverseArray($value);

} else {

echo $key . '=>' . $value . '<br>';

}

}

}

// 示例多维数组

$arr = array(

'fruit' => array(

'apple' => array(

'color' => 'red',

'taste' => 'sweet'

),

'orange' => array(

'color' => 'orange',

'taste' => 'sour'

)

),

'vegetable' => array(

'carrot' => array(

'color' => 'orange',

'taste' => 'sweet'

),

'cucumber' => array(

'color' => 'green',

'taste' => 'crisp'

)

)

);

// 遍历数组

traverseArray($arr);

php 生产环境需要屏蔽那些方法

eval():执行字符串作为 PHP 代码。因为可以执行任意字符串,存在代码注入漏洞,应该禁用

exec()、shell_exec()、system()、passthru():执行外部命令,会导致命令注入漏洞,应该禁用或限制使用

open()、fread()、fwrite()、file_get_contents()、file_put_contents()、file():文件操作函数,可以读写任意文件,存在文件读写漏洞,应该限制使用

extract():将数组中的元素导入到当前符号表中,存在变量覆盖漏洞,应该禁用

可以在 php.ini 文件中的 disable_functions 配置项中添加要禁用的方法名,如下


disable_functions = eval, exec, shell_exec, system, passthru, include, include_once, require, require_once, fopen, fread, fwrite, file_get_contents, file_put_contents, file, extract

什么是XSS注入,什么SQL注入,如何防范?

XSS注入

XSS注入是黑客通过篡改Web页面中的HTML代码,嵌入恶意脚本,从而实现窃取用户信息、劫持用户会话等攻击行为

SQL注入

SQL注入是黑客通过构造恶意输入参数,篡改应用程序动态生成的SQL语句,从而执行恶意的SQL查询功能

防范方式

输入过滤和转义:对于输入的数据进行过滤和转义,过滤掉不安全的字符和标签,避免恶意代码的注入

输出编码:对于从数据库中取出的数据,在输出到页面之前进行编码,避免恶意脚本的执行

验证输入和输出:对于输入和输出的数据都进行验证,确保数据的合法性和安全性

使用参数化查询:使用参数化查询可以避免SQL注入攻击,减少应用程序的漏洞

使用安全框架:使用一些安全框架,如Spring Security等,可以减少应用程序的漏洞

如何以最少流量消耗,获取一个远程资源(假设1G)的最后10个字节?

如果需要获取远程资源的最后10个字节,可以使用HTTP协议的Range头部来实现

Range头部用于指定客户端希望获取的资源范围,包括起始位置和结束位置

对于获取最后10个字节的情况,可以设置Range头部为"bytes=-10",表示获取从倒数第10个字节到文件末尾的所有内容


import requests

url = "http://example.com/resource"

headers = {"Range": "bytes=-10"}

response = requests.get(url, headers=headers)

if response.status_code == 206: # Partial Content

print(response.content)

else:

print("Failed to retrieve resource with status code", response.status_code)

上述代码使用requests库发送HTTP请求,并设置Range头部为"bytes=-10",然后判断响应的状态码是否为206(Partial Content

如果响应状态码为206,则表示服务器成功返回了客户端请求的部分内容,此时可以通过response.content获取最后10个字节的内容

如果响应状态码不为206,则表示无法获取资源的最后10个字节