Completed
Push — update/e2e-ci-slack-notificati... ( 1831b7 )
by Yaroslav
15:44 queued 07:54
created

functions.opengraph.php (6 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/**
3
 * Open Graph Tags
4
 *
5
 * Add Open Graph tags so that Facebook (and any other service that supports them)
6
 * can crawl the site better and we provide a better sharing experience.
7
 *
8
 * @link https://ogp.me/
9
 * @link https://developers.facebook.com/docs/opengraph/
10
 *
11
 * @package Jetpack
12
 */
13
14
add_action( 'wp_head', 'jetpack_og_tags' );
15
add_action( 'amp_story_head', 'jetpack_og_tags' );
16
17
/**
18
 * Outputs Open Graph tags generated by Jetpack.
19
 */
20
function jetpack_og_tags() {
21
	/**
22
	 * Allow Jetpack to output Open Graph Meta Tags.
23
	 *
24
	 * @module sharedaddy, publicize
25
	 *
26
	 * @since 2.0.0
27
	 * @deprecated 2.0.3 Duplicative filter. Use `jetpack_enable_open_graph`.
28
	 *
29
	 * @param bool true Should Jetpack's Open Graph Meta Tags be enabled. Default to true.
30
	 */
31
	if ( false === apply_filters( 'jetpack_enable_opengraph', true ) ) {
32
		_deprecated_function( 'jetpack_enable_opengraph', '2.0.3', 'jetpack_enable_open_graph' );
33
		return;
34
	}
35
36
	$is_amp_response = ( class_exists( 'Jetpack_AMP_Support' ) && Jetpack_AMP_Support::is_amp_request() );
37
38
	// Disable the widont filter on WP.com to avoid stray &nbsps.
39
	$disable_widont = remove_filter( 'the_title', 'widont' );
40
41
	$og_output = "\n";
42
	if ( ! $is_amp_response ) { // Because AMP optimizes the order or the nodes in the head.
43
		$og_output .= "<!-- Jetpack Open Graph Tags -->\n";
44
	}
45
	$tags = array();
46
47
	/**
48
	 * Filter the minimum width of the images used in Jetpack Open Graph Meta Tags.
49
	 *
50
	 * @module sharedaddy, publicize
51
	 *
52
	 * @since 2.0.0
53
	 *
54
	 * @param int 200 Minimum image width used in Jetpack Open Graph Meta Tags.
55
	 */
56
	$image_width = absint( apply_filters( 'jetpack_open_graph_image_width', 200 ) );
57
	/**
58
	 * Filter the minimum height of the images used in Jetpack Open Graph Meta Tags.
59
	 *
60
	 * @module sharedaddy, publicize
61
	 *
62
	 * @since 2.0.0
63
	 *
64
	 * @param int 200 Minimum image height used in Jetpack Open Graph Meta Tags.
65
	 */
66
	$image_height       = absint( apply_filters( 'jetpack_open_graph_image_height', 200 ) );
67
	$description_length = 197;
68
69
	if ( is_home() || is_front_page() ) {
70
		$site_type              = Jetpack_Options::get_option_and_ensure_autoload( 'open_graph_protocol_site_type', '' );
71
		$tags['og:type']        = ! empty( $site_type ) ? $site_type : 'website';
72
		$tags['og:title']       = get_bloginfo( 'name' );
73
		$tags['og:description'] = get_bloginfo( 'description' );
74
75
		$front_page_id = get_option( 'page_for_posts' );
76
		if ( 'page' === get_option( 'show_on_front' ) && $front_page_id && is_home() ) {
77
			$tags['og:url'] = get_permalink( $front_page_id );
78
		} else {
79
			$tags['og:url'] = home_url( '/' );
80
		}
81
82
		// Associate a blog's root path with one or more Facebook accounts.
83
		$facebook_admins = Jetpack_Options::get_option_and_ensure_autoload( 'facebook_admins', array() );
84
		if ( ! empty( $facebook_admins ) ) {
85
			$tags['fb:admins'] = $facebook_admins;
86
		}
87
	} elseif ( is_author() ) {
88
		$tags['og:type'] = 'profile';
89
90
		$author = get_queried_object();
91
92
		if ( is_a( $author, 'WP_User' ) ) {
93
			$tags['og:title'] = $author->display_name;
94
			if ( ! empty( $author->user_url ) ) {
95
				$tags['og:url'] = $author->user_url;
96
			} else {
97
				$tags['og:url'] = get_author_posts_url( $author->ID );
98
			}
99
			$tags['og:description']     = $author->description;
100
			$tags['profile:first_name'] = get_the_author_meta( 'first_name', $author->ID );
101
			$tags['profile:last_name']  = get_the_author_meta( 'last_name', $author->ID );
102
		}
103
	} elseif ( is_archive() ) {
104
		$tags['og:type']  = 'website';
105
		$tags['og:title'] = wp_get_document_title();
106
107
		$archive = get_queried_object();
108
		if ( ! empty( $archive ) ) {
109
			if ( is_category() || is_tag() || is_tax() ) {
110
				$tags['og:url']         = get_term_link( $archive->term_id, $archive->taxonomy );
111
				$tags['og:description'] = $archive->description;
112
			} elseif ( is_post_type_archive() ) {
113
				$tags['og:url']         = get_post_type_archive_link( $archive->name );
114
				$tags['og:description'] = $archive->description;
115
			}
116
		}
117
	} elseif ( is_singular() ) {
118
		global $post;
119
		$data = $post; // so that we don't accidentally explode the global.
120
121
		$tags['og:type'] = 'article';
122
		if ( empty( $data->post_title ) ) {
123
			$tags['og:title'] = ' ';
124
		} else {
125
			/** This filter is documented in core/src/wp-includes/post-template.php */
126
			$tags['og:title'] = wp_kses( apply_filters( 'the_title', $data->post_title, $data->ID ), array() );
0 ignored issues
show
The call to apply_filters() has too many arguments starting with $data->ID.

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...
127
		}
128
129
		$tags['og:url'] = get_permalink( $data->ID );
130
		if ( ! post_password_required() ) {
131
			/*
132
			 * If the post author set an excerpt, use that.
133
			 * Otherwise, pick the post content that comes before the More tag if there is one.
134
			 */
135
			$excerpt = ! empty( $data->post_excerpt )
136
				? $data->post_excerpt
137
				: explode( '<!--more-->', $data->post_content )[0];
138
139
			$tags['og:description'] = jetpack_og_get_description( $excerpt );
140
		}
141
142
		$tags['article:published_time'] = gmdate( 'c', strtotime( $data->post_date_gmt ) );
143
		$tags['article:modified_time']  = gmdate( 'c', strtotime( $data->post_modified_gmt ) );
144
		if ( post_type_supports( get_post_type( $data ), 'author' ) && isset( $data->post_author ) ) {
145
			$publicize_facebook_user = get_post_meta( $data->ID, '_publicize_facebook_user', true );
146
			if ( ! empty( $publicize_facebook_user ) ) {
147
				$tags['article:author'] = esc_url( $publicize_facebook_user );
148
			}
149
		}
150
	} elseif ( is_search() ) {
151
		if ( '' !== get_query_var( 's', '' ) ) {
152
			$tags['og:title'] = wp_get_document_title();
153
		}
154
	}
155
	/**
156
	 * Allow plugins to inject additional template-specific Open Graph tags.
157
	 *
158
	 * @module sharedaddy, publicize
159
	 *
160
	 * @since 3.0.0
161
	 *
162
	 * @param array $tags Array of Open Graph Meta tags.
163
	 * @param array $args Array of image size parameters.
164
	 */
165
	$tags = apply_filters( 'jetpack_open_graph_base_tags', $tags, compact( 'image_width', 'image_height' ) );
0 ignored issues
show
The call to apply_filters() has too many arguments starting with compact('image_width', 'image_height').

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...
166
167
	// Re-enable widont if we had disabled it.
168
	if ( $disable_widont ) {
169
		add_filter( 'the_title', 'widont' );
170
	}
171
172
	/**
173
	 * Do not return any Open Graph Meta tags if we don't have any info about a post.
174
	 *
175
	 * @module sharedaddy, publicize
176
	 *
177
	 * @since 3.0.0
178
	 *
179
	 * @param bool true Do not return any Open Graph Meta tags if we don't have any info about a post.
180
	 */
181
	if ( empty( $tags ) && apply_filters( 'jetpack_open_graph_return_if_empty', true ) ) {
182
		return;
183
	}
184
185
	$tags['og:site_name'] = get_bloginfo( 'name' );
186
187
	// Get image info and build tags.
188
	if ( ! post_password_required() ) {
189
		$image_info       = jetpack_og_get_image( $image_width, $image_height );
190
		$tags['og:image'] = $image_info['src'];
191
192
		if ( ! empty( $image_info['width'] ) ) {
193
			$tags['og:image:width'] = (int) $image_info['width'];
194
		}
195
		if ( ! empty( $image_info['height'] ) ) {
196
			$tags['og:image:height'] = (int) $image_info['height'];
197
		}
198
		if ( ! empty( $image_info['alt_text'] ) ) {
199
			$tags['og:image:alt'] = esc_attr( $image_info['alt_text'] );
200
		}
201
	}
202
203
	// Facebook whines if you give it an empty title.
204
	if ( empty( $tags['og:title'] ) ) {
205
		$tags['og:title'] = __( '(no title)', 'jetpack' );
206
	}
207
208
	// Shorten the description if it's too long.
209
	if ( isset( $tags['og:description'] ) ) {
210
		$tags['og:description'] = strlen( $tags['og:description'] ) > $description_length ? mb_substr( $tags['og:description'], 0, $description_length ) . '…' : $tags['og:description'];
211
	}
212
213
	// Try to add OG locale tag if the WP->FB data mapping exists.
214
	if ( defined( 'JETPACK__GLOTPRESS_LOCALES_PATH' ) && file_exists( JETPACK__GLOTPRESS_LOCALES_PATH ) ) {
215
		require_once JETPACK__GLOTPRESS_LOCALES_PATH;
216
		$_locale = get_locale();
217
218
		// We have to account for w.org vs WP.com locale divergence.
219
		if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
220
			$gp_locale = GP_Locales::by_field( 'slug', $_locale );
221
		} else {
222
			$gp_locale = GP_Locales::by_field( 'wp_locale', $_locale );
223
		}
224
	}
225
226
	if ( isset( $gp_locale->facebook_locale ) && ! empty( $gp_locale->facebook_locale ) ) {
227
		$tags['og:locale'] = $gp_locale->facebook_locale;
0 ignored issues
show
The variable $gp_locale does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
228
	}
229
230
	/**
231
	 * Allow the addition of additional Open Graph Meta tags, or modify the existing tags.
232
	 *
233
	 * @module sharedaddy, publicize
234
	 *
235
	 * @since 2.0.0
236
	 *
237
	 * @param array $tags Array of Open Graph Meta tags.
238
	 * @param array $args Array of image size parameters.
239
	 */
240
	$tags = apply_filters( 'jetpack_open_graph_tags', $tags, compact( 'image_width', 'image_height' ) );
0 ignored issues
show
The call to apply_filters() has too many arguments starting with compact('image_width', 'image_height').

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...
241
242
	// secure_urls need to go right after each og:image to work properly so we will abstract them here.
243
	$tags['og:image:secure_url'] = ( empty( $tags['og:image:secure_url'] ) ) ? '' : $tags['og:image:secure_url'];
244
	$secure                      = $tags['og:image:secure_url'];
245
	unset( $tags['og:image:secure_url'] );
246
	$secure_image_num = 0;
247
248
	foreach ( (array) $tags as $tag_property => $tag_content ) {
249
		// to accommodate multiple images.
250
		$tag_content = (array) $tag_content;
251
		$tag_content = array_unique( $tag_content );
252
253
		foreach ( $tag_content as $tag_content_single ) {
254
			if ( empty( $tag_content_single ) ) {
255
				continue; // Don't ever output empty tags.
256
			}
257
			$og_tag = sprintf( '<meta property="%s" content="%s" />', esc_attr( $tag_property ), esc_attr( $tag_content_single ) );
258
			/**
259
			 * Filter the HTML Output of each Open Graph Meta tag.
260
			 *
261
			 * @module sharedaddy, publicize
262
			 *
263
			 * @since 2.0.0
264
			 *
265
			 * @param string $og_tag HTML HTML Output of each Open Graph Meta tag.
266
			 */
267
			$og_output .= apply_filters( 'jetpack_open_graph_output', $og_tag );
268
			$og_output .= "\n";
269
270
			if ( 'og:image' === $tag_property ) {
271
				if ( is_array( $secure ) && ! empty( $secure[ $secure_image_num ] ) ) {
272
					$og_tag = sprintf( '<meta property="og:image:secure_url" content="%s" />', esc_url( $secure[ $secure_image_num ] ) );
273
					/** This filter is documented in functions.opengraph.php */
274
					$og_output .= apply_filters( 'jetpack_open_graph_output', $og_tag );
275
					$og_output .= "\n";
276
				} elseif ( ! is_array( $secure ) && ! empty( $secure ) ) {
277
					$og_tag = sprintf( '<meta property="og:image:secure_url" content="%s" />', esc_url( $secure ) );
278
					/** This filter is documented in functions.opengraph.php */
279
					$og_output .= apply_filters( 'jetpack_open_graph_output', $og_tag );
280
					$og_output .= "\n";
281
				}
282
				$secure_image_num++;
283
			}
284
		}
285
	}
286
287
	if ( ! $is_amp_response ) { // Because AMP optimizes the order or the nodes in the head.
288
		$og_output .= "\n<!-- End Jetpack Open Graph Tags -->";
289
	}
290
	$og_output .= "\n";
291
	// This is trusted output or added by a filter.
292
	echo $og_output; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
293
}
294
295
/**
296
 * Returns an image used in social shares.
297
 *
298
 * @since 2.0.0
299
 *
300
 * @param int  $width Minimum width for the image. Default is 200 based on Facebook's requirement.
301
 * @param int  $height Minimum height for the image. Default is 200 based on Facebook's requirement.
302
 * @param null $deprecated Deprecated.
303
 *
304
 * @return array The source ('src'), 'width', and 'height' of the image.
305
 */
