Skip to content

Latest commit

 

History

History
1542 lines (1404 loc) · 50.8 KB

gist7.md

File metadata and controls

1542 lines (1404 loc) · 50.8 KB

###Python requests SSL: CERTIFICATE_VERIFY_FAILED

requests.packages.urllib3.disable_warnings()
requests.get(url,verify=False )

###判断一个值是否存在于一个多维关联数组

https://github.com/VikinDev/v-collect
$collect = vcollect([
    ['id'=>1,'product'=>['name'=>['v'=>'i'],'sku'=>15]],
    ['id'=>2,'product'=>['name'=>'bibi','sku'=>10]],
]);

if($collect->where('product.name', 'aa')->toArray() == NULL) {
    echo 'Not in the array';
} else {
    echo 'In the array';
};
var_dump(in_array(2,array_dot($arr)));//true
var_dump(in_array(5,array_dot($arr)));//false
function array_dot($array, $prepend = '')
    {
        $results = [];

        foreach ($array as $key => $value) {
            if (is_array($value)) {
                $results = array_merge($results, array_dot($value, $prepend.$key.'.'));
            } else {
                $results[$prepend.$key] = $value;
            }
        }

        return $results;
    }

###laravel 密码验证

public function make($value, array $options = array())
    {
        $cost = isset($options['rounds']) ? $options['rounds'] : $this->rounds;

        $hash = password_hash($value, PASSWORD_BCRYPT, array('cost' => $cost));

        if ($hash === false)
        {
            throw new RuntimeException("Bcrypt hashing not supported.");
        }

        return $hash;
    }
public function check($value, $hashedValue, array $options = array())
    {
        return password_verify($value, $hashedValue);
    }

$password = Input::get('password_from_user'); 
$hash = Hash::make($password );//保存数据库

//对比
$input = 'password_from_user';
if(Hash::check($input, $hash)){
    
}

###将任意内容的字符串唯一均匀地转换为1~n之间的一个整数

$str = 'Created by PhpStorm.';

$crc32 = crc32($str);

// 因为crc32求出来的是一个32位整数,可为负数,所以abs一下
// 要多少范围内,就求余多少就行
$result = abs($crc32) % 100;

var_dump($result);//59

###无限节点的树结构插件

  var data = [{name: '水果', id: 1, children: [
            {name:'水果1',id:2,children:[
                {name:'水果2',id:3,children:[
                    {name:'水果2',id:3,children:[]}        
                ]}
            ]}
        ]
    }];
    
 function render(data){
         
    data.forEach(function(item,index){
        item.status = false;
        if(item.children){
            render(item.children);
        }
    }); 
    console.log(JSON.stringify(data));
    return data;
 }
 
 render(data);
 JSON.stringify(data)
"[{"name":"水果","id":1,"children":[{"name":"水果1","id":2,"children":[{"name":"水果2","id":3,"children":[{"name":"水果2","id":3,"children":[],"status":false}],"status":false}],"status":false}],"status":false}]"

###mysql 插入1百万条数据

mysql对sql长度有限制
要么分批100次每次插入一万,
mysql的配置文件(my.ini)中的max_allowed_packet = 1M 默认1M,修改成10M试试
或者用LOAD指令来加载数据文件。
source test.sql

###moment.js

  months: '一月_二月_三月_四月_五月_六月_七月_八月_九月_十月_十一月_十二月'.split('_'),
  monthsShort: '1月_2月_3月_4月_5月_6月_7月_8月_9月_10月_11月_12月'.split('_'),
  weekdays: '星期日_星期一_星期二_星期三_星期四_星期五_星期六'.split('_'),
  weekdaysShort: '周日_周一_周二_周三_周四_周五_周六'.split('_'),
  weekdaysMin: '日_一_二_三_四_五_六'.split('_'),
  longDateFormat: {
    LT: 'Ah点mm分',
    LTS: 'Ah点m分s秒',
    L: 'YYYY-MM-DD',
    LL: 'YYYY年MMMD日',
    LLL: 'YYYY年MMMD日Ah点mm分',
    LLLL: 'YYYY年MMMD日ddddAh点mm分',
    l: 'YYYY-MM-DD',
    ll: 'YYYY年MMMD日',
    lll: 'YYYY年MMMD日Ah点mm分',
    llll: 'YYYY年MMMD日ddddAh点mm分'
  },
  meridiemParse: /凌晨|早上|上午|中午|下午|晚上/,
  meridiemHour: function (h, meridiem) {
    let hour = h;
    if (hour === 12) {
      hour = 0;
    }
    if (meridiem === '凌晨' || meridiem === '早上' ||
      meridiem === '上午') {
      return hour;
    } else if (meridiem === '下午' || meridiem === '晚上') {
      return hour + 12;
    } else {
      // '中午'
      return hour >= 11 ? hour : hour + 12;
    }
  },
  meridiem: function (hour, minute, isLower) {
    const hm = hour * 100 + minute;
    if (hm < 600) {
      return '凌晨';
    } else if (hm < 900) {
      return '早上';
    } else if (hm < 1130) {
      return '上午';
    } else if (hm < 1230) {
      return '中午';
    } else if (hm < 1800) {
      return '下午';
    } else {
      return '晚上';
    }
  },
  calendar: {
    sameDay: function () {
      return this.minutes() === 0 ? '[今天]Ah[点整]' : '[今天]LT';
    },
    nextDay: function () {
      return this.minutes() === 0 ? '[明天]Ah[点整]' : '[明天]LT';
    },
    lastDay: function () {
      return this.minutes() === 0 ? '[昨天]Ah[点整]' : '[昨天]LT';
    },
    nextWeek: function () {
      let startOfWeek, prefix;
      startOfWeek = moment().startOf('week');
      prefix = this.diff(startOfWeek, 'days') >= 7 ? '[下]' : '[本]';
      return this.minutes() === 0 ? prefix + 'dddAh点整' : prefix + 'dddAh点mm';
    },
    lastWeek: function () {
      let startOfWeek, prefix;
      startOfWeek = moment().startOf('week');
      prefix = this.unix() < startOfWeek.unix() ? '[上]' : '[本]';
      return this.minutes() === 0 ? prefix + 'dddAh点整' : prefix + 'dddAh点mm';
    },
    sameElse: 'LL'
  },
  ordinalParse: /\d{1,2}(日|月|周)/,
  ordinal: function (number, period) {
    switch (period) {
      case 'd':
      case 'D':
      case 'DDD':
        return number + '';
      case 'M':
        return number + '';
      case 'w':
      case 'W':
        return number + '';
      default:
        return number;
    }
  },
  relativeTime: {
    future: '%s内',
    past: '%s前',
    s: '几秒',
    m: '1 分钟',
    mm: '%d 分钟',
    h: '1 小时',
    hh: '%d 小时',
    d: '1 天',
    dd: '%d 天',
    M: '1 个月',
    MM: '%d 个月',
    y: '1 年',
    yy: '%d 年'
  },
  week: {
    // GB/T 7408-1994《数据元和交换格式·信息交换·日期和时间表示法》与ISO 8601:1988等效
    dow: 1, // Monday is the first day of the week.
    doy: 4  // The week that contains Jan 4th is the first week of the year.
  }
});

