一、字符串的定义方式和各自区别
- 单引号
单引号不能解析变量
不能解析转义字符,只能解析单引号和反斜线本身
效率高 - 双引号
可以解析变量,变量可以使用特殊字符和{}包含
可以解析所有的转义字符 - Heredoc
类似于双引号,用来处理大文本
$str = <<<EOF
EOF;
- 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的值==========
'/<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;
}
}
}
加我微信公众号【皮蛋馅儿】,一起学习哦~