Completed
Push — update/sal/post-base ( 3dce43 )
by
unknown
30:48 queued 23:01
created

SAL_Post::safe_maybe_unserialize()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
nc 2
nop 1
dl 0
loc 7
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * This class wraps a WP_Post and proxies any undefined attributes
4
 * and methods to the wrapped class. We need to do this because at present
5
 * the WP_Post class is marked as final (in 4.5 this will change, though it's
6
 * not clear if there will be a mechanism to retrieve from the DB into the over-
7
 * ridden class dynamically).
8
 **/
9
10
require_once dirname( __FILE__ ) . '/class.json-api-metadata.php';
11
require_once dirname( __FILE__ ) . '/class.json-api-date.php';
12
require_once ( ABSPATH . "wp-includes/post.php" );
13
14
abstract class SAL_Post {
15
	public $post;
16
	public $context;
17
	public $site;
18
19
	function __construct( $site, $post, $context ) {
20
		$this->post = $post;
21
		$this->context = $context;
22
		$this->site = $site;
23
	}
24
25
	public function __set( $key, $value ) {
26
		$this->post->{ $key } = $value;
27
	}
28
29
	public function __get( $key ) {
30
		if ( $key === 'links' ) {
31
			require_once dirname( __FILE__ ) . '/class.json-api-links.php';
32
			return WPCOM_JSON_API_Links::getInstance();
33
		}
34
		return $this->post->{ $key };
35
	}
36
37
	public function __call( $name, $arguments ) {
38
		if ( is_callable( array( $this->post, $name ) ) ) {
39
			return call_user_func_array( array( $this->post, $name ), $arguments );
40
		} else {
41
			trigger_error("Call to undefined method '{$name}'");
42
		}
43
	}
44
45
	public function __isset ( $name ) {
46
		return isset( $this->post->{ $name } );
47
	}
48
49
	abstract public function get_like_count();
50
	abstract public function is_liked();
51
	abstract public function is_reblogged();
52
	abstract public function is_following();
53
	abstract public function get_global_id();
54
	abstract public function get_geo();
55
56
	public function get_menu_order() {
57
		return (int) $this->post->menu_order;
58
	}
59
60
	public function get_guid() {
61
		return (string) $this->post->guid;
62
	}
63
64
	public function get_type() {
65
		return (string) $this->post->post_type;
66
	}
67
68
	public function get_terms() {
69
		$taxonomies = get_object_taxonomies( $this->post, 'objects' );
70
		$terms = array();
71
		foreach ( $taxonomies as $taxonomy ) {
72
			if ( ! $taxonomy->public && ! current_user_can( $taxonomy->cap->assign_terms ) ) {
73
				continue;
74
			}
75
76
			$terms[ $taxonomy->name ] = array();
77
78
			$taxonomy_terms = wp_get_object_terms( $this->post->ID, $taxonomy->name, array( 'fields' => 'all' ) );
79
			foreach ( $taxonomy_terms as $term ) {
80
				$formatted_term = $this->format_taxonomy( $term, $taxonomy->name, 'display' );
81
				$terms[ $taxonomy->name ][ $term->name ] = $formatted_term;
82
			}
83
84
			$terms[ $taxonomy->name ] = (object) $terms[ $taxonomy->name ];
85
		}
86
87
		return (object) $terms;
88
	}
89
90
	public function get_tags() {
91
		$tags = array();
92
		$terms = wp_get_post_tags( $this->post->ID );
93
		foreach ( $terms as $term ) {
94
			if ( !empty( $term->name ) ) {
95
				$tags[$term->name] = $this->format_taxonomy( $term, 'post_tag', 'display' );
96
			}
97
		}
98
		return (object) $tags;
99
	}
100
101
	public function get_categories() {
102
		$categories = array();
103
		$terms = wp_get_object_terms( $this->post->ID, 'category', array( 'fields' => 'all' ) );
104 View Code Duplication
		foreach ( $terms as $term ) {
105
			if ( !empty( $term->name ) ) {
106
				$categories[$term->name] = $this->format_taxonomy( $term, 'category', 'display' );
107
			}
108
		}
109
		return (object) $categories;
110
	}
111
112
	public function get_attachments_and_count() {
113
		$attachments = array();
114
		$_attachments = new WP_Query( array( 'post_parent' => $this->post->ID, 'post_status' => 'inherit', 'post_type' => 'attachment', 'posts_per_page' => '20' ) );
115
		foreach ( $_attachments->posts as $attachment ) {
116
			$attachments[$attachment->ID] = $this->get_media_item_v1_1( $attachment->ID );
117
		}
118
		return array( (object) $attachments, (int) $_attachments->found_posts );
119
	}
120
121
	public function get_metadata() {
122
		$metadata = array();
123
		foreach ( (array) has_meta( $this->post->ID ) as $meta ) {
124
			// Don't expose protected fields.
125
			$meta_key = $meta['meta_key'];
126
127
			$show = !( WPCOM_JSON_API_Metadata::is_internal_only( $meta_key ) )
128
				&&
129
					(
130
						WPCOM_JSON_API_Metadata::is_public( $meta_key )
131
					||
132
						current_user_can( 'edit_post_meta', $this->post->ID , $meta_key )
133
					);
134
135
			// Only business plan subscribers can view custom meta description
136
			if ( Jetpack_SEO_Posts::DESCRIPTION_META_KEY == $meta_key && ! Jetpack_SEO_Utils::is_enabled_jetpack_seo() ) {
137
				$show = false;
138
			}
139
140
			if ( $show ) {
141
				$metadata[] = array(
142
					'id'    => $meta['meta_id'],
143
					'key'   => $meta['meta_key'],
144
					'value' => $this->safe_maybe_unserialize( $meta['meta_value'] ),
145
				);
146
			}
147
		}
148
149
		if ( ! empty( $metadata ) ) {
150
			return $metadata;
151
		} else {
152
			return false;
153
		}
154
	}
155
156
	public function get_meta() {
157
		$meta = (object) array(
158
			'links' => (object) array(
159
				'self'    => (string) $this->get_post_link(),
160
				'help'    => (string) $this->get_post_link( 'help' ),
161
				'site'    => (string) $this->get_site_link(),
162
				'replies' => (string) $this->get_post_link( 'replies/' ),
163
				'likes'   => (string) $this->get_post_link( 'likes/' ),
164
			),
165
		);
166
167
		$amp_permalink = get_post_meta( $this->post->ID, '_jetpack_amp_permalink', true );
168
169
		if ( ! empty( $amp_permalink ) ) {
170
			$meta->links->amp = (string) $amp_permalink;
171
		}
172
173
		// add autosave link if a more recent autosave exists
174
		if ( 'edit' === $this->context ) {
175
			$autosave = wp_get_post_autosave( $this->post->ID );
176
			if ( $autosave && $autosave->post_modified > $this->post->post_modified )
177
				$meta->links->autosave = (string) $this->get_post_link() . '/autosave';
178
		}
179
180
		return $meta;
181
	}
182
183
	public function get_current_user_capabilities() {
184
		return array(
185
			'publish_post' => current_user_can( 'publish_post', $this->post->ID ),
186
			'delete_post'  => current_user_can( 'delete_post', $this->post->ID ),
187
			'edit_post'    => current_user_can( 'edit_post', $this->post->ID )
188
		);
189
	}
190
191
	public function get_revisions() {
192
		if ( 'edit' !== $this->context ) {
193
			return false;
194
		}
195
196
		$revisions = array();
197
		$post_revisions = wp_get_post_revisions( $this->post->ID );
198
199
		foreach ( $post_revisions as $_post ) {
200
			$revisions[] = $_post->ID;
201
		}
202
203
		return $revisions;
204
	}
205
206
	public function get_other_urls() {
207
		$other_urls = array();
208
209
		if ( 'publish' !== $this->post->post_status ) {
210
			$other_urls = $this->get_permalink_suggestions( $this->post->post_title );
211
		}
212
213
		return (object) $other_urls;
214
	}
215
216
	protected function get_site_link() {
217
		return $this->links->get_site_link( $this->site->get_id() );
0 ignored issues
show
Documentation introduced by
The property links does not exist on object<SAL_Post>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
218
	}
219
220
	protected function get_post_link( $path = null ) {
221
		return $this->links->get_post_link( $this->site->get_id(), $this->post->ID, $path );
0 ignored issues
show
Documentation introduced by
The property links does not exist on object<SAL_Post>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
222
	}
223
224
	public function get_publicize_urls() {
225
		$publicize_URLs = array();
226
		$publicize      = get_post_meta( $this->post->ID, 'publicize_results', true );
227 View Code Duplication
		if ( $publicize ) {
228
			foreach ( $publicize as $service => $data ) {
229
				switch ( $service ) {
230
				case 'twitter' :
231
					foreach ( $data as $datum ) {
232
						$publicize_URLs[] = esc_url_raw( "https://twitter.com/{$datum['user_id']}/status/{$datum['post_id']}" );
233
					}
234
					break;
235
				case 'fb' :
236
					foreach ( $data as $datum ) {
237
						$publicize_URLs[] = esc_url_raw( "https://www.facebook.com/permalink.php?story_fbid={$datum['post_id']}&id={$datum['user_id']}" );
238
					}
239
					break;
240
				}
241
			}
242
		}
243
		return (array) $publicize_URLs;
244
	}
245
246
	public function get_page_template() {
247
		return (string) get_post_meta( $this->post->ID, '_wp_page_template', true );
248
	}
249
250
	// note this is overridden in jetpack-shadow
251
	public function get_featured_image() {
252
		$image_attributes = wp_get_attachment_image_src( get_post_thumbnail_id( $this->post->ID ), 'full' );
253
		if ( is_array( $image_attributes ) && isset( $image_attributes[0] ) ) {
254
			return (string) $image_attributes[0];
255
		} else {
256
			return '';
257
		}
258
	}
259
260
	public function get_post_thumbnail() {
261
		$thumb = null;
262
263
		$thumb_id = get_post_thumbnail_id( $this->post->ID );
264
265 View Code Duplication
		if ( ! empty( $thumb_id ) ) {
266
			$attachment = get_post( $thumb_id );
267
			if ( ! empty( $attachment ) )
268
				$featured_image_object = $this->get_attachment( $attachment );
269
270
			if ( ! empty( $featured_image_object ) ) {
271
				$thumb = (object) $featured_image_object;
272
			}
273
		}
274
275
		return $thumb;
276
	}
277
278
	public function get_format() {
279
		$format = (string) get_post_format( $this->post->ID );
280
		if ( !$format ) {
281
			$format = 'standard';
282
		}
283
284
		return $format;
285
	}
286
287 View Code Duplication
	private function get_attachment( $attachment ) {
288
		$metadata = wp_get_attachment_metadata( $attachment->ID );
289
290
		$result = array(
291
			'ID'        => (int) $attachment->ID,
292
			'URL'       => (string) wp_get_attachment_url( $attachment->ID ),
293
			'guid'      => (string) $attachment->guid,
294
			'mime_type' => (string) $attachment->post_mime_type,
295
			'width'     => (int) isset( $metadata['width']  ) ? $metadata['width']  : 0,
296
			'height'    => (int) isset( $metadata['height'] ) ? $metadata['height'] : 0,
297
		);
298
299
		if ( isset( $metadata['duration'] ) ) {
300
			$result['duration'] = (int) $metadata['duration'];
301
		}
302
303
		/** This filter is documented in class.jetpack-sync.php */
304
		return (object) apply_filters( 'get_attachment', $result );
305
	}
306
307
	public function get_date() {
308
		return (string) WPCOM_JSON_API_Date::format_date( $this->post->post_date_gmt, $this->post->post_date );
309
	}
310
311
	public function get_modified_date() {
312
		return (string) WPCOM_JSON_API_Date::format_date( $this->post->post_modified_gmt, $this->post->post_modified );
313
	}
314
315
	public function get_title() {
316
		if ( 'display' === $this->context ) {
317
			return (string) get_the_title( $this->post->ID );
318
		} else {
319
			return (string) htmlspecialchars_decode( $this->post->post_title, ENT_QUOTES );
320
		}
321
	}
322
323
	public function get_url() {
324
		if ( 'revision' === $this->post->post_type ) {
325
			return (string) esc_url_raw( get_permalink( $this->post->post_parent ) );
326
		} else {
327
			return (string) esc_url_raw( get_permalink( $this->post->ID ) );
328
		}
329
	}
330
331
	public function get_shortlink() {
332
		return (string) esc_url_raw( wp_get_shortlink( $this->post->ID ) );
333
	}
334
335
	public function get_content() {
336
		if ( 'display' === $this->context ) {
337
			// TODO: move this WPCOM-specific hack
338
			add_filter( 'the_password_form', array( $this, 'the_password_form' ) );
339
			$content = (string) $this->get_the_post_content_for_display();
340
			remove_filter( 'the_password_form', array( $this, 'the_password_form' ) );
341
			return $content;
342
		} else {
343
			return (string) $this->post->post_content;
344
		}
345
	}
346
347
	public function get_excerpt() {
348
		if ( 'display' === $this->context ) {
349
			add_filter( 'the_password_form', array( $this, 'the_password_form' ) );
350
			ob_start();
351
			the_excerpt();
352
			$response = (string) ob_get_clean();
353
			remove_filter( 'the_password_form', array( $this, 'the_password_form' ) );
354
		} else {
355
			$response = htmlspecialchars_decode( (string) $this->post->post_excerpt, ENT_QUOTES );
356
		}
357
		return $response;
358
	}
359
360
	public function get_status() {
361
		return (string) get_post_status( $this->post->ID );
362
	}
363
364
	public function is_sticky() {
365
		return (bool) is_sticky( $this->post->ID );
366
	}
367
368
	public function get_slug() {
369
		return (string) $this->post->post_name;
370
	}
371
372
	public function get_password() {
373
		$password = (string) $this->post->post_password;
374
		if ( 'edit' === $this->context ) {
375
			$password = htmlspecialchars_decode( (string) $password, ENT_QUOTES );
376
		}
377
		return $password;
378
	}
379
380
	public function get_parent() {
381
		if ( $this->post->post_parent ) {
382
			$parent = get_post( $this->post->post_parent );
383
			if ( 'display' === $this->context ) {
384
				$parent_title = (string) get_the_title( $parent->ID );
385
			} else {
386
				$parent_title = (string) htmlspecialchars_decode( $this->post->post_title, ENT_QUOTES );
387
			}
388
			return (object) array(
389
				'ID'   => (int) $parent->ID,
390
				'type' => (string) $parent->post_type,
391
				'link' => (string) $this->links->get_post_link( $this->site->get_id(), $parent->ID ),
392
				'title' => $parent_title,
393
			);
394
		} else {
395
			return false;
396
		}
397
	}
398
399
	function the_password_form() {
400
		return __( 'This post is password protected.', 'jetpack' );
401
	}
402
403
	public function get_discussion() {
404
		return array(
405
			'comments_open'  => (bool) comments_open( $this->post->ID ),
406
			'comment_status' => (string) $this->post->comment_status,
407
			'pings_open'     => (bool) pings_open( $this->post->ID ),
408
			'ping_status'    => (string) $this->post->ping_status,
409
			'comment_count'  => (int) $this->post->comment_count,
410
		);
411
	}
412
413
	public function is_likes_enabled() {
414
		/** This filter is documented in modules/likes.php */
415
		$sitewide_likes_enabled = (bool) apply_filters( 'wpl_is_enabled_sitewide', ! get_option( 'disabled_likes' ) );
416
		$post_likes_switched    = get_post_meta( $this->post->ID, 'switch_like_status', true );
417
418
		return $post_likes_switched || ( $sitewide_likes_enabled && $post_likes_switched !== '0' );
419
	}
420
421
	public function is_sharing_enabled() {
422
		$show = true;
423
		/** This filter is documented in modules/sharedaddy/sharing-service.php */
424
		$show = apply_filters( 'sharing_show', $show, $this->post );
425
426
		$switched_status = get_post_meta( $this->post->ID, 'sharing_disabled', false );
427
428
		if ( !empty( $switched_status ) )
429
			$show = false;
430
431
		return (bool) $show;
432
	}
433
434
	// No Blog ID parameter.  No Post ID parameter.  Depends on globals.
435
	// Expects setup_postdata() to already have been run
436 View Code Duplication
	function get_the_post_content_for_display() {
437
		global $pages, $page;
438
439
		$old_pages = $pages;
440
		$old_page  = $page;
441
442
		$content = join( "\n\n", $pages );
443
		$content = preg_replace( '/<!--more(.*?)?-->/', '', $content );
444
		$pages   = array( $content );
445
		$page    = 1;
446
447
		ob_start();
448
		the_content();
449
		$return = ob_get_clean();
450
451
		$pages = $old_pages;
452
		$page  = $old_page;
453
454
		return $return;
455
	}
456
457
	public function get_author() {
458
		if ( 0 == $this->post->post_author )
459
			return null;
460
461
		$show_email = $this->context === 'edit' && current_user_can( 'edit_post', $this->post->ID );
462
463
		$user = get_user_by( 'id', $this->post->post_author );
464
465
		if ( ! $user || is_wp_error( $user ) ) {
466
			trigger_error( 'Unknown user', E_USER_WARNING );
467
468
			return null;
469
		}
470
471
		// TODO factor this out
472
		if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
473
			$active_blog = get_active_blog_for_user( $user->ID );
474
			$site_id     = $active_blog->blog_id;
475
			$profile_URL = "https://en.gravatar.com/{$user->user_login}";
476
		} else {
477
			$profile_URL = 'https://en.gravatar.com/' . md5( strtolower( trim( $user->user_email ) ) );
478
			$site_id     = -1;
479
		}
480
481
		$author = array(
482
			'ID'          => (int) $user->ID,
483
			'login'       => (string) $user->user_login,
484
			'email'       => $show_email ? (string) $user->user_email : false, // (string|bool)
485
			'name'        => (string) $user->display_name,
486
			'first_name'  => (string) $user->first_name,
487
			'last_name'   => (string) $user->last_name,
488
			'nice_name'   => (string) $user->user_nicename,
489
			'URL'         => (string) esc_url_raw( $user->user_url ),
490
			'avatar_URL'  => (string) esc_url_raw( $this->get_avatar_url( $user->user_email ) ),
491
			'profile_URL' => (string) esc_url_raw( $profile_URL )
492
		);
493
494
		if ($site_id > -1) {
495
			$author['site_ID'] = (int) $site_id;
496
		}
497
498
		return (object) $author;
499
	}