###1天前

<script src="dist/timeago.js" type="text/javascript"></script>
new timeago().format(new Date());             //=> "just now"
new timeago(null, 'zh_CN').format('2016-09-07')           //=> "4月前"
new timeago().format(1473245023718);          //=> "4 months ago"

###Python 解数学方程

sudo pip install sympy
from sympy import *
x, y = symbols('x y')
print solve(x * 2 - 4, x)#[2]
print solve([2 * x - y - 3, 3 * x + y - 7],[x, y])#{y: 1, x: 2}

###判断类型

is.wechatApp = function() {
	if ('object' == typeof wx) {
		if (wx && is.fn(wx.createVideoContext)) {
			// wechat js sdk has no createVideoContext
			return true
		}
	}
	return false
}

###php curl

require __DIR__ . '/vendor/autoload.php';
//https://www.v2ex.com/t/333980#;  https://github.com/jmathai/php-multi-curl 
use \Curl\Curl;
$curl = new Curl();
$curl->setOpt(CURLOPT_FOLLOWLOCATION, true);
$curl->post('https://www.example.com/login/', array(
    'username' => 'myusername',
    'password' => 'mypassword',
));

$mc = JMathai\PhpMultiCurl\MultiCurl::getInstance();

  // Set up your cURL handle(s).
  $ch = curl_init('http://www.google.com');
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
  curl_setopt($ch, CURLOPT_POST, 1);

  // Add your cURL calls and begin non-blocking execution.
  $call = $mc->addCurl($ch);

  // Access response(s) from your cURL calls.
  $code = $call->code;

###避免重复注册同一个事件

// 缓存列表
var clientList = {};

// 注册
regist = function(key, fn) {            
    clientList[key] = null;   // 每次都清空,就不需要管他到底有没有注册过
    clientList[key] = fn;    // 重新注册一个
};


// 注册remove事件
regist("remove", function(){console.log("remove function")});
// 执行
clientList["remove"]();

###ajax是异步的

var list = [];//大的集合
$.each(that.categoryList, function(index,value){
   $.ajax({
       type: 'get',
       url: that.getHotUrl,
       data:{
           category:value.id,
           pageIndex:1,
           pageSize:2,
           token:token
       },
       dataType: 'json',
       success: function(response){
          if(response.code==200){
              var obj = new Object();  //集合对象
              obj.category_name=value.name;
              obj.items = response.data.items;
              console.log(obj);
              list.push(obj);
          }
      },
      error: function(err) {
           console.log(err);
     }
  });
});
console.log(list);//null

先执行的是console.log(list);
然后在执行console.log(obj);

setTimeout(function(){
    console.log(list);
},100);

###FileAPI上传文件

<div>
        <!-- "js-fileapi-wrapper" -- required class -->
        <div class="js-fileapi-wrapper upload-btn">
            <div class="upload-btn__txt">Choose files</div>
            <input id="choose" name="files" type="file" multiple />
        </div>
        <div id="images"><!-- previews --></div>
    </div>

    <script>window.FileAPI = { staticPath: '/js/FileAPI/dist/' };</script>
    <script src="/js/FileAPI/dist/FileAPI.min.js"></script>
    <script>
        var choose = document.getElementById('choose');
        FileAPI.event.on(choose, 'change', function (evt){
            var files = FileAPI.getFiles(evt); // Retrieve file list

            FileAPI.filterFiles(files, function (file, info/**Object*/){
                if( /^image/.test(file.type) ){
                    return  info.width >= 320 && info.height >= 240;
                }
                return  false;
            }, function (files/**Array*/, rejected/**Array*/){
                if( files.length ){
                    // Make preview 100x100
                    FileAPI.each(files, function (file){
                        FileAPI.Image(file).preview(100).get(function (err, img){
                            images.appendChild(img);
                        });
                    });

                    // Uploading Files
                    FileAPI.upload({
                        url: './ctrl.php',
                        files: { images: files },
                        progress: function (evt){ /* ... */ },
                        complete: function (err, xhr){ /* ... */ }
                    });
                }
            });
        });
    </script>
    
    /**
 * 获取要上传的文件大小
 * https://segmentfault.com/q/1010000008080489
 * @param element 需要配合jquery使用,$(上传文件的input)
 * @returns {*}
 */
function fileSize(element) {
    try {
        var fileSize = 0;
        // for IE
        if (window.ActiveXObject) {
            // before making an object of ActiveXObject,
            // please make sure ActiveX is enabled in your IE browser
            var objFSO = new ActiveXObject("Scripting.FileSystemObject");
            var filePath = element.get(0).value;
            var objFile = objFSO.getFile(filePath);
            fileSize = objFile.size; //size in kb
        }
        // for FF, Safari, Opeara and Others
        else {
            fileSize = element.get(0).files[0].size; //size in kb
        }

        //fileSize = fileSize / 1048576; //size in mb

        return fileSize;
    }
    catch (e) {
        return null;
    }
}

###http方式的git push需要密码

http协议不会记住也不会知道请求来自于谁,除非使用特殊方法,如cookie。因此对于那些需要授权的服务器,必须输入用户名和密码进行验证才能获取或推送数据,这样服务器才知道你是谁到底能不能获取或推送数据。
http协议的特点恰恰与ssh协议相反,ssh协议靠ssh key来识别你到底有没有权限推送或者获取数据,而ssh key保存在本地,如果你本地没有ssh key的话,当然是无法完成获取或推送数据的操作的。二者刚好形成互补对立的关系。
http协议
优点:省去了本地配置的麻烦,只要有URL和相应的权限便能进行相应的操作
缺点:每次操作都需要频繁验证,除非使用密码缓存机制git config --global credential.helper wincred
ssh协议
优点:推送或获取数据时不需要每次输入密码进行验证
缺点:在使用之前需要进行配置,并生成ssh key

