Completed
Push — renovate/gridicons-3.x ( c004c1...f8ccd4 )
by
unknown
284:06 queued 275:32
created

Highlander_Comments_Base   B

Complexity

Total Complexity 50

Size/Duplication

Total Lines 303
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 1

Importance

Changes 0
Metric Value
dl 0
loc 303
rs 8.4
c 0
b 0
f 0
wmc 50
lcom 1
cbo 1

13 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
A setup_globals() 0 1 1
A setup_actions() 0 7 1
A setup_filters() 0 4 1
B is_highlander_comment_post() 0 16 7
A sign_remote_comment_parameters() 0 19 3
A comments_array() 0 32 4
A sort_comments_by_comment_date_gmt() 0 7 3
A get_current_commenter() 0 26 5
A allow_logged_out_user_to_comment_as_external() 0 7 2
C allow_logged_in_user_to_comment_as_guest() 0 55 15
B set_comment_cookies() 0 22 6
A photon_avatar() 0 5 1

How to fix   Complexity   

Complex Class

Complex classes like Highlander_Comments_Base often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Highlander_Comments_Base, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/**
4
 * All the code shared between WP.com Highlander and Jetpack Highlander
5
 */
6
class Highlander_Comments_Base {
7
	function __construct() {
8
		$this->setup_globals();
9
		$this->setup_actions();
10
		$this->setup_filters();
11
	}
12
13
	/**
14
	 * Set any global variables or class variables
15
	 * @since JetpackComments (1.4)
16
	 */
17
	protected function setup_globals() {}
18
19
	/**
20
	 * Setup actions for methods in this class
21
	 * @since JetpackComments (1.4)
22
	 */
23
	protected function setup_actions() {
24
		// Before a comment is posted
25
		add_action( 'pre_comment_on_post', array( $this, 'allow_logged_out_user_to_comment_as_external' ) );
26
27
		// After a comment is posted
28
		add_action( 'comment_post', array( $this, 'set_comment_cookies' ) );
29
	}
30
31
	/**
32
	 * Setup filters for methods in this class
33
	 * @since JetpackComments (1.4)
34
	 */
35
	protected function setup_filters() {
36
		add_filter( 'comments_array', array( $this, 'comments_array' ) );
37
		add_filter( 'preprocess_comment', array( $this, 'allow_logged_in_user_to_comment_as_guest' ), 0 );
38
	}
39
40
	/**
41
	 * Is this a Highlander POST request?
42
	 * Optionally restrict to one or more credentials slug (facebook, twitter, ...)
43
	 *
44
	 * @param string Comment credentials slug
45
	 * @param ...
46
	 * @return false|string false if it's not a Highlander POST request.  The matching credentials slug if it is.
47
	 */
48
	function is_highlander_comment_post() {
49
		if ( empty( $_POST['hc_post_as'] ) ) {
50
			return false;
51
		}
52
53
		if ( func_num_args() ) {
54
			foreach ( func_get_args() as $id_source ) {
55
				if ( $id_source === $_POST['hc_post_as'] ) {
56
					return $id_source;
57
				}
58
			}
59
			return false;
60
		}
61
62
		return is_string( $_POST['hc_post_as'] ) && in_array( $_POST['hc_post_as'], $this->id_sources ) ? $_POST['hc_post_as'] : false;
0 ignored issues
show
Bug introduced by
The property id_sources does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
63
	}
64
65
	/**
66
	 * Signs an array of scalars with the self-hosted blog's Jetpack Token
67
	 *
68
	 * @param array $parameters
69
	 * @param string $key
70
	 * @return string HMAC
71
	 */
72
	static function sign_remote_comment_parameters( $parameters, $key ) {
73
		unset(
74
			$parameters['sig'],       // Don't sign the signature
75
			$parameters['replytocom'] // This parameter is unsigned - it changes dynamically as the comment form moves from parent comment to parent comment
76
		);
77
78
		ksort( $parameters );
79
80
		$signing = array();
81
		foreach ( $parameters as $k => $v ) {
82
			if ( ! is_scalar( $v ) ) {
83
				return new WP_Error( 'invalid_input', __( 'Invalid request', 'jetpack' ), array( 'status' => 400 ) );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'invalid_input'.

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...
84
			}
85
86
			$signing[] = "{$k}={$v}";
87
		}
88
89
		return hash_hmac( 'sha1', implode( ':', $signing ), $key );
90
	}
91
92
	/*
93
	  * After commenting as a guest while logged in, the user needs to see both:
94
	 *
95
	 * ( user_id = blah AND comment_approved = 0 )
96
	 * and
97
	 * ( comment_author_email = blah AND comment_approved = 0 )
98
	 *
99
	  * Core only does the first since the user is logged in.
100
	  *
101
	  * Add the second to the comments array.
102
	  */
103
	function comments_array( $comments ) {
104
		global $wpdb, $post;
105
106
		$commenter = $this->get_current_commenter();
107
108
		if ( ! $commenter['user_id'] ) {
109
			return $comments;
110
		}
111
112
		if ( ! $commenter['comment_author'] ) {
113
			return $comments;
114
		}
115
116
		$in_moderation_comments = $wpdb->get_results(
117
			$wpdb->prepare(
118
				"SELECT * FROM `$wpdb->comments` WHERE `comment_post_ID` = %d AND `user_id` = 0 AND `comment_author` = %s AND `comment_author_email` = %s AND `comment_approved` = '0' ORDER BY `comment_date_gmt` /* Highlander_Comments_Base::comments_array() */",
119
				$post->ID,
120
				wp_specialchars_decode( $commenter['comment_author'], ENT_QUOTES ),
121
				$commenter['comment_author_email']
122
			)
123
		);
124
125
		if ( ! $in_moderation_comments ) {
126
			return $comments;
127
		}
128
129
		// @todo ZOMG this is a bad idea
130
		$comments = array_merge( $comments, $in_moderation_comments );
131
		usort( $comments, array( $this, 'sort_comments_by_comment_date_gmt' ) );
132
133
		return $comments;
134
	}
135
136
	/**
137
	 * Comment sort comparator: comment_date_gmt
138
	 *
139
	 * @since JetpackComments (1.4)
140
	 * @param object $a
141
	 * @param object $b
142
	 * @return int
143
	 */
144
	public function sort_comments_by_comment_date_gmt( $a, $b ) {
145
		if ( $a->comment_date_gmt == $b->comment_date_gmt ) {
146
			return 0;
147
		}
148
149
		return $a->comment_date_gmt < $b->comment_date_gmt ? -1 : 1;
150
	}
151
152
	/**
153
	 * Get the current commenter's information from their cookie
154
	 *
155
	 * @since JetpackComments (1.4)
156
	 * @return array Commenters information from cookie
157
	 */
158
	protected function get_current_commenter() {
159
		// Defaults
160
		$user_id              = 0;
161
		$comment_author       = '';
162
		$comment_author_email = '';
163
		$comment_author_url   = '';
164
165
		if ( isset( $_COOKIE[ 'comment_author_' . COOKIEHASH ] ) ) {
166
			$comment_author = $_COOKIE[ 'comment_author_' . COOKIEHASH ];
167
		}
168
169
		if ( isset( $_COOKIE[ 'comment_author_email_' . COOKIEHASH ] ) ) {
170
			$comment_author_email = $_COOKIE[ 'comment_author_email_' . COOKIEHASH ];
171
		}
172
173
		if ( isset( $_COOKIE[ 'comment_author_url_' . COOKIEHASH ] ) ) {
174
			$comment_author_url = $_COOKIE[ 'comment_author_url_' . COOKIEHASH ];
175
		}
176
177
		if ( is_user_logged_in() ) {
178
			$user    = wp_get_current_user();
179
			$user_id = $user->ID;
180
		}
181
182
		return compact( 'comment_author', 'comment_author_email', 'comment_author_url', 'user_id' );
183
	}
184
185
	/**
186
	 * Allows a logged out user to leave a comment as a facebook or twitter credentialed user.
187
	 * Overrides WordPress' core comment_registration option to treat these commenters as "registered" (verified) users.
188
	 *
189
	 * @since JetpackComments (1.4)
190
	 * @return If no
191
	 */
192
	function allow_logged_out_user_to_comment_as_external() {
193
		if ( ! $this->is_highlander_comment_post( 'facebook', 'twitter', 'googleplus' ) ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->is_highlander_com...twitter', 'googleplus') of type false|string is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
194
			return;
195
		}
196
197
		add_filter( 'pre_option_comment_registration', '__return_zero' );
198
	}
199
200
	/**
201
	 * Allow a logged in user to post as a guest, FB, or twitter credentialed request.
202
	 * Bypasses WordPress' core overrides that force a logged in user to comment as that user.
203
	 * Respects comment_registration option.
204
	 *
205
	 * @since JetpackComments (1.4)
206
	 * @param array $comment_data
207
	 * @return int
208
	 */
209
	function allow_logged_in_user_to_comment_as_guest( $comment_data ) {
210
		// Bail if user registration is allowed
211
		if ( get_option( 'comment_registration' ) ) {
212
			return $comment_data;
213
		}
214
215
		// Bail if user is not logged in or not a post request
216
		if ( 'POST' != strtoupper( $_SERVER['REQUEST_METHOD'] ) || ! is_user_logged_in() ) {
217
			return $comment_data;
218
		}
219
220
		// Bail if this is not a guest or external service credentialed request
221
		if ( ! $this->is_highlander_comment_post( 'guest', 'facebook', 'twitter', 'googleplus' ) ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->is_highlander_com...twitter', 'googleplus') of type false|string is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
222
			return $comment_data;
223
		}
224
225
		$user = wp_get_current_user();
226
227
		foreach ( array(
228
			'comment_author'       => 'display_name',
229
			'comment_author_email' => 'user_email',
230
			'comment_author_url'   => 'user_url',
231
		) as $comment_field => $user_field ) {
232
			if ( $comment_data[ $comment_field ] != addslashes( $user->$user_field ) ) {
233
				return $comment_data; // some other plugin already did something funky
234
			}
235
		}
236
237
		if ( get_option( 'require_name_email' ) ) {
238
			if ( 6 > strlen( $_POST['email'] ) || empty( $_POST['author'] ) ) {
239
				wp_die( __( 'Error: please fill the required fields (name, email).', 'jetpack' ), 400 );
240
			} elseif ( ! is_email( $_POST['email'] ) ) {
241
				wp_die( __( 'Error: please enter a valid email address.', 'jetpack' ), 400 );
242
			}
243
		}
244
245
		$author_change = false;
246
		foreach ( array(
247
			'comment_author'       => 'author',
248
			'comment_author_email' => 'email',
249
			'comment_author_url'   => 'url',
250
		) as $comment_field => $post_field ) {
251
			if ( $comment_data[ $comment_field ] != $_POST[ $post_field ] && 'url' != $post_field ) {
252
				$author_change = true;
253
			}
254
			$comment_data[ $comment_field ] = $_POST[ $post_field ];
255
		}
256
257
		// Mark as guest comment if name or email were changed
258
		if ( $author_change ) {
259
			$comment_data['user_id'] = $comment_data['user_ID'] = 0;
260
		}
261
262
		return $comment_data;
263
	}
264
265
	/**
266
	 * Set the comment cookies or bail if comment is invalid
267
	 *
268
	 * @since JetpackComments (1.4)
269
	 * @param type $comment_id
270
	 * @return If comment is invalid
271
	 */
272
	public function set_comment_cookies( $comment_id ) {
273
		// Get comment and bail if it's invalid somehow
274
		$comment = get_comment( $comment_id );
275
		if ( empty( $comment ) || is_wp_error( $comment ) ) {
276
			return;
277
		}
278
279
		$id_source = $this->is_highlander_comment_post();
280
		if ( empty( $id_source ) ) {
281
			return;
282
		}
283
284
		// Set comment author cookies
285
		// phpcs:ignore WordPress.WP.CapitalPDangit
286
		if ( ( 'wordpress' != $id_source ) && is_user_logged_in() ) {
287
			/** This filter is already documented in core/wp-includes/comment-functions.php */
288
			$comment_cookie_lifetime = apply_filters( 'comment_cookie_lifetime', 30000000 );
289
			setcookie( 'comment_author_' . COOKIEHASH, $comment->comment_author, time() + $comment_cookie_lifetime, COOKIEPATH, COOKIE_DOMAIN );
290
			setcookie( 'comment_author_email_' . COOKIEHASH, $comment->comment_author_email, time() + $comment_cookie_lifetime, COOKIEPATH, COOKIE_DOMAIN );
291
			setcookie( 'comment_author_url_' . COOKIEHASH, esc_url( $comment->comment_author_url ), time() + $comment_cookie_lifetime, COOKIEPATH, COOKIE_DOMAIN );
292
		}
293
	}
294
295
	/**
296
	 * Get an avatar from Photon
297
	 *
298
	 * @since JetpackComments (1.4)
299
	 * @param string $url
300
	 * @param int $size
301
	 * @return string
302
	 */
303
	protected function photon_avatar( $url, $size ) {
304
		$size = (int) $size;
305
306
		return jetpack_photon_url( $url, array( 'resize' => "$size,$size" ) );
307
	}
308
}
309