【问题标题】:Php DestructorsPHP 析构函数
【发布时间】:2011-04-03 17:29:58
【问题描述】:

请给我一些你在课堂上必须使用 __destruct 的真实例子。

【问题讨论】:

  • 为什么票数接近?问题不是那么可怕的 IMO:它要求实际使用,其中在 PHP 中并没有那么多,因为大多数资源和连接都会自动关闭。
  • 这个问题应该被标记为社区维基
  • 我对 Pekka 投了赞成票,我对 daniphp 投了赞成票,并将这个问题标记为最喜欢的,只是因为我认为 Yorirou 错了 :)
  • @Pekka:如前所述,我刚刚使用 PHP 析构函数的方式将是该问题的另一个正确答案,这意味着该问题没有一个完整的答案。这就是我认为人们投票结束这一点的原因。编辑问题以询问“带有真实示例的 __destruct 用例”会更好吗?这样就有一个完整的答案。

标签: php destructor


【解决方案1】:

好的,因为我上一个答案显然没有达到目标,让我再试一次。互联网上有很多关于这个主题的资源和示例。做一些搜索和浏览其他框架的代码,你会看到一些很好的例子......

不要忘记,仅仅因为 PHP 会在终止时为您关闭资源并不意味着当您不再需要它们时明确关闭它们是不好的(或者不关闭它们是好的)...这取决于用例(它是一直使用到最后,还是在早期有一个调用,然后在其余的执行过程中不再需要)...

现在,我们知道__destruct 在对象被销毁时被调用。从逻辑上讲,如果对象被销毁会发生什么?嗯,这意味着它不再可用。因此,如果它有资源打开,那么在它们被销毁时关闭这些资源是否有意义?当然,在一般的网页中,页面很快就会终止,所以让 PHP 关闭它们通常并不可怕。但是,如果由于某种原因脚本长时间运行会发生什么?然后你有资源泄漏。那么,为什么不在不再需要时关闭所有内容(或者考虑到析构函数的范围,当它不再可用时)?

以下是现实世界框架中的一些示例:

  1. Lithium's lithium\net\Socket class
  2. Kohana's Memcached Driver
  3. Joomla's FTP Implementation
  4. Zend Frameworks's SMTP Mail Transport Class
  5. CodeIgniter's TTemplate Class
  6. A Tidy Filter Helper for Cake
  7. A Google-Groups Thread about using Destructors For the Symfony Session Class

有趣的是,Kohana 会跟踪标签,以便以后可以通过“命名空间”删除(而不仅仅是清除缓存)。因此它使用析构函数将这些更改刷新到硬存储中。

CodeIgniter 类还做了一些有趣的事情,它在析构函数的输出流中添加了调试输出。我并不是说这很好,但它是另一种用途的一个例子......

每当我的主控制器上有长时间运行的进程时,我都会使用析构函数。在构造函数中,我检查pid 文件。如果该文件存在(并且它的进程仍在运行),我会抛出一个异常。如果没有,我创建一个具有当前进程 ID 的文件。然后,在析构函数中删除该文件。所以它更多的是清理自己,而不仅仅是释放资源......

【讨论】:

  • 喜欢这个链接列表,它涵盖了这么多已知 PHP 软件。
  • 完整的用例列表 +1
【解决方案2】:

生成 HTML 页面还有另一个方便的用途

class HTMLgenerator {
  function __construct() {
    echo "<html><body>";
  }
  function __destruct() {
    echo "</body></html>";
  }
}

有了这个类,你可以写

$html = new HTMLgenerator();
echo "Hello, world!";

结果是

<html><body>Hello, world!</body></html>

