PHPCMS自定义字段

没有评论

2011 年 03 月 31 日 at 下午 5:49分类:PHPCMS

phpcms中模型表单的字段是存储在缓存文件里的 catches/caches_model/caches_data/model_field_{modelid}_cache.php
这个缓存文件是在后台更新全站缓存文件时 从数据表 pc_model_field 里取出数据来 生成的,再添加或修改内容(这里的添加或者是修改是指在我们后台用到的,如:我们给news模型添加个自定义字段。那么当我们在添加新闻或者是修改新闻的时候,会从news模型所在的modelid指定的model_fiels_modelid_cache.php文件中获取所有的字段,而这些字段就是我们在修改或者是添加新闻的表单中的那些控件)时会读取这个缓存文件,只有缓存文件中存在的字段所对应的值才会被添加或更新,而这个数据表里的内容是新建模型的时候,而新建模型就会新建字段,那么这些字段会被添加进pc_model_field表中,而添加模型字段的时候有个字段类型选择,可供选择的类型是从 phpcms/modules/content/fields/fields.inc.php 里取出来的。
那怎么让我们新建的这个自定义类型字段在添加内容的表单中显示出来呢?显示表单的模板并不是固定的,里面的字段的html代码是根据这个模型的字段信息(model_field_{modelid}_cache.php)动态生成的,而用来生成模板的字段信息是从caches/caches_model/caches_data/content_form.class.php 这个类中的 get() 方法中得到的,该get()方法会返回一个数组,该数组包括了该模型表单的字段的,表单html代码也包括在其中了, 而这个content_form.class.php文件也是更新缓存的时候自动生成的,内容取自 phpcms/modules/content/fields/content_form.class.php,以及其他内容,比如每个字段类型都有一个方法,都会加入到这个缓存文件中。稍后继续。。。

PHPCMS 生成首页静态文件分析

2条评论

2011 年 03 月 30 日 at 下午 2:51分类:PHPCMS

cms的最重要的一个功能就是生成html的静态页面。在2008的版本中和v9版本的生成html静态过程基本上差不多的。
phpcms2008:
在08的后台我们点击内容管理、生成html时,会有一系列的菜单,当我们点击“更新首页”时,那么在phpcms的根目录就会生成一个index.html的页面。这个index.hml的文件是一个纯html的静态文件。那么这个文件是怎么生成的呢?继续。。。
当我们在后台点击“更新首页”的选项时,链接地址会跳转到:admin.php?mod=phpcms&file=html&action=index这个文件中。按照phpcms2008的路劲解析规则,我们可以在admin/找到html.inc.php这个文件。此时的action是index.我们看如下的代码:

case 'index':
        $filesize = $html->index();
	showmessage('网站首页更新成功!<br />大小:'.sizecount($filesize));
	break;

在这个文件中还加载了html.class.php的静态处理类。调用了这个类中的index方法

//生成首页html
	function index()
	{
		global $PHPCMS;
		if(!$PHPCMS['ishtml']) return true;
		extract($GLOBALS, EXTR_SKIP);
		$head['title'] = $PHPCMS['sitename'].'-'.$PHPCMS['meta_title'];
		$head['keywords'] = $PHPCMS['meta_keywords'];
		$head['description'] = $PHPCMS['meta_description'];
		$subcats = subcat('phpcms', 0, 0);
		$catid = 0;
		ob_start();
		include template('platform', 'index');
		$file = PHPCMS_ROOT.$this->url->index();
		return createhtml($file);
	}

在这里类中还跳用了url.class.php类,调用了其中的index方法:

function index()
	{
		return 'index.'.$this->PHPCMS['fileext'];//后缀名
	}

这个方法返回的静态文件的拓展名(.html)。在html类中的index方法一开始定义了模板解析需要的一些变量。之后打开后台的缓存区。生成文件名:$file.调用函数createhtml

function createhtml($file)
{
    $data = ob_get_contents();
    ob_clean();
    dir_create(dirname($file));
    $strlen = file_put_contents($file, $data);
    @chmod($file,0777);
    return $strlen;
}

这个函数就是将缓冲区中的内容获取到然后写到指定的文件$file中,返回字节数。
这个之间其实有一个很容易误导的地方。这里的生成的index.html是完全静态的。那么这一个过程到底是怎么处理的呢?这么一些数据到底是怎么来的呢?其实想想也很简单。我们在前台的common.inc.php文件中开启了缓冲区,这里只是开启了,在整个前台我们根本就没有在哪里用到了缓冲区中的内容。我个人认为,前台开启缓冲区是为了页面缓存而使用的。如果没有开启页面缓存,那么前台整个缓冲区是没有任何作用的。在生成首页时,首先开启了缓冲区。然后调用template函数进行加载。要知道include这个函数其实就是加载一些html代码而这些代码是会输出到浏览器的。前台没有使用

$data = ob_get_contents();
    ob_clean();

获取值,而就直接把html和php的代码显示出来了。所以我们看到的源码是在缓存文件中的。但是在生成首页时,他采取这两行代码,它获取了这些输出到浏览器上上面的内容而这些内容正是一些结果,也就是是一些静态的代码。把这些数据保存到index.html中于是就实现了静态首页文件的生成。
phpcms v9:
当我们点击后台的生成首页时,跳转到的页面是:”admin.php?mod=content&c=create_html&a=public_index”。按照v9的url解析规则,我们找到的文件是:/phpcms/modules/content/create_html.php。这个文件的public_index方法:

//生成首页
	public function public_index() {
		$this->html = pc_base::load_app_class('html');
		$size = $this->html->index();
		showmessage(L('index_create_finish',array('size'=>sizecount($size))));
	}

这里也加载了html.class.php文件

