php技术笔记

基础

函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# 字符串函数
strpos($haystack,$needle[,$offset=0]) 返回needle在haystack中的位置,没有找到返回false
# 数组函数
array_filter($array[,$callback]) 将数组元素遍历传入callback函数,保留返回值为true的元素。默认去除值等同false(自动转换)的元素
array_pad($array,$size,$value) 用值将数组填充至指定长度
krsort(&$array[,$sort_flag=SORT_REGULAR]) 对数组按键名逆向排序,保存键名到值的关联
# 日期函数
time() 获取当前时间戳
date($format[,$timestamp]) 格式化时间
microtime([true]) 获取当前时间的微秒数[四位小数],如果没有true,那么返回 msec sec这样格式的字符串
strtotime 将字符串日期转换为UNIX时间戳 例:echo strtotime("1989-03-13"); echo strtotime("now");
# 文件函数:
copy($source,$dest) 将文件从source复制到destination
# 其他函数
realpath() 获取绝对路径
call_user_func_array($callback[,$array])
call_user_func($callback[, $parameter,..])
compact($variableNameOne[,$vaiableNameTwo,..]) 建立一个数组,包含变量名和对应的值
extract(array &$var_array) 将变量从数组中导入到当前的符号表中
chr(int $ascii) 返回ascii码对应的字符
ord(string $string) 返回字符串string的第一个字符的ascii码值
array_reduce(array $array,callable $callback[,mixed $initial=NULL]) 迭代得将数组合并为一个值
$array是输入的值
$callback(mixed $carry,mixed $item)
$carry上次迭代后的结果,初值是$initial
$item 本次迭代得值
$initial 初值
bool array_walk(array &$array,callable $callback[,mixed $userdata=NULL]) 将用户自定义函数funcname应用到array数组的每个单元
array_walk()不会受到array内部数组指针的影响
$callback 接收两个参数,array元素的值是第一个参数,key是第二个参数
如果需要改动array的值,则第一个参数需要时引用
a = [1,2,3];
function incr(&$value,$key,$prefix){
$value = $prefix.($value+1);
}
array_walk($a,'incr','prefix');
var_dump($a);
结果:
prefix2
prefix3
prefix4
# 过滤器
filter_var($str,$filter) 按照`$filter` 规则对`$str` 进行验证,满足返回原`$str`内容,不满足返回false
常用$filter有以下几个值:
* FILTER_VALIDATE_EMAIL
* FILTER_VALIDATE_IP
* FILTER_VALIDATE_BOOLEAN
* FILTER_VALIDATE_FLOAT
* FILTER_VALIDATE_INT
* FILTER_VALIDATE_URL
例:定义函数is_ip 验证$str是否是ip地址
function is_ip($str){
return filter_var($str,FILTER_VALIDATE_IP)?true:false;
}

面向对象编程

SPL

1. SplStack 栈

1
2
3
4
5
$stack = new SplStack();
$stack->push("1");
$stack->push("2");
echo $stack->pop();
echo $stack->pop();

2. SplQueue 队列

1
2
3
4
5
$queue = new SplQueue();
$queue->enqueue("1");
$queue->enqueue(2);
echo $queue->dequeue();
echo $queue->dequeue();

3. SplMiniHeap 堆(最小堆) 资料

说明: 堆是一颗完全二叉树,特点是父节点的值大于(小于)两个子节点的值,应用场景包括堆排序、优先队列等

1
2
3
4
5
$heap = new SplMinHeap();
$heap->insert(1);
$heap->insert(2);
echo $heap->extract();
echo $heap->extract();

4.SplFixedArray 固定长度的数组

1
2
3
4
$array = new SplFixedArray()
$array[0] = 123;
$array[9] = 1234;
var_dump($array);

PHP魔术方法

1.get/set //获取/设置变量
2.call/callStatic 调用不存在的方法/静态方法时调用
3.toString 直接输出对象时调用
4.
invoke 将对象当函数执行时运行的方法

