这是一种在PHP mvc中缓存查询和构建页面的可接受方式吗?

时间:2013-02-20 14:35:56

标签: php oop caching model-view-controller

下面的代码是否显示了缓存完全构建的页面和数据库查询的可接受方式?

构建页面的缓存以控制器中的__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>

4 个答案:

答案 0 :(得分:8)

“外包”缓存

首先,您必须了解GET请求的缓存通常在整个互联网上完成。特别是如果您的用户通过某种代理连接。

然后还可以设置长的过期时间,以便用户的浏览器可以缓存HTML页面和/或媒体文件。

要开始研究此问题,您应该阅读these two篇文章。

你的目标是什么?

在开始尝试添加缓存之前,请确保确实需要缓存。做一些基准测试,看看你的瓶颈是什么。 为优化而优化是毫无意义且通常有害的

如果你知道(并且有数据备份它......那不是“感觉”)你的应用程序正在进行太多的SQL查询,而不是跳到查询缓存,你应该首先检查一下,这些查询是什么对

  

例如:
如果您看到,为了生成tag cloud而对每个网页视图执行速度缓慢,您应该已经存储了完成标签云(作为HTML片段)并仅在更改内容时更新它。

此外,在尝试提高性能时,“添加缓存”绝不应是您的第一步。如果您的查询速度很慢,请使用EXPLAIN查看他们是否正确使用了索引。确保您没有多次查询相同的数据。如果查询真的有意义,也可以看看。

这不是MVC

我不确定,你在哪里学会这样写,但你似乎忽略了MVC的全部要点:

  • “view”不是模板
  • “model”不是数据库抽象
  • “controller”不是应用程序逻辑

您似乎也忽略了“图层”一词的含义。它不是“类”的同义词。图层是可重复使用的组件组,可在类似情况下重复使用 [1]

您可能会因阅读thisthis帖子而受益。它们应该帮助您理解这种架构模式的基础。

在MVC架构中缓存的位置?

在使用MVC(或MVC启发)架构时,基本上有两点可以进行缓存:视图和持久性逻辑。

对视图进行缓存主要需要重复使用一次渲染的模板(MVC中的每个视图都将处理多个模板和关联的UI逻辑)。请参阅前面标记云的示例。

持久性逻辑中的缓存取决于您的实现。

  • 您可以使用服务缓存应该传递给domain objects的数据:

      

    注意:在实际应用中,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
    
  • 如果将持久层扩展为使用简单映射器,则缓存的另一个点将是存储库和/或标识映射。用一个简单的代码示例解释起来就太难了。相反,您应该阅读Patterns of Enterprise Application Architecture book。

..代码中的其他一些不良做法:

  • 请停止使用单例来建立数据库连接。它使得单元测试成为不可能并导致与类的特定名称的紧密耦合。我建议改为使用this approach并在需要它的类中注入数据库连接。

  • 此外,如果您的查询没有参数,则准备它没有意义。 Prepared语句用于将数据传递给SQL ..但是您的示例都没有任何参数。

    这个问题的出现主要是因为你的魔法数据库类。相反,您应该在多个data mappers中分离持久性逻辑。这样你就不会面对所有查询都有单一方法的自身问题。

  • var关键字是PHP4的工件。现在我们使用publicprivateprotected

  • 您正在对代码中的连接详细信息进行硬编码。这基本上违反了OCP(愚蠢的版本:here)。

答案 1 :(得分:3)

更新

  

这是一种在PHP mvc中缓存查询和构建页面的可接受方式吗?

     

使用控制器中的__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(),则处理将完全停止,并且不会调用其他已注册的关闭函数。&#34;如果某些其他关闭功能在您之前运行,则无法保证您的关机功能会被调用。

恕我直言,你正在重新发明轮子,已经有很多框架。你想要一个简单而小巧的吗?好吧有Silex

是由创建Symfony的同一个人构建的。相信我,你会浪费你的时间。

或者为什么不尝试使用 Laravel 你只能选择你想要的包不是所有的框架 他们已经有一个设计良好的缓存系统,请参阅http://laravel.com/docs/cache

要了解如何在MVC风格中正确完成缓存,Symfony 2的文档有very good explanation

另请阅读此问题Caching strategies in MVC Framework

就任何必要性而言;这真的取决于你的情况。根据我的经验,除非您期望大量用户(例如,同时在您的网站上超过几十个),否则您不需要缓存。另一方面,像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()的更多信息

答案 2 :(得分:2)

这完全取决于您的性能问题所在。如果它们在您的数据库查询中,则缓存这些 - 但当然您的控制器需要为脏数据做好准备。

如果您要在控制器层缓存内容,那么性能会更好,但您可能需要更多缓存内容(您的数据库数据将小于您的HTML)。当然,用户需要为看到脏数据做好准备。

不幸的是,由于每个解决方案都有不同的要求,因此您无法拥有任何严格的规则。我的建议是只在你真正需要时才开始缓存数据。仔细查看性能瓶颈的位置并适当地进行缓存,并确保可以向外扩展(更多机器),而不仅仅是向上扩展(增加机器规格)。

答案 3 :(得分:1)

我添加了CodingInsane发布的内容!

他是对的,你可以轻松制作自己的小缓存机制。

只需将$ output的内容(在CodingInsane帖子中)写入如下文件:the_file.php.cache 并且下次在the_file.php中读取the_file.php.cache的内容并将其显示给用户并退出。

但是,任何缓存机制都需要一种更新(重建页面)的方法。为此,您只需跟踪内容是否已更改,或检查内容的上次​​更改时间,并将其与上次修改the_file.php.cache的时间进行比较filemtime()。

祝你好运!