Passed
Push — master ( e5c4f5...cb56ad )
by Paul
03:27
created

SiteReviews   C

Complexity

Total Complexity 56

Size/Duplication

Total Lines 351
Duplicated Lines 0 %

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
wmc 56
eloc 132
dl 0
loc 351
ccs 0
cts 211
cp 0
rs 5.5199
c 0
b 0
f 0

22 Methods

Rating   Name   Duplication   Size   Complexity  
A getExcerpt() 0 16 3
A getExcerptSplit() 0 7 2
A getExcerptIntlSplit() 0 12 4
A getOption() 0 6 2
A build() 0 9 2
A buildOptionAvatar() 0 9 2
A wrap() 0 8 2
A generateAvatar() 0 13 5
A buildOptionAssignedTo() 0 10 3
A generateSchema() 0 5 2
A buildOptionTitle() 0 7 3
A isOptionEnabled() 0 3 1
A buildOptionContent() 0 5 2
A buildReviews() 0 9 2
A normalizeText() 0 10 2
A isHiddenOrEmpty() 0 3 2
A buildOptionRating() 0 5 2
A buildReview() 0 17 4
A isHidden() 0 6 3
A buildOptionAuthor() 0 4 2
A buildOptionDate() 0 14 4
A buildOptionResponse() 0 9 2

How to fix   Complexity   

Complex Class

