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

Yoast / wordpress-seo / 6c9a496186af09ad5b9d1b8a59520a14b7920b96

15 Feb 2026 10:29PM UTC coverage: 52.986% (+0.01%) from 52.975%
6c9a496186af09ad5b9d1b8a59520a14b7920b96

push

github

web-flow
Merge pull request #22986 from Yoast/JRF/QA/use-more-specific-check

CS/QA: use slightly more specific checks in a few places

8482 of 15955 branches covered (53.16%)

Branch coverage included in aggregate %.

32462 of 61318 relevant lines covered (52.94%)

48791.32 hits per line

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

74.12
/src/surfaces/meta-surface.php
1
<?php
2

3
namespace Yoast\WP\SEO\Surfaces;
4

5
use Yoast\WP\SEO\Context\Meta_Tags_Context;
6
use Yoast\WP\SEO\Helpers\Indexable_Helper;
7
use Yoast\WP\SEO\Memoizers\Meta_Tags_Context_Memoizer;
8
use Yoast\WP\SEO\Models\Indexable;
9
use Yoast\WP\SEO\Repositories\Indexable_Repository;
10
use Yoast\WP\SEO\Surfaces\Values\Meta;
11
use Yoast\WP\SEO\Wrappers\WP_Rewrite_Wrapper;
12
use YoastSEO_Vendor\Symfony\Component\DependencyInjection\ContainerInterface;
13

14
/**
15
 * Meta_Surface class.
16
 *
17
 * Surface for the indexables.
18
 */
19
class Meta_Surface {
20

21
        /**
22
         * The container.
23
         *
24
         * @var ContainerInterface
25
         */
26
        private $container;
27

28
        /**
29
         * The memoizer for the meta tags context.
30
         *
31
         * @var Meta_Tags_Context_Memoizer
32
         */
33
        private $context_memoizer;
34

35
        /**
36
         * The indexable repository.
37
         *
38
         * @var Indexable_Repository
39
         */
40
        private $repository;
41

42
        /**
43
         * Holds the WP rewrite wrapper instance.
44
         *
45
         * @var WP_Rewrite_Wrapper
46
         */
47
        private $wp_rewrite_wrapper;
48

49
        /**
50
         * The indexable helper.
51
         *
52
         * @var Indexable_Helper
53
         */
54
        private $indexable_helper;
55

56
        /**
57
         * Meta_Surface constructor.
58
         *
59
         * @param ContainerInterface         $container            The DI container.
60
         * @param Meta_Tags_Context_Memoizer $context_memoizer     The meta tags context memoizer.
61
         * @param Indexable_Repository       $indexable_repository The indexable repository.
62
         * @param WP_Rewrite_Wrapper         $wp_rewrite_wrapper   The WP rewrite wrapper.
63
         * @param Indexable_Helper           $indexable_helper     The indexable helper.
64
         */
65
        public function __construct(
×
66
                ContainerInterface $container,
67
                Meta_Tags_Context_Memoizer $context_memoizer,
68
                Indexable_Repository $indexable_repository,
69
                WP_Rewrite_Wrapper $wp_rewrite_wrapper,
70
                Indexable_Helper $indexable_helper
71
        ) {
72
                $this->container          = $container;
×
73
                $this->context_memoizer   = $context_memoizer;
×
74
                $this->repository         = $indexable_repository;
×
75
                $this->wp_rewrite_wrapper = $wp_rewrite_wrapper;
×
76
                $this->indexable_helper   = $indexable_helper;
×
77
        }
78

79
        /**
80
         * Returns the meta tags context for the current page.
81
         *
82
         * @return Meta The meta values.
83
         */
84
        public function for_current_page() {
2✔
85
                return $this->build_meta( $this->context_memoizer->for_current_page() );
2✔
86
        }
87

88
        /**
89
         * Returns the meta tags context for the home page.
90
         *
91
         * @return Meta|false The meta values. False if none could be found.
92
         */
93
        public function for_home_page() {
10✔
94
                $front_page_id = (int) \get_option( 'page_on_front' );
10✔
95
                if ( \get_option( 'show_on_front' ) === 'page' && $front_page_id !== 0 ) {
10✔
96
                        $indexable = $this->repository->find_by_id_and_type( $front_page_id, 'post' );
4✔
97

98
                        if ( ! $indexable ) {
4✔
99
                                return false;
2✔
100
                        }
101

102
                        return $this->build_meta( $this->context_memoizer->get( $indexable, 'Static_Home_Page' ) );
2✔
103
                }
104

105
                $indexable = $this->repository->find_for_home_page();
6✔
106

107
                if ( ! $indexable ) {
6✔
108
                        return false;
2✔
109
                }
110

111
                return $this->build_meta( $this->context_memoizer->get( $indexable, 'Home_Page' ) );
4✔
112
        }
113

114
        /**
115
         * Returns the meta tags context for the posts page.
116
         *
117
         * @return Meta|false The meta values. False if none could be found.
118
         */
119
        public function for_posts_page() {
8✔
120
                $posts_page_id = (int) \get_option( 'page_for_posts' );
8✔
121
                if ( $posts_page_id !== 0 ) {
8✔
122
                        $indexable = $this->repository->find_by_id_and_type( $posts_page_id, 'post' );
4✔
123

124
                        if ( ! $indexable ) {
4✔
125
                                return false;
2✔
126
                        }
127

128
                        return $this->build_meta( $this->context_memoizer->get( $indexable, 'Static_Posts_Page' ) );
2✔
129
                }
130

131
                $indexable = $this->repository->find_for_home_page();
4✔
132

133
                if ( ! $indexable ) {
4✔
134
                        return false;
2✔
135
                }
136

137
                return $this->build_meta( $this->context_memoizer->get( $indexable, 'Home_Page' ) );
2✔
138
        }
139

140
        /**
141
         * Returns the meta tags context for a post type archive.
142
         *
143
         * @param string|null $post_type Optional. The post type to get the archive meta for. Defaults to the current post type.
144
         *
145
         * @return Meta|false The meta values. False if none could be found.
146
         */
147
        public function for_post_type_archive( $post_type = null ) {
4✔
148
                $post_type ??= \get_post_type();
4✔
149

150
                $indexable = $this->repository->find_for_post_type_archive( $post_type );
4✔
151

152
                if ( ! $indexable ) {
4✔
153
                        return false;
2✔
154
                }
155

156
                return $this->build_meta( $this->context_memoizer->get( $indexable, 'Post_Type_Archive' ) );
2✔
157
        }
158

159
        /**
160
         * Returns the meta tags context for the search result page.
161
         *
162
         * @return Meta|false The meta values. False if none could be found.
163
         */
164
        public function for_search_result() {
4✔
165
                $indexable = $this->repository->find_for_system_page( 'search-result' );
4✔
166

167
                if ( ! $indexable ) {
4✔
168
                        return false;
2✔
169
                }
170

171
                return $this->build_meta( $this->context_memoizer->get( $indexable, 'Search_Result_Page' ) );
2✔
172
        }
173

174
        /**
175
         * Returns the meta tags context for the search result page.
176
         *
177
         * @return Meta|false The meta values. False if none could be found.
178
         */
179
        public function for_404() {
4✔
180
                $indexable = $this->repository->find_for_system_page( '404' );
4✔
181

182
                if ( ! $indexable ) {
4✔
183
                        return false;
2✔
184
                }
185

186
                return $this->build_meta( $this->context_memoizer->get( $indexable, 'Error_Page' ) );
2✔
187
        }
188

189
        /**
190
         * Returns the meta tags context for a post.
191
         *
192
         * @param int $id The ID of the post.
193
         *
194
         * @return Meta|false The meta values. False if none could be found.
195
         */
196
        public function for_post( $id ) {
4✔
197
                $indexable = $this->repository->find_by_id_and_type( $id, 'post' );
4✔
198

199
                if ( ! $indexable ) {
4✔
200
                        return false;
2✔
201
                }
202

203
                return $this->build_meta( $this->context_memoizer->get( $indexable, 'Post_Type' ) );
2✔
204
        }
205

206
        /**
207
         * Returns the meta tags context for a number of posts.
208
         *
209
         * @param int[] $ids The IDs of the posts.
210
         *
211
         * @return Meta[]|false The meta values. False if none could be found.
212
         */
213
        public function for_posts( $ids ) {
2✔
214
                $indexables = $this->repository->find_by_multiple_ids_and_type( $ids, 'post' );
2✔
215

216
                if ( empty( $indexables ) ) {
2✔
217
                        return false;
×
218
                }
219

220
                // Remove all false values.
221
                $indexables = \array_filter( $indexables );
2✔
222

223
                return \array_map(
2✔
224
                        function ( $indexable ) {
2✔
225
                                return $this->build_meta( $this->context_memoizer->get( $indexable, 'Post_Type' ) );
2✔
226
                        },
2✔
227
                        $indexables
2✔
228
                );
2✔
229
        }
230

231
        /**
232
         * Returns the meta tags context for a term.
233
         *
234
         * @param int $id The ID of the term.
235
         *
236
         * @return Meta|false The meta values. False if none could be found.
237
         */
238
        public function for_term( $id ) {
2✔
239
                $indexable = $this->repository->find_by_id_and_type( $id, 'term' );
240

241
                if ( ! $indexable ) {
2✔
242
                        return false;
243
                }
244

245
                return $this->build_meta( $this->context_memoizer->get( $indexable, 'Term_Archive' ) );
246
        }
247

248
        /**
249
         * Returns the meta tags context for an author.
250
         *
251
         * @param int $id The ID of the author.
252
         *
253
         * @return Meta|false The meta values. False if none could be found.
254
         */
255
        public function for_author( $id ) {
2✔
256
                $indexable = $this->repository->find_by_id_and_type( $id, 'user' );
257

258
                if ( ! $indexable ) {
2✔
259
                        return false;
260
                }
261

262
                return $this->build_meta( $this->context_memoizer->get( $indexable, 'Author_Archive' ) );
263
        }
264

265
        /**
266
         * Returns the meta for an indexable.
267
         *
268
         * @param Indexable   $indexable The indexable.
269
         * @param string|null $page_type Optional. The page type if already known.
270
         *
271
         * @return Meta|false The meta values. False if none could be found.
272
         */
273
        public function for_indexable( $indexable, $page_type = null ) {
×
274

275
                if ( ! \is_a( $indexable, Indexable::class ) ) {
×
276
                        return false;
277
                }
278
                $page_type ??= $this->indexable_helper->get_page_type_for_indexable( $indexable );
279

280
                return $this->build_meta( $this->context_memoizer->get( $indexable, $page_type ) );
281
        }
282

283
        /**
284
         * Returns the meta for an indexable.
285
         *
286
         * @param Indexable[] $indexables The indexables.
287
         * @param string|null $page_type  Optional. The page type if already known.
288
         *
289
         * @return Meta|false The meta values. False if none could be found.
290
         */
291
        public function for_indexables( $indexables, $page_type = null ) {
×
292
                $closure = function ( $indexable ) use ( $page_type ) {
293
                        $this_page_type   = $page_type;
×
294
                        $this_page_type ??= $this->indexable_helper->get_page_type_for_indexable( $indexable );
295

296
                        return $this->build_meta( $this->context_memoizer->get( $indexable, $this_page_type ) );
297
                };
298

299
                return \array_map( $closure, $indexables );
300
        }
301

302
        /**
303
         * Returns the meta tags context for a url.
304
         *
305
         * @param string $url The url of the page. Required to be relative to the site url.
306
         *
307
         * @return Meta|false The meta values. False if none could be found.
308
         */
309
        public function for_url( $url ) {
24✔
310
                $url_parts  = \wp_parse_url( $url );
24✔
311
                $site_parts = \wp_parse_url( \site_url() );
312

313
                if ( ( ! \is_array( $url_parts ) || ! \is_array( $site_parts ) )
24✔
314
                        || ! isset( $url_parts['host'], $url_parts['path'], $site_parts['host'], $site_parts['scheme'] )
315
                ) {
316
                        return false;
317
                }
318

319
                if ( $url_parts['host'] !== $site_parts['host'] ) {
×
320
                        return false;
321
                }
322
                // Ensure the scheme is consistent with values in the DB.
323
                $url = $site_parts['scheme'] . '://' . $url_parts['host'] . $url_parts['path'];
324

325
                if ( $this->is_date_archive_url( $url ) ) {
2✔
326
                        $indexable = $this->repository->find_for_date_archive();
327
                }
328
                else {
329
                        $indexable = $this->repository->find_by_permalink( $url );
330
                }
331

332
                // If we still don't have an indexable abort, the WP globals could be anything so we can't use the unknown indexable.
333
                if ( ! $indexable ) {
×
334
                        return false;
335
                }
336
                $page_type = $this->indexable_helper->get_page_type_for_indexable( $indexable );
337

338
                if ( $page_type === false ) {
×
339
                        return false;
340
                }
341

342
                return $this->build_meta( $this->context_memoizer->get( $indexable, $page_type ) );
343
        }
344

345
        /**
346
         * Checks if a given URL is a date archive URL.
347
         *
348
         * @param string $url The url.
349
         *
350
         * @return bool
351
         */
352
        protected function is_date_archive_url( $url ) {
×
353
                $path = \wp_parse_url( $url, \PHP_URL_PATH );
×
354
                if ( $path === null ) {
×
355
                        return false;
356
                }
357

358
                $path         = \ltrim( $path, '/' );
×
359
                $wp_rewrite   = $this->wp_rewrite_wrapper->get();
×
360
                $date_rewrite = $wp_rewrite->generate_rewrite_rules( $wp_rewrite->get_date_permastruct(), \EP_DATE );
×
361
                $date_rewrite = \apply_filters( 'date_rewrite_rules', $date_rewrite );
362

363
                foreach ( (array) $date_rewrite as $match => $query ) {
×
364
                        if ( \preg_match( "#^$match#", $path ) ) {
×
365
                                return true;
366
                        }
367
                }
368

369
                return false;
370
        }
371

372
        /**
373
         * Creates a new meta value object
374
         *
375
         * @param Meta_Tags_Context $context The meta tags context.
376
         *
377
         * @return Meta The meta value
378
         */
379
        protected function build_meta( Meta_Tags_Context $context ) {
380
                return new Meta( $context, $this->container );
381
        }
382
}
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

© 2026 Coveralls, Inc