diff --git a/build/psalm-baseline.xml b/build/psalm-baseline.xml
index 4b122b002f23b..48a4a7a7db0b9 100644
--- a/build/psalm-baseline.xml
+++ b/build/psalm-baseline.xml
@@ -3566,6 +3566,11 @@
+
+
+ getTypedValue($userId, $app, $key, $default ? 'true' : 'false', $lazy, ValueType::BOOL)]]>
+
+
request->server]]>
diff --git a/lib/private/Config/UserConfig.php b/lib/private/Config/UserConfig.php
index 12222b02ebd9f..8c551afb5cc31 100644
--- a/lib/private/Config/UserConfig.php
+++ b/lib/private/Config/UserConfig.php
@@ -705,7 +705,11 @@ public function getValueBool(
bool $default = false,
bool $lazy = false,
): bool {
- $b = strtolower($this->getTypedValue($userId, $app, $key, $default ? 'true' : 'false', $lazy, ValueType::BOOL));
+ // The explicit (string) cast guards against a PHP OPcache bug where values passed
+ // by reference across function boundaries can have their type corrupted (e.g. bool
+ // returned as int). Affects PHP 8.x with OPcache enabled; fixed upstream in
+ // https://github.com/php/php-src/pull/21973. Keep until minimum PHP version is bumped.
+ $b = strtolower((string)$this->getTypedValue($userId, $app, $key, $default ? 'true' : 'false', $lazy, ValueType::BOOL));
return in_array($b, ['1', 'true', 'yes', 'on']);
}