writing Typecho Plugin记录

PHP,Typecho 2020-02-19 60 次浏览 次点赞

前言

​ 自从我于2020-01-05(阳历)使用小马哥推荐的Typecho建立博客后(别看我建立博客之前还有文章发布,那都是之前写的随笔,被我从QQ空间的日志搬迁到了这里),就深深的被其精简化吸引,Typecho的插件也是多样,且能自主开发,能满足大部分人的需求,我感觉这样的社区氛围很赞。主题也一样,都可以自主开发,相比WordPress来说更加灵活,增加了使用者多元化选择的自由体验感。也就是最近几天,突然想要了解一下插件的实现,之前也粗略看过其他插件的code实现,但是当时不大了解Typecho的框架机制,所以也没看懂Plugin里的那些类接口到底干嘛的。今天心血来潮,整整试试吧,先弄个简单的,看看Plugin实现的coding流程...以后要什么效果如果可以的话,自己可以尝试写一下……

过程

​ 上午起来后花了一点时间把以前写的鼠标点击效果与图标跟随的代码翻了出来改了一下(以前用原生js写的,又给改成jQuery了),下午打开了几个我正在用的插件,想看看他们咋写的,应该就能推出那些接口干嘛的,参照了Typeho最初默认自带的那个Hello World插件,顺带打开了官方文档,因为Hello World 那个插件太简洁了,就完全是官方文档上介绍的那样。官方文档


先暂停,眼疼,明天再写,看了一天电脑了,吓人~先碎觉碎觉,狗命要紧~


主要是这部分:

2.注释

/**
 * Hello World
 *
 * @package HelloWorld
 * @author qining
 * @version 1.0.0
 * @link http://typecho.org
 */
  • Hello World: 插件描述
  • @package: 插件名称
  • @author: 插件作者
  • @version: 插件版本
  • @link: 插件作者链接

注释信息.png

因为他这个注释跟名称一样,所以说有点不明显,不过大体还是能看出来。

3.插件主体

/* 激活插件方法 */
public static function activate(){}

/* 禁用插件方法 */
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 render(){}
  • activate: 插件的激活接口,主要填写一些插件的初始化程序。
  • deactivate: 这个是插件的禁用接口,主要就是插件在禁用时对一些资源的释放。
  • config: 插件的配置面板,用于制作插件的标准配置菜单。
  • personalConfig: 个人用户的配置面板,基本用不到。
  • render: 自己定义的方法,用来实现插件要完成的功能。

然后就没啥太重要的了,插件需要的一些东西放在config()里,Hello World插件的active()方法里放了这一句:

Typecho_Plugin::factory('admin/menu.php')->navBar = array('HelloWorld_Plugin', 'render');

​ 等号前边是接口代码,官方文档说复制进来就行,激活了用户管理那个面板,等号后边则是插件要实现的方法,这段代码会在接口处运行(也可以说是后台也启用了插件效果)。

// 当前接口的赋值以数组形式出现
// HelloWorld_Plugin 插件的类名,一般是插件名加上“_Plugin”,其中类名还可以用__CLASS__,不过经常是直接把插件类名写上
// render 插件实现的方法名,后面插件实现方法的命名要与此一致(当然你也可以改,不过一般不会改)

​ 因为我不仅要启用navBar,还需要向页面中添加css格式和js脚本(还是那句话,最简单的东西,熟悉一下流程),所以我将headerfooter也一块启用了:

public static function activate()
    {
        Typecho_Plugin::factory('Widget_Archive')->header = array(__CLASS__, 'header');
        Typecho_Plugin::factory('Widget_Archive')->footer = array(__CLASS__, 'footer');
        Typecho_Plugin::factory('admin/menu.php')->navBar = array('Mou_Plugin', 'render');
    }

​ 因为上边官方文档说类名类名还可以用__CLASS__ ,然后我就没改,其实原理都一样,只要后边把实现方法激活一下就行。

对于这个函数:

public static function deactivate(){}

