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

Yoast / wordpress-seo / ac933c658864ca7120bd502004dc6816cd9ad1cc

29 May 2026 08:54PM UTC coverage: 50.322%. First build
ac933c658864ca7120bd502004dc6816cd9ad1cc

Pull #23306

github

web-flow
Merge a2074da73 into 951901dd1
Pull Request #23306: 23302 manage user consent through yoast ai

38 of 93 new or added lines in 10 files covered. (40.86%)

20893 of 41519 relevant lines covered (50.32%)

4.19 hits per line

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

0.0
/src/ai-consent/application/consent-handler.php
1
<?php
2

3
namespace Yoast\WP\SEO\AI_Consent\Application;
4

5
use Yoast\WP\SEO\AI_Authorization\Application\Token_Manager;
6
use Yoast\WP\SEO\AI_HTTP_Request\Application\Request_Handler;
7
use Yoast\WP\SEO\AI_HTTP_Request\Domain\Exceptions\Bad_Request_Exception;
8
use Yoast\WP\SEO\AI_HTTP_Request\Domain\Exceptions\Forbidden_Exception;
9
use Yoast\WP\SEO\AI_HTTP_Request\Domain\Exceptions\Internal_Server_Error_Exception;
10
use Yoast\WP\SEO\AI_HTTP_Request\Domain\Exceptions\Not_Found_Exception;
11
use Yoast\WP\SEO\AI_HTTP_Request\Domain\Exceptions\Payment_Required_Exception;
12
use Yoast\WP\SEO\AI_HTTP_Request\Domain\Exceptions\Remote_Request_Exception;
13
use Yoast\WP\SEO\AI_HTTP_Request\Domain\Exceptions\Request_Timeout_Exception;
14
use Yoast\WP\SEO\AI_HTTP_Request\Domain\Exceptions\Service_Unavailable_Exception;
15
use Yoast\WP\SEO\AI_HTTP_Request\Domain\Exceptions\Too_Many_Requests_Exception;
16
use Yoast\WP\SEO\AI_HTTP_Request\Domain\Exceptions\Unauthorized_Exception;
17
use Yoast\WP\SEO\AI_HTTP_Request\Domain\Exceptions\WP_Request_Exception;
18
use Yoast\WP\SEO\AI_HTTP_Request\Domain\Request;
19
use Yoast\WP\SEO\Helpers\User_Helper;
20
use Yoast\WP\SEO\Loggers\Logger;
21

22
/**
23
 * Class Consent_Handler
24
 * Handles the consent given or revoked by the user, both locally (user meta) and remotely (Yoast AI service).
25
 *
26
 * @makePublic
27
 */
