ThinkPHP 3.2.3实现页面静态化功能的方法详解


Posted in PHP onAugust 03, 2017

前言

大家都知道PHP 的页面静态化有多种实现方式,比如使用输出缓冲(output buffering),该种方式是把数据缓存在 PHP 的缓冲区(内存)中,下一次取数据时直接从缓冲区中读取数据,从而避免了脚本的编译和访问数据库等过程;另一种方式是直接生成静态的 HTML 文件,使用文件读写函数来实现,一些内容不经常改动的页面可以使用静态页面,访客访问到的页面就是真实的 HTML 页面,一些常见的 CMS 会使用该种方法。

以第二种方法为例,参考 DedeCMS 5.7 的静态化功能,在 ThinkPHP 3.2.3 下实现该方法。由于 ThinkPHP 是单入口系统,而且每一个页面都要对应控制器中的某个方法,因此不能直接把静态文件的地址作为实际访问的地址,而是需要在控制器中以模版加载的方式读取静态文件。

首页静态化的实现

在 DedeCMS 5.7 中,可以生成静态的首页、栏目页和文章页。其中首页的生成在后台的“生成”栏目进行设置,包括模板的选择、首页静态文件的存放路径以及首页模式(使用动态首页还是静态首页),DedeCMS 还专门为首页的设置设计了一张表 dede_homepageset,包含的字段包括 templet(模板位置)、position(首页静态文件的路径)、showmod(首页模式),通过在后台进行设置与生成,来控制网站首页使用动态首页还是静态首页,用到的核心文件是 \dede\makehtml_homepage.php。

流程大致是:

① 在后台选择生成静态页面时,通过表单向 makehtml_homepage.php 发送请求,参数包括 dede_homepageset 的所有字段

② 根据传递参数中的 templet、position、showmod 更新 dede_homepageset 表

③ 如果 showmod 是使用静态,加载模板,把模板保存为静态文件。使用的方法是 fopen(),fwrite() 和 fclose(),非常简单

④ 生成了静态页面之后,访客访问的就直接是静态的 index.html,如果首页发生了改变,则手动在后台重新生成一下首页

在 ThinkPHP 中可以这样设计:

config.php

<?php
return array(
 //'配置项'=>'配置值'
 'INDEX_MOD'=>1,//首页模式 0-动态模式 1-静态模式
 'INDEX_HTML_FILE'=>__ROOT__.'Application/Home/View/Index/index_html.html',//静态首页地址
);

/Application/Home/Controller/IndexController.php

<?php
namespace Home\Controller;
use Think\Controller;
class IndexController extends Controller {
 
 //首页
 public function index() {
  if(1 == C('INDEX_MOD')) {
   //静态
   $this->display('index_html');
  } else {
   //动态
   $list = M('category')->select();
   $this->assign('list', $list);
   $this->display('index_php');
  }
 }
 
 //根据动态首页的内容生成静态页面
 public function makehtml_homepage() {
  $homepage = 'http://'.$_SERVER['HTTP_HOST'].U('Home/Index/index_php'); 
  $content = @file_get_contents($homepage);
  file_put_contents(C('INDEX_HTML_FILE'), $content);
 }
 
 //动态首页数据
 public function index_php() {
  C('INDEX_MOD', 0);
  $this->index();
 }
}

模版文件 /Application/Home/View/Index/index_php.php

<!DOCTYPE html>
<html lang="en">
<head>
 <meta charset="UTF-8">
 <title>Document</title>
</head>
<body>
 <volist name="list" id="vo">
  {$vo.cat_name}<br />
 </volist> 
</body>
</html>

在执行 http://ServerName/Home/Index/makehtml_homepage ,即手动生成静态首页后,在 /Application/Home/View/Index/ 路径下生成了静态文件:index_html.html,根据配置文件中设置的 INDEX_MODE 为静态,访问 http://ServerName 实际访问的就是新生成的静态文件。

ThinkPHP 也自带了生成静态文件的方法 buildHtml,使用方法是 buildHtml('生成的静态文件名称', '生成的静态文件路径', '指定要调用的模板文件');

方法在 /ThinkPHP/Library/Think/Controller.class.php,Line 86:

/**
  * 创建静态页面
  * @access protected
  * @htmlfile 生成的静态文件名称
  * @htmlpath 生成的静态文件路径
  * @param string $templateFile 指定要调用的模板文件
  * 默认为空 由系统自动定位模板文件
  * @return string
  */
 protected function buildHtml($htmlfile='',$htmlpath='',$templateFile='') {
  $content = $this->fetch($templateFile);
  $htmlpath = !empty($htmlpath)?$htmlpath:HTML_PATH;
  $htmlfile = $htmlpath.$htmlfile.C('HTML_FILE_SUFFIX');
  Storage::put($htmlfile,$content,'html');
  return $content;
 }

其中 Storage 类在 /ThinkPHP/Library/Think/Storage.class.php

<?php
// +----------------------------------------------------------------------
// | TOPThink [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2013 http://topthink.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
namespace Think;
// 分布式文件存储类
class Storage {

 /**
  * 操作句柄
  * @var string
  * @access protected
  */
 static protected $handler ;

 /**
  * 连接分布式文件系统
  * @access public
  * @param string $type 文件类型
  * @param array $options 配置数组
  * @return void
  */
 static public function connect($type='File',$options=array()) {
  $class = 'Think\\Storage\\Driver\\'.ucwords($type);
  self::$handler = new $class($options);
 }

 static public function __callstatic($method,$args){
  //调用缓存驱动的方法
  if(method_exists(self::$handler, $method)){
   return call_user_func_array(array(self::$handler,$method), $args);
  }
 }
}

默认的文件类型是 File,所以实例化的类的地址在 /ThinkPHP/Library/Think/Storage/Driver/File.class.php

<?php
// +----------------------------------------------------------------------
// | TOPThink [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2013 http://topthink.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
namespace Think\Storage\Driver;
use Think\Storage;
// 本地文件写入存储类
class File extends Storage{

 private $contents=array();

 /**
  * 架构函数
  * @access public
  */
 public function __construct() {
 }

 /**
  * 文件内容读取
  * @access public
  * @param string $filename 文件名
  * @return string  
  */
 public function read($filename,$type=''){
  return $this->get($filename,'content',$type);
 }

 /**
  * 文件写入
  * @access public
  * @param string $filename 文件名
  * @param string $content 文件内容
  * @return boolean   
  */
 public function put($filename,$content,$type=''){
  $dir   = dirname($filename);
  if(!is_dir($dir))
   mkdir($dir,0755,true);
  if(false === file_put_contents($filename,$content)){
   E(L('_STORAGE_WRITE_ERROR_').':'.$filename);
  }else{
   $this->contents[$filename]=$content;
   return true;
  }
 }

 /**
  * 文件追加写入
  * @access public
  * @param string $filename 文件名
  * @param string $content 追加的文件内容
  * @return boolean  
  */
 public function append($filename,$content,$type=''){
  if(is_file($filename)){
   $content = $this->read($filename,$type).$content;
  }
  return $this->put($filename,$content,$type);
 }

 /**
  * 加载文件
  * @access public
  * @param string $filename 文件名
  * @param array $vars 传入变量
  * @return void  
  */
 public function load($_filename,$vars=null){
  if(!is_null($vars))
   extract($vars, EXTR_OVERWRITE);
  include $_filename;
 }

 /**
  * 文件是否存在
  * @access public
  * @param string $filename 文件名
  * @return boolean  
  */
 public function has($filename,$type=''){
  return is_file($filename);
 }

 /**
  * 文件删除
  * @access public
  * @param string $filename 文件名
  * @return boolean  
  */
 public function unlink($filename,$type=''){
  unset($this->contents[$filename]);
  return is_file($filename) ? unlink($filename) : false; 
 }

 /**
  * 读取文件信息
  * @access public
  * @param string $filename 文件名
  * @param string $name 信息名 mtime或者content
  * @return boolean  
  */
 public function get($filename,$name,$type=''){
  if(!isset($this->contents[$filename])){
   if(!is_file($filename)) return false;
   $this->contents[$filename]=file_get_contents($filename);
  }
  $content=$this->contents[$filename];
  $info = array(
   'mtime'  => filemtime($filename),
   'content' => $content
  );
  return $info[$name];
 }
}

可以看到 get 和 put 方法所使用的方法是 file_get_contents() file_put_contents()

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对三水点靠木的支持。