反射

类和对象

基本概念

parent,self,static

parent引用的是父类作用域,self引用当前类作用域,static引用全部静态作用域,表现为子类覆盖父类,无子类则使用父类
new self    创建本类对象(创建语句定义位置类的对象) 
new static  创建本类对象(创建最终调用语句的类的对象) 静态延迟绑定
new parent创建父类的对象

注意:

    对对象直接赋值是对对象的引用
    extends可以实现单继承,子类中可以覆盖父类的方法,使用final关键字修饰的方法不可以被覆盖,使用final关键字修饰的类不能被继承
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class Parents{
public static function getObj(){
return new self; //创建当前类对象并返回
}
public static function getStatic(){
return new static; //创建当前类对象并返回
}
}
class Child extends Parents{
public $flag;
public static function getParent(){
return new parent; //创建父类对象并返回
}
}
var_dump(get_class(Child::getObj())); //结果: Parents
var_dump(get_class(Child:: getStatic())); //结果: Child
var_dump(get_class(Child::getParent())); //结果: Parents
var_dump(get_class(Parents::getObj())); //结果: Parents
var_dump(get_class(Parents::getStatic())); //结果: Parents
$obj = new Child;
$obj->flag = 'modified';
$obj_2 = $obj;
var_dump($obj_2->flag); //结果: modified

成员

成员访问:

  • 非静态属性: 可以使用$this->property来访问

类可见性

1
2
3
public 继承后为public。定义为public的成员可以在任何地方被访问
protected 继承后为protected。定义为protected的类成员可以被自身、子类以及父类访问
private 不继承。定义为私有的类成员只能被其定义所在的类访问
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
class Parents{
public function call(){
echo $this->a;
echo $this->b;
echo $this->c;
}
}
class Child extends Parents{
public $a = 'a';
protected $b = 'b';
private $c = 'c';
}
$obj_c = new Child;
$obj_c->call(); //结果: ab 报错c Cannot access private property Child::$c

组合与继承

1
2
组合表示类A中的某属性是另外一个类B的对象,且关系上类A包含类B 如兔子的构造组件包含腿
继承表示类B的所有特征类A都有,关系上是类A属于类B 如兔子属于动物
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
Rabbit has-a Leg 兔子包含腿组件
class Leg{
private $name = '大腿';
public function display(){
return $this->name;
}
}
class Rabbit{
private $leg;
function __construct(Leg $leg){
$this->leg = $leg;
}
function getLeg(){
return $this->leg->display();
}
}
$leg = new Leg;
$rabbit = new Rabbit($leg);
echo $rabbit->getLeg().'<br />';
//继承 Cat is-a Annimal 猫属于动物
abstract class Animal{
public $animal = '动物';
public function getSpecies(){
return $this->animal;
}
}
class Cat extends Animal{
}
$cat = new Cat;
echo $cat->getSpecies();

静态延迟绑定

get_called_class() 

​ static::

自动加载

  1. 利用composer
  2. 利用spl_autoload_register

性能分析

方案1: tideways+xhgui (适用于现代PHP) 参考文档 参考文档2
方案2: xhprof+xhgui (只适用于php5)

扩展开发

PHP7扩展开发之hello word

配置

查找PHP配置文件

php --ini

#执行结果
Configuration File (php.ini) Path: /usr/local/etc/php/5.5
Loaded Configuration File:         /usr/local/etc/php/5.5/php.ini
Scan for additional .ini files in: /usr/local/etc/php/5.5/conf.d
Additional .ini files parsed:      /usr/local/etc/php/5.5/conf.d/ext-igbinary.ini,
/usr/local/etc/php/5.5/conf.d/ext-mcrypt.ini,
/usr/local/etc/php/5.5/conf.d/ext-memcached.ini,
/usr/local/etc/php/5.5/conf.d/ext-mongo.ini,
/usr/local/etc/php/5.5/conf.d/ext-redis.ini,
/usr/local/etc/php/5.5/conf.d/ext-swoole.ini
  • php会扫描Scan for additional .ini files in目录,并读取将其中的.ini文件,等同于在php.ini中添加配置
  • Scan for additional .ini files in目录在编译时通过—with-config-file-scan-dir=PATH设定

