0%

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
/**
* 友好的时间显示
*
* size = 1, 显示1位,如: 1年、3个月、5天、20小时...
* size = 2, 显示2位,如: 1年1个月、1年3天、5天4小时、2小时25分...
* size = 3, 显示3位,如: 1年1个月4天、1年3天20小时、5天4小时3秒、2小时25分10秒...
* size = 4, 显示4位,如: 1年1个月4天16小时...
* size >= 5,显示5位,如: 1年1个月4天16小时15分钟...
*
* @param string|DateTime $datetime 日期字符串或日期 DateTime 对象
* @param int $size 精确到位数
* @param bool $absolute 正数的时间间隔
*
* @return string
*/
public static function friendlyDate($datetime, $size = 1, $absolute = false)
{
if (is_string($datetime)) {
$datetime = new DateTime($datetime);
}

if (! ($datetime instanceof DateTime)) {
throw new \InvalidArgumentException('invalid "DateTime" object');
}

$now = new DateTime();
if ($absolute && $datetime <= $now) {
return '';
}

$interval = $now->diff($datetime);

$intervalData = [
$interval->y, $interval->m, $interval->d,
$interval->h, $interval->i, $interval->s,
];
$intervalFormat = ['年', '个月', '天', '小时', '分钟', '秒'];

foreach ($intervalData as $key => $value) {
if ($value) {
$intervalData[$key] = $value . $intervalFormat[$key];
} else {
unset($intervalData[$key]);
unset($intervalFormat[$key]);
}
}

return implode('', array_slice($intervalData, 0, $size));
}

一道面试题

如果让你用 PHP 生成从 1 到100 万个数值,请问怎么做才能最省内存?

没错,这是一道面试题,如果让你写出答案,你会有什么样的思路呢?请先独自思考几分钟。

可能你想到的会是这种方式:

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
// 我自己本地测试大概用了 34MB 内存
function makeLargeArray($length){
$dataset = [];
for ($i = 0; $i < $length; $i++) {
$dataset[] = $i;
}
return $dataset;
}
$customRange = makeLargeArray(1000000);
foreach ($customRange as $i) {
echo $i, PHP_EOL;
}

// 记录使用的内存
function formatBytes($bytes, $precision = 2) {
$units = array("b", "kb", "mb", "gb", "tb");

$bytes = max($bytes, 0);
$pow = floor(($bytes ? log($bytes) : 0) / log(1024));
$pow = min($pow, count($units) - 1);

$bytes /= (1 << (10 * $pow));

return round($bytes, $precision) . " " . $units[$pow];
}

print formatBytes(memory_get_peak_usage());

你如果给出这种答案的话,肯定不会让面试官满意的。因为这道题的重点是节省内存,如果是如上的程序,虽然能够实现题目的前半部分要求,但实际运行起来却是一个非常吃内存的老虎。因为从 PHP 底层分析, makeLargeArray() 会预先创建一个由一百万个整数组成的数组分配内存!

那么到底要怎么做才能既节省内存,又能实现这道题的目标呢?

阅读全文 »

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
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
#运行用户
user nobody;
#启动进程,通常设置成和cpu的数量相等
worker_processes 1;

#全局错误日志及PID文件
#error_log logs/error.log;
#error_log logs/error.log notice;
#error_log logs/error.log info;

#pid logs/nginx.pid;

#工作模式及连接数上限
events {
#epoll是多路复用IO(I/O Multiplexing)中的一种方式,
#仅用于linux2.6以上内核,可以大大提高nginx的性能
use epoll;

#单个后台worker process进程的最大并发链接数
worker_connections 1024;

# 并发总数是 worker_processes 和 worker_connections 的乘积
# 即 max_clients = worker_processes * worker_connections
# 在设置了反向代理的情况下,max_clients = worker_processes * worker_connections / 4 为什么
# 为什么上面反向代理要除以4,应该说是一个经验值
# 根据以上条件,正常情况下的Nginx Server可以应付的最大连接数为:4 * 8000 = 32000
# worker_connections 值的设置跟物理内存大小有关
# 因为并发受IO约束,max_clients的值须小于系统可以打开的最大文件数
# 而系统可以打开的最大文件数和内存大小成正比,一般1GB内存的机器上可以打开的文件数大约是10万左右
# 我们来看看360M内存的VPS可以打开的文件句柄数是多少:
# $ cat /proc/sys/fs/file-max
# 输出 34336
# 32000 < 34336,即并发连接总数小于系统可以打开的文件句柄总数,这样就在操作系统可以承受的范围之内
# 所以,worker_connections 的值需根据 worker_processes 进程数目和系统可以打开的最大文件总数进行适当地进行设置
# 使得并发总数小于操作系统可以打开的最大文件数目
# 其实质也就是根据主机的物理CPU和内存进行配置
# 当然,理论上的并发总数可能会和实际有所偏差,因为主机还有其他的工作进程需要消耗系统资源。
# ulimit -SHn 65535

}