###0.1 + 0.2 = 0.30000000000000004

在有限的存储空间下,绝大部分的十进制小数都不能用二进制浮点数来精确表示。例如,0.1 这个简单的十进制小数就不能用二进制浮点数来表示。
不用浮点数相加,让他们放大为整数相加,再缩小变为浮点数
// 不符合预期:
for(let i=0, j=100; i<j; i++) { console.log(i++ * 0.1); }
// 符合预期:
for(let i=0, j=100; i<j; i++) { console.log(i++ / 10); }
function add(v1, v2) {
  var r1, r2, m;
  try {
    r1 = v1.toString().split(".")[1].length; 
  } catch (e) {
    r1 = 0;
  }
  console.log('r1:', r1); // 获取v1剪切小数点的后的位数 "r1:" 1
  try {
    r2 = v2.toString().split(".")[1].length;
  } catch (e) {
    r2 = 0;
  }
  console.log('r2:', r2); // 获取v2剪切小数点的后的位数 "r2:" 1
  m = Math.pow(10, Math.max(r1, r2)); // 使用 Math.pow 获取倍数,10的r1、r2中取最大值次幂。
  console.log('m:', m); // "m:" 10
  return (v1 * m + v2 * m) / m;
}
var a = 0.1;
var b = 0.2;
console.log(add(a, b)); // 0.3
console.log(a + b); // 0.30000000000000004

###js闭包

for(var i=0;i<boxes.length;i++){
    boxes[i].index=i+1;
    boxes[i].onclick=function(){
        alert(this.index)
        }
}
for(var i=0;i<boxes.length;i++){
     (function(i){
                 boxes[i].onclick=function(){
                         alert(i+1);
                     }                    
                 })(i);    
}

###高并发下,每个请求返回 100 条不重复的记录

乐观锁,先用 php 从 mysql 读取出没执行过的最后 100 条记录

SELECT * FROM list WHERE State = '0' LIMIT 100

然后一条一条修改 State 改成 1 ,修改成功的,则是有效可用的,否则就是被其他线程抢先修改了

UPDATE list SET State = '1' WHERE Id='1' AND State = '0'
遇到并发数据竞争第一个想到的应该是 redis ,因为 redis 天生单线程无竞争

用悲观锁select * from list where state = 0 for update limit 100; 然后再把 state 更新为 1.
加个字段 pid , UPDATE 的时候,顺便把这 100 条数据打上进程的标记: 
'UPDATE `list` SET `State` = '1', `pid` = ' . getmypid() . ' WHERE `State` = '0' LIMIT 100;' 
锁定了之后,再: 
'SELECT * FROM `list` WHERE `pid` = ' . getmypid() . ' LIMIT 100' 
来拿到这 100 条数据

###Python2 中的 xrange 与 python3 中的 range

#python2

timeit.timeit('1000000000 in range(0,1000000000,10)', number=1)
5.50357640805305

timeit.timeit('1000000000 in xrange(0,1000000000,10)', number=1)
2.3025200839183526

# python3

import timeit
timeit.timeit('1000000000 in range(0,1000000000,10)', number=1)
4.490355838248402e-06

### Laravel 5.2 处理 Emoji 表情

https://github.com/unicodeveloper/laravel-emoji
databases.php 配置文件
没必要修改MySQL配置的,依次检查你的数据库、表、字段的字符集设置即可
 'mysql_utf8mb4' => [
            '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', 'pn_'),
            'strict' => false,
            'engine' => null,
        ],
	class Comment extends Model
{
    protected $connection = 'mysql_utf8mb4';
        protected $table = 'comment';
}

###PHP调用phantomjs

composer require "jonnyw/php-phantomjs:4.*"
$client = Client::getInstance();
//这一步非常重要,务必跟服务器的phantomjs文件路径一致
$client->getEngine()->setPath('/usr/local/bin/phantomjs');
$request  = $client->getMessageFactory()->createRequest();
$response = $client->getMessageFactory()->createResponse();

//设置请求方法
$request->setMethod('GET');
//设置请求连接
$request->setUrl($link);
//发送请求获取响应
$client->send($request, $response);

if($response->getStatus() === 200) {
    //输出抓取内容
    echo $response->getContent();
    //获取内容后的处理
}
$client = Client::getInstance();
$client->isLazy(); // 让客户端等待所有资源加载完毕

$request = $client->getMessageFactory()->createRequest();
$request->setTimeout(5000); // 设置超时时间(超过这个时间停止加载并渲染输出画面)

###Supervisor 管理 Laravel 队列进程

