标签: php

  • HTML5跨域开发

    HTML5跨域开发

    HTML5中提供了跨域加载数据的方法,让我们得以从JSONP或者Flash中介等各种绕行方案中解脱出来,更加顺畅地与服务器交流。另一方面,因为PHP是最好的语言……所以在它与Node.js之间,我选择前者作为后端语言开发内容服务。

    这篇文章记录使用jQuery+PHP开发跨域应用时的小心得。

    身份验证

    做身份验证,最简单的办法就是使用PHP的SESSION保存用户信息,于是就要用到Cookie。默认情况下,跨域Ajax请求发起时候不包含Cookie,需要我们主动将XHRwithCredentials属性设为true才行。

    jQuery会把XHR封装成jqXHR,并且不暴露真正的XHR(说实话这点有点难以理解,尤其是在做上传进度条的时候)。然后它提供一个给真正XHR赋值的接口xhrField,所以写成代码就是这样事儿的:

    $.ajax(url, {
      xhrField: {
        withCredentials: true
      }
    }
    

    各种HTTP头

    如果不需要验证用户身份,直接在HTTP头中输出Access-Control-Allow-Origin: *即可。

    我的产品需要验证,那么首先,HTTP头中必须有Access-Control-Allow-Credentials: true;此时对域的限制也严格许多,不再允许像前面那样使用*放开给任何来源,必须指明哪个具体域可以接受。

    关于Access-Control-Allow-Origin的值,规范中的说明是“域名列表或null”,然则接下来的“注意”有点诡异:“实际生产中,‘列表或null’要求更严格。你可以认为它实际只允许单一域名或null,而非空格分隔的域名列表。”——既然如此你干脆写个“域名或null”不就完了……

    总之对于我们而言,返回的HTTP头中还要包含Access-Control-Allow-Origin: http://域名,指定允许作为来源的协议、域名、端口,并且只能有一个(组)。因为通常来说我们开发环境和生产环境不一样,所以这里的域名最好不要写在服务器配置里;使用PHP,通过$_SERVER['HTTP_ORIGIN']取出访问来源,与白名单比对,通过后再输出相应的头,更加合适。

    调试

    我选择JSON作为前后端交流的格式。为了方便浏览器解析(也是HTML5的要求),我还返回了Content-type: application/json头。

    使用PHP少不了使用Xdebug。出现错误时,Xdebug会返回完整的栈,有利排查。但是为了方便阅读,Xdebug还会给返回信息套上<table>结构,这时Chrome的Network工具就会把它解析成奇怪的格式,所以Content-type一定要最后和数据一起返回。

    与之相反的是前文说到的Access-Control-Allow-OriginAccess-Control-Allow-Credentials,这二位必须放在最前面。不然如果出现500错,响应头不包含这两个跨域标记,Chrome就会理所当然地不显示返回内容,也就无法看到错误描述,根本无法排查。

    参考资料

    1. Using CORS
    2. Cross-Origin Resource Sharing
    3. HTTP access control (CORS)
    4. jQuery.ajax()
  • composer导致没有log的错误

    composer导致没有log的错误

    我一般使用git pull从Github更新代码。某次提交新版本后,服务器开始报500错误,但是看log什么都没有。反复回想好像上次拉完代码没有composer dump-autoload,而且这个版本确实增加了几个类。于是赶紧生成autoload,故障解除。

  • Yosemite坑真多

    本着顶配解千愁的指导原则,入职2年半之后,借着南迁广州的机会,我向公司申请购置一批新机器。同配置笔记本性能远远落后于台式机,所以我自然而然的选择了顶配iMac 27作为主力开发机。升级CPU和内存的版本没有现货,等机器送到,大约是10月13日。

    关注IT产品尤其是苹果产品的同学可能记得,16日苹果开发布会,推出视网膜屏的iMac,连CPU带显卡甚至屏幕都有大幅升级……顶配的日子只持续了3天……哭昏在厕所……

    这还不是最惨的。更早的时候,苹果发布了Mac OS 10.10优胜美地(Yosemite),直接安装在新机器里,我这台自然也是。新系统有诸多纸面所载的改变,我就不多说了,跟本文关系不大;我只说跟开发相关的这部分。

    403错误

    升级后的Mac OS集成Apache 2.4.9,里面默认启用了authz模块,于是增加虚拟主机的时候,<DIERECTORY>配置里面必须写成

    Require all granted

    不然就会报403错误。我以前处理403都是暴力改权限,这次恨不得把/的权限都改了都没用,后来终于google到解决方案。

    不能使用Homebrew安装的php,以及不能用Homebrew升级php

    我有新版本癖,每周三次更新——使用开源产品的好处就是绝对不怕找不到更新。当我上周例行更新,发现PHP 5.5.19安装失败,报Cannot find OpenSSL's <evp>,因为项目紧急所以我只好先不管它。后面配置apache的时候,发现随Homebrew安装的新版libphp5.so不能用,apachectl -t 检查通不过,只好使用系统自带的5.5.14版。

    今天又google了下,终于找到解决方案。原因没搞太明白,总之是升级了之后 /usr/include 有点问题,需要重新链接一下。正确的解决方案是

    1. 安装XCode最新版
    2. 安装XCode命令行工具(CLT)最新版
    3. 试一下,不行的话重新链接一下 /usr/include
      sudo ln -s /Applications/Xcode6-Beta.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.10.sdk/usr/include /usr/include
    4. 然后 `brew upgrade` 就可以了

    这样搞了之后新的5.5.19的libphp5.so也可以使用了。

    丢失PNG支持

    如果只是版本低一点,其实是可以忍的。结果Mac OS团队不知道怎么想的,竟然把PHP中的PNG支持去掉了,导致我在本地开发的时候验证码出不来。如前文所述,这次升级本来就有诸多问题,验证码出不来我的第一想法肯定不是看PNG支持。而且,我们一直在用的验证码生成程序中,将生成函数写成 ImagePNG,竟然能运行,也是让我很奇怪……

    换用Homebrew安装的5.5.19 libphp5.so之后,问题解决。


    暂时遇到这么多问题,日后补充。

  • MySQL 同表复制数据

    MySQL 同表复制数据

    我觉得再这么下去,我真敢说我写过PHP了……

    需求很简单,在同一个表中复制数据。以前的代码是在PHP里先 select *,然后 extract 成变量,再组合成一个大 sql,最后插入。我觉得这样不好,首先要执行两次 sql,其次写那么一大篇 sql 也挺麻烦的。于是研究了下,发现并不复杂,这里总结一下:

    如果是从别的表里导入数据,可以这样写:

    INSERT INTO `table`
    SELECT *
    FROM `table2`
    WHERE `id`=1
    

    如果是同表,并且表里没有主键,这样也好使;但是有主键的话,会被告知主键重复,这个时候就只能把字段都写出来了:

    # 假设表结构为 id, col1, col2, col3
    INSERT INTO `table`
    SELECT NULL, col1, col2, col3
    FROM `table`
    WHERE `id`=1
    

    这里字段的顺序很重要,要参照表的顺序来写(我是用 MySQL Wordbench 连上库,然后用Alert table 看的);不过好处在于,如果我们需要更改其中某个字段的值,只要在 sql 里直接写就好。比如我们复制后想交换后两个字段,或者改变某个字段的值,就可以这样:

    # 假设表结构为 id, name, age, sex
    # 交换age和sex
    INSERT INTO `table`
    SELECT NULL, `name`, `sex`, `age`
    FROM `table`
    WHERE `id`=1
    
    # 更改某字段的值:将复制得到的name加上copy_前缀
    INSERT INTO `table`
    SELECT NULL, CONCAT('copy_', `name`), `age`, `sex`
    FROM `table`
    WHERE `id`=1
    

    id 这种自增的主键,直接插入 NULL 就可以了,MySQL 会自动帮我们补全(如前几段所示)。

    同时插入多条数据也很简单,这样即可:

    # 假设表结构为id, name, age, sex
    # 复制 id<10 的字段,加上copy_标识
    INSERT INTO `table`
    SELECT NULL, CONCAT('copy_', `name`), `age`, `sex`
    FROM `table`
    WHERE `id`<10
    

    实话实说我不太懂MySQL,不过这样写我觉得有几个好处:

    1. 省事儿,可以不再把变量一一抄上
    2. 可以一次复制多条数据
    3. 只执行一行sql,速度应该会更快
  • PHP通过Sql生成带特定索引的数组

    不明白原理,从别人代码中看到的,蛮好用,不过似乎性能不好:

    SELECT id, sum(rmb) AS rmb, sum(device) AS device
    FROM log_table
    WHERE date>'2012-10-20'
    GROUP BY id

    这样一段Sql,用PHP自带的pdo这样请求:

    $result = $dbh->query($sql)->fetchAll(PDO::FETCH_ASSOC|PDO::FETCH_UNIQUE|PDO::FETCH_GROUP);

    可以得到一个数组,使用sql中第一个字段也就是id为索引,每个元素为一个含两个属性(rmb, device)的对象。换成json写法大概是这样:

    // 以前
    [{id: 1, rmb: 1, device: 1}, {id: 2, rmb: 2, device:2}]
    
    // 现在
    {'1': {rmb: 1, device: 1}, '2': {rmb: 2, device: 2}}

    这样再操作会方便很多。

  • Android笔记

    这篇文章会记录我在Android开发过程中积累的各种知识和技巧。
    (更多…)