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 相关文章推荐
模板引擎Smarty深入浅出介绍
Dec 06 PHP
菜鸟学PHP之Smarty入门
Jan 04 PHP
php不用正则采集速度探究总结
Mar 24 PHP
PHP strtotime函数详解
Dec 18 PHP
使用php测试硬盘写入速度示例
Jan 27 PHP
支持中文和其他编码的php截取字符串函数分享(截取中文字符串)
Mar 13 PHP
使用PHP生成二维码的两种方法(带logo图像)
Mar 14 PHP
PHP回溯法解决0-1背包问题实例分析
Mar 23 PHP
PHP二维数组排序简单实现方法
Feb 14 PHP
PHP中$GLOBALS['HTTP_RAW_POST_DATA']和$_POST的区别分析
Jul 03 PHP
laravel 修改记住我功能的cookie保存时间的方法
Oct 14 PHP
php实现对短信验证码发送次数的限制实例讲解
Mar 04 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
dedecms模板标签代码官方参考
2007/03/17 PHP
PHP+jQuery 注册模块的改进(三):更新到Smarty3.1
2014/10/14 PHP
PHP SplObjectStorage使用实例
2015/05/12 PHP
随机显示经典句子或诗歌的javascript脚本
2007/08/04 Javascript
JavaScript 实现类的多种方法实例
2013/05/01 Javascript
Extjs中通过Tree加载右侧TabPanel具体实现
2013/05/05 Javascript
在子窗口中关闭父窗口的一句代码
2013/10/21 Javascript
深入理解JavaScript编程中的原型概念
2015/06/25 Javascript
Bootstrap 填充Json数据的实例代码
2017/01/11 Javascript
原生JS实现垂直手风琴效果
2017/02/19 Javascript
vue.js如何更改默认端口号8080为指定端口的方法
2017/07/14 Javascript
微信小程序实现的一键连接wifi功能示例
2019/04/24 Javascript
微信小程序 腾讯地图SDK 获取当前地址实现解析
2019/08/12 Javascript
vue 解决遍历对象显示的顺序不对问题
2019/11/07 Javascript
详解vue修改elementUI的分页组件视图没更新问题
2020/11/13 Javascript
Python中无限元素列表的实现方法
2014/08/18 Python
python语言使用技巧分享
2016/05/31 Python
浅谈Python里面小数点精度的控制
2018/07/16 Python
Python控制Firefox方法总结
2019/06/03 Python
python中的subprocess.Popen()使用详解
2019/12/25 Python
Django接收照片储存文件的实例代码
2020/03/07 Python
Django-xadmin后台导入json数据及后台显示信息图标和主题更改方式
2020/03/11 Python
Python多线程实现支付模拟请求过程解析
2020/04/21 Python
利用Python实现Excel的文件间的数据匹配功能
2020/06/16 Python
css3的transform中scale缩放详解
2014/12/08 HTML / CSS
HTML5 绘制图像(上)之:关于canvas元素引领下一代web页面的问题
2013/04/24 HTML / CSS
深入探究HTML5的History API
2015/07/09 HTML / CSS
Russell Stover巧克力官方网站:美国领先的精美巧克力制造商
2016/11/27 全球购物
BRASTY捷克:购买香水、化妆品、手袋和手表
2017/07/12 全球购物
德国圣伯纳德草药屋:Kräuterhaus Sanct Bernhard(有中文站)
2018/08/05 全球购物
文秘专业应届生求职信范文
2013/11/14 职场文书
工作失误检讨书(3篇)
2014/10/11 职场文书
大学生心理健康活动总结
2015/05/08 职场文书
2015年乡镇流动人口工作总结
2015/05/12 职场文书
党员学习中国梦心得体会
2016/01/05 职场文书
2016年教育局“我们的节日——端午节”主题活动总结
2016/04/01 职场文书