<!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>[52038] trunk: General: Introduce polyfills for `array_key_first()` and `array_key_last()` added in PHP 7.3.0.</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/52038">52038</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/52038","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-11-08 13:50:35 +0000 (Mon, 08 Nov 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'>General: Introduce polyfills for `array_key_first()` and `array_key_last()` added in PHP 7.3.0.

PHP 7.3.0 introduced two new functions: `array_key_first()` and `array_key_last()`. These two functions return the first and last key of each array respectively, without affecting the internal state of the array.

The polyfills make these two functions available for use in Core on PHP versions less than 7.3.0.

Ref:
* PHP RFC https://wiki.php.net/rfc/array_key_first_last
* PHP manual `array_key_first()` https://www.php.net/manual/en/function.array-key-first.php
* PHP manual `array_key_last()` https://www.php.net/manual/en/function.array-key-last.php

Props desrosj, pbearne, costdev, hellofromTonya, ayeshrajans, manzoorwanijk, audrasjb, sergeybiryukov.
Fixes <a href="https://core.trac.wordpress.org/ticket/45055">#45055</a>.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunksrcwpincludescompatphp">trunk/src/wp-includes/compat.php</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunktestsphpunittestscompatarrayKeyFirstphp">trunk/tests/phpunit/tests/compat/arrayKeyFirst.php</a></li>
<li><a href="#trunktestsphpunittestscompatarrayKeyLastphp">trunk/tests/phpunit/tests/compat/arrayKeyLast.php</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<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  2021-11-08 13:29:25 UTC (rev 52037)
+++ trunk/src/wp-includes/compat.php    2021-11-08 13:50:35 UTC (rev 52038)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -375,6 +375,48 @@
</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 ( ! function_exists( 'array_key_first' ) ) {
+       /**
+        * Polyfill for array_key_first() function added in PHP 7.3.
+        *
+        * Get the first key of the given array without affecting
+        * the internal array pointer.
+        *
+        * @since 5.9.0
+        *
+        * @param array $arr An array.
+        * @return string|int|null The first key of array if the array
+        *                         is not empty; `null` otherwise.
+        */
+       function array_key_first( array $arr ) {
+               foreach ( $arr as $key => $value ) {
+                       return $key;
+               }
+       }
+}
+
+if ( ! function_exists( 'array_key_last' ) ) {
+       /**
+        * Polyfill for `array_key_last()` function added in PHP 7.3.
+        *
+        * Get the last key of the given array without affecting the
+        * internal array pointer.
+        *
+        * @since 5.9.0
+        *
+        * @param array $arr An array.
+        * @return string|int|null The last key of array if the array
+        *.                        is not empty; `null` otherwise.
+        */
+       function array_key_last( array $arr ) {
+               if ( empty( $arr ) ) {
+                       return null;
+               }
+               end( $arr );
+               return key( $arr );
+       }
+}
+
</ins><span class="cx" style="display: block; padding: 0 10px"> // IMAGETYPE_WEBP constant is only defined in PHP 7.1 or later.
</span><span class="cx" style="display: block; padding: 0 10px"> if ( ! defined( 'IMAGETYPE_WEBP' ) ) {
</span><span class="cx" style="display: block; padding: 0 10px">        define( 'IMAGETYPE_WEBP', 18 );
</span></span></pre></div>
<a id="trunktestsphpunittestscompatarrayKeyFirstphp"></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/compat/arrayKeyFirst.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/tests/compat/arrayKeyFirst.php                                (rev 0)
+++ trunk/tests/phpunit/tests/compat/arrayKeyFirst.php  2021-11-08 13:50:35 UTC (rev 52038)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,76 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
+
+/**
+ * @group compat
+ *
+ * @covers ::array_key_first
+ */
+class Tests_Compat_arrayKeyFirst extends WP_UnitTestCase {
+
+       /**
+        * Test that array_key_first() is always available (either from PHP or WP).
+        * @ticket 45055
+        */
+       public function test_array_key_first_availability() {
+               $this->assertTrue( function_exists( 'array_key_first' ) );
+       }
+
+       /**
+        * @dataProvider data_array_key_first
+        *
+        * @ticket 45055
+        *
+        * @param bool $expected The value of the key extracted to extracted from given array.
+        * @param array $arr     The array to get first key from.
+        */
+       public function test_array_key_first( $expected, $arr ) {
+               if ( ! function_exists( 'array_key_first' ) ) {
+                       $this->markTestSkipped( 'array_key_first() is not available.' );
+               } else {
+                       $this->assertSame(
+                               $expected,
+                               array_key_first( $arr )
+                       );
+               }
+
+       }
+
+       /**
+        * Data provider.
+        *
+        * @return array[]
+        */
+       public function data_array_key_first() {
+               return array(
+                       'string key'  => array(
+                               'expected' => 'key1',
+                               'arr'      => array(
+                                       'key1' => 'val1',
+                                       'key2' => 'val2',
+                               ),
+                       ),
+                       'int key'     => array(
+                               'expected' => 99,
+                               'arr'      => array(
+                                       99 => 'val1',
+                                       1  => 'val2',
+                               ),
+                       ),
+                       'no key'      => array(
+                               'expected' => 0,
+                               'arr'      => array( 'val1', 'val2' ),
+                       ),
+                       'multi array' => array(
+                               'expected' => 99,
+                               'arr'      => array(
+                                       99 => array( 22 => 'val1' ),
+                                       1  => 'val2',
+                               ),
+                       ),
+                       'empty array' => array(
+                               'expected' => null,
+                               'arr'      => array(),
+                       ),
+               );
+       }
+}
</ins><span class="cx" style="display: block; padding: 0 10px">Property changes on: trunk/tests/phpunit/tests/compat/arrayKeyFirst.php
</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">+native
</ins><span class="cx" style="display: block; padding: 0 10px">\ No newline at end of property
</span><a id="trunktestsphpunittestscompatarrayKeyLastphp"></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/compat/arrayKeyLast.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/tests/compat/arrayKeyLast.php                         (rev 0)
+++ trunk/tests/phpunit/tests/compat/arrayKeyLast.php   2021-11-08 13:50:35 UTC (rev 52038)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,81 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
+
+/**
+ * @group compat
+ *
+ * @covers ::array_key_last
+ */
+class Tests_Compat_ArrayKeyLast extends WP_UnitTestCase {
+
+       /**
+        * Test that array_key_last() is always available (either from PHP or WP).
+        *
+        * @ticket 45055
+        */
+       public function test_array_key_last_availability() {
+               $this->assertTrue( function_exists( 'array_key_last' ) );
+       }
+
+       /**
+        * @dataProvider data_array_key_last
+        *
+        * @ticket 45055
+        *
+        * @param bool  $expected The value of the key extracted to extracted from given array.
+        * @param array $arr      The array to get last key from.
+        */
+       public function test_array_key_last( $expected, $arr ) {
+               if ( ! function_exists( 'array_key_last' ) ) {
+                       $this->markTestSkipped( 'array_key_last() is not available.' );
+               } else {
+                       $this->assertSame( $expected, array_key_last( $arr ) );
+               }
+       }
+
+       /**
+        * Data provider for test_array_key_last().
+        *
+        * @return array
+        */
+       public function data_array_key_last() {
+               return array(
+                       'string key'  => array(
+                               'expected' => 'key2',
+                               'arr'      => array(
+                                       'key1' => 'val1',
+                                       'key2' => 'val2',
+                               ),
+                       ),
+                       'int key'     => array(
+                               'expected' => 1,
+                               'arr'      => array(
+                                       99 => 'val1',
+                                       1  => 'val2',
+                               ),
+                       ),
+                       'no key'      => array(
+                               'expected' => 1,
+                               'arr'      => array( 'val1', 'val2' ),
+                       ),
+                       'multi array' => array(
+                               'expected' => 1,
+                               'arr'      => array(
+                                       99 => array( 22 => 'val1' ),
+                                       1  => 'val2',
+                               ),
+                       ),
+                       'mixed keys'  => array(
+                               'expected' => 1,
+                               'arr'      => array(
+                                       'val1',
+                                       'key2' => 'val2',
+                                       'val3',
+                               ),
+                       ),
+                       'empty array' => array(
+                               'expected' => null,
+                               'arr'      => array(),
+                       ),
+               );
+       }
+}
</ins><span class="cx" style="display: block; padding: 0 10px">Property changes on: trunk/tests/phpunit/tests/compat/arrayKeyLast.php
</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">+native
</ins><span class="cx" style="display: block; padding: 0 10px">\ No newline at end of property
</span></div>

</body>
</html>