/
var
/
www
/
barefootlaw.org
/
wp-content
/
plugins
/
autodescription
/
inc
/
classes
/
Upload File
HOME
<?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; } }