Yii 整合 WordPress

一直使用 yii 来做网站,也和朋友合作做了几个成品。只是框架虽好,要自己做一个像 wordpress 那样完善且功能丰富的博客系统却是有些费力。本站上一个版本就是基于yii做的,虽然也是像模像样,自己却是知道里面有诸多不顺畅的地方,索性还是用了 wordpress,当然也是不愿抛了yii提供的各种便利,那么就让她们华丽合体吧~~~

google搜一下,会有几篇关于她俩整合的文章,都是在yii的官方wiki上的,一般有两种方法
1 在wordpress 里面运行 yii
2 在 yii 里面运行 wordpress (本站使用的方法)

Run an Yii Application inside an WordPress page
这篇大概是最早做这项尝试的,后面的几篇都有提到,属于第一种方法。

Integrating Yii with WordPress
这也是第一种方法。

Integrating WordPress and Yii: yet another approach
这篇就开始用第二种方法了,看到似乎要改挺多代码的,也没有细看。

Integrating WordPress and Yii: still another approach, using Yii as the router/controller
这篇引用了上一篇文章,也是本站所使用的方法,稍微有些细节的修改,从逻辑上看相对简洁,两边都不需要做什么核心的修改,正文中介绍的便是这种方法了,权当是翻译,也分享下使用原文方法遇到的问题和解决。

Integrating WordPress and Yii,Working Out The Details.
和上一种方法一样,有点小修改,提到了为 yii 页面使用 wordpress 主题 的问题

好了,言归正传,来看看 yii 和 wordpress 的合体大法吧。

Step 1 建立 yii application
这个根据 yii 的教程走就好了,总之这一步创建一个基于 yii 的基本站点并配置好数据库。假设目录为 /xxx/website

Step 2 安装 wordpress
将 wordpress 的整个目录放在 /xxx/website,本文假设目录名为 wordpress。 在wp-config.php中配置使用的数据库,可以和 yii 使用同一个数据库,这样我们就可以在 wordpress 中使用 yii 的 model 的功能了。
另外,还需要修改数据库的 wp_options 表数据.
找到 option_name 为 siteurl 的行,修改 option_value 为 http://你的域名/wordpress
找到 option_name 为 home 的行,修改 option_value 为 http://你的域名

Step 3 定位 yii 的 404 页面到 wordpress 的 404 页面
这里要写一个类 ExceptionHandler 的类给 yii, 创建文件 /xxx/website/protected/components/ExceptionHandler.php , 代码如下

<?php
class ExceptionHandler
{
    public function __construct()
    {
        define('YII_ENABLE_EXCEPTION_HANDLER',false);
        set_exception_handler(array($this,'handleException'));
    }

    public function handleException($exception)
    {
        // disable error capturing to avoid recursive errors
        restore_error_handler();
        restore_exception_handler();

        $event=new CExceptionEvent($this,$exception);
        if($exception instanceof CHttpException && $exception->statusCode == 404)
        {
            try
            {
                Yii::app()->runController("blog/index");
            }
            catch(Exception $e) {}
            // if we throw an exception in WordPress on a 404, we can use
            // our main error handler to handle the error
        }

        if(!$event->handled)
        {
            Yii::app()->handleException($exception);
        }
    }
}
?>

Step 4 修改 yii 的入口文件 /xxx/website/index.php
这一步使得 yii 程序启动时加载 wordpress 的程序

define('WP_USE_THEMES', true);
$wp_did_header = true;
require_once('wordpress/wp-load.php');
 
require_once(dirname(__FILE__) . '/protected/components/ExceptionHandler.php');
$router = new ExceptionHandler();
 
....
 
require_once($yii); 
Yii::createWebApplication($config)->run();

