PHP面试题收集

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

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

EOF;

二、三大数据类型

  • 标量
    整型(integer)、布尔(boolean)、浮点(float)、字符串(string)
  • 复合
    数组(array)、对象(object)
  • 特殊
    资源(resource)、空值(NULL)
    注意:
    ①浮点类型不能用到比较运算中b=0.7; if (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的值==========
'//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

 $arr[$j + 1]) {
            $tmp = $arr[$j];
            $arr[$j] = $arr[$j + 1];
            $arr[$j + 1] = $tmp;
        }
    }
}
echo '
';
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面试题收集)