1. 前两天出去面试了,结果让我有点意外,我居然跪在了很简单的几道基础题上,使用框架开发项目时间久了,果然就如当时在学校老师说的那样,基础全忘光了,写项目的业务模块倒是熟练了很多,反而把最基本的东西忘的一干二净,刚好最近找工作也要复习,不如就把面试中遇到的面试题自己做一个详解,同时根据问到的知识点进行一个相关知识点的补充。
  2. 飞飞当时给我说过一句话挺有趣的,“面试一万个人然后招一个人,你最后没有面试上,就是自己太菜,不用管自己在那9999个人里面排多少位,这不重要,重要的是,你不是第一。”虽然这句话看起来说的有一些绝对,但还是有一些道理的,菜就是菜,慢慢补呗。

写出Session与Cookie区别

考点:PHP会话控制技术

为什么需要会话控制技术?
HTTP是无状态协议,没有一个内建机制维护两个事物之间的状态。同一个用户请求两次HTTP请求时,HTTP协议不会认为这两次请求都来自同一个用户,会当成两个独立的请求来处理。也就没有办法保持用户的登陆状态,所以会话控制技术的核心思想是 允许客户端跟踪服务端做出的连续请求 ,从而完成登陆状态的保持。
Cookie: 服务器发送给客户端的片段信息, 存储在客户端浏览器的内存或硬盘中的一种技术。

  • 写Cookie: setcookie(name,value,expire,path,domain,secure)
    • name:Cookie名称,可以使用 ‘a[b]’
    • value:Cookie值
    • expire:Unix时间戳,以秒为单位,如果不设置,则在浏览器关闭时过期
    • path:有效服务器路径,设置为 ‘/‘ 时,对整个域名domain有效,如果设置为 ‘/foo/‘ ,则仅对domain中/foo/目录及其子目录有效(例如:/foo/ban/)
    • domain:Cookie的有效域名/子域名,设置成子域名(例如:’www.example.com’ ),会使Cookie对这子域名和他的三级域名有效(例如:w2.www.example.com)。要让Cookie对整个域名有效,设置顶级域名就行了(例如:example.com)
  • 读取Cookie: $_COOKIE
  • 删除Cookie: setcookie(name,’’,time()-1) ,把Cookie设为过期即可

Cookie优缺点:

优点: 存储在客户端不会浪费服务器资源 缺点: 由于数据存储在客户端,不建议将一些敏感重要的数据使用Cookie存储。客户端有权限禁用掉Cookie的使用。

Session: 将用户的数据 存储在服务器 当中,用户无法禁止掉Session使用,但 Session不是完全脱离Cookie的 。Session是通过服务器客户端Cookie带过来的SESSIONID来确定用户数据的,如果Cookie被禁用,需要通过URL传递SESSIONID的形式保存SESSION的状态。

Session的使用

  1. 开启Session: session_start()
  2. 使用Session: $_SESSION 直接操作SESSION数组就可以
  3. 清空Session: $_SESSION=[]$_SESSION = null
  4. 删除Session(包括文件和Cookie): session_destroy()

Session的配置 php.ini 文件

  • session.auto_start 是否自动开启Session
  • session.cookie_domain 设置Session的有效域名
  • session.cookie_lifetime 设置的cookie有效时间,是从创建了这个cookie时开始计算,不会因为操作而刷新
  • session.cookit_path 设置session有效服务器路径
  • session.name 存储在客户端的Cookit的名称,
  • session.save_path 设置session在服务器存储路径
  • session.use_cookies 是否使用Cookie传递session
  • session.use_trans_sid 是否允许SESSIONID通过URL明文传输
  • session.gc_probability 清理多少次垃圾
  • session.gc_divisor 每访问多少次
  • session.gc_maxlifetime 设置的session的最大生命周期
  • session.save_handler 设置存储方式,可以存储到Redis中或MySQL中,默认是’files’类型,即文件存储

Session垃圾回收机制

    Session是存储在服务器上的,如果用户没有走退出程序而是直接关闭掉了浏览器,则Session文件会永久的保存在服务器上,时间长了会撑满内存,这个适合就需要使用Session的垃圾回收机制,大概流程是这样的:
  1. 当用户长时间没有再次访问Session,比如刷新、修改、添加等操作(时间根据:session.gc_maxlifetime设置的秒数,默认1440,PHP5默认1500),超过这个时间 则Session将会被设为过期的“垃圾文件”
  2. 当Session被设置为垃圾文件时,PHP每被请求设定次数 (次数根据: session.gc_divisor 设置的次数,默认1000),清除设定个数文件(session.gc_probability 默认是1次,也就是说每访问1000次清空一个过期文件),不建议把清除次数设置的特别小,会消耗服务器资源