pip install supervisor
echo_supervisord_conf > /etc/supervisord.conf
[include]
files = /etc/supervisor/*.conf
supervisord -c /etc/supervisord.conf
sudo supervisorctl reread

sudo supervisorctl update

sudo supervisorctl start laravel-worker:*

###定时任务

app\Console\Kernerl.php
 protected function schedule(Schedule $schedule)
    {
        $schedule->call(function () {
            DB::table('test')->insert(['name'=>'test']);
        })->everyMinute();
    }
    * * * * * /usr/bin/php /www/artisan schedule:run >> /dev/null 2>&1

###图片拖动文本框自动上传

public function attachment(Request $request)
    {
        $file = $request->file('image');
        // 图片验证
        $input = array('image' => $file);
        $rules = array(
            'image' => 'image'
        );
        // 自动验证
        $validator = \Validator::make($input, $rules);
        // 失败处理
        if ($validator->fails()) return \Response::json([
            'error' => 'Please choose a picture.'
        ]);
        // 移动目录地址
        $destinationPath = 'uploads/';
        // 获取图片文件名
        $filename = \Auth::user()->id . '_' . time() . $file->getClientOriginalName();
        // 移动图片
        $file->move($destinationPath, $filename);

        return \Response::json([
            'filename' => '/' . $destinationPath . $filename
        ]);
    }

###sql数据去重问题

CREATE TABLE `message` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `sendid` int(11) NOT NULL DEFAULT '0',
  `receiveid` int(11) NOT NULL DEFAULT '0',
  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=latin1;

INSERT INTO `message` (`id`, `sendid`, `receiveid`, `create_time`)
VALUES
    (1, 321, 3, '2017-01-13 10:23:03'),
    (2, 322, 4, '2017-01-13 10:23:11'),
    (3, 123123, 9, '2017-01-13 10:23:25'),
    (4, 0, 0, '2017-01-13 10:22:54'),
    (5, 4, 321, '2017-01-13 10:22:54'),
    (6, 4, 322, '2017-01-13 10:23:17'),
    (7, 9, 12232, '2017-01-13 10:23:30'),
    (8, 0, 0, '2017-01-13 11:29:42');
    SELECT *
FROM   message m3
WHERE  id NOT IN (#查询需要去重的id
           select DISTINCT m1.id
           FROM            message AS m1
           INNER JOIN      message AS m2
           WHERE           m1.id != m2.id #过滤掉自身关联
           AND             ((
                                                           m1.receiveid = m2.sendid
                                           AND             m1.sendid = m2.receiveid)
                           OR              (
                                                           m1.sendid = m2.sendid
                                           AND             m1.receiveid = m2.receiveid ) )
           AND             m1.create_time < m2.create_time #
           GROUP BY        m1.id,
                           m2.id);    

###pthreads 模块无法在 web 模式下运行

创建两个配置文件(解决,web模式下添加 pthreads 扩展出错)。

php.ini        # web 模式下会自动加载
php-cli.ini    # php-cli 模式下回自动加载

###php5.6中捕获fatal error(E_ERROR)

function handle()
{
    $error = error_get_last();
    var_dump($error);
}

register_shutdown_function('handle');

hahah();

###PHP如何远程下载

$text = urlencode("苏醒的秘密.txt");
$url  = "http://dzs.qisuu.com/txt/" . $text;
echo file_get_contents($url);

###解析xml

$xml = file_get_contents("php://input");
$xml = new \SimpleXMLElement($xml);
$data = [];
        foreach ($xml as $key => $value) {
            $data[$key] = strval($value);
        }

###php单例模式

class test
{
    private $props = [];
    private static $instance;
    private function __construct()
    {
        echo 2;
    }

    public static function getInstance()
    {
        if( !( self::$instance instanceof self ) )
        {
            echo 1;
            self::$instance =new self();
        }
        return self::$instance;
    }

    private function __clone()
    {
    }

    public function setProp($key, $val)
    {
        $this->props[$key] = $val;
    }

    public function getProp($key)
    {
        return $this->props[$key];
    }
}
$a = test::getInstance(); //12
$b = test::getInstance(); //没有输出

$a->setProp("name", "zhangsan");
echo $b->getProp("name");   //zhangsan

###php bind

function bind(Callable $func)
{
    $args = func_get_args();
    array_shift($args);

    return function() use ($func, $args) {
        return call_user_func_array($func, array_merge($args, func_get_args()));
    };
}

function add($x, $y) {
    return $x + $y;
}

$add0 = bind('add');
var_dump($add0(1, 2));

$add1 = bind('add', 1);
var_dump($add1(2));

$add12 = bind('add', 1, 2);
var_dump($add12());

###PHP无限分类查找

function unlimitedForLayer($cate, $child_name = 'child' , $pid_name = 'pid' , $id_name = 'id',$pid = 0){
    $arr = array();
    foreach ($cate as $v){
        if ($v[$pid_name] == $pid){
            $v[$child_name] = unlimitedForLayer($cate,$child_name,$pid_name,$id_name,$v[$id_name]);
            $arr[] = $v;
        }
    }
    return $arr;
}
<?php

$search_type = array(
  array('id'=>1,'name'=>"一级a",'parent_id'=>0),
  array('id'=>2,'name'=>"一级b",'parent_id'=>0),
  array('id'=>3,'name'=>"二级a",'parent_id'=>1),
  array('id'=>4,'name'=>"二级b",'parent_id'=>1),
  array('id'=>5,'name'=>"二级c",'parent_id'=>1),
  array('id'=>6,'name'=>"三级a",'parent_id'=>5),
  array('id'=>7,'name'=>"三级b",'parent_id'=>5),
  array('id'=>8,'name'=>"四级a",'parent_id'=>7),
  array('id'=>9,'name'=>"三级c",'parent_id'=>3),
  array('id'=>10,'name'=>"四级b",'parent_id'=>7),
  array('id'=>11,'name'=>"三级d",'parent_id'=>2),
  array('id'=>12,'name'=>"四级c",'parent_id'=>11),
  array('id'=>13,'name'=>"四级d",'parent_id'=>11),
  array('id'=>14,'name'=>"三级e",'parent_id'=>2),
  array('id'=>15,'name'=>"三级f",'parent_id'=>14)
);

// 相当于 select * from search_type where parent_id = 0 的结果
function select_where_parent_id( $parent_id, $data_source ) {
    $results = array();
    foreach ($data_source as $value) {
        if( $value['parent_id'] == $parent_id ) {
            $results[] = $value;
        }
    }
    return $results;
}

$childs = array();

// &$childs : 结果对象引用
// $parent_id : 父节点id
// $search : 查询对象数据
// $index : 递推深度,方便控制多少层
function get_type(&$childs, $parent_id, $search, $index) {
    $result = select_where_parent_id( $parent_id, $search );
    if( !empty($result) ) {
        $childs[$parent_id] = $result;
        foreach ($result as $value) {
            get_type( $childs, $value['id'], $search, ++$index );
        }
    }
}


get_type($childs, 0, $search_type, 1);
echo json_encode($childs);

###golang版ss服务

go get github.com/shadowsocks/shadowsocks-go/cmd/shadowsocks-server

cd shadowsocks/

ls
    bin pkg src

cd bin/

shadowsocks-server -h # 查看帮助

vim config.json # 编写配置文件
    {
        "server":"127.0.0.1",
        "server_port":8388,
        "local_port":1080,
        "password":"xxxx",
        "method": "aes-128-cfb-auth",
        "timeout":600
    }

./shadowsocks-server > log & # 在后台运行ss服务

###唯一ID生成原理

// mysql 自增ID + 事务 + 时间 + 随机数
public function generateTradeNumber()
{
    $tradeTime = date('YmdHi', time());

    $lastTrade     = TradeNumber::findBySql('SELECT * FROM `Trade` ORDER BY id DESC LIMIT 1 FOR UPDATE');
    $lastTradeTime = '';
    if (!empty($lastTrade)) {
        $lastTradeNumber = $lastTrade->getTradeNumber();
        $lastTradeTime   = substr($lastTradeNumber, 0, 12);
        $lastTradeSerial = substr($lastTradeNumber, 12);
        if ($tradeTime == $lastTradeTime) {
            return $lastTradeTime . ($lastTradeSerial >= 99999 ? $lastTradeSerial + 1 : '0' . ($lastTradeSerial + 1));
        }
    }

    $initSerialNumber = rand(10000, 99999);
    return $tradeTime . '0' . $initSerialNumber;
}

###nginx proxy_pass

location ^~ /static/ 
{ 
# http://backup/。。。 不带location中的东西
# 只要proxy_pass后面有东西就不带location中的东西
proxy_pass http://www.test.com/; 
}
# location中的匹配路径为/static/。加了/之后proxy_pass 不会加上/static/
# curl http://localhost:3000/static/index.html
# proxy_pass 转发为 http://www.test.com/index.html

###MySQL timestamp 2038

$table->dateTime('created_at');
$table->dateTime('published_at');
 public $timestamps = false;
$safe_date = new DateTime('2040-02-01');

$user = new User;
$user->created_at = $safe_date->format('l j F Y H:i');
datetime 更像日历上面的时间和你手表的时间的结合,就是指具体某个时间。
timestamp 更适合来记录时间
timestamp 和 UNIX timestamps显示直观,出问题了便于排错,比好多很长的 int 数字好看多了 timestamp 是自带时区转换的
int 是从1970年开始累加的,如果之前的时间需要用负数支持

###0.1 + 0.2==0.30000000000000004

//http://0.30000000000000004.com/
(0.1).toString(2)
"0.0001100110011001100110011001100110011001100110011001101"
9007199254740992 + 1 = 9007199254740992
Math.pow(2,53)
9007199254740992
当结果大于 Math.pow(2, 53) 时,会出现精度丢失,导致最终结果存在偏差,而当结果大于 Number.MAX_VALUE,直接返回 Infinity
(0.1 * 10 + 0.2 * 10) / 10 
=> 0.3
2177.74*100
=> 217773.99999999997
先乘10的整数倍,然后再用toFixed进行四舍五入,这样能保证结果还是准确的http://www.cnblogs.com/lvdabao/p/5690173.html
(2177.74*100).toFixed(0); //217774
<a href="xxx.jpg" download="改名后的文件,jpg" />
算某天再过20天是几月几号new Date(2017, 6, 20+20);
计算2016年7月份有多少天new Date(2016, 7, 0).getDate(); //31

###PHP脚本执行卡住的问题排查记录

ps aux | grep 'php' | grep -v 'php-fpm'
[tabalt@localhost ~] sudo strace -p 13793
Process 13793 attached - interrupt to quit
[tabalt@localhost ~] sudo netstat -tunpa | grep 13793
tcp        0      0 192.168.1.100:38019        192.168.1.101:3306        ESTABLISHED 13793/php
tcp        0      0 192.168.1.100:47107        192.168.1.102:6379        CLOSE_WAIT  13793/php 
echo("start foreach\n");
foreach($types as $type)
{
    echo("foreach $type\n");
    $result[$type] = $this->getSites($type);
}
echo("end foreach\n"); 
//getSites方法 实现拿8个不重复的网址写了2个循环,如果结果中不重复的网址只有7个就会有一个空,少于7个就会有死循环!于是查了下type为2的网址个数,果然是只有6个!
$sites = array();   // 省略从数据库查询的代码
$siteNum = 8;       // 省略从配置读的代码
$urlKeys = $result = array();
for($i = 0; $i < $siteNum; $i++)
{
    do {
        $site = array_shift($sites);
        $urlKey = md5($site['url']);
    } while(array_key_exists($urlKey, $urlKeys));

    $urlKeys[$urlKey] = 1;
    $result[] = $site;
}
return $result;

###PHP中的Traits

当方法或属性同名时,当前类中的方法会覆盖 trait的 方法,而 trait 的方法又覆盖了基类中的方法。

trait Drive {
        public function hello() {
            echo "hello drive\n";
        }
        public function driving() {
            echo "driving from drive\n";
        }
    }
    class Person {
        public function hello() {
            echo "hello person\n";
        }
        public function driving() {
            echo "driving from person\n";
        }
    }
    class Student extends Person {
        use Drive;
        public function hello() {
            echo "hello student\n";
        }
    }
    $student = new Student();
    $student->hello();//hello student

    $student->driving();//driving from drive
使用insteadof和as操作符来解决冲突,insteadof是使用某个方法替代另一个,而as是给方法取一个别名
<?php
trait Trait1 {
    public function hello() {
        echo "Trait1::hello\n";
    }
    public function hi() {
        echo "Trait1::hi\n";
    }
}
trait Trait2 {
    public function hello() {
        echo "Trait2::hello\n";
    }
    public function hi() {
        echo "Trait2::hi\n";
    }
}
class Class1 {
    use Trait1, Trait2 {
        Trait2::hello insteadof Trait1;
        Trait1::hi insteadof Trait2;
    }
}
class Class2 {
    use Trait1, Trait2 {
        Trait2::hello insteadof Trait1;
        Trait1::hi insteadof Trait2;
        Trait2::hi as hei;
        Trait1::hello as hehe;
    }
}
$Obj1 = new Class1();
$Obj1->hello();
$Obj1->hi();
echo "\n";
$Obj2 = new Class2();
$Obj2->hello();
$Obj2->hi();
$Obj2->hei();
$Obj2->hehe();

###PHP开发APP微信支付接口

/**
 * 生成随机数并返回
 */
