BJLC's Blog

无齌无衋懋葳蕤,鸾凤和鸣蓺清枫!


  • 首页

  • 关于

  • 标签

  • 分类

  • 归档

vld 扩展安装

发表于 2019-05-08 | 分类于 PHP

  在学习和研究PHP的过程中,总会需要查看opcodes,可以使用PHP自带的phpdbg命令进行查看,也可以通过vld扩展进行查看。
a.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
<?php
$a = 1;
echo $a;

/*phpdbg查看OPCode如下:
phpdbg -p* a.php
function name: (null)
L1-4 {main}() /root/PHPSourceCode/a.php - 0x7f0962c780a0 + 5 ops
L2 #0 EXT_STMT
L2 #1 ASSIGN $a 1
L3 #2 EXT_STMT
L3 #3 ECHO $a
L4 #4 RETURN 1

vld查看OPCode如下:
php -d vld.active=1 a.php
Finding entry points
Branch analysis from position: 0
1 jumps found. (Code = 62) Position 1 = -2
filename: /root/PHPSourceCode/a.php
function name: (null)
number of ops: 5
compiled vars: !0 = $a
line #* E I O op fetch ext return operands
-------------------------------------------------------------------------------------
2 0 E > EXT_STMT
1 ASSIGN !0, 1
3 2 EXT_STMT
3 ECHO !0
4 4 > RETURN 1

branch: # 0; line: 2- 4; sop: 0; eop: 4; out0: -2
path #1: 0,

  vld扩展安装如下:

1
2
3
4
5
git clone https://github.com/derickr/vld.git
cd vld
./configure --with-php-config=/usr/local/php/bin/php-config --enable-vld
make
make install

  安装成功后通过php --ini找到php.ini配置文件加入extension=vld.so,重启php-fpm,可以通过php -m | grep vld来查看是否安装完成,然后就可以使用vld来查看PHP执行的opcodes了。
  vld常用扩展参数说明:

vld.active:是否激活vld,默认是0(不激活),1(激活)
vld.execute :是否输出执行结果,默认是1(输出),0(不输出)
vld.verbosity:是否输出更详细的opcodes,大于等于3和小于0是(输出op1、op2、result),0,1,2效果一样不输出op1、op2、result,默认1
vld.save_dir:指定文件输出路径,默认为/tmp
vld.save_paths:控制是否输出dot语言文件,默认0(不输出),1(输出)

  dot是一种图形描述语言,由graphviz工具绘制:
Vld.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
class Vld
{
public $num;
public function __construct($num)
{
$this->num = $num;
}
public function increase()
{
return $this->num + 1;
}
}
$v = new Vld(1);
var_dump($v->increase());

  执行 php -dvld.active=1 -dvld.save_paths=1 vld.php
  进入/tmp目录,执行 dot -Tpng /tmp/paths.dot -o paths.png 生成如下图片:
Vld.png
  这里扩展一下,使用 graphviz 还可以画流程图哦,很高大上吧,下面一起来看看吧。graphviz 官方下载地址
demo.dot

1
2
3
4
5
6
7
digraph demo {
start [label="start"]
op [label="enter" shape=box]
end [label="end"]

start->op->end
}

  执行 dot -Tpng demo.dot -o demo.png 就可以得到如下流程图:
demo.png

Go安装

发表于 2019-05-05 | 更新于 2019-04-14 | 分类于 Go
1
2
3
4
5
6
7
8
//获取源码包
wget https://dl.google.com/go/go1.12.4.linux-amd64.tar.gz
//解压
tar -zxvf go1.12.4.linux-amd64.tar.gz
//修改/etc/profile添加GOPATH和GOROOT
export GOPATH=/home/zj/Go
export GOROOT=/usr/src/go
//复制/usr/src/go/bin下文件到/usr/local/bin,使go命令可全局使用

vim中go语法高亮

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//更新vim版本到7.4.2,卸载旧版vim
yum remove -y vim-enhanced
//安装新版本vim
wget https://github.com/vim/vim/archive/v7.4.2367.tar.gz
tar zxvf v7.4.2367.tar.gz
make && make install
//安装vim插件管理工具
mkdir -p ~/.vim/autoload ~/.vim/bundle
curl -LSso ~/.vim/autoload/pathogen.vim https://tpo.pe/pathogen.vim
//编辑.vimrc加入如下内容
execute pathogen#infect()
syntax on
filetype plugin indent on
//安装其他vim插件只需要将插件放到~/.vim/bundle里即可

Go语言JSON操作

发表于 2019-05-05 | 更新于 2019-04-03 | 分类于 Go

Go语言中通过encoding/json包对JSON串进行处理:

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
package main

import (
"fmt"
"encoding/json"
)

//构造总的JSON串结构体
type GoJson struct {
Name string
Type string
Properties Properties
}

//构造内部JSON串结构体
type Properties struct {
Slice string
Array string
Interface string
Struct string
}

func main() {
//解析JSON串
//待解析的JSON串
jsonStr := `{
"name": "go",
"type": "static",
"properties": {
"slice": "切片",
"array": "数组",
"interface": "接口",
"struct": "结构体"
}
}`
goJson := GoJson{}
err := json.Unmarshal([]byte(jsonStr), &goJson)
if err != nil {
fmt.Printf("%s", err.Error())
}
fmt.Printf("Name:%s\nType:%s\nProperties:%s\n", goJson.Name, goJson.Type, goJson.Properties)

//生成JSON串
properties := Properties{
Slice: "没有",
Array: "数组",
Interface: "接口",
Struct: "没有",
}
goJson1 := GoJson{
Name: "PHP",
Type: "Dynamic",
Properties: properties,
}
j, err := json.Marshal(goJson1)
if err != nil {
fmt.Println("%s", err.Error())
}
fmt.Println(string(j))
}

Go语言MD5加密

发表于 2019-05-05 | 更新于 2019-04-03 | 分类于 Go

Go中通过crypto/md5有两种MD5加密方式:

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
package main

import (
"fmt"
"crypto/md5"
"encoding/hex"
)

func main() {
//需要MD5的字符串
str := []byte("hello world")

//方法一:
//实例化md5对象,返回hash.Hash对象
m := md5.New()
m.Write(str)
//计算MD5检验和
md5Str := m.Sum(nil)
fmt.Printf("%x\n", md5Str)
//转换成16进制
fmt.Println(hex.EncodeToString(md5Str))

//方法二:
md5Str1 := md5.Sum(str)
fmt.Printf("%x\n", md5Str1)
//转换成16进制,需要注意md5后的数据类型
fmt.Println(hex.EncodeToString(md5Str1[:]))
}

Go语言urlencode和urldecode操作

发表于 2019-05-05 | 更新于 2019-04-03 | 分类于 Go

Go语言中可以通过net/url来进行urlencode和urldecode操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package main

import (
"fmt"
"net/url"
)

func main() {
u := url.Values{}
//urlencode
u.Add("name", "golang是编译型语言")
u.Add("type", "static静态类型")
urlencodeStr := u.Encode()
fmt.Println(urlencodeStr)

//urldecode
urldecodeStr, err := url.ParseQuery(urlencodeStr)
if err != nil {
fmt.Printf("urldecode错误,Error:%s\n", err.Error())
}
fmt.Println(urldecodeStr)
}

入世春秋

发表于 2019-05-05 | 更新于 2019-03-27 | 分类于 缥缃醉

  恍惚间,既已入世春秋,庠序雪泥鸿爪仿佛昨日,奈何?余之思,甚易,无齌无衋于其身,欲与佳期共氤氲,放浪形骸撷流水。漫步青石径,谈笑山野间。一朝蓺幽兰,馨香泛葳蕤,可知?花香已满径,辄待伊人还!洎入世,长齌,长衋,甚讟,夥罅隙。今夜无皓月,雨后凉风习习,惘顾,当变,遂执聿以书!
  社鼠城狐,鄙之;八面玲珑,蔑之;魑魅魍魉,揶揄之。齮龁日久,难以释怀,尝愤之、齌之。吾觉缧绁缠之,曾几何时,鲰生嚚臣余漦之、雠之,窆之于坟冢;然今余几成鬼蜮,媕娿不前,悲之、觳觫之。余非怙恶不悛,亦无罪不容诛,然若豭豨戆头。余既已虩虩,弇陋、癙矣!虽讟言夥,可诉之?焚琴煮鹤,高山流水!!谮言概不语,诤言、箴言语之,几人闻之,尝见紿矣,呕心沥血、兢兢业业亦不可待见!敬人者,人敬之;韪人者,亦谢之。人之二十余载,无不恫瘝,尝阅幽兰“子如不伤,我不尔觏。君子之伤,君子之守。世人暗蔽,不知贤者。伤不逢时,寄兰作操。”慕之、羡之。谁生厉阶,至今为梗。一傅众咻,鞭长莫及。抽刀出鞘天为遥,日月星辰芒骤韬。苍穹云既殁,瀚海水亦涸。今余思之、忖之,始觉无趣,心燹难熸,吾当返璞归真,无论它朝之目。愿为珈玏,弃之謷言,毷氉,猰貐,消艴,不葸谮言。瘗之奴颜婢膝,撷叶蓺豪情。摧眉折腰,戛乎难哉?踶之以懋,不似曏从之窳,但飨素之纯甄。讆之,骀荡之,遹葳蕤。愿得花香满径,笑揖清风洗我狂!
  终,诗一阕,如是,


一夕尔觏蓺心蕙,
清秋褚松花开阑。
鲛绡依梦似你眸,
梦舟轩翥始愿旻。
皎皎寒夜漫蓝溪,
瑊玏韶华彰艺馨。
蟾宫折桂践梁濠,
行懿形姝谓君兰!

书于丽园
二零一八年十二月十三日

揖槛扶风

发表于 2019-05-05 | 更新于 2019-03-27 | 分类于 缥缃醉

桂影斑驳夜黯淡,茶色涟涟印清秋。
登楼饮风候月盏,青丝残灯笼桃面。
云歇雨住萤草离,几壶明月共婵娟?
提笔扶风寄花笺,稻香醉竹人长久!

神奇的宏替换

发表于 2019-05-05 | 更新于 2019-03-27 | 分类于 PHP

  在C语言中可以使用#define来指定一个标识符(宏名)代表一个字符串。宏定义有两种方式,一种有参数宏定义,一种无参数宏定义。

1
2
3
4
//无参数宏定义
#define 宏名 字符串
//有参数宏定义
#define 宏名(参数) 字符串

  关于宏的几点说明:
1、宏名一般大写;
2、宏是预编译处理不做语法检查和计算,仅替换;
3、宏定义末尾不加分号;
4、宏使用#define定义,使用#undef取消定义;
5、宏定义可以嵌套,不会递归替换;
6、字符串""中不包含宏,宏定义不分配内存,变量定义分配内存;
7、宏名和参数间的括号不能有空格;
8、#用于在宏定义的参数两端加上"",##用于拼接宏;
9、每次宏展开的结果会被重复扫描,直到没有任何可以展开的宏为止;
10、每展开一个宏,都会记住这次展开,在这个宏展开的结果和后续展开中不会重复展开相同的宏;
11、带参数的宏,先对参数展开,除非宏定义体中有#和##。
  针对8,9条给出一个说明例子:demo.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include<stdio.h>
#define foo foo bar
#define bar bar bar foo
int main()
{
foo;
}
/*
先展开foo => foo bar,因为foo已经展开过了,之后的foo不再展开
此时展开bar => foo bar bar foo

通过 gcc -E demo.c 得到如下结果:
int main()
{
foo bar bar foo;
}
说明结论正确。
*/

  学习了宏定义以后我们来看一下PHP源码中的宏替换,PHP源码中有一个内存分配的宏替换,这个宏的位置在/usr/src/php-7.1.6/Zend/zend_alloc.h文件的106行:

1
2
3
4
5
6
7
8
9
10
11
# define ZEND_ALLOCATOR(size) \
ZEND_MM_BINS_INFO(_ZEND_BIN_ALLOCATOR_SELECTOR_START, size, y) \
((size <= ZEND_MM_MAX_LARGE_SIZE) ? _emalloc_large(size) : _emalloc_huge(size)) \
ZEND_MM_BINS_INFO(_ZEND_BIN_ALLOCATOR_SELECTOR_END, size, y)

# define _emalloc(size) \
(__builtin_constant_p(size) ? \
ZEND_ALLOCATOR(size) \
: \
_emalloc(size) \
)

  我们可以看到里面有四个宏定义,其中ZEND_MM_BINS_INFO和ZEND_MM_MAX_LARGE_SIZE这两个宏在/usr/src/php-7.1.6/Zend/zend_alloc_sizes.h中,详情如下:

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
#ifndef ZEND_ALLOC_SIZES_H
#define ZEND_ALLOC_SIZES_H

#define ZEND_MM_CHUNK_SIZE (2 * 1024 * 1024) /* 2 MB */
#define ZEND_MM_PAGE_SIZE (4 * 1024) /* 4 KB */
#define ZEND_MM_PAGES (ZEND_MM_CHUNK_SIZE / ZEND_MM_PAGE_SIZE) /* 512 */
#define ZEND_MM_FIRST_PAGE (1)

