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

AxeWP / wp-graphql-rank-math / 6257663232

21 Sep 2023 05:40AM UTC coverage: 92.176% (+0.8%) from 91.344%
6257663232

push

github

web-flow
chore: Update dev-deps and lint (#59)

* chore: update dev deps

* chore: Lint for wp-graphql-cs:2.0.0-beta

* chore: lint access-functions

* chore: set wp-graphql-stubs to fixed version

27 of 27 new or added lines in 12 files covered. (100.0%)

2533 of 2748 relevant lines covered (92.18%)

11.22 hits per line

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

89.68
/src/Model/Seo.php
1
<?php
2
/**
3
 * The abstract SEO model.
4
 *
5
 * @package \WPGraphQL\RankMath\Model
6
 */
7

8
namespace WPGraphQL\RankMath\Model;
9

10
use RankMath\Frontend\Breadcrumbs as RMBreadcrumbs;
11
use RankMath\Helper as RMHelper;
12
use RankMath\Paper\Paper;
13
use WPGraphQL\Model\Model;
14

15
/**
16
 * Class - Seo
17
 *
18
 * @property string $type The type of object.
19
 */
20
abstract class Seo extends Model {
21
        /**
22
         * Stores the incoming post data
23
         *
24
         * @var \WP_Post|\WP_Term|\WP_User|\WP_Post_Type $data
25
         */
26
        protected $data;
27

28
        /**
29
         * The database id for the current object.
30
         *
31
         * @var integer
32
         */
33
        protected int $database_id;
34

35
        /**
36
         * The current RankMath paper helper.
37
         *
38
         * @var \RankMath\Paper\Paper
39
         */
40
        protected $helper;
41

42
        /**
43
         * The settings prefix
44
         *
45
         * @var string
46
         */
47
        protected string $prefix;
48

49
        /**
50
         * The head markup.
51
         *
52
         * It's stored here to avoid having to query it multiple times.
53
         *
54
         * A `false` value is used to determine whether an attempt has already been made to fetch it.
55
         *
56
         * @var string|false|null
57
         */
58
        protected $full_head;
59

60
        /**
61
         * The Global Post at time of Model generation
62
         *
63
         * @var \WP_Post
64
         */
65
        protected $global_post;
66

67
        /**
68
         * Constructor.
69
         *
70
         * @param \WP_User|\WP_Term|\WP_Post|\WP_Post_Type $wp_object .
71
         * @param string                                   $capability .
72
         * @param string[]                                 $allowed_fields .
73
         */
74
        public function __construct( $wp_object, $capability = '', $allowed_fields = [] ) {
75
                $this->full_head = false;
6✔
76
                $this->data      = $wp_object;
6✔
77

78
                $allowed_fields = array_merge(
6✔
79
                        [
6✔
80
                                'breadcrumbs',
6✔
81
                                'title',
6✔
82
                                'description',
6✔
83
                                'robots',
6✔
84
                                'fullHead',
6✔
85
                                'jsonLd',
6✔
86
                                'openGraph',
6✔
87
                                'type',
6✔
88
                        ],
6✔
89
                        $allowed_fields
6✔
90
                );
6✔
91

92
                parent::__construct( $capability, $allowed_fields );
6✔
93

94
                rank_math()->variables->setup();
6✔
95
                // Seat up RM Globals.
96
                $url = $this->get_object_url();
6✔
97

98
                $this->setup_post_head( $url );
6✔
99
        }
100

101
        /**
102
         * {@inheritDoc}
103
         */
104
        public function setup(): void {
105
                Paper::reset();
6✔
106
                /** @var \RankMath\Paper\Paper $paper */
107
                $paper        = Paper::get();
6✔
108
                $this->helper = $paper;
6✔
109
        }
110

111
        /**
112
         * {@inheritDoc}
113
         */
114
        protected function init() {
115
                if ( empty( $this->fields ) ) {
6✔
116
                        $this->fields = [
6✔
117
                                'breadcrumbs'   => fn (): ?array => $this->get_breadcrumbs(),
6✔
118
                                'title'         => function (): ?string {
6✔
119
                                        $title = $this->helper->get_title();
6✔
120

121
                                        return ! empty( $title ) ? html_entity_decode( $title, ENT_QUOTES ) : null;
6✔
122
                                },
6✔
123
                                'description'   => function (): ?string {
6✔
124
                                        $description = $this->helper->get_description();
6✔
125

126
                                        return ! empty( $description ) ? html_entity_decode( $description, ENT_QUOTES ) : null;
6✔
127
                                },
6✔
128
                                'robots'        => function (): ?array {
6✔
129
                                        return $this->helper->get_robots() ?: null;
6✔
130
                                },
6✔
131
                                'canonicalUrl'  => function (): ?string {
6✔
132
                                        return $this->helper->get_canonical() ?: null;
5✔
133
                                },
6✔
134
                                'focusKeywords' => function (): ?array {
6✔
135
                                        $keywords = $this->helper->get_keywords();
5✔
136

137
                                        return ! empty( $keywords ) ? explode( ',', $keywords ) : null;
5✔
138
                                },
6✔
139
                                'fullHead'      => function (): ?string {
6✔
140
                                        $head = $this->get_head();
×
141
                                        return $head ?: null;
×
142
                                },
6✔
143
                                'jsonLd'        => static function () {
6✔
144
                                        ob_start();
6✔
145
                                        $json = new \RankMath\Schema\JsonLD();
6✔
146
                                        $json->setup();
6✔
147
                                        $json->json_ld();
6✔
148
                                        $output = ob_get_clean();
6✔
149

150
                                        return [ 'raw' => $output ?: null ];
6✔
151
                                },
6✔
152
                                'openGraph'     => function () {
6✔
153
                                        $head = $this->get_head();
1✔
154

155
                                        return ! empty( $head ) ? $this->parse_og_tags( $head ) : null;
1✔
156
                                },
6✔
157
                                'type'          => function (): string {
6✔
158
                                        return $this->get_object_type();
×
159
                                },
6✔
160
                        ];
6✔
161
                }
162
        }
163

164
        /**
165
         * Gets and parses the breadcrumbs for the object.
166
         *
167
         * @return ?array<string, string|mixed>[] The breadcrumbs.
168
         */
169
        protected function get_breadcrumbs(): ?array {
170
                // Get the crumbs and shape them.
171
                $crumbs      = RMBreadcrumbs::get()->get_crumbs();
5✔
172
                $breadcrumbs = array_map(
5✔
173
                        static function ( $crumb ) {
5✔
174
                                return [
5✔
175
                                        'text'     => $crumb[0] ?? null,
5✔
176
                                        'url'      => $crumb[1] ?? null,
5✔
177
                                        'isHidden' => ! empty( $crumb['hide_in_schema'] ),
5✔
178
                                ];
5✔
179
                        },
5✔
180
                        $crumbs
5✔
181
                );
5✔
182

183
                // Pop the current item's title.
184
                $remove_title = ( is_single( $this->database_id ) || is_page( $this->database_id ) ) && RMHelper::get_settings( 'general.breadcrumbs_remove_post_title' );
5✔
185
                if ( $remove_title ) {
5✔
186
                        array_pop( $breadcrumbs );
×
187
                }
188

189
                return ! empty( $breadcrumbs ) ? $breadcrumbs : null;
5✔
190
        }
191

192
        /**
193
         * Gets the hydrated meta, falling back to default settings.
194
         *
195
         * @param string $key The local meta key.
196
         * @param string $fallback Optional. The settings meta key.
197
         * @param string $default_value Optional. The default value.
198
         *
199
         * @return mixed|null
200
         */
201
        protected function get_meta( string $key, string $fallback = '', string $default_value = '' ) {
202
                $value = null;
5✔
203
                if ( $this->data instanceof \WP_Post ) {
5✔
204
                        $value = RMHelper::get_post_meta( $key, $this->database_id );
2✔
205
                } elseif ( $this->data instanceof \WP_Term ) {
3✔
206
                        $value = RMHelper::get_term_meta( $key, $this->database_id );
2✔
207
                } elseif ( $this->data instanceof \WP_User ) {
1✔
208
                        $value = RMHelper::get_user_meta( $key, $this->database_id );
1✔
209
                }
210

211
                if ( empty( $value ) && ! empty( $fallback ) ) {
5✔
212
                        $value = RMHelper::get_settings( "titles.{$fallback}", $default_value );
×
213
                        if ( ! empty( $value ) ) {
×
214
                                $value = RMHelper::replace_vars( $value, $this->data );
×
215
                        }
216
                }
217

218
                return ( ! empty( $value ) ? $value : $default_value ) ?: null;
5✔
219
        }
220

221
        /**
222
         * Gets the object type used to determine how the GraphQL interface should resolve.
223
         */
224
        abstract public function get_object_type(): string;
225

226
        /**
227
         * Gets the object-specific url to use for generating the RankMath <head>.
228
         */
229
        abstract protected function get_object_url(): string;
230

231
        /**
232
         * Gets the object-specific url to use for generating the RankMath <head>.
233
         *
234
         * @deprecated 0.0.8
235
         */
236
        protected function get_rest_url_param(): string {
237
                _deprecated_function( __FUNCTION__, '0.0.8', __NAMESPACE__ . '::get_object_url()' );
×
238
                return $this->get_object_url();
×
239
        }
240

241
        /**
242
         * Gets all the tags that go in the <head>.
243
         *
244
         * Shims the `RankMath\Rest\Headless::get_html_head() private method to avoid a REST Call.
245
         *
246
         * @throws \GraphQL\Error\Error When the REST request is invalid.
247
         * @throws \GraphQL\Error\UserError When REST response fails.
248
         */
249
        protected function get_head(): ?string {
250
                if ( false !== $this->full_head ) {
1✔
251
                        return $this->full_head;
×
252
                }
253

254
                ob_start();
1✔
255
                do_action( 'wp' ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals
1✔
256
                do_action( 'rank_math/head' );
1✔
257

258
                $head = ob_get_clean();
1✔
259

260
                $this->full_head = $head ?: null;
1✔
261

262
                return $this->full_head;
1✔
263
        }
264

265
        /**
266
         * Parses the Open Graph tags from the head.
267
         *
268
         * @param string $head The head.
269
         *
270
         * @return ?array<string, mixed> The tags.
271
         */
272
        protected function parse_og_tags( string $head ): ?array {
273
                $tags = [];
1✔
274

275
                if ( preg_match_all( '/<meta (property|name)="([^"]+):([^"]+)" content="([^"]+)" \/>/', $head, $matches ) ) {
1✔
276
                        $this->save_tags_from_matches( $matches, $tags );
1✔
277
                }
278

279
                return $tags ?: null;
1✔
280
        }
281

282
        /**
283
         * Saves the tags from the matches.
284
         *
285
         * @param string[][]           $matches The matches.
286
         * @param array<string, mixed> $tags The tags array reference.
287
         */
288
        private function save_tags_from_matches( array $matches, array &$tags ): void {
289
                // $matches[2] contains the OpenGraph prefix (og, article, twitter, etc ).
290
                foreach ( $matches[2] as $key => $prefix ) {
1✔
291
                        $property = $matches[3][ $key ];
1✔
292
                        $value    = $matches[4][ $key ];
1✔
293

294
                        // If meta tag already exists, save the values as an array.
295
                        if ( isset( $tags[ $prefix ][ $property ] ) ) {
1✔
296
                                if ( ! is_array( $tags[ $prefix ][ $property ] ) ) {
×
297
                                        $tags[ $prefix ][ $property ] = [ $tags[ $prefix ][ $property ] ];
×
298
                                }
299
                                $tags[ $prefix ][ $property ][] = $value;
×
300
                        } else {
301
                                $tags[ $prefix ][ $property ] = $value;
1✔
302
                        }
303
                }
304
        }
305

306
        /**
307
         * Prepare head output for a URL.
308
         *
309
         * Shims the RankMath\Rest\Headless::setup_post_head() private method to avoid a REST call.
310
         *
311
         * @param string $url The URL.
312
         */
313
        private function setup_post_head( string $url ): void {
314
                $headless = new \RankMath\Rest\Headless();
6✔
315
                // Setup WordPress.
316
                $_SERVER['REQUEST_URI'] = esc_url_raw( $headless->generate_request_uri( $url ) );
6✔
317
                remove_all_actions( 'wp' );
6✔
318
                remove_all_actions( 'parse_request' );
6✔
319
                remove_all_actions( 'rank_math/head' );
6✔
320
                remove_all_actions( 'rank_math/json_ld' );
6✔
321
                remove_all_actions( 'rank_math/opengraph/facebook' );
6✔
322
                remove_all_actions( 'rank_math/opengraph/twitter' );
6✔
323
                remove_all_actions( 'rank_math/opengraph/slack' );
6✔
324

325
                if ( $headless->is_home ) {
6✔
326
                        $GLOBALS['wp_query']->is_home = true;
1✔
327
                }
328

329
                remove_filter( 'option_rewrite_rules', [ $headless, 'fix_query_notice' ] );
6✔
330

331
                // Setup Rank Math.
332
                rank_math()->variables->setup();
6✔
333
                new \RankMath\Frontend\Frontend();
6✔
334
        }
335
}
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