http {
#设定mime类型,类型由mime.type文件定义
include mime.types;
default_type application/octet-stream;
#设定日志格式
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';

access_log logs/access.log main;

#sendfile 指令指定 nginx 是否调用 sendfile 函数(zero copy 方式)来输出文件,
#对于普通应用,必须设为 on,
#如果用来进行下载等应用磁盘IO重负载应用,可设置为 off,
#以平衡磁盘与网络I/O处理速度,降低系统的uptime.
sendfile on;
#tcp_nopush on;

#连接超时时间
#keepalive_timeout 0;
keepalive_timeout 65;
tcp_nodelay on;

#开启gzip压缩
gzip on;
gzip_disable "MSIE [1-6].";

#设定请求缓冲
client_header_buffer_size 128k;
large_client_header_buffers 4 128k;


#设定虚拟主机配置
server {
#侦听80端口
listen 80;
#定义使用 www.nginx.cn访问
server_name www.nginx.cn;

#定义服务器的默认网站根目录位置
root html;

#设定本虚拟主机的访问日志
access_log logs/nginx.access.log main;

#默认请求
location / {

#定义首页索引文件的名称
index index.php index.html index.htm;

}

# 定义错误提示页面
error_page 500 502 503 504 /50x.html;
location = /50x.html {
}

#静态文件,nginx自己处理
location ~ ^/(images|javascript|js|css|flash|media|static)/ {

#过期30天,静态文件不怎么更新,过期可以设大一点,
#如果频繁更新,则可以设置得小一点。
expires 30d;
}

#PHP 脚本请求全部转发到 FastCGI处理. 使用FastCGI默认配置.
location ~ .php$ {
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}

#禁止访问 .htxxx 文件
location ~ /.ht {
deny all;
}

}
}
阅读全文 »

Version

PhpStorm 2017.3.3
Postman 5.5.0
php-7.1.12-Win32-VC14-x64 TS

安装 xdebug 模块

前往 xdebug 官方网站 下载与自己的 PHP 版本匹配的 xdebug模块文件,如果不知道要选择哪个版本的 xdebug,可以在浏览器上查看 phpinfo(),复制全部 phpinfo()这里自动解析。最后,将 xdebug 模块文件放入PHP安装目录/ext文件夹中。

配置 php.ini

php.ini 添加如下几行配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
[xdebug]
;指定Xdebug扩展文件
zend_extension = php_xdebug.dll
xdebug.default_enable = 1
;远程调试
xdebug.remote_enable = on
;自动打开远程调试
xdebug.remote_autostart = 1
;代码自动跟踪
xdebug.auto_trace = off
;收集传递给函数的参数变量
xdebug.collect_params=on
;收集函数调用的返回值
xdebug.collect_return=on
;性能检测分析
xdebug.profiler_enable_trigger = on
;生成的分析文件
xdebug.profiler_output_name = cachegrind.out.%t.%p
;堆栈跟踪文件的存放目录
xdebug.profiler_output_dir = "D:/laragon/tmp"
extension=php_printer.dll
;指定传递给DBGp调试器处理程序的IDE Key 在Chrome以及FireFox中插件配置的时候要用到
xdebug.idekey=PHPSTORM
阅读全文 »

git 没有跟踪空目录,所以需要跟踪那么就需要添加文件,就是在目录下新建一个 .gitkeep 文件(文件名其实随意,不过常见的是 .gitkeep.keep)方法如下:

1
find . -type d -empty -exec touch {}/.gitkeep \;

