​​​​ 开发中遇到的问题整理 | 苏生不惑的博客

开发中遇到的问题整理

emoji

utf8 编码问题

1
2
3
4
5
6
7
8
9
10
11
  function nickname_encode($s){
$chars = preg_split('//u', $s, null, PREG_SPLIT_NO_EMPTY);
foreach ($chars as &$c) {
if (strlen($c) > 3 || $c === "%"){
$c = urlencode($c);
}
}

return implode($chars);
}
解码直接用urldecode就行了。

strtotime

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
var_dump(date("Y-m-d", strtotime("2017-06-31")));
//输出2017-07-01
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

需要获得多少天之前 $day

mktime(0, 0, 0, date("m", $startTime), date("d", $startTime) - $day, date("Y", $startTime));

这个坑在carbon中一样的有. 比如:Carbon::now()->subMonth(1)->startOfMonth() 一样的会出现上面说的问题. 你必须这样写:Carbon::now()->startOfMonth()->subMonth(1)
Carbon::now()->subMonthNoOverflow(1)->startOfMonth()
https://laravel-china.org/articles/17317

$i = 12;

while($i >= 1) {
echo date('Y-m', strtotime('-' . $i . ' month', strtotime('2018-8'))) . "\n";
$i--;
}
https://laravel-china.org/articles/18175?#reply67278

解决方法

三元运算符

1
2
3
4
5
6
7
8
9
10
11
运算符可 用于判断$a变量不存在的情况(也可用于数组),而使用三元运算符判断一个未定义的变量,PHP会抛出异常。也正是因为这样,用??判断一个赋值为0的变量的时候结果是不一样的。https://laravel-china.org/articles/17304?#reply64712
$a=0;
$c=1;
$b=$a??$c;
echo 'a:'.$a.',b:'.$b.',c:'.$c;
//a:0,b:0,c:1
$a=0;
$c=1;
$b=$a?$a:$c;
echo 'a:'.$a.',b:'.$b.',c:'.$c;
//a:0,b:1,c:1

