易栈网-膘叔

浏览模式: 标准 | 列表 | 图文
typecho 插件开发(一)

typecho 插件开发(一)

看typecho第二天了,不过我还是没有仔细查看源码,只是根据一些其他插件的应用来写自己的应用。

比如,看了Akismet插件后,知道了 Typecho_Widget_Helper_Form 对象还有一个 addRule 方法,用来对数据进行验证。

昨天看了一下源码,并对应我昨天的方法了解Typecho页面中可以被注入对象的地方,在插件的activate中写了两个方法,可以让插件在全局被应用。

Typecho_Plugin::factory('index.php')->begin = array('DbBackup_Plugin', 'backup');
Typecho_Plugin::factory('admin/common.php')->begin = array('DbBackup_Plugin', 'backup');

嗯。是准备考虑备份的。前后台都有。正在尝试。如果并未按照我预想的情况运行的话,可能只考虑前台触发了。

加了三个参数:

$rate = new Typecho_Widget_Helper_Form_Element_Text('rate', NULL, '1', _t('备份频率'),_t('备份数据的频率(最小单位为天,请使用整数)'));
$form->addInput($rate->addRule('isInteger',_t('请输入整数')));
$email = new Typecho_Widget_Helper_Form_Element_Text('email', NULL, '', _t('备份发送到'),_t('发送邮件地址。'));
$form->addInput($email->addRule('email',_t('请输入正确的邮箱地址')));
$lastbackup = new Typecho_Widget_Helper_Form_Element_Text('lastbackup', NULL, '', _t('上次备份时间'),_t('无需设置,由程序自动生成'));
$form->addInput($lastbackup);

但是isInteger,这个验证方法是验证is_numeric,其实我是希望是一个正整数。现在,先将就一下下喽。离完成还早,顺便附上两个昨天写的垃圾插件,敬请测试(主要是官方论坛我注册后收不到邮件,所以无法登录。过会我再换个邮箱试试)

文章分页:已经删除,请移步typecho 插件:内容分页SplitArchivePage 进行下载 【演示地址】

高亮搜索引擎来源关键字:295c8a9efc10a0f0499ca6bca62048a8 【这个,在这现在这个博客上测试成功,但是在typecho上没有办法测试,因为没有数据,google搜索不到。。。】

了解Typecho页面中可以被注入对象的地方

了解Typecho页面中可以被注入对象的地方

关于插件这个问题,可以查看这篇JianHua Zhang的Typecho阅读笔记三:插件机制,我想说的就是链接文章里的最后一句。
在Typecho_Plugin的__call魔术方法里把当前设置到的component全部打印在页面上,对于插件开发人员来说是一件非常好的事情,因为这样,你可以了解到,有哪些地方是你可以注入你的插件对象的。

0ca6220932144e8040b271afe2282991

看左上角(看不清的话,请点击看大图)。

Widget_Archive:select
Widget_Archive:handleInit

象这两个,代表了你在插件中就可以写上以下代码来进行注入。

Typecho_Plugin::factory("Widget_Archive")->select = array("插件类名","该类中的方法");

是不是很方便 ?打开每一个页面,你都可以看到类似的代码,有logo,有content,contentEx之类的。了解了这些,在相应的位置上写上你想要的效果,是不是很爽?

顺便,你需要查看一下http://docs.typecho.org/develop/widget。也可以了解一些代码是如何被调用(文中有一点点错误,在看的时候Typecho::widget,应该是Typecho_Widget::widget("xxx"))

JianHua Zhang的Typecho阅读笔记三:插件机制

JianHua Zhang的Typecho阅读笔记三:插件机制

文章来自: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

$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。了解了这一点,你可以根据你想要的效果为各个地方加上你的代码。


JianHua Zhang的Typecho阅读笔记二:数据库访问

JianHua Zhang的Typecho阅读笔记二:数据库访问

文章来自:http://blog.csdn.net/jh_zzz/archive/2010/01/11/5173851.aspx,由于我也正在看 typecho这个玩意所以,就记录下来。虽然我一天下来,也看了不少代码,但毕竟没有深读过。对于流程啥的,还没有开始关心,只是为了写而写。

第二篇:数据库访问

这一块比较复杂,我还没有完全理解为什么要把 SQL 语句的组装搞这么复杂。

从一个普通皮肤页面开始 themes\default\index.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()

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 中就有了一行行的数据了。

JianHua Zhang的Typecho阅读笔记一:页面渲染及路由机制

JianHua Zhang的Typecho阅读笔记一:页面渲染及路由机制

文章来自: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

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 。

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();

typecho 一天下来的心得

自从评论里有人推荐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进行扩展的。

jQuery的事件绑定命名空间

jQuery的bind的函数在实际应用中用的不是特别多,只是他可以绑定一个事件,但不会即时触发,也可以通过unbind来解除绑定。在没有看到这篇文章之前,我一直不知道原来bind也可以有命名空间。事实上,我看完这篇文章后,再去翻了一下手册,也才发现了一点点的注释。但手册也仅仅是一句话就带过去了。没有过多的深究,或许他认为命名空间这玩意很简单,没有必要多解释?

先看手册,由于bind方法有三个参数(type,[data],fn),所以手册上这么介绍:

.bind() 方法是用于往文档上附加行为的主要方式。所有JavaScript事件对象,比如focus, mouseover, 和 resize,都是可以作为type参数传递进来的。

jQuery还提供了一些绑定这些标准事件类型的简单方式,比如.click()用于简化.bind('click')。一共有以下这些:blur, focus, focusin, focusout, load, resize, scroll, unload, click, dblclick, mousedown, mouseup, mousemove, mouseover, mouseout, mouseenter, mouseleave, change, select, submit, keydown, keypress, keyup, error 。

任何作为type参数的字符串都是合法的;如果一个字符串不是原生的JavaScript事件名,那么这个事件处理函数会绑定到一个自定义事件上。这些自定义事件绝对不会由浏览器触发,但可以通过使用.trigger()或者.triggerHandler()在其他代码中手动触发。

如果type参数的字符串中包含一个点(.)字符,那么这个事件就看做是有命名空间的了。这个点字符就用来分隔事件和他的命名空间。举例来说,如果执行 .bind('click.name', handler) ,那么字符串中的 click 是事件类型,而字符串 name 就是命名空间。命名空间允许我们取消绑定或者触发一些特定类型的事件,而不用触发别的事件。参考unbind()来获取更多信息。

当一个事件传到一个元素上,所有绑定在上面的针对哪个事件的处理函数都会触发。如果注册了多个事件处理函数,总是按照绑定的顺序依次触发。当所有绑定的事件处理函数执行完毕后,事件继续沿着普通的事件冒泡途径上浮。
Zeal这么理解,并解析了上面的一段:
jQuery的 bind / unbind 方法应该说使用很简单,而且大多数时候可能并不会用到,取而代之的是直接用 click / keydown 之类的事件名风格的方法来做事件绑定操作。

但假设如下情况:需要在运行时根据用户交互的结果进行不同click事件处理逻辑的绑定,因而理论上会无数次对某一个事件进行 bind / unbind 操作。但又希望 unbind 的时候只把自己绑上去的处理逻辑给释放掉而不是所有其他地方有可能的额外的同一事件绑定逻辑。

这时候如果直接用 .click() / .bind('click') 加上 .unbind('click') 来进行重复绑定的话,被 unbind 掉的将是所有绑定在元素上的 click 处理逻辑,潜在会影响到该元素其他第三方的行为。当然如果在bind的时候是显示定义了function变量的话,可以在unbind的时候提供 function作为第二个参数来指定只unbind其中一个处理逻辑,但实际应用中很可能会碰到各种进行匿名函数绑定的情况。

对于这种问题,jQuery的解决方案是使用事件绑定的命名空间。即在事件名称后添加 .something 来区分自己这部分行为逻辑范围。

比如用 .bind('click.myCustomRoutine',function(){...}); 同样是把匿名函数绑定到 click 事件(你可以用自己的命名空间多次绑定不同的行为方法上去),当unbind的时候用 .unbind('click.myCustomRoutine') 即可释放所有绑定到 .myCustomRoutine 命名空间的 click 事件,而不会解除其他通过 .bind('click') 或另外的命名空间所绑定的事件行为。

同时,使用命令空间还可以让你一次性 unbind 所有此命名空间下的自定义事件绑定,通过 .unbind('.myCustomRoutine') 即可。

要注意的是,jQuery的命名空间并不支持多级空间。因为在jQuery里面,如果用 .unbind('click.myCustomRoutine.myCustomSubone') ,解除的是命名空间分别为 myCustomRoutine 和 myCustomSubone 的两个并列命名空间下的所有 click 事件,而不是 "myCustomRoutine 下的 myCustomSubone 子空间"。

如此看来,命名空间还是有点用的。只是不知道会有多少人用得上这个功能(不开发插件的人,估计永远用不到吧?)我也是仅作一下笔记而已

sablog 1.6小bug

sablog 1.6小bug

有几次遇到这个bug了。不过。说来也不算太大的BUG
重现:发表博客,选择附件。
OK,你发现附件选错了,然后删除,重选附件,并插入内容,这时候应该是 localfile=2 了。

这时候发表后,你会发现附件并没有被正确的在内容中替换。而是当成附件了。

让我们重试一下吧:
[localfile=2]

好文推荐:五款常用mysql slow log分析工具的比较

一直在用MYSQL,因此对于慢查询就非常在意,虽然自己写代码的时候不是特别注意,但真正上线后还是要关注关注的。

最简单的看LOG就是用phpmyadmin查看一下,但这终究不是办法。所幸,工具还是很多的。javaeye上就有人贴出了工具和使用方法 :

启用 slow log

有两种启用方式:
1, 在my.cnf 里 通过 log-slow-queries[=file_name]
2, 在mysqld进程启动时,指定--log-slow-queries[=file_name]选项

比较的五款常用工具

mysqldumpslow, mysqlsla, myprofi, mysql-explain-slow-log, mysqllogfilter

mysqldumpslow, mysql官方提供的慢查询日志分析工具. 输出图表如下:
2f3d2ecd6b54a43d6de761f53d428f79
主要功能是, 统计不同慢sql的
出现次数(Count),
执行最长时间(Time),
累计总耗费时间(Time),
等待锁的时间(Lock),
发送给客户端的行总数(Rows),
扫描的行总数(Rows),
用户以及sql语句本身(抽象了一下格式, 比如 limit 1, 20 用 limit N,N 表示).

mysqlsla, hackmysql.com推出的一款日志分析工具(该网站还维护了 mysqlreport, mysqlidxchk 等比较实用的mysql工具)
a8abda30154d1c8c31ffc0ec4a4d2cc1
整体来说, 功能非常强大. 数据报表,非常有利于分析慢查询的原因, 包括执行频率, 数据量, 查询消耗等.
格式说明如下:
总查询次数 (queries total), 去重后的sql数量 (unique)
输出报表的内容排序(sorted by)
最重大的慢sql统计信息, 包括 平均执行时间, 等待锁时间, 结果行的总数, 扫描的行总数.
Count, sql的执行次数及占总的slow log数量的百分比.
Time, 执行时间, 包括总时间, 平均时间, 最小, 最大时间, 时间占到总慢sql时间的百分比.
95% of Time, 去除最快和最慢的sql, 覆盖率占95%的sql的执行时间.
Lock Time, 等待锁的时间.
95% of Lock , 95%的慢sql等待锁时间.
Rows sent, 结果行统计数量, 包括平均, 最小, 最大数量.
Rows examined, 扫描的行数量.
Database, 属于哪个数据库
Users, 哪个用户,IP, 占到所有用户执行的sql百分比
Query abstract, 抽象后的sql语句
Query sample, sql语句
除了以上的输出, 官方还提供了很多定制化参数, 是一款不可多得的好工具.
mysql-explain-slow-log, 德国人写的一个perl脚本.
http://www.willamowius.de/mysql-tools.html
jquery-easyui-1.1-demo-修正.rar
566f427f455d86c2965dfde40e3a6d52
功能上有点瑕疵, 不仅把所有的 slow log 打印到屏幕上, 而且统计也只有数量而已. 不推荐使用.
mysql-log-filter, google code上找到的一个分析工具.提供了 python 和 php 两种可执行的脚本.
http://code.google.com/p/mysql-log-filter/
5b4f0aa88794991e8ad8804b4039b6d3
功能上比官方的mysqldumpslow, 多了查询时间的统计信息(平均,最大, 累计), 其他功能都与 mysqldumpslow类似.
特色功能除了统计信息外, 还针对输出内容做了排版和格式化, 保证整体输出的简洁. 喜欢简洁报表的朋友, 推荐使用一下.
myprofi, 纯php写的一个开源分析工具.项目在 sourceforge 上.
http://myprofi.sourceforge.net/
bb63e990e24b9dc80dc528521a551453
功能上, 列出了总的慢查询次数和类型, 去重后的sql语句, 执行次数及其占总的slow log数量的百分比.
从整体输出样式来看, 比mysql-log-filter还要简洁. 省去了很多不必要的内容. 对于只想看sql语句及执行次数的用户来说, 比较推荐.

总结

工具/功能 一般统计信息 高级统计信息 脚本 优势
mysqldumpslow 支持 不支持 perl mysql官方自带
mysqlsla 支持 支持 perl 功能强大,数据报表齐全,定制化能力强.
mysql-explain-slow-log 支持 不支持 perl 无
mysql-log-filter 支持 部分支持 python or php 不失功能的前提下,保持输出简洁
myprofi 支持 不支持 php 非常精简
---EOF---
作者:galaxystar,来自:http://www.javaeye.com/topic/242516,他现在的博客是:http://kenwublog.com/。
犀利哥上幸福魔方了。。。

犀利哥上幸福魔方了。。。

准备看。先标题党一下。。。
只是觉得这样一直在炒作,好象对于犀利哥不太好。唉。不管了,先看电视,等会评论。

--继续--

还是只看了一会,不忍看完。(看看时间好象不对,原来,老婆看的是昨天节目的回看。。。,意思也就是网上已有相应评论了吧)

犀利哥的事情相对于其他很多事情来说也算是挺幸福的了,最起码,他还活着,而且还有孩子,有父母。只是媒体老是喜欢拿他的事情出来炒作,说是帮助,其实也是博眼球吧?

看他们一家子上节目,头没有一个抬得高的,不自信、内向,看来是他们一家的传统了,也确实,媒体很强势,不低头怎么行?

不想多说啥,希望犀利哥能够走好,祝幸福吧。不要成为娱乐圈的娱乐分子(哦,是被娱乐分子)就好