2025-04-14

比如页面中。需要向用户发邮件和写入用户访问日志等。其实这些并不需要直接表现在页面。因为会影响页面响应速度. 我们可以利用drupal terminate功能将这种功能在terminate event中实现并不需要影响页面输出

terminate功能请看index.php

<?php

/**
 * @file
 * The PHP page that serves all page requests on a Drupal installation.
 *
 * All Drupal code is released under the GNU General Public License.
 * See COPYRIGHT.txt and LICENSE.txt files in the "core" directory.
 */
use Drupal\Core\DrupalKernel;
use Symfony\Component\HttpFoundation\Request;
$autoloader = require_once 'autoload.php';

$kernel = new DrupalKernel('prod', $autoloader);

$request = Request::createFromGlobals();
$response = $kernel->handle($request);
$response->send();

$kernel->terminate($request, $response);

 

在$response->send()了以后其实页面就已经完成了,这里使用的是

fastcgi_finish_request, 这里请看我的另一篇文章。PHP提前响应页面

。下面的$kernel->terminate()这里其实是在后台悄悄完成。drupal提供了event

return [
      KernelEvents::TERMINATE => ['onKernelTerminate'],
 ];

 

功能实现

先定义一个function用来保存需要在terminate中需要调用的函数和参数

function _terminate_run($callback, $arguments = []) {
  $terminate_callbacks = &drupal_static('terminate_callbacks');
  if (!isset($terminate_callbacks)) {
    $terminate_callbacks = [];
  }
  $terminate_callbacks[] = [
    'callback' => $callback,
    'arguments' => $arguments
  ];
}

 

接着定义一个服务。用来执行event

services:
  mymodule.event_subscriber:
    class: Drupal\mymodule\EventSubscriber\AutomatedTaskSubscriber
    tags:
      - { name: event_subscriber }

 

定义这个类

<?php

declare(strict_types=1);

namespace Drupal\mymodule\EventSubscriber;

use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\KernelEvents;

/**
 * @todo Add description for this subscriber.
 */
final class AutomatedTaskSubscriber implements EventSubscriberInterface {


  /**
   * Kernel response event handler.
   */
  public function onKernelTerminate(): void {
    // 通过函数获取保存的callback
    $terminate_callbacks = drupal_static('terminate_callbacks');
    if ($terminate_callbacks) {
      foreach ($terminate_callbacks as $callback_infos) {
        try {
          \Drupal::logger('terminate_callbacks')->notice('terminate callback is running: <pre>' . print_r($callback_infos, true));
          call_user_func($callback_infos['callback'], $callback_infos['arguments']);
        } catch (\Exception $e) {
          \Drupal::logger('terminate_callbacks')->error('terminate callback is error: ' . $e->getMessage());
        }
      }
    }
  }

  /**
   * {@inheritdoc}
   */
  public static function getSubscribedEvents(): array {
    return [
      KernelEvents::TERMINATE => ['onKernelTerminate'],
    ];
  }

}

 

测试

 

function write_file_fuc($time) {
  file_put_contents('write_file_fuc.txt', $time);
}


/**
 * Returns responses for test_code routes.
 */
class TestCodeController extends ControllerBase {



  public function writefile($time) {
    sleep(10);
    file_put_contents('time.txt', $time);
  }

  /**
   * Builds the response.
   */
  public function build() {

    _terminate_run([$this, 'writefile'], [time()]);
    _terminate_run('write_file_fuc', [time()]);
    $build['content'] = [
      '#type' => 'item',
      '#markup' => $this->t('It works!'),
    ];

    return $build;
  }

}

 

这里可以看到。页面响应速度会很快而且不会影响写入一个文件的功能. 就算写入文件需要等10秒也是后端执行

我们通常这里可以用来做一些不影响页面响应但是又要很久的事情。比如发邮件。比如写日志

 

标签: PHP