Current File : /home/honehdyv/readbtooom.com/wp-content/plugins/autodescription/inc/classes/generate-url.class.php |
<?php
/**
* @package The_SEO_Framework\Classes\Facade\Generate_Url
* @subpackage The_SEO_Framework\Getters\URL
*/
namespace The_SEO_Framework;
\defined( 'THE_SEO_FRAMEWORK_PRESENT' ) or die;
/**
* The SEO Framework plugin
* Copyright (C) 2015 - 2023 Sybre Waaijer, CyberWire B.V. (https://cyberwire.nl/)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 as published
* by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Class The_SEO_Framework\Generate_Url
*
* Generates URL and permalink SEO data based on content.
*
* @since 2.8.0
*/
class Generate_Url extends Generate_Title {
/**
* Determines if the given page has a custom canonical URL.
*
* @since 3.2.4
* @since 4.2.0 1. Now also detects canonical URLs for taxonomies.
* 2. Now also detects canonical URLs for PTAs.
* 3. Now supports the `$args['pta']` index.
*
* @param null|array $args The canonical URL arguments, leave null to autodetermine query : {
* int $id The Post, Page or Term ID to generate the URL for.
* string $taxonomy The taxonomy.
* }
* @return bool
*/
public function has_custom_canonical_url( $args = null ) {
if ( null === $args ) {
if ( $this->is_singular() ) {
$has = $this->get_singular_custom_canonical_url();
} elseif ( $this->is_term_meta_capable() ) {
$has = $this->get_taxonomical_custom_canonical_url();
} elseif ( \is_post_type_archive() ) {
$has = $this->get_post_type_archive_custom_canonical_url();
} else {
$has = false;
}
} else {
$this->fix_generation_args( $args );
if ( $args['taxonomy'] ) {
$has = $this->get_taxonomical_custom_canonical_url( $args['id'] );
} elseif ( $args['pta'] ) {
$has = $this->get_post_type_archive_custom_canonical_url( $args['pta'] );
} else {
$has = $this->get_singular_custom_canonical_url( $args['id'] );
}
}
return (bool) $has;
}
/**
* Caches and returns the current URL.
* Memoizes the return value.
*
* @since 3.0.0
*
* @return string The current URL.
*/
public function get_current_canonical_url() {
return memo() ?? memo( $this->get_canonical_url() );
}
/**
* Caches and returns the current permalink.
* This link excludes any pagination. Great for structured data.
*
* Does not work for unregistered pages, like search, 404, date, author, and CPTA.
* Memoizes the return value.
*
* @since 3.0.0
* @since 3.1.0 Now properly generates taxonomical URLs.
*
* @return string The current permalink.
*/
public function get_current_permalink() {
return memo() ?? memo(
$this->get_canonical_url( [
'id' => $this->get_the_real_ID(),
'taxonomy' => $this->get_current_taxonomy(),
] )
);
}
/**
* Caches and returns the homepage URL.
* Memoizes the return value.
*
* @since 3.0.0
*
* @return string The home URL.
*/
public function get_homepage_permalink() {
return memo() ?? memo(
$this->get_canonical_url( [ 'id' => $this->get_the_front_page_ID() ] )
);
}
/**
* Returns a canonical URL based on parameters.
* The URL will never be paginated.
*
* @since 3.0.0
* @since 4.0.0 Now preemptively fixes the generation arguments, for easier implementation.
* @since 4.2.0 Now supports the `$args['pta']` index.
* @since 4.2.3 Marked for deprecation.
* @todo deprecate me.
* @uses $this->get_canonical_url()
*
* @param array $args The canonical URL arguments : {
* int $id The Post, Page or Term ID to generate the URL for.
* string $taxonomy The taxonomy.
* string $pta The pta.
* bool $get_custom_field Whether to get custom canonical URLs from user settings.
* }
* @return string The canonical URL, if any.
*/
public function create_canonical_url( $args = [] ) {
return $this->get_canonical_url( $args );
}
/**
* Returns the current canonical URL.
* Removes pagination if the URL isn't obtained via the query.
*
* @since 3.0.0
* @since 4.2.0 Now supports the `$args['pta']` index.
* @since 4.2.3 Now accepts arguments publicly.
*
* @param array|null $args The canonical URL arguments, leave null to autodetermine query : {
* int $id The Post, Page or Term ID to generate the URL for.
* string $taxonomy The taxonomy.
* string $pta The pta.
* bool $get_custom_field Whether to get custom canonical URLs from user settings.
* }
* @return string The canonical URL, if any.
*/
public function get_canonical_url( $args = null ) {
if ( $args ) {
$args += [
'id' => 0,
'taxonomy' => '',
'pta' => '',
'get_custom_field' => false,
];
// See and use `$this->get_canonical_url()` instead.
$url = $this->build_canonical_url( $args );
if ( $args['id'] === $this->get_the_real_ID() )
$url = $this->remove_pagination_from_url( $url );
} else {
$url = $this->generate_canonical_url();
}
if ( ! $url )
return '';
if ( $this->matches_this_domain( $url ) )
$url = $this->set_preferred_url_scheme( $url );
return $this->clean_canonical_url( $url );
}
/**
* Builds canonical URL from input arguments.
*
* @since 3.0.0
* @since 3.2.2 Now tests for the static frontpage metadata prior getting fallback data.
* @since 4.0.0 Can now fetch custom canonical URL for terms.
* @since 4.2.0 Now supports the `$args['pta']` index.
* @see $this->get_canonical_url()
*
* @param array $args Required. Use $this->get_canonical_url().
* @return string The canonical URL.
*/
protected function build_canonical_url( $args ) {
$url = '';
if ( $args['taxonomy'] ) {
$url = (
$args['get_custom_field']
? $this->get_taxonomical_custom_canonical_url( $args['id'] )
: ''
) ?: $this->get_taxonomical_canonical_url( $args['id'], $args['taxonomy'] );
} elseif ( $args['pta'] ) {
$url = (
$args['get_custom_field']
? $this->get_post_type_archive_custom_canonical_url( $args['pta'] )
: ''
) ?: $this->get_post_type_archive_canonical_url( $args['pta'] );
} else {
if ( $this->is_static_frontpage( $args['id'] ) ) {
$url = (
$args['get_custom_field']
? $this->get_singular_custom_canonical_url( $args['id'] )
: ''
) ?: $this->get_raw_home_canonical_url();
} elseif ( $this->is_real_front_page_by_id( $args['id'] ) ) {
$url = $this->get_raw_home_canonical_url();
} elseif ( $args['id'] ) {
$url = (
$args['get_custom_field']
? $this->get_singular_custom_canonical_url( $args['id'] )
: ''
) ?: $this->get_singular_canonical_url( $args['id'] );
}
}
return $url;
}
/**
* Generates canonical URL from current query.
*
* @since 3.0.0
* @since 4.0.0 Can now fetch custom canonical URL for terms.
* @since 4.2.0 1. No longer relies on passing through the page ID.
* 2. Can now return custom canonical URLs for post type archives.
* @see $this->get_canonical_url()
*
* @return string The canonical URL.
*/
protected function generate_canonical_url() {
if ( $this->is_real_front_page() ) {
if ( $this->has_page_on_front() ) {
$url = $this->get_singular_custom_canonical_url()
?: $this->get_home_canonical_url();
} else {
$url = $this->get_home_canonical_url();
}
} elseif ( $this->is_singular() ) {
$url = $this->get_singular_custom_canonical_url()
?: $this->get_singular_canonical_url();
} elseif ( $this->is_archive() ) {
if ( $this->is_term_meta_capable() ) {
$url = $this->get_taxonomical_custom_canonical_url()
?: $this->get_taxonomical_canonical_url();
} elseif ( \is_post_type_archive() ) {
$url = $this->get_post_type_archive_custom_canonical_url()
?: $this->get_post_type_archive_canonical_url();
} elseif ( $this->is_author() ) {
$url = $this->get_author_canonical_url();
} elseif ( \is_date() ) {
if ( \is_day() ) {
$url = $this->get_date_canonical_url( \get_query_var( 'year' ), \get_query_var( 'monthnum' ), \get_query_var( 'day' ) );
} elseif ( \is_month() ) {
$url = $this->get_date_canonical_url( \get_query_var( 'year' ), \get_query_var( 'monthnum' ) );
} elseif ( \is_year() ) {
$url = $this->get_date_canonical_url( \get_query_var( 'year' ) );
}
}
} elseif ( $this->is_search() ) {
$url = $this->get_search_canonical_url();
}
return $url ?? '';
}
/**
* Cleans canonical URL.
* Looks at permalink settings to determine roughness of escaping.
*
* @since 3.0.0
*
* @param string $url A fully qualified URL.
* @return string A fully qualified clean URL.
*/
public function clean_canonical_url( $url ) {
if ( $this->pretty_permalinks )
return \esc_url( $url, [ 'https', 'http' ] );
// Keep the &'s more readable when using query-parameters.
return \esc_url_raw( $url, [ 'https', 'http' ] );
}
/**
* Returns home canonical URL.
* Automatically adds pagination if the ID matches the query.
*
* @since 3.0.0
* @since 3.2.4 1. Now adds a slash to the home URL when it's a root URL.
* 2. Now skips slashing when queries have been appended to the URL.
* 3. Home-as-page pagination is now supported.
*
* @return string The home canonical URL.
*/
public function get_home_canonical_url() {
$url = $this->get_raw_home_canonical_url();
if ( ! $url ) return '';
$query_id = $this->get_the_real_ID();
if ( $this->has_page_on_front() ) {
if ( $this->is_static_frontpage( $query_id ) ) {
// Yes, we use the pagination base for the static frontpage.
$url = $this->add_pagination_to_url( $url, $this->page(), true );
}
} elseif ( (int) \get_option( 'page_for_posts' ) === $query_id ) {
$url = $this->add_pagination_to_url( $url, $this->paged(), true );
}
return $this->slash_root_url( $url );
}
/**
* Returns home canonical URL without query considerations.
*
* @since 4.2.0
* @since 4.2.2 Now adds a trailing slash if the URL is a root URL.
*
* @return string The home canonical URL without query considerations.
*/
public function get_raw_home_canonical_url() {
return $this->slash_root_url( $this->set_preferred_url_scheme( $this->get_home_url() ) );
}
/**
* Returns singular custom field's canonical URL.
*
* @since 3.0.0
* @since 4.2.0 The first parameter is now optional.
*
* @param int|null $id The page ID.
* @return string The custom canonical URL, if any.
*/
public function get_singular_custom_canonical_url( $id = null ) {
return $this->get_post_meta_item( '_genesis_canonical_uri', $id ) ?: '';
}
/**
* Returns singular canonical URL.
*
* @since 3.0.0
* @since 3.1.0 Added WC Shop and WP Blog (as page) pagination integration via $this->paged().
* @since 3.2.4 Removed pagination support for singular posts, as the SEO attack is now mitigated via WordPress.
* @since 4.0.5 Now passes the `$id` to `is_singular_archive()`
* @since 4.2.0 1. Added memoization.
* 2. When the $id isn't set, the URL won't get tested for pagination issues.
* @since 4.2.3 Rectified pagination removal issue. No longer adds pagination when $post_id is null.
*
* @param int|null $post_id The page ID. Leave null to autodetermine.
* @return string The custom canonical URL, if any.
*/
public function get_singular_canonical_url( $post_id = null ) {
// phpcs:ignore, WordPress.CodeAnalysis.AssignmentInCondition -- I know.
if ( null !== $memo = umemo( __METHOD__, null, $post_id ) ) return $memo;
$url = \wp_get_canonical_url(
$post_id ?? $this->get_the_real_ID()
);
if ( ! $url ) return '';
$page = \get_query_var( 'page', 1 ) ?: 1;
// Remove undesired/fake pagination. See: <https://core.trac.wordpress.org/ticket/37505>
if ( $page > 1 && $page !== $this->page() )
$url = $this->remove_pagination_from_url( $url, $page, false );
// Singular archives, like blog pages and shop pages, use the pagination base with 'paged'.
// wp_get_canonical_url() only tests 'page'. Fix that:
if ( null === $post_id && $this->is_singular_archive() )
$url = $this->add_pagination_to_url( $url, $this->paged(), true );
return umemo( __METHOD__, $url, $post_id );
}
/**
* Returns taxonomical custom field's canonical URL.
*
* @since 4.0.0
* @since 4.2.0 The first parameter is now optional.
*
* @param int $term_id The term ID.
* @return string The custom canonical URL, if any.
*/
public function get_taxonomical_custom_canonical_url( $term_id = null ) {
return $this->get_term_meta_item( 'canonical', $term_id ) ?: '';
}
/**
* Returns post type archive custom field's canonical URL.
*
* @since 4.2.0
*
* @param string $pta The post type.
* @return string The custom canonical URL, if any.
*/
public function get_post_type_archive_custom_canonical_url( $pta = '' ) {
return $this->get_post_type_archive_meta_item( 'canonical', $pta ) ?: '';
}
/**
* Returns taxonomical canonical URL.
* Automatically adds pagination if the ID matches the query.
*
* @since 3.0.0
* @since 4.0.0 1. Renamed from "get_taxonomial_canonical_url" (note the typo)
* 2. Now works on the admin-screens.
* @since 4.2.0 1. Added memoization.
* 2. The parameters are now optional.
*
* @param int|null $term_id The term ID. Leave null to autodetermine.
* @param string $taxonomy The taxonomy. Leave empty to autodetermine.
* @return string The taxonomical canonical URL, if any.
*/
public function get_taxonomical_canonical_url( $term_id = null, $taxonomy = '' ) {
// phpcs:ignore, WordPress.CodeAnalysis.AssignmentInCondition -- I know.
if ( null !== $memo = umemo( __METHOD__, null, $term_id, $taxonomy ) )
return $memo;
$url = \get_term_link( $term_id ?: $this->get_the_real_ID(), $taxonomy );
if ( \is_wp_error( $url ) )
return umemo( __METHOD__, '', $term_id, $taxonomy );
if ( null === $term_id )
$url = $this->add_pagination_to_url( $url, $this->paged(), true );
return umemo( __METHOD__, $url, $term_id, $taxonomy );
}
/**
* Returns post type archive canonical URL.
*
* @since 3.0.0
* @since 4.0.0 1. Deprecated first parameter as integer. Use strings or null.
* 2. Now forwards post type object calling to WordPress's function.
* @since 4.2.0 1. Now correctly adds pagination to the URL.
* 2. Removed argument type deprecation doing it wrong warning.
*
* @param null|string $post_type The post type archive's post type.
* Leave null to autodetermine query and allow pagination.
* @return string The post type archive canonical URL, if any.
*/
public function get_post_type_archive_canonical_url( $post_type = null ) {
$url = \get_post_type_archive_link( $post_type ?? $this->get_current_post_type() );
if ( ! $url ) return '';
return null === $post_type
? $this->add_pagination_to_url( $url, $this->paged(), true )
: $url;
}
/**
* Returns author canonical URL.
* Automatically adds pagination if the ID matches the query.
*
* @since 3.0.0
* @since 4.2.0 1. The first parameter is now optional.
* 2. When the $id isn't set, the URL won't get tested for pagination issues.
*
* @param int|null $id The author ID. Leave null to autodetermine.
* @return string The author canonical URL, if any.
*/
public function get_author_canonical_url( $id = null ) {
$url = \get_author_posts_url( $id ?? $this->get_the_real_ID() );
if ( ! $url ) return '';
return null === $id
? $this->add_pagination_to_url( $url, $this->paged(), true )
: $url;
}
/**
* Returns date canonical URL.
* Automatically adds pagination if the date input matches the query.
*
* @since 3.0.0
*
* @param int $year The year.
* @param int $month The month.
* @param int $day The day.
* @return string The author canonical URL, if any.
*/
public function get_date_canonical_url( $year, $month = null, $day = null ) {
if ( $day ) {
$link = \get_day_link( $year, $month, $day );
$_get = 'day';
} elseif ( $month ) {
$link = \get_month_link( $year, $month );
$_get = 'month';
$_get = 1;
} else {
$link = \get_year_link( $year );
$_get = 'year';
}
// Determine whether the input matches query.
$_paginate = true;
switch ( $_get ) {
case 'day':
$_day = \get_query_var( 'day' );
$_paginate = $_paginate && $_day == $day; // phpcs:ignore, WordPress.PHP.StrictComparisons.LooseComparison
// No break. Test month too.
case 'month':
$_month = \get_query_var( 'monthnum' );
$_paginate = $_paginate && $_month == $month; // phpcs:ignore, WordPress.PHP.StrictComparisons.LooseComparison
// No break. Test year too.
case 'year':
$_year = \get_query_var( 'year' );
$_paginate = $_paginate && $_year == $year; // phpcs:ignore, WordPress.PHP.StrictComparisons.LooseComparison
break;
}
if ( $_paginate ) {
// Adds pagination if input matches query.
$link = $this->add_pagination_to_url( $link, $this->paged(), true );
}
return $link;
}
/**
* Returns search canonical URL.
* Automatically adds pagination if the input matches the query.
*
* @since 3.0.0
* @since 3.1.0 1. The first parameter now defaults to null.
* 2. The search term is now matched with the input query if not set,
* instead of it being empty.
*
* @param string $search_query The search query. Mustn't be escaped.
* When left empty, the current query will be used.
* @return string The search link.
*/
public function get_search_canonical_url( $search_query = null ) {
$url = \get_search_link( $search_query ?? \get_search_query( false ) );
if ( ! $url ) return '';
return null === $search_query
? $this->add_pagination_to_url( $url, $this->paged(), true )
: $url;
}
/**
* Returns preferred $url scheme.
* Which can automatically be detected when not set, based on the site URL setting.
* Memoizes the return value.
*
* @since 3.0.0
* @since 4.0.0 Now gets the "automatic" scheme from the WordPress home URL.
*
* @return string The preferred URl scheme.
*/
public function get_preferred_scheme() {
// phpcs:ignore, WordPress.CodeAnalysis.AssignmentInCondition -- I know.
if ( null !== $memo = memo() ) return $memo;
$scheme = $this->get_option( 'canonical_scheme' );
switch ( $scheme ) :
case 'https':
case 'http':
break;
default:
case 'automatic':
$scheme = $this->detect_site_url_scheme();
break;
endswitch;
/**
* @since 2.8.0
* @param string $scheme The current URL scheme.
*/
return memo( (string) \apply_filters( 'the_seo_framework_preferred_url_scheme', $scheme ) );
}
/**
* Detects site's URL scheme from site options.
* Falls back to is_ssl() when the hom misconfigured via wp-config.php
*
* NOTE: Some (insecure, e.g. SP) implementations for the `WP_HOME` constant, where
* the scheme is interpreted from the request, may cause this to be unreliable.
* We're going to ignore those edge-cases; they're doing it wrong.
*
* However, should we output a notification? Or let them suffer until they use Monitor to find the issue for them?
* Yea, Monitor's great for that. Gibe moni plos.
*
* @since 4.0.0
*
* @return string The detected URl scheme, lowercase.
*/
public function detect_site_url_scheme() {
return strtolower( parse_url(
$this->get_home_url(),
PHP_URL_SCHEME
) ) ?: (
$this->is_ssl() ? 'https' : 'http'
);
}
/**
* Sets URL to preferred URL scheme.
* Does not sanitize output.
*
* @since 2.8.0
*
* @param string $url The URL to set scheme for.
* @return string The URL with the preferred scheme.
*/
public function set_preferred_url_scheme( $url ) {
return $this->set_url_scheme( $url, $this->get_preferred_scheme() );
}
/**
* Sets URL scheme for input URL.
* WordPress core function, without filter.
*
* @since 2.4.2
* @since 3.0.0 $use_filter now defaults to false.
* @since 3.1.0 The third parameter ($use_filter) is now $deprecated.
* @since 4.0.0 Removed the deprecated parameter.
*
* @param string $url Absolute url that includes a scheme.
* @param string $scheme Optional. Scheme to give $url. Currently 'http', 'https', 'login', 'login_post', 'admin', or 'relative'.
* @return string url with chosen scheme.
*/
public function set_url_scheme( $url, $scheme = null ) {
if ( empty( $scheme ) ) {
$scheme = $this->is_ssl() ? 'https' : 'http';
} elseif ( 'admin' === $scheme || 'login' === $scheme || 'login_post' === $scheme || 'rpc' === $scheme ) {
$scheme = $this->is_ssl() || \force_ssl_admin() ? 'https' : 'http';
} elseif ( 'http' !== $scheme && 'https' !== $scheme && 'relative' !== $scheme ) {
$scheme = $this->is_ssl() ? 'https' : 'http';
}
$url = $this->make_fully_qualified_url( $url );
if ( 'relative' === $scheme ) {
$url = ltrim( preg_replace( '/^\w+:\/\/[^\/]*/', '', $url ) );
if ( '/' === ( $url[0] ?? '' ) )
$url = '/' . ltrim( $url, "/ \t\n\r\0\x0B" );
} else {
$url = preg_replace( '#^\w+://#', $scheme . '://', $url );
}
return $url;
}
/**
* Adds pagination to input URL.
*
* @since 4.2.3
*
* @param string $url The fully qualified URL.
* @param int $page The page number. Should be bigger than 1 to paginate.
* @param bool $use_base Whether to use pagination base.
* If null, it will autodetermine.
* Should be true on archives and the homepage (blog and static!).
* False on singular post types.
* @return string The fully qualified URL with pagination.
*/
public function add_pagination_to_url( $url, $page = null, $use_base = null ) {
$page = $page ?? max( $this->paged(), $this->page() );
if ( $page < 2 )
return $url;
$use_base = $use_base ?? (
$this->is_real_front_page() || $this->is_archive() || $this->is_singular_archive() || $this->is_search()
);
if ( $this->pretty_permalinks ) {
$_query = parse_url( $url, PHP_URL_QUERY );
// Remove queries, add them back later.
if ( $_query )
$url = $this->s_url( $url );
static $base;
$base = $base ?: $GLOBALS['wp_rewrite']->pagination_base;
if ( $use_base ) {
$url = \user_trailingslashit( \trailingslashit( $url ) . "$base/$page", 'paged' );
} else {
$url = \user_trailingslashit( \trailingslashit( $url ) . $page, 'single_paged' );
}
if ( $_query )
$url = $this->append_url_query( $url, $_query );
} else {
if ( $use_base ) {
$url = \add_query_arg( 'paged', $page, $url );
} else {
$url = \add_query_arg( 'page', $page, $url );
}
}
return $url;
}
/**
* Adds pagination to input URL.
*
* @since 3.0.0
* @since 3.2.4 1. Now considers query arguments when using pretty permalinks.
* 2. The second and third parameters are now optional.
* @since 4.2.0 Now properly adds pagination to search links.
* @todo deprecate
*
* @param string $url The fully qualified URL.
* @param int $page The page number. Should be bigger than 1 to paginate.
* @param bool $use_base Whether to use pagination base.
* If null, it will autodetermine.
* Should be true on archives and the homepage (blog and static!).
* False on singular post types.
* @return string The fully qualified URL with pagination.
*/
public function add_url_pagination( $url, $page = null, $use_base = null ) {
return $this->add_pagination_to_url( $url, $page, $use_base );
}
/**
* Removes pagination from input URL.
* The URL must match this query if no second parameter is provided.
*
* @since 3.0.0
* @since 3.2.4 1. Now correctly removes the pagination base on singular post types.
* 2. The second parameter now accepts null or a value.
* 3. The third parameter is now changed to $use_base, from the archive pagination number.
* 4. Now supports pretty permalinks with query parameters.
* 5. Is now public.
* @since 4.1.2 Now correctly reappends query when pagination isn't removed.
* @since 4.2.0 Now properly removes pagination from search links.
*
* @param string $url The fully qualified URL to remove pagination from.
* @param int|null $page The page number to remove. If null, it will get number from query.
* @param bool|null $use_base Whether to remove the pagination base.
* If null, it will autodetermine.
* Should be true on archives and the homepage (blog and static!).
* False on singular post types.
* @return string $url The fully qualified URL without pagination.
*/
public function remove_pagination_from_url( $url, $page = null, $use_base = null ) {
if ( $this->pretty_permalinks ) {
$page = $page ?? max( $this->paged(), $this->page() );
if ( $page > 1 ) {
$_use_base = $use_base ?? (
$this->is_real_front_page() || $this->is_archive() || $this->is_singular_archive() || $this->is_search()
);
$user_slash = ( $GLOBALS['wp_rewrite']->use_trailing_slashes ? '/' : '' );
if ( $_use_base ) {
$find = "/{$GLOBALS['wp_rewrite']->pagination_base}/{$page}{$user_slash}";
} else {
$find = "/{$page}{$user_slash}";
}
$_query = parse_url( $url, PHP_URL_QUERY );
// Remove queries, add them back later.
if ( $_query )
$url = $this->s_url( $url );
$pos = strrpos( $url, $find );
// Defensive programming, only remove if $find matches the stack length, without query arguments.
if ( $pos && $pos + \strlen( $find ) === \strlen( $url ) ) {
$url = substr( $url, 0, $pos );
$url = \user_trailingslashit( $url );
// Add back the query.
if ( $_query )
$url = $this->append_url_query( $url, $_query );
}
}
} else {
$url = \remove_query_arg( [ 'page', 'paged', 'cpage' ], $url );
}
return $url;
}
/**
* Adjusts category post link.
*
* @since 3.0.0
* @since 4.0.3 Now fills in a fallback $post object when null.
* @access private
*
* @param \WP_Term $term The category to use in the permalink.
* @param array $terms Array of all categories (WP_Term objects) associated with the post. Unused.
* @param \WP_Post $post The post in question.
* @return \WP_Term The primary term.
*/
public function _adjust_post_link_category( $term, $terms = null, $post = null ) {
return $this->get_primary_term(
( $post ?? \get_post( $this->get_the_real_ID() ) )->ID,
$term->taxonomy
) ?: $term;
}
/**
* Generates shortlink URL.
*
* @since 2.2.2
* @since 3.1.0 1. No longer accepts $post_id input. Output's based on query only.
* 2. Shortened date archive URL length.
* 3. Removed query parameter collisions.
*
* @return string|null Escaped site Shortlink URL.
*/
public function get_shortlink() {
if ( ! $this->get_option( 'shortlink_tag' ) ) return '';
if ( $this->is_real_front_page() ) return '';
// We slash it because plain permalinks do that too, for consistency.
$home = \trailingslashit( $this->get_homepage_permalink() );
$id = $this->get_the_real_ID();
$url = '';
if ( $this->is_singular() ) {
$url = \add_query_arg( [ 'p' => $id ], $home );
} elseif ( $this->is_archive() ) {
if ( $this->is_category() ) {
$url = \add_query_arg( [ 'cat' => $id ], $home );
} elseif ( $this->is_tag() ) {
$url = \add_query_arg( [ 'post_tag' => $id ], $home );
} elseif ( \is_date() && isset( $GLOBALS['wp_query']->query ) ) {
// FIXME: Core Report: WP doesn't accept paged parameters w/ date parameters. It'll lead to the homepage.
$_query = $GLOBALS['wp_query']->query;
$_date = [
'y' => $_query['year'] ?? '',
'm' => $_query['monthnum'] ?? '',
'd' => $_query['day'] ?? '',
];
$url = \add_query_arg( [ 'm' => implode( '', $_date ) ], $home );
} elseif ( $this->is_author() ) {
$url = \add_query_arg( [ 'author' => $id ], $home );
} elseif ( $this->is_tax() ) {
// Generate shortlink for object type and slug.
$object = \get_queried_object();
$tax = $object->taxonomy ?? '';
$slug = $object->slug ?? '';
if ( $tax && $slug )
$url = \add_query_arg( [ $tax => $slug ], $home );
}
} elseif ( $this->is_search() ) {
$url = \add_query_arg( [ 's' => \get_search_query( false ) ], $home );
}
if ( ! $url ) return '';
if ( $this->is_archive() || $this->is_singular_archive() || $this->is_search() ) {
$paged = $this->maybe_get_paged( $this->paged(), false, true );
$url = \add_query_arg( [ 'paged' => $paged ], $url );
} else {
$page = $this->maybe_get_paged( $this->page(), false, true );
$url = \add_query_arg( [ 'page' => $page ], $url );
}
// Append queries other plugins might've filtered.
if ( $this->is_singular() ) {
$url = $this->append_url_query(
$url,
parse_url( \get_permalink( $id ), PHP_URL_QUERY )
);
}
return \esc_url_raw( $url, [ 'https', 'http' ] );
}
/**
* Generates Previous and Next links.
*
* @since 2.2.4
* @since 3.1.0 1. Now recognizes WC Shops and WP Blog pages as archival types.
* 2. Now sanitizes canonical URL according to permalink settings.
* 3. Removed second parameter. It was only a source of bugs.
* 4. Removed WordPress Core `get_pagenum_link` filter.
* @uses $this->get_paged_urls();
* @api Not used internally.
*
* @param string $next_prev Whether to get the previous or next page link.
* Accepts 'prev' and 'next'.
* @return string Escaped site Pagination URL
*/
public function get_paged_url( $next_prev ) {
return $this->get_paged_urls()[ $next_prev ];
}
/**
* Generates Previous and Next links.
*
* @since 3.1.0
* @since 3.2.4 1. Now correctly removes the pagination base from singular URLs.
* 2. Now returns no URLs when a custom canonical URL is set.
* @since 4.1.0 Removed memoization.
* @since 4.1.2 1. Added back memoization.
* 2. Reduced needless canonical URL generation when it wouldn't be processed anyway.
*
* @return array Escaped site Pagination URLs: {
* string 'prev'
* string 'next'
* }
*/
public function get_paged_urls() {
// phpcs:ignore, WordPress.CodeAnalysis.AssignmentInCondition -- I know.
if ( null !== $memo = memo() ) return $memo;
$prev = $next = '';
if ( $this->has_custom_canonical_url() ) goto end;
if (
$this->is_singular()
&& ! $this->is_singular_archive()
&& $this->is_multipage()
) {
$_run = $this->is_real_front_page()
? $this->get_option( 'prev_next_frontpage' )
: $this->get_option( 'prev_next_posts' );
if ( ! $_run ) goto end;
$page = $this->page();
$_numpages = $this->numpages();
} elseif (
$this->is_real_front_page()
|| $this->is_archive()
|| $this->is_singular_archive()
|| $this->is_search()
) {
$_run = $this->is_real_front_page()
? $this->get_option( 'prev_next_frontpage' )
: $this->get_option( 'prev_next_archives' );
if ( ! $_run ) goto end;
$page = $this->paged();
$_numpages = $this->numpages();
} else {
goto end;
}
// See if-statements below.
if ( ! ( $page + 1 <= $_numpages || $page > 1 ) ) goto end;
$canonical_url = memo( null, 'canonical' )
?? memo(
$this->remove_pagination_from_url( $this->get_current_canonical_url() ),
'canonical'
);
// If this page is not the last, create a next-URL.
if ( $page + 1 <= $_numpages )
$next = $this->add_pagination_to_url( $canonical_url, $page + 1 );
// If this page is not the first, create a prev-URL.
if ( $page > 1 )
$prev = $this->add_pagination_to_url( $canonical_url, $page - 1 );
end:;
return memo( compact( 'next', 'prev' ) );
}
/**
* Fetches home URL host. Like "wordpress.org".
* If this fails, you're going to have a bad time.
* Memoizes the return value.
*
* @since 2.7.0
* @since 2.9.2 1. Now considers port too.
* 2. Now uses get_home_url(), rather than get_option('home').
*
* @return string The home URL host.
*/
public function get_home_host() {
// phpcs:ignore, WordPress.CodeAnalysis.AssignmentInCondition -- I know.
if ( null !== $memo = memo() ) return $memo;
$parsed_url = parse_url(
$this->get_home_url()
);
$host = $parsed_url['host'] ?? '';
if ( $host && isset( $parsed_url['port'] ) )
$host .= ":{$parsed_url['port']}";
return memo( $host );
}
/**
* Add $paged if Paginated and allowed through arguments.
*
* @since 2.6.0
*
* @param int $paged The current page number.
* @param bool $singular Whether to allow plural and singular.
* @param bool $plural Whether to allow plural regardless.
*
* @return int|bool $paged. False if not allowed or on page 0. int if allowed.
*/
protected function maybe_get_paged( $paged = 0, $singular = false, $plural = true ) {
if ( $paged ) {
if ( $singular )
return $paged;
if ( $plural && $paged >= 2 )
return $paged;
}
return false;
}
/**
* Makes a fully qualified URL by adding the scheme prefix.
* Always adds http prefix, not https.
*
* NOTE: Expects the URL to have either a scheme, or a relative scheme set.
* Domain-relative URLs will not be parsed correctly.
* '/path/to/folder/` will become `http:///path/to/folder/`
*
* @since 2.6.5
* @see `$this->set_url_scheme()` to set the correct scheme.
* @see `$this->convert_to_url_if_path()` to create URLs from paths.
*
* @param string $url Required the current maybe not fully qualified URL.
* @return string $url
*/
public function make_fully_qualified_url( $url ) {
if ( '//' === substr( $url, 0, 2 ) ) {
$url = "http:$url";
} elseif ( 'http' !== substr( $url, 0, 4 ) ) {
$url = "http://{$url}";
}
return $url;
}
/**
* Makes a fully qualified URL from any input.
*
* @since 4.0.0
* @see `$this->s_relative_url()` to make URLs relative.
*
* @param string $path Either the URL or path. Will always be transformed to the current domain.
* @param string $url The URL to add the path to. Defaults to the current home URL.
* @return string $url
*/
public function convert_to_url_if_path( $path, $url = '' ) {
return \WP_Http::make_absolute_url(
$path,
\trailingslashit( $url ?: $this->set_preferred_url_scheme( $this->get_home_host() ) )
);
}
/**
* Appends given query to given URL.
*
* @since 4.1.4
*
* @param string $url A fully qualified URL.
* @param string $query A fully qualified query taken from parse_url( $url, PHP_URL_QUERY );
* @return string A fully qualified URL with appended $query.
*/
public function append_url_query( $url, $query = '' ) {
if ( ! $query )
return $url;
$_fragment = parse_url( $url, PHP_URL_FRAGMENT );
if ( $_fragment )
$url = str_replace( "#$_fragment", '', $url );
parse_str( $query, $results );
if ( $results )
$url = \add_query_arg( $results, $url );
if ( $_fragment )
$url .= "#$_fragment";
return $url;
}
/**
* Tests if input URL matches current domain.
*
* @since 2.9.4
* @since 4.1.0 Improved performance by testing an early match.
*
* @param string $url The URL to test. Required.
* @return bool true on match, false otherwise.
*/
public function matches_this_domain( $url ) {
if ( ! $url )
return false;
$home_domain = memo() ?? memo(
$this->set_url_scheme( \esc_url_raw(
$this->get_home_url(),
[ 'https', 'http' ]
) )
);
// Test for likely match early, before transforming.
if ( 0 === stripos( $url, $home_domain ) )
return true;
$url = $this->set_url_scheme( \esc_url_raw(
$url,
[ 'https', 'http' ]
) );
// If they start with the same, we can assume it's the same domain.
return 0 === stripos( $url, $home_domain );
}
/**
* Returns the home URL. Created for the WordPress method is slow for it
* performs "set_url_scheme" calls slowly. We rely on this method for some
* plugins filter `home_url`.
* Memoized.
*
* @since 4.2.0
*
* @return string The home URL.
*/
public function get_home_url() {
return umemo( __METHOD__ ) ?? umemo( __METHOD__, \get_home_url() );
}
/**
* Slashes the root (home) URL.
*
* @since 4.2.2
*
* @param string $url The root URL.
* @return string The root URL plausibly with added slashes.
*/
protected function slash_root_url( $url ) {
$parsed = parse_url( $url );
// Don't slash the home URL if it's been modified by a (translation) plugin.
if ( ! isset( $parsed['query'] ) ) {
if ( isset( $parsed['path'] ) && '/' !== $parsed['path'] ) {
// Paginated URL or subdirectory.
$url = \user_trailingslashit( $url, 'home' );
} else {
$url = \trailingslashit( $url );
}
}
return $url;
}
}