private function getNonceStr() {
    $code = "";
    for ($i=0; $i > 10; $i++) { 
        $code .= mt_rand(1000);        //获取随机数
    }
    $nonceStrTemp = md5($code);
    $nonce_str = mb_substr($nonceStrTemp, 5,37);      //MD5加密后截取32位字符
    return $nonce_str;
}

/**
 * 获取参数签名;
 * @param  Array  要传递的参数数组
 * @return String 通过计算得到的签名;
 */
private function getSign($params) {
    ksort($params);        //将参数数组按照参数名ASCII码从小到大排序
    foreach ($params as $key => $item) {
        if (!empty($item)) {         //剔除参数值为空的参数
            $newArr[] = $key.'='.$item;     // 整合新的参数数组
        }
    }
    $stringA = implode("&", $newArr);         //使用 & 符号连接参数
    $stringSignTemp = $stringA."&key=".$this->key;        //拼接key
                                         // key是在商户平台API安全里自己设置的
    $stringSignTemp = MD5($stringSignTemp);       //将字符串进行MD5加密
    $sign = strtoupper($stringSignTemp);      //将所有字符转换为大写
    return $sign;
}
/**
 * 构造函数,初始化成员变量
 * @param  String $appid  商户的应用ID
 * @param  Int $mch_id 商户编号
 * @param String $key 秘钥
 */