306
function jetpack_og_get_image( $width = 200, $height = 200, $deprecated = null ) {
307
	if ( ! empty( $deprecated ) ) {
308
		_deprecated_argument( __FUNCTION__, '6.6.0' );
309
	}
310
	$image = array();
311
312
	if ( is_singular() && ! is_home() ) {
313
		// Grab obvious image if post is an attachment page for an image.
314
		if ( is_attachment( get_the_ID() ) && 'image' === substr( get_post_mime_type(), 0, 5 ) ) {
315
			$image['src'] = wp_get_attachment_url( get_the_ID() );
316
		}
317
318
		// Attempt to find something good for this post using our generalized PostImages code.
319
		if ( empty( $image ) && class_exists( 'Jetpack_PostImages' ) ) {
320
			$post_images = Jetpack_PostImages::get_images(
321
				get_the_ID(),
322
				array(
323
					'width'  => $width,
324
					'height' => $height,
325
				)
326
			);
327
			if ( $post_images && ! is_wp_error( $post_images ) ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $post_images of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
328
				foreach ( (array) $post_images as $post_image ) {
329
					$image['src'] = $post_image['src'];
330 View Code Duplication
					if ( isset( $post_image['src_width'], $post_image['src_height'] ) ) {
331
						$image['width']  = $post_image['src_width'];
332
						$image['height'] = $post_image['src_height'];
333
					}
334
					if ( ! empty( $post_image['alt_text'] ) ) {
335
						$image['alt_text'] = $post_image['alt_text'];
336
					}
337
				}
338
			}
339
		}
340
	} elseif ( is_author() ) {
341
		$author = get_queried_object();
342
		if ( is_a( $author, 'WP_User' ) ) {
343
			$image['src'] = get_avatar_url(
344
				$author->user_email,
345
				array(
346
					'size' => $width,
347
				)
348
			);
349
		}
350
	}
351
352
	// First fall back, blavatar.
353
	if ( empty( $image ) && function_exists( 'blavatar_domain' ) ) {
354
		$blavatar_domain = blavatar_domain( site_url() );
355
		if ( blavatar_exists( $blavatar_domain ) ) {
356
			$image['src']    = blavatar_url( $blavatar_domain, 'img', $width, false, true );
357
			$image['width']  = $width;
358
			$image['height'] = $height;
359
		}
360
	}
361
362
	// Second fall back, Site Logo.
363 View Code Duplication
	if ( empty( $image ) && ( function_exists( 'jetpack_has_site_logo' ) && jetpack_has_site_logo() ) ) {
364
		$image_id = jetpack_get_site_logo( 'id' );
365
		$logo     = wp_get_attachment_image_src( $image_id, 'full' );
366
		if (
367
			isset( $logo[0], $logo[1], $logo[2] )
368
			&& ( _jetpack_og_get_image_validate_size( $logo[1], $logo[2], $width, $height ) )
369
		) {
370
			$image['src']    = $logo[0];
371
			$image['width']  = $logo[1];
372
			$image['height'] = $logo[2];
373
		}
374
	}
375
376
	// Third fall back, Core Site Icon, if valid in size.
377 View Code Duplication
	if ( empty( $image ) && has_site_icon() ) {
378
		$image_id = get_option( 'site_icon' );
379
		$icon     = wp_get_attachment_image_src( $image_id, 'full' );
380
		if (
381
			isset( $icon[0], $icon[1], $icon[2] )
382
			&& ( _jetpack_og_get_image_validate_size( $icon[1], $icon[2], $width, $height ) )
383
		) {
384
			$image['src']    = $icon[0];
385
			$image['width']  = $icon[1];
386
			$image['height'] = $icon[2];
387
		}
388
	}
389
390
	// Final fall back, blank image.
391
	if ( empty( $image ) ) {
392
		/**
393
		 * Filter the default Open Graph Image tag, used when no Image can be found in a post.
394
		 *
395
		 * @since 3.0.0
396
		 *
397
		 * @param string $str Default Image URL.
398
		 */
399
		$image['src'] = apply_filters( 'jetpack_open_graph_image_default', 'https://s0.wp.com/i/blank.jpg' );
400
	}
401
402
	return $image;
403
}
404
405
406
/**
407
 * Validate the width and height against required width and height
408
 *
409
 * @param int $width      Width of the image.
410
 * @param int $height     Height of the image.
411
 * @param int $req_width  Required width to pass validation.
412
 * @param int $req_height Required height to pass validation.
413
 *
414
 * @return bool - True if the image passed the required size validation
415
 */
