难以抚平的悔恨

2009-5-10,对我来说是个灾难一样的日子。在 host-monster.com 买的虚拟主机,因为服务器故障,主机商清空了所有用户的所有数据库和文件。

而主机商提供的备份服务,也因我占用的磁盘空间超过4G,而拒绝备份,只保留了几天前的少数数据库的备份。

幸运的是,我的 www.leakon.com 数据库得以保全,这是我 2 年多积攒的全部心血。

遗憾的是,我 GF 的 www.xyoyou.com 的数据大部分已经无法恢复。

这里多亏了抓虾,在他那里保存了 yoyo 的全部日志的标题,摘要和日期。

然后我用 google 的 site:xyoyou.com + 标题 的方式,一篇一篇从快照中找回了全部原文。当然也要感谢 Baidu,Google 搜不到的,恰好 Baidu 可以找到~

其实这也怪不得别人,是我没有把数据备份好,我以为美国的付费主机的服务是可以信赖的,事实证明没有任何服务是可信赖的。

近一年看了一些历史方面的书,得到一个被验证了无数次的理论:问题总是出现在没有问题的时候。

一个人有一个计划要实施,其中有 10 个重要环节,这里面有 9 个在他看来是会遇到麻烦的,有 1 个在他看来是 100% 没有问题的。

而结果往往是这个人失败,调查原因的时候,又发现就是那最不可能出现问题的环节出了大问题,另外 9 个环节顺利执行。

现实点的例子,吴敬中最信任的人是余则成,对李涯总是不冷不热。事实呢,保密局最大的间谍就是那个最受信任的余则成。我非常为李涯感到惋惜,他忠心耿耿,真可谓是鞠躬尽瘁,死而后已。

现在我能做的,就是尽可能挽回损失,减少损失。不再信任任何标榜为“可靠”的服务。

计划做 3 台服务器互为热备,在家用一台服务器每日备份 3 台线上服务器所有数据,每周刻多分 DVD 保存,并把 DVD 邮寄至不同住处。

目前能做的也就这些了,希望我的教训给朋友们一个警示,自己保证数据安全,不要相信任何人。

残酷的现实,让我很不情愿地接受一个新的态度:

这个世界上,唯一能让我信赖的,就是我自己。

特别的日子

结束了杭州5日游,结束了上一份值得回味的工作,休息了几天,回到自己久别的家。

在路上,突然想到今天是个特别的日子。不是今天特别,而是今天加上6个月,我就27了。

很早以前,我有个计划,给自己6个月的时间,从零开始,专心做一个网站。

现在我已经放下了所有的事,从今天开始,执行我的计划。

就算鸣志吧,6个月后看结果!

[童话] 河蟹 蜘蛛 墙

一只河蟹在路上走,抬头一看,一只蜘蛛正在墙角织网。

河蟹:你这网备案了吗?
蜘蛛:啥,我织个网还要备案啊?
河蟹:当然要备案了,将来要是出个啥事,也能查到这是谁的网啊!
蜘蛛:……
河蟹:赶紧停下去备案,没有备案你的网就不能织,织好我也得给你剪断。

河蟹晃了晃他的两个大钳子,蜘蛛慌了神,赶紧屁颠屁颠跑去备案。蜘蛛还是第一次去这个部门,他发现该部门是一群蜗牛在负责。

好多天过去了,蜘蛛的备案还没拿到,去蜗牛那里问了好几次,都说正在处理。蜘蛛太饿了,于是偷偷织好了自己的网。

有一天早上起来,蜘蛛伸了个懒腰,去吃早餐,结果发现自己的网不见了。邻居们告诉他,河蟹二话不说,直接就把网给剪了。

蜘蛛很生气,真想找河蟹去拼命,可是想想河蟹的大钳子,还是作罢了。

好心的草泥ma偷偷告诉蜘蛛,墙外边织网是不需要备案的。蜘蛛很高兴,虽然墙很高很高,他还是努力爬了过去。果然,墙外风和日丽,景色无限,重要的是没有可恶的河蟹来烦人。

可是时间长了,蜘蛛又开始有点怀念了,墙里面有些虫子美味又弱智,想起来就流口水。可惜这墙太高了,这些弱智的虫子不知道怎么飞出来。

于是蜘蛛翻过墙回来,果然不出他所料,蜗牛们已经审议通过了他的备案。蜘蛛迅速在老地方织了一张新网,墙内的虫子们真是又傻又美味啊!