#define ZEND_MM_MIN_SMALL_SIZE 8
#define ZEND_MM_MAX_SMALL_SIZE 3072
#define ZEND_MM_MAX_LARGE_SIZE (ZEND_MM_CHUNK_SIZE - (ZEND_MM_PAGE_SIZE * ZEND_MM_FIRST_PAGE))

/* num, size, count, pages */
#define ZEND_MM_BINS_INFO(_, x, y) \
_( 0, 8, 512, 1, x, y) \
_( 1, 16, 256, 1, x, y) \
_( 2, 24, 170, 1, x, y) \
_( 3, 32, 128, 1, x, y) \
_( 4, 40, 102, 1, x, y) \
_( 5, 48, 85, 1, x, y) \
_( 6, 56, 73, 1, x, y) \
_( 7, 64, 64, 1, x, y) \
_( 8, 80, 51, 1, x, y) \
_( 9, 96, 42, 1, x, y) \
_(10, 112, 36, 1, x, y) \
_(11, 128, 32, 1, x, y) \
_(12, 160, 25, 1, x, y) \
_(13, 192, 21, 1, x, y) \
_(14, 224, 18, 1, x, y) \
_(15, 256, 16, 1, x, y) \
_(16, 320, 64, 5, x, y) \
_(17, 384, 32, 3, x, y) \
_(18, 448, 9, 1, x, y) \
_(19, 512, 8, 1, x, y) \
_(20, 640, 32, 5, x, y) \
_(21, 768, 16, 3, x, y) \
_(22, 896, 9, 2, x, y) \
_(23, 1024, 8, 2, x, y) \
_(24, 1280, 16, 5, x, y) \
_(25, 1536, 8, 3, x, y) \
_(26, 1792, 16, 7, x, y) \
_(27, 2048, 8, 4, x, y) \
_(28, 2560, 8, 5, x, y) \
_(29, 3072, 4, 3, x, y)

