码库记事本

码库记事本

PHPCMS源码分析

小诸哥 0

PHPCMS

一、模版引擎

如:调用单页面index.php?m=content&c=index&a=lists&catid=9.
1.先获取到模版变量的值$template_list="list";然后通过$type!=0来判断是单页面,
   然后通过$template = $setting[\'page_template\'] ? $setting[\'page_template\'] : \'page\';获取单页免的模版page.html.

2.引入模版include template(\'content\',$template);

3.查看template()函数:定义在/phpcms/libs/functions/global.func.php.
  template($module = \'content\', $template = \'index\', $style = \'\');
  传入的三个参数分别是:
  (1)module;
  (2)模版,比如单页面page.html;
  (3)模版风格,就是/phpcms/templates/下面的目录名,比如default,che.
  该函数实现的功能:
  (1)通过file_exists($compiledtplfile):判断模版编译文件是否存在;
  (2)通过filemtime()判断模版文件的生成时间是否大于模版编译文件;
  以上两点如果(||连接)为真,则通过$template_cache->template_compile($module, $template, $style);编译模版文件;

4.查看template_compile()函数:定义在/phpcms/libs/classes/template_cache.class.php.
  template_compile($module, $template, $style = \'default\');
  传入的三个参数分别是:
  (同上)
  该函数实现的功能:
  (1)通过$content = @file_get_contents ( $tplfile );读取模版源文件的内容;
  (2)通过$content = $this->template_parse($content);正则匹配替换掉标签,将标签替换为php代码:
  (3)通过$strlen = file_put_contents ( $compiledtplfile, $content );将替换后的字符串写入模版编译文件;
  (4)返回$strlen,即用include template(\'content\',$template);将$strlen引入到控制器代码里。其本质就是php和html代码混编。

5.模版解析:通过template_parse($str)用正则替换标签。

    /**
     * 解析模板
     *
     * @param $str    模板内容
     * @return ture
     */
    public function template_parse($str) {
        $str = preg_replace ( "/\{template\s+(.+)\}/", "<?php include template(\\1); ?>", $str );
        $str = preg_replace ( "/\{include\s+(.+)\}/", "<?php include \\1; ?>", $str );
        $str = preg_replace ( "/\{php\s+(.+)\}/", "<?php \\1?>", $str );
        $str = preg_replace ( "/\{if\s+(.+?)\}/", "<?php if(\\1) { ?>", $str );
        $str = preg_replace ( "/\{else\}/", "<?php } else { ?>", $str );
        $str = preg_replace ( "/\{elseif\s+(.+?)\}/", "<?php } elseif (\\1) { ?>", $str );
        $str = preg_replace ( "/\{\/if\}/", "<?php } ?>", $str );
        //for 循环
        $str = preg_replace("/\{for\s+(.+?)\}/","<?php for(\\1) { ?>",$str);
        $str = preg_replace("/\{\/for\}/","<?php } ?>",$str);
        //++ --
        $str = preg_replace("/\{\+\+(.+?)\}/","<?php ++\\1; ?>",$str);
        $str = preg_replace("/\{\-\-(.+?)\}/","<?php ++\\1; ?>",$str);
        $str = preg_replace("/\{(.+?)\+\+\}/","<?php \\1++; ?>",$str);
        $str = preg_replace("/\{(.+?)\-\-\}/","<?php \\1--; ?>",$str);
        $str = preg_replace ( "/\{loop\s+(\S+)\s+(\S+)\}/", "<?php \$n=1;if(is_array(\\1)) foreach(\\1 AS \\2) { ?>", $str );
        $str = preg_replace ( "/\{loop\s+(\S+)\s+(\S+)\s+(\S+)\}/", "<?php \$n=1; if(is_array(\\1)) foreach(\\1 AS \\2 => \\3) { ?>", $str );
        $str = preg_replace ( "/\{\/loop\}/", "<?php \$n++;}unset(\$n); ?>", $str );
        $str = preg_replace ( "/\{([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff:]*\(([^{}]*)\))\}/", "<?php echo \\1;?>", $str );
        $str = preg_replace ( "/\{\\$([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff:]*\(([^{}]*)\))\}/", "<?php echo \\1;?>", $str );
        $str = preg_replace ( "/\{(\\$[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)\}/", "<?php echo \\1;?>", $str );
        $str = preg_replace("/\{(\\$[a-zA-Z0-9_\[\]\\'\"\$\x7f-\xff]+)\}/es", "\$this->addquote(\'<?php echo \\1;?>\')",$str);
        $str = preg_replace ( "/\{([A-Z_\x7f-\xff][A-Z0-9_\x7f-\xff]*)\}/s", "<?php echo \\1;?>", $str );
        $str = preg_replace("/\{pc:(\w+)\s+([^}]+)\}/ie", "self::pc_tag(\'$1\',\'$2\', \'$0\')", $str);
        $str = preg_replace("/\{\/pc\}/ie", "self::end_pc_tag()", $str);
        $str = "<?php defined(\'IN_PHPCMS\') or exit(\'No permission resources.\'); ?>" . $str;
        return $str;
    }

      关键点:1.\s 空格匹配的使用;
                 2.\1  \2分组的使用;
                 3.替换为self::pc_tag函数的使用。


 二、框架实现思路(参考:http://www.tuicool.com/articles/BvU3i2v)
  通过单一入口的pc_base::create_app()来创建一个应用,调用不同的类库处理不同的应用;
      当处理application时:调用框架类库文件下的application.class.php文件,执行构造函数,加载param路由类,使用路由类中的$param->route_m(),$param->route_c(),$param->route_a()获取模块和控制器以及方法,route_m为模块,在modules文件下,然后是控制器和方法,标准的mvc结构。然后执行int函数,执行load_controller加载获取到的控制器并且实例化!

 

三、外部引用来调用phpcms的类库

include_once \'../phpcms/base.php\';//引入主文件
pc_base::load_sys_class(\'model\', \'\', 0);//加载model类
class Api extends model {//继承model类,即调用model的各种数据库方法
    public function __construct() {
        $this->db_config = include "../config/database.php";//此处得包含进数据库配置文件
        $this->db_setting = \'default\';
        //$this->table_name = \'admin\'; 
        parent::__construct();
    }
}
$apiDb = new Api();

$adpos = isset($_GET[\'adpos\'])&&!empty($_GET[\'adpos\'])?$_GET[\'adpos\']:"0";
if($adpos=="login"){
    $spaceid = 25;
}elseif ($adpos=="register"){
    $spaceid = 23;
}else{
    $spaceid = 0;
}

$sql = "SELECT s.width,s.height,p.setting FROM `u8_poster_space` s LEFT JOIN `u8_poster` p ON s.spaceid=p.spaceid WHERE p.spaceid=$spaceid";

$res = $apiDb->db->get_ones($sql);//根据sql语句获取一行结果集

 

四、数据库实现原理

1.三大类
 mysql.class.php 数据库实现类 :final class mysql;
 db_factory.class.php 数据库工厂类 :final class db_factory;
 model.class.php 数据模型基类 :class model。

2.实现流程(/phpcms/libs/class/)
  (1)mysql.class.php提供数据库连接、查询、执行等各种实现方法;

/**
     * 真正开启数据库连接
     *             
     * @return void
     */
    public function connect() {
        $func = $this->config[\'pconnect\'] == 1 ? \'mysql_pconnect\' : \'mysql_connect\';
        if(!$this->link = @$func($this->config[\'hostname\'], $this->config[\'username\'], $this->config[\'password\'], 1)) {
            $this->halt(\'Can not connect to MySQL server\');
            return false;
        }

        if($this->version() > \'4.1\') {
            $charset = isset($this->config[\'charset\']) ? $this->config[\'charset\'] : \'\';
            $serverset = $charset ? "character_set_connection=\'$charset\',character_set_results=\'$charset\',character_set_client=binary" : \'\';
            $serverset .= $this->version() > \'5.0.1\' ? ((empty($serverset) ? \'\' : \',\')." sql_mode=\'\' ") : \'\';
            $serverset && mysql_query("SET $serverset", $this->link);        
        }

        if($this->config[\'database\'] && !@mysql_select_db($this->config[\'database\'], $this->link)) {
            $this->halt(\'Cannot use database \'.$this->config[\'database\']);
            return false;
        }
        $this->database = $this->config[\'database\'];
        return $this->link;
    }

  (2)db_factory.class.php用get_instance($db_config = \'\')方法返回当前工厂类,用get_database($db_name)方法返回数据库操作实例(它调用工厂类的connect($db_name)来实现工厂类原理),

/**
     *  加载数据库驱动
     * @param $db_name     数据库配置名称
     * @return object
     */
    public function connect($db_name) {
        $object = null;
        switch($this->db_config[$db_name][\'type\']) {
            case \'mysql\' :
                pc_base::load_sys_class(\'mysql\', \'\', 0);
                $object = new mysql();
                break;
            case \'mysqli\' :
                $object = pc_base::load_sys_class(\'mysqli\');
                break;
            case \'access\' :
                $object = pc_base::load_sys_class(\'db_access\');
                break;
            default :
                pc_base::load_sys_class(\'mysql\', \'\', 0);
                $object = new mysql();
        }
        $object->open($this->db_config[$db_name]);
        return $object;
    }

  (3)model.class.php加载工厂类(pc_base::load_sys_class(\'db_factory\', \'\', 0);),提供数据库的curd操作方法。

$this->db = db_factory::get_instance($this->db_config)->get_database($this->db_setting);//获取数据库实例

  总结:通过工厂类的get_instance()获取当前实例,调用工厂方法的get_database()连接数据库并返回数据库操作实例,并返回给model的$this->db,最后所有方法的调用直接通过$this->db调用model里的方法即可。

标签: 源码分析