500
501
	protected function get_avatar_url( $email, $avatar_size = 96 ) {
502
		$avatar_url = wpcom_get_avatar_url( $email, $avatar_size, '', true );
503
		if ( ! $avatar_url || is_wp_error( $avatar_url ) ) {
504
			return '';
505
		}
506
507
		return esc_url_raw( htmlspecialchars_decode( $avatar_url[0] ) );
508
	}
509
510
	/**
511
 	 * Get extra post permalink suggestions
512
 	 * @return array	array of permalink suggestions: 'permalink_URL', 'suggested_slug'
513
 	 */
514
	public function get_permalink_suggestions( $title ) {
515
		$suggestions = array();
516
		list( $suggestions['permalink_URL'], $suggestions['suggested_slug'] ) = get_sample_permalink( $this->post->ID, $title );
517
		return $suggestions;
518
	}
519
520
	private function format_taxonomy( $taxonomy, $taxonomy_type, $context ) {
521
		// Permissions
522
		switch ( $context ) {
523
		case 'edit' :
524
			$tax = get_taxonomy( $taxonomy_type );
525
			if ( !current_user_can( $tax->cap->edit_terms ) )
526
				return new WP_Error( 'unauthorized', 'User cannot edit taxonomy', 403 );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'unauthorized'.

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...
527
			break;
528
		case 'display' :
529
			if ( -1 == get_option( 'blog_public' ) && ! current_user_can( 'read' ) ) {
530
				return new WP_Error( 'unauthorized', 'User cannot view taxonomy', 403 );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'unauthorized'.

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...
531
			}
532
			break;
533
		default :
534
			return new WP_Error( 'invalid_context', 'Invalid API CONTEXT', 400 );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'invalid_context'.

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...
535
		}
536
537
		$response                = array();
538
		$response['ID']          = (int) $taxonomy->term_id;
539
		$response['name']        = (string) $taxonomy->name;
540
		$response['slug']        = (string) $taxonomy->slug;
541
		$response['description'] = (string) $taxonomy->description;
542
		$response['post_count']  = (int) $taxonomy->count;
543
544
		if ( is_taxonomy_hierarchical( $taxonomy_type ) ) {
545
			$response['parent'] = (int) $taxonomy->parent;
546
		}
547
548
		$response['meta'] = (object) array(
549
			'links' => (object) array(
550
				'self' => (string) $this->links->get_taxonomy_link( $this->site->get_id(), $taxonomy->slug, $taxonomy_type ),
551
				'help' => (string) $this->links->get_taxonomy_link( $this->site->get_id(), $taxonomy->slug, $taxonomy_type, 'help' ),
552
				'site' => (string) $this->links->get_site_link( $this->site->get_id() ),
553
			),
554
		);
555
556
		return (object) $response;
557
	}