#endif /* ZEND_ALLOC_SIZES_H */

  我们可以把这个宏从代码里面摘出来test_zend.c如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include "zend_alloc_sizes.h"
#include<stdio.h>

# define _ZEND_BIN_ALLOCATOR_SELECTOR_START(_num, _size, _elements, _pages, size, y) \
((size <= _size) ? _emalloc_ ## _size() :
# define _ZEND_BIN_ALLOCATOR_SELECTOR_END(_num, _size, _elements, _pages, size, y) \
)

# define ZEND_ALLOCATOR(size) \
ZEND_MM_BINS_INFO(_ZEND_BIN_ALLOCATOR_SELECTOR_START, size, y) \
((size <= ZEND_MM_MAX_LARGE_SIZE) ? _emalloc_large(size) : _emalloc_huge(size)) \
ZEND_MM_BINS_INFO(_ZEND_BIN_ALLOCATOR_SELECTOR_END, size, y)

# define _emalloc(size) \
(ZEND_ALLOCATOR(size))

int main()
{
_emalloc(size);
}

  接下来我们来一步步展开这个宏:
1、展开 _emalloc(size)
  展开以后发现ZEND_ALLOCATOR(size)这个宏
2、展开 ZEND_ALLOCATOR(size)
  展开以后发现ZEND_MM_BINS_INFO(_ZEND_BIN_ALLOCATOR_SELECTOR_START, size, y)这个宏