PHP-FPM

Fpm(fastCGI进程管理器)是PHP FastCGI进程管理

1
2
3
# 向master进程发送信号
$ kill -USR2 pid
USR2 平滑重启php-fpm

与php-fpm相关的有两个文件php.ini和php-fpm.conf,后者是php-fpm的配置文件

php-fpm.conf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
进程监听方式
i.unixsocket方式
listen=/run/php/php5.6.sock
* 效率高,缺点sock属于本地通讯,故php需要和nginx在同一台机器
ii.网络地址端口方式 (tcp协议)
listen=127.0.0.1:9000
listen=9000
max_children
php-cgi进程多了排队请求就会变少
max-children setting consider raising it
一个php-fpm进程通常最大占用30M内存,按照自己内存大小来合理设定最大值即可,比如100个需要30*100=3G内存
request_terminate_timeout
最长执行时间
当经常出现502时,可以尝试调整该项
max_requests
指一个worker进程在处理多少个请求后就终止掉,master重新生成一个新的worker
这个配置的目的是避免php解析器或程序引起的第三方库造成的内存泄漏
正是因为这个机制,导致高并发中经常导致502的错误,解决方法是将请求数调大一点

信号处理

pcntl_signal 安装信号处理器
pcntl_alarm 创建计时器,指定秒数后向进程发送SIGALRM信号
pcntl_signal_dispatch 分发信号

信号

SIGALRM     定时器信号
SIGTERM     kill pid时会将SIGTERM发送给进程
SIGKILL     kill -9 pid”时会将SIGKILL发送给进程

历史

  • PHP5.3
  1. 闭包
  2. __callStatic
  3. 命名空间
  • PHP5.4
  1. traits
  2. new生成对象的同时调用对象成员

Session

自定义session处理

1.session_set_save_handler()
2.session.save_handler

为什么不推荐使用php自带的文件型session处理方式

php官方手册有这么一段话

无论是通过调用函数 session_start() 手动开启会话, 还是使用配置项 session.auto_start 自动开启会话, 对于基于文件的会话数据保存(PHP 的默认行为)而言, 在会话开始的时候都会给会话数据文件加锁, 直到 PHP 脚本执行完毕或者显式调用 session_write_close() 来保存会话数据。 在此期间,其他脚本不可以访问同一个会话数据文件。

为了证明这段话

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
//session1.php
session_start();
sleep(5);
var_dump($_SESSION);
?>
<?php
//session2.php
session_start();
var_dump($_SESSION);
?>

访问session1.php后马上访问session2.php,等待5秒后session1.php和session2.php页面才打开,而只打开session2.php的话是秒开。这是因为当session1.php开启session后就对session文件加锁,只有显式使用session_write_cloese()或等待脚本执行完毕才会释放锁,期间其他相同session_id的请求就会被阻塞,只能被迫等待。

所以推荐使用redis或memecache作为session处理器

Session数据什么时候删除

官方手册中的说明

session.gc_maxlifetime 指定过了多少秒之后数据就会被视为”垃圾”并被清除。 垃圾搜集可能会在 session 启动的时候开始( 取决于 session.gc_probabilitysession.gc_divisor)。 session.gc_probabilitysession.gc_divisor 合起来用来管理 gc(garbage collection 垃圾回收)进程启动的概率。此概率用 gc_probability/gc_divisor 计算得来。例如 1/100 意味着在每个请求中有 1% 的概率启动 gc 进程。session.gc_probability 默认为 1,session.gc_divisor 默认为 100。

