From 3273a22e1d5f74aff243c7d3c806d3f3f0d0a753 Mon Sep 17 00:00:00 2001 From: lucashedding <lucashedding@1463982.no-reply.drupal.org> Date: Wed, 23 Oct 2019 14:54:45 -0600 Subject: [PATCH] Issue #3088474 by heddn: Add an opcache clearing mechanism --- automatic_updates.services.yml | 4 + src/ProjectInfoTrait.php | 4 +- src/ReadinessChecker/OpcodeCache.php | 55 +++++++++++++ src/Services/InPlaceUpdate.php | 12 +++ .../ReadinessChecker/OpcodeCacheTest.php | 81 +++++++++++++++++++ .../Kernel/ReadinessChecker/PhpSapiTest.php | 4 +- 6 files changed, 156 insertions(+), 4 deletions(-) create mode 100644 src/ReadinessChecker/OpcodeCache.php create mode 100644 tests/src/Kernel/ReadinessChecker/OpcodeCacheTest.php diff --git a/automatic_updates.services.yml b/automatic_updates.services.yml index 14de296630..a9381067cc 100644 --- a/automatic_updates.services.yml +++ b/automatic_updates.services.yml @@ -103,6 +103,10 @@ services: - '@extension.list.theme' tags: - { name: readiness_checker, category: warning} + automatic_updates.opcode_cache: + class: Drupal\automatic_updates\ReadinessChecker\OpcodeCache + tags: + - { name: readiness_checker, category: error} automatic_updates.php_sapi: class: Drupal\automatic_updates\ReadinessChecker\PhpSapi arguments: diff --git a/src/ProjectInfoTrait.php b/src/ProjectInfoTrait.php index b2cbd864fd..219ca5f953 100644 --- a/src/ProjectInfoTrait.php +++ b/src/ProjectInfoTrait.php @@ -40,12 +40,12 @@ trait ProjectInfoTrait { $file_paths = $this->getExtensionList($extension_type)->getPathnames(); $infos = $this->getExtensionList($extension_type)->getAllAvailableInfo(); array_walk($infos, function (array &$info, $key) use ($file_paths) { - $info['packaged'] = $info['project'] ?? FALSE; + $info['packaged'] = isset($info['project']) ? $info['project'] : FALSE; $info['install path'] = $file_paths[$key] ? dirname($file_paths[$key]) : ''; $info['project'] = $this->getProjectName($key, $info); $info['version'] = $this->getExtensionVersion($info); }); - $system = $infos['system'] ?? NULL; + $system = isset($infos['system']) ? $infos['system'] : NULL; $infos = array_filter($infos, function (array $info, $project_name) { return $info && $info['project'] === $project_name; }, ARRAY_FILTER_USE_BOTH); diff --git a/src/ReadinessChecker/OpcodeCache.php b/src/ReadinessChecker/OpcodeCache.php new file mode 100644 index 0000000000..2bfe90401c --- /dev/null +++ b/src/ReadinessChecker/OpcodeCache.php @@ -0,0 +1,55 @@ +<?php + +namespace Drupal\automatic_updates\ReadinessChecker; + +use Drupal\Core\StringTranslation\StringTranslationTrait; + +/** + * Error if opcode caching is enabled and updates are executed via CLI. + */ +class OpcodeCache implements ReadinessCheckerInterface { + use StringTranslationTrait; + + /** + * {@inheritdoc} + */ + public function run() { + $messages = []; + if ($this->isCli() && $this->hasOpcodeFileCache()) { + $messages[] = $this->t('Automatic updates cannot run via CLI when opcode file cache is enabled.'); + } + return $messages; + } + + /** + * Determine if PHP is running via CLI. + * + * @return bool + * TRUE if CLI, FALSE otherwise. + */ + protected function isCli() { + return PHP_SAPI === 'cli'; + } + + /** + * Determine if opcode cache is enabled. + * + * If opcache.validate_timestamps is disabled or enabled with + * opcache.revalidate_freq greater then 2, then a site is considered to have + * opcode caching. The default php.ini setup is + * opcache.validate_timestamps=TRUE and opcache.revalidate_freq=2. + * + * @return bool + * TRUE if opcode file cache is enabled, FALSE otherwise. + */ + protected function hasOpcodeFileCache() { + if (!ini_get('opcache.validate_timestamps')) { + return TRUE; + } + if (ini_get('opcache.revalidate_freq') > 2) { + return TRUE; + } + return FALSE; + } + +} diff --git a/src/Services/InPlaceUpdate.php b/src/Services/InPlaceUpdate.php index ce2a909eb5..4b865fab59 100644 --- a/src/Services/InPlaceUpdate.php +++ b/src/Services/InPlaceUpdate.php @@ -153,6 +153,9 @@ class InPlaceUpdate implements UpdateInterface { if (!$success) { $this->rollback($project_root); } + else { + $this->clearOpcodeCache(); + } } } return $success; @@ -561,4 +564,13 @@ class InPlaceUpdate implements UpdateInterface { return new \ArrayIterator($deletions); } + /** + * Clear opcode cache on successful update. + */ + protected function clearOpcodeCache() { + if (function_exists('opcache_reset')) { + opcache_reset(); + } + } + } diff --git a/tests/src/Kernel/ReadinessChecker/OpcodeCacheTest.php b/tests/src/Kernel/ReadinessChecker/OpcodeCacheTest.php new file mode 100644 index 0000000000..3f21105642 --- /dev/null +++ b/tests/src/Kernel/ReadinessChecker/OpcodeCacheTest.php @@ -0,0 +1,81 @@ +<?php + +namespace Drupal\Tests\automatic_updates\Kernel\ReadinessChecker; + +use Drupal\KernelTests\KernelTestBase; + +/** + * Tests opcode caching and execution via CLI. + * + * @group automatic_updates + */ +class OpcodeCacheTest extends KernelTestBase { + + /** + * {@inheritdoc} + */ + public static $modules = [ + 'automatic_updates', + ]; + + /** + * Tests the functionality of supported PHP version readiness checks. + * + * @dataProvider opcodeCacheProvider + */ + public function testOpcodeCache($ini, $ini_value, $failure) { + ini_set($ini, $ini_value); + $messages = $this->container->get('automatic_updates.opcode_cache')->run(); + if ($failure) { + $this->assertNotEmpty($messages); + $this->assertEquals((string) $messages[0], 'Automatic updates cannot run via CLI when opcode file cache is enabled.'); + } + else { + $this->assertEmpty($messages); + } + } + + /** + * Data provider for opcode cache testing. + */ + public function opcodeCacheProvider() { + $datum[] = [ + 'ini' => 'opcache.validate_timestamps', + 'ini_value' => 0, + 'failure' => TRUE, + ]; + $datum[] = [ + 'ini' => 'opcache.validate_timestamps', + 'ini_value' => 1, + 'failure' => FALSE, + ]; + $datum[] = [ + 'ini' => 'opcache.validate_timestamps', + 'ini_value' => FALSE, + 'failure' => TRUE, + ]; + $datum[] = [ + 'ini' => 'opcache.validate_timestamps', + 'ini_value' => TRUE, + 'failure' => FALSE, + ]; + $datum[] = [ + 'ini' => 'opcache.validate_timestamps', + 'ini_value' => 2, + 'failure' => FALSE, + ]; + $datum[] = [ + 'ini' => 'opcache.revalidate_freq', + 'ini_value' => 3, + 'failure' => TRUE, + ]; + $datum[] = [ + 'ini' => 'opcache.revalidate_freq', + 'ini_value' => 2, + 'failure' => FALSE, + ]; + + return $datum; + } + +} diff --git a/tests/src/Kernel/ReadinessChecker/PhpSapiTest.php b/tests/src/Kernel/ReadinessChecker/PhpSapiTest.php index 3eb397519e..1bc09c1078 100644 --- a/tests/src/Kernel/ReadinessChecker/PhpSapiTest.php +++ b/tests/src/Kernel/ReadinessChecker/PhpSapiTest.php @@ -19,9 +19,9 @@ class PhpSapiTest extends KernelTestBase { ]; /** - * Tests the functionality of supported PHP version readiness checks. + * Tests PHP SAPI changes. */ - public function testSupportedPhpVersion() { + public function testPhpSapiChanges() { $messages = $this->container->get('automatic_updates.php_sapi')->run(); $this->assertEmpty($messages); $messages = $this->container->get('automatic_updates.php_sapi')->run(); -- GitLab