摘要:关于typecho制作qq卡片时的问题,以及typecho模板中functions.php何时载入,还有$this变量的探究

引子:在写typecho的制作qq卡片时,发现会报一些奇怪的问题,错误如下:

Call to a member function siteurl() on null
Error: Call to a member function siteurl() on null in /www/usr/themes/initial-master/functions.php:242
Stack trace:
#0 /www/usr/themes/initial-master/header.php(21): qqpostThumb(Object(Widget_Archive))
#1 /www/var/Widget/Archive.php(1935): require('/www/usr/themes...')
#2 /www/usr/themes/initial-master/post.php(2): Widget_Archive->need('header.php')
#3 /www/var/Widget/Archive.php(2020): require_once('/www/usr/themes...')
#4 /www/var/Typecho/Router.php(135): Widget_Archive->render()
#5 /www/index.php(21): Typecho_Router::dispatch()
#6 {main}</code></pre>

错误代码是在header.php中添加了qqpostThumb2($this),当时我是想把$this传进去,然后在functions.php里调用,

//functions.php中的代码大意
function qqpostTumb2($a)
{
$b=$a->options->siteurl()
}

很诡异但是如果添加qqpostThumb2($this->options),把$this传进去,然后在functions.php里调用,

//functions.php中的代码大意
function qqpostTumb2($a)
{
$b=$a->siteurl()
}

这样就没问题,那么问题出现在哪里,为什么qqpostThumb2($this->options)可以而qqpostThumb2($this)不可以?

下面是调试过程(从index.php开始):

  1. /www/index.php(21): Typecho_Router::dispatch() //index.php调用Typecho_Router::dispatch()分配路由
  2. 分配路由过后进入 /www/var/Typecho/Router.php文件,定位到133行
    相关代码:

    $widget = Typecho_Widget::widget($route['widget'], NULL, $params); //在这一行代码载入functions.php文件
    if (isset($route['action'])) {
                            $widget->{$route['action']}();   //$route['action']要进行的操作,比如文章是$widget->render()
                        }
    //$route['widget']为Widget_Archive,Typecho_Widget::widget函数内会创建一个Widget_Archive类的实例,然后实例调用execute方法.
    1. 找到/www/var/Typecho/Widget.php文件定位到189行,即是Typecho_Widget::widget函数代码

      /**
       * 工厂方法,将类静态化放置到列表中
       *
       * @access public
       * @param string $alias 组件别名
       * @param mixed $params 传递的参数
       * @param mixed $request 前端参数
       * @param boolean $enableResponse 是否允许http回执
       * @return Typecho_Widget
       * @throws Typecho_Exception
       */
      public static function widget($alias, $params = NULL, $request = NULL, $enableResponse = true)
      {
          $parts = explode('@', $alias);
          $className = $parts[0];
          $alias = empty($parts[1]) ? $className : $parts[1];
      
          if (isset(self::$_widgetAlias[$className])) {
              $className = self::$_widgetAlias[$className];
          }
      
          if (!isset(self::$_widgetPool[$alias])) {
              /** 如果类不存在 */
              if (!class_exists($className)) {
                  throw new Typecho_Widget_Exception($className);
              }
      
              /** 初始化request */
              if (!empty($request)) {
                  $requestObject = new Typecho_Request();
                  $requestObject->setParams($request);
              } else {
                  $requestObject = Typecho_Request::getInstance();
              }
      
              /** 初始化response */
              $responseObject = $enableResponse ? Typecho_Response::getInstance()
              : Typecho_Widget_Helper_Empty::getInstance();
      
              /** 初始化组件 */
              $widget = new $className($requestObject, $responseObject, $params);
              $widget->execute(); //这里execute()函数里面载入functions.php
              self::$_widgetPool[$alias] = $widget;
          }
      
          return self::$_widgetPool[$alias];
      }  
    2. $widget是一个Widget_Archive类,找到/www/var/Widget/Archive.php文件,查看
      execute函数的第1372行,可见在这里载入的function.php

      /** 初始化皮肤函数 */
      $functionsFile = $this->_themeDir . 'functions.php';
      if ((!$this->_invokeFromOutside || $this->parameter->type == 404) && file_exists($functionsFile)) {
          require_once $functionsFile;
          if (function_exists('themeInit')) {
              themeInit($this);
          }
      }

      require_once $functionsFile导入的就是主题模板里的functions文件,themeInit()是functions.php里的初始化函数,functions.php是从这导入的。

  3. 返回到第二步中的$widget = Typecho_Widget::widget($route['widget'], NULL, $params)处;执行完Typecho_Widget::widget后进入$widget->{$route['action']},这个时候因为请求页面类型文章页所以是
    $widget->render(),然后在/www/var/Widget/Archive.php的第2020行处require /www/usr/themes/initial-matser/post.php,由于post.php里调用了$this->need("header.php"),need()自定义函数作用相当于require函数,所以又导入了header.php,然后调用functions里的函数qqpostThumb($this)。
    至于为什么qqpostThumb2($this->options)和qqpostThumb2($this),那是因为$this->options是一个protected类型变量而functions.php是require进去的所以不能访问$this>options这个变量。

    期间用到的一些文件:

    typecho制作qq卡片时的问题1.jpg