有一天中午,蜘蛛突然感觉到他的网剧烈颤动,睁眼一看,一对正在xxoo的苍蝇不小心撞到了网上。蜘蛛已经吃饱了,正好正午骄阳似火烤得受不了,他笑着骂了一声“傻X”,就躲回洞里睡午觉去了。

迷糊中突然听见有人喊,蜘蛛揉着眼出来一看,原来是河蟹。

河蟹在下面指着那一对苍蝇,歇斯底里地朝他嚷:“你看看,你看看!多低俗!多色情!你有没有社会公德心啊?!你这多损害青少年身心健康你知道吗?”

说完,河蟹挥动自己的钳子,把蜘蛛的网剪了个粉碎。

蜘蛛心疼地看着被剪碎的网,心想:“我纳税养着你,你怎么能这么没人性呢。”可是他又对河蟹无可奈何,一番思索之后,他和草泥ma依依惜别,爬到了墙外。

蜘蛛从此过上了幸福的生活。

[注] 一般人都认为蜘蛛就一直待在蛛网的中间,本童话中蜘蛛钻进洞里休息的部分有悖科学事实。实际上,蜘蛛也在进化,蛛网也在发展,现在已经进入 Web 2.0 时代了,大量蜘蛛在织网时都会因地制宜织一个洞。欲了解更多 Web 2.0 知识,请参考这里

转自:http://qingbo.org/archives/586.html

Tags: ,

FCKeditor 2.6 安装 配置 问题 图片上传

今天给使用 Symfony 的项目装了 FCKeditor 编辑器,配了图片上传。

遇到一些问题,总结下。

安装时,为了简单,最好把解压的 fckeditor 文件夹放在 web/ 目录下。

在模板中加入编辑器很简单:

$webDir = sfConfig::get(‘sf_web_dir’); // DocumentRoot 的服务端路径
$editorInclude = $webDir . “fckeditor/fckeditor.php”;
require_once($editorInclude);

$oFCKeditor = new FCKeditor(‘detail’) ; // form 表单字段名称是 detail
$oFCKeditor->BasePath = ‘/fckeditor/’ ; // 客户端访问编辑器资源文件的路径
$oFCKeditor->Width = ‘100%’; // 宽
$oFCKeditor->Height = ‘100%’; // 高
$oFCKeditor->Value = $articleItem->detail; // $articleItem 是 ORM 对象,直接引用 detail 字段
$oFCKeditor->Config = array(
        ‘AutoDetectLanguage’ => false, // 关闭浏览器语言自动检测
        ‘DefaultLanguage’ => ‘zh-cn’ // 设置简体中文
);
$oFCKeditor->Create() ;

设置完了,刷新页面,直接可用!很简单。

已经检查的问题,可以很好地处理 XSS 问题,在编辑器里填写 <script>alert(123)</script> 这样的字符串,都会转义成纯字符,不会被浏览器解析为 HTML 代码。这在编辑器和最终页面都可以正常显示,需要注意的是输出的时候都要加 htmlspecialchars 转义,这是必须养成的习惯!

图片上传的问题。

默认是关闭的,修改 fckeditor/editor/filemanager/connectors/php/config.php 为:

$Config[‘Enabled’] = true;

$Config[‘UserFilesPath’] = ‘/uploads/’; // 这是 Symfony 自动生成的目录。

现在可以上传了,不过会遇到 2 个问题。

问题 1:点击“发送到服务器上”按钮,浏览器 alert 一个错误,显示“Error creating folder “redirect:/index.php” (Can’t create redirect: directory)”。参考了“在symfony中使用FCKeditor上传图片附件” 这篇文章,得到了很好的解决。是 .htaccess 造成的。最简单的办法是在 uploads 目录下再放置一个 .htaccess,关闭 rewrite 即可,内容如下:

Options +FollowSymLinks +ExecCGI
<IfModule mod_rewrite.c>
  RewriteEngine Off
</IfModule>

问题 2:上传文件后修改文件名。fckeditor 默认是保留原有文件名。英文名的文件还勉强能用,如果是中文文件名就麻烦了。最好是统一按照自己的命名规则修改文件名。这时编辑 fckeditor/editor/filemanager/connectors/php/io.php,修改一个函数:

function SanitizeFileName( $sNewFileName ) {
  $arr = explode(‘.’, $sNewFileName);
  $ext = array_pop($arr);
  $filename = date(‘Ymd_His_’) . rand(1000, 9999) . ‘.’ . $ext;
  return $filename;

把原函数改名作为备份。这函数就是保留了扩展名,把主文件名按照日期编码。呃,为了更好地保存文件,其实还应该把扩展名统一大小写,我记得有一个 pathinfo 函数,返回值有一个 extension 字段就是扩展名。其实过程都差不多,您自选吧。

总的说来,fckeditor 给我的感觉是安装简单,功能强大,代码逻辑清晰,便于修改。

赞一个~~

另:推荐一个 Blog

4’s symfony blog

这兄弟很深入地了解 Symfony 框架,我已订阅了他的 RSS,每天学习中……

睡了,晚安……

Tags:

软件架构师应该读上两遍的十篇论文

看到别人写的标题是:所有程序员都应该至少读上两遍的十篇论文。

我看了下英文原版地址:

10 Papers Every Software Architect Should Read (At Least Twice)

我觉得应该翻译成软件架构师~~

原文引用如下:

Earlier today I read a post by Michael Feathers Called “10 Papers Every Developer Should Read (At  Least Twice). I knew some of the articles mentioned there and learnt about few interesting ones.I liked it so much,  I thought I’d compile a similar list for software architects – based on stuff I read over the years.

1. The Byzantine Generals Problem (1982) by Leslie Lamport, Robert Shostak and Marshall Pease – The problem with distributed consensus
2. Go To statements considered harmfull (1968) – by Edsger W. Dijkstra – Didn’t you always want to know why ? 🙂
3. A Note on Distributed Computing (1994) – by Samuel C. Kendall, Jim Waldo, Ann Wollrath and Geoff Wyant – Also on Michael’s list but it is one of the foundation papers on distributed computing
4. Big Ball of Mud (1999) – Brian Foote and Joseph Yoder – patterns or anti-patterns?
5. No Silver Bullet Essence and Accidents of Software Engineering (1987) – Frederick P. Brooks – On the limitations of Technology and Technological innovations.
6. The Open Closed Principle (1996) – Robert C. Martin (Uncle Bob) – The first in a series of articles on Object Oriented Principles (you remember the debate on SOLID...)
7. IEEE1471-2000 A recommended practice for architectural description of software intensive systems (2000) various- It is a standard and not a paper but it is the best foundation for describing a software architecture I know.
8. Harvest, Yield, and Scalable Tolerant Systems (1999) Armando Fox, Eric A. Brewer – That’s where the CAP theorem was first defined
9. An Introduction to Software Architecture (1993) – David Garlan and Mary Shaw – one of the foundation articles of software architecture field (although based on earlier work by the two)
10. Who Needs an Architect? (2003) Martin Fowler – Do we or don’t we?

I could come up with quite a few more articles not to mention books that aren’t in this list. However these are definitely some of the most influential papers I read.

我得好好读读~~

Tags: ,

Apache ReWrite QUERY_STRING 问号 ?

看一条应用中简单的 rewrite 规则:
将请求:
http://www.leakon.com/soft/install?ver=2.0
rewrite 为:
http://www. leakon.com/my/soft/install.php

配置文件 httpd.conf 加上如下配置:

RewriteCond %{QUERY_STRING} ^ver\=([09]+\.[09]+)?$ [NC]
RewriteRule ^/soft/install$ http://www. leakon.com/my/soft/install.php?[L]

请注意配置中的两个问号。

请求时,从 QUERY_STRING 取得 url 的查询字符串,这里只获取了 ver 的值,而且限定是带浮点的数字,ver 之后的查询字符都被忽略。实际上是用到 ? 来终结。

官方文档解释为:
注意:查询字符串
Pattern 不会按照查询字符串进行匹配。为了达到这个目的,你必须使用一个带有 %{QUERY_STRING} 变量的 RewriteCond 指令。

当 然,你也可以在替换字符串中创建包含查询字符串的 url:在替换字符串中使用问号,以标明其后的部分应该被重新注入到QUERY_STRING中。

而要删除一个已有的请求串,则可以用问号来终结替换字符串。为了联合新旧查询字符串,请使用[QSA]标志。

Tags:

金山 间谍门 内鬼 剑侠情缘网络版 虚拟币 大银票

这事儿值得反思。

看不懂标题的请 Google 一下。

前几年,每天中午吃饭时我都看 BTV-3 的法制进行时,有时候看到好多年纪轻轻,正值事业和人生发展的黄金期的同龄人,穿着黄色的号衣接受记着采访。这时我会非常仔细地看他们的眼睛和面部表情,从中可以看到深深的悔恨。

从小到大,谁没犯过错误?只是我们犯得都是小错,不会付出很大代价。

当你真的穿上那XX看守所的囚服,听法官宣判你的N年刑期时,是否能意识到代价有多么沉重?

我的朋友也曾经聊过,说做某某事可以捞到很多钱,一般抓不到,没事儿。

我的回应都是:这钱看似容易挣,背后的风险,可能只有蹲大牢的人才能知道,不过他已经没有机会亲自告诉你了。

前几个月,那个donews的刘韧,不是被老周设套,让天朝的公安给抓个现行么。

这哥们按说挺NB的,掌管着一个在业内知名的网站,手里应该也有很多资源。

估计他也不缺那几万块钱,到了那种阶段和层次的人,来钱的方法已经很多。

要说,他应该也是很风光了。肯定是有房有车有地位,别人羡慕得不得了。至少我很羡慕,我至今连200块钱的二手自行车都舍不得买。

但今天呢,我不羡慕他了,我开始鄙视他,嘲笑他,感谢他以身试法,给我做了个反面教材,让我别去犯类似的错误。

用商人常用的思维去考虑,咱算算成本和收益,看看回报率是什么样的。

这哥们个人资产相信不在200w以下,估计说少了,我孤陋寡闻,权且算这么多。

犯事儿了,大牢至少要蹲 3、5 年,出来差不多2012年了,据说那会儿城铁都修到亦庄了。

这位小哥出事儿前,从业也有3、4年吧。蹲大牢按4年算,事前也按4年从业算,等于是8年。

200 除以 8,合着每年 25 万的收入。也就一高级程序员的收入。

4年后,普通程序员估计年薪肯定超30w了,这哥们放出来后得重新找工作。

而且已经臭名昭著,估计200年后,这哥们的事迹能进教科书,至少也能在搜索引擎上找到。

要是人脉够硬,也许很快能“再造辉煌”。不过按中国人5000年来长期养成的“墙倒众人推,破鼓万人锤”的优良传统,这位没有利用价值的仁兄,前途实在渺茫。

话题回到“金山间谍门”。

几个月前活生生的例子摆在眼前,还是有人铤而走险。

我看到新闻,对里面那哥们儿“买宝马”的事儿比较感兴趣。估计刚买车那会儿,在他哥们儿和大美妞儿面前很是风光了一把。现在呢,这些男男女女们,看法治进行时的时候也许会跟家人朋友说上这么一句:“这孙子我认识,丫那会儿不是NB么,这回SB了吧,活该!”

不是我心里阴暗,这事儿实在是正常不过了。

有句话说,风险越大,回报越大。

我觉得,炒股票这么说还可以,拿自己的前途去赌,我看还是当回懦夫吧。

要说NB,赖昌星,陈良宇,比这帮孙子NB多了,现在呢,赖哥跑路了,连家都回不了。陈叔不知干嘛呢,反正不如以前NB了。我高中语文老师蔡先生说过一句名言:谁疼谁知道。我看相当精辟。

其实,本本分分做人,踏踏实实做事,挺好的。

只要功夫深,铁杵磨成针。

前人总结的经验,至今依然灵验。

只要付出努力,一样可以有好的回报。

急功近利,铤而走险,君子不为!

Tags: ,

What is Dependency Injection? 依赖注入?

转自 Symfony-Project 的创始人:Fabien Potencier。

查了下中文,貌似应该翻译成 “依赖注入”?

This article is the first of a series on Dependency Injection in general and the implementation of a Dependency Injection Container in PHP.

Today, I won’t talk about the container yet as I first want to introduce the concept of Dependency Injection with some concrete examples that will hopefully demonstrate the problems it tries to solve and the benefits it gives to the developer. If you already knows the concept of Dependency Injection, you can safely skip this article and instead wait for the next one.

Dependency Injection is probably one of the most dead simple design pattern I know. And odds are you have probably already used Dependency Injection. But it is also one of the most difficult one to explain well. I think it is partly due to the nonsense examples used in most introductions to Dependency Injection. I have tried to come up with examples that fits the PHP world better. As PHP is a language mainly used for web development, let’s take a simple Web example.

To overcome the statelessness of the HTTP protocol, web applications need a way to store user information between web requests. This is of course quite simple to achieve by using a cookie, or even better, by using the built-in PHP session mechanism:

$_SESSION['language'] = 'fr';
 

The above code stores the user language in the

language

session variable. So, for all subsequent requests of the same user, the

language

will be available in the global

$_SESSION

array:

$user_language = $_SESSION['language'];
 

As Dependency Injection only makes sense in an Object-Oriented world, let’s pretend we have a

SessionStorage

class that wraps the PHP session mechanism:

class SessionStorage
{
  function __construct($cookieName = 'PHP_SESS_ID')
  {
    session_name($cookieName);
    session_start();
  }
 
  function set($key, $value)
  {
    $_SESSION[$key] = $value;
  }
 
  function get($key)
  {
    return $_SESSION[$key];
  }
 
  // ...
}
 

… and a

User

class that provides a nice high-level interface:

class User
{
  protected $storage;
 
  function __construct()
  {
    $this->storage = new SessionStorage();
  }
 
  function setLanguage($language)
  {
    $this->storage->set('language', $language);
  }
 
  function getLanguage()
  {
    return $this->storage->get('language');
  }
 
  // ...
}
 

Those classes are simple enough and using the

User

class is also rather easy:

$user = new User();
$user->setLanguage('fr');
$user_language = $user->getLanguage();
 

All is good and well… until you want more flexibility. What if you want to change the session cookie name for instance? Here are some random possibilities:

  • Hardcode the name in the
    User

    class in the

    SessionStorage

    constructor:

    class User
    {
      function __construct()
      {
        $this->storage = new SessionStorage('SESSION_ID');
      }
     
      // ...
    }
     
  • Define a constant outside of the
    User

    class:

    class User
    {
      function __construct()
      {
        $this->storage = new SessionStorage(STORAGE_SESSION_NAME);
      }
     
      // ...
    }
     
    define('STORAGE_SESSION_NAME', 'SESSION_ID');
     
  • Add the session name as a
    User

    constructor argument:

    class User
    {
      function __construct($sessionName)
      {
        $this->storage = new SessionStorage($sessionName);
      }
     
      // ...
    }
     
    $user = new User('SESSION_ID');
     
  • Add an array of options for the storage class:
    class User
    {
      function __construct($storageOptions)
      {
        $this->storage       new SessionStorage($storageOptions['session_name']);
      }
     
      // ...
    }
     
    $user = new User(array('session_name' => 'SESSION_ID'));
     

All these alternatives are quite bad. Hardcoding the session name in the

User

class does not really solve the problem as you cannot easily change your mind later on without changing the

User

class again. Using a constant is also a bad idea as the

User

class now depends on a constant to be set. Passing the session name as an argument or as an array of options is probably the best solution, but it still smells bad. It clutters the

User

constructor arguments with things that are not relevant to the object itself.

But there is yet another problem that cannot be solved easily: How can I change the

SessionStorage

class? For instance, to replace it with a mock object to ease testing. Or perhaps because you want to store the sessions in a database table or in memory. That’s impossible with the current implementation, except if you change the

User

class.

Enter Dependency Injection. Instead of creating the

SessionStorage

object inside the

User

class, let’s inject the

SessionStorage

object in the

User

object by passing it as a constructor argument:

class User
{
  function __construct($storage)
  {
    $this->storage = $storage;
  }
 
  // ...
}
 

That’s Dependency Injection. Nothing more! Using the

User

class is now a bit more involving as you first need to create the

SessionStorage

object:

$storage = new SessionStorage('SESSION_ID');
$user = new User($storage);
 

Now, configuring the session storage object is dead simple, and replacing the session storage class is also very easy. And everything is possible without changing the

User

class thanks to the better separation of concerns.

The Pico Container website describes Dependency Injection like this:

“Dependency Injection is where components are given their dependencies through their constructors, methods, or directly into fields.”

As any other design pattern, Dependency Injection also has some anti-patterns. The Pico Container website describes some of them.

Dependency Injection is not restricted to constructor injection:

  • Constructor Injection:
    class User
    {
      function __construct($storage)
      {
        $this->storage = $storage;
      }
     
      // ...
    }
     
  • Setter Injection:
    class User
    {
      function setSessionStorage($storage)
      {
        $this->storage = $storage;
      }
     
      // ...
    }
     
  • Property Injection:
    class User
    {
      public $sessionStorage;
    }
     
    $user->sessionStorage = $storage;
     

As a rule of thumb, constructor injection is best for required dependencies, like in our example, and setter injection is best for optional dependencies, like a cache object for instance.

Nowadays, most modern PHP frameworks heavily use Dependency Injection to provide a set of decoupled but cohesive components:

// symfony: A constructor injection example
$dispatcher = new sfEventDispatcher();
$storage = new sfMySQLSessionStorage(
         array(
            'database' => 'session',
            'db_table' => 'session'));
$user = new sfUser($dispatcher, $storage,
         array('default_culture' => 'en'));
 
// Zend Framework: A setter injection example
$transport = new Zend_Mail_Transport_Smtp('smtp.gmail.com',
 array(
  'auth'     => 'login',
  'username' => 'foo',
  'password' => 'bar',
  'ssl'      => 'ssl',
  'port'     => 465,
));
 
$mailer = new Zend_Mail();
$mailer->setDefaultTransport($transport);
 

If you are interested in learning more about Dependency Injection, I highly recommend you read the Martin Fowler introduction or the excellent Jeff More presentation. You can also have a look at a presentation I gave last year on Dependency Injection, where I talk in more detail on the example I have talked about in this article.

That’s all for today. I hope you now have a better understanding of the Dependency Injection concept. In the next installment of this series, I will talk about Dependency Injection Containers.

Tags:

Conference PHP Quebec 2009

一年一度的 PHP Quebec 大会,本月初在加拿大的蒙特利尔举办。这次大会的主题是 “Get Further with PHP”,中文怎么说呢,“与PHP 一起深入发展”?

这是官方主页:

http://conf.phpquebec.com/en

我第一次听说这个 PHP Quebec 是在 2008 年,从网上找到一本 Symfony 的 PDF,是 Fabien Potencier (Symfony-Project 的创始人) 在 PHP Quebec 2007 上演示 Symfony 的。这个 PDF 我已经收藏,可以在我这里下载:

Symfony 文档 电子书 PHP Quebec 2007

今天恰好又看到新闻说到这个 PHP Quebec,去官网看了下,有好多演讲主题,最让我兴奋的是有 40 多份 PDF 和 PPT 文档可供下载!!!

这里是下载页面,着急的朋友就先去下吧:

http://conf.phpquebec.com/en/slides

虽然看 PDF 没有去现场效果好,但是从这些资料中,我们可以看到在 PHP 这个圈子中,国外的最先进的模式和理念。多看看人家是怎么应用 PHP 的,再看看自己哪些地方跟人家相比还有很大差距,早点下功夫弥补。甚至受到他们的启发,自己去创造一些新的应用。省得人家总说我们中国人没有创新力,只会抄袭。

不要光埋头苦干,应该抬起头,看看新的世界是什么样子。也许我们能够从中学到事半功倍的做事方法。

为了大家能方便地下载那些 PDF,我写了个 Linux 下运行的 sh 脚本,就是 wget 所有的 url,不过有些 url 是引用到另一个 site 的 flash 或 html,没法下载,我就直接过滤掉了,这样的 url 有 3 个:

  • http://www.slideshare.net/coogle/beyond-the-browser
  • http://conf.phpquebec.com/slides/2009/Building-applications-with-Yahoo-Pipes
  • http://csarven.ca/presentations/microformats-05

大伙儿自己单独下吧。

我的 sh 脚本在这里下载:

>>无敌下载 sh (linux 命令行)<<

 

贴心提示!

建议用 nohup 在后台运行,省得不小心 shell 断了又得重新下!

shell> nohup sh get_pdf_php_quebec_2009.sh > log.txt 2>&1 &

Tags: ,

开发人员为什么要支持非IE浏览器的四个故事

开发人员为什么要支持非IE浏览器的四个故事

最近一不小心陷入了对第二次浏览器大战的深思,并参与了几次网上讨论。颇有心得,不知何处分享。下面要讲的四个故事,都是客户(或者你的雇主)对你我(这样的开发人员)讲的故事。来源于生活,高于生活。

1)
你受雇于Z商银行专业版开发团队,不久结识了负责专业版客服的漂亮MM。爱情的滋味让你忘记了工作的烦恼,但是你却清楚的记得那一天走进心仪MM的办公 室,她的老板正在抱怨客户的投诉。原来很多江浙一带的有钱小老板,一直是Z商银行的忠实客户,可是人有钱就有了精神追求,他们这两年纷纷配备了Mac的本 本,有白色的有银色的。。。明白了吧?!现在他们的钱都转存至浦东发展银行和深圳发展银行了。

数据显示,MacBook和MacBook Pro在中国的销量每年都在高速增长,这还没有统计从香港出货而最终用户是大陆人的数量。Mac上人们常用两个浏览器,Safari和 Firefox,Safari非常酷,而且对标准的支持非常好,Firefox插件多,安全性好。最重要的是,如果你已经具有开发IE Web App的能力,那么就已经具备开发标准Web App的能力了。只要有心,万事不难。微软自己也不再喜欢ActiveX了,他们经常游说你们团队用Silverlight。这次,你的老板还会上当吗? 千万别忘了,这还是微软的私有技术,即使他们承诺Silverlight将跨平台,你也会觉得难用无比,别忘了他们曾经做过让网页设计师谈虎色变的IE for Mac。

前事不忘后事之师。我们能有今天的幸福生活,一要感谢党,二要感谢开放的国际互联网。HTML5,CSS3,以及其它的W3C标准共同组成了我们通常所说 的Open Web。很多人误解了,以为ActiveX是Web安全性的不二选择。其实安全性向来都是开放平台、开放架构、开放源代码远胜于专有平台、专有架构、专有 技术。HTTPS已经非常安全了。选择Open Web之后的最重要工作,就是让每个程序员都有一颗安全性第一的心。

到了晚上,MM完全没有约会的心情了,她一个劲的问你,这两个银行很小啊,根本没有什么研发力量,为什么他们能做的事情,你们不能做?这怎么办?你又不能 认错又不能让MM对公司失去信心,虽然她的信心已经失去了,虽然这根本不是你的错。憋了半天,你只能来一句:“他们不主流,我们不标准。”