// 将构造函数设置为私有,禁止用户实例化该类
private function __construct($appid, $mch_id, $key) {
    if (is_string($appid) && is_string($mch_id)) {
        $this->appid = $appid;
        $this->mch_id = $mch_id;
        $this->key = $key;
    }
}

/**
 * 获取微信支付类实例
 * 该类使用单例模式
 * @return WeEncryption         本类实例
 */
public static function getInstance() {
    if(self::$instance == null) {
        self::$instance = new Self(APPID, MCHID, APP_KEY);
    }
    return self::$instance;
}
public function setNotifyUrl($url) {
    if (is_string($url)) {
        $this->notify_url = $url;
    }
}
/**
 * 拼装请求的数据
 * @return  String 拼装完成的数据
 */
private function setSendData($data) {
    $this->sTpl = "<xml>
                        <appid><![CDATA[%s]]></appid>
                        <body><![CDATA[%s]]></body>
                        <mch_id><![CDATA[%s]]></mch_id>
                        <nonce_str><![CDATA[%s]]></nonce_str>
                        <notify_url><![CDATA[%s]]></notify_url>
                        <out_trade_no><![CDATA[%s]]></out_trade_no>
                        <spbill_create_ip><![CDATA[%s]]></spbill_create_ip>
                        <total_fee><![CDATA[%d]]></total_fee>
                        <trade_type><![CDATA[%s]]></trade_type>
                        <sign><![CDATA[%s]]></sign>
                    </xml>";                          //xml数据模板

    $nonce_str = $this->getNonceStr();        //调用随机字符串生成方法获取随机字符串

    $data['appid'] = $this->appid;
    $data['mch_id'] = $this->mch_id;
    $data['nonce_str'] = $nonce_str;
    $data['notify_url'] = $this->notify_url;
    $data['trade_type'] = $this->trade_type;      //将参与签名的数据保存到数组
    // 注意:以上几个参数是追加到$data中的,$data中应该同时包含开发文档中要求必填的剔除sign以外的所有数据
    $sign = $this->getSign($data);        //获取签名

    $data = sprintf($this->sTpl, $this->appid, $data['body'], $this->mch_id, $nonce_str, $this->notify_url, $data['out_trade_no'], $data['spbill_create_ip'], $data['total_fee'], $this->trade_type, $sign);
    //生成xml数据格式
    return $data;
}
/**
 * 发送下单请求;
 * @param  Curl   $curl 请求资源句柄
 * @return mixed       请求返回数据
 */
public function sendRequest(Curl $curl, $data) {
    $data = $this->setSendData($data);            //获取要发送的数据
    $url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
    $curl->setUrl($url);          //设置请求地址
    $content = $curl->execute(true, 'POST', $data);       //执行该请求
    return $content;      //返回请求到的数据
}
/**
     * 将XML数据转换为对象
     * @param $xml  XmlTransfer
     * @return array
     */
    public function xml2Array($xml)
    {
        $obj = null;
        if (is_string($xml) && !empty($xml)) {
            $obj = simplexml_load_string($xml, "SimpleXMLElement", LIBXML_NOCDATA);
        }
        return (Array)$obj;
    }

    /**
     * 将数组转换成XML
     * @param $data
     * @return string
     */
    public function array2XML($data)
    {
        $xmlString = "";
        if (is_array($data) && !empty($data)) {
            $xmlString .= "<xml>";
            foreach ($data as $tag => $node)
            {
                $xmlString .= "<".$tag."><![CDATA[".$node."]]></".$tag.">";
            }
            $xmlString .= "</xml>";
        }
        return $xmlString;
    }
    $obj = $encpt->getNotifyData();     // 接收数据对象
    
    if ($obj) {
    $data = array(
        'appid'                =>    $obj->appid,
        'mch_id'            =>    $obj->mch_id,
        'nonce_str'            =>    $obj->nonce_str,
        'result_code'        =>    $obj->result_code,
        'openid'            =>    $obj->openid,
        'trade_type'        =>    $obj->trade_type,
        'bank_type'            =>    $obj->bank_type,
        'total_fee'            =>    $obj->total_fee,
        'cash_fee'            =>    $obj->cash_fee,
        'transaction_id'    =>    $obj->transaction_id,
        'out_trade_no'        =>    $obj->out_trade_no,
        'time_end'            =>    $obj->time_end
        );
                // 拼装数据进行第三次签名
    $sign = $encpt->getSign($data);        // 获取签名

    /** 将签名得到的sign值和微信传过来的sign值进行比对,如果一致,则证明数据是微信返回的。 */
    if ($sign == $obj->sign) {
        $reply = "<xml>
                    <return_code><![CDATA[SUCCESS]]></return_code>
                    <return_msg><![CDATA[OK]]></return_msg>
                </xml>";
        echo $reply;      // 向微信后台返回结果。
        exit;
    }
}

###过滤微信昵称中的emoji表情

MySQL的utf8编码只支持3个字节的长度,而emoji的编码多为4个字节甚至6个字节的长度https://github.com/hidehalo/Emoji 
preg_replace("#(\\\ud[0-9a-f]{3})|(\\\ue[0-9a-f]{3})#ie",$nick_name)
function remove_emoji($text) {
  // Match Emoticons
  $regex_emoticons = '/[\x{1F600}-\x{1F64F}]/u';

  // Match Miscellaneous Symbols and Pictographs
  $regex_symbols = '/[\x{1F300}-\x{1F5FF}]/u';

  // Match Transport And Map Symbols
  $regex_transport = '/[\x{1F680}-\x{1F6FF}]/u';
  
  // Match flags (iOS)
  $regex_flags = '/[\x{1F1E0}-\x{1F1FF}]/u';

  return preg_replace(array($regex_emoticons, $regex_symbols, $regex_transport, $regex_flags), '', $text);
}
function remove_emoji2($text) {
  return preg_replace('/[\x{1F600}-\x{1F64F}]|[\x{1F300}-\x{1F5FF}]|[\x{1F680}-\x{1F6FF}]|[\x{1F1E0}-\x{1F1FF}]/u', '', $text);
}
$str = "无敌[email protected]😄😊";
echo remove_emoji2($str);//无敌[email protected]

