Completed
Push — update/http-to-https ( 98b89c )
by
unknown
09:46 queued 02:50
created

VideoPress_Video   D

Complexity

Total Complexity 58

Size/Duplication

Total Lines 369
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 1

Importance

Changes 0
Metric Value
dl 0
loc 369
rs 4.5599
c 0
b 0
f 0
wmc 58
lcom 1
cbo 1

4 Methods

Rating   Name   Duplication   Size   Complexity  
F __construct() 0 101 33
C calculate_expiration() 0 21 13
A hostname() 0 3 1
B get_data() 0 48 11

How to fix   Complexity   

Complex Class

Complex classes like VideoPress_Video 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 VideoPress_Video, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * VideoPress video object retrieved from VideoPress servers and parsed.
4
 *
5
 * @since 1.3
6
 */
7
class VideoPress_Video {
8
	public $version = 3;
9
10
	/**
11
	 * Manifest version returned by remote service.
12
	 *
13
	 * @var string
14
	 * @since 1.3
15
	 */
16
	const manifest_version = '1.5';
17
18
	/**
19
	 * Expiration of the video expressed in Unix time
20
	 *
21
	 * @var int
22
	 * @since 1.3
23
	 */
24
	public $expires;
25
26
	/**
27
	 * VideoPress unique identifier
28
	 *
29
	 * @var string
30
	 * @since 1.3
31
	 */
32
	public $guid;
33
34
	/**
35
	 * WordPress.com blog identifier
36
	 *
37
	 * @var int
38
	 * @since 1.5
39
	 */
40
	public $blog_id;
41
42
	/**
43
	 * Remote blog attachment identifier
44
	 *
45
	 * @var int
46
	 * @since 1.5
47
	 */
48
	public $post_id;
49
50
	/**
51
	 * Maximum desired width.
52
	 *
53
	 * @var int
54
	 * @since 1.3
55
	 */
56
	public $maxwidth;
57
58
	/**
59
	 * Video width calculated based on original video dimensions and the requested maxwidth
60
	 *
61
	 * @var int
62
	 * @since 1.3
63
	 */
64
	public $calculated_width;
65
66
	/**
67
	 * Video height calculated based on original video dimensions and the requested maxwidth
68
	 *
69
	 * @var int
70
	 * @since 1.3
71
	 */
72
	public $calculated_height;
73
74
	/**
75
	 * Video title
76
	 *
77
	 * @var string
78
	 * @since 1.3
79
	 */
80
	public $title;
81
82
	/**
83
	 * Video description
84
	 *
85
	 * @var string
86
	 * @since 4.4
87
	 */
88
	public $description;
89
90
	/**
91
	 * Directionality of title text. ltr or rtl
92
	 *
93
	 * @var string
94
	 * @since 1.3
95
	 */
96
	public $text_direction;
97
98
	/**
99
	 * Text and audio language as ISO 639-2 language code
100
	 *
101
	 * @var string
102
	 * @since 1.3
103
	 */
104
	public $language;
105
106
	/**
107
	 * Video duration in whole seconds
108
	 *
109
	 * @var int
110
	 * @since 1.3
111
	 */
112
	public $duration;
113
114
	/**
115
	 * Recommended minimum age of the viewer.
116
	 *
117
	 * @var int
118
	 * @since 1.3
119
	 */
120
	public $age_rating;
121
122
	/**
123
	 * Video author has restricted video embedding or sharing
124
	 *
125
	 * @var bool
126
	 * @since 1.3
127
	 */
128
	public $restricted_embed;
129
130
	/**
131
	 * Poster frame image URI for the given video guid and calculated dimensions.
132
	 *
133
	 * @var string
134
	 * @since 1.3
135
	 */
136
	public $poster_frame_uri;
137
138
	/**
139
	 * Video files associated with the given guid for the calculated dimensions.
140
	 *
141
	 * @var stdClass
142
	 * @since 1.3
143
	 */
144
	public $videos;
145
146
	/**
147
	 * Video player information
148
	 *
149
	 * @var stdClass
150
	 * @since 1.3
151
	 */
152
	public $players;
153
154
	/**
155
	 * Video player skinning preferences including background color and watermark
156
	 *
157
	 * @var array
158
	 * @since 1.5
159
	 */
160
	public $skin;
161
162
	/**
163
	 * Closed captions if available for the given video. Associative array of ISO 639-2 language code and a WebVTT URI
164
	 *
165
	 * @var array
166
	 * @since 1.5
167
	 */
168
	public $captions;
169
170
	/**
171
	 * Setup the object.
172
	 * Request video information from VideoPress servers and process the response.
173
	 *
174
	 * @since 1.3
175
	 * @var string $guid VideoPress unique identifier
176
	 * @var int $maxwidth maximum requested video width. final width and height are calculated on VideoPress servers based on the aspect ratio of the original video upload.
177
	 */
178
	public function __construct( $guid, $maxwidth = 640 ) {
179
		$this->guid = $guid;
180
181
		$maxwidth = absint( $maxwidth );
182
		if ( $maxwidth > 0 ) {
183
			$this->maxwidth = $maxwidth;
184
		}
185
186
		$data = $this->get_data();
187
		if ( is_wp_error( $data ) || empty( $data ) ) {
188
			/** This filter is documented in modules/videopress/class.videopress-player.php */
189
			if ( ! apply_filters( 'jetpack_videopress_use_legacy_player', false ) ) {
190
				// Unlike the Flash player, the new player does it's own error checking, age gate, etc.
191
				$data = (object) array(
192
					'guid'   => $guid,
193
					'width'  => $maxwidth,
194
					'height' => $maxwidth / 16 * 9,
195
				);
196
			} else {
197
				$this->error = $data;
0 ignored issues
show
Bug introduced by
The property error 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...
198
				return;
199
			}
200
		}
201
202
		if ( isset( $data->blog_id ) ) {
203
			$this->blog_id = absint( $data->blog_id );
204
		}
205
206
		if ( isset( $data->post_id ) ) {
207
			$this->post_id = absint( $data->post_id );
208
		}
209
210
		if ( isset( $data->title ) && $data->title !== '' ) {
211
			$this->title = trim( str_replace( '&nbsp;', ' ', $data->title ) );
212
		}
213
214
		if ( isset( $data->description ) && $data->description !== '' ) {
215
			$this->description = trim( $data->description );
216
		}
217
218
		if ( isset( $data->text_direction ) && $data->text_direction === 'rtl' ) {
219
			$this->text_direction = 'rtl';
220
		} else {
221
			$this->text_direction = 'ltr';
222
		}
223
224
		if ( isset( $data->language ) ) {
225
			$this->language = $data->language;
226
		}
227
228
		if ( isset( $data->duration ) && $data->duration > 0 ) {
229
			$this->duration = absint( $data->duration );
230
		}
231
232
		if ( isset( $data->width ) && $data->width > 0 ) {
233
			$this->calculated_width = absint( $data->width );
234
		}
235
236
		if ( isset( $data->height ) && $data->height > 0 ) {
237
			$this->calculated_height = absint( $data->height );
238
		}
239
240
		if ( isset( $data->age_rating ) ) {
241
			$this->age_rating = absint( $this->age_rating );
242
		}
243
244
		if ( isset( $data->restricted_embed ) && $data->restricted_embed === true ) {
245
			$this->restricted_embed = true;
246
		} else {
247
			$this->restricted_embed = false;
248
		}
249
250
		if ( isset( $data->posterframe ) && $data->posterframe !== '' ) {
251
			$this->poster_frame_uri = esc_url_raw( $data->posterframe, array( 'http', 'https' ) );
252
		}
253
254
		if ( isset( $data->mp4 ) || isset( $data->ogv ) ) {
255
			$this->videos = new stdClass();
256
			if ( isset( $data->mp4 ) ) {
257
				$this->videos->mp4 = $data->mp4;
258
			}
259
			if ( isset( $data->ogv ) ) {
260
				$this->videos->ogv = $data->ogv;
261
			}
262
		}
263
264
		if ( isset( $data->swf ) ) {
265
			if ( ! isset( $this->players ) ) {
266
				$this->players = new stdClass();
267
			}
268
			$this->players->swf = $data->swf;
269
		}
270
271
		if ( isset( $data->skin ) ) {
272
			$this->skin = $data->skin;
273
		}
274
275
		if ( isset( $data->captions ) ) {
276
			$this->captions = (array) $data->captions;
277
		}
278
	}
279
280
	/**
281
	 * Convert an Expires HTTP header value into Unix time for use in WP Cache
282
	 *
283
	 * @since 1.3
284
	 * @var string $expires_header
285
	 * @return int|bool Unix time or false
286
	 */
287
	public static function calculate_expiration( $expires_header ) {
288
		if ( empty( $expires_header ) || ! is_string( $expires_header ) ) {
289
			return false;
290
		}
291
292
		if (
293
			class_exists( 'DateTimeZone' )
294
			&& method_exists( 'DateTime', 'createFromFormat' )
295
		) {
296
			$expires_date = DateTime::createFromFormat( 'D, d M Y H:i:s T', $expires_header, new DateTimeZone( 'UTC' ) );
297
			if ( $expires_date instanceof DateTime ) {
298
				return date_format( $expires_date, 'U' );
299
			}
300
		} else {
301
			$expires_array = strptime( $expires_header, '%a, %d %b %Y %H:%M:%S %Z' );
302
			if ( is_array( $expires_array ) && isset( $expires_array['tm_hour'] ) && isset( $expires_array['tm_min'] ) && isset( $expires_array['tm_sec'] ) && isset( $expires_array['tm_mon'] ) && isset( $expires_array['tm_mday'] ) && isset( $expires_array['tm_year'] ) ) {
303
				return gmmktime( $expires_array['tm_hour'], $expires_array['tm_min'], $expires_array['tm_sec'], 1 + $expires_array['tm_mon'], $expires_array['tm_mday'], 1900 + $expires_array['tm_year'] );
304
			}
305
		}
306
		return false;
307
	}
308
309
	/**
310
	 * Extract the site's host domain for statistics and comparison against an allowed site list in the case of restricted embeds.
311
	 *
312
	 * @since 1.2
313
	 * @param string $url absolute URL
314
	 * @return bool|string host component of the URL, or false if none found
315
	 */
316
	public static function hostname( $url ) {
317
		return parse_url( esc_url_raw( $url ), PHP_URL_HOST );
318
	}
319
320
321
	/**
322
	 * Request data from WordPress.com for the given guid, maxwidth, and calculated blog hostname.
323
	 *
324
	 * @since 1.3
325
	 * @return stdClass|WP_Error parsed JSON response or WP_Error if request unsuccessful
326
	 */
327
	private function get_data() {
328
		global $wp_version;
329
330
		$domain         = self::hostname( home_url() );
331
		$request_params = array(
332
			'guid'   => $this->guid,
333
			'domain' => $domain,
334
		);
335
		if ( isset( $this->maxwidth ) && $this->maxwidth > 0 ) {
336
			$request_params['maxwidth'] = $this->maxwidth;
337
		}
338
339
		$url = 'https://v.wordpress.com/data/wordpress.json';
340
341
		$response = wp_remote_get(
342
			add_query_arg( $request_params, $url ),
343
			array(
344
				'redirection' => 1,
345
				'user-agent'  => 'VideoPress plugin ' . $this->version . '; WordPress ' . $wp_version . ' (' . home_url( '/' ) . ')',
346
			)
347
		);
348
349
		unset( $request_params );
350
		unset( $url );
351
		$response_body = wp_remote_retrieve_body( $response );
352
		$response_code = absint( wp_remote_retrieve_response_code( $response ) );
353
354
		if ( is_wp_error( $response ) ) {
355
			return $response;
356
		} elseif ( $response_code === 400 ) {
357
			return new WP_Error( 'bad_config', __( 'The VideoPress plugin could not communicate with the VideoPress servers. This error is most likely caused by a misconfigured plugin. Please reinstall or upgrade.', 'jetpack' ) );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'bad_config'.

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...
358
		} elseif ( $response_code === 403 ) {
359
			return new WP_Error( 'http_forbidden', '<p>' . sprintf( __( '<strong>%s</strong> is not an allowed embed site.', 'jetpack' ), esc_html( $domain ) ) . '</p><p>' . __( 'Publisher limits playback of video embeds.', 'jetpack' ) . '</p>' );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'http_forbidden'.

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...
360
		} elseif ( $response_code === 404 ) {
361
			return new WP_Error( 'http_not_found', '<p>' . sprintf( __( 'No data found for VideoPress identifier: <strong>%s</strong>.', 'jetpack' ), $this->guid ) . '</p>' );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'http_not_found'.

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...
362
		} elseif ( $response_code !== 200 || empty( $response_body ) ) {
363
			return;
364
		} else {
365
			$expires_header = wp_remote_retrieve_header( $response, 'Expires' );
366
			if ( ! empty( $expires_header ) ) {
367
				$expires = self::calculate_expiration( $expires_header );
368
				if ( ! empty( $expires ) ) {
369
					$this->expires = $expires;
0 ignored issues
show
Documentation Bug introduced by
It seems like $expires can also be of type boolean. However, the property $expires is declared as type integer. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
370
				}
371
			}
372
			return json_decode( $response_body );
373
		}
374
	}
375
}
376