【讨论】:

  • 我认为这不应该在现实世界中使用,看起来像一个黑客。依赖于非常具体的用法。我只是将 HTMLgenerator 实现为在其构造函数中获取一些 HTML,并覆盖 toString 方法。来电者可以简单地说echo new HTMLGenerator()。如果你使用这个 _destructor 回显模式,你最好只在一个地方使用它,否则你会在地狱里试图弄清楚什么时候会输出。简而言之,这是对功能的滥用。
  • 我同意。这只是一个想法,它可能会在一些聪明的头脑中引发一些聪明的想法,所以让这个 exot 活下去吧:-)
  • 很有创意,但确实很臭! :)
【解决方案3】:

例如:

<?php
class Session
{
    protected $data = array();

    public function __construct()
    {
        // load session data from database or file
    }

    // get and set functions

    public function __destruct()
    {
        // store session data in database or file
    }
};

这就是为什么要使用 destruct。您一直阻止读取和写入会话源,并且仅在开始和结束时执行此操作。

【讨论】:

    【解决方案4】:

    我创建了一个 php 页面,它将生成一个电影信息 jpg 文件。此页面必须收集一些信息并运行 inkscape 将模板(svg 文件)转换为 png,然后再转换为 jpg。 svg 包含指向必须是文件的其他图像的相对链接。所以我的页面将必要的文件下载到一个临时文件夹中,转换为 svg 文件。最后,必须删除临时文件夹。

    我将临时文件夹删除放入析构函数中。在页面意外结束的原因有很多之前,我唯一可以确定的是在页面退出时将调用析构函数。

    希望这会有所帮助。

    【讨论】:

      【解决方案5】:

      如果您使用自定义数据库连接器/包装器,析构函数非常有用。

      在构造函数中,可以传递连接信息。因为您可以使用析构函数(而不是终结器等),所以您可以依靠它来为您关闭连接。这更方便,但它确实很有用。

      例如,当 PHP 决定显式“释放”对象(即不再使用)时,它将在那时调用析构函数。这在我描述的场景中更有用,因为您无需等待垃圾收集器运行并调用终结器。

      0.02 美元

      伊恩

      【讨论】:

        【解决方案6】:
        <?php
        class Database
        {
            private $connection;
            private $cache = array();
        
            function __construct([$params])
            {
                //Connection here
            }
        
            //Query
            public function query(Query $Query)
            {
                if($this->is_cached($Query->checksum))
                {
                    return $this->get_cache($Query->checksum);
                }
                //...
            }
            public function __destruct()
            {
                unset($this->connection);
                $this->WriteCache();
                unset($this->cache);
                shutdown_log($this,'Destruction Completed');
            }
        }
        ?>
        

        有一个例子应该让你明白。

        【讨论】:

          【解决方案7】:

          如果您使用fopen() 返回的句柄进行日志记录,您可以使用__destruct() 确保在您的类被销毁时在我们的资源上调用fclose()

          【讨论】:

            【解决方案8】:

            你说得对,__destruct 对于短时间运行的 php 脚本来说是不必要的。数据库连接、文件句柄等在脚本退出时关闭,如果变量超出范围,有时甚至更早关闭。

            我能想到的一个例子是将日志写入数据库。由于我们不想在脚本中某处创建的每个日志条目触发一个查询,因此我们在日志记录类的 __destruct 中编写了“写入数据库”部分,因此当脚本结束时,所有内容都会一次性插入到数据库中。

            另一个例子:如果您允许用户上传文件,析构函数有时是删除临时文件的好地方(以防脚本出现问题,至少可以清理)

            但即使对于文件句柄,它也很有用。我在一个应用程序上工作过,它确实使用了包含在对象中的旧 fopen 等调用,当在大型文件树上使用这些调用时,php 迟早会用完文件句柄,因此在脚本运行时进行清理不仅很好而且很有必要.

            【讨论】:

              【解决方案9】:

              我对大量“低级”对象使用 APC 缓存,否则会占用过多内存;我有一个 cacheCollection 对象,它在脚本执行期间处理这些“低级”对象与 APC 之间的读取和写入。当脚本终止时,必须从 APC 中清除对象,因此我使用 cacheCollection __destruct 方法来执行该功能。

              【讨论】:

              • 你在开玩笑吧 - 否决了提供我自己用法的真实示例,而这正是问题所要求的?您可以争辩说这是一种不恰当的用法,但它绝对是“在野外”的现实生活中的用法,这正是 OP 所要求的。
              【解决方案10】:

              我在包装数据库连接的日志记录类中使用了__destruct()

              <?php
              
              class anyWrap
              {
                private $obj,$calls,$log,$hooks;
                function anyWrap($obj, $logfile = NULL)
                {
                     if(is_null($logfile))
                     {
                       $this->log = dirname(__FILE__) . "/../logs/wrapLog.txt";
                     }
                     $this->hooks = array();
                     $this->dbCalls = 0;
                     $this->obj = $obj;
                }
              
                 public function __set($attri, $val) {
                     $this->obj->$attri = $val;
                 }
              
                 public function __get($attri) {      
                     return $this->obj->$attri;
                 }
                public function __hook($method)
                {
                 $this->hooks[] = $method;
                }
              
              
                 public function __call($name,$args)
                 {
                     $this->calls++;
                     if(in_array($name,$this->hooks))
                     {
                         file_put_contents($this->log,var_export($args,TRUE)."\r\n",FILE_APPEND);
                     }
                     return call_user_func_array(array($this->obj,$name),$args);
                 }
                 //On destruction log diagnostics
                 public function __destruct()
                 {
                      unset($this->dbReal);
                      file_put_contents($this->log,$this->calls."\r\n",FILE_APPEND);
                 }
              }
              

              脚本挂钩到数据库调用并记录准备语句,然后当脚本运行结束时(我不总是知道什么时候),它最终会将数据库调用次数记录到文件中。通过这种方式,我可以查看在数据库上调用了多少次某些函数并相应地计划我的优化。

              【讨论】:

                【解决方案11】:

                如果您在 MySQL 数据库中使用 PHP 脚本创建 视图,则必须在脚本末尾删除该视图.因为如果没有,下次执行该脚本时将不会创建视图,因为数据库中已经存在类似名称的视图。为此,您可以使用析构函数。

                【讨论】:

                • 假设您指的是数据库视图,为什么要在每个执行周期都创建它(并在每次运行后删除它)?为什么不只创建一次并将其留在那里?根据他们的规则,生产代码不应该以CREATEDROP 特权运行,而是单独在每个请求上执行......或者我误解了你的意思? (否则它是使用析构函数的有效示例,所以没有 -1)...
                【解决方案12】:

                这是一个相当不寻常的析构函数用例,我认为像 pest 这样的库正在使用它来将方法链与函数相结合,或者换句话说,实现函数的流畅接口,如下所示:

                <?php
                
                class TestCase {
                    private $message;
                    private $closure;
                    private $endingMessage;
                    public function __construct($message, $closure) {
                        $this->message = $message;
                        $this->closure = $closure;
                    }
                
                    public function addEndingMessage($message) {
                        $this->endingMessage = $message;
                        return $this;
                    }
                    private function getClosure() {
                        return $this->closure;
                    }
                    public function __destruct() {
                        echo $this->message . ' - ';
                        $this->getClosure()();
                        echo  $this->endingMessage ? ' - ' . $this->endingMessage : '';
                        echo "\r\n";
                    }
                }
                
                function it($message, $closure) {
                    return new TestCase($message, $closure);
                }
                
                
                
                it('ok nice', function() {
                    echo 'what to do next?';
                });//outputs: ok nice - what to do next?
                
                it('ok fine', function() {
                    echo 'what should I do?';
                })->addEndingMessage('THE END');//outputs: ok fine - what should I do? - THE END
                

                【讨论】:

                  猜你喜欢
                  • 2018-08-29
                  • 2011-02-16
                  • 2010-09-19
                  • 1970-01-01
                  • 2013-05-06
                  • 1970-01-01
                  • 2012-11-21
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多