下面的代码是否显示了一种可接受的方式来缓存完全构建的页面和数据库查询?
构建页面的缓存从控制器中的__construct
开始,然后用__destruct
结束,在本例中,所有页面默认缓存15分钟到一个文件。
查询缓存是用apc
完成的,它们存储在内存中,每次查询指定的时间量。在实际的站点中,apc缓存会有另一个类,以便在需要时进行更改。
我的目标是构建尽可能简单的mvc,我是失败了还是走上了正确的道路?
控制器
//config
//autoloader
//initialiser -
class controller {
var $cacheUrl;
function __construct(){
$cacheBuiltPage = new cache();
$this->cacheUrl = $cacheBuiltPage->startFullCache();
}
function __destruct(){
$cacheBuiltPage = new cache();
$cacheBuiltPage->endFullCache($this->cacheUrl);
}
}
class forumcontroller extends controller{
function buildForumThread(){
$threadOb = new thread();
$threadTitle = $threadOb->getTitle($data['id']);
require 'thread.php';
}
}
型号
class thread extends model{
public function getTitle($threadId){
$core = Connect::getInstance();
$data = $core->dbh->selectQuery("SELECT title FROM table WHERE id = 1");
return $data;
}
}
数据库
class database {
public $dbh;
private static $dsn = "mysql:host=localhost;dbname=";
private static $user = "";
private static $pass = '';
private static $instance;
private function __construct () {
$this->dbh = new PDO(self::$dsn, self::$user, self::$pass);
}
public static function getInstance(){
if(!isset(self::$instance)){
$object = __CLASS__;
self::$instance = new $object;
}
return self::$instance;
}
public function selectQuery($sql, $time = 0) {
$key = md5('query'.$sql);
if(($data = apc_fetch($key)) === false) {
$stmt = $this->dbh->query($sql);
$data = $stmt->fetchAll();
apc_store($key, $data, $time);
}
return $data;
}
}
缓存
class cache{
var url;
public function startFullCache(){
$this->url = 'cache/'.md5($_SERVER['PHP_SELF'].$_SERVER['QUERY_STRING']);
if((@filesize($this->url) > 1) && (time() - filectime($this->url)) < (60 * 15)){
readfile($this->url);
exit;
}
ob_start();
return $this->url;
}
public function endFullCache($cacheUrl){
$output = ob_get_contents();
ob_end_clean();
$output = sanitize_output($output);
file_put_contents($cacheUrl, $output);
echo $output;
flush();
}
}
查看
<html>
<head>
<title><?=$threadTitle[0]?> Thread - Website</title>
</head>
<body>
<h1><?=$threadTitle[0]?> Thread</h1>
</body>
</html>
"外包"缓存
首先,您必须了解GET
请求的缓存通常在整个互联网上完成。尤其是当您的用户通过某种代理进行连接时。
此外,还可以设置长过期时间,这样用户的浏览器就可以缓存HTML页面和/或媒体文件。
为了开始研究这一点,您应该阅读这两篇文章。
你的目标是什么
在你开始尝试添加缓存之前,确保你确实需要它。做一些基准测试,看看你的瓶颈是什么为了优化而优化是毫无意义的,而且通常;有害。
如果你知道(并且有数据支持它……这与"感觉"无关)你的应用程序正在进行太多的SQL查询,而不是跳到查询缓存,你应该从检查这些查询的目的开始。
例如:
如果您看到,您对每个页面视图执行慢速查询只是为了生成标记云,那么您应该将已经完成的标记云(作为HTML片段)存储起来,并仅在发生更改时更新它
此外,在尝试提高性能时,"添加缓存"永远不应该是您的第一步。如果查询速度较慢,请使用EXPLAIN
查看它们是否正确使用了索引。请确保您没有多次查询同一数据。还要看看,查询是否真的有意义。
这不是MVC
我不确定,你是从哪里学会这样写的,但你似乎错过了MVC的全部要点:
- "视图"不是模板
- "模型"不是数据库抽象
- "控制器"不是应用程序逻辑
你似乎也忽略了"图层"这个词的含义。它不是"类"的同义词。层是可重复使用的组件组,在类似情况下可重复使用[1]。
你可能会从阅读这篇文章和这篇文章中受益。它们应该能帮助您理解这个体系结构模式的基本原理。
MVC架构中的缓存位置
在使用MVC(或受MVC启发的)体系结构时,基本上有两点可以进行缓存:视图和持久性逻辑。
视图缓存主要需要重用一次渲染的模板(MVC中的每个视图都要处理多个模板和相关的UI逻辑)。请参阅前面使用标记云的示例。
持久性逻辑中的缓存将取决于您的实现。
-
您可以在服务中缓存应该传递给域对象的数据:
注意:在实际应用程序中,
new
实例不在此处。相反,您将使用一些工厂$user = new User; $user->setId( 42 ); $cache = new Cache; if ( !$cache->fetch( $user )) { $mapper = new UserMappper; $mapper->fetch( $user ); } $user->setStatus( User::STATUS_BANNED ); $cache->store( $user ); $mapper->store( $user ); // User instance has been populated with data
-
如果将持久性层扩展到不使用简单的映射器,那么缓存的另一个点将是存储库和/或标识映射。用一个简单的代码示例来解释这一点太难了。相反,您应该阅读《企业应用程序架构模式》一书。
代码中的其他一些不良做法:
-
请停止使用singleton建立数据库连接。它使得编写单元测试变得不可能,并导致类的特定名称紧密耦合。我建议使用这种方法,并在需要的类中注入DB连接
-
此外,如果您的查询没有参数,那么准备它就没有意义。准备好的语句用于将数据传递到SQL。。但是您的示例中没有任何参数。
这个问题的出现主要是因为您神奇的数据库类。相反,您应该在多个数据映射器中分离持久性逻辑。这样一来,您就不会面临为所有查询使用单一方法的问题。
-
var
关键字是PHP4的一个工件。现在我们使用public
、private
和protected
。 -
您正在对代码中的连接详细信息进行硬编码。这基本上是违反了OCP(简化版:这里)。
更新
这是在PHPmvc中缓存查询和构建页面的一种可接受的方式吗
构建页面的缓存从控制器中的__construct开始,然后用__destruct结束,在本例中,所有页面默认缓存15分钟到一个文件。
当释放所有引用或脚本终止时,将调用析构函数。我认为这意味着当脚本正确终止时。我想说的是,关键异常不能保证析构函数被调用。
例如
class A
{
public function __construct()
{
echo "Construct'n";
}
public function __destruct()
{
echo "Destruct'n";
}
}
测试代码:
$test = new A();
die( "Dead'n"); // Will output Construct; dead; Destruct
$test = new A();
throw new Exception("Blah'n"); // Construct, Fatal error (no destruct)
$test = new A();
require_once( 'invalid_file.php'); // Construct, Fatal error (no destruct)
您最好使用register_shutdown_function()来确定。但是要小心
可以对register_shutdown_function()进行多次调用,每次调用的顺序与注册时相同。如果在一个注册的关闭函数中调用exit(),则处理将完全停止,并且不会调用其他注册的关闭功能。"如果其他关闭功能在您之前运行,则无法保证您的关闭功能会被调用。
IMHO,你正在重新发明轮子——已经有很多框架了。你想要一个简单小巧的吗?有Silex
它是由创造Symfony的同一个人建造的。相信我,你会浪费时间的。
或者为什么不尝试使用Laravel您可以只选择您想要的包,而不是所有的框架他们已经有了一个设计良好的缓存系统http://laravel.com/docs/cache
为了理解MVC风格的Symfony 2的文档中缓存是如何正确完成的,有一个很好的解释
另请阅读此问题MVC框架中的缓存策略
就这一切的必要性而言;这真的取决于你的情况。根据我的经验,除非你期望有大量的用户(比如说,同时在你的网站上有几十个以上),否则你不需要缓存。另一方面,像StackOverflow这样的网站如果没有经过深思熟虑的缓存策略,就无法正常运行。
How would you create a build function to cache the entire built page?
正如我从你的问题中了解到的,你想缓存整个页面吗?
How would you create a build function to cache the entire built page?
您可以简单地使用输出缓冲来实现这一点。
例如,您有index.php代码:
<?php
ob_start();
// All your mvc and application logic here
$output = ob_get_contents();
ob_end_clean();
ob_start()和ob_get_contents()之间的整个页面输出现在被捕获在$output中
你可以把它保存在一个文件中,然后做任何你想要的
您可以在PHP网站中阅读更多关于ob_get_contents()的信息
这一切都取决于您的性能问题所在。如果它们在您的DB查询中,那么缓存它们——当然,您的控制器需要为脏数据做好准备。
如果您在控制器层缓存内容,那么性能会更好,但您可能会有更多的内容要缓存(您的DB数据将比HTML小)。当然,用户需要为看到脏数据做好准备。
不幸的是,由于每个解决方案都有不同的要求,因此您不能真正制定任何硬性规则。我的建议是,只有在真正需要的时候才开始缓存数据。仔细看看你的性能瓶颈在哪里,适当地进行缓存,并确保你可以向外扩展(更多的机器),而不仅仅是向上扩展(增加机器规格)。
我添加到CodingInsane发布的内容中!
他是对的,这样你就可以很容易地制作自己的小缓存机制。
只需将$output的内容(在CodingInsane的帖子中)写入一个文件中,如:file.php.cache下次在_file.php中读取_file.php缓存的内容并显示给用户并退出。
然而,任何缓存机制都需要一种更新(重建页面)的方法。要做到这一点,您只需要跟踪内容是否发生了更改,或者检查内容上次更改的时间,并将其与file.php.cache上次修改filemtime()的时间进行比较。
祝你好运!