PHP 相关文章推荐
discuz安全提问算法
Jun 06 PHP
php表单转换textarea换行符的方法
Sep 10 PHP
php 验证码(倾斜,正弦干扰线,黏贴,旋转)
Jun 29 PHP
php类声明和php类使用方法示例分享
Mar 29 PHP
php导出CSV抽象类实例
Sep 24 PHP
php实现用于验证所有类型的信用卡类
Mar 24 PHP
php分割合并两个字符串的函数实例
Jun 19 PHP
php+ajax实现仿百度查询下拉内容功能示例
Oct 20 PHP
Laravel框架基于ajax实现二级联动功能示例
Jan 17 PHP
PHP中常用的三种设计模式详解【单例模式、工厂模式、观察者模式】
Jun 14 PHP
PHP实现通过二维数组键值获取一维键名操作示例
Oct 11 PHP
解决windows上php xdebug 无法调试的问题
Feb 19 PHP
PHP7扩展开发教程之Hello World实现方法示例
Aug 03 #PHP
Kindeditor编辑器添加图片上传水印功能(php代码)
Aug 03 #PHP
phpStudy中升级MySQL版本到5.7.17的方法步骤
Aug 03 #PHP
使用PHP json_decode可能遇到的坑与解决方法
Aug 03 #PHP
Yii 2中的load()和save()示例详解
Aug 03 #PHP
Yii2使用表单上传文件的实例代码
Aug 03 #PHP
yii2学习教程之5种内置行为类详解
Aug 03 #PHP
You might like
php项目打包方法
2008/02/18 PHP
php远程下载类分享
2016/04/13 PHP
php无法连接mysql数据库的正确解决方法
2016/07/01 PHP
javascript 极速 隐藏/显示万行表格列只需 60毫秒
2009/03/28 Javascript
jquery文本框中的事件应用以输入邮箱为例
2014/05/06 Javascript
AngularJS中实现动画效果的方法
2016/07/28 Javascript
JAVA Web实时消息后台服务器推送技术---GoEasy
2016/11/04 Javascript
JavaScript数据结构之二叉树的删除算法示例
2017/04/13 Javascript
angular项目中bootstrap-datetimepicker时间插件的使用示例
2018/03/15 Javascript
解决vue js IOS H5focus无法自动弹出键盘的问题
2018/08/30 Javascript
4个顶级开源JavaScript图表库
2018/09/29 Javascript
Vue插件从封装到发布的完整步骤记录
2019/02/28 Javascript
JS实现点击下拉列表文本框中出现对应的网址,点击跳转按钮实现跳转
2019/11/25 Javascript
[02:56]《DAC最前线》之国外战队抵达上海备战亚洲邀请赛
2015/01/28 DOTA
[38:44]DOTA2上海特级锦标赛A组小组赛#2 Secret VS CDEC第二局
2016/02/25 DOTA
[02:12]探秘2016国际邀请赛中国区预选赛选手房间
2016/06/25 DOTA
MySQLdb ImportError: libmysqlclient.so.18解决方法
2014/08/21 Python
Python3中的列表生成式、生成器与迭代器实例详解
2018/06/11 Python
pytorch中交叉熵损失(nn.CrossEntropyLoss())的计算过程详解
2020/01/02 Python
python GUI库图形界面开发之PyQt5切换按钮控件QPushButton详细使用方法与实例
2020/02/28 Python
HTML5 本地存储 LocalStorage详解
2016/06/24 HTML / CSS
南非最大的花卉和送礼服务:NetFlorist
2017/09/13 全球购物
印度在线杂货店:bigbasket
2018/08/23 全球购物
对于没有初始化的变量的初始值可以作怎样的假定
2014/10/12 面试题
Java程序员面试题
2013/07/15 面试题
文明演讲稿范文
2014/05/12 职场文书
项目投资合作意向书
2014/07/29 职场文书
会议接待欢迎标语
2014/10/08 职场文书
幼儿园校车安全责任书
2015/05/08 职场文书
党支部培养考察意见
2015/06/02 职场文书
男人帮观后感
2015/06/18 职场文书
2016年社会主义核心价值观心得体会
2016/01/21 职场文书
2016年小学“公民道德宣传日”活动总结
2016/04/01 职场文书
基于nginx实现上游服务器动态自动上下线无需reload的实现方法
2021/03/31 Servers
SQLServer2008提示评估期已过解决方案
2021/04/12 SQL Server
《勇者辞职不干了》上卷BD发售宣传CM公开
2022/04/08 日漫