Когда __destruct не будет вызываться в PHP?

class MyDestructableClass {
   function __construct() {
       print "\nIn constructor\n";
       $this->name = "MyDestructableClass";
   }

   function __destruct() {
       print "\nDestroying " . $this->name . "\n";
   }
}

$obj = new MyDestructableClass();

Когда приведенный выше сценарий находится в сложной среде, __destruct не позвонят, когда exit, но я не могу воспроизвести это легко. Кто-нибудь когда-нибудь замечал это?

РЕДАКТИРОВАТЬ

Я опубликую весь материал здесь, это среда тестирования Symfony, что означает, что вы можете легко воспроизвести его, если вы знакомы с фреймворком:

require_once dirname(__FILE__).'/../bootstrap/Doctrine.php';


$profiler = new Doctrine_Connection_Profiler();

$conn = Doctrine_Manager::connection();
$conn->setListener($profiler);

$t = new lime_test(0, new lime_output_color());

class MyDestructableClass {
   function __construct() {
       print "\nIn constructor\n";
       $this->name = "MyDestructableClass";
   }

   function __destruct() {
       print "\nDestroying " . $this->name . "\n";
   }
}

$obj = new MyDestructableClass();
$news = new News();

$news->setUrl('http://test');
$news->setHash('http://test');
$news->setTitle('http://test');
$news->setSummarize('http://test');
$news->setAccountId(1);
$news->setCategoryId(1);
$news->setThumbnail('http://test');
$news->setCreatedAt(date('Y-m-d H:i:s',time()));
$news->setUpdatedAt(date('Y-m-d H:i:s',time()));
$news->save();
exit();

7 ответов

__destruct не будет называться:

  • Если exit называется в другом деструкторе
  • В зависимости от версии PHP: если exit вызывается в функции отключения, зарегистрированной register_shutdown_function
  • Если где-то в коде есть фатальная ошибка
  • Если другой деструктор выдает исключение
  • Если вы попытаетесь обработать исключение в деструкторе (PHP >= 5.3.0)

Думаю, это все, что я могу думать прямо сейчас

И что сказал Паскаль МАРТИН. Это первый шаг отладки этого.

__destruct метод также не будет вызываться, если скрипт работает на CLI и получает SIGTERM (Ctrl+C)

Отсутствие вывода на экране не означает, что деструктор не вызывается: выход может быть захвачен с использованием output_buffering (может, это делает известь, чтобы иметь возможность работать с ним?), И не отражаться, например, при завершении сценария,

В целях тестирования вы можете попробовать записать файл в __destruct метод, а не просто повторение некоторого текста.
(Просто убедитесь, что ваше приложение / PHP имеет необходимые привилегии для записи в файл назначения)

(Я уже сталкивался с ситуациями, когда я не видел бы вывод, сделанный в деструкторе - но это фактически называлось)

Как сказано в документации PHP:

Деструктор будет вызван, даже если выполнение скрипта остановлено с помощью exit(), призвание exit() в деструкторе предотвратит выполнение оставшихся подпрограмм выключения.

Я знаю, что я немного опоздал на вечеринку, но для людей, которые также хотят получить __destruct для выполнения при возникновении ошибок CTRL+C и / или Fatal, вы можете попробовать это (ниже тестовый пример):

Index.php

<?php

// Setup CTRL+C and System kill message handler
// The only signal that cannot be caught is the SIGKILL (very hard kill)
declare(ticks = 1); // Required else it won't work.
pcntl_signal(SIGTERM, 'close'); // System kill (Unhappy Termination)
pcntl_signal(SIGINT, 'close'); // CTRL+C (Happy Termination)

// Shutdown functions will be executed even on fatal errors
register_shutdown_function('close');

function close($signal = null) // only pcntl_signal fills $signal so null is required
{
    // Check if there was an fatal error (else code below isn't needed)
    $err = error_get_last();
    if(is_array($err))
    {
        foreach(array_keys($GLOBALS) as $key)
        {
            if(in_array($key, ['_GET', '_POST', '_COOKIE', '_FILES', '_SERVER', '_REQUEST', '_ENV', 'GLOBALS']))
                continue;

            // This will automatically call __destruct
            unset($GLOBALS[$key]);
        }
    }
}

// Example
class blah
{
    private $id = '';

    public function __construct()
    {
        $this->id = uniqid();
        // note this piece of code, doesn't work on windows!
        exec('mkdir /tmp/test_'.$this->id);
    }

    public function __destruct()
    {
        // note this piece of code, doesn't work on windows!
        exec('rm /tmp/test_'.$this->id.' -R');
    }
}

// Test
$a = new blah();
$b = new blah();
$c = new blah();
$d = new blah();
$e = new blah();
$f = new blah();
$g = new blah();
$h = new blah();
$i = new blah();
$j = new blah();
$k = new blah();
$l = new blah();
$m = new blah();
$n = new blah();
$o = new blah();
$p = new blah();
$q = new blah();
$r = new blah();
$s = new blah();
$t = new blah();
$u = new blah();
$v = new blah();
$w = new blah();
$x = new blah();
$y = new blah();
$z = new blah();

// The script that causes an fatal error
require_once(__DIR__.'/test.php');

test.php

<?php

// this will create a parse (E_PARSE) error.
asdsaddsaadsasd

Примечание: вызов exit или выдача исключений в деструкторах или функциях выключения приведет к немедленному завершению работы скрипта.

Если вы передаете ссылку на экземпляр класса другому классу и вызываете выход вне этого класса, __destruct класса не будет вызываться. Мне это кажется ошибкой.

      $a = new classa();
$b = new classb($a);  // where classb constructor is __construct(&a) {}
exit;                 

ни деструкторы вызываться не будут, и порядок уничтожения не гарантируется. Я пробовал много способов просто повторить сообщение в деструкторе, и оно никогда не печатается, если я явно не скажу:

      unset($b);     // properly calls classb::__destruct
unset($a);     // properly calls classa::__destruct
exit;

Поскольку я не могу получить никаких результатов, я не могу сказать, является ли это состоянием гонки деструкторов или просто ожидаемыми результатами; В любом случае, unset() всегда правильно вызывает деструктор. Я знаю, что это боль, но лучше, чем жить с этой ошибкой. Им необходимо правильно обрабатывать счетчики ссылок на классы и порядок зависимостей при вызове выхода и вызывать деструкторы в правильном порядке.

Не знакомы с Доктриной, но отметьте один момент: проверьте возможные исключения в __construct()/__destruct(), которые могут привести к фатальным ошибкам.

Другие вопросы по тегам