优点: 数据存储在服务器,相比Cookie比较更安全。 缺点: 占用服务器资源,如果垃圾回收机制设置不合理,会撑满服务器硬盘。

Cookie被禁用时如何传递SESSIONID

<!-- 使用session_name()和session_id()传递 -->
<a href="<?php echo session_name().'='.session_id()?>">下一个页面</a>
<!-- 使用SID常量传递,SID比较智能,比较推荐使用这个-->
<!-- 在Cookie开启的时候,值为空,Cookie禁用的时候才会输出 -->
<a href="<?php echo SID?>">下一个页面</a>

Session存储共享(Redis/MySQL/Memcache)

    如果需要使用多台服务器的话,直接将Session存储到其中一台服务器的时候,可能下次访问的时候就是另一台服务器了,这种情况下Session因为不能共享的原因会导致用户的Session信息无法共享在其他的服务器上。这时需要使用 `session_set_save_handler`设置用户自定义会话存储函数。

写出下列代码输出结果

<?php
    var_dump(empty($a));
    var_dump(isset($a));
    var_dump(is_null($a));

empty($a)

    输出 `bool(true)` , 判断一个变量是否为空,当变量不存在时,或它的值等同于false时,那么它就会被认为不存在, empty() 并不会产生警告 , empty() 本质上与 !isset($var) || $var == false 等价

isset($a)

    输出 `bool(fasle)` ,判断变量是否设置且非NULL,使用 unset() 释放变量后,使用 isset() 结果为false。 isset() 可以传递多个变量,只有当所有变量都被设置时才会返回TRUE,计算过程从左往右,中途遇到没有设置的变量则不会继续往后计算。<br />**is_null($a)**

    输出 `bool(true)` ,检测变量是否为NULL,以下三种情况变量被认为是NULL:
  • 赋值为NULL
  • 尚未被复制 (只是被声明了)
  • 被 unset() 掉的变量

使用正则验证邮箱的正确性

我写的面试答案:[0-9a-zA-Z]+@[0-9a-zA-Z]{2,}.[0-9a-zA-Z]{2,4}

太久不用正则的原因导致我写的这个正则,用到是能用,但是写的过于繁琐,还有就是小部分情况下无法验证。错误: 使用.的时候需要转义,否则匹配不到 “.” 会出现错误

改良后的写法:^\w+@\w{2,}\.\w{2,4}$

正则表达式的所用:分割 查找 匹配 替换

关于正则的用法补充:

  • 分隔符
    • / 正斜线
    • # hash符号
    • ~ 取反符号
  • 通用原子
    • \d 十进制的0-9
    • \D 除了十进制的0-9,可以理解为 \d取反
    • \w 数字字母下划线
    • \W除了数字字母下划线,可理解为\w取反
    • \s 空白字符(空格)
    • \S除了空白字符,可理解为 \s取反
  • 元字符
    • . 除了换行符以外的任意字符
    • * 匹配前面的内容,零次、一次、多次
    • ? 匹配前面的内容,零次、一次
    • ^ 必须以此开头
    • $ 必须以此结尾
    • + 出现一次或多次
    • {n} 只能出现N次
    • {n,} 至少出现N次
    • {n,m} 至少出现N次,最多出现M次
    • [] 或,比如[123] 表示 1或2或3
    • () 表示一个整体
    • [^123] 表示 除了1 除了2 除了3
    • | 表示或的意思
    • [-] 范围值,例如 [0-9]
  • 模式修正符
    • i 不区分大小写
    • m 每一行分别匹配
    • s 修正换行符,如果由换行符就需要使用这个修正,否则匹配不上
    • U 取消贪婪模式
    • x 忽略模式中的空白字符
    • A 必须以这个模式开头
    • D 修正对\n的忽略
    • u utf-8编码,中文匹配时有用

补充一些常用正则规则:

  • 验证手机号:^1[3456789]\d{9}$
  • 验证身份证号:^\d{17}[\dxX]$

设计一个无限级分类表,并写出查询代码

什么是无限级分类?

    无限分类就是在一个分类下面可以创建多个子类,而其子类下面同样可以创建不同子类,如此往复,没有终点。

        使用无限分类一个可以做分类的下拉列表,也可以做分类的面包屑导航。

以父ID实现的无限分类

    使用递归算法。表中一个字段id,一个字段pid(父ID),这样可以根据WHERE id = pid 来查出上一层内容,运用递归至顶层。

表名:cate

字段名 类型
id int(11) 主键索引 无符号
pid int(11) 无符号 父级id,根据这个判断父级是谁
title var_char() 分类名称

代码实现:

<?php
    $dsn="mysql:host=127.0.0.1;port=3306;dbname=test;charset=utf8mb4;";
    $pdo=new PDO($dsn,'root','root');
    $sql='SELECT * FROM `cate` Order BY pid';
    $result=$pdo->query($sql,PDO::FETCH_OBJ);
    $categories =[];
    // 循环结果集 按照层级拼
    foreach($result as $value){
        $categories[$value->pid][]=$value;
    }
    $tree=[];
    // 拼接树形结构
    function catetree($categories,$n){
        global $tree;
        // 如果由子类再循环
        if(isset($categories[$n])){
            foreach($categories[$n] as $category){
                // 几层就输出几个
                $tree[] = '├'.str_repeat('-',$n).$category->title;
                catetree($categories,$category->id);
            }
        }
    }
    // 调用函数
    catetree($categories,0);
    // 输出结果
    var_dump($tree);    
/**
    array (size=10)
      0 => string '├新闻' (length=9)
      1 => string '├-国内新闻' (length=16)
      2 => string '├---北京新闻' (length=18)
      3 => string '├-国际新闻' (length=16)
      4 => string '├----美国新闻' (length=19)
      5 => string '├图片' (length=9)
      6 => string '├--风景图片' (length=17)
      7 => string '├--美女图片' (length=17)
      8 => string '├-------日韩明星' (length=22)
      9 => string '├---------日本电影' (length=24)
      **/
?>

MySQL的InnoDB和MyISAM引擎区别,分别适合那些场景?

  • InooDB引擎
    • 支持事务,性能优秀
    • 数据存储在共享表空间,可通过配置分开
    • 对于主键查询性能高于其他引擎
    • 读取数据时自动在内存构建Hash索引,插入数据时自动构建插入缓冲区
    • 通过一些机制和工具支持真正的热备份
    • 支持崩溃后的安全恢复
    • 支持行级锁,但没有命中索引的情况下依然会使用表锁
    • 支持外键
  • MyISAM引擎
    • 5.1版本之前MyISAM引擎是默认的存储引擎
    • 拥有全文索引、空间函数和压缩
    • 不支持事务和行级锁,不支持崩溃后的安全恢复
    • 表存储的两个文件MYD和MTI
    • 设计简单,再某些场景下性能会很好

InnoDB 适用场景: 需要事务的操作;更新数据需要使用行级锁;大数据量读写。 MyISAM适用场景: 不需要事务的操作;批量插入快;更新少;统计和读取频繁。

Redis常用类型,分别适合那些场景

String(字符串)

    特点:字符串的一个常见用途就是用来缓存用户信息,我们将用户信息结构体使用JSON序列化成字符串,然后将序列化后的字符串塞进Redis来缓存,取用户信息的时候再经过一次反序列化过程。

Hash(哈希)

特点:Hash是一个键值对(K/V)合集,特别适合用于存储对象。

    相比字符串序列化的存储信息更加直观。更新和获取数据时不需要像字符串那样全部取出来,只需要取出或者写入需要的部分即可。但是哈希结构的存储消耗高于单个字符串。

list(列表)

特点:简单的字符串列表,按照插入的顺序进行排序

    列表结构将常被用来做异步队列使用,将需要延后处理的任务结构体序列化成字符串,塞进Redis的列表,另一个线程通过轮询对列表中的数据进行处理。

set(集合)

特点:set是String的无序合集,和列表不同的是集合中不允许有重复的元素

    集合适合用来存储某些活动的用户ID,因为有去重功能,可以保证同一个用户不会中奖两次。

    黑名单/白名单功能,`sismember` 命令可以方便的判断这个条件是否存在于黑名单/白名单之中

zset(有序集合)

特点:保留了set中不允许有重复元素的特点,但不同的是Zset是可以排序的,与列表的索引下标作为排序依据不同,Zset可以给每个元素设置一个数字,作为排序依据。

    适合用来做排行榜这类的功能,比如:
  • 粉丝列表,根据粉丝关注时间来排序;
  • 热搜榜单:根据热搜点击次数来排序
  • 成绩榜单:根据学生的成绩分数排序

写出冒泡排序

冒泡排序很简单,就直接上代码吧

<?php
function Bubble(Array $arr){
    $length=count($arr) - 1;
    for ($i=0; $i < $length; $i++) { 
        for ($j=0; $j < $length; $j++) { 
            if($arr[$j] > $arr[$j+1]){
                $temp=$arr[$j];
                $arr[$j]=$arr[$j+1];
                $arr[$j+1]=$temp;
            }
        }
    }
    return $arr;
}

// 设置一个数组
$arr=[5,3,6,8,1,2,4,9,7];
var_dump(Bubble($arr));