2)
你大学毕业不到5年,与朋友一起创业做Web 2.0网站。专家给你们团队的建议是:用户第一,理念第二,技术第三。你非常赞同这一说法。作为主力开发,你非常希望产品能带给用户不一样的感觉,让用户 在第一次就记住这个网站,而不是在浏览器还没有渲染完页面时就关闭页签。你非常羡慕Google的很多应用,简约而不简单,易用性强,速度快。

作为Web开发的老手(还不敢称专家),让我来给你一些建议。第一,你要坚持在团队中宣扬少用图片的口号。第二,使用更多的Ajax异步装载,不断提升性 能,优化用户体验。第三,大量用客户的计算资源,(反正客户的资源丰富而且几乎无成本),减少服务器的压力。第四,宣扬瘦服务器的理念,因为公司小,业务 变化很快,投入服务器的资源不容易变换,如果设计成瘦服务器,成本投在客户端,因为客户端环境单一,全部重来也没有多少成本,容易随需应变。好,我们首先 假设这些建议你已经接受了。

接下来,5年从未遇到的问题来了。由于客户端代码越来越多,越来越复杂,影响了项目进度。老板开始质疑你的这种方式是否可取。再有,公司重金请人做网页设 计,结果你总是提减少图片,设计师不悦,常常产生私人恩怨。更麻烦的是,连你自己都开始担心,因为前端代码常有bug,导致IE弹出对话框,打破了你一直 的梦想。