public function index() {
		if($this->siteid==1) {
			$file = PHPCMS_PATH.'index.html';
			//添加到发布点队列
			$this->queue->add_queue('edit','/index.html',$this->siteid);
		} else {
			$site_dir = $this->sitelist[$this->siteid]['dirname'];
			$file = $this->html_root.'/'.$site_dir.'/index.html';
			//添加到发布点队列
			$this->queue->add_queue('edit',$file,$this->siteid);
			$file = PHPCMS_PATH.$file;
		}
		define('SITEID', $this->siteid);
		//SEO
		$SEO = seo($this->siteid);
		$siteid = $this->siteid;
		$CATEGORYS = $this->categorys;
		$style = $this->sitelist[$siteid]['default_style'];
		ob_start();
		include template('content','index',$style);
		return $this->createhtml($file, 1);
	}

其实这些和2008版本基本上是差不多的。
这里调用的createhtml方法

/**
	* 写入文件
	* @param $file 文件路径
	* @param $copyjs 是否复制js,跨站调用评论时,需要该js
	*/
	private function createhtml($file, $copyjs = '') {
		$data = ob_get_contents();
		ob_clean();
		$dir = dirname($file);
		if(!is_dir($dir)) {
			mkdir($dir, 0777,1);
		}
		if ($copyjs && !file_exists($dir.'/js.html')) {
			@copy(PC_PATH.'modules/content/templates/js.html', $dir.'/js.html');
		}
		$strlen = file_put_contents($file, $data);
		@chmod($file,0777);
		if(!is_writable($file)) {
			$file = str_replace(PHPCMS_PATH,'',$file);
			showmessage(L('file').':'.$file.'<br>'.L('not_writable'));
		}
		return $strlen;
	}

有些东西做了修改,但是大致的流程还是没变的。生成的index.html文件在phpcms的根目录中。

PHPCMS 2008 新模块的开发分析

没有评论

2011 年 03 月 29 日 at 下午 2:18分类:PHPCMS

PHPCMS 的模块安装是通过后台管理面板 系统设置->模块管理->安装模块 来实现的,其对应的地址是 admin.php?mod=phpcms&file=module&action=install,既然这个是从admin.php文件进去的,那么先来分析下根目录下的admin.php文件。admin.php 前面的一系列是一些权限的判断,这里我暂且不予关心。这里我们所关心的主要是下面这2行代码:

/**
*  下面这两句话的意思是,如果当前的模块不是phpcms的话,那么就加载当前模块下面的admin.inc.php,因此我们新开发一个模块 新模块/admin/admin.inc.php 文件是必不可少的。
*  引入当前模块下的 $file 文件($file 通过 admin.php?mod=phpcms&file=module&action=install 得到,$_GET['file']), 如果找不到文件则提示错误信息。
*/
if($mod != 'phpcms' && !@include PHPCMS_ROOT.$M['path'].'admin/admin.inc.php') 
	showmessage('The file ./'.$M['path'].'admin.inc.php is not exists!');

if(!@include PHPCMS_ROOT.(isset($M['path']) ? $M['path'] : '').'admin/'.$file.'.inc.php') 
	showmessage("The file ./{$M['path']}admin/{$file}.inc.php is not exists!");

从上面2行代码我们可以很容易根据 admin.php?mod=phpcms&file=module&action=install 定位到 phpcms根目录/admin/module.inc.php。
分析 module.inc.php 可以根据 admin.php?mod=phpcms&file=module&action=install 的 action=install 部分我们可以大致确认实际进行模块安装的文件大概从 第10行 到第65行。
现在我们开始以 vote 为例分析一模块安装。
首先我们来到第72行 include admin_tpl(‘module_install’); // 在一行的作用就是点后台 安装模块 后默认显示的输入表单
假设 vote 是我们新开发的一模块,其所在目录为 phpcms根目录/vote/,因此安装模块时,模块安装目录中要填写 vote,submit 提交后,代码跳转到:

if(isset($confirm) && $confirm==1)
			{
				// 判断安装目录是否存在,其实就是判断是否有 phpcms根目录/vote/install/ 目录,
				if(!is_dir(PHPCMS_ROOT.$installdir."/install/"))
				{
					showmessage($LANG['module_install_dir_not_exist']);
				}
				
			    require_once PHPCMS_ROOT.$installdir."/install/config.inc.php";
				// 如果新开发的模块和已有模块冲突(名称相同),则提示错误信息。
				if(array_key_exists($module, $MODULE)) showmessage($LANG['installed_module_unstall_it_then_continue']);
			    include admin_tpl('module_install_confirm');
			}
			else
			{
                           //加载安装的表单填写页面
			    include admin_tpl('module_install');
			}

在确认模块信息并 submit 提交后,代码跳转到:

if(isset($confirminstall) && $confirminstall)
		{
			//加载安装模块的说明信息
			require_once PHPCMS_ROOT.$installdir."/install/config.inc.php";
			
			//判断模块是否安装了
			$r = $db->get_one("SELECT module From ".DB_PRE."module WHERE module='$installdir'");
			if($r) showmessage($LANG['installed_module_unstall_it_then_continue']);					
			
			//判断sql文件是否存在
			if(file_exists(PHPCMS_ROOT.$installdir."/install/mysql.sql"))
			{
				$sql = file_get_contents(PHPCMS_ROOT.$installdir."/install/mysql.sql");
				sql_execute($sql);
			}
			// 如果新开发模块 instal 目录下存在 extention.inc.php 进行 menu 的设置
            // 这里主要进行模块后台的管理菜单设置
			if(file_exists(PHPCMS_ROOT.$installdir."/install/extention.inc.php"))
			{
				@extract($db->get_one("SELECT menuid AS member_0 FROM ".DB_PRE."menu WHERE keyid='member_0'"));
				@extract($db->get_one("SELECT menuid AS member_1 FROM ".DB_PRE."menu WHERE keyid='member_1'"));
				@include (PHPCMS_ROOT.$installdir."/install/extention.inc.php");
			}
			// 用 FTP 进行 目录权限设置。
			if(FTP_ENABLE)
			{
				require_once PHPCMS_ROOT.'include/ftp.class.php';
				$ftp = new ftp(FTP_HOST, FTP_PORT, FTP_USER, FTP_PW, FTP_PATH);
				if(file_exists(PHPCMS_ROOT.$installdir."/install/chmod.txt"))
				{
					$files = file(PHPCMS_ROOT.$installdir."/install/chmod.txt");
					$files = array_filter($files);//删除$files中指为false的条目
					foreach($files as $file)
					{
						$ftp->dir_chmod(PHPCMS_ROOT.$file);
					}
				}
			}
			
			//复制模板目录到系统模板目录下面
			if(file_exists(PHPCMS_ROOT.$installdir."/install/templates/"))
			{
				dir_copy(PHPCMS_ROOT.$installdir."/install/templates/", PHPCMS_ROOT.'templates/'.TPL_NAME.'/'.$module.'/');
			}

			// 复制 语言文件 到系统 语言文件下
			if(file_exists(PHPCMS_ROOT.$installdir."/install/languages/"))
			{
				dir_copy(PHPCMS_ROOT.$installdir.'/install/languages/', PHPCMS_ROOT.'languages/'.LANG.'/');
			}
			//更新全部缓存
			cache_all();
			//更新tags
			tags_update();
                         //提示模块安装成功
			showmessage($LANG['module_install_success'], "?mod=".$mod."&file=module&action=updatecache");
		}

虽然说是以 vote 为例讲模块的开发,但是几乎没多少具体涉及到 vote,主要是讲了下整个模块安装的流程和所需要的文件信息。。。

PHPCMS V9 pc标签的分析

没有评论

2011 年 03 月 29 日 at 上午 11:00分类:PHPCMS

在phpcms 2008的版本中我们用到的标签是tag,关于这个标签的使用在前两篇的分析中已有讲解。这里主要聊一下V9版本中的pc标签的解析过程。
在我们的模板文件中,我们经常看到如下形式的代码:

{pc:content  action="position" posid="2" order="listorder DESC" num="4"}
{pc:content  action="position" posid="1"  order="listorder DESC" thumb="1" num="5"}
.......

像这样子的一些我们称之为模板标签。那么系统是怎么解析的呢?
我们按照系统解析的步骤来走一遍。。。
模板,首先要被php文件所包含也就是include到当前的php文件中,在这个include的过程中,要很多事情,那就是对模板中自定义的一些代码进行解析。这时用到的函数是template函数。这个函数在global.func.php文件中。其实以我个人观点来看,不管是UCHome,还是Dixcuz,还是ThinkPHP或者是CI等的模板解析都是一个模子的概念。在template函数中我们看到加载模板解析类template_cache.class.php文件。而且还调用了其中的template_compile方法对模板进行解析,我们来看解析函数template_parse里面的部分代码

$str = preg_replace("/\{pc:(\w+)\s+([^}]+)\}/ie", "self::pc_tag('$1','$2', '$0')", $str);
$str = preg_replace("/\{\/pc\}/ie", "self::end_pc_tag()", $str);

这就是匹配模板中的pc标签的内容的编译过程。看到上面的代码我们知道把匹配上的pc标签的内容交给了方法pc_tag来处理。我们找到该类中的pc_tag方法。
这个方法是一个非常庞大的方法,代码很多。这里不再卓行分析,把大致的解析过程讲一下:
该方法接收三个参数:$op:操作的模块,$data:pc标签的相关参数,$html:整个标签的内容({pc:…..}),之后对整个$data标量进行正则处理匹配出键值对的形式。然后通过循环,将所有参数存放到$datas数组中。然后判断当前操作的$op的值的情况。在标签中还有些特殊的标签调用:get,json,block,xml这四种特殊的标签调用,我们现在就不讲了,我们只讲普通的。到这里时,会进入esle的控制中,代码大致在183行左右。刚刚我们说过$op表示的是一个模块,那么一进这个esle入口便会判断这个模块是否存在,此外还要判断一个标签解析类是否存在。该标签解析类是在每个模块下面的classes/的以“模块名_tag.class.php”的文件。这些文件的功能就是用来解析pc的数据信息的。在前面的参数中我们必选的一个参数action,该参数就是事件/方法。再判断该操作方法是否存在于“模块名_tag.class.php这个文件中,如果存在的话,那就才能执行下面的代码。我们来看下调用标签解析类中的方法的一行代码:

$str .= '$'.$return.' = $'.$op.'_tag->'.$action.'('.self::arr_to_html($datas).');';

举个例子来说:
如果我们解析如下标签:

{pc:content  action="position" posid="2" order="listorder DESC" num="4"}

那么调用的模板解析类是modules/content/classes/content_tag.class.php.
调用的解析方法是:$data = $content_tag->position($datas);
返回值$return是在pc标签中定义的返回值变量,如果没有那么就是data.那么整个数据调用的结果都是在$data变量中了。我们就可以在模板中使用这个变量了。
注意:返回值并不是直接返回data变量给pc标签的,而是返回相关的php代码,这个php代码是在字符串$str中的。因此,pc标签的解析结果并不是一个值,而是返回的是一些php代码。跟一般的变量解析一样返回一串php代码。

PHPCMS v9入口文件分析

一条评论

