<?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/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Terrysco&#039;s Blog &#187; drupal</title>
	<atom:link href="http://www.terrysco.com/node/tag/drupal/feed" rel="self" type="application/rss+xml" />
	<link>http://www.terrysco.com</link>
	<description>仅关注于互联网行业， Linux平台开发。</description>
	<lastBuildDate>Sat, 05 Nov 2011 21:24:06 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.2.1</generator>
		<item>
		<title>用Drupal快速实现mobile平台服务端</title>
		<link>http://www.terrysco.com/node/drupal-as-mobile-backend.html</link>
		<comments>http://www.terrysco.com/node/drupal-as-mobile-backend.html#comments</comments>
		<pubDate>Sat, 05 Nov 2011 21:23:40 +0000</pubDate>
		<dc:creator>terrysco</dc:creator>
				<category><![CDATA[CMS/FrameWork]]></category>
		<category><![CDATA[drupal]]></category>

		<guid isPermaLink="false">http://www.terrysco.com/?p=445</guid>
		<description><![CDATA[用Drupal很容易实现一个API，让手机平台或者其他系统使用json的格式进行通信。 &#60;?php define('API_ERRORNO_INVALID_ACTION', 1); define('API_ERRORNO_INVALID_PARAM', 2); define('API_ERRORNO_NO_RECORD', 3); define('API_ERRORNO_INVALID_NAME', 4); define('API_ERRORNO_USER_BLOCKED', 5); define('API_ERRORNO_LOGIN_FAILED', 6); // 根据错误代码返回错误信息 function api_message_wrapper($errno) { $message = array( API_ERRORNO_INVALID_ACTION =&#62; '非法的请求动作', API_ERRORNO_INVALID_PARAM =&#62; '非法的请求参数', API_ERRORNO_NO_RECORD =&#62; '查找不到数据', API_ERRORNO_INVALID_NAME =&#62; '非法的手机号码', API_ERRORNO_USER_BLOCKED =&#62; '当前用户已经被禁用', API_ERRORNO_LOGIN_FAILED =&#62; '登陆失败', ); return isset($message[$errno]) ? $message[$errno] : '未知的错误'; } // 验证合法的action function api_action_route($action) { $valid_actions = [...]]]></description>
			<content:encoded><![CDATA[<pre>用Drupal很容易实现一个API，让手机平台或者其他系统使用json的格式进行通信。</pre>
<pre>&lt;?php</pre>
<pre>define('API_ERRORNO_INVALID_ACTION', 1);
define('API_ERRORNO_INVALID_PARAM', 2);
define('API_ERRORNO_NO_RECORD', 3);
define('API_ERRORNO_INVALID_NAME', 4);
define('API_ERRORNO_USER_BLOCKED', 5);
define('API_ERRORNO_LOGIN_FAILED', 6);

// 根据错误代码返回错误信息
function api_message_wrapper($errno) {
  $message = array(
    API_ERRORNO_INVALID_ACTION =&gt; '非法的请求动作',
    API_ERRORNO_INVALID_PARAM =&gt; '非法的请求参数',
    API_ERRORNO_NO_RECORD =&gt; '查找不到数据',
    API_ERRORNO_INVALID_NAME =&gt; '非法的手机号码',
    API_ERRORNO_USER_BLOCKED =&gt; '当前用户已经被禁用',
    API_ERRORNO_LOGIN_FAILED =&gt; '登陆失败',
  );
  return isset($message[$errno]) ? $message[$errno] : '未知的错误';
}

// 验证合法的action
function api_action_route($action) {
  $valid_actions = array(
      'login' =&gt; 'login',
      'register' =&gt; 'register',
      'getCardInfos' =&gt; 'get_card_info',
  );
  return isset($valid_actions[$action]) ? $valid_actions[$action] : false;
}

function api_menu() {
  $items['api'] = array(
    'page callback' =&gt; 'api_dispatch',
    'access callback' =&gt; TRUE,
    //'access arguments' =&gt; array('使用API'),
    'type' =&gt; MENU_CALLBACK,
    'file' =&gt; 'api.functions.inc',
  );
  return $items;
}

// 成功返回
function api_response_sucess($data) {
  drupal_json(array('ERRNO' =&gt; 0, 'DATA' =&gt; $data));
}

// 失败返回
function api_response_error($errno) {
  if ($errno &amp;&amp; is_numeric($errno)) {
    drupal_json(array(
      'ERRNO' =&gt; $errno,
      'MSG' =&gt; api_message_wrapper($errno),
    ));
  }
}

// API调度入口
function api_dispatch() {
  if (!isset($_POST['ACTION']) || !api_action_route($_POST['ACTION'])) {
    api_response_error(API_ERRORNO_INVALID_ACTION);
  }
  elseif (!isset($_POST['PARAM'])) {
    api_response_error(API_ERRORNO_INVALID_PARAM);
  }
  else {
    $param = json_decode($_POST['PARAM']);
    call_user_func('api_call_'.  api_action_route($_POST['ACTION']), $param);
  }
}</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.terrysco.com/node/drupal-as-mobile-backend.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Drupal编程总结</title>
		<link>http://www.terrysco.com/node/drupal-programming-summary.html</link>
		<comments>http://www.terrysco.com/node/drupal-programming-summary.html#comments</comments>
		<pubDate>Wed, 22 Dec 2010 10:35:17 +0000</pubDate>
		<dc:creator>terrysco</dc:creator>
				<category><![CDATA[CMS/FrameWork]]></category>
		<category><![CDATA[drupal]]></category>

		<guid isPermaLink="false">http://www.terrysco.com/?p=412</guid>
		<description><![CDATA[从2007年末接触Drupal开始，已经3年时间了。Drupal已经从5.x版本升级到了7.x，模块开发也发生了很多变化。 1. 在查询数据库的时候，考虑到一些特殊情况尽量使用静态变量，这相当于drupal的第一层缓存，在drupal的很多load相关函数中到处可见。比如： function terrysco_get_group($lgid, $reset = false) { // 使用静态变量做缓存 static $linkedin = array(); if ($reset) { $terrysco = array(); } if (!isset($terrysco[$lgid]) &#38;&#38; is_numeric($lgid)) { $terrysco[$lgid] = db_fetch_array(db_query(&#8216;SELECT * FROM {linkedin_groups} WHERE lgid = %d&#8217;, $lgid)); } return isset($terrysco[$lgid]) ? $terrysco[$lgid] : false; } 这样，在要处理一个大的结果集的时候，在循环中调用该函数，就不会每次循环都查询数据库了。 2. 深入理解drupal的hook机制，用好module_invoke_all来设计你的模块机制，大大增强扩展性。其他方面，多想想怎么和别的模块工作的更好，不要进行hack的工作，要给别的模块留下更改你输出主题，更改你返回数据的机会。 3. 多使用表单给用户进行参数配置，将这些配置用variable_set存起来随时使用，生成配置表单的时候可以使用system_settings_form()将表单数组包装起来，省去了写提交函数的部分。另外，将一些数据分析后可以适当存储在session或者cookie中，不一定非要写数据库。 4. 不要过于依赖node机制。node给我们提供了很多方便，很多第三方模块装上后就能很好的工作，比如fivestar，views之类的东西，但也要考虑实际项目需要。很多小的数据如果量很大，不建议放在node表中。而且node表以nid作为auto_increment的primary key，给我们日后做扩展留下了很多麻烦。 5. [...]]]></description>
			<content:encoded><![CDATA[<p>从2007年末接触Drupal开始，已经3年时间了。Drupal已经从5.x版本升级到了7.x，模块开发也发生了很多变化。</p>
<p>1. 在查询数据库的时候，考虑到一些特殊情况尽量使用静态变量，这相当于drupal的第一层缓存，在drupal的很多load相关函数中到处可见。比如：</p>
<p>function terrysco_get_group($lgid, $reset = false) {</p>
<p> // 使用静态变量做缓存</p>
<p> static $linkedin = array();</p>
<p> if ($reset) {</p>
<p> $terrysco = array();</p>
<p> }</p>
<p> if (!isset($terrysco[$lgid]) &amp;&amp; is_numeric($lgid)) {</p>
<p> $terrysco[$lgid] = db_fetch_array(db_query(&#8216;SELECT * FROM {linkedin_groups} WHERE lgid = %d&#8217;, $lgid));</p>
<p> }</p>
<p> return isset($terrysco[$lgid]) ? $terrysco[$lgid] : false;</p>
<p>}</p>
<p>这样，在要处理一个大的结果集的时候，在循环中调用该函数，就不会每次循环都查询数据库了。</p>
<p>2. 深入理解drupal的hook机制，用好module_invoke_all来设计你的模块机制，大大增强扩展性。其他方面，多想想怎么和别的模块工作的更好，不要进行hack的工作，要给别的模块留下更改你输出主题，更改你返回数据的机会。</p>
<p>3. 多使用表单给用户进行参数配置，将这些配置用variable_set存起来随时使用，生成配置表单的时候可以使用system_settings_form()将表单数组包装起来，省去了写提交函数的部分。另外，将一些数据分析后可以适当存储在session或者cookie中，不一定非要写数据库。</p>
<p>4. 不要过于依赖node机制。node给我们提供了很多方便，很多第三方模块装上后就能很好的工作，比如fivestar，views之类的东西，但也要考虑实际项目需要。很多小的数据如果量很大，不建议放在node表中。而且node表以nid作为auto_increment的primary key，给我们日后做扩展留下了很多麻烦。</p>
<p>5. 不要过于依赖第三方模块。第三方模块的一个共同点就是功能强大，代码质量还算不错，但是正因为如此，模块开发者考虑到的是通用性，一个大的模块可能很多功能我们使用不了，这时候就不要偷懒了，自己动手吧。而且很多第三方模块是有问题的，自己细心研究下就能发现很多。</p>
<p>6. Drupal本身支持快速切换数据库，这就使得我们可以在开始的时候就做好设计，看看是否将某些业务分离。当然，如果是中小型的项目就不用了。</p>
</p>
<p>未完待续。。。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.terrysco.com/node/drupal-programming-summary.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>关于drupal架构的思考</title>
		<link>http://www.terrysco.com/node/drupal-extension.html</link>
		<comments>http://www.terrysco.com/node/drupal-extension.html#comments</comments>
		<pubDate>Fri, 11 Dec 2009 09:58:11 +0000</pubDate>
		<dc:creator>terrysco</dc:creator>
				<category><![CDATA[CMS/FrameWork]]></category>
		<category><![CDATA[drupal]]></category>

		<guid isPermaLink="false">http://www.terrysco.com/?p=289</guid>
		<description><![CDATA[自从drupalchina推荐我的blog后，很多drupal的开发者和我讨论drupal的性能，drupal的缺陷，大有感触。 很多人都说drupal站点不适合扩展，或者简单的做到数据库和代码分离后就茫然了。最近参考了很多软件和web架构的书，反过来对drupal这个系统做了一些关于架构扩展的思考。 以自己现有水平来看，drupal的瓶颈似乎在PHP代码执行层面。网上说使用APC加速可以大大加强drupal的性能，自己还没有手动进行测试，所以没有发言权。但目前在自己使用过程中，还是看到drupal一些小尴尬。 drupal的缓存系统比较简单，只是对匿名用户（因为匿名用户访问页面变更比较少）进行页面缓存，并且是以数据库的形式进行缓存。直接序列化页面到cache_page表中，这种性能可想而知，如果对百万级的访问量来说，db将会是个噩梦。 drupal用apache的rewrite模块实现了简洁链接，伪静态页面是提高了SEO效果，但是针对生成静态页面来说，还是比较困难的。另外，drupal的代码可以更加精简，否则为了强大的扩展性损失大幅性能也是不可取的。 有人说代码和架构扩展没有关系（忘记哪位大牛说的），其实我不是很赞同这句话，代码和数据库刚开始的设计决定或者制约了很多后面需要扩展的条件，比如水平划分数据库表的时候，最好是选取primary key，但是这个字段最好不是auto_increment的（道理很容易想到），再比如数据库抽象层的代码如果做memcache缓存，好修改吗？ 很多时候，代码级别制约扩展性的很大一个因素就是联合查询，因为在做垂直划分数据库的时候，分离的都是一些关联性不大，并且不会牵扯到联合查询的库表，但drupal很多地方都使用的inner join（几乎每个模块都能看到）。当用户并发数越来越大，服务器最大的压力来源于数据库，这时，有些朋友就说了”负载均衡“啊。 没有那么简单，首先我们考虑基于重定向的负载均衡，多做几个后端服务器，使用RR或者简单随即抽取机制来轮流使用服务器，drupal和其他站点不同的一点小小特性就是唯一入口index.php，也就是说在这个文件我们可以写入一些代码，实现重定向的负载均衡，这点是比较合适的。或者您为了避免不是真正的压力平衡，可以使用DNS负载均衡，这样下来，稍微大点的数据量我们勉强顶得住了。 从水平划分的方面来看，首先最大的表应该是drupal的node表，nid字段是primary key,并且auto_increment的，这就很难进行分表存储。另外就算使用了一些别的算法，分N个表出来存储node，问题是如果使那些众多的node相关操作代码使用不会出问题？ 另外一种做法就是使用ngnix的反向代理功能，很多java的开发者叫分布式。就算后端服务器性能差异，我们还是可以通过配置文件来决定权重，能者多得的方式，让每个机器发挥到极限。还可以考虑的方式就是使用apache加载我们定义的lua脚本，匹配select和insert，update，delete等字符串，用来区分属于何种sql查询，进一步实现读写分离。不过在机器之间的数据同步也是个麻烦的事情，我没有在运维方面有多少经验，最多就是使用过rsync。 分析种种情景，使用drupal的用户不要担心，只要我们想的到，可扩展的办法还是很多的。性能决定在你自己手里。]]></description>
			<content:encoded><![CDATA[<p>自从drupalchina推荐我的blog后，很多drupal的开发者和我讨论drupal的性能，drupal的缺陷，大有感触。</p>
<p>很多人都说drupal站点不适合扩展，或者简单的做到数据库和代码分离后就茫然了。最近参考了很多软件和web架构的书，反过来对drupal这个系统做了一些关于架构扩展的思考。</p>
<p>以自己现有水平来看，drupal的瓶颈似乎在PHP代码执行层面。网上说使用APC加速可以大大加强drupal的性能，自己还没有手动进行测试，所以没有发言权。但目前在自己使用过程中，还是看到drupal一些小尴尬。</p>
<p>drupal的缓存系统比较简单，只是对匿名用户（因为匿名用户访问页面变更比较少）进行页面缓存，并且是以数据库的形式进行缓存。直接序列化页面到cache_page表中，这种性能可想而知，如果对百万级的访问量来说，db将会是个噩梦。</p>
<p>drupal用apache的rewrite模块实现了简洁链接，伪静态页面是提高了SEO效果，但是针对生成静态页面来说，还是比较困难的。另外，drupal的代码可以更加精简，否则为了强大的扩展性损失大幅性能也是不可取的。</p>
<p>有人说代码和架构扩展没有关系（忘记哪位大牛说的），其实我不是很赞同这句话，代码和数据库刚开始的设计决定或者制约了很多后面需要扩展的条件，比如水平划分数据库表的时候，最好是选取primary key，但是这个字段最好不是auto_increment的（道理很容易想到），再比如数据库抽象层的代码如果做memcache缓存，好修改吗？</p>
<p>很多时候，代码级别制约扩展性的很大一个因素就是联合查询，因为在做垂直划分数据库的时候，分离的都是一些关联性不大，并且不会牵扯到联合查询的库表，但drupal很多地方都使用的inner join（几乎每个模块都能看到）。当用户并发数越来越大，服务器最大的压力来源于数据库，这时，有些朋友就说了”负载均衡“啊。</p>
<p>没有那么简单，首先我们考虑基于重定向的负载均衡，多做几个后端服务器，使用RR或者简单随即抽取机制来轮流使用服务器，drupal和其他站点不同的一点小小特性就是唯一入口index.php，也就是说在这个文件我们可以写入一些代码，实现重定向的负载均衡，这点是比较合适的。或者您为了避免不是真正的压力平衡，可以使用DNS负载均衡，这样下来，稍微大点的数据量我们勉强顶得住了。</p>
<p>从水平划分的方面来看，首先最大的表应该是drupal的node表，nid字段是primary key,并且auto_increment的，这就很难进行分表存储。另外就算使用了一些别的算法，分N个表出来存储node，问题是如果使那些众多的node相关操作代码使用不会出问题？</p>
<p>另外一种做法就是使用ngnix的反向代理功能，很多java的开发者叫分布式。就算后端服务器性能差异，我们还是可以通过配置文件来决定权重，能者多得的方式，让每个机器发挥到极限。还可以考虑的方式就是使用apache加载我们定义的lua脚本，匹配select和insert，update，delete等字符串，用来区分属于何种sql查询，进一步实现读写分离。不过在机器之间的数据同步也是个麻烦的事情，我没有在运维方面有多少经验，最多就是使用过rsync。</p>
<p>分析种种情景，使用drupal的用户不要担心，只要我们想的到，可扩展的办法还是很多的。性能决定在你自己手里。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.terrysco.com/node/drupal-extension.html/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>给drupal批量导入外部数据</title>
		<link>http://www.terrysco.com/node/drupal-colletion-info.html</link>
		<comments>http://www.terrysco.com/node/drupal-colletion-info.html#comments</comments>
		<pubDate>Fri, 16 Oct 2009 12:37:11 +0000</pubDate>
		<dc:creator>terrysco</dc:creator>
				<category><![CDATA[CMS/FrameWork]]></category>
		<category><![CDATA[drupal]]></category>

		<guid isPermaLink="false">http://www.terrysco.com/?p=276</guid>
		<description><![CDATA[很多时候，我们想给自己的drupal站点批量导入节点数据，其中包括很多方法，例如csv格式文件的导入，通过别的站点抓取到得信息，整合别的系统时，相互数据的迁移都要用到这些内容。我们建立一个简单的模型，只考虑节点基本数据，不考虑其他因素（比如节点图片，节点术语分类等等），将模型简单化有助于我们解决问题。 以拿抓取页面为例，我们使用正则表达式过滤出需要的信息后，将其存放在一个一维关联数组中，其中键对应数据库中的节点字段，比如title，body，name。。。然后我们手动调用node_submit方法，将一维数组传进去，这个方法会对我们的数组做一些存表之前的工作，我们直接可以在api.drupal.org查看这个方法的代码。 &#60;?php function node_submit($node) { global $user; // Convert the node to an object, if necessary. $node = (object)$node; // Auto-generate the teaser, but only if it hasn't been set (e.g. by a // module-provided 'teaser' form item). if (!isset($node-&#62;teaser)) { $node-&#62;teaser = isset($node-&#62;body) ? node_teaser($node-&#62;body, isset($node-&#62;format) ? $node-&#62;format : NULL) : ''; } [...]]]></description>
			<content:encoded><![CDATA[<p>很多时候，我们想给自己的drupal站点批量导入节点数据，其中包括很多方法，例如csv格式文件的导入，通过别的站点抓取到得信息，整合别的系统时，相互数据的迁移都要用到这些内容。我们建立一个简单的模型，只考虑节点基本数据，不考虑其他因素（比如节点图片，节点术语分类等等），将模型简单化有助于我们解决问题。</p>
<p>以拿抓取页面为例，我们使用正则表达式过滤出需要的信息后，将其存放在一个一维关联数组中，其中键对应数据库中的节点字段，比如title，body，name。。。然后我们手动调用node_submit方法，将一维数组传进去，这个方法会对我们的数组做一些存表之前的工作，我们直接可以在api.drupal.org查看这个方法的代码。</p>
<pre><code><span>&lt;?php
</span><span>function</span> <span><a title="Prepare node for save and allow modules to make changes." href="http://api.drupal.org/api/function/node_submit/5">node_submit</a></span>(<span>$node</span>) {
  <span>global</span> <span>$user</span>;

  <span>// Convert the node to an object, if necessary.
</span>  <span>$node</span> = <span>(object)</span><span>$node</span>;

  <span>// Auto-generate the teaser, but only if it hasn't been set (e.g. by a
</span>  <span>// module-provided 'teaser' form item).
</span>  <span>if</span> (!<span>isset</span>(<span>$node</span>-&gt;<span>teaser</span>)) {
    <span>$node</span>-&gt;<span>teaser</span> = <span>isset</span>(<span>$node</span>-&gt;<span>body</span>) ? <span><a title="Automatically generate a teaser for a node body." href="http://api.drupal.org/api/function/node_teaser/5">node_teaser</a></span>(<span>$node</span>-&gt;<span>body</span>, <span>isset</span>(<span>$node</span>-&gt;<span>format</span>) ? <span>$node</span>-&gt;<span>format</span> : <span>NULL</span>) : <span>''</span>;
  }

  <span>if</span> (<span><a title="Determine whether the user has a given privilege." href="http://api.drupal.org/api/function/user_access/5">user_access</a></span>(<span>'administer nodes'</span>)) {
    <span>// Populate the "authored by" field.
</span>    <span>if</span> (<span>$account</span> = <span><a title="Fetch a user object." href="http://api.drupal.org/api/function/user_load/5">user_load</a></span>(<span>array</span>(<span>'name'</span> =&gt; <span>$node</span>-&gt;<span>name</span>))) {
      <span>$node</span>-&gt;<span>uid</span> = <span>$account</span>-&gt;<span>uid</span>;
    }
    <span>else</span> {
      <span>$node</span>-&gt;<span>uid</span> = <span>0</span>;
    }
  }

  <span>$node</span>-&gt;<span>created</span> = !<span>empty</span>(<span>$node</span>-&gt;<span>date</span>) ? <span>strtotime</span>(<span>$node</span>-&gt;<span>date</span>) : <span>time</span>();

  <span>// Do node-type-specific validation checks.
</span>  <span><a title="Invoke a node hook." href="http://api.drupal.org/api/function/node_invoke/5">node_invoke</a></span>(<span>$node</span>, <span>'submit'</span>);
  <span><a title="Invoke a hook_nodeapi() operation in all modules." href="http://api.drupal.org/api/function/node_invoke_nodeapi/5">node_invoke_nodeapi</a></span>(<span>$node</span>, <span>'submit'</span>);

  <span>$node</span>-&gt;<span>validated</span> = <span>TRUE</span>;

  <span>return</span> <span>$node</span>;
}
<span>?&gt;</span></code></pre>
<p>除了一些数据的可用性检查外，该方法调用了所有实现submit和nodeapi中实现submit的函数，之后就简单了，再调用node_save即可。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.terrysco.com/node/drupal-colletion-info.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>剖析ubercart</title>
		<link>http://www.terrysco.com/node/drupal-ubercart.html</link>
		<comments>http://www.terrysco.com/node/drupal-ubercart.html#comments</comments>
		<pubDate>Tue, 04 Aug 2009 10:25:05 +0000</pubDate>
		<dc:creator>terrysco</dc:creator>
				<category><![CDATA[CMS/FrameWork]]></category>
		<category><![CDATA[drupal]]></category>

		<guid isPermaLink="false">http://www.terrysco.com/?p=240</guid>
		<description><![CDATA[ubercart是一套很好的搭建电子商务平台代码，被很好的以模块形式应用到了drupal上，接合drupal的强大功能很容易生成电子商务站点。 下载了最新版本的模块看了看，代码量确实巨大。要先学会怎么用，再去看代码我想工作效率会更高，所以启用模块建了几个产品试试，购物车和付费平台什么的功能都走通了之后开始挑几个重要的代码段看了看。因为我要实现一个充值后增加userpoints积分的功能，找了半天终于找到一个相关模块，但是问题很多，issue页面也有人提出了类似的问题。没办法只能靠自己了，了解product模块和feature相关特性后，很轻松的就找到了问题所在。大家需要类似功能的可以参考下。 http://drupal.org/node/514534 解决办法在评论里面，不过我没有测试，只是看到代码流程没有问题了。首先建立一个feature，将产品节点绑定到这个feature里面，再实现order这个hook，在里面判断当订单完成状态后，使用userpoints模块的接口给购买用户增加分数。至于points的分类和数目完全可以自己在后台设置，还是不错的功能，只是纳闷作者为什么这么粗心，错了一个变量，错了一个表名。]]></description>
			<content:encoded><![CDATA[<p>ubercart是一套很好的搭建电子商务平台代码，被很好的以模块形式应用到了drupal上，接合drupal的强大功能很容易生成电子商务站点。</p>
<p>下载了最新版本的模块看了看，代码量确实巨大。要先学会怎么用，再去看代码我想工作效率会更高，所以启用模块建了几个产品试试，购物车和付费平台什么的功能都走通了之后开始挑几个重要的代码段看了看。因为我要实现一个充值后增加userpoints积分的功能，找了半天终于找到一个相关模块，但是问题很多，issue页面也有人提出了类似的问题。没办法只能靠自己了，了解product模块和feature相关特性后，很轻松的就找到了问题所在。大家需要类似功能的可以参考下。</p>
<p>http://drupal.org/node/514534</p>
<p>解决办法在评论里面，不过我没有测试，只是看到代码流程没有问题了。首先建立一个feature，将产品节点绑定到这个feature里面，再实现order这个hook，在里面判断当订单完成状态后，使用userpoints模块的接口给购买用户增加分数。至于points的分类和数目完全可以自己在后台设置，还是不错的功能，只是纳闷作者为什么这么粗心，错了一个变量，错了一个表名。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.terrysco.com/node/drupal-ubercart.html/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>编写安全的drupal代码</title>
		<link>http://www.terrysco.com/node/drupal-security-code.html</link>
		<comments>http://www.terrysco.com/node/drupal-security-code.html#comments</comments>
		<pubDate>Mon, 03 Aug 2009 14:43:03 +0000</pubDate>
		<dc:creator>terrysco</dc:creator>
				<category><![CDATA[Web Security]]></category>
		<category><![CDATA[drupal]]></category>

		<guid isPermaLink="false">http://www.terrysco.com/?p=234</guid>
		<description><![CDATA[本文主要是参考那本经典的drupal 6 模块开发指南，接合自己对脚本安全的一点理解整理出来的，方便E文不是很好的朋友共享。 首先是文本格式转换方面的有效函数，以前的文章我也提过。check_plain()函数能安全的将用户任意输入转换为纯文本，filter_xss()函数放宽了用户的输入，可以输入一些不被过滤的html字符，既有好的样式也保证了安全性，而drupal_urlencode()则对url安全编码，比较常用，是对php的urlencode函数更好的一次封装。mime_header_encode()函数对邮件更好的编码，使用drupal_mail()函数时不需调用，已经内部应用了。 &#8212;&#8212;&#8212;&#8212;-为database抽象层使用安全方面预留位置&#8212;&#8212;&#8212;&#8212;&#8212; drupal的文件安全已经在htaccess文件中做了很多限制，这个是web容器级别的安全，脚本方面我们仍然要考虑，使用文件路径前，最好调用file_check_location函数。为了避免邮件头部注入，drupal为我们引进了mime_header_encode()函数，文中提到一个很简单的例子作为参考。 如果邮件的标题（subject）是用户输入的，如果输入 Have a nice day%0ABcc:spamtarget@example.com%0A%0AL0w%20c0st%20mortgage! 邮件头部注入后则会变成： Subject: Have a nice day Bcc: spamtarget@example.com L0w c0st mortgage! &#8230; 一目了然。 另外，我们经常会编写独立的php文件来为drupal做一些维护或者数据迁移的工作， include_once &#8216;includes/bootstrap.inc&#8217;; drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL); 这两行代码执行后，我们就可以在独立的php文件中使用drupal的db抽象层和相关函数，但最好我们加上uid为1的判断，只有超级用户才可以执行该php文件，如果该文件是操作发布站点数据，则这个限制更显得至关重要。 ajax的安全大家可以参考其他资料，请求脚本程序要格外小心。 最后，drupal的表单api中，title和description等元素必须check_plain()转换为纯文本，而default_value则无需，因为theme_textfield() 在includes/form.inc文件中已经调用一次了，二次转义将会使数据更加混乱。 以上只是接合drupal的安全方面简单陈述，要想更加安全你的代码，请参考更多的PHP安全方面的资料。]]></description>
			<content:encoded><![CDATA[<p>本文主要是参考那本经典的drupal 6 模块开发指南，接合自己对脚本安全的一点理解整理出来的，方便E文不是很好的朋友共享。</p>
<p>首先是文本格式转换方面的有效函数，以前的文章我也提过。check_plain()函数能安全的将用户任意输入转换为纯文本，filter_xss()函数放宽了用户的输入，可以输入一些不被过滤的html字符，既有好的样式也保证了安全性，而drupal_urlencode()则对url安全编码，比较常用，是对php的urlencode函数更好的一次封装。mime_header_encode()函数对邮件更好的编码，使用drupal_mail()函数时不需调用，已经内部应用了。</p>
<p>&#8212;&#8212;&#8212;&#8212;-为database抽象层使用安全方面预留位置&#8212;&#8212;&#8212;&#8212;&#8212;</p>
<p>drupal的文件安全已经在htaccess文件中做了很多限制，这个是web容器级别的安全，脚本方面我们仍然要考虑，使用文件路径前，最好调用file_check_location函数。为了避免邮件头部注入，drupal为我们引进了mime_header_encode()函数，文中提到一个很简单的例子作为参考。</p>
<p>如果邮件的标题（subject）是用户输入的，如果输入</p>
<p>Have a nice day%0ABcc:spamtarget@example.com%0A%0AL0w%20c0st%20mortgage!</p>
<p>邮件头部注入后则会变成：</p>
<p>Subject: Have a nice day<br />
Bcc: spamtarget@example.com<br />
L0w c0st mortgage!<br />
&#8230;</p>
<p>一目了然。</p>
<p>另外，我们经常会编写独立的php文件来为drupal做一些维护或者数据迁移的工作，</p>
<p>include_once &#8216;includes/bootstrap.inc&#8217;;<br />
drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);</p>
<p>这两行代码执行后，我们就可以在独立的php文件中使用drupal的db抽象层和相关函数，但最好我们加上uid为1的判断，只有超级用户才可以执行该php文件，如果该文件是操作发布站点数据，则这个限制更显得至关重要。</p>
<p>ajax的安全大家可以参考其他资料，请求脚本程序要格外小心。</p>
<p>最后，drupal的表单api中，title和description等元素必须check_plain()转换为纯文本，而default_value则无需，因为theme_textfield() 在includes/form.inc文件中已经调用一次了，二次转义将会使数据更加混乱。</p>
<p>以上只是接合drupal的安全方面简单陈述，要想更加安全你的代码，请参考更多的PHP安全方面的资料。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.terrysco.com/node/drupal-security-code.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>快速从drupal5升级到drupal6</title>
		<link>http://www.terrysco.com/node/from-drupal5-to-drupal6.html</link>
		<comments>http://www.terrysco.com/node/from-drupal5-to-drupal6.html#comments</comments>
		<pubDate>Sat, 01 Aug 2009 07:00:32 +0000</pubDate>
		<dc:creator>terrysco</dc:creator>
				<category><![CDATA[CMS/FrameWork]]></category>
		<category><![CDATA[drupal]]></category>

		<guid isPermaLink="false">http://www.terrysco.com/?p=236</guid>
		<description><![CDATA[从drupal5升级到drupal6的必要性有必要先说明下（听着有点绕口）：一年前drupal6刚出来的时候大家都不建议升级，因为最大的问题就是相关模块支持太少，但到现在drupal6已经相当成熟，包括更严格的菜单路径映射功能（drupal5的时候菜单系统出现很多安全问题），更好的jquery加强站点UE，以前设置权重值很是麻烦，而现在只要进行拖动保存后就可以了，另外很多api更加细化，人性化和简单化。BTW，能升级到windows7的用户赶紧升级吧，因为比XP好用很多，我没有枪手嫌疑，因为我是个忠实的Linux用户。 关于升级服务器端部署问题这篇文章将不涉及，前面文章有介绍过，并且我记得在drupal.org上有篇文章是专门介绍如何从drupal5直接升级到drupal6的，我这里只是介绍模块开发人员要学习和参考的东西。 首先，菜单系统。我想这是在drupal6中最大的变化，首先$items这个序列化数组的键完全不同了，从callback 和 callback arguments变成了page callback，access也变成了access_callback以及access_arguments（包括title属性），这就意味着开发人员可以更好的定制drupal的菜单系统。而且drupal6引入的path arguments更好的规范了drupal的动态菜单，配合load后缀函数方面页面传递参数，这里改动比较复杂，大家参考官方文档。 数据库层改动不大，接下来说说form api几个地方的改动，几个hook的参数发生了改变，form_alter , form_validate, form_submit，第一个变成了三个参数，最后一个参数才是form_id，并且为了性能问题，因为之前form_alter要检测所有的form，加入了modulename_form_formname_alter函数。第二个参数数组发生了变化，现在都用$form_state['value']['element_name']判断，其中element_name是form数组中定义的表单元素。第三个提交后返回地址更加方便，直接修改$form['redirect']的值为内部链接就可以了，不用drupal_goto函数（其实也是对drupal_goto的再次包装），不过记得传递进来的是地址引用，而不是值。 在使用上来看，很多模块都更新了，操作更加方面，所以建议大家升级到drupal6系统。]]></description>
			<content:encoded><![CDATA[<p>从drupal5升级到drupal6的必要性有必要先说明下（听着有点绕口）：一年前drupal6刚出来的时候大家都不建议升级，因为最大的问题就是相关模块支持太少，但到现在drupal6已经相当成熟，包括更严格的菜单路径映射功能（drupal5的时候菜单系统出现很多安全问题），更好的jquery加强站点UE，以前设置权重值很是麻烦，而现在只要进行拖动保存后就可以了，另外很多api更加细化，人性化和简单化。BTW，能升级到windows7的用户赶紧升级吧，因为比XP好用很多，我没有枪手嫌疑，因为我是个忠实的Linux用户。</p>
<p>关于升级服务器端部署问题这篇文章将不涉及，前面文章有介绍过，并且我记得在drupal.org上有篇文章是专门介绍如何从drupal5直接升级到drupal6的，我这里只是介绍模块开发人员要学习和参考的东西。</p>
<p>首先，菜单系统。我想这是在drupal6中最大的变化，首先$items这个序列化数组的键完全不同了，从callback 和 callback arguments变成了page callback，access也变成了access_callback以及access_arguments（包括title属性），这就意味着开发人员可以更好的定制drupal的菜单系统。而且drupal6引入的path arguments更好的规范了drupal的动态菜单，配合load后缀函数方面页面传递参数，这里改动比较复杂，大家参考官方文档。</p>
<p>数据库层改动不大，接下来说说form api几个地方的改动，几个hook的参数发生了改变，form_alter , form_validate, form_submit，第一个变成了三个参数，最后一个参数才是form_id，并且为了性能问题，因为之前form_alter要检测所有的form，加入了modulename_form_formname_alter函数。第二个参数数组发生了变化，现在都用$form_state['value']['element_name']判断，其中element_name是form数组中定义的表单元素。第三个提交后返回地址更加方便，直接修改$form['redirect']的值为内部链接就可以了，不用drupal_goto函数（其实也是对drupal_goto的再次包装），不过记得传递进来的是地址引用，而不是值。</p>
<p>在使用上来看，很多模块都更新了，操作更加方面，所以建议大家升级到drupal6系统。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.terrysco.com/node/from-drupal5-to-drupal6.html/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>drupal的WAP页面</title>
		<link>http://www.terrysco.com/node/drupal-wap.html</link>
		<comments>http://www.terrysco.com/node/drupal-wap.html#comments</comments>
		<pubDate>Thu, 16 Jul 2009 03:58:06 +0000</pubDate>
		<dc:creator>terrysco</dc:creator>
				<category><![CDATA[CMS/FrameWork]]></category>
		<category><![CDATA[drupal]]></category>

		<guid isPermaLink="false">http://www.terrysco.com/?p=223</guid>
		<description><![CDATA[要给站点提供一些wap访问的简单页面，数据在drupal的db中已经有了，网上找了找没有相关模块，只能自己动手了。 在站点根目录下创建文件wap.php &#160; $output = ''; &#160; switch &#40;$_GET&#91;'op'&#93;&#41; &#123; case 'list': $pid = &#40; isset&#40;$_GET&#91;'id'&#93;&#41; ? intval&#40;$_GET&#91;'id'&#93;&#41; : '' &#41;; &#160; $result = // fetch from database. &#160; $output .= ''; foreach &#40;$result as $key =&#38;gt; $val&#41; &#123; $output .= ' &#160; '; $output .= '&#60;a href=&#34;?'. htmlentities&#40;'op=view&#38;amp;id='. $key&#41; . '&#34;&#62;'.$val. '&#60;/a&#62;'; $output .= [...]]]></description>
			<content:encoded><![CDATA[<p>要给站点提供一些wap访问的简单页面，数据在drupal的db中已经有了，网上找了找没有相关模块，只能自己动手了。</p>
<p>在站点根目录下创建文件wap.php</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;">&nbsp;
<span style="color: #000088;">$output</span> <span style="color: #339933;">=</span> <span style="color: #0000ff;">''</span><span style="color: #339933;">;</span>
&nbsp;
<span style="color: #b1b100;">switch</span> <span style="color: #009900;">&#40;</span><span style="color: #000088;">$_GET</span><span style="color: #009900;">&#91;</span><span style="color: #0000ff;">'op'</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
  <span style="color: #b1b100;">case</span> <span style="color: #0000ff;">'list'</span><span style="color: #339933;">:</span>
    <span style="color: #000088;">$pid</span> <span style="color: #339933;">=</span> <span style="color: #009900;">&#40;</span> <span style="color: #990000;">isset</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$_GET</span><span style="color: #009900;">&#91;</span><span style="color: #0000ff;">'id'</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span> ? <span style="color: #990000;">intval</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$_GET</span><span style="color: #009900;">&#91;</span><span style="color: #0000ff;">'id'</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">:</span> <span style="color: #0000ff;">''</span> <span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
    <span style="color: #000088;">$result</span> <span style="color: #339933;">=</span> <span style="color: #666666; font-style: italic;">// fetch from database.</span>
&nbsp;
    <span style="color: #000088;">$output</span> <span style="color: #339933;">.=</span> <span style="color: #0000ff;">''</span><span style="color: #339933;">;</span>
    <span style="color: #b1b100;">foreach</span> <span style="color: #009900;">&#40;</span><span style="color: #000088;">$result</span> <span style="color: #b1b100;">as</span> <span style="color: #000088;">$key</span> <span style="color: #339933;">=&amp;</span>gt<span style="color: #339933;">;</span> <span style="color: #000088;">$val</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
      <span style="color: #000088;">$output</span> <span style="color: #339933;">.=</span> <span style="color: #0000ff;">'
&nbsp;
'</span><span style="color: #339933;">;</span>
      <span style="color: #000088;">$output</span> <span style="color: #339933;">.=</span> <span style="color: #0000ff;">'&lt;a href=&quot;?'</span><span style="color: #339933;">.</span> <span style="color: #990000;">htmlentities</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'op=view&amp;amp;id='</span><span style="color: #339933;">.</span> <span style="color: #000088;">$key</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">.</span> <span style="color: #0000ff;">'&quot;&gt;'</span><span style="color: #339933;">.</span><span style="color: #000088;">$val</span><span style="color: #339933;">.</span> <span style="color: #0000ff;">'&lt;/a&gt;'</span><span style="color: #339933;">;</span>
      <span style="color: #000088;">$output</span> <span style="color: #339933;">.=</span> <span style="color: #0000ff;">'
&nbsp;
'</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
    <span style="color: #000088;">$output</span> <span style="color: #339933;">.=</span> <span style="color: #0000ff;">''</span><span style="color: #339933;">;</span>
    <span style="color: #b1b100;">break</span><span style="color: #339933;">;</span>
&nbsp;
  <span style="color: #b1b100;">default</span><span style="color: #339933;">:</span>
    <span style="color: #b1b100;">break</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span>
<span style="color: #000088;">$output</span> <span style="color: #339933;">.=</span> <span style="color: #0000ff;">''</span><span style="color: #339933;">;</span>
<span style="color: #b1b100;">print</span> <span style="color: #000088;">$output</span><span style="color: #339933;">;</span></pre></div></div>

<p>一个简单的功能就有了，可以分离模板，包含别的功能进来，做更多的处理。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.terrysco.com/node/drupal-wap.html/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>【转载】drupal的性能问题</title>
		<link>http://www.terrysco.com/node/drupal-performance.html</link>
		<comments>http://www.terrysco.com/node/drupal-performance.html#comments</comments>
		<pubDate>Tue, 09 Jun 2009 18:26:54 +0000</pubDate>
		<dc:creator>terrysco</dc:creator>
				<category><![CDATA[CMS/FrameWork]]></category>
		<category><![CDATA[drupal]]></category>
		<category><![CDATA[performance]]></category>

		<guid isPermaLink="false">http://www.anyliv.com/www.terrysco.com/?p=97</guid>
		<description><![CDATA[<p>原文地址：&#160;<a href="http://www.bloggern.com/3577.html">http://www.bloggern.com/3577.html</a>&#160;</p><p><span class="Apple-style-span" style="font-family: Verdana; font-size: 14px; line-height: 21px; ">我因为工作原因，曾经对Drupal做过比较全面的测试。当时的环境是双服务器（DB server+Web Server），硬件配置都是单CPU+4G。数据库里面有几千条记录。用JMeter对各种情况下（开/关各种cache模块，logged user/anonymous user）不同页面的读取和写入操作都进行过测试。&#160;<br />...</p></span>
]]></description>
			<content:encoded><![CDATA[<p>原文地址： <a href="http://www.bloggern.com/3577.html">http://www.bloggern.com/3577.html</a></p>
<p><span class="Apple-style-span" style="font-family: Verdana; font-size: 14px; line-height: 21px; ">我因为工作原因，曾经对Drupal做过比较全面的测试。当时的环境是双服务器（DB server+Web Server），硬件配置都是单CPU+4G。数据库里面有几千条记录。用JMeter对各种情况下（开/关各种cache模块，logged user/anonymous user）不同页面的读取和写入操作都进行过测试。 </span><span class="Apple-style-span" style="font-family: Verdana; font-size: 14px; line-height: 21px; ">测试的结论可能和很多人印象中不一样。都说Drupal对数据库要求高，但事实上，无论是cache还是非cache模式，DB server的压力都是相当小的（CPU在10%以下），而Web Server的CPU在80%以上。跟踪所有的db query的执行时间后，也证明了这一点（全部db query的执行时间只占页面生成时间的一小部分）。所以结论是，Drupal在大量logged user并发情况下的瓶颈，在于执行Drupal代码的CPU时间。<br />
<span id="more-97"></span>之所以出现这种情况，和PHP本身的执行机制和Drupal的实现方式有关。Drupal在生成一个非cached的页面时，不管这个页面多么简单，都要执行一个完整的bootstrap过程，即使只启用了最少的模块，这个过程也要调用几十个PHP文件，执行成千上万行PHP代码。而PHP的机制又决定了没有任何PHP代码或者对象能够驻留内存，每次响应请求都必须执行完整的初始化工作。<br />
而anonymous user之所以快，是因为Drupal在执行cached page的时候，不会执行完整的bootstrap过程，它先检查是否cached page，是的话就读取缓存，然后结束工作。这样当然就快了。<br />
以这个结论为前提，可以解释一些事情：<br />
1 为什么Drupal的性能在各种环境下相差并不多。无论是双服务器，单服务器，甚至内存非常小的虚拟机，logged user的RPS值总是在10~20之间。数据库里面有几百条，或者几十万条记录，影响也不大。因为瓶颈并不在于DB或者内存，而是在于执行代码的过程。<br />
2 为什么使用APC/XCache这样的代码优化程序，能够得到极大的性能提升。在我自己的虚机环境上，RPS从3~4提升到了12。因为它提升的是PHP代码的执行时间。<br />
从这个结论出发，列出一些对优化Drupal的logged user性能有明显作用和没有明显作用的措施： </span></p>
<p>没有明显作用的：<br />
1 加内存。在并发数只有10+的时候，即使每个请求占10M内存，也只有100M+内存而已。<br />
2 DB server和Web server分开，或者增强DB server的配置。一台中等性能的mysql服务器，应付200~300的并发是很轻松的事情，在并发数只有10+的时候，db server实际上闲得没事干。<br />
3 基础软件的优化，例如从Windows转移到Linux，从apache转移到httpd，从MySQL迁移到其他数据库，除了从Windows转移到Linux会有比较明显的提升以外（因为提升了PHP的执行效率），其它的措施可能会快一些，但不会有数量级的提高，因为瓶颈不在那里。</p>
<p>有明显作用的：<br />
1 使用APC/XCache这样的代码优化程序，速度会有几倍的提升。估计大家都已经这样做过了。<br />
2 增加web server的CPU数量。双核的肯定比单核的快，8个CPU肯定比2个CPU快得多。<br />
3 使用多web server+单db server的配置，把代码执行的压力分散到不同的web server上。上文说到，单台db server可以轻松应付200+的并发，这意味着理论上可以支持10台以上的web server。<br />
4 使用Quercus这样的引擎，把PHP代码编译成Java，再在Java VM中运行，理论上会有很大的提高。原因是，第一，Java的运行效率比PHP高，第二，Java代码是可以cache的，不需要每次都重新加载。这里有个测试结果：<br />
<a href="http://www.workhabit.org/resin-backed-php-drives-4x-performance-improvements-drupal" target="_blank">http://www.workhabit.org/resin-backed-php-drives-4x-performance-improvements-drupal</a><br />
Drupal在Quercus下有4倍的性能提高，但是这个数字跟Drupal在打开APC/eAccelerator下的提升差不多，所以没有太大的价值。</p>
<p>另外一种思路是代码本身的优化。<br />
使用cache API没有任何意义，因为logger user根本不用调用它。<br />
Drupal.org上有人提出，即使是logged user，有很多页面也是不用定制化的，这意味着可以cache它们。但是Drupal没有提供这样一种机制。只要是logged user，Drupal就会执行完整的bootstrap过程，即使只打印出一个hello world，因此实际上你没有办法在logged user状态下cache单个页面。<br />
我研究了一下Drupal的bootstrap过程，发现也许这样是可以的：实现hook_boot函数，这是bootstrap中执行最靠前的一个函数，它被调用时，bootstrap的大部分过程还没有执行。在hook_boot中，检查当前页面是否需要cache，如果是，直接读cache生成页面，然后调用exit()强行结束。这应该是可行的，但太过hack了一点。</p>
<p>另外，说说我对Drupal的看法。Drupal是我见过的web应用里面架构做得最好的一个（不仅仅是PHP，也包括Java和.NET应用）。在没有见过Drupal之前，我就想过，如果能把Eclipse的实现思路用web平台的方式实现，通过微内核+plugin实现所有的功能，那是一个多么方便的事情。接触Drupal以后，发现这就是我在寻找的东西。<br />
从性能上来说，Drupal在anonymous user的情况下是非常优秀的，根据我以前的测试，基于file-based cache的插件，RPS在350以上，这意味着单机就可以实现万人在线的站点了。但是，Drupal在logged user的情况下性能不佳，这也是事实。RPS 10+可能也可以应付几百人在线的情况，但是想想同等配置下，原生PHP和Java都能到100+，还是很让人郁闷啊。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.terrysco.com/node/drupal-performance.html/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>关于drupal区块的小问题</title>
		<link>http://www.terrysco.com/node/drupal-block.html</link>
		<comments>http://www.terrysco.com/node/drupal-block.html#comments</comments>
		<pubDate>Tue, 02 Jun 2009 18:32:01 +0000</pubDate>
		<dc:creator>terrysco</dc:creator>
				<category><![CDATA[CMS/FrameWork]]></category>
		<category><![CDATA[drupal]]></category>

		<guid isPermaLink="false">http://www.anyliv.com/www.terrysco.com/?p=96</guid>
		<description><![CDATA[<p>drupal模块开发中经常用到区块的概念，将一些相关数据放到区块中，配合权限验证，将用户能看到的信息以区块的形式展示到用户浏览器中。不过区块是跟节点无关了，如果要根据当前节点页面的特性针对性的在区块中显示内容，我的做法是：</p><p>if (arg(0) == 'node' &#38;&#38; is_numeric(arg(1)) { // 初步设想是仅仅当路径是' node / 3 ',类似这样的地址时显示</p><p>&#160; $node = node_load(arg(1));</p>...
]]></description>
			<content:encoded><![CDATA[<p>drupal模块开发中经常用到区块的概念，将一些相关数据放到区块中，配合权限验证，将用户能看到的信息以区块的形式展示到用户浏览器中。不过区块是跟节点无关了，如果要根据当前节点页面的特性针对性的在区块中显示内容，我的做法是：</p>
<p>if (arg(0) == &#8216;node&#8217; &amp;&amp; is_numeric(arg(1)) { // 初步设想是仅仅当路径是&#8217; node / 3 &#8216;,类似这样的地址时显示</p>
<p>&nbsp; $node = node_load(arg(1));</p>
<p>&nbsp; if (!$node-&gt;teaser) // 初步设想是节点以全节点方式显示的时候才有效（提高一点效率）</p>
<p>}</p>
<p>在调试的过程中发现，arg函数是和drupal内置的path模块无关的。也就是说如果给某个节点设置了path，比如给公司的帮助页面设置了path : www.target.com/help，那么arg(0)我们取到的还是node，而不是help。</p>
<p>另外， $node对象中的teaser属性指的是节点的摘要，而不是&ldquo;是否以全文的方式显示&rdquo;，这个跟nodeapi钩子，以及hook_view中的$teaser形式参数是不一样的，后者确确实实指的是&ldquo;是否以全文的方式显示&rdquo;，也就是说上面的判断是错误的。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.terrysco.com/node/drupal-block.html/feed</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
	</channel>
</rss>