416
function _jetpack_og_get_image_validate_size( $width, $height, $req_width, $req_height ) {
417
	if ( ! $width || ! $height ) {
418
		return false;
419
	}
420
421
	$valid_width         = ( $width >= $req_width );
422
	$valid_height        = ( $height >= $req_height );
423
	$is_image_acceptable = $valid_width && $valid_height;
424
425
	return $is_image_acceptable;
426
}
427
428
/**
429
 * Gets a gravatar URL of the specified size.
430
 *
431
 * @param string $email E-mail address to get gravatar for.
432
 * @param int    $width Size of returned gravatar.
433
 * @return array|bool|mixed|string
434
 */
435
function jetpack_og_get_image_gravatar( $email, $width ) {
436
	return get_avatar_url(
437
		$email,
438
		array(
439
			'size' => $width,
440
		)
441
	);
442
}
443
444
/**
445
 * Clean up text meant to be used as Description Open Graph tag.
446
 *
447
 * There should be:
448
 * - no links
449
 * - no shortcodes
450
 * - no html tags or their contents
451
 * - not too many words.
452
 *
453
 * @param string       $description Text coming from WordPress (autogenerated or manually generated by author).
454
 * @param WP_Post|null $data        Information about our post.
455
 *
456
 * @return string $description Cleaned up description string.
457
 */
