WC_Admin_Permalink_Settings::get_stored_product_base │ private │ WC 1.0

Resolve a posted product permalink choice to the value that gets persisted for it.

The render path checks a radio by comparing the stored base against this, and the save path stores what this returns, so both derive the base from one set of rules instead of restating them separately -- which is what made every predefined structure revert to "Custom base". See https://github.com/woocommerce/woocommerce/issues/29050.

Must run inside a wc_switch_to_site_locale() window: the Default base is a translated slug, and an administrator whose profile language differs from the site language would otherwise store one translation and compare against another.

Sharing the rules is not the same as receiving identical input: the save path runs the posted value through sanitize_text_field() first, which strips percent-encoded octets, so a Shop page whose slug carries a literal percent-escape can still resolve differently on the two sides.

Method of the class: WC_Admin_Permalink_Settings{}

No Hooks.

Returns

string. The base as it is stored.

Usage

// private - for code of main (parent) class only
$result = $this->get_stored_product_base( $posted_base, ?string $posted_structure ): string;
$posted_base(string) (required)
Posted product_permalink radio value.
?string $posted_structure
.
Default: null

WC_Admin_Permalink_Settings::get_stored_product_base() code WC 11.2.1

private function get_stored_product_base( string $posted_base, ?string $posted_structure = null ): string {
	$default_base = wc_sanitize_permalink( _x( 'product', 'slug', 'woocommerce' ) );

	if ( 'custom' === $posted_base ) {
		if ( null === $posted_structure ) {
			// A missing or non-string field resolves to the default base, so the stored slug
			// stays deterministic and in the site locale.
			$base = $default_base;
		} else {
			// Remove every `#` so the base cannot open a URL fragment, prepend the leading
			// slash, then collapse each run of slashes into one.
			$base = (string) preg_replace( '~/+~', '/', '/' . str_replace( '#', '', trim( $posted_structure ) ) );
		}

		// A base of nothing but the category token gives products the same URL shape as the
		// category archives they sit under, so the two collide. Prefix the default base.
		if ( '/%product_cat%/' === trailingslashit( $base ) ) {
			$base = '/' . $default_base . $base;
		}
	} else {
		// The Default radio posts an empty value; store the site-locale slug.
		$base = '' === $posted_base ? $default_base : $posted_base;
	}

	$base = wc_sanitize_permalink( $base );

	/*
	 * Resolve an empty base to the default one. A custom base that is blank, whitespace, or
	 * nothing but hashes and slashes sanitizes down to empty, and an empty base does not
	 * survive: wc_get_permalink_structure() drops it and refills the option from the request
	 * locale, outside any locale window, persisting a slug the site locale never chose -- the
	 * same divergence this resolver exists to prevent.
	 *
	 * This narrows that window rather than closing it. The default base is itself a translated
	 * slug run through wc_sanitize_permalink(), so a translation or a gettext filter that
	 * resolves it to something sanitizing away -- `/` untrailingslashits to nothing -- leaves
	 * this assigning empty to empty.
	 */
	if ( '' === $base ) {
		$base = $default_base;
	}

	/*
	 * A base equal to the Default structure is reported in Default's bare form. The Custom
	 * base field is the next tab stop after the radio group and focusing it selects Custom
	 * base, so a single Tab from Default posts the field's prefilled `/product/` through the
	 * custom branch, which prepends a slash. The two forms are not interchangeable: under
	 * index.php (PATHINFO) permalinks the leading slash reaches register_post_type() and
	 * produces an `index.php//product/%product%` permastruct whose URLs do not resolve, while
	 * the bare slug works everywhere. Converging on the bare form keeps Default checked after
	 * that keystroke and never persists the broken shape.
	 */
	return '/' . $default_base === $base ? $default_base : $base;
}