###PDOStatement::bindParam的一个陷阱

$dbh = new PDO('mysql:host=localhost;dbname=test', "test");

$query = <<<QUERY
  INSERT INTO `user` (`username`, `password`) VALUES (:username, :password);
QUERY;
$statement = $dbh->prepare($query);

$bind_params = array(':username' => "laruence", ':password' => "weibo");
foreach( $bind_params as $key => $value ){
    $statement->bindParam($key, $value);
}
$statement->execute();

最终执行的SQL是:

INSERT INTO `user` (`username`, `password`) VALUES ("weibo", "weibo");
//第一次循环
$value = $bind_params[":username"];
$statement->bindParam(":username", &$value); //此时, :username是对$value变量的引用

//第二次循环
$value = $bind_params[":password"]; //oops! $value被覆盖成了:password的值
$statement->bindParam(":password", &$value);
1.不要使用foreach, 而是手动赋值
bindParam和bindValue的不同之处, bindParam要求第二个参数是一个引用变量(reference).
<?php
$statement->bindParam(":username", $bind_params[":username"]); //$value是引用变量了
$statement->bindParam(":password", $bind_params[":password"]);
2.使用bindValue代替bindParam, 或者直接在execute中传递整个参数数组.
3.使用foreach和reference(不推荐, 原因参看:微博)

<?php
foreach( $bind_params as $key => &$value ) { //注意这里
    $statement->bindParam($key, $value);
}

###复制对象时的深copy和浅copy

public function __clone() {
    $this->balance = clone $this->balance;
}

###PHP实现搜索附近的人

/**
 * 根据经纬度和半径计算出范围
 * @param string $lat 经度
 * @param String $lng 纬度
 * @param float $radius 半径
 * @return Array 范围数组
 */
private function calcScope($lat, $lng, $radius) {
    $degree = (24901*1609)/360.0;
    $dpmLat = 1/$degree;

    $radiusLat = $dpmLat*$radius;
    $minLat = $lat - $radiusLat;       // 最小经度
    $maxLat = $lat + $radiusLat;       // 最大经度

    $mpdLng = $degree*cos($lat * (PI/180));
    $dpmLng = 1 / $mpdLng;
    $radiusLng = $dpmLng*$radius;
    $minLng = $lng - $radiusLng;      // 最小纬度
    $maxLng = $lng + $radiusLng;      // 最大纬度

    /** 返回范围数组 */
    $scope = array(
        'minLat'    =>  $minLat,
        'maxLat'    =>  $maxLat,
        'minLng'    =>  $minLng,
        'maxLng'    =>  $maxLng
        );
    return $scope;
}
/**
 * 根据经纬度和半径查询在此范围内的所有的电站
 * @param  String $lat    经度
 * @param  String $lng    纬度
 * @param  float $radius 半径
 * @return Array         计算出来的结果
 */
public function searchByLatAndLng($lat, $lng, $radius) {
    $scope = $this->calcScope($lat, $lng, $radius);        // 调用范围计算函数,获取最大最小经纬度
    /** 查询经纬度在 $radius 范围内的电站的详细地址 */
    $sql = 'SELECT `字段` FROM `表名` WHERE `Latitude` < '.$scope['maxLat'].' and `Latitude` > '.$scope['minLat'].' and `Longitude` < '.$scope['maxLng'].' and `Longitude` > '.$scope['minLng'];

    $stmt = self::$db->query($sql);
    $res = $stmt->fetchAll(PDO::FETCH_ASSOC);       // 获取查询结果并返回
    return $res;
}
/**
 * 获取两个经纬度之间的距离
 * @param  string $lat1 经一
 * @param  String $lng1 纬一
 * @param  String $lat2 经二
 * @param  String $lng2 纬二
 * @return float  返回两点之间的距离
 */
public function calcDistance($lat1, $lng1, $lat2, $lng2) {
    /** 转换数据类型为 double */
    $lat1 = doubleval($lat1);
    $lng1 = doubleval($lng1);
    $lat2 = doubleval($lat2);
    $lng2 = doubleval($lng2);
    /** 以下算法是 Google 出来的,与大多数经纬度计算工具结果一致 */
    $theta = $lng1 - $lng2;
    $dist = sin(deg2rad($lat1)) * sin(deg2rad($lat2)) +  cos(deg2rad($lat1)) * cos(deg2rad($lat2)) * cos(deg2rad($theta));
    $dist = acos($dist);
    $dist = rad2deg($dist);
    $miles = $dist * 60 * 1.1515;
    return ($miles * 1.609344);
}

###chrome浏览器表单自动填充存在的隐患

1、input类型即type="text" 改成type="hidden";
2、使用样式将input隐藏起来 如 <input type="text" style="display:none;">
3、使用样式将input隐藏起来 如 <input type="text" style="visibility:hide;">
下面这三种方式目前在最新版本的chrome浏览已经不存在此问题

###MySQL排序原理与案例分析

可以利用索引避免排序的SQL

SELECT * FROM t1 ORDER BY key_part1,key_part2;

SELECT * FROM t1 WHERE key_part1 = constant ORDER BY key_part2;

SELECT * FROM t1 WHERE key_part1 > constant ORDER BY key_part1 ASC;

SELECT * FROM t1 WHERE key_part1 = constant1 AND key_part2 > constant2 ORDER BY key_part2;
不能利用索引避免排序的SQL

//排序字段在多个索引中,无法使用索引排序

SELECT * FROM t1 ORDER BY key_part1,key_part2, key2;

 

//排序键顺序与索引中列顺序不一致,无法使用索引排序

SELECT * FROM t1 ORDER BY key_part2, key_part1;

 

//升降序不一致,无法使用索引排序

SELECT * FROM t1 ORDER BY key_part1 DESC, key_part2 ASC;

 

//key_part1是范围查询,key_part2无法使用索引排序

SELECT * FROM t1 WHERE key_part1> constant ORDER BY key_part2;

