• 2008-04-11

    Zend Framework 的缓存模块 Zend_Cache 使用

    Views: 24685 | No Comments

    缓存有几个重要的概念:

    • 数据本身
    • 数据的标识
    • 缓存生命期
    • 缓存操作接口

    缓存的获取操作一般十分快速, 而数据库操作一般是昂贵的, 所以通过缓存可以提高系统的整体性能.

    Zend_Cache 的使用比较简单, 它可以把数据保存到 File, Memcache, SQLite 等介质(称为后端, Backend)中. 还有前端(Frontend), 主要用来对要缓存的数据进行转换, 如序列化.

    ==data==>Frontend==>mediate data==>Backend==>File, Memcache, etc

    使用例子:

    <?php
    // "load" Zend_Cache 工厂 
    require 'Zend/Cache.php'; 
    
    // 选择一个前端(例如'Core', 'Output', 'Page'...)
    $frontendName = 'Core';
    
    // 选择一个后端(例如'File'或者'Sqlite'...)
    $backendName = 'File';
    
    // 为选择的前端设置一个选项数组
    // 前端把要缓存的数据序列化, 这样就可以缓存数组, 对象的实例等数据.
    $frontendOptions = array('automatic_serialization'=>true);
    
    // 为选择的后端设置一个选项数组
    // 缓存文件被保存在 ./cache 目录下.
    $backendOptions = array('cache_dir' => './cache');
    
    // 创建实例(当然,最后两个参数是可选的)
    $cache = Zend_Cache::factory($frontendName, $backendName, $frontendOptions, $backendOptions);
    
    // 数据的标识
    $id = 'mydata';
    $data = $cache->load($id);
    if(!$data){
        // 缓存中没有需要的数据(缓存未命中), 所以需要从数据库或者其它地方把数据放入缓存.
        // 这里的代码是耗时和耗资源的.
        $data = query_database();
        $tags = array();
        // 缓存的生命期为 10 秒.
        $lifetime = 10;
        $cache->save($data, $id, $tags, $lifetime);
    }
    
    // 对数据进行处理.
    manipulate(data);
    ?>
    
    Posted by ideawu at 2008-04-11 11:56:02 Tags:
  • 2008-03-19

    Zend Framework 有优点在哪?

    Views: 25622 | 1 Comment

    今天我使用 Zend Framework(以下简称 ZF) 制作一个数据库操作的 Demo.

    在制作 Demo 中, 我未体会到 ZF 有明确的核心思想. 我根据 ZF 的要求写了单独的一个文件(控制器), 然后创建了单独的一个模板文件(事实上是一个普通的 PHP 文件), 而且还从 Zend_Db_Table 派生了一个类(只有类的框架, 没有任何逻辑代码), 该类拥有了 CRUD 能力.

    和不使用 ZF 相比, 这样做从代码量上没有任何减少, 甚至结构上没有任何改变. $_GET['id'] 变成了 $this->_request->getParam('id'); include('header.html') 变成了 $this->reader('header.html'); 没有了 SQL 字符串拼接, 但是为了保存一行数据仍然需要手工创建一个关联数组.

    那么 ZF 在界面层提供了什么工具? 视图文件还是普通的 PHP 脚本, 为了显示一个数组, 仍然需要写自己的

    <?php foreach...{?>
    ...
    <?php}?>

    在 1.0.5 版本的中, 增加了使用 PHP 脚本方式生成界面的 Zend_Form 模块, 完全地将表单的验证等逻辑处理与表单的界面耦合在一个 PHP 类中. 这样做, 根本就是违反了表现与结构分离的原则, 界面开发人员无法单独的设计界面. 而分离的一个重要目标就是让界面开发人员不需要 Web 服务器就能设计界面.

    由于个人的局限性, 所以我希望 ZF fans 能交流你们对 ZF 的看法.

    Posted by ideawu at 2008-03-19 21:19:09 Tags:
  • 2007-10-07

    所谓的重新发明轮子

    Views: 18059 | 1 Comment

    "不要重新发明轮子"是从国外IT界流传进来的一句话, 目的是告诫开发者尽量使用现有的技术和组件, 不要随意重新发明这些技术和组件. 但是, 国内的IT论坛上似乎过度使用了这种思想. 常常在论坛上看到一些人在别人通过重新发明轮子来研究某项技术时, 叫嚷着"不要发明轮子", 弄得仿佛他自己就是国外的专家的闭门弟子一样. 所以我从来没有使用过"不要重新发明轮子"这句话.

    最近, 我在看国内使用者较多的某个PHP程序时, 发现作者似乎在故意卖弄地"重新发明轮子", 所以我不得不使用"不要重新发明轮子"来指责他们.

    问题出现了某个函数的参数传递上面. 因为该函数接受近10个参数, 所以作者把只接受一个字符串作为参数, 然后自己在函数中解析这些字符串. 正如注释中写的, "本方法对传递进来的参数进行解析".

    这太愚蠢了! 无论你使用PHP解析字符串的功夫再怎么高明, 难道你比语言的编译器解析得更高效吗? 而且, 那个作者的解析明显就很拙劣. 他的解析是这样的: 输入形式为类似"a:1;b:1,2;c:1-2"之类的, 大概表示a=1, b=[1,2], c='1-2'. 很明显的一个缺点就是不支持空白字符. 事实上, 如果作者确实需要参数解析, 完全可以使用URL编码, 然后使用PHP内置的URL解码功能. 或者最合适的方法是将关联数组作为参数, 这样调用:

    f(array('a'=>1, 'b'=>array(1,2), 'c'=>'1-2'));

    不管怎么说, 该作者"重新发明轮子"发明得太笨拙了! 可能"不要重新发明轮子"的本意是, 如果你无法发明功能和原有的一样, 但是实现简单或者产品的性质更好, 那么就不要这么做. 并不是说不要重新发明功能一样的东西.

    Posted by ideawu at 2007-10-07 09:36:02
  • 2007-08-18

    fopen 函数的设计

    Views: 16813 | 2 Comments

    最近, 我要使用 PHP 的随机写文件功能, 但是, 竟然找不到. 我当时知道, PHP 的 fopen 和 C 语言中的同名函数行为差不多, 参数 w 会把原文件的内容删除, a 又只能追加. 我想当然地认为参数 r 只能是读, 即使是 r+ 也是读, 因为 r=read.

    事实上, r+ 是随机读写的功能! 我不知道这样的参数设计的原因, 但是如果按照我的思维, 我会这样设计:

    r: 只读
    w: 只写
    a: 只追加
    +: 如果不存在则创建
    

    r+ 和 wa 被禁止, 因为读操作不应该创建一个文件, 而只写和只追加在语义上有冲突.

    回到这件事上, 我的问题是太想当然, 导致看手册不小心.

    Posted by ideawu at 2007-08-18 10:11:02
  • 2007-08-15

    通过 HTTP POST 发送二进制数据

    Views: 37293 | No Comments

    我暂时还不清楚 HTTP 文件上传的具体细节, 但是我知道通过浏览器上传文件, 然后用 PHP 接收, 需要使用 PHP 的预定义变量 $_FILES. 最近我有一个应用需要使用 PHP 的预定义变量 $_POST 获取上传的文件(或者任何数据), 也就是通过 HTTP POST 参数传递二进制数据.

    工作在 ContentType = "application/x-www-form-urlencoded" 的模式下时, HTTP 协议使用 ASCII 字符集的一个子集来编码要传输的字节流. 如字符串 "a@" 如果以 ASCII 编码存在于内存中(以字节数组的形式), 也就是内存按字节读取为 HEX 是: 0x61 0x40, 那么该字节数组被传输时会被编码(URL 编码)成为 0x61 0x25 0x34 0x30, 被编码后字节数组如果按照 ASCII 编码显示, 就是 "a%40". 也就是说, 当你想传输 0x61 0x40 时, HTTP 传输的是 0x61 0x25 0x34 0x30.

    这种编码过程几乎所有的编程语言都提供支持, 在 C# 中你可以使用 HttpUtility.UrlEncode(), 输入一个字节数组, 返回一个 URL 编码的 字节数组.

    在 PHP 脚本中, 当你使用 $_POST['data'] 获取数组时, 该数组已经被 PHP 自动解码了. 按照上面的例子, $_POST['data'] 所指向的内存内容为 0x61 0x40 -- 但是, 默认情况下并不总是, 如果该数据包含单引号等少数几个字符, 那么它们的前面会被 PHP 加上 \.

    下面先给出 PHP 脚本, 保存一个通过 HTTP 参数传递的文件:

    <?php
        $filename = $_POST['filename'];
        $data = $_POST['data'];
        if(get_magic_quotes_gpc()){
            // 去掉斜杠
            $data = stripslashes($data);
        }
        file_put_contents($filename, $data);
    ?>
    

    C# 客户端为:

    using System;
    using System.Text;
    using System.Net;
    using System.Web;
    using System.IO;
    
    class Test
    {
        public static void Main(){
            try{
                WebResponse response;
                HttpWebRequest request;
                request = (HttpWebRequest)HttpWebRequest.Create("http://localhost/test.php");
                request.Method = "POST";
                request.ContentType = "application/x-www-form-urlencoded";
    
                String filename = "test.jpg";
                // HTTP 参数名
                byte[] keyBytes = Encoding.UTF8.GetBytes(String.Format("filename={0}&data=", filename));
    
                FileStream fs = new FileStream(filename, FileMode.Open);
                byte[] buffer = new byte[8192];
                int n = fs.Read(buffer, 0, buffer.Length);
                byte[] dataArray = new byte[n];
                Array.Copy(buffer, dataArray, n);
                dataArray = HttpUtility.UrlEncodeToBytes(dataArray);
    
                request.ContentLength = keyBytes.Length + dataArray.Length;
    
                Stream dataStream = request.GetRequestStream();
                // 发送参数名
                dataStream.Write(keyBytes, 0, keyBytes.Length);
                // 发送名为 data 对应的值.
                dataStream.Write(dataArray, 0, dataArray.Length);
                dataStream.Close();
    
                response = (HttpWebResponse)request.GetResponse();
                // 读取服务器的返回, 在本应用中, 如果正常, 返回空字符串.
                StreamReader sr = new StreamReader(response.GetResponseStream(), Encoding.GetEncoding("UTF-8"));
                Console.Write(sr.ReadToEnd());
    
                response.Close();
            } catch (Exception e) {
                Console.Write("Error: " + e.ToString() + "\r\n");
            }
        }
    
    }
    

    如果你要测试的文件小于 8K, 可以直接使用上面的程序测试. 如果大于, 你可以加大 buffer 的容量.

    上传成功后, 使用文件比较工具对比两个文件是否一致:

    diff test.jpg up_test.jpg
    

    这种方式的典型的应用是自定义文件上传, 你只需要更改少量的代码就可以实现支持断点续传功能的文件上传.

    Posted by ideawu at 2007-08-15 11:21:02
  • 2007-08-12

    PHP str_replace 函数的参数设计不合理之处

    Views: 18521 | 6 Comments

    PHP str_replace 函数的声明大概是这样: str_replace($search, $replace, $input[, &$count]), 比如在对一个字符串进行替换操作, $input 就是源字符串(称为数据源). 这很不合理, 因为它把数据源放在第3位, 而 str_pos, strtok, str_repeat 等等函数都是把数据源放在第1位.

    Posted by ideawu at 2007-08-12 15:42:52
|<<<2345678910>>>| 8/10 Pages, 55 Results.