Completed
Push — update/dialogue-add-fallback-_... ( 94cf95...94a3a5 )
by
unknown
09:44
created

Jetpack_ReCaptcha::verify()   C

Complexity

Conditions 12
Paths 18

Size

Total Lines 60

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 12
nc 18
nop 2
dl 0
loc 60
rs 6.446
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * Class that handles reCAPTCHA.
5
 */
6
class Jetpack_ReCaptcha {
7
8
	/**
9
	 * URL to which requests are POSTed.
10
	 *
11
	 * @const string
12
	 */
13
	const VERIFY_URL = 'https://www.google.com/recaptcha/api/siteverify';
14
15
	/**
16
	 * Site key to use in HTML code.
17
	 *
18
	 * @var string
19
	 */
20
	private $site_key;
21
22
	/**
23
	 * Shared secret for the site.
24
	 *
25
	 * @var string
26
	 */
27
	private $secret_key;
28
29
	/**
30
	 * Config for reCAPTCHA instance.
31
	 *
32
	 * @var array
33
	 */
34
	private $config;
35
36
	/**
37
	 * Error codes returned from reCAPTCHA API.
38
	 *
39
	 * @see https://developers.google.com/recaptcha/docs/verify
40
	 *
41
	 * @var array
42
	 */
43
	private $error_codes;
44
45
	/**
46
	 * Create a configured instance to use the reCAPTCHA service.
47
	 *
48
	 * @param string $site_key   Site key to use in HTML code.
49
	 * @param string $secret_key Shared secret between site and reCAPTCHA server.
50
	 * @param array  $config     Config array to optionally configure reCAPTCHA instance.
51
	 */
52
	public function __construct( $site_key, $secret_key, $config = array() ) {
53
		$this->site_key   = $site_key;
54
		$this->secret_key = $secret_key;
55
		$this->config     = wp_parse_args( $config, $this->get_default_config() );
0 ignored issues
show
Documentation introduced by
$this->get_default_config() is of type array, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
Documentation Bug introduced by
It seems like wp_parse_args($config, $...->get_default_config()) can be null. However, the property $config is declared as array. Maybe change the type of the property to array|null or add a type check?

Our type inference engine has found an assignment of a scalar value (like a string, an integer or null) to a property which is an array.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property.

To type hint that a parameter can be either an array or null, you can set a type hint of array and a default value of null. The PHP interpreter will then accept both an array or null for that parameter.

function aContainsB(array $needle = null, array  $haystack) {
    if (!$needle) {
        return false;
    }

    return array_intersect($haystack, $needle) == $haystack;
}

The function can be called with either null or an array for the parameter $needle but will only accept an array as $haystack.

Loading history...
56
57
		$this->error_codes = array(
58
			'missing-input-secret'   => __( 'The secret parameter is missing', 'jetpack' ),
59
			'invalid-input-secret'   => __( 'The secret parameter is invalid or malformed', 'jetpack' ),
60
			'missing-input-response' => __( 'The response parameter is missing', 'jetpack' ),
61
			'invalid-input-response' => __( 'The response parameter is invalid or malformed', 'jetpack' ),
62
			'invalid-json'           => __( 'Invalid JSON', 'jetpack' ),
63
			'unexpected-response'    => __( 'Unexpected response', 'jetpack' ),
64
			'unexpected-hostname'    => __( 'Unexpected hostname', 'jetpack' ),
65
		);
66
	}
67
68
	/**
69
	 * Get default config for this reCAPTCHA instance.
70
	 *
71
	 * @return array Default config
72
	 */
73
	public function get_default_config() {
74
		return array(
75
			'language'       => get_locale(),
76
			'script_async'   => false,
77
			'script_defer'   => true,
78
			'script_lazy'    => false,
79
			'tag_class'      => 'g-recaptcha',
80
			'tag_attributes' => array(
81
				'theme'    => 'light',
82
				'type'     => 'image',
83
				'tabindex' => 0,
84
			),
85
		);
86
	}
87
88
	/**
89
	 * Calls the reCAPTCHA siteverify API to verify whether the user passes
90
	 * CAPTCHA test.
91
	 *
92
	 * @param string $response  The value of 'g-recaptcha-response' in the submitted
93
	 *                          form.
94
	 * @param string $remote_ip The end user's IP address.
95
	 *
96
	 * @return bool|WP_Error Returns true if verified. Otherwise WP_Error is returned.
97
	 */
98
	public function verify( $response, $remote_ip ) {
99
		// No need make a request if response is empty.
100
		if ( empty( $response ) ) {
101
			return new WP_Error( 'missing-input-response', $this->error_codes['missing-input-response'], 400 );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'missing-input-response'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
102
		}
103
104
		$resp = wp_remote_post( self::VERIFY_URL, $this->get_verify_request_params( $response, $remote_ip ) );
105
		if ( is_wp_error( $resp ) ) {
106
			return $resp;
107
		}
108
109
		$resp_decoded = json_decode( wp_remote_retrieve_body( $resp ), true );
110
		if ( ! $resp_decoded ) {
111
			return new WP_Error( 'invalid-json', $this->error_codes['invalid-json'], 400 );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'invalid-json'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
112
		}
113
114
		// Default error code and message.
115
		$error_code    = 'unexpected-response';
116
		$error_message = $this->error_codes['unexpected-response'];
117
118
		// Use the first error code if exists.
119
		if ( isset( $resp_decoded['error-codes'] ) && is_array( $resp_decoded['error-codes'] ) ) {
120
			if ( isset( $resp_decoded['error-codes'][0] ) && isset( $this->error_codes[ $resp_decoded['error-codes'][0] ] ) ) {
121
				$error_message = $this->error_codes[ $resp_decoded['error-codes'][0] ];
122
				$error_code    = $resp_decoded['error-codes'][0];
123
			}
124
		}
125
126
		if ( ! isset( $resp_decoded['success'] ) ) {
127
			return new WP_Error( $error_code, $error_message );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with $error_code.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
128
		}
129
130
		if ( true !== $resp_decoded['success'] ) {
131
			return new WP_Error( $error_code, $error_message );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with $error_code.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
132
		}
133
		// Validate the hostname matches expected source
134
		if ( isset( $resp_decoded['hostname'] ) ) {
135
			$url = wp_parse_url( get_home_url() );
136
137
			/**
138
			 * Allow other valid hostnames.
139
			 *
140
			 * This can be useful in cases where the token hostname is expected to be
141
			 * different from the get_home_url (ex. AMP recaptcha token contains a different hostname)
142
			 *
143
			 * @module sharedaddy
144
			 *
145
			 * @since 9.1.0
146
			 *
147
			 * @param array [ $url['host'] ] List of the valid hostnames to check against.
148
			 */
149
			$valid_hostnames = apply_filters( 'jetpack_recaptcha_valid_hostnames', array( $url['host'] ) );
150
151
			if ( ! in_array( $resp_decoded['hostname'], $valid_hostnames, true ) ) {
152
				return new WP_Error( 'unexpected-host', $this->error_codes['unexpected-hostname'] );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'unexpected-host'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
153
			}
154
		}
155
156
		return true;
157
	}
158
159
	/**
160
	 * Get siteverify request parameters.
161
	 *
162
	 * @param string $response  The value of 'g-recaptcha-response' in the submitted
163
	 *                          form.
164
	 * @param string $remote_ip The end user's IP address.
165
	 *
166
	 * @return array
167
	 */
168
	public function get_verify_request_params( $response, $remote_ip ) {
169
		return array(
170
			'body' => array(
171
				'secret'   => $this->secret_key,
172
				'response' => $response,
173
				'remoteip' => $remote_ip,
174
			),
175
			'sslverify' => true,
176
		);
177
	}
178
179
	/**
180
	 * Get reCAPTCHA HTML to render.
181
	 *
182
	 * @return string
183
	 */
184
	public function get_recaptcha_html() {
185
		$url = sprintf(
186
			'https://www.google.com/recaptcha/api.js?hl=%s',
187
			rawurlencode( $this->config['language'] )
188
		);
189
190
		$html = sprintf(
191
			'
192
			<div
193
				class="%s"
194
				data-sitekey="%s"
195
				data-theme="%s"
196
				data-type="%s"
197
				data-tabindex="%s"
198
				data-lazy="%s"
199
				data-url="%s"></div>
200
			',
201
			esc_attr( $this->config['tag_class'] ),
202
			esc_attr( $this->site_key ),
203
			esc_attr( $this->config['tag_attributes']['theme'] ),
204
			esc_attr( $this->config['tag_attributes']['type'] ),
205
			esc_attr( $this->config['tag_attributes']['tabindex'] ),
206
			$this->config['script_lazy'] ? 'true' : 'false',
207
			esc_attr( $url )
208
		);
209
210
		if ( ! $this->config['script_lazy'] ) {
211
			$html = $html . sprintf(
212
				// phpcs:ignore WordPress.WP.EnqueuedResources.NonEnqueuedScript
213
				'<script src="%s"%s%s></script>
214
				',
215
				$url,
216
				$this->config['script_async'] && ! $this->config['script_defer'] ? ' async' : '',
217
				$this->config['script_defer'] ? ' defer' : ''
218
			);
219
		}
220
221
		return $html;
222
	}
223
}
224