Tag: StBlog

StBlog中的类似TP的标签库功能的小插件

没有评论

2010 年 12 月 01 日 at 下午 10:47分类:PHP | WEB开发

在一般博客的程序中基本的样式好像就是一边是内容板块,一边是什么分类的板块!像这样子的样式的话,我们在切换整个页面的时候其实变换的是内容板块的内容,而另一边的分类板块是不变的!这样子就要求我们在写程序的时候要注意点了,要使用恰当的方法了!之前在TP中我用的是TP的标签库功能实现了这么一个功能,现在在CI 中,今天看到了StBlog实现的方法。过程如下:
首先我们打开右边板块的试图文件,一下是部分代码:

<h5>日志分类</h5>
 <ul>
     <?php $this->plugin->trigger('Widget::Categories', '<li><a href="{permalink}" title="{description}">{title} [{count}]</a></li>');?>
 </ul>
    
    <h5>最新日志</h5>
 <ul class="post_list">
        <?php $this->plugin->trigger('Widget::Posts::Recent', '<li><a href="{permalink}" title="{title}">{title}</a></li>');?>
 </ul>
    
    <h5>最新评论</h5>
 <ul class="recent_comments">
     <?php $this->plugin->trigger('Widget::Comments::Recent', '<li><a href="{permalink}" title="{parent_post_desc}">{title}: </a><p>{content}</p></li>', 50, '...');?>
 </ul>
 
 <h5>日志归档</h5>
 <ul>
     <?php $this->plugin->trigger('Widget::Posts::Archive', '<li><a href="{permalink}">{title} [{count}]</a></li>', 'month', 'Y年m月');?>
 </ul>

看着这些代码,我们能够直接看懂的好像就是

$this->plugin->trigger()

很明显这是调用了plugin这么一个类文件,我们先找到这个类文件。
其实一看这个类就知道了,一旦这个被加载,是要执行很多操作的,我们先来看构造函数:

public function __construct()
    {
        /** 获取CI句柄 **/
		$this->_CI = & get_instance();
		
		$plugins = $this->_CI->utility->get_active_plugins();
		//print_r($plugins);
		if($plugins && is_array($plugins))
		{
			foreach($plugins as $plugin)
			{
				$plugin_dir = $plugin['directory'] . '/' . ucfirst($plugin['directory']) . '.php';
				
				$path = FCPATH . ST_PLUGINS_DIR . '/' . $plugin_dir;
				
				/** 仅能识别"插件目录/插件/插件.php"目录下的插件 */
				if (preg_match("/^[\w\-\/]+\.php$/", $plugin_dir) && file_exists($path))
				{	
					//加载每一个小功能插件
					include_once($path);

					$class = ucfirst($plugin['directory']);
					
					if (class_exists($class)) 
					{
						/** 初始化插件 */
						//初始化时要调用相应类文件的初始函数(也就是条用了本文件的register函数)
						new $class($this);//传递当前对象
					}
				}
			}
		}

先来分析这个构造函数,

$plugins = $this->_CI->utility->get_active_plugins();

这句话的功能是:从数据库中读取激活的插件名称,因为所有的插件是否激活都是通过后台写进数据库的!通过这个函数可以获取数据库中所有已经激活的插件,至于这个函数 可以去看看utility类文件里面是怎么处理的!
获取的变量$plugins是一个二维数组!接下来遍历这个二维数组,通过索引directory获取的插件的名称,然后再生成相关的路径和文件名,此后在通过PHP的系统函数include_once()来加载这些插件,这些插件放在系统根目录中的st_plugins文件夹下面。其实这个目录路径是可以更改的!这个目录下面每一个文件夹里面就是一个插件,文件夹的名字就是插件的名字!加载完插件之后,就执行了

new $class($this);//传递当前对象

初始化插件!这个初始化是在循环里面,因此只要是在数据库中被激活的插件在这里都会被初始化操作!其实所有的插件的初始化函数都是一样的,只是插件的钩子(即register函数的第一个参数)不一样罢了,代码如下:

public function __construct(&$plugin)
	{
		$plugin->register('Widget::Comments::Recent', $this, 'show_recent_comments');
		
		$this->_CI = &get_instance();
	}

看到该构造函数里面的变量了吗?其实他是个对象!其实这种写法和下面的这种写法是等效的:

$ci = &get_instance();
		$ci->load->libarary('plugin');
		$ci->plugin->register('Widget::Posts::Recent', $this, 'show_recent_posts');

还要注意的一点就是其中的第二个参数和滴三个参数,第二个参数是当前类的一个对象,第三个参数是当前类中的一个方法,这个方法在后面的操作中会被自动的调用!
这里调用了plugin类中的register方法,我们在返回找到这个方法:

[]public function register($hook, &$reference, $method)
	{
		//通过对象获取类的名称
		$key = get_class($reference).'->'.$method;		//返回一个字符串如:Related_posts->show_related_posts
		//$reference为对应的对象,$method为该对象内的一个方法
		$this->_listeners[$hook][$key] = array(&$reference, $method);
		
		log_message('debug', "$hook Registered: $key");
	}

这个方法的功能是将要使用的插件放在一个指定索引和格式的数组中($this->_listeners[$hook][$key]);
上面讲的就是在系统初始化的时候执行的
接下来我们看视图文件,它是调用了plugin类中trigger方法:

public function trigger($hook)
	{
		$result = '';
		//判断钩子是否存在
		if($this->check_hook_exist($hook))
		{
			foreach ($this->_listeners[$hook] as $listener)
			{
				$class  = & $listener[0];		//对象名
				$method = $listener[1];			//方法名
				
				if(method_exists($class, $method))
				{
					$args = array_slice(func_get_args(), 1);//取从第一个开始(除第一个)到最后一个之间的变量
					//调用用户自定义的方法/函数
					//print_r($args);
					$result = call_user_func_array(array($class, $method), $args);
				}
			}
		}
		
		log_message('debug', "Hook Triggerred: $hook");
		
		return $result;
	}

	/**
	 * 检查钩子是否存在
	 * @param string $hook 钩子的名称
	 * @return array
	 */
	public function check_hook_exist($hook)
	{
		if(isset($this->_listeners[$hook]) && is_array($this->_listeners[$hook]) && count($this->_listeners[$hook]) > 0)
		{
			return TRUE;
		}
		
		return FALSE;
	}

这个函数是在加载页面的时候被执行的。其他的就不用多说了,我们只要来看看循环里面的判断部分:
首先判断对象中是否存在该方法 这是肯定的 因为这两个都是从插件类文件中传过来的。之后的获取参数并处理参数部分,
通过程序来看我们只需要获取除了第一个参数以外的全部参数。这是为什么呢?因为第一个参数是插件的钩子,没有任何意义的!因此$args变量是一个数组!我们在通过call_user_func_array()函数来使插件类的对象调用指定的方法(show_recent_posts()),
这个方法在每一个插件中都有,是用来处理数据,我们哪一个插件来说明:
如显示最新日志的:

public function show_recent_posts($format)
	{
		//变量$format是一个html的字符串,其中的一些需要更改的值用{}扩起来了
		/** 输出格式为空?*/
		if(empty($format)) return;
		
		/** 输出多少条? */
		$list_size = setting_item('posts_list_size');
		$list_size = ($list_size && is_numeric($list_size)) ? intval($list_size) : 10;
		
		$posts = $this->_CI->stcache->get('Widget::Posts::Recent');	//判断是否有相应的缓存数据
		
		if(FALSE == $posts)
		{
			$posts = $posts = $this->_CI->db->select('slug, title')
				 ->from('posts')
        		 ->where('type', 'post')
        		 ->where('status', 'publish')
        		 ->orderby('created', 'DESC')
        		 ->limit($list_size)
        		 ->offset(0)
        		 ->get()
        		 ->result();
        
        	$this->_CI->stcache->set('Widget::Posts::Recent', $posts);	//放入缓存
		}
				
		if($posts)
		{
			foreach($posts as $post)
			{
				$wildcards = array('{permalink}', '{title}');
				
				$replaces = array(site_url('posts/'. $post->slug), $post->title);
				
				echo str_replace($wildcards, $replaces, $format) . "\r\n";   //将指定的字符进行替换成我们需要的数据
			}	
		
		}		
		
	}

通过这样子调用,到最后echo一下 就能把数据库中相应的数据输出到我们调用plugin中的trigger方法的地方

($this->plugin->trigger())

!而且不管在哪个页面都是可以直接调用的

stblog学习笔记1

没有评论

2010 年 12 月 01 日 at 下午 1:23分类:PHP | WEB开发

最近在学习CI 的开源产品StBlog,初次学习感觉这个产品写的很不错 对于我这样子的一个CI 新手来说是一个很不错的学习机会。在大致的理解了一下整个的博客系统的架构之后,感觉这个里面有好多东西都是我在之前一段时间里面学习CI没有接触到的东西,就比如说自定义模版的路径,这样子以来 使得整个网站有了一个新的功能 那就是可以选择主题和皮肤了!主要的实现过程就是自己在项目的类库文件夹中首先扩展Loader.php文件(class MY_Loader extends CI_Loader),然后在这个类里面定义两个方法(一个是项目的前台的重写路径,一个是后台的重写路径,后台的路径可以说不变还是原来的那个),该方法的功能是用来重写视图的路径的,详见代码:

 public function __construct() 
    {
        parent::CI_Loader();
    }

	 /**
	 * 打开皮肤功能
	 * 
	 * @access public
	 * @return void
	 */ 
    public function switch_theme_on()
    {
    	$this-&gt;_ci_view_path = FCPATH . ST_THEMES_DIR . DIRECTORY_SEPARATOR . $this-&gt;theme . DIRECTORY_SEPARATOR;
    }

	 /**
	 * 关闭皮肤功能
	 * 
	 * @access public
	 * @return void
	 */ 
    public function switch_theme_off()
    {
    	//just do nothing
    }

接着我们要扩展系统的控制器类(class QT_Controller extends Controller和class HT_Controller extends Controller),然后继承系统的控制器类,在这个类文件中实际上是有两个类的 一个是前台的(QT_Controller),只要是控制前台要操作的一些操作的,然后在之后的写前台的操作代码是只要继承这个类(QT_Controller)就可以了,别忘了在这个类中写上

/** 前台页面均使用主题皮肤功能 */
	    $this-&gt;load-&gt;switch_theme_on();

功能是开启主题皮肤功能我们很多的加载什么类啊 辅助函数啊等都可以在这个前台主控制器里面加载。再来说后台(HT_Controller)其实是跟前台一样的功能的,只不过不同的是使用了

 /** 后台管理页面,不使用皮肤 */
	    $this-&gt;load-&gt;switch_theme_off();

不开启主题皮肤功能,如果要开启的只要写成

/** 前台页面均使用主题皮肤功能 */
	    $this-&gt;load-&gt;switch_theme_on();

就OK了,这样子就描述完了实现前后台主题皮肤的功能!
接下来研究了主要的前台模型的类文件,感觉自己还是很有相当大的差距的,人家写的就是不一样。首先为了避免出现直接的数据库的数据表名,我们可以在模型文件类里面用const来定义表常量,就是把表名赋值给一个自定义的常量,之后在条用表名的时候用这个常量就可以了!其次也许是为了方便检查错误把,在初始化函数里面使用

log_message('debug', "STBLOG: Posts Model Class Initialized");

这样子通过查看日志文件就可以知道这个类已经被加载了。
看到stblog整个模型文件里面函数时,感到都有一个特征 而且我感到这样子蛮好的,就是先把所有各种各样带条件查询相关的函数写在一块,然后把所有的CURD的操作写在一块,这样子感觉整个模型文件蛮清晰的!要找一个函数能够快速找到!
再来说说单个函数的写法,其实要是我自己写的话,也无非就是整理查询条件等东西:首先,所有的查询方法中都有默认的参数项,在生成条件($this->db->where(XXXX))之前我们可以先判断这个参数值的一些情况如:是否为空 是否是数字类型的等等情况,然后在生成条件语句,再者我们可以用链式的写法如:

$this-&gt;db-&gt;select('uid')-&gt;from(self::TBL_USERS)-&gt;where($key, $value);

,在这之后我们还可以不断的写我们需要的各种条件语句 如order_By啊等,在这之后我们只要用

$this-&gt;db-&gt;select('uid')-&gt;from(self::TBL_USERS)-&gt;where($key, $value);

l来返回结果集或者:

$this-&gt;db-&gt;get(self::TBL_USERS)-&gt;row();

l来返回第一行数据,或者

$this-&gt;db-&gt;get(self::TBL_USERS)-&gt;num_rows();

l返回结果数据的数目等等
因为stblog在他的autoload.php文件中默认加载了一些类和辅助函数!因此在整个项目的书写时只要随时拿来用就可以了!
此外 要注意的是有个函数叫做setting_item()的,这个函数的作用是用来获取数据表setting的一些值的,一次获取一个指定name的值,这个函数定义在library的common.php文件中,虽然这个是个类,但是setting_item()函数是定义在类之外的,也就相当于我们一般的辅助函数之类的用法,这个函数用到了stblog自定义的缓存类stcache.php文件,这个文件里面的东西看起来很简单,这个文件中使用文件的辅助函数(file),只要利用文件辅助函数来将数据写入文件实现数据的定时缓存的!
stblog的url都是使用路由来实现的,一开始我也纳闷了半天,后天突然想到可能是设置了路由,打开routes.php一看 果不其然,里面设置了很多的路由规则!看的时候一定要注意哦!
一般情况下我们从网上下载下来CI系统,所有的东西都是放在system文件夹中的,而我们只要开发的目录是在application目录中,其实我个人也不是很喜欢这个样子的目录结果,我实现类似于TP那样子的,把我们要开发的项目目录文件夹APP放在框架同级目录 ,而不是在框架目录里面,其实在CI中要做到这点很简单的,只要把整个application文件剪切出来放在与system文件夹同级目录就行了,并不需要修改任何地方!这样子就实现了类似TP那样子的外围目录效果了!在CI中我们一般的如js啊css啊图片啊等都是在与system文件夹同级目录的目录上建立一个或者几个文件夹来存放相应的东西的!我们的上传文件的目录也是定义在这个地方,当然这个目录是自己定义的,可以放在自己想要的任意位置!我们甚至可以将我们的模版目录定义到入口文件的地方,定义的方法上面已经讲过了!不过要记住的一点就是所有的目录都是相对于index.php文件的,为什么呢?因为stblog是采用单一入口流程来运行的!