好吧,既然你能看到这里,我打算教你点绝活。在IE中使用直角矩形,而在Firefox/Safari/Chrome中使用圆角矩形。如果你的设计师喜欢 圆角矩形,就给他/她看Safari中的效果,设计师都喜欢Safari,如果他/她喜欢直角,就给看IE!因为在Firefox/Safari /Chrome中,圆角可以用CSS实现,完全没有贴图。这一招还要用在阴影上。我太太最近还在跟我炫耀她能用CSS3直接实现雕刻字体效果,不过也不是 在IE中。至于Ajax等开发的复杂性,建议你常年使用jQuery和jQuery插件。这个系统的所有API都是跨浏览器的,零学习成本,会用 Javascript就会用,零host成本,因为Google帮你host。这样你可以在Firefox上用Firebug开发,或者在Safari 4里开发(透露一下,Safari 4的调试功能真的超级棒),然后在IE上跑,没有任何问题。2008年年初的调查,jQuery市场占有率不足20%,年末已经过70%了,所以我常把 2008年称为jQuery年,这是国际开发者社群的共同选择。

很多开发人员拒绝非IE浏览器,是因为他们害怕浏览器间的差异,带来开发成本,尤其是CSS上的差异很大。不过别忘了,资本主义世界只要有利润,人们就会 勇往直前。那么多大公司(包括微软)都在奋发图强开发Web App,包括IBM的Lotus产品线,你遇到的难题,别人都遇到过。我们搞开发的就是要站在Google/IBM/Apple这些大公司的肩膀上。