2011 年 03 月 28 日 at 下午 3:33分类:PHPCMS

phpcms2008版本和v9版本的最大区别个人感觉在于v9版本完全采用mvc的开发模式进行面向对象的开发,而2008版本几乎完全就是采用面向过程的开发,只是其中的部分操作用到了类的方式来处理。相对于08而言,v9的看上去更加的直爽,简洁。不像08那样子看上去乱糟糟的一片。所以之后呢本人将会慢慢的研究v9版本的相关知识点。
我们看到入口文件很简单。就是一个调用类的过程。我们主要来看下这个类文件/phpcms/base.php,该文件一开始定义了一些常量和加载了相关的系统函数库和相关的配置项。该文件中最重要的一下莫过于base类了。该类是v9系统的基类,该类定义了
1、加载系统函数库的方法:load_sys_func(),
2、加载应用函数库的方法:load_app_func(),
3、加载系统类库的方法:load_sys_class(),
4、加载应用类库的方法:load_app_class(),
5、加载模型的方法:load_model(),
6、加载配置文件的方法:load_config();
这些方法函数在v9项目中是你经常用到的,经常用来加载相应的函数啊类啊或者是模型啊配置文件啊等相关信息。
在base类的creat_app方法是用来创建一个项目的方法。该方法加载了类application,找到该类文件。
其实这个类文件功能也是比较简单的。其一:调用参数处理类(param.class.php),近GET/POST来的参数进行处理,获取到需要操作的模块名m,控制器名c,事件名a,分别调用的方法为:$param->route_m(), $param->route_c(), $param->route_a();
接着在初始化函数中调用方法init()来调用事件a.在调用事件a之前,必须要知道控制器和模块是什么,那么在init的方法中一开始就调用方法load_controller()来获得当前的控制器c和操作事件a。经过相关的判断之后,通过函数:call_user_func(array($controller, ROUTE_A));来加载我们项目中的控制器,并且调用指定的方法。从而开始了整个项目的运行。。。

PHPCMS2008 源码分析总结

没有评论

2011 年 03 月 28 日 at 下午 2:09分类:PHPCMS

