• 2018-05-31

    PHP 获取周一,上个月的正确做法

    Views: 27603 | 3 Comments

    如果你理所当然地认为在 PHP 中获取周一,上周一,上个月,下个月这些时间能简单地用 strtotime() 函数来获取的话,那你就大错特错了!

    $now = strtotime('2018-01-31');
    echo date('Y-m-d', strtotime('+1 month', $now)) . "\n";
    

    输出的结果是:

    2018-03-03
    

    并不是期望的返回2月份的某一天,而返回了错误的3月份。为什么呢?这和 strtotime() 的实现原理有关:

    • 将月份加1,就变成了 2018-02-31
    • 因为2月没有31号,所以修正,从 2018-02-28 再往前加3天

    类似的,星期相关的代码也有问题。

    $now = strtotime('2018-05-20'); // 这一天是周日
    echo date('Y-m-d', strtotime('this monday', $now)) . "\n";
    echo date('Y-m-d', strtotime('+0 monday', $now)) . "\n";
    

    输出的结果是:

    2018-05-21
    2018-05-21
    

    +0 monday 和 +1 monday 的结果是一样的,并不能将这种格式理解为本周一和下周一(见评论),特别容易出错的是,你可能会错误地认为 +1 monday 是下周一,但事实不是。要得到下周N,或者下下周N,需要先求出本周一。本周一的求法如下:

    $now = strtotime('2018-05-20');
    $time = $now - 86400 * (date('N', $now) - 1);
    echo date('Y-m-d', $time) . "\n"; // 2018-05-14
    

    然后再用本周一加减N个7天,得出前后的周一,之后再根据周一求周N。还有,"+n monday" 和 "-n monday" 的结果和期望不同,其处理逻辑不一致。结合评论,+n xxx 是从当天(含)开始找,-n xxx 是从前一天开始找, +-0 按 +1 处理。

    一句话总结:PHP strtotime() 求上周,下周,上个月,下个月等,不能简单处理,需要特殊处理,都需要先求出本周一或者本周一号,再手动处理,而且星期的处理应该在求出本周一之后用时间戳整数运算。

    Posted by ideawu at 2018-05-31 19:09:33
  • 2016-08-19

    集成于 iphp 框架的 PHP 并发模型和工具

    Views: 26810 | 2 Comments

    由于 PHP 具有"所思即所写"的强大优势, 使其在 Web 之外, 也被广泛用于后台脚本编写. 而且, 当你已经用 PHP 来开发 Web 应用时, 你显然不愿意再引入 Java 或者 Python 等语言, 再说, 这些语言相对 PHP 的强大优势, 显示太弱了.

    不过, 用 PHP 来编写后台脚本, 也有一些劣势, 那便是 PHP 缺少并发模型. 例如, 当你用 HTTP 请求第三方服务, 而第三方每一个请求要处理 10 秒时, 这就需要你并发地发起请求. curl_multi 当然能处理, 但不通用, 而且对代码逻辑的改变太大, 需要从串行化改为批量化, 对思维干扰太大.

    为此, PHP 提供了一套 Master-Workers 并发模型, 帮助你实现 PHP 的并发处理.

    Continue reading »

    Posted by ideawu at 2016-08-19 14:29:36
  • 2016-06-23

    PHP过滤掉Emoji表情字符

    Views: 22621 | 1 Comment

    这段代码在 stackoverflow 上搜到, 据说是 Smarty 用的.

    function smarty_modifier_emojistrip($string)
    {       
        return preg_replace('/\xEE[\x80-\xBF][\x80-\xBF]|\xEF[\x81-\x83][\x80-\xBF]/', '', $string);
    }
    
    Posted by ideawu at 2016-06-23 15:53:56
  • 2016-02-25

    PHP 用 curl 读取 HTTP chunked 数据

    Views: 20150 | No Comments

    对于 Web 服务器返回的 HTTP chunked 数据, 我们可能希望在每一个 chunk 返回时得到回调, 而不是所有的响应返回后再回调. 例如, 当服务器是 icomet 的时候.

    在 PHP 中使用 curl 代码如下:

    <?php  
    $url = "http://127.0.0.1:8100/stream";
    
    $ch = curl_init($url);
    curl_setopt($ch, CURLOPT_WRITEFUNCTION, 'myfunc');
    $result = curl_exec($ch);
    curl_close($ch);
    
    function myfunc($ch, $data){
        $bytes = strlen($data);
        // 处理 data
        return $bytes;
    }
    

    但是, 这里有一个问题. 对于一个 chunk, 回调函数可能会被调用多次, 每一次大概是 16k 的数据. 这显然不是我们希望得到的. 因为 icomet 的一个 chunk 是以 "\n" 结尾, 所以回调函数可以做一下缓冲.

    function myfunc($ch, $data){
        $bytes = strlen($data);
        static $buf = '';
        $buf .= $data;
        while(1){
            $pos = strpos($buf, "\n");
            if($pos === false){
                break;
            }
            $data = substr($buf, 0, $pos+1);
            $buf = substr($buf, $pos+1);
    
            // 处理 data
        }
    }
    
    Posted by ideawu at 2016-02-25 16:44:11
  • 2016-02-16

    iphp 框架增加 lazyload 特性

    Views: 15302 | No Comments

    基类中为了方便原则, 一般会加载所有可能用到的属性到 Context 中, 但这会导致性能问题, 所以需要 lazyload 机制. 例如, 某些属性只有在子类中被用到时, 才会真正地去查询数据库, 如果子类中不使用这些属性, 则不会发生数据库请求.

    原型:

    Context::lazyload($name, callable $callback_func);
    

    示例:

    class AppController extends Controller
    {
        function init($ctx){
            parent::init($ctx);
            $ctx->lazyload('account', array($this, 'ctx_lazyload'));
        }
    
        private function ctx_lazyload($name, $ctx){
            if($name == 'account'){
                return Account::get($ctx->user->id);
            }
        }
    }
    
    Posted by ideawu at 2016-02-16 11:41:54
  • 2015-05-07

    PHP 解码 C 字符串

    Views: 27574 | 2 Comments

    有时候, 需要将 C 语言字符串字面量编码的字符串转换成其在内存中实际的值, 如果用 Python, 是有内置的函数, 但 PHP 没有, 只能写一个.

    C 语言字符串字面量编码, 就是类似:

    \r\n\x90
    

    它们在内存中肯定不是这样.

    完整代码如下, 需要 PHP 5.4+.

    function unescape_c_str($str){
        $ret = '';
        $len = strlen($str);
        for($i=0; $i<$len; $i++){
            if($str[$i] != '\\'){
                $ret .= $str[$i];
                continue;
            }
            $i++;
            switch($str[$i]){
                case 's':
                    $ret .= ' ';
                    break;
                case 'a':
                    $ret .= "\a";
                    break;
                case 'b':
                    $ret .= "\b";
                    break;
                case 'f':
                    $ret .= "\f";
                    break;
                case 'v':
                    $ret .= "\v";
                    break;
                case 'r':
                    $ret .= "\r";
                    break;
                case 'n':
                    $ret .= "\n";
                    break;
                case 't':
                    $ret .= "\t";
                    break;
                case '\\':
                    $ret .= "\\";
                    break;
                case 'x':
                    $hex = substr($str, $i+1, 2);
                    $ret .= hex2bin($hex);
                    $i += 2;
                    break;
                default:
                    $ret .= $str[$i];
                    break;
            }
        }
        return $ret;
    }
    
    Posted by ideawu at 2015-05-07 12:51:28
|<<<123456789>>>| 1/10 Pages, 55 Results.