3、展开 ZEND_MM_BINS_INFO(_ZEND_BIN_ALLOCATOR_SELECTOR_START, size, y)
  结合前面的ZEND_MM_BINS_INFO(_, x, y)得到如下结果:

1
2
3
4
5
6
_ZEND_BIN_ALLOCATOR_SELECTOR_START(0, 8, 512, 1, size, y) \
_ZEND_BIN_ALLOCATOR_SELECTOR_START(1, 16, 256, 1, size, y) \
.
.
.
_ZEND_BIN_ALLOCATOR_SELECTOR_START(29, 3072, 4, 3, size, y) \

4、展开 _ZEND_BIN_ALLOCATOR_SELECTOR_START(_num, _size, _elements, _pages, size, y)

1
2
3
((size <= 8) ? _emalloc_8() : ((size <= 16 ? _emalloc_16() :
.........................................................
((size <= 3072) ? _emalloc_3072() :

5、展开 ZEND_MM_MAX_LARGE_SIZE

1
((size <= ((2 * 1024 * 1024) - ((4 * 1024) * (1)))) ? lloc_large(size) : _emalloc_huge(size))

6、展开 ZEND_MM_BINS_INFO(_ZEND_BIN_ALLOCATOR_SELECTOR_END, size, y)

1
2
3
4
5
6
_ZEND_BIN_ALLOCATOR_SELECTOR_END(0, 8, 512, 1, size, y) \
_ZEND_BIN_ALLOCATOR_SELECTOR_END(1, 16, 256, 1, size, y) \
.
.
.
_ZEND_BIN_ALLOCATOR_SELECTOR_END(29, 3072, 4, 3, size, y) \

7、展开 _ZEND_BIN_ALLOCATOR_SELECTOR_END(_num, _size, _elements, _pages, size, y)

1
)))))))))))))))))))))))))))))))

8、到此所有宏展开完毕,组合以后得到如下结果

1
2
3
(((size <= 8) ? _emalloc_8() : ((size <= 16 ? _emalloc_16() :
.........................................................
((size <= 3072) ? _emalloc_3072() : ((size <= ((2 * 1024 * 1024) - ((4 * 1024) * (1)))) ? lloc_large(size) : _emalloc_huge(size)) )))))))))))))))))))))))))))))))

9、执行 gcc -E test_zend.c 得到结果如下