整体框架
1. 主要目录
Include:包含目录,整个网站的公用函数、包含文件等都放在这里,十分重要
Languages:语言目录,所有的语言包都放在这里,一个子目录就是一个语言包
Data:缓存目录,里面对缓存文件进行了分类
Data/Cache:文本缓存目录,对数据库的数据进行了文本缓存,会经常用到
Data/Cache_template:模板目标文件目录,存放模板源文件编译的目标php文件
Data/Cache_page:静态缓存目录,动态文件的静态缓存页面
Data/datasource:数据库表和字段的说明文件
Templates:模板源文件目录,存放模板和标签的源文件
Admin:后台目录,存放后台相关的文件
/Admin/template:后台模板目录
Uploadfile:上传目录,存放上传的所有图片和文件
2. 主要文件
/admin.php:后台的入口页面
/index.php:首页的入口页面
/list.php:栏目页的入口页面
/show.php:最终页的入口页面
/include/cache.func.php:文本缓存的相关函数
/include/common.inc.php:动态页面的主包含文件,负责页面输出的主要工作,极为重要
/include/config.inc.php:配置文件,定义全局性的常量
/include/date.class.php:日期类
/include/db_mysql.class.php:连接mysql数据库的数据类
/include/dir.func.php:目录类,在生成静态页、创建缓存的时候会经常用到
/include/form.class.php:界面类,封装了一些生成界面的公共函数
/include/global.func.php:公用函数库,十分重要
/include/priv_group.class.php:会员组的权限类
/include/priv_role.class.php:角色的权限类
/include/template.func.php:模板相关函数库,在模板编译的时候需要用到
/include/upload.class.php:上传类,上传文件或者图片需要用到
3. 页面的输出过程
第一, 通过/admin.php的入口页面,根据不同的参数,包含不同的页面
第二, 后台的整体框架分为两部分,一部分是上边的头和菜单以及左边的菜单,另一部分是右边的操作区。通过jquery的load方法,只在操作区上加载各个页面的模板,实现局部刷新页面的效果,看样子像是框架,实际上是AJAX。
第三, 输出页面的准备工作是由/include/common.inc.php来完成的,而真正的输出页面的工作,是由template函数取出包含页面就可以了,比较简单。
第四, 这里主要介绍common.inc.php这个文件
首先,定义各个常数
其次,包含所有需要的文件,有包含文件、函数库和类文件,还有语言包
然后,判断是否开启了页面缓存,如果开启了,就输出缓存页面,具体的缓存机制稍后做详细分析
接着,用ob函数开辟一个缓存区,准备把输出的页面放入到缓存区,这里还判断了是否需要压缩页面。压缩页面的好处就是可以减少页面体积,提高浏览速度,但付出的代价就是服务器的性能。
开启数据库连接
判断是否开启了魔法函数,如果没有开启,需要用addslashes函数对GET、POST和Cookie进行非法字符的过滤,在用extract展开为变量
还需要对URL进行非法字符的判断,这些都是安全方面的考虑
如果开启了自动更新文本缓存,需要包含cache.func.php文件,然后更新缓存,这个选项最好是不要开启,否则会影响速度
获取文本缓存的内容,并展开为变量,然后unset文本缓存
如果是前台,需要判断刷新最小间隔时间
主要就是这么多步骤,这是访问所有php文件之前都必须做的准备工作,因为考虑得比较细,所以才会这么复杂。
4. 前台页面和后台页面输出过程的区别
后台页面输出明显比前台页面要复杂得多,主要是需要包含的页面和定义的常量要多很多,而且后台页面还需要判断角色的权限以及是否登录。
前台页面需要设置seo相关项目,内容页是写死的,其他页面都是通过后台的栏目设置,然后从文本缓存中读取出来的,而后台不用设置seo选项。
5. 关于数组的应用
在系统中大量运用了数组,特别是多维数组
首先,很多文本缓存都是用数组形式表现的
其次,很多字段也都是保存为数组的
再次,数组的运用非常广泛,比如查询出来的记录集,也被转成了数组,返回给页面,页面处理的就是数组,和数据库没有任何关系。
6. 面向对象的运用
封装了多个对象,这个是应该好好学习的
但是对数据类的封装比较简单,应该更加通用和抽象一点,比如把保存数据封装成一个方法,其他所有操作都调用这个方法,数据通过多维数组来传递。
四、模板引擎
1. 系统常量:系统自定义的常量,一般都是数组取文本缓存或者变量取Cookie
2. 标签:这里的标签实际上就是设置get函数的各个参数,包括获取数据的sql语句,对应的查询条件和自定义参数等,模板引擎的精华就在于此。get函数在编译成php文件的时候,会自动转换为tag函数
3. 模板:分为普通模板和标签模板,在标题上加以区分
4. 页面:该系统没有集中对页面进行管理,对页面没有一个明显清晰的概念。所有的模板都对应一个html页面,是模板源文件,然后再编译成php文件。
5. 页面的对应关系:是通过模型来对应页面的,模型就相当于一个模块,每个模块的前台页面模板不相同,比如新闻和产品。每个模型都有三种页面,栏目页、列表页和内容页。而栏目和模型是绑定的,这样每个栏目都有对应的模板。
6. 模板和标签的关系:模板是展示格式,标签是取数据。也就是说模板都是可见的,而标签都是和动态数据相关的,并没有明确的格式,只有和模板关联起来才会输出内容。
7. 好处
第一, 修改和保存模板都是通过文本文件,而不是通过数据库,可以大大提高速度,而且便于用ftp直接修改模板源文件。另外,还可以对模板文件分类,形成多套模板。
第二, 在模板中增加了逻辑语法,使得模板制作更加灵活简便
第三, 通过正则把模板源文件转换成符合PHP语法的动态页面,完全避免了字符替换中的复杂逻辑处理,把复杂业务都交给php文件来处理。
第四, 引入了风格,可以统一更换css
第五, 替换模板采用的是template标记,在编译的时候,会转换为include文件,就避免了模板的查找和替换。同时保证了页面的完整性,取代了框架的概念。
第六, 静态页的生成,都是通过入口动态页,把结果页面通过缓存取出来,再写入静态页面,节省了编译模板文件的步骤。
第七, 动态页还是静态页,是通过各个栏目来设置的,可以根据需要实现部分动态
第八, 标签的设置实现了可视化
第九, 样式表实现了统一设置,是通过文件路径参数化来实现,文件路径由皮肤路径和模块名组成,模块是一个比较好的概念,便于有效管理。
第十, 因为采用标准的php语法,数据库中的数据在模版中都是用字段数组的形式表现出来,替代了繁琐的标签设置。
8. 坏处
第一, 优化选项没有集中管理,不利于优化人员的工作。特别是内容页的优化选项,是写在动态页面中的,如果要做调整,必须要更改程序文件,而且不能针对各个模板设置不同的优化选项。可见,在设计框架时,根本就没有考虑内容页的优化,只是考虑了首页和各个栏目页的优化,这可能考虑到了内容页的优化大多数情况下不受重视。
第二, 对页面没有一个清晰的概念,更没有管理页面的功能,都是以模板页面的形式出现。
第三, 模板和标签的概念混淆,模板调用标签,标签又引用模板,这可能会让人弄不明白。而且模板没有明确的分类,只是用标题区分了普通模板和标签模板。
第四, 可以实现页面预览效果,但如果有参数的话,需要输出参数后才能预览,这就比较麻烦,为什么不能实现参数的自动填充呢。
第五, 从一个页面要修改相关模板,必须要点击三次鼠标,一是不直观不方便,二是速度会比较慢,效率比较低。另外,从一个模板中,无法找到调用或者引用它的页面情况。
第六, 无法实时生成静态页,而且不能统一生成静态页,没有实现一键生成,在生成页面的时候没有显示进度条。
第七, 不能实现共享标签,如果只是查询参数不同,就必须重新增加一个标签。不如getinfo函数方便,可以根据具体情况随意调用。当然getinfo函数不够直观,虽然也有可视化界面。
9. 综述
综上所述,该系统的模板引擎在架构上来说还是很大气的,特别是充分利用了PHP语言的特点,把模板文件编译成PHP页面,大大简化了模板引擎。另一方面,由于架构上的限制,只能局限于PHP语法的转换,而无法在转换过程中作更多的工作,在提高速度和开发效率的时候,同时降低了维护的效率和易用性。
五、缓存机制
1. 文本缓存:就是把数据库中的数据,写到文本上,前提是这些数据是固定不变的或者是不经常更新的。在文本上表现的是数组形式,通过cache_read函数返回include文件,再赋值给数组,实现从文本取数据,省略了读取数据库的步骤,大大提高了速度。
2. 静态缓存:和IE的缓存差不多,都有一个缓存周期,在这个周期之内,可以访问静态缓存,不用访问动态页面,提高访问速度。
静态缓存的生成过程:在访问php页面的时候,如果有缓存页面,并且没有失效,就输出静态缓存,否则,开辟一个缓存区域,把php页面输出的内容,写入到缓存页面。该页面的目录和名称是把页面的php文件名称,包括参数,用md5函数加密后得到的字符串。
判断缓存文件是否失效,根据时间戳,生成缓存的时间戳写在缓存页面的开始,输出缓存页面,并不是简单的载入或者包含,而是把时间戳后面的内容echo出来。
3. 压缩功能:需要开启gzip扩展,压缩功能可以减小页面的尺寸,提高访问速度,但对服务器的压力很大,需要有较好的服务器。
4. 好处
第一, 网络访问的瓶颈在数据库的读取上,尽量减少对数据库的读取工作,是缓存的核心任务
第二, 静态缓存在大访问量的时候,速度会有较大的提高,如果访问量不大,最好关闭静态缓存。
六、权限体系
1. 权限的分配
首先,必须先设置会员,然后再设置管理员。会员有会员组,管理员有角色。
其次,说明栏目与模块的区别。栏目是针对前台,模块是针对后台的。栏目的权限分配可以分别针对会员组和角色,模块的权限分配只针对角色。
然后,会员组和角色的权限表是分开的。在登录的时候,会判断是前台还是后台,如果是前台,就取会员组表的权限,如果是后台,就取角色表的权限。
2. 权限的判断
首先,根据用户序号,从文本缓存中取出角色的序号
然后判断该角色是否有权限操作该模块
权限的判断很简单,并没有用权限编号来实现。这个跟系统架构有关,因为整个后台只有一个入口页面,所以通过传递参数的方式,可以实现模块名称,也就相当于模块编号的传递,可以实现统一判断用户的权限,而不必把权限号写在页面中,便于维护。
3. 优点
第一, 实现了权限判断的集中统一,便于维护
第二, 把前后台的权限分离开来,即栏目和模块区分开来,条理比较清晰
第三, 权限设置比较细,有一套完整的审核、编辑权限机制,是和工作流结合起来
4. 缺点
第一, 管理员和会员没有分离,要想设置管理员,必须设置会员,前后台还是没有完全分离。
第二, 设置权限太麻烦,需要针对每个栏目或者模块进行设置,不知道这是不是和工作流结合有关。如果栏目或者模块多了,再加上管理员也多,设置起来会十分繁琐,特别是人员流动频繁的网站,权限分配的可用性很低。角色和会员组没有发挥自身的功能,只是简单的分组,如果加上权限分配功能,对权限进行统一分配,会好很多。
第三, 用户登录后,只是一级权限做了屏蔽,但二级和三级权限是全部都显示出来,对用户来说是十分不友好的。
5. 综述
综上所述,系统的权限体系因为整体框架的限制,存在较大的局限性,较低的可用性,不是做得特别好,有待改进。