FAQ

1. json_decode的坑

1
2
3
答: json_decode( `$str` )默认返回 `stdClass` 类的对象,stdClass类可以看做所有类的基类,这个类的特点是没有方法。如果你想让json_decode返回数组,需要设置第二个参数为true
例:
json_decode($str,true)

2. 单双引号转义

1
2
单引号: \对\\ '对应\'
双引号: \对\\ "对应\" 换行对应\n 回车对应\r \t水平制表符 \$美元标记

3. PHP中浮点数精度问题

1
2
3
4
5
6
7
8
9
10
11
浮点数在PHP中以近似数的形式存在,不要进行直接大小判断。
方法1 使用bccomp比较两个任意精度的数字,相等返回0,左边大返回1,右边大返回-1
方法2 使用round对浮点数进行四舍五入,之后再做比较
$a=0.1+0.2;
$b=0.3;
var_dump($a == $b); //false
printf("%0.20f",$a); //0.30000000000000004441
printf("%0.20f",$b); //0.29999999999999998890
var_dump(bccomp($a,$b,2)); //在小数精度为2的情况下比较$a $b,结果0
var_dump(round($a,2),round($b,2)) //0.3 0.3

参考资料 http://php.net/float

4. 过滤emoji表情

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
private static function removeEmoji($text) {
$clean_text = "";
// Match Emoticons
$regexEmoticons = '/[\x{1F600}-\x{1F64F}]/u';
$clean_text = preg_replace($regexEmoticons, '', $text);
// Match Miscellaneous Symbols and Pictographs
$regexSymbols = '/[\x{1F300}-\x{1F5FF}]/u';
$clean_text = preg_replace($regexSymbols, '', $clean_text);
// Match Transport And Map Symbols
$regexTransport = '/[\x{1F680}-\x{1F6FF}]/u';
$clean_text = preg_replace($regexTransport, '', $clean_text);
// Match Miscellaneous Symbols
$regexMisc = '/[\x{2600}-\x{26FF}]/u';
$clean_text = preg_replace($regexMisc, '', $clean_text);
// Match Dingbats
$regexDingbats = '/[\x{2700}-\x{27BF}]/u';
$clean_text = preg_replace($regexDingbats, '', $clean_text);
return $clean_text;
}

5. call_user_func对函数及类中方法的访问权限

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
访问类中方法
public
静态,可直接调用 call_user_func(array('class','method'))
非静态,需要指定具体对象,否则会报strict错误 call_user_func(array($obj,'method'))
public
不可访问
访问函数
可直接访问
/**
* 实验call_user_func与call_user_func_array使用方法
* Email: sxk10812139@163.com
* DateTime: 16/9/19 02:00
*/
class obj{
public function a(){
echo __METHOD__.PHP_EOL;
}
public static function b(){
echo __METHOD__.PHP_EOL;
}
protected function c(){
echo __METHOD__.PHP_EOL;
}
}
$obj = new obj();
call_user_func(array('obj','a')); //报strict错误
call_user_func(array('obj','b')); //正常访问
call_user_func(array($obj,'a')); //正常访问
call_user_func(array($obj,'c')); //报错
call_user_func(array($obj,'xx'),array(1,2,3));

6. 空数组在json_encode后会变成[],而前端期望的是对象{},如何解决

1
2
3
方法1 json_encode((object)$var) //推荐 json_encode前指定输出类型
方法2 json_encode($var,JSON_FORCE_OBJECT);
方法3 json_encode(new stdClass());

7. 类型转换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$a='00000';
$b=0;
$c='0';
if($a==0){
echo 1;
}
if(0){
echo 2;
}
if($a){
echo 3;
}
var_dump((bool)$a,(bool)$b,(bool)$c);
结果是:
13
true false false

发起一个请求后,中途如果断开,比如关闭网页,此后php代码是否会继续执行

会继续执行,直到达到最大执行时间