File: //proc/self/cwd/wp-content/plugins/autodescription/inc/classes/meta/uri.class.php
<?php
/**
* @package The_SEO_Framework\Classes\Meta
* @subpackage The_SEO_Framework\Meta\URI
*/
namespace The_SEO_Framework\Meta;
\defined( 'THE_SEO_FRAMEWORK_PRESENT' ) or die;
use function \The_SEO_Framework\{
umemo,
get_query_type_from_args,
normalize_generation_args,
};
use \The_SEO_Framework\{
Data,
Helper\Query,
Meta,
};
/**
* The SEO Framework plugin
* Copyright (C) 2023 - 2024 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/>.
*/
/**
* Holds getters for meta tag output.
*
* Unlike other Meta functionality, we do not use "generated" vs. "custom" for every single function.
* This is because URIs are ambiguously used throughout WordPress.
* Often, we just need a URL, not a "generated" URL.
*
* Glossary:
* indexable: The final canonical URL that may be emptied if "noindex" is set.
* canonical: The final canonical URl.
* custom_canonical: The custom user-inputted canonical URL.
* generated/nothing: The permalink with query adjustments (such as pagination).
* bare: The permalink generated by WordPress.
*
* @since 5.0.0
* @access protected
* Use tsf()->uri() instead.
*/
class URI {
/**
* Gets the canonical URL for indexation.
*
* @since 5.0.0
*
* @return string $url If indexable, a canonical URL will appear.
*/
public static function get_indexable_canonical_url() {
$custom_url = static::get_custom_canonical_url();
if ( $custom_url )
return $custom_url;
if ( \has_filter( 'the_seo_framework_rel_canonical_output' ) ) {
/**
* @since 2.6.5
* @since 5.0.0 Deprecated.
* @deprecated
* @param string $url The canonical URL. Must be escaped.
* @param int $id The current page or term ID.
*/
$url = (string) \apply_filters_deprecated(
'the_seo_framework_rel_canonical_output',
[
'',
\The_SEO_Framework\Helper\Query::get_the_real_id(),
],
'5.0.0 of The SEO Framework',
'the_seo_framework_meta_render_data',
);
if ( $url ) return $url;
}
if ( str_contains( Robots::get_meta(), 'noindex' ) )
return '';
return static::get_generated_url();
}
/**
* 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.
* @since 5.0.0 1. No longer calls the query in the sitemap to remove pagination.
* 2. Moved from `\The_SEO_Framework\Load`.
* 3. Now always returns a sanitized URL.
*
* @param array|null $args The query arguments. Accepts 'id', 'tax', 'pta', and 'uid'.
* Leave null to autodetermine query.
* @return string The canonical URL, if any.
*/
public static function get_canonical_url( $args = null ) {
return static::get_custom_canonical_url( $args )
?: static::get_generated_url( $args );
}
/**
* Returns the custom canonical URL.
*
* @since 5.0.0
*
* @param array|null $args The query arguments. Accepts 'id', 'tax', 'pta', and 'uid'.
* Leave null to autodetermine query.
* @return string The custom canonical URL, if any.
*/
public static function get_custom_canonical_url( $args = null ) {
return isset( $args )
? static::get_custom_canonical_url_from_args( $args )
: static::get_custom_canonical_url_from_query();
}
/**
* Returns the generated 'canonical' URL.
* Because this isn't 'canonical', we're not ascribing that name.
* Memoizes if $args is null.
*
* @since 5.0.0
*
* @param array|null $args The query arguments. Accepts 'id', 'tax', 'pta', and 'uid'.
* Leave null to autodetermine and memoize query.
* @return string The custom canonical URL, if any.
*/
public static function get_generated_url( $args = null ) {
return isset( $args )
? static::get_generated_url_from_args( $args )
: static::get_generated_url_from_query();
}
/**
* Returns the custom canonical URL, based on query.
*
* @since 5.0.0
*
* @return string The custom canonical URL, if any.
*/
public static function get_custom_canonical_url_from_query() {
if ( Query::is_real_front_page() ) {
if ( Query::is_static_front_page() ) {
$url = Data\Plugin::get_option( 'homepage_canonical' )
?: Data\Plugin\Post::get_meta_item( '_genesis_canonical_uri' );
} else {
$url = Data\Plugin::get_option( 'homepage_canonical' );
}
} elseif ( Query::is_singular() ) {
$url = Data\Plugin\Post::get_meta_item( '_genesis_canonical_uri' );
} elseif ( Query::is_editable_term() ) {
$url = Data\Plugin\Term::get_meta_item( 'canonical' );
} elseif ( \is_post_type_archive() ) {
$url = Data\Plugin\PTA::get_meta_item( 'canonical' );
}
if ( empty( $url ) ) return '';
if ( URI\Utils::url_matches_blog_domain( $url ) )
$url = URI\Utils::set_preferred_url_scheme( $url );
return \sanitize_url( $url, [ 'https', 'http' ] );
}
/**
* Returns the custom canonical URL, based on arguments.
*
* @since 5.0.0
*
* @param array $args The query arguments. Accepts 'id', 'tax', 'pta', and 'uid'.
* @return string The custom canonical URL, if any.
*/
public static function get_custom_canonical_url_from_args( $args ) {
normalize_generation_args( $args );
switch ( get_query_type_from_args( $args ) ) {
case 'single':
if ( Query::is_static_front_page( $args['id'] ) ) {
$url = Data\Plugin::get_option( 'homepage_canonical' )
?: Data\Plugin\Post::get_meta_item( '_genesis_canonical_uri', $args['id'] );
} else {
$url = Data\Plugin\Post::get_meta_item( '_genesis_canonical_uri', $args['id'] );
}
break;
case 'term':
$url = Data\Plugin\Term::get_meta_item( 'canonical', $args['id'] );
break;
case 'homeblog':
$url = Data\Plugin::get_option( 'homepage_canonical' );
break;
case 'pta':
$url = Data\Plugin\PTA::get_meta_item( 'canonical', $args['pta'] );
}
if ( empty( $url ) ) return '';
if ( URI\Utils::url_matches_blog_domain( $url ) )
$url = URI\Utils::set_preferred_url_scheme( $url );
return \sanitize_url( $url, [ 'https', 'http' ] );
}
/**
* Gets generated canonical URL, based on query.
*
* @since 5.0.0
*
* @return string The generated canonical URL.
*/
public static function get_generated_url_from_query() {
if ( Query::is_real_front_page() ) {
$url = static::get_front_page_url();
} elseif ( Query::is_singular() ) {
$url = static::get_singular_url();
} elseif ( Query::is_archive() ) {
if ( Query::is_editable_term() ) {
$url = static::get_term_url();
} elseif ( \is_post_type_archive() ) {
$url = static::get_pta_url();
} elseif ( Query::is_author() ) {
$url = static::get_author_url();
} elseif ( \is_date() ) {
$url = static::get_date_url();
}
} elseif ( Query::is_search() ) {
$url = static::get_search_url();
}
return $url ?? '' ?: '';
}
/**
* Gets generated permalink, based on args.
*
* @since 5.0.0
*
* @param array $args The query arguments. Accepts 'id', 'tax', 'pta', and 'uid'.
* @return string The generated URL.
*/
public static function get_generated_url_from_args( $args ) {
normalize_generation_args( $args );
switch ( get_query_type_from_args( $args ) ) {
case 'single':
if ( Query::is_static_front_page( $args['id'] ) ) {
$url = static::get_bare_front_page_url();
} else {
$url = static::get_bare_singular_url( $args['id'] );
}
break;
case 'term':
$url = static::get_bare_term_url( $args['id'], $args['tax'] );
break;
case 'homeblog':
$url = static::get_bare_front_page_url();
break;
case 'pta':
$url = static::get_bare_pta_url( $args['pta'] );
break;
case 'user':
$url = static::get_bare_author_url( $args['uid'] );
}
return $url ?: '';
}
/**
* Returns home canonical URL.
* Automatically adds pagination if the ID matches the query.
*
* @since 5.0.0
*
* @return string The home URL.
*/
public static function get_front_page_url() {
$url = URI\Utils::slash_front_page_url( Data\Blog::get_front_page_url() );
if ( empty( $url ) ) return '';
$page = max( Query::paged(), Query::page() );
if ( $page > 1 )
$url = URI\Utils::add_pagination_to_url( $url, $page, true );
return \sanitize_url(
URI\Utils::set_preferred_url_scheme( $url ),
[ 'https', 'http' ],
);
}
/**
* Returns home URL without query adjustments.
*
* @since 5.0.0
*
* @return string The home URL.
*/
public static function get_bare_front_page_url() {
return umemo( __METHOD__ ) ?? umemo(
__METHOD__,
\sanitize_url(
URI\Utils::slash_front_page_url( URI\Utils::set_preferred_url_scheme(
Data\Blog::get_front_page_url(),
) ),
[ 'https', 'http' ],
),
);
}
/**
* Returns singular canonical URL.
*
* @since 5.0.0
*
* @param int|null $post_id The page ID. Leave null to autodetermine.
* @return string The singular URL.
*/
public static function get_singular_url( $post_id = null ) {
if ( isset( $post_id ) )
return static::get_bare_singular_url( $post_id );
$url = \get_permalink( Query::get_the_real_id() );
if ( empty( $url ) ) return '';
if ( Query::is_singular_archive() ) {
// Singular archives, like blog pages and shop pages, use the pagination base with 'paged'.
$paged = Query::paged();
if ( $paged > 1 )
$url = URI\Utils::add_pagination_to_url( $url, $paged, true );
} else {
$page = Query::page();
if ( $page > 1 )
$url = URI\Utils::add_pagination_to_url( $url, $page, false );
}
return \sanitize_url(
URI\Utils::set_preferred_url_scheme( $url ),
[ 'https', 'http' ],
);
}
/**
* Returns singular permalink without query adjustments.
*
* @since 5.0.0
*
* @param int|null $post_id The post ID to get the URL from. Leave null to autodetermine.
* @return string The singular canonical URL without complex optimizations.
*/
public static function get_bare_singular_url( $post_id = null ) {
$url = \get_permalink( $post_id );
if ( empty( $url ) ) return '';
return \sanitize_url(
URI\Utils::set_preferred_url_scheme( $url ),
[ 'https', 'http' ],
);
}
/**
* Returns taxonomical canonical URL.
* Automatically adds pagination if the ID matches the query.
*
* @since 5.0.0
*
* @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 static function get_term_url( $term_id = null, $taxonomy = '' ) {
if ( isset( $term_id ) )
return static::get_bare_term_url( $term_id, $taxonomy );
$url = \get_term_link( Query::get_the_real_id(), $taxonomy );
if ( empty( $url ) || ! \is_string( $url ) )
return '';
return \sanitize_url(
URI\Utils::set_preferred_url_scheme( URI\Utils::add_pagination_to_url(
$url,
Query::paged(),
true,
) ),
[ 'https', 'http' ],
);
}
/**
* Returns taxonomical permalink without query adjustments.
*
* @since 5.0.0
*
* @param int|null $term_id The term ID.
* @param string $taxonomy The taxonomy. Leave empty to autodetermine.
* @return string The taxonomical canonical URL, if any.
*/
public static function get_bare_term_url( $term_id = null, $taxonomy = '' ) {
if ( empty( $term_id ) ) {
$term_id = Query::get_the_real_id();
$taxonomy = Query::get_current_taxonomy();
}
$url = \get_term_link( $term_id, $taxonomy );
// get_term_link() can return WP_Error.
if ( empty( $url ) || ! \is_string( $url ) )
return '';
return \sanitize_url(
URI\Utils::set_preferred_url_scheme( $url ),
[ 'https', 'http' ],
);
}
/**
* Returns post type archive canonical URL.
*
* @since 5.0.0
*
* @param ?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 static function get_pta_url( $post_type = null ) {
if ( isset( $post_type ) )
return static::get_bare_pta_url( $post_type );
$url = \get_post_type_archive_link( $post_type ?? Query::get_current_post_type() );
if ( empty( $url ) ) return '';
return \sanitize_url(
URI\Utils::set_preferred_url_scheme( URI\Utils::add_pagination_to_url(
$url,
Query::paged(),
true,
) ),
[ 'https', 'http' ],
);
}
/**
* Returns post type archive canonical URL without query adjustments.
*
* @since 5.0.0
*
* @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 static function get_bare_pta_url( $post_type = null ) {
$url = \get_post_type_archive_link( $post_type ?? Query::get_current_post_type() );
if ( empty( $url ) ) return '';
return \sanitize_url(
URI\Utils::set_preferred_url_scheme( $url ),
[ 'https', 'http' ],
);
}
/**
* Returns author canonical URL.
*
* @since 5.0.0
*
* @param ?int $id The author ID. Leave null to autodetermine.
* @return string The author canonical URL, if any.
*/
public static function get_author_url( $id = null ) {
if ( isset( $id ) )
return static::get_bare_author_url( $id );
$url = \get_author_posts_url( Query::get_the_real_id() );
if ( empty( $url ) ) return '';
return \sanitize_url(
URI\Utils::set_preferred_url_scheme( URI\Utils::add_pagination_to_url(
$url,
Query::paged(),
true,
) ),
[ 'https', 'http' ],
);
}
/**
* Returns author canonical URL without query adjustments.
*
* @since 5.0.0
*
* @param int|null $id The author ID. Leave null to autodetermine.
* @return string The author canonical URL, if any.
*/
public static function get_bare_author_url( $id = null ) {
$url = \get_author_posts_url( $id ?? Query::get_the_real_id() );
if ( empty( $url ) ) return '';
return \sanitize_url(
URI\Utils::set_preferred_url_scheme( $url ),
[ 'https', 'http' ],
);
}
/**
* Returns date canonical URL.
* Automatically adds pagination if the date input matches the query.
*
* @since 3.0.0
*
* @param ?int $year The year. Leave null to autodetermine entire query.
* @param ?int $month The month. If set, year must also be set.
* @param ?int $day The day. If set, month and year must also be set.
* @return string The date canonical URL, if any.
*/
public static function get_date_url( $year = null, $month = null, $day = null ) {
if ( isset( $year ) )
return static::get_bare_date_url( $year, $month, $day );
$year = \get_query_var( 'year' );
$month = \get_query_var( 'monthnum' );
$day = \get_query_var( 'day' );
if ( $day ) {
$url = \get_day_link( $year, $month, $day );
} elseif ( $month ) {
$url = \get_month_link( $year, $month );
} else {
$url = \get_year_link( $year );
}
if ( empty( $url ) ) return '';
return \sanitize_url(
URI\Utils::set_preferred_url_scheme( URI\Utils::add_pagination_to_url(
$url,
Query::paged(),
true,
) ),
[ 'https', 'http' ],
);
}
/**
* Returns date canonical URL without query adjustments.
*
* @since 5.0.0
*
* @param int $year The year.
* @param ?int $month The month.
* @param ?int $day The day.
* @return string The date canonical URL, if any.
*/
public static function get_bare_date_url( $year, $month = null, $day = null ) {
if ( $day ) {
$url = \get_day_link( $year, $month, $day );
} elseif ( $month ) {
$url = \get_month_link( $year, $month );
} else {
$url = \get_year_link( $year );
}
if ( empty( $url ) ) return '';
return \sanitize_url(
URI\Utils::set_preferred_url_scheme( $url ),
[ 'https', 'http' ],
);
}
/**
* Returns search canonical URL.
* Automatically adds pagination if the input matches the query.
*
* @since 5.0.0
*
* @param string $search_query The search query. Mustn't be escaped.
* When left empty, the current query will be used.
* @return string The search canonical URL.
*/
public static function get_search_url( $search_query = null ) {
if ( isset( $search_query ) )
return static::get_bare_search_url( $search_query );
$url = \get_search_link();
if ( empty( $url ) ) return '';
return \sanitize_url(
URI\Utils::set_preferred_url_scheme( URI\Utils::add_pagination_to_url(
$url,
Query::paged(),
true,
) ),
[ 'https', 'http' ],
);
}
/**
* Returns search canonical URL without query adjustments.
*
* @since 5.0.0
*
* @param string $search_query The search query. Mustn't be escaped.
* @return string The date canonical URL, if any.
*/
public static function get_bare_search_url( $search_query ) {
$url = \get_search_link( $search_query );
if ( empty( $url ) ) return '';
return \sanitize_url(
URI\Utils::set_preferred_url_scheme( $url ),
[ 'https', 'http' ],
);
}
/**
* Gets 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.
* @since 5.0.0 1. Removed memoization thanks to optimization.
* 2. Moved from `\The_SEO_Framework\Load`.
* 3. Removed option checks (unintentionally)
* @since 5.0.5 Reinstated missing option checks.
* @todo make this a getter via $args.
*
* @return array {
* Paginated URLs for the current request. May be empty if settings do not permit generation.
*
* @type string $prev The previous URL. May be empty if no previous URL is available.
* @type string $next The next URL. May be empty if no next URL is available.
* }
*/
public static function get_paged_urls() {
if ( Query::is_real_front_page() ) {
$get = Data\Plugin::get_option( 'prev_next_frontpage' );
} elseif ( Query::is_singular() || Query::is_singular_archive() ) {
$get = Data\Plugin::get_option( 'prev_next_posts' );
} elseif ( Query::is_archive() ) {
$get = Data\Plugin::get_option( 'prev_next_archives' );
} else {
$get = false;
}
return $get ? static::get_generated_paged_urls() : [ '', '' ];
}
/**
* Generates Previous and Next links.
*
* @since 5.0.5
*
* @return array {
* Paginated URLs for the current request.
*
* @type string $prev The previous URL. May be empty if no previous URL is available.
* @type string $next The next URL. May be empty if no next URL is available.
* }
*/
public static function get_generated_paged_urls() {
$page = max( Query::paged(), Query::page() );
$numpages = Query::numpages();
// If this page is not the last, create a next-URL.
if ( ( $page + 1 ) <= $numpages ) {
$url = URI\Utils::remove_pagination_from_url( static::get_generated_url() );
if ( $url )
$next = \sanitize_url(
URI\Utils::add_pagination_to_url( $url, $page + 1 ),
[ 'https', 'http' ],
);
}
// If this page is not the first, create a prev-URL.
if ( $page > 1 ) {
$url ??= URI\Utils::remove_pagination_from_url( static::get_generated_url() );
if ( $url )
$prev = \sanitize_url(
URI\Utils::add_pagination_to_url( $url, $page - 1 ),
[ 'https', 'http' ],
);
}
return [
$prev ?? '',
$next ?? '',
];
}
/**
* Returns the redirect URL, if any.
*
* @since 4.1.4
* @since 4.2.0 1. Now supports the `$args['pta']` index.
* 2. Now redirects post type archives.
* @since 5.0.0 1. Now expects an ID before getting a post meta item.
* 2. Moved from `\The_SEO_Framework\Load`.
*
* @param array|null $args The query arguments. Accepts 'id', 'tax', 'pta', and 'uid'.
* Leave null to autodetermine query.
* @return string The canonical URL if found, empty string otherwise.
*/
public static function get_redirect_url( $args = null ) {
if ( isset( $args ) ) {
normalize_generation_args( $args );
switch ( get_query_type_from_args( $args ) ) {
case 'single':
if ( Query::is_static_front_page( $args['id'] ) ) {
$url = Data\Plugin::get_option( 'homepage_redirect' )
?: Data\Plugin\Post::get_meta_item( 'redirect', $args['id'] );
} else {
$url = Data\Plugin\Post::get_meta_item( 'redirect', $args['id'] );
}
break;
case 'term':
$url = Data\Plugin\Term::get_meta_item( 'redirect', $args['id'] );
break;
case 'homeblog':
$url = Data\Plugin::get_option( 'homepage_redirect' );
break;
case 'pta':
$url = Data\Plugin\PTA::get_meta_item( 'redirect', $args['pta'] );
}
} else {
if ( Query::is_real_front_page() ) {
if ( Query::is_static_front_page() ) {
$url = Data\Plugin::get_option( 'homepage_redirect' )
?: Data\Plugin\Post::get_meta_item( 'redirect' );
} else {
$url = Data\Plugin::get_option( 'homepage_redirect' );
}
} elseif ( Query::is_singular() ) {
$url = Data\Plugin\Post::get_meta_item( 'redirect' );
} elseif ( Query::is_editable_term() ) {
$url = Data\Plugin\Term::get_meta_item( 'redirect' );
} elseif ( \is_post_type_archive() ) {
$url = Data\Plugin\PTA::get_meta_item( 'redirect' );
}
}
if ( empty( $url ) ) return '';
return \sanitize_url( $url, [ 'https', 'http' ] );
}
/**
* Generates shortlink URL.
*
* @since 5.0.0
* @todo make this a getter via $args.
*
* @return string The shortlink URL.
*/
public static function get_shortlink_url() {
if (
! Data\Plugin::get_option( 'shortlink_tag' )
|| Query::is_real_front_page()
) return '';
return static::get_generated_shortlink_url();
}
/**
* Generates shortlink URL.
*
* @since 5.0.0
* @todo Append queries of other plugins for other pages as well?
*
* @return string The shortlink URL.
*/
public static function get_generated_shortlink_url() {
if ( Query::is_singular() ) {
$query = [ 'p' => Query::get_the_real_id() ];
} elseif ( Query::is_archive() ) {
if ( Query::is_category() ) {
$query = [ 'cat' => Query::get_the_real_id() ];
} elseif ( Query::is_tag() ) {
$slug = \get_queried_object()->slug ?? '';
if ( $slug )
$query = [ 'tag' => $slug ];
} elseif ( Query::is_tag() || Query::is_tax() ) {
// Generate shortlink for object type and slug.
$object = \get_queried_object();
$tax = $object->taxonomy ?? '';
$slug = $object->slug ?? '';
if ( $tax && $slug )
$query = [ $tax => $slug ];
} elseif ( \is_date() && isset( $GLOBALS['wp_query']->query ) ) {
// FIXME: Make Trac ticket: 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'] ?? '',
];
$query = [ 'm' => implode( '', $_date ) ];
} elseif ( Query::is_author() ) {
$query = [ 'author' => Query::get_the_real_id() ];
}
} elseif ( Query::is_search() ) {
$query = [ 's' => \get_search_query( false ) ];
}
if ( empty( $query ) ) return '';
$page = Query::page();
$paged = Query::paged();
if ( $page > 1 ) {
$query += [ 'page' => $page ];
} elseif ( $paged > 1 ) {
$query += [ 'paged' => $paged ];
}
$query = http_build_query( $query );
$extra_query = parse_url( static::get_generated_url( null ), \PHP_URL_QUERY );
if ( $extra_query )
$query .= "&$extra_query";
return \sanitize_url(
URI\Utils::append_query_to_url(
static::get_bare_front_page_url(),
$query,
),
[ 'https', 'http' ],
);
}
}