Complex classes like SiteReviews 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.

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 SiteReviews, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace GeminiLabs\SiteReviews\Modules\Html\Partials;
4
5
use GeminiLabs\SiteReviews\Database\OptionManager;
6
use GeminiLabs\SiteReviews\Database\ReviewManager;
7
use GeminiLabs\SiteReviews\Helper;
8
use GeminiLabs\SiteReviews\Modules\Date;
9
use GeminiLabs\SiteReviews\Modules\Html\Builder;
10
use GeminiLabs\SiteReviews\Modules\Html\Partial;
11
use GeminiLabs\SiteReviews\Modules\Html\ReviewHtml;
12
use GeminiLabs\SiteReviews\Modules\Html\ReviewsHtml;
13
use GeminiLabs\SiteReviews\Modules\Html\Template;
14
use GeminiLabs\SiteReviews\Modules\Polylang;
15
use GeminiLabs\SiteReviews\Modules\Schema;
16
use GeminiLabs\SiteReviews\Review;
17
use GeminiLabs\SiteReviews\Reviews;
18
use IntlRuleBasedBreakIterator;
19
use WP_Post;
20
21
class SiteReviews
22
{
23
	/**
24
	 * @var array
25
	 */
26
	public $args;
27
28
	/**
29
	 * @var Review
30
	 */
31
	public $current;
32
33
	/**
34
	 * @var array
35
	 */
36
	public $options;
37
38
	/**
39
	 * @var Reviews
40
	 */
41
	protected $reviews;
42
43
	/**
44
	 * @param Reviews|null $reviews
45
	 * @return void|string
46
	 */
47
	public function build( array $args = [], $reviews = null )
48
	{
49
		$this->args = $args;
50
		$this->options = glsr( Helper::class )->flattenArray( glsr( OptionManager::class )->all() );
51
		$this->reviews = $reviews instanceof Reviews
52
			? $reviews
53
			: glsr( ReviewManager::class )->get( $args );
54
		$this->generateSchema();
55
		return $this->buildReviews();
56
	}
57
58
	/**
59
	 * @return ReviewHtml
60
	 */
61
	public function buildReview( Review $review )
62
	{
63
		$review = apply_filters( 'site-reviews/review/build/before', $review );
64
		$this->current = $review;
65
		$renderedFields = [];
66
		foreach( $review as $key => $value ) {
67
			$method = glsr( Helper::class )->buildMethodName( $key, 'buildOption' );
68
			$field = method_exists( $this, $method )
69
				? $this->$method( $key, $value )
70
				: apply_filters( 'site-reviews/review/build/'.$key, false, $value, $this, $review );
71
			if( $field === false )continue;
72
			$renderedFields[$key] = $field;
73
		}
74
		$this->wrap( $renderedFields, $review );
75
		$renderedFields = apply_filters( 'site-reviews/review/build/after', $renderedFields, $review );
76
		$this->current = null;
77
		return new ReviewHtml( (array)$renderedFields );
78
	}
79
80
	/**
81
	 * @return ReviewsHtml
82
	 */
83
	public function buildReviews()
84
	{
85
		$renderedReviews = [];
86
		foreach( $this->reviews as $index => $review ) {
87
			$renderedReviews[] = glsr( Template::class )->build( 'templates/review', [
88
				'context' => $this->buildReview( $review )->values,
89
			]);
90
		}
91
		return new ReviewsHtml( $renderedReviews, $this->reviews->max_num_pages, $this->args );
92
	}
93
94
	/**
95
	 * @return void
96
	 */
97
	public function generateSchema()
98
	{
99
		if( !wp_validate_boolean( $this->args['schema'] ))return;
100
		glsr( Schema::class )->store(
101
			glsr( Schema::class )->build( $this->args )
102
		);
103
	}
104
105
	/**
106
	 * @param string $key
107
	 * @param string $path
108
	 * @return bool
109
	 */
110
	public function isHidden( $key, $path = '' )
111
	{
112
		$isOptionEnabled = !empty( $path )
113
			? $this->isOptionEnabled( $path )
114
			: true;
115
		return in_array( $key, $this->args['hide'] ) || !$isOptionEnabled;
116
	}
117
118
	/**
119
	 * @param string $key
120
	 * @param string $value
121
	 * @return void|string
122
	 */
123
	protected function buildOptionAssignedTo( $key, $value )
124
	{
125
		if( $this->isHidden( $key, 'settings.reviews.assigned_links' ))return;
126
		$post = glsr( Polylang::class )->getPost( $value );
127
		if( !( $post instanceof WP_Post ))return;
128
		$permalink = glsr( Builder::class )->a( get_the_title( $post->ID ), [
129
			'href' => get_the_permalink( $post->ID ),
130
		]);
131
		$assignedTo = sprintf( __( 'Review of %s', 'site-reviews' ), $permalink );
132
		return '<span>'.$assignedTo.'</span>';
133
	}
134
135
	/**
136
	 * @param string $key
137
	 * @param string $value
138
	 * @return void|string
139
	 */
140
	protected function buildOptionAuthor( $key, $value )
141
	{
142
		if( $this->isHidden( $key ))return;
143
		return '<span>'.$value.'</span>';
144
	}
145
146
	/**
147
	 * @param string $key
148
	 * @param string $value
149
	 * @return void|string
150
	 */
151
	protected function buildOptionAvatar( $key, $value )
152
	{
153
		if( $this->isHidden( $key, 'settings.reviews.avatars' ))return;
154
		$size = $this->getOption( 'settings.reviews.avatars_size', 40 );
155
		return glsr( Builder::class )->img([
156
			'height' => $size,
157
			'src' => $this->generateAvatar( $value ),
158
			'style' => sprintf( 'width:%1$spx; height:%1$spx;', $size ),
159
			'width' => $size,
160
		]);
161
	}
162
163
	/**
164
	 * @param string $key
165
	 * @param string $value
166
	 * @return void|string
167
	 */
168
	protected function buildOptionContent( $key, $value )
169
	{
170
		$text = $this->normalizeText( $value );
171
		if( $this->isHiddenOrEmpty( $key, $text ))return;
172
		return '<p>'.$text.'</p>';
173
	}
174
175
	/**
176
	 * @param string $key
177
	 * @param string $value
178
	 * @return void|string
179
	 */
180
	protected function buildOptionDate( $key, $value )
181
	{
182
		if( $this->isHidden( $key ))return;
183
		$dateFormat = $this->getOption( 'settings.reviews.date.format', 'default' );
184
		if( $dateFormat == 'relative' ) {
185
			$date = glsr( Date::class )->relative( $value );
186
		}
187
		else {
188
			$format = $dateFormat == 'custom'
189
				? $this->getOption( 'settings.reviews.date.custom', 'M j, Y' )
190
				: (string)get_option( 'date_format' );
191
			$date = date_i18n( $format, strtotime( $value ));
192
		}
193
		return '<span>'.$date.'</span>';
194
	}
195
196
	/**
197
	 * @param string $key
198
	 * @param string $value
199
	 * @return void|string
200
	 */
201
	protected function buildOptionRating( $key, $value )
202
	{
203
		if( $this->isHiddenOrEmpty( $key, $value ))return;
204
		return glsr( Partial::class )->build( 'star-rating', [
205
			'rating' => $value,
206
		]);
207
	}
208
209
	/**
210
	 * @param string $key
211
	 * @param string $value
212
	 * @return void|string
213
	 */
214
	protected function buildOptionResponse( $key, $value )
215
	{
216
		if( $this->isHiddenOrEmpty( $key, $value ))return;
217
		$title = sprintf( __( 'Response from %s', 'site-reviews' ), get_bloginfo( 'name' ));
218
		$text = $this->normalizeText( $value );
219
		$text = '<p><strong>'.$title.'</strong></p><p>'.$text.'</p>';
220
		$response = glsr( Builder::class )->div( $text, ['class' => 'glsr-review-response-inner'] );
221
		$background = glsr( Builder::class )->div( ['class' => 'glsr-review-response-background'] );
222
		return $response.$background;
223
	}
224
225
	/**
226
	 * @param string $key
227
	 * @param string $value
228
	 * @return void|string
229
	 */
230
	protected function buildOptionTitle( $key, $value )
231
	{
232
		if( $this->isHidden( $key ))return;
233
		if( empty( $value )) {
234
			$value = __( 'No Title', 'site-reviews' );
235
		}
236
		return '<h3>'.$value.'</h3>';
237
	}
238
239
	/**
240
	 * @param string $avatarUrl
241
	 * @return string
242
	 */
243
	protected function generateAvatar( $avatarUrl )
244
	{
245
		if( !$this->isOptionEnabled( 'settings.reviews.avatars_regenerate' ) || $this->current->review_type != 'local' ) {
246
			return $avatarUrl;
247
		}
248
		$authorIdOrEmail = get_the_author_meta( 'ID', $this->current->user_id );
249
		if( empty( $authorIdOrEmail )) {
250
			$authorIdOrEmail = $this->current->email;
251
		}
252
		if( $newAvatar = get_avatar_url( $authorIdOrEmail )) {
253
			return $newAvatar;
254
		}
255
		return $avatarUrl;
256
	}
257
258
	/**
259
	 * @param string $text
260
	 * @return string
261
	 */
262
	protected function getExcerpt( $text )
263
	{
264
		$limit = intval( $this->getOption( 'settings.reviews.excerpts_length', 55 ));
265
		$split = extension_loaded( 'intl' )
266
			? $this->getExcerptIntlSplit( $text, $limit )
267
			: $this->getExcerptSplit( $text, $limit );
268
		$hiddenText = substr( $text, $split );
269
		if( !empty( $hiddenText )) {
270
			$showMore = glsr( Builder::class )->span( $hiddenText, [
271
				'class' => 'glsr-hidden glsr-hidden-text',
272
				'data-show-less' => __( 'Show less', 'site-reviews' ),
273
				'data-show-more' => __( 'Show more', 'site-reviews' ),
274
			]);
275
			$text = ltrim( substr( $text, 0, $split )).$showMore;
276
		}
277
		return nl2br( $text );
278
	}
279
280
	/**
281
	 * @param string $text
282
	 * @param int $limit
283
	 * @return int
284
	 */
285
	protected function getExcerptIntlSplit( $text, $limit )
286
	{
287
		$words = IntlRuleBasedBreakIterator::createWordInstance( '' );
288
		$words->setText( $text );
289
		$count = 0;
290
		foreach( $words as $offset ){
291
			if( $words->getRuleStatus() === IntlRuleBasedBreakIterator::WORD_NONE )continue;
292
			$count++;
293
			if( $count != $limit )continue;
294
			return $offset;
295
		}
296
		return strlen( $text );
297
	}
298
299
	/**
300
	 * @param string $text
301
	 * @param int $limit
302
	 * @return int
303
	 */
304
	protected function getExcerptSplit( $text, $limit )
305
	{
306
		if( str_word_count( $text, 0 ) > $limit ) {
307
			$words = array_keys( str_word_count( $text, 2 ));
308
			return $words[$limit];
309
		}
310
		return strlen( $text );
311
	}
312
313
	/**
314
	 * @param string $path
315
	 * @param mixed $fallback
316
	 * @return mixed
317
	 */
318
	protected function getOption( $path, $fallback = '' )
319
	{
320
		if( array_key_exists( $path, $this->options )) {
321
			return $this->options[$path];
322
		}
323
		return $fallback;
324
	}
325
326
	/**
327
	 * @param string $key
328
	 * @param string $value
329
	 * @return bool
330
	 */
331
	protected function isHiddenOrEmpty( $key, $value )
332
	{
333
		return $this->isHidden( $key ) || empty( $value );
334
	}
335
336
	/**
337
	 * @param string $path
338
	 * @return bool
339
	 */
340
	protected function isOptionEnabled( $path )
341
	{
342
		return $this->getOption( $path ) == 'yes';
343
	}
344
345
	/**
346
	 * @param string $text
347
	 * @return string
348
	 */
349
	protected function normalizeText( $text )
350
	{
351
		$text = wp_kses( $text, wp_kses_allowed_html() );
352
		$text = convert_smilies( strip_shortcodes( $text ));
353
		$text = str_replace( ']]>', ']]&gt;', $text );
354
		$text = preg_replace( '/(\R){2,}/', '$1', $text );
355
		if( $this->isOptionEnabled( 'settings.reviews.excerpts' )) {
356
			$text = $this->getExcerpt( $text );
357
		}
358
		return wptexturize( $text );
359
	}
360
361
	/**
362
	 * @return void
363
	 */
364
	protected function wrap( array &$renderedFields, Review $review )
365
	{
366
		$renderedFields = apply_filters( 'site-reviews/review/wrap', $renderedFields, $review );
367
		array_walk( $renderedFields, function( &$value, $key ) use( $review ) {
368
			$value = apply_filters( 'site-reviews/review/wrap/'.$key, $value, $review );
369
			if( empty( $value ))return;
370
			$value = glsr( Builder::class )->div( $value, [
371
				'class' => 'glsr-review-'.$key,
372
			]);
373
		});
374
	}
375
}
376