Profile老王的网络日志BlogLists Tools Help

Blog


    7/4/2006

    利用DIA+UML2PHP5完成PHP的对象建模

    本文为老王在公司利用上班时间偷偷写的,所以版权归老王及公司所有
     
    写作条件那是相当艰苦,一来因为上班,要偷偷进行,二来没有截图软件,只能用qq自带的截图功能。
     
    btw:家里电脑中了IEBAR,上网搜索才发现IEBAR这个流氓软件发现竟然是我们公司的产品(除了PHP组,我和公司别人都不熟悉),相煎何太急啊。B4坐我后面那几个制造iebar的程序员,强烈建议中央出台相关法规,监督并提高程序员的道德修养。
      
    言归正传,有的人可能说,php这东西也搞UML似乎有点不守本分了,UML是java才搞的东西。我不由得想反驳一下,八路军虽然装备差,但也不能只搞游击战,时不时也要根据情况来个百团大战,php也是一样,小项目你可以随心所欲的做,但是不见得不能使用uml这些高级货,共产党还用从国民党手里缴获的美式装备武装自己呢,我们也不妨把这遍文章看成是php从java阵营中缴获的战利品。
     
    老实说,我很少进行UML建模,至多是自己在纸上画画,为了脱离这种土八路的境地,我决定学习一下,让自己成为正规军。我选择的工具是DIA+UML2PHP5,因为免费,而且跨平台,Linux,Windows都可以运行。
    首先下载软件(http://uml2php5.zpmag.com/windows.php):
     
     
    安装很简单,先安装DIA,然后解压缩UML2PHP5压缩包,把下列文件覆盖到DIA目录的xslt子目录内:
    stylesheet.xml
    dia-uml-classes.zx
    dia-uml2php5.zx
    dia-uml2phpsoap.zx
    dia-uml2php5.conf.xsl
    注意:先把xslt目录下的stylesheet.xml改名为stylesheet.xml.old,以免覆盖。
    这个时候,基本的安装就已经完成了,我们可以画UML图并生成源代码了。
     
     
    打开dia,并新建一个文件。注意在下面的面板菜单中选这个uml。
     

     

    然后随便画一个对象关系图,我这里画了一个简单的继承。

    注意在画继承关系的连线时,一定要确认已经连接了两个对象,如果已经连接了,连接点会变红,我们拖动对象的方框,连线会自动跟随

     

     

    先保存.dia文件,然后选择export导出,对应的选项选择如下:

     

     

    确定后会再弹出一个菜单,相应选择如下:

     

     

    确定就可以了,在你保存.dia的目录里应该已经生成了相应的.php文件了。

     

    我们还可以通过php代码生成相应的uml图,注意要使用uml2php5-2.2.0版本,低版本没有这个功能,不过我测试了一下,似乎只能生成类图,和继承关系,而实现接口,聚合/合成等等关系没有实现。至于具体的过程并不复杂,我还在上班,就不写了,详细可以看 http://uml2php5.zpmag.com/en/TOOLS_php2uml.php

     

    昨天KnightE留言说dia不好,我想他喜欢的应该是ArgoUML那样的东西,改天我也熟悉一下。

     

    --------------------------------------------------------------------------------------

     

    7月5日补充:***************************************

     

     

    偶然发现了新版本的dia for windows,使用了gtk库,界面好了很多,推荐使用这个。

     

    下载地址:

     

    http://dia-installer.sourceforge.net/

     

    http://prdownloads.sourceforge.net/gimp-win/gtk%2B-2.8.15-setup.zip?download

    http://prdownloads.sourceforge.net/dia-installer/dia-setup-0.95-1.zip?download

    6/14/2006

    一个类似str_rot13的方法,可以用来实现passport的可逆加密

    加密和解密为同一个,类似str_rot13,但多了一个key的形参,所以更灵活一些。
     
    <?php
    function EncryptUDF($encryptString, $encryptKey)
    {
        $decryptString = '';
        for($i = 0; $i < strlen($encryptString); $i++)
        {
            $keyChar = ord($encryptKey[$i]);
            $encryptChar = ord($encryptString[$i]);
            $decryptChar = $keyChar ^ $encryptChar;
            $decryptString .= chr($decryptChar);
        }
        return $decryptString;
    }
    var_dump(EncryptUDF('hello', 'wang'));
    ?>
    12/30/2005

    PHP5.1的时区问题。

    if(function_exists('date_default_timezone_set')) { date_default_timezone_set('Asia/Shanghai'); }
    12/26/2005

    在PHP中模拟实现类似JAVA的转发(forward)

    java中web组件有三种关系,包含,重定向,转发。前两个很简单不用多少,至于转发,php本身没有缺省的对应方式,不过很多开源代码提供了相应的模拟方式,如下:
     
    Phrame:
    ----------------------------------
     /**
      * Forward the specified HttpRequest to another resource (PHP file) on the server. This method allows one
      * PHP script to do preliminary processing of a request and another resource to generate the response.
      *
      * J2EE adepts: Due to the nature of PHP, this method is implemented quite differently, although the result is
      * more or less the same. In stead of transfering the processing of the HttpRequest to the new resource, PHP's
      * include mechanism is used to process the resource WITHIN the forward method. The resulting output, if any
      * (i.e. if one or more response headers and possibly response body content are available) is returned to the
      * requesting web client.
      *
      * Phrame also introduces several features not implemented or implemented differently in J2EE / Struts:
      * - language negotiation (explained in the ActioServer documentation)
      * - availability of request and response in PHP variables:
      *  HttpRequest     => $_HTTP_REQUEST
      *   HttpResponse    => $_HTTP_RESPONSE
      *
      * Note: more variables are available if you define the Phrame tag library in the server's option array.
      *
      * @access public
      * @param HttpRequest  $_HTTP_REQUEST  The request we are processing
      * @param HttpResponse $_HTTP_RESPONSE  The response we are creating
      */
     function forward(&$_HTTP_REQUEST, &$_HTTP_RESPONSE)
     {
      // Store query parameters as attributes in the request
      foreach ($this->queryParams as $key => $value) {
       $_HTTP_REQUEST->setAttribute($key, $value);
      }
      
      // Handle language negotiation
      if (PHRAME_USE_LANGUAGE_NEGOTIATION) {
       $locale = NULL;
       
       // Get locale from the session, if present
       $session = $_HTTP_REQUEST->getSession();
       if (is_object($session)) {
        $locale = $session->getAttribute(_LOCALE_KEY);
       }
       
       // If the locale is not found; get it from the request
       if ($locale == NULL) {
        $locale = $_HTTP_REQUEST->getLocale();
       }
       
       // Localise the URI (first two chars of the locale represent the language)
       $uri = str_replace('.', '_'.substr($locale, 0, 2).'.', $this->path);
       
       // Adjust the forward path if necessary
       if (file_exists(PHRAME_DOCUMENT_ROOT.PHRAME_CONTEXT.$uri)) {
        $this->path = $uri;
       }
      }
      
      // Load tag libraries just in time
      $context = new ServerContext;
      $tagLibs = $context->getAttribute('_TAGLIBS');
      if (is_array($tagLibs)) {
       foreach ($tagLibs as $tagLib) {
        if (!include(PHRAME_DOCUMENT_ROOT.PHRAME_CONTEXT.$tagLib)) {
         trigger_error('Tag library \''.$tagLib.'\' not found or error loading tag library', E_USER_ERROR);
         exit;
        }
       }
      }
      
      // Build the response body
      ob_start();
      if (!include(PHRAME_DOCUMENT_ROOT.PHRAME_CONTEXT.$this->path)) {
       trigger_error('Resource at URI \''.$this->path.'\' not found or error loading resource', E_USER_ERROR);
       exit;
      }
      $_HTTP_RESPONSE->setBuffer(ob_get_contents());
      ob_end_clean();
      
      // Send the response headers and body content (if any) to the client
      $_HTTP_RESPONSE->flushBuffer();

     }
    }
    ----------------------------------
    12/16/2005

    自己编译php的chm手册

    1、首先下载php的多html页面格式的手册文档。
     
     
    2、使用TortoiseCVS下载cvs资源
     
    cvs -d :pserver:cvsread@cvs.php.net:/repository checkout phpdoc
     
    3、下载Microsoft HTML Help Workshop
     
    4、创建工作目录,这里是:E:\phpchm。
    5、把从CVS取回来的chm和en文件夹拷贝进来。
    6、把刚才下载的多个html文件形式的php手册文件夹拷贝进来。
    7、将make_chm.bat从E:\phpchm\chm文件夹里拷贝到工作目录下。 
     
    8、配置make_chm.bat。
    set PHP_PATH=C:\php\php.exe
    9、打开cmd,双击make_chm.bat 。如果没有问题的话,在E:\phpchm\chm目录下就会生成新的php_manual_zh.chm文件。
     

    搜索引擎友好?!

    我习惯使用PATH_INFO的方式来实现搜索引擎友好,比如:
     
     
    但是index.php能看到扩展名很不爽,解决方法如下:
     
    如何隐蔽应用:例如 .php,的扩展名:
    在APACHE中这样配置:
    <FilesMatch "^app_name$">
        ForceType application/x-httpd-php
    </FilesMatch>
     
    如何更像静态页面:app_name/my/app.html
    解析的PATH_INFO参数的时候,把最后一个参数的最后5个字符“.html”截断即可。
    注意:APACHE2中缺省是不允许PATH_INFO的,需要设置 AcceptPathInfo on
     
    12/13/2005

    UTF-8编码下求字符串的长度

    <?php
    $string = "王";

    echo strlen(utf8_decode($string));
    ?>

     
    附:
     
    /**
    * Unicode aware replacement for strlen()
    *
    * utf8_decode() converts characters that are not in ISO-8859-1
    * to '?', which, for the purpose of counting, is alright - It's
    * even faster than mb_strlen.
    *
    * @author <chernyshevsky at hotmail dot com>
    * @see    strlen()
    * @see    utf8_decode()
    */

    function utf8_strlen($string){
      return strlen(utf8_decode($string));
    }
     
    /**
    * Unicode aware replacement for substr()
    *
    * @author lmak at NOSPAM dot iti dot gr
    * @link   http://www.php.net/manual/en/function.substr.php
    * @see    substr()
    */

    function utf8_substr($str,$start,$length=null){
       preg_match_all("/./u", $str, $ar);
     
       if($length != null) {
           return join("",array_slice($ar[0],$start,$length));
       } else {
           return join("",array_slice($ar[0],$start));
       }
    } 
     
    12/8/2005

    POST上传时的数据格式

    POST上传时的数据格式:
     
    POST /upload.php HTTP/1.1
    Host: example.org
    Content-Type: multipart/form-data; boundary=----------12345
    Content-Length: 245

    ----------12345
    Content-Disposition: form-data; name="attachment"; filename="author.txt"
    Content-Type: text/plain

    Chris Shiflett
    http://shiflett.org/

    ----------12345
    Content-Disposition: form-data; name="MAX_FILE_SIZE"

    1024
    ----------12345--
    12/7/2005

    PHP能百分百弱耦合的分层设计?

    我现在写程序,一般是要分成BIZ和DAO两层的,但是一个问题一直困扰我,比如说DB对象(PHP中通常是ADODB)的获得正常应该是在BIZ里用工厂方法获得,再开启事务处理,再用形参的方式传递给静态调用的DAO方法,但是这样的话这个DB始终贯穿BIZ和DAO,是一件很让人恼火的事情。
    11/25/2005

    分子目录存放session提高效率

    当我们查看php.ini的内容的时候,我们会发现如下:
    ; As of PHP 4.0.1, you can define the path as:
    ;
    ;     session.save_path = "N;/path"
    ;
    ; where N is an integer.  Instead of storing all the session files in
    ; /path, what this will do is use subdirectories N-levels deep, and
    ; store the session data in those directories.  This is useful if you
    ; or your OS have problems with lots of files in one directory, and is
    ; a more efficient layout for servers that handle lots of sessions.
    ;
    ; NOTE 1: PHP will not create this directory structure automatically.
    ;         You can use the script in the ext/session dir for that purpose.
    ; NOTE 2: See the section on garbage collection below if you choose to
    ;         use subdirectories for session storage
    ;
    ; The file storage module creates files using mode 600 by default.
    ; You can change that by using
    ;
    ;     session.save_path = "N;MODE;/path"
    ;
    ; where MODE is the octal representation of the mode. Note that this
    ; does not overwrite the process's umask.
    ;session.save_path = "/tmp"
     
    也就是说,session的存放可以分子目录,但是要自己建立子目录,我写了个小程序,以三级子目录为例完成这个功能:
     
    <?php
    set_time_limit(0);
    $string = '0123456789abcdefghijklmnopqrstuvwxyz';
    $length = strlen($string);
    function makeDir($param)
    {
        if(! file_exists($param))
        {
            makeDir(dirname($param));
            mkdir($param);
        }
    }
    for($i = 0; $i <= $length; $i++)
    {
     for($j = 0; $j <= $length; $j++)
     {
      for($k = 0; $k <= $length; $k++)
      {
       makeDir($string[$i] . '/' . $string[$j] . '/' . $string[$k]);
      }
     }
    }
    ?>
     
    11/24/2005

    转载NIO的一篇好文

    PHP 实现多服务器共享 SESSION 数据

    肖理达 (KrazyNio AT hotmail.com), 2005.09.13, 转载请注明出处

    一、问题起源

    稍大一些的网站,通常都会有好几个服务器,每个服务器运行着不同功能的模块,使用不同的二级域名,而一个整体性强的网站,用户系统是统一的,即一套用户名、密码在整个网站的各个模块中都是可以登录使用的。各个服务器共享用户数据是比较容易实现的,只需要在后端放个数据库服务器,各个服务器通过统一接口对用户数据进行访问即可。但还存在一个问题,就是用户在这个服务器登录之后,进入另一个服务器的别的模块时,仍然需要重新登录,这就是一次登录,全部通行的问题,映射到技术上,其实就是各个服务器之间如何实现共享 SESSION 数据的问题。

    二、PHP SESSION 的工作原理

    在解决问题之前,先来了解一下 PHP SESSION 的工作原理。在客户端(如浏览器)登录网站时,被访问的 PHP 页面可以使用 session_start() 打开 SESSION,这样就会产生客户端的唯一标识 SESSION ID(此 ID 可通过函数 session_id() 获取/设置)。SESSION ID 可以通过两种方式保留在客户端,使得请求不同的页面时,PHP 程序可以获知客户端的 SESSION ID;一种是将 SESSION ID 自动加入到 GET 的 URL 中,或者 POST 的表单中,默认情况下,变量名为 PHPSESSID;另一种是通过 COOKIE,将 SESSION ID 保存在 COOKIE 中,默认情况下,这个 COOKIE 的名字为 PHPSESSID。这里我们主要以 COOKIE 方式进行说明,因为应用比较广泛。
    那么 SESSION 的数据保存在哪里呢?当然是在服务器端,但不是保存在内存中,而是保存在文件或数据库中。默认情况下,php.ini 中设置的 SESSION 保存方式是 files(session.save_handler = files),即使用读写文件的方式保存 SESSION 数据,而 SESSION 文件保存的目录由 session.save_path 指定,文件名以 sess_ 为前缀,后跟 SESSION ID,如:sess_c72665af28a8b14c0fe11afe3b59b51b。文件中的数据即是序列化之后的 SESSION 数据了。如果访问量大,可能产生的 SESSION 文件会比较多,这时可以设置分级目录进行 SESSION 文件的保存,效率会提高很多,设置方法为:session.save_path="N;/save_path",N 为分级的级数,save_path 为开始目录。当写入 SESSION 数据的时候,PHP 会获取到客户端的 SESSION_ID,然后根据这个 SESSION ID 到指定的 SESSION 文件保存目录中找到相应的 SESSION 文件,不存在则创建之,最后将数据序列化之后写入文件。读取 SESSION 数据是也是类似的操作流程,对读出来的数据需要进行解序列化,生成相应的 SESSION 变量。

    三、多服务器共享 SESSION 的主要障碍及解决办法

    通过了解 SESSION 的工作原理,我们可以发现,在默认情况下,各个服务器会各自分别对同一个客户端产生 SESSION ID,如对于同一个用户浏览器,A 服务器产生的 SESSION ID 是 30de1e9de3192ba6ce2992d27a1b6a0a,而 B 服务器生成的则是 c72665af28a8b14c0fe11afe3b59b51b。另外,PHP 的 SESSION 数据都是分别保存在本服务器的文件系统中。如下图所示:

    image

    确定了问题所在之后,就可以着手进行解决了。想要共享 SESSION 数据,那就必须实现两个目标:一个是各个服务器对同一个客户端产生的 SESSION ID 必须相同,并且可通过同一个 COOKIE 进行传递,也就是说各个服务器必须可以读取同一个名为 PHPSESSID 的 COOKIE;另一个是 SESSION 数据的存储方式/位置必须保证各个服务器都能够访问到。简单地说就是多服务器共享客户端的 SESSION ID,同时还必须共享服务器端的 SESSION 数据。

    第一个目标的实现其实很简单,只需要对 COOKIE 的域(domain)进行特殊地设置即可,默认情况下,COOKIE 的域是当前服务器的域名/IP 地址,而域不同的话,各个服务器所设置的 COOKIE 是不能相互访问的,如 www.aaa.com 的服务器是不能读写 www.bbb.com 服务器设置的 COOKIE 的。这里我们所说的同一网站的服务器有其特殊性,那就是他们同属于同一个一级域,如:aaa.infor96.com 和 www.infor96.com 都属于域 .infor96.com,那么我们就可以设置 COOKIE 的域为 .infor96.com,这样 aaa.infor96.com、www.infor96.com 等等都可以访问此 COOKIE。PHP 代码中的设置方法如下:
    ini_set('session.cookie_domain', '.infor96.com');
    这样各个服务器共享同一客户端 SESSION ID 的目的就达到了。

    第二个目标的实现可以使用文件共享方式,如 NFS 方式,但设置、操作上有些复杂。我们可以参考先前所说的统一用户系统的方式,即使用数据库来保存 SESSION 数据,这样各个服务器就可以方便地访问同一个数据源,获取相同的 SESSION 数据了。

    解决办法如下图所示:

    image

    四、代码实现

    首先创建数据表,MySQL 的 SQL 语句如下:
       CREATE TABLE `sess` (
         `sesskey` varchar(32) NOT NULL default '',
          `expiry` bigint(20) NOT NULL default '0',
          `data` longtext NOT NULL,
          PRIMARY KEY  (`sesskey`),
          KEY `expiry` (`expiry`)
        ) TYPE=MyISAM
    sesskey 为 SESSION ID,expiry 为 SESSION 过期时间,data 用于保存 SESSION 数据。

    默认情况下 SESSION 数据是以文件方式保存,想要使用数据库方式保存,就必须重新定义 SESSION 各个操作的处理函数。PHP 提供了session_set_save_handle() 函数,可以用此函数自定义 SESSION 的处理过程,当然首先要先将 session.save_handler 改成 user,可在 PHP 中进行设置:
    session_module_name('user');

    接下来着重讲一下 session_set_save_handle() 函数,此函数有六个参数:
    session_set_save_handler ( string open, string close, string read, string write, string destroy, string gc )
    各个参数为各项操作的函数名,这些操作依次是:打开、关闭、读取、写入、销毁、垃圾回收。PHP 手册中有详细的例子,在这里我们使用 OO 的方式来实现这些操作,详细代码如下:
    <?php
    define('MY_SESS_TIME', 3600);   //SESSION 生存时长
    //类定义
    class My_Sess
    {
        function init()
        {
            $domain = '.infor96.com';
            //不使用 GET/POST 变量方式
            ini_set('session.use_trans_sid',    0);
            //设置垃圾回收最大生存时间
            ini_set('session.gc_maxlifetime',   MY_SESS_TIME);
    
            //使用 COOKIE 保存 SESSION ID 的方式
            ini_set('session.use_cookies',      1);
            ini_set('session.cookie_path',      '/');
            //多主机共享保存 SESSION ID 的 COOKIE
            ini_set('session.cookie_domain',    $domain);
    
            //将 session.save_handler 设置为 user,而不是默认的 files
            session_module_name('user');
            //定义 SESSION 各项操作所对应的方法名:
            session_set_save_handler(
                array('My_Sess', 'open'),   //对应于静态方法 My_Sess::open(),下同。
                array('My_Sess', 'close'),
                array('My_Sess', 'read'),
                array('My_Sess', 'write'),
                array('My_Sess', 'destroy'),
                array('My_Sess', 'gc')
            );
        }   //end function
    
        function open($save_path, $session_name) {
            return true;
        }   //end function
    
        function close() {
            global $MY_SESS_CONN;
    
            if ($MY_SESS_CONN) {    //关闭数据库连接
                $MY_SESS_CONN->Close();
            }
            return true;
        }   //end function
    
        function read($sesskey) {
            global $MY_SESS_CONN;
    
            $sql = 'SELECT data FROM sess WHERE sesskey=' . $MY_SESS_CONN->qstr($sesskey) . ' AND expiry>=' . time();
            $rs =& $MY_SESS_CONN->Execute($sql);
            if ($rs) {
                if ($rs->EOF) {
                    return '';
                } else {    //读取到对应于 SESSION ID 的 SESSION 数据
                    $v = $rs->fields[0];
                    $rs->Close();
                    return $v;
                }   //end if
            }   //end if
            return '';
        }   //end function
    
        function write($sesskey, $data) {
            global $MY_SESS_CONN;
            
            $qkey = $MY_SESS_CONN->qstr($sesskey);
            $expiry = time() + My_SESS_TIME;    //设置过期时间
            
            //写入 SESSION
            $arr = array(
                'sesskey' => $qkey,
                'expiry'  => $expiry,
                'data'    => $data);
            $MY_SESS_CONN->Replace('sess', $arr, 'sesskey', $autoQuote = true);
            return true;
        }   //end function
    
        function destroy($sesskey) {
            global $MY_SESS_CONN;
    
            $sql = 'DELETE FROM sess WHERE sesskey=' . $MY_SESS_CONN->qstr($sesskey);
            $rs =& $MY_SESS_CONN->Execute($sql);
            return true;
        }   //end function
    
        function gc($maxlifetime = null) {
            global $MY_SESS_CONN;
    
            $sql = 'DELETE FROM sess WHERE expiry<' . time();
            $MY_SESS_CONN->Execute($sql);
            //由于经常性的对表 sess 做删除操作,容易产生碎片,
            //所以在垃圾回收中对该表进行优化操作。
            $sql = 'OPTIMIZE TABLE sess';
            $MY_SESS_CONN->Execute($sql);
            return true;
        }   //end function
    }   ///:~
    
    //使用 ADOdb 作为数据库抽象层。
    require_once('adodb/adodb.inc.php');
    //数据库配置项,可放入配置文件中(如:config.inc.php)。
    $db_type = 'mysql';
    $db_host = '192.168.212.1';
    $db_user = 'sess_user';
    $db_pass = 'sess_pass';
    $db_name = 'sess_db';
    //创建数据库连接,这是一个全局变量。
    $GLOBALS['MY_SESS_CONN'] =& ADONewConnection($db_type);
    $GLOBALS['MY_SESS_CONN']->Connect( $db_host$db_user$db_pass$db_name);
    //初始化 SESSION 设置,必须在 session_start() 之前运行!!
    My_Sess::init();
    ?>

    五、遗留问题

    如果网站的访问量很大的话,SESSION 的读写会频繁地对数据库进行操作,这样效率就会明显降低。考虑到 SESSION 数据一般不会很大,可以尝试用 C/Java 写个多线程的程序,用 HASH 表保存 SESSION 数据,并通过 socket 通信进行数据读写,这样 SESSION 就保存在内存中,读写速度应该会快很多。另外还可以通过负载均衡来分担服务器负载。不过这些都只是我自己的一些想法和假设,并没有实践过 :( 。。。。。。

    Posted by: Nio on 2005-09-13 | 10:05 pm
     
    ////////////////////////////////////////////////
     
    后记:nio把同一域名,多台服务器如何共享SESSION阐述的很清楚了,不过如何才能满足不同域名,不同服务器共享SESSION的功能呢?只要把sessionid通过url传递就可以了,不过这样的化,网站里涉及链接的地方都要在URL上加上sessionid,这也是很烦的一件事情,但是如果你的应用程序采用了前端控制器的写法,再结合 output_add_rewrite_var 函数的使用,应该很容易能够实现。
    11/10/2005

    EVAL的一个注意事项

     
    eval跟include(require)相比速度上没有差别。eval要想执行一个文件,实现跟include(require)同样的效果,需要如下代码,默认文件的内容一开始就是php代码,所以需要先结束一下,然后再打开:
    eval("?>" file_get_contents($filename) . "<?php"); 
    11/8/2005

    PHP责任链的改进

    昨天发的责任链很不爽的一点是:
     
    outbufferFilter.php
     
    <?php
    class OutbufferFilter extends Filter
    {
        function OutbufferFilter($filter = null)
        {
            $this->Filter($filter);
        }

        function doHeadFilter()
        {
            ob_start('ob_gzhandler');
            ob_implicit_flush(0);
            $this->doNextHeadFilter();
        }
     
        function doTailFilter()
        {
            ob_end_flush();
            $this->doNextTailFilter();
        }
    }
    ?>
     
    注意粗体,每个方法都要这样写无疑是很讨厌的事情,所以我改进了一下。
     
    <?php
    class Filter
    {
        var $filter;
        function Filter($filter)
        {
            $this->filter = $filter;
        }
        function headFilter()
        {
            $this->doHeadFilter();
            $this->doNextHeadFilter();
        }
        function tailFilter()
        {
            $this->doTailFilter();
            $this->doNextTailFilter();
        }
        function doHeadFilter()
        {
            trigger_error('Not Implemented');
        }
        function doTailFilter()
        {
            trigger_error('Not Implemented');
        }
        function doNextHeadFilter()
        {
            if(! is_null($this->filter))
            {
                $this->filter->headFilter();
            }
        }
        function doNextTailFilter()
        {
            if(! is_null($this->filter))
            {
                $this->filter->tailFilter();
            }
        }
    }
    ?>
     
    <?php
    class OutbufferFilter extends Filter
    {
        function OutbufferFilter($filter = null)
        {
            $this->Filter($filter);
        }
        function doHeadFilter()
        {
            //ob_start('ob_gzhandler');
            //ob_implicit_flush(0);
            echo 'head:OutbufferFilter<br>';
            //不再调用$this->doNextHeadFilter();
        }
        function doTailFilter()
        {
            echo 'tail:OutbufferFilter<br>';
            //不再调用$this->doNextTailFilter();
        }
    }
    ?>
     
    为啥要去掉这句(//不再调用。。。)??因为对一个方法而言,要不就专注与责任链的实现,要不就专著与责任链的传递,而$this->doNextTailFilter等都属于责任链的传递,这样方法的功能就不单一了。
     
    可能大家会觉得就为了不写这句(//不再调用。。。)花这么大力气值得么?答案是值得,对一个class来说,他中方法的功能在一定的粒度上应该尽可能的单一,如果你的function超过了几百行,你可以说它既可以做这个功能,又可以做那个功能,但我说,它多半是不好的,脆弱的,因为变化的东西多了。这样具有N个功能的CLASS通常叫做god class,我们应该避免。
     
    打个比方,一个class就好像一个房子,我们的房子通常会分很多房间,每个房间做不同的事情,如果是GOD CLASS的化,它就会不分房间,想想多可怕啊,我们在同一个地方吃饭,便便,睡觉,唉,所以还是好好学习设计模式吧,不然你的代码就好像我上面说的餐厅和厕所在一起的房子一样让你恶心。
     
    <?php
    include('filter.php');
    include('outbufferFilter.php');
    include('sessionFilter.php');
    include('fixMagicQuotesFilter.php');
    $filter = new OutbufferFilter(
              new SessionFilter(
              new FixMagicQuotesFilter(
              )));
    $filter->headFilter();
    echo '<br>';
    echo 'something to do<br>';
    echo '<br>';
    $filter->tailFilter();
    ?>
     
    也就是模板模式的使用,哈哈,一个小小的例子结合使用了责任链模式和模板模式,还不错.
     
    //////////////////////////////////////
     
    补充:通过这几天我的思考来看,我这个改进是有问题的,因为这样的话,传递就变成无条件的了。唉
    11/7/2005

    php中责任链的实现

    受我朋友人月神话(QQ:45243298)的点拨,我写了下面这些代码,来搞一个php中责任链的实现,至于什么是责任链,它的作用好处是什么我就不说了,自己google一下就好了:
     
    ----------------------
    filter.php
     
    <?php
    class Filter
    {
        var $filter;
     
        function Filter($filter)
        {
            $this->filter = $filter;
        }
     
        function doHeadFilter()
        {
            $this->doNextHeadFilter();
        }
     
        function doTailFilter()
        {
            $this->doNextTailFilter();
        }
     
        function doNextHeadFilter()
        {
            if(! is_null($this->filter))
            {
                $this->filter->doHeadFilter();
            }
        }
     
        function doNextTailFilter()
        {
            if(! is_null($this->filter))
            {
                $this->filter->doTailFilter();
            }
        }
    }
    ?>
     
    ----------------------
    outbufferFilter.php
     
    <?php
    class OutbufferFilter extends Filter
    {
        function OutbufferFilter($filter = null)
        {
            $this->Filter($filter);
        }

        function doHeadFilter()
        {
            ob_start('ob_gzhandler');
            ob_implicit_flush(0);
            $this->doNextHeadFilter();
        }
     
        function doTailFilter()
        {
            ob_end_flush();
            $this->doNextTailFilter();
        }
    }
    ?>
     
    ----------------------
    sessionFilter.php
     
    <?php
    class SessionFilter extends Filter
    {
        function SessionFilter($filter = null)
        {
            $this->Filter($filter);
        }

        function doHeadFilter()
        {
            if(session_id())
            {
                session_write_close();
            }
            session_cache_limiter('private');
            session_start();
            $this->doNextHeadFilter();
        }
     
        function doTailFilter()
        {
            session_write_close();
            $this->doNextTailFilter();
        }
    }
    ?>
     
    ----------------------
    fixMagicQuotesFilter.php
     
    <?php
    class FixMagicQuotesFilter extends Filter
    {
        function FixMagicQuotesFilter($filter = null)
        {
            $this->Filter($filter);
        }

        function doHeadFilter()
        {
            if(get_magic_quotes_gpc())
            {
                function fixMagicQuotes($param)
                {
                    return is_array($param) ?
                               array_map('fixMagicQuotes', $param) : stripslashes($param);
                }
                $_GET     = array_map('fixMagicQuotes', $_GET);
                $_POST    = array_map('fixMagicQuotes', $_POST);
                $_COOKIE  = array_map('fixMagicQuotes', $_COOKIE);
                $_REQUEST = array_map('fixMagicQuotes', $_REQUEST);
            }
            $this->doNextHeadFilter();
        }
    }
    ?>
     
    ----------------------
    index.php
     
    <?php
    include('filter.php');
    include('outbufferFilter.php');
    include('sessionFilter.php');
    include('fixMagicQuotesFilter.php');
     
    $filter = new OutbufferFilter(
              new SessionFilter(
              new FixMagicQuotesFilter(
              )));
     
    $filter->doHeadFilter();
     
    //something to do
     
    $filter->doTailFilter();
    ?>
     
    ----------------------
     
     
    9/27/2005

    递归的mkdir

    function makeDir($param)
    {
        if(! file_exists($param))
        {
            makeDir(dirname($param));
            mkdir($param);
        }
        return realpath($param);
    }
    7/15/2005

    Path Disclosure and PHP

    In the past few days I've been testing a number of my own applications and scripts as well as various bits and pieces of applications written by others that I use, using an automated scanning tool I have written. One particular issue I came across, common to all applications is the inevitable "path disclosure vulnerability". The premise behind this so called vulnerability is that remote attackers by specifying certain value can make the script report it's own location on disk. Theoretically this combined with another vulnerability could be used to do *something* or rather then potentially could be bad. As you can probably tell, I don't see this as a something to be terribly concerned about in most cases.

    To demonstrate consider the following, most PHP function that take strings as parameters, like our favorite validation functions such as htmlspecialchars(), addslashes(), and so on will raise warnings when values they are passed are arrays rather then strings. This means that to get the path of the script, you simply need to pass the arguments as arrays rather then expected strings and then simply read the warning message generated by PHP to see the error.

    PHP:

    <?php

    // script.php?name[]=foo instead of expected script.php?name=foo

    $name htmlspecialchars($_GET['name']);

    // will emit: 
    // Warning: htmlspecialchars() expects parameter 1 to be string, 
    // array given in /path/to/script.php on line  2

    ?>



    To solve this problem there are just two solutions:
    1) Disable displaying of errors to screen and instead log them to a file, so if the Warning or Notice regarding array being used as a string is emitted, it won't be shown to the user using the site. This can be done within the script through:
    PHP:

    <?php

    ini_set
    (“display_errors”0);
    ini_set(“log_errors”1);
    ini_set(“error_log”/path/to/php/errors”);

    ?>



    2) Meticulously go through the code forcing PHP to cast the data to the desired type, via (int), (float) or (string) casts, this too will eliminate the Notice or Warning message.
    7/13/2005

    实战安装LAMP

    Linux采用的是RedHat9.0.x版本,它的安装不属于本文的讨论范围
    Apache采用的是1.3.x版本,如果你用的是2.x版本的话,安装步骤会有一点不同
    mysql采用的是4.1.x版本,这个版本增加了很多功能
    php采用的是4.4.x版本,修改了若干的Bug
    现在我们假设Linux已经安装好了,我们先来安装Mysql
    shell> groupadd mysql
    shell> useradd -g mysql mysql
    shell> gunzip < mysql-VERSION.tar.gz | tar -xvf -
    shell> cd mysql-VERSION
    shell> ./configure --prefix=/usr/local/mysql
    shell> make
    shell> make install
    shell> cp support-files/my-medium.cnf /etc/my.cnf
    shell> cd /usr/local/mysql
    shell> bin/mysql_install_db --user=mysql
    shell> chown -R root  .
    shell> chown -R mysql var
    shell> chgrp -R mysql .
    shell> bin/mysqld_safe --user=mysql &
    好了,Mysql安装完成了,是不是很easy
    下面安装Apache和Php
    1.  gunzip apache_xxx.tar.gz
    2.  tar -xvf apache_xxx.tar
    3.  gunzip php-xxx.tar.gz
    4.  tar -xvf php-xxx.tar
    5.  cd apache_xxx
    6.  ./configure --prefix=/www --enable-module=so
    7.  make
    8.  make install
    9.  cd ../php-xxx
    10. Now, configure your PHP.  This is where you customize your PHP
        with various options, like which extensions will be enabled.  Do a
        ./configure --help for a list of available options.  In our example
        we'll do a simple configure with Apache 1 and MySQL support.  Your
        path to apxs may differ from our example.
          ./configure --with-mysql --with-apxs=/www/bin/apxs
    11. make
    12. make install
        If you decide to change your configure options after installation,
        you only need to repeat the last three steps. You only need to
        restart apache for the new module to take effect. A recompile of
        Apache is not needed.
     
        Note that unless told otherwise, 'make install' will also install PEAR,
        various PHP tools such as phpize, install the PHP CLI, and more.
    13. Setup your php.ini file:
          cp php.ini-dist /usr/local/lib/php.ini
        You may edit your .ini file to set PHP options.  If you prefer your
        php.ini in another location, use --with-config-file-path=/some/path in
        step 10.
       
        If you instead choose php.ini-recommended, be certain to read the list
        of changes within, as they affect how PHP behaves.
    14. Edit your httpd.conf to load the PHP module.  The path on the right hand
        side of the LoadModule statement must point to the path of the PHP
        module on your system.  The make install from above may have already
        added this for you, but be sure to check.
           
        For PHP 4:
               
          LoadModule php4_module libexec/libphp4.so
        For PHP 5:
                         
          LoadModule php5_module libexec/libphp5.so
         
    15. And in the AddModule section of httpd.conf, somewhere under the
        ClearModuleList, add this:
       
        For PHP 4:
       
          AddModule mod_php4.c
         
        For PHP 5:
       
          AddModule mod_php5.c
    16. Tell Apache to parse certain extensions as PHP.  For example,
        let's have Apache parse the .php extension as PHP.  You could
        have any extension(s) parse as PHP by simply adding more, with
        each separated by a space.  We'll add .phtml to demonstrate.
          AddType application/x-httpd-php .php .phtml
        It's also common to setup the .phps extension to show highlighted PHP
        source, this can be done with:
       
          AddType application/x-httpd-php-source .phps
    17. Use your normal procedure for starting the Apache server. (You must
        stop and restart the server, not just cause the server to reload by
        using a HUP or USR1 signal.)
    这样安装就完成了,下面我们可以用一下几种方法来启动apache服务
    1. Several Linux and SysV variants:
    /etc/rc.d/init.d/httpd restart
    2. Using apachectl scripts:
    /path/to/apachectl stop
    /path/to/apachectl start
    3. httpdctl and httpsdctl (Using OpenSSL), similar to apachectl:
    /path/to/httpsdctl stop
    /path/to/httpsdctl start
    4. Using mod_ssl, or another SSL server, you may want to manually
    stop and start:
    /path/to/apachectl stop
    /path/to/apachectl startssl