Error while reading line from the server. [tcp://127.0.0.1:6379]

redis 加了代理 Twemproxy,而 predis 对这个配置默认执行 select 操作,导致了连接错误。

大家以后要注意,如果 redis 有代理的话,别忘了把 'database' => 0,这个配置删掉。
https://laravel-china.org/topics/6774/meet-a-pit-of-laravel-redis-share

PHP empty 函数判断结果为空

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class person
{
protected $attributes = [];

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

public function __get($name)
{
return $this->attributes[$name] ?? null;
}
}
https://laravel-china.org/articles/12530/the-shock-php-empty-function-is-empty-but-the-actual-value-is-nonempty

https://laravel-china.org/topics/3021/isset-is-not-right-after-upgrading-php7
var_dump(
$person->firstName,
empty($person->firstName),
isset($person->firstName),
is_null($person->firstName)
);

Laravel Redis 多个进程同时取队列问题

Laravel 使用 Redis 的 list 作为队列的数据结构,并会为每个队列分配一个 ID
Laravel 中入队列方法

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
 
public function pushRaw($payload, $queue = null, array $options = [])
{
$this->getConnection()->rpush($this->getQueue($queue), $payload);

return Arr::get(json_decode($payload, true), 'id');
}
用的是 Redis 的 rpush 命令。

Laravel 中取队列方法

public function pop($queue = null)
{
$original = $queue ?: $this->default;
$queue = $this->getQueue($queue);
$this->migrateExpiredJobs($queue.':delayed', $queue);
if (! is_null($this->expire)) {
$this->migrateExpiredJobs($queue.':reserved', $queue);
}
list($job, $reserved) = $this->getConnection()->eval(
LuaScripts::pop(), 2, $queue, $queue.':reserved', $this->getTime() + $this->expire
);
if ($reserved) {
return new RedisJob($this->container, $this, $job, $reserved, $original);
}
}
这里用的是 lua 脚本取队列,如下:


public static function pop()
{
return <<<'LUA'
local job = redis.call('lpop', KEYS[1])
local reserved = false
if(job ~= false) then
reserved = cjson.decode(job)
reserved['attempts'] = reserved['attempts'] + 1
reserved = cjson.encode(reserved)
redis.call('zadd', KEYS[2], ARGV[1], reserved)
end
return {job, reserved}
LUA;
}

那么结论是:从 Laravel 的处理方式和打印的日志结果看,即使多个进程读取同一个队列,也不会读取到一样的数据。

队列执行失败之后根本不会执行failed方法

执行队列侦听器时加上 –tries参数即可,failed()方法只有在重试指定次数完成后才会调用failed()方法如:
http://blog.zhouchenxi.cn/post/51.html
php artisan queue:listen –queue=default,listeners –tries=3
顺便提一下:如果指定了队列名称必须在侦听器的参数上加上 –queue参数才行,不然没有指定的队列是不会运行的。
event 指定队列

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
  public $queue = 'listeners';
public function queue($queue, $job, $data)
{
if (isset($this->queue, $this->delay)) {
return $queue->laterOn($this->queue, $this->delay, $job, $data);
}

if (isset($this->queue)) {
return $queue->pushOn($this->queue, $job, $data);
}

if (isset($this->delay)) {
return $queue->later($this->delay, $job, $data);
}

return $queue->push($job, $data);
}

php7不支持 preg_replace e修正符

车轮升级PHP7踩过的一些坑 http://php.swoole.com/wiki/%E8%BD%A6%E8%BD%AE%E5%8D%87%E7%BA%A7PHP7%E8%B8%A9%E8%BF%87%E7%9A%84%E4%B8%80%E4%BA%9B%E5%9D%91

php5 php7不兼容的地方:https://segmentfault.com/a/1190000013837897

1
2
3
4
5
$joinStr = preg_replace("/__([A-Z_-]+)__/e",$prex.".strtolower('$1')",$joinStr);
//替换为
preg_replace_callback("/__([A-Z_-]+)__/",function($r) use (&$joinStr){
$joinStr = $prex.strtolower($r[1]);
},$joinStr);

内存溢出

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
//https://laravel-china.org/articles/16312/handling-large-amounts-of-data-to-write-files-to-avoid-memory-overflow-and-response-timeout
$beginIndex = 0;
$pageLimit = 50000;
$endIndex = $beginIndex + $pageLimit;

while ($pageData = \DB::connection('hub_log')->select("SELECT * FROM `{$tableName}` WHERE id > {$beginIndex} and id <= {$endIndex}")) {

foreach ($pageData as $row) {
file_put_contents(storage_path('mcd_rawdata/'.$tableName.'.txt'), $row->Content . "\n", FILE_APPEND);
}

$beginIndex += $pageLimit;
$endIndex += $pageLimit;
}

$public_path = $time.'test.txt';

DB::table('20171220')->orderBy('id')->chunk(10000,function($data) use ($public_path){
$arr = '';
foreach ($data->toArray() as $key => $value) {
$arr .= $value->Content."\n";
}

file_put_contents(public_path($public_path),$arr, FILE_APPEND);
unset($data);unset($arr);
});

git bash 输入python命令停滞

$ python -i
$ winpty python -3

保留两位小数

round(a,2)
r = lambda f: f - f % 0.01
int(a*100)/100
print(“%.2f” %a)

后期绑定

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//https://www.v2ex.com/t/482659#reply8
def testFun():
return(lambda x : i*x for i in range(4))
for everyLambda in testFun():
print(everyLambda(2))
& python test.py
0
2
4
6
def testFun():
temp = [lambda x : i*x for i in range(4)]
return temp
for everyLambda in testFun():
print(everyLambda(2))
& python test.py
6
6
6
6
第一个是括号(), 为生成器, 返回 generator
第二个是中括号[], 为列表生成式, 返回数组 列表表达式是即时计算的, 而生成器是迭代时才会计算

{} + []

1
2
3
4
5
6
({}).valueOf() // []
// 返回来的是个对象,继续调用 toString https://www.v2ex.com/t/480998#reply11
[].toString() // ""

[] + 1// "1"
[] + 'a' // "a"

echo 后发送 header

使用 fastcgi_finish_request();( PHP-fpm )可以强制断开客户端连接 session 本质上是 header set-cookie,所以不算输出。这里的输出指的是 response body 的部分
https://www.v2ex.com/t/481127#reply16
clipboard.png

isset在php5.6-和php7.0+的一些差异

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
https://segmentfault.com/a/1190000016097997
class ProductCategory
{
const TYPES = [
1 => 'type1',
2 => 'type2',
];

public function getType()
{
return isset(self::TYPES[$this->type]) ? self:TYPES[$this->type] : 'unrecognized_type';
}
}
//把汉字拆分为数组
function ch2arr($str)
{
return preg_split('//u', $str, null, PREG_SPLIT_NO_EMPTY);
}

laravel 中使用 导出 excel 时报错

PHPExcel_Calculation_Exception: Q5!A65 → Formula Error: An unexpected error occured in /application/www/web_git-pull/vendor/phpoffice/phpexcel/Classes/PHPExcel/Cell.php:291

原因:

在excel中一个单元格如果是以=开头,则说明这个单元格是根据其他单元格的值算出来的,=后面必须跟着一个合法的表达式,而那个字符串是用户的输入,很明显不应该是一个合法的表达式,所以应该在代码中过滤掉

解决:
$str = "\t".$str;//同时解决 如 11111111111 显示成 1.11111E+29
参考
https://github.com/Maatwebsite/Laravel-Excel/issues/506
https://stackoverflow.com/questions/11189145/formula-error-in-phpexcel

gzip UnicodeDecodeError

gzip解码问题

clipboard.png

1
2
3
4
5
6
7
8
9
import urllib.request
import gzip

# 注意要先读取内容才可以解压缩
data = urllib.request.urlopen(url).read()
try:
html =data.decode("utf-8")
except:
html =gzip.decompress(data).decode("utf-8")

Intervention/Image 图片写入中文

1
2
3
4
5
6
7
8
9
10
11
12
https://laravel-china.org/topics/13642/problems-encountered-in-writing-chinese-with-interventionimage-pictures-and-solutions
function to_unicode($string)
{
$str = mb_convert_encoding($string, 'UCS-2', 'UTF-8');
$arrstr = str_split($str, 2);
$unistr = '';
foreach ($arrstr as $n) {
$dec = hexdec(bin2hex($n));
$unistr .= '&#' . $dec . ';';
}
return $unistr;
}

获取某字段修改前的值

1
2
3
4
5
6
7
8
Issue::saving(function(Issue $issue){
if ($issue->isDirty('title')) {
$user = Auth::user()->username;
$oldTitle = $issue->getOriginal('title'); // 原始值
$newTitle = $issue->title; // 新值
ActionLog::log("$user 把标题 $oldTitle 修改为 $newTitle");
}
});

前一天

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
https://www.cnblogs.com/lhm166/articles/6066762.html
$mytime=mktime(0, 0, 0, date('m'), date('d')-1, date('Y'));//获取时间戳
$mytime=date("Y-m-d H:i:s", strtotime("-1 day")); //获取格式为2016-12-30 13:26:13
$timetoday = strtotime(date("Y-m-d",time()));
function mdate($time = NULL) {
$text = '';
$time = $time === NULL || $time > time() ? time() : intval($time);
$t = time() - $time; //时间差 (秒)
$y = date('Y', $time)-date('Y', time());//是否跨年
switch($t){
case $t == 0:
$text = '刚刚';
break;
case $t < 60:
$text = $t . '秒前'; // 一分钟内
break;
case $t < 60 * 60:
$text = floor($t / 60) . '分钟前'; //一小时内
break;
case $t < 60 * 60 * 24:
$text = floor($t / (60 * 60)) . '小时前'; // 一天内
break;
case $t < 60 * 60 * 24 * 3:
$text = floor($time/(60*60*24)) ==1 ?'昨天 ' . date('H:i', $time) : '前天 ' . date('H:i', $time) ; //昨天和前天
break;
case $t < 60 * 60 * 24 * 30:
$text = date('m月d日 H:i', $time); //一个月内
break;
case $t < 60 * 60 * 24 * 365&&$y==0:
$text = date('m月d日', $time); //一年内
break;
default:
$text = date('Y年m月d日', $time); //一年以前
break;
}

return $text;
}function friend_date($time)
{
if (!$time)
return false;
$fdate = '';
$d = time() - intval($time);
$ld = $time - mktime(0, 0, 0, 0, 0, date('Y')); //得出年
$md = $time - mktime(0, 0, 0, date('m'), 0, date('Y')); //得出月
$byd = $time - mktime(0, 0, 0, date('m'), date('d') - 2, date('Y')); //前天
$yd = $time - mktime(0, 0, 0, date('m'), date('d') - 1, date('Y')); //昨天
$dd = $time - mktime(0, 0, 0, date('m'), date('d'), date('Y')); //今天
$td = $time - mktime(0, 0, 0, date('m'), date('d') + 1, date('Y')); //明天
$atd = $time - mktime(0, 0, 0, date('m'), date('d') + 2, date('Y')); //后天
if ($d == 0) {
$fdate = '刚刚';
} else {
switch ($d) {
case $d < $atd:
$fdate = date('Y年m月d日', $time);
break;
case $d < $td:
$fdate = '后天' . date('H:i', $time);
break;
case $d < 0:
$fdate = '明天' . date('H:i', $time);
break;
case $d < 60:
$fdate = $d . '秒前';
break;
case $d < 3600:
$fdate = floor($d / 60) . '分钟前';
break;
case $d < $dd:
$fdate = floor($d / 3600) . '小时前';
break;
case $d < $yd:
$fdate = '昨天' . date('H:i', $time);
break;
case $d < $byd:
$fdate = '前天' . date('H:i', $time);
break;
case $d < $md:
$fdate = date('m月d日 H:i', $time);
break;
case $d < $ld:
$fdate = date('m月d日', $time);
break;
default:
$fdate = date('Y年m月d日', $time);
break;
}
}
return $fdate;
}

phpstorm xdebug

1
2
3
4
5
6
7
8
9
https://www.cnblogs.com/baocheng/p/5775938.html
https://www.cnblogs.com/niuxiaoling/p/8027161.html
https://laravel-china.org/topics/13275/vagrant-phpstorm-xdebug
https://laravel-china.org/articles/12974/phpstrom-xdebug-breakpoint-debug-tutorial
https://laravel-china.org/articles/16425
如果出现Can't start listening for connections from 'xdebug': Port 9000 is busy
可修改端口号,要修改两个地方
第一个地方是刚才php.ini里面的xdebug.remote_port=9000
第二个地方是phpstorm - setting - Languages&Frameworks - 相应的语言(比如PHP) - debug - 修改里面的Debug port

Python for 循环中的陷阱

https://www.v2ex.com/t/470443#reply38

pyinstaller 打包 exe

pyinstaller -p venv_path (中间一定要有空格) you.py https://www.v2ex.com/t/476247#reply25
http://ju.outofmemory.cn/entry/137370

php捕获fatal error

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function fatal_handler() {
$errfile = "unknown file";
$errstr = "shutdown";
$errno = E_CORE_ERROR;
$errline = 0;
$error = error_get_last();
if($error){
//发送邮件队列也可以http://www.iamtb.cn/2017/11/17/php-catch-fatel-error/
file_put_contents('./testerror11.txt', json_encode($error));
}
}
register_shutdown_function("fatal_handler");
try{
$db=new db();
}catch(Exception $e){
echo $e->error_msg();
}

PHP7开始,含十六进制字符串不再被认为是数字

1
2
3
4
5
6
7
8
9
10
$str = "0xffff";//https://segmentfault.com/q/1010000004829059
$int = filter_var($str, FILTER_VALIDATE_INT, FILTER_FLAG_ALLOW_HEX);
if (false === $int) {
throw new Exception("Invalid integer!");
}
var_dump($int); // int(65535)
$t1 = 0x3FFFFFFF & (1 * (0xd5b42e11));
$t2 = 0x3FFFFFFF & (1 * (filter_var("0xd5b42e11", FILTER_VALIDATE_INT, FILTER_FLAG_ALLOW_HEX)));

var_dump($t1,$t2);

php artisan config:cache

clipboard.png

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
cat config/sentry.php
return array(
'dsn' => env('SENTRY_LARAVEL_DSN'),

// capture release as git sha
// 'release' => trim(exec('git log --pretty="%h" -n1 HEAD')),

// Capture bindings on SQL queries
'breadcrumbs.sql_bindings' => true,

// Capture default user context
'user_context' => false,

//transport function
'transport'=>function(Raven_Client $raven_Client,&$data){
$raven_Client->setTransport(null);
$raven_Client->close_curl_resource();
\Queue::pushOn('sentry',new \App\Commands\sentry($raven_Client,$data));
},
);

'transport'=>new \app\SentryTransport(),
class SentryTransport
{
public static function __set_state($arr){
return function($raven_Client,&$data){
\Queue::pushOn('sentry',new \App\Commands\sentry($raven_Client,$data));
};
}
}

curl超时

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
//重试
$ch = curl_init();
// curl_setopt ($ch, CURLOPT_URL, 'produc_redis.php.com');
curl_setopt ($ch, CURLOPT_URL, '11.11.11.1');

https://segmentfault.com/a/1190000011188541
// I changed UA here
curl_setopt ($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.1) Gecko/20061204 Firefox/2.0.0.1');

curl_setopt ($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt ($ch, CURLOPT_CONNECTTIMEOUT, 2);
curl_setopt ($ch, CURLOPT_AUTOREFERER, true);
curl_setopt ($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt ($ch, CURLOPT_SSL_VERIFYHOST, 2);

$html = curl_exec($ch);

var_dump($html,curl_error($ch));

$num=3;
for($i=1;$i<=$num;$i++){
if(curl_getinfo($ch,CURLINFO_HTTP_CODE)=='0' && $i<=$num){
echo "retry $i 次".PHP_EOL;
if($i==3){
curl_setopt ($ch, CURLOPT_URL, '220.181.57.217');
}
$html = curl_exec($ch);
}
}

var_dump($html);

var_dump(curl_error($ch));

// var_dump(curl_getinfo($ch));

/**
* curl请求
*https://segmentfault.com/a/1190000010197068
* @param $url
* @param string $postData
* @param int $timeout
* @return array|mixed
* @throws Exception
*/
protected static function post($url, $postData = '', $timeout = 5)
{
$ret = array();
$times = 5;
do {
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HEADER, false);
if ($postData != '') {
curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
}
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout);
// 重要, 该处不能丢 curl 执行最大秒数
curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
$output = curl_exec($ch);
if ($errNo = curl_errno($ch)) {
error_log("Error [$errNo]: " . curl_error($ch));
} else {
$ret = json_decode($output, true);
// 解析的结果集为空时停止查询
if (!is_array($ret) && !trim($ret)) {
throw new Exception(__METHOD__ . ": cURL调用失败, 信息为: " . $output);
}
unset($output);
}
curl_close($ch);
if (isset($ret[0]) && $ret[0]) {
return $ret;
}
} while ($times--);
exit(__METHOD__ . ": cURL请求重试至 {$times} 次后仍无响应, 执行退出");
}

cURL error 28

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
cURL error 28: See http://curl.haxx.se/libcurl/c/libcurl-errors.html
$client=new GuzzleHttp\Client();
try {
$tmp = $client->get($url,array("timeout"=>3,"connect_timeout"=>3));
$res = json_decode(strval($tmp->getBody()),1);
} catch (\Exception $e) {
$res = [];
\Log::info('error', ['url' => $url, 'msg' => $e->getMessage()]);
}
$client=new GuzzleHttp\Client();
$requests=[
$client->createRequest('GET',$url,["timeout"=>3,"connect_timeout"=>3]),
$client->createRequest('GET',$url2,["timeout"=>3,"connect_timeout"=>3]),

];
$result = Pool::batch($client, $requests, ['pool_size' => 2]);
//$result->getResult($requests[0]) instanceof GuzzleHttp\Message\Response
if ($result[0] instanceof Response && $result[0]->getStatusCode() == 200) {
$data = json_decode($result[0]->getBody(), true);
}

格式化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import prettytable as pt
https://www.v2ex.com/t/483984#reply15
## 按行添加数据
tb = pt.PrettyTable()
tb.field_names = ["City name", "Area", "Population", "Annual Rainfall"]
tb.add_row(["Adelaide",1295, 1158259, 600.5])
tb.add_row(["Brisbane",5905, 1857594, 1146.4])
tb.add_row(["Darwin", 112, 120900, 1714.7])
tb.add_row(["Hobart", 1357, 205556,619.5])

print(tb)
#str.ljust 和 rjust 才是王道,谁用谁知道!

+-----------+------+------------+-----------------+
| City name | Area | Population | Annual Rainfall |
+-----------+------+------------+-----------------+
| Adelaide | 1295 | 1158259 | 600.5 |
| Brisbane | 5905 | 1857594 | 1146.4 |
| Darwin | 112 | 120900 | 1714.7 |
| Hobart | 1357 | 205556 | 619.5 |
+-----------+------+------------+-----------------+

unicode

1
2
3
4
5
6
7
8
9
10
11
12
13
Unicode 每个字符的详情,可以查官方文档: https://www.unicode.org/charts/
python2 代码:

for i in range(0x005b,0x005f):
print (unichr(i))#print (chr(i))
python3 代码请查看 http://www.chenxm.cc/post/683.html
unicode 字符表示方法 \u + 16 进制数值,例如 \u0001 print(''.join(chr(u) for u in range(0, 0x41))) \u0040 这里 0040 就是 16 进制的,range 是左闭右开的,所以\u0020-\u0040 的码点范围是 range(0x00, 0x40+1)
0b 二进制 0o 八进制 0x 16 进制
0b10 // 2
0o10 // 8
0x10 // 16
unicode 字符表示方法 \u + 16 进制数值,例如 \u0001
print(u'\u0061') // ahttps://www.v2ex.com/t/484923#reply15

laravel更新后读取数据失败

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
https://laravel-china.org/articles/16566/take-a-laravel-mysql-to-verify-the-experience-of-bug#reply62596
//创建数据
$meeting = meeting::create($data);

//查询数据
$meeting = meeting::findOrFail($meeting->id);

//纪录操作日志
Log::insert($meeting);
线上数据库是读写分离的,主库写入,但更新数据后马上查询,从库可能没来得及更新数据
当网络不好时遇到从库更新不及时 findOrFail 就会报 404,导致后面所有操作都失效,自然验证也失效了
解决方法

更新完后,查询指定使用主库
更新后禁止查询,直接使用 create 返回的 model 实例,对于数据库中的有默认值的字段,手动加入到 model 实例中

批量更新

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
//https://laravel-china.org/topics/6864/how-to-batch-update-accumulate-a-field
https://laravel-china.org/articles/16620/laravel-project-execl-batch-import-database-data-update-data-no-insertion
$arr = [
// user_id => reward
'1' => 50,
'2' => 30,
'3' => 60
];
User::find(array_keys($arr))->each(function ($query) use ($arr) {
$query->increment('award' , $arr[$query->id]);
});
# 批量更新相同的 award
User::find(array_keys($arr))->each->increment('award', 30);

/**
* 批量更新表的值,防止阻塞
* @note 生成的SQL语句如下:
* update mj_node set sort = case id
* when 13 then 1
* when 1 then 4
* when 7 then 5
* when 8 then 6
* when 9 then 7
* when 10 then 8
* when 11 then 9
* when 12 then 10
* end where id in (13,1,7,8,9,10,11,12)
* @param $conditions_field 条件字段
* @param $values_field 需要被更新的字段
* @param $conditions
* @param $values
* @return int
*/
public function batchUpdate($conditions_field, $values_field, $conditions, $values)
{
$table = $this->model->getFullTableName();//返回完整表名,自己在项目中定义
$sql = 'update ' . $table . ' set '. $values_field .' = case ' .$conditions_field;
foreach ($conditions as $key => $condition) {
$sql .= ' when ' . $condition . ' then ?';
}
$sql .= ' end where id in (' . implode(',', $conditions) . ')';
return DB::update($sql, $values);//项目中需要引入DB facade
}

cat 多换行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
https://www.v2ex.com/t/188162
调试shell脚本可以使用 sh -x ****.sh,有详细输出内容
Bash的echo默认在末尾加了换行符"\n",所以你cat那个文件多了一个换行符,给echo加上-n参数就能不自动加换行符了。

var= cat tmp.txt;echo $var"abc" 改成如下的形式

var= `cat tmp.txt`;echo $var"abc"或者 #注意不是单引号,是tab键上面1前面的那个符号
var=$(cat tmp.txt);echo $var"abc"
grep显示前后几行信息
grep -C 5 foo file 显示file文件里匹配foo字串那行以及上下5
grep -B 5 foo file 显示foo及前5
grep -A 5 foo file 显示foo及后5
$grep -5 'parttern' inputfile //打印匹配行的前后5行
查看某一个文件第5行和第10

sed -n '5,10p' filename 这样你就可以只查看文件的第5行到第10行。

grep and or

1
2
3
4
5
6
https://blog.csdn.net/jackaduma/article/details/6900242
grep -E 'Tech|Sales' employee.txt
grep -v 'pattern1' filename
$ grep -E 'Manager.*Sales|Sales.*Manager' employee.txt # -E 'pattern1.*pattern2'
egrep "php.*artisan.*$1"
egrep 'pattern1|pattern2' filename

memcached 默认的过期时间则 不能大于 30 天

https://laravel-china.org/topics/2461/laravel-session-using-memcached-stepping-on-the-pit

clipboard.png

unix:///tmp/supervisor.sock refused connection

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
[root@localhost supervisor]# supervisorctl -c /etc/supervisord.conf
unix:///tmp/supervisor.sock refused connection
supervisor> exit
rm -f /tmp/supervisor.sock
[root@localhost supervisor]# ps aux|grep super
root 48 0.0 0.0 0 0 ? S 2017 2:17 [sync_supers]
root 8246 0.0 0.1 208064 13404 ? Ss 18:17 0:00 /usr/bin/python /usr/bin/supervisord -c /etc/supervisord.conf
supervisord是服务端,是个deamon,supervisorctl是客户https://www.v2ex.com/t/210122
修改supervisor.conf文件后,要执行supervisorctl reload,重新加载配置文件
vi artisan.conf
[root@localhost supervisor]# supervisorctl status
unix:///tmp/supervisor.sock no such file
[root@localhost supervisor]# ps aux|grep super
root 48 0.0 0.0 0 0 ? S 2017 2:17 [sync_supers]
root 19472 0.0 0.0 103256 844 pts/5 S+ 19:57 0:00 grep super
[root@localhost supervisor]# supervisord -c /etc/supervisord.conf
Error: Invalid user name www in section 'program:artisan_queue' (file: '/etc/supervisor/artisan.conf')
For help, use /usr/bin/supervisord -h
[root@localhost supervisor]# vi /etc/supervisor/artisan.conf
[root@localhost supervisor]# supervisord -c /etc/supervisord.conf
[root@localhost supervisor]# supervisorctl status
artisan_queue BACKOFF Exited too quickly (process log may have details)

http://blog.51cto.com/jxlwc/2112062
https://www.jianshu.com/p/3658c963d28b

Specified key was too long

1
2
3
4
5
6
7
8
9
10
11
12
https://laravel-china.org/articles/17094/larave-admin-installation?#reply64528
默认的数据库字符集,现在utf8mb4包括存储emojis支持。如果你运行MySQL v5.7.7或者更高版本,则不需要做任何事情。
https://laravel-china.org/articles/4195/laravel-54-common-error-specified-key-was-too-long
https://segmentfault.com/a/1190000008416200
把config下的database.php下的mysql设置中,将'strict' => false就行了..
Schema::defaultStringLength(191); 是设置好的默认的字符串长度,也就是说,迁移中的字段没有设置长度的话,varchar 字段会被默认成长度只有 191
use Illuminate\Support\Facades\Schema;

public function boot()
{
Schema::defaultStringLength(191);
}

laravel with 查询中只查询个别字段

1
2
3
4
5
6
7
8
select 的时候没有 id 的话就会为 null https://laravel-china.org/articles/17379
public function brand()
{
return $this->belongsTo(Brand::class, 'brandId')->select('id', 'briefCode');
}
$result = Brand::with(['brand' => function($query){
$query->select('id', 'briefCode');
}])->get();

PHP输出A到Z

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
for($i='A'; $i<='Z'; $i++)
{
echo $i . '<br>';
}
for($i=ord('A'); $i<=ord('Z'); $i++)
{
echo chr($i) . '<br>';
}
for($i = 'A'; $i != 'BB'; $i++)
{
echo $i . '<br>';
}

$al = 'A';
while($al != 'BB')
{
echo $al++ . '<br>';
}

env函数的小坑

1
2
3
4
5
6
7
8
dump(env('APP_ENV') === null); // true 
因为没有执行 config:cache 命令,而项目上线后为了优化访问速度,生成了缓存文件,导致env取值失败
如果使用了config:cache,env函数只能在config目录下的配置文件的php里使用,不可以在其他地方使用。

一个非常简单的办法就是将
env('APP_ENV')改为config('app.env')
config函数会优先读取 bootstrap/cache/config.php 中缓存的配置,如果没有缓存文件,则会直接读取 config 目录下的所有配置文件
https://www.jianshu.com/p/83f9cd407751

任务调度的列表

1
2
3
4
5
//1. 加载Console内核
app()->make(\Illuminate\Contracts\Console\Kernel::class);

//2. 获取计划任务列表
$scheduleList = app()->make(\Illuminate\Console\Scheduling\Schedule::class)->events();

在模型事件中获取某字段修改前的值

1
2
3
4
5
6
7
8
Issue::saving(function(Issue $issue){
if ($issue->isDirty('title')) {
$user = Auth::user()->username;
$oldTitle = $issue->getOriginal('title'); // 原始值https://laravel-china.org/wikis/16169
$newTitle = $issue->title; // 新值
ActionLog::log("$user 把标题 $oldTitle 修改为 $newTitle");
}
});

laravel SerializesModels

因为我们在任务类里引用了 SerializesModels 这个 trait,使得 Eloquent 模型在处理任务时可以被优雅地序列化和反序列化。如果你的队列任务类在构造器中接收了一个 Eloquent 模型,那么只有可识别出该模型的属性会被序列化到队列里。当任务被实际运行时,队列系统便会自动从数据库中重新取回完整的模型。这整个过程对你的应用程序来说是完全透明的,这样可以避免在序列化完整的 Eloquent 模式实例时所带来的一些问题。https://laravel-china.org/topics/2993/laravel-novice-stepped-on-the-pit-to-share

composer require失败

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
cat composer.json
"monolog/monolog": "^2.0@dev",

$ composer require dees040/laravel-api-responses
1/1: https://packagist.laravel-china.org/p/provider-latest$0449728151603 e355a0ee137fb34881e699652e41e3e1de11ba9c4ea7a47a312.json
Finished: success: 1, skipped: 0, failure: 0, total: 1
Using version dev-master for dees040/laravel-api-responses
./composer.json has been updated
Loading composer repositories with package information
Updating dependencies (including require-dev)
Your requirements could not be resolved to an installable set of packages.

Problem 1
- The requested package monolog/monolog (locked at 1.23.0, required as ^2.0@dev ) is satisfiable by monolog/monolog[1.23.0] but these conflict with your requiremen ts or minimum-stability.
Problem 2
- laravel/framework v5.5.42 requires monolog/monolog ~1.12 -> satisfiable by mo nolog/monolog[1.x-dev].
- laravel/framework v5.5.42 requires monolog/monolog ~1.12 -> satisfiable by mo nolog/monolog[1.x-dev].
- laravel/framework v5.5.42 requires monolog/monolog ~1.12 -> satisfiable by mo nolog/monolog[1.x-dev].
- Can only install one of: monolog/monolog[2.x-dev, 1.x-dev].
- Installation request for monolog/monolog ^2.0@dev -> satisfiable by monolog/m onolog[2.x-dev].
- Installation request for laravel/framework (locked at v5.5.42, required as 5. 5.*) -> satisfiable by laravel/framework[v5.5.42].

$composer require "monolog/monolog:~1.12"
1/1: https://packagist.laravel-china.org/p/provider-latest$d12a81f48a3e1dad512c1d2c375298e3a8ea414dff5000d5f345939429c6b29b.json
Finished: success: 1, skipped: 0, failure: 0, total: 1
./composer.json has been updated
Loading composer repositories with package information
Updating dependencies (including require-dev)
1/1: https://dl.laravel-china.org/monolog/monolog/c465e1144536862e03f519cb3d65e924062cabfb.zip
Finished: success: 1, skipped: 0, failure: 0, total: 1
Package operations: 0 installs, 1 update, 0 removals
- Removing monolog/monolog (1.23.0)
- Installing monolog/monolog (1.x-dev c465e11):
Cloning c465e11445 from cache
Package phpoffice/phpexcel is abandoned, you should avoid using it. Use phpoffice/phpspreadsheet instead.
Writing lock file
Generating optimized autoload files
> Illuminate\Foundation\ComposerScripts::postAutoloadDump
> @php artisan package:discover
Discovered Package: 96qbhy/hyid
Discovered Package: appstract/laravel-opcache
Discovered Package: appstract/lush-http
Discovered Package: barryvdh/laravel-debugbar
Discovered Package: barryvdh/laravel-dompdf
Discovered Package: barryvdh/laravel-ide-helper
Discovered Package: base62/base62
Discovered Package: cblink/laravel-excel-zip
Discovered Package: cblink/laravel-sso
Discovered Package: chumper/zipper
Discovered Package: encore/laravel-admin
Discovered Package: encore/redis-manager
Discovered Package: fideloper/proxy
Discovered Package: foryoufeng/laravel-generator
Discovered Package: ibrand/laravel-miniprogram-poster
Discovered Package: intervention/image
Discovered Package: itsgoingd/clockwork
Discovered Package: jacobcyl/ali-oss-storage
Discovered Package: jellybool/flysystem-upyun
Discovered Package: jenssegers/agent
Discovered Package: jenssegers/mongodb
Discovered Package: laravel-admin-ext/helpers
Discovered Package: laravel/tinker
Discovered Package: latrell/lock
Discovered Package: lubusin/laravel-decomposer
Discovered Package: maatwebsite/excel
Discovered Package: nesbot/carbon
Discovered Package: overtrue/laravel-query-logger
Discovered Package: overtrue/laravel-socialite
Discovered Package: prettus/l5-repository
Discovered Package: sentry/sentry-laravel
Discovered Package: simplesoftwareio/simple-qrcode
Discovered Package: spatie/laravel-tail
Discovered Package: tymon/jwt-auth
Discovered Package: zgldh/qiniu-laravel-storage
Package manifest generated successfully.

$ composer require dees040/laravel-api-responses
1/1: https://packagist.laravel-china.org/p/provider-latest$14d9ca1b35af157c565d6c564cde71c59043d7457135c5a4317471625a1db2e7.json
Finished: success: 1, skipped: 0, failure: 0, total: 1
Using version dev-master for dees040/laravel-api-responses
./composer.json has been updated
Loading composer repositories with package information
Updating dependencies (including require-dev)
1/1: https://dl.laravel-china.org/dees040/laravel-api-responses/9aec7eb323fc45e17f1dbb9c6e203d240836f91b.zip
Finished: success: 1, skipped: 0, failure: 0, total: 1
Package operations: 1 install, 0 updates, 0 removals
- Installing dees040/laravel-api-responses (dev-master 9aec7eb): Cloning 9aec7eb323 from cache
Package phpoffice/phpexcel is abandoned, you should avoid using it. Use phpoffice/phpspreadsheet instead.
Writing lock file
Generating optimized autoload files
> Illuminate\Foundation\ComposerScripts::postAutoloadDump
> @php artisan package:discover

$ composer require phpoffice/phpspreadsheet
1/2: https://packagist.laravel-china.org/p/provider-latest$8512051929ea08e8189e440a1f5293135717f61525e93d6f4577794143032dd5.json
2/2: https://packagist.laravel-china.org/p/provider-2018-10$4f971ac341cf575b7ed47aa09773abb926ec0c2baad56a0da2c9eed868beb649.json
Finished: success: 2, skipped: 0, failure: 0, total: 2
Using version dev-master for phpoffice/phpspreadsheet
./composer.json has been updated
Loading composer repositories with package information
Updating dependencies (including require-dev)
Your requirements could not be resolved to an installable set of packages.

Problem 1
- maatwebsite/excel 3.0.9 requires phpoffice/phpspreadsheet ^1.2 -> satisfiable by phpoffice/phpspreadsheet[1.2.0, 1.2.1, 1.3.0, 1.3.1, 1.4.0, 1.4.1, 1.5.0] but these conflict with your requirements or minimum-stability.
- maatwebsite/excel 3.0.9 requires phpoffice/phpspreadsheet ^1.2 -> satisfiable by phpoffice/phpspreadsheet[1.2.0, 1.2.1, 1.3.0, 1.3.1, 1.4.0, 1.4.1, 1.5.0] but these conflict with your requirements or minimum-stability.
- maatwebsite/excel 3.0.9 requires phpoffice/phpspreadsheet ^1.2 -> satisfiable by phpoffice/phpspreadsheet[1.2.0, 1.2.1, 1.3.0, 1.3.1, 1.4.0, 1.4.1, 1.5.0] but these conflict with your requirements or minimum-stability.
- Installation request for maatwebsite/excel (locked at 3.0.9) -> satisfiable by maatwebsite/excel[3.0.9].


Installation failed, reverting ./composer.json to its original content.

suping3@BJ-D-212361A MINGW64 /d/code/blog
$ composer require "phpoffice/phpspreadsheet:^1.2"
1/3: https://packagist.laravel-china.org/p/provider-2018-10$edb575ac09ff566f26e69fb52521dbd7b5bd90cb0e97c11c04a834f1ba157b0b.json
2/3: https://packagist.laravel-china.org/p/provider-latest$f7aba8aec5290950424531a654a2519842e07f0dcd995755aa8e64e7f622ad17.json
3/3: https://packagist.laravel-china.org/p/provider-2018-07$1f1ac5377ce1027162f91a3db54217c36e38f7ae18744629e6e89c5e6d410c78.json
Finished: success: 3, skipped: 0, failure: 0, total: 3
./composer.json has been updated
Loading composer repositories with package information
Updating dependencies (including require-dev)
1/1: https://dl.laravel-china.org/phpoffice/phpspreadsheet/2dfd06c59825914a1a325f2a2ed13634b9d8c411.zip
Finished: success: 1, skipped: 0, failure: 0, total: 1

Fatal error: Class ‘PHPUnit_Framework_TestCase’ not found

1
2
3
4
5
6
7
8
9
10
11
https://github.com/dees040/laravel-api-responses/issues/2
vi vendor/dees040/laravel-api-responses/tests/ResponseTest.php

class ResponseTest extends \PHPUnit\Framework\TestCase

$ phpunit vendor/dees040/laravel-api-responses/tests/ResponseTest.php
PHPUnit 6.5.3 by Sebastian Bergmann and contributors.

.............. 14 / 14 (100%)

Time: 378 ms, Memory: 16.00MB

Laravel 的 collection

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$collection = new \Illuminate\Database\Eloquent\Collection(['item a', 'item b', 'item c']);
$collection = $collection->unique();
执行时候会报错

PHP Fatal error: Call to a member function getKey() on string in /Project/nginx/webbpm-dev.ssl.lol/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Collection.php on line 259

[Symfony\Component\Debug\Exception\FatalErrorException]
Call to a member function getKey() on string
这是因为 Illuminate\Database\Eloquent\Collection 通过调用自身的 getDictionary 方法,使用根据主键去重复的逻辑覆盖了 Illuminate\Support\Collection 的逻辑。

以上代码改成https://laravel-china.org/topics/18696

$collection = collect(['item a', 'item b', 'item c']);
$collection = $collection->unique();

使用”mews/captcha:~2.0” 验证码图片不显示问题

1
2
3
4
5
6
7
8
9
10
11
12
13
Symfony\Component\Debug\Exception\FatalThrowableError: Call to undefined function Intervention\Image\Gd\imagettfbbox() in file /usr/share/nginx/html/LaraBBS/vendor/intervention/image/src/Intervention/Image/Gd/Font.php on line 85

如果已经开启了gd,请参考下 https://laravel-china.org/articles/18815?#reply69185

FROM php:7.0-fpm
RUN apt-get update && apt-get install -y \
libfreetype6-dev \
libjpeg62-turbo-dev \
libmcrypt-dev \
libpng-dev \
&& docker-php-ext-install -j$(nproc) iconv mcrypt \
&& docker-php-ext-configure gd --with-freetype-dir=/usr/include/ --with-jpeg-dir=/usr/include/ \
&& docker-php-ext-install -j$(nproc) gd

在解析外部变量时的一个问题

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
//https://segmentfault.com/a/1190000017051950
<form method="post">
<input type="text" name="id[]" value="1">
<input type="text" name="id[]_text" value="a">
<input type="text" name="id[]" value="2">
<input type="text" name="id[]_text" value="b">
<input type="submit">
</form>
var_export($_POST);
array (
'id' =>
array (
0 => '1',
1 => 'a',
2 => '2',
3 => 'b',
),
)
$foo["id"] = 1;
$foo["id[]_text"] = 2;

var_export($foo);

extract($foo);

var_export(get_defined_vars());

Laravel with () limit

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
比如如何取得每篇文章中的前10条评论
下面为错误写法:https://laravel-china.org/articles/19932

essay::with(['comment' => function($query) {
$query->take(10)
}])
public function products()
{
return $this->belongsToMany(Hproduct::class,'hproduct_has_type','type_id','product_id');
}

public function topTenproducts()
{
return $this->products()->with('supplier')->where('is_sale','=',1)->limit(4);
}

public function getProdcutsWithtopTenproducts($col)
{
$model = $this->whereIn('id',$col)->get();
$model->each(function ($model) {
$model->load('topTenproducts');
});
return $model->toArray();
}

laravel 5.5 索引长度超过限制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
laravel5.4中改变了默认的编码类型(utf8 => utf8mb4), 从而使email字段索引长度超过了的默认限制长度 https://laravel-china.org/articles/18306
InnoDB 引擎中:

uft8 varchar 最大长度 255
utf8mb4 varchar 最大长度 191
设置一下 varchar 的长度不超过 191 即可
table->string('email')->index(); varchar会使用utf8mb4编码, utf8mb4编码(4bytes)属于utf8编码(3bytes)的扩展类型,
由此可以计算索引长度为255*4+2=1022 >767
$ php artisan migrate
Migration table created successfully.

In Connection.php line 664:

SQLSTATE[42000]: Syntax error or access violation: 1071 Specified key was t
oo long; max key length is 1000 bytes (SQL: alter table `users` add unique
`users_email_unique`(`email`))

cron不执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
1:环境变量https://laravel-china.org/articles/18697
当Cron无法生效时,可能是Cron执行环境变量不正确引起的
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/opt/mysql/bin:/opt/php7/bin:/opt/memcached/bin:/root/bin
* * * * * php /path-to-your-project/artisan schedule:run >> /dev/null 2>&1
2:Cron 执行用户导致 Laravel log 不可写
通过 crontab -e 命令创建的 Cron 是属于 root 用户,如果定时任务在实行时主动写入日志或者遇到异常未捕捉,会创建 root 权限的日志文件,最终会导致 php-fpm 的 www 账号无法写入。

因此需要在创建 cron 的时候指定用户

crontab -u www -e
3:cron 内容最后一行未回车

解决上述两点问题后,如果仍然发现 cron 不执行,请确认

* * * * * php /path-to-your-project/artisan schedule:run >> /dev/null 2>&1

composer update后输出Killed

1
2
3
4
5
6
//https://laravel-china.org/articles/20443
解决方法:
在本地环境运行composer update
上传新的composer.lock文件到服务器
服务器上运行composer install
这样就解决的依赖包更新的问题。

Memcached::get(): could not unserialize value, no igbinary support

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
./configure --enable-memcached --enable-memcached-igbinary --with-php-config=
/usr/local/bin/php-config
但是很不幸,并没有如期待的那样编译成功,遇到如下错误:http://www.helpergarden.com/2017/11/php-memcached%E6%89%A9%E5%B1%95%E5%AE%89%E8%A3%85.html

"./php_memcached.h:23:10: fatal error: 'Zend/zend_smart_str.h' file not found
#include "Zend/zend_smart_str.h""
```
### cURL version 7.10.5 or later is required to compile php with cURL support
```javascript
#./configure --with-php-config=/usr/local/php/bin/php-config
configure: WARNING: You will need re2c 0.13.4 or later if you want to regenerate PHP parsers.
checking for gawk... gawk
checking for cURL support... yes, shared
checking for pkg-config... /usr/bin/pkg-config
checking for libcurl.pc... using default path
checking for cURL 7.10.5 or greater... configure: error: cURL version 7.10.5 or later is required to compile php with cURL support
curl: try 'curl --help' or 'curl --manual' for more information
[root@VM_0_14_centos curl]# yum install curl-devel
Loaded plugins: fastestmirror, langpacks
Repository epel is listed more than once in the configuration
carlwgeorge-ripgrep | 3.0 kB 00:00:00

docker-ce-stable
#./configure --with-php-config=/usr/local/php/bin/php-config
#make && make install

https://www.oschina.net/question/81181_19353?sort=default&p=2

nginx 配置问题-FastCGI sent in stderr: “Primary script unknown”

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
一个关于 nginx 配置的问题,配置一个 php 网站,页面总是输出 File not found.,查看 nginx log 中报错:
tail -f /var/log/nginx/error.log
FastCGI sent in stderr: "Primary script unknown" while reading response header from upstream
[root@VM_0_14_centos ~]# cat /etc/nginx/conf.d/xss.conf
server {
listen 9696 default_server;
server_name _;
root /root/xssplatform/;
#root /usr/share/nginx/html/xssplatform/;

rewrite "^/([0-9a-zA-Z]{6})$" /index.php?do=code&urlKey=$1 last;
rewrite "^/do/auth/(\w+?)(/domain/([\w\.]+?))?$" /index.php?do=do&auth=$1&domain=$3 last;
rewrite "^/register/(.*?)$" /index.php?do=register&key=$1 last;
rewrite "^/register-validate/(.*?)$" /index.php?do=register&act=validate&key=$1 last;

location / {

index index.html index.htm index.php;
}

location ~ \.php$ {
fastcgi_pass 127.0.0.1:9005;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}

}
fpm 用nobody用户运行 /root/xssplatform/ 权限也是nobody
[root@VM_0_14_centos ~]# lsof -i:9005
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
php-fpm 13691 root 7u IPv4 30712498 0t0 TCP VM_0_14_centos:9005 (LISTEN)
php-fpm 13692 nobody 0u IPv4 30712498 0t0 TCP VM_0_14_centos:9005 (LISTEN)
php-fpm 13693 nobody 0u IPv4 30712498 0t0 TCP VM_0_14_centos:9005 (LISTEN)
https://www.v2ex.com/t/341441
1.给我看下你 php-fpm 的配置
2.请确定 /var/run/php/php5.6-fpm.sock 这个在 php-fpm 配置正确
3.注意 php 和 nginx 执行权限
整个root目录树的权限都打开才可以
mv /root/xssplatform/ /usr/share/nginx/html/ 就好了

intervention/iamge下载图片过慢

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 记录开始时间
$startTimestamp = microtime(true);

$url = 'http://wx.qlogo.cn/mmopen/XxT9TiaJ1ibf06TNRCMjQADS4opDHvQLguLZHpqkRlvuJYZicvJW4iaOalPsKIs0kpZ3F6864ZzibyObYiaucUQSrdp4pFTNDyIpxw/0';

$avatar = \Image::make($url);

// 记录结束时间https://segmentfault.com/a/1190000011989802
$endTimestamp = microtime(true);
$startTimestamp = microtime(true);

$client = new \GuzzleHttp\Client();

$url = 'http://wx.qlogo.cn/mmopen/XxT9TiaJ1ibf06TNRCMjQADS4opDHvQLguLZHpqkRlvuJYZicvJW4iaOalPsKIs0kpZ3F6864ZzibyObYiaucUQSrdp4pFTNDyIpxw/0';

$avatarResponse = $client->get($url);

$avatar = \Image::make($avatarResponse->getBody()->getContents());

$endTimestamp = microtime(true);
三次平均耗时在 0.07 秒左右,和前面的 16 秒相比,差了 200 多倍

MySQL由一个双引号引发的血案

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
//https://mp.weixin.qq.com/s/TJsBPK-RbmvIS0PNJ8GqtQ
mysql [localhost] {msandbox} (test) > select id,str_col from tbl_name where str_col="xxx" = "yyy";
+----+---------+
| id | str_col |
+----+---------+
| 1 | aaa |
| 2 | aaa |
| 3 | aaa |
| 4 | aaa |
+----+---------+
mysql [localhost] {msandbox} (test) > warnings
Show warnings enabled.
mysql [localhost] {msandbox} (test) > explain extended select id,str_col from tbl_name where str_col="xxx" = "yyy"\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: tbl_name
type: index
possible_keys: NULL
key: idx_str
key_len: 33
ref: NULL
rows: 4
filtered: 100.00
Extra: Using where; Using index
1 row in set, 1 warning (0.00 sec)

Note (Code 1003): /* select#1 */ select `test`.`tbl_name`.`id` AS `id`,`test`.`tbl_name`.`str_col` AS `str_col` from `test`.`tbl_name` where ((`test`.`tbl_name`.`str_col` = 'xxx') = 'yyy')
他把where条件转化成了 http://www.fordba.com/mysql-double-quotation-marks-accident.html

((`test`.`tbl_name`.`str_col` = 'xxx') = 'yyy')

foreach

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
$arr = [1, 2, 3, 4];
foreach($arr as &$value) {
$value = $value * 2;
}

// $arr is now [2, 4, 6, 8]
unset($value); // 最后取消掉引用

$value的引用仅在被遍历的数组可以被引用时才可用
/*
此段代码可以运行
运行结果:
1-2
2-4
3-6
4-8
*/
foreach (array(1, 2, 3, 4) as &$value) {
echo $value, '-';
$value = $value * 2;
echo $value, PHP_EOL;
}
foreach按照值进行循环的时候(by-value), foreach是对该数组的一个拷贝进行操作. 所以在循环过程中修改不影响循环结果
$arr = [0, 1, 2];
$ref = &$arr;

foreach ($arr as $val) {
var_dump($val);
unset($arr[1]);
}
$arr = [1, 2, 3];

foreach ($arr as $k => &$v) {
$v = $v * 2;
}

foreach ($arr as $k => $v) {
echo $v, PHP_EOL;
}

/*
输出:
2
4
4
*/
$arr = [1, 2, 3];

foreach ($arr as $k => &$v) {
$v = $v * 2;
}
unset($v);//数组最后一个元素的 $value 引用在 foreach 循环之后仍会保留。建议使用 unset() 来将其销毁。
foreach ($arr as $k => $v) {
echo $v, PHP_EOL;
}

/*
输出:
2
4
6
*/

proc_open()打开两个进程

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
//https://blog.jiaojie.site/_posts/2017/08/15/proc_open-and-bash/
$descriptorspec = array(
0 => array("pipe", "r"), // stdin is a pipe that the child will read from
1 => array("pipe", "w"), // stdout is a pipe that the child will write to
2 => array("file", "/tmp/error-output.txt", "a") // stderr is a file to write to
);

$cwd = '/tmp';
$env = array('some_option' => 'aeiou');

$process = proc_open('sleep 100', $descriptorspec, $pipes, $cwd, $env);

if (is_resource($process)) {
// $pipes now looks like this:
// 0 => writeable handle connected to child stdin
// 1 => readable handle connected to child stdout
// Any error output will be appended to /tmp/error-output.txt

fwrite($pipes[0], '<?php print_r($_ENV); ?>');
fclose($pipes[0]);

echo stream_get_contents($pipes[1]);
fclose($pipes[1]);

// It is important that you close any pipes before calling
// proc_close in order to avoid a deadlock
$return_value = proc_close($process);

echo "command returned $return_value\n";
}
执行php sleep.php父进程下面竟然出现了两个进程
PHP的proc_open系列函数在Linux操作系统中应该是通过系统默认的sh -c命令进行执行的,要把不同的sh带来的结果考虑进去
将sh的软链接改到bash上面,PHP代码按照预期的结果执行下去了
[root@VM_0_14_centos incubator-superset]# ll /bin/sh
lrwxrwxrwx 1 root root 4 Mar 19 2018 /bin/sh -> bash

MySQL大数据分页

1
2
3
4
5
6
7
8
9
10
11
12
https://blog.jiaojie.site/_posts/2017/12/22/mysql-pagination-tips/
SELECT * FROM test where status = 1 limit start, 1000
select * from test a ,
(select id from test where status = 1 limit 300000, 10) b
where a.id = b.id;
select * from test
inner join (select id from test limit 300000, 10) t2
using (id);
select * from test
where status = 1 and id > xxx
order by id asc
limit 10

每三位加个逗号分割

1
2
3
4
5
6
7
8
9
10
11
12
13

$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);

