<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[59803] trunk: Security: Explicitly require the `hash` PHP extension and add requirement checks during installation and upgrade.</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { white-space: pre-line; overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta" style="font-size: 105%">
<dt style="float: left; width: 6em; font-weight: bold">Revision</dt> <dd><a style="font-weight: bold" href="https://core.trac.wordpress.org/changeset/59803">59803</a><script type="application/ld+json">{"@context":"http://schema.org","@type":"EmailMessage","description":"Review this Commit","action":{"@type":"ViewAction","url":"https://core.trac.wordpress.org/changeset/59803","name":"Review Commit"}}</script></dd>
<dt style="float: left; width: 6em; font-weight: bold">Author</dt> <dd>johnbillion</dd>
<dt style="float: left; width: 6em; font-weight: bold">Date</dt> <dd>2025-02-11 11:12:03 +0000 (Tue, 11 Feb 2025)</dd>
</dl>

<pre style='padding-left: 1em; margin: 2em 0; border-left: 2px solid #ccc; line-height: 1.25; font-size: 105%; font-family: sans-serif'>Security: Explicitly require the `hash` PHP extension and add requirement checks during installation and upgrade.

This extension provides the `hash()` function and support for the SHA-256 algorithm, both of which are required for upcoming security related changes. This extension is almost universally enabled, however it is technically possible to disable it on PHP 7.2 and 7.3, hence the introduction of this requirement and the corresponding requirement checks prior to installing or upgrading WordPress.

Props peterwilsoncc, ayeshrajans, dd32, SergeyBiryukov, johnbillion.

Fixes <a href="https://core.trac.wordpress.org/ticket/60638">#60638</a>, <a href="https://core.trac.wordpress.org/ticket/62815">#62815</a>, <a href="https://core.trac.wordpress.org/ticket/56017">#56017</a>

See <a href="https://core.trac.wordpress.org/ticket/21022">#21022</a></pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkcomposerjson">trunk/composer.json</a></li>
<li><a href="#trunksrcwpadminincludesclasswpsitehealthphp">trunk/src/wp-admin/includes/class-wp-site-health.php</a></li>
<li><a href="#trunksrcwpadminincludesupdatecorephp">trunk/src/wp-admin/includes/update-core.php</a></li>
<li><a href="#trunksrcwpadmininstallphp">trunk/src/wp-admin/install.php</a></li>
<li><a href="#trunksrcwpadminupgradephp">trunk/src/wp-admin/upgrade.php</a></li>
<li><a href="#trunksrcwpincludesclasswpsessiontokensphp">trunk/src/wp-includes/class-wp-session-tokens.php</a></li>
<li><a href="#trunksrcwpincludesclasswpdbphp">trunk/src/wp-includes/class-wpdb.php</a></li>
<li><a href="#trunksrcwpincludescompatphp">trunk/src/wp-includes/compat.php</a></li>
<li><a href="#trunksrcwpincludesloadphp">trunk/src/wp-includes/load.php</a></li>
<li><a href="#trunksrcwpincludespluggablephp">trunk/src/wp-includes/pluggable.php</a></li>
<li><a href="#trunksrcwpincludesversionphp">trunk/src/wp-includes/version.php</a></li>
<li><a href="#trunksrcwpsettingsphp">trunk/src/wp-settings.php</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkcomposerjson"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/composer.json</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/composer.json       2025-02-10 22:27:49 UTC (rev 59802)
+++ trunk/composer.json 2025-02-11 11:12:03 UTC (rev 59803)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -10,6 +10,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">                "issues": "https://core.trac.wordpress.org/"
</span><span class="cx" style="display: block; padding: 0 10px">        },
</span><span class="cx" style="display: block; padding: 0 10px">        "require": {
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                "ext-hash": "*",
</ins><span class="cx" style="display: block; padding: 0 10px">                 "ext-json": "*",
</span><span class="cx" style="display: block; padding: 0 10px">                "php": ">=7.2.24"
</span><span class="cx" style="display: block; padding: 0 10px">        },
</span></span></pre></div>
<a id="trunksrcwpadminincludesclasswpsitehealthphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-admin/includes/class-wp-site-health.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-admin/includes/class-wp-site-health.php      2025-02-10 22:27:49 UTC (rev 59802)
+++ trunk/src/wp-admin/includes/class-wp-site-health.php        2025-02-11 11:12:03 UTC (rev 59803)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -923,7 +923,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        ),
</span><span class="cx" style="display: block; padding: 0 10px">                        'hash'      => array(
</span><span class="cx" style="display: block; padding: 0 10px">                                'function' => 'hash',
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                'required' => false,
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                         'required' => true,
</ins><span class="cx" style="display: block; padding: 0 10px">                         ),
</span><span class="cx" style="display: block; padding: 0 10px">                        'imagick'   => array(
</span><span class="cx" style="display: block; padding: 0 10px">                                'extension' => 'imagick',
</span></span></pre></div>
<a id="trunksrcwpadminincludesupdatecorephp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-admin/includes/update-core.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-admin/includes/update-core.php       2025-02-10 22:27:49 UTC (rev 59802)
+++ trunk/src/wp-admin/includes/update-core.php 2025-02-11 11:12:03 UTC (rev 59803)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1009,9 +1009,6 @@
</span><span class="cx" style="display: block; padding: 0 10px">  * @global array              $_old_requests_files
</span><span class="cx" style="display: block; padding: 0 10px">  * @global array              $_new_bundled_files
</span><span class="cx" style="display: block; padding: 0 10px">  * @global wpdb               $wpdb                   WordPress database abstraction object.
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- * @global string             $wp_version
- * @global string             $required_php_version
- * @global string             $required_mysql_version
</del><span class="cx" style="display: block; padding: 0 10px">  *
</span><span class="cx" style="display: block; padding: 0 10px">  * @param string $from New release unzipped path.
</span><span class="cx" style="display: block; padding: 0 10px">  * @param string $to   Path to old WordPress installation.
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1075,7 +1072,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        /*
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-         * Import $wp_version, $required_php_version, and $required_mysql_version from the new version.
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+  * Import $wp_version, $required_php_version, $required_php_extensions, and $required_mysql_version from the new version.
</ins><span class="cx" style="display: block; padding: 0 10px">          * DO NOT globalize any variables imported from `version-current.php` in this function.
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><span class="cx" style="display: block; padding: 0 10px">         * BC Note: $wp_filesystem->wp_content_dir() returned unslashed pre-2.8.
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1181,17 +1178,29 @@
</span><span class="cx" style="display: block; padding: 0 10px">                );
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        // Add a warning when the JSON PHP extension is missing.
-       if ( ! extension_loaded( 'json' ) ) {
-               return new WP_Error(
-                       'php_not_compatible_json',
-                       sprintf(
-                               /* translators: 1: WordPress version number, 2: The PHP extension name needed. */
-                               __( 'The update cannot be installed because WordPress %1$s requires the %2$s PHP extension.' ),
-                               $wp_version,
-                               'JSON'
-                       )
-               );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ if ( isset( $required_php_extensions ) && is_array( $required_php_extensions ) ) {
+               $missing_extensions = new WP_Error();
+
+               foreach ( $required_php_extensions as $extension ) {
+                       if ( extension_loaded( $extension ) ) {
+                               continue;
+                       }
+
+                       $missing_extensions->add(
+                               "php_not_compatible_{$extension}",
+                               sprintf(
+                                       /* translators: 1: WordPress version number, 2: The PHP extension name needed. */
+                                       __( 'The update cannot be installed because WordPress %1$s requires the %2$s PHP extension.' ),
+                                       $wp_version,
+                                       $extension
+                               )
+                       );
+               }
+
+               // Add a warning when required PHP extensions are missing.
+               if ( $missing_extensions->has_errors() ) {
+                       return $missing_extensions;
+               }
</ins><span class="cx" style="display: block; padding: 0 10px">         }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        /** This filter is documented in wp-admin/includes/update-core.php */
</span></span></pre></div>
<a id="trunksrcwpadmininstallphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-admin/install.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-admin/install.php    2025-02-10 22:27:49 UTC (rev 59802)
+++ trunk/src/wp-admin/install.php      2025-02-11 11:12:03 UTC (rev 59803)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -232,12 +232,13 @@
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px"> /**
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- * @global string $wp_version             The WordPress version string.
- * @global string $required_php_version   The required PHP version string.
- * @global string $required_mysql_version The required MySQL version string.
- * @global wpdb   $wpdb                   WordPress database abstraction object.
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ * @global string   $wp_version              The WordPress version string.
+ * @global string   $required_php_version    The required PHP version string.
+ * @global string[] $required_php_extensions The names of required PHP extensions.
+ * @global string   $required_mysql_version  The required MySQL version string.
+ * @global wpdb     $wpdb                    WordPress database abstraction object.
</ins><span class="cx" style="display: block; padding: 0 10px">  */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-global $wp_version, $required_php_version, $required_mysql_version, $wpdb;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+global $wp_version, $required_php_version, $required_php_extensions, $required_mysql_version, $wpdb;
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px"> $php_version   = PHP_VERSION;
</span><span class="cx" style="display: block; padding: 0 10px"> $mysql_version = $wpdb->db_version();
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -298,6 +299,29 @@
</span><span class="cx" style="display: block; padding: 0 10px">        die( '<h1>' . __( 'Requirements Not Met' ) . '</h1><p>' . $compat . '</p></body></html>' );
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+if ( isset( $required_php_extensions ) && is_array( $required_php_extensions ) ) {
+       $missing_extensions = array();
+
+       foreach ( $required_php_extensions as $extension ) {
+               if ( extension_loaded( $extension ) ) {
+                       continue;
+               }
+
+               $missing_extensions[] = sprintf(
+                       /* translators: 1: URL to WordPress release notes, 2: WordPress version number, 3: The PHP extension name needed. */
+                       __( 'You cannot install because <a href="%1$s">WordPress %2$s</a> requires the %3$s PHP extension.' ),
+                       $version_url,
+                       $wp_version,
+                       $extension
+               );
+       }
+
+       if ( count( $missing_extensions ) > 0 ) {
+               display_header();
+               die( '<h1>' . __( 'Requirements Not Met' ) . '</h1><p>' . implode( '</p><p>', $missing_extensions ) . '</p></body></html>' );
+       }
+}
+
</ins><span class="cx" style="display: block; padding: 0 10px"> if ( ! is_string( $wpdb->base_prefix ) || '' === $wpdb->base_prefix ) {
</span><span class="cx" style="display: block; padding: 0 10px">        display_header();
</span><span class="cx" style="display: block; padding: 0 10px">        die(
</span></span></pre></div>
<a id="trunksrcwpadminupgradephp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-admin/upgrade.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-admin/upgrade.php    2025-02-10 22:27:49 UTC (rev 59802)
+++ trunk/src/wp-admin/upgrade.php      2025-02-11 11:12:03 UTC (rev 59803)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -36,12 +36,13 @@
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px"> /**
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- * @global string $wp_version             The WordPress version string.
- * @global string $required_php_version   The required PHP version string.
- * @global string $required_mysql_version The required MySQL version string.
- * @global wpdb   $wpdb                   WordPress database abstraction object.
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ * @global string   $wp_version              The WordPress version string.
+ * @global string   $required_php_version    The required PHP version string.
+ * @global string[] $required_php_extensions The names of required PHP extensions.
+ * @global string   $required_mysql_version  The required MySQL version string.
+ * @global wpdb     $wpdb                    WordPress database abstraction object.
</ins><span class="cx" style="display: block; padding: 0 10px">  */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-global $wp_version, $required_php_version, $required_mysql_version, $wpdb;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+global $wp_version, $required_php_version, $required_php_extensions, $required_mysql_version, $wpdb;
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px"> $step = (int) $step;
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -54,6 +55,24 @@
</span><span class="cx" style="display: block; padding: 0 10px">        $mysql_compat = version_compare( $mysql_version, $required_mysql_version, '>=' );
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+$missing_extensions = array();
+
+if ( isset( $required_php_extensions ) && is_array( $required_php_extensions ) ) {
+       foreach ( $required_php_extensions as $extension ) {
+               if ( extension_loaded( $extension ) ) {
+                       continue;
+               }
+
+               $missing_extensions[] = sprintf(
+                       /* translators: 1: URL to WordPress release notes, 2: WordPress version number, 3: The PHP extension name needed. */
+                       __( 'You cannot upgrade because <a href="%1$s">WordPress %2$s</a> requires the %3$s PHP extension.' ),
+                       $version_url,
+                       $wp_version,
+                       $extension
+               );
+       }
+}
+
</ins><span class="cx" style="display: block; padding: 0 10px"> header( 'Content-Type: ' . get_option( 'html_type' ) . '; charset=' . get_option( 'blog_charset' ) );
</span><span class="cx" style="display: block; padding: 0 10px"> ?>
</span><span class="cx" style="display: block; padding: 0 10px"> <!DOCTYPE html>
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -126,8 +145,8 @@
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        echo '<p>' . $message . '</p>';
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        ?>
-       <?php
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+elseif ( count( $missing_extensions ) > 0 ) :
+       echo '<p>' . implode( '</p><p>', $missing_extensions ) . '</p>';
</ins><span class="cx" style="display: block; padding: 0 10px"> else :
</span><span class="cx" style="display: block; padding: 0 10px">        switch ( $step ) :
</span><span class="cx" style="display: block; padding: 0 10px">                case 0:
</span></span></pre></div>
<a id="trunksrcwpincludesclasswpsessiontokensphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-includes/class-wp-session-tokens.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/class-wp-session-tokens.php 2025-02-10 22:27:49 UTC (rev 59802)
+++ trunk/src/wp-includes/class-wp-session-tokens.php   2025-02-11 11:12:03 UTC (rev 59803)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -68,12 +68,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">         * @return string A hash of the session token (a verifier).
</span><span class="cx" style="display: block; padding: 0 10px">         */
</span><span class="cx" style="display: block; padding: 0 10px">        private function hash_token( $token ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                // If ext/hash is not present, use sha1() instead.
-               if ( function_exists( 'hash' ) ) {
-                       return hash( 'sha256', $token );
-               } else {
-                       return sha1( $token );
-               }
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         return hash( 'sha256', $token );
</ins><span class="cx" style="display: block; padding: 0 10px">         }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        /**
</span></span></pre></div>
<a id="trunksrcwpincludesclasswpdbphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-includes/class-wpdb.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/class-wpdb.php      2025-02-10 22:27:49 UTC (rev 59802)
+++ trunk/src/wp-includes/class-wpdb.php        2025-02-11 11:12:03 UTC (rev 59803)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -2412,12 +2412,10 @@
</span><span class="cx" style="display: block; padding: 0 10px">                static $placeholder;
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                if ( ! $placeholder ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        // If ext/hash is not present, compat.php's hash_hmac() does not support sha256.
-                       $algo = function_exists( 'hash' ) ? 'sha256' : 'sha1';
</del><span class="cx" style="display: block; padding: 0 10px">                         // Old WP installs may not have AUTH_SALT defined.
</span><span class="cx" style="display: block; padding: 0 10px">                        $salt = defined( 'AUTH_SALT' ) && AUTH_SALT ? AUTH_SALT : (string) rand();
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        $placeholder = '{' . hash_hmac( $algo, uniqid( $salt, true ), $salt ) . '}';
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 $placeholder = '{' . hash_hmac( 'sha256', uniqid( $salt, true ), $salt ) . '}';
</ins><span class="cx" style="display: block; padding: 0 10px">                 }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                /*
</span></span></pre></div>
<a id="trunksrcwpincludescompatphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-includes/compat.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/compat.php  2025-02-10 22:27:49 UTC (rev 59802)
+++ trunk/src/wp-includes/compat.php    2025-02-11 11:12:03 UTC (rev 59803)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -263,118 +263,6 @@
</span><span class="cx" style="display: block; padding: 0 10px">        return --$count;
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-if ( ! function_exists( 'hash_hmac' ) ) :
-       /**
-        * Compat function to mimic hash_hmac().
-        *
-        * The Hash extension is bundled with PHP by default since PHP 5.1.2.
-        * However, the extension may be explicitly disabled on select servers.
-        * As of PHP 7.4.0, the Hash extension is a core PHP extension and can no
-        * longer be disabled.
-        * I.e. when PHP 7.4.0 becomes the minimum requirement, this polyfill
-        * and the associated `_hash_hmac()` function can be safely removed.
-        *
-        * @ignore
-        * @since 3.2.0
-        *
-        * @see _hash_hmac()
-        *
-        * @param string $algo   Hash algorithm. Accepts 'md5' or 'sha1'.
-        * @param string $data   Data to be hashed.
-        * @param string $key    Secret key to use for generating the hash.
-        * @param bool   $binary Optional. Whether to output raw binary data (true),
-        *                       or lowercase hexits (false). Default false.
-        * @return string|false The hash in output determined by `$binary`.
-        *                      False if `$algo` is unknown or invalid.
-        */
-       function hash_hmac( $algo, $data, $key, $binary = false ) {
-               return _hash_hmac( $algo, $data, $key, $binary );
-       }
-endif;
-
-/**
- * Internal compat function to mimic hash_hmac().
- *
- * @ignore
- * @since 3.2.0
- *
- * @param string $algo   Hash algorithm. Accepts 'md5' or 'sha1'.
- * @param string $data   Data to be hashed.
- * @param string $key    Secret key to use for generating the hash.
- * @param bool   $binary Optional. Whether to output raw binary data (true),
- *                       or lowercase hexits (false). Default false.
- * @return string|false The hash in output determined by `$binary`.
- *                      False if `$algo` is unknown or invalid.
- */
-function _hash_hmac( $algo, $data, $key, $binary = false ) {
-       $packs = array(
-               'md5'  => 'H32',
-               'sha1' => 'H40',
-       );
-
-       if ( ! isset( $packs[ $algo ] ) ) {
-               return false;
-       }
-
-       $pack = $packs[ $algo ];
-
-       if ( strlen( $key ) > 64 ) {
-               $key = pack( $pack, $algo( $key ) );
-       }
-
-       $key = str_pad( $key, 64, chr( 0 ) );
-
-       $ipad = ( substr( $key, 0, 64 ) ^ str_repeat( chr( 0x36 ), 64 ) );
-       $opad = ( substr( $key, 0, 64 ) ^ str_repeat( chr( 0x5C ), 64 ) );
-
-       $hmac = $algo( $opad . pack( $pack, $algo( $ipad . $data ) ) );
-
-       if ( $binary ) {
-               return pack( $pack, $hmac );
-       }
-
-       return $hmac;
-}
-
-if ( ! function_exists( 'hash_equals' ) ) :
-       /**
-        * Timing attack safe string comparison.
-        *
-        * Compares two strings using the same time whether they're equal or not.
-        *
-        * Note: It can leak the length of a string when arguments of differing length are supplied.
-        *
-        * This function was added in PHP 5.6.
-        * However, the Hash extension may be explicitly disabled on select servers.
-        * As of PHP 7.4.0, the Hash extension is a core PHP extension and can no
-        * longer be disabled.
-        * I.e. when PHP 7.4.0 becomes the minimum requirement, this polyfill
-        * can be safely removed.
-        *
-        * @since 3.9.2
-        *
-        * @param string $known_string Expected string.
-        * @param string $user_string  Actual, user supplied, string.
-        * @return bool Whether strings are equal.
-        */
-       function hash_equals( $known_string, $user_string ) {
-               $known_string_length = strlen( $known_string );
-
-               if ( strlen( $user_string ) !== $known_string_length ) {
-                       return false;
-               }
-
-               $result = 0;
-
-               // Do not attempt to "optimize" this.
-               for ( $i = 0; $i < $known_string_length; $i++ ) {
-                       $result |= ord( $known_string[ $i ] ) ^ ord( $user_string[ $i ] );
-               }
-
-               return 0 === $result;
-       }
-endif;
-
</del><span class="cx" style="display: block; padding: 0 10px"> // sodium_crypto_box() was introduced in PHP 7.2.
</span><span class="cx" style="display: block; padding: 0 10px"> if ( ! function_exists( 'sodium_crypto_box' ) ) {
</span><span class="cx" style="display: block; padding: 0 10px">        require ABSPATH . WPINC . '/sodium_compat/autoload.php';
</span></span></pre></div>
<a id="trunksrcwpincludesloadphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-includes/load.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/load.php    2025-02-10 22:27:49 UTC (rev 59802)
+++ trunk/src/wp-includes/load.php      2025-02-11 11:12:03 UTC (rev 59803)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -147,11 +147,12 @@
</span><span class="cx" style="display: block; padding: 0 10px">  * @since 3.0.0
</span><span class="cx" style="display: block; padding: 0 10px">  * @access private
</span><span class="cx" style="display: block; padding: 0 10px">  *
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- * @global string $required_php_version The required PHP version string.
- * @global string $wp_version           The WordPress version string.
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ * @global string   $required_php_version    The required PHP version string.
+ * @global string[] $required_php_extensions The names of required PHP extensions.
+ * @global string   $wp_version              The WordPress version string.
</ins><span class="cx" style="display: block; padding: 0 10px">  */
</span><span class="cx" style="display: block; padding: 0 10px"> function wp_check_php_mysql_versions() {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        global $required_php_version, $wp_version;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ global $required_php_version, $required_php_extensions, $wp_version;
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        $php_version = PHP_VERSION;
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -168,6 +169,30 @@
</span><span class="cx" style="display: block; padding: 0 10px">                exit( 1 );
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+        $missing_extensions = array();
+
+       if ( isset( $required_php_extensions ) && is_array( $required_php_extensions ) ) {
+               foreach ( $required_php_extensions as $extension ) {
+                       if ( extension_loaded( $extension ) ) {
+                               continue;
+                       }
+
+                       $missing_extensions[] = sprintf(
+                               'WordPress %1$s requires the <code>%2$s</code> PHP extension.',
+                               $wp_version,
+                               $extension
+                       );
+               }
+       }
+
+       if ( count( $missing_extensions ) > 0 ) {
+               $protocol = wp_get_server_protocol();
+               header( sprintf( '%s 500 Internal Server Error', $protocol ), true, 500 );
+               header( 'Content-Type: text/html; charset=utf-8' );
+               echo implode( '<br>', $missing_extensions );
+               exit( 1 );
+       }
+
</ins><span class="cx" style="display: block; padding: 0 10px">         // This runs before default constants are defined, so we can't assume WP_CONTENT_DIR is set yet.
</span><span class="cx" style="display: block; padding: 0 10px">        $wp_content_dir = defined( 'WP_CONTENT_DIR' ) ? WP_CONTENT_DIR : ABSPATH . 'wp-content';
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span></span></pre></div>
<a id="trunksrcwpincludespluggablephp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-includes/pluggable.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/pluggable.php       2025-02-10 22:27:49 UTC (rev 59802)
+++ trunk/src/wp-includes/pluggable.php 2025-02-11 11:12:03 UTC (rev 59803)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -772,9 +772,7 @@
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                $key = wp_hash( $username . '|' . $pass_frag . '|' . $expiration . '|' . $token, $scheme );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                // If ext/hash is not present, compat.php's hash_hmac() does not support sha256.
-               $algo = function_exists( 'hash' ) ? 'sha256' : 'sha1';
-               $hash = hash_hmac( $algo, $username . '|' . $expiration . '|' . $token, $key );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         $hash = hash_hmac( 'sha256', $username . '|' . $expiration . '|' . $token, $key );
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                if ( ! hash_equals( $hash, $hmac ) ) {
</span><span class="cx" style="display: block; padding: 0 10px">                        /**
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -875,9 +873,7 @@
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                $key = wp_hash( $user->user_login . '|' . $pass_frag . '|' . $expiration . '|' . $token, $scheme );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                // If ext/hash is not present, compat.php's hash_hmac() does not support sha256.
-               $algo = function_exists( 'hash' ) ? 'sha256' : 'sha1';
-               $hash = hash_hmac( $algo, $user->user_login . '|' . $expiration . '|' . $token, $key );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         $hash = hash_hmac( 'sha256', $user->user_login . '|' . $expiration . '|' . $token, $key );
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                $cookie = $user->user_login . '|' . $expiration . '|' . $token . '|' . $hash;
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span></span></pre></div>
<a id="trunksrcwpincludesversionphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-includes/version.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/version.php 2025-02-10 22:27:49 UTC (rev 59802)
+++ trunk/src/wp-includes/version.php   2025-02-11 11:12:03 UTC (rev 59803)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -40,6 +40,16 @@
</span><span class="cx" style="display: block; padding: 0 10px"> $required_php_version = '7.2.24';
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px"> /**
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ * Holds the names of required PHP extensions.
+ *
+ * @global string[] $required_php_extensions
+ */
+$required_php_extensions = array(
+       'json',
+       'hash',
+);
+
+/**
</ins><span class="cx" style="display: block; padding: 0 10px">  * Holds the required MySQL version.
</span><span class="cx" style="display: block; padding: 0 10px">  *
</span><span class="cx" style="display: block; padding: 0 10px">  * @global string $required_mysql_version
</span></span></pre></div>
<a id="trunksrcwpsettingsphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-settings.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-settings.php 2025-02-10 22:27:49 UTC (rev 59802)
+++ trunk/src/wp-settings.php   2025-02-11 11:12:03 UTC (rev 59803)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -22,14 +22,15 @@
</span><span class="cx" style="display: block; padding: 0 10px">  * include version.php from another installation and don't override
</span><span class="cx" style="display: block; padding: 0 10px">  * these values if already set.
</span><span class="cx" style="display: block; padding: 0 10px">  *
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- * @global string $wp_version             The WordPress version string.
- * @global int    $wp_db_version          WordPress database version.
- * @global string $tinymce_version        TinyMCE version.
- * @global string $required_php_version   The required PHP version string.
- * @global string $required_mysql_version The required MySQL version string.
- * @global string $wp_local_package       Locale code of the package.
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ * @global string   $wp_version              The WordPress version string.
+ * @global int      $wp_db_version           WordPress database version.
+ * @global string   $tinymce_version         TinyMCE version.
+ * @global string   $required_php_version    The required PHP version string.
+ * @global string[] $required_php_extensions The names of required PHP extensions.
+ * @global string   $required_mysql_version  The required MySQL version string.
+ * @global string   $wp_local_package        Locale code of the package.
</ins><span class="cx" style="display: block; padding: 0 10px">  */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-global $wp_version, $wp_db_version, $tinymce_version, $required_php_version, $required_mysql_version, $wp_local_package;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+global $wp_version, $wp_db_version, $tinymce_version, $required_php_version, $required_php_extensions, $required_mysql_version, $wp_local_package;
</ins><span class="cx" style="display: block; padding: 0 10px"> require ABSPATH . WPINC . '/version.php';
</span><span class="cx" style="display: block; padding: 0 10px"> require ABSPATH . WPINC . '/compat.php';
</span><span class="cx" style="display: block; padding: 0 10px"> require ABSPATH . WPINC . '/load.php';
</span></span></pre>
</div>
</div>

</body>
</html>