• 奶茶,这是你深恶痛疾的技术文……

    在搞CakePHP,发现一个很有趣的现象。

    如果在站点A内,无论页面怎么跳转,Session都是有效的。
    比如现在已经有一个$_SESSION['user'] = 'emma';
    如果这时候从站点B链接到站点A来,也就是用普通的<a href='http://site-a.com/page'>link</a>
    那么,这个站点A的$_SESSION就会“呼”的一下消失。

    这是为什么捏。找了差不多两三个小时。终于很认真地在/cake/libs/session.php中看到如下的一串,这是在function __initSession中写的:

    if (function_exists('ini_set')) {
        ini_set('session.referer_check', $this->host);
    }

    然后去查一下session.referer_check是做啥用的。一切就真相大白了。如果设了这个值为abc.com,那么php就会去check一下HTTP Referer中是否含有abc.com,如果没有,就重新生成一个session id,所以原有的session就会丢失。

    从上面的例子来看,在A站点里活动,HTTP Referer都是A站,所以都OK。如果从B站链到A站,那么这个Referer就成了B站点,于是就重新生成了session id,于是就…………咳咳。

    solution:
    在config/core.php中设置

    Configure::write('Security.level', 'low');

    同学们,看代码还是有必要的。。。

  • 这节课我们讲的是printf,以php为例。

    <?php
    printf('how %s? $%.2f', 'much', 2.0);
    ?>

    将会输出

    how much, 2.00

    但童鞋们注意了:

    <?php
    printf('how %2$.2f? %1$s', 'much', 2.0);
    ?>

    会得到什么呢?是不是没有看过%1$s这种东西?
    上面那个PHP会得到

    how 2.00? much

    后面的参数倒过来了。%1$s的意思是,当前位置用字符串(s)输出后面的第一个参数。%2$.2f表示在当前位置以两位浮点数输出第二个参数。大家可以多试试。

    为什么要这样捏?不是多此一举吗。
    嗯。这个在i18n里就经常用到了。

    比如输出一个sprintf(_('the %s with %s hat'), _('girl'), _('red'));
    在i18n里面,the %s with %s,应该翻译成“戴着红色帽子的女孩”,可以看到英文和中文对修饰词顺序是不一样的。如果我们这里在po文件里这样写

    msgid "the %s with %s hat"
    msgstr "戴着%s帽子的%s"

    那会得到“戴着女孩帽子的红色”,那就闹笑话了,或是你可以这样写po文件:

    msgid "the %s with %s hat"
    msgstr "%s, 帽子是%s的那个"

    这样得到“女孩, 帽子是红色的那个”,意思是正确了,但“信达雅”中的“雅”就达不到要求了。

    这时候如果我们写成

    in PHP:
    sprintf(_('the %1$s with %2$s hat'), _('girl'), _('red'));

    in PO:
    msgid "the %1$s with %2$s hat"
    msgstr "戴着%2$s帽子的%1$s"

    那一切都变得如此美妙起来。

  • 从明天起,做一个幸福的人
    喂马,劈柴,周游世界
    从明天起,关心粮食和蔬菜
    我有一所房子,面朝大海,春暖花开

    从明天起,和每一个亲人通信
    告诉他们我的幸福
    那幸福的闪电告诉我的
    我将告诉每一个人

    给每一条河每一座山取一个温暖的名字
    陌生人,我也为你祝福
    愿你有一个灿烂的前程
    愿你有情人终成眷属
    愿你在尘世获得幸福
    我只愿面朝大海,春暖花开

    这首诗我给它起名叫《面朝大海,春暖花开》。
    ……
    好吧,我承认这是海子的诗。

    昨天和今天并不算幸福,所以我打算明天开始幸福。
    海子是孤独的,也许并没有人能理解他。所以来来往往的陌生人,并不能让他投以足够的眼光。他只是愿面朝大海。

    每天都努力去获取收获,但往往不能得到,要明天,或是明天的明天,或是明天的n次方才能知道结果是怎样。
    但我会依然会像今天这样执着地去奋斗。尽管昨天今天不幸福,那我要让明天幸福起来。


  • 博客会有一个新的模板系统。
    比如可能会是这个模样:

    有点小PP吧:)

    另外站点会用新架构完成。代码100%重写。

  • 第一篇翻译下来的教程,花不了什么时间。Google先翻译,然后我润笔而已并对实际状态做了修改。润得不好,大家多包涵,将就着看了。其实还是看英文舒服。先译出来,然后我还要自己调试一下看看。
    转载请遵循我的CC协定。

    原文出处:How to use ACL with Cake PHP 1.2.x?

    这篇教程将简短地向你介绍如何在Cakephp 1.2.x中使用Acl。我曾经在这上面花了大量的时间,但在Gwoo、AD7Six还有其它朋友的帮助下,加上一些的调试及源代码的阅读,我写出了这篇教程。

    在开始之前,我想你应该有一些ACL的基本概念以及它应该用在什么地方。如果你对这些不甚了解的话,请阅读http://manual.cakephp.org/chapter/acl

    首先你应该通过在命令行下输入这行命令来创建ACL所需要的数据库
    cake acl initdb

    现在,我们将初始化一些更高级别的aro和aco设置。你也可以通过控制台命令来运行。不过,我更喜欢通过controller来运行,并没有什么特别,照着做就好了!

    Aros (可以是用户或是某项服务等)是一个要求访问Acos(可以是controller,action或其它服务)的东西。但在这个例子里,我们将限制Aros作为users,而把Acos作为controllers。我们将设置以下Aros(用户) :
    1. Admin
    2. |-->User::1
    3. User
    4. Guest
    根 据你的需求,你 可以增加更多的用户类别,但我们目前只谈论最简单的状态。Admin,User和Guest是一个用户组,而实际每个用户将属于其中的用户组。 'User::1'代表着一个用户id为1的用户。我们定义用户id为1的用户是admin下的一个实际用户,该用户会继承所有管理员的权限。这么做是没 有必要的,但是,你如果确定你的系统中至少有一个用户是管理员,那么,为什么不在这时候做这个操作呢。当然,这个用户的id是多少应该由你的系统具体情况 来决定。

    我们还将创建以下的Acos,也就是controllers
    1. User
    2. Post

    我们将添加两个acos,'User'和'Post'。但现在,你认为如果Acos是一个controller, 那么为什么不根据controller的命名规 则,命名为复数的'Posts',而是'Post'?好问题。这是因为通常一个controller的动作,可分为四种类型的动作:'创建 Create', '读取Read','更新Update'或'删除Delete'。而这四个动作是针对对model中的单个记录或一组记录的。因此,我们的操作是基于记录 层面的访问控制(Access Control)。我们希望确定当前的Aro(一个User)是否对一个Aco(比如一个Post的记录)有权限进行'C', 'R', 'R'或'D'的操作。如果有权限,就允许该Aro进行操作,否则就拒绝操作。以下代码,就列出了手工创建刚刚我们所讨论的Aro和Aco。

    Controller Class:

    <?php 
    class InitAclController extends AppController
    {
      var 
    $name 'InitAcl';
      var 
    $components = array('Acl');
      var 
    $uses = array();

      function 
    setupAcl()
      {
        
    $aro = $this->Acl->Aro;

        
    $aro->create();
        
    $aro->save(array(
          
    'model'=>'User',
          
    'foreign_key'=>null,
          
    'parent_id'=>null,
          
    'alias'=>'Admin'));

        
    $aro->create();
        
    $aro->save(array(
          
    'model'=>'User',
          
    'foreign_key'=>null,
          
    'parent_id'=>null,
          
    'alias'=>'User'));

        
    $aro->create();
        
    $aro->save(array(
          
    'model'=>'User',
          
    'foreign_key'=>null,
          
    'parent_id'=>null,
          
    'alias'=>'Guest'));

        
    $parent $aro->findByAlias('Admin');
        
    $parentId $parent['Aro']['id'];    

        
    $aro->create();
        
    $aro->save(array(
          
    'model'=>'User',
          
    'foreign_key'=>1,
          
    'parent_id'=>$parentId,
          
    'alias'=>'User::1'));

        
        
        
    $aco = $this->Acl->Aco;
        
    $aco->create();
        
    $aco->save(array(
           
    'model'=>'User',
           
    'foreign_key'=>null,
           
    'parent_id'=>null,
           
    'alias'=>'User'));
           
        
    $aco->create();
        
    $aco->save(array(
           
    'model'=>'Post',
           
    'foreign_key'=>null,
           
    'parent_id'=>null,
           
    'alias'=>'Post'));
      
       
    // Give admin full control over acos 'User' & 'Post'
       
    $this->Acl->allow('Admin''User''*');
       
    $this->Acl->allow('Admin''Post''*');

       
    // Give the user group only create & read access for 'Post' 
       
    $this->Acl->allow('User''Post', array('create''read'));

       
    // Give the Guests only create access for 'User'
       
    $this->Acl->allow('Guest''User''create');
       }
    }
    ?>


    可 以看到,通过Acl,我们授予Admin所有在'User'和'Post'上的Aco。即,Admin可以对所有的user和post做“增读改删”四个 操作。或是这么说,任何控制器的行动,其中包括创建,读取,更新或删除一个'User'或'Post'的记录,Admin组 是都被允许的。如果某么用户属于Admin组,他也被授予了这样的权限。

    'User'这一Aro只允许对'Post'这一Aco进行Create以及Read操 作,这意味着一个'User'组可以访问到一个对一条'post‘记录可以进行'Create'和'Read'的操作的action,而这正是我们想要 的。我们想让任何用户属于'User'组能创造新的post,并且可以读取post记录。但是我们不希望所有的user(aro),能够'更新 update'或'删除delete'任何一条'Post'(Acos)。这意味着,如果你在'User'组中,你将不能访问'Post'的相关'U'和 'D'的任何action。但创建这个Post的用户应该要有'u'和'd'的权限!!稍后我会为创建post记录的用户赋予这条post的全部权限,而 其它人对这条记录只有Read的权限。这样的做法,只为了明确你的概念。值得注意的是,以上我们没有对'User'这一aco做任何'allow'的设 定,所以这意味着默认为'User'的用户组的及'User'组下的成员,都没有获得对'User'记录的任何'CRUD'(acos)操作 。一个用户只有对自己的才能进行CRUD操作,对其他用户就没有权限。这就是我们为什么这么做的原因:)

    'Guest'这一aro只允许访问对'User'这一aco有创建的权限。也就是说,Guest只能创建一个新的用户账号,而不能对现有的账号做别的什么操作。

    我们现在已基本设置完成了,我们希望在一个新用户加到这个系统中的时候,就得到相关的aro和aco信息。下面的代码就显示了如何手工创造aro和aco,以及如何设置权限。


    Controller Class:

    <?php 
    class UsersController extends AppController
    {
      var 
    $name 'Users';

      var 
    $components = array('Acl');

      function 
    register()
      {
         if(!empty(
    $this->data))
         {
            
    $this->User->data $this->data;

            if (
    $this->User->validates())
            {
               if (
    $this->User->save())
               {
                   
    $aro = $this->Acl->Aro;
                   
    $parent $aro->findByAlias('User');
                   
    $parentId $parent['aro']['id'];

                   
    $aro->create();
                   
    $alias $this->User->name.'::'.$this->User->id;
                   
    $aro->save(
                     
    'model'       => $this->User->name,
                     
    'foreign_key' => $this->User->id,
                     
    'parent_id'   => $parentId,
                     
    'alias'   => $alias
                           
    );

                   
    $aco = $this->Acl->Aco;
                   
    $parent $aco->findByAlias('User');
                   
    $parentId $parent['aco']['id'];

                   
    $aco->create();
                   
    $aco->save(
                     
    'model'       => $this->User->name,
                     
    'foreign_key' => $this->User->id,
                     
    'parent_id'   => $parentId,
                     
    'alias'       => $alias
                           
    );

                   
    $this->Acl->allow(
                        
    $alias
                        
    $alias
                        array(
    'read','update'));
               }
            }
     
         }
      }
    }
    ?>


    以 上各位可以清楚看到,如何在新建用户的时候同时创建aro和aco信息。你也看到了如何让用户获得对自己操作的全部CRUD权限。或者这样说,用户A在网 站上注册了一个账号,同时他将获得一个与用户'5'。根据上述代码,系统会创建出一个别名为'User::5'的aro,同时在aros_acos表中创 建一条记录。这条记录说明'User::5'具有对'User::5'的aco的所有CRUD权限。现在除了用户A或是Admin组中的用户以外,其它人 都没有访问用户A账号的权限。现在我们来看下面的代码来证明我们刚刚的设想。

    Controller Class:

    <?php 
    class TestController extends AppController
    {
      var 
    $name 'Test';
      var 
    $components = array('Acl');
      var 
    $uses = array('User');
      var 
    $curLoggedInUserId 3;

      function 
    view()
      { 
        
    $aroAlias 'User::'.$curLoggedInUserId;
        
    $acoAlias 'User::5';

        if (
    $this->Acl->check($aroAlias$acoAlias'read'))
        {
           echo 
    'Read access allowed for User Id'.$curLoggedInUserId;
        }
        else
        {
           echo 
    'Read access denied for User Id'.$curLoggedInUserId;
        }
      }
    }
    ?>


    当你访问以上页面(http://localhost/test/view)的时候,你会得到'access denied'的提示 。现在把$curloggedinuserid的值改为5,并再次访问这个页面,你会得到'allowed access'的提示。这是因为这个时候来访者以用户A的身份登录。我们已经定义用户A拥有对用户A的账号有所有的权限。注注意当你设置$ curloggedinuserid = 1的时候,我们仍然得到了一个'allowed access'的提示 ,为什么会造成这种情况呢?因为用户id为1的用户属于Admin组,他拥有对'User'这一aco的所有CRUD权限。上述代码是一个非常粗略的代 码,只是用于展示的目的,并不是说这就可以在你的实际应用中就这么编码。

    以上是一本手工并且繁琐的方式,来创造aro和aco。现在,我现在会告诉你一个神奇的方式来创造aro和aco,而不需要花什么气力。所有要做的,只是利用cakephp1.2版中的Acl Behavior。以下是你将要放到Model 'Post'中的代码。

    Model Class:

    <?php 
    class Post extends AppModel{
    var 
    $name 'Post';
    var 
    $actsAs = array('Acl'=>'controlled');
    // 'controlled' means you want to create a 'aco'
    // 'requester' means you want to create an 'aro'
    // 译注:'controlled'及'requester'可以指定Acl组件现在是以aco或是aro方式来工作

    /**
     * Returns the parent Alias for current
     */
    function parentNode()
    {
        return 
    $this->name;
    }

    }
    ?>


    上述代码,将会在创建一个新post的时候自动地创建一个aco。而Acl Behavior来做所有的其它事情。在Acl Behavior中,有一个'afterSave'的回调方法。这个方法将在这个model每次save动作之后自动地调用。

    Acl behavior甚至可以在post被删除的时候自动地删除关联的aco,而不需要多做些什么。很酷吧?hiahia!现在如果你想对新创建出的aco设置一些权限的话应该怎么做呢?请看下面的代码:

    Controller Class:

    <?php 
    class PostsController extends AppController {

       var 
    $name 'Posts';
       var 
    $helpers = array('Html''Form' );
       var 
    $uses = array('Post');
       var 
    $components = array('Acl');

       function 
    add() {
           if(!empty(
    $this->data)) {
           
    $this->Post->data $this->data;
                
               if (
    $this->Post->validates())
           {
             
    $this->Post->create();
                    
            if(
    $this->Post->save($this->data)) 
                    { 
                        
    $acoNode = array('model'=>$this->Post->name,
                                         
    'foreign_key' =>$this->Post->id);

                        
    $aroNode = array('model'=>'User',
                                       
    'foreign_key'=>$this->getUserId());

                
    // User has full control of the post he created
                
    $this->Acl->allow($aroNode$acoNode'*');
            }
        }
        }
    }
    ?>


    因此,如果从Posts这一controller的add方法保存数据成功,我们知道Aco已经创建出来了,然后我们要做的就是为适当的aro和aco节点设计相关的CRUD权限,一切就这样完成了!

    我很欢迎所有的评论和建议。如果你在操作过程中出现了什么问题,请联系我。baking还是个很有趣的活的。

    Cheers,
    Ketan Patel



  • 在明年四月看完这本小书不容易呢。。
    所以要改变一下看书的计划,早上起来看书比较好,晚上看比较没有效率。早上早起看书比较好。明天早四十分钟起来看鸟语。
    目前晚上适合看七七八八的其它书。

    抛弃什么新浪网易吧,上面没啥东西我可以看了。看书比较实在。

  • 学友啊,给我唱个歌吧。于是音乐响起,“想和你去吹吹风”。嗯,真乖学友。

    下半年的大项目,于是这周开始把一些工作陆陆续续地交给小弟小妹们。已经躬亲了两年了,该出成果的时候了。
    于是在想项目开发代号是什么,其它的开发组喜欢用一些地名来做项目开发代号,比如Cambridge, Zurich, Casablanca之类。子哦,要起得有情调一点嘛。于是在想,我们该用什么。不能再用地名了,搞得我们跟开发世界地图似的。
    Debian用的是动画片“玩具总动员”里的卡通人物名字做为开发代号,比如Woody,就是那个小牛仔,Buzz是那个宇航员,Sarge,那个绿色塑料玩具士兵首领(小时候我有很多的Sarge)。
    我在想我们要不要用“花木兰”的人物做开发代号,比如“Mulan”,比如"Mushu“,Mushu是那个小龙(其实应该说是壁虎吧?我特别喜欢它,太逗了它),比如”Fa“,那个花将军。
    或是用大导演的名字?斯皮尔伯格?不要,太长了。小刚冯?不雅致没特色。当然我们冯导在拍《晚饭》之前的电影那都是个顶个的经典。那个那个“地主家也没有余粮啊”,笑了我多少年啊。
    或是用水果名,于是征求各mm的意见,你喜欢吃啥水果?草莓Berry?葡萄Grape?然后一个单词跳了进来,Apple,苹果。脑子里涌现出电影“达芬奇密码”中的情节,是什么推动了近代物理学的发展?APPLE,苹果嘛。如果不是那个苹果砸中了牛顿的小脑袋的话,哪有今天我们的神舟n号啊?不过要是叫Apple的话,Jobs会跑过来跟我拼命的说我抢了他们的牌子。于是我们叫AppleTree,对,就是AppleTree。

    这些天在考虑框架性的东西,越简单越好。扩展性要好,安全性要顾上。兼容性的问题我们也在考虑。毕竟是个中等项目。小估计一下超过24个人月的。我们有我们的做事规则,公司也有人掌控过3位数人月的项目的,但闭门造了这么久的车,迭代了N次也没个成果出来。我们组会尽可能地快速开发,快速调整。对于这个项目来讲,快速反应和足够的稳定性才是最重要的。
    有了老家伙们的经验,带着我们这些年轻人,应该可以把系统做得漂漂亮亮的。

    AppleTree,秋冬的时候结个大苹果吧。

  • 原先想起名叫“也谈PHP框架”,后来想想谈不了那么多,就想改叫“浅谈PHP框架”,后来又想谈也谈不了那么深入,所以定名“浅聊PHP框架”。聊聊而已,有火花就好。

    1. MVC
    model - view -controller
    MVC显然是核心的东西。为什么要MVC分开?如果你是小型应用的话,可能看不出MVC到底可以做什么用的。但只要你是在维护一个有20w行以上业务逻辑的系统,就知道MVC可以起什么作用了。
    MVC给我带来最好的东西就在于层次清晰,带来的就是代码维护上的方便和快捷。
    目前我所在的公司的开发部只走了软件生命周期的一半,开发完了,迭代OK了,随着使用说明书一块交付业务。然后就不管了,开发人员转去另一个项目组。剩下的产品维护、bug跟踪、版本升级等等东东基本上不管了。剩下的这一半周期很大程度上是由我们部门和网管来操作(当然我们也有自主开发的产品,而且是给公司带来直接利润的)。
    这剩下一部分工作,如果有人做过,就知道修改bug、修改界面、修改什么什么的是一件多么头疼的工作。如果业务流程有变动,程序就要跟着修改,而且这样的修改非常强调时效性。有的可能两三天内就要完成,甚至一天内就要修改上线。如果还继续采用传统的混合代码型的结构,造成的结果就是:你花了很多时间在理解原来的程序的思路上。MVC就把这部分浪费的时间帮你捡回来了。因为你可以一眼看出这个是在哪里处理的。维护起来就比较方便了。而且可以和UI分开,UI人员在做美工的时候,你可以修改代码。互不影响。
    当然有经验的UI还可以把HTML和CSS分开,更好的UI可以把HTML/CSS/JS都分开。神奇吧。。
    这么分开还有一个好处就是代码重用性会提高一些。你可以很清楚地看到哪些代码是已有的可以用。因为不可能每个新员工都清楚哪些是我可以利用的,而不是重复发明了轮子。

    2. 封装
    model和controller里不免要用到很多class。
    封装好class,可以让逻辑更清楚。比如xmren里的,我尽量使得和操作流相关的类做到不输出字串,不用到echo这样的东西。echo的操作应该放到view里去。
    封装也要搞得漂亮一些。最近还在看UML的东西(还要看琴谱),这样对如何封装类可能会有帮助吧。慢慢来。
    什么类做成静态类,什么类做什么用,咳咳,好好研究哪。

    3. 现有的PHP框架
    我看了一些PHP框架,包括CakePHP/FleaPHP和少量的Zend Framework,觉得它们都是优秀的框架。但它们都会有一些局限性。
    对CakePHP/FleaPHP这两个学习ROR出来的框架而言,我对他们最大的不满就在于他们对数据库操作的不灵活。不过话要说回来,他们的“不灵活”源自于ROR的规约性的开发,这样规约性的开发可以大大加快开发速度。但对于商用而言或是准备升级而言,修改数据库显然成本太高。
    如果他们把数据库操作这一块当做一个“插件”,那我想可用性就更高了。从迁移成本上考虑,理当如此。
    不过他们的RBAC(Role Based Access Control)好像还不错。可以吸收。
    btw, 对CakePHP中的route.php实在是赞不绝口,很聪明,不过还不够聪明,还能再改进一些。

    4. 小结
    对中小型网站的开发,包括xmren这样的,都可以用这些现成的框架。加快开发速度,效果也不错。
    如果对于网站要升级、或是大型商用网站。就要好好考虑了。目前现有的PHP框架可能都不适用于大型应用,对于此类,还是要因地制宜地开发自己的系统。多多考虑,前期规划远胜于后期修改。
  • 最近比较迷PHPChina,发现自己已经能解决上面的80%的问题了,当然包括一些大龄PHPer的个人问题我是爱莫能助不是,咱又不是月老。

    不过在PHPChina的论坛上面发现一堆不喜欢Google或baidu的人。比如上来就是“大侠救命”“高人请进”“跪求”“如何实现某某某功能”“急,在线等”之类。问的问题倒不难,但这些朋友的态度却让我有点不太舒服。

    很早时候就在鼓浪的Unix版(现在改成Linux版了)上看到置底的“如何有效地提问”。也有意识地让自己尽可能多地去尝试和解决问题。实在解决不了,而又很急了,才提出自己的问题,在网上寻求帮助。但PHPChina上的,多半是希望你能给出一整个解决方案,他们捡现成的。不妥不妥。

    掌握一门手艺,要的是勤学苦练动脑子。有三分之一的问题是在PHP手册或是其它官方手册里就有的,舍不得花时间去查。有三分之一的问题是因为知识面不够,有的东西不能举一反三,由此及彼。需要时间来磨练。有六分之一是因为毕业设计写不出东西来。还有六分之一是舍不得STFW。
    我就不清楚为什么他们不舍得得google一下关键字,一定要“急、在线等”之类。我用问题的关键字一打,第一条就是他们要的结果,为什么还需要在论坛上发贴子等结果呢?

    受人以鱼不如授人以渔,我在回答他们的问题的时候,尽量用思路来引导他们。因为这样才会是一个比较好的学习的过程。如果像“毕业设计”之流的,pay me, i will try.

    我觉得这样的习惯不错,掌握核心的东西才是主要的。这样才能持续不断地吸收新东西进来,靠着别人给你的东西,始终是不能达到一种“以不变应万变”的境界的。

  • 标题长了点,但可以引出我们今天的问题。咳咳。

    有的时候在调PHP的时候,发现明明文件是空的,却告诉你header has been sent。那说明已经有字符输出了?不过在session_start()之前明明已经没有东西了?点解?
    找了一下,是UTF-8文件的BOM(Bytes Of Mark)在作祟。

    What is BOM?在神奇的CSDN找到以下东东。

    在UCS 编码中有一个叫做"ZERO WIDTH NO-BREAK SPACE"的字符,它的编码是FEFF。而FFFE在UCS中是不存在的字符,所以不应该出现在实际传输中。UCS规范建议我们在传输字节流前,先传输字符"ZERO WIDTH NO-BREAK SPACE"。这样如果接收者收到FEFF,就表明这个字节流是Big-Endian的;如果收到FFFE,就表明这个字节流是Little-Endian的。因此字符"ZERO WIDTH NO-BREAK SPACE"又被称作BOM。
    UTF-8不需要BOM来表明字节顺序,但可以用BOM来表明编码方式。字符"ZERO WIDTH NO-BREAK SPACE"的UTF-8编码是EF BB BF。所以如果接收者收到以EF BB BF开头的字节流,就知道这是UTF-8编码了。
    Windows就是使用BOM来标记文本文件的编码方式的。

    也就是说,如果UTF-8编码的PHP程序,看到有开头时候说的那些现象,那就应该是这个文件头部有着EFBBBF这三个字节的输出。不过这几个字符是看不到滴,怎么删呢?

    1. 使用Dreamweaver2004打开一个utf8编码的文件,按Ctrl+J,可以看到一个BOM的checkbox选项,去掉它。 
    2. BOM占了文件的前三个字节,普通的文本编辑器是看不到的,用winhex打开可以看到前3个字节的bom标记。   

    编辑器在保存的时候默认是添加BOM的,有些则不是。 win2k下的记事本默认是添加的。如果用EditPlus的话,在参数->文件里可以看到有关UTF-8文件的保存选项,也有可能汉化为“签名”的。选“总是删除”就OK了。UltraEdit在另存的时候也有这样的选项。