Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
36 / 36
100.00% covered (success)
100.00%
4 / 4
CRAP
100.00% covered (success)
100.00%
1 / 1
User_Account_Status
100.00% covered (success)
100.00%
36 / 36
100.00% covered (success)
100.00%
4 / 4
13
100.00% covered (success)
100.00%
1 / 1
 check_account_errors
100.00% covered (success)
100.00%
12 / 12
100.00% covered (success)
100.00%
1 / 1
2
 possible_account_mismatch
100.00% covered (success)
100.00%
13 / 13
100.00% covered (success)
100.00%
1 / 1
4
 clean_account_mismatch_transients
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
6
 normalize_email
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2/**
3 * Class to check for account errors in the Jetpack Connection.
4 *
5 * @package automattic/jetpack-connection
6 * @since 6.11.0
7 */
8
9namespace Automattic\Jetpack\Connection;
10
11/**
12 * Class User_Account_Status
13 */
14class User_Account_Status {
15    /**
16     * Check for possible account errors between the local user and WPCOM account.
17     *
18     * @since 6.11.0
19     *
20     * @param string $current_user_email The email of the current WordPress user.
21     * @param string $wpcom_user_email The email of the connected WordPress.com account.
22     *
23     * @return array An array of possible account errors, empty if no errors.
24     */
25    public function check_account_errors( $current_user_email, $wpcom_user_email ) {
26        $errors = array();
27
28        // Check for email mismatch error.
29        $has_mismatch = $this->possible_account_mismatch( $current_user_email, $wpcom_user_email );
30        if ( $has_mismatch ) {
31            $errors['mismatch'] = array(
32                'type'    => 'mismatch',
33                'message' => __( 'Your WordPress.com email is also used by another user account. This won’t affect functionality but may cause confusion about which user account is connected.', 'jetpack-connection' ),
34                'details' => array(
35                    'site_email'  => $current_user_email,
36                    'wpcom_email' => $wpcom_user_email,
37                ),
38            );
39        }
40
41        /**
42         * Filters the account errors.
43         *
44         * @since 6.11.0
45         *
46         * @param array  $errors             The array of account errors.
47         * @param string $current_user_email The email of the current WordPress user.
48         * @param string $wpcom_user_email   The email of the connected WordPress.com account.
49         */
50        return apply_filters( 'jetpack_connection_account_errors', $errors, $current_user_email, $wpcom_user_email );
51    }
52
53    /**
54     * Check if there is a possible account mismatch between the local user and WPCOM account.
55     *
56     * @since 6.11.0
57     *
58     * @param string $current_user_email The email of the current WordPress user.
59     * @param string $wpcom_user_email The email of the connected WordPress.com account.
60     *
61     * @return bool Whether there is a possible account mismatch.
62     */
63    public function possible_account_mismatch( $current_user_email, $wpcom_user_email ) {
64        if ( ! $wpcom_user_email ) {
65            return false;
66        }
67
68        $normalized_wpcom_email = self::normalize_email( $wpcom_user_email );
69
70        // WordPress.com preserves the casing an address was registered with, while WordPress
71        // stores addresses lowercased, so the two can represent the same address. Compare them
72        // case-insensitively, the same way the get_user_by() lookup below matches.
73        if ( self::normalize_email( $current_user_email ) === $normalized_wpcom_email ) {
74            return false;
75        }
76
77        // The result depends only on the WordPress.com address, so it is cached per address
78        // rather than per user.
79        $transient_key = 'jetpack_account_mismatch_' . md5( $normalized_wpcom_email );
80
81        $cached_result = get_transient( $transient_key );
82
83        if ( false !== $cached_result ) {
84            return (bool) $cached_result;
85        }
86
87        // Check if there's a WordPress user with the WPCOM email .
88        $wpcom_email_user = get_user_by( 'email', $wpcom_user_email );
89        $mismatch_exists  = false !== $wpcom_email_user;
90
91        // Store the result in a transient for 24 hours.
92        set_transient( $transient_key, $mismatch_exists, DAY_IN_SECONDS );
93
94        return $mismatch_exists;
95    }
96
97    /**
98     * Clears account mismatch transients for a user when they update their email or are deleted.
99     *
100     * @since 6.11.0
101     *
102     * @param string|int $user_id_or_email User ID or email address.
103     * @return void
104     */
105    public function clean_account_mismatch_transients( $user_id_or_email ) {
106        $email = null;
107
108        if ( is_numeric( $user_id_or_email ) ) {
109            $user = get_userdata( $user_id_or_email );
110            if ( $user && isset( $user->user_email ) ) {
111                $email = $user->user_email;
112            }
113        } else {
114            $email = $user_id_or_email;
115        }
116
117        if ( ! $email || ! is_email( $email ) ) {
118            return;
119        }
120
121        $transient_key = 'jetpack_account_mismatch_' . md5( self::normalize_email( $email ) );
122        delete_transient( $transient_key );
123    }
124
125    /**
126     * Normalize an email address so that addresses differing only in case are treated as equal.
127     *
128     * @since 8.10.3
129     *
130     * @param string $email The email address to normalize.
131     *
132     * @return string The normalized email address.
133     */
134    private static function normalize_email( $email ) {
135        return strtolower( trim( (string) $email ) );
136    }
137}