Submitted by gouki on 2010, June 12, 10:57 AM
文章来自:http://blog.csdn.net/jh_zzz/archive/2010/01/11/5173876.aspx
由于我也正在看 typecho这个玩意所以,就记录下来。虽然我一天下来,也看了不少代码,但毕竟没有深读过。对于流程啥的,还没有开始关心,只是为了写而写。
第三篇:插件机制
以 index.php 为例:
/** 初始化组件 */
Typecho_Widget:: widget('Widget_Init' );
Init 的 execute 中会初始化 Typecho_Plugin ,这里 $options -> plugins 是从数据库读出来后反序列化的:
Typecho_Plugin:: init($options -> plugins);
init 中分别将 plugins 中的 activated 和 handles
PHP代码
- $component = $this -> _handle . ':' . $component ;
- $last = count($args );
- $args [$last ] = $last > 0 ? $args [0 ] : false ;
-
- if (isset (self:: $_plugins ['handles' ][$component ])) {
- $args [$last ] = NULL ;
- $this -> _signal = true ;
- foreach (self:: $_plugins ['handles' ][$component ] as $callback ) {
- $args [$last ] = call_user_func_array($callback , $args );
- }
- }
__call 查找对应 index.php:begin 的 Typecho_Plugin ,如果找到的话,就会调用相应的方法。例如如果找到的是 HelloWorld_Plugin ,则 HelloWorld_Plugin.render() 会被执行。
(
[0] => HelloWorld_Plugin
[1] => render
)
简单说一下 Plugin 是如何加载的,在 config.inc.php 中首先设置了包含路径,插件路径也在其中:
/** 设置包含路径 */
@ set_include_path(get_include_path() . PATH_SEPARATOR .
__TYPECHO_ROOT_DIR__ . '/var' . PATH_SEPARATOR .
__TYPECHO_ROOT_DIR__ . __TYPECHO_PLUGIN_DIR__ );
HelloWorld_Plugin 此时尚未被加载,所以当执行到 HelloWorld_Plugin.render() 时 , Typecho_Common::__autoLoad 函数被执行,这里会自动加载指定的插件文件:
@ include_once str_replace('_' , '/' , $className ) . '.php' ;
例如对于 HelloWorld_Plugin ,文件就是 HelloWorld\Plugin.php ,因为 usr/plugin 目录已经在包含的路径中,所以这个文件可以正常加 载。
当初我学习 php 的时候还是 php3 ,现在一些新特性我都不知道,这一段我看了半天才搞 清楚,这次读这些代码了解了不少 php 的新特性:)
---EOF---
至此,JianHua Zhang的三篇博客都已经转载完毕,但是对于最后这个插件,我是建议在开发的时候,Typecho_Plugin类的__call方法里加上一行:
echo $component . '<br>';
这样可以在显示页面的时候让你了解你每块区域加载了哪些插件,也就是说,这些插件位置,是可以被你注入代码的。OK。了解了这一点,你可以根据你想要的效果为各个地方加上你的代码。
Tags: typecho, 笔记
PHP | 评论:0
| 阅读:19014
Submitted by gouki on 2010, June 12, 10:49 AM
文章来自:http://blog.csdn.net/jh_zzz/archive/2010/01/11/5173851.aspx,由于我也正在看 typecho这个玩意所以,就记录下来。虽然我一天下来,也看了不少代码,但毕竟没有深读过。对于流程啥的,还没有开始关心,只是为了写而写。
第二篇:数据库访问
这一块比较复杂,我还没有完全理解为什么要把 SQL 语句的组装搞这么复杂。
从一个普通皮肤页面开始 themes\default\index.php ,代码如下:
PHP代码
- <?php while ($this -> next()): ?>
- < div class ="post">
- < h2 class ="entry_title">< a href ="<?php $this -> permalink() ?>"><?php $this -> title() ?></ a ></ h2 >
- < p class ="entry_data">
- < span ><?php _e(' 作者: ' ); ?><?php $this -> author(); ?></ span >
- < span ><?php _e(' 发布时间: ' ); ?><?php $this -> date('F j, Y' ); ?></ span >
- < span ><?php _e(' 分类: ' ); ?><?php $this -> category(',' ); ?></ span >
- < a href ="<?php $this -> permalink() ?>#comments"><?php $this -> commentsNum('No Comments' , '1 Comment' , '%d Comments' ); ?></ a >
- </ p >
- <?php $this -> content(' 阅读剩余部分 ...' ); ?>
- </ div >
- <?php endwhile ; ?>
从上面我们知道这个文件是在 Widget_Archive 执行时被 包含进来的,所以在这里 $this 就是 Widget_Archive , next() 函数具体实现在 Typecho_Widget 中,他只是从 $stack 中取出一行数据返回,然后移向下一行, $stack 中的数据是如何生成的呢?
在 Widget_Archive 的 execute 函数中,有这几行:
$select = $this -> select()-> where ('table.contents.status = ?' , 'publish' )
-> where('table.contents.created < ?' , $this -> options-> gmtTime);
… 中间代码略过 …
$select -> order('table.contents.created' , Typecho_Db:: SORT_DESC)
-> page($this -> _currentPage, $this -> parameter-> pageSize);
$this -> db-> fetchAll ($select , array ($this , 'push' ));
下面是 Widget_Abstract_Contents:: select()
PHP代码
- public function fetchAll($query , array $filter = NULL )
- {
-
- $resource = $this -> query ($query , self:: READ);
- $result = array ();
-
-
- if (! emptyempty ($filter )) {
- list ($object , $method ) = $filter ;
- }
-
-
- while ($rows = $this -> _adapter-> fetch ($resource )) {
-
- $result [] = $filter ? call_user_func (array (& $object , $method ), $rows ) : $rows ;
- }
- return $result ;
- }
首先执行 query() , query() 函数会执行:
$resource = $this -> _adapter-> query($query , $handle , $op , $action );
然后将查询的结果返回,根据上面 提到的 config.inc.php ,这里的 _adapter 是 Typecho_Db_Adapter_Mysql ,他其实就是执行了一个 mysql 查询:
if ($resource = @ mysql_query($query instanceof Typecho_Db_Query ? $query -> __toString() : $query , $handle )) {
return $resource ;
}
这里的 $query->__toString() 会返回经过 Typecho_Db_Query 处理后的 最终用来执行的 SQL 语句。
得到查询结果后,上面的 fetchAll 函数就循环调用回调函数将数据回调出去,前面的代码中 Widget_Archive 在调用时指定的是 push ,在 push 函数中返回的每一行数据都被压入 $stack ,这样 $stack 中就有了一行行的数据了。
Tags: typecho, 笔记
PHP | 评论:0
| 阅读:19210
Submitted by gouki on 2010, June 12, 10:43 AM
文章来自:http://blog.csdn.net/jh_zzz/archive/2010/01/11/5173806.aspx,由于我也正在看typecho这个玩意所以,就记录下来。虽然我一天下来,也看了不少代码,但毕竟没有深读过。对于流程啥的,还没有开始关心,只是为了写而写。
第一篇:页面渲染及路由机制
从 index.php 开始看,
/** 初始化组件 */
Typecho_Widget:: widget('Widget_Init' );
看 Typecho_Widget:: widget 函数,查找 Widget\Init.php , Widget 下的文件都是从 Typecho_Widget 派生的,这里创建该对象实例,并将相关的 Request , Response 对象作为参数传递过去,然后调用该对象的 execute 方法。
看一下 Init 中的 execute ,首先会初始化一些参数,重点看看 MVC 架构的路由:
Typecho_Router:: setPathInfo($this -> request-> getPathInfo());
Typecho_Router:: setRoutes($options -> routingTable);
首先设置路径,然后初始化路由, $options -> routingTable 默认值是在安装时写在数据库中的,运行时再读出来, Typecho_Router 的 setRoutes 函数调用了 Typecho_Router_Parser 的 parse 函数, parse 函数遍历整个 routingTable 数组,将处理后的路由数组返回给 Typecho_Router ,保存在 $_routingTable
PHP代码
- if (! $validated && ! emptyempty ($this -> _archiveSlug)) {
- $themeFile = $this -> _archiveType . '/' . $this -> _archiveSlug . '.php' ;
- if (file_exists($themeDir . $themeFile )) {
- $this -> _themeFile = $themeFile ;
- $validated = true ;
- }
- }
这里需要看一下 _archiveType 和 _archiveSlug 是怎么来的:
在 Widget_Archived 的 execute 函数中会根据 $this -> parameter-> type 执行相应的 handler 。
PHP代码
- if (isset ($handles [$this -> parameter-> type])) {
- $handle = $handles [$this -> parameter-> type];
- $this -> {$handle }($select , $hasPushed );
- } else {
- $hasPushed = $this -> pluginHandle()-> handle($this -> parameter-> type, $this , $select );
- }
$this -> parameter-> type 这个变量是在构造函数中赋值的 :
$this -> parameter-> type = Typecho_Router:: $current ;
Typecho_Router:: $current 根据路由表可以查到对应于 index.php 就是 index 。
所以对于上面标黄的代码对应于 index.php 最终执行的是的是 $handles [‘index’] 对应的 handle ,就是 indexHandle 。可以看到对于其他的 Handle 一般都会设置 _archiveType 及 _archiveSlug 变量, indexHandle 没有,因为 _archiveType 默认就是 index ,所以在 index.php 中 _archiveType 等于 index , _archiveSlug 为空。
所以根据前面 render 函数中的代码,最终是找到对应于 $themeDir 下的 $this -> _archiveType . '/' . $this -> _archiveSlug . '.php' 文件,然后直接包含进来,我们看到的就是这个文件的输出了。
/** 注册一个结束插件 */
Typecho_Plugin:: factory('index.php' )-> end();
Tags: typecho, 笔记
PHP | 评论:0
| 阅读:21682
Submitted by gouki on 2010, June 12, 9:06 AM
自从评论里有人推荐typecho后,自己也下载了看了一下。确实,代码很漂亮,最关键的是注释是中文的。这点很让人心情愉快。虽然wordpress的英文注释也很容易懂,但毕竟不是自己的语言,总有点心里障碍。
前天晚上下载了一份看看,昨天在参考官方的一些插件的同时,自己临摹了两个。一个是搜索引擎来源关键字高亮,一个就是微博上有朋友提出的内容分页。
东西嘛。都扔在http://neatstudio.com/typecho/上面。还没有正式完成,只能算是一个测试版吧。
下面就是一些心得,希望可以给其他开发人员带来一点帮助,当然我这个只是看了一天的心得,与其他人员的相比应该是差很多了。但分享总比藏着好吧?
1、文档中Typecho::widget('Options') 错误,应当为:Typecho_Widget::widget('Widget_Options');
2、全局地址为:Typecho_Common::url('index.php', Typecho_Widget::widget('Widget_Options')->siteUrl) ,再与Router组合
3、Router,当前名称为:Typecho_Router::$current
4、针对内容做插件,需要在activate中加入:
Typecho_Plugin::factory('Widget_Abstract_Contents')->content = array('HelloWorld_Plugin', 'parse');
或
Typecho_Plugin::factory('Widget_Abstract_Contents')->contentEx = array('HelloWorld_Plugin', 'parse'); // contentEx好象是处理过的字符串。
5、针对摘要处理(摘要是用在列表中的),如同4一样,只是contentEx换 为excerptEx
由于4、5都没有官方说明,但是,在官方的插件示例中,采用的是contentEx,而且源码中,___content和___excerpt的最后return都是有Ex的版本。(这两个函数在入口时都是先对没有Ex的的变量作了处理,具体还是需要sluke的鉴定)
6、其实4、5的功能,都能算是代码植入吧,在后台页面中,更容易被植入,比如Typecho_Plugin::factory("admin/post.php")->content = array('classname','functionname'),你只需要把源文件打开,看看哪里有能够植入的类就行了。就象post.php和page.php中都有一个richEdit,就是专门等着别人为text这个textarea进行扩展的。
Tags: typecho, wordpress, sablog, 文档, 心得
PHP | 评论:0
| 阅读:25209