var_dump($output);//"12,345,678"

use & Referance

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
use中传递php基本数据类型时,绝大多数情况都是传递的copy。

这个大多数情况包括boolean/integer/float/string和array这样的类型。

针对object的类型,传递的是引用,不需要加&符号。
$fib = function($n)use(&$fib)
{
if($n == 0 || $n == 1) return 1;
return $fib($n - 1) + $fib($n - 2);
};

echo $fib(10);//89
$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"

判断 Eloqument 模型查询数据结果是否为空

1
2
3
4
5
6
7
8
9
10
11
return DB::table('orders')->where('finalized', 1)->exists();

return DB::table('orders')->where('finalized', 1)->doesntExist();
return Users::where('email','xxx@qq.com')->exists();

return Users::where('email','xxx@qq.com')->doesntExist();
$users = User::where('active', 1)->all();

return $users->isEmpty();

return $users->isNotEmpty();

requires ext-pcntl

1
2
3
4
5
6
7
8
9
10
11
12
laravel/horizon v1.4.3 requires ext-pcntl * -> the requested PHP extension pcntl is missing from your system
在composer.json中增加

"config": {
"preferred-install": "dist",
"sort-packages": true,
"optimize-autoloader": true,
"platform": {
"ext-pcntl": "7.2",
"ext-posix": "7.2"
}
},