558
559
	// TODO: factor this out into site
560
	private function get_media_item_v1_1( $media_id ) {
561
		$media_item = get_post( $media_id );
562
563
		if ( ! $media_item || is_wp_error( $media_item ) )
564
			return new WP_Error( 'unknown_media', 'Unknown Media', 404 );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'unknown_media'.

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...
565
566
		$file = basename( wp_get_attachment_url( $media_item->ID ) );
567
		$file_info = pathinfo( $file );
568
		$ext  = $file_info['extension'];
569
570
		$response = array(
571
			'ID'           => $media_item->ID,
572
			'URL'          => wp_get_attachment_url( $media_item->ID ),
573
			'guid'         => $media_item->guid,
574
			'date'         => (string) WPCOM_JSON_API_Date::format_date( $media_item->post_date_gmt, $media_item->post_date ),
575
			'post_ID'      => $media_item->post_parent,
576
			'author_ID'    => (int) $media_item->post_author,
577
			'file'         => $file,
578
			'mime_type'    => $media_item->post_mime_type,
579
			'extension'    => $ext,
580
			'title'        => $media_item->post_title,
581
			'caption'      => $media_item->post_excerpt,
582
			'description'  => $media_item->post_content,
583
			'alt'          => get_post_meta( $media_item->ID, '_wp_attachment_image_alt', true ),
584
			'thumbnails'   => array()
585
		);
586
587
		if ( in_array( $ext, array( 'jpg', 'jpeg', 'png', 'gif' ) ) ) {
588
			$metadata = wp_get_attachment_metadata( $media_item->ID );
589
			if ( isset( $metadata['height'], $metadata['width'] ) ) {
590
				$response['height'] = $metadata['height'];
591
				$response['width'] = $metadata['width'];
592
			}
593
594 View Code Duplication
			if ( isset( $metadata['sizes'] ) ) {
595
				/**
596
				 * Filter the thumbnail sizes available for each attachment ID.
597
				 *
598
				 * @module json-api
599
				 *
600
				 * @since 3.9.0
601
				 *
602
				 * @param array $metadata['sizes'] Array of thumbnail sizes available for a given attachment ID.
603
				 * @param string $media_id Attachment ID.
604
				 */
605
				$sizes = apply_filters( 'rest_api_thumbnail_sizes', $metadata['sizes'], $media_id );
606
				if ( is_array( $sizes ) ) {
607
					foreach ( $sizes as $size => $size_details ) {
608
						$response['thumbnails'][ $size ] = dirname( $response['URL'] ) . '/' . $size_details['file'];
609
					}
610
				}
611
			}
612
613
			if ( isset( $metadata['image_meta'] ) ) {
614
				$response['exif'] = $metadata['image_meta'];
615
			}
616
		}
617
618 View Code Duplication
		if ( in_array( $ext, array( 'mp3', 'm4a', 'wav', 'ogg' ) ) ) {
619
			$metadata = wp_get_attachment_metadata( $media_item->ID );
620
			$response['length'] = $metadata['length'];
621
			$response['exif']   = $metadata;
622
		}
623
624
		if ( in_array( $ext, array( 'ogv', 'mp4', 'mov', 'wmv', 'avi', 'mpg', '3gp', '3g2', 'm4v' ) ) ) {
625
			$metadata = wp_get_attachment_metadata( $media_item->ID );
626
			if ( isset( $metadata['height'], $metadata['width'] ) ) {
627
				$response['height'] = $metadata['height'];
628
				$response['width']  = $metadata['width'];
629
			}
630
631
			if ( isset( $metadata['length'] ) ) {
632
				$response['length'] = $metadata['length'];
633
			}
634
635
			// add VideoPress info
636
			if ( function_exists( 'video_get_info_by_blogpostid' ) ) {
637
				$info = video_get_info_by_blogpostid( $this->site->get_id(), $media_id );
638
639
				// Thumbnails
640 View Code Duplication
				if ( function_exists( 'video_format_done' ) && function_exists( 'video_image_url_by_guid' ) ) {
641
					$response['thumbnails'] = array( 'fmt_hd' => '', 'fmt_dvd' => '', 'fmt_std' => '' );
642
					foreach ( $response['thumbnails'] as $size => $thumbnail_url ) {
643
						if ( video_format_done( $info, $size ) ) {
0 ignored issues
show
Bug introduced by
It seems like $info defined by video_get_info_by_blogpo...e->get_id(), $media_id) on line 637 can also be of type boolean; however, video_format_done() does only seem to accept object<stdClass>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
644
							$response['thumbnails'][ $size ] = video_image_url_by_guid( $info->guid, $size );
645
						} else {
646
							unset( $response['thumbnails'][ $size ] );
647
						}
648
					}
649
				}
650
651
				$response['videopress_guid'] = $info->guid;
652
				$response['videopress_processing_done'] = true;
653
				if ( '0000-00-00 00:00:00' == $info->finish_date_gmt ) {
654
					$response['videopress_processing_done'] = false;
655
				}
656
			}
657
		}