PHPCMS2008 权限机制:用户、用户组及角色总结

没有评论

2011 年 03 月 28 日 at 上午 11:18分类:PHPCMS

详细内容:http://blog.csdn.net/zhangjhtt/archive/2011/02/26/6210635.aspx;
1. 用户相关的表:
pcs_member:存储用户基本信息,主要字段:userid,username,password,touserid[与ucenter整合用],groupid …
pcs_member_cache:存储用户基本信息,表结构与pcs_member基本完全一致,不同之处(也是此表存在的意义)是该表 因为pcs_member操作频繁,同时又无法缓存(disk,memcache),会成为系统的瓶颈,所以搞出了 _cache[ENGINE=MEMORY ]来解决这个问题,所有的select都到 _cache中操作。
2. 用户组相关的表:
pcs_member_group:所有组的集合(系统组&扩展组),定义了是否为系统组、用户要在该组中混要交的费用等。
pcs_member_group_extend:存储某个用户在某个扩展组中混的怎么样,交了多少钱,什么时候到期等。
pcs_member_group_priv:定义了我们组->栏目(资源)权限的对应关系,可以看出,组1对catid==11 || 12的资源有browse、input、view的权限。
延伸:该表的一种变体是将priv定义为int(4字节,可表示4294967295种权限).这样该表数据记录将大大减少,并且可以分别权限大小。
3. 角色相关表
pcs_role:管理员都有哪些岗位?

pcs_admin_role: 管理员组中的人也有不同的职能(角色)也可以有多个职能,此表定义管理员的角色。userid==20的人有两项职能。

pcs_admin_role_priv:每个角色对应有哪些权限?roleid==1的角色对module==’contract’的资源有好几项权限。

一般的文章内容页、文章列表页、栏目页静态化
涉及的表:
pcs_content:为所有的栏目内容提供存储“系统属性”,因为有频繁的列表输出,所以将内容(非系统属性,较长的正文部分)放到 pcs_c_xxx 表中。比如所有的modelid==“news”的的栏目,他们的正文和一些属性(不需要在文章列表中出现的属性)都在pcs_c_news表中。
发布一篇新闻时,按照以下的步骤实现缓存:
1. 由catid得到该新闻的模型;
2. 分离系统属性和模型属性,系统属性存入pcs_content表,模型属性存入pcs_c_news表;
3. 根据contentid等生成新闻静态html的url路径,存储该路径到pcs_content.url;
4. 将文章关键词放到pcs_content_tag表;
5. 生成静态文件。

PHPCMS2008后台登陆过程分析

没有评论

2011 年 03 月 25 日 at 下午 5:55分类:PHPCMS

我们要进入后台那么就得运行admin.php这个文件,这个文件就是后台的入口文件。
我们来分析以下从登陆到打开后台页面这个过程中的系统都做了什么。
我们看admin.php这个文件,发现,其实这个文件也是包含了核心文件common.inc.php的,当我们运行的时候,common.inc.php的所有代码都会执行。admin.php文件首先加载了后台需要操作的一些类文件,还加载了后台需要的语言包。
这行代码

if(!isset($file)) $file = 'index';

给出了当前的要操作的文件名。此时刚刚执行admin.php,那么该值为默认的index.继续。。接下来就是判断了