28
class Consent_Handler implements Consent_Handler_Interface {
29

30
        /**
31
         * Holds the user helper instance.
32
         *
33
         * @var User_Helper
34
         */
35
        private $user_helper;
36

37
        /**
38
         * The token manager instance.
39
         *
40
         * @var Token_Manager
41
         */
42
        private $token_manager;
43

44
        /**
45
         * The request handler instance.
46
         *
47
         * @var Request_Handler
48
         */
49
        private $request_handler;
50

51
        /**
52
         * The logger instance.
53
         *
54
         * @var Logger
55
         */
56
        private $logger;
57

58
        /**
59
         * Class constructor.
60
         *
61
         * @param User_Helper     $user_helper     The user helper.
62
         * @param Token_Manager   $token_manager   The token manager, used to obtain a JWT for the consent endpoints.
63
         * @param Request_Handler $request_handler The request handler, used to call the AI service's consent endpoints.
64
         * @param Logger          $logger          The logger, used to record best-effort failures during revoke.
65
         */
NEW
66
        public function __construct(
×
67
                User_Helper $user_helper,
68
                Token_Manager $token_manager,
69
                Request_Handler $request_handler,
70
                Logger $logger
71
        ) {
NEW
72
                $this->user_helper     = $user_helper;
×
NEW
73
                $this->token_manager   = $token_manager;
×
NEW
74
                $this->request_handler = $request_handler;
×
NEW
75
                $this->logger          = $logger;
×
76
        }
77

78
        // phpcs:disable Squiz.Commenting.FunctionCommentThrowTag.WrongNumber -- PHPCS doesn't take into account exceptions thrown in called methods.
79

80
        /**
81
         * Records the user's consent on the Yoast AI service and, on success, in the local user meta.
82
         *
83
         * Transactional: any HTTP-layer exception is propagated and the local meta is left untouched, so
84
         * the local and server state stay in sync.
85
         *
86
         * @param int $user_id The user ID.
87
         *
88
         * @return void
89
         *
90
         * @throws Bad_Request_Exception           When the AI service responds with 400.
91
         * @throws Forbidden_Exception             When the AI service responds with 403.
92
         * @throws Internal_Server_Error_Exception When the AI service responds with 500.
93
         * @throws Not_Found_Exception             When the AI service responds with 404.
94
         * @throws Payment_Required_Exception      When the AI service responds with 402.
95
         * @throws Request_Timeout_Exception       When the AI service responds with 408.
96
         * @throws Service_Unavailable_Exception   When the AI service responds with 503.
97
         * @throws Too_Many_Requests_Exception     When the AI service responds with 429.
98
         * @throws Unauthorized_Exception          When the AI service responds with 401.
99
         * @throws WP_Request_Exception            When the underlying WordPress HTTP call fails.
100
         */
NEW
101
        public function grant_consent( int $user_id ) {
×
NEW
102
                $user = \get_user_by( 'id', $user_id );
×
NEW
103
                $jwt  = $this->token_manager->get_or_request_access_token( $user );
×
104

NEW
105
                $this->request_handler->handle(
×
NEW
106
                        new Request( '/user/consent', [], [ 'Authorization' => "Bearer $jwt" ], Request::METHOD_POST ),
×
NEW
107
                );
×
108

NEW
109
                $this->user_helper->update_meta( $user_id, '_yoast_wpseo_ai_consent', true );
×
110
        }
111

112
        /**
113
         * Revokes the user's consent on the Yoast AI service and clears the local user meta.
114
         *
115
         * Security-first: the local meta is always cleared, even if the remote DELETE fails. HTTP-layer
116
         * failures are logged as warnings and swallowed; programmer errors (non-`Remote_Request_Exception`
117
         * / non-`WP_Request_Exception`) are not caught and will propagate.
118
         *
119
         * @param int $user_id The user ID.
120
         *
121
         * @return void
122
         */
NEW
123
        public function revoke_consent( int $user_id ) {
×
124
                try {
NEW
125
                        $user = \get_user_by( 'id', $user_id );
×
NEW
126
                        $jwt  = $this->token_manager->get_or_request_access_token( $user );
×
127

NEW
128
                        $this->request_handler->handle(
×
NEW
129
                                new Request( '/user/consent', [], [ 'Authorization' => "Bearer $jwt" ], Request::METHOD_DELETE ),
×
NEW
130
                        );
×
NEW
131
                } catch ( Remote_Request_Exception | WP_Request_Exception $e ) {
×
NEW
132
                        $this->logger->warning(
×
NEW
133
                                'Failed to revoke consent on the Yoast AI service; clearing local consent anyway.',
×
NEW
134
                                [
×
NEW
135
                                        'user_id'   => $user_id,
×
NEW
136
                                        'exception' => $e->getMessage(),
×
NEW
137
                                ],
×
NEW
138
                        );
×
139
                }
140

NEW
141
                $this->user_helper->delete_meta( $user_id, '_yoast_wpseo_ai_consent' );
×
142
        }
143

144
        // phpcs:enable Squiz.Commenting.FunctionCommentThrowTag.WrongNumber
145
}
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