Step 5 在 yii 中运行 wordpress
经过以上4步,基本的准备工作就做好了,下面让 wordpress 在我们的 yii 框架里运行起来把。
原文在这一步是另外创建了一个 wordpress 的 controller,但是我希望首页就是 wordpress 的博客首页,所以就直接用现成的 SiteController 了。
打开 /xxx/website/protected/controllers/SiteController.php
修改里面的 actionIndex函数为

$this->layout = false; // note that we disable the layout
try {
    //spl_autoload_unregister(array('YiiBase', 'autoload'));
    wp();
    require_once( ABSPATH . WPINC . '/template-loader.php' );
    //spl_autoload_register(array('YiiBase', 'autoload'));

    Yii::app()->end();
}
// if we threw an exception in a WordPress functions.php
// when we find a 404 header, we could use our main Yii
// error handler to handle the error, log as desired
// and then throw the exception on up the chain and
// let Yii handle things from here

// without the above, WordPress becomes our 404 error
// handler for the entire Yii app
catch (Exception $e) {
    throw $e;
}

其实光运行上述代码是会遇到 yii autoload 的问题的,这也是原文没有提到的,让我好一顿折腾。
这里有两个办法,一个治标,一个治本。

治标的方法,就是解开上述代码中被我注释掉的 spl_autoload_unregister 相关的两句,这样,在运行 wp() 时就不会调用 yii 的 autoload 函数产生错误。但是这样在 wordpress 里面就有很多 yii 的功能不能使用了。 所以我后来使用了另一个治本的方法,将这两句注掉了。

治本的方法,需要修改wordpress的一小点代码。
在 wp-includes/post.php 中,找到下面代码

if ( is_a( $post, 'WP_Post' ) ) {
      $_post = $post;
 } elseif ( is_object( $post ) ) {
      if ( empty( $post->filter ) ) {
           $_post = sanitize_post( $post, 'raw' );
           $_post = new WP_Post( $_post );
      } elseif ( 'raw' == $post->filter ) {
           $_post = new WP_Post( $post );
      } else {
           $_post = WP_Post::get_instance( $post->ID );
      }
 } else {
      $_post = WP_Post::get_instance( $post );
 }

错误的地方在于 is_a 会调用YIIBase::autoload, 由于$post可能是数字,从而include的时候,导致exception,
修改的方法就是先检查is_object,修改如下

if ( is_object( $post ) ){
  if ( is_a( $post, 'WP_Post' ) ) {
       $_post = $post;
  } else {
       if ( empty( $post->filter ) ) {
            $_post = sanitize_post( $post, 'raw' );
            $_post = new WP_Post( $_post );
       } elseif ( 'raw' == $post->filter ) {
            $_post = new WP_Post( $post );
       } else {
            $_post = WP_Post::get_instance( $post->ID );
       }
  }
}

好了,在浏览器中输入你的域名,一个整合在 yii 里的 wordpress 就呈现啦。

Step 6 让 yii 的页面使用 wordpress 的主题
如果希望你的网站统一用 wordpress 的主题,同时又想用 yii 来写页面(废话,不然整这么多干嘛),那么再做下面一个小修改就好了。
打开 /xxx/website/protected/views/layouts/main.php 将里面的内容替换为

<?php get_header(); ?>
<div id="primary" class="site-content">
    <div id="content" role="main">
        <?php
             // echos Yii content
             echo $content;
        ?>
    </div>
</div>
<?php get_sidebar(); ?>
<?php get_footer(); ?>

OK,大功告成。当然如果你对 yii 熟悉,这些 controller、layout 什么都可以随便搞。总之你现在已经是左手倚天右手屠龙啦,爱怎么折腾怎么折腾吧。



发表评论?

3 条评论。

  1. 整合后具体能实现什么功能和效果,能简单说明一下吗

    回复回复
  2. @buffer: 你好,你可以说下错误是什么。我自己这个网站就是这么做的,我遇到的出错的地方,在 Step 5 里面有说解决的办法

    回复回复
  3. 很不靠谱,严重错误,但是依然感谢你的翻译精神

    回复回复

发表评论