Issues (4967)

Security Analysis    not enabled

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

src/wp-includes/link-template.php (46 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
 * WordPress Link Template Functions
4
 *
5
 * @package WordPress
6
 * @subpackage Template
7
 */
8
9
/**
10
 * Displays the permalink for the current post.
11
 *
12
 * @since 1.2.0
13
 * @since 4.4.0 Added the `$post` parameter.
14
 *
15
 * @param int|WP_Post $post Optional. Post ID or post object. Default is the global `$post`.
0 ignored issues
show
Consider making the type for parameter $post a bit more specific; maybe use integer.
Loading history...
16
 */
17
function the_permalink( $post = 0 ) {
18
	/**
19
	 * Filters the display of the permalink for the current post.
20
	 *
21
	 * @since 1.5.0
22
	 * @since 4.4.0 Added the `$post` parameter.
23
	 *
24
	 * @param string      $permalink The permalink for the current post.
25
	 * @param int|WP_Post $post      Post ID, WP_Post object, or 0. Default 0.
26
	 */
27
	echo esc_url( apply_filters( 'the_permalink', get_permalink( $post ), $post ) );
28
}
29
30
/**
31
 * Retrieves a trailing-slashed string if the site is set for adding trailing slashes.
32
 *
33
 * Conditionally adds a trailing slash if the permalink structure has a trailing
34
 * slash, strips the trailing slash if not. The string is passed through the
35
 * {@see 'user_trailingslashit'} filter. Will remove trailing slash from string, if
36
 * site is not set to have them.
37
 *
38
 * @since 2.2.0
39
 *
40
 * @global WP_Rewrite $wp_rewrite
41
 *
42
 * @param string $string      URL with or without a trailing slash.
43
 * @param string $type_of_url Optional. The type of URL being considered (e.g. single, category, etc)
44
 *                            for use in the filter. Default empty string.
45
 * @return string The URL with the trailing slash appended or stripped.
46
 */
47
function user_trailingslashit($string, $type_of_url = '') {
48
	global $wp_rewrite;
49
	if ( $wp_rewrite->use_trailing_slashes )
50
		$string = trailingslashit($string);
51
	else
52
		$string = untrailingslashit($string);
53
54
	/**
55
	 * Filters the trailing-slashed string, depending on whether the site is set to use trailing slashes.
56
	 *
57
	 * @since 2.2.0
58
	 *
59
	 * @param string $string      URL with or without a trailing slash.
60
	 * @param string $type_of_url The type of URL being considered. Accepts 'single', 'single_trackback',
61
	 *                            'single_feed', 'single_paged', 'commentpaged', 'paged', 'home', 'feed',
62
	 *                            'category', 'page', 'year', 'month', 'day', 'post_type_archive'.
63
	 */
64
	return apply_filters( 'user_trailingslashit', $string, $type_of_url );
65
}
66
67
/**
68
 * Displays the permalink anchor for the current post.
69
 *
70
 * The permalink mode title will use the post title for the 'a' element 'id'
71
 * attribute. The id mode uses 'post-' with the post ID for the 'id' attribute.
72
 *
73
 * @since 0.71
74
 *
75
 * @param string $mode Optional. Permalink mode. Accepts 'title' or 'id'. Default 'id'.
76
 */
77
function permalink_anchor( $mode = 'id' ) {
78
	$post = get_post();
79
	switch ( strtolower( $mode ) ) {
80
		case 'title':
81
			$title = sanitize_title( $post->post_title ) . '-' . $post->ID;
82
			echo '<a id="'.$title.'"></a>';
83
			break;
84
		case 'id':
85
		default:
86
			echo '<a id="post-' . $post->ID . '"></a>';
87
			break;
88
	}
89
}
90
91
/**
92
 * Retrieves the full permalink for the current post or post ID.
93
 *
94
 * This function is an alias for get_permalink().
95
 *
96
 * @since 3.9.0
97
 *
98
 * @see get_permalink()
99
 *
100
 * @param int|WP_Post $post      Optional. Post ID or post object. Default is the global `$post`.
0 ignored issues
show
Consider making the type for parameter $post a bit more specific; maybe use integer.
Loading history...
101
 * @param bool        $leavename Optional. Whether to keep post name or page name. Default false.
102
 *
103
 * @return string|false The permalink URL or false if post does not exist.
104
 */
105
function get_the_permalink( $post = 0, $leavename = false ) {
106
	return get_permalink( $post, $leavename );
107
}
108
109
/**
110
 * Retrieves the full permalink for the current post or post ID.
111
 *
112
 * @since 1.0.0
113
 *
114
 * @param int|WP_Post $post      Optional. Post ID or post object. Default is the global `$post`.
0 ignored issues
show
Consider making the type for parameter $post a bit more specific; maybe use integer.
Loading history...
115
 * @param bool        $leavename Optional. Whether to keep post name or page name. Default false.
116
 * @return string|false The permalink URL or false if post does not exist.
117
 */
118
function get_permalink( $post = 0, $leavename = false ) {
119
	$rewritecode = array(
120
		'%year%',
121
		'%monthnum%',
122
		'%day%',
123
		'%hour%',
124
		'%minute%',
125
		'%second%',
126
		$leavename? '' : '%postname%',
127
		'%post_id%',
128
		'%category%',
129
		'%author%',
130
		$leavename? '' : '%pagename%',
131
	);
132
133
	if ( is_object( $post ) && isset( $post->filter ) && 'sample' == $post->filter ) {
134
		$sample = true;
135
	} else {
136
		$post = get_post( $post );
137
		$sample = false;
138
	}
139
140
	if ( empty($post->ID) )
141
		return false;
142
143
	if ( $post->post_type == 'page' )
144
		return get_page_link($post, $leavename, $sample);
145
	elseif ( $post->post_type == 'attachment' )
146
		return get_attachment_link( $post, $leavename );
147
	elseif ( in_array($post->post_type, get_post_types( array('_builtin' => false) ) ) )
148
		return get_post_permalink($post, $leavename, $sample);
149
150
	$permalink = get_option('permalink_structure');
151
152
	/**
153
	 * Filters the permalink structure for a post before token replacement occurs.
154
	 *
155
	 * Only applies to posts with post_type of 'post'.
156
	 *
157
	 * @since 3.0.0
158
	 *
159
	 * @param string  $permalink The site's permalink structure.
160
	 * @param WP_Post $post      The post in question.
161
	 * @param bool    $leavename Whether to keep the post name.
162
	 */
163
	$permalink = apply_filters( 'pre_post_link', $permalink, $post, $leavename );
164
165
	if ( '' != $permalink && !in_array( $post->post_status, array( 'draft', 'pending', 'auto-draft', 'future' ) ) ) {
166
		$unixtime = strtotime($post->post_date);
167
168
		$category = '';
169
		if ( strpos($permalink, '%category%') !== false ) {
170
			$cats = get_the_category($post->ID);
171
			if ( $cats ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $cats 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...
172
				$cats = wp_list_sort( $cats, array(
173
					'term_id' => 'ASC',
174
				) );
175
176
				/**
177
				 * Filters the category that gets used in the %category% permalink token.
178
				 *
179
				 * @since 3.5.0
180
				 *
181
				 * @param WP_Term  $cat  The category to use in the permalink.
182
				 * @param array    $cats Array of all categories (WP_Term objects) associated with the post.
183
				 * @param WP_Post  $post The post in question.
184
				 */
185
				$category_object = apply_filters( 'post_link_category', $cats[0], $cats, $post );
186
187
				$category_object = get_term( $category_object, 'category' );
188
				$category = $category_object->slug;
189
				if ( $parent = $category_object->parent )
190
					$category = get_category_parents($parent, false, '/', true) . $category;
191
			}
192
			// show default category in permalinks, without
193
			// having to assign it explicitly
194
			if ( empty($category) ) {
195
				$default_category = get_term( get_option( 'default_category' ), 'category' );
196
				if ( $default_category && ! is_wp_error( $default_category ) ) {
197
					$category = $default_category->slug;
198
				}
199
			}
200
		}
201
202
		$author = '';
203
		if ( strpos($permalink, '%author%') !== false ) {
204
			$authordata = get_userdata($post->post_author);
205
			$author = $authordata->user_nicename;
206
		}
207
208
		$date = explode(" ",date('Y m d H i s', $unixtime));
209
		$rewritereplace =
210
		array(
211
			$date[0],
212
			$date[1],
213
			$date[2],
214
			$date[3],
215
			$date[4],
216
			$date[5],
217
			$post->post_name,
218
			$post->ID,
219
			$category,
220
			$author,
221
			$post->post_name,
222
		);
223
		$permalink = home_url( str_replace($rewritecode, $rewritereplace, $permalink) );
224
		$permalink = user_trailingslashit($permalink, 'single');
225
	} else { // if they're not using the fancy permalink option
226
		$permalink = home_url('?p=' . $post->ID);
227
	}
228
229
	/**
230
	 * Filters the permalink for a post.
231
	 *
232
	 * Only applies to posts with post_type of 'post'.
233
	 *
234
	 * @since 1.5.0
235
	 *
236
	 * @param string  $permalink The post's permalink.
237
	 * @param WP_Post $post      The post in question.
238
	 * @param bool    $leavename Whether to keep the post name.
239
	 */
240
	return apply_filters( 'post_link', $permalink, $post, $leavename );
241
}
242
243
/**
244
 * Retrieves the permalink for a post of a custom post type.
245
 *
246
 * @since 3.0.0
247
 *
248
 * @global WP_Rewrite $wp_rewrite
249
 *
250
 * @param int $id         Optional. Post ID. Default uses the global `$post`.
251
 * @param bool $leavename Optional, defaults to false. Whether to keep post name. Default false.
252
 * @param bool $sample    Optional, defaults to false. Is it a sample permalink. Default false.
253
 * @return string|WP_Error The post permalink.
254
 */
255
function get_post_permalink( $id = 0, $leavename = false, $sample = false ) {
256
	global $wp_rewrite;
257
258
	$post = get_post($id);
259
260
	if ( is_wp_error( $post ) )
261
		return $post;
262
263
	$post_link = $wp_rewrite->get_extra_permastruct($post->post_type);
264
265
	$slug = $post->post_name;
266
267
	$draft_or_pending = get_post_status( $id ) && in_array( get_post_status( $id ), array( 'draft', 'pending', 'auto-draft', 'future' ) );
268
269
	$post_type = get_post_type_object($post->post_type);
270
271
	if ( $post_type->hierarchical ) {
272
		$slug = get_page_uri( $id );
273
	}
274
275
	if ( !empty($post_link) && ( !$draft_or_pending || $sample ) ) {
276
		if ( ! $leavename ) {
277
			$post_link = str_replace("%$post->post_type%", $slug, $post_link);
278
		}
279
		$post_link = home_url( user_trailingslashit($post_link) );
280
	} else {
281
		if ( $post_type->query_var && ( isset($post->post_status) && !$draft_or_pending ) )
282
			$post_link = add_query_arg($post_type->query_var, $slug, '');
283
		else
284
			$post_link = add_query_arg(array('post_type' => $post->post_type, 'p' => $post->ID), '');
285
		$post_link = home_url($post_link);
286
	}
287
288
	/**
289
	 * Filters the permalink for a post of a custom post type.
290
	 *
291
	 * @since 3.0.0
292
	 *
293
	 * @param string  $post_link The post's permalink.
294
	 * @param WP_Post $post      The post in question.
295
	 * @param bool    $leavename Whether to keep the post name.
296
	 * @param bool    $sample    Is it a sample permalink.
297
	 */
298
	return apply_filters( 'post_type_link', $post_link, $post, $leavename, $sample );
299
}
300
301
/**
302
 * Retrieves the permalink for the current page or page ID.
303
 *
304
 * Respects page_on_front. Use this one.
305
 *
306
 * @since 1.5.0
307
 *
308
 * @param int|WP_Post $post      Optional. Post ID or object. Default uses the global `$post`.
309
 * @param bool        $leavename Optional. Whether to keep the page name. Default false.
310
 * @param bool        $sample    Optional. Whether it should be treated as a sample permalink.
311
 *                               Default false.
312
 * @return string The page permalink.
313
 */
314
function get_page_link( $post = false, $leavename = false, $sample = false ) {
315
	$post = get_post( $post );
316
317 View Code Duplication
	if ( 'page' == get_option( 'show_on_front' ) && $post->ID == get_option( 'page_on_front' ) )
318
		$link = home_url('/');
319
	else
320
		$link = _get_page_link( $post, $leavename, $sample );
321
322
	/**
323
	 * Filters the permalink for a page.
324
	 *
325
	 * @since 1.5.0
326
	 *
327
	 * @param string $link    The page's permalink.
328
	 * @param int    $post_id The ID of the page.
329
	 * @param bool   $sample  Is it a sample permalink.
330
	 */
331
	return apply_filters( 'page_link', $link, $post->ID, $sample );
332
}
333
334
/**
335
 * Retrieves the page permalink.
336
 *
337
 * Ignores page_on_front. Internal use only.
338
 *
339
 * @since 2.1.0
340
 * @access private
341
 *
342
 * @global WP_Rewrite $wp_rewrite
343
 *
344
 * @param int|WP_Post $post      Optional. Post ID or object. Default uses the global `$post`.
345
 * @param bool        $leavename Optional. Whether to keep the page name. Default false.
346
 * @param bool        $sample    Optional. Whether it should be treated as a sample permalink.
347
 *                               Default false.
348
 * @return string The page permalink.
349
 */
350
function _get_page_link( $post = false, $leavename = false, $sample = false ) {
351
	global $wp_rewrite;
352
353
	$post = get_post( $post );
354
355
	$draft_or_pending = in_array( $post->post_status, array( 'draft', 'pending', 'auto-draft' ) );
356
357
	$link = $wp_rewrite->get_page_permastruct();
358
359
	if ( !empty($link) && ( ( isset($post->post_status) && !$draft_or_pending ) || $sample ) ) {
360
		if ( ! $leavename ) {
361
			$link = str_replace('%pagename%', get_page_uri( $post ), $link);
362
		}
363
364
		$link = home_url($link);
365
		$link = user_trailingslashit($link, 'page');
366
	} else {
367
		$link = home_url( '?page_id=' . $post->ID );
368
	}
369
370
	/**
371
	 * Filters the permalink for a non-page_on_front page.
372
	 *
373
	 * @since 2.1.0
374
	 *
375
	 * @param string $link    The page's permalink.
376
	 * @param int    $post_id The ID of the page.
377
	 */
378
	return apply_filters( '_get_page_link', $link, $post->ID );
379
}
380
381
/**
382
 * Retrieves the permalink for an attachment.
383
 *
384
 * This can be used in the WordPress Loop or outside of it.
385
 *
386
 * @since 2.0.0
387
 *
388
 * @global WP_Rewrite $wp_rewrite
389
 *
390
 * @param int|object $post      Optional. Post ID or object. Default uses the global `$post`.
391
 * @param bool       $leavename Optional. Whether to keep the page name. Default false.
392
 * @return string The attachment permalink.
393
 */
394
function get_attachment_link( $post = null, $leavename = false ) {
395
	global $wp_rewrite;
396
397
	$link = false;
398
399
	$post = get_post( $post );
400
	$parent = ( $post->post_parent > 0 && $post->post_parent != $post->ID ) ? get_post( $post->post_parent ) : false;
401
	if ( $parent && ! in_array( $parent->post_type, get_post_types() ) ) {
402
		$parent = false;
403
	}
404
405
	if ( $wp_rewrite->using_permalinks() && $parent ) {
406
		if ( 'page' == $parent->post_type )
407
			$parentlink = _get_page_link( $post->post_parent ); // Ignores page_on_front
408
		else
409
			$parentlink = get_permalink( $post->post_parent );
410
411
		if ( is_numeric($post->post_name) || false !== strpos(get_option('permalink_structure'), '%category%') )
412
			$name = 'attachment/' . $post->post_name; // <permalink>/<int>/ is paged so we use the explicit attachment marker
413
		else
414
			$name = $post->post_name;
415
416
		if ( strpos($parentlink, '?') === false )
417
			$link = user_trailingslashit( trailingslashit($parentlink) . '%postname%' );
0 ignored issues
show
It seems like $parentlink defined by get_permalink($post->post_parent) on line 409 can also be of type false; however, trailingslashit() does only seem to accept string, did you maybe forget to handle an error condition?

This check looks for type mismatches where the missing type is false. This is usually indicative of an error condtion.

Consider the follow example

<?php

function getDate($date)
{
    if ($date !== null) {
        return new DateTime($date);
    }

    return false;
}

This function either returns a new DateTime object or false, if there was an error. This is a typical pattern in PHP programming to show that an error has occurred without raising an exception. The calling code should check for this returned false before passing on the value to another function or method that may not be able to handle a false.

Loading history...
418
419
		if ( ! $leavename )
420
			$link = str_replace( '%postname%', $name, $link );
421
	} elseif ( $wp_rewrite->using_permalinks() && ! $leavename ) {
422
		$link = home_url( user_trailingslashit( $post->post_name ) );
423
	}
424
425
	if ( ! $link )
0 ignored issues
show
Bug Best Practice introduced by
The expression $link of type string|false 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...
426
		$link = home_url( '/?attachment_id=' . $post->ID );
427
428
	/**
429
	 * Filters the permalink for an attachment.
430
	 *
431
	 * @since 2.0.0
432
	 *
433
	 * @param string $link    The attachment's permalink.
434
	 * @param int    $post_id Attachment ID.
435
	 */
436
	return apply_filters( 'attachment_link', $link, $post->ID );
437
}
438
439
/**
440
 * Retrieves the permalink for the year archives.
441
 *
442
 * @since 1.5.0
443
 *
444
 * @global WP_Rewrite $wp_rewrite
445
 *
446
 * @param int|bool $year False for current year or year for permalink.
447
 * @return string The permalink for the specified year archive.
448
 */
449
function get_year_link( $year ) {
450
	global $wp_rewrite;
451
	if ( !$year )
452
		$year = gmdate('Y', current_time('timestamp'));
453
	$yearlink = $wp_rewrite->get_year_permastruct();
454
	if ( !empty($yearlink) ) {
455
		$yearlink = str_replace('%year%', $year, $yearlink);
456
		$yearlink = home_url( user_trailingslashit( $yearlink, 'year' ) );
457
	} else {
458
		$yearlink = home_url( '?m=' . $year );
459
	}
460
461
	/**
462
	 * Filters the year archive permalink.
463
	 *
464
	 * @since 1.5.0
465
	 *
466
	 * @param string $yearlink Permalink for the year archive.
467
	 * @param int    $year     Year for the archive.
468
	 */
469
	return apply_filters( 'year_link', $yearlink, $year );
470
}
471
472
/**
473
 * Retrieves the permalink for the month archives with year.
474
 *
475
 * @since 1.0.0
476
 *
477
 * @global WP_Rewrite $wp_rewrite
478
 *
479
 * @param bool|int $year  False for current year. Integer of year.
480
 * @param bool|int $month False for current month. Integer of month.
481
 * @return string The permalink for the specified month and year archive.
482
 */
483
function get_month_link($year, $month) {
484
	global $wp_rewrite;
485
	if ( !$year )
486
		$year = gmdate('Y', current_time('timestamp'));
487
	if ( !$month )
488
		$month = gmdate('m', current_time('timestamp'));
489
	$monthlink = $wp_rewrite->get_month_permastruct();
490 View Code Duplication
	if ( !empty($monthlink) ) {
491
		$monthlink = str_replace('%year%', $year, $monthlink);
492
		$monthlink = str_replace('%monthnum%', zeroise(intval($month), 2), $monthlink);
493
		$monthlink = home_url( user_trailingslashit( $monthlink, 'month' ) );
494
	} else {
495
		$monthlink = home_url( '?m=' . $year . zeroise( $month, 2 ) );
496
	}
497
498
	/**
499
	 * Filters the month archive permalink.
500
	 *
501
	 * @since 1.5.0
502
	 *
503
	 * @param string $monthlink Permalink for the month archive.
504
	 * @param int    $year      Year for the archive.
505
	 * @param int    $month     The month for the archive.
506
	 */
507
	return apply_filters( 'month_link', $monthlink, $year, $month );
508
}
509
510
/**
511
 * Retrieves the permalink for the day archives with year and month.
512
 *
513
 * @since 1.0.0
514
 *
515
 * @global WP_Rewrite $wp_rewrite
516
 *
517
 * @param bool|int $year  False for current year. Integer of year.
518
 * @param bool|int $month False for current month. Integer of month.
519
 * @param bool|int $day   False for current day. Integer of day.
520
 * @return string The permalink for the specified day, month, and year archive.
521
 */
522
function get_day_link($year, $month, $day) {
523
	global $wp_rewrite;
524
	if ( !$year )
525
		$year = gmdate('Y', current_time('timestamp'));
526
	if ( !$month )
527
		$month = gmdate('m', current_time('timestamp'));
528
	if ( !$day )
529
		$day = gmdate('j', current_time('timestamp'));
530
531
	$daylink = $wp_rewrite->get_day_permastruct();
532
	if ( !empty($daylink) ) {
533
		$daylink = str_replace('%year%', $year, $daylink);
534
		$daylink = str_replace('%monthnum%', zeroise(intval($month), 2), $daylink);
535
		$daylink = str_replace('%day%', zeroise(intval($day), 2), $daylink);
536
		$daylink = home_url( user_trailingslashit( $daylink, 'day' ) );
537
	} else {
538
		$daylink = home_url( '?m=' . $year . zeroise( $month, 2 ) . zeroise( $day, 2 ) );
539
	}
540
541
	/**
542
	 * Filters the day archive permalink.
543
	 *
544
	 * @since 1.5.0
545
	 *
546
	 * @param string $daylink Permalink for the day archive.
547
	 * @param int    $year    Year for the archive.
548
	 * @param int    $month   Month for the archive.
549
	 * @param int    $day     The day for the archive.
550
	 */
551
	return apply_filters( 'day_link', $daylink, $year, $month, $day );
552
}
553
554
/**
555
 * Displays the permalink for the feed type.
556
 *
557
 * @since 3.0.0
558
 *
559
 * @param string $anchor The link's anchor text.
560
 * @param string $feed   Optional. Feed type. Default empty.
561
 */
562
function the_feed_link( $anchor, $feed = '' ) {
563
	$link = '<a href="' . esc_url( get_feed_link( $feed ) ) . '">' . $anchor . '</a>';
564
565
	/**
566
	 * Filters the feed link anchor tag.
567
	 *
568
	 * @since 3.0.0
569
	 *
570
	 * @param string $link The complete anchor tag for a feed link.
571
	 * @param string $feed The feed type, or an empty string for the
572
	 *                     default feed type.
573
	 */
574
	echo apply_filters( 'the_feed_link', $link, $feed );
575
}
576
577
/**
578
 * Retrieves the permalink for the feed type.
579
 *
580
 * @since 1.5.0
581
 *
582
 * @global WP_Rewrite $wp_rewrite
583
 *
584
 * @param string $feed Optional. Feed type. Default empty.
585
 * @return string The feed permalink.
586
 */
587
function get_feed_link( $feed = '' ) {
588
	global $wp_rewrite;
589
590
	$permalink = $wp_rewrite->get_feed_permastruct();
591
	if ( '' != $permalink ) {
592
		if ( false !== strpos($feed, 'comments_') ) {
593
			$feed = str_replace('comments_', '', $feed);
594
			$permalink = $wp_rewrite->get_comment_feed_permastruct();
595
		}
596
597
		if ( get_default_feed() == $feed )
598
			$feed = '';
599
600
		$permalink = str_replace('%feed%', $feed, $permalink);
601
		$permalink = preg_replace('#/+#', '/', "/$permalink");
602
		$output =  home_url( user_trailingslashit($permalink, 'feed') );
603
	} else {
604
		if ( empty($feed) )
605
			$feed = get_default_feed();
606
607
		if ( false !== strpos($feed, 'comments_') )
608
			$feed = str_replace('comments_', 'comments-', $feed);
609
610
		$output = home_url("?feed={$feed}");
611
	}
612
613
	/**
614
	 * Filters the feed type permalink.
615
	 *
616
	 * @since 1.5.0
617
	 *
618
	 * @param string $output The feed permalink.
619
	 * @param string $feed   Feed type.
620
	 */
621
	return apply_filters( 'feed_link', $output, $feed );
622
}
623
624
/**
625
 * Retrieves the permalink for the post comments feed.
626
 *
627
 * @since 2.2.0
628
 *
629
 * @param int    $post_id Optional. Post ID. Default is the ID of the global `$post`.
630
 * @param string $feed    Optional. Feed type. Default empty.
631
 * @return string The permalink for the comments feed for the given post.
632
 */
633
function get_post_comments_feed_link( $post_id = 0, $feed = '' ) {
634
	$post_id = absint( $post_id );
635
636
	if ( ! $post_id )
637
		$post_id = get_the_ID();
638
639
	if ( empty( $feed ) )
640
		$feed = get_default_feed();
641
642
	$post = get_post( $post_id );
0 ignored issues
show
It seems like $post_id can also be of type double or false; however, get_post() does only seem to accept integer|object<WP_Post>|null, 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...
643
	$unattached = 'attachment' === $post->post_type && 0 === (int) $post->post_parent;
644
645
	if ( '' != get_option('permalink_structure') ) {
646 View Code Duplication
		if ( 'page' == get_option('show_on_front') && $post_id == get_option('page_on_front') )
647
			$url = _get_page_link( $post_id );
648
		else
649
			$url = get_permalink($post_id);
0 ignored issues
show
It seems like $post_id can also be of type double or false; however, get_permalink() does only seem to accept integer, 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...
650
651
		if ( $unattached ) {
652
			$url =  home_url( '/feed/' );
653
			if ( $feed !== get_default_feed() ) {
654
				$url .= "$feed/";
655
			}
656
			$url = add_query_arg( 'attachment_id', $post_id, $url );
657
		} else {
658
			$url = trailingslashit($url) . 'feed';
0 ignored issues
show
It seems like $url can also be of type false; however, trailingslashit() does only seem to accept string, did you maybe forget to handle an error condition?
Loading history...
659
			if ( $feed != get_default_feed() )
660
				$url .= "/$feed";
661
			$url = user_trailingslashit($url, 'single_feed');
662
		}
663
	} else {
664
		if ( $unattached ) {
665
			$url = add_query_arg( array( 'feed' => $feed, 'attachment_id' => $post_id ), home_url( '/' ) );
666
		} elseif ( 'page' == $post->post_type ) {
667
			$url = add_query_arg( array( 'feed' => $feed, 'page_id' => $post_id ), home_url( '/' ) );
668
		} else {
669
			$url = add_query_arg( array( 'feed' => $feed, 'p' => $post_id ), home_url( '/' ) );
670
		}
671
	}
672
673
	/**
674
	 * Filters the post comments feed permalink.
675
	 *
676
	 * @since 1.5.1
677
	 *
678
	 * @param string $url Post comments feed permalink.
679
	 */
680
	return apply_filters( 'post_comments_feed_link', $url );
681
}
682
683
/**
684
 * Displays the comment feed link for a post.
685
 *
686
 * Prints out the comment feed link for a post. Link text is placed in the
687
 * anchor. If no link text is specified, default text is used. If no post ID is
688
 * specified, the current post is used.
689
 *
690
 * @since 2.5.0
691
 *
692
 * @param string $link_text Optional. Descriptive link text. Default 'Comments Feed'.
693
 * @param int    $post_id   Optional. Post ID. Default is the ID of the global `$post`.
694
 * @param string $feed      Optional. Feed format. Default empty.
695
 */
696
function post_comments_feed_link( $link_text = '', $post_id = '', $feed = '' ) {
697
	$url = get_post_comments_feed_link( $post_id, $feed );
698
	if ( empty( $link_text ) ) {
699
		$link_text = __('Comments Feed');
700
	}
701
702
	$link = '<a href="' . esc_url( $url ) . '">' . $link_text . '</a>';
703
	/**
704
	 * Filters the post comment feed link anchor tag.
705
	 *
706
	 * @since 2.8.0
707
	 *
708
	 * @param string $link    The complete anchor tag for the comment feed link.
709
	 * @param int    $post_id Post ID.
710
	 * @param string $feed    The feed type, or an empty string for the default feed type.
711
	 */
712
	echo apply_filters( 'post_comments_feed_link_html', $link, $post_id, $feed );
713
}
714
715
/**
716
 * Retrieves the feed link for a given author.
717
 *
718
 * Returns a link to the feed for all posts by a given author. A specific feed
719
 * can be requested or left blank to get the default feed.
720
 *
721
 * @since 2.5.0
722
 *
723
 * @param int    $author_id Author ID.
724
 * @param string $feed      Optional. Feed type. Default empty.
725
 * @return string Link to the feed for the author specified by $author_id.
726
 */
727
function get_author_feed_link( $author_id, $feed = '' ) {
728
	$author_id = (int) $author_id;
729
	$permalink_structure = get_option('permalink_structure');
730
731
	if ( empty($feed) )
732
		$feed = get_default_feed();
733
734
	if ( '' == $permalink_structure ) {
735
		$link = home_url("?feed=$feed&amp;author=" . $author_id);
736
	} else {
737
		$link = get_author_posts_url($author_id);
738
		if ( $feed == get_default_feed() )
739
			$feed_link = 'feed';
740
		else
741
			$feed_link = "feed/$feed";
742
743
		$link = trailingslashit($link) . user_trailingslashit($feed_link, 'feed');
744
	}
745
746
	/**
747
	 * Filters the feed link for a given author.
748
	 *
749
	 * @since 1.5.1
750
	 *
751
	 * @param string $link The author feed link.
752
	 * @param string $feed Feed type.
753
	 */
754
	$link = apply_filters( 'author_feed_link', $link, $feed );
755
756
	return $link;
757
}
758
759
/**
760
 * Retrieves the feed link for a category.
761
 *
762
 * Returns a link to the feed for all posts in a given category. A specific feed
763
 * can be requested or left blank to get the default feed.
764
 *
765
 * @since 2.5.0
766
 *
767
 * @param int    $cat_id Category ID.
768
 * @param string $feed   Optional. Feed type. Default empty.
769
 * @return string Link to the feed for the category specified by $cat_id.
0 ignored issues
show
Should the return type not be string|false?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
770
 */
771
function get_category_feed_link( $cat_id, $feed = '' ) {
772
	return get_term_feed_link( $cat_id, 'category', $feed );
773
}
774
775
/**
776
 * Retrieves the feed link for a term.
777
 *
778
 * Returns a link to the feed for all posts in a given term. A specific feed
779
 * can be requested or left blank to get the default feed.
780
 *
781
 * @since 3.0.0
782
 *
783
 * @param int    $term_id  Term ID.
784
 * @param string $taxonomy Optional. Taxonomy of `$term_id`. Default 'category'.
785
 * @param string $feed     Optional. Feed type. Default empty.
786
 * @return string|false Link to the feed for the term specified by $term_id and $taxonomy.
787
 */
788
function get_term_feed_link( $term_id, $taxonomy = 'category', $feed = '' ) {
789
	$term_id = ( int ) $term_id;
790
791
	$term = get_term( $term_id, $taxonomy  );
792
793
	if ( empty( $term ) || is_wp_error( $term ) )
794
		return false;
795
796
	if ( empty( $feed ) )
797
		$feed = get_default_feed();
798
799
	$permalink_structure = get_option( 'permalink_structure' );
800
801
	if ( '' == $permalink_structure ) {
802
		if ( 'category' == $taxonomy ) {
803
			$link = home_url("?feed=$feed&amp;cat=$term_id");
804
		}
805
		elseif ( 'post_tag' == $taxonomy ) {
806
			$link = home_url("?feed=$feed&amp;tag=$term->slug");
807
		} else {
808
			$t = get_taxonomy( $taxonomy );
809
			$link = home_url("?feed=$feed&amp;$t->query_var=$term->slug");
810
		}
811
	} else {
812
		$link = get_term_link( $term_id, $term->taxonomy );
813
		if ( $feed == get_default_feed() )
814
			$feed_link = 'feed';
815
		else
816
			$feed_link = "feed/$feed";
817
818
		$link = trailingslashit( $link ) . user_trailingslashit( $feed_link, 'feed' );
0 ignored issues
show
It seems like $link can also be of type object<WP_Error>; however, trailingslashit() does only seem to accept string, 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...
819
	}
820
821
	if ( 'category' == $taxonomy ) {
822
		/**
823
		 * Filters the category feed link.
824
		 *
825
		 * @since 1.5.1
826
		 *
827
		 * @param string $link The category feed link.
828
		 * @param string $feed Feed type.
829
		 */
830
		$link = apply_filters( 'category_feed_link', $link, $feed );
831
	} elseif ( 'post_tag' == $taxonomy ) {
832
		/**
833
		 * Filters the post tag feed link.
834
		 *
835
		 * @since 2.3.0
836
		 *
837
		 * @param string $link The tag feed link.
838
		 * @param string $feed Feed type.
839
		 */
840
		$link = apply_filters( 'tag_feed_link', $link, $feed );
841
	} else {
842
		/**
843
		 * Filters the feed link for a taxonomy other than 'category' or 'post_tag'.
844
		 *
845
		 * @since 3.0.0
846
		 *
847
		 * @param string $link The taxonomy feed link.
848
		 * @param string $feed Feed type.
849
		 * @param string $taxonomy The taxonomy name.
850
		 */
851
		$link = apply_filters( 'taxonomy_feed_link', $link, $feed, $taxonomy );
852
	}
853
854
	return $link;
855
}
856
857
/**
858
 * Retrieves the permalink for a tag feed.
859
 *
860
 * @since 2.3.0
861
 *
862
 * @param int    $tag_id Tag ID.
863
 * @param string $feed   Optional. Feed type. Default empty.
864
 * @return string The feed permalink for the given tag.
0 ignored issues
show
Should the return type not be string|false?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
865
 */
866
function get_tag_feed_link( $tag_id, $feed = '' ) {
867
	return get_term_feed_link( $tag_id, 'post_tag', $feed );
868
}
869
870
/**
871
 * Retrieves the edit link for a tag.
872
 *
873
 * @since 2.7.0
874
 *
875
 * @param int    $tag_id   Tag ID.
876
 * @param string $taxonomy Optional. Taxonomy slug. Default 'post_tag'.
877
 * @return string The edit tag link URL for the given tag.
878
 */
879
function get_edit_tag_link( $tag_id, $taxonomy = 'post_tag' ) {
880
	/**
881
	 * Filters the edit link for a tag (or term in another taxonomy).
882
	 *
883
	 * @since 2.7.0
884
	 *
885
	 * @param string $link The term edit link.
886
	 */
887
	return apply_filters( 'get_edit_tag_link', get_edit_term_link( $tag_id, $taxonomy ) );
888
}
889
890
/**
891
 * Displays or retrieves the edit link for a tag with formatting.
892
 *
893
 * @since 2.7.0
894
 *
895
 * @param string  $link   Optional. Anchor text. Default empty.
896
 * @param string  $before Optional. Display before edit link. Default empty.
897
 * @param string  $after  Optional. Display after edit link. Default empty.
898
 * @param WP_Term $tag    Optional. Term object. If null, the queried object will be inspected.
899
 *                        Default null.
900
 */
901
function edit_tag_link( $link = '', $before = '', $after = '', $tag = null ) {
902
	$link = edit_term_link( $link, '', '', $tag, false );
903
904
	/**
905
	 * Filters the anchor tag for the edit link for a tag (or term in another taxonomy).
906
	 *
907
	 * @since 2.7.0
908
	 *
909
	 * @param string $link The anchor tag for the edit link.
910
	 */
911
	echo $before . apply_filters( 'edit_tag_link', $link ) . $after;
912
}
913
914
/**
915
 * Retrieves the URL for editing a given term.
916
 *
917
 * @since 3.1.0
918
 * @since 4.5.0 The `$taxonomy` argument was made optional.
919
 *
920
 * @param int    $term_id     Term ID.
921
 * @param string $taxonomy    Optional. Taxonomy. Defaults to the taxonomy of the term identified
922
 *                            by `$term_id`.
923
 * @param string $object_type Optional. The object type. Used to highlight the proper post type
924
 *                            menu on the linked page. Defaults to the first object_type associated
925
 *                            with the taxonomy.
926
 * @return string|null The edit term link URL for the given term, or null on failure.
927
 */
928
function get_edit_term_link( $term_id, $taxonomy = '', $object_type = '' ) {
929
	$term = get_term( $term_id, $taxonomy );
930
	if ( ! $term || is_wp_error( $term ) ) {
931
		return;
932
	}
933
934
	$tax = get_taxonomy( $term->taxonomy );
935
	if ( ! $tax || ! current_user_can( 'edit_term', $term->term_id ) ) {
936
		return;
937
	}
938
939
	$args = array(
940
		'taxonomy' => $taxonomy,
941
		'tag_ID'   => $term->term_id,
942
	);
943
944
	if ( $object_type ) {
945
		$args['post_type'] = $object_type;
946
	} elseif ( ! empty( $tax->object_type ) ) {
947
		$args['post_type'] = reset( $tax->object_type );
948
	}
949
950
	if ( $tax->show_ui ) {
951
		$location = add_query_arg( $args, admin_url( 'term.php' ) );
952
	} else {
953
		$location = '';
954
	}
955
956
	/**
957
	 * Filters the edit link for a term.
958
	 *
959
	 * @since 3.1.0
960
	 *
961
	 * @param string $location    The edit link.
962
	 * @param int    $term_id     Term ID.
963
	 * @param string $taxonomy    Taxonomy name.
964
	 * @param string $object_type The object type (eg. the post type).
965
	 */
966
	return apply_filters( 'get_edit_term_link', $location, $term_id, $taxonomy, $object_type );
967
}
968
969
/**
970
 * Displays or retrieves the edit term link with formatting.
971
 *
972
 * @since 3.1.0
973
 *
974
 * @param string $link   Optional. Anchor text. Default empty.
975
 * @param string $before Optional. Display before edit link. Default empty.
976
 * @param string $after  Optional. Display after edit link. Default empty.
977
 * @param object $term   Optional. Term object. If null, the queried object will be inspected. Default null.
978
 * @param bool   $echo   Optional. Whether or not to echo the return. Default true.
979
 * @return string|void HTML content.
980
 */
981
function edit_term_link( $link = '', $before = '', $after = '', $term = null, $echo = true ) {
982
	if ( is_null( $term ) )
983
		$term = get_queried_object();
984
985
	if ( ! $term )
986
		return;
987
988
	$tax = get_taxonomy( $term->taxonomy );
989
	if ( ! current_user_can( 'edit_term', $term->term_id ) ) {
990
		return;
991
	}
992
993
	if ( empty( $link ) )
994
		$link = __('Edit This');
995
996
	$link = '<a href="' . get_edit_term_link( $term->term_id, $term->taxonomy ) . '">' . $link . '</a>';
997
998
	/**
999
	 * Filters the anchor tag for the edit link of a term.
1000
	 *
1001
	 * @since 3.1.0
1002
	 *
1003
	 * @param string $link    The anchor tag for the edit link.
1004
	 * @param int    $term_id Term ID.
1005
	 */
1006
	$link = $before . apply_filters( 'edit_term_link', $link, $term->term_id ) . $after;
1007
1008
	if ( $echo )
1009
		echo $link;
1010
	else
1011
		return $link;
1012
}
1013
1014
/**
1015
 * Retrieves the permalink for a search.
1016
 *
1017
 * @since  3.0.0
1018
 *
1019
 * @global WP_Rewrite $wp_rewrite
1020
 *
1021
 * @param string $query Optional. The query string to use. If empty the current query is used. Default empty.
1022
 * @return string The search permalink.
1023
 */
1024
function get_search_link( $query = '' ) {
1025
	global $wp_rewrite;
1026
1027
	if ( empty($query) )
1028
		$search = get_search_query( false );
1029
	else
1030
		$search = stripslashes($query);
1031
1032
	$permastruct = $wp_rewrite->get_search_permastruct();
1033
1034 View Code Duplication
	if ( empty( $permastruct ) ) {
1035
		$link = home_url('?s=' . urlencode($search) );
1036
	} else {
1037
		$search = urlencode($search);
1038
		$search = str_replace('%2F', '/', $search); // %2F(/) is not valid within a URL, send it un-encoded.
1039
		$link = str_replace( '%search%', $search, $permastruct );
1040
		$link = home_url( user_trailingslashit( $link, 'search' ) );
1041
	}
1042
1043
	/**
1044
	 * Filters the search permalink.
1045
	 *
1046
	 * @since 3.0.0
1047
	 *
1048
	 * @param string $link   Search permalink.
1049
	 * @param string $search The URL-encoded search term.
1050
	 */
1051
	return apply_filters( 'search_link', $link, $search );
1052
}
1053
1054
/**
1055
 * Retrieves the permalink for the search results feed.
1056
 *
1057
 * @since 2.5.0
1058
 *
1059
 * @global WP_Rewrite $wp_rewrite
1060
 *
1061
 * @param string $search_query Optional. Search query. Default empty.
1062
 * @param string $feed         Optional. Feed type. Default empty.
1063
 * @return string The search results feed permalink.
1064
 */
1065 View Code Duplication
function get_search_feed_link($search_query = '', $feed = '') {
0 ignored issues
show
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1066
	global $wp_rewrite;
1067
	$link = get_search_link($search_query);
1068
1069
	if ( empty($feed) )
1070
		$feed = get_default_feed();
1071
1072
	$permastruct = $wp_rewrite->get_search_permastruct();
1073
1074
	if ( empty($permastruct) ) {
1075
		$link = add_query_arg('feed', $feed, $link);
1076
	} else {
1077
		$link = trailingslashit($link);
1078
		$link .= "feed/$feed/";
1079
	}
1080
1081
	/**
1082
	 * Filters the search feed link.
1083
	 *
1084
	 * @since 2.5.0
1085
	 *
1086
	 * @param string $link Search feed link.
1087
	 * @param string $feed Feed type.
1088
	 * @param string $type The search type. One of 'posts' or 'comments'.
1089
	 */
1090
	return apply_filters( 'search_feed_link', $link, $feed, 'posts' );
1091
}
1092
1093
/**
1094
 * Retrieves the permalink for the search results comments feed.
1095
 *
1096
 * @since 2.5.0
1097
 *
1098
 * @global WP_Rewrite $wp_rewrite
1099
 *
1100
 * @param string $search_query Optional. Search query. Default empty.
1101
 * @param string $feed         Optional. Feed type. Default empty.
1102
 * @return string The comments feed search results permalink.
1103
 */
1104 View Code Duplication
function get_search_comments_feed_link($search_query = '', $feed = '') {
0 ignored issues
show
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1105
	global $wp_rewrite;
1106
1107
	if ( empty($feed) )
1108
		$feed = get_default_feed();
1109
1110
	$link = get_search_feed_link($search_query, $feed);
1111
1112
	$permastruct = $wp_rewrite->get_search_permastruct();
1113
1114
	if ( empty($permastruct) )
1115
		$link = add_query_arg('feed', 'comments-' . $feed, $link);
1116
	else
1117
		$link = add_query_arg('withcomments', 1, $link);
1118
1119
	/** This filter is documented in wp-includes/link-template.php */
1120
	return apply_filters( 'search_feed_link', $link, $feed, 'comments' );
1121
}
1122
1123
/**
1124
 * Retrieves the permalink for a post type archive.
1125
 *
1126
 * @since 3.1.0
1127
 * @since 4.5.0 Support for posts was added.
1128
 *
1129
 * @global WP_Rewrite $wp_rewrite
1130
 *
1131
 * @param string $post_type Post type.
1132
 * @return string|false The post type archive permalink.
1133
 */
1134
function get_post_type_archive_link( $post_type ) {
1135
	global $wp_rewrite;
1136
	if ( ! $post_type_obj = get_post_type_object( $post_type ) )
1137
		return false;
1138
1139
	if ( 'post' === $post_type ) {
1140
		$show_on_front = get_option( 'show_on_front' );
1141
		$page_for_posts  = get_option( 'page_for_posts' );
1142
1143
		if ( 'page' == $show_on_front && $page_for_posts ) {
1144
			$link = get_permalink( $page_for_posts );
1145
		} else {
1146
			$link = get_home_url();
1147
		}
1148
		/** This filter is documented in wp-includes/link-template.php */
1149
		return apply_filters( 'post_type_archive_link', $link, $post_type );
1150
	}
1151
1152
	if ( ! $post_type_obj->has_archive )
1153
		return false;
1154
1155
	if ( get_option( 'permalink_structure' ) && is_array( $post_type_obj->rewrite ) ) {
1156
		$struct = ( true === $post_type_obj->has_archive ) ? $post_type_obj->rewrite['slug'] : $post_type_obj->has_archive;
1157
		if ( $post_type_obj->rewrite['with_front'] )
1158
			$struct = $wp_rewrite->front . $struct;
1159
		else
1160
			$struct = $wp_rewrite->root . $struct;
1161
		$link = home_url( user_trailingslashit( $struct, 'post_type_archive' ) );
1162
	} else {
1163
		$link = home_url( '?post_type=' . $post_type );
1164
	}
1165
1166
	/**
1167
	 * Filters the post type archive permalink.
1168
	 *
1169
	 * @since 3.1.0
1170
	 *
1171
	 * @param string $link      The post type archive permalink.
1172
	 * @param string $post_type Post type name.
1173
	 */
1174
	return apply_filters( 'post_type_archive_link', $link, $post_type );
1175
}
1176
1177
/**
1178
 * Retrieves the permalink for a post type archive feed.
1179
 *
1180
 * @since 3.1.0
1181
 *
1182
 * @param string $post_type Post type
1183
 * @param string $feed      Optional. Feed type. Default empty.
1184
 * @return string|false The post type feed permalink.
1185
 */
1186
function get_post_type_archive_feed_link( $post_type, $feed = '' ) {
1187
	$default_feed = get_default_feed();
1188
	if ( empty( $feed ) )
1189
		$feed = $default_feed;
1190
1191
	if ( ! $link = get_post_type_archive_link( $post_type ) )
1192
		return false;
1193
1194
	$post_type_obj = get_post_type_object( $post_type );
1195
	if ( get_option( 'permalink_structure' ) && is_array( $post_type_obj->rewrite ) && $post_type_obj->rewrite['feeds'] ) {
1196
		$link = trailingslashit( $link );
1197
		$link .= 'feed/';
1198
		if ( $feed != $default_feed )
1199
			$link .= "$feed/";
1200
	} else {
1201
		$link = add_query_arg( 'feed', $feed, $link );
1202
	}
1203
1204
	/**
1205
	 * Filters the post type archive feed link.
1206
	 *
1207
	 * @since 3.1.0
1208
	 *
1209
	 * @param string $link The post type archive feed link.
1210
	 * @param string $feed Feed type.
1211
	 */
1212
	return apply_filters( 'post_type_archive_feed_link', $link, $feed );
1213
}
1214
1215
/**
1216
 * Retrieves the URL used for the post preview.
1217
 *
1218
 * Allows additional query args to be appended.
1219
 *
1220
 * @since 4.4.0
1221
 *
1222
 * @param int|WP_Post $post         Optional. Post ID or `WP_Post` object. Defaults to global `$post`.
1223
 * @param array       $query_args   Optional. Array of additional query args to be appended to the link.
1224
 *                                  Default empty array.
1225
 * @param string      $preview_link Optional. Base preview link to be used if it should differ from the
1226
 *                                  post permalink. Default empty.
1227
 * @return string|null URL used for the post preview, or null if the post does not exist.
1228
 */
1229
function get_preview_post_link( $post = null, $query_args = array(), $preview_link = '' ) {
1230
	$post = get_post( $post );
1231
	if ( ! $post ) {
1232
		return;
1233
	}
1234
1235
	$post_type_object = get_post_type_object( $post->post_type );
1236
	if ( is_post_type_viewable( $post_type_object ) ) {
0 ignored issues
show
It seems like $post_type_object defined by get_post_type_object($post->post_type) on line 1235 can be null; however, is_post_type_viewable() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
1237
		if ( ! $preview_link ) {
1238
			$preview_link = set_url_scheme( get_permalink( $post ) );
0 ignored issues
show
It seems like get_permalink($post) targeting get_permalink() can also be of type false; however, set_url_scheme() does only seem to accept string, did you maybe forget to handle an error condition?
Loading history...
1239
		}
1240
1241
		$query_args['preview'] = 'true';
1242
		$preview_link = add_query_arg( $query_args, $preview_link );
1243
	}
1244
1245
	/**
1246
	 * Filters the URL used for a post preview.
1247
	 *
1248
	 * @since 2.0.5
1249
	 * @since 4.0.0 Added the `$post` parameter.
1250
	 *
1251
	 * @param string  $preview_link URL used for the post preview.
1252
	 * @param WP_Post $post         Post object.
1253
	 */
1254
	return apply_filters( 'preview_post_link', $preview_link, $post );
1255
}
1256
1257
/**
1258
 * Retrieves the edit post link for post.
1259
 *
1260
 * Can be used within the WordPress loop or outside of it. Can be used with
1261
 * pages, posts, attachments, and revisions.
1262
 *
1263
 * @since 2.3.0
1264
 *
1265
 * @param int    $id      Optional. Post ID. Default is the ID of the global `$post`.
1266
 * @param string $context Optional. How to output the '&' character. Default '&amp;'.
1267
 * @return string|null The edit post link for the given post. null if the post type is invalid or does
1268
 *                     not allow an editing UI.
1269
 */
1270
function get_edit_post_link( $id = 0, $context = 'display' ) {
1271
	if ( ! $post = get_post( $id ) )
1272
		return;
1273
1274
	if ( 'revision' === $post->post_type )
1275
		$action = '';
1276
	elseif ( 'display' == $context )
1277
		$action = '&amp;action=edit';
1278
	else
1279
		$action = '&action=edit';
1280
1281
	$post_type_object = get_post_type_object( $post->post_type );
1282
	if ( !$post_type_object )
1283
		return;
1284
1285
	if ( !current_user_can( 'edit_post', $post->ID ) )
1286
		return;
1287
1288
	if ( $post_type_object->_edit_link ) {
1289
		$link = admin_url( sprintf( $post_type_object->_edit_link . $action, $post->ID ) );
1290
	} else {
1291
		$link = '';
1292
	}
1293
1294
	/**
1295
	 * Filters the post edit link.
1296
	 *
1297
	 * @since 2.3.0
1298
	 *
1299
	 * @param string $link    The edit link.
1300
	 * @param int    $post_id Post ID.
1301
	 * @param string $context The link context. If set to 'display' then ampersands
1302
	 *                        are encoded.
1303
	 */
1304
	return apply_filters( 'get_edit_post_link', $link, $post->ID, $context );
1305
}
1306
1307
/**
1308
 * Displays the edit post link for post.
1309
 *
1310
 * @since 1.0.0
1311
 * @since 4.4.0 The `$class` argument was added.
1312
 *
1313
 * @param string $text   Optional. Anchor text. If null, default is 'Edit This'. Default null.
1314
 * @param string $before Optional. Display before edit link. Default empty.
1315
 * @param string $after  Optional. Display after edit link. Default empty.
1316
 * @param int    $id     Optional. Post ID. Default is the ID of the global `$post`.
1317
 * @param string $class  Optional. Add custom class to link. Default 'post-edit-link'.
1318
 */
1319
function edit_post_link( $text = null, $before = '', $after = '', $id = 0, $class = 'post-edit-link' ) {
1320
	if ( ! $post = get_post( $id ) ) {
1321
		return;
1322
	}
1323
1324
	if ( ! $url = get_edit_post_link( $post->ID ) ) {
1325
		return;
1326
	}
1327
1328
	if ( null === $text ) {
1329
		$text = __( 'Edit This' );
1330
	}
1331
1332
	$link = '<a class="' . esc_attr( $class ) . '" href="' . esc_url( $url ) . '">' . $text . '</a>';
1333
1334
	/**
1335
	 * Filters the post edit link anchor tag.
1336
	 *
1337
	 * @since 2.3.0
1338
	 *
1339
	 * @param string $link    Anchor tag for the edit link.
1340
	 * @param int    $post_id Post ID.
1341
	 * @param string $text    Anchor text.
1342
	 */
1343
	echo $before . apply_filters( 'edit_post_link', $link, $post->ID, $text ) . $after;
1344
}
1345
1346
/**
1347
 * Retrieves the delete posts link for post.
1348
 *
1349
 * Can be used within the WordPress loop or outside of it, with any post type.
1350
 *
1351
 * @since 2.9.0
1352
 *
1353
 * @param int    $id           Optional. Post ID. Default is the ID of the global `$post`.
1354
 * @param string $deprecated   Not used.
1355
 * @param bool   $force_delete Optional. Whether to bypass trash and force deletion. Default false.
1356
 * @return string|void The delete post link URL for the given post.
1357
 */
1358
function get_delete_post_link( $id = 0, $deprecated = '', $force_delete = false ) {
1359
	if ( ! empty( $deprecated ) )
1360
		_deprecated_argument( __FUNCTION__, '3.0.0' );
1361
1362
	if ( !$post = get_post( $id ) )
1363
		return;
1364
1365
	$post_type_object = get_post_type_object( $post->post_type );
1366
	if ( !$post_type_object )
1367
		return;
1368
1369
	if ( !current_user_can( 'delete_post', $post->ID ) )
1370
		return;
1371
1372
	$action = ( $force_delete || !EMPTY_TRASH_DAYS ) ? 'delete' : 'trash';
1373
1374
	$delete_link = add_query_arg( 'action', $action, admin_url( sprintf( $post_type_object->_edit_link, $post->ID ) ) );
1375
1376
	/**
1377
	 * Filters the post delete link.
1378
	 *
1379
	 * @since 2.9.0
1380
	 *
1381
	 * @param string $link         The delete link.
1382
	 * @param int    $post_id      Post ID.
1383
	 * @param bool   $force_delete Whether to bypass the trash and force deletion. Default false.
1384
	 */
1385
	return apply_filters( 'get_delete_post_link', wp_nonce_url( $delete_link, "$action-post_{$post->ID}" ), $post->ID, $force_delete );
1386
}
1387
1388
/**
1389
 * Retrieves the edit comment link.
1390
 *
1391
 * @since 2.3.0
1392
 *
1393
 * @param int|WP_Comment $comment_id Optional. Comment ID or WP_Comment object.
0 ignored issues
show
Consider making the type for parameter $comment_id a bit more specific; maybe use integer.
Loading history...
1394
 * @return string|void The edit comment link URL for the given comment.
1395
 */
1396
function get_edit_comment_link( $comment_id = 0 ) {
1397
	$comment = get_comment( $comment_id );
1398
1399
	if ( !current_user_can( 'edit_comment', $comment->comment_ID ) )
1400
		return;
1401
1402
	$location = admin_url('comment.php?action=editcomment&amp;c=') . $comment->comment_ID;
1403
1404
	/**
1405
	 * Filters the comment edit link.
1406
	 *
1407
	 * @since 2.3.0
1408
	 *
1409
	 * @param string $location The edit link.
1410
	 */
1411
	return apply_filters( 'get_edit_comment_link', $location );
1412
}
1413
1414
/**
1415
 * Displays the edit comment link with formatting.
1416
 *
1417
 * @since 1.0.0
1418
 *
1419
 * @param string $text   Optional. Anchor text. If null, default is 'Edit This'. Default null.
1420
 * @param string $before Optional. Display before edit link. Default empty.
1421
 * @param string $after  Optional. Display after edit link. Default empty.
1422
 */
1423 View Code Duplication
function edit_comment_link( $text = null, $before = '', $after = '' ) {
0 ignored issues
show
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1424
	$comment = get_comment();
1425
1426
	if ( ! current_user_can( 'edit_comment', $comment->comment_ID ) ) {
1427
		return;
1428
	}
1429
1430
	if ( null === $text ) {
1431
		$text = __( 'Edit This' );
1432
	}
1433
1434
	$link = '<a class="comment-edit-link" href="' . esc_url( get_edit_comment_link( $comment ) ) . '">' . $text . '</a>';
1435
1436
	/**
1437
	 * Filters the comment edit link anchor tag.
1438
	 *
1439
	 * @since 2.3.0
1440
	 *
1441
	 * @param string $link       Anchor tag for the edit link.
1442
	 * @param int    $comment_id Comment ID.
1443
	 * @param string $text       Anchor text.
1444
	 */
1445
	echo $before . apply_filters( 'edit_comment_link', $link, $comment->comment_ID, $text ) . $after;
1446
}
1447
1448
/**
1449
 * Displays the edit bookmark link.
1450
 *
1451
 * @since 2.7.0
1452
 *
1453
 * @param int|stdClass $link Optional. Bookmark ID. Default is the id of the current bookmark.
0 ignored issues
show
Consider making the type for parameter $link a bit more specific; maybe use integer.
Loading history...
1454
 * @return string|void The edit bookmark link URL.
1455
 */
1456
function get_edit_bookmark_link( $link = 0 ) {
1457
	$link = get_bookmark( $link );
1458
1459
	if ( !current_user_can('manage_links') )
1460
		return;
1461
1462
	$location = admin_url('link.php?action=edit&amp;link_id=') . $link->link_id;
1463
1464
	/**
1465
	 * Filters the bookmark edit link.
1466
	 *
1467
	 * @since 2.7.0
1468
	 *
1469
	 * @param string $location The edit link.
1470
	 * @param int    $link_id  Bookmark ID.
1471
	 */
1472
	return apply_filters( 'get_edit_bookmark_link', $location, $link->link_id );
1473
}
1474
1475
/**
1476
 * Displays the edit bookmark link anchor content.
1477
 *
1478
 * @since 2.7.0
1479
 *
1480
 * @param string $link     Optional. Anchor text. Default empty.
1481
 * @param string $before   Optional. Display before edit link. Default empty.
1482
 * @param string $after    Optional. Display after edit link. Default empty.
1483
 * @param int    $bookmark Optional. Bookmark ID. Default is the current bookmark.
1484
 */
1485 View Code Duplication
function edit_bookmark_link( $link = '', $before = '', $after = '', $bookmark = null ) {
0 ignored issues
show
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1486
	$bookmark = get_bookmark($bookmark);
1487
1488
	if ( !current_user_can('manage_links') )
1489
		return;
1490
1491
	if ( empty($link) )
1492
		$link = __('Edit This');
1493
1494
	$link = '<a href="' . esc_url( get_edit_bookmark_link( $bookmark ) ) . '">' . $link . '</a>';
1495
1496
	/**
1497
	 * Filters the bookmark edit link anchor tag.
1498
	 *
1499
	 * @since 2.7.0
1500
	 *
1501
	 * @param string $link    Anchor tag for the edit link.
1502
	 * @param int    $link_id Bookmark ID.
1503
	 */
1504
	echo $before . apply_filters( 'edit_bookmark_link', $link, $bookmark->link_id ) . $after;
1505
}
1506
1507
/**
1508
 * Retrieves the edit user link.
1509
 *
1510
 * @since 3.5.0
1511
 *
1512
 * @param int $user_id Optional. User ID. Defaults to the current user.
1513
 * @return string URL to edit user page or empty string.
1514
 */
1515
function get_edit_user_link( $user_id = null ) {
1516
	if ( ! $user_id )
0 ignored issues
show
Bug Best Practice introduced by
The expression $user_id of type integer|null is loosely compared to false; this is ambiguous if the integer can be zero. You might want to explicitly use === null instead.

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

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
1517
		$user_id = get_current_user_id();
1518
1519
	if ( empty( $user_id ) || ! current_user_can( 'edit_user', $user_id ) )
1520
		return '';
1521
1522
	$user = get_userdata( $user_id );
1523
1524
	if ( ! $user )
1525
		return '';
1526
1527
	if ( get_current_user_id() == $user->ID )
1528
		$link = get_edit_profile_url( $user->ID );
1529
	else
1530
		$link = add_query_arg( 'user_id', $user->ID, self_admin_url( 'user-edit.php' ) );
1531
1532
	/**
1533
	 * Filters the user edit link.
1534
	 *
1535
	 * @since 3.5.0
1536
	 *
1537
	 * @param string $link    The edit link.
1538
	 * @param int    $user_id User ID.
1539
	 */
1540
	return apply_filters( 'get_edit_user_link', $link, $user->ID );
1541
}
1542
1543
// Navigation links
1544
1545
/**
1546
 * Retrieves the previous post that is adjacent to the current post.
1547
 *
1548
 * @since 1.5.0
1549
 *
1550
 * @param bool         $in_same_term   Optional. Whether post should be in a same taxonomy term. Default false.
1551
 * @param array|string $excluded_terms Optional. Array or comma-separated list of excluded term IDs. Default empty.
1552
 * @param string       $taxonomy       Optional. Taxonomy, if $in_same_term is true. Default 'category'.
1553
 * @return null|string|WP_Post Post object if successful. Null if global $post is not set. Empty string if no
1554
 *                             corresponding post exists.
1555
 */
1556
function get_previous_post( $in_same_term = false, $excluded_terms = '', $taxonomy = 'category' ) {
1557
	return get_adjacent_post( $in_same_term, $excluded_terms, true, $taxonomy );
1558
}
1559
1560
/**
1561
 * Retrieves the next post that is adjacent to the current post.
1562
 *
1563
 * @since 1.5.0
1564
 *
1565
 * @param bool         $in_same_term   Optional. Whether post should be in a same taxonomy term. Default false.
1566
 * @param array|string $excluded_terms Optional. Array or comma-separated list of excluded term IDs. Default empty.
1567
 * @param string       $taxonomy       Optional. Taxonomy, if $in_same_term is true. Default 'category'.
1568
 * @return null|string|WP_Post Post object if successful. Null if global $post is not set. Empty string if no
1569
 *                             corresponding post exists.
1570
 */
1571
function get_next_post( $in_same_term = false, $excluded_terms = '', $taxonomy = 'category' ) {
1572
	return get_adjacent_post( $in_same_term, $excluded_terms, false, $taxonomy );
1573
}
1574
1575
/**
1576
 * Retrieves the adjacent post.
1577
 *
1578
 * Can either be next or previous post.
1579
 *
1580
 * @since 2.5.0
1581
 *
1582
 * @global wpdb $wpdb WordPress database abstraction object.
1583
 *
1584
 * @param bool         $in_same_term   Optional. Whether post should be in a same taxonomy term. Default false.
1585
 * @param array|string $excluded_terms Optional. Array or comma-separated list of excluded term IDs. Default empty.
1586
 * @param bool         $previous       Optional. Whether to retrieve previous post. Default true
1587
 * @param string       $taxonomy       Optional. Taxonomy, if $in_same_term is true. Default 'category'.
1588
 * @return null|string|WP_Post Post object if successful. Null if global $post is not set. Empty string if no
1589
 *                             corresponding post exists.
1590
 */
1591
function get_adjacent_post( $in_same_term = false, $excluded_terms = '', $previous = true, $taxonomy = 'category' ) {
1592
	global $wpdb;
1593
1594
	if ( ( ! $post = get_post() ) || ! taxonomy_exists( $taxonomy ) )
1595
		return null;
1596
1597
	$current_post_date = $post->post_date;
1598
1599
	$join = '';
1600
	$where = '';
1601
	$adjacent = $previous ? 'previous' : 'next';
1602
1603
	if ( $in_same_term || ! empty( $excluded_terms ) ) {
1604
		if ( ! empty( $excluded_terms ) && ! is_array( $excluded_terms ) ) {
1605
			// back-compat, $excluded_terms used to be $excluded_terms with IDs separated by " and "
1606
			if ( false !== strpos( $excluded_terms, ' and ' ) ) {
1607
				_deprecated_argument( __FUNCTION__, '3.3.0', sprintf( __( 'Use commas instead of %s to separate excluded terms.' ), "'and'" ) );
1608
				$excluded_terms = explode( ' and ', $excluded_terms );
1609
			} else {
1610
				$excluded_terms = explode( ',', $excluded_terms );
1611
			}
1612
1613
			$excluded_terms = array_map( 'intval', $excluded_terms );
1614
		}
1615
1616
		if ( $in_same_term ) {
1617
			$join .= " INNER JOIN $wpdb->term_relationships AS tr ON p.ID = tr.object_id INNER JOIN $wpdb->term_taxonomy tt ON tr.term_taxonomy_id = tt.term_taxonomy_id";
1618
			$where .= $wpdb->prepare( "AND tt.taxonomy = %s", $taxonomy );
1619
1620
			if ( ! is_object_in_taxonomy( $post->post_type, $taxonomy ) )
1621
				return '';
1622
			$term_array = wp_get_object_terms( $post->ID, $taxonomy, array( 'fields' => 'ids' ) );
1623
1624
			// Remove any exclusions from the term array to include.
1625
			$term_array = array_diff( $term_array, (array) $excluded_terms );
1626
			$term_array = array_map( 'intval', $term_array );
1627
1628
			if ( ! $term_array || is_wp_error( $term_array ) )
0 ignored issues
show
Bug Best Practice introduced by
The expression $term_array 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...
1629
				return '';
1630
1631
			$where .= " AND tt.term_id IN (" . implode( ',', $term_array ) . ")";
1632
		}
1633
1634
		/**
1635
		 * Filters the IDs of terms excluded from adjacent post queries.
1636
		 *
1637
		 * The dynamic portion of the hook name, `$adjacent`, refers to the type
1638
		 * of adjacency, 'next' or 'previous'.
1639
		 *
1640
		 * @since 4.4.0
1641
		 *
1642
		 * @param string $excluded_terms Array of excluded term IDs.
1643
		 */
1644
		$excluded_terms = apply_filters( "get_{$adjacent}_post_excluded_terms", $excluded_terms );
1645
1646
		if ( ! empty( $excluded_terms ) ) {
1647
			$where .= " AND p.ID NOT IN ( SELECT tr.object_id FROM $wpdb->term_relationships tr LEFT JOIN $wpdb->term_taxonomy tt ON (tr.term_taxonomy_id = tt.term_taxonomy_id) WHERE tt.term_id IN (" . implode( ',', array_map( 'intval', $excluded_terms ) ) . ') )';
1648
		}
1649
	}
1650
1651
	// 'post_status' clause depends on the current user.
1652
	if ( is_user_logged_in() ) {
1653
		$user_id = get_current_user_id();
1654
1655
		$post_type_object = get_post_type_object( $post->post_type );
1656
		if ( empty( $post_type_object ) ) {
1657
			$post_type_cap    = $post->post_type;
1658
			$read_private_cap = 'read_private_' . $post_type_cap . 's';
1659
		} else {
1660
			$read_private_cap = $post_type_object->cap->read_private_posts;
1661
		}
1662
1663
		/*
1664
		 * Results should include private posts belonging to the current user, or private posts where the
1665
		 * current user has the 'read_private_posts' cap.
1666
		 */
1667
		$private_states = get_post_stati( array( 'private' => true ) );
1668
		$where .= " AND ( p.post_status = 'publish'";
1669
		foreach ( (array) $private_states as $state ) {
1670
			if ( current_user_can( $read_private_cap ) ) {
1671
				$where .= $wpdb->prepare( " OR p.post_status = %s", $state );
1672
			} else {
1673
				$where .= $wpdb->prepare( " OR (p.post_author = %d AND p.post_status = %s)", $user_id, $state );
1674
			}
1675
		}
1676
		$where .= " )";
1677
	} else {
1678
		$where .= " AND p.post_status = 'publish'";
1679
	}
1680
1681
	$op = $previous ? '<' : '>';
1682
	$order = $previous ? 'DESC' : 'ASC';
1683
1684
	/**
1685
	 * Filters the JOIN clause in the SQL for an adjacent post query.
1686
	 *
1687
	 * The dynamic portion of the hook name, `$adjacent`, refers to the type
1688
	 * of adjacency, 'next' or 'previous'.
1689
	 *
1690
	 * @since 2.5.0
1691
	 * @since 4.4.0 Added the `$taxonomy` and `$post` parameters.
1692
	 *
1693
	 * @param string  $join           The JOIN clause in the SQL.
1694
	 * @param bool    $in_same_term   Whether post should be in a same taxonomy term.
1695
	 * @param array   $excluded_terms Array of excluded term IDs.
1696
	 * @param string  $taxonomy       Taxonomy. Used to identify the term used when `$in_same_term` is true.
1697
	 * @param WP_Post $post           WP_Post object.
1698
	 */
1699
	$join = apply_filters( "get_{$adjacent}_post_join", $join, $in_same_term, $excluded_terms, $taxonomy, $post );
1700
1701
	/**
1702
	 * Filters the WHERE clause in the SQL for an adjacent post query.
1703
	 *
1704
	 * The dynamic portion of the hook name, `$adjacent`, refers to the type
1705
	 * of adjacency, 'next' or 'previous'.
1706
	 *
1707
	 * @since 2.5.0
1708
	 * @since 4.4.0 Added the `$taxonomy` and `$post` parameters.
1709
	 *
1710
	 * @param string $where          The `WHERE` clause in the SQL.
1711
	 * @param bool   $in_same_term   Whether post should be in a same taxonomy term.
1712
	 * @param array  $excluded_terms Array of excluded term IDs.
1713
	 * @param string $taxonomy       Taxonomy. Used to identify the term used when `$in_same_term` is true.
1714
	 * @param WP_Post $post           WP_Post object.
1715
	 */
1716
	$where = apply_filters( "get_{$adjacent}_post_where", $wpdb->prepare( "WHERE p.post_date $op %s AND p.post_type = %s $where", $current_post_date, $post->post_type ), $in_same_term, $excluded_terms, $taxonomy, $post );
1717
1718
	/**
1719
	 * Filters the ORDER BY clause in the SQL for an adjacent post query.
1720
	 *
1721
	 * The dynamic portion of the hook name, `$adjacent`, refers to the type
1722
	 * of adjacency, 'next' or 'previous'.
1723
	 *
1724
	 * @since 2.5.0
1725
	 * @since 4.4.0 Added the `$post` parameter.
1726
	 *
1727
	 * @param string $order_by The `ORDER BY` clause in the SQL.
1728
	 * @param WP_Post $post    WP_Post object.
1729
	 */
1730
	$sort  = apply_filters( "get_{$adjacent}_post_sort", "ORDER BY p.post_date $order LIMIT 1", $post );
1731
1732
	$query = "SELECT p.ID FROM $wpdb->posts AS p $join $where $sort";
1733
	$query_key = 'adjacent_post_' . md5( $query );
1734
	$result = wp_cache_get( $query_key, 'counts' );
1735
	if ( false !== $result ) {
1736
		if ( $result )
1737
			$result = get_post( $result );
1738
		return $result;
1739
	}
1740
1741
	$result = $wpdb->get_var( $query );
1742
	if ( null === $result )
1743
		$result = '';
1744
1745
	wp_cache_set( $query_key, $result, 'counts' );
1746
1747
	if ( $result )
1748
		$result = get_post( $result );
1749
1750
	return $result;
1751
}
1752
1753
/**
1754
 * Retrieves the adjacent post relational link.
1755
 *
1756
 * Can either be next or previous post relational link.
1757
 *
1758
 * @since 2.8.0
1759
 *
1760
 * @param string       $title          Optional. Link title format. Default '%title'.
1761
 * @param bool         $in_same_term   Optional. Whether link should be in a same taxonomy term. Default false.
1762
 * @param array|string $excluded_terms Optional. Array or comma-separated list of excluded term IDs. Default empty.
1763
 * @param bool         $previous       Optional. Whether to display link to previous or next post. Default true.
1764
 * @param string       $taxonomy       Optional. Taxonomy, if $in_same_term is true. Default 'category'.
1765
 * @return string|void The adjacent post relational link URL.
1766
 */
1767
function get_adjacent_post_rel_link( $title = '%title', $in_same_term = false, $excluded_terms = '', $previous = true, $taxonomy = 'category' ) {
1768 View Code Duplication
	if ( $previous && is_attachment() && $post = get_post() )
1769
		$post = get_post( $post->post_parent );
1770
	else
1771
		$post = get_adjacent_post( $in_same_term, $excluded_terms, $previous, $taxonomy );
1772
1773
	if ( empty( $post ) )
1774
		return;
1775
1776
	$post_title = the_title_attribute( array( 'echo' => false, 'post' => $post ) );
1777
1778
	if ( empty( $post_title ) )
1779
		$post_title = $previous ? __( 'Previous Post' ) : __( 'Next Post' );
1780
1781
	$date = mysql2date( get_option( 'date_format' ), $post->post_date );
1782
1783
	$title = str_replace( '%title', $post_title, $title );
1784
	$title = str_replace( '%date', $date, $title );
1785
1786
	$link = $previous ? "<link rel='prev' title='" : "<link rel='next' title='";
1787
	$link .= esc_attr( $title );
1788
	$link .= "' href='" . get_permalink( $post ) . "' />\n";
1789
1790
	$adjacent = $previous ? 'previous' : 'next';
1791
1792
	/**
1793
	 * Filters the adjacent post relational link.
1794
	 *
1795
	 * The dynamic portion of the hook name, `$adjacent`, refers to the type
1796
	 * of adjacency, 'next' or 'previous'.
1797
	 *
1798
	 * @since 2.8.0
1799
	 *
1800
	 * @param string $link The relational link.
1801
	 */
1802
	return apply_filters( "{$adjacent}_post_rel_link", $link );
1803
}
1804
1805
/**
1806
 * Displays the relational links for the posts adjacent to the current post.
1807
 *
1808
 * @since 2.8.0
1809
 *
1810
 * @param string       $title          Optional. Link title format. Default '%title'.
1811
 * @param bool         $in_same_term   Optional. Whether link should be in a same taxonomy term. Default false.
1812
 * @param array|string $excluded_terms Optional. Array or comma-separated list of excluded term IDs. Default empty.
1813
 * @param string       $taxonomy       Optional. Taxonomy, if $in_same_term is true. Default 'category'.
1814
 */
1815
function adjacent_posts_rel_link( $title = '%title', $in_same_term = false, $excluded_terms = '', $taxonomy = 'category' ) {
1816
	echo get_adjacent_post_rel_link( $title, $in_same_term, $excluded_terms, true, $taxonomy );
1817
	echo get_adjacent_post_rel_link( $title, $in_same_term, $excluded_terms, false, $taxonomy );
1818
}
1819
1820
/**
1821
 * Displays relational links for the posts adjacent to the current post for single post pages.
1822
 *
1823
 * This is meant to be attached to actions like 'wp_head'. Do not call this directly in plugins
1824
 * or theme templates.
1825
 *
1826
 * @since 3.0.0
1827
 *
1828
 * @see adjacent_posts_rel_link()
1829
 */
1830
function adjacent_posts_rel_link_wp_head() {
1831
	if ( ! is_single() || is_attachment() ) {
1832
		return;
1833
	}
1834
	adjacent_posts_rel_link();
1835
}
1836
1837
/**
1838
 * Displays the relational link for the next post adjacent to the current post.
1839
 *
1840
 * @since 2.8.0
1841
 *
1842
 * @see get_adjacent_post_rel_link()
1843
 *
1844
 * @param string       $title          Optional. Link title format. Default '%title'.
1845
 * @param bool         $in_same_term   Optional. Whether link should be in a same taxonomy term. Default false.
1846
 * @param array|string $excluded_terms Optional. Array or comma-separated list of excluded term IDs. Default empty.
1847
 * @param string       $taxonomy       Optional. Taxonomy, if $in_same_term is true. Default 'category'.
1848
 */
1849
function next_post_rel_link( $title = '%title', $in_same_term = false, $excluded_terms = '', $taxonomy = 'category' ) {
1850
	echo get_adjacent_post_rel_link( $title, $in_same_term, $excluded_terms, false, $taxonomy );
1851
}
1852
1853
/**
1854
 * Displays the relational link for the previous post adjacent to the current post.
1855
 *
1856
 * @since 2.8.0
1857
 *
1858
 * @see get_adjacent_post_rel_link()
1859
 *
1860
 * @param string       $title          Optional. Link title format. Default '%title'.
1861
 * @param bool         $in_same_term   Optional. Whether link should be in a same taxonomy term. Default false.
1862
 * @param array|string $excluded_terms Optional. Array or comma-separated list of excluded term IDs. Default true.
1863
 * @param string       $taxonomy       Optional. Taxonomy, if $in_same_term is true. Default 'category'.
1864
 */
1865
function prev_post_rel_link( $title = '%title', $in_same_term = false, $excluded_terms = '', $taxonomy = 'category' ) {
1866
	echo get_adjacent_post_rel_link( $title, $in_same_term, $excluded_terms, true, $taxonomy );
1867
}
1868
1869
/**
1870
 * Retrieves the boundary post.
1871
 *
1872
 * Boundary being either the first or last post by publish date within the constraints specified
1873
 * by $in_same_term or $excluded_terms.
1874
 *
1875
 * @since 2.8.0
1876
 *
1877
 * @param bool         $in_same_term   Optional. Whether returned post should be in a same taxonomy term.
1878
 *                                     Default false.
1879
 * @param array|string $excluded_terms Optional. Array or comma-separated list of excluded term IDs.
1880
 *                                     Default empty.
1881
 * @param bool         $start          Optional. Whether to retrieve first or last post. Default true
1882
 * @param string       $taxonomy       Optional. Taxonomy, if $in_same_term is true. Default 'category'.
1883
 * @return null|array Array containing the boundary post object if successful, null otherwise.
1884
 */
1885
function get_boundary_post( $in_same_term = false, $excluded_terms = '', $start = true, $taxonomy = 'category' ) {
1886
	$post = get_post();
1887
	if ( ! $post || ! is_single() || is_attachment() || ! taxonomy_exists( $taxonomy ) )
1888
		return null;
1889
1890
	$query_args = array(
1891
		'posts_per_page' => 1,
1892
		'order' => $start ? 'ASC' : 'DESC',
1893
		'update_post_term_cache' => false,
1894
		'update_post_meta_cache' => false
1895
	);
1896
1897
	$term_array = array();
1898
1899
	if ( ! is_array( $excluded_terms ) ) {
1900
		if ( ! empty( $excluded_terms ) )
1901
			$excluded_terms = explode( ',', $excluded_terms );
1902
		else
1903
			$excluded_terms = array();
1904
	}
1905
1906
	if ( $in_same_term || ! empty( $excluded_terms ) ) {
1907
		if ( $in_same_term )
1908
			$term_array = wp_get_object_terms( $post->ID, $taxonomy, array( 'fields' => 'ids' ) );
1909
1910
		if ( ! empty( $excluded_terms ) ) {
1911
			$excluded_terms = array_map( 'intval', $excluded_terms );
1912
			$excluded_terms = array_diff( $excluded_terms, $term_array );
1913
1914
			$inverse_terms = array();
1915
			foreach ( $excluded_terms as $excluded_term )
1916
				$inverse_terms[] = $excluded_term * -1;
1917
			$excluded_terms = $inverse_terms;
1918
		}
1919
1920
		$query_args[ 'tax_query' ] = array( array(
1921
			'taxonomy' => $taxonomy,
1922
			'terms' => array_merge( $term_array, $excluded_terms )
1923
		) );
1924
	}
1925
1926
	return get_posts( $query_args );
1927
}
1928
1929
/**
1930
 * Retrieves the previous post link that is adjacent to the current post.
1931
 *
1932
 * @since 3.7.0
1933
 *
1934
 * @param string       $format         Optional. Link anchor format. Default '&laquo; %link'.
1935
 * @param string       $link           Optional. Link permalink format. Default '%title%'.
1936
 * @param bool         $in_same_term   Optional. Whether link should be in a same taxonomy term. Default false.
1937
 * @param array|string $excluded_terms Optional. Array or comma-separated list of excluded term IDs. Default empty.
1938
 * @param string       $taxonomy       Optional. Taxonomy, if $in_same_term is true. Default 'category'.
1939
 * @return string The link URL of the previous post in relation to the current post.
1940
 */
1941
function get_previous_post_link( $format = '&laquo; %link', $link = '%title', $in_same_term = false, $excluded_terms = '', $taxonomy = 'category' ) {
1942
	return get_adjacent_post_link( $format, $link, $in_same_term, $excluded_terms, true, $taxonomy );
1943
}
1944
1945
/**
1946
 * Displays the previous post link that is adjacent to the current post.
1947
 *
1948
 * @since 1.5.0
1949
 *
1950
 * @see get_previous_post_link()
1951
 *
1952
 * @param string       $format         Optional. Link anchor format. Default '&laquo; %link'.
1953
 * @param string       $link           Optional. Link permalink format. Default '%title'.
1954
 * @param bool         $in_same_term   Optional. Whether link should be in a same taxonomy term. Default false.
1955
 * @param array|string $excluded_terms Optional. Array or comma-separated list of excluded term IDs. Default empty.
1956
 * @param string       $taxonomy       Optional. Taxonomy, if $in_same_term is true. Default 'category'.
1957
 */
1958
function previous_post_link( $format = '&laquo; %link', $link = '%title', $in_same_term = false, $excluded_terms = '', $taxonomy = 'category' ) {
1959
	echo get_previous_post_link( $format, $link, $in_same_term, $excluded_terms, $taxonomy );
1960
}
1961
1962
/**
1963
 * Retrieves the next post link that is adjacent to the current post.
1964
 *
1965
 * @since 3.7.0
1966
 *
1967
 * @param string       $format         Optional. Link anchor format. Default '&laquo; %link'.
1968
 * @param string       $link           Optional. Link permalink format. Default '%title'.
1969
 * @param bool         $in_same_term   Optional. Whether link should be in a same taxonomy term. Default false.
1970
 * @param array|string $excluded_terms Optional. Array or comma-separated list of excluded term IDs. Default empty.
1971
 * @param string       $taxonomy       Optional. Taxonomy, if $in_same_term is true. Default 'category'.
1972
 * @return string The link URL of the next post in relation to the current post.
1973
 */
1974
function get_next_post_link( $format = '%link &raquo;', $link = '%title', $in_same_term = false, $excluded_terms = '', $taxonomy = 'category' ) {
1975
	return get_adjacent_post_link( $format, $link, $in_same_term, $excluded_terms, false, $taxonomy );
1976
}
1977
1978
/**
1979
 * Displays the next post link that is adjacent to the current post.
1980
 *
1981
 * @since 1.5.0
1982
 * @see get_next_post_link()
1983
 *
1984
 * @param string       $format         Optional. Link anchor format. Default '&laquo; %link'.
1985
 * @param string       $link           Optional. Link permalink format. Default '%title'
1986
 * @param bool         $in_same_term   Optional. Whether link should be in a same taxonomy term. Default false.
1987
 * @param array|string $excluded_terms Optional. Array or comma-separated list of excluded term IDs. Default empty.
1988
 * @param string       $taxonomy       Optional. Taxonomy, if $in_same_term is true. Default 'category'.
1989
 */
1990
function next_post_link( $format = '%link &raquo;', $link = '%title', $in_same_term = false, $excluded_terms = '', $taxonomy = 'category' ) {
1991
	 echo get_next_post_link( $format, $link, $in_same_term, $excluded_terms, $taxonomy );
1992
}
1993
1994
/**
1995
 * Retrieves the adjacent post link.
1996
 *
1997
 * Can be either next post link or previous.
1998
 *
1999
 * @since 3.7.0
2000
 *
2001
 * @param string       $format         Link anchor format.
2002
 * @param string       $link           Link permalink format.
2003
 * @param bool         $in_same_term   Optional. Whether link should be in a same taxonomy term. Default false.
2004
 * @param array|string $excluded_terms Optional. Array or comma-separated list of excluded terms IDs. Default empty.
2005
 * @param bool         $previous       Optional. Whether to display link to previous or next post. Default true.
2006
 * @param string       $taxonomy       Optional. Taxonomy, if $in_same_term is true. Default 'category'.
2007
 * @return string The link URL of the previous or next post in relation to the current post.
2008
 */
2009
function get_adjacent_post_link( $format, $link, $in_same_term = false, $excluded_terms = '', $previous = true, $taxonomy = 'category' ) {
2010 View Code Duplication
	if ( $previous && is_attachment() )
2011
		$post = get_post( get_post()->post_parent );
2012
	else
2013
		$post = get_adjacent_post( $in_same_term, $excluded_terms, $previous, $taxonomy );
2014
2015
	if ( ! $post ) {
2016
		$output = '';
2017
	} else {
2018
		$title = $post->post_title;
2019
2020
		if ( empty( $post->post_title ) )
2021
			$title = $previous ? __( 'Previous Post' ) : __( 'Next Post' );
2022
2023
		/** This filter is documented in wp-includes/post-template.php */
2024
		$title = apply_filters( 'the_title', $title, $post->ID );
2025
2026
		$date = mysql2date( get_option( 'date_format' ), $post->post_date );
2027
		$rel = $previous ? 'prev' : 'next';
2028
2029
		$string = '<a href="' . get_permalink( $post ) . '" rel="'.$rel.'">';
2030
		$inlink = str_replace( '%title', $title, $link );
2031
		$inlink = str_replace( '%date', $date, $inlink );
2032
		$inlink = $string . $inlink . '</a>';
2033
2034
		$output = str_replace( '%link', $inlink, $format );
2035
	}
2036
2037
	$adjacent = $previous ? 'previous' : 'next';
2038
2039
	/**
2040
	 * Filters the adjacent post link.
2041
	 *
2042
	 * The dynamic portion of the hook name, `$adjacent`, refers to the type
2043
	 * of adjacency, 'next' or 'previous'.
2044
	 *
2045
	 * @since 2.6.0
2046
	 * @since 4.2.0 Added the `$adjacent` parameter.
2047
	 *
2048
	 * @param string  $output   The adjacent post link.
2049
	 * @param string  $format   Link anchor format.
2050
	 * @param string  $link     Link permalink format.
2051
	 * @param WP_Post $post     The adjacent post.
2052
	 * @param string  $adjacent Whether the post is previous or next.
2053
	 */
2054
	return apply_filters( "{$adjacent}_post_link", $output, $format, $link, $post, $adjacent );
2055
}
2056
2057
/**
2058
 * Displays the adjacent post link.
2059
 *
2060
 * Can be either next post link or previous.
2061
 *
2062
 * @since 2.5.0
2063
 *
2064
 * @param string       $format         Link anchor format.
2065
 * @param string       $link           Link permalink format.
2066
 * @param bool         $in_same_term   Optional. Whether link should be in a same taxonomy term. Default false.
2067
 * @param array|string $excluded_terms Optional. Array or comma-separated list of excluded category IDs. Default empty.
2068
 * @param bool         $previous       Optional. Whether to display link to previous or next post. Default true.
2069
 * @param string       $taxonomy       Optional. Taxonomy, if $in_same_term is true. Default 'category'.
2070
 */
2071
function adjacent_post_link( $format, $link, $in_same_term = false, $excluded_terms = '', $previous = true, $taxonomy = 'category' ) {
2072
	echo get_adjacent_post_link( $format, $link, $in_same_term, $excluded_terms, $previous, $taxonomy );
2073
}
2074
2075
/**
2076
 * Retrieves the link for a page number.
2077
 *
2078
 * @since 1.5.0
2079
 *
2080
 * @global WP_Rewrite $wp_rewrite
2081
 *
2082
 * @param int  $pagenum Optional. Page ID. Default 1.
2083
 * @param bool $escape  Optional. Whether to escape the URL for display, with esc_url(). Defaults to true.
2084
 * 	                    Otherwise, prepares the URL with esc_url_raw().
2085
 * @return string The link URL for the given page number.
2086
 */
2087
function get_pagenum_link($pagenum = 1, $escape = true ) {
2088
	global $wp_rewrite;
2089
2090
	$pagenum = (int) $pagenum;
2091
2092
	$request = remove_query_arg( 'paged' );
2093
2094
	$home_root = parse_url(home_url());
2095
	$home_root = ( isset($home_root['path']) ) ? $home_root['path'] : '';
2096
	$home_root = preg_quote( $home_root, '|' );
2097
2098
	$request = preg_replace('|^'. $home_root . '|i', '', $request);
2099
	$request = preg_replace('|^/+|', '', $request);
2100
2101
	if ( !$wp_rewrite->using_permalinks() || is_admin() ) {
2102
		$base = trailingslashit( get_bloginfo( 'url' ) );
2103
2104
		if ( $pagenum > 1 ) {
2105
			$result = add_query_arg( 'paged', $pagenum, $base . $request );
2106
		} else {
2107
			$result = $base . $request;
2108
		}
2109
	} else {
2110
		$qs_regex = '|\?.*?$|';
2111
		preg_match( $qs_regex, $request, $qs_match );
2112
2113
		if ( !empty( $qs_match[0] ) ) {
2114
			$query_string = $qs_match[0];
2115
			$request = preg_replace( $qs_regex, '', $request );
2116
		} else {
2117
			$query_string = '';
2118
		}
2119
2120
		$request = preg_replace( "|$wp_rewrite->pagination_base/\d+/?$|", '', $request);
2121
		$request = preg_replace( '|^' . preg_quote( $wp_rewrite->index, '|' ) . '|i', '', $request);
2122
		$request = ltrim($request, '/');
2123
2124
		$base = trailingslashit( get_bloginfo( 'url' ) );
2125
2126
		if ( $wp_rewrite->using_index_permalinks() && ( $pagenum > 1 || '' != $request ) )
2127
			$base .= $wp_rewrite->index . '/';
2128
2129
		if ( $pagenum > 1 ) {
2130
			$request = ( ( !empty( $request ) ) ? trailingslashit( $request ) : $request ) . user_trailingslashit( $wp_rewrite->pagination_base . "/" . $pagenum, 'paged' );
2131
		}
2132
2133
		$result = $base . $request . $query_string;
2134
	}
2135
2136
	/**
2137
	 * Filters the page number link for the current request.
2138
	 *
2139
	 * @since 2.5.0
2140
	 *
2141
	 * @param string $result The page number link.
2142
	 */
2143
	$result = apply_filters( 'get_pagenum_link', $result );
2144
2145
	if ( $escape )
2146
		return esc_url( $result );
2147
	else
2148
		return esc_url_raw( $result );
2149
}
2150
2151
/**
2152
 * Retrieves the next posts page link.
2153
 *
2154
 * Backported from 2.1.3 to 2.0.10.
2155
 *
2156
 * @since 2.0.10
2157
 *
2158
 * @global int $paged
2159
 *
2160
 * @param int $max_page Optional. Max pages. Default 0.
2161
 * @return string|void The link URL for next posts page.
2162
 */
2163
function get_next_posts_page_link($max_page = 0) {
2164
	global $paged;
2165
2166
	if ( !is_single() ) {
2167
		if ( !$paged )
2168
			$paged = 1;
2169
		$nextpage = intval($paged) + 1;
2170
		if ( !$max_page || $max_page >= $nextpage )
2171
			return get_pagenum_link($nextpage);
2172
	}
2173
}
2174
2175
/**
2176
 * Displays or retrieves the next posts page link.
2177
 *
2178
 * @since 0.71
2179
 *
2180
 * @param int   $max_page Optional. Max pages. Default 0.
2181
 * @param bool  $echo     Optional. Whether to echo the link. Default true.
2182
 * @return string|void The link URL for next posts page if `$echo = false`.
2183
 */
2184
function next_posts( $max_page = 0, $echo = true ) {
2185
	$output = esc_url( get_next_posts_page_link( $max_page ) );
2186
2187
	if ( $echo )
2188
		echo $output;
2189
	else
2190
		return $output;
2191
}
2192
2193
/**
2194
 * Retrieves the next posts page link.
2195
 *
2196
 * @since 2.7.0
2197
 *
2198
 * @global int      $paged
2199
 * @global WP_Query $wp_query
2200
 *
2201
 * @param string $label    Content for link text.
2202
 * @param int    $max_page Optional. Max pages. Default 0.
2203
 * @return string|void HTML-formatted next posts page link.
2204
 */
2205
function get_next_posts_link( $label = null, $max_page = 0 ) {
2206
	global $paged, $wp_query;
2207
2208
	if ( !$max_page )
2209
		$max_page = $wp_query->max_num_pages;
2210
2211
	if ( !$paged )
2212
		$paged = 1;
2213
2214
	$nextpage = intval($paged) + 1;
2215
2216
	if ( null === $label )
2217
		$label = __( 'Next Page &raquo;' );
2218
2219 View Code Duplication
	if ( !is_single() && ( $nextpage <= $max_page ) ) {
2220
		/**
2221
		 * Filters the anchor tag attributes for the next posts page link.
2222
		 *
2223
		 * @since 2.7.0
2224
		 *
2225
		 * @param string $attributes Attributes for the anchor tag.
2226
		 */
2227
		$attr = apply_filters( 'next_posts_link_attributes', '' );
2228
2229
		return '<a href="' . next_posts( $max_page, false ) . "\" $attr>" . preg_replace('/&([^#])(?![a-z]{1,8};)/i', '&#038;$1', $label) . '</a>';
2230
	}
2231
}
2232
2233
/**
2234
 * Displays the next posts page link.
2235
 *
2236
 * @since 0.71
2237
 *
2238
 * @param string $label    Content for link text.
2239
 * @param int    $max_page Optional. Max pages. Default 0.
2240
 */
2241
function next_posts_link( $label = null, $max_page = 0 ) {
2242
	echo get_next_posts_link( $label, $max_page );
2243
}
2244
2245
/**
2246
 * Retrieves the previous posts page link.
2247
 *
2248
 * Will only return string, if not on a single page or post.
2249
 *
2250
 * Backported to 2.0.10 from 2.1.3.
2251
 *
2252
 * @since 2.0.10
2253
 *
2254
 * @global int $paged
2255
 *
2256
 * @return string|void The link for the previous posts page.
2257
 */
2258
function get_previous_posts_page_link() {
2259
	global $paged;
2260
2261
	if ( !is_single() ) {
2262
		$nextpage = intval($paged) - 1;
2263
		if ( $nextpage < 1 )
2264
			$nextpage = 1;
2265
		return get_pagenum_link($nextpage);
2266
	}
2267
}
2268
2269
/**
2270
 * Displays or retrieves the previous posts page link.
2271
 *
2272
 * @since 0.71
2273
 *
2274
 * @param bool $echo Optional. Whether to echo the link. Default true.
2275
 * @return string|void The previous posts page link if `$echo = false`.
2276
 */
2277
function previous_posts( $echo = true ) {
2278
	$output = esc_url( get_previous_posts_page_link() );
2279
2280
	if ( $echo )
2281
		echo $output;
2282
	else
2283
		return $output;
2284
}
2285
2286
/**
2287
 * Retrieves the previous posts page link.
2288
 *
2289
 * @since 2.7.0
2290
 *
2291
 * @global int $paged
2292
 *
2293
 * @param string $label Optional. Previous page link text.
2294
 * @return string|void HTML-formatted previous page link.
2295
 */
2296
function get_previous_posts_link( $label = null ) {
2297
	global $paged;
2298
2299
	if ( null === $label )
2300
		$label = __( '&laquo; Previous Page' );
2301
2302 View Code Duplication
	if ( !is_single() && $paged > 1 ) {
2303
		/**
2304
		 * Filters the anchor tag attributes for the previous posts page link.
2305
		 *
2306
		 * @since 2.7.0
2307
		 *
2308
		 * @param string $attributes Attributes for the anchor tag.
2309
		 */
2310
		$attr = apply_filters( 'previous_posts_link_attributes', '' );
2311
		return '<a href="' . previous_posts( false ) . "\" $attr>". preg_replace( '/&([^#])(?![a-z]{1,8};)/i', '&#038;$1', $label ) .'</a>';
2312
	}
2313
}
2314
2315
/**
2316
 * Displays the previous posts page link.
2317
 *
2318
 * @since 0.71
2319
 *
2320
 * @param string $label Optional. Previous page link text.
2321
 */
2322
function previous_posts_link( $label = null ) {
2323
	echo get_previous_posts_link( $label );
2324
}
2325
2326
/**
2327
 * Retrieves the post pages link navigation for previous and next pages.
2328
 *
2329
 * @since 2.8.0
2330
 *
2331
 * @global WP_Query $wp_query
2332
 *
2333
 * @param string|array $args {
2334
 *     Optional. Arguments to build the post pages link navigation.
2335
 *
2336
 *     @type string $sep      Separator character. Default '&#8212;'.
2337
 *     @type string $prelabel Link text to display for the previous page link.
2338
 *                            Default '&laquo; Previous Page'.
2339
 *     @type string $nxtlabel Link text to display for the next page link.
2340
 *                            Default 'Next Page &raquo;'.
2341
 * }
2342
 * @return string The posts link navigation.
2343
 */
2344
function get_posts_nav_link( $args = array() ) {
2345
	global $wp_query;
2346
2347
	$return = '';
2348
2349
	if ( !is_singular() ) {
2350
		$defaults = array(
2351
			'sep' => ' &#8212; ',
2352
			'prelabel' => __('&laquo; Previous Page'),
2353
			'nxtlabel' => __('Next Page &raquo;'),
2354
		);
2355
		$args = wp_parse_args( $args, $defaults );
2356
2357
		$max_num_pages = $wp_query->max_num_pages;
2358
		$paged = get_query_var('paged');
2359
2360
		//only have sep if there's both prev and next results
2361
		if ($paged < 2 || $paged >= $max_num_pages) {
2362
			$args['sep'] = '';
2363
		}
2364
2365
		if ( $max_num_pages > 1 ) {
2366
			$return = get_previous_posts_link($args['prelabel']);
2367
			$return .= preg_replace('/&([^#])(?![a-z]{1,8};)/i', '&#038;$1', $args['sep']);
2368
			$return .= get_next_posts_link($args['nxtlabel']);
2369
		}
2370
	}
2371
	return $return;
2372
2373
}
2374
2375
/**
2376
 * Displays the post pages link navigation for previous and next pages.
2377
 *
2378
 * @since 0.71
2379
 *
2380
 * @param string $sep      Optional. Separator for posts navigation links. Default empty.
2381
 * @param string $prelabel Optional. Label for previous pages. Default empty.
2382
 * @param string $nxtlabel Optional Label for next pages. Default empty.
2383
 */
2384
function posts_nav_link( $sep = '', $prelabel = '', $nxtlabel = '' ) {
2385
	$args = array_filter( compact('sep', 'prelabel', 'nxtlabel') );
2386
	echo get_posts_nav_link($args);
2387
}
2388
2389
/**
2390
 * Retrieves the navigation to next/previous post, when applicable.
2391
 *
2392
 * @since 4.1.0
2393
 * @since 4.4.0 Introduced the `in_same_term`, `excluded_terms`, and `taxonomy` arguments.
2394
 *
2395
 * @param array $args {
2396
 *     Optional. Default post navigation arguments. Default empty array.
2397
 *
2398
 *     @type string       $prev_text          Anchor text to display in the previous post link. Default '%title'.
2399
 *     @type string       $next_text          Anchor text to display in the next post link. Default '%title'.
2400
 *     @type bool         $in_same_term       Whether link should be in a same taxonomy term. Default false.
2401
 *     @type array|string $excluded_terms     Array or comma-separated list of excluded term IDs. Default empty.
2402
 *     @type string       $taxonomy           Taxonomy, if `$in_same_term` is true. Default 'category'.
2403
 *     @type string       $screen_reader_text Screen reader text for nav element. Default 'Post navigation'.
2404
 * }
2405
 * @return string Markup for post links.
2406
 */
2407
function get_the_post_navigation( $args = array() ) {
2408
	$args = wp_parse_args( $args, array(
2409
		'prev_text'          => '%title',
2410
		'next_text'          => '%title',
2411
		'in_same_term'       => false,
2412
		'excluded_terms'     => '',
2413
		'taxonomy'           => 'category',
2414
		'screen_reader_text' => __( 'Post navigation' ),
2415
	) );
2416
2417
	$navigation = '';
2418
2419
	$previous = get_previous_post_link(
2420
		'<div class="nav-previous">%link</div>',
2421
		$args['prev_text'],
2422
		$args['in_same_term'],
2423
		$args['excluded_terms'],
2424
		$args['taxonomy']
2425
	);
2426
2427
	$next = get_next_post_link(
2428
		'<div class="nav-next">%link</div>',
2429
		$args['next_text'],
2430
		$args['in_same_term'],
2431
		$args['excluded_terms'],
2432
		$args['taxonomy']
2433
	);
2434
2435
	// Only add markup if there's somewhere to navigate to.
2436
	if ( $previous || $next ) {
2437
		$navigation = _navigation_markup( $previous . $next, 'post-navigation', $args['screen_reader_text'] );
2438
	}
2439
2440
	return $navigation;
2441
}
2442
2443
/**
2444
 * Displays the navigation to next/previous post, when applicable.
2445
 *
2446
 * @since 4.1.0
2447
 *
2448
 * @param array $args Optional. See get_the_post_navigation() for available arguments.
2449
 *                    Default empty array.
2450
 */
2451
function the_post_navigation( $args = array() ) {
2452
	echo get_the_post_navigation( $args );
2453
}
2454
2455
/**
2456
 * Returns the navigation to next/previous set of posts, when applicable.
2457
 *
2458
 * @since 4.1.0
2459
 *
2460
 * @global WP_Query $wp_query WordPress Query object.
2461
 *
2462
 * @param array $args {
2463
 *     Optional. Default posts navigation arguments. Default empty array.
2464
 *
2465
 *     @type string $prev_text          Anchor text to display in the previous posts link.
2466
 *                                      Default 'Older posts'.
2467
 *     @type string $next_text          Anchor text to display in the next posts link.
2468
 *                                      Default 'Newer posts'.
2469
 *     @type string $screen_reader_text Screen reader text for nav element.
2470
 *                                      Default 'Posts navigation'.
2471
 * }
2472
 * @return string Markup for posts links.
2473
 */
2474 View Code Duplication
function get_the_posts_navigation( $args = array() ) {
0 ignored issues
show
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2475
	$navigation = '';
2476
2477
	// Don't print empty markup if there's only one page.
2478
	if ( $GLOBALS['wp_query']->max_num_pages > 1 ) {
2479
		$args = wp_parse_args( $args, array(
2480
			'prev_text'          => __( 'Older posts' ),
2481
			'next_text'          => __( 'Newer posts' ),
2482
			'screen_reader_text' => __( 'Posts navigation' ),
2483
		) );
2484
2485
		$next_link = get_previous_posts_link( $args['next_text'] );
2486
		$prev_link = get_next_posts_link( $args['prev_text'] );
2487
2488
		if ( $prev_link ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $prev_link of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null 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...
2489
			$navigation .= '<div class="nav-previous">' . $prev_link . '</div>';
2490
		}
2491
2492
		if ( $next_link ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $next_link of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null 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...
2493
			$navigation .= '<div class="nav-next">' . $next_link . '</div>';
2494
		}
2495
2496
		$navigation = _navigation_markup( $navigation, 'posts-navigation', $args['screen_reader_text'] );
2497
	}
2498
2499
	return $navigation;
2500
}
2501
2502
/**
2503
 * Displays the navigation to next/previous set of posts, when applicable.
2504
 *
2505
 * @since 4.1.0
2506
 *
2507
 * @param array $args Optional. See get_the_posts_navigation() for available arguments.
2508
 *                    Default empty array.
2509
 */
2510
function the_posts_navigation( $args = array() ) {
2511
	echo get_the_posts_navigation( $args );
2512
}
2513
2514
/**
2515
 * Retrieves a paginated navigation to next/previous set of posts, when applicable.
2516
 *
2517
 * @since 4.1.0
2518
 *
2519
 * @param array $args {
2520
 *     Optional. Default pagination arguments, see paginate_links().
2521
 *
2522
 *     @type string $screen_reader_text Screen reader text for navigation element.
2523
 *                                      Default 'Posts navigation'.
2524
 * }
2525
 * @return string Markup for pagination links.
2526
 */
2527
function get_the_posts_pagination( $args = array() ) {
2528
	$navigation = '';
2529
2530
	// Don't print empty markup if there's only one page.
2531
	if ( $GLOBALS['wp_query']->max_num_pages > 1 ) {
2532
		$args = wp_parse_args( $args, array(
2533
			'mid_size'           => 1,
2534
			'prev_text'          => _x( 'Previous', 'previous set of posts' ),
2535
			'next_text'          => _x( 'Next', 'next set of posts' ),
2536
			'screen_reader_text' => __( 'Posts navigation' ),
2537
		) );
2538
2539
		// Make sure we get a string back. Plain is the next best thing.
2540
		if ( isset( $args['type'] ) && 'array' == $args['type'] ) {
2541
			$args['type'] = 'plain';
2542
		}
2543
2544
		// Set up paginated links.
2545
		$links = paginate_links( $args );
2546
2547
		if ( $links ) {
2548
			$navigation = _navigation_markup( $links, 'pagination', $args['screen_reader_text'] );
2549
		}
2550
	}
2551
2552
	return $navigation;
2553
}
2554
2555
/**
2556
 * Displays a paginated navigation to next/previous set of posts, when applicable.
2557
 *
2558
 * @since 4.1.0
2559
 *
2560
 * @param array $args Optional. See get_the_posts_pagination() for available arguments.
2561
 *                    Default empty array.
2562
 */
2563
function the_posts_pagination( $args = array() ) {
2564
	echo get_the_posts_pagination( $args );
2565
}
2566
2567
/**
2568
 * Wraps passed links in navigational markup.
2569
 *
2570
 * @since 4.1.0
2571
 * @access private
2572
 *
2573
 * @param string $links              Navigational links.
2574
 * @param string $class              Optional. Custom class for nav element. Default: 'posts-navigation'.
2575
 * @param string $screen_reader_text Optional. Screen reader text for nav element. Default: 'Posts navigation'.
2576
 * @return string Navigation template tag.
2577
 */
2578
function _navigation_markup( $links, $class = 'posts-navigation', $screen_reader_text = '' ) {
2579
	if ( empty( $screen_reader_text ) ) {
2580
		$screen_reader_text = __( 'Posts navigation' );
2581
	}
2582
2583
	$template = '
2584
	<nav class="navigation %1$s" role="navigation">
2585
		<h2 class="screen-reader-text">%2$s</h2>
2586
		<div class="nav-links">%3$s</div>
2587
	</nav>';
2588
2589
	/**
2590
	 * Filters the navigation markup template.
2591
	 *
2592
	 * Note: The filtered template HTML must contain specifiers for the navigation
2593
	 * class (%1$s), the screen-reader-text value (%2$s), and placement of the
2594
	 * navigation links (%3$s):
2595
	 *
2596
	 *     <nav class="navigation %1$s" role="navigation">
2597
	 *         <h2 class="screen-reader-text">%2$s</h2>
2598
	 *         <div class="nav-links">%3$s</div>
2599
	 *     </nav>
2600
	 *
2601
	 * @since 4.4.0
2602
	 *
2603
	 * @param string $template The default template.
2604
	 * @param string $class    The class passed by the calling function.
2605
	 * @return string Navigation template.
2606
	 */
2607
	$template = apply_filters( 'navigation_markup_template', $template, $class );
2608
2609
	return sprintf( $template, sanitize_html_class( $class ), esc_html( $screen_reader_text ), $links );
2610
}
2611
2612
/**
2613
 * Retrieves the comments page number link.
2614
 *
2615
 * @since 2.7.0
2616
 *
2617
 * @global WP_Rewrite $wp_rewrite
2618
 *
2619
 * @param int $pagenum  Optional. Page number. Default 1.
2620
 * @param int $max_page Optional. The maximum number of comment pages. Default 0.
2621
 * @return string The comments page number link URL.
2622
 */
2623
function get_comments_pagenum_link( $pagenum = 1, $max_page = 0 ) {
2624
	global $wp_rewrite;
2625
2626
	$pagenum = (int) $pagenum;
2627
2628
	$result = get_permalink();
2629
2630
	if ( 'newest' == get_option('default_comments_page') ) {
2631 View Code Duplication
		if ( $pagenum != $max_page ) {
2632
			if ( $wp_rewrite->using_permalinks() )
2633
				$result = user_trailingslashit( trailingslashit($result) . $wp_rewrite->comments_pagination_base . '-' . $pagenum, 'commentpaged');
0 ignored issues
show
It seems like $result can also be of type false; however, trailingslashit() does only seem to accept string, did you maybe forget to handle an error condition?
Loading history...
2634
			else
2635
				$result = add_query_arg( 'cpage', $pagenum, $result );
2636
		}
2637 View Code Duplication
	} elseif ( $pagenum > 1 ) {
2638
		if ( $wp_rewrite->using_permalinks() )
2639
			$result = user_trailingslashit( trailingslashit($result) . $wp_rewrite->comments_pagination_base . '-' . $pagenum, 'commentpaged');
0 ignored issues
show
It seems like $result can also be of type false; however, trailingslashit() does only seem to accept string, did you maybe forget to handle an error condition?
Loading history...
2640
		else
2641
			$result = add_query_arg( 'cpage', $pagenum, $result );
2642
	}
2643
2644
	$result .= '#comments';
2645
2646
	/**
2647
	 * Filters the comments page number link for the current request.
2648
	 *
2649
	 * @since 2.7.0
2650
	 *
2651
	 * @param string $result The comments page number link.
2652
	 */
2653
	return apply_filters( 'get_comments_pagenum_link', $result );
2654
}
2655
2656
/**
2657
 * Retrieves the link to the next comments page.
2658
 *
2659
 * @since 2.7.1
2660
 *
2661
 * @global WP_Query $wp_query
2662
 *
2663
 * @param string $label    Optional. Label for link text. Default empty.
2664
 * @param int    $max_page Optional. Max page. Default 0.
2665
 * @return string|void HTML-formatted link for the next page of comments.
2666
 */
2667
function get_next_comments_link( $label = '', $max_page = 0 ) {
2668
	global $wp_query;
2669
2670
	if ( ! is_singular() )
2671
		return;
2672
2673
	$page = get_query_var('cpage');
2674
2675
	if ( ! $page ) {
2676
		$page = 1;
2677
	}
2678
2679
	$nextpage = intval($page) + 1;
2680
2681
	if ( empty($max_page) )
2682
		$max_page = $wp_query->max_num_comment_pages;
2683
2684
	if ( empty($max_page) )
2685
		$max_page = get_comment_pages_count();
2686
2687
	if ( $nextpage > $max_page )
2688
		return;
2689
2690
	if ( empty($label) )
2691
		$label = __('Newer Comments &raquo;');
2692
2693
	/**
2694
	 * Filters the anchor tag attributes for the next comments page link.
2695
	 *
2696
	 * @since 2.7.0
2697
	 *
2698
	 * @param string $attributes Attributes for the anchor tag.
2699
	 */
2700
	return '<a href="' . esc_url( get_comments_pagenum_link( $nextpage, $max_page ) ) . '" ' . apply_filters( 'next_comments_link_attributes', '' ) . '>'. preg_replace('/&([^#])(?![a-z]{1,8};)/i', '&#038;$1', $label) .'</a>';
2701
}
2702
2703
/**
2704
 * Displays the link to the next comments page.
2705
 *
2706
 * @since 2.7.0
2707
 *
2708
 * @param string $label    Optional. Label for link text. Default empty.
2709
 * @param int    $max_page Optional. Max page. Default 0.
2710
 */
2711
function next_comments_link( $label = '', $max_page = 0 ) {
2712
	echo get_next_comments_link( $label, $max_page );
2713
}
2714
2715
/**
2716
 * Retrieves the link to the previous comments page.
2717
 *
2718
 * @since 2.7.1
2719
 *
2720
 * @param string $label Optional. Label for comments link text. Default empty.
2721
 * @return string|void HTML-formatted link for the previous page of comments.
2722
 */
2723
function get_previous_comments_link( $label = '' ) {
2724
	if ( ! is_singular() )
2725
		return;
2726
2727
	$page = get_query_var('cpage');
2728
2729
	if ( intval($page) <= 1 )
2730
		return;
2731
2732
	$prevpage = intval($page) - 1;
2733
2734
	if ( empty($label) )
2735
		$label = __('&laquo; Older Comments');
2736
2737
	/**
2738
	 * Filters the anchor tag attributes for the previous comments page link.
2739
	 *
2740
	 * @since 2.7.0
2741
	 *
2742
	 * @param string $attributes Attributes for the anchor tag.
2743
	 */
2744
	return '<a href="' . esc_url( get_comments_pagenum_link( $prevpage ) ) . '" ' . apply_filters( 'previous_comments_link_attributes', '' ) . '>' . preg_replace('/&([^#])(?![a-z]{1,8};)/i', '&#038;$1', $label) .'</a>';
2745
}
2746
2747
/**
2748
 * Displays the link to the previous comments page.
2749
 *
2750
 * @since 2.7.0
2751
 *
2752
 * @param string $label Optional. Label for comments link text. Default empty.
2753
 */
2754
function previous_comments_link( $label = '' ) {
2755
	echo get_previous_comments_link( $label );
2756
}
2757
2758
/**
2759
 * Displays or retrieves pagination links for the comments on the current post.
2760
 *
2761
 * @see paginate_links()
2762
 * @since 2.7.0
2763
 *
2764
 * @global WP_Rewrite $wp_rewrite
2765
 *
2766
 * @param string|array $args Optional args. See paginate_links(). Default empty array.
2767
 * @return string|void Markup for pagination links.
0 ignored issues
show
Should the return type not be null|array|string? Also, consider making the array more specific, something like array<String>, or String[].

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

If the return type contains the type array, this check recommends the use of a more specific type like String[] or array<String>.

Loading history...
2768
 */
2769
function paginate_comments_links( $args = array() ) {
2770
	global $wp_rewrite;
2771
2772
	if ( ! is_singular() )
2773
		return;
2774
2775
	$page = get_query_var('cpage');
2776
	if ( !$page )
2777
		$page = 1;
2778
	$max_page = get_comment_pages_count();
2779
	$defaults = array(
2780
		'base' => add_query_arg( 'cpage', '%#%' ),
2781
		'format' => '',
2782
		'total' => $max_page,
2783
		'current' => $page,
2784
		'echo' => true,
2785
		'add_fragment' => '#comments'
2786
	);
2787
	if ( $wp_rewrite->using_permalinks() )
2788
		$defaults['base'] = user_trailingslashit(trailingslashit(get_permalink()) . $wp_rewrite->comments_pagination_base . '-%#%', 'commentpaged');
0 ignored issues
show
It seems like get_permalink() can also be of type false; however, trailingslashit() does only seem to accept string, did you maybe forget to handle an error condition?
Loading history...
2789
2790
	$args = wp_parse_args( $args, $defaults );
2791
	$page_links = paginate_links( $args );
2792
2793
	if ( $args['echo'] )
2794
		echo $page_links;
2795
	else
2796
		return $page_links;
2797
}
2798
2799
/**
2800
 * Retrieves navigation to next/previous set of comments, when applicable.
2801
 *
2802
 * @since 4.4.0
2803
 *
2804
 * @param array $args {
2805
 *     Optional. Default comments navigation arguments.
2806
 *
2807
 *     @type string $prev_text          Anchor text to display in the previous comments link.
2808
 *                                      Default 'Older comments'.
2809
 *     @type string $next_text          Anchor text to display in the next comments link.
2810
 *                                      Default 'Newer comments'.
2811
 *     @type string $screen_reader_text Screen reader text for nav element. Default 'Comments navigation'.
2812
 * }
2813
 * @return string Markup for comments links.
2814
 */
2815 View Code Duplication
function get_the_comments_navigation( $args = array() ) {
0 ignored issues
show
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2816
	$navigation = '';
2817
2818
	// Are there comments to navigate through?
2819
	if ( get_comment_pages_count() > 1 ) {
2820
		$args = wp_parse_args( $args, array(
2821
			'prev_text'          => __( 'Older comments' ),
2822
			'next_text'          => __( 'Newer comments' ),
2823
			'screen_reader_text' => __( 'Comments navigation' ),
2824
		) );
2825
2826
		$prev_link = get_previous_comments_link( $args['prev_text'] );
2827
		$next_link = get_next_comments_link( $args['next_text'] );
2828
2829
		if ( $prev_link ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $prev_link of type null|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null 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...
2830
			$navigation .= '<div class="nav-previous">' . $prev_link . '</div>';
2831
		}
2832
2833
		if ( $next_link ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $next_link of type null|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null 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...
2834
			$navigation .= '<div class="nav-next">' . $next_link . '</div>';
2835
		}
2836
2837
		$navigation = _navigation_markup( $navigation, 'comment-navigation', $args['screen_reader_text'] );
2838
	}
2839
2840
	return $navigation;
2841
}
2842
2843
/**
2844
 * Displays navigation to next/previous set of comments, when applicable.
2845
 *
2846
 * @since 4.4.0
2847
 *
2848
 * @param array $args See get_the_comments_navigation() for available arguments. Default empty array.
2849
 */
2850
function the_comments_navigation( $args = array() ) {
2851
	echo get_the_comments_navigation( $args );
2852
}
2853
2854
/**
2855
 * Retrieves a paginated navigation to next/previous set of comments, when applicable.
2856
 *
2857
 * @since 4.4.0
2858
 *
2859
 * @see paginate_comments_links()
2860
 *
2861
 * @param array $args {
2862
 *     Optional. Default pagination arguments.
2863
 *
2864
 *     @type string $screen_reader_text Screen reader text for nav element. Default 'Comments navigation'.
2865
 * }
2866
 * @return string Markup for pagination links.
2867
 */
2868
function get_the_comments_pagination( $args = array() ) {
2869
	$navigation = '';
2870
	$args       = wp_parse_args( $args, array(
2871
		'screen_reader_text' => __( 'Comments navigation' ),
2872
	) );
2873
	$args['echo'] = false;
2874
2875
	// Make sure we get plain links, so we get a string we can work with.
2876
	$args['type'] = 'plain';
2877
2878
	$links = paginate_comments_links( $args );
2879
2880
	if ( $links ) {
2881
		$navigation = _navigation_markup( $links, 'comments-pagination', $args['screen_reader_text'] );
2882
	}
2883
2884
	return $navigation;
2885
}
2886
2887
/**
2888
 * Displays a paginated navigation to next/previous set of comments, when applicable.
2889
 *
2890
 * @since 4.4.0
2891
 *
2892
 * @param array $args See get_the_comments_pagination() for available arguments. Default empty array.
2893
 */
2894
function the_comments_pagination( $args = array() ) {
2895
	echo get_the_comments_pagination( $args );
2896
}
2897
2898
/**
2899
 * Retrieves the Press This bookmarklet link.
2900
 *
2901
 * @since 2.6.0
2902
 *
2903
 * @global bool          $is_IE      Whether the browser matches an Internet Explorer user agent.
2904
 */
2905
function get_shortcut_link() {
2906
	global $is_IE;
2907
2908
	include_once( ABSPATH . 'wp-admin/includes/class-wp-press-this.php' );
2909
2910
	$link = '';
2911
2912
	if ( $is_IE ) {
2913
		/*
2914
		 * Return the old/shorter bookmarklet code for MSIE 8 and lower,
2915
		 * since they only support a max length of ~2000 characters for
2916
		 * bookmark[let] URLs, which is way to small for our smarter one.
2917
		 * Do update the version number so users do not get the "upgrade your
2918
		 * bookmarklet" notice when using PT in those browsers.
2919
		 */
2920
		$ua = $_SERVER['HTTP_USER_AGENT'];
2921
2922
		if ( ! empty( $ua ) && preg_match( '/\bMSIE (\d)/', $ua, $matches ) && (int) $matches[1] <= 8 ) {
2923
			$url = wp_json_encode( admin_url( 'press-this.php' ) );
2924
2925
			$link = 'javascript:var d=document,w=window,e=w.getSelection,k=d.getSelection,x=d.selection,' .
2926
				's=(e?e():(k)?k():(x?x.createRange().text:0)),f=' . $url . ',l=d.location,e=encodeURIComponent,' .
2927
				'u=f+"?u="+e(l.href)+"&t="+e(d.title)+"&s="+e(s)+"&v=' . WP_Press_This::VERSION . '";' .
2928
				'a=function(){if(!w.open(u,"t","toolbar=0,resizable=1,scrollbars=1,status=1,width=600,height=700"))l.href=u;};' .
2929
				'if(/Firefox/.test(navigator.userAgent))setTimeout(a,0);else a();void(0)';
2930
		}
2931
	}
2932
2933
	if ( empty( $link ) ) {
2934
		$src = @file_get_contents( ABSPATH . 'wp-admin/js/bookmarklet.min.js' );
2935
2936
		if ( $src ) {
2937
			$url = wp_json_encode( admin_url( 'press-this.php' ) . '?v=' . WP_Press_This::VERSION );
2938
			$link = 'javascript:' . str_replace( 'window.pt_url', $url, $src );
2939
		}
2940
	}
2941
2942
	$link = str_replace( array( "\r", "\n", "\t" ),  '', $link );
2943
2944
	/**
2945
	 * Filters the Press This bookmarklet link.
2946
	 *
2947
	 * @since 2.6.0
2948
	 *
2949
	 * @param string $link The Press This bookmarklet link.
2950
	 */
2951
	return apply_filters( 'shortcut_link', $link );
2952
}
2953
2954
/**
2955
 * Retrieves the URL for the current site where the front end is accessible.
2956
 *
2957
 * Returns the 'home' option with the appropriate protocol. The protocol will be 'https'
2958
 * if is_ssl() evaluates to true; otherwise, it will be the same as the 'home' option.
2959
 * If `$scheme` is 'http' or 'https', is_ssl() is overridden.
2960
 *
2961
 * @since 3.0.0
2962
 *
2963
 * @param  string      $path   Optional. Path relative to the home URL. Default empty.
2964
 * @param  string|null $scheme Optional. Scheme to give the home URL context. Accepts
2965
 *                             'http', 'https', 'relative', 'rest', or null. Default null.
2966
 * @return string Home URL link with optional path appended.
2967
 */
2968
function home_url( $path = '', $scheme = null ) {
2969
	return get_home_url( null, $path, $scheme );
2970
}
2971
2972
/**
2973
 * Retrieves the URL for a given site where the front end is accessible.
2974
 *
2975
 * Returns the 'home' option with the appropriate protocol. The protocol will be 'https'
2976
 * if is_ssl() evaluates to true; otherwise, it will be the same as the 'home' option.
2977
 * If `$scheme` is 'http' or 'https', is_ssl() is overridden.
2978
 *
2979
 * @since 3.0.0
2980
 *
2981
 * @global string $pagenow
2982
 *
2983
 * @param  int         $blog_id Optional. Site ID. Default null (current site).
2984
 * @param  string      $path    Optional. Path relative to the home URL. Default empty.
2985
 * @param  string|null $scheme  Optional. Scheme to give the home URL context. Accepts
2986
 *                              'http', 'https', 'relative', 'rest', or null. Default null.
2987
 * @return string Home URL link with optional path appended.
2988
 */
2989
function get_home_url( $blog_id = null, $path = '', $scheme = null ) {
2990
	global $pagenow;
2991
2992
	$orig_scheme = $scheme;
2993
2994 View Code Duplication
	if ( empty( $blog_id ) || !is_multisite() ) {
2995
		$url = get_option( 'home' );
2996
	} else {
2997
		switch_to_blog( $blog_id );
2998
		$url = get_option( 'home' );
2999
		restore_current_blog();
3000
	}
3001
3002
	if ( ! in_array( $scheme, array( 'http', 'https', 'relative' ) ) ) {
3003
		if ( is_ssl() && ! is_admin() && 'wp-login.php' !== $pagenow )
3004
			$scheme = 'https';
3005
		else
3006
			$scheme = parse_url( $url, PHP_URL_SCHEME );
3007
	}
3008
3009
	$url = set_url_scheme( $url, $scheme );
0 ignored issues
show
It seems like $scheme defined by parse_url($url, PHP_URL_SCHEME) on line 3006 can also be of type false; however, set_url_scheme() does only seem to accept string|null, did you maybe forget to handle an error condition?

This check looks for type mismatches where the missing type is false. This is usually indicative of an error condtion.

Consider the follow example

<?php

function getDate($date)
{
    if ($date !== null) {
        return new DateTime($date);
    }

    return false;
}

This function either returns a new DateTime object or false, if there was an error. This is a typical pattern in PHP programming to show that an error has occurred without raising an exception. The calling code should check for this returned false before passing on the value to another function or method that may not be able to handle a false.

Loading history...
3010
3011
	if ( $path && is_string( $path ) )
3012
		$url .= '/' . ltrim( $path, '/' );
3013
3014
	/**
3015
	 * Filters the home URL.
3016
	 *
3017
	 * @since 3.0.0
3018
	 *
3019
	 * @param string      $url         The complete home URL including scheme and path.
3020
	 * @param string      $path        Path relative to the home URL. Blank string if no path is specified.
3021
	 * @param string|null $orig_scheme Scheme to give the home URL context. Accepts 'http', 'https',
3022
	 *                                 'relative', 'rest', or null.
3023
	 * @param int|null    $blog_id     Site ID, or null for the current site.
3024
	 */
3025
	return apply_filters( 'home_url', $url, $path, $orig_scheme, $blog_id );
3026
}
3027
3028
/**
3029
 * Retrieves the URL for the current site where WordPress application files
3030
 * (e.g. wp-blog-header.php or the wp-admin/ folder) are accessible.
3031
 *
3032
 * Returns the 'site_url' option with the appropriate protocol, 'https' if
3033
 * is_ssl() and 'http' otherwise. If $scheme is 'http' or 'https', is_ssl() is
3034
 * overridden.
3035
 *
3036
 * @since 3.0.0
3037
 *
3038
 * @param string $path   Optional. Path relative to the site URL. Default empty.
3039
 * @param string $scheme Optional. Scheme to give the site URL context. See set_url_scheme().
3040
 * @return string Site URL link with optional path appended.
3041
 */
3042
function site_url( $path = '', $scheme = null ) {
3043
	return get_site_url( null, $path, $scheme );
3044
}
3045
3046
/**
3047
 * Retrieves the URL for a given site where WordPress application files
3048
 * (e.g. wp-blog-header.php or the wp-admin/ folder) are accessible.
3049
 *
3050
 * Returns the 'site_url' option with the appropriate protocol, 'https' if
3051
 * is_ssl() and 'http' otherwise. If `$scheme` is 'http' or 'https',
3052
 * `is_ssl()` is overridden.
3053
 *
3054
 * @since 3.0.0
3055
 *
3056
 * @param int    $blog_id Optional. Site ID. Default null (current site).
3057
 * @param string $path    Optional. Path relative to the site URL. Default empty.
3058
 * @param string $scheme  Optional. Scheme to give the site URL context. Accepts
3059
 *                        'http', 'https', 'login', 'login_post', 'admin', or
3060
 *                        'relative'. Default null.
3061
 * @return string Site URL link with optional path appended.
3062
 */
3063
function get_site_url( $blog_id = null, $path = '', $scheme = null ) {
3064 View Code Duplication
	if ( empty( $blog_id ) || !is_multisite() ) {
3065
		$url = get_option( 'siteurl' );
3066
	} else {
3067
		switch_to_blog( $blog_id );
3068
		$url = get_option( 'siteurl' );
3069
		restore_current_blog();
3070
	}
3071
3072
	$url = set_url_scheme( $url, $scheme );
3073
3074
	if ( $path && is_string( $path ) )
3075
		$url .= '/' . ltrim( $path, '/' );
3076
3077
	/**
3078
	 * Filters the site URL.
3079
	 *
3080
	 * @since 2.7.0
3081
	 *
3082
	 * @param string      $url     The complete site URL including scheme and path.
3083
	 * @param string      $path    Path relative to the site URL. Blank string if no path is specified.
3084
	 * @param string|null $scheme  Scheme to give the site URL context. Accepts 'http', 'https', 'login',
3085
	 *                             'login_post', 'admin', 'relative' or null.
3086
	 * @param int|null    $blog_id Site ID, or null for the current site.
3087
	 */
3088
	return apply_filters( 'site_url', $url, $path, $scheme, $blog_id );
3089
}
3090
3091
/**
3092
 * Retrieves the URL to the admin area for the current site.
3093
 *
3094
 * @since 2.6.0
3095
 *
3096
 * @param string $path   Optional path relative to the admin URL.
3097
 * @param string $scheme The scheme to use. Default is 'admin', which obeys force_ssl_admin() and is_ssl().
3098
 *                       'http' or 'https' can be passed to force those schemes.
3099
 * @return string Admin URL link with optional path appended.
3100
 */
3101
function admin_url( $path = '', $scheme = 'admin' ) {
3102
	return get_admin_url( null, $path, $scheme );
3103
}
3104
3105
/**
3106
 * Retrieves the URL to the admin area for a given site.
3107
 *
3108
 * @since 3.0.0
3109
 *
3110
 * @param int    $blog_id Optional. Site ID. Default null (current site).
3111
 * @param string $path    Optional. Path relative to the admin URL. Default empty.
3112
 * @param string $scheme  Optional. The scheme to use. Accepts 'http' or 'https',
3113
 *                        to force those schemes. Default 'admin', which obeys
3114
 *                        force_ssl_admin() and is_ssl().
3115
 * @return string Admin URL link with optional path appended.
3116
 */
3117 View Code Duplication
function get_admin_url( $blog_id = null, $path = '', $scheme = 'admin' ) {
0 ignored issues
show
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
3118
	$url = get_site_url($blog_id, 'wp-admin/', $scheme);
3119
3120
	if ( $path && is_string( $path ) )
3121
		$url .= ltrim( $path, '/' );
3122
3123
	/**
3124
	 * Filters the admin area URL.
3125
	 *
3126
	 * @since 2.8.0
3127
	 *
3128
	 * @param string   $url     The complete admin area URL including scheme and path.
3129
	 * @param string   $path    Path relative to the admin area URL. Blank string if no path is specified.
3130
	 * @param int|null $blog_id Site ID, or null for the current site.
3131
	 */
3132
	return apply_filters( 'admin_url', $url, $path, $blog_id );
3133
}
3134
3135
/**
3136
 * Retrieves the URL to the includes directory.
3137
 *
3138
 * @since 2.6.0
3139
 *
3140
 * @param string $path   Optional. Path relative to the includes URL. Default empty.
3141
 * @param string $scheme Optional. Scheme to give the includes URL context. Accepts
3142
 *                       'http', 'https', or 'relative'. Default null.
3143
 * @return string Includes URL link with optional path appended.
3144
 */
3145
function includes_url( $path = '', $scheme = null ) {
3146
	$url = site_url( '/' . WPINC . '/', $scheme );
3147
3148
	if ( $path && is_string( $path ) )
3149
		$url .= ltrim($path, '/');
3150
3151
	/**
3152
	 * Filters the URL to the includes directory.
3153
	 *
3154
	 * @since 2.8.0
3155
	 *
3156
	 * @param string $url  The complete URL to the includes directory including scheme and path.
3157
	 * @param string $path Path relative to the URL to the wp-includes directory. Blank string
3158
	 *                     if no path is specified.
3159
	 */
3160
	return apply_filters( 'includes_url', $url, $path );
3161
}
3162
3163
/**
3164
 * Retrieves the URL to the content directory.
3165
 *
3166
 * @since 2.6.0
3167
 *
3168
 * @param string $path Optional. Path relative to the content URL. Default empty.
3169
 * @return string Content URL link with optional path appended.
3170
 */
3171
function content_url( $path = '' ) {
3172
	$url = set_url_scheme( WP_CONTENT_URL );
3173
3174
	if ( $path && is_string( $path ) )
3175
		$url .= '/' . ltrim($path, '/');
3176
3177
	/**
3178
	 * Filters the URL to the content directory.
3179
	 *
3180
	 * @since 2.8.0
3181
	 *
3182
	 * @param string $url  The complete URL to the content directory including scheme and path.
3183
	 * @param string $path Path relative to the URL to the content directory. Blank string
3184
	 *                     if no path is specified.
3185
	 */
3186
	return apply_filters( 'content_url', $url, $path);
3187
}
3188
3189
/**
3190
 * Retrieves a URL within the plugins or mu-plugins directory.
3191
 *
3192
 * Defaults to the plugins directory URL if no arguments are supplied.
3193
 *
3194
 * @since 2.6.0
3195
 *
3196
 * @param  string $path   Optional. Extra path appended to the end of the URL, including
3197
 *                        the relative directory if $plugin is supplied. Default empty.
3198
 * @param  string $plugin Optional. A full path to a file inside a plugin or mu-plugin.
3199
 *                        The URL will be relative to its directory. Default empty.
3200
 *                        Typically this is done by passing `__FILE__` as the argument.
3201
 * @return string Plugins URL link with optional paths appended.
3202
 */
3203
function plugins_url( $path = '', $plugin = '' ) {
3204
3205
	$path = wp_normalize_path( $path );
3206
	$plugin = wp_normalize_path( $plugin );
3207
	$mu_plugin_dir = wp_normalize_path( WPMU_PLUGIN_DIR );
3208
3209
	if ( !empty($plugin) && 0 === strpos($plugin, $mu_plugin_dir) )
3210
		$url = WPMU_PLUGIN_URL;
3211
	else
3212
		$url = WP_PLUGIN_URL;
3213
3214
3215
	$url = set_url_scheme( $url );
3216
3217
	if ( !empty($plugin) && is_string($plugin) ) {
3218
		$folder = dirname(plugin_basename($plugin));
3219
		if ( '.' != $folder )
3220
			$url .= '/' . ltrim($folder, '/');
3221
	}
3222
3223
	if ( $path && is_string( $path ) )
3224
		$url .= '/' . ltrim($path, '/');
3225
3226
	/**
3227
	 * Filters the URL to the plugins directory.
3228
	 *
3229
	 * @since 2.8.0
3230
	 *
3231
	 * @param string $url    The complete URL to the plugins directory including scheme and path.
3232
	 * @param string $path   Path relative to the URL to the plugins directory. Blank string
3233
	 *                       if no path is specified.
3234
	 * @param string $plugin The plugin file path to be relative to. Blank string if no plugin
3235
	 *                       is specified.
3236
	 */
3237
	return apply_filters( 'plugins_url', $url, $path, $plugin );
3238
}
3239
3240
/**
3241
 * Retrieves the site URL for the current network.
3242
 *
3243
 * Returns the site URL with the appropriate protocol, 'https' if
3244
 * is_ssl() and 'http' otherwise. If $scheme is 'http' or 'https', is_ssl() is
3245
 * overridden.
3246
 *
3247
 * @since 3.0.0
3248
 *
3249
 * @see set_url_scheme()
3250
 *
3251
 * @param string $path   Optional. Path relative to the site URL. Default empty.
3252
 * @param string $scheme Optional. Scheme to give the site URL context. Accepts
3253
 *                       'http', 'https', or 'relative'. Default null.
3254
 * @return string Site URL link with optional path appended.
3255
 */
3256
function network_site_url( $path = '', $scheme = null ) {
3257
	if ( ! is_multisite() )
3258
		return site_url($path, $scheme);
3259
3260
	$current_network = get_network();
3261
3262 View Code Duplication
	if ( 'relative' == $scheme )
3263
		$url = $current_network->path;
3264
	else
3265
		$url = set_url_scheme( 'http://' . $current_network->domain . $current_network->path, $scheme );
3266
3267
	if ( $path && is_string( $path ) )
3268
		$url .= ltrim( $path, '/' );
3269
3270
	/**
3271
	 * Filters the network site URL.
3272
	 *
3273
	 * @since 3.0.0
3274
	 *
3275
	 * @param string      $url    The complete network site URL including scheme and path.
3276
	 * @param string      $path   Path relative to the network site URL. Blank string if
3277
	 *                            no path is specified.
3278
	 * @param string|null $scheme Scheme to give the URL context. Accepts 'http', 'https',
3279
	 *                            'relative' or null.
3280
	 */
3281
	return apply_filters( 'network_site_url', $url, $path, $scheme );
3282
}
3283
3284
/**
3285
 * Retrieves the home URL for the current network.
3286
 *
3287
 * Returns the home URL with the appropriate protocol, 'https' is_ssl()
3288
 * and 'http' otherwise. If `$scheme` is 'http' or 'https', `is_ssl()` is
3289
 * overridden.
3290
 *
3291
 * @since 3.0.0
3292
 *
3293
 * @param  string $path   Optional. Path relative to the home URL. Default empty.
3294
 * @param  string $scheme Optional. Scheme to give the home URL context. Accepts
3295
 *                        'http', 'https', or 'relative'. Default null.
3296
 * @return string Home URL link with optional path appended.
3297
 */
3298
function network_home_url( $path = '', $scheme = null ) {
3299
	if ( ! is_multisite() )
3300
		return home_url($path, $scheme);
3301
3302
	$current_network = get_network();
3303
	$orig_scheme = $scheme;
3304
3305
	if ( ! in_array( $scheme, array( 'http', 'https', 'relative' ) ) )
3306
		$scheme = is_ssl() && ! is_admin() ? 'https' : 'http';
3307
3308 View Code Duplication
	if ( 'relative' == $scheme )
3309
		$url = $current_network->path;
3310
	else
3311
		$url = set_url_scheme( 'http://' . $current_network->domain . $current_network->path, $scheme );
3312
3313
	if ( $path && is_string( $path ) )
3314
		$url .= ltrim( $path, '/' );
3315
3316
	/**
3317
	 * Filters the network home URL.
3318
	 *
3319
	 * @since 3.0.0
3320
	 *
3321
	 * @param string      $url         The complete network home URL including scheme and path.
3322
	 * @param string      $path        Path relative to the network home URL. Blank string
3323
	 *                                 if no path is specified.
3324
	 * @param string|null $orig_scheme Scheme to give the URL context. Accepts 'http', 'https',
3325
	 *                                 'relative' or null.
3326
	 */
3327
	return apply_filters( 'network_home_url', $url, $path, $orig_scheme);
3328
}
3329
3330
/**
3331
 * Retrieves the URL to the admin area for the network.
3332
 *
3333
 * @since 3.0.0
3334
 *
3335
 * @param string $path   Optional path relative to the admin URL. Default empty.
3336
 * @param string $scheme Optional. The scheme to use. Default is 'admin', which obeys force_ssl_admin()
3337
 *                       and is_ssl(). 'http' or 'https' can be passed to force those schemes.
3338
 * @return string Admin URL link with optional path appended.
3339
 */
3340 View Code Duplication
function network_admin_url( $path = '', $scheme = 'admin' ) {
0 ignored issues
show
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
3341
	if ( ! is_multisite() )
3342
		return admin_url( $path, $scheme );
3343
3344
	$url = network_site_url('wp-admin/network/', $scheme);
3345
3346
	if ( $path && is_string( $path ) )
3347
		$url .= ltrim($path, '/');
3348
3349
	/**
3350
	 * Filters the network admin URL.
3351
	 *
3352
	 * @since 3.0.0
3353
	 *
3354
	 * @param string $url  The complete network admin URL including scheme and path.
3355
	 * @param string $path Path relative to the network admin URL. Blank string if
3356
	 *                     no path is specified.
3357
	 */
3358
	return apply_filters( 'network_admin_url', $url, $path );
3359
}
3360
3361
/**
3362
 * Retrieves the URL to the admin area for the current user.
3363
 *
3364
 * @since 3.0.0
3365
 *
3366
 * @param string $path   Optional. Path relative to the admin URL. Default empty.
3367
 * @param string $scheme Optional. The scheme to use. Default is 'admin', which obeys force_ssl_admin()
3368
 *                       and is_ssl(). 'http' or 'https' can be passed to force those schemes.
3369
 * @return string Admin URL link with optional path appended.
3370
 */
3371
function user_admin_url( $path = '', $scheme = 'admin' ) {
3372
	$url = network_site_url('wp-admin/user/', $scheme);
3373
3374
	if ( $path && is_string( $path ) )
3375
		$url .= ltrim($path, '/');
3376
3377
	/**
3378
	 * Filters the user admin URL for the current user.
3379
	 *
3380
	 * @since 3.1.0
3381
	 *
3382
	 * @param string $url  The complete URL including scheme and path.
3383
	 * @param string $path Path relative to the URL. Blank string if
3384
	 *                     no path is specified.
3385
	 */
3386
	return apply_filters( 'user_admin_url', $url, $path );
3387
}
3388
3389
/**
3390
 * Retrieves the URL to the admin area for either the current site or the network depending on context.
3391
 *
3392
 * @since 3.1.0
3393
 *
3394
 * @param string $path   Optional. Path relative to the admin URL. Default empty.
3395
 * @param string $scheme Optional. The scheme to use. Default is 'admin', which obeys force_ssl_admin()
3396
 *                       and is_ssl(). 'http' or 'https' can be passed to force those schemes.
3397
 * @return string Admin URL link with optional path appended.
3398
 */
3399
function self_admin_url( $path = '', $scheme = 'admin' ) {
3400
	if ( is_network_admin() )
3401
		return network_admin_url($path, $scheme);
3402
	elseif ( is_user_admin() )
3403
		return user_admin_url($path, $scheme);
3404
	else
3405
		return admin_url($path, $scheme);
3406
}
3407
3408
/**
3409
 * Sets the scheme for a URL.
3410
 *
3411
 * @since 3.4.0
3412
 * @since 4.4.0 The 'rest' scheme was added.
3413
 *
3414
 * @param string      $url    Absolute URL that includes a scheme
3415
 * @param string|null $scheme Optional. Scheme to give $url. Currently 'http', 'https', 'login',
3416
 *                            'login_post', 'admin', 'relative', 'rest', 'rpc', or null. Default null.
3417
 * @return string $url URL with chosen scheme.
3418
 */
3419
function set_url_scheme( $url, $scheme = null ) {
3420
	$orig_scheme = $scheme;
3421
3422
	if ( ! $scheme ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $scheme of type string|null is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null 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...
3423
		$scheme = is_ssl() ? 'https' : 'http';
3424
	} elseif ( $scheme === 'admin' || $scheme === 'login' || $scheme === 'login_post' || $scheme === 'rpc' ) {
3425
		$scheme = is_ssl() || force_ssl_admin() ? 'https' : 'http';
3426
	} elseif ( $scheme !== 'http' && $scheme !== 'https' && $scheme !== 'relative' ) {
3427
		$scheme = is_ssl() ? 'https' : 'http';
3428
	}
3429
3430
	$url = trim( $url );
3431
	if ( substr( $url, 0, 2 ) === '//' )
3432
		$url = 'http:' . $url;
3433
3434
	if ( 'relative' == $scheme ) {
3435
		$url = ltrim( preg_replace( '#^\w+://[^/]*#', '', $url ) );
3436
		if ( $url !== '' && $url[0] === '/' )
3437
			$url = '/' . ltrim($url , "/ \t\n\r\0\x0B" );
3438
	} else {
3439
		$url = preg_replace( '#^\w+://#', $scheme . '://', $url );
3440
	}
3441
3442
	/**
3443
	 * Filters the resulting URL after setting the scheme.
3444
	 *
3445
	 * @since 3.4.0
3446
	 *
3447
	 * @param string      $url         The complete URL including scheme and path.
3448
	 * @param string      $scheme      Scheme applied to the URL. One of 'http', 'https', or 'relative'.
3449
	 * @param string|null $orig_scheme Scheme requested for the URL. One of 'http', 'https', 'login',
3450
	 *                                 'login_post', 'admin', 'relative', 'rest', 'rpc', or null.
3451
	 */
3452
	return apply_filters( 'set_url_scheme', $url, $scheme, $orig_scheme );
3453
}
3454
3455
/**
3456
 * Retrieves the URL to the user's dashboard.
3457
 *
3458
 * If a user does not belong to any site, the global user dashboard is used. If the user
3459
 * belongs to the current site, the dashboard for the current site is returned. If the user
3460
 * cannot edit the current site, the dashboard to the user's primary site is returned.
3461
 *
3462
 * @since 3.1.0
3463
 *
3464
 * @param int    $user_id Optional. User ID. Defaults to current user.
3465
 * @param string $path    Optional path relative to the dashboard. Use only paths known to
3466
 *                        both site and user admins. Default empty.
3467
 * @param string $scheme  The scheme to use. Default is 'admin', which obeys force_ssl_admin()
3468
 *                        and is_ssl(). 'http' or 'https' can be passed to force those schemes.
3469
 * @return string Dashboard URL link with optional path appended.
3470
 */
3471
function get_dashboard_url( $user_id = 0, $path = '', $scheme = 'admin' ) {
3472
	$user_id = $user_id ? (int) $user_id : get_current_user_id();
3473
3474
	$blogs = get_blogs_of_user( $user_id );
3475
	if ( is_multisite() && ! user_can( $user_id, 'manage_network' ) && empty($blogs) ) {
3476
		$url = user_admin_url( $path, $scheme );
3477
	} elseif ( ! is_multisite() ) {
3478
		$url = admin_url( $path, $scheme );
3479
	} else {
3480
		$current_blog = get_current_blog_id();
3481
		if ( $current_blog  && ( user_can( $user_id, 'manage_network' ) || in_array( $current_blog, array_keys( $blogs ) ) ) ) {
3482
			$url = admin_url( $path, $scheme );
3483
		} else {
3484
			$active = get_active_blog_for_user( $user_id );
3485
			if ( $active )
3486
				$url = get_admin_url( $active->blog_id, $path, $scheme );
3487
			else
3488
				$url = user_admin_url( $path, $scheme );
3489
		}
3490
	}
3491
3492
	/**
3493
	 * Filters the dashboard URL for a user.
3494
	 *
3495
	 * @since 3.1.0
3496
	 *
3497
	 * @param string $url     The complete URL including scheme and path.
3498
	 * @param int    $user_id The user ID.
3499
	 * @param string $path    Path relative to the URL. Blank string if no path is specified.
3500
	 * @param string $scheme  Scheme to give the URL context. Accepts 'http', 'https', 'login',
3501
	 *                        'login_post', 'admin', 'relative' or null.
3502
	 */
3503
	return apply_filters( 'user_dashboard_url', $url, $user_id, $path, $scheme);
3504
}
3505
3506
/**
3507
 * Retrieves the URL to the user's profile editor.
3508
 *
3509
 * @since 3.1.0
3510
 *
3511
 * @param int    $user_id Optional. User ID. Defaults to current user.
3512
 * @param string $scheme  Optional. The scheme to use. Default is 'admin', which obeys force_ssl_admin()
3513
 *                        and is_ssl(). 'http' or 'https' can be passed to force those schemes.
3514
 * @return string Dashboard URL link with optional path appended.
3515
 */
3516
function get_edit_profile_url( $user_id = 0, $scheme = 'admin' ) {
3517
	$user_id = $user_id ? (int) $user_id : get_current_user_id();
3518
3519
	if ( is_user_admin() )
3520
		$url = user_admin_url( 'profile.php', $scheme );
3521
	elseif ( is_network_admin() )
3522
		$url = network_admin_url( 'profile.php', $scheme );
3523
	else
3524
		$url = get_dashboard_url( $user_id, 'profile.php', $scheme );
3525
3526
	/**
3527
	 * Filters the URL for a user's profile editor.
3528
	 *
3529
	 * @since 3.1.0
3530
	 *
3531
	 * @param string $url     The complete URL including scheme and path.
3532
	 * @param int    $user_id The user ID.
3533
	 * @param string $scheme  Scheme to give the URL context. Accepts 'http', 'https', 'login',
3534
	 *                        'login_post', 'admin', 'relative' or null.
3535
	 */
3536
	return apply_filters( 'edit_profile_url', $url, $user_id, $scheme);
3537
}
3538
3539
/**
3540
 * Returns the canonical URL for a post.
3541
 *
3542
 * When the post is the same as the current requested page the function will handle the
3543
 * pagination arguments too.
3544
 *
3545
 * @since 4.6.0
3546
 *
3547
 * @param int|WP_Post $post Optional. Post ID or object. Default is global `$post`.
3548
 * @return string|false The canonical URL, or false if the post does not exist or has not
3549
 *                      been published yet.
3550
 */
3551
function wp_get_canonical_url( $post = null ) {
3552
	$post = get_post( $post );
3553
3554
	if ( ! $post ) {
3555
		return false;
3556
	}
3557
3558
	if ( 'publish' !== $post->post_status ) {
3559
		return false;
3560
	}
3561
3562
	$canonical_url = get_permalink( $post );
3563
3564
	// If a canonical is being generated for the current page, make sure it has pagination if needed.
3565
	if ( $post->ID === get_queried_object_id() ) {
3566
		$page = get_query_var( 'page', 0 );
3567
		if ( $page >= 2 ) {
3568
			if ( '' == get_option( 'permalink_structure' ) ) {
3569
				$canonical_url = add_query_arg( 'page', $page, $canonical_url );
3570
			} else {
3571
				$canonical_url = trailingslashit( $canonical_url ) . user_trailingslashit( $page, 'single_paged' );
0 ignored issues
show
It seems like $canonical_url can also be of type false; however, trailingslashit() does only seem to accept string, did you maybe forget to handle an error condition?
Loading history...
3572
			}
3573
		}
3574
3575
		$cpage = get_query_var( 'cpage', 0 );
3576
		if ( $cpage ) {
3577
			$canonical_url = get_comments_pagenum_link( $cpage );
3578
		}
3579
	}
3580
3581
	/**
3582
	 * Filters the canonical URL for a post.
3583
	 *
3584
	 * @since 4.6.0
3585
	 *
3586
	 * @param string  $canonical_url The post's canonical URL.
3587
	 * @param WP_Post $post          Post object.
3588
	 */
3589
	return apply_filters( 'get_canonical_url', $canonical_url, $post );
3590
}
3591
3592
/**
3593
 * Outputs rel=canonical for singular queries.
3594
 *
3595
 * @since 2.9.0
3596
 * @since 4.6.0 Adjusted to use wp_get_canonical_url().
3597
 */
3598
function rel_canonical() {
3599
	if ( ! is_singular() ) {
3600
		return;
3601
	}
3602
3603
	$id = get_queried_object_id();
3604
3605
	if ( 0 === $id ) {
3606
		return;
3607
	}
3608
3609
	$url = wp_get_canonical_url( $id );
3610
3611
	if ( ! empty( $url ) ) {
3612
		echo '<link rel="canonical" href="' . esc_url( $url ) . '" />' . "\n";
3613
	}
3614
}
3615
3616
/**
3617
 * Returns a shortlink for a post, page, attachment, or site.
3618
 *
3619
 * This function exists to provide a shortlink tag that all themes and plugins can target.
3620
 * A plugin must hook in to provide the actual shortlinks. Default shortlink support is
3621
 * limited to providing ?p= style links for posts. Plugins can short-circuit this function
3622
 * via the {@see 'pre_get_shortlink'} filter or filter the output via the {@see 'get_shortlink'}
3623
 * filter.
3624
 *
3625
 * @since 3.0.0.
3626
 *
3627
 * @param int    $id          Optional. A post or site id. Default is 0, which means the current post or site.
3628
 * @param string $context     Optional. Whether the id is a 'site' id, 'post' id, or 'media' id. If 'post',
3629
 *                            the post_type of the post is consulted. If 'query', the current query is consulted
3630
 *                            to determine the id and context. Default 'post'.
3631
 * @param bool   $allow_slugs Optional. Whether to allow post slugs in the shortlink. It is up to the plugin how
3632
 *                            and whether to honor this. Default true.
3633
 * @return string A shortlink or an empty string if no shortlink exists for the requested resource or if shortlinks
3634
 *                are not enabled.
3635
 */
3636
function wp_get_shortlink( $id = 0, $context = 'post', $allow_slugs = true ) {
3637
	/**
3638
	 * Filters whether to preempt generating a shortlink for the given post.
3639
	 *
3640
	 * Passing a truthy value to the filter will effectively short-circuit the
3641
	 * shortlink-generation process, returning that value instead.
3642
	 *
3643
	 * @since 3.0.0
3644
	 *
3645
	 * @param bool|string $return      Short-circuit return value. Either false or a URL string.
3646
	 * @param int         $id          Post ID, or 0 for the current post.
3647
	 * @param string      $context     The context for the link. One of 'post' or 'query',
3648
	 * @param bool        $allow_slugs Whether to allow post slugs in the shortlink.
3649
	 */
3650
	$shortlink = apply_filters( 'pre_get_shortlink', false, $id, $context, $allow_slugs );
3651
3652
	if ( false !== $shortlink ) {
3653
		return $shortlink;
3654
	}
3655
3656
	$post_id = 0;
3657
	if ( 'query' == $context && is_singular() ) {
3658
		$post_id = get_queried_object_id();
3659
		$post = get_post( $post_id );
3660
	} elseif ( 'post' == $context ) {
3661
		$post = get_post( $id );
3662
		if ( ! empty( $post->ID ) )
3663
			$post_id = $post->ID;
3664
	}
3665
3666
	$shortlink = '';
3667
3668
	// Return p= link for all public post types.
3669
	if ( ! empty( $post_id ) ) {
3670
		$post_type = get_post_type_object( $post->post_type );
0 ignored issues
show
The variable $post 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...
3671
3672
		if ( 'page' === $post->post_type && $post->ID == get_option( 'page_on_front' ) && 'page' == get_option( 'show_on_front' ) ) {
3673
			$shortlink = home_url( '/' );
3674
		} elseif ( $post_type->public ) {
3675
			$shortlink = home_url( '?p=' . $post_id );
3676
		}
3677
	}
3678
3679
	/**
3680
	 * Filters the shortlink for a post.
3681
	 *
3682
	 * @since 3.0.0
3683
	 *
3684
	 * @param string $shortlink   Shortlink URL.
3685
	 * @param int    $id          Post ID, or 0 for the current post.
3686
	 * @param string $context     The context for the link. One of 'post' or 'query',
3687
	 * @param bool   $allow_slugs Whether to allow post slugs in the shortlink. Not used by default.
3688
	 */
3689
	return apply_filters( 'get_shortlink', $shortlink, $id, $context, $allow_slugs );
3690
}
3691
3692
/**
3693
 * Injects rel=shortlink into the head if a shortlink is defined for the current page.
3694
 *
3695
 * Attached to the {@see 'wp_head'} action.
3696
 *
3697
 * @since 3.0.0
3698
 */
3699
function wp_shortlink_wp_head() {
3700
	$shortlink = wp_get_shortlink( 0, 'query' );
3701
3702
	if ( empty( $shortlink ) )
3703
		return;
3704
3705
	echo "<link rel='shortlink' href='" . esc_url( $shortlink ) . "' />\n";
3706
}
3707
3708
/**
3709
 * Sends a Link: rel=shortlink header if a shortlink is defined for the current page.
3710
 *
3711
 * Attached to the {@see 'wp'} action.
3712
 *
3713
 * @since 3.0.0
3714
 */
3715
function wp_shortlink_header() {
3716
	if ( headers_sent() )
3717
		return;
3718
3719
	$shortlink = wp_get_shortlink(0, 'query');
3720
3721
	if ( empty($shortlink) )
3722
		return;
3723
3724
	header('Link: <' . $shortlink . '>; rel=shortlink', false);
3725
}
3726
3727
/**
3728
 * Displays the shortlink for a post.
3729
 *
3730
 * Must be called from inside "The Loop"
3731
 *
3732
 * Call like the_shortlink( __( 'Shortlinkage FTW' ) )
3733
 *
3734
 * @since 3.0.0
3735
 *
3736
 * @param string $text   Optional The link text or HTML to be displayed. Defaults to 'This is the short link.'
3737
 * @param string $title  Optional The tooltip for the link. Must be sanitized. Defaults to the sanitized post title.
3738
 * @param string $before Optional HTML to display before the link. Default empty.
3739
 * @param string $after  Optional HTML to display after the link. Default empty.
3740
 */
3741
function the_shortlink( $text = '', $title = '', $before = '', $after = '' ) {
3742
	$post = get_post();
3743
3744
	if ( empty( $text ) )
3745
		$text = __('This is the short link.');
3746
3747
	if ( empty( $title ) )
3748
		$title = the_title_attribute( array( 'echo' => false ) );
3749
3750
	$shortlink = wp_get_shortlink( $post->ID );
3751
3752
	if ( !empty( $shortlink ) ) {
3753
		$link = '<a rel="shortlink" href="' . esc_url( $shortlink ) . '" title="' . $title . '">' . $text . '</a>';
3754
3755
		/**
3756
		 * Filters the short link anchor tag for a post.
3757
		 *
3758
		 * @since 3.0.0
3759
		 *
3760
		 * @param string $link      Shortlink anchor tag.
3761
		 * @param string $shortlink Shortlink URL.
3762
		 * @param string $text      Shortlink's text.
3763
		 * @param string $title     Shortlink's title attribute.
3764
		 */
3765
		$link = apply_filters( 'the_shortlink', $link, $shortlink, $text, $title );
3766
		echo $before, $link, $after;
3767
	}
3768
}
3769
3770
3771
/**
3772
 * Retrieves the avatar URL.
3773
 *
3774
 * @since 4.2.0
3775
 *
3776
 * @param mixed $id_or_email The Gravatar to retrieve a URL for. Accepts a user_id, gravatar md5 hash,
3777
 *                           user email, WP_User object, WP_Post object, or WP_Comment object.
3778
 * @param array $args {
0 ignored issues
show
Should the type for parameter $args not be array|null? Also, consider making the array more specific, something like array<String>, or String[].

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive. In addition it looks for parameters that have the generic type array and suggests a stricter type like array<String>.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
3779
 *     Optional. Arguments to return instead of the default arguments.
3780
 *
3781
 *     @type int    $size           Height and width of the avatar in pixels. Default 96.
3782
 *     @type string $default        URL for the default image or a default type. Accepts '404' (return
3783
 *                                  a 404 instead of a default image), 'retro' (8bit), 'monsterid' (monster),
3784
 *                                  'wavatar' (cartoon face), 'indenticon' (the "quilt"), 'mystery', 'mm',
3785
 *                                  or 'mysteryman' (The Oyster Man), 'blank' (transparent GIF), or
3786
 *                                  'gravatar_default' (the Gravatar logo). Default is the value of the
3787
 *                                  'avatar_default' option, with a fallback of 'mystery'.
3788
 *     @type bool   $force_default  Whether to always show the default image, never the Gravatar. Default false.
3789
 *     @type string $rating         What rating to display avatars up to. Accepts 'G', 'PG', 'R', 'X', and are
3790
 *                                  judged in that order. Default is the value of the 'avatar_rating' option.
3791
 *     @type string $scheme         URL scheme to use. See set_url_scheme() for accepted values.
3792
 *                                  Default null.
3793
 *     @type array  $processed_args When the function returns, the value will be the processed/sanitized $args
3794
 *                                  plus a "found_avatar" guess. Pass as a reference. Default null.
3795
 * }
3796
 * @return false|string The URL of the avatar we found, or false if we couldn't find an avatar.
3797
 */
3798
function get_avatar_url( $id_or_email, $args = null ) {
3799
	$args = get_avatar_data( $id_or_email, $args );
3800
	return $args['url'];
3801
}
3802
3803
/**
3804
 * Retrieves default data about the avatar.
3805
 *
3806
 * @since 4.2.0
3807
 *
3808
 * @param mixed $id_or_email The Gravatar to retrieve. Accepts a user_id, gravatar md5 hash,
3809
 *                            user email, WP_User object, WP_Post object, or WP_Comment object.
3810
 * @param array $args {
0 ignored issues
show
Should the type for parameter $args not be array|null? Also, consider making the array more specific, something like array<String>, or String[].

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive. In addition it looks for parameters that have the generic type array and suggests a stricter type like array<String>.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
3811
 *     Optional. Arguments to return instead of the default arguments.
3812
 *
3813
 *     @type int    $size           Height and width of the avatar image file in pixels. Default 96.
3814
 *     @type int    $height         Display height of the avatar in pixels. Defaults to $size.
3815
 *     @type int    $width          Display width of the avatar in pixels. Defaults to $size.
3816
 *     @type string $default        URL for the default image or a default type. Accepts '404' (return
3817
 *                                  a 404 instead of a default image), 'retro' (8bit), 'monsterid' (monster),
3818
 *                                  'wavatar' (cartoon face), 'indenticon' (the "quilt"), 'mystery', 'mm',
3819
 *                                  or 'mysteryman' (The Oyster Man), 'blank' (transparent GIF), or
3820
 *                                  'gravatar_default' (the Gravatar logo). Default is the value of the
3821
 *                                  'avatar_default' option, with a fallback of 'mystery'.
3822
 *     @type bool   $force_default  Whether to always show the default image, never the Gravatar. Default false.
3823
 *     @type string $rating         What rating to display avatars up to. Accepts 'G', 'PG', 'R', 'X', and are
3824
 *                                  judged in that order. Default is the value of the 'avatar_rating' option.
3825
 *     @type string $scheme         URL scheme to use. See set_url_scheme() for accepted values.
3826
 *                                  Default null.
3827
 *     @type array  $processed_args When the function returns, the value will be the processed/sanitized $args
3828
 *                                  plus a "found_avatar" guess. Pass as a reference. Default null.
3829
 *     @type string $extra_attr     HTML attributes to insert in the IMG element. Is not sanitized. Default empty.
3830
 * }
3831
 * @return array $processed_args {
3832
 *     Along with the arguments passed in `$args`, this will contain a couple of extra arguments.
3833
 *
3834
 *     @type bool   $found_avatar True if we were able to find an avatar for this user,
3835
 *                                false or not set if we couldn't.
3836
 *     @type string $url          The URL of the avatar we found.
3837
 * }
3838
 */
3839
function get_avatar_data( $id_or_email, $args = null ) {
3840
	$args = wp_parse_args( $args, array(
0 ignored issues
show
It seems like $args can also be of type null; however, wp_parse_args() does only seem to accept string|array|object, 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...
3841
		'size'           => 96,
3842
		'height'         => null,
3843
		'width'          => null,
3844
		'default'        => get_option( 'avatar_default', 'mystery' ),
3845
		'force_default'  => false,
3846
		'rating'         => get_option( 'avatar_rating' ),
3847
		'scheme'         => null,
3848
		'processed_args' => null, // if used, should be a reference
3849
		'extra_attr'     => '',
3850
	) );
3851
3852 View Code Duplication
	if ( is_numeric( $args['size'] ) ) {
3853
		$args['size'] = absint( $args['size'] );
3854
		if ( ! $args['size'] ) {
3855
			$args['size'] = 96;
3856
		}
3857
	} else {
3858
		$args['size'] = 96;
3859
	}
3860
3861 View Code Duplication
	if ( is_numeric( $args['height'] ) ) {
3862
		$args['height'] = absint( $args['height'] );
3863
		if ( ! $args['height'] ) {
3864
			$args['height'] = $args['size'];
3865
		}
3866
	} else {
3867
		$args['height'] = $args['size'];
3868
	}
3869
3870 View Code Duplication
	if ( is_numeric( $args['width'] ) ) {
3871
		$args['width'] = absint( $args['width'] );
3872
		if ( ! $args['width'] ) {
3873
			$args['width'] = $args['size'];
3874
		}
3875
	} else {
3876
		$args['width'] = $args['size'];
3877
	}
3878
3879
	if ( empty( $args['default'] ) ) {
3880
		$args['default'] = get_option( 'avatar_default', 'mystery' );
3881
	}
3882
3883
	switch ( $args['default'] ) {
3884
		case 'mm' :
3885
		case 'mystery' :
3886
		case 'mysteryman' :
3887
			$args['default'] = 'mm';
3888
			break;
3889
		case 'gravatar_default' :
3890
			$args['default'] = false;
3891
			break;
3892
	}
3893
3894
	$args['force_default'] = (bool) $args['force_default'];
3895
3896
	$args['rating'] = strtolower( $args['rating'] );
3897
3898
	$args['found_avatar'] = false;
3899
3900
	/**
3901
	 * Filters whether to retrieve the avatar URL early.
3902
	 *
3903
	 * Passing a non-null value in the 'url' member of the return array will
3904
	 * effectively short circuit get_avatar_data(), passing the value through
3905
	 * the {@see 'get_avatar_data'} filter and returning early.
3906
	 *
3907
	 * @since 4.2.0
3908
	 *
3909
	 * @param array  $args        Arguments passed to get_avatar_data(), after processing.
3910
	 * @param mixed  $id_or_email The Gravatar to retrieve. Accepts a user_id, gravatar md5 hash,
3911
	 *                            user email, WP_User object, WP_Post object, or WP_Comment object.
3912
	 */
3913
	$args = apply_filters( 'pre_get_avatar_data', $args, $id_or_email );
3914
3915
	if ( isset( $args['url'] ) && ! is_null( $args['url'] ) ) {
3916
		/** This filter is documented in wp-includes/link-template.php */
3917
		return apply_filters( 'get_avatar_data', $args, $id_or_email );
3918
	}
3919
3920
	$email_hash = '';
3921
	$user = $email = false;
3922
3923
	if ( is_object( $id_or_email ) && isset( $id_or_email->comment_ID ) ) {
3924
		$id_or_email = get_comment( $id_or_email );
3925
	}
3926
3927
	// Process the user identifier.
3928
	if ( is_numeric( $id_or_email ) ) {
3929
		$user = get_user_by( 'id', absint( $id_or_email ) );
3930
	} elseif ( is_string( $id_or_email ) ) {
3931
		if ( strpos( $id_or_email, '@md5.gravatar.com' ) ) {
3932
			// md5 hash
3933
			list( $email_hash ) = explode( '@', $id_or_email );
3934
		} else {
3935
			// email address
3936
			$email = $id_or_email;
3937
		}
3938
	} elseif ( $id_or_email instanceof WP_User ) {
3939
		// User Object
3940
		$user = $id_or_email;
3941
	} elseif ( $id_or_email instanceof WP_Post ) {
3942
		// Post Object
3943
		$user = get_user_by( 'id', (int) $id_or_email->post_author );
3944
	} elseif ( $id_or_email instanceof WP_Comment ) {
3945
		/**
3946
		 * Filters the list of allowed comment types for retrieving avatars.
3947
		 *
3948
		 * @since 3.0.0
3949
		 *
3950
		 * @param array $types An array of content types. Default only contains 'comment'.
3951
		 */
3952
		$allowed_comment_types = apply_filters( 'get_avatar_comment_types', array( 'comment' ) );
3953
		if ( ! empty( $id_or_email->comment_type ) && ! in_array( $id_or_email->comment_type, (array) $allowed_comment_types ) ) {
3954
			$args['url'] = false;
3955
			/** This filter is documented in wp-includes/link-template.php */
3956
			return apply_filters( 'get_avatar_data', $args, $id_or_email );
3957
		}
3958
3959
		if ( ! empty( $id_or_email->user_id ) ) {
3960
			$user = get_user_by( 'id', (int) $id_or_email->user_id );
3961
		}
3962
		if ( ( ! $user || is_wp_error( $user ) ) && ! empty( $id_or_email->comment_author_email ) ) {
3963
			$email = $id_or_email->comment_author_email;
3964
		}
3965
	}
3966
3967
	if ( ! $email_hash ) {
3968
		if ( $user ) {
3969
			$email = $user->user_email;
3970
		}
3971
3972
		if ( $email ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $email of type string|false is loosely compared to true; 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...
3973
			$email_hash = md5( strtolower( trim( $email ) ) );
3974
		}
3975
	}
3976
3977
	if ( $email_hash ) {
3978
		$args['found_avatar'] = true;
3979
		$gravatar_server = hexdec( $email_hash[0] ) % 3;
3980
	} else {
3981
		$gravatar_server = rand( 0, 2 );
3982
	}
3983
3984
	$url_args = array(
3985
		's' => $args['size'],
3986
		'd' => $args['default'],
3987
		'f' => $args['force_default'] ? 'y' : false,
3988
		'r' => $args['rating'],
3989
	);
3990
3991
	if ( is_ssl() ) {
3992
		$url = 'https://secure.gravatar.com/avatar/' . $email_hash;
3993
	} else {
3994
		$url = sprintf( 'http://%d.gravatar.com/avatar/%s', $gravatar_server, $email_hash );
3995
	}
3996
3997
	$url = add_query_arg(
3998
		rawurlencode_deep( array_filter( $url_args ) ),
3999
		set_url_scheme( $url, $args['scheme'] )
4000
	);
4001
4002
	/**
4003
	 * Filters the avatar URL.
4004
	 *
4005
	 * @since 4.2.0
4006
	 *
4007
	 * @param string $url         The URL of the avatar.
4008
	 * @param mixed  $id_or_email The Gravatar to retrieve. Accepts a user_id, gravatar md5 hash,
4009
	 *                            user email, WP_User object, WP_Post object, or WP_Comment object.
4010
	 * @param array  $args        Arguments passed to get_avatar_data(), after processing.
4011
	 */
4012
	$args['url'] = apply_filters( 'get_avatar_url', $url, $id_or_email, $args );
4013
4014
	/**
4015
	 * Filters the avatar data.
4016
	 *
4017
	 * @since 4.2.0
4018
	 *
4019
	 * @param array  $args        Arguments passed to get_avatar_data(), after processing.
4020
	 * @param mixed  $id_or_email The Gravatar to retrieve. Accepts a user_id, gravatar md5 hash,
4021
	 *                            user email, WP_User object, WP_Post object, or WP_Comment object.
4022
	 */
4023
	return apply_filters( 'get_avatar_data', $args, $id_or_email );
4024
}
4025
4026
/**
4027
 * Retrieves the URL of a file in the theme.
4028
 *
4029
 * Searches in the stylesheet directory before the template directory so themes
4030
 * which inherit from a parent theme can just override one file.
4031
 *
4032
 * @since 4.7.0
4033
 *
4034
 * @param string $file Optional. File to search for in the stylesheet directory.
4035
 * @return string The URL of the file.
4036
 */
4037 View Code Duplication
function get_theme_file_uri( $file = '' ) {
0 ignored issues
show
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
4038
	$file = ltrim( $file, '/' );
4039
4040
	if ( empty( $file ) ) {
4041
		$url = get_stylesheet_directory_uri();
4042
	} elseif ( file_exists( get_stylesheet_directory() . '/' . $file ) ) {
4043
		$url = get_stylesheet_directory_uri() . '/' . $file;
4044
	} else {
4045
		$url = get_template_directory_uri() . '/' . $file;
4046
	}
4047
4048
	/**
4049
	 * Filters the URL to a file in the theme.
4050
	 *
4051
	 * @since 4.7.0
4052
	 *
4053
	 * @param string $url  The file URL.
4054
	 * @param string $file The requested file to search for.
4055
	 */
4056
	return apply_filters( 'theme_file_uri', $url, $file );
4057
}
4058
4059
/**
4060
 * Retrieves the URL of a file in the parent theme.
4061
 *
4062
 * @since 4.7.0
4063
 *
4064
 * @param string $file Optional. File to return the URL for in the template directory.
4065
 * @return string The URL of the file.
4066
 */
4067 View Code Duplication
function get_parent_theme_file_uri( $file = '' ) {
0 ignored issues
show
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
4068
	$file = ltrim( $file, '/' );
4069
4070
	if ( empty( $file ) ) {
4071
		$url = get_template_directory_uri();
4072
	} else {
4073
		$url = get_template_directory_uri() . '/' . $file;
4074
	}
4075
4076
	/**
4077
	 * Filters the URL to a file in the parent theme.
4078
	 *
4079
	 * @since 4.7.0
4080
	 *
4081
	 * @param string $url  The file URL.
4082
	 * @param string $file The requested file to search for.
4083
	 */
4084
	return apply_filters( 'parent_theme_file_uri', $url, $file );
4085
}
4086
4087
/**
4088
 * Retrieves the path of a file in the theme.
4089
 *
4090
 * Searches in the stylesheet directory before the template directory so themes
4091
 * which inherit from a parent theme can just override one file.
4092
 *
4093
 * @since 4.7.0
4094
 *
4095
 * @param string $file Optional. File to search for in the stylesheet directory.
4096
 * @return string The path of the file.
4097
 */
4098 View Code Duplication
function get_theme_file_path( $file = '' ) {
0 ignored issues
show
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
4099
	$file = ltrim( $file, '/' );
4100
4101
	if ( empty( $file ) ) {
4102
		$path = get_stylesheet_directory();
4103
	} elseif ( file_exists( get_stylesheet_directory() . '/' . $file ) ) {
4104
		$path = get_stylesheet_directory() . '/' . $file;
4105
	} else {
4106
		$path = get_template_directory() . '/' . $file;
4107
	}
4108
4109
	/**
4110
	 * Filters the path to a file in the theme.
4111
	 *
4112
	 * @since 4.7.0
4113
	 *
4114
	 * @param string $path The file path.
4115
	 * @param string $file The requested file to search for.
4116
	 */
4117
	return apply_filters( 'theme_file_path', $path, $file );
4118
}
4119
4120
/**
4121
 * Retrieves the path of a file in the parent theme.
4122
 *
4123
 * @since 4.7.0
4124
 *
4125
 * @param string $file Optional. File to return the path for in the template directory.
4126
 * @return string The path of the file.
4127
 */
4128 View Code Duplication
function get_parent_theme_file_path( $file = '' ) {
0 ignored issues
show
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
4129
	$file = ltrim( $file, '/' );
4130
4131
	if ( empty( $file ) ) {
4132
		$path = get_template_directory();
4133
	} else {
4134
		$path = get_template_directory() . '/' . $file;
4135
	}
4136
4137
	/**
4138
	 * Filters the path to a file in the parent theme.
4139
	 *
4140
	 * @since 4.7.0
4141
	 *
4142
	 * @param string $path The file path.
4143
	 * @param string $file The requested file to search for.
4144
	 */
4145
	return apply_filters( 'parent_theme_file_path', $path, $file );
4146
}
4147