diff --git a/src/wp-includes/icons.php b/src/wp-includes/icons.php
new file mode 100644
index 0000000000000..6036c95f9b5fc
--- /dev/null
+++ b/src/wp-includes/icons.php
@@ -0,0 +1,81 @@
+get_registered_icon( $name );
+ if ( is_null( $icon ) ) {
+ return '';
+ }
+
+ $svg = $icon['content'];
+ if ( empty( $svg ) ) {
+ return '';
+ }
+
+ $args = wp_parse_args(
+ $args,
+ array(
+ 'size' => 24,
+ 'class' => '',
+ 'label' => '',
+ )
+ );
+
+ $processor = new WP_HTML_Tag_Processor( $svg );
+ if ( ! $processor->next_tag( 'svg' ) ) {
+ return '';
+ }
+
+ if ( is_numeric( $args['size'] ) ) {
+ $size = absint( $args['size'] );
+ $processor->set_attribute( 'width', (string) $size );
+ $processor->set_attribute( 'height', (string) $size );
+ }
+
+ if ( ! empty( $args['class'] ) ) {
+ foreach ( preg_split( '/\s+/', $args['class'], -1, PREG_SPLIT_NO_EMPTY ) as $class_name ) {
+ $processor->add_class( $class_name );
+ }
+ }
+
+ if ( ! empty( $args['label'] ) ) {
+ $processor->set_attribute( 'role', 'img' );
+ $processor->set_attribute( 'aria-label', $args['label'] );
+ $processor->remove_attribute( 'aria-hidden' );
+ $processor->remove_attribute( 'focusable' );
+ } else {
+ $processor->set_attribute( 'aria-hidden', 'true' );
+ $processor->set_attribute( 'focusable', 'false' );
+ $processor->remove_attribute( 'role' );
+ $processor->remove_attribute( 'aria-label' );
+ }
+
+ return $processor->get_updated_html();
+}
diff --git a/src/wp-settings.php b/src/wp-settings.php
index 7e951681e4d53..bb5739d867582 100644
--- a/src/wp-settings.php
+++ b/src/wp-settings.php
@@ -299,6 +299,7 @@
require ABSPATH . WPINC . '/class-wp-connector-registry.php';
require ABSPATH . WPINC . '/connectors.php';
require ABSPATH . WPINC . '/class-wp-icons-registry.php';
+require ABSPATH . WPINC . '/icons.php';
require ABSPATH . WPINC . '/widgets.php';
require ABSPATH . WPINC . '/class-wp-widget.php';
require ABSPATH . WPINC . '/class-wp-widget-factory.php';
diff --git a/tests/phpunit/tests/icons/wpGetIcon.php b/tests/phpunit/tests/icons/wpGetIcon.php
new file mode 100644
index 0000000000000..270f94104550c
--- /dev/null
+++ b/tests/phpunit/tests/icons/wpGetIcon.php
@@ -0,0 +1,125 @@
+assertStringStartsWith( '', $output );
+ }
+
+ /**
+ * @ticket 64847
+ */
+ public function test_wp_get_icon_returns_empty_string_for_unknown_icon() {
+ $output = wp_get_icon( 'this-icon-does-not-exist' );
+ $this->assertSame( '', $output );
+ }
+
+ /**
+ * @ticket 64847
+ */
+ public function test_wp_get_icon_default_attributes() {
+ $output = wp_get_icon( 'core/plus' );
+ // WP_HTML_Tag_Processor lowercases attribute names.
+ $this->assertStringContainsString( 'viewbox="0 0 24 24"', $output );
+ $this->assertStringContainsString( 'width="24"', $output );
+ $this->assertStringContainsString( 'height="24"', $output );
+ $this->assertStringContainsString( 'aria-hidden="true"', $output );
+ $this->assertStringContainsString( 'focusable="false"', $output );
+ }
+
+ /**
+ * @ticket 64847
+ */
+ public function test_wp_get_icon_custom_size() {
+ $output = wp_get_icon( 'core/plus', array( 'size' => 32 ) );
+ $this->assertStringContainsString( 'width="32"', $output );
+ $this->assertStringContainsString( 'height="32"', $output );
+ }
+
+ /**
+ * @ticket 64847
+ */
+ public function test_wp_get_icon_size_null_leaves_dimensions_untouched() {
+ $output = wp_get_icon( 'core/plus', array( 'size' => null ) );
+ $this->assertStringNotContainsString( 'width=', $output );
+ $this->assertStringNotContainsString( 'height=', $output );
+ }
+
+ /**
+ * @ticket 64847
+ */
+ public function test_wp_get_icon_size_zero_outputs_zero_dimensions() {
+ $output = wp_get_icon( 'core/plus', array( 'size' => 0 ) );
+ $this->assertStringContainsString( 'width="0"', $output );
+ $this->assertStringContainsString( 'height="0"', $output );
+ }
+
+ /**
+ * @ticket 64847
+ */
+ public function test_wp_get_icon_custom_class() {
+ $output = wp_get_icon( 'core/plus', array( 'class' => 'my-button-icon' ) );
+ $this->assertStringContainsString( 'class="my-button-icon"', $output );
+ }
+
+ /**
+ * @ticket 64847
+ */
+ public function test_wp_get_icon_multiple_classes() {
+ $output = wp_get_icon( 'core/plus', array( 'class' => 'foo bar baz' ) );
+ $this->assertStringContainsString( 'class="foo bar baz"', $output );
+ }
+
+ /**
+ * @ticket 64847
+ */
+ public function test_wp_get_icon_with_label() {
+ $output = wp_get_icon( 'core/plus', array( 'label' => 'Add item' ) );
+ $this->assertStringContainsString( 'role="img"', $output );
+ $this->assertStringContainsString( 'aria-label="Add item"', $output );
+ $this->assertStringNotContainsString( 'aria-hidden', $output );
+ $this->assertStringNotContainsString( 'focusable', $output );
+ }
+
+ /**
+ * @ticket 64847
+ */
+ public function test_wp_get_icon_without_label_is_hidden() {
+ $output = wp_get_icon( 'core/plus' );
+ $this->assertStringContainsString( 'aria-hidden="true"', $output );
+ $this->assertStringContainsString( 'focusable="false"', $output );
+ $this->assertStringNotContainsString( 'role="img"', $output );
+ $this->assertStringNotContainsString( 'aria-label', $output );
+ }
+
+ /**
+ * @ticket 64847
+ */
+ public function test_wp_get_icon_contains_svg_content() {
+ $output = wp_get_icon( 'core/plus' );
+ $this->assertStringContainsString( ' '">' ) );
+ $this->assertStringNotContainsString( '