<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	>

<channel>
	<title>STRIVE &#124; 杰瑞's Blog</title>
	<atom:link href="http://blog.jerryjiang.net/index.php?feed=rss2" rel="self" type="application/rss+xml" />
	<link>http://blog.jerryjiang.net</link>
	<description>杰瑞 (jerry) 的部落格</description>
	<pubDate>Mon, 27 Jul 2009 15:54:17 +0000</pubDate>
	<generator>http://wordpress.org/?v=2.7.1</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>MySQL与事务</title>
		<link>http://blog.jerryjiang.net/?p=48</link>
		<comments>http://blog.jerryjiang.net/?p=48#comments</comments>
		<pubDate>Mon, 27 Jul 2009 15:51:20 +0000</pubDate>
		<dc:creator>杰瑞</dc:creator>
		
		<category><![CDATA[Php Development]]></category>

		<guid isPermaLink="false">http://blog.jerryjiang.net/?p=48</guid>
		<description><![CDATA[今天在网上看见一个关于mysql事物的说明，我觉得写得不错，拿给大家看看
MySQL5.X都已经发布好久了，但是还有很多人认为MySQL是不支持事务处理的，这不得不怪他们是孤陋寡闻的，其实，只要你的MySQL版本支持BDB或InnoDB表类型，那么你的MySQL就具有事务处理的能力。这里面，又以InnoDB表类型用的最多，虽然后来发生了诸如Oracle收购InnoDB等令MySQL不爽的事情，但那些商业上的斗争与技术无关，下面以InnoDB表类型为例简单说一下MySQL中的事务。

先来明确一下事务涉及的相关知识：
事务都应该具备ACID特征。所谓ACID是Atomic（原子性），Consistent（一致性），Isolated（隔离性），Durable（持续性）四个词的首字母所写，下面以“银行转帐”为例来分别说明一下它们的含义：
原子性：组成事务处理的语句形成了一个逻辑单元，不能只执行其中的一部分。换句话说，事务是不可分割的最小单元。比如：银行转帐过程中，必须同时从一个帐户减去转帐金额，并加到另一个帐户中，只改变一个帐户是不合理的。
一致性：在事务处理执行前后，数据库是一致的。也就是说，事务应该正确的转换系统状态。比如：银行转帐过程中，要么转帐金额从一个帐户转入另一个帐户，要么两个帐户都不变，没有其他的情况。
隔离性：一个事务处理对另一个事务处理没有影响。就是说任何事务都不可能看到一个处在不完整状态下的事务。比如说，银行转帐过程中，在转帐事务没有提交之前，另一个转帐事务只能处于等待状态。
持续性：事务处理的效果能够被永久保存下来。反过来说，事务应当能够承受所有的失败，包括服务器、进程、通信以及媒体失败等等。比如：银行转帐过程中，转帐后帐户的状态要能被保存下来。
再来看看哪些问题会用到事务处理：
这里不说“银行转帐”的例子了，说一个大家实际更容易遇到的“网上购书”的例子。先假设一下问题的背景：网上购书，某书（数据库编号为123）只剩最后一本，而这个时候，两个用户对这本书几乎同时发出了购买请求，让我们看看整个过程：
在具体分析之前，先来看看数据表的定义：
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-
create table book
(
    book_id unsigned int(10) not null auto_increment,
    book_name varchar(100) not null,
    book_price float(5, 2) not null, #我假设每本书的价格不会超过999.99元
    book_number int(10) not null,
    primary key (book_id)
)
type = innodb; #engine = innodb也行
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-
对于用户甲来说，他的动作稍微比乙快一点点，其购买过程所触发的动作大致是这样的：
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-
1. SELECT book_number FROM book WHERE  book_id = 123;
book_number大于零，确认购买行为并更新book_number
2. [...]]]></description>
			<content:encoded><![CDATA[<p>今天在网上看见一个关于mysql事物的说明，我觉得写得不错，拿给大家看看</p>
<p>MySQL5.X都已经发布好久了，但是还有很多人认为MySQL是不支持事务处理的，这不得不怪他们是孤陋寡闻的，其实，只要你的MySQL版本支持BDB或InnoDB表类型，那么你的MySQL就具有事务处理的能力。这里面，又以InnoDB表类型用的最多，虽然后来发生了诸如Oracle收购InnoDB等令MySQL不爽的事情，但那些商业上的斗争与技术无关，下面以InnoDB表类型为例简单说一下MySQL中的事务。</p>
<p><span id="more-48"></span></p>
<p>先来明确一下事务涉及的相关知识：</p>
<p>事务都应该具备ACID特征。所谓ACID是Atomic（原子性），Consistent（一致性），Isolated（隔离性），Durable（持续性）四个词的首字母所写，下面以“银行转帐”为例来分别说明一下它们的含义：</p>
<p>原子性：组成事务处理的语句形成了一个逻辑单元，不能只执行其中的一部分。换句话说，事务是不可分割的最小单元。比如：银行转帐过程中，必须同时从一个帐户减去转帐金额，并加到另一个帐户中，只改变一个帐户是不合理的。</p>
<p>一致性：在事务处理执行前后，数据库是一致的。也就是说，事务应该正确的转换系统状态。比如：银行转帐过程中，要么转帐金额从一个帐户转入另一个帐户，要么两个帐户都不变，没有其他的情况。</p>
<p>隔离性：一个事务处理对另一个事务处理没有影响。就是说任何事务都不可能看到一个处在不完整状态下的事务。比如说，银行转帐过程中，在转帐事务没有提交之前，另一个转帐事务只能处于等待状态。</p>
<p>持续性：事务处理的效果能够被永久保存下来。反过来说，事务应当能够承受所有的失败，包括服务器、进程、通信以及媒体失败等等。比如：银行转帐过程中，转帐后帐户的状态要能被保存下来。</p>
<p>再来看看哪些问题会用到事务处理：</p>
<p>这里不说“银行转帐”的例子了，说一个大家实际更容易遇到的“网上购书”的例子。先假设一下问题的背景：网上购书，某书（数据库编号为123）只剩最后一本，而这个时候，两个用户对这本书几乎同时发出了购买请求，让我们看看整个过程：</p>
<p>在具体分析之前，先来看看数据表的定义：</p>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-</p>
<p>create table book<br />
(<br />
    book_id unsigned int(10) not null auto_increment,<br />
    book_name varchar(100) not null,<br />
    book_price float(5, 2) not null, #我假设每本书的价格不会超过999.99元<br />
    book_number int(10) not null,<br />
    primary key (book_id)<br />
)<br />
type = innodb; #engine = innodb也行</p>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-</p>
<p>对于用户甲来说，他的动作稍微比乙快一点点，其购买过程所触发的动作大致是这样的：</p>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-</p>
<p>1. SELECT book_number FROM book WHERE  book_id = 123;</p>
<p>book_number大于零，确认购买行为并更新book_number</p>
<p>2. UPDATE book SET book_number = book_number - 1 WHERE  book_id = 123;</p>
<p>购书成功</p>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-</p>
<p>而对于用户乙来说，他的动作稍微比甲慢一点点，其购买过程所触发的动作和甲相同：</p>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-</p>
<p>1. SELECT book_number FROM book WHERE  book_id = 123;</p>
<p>这个时候，甲刚刚进行完第一步的操作，还没来得及做第二步操作，所以book_number一定大于零</p>
<p>2. UPDATE book SET book_number = book_number - 1 WHERE  book_id = 123;</p>
<p>购书成功</p>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-</p>
<p>表面上看甲乙的操作都成功了，他们都买到了书，但是库存只有一本，他们怎么可能都成功呢？再看看数据表里book_number的内容，已经变成 “-1”了，这当然是不能允许的（实际上，声明这样的列类型应该加上unsigned的属性，以保证其不能为负，这里是为了说明问题所以没有这样设置）</p>
<p>好了，问题陈述清楚了，再来看看怎么利用事务来解决这个问题，打开MySQL手册，可以看到想用事务来保护你的SQL正确执行其实很简单，基本就是三个语句：开始，提交，回滚。</p>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-</p>
<p>开始：START TRANSACTION或BEGIN语句可以开始一项新的事务</p>
<p>提交：COMMIT可以提交当前事务，是变更成为永久变更</p>
<p>回滚：ROLLBACK可以回滚当前事务，取消其变更</p>
<p>此外，SET AUTOCOMMIT = {0 | 1}可以禁用或启用默认的autocommit模式，用于当前连接。</p>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-</p>
<p>那是不是只要用事务语句包一下我们的SQL语句就能保证正确了呢？比如下面代码：</p>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-</p>
<p>BEGIN;</p>
<p>SELECT book_number FROM book WHERE  book_id = 123;</p>
<p>// &#8230;</p>
<p>UPDATE book SET book_number = book_number - 1 WHERE  book_id = 123;</p>
<p>COMMIT;</p>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-</p>
<p>答案是否定了，这样依然不能避免问题的发生，如果想避免这样的情况，实际应该如下：</p>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-</p>
<p>BEGIN;</p>
<p>SELECT book_number FROM book WHERE  book_id = 123 FOR UPDATE;</p>
<p>// &#8230;</p>
<p>UPDATE book SET book_number = book_number - 1 WHERE  book_id = 123;</p>
<p>COMMIT;</p>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-</p>
<p>由于加入了FOR UPDATE，所以会在此条记录上加上一个行锁，如果此事务没有完全结束，那么其他的事务在使用SELECT &#8230; FOR UPDATE请求的时候就会处于等待状态，直到上一个事务结束，它才能继续，从而避免了问题的发生，需要注意的是，如果你其他的事务使用的是不带FOR UPDATE的SELECT语句，将得不到这种保护。</p>
<p>最后看看PHP + MySQL事务操作的代码演示：</p>
<p>实际LAMP应用中，一般PHP使用AdoDB操作MySQL，下面给出AdoDB相应的代码方便大家查阅：</p>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-</p>
<p>< ?php<br />
// ...</p>
<p>$adodb->startTrans();</p>
<p>//实际，getOne所调用的查询也可以直接放到rowLock来进行，这里只是为了演示效果能更明显些。</p>
<p>$adodb->rowLock(&#8217;book&#8217;, &#8216;book_id = 123&#8242;);</p>
<p>$bookNumber = $adodb->getOne(&#8221;SELECT book_number FROM book WHERE  book_id = 123&#8243;);</p>
<p>$adodb->execute(&#8221;UPDATE book SET book_number = book_number - 1 WHERE  book_id = 123&#8243;);</p>
<p>$adodb->completeTrans();</p>
<p>// &#8230;<br />
?></p>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-</p>
<p>其中，rowLock的方法就是调用的FOR UPDATE来实现的行锁，你可能会想把“FOR UPDATE”直接写到$adodb->getOne()调用的那条SQL语句里面去实现行锁的功能，不错，那样确实可以，但是并不是所有的数据库都使用“FOR UPDATE”语法来实现行锁功能，比如Sybase使用“HOLDLOCK”的语法来实现行锁功能，所以为了你的数据库抽象层保持可移植性，我还是劝你用rowLock来实现行锁功能，至于可移植性就交给AdoDB好了，嗯，有点扯远了，今儿就说到这里了。</p>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-</p>
<p>附：</p>
<p>AdoDB中存在一个setTransactionMode()方法，能够设置事务的隔离级别，如下：</p>
<p>SetTransactionMode allows you to pass in the transaction mode to use for all subsequent transactions for that connection session. Note: if you have persistent connections and using mysql or mssql, you might have to explicitly reset your transaction mode at the beginning of each page request. This is only supported in postgresql, mssql, mysql with InnoDB and oci8 currently. For example:</p>
<p>$db->SetTransactionMode(&#8221;SERIALIZABLE&#8221;);<br />
$db->BeginTrans();<br />
$db->Execute(&#8230;); $db->Execute(&#8230;);<br />
$db->CommiTrans();</p>
<p>$db->SetTransactionMode(&#8221;"); // restore to default<br />
$db->StartTrans();<br />
$db->Execute(&#8230;); $db->Execute(&#8230;);<br />
$db->CompleteTrans();</p>
<p>Supported values to pass in:</p>
<p>    * READ UNCOMMITTED (allows dirty reads, but fastest)<br />
    * READ COMMITTED (default postgres, mssql and oci8)<br />
    * REPEATABLE READ (default mysql)<br />
    * SERIALIZABLE (slowest and most restrictive)</p>
<p>You can also pass in database specific values such as &#8216;SNAPSHOT&#8217; for mssql or &#8216;READ ONLY&#8217; for oci8/postgres. </p>
]]></content:encoded>
			<wfw:commentRss>http://blog.jerryjiang.net/?feed=rss2&amp;p=48</wfw:commentRss>
		</item>
		<item>
		<title>php 接口类，抽象类 的实际作用</title>
		<link>http://blog.jerryjiang.net/?p=43</link>
		<comments>http://blog.jerryjiang.net/?p=43#comments</comments>
		<pubDate>Wed, 17 Jun 2009 16:37:07 +0000</pubDate>
		<dc:creator>杰瑞</dc:creator>
		
		<category><![CDATA[Php Development]]></category>

		<category><![CDATA[抽象类]]></category>

		<category><![CDATA[接口类]]></category>

		<guid isPermaLink="false">http://blog.jerryjiang.net/?p=43</guid>
		<description><![CDATA[今天有个小兄弟问我，php里面的接口类，抽象类到底有什么用呢，他完全不会用，觉得用也没有什么意思，其实和他的想法是一样的，在我才接触php的时候，也觉得这个东西没有什么用，在后来的逐渐接触大系统以后，才发现其实这些东西还是有一定的作用的，下面我就简单的说说。
1.php 接口类:interface
其实他们的作用很简单，当有很多人一起开发一个项目时，可能都会去调用别人写的一些类，那你就会问，我怎么知道他的某个功能的实现方法是怎么命名的呢，这个时候php接口类就起到作用了，当我们定义了一个接口类时，它里面的方式是下面的子类必须实现的，比如 

interface Shop
{
	public function buy($gid);
	public function sell($gid);
	public function view($gid);
}

我声明一个shop接口类，定义了三个方法：买(buy),卖(sell),看(view),那么继承此类的所有子类都必须实现这3个方法少一个都不行，如果子类没有实现这些话，就无法运行。实际上接口类说白了，就是一个类的模板，一个类的规定，如果你属于这类，你就必须遵循我的规定，少一个都不行，但是具体你怎么去做，我不管，那是你的事，如：

class BaseShop implements Shop
{
	public function buy($gid)
	{
		echo '你购买了ID为 :' . $gid . '的商品';
	}
	public function sell($gid)
	{
		echo '你购卖ID为 :' . $gid . '的商品';
	}
	public function view($gid)
	{
		echo '你浏览了ID为 :' . $gid . '的商品';
	}
}

你想想，在一个多人合作的大项目里面，有了接口类是多么的方便，这样你就不用去问别人，你的某某功能的方法名是什么了，当然如果你们喜欢这样我也没有办法。
结论 ： 接口类就是一个类的领导者，指明方向，子类必须完成它指定方法。
2.php 抽象类 : abstract
其实抽象类和接口类有一部分很像，记得在哪里看见这样一句话，抽象类就把类像的部分抽出来，这句看上去很搞笑，其实它说出了抽象类的真理，抽象类的作用是，当你发现你的很多类里面用很多方法你不断的在重复写，那你就可以考虑使用抽象类了，你可能会说“我不是可以重写一个类每个公共类我个实例化一个这个公共类，调用相同的方法就可以了”，这里是可以，实际上抽象类做的工作也就是这个，不过他省去了你实例化的这个步骤，让你就像直接调用本类方法一样方便，而且你还可以重载这个方法。如：

abstract class BaseShop {
	public function buy($gid)
	{
		echo '你购买了ID为 :' . $gid . '的商品';
	}
	public function sell($gid)
	{
		echo [...]]]></description>
			<content:encoded><![CDATA[<p>今天有个小兄弟问我，php里面的接口类，抽象类到底有什么用呢，他完全不会用，觉得用也没有什么意思，其实和他的想法是一样的，在我才接触php的时候，也觉得这个东西没有什么用，在后来的逐渐接触大系统以后，才发现其实这些东西还是有一定的作用的，下面我就简单的说说。</p>
<p><strong>1.php 接口类:interface</strong></p>
<p>其实他们的作用很简单，当有很多人一起开发一个项目时，可能都会去调用别人写的一些类，那你就会问，我怎么知道他的某个功能的实现方法是怎么命名的呢，这个时候php接口类就起到作用了，当我们定义了一个接口类时，它里面的方式是下面的子类必须实现的，比如 </p>
<p><code><br />
interface Shop<br />
{<br />
	public function buy($gid);<br />
	public function sell($gid);<br />
	public function view($gid);<br />
}<br />
</code></p>
<p>我声明一个shop接口类，定义了三个方法：买(buy),卖(sell),看(view),那么继承此类的所有子类都必须实现这3个方法少一个都不行，如果子类没有实现这些话，就无法运行。实际上接口类说白了，就是一个类的模板，一个类的规定，如果你属于这类，你就必须遵循我的规定，少一个都不行，但是具体你怎么去做，我不管，那是你的事，如：</p>
<p><code><br />
class BaseShop implements Shop<br />
{<br />
	public function buy($gid)<br />
	{<br />
		echo '你购买了ID为 :' . $gid . '的商品';<br />
	}</p>
<p>	public function sell($gid)<br />
	{<br />
		echo '你购卖ID为 :' . $gid . '的商品';<br />
	}</p>
<p>	public function view($gid)<br />
	{<br />
		echo '你浏览了ID为 :' . $gid . '的商品';<br />
	}<br />
}<br />
</code></p>
<p>你想想，在一个多人合作的大项目里面，有了接口类是多么的方便，这样你就不用去问别人，你的某某功能的方法名是什么了，当然如果你们喜欢这样我也没有办法。</p>
<p><strong>结论 ： 接口类就是一个类的领导者，指明方向，子类必须完成它指定方法。</strong></p>
<p><strong>2.php 抽象类 : abstract</strong></p>
<p>其实抽象类和接口类有一部分很像，记得在哪里看见这样一句话，抽象类就把类像的部分抽出来，这句看上去很搞笑，其实它说出了抽象类的真理，抽象类的作用是，当你发现你的很多类里面用很多方法你不断的在重复写，那你就可以考虑使用抽象类了，你可能会说“我不是可以重写一个类每个公共类我个实例化一个这个公共类，调用相同的方法就可以了”，这里是可以，实际上抽象类做的工作也就是这个，不过他省去了你实例化的这个步骤，让你就像直接调用本类方法一样方便，而且你还可以重载这个方法。如：</p>
<p><code><br />
abstract class BaseShop {<br />
	public function buy($gid)<br />
	{<br />
		echo '你购买了ID为 :' . $gid . '的商品';<br />
	}</p>
<p>	public function sell($gid)<br />
	{<br />
		echo '你购卖ID为 :' . $gid . '的商品';<br />
	}</p>
<p>	public function view($gid)<br />
	{<br />
		echo '你浏览了ID为 :' . $gid . '的商品';<br />
	}<br />
}</p>
<p>class BallShop extends BaseShop<br />
{<br />
	var $itme_id = null;</p>
<p>	public function __construct()<br />
	{<br />
		$this->itme_id = 2314;<br />
	}</p>
<p>	public function open()<br />
	{<br />
		$this->sell($this->itme_id);<br />
	}<br />
}<br />
</code></p>
<p>这里是一个例子，想上面一样我定义了一个商店类，抽所了它所有像的部分，买(buy),卖(sell),看(view),并且抽象类里都实现了这些方法，那么继承它的子类就自动获得了这些方法，子类就做它自己独特的东西，介绍代码的重复，提高复用性。</p>
<p><strong>结论： 抽象类就是一个类的服务提供商，拥有众多服务，你不用必须用，当需要的时候你来用就可以，如果你觉得不提供服务不满意，你还可以自己来做。</strong></p>
<p>呵呵，以上就是我对php 接口类，抽象类一些拙见，希望能对搞不清这两个朋友一些帮助，如果什么意见欢迎留言</p>
<p>jerry <jerryjiang15 @gmail.com> 2009.6.18</jerryjiang15></p>
]]></content:encoded>
			<wfw:commentRss>http://blog.jerryjiang.net/?feed=rss2&amp;p=43</wfw:commentRss>
		</item>
		<item>
		<title>sprintf用法</title>
		<link>http://blog.jerryjiang.net/?p=37</link>
		<comments>http://blog.jerryjiang.net/?p=37#comments</comments>
		<pubDate>Mon, 01 Jun 2009 08:36:07 +0000</pubDate>
		<dc:creator>杰瑞</dc:creator>
		
		<category><![CDATA[Php Development]]></category>

		<guid isPermaLink="false">http://blog.jerryjiang.net/?p=37</guid>
		<description><![CDATA[今天在网上发现了这个sprintf(format,arg1,arg2,arg++)的详细介绍，转载一下

format：
Required. Specifies the string and how to format the variables in it.
必要参数。指定字符串，以及如何定义其中变量的格式。 
Possible format values:
可能值如下：
%% - Returns a percent sign
%% -返回百分号
%b - Binary number
%b –返回二进制数
%c - The character according to the ASCII value
%c –返回与ASCII值相对应的字符
%d - Signed decimal number
%d –带有正负号的十进制数
%e - Scientific notation (e.g. 1.2e+2)
%e –科学计数符号（如：1.2e+2）
%u - Unsigned decimal number
%u –不带正负号的十进制数
%f - Floating-point number (local settings [...]]]></description>
			<content:encoded><![CDATA[<p>今天在网上发现了这个sprintf(format,arg1,arg2,arg++)的详细介绍，转载一下</p>
<p><span id="more-37"></span><br />
format：<br />
Required. Specifies the string and how to format the variables in it.<br />
必要参数。指定字符串，以及如何定义其中变量的格式。 </p>
<p>Possible format values:<br />
可能值如下：</p>
<p>%% - Returns a percent sign<br />
%% -返回百分号<br />
%b - Binary number<br />
%b –返回二进制数<br />
%c - The character according to the ASCII value<br />
%c –返回与ASCII值相对应的字符<br />
%d - Signed decimal number<br />
%d –带有正负号的十进制数<br />
%e - Scientific notation (e.g. 1.2e+2)<br />
%e –科学计数符号（如：1.2e+2）<br />
%u - Unsigned decimal number<br />
%u –不带正负号的十进制数<br />
%f - Floating-point number (local settings aware)<br />
%f – 浮点数据（本地设置）<br />
%F - Floating-point number (not local settings aware)<br />
%F –浮点数据（非本地设置）<br />
%o - Octal number<br />
%o –十进制数<br />
%s - String<br />
%s –字符串<br />
%x - Hexadecimal number (lowercase letters)<br />
%x –十六进制数（小写字母）<br />
%X - Hexadecimal number (uppercase letters)<br />
%X –十六进制数（大写字母）<br />
Additional format values. These are placed between the % and the letter (example %.2f):<br />
其它格式的值。它是位于%和字母之间的（如：%.2f）</p>
<p>+ (Forces both + and - in front of numbers. By default, only negative numbers are marked)<br />
+（在数字前加上+和-；默认情况下，只有负数是被标记出来的）<br />
‘ (Specifies what to use as padding. Default is space. Must be used together with the width specifier. Example: %’x20s (this uses “x” as padding)<br />
’（指定使用什么作为补白，默认值是空格。它必须与宽度指定器一起使用。如：%’x20s（使用“x”作为padding））<br />
- (Left-justifies the variable value)<br />
- （左调整变量值）<br />
[0-9] (Specifies the minimum width held of to the variable value)<br />
[0-9]（指定变量值的最小宽度）<br />
.[0-9] (Specifies the number of decimal digits or maximum string length)<br />
.[0-9]（指定十进制数值或最大字符串长度）<br />
Note: If multiple additional format values are used, they must be in the same order as above.<br />
注意：如果使用附加格式值，那么它必须与上述顺序相同</p>
<p>arg1：<br />
Required. The argument to be inserted at the first %-sign in the format string<br />
必要参数。这个自变量（arg1）必须安插在第一个%-符号前</p>
<p>arg2：<br />
Optional. The argument to be inserted at the second %-sign in the format string<br />
可选参数。这个自变量（arg2）必须安插在第二个%-符号前</p>
<p>arg++：<br />
Optional. The argument to be inserted at the third, fourth, etc. %-sign in the format string<br />
可选参数。与上述自变量相同，它们可以安插在第三个、第四个……（依次类推）%-符号前。</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.jerryjiang.net/?feed=rss2&amp;p=37</wfw:commentRss>
		</item>
		<item>
		<title>[js] execCommand 命令详解</title>
		<link>http://blog.jerryjiang.net/?p=33</link>
		<comments>http://blog.jerryjiang.net/?p=33#comments</comments>
		<pubDate>Tue, 28 Apr 2009 05:46:59 +0000</pubDate>
		<dc:creator>杰瑞</dc:creator>
		
		<category><![CDATA[Javascript]]></category>

		<guid isPermaLink="false">http://blog.jerryjiang.net/?p=33</guid>
		<description><![CDATA[document.execCommand()方法处理Html数据时常用语法格式如下:
复制内容到剪贴板
代码:
document.execCommand(sCommand[,交互方式, 动态参数])
其中：sCommand为指令参数（如下例中的&#8221;2D-Position&#8221;），交互方式参数如果是true的话将显示对话框，如果为false的话，则不显示对话框（下例中的&#8221;false&#8221;即表示不显示对话框），动态参数一般为一可用值或属性值（如下例中的&#8221;true&#8221;）。

document.execCommand(&#8221;2D-Position&#8221;,&#8221;false&#8221;,&#8221;true&#8221;);
调用execCommand()可以实现浏览器菜单的很多功能. 如保存文件,打开新文件,撤消、重做操作&#8230;等等. 有了这个方法,就可以很容易的实现网页中的文本编辑器.
如果灵活运用,可以很好的辅助我们完成各种项目.
使用的例子如下:
1、〖全选〗命令的实现
[格式]:document.execCommand(&#8221;selectAll&#8221;)
[说明]将选种网页中的全部内容！
[举例]在之间加入：
全选
2、〖打开〗命令的实现
[格式]:document.execCommand(&#8221;open&#8221;)
[说明]这跟VB等编程设计中的webbrowser控件中的命令有些相似，大家也可依此琢磨琢磨。
[举例]在之间加入：
打开
3、〖另存为〗命令的实现
[格式]:document.execCommand(&#8221;saveAs&#8221;)
[说明]将该网页保存到本地盘的其它目录！
[举例]在之间加入：
另存为
4、〖打印〗命令的实现
[格式]:document.execCommand(&#8221;print&#8221;)
[说明]当然，你必须装了打印机！
[举例]在之间加入：
打印
Js代码 下面列出的是指令参数及意义
//相当于单击文件中的打开按钮
document.execCommand(&#8221;Open&#8221;);
//将当前页面另存为
document.execCommand(&#8221;SaveAs&#8221;);
//剪贴选中的文字到剪贴板;
document.execCommand(&#8221;Cut&#8221;,&#8221;false&#8221;,null);
//删除选中的文字;
document.execCommand(&#8221;Delete&#8221;,&#8221;false&#8221;,null);
//改变选中区域的字体;
document.execCommand(&#8221;FontName&#8221;,&#8221;false&#8221;,sFontName);
//改变选中区域的字体大小;
document.execCommand(&#8221;FontSize&#8221;,&#8221;false&#8221;,sSize&#124;iSize);
//设置前景颜色;
document.execCommand(&#8221;ForeColor&#8221;,&#8221;false&#8221;,sColor);
//使绝对定位的对象可直接拖动;
document.execCommand(&#8221;2D-Position&#8221;,&#8221;false&#8221;,&#8221;true&#8221;);
//使对象定位变成绝对定位;
document.execCommand(&#8221;AbsolutePosition&#8221;,&#8221;false&#8221;,&#8221;true&#8221;);
//设置背景颜色;
document.execCommand(&#8221;BackColor&#8221;,&#8221;false&#8221;,sColor);
//使选中区域的文字加粗;
document.execCommand(&#8221;Bold&#8221;,&#8221;false&#8221;,null);
//复制选中的文字到剪贴板;
document.execCommand(&#8221;Copy&#8221;,&#8221;false&#8221;,null);
//设置指定锚点为书签;
document.execCommand(&#8221;CreateBookmark&#8221;,&#8221;false&#8221;,sAnchorName);
//将选中文本变成超连接,若第二个参数为true,会出现参数设置对话框;
document.execCommand(&#8221;CreateLink&#8221;,&#8221;false&#8221;,sLinkURL);
//设置当前块的标签名;
document.execCommand(&#8221;FormatBlock&#8221;,&#8221;false&#8221;,sTagName);  
//相当于单击文件中的打开按钮
document.execCommand(&#8221;Open&#8221;);
//将当前页面另存为
document.execCommand(&#8221;SaveAs&#8221;);
//剪贴选中的文字到剪贴板;
document.execCommand(&#8221;Cut&#8221;,&#8221;false&#8221;,null);
//删除选中的文字;
document.execCommand(&#8221;Delete&#8221;,&#8221;false&#8221;,null);
//改变选中区域的字体;
document.execCommand(&#8221;FontName&#8221;,&#8221;false&#8221;,sFontName);
//改变选中区域的字体大小;
document.execCommand(&#8221;FontSize&#8221;,&#8221;false&#8221;,sSize&#124;iSize);
//设置前景颜色;
document.execCommand(&#8221;ForeColor&#8221;,&#8221;false&#8221;,sColor);
//使绝对定位的对象可直接拖动;
document.execCommand(&#8221;2D-Position&#8221;,&#8221;false&#8221;,&#8221;true&#8221;);
//使对象定位变成绝对定位;
document.execCommand(&#8221;AbsolutePosition&#8221;,&#8221;false&#8221;,&#8221;true&#8221;);
//设置背景颜色;
document.execCommand(&#8221;BackColor&#8221;,&#8221;false&#8221;,sColor);
//使选中区域的文字加粗;
document.execCommand(&#8221;Bold&#8221;,&#8221;false&#8221;,null);
//复制选中的文字到剪贴板;
document.execCommand(&#8221;Copy&#8221;,&#8221;false&#8221;,null);
//设置指定锚点为书签;
document.execCommand(&#8221;CreateBookmark&#8221;,&#8221;false&#8221;,sAnchorName);
//将选中文本变成超连接,若第二个参数为true,会出现参数设置对话框;
document.execCommand(&#8221;CreateLink&#8221;,&#8221;false&#8221;,sLinkURL);
//设置当前块的标签名;
document.execCommand(&#8221;FormatBlock&#8221;,&#8221;false&#8221;,sTagName);
document对象execCommand通常在IE中在线处理Html数据时非常有用，它可以让你轻而易举实现文字的加粗、加颜色、加字体等一系列的命令。
    D-Position 允许通过拖曳移动绝对定位的对象。
    AbsolutePosition 设定元素的 position 属性为“absolute”(绝对)。
    BackColor 设置或获取当前选中区的背景颜色。
    BlockDirLTR 目前尚未支持。
    BlockDirRTL 目前尚未支持。
    Bold 切换当前选中区的粗体显示与否。
    BrowseMode 目前尚未支持。
    Copy 将当前选中区复制到剪贴板。
    CreateBookmark 创建一个书签锚或获取当前选中区或插入点的书签锚的名称。
  [...]]]></description>
			<content:encoded><![CDATA[<p>document.execCommand()方法处理Html数据时常用语法格式如下:<br />
复制内容到剪贴板<br />
代码:<br />
document.execCommand(sCommand[,交互方式, 动态参数])</p>
<p>其中：sCommand为指令参数（如下例中的&#8221;2D-Position&#8221;），交互方式参数如果是true的话将显示对话框，如果为false的话，则不显示对话框（下例中的&#8221;false&#8221;即表示不显示对话框），动态参数一般为一可用值或属性值（如下例中的&#8221;true&#8221;）。</p>
<p><span id="more-33"></span><br />
document.execCommand(&#8221;2D-Position&#8221;,&#8221;false&#8221;,&#8221;true&#8221;);</p>
<p>调用execCommand()可以实现浏览器菜单的很多功能. 如保存文件,打开新文件,撤消、重做操作&#8230;等等. 有了这个方法,就可以很容易的实现网页中的文本编辑器.</p>
<p>如果灵活运用,可以很好的辅助我们完成各种项目.</p>
<p>使用的例子如下:</p>
<p>1、〖全选〗命令的实现<br />
[格式]:document.execCommand(&#8221;selectAll&#8221;)<br />
[说明]将选种网页中的全部内容！<br />
[举例]在<body></body>之间加入：<br />
<a href="#" onclick=document.execCommand("selectAll")>全选</a></p>
<p>2、〖打开〗命令的实现<br />
[格式]:document.execCommand(&#8221;open&#8221;)<br />
[说明]这跟VB等编程设计中的webbrowser控件中的命令有些相似，大家也可依此琢磨琢磨。<br />
[举例]在<body></body>之间加入：<br />
<a href="#" onclick=document.execCommand("open")>打开</a></p>
<p>3、〖另存为〗命令的实现<br />
[格式]:document.execCommand(&#8221;saveAs&#8221;)<br />
[说明]将该网页保存到本地盘的其它目录！<br />
[举例]在<body></body>之间加入：<br />
<a href="#" onclick=document.execCommand("saveAs")>另存为</a></p>
<p>4、〖打印〗命令的实现<br />
[格式]:document.execCommand(&#8221;print&#8221;)<br />
[说明]当然，你必须装了打印机！<br />
[举例]在<body></body>之间加入：<br />
<a href="#" onclick=document.execCommand("print")>打印</a></p>
<p>Js代码 下面列出的是指令参数及意义</p>
<p>//相当于单击文件中的打开按钮<br />
document.execCommand(&#8221;Open&#8221;);</p>
<p>//将当前页面另存为<br />
document.execCommand(&#8221;SaveAs&#8221;);</p>
<p>//剪贴选中的文字到剪贴板;<br />
document.execCommand(&#8221;Cut&#8221;,&#8221;false&#8221;,null);</p>
<p>//删除选中的文字;<br />
document.execCommand(&#8221;Delete&#8221;,&#8221;false&#8221;,null);</p>
<p>//改变选中区域的字体;<br />
document.execCommand(&#8221;FontName&#8221;,&#8221;false&#8221;,sFontName);</p>
<p>//改变选中区域的字体大小;<br />
document.execCommand(&#8221;FontSize&#8221;,&#8221;false&#8221;,sSize|iSize);</p>
<p>//设置前景颜色;<br />
document.execCommand(&#8221;ForeColor&#8221;,&#8221;false&#8221;,sColor);</p>
<p>//使绝对定位的对象可直接拖动;<br />
document.execCommand(&#8221;2D-Position&#8221;,&#8221;false&#8221;,&#8221;true&#8221;);</p>
<p>//使对象定位变成绝对定位;<br />
document.execCommand(&#8221;AbsolutePosition&#8221;,&#8221;false&#8221;,&#8221;true&#8221;);</p>
<p>//设置背景颜色;<br />
document.execCommand(&#8221;BackColor&#8221;,&#8221;false&#8221;,sColor);</p>
<p>//使选中区域的文字加粗;<br />
document.execCommand(&#8221;Bold&#8221;,&#8221;false&#8221;,null);</p>
<p>//复制选中的文字到剪贴板;<br />
document.execCommand(&#8221;Copy&#8221;,&#8221;false&#8221;,null);</p>
<p>//设置指定锚点为书签;<br />
document.execCommand(&#8221;CreateBookmark&#8221;,&#8221;false&#8221;,sAnchorName);</p>
<p>//将选中文本变成超连接,若第二个参数为true,会出现参数设置对话框;<br />
document.execCommand(&#8221;CreateLink&#8221;,&#8221;false&#8221;,sLinkURL);</p>
<p>//设置当前块的标签名;<br />
document.execCommand(&#8221;FormatBlock&#8221;,&#8221;false&#8221;,sTagName);  </p>
<p>//相当于单击文件中的打开按钮<br />
document.execCommand(&#8221;Open&#8221;);</p>
<p>//将当前页面另存为<br />
document.execCommand(&#8221;SaveAs&#8221;);</p>
<p>//剪贴选中的文字到剪贴板;<br />
document.execCommand(&#8221;Cut&#8221;,&#8221;false&#8221;,null);</p>
<p>//删除选中的文字;<br />
document.execCommand(&#8221;Delete&#8221;,&#8221;false&#8221;,null);</p>
<p>//改变选中区域的字体;<br />
document.execCommand(&#8221;FontName&#8221;,&#8221;false&#8221;,sFontName);</p>
<p>//改变选中区域的字体大小;<br />
document.execCommand(&#8221;FontSize&#8221;,&#8221;false&#8221;,sSize|iSize);</p>
<p>//设置前景颜色;<br />
document.execCommand(&#8221;ForeColor&#8221;,&#8221;false&#8221;,sColor);</p>
<p>//使绝对定位的对象可直接拖动;<br />
document.execCommand(&#8221;2D-Position&#8221;,&#8221;false&#8221;,&#8221;true&#8221;);</p>
<p>//使对象定位变成绝对定位;<br />
document.execCommand(&#8221;AbsolutePosition&#8221;,&#8221;false&#8221;,&#8221;true&#8221;);</p>
<p>//设置背景颜色;<br />
document.execCommand(&#8221;BackColor&#8221;,&#8221;false&#8221;,sColor);</p>
<p>//使选中区域的文字加粗;<br />
document.execCommand(&#8221;Bold&#8221;,&#8221;false&#8221;,null);</p>
<p>//复制选中的文字到剪贴板;<br />
document.execCommand(&#8221;Copy&#8221;,&#8221;false&#8221;,null);</p>
<p>//设置指定锚点为书签;<br />
document.execCommand(&#8221;CreateBookmark&#8221;,&#8221;false&#8221;,sAnchorName);</p>
<p>//将选中文本变成超连接,若第二个参数为true,会出现参数设置对话框;<br />
document.execCommand(&#8221;CreateLink&#8221;,&#8221;false&#8221;,sLinkURL);</p>
<p>//设置当前块的标签名;<br />
document.execCommand(&#8221;FormatBlock&#8221;,&#8221;false&#8221;,sTagName);</p>
<p>document对象execCommand通常在IE中在线处理Html数据时非常有用，它可以让你轻而易举实现文字的加粗、加颜色、加字体等一系列的命令。</p>
<p>    D-Position 允许通过拖曳移动绝对定位的对象。<br />
    AbsolutePosition 设定元素的 position 属性为“absolute”(绝对)。<br />
    BackColor 设置或获取当前选中区的背景颜色。<br />
    BlockDirLTR 目前尚未支持。<br />
    BlockDirRTL 目前尚未支持。<br />
    Bold 切换当前选中区的粗体显示与否。<br />
    BrowseMode 目前尚未支持。<br />
    Copy 将当前选中区复制到剪贴板。<br />
    CreateBookmark 创建一个书签锚或获取当前选中区或插入点的书签锚的名称。<br />
    CreateLink 在当前选中区上插入超级链接，或显示一个对话框允许用户指定要为当前选中区插入的超级链接的 URL。<br />
    Cut 将当前选中区复制到剪贴板并删除之。<br />
    Delete 删除当前选中区。<br />
    DirLTR 目前尚未支持。<br />
    DirRTL 目前尚未支持。<br />
    EditMode 目前尚未支持。<br />
    FontName 设置或获取当前选中区的字体。<br />
    FontSize 设置或获取当前选中区的字体大小。<br />
    ForeColor 设置或获取当前选中区的前景(文本)颜色。<br />
    FormatBlock 设置当前块格式化标签。<br />
    Indent 增加选中文本的缩进。<br />
    InlineDirLTR 目前尚未支持。<br />
    InlineDirRTL 目前尚未支持。<br />
    InsertButton 用按钮控件覆盖当前选中区。<br />
    InsertFieldset 用方框覆盖当前选中区。<br />
    InsertHorizontalRule 用水平线覆盖当前选中区。<br />
    InsertIFrame 用内嵌框架覆盖当前选中区。<br />
    InsertImage 用图像覆盖当前选中区。<br />
    InsertInputButton 用按钮控件覆盖当前选中区。<br />
    InsertInputCheckbox 用复选框控件覆盖当前选中区。<br />
    InsertInputFileUpload 用文件上载控件覆盖当前选中区。<br />
    InsertInputHidden 插入隐藏控件覆盖当前选中区。<br />
    InsertInputImage 用图像控件覆盖当前选中区。<br />
    InsertInputPassword 用密码控件覆盖当前选中区。<br />
    InsertInputRadio 用单选钮控件覆盖当前选中区。<br />
    InsertInputReset 用重置控件覆盖当前选中区。<br />
    InsertInputSubmit 用提交控件覆盖当前选中区。<br />
    InsertInputText 用文本控件覆盖当前选中区。<br />
    InsertMarquee 用空字幕覆盖当前选中区。<br />
   InsertOrderedList 切换当前选中区是编号列表还是常规格式化块。<br />
    InsertParagraph 用换行覆盖当前选中区。<br />
    InsertSelectDropdown 用下拉框控件覆盖当前选中区。<br />
    InsertSelectListbox 用列表框控件覆盖当前选中区。<br />
    InsertTextArea 用多行文本输入控件覆盖当前选中区。<br />
    InsertUnorderedList 切换当前选中区是项目符号列表还是常规格式化块。<br />
    Italic 切换当前选中区斜体显示与否。<br />
    JustifyCenter 将当前选中区在所在格式化块置中。<br />
    JustifyFull 目前尚未支持。<br />
    JustifyLeft 将当前选中区所在格式化块左对齐。<br />
    JustifyNone 目前尚未支持。<br />
    JustifyRight 将当前选中区所在格式化块右对齐。<br />
    LiveResize 迫使 MSHTML 编辑器在缩放或移动过程中持续更新元素外观，而不是只在移动或缩放完成后更新。<br />
    MultipleSelection 允许当用户按住 Shift 或 Ctrl 键时一次选中多于一个站点可选元素。<br />
    Open 目前尚未支持。<br />
    Outdent 减少选中区所在格式化块的缩进。<br />
    OverWrite 切换文本状态的插入和覆盖。<br />
    Paste 用剪贴板内容覆盖当前选中区。<br />
    PlayImage 目前尚未支持。<br />
    Print 打开打印对话框以便用户可以打印当前页。<br />
    Redo 目前尚未支持。<br />
    Refresh 刷新当前文档。<br />
    RemoveFormat 从当前选中区中删除格式化标签。<br />
    RemoveParaFormat 目前尚未支持。<br />
    SaveAs 将当前 Web 页面保存为文件。<br />
    SelectAll 选中整个文档。<br />
    SizeToControl 目前尚未支持。<br />
    SizeToControlHeight 目前尚未支持。<br />
    SizeToControlWidth 目前尚未支持。<br />
    Stop 目前尚未支持。<br />
    StopImage 目前尚未支持。<br />
    StrikeThrough 目前尚未支持。<br />
    Subscript 目前尚未支持。<br />
    Superscript 目前尚未支持。<br />
    UnBookmark 从当前选中区中删除全部书签。<br />
    Underline 切换当前选中区的下划线显示与否。<br />
    Undo 目前尚未支持。<br />
    Unlink 从当前选中区中删除全部超级链接。<br />
    Unselect 清除当前选中区的选中状态。</p>
<p>关于document.execCommand：<br />
          要执行编辑命令，可调用   document.execCommand，并传递对应于命令   ID   的字符串。另外还有可选的第二个参数，该参数指定如果可以应用的话是否显示此命令的用户界面。传递整数   1   将显示用户界面，整数   0   将跳过它。这个参数通常不用于编辑命令。因为默认值为   0，所以假如您没有使用第三个参数（在这种情况下，还必须为第二个参数传递值），一般可以不管它。第三个参数也是可选的，在可应用的情况下，使用它来将任何所需参数传递给该命令。</p>
<p>Pixy方法受到IE的cache bug影响会闪烁。其实并没有说清楚这个问题，但其实该bug是有条件的，即IE的cache设置为Every visit to the page，而不是默认的Automatically。基本上，只有开发者才会把cache设置为每次访问检查更新，所以这个bug其实不会影响真正的用户（根据在winxpsp2的ie6下测试，虽然可能仍然调用了一次网络存取的api，但是并没有发生实际的请求，症状就是鼠标有极短时间的抖动，但是图像不会闪烁）。此外有人发现了一个未公开的方法来让IE对背景图进行缓存：<br />
document.execCommand(&#8221;BackgroundImageCache&#8221;,false,true)</p>
<p>用这种方法甚至避免了api调用，貌似是直接缓存在IE内存中。</p>
<p>IE6下设置背景图片是不会被真正cache住的，就算服务器做了cache，如果想cache住只能~~~</p>
<p>做过UI设计和开发的人一定知道，IE(不包括IE7)会经常从服务器端重新载入背景图片，好端端的UI界面在IE(不包括IE7)中就这样被折腾着&#8230;&#8230;</p>
<p>Erik发现了一个简单的解决办法（针对IE7以下的IE有效，其实在IE7中已经修复了这个Bug）<br />
程序代码 程序代码</p>
<p>document.execCommand(&#8221;BackgroundImageCache&#8221;, false, true);</p>
<p>/**<br />
* update@2007-04-02<br />
*/<br />
今天阅读Ext的源码时发现Jack Slocum已经考虑到了这一点，在Ext.js中给出了他的实现，在其它Ajax框架中应该还没有这种类似的代码，从这一个细节上就能看出Ext的全面～<br />
程序代码 程序代码</p>
<p>var isIE = ua.indexOf(&#8221;msie&#8221;) > -1, isIE7 = ua.indexOf(&#8221;msie 7&#8243;) > -1;<br />
// remove css image flicker<br />
if(isIE &#038;&#038; !isIE7){<br />
     try{<br />
         document.execCommand(&#8221;BackgroundImageCache&#8221;, false, true);<br />
     }catch(e){}<br />
}</p>
<p>/**<br />
* update@2007-04-10<br />
*/<br />
今天阅读幻宇的dreamplayer播放器源码时发现幻宇也针对IE的背景缓存进行了修复，只是他并没考虑到IE7中已经不存在这个现象了，这是 evml.js中的一段相关代码～(顺便嘀咕两句：这家伙，写JS从来不加分号的，以前是这样，现在还是这样，这样的话怎么进行压缩呀，汗～下面的代码按照我的习惯都已加上分号，哪怕只有两三句而已～)<br />
程序代码 程序代码</p>
<p>window.isIE=navigator.appName.indexOf(&#8221;Microsoft&#8221;)==0;<br />
if(isIE){<br />
     document.documentElement.addBehavior(&#8221;#default#userdata&#8221;);<br />
     document.execCommand(&#8221;BackgroundImageCache&#8221;,false,true);</p>
<p><a href="http://www.cnblogs.com/Apollo/archive/2006/08/02/465520.html">查看更多关于execCommand的内容</a></p>
]]></content:encoded>
			<wfw:commentRss>http://blog.jerryjiang.net/?feed=rss2&amp;p=33</wfw:commentRss>
		</item>
		<item>
		<title>清明·合肥</title>
		<link>http://blog.jerryjiang.net/?p=28</link>
		<comments>http://blog.jerryjiang.net/?p=28#comments</comments>
		<pubDate>Mon, 13 Apr 2009 16:00:59 +0000</pubDate>
		<dc:creator>杰瑞</dc:creator>
		
		<category><![CDATA[Living]]></category>

		<guid isPermaLink="false">http://blog.jerryjiang.net/?p=28</guid>
		<description><![CDATA[清明我和我家小虎到了合肥，天气终于从不断的下雨中走了出来，很久没有看见太阳的我，开开心心在太阳下面游玩了两天，感觉很好，真希望以后每天都是这种天气，唉，不过时间过的很快，不知不觉三天就完了，感觉休息总是过的很快。

站在花丛中的我
]]></description>
			<content:encoded><![CDATA[<p>清明我和我家小虎到了合肥，天气终于从不断的下雨中走了出来，很久没有看见太阳的我，开开心心在太阳下面游玩了两天，感觉很好，真希望以后每天都是这种天气，唉，不过时间过的很快，不知不觉三天就完了，感觉休息总是过的很快。</p>
<p><img src="http://blog.jerryjiang.net/wp-content/uploads/2009/04/img_0008e589afe69cac.jpg" alt="img_0008e589afe69cac" title="img_0008e589afe69cac" width="600" height="450" class="aligncenter size-full wp-image-27" /><br />
站在花丛中的我</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.jerryjiang.net/?feed=rss2&amp;p=28</wfw:commentRss>
		</item>
		<item>
		<title>产品设计与用户体验——pony峰会分享笔记[转]</title>
		<link>http://blog.jerryjiang.net/?p=22</link>
		<comments>http://blog.jerryjiang.net/?p=22#comments</comments>
		<pubDate>Tue, 24 Mar 2009 07:33:22 +0000</pubDate>
		<dc:creator>杰瑞</dc:creator>
		
		<category><![CDATA[Living]]></category>

		<guid isPermaLink="false">http://blog.jerryjiang.net/?p=22</guid>
		<description><![CDATA[     背景：pony是公司的首席体验官、首席产品经理。这次在产品峰会上pony将自己平时经验的积累与大家交流，体验较细。这次分享研发管理部，设计中心整理了些材料。主要的案例是qqmail和qq影音的内容。以此为demo来讲解。
　　pony的讲解主要分为三大部分：产品设计、产品运营、交互设计。
　　在开场首先提到，互联网同类产品竞争激烈，只有抓住用户的心才能持续走下去。产品要赢得用户的心，要从一些小的点来赢得用户。
　　第一部分：产品设计
　　这个部分，感受最深的是两个词：核心能力、口碑。这部分还着重提到了pony对产品经理素质、开发人员心态的期望。
　　核心能力
　　任何产品都有核心功能，能帮助到用户，解决用户某一方面的需求，如节省时间、解决问题，提升效率等等。
　　很多产品经理对核心能力的关注不够，不是说完全没有关注，而是没有关注到度。核心能力不仅仅是功能上也，也包括性能上的。对于技术出身的产品经理，特别是做后台出来的，对于性能的关注，如果自己有能力、有信心做到对核心能力的关注，肯定会渴望将速度、后台做到极限。现在很多产品都没做好，一抓问题一大堆。如，前阵子网页速度优化，好多东西可以优化，一下提速好多，之前不知道都做什么去了。之前用户忍受了很久，同时浪费时间、浪费我们的资源。不抓，都没人理，很说部过去。要在性能方面放入更多精力。
　　谈到核心的能力，首先要有技术突破点。如做影音的时候，不是要做人家有我也有的东西。以前公司做的你有我有的东西，总是排在第二第三，虽然也有机会，但缺乏第一次出来亮相失去用户的认同感。
　　第一要关注你的产品的硬指标，在设计和开发的时候要考虑到外部会将对它与竞争对手做评测。如播放能力，占用内存。qq影音的核心性能和速度直接超越暴风影音。这样就能看到用户很多的好评和口碑。所以之后如果qq影音不出大问题，发展的势头将会很好。

　　硬指标评测cpu占用、高清加速，当时也有很多发展方向，如网络播放啊、交流啊、分享啊，也是思路。现在都砍掉，就是要做播放器，是用户的需求，纯用户需求不需要多少钱的。高清的，并不是很多人需要的，但是是高端用户的需求（这个后面口碑创造会再提到）。只有硬指标满足了，用户说，我这个破机器，暴风影音不能放，qq影音能放。这句话说出来，这样口碑就出来了。用户知道你行，差异化出来了。口碑要有差异性。
　　核心能力要做到极致。要多想如何通过技术实现差异化，人家做不到，或者通过半年一年才能追上来。
　　如，用户总评论qq的时候说用qq唯一的理由是传文件快，有群。于是这些就是我们的优势，那我们就要将优势发挥到极致。我们需要更加深入的去想，要想到要不要做传输速度、中转啊。离线传文件在邮件体现就是一个中转站，超大文件，也不难，就是要去做。产品部门很快的去做，去测试。用户用的量也不一定大，但几个月用一次，口碑就来了。用户会说，我要传大文件，找了半天找不到可以传的地方，万般无赖之下用了很烂的qqmail，居然行了。于是我们的口碑就来了。做了很多测试、逐步放量，看变化，因为到期就删掉，成本也没提升多少。
　　要做大，要考虑到如何做到极致让人家想到也追不上，我们这么多年在idc上的功力不能浪费，需要我们去做。高速上传、城域网中专站，支持高速地上传……，又发现问题，如不在邮件，在im做怎么体验，这个我们在后面要逐步考虑到做起来。我们的目的是要让用户感到超快、飞快，让用户体验非常好。这些都需要大量技术和后台来配合。
　　产品的发展都需要产品经理来配合。现在我们产品经理有是做研发出身的不多。而很多产品和服务是需要大量技术背景的，目前我们希望的产品经理是非常资深的，做过前端、后端开发的技术研发人员晋升而来的，刚毕业的人员来做产品经理很人担心。好的产品最好交到一个有技术能力的、有经验的产品人员手上，会让大家更加放心。如果产品人员太烂，让很多兄弟陪着干，结果发现方向错误是非常浪费和挫伤团队士气的。
　　产品最难的是订优先级和先后次序。要看哪个是用户最核心的。功能好不好不是说有用户用了，用量多少了，写个报告统计下流量证明是好。这个是很错误的，好不好要看用户是不是要用这个功能，用户要用的实时出现。腾讯很多产品经理的激情还不够，做出来的产品比较大路货。虽然挑剔不出很不对的东西，但放出去用户也没有感觉，最后就不了了之。pony有时候很痛心，希望大家在产品设计之初就想的透彻一点。产品经理需要投入更多的关注度，关注度不一样，结果出来的很不一样。
　　口碑
　　做产品要做口碑，要关注高端用户、意见领袖关注的点。以前的思路是抓大放小，满足大部分小白用户的需求。但是高端用户这块是真正可以拿口碑的。
　　如何提高口碑，看最高端用户的关注，这个是在基础功能比较好的情况下考虑。如邮件搜索啊，rss啊，这些是很炫的用户会在博客和论坛里面提及的。做起来也不难，在有能力的情况下保证。在产品已经成型的情况下，要考虑到，对高端用户的心态要不一样。如果想要获得高端用户的口碑，还需要在产品的设计上大气些。如，让用户在我们的qqmail上使用别的邮箱的地址，而不带任何自己qqmail的尾巴。之前我们做的时候不会自动保存别的邮箱的地址，自己心里打个小九九，让别人不方便使用外部邮箱地址，好使用我们的。这些小九九，高端用户都是看的出来，反倒不好。所以要改掉，要做到真正的方便到用户。 改变用户习惯要让他信任你，改变有过程的，需要通过我们的努力让用户慢慢改过来。如，关闭数字帐号，发现很多bug，拍拍都不知道改。如，独立密码，之前不是双密码，而是改整体密码。
　　需要满足高端用户，让他不要怀疑你、bs你。如浏览器到兼容，可能你会考虑很多浏览器的覆盖率不高而不去做，但在高端用户来看，这是个态度问题，如果你的产品连这个都没考虑，其他的我就都怀疑了。你这个产品团队的意识好不好。再如同文件夹是否对齐，是否会引起杀毒软件的报警，都是小事但要关注。
　　个性化服务，并不是大众化服务，也是拿口碑的。
　　一个产品在没有口碑的时候，不要滥用平台，如要im带呀，投入营销资源呀，要marking联系pr公司投放广告呀，广告位提要求……等着人家砍，想一半也够了。产品经理精力好像分布的很好50% 产品、30%营销、20%……。如果你在基础处控制的好，也可以。但90%的时候第一点都做不好。如果你的实力和胜算不到70-80%，那么把精力放在最核心的地方。在已经获得良好口碑，处于上升期的产品才考虑这些。
　　产品经理关注最最核心、获得用户口碑的战略点，如果这块没做透，做营销只是告诉用户过来，失望，再花更多的精力弥补，是得不偿失的。当用户没有自动在增长（用户会主动给朋友推荐来使用我们的产品的时候），看着用户的增长，否则不要去打扰用户，否则可能是好心办坏事。这个时候，每做一件事情，每加一个东西要很慎重的考虑，真的是有建设性的去增加产品的一个口碑。当用户口碑坏掉后，再将用户拉回来很难。
　　加功能，在管理控制功能上也要有技巧。在核心功能做好后，常用功能是要逐步补齐的。产品在局部、细小之处的创新需要永不满足。作为一个有良好口碑的产品，每加一个功能都要考虑清楚，这个功能给10%的用户带来好感的时候是否会给90%的用户带来困惑。如果有冲突的要聪明点，分情况避免。每个功能不一定要用的多才是好，而是用了的人都觉得好就是好。
　　做产品开发的时候需要有较强的研发机制保证，这样可以让产品开发更加敏捷更加快速。有些需求，提一下都可以得到很快反应。qqmail也会每天排好规划，为什么能很快反应，如文件加锁。有些产品做个东西写ppt、做汇报……，人家顺手就做了。很多产品不敏捷，大家要敏捷点、就算是大项目也要灵活。不能说等3个月后再给你个东西看，这个时候竞争对手都不知道跑到好远了。
　　开发人员的心态要关注产品，不要是公事公办的态度。你要知道用户、同行会关注你的产品，在这种驱动下开发人员要自动去完成。不能说什么都要产品做好后，流水线样的送过来我才做。开发人员要参与，40-50%左右的产品最终体验应该是由开发人员决定的。产品人员不要嫉妒有些工作是是开发人员设计的，只有这样才是团队共同参与的。如果都是产品想的就完蛋了，那么这个team做这个产品没有什么机会，必然会产生产品迭代慢的效果。这样一个格局太不行了。
　　运营式管理
　　这个部分感受最深的一个词：天天用。这部分还谈到了pony眼中产品经理的一些基本要求。
　　我们的产品不是单机版，需要有强的用户感和技术功底外，很重要的是服务。我们要关注很多很复杂的内容，如架构啊，应用啊，产品需要有更好的架构，这个是需要花很多精力，常态下可能看不出来。所以需要高层从kpi上考虑。这个是考功力，谁做的好，总办领导是看得到的。设计的好的架构不会手乱脚乱。如把核心的东西做成组件模块分发。
　　发现产品的不足，最简单的方法就是产品天天用。天天去看，去论坛，去博客、去订阅。产品经理要敏感点，找出你的产品不足之处。有的产品经理说找不出来很奇怪，上线的时候坚持三个月天天用，问题是有限的，一天发现一个，解决掉，这样慢慢的已经开始逼近你那个很有口碑的点了。不要因为工作没有技术含量就不去做，很多好的产品都是靠这个方法做出来的。对于高层来说，不仅仅是安排下面的人去做就可以了，一定要自己做。这些都不难，关键要坚持。意识要提高。你要做到每个周末，都心痒痒要去做。心里一定要想着，这个周末不试，肯定出事。这样坚持，到一个产品基本成型，就可以去看下个产品了。
　　从哪个地方找，论坛啊、博客啊，rss订阅啊。高端用户不屑于去论坛提，在博客提，需要产品经理自己去追出来。如qqmail、影音的产品经理自己去查、去搜，然后主动和用户接触，解决，有些确实是用户搞错了，有些是我们的问题。产品经理心态要很好，希望用户能找出问题我们再解决掉。哪怕再小的问题解决了也是完成一件大事。有些事情做了，见效很快。运营方面要天天去看的，产品经理要关注多个方面，比如说你的产品慢，用户不会管你的idc烂或者其他原因，只是知道你慢。产品经理要全面，服务器端哪个方面的问题能找出来。跟踪用户定位问题。如果pony都能搜索到的问题，没看到产品经理出现，那么就是你没做到位。
　　交互设计
　　交互要求我们细致，视觉简洁清爽。
　　产品经理要想到自己是个挑剔的用户，想像自己是个笨用户，复杂的看不懂。
　　产品人员的精力有限，交互内容很多，所以要抓最常见的一块。流量、用量最大的地方都要考虑。规范到要让用户使用的舒服。要在感觉、触觉上都有琢磨，有困惑要想到去改善。如鼠标少移动、可快速点到等等。
    像邮箱的一个按钮“返回”放在哪儿，上线测，放右边还是左边，大家都会多放琢磨，怎么放更好，并上线尝试，现在的方案折中比较好。如输入邮箱密码出错，输入框内的内容select上，不用用户清楚可以直接输出。这些都是对用户体验的优化。
　　如对同个用户发信，在此用户有多个邮箱的情况下会默认选最近用的一个帐号。这些需求都小，但你想清楚，用户就会说好，虽然用户未必说的出好在哪儿。
　　产品的使用要符合用户的习惯，如写邮件的时候copy东西，更多人习惯用键盘来操作。虽然有些技术难度，但也可以解决。交互，对鼠标反馈的灵敏性，便捷性。
　　不强迫用户，如点亮图标。如qqmail，不为1%的需求骚扰99%的用户
　　操作便利，如qq音乐，新旧列表，两者都要兼顾到，如qq影音的快捷播放，从圆形到方形，最后因为影响性能而放弃。
　　美术、淡淡的，点到即止，如qqmail，qqmail在ui上的启发，不用太重也能做的很好。后来用在大量的产品，如hummer、影音。有图案和简洁不矛盾。
　　重点突出，防止不必要的低龄化，还提到了一些内容，如产品成功关键点等等，这些在pony的ppt上有，没有记下来，大家可以在之后腾讯峰会吧（http://km.oa.com/group/forum）直接看pony的ppt。
　　最后pony谈了一下有些产品的态度问题——态度很好，不解决问题。只做表面功夫，与其花一段时间写个长长的报告，不如实实际际的去解决问题。
　　外部也有很多优秀的产品可以学习，学习不是学皮毛，学样子，要学会。
　　外部可以学习的优秀产品，web类的，google、yahoo、facebook、apple，非web类的没有记录下来。
　　总结一下：
　　1、核心功能要做透，做的人家追不上，自己的优势要尽量的发挥；
　　2、产品口碑要建立，要关注高端用户，要调整自己心态；
　　3、敏捷、快，产品迭代要快，快速实现、快速响应，要做到真正的迭代；
　　4、产品人员要全面，要能找出核心需求，要关注技术（架构、服务是不是好），要关注产品（天天用），要关注用户（还需要出去寻找问题并解决）；
　　5、开发人员心态要好，要有参与感，不要被动的等；
　　6、交互设计简洁，关注要点，当自己是个挑剔的、笨的用户；
　　7、想办法利用公司的资源，如pony等人都是大家的公共资源，要争取到pony对自己产品的关注，会给你的产品带来很多好的指导和创意。（这个不是pony说的，是后来jeff提到的，不过很实在，pony做过这么多的产品，有这么好的产品感觉，如果给你的产品提出建议，是对产品很大的帮助） 
]]></description>
			<content:encoded><![CDATA[<p>     背景：pony是公司的首席体验官、首席产品经理。这次在产品峰会上pony将自己平时经验的积累与大家交流，体验较细。这次分享研发管理部，设计中心整理了些材料。主要的案例是qqmail和qq影音的内容。以此为demo来讲解。</p>
<p>　　pony的讲解主要分为三大部分：产品设计、产品运营、交互设计。</p>
<p>　　在开场首先提到，互联网同类产品竞争激烈，只有抓住用户的心才能持续走下去。产品要赢得用户的心，要从一些小的点来赢得用户。</p>
<p>　　第一部分：产品设计</p>
<p>　　这个部分，感受最深的是两个词：核心能力、口碑。这部分还着重提到了pony对产品经理素质、开发人员心态的期望。</p>
<p>　　核心能力<br />
　　任何产品都有核心功能，能帮助到用户，解决用户某一方面的需求，如节省时间、解决问题，提升效率等等。<br />
　　很多产品经理对核心能力的关注不够，不是说完全没有关注，而是没有关注到度。核心能力不仅仅是功能上也，也包括性能上的。对于技术出身的产品经理，特别是做后台出来的，对于性能的关注，如果自己有能力、有信心做到对核心能力的关注，肯定会渴望将速度、后台做到极限。现在很多产品都没做好，一抓问题一大堆。如，前阵子网页速度优化，好多东西可以优化，一下提速好多，之前不知道都做什么去了。之前用户忍受了很久，同时浪费时间、浪费我们的资源。不抓，都没人理，很说部过去。要在性能方面放入更多精力。</p>
<p>　　谈到核心的能力，首先要有技术突破点。如做影音的时候，不是要做人家有我也有的东西。以前公司做的你有我有的东西，总是排在第二第三，虽然也有机会，但缺乏第一次出来亮相失去用户的认同感。</p>
<p>　　第一要关注你的产品的硬指标，在设计和开发的时候要考虑到外部会将对它与竞争对手做评测。如播放能力，占用内存。qq影音的核心性能和速度直接超越暴风影音。这样就能看到用户很多的好评和口碑。所以之后如果qq影音不出大问题，发展的势头将会很好。<br />
<span id="more-22"></span></p>
<p>　　硬指标评测cpu占用、高清加速，当时也有很多发展方向，如网络播放啊、交流啊、分享啊，也是思路。现在都砍掉，就是要做播放器，是用户的需求，纯用户需求不需要多少钱的。高清的，并不是很多人需要的，但是是高端用户的需求（这个后面口碑创造会再提到）。只有硬指标满足了，用户说，我这个破机器，暴风影音不能放，qq影音能放。这句话说出来，这样口碑就出来了。用户知道你行，差异化出来了。口碑要有差异性。</p>
<p>　　核心能力要做到极致。要多想如何通过技术实现差异化，人家做不到，或者通过半年一年才能追上来。</p>
<p>　　如，用户总评论qq的时候说用qq唯一的理由是传文件快，有群。于是这些就是我们的优势，那我们就要将优势发挥到极致。我们需要更加深入的去想，要想到要不要做传输速度、中转啊。离线传文件在邮件体现就是一个中转站，超大文件，也不难，就是要去做。产品部门很快的去做，去测试。用户用的量也不一定大，但几个月用一次，口碑就来了。用户会说，我要传大文件，找了半天找不到可以传的地方，万般无赖之下用了很烂的qqmail，居然行了。于是我们的口碑就来了。做了很多测试、逐步放量，看变化，因为到期就删掉，成本也没提升多少。</p>
<p>　　要做大，要考虑到如何做到极致让人家想到也追不上，我们这么多年在idc上的功力不能浪费，需要我们去做。高速上传、城域网中专站，支持高速地上传……，又发现问题，如不在邮件，在im做怎么体验，这个我们在后面要逐步考虑到做起来。我们的目的是要让用户感到超快、飞快，让用户体验非常好。这些都需要大量技术和后台来配合。</p>
<p>　　产品的发展都需要产品经理来配合。现在我们产品经理有是做研发出身的不多。而很多产品和服务是需要大量技术背景的，目前我们希望的产品经理是非常资深的，做过前端、后端开发的技术研发人员晋升而来的，刚毕业的人员来做产品经理很人担心。好的产品最好交到一个有技术能力的、有经验的产品人员手上，会让大家更加放心。如果产品人员太烂，让很多兄弟陪着干，结果发现方向错误是非常浪费和挫伤团队士气的。</p>
<p>　　产品最难的是订优先级和先后次序。要看哪个是用户最核心的。功能好不好不是说有用户用了，用量多少了，写个报告统计下流量证明是好。这个是很错误的，好不好要看用户是不是要用这个功能，用户要用的实时出现。腾讯很多产品经理的激情还不够，做出来的产品比较大路货。虽然挑剔不出很不对的东西，但放出去用户也没有感觉，最后就不了了之。pony有时候很痛心，希望大家在产品设计之初就想的透彻一点。产品经理需要投入更多的关注度，关注度不一样，结果出来的很不一样。</p>
<p>　　口碑<br />
　　做产品要做口碑，要关注高端用户、意见领袖关注的点。以前的思路是抓大放小，满足大部分小白用户的需求。但是高端用户这块是真正可以拿口碑的。</p>
<p>　　如何提高口碑，看最高端用户的关注，这个是在基础功能比较好的情况下考虑。如邮件搜索啊，rss啊，这些是很炫的用户会在博客和论坛里面提及的。做起来也不难，在有能力的情况下保证。在产品已经成型的情况下，要考虑到，对高端用户的心态要不一样。如果想要获得高端用户的口碑，还需要在产品的设计上大气些。如，让用户在我们的qqmail上使用别的邮箱的地址，而不带任何自己qqmail的尾巴。之前我们做的时候不会自动保存别的邮箱的地址，自己心里打个小九九，让别人不方便使用外部邮箱地址，好使用我们的。这些小九九，高端用户都是看的出来，反倒不好。所以要改掉，要做到真正的方便到用户。 改变用户习惯要让他信任你，改变有过程的，需要通过我们的努力让用户慢慢改过来。如，关闭数字帐号，发现很多bug，拍拍都不知道改。如，独立密码，之前不是双密码，而是改整体密码。</p>
<p>　　需要满足高端用户，让他不要怀疑你、bs你。如浏览器到兼容，可能你会考虑很多浏览器的覆盖率不高而不去做，但在高端用户来看，这是个态度问题，如果你的产品连这个都没考虑，其他的我就都怀疑了。你这个产品团队的意识好不好。再如同文件夹是否对齐，是否会引起杀毒软件的报警，都是小事但要关注。</p>
<p>　　个性化服务，并不是大众化服务，也是拿口碑的。</p>
<p>　　一个产品在没有口碑的时候，不要滥用平台，如要im带呀，投入营销资源呀，要marking联系pr公司投放广告呀，广告位提要求……等着人家砍，想一半也够了。产品经理精力好像分布的很好50% 产品、30%营销、20%……。如果你在基础处控制的好，也可以。但90%的时候第一点都做不好。如果你的实力和胜算不到70-80%，那么把精力放在最核心的地方。在已经获得良好口碑，处于上升期的产品才考虑这些。</p>
<p>　　产品经理关注最最核心、获得用户口碑的战略点，如果这块没做透，做营销只是告诉用户过来，失望，再花更多的精力弥补，是得不偿失的。当用户没有自动在增长（用户会主动给朋友推荐来使用我们的产品的时候），看着用户的增长，否则不要去打扰用户，否则可能是好心办坏事。这个时候，每做一件事情，每加一个东西要很慎重的考虑，真的是有建设性的去增加产品的一个口碑。当用户口碑坏掉后，再将用户拉回来很难。</p>
<p>　　加功能，在管理控制功能上也要有技巧。在核心功能做好后，常用功能是要逐步补齐的。产品在局部、细小之处的创新需要永不满足。作为一个有良好口碑的产品，每加一个功能都要考虑清楚，这个功能给10%的用户带来好感的时候是否会给90%的用户带来困惑。如果有冲突的要聪明点，分情况避免。每个功能不一定要用的多才是好，而是用了的人都觉得好就是好。</p>
<p>　　做产品开发的时候需要有较强的研发机制保证，这样可以让产品开发更加敏捷更加快速。有些需求，提一下都可以得到很快反应。qqmail也会每天排好规划，为什么能很快反应，如文件加锁。有些产品做个东西写ppt、做汇报……，人家顺手就做了。很多产品不敏捷，大家要敏捷点、就算是大项目也要灵活。不能说等3个月后再给你个东西看，这个时候竞争对手都不知道跑到好远了。</p>
<p>　　开发人员的心态要关注产品，不要是公事公办的态度。你要知道用户、同行会关注你的产品，在这种驱动下开发人员要自动去完成。不能说什么都要产品做好后，流水线样的送过来我才做。开发人员要参与，40-50%左右的产品最终体验应该是由开发人员决定的。产品人员不要嫉妒有些工作是是开发人员设计的，只有这样才是团队共同参与的。如果都是产品想的就完蛋了，那么这个team做这个产品没有什么机会，必然会产生产品迭代慢的效果。这样一个格局太不行了。</p>
<p>　　运营式管理</p>
<p>　　这个部分感受最深的一个词：天天用。这部分还谈到了pony眼中产品经理的一些基本要求。</p>
<p>　　我们的产品不是单机版，需要有强的用户感和技术功底外，很重要的是服务。我们要关注很多很复杂的内容，如架构啊，应用啊，产品需要有更好的架构，这个是需要花很多精力，常态下可能看不出来。所以需要高层从kpi上考虑。这个是考功力，谁做的好，总办领导是看得到的。设计的好的架构不会手乱脚乱。如把核心的东西做成组件模块分发。</p>
<p>　　发现产品的不足，最简单的方法就是产品天天用。天天去看，去论坛，去博客、去订阅。产品经理要敏感点，找出你的产品不足之处。有的产品经理说找不出来很奇怪，上线的时候坚持三个月天天用，问题是有限的，一天发现一个，解决掉，这样慢慢的已经开始逼近你那个很有口碑的点了。不要因为工作没有技术含量就不去做，很多好的产品都是靠这个方法做出来的。对于高层来说，不仅仅是安排下面的人去做就可以了，一定要自己做。这些都不难，关键要坚持。意识要提高。你要做到每个周末，都心痒痒要去做。心里一定要想着，这个周末不试，肯定出事。这样坚持，到一个产品基本成型，就可以去看下个产品了。</p>
<p>　　从哪个地方找，论坛啊、博客啊，rss订阅啊。高端用户不屑于去论坛提，在博客提，需要产品经理自己去追出来。如qqmail、影音的产品经理自己去查、去搜，然后主动和用户接触，解决，有些确实是用户搞错了，有些是我们的问题。产品经理心态要很好，希望用户能找出问题我们再解决掉。哪怕再小的问题解决了也是完成一件大事。有些事情做了，见效很快。运营方面要天天去看的，产品经理要关注多个方面，比如说你的产品慢，用户不会管你的idc烂或者其他原因，只是知道你慢。产品经理要全面，服务器端哪个方面的问题能找出来。跟踪用户定位问题。如果pony都能搜索到的问题，没看到产品经理出现，那么就是你没做到位。</p>
<p>　　交互设计</p>
<p>　　交互要求我们细致，视觉简洁清爽。</p>
<p>　　产品经理要想到自己是个挑剔的用户，想像自己是个笨用户，复杂的看不懂。</p>
<p>　　产品人员的精力有限，交互内容很多，所以要抓最常见的一块。流量、用量最大的地方都要考虑。规范到要让用户使用的舒服。要在感觉、触觉上都有琢磨，有困惑要想到去改善。如鼠标少移动、可快速点到等等。</p>
<p>    像邮箱的一个按钮“返回”放在哪儿，上线测，放右边还是左边，大家都会多放琢磨，怎么放更好，并上线尝试，现在的方案折中比较好。如输入邮箱密码出错，输入框内的内容select上，不用用户清楚可以直接输出。这些都是对用户体验的优化。</p>
<p>　　如对同个用户发信，在此用户有多个邮箱的情况下会默认选最近用的一个帐号。这些需求都小，但你想清楚，用户就会说好，虽然用户未必说的出好在哪儿。</p>
<p>　　产品的使用要符合用户的习惯，如写邮件的时候copy东西，更多人习惯用键盘来操作。虽然有些技术难度，但也可以解决。交互，对鼠标反馈的灵敏性，便捷性。</p>
<p>　　不强迫用户，如点亮图标。如qqmail，不为1%的需求骚扰99%的用户</p>
<p>　　操作便利，如qq音乐，新旧列表，两者都要兼顾到，如qq影音的快捷播放，从圆形到方形，最后因为影响性能而放弃。</p>
<p>　　美术、淡淡的，点到即止，如qqmail，qqmail在ui上的启发，不用太重也能做的很好。后来用在大量的产品，如hummer、影音。有图案和简洁不矛盾。</p>
<p>　　重点突出，防止不必要的低龄化，还提到了一些内容，如产品成功关键点等等，这些在pony的ppt上有，没有记下来，大家可以在之后腾讯峰会吧（http://km.oa.com/group/forum）直接看pony的ppt。</p>
<p>　　最后pony谈了一下有些产品的态度问题——态度很好，不解决问题。只做表面功夫，与其花一段时间写个长长的报告，不如实实际际的去解决问题。</p>
<p>　　外部也有很多优秀的产品可以学习，学习不是学皮毛，学样子，要学会。</p>
<p>　　外部可以学习的优秀产品，web类的，google、yahoo、facebook、apple，非web类的没有记录下来。</p>
<p>　　总结一下：</p>
<p>　　1、核心功能要做透，做的人家追不上，自己的优势要尽量的发挥；</p>
<p>　　2、产品口碑要建立，要关注高端用户，要调整自己心态；</p>
<p>　　3、敏捷、快，产品迭代要快，快速实现、快速响应，要做到真正的迭代；</p>
<p>　　4、产品人员要全面，要能找出核心需求，要关注技术（架构、服务是不是好），要关注产品（天天用），要关注用户（还需要出去寻找问题并解决）；</p>
<p>　　5、开发人员心态要好，要有参与感，不要被动的等；</p>
<p>　　6、交互设计简洁，关注要点，当自己是个挑剔的、笨的用户；</p>
<p>　　7、想办法利用公司的资源，如pony等人都是大家的公共资源，要争取到pony对自己产品的关注，会给你的产品带来很多好的指导和创意。（这个不是pony说的，是后来jeff提到的，不过很实在，pony做过这么多的产品，有这么好的产品感觉，如果给你的产品提出建议，是对产品很大的帮助） </p>
]]></content:encoded>
			<wfw:commentRss>http://blog.jerryjiang.net/?feed=rss2&amp;p=22</wfw:commentRss>
		</item>
		<item>
		<title>10大实用web应用界面技术[译]</title>
		<link>http://blog.jerryjiang.net/?p=17</link>
		<comments>http://blog.jerryjiang.net/?p=17#comments</comments>
		<pubDate>Tue, 24 Mar 2009 07:06:59 +0000</pubDate>
		<dc:creator>杰瑞</dc:creator>
		
		<category><![CDATA[Php Development]]></category>

		<guid isPermaLink="false">http://blog.jerryjiang.net/?p=17</guid>
		<description><![CDATA[
     无事看到不错的外文，简单翻译了一下共享出来，希望对大家有用。（转载请表明出处，以下为译文全部。）
　　当今越来越多的应用程序迁移到web平台上。由于没有平台的限制和安装的要求，SAAS的模式看起来非常有吸引力。Web应用程序的界面设计，其核心就是网页设计，但它的重点主要是在功能方面。要超越桌面应用程序， Web应用程序必须提供简单、直观和即时响应的用户界面，让他们的用户花更少的精力和时间去完成事情。
　　以前，我们并没有注意到web应用程序这个方式，但是现在是时候仔细看看一些实用的技术和设计解决方案，让Web应用更友好更漂亮。本文主要介绍的是在现代Web应用程序中，我们对设计模式以及有效设计解决方案进行大量研究并得到结果的第一部分，您可以在下面找到10个收集到的实用界面设计技术以及在许多成功Web应用程序中得出的最佳做法。
查看更多精彩内容
]]></description>
			<content:encoded><![CDATA[<p><img src="http://blog.jerryjiang.net/wp-content/uploads/2009/03/5200911318442-300x47.jpg" alt="5200911318442" title="5200911318442" width="300" height="47" class="aligncenter size-medium wp-image-18" /></p>
<p>     无事看到不错的外文，简单翻译了一下共享出来，希望对大家有用。（转载请表明出处，以下为译文全部。）</p>
<p>　　当今越来越多的应用程序迁移到web平台上。由于没有平台的限制和安装的要求，SAAS的模式看起来非常有吸引力。Web应用程序的界面设计，其核心就是网页设计，但它的重点主要是在功能方面。要超越桌面应用程序， Web应用程序必须提供简单、直观和即时响应的用户界面，让他们的用户花更少的精力和时间去完成事情。</p>
<p>　　以前，我们并没有注意到web应用程序这个方式，但是现在是时候仔细看看一些实用的技术和设计解决方案，让Web应用更友好更漂亮。本文主要介绍的是在现代Web应用程序中，我们对设计模式以及有效设计解决方案进行大量研究并得到结果的第一部分，您可以在下面找到10个收集到的实用界面设计技术以及在许多成功Web应用程序中得出的最佳做法。</p>
<p><a href="http://www.jojobox.cn/blog/article.asp?id=151">查看更多精彩内容</a></p>
]]></content:encoded>
			<wfw:commentRss>http://blog.jerryjiang.net/?feed=rss2&amp;p=17</wfw:commentRss>
		</item>
		<item>
		<title>php邮件发送类</title>
		<link>http://blog.jerryjiang.net/?p=10</link>
		<comments>http://blog.jerryjiang.net/?p=10#comments</comments>
		<pubDate>Tue, 24 Mar 2009 06:21:16 +0000</pubDate>
		<dc:creator>杰瑞</dc:creator>
		
		<category><![CDATA[Php Development]]></category>

		<guid isPermaLink="false">http://blog.jerryjiang.net/?p=10</guid>
		<description><![CDATA[新开张之际放上以前写的一个php 邮件发送类，还是挺好用的，如果有什么要补充的请大家指教


/**
 * php Socket邮件发送类
 *
 * 提供用户邮件smtp发送,需要用户名和密码
 * @package     Class
 * @author      jerry 
 */
class SendMail
{
	var $smtp;             //smtp地址
	var $port;             //smtp端口号
	var $auth_username; [...]]]></description>
			<content:encoded><![CDATA[<p>新开张之际放上以前写的一个php 邮件发送类，还是挺好用的，如果有什么要补充的请大家指教</p>
<p><span id="more-10"></span></p>
<p><code></p>
<p>/**<br />
 * php Socket邮件发送类<br />
 *<br />
 * 提供用户邮件smtp发送,需要用户名和密码<br />
 * @package     Class<br />
 * @author      jerry <jerryjiang15@gmail.com><br />
 */<br />
class SendMail<br />
{<br />
	var $smtp;             //smtp地址<br />
	var $port;             //smtp端口号<br />
	var $auth_username;    //smtp邮箱用户名<br />
	var $auth_password;    //smtp邮箱密码<br />
	var $host_name;        //主机名字<br />
	var $return_info = array();  //是否显示调试信息<br />
	var $fp;               //socket句柄<br />
	var $attachment = array();   //附件</p>
<p>	/**<br />
	 * 构造函数<br />
	 *<br />
	 * @param string $smtp   smtp服务器地址<br />
	 * @param int $port      端口号<br />
	 * @param string $auth_username  登陆名<br />
	 * @param string $auth_password  密码<br />
	 * @param string $host_name    服务器名字<br />
	 */<br />
	function SendMail($smtp,$port = &#8216;25&#8242;,$auth_username = &#8221;,$auth_password = &#8221;,$host_name = &#8221;)<br />
	{<br />
		$this->smtp = $smtp;<br />
		$this->port = $port;<br />
		$this->auth_username = $auth_username;<br />
		$this->auth_password = $auth_password;</p>
<p>		if(empty($host_name))<br />
		{<br />
			$this->host_name = gethostbyaddr($_SERVER['REMOTE_ADDR']);<br />
		}<br />
		else<br />
		{<br />
			$this->host_name = $host_name;<br />
		}<br />
	}</p>
<p>	 function doCommand($command, $code)<br />
    {<br />
    	fputs($this->fp,$command);</p>
<p>    	$last_message = fgets ( $this->fp, 512 );</p>
<p>    	$error[command] = $command;<br />
        $error[message] = $last_message;</p>
<p>        $this->return_info[] = $error;</p>
<p>        if(!ereg(&#8221;^$code&#8221;, $last_message))<br />
        {<br />
            return false;<br />
        }<br />
        else<br />
        {<br />
        	return true;<br />
        }<br />
    }</p>
<p>    function addAttachment($file)<br />
    {<br />
    	if(file_exists($file))<br />
    	{<br />
    		$this->attachment[] = $file;<br />
		}<br />
		else<br />
		{<br />
			die(&#8221;The file $file does not exist&#8221;);<br />
		}<br />
    }</p>
<p>    function clearAttachment()<br />
    {<br />
    	$this->attachment = &#8221;;<br />
    }</p>
<p>    /**<br />
     * Determine if a string contains 8-bit characters.<br />
     * @access private<br />
     *<br />
     * @param string $string  The string to check.<br />
     * @return boolean        true if it does, false if it doesn&#8217;t.<br />
     */<br />
    function is8bit($string)<br />
    {<br />
        if (is_string($string))<br />
        {<br />
            for ($i = 0; $i < strlen($string); $i++)<br />
            {<br />
                if (ord($string[$i]) >> 7)<br />
                    return true;<br />
            }<br />
            return false;<br />
        }<br />
        return false;<br />
    }</p>
<p>    /**<br />
     * Encode a string containing non-ascii characters according to RFC 2047.<br />
     *<br />
     * @param string $text    The text to encode.<br />
     * @param string $charset (optional) The character set of the text.<br />
     * @param boolean $outer  Is this the final iteration?<br />
     *<br />
     * @return string The text, encoded only if it contains non-ascii characters.<br />
     */<br />
    function encode($text, $charset = null, $outer = true)<br />
    {<br />
        if ($this->is8bit($text)) {<br />
            if (((strlen($text) * 3) + strlen($charset) + 7) > 76) {<br />
                $text = $this->encode(substr($text, 0, (23 - strlen($charset))), $charset) . $this->encode(substr($text, (23 - strlen($charset))), $charset, false);<br />
            } else {<br />
                $text = &#8220;=?$charset?B?&#8221; . strtr(trim(base64_encode($text)), &#8216; &#8216;, &#8216;_&#8217;) . &#8220;?=\n\t&#8221;;<br />
            }<br />
        }</p>
<p>        // if this is the final iteration, take off any trailing<br />
        // newline/tab chars.<br />
        if ($outer &#038;&#038; (substr($text, -2) == &#8220;\n\t&#8221;))<br />
            $text = substr($text, 0, -2);</p>
<p>        return $text;<br />
    }</p>
<p>    function debug()<br />
    {<br />
    	print_r($this->return_info);<br />
    }</p>
<p>    function getContentType($file_name)<br />
    {<br />
		//&#8211;去除路径<br />
		$file_name = basename($file_name);<br />
		//&#8211;去除没有扩展名的文件<br />
		if(strrchr($file_name, &#8220;.&#8221;) == false)<br />
		{<br />
			return &#8220;application/octet-stream&#8221;;<br />
		}<br />
		//&#8211;提区扩展名并进行判断<br />
		$extension = strrchr($file_name, &#8220;.&#8221;); </p>
<p>		switch($extension)<br />
		{<br />
			case &#8220;.gif&#8221;:<br />
				return &#8220;image/gif&#8221;;<br />
			break;<br />
			case &#8220;.gz&#8221;:<br />
				return &#8220;application/x-gzip&#8221;;<br />
			break;<br />
			case &#8220;.htm&#8221;:<br />
				return &#8220;text/html&#8221;;<br />
			break;<br />
			case &#8220;.html&#8221;:<br />
				return &#8220;text/html&#8221;;<br />
			break;<br />
			case &#8220;.jpg&#8221;:<br />
				return &#8220;image/jpeg&#8221;;<br />
			break;<br />
			case &#8220;.tar&#8221;:<br />
				return &#8220;application/x-tar&#8221;;<br />
			break;<br />
			case &#8220;.txt&#8221;:<br />
				return &#8220;text/plain&#8221;;<br />
			break;<br />
			case &#8220;.zip&#8221;:<br />
				return &#8220;application/zip&#8221;;<br />
			break;<br />
			default:<br />
				return &#8220;application/octet-stream&#8221;;<br />
			break;<br />
		} </p>
<p>		return &#8220;application/octet-stream&#8221;;<br />
	} </p>
<p>	function send($from,$auth = &#8221;,$to,$receiver,$subject,$message,$type = &#8216;txt&#8217;,$charset = &#8216;gb2312&#8242;)<br />
	{<br />
		$this->fp = fsockopen($this->smtp,$this->port,$errno,$errstr,120) or die(&#8221;not to connection this server:$this->smtp&#8221;);<br />
		$this->return_info[] = $return = fgets($this->fp,512);<br />
		if( !ereg(&#8221;^220&#8243;,$return) )   //判断服务器是否就绪<br />
		{<br />
			fclose($this->fp);<br />
			return false;<br />
		}</p>
<p>		if($this->doCommand(&#8221;helo {$this->host_name}\r\n&#8221;,&#8217;250&#8242;) === false)   //与服务器打招呼<br />
		{<br />
			fclose($this->fp);<br />
			return false;<br />
		}</p>
<p>		//如果需要用户验证<br />
		if($this->auth_username)<br />
		{<br />
			if($this->doCommand(&#8221;auth login\r\n&#8221;,&#8217;334&#8242;) === false)   //开始确认用户和密码<br />
			{<br />
				fclose($this->fp);<br />
				return false;<br />
			}</p>
<p>			if($this->doCommand(base64_encode(&#8221;{$this->auth_username}&#8221;).&#8221;\r\n&#8221;,&#8217;334&#8242;) === false)   //提交用戶名<br />
			{<br />
				fclose($this->fp);<br />
				return false;<br />
			}</p>
<p>			if($this->doCommand(base64_encode(&#8221;{$this->auth_password}&#8221;).&#8221;\r\n&#8221;,&#8217;235&#8242;) === false)   //提交密码<br />
			{<br />
				fclose($this->fp);<br />
				return false;<br />
			}<br />
		}</p>
<p>		if($this->doCommand(&#8221;MAIL FROM:<{$from}> \r\n&#8221;,&#8217;250&#8242;) === false)   //提交發信人EMAIL址<br />
		{<br />
			fclose($this->fp);<br />
			return false;<br />
		}</p>
<p>		$to = explode(&#8217;,',$to);</p>
<p>		foreach ($to as $value)<br />
		{<br />
			if($this->doCommand(&#8221;RCPT TO:<{$value}> \r\n&#8221;,&#8217;250&#8242;) === false)   //提交收件EMAIL址<br />
			{<br />
				fclose($this->fp);<br />
				return false;<br />
			}<br />
		}</p>
<p>		if($this->doCommand(&#8221;RCPT TO:<{$to}> \r\n&#8221;,&#8217;250&#8242;) === false)   //提交收件EMAIL址<br />
		{<br />
			fclose($this->fp);<br />
			return false;<br />
		}</p>
<p>		if($this->doCommand(&#8221;data\r\n&#8221;,&#8217;354&#8242;) === false)   //提交内容申请<br />
		{<br />
			fclose($this->fp);<br />
			return false;<br />
		}</p>
<p>		$to_send = &#8220;Return-Path: <$from>\r\n&#8221;;          //信件內容</p>
<p>		if(!empty($auth))<br />
		{<br />
			$to_send .= &#8220;From: &#8221; . $this->encode($auth,$charset) . &#8221; <$from>\r\n&#8221;;<br />
		}<br />
		else<br />
		{<br />
			$to_send .= &#8220;From: $from\r\n&#8221;;<br />
		}<br />
		if(!empty($receiver))<br />
		{<br />
			if(count($to) == 1)<br />
			{<br />
				$to_send .= &#8220;To: &#8221; . $this->encode($receiver,$charset) . &#8221; <" . $to[0] . ">\r\n&#8221;;<br />
			}<br />
			else<br />
			{<br />
				$to_send .= &#8220;To: &#8221; . $this->encode($receiver,$charset) . &#8221; <everyone@anymail.com>\r\n&#8221;;<br />
			}<br />
		}</p>
<p>		$to_send .= &#8216;Subject: &#8216; . $this->encode(str_replace(&#8221;\n&#8221;, &#8216; &#8216;, $subject),$charset) . &#8220;\r\n&#8221;; </p>
<p>		if(!empty($this->attachment))<br />
    	{<br />
    		$boundary = uniqid(&#8221;");<br />
    		$to_send .= &#8220;MIME-Version: 1.0\r\n&#8221;;<br />
    		$to_send .= &#8220;Content-Type: multipart/mixed; boundary=\&#8221;$boundary\&#8221;\r\n&#8221;;<br />
    		$to_send .= &#8220;This is a multi-part message in MIME format.&#8221;;<br />
    		$to_send .= &#8220;\r\n\r\n&#8211;$boundary\r\n&#8221;;<br />
    	}</p>
<p>    	if($type == &#8216;txt&#8217;)<br />
		{<br />
			$to_send .= &#8220;Content-type: text/plain;&#8221;;<br />
		}<br />
		else<br />
		{<br />
			$to_send .= &#8220;Content-type: text/html;&#8221;;<br />
		}</p>
<p>    	$to_send .= &#8220;charset=$charset\r\n&#8221;;<br />
    	$to_send .= &#8220;Content-transfer-encoding: 8bit\r\n&#8221;; </p>
<p>    	if(empty($this->attachment))<br />
    	{<br />
    		$to_send .= &#8220;\r\n$message\r\n.\r\n&#8221;;<br />
    	}<br />
		else<br />
		{<br />
			$to_send .= &#8220;\r\n$message\r\n\r\n&#8221;;<br />
		}</p>
<p>		if(!empty($this->attachment))<br />
    	{<br />
    		foreach ($this->attachment as $key=>$value)<br />
    		{</p>
<p>    			# 确定上传文件的MIME类型<br />
				$mime_type = $this->getContentType($value);</p>
<p>				# 确定文件的名字<br />
				$attachment_name = str_replace(&#8221;\\&#8221;,&#8221;/&#8221; , $value);<br />
				$attachment_name = explode(&#8217;/&#8217; , $attachment_name);<br />
				$file_name = end($attachment_name);</p>
<p>				# 打开文件<br />
				$fp = fopen($value, &#8220;r&#8221;);<br />
				# 把整个文件读入一个变量<br />
				$read = fread($fp, filesize($value)); </p>
<p>				# 好，现在变量$read中保存的是包含整个文件内容的文本块。<br />
				# 现在我们要把这个文本块转换成邮件程序可以读懂的格式<br />
				# 我们用base64方法把它编码<br />
				$read = base64_encode($read); </p>
<p>				# 现在我们有一个用base64方法编码的长字符串。<br />
				# 下一件事是要把这个长字符串切成由每行76个字符组成的小块<br />
				$read = chunk_split($read);</p>
<p>				$to_send .= &#8220;&#8211;$boundary\r\n&#8221;;<br />
				$to_send .= &#8220;Content-type: $mime_type; name=$file_name\r\n&#8221;;<br />
				$to_send .= &#8220;Content-disposition: attachment; filename=$file_name\r\n&#8221;;<br />
				$to_send .= &#8220;Content-transfer-encoding: base64\r\n\r\n&#8221;; </p>
<p>				$to_send .= &#8220;$read\r\n&#8221;;<br />
			}</p>
<p>    		$to_send .= &#8220;\r\n&#8211;$boundary&#8211;\r\n.\r\n&#8221;;<br />
    	}</p>
<p>		if($this->doCommand($to_send,&#8217;250&#8242;) === false)<br />
		{<br />
			fclose($this->fp);<br />
			return false;<br />
		}</p>
<p>		//发信完毕，关闭链接<br />
		if($this->doCommand(&#8221;quit\r\n&#8221;,&#8217;221&#8242;) === false)    //关闭连接<br />
		{<br />
			fclose($this->fp);<br />
			return false;<br />
		}</p>
<p>		return true;<br />
	}<br />
}</p>
<p>$mail_model = new SendMail(&#8221;stmp.163.com&#8221;,25,&#8221;example@exampl.com&#8221;,&#8221;password&#8221;,&#8221;host name&#8221;);  //声明类<br />
$mail_model->send(&#8221;example@exampl.com&#8221;,&#8217;auth&#8217;,'to&#8217;,&#8217;subject&#8217;,'body&#8217;,'html&#8217;,'gb2312&#8242;);    //执行发送</p>
<p></code></p>
]]></content:encoded>
			<wfw:commentRss>http://blog.jerryjiang.net/?feed=rss2&amp;p=10</wfw:commentRss>
		</item>
		<item>
		<title>我的部落格重新开张</title>
		<link>http://blog.jerryjiang.net/?p=1</link>
		<comments>http://blog.jerryjiang.net/?p=1#comments</comments>
		<pubDate>Tue, 24 Mar 2009 03:29:06 +0000</pubDate>
		<dc:creator>杰瑞</dc:creator>
		
		<category><![CDATA[Leisure]]></category>

		<guid isPermaLink="false">http://blog.jerryjiang.net/?p=1</guid>
		<description><![CDATA[这几天突然发现我的 blog 访问不了了，一看才知道原来是域名过期了， 晚上回家折磨了一晚上小杨同学，把我的blog 弄好，结果发现以前的内容全部都找不回来了，真是伤心啊，看来以后要经常来照顾下这个部落格了。
最后还是要谢谢下小杨，和马上要给我上海车展票的小呆
]]></description>
			<content:encoded><![CDATA[<p>这几天突然发现我的 blog 访问不了了，一看才知道原来是域名过期了， 晚上回家折磨了一晚上小杨同学，把我的blog 弄好，结果发现以前的内容全部都找不回来了，真是伤心啊，看来以后要经常来照顾下这个部落格了。</p>
<p>最后还是要谢谢下小杨，和马上要给我上海车展票的小呆</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.jerryjiang.net/?feed=rss2&amp;p=1</wfw:commentRss>
		</item>
	</channel>
</rss>