在感受了一次成功的喜悦后,绝大部分开发人员还是会相信:支持非IE浏览器,俺,能跑!!

3)
你大学毕业就进入了一家大型软件公司工作,ERP/CRM是公司的主要产品线类型,工资和福利是你与同龄人相比的骄傲,虚荣心是你参加同学聚会的动力。不 久你升任产品经理,前途似锦。但困扰你的是每次与客户面对面,客户都对你的个人魅力毫无兴趣,而是反反复复的提一个字:“省”!

你花了一个月的时间和客户吃住在一起,通过IT部门了解客户的IT支出到了哪里?尽管如此,客户还是希望你能够拿出一个方案让他们在2009~2010年 经济危机的时候每年节省几百万。怎么办?把客户的员工裁了?还是把你裁了?减少买你们公司软件的支出?(这两年你们公司也不好过,这跟把你裁了是一样 的。)还是减少。。。等等。。。硬件?对,硬件!

一方面,如果客户对新员工和需要更换的笔记本采用Netbook,就可以节约大量成本。另一方面,如果采用云计算的产品,可以把需要支出的服务器端软件改 为租用方式,用浏览器访问。两个方面前后一致且顺理成章。Netbook之所以存在市场就是因为大量的软件应用由Web App取代。新员工只要有浏览器用就可以访问ERP/CRM这些你们公司的拳头产品。

