美文网首页php
PHP面试题收集

PHP面试题收集

作者: 皮蛋馅儿 | 来源:发表于2020-03-05 22:38 被阅读0次

    一、字符串的定义方式和各自区别

    • 单引号
      单引号不能解析变量
      不能解析转义字符,只能解析单引号和反斜线本身
      效率高
    • 双引号
      可以解析变量,变量可以使用特殊字符和{}包含
      可以解析所有的转义字符
    • Heredoc
      类似于双引号,用来处理大文本
    $str = <<<EOF
    
    EOF;
    
    • Newdoc
      类似于单引号,用来处理大文本
    $str = <<<'EOF'
    
    EOF;
    

    二、三大数据类型

    • 标量
      整型(integer)、布尔(boolean)、浮点(float)、字符串(string)
    • 复合
      数组(array)、对象(object)
    • 特殊
      资源(resource)、空值(NULL)
      注意:
      ①浮点类型不能用到比较运算中a=0.1;b=0.7; if (a +b == 0.8) 返回false
      ②放在 if 条件判断返回FLASE的七种情况
    整型0,  浮点0.0,  空字符串'',  0字符串'0',  布尔false ,  空数组array(),  NULL
    

    ③超全局数组

    $GLOBALS    $_SERVER    $_GET    $_POST 
    $_FILES     $_COOKIE    $_SESSION    $_REQUEST    $_ENV
    
    $_SERVER['SERVER_ADDR']   服务端的ip地址
    $_SERVER['REMOTE_ADDR']   客户端的ip地址
    
    获取客户端 IP 都要依次获取 HTTP_CLIENT_IP 、HTTP_X_FORWARDED_FOR 和 REMOTE_ADDR 
    
    HTTP_CLIENT_IP: 头是有的,只是未成标准,不一定服务器都实现了。
    
    X-Forwarded-For(XFF):  是用来识别通过http代理或者负载均衡连接到web服务器的客户端最原始的IP地址的HTTP请求头字段,格式:clientip,proxy1,proxy2
    
    REMOTE_ADDR: 是可靠的, 它是最后一个跟你的服务器握手的IP,可能是用户的代理服务器,也可能是自己的反向代理。
    
    

    ④为NULL的三种情况
    直接赋值为null、未定义的变量、unset销毁的变量
    ⑤常量定义(常量一经定义,不能修改,不能删除)
    const 更快,是语言结构,可以定义类常量
    define 是函数,不能定义类常量
    预定义常量

    __FILE__ 文件的完整路径和文件名。如果用在被包含文件中,则返回被包含的文件名
    __LINE__ 文件中的当前行号
    __DIR__文件所在的目录。如果用在被包括文件中,则返回被包括的文件所在的目录
    __FUNCTION__ 函数名称
    __CLASS__ 类的名称
    __TRAIT__ Trait 的名字
    __METHOD__ 类的方法名
    __NAMESPACE__ 当前命名空间的名称(区分大小写)
    

    三、内置函数(参见PHP官网的字符串和数组内置函数)

    array_shift() 删除数组第一个元素;并返回被删除元素的值
    array_pop() 删除数组最后一个元素;并返回被删除元素的值
    array_unshift() 向数组头部插入新元素;返回新数组元素的个数
    array_push() 向数组尾部插入新元素;返回新数组元素的个数
    

    四、正则表达式
    作用:分割、查找、匹配、替换字符串

    ========== 匹配139开头的手机号码==========
    '/^139\d{8}$/'
    
    ==========匹配img标签里面的src的值==========
    '/<img.*?src="(.*?)".*?\/?>/i'
    注意:
    (.*)是后向引用
    前面加?是禁止贪婪,最后一个问号表示重复前面内容的0次或一次,
    

    五、文件目录操作

    ===================== 在一个文件的开头加入指定字符串=====================
    // 打开文件
    $file = 'file.txt';
    $handle = fopen($file, 'r');
    // 将文件的内容读取出来,在开头加入hello
    $content = fread($handle, filesize($file));
    $content = 'hello~' . $content;
    fclose($handle);
    // 将拼接好的字符串写回文件
    $handle = fopen($file, 'w');
    fwrite($handle, $content);
    fclose($handle);
    
    
    =====================遍历一个目录下的所有文件=====================
    public function loopDir($dir)
    {
        $array = [];
        if (is_dir($dir)) {
            // 打开目录
            $handle = opendir($dir);
            while ($file = readdir($handle)) {
                if ($file == '.' || $file == '..') {
                    continue;
                }
    
                $fileDir = $dir . '/' . $file;
                if (is_file($fileDir)) {
                    $array[] = $file;
                } else if (is_dir($fileDir)) {
                    $array[$file] = $this->loopDir($fileDir);
                }
            }
            closedir($handle);
        }
        return $array;
    }
    

    六、会话控制
    =======================SESSION和COOKIE =======================
    SESSION存储在服务器端,COOKIE保存在客户端。Session比较安全,cookie用某些手段可以修改,不安全。Session依赖于cookie进行传递。禁用cookie后,session还可以使用,在存储session的文件中,生成sessionID,通过get传参的方式将sessionID传到要实现session共享的页面,读取sessionID,从而从session中获取数据。

    关闭cookie,使用session的方法
    设置php.ini配置文件中的“session.use_trans_sid = 1”,或者编译时打开打开了“--enable-trans-sid”选项,让PHP自动跨页传递Session ID。
    手动通过URL传值、隐藏表单传递Session ID。
    用文件、数据库等形式保存Session ID,在跨页过程中手动调用。

    总结一下,上面的方法有一个共同点,就是在前一页取得Session ID,然后想办法传递到下一页,在下一页的session_start();代码之前加代码Session ID(传过来的Session ID)。

    =======================session的垃圾回收机制=======================
    session.gc_maxlifetime = 1440(Session数据在服务器端储存的时间)
    session.gc_probability = 1
    session.gc_divisor = 100

    session.gc_divisor 与 session.gc_probability 合起来定义了在每个会话初始化时启动 gc(garbage collection 垃圾回收)进程的概率。此概率用 gc_probability/gc_divisor 计算得来。例如 1/100 意味着在每个请求中有 1% 的概率启动 gc 进程。session.gc_divisor 默认为 100。

    GC 的工作,就是扫描所有的session信息,用当前时间减去session的最后修改时间(modifieddate),同session.gc_maxlifetime参数进行比较,如果生存时间已经超过gc_maxlifetime,就把该session删除

    七、面向对象

    • PHP类权限控制修饰符:public、protected、private

    • PHP单继承

    • 抽象类和接口
      抽象类:任何一个类,如果它里面至少有一个方法是被声明为抽象的,那么这个类就必须被声明为抽象的。抽象方法没有方法体。
      继承一个抽象类的时候,子类必须定义父类中的所有抽象方法;另外,这些方法的访问控制必须和父类中一样(或者更为宽松)。
      接口:是通过 interface 关键字来定义的,接口中的方法也是没有方法体。接口中不可以声明变量,但可以声明类常量。
      接口中定义的所有方法都必须是公有,这是接口的特性。
      需要注意的是,在接口中定义一个构造方法是被允许的。在有些场景下这可能会很有用,例如用于工厂模式时。
      要实现一个接口,使用 implements 操作符。类中必须实现接口中定义的所有方法,否则会报一个致命错误。类可以实现多个接口,用逗号来分隔多个接口的名称。

    • 魔术方法

    1. __construct  具有构造函数的类会在每次创建新对象时先调用此方法
    2. __destruct  对象的所有引用都被删除或者当对象被显式销毁时执行。
    3.__call()  在对象中调用一个不可访问方法时会被调用。
    4.__callStatic()  在静态上下文中调用一个不可访问方法时会被调用。
    5.__set()   在给不可访问的属性赋值时调用
    6.__get()  读取不可访问的属性值时自动调用
    7.__isset()  当对不可访问的私有属性使用isset或empty时自动调用
    8.__unset()  当对不可访问的私有属性使用unset时自动调用
    9.__toString() 当一个类的实例对象被当成一个字符串输出时调用
    10.__invoke()将一个PHP对象当成一个函数执行时会回调
    11.__clone()克隆一个对象
    
    • 设计模式

    1.工厂模式,工厂方法或者类生成对象,而不是在代码中直接new

    class Factory
    {
        public static function createDatabase()
        {
            $db = new Database();
            return $db;
        }
    }
    
    调用:Factory::createDatabase();
    

    查看PHP工厂模式
    使用场景:假如很多个地方都用到同个类的new操作,如果修改了这个类的名称或者这个类的一些参数发生了一些变化,那么每个地方都要修改,用工厂模式可解决此问题

    2.单例模式,使某个类的对象仅允许创建一个
    查看PHP单例模式

    3.注册模式,全局共享和交换对象
    查看PHP注册模式

    4.适配器模式,将截然不同的函数接口封装成统一的API
    查看PHP适配器模式

    5.策略模式,将一组特定的行为和算法封装成类,以适应某些特定的上下文环境
    查看策略模式

    八、网络协议
    HTTP协议状态码
    常见状态码:

    • 1xx 信息提示
      100 (Continue/继续)
      101 (Switching Protocols/转换协议)

    • 2xx 成功
      200 (OK/正常)
      201 (Created/已创建)
      202 (Accepted/接受)
      204 (No Content/无内容)
      206 (Partial Content/局部内容)

    • 3xx 重定向
      300 (Multiple Choices/多重选择)
      301 (Moved Permanently/永久移动)
      302 (Found/找到/临时移动)
      307 (Temporary Redirect/临时重定向)

    • 4xx 客户端错误
      400 (Bad Request/错误请求)
      401 (Unauthorized/未授权)
      403 (Forbidden/禁止)
      404 (Not Found/未找到)
      405 (Method Not Allowed/方法未允许)

    • 5xx 服务器错误
      500 (Internal Server Error/内部服务器错误)
      501 (Not Implemented/未实现)
      502 (Bad Gateway/错误的网关)
      503 (Service Unavailable/服务无法获得,服务器在维护或已超载而无法响应)
      504 (Gateway Timeout/网关超时)

    网络7层协议(OSI七层模型)
    7 应用层 (协议有HTTP/HTTPS/FTP/SMTP/DNS等)
    6 表示层
    5 会话层
    4 传输层 (协议有TCP/UDP)
    3 网络层
    2 数据链路层
    1 物理层
    7、6、5、4层定义了应用程序的功能
    3、2、1层主要面向通过网络的端到端的数据流

    常见的网络协议含义以及端口

    • FTP(文件传输协议,默认端口21)
    • Telnet (用于远程登录的端口,用户可以远程链接计算机 23)
    • SMTP(定义了简单邮件传输协议 发送邮件 25)
    • POP3(主要用于接收邮件,端口110)
    • HTTP(超文本传输协议 端口80)
    • DNS(用于域名简析服务 53)

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

    WEB 中,
    Web Server 一般指Apache、Nginx、IIS、Lighttpd、Tomcat等服务器,
    Web Application 一般指PHP、Java、Asp.net等应用程序。

    九、AJAX的工作原理
    XMLHttpRequest是AJAX的基础
    XMLHttpRequest用于在后台与服务器交换数据
    客户端发送请求,请求交给xhr,xhr把请求提交给服务,服务器进行业务处理,服务器响应数据交给xhr对象,xhr对象接收数据,由javascript把数据写到页面上

    十、Linux
    Linux常用命令

    • 目录操作
      cd、mv、rm、pwd、tree、cp、ls
    • 文件查找和比较
      locate、find
    • 文件处理
      touch、unlink、rename、ln、cat
    • 压缩解压
      bzip2/bunzip2、gzip/gunzip、zip/unzip、tar
    • 系统安全
      sudo、su、chmod、setfacl
    • 进程管理
      w、top、ps、kill、pkill、pstree、killall
    • 系统关机和重启
      shutdown、reboot
    • 网络应用
      curl、telnet、mail、elinks
    • 网络测试
      ping、netstat、host
    • 软件包管理
      yum、rpm、apt-get

    解压
    tar -xvf file.tar //解压 tar包
    tar -xzvf file.tar.gz //解压tar.gz
    tar -xjvf file.tar.bz2 //解压 tar.bz2
    tar -xZvf file.tar.Z //解压tar.Z
    unrar e file.rar //解压rar
    unzip file.zip //解压zip

    总结
    1、.tar 用 tar -xvf 解压
    2、
    .gz 用 gzip -d或者gunzip 解压
    3、.tar.gz和.tgz 用 tar -xzf 解压
    4、.bz2 用 bzip2 -d或者用bunzip2 解压
    5、
    .tar.bz2用tar -xjf 解压
    6、.Z 用 uncompress 解压
    7、
    .tar.Z 用tar -xZf 解压
    8、.rar 用 unrar e解压
    9、
    .zip 用 unzip 解压

    系统定时任务
    crontab命令
    crontab -e # 创建一个任务调度,会进入到vi编辑界面,编写要调度的任务
    crontab -l # 列出定时的任务
    crontab -r con_name # 删除crontab文件
    which ifconfig # 获取命令路径

    * * * * * 命令(分 时 日 月 周)
    
    每天0点重启服务器
    0 0 * * * reboot
    

    十一、MySQL
    1、varchar和char的区别

    • 定长和变长
      char 长度固定,如果插入的长度小于定义长度时,则用空格填充;
      varchar长度可变,小于定义长度时,还是按实际长度存储。

    因为char长度固定,所以存取速度要比varchar快得多,但是char因为其长度固定,所以会占据多余的空间,可谓是以空间换取时间效率。varchar则刚好相反,以时间换空间。

    • 存储的容量不同
      varchar(M)和char(M),M都表示字符数,varchar的最大长度为65535个字符,不同的编码所对应的最大可存储的字符数不同,char最多可以存放255个字符

    2、数据库引擎
    InnoDB:数据存储在共享表空间,对主键查询的性能高于其他类型的存储引擎。
    支持事务处理,支持外键,支持行级锁,支持崩溃修复能力和并发控制。如果需要对事务的完整性要求比较高(比如银行),要求实现并发控制(比如售票),那选择InnoDB有很大的优势。如果需要频繁的更新、删除操作的数据库,也可以选择InnoDB,因为支持事务的提交(commit)和回滚(rollback)。

    MyISAM:拥有全文索引、压缩、空间函数。
    不支持事务和行级锁,不支持奔溃后的安全恢复,表存储在MYD和MYI两个文件,设计简单,某些场景下(比如 select count(*))性能很好。

    MEMORY:所有的数据都在内存中,数据的处理速度快,但是安全性不高。如果需要很快的读写速度,对数据的安全性要求较低,可以选择MEMOEY。它对表的大小有要求,不能建立太大的表。所以,这类数据库只使用在相对较小的数据库表。

    merge:用于日志和数据仓库

    archive:用于日志,只有select和insert,不支持索引。

    以下两点必须使用InnoDB:
    1)可靠性高或者要求事务处理,则使用InnoDB。这个是必须的。
    2)表更新和查询都相当的频繁,并且表锁定的机会比较大的情况指定InnoDB数据引擎的创建。
    对比之下,MyISAM的使用场景:
    1)做很多count的计算的。如一些日志,调查的业务表。
    2)插入修改不频繁,查询非常频繁的。

    3、MySQL索引
    索引对性能的影响

    • 大大减少服务器需要扫描的数据量
    • 帮助服务器避免排序和临时表
    • 将随机I/O变顺序I/O
    • 大大提高查询速度,降低写的速度,占用磁盘

    索引使用场景

    • 非常小的表,大部分情况下不使用索引效率更高
    • 中到大型表,索引非常有效
    • 特大型表,建立和使用索引的代价随之增长,可使用分区来解决

    索引类型

    • 普通索引:最基本的索引,没有任何约束限制
    • 唯一索引:与普通索引类似,但是有唯一性约束
    • 主键索引:特殊的唯一索引,不允许有空值
    • 组合索引:将多个列组合在一起创建索引,可以覆盖多个列
    • 外键索引:只有InnoDB类型的表才可以使用,保证数据的一致性、完整性和实现级联操作
    • 全文索引:MySQL自带的全文索引只能用于MyISAM,并且只能对英文进行全文检索

    索引创建原则:

    • 最适合索引的列是出现在where子句中的列,或连接子句中的列而不是出现在select关键字后的列
    • 索引列的基数越大,索引的效果越好
    • 对字符串进行索引,应该定制一个前缀长度,可以节省大量的索引空间
    • 根据情况创建复合索引,可以提供查询效率(比如查询第几章第几节第几段落)
    • 避免创建过多索引,索引会占用磁盘空间,降低写操作效率
    • 主键尽可能选择较短的数据类型

    通俗理解口诀:
    全值匹配我最爱,最左前缀要遵守;(复合索引)
    带头大哥不能死,中间兄弟不能断;(复合索引)
    索引列上少计算,范围之后全失效;
    LIKE百分写最右,覆盖索引不写星;
    不等空值还有or,索引失效要少用。

    4、分析SQL语句查询速度慢的方法

    • 记录慢查询日志
    • 使用explain【别名desc】
      分析单条SQL语句
    • 使用show profile,步骤如下:
      set profiling= 1;服务器上执行的所有语句会检测消耗时间,存到临时表
      show profiles
      show profile for query 临时表id
    • 使用show status
      show status会返回一些计数器,show global status查看服务器级别的所有计数
    • 使用show processlist
      观察是否有大量线程处于不正常的状态或者特征

    5、MySQL语句优化
    ①优化查询过程中的数据访问

    • 查询不需要的记录,使用limit解决
    • select 指定列
    • 重复查询相同的数据,可以使用缓存
    • 适当使用索引
    • 改变数据库和表的结构,适当使用逆范式

    ②优化长难的查询语句

    • 将一个大的查询分为多个小的相同的查询

    ③优化特定类型查询语句

    • 使用count(*),而不是使用count(指定列)
    • 使用关联查询代替子查询

    6、分区和分表
    分区:就是把一个数据表的文件和索引分散存储在不同的物理文件中。 表分区,是指根据一定规则,将数据库中的一张表分解成多个更小的、容易管理的部分。从逻辑上看,只有一张表,但是底层却是由多个物理分区组成。

    分表: 通过一定规则,将一张表(垂直或者水平)分解成多张不同的表,每一个小表都是完整的一张表

    分表与分区的区别:
    实现方式上:分区从逻辑上来讲只有一张表,而分表则是将一张表分解成多张表。

    数据处理上:分表后数据都是存放在分表里,总表只是一个外壳,存取数据发生在一个一个的分表里面。分区则不存在分表的概念,分区只不过把存放数据的文件分成了许多小块,分区后的表还是一张表,数据处理还是由自己来完成。

    提高性能上:分表后,单表的并发能力提高了,磁盘I/O性能也提高了。分区突破了磁盘I/O瓶颈,想提高磁盘的读写能力,来增加mysql性能。
    在这一点上,分区和分表的侧重点不同,分表重点是存取数据时,如何提高mysql并发能力上;而分区呢,如何突破磁盘的读写能力,从而达到提高mysql性能的目的。

    实现的难易度上:分表的方法有很多,用merge来分表,是最简单的一种方式。这种方式和分区难易度差不多,并且对程序代码来说可以做到透明的。如果是用其他分表方式就比分区麻烦了。 分区实现是比较简单的,建立分区表,跟建平常的表没什么区别,并且对代码端来说是透明的。
    分区和分表的目的就是减少数据库的执行负担,稳定SQL性能。

    分区
    mysql支持的分区类型包括Range、List、Hash、Key

    CREATE TABLE user (
        id INT NOT NULL auto_increment,
        username VARCHAR (10),
        PRIMARY KEY (id)
    ) ENGINE = INNODB charset = utf8 PARTITION BY RANGE (id)(
        PARTITION user_1 VALUES less than (10),
        PARTITION user_2 VALUES less than (20),
        PARTITION user_3 VALUES less than MAXVALUE
    );
    

    表分区好处:

    • 分区表的数据可以分布在不同的物理设备上,从而高效地利用多个硬件设备。
    • 和单个磁盘或者文件系统相比,可以存储更多数据
    • 优化查询。在where语句中包含分区条件时,可以只扫描一个或多个分区表来提高查询效率;涉及sum和count语句时,也可以在多个分区上并行处理,最后汇总结果。
    • 分区表更容易维护。例如:想批量删除大量数据可以清除整个分区。
    • 可以使用分区表来避免某些特殊的瓶颈,例如InnoDB的单个索引的互斥访问,ext3问价你系统的inode锁竞争等。

    分区限制

    • 一个表最多只能有1024个分区
    • 分区字段中如果有主键和唯一索引列,那么主键列和唯一列都必须包含进来
    • 分区表无法使用外键约束
    • 需要对现有表的结构进行修改
    • 所有的分区都必须使用相同的存储引擎
    • 对于MyISAM的分区表,不能使用load index into cache

    分表

    // 创建一个完整表存储着所有的成员信息
    CREATE TABLE `member` (
    `id`  int(10) UNSIGNED NOT NULL AUTO_INCREMENT ,
    `name`  varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' ,
    PRIMARY KEY (`id`)
    ) ENGINE=MyISAM DEFAULT CHARSET = utf8 AUTO_INCREMENT = 1;
    
    // 插入数据(第2条语句多执行几次就有了很多数据):
    insert into member(name) values('a');
    insert into member(name) select name from member;
    
    // 创建两个分表tb_member1,tb_member2
    DROP TABLE IF EXISTS tb_member1;
    CREATE TABLE tb_member1 (
    `id` INT (10) UNSIGNED NOT NULL AUTO_INCREMENT,
    `name` VARCHAR (20) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '',
      PRIMARY KEY (`id`)
    ) ENGINE = MyISAM DEFAULT CHARSET = utf8 AUTO_INCREMENT = 1;
    
    DROP TABLE IF EXISTS tb_member2;
    CREATE TABLE tb_member2 LIKE tb_member1;
    
    // 创建主表tb_member
    DROP TABLE IF EXISTS tb_member;
    CREATE TABLE tb_member (
    `id` INT (10) UNSIGNED NOT NULL AUTO_INCREMENT,
    `name` VARCHAR (20) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '',
      PRIMARY KEY (`id`)
    ) ENGINE = MERGE UNION = (tb_member1, tb_member2) INSERT_METHOD = LAST CHARSET = utf8 AUTO_INCREMENT = 1;
    
    // 把数据分到两个分表中去
    insert into tb_member1(id, name) select id,name from member where id%2=0;
    insert into tb_member2(id, name) select id,name from member where id%2=1;
    

    分表存在的问题:

    • 跨库跨表的join问题
    • 额外的数据管理负担和数据运算压力(数据的定位问题和数据的增删改查的重复执行问题)

    分表的几种常见方法:

    • 预先估计某个大表的数据量,将其均分为固定数量表(自增id取模、自增id两位尾数取模、对某个字段进行hash)
    • 时间增长较快的数据可以按时间拆分(按天、按月、按年等)
    • 按每个表固定记录行数拆分
    • 将很久之前的数据迁移到一张历史表

    7、MySQL主从复制
    MySQL之间数据复制的基础是二进制日志文件(binary log file)。
    Master 将改变记录到二进制日志中。
    Slave 将 Master 的二进制日志拷贝到它的中继日志( Relay_log )
    Slave 重做中继日志中的事件,将改变反映它自己的数据

    MySQL主从复制解决的问题

    • 数据分布:随意停止或开始复制,并在不同地理位置分布数据备份
    • 负载均衡:降低单个服务器的压力
    • 高可用和故障切换:帮助应用程序避免单点失败
    • 升级测试:可以使用更高版本的MySQL作为从库

    8、MySQL安全

    SQL查询安全解决方案

    • 使用预处理语句防止SQL注入
    • 写入数据库的数据要转义特殊字符
    • 查询错误信息不要返回给用户,将错误记录到日志

    MySQL其他安全设置

    • 定期做好数据备份
    • 关闭远程访问数据库权限
    • 修改root口令,不用默认口令
    • 改变root用户名称
    • 限制一般用户浏览其他库

    9、数据表创建命令

    CREATE TABLE `user` (
      `id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增id',
      `username` varchar(20) NOT NULL DEFAULT '' COMMENT '用户名',
      `password` char(32) NOT NULL DEFAULT '' COMMENT '密码',
      `phone` varchar(11) NOT NULL DEFAULT '' COMMENT '手机号码',
      `token` varchar(100) NOT NULL DEFAULT '' COMMENT 'app登录token',
      `create_time` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '添加时间',
      `status` tinyint(1) NOT NULL DEFAULT '0' COMMENT '状态,-1删除,0待审,1正常',
      PRIMARY KEY (`id`),
      KEY `phone` (`phone`) USING BTREE,
      KEY `token` (`token`) USING BTREE
    ) ENGINE=InonoDB DEFAULT CHARSET=utf8mb4;
    

    10、PHP连接数据库的方式

    • MySQL 只支持MySQL操作,不支持预处理,面向过程
    • MySQLi 只支持MySQL操作,支持预处理,面向对象和过程,效率较高
    • PDO 扩展性好,支持预处理,面向对象

    PDO操作数据库

    $pdo = new \PDO("mysql:host=$host;dbname=$dbname", $user, $name);
    $sql = 'SELECT id, username FROM user WHERE id=:id';
    $prepare = $pdo->prepare($sql);
    $prepare->execute([':id' => $id]);
    $result = $prepare->fetchAll(\PDO::FETCH_ASSOC);
    

    十二、MVC
    单一入口:用一个处理程序文件处理所有的HTTP请求,根据请求参数的不同区分不同模块和操作

    好处:

    • 可以进行统一的安全性检查
    • 集中处理程序

    框架对比
    ThinkPHP 5 是一个为API开发而设计的高性能框架,采用全新的架构思想,引入了很多的PHP新特性,优化了核心,减少了依赖,实现了真正的惰性加载,支持composer,并针对API开发做了大量的优化。
    规范:遵循PSR-2、PSR-4规范,Composer及单元测试支持;
    严谨:异常严谨的错误检测和安全机制,详细的日志信息;
    灵活:减少核心依赖,扩展更灵活、方便,支持命令行指令扩展;
    API友好:出色的性能和REST支持、远程调试,更好的支持API开发;
    高效:惰性加载,及路由、配置和自动加载的缓存机制;
    ORM:重构的数据库、模型及关联,MongoDb支持;

    Yii2是一个通用的 Web 编程框架,即可以用于开发各种用 PHP 构建的 Web 应用。 因为基于组件的框架结构和设计精巧的缓存支持,它特别适合开发大型应用, 如门户网站、社区、内容管理系统(CMS)、 电子商务项目和 RESTful Web 服务等。

    laravel框架的设计思想比较先进,非常适合应用各种开发模式,作为一个框架,它为你准备好了一切。
    laravel框架最大的特点和优秀之处就是集合了php比较新的特点,以及各种各样的设计模式,Ioc模式,依赖注入等。
    强大的rest router:用简单的回调函数就可以调用,快速绑定controller和router
    artisan:命令行工具,很多手动的工作都自动化
    可继承的模板,简化view的开发和管理
    blade模板:渲染速度更快
    ORM操作数据库
    migration管理数据库和版本控制
    composer也是亮点
    测试功能也很强大

    CodeIgniter 是一个小巧但功能强大的 PHP 框架,特点:简单、小巧、出色的性能、安全、几乎0配置

    十三、算法
    算法的特征:有穷性、确切性、输入项、输出项、可行性

    1、冒泡排序:两两相邻的数进行比较,如果反序就交换,否则不交换
    如数组:$sort = [6,1,2,4,5,3]; 进行冒泡排序(从小到大)
    第一轮:
     第一次:1,6,2,4,5,3
     第二次:1,2,6,4,5,3
     第三次:1,2,4,6,5,3
     第四次:1,2,4,5,6,3
     第五次:1,2,4,5,3,6

    第二轮:
     第一次:1,2,4,5,3,6
     第二次:1,2,4,5,3,6
     第三次:1,2,4,5,3,6
     第四次:1,2,4,3,5,6

    第三轮:
     第一次:1,2,4,3,5,6
     第二次:1,2,4,3,5,6
     第三次:1,2,3,4,5,6

    第四轮:
     第一次:1,2,3,4,5,6
     第二次:1,2,3,4,5,6

    第五轮:
     第一次:1,2,3,4,5,6

    <?php
    $arr = [6, 7, 3, 5, 9, 10, 1];
    $length = count($arr);
    // 轮
    for ($i = 1; $i < $length; $i++) {
        // 次
        for ($j = 0; $j < $length - $i; $j++) {
            if ($arr[$j] > $arr[$j + 1]) {
                $tmp = $arr[$j];
                $arr[$j] = $arr[$j + 1];
                $arr[$j + 1] = $tmp;
            }
        }
    }
    echo '<pre>';
    print_r($arr);
    

    2、时间复杂度计算方式:
    O(n^2)、 O(1)、 O(n)

    • 用常数1来取代所有时间中的所有加法常数(比如最终只计算5次,那么不是O(5)而是O(1))
    • 在修改后的运行次数函数中,只保留最高阶项(比如最终表达式是n^2+n+1,
      那么最终的时间复杂度就是O(n^2))
    • 如果最高阶存在且不是1,则去除与这个项相乘的常数(比如最终是2n^2,
      那么最终的时间复杂度就是O(n^2))

    3、

    // 把my_user_name转换成MyUserName
    $newStr = '';
    $str = 'my_user_name';
    $arr = explode('_', $str);
    foreach ($arr as $value) {
        $newStr .= ucfirst($value);
    }
    
    
    //  字符串反转
    第一种:$str ='abc';
    for ($i = 0; true; $i++) {
        if (!isset($str[$i])) break;
    }
    
    $newStr = '';
    for ($j = $i -1 ;$j >= 0; $j --) {
        $newStr .= $str[$j];
    }
    
    第二种: $str = 'abcde';
    $newStr = '';
    for ($i = strlen($str) - 1; $i >= 0; $i--) {
        $newStr .= $str[$i];
    }
    
    第三种:function mb_strrev($str)
    {
        $r = '';
        for ($i = mb_strlen($str); $i >= 0; $i--) {
            $r .= mb_substr($str, $i, 1);
        }
        return $r;
    }
    echo mb_strrev("☆❤world我"); 
    // 我dlrow❤☆
    
    
    // 实现array_merge(数组合并)
    function arrayMerge()
    {
        $return = [];
        $arrays = func_get_args();
        foreach ($arrays as $arr) {
            if (is_array($arr)) {
                foreach ($arr as $val) {
                    $return[] = $val;
                }
            }
        }
        return $return;
    }
    
    
    //奇偶数
    $a = [6, 7, 8, 9];
    $arr = array_filter($a, function ($v) {
        return ($v & 1);
    });
    返回:
    [
      1 => 7
      3 => 9
    ]
    
    
    // 读取文件里面的内容
    第一种:
    $file = 'E:\php1\wamp64\www\test.txt';
    $content = file($file);
    foreach ($content as $key => $value) {
        $value = mb_convert_encoding($value, 'utf-8', 'gbk');
        $v = explode("\t", $value);
    }
    
    第二种:
    $handle = fopen($file, 'r');
    if ($handle) {
        while (($row = fgets($handle)) !== false) {
            $row = mb_convert_encoding($row, 'utf-8', 'gbk');
            $v = explode("\t", $row);
        }
        fclose($handle);
    }
    
    
    // 24个字母排序字符串
    $arr = range('a', 'z');
    $str = implode($arr);// abcdefghijklmnopqrstuvwxyz
    
    
    // 交换两个变量的值
    $a = "aa";
    $b = "bb";
    var_dump([$a, $b]); // ['aa', 'bb']
    list($a, $b) = [$b, $a];
    var_dump([$a, $b]); ['bb', 'aa']
    

    十四、高并发
    QPS:每秒钟请求或者查询的数量,在互联网领域,指的是每秒响应请求数(HTTP请求),QPS不等于并发连接数,并发连接数是系统同时处理的请求数量

    吞吐量:单位时间内处理的请求数量(通常由QPS与并发数决定)

    PV:综合浏览量(Page View),即页面浏览量或者点击量,一个访客在24小时内访问的页面数量
    同一个人浏览网站的同一个页面,只记一次PV

    UV:独立访客(Unique Visitor),即一定时间范围内相同访客多次访问网站,只计算为1个独立访客

    带宽:计算带宽大小需要关注的两个指标,峰值流量和页面的平均大小

    压力测试
    测试能承受 最大并发
    测试最大承受的QPS值

    常用的性能测试工具:ab、wrk、http_load等

    ab(apache benchmark),是apache官方推出的工具。
    创建多个并发访问线程,模拟多个访问者同时对某一URL地址进行访问。
    它的测试目标是基于URL的,因此它既可以用来测试Apache的负载压力,还可以测试nginx、lighthttp、tomcat、IIS等其他web服务器的压力。

    注意:

    • 测试机器与被测试机器分开
    • 不要对线上服务做压力测试
    • 测试工具ab所在机器和被测试的前端机的CPU、内存、网络等都不超过最高限度的75%
    // 模拟并发请求100次,总共请求5000次
    ab -c 100 -n 5000 待测试网站
    

    高并发解决方案
    1、流量优化
    (1) 防盗链处理(去除恶意请求)
    (2) 控制大文件的下载。
    (3) 确认服务器硬件是否足够支持当前的流量
    (4) 使用流量分析统计软件
    (5) 尽量使用静态页,缓存

    2、前端优化
    (1) 减少HTTP请求[将css、js等合并、使用图片地图或者CSS精灵、图片Base64编码]
    (2) 添加异步请求(先不将所有数据都展示给用户,用户触发某个事件,才会异步请求数据)
    (3) 启用浏览器缓存和文件压缩
    (4) CDN加速
    (5) 建立独立的图片服务器(减少I/O)

    3、服务端优化
    (1) 页面静态化(使用模板引擎、利用ob系列函数)
    (2) 并发处理
    (3) 队列处理

    4、数据库优化
    (1) 数据库缓存
    (2) 分库分表,分区
    (3) 读写分离
    (4) 负载均衡

    5、web服务器优化
    (1) nginx反向代理实现负载均衡
    (2) lvs实现负载均衡

    十五、进程、线程、协程
    进程三态模型:运行、就绪、阻塞
    进程五态模型:新建态、活跃就绪/静止就绪、运行、活跃阻塞/静止阻塞、终止态

    线程:有时被称为轻量级进程,是程序执行流的最小单元

    线程状态:就绪、阻塞、运行

    协程:是一种用户态的轻量级线程,协程的调度完全由用户控制

    线程和进程的区别

    • 线程是进程内的一个执行单元,进程中至少有一个线程,他们共享进程的地址空间,而进程有自己独立的地址空间
    • 进程是资源分配和拥有的单位,同一个进程内的线程共享进程的资源
    • 线程是处理器调度的基本单位,而进程不是
    • 两者都可并发执行

    线程和协程的区别

    • 一个线程可以多个协程,一个进程也可以单独拥有多个协程
    • 线程进程都是同步机制,协程是异步

    同步阻塞

    • 创建一个socket
    • 进入while循环,阻塞在进程accept操作上,等待客户端连接进入
    • 主进程在多进程模式下通过fork创建子进程
    • 多线程模式下可以创建子线程
    • 子进程/线程创建成功后进入while循环,阻塞在recv调用上,等待客户端向服务端发送数据
    • 收到数据后服务器程序进行处理然后使用send向客户端发送响应
    • 当客户端连接关闭时,子进程/线程退出并销毁所有资源。主进程/线程会回收掉此子进程/线程
    // 创建一个socket
    $sockServer = stream_socket_server('tcp://0.0.0.0:8000', $errno, $errstr);
    
    for ($i = 0; $i < 5; $i++) {
        if (pcntl_fork() == 0) {
            while (true) {
                $conn = stream_socket_accept($sockServer);
                if ($conn == false) {
                    continue;
                }
    
                $request = fread($conn, 9000);
                $response = 'hello';
                fwrite($conn, $response);
                fclose($conn);
            }
            exit(0);
        }
    }
    
    实现多进程并行操作(可做守护进程) 
    
    /**
     * 入口函数
     * 将此文件保存为 ProcessOpera.php
     * 在terminal中运行 /usr/local/php/bin/php ProcessOpera.php &
     * 查看进程 ps aux|grep php
     */
    ProcessOpera("runCode", [], 8);
    
    /**
     * @param array $opt
     * run Code
     */
    function runCode($opt = [])
    {
        //需要在守护进程中运行的代码
    }
    
    
    /**
     * @param string $func 子进程执行具体事物的函数名称
     * @param array $opts $func的参数 数组形式
     * @param int $pNum fork的子进程数量
     */
    function ProcessOpera($func, $opts = array(), $pNum = 1)
    {
        while (true) {
            $pid = pcntl_fork();
            if ($pid == -1) {
                exit("pid fork error");
            }
            if ($pid) {
                static $execute = 0;
                $execute++;
                if ($execute >= $pNum) {
                    pcntl_wait($status);
                    $execute--;
                }
            } else {
                while (true) {
                    // some code
                    $func($opts);
                    sleep(1);
                }
                exit(0);
            }
        }
    }
    
    实现多线程
    
    class My extends Thread
    {
        protected $param;
        protected $name;
        public $running;
    
        function __construct($name)
        {
            $this->running = 1;
            $this->param = 0;
            $this->name = $name;
        }
    
        public function run()
        {
            while ($this->running) {
                if ($this->param) {
                    $time = rand(1, 5);
                    echo 'I am thread ' . $this->name . ',pid: ' . $this->getCreatorId() . ",param: {$this->param},need {$time}s\n";
                    sleep($time);
                    $this->param = 0;
                } else {
                    echo "Thread {$this->name} waiting...\n";
                }
                sleep(1);
            }
        }
    }
    
    $pool = [];
    $pool[] = new My('a');
    $pool[] = new My('b');
    $pool[] = new My('c');
    //开启所有线程
    foreach ($pool as $w) {
        $w->start();
    }
    //派发任务
    unset($w);
    for ($i = 1; $i < 10; $i++) {
        $worker_content = $i;
        while (1) {
            foreach ($pool as $w) {
                if (!$w->param) {
                    $w->param = $worker_content;
                    echo "Thread {$w->name} empty,put param {$worker_content}.\n";
                    break 2;
                }
            }
            sleep(1);
        }
    }
    
    unset($w);
    while (count($pool)) {
        foreach ($pool as $k => $w) {
            if (!$w->param) {
                $w->running = false;
                unset($pool[$k]);
                echo "Thread {$w->name} end,exit!\n";
            }
        }
        sleep(1);
    }
    
    echo 'All thread end!';
    

    PHP并发编程实践

    • PHP的Swoole
    • 消息队列(应用解耦、流量削锋、日志处理)
    • 接口的并发请求(curl_multi_init)

    缓存
    memcache和redis的区别

    • 性能相差不大
    • redis依赖客户端来实现分布式读写
    • memcache本身没有数据冗余机制
    • redis支持(快照、AOF),依赖快照进行持久化,aof增强了可靠性的同时,对性能有所影响
    • memcache不支持持久化,通常做缓存,提升性能
    • memcache在并发场景下,用cas保证一致性,redis事务支持比较弱,只能保证事务中每个操作连续进行
    • redis支持多种数据类型
    • redis用于数据量较小的高性能操作和运算上
    • memcache用于在动态系统中减少数据库负载,提升性能

    web服务器的负载均衡
    七层负载均衡:基于url等应用层信息的负载均衡
    nginx的proxy是他一个很强大的功能,实现了七层负载均衡
    特点:

    • 功能强大、性能卓越、运行稳定
    • 配置灵活简单
    • 能够自动剔除工作不正常的后端服务器
    • 上传文件使用异步模式
    • 支持多种分配策略,可以分配权重

    实现

    http {
        upstream my {
            server server1;
            server server2;
            server server3;
        }
        server {
            listen 80;
            location / {
                proxy_pass http://my;
            }
        }
    }
    

    加我微信公众号【皮蛋馅儿】,一起学习哦~

    相关文章

      网友评论

        本文标题:PHP面试题收集

        本文链接:https://www.haomeiwen.com/subject/qdhfrhtx.html