1
2
3
4
int main()
{
(((size <= 8) ? _emalloc_8() : ((size <= 16) ? _emalloc_16() : ((size <= 24) ? _emalloc_24() : ((size <= 32) ? _emalloc_32() : ((size <= 40) ? _emalloc_40() : ((size <= 48) ? _emalloc_48() : ((size <= 56) ? _emalloc_56() : ((size <= 64) ? _emalloc_64() : ((size <= 80) ? _emalloc_80() : ((size <= 96) ? _emalloc_96() : ((size <= 112) ? _emalloc_112() : ((size <= 128) ? _emalloc_128() : ((size <= 160) ? _emalloc_160() : ((size <= 192) ? _emalloc_192() : ((size <= 224) ? _emalloc_224() : ((size <= 256) ? _emalloc_256() : ((size <= 320) ? _emalloc_320() : ((size <= 384) ? _emalloc_384() : ((size <= 448) ? _emalloc_448() : ((size <= 512) ? _emalloc_512() : ((size <= 640) ? _emalloc_640() : ((size <= 768) ? _emalloc_768() : ((size <= 896) ? _emalloc_896() : ((size <= 1024) ? _emalloc_1024() : ((size <= 1280) ? _emalloc_1280() : ((size <= 1536) ? _emalloc_1536() : ((size <= 1792) ? _emalloc_1792() : ((size <= 2048) ? _emalloc_2048() : ((size <= 2560) ? _emalloc_2560() : ((size <= 3072) ? _emalloc_3072() : ((size <= ((2 * 1024 * 1024) - ((4 * 1024) * (1)))) ? _emalloc_large(size) : _emalloc_huge(size)) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ));
}

  验证正确,这就是宏替换,看源码会有很多这样复杂的宏定义,只要认真仔细,都是可以轻松替换的。赶快去试试吧。

PHP7新特性

发表于 2019-05-05 | 更新于 2019-03-24 | 分类于 PHP

PHP7除了在性能方面提升了很多,也加入了很多新特性,一起看一下吧。

1、太空船操作符(<=>)
  太空船操作符用于比较两个表达式,大于、等于和小于时分别返回1、0、-1。

1
2
3
4
5
6
7
8
<?php
echo 1 <=> 1; //0
echo 1 <=> 0; //1
echo 1.8 <=> 2; //-1

echo 'a' <=> 'a'; //0
echo 'a' <=> 'b'; //-1
echo 'b' <=> 'a'; //1

2、标量类型申明和返回值类型声明
  PHP7可以对以下几种标量类型的参数做声明:字符串(string)、整型(int)、浮点型(float),布尔型(bool)。参数类型声明不受限于默认模式和严格模式,默认模式时传入的参数不符合声明类型会进行类型转换,严格模式则会报错。
参数类型声明

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
//声明为严格模式,注释此行将不会报错
declare(strict_types = 1);

function sum(int ...$ints)
{
return array_sum($ints);
}

var_dump(sum(1, '1.1', 1.1));

/*执行结果如下:
declare(strict_types = 1);
PHP Fatal error: Uncaught TypeError: Argument 2 passed to sum() must be of the type integer, string given

declare(strict_types = 1);
int(3)
*/

返回值类型声明

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
//声明为严格模式,注释此行将不会报错
declare(strict_types = 1);

function sum(int ...$ints) : int
{
return array_sum($ints);
}

var_dump(sum(1, '1.1', 1.1));
/*执行结果如下:
declare(strict_types = 1);
PHP Fatal error: Uncaught TypeError: Argument 2 passed to sum() must be of the type integer, string given

declare(strict_types = 0);
int(3)
*/

  PHP7.1可以将函数返回值类型定义为 viod ,无论是否开启严格模式,只要函数中有 return;,那么其他 return语句都将报错。参数类型不可以声明为 viod。
返回值声明为 void

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php
declare(strict_types = 1);

function testVoid(int ...$ints) : void
{
return array_sum($ints);
return null;
return ;
}

var_dump(testVoid(1, 2, 6));
/*运行结果如下:
return array_sum($ints);
PHP Fatal error: A void function must not return a value

return null;
PHP Fatal error: A void function must not return a value (did you mean "return;" instead of "return null;"?)

return;
NULL
*/

  PHP7.1对于参数类型和返回值类型还可以是可空类型,在参数或者返回值类型声明前加?即可。
可空参数类型和返回值类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?php
declare(strict_types = 1);

function test(?int ...$ints) : ?int
{
return array_sum($ints);
return strval(implode(',', $ints));
return null;
return;
}
var_dump(test(1, 2, 4));
/*执行结果如下:
return array_sum($ints);
int(7)

return strval(implode(',', $ints));
PHP Fatal error: Uncaught TypeError: Return value of test() must be of the type integer or null, string returned

return null;
NULL

return;
PHP Fatal error: A function with return type must return a value (did you mean "return null;" instead of "return;"?)

3、null合并运算符
  PHP 7以前我们通常会写这样的代码:
  $name = isset($res['name']) ? $res['name'] : '';
  PHP7以后我们可以这样写:
  $name = $res['name'] ?? '';
  这样就极大简化了我们的代码。
  对于这样的代码我们也可以做简化:
  $name = !empty($res['name']) ? $res['name'] : '';
  $name = $res['name'] ?: '';
  对于连续的三元运算符还可以这样写:
  $name = $res['name'] ?? $data['name'] ?? '';
4、define定义常量数组
  PHP7之前无法通过define来定义一个常量数组,PHP7支持。

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
define('TYPES', [
1, 2, 3
]);
print_r(TYPES);
/*执行结果如下:
Array
(
[0] => 1
[1] => 2
[2] => 3
)
*/

5、批量导入namespace命名空间
  PHP7之前如果需要导入同一个namespace的多个类,需要这样写:

1
2
3
4
<?php
use Namespace\A;
use Namespace\B;
use Namespace\C;

  PHP7可以这样写:

1
2
<?php
use Namespace\{A, B, C};

6、throwable接口
  PHP7之前如果代码有语法错误,或者fatal error程序会直接报错退出,PHP7实现了throwable接口,原来的Exception和部分Error实现了该接口,那么这种Error和Exception会被第一个try catch捕获。没有匹配的catch块,则会调用异常处理函数,如果没有注册,则会fatal error。Error类并非继承Exception,所以要想捕获Error,可以catch (Error $e) {}或者通过注册异常处理函数set_exception_handler()来捕获Error。

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
<?php
try {
test();
} catch (Error $e) {
print_r($e);
}

set_exception_handler(function ($e) {
print_r($e);
});
test();
/*执行结果如下:
Error Object
(
[message:protected] => Call to undefined function test()
[string:Error:private] =>
[code:protected] => 0
[file:protected] => D:\xampp\htdocs\demo\bz.github.io\exception.php
[line:protected] => 14
[trace:Error:private] => Array
(
)

[previous:Error:private] =>
[xdebug_message] =>
Error: Call to undefined function test()
)
*/

7、intdiv函数

1
2
3
<?php
echo intdiv(10, 3); // 3
echo intval(10 / 3); // 3

为什么纯PHP文件末尾不建议使用闭合标签

发表于 2019-03-21 | 分类于 PHP

先看两个PHP文件,
A.php ?> 后面有空行

1
2
3
4
5
6
7
8
9
<?php
class A
{
const A_TEST = 'A';
}
?>


.

B.php 引入 A.php,在输出之前有header等函数调用

1
2
3
4
5
require_once(__DIR__ . '/A.php');

header('HTTP/1.1 404 Not Found');
header('status:404 Not Found');
echo A::A_TEST;

此时执行B.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


PHP Warning: Cannot modify header information - headers already sent by (output started at D:\xampp\htdocs\demo\short1.php:10) in D:\xampp\htdocs\demo\short2.php on line 5
PHP Stack trace:
PHP 1. {main}() D:\xampp\htdocs\demo\short2.php:0
PHP 2. header() D:\xampp\htdocs\demo\short2.php:5

Warning: Cannot modify header information - headers already sent by (output started at D:\xampp\htdocs\demo\short1.php:10) in D:\xampp\htdocs\demo\short2.php on line 5

Call Stack:
0.0003 375864 1. {main}() D:\xampp\htdocs\demo\short2.php:0
0.0006 376280 2. header() D:\xampp\htdocs\demo\short2.php:5

PHP Warning: Cannot modify header information - headers already sent by (output started at D:\xampp\htdocs\demo\short1.php:10) in D:\xampp\htdocs\demo\short2.php on line 6
PHP Stack trace:
PHP 1. {main}() D:\xampp\htdocs\demo\short2.php:0
PHP 2. header() D:\xampp\htdocs\demo\short2.php:6

Warning: Cannot modify header information - headers already sent by (output started at D:\xampp\htdocs\demo\short1.php:10) in D:\xampp\htdocs\demo\short2.php on line 6

Call Stack:
0.0003 375864 1. {main}() D:\xampp\htdocs\demo\short2.php:0
0.0013 376280 2. header() D:\xampp\htdocs\demo\short2.php:6

short1[Finished in 0.2s]

这是因为在调用header函数之前已经有空行输出了,而这些函数在调用前是不允许有输出的,所以如果文件是纯PHP代码,那么建议去掉PHP文件的闭合标签,这样可以避免在PHP文件结束标记之后输入空格或者空行之后,输出无意义的空格或者空行。

header() 用于发送原生的 HTTP 头。header() 必须在任何实际输出之前调用,不管是普通的 HTML 标签,还是文件或 PHP 输出的空行,空格。这是个常见的错误,在通过include,require,或者其访问其他文件里面的函数的时候,如果在header()被调用之前,其中有空格或者空行。 同样的问题也存在于单独的 PHP/HTML 文件中。

123

清墨彰蓺

21 日志
5 分类
19 标签
© 2020 清墨彰蓺
由 Hexo 强力驱动 v3.8.0
|
主题 – NexT.Gemini v7.0.1
京ICP备18014858号-1
总访客量 | 总访问量