diff --git a/src/wp-includes/user.php b/src/wp-includes/user.php
index 018c960c03..62348031a1 100644
--- a/src/wp-includes/user.php
+++ b/src/wp-includes/user.php
@@ -765,6 +765,127 @@ function get_users( $args = array() ) {
 	return (array) $user_search->get_results();
 }
 
+/**
+ * List all the users of the site, with several options available.
+ *
+ * @since 5.9.0
+ *
+ * @param string|array $args {
+ *     Optional. Array or string of default arguments.
+ *
+ *     @type string $orderby       How to sort the users. Accepts 'nicename', 'email', 'url', 'registered',
+ *                                 'user_nicename', 'user_email', 'user_url', 'user_registered', 'name',
+ *                                 'display_name', 'post_count', 'ID', 'meta_value', 'user_login'. Default 'name'.
+ *     @type string $order         Sorting direction for $orderby. Accepts 'ASC', 'DESC'. Default 'ASC'.
+ *     @type int    $number        Maximum users to return or display. Default empty (all users).
+ *     @type bool   $exclude_admin Whether to exclude the 'admin' account, if it exists. Default false.
+ *     @type bool   $show_fullname Whether to show the user's full name. Default false.
+ *     @type string $feed          If not empty, show a link to the user's feed and use this text as the alt
+ *                                 parameter of the link. Default empty.
+ *     @type string $feed_image    If not empty, show a link to the user's feed and use this image URL as
+ *                                 clickable anchor. Default empty.
+ *     @type string $feed_type     The feed type to link to, such as 'rss2'. Defaults to default feed type.
+ *     @type bool   $echo          Whether to output the result or instead return it. Default true.
+ *     @type string $style         If 'list', each user is wrapped in an `<li>` element, otherwise the users
+ *                                 will be separated by commas.
+ *     @type bool   $html          Whether to list the items in HTML form or plaintext. Default true.
+ *     @type string $exclude       An array, comma-, or space-separated list of user IDs to exclude. Default empty.
+ *     @type string $include       An array, comma-, or space-separated list of user IDs to include. Default empty.
+ * }
+ * @return null|string The output, if echo is set to false. Otherwise null.
+ */
+function wp_list_users( $args = array() ) {
+
+	$defaults = array(
+		'orderby'       => 'name',
+		'order'         => 'ASC',
+		'number'        => '',
+		'exclude_admin' => true,
+		'show_fullname' => false,
+		'feed'          => '',
+		'feed_image'    => '',
+		'feed_type'     => '',
+		'echo'          => true,
+		'style'         => 'list',
+		'html'          => true,
+		'exclude'       => '',
+		'include'       => '',
+	);
+
+	$args = wp_parse_args( $args, $defaults );
+
+	$return = '';
+
+	$query_args           = wp_array_slice_assoc( $args, array( 'orderby', 'order', 'number', 'exclude', 'include' ) );
+	$query_args['fields'] = 'ids';
+	$users                = get_users( $query_args );
+
+	foreach ( $users as $user_id ) {
+		$user = get_userdata( $user_id );
+
+		if ( $args['exclude_admin'] && 'admin' === $user->display_name ) {
+			continue;
+		}
+
+		if ( $args['show_fullname'] && $user->first_name && $user->last_name ) {
+			$name = "$user->first_name $user->last_name";
+		} else {
+			$name = $user->display_name;
+		}
+
+		if ( ! $args['html'] ) {
+			$return .= $name . ', ';
+
+			continue; // No need to go further to process HTML.
+		}
+
+		if ( 'list' === $args['style'] ) {
+			$return .= '<li>';
+		}
+
+		$row = $name;
+
+		if ( ! empty( $args['feed_image'] ) || ! empty( $args['feed'] ) ) {
+			$row .= ' ';
+			if ( empty( $args['feed_image'] ) ) {
+				$row .= '(';
+			}
+
+			$row .= '<a href="' . get_author_feed_link( $user->ID, $args['feed_type'] ) . '"';
+
+			$alt = '';
+			if ( ! empty( $args['feed'] ) ) {
+				$alt  = ' alt="' . esc_attr( $args['feed'] ) . '"';
+				$name = $args['feed'];
+			}
+
+			$row .= '>';
+
+			if ( ! empty( $args['feed_image'] ) ) {
+				$row .= '<img src="' . esc_url( $args['feed_image'] ) . '" style="border: none;"' . $alt . ' />';
+			} else {
+				$row .= $name;
+			}
+
+			$row .= '</a>';
+
+			if ( empty( $args['feed_image'] ) ) {
+				$row .= ')';
+			}
+		}
+
+		$return .= $row;
+		$return .= ( 'list' === $args['style'] ) ? '</li>' : ', ';
+	}
+
+	$return = rtrim( $return, ', ' );
+
+	if ( ! $args['echo'] ) {
+		return $return;
+	}
+	echo $return;
+}
+
 /**
  * Get the sites a user belongs to.
  *
diff --git a/tests/phpunit/tests/user/listUsers.php b/tests/phpunit/tests/user/listUsers.php
new file mode 100644
index 0000000000..3166f07a08
--- /dev/null
+++ b/tests/phpunit/tests/user/listUsers.php
@@ -0,0 +1,193 @@
+<?php
+/**
+ * @group user
+ * @ticket 15145
+ */
+class Tests_User_ListUsers extends WP_UnitTestCase {
+	private static $user_ids = array();
+
+	public static function wpSetUpBeforeClass( WP_UnitTest_Factory $factory ) {
+		self::$user_ids[] = $factory->user->create(
+			array(
+				'user_login'   => 'zack',
+				'display_name' => 'zack',
+				'role'         => 'subscriber',
+				'first_name'   => 'zack',
+				'last_name'    => 'moon',
+				'user_email'   => 'm.zack@example.com',
+				'user_url'     => 'http://moonzack.fake',
+			)
+		);
+
+		self::$user_ids[] = $factory->user->create(
+			array(
+				'user_login'   => 'jane',
+				'display_name' => 'jane',
+				'role'         => 'contributor',
+				'first_name'   => 'jane',
+				'last_name'    => 'reno',
+				'user_email'   => 'r.jane@example.com',
+				'user_url'     => 'http://janereno.fake',
+			)
+		);
+
+		self::$user_ids[] = $factory->user->create(
+			array(
+				'user_login'   => 'michelle',
+				'display_name' => 'michelle',
+				'role'         => 'subscriber',
+				'first_name'   => 'michelle',
+				'last_name'    => 'jones',
+				'user_email'   => 'j.michelle@example.com',
+				'user_url'     => 'http://lemichellejones.fake',
+			)
+		);
+
+		self::$user_ids[] = $factory->user->create(
+			array(
+				'user_login'   => 'paul',
+				'display_name' => 'paul',
+				'role'         => 'subscriber',
+				'first_name'   => 'paul',
+				'last_name'    => 'norris',
+				'user_email'   => 'n.paul@example.com',
+				'user_url'     => 'http://awildpaulappeared.fake',
+			)
+		);
+
+		foreach ( self::$user_ids as $user ) {
+			$factory->post->create(
+				array(
+					'post_type'   => 'post',
+					'post_author' => $user,
+				)
+			);
+		}
+	}
+
+	/**
+	 * Test that wp_list_users() creates the expected list of users.
+	 *
+	 * @dataProvider data_should_create_a_user_list
+	 * @covers ::wp_list_users
+	 *
+	 * @param array  $args     The arguments to create a list of users.
+	 * @param string $expected The expected result.
+	 */
+	public function test_should_create_a_user_list( $args, $expected ) {
+		$actual = wp_list_users( $args );
+
+		if ( null === $actual ) {
+			$this->expectOutputString( $expected );
+		} else {
+			$this->assertSame( $expected, $actual );
+		}
+	}
+
+	/**
+	 * Data provider.
+	 *
+	 * @return array
+	 */
+	public function data_should_create_a_user_list() {
+		return array(
+			'defaults when no args are supplied'      => array(
+				'args'     => array(),
+				'expected' => '<li>jane</li><li>michelle</li><li>paul</li><li>zack</li>',
+			),
+			'the admin account included'              => array(
+				'args'     => array(
+					'exclude_admin' => false,
+				),
+				'expected' => '<li>admin</li><li>jane</li><li>michelle</li><li>paul</li><li>zack</li>',
+			),
+			'the full name of each user'              => array(
+				'args'     => array(
+					'show_fullname' => true,
+				),
+				'expected' => '<li>jane reno</li><li>michelle jones</li><li>paul norris</li><li>zack moon</li>',
+			),
+			'the feed of each user'                   => array(
+				'args'     => array(
+					'feed' => 'User feed',
+				),
+				'expected' => '<li>jane (<a href="http://example.org/?feed=rss2&amp;author=3">User feed</a>)</li>' .
+							'<li>michelle (<a href="http://example.org/?feed=rss2&amp;author=4">User feed</a>)</li>' .
+							'<li>paul (<a href="http://example.org/?feed=rss2&amp;author=5">User feed</a>)</li>' .
+							'<li>zack (<a href="http://example.org/?feed=rss2&amp;author=2">User feed</a>)</li>',
+			),
+			'the feed of each user and an image'      => array(
+				'args'     => array(
+					'feed'       => 'User feed with image',
+					'feed_image' => 'http://example.org/image.jpg',
+				),
+				'expected' => '<li>jane <a href="http://example.org/?feed=rss2&amp;author=3"><img src="http://example.org/image.jpg" style="border: none;" alt="User feed with image" /></a></li>' .
+							'<li>michelle <a href="http://example.org/?feed=rss2&amp;author=4"><img src="http://example.org/image.jpg" style="border: none;" alt="User feed with image" /></a></li>' .
+							'<li>paul <a href="http://example.org/?feed=rss2&amp;author=5"><img src="http://example.org/image.jpg" style="border: none;" alt="User feed with image" /></a></li>' .
+							'<li>zack <a href="http://example.org/?feed=rss2&amp;author=2"><img src="http://example.org/image.jpg" style="border: none;" alt="User feed with image" /></a></li>',
+			),
+			'a feed of the specified type'            => array(
+				'args'     => array(
+					'feed'      => 'User feed as atom',
+					'feed_type' => 'atom',
+				),
+				'expected' => '<li>jane (<a href="http://example.org/?feed=atom&amp;author=3">User feed as atom</a>)</li>' .
+							'<li>michelle (<a href="http://example.org/?feed=atom&amp;author=4">User feed as atom</a>)</li>' .
+							'<li>paul (<a href="http://example.org/?feed=atom&amp;author=5">User feed as atom</a>)</li>' .
+							'<li>zack (<a href="http://example.org/?feed=atom&amp;author=2">User feed as atom</a>)</li>',
+			),
+			'no output via echo'                      => array(
+				'args'     => array(
+					'echo' => false,
+				),
+				'expected' => '<li>jane</li><li>michelle</li><li>paul</li><li>zack</li>',
+			),
+			'commas separating each user'             => array(
+				'args'     => array(
+					'style' => '',
+				),
+				'expected' => 'jane, michelle, paul, zack',
+			),
+			'plain text format'                       => array(
+				'args'     => array(
+					'html' => false,
+				),
+				'expected' => 'jane, michelle, paul, zack',
+			),
+		);
+	}
+
+	/**
+	 * Tests that wp_list_users() does not create a user list.
+	 *
+	 * @dataProvider data_should_not_create_a_user_list
+	 * @covers ::wp_list_users
+	 *
+	 * @param array  $args     The arguments to create a list of users.
+	 */
+	public function test_should_not_create_a_user_list( $args ) {
+		$actual = wp_list_users( $args );
+
+		if ( null === $actual ) {
+			$this->expectOutputString( '', 'wp_list_users() did not output an empty string.' );
+		} else {
+			$this->assertSame( $actual, 'wp_list_users() did not return an empty string.' );
+		}
+	}
+
+	/**
+	 * Data provider.
+	 *
+	 * @return array
+	 */
+	public function data_should_not_create_a_user_list() {
+		return array(
+			'an empty user query result' => array(
+				'args'     => array(
+					'include' => array( 9999 ),
+				),
+				'expected' => '',
+			),
+		);
+	}
+}
