• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Yoast / wordpress-seo / 6987097851

25 Nov 2023 04:49AM UTC coverage: 49.206% (-0.1%) from 49.302%
6987097851

push

github

web-flow
Merge pull request #20878 from Yoast/JRF/ghactions-minor-tweak

GH Actions: update a few links in inline comments

15305 of 31104 relevant lines covered (49.21%)

4.03 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

81.4
/src/generators/schema/article.php
1
<?php
2

3
namespace Yoast\WP\SEO\Generators\Schema;
4

5
use WP_User;
6
use Yoast\WP\SEO\Config\Schema_IDs;
7

8
/**
9
 * Returns schema Article data.
10
 */
11
class Article extends Abstract_Schema_Piece {
12

13
        /**
14
         * Determines whether or not a piece should be added to the graph.
15
         *
16
         * @return bool
17
         */
18
        public function is_needed() {
8✔
19
                if ( $this->context->indexable->object_type !== 'post' ) {
8✔
20
                        return false;
2✔
21
                }
22

23
                // If we cannot output a publisher, we shouldn't output an Article.
24
                if ( $this->context->site_represents === false ) {
6✔
25
                        return false;
2✔
26
                }
27

28
                // If we cannot output an author, we shouldn't output an Article.
29
                if ( ! $this->helpers->schema->article->is_author_supported( $this->context->indexable->object_sub_type ) ) {
4✔
30
                        return false;
2✔
31
                }
32

33
                if ( $this->context->schema_article_type !== 'None' ) {
2✔
34
                        $this->context->has_article = true;
2✔
35
                        return true;
2✔
36
                }
37

38
                return false;
×
39
        }
40

41
        /**
42
         * Returns Article data.
43
         *
44
         * @return array Article data.
45
         */
46
        public function generate() {
14✔
47
                $author = \get_userdata( $this->context->post->post_author );
14✔
48
                $data   = [
7✔
49
                        '@type'            => $this->context->schema_article_type,
14✔
50
                        '@id'              => $this->context->canonical . Schema_IDs::ARTICLE_HASH,
14✔
51
                        'isPartOf'         => [ '@id' => $this->context->main_schema_id ],
14✔
52
                        'author'           => [
7✔
53
                                'name' => ( $author instanceof WP_User ) ? $this->helpers->schema->html->smart_strip_tags( $author->display_name ) : '',
14✔
54
                                '@id'  => $this->helpers->schema->id->get_user_schema_id( $this->context->post->post_author, $this->context ),
14✔
55
                        ],
7✔
56
                        'headline'         => $this->helpers->schema->html->smart_strip_tags( $this->helpers->post->get_post_title_with_fallback( $this->context->id ) ),
14✔
57
                        'datePublished'    => $this->helpers->date->format( $this->context->post->post_date_gmt ),
14✔
58
                        'dateModified'     => $this->helpers->date->format( $this->context->post->post_modified_gmt ),
14✔
59
                        'mainEntityOfPage' => [ '@id' => $this->context->main_schema_id ],
14✔
60
                        'wordCount'        => $this->word_count( $this->context->post->post_content, $this->context->post->post_title ),
14✔
61
                ];
7✔
62

63
                if ( $this->context->post->comment_status === 'open' ) {
14✔
64
                        $data['commentCount'] = \intval( $this->context->post->comment_count, 10 );
10✔
65
                }
66

67
                if ( $this->context->site_represents_reference ) {
14✔
68
                        $data['publisher'] = $this->context->site_represents_reference;
2✔
69
                }
70

71
                $data = $this->add_image( $data );
14✔
72
                $data = $this->add_keywords( $data );
14✔
73
                $data = $this->add_sections( $data );
14✔
74
                $data = $this->helpers->schema->language->add_piece_language( $data );
14✔
75

76
                if ( \post_type_supports( $this->context->post->post_type, 'comments' ) && $this->context->post->comment_status === 'open' ) {
14✔
77
                        $data = $this->add_potential_action( $data );
8✔
78
                }
79

80
                return $data;
14✔
81
        }
82

83
        /**
84
         * Adds tags as keywords, if tags are assigned.
85
         *
86
         * @param array $data Article data.
87
         *
88
         * @return array Article data.
89
         */
90
        private function add_keywords( $data ) {
14✔
91
                /**
92
                 * Filter: 'wpseo_schema_article_keywords_taxonomy' - Allow changing the taxonomy used to assign keywords to a post type Article data.
93
                 *
94
                 * @api string $taxonomy The chosen taxonomy.
95
                 */
96
                $taxonomy = \apply_filters( 'wpseo_schema_article_keywords_taxonomy', 'post_tag' );
14✔
97

98
                return $this->add_terms( $data, 'keywords', $taxonomy );
14✔
99
        }
100

101
        /**
102
         * Adds categories as sections, if categories are assigned.
103
         *
104
         * @param array $data Article data.
105
         *
106
         * @return array Article data.
107
         */
108
        private function add_sections( $data ) {
14✔
109
                /**
110
                 * Filter: 'wpseo_schema_article_sections_taxonomy' - Allow changing the taxonomy used to assign keywords to a post type Article data.
111
                 *
112
                 * @api string $taxonomy The chosen taxonomy.
113
                 */
114
                $taxonomy = \apply_filters( 'wpseo_schema_article_sections_taxonomy', 'category' );
14✔
115

116
                return $this->add_terms( $data, 'articleSection', $taxonomy );
14✔
117
        }
118

119
        /**
120
         * Adds a term or multiple terms, comma separated, to a field.
121
         *
122
         * @param array  $data     Article data.
123
         * @param string $key      The key in data to save the terms in.
124
         * @param string $taxonomy The taxonomy to retrieve the terms from.
125
         *
126
         * @return mixed Article data.
127
         */
128
        protected function add_terms( $data, $key, $taxonomy ) {
14✔
129
                $terms = \get_the_terms( $this->context->id, $taxonomy );
14✔
130

131
                if ( ! \is_array( $terms ) ) {
14✔
132
                        return $data;
2✔
133
                }
134

135
                $callback = static function( $term ) {
6✔
136
                        // We are using the WordPress internal translation.
137
                        return $term->name !== \__( 'Uncategorized', 'default' );
10✔
138
                };
12✔
139
                $terms    = \array_filter( $terms, $callback );
12✔
140

141
                if ( empty( $terms ) ) {
12✔
142
                        return $data;
2✔
143
                }
144

145
                $data[ $key ] = \wp_list_pluck( $terms, 'name' );
10✔
146

147
                return $data;
10✔
148
        }
149

150
        /**
151
         * Adds an image node if the post has a featured image.
152
         *
153
         * @param array $data The Article data.
154
         *
155
         * @return array The Article data.
156
         */
157
        private function add_image( $data ) {
14✔
158
                if ( $this->context->main_image_url !== null ) {
14✔
159
                        $data['image']        = [
14✔
160
                                '@id' => $this->context->canonical . Schema_IDs::PRIMARY_IMAGE_HASH,
14✔
161
                        ];
7✔
162
                        $data['thumbnailUrl'] = $this->context->main_image_url;
14✔
163
                }
164

165
                return $data;
14✔
166
        }
167

168
        /**
169
         * Adds the potential action property to the Article Schema piece.
170
         *
171
         * @param array $data The Article data.
172
         *
173
         * @return array The Article data with the potential action added.
174
         */
175
        private function add_potential_action( $data ) {
8✔
176
                /**
177
                 * Filter: 'wpseo_schema_article_potential_action_target' - Allows filtering of the schema Article potentialAction target.
178
                 *
179
                 * @api array $targets The URLs for the Article potentialAction target.
180
                 */
181
                $targets = \apply_filters( 'wpseo_schema_article_potential_action_target', [ $this->context->canonical . '#respond' ] );
8✔
182

183
                $data['potentialAction'][] = [
8✔
184
                        '@type'  => 'CommentAction',
8✔
185
                        'name'   => 'Comment',
8✔
186
                        'target' => $targets,
8✔
187
                ];
4✔
188

189
                return $data;
8✔
190
        }
191

192
        /**
193
         * Does a simple word count but tries to be relatively smart about it.
194
         *
195
         * @param string $post_content The post content.
196
         * @param string $post_title   The post title.
197
         *
198
         * @return int The number of words in the content.
199
         */
200
        private function word_count( $post_content, $post_title = '' ) {
×
201
                // Add the title to our word count.
202
                $post_content = $post_title . ' ' . $post_content;
×
203

204
                // Strip pre/code blocks and their content.
205
                $post_content = \preg_replace( '@<(pre|code)[^>]*?>.*?</\\1>@si', '', $post_content );
×
206

207
                // Add space between tags that don't have it.
208
                $post_content = \preg_replace( '@><@', '> <', $post_content );
×
209

210
                // Strips all other tags.
211
                $post_content = \wp_strip_all_tags( $post_content );
×
212

213
                $characters = '';
×
214

215
                if ( \preg_match( '@[а-я]@ui', $post_content ) ) {
×
216
                        // Correct counting of the number of words in the Russian and Ukrainian languages.
217
                        $alphabet = [
218
                                'ru' => 'абвгдеёжзийклмнопрстуфхцчшщъыьэюя',
×
219
                                'ua' => 'абвгґдеєжзиіїйклмнопрстуфхцчшщьюя',
220
                        ];
221

222
                        $characters  = \implode( '', $alphabet );
×
223
                        $characters  = \preg_split( '//u', $characters, -1, \PREG_SPLIT_NO_EMPTY );
×
224
                        $characters  = \array_unique( $characters );
×
225
                        $characters  = \implode( '', $characters );
×
226
                        $characters .= \mb_strtoupper( $characters );
×
227
                }
228

229
                // Remove characters from HTML entities.
230
                $post_content = \preg_replace( '@&[a-z0-9]+;@i', ' ', \htmlentities( $post_content ) );
×
231

232
                return \str_word_count( $post_content, 0, $characters );
×
233
        }
234
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc