<?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>Practice3</title>
	<atom:link href="http://fwso.cn/feed/" rel="self" type="application/rss+xml" />
	<link>http://fwso.cn</link>
	<description>行.思.悟</description>
	<lastBuildDate>Fri, 06 Apr 2012 01:02:49 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.2</generator>
		<item>
		<title>Drupal架构与实现分析</title>
		<link>http://fwso.cn/php/drupal-architecture-and-implement-analysis/</link>
		<comments>http://fwso.cn/php/drupal-architecture-and-implement-analysis/#comments</comments>
		<pubDate>Mon, 26 Mar 2012 12:33:38 +0000</pubDate>
		<dc:creator>James Tang</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Drupal]]></category>

		<guid isPermaLink="false">http://fwso.cn/?p=375</guid>
		<description><![CDATA[不尽知用兵之害者，则不能尽知用兵之利——孙武《孙子兵法》 Drupal也算是架构精良的PHP开源CMS，它最大的优点是可以灵活性高，不仅可以用于实现一般的CMS功能，还可以容易的实现其它应用，如Drupal官方所说，可以实现wiki, blog甚至商城等.它最大的问题是灵活性太高，如果没有根据实际情况进行设计与管理，后果会很糟糕。 先看一下Drupal的总体架构，下图是Drupal官方的信息流图，详细参见：http://drupal.org/getting-started/before/overview。 从图上看起来非常清晰明了，不过这并没有体现出Drupal的架构方式。在我看来Drupal的基本架构就是Drupal核心+模块+模板，所谓的Drupal核心除模块之外的最基础的代码，全部在includes目录下面，另外Drupal有几个核心模块，如System, User, Node等。 Drupal的处理流程如下： 系统初始化 路径解析（路由） 页面内容渲染 页面布局及block渲染 生成页面 1. Hook Drupal几乎是纯面向过程的架构，这或许让很多习惯OOP的人有点惊讶，这或许也是Drupal变得难以管理（源码，模块）的原因。Hook是Drupal架构实现的关键,模块通过种hook与Drupal进行交互，这种方式被称为基于过程的AOP[1]。 大多数情况下，Drupal通过module_invoke_all()来调用指定的hooks, 如下面代码将调用所有MODULE_NAME_cron()这样的函数: module_invoke_all('cron'); 下面是module_invoke_all()的源码，参数通过func_get_args函数获得，返回值是可选的。顺便提一下，在Drupal及其模块中经常用到func_get_args函数。 function module_invoke_all() { $args = func_get_args(); $hook = $args[0]; unset($args[0]); $return = array(); foreach (module_implements($hook) as $module) { $function = &#8230; <a href="http://fwso.cn/php/drupal-architecture-and-implement-analysis/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<blockquote class="post-top-quote"><p>
不尽知用兵之害者，则不能尽知用兵之利——孙武《孙子兵法》
</p></blockquote>
<p>Drupal也算是架构精良的PHP开源CMS，它最大的优点是可以灵活性高，不仅可以用于实现一般的CMS功能，还可以容易的实现其它应用，如Drupal官方所说，可以实现wiki, blog甚至商城等.它最大的问题是灵活性太高，如果没有根据实际情况进行设计与管理，后果会很糟糕。</p>
<p>先看一下Drupal的总体架构，下图是Drupal官方的信息流图，详细参见：http://drupal.org/getting-started/before/overview。</p>
<p><a href="http://fwso.cn/wp-content/uploads/2012/03/drupal_flow_0.gif"><img src="http://fwso.cn/wp-content/uploads/2012/03/drupal_flow_0.gif" alt="" title="Drupal information flow" width="528" height="562" class="alignnone size-full wp-image-380" /></a></p>
<p>从图上看起来非常清晰明了，不过这并没有体现出Drupal的架构方式。在我看来Drupal的基本架构就是Drupal核心+模块+模板，所谓的Drupal核心除模块之外的最基础的代码，全部在includes目录下面，另外Drupal有几个核心模块，如System, User, Node等。</p>
<p>Drupal的处理流程如下：</p>
<ul>
<li>系统初始化</li>
<li>路径解析（路由）</li>
<li>页面内容渲染</li>
<li>页面布局及block渲染</li>
<li>生成页面</li>
</ul>
<h2>1. Hook</h2>
<p>Drupal几乎是纯面向过程的架构，这或许让很多习惯OOP的人有点惊讶，这或许也是Drupal变得难以管理（源码，模块）的原因。Hook是Drupal架构实现的关键,模块通过种hook与Drupal进行交互，这种方式被称为基于过程的AOP<sup>[1]</sup>。</p>
<p>大多数情况下，Drupal通过module_invoke_all()来调用指定的hooks, 如下面代码将调用所有MODULE_NAME_cron()这样的函数:</p>
<pre class="brush:php">
module_invoke_all('cron');
</pre>
<p>下面是module_invoke_all()的源码，参数通过func_get_args函数获得，返回值是可选的。顺便提一下，在Drupal及其模块中经常用到func_get_args函数。</p>
<pre class="brush:php">
function module_invoke_all() {
  $args = func_get_args();
  $hook = $args[0];
  unset($args[0]);
  $return = array();
  foreach (module_implements($hook) as $module) {
    $function = $module .'_'. $hook;
    $result = call_user_func_array($function, $args);
    if (isset($result) &#038;&#038; is_array($result)) {
      $return = array_merge_recursive($return, $result);
    }
    else if (isset($result)) {
      $return[] = $result;
    }
  }

  return $return;
}
</pre>
<p>module_invoke_all()并不是唯一的调用hook的方式，另一个比较常用的是drupal_alter，hook_menu_alter()、hook_mail_alter等就是通过该函数实现的。drupal_alter()与module_invoke_all()的主要差别是drupal_alter()是通过传递参数引用给模块，让模块可以个性数据。</p>
<pre class="brush:php">
drupal_alter('menu', $callbacks);
</pre>
<pre class="brush:php">
drupal_alter('form', $data, $form_id);
</pre>
<p>drupal_alter()的源码片段:</p>
<pre class="brush:php">
function drupal_alter($type, &#038;$data) {
  //省略
  foreach (module_implements($type .'_alter') as $module) {
    $function = $module .'_'. $type .'_alter';
    call_user_func_array($function, $args);
  }
}
</pre>
<p>除了drupal_alter()与module_invoke_all()之外，模块也可以自定义调用hook的逻辑，就像hook_nodeapi的实现一样，如下面的代码所示。但它们都需要调用module_implements()来得知有哪些模块实现了该hook。</p>
<pre class="brush:php">
function node_invoke_nodeapi(&#038;$node, $op, $a3 = NULL, $a4 = NULL) {
  $return = array();
  foreach (module_implements('nodeapi') as $name) {
    $function = $name .'_nodeapi';
    $result = $function($node, $op, $a3, $a4);
    if (isset($result) &#038;&#038; is_array($result)) {
      $return = array_merge($return, $result);
    }
    else if (isset($result)) {
      $return[] = $result;
    }
  }
  return $return;
}
</pre>
<h2>2. 模块</h2>
<p>模块主要用于扩展系统功能，一般都会实现一个或多个hook，如hook_menu是最常见的，但这并不是必须的。是简单的模块只需要包含MODULE_NAME.module与MODULE_NAME.info即可，MODULE_NAME.install用于处理模块安装及卸载相关的逻辑。</p>
<p>所有模块信息都保存在system表中，system同时也保存模板信息。system.status为0表示模块未启用，system.weight有时候很重要，比如模块A和B同时实现hook_x(), 但A必须在B之后执行，则可以通过weight来控制。weight值越大越迟加载，因为模块的加载顺序如下：</p>
<pre class="brush:sql">
SELECT name, filename, throttle FROM {system} WHERE type = 'module' AND status = 1 ORDER BY weight ASC, filename ASC
</pre>
<p>throttle用于控制模块是否被加载，不过几乎不用它。</p>
<h2>3. Form</h2>
<p>Drupal定义了一套很详细的API用于定义表单及相关处理逻辑，包括元素默认值、验证、显示结构及顺序等，并提供了默认的显示结构与样式（虽然这很不实用）。</p>
<p>第一个表单对应一个函数，也称为form_id， Drupal通过drupal_get_form($form_id)来获得该表单的定义并渲染HTML。如下面代码所示：</p>
<pre class="brush:php">
function user_login(&#038;$form_state) {
  global $user;

  // If we are already logged on, go to the user page instead.
  if ($user->uid) {
    drupal_goto('user/'. $user->uid);
  }

  // Display login form:
  $form['name'] = array('#type' => 'textfield',
    '#title' => t('Username'),
    '#size' => 60,
    '#maxlength' => USERNAME_MAX_LENGTH,
    '#required' => TRUE,
  );

  $form['name']['#description'] = t('Enter your @s username.', array('@s' => variable_get('site_name', 'Drupal')));
  $form['pass'] = array('#type' => 'password',
    '#title' => t('Password'),
    '#description' => t('Enter the password that accompanies your username.'),
    '#required' => TRUE,
  );
  $form['#validate'] = user_login_default_validators();
  $form['submit'] = array('#type' => 'submit', '#value' => t('Log in'), '#weight' => 2);

  return $form;
}
</pre>
<p>这是用户登录的表单,form_id就是user_login， 这里有一个参数$form_state，但这并不是定义表单所必须的，通过drupal_get_form(&#8216;user_login&#8217;,$form_state)获得该表单并在页面显示。默认情况下表单的action就是当前页面，但可以过来#action来指定其它路径，但#action指向的页面必须调用drupal_get_form($form_id),如drupal_get_form(&#8216;user_login&#8217;,$form_state)，否则表单将无法被处理，除非不使用Drupal的方式而是手动来处理表单内容。</p>
<p>第一个表单在渲染之前，Drupal都会自动加上一个form_build_id。</p>
<pre class="brush:html">
&lt;input type="hidden" name="form_build_id" id="form-cb48b9e32a4a25f8b1e483153f2c7b06" value="form-cb48b9e32a4a25f8b1e483153f2c7b06" /&gt;
</pre>
<p>form_build_id的主要作用是用于表单缓存，很多时候表单缓存中包含一些跟踪上下文信息，所以如果表单的form_build_id被个性或超时就不能被正确处理。默认情况下，Drupal的缓存是保存在数据库中，表单的缓存则保存在cache_form表中。</p>
<h2>4. Menu</h2>
<p>Drupal的Menu非常重要，它用于定义用户可以访问的路径，模块通过实现hook_menu()来新的路径，每个路径对应一个页面。hook_menu()返回一个定义了menu的数组，如下所示：</p>
<pre class="brush:php">
 $items['node/%node'] = array(
    'title' => 'View',
    'page callback' => 'node_page_view',
    'page arguments' => array(1),
    'access callback' => 'node_access',
    'access arguments' => array('view', 1),
    'type' => MENU_CALLBACK,
  );
</pre>
<p>该数组定义了路径的页面回调函数，访问权限等。</p>
<p>这些信息并不是第次请求都会查找实现了hook_menu的函数，而是缓存在menu_router表中。路径的层次关系等信息刚是缓存在cache_menu表中。</p>
<p>详细文档请参考：http://drupal.org/node/102338</p>
<h2>5. 模板</h2>
<p>类似于hook_menu，hook_theme用于定义模板，模板的定义信息叫做theme registry, 一般都是保存在缓存中，所以每次更新都要更新缓存。</p>
<p>theme()是Drupal模板系统的关键函数，用于渲染HTML。Drupal的模板并非都是模板文件，还可以是函数，一般以theme_开头。</p>
<p>详细文档请参考：http://drupal.org/documentation/theme</p>
<h2>6. 内容与用户</h2>
<p>CMS当然离不开内容与内容管理，Drupal中内容的关键概念是节点，但它并不是在Drupal核心中实现，而是在Node核心模块中实现。所有内容都被看作节点，节点有不同类型，不同类型在总体结构上是一致的。主要的三个数据库关系表分别是node, node_revisions, node_type。另外涉及单个节点权限管理的表是node_access。</p>
<p>用户及用户管理部分主要有三个主要定义：用户(user), 用户组(user roles)及相关权限(permission)。</p>
<h2>7. 系统变量</h2>
<p>Drupal的系统变量以键值对的形式保存在variable表中，也可以在settings.php中配置。variable_get()、variable_set()、variable_del()分别用于获取、设置及删除系统变量。</p>
<h2>8. 缓存</h2>
<p>Drupal的默认缓存保存在数据库中，缓存表在cache_开头。如果需要以其它方式保存缓存数据，如保存到memcache中，只需要实现cache_set(), cache_get(), cache_clear_all()函数，并将cache_inc系统变量设置为自定义缓存处理的PHP文件路径即可。</p>
<h2>9. 总结</h2>
<p>Hook系统让Drupal具有良好的扩展性，同时也容易让调试与维护变得很困难。我认为如果不是用Drupal做一个像个人博客一样的简单网站，就应该知道：</p>
<p>1. Drupal虽然能做很多东西，但最好只用来做CMS相关的事，不用的东西彻底砍掉，免得浪费资源。<br />
2. 仔细分析设计模块与模板，特别是hook的使用一定要慎重，配置要统一且简单，不然调试将变得异常困难。<br />
3. 不要依赖社区的模块，包括Drupal安装包自带的，大多模块考虑太多的通用性，而不是性能。<br />
4. 手动打补丁，不要使用自动更新。<br />
5. 必要时修改Drupal内核。</p>
<h2>10. 参考</h2>
<p>1. Programming language trade-off: http://www.garfieldtech.com/blog/language-tradeoffs<br />
2. Architectural priorities: http://www.garfieldtech.com/blog/architectural-priorities<br />
3. The Drupal Overview: http://drupal.org/getting-started/before/overview<br />
4. Form API Reference: http://api.drupal.org/api/drupal/developer!topics!forms_api_reference.html/6</p>
]]></content:encoded>
			<wfw:commentRss>http://fwso.cn/php/drupal-architecture-and-implement-analysis/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Tools for testing REST API</title>
		<link>http://fwso.cn/notes/tools-for-testing-rest-api-1/</link>
		<comments>http://fwso.cn/notes/tools-for-testing-rest-api-1/#comments</comments>
		<pubDate>Fri, 02 Mar 2012 06:07:33 +0000</pubDate>
		<dc:creator>James Tang</dc:creator>
				<category><![CDATA[Notes]]></category>
		<category><![CDATA[API]]></category>
		<category><![CDATA[curl]]></category>
		<category><![CDATA[REST]]></category>

		<guid isPermaLink="false">http://fwso.cn/?p=366</guid>
		<description><![CDATA[1. CURL curl是一个很强大很实用的工具： curl -v -X POST -H "Content-Type: application/json" -H "Accept: application/json" -d '{"id":1, "method":"message.query", "jsonrpc":"2.0", "params": {"user_id":9} }' http://fwso.cn/services/json-rpc 2. REST Client WizTools.org RESTClient是一个用Java写的工具， Swing界面，使用比较方便： 3. WFetch Windows下可以使用微软的WFetch, 功能比较齐全，但感觉不是很好用。]]></description>
			<content:encoded><![CDATA[<h2>1. CURL</h2>
<p>curl是一个很强大很实用的工具：</p>
<pre class="brush:bash">
curl -v -X POST -H "Content-Type: application/json" -H "Accept: application/json" -d '{"id":1, "method":"message.query", "jsonrpc":"2.0", "params": {"user_id":9} }' http://fwso.cn/services/json-rpc
</pre>
<h2>2. REST Client</h2>
<p><a href="" title="WizTools.org RESTClient">WizTools.org RESTClient</a>是一个用Java写的工具， Swing界面，使用比较方便：</p>
<p><a href="http://fwso.cn/wp-content/uploads/2012/03/RESTClient-wizTools.png"><img src="http://fwso.cn/wp-content/uploads/2012/03/RESTClient-wizTools.png" alt="" title="RESTClient-wizTools" width="730" height="669" class="alignnone size-full wp-image-367" /></a></p>
<h2>3. WFetch</h2>
<p>Windows下可以使用微软的<a href="http://www.microsoft.com/download/en/details.aspx?displaylang=en&#038;id=21625">WFetch</a>, 功能比较齐全，但感觉不是很好用。</p>
<p><a href="http://fwso.cn/wp-content/uploads/2012/03/wfetch.jpg"><img src="http://fwso.cn/wp-content/uploads/2012/03/wfetch.jpg" alt="" title="wfetch" width="804" height="864" class="alignnone size-full wp-image-368" /></a></p>
]]></content:encoded>
			<wfw:commentRss>http://fwso.cn/notes/tools-for-testing-rest-api-1/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>PHP in_array()与array_search的实现</title>
		<link>http://fwso.cn/php/implementation-of-in_array-array_search-in-c-php/</link>
		<comments>http://fwso.cn/php/implementation-of-in_array-array_search-in-c-php/#comments</comments>
		<pubDate>Thu, 01 Mar 2012 10:08:53 +0000</pubDate>
		<dc:creator>James Tang</dc:creator>
				<category><![CDATA[Notes]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[C]]></category>

		<guid isPermaLink="false">http://fwso.cn/?p=363</guid>
		<description><![CDATA[今天看PHP源码的时候发现in_array与array_search是通过同一个函数php_search_array来实现的，只是最后一个参数不同. in_array的实现： PHP_FUNCTION(in_array) { php_search_array(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0); } array_search的实现： PHP_FUNCTION(array_search) { php_search_array(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1); } php_search_array的代码： static void php_search_array(INTERNAL_FUNCTION_PARAMETERS, int behavior) /* {{{ */ { zval *value, /* value to check for */ *array, /* array to check in */ **entry, /* &#8230; <a href="http://fwso.cn/php/implementation-of-in_array-array_search-in-c-php/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>今天看PHP源码的时候发现in_array与array_search是通过同一个函数php_search_array来实现的，只是最后一个参数不同. </p>
<p>in_array的实现：</p>
<pre class="brush:c">
PHP_FUNCTION(in_array)
{
    php_search_array(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
}
</pre>
<p>array_search的实现：</p>
<pre class="brush:c">
PHP_FUNCTION(array_search)
{
    php_search_array(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
}
</pre>
<p>php_search_array的代码：</p>
<pre class="brush:c">
static void php_search_array(INTERNAL_FUNCTION_PARAMETERS, int behavior) /* {{{ */
{
    zval *value,                /* value to check for */
         *array,                /* array to check in */
         **entry,               /* pointer to array entry */
          res;                  /* comparison result */
    HashPosition pos;           /* hash iterator */
    zend_bool strict = 0;       /* strict comparison or not */
    ulong num_key;
    uint str_key_len;
    char *string_key;
    int (*is_equal_func)(zval *, zval *, zval * TSRMLS_DC) = is_equal_function;

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "za|b", &#038;value, &#038;array, &#038;strict) == FAILURE) {
        return;
    }

    if (strict) {
        is_equal_func = is_identical_function;
    }

    zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(array), &#038;pos);
    while (zend_hash_get_current_data_ex(Z_ARRVAL_P(array), (void **)&#038;entry, &#038;pos) == SUCCESS) {
        is_equal_func(&#038;res, value, *entry TSRMLS_CC);
        if (Z_LVAL(res)) {
            if (behavior == 0) {
                RETURN_TRUE;
            } else {
                /* Return current key */
                switch (zend_hash_get_current_key_ex(Z_ARRVAL_P(array), &#038;string_key, &#038;str_key_len, &#038;num_key, 0, &#038;pos)) {
                    case HASH_KEY_IS_STRING:
                        RETURN_STRINGL(string_key, str_key_len - 1, 1);
                        break;
                    case HASH_KEY_IS_LONG:
                        RETURN_LONG(num_key);
                        break;
break;
                }
            }
        }
        zend_hash_move_forward_ex(Z_ARRVAL_P(array), &#038;pos);
    }

    RETURN_FALSE;
}
</pre>
<p>behavior参数为1时为search，0为判断元素是否存在.<br />
默认情况下用is_equal_function函数来判断元素值是否相同，如果是严格比较，则使用is_identical_function函数。</p>
<p>in_array与array_search在PHP中调用时的参数都是( mixed $needle , array $haystack [, bool $strict = false ] ), 所以<br />
zend_parse_parameters的type_spec参数的值是&#8221;za|b&#8221;, z表示任意类型, a表示必须是array, |表示之后的参数为可选, b是boolean。</p>
]]></content:encoded>
			<wfw:commentRss>http://fwso.cn/php/implementation-of-in_array-array_search-in-c-php/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>查找数组中的唯一元素</title>
		<link>http://fwso.cn/notes/%e6%9f%a5%e6%89%be%e6%95%b0%e7%bb%84%e4%b8%ad%e7%9a%84%e5%94%af%e4%b8%80%e5%85%83%e7%b4%a0/</link>
		<comments>http://fwso.cn/notes/%e6%9f%a5%e6%89%be%e6%95%b0%e7%bb%84%e4%b8%ad%e7%9a%84%e5%94%af%e4%b8%80%e5%85%83%e7%b4%a0/#comments</comments>
		<pubDate>Thu, 01 Mar 2012 06:34:56 +0000</pubDate>
		<dc:creator>James Tang</dc:creator>
				<category><![CDATA[Notes]]></category>
		<category><![CDATA[algorithm]]></category>
		<category><![CDATA[算法]]></category>

		<guid isPermaLink="false">http://fwso.cn/?p=358</guid>
		<description><![CDATA[问题 例如给定一个数组有101个元素，其实50个元素出现再次，只有一个仅出现一次，写一个函数找出该唯一的元素，元素均为整型。 1. 最差的方法 前提是元素都大于0. int findOdd(int *input, int len) { int i, j, z, *buff, blen = (int) (len / 2 + 1); buff = (int *) malloc(sizeof(int) * blen); for (i = 0; i < len; i++) { &#8230; <a href="http://fwso.cn/notes/%e6%9f%a5%e6%89%be%e6%95%b0%e7%bb%84%e4%b8%ad%e7%9a%84%e5%94%af%e4%b8%80%e5%85%83%e7%b4%a0/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<h2>问题</h2>
<p>例如给定一个数组有101个元素，其实50个元素出现再次，只有一个仅出现一次，写一个函数找出该唯一的元素，元素均为整型。</p>
<h2>1. 最差的方法</h2>
<p>前提是元素都大于0.</p>
<pre class="brush:c">
int findOdd(int *input, int len) {
	int i, j, z, *buff, blen = (int) (len / 2 + 1);

	buff = (int *) malloc(sizeof(int) * blen);

	for (i = 0; i < len; i++) {
		z = -1;
		for(j = 0; j < blen; j++) {
			if (buff[j] == 0) {
				if (z == -1) {
					z = j;
				}
				continue;
			}
			if (buff[j] == input[i]) {
				buff[j] = 0;
				z = -1;
				break;
			}
		}

		if (z >= 0) {
			buff[z] = input[i];
		}

	}

	j = 0;

	for (i = 0; i < blen; i++) {
		if (buff[i] != 0) {
			j = buff[i];
			break;
		}
	}

	free(buff);
	return j;
}
</pre>
<h2>2. 好点的方法</h2>
<p>使用stdlib的快速排序.</p>
<pre class="brush:c">
int compare (const void * a, const void * b) {
	return ( *(int*)a - *(int*)b );
}

int findOdd2(int *input, int len) {
	int i = 0;

	qsort(input, len, sizeof(int), compare);	

	while (i < len) {
		if (input[i] != input[i+1]) {
			break;
		}
		i = i + 2;
	}
	return input[i];
}
</pre>
<h2>3. 最佳的算法</h2>
<p>不知道，请您指点。</p>
]]></content:encoded>
			<wfw:commentRss>http://fwso.cn/notes/%e6%9f%a5%e6%89%be%e6%95%b0%e7%bb%84%e4%b8%ad%e7%9a%84%e5%94%af%e4%b8%80%e5%85%83%e7%b4%a0/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>数码照片EXIF信息读取(PHP)</title>
		<link>http://fwso.cn/php/%e6%95%b0%e7%a0%81%e7%85%a7%e7%89%87exif%e4%bf%a1%e6%81%af%e8%af%bb%e5%8f%96php/</link>
		<comments>http://fwso.cn/php/%e6%95%b0%e7%a0%81%e7%85%a7%e7%89%87exif%e4%bf%a1%e6%81%af%e8%af%bb%e5%8f%96php/#comments</comments>
		<pubDate>Tue, 15 Nov 2011 09:42:48 +0000</pubDate>
		<dc:creator>James Tang</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[EXIF]]></category>
		<category><![CDATA[数码照片]]></category>

		<guid isPermaLink="false">http://fwso.cn/?p=330</guid>
		<description><![CDATA[EXIF(Exchangeable image file format)是数码相机用于记录图片属性及拍摄数据的标准，最初由日本电子工业发展协会制定。这些信息是可以修改的，因此这些数据只有参考价值。在Windows下查看图片属性可以看到并修改这些信息，下图是在Picasa中看到的部分信息： Exif扩展 PHP的Exif扩展用于处理这些信息，在编译PHP时&#8211;enable-exif即可。该简单比较简单，一共只有５个函数(准确地说只有４个，因为read_exif_data只是exif_read_data的别名)，本文主要使用exif_read_data()与exif_thumbnail(). string exif_thumbnail ( string $filename [, int &#038;$width [, int &#038;$height [, int &#038;$imagetype ]]] ) exif_thumbnail读取图片的缩略图，后面三个可选参数分别用于返回缩略图的宽、高及图片类型，图片类型是整型，如2代表JPEG格式(参考exif_imagetype)，GD扩展提供了image_type_to_mime_type()函数可以方便地返回对应的mime类型。 array exif_read_data ( string $filename [, string $sections = NULL [, bool $arrays = false [, bool &#8230; <a href="http://fwso.cn/php/%e6%95%b0%e7%a0%81%e7%85%a7%e7%89%87exif%e4%bf%a1%e6%81%af%e8%af%bb%e5%8f%96php/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>EXIF(Exchangeable image file format)是数码相机用于记录图片属性及拍摄数据的标准，最初由日本电子工业发展协会制定。这些信息是可以修改的，因此这些数据只有参考价值。在Windows下查看图片属性可以看到并修改这些信息，下图是在Picasa中看到的部分信息：<br />
<img src="http://fwso.cn/wp-content/uploads/2011/11/picasa-001.png" alt="" title="Show EXIF information for image in Picasa3" width="308" height="374" class="alignnone size-full wp-image-331" /></p>
<h2>Exif扩展</h2>
<p>PHP的<a href="http://cn2.php.net/manual/en/book.exif.php" title="PHP Exif Extension" target="_blank">Exif扩展</a>用于处理这些信息，在编译PHP时&#8211;enable-exif即可。该简单比较简单，一共只有５个函数(准确地说只有４个，因为read_exif_data只是exif_read_data的别名)，本文主要使用exif_read_data()与exif_thumbnail().</p>
<pre class="brush:php">
string exif_thumbnail ( string $filename [, int &#038;$width [, int &#038;$height [, int &#038;$imagetype ]]] )
</pre>
<p>exif_thumbnail读取图片的缩略图，后面三个可选参数分别用于返回缩略图的宽、高及图片类型，图片类型是整型，如2代表JPEG格式(参考<a href="http://cn2.php.net/manual/en/function.exif-imagetype.php" title="exif_imagetype" target="_blank">exif_imagetype</a>)，GD扩展提供了<a href="http://cn2.php.net/manual/en/function.image-type-to-mime-type.php" title="image_type_to_mime_type" target="_blank">image_type_to_mime_type()</a>函数可以方便地返回对应的mime类型。</p>
<pre class="brush:php">
array exif_read_data ( string $filename [, string $sections = NULL [, bool $arrays = false [, bool $thumbnail = false ]]] )
</pre>
<p><a href="http://cn2.php.net/manual/en/function.exif-read-data.php" title="exif_read_data" target="_blank">exif_read_data</a>用于读取所有EXIF信息，返回的信息分为多个部分，如FILE, IFD0, EXIF等，详细解析参考文档。</p>
<p>顺便提一下，<a href="http://cn2.php.net/manual/en/function.imagick-getimageproperties.php" title="Imagick::getImageProperties" target="_blank">Imagick</a>的getImageProperties()也可以读取部分exif信息。</p>
<h2>读取信息</h2>
<p>下面示例读取并列出所有信息。</p>
<pre class="brush:php">
$img = 'yansuo_p1.jpg';

//Read exif meta information
$info = exif_read_data($img, NULL, true, false);

//Read thumbnail data
$thumb = exif_thumbnail($img, $width, $height, $type);

//Get mime type: require GD2 extension
$mimeType = image_type_to_mime_type($type);
$data = base64_encode($thumb);

echo 'Thumbnail: ', $width , 'x', $height, ', Type:',
	$type, ';', $mimeType , "&lt;br /&gt;\n";
echo '&lt;img alt="" src="data:', $type, ';base64,', $data, '" /&gt;';

foreach ($info as $key=>$sinfo) {
	echo '&lt;h3&gt;', $key, '&lt;/h3&gt;';
	if (is_array($sinfo)) {
		echo '&lt;table border="1" cellspacing="0" borderColor="#CCC"&gt;';
		foreach ($sinfo as $skey=>$svalue) {
			echo '&lt;tr&gt;&lt;td&gt;', $skey, '&lt;/td&gt;&lt;td&gt;', $svalue, '&lt;/td&gt;&lt;/tr&gt;';
		}
		echo '&lt;/table&gt;';
	}
}
</pre>
<p>测试图片：<a href="http://lib.fwso.cn/exif/yansuo_p1.jpg" title="Exif demo image" target="_blank">http://lib.fwso.cn/exif/yansuo_p1.jpg</a><br />
示例: <a href="http://lib.fwso.cn/exif/exif_read_data.php" title="exif demo" target="_blank">http://lib.fwso.cn/exif/exif_read_data.php</a></p>
<p>注意到数据分为了几个部分（组）， FILE是图片文件的基本信息.</p>
<table border="1" cellspacing="0" bordercolor="#CCC">
<caption>表１, FILE</caption>
<tbody>
<tr>
<td>FileName</td>
<td>yansuo_p1.jpg</td>
</tr>
<tr>
<td>FileDateTime</td>
<td>1321330357</td>
</tr>
<tr>
<td>FileSize</td>
<td>5439972</td>
</tr>
<tr>
<td>FileType</td>
<td>2</td>
</tr>
<tr>
<td>MimeType</td>
<td>image/jpeg</td>
</tr>
<tr>
<td>SectionsFound</td>
<td>ANY_TAG, IFD0, THUMBNAIL, EXIF, INTEROP, MAKERNOTE</td>
</tr>
</tbody>
</table>
<p>COMPUTED是经过exif扩展处理的一些数据，IsColor没有找到相关资料，应该是否彩色的意思；ByteOrderMotorola是字节顺序，0为低字节序(little-endian), 1为高字节序(big-endian);CCDWidth表示相机感光器大小，该值跟计算方法有点关系，Canon 600D的感光器大小为22.3mm,这里显示为22，Picasa为23；ApertureFNumber,及后面几项不用解释了。其实这些我只关心Width, Height, CCDWidth, ApertureFNumber.</p>
<table border="1" cellspacing="0" bordercolor="#CCC">
<caption>表２, COMPUTED</caption>
<tbody>
<tr>
<td>html</td>
<td>width=&#8221;5184&#8243; height=&#8221;3456&#8243;</td>
</tr>
<tr>
<td>Height</td>
<td>3456</td>
</tr>
<tr>
<td>Width</td>
<td>5184</td>
</tr>
<tr>
<td>IsColor</td>
<td>1</td>
</tr>
<tr>
<td>ByteOrderMotorola</td>
<td>0</td>
</tr>
<tr>
<td>CCDWidth</td>
<td>22mm</td>
</tr>
<tr>
<td>ApertureFNumber</td>
<td>f/6.3</td>
</tr>
<tr>
<td>UserComment</td>
<td></td>
</tr>
<tr>
<td>UserCommentEncoding</td>
<td>UNDEFINED</td>
</tr>
<tr>
<td>Thumbnail.FileType</td>
<td>2</td>
</tr>
<tr>
<td>Thumbnail.MimeType</td>
<td>image/jpeg</td>
</tr>
</tbody>
</table>
<p>IFD0表示主要图相的属性，对应的IFD1就是thumbnail。IFD0中我关心的数据只是Make和Model.</p>
<table border="1" cellspacing="0" bordercolor="#CCC">
<caption>表3, IFD0</caption>
<tbody>
<tr>
<td>Make</td>
<td>Canon</td>
</tr>
<tr>
<td>Model</td>
<td>Canon EOS 600D</td>
</tr>
<tr>
<td>Orientation</td>
<td>1</td>
</tr>
<tr>
<td>XResolution</td>
<td>72/1</td>
</tr>
<tr>
<td>YResolution</td>
<td>72/1</td>
</tr>
<tr>
<td>ResolutionUnit</td>
<td>2</td>
</tr>
<tr>
<td>DateTime</td>
<td>2011:11:05 14:38:23</td>
</tr>
<tr>
<td>Artist</td>
<td></td>
</tr>
<tr>
<td>YCbCrPositioning</td>
<td>2</td>
</tr>
<tr>
<td>Copyright</td>
<td></td>
</tr>
<tr>
<td>Exif_IFD_Pointer</td>
<td>348</td>
</tr>
</tbody>
</table>
<p>EXIF IFD部分数据比较多,只列出了部分，其中ExposureTime是曝光时间, FNumber是光圈大小, ExposureProgram是曝光程序（０表示未定义, 1表示手动曝光, 2表示程序自动曝光, 3表示光圈优先自动曝光,4表示快门优先自动曝光&#8230;), ISOSpeedRatings为ISO感光度,ExifVersion为EXIF标准的版本, Flash为闪光方式(16关闭,没有闪光,<a href="http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/EXIF.html#Flash" title="EXIF Flash values" target="_blank">参考</a>),FocalLength为焦距(这里是20mm, Canon 600D的实际焦距是20&#215;1.6=32mm), </p>
<table border="1" cellspacing="0" bordercolor="#CCC">
<caption>表4, EXIF IFD</caption>
<tbody>
<tr>
<td>ExposureTime</td>
<td>1/800</td>
</tr>
<tr>
<td>FNumber</td>
<td>63/10</td>
</tr>
<tr>
<td>ExposureProgram</td>
<td>3</td>
</tr>
<tr>
<td>ISOSpeedRatings</td>
<td>100</td>
</tr>
<tr>
<td>UndefinedTag:0&#215;8830</td>
<td>2</td>
</tr>
<tr>
<td>UndefinedTag:0&#215;8832</td>
<td>100</td>
</tr>
<tr>
<td>ExifVersion</td>
<td>0230</td>
</tr>
<tr>
<td>DateTimeOriginal</td>
<td>2011:11:05 14:38:23</td>
</tr>
<tr>
<td>DateTimeDigitized</td>
<td>2011:11:05 14:38:23</td>
</tr>
<tr>
<td>ComponentsConfiguration</td>
<td></td>
</tr>
<tr>
<td>ShutterSpeedValue</td>
<td>630784/65536</td>
</tr>
<tr>
<td>ApertureValue</td>
<td>352256/65536</td>
</tr>
<tr>
<td>ExposureBiasValue</td>
<td>0/1</td>
</tr>
<tr>
<td>MeteringMode</td>
<td>5</td>
</tr>
<tr>
<td>Flash</td>
<td>16</td>
</tr>
<tr>
<td>FocalLength</td>
<td>20/1</td>
</tr>
<tr>
<td>FocalPlaneXResolution</td>
<td>5184000/905</td>
</tr>
<tr>
<td>FocalPlaneYResolution</td>
<td>3456000/595</td>
</tr>
<tr>
<td>FocalPlaneResolutionUnit</td>
<td>2</td>
</tr>
<tr>
<td>CustomRendered</td>
<td>0</td>
</tr>
<tr>
<td>ExposureMode</td>
<td>0</td>
</tr>
<tr>
<td>WhiteBalance</td>
<td>0</td>
</tr>
<tr>
<td>SceneCaptureType</td>
<td>0</td>
</tr>
<tr>
<td>UndefinedTag:0xA430</td>
<td></td>
</tr>
<tr>
<td>UndefinedTag:0xA431</td>
<td>074053015010</td>
</tr>
<tr>
<td>UndefinedTag:0xA432</td>
<td>Array</td>
</tr>
<tr>
<td>UndefinedTag:0xA434</td>
<td>EF-S18-55mm f/3.5-5.6 IS II</td>
</tr>
<tr>
<td>UndefinedTag:0xA435</td>
<td>0000042eb8</td>
</tr>
</tbody>
</table>
<p>还注意到一些数据属性为UndefinedTag:0x****, 这些属性都是非标准属性，是由相机生产商自定义的一些数据。特别是MakerNote的属性都是根据不能品牌型号而定的。</p>
<p>一个简单示例：<a href="http://lib.fwso.cn/exif/index.php" title="EXIF demo">http://lib.fwso.cn/exif/index.php</a></p>
<h2>参考</h2>
<p>1. <a href="http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/EXIF.html" title="EXIF tags">EXIF tags</a></p>
]]></content:encoded>
			<wfw:commentRss>http://fwso.cn/php/%e6%95%b0%e7%a0%81%e7%85%a7%e7%89%87exif%e4%bf%a1%e6%81%af%e8%af%bb%e5%8f%96php/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>MongoDB入门(PHP)</title>
		<link>http://fwso.cn/php/mongodb%e5%85%a5%e9%97%a8php/</link>
		<comments>http://fwso.cn/php/mongodb%e5%85%a5%e9%97%a8php/#comments</comments>
		<pubDate>Thu, 10 Nov 2011 09:33:54 +0000</pubDate>
		<dc:creator>James Tang</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[MongoDB]]></category>

		<guid isPermaLink="false">http://fwso.cn/?p=313</guid>
		<description><![CDATA[MongoDB是目前一款比较流行的文档数据(document-oriented database), 类似的还有Apache CouchDB等。 安装 MongoDB安装非常简单，在http://www.mongodb.org下载对应版本(如Linux 32bit/64bit)，解压即可。 PHP需要安装Mongo扩展(MongoDB driver for PHP),通过pecl或在http://pecl.php.net/package/mongo下载编译即可。 MongoDB服务启动/停止 例如: ./mongod --port 10001 \ --logpath=/var/logs/mongodb/mongod.log \ --dbpath=/var/data/db/ \ --pidfilepath /var/run/mongod.pid \ --directoryperdb 很多其它参数参考： ./mongod --help &#124; less 停止服务，直接Ctrl+C或者kill `cat /var/run/mongod.pid`即可。 另外先提一下MongoDB提供的Shell客户端：./mongo, 很多操作需要使用它，例如数据管理配置等。 ./mongo 默认连接到localhost:27017/test ./mongo 192.168.0.33:10001/blog 连接到192.168.0.33的服务器，端口为10001, &#8230; <a href="http://fwso.cn/php/mongodb%e5%85%a5%e9%97%a8php/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>MongoDB是目前一款比较流行的文档数据(document-oriented database), 类似的还有Apache CouchDB等。</p>
<h2>安装</h2>
<p>MongoDB安装非常简单，在http://www.mongodb.org下载对应版本(如Linux 32bit/64bit)，解压即可。</p>
<p>PHP需要安装Mongo扩展(MongoDB driver for PHP),通过pecl或在http://pecl.php.net/package/mongo下载编译即可。</p>
<h2>MongoDB服务启动/停止</h2>
<p>例如:</p>
<pre class="brush:bash">
./mongod --port 10001 \
--logpath=/var/logs/mongodb/mongod.log \
--dbpath=/var/data/db/ \
--pidfilepath /var/run/mongod.pid \
--directoryperdb
</pre>
<p>很多其它参数参考：</p>
<pre class="brush:bash">
./mongod --help | less
</pre>
<p>停止服务，直接Ctrl+C或者kill `cat /var/run/mongod.pid`即可。</p>
<p>另外先提一下MongoDB提供的Shell客户端：./mongo, 很多操作需要使用它，例如数据管理配置等。</p>
<pre class="brush:bash">
./mongo
</pre>
<p>默认连接到localhost:27017/test</p>
<pre class="brush:bash">
./mongo 192.168.0.33:10001/blog
</pre>
<p>连接到192.168.0.33的服务器，端口为10001, 并使用blog数据库.</p>
<h2>database与collection</h2>
<p>MongoDB是free-schema数据库，所以文档结构不需要像关系型数据库一样严格定义，也没有类似create database, create table, alter table等之类的操作。</p>
<p>例如下面命令将工作数据库切换到blog，但blog并不需要预先create.</p>
<pre class="brush:bash;">
&gt; use blog
</pre>
<p>对于collection也是类似，不需要预先创建, 下面命令将文档插入名为posts的collection.</p>
<pre class="brush:bash:">
&gt; db.posts.insert({"title":"Hello, MongoDB", "date":new Date()})
</pre>
<p>Collection类似于关系型数据库的表.</p>
<p>注意到这里插入的简单文档看起来是一个JSON。MongoDB存储文档的格式正是JSON,不过是Binary JSON,所要又称为<a href="http://bsonspec.org/" title="BSON offical site">BSON</a>.</p>
<h2>PHP访问数据库</h2>
<p>详细文档请参考:<a href="http://cn2.php.net/manual/en/book.mongo.php" title="MongoDB driver for PHP" target="_blank">PHP:Mongo</a>, 下面例举简单示例。</p>
<p><strong>连接数据库</strong>：</p>
<pre class="brush:php">
$mongo = new Mongo("192.168.0.33:10001");
</pre>
<p><strong>选择数据库</strong>：</p>
<pre class="brush:php">
//use database blog
$blog = $mongo->blog;
</pre>
<p><strong>选择Collection</strong>:</p>
<pre class="brush:php">
//collection posts
$posts = $blog->posts;
</pre>
<p><strong>写入数据</strong>：</p>
<pre class="brush:php">
$post = array(
    'title'=>'Hello, MongoDB',
    'content'=>'
<h1>Great MongoDB</h1>

',
    'date'=>new MongoDate(/*default=time()*/),
    'views' => 0
);

//insert one document
$posts->insert($post);

$post2 = array(
        'title' => 'Powerfull mongoDB',
        'content' => '
<h1>Powerfull, mongoDB</h1>

',
        'author' => 'James Tang',
        'date' => new MongoDate(/*time()*/),
        'views' => 0
);

//insert multiple documents
$posts->batchInsert(array($post, $post2));
</pre>
<p>写入操作可以有可选参数,　如：</p>
<pre class="brush:php">
$posts->batchInsert(array($post, $post2),array(
    'safe'=> true
));
</pre>
<p>&#8216;safe&#8217;=>true 表示等待数据库响应再返回，如果写入失败则抛出MongoCursorException异常，其它参数参考文档。</p>
<p><strong>更新</strong>：</p>
<pre class="brush:php">
//Query one document
$post = $posts->findOne(array('title' => 'Hello, MongoDB'));

if ($post) {
        $post['title'] = 'Hello, mongoDB 2';

        //update
        $posts->update(array('_id'=> $post['_id']), $post);
}
</pre>
<p><strong>Upsert</strong>, 即更新或者新增加：</p>
<pre class="brush:php">

//use 'upsert' option of update() method
$posts->update(array('title'=> $post['title']), $post, array(
   'upsert'=>true,
));

//use save() method, more convenient
$posts->save($post);
</pre>
<p>MongoDB提供了更新操作符，如$inc, $set, $push, &#8230;</p>
<pre class="brush:php">
//$inc modifier, value of `views` field will increase one.
$posts->update(array('_id'=> $post['_id']), array(
        '$inc' => array('views'=>1)
));

//$set modifier, set the value of `views` to 0
$posts->update(array('_id'=> $post['_id']), array(
        '$set' => array('views'=>0)
));

//$push adds an element to the end of an array
//if the specified key already exists and
//creates a new array if it does not.
//a new field `tags` will be added for our example
//and has one element `php`
$posts->update(array('_id'=> $post['_id']), array(
        '$push' => array('tags'=>'php')
));
</pre>
<p>更多关于更新与操作符的信息，请参考：<a href="http://www.mongodb.org/display/DOCS/Updating" title="ＭongoDB updating modifier" target="_blank">Updating</a>。</p>
<p><strong>删除</strong>:</p>
<pre class="brush:php">
$posts->remove(array(
        'title' => 'Hello, mongoDB'
));

//remove all document in the collections
$posts->remove();

//more efficient than remove()
$posts->drop();
</pre>
<p>简单查询：</p>
<pre class="brush:php">
//query one document
$posts->findOne(/*criteria*/);

//a cursor of documents
$posts->find(/*criteria*/);
</pre>
<p>示例：</p>
<pre class="brush:php">
//query the document with title contains "mongoDB", case insensitive,
//and only return the title, date fields.
$cursor = $posts->find(array(
        'title' => new MongoRegex('/mongoDB/i'),
),array(
        '_id'=>false,
        'title',
        'date'
));

//sorting document
$cursor->sort(array(
        'title' => -1, //1-ascending, -1 descending
));

//limit in 3 document
$cursor->limit(3);

foreach ($cursor as $post) {
        echo $post['title'] . "\n";
        echo date('Y-m-d', $post['date']->sec) . "\n";
}
</pre>
<p>参考：<a href="http://www.mongodb.org/display/DOCS/Querying" title="MongoDB querying" target="_blank">http://www.mongodb.org/display/DOCS/Querying</a></p>
<h2>&#8220;_id&#8221;与ObjectId</h2>
<p>每一个Document都有一个_id属性，每个文档的_id必须唯一，如果创建文档时没有指定，Mongo客户端会自动生成一个_id，其值为ObjectId类型，在PHP中对应的类为MongoId.</p>
<p>&#8220;_id&#8221;也可以是其它类型，如Int, string,但不能是array。</p>
<p>ObjectId以12字节存储，其生成方法如下：</p>
<table>
<tr>
<td>0</td>
<td>1</td>
<td>2</td>
<td style="border-right:1px solid #CCC;">3</td>
<td>4</td>
<td>5</td>
<td style="border-right:1px solid #CCC;">6</td>
<td>7</td>
<td style="border-right:1px solid #CCC;">8</td>
<td>9</td>
<td>10</td>
<td>11</td>
</tr>
<tr>
<td colspan="4" style="border-right:1px solid #CCC;">Timestamp</td>
<td colspan="3" style="border-right:1px solid #CCC;">Machine</td>
<td colspan="2" style="border-right:1px solid #CCC;">PID</td>
<td colspan="3" style="border-right:1px solid #CCC;">Increment</td>
</tr>
</table>
<ul>
<li>Timestamp精确到秒，如1320978110</li>
<li>Machine为hostname, 或者Mac/网络地址, 或者虚拟机ID的ＭD5序列的前三字节</li>
<li>Increment,类似于自增，所以在新增多个文档时可能这几位是连续的，但也可能是随机数</li>
</ul>
<p>除了timestamp可以确定外，后面三部分可能会因客户端不同而生成方式有点差异。</p>
<p>例如ObjectId(&#8220;4ebc86bedf8a426362118b5d&#8221;)，这是ObjectID的表示，4ebc86be对应1320978110。</p>
<p>但ObjectID之所以表示为24个字符的字符串，是因为第个字节以两个十六进制数表示。</p>
<h2>总结</h2>
<p>MongoDB更适合面向对象编程,free-schema能够更加敏捷地就会需求变化。其它高级特性及应用需要进一步学习。</p>
<div style="width:425px" id="__ss_10101992"> <strong style="display:block;margin:12px 0 4px"><a href="http://www.slideshare.net/fwso/mongodb-withphpjamestang-10101992" title="Introduction to MongoDB with PHP" target="_blank">Introduction to MongoDB with PHP</a></strong> <iframe src="http://www.slideshare.net/slideshow/embed_code/10101992" width="425" height="355" frameborder="0" marginwidth="0" marginheight="0" scrolling="no"></iframe>
<div style="padding:5px 0 12px"> View more <a href="http://www.slideshare.net/" target="_blank">presentations</a> from <a href="http://www.slideshare.net/fwso" target="_blank">fwso</a> </div>
</p></div>
]]></content:encoded>
			<wfw:commentRss>http://fwso.cn/php/mongodb%e5%85%a5%e9%97%a8php/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>PHP5.4新特性小结</title>
		<link>http://fwso.cn/php/php5-4%e6%96%b0%e7%89%b9%e6%80%a7%e5%b0%8f%e7%bb%93/</link>
		<comments>http://fwso.cn/php/php5-4%e6%96%b0%e7%89%b9%e6%80%a7%e5%b0%8f%e7%bb%93/#comments</comments>
		<pubDate>Thu, 27 Oct 2011 15:15:07 +0000</pubDate>
		<dc:creator>James Tang</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[PHP PHP5.4]]></category>

		<guid isPermaLink="false">http://fwso.cn/?p=307</guid>
		<description><![CDATA[PHP5.4似乎很受关注，今天看了一些PHP5.4主要特性相关文章，因此在这里小结一下。 昨天(2011-10-26)官网发布PHP5.4.0beta2，其中好几点更新是由Laruence贡献的！本文部分内容也是源自Laruence的博客。 1. Buid-in web server PHP5.4内置了一个简单的Web服务器，这样在做一些简单程序就方便多了，省去了环境配置的工作，特别对于初学者来说。 把当前目录作为Root Document只需要这条命令即可: $ php -S localhost:3300 也可以指定其它路径： $ php -S localhost:3300 -t /path/to/root 还可以指定路由： $ php -S localhost:3300 router.php 参考：PHP: Build-in web server 2. Traits Traits提供了一种灵活的代码重用机制，即不像interface一样只能定义方法但不能实现，又不能像class一样只能单继承。至于在实践中怎样使用，还需要深入思考。 官网的一个例子： trait SayWorld { public function sayHello() &#8230; <a href="http://fwso.cn/php/php5-4%e6%96%b0%e7%89%b9%e6%80%a7%e5%b0%8f%e7%bb%93/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>PHP5.4似乎很受关注，今天看了一些PHP5.4主要特性相关文章，因此在这里小结一下。</p>
<p>昨天(2011-10-26)官网发布PHP5.4.0beta2，其中好几点更新是由<a href="http://www.laruence.com/">Laruence</a>贡献的！本文部分内容也是源自Laruence的博客。</p>
<h2>1. Buid-in web server</h2>
<p>PHP5.4内置了一个简单的Web服务器，这样在做一些简单程序就方便多了，省去了环境配置的工作，特别对于初学者来说。</p>
<p>把当前目录作为Root Document只需要这条命令即可:</p>
<pre class="brush:bash">
$ php -S localhost:3300
</pre>
<p>也可以指定其它路径：</p>
<pre class="brush:bash">
$ php -S localhost:3300 -t /path/to/root
</pre>
<p>还可以指定路由：</p>
<pre class="brush:bash">
$ php -S localhost:3300 router.php
</pre>
<p>参考：<a href="http://php.net/manual/en/features.commandline.webserver.php">PHP: Build-in web server</a></p>
<h2>2. Traits</h2>
<p>Traits提供了一种灵活的代码重用机制，即不像interface一样只能定义方法但不能实现，又不能像class一样只能单继承。至于在实践中怎样使用，还需要深入思考。</p>
<p>官网的一个例子：</p>
<pre class="brush:php">
trait SayWorld {
        public function sayHello() {
                parent::sayHello();
                echo "World!\n";
                echo 'ID:' . $this->id . "\n";
        }
}

class Base {
        public function sayHello() {
                echo 'Hello ';
        }
}

class MyHelloWorld extends Base {
        private $id;

        public function __construct() {
                $this->id = 123456;
        }

        use SayWorld;
}

$o = new MyHelloWorld();
$o->sayHello();

/*will output:
Hello World!
ID:123456
 */
</pre>
<p>参考：http://cn.php.net/manual/en/language.oop5.traits.php</p>
<h2>3. Short array syntax</h2>
<p>PHP5.4提供了数组简短语法：</p>
<pre class="brush:php">
$arr = [1,'james', 'james@fwso.cn'];
</pre>
<h2>4. Array dereferencing</h2>
<pre class="brush:php">
function myfunc() {
    return array(1,'james', 'james@fwso.cn');
}
</pre>
<p>我认为比数组简短语法更方便的是dereferencing，以前我们需要这样：</p>
<pre class="brush:php">
$arr = myfunc();
echo $arr[1];
</pre>
<p>在PHP5.4中这样就行了：</p>
<pre class="brush:php">
echo myfunc()[1];
</pre>
<h2>5. Upload progress</h2>
<p>Session提供了上传进度支持，通过$_SESSION["upload_progress_name"]就可以获得当前文件上传的进度信息，结合Ajax就能很容易实现上传进度条了。</p>
<p>参考：http://www.laruence.com/2011/10/10/2217.html</p>
<h2>6. JsonSerializable Interface</h2>
<p>实现了JsonSerializable接口的类的实例在json_encode序列化的之前会调用jsonSerialize方法，而不是直接序列化对象的属性。</p>
<p>参考：<a href="http://www.laruence.com/2011/10/10/2204.html">http://www.laruence.com/2011/10/10/2204.html</a></p>
<h2>7. Use mysqlnd by default</h2>
<p>现在mysql, mysqli, pdo_mysql默认使用mysqlnd本地库，在PHP5.4以前需要:</p>
<pre class="brush:bash">
$./configure --with-mysqli=mysqlnd
</pre>
<p>现在：</p>
<pre class="brush:bash">
$./configure --with-mysqli
</pre>
<h2>8. 更多</h2>
<p><a href="http://cn2.php.net/releases/NEWS_5_4_0_beta2.txt">http://cn2.php.net/releases/NEWS_5_4_0_beta2.txt</a></p>
]]></content:encoded>
			<wfw:commentRss>http://fwso.cn/php/php5-4%e6%96%b0%e7%89%b9%e6%80%a7%e5%b0%8f%e7%bb%93/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>项目中遇到的问题</title>
		<link>http://fwso.cn/essay/%e9%a1%b9%e7%9b%ae%e4%b8%ad%e9%81%87%e5%88%b0%e7%9a%84%e9%97%ae%e9%a2%98/</link>
		<comments>http://fwso.cn/essay/%e9%a1%b9%e7%9b%ae%e4%b8%ad%e9%81%87%e5%88%b0%e7%9a%84%e9%97%ae%e9%a2%98/#comments</comments>
		<pubDate>Sun, 09 Oct 2011 14:47:38 +0000</pubDate>
		<dc:creator>James Tang</dc:creator>
				<category><![CDATA[Essay]]></category>

		<guid isPermaLink="false">http://fwso.cn/?p=303</guid>
		<description><![CDATA[总结一下在项目中遇到的问题，我发现大部分问题都是可以避免的。 1. 管理问题 我并不懂管理，只是基于个人理解来总结一些问题： 项目进度安排不合理甚至没有明确的进度安排，阶段目标不明确，有的管理者喜欢说“尽快完成”，尽快到底是多快!？ 人员安排不恰当，招人的时候分了中高级程序员(或者工程师，我很混淆这两个概念，工程师听起来似乎高级一点，但事实上&#8230;），但在实际工作似乎没有体现出来。另外就是经常让另一个开发人员修复其他开发人员程序中的Bug，没有正确认识bug的严重程度，没有意识到修复bug可能会引入新的bug，特别是在对整个项目没有全局把握的情况下。当然，如果原开发人员已经离职就另当别论了。 管理者不懂技术、或者出于其它目的，总之无论客户提出什么需求都统统揽下，返回第一点 最后，管理者不以身作则，只知道让程序员去做事情，自己很官僚。 2. 文档问题 其实文档也应该属于管理的问题，但我觉得这个问题很重要。 对于文档问题主要体现在： 没有文档，完成任务很需要想像力(很恐怖吧)。 文档混乱，不知道这个文档是需求文档，还是设计文档。 文档不规范，用语就像在写散文，用词不严谨，太多假设和歧义。 3. 规范问题 这里特指编码规范，要么没有编码规范，各自用自己的方式写代码，如果是注重代码重量的程序员还好，如果遇到连缩进都不用的程序员简直让人发疯。要么就是有规范，但没有人遵守，其实跟没有一样。 4. 态度问题 很多人写代码好像从来不好好测试一下自己的代码，因为我经常在修复别人的bug的时候发现是些非常愚蠢的问题。 另一个问题就是有人为了快速解决问题完全不考虑效率和优化问题，用一些令人费解而笨拙的方式来解决问题。虽说不要过早考虑优化，但至少不能过度不优化，也不能不考虑基本的扩展性、稳定性、安全性&#8230;]]></description>
			<content:encoded><![CDATA[<p>总结一下在项目中遇到的问题，我发现大部分问题都是可以避免的。</p>
<h2>1. 管理问题</h2>
<p>我并不懂管理，只是基于个人理解来总结一些问题：</p>
<ol>
<li>项目进度安排不合理甚至没有明确的进度安排，阶段目标不明确，有的管理者喜欢说“尽快完成”，尽快到底是多快!？</li>
<li>人员安排不恰当，招人的时候分了中高级程序员(或者工程师，我很混淆这两个概念，工程师听起来似乎高级一点，但事实上&#8230;），但在实际工作似乎没有体现出来。另外就是<strong>经常</strong>让另一个开发人员修复其他开发人员程序中的Bug，没有正确认识bug的严重程度，没有意识到修复bug可能会引入新的bug，特别是在对整个项目没有全局把握的情况下。当然，如果原开发人员已经离职就另当别论了。</li>
<li>管理者不懂技术、或者出于其它目的，总之无论客户提出什么需求都统统揽下，返回第一点</li>
<li>最后，管理者不以身作则，只知道让程序员去做事情，自己很官僚。</li>
</ol>
<h2>2. 文档问题</h2>
<p>其实文档也应该属于管理的问题，但我觉得这个问题很重要。</p>
<p>对于文档问题主要体现在：</p>
<ol>
<li>没有文档，完成任务很需要想像力(很恐怖吧)。</li>
<li>文档混乱，不知道这个文档是需求文档，还是设计文档。</li>
<li>文档不规范，用语就像在写散文，用词不严谨，太多假设和歧义。</li>
</ol>
<h2>3. 规范问题</h2>
<p>这里特指编码规范，要么没有编码规范，各自用自己的方式写代码，如果是注重代码重量的程序员还好，如果遇到连缩进都不用的程序员简直让人发疯。要么就是有规范，但没有人遵守，其实跟没有一样。</p>
<h2>4. 态度问题</h2>
<p>很多人写代码好像从来不好好测试一下自己的代码，因为我经常在修复别人的bug的时候发现是些非常愚蠢的问题。</p>
<p>另一个问题就是有人为了快速解决问题完全不考虑效率和优化问题，用一些令人费解而笨拙的方式来解决问题。虽说不要过早考虑优化，但至少不能过度不优化，也不能不考虑基本的扩展性、稳定性、安全性&#8230;</p>
]]></content:encoded>
			<wfw:commentRss>http://fwso.cn/essay/%e9%a1%b9%e7%9b%ae%e4%b8%ad%e9%81%87%e5%88%b0%e7%9a%84%e9%97%ae%e9%a2%98/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>程序员该做的事</title>
		<link>http://fwso.cn/essay/%e7%a8%8b%e5%ba%8f%e5%91%98%e8%af%a5%e5%81%9a%e7%9a%84%e4%ba%8b/</link>
		<comments>http://fwso.cn/essay/%e7%a8%8b%e5%ba%8f%e5%91%98%e8%af%a5%e5%81%9a%e7%9a%84%e4%ba%8b/#comments</comments>
		<pubDate>Thu, 29 Sep 2011 03:30:21 +0000</pubDate>
		<dc:creator>James Tang</dc:creator>
				<category><![CDATA[Essay]]></category>
		<category><![CDATA[程序员]]></category>

		<guid isPermaLink="false">http://fwso.cn/?p=299</guid>
		<description><![CDATA[今天在车上一直在思考什么是真正的程序员，程序员应该做的是什么，在我看来程序员主要应该专注于三个方面: 1. 参与实现完整的系统，大部分工作应该是通过编码来实现算法与业务逻辑 2. 项目管理，像项目经理之类的工作，不仅要是一个合格的程序员，还需要熟悉业务流程和项目管理 3. 系统架构，构建系统的蓝图 作为程序员最基本的应该是对计算机系统和软件工程有足够的理解，否则不能算是程序员。 现在很多所谓的程序员所做的工作其实根本不算真正的程序员，比如只是做简单的二次开发，利用开源或者其它已有程序进行简单的整合，作一些简单的修改和配置，而对整个程序的实现没有较为深入的常识，这样的“程序员”最多算是一个计算机高级用户。]]></description>
			<content:encoded><![CDATA[<p>今天在车上一直在思考什么是真正的程序员，程序员应该做的是什么，在我看来程序员主要应该专注于三个方面:</p>
<p>1. 参与实现完整的系统，大部分工作应该是通过编码来实现算法与业务逻辑<br />
2. 项目管理，像项目经理之类的工作，不仅要是一个合格的程序员，还需要熟悉业务流程和项目管理<br />
3. 系统架构，构建系统的蓝图</p>
<p>作为程序员最基本的应该是对计算机系统和软件工程有足够的理解，否则不能算是程序员。</p>
<p>现在很多所谓的程序员所做的工作其实根本不算真正的程序员，比如只是做简单的二次开发，利用开源或者其它已有程序进行简单的整合，作一些简单的修改和配置，而对整个程序的实现没有较为深入的常识，这样的“程序员”最多算是一个计算机高级用户。</p>
]]></content:encoded>
			<wfw:commentRss>http://fwso.cn/essay/%e7%a8%8b%e5%ba%8f%e5%91%98%e8%af%a5%e5%81%9a%e7%9a%84%e4%ba%8b/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>phpmemecahed扩展编译的问题</title>
		<link>http://fwso.cn/php/phpmemecahed%e6%89%a9%e5%b1%95%e7%bc%96%e8%af%91%e7%9a%84%e9%97%ae%e9%a2%98/</link>
		<comments>http://fwso.cn/php/phpmemecahed%e6%89%a9%e5%b1%95%e7%bc%96%e8%af%91%e7%9a%84%e9%97%ae%e9%a2%98/#comments</comments>
		<pubDate>Wed, 17 Aug 2011 14:04:39 +0000</pubDate>
		<dc:creator>James Tang</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[libmemcached]]></category>
		<category><![CDATA[memcached]]></category>

		<guid isPermaLink="false">http://fwso.cn/?p=296</guid>
		<description><![CDATA[PHP有两个memcached的扩展，其中一个较新的扩展也叫做memcached,跟memcached服务程序同名，所以有些混淆。 今天下载最新的扩展memcached-2.0.0b2.tgz, 编译的时候出现错误，问题是php_memcached.c中MEMCACHED_BEHAVIOR_TCP_KEEPALIVE没有定义。经google发现是MEMCACHED_BEHAVIOR_TCP_KEEPALIVE应该是在libmemcached中定义的，但在libmemcached的头文件中并也没有找到该常量。 最后发现原来是系统安装的libmemcached版本太老了，该版本是通过yum安装的。因此只有下载新的libmemcached,我下载的是 libmemcached-0.50，而不是最新的0.51，因为最新版有一个bug而无法编译完成。 至于php-memcached和memcache差别，参考memcache v.s. memcached]]></description>
			<content:encoded><![CDATA[<p>PHP有两个memcached的扩展，其中一个较新的扩展也叫做memcached,跟memcached服务程序同名，所以有些混淆。</p>
<p>今天下载最新的扩展<a href="http://pecl.php.net/package/memcached">memcached-2.0.0b2.tgz</a>, 编译的时候出现错误，问题是php_memcached.c中MEMCACHED_BEHAVIOR_TCP_KEEPALIVE没有定义。经google发现是MEMCACHED_BEHAVIOR_TCP_KEEPALIVE应该是在libmemcached中定义的，但在libmemcached的头文件中并也没有找到该常量。</p>
<p>最后发现原来是系统安装的libmemcached版本太老了，该版本是通过yum安装的。因此只有下载新的libmemcached,我下载的是  libmemcached-0.50，而不是最新的0.51，因为最新版有一个bug而无法编译完成。</p>
<p>至于php-memcached和memcache差别，参考<a href="http://serverfault.com/questions/63383/memcache-vs-memcached">memcache v.s. memcached</a></p>
]]></content:encoded>
			<wfw:commentRss>http://fwso.cn/php/phpmemecahed%e6%89%a9%e5%b1%95%e7%bc%96%e8%af%91%e7%9a%84%e9%97%ae%e9%a2%98/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
<!-- This Quick Cache file was built for (  fwso.cn/feed/ ) in 0.25797 seconds, on May 19th, 2012 at 8:23 pm CST. -->
<!-- This Quick Cache file will automatically expire ( and be re-built automatically ) on May 20th, 2012 at 8:23 pm CST -->
<!-- +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<!-- Quick Cache Is Fully Functional :-) ... A Quick Cache file was just served for (  fwso.cn/feed/ ) in 0.00051 seconds, on May 20th, 2012 at 7:42 am UTC. -->