​ 一般是在禁用插件时,需要释放的资源,我也没搞明白这个资源指的是啥,有关数据库之类的?用到的很少,也不敢说,我是用不着嘿嘿...

关键下边这个函数:

 public static function config(Typecho_Widget_Helper_Form $form)
{
    $name = new Typecho_Widget_Helper_Form_Element_Text('word', NULL, 'Hello World', _t('说点什么'));
    $form->addInput($name);
}
$form->addInput($name);

这句则是把定义的变量写入到配置项中,以便后面使用。

这是Hello World插件里的,没有什么实际作用,就是在插件设置里给你展示了一个表单:

hello设置.png

Typecho_Widget_Helper_Form这个类里还有一些别的我们如果要做开关之类的会用的到,比如说:

//mouseFollow开关
$options = [
     'default' => _t('关闭'),
     'paper' => "<img src='{$mouseFollowImageDir}/paper.ico' alt='paper'>",
  ];
$followType = new Typecho_Widget_Helper_Form_Element_Radio('followType', $options, 'default', _t('follow样式,默认关闭'));
$form->addInput($followType);

​ 先拿我的举个例子,Typecho_Widget_Helper_Form_Element_Radio他们都是继承自Typecho_Widget_Helper_Form_Element这个类的,这个父类的构造方法:

/**
     * 构造函数
     *
     * @access public
     * @param string $name 表单输入项名称
     * @param array $options 选择项
     * @param mixed $value 表单默认值
     * @param string $label 表单标题
     * @param string $description 表单描述
     * @return void
     */
public function __construct($name = NULL, array $options = NULL, $value = NULL, $label = NULL, $description = NULL)
{
    #code...
}

​ 我们可以看到第二个options行为参数需要传递一个数组,正好对应了选择项,$value可以填默认值,这样就可以给用户自己选择启不启用某一项功能,第一个参数和后边两个就好看了。

因为我在当前类里定义了一个常量

const STATIC_DIR = '/usr/plugins/Mou/static';

所以在config里再定义一下路径用来访问那个图标:

(这里和Helper::options()->pluginUrl这个方法效果一样,所以就没用着那个常量)

$dir = self::STATIC_DIR;
$staticDir = Helper::options()->pluginUrl . '/Mou/static/';
$mouseFollowImageDir = $staticDir . 'images';

这里的实现效果就是在设置的面板上显示出选项的图标:

图表显示.png

这里的路径要好好看看,如果显示不出来的话,用调试工具看看路径是不是多了或者少了一个/

之后便是人用户的配置方法:

/* 个人用户的配置方法 */
public static function personalConfig(Typecho_Widget_Helper_Form $form){}

不过在观察了几个插件的代码后发现基本都空着,所以我也空着了,上网查了一下:

public static function personalConfig(Typecho_Widget_Helper_Form $form) 插件的个性化配置面板。用法暂时还不明,有待Hanny进一步研究。

​ 我还是有空去看看源码,回头找着再补上吧~~话说回来,相关的官方文档太少了,网上搜出来的大多是一些个人博客里的东西。

接下来render

public static function render()
    {
        echo '<span class="message success">'
            . htmlspecialchars(Typecho_Widget::widget('Widget_Options')->plugin('HelloWorld')->word)
            . '</span>';
    }

函数里的内容便是要显示已经自定义好的欢迎话语。

Typecho_Widget::widget('Widget_Options')->plugin('HelloWorld')->word这句是调用插件配置项的,上边的配置项里有条这个:

$name = new Typecho_Widget_Helper_Form_Element_Text('word', NULL, 'Hello World', _t('说点什么'));
$form->addInput($name);

这里便是通过类的一些方法Typecho_Widget::widget('Widget_Options')->plugin('HelloWorld')->word提了出来。

​ 如果你仅仅是向页面中添加一点js脚本和css样式表,在你激活的herderfooter填写实现逻辑就行,可能有人会说就添加点cssjs而且,用得着小题大做弄个Plugin吗?我的回答还是一样,

  • 第一:熟悉一下Typecho Plugin的大体coding 流程
  • 第二:Typeho以其精简化深受光大用户的喜爱,我是完全可以直接在源码中扔进去cssjs但是这样对于Typecho日后升级更新啥的可能会影响到实现效果。这里的一个博主与我观点一致。

其原文内容如下(翻了半天历史记录才找着,汗~):

其实类似这种炫酷的鼠标插件一般借助CSSJS代码就可以实现,不过明月还是喜欢使用插件,毕竟插件实现的更稳定,兼容性也有保障,无论是升级Typecho还是主题都不会有太大的影响。

 /**
     *为header添加css文件
     * @return void
     */
    public static function header()
    {
        $StaticCssUrl = Helper::options()->pluginUrl . '/Mou/static/css/';
        echo '<link rel="stylesheet" href=" ' . $StaticCssUrl . 'style.css"/>';
    }

    /**
     *为footer添加js文件
     * @return void
     */
    public static function footer()
    {
        $dir = self::STATIC_DIR;
        $StaticJsUrl = Helper::options()->pluginUrl . '/Mou/static/js/';
        $followType = Typecho_Widget::widget('Widget_Options')->plugin('Mou')->followType;
        echo '<script type="text/javascript" src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js"></script>';
        $mouseFollowImageDir = $dir . '/images';
        // 调试部分
        // $test = 'test';
        // echo '<script type="text/javascript" src="' . $followType . $test . 'main.js"></script>';
        self::handleFollowType($followType);
        echo '<script type="text/javascript" src="' . $StaticJsUrl . 'main.js"></script>';
    }

这里看到还调用了一个函数handleFollowType()

/*mouseFollowStyle*/
    private static function handleFollowType($followType)
    {
        if ($followType != 'default') {
            $followTypeImage = $followType . '.ico';
            $dir = self::STATIC_DIR;
            $mouseFollowImagesDir = self::STATIC_DIR . '/images';
            $js .= '<script>';
            $js .= <<<JS
            $(document).ready(function () {
                $('body').append('<div id="followMouseContent"><img src="{$mouseFollowImagesDir}/{$followTypeImage}" alt="followMouse"></div>');
                $(document).mousemove(function (e) {
                var x = e.pageX;
                var y = e.pageY;
                var mms = document.getElementById('followMouseContent');
                var cx = parseInt(x) - parseInt(80);
                var cy = parseInt(y) + parseInt(10);
                mms.style.left = cx + "px";
                mms.style.top = cy + "px";
                });
            });
JS;
            $js .= '</script>';
            echo $js;
        } else {
            echo '';
        }
    }

这里主要是看看你的选择啦,要是不启用就没啥事了,启用的话判断一下你启用的图标。

这里调用的时候还犯了一个语法错误,用了$this->handleFollowType($followType)

PHP$thisself的区别 this是在实例化的时候来确定指向谁。 ... self是指向类本身,也就是self是不指向任何已经实例化的对象,一般self使用来指向类中的静态变量。

我怕不是个憨批……

现在流程说完了,要去后台启用看看生不生效。

​ 一般是没啥子问题,我这里却掉坑去了,其一便是上边那个self$this用混了,其二便是一个非常低级的错误……我写完后放在服务器的plugins文件夹下,启用没问题,但是那个图标选择却没生效,我又看了看逻辑没错啊,起初也是摸索着写,以为接口的一些参数东西还没整好,然后一顿查一顿看源码,没错啊肿末回事?反正做了一堆跟问题不搭边的事,还又把自己写的东西搞得一团乱麻,最后借助调试工具看了一下$followType = Typecho_Widget::widget('Widget_Options')->plugin('Mou')->followType;的传值,也没错啊,传的正确,但为何不顶用哪?条件判断也没错啊!不就是=某一项的时候选择哪一项嘛,咋还默认不启用了呢?WDFK!!!看出来了吧,我用的=来判断相不相等……赤果果的憨批一个真是心里上万只草泥马奔腾而过……这倒又让我想起来了前些日子练习Python爬虫,爬了猫眼电影的Top100榜,这个我也是醉了……我当时写了:

pattern = re.compile('<dd>.*?board-index.*?>(\d+)</i>.*?data-src="(.*?)".*?name"><a'
                         +'.*?>(.*?)</a>.*?star">(.*?)</p>.*?releasetime">(.*?)</p>'
                         +'.*?integer">(.*?)</i>.*?fraction">(.*?)</i>.*?</dd>', re.S)
items = re.findall(pattern, html)
for item in items:
    yield {
      'index': item[0],
      'image': item[1],
      'title': item[2],
      'actor': item[3].strip()[3:],
      'time': item[4].strip()[5:],
      'score': item[5]+item[6]
   }

​ 但是给我返回的 items 一直为空?嘛情况呀这是?正则语法错了?一查没嘚问题呀,而且错了会报错的吧,漏了条件?呃?不用 ^ 来标注起始呀……然后我又找了别的网站的源码,试着用正则提取一下,能提取出来也没问题啊,然后我居然去查了半天 compilefindall 函数……沃日,最后再重新一看那个源码:

<dd>
    <i class="board-index board-index-1">1</i>
    <a href="/films/1203" title="霸王别姬" class="image-link" data-act="boarditem-click" data-val="{movieId:1203}">
      <img src="//s3plus.meituan.net/v1/mss_e2821d7f0cfe4ac1bf9202ecf9590e67/cdn-prod/file:5788b470/image/loading_2.e3d934bf.png" alt="" class="poster-default">
      <img alt="霸王别姬" class="board-img" src="https://p0.meituan.net/movie/ce4da3e[email protected]_220h_1e_1c">
    </a>
    <div class="board-item-main">
      <div class="board-item-content">
              <div class="movie-item-info">
        <p class="name"><a href="/films/1203" title="霸王别姬" data-act="boarditem-click" data-val="{movieId:1203}">霸王别姬</a></p>
        <p class="star">
                主演:张国荣,张丰毅,巩俐
        </p>
<p class="releasetime">上映时间:1993-07-26</p>    </div>
    <div class="movie-item-number score-num">
<p class="score"><i class="integer">9.</i><i class="fraction">5</i></p>        
    </div>

      </div>
    </div>

</dd>

​ 我又吐了……人家的标签是嘛?是 <i>标签,我开始写的啥?是 <li> 标签……一开始就错了,能给你返回结果,骚年做梦去吧……一些简单的问题恰巧是一些人经常忽视的,有些你眼中的 Bug 可能就是哪个细节没做到位,而且不易察觉,因为大多数人会认为这么简单的东西没问题,恰巧这些忽视的就是问题所在。

​ 现在这个极为简单的插件算是基本完工,还有一些东西需要完善,比如多几个跟随图标的选择,字体这一块暂时是固定死的,用了字珠压缩字体包提高一下速度,后续也会像跟随图标一样可供选择,其文字和字体的选择后续会安排上……还有一个毛病就是弹出的字体是用的 <span> 标签,开始以为会对博客中的一些标签产生影响(我觉得id冲突了),可以通过创建新的标签 tag 进行解决(HTML5)刚才一看用的通配符选择器,(lll¬ω¬)我的我的~然后我看着本地的图标跟随很流畅如德芙巧克力般纵享丝滑然后博客里一启用咋感觉这么僵硬~~陷入沉思……

该看网课了,已经落下三天了,再不看看说不过去……这个等着以后再进行完善吧(●ˇ∀ˇ●)

git插件地址


本文由 fmujie 创作,采用 知识共享署名 3.0,可自由转载、引用,但需署名作者且注明文章出处。

2 条评论

  1. sem2333
    sem2333

    老哥 文章不错 就是想知道你这字体咋整

    1. fmujie
      fmujie

      兄弟你问的是代码块里的字体吗?这个是插件的字体 火云字体 CSS没有更新,所以样式标签名冲突了,呜呜~~~(我在服务器里vim看了一下css样式文件,显示是更新了,但不知道客户端为何会这样)

添加新评论

召唤看板娘