<!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>[51911] trunk: FileSystem API: Fix autovivification deprecation notice in `recurse_dirsize()`.</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/51911">51911</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/51911","name":"Review Commit"}}</script></dd>
<dt style="float: left; width: 6em; font-weight: bold">Author</dt> <dd>hellofromTonya</dd>
<dt style="float: left; width: 6em; font-weight: bold">Date</dt> <dd>2021-10-15 22:52:43 +0000 (Fri, 15 Oct 2021)</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'>FileSystem API: Fix autovivification deprecation notice in `recurse_dirsize()`.

>PHP natively allows for autovivification (auto-creation of arrays from falsey values). This feature is very useful and used in a lot of PHP projects, especially if the variable is undefined. However, there is a little oddity that allows creating an array from a `false` and `null` value.

The above quote is from the PHP 8.1 RFC and the (accepted) RFC changes the behaviour described above to deprecated auto creation of arrays from `false`. As it is deprecated, it _will_ still work for the time being, but as of PHP 9.0, this will become a Fatal Error, so we may as well fix it now.

The `recurse_dirsize()` function retrieves a transient and places it in the `$directory_cache` variable, but the `get_transient()` function in WP returns `false` when the transient doesn't exist, which subsequently can lead to the above mentioned deprecation notice.

By verifying that the `$directory_cache` variable is an array before assigning to it and initializing it to an empty array, if it's not, we prevent the deprecation notice, as well as harden the function against potentially corrupted transients where this transient would not return the expected array format, but some other variable type.

Includes adding dedicated unit tests for both the PHP 8.1 issue, as well as the hardening against corrupted transients.

Includes some girl-scouting: touching up a parameter description and some code layout.

Refs:
* https://wiki.php.net/rfc/autovivification_false
* https://developer.wordpress.org/reference/functions/get_transient/

Follow-up to <a href="https://core.trac.wordpress.org/changeset/49212">[49212]</a>, <a href="https://core.trac.wordpress.org/changeset/49744">[49744]</a>.