好,于是你再次向客户提出了这10年来全球的IT大公司(除微软)不断向客户提出的建议:Linux。

今天就是尝试Linux的最佳时间。Dvorak,这位以批判大公司出名的IT评论家,现在建议每个人都要尝试一下Ubuntu。真的很好用。你可以不相 信我但是不能不相信Dvorak,他从不盲目追随新技术,加上年龄的原因,他总是比我们这些年轻人对新事物更抵触。据说俄罗斯的国立中小学已经全部是 Linux教学了。你心里的小算盘开始响了,如果每位员工减少本本方面的2000块支出,几百万不在话下。

如果被客户问及Ubuntu不好用怎么办?你嘴角微微一笑,没关系,反正客户大部分时间都只干三件事:聊天、看电影,用办公软件。这些都是Ubuntu的 强项。聊天?QQ和MSN、Skype都支持。看电影,有跟暴风影音一样强的(还不止一个)。办公,OpenOffice存取MS Office格式照常使用。至于你的软件,早就Firefox罗。

怎么?客户还要培训?你挣钱的时候到了。Windows的钱都省了,出点服务费咋了?

4)
年过30的你急于在事业上攀到新的高峰,才能给妻子和正要上小学的孩子一个交代。天赐良机你被一个中型企业挖去做CTO,而你的老板,则是个精明的美国商人。(怎么听起来像小说《最后期限》?)为了拿到VC的钱,老板要求你夜以继日的扩大用户群。怎么办?八仙过海。

大量的软件公司正在尝试把他们最挣钱的产品移植到Web上。这种趋势从2004年就已经开始了。不过用户还是不太喜欢Web Service或者SaaS这样的技术术语,比较讨用户喜欢的概念是“云计算”。然而金融危机到来,VC变得非常谨慎,不见兔子不撒鹰,除了大规模用户数 量的增长,其余免谈。在这个冬季临危受命的你,光靠国内市场是显然不够的。加上笃信《世界是平的》,你决定到全球市场去碰碰运气。无论你是否相信,只要支 持中英两种文字,就支持了全球半数以上的网民。你六级不都过了吗,这有什么难的?

想象一下很多厂商靠iPhone和Android活着。如果让你选择一个作为平台,你愿意选哪一个?呵呵。你猜我选哪一个?Both!其实很多人不知道, 如果做了iPhone的Web App就等于做了Android的Web App,他们是完全相同的浏览器内核Webkit。不相信?那你看看iPhone上的Gmail和Google Reader,是不是跟Android一模一样?以前是不是一直以为Google做了iPhone版?上当了,Google并未用Apple的风格,而是 使用自己的风格,但是看起来易用性一点也不输给本地iPhone应用。更多的喜讯还在后面,Nokia S60也是Webkit,Blackberry上也有Webkit了,Gnome的缺省浏览器很快也是Webkit了。。。

所以,坚持所有Web产品都支持Safari/Chrome的开发团队,获得了史上从未有过的光荣。这不仅是来自VC的青睐,也是直接来自客户的认同,更是钱在向你招手。。。(抱歉我总是想着钱,可谁不是呢?)。。。

Firefox呢?也是不可多得的marketing阵地!因为Firefox插件多,而且非常容易开发。很多小公司靠这个活着呢。因为Firefox的 用户基数大(比IE7大),所以一点细小的易用性改进都能吸引大量用户的眼球。这么多人痴迷与Facebook App和开心App,也是同一个道理。

所以,我们的口号是:支持非IE浏览器,他好,你也好!用户好,投资人好,老板好。大家好,才是真的好!(怎么样?被我雷死了吧。)

结语)

还是前面说的那句话,用户第一,理念第二,技术第三。不要为你的技术找借口。更好的为用户服务就是你的使命。在世界平坦化的今天,把国人的聪明才智展现于世界舞台就是你的机遇。努力的去做吧。正所谓你不下地狱谁下地狱!

欢迎大家续写这些故事和添加故事。谢谢!

不怕危机的泡泡
(转载本文需注明出处:Brian Sun @ 爬树的泡泡[http://www.briansun.com]) 

Tags: