PHP面试题总结

PHP基础方面

php常用数组、字符串函数

php文件操作函数

PSR规范

PSR 是由 PHP FIG 组织制定的 PHP 规范,是 PHP 开发的实践标准。
文档地址 https://learnku.com/docs/psr

- PSR0 自动加载已经废弃,psr4取代
- PSR1 基础编码规范
- PSR2 编程风格规范
- PSR3 日志接口规范
- PSR4 自动加载规范
- PSR6 缓存接口规范
- PSR7 HTTP消息接口规范 
- PSR8
- PSR9
- PSR10
- PSR11 容器接口
- PSR12 编码规范补充
- PSR13 超媒体链接
- PSR14 事件分发器
- PSR15 HTTP请求处理器
- PSR16 缓存接口
- PSR17 HTTP工厂
- PSR18 HTTP客户端

PHP7新特性

  • 标量类型声明
  • 返回值类型声明
  • NULL 合并运算符
  • 太空船操作符(组合比较符)
//太空船操作符用于比较两个表达式。当$a大于、等于或小于$b时它分别返回-1、0或1。
 1; // 0
echo 1 <=> 2; // -1
echo 2 <=> 1; // 1

// 浮点型
echo 1.5 <=> 1.5; // 0
echo 1.5 <=> 2.5; // -1
echo 2.5 <=> 1.5; // 1
 
// 字符串
echo "a" <=> "a"; // 0
echo "a" <=> "b"; // -1
echo "b" <=> "a"; // 1
  • 通过 define() 定义常量数组
  • 匿名类
  • Unicode codepoint 转译语法
  • 整除
  • use 加强
use some\namespace\{ClassA, ClassB, ClassC as C};
use function some\namespace\{fn_a, fn_b, fn_c};
use const some\namespace\{ConstA, ConstB, ConstC};
  • 一次捕捉多种类型的异常 / 错误
try {
      throw new LengthException("LengthException");
    //   throw new DivisionByZeroError("DivisionByZeroError");
    //   throw new Exception("Exception");
} catch (\DivisionByZeroError | \LengthException $e) {
    echo "出错消息 --- ", $e->getMessage(), PHP_EOL;
} catch (\Exception $e) {
    echo "出错消息 --- ", $e->getMessage(), PHP_EOL;
} finally {
    // ...
}
  • 可见性修饰符的变化
  1. 函数/方法:public、private、protected、abstract、final
  2. 类:abstract、final
  3. 属性/变量:public、private、protected
  4. 类常量:public、private、protected

PHP_FPM性能调优

  • pm = dynamic: 子进程的数量根据以下配置动态设置
    pm.max_children,
    pm.start_servers,
    pm.min_spare_servers,
    pm.max_spare_servers
  • pm = ondemand: 进程在请求时按需创建,而不是动态的,其中 pm.start_servers 进程数量在服务启动时创建
  • pm = static: 子进程的数量由 pm.max_children 决定

当流量波动比较大的时候,,PHP-FPM 的 ondemand 和 dynamic 会因为固有开销而限制吞吐量。 您需要了解您的系统并设置 PHP-FPM 进程数,以匹配服务器的最大容量。
从 pm.max_children 开始,根据 pm dynamic 或 ondemand 的最大使用情况去设置

您会注意到,在 pm static 模式下,因为您将所有内容都保存在内存中,所以随着时间的推移,流量峰值会对 CPU 造成比较小的峰值,并且您的服务器负载和 CPU 平均值将变得更加平滑。 每个需要手动调整的 PHP-FPM 进程数的平均大小会有所不同

Session 回收机制

session 存储方式:文件|memcache|redis|数据库
设定一个30分钟过期的session 如果设置key 过期时间30分钟即可
如果文件形式,php gc机制有一定的触发几率,30分钟临界点不一定会过期

session.save_handler= file
session.gc_probability  = 1
sesssion.gc_divisor = 1000
session.gc_maxfiletime= 1440 //设置过期时间24分钟
//过期的概率  session.gc_probability / session.gc_divisor
session.save_path = 

php垃圾回收机制

php变量底层定义

struct _zval_struct {
    union {
        long lval;
        double dval;
        struct {
            char *val;
            int len;
        } str;
        HashTable *ht;
        zend_object_value obj;
    } value;                    //变量value值
    zend_uint refcount__gc;   //引用计数内存中使用次数,为0删除该变量
    zend_uchar type;            //变量类型
    zend_uchar is_ref__gc;    //区分是否是引用变量
};

变量容器

  • 非array和object变量

每次将常量赋值给一个变量时,都会产生一个变量容器

举例:

$a = '许铮的技术成长之路';
xdebug_debug_zval('a')
a: (refcount=1, is_ref=0)='许铮的技术成长之路'
  • array和object变量

会产生元素个数+1的变量容器

举例:

$b = [
'name' => '许铮的技术成长之路',
'number' => 3
];
b: (refcount=1, is_ref=0)=array ('name' => (refcount=1, is_ref=0)='许铮的技术成长之路', 'number' => (refcount=1, is_ref=0)=3)
xdebug_debug_zval('b')

垃圾回收机制:
1、以php的引用计数机制为基础(php5.3以前只有该机制)
2、同时使用根缓冲区机制,当php发现有存在循环引用的zval时,就会把其投入到根缓冲区,当根缓冲区达到配置文件中的指定数量后,就会进行垃圾回收,以此解决循环引用导致的内存泄漏问题

函数 static 和 global 关键字的作用,如:

function foo()
{
    static $a = 111;
    global $b
}

static 是静态变量,在局部函数中存在且只初始化一次,使用过后再次使用会使用上次执行的结果
static 在类中是静态方法,不需要实例化可直接使用
global 关键字引用全局变量
$GLOBAL 在函数内使用具有全局作用域的变量,$GLOBAL['a']

子类重写父类的 protected 方法有什么限制?要遵守什么规则?

public 类内,类外,可以访问
protected 类内,子类的类内,可以访问
private 只有当前类的类内,可以访问

子类可以继承父类私有属性,不能继承私有方法
子类继承自父类的属性方法,子类必须和父类的修饰相同,或者更松弛。
父类为 public,则子类必须为 public。
父类为 protected,子类需为 public 或 protected。
父类为 private,则子类可为 public protected private

列举常用的设计模式并说明?单例模式,观察者模式等等

- 单例模式构造方法是私有的
- 实例用静态变量存储
- 不能序列化或克隆
final class Singleton{
    private  static $instance;
    private function __construct(){}
    public static function getInstance()
    {
        if(null === static::$instance){
            static::$instance = new static();
        }
        return static::$instance;
    }
    private function __clone(){}
    private function __wakeup(){}
}

写一段代码,实现PHP内部的通知机制,如当一个类的属性发生变化时,另外一个类就可以收到通知。

抽象类和接口的区别

定义:

  • 接口

    1. 接口是个集合,并不是类
    2. 接口中没有构造方法
    3. 接口中的方法必须是抽象的
    4. 接口中除了static、final变量,不能有其他变量
    5. 接口支持多继承(一个类可以实现多个接口)
  • 抽象类

    1. 使用abstract修饰符修饰的类
    2. 一个抽象类不能实例化,因为“没有包含足够多的信息来描述一个具体的对象“

区别:

  • 抽象类使用extends关键字来继承抽象类,子类使用关键字implements来实现接口
  • 抽象类可以有构造方法,而接口不能有构造方法
  • 抽象方法可以有public、protected和default这些修饰符,接口方法默认修饰符是public。不可以使用其它修饰符
  • 抽象方法比接口速度要快(接口是稍微有点慢的,因为它需要时间去寻找在类中实现的方法)
  • 抽象类中添加新的方法,可以给它提供默认的实现。因此你不需要改变你现在的代码。 如果在接口中添加方法,那么必须改变实现该接口的类

依赖注入、控制翻转

进程、线程、协程

大数据量文件读取

使用yield迭代器

function getLine($fileName){
    $f = fopen($fileName,'r');
    try {
        while ($data = fgets($f)){
            yield $data;
        }
    } finally {
        fclose($f);
    }
}

$mem = memory_get_usage();
$path = "./bbb/aaa.mp4";
foreach (getLine($path) as $k=>$v){
    //
}

$memEnd = memory_get_usage();
echo ($memEnd - $mem) /1024/1024 . "MB";

断点续传原理及实现

主要依靠HTTP协议中 header HTTP_RANGE实现.
Http Header中 Range、Content-Range()
Http Header中一般断点下载时才用到Range和Content-Range实体头,
Range用户请求头中,指定第一个字节的位置和最后一个字节的位置,如(Range:200-300)
Content-Range用于响应头

举例:

getRange($file_size);
            header('cache-control:public');
            header('content-type:application/octet-stream');
            header('content-disposition:attachment; filename='.$name);
            if($reload && $ranges!=null){ // 使用续传
                header('HTTP/1.1 206 Partial Content');
                header('Accept-Ranges:bytes');
                // 剩余长度
                header(sprintf('content-length:%u',$ranges['end']-$ranges['start']));
                // range信息
                header(sprintf('content-range:bytes %s-%s/%s', $ranges['start'], $ranges['end'], $file_size));
                // fp指针跳到断点位置
                fseek($fp, sprintf('%u', $ranges['start']));
            }else{
                header('HTTP/1.1 200 OK');
                header('content-length:'.$file_size);
            }
            while(!feof($fp)){
                echo fread($fp, round($this->_speed*1024,0));
                ob_flush();
            }
            ($fp!=null) && fclose($fp);
        }else{
            return '';
        }
    }
    /**设置下载速度
     * @param int $speed
     */
    public function setSpeed($speed){
        if(is_numeric($speed) && $speed>16 && $speed<4096){
            $this->_speed = $speed;
        }
    }
    /**获取header range信息
     * @param  int   $file_size 文件大小
     * @return Array
     */
    private function getRange($file_size){
        if(isset($_SERVER['HTTP_RANGE']) && !empty($_SERVER['HTTP_RANGE'])){
            $range = $_SERVER['HTTP_RANGE'];
            $range = preg_replace('/[\s|,].*/', '', $range);
            $range = explode('-', substr($range, 6));
            if(count($range)<2){
                $range[1] = $file_size;
            }
            $range = array_combine(array('start','end'), $range);
            if(empty($range['start'])){
                $range['start'] = 0;
            }
            if(empty($range['end'])){
                $range['end'] = $file_size;
            }
            return $range;
        }
        return null;
    }

} 

快排、冒泡、二分

  1. 快排
 $v){
             $up[] = $v[$i];
         }else{
             $low[] = $v[$i];
         }
     }
     $low = quickSort($low);
     $up = quickSort($up);
    return array_merge($low, array($v), $up);
}
  1. 冒泡
循环比较|交换键值
function bubbleSort($arr){
    $len = count($arr);
    for ($i= 0;$i<$len;$i++){
        for ($j = $i+1;$j < $len;$j++){
            if($arr[$i] < $arr[$j]){
                list($arr[$j],$arr[$i]) = [$arr[$i],$arr[$j]];
            }
        }
    }
}

3.二分

二分查找也称折半查找(Binary Search),它是一种效率较高的查找方法。但是,折半查找要求线性表必须采用顺序存储结构,而且表中元素按关键字有序排列
  1. 确定要查找的区间
  2. 确定要二分时的参照点
  3. 区间内选取二分点
  4. 根据二分点的值,综合左右区间情况以及求解的目的,舍去一半无用的区间
  5. 继续在有效区间重复上面的步骤
/**
 * @param array $arr 待查找区间
 * @param int $number 查找数
 * @param int $lower 区间最低点
 * @param int $high 区间最高点
 * @return int
 */
function binarySearch(&$arr, $number, $lower, $high){
    $middle = intdiv(($lower + $high) / 2);
    if($lower > $high) return -1;
    if($number > $arr[$middle]){
        binarySearch($arr,$number,$middle+1,$high);
    }elseif ($number > $arr[$middle]){
        binarySearch($arr,$number,$lower,$arr[$middle]-1);
    }else{
        return $middle;
    }
}

PHP文件末尾是否应该加 ?> 结束符号,为什么?

如果文件内容是纯 PHP 代码,最好在文件末尾删除 PHP
结束标记。这可以避免在 PHP 结束标记之后万一意外加入了空格或者换行符,会导致
PHP 开始输出这些空白,而脚本中此时并无输出的意图。
这些影响最多的时候应该是在使用 include 和 require的时候,加了结束标签 如果又在后面加了空格都有可能会引起多余的输出、php错误、之后的输出无法显示、空白页。因此,所有的php文件应该省略这个php闭合标签,并插入一段注释来标明这是文件的底部并定位这个文件在这个应用的相对路径。这样有利于你确定这个文件已经结束而不是被删节的。

谈一谈 PHP 开源框架 CI,ThinkPHP,Laravel 的优缺点及选型依据

CI 非常轻量级,是一个简单的MVC框架,性能也很快。
ThinkPHP3.2 国内使用比较多,优点是文档非常多,各种问题解决方案比较多,缺点是代码不够规范,理念落后。
Laravel 是一个现代化的PHP开发框架,代码优雅,使用 composer 方式扩展功能,社区活跃,缺点是比较重,比较适合做后台管理或者应用型WEB系统。

使用 PHP 下载网络图片,有哪些方法?

  1. 使用file_get_contents
  2. 使用curl
  3. 使用fopen

什么是 CGI?什么是 FastCGI?php-fpm,FastCGI,Nginx 之间是什么关系?

  1. CGI:是 Web Server 与 Web Application 之间数据交换的一种协议。
  2. FastCGI:同 CGI,是一种通信协议,但比 CGI 在效率上做了一些优化。同样,SCGI 协议与 FastCGI 类似。
  3. PHP-CGI:是 PHP (Web Application)对 Web Server 提供的 CGI 协议的接口程序。
  4. PHP-FPM:是 PHP(Web Application)对 Web Server 提供的 FastCGI 协议的接口程序,额外还提供了相对智能一些任务(fastcgi的进程管理器)

PHP读取文件的三种方法

fopen打开文件,fread全部读取内容
file_get_contents全部读取内容

web安全

CSRF攻击原理和防范

CSRF(Cross-site request forgery,跨站请求伪造),是通过伪造请求,冒充用户在站内进行操作
原因 
CSRF 攻击之所以能够成功:是因为完全伪造用户的请求,该请求中所有的用户验证信息都是存在于 cookie 中,因此黑客可以在不知道这些验证信息的情况下直接利用用户自己的 cookie 来通过安全验证
    
解决办法:在请求中放入黑客所不能伪造的信息,并且该信息不存在于 cookie 之中
- 验证 HTTP Referer 字段
- 关键操作只接受POST请求,因为GET请求的参数携带在URL中,很容易进行模拟,而POST请求的参数在http body中
- 验证码,每次操作都需要用户进行互动,从而简单有效的防御了CSRF攻击,但是验证码太多,也会影响用户体验
- 可以在 HTTP 请求中以参数的形式加入一个随机产生的 token,并在服务器端来验证这个 token
- 可以在 HTTP 头中自定义的属性里加入一个随机产生的 token,通过XMLHttpRequest,可以给所有请求加上这个token,通过XMLHttpRequest 请求的地址不会被记录到浏览器的地址栏,也不用担心 token 会透过 Referer 泄露到其他网站中
- 

XSS攻击原理和防范

XSS(Cross Site Scripting,跨站脚本攻击),是注入攻击的一种,特点是不对服务器端造成任何伤害,而是通过一些正常的站内交互途径
所有可输入的地方没有对输入数据进行处理的话,都会存在XSS漏洞,防御 XSS 攻击最简单直接的方法,就是过滤用户的输入可以直接对用户的输入进行转义(htmlspecialchars|)

SQL注入攻击防范

Pdo的实现原理

密码哈希

计算机网络

HTTP协议

HTTPS

TCP/IP协议

WebSocket连接过程

webSocket 是一个持久化协议,webSocket 是基于HTTP协议的,或者说 借用 HTTP的协议来完成一部分握手。

  1. 发送一个GET请求;header信息包含以下

    • Upgrade: websocket
    • Connection: Upgrade
  2. 服务器收到了协议,返回一个 Switching Protocol, 这样就连接成功了
  3. 接下来的通信都是websocket

HTTP常见状态码

- 100 Continue  继续。客户端应继续其请求
- 101 Switching Protocols  切换协议。服务器根据客户端的请求切换协议。只能切换到更高级的协议,例如,切换到HTTP的新版本协议
- 200 OK  请求成功。一般用于GET与POST请求
- 201 Created 已创建。成功请求并创建了新的资源
- 202 Accepted             已接受。已经接受请求,但未处理完成
- 203 Non-Authoritative Information 非授权信息。请求成功。但返回的meta信息不在原始的服务器,而是一个副本
- 204 No Content          无内容。服务器成功处理,但未返回内容。在未更新网页的情况下,可确保浏览器继续显示当前文档
- 205 Reset Content  重置内容。服务器处理成功,用户终端(例如:浏览器)应重置文档视图。可通过此返回码清除浏览器的表单域
- 206 Partial Content       部分内容。服务器成功处理了部分GET请求
- 300 Multiple Choices  多种选择。请求的资源可包括多个位置,相应可返回一个资源特征与地址的列表用于用户终端(例如:浏览器)选择
- 301 Moved Permanently     永久移动。请求的资源已被永久的移动到新URI,返回信息会包括新的URI, 浏览器会自动定向到新URI。今后任何新的请求都应使用新的URI代替
- 302 Found临时移动。与301类似。但资源只是临时被移动。客户端应继续使用原有URI
- 303 See Other             查看其它地址。与301类似。使用GET和POST请求查看
- 304 Not Modified        未修改。所请求的资源未修改,服务器返回此状态码时,不会返回任何资源。客户端通常会缓存访问过的资源,通过提供一个头信息指出客户端希望只返回在指定日期之后修改的资源
- 305 Use Proxy             使用代理。所请求的资源必须通过代理访问
- 306 Unused                已经被废弃的HTTP状态码
- 307 Temporary Redirect    临时重定向。与302类似。使用GET请求重定向
- 400 Bad Request       客户端请求的语法错误,服务器无法理解
- 401 Unauthorized     请求要求用户的身份认证
- 402 Payment Required  保留,将来使用
- 403 Forbidden         服务器理解请求客户端的请求,但是拒绝执行此请求
- 404 Not Found         服务器无法根据客户端的请求找到资源(网页)。通过此代码,网站设计人员可设置"您所请求的资源无法找到"的个性页面
- 405 Method Not Allowed 客户端请求中的方法被禁止
- 406 Not Acceptable   服务器无法根据客户端请求的内容特性完成请求
- 407 Proxy Authentication Required 请求要求代理的身份认证,与401类似,但请求者应当使用代理进行授权
- 408 Request Time-out     服务器等待客户端发送的请求时间过长,超时
- 409 Conflict  服务器完成客户端的 PUT 请求时可能返回此代码,服务器处理请求时发生了冲突
- 410 Gone  客户端请求的资源已经不存在。410不同于404,如果资源以前有现在被永久删除了可使用410代码,网站设计人员可通过301代码指定资源的新位置
- 411 Length Required   服务器无法处理客户端发送的不带Content-Length的请求信息
- 412 Precondition Failed       客户端请求信息的先决条件错误
- 413 Request Entity Too Large    由于请求的实体过大,服务器无法处理,因此拒绝请求。为防止客户端的连续请求,服务器可能会关闭连接。如果只是服务器暂时无 法处理,则会包含一个Retry-After的响应信息
- 414 Request-URI Too Large       请求的URI过长(URI通常为网址),服务器无法处理
- 415 Unsupported Media Type  服务器无法处理请求附带的媒体格式
- 416 Requested range not satisfiable 客户端请求的范围无效
- 417 Expectation Failed    服务器无法满足Expect的请求头信息
- 500 Internal Server Error 服务器内部错误,无法完成请求
- 501 Not Implemented       服务器不支持请求的功能,无法完成请求
- 502 Bad Gateway   作为网关或者代理工作的服务器尝试执行请求时,从远程服务器接收到了一个无效的响应
- 503 Service Unavailable   由于超载或系统维护,服务器暂时的无法处理客户端的请求。延时的长度可包含在服务器的Retry-After头信息中
- 504 Gateway Time-out            充当网关或代理的服务器,未及时从远端服务器获取请求
- 505 HTTP Version not supported  服务器不支持请求的HTTP协议的版本,无法完成处理

三次握手 4次挥手

网络7层模型

Mysql

Redis

redis常见数据结构及使用场景

redis数据结构的底层实现

redis持久化

Memcache 和 Redis 的读写性能(qps)如何?两者优缺点?Redis 支持哪些数据类型?Redis 如何持久化?

读写性能:memcache更加快速,在读取性能上比 Redis 快,缺点是仅支持字符串。
Redis支持丰富的数据结构类型,字符串,散列(哈希),集合,有序集合,还支持订阅发布,地理位置等等。
实际运用中可以redis,memcache结合,memcache可作为session存储的方式,session都是KV类型键值对。

Redis 提供了多种不同级别的持久化方式:
1、RDB 持久化可以在指定的时间间隔内生成数据集的时间点快照(point-in-time snapshot)。
2、AOF 持久化记录服务器执行的所有写操作命令,并在服务器启动时,通过重新执行这些命令来还原数据集。 AOF 文件中的命令全部以 Redis 协议的格式来保存,新命令会被追加到文件的末尾。 Redis 还可以在后台对 AOF 文件进行重写(rewrite),使得 AOF 文件的体积不会超出保存数据集状态所需的实际大小。
3、Redis 还可以同时使用 AOF 持久化和 RDB 持久化。 在这种情况下, 当 Redis 重启时, 它会优先使用 AOF 文件来还原数据集, 因为 AOF 文件保存的数据集通常比 RDB 文件所保存的数据集更完整。

Nginx

nginx重写

nginx 调优

负载均衡

你可能感兴趣的:(php,面试)