Tribe__Rewrite::get_canonical_url( string $url, bool $force = false )

Returns the canonical URLs associated with a ugly link.

This method will handle "our" URLs to go from their ugly form, filled with query vars, to the "pretty" one, if possible.


Parameters

$url

(string) (Required) The URL to try and translate into its canonical form.

$force

(bool) (Optional) Whether to try and use the cache or force a new canonical URL conversion.

Default value: false


Top ↑

Return

(string) The canonical URL, or the input URL if it could not be resolved to a canonical one.


Top ↑

Source

File: src/Tribe/Rewrite.php

	public function get_canonical_url( $url, $force = false ) {
		if ( get_class( $this ) === Tribe__Rewrite::class ) {
			throw new BadMethodCallException(
				'Method get_canonical_url should only be called on extending classes.'
			);
		}

		if ( null === $this->rewrite ) {
			// We re-do this check here as the object might have been initialized before the global rewrite was set.
			$this->setup();
		}

		/**
		 * Filters the canonical URL for an input URL before any kind of logic runs.
		 *
		 * @since 4.9.11
		 *
		 * @param string|null    $canonical_url The canonical URL, defaults to `null`; returning a non `null` value will
		 *                                      make the logic bail and return the value.
		 * @param string         $url           The input URL to resolve to a canonical one.
		 * @param Tribe__Rewrite $this          This rewrite object.
		 */
		$canonical_url = apply_filters( 'tribe_rewrite_pre_canonical_url', null, $url );
		if ( null !== $canonical_url ) {
			return $canonical_url;
		}

		$home_url = home_url();

		// It's not a path we, or WP, could possibly handle.
		$has_http_scheme = (bool) parse_url( $url, PHP_URL_SCHEME );
		if (
			$home_url === $url
			|| ( $has_http_scheme && false === strpos( $url, $home_url ) )
		) {
			return $url;
		}

		$canonical_url = $url;
		// To avoid issues with missing `path` component let's always add a trailing '/'.
		if ( false !== strpos( $url, '?' ) ) {
			$canonical_url = preg_replace( '~(\\/)*\\?~', '/?', $canonical_url );
		} elseif ( false !== strpos( $url, '#' ) ) {
			$canonical_url = preg_replace( '~(\\/)*#~', '/#', $canonical_url );
		}

		// Canonical URLs are supposed to contain the home URL.
		if ( false === strpos( $canonical_url, $home_url ) ) {
			$canonical_url = home_url( $canonical_url );
		}

		if ( empty( $canonical_url ) ) {
			return $home_url;
		}

		if ( ! $force ) {
			$this->warmup_cache(
				'canonical_url',
				WEEK_IN_SECONDS,
				Listener::TRIGGER_GENERATE_REWRITE_RULES
			);
			if ( isset( $this->canonical_url_cache[ $url ] ) ) {
				return $this->canonical_url_cache[ $url ];
			}
		}

		$query         = (string) parse_url( $url, PHP_URL_QUERY );
		wp_parse_str( $query, $query_vars );

		// Remove the `paged` query var if it's 1.
		if ( isset( $query_vars['paged'] ) && 1 === (int) $query_vars['paged'] ) {
			unset( $query_vars['paged'] );
		}

		ksort( $query_vars );

		$our_rules          = $this->get_handled_rewrite_rules();
		$handled_query_vars = $this->get_rules_query_vars( $our_rules );

		if (
			empty( $our_rules )
			|| ! in_array( Arr::get( $query_vars, 'post_type', 'post' ), $this->get_post_types(), true )
		) {
			$wp_canonical = redirect_canonical( $canonical_url, false );
			if ( empty( $wp_canonical ) ) {
				$wp_canonical = $canonical_url;
			}

			$this->canonical_url_cache[ $url ] = $wp_canonical;

			return $wp_canonical;
		}

		$bases = (array) $this->get_bases();
		ksort( $bases );

		$localized_matchers = $this->get_localized_matchers();
		$dynamic_matchers   = $this->get_dynamic_matchers( $query_vars );

		// Try to match only on the query vars we're actually handling.
		$matched_vars   = array_intersect_key( $query_vars, array_combine( $handled_query_vars, $handled_query_vars ) );
		$unmatched_vars = array_diff_key( $query_vars, array_combine( $handled_query_vars, $handled_query_vars ) );

		if ( empty( $matched_vars ) ) {
			// The URL does contain query vars, but none we handle.
			$wp_canonical = trailingslashit( redirect_canonical( $url, false ) );
			$this->canonical_url_cache[ $url ] = $wp_canonical;

			return $wp_canonical;
		}

		$found = false;

		foreach ( $our_rules as $link_template => $index_path ) {
			wp_parse_str( (string) parse_url( $index_path, PHP_URL_QUERY ), $link_vars );
			ksort( $link_vars );

			if ( array_keys( $link_vars ) !== array_keys( $matched_vars ) ) {
				continue;
			}

			if ( ! (
				Arr::get( $matched_vars, 'post_type', '' ) === Arr::get( $link_vars, 'post_type', '' )
				&& Arr::get( $matched_vars, 'eventDisplay', '' ) === Arr::get( $link_vars, 'eventDisplay', '' )
			) ) {
				continue;
			}

			$replace = array_map( function ( $localized_matcher ) use ( $matched_vars ) {
				if ( ! is_array( $localized_matcher ) ) {
					// For the dates.
					return isset( $matched_vars[ $localized_matcher ] )
						? $matched_vars[ $localized_matcher ]
						: '';
				}

				$query_var  = $localized_matcher['query_var'];
				$query_vars = [ $query_var ];

				if ( $query_var === 'name' ) {
					$query_vars = array_merge( $query_vars, $this->get_post_types() );
				}

				if ( ! array_intersect( array_keys( $matched_vars ), $query_vars ) ) {
					return '';
				}

				/*
				 * We use `end` as, by default, the localized version of the slug in the current language will be at the
				 * end of the array.
				 * @todo here we should keep a map, that has to generated at permalink flush time, to map locales/slugs.
				 */
				return end( $localized_matcher['localized_slugs'] );
			}, $localized_matchers );

			// Include dynamic matchers now.
			$replace = array_merge( $dynamic_matchers, $replace );
			$replaced = str_replace( array_keys( $replace ), $replace, $link_template );

			// Remove trailing chars.
			$path     = rtrim( $replaced, '?$' );
			$resolved = trailingslashit( home_url( $path ) );
			$found = true;

			break;
		}

		if ( empty( $resolved ) ) {
			$wp_canonical = redirect_canonical( $canonical_url, false );
			$resolved     = empty( $wp_canonical ) ? $canonical_url : $wp_canonical;
		}

		if ( $canonical_url !== $resolved ) {
			// Be sure to add a trailing slash to the URL; before `?` or `#`.
			$resolved = preg_replace( '/(?<!\\/)(#|\\?)/u', '/$1', $resolved );
		}

		if ( count( $unmatched_vars ) ) {
			$resolved = add_query_arg( $unmatched_vars, $resolved );
		}

		/**
		 * Filters the resolved canonical URL to allow third party code to modify it.
		 *
		 * Mind that the value will be cached and hence this filter will fire once per URL per request and, second, this
		 * filter will fire after all the logic to resolve the URL ran. If you want to filter the canonical URL before
		 * the logic runs then use the `tribe_rewrite_pre_canonical_url` filter.
		 *
		 * @since 4.9.11
		 *
		 * @param string         $resolved The resolved, canonical URL.
		 * @param string         $url      The original URL to resolve.
		 * @param Tribe__Rewrite $this     This object.
		 */
		$resolved = apply_filters( 'tribe_rewrite_canonical_url', $resolved, $url, $this );

		if ( $found ) {
			// Since we're caching let's not cache unmatched rules to allow for their later, valid resolution.
			$this->canonical_url_cache[ $url ] = $resolved;
		}

		return $resolved;
	}

Top ↑

Changelog

Changelog
Version Description
4.9.11 Introduced.