458
function jetpack_og_get_description( $description = '', $data = null ) {
459
	// Remove tags such as <style or <script.
460
	$description = wp_strip_all_tags( $description );
461
462
	/*
463
	 * Clean up any plain text entities left into formatted entities.
464
	 * Intentionally not using a filter to prevent pollution.
465
	 * @see https://github.com/Automattic/jetpack/pull/2899#issuecomment-151957382
466
	 */
467
	$description = wp_kses(
468
		trim(
469
			convert_chars(
470
				wptexturize( $description )
471
			)
472
		),
473
		array()
474
	);
475
476
	// Remove shortcodes.
477
	$description = strip_shortcodes( $description );
478
479
	// Remove links.
480
	$description = preg_replace(
481
		'@https?://[\S]+@',
482
		'',
483
		$description
484
	);
485
486
	/*
487
	 * Limit things to a small text blurb.
488
	 * There isn't a hard limit set by Facebook, so let's rely on WP's own limit.
489
	 * (55 words or the localized equivalent).
490
	 * This limit can be customized with the wp_trim_words filter.
491
	 */
492
	$description = wp_trim_words( $description );
493
494
	// Let's set a default if we have no text by now.
495
	if ( empty( $description ) ) {
496
		/**
497
		 * Filter the fallback `og:description` used when no excerpt information is provided.
498
		 *
499
		 * @module sharedaddy, publicize
500
		 *
501
		 * @since 3.9.0
502
		 *
503
		 * @param string $var  Fallback og:description. Default is translated `Visit the post for more'.
504
		 * @param object $data Post object for the current post.
505
		 */
506
		$description = apply_filters(
507
			'jetpack_open_graph_fallback_description',
508
			__( 'Visit the post for more.', 'jetpack' ),
509
			$data
0 ignored issues
show
The call to apply_filters() has too many arguments starting with $data.

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...
510
		);
511
	}
512
513
	return $description;
514
}
515