class-schema.php 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. <?php
  2. /**
  3. * WPSEO plugin file.
  4. *
  5. * @package WPSEO\Frontend\Schema
  6. */
  7. /**
  8. * Class WPSEO_Schema
  9. *
  10. * Outputs schema code specific for Google's JSON LD stuff.
  11. *
  12. * @since 1.8
  13. */
  14. class WPSEO_Schema implements WPSEO_WordPress_Integration {
  15. /**
  16. * Holds the parsed blocks for the current page.
  17. *
  18. * @var array
  19. */
  20. private $parsed_blocks = [];
  21. /**
  22. * Holds context variables about the current page and site.
  23. *
  24. * @var WPSEO_Schema_Context
  25. */
  26. private $context;
  27. /**
  28. * Registers the hooks.
  29. */
  30. public function register_hooks() {
  31. add_action( 'wpseo_head', [ $this, 'json_ld' ], 91 );
  32. add_action( 'wpseo_json_ld', [ $this, 'generate' ], 1 );
  33. // This AMP hook is only used in Reader (formerly Classic) mode.
  34. add_action( 'amp_post_template_head', [ $this, 'json_ld' ], 9 );
  35. }
  36. /**
  37. * JSON LD output function that the functions for specific code can hook into.
  38. *
  39. * @since 1.8
  40. */
  41. public function json_ld() {
  42. $deprecated_data = [
  43. '_deprecated' => 'Please use the "wpseo_schema_*" filters to extend the Yoast SEO schema data - see the WPSEO_Schema class.',
  44. ];
  45. /**
  46. * Filter: 'wpseo_json_ld_output' - Allows disabling Yoast's schema output entirely.
  47. *
  48. * @api mixed If false or an empty array is returned, disable our output.
  49. */
  50. $return = apply_filters( 'wpseo_json_ld_output', $deprecated_data, '' );
  51. if ( $return === [] || $return === false ) {
  52. return;
  53. }
  54. // Remove the AMP hook that also outputs Schema metadata on AMP pages.
  55. remove_action( 'amp_post_template_head', 'amp_print_schemaorg_metadata' );
  56. do_action( 'wpseo_json_ld' );
  57. }
  58. /**
  59. * Outputs the JSON LD code in a valid JSON+LD wrapper.
  60. *
  61. * @since 10.2
  62. *
  63. * @return void
  64. */
  65. public function generate() {
  66. $graph = [];
  67. $this->context = new WPSEO_Schema_Context();
  68. $pieces = $this->get_graph_pieces();
  69. // Parse the Gutenberg blocks so we know whether to show pieces for those.
  70. $this->parse_blocks();
  71. foreach ( $pieces as $piece ) {
  72. $class = str_replace( 'wpseo_schema_', '', strtolower( get_class( $piece ) ) );
  73. /**
  74. * Filter: 'wpseo_schema_needs_<class name>' - Allows changing which graph pieces we output.
  75. *
  76. * @api bool $is_needed Whether or not to show a graph piece.
  77. */
  78. $is_needed = apply_filters( 'wpseo_schema_needs_' . $class, $piece->is_needed() );
  79. if ( ! $is_needed ) {
  80. continue;
  81. }
  82. $graph_piece = $piece->generate();
  83. /**
  84. * Filter: 'wpseo_schema_<class name>' - Allows changing graph piece output.
  85. *
  86. * @api array $graph_piece The graph piece to filter.
  87. */
  88. $graph_piece = apply_filters( 'wpseo_schema_' . $class, $graph_piece );
  89. if ( is_array( $graph_piece ) ) {
  90. $graph[] = $graph_piece;
  91. }
  92. }
  93. foreach ( $this->parsed_blocks as $block_type => $blocks ) {
  94. foreach ( $blocks as $block ) {
  95. /**
  96. * Filter: 'wpseo_schema_block_<block-type>' - Allows filtering graph output per block.
  97. *
  98. * @param WP_Block_Parser_Block $block The block.
  99. * @param WPSEO_Schema_Context $context A value object with context variables.
  100. *
  101. * @api array $graph Our Schema output.
  102. */
  103. $block_type = strtolower( $block['blockName'] );
  104. $graph = apply_filters( 'wpseo_schema_block_' . $block_type, $graph, $block, $this->context );
  105. }
  106. }
  107. WPSEO_Utils::schema_output( $graph, 'yoast-schema-graph yoast-schema-graph--main' );
  108. }
  109. /**
  110. * Gets all the graph pieces we need.
  111. *
  112. * @return array A filtered array of graph pieces.
  113. */
  114. private function get_graph_pieces() {
  115. $pieces = [
  116. new WPSEO_Schema_Organization( $this->context ),
  117. new WPSEO_Schema_Person( $this->context ),
  118. new WPSEO_Schema_Website( $this->context ),
  119. new WPSEO_Schema_MainImage( $this->context ),
  120. new WPSEO_Schema_WebPage( $this->context ),
  121. new WPSEO_Schema_Breadcrumb( $this->context ),
  122. new WPSEO_Schema_Article( $this->context ),
  123. new WPSEO_Schema_Author( $this->context ),
  124. new WPSEO_Schema_FAQ( $this->context ),
  125. new WPSEO_Schema_HowTo( $this->context ),
  126. ];
  127. /**
  128. * Filter: 'wpseo_schema_graph_pieces' - Allows adding pieces to the graph.
  129. *
  130. * @param WPSEO_Schema_Context $context An object with context variables.
  131. *
  132. * @api array $pieces The schema pieces.
  133. */
  134. return apply_filters( 'wpseo_schema_graph_pieces', $pieces, $this->context );
  135. }
  136. /**
  137. * Parse the blocks and pass them on to our head.
  138. */
  139. private function parse_blocks() {
  140. if ( ! function_exists( 'parse_blocks' ) ) {
  141. return;
  142. }
  143. if ( ! is_singular() ) {
  144. return;
  145. }
  146. $this->get_parsed_blocks();
  147. foreach ( array_keys( $this->parsed_blocks ) as $block_type ) {
  148. /**
  149. * Filter: 'wpseo_pre_schema_block_type_<block-type>' - Allows hooking things to change graph output based on the blocks on the page.
  150. *
  151. * @param string $block_type The block type.
  152. * @param array $blocks All the blocks of this block type.
  153. * @param WPSEO_Schema_Context $context A value object with context variables.
  154. */
  155. do_action( 'wpseo_pre_schema_block_type_' . $block_type, $this->parsed_blocks[ $block_type ], $this->context );
  156. }
  157. }
  158. /**
  159. * Parse the blocks and loop through them.
  160. */
  161. private function get_parsed_blocks() {
  162. $post = get_post();
  163. $parsed_blocks = parse_blocks( $post->post_content );
  164. foreach ( $parsed_blocks as $block ) {
  165. if ( ! isset( $this->parsed_blocks[ $block['blockName'] ] ) || ! is_array( $this->parsed_blocks[ $block['blockName'] ] ) ) {
  166. $this->parsed_blocks[ $block['blockName'] ] = [];
  167. }
  168. $this->parsed_blocks[ $block['blockName'] ][] = $block;
  169. }
  170. }
  171. }