实现代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* 对微信退款回调的加密数据进行解密
*
* @param string $encryptData 原始加密数据
* @param string $key 商户 key
*
* @return string 解密后的 xml 数据
*/
public function decryptData( $encryptData, $key = '' )
{
//1. base64_decode
$encryptData = base64_decode($encryptData);

//2. md5 original key
$md5LowerKey = strtolower(md5($key));

//3. decrypt AES ECB
$decrypted = openssl_decrypt($encryptData, 'AES-256-ECB', $md5LowerKey, OPENSSL_RAW_DATA);

return $decrypted;
}

遇到的坑

  • openssl_decrypt 第四个参数一定要使用 OPENSSL_RAW_DATA

PHP 中的 Exception, Error, Throwable

  • PHP 中将代码自身异常(一般是环境或者语法非法所致)称作错误 Error,将运行中出现的逻辑错误称为异常 Exception
  • 错误是没法通过代码处理的,而异常则可以通过 try/catch 来处理
  • PHP7 中出现了 Throwable 接口,该接口由 ErrorException 实现,用户不能直接实现 Throwable 接口,而只能通过继承 Exception 来实现接口

PHP7 异常处理机制

过去的 PHP,处理致命错误几乎是不可能的。致命错误不会调用由 set_error_handler() 设置的处理方式,而是简单的停止脚本的执行。

在 PHP7 中,当致命错误和可捕获的错误(E_ERRORE_RECOVERABLE_ERROR)发生时会抛出异常,而不是直接停止脚本的运行。对于某些情况,比如内存溢出,致命错误则仍然像之前一样直接停止脚本执行。在 PHP7 中,一个未捕获的异常也会是一个致命错误。这意味着在 PHP5.x 中致命错误抛出的异常未捕获,在 PHP7 中也是致命错误。

注意:其他级别的错误如 warningnotice,和之前一样不会抛出异常,只有 fatalrecoverable 级别的错误会抛出异常。

阅读全文 »

容器,字面上理解就是装东西的东西。常见的变量、对象属性等都可以算是容器。一个容器能够装什么,全部取决于你对该容器的定义。当然,有这样一种容器,它存放的不是文本、数值,而是对象、对象的描述(类、接口)或者是提供对象的回调,通过这种容器,我们得以实现许多高级的功能,其中最常提到的,就是 “解耦” 、“依赖注入(DI)”。本文就从这里开始。

Version

Laravel Framework version 5.1.41 (LTS)

IoC 容器, laravel 的核心

Laravel 的核心就是一个 IoC 容器,根据文档,称其为“服务容器”,顾名思义,该容器提供了整个框架中需要的一系列服务。作为初学者,很多人会在这一个概念上犯难,因此,我打算从一些基础的内容开始讲解,通过理解面向对象开发中依赖的产生和解决方法,来逐渐揭开“依赖注入”的面纱,逐渐理解这一神奇的设计理念。

本文一大半内容都是通过举例来让读者去理解什么是 IoC(控制反转)DI(依赖注入),通过理解这些概念,来更加深入。更多关于 laravel 服务容器的用法建议阅读文档即可。

IoC 容器诞生的故事

讲解 IoC 容器有很多的文章,我之前也写过。但现在我打算利用当下的灵感重新来过,那么开始吧。

阅读全文 »

将 markdown 转化为 html 的转化器叫做 markdown 渲染器。

在 Hexo 中默认的 markdown 渲染器是 hexo-renderer-marked ,这个渲染器是不支持 emoji 表情的。

在 NexT 的 issue 上有推荐使用 hexo-tag-emojis 这个插件来支持 emoji 表情,但可惜的是这个插件已经不再维护,已经不支持 Hexo v3+ 了。还好在 Hexo 的 plugins 页,我们找到了另外一个 emoji 插件 hexo-filter-github-emojis

下面我们就来实现 Hexo NexT 对 emoji 的支持。

Version

Hexo 3.3.9
NexT 5.1.2

安装

1
$ npm install hexo-filter-github-emojis --save

配置

打开 站点配置文件,添加以下内容:

1
2
3
4
5
6
githubEmojis:
enable: true
className: github-emoji
unicode: false
styles:
localEmojis:

具体的每个配置项含义可以看插件的文档

阅读全文 »