exec函数被禁

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
为了保证数据的有序性,需要对同时消费的进程进行数量限制(同时消费队列的进程不得超过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 script.php `cat /dev/urandom | head -n 10 | md5sum | head -c 10`
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);
}

curl 用完千万别忘 close

1
2
3
4
5
6
7
8
9
10
11
12
//https://laravel-china.org/articles/9172/curl-do-not-forget-close
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HEADER, false);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
try{
$content = curl_exec($ch);
} finally{
curl_close($ch);
}

Memcached::get(): could not unserialize value, no igbinary support

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
https://stackoverflow.com/questions/35200566/warning-memcachedgetmulti-could-not-unserialize-value-no-igbinary-support
[root@localhost igbinary-2.0.8]# php7 -i|grep igbinary
igbinary support => no
PWD => /data1/projects/suping/igbinary-2.0.8


php -i | grep igbinary
igbinary
igbinary support => enabled
igbinary version => 2.0.1
igbinary APC serializer ABI => 0
igbinary session support => yes
igbinary.compact_strings => On => On
igbinary support => yes
memcached.serializer => igbinary => igbinary
Available serializers => php, igbinary
Registered serializer handlers => php php_binary igbinary

#wget http://pecl.php.net/get/igbinary-2.0.8.tgz
--2018-12-29 19:58:07-- http://pecl.php.net/get/igbinary-2.0.8.tgz
Resolving pecl.php.net... 104.236.228.160
Connecting to pecl.php.net|104.236.228.160|:80... connected.
HTTP request sent, awaiting response... 302 Found
Location: http://120.52.51.17/pecl.php.net/get/igbinary-2.0.8.tgz [following]
--2018-12-29 19:58:08-- http://120.52.51.17/pecl.php.net/get/igbinary-2.0.8.tgz
Connecting to 120.52.51.17:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 76708 (75K) [application/octet-stream]
Saving to: “igbinary-2.0.8.tgz”

100%[================================================================================>] 76,708 1.75K/s in 55s

2018-12-29 19:59:12 (1.35 KB/s) - “igbinary-2.0.8.tgz” saved [76708/76708]
#tar -zxvf igbinary-2.0.8.tgz
# cd igbinary-2.0.8
# /data0/opt/php7/bin/phpize
Configuring for:
PHP Api Version: 20151012
Zend Module Api No: 20151012
Zend Extension Api No: 320151012
# ./configure --with-php-config=/data/php7/bin/php-config
#make && make install
Build complete.
Don't forget to run 'make test'.

Installing shared extensions: /data0/opt/php7/lib/php/extensions/no-debug-zts-20151012/
Installing header files: /data0/opt/php7/include/php/

PHP 读取 ZIP乱码

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
//https://laravel-china.org/topics/21866
/**
* 获取ZIP压缩文件内容列表
* @param [string] $file ZIP文件路径
* @param [string] $dir 需要列出的文件夹
* @param [bool] $subdir 是否列出子文件夹内容
* @param [callable] $callback 列出每一项时的回调函数
* @return [Array]
*/
function zip_list_content($file, $dir=null, $subdir=true, $callback=null) {
$zip = new ZipArchive();
if (!$zip->open($file)) {
return false;
}
$count = $zip->numFiles;
if ($dir) $dir = trim($dir, '/');
if (is_callable($callback) $_callback = true; else $_callback = false;
$results = [];
for ($i = 0; $i < $count; $i++) {
$entry = $zip->statIndex($i, ZipArchive::FL_ENC_RAW);
$entry['name'] = rtrim(str_replace('\\', '/', $entry['name']), '/');
$encoding = mb_detect_encoding($entry['name'], array('Shift_JIS','EUC_JP','EUC_KR','KOI8-R','ASCII','GB2312','GBK','BIG5','UTF-8'));
$filename = iconv($encoding, 'UTF-8', $entry['name']);
$filename = $filename ?: $entry['name'];
$size = $entry['size'];
$comp_size = $entry['comp_size'];
$mtime = $entry['mtime'];
$crc = $entry['crc'];
$is_dir = ($crc == 0);
$path = '/' . $filename;
if ($dir and (stripos($filename, $dir) !== 0 or strtolower($filename) == strtolower($dir))) continue;
$_names = explode('/', $filename);
$_idx = count($_names)-1;
if (!$subdir and $_idx != count(explode('/', $dir))) continue;
$name = $_names[$_idx];
$index = $i;
//$data = $zip->getFromIndex($i);
$entry = compact('name', 'path', 'size', 'comp_size', 'mtime', 'crc', 'index', 'is_dir');
$results[] = $entry;
if ($_callback) @call_user_func($callback, $entry, $index, $zip);
}
$zip->close();
return $results;
}

Memcached::get()could not unserialize value, no igbinary support

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
使用laravel Cache::get()时报错,版本PHP7 ,报错文件 
vendor/laravel/framework/src/Illuminate/Cache/MemcachedStore.php
重新安装memcached,3.x版本是支持php7的,而php5.2-5.6需要使用2.x版本
$ wget https://codeload.github.com/php-memcached-dev/php-memcached/tar.gz/v3.1.3
$ tar -zxvf php-memcached-3.1.3.tar.gz
$ cd php-memcached-3.1.3
$/php7/bin/phpize
Configuring for:
PHP Api Version: 20151012
Zend Module Api No: 20151012
Zend Extension Api No: 320151012

$ ./configure --enable-memcached --enable-memcached-igbinary --with-php-config=/php7/bin/php-config
$ make && make install
Build complete.
Don't forget to run 'make test'.

Installing shared extensions: /php7/lib/php/extensions/no-debug-zts-20151012/

$ php7 --ini
Configuration File (php.ini) Path: /php7/etc/
Loaded Configuration File: /php7/etc/php.ini
Scan for additional .ini files in: (none)
Additional .ini files parsed: (none)
$ vi /php7/etc/php.ini
extension=memcache.so
extension=/php7/lib/php/extensions/no-debug-zts-20151012/memcached.so

安装igbinary
#wget http://pecl.php.net/get/igbinary-2.0.8.tgz
#tar -zxvf igbinary-2.0.8.tgz
# cd igbinary-2.0.8
# /php7/bin/phpize
Configuring for:
PHP Api Version: 20151012
Zend Module Api No: 20151012
Zend Extension Api No: 320151012
# ./configure --with-php-config=/php7/bin/php-config
#make && make install
Build complete.
Don't forget to run 'make test'.

Installing shared extensions: /php7/lib/php/extensions/no-debug-zts-20151012/
Installing header files: /php7/include/php/


参考http://www.runoob.com/memcached/php-connect-memcached.html

深入理解PHP之foreach

laravel 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
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
现象 https://suren1986.in/index.php/archives/44/
有时跑一些很耗费时间的异步脚本,会在 artisan queue:listen 时通过 --timeout=3600 设置长一点的时间,以避免超时。
如果: 1. 使用 Redis 存放 queue 信息; 2. 跑了多个进程 artisan queue:listen --timeout=36003. 监听同一个 queue;4. Job 的时间超过 60 秒。就会发生同一个脚本被运行多次的情况。

"LPOP"
"queues:default"

#取出任务后,先要放到 queues:default:reserved zset 中

"ZADD"
"queues:default:reserved"
"1485386842"
"{\"job\":\"Illuminate\\\\Queue\\\\CallQueuedHandler@call\",\"data\":{\"commandName\":\"App\\\\Jobs\\\\ExampleJob\",\"command\":\"O:19:\\\"App\\\\Jobs\\\\ExampleJob\\\":7:{s:17:\\\"\\u0000*\\u0000userIdentifier\\\";N;s:9:\\\"\\u0000*\\u0000realIp\\\";N;s:12:\\\"\\u0000*\\u0000requestId\\\";N;s:6:\\\"\\u0000*\\u0000job\\\";N;s:10:\\\"connection\\\";N;s:5:\\\"queue\\\";N;s:5:\\\"delay\\\";N;}\"},\"id\":\"bwA7ICPqnjYiM0ErjRBNwn0kVWF6KeAs\",\"attempts\":1}"

# 任务执行完毕后, 从 queues:default:reserved zset 中删除

"ZREM"
"queues:default:reserved"
"{\"job\":\"Illuminate\\\\Queue\\\\CallQueuedHandler@call\",\"data\":{\"commandName\":\"App\\\\Jobs\\\\ExampleJob\",\"command\":\"O:19:\\\"App\\\\Jobs\\\\ExampleJob\\\":7:{s:17:\\\"\\u0000*\\u0000userIdentifier\\\";N;s:9:\\\"\\u0000*\\u0000realIp\\\";N;s:12:\\\"\\u0000*\\u0000requestId\\\";N;s:6:\\\"\\u0000*\\u0000job\\\";N;s:10:\\\"connection\\\";N;s:5:\\\"queue\\\";N;s:5:\\\"delay\\\";N;}\"},\"id\":\"bwA7ICPqnjYiM0ErjRBNwn0kVWF6KeAs\",\"attempts\":1}"

# 如果任务失败,会放到 queue:default:delay zset 中

"ZADD"
"queues:default:delayed"
"1485386783"
"{\"job\":\"Illuminate\\\\Queue\\\\CallQueuedHandler@call\",\"data\":{\"commandName\":\"App\\\\Jobs\\\\ExampleJob\",\"command\":\"O:19:\\\"App\\\\Jobs\\\\ExampleJob\\\":7:{s:17:\\\"\\u0000*\\u0000userIdentifier\\\";N;s:9:\\\"\\u0000*\\u0000realIp\\\";N;s:12:\\\"\\u0000*\\u0000requestId\\\";N;s:6:\\\"\\u0000*\\u0000job\\\";N;s:10:\\\"connection\\\";N;s:5:\\\"queue\\\";N;s:5:\\\"delay\\\";N;}\"},\"id\":\"uuPBCq4QE9ocnw8UbkLhUl2Lh07yPm6M\",\"attempts\":1}"
/**
* Pop the next job off of the queue.
*laravel 5.0
* @param string $queue
* @return \Illuminate\Contracts\Queue\Job|null
*/
public function pop($queue = null)
{
$original = $queue ?: $this->default;

$queue = $this->getQueue($queue);

if ( ! is_null($this->expire))
{
$this->migrateAllExpiredJobs($queue);
}

$job = $this->getConnection()->lpop($queue);

if ( ! is_null($job))
{
$this->getConnection()->zadd($queue.':reserved', $this->getTime() + $this->expire, $job);

return new RedisJob($this->container, $this, $job, $original);
}
}

/**
* Pop the next job off of the queue.
*laravel 5.5
* @param string $queue
* @return \Illuminate\Contracts\Queue\Job|null
*/
public function pop($queue = null)
{
$this->migrate($prefixed = $this->getQueue($queue));

list($job, $reserved) = $this->retrieveNextJob($prefixed);

if ($reserved) {
return new RedisJob(
$this->container, $this, $job,
$reserved, $this->connectionName, $queue ?: $this->default
);
}
}

原因
在 Laravel 的框架中,通过 Redis 获取下一个 Job 是通过 Illuminate\RedisQueue 完成的。
获取下个 Job 的函数是 public function pop()。
假设 queue 的名称为 defaultLaravel 在获取到脚本后,会将它放入 default:reserved 中。 default:reserved 的结构是 sorted hash,被放入的 Job 会被赋予一个权值:time() + this->expire,默认的 this->expire = 60,Laravel 官方的配置中(config/queue.php),也默认了 60 秒。
这个函数在从 Redis queue 中 pop 一个数据前,会将所有过期的 Job 放回到原来的 queue 中。
过期 Job 的来源包括 default:reserved,获取所有 default:reserved 中,权值小于当前时间的 Job。
于是,超过 60 秒没有执行完成的 Job 被放入了 default queue 中,等待下一次执行。
解决方案
解决方法很简单,将 config/queue.php,相应配置的 expire 设置为 null 即可。
因为 Illuminate\RedisQueue 中,如果 expire 为 null,是不会做将将所有过期的 Job 放回到原来的 queue 中的操作的。

安装上redisadmin后查看,发现添加的任务一直保留在队列中,没有被取走(删除)。并且这条任务的状态变为了reserved(保留的)。

然后在网上发现了这篇文章(http://yansu.org/2014/04/11/redis-queue-in-laravel.html),它讲了laravel使用redis作为队列系统的执行流程。
其中讲了什么情况下任务的状态会变为reserved

RedisQueue:pop()方法中,有这样一句:

$this->redis->zadd($queue.':reserved', $this->getTime() + 60, $job);
这里将当前执行的任务放到另外一个reserved队列中,超时时间是60s。也就是说,如果60s后这个任务没有被删除掉,则任务会重新被放入队列中来。因此,在实际的使用过程中,任务很可能被多次执行。解决的办法是

class SendEmail {

public function fire($job, $data)
{
$job->delete();
// job
}

}

retry_after配置选项和--timeout命令行选项是不一样的,但是可以同时工作来保证任务不会丢失并且不会重复执行。

--timeout应该永远都要比retry_after短至少几秒钟的时间。这样就能保证任务进程总能在失败重试前就被杀死了。如果你的--timeout选项大于retry_after配置选项,你的任务可能被执行两次。
https://phpartisan.cn/news/97.html

对队列的重试次数做限制,超出限制后删除队列或延迟处理。
通过对队列错误事件的监听我才知道,原来是由于在我的事件中使用了Model模型,再进入队列执行的时候,
会使用Model的主键再去反查一次模型数据,而且使用的是firstOrFail方法,可能是由于之前的一些数据被物理删除
了导致找不到的原因,这这里就报错了,而且没有删除的机制,所以队列一直在重试!
使用Laravel队列时一定要记得监听
Illuminate\Queue\Events\JobExceptionOccurred;
Illuminate\Queue\Events\JobFailed;
两个事件不然发生错误队列就删除不了了,如果是在执行队列中handle函数中
一定要记得及时删除队列,使用delete()方法即可!

如果在执行handle的过程中出现抛出异常,那么本任务就算执行失败,就会将失败的任务存放到fail_jobs表当中。

当然也可以手动释放任务,如下尝试执行超过2次,就释放本次任务,不在存入fail_jobs表中。

if($this->attempts() > 2){
$this->release();
}
被放入fail_jobs表中的任务是不会再执行了,重启之后和不会执行。
此外我们可以在服务中注册Queue::failing来侦听失败时间,异步通知给开发人员
https://www.omybug.com/2017/05/05/Laravel-%E9%98%9F%E5%88%97/
class AppServiceProvider extends ServiceProvider{
public function boot()
{
Queue::failing(function ($connection, $job, $data) {
// Notify team of failing job...
});
}
........
}
在队列任务类上直接定义failed方法
/**
* 处理失败任务
*
* @return void
*/
public function failed()
{
// Called when the job is failing...
}
要查看已插入到failed_jobs数据表中的所有失败任务,可以使用Artisan命令queue:failed:

php artisan queue:failed
+------+------------+---------+------------------------------------------+---------------------+
| ID | Connection | Queue | Class | Failed At |
+------+------------+---------+------------------------------------------+---------------------+
| 8766 | redis | default | Illuminate\Events\CallQueuedHandler@call | 2019-01-09 13:40:42 |
| 8765 | redis | default | Illuminate\Events\CallQueuedHandler@call | 2019-01-09 13:37:43 |
| 8764 | redis | default | Illuminate\Events\CallQueuedHandler@call | 2019-01-09 13:36:49
该命令将会列出任务ID,连接,对列和失败时间,任务ID可用于重试失败任务,例如,要重试一个ID为5的失败任务,要用到下面的命令:

php artisan queue:retry 5
要重试所有失败任务,使用如下命令即可:

php artisan queue:retry all
如果你要删除一个失败任务,可以使用queue:forget命令:

php artisan queue:forget 5
要删除所有失败任务,可以使用queue:flush命令:

php artisan queue:flush

MySQL导入SQL出现MySQL server has gone away错误

1
2
3
4
5
6
7
8
9
10
11
12
13
MySQL导入SQL出现MySQL server has gone away错误

项目中需要导入一个200M的SQL文件,导入过程中会出现MySQL server has gone away错误

查看一下允许导入的文件大小,默认是1M

show global variables like 'max_allowed_packet';
调整my.cnf中的值
max_allowed_packet = 512M

零时调整可以用,重启之后会失效

set global max_allowed_packet=536870912;

pdo查询出的数据均自动转换为字符串

1
2
3
4
5
6
7
8
9
PDO::ATTR_STRINGIFY_FETCHES 提取的时候将数值转换为字符串。 
PDO::ATTR_EMULATE_PREPARES 启用或禁用预处理语句的模拟。
在实例化medoo的时候在option参数设置中加入这两个选项,即可查询出原有数据类型:
https://blog.cooooler.com/2018/12/15/%E4%BD%BF%E7%94%A8medoo%E6%9F%A5%E8%AF%A2%E5%87%BA%E7%9A%84%E6%95%B0%E6%8D%AE%E5%9D%87%E8%87%AA%E5%8A%A8%E8%BD%AC%E6%8D%A2%E4%B8%BA%E5%AD%97%E7%AC%A6%E4%B8%B2/
'option' => [
PDO::ATTR_CASE => PDO::CASE_NATURAL,
PDO::ATTR_STRINGIFY_FETCHES => false,
PDO::ATTR_EMULATE_PREPARES => false
],

git clone克隆速度过慢问题

1
2
3
//https://blog.cooooler.com/2018/07/18/%E8%A7%A3%E5%86%B3git-clone%E5%85%8B%E9%9A%86%E9%80%9F%E5%BA%A6%E8%BF%87%E6%85%A2%E9%97%AE%E9%A2%98/
git config --global http.proxy socks5://127.0.0.1:1080
git config --global https.proxy socks5://127.0.0.1:1080

laravel中导出excel乱码

1
2
3
4
5
6
7
8
9
10
//https://blog.cooooler.com/2018/05/11/PHP%E5%AF%BC%E5%87%BAexcel%E4%B9%B1%E7%A0%81%E7%9A%84%E7%BB%88%E6%9E%81%E8%A7%A3%E5%86%B3%E5%A4%A7%E6%B3%95/
Excel::create($title, function ($excel) use ($data, $type, $rows) {
$excel->sheet('导出excel', function ($sheet) use ($data, $type, $rows) {
$sheet->rows($data);
});

// 设置bom防止中文乱码 只需要在生成文件时输出BOM头即可。
echo chr(0xEF) . chr(0xBB) . chr(0xBF);

})->export($type);

laravel中进行PDF导出遇到的问题

1
2
3
4
5
6
7
8
9
10
11
12
https://blog.cooooler.com/2018/08/29/%E6%80%BB%E7%BB%93%E4%B8%80%E4%B8%8Blaravel%E4%B8%AD%E8%BF%9B%E8%A1%8CPDF%E5%AF%BC%E5%87%BA%E9%81%87%E5%88%B0%E7%9A%84%E9%97%AE%E9%A2%98/
composer require barryvdh/laravel-snappy
` php artisan vendor:publish --provider="Barryvdh\Snappy\ServiceProvider" `
导出pdf中文乱码问题

首先需要下载一个支持中文的字体包,比如微软雅黑
然后在/usr/share/fonts目录中新建一个文件夹,例如文件夹名为chinese
将下载好的字体文件移动到新建的文件夹中
然后执行以下命令安装字体
mkfontscale
mkfontdir
fc-cache

curl获取数据乱码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
composer require phlib/guzzle-middleware
try {

// 生成handler
$handler = \GuzzleHttp\HandlerStack::create();
$handler->push(new \Phlib\Guzzle\ConvertCharset());
$handler->push(new \Phlib\Guzzle\AbsoluteUrls());

// 设置请求参数
$option = [
'timeout' => 10,
'handler' => $handler
];

// 发起请求
$client = new \GuzzleHttp\Client($option);
$response = $client->get('需要请求的url');

} catch (\Exception $exception) {
return $exception->getMessage();
}

return (string)$response->getBody();

mysql emoji

1
2
3
4
5
6
7
8
utf8_unicode_ci 只能存储 3 个字节的字符,无法应对 Emoji 这种需要 4 个字节的字符。https://suren1986.in/index.php/archives/45/
弄清楚问题,解决起来就简单了,修改字符集为 utf8mb4。不过修改的时候又碰到 1 个问题:utf8mb4_bin, utf8mb4_unicode_ci, utf8mb4_general_ci 应该选哪个?

参考 MySQL 的 官方文档, ci 后缀表示不区分大小写(case insensitive),bin 因为通过二进制比较,所以区分大小写:参考 stackoverflow 的这篇 回答 unicode 在对很多语言来说排序和比较是准确的,但是相对慢一些,general 没有实现所有的 Unicode 排序规则,有些语言会有问题,但是快一些,不过到具体实践中,中文没什么差别,速度相差无几,我觉得用 unicode 更好一些,少留坑,

还好我的表数据量不大,线上很快就转换完了,几百万数据就得半夜三更弄了。

改完数据表还没结束,代码中连接数据库时指定的字符集也得改为 utf8mb4。

composer install、update出错需要Token

1
2
3
4
5
报错信息:使用SSH Key做身份认证克隆失败,请输入你的Github凭证来访问私人的镜像。(翻译得不好,大概是这个意思)

可是还是不懂这里需要填写的是什么,还是几番Google找到了答案。

原来这里要输入的本地SSH生成的公钥,复制~/.ssh/id_rsa.pub内容粘贴到这里,回车就可以了

MISCONF Redis is configured to save RDB snapshots

1
2
3
4
5
6
7
8
9
10
https://laravel-china.org/articles/22025#fa405f
有两种修改方法,一种是通过redis命令行修改,另一种是直接修改redis.conf配置文件
命令行修改方式示例:
127.0.0.1:6379> config set stop-writes-on-bgsave-error no
修改redis.conf文件:
vi打开redis-server配置的redis.conf文件,然后使用快捷匹配模式:/stop-writes-on-bgsave-error定位到stop-writes-on-bgsave-error字符串所在位置,接着把后面的yes设置为no即可。

我用的命令行修改方式,
docker exec -it 0d redis-cli 0d是我的redis容器
127.0.0.1:6379> config set stop-writes-on-bgsave-error no

laravel的坑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
https://www.jianshu.com/p/da9c0cae59ef
1.多数据库使用事务的坑(model指定了$connection,用的是/config/database.php里的连接)
使用DB::beginTransaction()是没有效果的,相当于未使用事务,必须指定连接,事务才能生效
DB::connection('连接名')->beginTransaction();
DB::connection('连接名')->rollback();
DB::connection('连接名')->commit();
2.主从分离中事务的坑
在事务开始直到结束这一过程中,所有的查询更新删除操作都作用在主库,所以不必担心在事务中的查询会查询到从库,也没必要在事务中为了查询主库而使用onWriteConnection;
未使用事务的地方,想要读主库的数据,可以使用onWriteConnection: Table::onWriteConnection()->find($id);
3.increment与decrement的坑
$rs1 = Order::find(41)->decrement('shipping_fee', 0);
echo '$rs1 = '.$rs1.' ';
$rs2 = Order::find(41)->decrement('order_amount', 0);
echo '$rs2 = '.$rs2.' ';
运行结果:$rs1 = 1 $rs2 = 0
原因分析:查看这两行代码执行的真实sql语句:
update orders set shipping_fee = shipping_fee - 0, updated_at = '2017-08-03 10:10:25' where id = '41'
update orders set order_amount = order_amount - 0, updated_at = '2017-08-03 10:10:25' where id = '41'
结论:因为使用了laravel时间自动更新的功能,而且更新的是同一条数据,updated_at为最新时间,第一条语句执行返回条数1,而第二条的updated_at跟第一条一样,返回的更新条数就为0了。

crontab

1
2
新创建的cron job,不会马上执行,至少要过2分钟才执行。如果重启cron则马上执行。
当crontab失效时,可以尝试/etc/init.d/crond restart解决问题,或者查看日志看某个job有没有执行/报错tail -f /var/log/cron

laravel 读操连接了读库和写库

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
/**vendor\laravel\framework\src\Illuminate\Database\Connectors\ConnectionFactory.php
* Establish a PDO connection based on the configuration.
*https://laravel-china.org/topics/1879/laravel-5-configuration-read-and-write-separation-and-source-analysis
* @param array $config
* @param string $name
* @return \Illuminate\Database\Connection
*/
public function make(array $config, $name = null)
{
$config = $this->parseConfig($config, $name);
// 如果配置了读写分离,则同时创建读库和写库的链接
if (isset($config['read'])) {
return $this->createReadWriteConnection($config);
}
// 如果没有配置,默认创建单个数据库链接
return $this->createSingleConnection($config);
}
protected function createSingleConnection(array $config)
{
$pdo = $this->createConnector($config)->connect($config);dump($config);

return $this->createConnection($config['driver'], $pdo, $config['database'], $config['prefix'], $config);
}
array:11 [▼
"driver" => "mysql"
"port" => "3306"
"host" => "127.0.0.1"
"database" => "mysql"
"username" => "root"
"password" => "root"
"charset" => "utf8"
"collation" => "utf8_unicode_ci"
"prefix" => ""
"strict" => false
"name" => "read"
]
cat config/database.php
'connections' => [

'mysql' => [
'driver' => 'mysql',
'host' => env('DB_HOST', 'localhost'),
'port' => env('DB_PORT', 3306),
'database' => env('DB_DATABASE', 'forge'),
'username' => env('DB_USERNAME', 'forge'),
'password' => env('DB_PASSWORD', ''),
'charset' => 'utf8mb4',
'collation' => 'utf8mb4_unicode_ci',
'prefix' => env('DB_PREFIX', 'tbl_'),
'timezone' => env('DB_TIMEZONE', '+00:00'),
'strict' => false,
'options' => array(
PDO::ATTR_STRINGIFY_FETCHES => true,
PDO::ATTR_EMULATE_PREPARES => false
),
],

'mysql-read' => [
'driver' => 'mysql',
'host' => env('DB_HOST_READ', 'localhost'),
'port' => env('DB_PORT', 3306),
'database' => env('DB_DATABASE', 'forge'),
'username' => env('DB_USERNAME', 'forge'),
'password' => env('DB_PASSWORD', ''),
'charset' => 'utf8mb4',
'collation' => 'utf8mb4_unicode_ci',
'prefix' => env('DB_PREFIX', 'tbl_'),
'timezone' => env('DB_TIMEZONE', '+00:00'),
'strict' => false,
'options' => array(
PDO::ATTR_STRINGIFY_FETCHES => true,
PDO::ATTR_EMULATE_PREPARES => false
),
],

'mysql-write' => [
'driver' => 'mysql',
'host' => env('DB_HOST_WRITE', 'localhost'),
'port' => env('DB_PORT', 3306),
'database' => env('DB_DATABASE', 'forge'),
'username' => env('DB_USERNAME', 'forge'),
'password' => env('DB_PASSWORD', ''),
'charset' => 'utf8mb4',
'collation' => 'utf8mb4_unicode_ci',
'prefix' => env('DB_PREFIX', 'tbl_'),
'timezone' => env('DB_TIMEZONE', '+00:00'),
'strict' => false,
'options' => array(
PDO::ATTR_STRINGIFY_FETCHES => true,
PDO::ATTR_EMULATE_PREPARES => false
),
],
],
class DBConnService
{
private static $read_conn = null;
private static $write_conn = null;

private function __construct() {}

public static function writeConn() {
if (self::$write_conn === null) {
self::$write_conn = DB::connection('mysql-write');
}

return self::$write_conn;
}

public static function readConn() {
if (self::$read_conn === null) {
self::$read_conn = DB::connection('mysql-read');
}

return self::$read_conn;
}

}
$sql = "SELECT $fields FROM $table WHERE field = ? ";
$result = DBConnService::readConn()->select($sql, [$field]);
//$type = OrderTypes::find($type);
$type = OrderTypes::on('mysql-read')->find($type);//https://laravelacademy.org/post/9692.html
这样查询时只连接从库一次,而且一次请求中也只连接从库一次http://www.voidcn.com/article/p-dypdejbw-bew.html

502 Bad Gateway

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
 [error] 26653#0: *748 recv() failed (104: Connection reset by peer) while reading response header from upstream, client: 10.235.51.48, server: xxx.cn, request: "GET /api/openapi.php HTTP/1.1", upstream: "fastcgi://127.0.0.1:9001", host: "xx.cn"
在php.ini和php-fpm.conf中分别有这样两个配置项:max_execution_time和request_terminate_timeout。

这两项都是用来配置一个PHP脚本的最大执行时间的。当超过这个时间时,PHP-FPM不只会终止脚本的执行,

还会终止执行脚本的Worker进程。所以Nginx会发现与自己通信的连接断掉了,就会返回给客户端502错误。 request_terminate_timeout可以覆盖max_execution_time,所以如果不想改全局的php.ini,那只改PHP-FPM的配置就可以了。
打开 php-fpm.conf 发现了:request_terminate_timeout = 5
request_terminate_timeout mixed
设置单个请求的超时中止时间。该选项可能会对 php.ini 设置中的 ‘max_execution_time’ 因为某些特殊原因没有中止运行的脚本有用。设置为 ‘0’ 表示 ‘Off’。可用单位:s(秒),m(分),h(小时)或者 d(天)。默认单位:s(秒)。默认值:0(关闭)。
估计就是这儿了 ,改成 30 还是不行
重启fpm 就好了
[root@localhost ~]# cat fpm_restart.sh
#! /bin/bash
PIDS=`ps aux | grep fpm | grep php | awk '{print $2}'`
for PID in $PIDS
do
kill -9 $PID
done

/data0/opt/php55/sbin/php-fpm
https://blog.csdn.net/crj121624/article/details/79956283
如果你服务器并发量非常大,那只能先增加机器,然后按以下方式优化会取得更好效果;但如果你并发不大却出现502,一般都可以归结为配置问题,脚本超时问题。

ffmpeg: command not found

1
2
3
4
5
6
7
8
9
10
11
12
13
通过PHP调用这个命令,没有引入对应的环境变量,导致找不到这个命令

解决办法

在PHP-fpm.conf的配置文件里面把下面几行前面的;去掉
我的PHP配置文件php-fpm.conf 在/usr/local/php/etc/php-fpm.conf ;
env[PATH]里面加入ffmpeg的路径,比如我的在/data/bin

env[PATH] = /usr/local/bin:/usr/bin:/bin:/data/bin
env[TMP] = /tmp
env[TMPDIR] = /tmp
env[TEMP] = /tmp
env[LD_LIBRARY_PATH] = /usr/local/lib

Warning: simplexml_load_file()

1
2
3
Warning: simplexml_load_file(): I/O warning : failed to load external entity "xxx "
后来将simplexml_load_file("XXXX")替换成
simplexml_load_string(file_get_contents("XXXX"));

同时读写一个文件的问题

1
2
3
4
5
6
7
8
$fp = fopen("/tmp/lock.txt","w+");
if(flock($fp, LOCK_EX)){// 进行排它型锁定http://www.bowen-tech.top/articles/detail/ar_5bc44ba823667_CJPBkHZN
fwrite($fp,"Write something here\n");
flock($fp, LOCK_UN);// 释放锁定
}else{
echo "Couldn't lock the file !";
}
fclose($fp);

nginx: [error] open() “/run/nginx.pid” failed

1
2
3
4
5
6
7
8
9
10
11
12
13
[root@VM_0_14_centos gogs]# ps aux|grep nginx
root 7984 0.0 0.0 4228 24 ? Ss Jan21 0:00 runsv nginx
root 9479 0.0 0.0 4372 4 ? S Jan21 0:00 svlogd -tt /var/log/gitlab/nginx
root 15221 0.0 0.0 112708 972 pts/0 R+ 14:42 0:00 grep --color=auto nginx
root 24987 0.0 0.0 38456 36 ? Ss Jan22 0:00 nginx: master process /opt/gitlab/embedded/sbin/nginx -p /var/opt/gitlab/nginx
gitlab-+ 24993 0.0 0.0 44792 84 ? S Jan22 0:00 nginx: worker process
gitlab-+ 24994 0.0 0.0 40540 120 ? S Jan22 0:00 nginx: cache manager process
[root@VM_0_14_centos gogs]# pkill -9 nginx
[root@VM_0_14_centos gogs]# nginx -s reload
nginx: [error] invalid PID number "" in "/run/nginx.pid"
nginx根本就没有启动过,所以pid文件的值为空没法平滑启动,先启动了才能平滑启动
[root@VM_0_14_centos gogs]# nginx -c /etc/nginx/nginx.conf
[root@VM_0_14_centos gogs]# nginx -s reload

502

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
Nginx的超时配置
fastcgi_connct_timeout 60
fastcgi_read_timeout 300
fastcgi_send_timeout 300

php.ini PHP-FPM 的超时配置
max_execution_time 300
request_terminate_timeout 0
function pft_file_get_contents($url, $timeout = 10, $header = []){
$url = strval($url);
$timeout = intval($timeout);
$timeout = $timeout <= 0 ? 10 : $timeout;

$contextOptions = [
'http' => ['timeout' => $timeout]
];
if($header) {
$contextOptions['http']['header'] = $header;
}

$context = stream_context_create($contextOptions);
$res = file_get_contents($url, false, $context);
return $res;
}
在Mysql和Redis服务器的配置中就会设置wait_timeout 和 timeout 参数,保证连接在超时没有连接的情况下断开连接。在程序方面需要做的就是处理超时后的'The MySQL server has gone away''PHP Fatal error: Uncaught exception 'RedisException' with message 'read error on connection'' 的异常捕获和重连接处理。不过正常情况下,这些在底层框架应该都做了统一的封装。
https://www.jianshu.com/p/3e3fb0459ca3

php.ini max_execution_time=1,不一定1s后就会中止脚本,可能是2s、3s甚至更长的时间;而request_terminate_timeout=4则就会在4s后中止脚本的执行。所以在配置超时时间的时候,最好两个都配置,max_execution_time时间短一点,而request_terminate_timeout时间长一点

php -a 无法使用

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
//https://github.com/codcodog/Blog/issues/77
编译安装的 PHP 7.1.12, 在命令行输入 php -a, 提示 Interactive mode enabled, 无法使用.

解决办法:
安装 readline 扩展.

$ cd /usr/local/src/php-7.1.12/ext/readline
$ phpize
$ ./configure --with-php-config=/usr/local/php/bin/php-config
checking for PHP installed headers prefix... /usr/local/php/include/php
checking if debug is enabled... no
checking if zts is enabled... no
checking for re2c... re2c
checking for re2c version... 0.14.3 (ok)
checking for gawk... gawk
checking for libedit readline replacement... yes, shared
configure: error: Please reinstall libedit - I cannot find readline.h
$ make && make install
$ make clean
编辑 php.ini, 添加 extension=readline.so 扩展.

查看是否加载扩展

$ php -m | grep readline
readline

json_decode()返回null

1
2
3
4
5
6
7
json_decode()函数,要求输入的字符串是有要求的:

使用UTF-8编码
不能在最后元素有逗号
不能使用单引号
不能有\r,\t
所以,在接口传过来的json字符串中,需要查看是否包含了BOM头,如果有,去除

env忽略

1
2
在env中为DB_PASSWORD=abcde#142!*,但是在PHP代码中读取的数据库密码配置为abcde
.env文件中,对密码字段加上双引号,如DB_PASSWORD="abcde#142!*",然后一切恢复正常。

exceeded the timeout of 60 seconds

1
2
3
4
5
6
[Symfony\Component\Process\Exception\ProcessTimedOutException]                                                                                                                                                                              
The process ""/usr/local/Cellar/php55/5.5.14/bin/php" artisan queue:work
--queue="QUEUE_URL" --delay=0 --memory=128 --sleep=3 --tries=0"
exceeded the timeout of 60 seconds.
https://stackoverflow.com/questions/25877752/laravel-queue-process-timeout-error/26238686
php artisan queue:listen --timeout=0

set_exception_handler() 不再保证收到的一定是 Exception 对象

1
2
3
4
5
6
7
8
9
10
<?php
// PHP 5 时代的代码将会出现问题
function handler(Exception $e) { ... }
set_exception_handler('handler');

// 兼容 PHP 5 和 7 http://www.syyong.com/php/Php5-5-upgrade-to-php7-2.html
function handler($e) { ... }

// 仅支持 PHP 7
function handler(Throwable $e) { ... }

二维码生成乱码

1
2
3
4
5
6
7
8
9
composer require endroid/qr-code
use Endroid\QrCode\QrCode;

$qrCode = new QrCode('Life is too short to be generating QR codes');

header('Content-Type: '.$qrCode->getContentType());
echo $qrCode->writeString();
//加上die;才能访问 https://learnku.com/articles/22850?#reply80905
die;

yum Error: rpmdb open failed

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
 yum install net-tools
error: rpmdb: BDB0113 Thread/process 23439/140210621216832 failed: BDB1507 Thread died in Berkeley DB library
error: db5 error(-30973) from dbenv->failchk: BDB0087 DB_RUNRECOVERY: Fatal error, run database recovery
error: cannot open Packages index using db5 - (-30973)
error: cannot open Packages database in /var/lib/rpm
CRITICAL:yum.main:

Error: rpmdb open failed
由于强制结束了yum 操作而导致rpm数据库被损坏了!
1. 进入目录, 查看相关rpmdb文件
# cd /var/lib/rpm/
# ls | grep 'db.'
__db.001
__db.002
__db.003
__db.004

2. 删除损坏的rpmdb文件
将原rpmdb文件都更名为结尾带.bak的文件
# for i in `ls | grep 'db.'`;do mv $i $i.bak;done

# rm -f __db.* # 清除原rpmdb文件

3. 重建rpm数据库
# rpm --rebuilddb

4. 清除所有yum的缓存
# yum clean all

泄露 env 配置

1
2
3
4
5
6
7
8
Laravel 默认依赖的 filp/whoops包 ,当 .env 文件中配置 APP_DEBUG=true 时,会在出错页面打印 .env 配置信息。
Laravel 常用的 barryvdh/laravel-debugbar 包,当 .env 文件中配置 APP_DEBUG=true 时,会输出 session 等敏感信息。
以上两个包默认都在 composer.json 的 require-dev 分支里面,生产环境不安装这两个包,就算 APP_DEBUG 配置成 true,也不会泄露太多敏感的信息。

所以,生产环境部署系统时,一定要加 --no-dev:

composer install --no-dev -vvv
切记:不要让别人能直接访问到 .env 文件

laravel 路由冲突

1
2
3
4
5
6
7
8
9
10
11
//restful 查看
Route::get('products/{product}', 'ProductsController@show')->name('products.show');

//收藏
Route::get('products/favorites', 'ProductsController@favorites')->name('products.favorites');
教程中给出的一种解决方式是将两个路由的位置进行上下调整,当项目变的越来越大,路由增多后,这样的操作可能会给后期挖坑

所以建议增加where条件限制

//restful 查看,product仅支持数整,当id规则变更后,只需更改正则条件
Route::get('products/{product}', 'ProductsController@show')->name('products.show')->where(['product' => '[0-9]+']);

No ‘Access-Control-Allow-Origin’ header is present on the requested resource

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
发送非简单请求时,伺服器端会先收到一个 OPTIONS 的预请求,前端只有收到这个预请求的正常回应,才会发送正式的 POST 请求。
新增中间件 app\Http\Middleware\Cors.php,处理 POST 请求
public function handle($request, Closure $next)
{
$response = $next($request);
if ($request->getMethod() === 'OPTIONS') {
$response->header('Access-Control-Allow-Origin', '*');
$response->header('Access-Control-Allow-Methods', 'GET, POST, PUT, PATCH, DELETE, OPTIONS');
$response->header('Access-Control-Allow-Credentials', 'true');
$response->header('Access-Control-Allow-Headers', 'Authorization, Content-Type, X-PINGOTHER');
}
$response->header('Access-Control-Allow-Origin', '*');

return $response;
}
protected $routeMiddleware = [
...

'cors' => \App\Http\Middleware\Cors::class,
];
Route::post('upload', 'UploadController@upload')->middleware('cors');

Git status中文乱码问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
https://blog.huzhifeng.com/2016/03/08/Git-Tips-And-Tricks/

git config --global core.quotepath false
root@huzhifeng:~/blog# cat ~/.gitconfig
[user]
name = huzhifeng
email = zhifeng.hu@email.com
[core]
editor = vim
quotepath = false
[alias]
co = checkout
st = status
dt = difftool
di = diff
root@huzhifeng:~/blog#
root@huzhifeng:~/blog# git status
On branch master
Your branch is up-to-date with 'origin/master'.
Untracked files:
(use "git add <file>..." to include in what will be committed)
source/_posts/WiFi定时开关.md
nothing added to commit but untracked files present (use "git add" to track)
root@huzhifeng:~/blog#

Cannot run program “svn”

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
svn1.8使用了命令工具(command line client tools)默认安装SVN时是未安装此客户端的
安装的时候选择 command line client tools (will be installed on local)
$ ls /d/svn/bin/*.exe
/d/svn/bin/ConnectVPN.exe* /d/svn/bin/TortoiseBlame.exe* /d/svn/bin/TortoiseProc.exe*
/d/svn/bin/sendrpt.exe* /d/svn/bin/TortoiseIDiff.exe* /d/svn/bin/TortoiseUDiff.exe*
/d/svn/bin/SubWCRev.exe* /d/svn/bin/TortoiseMerge.exe* /d/svn/bin/TSVNCache.exe*
/d/svn/bin/SubWCRevCOM.exe* /d/svn/bin/TortoisePlink.exe*

安装后就有svn 命令
$ ls /d/svn/bin/*.exe
/d/svn/bin/ConnectVPN.exe* /d/svn/bin/svndumpfilter.exe* /d/svn/bin/TortoiseBlame.exe*
/d/svn/bin/sendrpt.exe* /d/svn/bin/svnlook.exe* /d/svn/bin/TortoiseIDiff.exe*
/d/svn/bin/SubWCRev.exe* /d/svn/bin/svnmucc.exe* /d/svn/bin/TortoiseMerge.exe*
/d/svn/bin/SubWCRevCOM.exe* /d/svn/bin/svnrdump.exe* /d/svn/bin/TortoisePlink.exe*
/d/svn/bin/svn.exe* /d/svn/bin/svnserve.exe* /d/svn/bin/TortoiseProc.exe*
/d/svn/bin/svnadmin.exe* /d/svn/bin/svnsync.exe* /d/svn/bin/TortoiseUDiff.exe*
/d/svn/bin/svnbench.exe* /d/svn/bin/svnversion.exe* /d/svn/bin/TSVNCache.exe*

https://www.jianshu.com/p/9cd383bdbe09 PhpStorm中如何配置SVN

svn比较版本 svn try setting http-chunked-requests to auto or no

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
https://stackoverflow.com/questions/17399664/subversion-1-8-0-client-working-against-a-1-6-11-server 
https://www.cnblogs.com/QLeelulu/archive/2009/12/09/1619927.html
vi C:\Users\xxx\AppData\Roaming\Subversion\servers
[global]
# http-proxy-exceptions = *.exception.com, www.internal-site.org
# http-proxy-host = defaultproxy.whatever.com
# http-proxy-port = 7000
# http-proxy-username = defaultusername
# http-proxy-password = defaultpassword
# http-compression = auto
# No http-timeout, so just use the builtin default.
# ssl-authority-files = /path/to/CAcert.pem;/path/to/CAcert2.pem
#
# Password / passphrase caching parameters:
# store-passwords = no
# store-ssl-client-cert-pp = no
# store-plaintext-passwords = no
# store-ssl-client-cert-pp-plaintext = no
http-chunked-requests = no
先选择ShowLog查看修改记录,然后选择你要比较的两个版本,然后按右键选 Compare revisions 单机对应文件可以看到差异
比较后选中文件导出差异文件

ipv6惹的祸

1
2
3
4
5
6
7
8
9
10
11
Operation timed out after 0 milliseconds with 0 out of 0 bytes received

Resolving timed out after 5514 milliseconds
wget www.domain.com
--2016-11-19 22:17:30-- http://www.domain.com/
Resolving www.domain.com... # 此处停滞约 5 秒
xxx.xxx.xxx.xxx
Connecting to www.domain.com|xxx.xxx.xxx.xxx|:80... connected.
HTTP request sent, awaiting response... 200 OK

curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4 );

php 只接受1000个 请求参数

1
2
3
4
5
6
7
8
在查看php的error log 发现有句提示如下
http://www.54php.cn/default/156.html


Unknown: Input variables exceeded 1000


这句提示就是 最多1000 的请求参数,这个在某些项目中1000可能满足不了需求,所以我们需要修改下,找到php.ini文件 ,搜索关键字 max_input_vars 根据项目需求设置满足你需求的值

502 Bad Gateway错误

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
php程序执行时间过长而超时,检查nginx和fastcgi中各种timeout设置。

nginx:

fastcgi_connect_timeout 300;

fastcgi_send_timeout 300;

fastcgi_read_timeout300;

keepalive_timeout;

php-fpm:

request_terminate_timeout=300;

php.ini:

max_execution_time=300;



不过request_terminate_timeout参数会直接杀掉php进程,然后重启php进程,这样前端nginx就会返回104: Connection reset by peer,最好设成request_terminate_timeout=0;但最重要的是程序里要设置好超时,不要使用php-fpm的request_terminate_timeout。https://blog.51cto.com/smileyouth/1586237
使用 netstat -napo |grep "php-fpm" | wc -l 查看一下当前fastcgi进程个数,如果个数接近conf里配置的上限,就需要调高进程数。
http://www.nginx.cn/102.html
[root@localhost ~]# grep slow /etc/php-fpm.d/www.conf
; - 'slowlog'
; The log file for slow requests
; Note: slowlog is mandatory if request_slowlog_timeout is set
slowlog = /var/log/php7/slow.log
; dumped to the 'slowlog' file. A value of '0s' means 'off'.
request_slowlog_timeout = 2s

[root@localhost ~]# tail -f /var/log/php7/slow.log
[0x00007f347c6143a0] xhprof_generate_image_by_dot() /data1/projects/public/xhprof/xhprof_lib/utils/callgraph_utils.php:450
[0x00007f347c614270] xhprof_get_content_by_run() /data1/projects/public/xhprof/xhprof_lib/utils/callgraph_utils.php:474
[0x00007f347c614170] xhprof_render_image() /data1/projects/public/xhprof/xhprof_html/callgraph.php:85

[22-Feb-2019 17:20:07] [pool www] pid 3899
script_filename = /data1/projects/public/xhprof/xhprof_html/callgraph.php
[0x00007f347c6144c0] stream_get_contents() /data1/projects/public/xhprof/xhprof_lib/utils/callgraph_utils.php:117
[0x00007f347c6143a0] xhprof_generate_image_by_dot() /data1/projects/public/xhprof/xhprof_lib/utils/callgraph_utils.php:450
[0x00007f347c614270] xhprof_get_content_by_run() /data1/projects/public/xhprof/xhprof_lib/utils/callgraph_utils.php:474
[0x00007f347c614170] xhprof_render_image() /data1/projects/public/xhprof/xhprof_html/callgraph.php:85

[root@localhost ~]# cat /etc/php-fpm.conf|grep error
;error_log = log/php-fpm.log
error_log = /var/log/php-fpm.log

tail -f /var/log/php-fpm.log
[22-Feb-2019 16:53:21] WARNING: [pool www] child 1649, script '/data1/projects/suping/public/xhprof/xhprof_html/callgraph.php' (request: "GET /public/xhprof/xhprof_html/callgraph.php") executing too slow (2.619120 sec), logging
[22-Feb-2019 16:53:21] NOTICE: child 1649 stopped for tracing
[22-Feb-2019 16:53:21] NOTICE: about to trace 1649
[22-Feb-2019 16:53:21] NOTICE: finished trace of 1649
[22-Feb-2019 16:53:29] WARNING: [pool www] child 1649, script '/data1/projects/suping/public/xhprof/xhprof_html/callgraph.php' (request: "GET /public/xhprof/xhprof_html/callgraph.php") execution timed out (10.619676 sec), terminating
[22-Feb-2019 16:53:29] WARNING: [pool www] child 1649 exited on signal 15 (SIGTERM) after 234.714340 seconds from start
[22-Feb-2019 16:53:29] NOTICE: [pool www] child 2342 started
[root@localhost suping]# grep timeout /data0/opt/php7/etc/php-fpm.d/www.conf
; pm.process_idle_timeout - The number of seconds after which
;pm.process_idle_timeout = 10s;
; Note: slowlog is mandatory if request_slowlog_timeout is set
; The timeout for serving a single request after which a PHP backtrace will be
request_slowlog_timeout = 2s
; The timeout for serving a single request after which the worker process will
request_terminate_timeout = 10


内存过高,php脚本执行超时这两种情况都返回的是500.
500502的区别是,
500是php脚本自身错误(内存超出配置memory_limit;超过cpu执行时间set_time_limit(),语法错误等)
502是php-fpm进程本身挂掉(php-fpm未启动;request_terminate_timeout超时)

php-fpm进程和php脚本是两回事,因此状态码上也会有区别。
php-cgi进程数不够用、php执行时间长、或者是php-cgi进程死掉,都会出现502错误。慢查询2s,超时10s,改为15

字符集转转换失败

1
2
3
4
5
$xml = iconv('UTF-8', 'GBK', $xml);
就是这一步,iconv转换失败的时候会返回false而不是报错,也没有日志记录,所以 $xml 被置为 false 了。。。。。至于为什么会失败,我也不知道,应该是有某种特殊编码把,有一个办法可以跳过这个转换失败,就是在目标字符集加一个选项:

$xml = iconv('UTF-8', 'GBK//IGNORE', $xml);
http://blog.wuxu92.com/php-iconv-ignore-option/

PHP7 MongoDB

1
2
3
4
5
6
7
8
$mongo = new MongoDB\Driver\Manager();
$cursor = $mongo->executeQuery('db.collection', new MongoDB\Driver\Query($arr, $opts), new MongoDB\Driver\ReadPreference(MongoDB\Driver\ReadPreference::RP_PRIMARY_PREFERRED));
//注意,这里的 $arr和 $opts; https://my.oschina.net/jsk/blog/644287
$arr = ['_id'=> ['$in'=> $ids] ]; // 根据id数组获取集合
$opts = ['limit'=> $limit, 'skip'=> $skip ]; // 不推荐
// 这里在传递$limit和$skip的时候,最好先转成整数,不然不生效:
$opts = ['limit'=> (int) $limit, 'skip'=> (int) $skip ]; // 推荐写法。
// 写到这里,让人怀疑是不是在用php了,怎么还需要这样的类型转换。字符串整数都不行!

MISCONF Redis is configured to save RDB snapshots

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
 Redis 被配置为保存 RDB 快照,但是现在无法写入磁盘,那么这无非是两个问题,一个是没有权限,一个是空间不足,于是立即登录到 Redis 所在服务器,分别查看了权限和空间都没有问题,当务之急是先修复问题,然后再定位原因,所以立即在服务启动 redis-cli,将 stop-writes-on-bgsave-error 设置为 on(该选项在 Redis 配置文件中默认配置为 yes,表示 Redis 报错时停止写入并报错,我们将其设置为 on 表示忽略报错,继续执行):
https://laravelacademy.org/post/9666.html
#redis-cli
127.0.0.1:6379> config set stop-writes-on-bgsave-error no
打开 Redis Server 的日志文件 /var/log/redis/redis-server.log,想从这里寻找蛛丝马迹,果不其然,发现了大量报错日志:

14632:C 09 Nov 08:58:14.020 # Failed opening .rdb for saving: Read-only file system
30211:M 09 Nov 08:58:14.120 # Background saving error

把 Redis 配置文件中 bind 选项配置为了 0.0.0.0,以便让多个机器可以访问,但是没有配置任何权限和 iptables 过滤规则,这样导致的结果是外网可以自由访问我的 Redis 服务器,这种故障会导致 Redis 被 block
配置允许 192.168.0.1192.168.0.2 这两个 IP 访问 6379 端口(具体 IP 以你自己服务器的公网 IP 为准,允许自身访问 IP 可以配置为 127.0.0.1):

# 禁止任何 IP 访问 6379 端口
iptables -I INPUT -p TCP --dport 6379 -j REJECT
# 允许指定 IP 访问 6379 端口
iptables -I INPUT -s 192.168.0.1 -p tcp --dport 6379 -j ACCEPT
iptables -I INPUT -s 192.168.0.2 -p tcp --dport 6379 -j ACCEPT
# 保存并生效
iptables-save
然后我们需要通过 kill -9 杀死 redis-server 进程,并通过 redis-server /etc/redis/redis.conf 重新启动
如果你是将 Redis 服务器安装在应用所在服务器,并且只有这么一台机器,不用这么大费周章的配置,只需在 Redis 配置文件中将 bind 选项设置为 127.0.0.1 只允许本机访问即可。

mongodb3.6 The ‘cursor’ option is required, except for aggregate with the explain argument

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
//Mongo-记一次安装启动异常 http://zhangyuyu.github.io/2017/12/27/Mongo-%E8%AE%B0%E4%B8%80%E6%AC%A1%E5%AE%89%E8%A3%85%E5%90%AF%E5%8A%A8%E9%94%99%E8%AF%AF/
//https://github.com/jenssegers/laravel-mongodb/pull/1570/commits/6076df9cee559ff6e75956b7a0f9ccf7c35f76b5#diff-c19a83820d10bafa13d449ec36d0b360
//https://github.com/jenssegers/laravel-mongodb/pull/1569/commits/36222977ca52635b7d06377f9553fe67b903fede#diff-d013b1af075671441bada29dbedab6ff
/vendor/jenssegers/mongodb/src/Jenssegers/Mongodb/Collection.php:47
public function __call($method, $parameters)
{
$start = microtime(true);
//增加判断
if($method == 'aggregate')
{
$parameters[] = ['cursor' =>(object) []];
}
$result = call_user_func_array([$this->collection, $method], $parameters);
if($method == 'aggregate')
{
$result['result'] = $result['cursor']['firstBatch'];
}
if ($this->connection->logging())
{
// Once we have run the query we will calculate the time that it took to run and
// then log the query, bindings, and execution time so we will report them on
// the event that the developer needs them. We'll log time in milliseconds.
$time = $this->connection->getElapsedTime($start);

$query = [];

// Convert the query paramters to a json string.
foreach ($parameters as $parameter)
{
try
{
$query[] = json_encode($parameter);
}
catch (Exception $e)
{
$query[] = '{...}';
}
}

$queryString = $this->collection->getName() . '.' . $method . '(' . implode(',', $query) . ')';

$this->connection->logQuery($queryString, [], $time);
}

return $result;
}

// /vendor/jenssegers/mongodb/src/Jenssegers/Mongodb/Query/Builder.php:228
public function getFresh($columns = [])
{
// If no columns have been specified for the select statement, we will set them
// here to either the passed columns, or the standard default of retrieving
// all of the columns on the table using the "wildcard" column character.
if (is_null($this->columns)) $this->columns = $columns;

// Drop all columns if * is present, MongoDB does not work this way.
if (in_array('*', $this->columns)) $this->columns = [];

// Compile wheres
$wheres = $this->compileWheres();

// Use MongoDB's aggregation framework when using grouping or aggregation functions.
if ($this->groups or $this->aggregate or $this->paginating)
{
$group = [];

// Add grouping columns to the $group part of the aggregation pipeline.
if ($this->groups)
{
foreach ($this->groups as $column)
{
$group['_id'][$column] = '$' . $column;

// When grouping, also add the $last operator to each grouped field,
// this mimics MySQL's behaviour a bit.
$group[$column] = ['$last' => '$' . $column];
}

// Do the same for other columns that are selected.
foreach ($this->columns as $column)
{
$key = str_replace('.', '_', $column);

$group[$key] = ['$last' => '$' . $column];
}
}

// Add aggregation functions to the $group part of the aggregation pipeline,
// these may override previous aggregations.
if ($this->aggregate)
{
$function = $this->aggregate['function'];

foreach ($this->aggregate['columns'] as $column)
{
// Translate count into sum.
if ($function == 'count')
{
$group['aggregate'] = ['$sum' => 1];
}
// Pass other functions directly.
else
{
$group['aggregate'] = ['$' . $function => '$' . $column];
}
}
}

// When using pagination, we limit the number of returned columns
// by adding a projection.
if ($this->paginating)
{
foreach ($this->columns as $column)
{
$this->projections[$column] = 1;
}
}

// The _id field is mandatory when using grouping.
if ($group and empty($group['_id']))
{
$group['_id'] = null;
}

// Build the aggregation pipeline.
$pipeline = [];
if ($wheres) $pipeline[] = ['$match' => $wheres];
if ($group) $pipeline[] = ['$group' => $group];

// Apply order and limit
if ($this->orders) $pipeline[] = ['$sort' => $this->orders];
if ($this->offset) $pipeline[] = ['$skip' => $this->offset];
if ($this->limit) $pipeline[] = ['$limit' => $this->limit];
if ($this->projections) $pipeline[] = ['$project' => $this->projections];

// Execute aggregation
$results = $this->collection->aggregate($pipeline);

// Return results 更改返回值
return $results['cursor']['firstBatch'];
}

php发送email 多了!

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
https://blog.csdn.net/yylad/article/details/9048871

造成这种现象的两种可能和解决办法:

 html表情和CSS与邮件客户端不兼容, 
以outlook为例, 参考http://msdn.microsoft.com/zh-cn/library/aa338201(v=office.12).aspx, 这个问题没其他办法, 只能自己优化代码, 采用标准化写法, 如标签尽量完整, 浏览器可以识别<br>表情, 但要换成标准写法<br/>, 还有就是记得闭合标签.



邮件内容过长, 导致解析出问题.
通常邮件信息每行不得超过998个字符, 而整个html邮件体中没有换行符, 会自动的被认为是一行, 所以就会有问题 (998 听起来有点像电视购物呢 - -!!). 怎么办呢?

两种解决办法:

1. 人工增加换行符: 以上面代码为例, 将$body取值替换为 

$body = wordwrap(get_email_body($an_array),900, "\n");
即, 在邮件体字符串中,每900个字符加一个换行符, 这样就保证了每行字符串不会超过上限. 但同时也会带来新的问题, 因为额外加的字符串在某些情况下, 可能会导致邮件布局有问题(特定浏览器, 操作系统或者邮件客户端).

2. 用base64编码 

$header = "MIME-Version: 1.0\r\nContent-Transfer-Encoding: base64\r\nContent-type: text/html; charset=utf-8\r\nFrom: test<test@test.com>";
$body = rtrim(chunk_split(base64_encode(get_email_body($an_array))));
首先给body添加base64编码, 然后在header中添加Content-Transfer-Encoding: base64说明. 因为base64编码会自动每76个字符添加一个换行符, 所以自然满足了每998字符换行的问题.
---------------------

If you are seeing unwanted line breaks preceded by exclamation marks ("!") in the emails you send with mail(), you may be having the same problem I had: exceeding the maximum line length (998 chars) specified in RFC 2822 (http://www.faqs.org/rfcs/rfc2822.html).
You can get around this restriction by using base64 encoding: add a "Content-Transfer-Encoding: base64" header and encode the contents with
$base64contents = rtrim(chunk_split(
base64_encode($contents)));

$headers = "From:someone@somewhere.com\r\n";
$headers .= "MIME-Version: 1.0\r\n";
$headers .= "Content-Type: text/html; charset=utf-8\r\n";
$headers .= "Content-Transfer-Encoding:base64 \r\n";
$message = "THIS IS A TEST";
$messagebody= $base64contents = rtrim(chunk_split(base64_encode($message)));
mail(tofoo@bar.com, $subject, $messagebody, $headers )

@mail($to, $subject, $msg, "From: $from\nContent-Type: text/html; charset=iso-8859-1; Content-Transfer-Encoding: base64");

PhpSpreadsheet 输出 Excel 时自动在 xls 后面加入 html

1
2
3
4
5
6
7
8
//http://www.sunbelife.com/p/b63c.html
header('Content-Type: application/vnd.ms-excel');
header('Content-Disposition: attachment;filename="赴宴信息.xls"');
header('Cache-Control: max-age=0');

$writer = \PhpOffice\PhpSpreadsheet\IOFactory::createWriter($spreadsheet, 'Xls');
$writer->save('php://output');
exit;

Composer 使用镜像出现权限问题

1
2
3
4
5
6
7
8
9
vagrant@homestead:~/Code/larabbs$ composer require "overtrue/laravel-lang:~3.0"
./composer.json has been updated
Loading composer repositories with package information
Updating dependencies (including require-dev)
file_put_contents(/home/vagrant/.composer/cache/repo/https---packagist.laravel-china.org/provider-illuminate$console.json): failed to open stream: Permission denied
https://packagist.laravel-china.org could not be fully loaded, package information was loaded from the local cache and may be out of date

sudo chown -R $USER ~/.composer/
https://learnku.com/laravel/t/29560

No query results for model

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
如果将

Route::get('/users/{user}', 'UserController@show')->name('users.show');
放在了

Route::get('/users/info', 'UserController@info')->name('users.info');
前面的话,请求 /users/info 会提示

No query results for model [App\Post].
原来测试的时候没有想过路由冲突的问题。通过更换路由先后顺序确实是可以解决这个情况。
但是需要这样解决,太无法体现 laravel 的优雅了。我们可以用上路由提供的正则表达式来很好解决这个问题。

//用户信息
Route::get('/users/{user}', 'UserController@show')->where('user', '[0-9]+')->name('users.show');
https://learnku.com/articles/25947#replies

composer Out of memory

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
$ composer require chenhua/laravel5-markdown-editor
1/2: https://packagist.laravel-china.org/p/provider-latest$5f7ff05acde3b89b16151c86e800d5ac9afebd3a1b827d539c5f341b3355db2f.json
2/2: https://packagist.laravel-china.org/p/provider-2019-04$ceaf82cd34c625192605362e4eb8f1477899cb63e2a0b3cb527e65d124b42a24.json
Finished: success: 2, skipped: 0, failure: 0, total: 2
Using version ^1.0 for chenhua/laravel5-markdown-editor
./composer.json has been updated
Loading composer repositories with package information
Updating dependencies (including require-dev)

VirtualAlloc() failed: [0x00000008] ▒洢▒ռ䲻▒㣬▒޷▒▒▒▒▒▒▒▒▒


VirtualFree() failed: [0x000001e7] ▒▒ͼ▒▒▒▒▒▒Ч▒ĵ▒ַ▒▒


VirtualAlloc() failed: [0x00000008] ▒洢▒ռ䲻▒㣬▒޷▒▒▒▒▒▒▒▒▒


VirtualFree() failed: [0x000001e7] ▒▒ͼ▒▒▒▒▒▒Ч▒ĵ▒ַ▒▒

PHP Fatal error: Out of memory (allocated 962592768) (tried to allocate 4096 bytes) in phar://D:/php_study/PHPTutorial/php/php-7.1.13-nts/composer/src/Composer/DependencyResolver/RuleWatchGraph.php on line 52

Fatal error: Out of memory (allocated 962592768) (tried to allocate 4096 bytes) in phar://D:/php_study/PHPTutorial/php/php-7.1.13-nts/composer/src/Composer/DependencyResolver/RuleWatchGraph.php on line 52
php -d memory_limit=-1 `which composer` update/install -vvv, 去掉内存限制
php -d memory_limit=1G composer require chenhua/laravel5-markdown-editor

sudo vim /etc/php5/cli/php.ini 按: 进入命令行模式 输入 /memory_limit,找到 memory_limit 修改配置为 memory_limit = 1024M

free -m
total used free shared buff/cache available
Mem: 864 372 306 50 185 296
Swap: 0 0 0
# 如上发现Swap实际都为0
/bin/dd if=/dev/zero of=/var/swap.1 bs=1M count=1024
/sbin/mkswap /var/swap.1
/sbin/swapon /var/swap.1
# 再次使用free -m,发现已经有了Swap内存配置
total used free shared buff/cache available
Mem: 864 383 67 49 413 267
Swap: 1023 0 1023
# 再次运行composer install即可

mysql 慢查询 502

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
大量请求响应为502,但每次故障发生时,错误响应一般集中在一台Web服务器
MySQL数据库服务器CPU使用率飙升

故障刚开始出现时,重启/关闭出现故障现象的MySQL服务,或将出现故障的Web服务器上所有PHP-FPM重启,也能解一时的问题,但治不了本,故障还是频繁出现。

在故障发生时,从相关服务器上收集到的信息如下所示:

1.出现故障现象的Web服务器 - CPU使用率、内存使用率等系统指标均正常,但PHP-FPM子进程数达到上限(12 x 10 = 120),并且PHP-FPM进程与数据库代理服务器之间的网络连接数量较多(与PHP-FPM子进程数大致相当)

2.出现故障现象的MySQL服务器 - CPU使用率飙升,主要为MySQL进程占用;MySQL进程与数据库代理服务器之间的网络连接较多

3.继而,对出现故障现象的MySQL服务器上的数据库执行命令SHOW PROCESSLIST(查看当前MySQL实例运行着哪些线程)

Query:表明当前线程正在执行一次SQL查询操作。该SQL为SELECT h.host, p.result, p.update_time FROM PIXIU p join Host h using(host_id) WHERE ...,线程所处状态为“Sorting result”(正在创建排序索引),持续时间为86-99秒左右。很明显,这句SQL语句花费的时间过长,存在问题。
综合上面所述,可以引出一个猜测:由于这条SQL查询需耗费较长时间,并且被频繁执行,涉及该SQL的请求需要较长时间完成,大量SQL线程排队无响应,阻塞了大量PHP-FPM进程,在某些时候会达到PHP-FPM并发子进程数上限(更何况某个会被频繁访问的页面请求涉及该SQL,导致情况更糟),PHP-FPM无法处理新的请求,对于已有的请求也会因为超时导致Nginx响应502

那么针对该猜测,可以做两个优化来解决故障:

优化这条SQL
使用缓存
这条SQL的完整语句为: SELECT h.host,p.result,p.update_time FROM Pixiu p join Host h using(host_id) WHERE result!='[]' order by update_time desc ,其中字段p.result的类型为 mediumtext NOT NULL ,字段p.update_time的类型为 timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP 。

由于业务逻辑并不要求该SQL的结果是排序的,所以我们将该SQL中的排序条件order by update_time desc删除,经测试发现查询时间大幅度降低到9ms左右(原来的平均查询时间为600-700ms左右),另外,由于业务逻辑对于该条SQL涉及的数据的实时性要求不高,我们使用Memcached缓存了该SQL的查询结果。
http://blog.xiayf.cn/2015/10/02/note-of-a-system-fault/

数据库管理后台默认是只读:读数据表列表、数据表结构、单个表的若干条数据
我们应用在实现上有这样的逻辑:登录用户的每次访问需要登录权限的页面都会自动更新用户的最新的访问时间,即Users数据表的updated_time字段,也即会写Users数据表。
由于磁盘已满,所以写会失败,故障信息提示“数据库中找不到Users数据表”,估计和MySQL的写实现有关。

之后清理了磁盘,故障立即恢复。

api频率限制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 每个IP一分钟10次
$limit = 10;

$cache = new Memcached();
$cache->addServer('127.0.0.1', 11211);

$key = __FUNCTION__.$_SERVER['REMOTE_ADDR'];
$requestNum = $cache->get($key);

if ($requestNum !== FALSE && $requestNum > 10) {
echo json_encode(array(
'code' => 403,
'message' => '请求太频繁,请一分钟后再试',
));
return;
}

$cache->add($key, 0, time()+60);
$cache->increment($key, 1);
http://blog.xiayf.cn/2016/06/05/frequency-limitation/

curl请求https

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
$ curl 'https://x.x.x.x/get_ips'

curl: (60) SSL certificate problem, verify that the CA cert is OK. Details:
error:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed
More details here: http://curl.haxx.se/docs/sslcerts.html

发现可能是https证书的问题。于是添加--verbose参数,再次使用curl进行请求,以获取更多交互信息。

截取部分输出如下

$ curl 'https://x.x.x.x/get_ips' --verbose

* About to connect() to x.x.x.x port 80
* Trying x.x.x.x... connected
* Connected to x.x.x.x (x.x.x.x) port 80
* successfully set certificate verify locations:
* CAfile: /etc/pki/tls/certs/ca-bundle.crt
CApath: none
* SSLv2, Client hello (1):
SSLv3, TLS handshake, Server hello (2):
SSLv3, TLS handshake, CERT (11):
SSLv3, TLS alert, Server hello (2):
SSL certificate problem, verify that the CA cert is OK. Details:
error:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed
* Closing connection #0
curl: (60) SSL certificate problem, verify that the CA cert is OK. Details:
error:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed
More details here: http://curl.haxx.se/docs/sslcerts.html
可见使用的证书的是/etc/pki/tls/certs/ca-bundle.crt。

使用测试机上的证书替换线上服务器的证书后,问题解决。

如果没有可用的证书,可以使用如下方法:

$ curl http://curl.haxx.se/ca/cacert.pem -o /etc/pki/tls/certs/ca-bundle.crt
请求https的资源时,遇到证书不匹配的问题,一般的工具都有不进行https证书验证的选项,比如:

$ wget 'https://x.x.x.x/get_ips' --no-check-certificate
$ curl 'https://x.x.x.x/get_ips' -k
当然,也可以在请求时指定证书,或者对使用的https ca证书进行更新
http://blog.gaoyuan.xyz/2014/05/14/a-trouble-in-request-https-in-curl/

pip error: command ‘gcc’ failed with exit status

1
2
3
4
5
6
7
8
9
10
11
12
Command /usr/bin/python -c "import setuptools;__file__='/tmp/pip-build-root/lxml/setup.py';exec(compile(open(__file__).read().replace('\r\n', '\n'), __file__, 'exec'))" install --record /tmp/pip-PjviBq-record/install-record.txt --single-version-externally-managed failed with error code 1 in /tmp/pip-build-root/lxml
Storing complete log in /root/.pip/pip.log
For python 2.7

$ sudo yum -y install gcc gcc-c++ kernel-devel
$ sudo yum -y install python-devel libxslt-devel libffi-devel openssl-devel
$ pip install "your python packet"
For python 3.4

$ sudo apt-get install python3-dev
$ pip install rdbtools python-lzf
https://learnku.com/articles/33211

Nginx 中 502 和 504 错误详解

面试题

把SESSION存储在数据库中

PHP超时的坑

Nginx常见的错误及解决方法

Laravel Redis 多个进程同时取队列问题详解

使用 Laravel Queue 不得不明白的知识

Lua在Redis的应用

Redis 实战之薄荷 timeline 的优化

Laravel5.2队列驱动expire参数设置带来的重复执行问题 数据库驱动

为什么 Laravel 会重复执行同一个队列任务?

Laravel queue 重复执行脚本的问题

Laravel队列使用中踩的坑,不报错但是队列一直再重试