Index: src/wp-includes/shortcodes.php
===================================================================
--- src/wp-includes/shortcodes.php	(revision 35215)
+++ src/wp-includes/shortcodes.php	(working copy)
@@ -228,6 +228,38 @@
 }
 
 /**
+ * Executes a single shortcode with given attributes.
+ *
+ * This function is not meant for use with nested shortcodes. For that, use do_shortcode().
+ *
+ * @since 4.4.0
+ *
+ * @global array $shortcode_tags Shortcode tags.
+ *
+ * @param string $tag     Name of a registered shortcode.
+ * @param array  $atts    Optional. Attributes to pass to the shortcode hook. Default empty array.
+ * @param string $content Optional. Content to use inside the shortcode tag. Default null (self-closing).
+ * @return string|WP_Error Shortcode content, or WP_Error instance if invalid.
+ */
+function do_single_shortcode( $tag, $atts = array(), $content = null ) {
+	global $shortcode_tags;
+
+	if ( empty( $shortcode_tags[ $tag ] ) ) {
+		/* translators: 1: shortcode tag name */
+		return new WP_Error( 'shortcode_invalid_tag', sprintf( __( 'Shortcode %s is not registered' ), $tag ), $tag );
+	}
+
+	$callback = $shortcode_tags[ $tag ];
+
+	if ( ! is_callable( $callback ) ) {
+		/* translators: 1: shortcode tag name */
+		return new WP_Error( 'shortcode_invalid_callback', sprintf( __( 'Shortcode %s does not have a valid registered callback.' ), $tag ), $tag );
+	}
+
+	return call_user_func( $callback, $atts, $content, $tag );
+}
+
+/**
  * Retrieve the shortcode regular expression for searching.
  *
  * The regular expression combines the shortcode tags in the regular expression
Index: tests/phpunit/tests/shortcode.php
===================================================================
--- tests/phpunit/tests/shortcode.php	(revision 35215)
+++ tests/phpunit/tests/shortcode.php	(working copy)
@@ -4,7 +4,7 @@
  */
 class Tests_Shortcode extends WP_UnitTestCase {
 
-	protected $shortcodes = array( 'test-shortcode-tag', 'footag', 'bartag', 'baztag', 'dumptag', 'hyphen', 'hyphen-foo', 'hyphen-foo-bar', 'url' );
+	protected $shortcodes = array( 'test-shortcode-tag', 'footag', 'bartag', 'baztag', 'dumptag', 'dumptag-and-content', 'hyphen', 'hyphen-foo', 'hyphen-foo-bar', 'url' );
 
 	function setUp() {
 		parent::setUp();
@@ -61,6 +61,23 @@
 		return $out;
 	}
 
+	function _shortcode_dumptag_and_content( $atts, $content = '' ) {
+		$out = 'content = [dumptag-and-content';
+
+		if ( ! empty( $atts ) ) {
+			foreach ( $atts as $k => $v ) {
+				$out .= " $k='$v'";
+			}
+		}
+		$out .= "]";
+
+		if ( ! empty( $content ) ) {
+			$out .= "{$content}[/dumptag-and-content]";
+		}
+
+		return $out;
+	}
+
 	function _shortcode_hyphen() {
 		return __FUNCTION__;
 	}
@@ -646,4 +663,114 @@
 
 	}
 
+	/**
+	 * @ticket 25435
+	 */
+	public function test_do_single_shortcode_no_atts_self_closing_test_args_only() {
+		do_single_shortcode( 'test-shortcode-tag' );
+
+		$this->assertSame( 'test-shortcode-tag', $this->tagname );
+		$this->assertSame( array(), $this->atts );
+		$this->assertSame( null, $this->content );
+	}
+
+	/**
+	 * @ticket 25435
+	 */
+	public function test_do_single_shortcode_no_atts_self_closing_output() {
+		$out = do_single_shortcode( 'dumptag-and-content' );
+		$this->assertSame( 'content = [dumptag-and-content]', $out );
+	}
+
+	/**
+	 * @ticket 25435
+	 */
+	public function test_do_single_shortcode_no_atts_with_content_test_args_only() {
+		do_single_shortcode( 'test-shortcode-tag', array(), 'Foo Content' );
+
+		$this->assertSame( 'test-shortcode-tag', $this->tagname );
+		$this->assertSame( array(), $this->atts );
+		$this->assertSame( 'Foo Content', $this->content );
+	}
+
+	/**
+	 * @ticket 25435
+	 */
+	public function test_do_single_shortcode_no_atts_with_content_output() {
+		$out = do_single_shortcode( 'dumptag-and-content', array(), 'Foo Content' );
+
+		$this->assertSame( 'content = [dumptag-and-content]Foo Content[/dumptag-and-content]', $out );
+	}
+
+	/**
+	 * @ticket 25435
+	 */
+	public function test_do_single_shortcode_with_atts_self_closing_args_only() {
+		$atts = array( 'foo' => 'bar', 'baz' => 'bot' );
+
+		do_single_shortcode( 'test-shortcode-tag', $atts );
+
+		$this->assertSame( 'test-shortcode-tag', $this->tagname );
+		$this->assertSame( $atts, $this->atts );
+		$this->assertSame( null, $this->content );
+	}
+
+	/**
+	 * @ticket 25435
+	 */
+	public function test_do_single_shortcode_with_atts_self_closing_ouput() {
+		$atts = array( 'foo' => 'bar', 'baz' => 'bot' );
+
+		$out = do_single_shortcode( 'dumptag-and-content', $atts );
+
+		$this->assertSame( "content = [dumptag-and-content foo='bar' baz='bot']", $out );
+	}
+
+	/**
+	 * @ticket 25435
+	 */
+	public function test_do_single_shortcode_with_atts_with_content_args_only() {
+		$atts = array( 'foo' => 'bar', 'baz' => 'bot' );
+
+		do_single_shortcode( 'test-shortcode-tag', $atts, 'Foo Content' );
+
+		$this->assertSame( 'test-shortcode-tag', $this->tagname );
+		$this->assertEqualSets( $atts, $this->atts );
+		$this->assertSame( 'Foo Content', $this->content );
+	}
+
+	/**
+	 * @ticket 25435
+	 */
+	public function test_do_single_shortcode_with_atts_with_content_output() {
+		$atts = array( 'foo' => 'bar', 'baz' => 'bot' );
+
+		$out = do_single_shortcode( 'dumptag-and-content', $atts, 'Foo Content' );
+
+		$this->assertSame( "content = [dumptag-and-content foo='bar' baz='bot']Foo Content[/dumptag-and-content]", $out );
+	}
+
+	/**
+	 * @ticket 25435
+	 */
+	public function test_do_single_shortcode_expect_WP_Error_for_invalid_tag() {
+		$out = do_single_shortcode( 'not-a-real-tag' );
+
+		$this->assertWPError( $out );
+		$this->assertSame( 'shortcode_invalid_tag', $out->get_error_code() );
+	}
+
+	/**
+	 * @ticket 25435
+	 */
+	public function test_do_single_shortcode_expect_WP_Error_for_invalid_callback() {
+		// Add to the array so it'll be unset on tearDown().
+		$this->shortcodes[] = 'real-tag-fake-callback';
+
+		add_shortcode( 'real-tag-fake-callback', 'fake_callback' );
+		$out = do_single_shortcode( 'real-tag-fake-callback' );
+
+		$this->assertWPError( $out );
+		$this->assertSame( 'shortcode_invalid_callback', $out->get_error_code() );
+	}
 }