###动态修改php的配置项

程序每次执行都自动加载一个header.php
是apache+php的组合,我们可以在apache的配置文件中加入如下指令即可。


Php_value auto_prepend_file /home/www/bo56.com/header.php
如果是nginx+php组合,可以加入如下指令

fastcgi_param PHP_VALUE "auto_prepend_file=/home/www/bo56.com/header.php";
注意,nginx中多次使用 PHP_VALUE时,最后的一个会覆盖之前的。如果想设置多个配置项,需要写在一起,然后用换行分割。如:

fastcgi_param PHP_VALUE "auto_prepend_file=/home/www/bo56.com/header.php \n auto_append_file=/home/www/bo56.com/external/footer.php";

###sql语句中的通配符

select en_name from action_conf where en_name like 'exp_site_10_%'
select en_name from action_conf where en_name like 'exp\_site\_10\_%'
% 替代一个或多个字符
_ 仅替代一个字符
[charlist] 字符列中的任何单一字符
[^charlist]或[!charlist] 不在字符列中的任何单一字符

###个echo引起的进程崩溃

sleep(50);
echo "aaa\n";
file_put_contents("/tmp/test.txt",time());
$ php test.php &

$ php test.php > /dev/null 2 >&1 &
$ nohup php test.php &

###php实现并发处理之curl篇

/*
 * @purpose: 使用curl并行处理url
 * @return: array 每个url获取的数据
 * @param: $urls array url列表
 * @param: $callback string 需要进行内容处理的回调函数。示例:func(array)
 */
function curl($urls = array(), $callback = '')
{
    $response = array();
    if (empty($urls)) {
        return $response;
    }
    $chs = curl_multi_init();
    $map = array();
    foreach($urls as $url){
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_TIMEOUT, 1);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_HEADER, 0);
        curl_setopt($ch, CURLOPT_NOSIGNAL, true);
        curl_multi_add_handle($chs, $ch);
        $map[strval($ch)] = $url;
    }
    do{
        if (($status = curl_multi_exec($chs, $active)) != CURLM_CALL_MULTI_PERFORM) {
            if ($status != CURLM_OK) { break; } //如果没有准备就绪,就再次调用curl_multi_exec
            while ($done = curl_multi_info_read($chs)) {
                $info = curl_getinfo($done["handle"]);
                $error = curl_error($done["handle"]);
                $result = curl_multi_getcontent($done["handle"]);
                $url = $map[strval($done["handle"])];
                $rtn = compact('info', 'error', 'result', 'url');
                if (trim($callback)) {
                    $callback($rtn);
                }
                $response[$url] = $rtn;
                curl_multi_remove_handle($chs, $done['handle']);
                curl_close($done['handle']);
                //如果仍然有未处理完毕的句柄,那么就select
                if ($active > 0) {
                    curl_multi_select($chs, 0.5); //此处会导致阻塞大概0.5秒。
                }
            }
        }
    }
    while($active > 0); //还有句柄处理还在进行中
    curl_multi_close($chs);
    return $response;
}
 
//使用方法
function deal($data){
    if ($data["error"] == '') {
        echo $data["url"]." -- ".$data["info"]["http_code"]."\n";
    } else {
        echo $data["url"]." -- ".$data["error"]."\n";
    }
}
$urls = array();
for ($i = 0; $i < 10; $i++) {
    $urls[] = 'http://www.baidu.com/s?wd=etao_'.$i;
    $urls[] = 'http://www.so.com/s?q=etao_'.$i;
    $urls[] = 'http://www.soso.com/q?w=etao_'.$i;
}
curl($urls, "deal");

###正则检测是否为utf8编码

function is_utf8($string) {
    return preg_match('%^(?:
            [\x09\x0A\x0D\x20-\x7E] # ASCII
            | [\xC2-\xDF][\x80-\xBF] # non-overlong 2-byte
            | \xE0[\xA0-\xBF][\x80-\xBF] # excluding overlongs
            | [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2} # straight 3-byte
            | \xED[\x80-\x9F][\x80-\xBF] # excluding surrogates
            | \xF0[\x90-\xBF][\x80-\xBF]{2} # planes 1-3
            | [\xF1-\xF3][\x80-\xBF]{3} # planes 4-15
            | \xF4[\x80-\x8F][\x80-\xBF]{2} # plane 16
    )*$%xs', $string);
}
$str = "牛皮凉鞋";
var_dump(is_utf8($str));

###apc可能导致php-fpm罢工

php中集成了一个phpdbg的工具。可以像gdb调试c语言程序一样 http://phpdbg.com/docs
ps aux |grep php
sudo pstack 11740
 sudo gdb -p 26748

###字符串数字比较

当比较的一方是数字时,字符串会转换成数字,然后再进行比较。

如果比较的两方全部为字符串时,当然就不存在转换,只是单纯的进行字符串比较了。

需要注意的是,字符串转换成数字时,如果字符串被视为十进制格式时,大概的转换规则如下:

1.过滤前面的一些字符。这些字符包括 空格,'\t''\n' , '\r' ,'\v' , '\f'0

2.把后面不是数字的字符和之后字符也过滤掉。如 123abc4 就会把a和之后的字符过滤掉。
$key=1;
var_dump($key == " \t01ab");//true
	$key='1';
var_dump($key == " \t01ab");//false
$key=0;
var_dump($key == " \t01ab");//false

$key=0;
var_dump($key == " \t00ab");//true
$key=0;
var_dump($key == " ab");//true
$key='0';
var_dump($key == " ab");//false

###决crond脚本执行并发冲突

之前执行的test.php还未结束,新的test.php又被执行
$lockfile = '/tmp/mytest.lock';  
   
if(file_exists($lockfile)){  
    exit();  
}
file_put_contents($lockfile, date("Y-m-d H:i:s"));
   
sleep(70);
 
unlink($lockfile);
$fp = popen("ps aux | grep 'test.php' | wc -l", "r");
$proc_num = fgets($fp);
if ($proc_num > 3) {
    exit;
}
sleep(70);

###php中的register_shutdown_function和fastcgi_finish_request

function catch_error(){
    $error = error_get_last();
    if($error){
        var_dump($error);
    }
}
register_shutdown_function("catch_error");
ini_set('memory_limit','1M');
$content = str_repeat("aaaaaaaaaaaaaaaaaaaaaaa",100000);
echo "aa";