Typecho插件开发教程1:登录界面美化
本文以登录界面美化为例来介绍Typecho
插件开发。之所以选择这个插件,是因为它简单且比较有趣,适合入门学习。
开发环境搭建
工欲善其事,必先利其器。搭建好开发环境对于后续进行插件开发来说是很重要的, 有个好用的 IDE 能有效提升开发效率。这里推荐使用 JetBrains 家的 PhpStorm
,配合XDebug
插件,来作为我们的开发环境。
PhpStorm
的安装,省略。如果有edu邮箱可以免费使用,其余的自行解决。官网:PhpStorm: The Lightning-Smart IDE for PHP Programming by JetBrains
XDebug
的安装,参考官网:Xdebug: Documentation » Installation。
我的是Linux环境,安装配置总体上还是比较简单的。php.ini
里配置xdebug
(仅供参考,对于Windows,zend_extension
那一行的配置肯定不一样):
[xdebug]
zend_extension=/usr/lib/php/modules/xdebug.so
xdebug.remote_enable = 1
xdebug.remote_handler = dbgp
xdebug.remote_mode = req
xdebug.remote_host = localhost
xdebug.remote_port = 9000
xdebug.idekey = PHPSTORM
之后可以在PhpStorm
的设置里的 PHP > Debug 那里验证一下,确认端口号和上面配置的一样(默认:9000),验证全部通过就OK了:
IDE 拥有代码补全、智能提示、断点调试等功能,在开发过程中还是非常实用的。
插件接口
给插件取个名字,假设叫LoginBeautify
吧,名字注意不能有_
。然后在Typecho
的插件目录创建一个和插件名一致的文件夹(也就是LoginBeautify
),在新建的这个文件夹下创建文件Plugin.php
。这个是Typecho
的约定,Typecho
会自动扫描插件目录,解析目录下的Plugin.php
。
在Plugin.php
内键入以下内容,主要是插件的描述信息,填写一下就行:
<?php
if (!defined('__TYPECHO_ROOT_DIR__')) exit;
/**
* 登录界面美化
*
* @package LoginBeautify
* @author jlice
* @version 1.0.0
* @link https://muranxuan.top
*/
class LoginBeautify_Plugin implements Typecho_Plugin_Interface
{
}
这里需要注意两点:
- 插件的类名必须是
XXX_Plugin
(其中XXX
为插件的名字),否则Typecho
无法正确加载插件。 - 必须实现
Typecho_Plugin_Interface
接口,也就是要implements Typecho_Plugin_Interface
Typecho_Plugin_Interface
有一些方法需要我们实现。如果像上面这样空着,会报错,此时在红线上按Alt+Enter快捷键,然后会提示 Add method stubs,然后按Enter就会自动生成相应的方法代码了,还有默认的注释。
有4个方法,其含义很好理解。activate
和deactivate
分别是启用和禁用插件时运行的代码,config
和personalConfig
分别是插件的配置面板和用户的配置面板(也就是在个人设置那里出现)。
需要注意的是,上面这些方法都是静态的(有static
修饰),粗糙点说就是不能用$this
。
钩子
插件是为了便于扩展程序的功能,Typecho
在很多地方预留了钩子,供插件挂载。这个要怎么理解呢?结合钩子的图片:
假如Typecho
的作者觉得某个地方可以交给插件去完成,可以在这个地方放一个钩子,然后插件挂载到这个钩子上(有点像把东西挂在挂钩上),程序在运行时,会检查钩子上有没有挂载插件,如果有会执行插件的代码。
通过上面的理解,我们可以知道,插件要声明注册到哪个钩子,执行什么代码,这样Typecho
才能知道何时执行以及执行什么代码。当然,插件也可以挂载到多个钩子。实际上,Typecho
在启用或禁用插件时就会把插件的数据保存到数据库,位于options
表里。
那么,Typecho
预留了哪些钩子呢?
Typecho
预留的钩子以两种形式出现:
Typecho_Plugin::factory()->function();
$this->pluginHandle()->function();
如果要看所有的钩子,可以参考官方文档,或者在代码里搜索。选择网站的根目录,然后按Ctrl+Shift+F搜索:
找到合适的钩子
钩子那么多,怎么能知道哪个钩子符合自己的需求呢?这个问题,em...,其实不太好回答。
我们注意到两个目录:一个是var/Widget/
,这里是Typecho
的核心代码,里面有Typecho
定义的各种Widget
;另一个是admin/
,这里是后台管理的代码。
Widget
是组件的意思,例如Widget_Archive
就是输出文章的,例如首页和文章页面。Widget
里的钩子可以定制这些组件的行为。具体有哪些组件可以参考官方文档,或者直接看var/Widget/
这个目录就可以了。
钩子的位置有两个层面的意思,或者说为了指定要挂载哪个钩子,我们需要确定两件事:一是这个钩子在哪,对于admin/
下的,需要指定.php
文件位置,对于var/Widget/
里的钩子,只需要指明类名即可(也就是Widget
的名字);二是这个钩子的名字,也就是function
的名字。
下面是一些例子:
// var/Widget/Upload.php
$result = Typecho_Plugin::factory('Widget_Upload')->trigger($hasUploaded)->uploadHandle($file);
// Widget: Widget_Upload function: uploadHandle
// var/Widget/Contents/Post/Edit.php
$this->pluginHandle()->finishPublish($contents, $this);
// Widget: Widget_Contents_Post_Edit function: finishPublish
// admin/write-js.php
Typecho_Plugin::factory('admin/write-js.php')->write();
// php: admin/write-js.php function: write
需要注意的是第一个例子里的trigger
并不是钩子的名字,这个是Typecho
用来判断是否有插件挂载到这里用的。
由于我们要做登录界面美化,也就是改登录界面背景,很自然地想到后台管理的登录界面,也就是admin/login.php
。然而,这个文件里并没有提 供钩子。但是,我们注意到,在这个文件的最后引入了footer.php
:
<?php
include 'footer.php';
>
按住Ctrl键点击footer.php
,可以跳转到footer.php
,而footer.php
里有一个钩子:
/** 注册一个结束插件 */
Typecho_Plugin::factory('admin/footer.php')->end();
于是,我们在插件的activate
方法里挂载这个钩子:
public static function activate()
{
Typecho_Plugin::factory('admin/footer.php')->end = array('LoginBeautify_Plugin', 'end');
}
我们把LoginBeautify_Plugin
这个类(也就是本插件的类名)的end
方法挂载到这个钩子上。不过,我们的LoginBeautify_Plugin
类并没有end
方法,因此需要添加一个end
方法。现在LoginBeautify_Plugin
类的代码如下(省略了注释):
<?php
if (!defined('__TYPECHO_ROOT_DIR__')) exit;
class LoginBeautify_Plugin implements Typecho_Plugin_Interface
{
public static function activate()
{
Typecho_Plugin::factory('admin/footer.php')->end = array('LoginBeautify_Plugin', 'end');
}
public static function deactivate(){}
public static function config(Typecho_Widget_Helper_Form $form){}
public static function personalConfig(Typecho_Widget_Helper_Form $form){}
public static function end()
{
}
}
由于钩子end
没有输入参数,所以我们这里增加的end
方法也没有输入参数。如果钩子有输入参 数,我们对应增加的方法里应该有对应的输入参数。
使用Widget
其实后台管理的界面都引入了footer.php
,因此,我们需要判断用户的登录状态,当用户没有登录时才修改背景,否则就会影响到后台管理的其它界面。
我们注意到login.php
的最开始有如下代码:
<?php
include 'common.php';
if ($user->hasLogin()) {
$response->redirect($options->adminUrl);
}
这几行代码的意思是:引入common.php
,如果用户已经登录了,就跳转到管理地址。可是,$user
、$response
和$options
这些变量哪来的呢?
按住Ctrl键点击common.php
,可以跳转到common.php
。可以发现如下代码:
Typecho_Widget::widget('Widget_Options')->to($options);
Typecho_Widget::widget('Widget_User')->to($user);
Typecho_Widget::widget('Widget_Security')->to($security);
Typecho_Widget::widget('Widget_Menu')->to($menu);
Typecho_Widget::widget
是一个工厂方法,可以返回类的实例。to()
方法可以把这个实例赋值给某个变量。
因此,在我们的插件里页可以采用类似的做法(图片地址替换成自己的哦):
public static function end()
{
Typecho_Widget::widget('Widget_User')->to($user);
if (!$user->hasLogin()) {
?> <script>$("body").css("background", "url('https://muranxuan.top/usr/uploads/demo.jpg')");</script> <?php
}
}
当用户没有登录时,我们需要做点事情。这里,我们通过jQuery
来修改页面的背景。效果如下:
表单配置
目前,页面的背景是写死在代码里的,不支持配置。所以,我们希望插件能配置页面背景的 URL。为此,在config
方法里,增加背景 URL 的输入框:
public static function config(Typecho_Widget_Helper_Form $form)
{
$url = new Typecho_Widget_Helper_Form_Element_Text('bgUrl', NULL, NULL, _t("背景URL"));
$form->addInput($url);
}
Typecho_Widget_Helper_Form_Element_Text
可以用来创建一个文本框,有5个参数:表单输入项名称,选择项,默认值,标题,描述。
在end
方法里,我们将原先写死的 URL 换成表单里bgUrl
的值(注意写法,是上面第1个参数bgUrl
哦,不是=
左边的$url
):
public static function end()
{
Typecho_Widget::widget('Widget_User')->to($user);
$url = Typecho_Widget::widget('Widget_Options')->plugin('LoginBeautify')->bgUrl;
if (!$user->hasLogin()) {
?> <script>$("body").css("background", "url(<?= $url ?>)");</script> <?php
}
}
这样,我们就可以在插件配置界面去设置背景 URL 了。
如果出现下面的错误,把插件禁用再启用就可以了:
PHP模板语法
也许对于不熟悉 PHP 的人来说,前面的代码里,用户没有登录时设置背景这一行可能看得不太懂:
> <script>$("body").css("background", "url(<?= $url ?>)");</script> <?php
首先,?>
是 PHP 的结束标签,表示 PHP 代码告一段落了。后面接着是输出的 HTML 了。在 HTML 里,我们写了一小段 JavaScript 代码,用来设置背景图片的 URL。为了能在 HTML 里拼接 PHP 里的变量,使用<?= ?>
这样的模板语法,这样我们就把从插件配置里获取的背景图片 URL 插入到了这里。最后是<?php
表示又开始是 PHP 代码了。
貌似有点绕?我们也可以用纯 PHP 做这件事,这一行代码可以替换为:
echo '<script>$("body").css("background", "url(' . $url . ')");</script>';
其中,.
是 PHP 里字符串拼接符,利用 PHP 拼接出上面的 HTML 内容,然后利用echo
输出到页面。
下面是本插件的全部代码,便于学习:
<?php
if (!defined('__TYPECHO_ROOT_DIR__')) exit;
/**
* 登录界面美化
*
* @package LoginBeautify
* @author jlice
* @version 1.0.0
* @link https://muranxuan.top
*/
class LoginBeautify_Plugin implements Typecho_Plugin_Interface
{
public static function activate()
{
Typecho_Plugin::factory('admin/footer.php')->end = array('LoginBeautify_Plugin', 'end');
}
public static function deactivate() {}
public static function config(Typecho_Widget_Helper_Form $form)
{
$url = new Typecho_Widget_Helper_Form_Element_Text(_t('bgUrl'), NULL, NULL, _t("背景URL"));
$form->addInput($url);
}
public static function personalConfig(Typecho_Widget_Helper_Form $form) {}
public static function end()
{
Typecho_Widget::widget('Widget_User')->to($user);
$url = Typecho_Widget::widget('Widget_Options')->plugin('LoginBeautify')->bgUrl;
if (!$user->hasLogin()) {
echo '<script>$("body").css("background", "url(' . $url . ')");</script>';
}
}
}
由于本期是Typecho
插件教程的第1期,所以写得非常的基础,应该很容易看懂吧(我自己是这么觉得的)。
如果需要更好的登录界面美化,可以参考这个插件:Typecho登录/注册美化插件 - Typecho爱好者博客
好了,本期的插件教程就到这里了,你学会了么?赶紧动手试试吧!