diff --git a/src/wp-includes/class-wp-block-type-registry.php b/src/wp-includes/class-wp-block-type-registry.php
new file mode 100644
index 0000000000..f95dec6ce4
--- /dev/null
+++ b/src/wp-includes/class-wp-block-type-registry.php
@@ -0,0 +1,182 @@
+<?php
+/**
+ * Blocks API: WP_Block_Type_Registry class
+ *
+ * @package gutenberg
+ * @since 0.6.0
+ */
+
+/**
+ * Core class used for interacting with block types.
+ *
+ * @since 0.6.0
+ */
+final class WP_Block_Type_Registry {
+	/**
+	 * Registered block types, as `$name => $instance` pairs.
+	 *
+	 * @since 0.6.0
+	 * @access private
+	 * @var WP_Block_Type[]
+	 */
+	private $registered_block_types = array();
+
+	/**
+	 * Container for the main instance of the class.
+	 *
+	 * @since 0.6.0
+	 * @access private
+	 * @static
+	 * @var WP_Block_Type_Registry|null
+	 */
+	private static $instance = null;
+
+	/**
+	 * Registers a block type.
+	 *
+	 * @since 0.6.0
+	 * @access public
+	 *
+	 * @param string|WP_Block_Type $name Block type name including namespace, or alternatively a
+	 *                                   complete WP_Block_Type instance. In case a WP_Block_Type
+	 *                                   is provided, the $args parameter will be ignored.
+	 * @param array                $args {
+	 *     Optional. Array of block type arguments. Any arguments may be defined, however the
+	 *     ones described below are supported by default. Default empty array.
+	 *
+	 *     @type callable $render_callback Callback used to render blocks of this block type.
+	 *     @type array    $attributes      Block attributes mapping, property name to schema.
+	 * }
+	 * @return WP_Block_Type|false The registered block type on success, or false on failure.
+	 */
+	public function register( $name, $args = array() ) {
+		$block_type = null;
+		if ( $name instanceof WP_Block_Type ) {
+			$block_type = $name;
+			$name       = $block_type->name;
+		}
+
+		if ( ! is_string( $name ) ) {
+			$message = __( 'Block type names must be strings.', 'gutenberg' );
+			_doing_it_wrong( __METHOD__, $message, '0.1.0' );
+			return false;
+		}
+
+		if ( preg_match( '/[A-Z]+/', $name ) ) {
+			$message = __( 'Block type names must not contain uppercase characters.', 'gutenberg' );
+			_doing_it_wrong( __METHOD__, $message, '1.5.0' );
+			return false;
+		}
+
+		$name_matcher = '/^[a-z0-9-]+\/[a-z0-9-]+$/';
+		if ( ! preg_match( $name_matcher, $name ) ) {
+			$message = __( 'Block type names must contain a namespace prefix. Example: my-plugin/my-custom-block-type', 'gutenberg' );
+			_doing_it_wrong( __METHOD__, $message, '0.1.0' );
+			return false;
+		}
+
+		if ( $this->is_registered( $name ) ) {
+			/* translators: 1: block name */
+			$message = sprintf( __( 'Block type "%s" is already registered.', 'gutenberg' ), $name );
+			_doing_it_wrong( __METHOD__, $message, '0.1.0' );
+			return false;
+		}
+
+		if ( ! $block_type ) {
+			$block_type = new WP_Block_Type( $name, $args );
+		}
+
+		$this->registered_block_types[ $name ] = $block_type;
+
+		return $block_type;
+	}
+
+	/**
+	 * Unregisters a block type.
+	 *
+	 * @since 0.6.0
+	 * @access public
+	 *
+	 * @param string|WP_Block_Type $name Block type name including namespace, or alternatively a
+	 *                                   complete WP_Block_Type instance.
+	 * @return WP_Block_Type|false The unregistered block type on success, or false on failure.
+	 */
+	public function unregister( $name ) {
+		if ( $name instanceof WP_Block_Type ) {
+			$name = $name->name;
+		}
+
+		if ( ! $this->is_registered( $name ) ) {
+			/* translators: 1: block name */
+			$message = sprintf( __( 'Block type "%s" is not registered.', 'gutenberg' ), $name );
+			_doing_it_wrong( __METHOD__, $message, '0.1.0' );
+			return false;
+		}
+
+		$unregistered_block_type = $this->registered_block_types[ $name ];
+		unset( $this->registered_block_types[ $name ] );
+
+		return $unregistered_block_type;
+	}
+
+	/**
+	 * Retrieves a registered block type.
+	 *
+	 * @since 0.6.0
+	 * @access public
+	 *
+	 * @param string $name Block type name including namespace.
+	 * @return WP_Block_Type|null The registered block type, or null if it is not registered.
+	 */
+	public function get_registered( $name ) {
+		if ( ! $this->is_registered( $name ) ) {
+			return null;
+		}
+
+		return $this->registered_block_types[ $name ];
+	}
+
+	/**
+	 * Retrieves all registered block types.
+	 *
+	 * @since 0.6.0
+	 * @access public
+	 *
+	 * @return WP_Block_Type[] Associative array of `$block_type_name => $block_type` pairs.
+	 */
+	public function get_all_registered() {
+		return $this->registered_block_types;
+	}
+
+	/**
+	 * Checks if a block type is registered.
+	 *
+	 * @since 0.6.0
+	 * @access public
+	 *
+	 * @param string $name Block type name including namespace.
+	 * @return bool True if the block type is registered, false otherwise.
+	 */
+	public function is_registered( $name ) {
+		return isset( $this->registered_block_types[ $name ] );
+	}
+
+	/**
+	 * Utility method to retrieve the main instance of the class.
+	 *
+	 * The instance will be created if it does not exist yet.
+	 *
+	 * @since 0.6.0
+	 * @access public
+	 * @static
+	 *
+	 * @return WP_Block_Type_Registry The main instance.
+	 */
+	public static function get_instance() {
+		if ( null === self::$instance ) {
+			self::$instance = new self();
+		}
+
+		return self::$instance;
+	}
+}
diff --git a/src/wp-includes/class-wp-block-type.php b/src/wp-includes/class-wp-block-type.php
new file mode 100644
index 0000000000..0c18bb6efa
--- /dev/null
+++ b/src/wp-includes/class-wp-block-type.php
@@ -0,0 +1,198 @@
+<?php
+/**
+ * Blocks API: WP_Block_Type class
+ *
+ * @package gutenberg
+ * @since 0.6.0
+ */
+
+/**
+ * Core class representing a block type.
+ *
+ * @since 0.6.0
+ *
+ * @see register_block_type()
+ */
+class WP_Block_Type {
+	/**
+	 * Block type key.
+	 *
+	 * @since 0.6.0
+	 * @var string
+	 */
+	public $name;
+
+	/**
+	 * Block type render callback.
+	 *
+	 * @since 0.6.0
+	 * @var callable
+	 */
+	public $render_callback;
+
+	/**
+	 * Block type attributes property schemas.
+	 *
+	 * @since 0.10.0
+	 * @var array
+	 */
+	public $attributes;
+
+	/**
+	 * Block type editor script handle.
+	 *
+	 * @since 2.0.0
+	 * @var string
+	 */
+	public $editor_script;
+
+	/**
+	 * Block type front end script handle.
+	 *
+	 * @since 2.0.0
+	 * @var string
+	 */
+	public $script;
+
+	/**
+	 * Block type editor style handle.
+	 *
+	 * @since 2.0.0
+	 * @var string
+	 */
+	public $editor_style;
+
+	/**
+	 * Block type front end style handle.
+	 *
+	 * @since 2.0.0
+	 * @var string
+	 */
+	public $style;
+
+	/**
+	 * Constructor.
+	 *
+	 * Will populate object properties from the provided arguments.
+	 *
+	 * @since 0.6.0
+	 *
+	 * @see register_block_type()
+	 *
+	 * @param string       $block_type Block type name including namespace.
+	 * @param array|string $args       Optional. Array or string of arguments for registering a block type.
+	 *                                 Default empty array.
+	 */
+	public function __construct( $block_type, $args = array() ) {
+		$this->name = $block_type;
+
+		$this->set_props( $args );
+	}
+
+	/**
+	 * Renders the block type output for given attributes.
+	 *
+	 * @since 0.6.0
+	 *
+	 * @param array  $attributes Optional. Block attributes. Default empty array.
+	 * @param string $content    Optional. Block content. Default empty string.
+	 * @return string Rendered block type output.
+	 */
+	public function render( $attributes = array(), $content = '' ) {
+		if ( ! $this->is_dynamic() ) {
+			return '';
+		}
+
+		$attributes = $this->prepare_attributes_for_render( $attributes );
+
+		return (string) call_user_func( $this->render_callback, $attributes, $content );
+	}
+
+	/**
+	 * Returns true if the block type is dynamic, or false otherwise. A dynamic
+	 * block is one which defers its rendering to occur on-demand at runtime.
+	 *
+	 * @return boolean Whether block type is dynamic.
+	 */
+	public function is_dynamic() {
+		return is_callable( $this->render_callback );
+	}
+
+	/**
+	 * Validates attributes against the current block schema, populating
+	 * defaulted and missing values, and omitting unknown attributes.
+	 *
+	 * @param  array $attributes Original block attributes.
+	 * @return array             Prepared block attributes.
+	 */
+	public function prepare_attributes_for_render( $attributes ) {
+		if ( ! isset( $this->attributes ) ) {
+			return $attributes;
+		}
+
+		$prepared_attributes = array();
+
+		foreach ( $this->attributes as $attribute_name => $schema ) {
+			$value = null;
+
+			if ( isset( $attributes[ $attribute_name ] ) ) {
+				$is_valid = rest_validate_value_from_schema( $attributes[ $attribute_name ], $schema );
+				if ( ! is_wp_error( $is_valid ) ) {
+					$value = rest_sanitize_value_from_schema( $attributes[ $attribute_name ], $schema );
+				}
+			}
+
+			if ( is_null( $value ) && isset( $schema['default'] ) ) {
+				$value = $schema['default'];
+			}
+
+			$prepared_attributes[ $attribute_name ] = $value;
+		}
+
+		return $prepared_attributes;
+	}
+
+	/**
+	 * Sets block type properties.
+	 *
+	 * @since 0.6.0
+	 *
+	 * @param array|string $args Array or string of arguments for registering a block type.
+	 */
+	public function set_props( $args ) {
+		$args = wp_parse_args(
+			$args,
+			array(
+				'render_callback' => null,
+			)
+		);
+
+		$args['name'] = $this->name;
+
+		foreach ( $args as $property_name => $property_value ) {
+			$this->$property_name = $property_value;
+		}
+	}
+
+	/**
+	 * Get all available block attributes including possible layout attribute from Columns block.
+	 *
+	 * @return array Array of attributes.
+	 */
+	public function get_attributes() {
+		return is_array( $this->attributes ) ?
+			array_merge(
+				$this->attributes,
+				array(
+					'layout' => array(
+						'type' => 'string',
+					),
+				)
+			) :
+			array(
+				'layout' => array(
+					'type' => 'string',
+				),
+			);
+	}
+}
diff --git a/src/wp-settings.php b/src/wp-settings.php
index 44c8a91a07..ba0f9388d7 100644
--- a/src/wp-settings.php
+++ b/src/wp-settings.php
@@ -240,6 +240,8 @@ require( ABSPATH . WPINC . '/rest-api/fields/class-wp-rest-comment-meta-fields.p
 require( ABSPATH . WPINC . '/rest-api/fields/class-wp-rest-post-meta-fields.php' );
 require( ABSPATH . WPINC . '/rest-api/fields/class-wp-rest-term-meta-fields.php' );
 require( ABSPATH . WPINC . '/rest-api/fields/class-wp-rest-user-meta-fields.php' );
+require( ABSPATH . WPINC . '/class-wp-block-type.php' );
+require( ABSPATH . WPINC . '/class-wp-block-type-registry.php' );
 
 $GLOBALS['wp_embed'] = new WP_Embed();
 
diff --git a/tests/phpunit/tests/blocks/class-block-type-registry-test.php b/tests/phpunit/tests/blocks/class-block-type-registry-test.php
new file mode 100644
index 0000000000..0ba75cd8d0
--- /dev/null
+++ b/tests/phpunit/tests/blocks/class-block-type-registry-test.php
@@ -0,0 +1,153 @@
+<?php
+/**
+ * WP_Block_Type_Registry Tests
+ *
+ * @package Gutenberg
+ */
+
+/**
+ * Tests for WP_Block_Type_Registry
+ */
+class Block_Type_Registry_Test extends WP_UnitTestCase {
+
+	/**
+	 * Dummy block type registry.
+	 *
+	 * @var WP_Block_Type_Registry
+	 */
+	private $registry = null;
+
+	function setUp() {
+		parent::setUp();
+
+		$this->registry = new WP_Block_Type_Registry();
+	}
+
+	function tearDown() {
+		parent::tearDown();
+
+		$this->registry = null;
+	}
+
+	/**
+	 * Should reject numbers
+	 *
+	 * @expectedIncorrectUsage WP_Block_Type_Registry::register
+	 */
+	function test_invalid_non_string_names() {
+		$result = $this->registry->register( 1, array() );
+		$this->assertFalse( $result );
+	}
+
+	/**
+	 * Should reject blocks without a namespace
+	 *
+	 * @expectedIncorrectUsage WP_Block_Type_Registry::register
+	 */
+	function test_invalid_names_without_namespace() {
+		$result = $this->registry->register( 'paragraph', array() );
+		$this->assertFalse( $result );
+	}
+
+	/**
+	 * Should reject blocks with invalid characters
+	 *
+	 * @expectedIncorrectUsage WP_Block_Type_Registry::register
+	 */
+	function test_invalid_characters() {
+		$result = $this->registry->register( 'still/_doing_it_wrong', array() );
+		$this->assertFalse( $result );
+	}
+
+	/**
+	 * Should reject blocks with uppercase characters
+	 *
+	 * @expectedIncorrectUsage WP_Block_Type_Registry::register
+	 */
+	function test_uppercase_characters() {
+		$result = $this->registry->register( 'Core/Paragraph', array() );
+		$this->assertFalse( $result );
+	}
+
+	/**
+	 * Should accept valid block names
+	 */
+	function test_register_block_type() {
+		$name     = 'core/paragraph';
+		$settings = array(
+			'icon' => 'editor-paragraph',
+		);
+
+		$block_type = $this->registry->register( $name, $settings );
+		$this->assertEquals( $name, $block_type->name );
+		$this->assertEquals( $settings['icon'], $block_type->icon );
+		$this->assertEquals( $block_type, $this->registry->get_registered( $name ) );
+	}
+
+	/**
+	 * Should fail to re-register the same block
+	 *
+	 * @expectedIncorrectUsage WP_Block_Type_Registry::register
+	 */
+	function test_register_block_type_twice() {
+		$name     = 'core/paragraph';
+		$settings = array(
+			'icon' => 'editor-paragraph',
+		);
+
+		$result = $this->registry->register( $name, $settings );
+		$this->assertNotFalse( $result );
+		$result = $this->registry->register( $name, $settings );
+		$this->assertFalse( $result );
+	}
+
+	/**
+	 * Should accept a WP_Block_Type instance
+	 */
+	function test_register_block_type_instance() {
+		$block_type = new WP_Dummy_Block_Type( 'core/dummy' );
+
+		$result = $this->registry->register( $block_type );
+		$this->assertSame( $block_type, $result );
+	}
+
+	/**
+	 * Unregistering should fail if a block is not registered
+	 *
+	 * @expectedIncorrectUsage WP_Block_Type_Registry::unregister
+	 */
+	function test_unregister_not_registered_block() {
+		$result = $this->registry->unregister( 'core/unregistered' );
+		$this->assertFalse( $result );
+	}
+
+	/**
+	 * Should unregister existing blocks
+	 */
+	function test_unregister_block_type() {
+		$name     = 'core/paragraph';
+		$settings = array(
+			'icon' => 'editor-paragraph',
+		);
+
+		$this->registry->register( $name, $settings );
+		$block_type = $this->registry->unregister( $name );
+		$this->assertEquals( $name, $block_type->name );
+		$this->assertEquals( $settings['icon'], $block_type->icon );
+		$this->assertFalse( $this->registry->is_registered( $name ) );
+	}
+
+	function test_get_all_registered() {
+		$names    = array( 'core/paragraph', 'core/image', 'core/blockquote' );
+		$settings = array(
+			'icon' => 'random',
+		);
+
+		foreach ( $names as $name ) {
+			$this->registry->register( $name, $settings );
+		}
+
+		$registered = $this->registry->get_all_registered();
+		$this->assertEqualSets( $names, array_keys( $registered ) );
+	}
+}
diff --git a/tests/phpunit/tests/blocks/class-block-type-test.php b/tests/phpunit/tests/blocks/class-block-type-test.php
new file mode 100644
index 0000000000..cada34b83c
--- /dev/null
+++ b/tests/phpunit/tests/blocks/class-block-type-test.php
@@ -0,0 +1,258 @@
+<?php
+/**
+ * WP_Block_Type Tests
+ *
+ * @package Gutenberg
+ */
+
+/**
+ * Tests for WP_Block_Type
+ */
+class Block_Type_Test extends WP_UnitTestCase {
+	function setUp() {
+		parent::setUp();
+	}
+
+	/**
+	 * Editor user ID.
+	 *
+	 * @var int
+	 */
+	protected static $editor_user_id;
+
+	/**
+	 * ID for a post containing blocks.
+	 *
+	 * @var int
+	 */
+	protected static $post_with_blocks;
+
+	/**
+	 * ID for a post without blocks.
+	 *
+	 * @var int
+	 */
+	protected static $post_without_blocks;
+
+	/**
+	 * Set up before class.
+	 */
+	public static function wpSetUpBeforeClass() {
+		self::$editor_user_id = self::factory()->user->create(
+			array(
+				'role' => 'editor',
+			)
+		);
+
+		self::$post_with_blocks = self::factory()->post->create(
+			array(
+				'post_title'   => 'Example',
+				'post_content' => "<!-- wp:core/text {\"dropCap\":true} -->\n<p class=\"has-drop-cap\">Tester</p>\n<!-- /wp:core/text -->",
+			)
+		);
+
+		self::$post_without_blocks = self::factory()->post->create(
+			array(
+				'post_title'   => 'Example',
+				'post_content' => 'Tester',
+			)
+		);
+	}
+
+	function test_set_props() {
+		$name = 'core/dummy';
+		$args = array(
+			'render_callback' => array( $this, 'render_dummy_block' ),
+			'foo'             => 'bar',
+		);
+
+		$block_type = new WP_Block_Type( $name, $args );
+
+		$this->assertSame( $name, $block_type->name );
+		$this->assertSame( $args['render_callback'], $block_type->render_callback );
+		$this->assertSame( $args['foo'], $block_type->foo );
+	}
+
+	function test_render() {
+		$attributes = array(
+			'foo' => 'bar',
+			'bar' => 'foo',
+		);
+
+		$block_type = new WP_Block_Type(
+			'core/dummy',
+			array(
+				'render_callback' => array( $this, 'render_dummy_block' ),
+			)
+		);
+		$output     = $block_type->render( $attributes );
+		$this->assertEquals( $attributes, json_decode( $output, true ) );
+	}
+
+	function test_render_with_content() {
+		$attributes = array(
+			'foo' => 'bar',
+			'bar' => 'foo',
+		);
+
+		$content = 'baz';
+
+		$expected = array_merge( $attributes, array( '_content' => $content ) );
+
+		$block_type = new WP_Block_Type(
+			'core/dummy',
+			array(
+				'render_callback' => array( $this, 'render_dummy_block_with_content' ),
+			)
+		);
+		$output     = $block_type->render( $attributes, $content );
+		$this->assertEquals( $expected, json_decode( $output, true ) );
+	}
+
+	function test_render_for_static_block() {
+		$block_type = new WP_Block_Type( 'core/dummy', array() );
+		$output     = $block_type->render();
+
+		$this->assertEquals( '', $output );
+	}
+
+	function test_is_dynamic_for_static_block() {
+		$block_type = new WP_Block_Type( 'core/dummy', array() );
+
+		$this->assertFalse( $block_type->is_dynamic() );
+	}
+
+	function test_is_dynamic_for_dynamic_block() {
+		$block_type = new WP_Block_Type(
+			'core/dummy',
+			array(
+				'render_callback' => array( $this, 'render_dummy_block' ),
+			)
+		);
+
+		$this->assertTrue( $block_type->is_dynamic() );
+	}
+
+	function test_prepare_attributes() {
+		$attributes = array(
+			'correct'            => 'include',
+			'wrongType'          => 5,
+			'wrongTypeDefaulted' => 5,
+			/* missingDefaulted */
+			'undefined'          => 'omit',
+		);
+
+		$block_type = new WP_Block_Type(
+			'core/dummy',
+			array(
+				'attributes' => array(
+					'correct'            => array(
+						'type' => 'string',
+					),
+					'wrongType'          => array(
+						'type' => 'string',
+					),
+					'wrongTypeDefaulted' => array(
+						'type'    => 'string',
+						'default' => 'defaulted',
+					),
+					'missingDefaulted'   => array(
+						'type'    => 'string',
+						'default' => 'define',
+					),
+				),
+			)
+		);
+
+		$prepared_attributes = $block_type->prepare_attributes_for_render( $attributes );
+
+		$this->assertEquals(
+			array(
+				'correct'            => 'include',
+				'wrongType'          => null,
+				'wrongTypeDefaulted' => 'defaulted',
+				'missingDefaulted'   => 'define',
+			),
+			$prepared_attributes
+		);
+	}
+
+	function test_has_block_with_mixed_content() {
+		$mixed_post_content = 'before' .
+		'<!-- wp:core/dummy --><!-- /wp:core/dummy -->' .
+		'<!-- wp:core/dummy_atts {"value":"b1"} --><!-- /wp:core/dummy_atts -->' .
+		'<!-- wp:core/dummy-child -->
+		<p>testing the test</p>
+		<!-- /wp:core/dummy-child -->' .
+		'between' .
+		'<!-- wp:core/self-close-dummy /-->' .
+		'<!-- wp:custom/dummy {"value":"b2"} /-->' .
+		'after';
+
+		$this->assertTrue( has_block( 'core/dummy', $mixed_post_content ) );
+
+		$this->assertTrue( has_block( 'core/dummy_atts', $mixed_post_content ) );
+
+		$this->assertTrue( has_block( 'core/dummy-child', $mixed_post_content ) );
+
+		$this->assertTrue( has_block( 'core/self-close-dummy', $mixed_post_content ) );
+
+		$this->assertTrue( has_block( 'custom/dummy', $mixed_post_content ) );
+
+		// checking for a partial block name should fail.
+		$this->assertFalse( has_block( 'core/dumm', $mixed_post_content ) );
+
+		// checking for a wrong namespace should fail.
+		$this->assertFalse( has_block( 'custom/dummy_atts', $mixed_post_content ) );
+
+		// checking for namespace only should not work. Or maybe ... ?
+		$this->assertFalse( has_block( 'core', $mixed_post_content ) );
+	}
+
+	function test_has_block_with_invalid_content() {
+		// some content with invalid HMTL comments and a single valid block.
+		$invalid_content = 'before' .
+		'<!- - wp:core/weird-space --><!-- /wp:core/weird-space -->' .
+		'<!--wp:core/untrimmed-left --><!-- /wp:core/untrimmed -->' .
+		'<!-- wp:core/dummy --><!-- /wp:core/dummy -->' .
+		'<!-- wp:core/untrimmed-right--><!-- /wp:core/untrimmed2 -->' .
+		'after';
+
+		$this->assertFalse( has_block( 'core/text', self::$post_without_blocks ) );
+
+		$this->assertFalse( has_block( 'core/weird-space', $invalid_content ) );
+
+		$this->assertFalse( has_block( 'core/untrimmed-left', $invalid_content ) );
+
+		$this->assertFalse( has_block( 'core/untrimmed-right', $invalid_content ) );
+
+		$this->assertTrue( has_block( 'core/dummy', $invalid_content ) );
+	}
+
+	function test_post_has_block() {
+		// should fail for a non-existent block `custom/dummy`.
+		$this->assertFalse( has_block( 'custom/dummy', self::$post_with_blocks ) );
+
+		// this functions should not work without the second param until the $post global is set.
+		$this->assertFalse( has_block( 'core/text' ) );
+		$this->assertFalse( has_block( 'core/dummy' ) );
+
+		global $post;
+		$post = get_post( self::$post_with_blocks );
+
+		// check if the function correctly detects content from the $post global.
+		$this->assertTrue( has_block( 'core/text' ) );
+		// even if it detects a proper $post global it should still be false for a missing block.
+		$this->assertFalse( has_block( 'core/dummy' ) );
+	}
+
+	function render_dummy_block( $attributes ) {
+		return json_encode( $attributes );
+	}
+
+	function render_dummy_block_with_content( $attributes, $content ) {
+		$attributes['_content'] = $content;
+
+		return json_encode( $attributes );
+	}
+}
