面试官的问题:
问面试题第一组:
答1.PHP中常用的十个数组函数
in_array
array_search
array_key_exists
array_values
array_filter
array_column
array_merge
array_pop
list
ksort
in_array
检查数组中是否存在某个值
@see https://www.php.net/manual/zh/function.in-array.php
// in_array ( mixed $needle , array $haystack [, bool $strict = FALSE ] ) : bool
// 大海捞针,在大海(haystack)中搜索针( needle),如果没有设置 strict 则使用宽松的比较。
$haystack = ["a", "b", "c", "d"];
var_dump(in_array("a", $haystack));
// bool(true)
array_search
在数组中搜索给定的值,如果成功则返回首个相应的键名
@see https://www.php.net/manual/zh/function.array-search.php
// array_search ( mixed $needle , array $haystack [, bool $strict = false ] ) : mixed
// 大海捞针,在大海(haystack)中搜索针( needle),如果没有设置 strict 则使用宽松的比较。
$haystack = ["a", "b", "c", "d"];
var_dump(array_search("a", $haystack));
// int(0)
$haystack = ["a", "b", "c", "d"];
var_dump(array_search("a", $haystack));
// bool(false)
array_key_exists
检查数组里是否有指定的键名或索引
@see https://www.php.net/manual/zh/function.array-key-exists.php
// array_key_exists ( mixed $key , array $array ) : bool
// 数组里有键 key 时,array_key_exists() 返回 TRUE。 key 可以是任何能作为数组索引的值。
$haystack = ["a", "b", "c", "d"];
var_dump(array_key_exists(0, $haystack));
// bool(true)
$haystack = ["a", "b", "c", "d"];
var_dump(array_key_exists(4, $haystack));
// bool(false)
$haystack = ["a"=>"aa", "b"=>"bb", "c"=>"cc", "d"=>"dd"];
var_dump(array_key_exists("a", $haystack));
// bool(true)
array_values
返回数组中所有的值
@see https://www.php.net/manual/zh/function.array-key-exists.php
// array_values ( array $array ) : array
// array_values() 返回$array数组中所有的值并给其建立数字索引。
$haystack = ["a"=>"aa", "b"=>"bb", "c"=>"cc", "d"=>"dd"];
var_dump(array_values($haystack));
Array
(
[0] => aa
[1] => bb
[2] => cc
[3] => dd
)
array_filter
用回调函数过滤数组中的单元
@see https://www.php.net/manual/zh/function.array-filter.php
array_filter ( array $array [, callable $callback [, int $flag = 0 ]] ) : array
array:要循环的数组
callback:使用的回调函数。
如果没有提供 callback 函数, 将删除 array 中所有等值为 FALSE 的条目。更多信息见转换为布尔值。
flag:决定callback接收的参数形式:
ARRAY_FILTER_USE_KEY - callback接受键名作为的唯一参数
ARRAY_FILTER_USE_BOTH - callback同时接受键名和键值
依次将 array 数组中的每个值传递到 callback 函数。如果 callback 函数返回 true,则 array 数组的当前值会被包含在返回的结果数组中。数组的键名保留不变。
function myfilter($item)
{
if ($item === "bb")
{
return false;
}
return true;
}
$haystack = ["a"=>"aa", "b"=>"bb", "c"=>"cc", "d"=>"dd"];
print_r(array_filter($haystack,"myfilter"));
Array
(
[a] => aa
[c] => cc
[d] => dd
)
array_column
返回数组中指定的一列
@see https://www.php.net/manual/zh/function.array-column.php
// array_column ( array $input , mixed $column_key [, mixed $index_key = null ] ) : array
// array_column() 返回input数组中键值为column_key的列, 如果指定了可选参数index_key,那么input数组中的这一列的值将作为返回数组中对应值的键。
// 取出数组中的password列
$haystack = [
["user" => "user1", "password" => "password1"],
["user" => "user2", "password" => "password2"],
["user" => "user3", "password" => "password3"],
["user" => "user4", "password" => "password4"],
];
print_r(array_column($haystack,"password"));
Array
(
[0] => password1
[1] => password2
[2] => password3
[3] => password4
)
// 取出数组中的password列,并使用user列做为key值。
print_r(array_column($haystack,"password"));
Array
(
[user1] => password1
[user2] => password2
[user3] => password3
[user4] => password4
)
array_merge
合并一个或多个数组
@see https://www.php.net/manual/zh/function.array-merge.php
// array_merge ( array $array1 [, array $... ] ) : array
// 如果数组中key相同,则value取最后一个相同key的value
$haystack_1 = ["user" => "user1", "password" => "password1"];
$haystack_2 = ["user" => "user2", "email" => "email2"];
print_r(array_merge($haystack_1, $haystack_2));
Array
(
[user] => user2
[password] => password1
[email] => email2
)
array_pop
弹出数组最后一个单元(出栈)
@see https://www.php.net/manual/zh/function.array-pop.php
// array_pop ( array &$array ) : mixed
// 弹出数组最后一个单元(出栈),传递的参数为数组指针,返回值为出栈元素。
$haystack = ["user" => "user1", "password" => "password1"];
print_r(array_pop($haystack)); // password1
print_r($haystack);
(
[user] => user1
)
list
把数组中的值赋给一组变量
@see https://www.php.net/manual/zh/function.list.php
list ( mixed $var1 [, mixed $... ] ) : array
// 可以理解为出栈,传递的参数为数组指针,返回值为出栈元素。
$haystack = ["user", "password"];
list($u, $p) = $haystack;
echo $u.$p;
userpassword
ksort
对数组按照键名排序
@see https://www.php.net/manual/zh/function.ksort.php
ksort ( array &$array [, int $sort_flags = SORT_REGULAR ] ) : bool
// 对数组按照键名排序,保留键名到数据的关联。成功时返回 TRUE, 或者在失败时返回 FALSE。
$haystack = [
"c"=>"value",
"a"=>"value",
"b"=>"value"
];
ksort($haystack);
print_r($haystack);
Array
(
[a] => value
[b] => value
[c] => value
)
2.php 设计模式和使用场景
设计模式六大原则
开放封闭原则:一个软件实体如类、模块和函数应该对扩展开放,对修改关闭。
里氏替换原则:所有引用基类的地方必须能透明地使用其子类的对象.
依赖倒置原则:高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该依赖细节;细节应该依赖抽象。
单一职责原则:不要存在多于一个导致类变更的原因。通俗的说,即一个类只负责一项职责。
接口隔离原则:客户端不应该依赖它不需要的接口;一个类对另一个类的依赖应该建立在最小的接口上。
迪米特法则:一个对象应该对其他对象保持最少的了解。
1. Singleton(单例模式)
单例模式是最常见的模式之一,在Web应用的开发中,常常用于允许在运行时为某个特定的类创建仅有一个可访问的实例。
使用场景:只实例化一次,内部实例化,对外只有一个开放方法,只能通过调取该方法进行调取实例化对象。数据库连接
2. Factory(工厂模式)
工厂模式是另一种非常常用的模式,正如其名字所示:确实是对象实例的生产工厂。某些意义上,工厂模式提供了通用的方法有助于我们去获取对象,而不需要关心其具体的内在的实现
使用场景:使用方法 new实例化类,每次实例化只需调用工厂类中的方法实例化即可。
优点:由于一个类可能会在很多地方被实例化。当类名或参数发生变化时,工厂模式可简单快捷的在工厂类下的方法中 一次性修改,避免了一个个的去修改实例化的对象。
3. Prototype(原型模式)
有时候,部分对象需要被初始化多次。而特别是在如果初始化需要耗费大量时间与资源的时候进行预初始化并且存储下这些对象,就会用到原型模式:
使用场景:初始化需要耗费大量资源和时间
4. Adapter(适配器模式)
这种模式允许使用不同的接口重构某个类,可以允许使用不同的调用方式进行调用
使用场景:利用接口,将不同的类 组装一组有相同名称的方法 ,利用接口把 PDO mysql mysqli 封装成相同都数据库操作
5. Bridge(桥接模式)
将抽象部分与它的实现部分分离,使他们都可以独立的变抽象与它的实现分离,即抽象类和它的派生类用来实现自己的对象
桥接与适配器模式的关系(适配器模式上面已讲解):
桥接属于聚合关系,两者关联 但不继承
适配器属于组合关系,适配者需要继承源
6. Decorator(装饰器模式)
根据运行时不同的情景动态地为某个对象调用前后添加不同的行
使用场景:symfony 控制器中beforepost afterpost 中post提交前和提交后,对数据处理
7. Facade(门面模式)
门面模式 (Facade)又称外观模式,用于为子系统中的一组接口提供一个一致的界面。门面模式定义了一个高层接口,这个接口使得子系统更加容易使用:引入门面角色之后,用户只需要直接与门面角色交互,用户与子系统之间的复杂关系由门面角色来实现,从而降低了系统的耦
8. Strategy(策略模式)
将一组特定的行为和算法封装成类,以适应某些特定的上下文环境,这种模式就是策略模式
使用场景:个人理解,策略模式是依赖注入,控制反转的基础
9. Observer(观察者模式)
一个对象状态发生改变时,依赖它的对象全部会收到通知,并自动更新
使用场景:用户登录,需要写日志,送积分,参与活动 等
使用消息队列,把用户和日志,积分,活动之间解耦合
3.PHP异步执行的常用方式
1、使用前端Ajax处理
$.ajax(“do.php”, { name: ‘ityangs’,job:’PHP Programmer’} );
2、使用popen函数执行本地文件
pclose(popen(‘php /var/www/do.php &’, ‘r’));
3、使用CURL
设置curl的超时时间 CURLOPT_TIMEOUT 为1 (最小为1),因此客户端需要等待1秒,curl请求地址必须为绝对路径
$param = array(
‘name’=>’ityangs’,
‘job’=>’PHP Programmer’
);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL,”http://www.example.com/do.php“);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($param)); //将数组转换为URL请求字符串
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HEADER, false);
curl_setopt($ch, CURLOPT_TIMEOUT, 1);
curl_exec($ch);
curl_close($ch);
4、使用fsockopen函数,需要自己拼接header部分
function doRequest($url, $param=array(),$timeout =10){
$urlParmas = parse_url($url);
$host = $urlParmas[‘host’];
$path = $urlParmas[‘path’];
$port = isset($urlParmas[‘port’])? $urlParmas[‘port’] :80;
$errno = 0;
$errstr = ”;
$fp = fsockopen($host, $port, $errno, $errstr, $timeout);
$query = isset($param)? http_build_query($param) : ”;
$out = “POST “.$path.” HTTP/1.1\r\n”;
$out .= “host:”.$host.”\r\n”;
$out .= “content-length:”.strlen($query).”\r\n”;
$out .= “content-type:application/x-www-form-urlencoded\r\n”;
$out .= “connection:close\r\n\r\n”;
$out .= $query;
fputs($fp, $out);
fclose($fp);
}
$url = ‘http://www.example.com/do.php‘;
$param = array(
‘name’=>’ityangs’,
‘job’=>’PHP Programmer’
);
doRequest($url, $param);
注意:
1、如果使用Apache作为Web服务器,让PHP支持异步首先必须得在Apache配置文件httpd.conf配置enablesendfile
on。
2、在异步执行的PHP文件中建议加上一下两个配置:
ignore_user_abort(true); // 忽略客户端断开
set_time_limit(0); // 设置执行不超时
问面试题第二组:
答4.linux 批量kill php或php-fpm进程
ps -ef|grep php|grep -v grep|cut -c 9-15|xargs kill -9
ps -ef|grep php-fpm|awk -F ‘ ‘ ‘{print $2}’|xargs kill -9
管道符“|”用来隔开两个命令,管道符左边命令的输出会作为管道符右边命令的输入。下面说说用管道符联接起来的
几个命令:
“ps – ef”是Red Hat 里查看所有进程的命令。这时检索出的进程将作为下一条命令“grep LOCAL=NO”的输入。
“grep php”的输出结果是,所有含有关键字“LOCAL=NO”的进程
“grep -v grep”是在列出的进程中去除含有关键字“grep”的进程。
“cut -c 9-15”是截取输入行的第9个字符到第15个字符,而这正好是进程号PID。
“xargs kill -9”中的xargs命令是用来把前面命令的输出结果(PID)作为“kill -9”命令的参数,并执行该令。
“kill -9”会强行杀掉指定进程,这样就成功清除了php所有进程
5.HTTP状态码
常见的 HTTP 相应状态码
200:请求被正常处理
204:请求被受理但没有资源可以返回
206:客户端只是请求资源的一部分,服务器只对请求的部分资源执行 GET 方法,相应报文中通过 Content-Range 指定范围的资源。
301:永久性重定向
302:临时重定向
303:与 302 状态码有相似功能,只是它希望客户端在请求一个 URI 的时候,能通过 GET 方法重定向到另一个 URI 上
304:发送附带条件的请求时,条件不满足时返回,与重定向无关
307:临时重定向,与 302 类似,只是强制要求使用 POST 方法
400:请求报文语法有误,服务器无法识别
401:请求需要认证
403:请求的对应资源禁止被访问
404:服务器无法找到对应资源
500:服务器内部错误
503:服务器正忙
6.PHP验证邮箱格式的两种方式
1:通过自带方式验证邮箱
eg: $result = filter_var(‘bob@example.com’, FILTER_VALIDATE_EMAIL);
打印结果时,如果格式正确,则输出邮箱地址,错误则输出false
2:正则匹配
(1)$regex= ‘/\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*/’;
(2)$regex=”/([a-z0-9]*[-_.]?[a-z0-9]+)*@([a-z0-9]*[-_]?[a-z0-9]+)+[.][a-z]{2,3}([.][a-z]{2})?/i”;
上面两个正则表达式都可以用来匹配邮箱,匹配方式如下:
$str = “email@example.com”;
$result = preg_match($regex,$str);
若邮箱格式正确,result值为1,错误则为false
PS: 顺手贴上固话和手机号码的正则验证↓
$isTel=”/^([0-9]{3,4}-)?[0-9]{7,8}$/”;
$isMob=”/^1[3-8]{1}[0-9]{9}$/”;
问面试题第三组:
答7.博客系统优化
假设有一个博客系统,数据库存储采用 MySQL,用户数量为 1000 万,预计文章总数为 10 亿,每天有至少 10 万的更新量,每天访问量为 5000 万,对数据库的读写操作的比例超过 10:1,你如何设计该系统以确保系统高效、稳定的运行?
提示:可以从数据库设计、系统框架、及网络架构方面进行描述,可以自由发挥。
增加 cache
两套数据库,一个为主数据库,一个为更新数据库,其中更新数据库作为保存近 N(1,2,3)天更新数据,写入后放进 cache,并增加 lease 及时淘汰不常访问的记录,同时后台定期合并到主数据库。
数据读取时,cache-> 更新数据库 -> 主数据库
主数据库中将元信息(如用户信息)与文章实际内容分开,对每篇文章内容计算热度权重,做主键之一,然后分表。
8.如何查看Mysql慢查询的sql是什么
1,slow_query_log
这个参数设置为 ON,可以捕获执行时间超过一定数值的 SQL 语句。
2,long_query_time
当 SQL 语句执行时间超过此数值时,就会被记录到日志中,建议设置为 1 或者更短。
3,slow_query_log_file
记录日志的文件名。
4,log_queries_not_using_indexes
这个参数设置为 ON,可以捕获到所有未使用索引的 SQL 语句,尽管这个 SQL 语句有可能执行得挺快。
9.MySQL如何定位并优化慢查询sql
1.如何定位并优化慢查询sql
a.根据慢日志定位慢查询sql
SHOW VARIABLES LIKE ‘%query%’ 查询慢日志相关信息
slow_query_log 默认是off关闭的,使用时,需要改为on 打开
slow_query_log_file 记录的是慢日志的记录文件
long_query_time 默认是10S,每次执行的sql达到这个时长,就会被记录
SHOW STATUS LIKE ‘%slow_queries%’ 查看慢查询状态
Slow_queries 记录的是慢查询数量 当有一条sql执行一次比较慢时,这个vlue就是1 (记录的是本次会话的慢sql条数)
注意:
如何打开慢查询 : SET GLOBAL slow_query_log = ON;
将默认时间改为1S: SET GLOBAL long_query_time = 1;
(设置完需要重新连接数据库,PS:仅在这里改的话,当再次重启数据库服务时,所有设置又会自动恢复成默认值,永久改变需去my.ini中改)
b.使用explain等工具分析sql
在要执行的sql前加上explain 例如:EXPLAIN SELECT menu_name FROM t_sys_menu ORDER BY menu_id DESC;
接着看explain的关键字段
type:
如果发现type的值是最后两个中的其中一个时,证明语句需要优化了。
extra:
c.修改sql或者尽量让sql走索引
mysql查询优化器会根据具体情况自己判断走哪个索引,不一定是走主键(explain中的key可以看到走的哪个key)具体情况根据具体情况来定,当你要强制执行走某一个key时:
在查询的最后加上 force index(primary); 强制走主键的
2.联合索引的最左匹配原则的成因
最左匹配原则的概念参考:https://www.cnblogs.com/lanqi/p/10282279.html
成因:
当通过(col3,col2)这样的联合索引去查找时,可以看到也是一个B+树的结构向下查找,若直接通过col2去查找,无法直接查找到34、77。也就用不到这个联合索引了。
3.索引是建立得越多越好吗
1.数据量小的表不需要建立索引,建立会增加额外的索引开销。
2.数据变更需要维护索引,因此更多的索引意味着更多的维护成本。
3.更多的索引意味着也需要更多的空间。
问面试题第四组:
答10.冒泡排序
public fuction bubble_sort($array)
{
$len = count($array);
if($len <=1) return false; for($i=0; $i$i; $j–)
{
if($array[$j] < $array[$j-1])
{
$tmp = $array[$j];
$array[$j] = $array[$j-1];
$array[$j-1] = $tmp;
}
}
}
return $array;
}
11.快速排序
public function quick_sort($array)
{
$len = count($array);
if($len <= 1) return false;
$key = array[0];
$l = [];
$r = [];
for($i=1; $i
{
if($array[$i] <=$key)
{
$l[] = array[$i];
}else{
$r[] = array[$i];
}
}
$l = quick_sort($l);
$r = qucik_sort($r);
return array_merge($l, array($key), $r);
}
11.【SQL】通过SQL 语句创建学生信息表(学号、学生姓名、班级)、学习成绩表(学号、学科、成绩)
1、创建数据库
Create Database School
2、创建数据表
学生表(学号、姓名、性别、所在班级、年龄)
Create table student( Sno nvarchar(100) not null primary key, Sname nvarchar(100), Ssex nvarchar(20), Sclass nvarchar(20), Sage int);
成绩表(学号、科目、分数)
Create table score( Sno nvarchar(100) not null, Ssubject nvarchar(20), Sgrade int, FOREIGN KEY (Sno) REFERENCES student(Sno));
3、执行结果
12.PHP 解决未定义变量报错
在PHP中 有时候会出现
Notice: Undefined index: sid in D:\Apache Group\Apache2\htdocs\php_mobile\mobile\chao\sinnsei_publish.php on line 10
这个报错,原因是因为没有定义,因为PHP是弱类型语言,和JAVA等不一样,不一定要初始化,所以这种问题其实不是什么大问题
不过,出现在页面上确实不好看,
方法一:初始化变量进行赋值
方法二:加@来对错误进行抑制
1 $sid = @$_POST["sid"];
问面试题第五组:
答13.SQL查询–查询用户每个用户的最后一次登录记录
1、创建测试表,
create table test_log(user_id number, v_date date);
2、插入测试数据,
insert into test_log
select round(level/4), sysdate-level from dual connect by level<1000;
3、查询表中数据,一个用户会有多条时间记录,select t.*, rowid from test_log t;
4、编写sql,获取所需目标数据,每个用户只有最新一条记录;
select *
from (select t.*,
row_number() over(partition by user_id order by v_date desc) rn
from test_log t)
where rn = 1
order by user_id;
14.mysql联合索引
比较简单的是单列索引(b+tree)。遇到多条件查询时,不可避免会使用到多列索引。联合索引又叫复合索引。
b+tree结构如下:
每一个磁盘块在mysql中是一个页,页大小是固定的,mysql innodb的默认的页大小是16k,每个索引会分配在页上的数量是由字段的大小决定。当字段值的长度越长,每一页上的数量就会越少,因此在一定数据量的情况下,索引的深度会越深,影响索引的查找效率。
在这里插入图片描述
对于复合索引(多列b+tree,使用多列值组合而成的b+tree索引)。遵循最左侧原则,从左到右的使用索引中的字段,一个查询可以只使用索引中的一部份,但只能是最左侧部分。例如索引是key index (a,b,c). 可以支持a a,b a,b,c 3种组合进行查找,但不支持 b,c进行查找。当使用最左侧字段时,索引就十分有效。
创建表test如下:
create table test(
a int,
b int,
c int,
KEY a(a,b,c));
比如(a,b,c)的时候,b+数是按照从左到右的顺序来建立搜索树的,比如当(a=? and b=? and c=?)这样的数据来检索的时候,b+树会优先比较a列来确定下一步的所搜方向,如果a列相同再依次比较b列和c列,最后得到检索的数据;但当(b=? and c=?)这样的没有a列的数据来的时候,b+树就不知道下一步该查哪个节点,因为建立搜索树的时候a列就是第一个比较因子,必须要先根据a列来搜索才能知道下一步去哪里查询。比如当(a=? and c=?)这样的数据来检索时,b+树可以用a列来指定搜索方向,但下一个字段b列的缺失,所以只能把a列的数据找到,然后再匹配c列的数据了, 这个是非常重要的性质,即索引的最左匹配特性。以下通过例子分析索引的使用情况,以便于更好的理解联合索引的查询方式和使用范围。
一、多列索引在and查询中应用
select * from test where a=? and b=? and c=?;查询效率最高,索引全覆盖。
select * from test where a=? and b=?;索引覆盖a和b。
select * from test where b=? and a=?;经过mysql的查询分析器的优化,索引覆盖a和b。
select * from test where a=?;索引覆盖a。
select * from test where b=? and c=?;没有a列,不走索引,索引失效。
select * from test where c=?;没有a列,不走索引,索引失效。
二、多列索引在范围查询中应用
select * from test where a=? and b between ? and ? and c=?;索引覆盖a和b,因b列是范围查询,因此c列不能走索引。
select * from test where a between ? and ? and b=?;a列走索引,因a列是范围查询,因此b列是无法使用索引。
select * from test where a between ? and ? and b between ? and ? and c=?;a列走索引,因a列是范围查询,b列是范围查询也不能使用索引。
三、多列索引在排序中应用
select * from test where a=? and b=? order by c;a、b、c三列全覆盖索引,查询效率最高。
select * from test where a=? and b between ? and ? order by c;a、b列使用索引查找,因b列是范围查询,因此c列不能使用索引,会出现file sort。
四,总结联合索引的使用在写where条件的顺序无关,mysql查询分析会进行优化而使用索引。但是减轻查询分析器的压力,最好和索引的从左到右的顺序一致。使用等值查询,多列同时查询,索引会一直传递并生效。因此等值查询效率最好。索引查找遵循最左侧原则。但是遇到范围查询列之后的列索引失效。排序也能使用索引,合理使用索引排序,避免出现file sort。
15.PHP实现斐波那契数列(递归 + 非递归)实现
斐波那契数列:
1 1 2 3 5 8 13 21 34 55 …
概念:
前两个值都为1,该数列从第三位开始,每一位都是当前位前两位的和
规律公式为:
Fn = F(n-1) + F(n+1)
F:指当前这个数列
n:指数列的下标
非递归写法:
function fbnq($n){ //传入数列中数字的个数
if($n <= 0){
return 0;
}
$array[1] = $array[2] = 1; //设第一个值和第二个值为1
for($i=3;$i<=$n;$i++){ //从第三个值开始
$array[$i] = $array[$i-1] + $array[$i-2];
//后面的值都是当前值的前一个值加上前两个值的和
}
return $array;
}
递归写法:
function fbnq($n){
if($n <= 0) return 0;
if($n == 1 || $n == 2) return 1;
return fbnq($n - 1) + fbnq($n - 2);
}