Blog

  • 肾上在“饭婚礼”上的证婚词

    全文贴出如下。

    =======

    各位来宾,大家好!

    今天是公元2011年6月4日,农历五月初三。今天是我的好朋友猛禽和京京结婚的好日子。我很荣幸接受他们夫妇的邀请,担任他们婚姻合情合理、合法合体的证婚人。

    肾上鄙人在下我和他们认识已经有很多年了。在过去几年来的河蟹大会、床菜会、小规模高姿态很黄很暴力的群P会上,肾上见证了他们的情感历程。他们终于走到今天,我们能不为他们祝福吗?

    婚姻首先是一种责任,更是承担这些责任的承诺。从此,夫妇双方就不再简单地过着一个人的生活,而必须承担更多的责任。猛禽和JJ都是我的好朋友,我相信,当你们了解到你们所要承担的责任后,再做出在你们余下的生命历程中走在一起的承诺,更能全心全意的、义无反顾的去实现这些责任,从而在你们的人生道路上取得了不起的成就。这是多么了不起的一个承诺啊!这是一个比“牢不可破的誓言”更牢不可破的承诺。

    婚礼只是一个仪式,而婚姻才能使你们的感情升华、永固。当时光流转、韶华逝去,神马都变成了浮云,惟有你们在一起度过的美好的时光永存。

    最后,我改编一下著名作家马尔克斯在他的《霍乱时期的爱情》一书结尾的一段话作为我的证婚词的结束:

    船长看了一下费尔米纳,在她的睫毛上看到了初霜的闪光。然后他又看了一眼阿里萨,看到了他那不可战胜的自制力和勇敢无畏的爱。于是,终于悟到了爱情与生命跟死亡相比,爱情才是无限的这一真谛,这使船长大吃一惊。 “您认为我们这样瞎扯谈的来来去去可以继续到何时?”他问。 阿里萨早在五十三年七个月零十一个日日夜夜之前就准备好了答案。 他说:“永生永世!”

  • 少说话,多看书

    自从2月10号写了一篇关于《Decision Points》的读书笔记后,一直没有写读书笔记。但是,我还是一直在看书。期间一共看了这么几本:

    My Spiritual Journey
    The Grand Design
    Alone
    The Girl With The Dragon Tattoo
    The Girl Who Played With Fire
    The Girl Who Kicked The Hornet's Nest

    后三本也统称为The Millennium Trilogy,千禧年三部曲。

    第一本书太敏感,所以我根本没有打算写读书笔记,只是写了一些在读的过程中学到的词汇作为参考。

    第二本书是霍金的作品。正如我在当当的评论里提到的,我看的是原版,译文我只看了一些,觉得可以推敲和斟酌的地方还很多。

    第三本书是一本悬疑惊悚类的小说。注意,它不是推理小说!讲的是一个狙击手奉命出发,然后射杀了一个“对其妻子和儿子产生致命威胁”的男人并受到调查的故事。到最后,情节有点狗血。但是整个故事中围绕着那个“妻子”是否是在设局(诱使她丈夫进入那样一个“狂暴而失控”的状态)的讨论还是有看头。

    后面3本属于一个系列。严格的说,这个千禧年三部曲只有2部——最多是2.5部。

    《龙纹身的女孩》的主角是男记者Blomkvist,女主角是Salander,如同蝴蝶梦一般,还有一个隐藏的女主角:Harriet Vanger。这个故事讲述的是Blomkvist因为调查Wennerstrom企业受挫而不得不暂避风头,于是受企业家Henrik Vanger的邀请,以编写Vanger家族史为名,实际去调查将近30年前家族成员Harriet Vanger神秘失踪并最终聘请Salander作为其调查副手的故事。另一条线是Salander被其监护人Bjuman凌辱而设计报复的故事。

    第二和第三部以调查地下黑帮进行性交易开始,并最终以揭露瑞典秘密警察机构中的一个秘密部门的黑幕而结束。Salander所受到的种种非人权的待遇都是因为这个秘密部门为了掩盖她父亲Zalachenko是一个从前苏联叛逃而来的间谍的事实而作出的针对性的行动。

    我不想剧透,但是看完这三部曲,我的最大的感受就是:黑客万能,分权万岁!

  • Symfony 2入门简介

    我一直关注Symfony这个框架。最近这个框架推出了Symfony 2,其官方站点也迁移到了http://symfony.com/。目前这个框架还没有正式发布,写作本文时可以下载到的是PR10版本(下载地址请戳这里)。

    请注意:Symfony 2需要PHP 5.3的支持,因为它用到了PHP的新特性namespace。

    由于SF2还是在PR阶段,有一些问题还是很正常的。最突出的是,对cache的管理存在着很大的bug。例如,清空缓存的命令:

    app/console cache:clear

    这个命令会出错,提示对app/cache下的某个目录无法进行写操作,而之前即使你使用了chmod命令将cache目录设置为777也是没有用的。你需要再次运行chmod,然后再cache:clear才可以。另外一个问题就是,app_dev.php(开发环境)和app.php(应用环境)有时不能同步,往往出现app_dev下可以的,在app.php下不可以的情况。这应该是和cache的管理有关。

    另外,由于SF2的web目录下不再使用index.php作为应用的入口,而改用app.php或者app_dev.php作为入口,开发人员不仅需要配置web/.htaccess文件(所幸的是SF2自带了一个,而且是可用的),还要修改站点的vhost文件,将该目录的Allowoverride修改为All。

    ========

    SF2的另一个根本性的变化时引入了Doctrine 2作为其DBAL和ORM。而Doctrine 2的最大变化就是抛弃了所谓的Active Record的操作,而引入了所谓的Domain Driven Design (DDD)的概念。什么是AR?什么是DDD?这两个概念的介绍本身都需要一大本书,但是作为基本的理解,我们可以看一个对比:

    //假定我们已经对一个需要持续化的对象进行了建模
    //在AR模式下我们这样操作:
    $post->save();
    //在DDD模式下我们这样操作:
    $manager=//...获得对象管理员的实例
    $manager->persist($post);
    $manager->flush();

    DDD认为,只有一个对象的建模是按照其实际世界的原型而进行时,才是最好的建模。换句话说,一个post对象自己不能保存自己,而必须有一个管理者(SF2中称为Entity Manager)来进行这个操作。

    这么做,至少有一个好处:对数据对象的操作都在内存中进行,直到flush后才持续化到数据库中去。另外一个好处是——还没有测试——根据SF2的文档,在修改表结构的时候——或者更确切的说是修改对象模型(一个类)的时候,对数据库结构的修改是叠加式的,而不是从头开始。这样,之前的数据可以得到保存,而不像SF1中,每次重建数据库都会删除之前的数据。

    =======

    最后我说说安装、配置。应该说SF2的配置非常简单。SF2将所有的“应用”都视作一个bundle,映射到一个namespace,物理上映射到磁盘上的一个目录结构。

    解压,比如:`/var/www/Symfony`就可以了。

    然后修改你的vhost文件,指定DocumentRoot到/var/www/Symfony/web就可以。另外,web目录下有 个.htaccess文件,可以看看。这个需要在你的vhost文件中设置Override All比较好。然后重启apache,访问:localhost/app_dev.php就可以了。

    然后是创建新的bundle:app/console init:bundle “tr/DemoBundle” src就可以在Symfony/src下创建一个新的bundle:tr/DemoBundle,这也是它的目录结构。

    然后要注册namespace和autoload:

    // app/AppKernel.php
    class AppKernel extends Kernel
    {
        public function registerBundles()
        {
            $bundles = array(
                new SymfonyBundleFrameworkBundleFrameworkBundle(),
                new SymfonyBundleSecurityBundleSecurityBundle(),
                ... ...
                new trDemoBundletrDemoBundle(),
    // app/autoload.php
    use SymfonyComponentClassLoaderUniversalClassLoader;
    $loader = new UniversalClassLoader();
    $loader->registerNamespaces(array(
        'Symfony'    => array(__DIR__.'/../vendor/symfony/src', __DIR__.'/../vendor/bundles'),
        ... ...
        'tr'         => __DIR__.'/../src',

    然后就是常规的修改controller/view之类的工作了。SF2建议使用twig这个也是由Symfony创始人Fabien Potencier创建的模板引擎。基本语法可以查查这里:http://www.twig-project.org/,应该说还是很简单的。

    当然,你可以修改一下routing.yml,SF2可以使用嵌套routing,具体语法可以参考文档。我现在的app/config /routing.yml很简单:

    homepage:
        pattern: /
        defaults: { _controller: trDemoBundle:Default:index }

    这样的话,访问localhost/app_dev.php就可以到自己的主页。

    本文推送到[go4pro.org]

  • 重构狗屎皮:第六天

    在第六天,重构后的狗屎皮终于上线了:http://www.go4pro.org 。总结一下,目前新版的狗屎皮的架构是这样的:

    第一大功能模块是后台的机器人抓取模块,由猛禽开发,使用的语言是Python。机器人负责遍历所有的文章来源的RSS并取出新的文章并存入数据库。

    第二大功能模块是前台,由TR开发,使用的语言是PHP+Symfony 1.4。这个模块用来显示数据库里的文章等。

    第三大功能模块是后台管理,由TR开发,使用的语言是PHP+Symfony 1.4。说实话,这个后台开发基本没有进行编程的工作,只是进行了一些配置就完成了。

    说说体会。

    用Symfony作为框架对于开发WEB应用是非常快而高效的。Symfony的优势在于提供了一个应用(前台、后台)的基本框架,这个框架可以让程序员用最少的代码、结构化/自动化的完成大量工作,并专注于业务逻辑的编写;而V层次的模板结构让版面设计人员可以用最少的PHP代码来完成页面的设计。在Symfony 2架构中,更是引进了一个由Symfony创始人Fabian Potencier开发的twig模板引擎。在twig引擎中,版面设计人员可以用类似Smarty的语法编写模板。我会专门写一篇文章讲述Symfony 2。这里不再展开。

    我没有专门整天用来进行狗屎皮重构的开发,每天也就最多1-2个小时。就是这样的零敲碎打,我也只用了6个晚上完成了现在能基本使用的站点。这是非常快的一个速度。如果是专业开发——加上模板设计,应该可以控制在一个工作周之内。

    使用框架的还有一个好处是,几乎所有的组件(M/V/C)都是可以decouple的。例如,现在的站点用的是mysql,但是只要修改一下数据库的配置,就可以顺利的切换到PostgreSQL,而不用修改V/C层次。

    我还是很推荐使用PHP,并使用Symfony作为框架来进行快速开发。

    本文收录于[go4pro.org]。

  • 重构狗屎皮:第五天

    继重构狗屎皮第四天后,我要开始非常重要的一个环节:狗屎皮后台的管理。而后台管理必然牵涉到用户登录。

    在Symfony框架中,这两个功能都可以简单的实施。 首先是后台模块的创建,基本上需要如下几个命令:

    //创建后台应用
    symfony generate:app backend
    
    //创建后台模块,其中的G4pArticle/G4pCategory是之前创建前台应用时的数据模型
    
    symfony doctrine:generate-admin backend G4pArticle --module=article
    symfony doctrine:generate-admin backend G4pCategory --module=category

    这时,如果你访问http://localhost/backend_dev.php/article就已经可以看到一个最基本的CRUD界面。这个界面功能已经十分完备:

    • 分页
    • 排序
    • 过滤
    • 单个记录的CRUD操作
    • 多个记录的批操作
    • 数据验证
    • 等等等等……

    我们当然可以对其进行一些定制,具体的我就不再一一列出。

    这时的后台界面还没有任何验证机制,这样的状态是不能被用到实际环境中去的。所以要加入后台的用户验证机制。

    Symfony准备了所谓的“插件”机制来协助我们完成这个步骤。通过以下命令可以安装Symfony内置的sfDoctrineGuardPlugin插件:

    symfony plugin:install sfDoctrineGuardPlugin

    在我的安装中,出现了一些问题。首先,我的PHP开发环境是5.3,在运行上述命令时会出现一些警告,声明一些函数已经过时。当然这个不是最致命的;其次,不知道是不是我的RPWT还是服务器的问题,解析这个插件的XML源的时候出现404错误,提示找不到这个包!幸好我在另外一个项目中已经下载好了这个插件,将所有插件的文件拷贝过来后,稍作调整即可:

    // config/ProjectConfiguration.class.php
    class ProjectConfiguration extends sfProjectConfiguration
    {
      public function setup()
      {
        $this->enablePlugins(array(
             'sfDoctrinePlugin',
             'sfDoctrineGuardPlugin' //这个要手动添加,但是如果通过plugin:install安装则不需要
        ));
      }
    }
    
    $ php symfony doctrine:build --all --and-load --no-confirmation
    
    //重新生成数据库,以加入用户信息
    // apps/backend/lib/myUser.class.php
    // 修改myUser类使其从sfGuardSecurityUser类派生而来
    class myUser extends sfGuardSecurityUser
    {
    }
    # apps/backend/config/settings.yml
    //修改登录使用的模块和动作
    all:
      .settings:
        enabled_modules: [default, sfGuardAuth]
        # ...
      .actions:
        login_module:    sfGuardAuth
        login_action:    signin
        # ...
    //最后创建用户root,并提升为管理员
    $ php symfony guard:create-user root SecretPass
    $ php symfony guard:promote root

    进行了如上步骤后,如果再次访问backend_dev.php,就会首先进入登录界面: 输入正确的用户名和密码后进入管理界面。

    至此,狗屎皮重构已经基本完成!祝贺我吧! 本文收录于[go4pro.org]

  • 重构狗屎皮:第四天

    今天进入狗屎皮重构的第四天。有件事要纪念一下:从今天晚间开始,我的虚拟主机上站点:BSpMq.com以及rsywx.net无法正常访问,必须翻墙。我实在不能明白的是,这样一个纯粹是打P扯谈的站点,一个以藏书、温和时事评论的站点怎么会被封?GFW真的是不可理喻了!

    进入妓术专题。

    今天进入狗屎皮重构的第四天。我主要解决了分页的问题。

    Symfony进行分页也非常简单,因为它内置了sfPagination这个类,我们只要传递一些参数,并在模板中加入一些代码就可以了。

    在我的开发中,第一个要解决的分页界面是“类别”中的文章。先放最终效果:

    category_with_pagination

    具体实现步骤如下:

    首先,修改routing.yml文件中关于显示属于某个类别的文章的路径,加入分页参数:

    show_category:
        url: /category/:slug/:page
        class: sfDoctrineRoute
        options:
            model: G4pCategory
            type: object
        param:
            module: category
            action: show
            page: 1

    为了简便,我给它定义了缺省的页数为1。

    然后,修改category/actions/actions.class.php文件中的executeShow函数:

        public function executeShow(sfWebRequest $request)
        {
            $this->category = $this->getRoute()->getObject();
            $this->c_slug=$this->category->getName();
            $this->pager=new sfDoctrinePager('G4pArticle', sfConfig::get('app_max_articles_on_category'));
            $this->pager->setQuery($this->category->getArticlesQuery($this->category->getId()));
            $this->pager->setPage($request->getParameter('page', 1));
            $this->pager->init();
        }

    这里的关键是最后四行。我创建了一个sfDoctrinePager对象,传递了它的查询SQL,并设置其要显示的页面(从sfWebRequest,也就是从$_GET中传递进来),然后进行初始化。注意,这里的SQL——严格说是一个查询的对象不是直接来自于之前我常用的G4pCategoryTable.class.php文件,而是来自G4pCategory.class.php文件。这两个文件的区别说实话我也不是很清楚。按照我的认为,前者主要是包含一些直接返回记录集的函数,我们可以把这个文件看成是一个虚拟的表;后者是一些类方法,一般不会包含直接返回记录集的方法,并可以对类的操作如save,get某个字段的方法进行改写。

    getArticlesQuery函数具体定义如下:

        public function getArticlesQuery($id)
        {
            $q=Doctrine::getTable('G4pArticle')->createQuery('a')
            ->leftJoin('a.G4pCategory')
            ->where('a.G4pCategory.id=?', array($id))
            ->orderBy('a.published_at desc, id');
            return $q;
        }

    它和我之前定义的一些方法还是比较类似的,唯一的区别是我直接返回一个Query对象,而不是返回记录集(通过$q->execute()得到)。这是因为具体要得到哪些记录必须由pager来确定,而pager是根据当前页面和每页显示的帖子数量来构建对应的SQL语句的。

    另外,请注意这个SQL的构成方法。Article表和Category表显然是一对多的关系(“一”在category表这边),所以我用left join来连接category表,我不需要指定on字段,因为这个是由Symfony根据表关系自动生成的。而在where子句中,我用了带参数的SQL构造,这样也可以有效的防止一些恶意的SQL注入攻击。

    最后,我们修改category模块的showSuccess.php函数(它其实就是一个模板文件),引入对应的记录集并加以操作,显示必要的导航等等。全部代码如下。为了方便阅读,我直接加了一些注释,如果你要直接使用代码,请将我的注释去除或者以正确的方式插入:

    <?php $articles=$pager-?>getResults() //获得当前的记录集 ?>
    <div class="pick">
        <div class="titlepic"><span class="sectionhead">文章分类:</span><span class="slug"><?php echo $c_slug; ??></span>
            <span class="more"><?php echo link_to('...所有文章', 'list_article') ??>
            </span>
        </div>
        <br></br>
        <div class="article">
            <ul>
                <?php foreach ($articles as $a):
                    $acount=Doctrine_Core::getTable('g4pcomment')-?>getCommentCount($a->id)->c;
                ?>
                <li><?php echo link_to($a-?>title, 'show_article', $a) ?>
                    <div class="writer">作者:<?php echo link_to($a-?>author, 'show_author', $a) ?> | <?php echo $a-?>published_at ?> | <a echo="" href="<?php" url_for=""><?php echo $acount ??>评</a>
                    </div>
                </li>
                <?php endforeach; ??>
            </ul>
            <hr></hr>
            <?php if($pager-?>haveToPaginate()): //判断是否需要分页,如果需要,那么显示导航条 ?>
            <a echo="" href="<?php" url_for=""><img alt="第一页" src="/images/first.png" title="第一页"></img></a>
            <a echo="" href="<?php" url_for=""><img alt="上一页" src="/images/previous.png" title="上一页"></img></a>
            <?php foreach ($pager-?>getLinks() as $page): ?>
            <?php if($page==$pager-?>getPage()): ?>
                <span class="currentpage"><?php echo $page ??></span>
            <?php else: ??>
            <a echo="" href="<?php" url_for=""><?php echo $page ??></a>
            <?php endif ??>
            <?php endforeach ??>
            <a echo="" href="<?php" url_for=""><img alt="下一页" src="/images/next.png" title="下一页"></img></a>
            <a echo="" href="<?php" url_for=""><img alt="末页" src="/images/last.png" title="末页"></img></a>
            <?php endif ??>
            <br></br><strong>本类别中共有<?php echo count($pager) ??>篇文章。</strong>
            <?php if($pager-?>haveToPaginate()): ?>
            当前为第<?php echo $pager-?>getPage()?>页,共<?php echo $pager-?>getLastPage() ?>页。
            <?php endif ??>
        </div>
    </div>

    就这么简单直接。 ===== 作为第4天,今天的工作其实还可以延续,因为我可以用相同的、很少的工作量完成其它几个页面的显示。不过这个就让我稍微偷偷懒,用个实际中的几天来完成吧。 本文收录于[go4pro.org]

    220322注:此时的Symfony还没有推出后来很流行的Twig模板引擎。

  • 重构狗屎皮:第三天

    今天进入了狗屎皮重构的第三天。今天要解决一个很关键的问题:如何让用户贴出评论?

    先看界面:

    ui-master-detail

    这时一个标准的主从表界面:帖子内容、已经有了的评论内容再加上一个表单方便用户提交评论。

    我本来认为要完成用户提交评论,在Symfony下是很简单的——简单到你可以不用写任何额外的代码,但实际情况并不如此。

    主要的一些修改如下。

    首先,要修改article的action.class.php文件中的executeShow函数:

    class articleActions extends sfActions
    {
        public function executeShow(sfWebRequest $request)
        {
            $article=$this->getRoute()->getObject();
            $this->article = $article;
            $this->comments=Doctrine_Core::getTable('g4pcomment')->getComments($article->getId());
            $this->form=new G4pCommentForm();
        }
        ......
    }

    上述代码中必须人肉创建了一个comment的表单,并传递到模板中进行进一步处理。

    在showSuccess.php模板中,通过include_partial来显示comment表单:

    include_partial('comment/form', array('form'=>$form, 'article_id'=>$article->getId()));

    注意,我还额外传递了一个article_id参数进去,这是为了后续的处理。

    修改comment表单文件如下,让它带一个article_id的参数:

    <?php echo form_tag_for($form, '@comment') ?>

    最后,修改comment的create以及processForm函数:

    public function executeCreate(sfWebRequest $request)
        {
            $this->forward404Unless($request->isMethod(sfRequest::POST));
            $aid=$request->getParameter('aid');
            $nc=new G4pComment();
            $nc->set('article_id', $aid);
            $nc->set('created_at', date('Y-m-d H:i:s'));
            $this->form = new G4pCommentForm($nc);
            $this->processForm($request, $this->form);
            //$this->setTemplate('article/show');
        }
    
    protected function processForm(sfWebRequest $request, sfForm $form)
        {
            $form->bind($request->getParameter($form->getName()), $request->getFiles($form->getName()));
            if ($form->isValid())
            {
                $comment = $form->save();
                $article=$comment->getG4pArticle();
                //var_dump($article);
                $this->redirect($this->generateUrl('show_article', array('id'=>$article->getId(), 'title_slug'=>$article->getTitleSlug())));
            }
        }

    在executeCreate函数中,设置了一个comment的外键article_id(从前一个页面传递过来),设置了comment创建的时间。

    在processForm函数中,需要修改一下处理完毕后的重定向,回到原来的帖子的页面,显示帖子、评论等。

    最后,我不得不说两句。我认为这个要求是几乎所有WEB程序都会用到的,但是我提问到Symfony的官方论坛后三天没有人回答!最后还是靠自己解决了问题……

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

  • 从地震、核泄露,到盐慌子孙

    地震我们中国人已经听过、甚至亲身经历过了。这次的日本地震虽说震级相当的大,但是如果没有加上“核泄露”,我相信不会在中国引发盐慌。

    我很少看新闻,我现在对外部世界的了解来自微博。我稍微回想了一下,这整个这个过程大概是这样的:

    1. 核电站爆炸——核泄漏
    2. 核泄漏防护——“碘”第一次进入视野
    3. 中国的食盐都是加碘盐——抢购盐

    这三步中,只有第一步是逻辑的推理。到了第二步,就有点待考的意思了;而到了第三步,则完全是失去了理智的举动。

    微博的风向是瞬息万变的,今天的新浪微博上,凡是我fo的,都不再谈地震、核泄露,而只谈盐慌子孙。

    微博的人才也是济济的,很快各类调侃的段子、对联、笑话就开始蔓延。我也转了好几个。

    ====

    我转的目的,却不完全是为了嘲笑那些在我们看来失去了理智从而变得异常不可理喻的同胞们——即使也许这些段子的本意就只是这个。

    我在思考两个问题:

    1. 他们为什么会如此行为?
    2. 为什么大家不愿意思考第一个问题,而更愿意提前进入全民欢乐模式?

    我先试图根据我的想法来回答第一个问题。

    今天我在微博上说过,“私”人要维持自己的生存权利是天经地义的。

    如果——只是如果——碘盐真的能防止核泄漏造成的核辐射,那么作为一个“私”人,他多买几包盐就是正常不过的举动——甚至可以说是正常的反应。

    而只有在确定自己的生存已经不再有威胁后,“私”人才会去考虑他所生活的群体中其它“私”人的生存问题。这时他要将他人分成两类:此人的生存将更有利于他自己的生存或者反之。他的优先考虑范围将是前者而不是后者。

    虽然在大部分日常生活情况下,我们都能很清楚的判断并分类,但是在这样一个突发、紧急情况下,我们却不再能、也不再愿、也没有时间去进行这样的判断,所以,在这样的情形下,唯一合乎逻辑和人性生存——及竞争——本性的行为,自然就是占据任何可以占据的资源,从而让他人无资源可占据。在当前情形下,这个资源很离奇的成为了“盐”。

    我们现在很淡定的讨论着这个问题,若有若无的在心里BS着他们,嘲笑着他们……却没有想到他们也许真的是没有别的选择。

    他们不像我们有较高的收入,没有那么畅通的获得信息的渠道,而生活的折磨让他们对ZF的公信、砖家的辟谣早已失去了信心并产生了习惯性的逆向思维,他们还能有什么选择?

    我们——我们这些在这里写着微博,带着笑意——也许还有一些善意,嘲笑着他们的人,我们怎么能这样对一个被逼如此去做的人呢?我们难道可以因为我们有别的选择就选择去嘲笑没有选择的人吗?

    撒谎的最高境界是:自己知道是在撒谎,而其它所有人都信以为真。可是,我们现在却只能听到撒谎的最杯具境界:自己知道自己是在撒谎,所有人也都知道他在撒谎,而只有他自己认为别人不知道他在撒谎!

    这个状态其实也是很玄妙的。平时无事,或者事不关己的时候,大家都会很理性、很温顺、很默契;而一旦有事,甚至出现人命关天的大事的时候,大家就一致抛弃谎言,很暴力、很夸张、很盐慌!

    这个状态其实是非常脆弱的。平时和谐,因无核胁;一有核胁,全无和谐。诱因多种多样,这次是盐,下次是醋,下次可能是柴米油酱茶。

    有人恐慌,就有人利用这个恐慌牟利,就有人善于制造恐慌。这个产业链是非常完善而又高效的……而不能阻止这样的产业链的产生,ZF难辞其咎!

    ===========

    我们其实都很清楚:碘盐其实无效,核泄漏要传到中国还为时尚早。所以,我们无法理解他们的行为。(我在这里用“我们”、“他们”并没有任何歧视的意思,只是为了更方便的区分一下两种类型的人群)。

    我相信所有这些网上的搞笑段子都很搞笑,也大都是善意的。只要是善意的,就还是带有劝醒性质的。所以,我也很是善意的加入了这个转发的行列。

    我只是希望,这样的一种恐慌能及时停止,ZF能切实拿出一些措施,不但要补充盐的供应,还要追究那些无良超市、无良供应商乘机哄抬物价的可耻行径。

    1.5元的盐涨到4元、6元,也许对于收入颇丰的家庭来说根本就无所谓,但是对于那些收入本来就捉襟见肘的家庭来说意味着神马,你们这些恶意哄抬物价、人为加剧紧张的兔崽子们想过吗!有木有!!!!!

  • 重构狗屎皮:第二天

    今天进入狗屎皮重构的第二天。实践证明,这第二天,确实是非常2的一天。

    首先,Symfony推出了一个新框架:Symfony 2。具体介绍见:http://www.symfony.com/。我刚搭建了Symfony 1.4.9的环境,实在不想在一个全新的框架上进行开发——虽然如此做对熟悉一个东西是很有好处的。

    其次,在进行代码版本控制——我用的是Mercurial——时,输入hg push死活没有反应……最后发现,由于在Windows下需要使用plink来连接远程的repo,而在第一次连接时,需要确认接受来自远程主机的一个证书,而直接hg push时,hg会间接调用plink,而此时Windows的CMD窗口无法正确的显示这个提示而导致出现“假死”。

    解决方法也很简单,先显式运行一次plink,接收远程主机证书后再运行hg push即可。

    第三,由于上次用Symfony还是进行我的任氏有无轩改版,距离今天已经一年多了,所以很多基本的东西都忘记了。比如:

    • 如果对route进行了一些修改,最好是进行一次symfony cc;
    • 如何进行主模板、子模板间的嵌套,以及数据的传递;
    • ……

    不过,总体而言还是非常顺利的。今天已经可以看到一些效果:

    go4pro.index go4pro.article.list

    我用Symfony开发WEB的速度确实是比我用Python开发WEB要快的多。

  • 重构狗屎皮:第一天

    今天开始重构狗屎皮。

    基本的框架是:后台机器人采用pMq编写的机器人进行采集,这个机器人是用Python编写的;由于我对Python以及基于Python的框架(我们之前用的是TG2)不是很熟悉,所以决定重新用Symofny改写。

    今天是第一天。主要是进行数据库的构建。

    解决了一个“小”问题,就是:Symfony在缺省时数据库所有文本字段采用的是拉丁编码,必须设置一下强迫其采用UTF8编码,代码如下:

    # File: config/database.yml
    all:
        doctrine:
            class: sfDoctrineDatabase
            param:
                dsn: mysql:host=localhost;dbname=go4pro
                username: root
                password:
                attributes:
                    default_table_charset: utf8
                    default_table_collate: utf8_general_ci

    最后三行紧要。

    今天还对样本数据进行了一些编写。用Symfony内置的机制进行数据填充还是很快很快的。

    比如我们可以这样编写一个comment.yml文件:

    # File data/fixtures/comment.yml
    G4pComment:
        first_comment:
            G4pArticle: first
            username: Dummy
            content: 好文章!
    <?php for($i=1;$i<=15;$i++): ??>;
        comment_<?php echo $i ??>:
            G4pArticle: first
            username: 访客<?php echo $i.n; ??>
            content: 我是路人<?php echo $i.n; ??>
    <?php endfor; ??>

    这样就可以一下子产生1+15个数据。YML文件是非常讲究格式的,必须注意书写时的缩进。