if($_userid && $_groupid == 1 && $_SESSION['is_admin'] == 1)
{
	........
}
elseif($file != 'login')
{
	//最后一个参数1表示直接跳转到指定的页面
	showmessage('请登录!', '?mod=phpcms&file=login&forward='.urlencode(URL),1,1);
}

我们当前的$_userid在common.inc.php文件中设置了为0,这会调用showmessage函数,

//第四个参数判断是否直接跳转而不经过showmessaged的模板
function showmessage($msg, $url_forward = 'goback', $ms = 1250, $direct = 0)
{
    global $PHPCMS;
    if($url_forward && $url_forward != 'goback' && $url_forward != 'close') $url_forward = url($url_forward, 1);
    if($direct && $url_forward && $url_forward!='goback')
    {
        ob_clean();
        header("location:$url_forward");
        exit("<script>self.location='$url_forward';</script>");
    }
    include defined('IN_ADMIN') ? PHPCMS_ROOT.'admin/templates/showmessage.tpl.php' : template('phpcms','showmessage');
    exit;
}

这里的第四个参数设置为1,那么就睡直接跳转到指定的url中。其实他还是跳转到admin.php这个文件中。前面的判断不变。只是在这些判断中都不会有执行判断语句中的代码。因为所有的if条件都是不成立的。这时变化的是$file变量的值为login。当系统运行到下面这两行代码的时候:

/**
*  下面这两句话的意思是,如果我当前的模块不是phpcms的话,那么就先加载当前模块下面的admin.inc.php,
*  然后再加载admin模块下面的admin.inc.php,如果我当前的模块是phpcms的话,那么就直接加载admin下面的admin.inc.php
*/
if($mod != 'phpcms' && !@include PHPCMS_ROOT.$M['path'].'admin/admin.inc.php') 
	showmessage('The file ./'.$M['path'].'admin.inc.php is not exists!');

if(!@include PHPCMS_ROOT.(isset($M['path']) ? $M['path'] : '').'admin/'.$file.'.inc.php') 
	showmessage("The file ./{$M['path']}admin/{$file}.inc.php is not exists!");

这时系统回调用文件admin/login.inc.php…..
我们也切换页面到login.inc.php中。
在这个文件一开始就进行了判断

//当登陆成功后。跳转,执行这行代码
if($_userid && $_groupid == 1 && $_SESSION['is_admin'] == 1) showmessage($LANG['you_are_logined'], '?mod=phpcms&file=index&action=index');

现在这行代码根本不会执行,继续往下。。。接下来有相关权限安全的判断,在这期间,还调用了times类 来记录登陆失败的一些参数设置,方面下面记录入数据库。看如下代码:

if($dosubmit)
{
	。。。。。。
	showmessage($LANG['login_success'], $forward,10,1);//跳转到当前页面
}
else
{
        。。。。。。
	include admin_tpl('login');
}