Props jrf, hellofromTonya.
See <a href="https://core.trac.wordpress.org/ticket/53635">#53635</a>.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunksrcwpincludesfunctionsphp">trunk/src/wp-includes/functions.php</a></li>
<li><a href="#trunktestsphpunittestsfunctionscleanDirsizeCachephp">trunk/tests/phpunit/tests/functions/cleanDirsizeCache.php</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li>trunk/tests/phpunit/tests/functions/fixtures/</li>
<li><a href="#trunktestsphpunittestsfunctionsfixturesdummytxt">trunk/tests/phpunit/tests/functions/fixtures/dummy.txt</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunksrcwpincludesfunctionsphp"></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/functions.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/functions.php       2021-10-15 22:23:35 UTC (rev 51910)
+++ trunk/src/wp-includes/functions.php 2021-10-15 22:52:43 UTC (rev 51911)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -8120,8 +8120,9 @@
</span><span class="cx" style="display: block; padding: 0 10px">  * @param string       $directory          Full path of a directory.
</span><span class="cx" style="display: block; padding: 0 10px">  * @param string|array $exclude            Optional. Full path of a subdirectory to exclude from the total,
</span><span class="cx" style="display: block; padding: 0 10px">  *                                         or array of paths. Expected without trailing slash(es).
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- * @param int          $max_execution_time Maximum time to run before giving up. In seconds. The timeout is global
- *                                         and is measured from the moment WordPress started to load.
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ * @param int          $max_execution_time Optional. Maximum time to run before giving up. In seconds.
+ *                                         The timeout is global and is measured from the moment
+ *                                         WordPress started to load.
</ins><span class="cx" style="display: block; padding: 0 10px">  * @param array        $directory_cache    Optional. Array of cached directory paths.
</span><span class="cx" style="display: block; padding: 0 10px">  *
</span><span class="cx" style="display: block; padding: 0 10px">  * @return int|false|null Size in bytes if a valid directory. False if not. Null if timeout.
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -8194,7 +8195,9 @@
</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">-                                        if ( $max_execution_time > 0 && microtime( true ) - WP_START_TIMESTAMP > $max_execution_time ) {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                 if ( $max_execution_time > 0 &&
+                                               ( microtime( true ) - WP_START_TIMESTAMP ) > $max_execution_time
+                                       ) {
</ins><span class="cx" style="display: block; padding: 0 10px">                                                 // Time exceeded. Give up instead of risking a fatal timeout.
</span><span class="cx" style="display: block; padding: 0 10px">                                                $size = null;
</span><span class="cx" style="display: block; padding: 0 10px">                                                break;
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -8205,6 +8208,10 @@
</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><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+        if ( ! is_array( $directory_cache ) ) {
+               $directory_cache = array();
+       }
+
</ins><span class="cx" style="display: block; padding: 0 10px">         $directory_cache[ $directory ] = $size;
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        // Only write the transient on the top level call and not on recursive calls.
</span></span></pre></div>
<a id="trunktestsphpunittestsfunctionscleanDirsizeCachephp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/tests/phpunit/tests/functions/cleanDirsizeCache.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/tests/functions/cleanDirsizeCache.php 2021-10-15 22:23:35 UTC (rev 51910)
+++ trunk/tests/phpunit/tests/functions/cleanDirsizeCache.php   2021-10-15 22:52:43 UTC (rev 51911)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -3,7 +3,6 @@
</span><span class="cx" style="display: block; padding: 0 10px"> /**   * Tests specific to the directory size caching.   * - * @covers ::clean_dirsize_cache   * @group functions.php   */  class Tests_Functions_CleanDirsizeCache extends WP_UnitTestCase { @@ -13,6 +12,8 @@
</span><span class="cx" style="display: block; padding: 0 10px">         *          * @ticket 52241          * +        * @covers ::clean_dirsize_cache +        *          * @dataProvider data_clean_dirsize_cache_with_invalid_inputs          *          * @param mixed  $path             Path input to use in the test. @@ -56,6 +57,8 @@
</span><span class="cx" style="display: block; padding: 0 10px">         *          * @ticket 52241          * +        * @covers ::clean_dirsize_cache +        *          * @dataProvider data_clean_dirsize_cache_with_non_path_string          *          * @param string $path           Path input to use in the test. @@ -100,4 +103,38 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        'string' => array( 'size' => 42 ),                 );         } + +       /** +        * Test the behaviour of the function when the transient doesn't exist. +        * +        * @ticket 52241 +        * @ticket 53635 +        * +        * @covers ::recurse_dirsize +        */ +       public function test_recurse_dirsize_without_transient() { +               delete_transient( 'dirsize_cache' ); + +               $size = recurse_dirsize( __DIR__ . '/fixtures' ); + +               $this->assertGreaterThan( 10, $size ); +       } + +       /** +        * Test the behaviour of the function when the transient does exist, but is not an array. +        * +        * In particular, this tests that no PHP TypeErrors are being thrown. +        * +        * @ticket 52241 +        * @ticket 53635 +        * +        * @covers ::recurse_dirsize +        */ +       public function test_recurse_dirsize_with_invalid_transient() { +               set_transient( 'dirsize_cache', 'this is not a valid transient for dirsize cache' ); + +               $size = recurse_dirsize( __DIR__ . '/fixtures' ); + +               $this->assertGreaterThan( 
 10, $size ); +       }  }
</span></span></pre></div>
<a id="trunktestsphpunittestsfunctionsfixturesdummytxt"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: trunk/tests/phpunit/tests/functions/fixtures/dummy.txt</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/tests/functions/fixtures/dummy.txt                            (rev 0)
+++ trunk/tests/phpunit/tests/functions/fixtures/dummy.txt      2021-10-15 22:52:43 UTC (rev 51911)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+This is a dummy text file which is only used by the `Tests_Multisite_CleanDirsizeCache::test_recurse_dirsize_without_transient()` test.
</ins><span class="cx" style="display: block; padding: 0 10px">Property changes on: trunk/tests/phpunit/tests/functions/fixtures/dummy.txt
</span><span class="cx" style="display: block; padding: 0 10px">___________________________________________________________________
</span></span></pre></div>
<a id="svneolstyle"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: svn:eol-style</h4></div>
<ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+CR
</ins><span class="cx" style="display: block; padding: 0 10px">\ No newline at end of property
</span></div>

</body>
</html>