Category: 编程、软件、技术

  • 微信订阅号的开发

    最近看了一下微信订阅号的开发。

    我要做一个个人订阅号,用户订阅之后,可以通过输入命令获得交互,得到英文单词的解释,大概就是这样。

    微信开发权限获得就不讲了,需要帮助的同学可以去访问相应的站点。

    首先,我开了一个域名(http://weixin.rsywx.com) 作为和微信通讯的接口。

    按照微信的说法,用户输入命令后,该接口将“被动”地回复信息。而这也是我这个订阅号和用户交互的主要渠道。

    在weixin.rsywx.com中,我用Silex框架构建了我的应用。这是因为我需要在这个框架中进行一些控制。当然你也可以用一个plain PHP文件来完成这个接口的工作。 该应用的入口文件很简单:

    require_once __DIR__ . '/../vendor/autoload.php';
    require_once __DIR__ . '/./WechatCallbackAPI.php';
    $app = new Silex\Application();
    $app['debug'] = true;
    // Twig registration
    $app->register(new Silex\Provider\TwigServiceProvider(), ['twig.path' => __DIR__ . '/../views']);
    $app->post('/', function () use ($app)
    {
        $obj=new WechatCallbackAPI();
        echo $res=$obj->response($app);
    });
    $app->run();

    其中的Twig注册不是必要的,因为微信要求的回应是一个XML字符串。不过放进去也没有关系。 对于“/”这个路径,我们在响应微信的请求时必须使用POST方式。同时我们创建了一个对象,并调用response方法来返回消息。 在WechatCallbackAPI中,我们抄袭微信的实例代码并作一些改进:

    public function response($app)
        {
            $post=$GLOBALS['HTTP_RAW_POST_DATA'];
            //$post=file_get_contents('php://input');
            if(!empty($post))
            {
                libxml_disable_entity_loader(true);
                $obj=simplexml_load_string($post, 'SimpleXMLElement', LIBXML_NOCDATA);
                $from=$obj->FromUserName;
                $to=$obj->ToUserName;
                $key=strtolower(trim($obj->Content));
                if($key==?||$key==?||$key=='help')
                {
                    $wpme=new WPME($from, $to);
                    $res=$wpme->help();
            return $res;
                }
                else if (substr($key, 0,4)=='wpme')
                {
                    $wpme=new WPME($from, $to);
                    $wpme->setKey($key);
                    $res=$wpme->invoke();
                    return $res;
                }
                else if(substr($key, 0,4)=='wotd')
                {
                }
                else
                {
                    $wpme=new WPME($from, $to);
                    $res=$wpme->unknown();
                    return $res;
                }
            }

    注意,这里还用到了一个在PHP 5.6中被标记为deprecated,而在PHP 7将是obsolete的功能:

    $post=$GLOBALS['HTTP_RAW_POST_DATA'];

    PHP手册中建议用

    <pre lang="php">$post=file_get_contents('php://input');

    但是经过测试似乎不行。微信就是这么任性。

    在该response函数中,我们根据不同的用户输入来调用不同的功能。上述代码中列出了WPME(Word Power Made Easy)模块中的判断。WPME模块中最重要的方法是invoke,它将根据不同的命令($key)去进一步调用不同的资源并构造返回值。而另外一些命令在WPME中直接处理,比如最简单的help方法:

    public function help()
        {
            $content = Available commands:\nwpme1: Latest WPME\nwpme7: Last 7 WPMEs\nwpme????: Search WPME for ????;
            $res = sprintf(WPME::$txtTemplate, $this->from, $this->to, time(), $content);
            return $res;
        }

    help将列出可以使用的命令,比如wpme1,然后渲染一个静态文本模板并返回。文本模块的格式可以参考微信文档。

    而invoke方法如下:

    public function invoke()
        {
            $uri = https://rsywx.net/wechat/ . htmlentities($this->key);
            $content = file_get_contents($uri);
            preg_match_all('/(.*?)<\/title>/i', $content, $matches);
            $res = sprintf(WPME::$newsTemplate, $this->from, $this->to, time(), $matches[1][0], date('Y-m-d'), $uri);
            return $res;
        }

    我们可以看到,我们的一般命令将返回图文信息。而图文信息的模板格式也请参考微信文档。这里我们并不进行任何实质性的处理,真正的内容渲染我们在另外一个站点:https://rsywx.net 中进行。

    rsywx.net是一个之前我单独开发的站点,考虑到微信使用的一些功能可以在该站点中复用,所以我将微信需要的返回信息的功能(控制、数据、呈现)放在这里实现。这个站点用Symfony完成。其实,我这个微信号最本质的内容呈现逻辑都是在rsywx.net中完成的。

    我们看一个indexAction方法。这个方法接受来自weixin.rsywx.com的调用,并根据不同的关键字进行处理:

    public function indexAction($key)
        {
            $entity = substr($key, 0, 4);
            if ($entity == 'wpme')
            {
                if ($key == 'wpme1' || $key == 'wpme7')
                {
                    $uri = http://api/$entity/$key;
                    $res = json_decode(file_get_contents($uri))->out;
                    $theme = $this->container->getParameter('theme');
                    return $this->render(AppBundle:$theme/wechat:$key.html.twig, ['res' => $res->res]);
                }
                else //The key is like wpme????, where ???? is the search term
                {
                    $search = substr($key, 4);
                    $uri = http://api/$entity/s/$search;
                    $res = json_decode(file_get_contents($uri))->out;
                    $theme = $this->container->getParameter('theme');
                    return $this->render(AppBundle:$theme/wechat:wpme7.html.twig, ['res' => $res->res]);
                }
            }
        }

    如果从weixin传来的命令是wpme1(列出最新的单词)或者wpme7(列出7个最新的单词),那么rsywx.net将进一步调用http://api.rsywx.com 中相应的RESTful API接口获得数据并渲染;如果是类似wpme????这样的命令,那么将进行搜索,并将结果返回。

    最后我们来看http://api.rsywx.com ,如果我们直接访问这个站点,将会呈现该站点所提供的所有接口的API文档:

    Ashampoo_Snap_2016.02.01_20h46m03s_002_Chrome

    而在这个站点中,我同样用Silex框架实现了对应的方法来返回数据(实际上,rsywx.net的所有数据也都是由该站点提供的)。

    好了,如果你现在在公众号中输入这样的一个命令:wpme1,你将看到如下的界面:

    Screenshot_20160201-205011

    Screenshot_20160201-205018

    怎么样?是不是很酷?

    本文收录于[go4pro.org]

  • 学习了JavaScript中的reduce

    之前一直没有认认真真地学习JavaScript(https://en.wikipedia.org/wiki/ECMAScript),因为一直只是将它作为在Web中进行编程,进行动态效果呈现的一个辅助脚本(我用jQuery比较多,还用过一段时间的Dart)。

    这两天因为脚扭了在家里休息了几天,随手拿起《Eloquent JavaScript》就看几段,就顺手学习了一下JavaScript中的reduce。

    完整的说明在这里

    按照定义,reduce对一个数组中的每个元素进行一个类似于“累加”的操作,将其规整到一个单一值。 它的完整语法如下:

    arr.reduce(callback[, initialValue])

    而其中的callback函数,又可以最多有四个参数:

    • previousValue
    • currentValue
    • index
    • array

    我们先看一个对数组中所有元素求和的例子:

    var total = [0, 1, 2, 3].reduce(
        function(sum, current) {
            return sum+current;
        });
    // total == 6

    这是一个非常直观的例子。 计算机程序求和总是用一个循环的方式。所以,“和”会保存在一个变量中,然后顺序一个元素一个元素的相加。

    注意,由于我们没有给出initialValue,所以在第一次运行时,sum(作为previousValue)就会取数组的第一个数值0,而此时的currentValue就会是数组的第二个元素1。在这个程序中,这么做当然是没有问题的。 我们可以将这个程序等价地写作:

    var total = [0, 1, 2, 3].reduce(
        function(sum, current) {
            return sum+current;
        }, 0);
    // total == 6

    此时我们显式地给予初始值为0(求和当然是如此),得到的结果是一致的。

    第二个例子是将多维数组“压扁”成一维数组。这也可以用reduce来完成。

    var arr=[[1,2,3],[4,5],[6,1]];
    var x=arr.reduce(
      function(flat, current)
      {
        return flat.concat(current);
      }, [])
    );

    这里必须给定initialValue。

    本文收录于[go4pro.org]

  • PHP 7已经来临(一)

    PHP界最近最热门的消息就是:PHP 7已经来临!Sitepoint的Bruno适时地写了一篇文章,列出了一些资源和回顾。 我用百度搜索了一下国内的相关内容,发现相关的讨论还是非常非常少。所以觉得有必要结合我的实践,写点东西,以求推动国内的PHP7的落地。

    安装

    PHP创始人Rasmus Lerdorf弄了一个PHP 7的Vagrant盒子,供大家下载尝鲜使用PHP 7。其Github的地址:https://github.com/rlerdorf/php7dev。安装过程应该说很简单的,大家可以参照仓库里的指引。

    有一个需要提示的地方。我相信很多人和我一样,是用Vagrant+VirtualBox的方式来安装虚拟机的。如果vagrant up这个PHP 7的虚拟机时出现问题,可以通过在VirtualBox中直接启动这个盒子的手段帮助解决。VB中的启动会给出更多、更详细的出错信息。我也是通过这个方法解决了这个盒子无法启动的问题:需要禁用这个虚拟机的USB 2.0功能。

    安装完成并启动后,可以通过Putty登录到虚拟机中,进行一次apt-get的更新,然后用php -v检验:

    6-1

    或者用浏览器访问这个虚拟机,会得到一个标准的PHP信息页面:

    6-2

    在这篇名为What to Expect的文章中,作者Davey Shafik列出了一些PHP7的重要特点。

    首先提到的是抽象语法树(Abstract Syntax Tree,简称AST),它将取代目前直接从解析器里产生操作代码的做法。

    将解析器和编译器分离将允许我们取消众多黑客手段,而且允许实现仅靠一次过编译(single-pass compilation)无法实现的语法特性。

    第一,解析器和编译器将更易于维护。

    第二,将语法确定与技术问题分开。在目前的处理中,有些语法元素非常难以实现——如果不是不可能的话。比如对yield表达式的限制: $result = yield fn(); //不合法$result = (yield fn()); //合法

    另外,当前的编译器结构不允许我们实现某些类型的语法。比如:

    • 数组解构:[$a, $b, $c]=$array,而不需要使用一个特定的list()语法。
    • 结果表达式在前的列表推导和生成子表达式,例如Python中的[x * x for x in list]。目前的PHP中,只支持反过来的语法:foreach ($list as $x) yield $x*$x
    • C#类型的表达式树和LINQ。

    AST对运行时的性能或者内存占用只有很少的影响。但是确实影响了编译过程中的性能和内存使用。但需要强调的是,这样的差别只在不使用opcode cache的时候才存在。

    PHP的官方Wiki中给出了一个对照表,针对不同大小的PHP文件各编译1000次的时间差异:

    文件 7之前 7 提升
    小(100行) 0.180s 0.160s -12.5%
    中(700行) 1.492s 1.268s -17.7%
    大(2800行) 6.703s 5.736s -16.9%

    和峰值内存使用:

    文件 7之前 7 增加
    小(100行) 378K 414K +9.5%
    中(700行) 507K 643K +26.8%
    大(2800行) 1084K 1857K +71.3%

    还有一个编译PhpParser项目的实例。总体而言,AST要快10%-15%,但需要更多的内存。多用多少内存取决于文件大小。小文件只要多用10%,而大文件需要多用70%以上。在比较实际的情形中,内存差异在5%左右。

    最后谈到了语法和行为的改变。

    yield不用括号了。以下语法都是对的:

    $result = yield;
    $result = yield $v;
    $result = yield $k => $v;
    

    括号不影响行为:一个问题是($foo)['bar']='baz'$foo['bar']='baz'表现出的行为不同。与之类似的还有byRef(func())byRef((func()))现在都会抛出一个严格标准下的需注意情况(如果byRef引用其参数,而func并不返回引用变量)。对list()的改变目前list()赋值是从右到左。在AST下,将是从左到右。

    list($array[], $array[], $array[]) = [1, 2, 3];
    var_dump($array);
    // OLD: $array = [3, 2, 1]
    // NEW: $array = [1, 2, 3]
    

    另一个赋值顺序有关系的例子是,列表赋值的左边和右边是不是用一个变量:

    $a = [1, 2];
    list($a, $b) = $a;
    // OLD: $a = 1, $b = 2
    // NEW: $a = 1, $b = null + Undefined index 1
    $b = [1, 2];
    list($a, $b) = $b;
    // OLD: $a = null + Undefined index 0, $b = 2
    // NEW: $a = 1, $b = 2
    

    现在的list()将只存取一个偏移量一次:

    list(list($a, $b)) = $array;
    // OLD:
    $b = $array[0][1];
    $a = $array[0][0];
    // NEW:
    $_tmp = $array[0];
    $a = $_tmp[0];
    $b = $_tmp[1];
    

    空的list()现在不被允许了。

    按引用赋值的自动赋值顺序AST中将调整为从左到右

    $obj = new stdClass;
    $obj->a = &$obj->b;
    $obj->b = 1;
    var_dump($obj);
    // OLD:
    object(stdClass)#1 (2) {
      [b]=>
      &int(1)
      [a]=>
      &int(1)
    }
    // NEW:
    object(stdClass)#1 (2) {
      [a]=>
      &int(1)
      [b]=>
      &int(1)
    }
    

    允许直接调用__clone现在可以进行类似$obj->__clone()这样的调用。(未完待续)

    本文收录于[go4pro.org]

  • Vagrant虚拟机中apt-get的翻墙

    用Vagrant安装虚拟机不是什么麻烦的事。这次我装了一个虚拟机后,准备用这台虚拟机学习一下HHVM。

    按照HHVM官方的文档,其实是很简单的。但是只有一个问题:HHVM的仓库地址(http://dl.hhvm.com)被墙。所以……我们只能用翻墙的方式。

    然后问题又来了,我的宿主机上装的是Shadowsocks,只提供Socks5代理;虚拟机中的apt-get只能使用HTTP代理,所以还需要privoxy来过渡。 整体步骤如下:

    1. 先安装privoxy,这可以通过一般的apt-get完成。
    2. 修改privoxy的配置文件/etc/privoxy/config,增加一行:
      forward-socks5   /               10.0.0.2:1080 .

    这里的10.0.0.2:1080是宿主机SS的服务地址。

    1. 重新启动privoxy。
    2. 在进行apt-get update时输入类似这样的命令:
      sudo apt-get -o Acquire::http::proxy=http://127.0.0.1:8118/ update

    即可。其中的127.0.0.1:8118是虚拟机中privoxy的服务地址。

    (本文收录于[go4pro.org]

  • BitTorrent Sync 2.0正式发布

    2013年4月份的时候,我推荐过BitTorrent Sync这个同步软件大杀器(文章链接)。当时这个软件还在很初期的阶段。

    今天,它已经正式发布了2.0。下载地址:http://www.getsync.com/platforms/desktop。还有移动版本和特殊硬件版本。

    强烈推荐大家进行一次升级。

    根据我的使用体验,主要的修订如下:

    1. 增加了一个“Owner”的设置。也就是说,如今的文件夹同步不光是一个键值的匹配,也可以是一个Owner的同步。换句话说,即使在一台新的电脑上安装好BTS后没有进行任何设置,但是只要是设置了同一个Owner,那么在另外一台电脑上设置好的同步就会自动映射过来。这个功能我个人觉得不是很好用。因为我是喜欢将BTS当做同步和备份使用的,所以我一定会手动设置各个同步目录间的映射。这点请非常注意。而且这点很讨厌!
    2. 一个目录可以临时停止同步,脱离同步,也可以再恢复同步。以前这么做的时候,需要每次输入key,但是现在的话不需要。一个脱离同步的目录,会留在界面中,等待重新连接或者正式移除同步。

    总的来说,这次的升级是颠覆性的。但是值得立刻升级。此次升级,BTS终于走出收费的第一步:提供Pro版本。年费40美元,提供一些额外的操控功能,如控制一个大目录中各个文件的共享设置等。

    另外,BTS应该对目录的共享进行了一些小的改进,虽然2.0和1.4之间可以互相同步,但是BTS会明确地告诉你这个目录是1.4。建议删除同步后再重新生成。

    BTS 2.0下,不再强调同步键,而是用https://link.getsync.com/#f=My%20Books&sz=0&t=2&s=KYMD4…EKZAOUD3JTZXQ&i=CHSAXI…WLXPV2LJWPRVV&e=1425736233&v=2.0的方式进行文件夹同步的设置。

    最后放两张图给大家看看效果。一张是Linux下的截屏,一张是Windows下的:

    Ashampoo_Snap_2015.03.04_22h04m00s_005_Sync Ashampoo_Snap_2015.03.04_22h08m17s_006_Sync

    注:自动备份过来的目录都会放在”~/BitTorrent Sync”之下。参照如下步骤重新将同步目录归并到想要的地方:

    1. 暂停目的地该目录的同步,同时脱离同步,但是不要删除同步。
    2. 在源目录的设置中,拷贝出链接。
    3. 在目的地设置中选择人工链接,黏贴2中的链接并选择新的目的目录即可。

    BTS将自动将刚才那个人工脱离同步的目录链接回来,但是此次同步目的地是新的地方了。注意,不用给出全路径。比如,你要备份的实际目录是在/sync/Taylor,请直接制定/sync。BTS会根据你的源目录的最后一个子目录(比如e:\Users\Taylor中的Taylor)在/sync目录下创建Taylor这个目录进行同步。

    Let’s BTS!

    本文收录于[Go4Pro.org]

  • Elevate使用心得

    Elevate真的是一个很强大的英语训练软件。我已经连续每天受训达43天了。想想为了保持脑子清爽,延缓得老年痴呆的节奏,我也是蛮拼的。

    ks_93365

    Elevate主要训练这五个部分:Speaking,Writing,Reading,Listening和Math。严格地说,最后一个部分Math不在传统的听说读写之中,但是要考虑一下用英文做数学的情景,所以也还是一个英语训练——当然从一个侧面也反应出,老外普遍数学不行的事实。

    训练都是采用“游戏”的方式。比如,Math部分中的Discount部分,会列出几个价格标签,然后再贴上”10% discount”或者”20% mark-up”的标记,用户由低到高依次点击各个价格标签;再比如Writing部分的Abbrevity部分,给出一个句子,要你点击其中语义重复的部分(比如repeated again这样的)……

    经过43天不间断的联系,我现在的水平如下:

    IMG_0792

    这个软件是免费软件,但是有升级到PRO的选项。由于是采用subsription模式而不是一次性购买,所以我没有升级。免费版每天可以做三个随机选择的练习。我现在一般就是在早餐的时候用5-10分钟的时间做完。

    不得不说,随着难度的逐次提高,还是蛮有紧迫感的。不过,人总要活着有点压力,是吗?

  • 解决git push时出现的https错误

    (本文收录于[go4pro.org])

    这两天更新了一点我的“任氏有无轩“站点,结果发现无论是使用命令行的git还是SourceTree,都无法push到远程repo,出现错误如下:

    Ashampoo_Snap_2014.07.26_09h38m21s_001_

    在网上查了一下,基本上都指向Windows搜索一个DLL——本例中是libcurl.dll——的路径优先问题。

    按照MSDN官方的说法是:PATH变量中设置的路径是要到很后面才会被搜索到的。于是我在Windows命令行中开始搜索所有的libcurl.dll文件,发现归于Windows系统目录下的有三处:

    Ashampoo_Snap_2014.07.26_09h43m27s_002_

    经过试错法,确定在我的Windows 8.1 64位版中,最后一个libcurl.dll需要被替换(注:需要admin权限才可以替换系统文件)。支持https的libcurl.dll的大小应该是360448字节。

    然后再次git push,无论是在命令行还是SourceTree中都可以了。

  • PHP开发者经常会犯的另外7个错误

    本文原始链接:7 More Mistakes Commonly Made by PHP Developers。作者:Bruno Skvorc

    Thanks for Sitepoint.com authorization for translation and publication in my blog.

    本文翻译力求忠实原始文档。

    ==========译文开始分割线==============

    在6月的时候,TopTal(自由编程人员市场)发表了一篇文章,题为《PHP程序员最常犯的10个错误》。那个列表当然不是穷尽的,但确实写得不错,并指出了一些非常有趣的、值得每个人关注的陷阱——即便如我个人都不会将这些错误认为是很常见的。

    我希望你对这篇文章加以通读——其中有一些确实很有价值的、你需要知道的信息,特别是前八个要点。几天前,Anna Filina扩展了这个列表,并加入了七个新的项目。尽管这些地方不那么特定也不那么常见,她的观点还是有分量的,需要在开发时加以考虑。

    PHP开发者经常会犯的另外7个错误

    TopPal中某位仁兄请我看看他们的列表,还有没有补充,社交网络上的一些粉丝也表示有兴趣看到这个列表继续。所以,我愿意借此机会在这个列表中加入我自己的一些事项。这些都是我不断需要去警示我的团队或者粉丝们的。

    1. 使用mysql扩展

    这个消息其实很旧了,但是尚未注意这个事实的开发者数量还是大到令人担忧。如果用到SQL数据库,特别是MySQL,有太多的开发者还是倾向于使用mysql扩展。这个扩展已经官方认定过时。它不安全,不可靠,不支持SSL,不支持MySQL中的一些现代特性。它也会产生过时提醒,出现在你的应用的最顶部——但不会中断应用。滑稽的是,这意味着我们可以简单地Google这个关键词,就能找到大量这样还是用着这一不安全设置的站点。因为这个,这些应用所面对的伤害是令人震惊的。

    避免使用mysql,我们可以选择:MySQLi或者PDO。例如,使用MySQLi简单到几乎只要在API调用后加一个“i”即可:

    $c = mysql_connect(host, user, pass);
    mysql_select_db(database);
    $result = mysql_query(SELECT * FROM posts LIMIT 1);
    $row = mysql_fetch_assoc($result);

    对比:

    $mysqli = new mysqli(host, user, pass, database);
    $result = $mysqli->query(SELECT * FROM posts LIMIT 1);
    $row = $result->fetch_assoc();

    使得我们的设置变得更安全,所要做的就是这些。 不过你应该选择PDO。详见第2点。

    1. 不使用PDO

    不要误会,mysqli确实(确实确实)比古老的mysql扩展超出了好几代。它更新及时,安全,可靠,快速。但是,它只适用于MySQL。使用PDO可以让你使用一些美妙而实用的面向对象语法,也能让你做好准备使用另外的SQL数据库(如PostgreSQL,MS SQL以及其它)。另外,PDO可以让你使用命名参数。这一特性非常有用,只要充分利用它带来的好处,就很少有人能想象不这么做。最后,还有这点:你可以直接将获取的数据注入到一个新对象中,对于大项目而言,这能节省时间,而且是令人愉悦的。

    1. 不重写URL

    这是一个常常被忽略却也容易修正的问题。诸如myapp.com/index.php?p=34&g=24这样的URL如今简直就是不能被接受的。由于几乎不可能写出一个好的URL重写规则来涵盖每个服务器和框架,几乎每个框架都有一个指南,告诉我们如何设置干净的URL(Laravel, Phalcon, Symfony, Zend)。如果哪个框架不这么做,那么它就不值得我们使用——这些框架显然不关注现代化的实践。

    1. 抑制错误提示

    我在以前的一篇文章中写过这个问题,但还是有必要再说一说。任何时候你发现自己使用@运算符,请再考虑考虑,尝试从另一个角度更认真地处理这个问题。用我的话来说,在一个应用的功能中包含20行引用的cURL代码也比单单一行前放一个@来的好。

    我个人的经验告诉我,我在原来的帖子中建议的方法是一个好方法:打开所有的提示并转化其为致命错误。要确保在错误日志中没有任何记录,因为确实没有要记录的东西要比用@遮在眼前,装作看不见有错误发生要好。

    最近我们发现了一些Heroku add-ons可以用来开发生产环境下的PHP应用,其中一个Papertrail很棒。这个add-on让你将应用所有的错误推送到后台,从而可以轻松的加以搜索、分组并在日后加以清除。所以,即使真的有错误发生,让它们被记录下来,然后通过修订代码来消除错误。这比抑制它们,愚弄你的用户来的好。

    1. 条件判断中的赋值

    即使再有经验的程序员有时也会手指一滑,写下if ($condition = 'value') {而不是if ($condition == 'value') {。我们的手会打滑,键盘可能没有记录按键,我们从代码的另一部分——那一部分确实进行的是赋值操作——拷贝了代码。这些都有可能,而我们只有在运行应用时才发现问题。

    要完全避免这个问题,有几个方法:

    • 使用一个好的IDE。任何好IDE(比如PhpStorm)会在检测到这一问题时警告说”条件判断中进行了赋值“。
    • 使用“Yoda Conditions”。在众多流行项目,甚至大型框架中,都会看到这个应用。通过交换比较双方(if ('value' = $condition) {),即使比较弱的IDE也能注意到这个问题。有人认为Yoda语法很讨厌而且毫无意义,其信条是绝不使用(“更小心地编码,蠢货!”)。单就个人而言,如果能帮到你的话,我是建议使用的。如果我们都是优秀人物,那么WordPress和Zend Framework就不会存在了。
    • 牢牢记住,每次写的时候都要检查一次。所要的知识练习,但即使对于最好的开发人员而言,这也可能发生。所以前两点会有用。
    1. 太透明了

    我这么说可能会有反对意见,不过我还是要说。除非你对框架的开发者有100%的信心,或者也不运行高利润、高流量的商务关键应用,你应该总是努力隐藏你的后台——不要告诉大家你的应用基于什么框架。这实际上能对防止攻击起到作用——如果发现了该框架的一个安全漏洞的话。例如:

    If you use Symfony2 translator and have a route with a {_locale} parameter upgrade NOW http://t.co/jihXHB8MzT— Jérémy DERUSSÉ (@jderusse) July 15, 2014

    在该Tweet中,有关代码注入的一个严重问题已经变成了广为人知的知识。如果你正在上班,可以立即升级而不用担心开发问题或者让团队停止,那很好。但是对大部分使用Symfony的人和公司而言,情况不是如此。即便Symfony可以通过Composer升级(如Ryan在评论中提到的那样),在大的多层环境的团队中,通常这要花一些时间来获得批准。所有使用这一翻译机制的站点,并且也声明了是Symfony的用户的站点,就会面临该缺陷,直到被修正。

    上面提到的使用Symfony,只是一个例子。多年来,类似的情形发生在无数其它的软件中。我还在使用ZF框架开发商业应用那会,也发生过这样的情况,并因此受到了攻击。WordPress也有其安全性漏洞,而我们也知道世上有多少站点是由它来运行的。会发生这些事情。有时,开源和透明不是最好的方法来处理那些承担公司主要收入来源的应用。

    1. 没有移除开发配置

    最后,我要提一提移除开发配置。最近(声明,我再次提到Symfony纯粹是巧合),Cnet遭受了一次攻击,原因就是没有移除它们的开发配置。

    Uhmmm no: http://t.co/rAQis1ycWq #security #symfony— Marco Pivetta (@Ocramius) July 15, 2014

    Cnet是全球最大的技术新闻站点之一,它基于Symfony。你可能知道,Symfony有两个应用入口:app.php和app_dev.php。你的浏览器访问其中之一,进入的是生产环境,如果访问带有_dev后缀的那个,显然你会进入开发环境,从而有除错器、敏感数据以及诸如此类。这样是好是坏可是很多讨论的话题(再次感谢Ryan指出了这点)。但无可否认的是,它让一些比较笨拙的开发者面临着Cnet面临的同样的错误。另外,通过app_dev访问的任何其它URL也会导向其它的app_dev相关的URL。换句话说,不仅是首页在开发环境下运行,而是整个站点——放在Cnet的案例中,这可是大量的访问。

    如果你跟踪了Twitter上的讨论,情形很快就糟糕到令人尴尬——而更可悲的是,只要几秒钟的工作就可以避免:

    • 开发者应该在生产用服务器中删除app_dev.php
    • 开发者应该将可以访问app_dev.php的IP放入白名单,这也是缺省配置——除非你故意放松了这些限制

    任何一个方法都可以完全避免所有的问题。记住,在发布到生产环境时,要确保你的开发配置要么完全不可访问,要么只有在白名单中的IP可以访问。

    结论

    (本文收录于[go4pro.org])

  • Dart 1.5.3的一些变化

    Ashampoo_Snap_2014.07.15_22h13m10s_003_

    Dart是一个发展非常快速的语言。如今最新的版本是1.5.3。自从今年1月份我写了两篇关于Dart的文章( )以来,Dart语言本身,尤其是我用到的Polymer部分,有了很大的变化。

    为了准备8月9日(暂定)于苏州举行的,由G4P.ccGDG Suzhou联合举办的活动,我对之前编写的Dart小程序进行了调试。本文就在这次调试中发现的变化进行一些总结。

    一个Dart/Polymer程序的架构没有变化,还是由三部分组成:

    • 入口程序,比如getqotd.html。这个文件提供了页面的布局和对Polymer元素的调用。
    • Polymer元素模板,比如qotd.html。这个文件定义了一个Polymer元素的布局和一些基本设置。
    • Polymer元素的代码,比如qotd.dart。这个文件也是该Polymer元素的驱动代码部分,也可以是我们编写的一个Polymer/Dart程序的核心。

    入口程序

    <head>
      <meta charset=utf-8>
      <meta name=viewport content=width=device-width, initial-scale=1.0>
      <title>QOTD Sampe App</title>
      <script src=packages/web_components/platform.js></script>
      <script src=packages/web_components/dart_support.js></script>
      <!-- import the qotd -->
      <link rel=import href=qotd.html>
      <script type=application/dart>export 'package:polymer/init.dart';</script>
      <script src=packages/browser/dart.js></script>
      <link rel=stylesheet href=css/bootstrap.min.css>
    </head>

    这是一个标准的入口程序的HTML头部分。与之前相比,该部分加入了移动设备的支持。

    另外,所引用的JS文件也改为platform.jsdart_support.js。而原来的:<script type=application/dart>export 'package:polymer/init.dart';</script><script src=packages/browser/dart.js></script>已经被弃用。我个人对这个改变比较欢迎。毕竟原来的语句很不像HTML。

    模板

    重要的改变有两个。

    一个是在原先的<polymer-element>定义之前必须再加一个<link rel=import href=packages/polymer/polymer.html>语句。

    第二,鉴于Dart已经重新对shadow DOM进行了更好的支持,于是可以在<template>中直接加入对CSS的引用,如:<link type=text/css rel=stylesheet href=css/bootstrap.min.css>这样。

    代码

    鉴于上述的第二点,代码中原先需要的对shadowDOM进行applyAuthorStyles的操作就不再必要。事实上,如果你写了,Dart编译器会给出deprecated的警告。

    基本上我发现的改动就是这些。当然,改动肯定不止这些。我在O\’Reilly买了一本Dart: Up and Running的书,电子版已经更新并专列了一章介绍新的变换。我印象比较深的是Reflection的引入。

    本文收录于[go4pro.org]

  • CSS2中文版下载地址

    这两天看我的访问记录,还有人在尝试下载我多年前翻译的CSS2规范的中文版。

    由于网站改版,原来的地址已经失效。

    现在可以直接从这个地址下载: http://www.rsywx.net/download/CSS2_Chinese_TR@SOE.zip

    免责声明:这个版本的翻译还是差不多7年前完成的,目前已经不再维护。目前也没有翻译CSS 3.x的计划。