658
659
		$response['thumbnails'] = (object) $response['thumbnails'];
660
661
		$response['meta'] = (object) array(
662
			'links' => (object) array(
663
				'self' => (string) $this->links->get_media_link( $this->site->get_id(), $media_id ),
664
				'help' => (string) $this->links->get_media_link( $this->site->get_id(), $media_id, 'help' ),
665
				'site' => (string) $this->links->get_site_link( $this->site->get_id() ),
666
			),
667
		);
668
669
		// add VideoPress link to the meta
670
		if ( in_array( $ext, array( 'ogv', 'mp4', 'mov', 'wmv', 'avi', 'mpg', '3gp', '3g2', 'm4v' ) ) ) {
671 View Code Duplication
			if ( function_exists( 'video_get_info_by_blogpostid' ) ) {
672
				$response['meta']->links->videopress = (string) $this->links->get_link( '/videos/%s', $response['videopress_guid'], '' );
0 ignored issues
show
Documentation introduced by
The property links does not exist on object<SAL_Post>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
673
			}
674
		}
675
676 View Code Duplication
		if ( $media_item->post_parent > 0 ) {
677
			$response['meta']->links->parent = (string) $this->links->get_post_link( $this->site->get_id(), $media_item->post_parent );
0 ignored issues
show
Documentation introduced by
The property links does not exist on object<SAL_Post>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
678
		}
679
680
		return (object) $response;
681
	}
682
683
	/**
684
	 * Temporary wrapper around maybe_unserialize() to catch exceptions thrown by unserialize().
685
	 *
686
	 * Can be removed after https://core.trac.wordpress.org/ticket/45895 lands in Core.
687
	 *
688
	 * @param  string $original Serialized string.
689
	 * @return string Unserialized string or original string if an exception was raised.
690
	 **/
691
	protected function safe_maybe_unserialize( $original ) {
692
		try {
693
			return maybe_unserialize( $original );
694
		} catch ( Exception $e ) {
695
			return $original;
696
		}
697
	}
698
}
699