在有提交动作的时候,$dosubmit是有值的。其实这个是$_GET['dosubmit']在common.inc.php文件中通过extract函数转换来的。首先进入这个页面的时候这个值是空的,所以会执行include admin_tpl(‘login’);加载登陆框的模板文件。之后,当我们点击登陆后,便执行了if里面的代码。这个里面代码我不详细说明,主要调用了member.class.php类进行登陆操作的一些权限判断,同时,如果登陆失败的调用如下代码[

//登陆失败
	if(!$result)
	{	
		if($PHPCMS['maxloginfailedtimes']) $times->add();//记录错误的登陆次数
		$code->add();//记录验证码错误次数
		showmessage($member->msg(), HTTP_REFERER);
	}

,向数据库记录失败的次数。
之后调用admin.class.php文件对用户登陆情况做一些判断。如果都没有问题的话,就执行如下代码:

$_SESSION['is_admin'] = 1;
showmessage($LANG['login_success'], $forward,10,1);//跳转到当前页面

这里showmessage的第四个参数为1,说明也是执行跳转到指定的页面。这里的这个$forward变量为空,那么就是跳转到本页面咯。我们又来执行一下这个页面,着重看这个login.inc.php文件一开始的这段代码:

//当登陆成功后。跳转,执行这行代码
if($_userid && $_groupid == 1 && $_SESSION['is_admin'] == 1) showmessage($LANG['you_are_logined'], '?mod=phpcms&file=index&action=index');

此时这个条件就成立了,又是一个页面跳转。此时跳转到的admin.php文件中。按照之前的分析,这个时候会加载文件index.inc.php了,我们来看下这个文件

switch($action)
{
	case 'main':
。。。。。。
        	break;

	case 'get_msg':
		。。。。。。
		break;

	default:
		。。。。。。
		include admin_tpl('index');
}

这个文件的大致格式就是上面这个样子,我们看到在刚刚的那个url中的action参数的值是index。那么在index.inc.php文件中就是执行default这个选项了,那么就会加载index.tpl.php这个后台模板的首页文件。这样子就显示出后台我们看到的那个操作页面了。其实这个过程可以可以描述为:
admin.php —->首次运行$file=index—->admin.php32行代码成立,执行跳转到本页面—>再次运行$file=login—>加载login.inc.php文件—>首次没有任何操作,加载登陆框模板文件—>登陆框提交到login.inc.php文件,判断有操作动作—>执行登陆相关操作—>登陆成功—>跳转到login.inc.php文件执行login.inc.php的第四行代码—>跳转到admin.php文件—>此时$file=index,amdin.php文件的21行代码成立,32行代码不执行。—->加载index.inc.php文件。加载后台的模板文件。登陆完毕。。。

PHPCMS2008缓存解析–模板缓存

没有评论

2011 年 03 月 25 日 at 上午 11:58分类:PHPCMS

在PHPCMS中的模板缓存和之前在UCHome中的模板缓存几乎就是一个模子刻出来的。
讲到模板缓存离不开讲下模板编译和解析的过程。
在PHPCMS中调用模板的函数是template()这个函数位于global.func.php文件中。

//模版调用函数(模块名,模版名,是否tag)
function template($module = 'phpcms', $template = 'index', $istag = 0)
{
	//根据参数生成cache模板php文件
    $compiledtplfile = TPL_CACHEPATH.$module.'_'.$template.'.tpl.php';
    
	//判断模版生成文件是否过期,或不存在,否则重新生成
    if(TPL_REFRESH && (!file_exists($compiledtplfile) || @filemtime(TPL_ROOT.TPL_NAME.'/'.$module.'/'.$template.'.html') > @filemtime($compiledtplfile) || @filemtime(TPL_ROOT.TPL_NAME.'/tag.inc.php') > @filemtime($compiledtplfile)))
     {
        require_once PHPCMS_ROOT.'include/template.func.php';
        //生成最新模版相关文件
        template_compile($module, $template, $istag);
    }
	//返回编译(可能是刚刚编译的,也可能是之前编译好未过期的)好的.php文件
    return $compiledtplfile;
}

我们在每个模块的php文件中都可以看到类似如下的代码:

include template('module', 'template');

这就是加载模板的意思。模板是同template函数解析过的
我们看看上面的template函数。首先根据规则生成cache的模板文件名:

$compiledtplfile = TPL_CACHEPATH.$module.'_'.$template.'.tpl.php';

然后通过判断这个文件是否存在,是否过了有效期,是否开启了模板缓存的开关。如果没有的话,那么就直接调用这个缓存的模板文件。如果已经过期的话,或者是由于其他的原因的。那么就调用模板解析函数文件template.func.php中的相关函数进行模板的解析。注意这个文件的中的解析的时候,他并不会返回文件的内容,而是返回缓存的模板文件的内容大小。其实返回不返回这个都不所谓的。因为在主要的是在template函数的最后都会 return $compiledtplfile;这样子。其实说白了这个$compiledtplfile有可能是之前的没有过期的,也有可能是过期了然后重新生成的。不管他是怎么回事,经过上面的处理,这个模板缓存文件肯定是存在的。
这样子模板文件的缓存读取和写入就完成了。就这么简单。。。

PHPCMS2008缓存解析–文本缓存

没有评论

2011 年 03 月 25 日 at 上午 11:41分类:PHPCMS

文本缓存,指的是整个站所要经常用到的一些数据,比如在这里的模块数据信息啊以及一些经常要用到的数据。
文本缓存主要用到的函数文件上是cache.func.php文件
这个文件里面全是有关生成文本缓存的函数。文本缓存是个好东西。一般的项目,我们用不着内存缓存 : memcached ,文本搞定。原理是这样的: 我们在后台是不是可以设置很多有关网站的参数。而这些参数很多都是固定的。就不变化的。都存到咱的数据库上。而我们程序那里呢,每次都要访问数据库读出参数来进行我们程序中的操作。首先数据库查询是个很耗硬盘IO资源的一个东西,所以文本缓存刚好能减轻数据库那边的承重。我们在程序开始就把数据库里面的配置都转化为数组 等 放到 php文件里面。这样我们可以直接访问php文件而不用每次都访问数据库了。 php文本缓存其实成了我们程序和数据库的一个中间件。 所以我们自己写自己的文本缓存的时候其实要实现的很简单: 读数据库 -> 写到PHP文件 -> 程序中include /require来吧。开始文本缓存学习
我们先来看下缓存的总操作函数cache_all()

function cache_all()
{
	@set_time_limit(600);
	cache_common();
	cache_module();
	cache_model();
	cache_category();
	cache_area();
	cache_type();
	cache_member_group();
        cache_role();
	cache_author();
	cache_keyword();
	cache_copyfrom();
	cache_pos();
        cache_status();
	cache_workflow();
	tags_update();
	return TRUE;
}

文本缓存的的缓存文件位于data/cache/*下面。这个没有下面的所有文件都是在这个文件里面生成的。
那么系统是什么时候调用这个函数来生成这些缓存的文本文件的呢?我们来看下在common.inc.php文件中的下面这段代码:

//获得模块的相关值
$CACHE = cache_read('common.php');
//如果不存在 那么就重新编译生成
if(!$CACHE)
{
	require_once 'cache.func.php';
	//data/cache/目录下面的所有缓存文件都是通过这个函数生成的
	cache_all();
	$CACHE = cache_read('common.php');
}

这里的这个common.php文件就是在data/cahce/下面。这个文件里面包含的数据非常的多:有模块(MODULE)信息、有模型(model)信息、有全局常量(PHPCMS)信息等等。这里首先获取这个文件的内容,如果为空的话,那么就调用文本缓存文件进行调用函数cache_all()进行文本数据的生成。
在总操作函数中调用的那些函数都是通过sql获取数据库中的数据,然后写入文件中。这些都没有什么可讲的。
这个里面还有个函数cache_table(),主要的功能是生成表的cache文件函数,会将数据库中,按主键ID生成数据表的cache文件,典型例子是 /data/cache/下category_*.php 的文件
在这些文本缓存的过程中会用到几个写和读的函数:
cache_write()、cache_read()、cache_delete()这几个函数的功能是将缓存的文本内容写入文件和读取内容或者删除缓存文本文件的功能。
具体的每一个缓存文本的函数是怎么执行的,都调用的那些数据表这里就不在一一讲解了,因为这些只要看看函数源码就能明白了。
下一贴将分析phpcms模板缓存的实现过程。