Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion assets/js/domain-logs.min.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions inc/class-domain-mapping.php
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,15 @@ public function get_www_and_nowww_versions($domain) {
return [$nowww, $www];
}

/**
* Check if this is a special loopback request.
*
* @param null|false|\WP_Site $current_site Current Site.
* @param string $domain Current domain.
* @param string $path Current Path.
*
* @return void
*/
public function verify_dns_mapping($current_site, $domain, $path) {

// Nonce functions are unavailable and the wp_hash is basically the same.
Expand Down Expand Up @@ -274,6 +283,7 @@ public function verify_dns_mapping($current_site, $domain, $path) {
*/
public function check_domain_mapping($site, $domain) {

$this->verify_dns_mapping($site, $domain, '/');
// Have we already matched? (Allows other plugins to match first)
if ( ! empty($site)) {
return $site;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,12 @@ public function on_add_subdomain($subdomain, $site_id): void {
return;
}

$should_add_www = apply_filters('wu_cloudflare_should_add_www', true, $subdomain, $site_id);
$should_add_www = apply_filters(
'wu_cloudflare_should_add_www',
\WP_Ultimo\Managers\Domain_Manager::get_instance()->should_create_www_subdomain($subdomain),
$subdomain,
$site_id
);

$domains_to_send = [$subdomain];

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,7 @@ private function get_domains(): array {
$domain_list = $this->get_domain_list();

foreach ($domain_list as $naked_domain) {
if (! str_starts_with((string) $naked_domain, 'www.') && ! str_starts_with((string) $naked_domain, '*.')) {
if (! str_starts_with((string) $naked_domain, 'www.') && ! str_starts_with((string) $naked_domain, '*.') && \WP_Ultimo\Managers\Domain_Manager::get_instance()->should_create_www_subdomain($naked_domain)) {
$domain_list[] = 'www.' . $naked_domain;
}
}
Expand Down Expand Up @@ -378,7 +378,7 @@ public function get_all_mapped_domains() {
foreach ($mappings as $domain) {
$final_domain_list[] = $domain;

if (! str_starts_with((string) $domain, 'www.')) {
if (! str_starts_with((string) $domain, 'www.') && \WP_Ultimo\Managers\Domain_Manager::get_instance()->should_create_www_subdomain($domain)) {
$final_domain_list[] = "www.$domain";
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ public function on_add_domain($domain, $site_id): void {
$this->get_runcloud_base_url('domains'),
[
'name' => $domain,
'www' => true,
'www' => \WP_Ultimo\Managers\Domain_Manager::get_instance()->should_create_www_subdomain($domain),
'redirection' => 'non-www',
],
'POST'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

use Psr\Log\LogLevel;

defined( 'ABSPATH' ) || exit;
defined('ABSPATH') || exit;

/**
* This base class should be extended to implement new host integrations for SSL and domains.
Expand Down Expand Up @@ -119,10 +119,16 @@ public function on_add_domain($domain, $site_id): void {
$current_domain_list = $this->get_server_pilot_domains();

if ($current_domain_list && is_array($current_domain_list)) {
$domains_to_add = [$domain];

if (\WP_Ultimo\Managers\Domain_Manager::get_instance()->should_create_www_subdomain($domain)) {
$domains_to_add[] = 'www.' . $domain;
}

$this->send_server_pilot_api_request(
'',
[
'domains' => array_merge($current_domain_list, [$domain, 'www.' . $domain]),
'domains' => array_merge($current_domain_list, $domains_to_add),
]
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ public function on_add_domain($domain, $site_id): void {

$domains = [$domain];

if (! str_starts_with($domain, 'www.')) {
if (! str_starts_with($domain, 'www.') && \WP_Ultimo\Managers\Domain_Manager::get_instance()->should_create_www_subdomain($domain)) {
$domains[] = "www.$domain";
}

Expand Down
64 changes: 64 additions & 0 deletions inc/managers/class-domain-manager.php
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,70 @@ public function add_domain_mapping_settings(): void {
],
]
);

wu_register_settings_field(
'domain-mapping',
'auto_create_www_subdomain',
[
'title' => __('Create www Subdomain Automatically?', 'multisite-ultimate'),
'desc' => __('Control when www subdomains should be automatically created for mapped domains.', 'multisite-ultimate'),
'tooltip' => __('This setting applies to all hosting integrations and determines when a www version of the domain should be automatically created.', 'multisite-ultimate'),
'type' => 'select',
'default' => 'always',
'options' => [
'always' => __('Always - Create www subdomain for all domains', 'multisite-ultimate'),
'main_only' => __('Only for main domains (e.g., example.com but not subdomain.example.com)', 'multisite-ultimate'),
'never' => __('Never - Do not automatically create www subdomains', 'multisite-ultimate'),
],
'require' => [
'enable_domain_mapping' => true,
],
]
);
}

/**
* Check if a www subdomain should be created for the given domain.
*
* @since 2.0.0
* @param string $domain The domain to check.
* @return bool True if www subdomain should be created, false otherwise.
*/
public function should_create_www_subdomain($domain) {

$setting = wu_get_setting('auto_create_www_subdomain', 'always');

switch ($setting) {
case 'never':
return false;

case 'main_only':
// Check if this is a main domain (no subdomain parts)
// A main domain has only 2 parts when split by dots (e.g., example.com)
// or 3 parts if it's a known TLD structure (e.g., example.co.uk)
$parts = explode('.', $domain);

// Simple heuristic: if domain has only 2 parts, it's definitely a main domain
if (count($parts) <= 2) {
return true; // e.g., example.com
}

// For 3+ parts, check if it's a main domain with multi-part TLD
$known_multi_part_tlds = ['.co.uk', '.com.au', '.co.nz', '.com.br', '.co.in'];
$last_two_parts = '.' . $parts[ count($parts) - 2 ] . '.' . $parts[ count($parts) - 1 ];

// If it has exactly 3 parts and matches a known multi-part TLD, it's a main domain
if (count($parts) === 3 && in_array($last_two_parts, $known_multi_part_tlds, true)) {
return true; // e.g., example.co.uk
}

// Otherwise, it's a subdomain
return false;

case 'always':
default:
return true;
}
}
Comment on lines +420 to 470
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Harden domain normalization and avoid fatal on unexpected input; fix @SInCE

Cast to string before strtolower/trim and strip scheme/port/trailing dot to cover common edge cases (URLs passed in, ports, trailing dot). Also align the docblock @SInCE to the version introducing this method.

Apply:

-	 * @since 2.0.0
+	 * @since 2.4.4
@@
-	public function should_create_www_subdomain($domain) {
+	public function should_create_www_subdomain($domain) {
@@
-		// Normalize incoming domain
-		$domain = trim(strtolower($domain));
+		// Normalize incoming domain
+		$domain = strtolower(trim((string) $domain));
+		// If a URL sneaks in, extract host
+		if (strpos($domain, '://') !== false) {
+			$parsed = wp_parse_url($domain);
+			if (!empty($parsed['host'])) {
+				$domain = $parsed['host'];
+			}
+		}
+		// Drop trailing dot and any port suffix
+	$domain = rtrim($domain, '.');
+	$domain = preg_replace('/:\d+$/', '', $domain);

Optional: add a return type for consistency with this class’ other methods.

-	public function should_create_www_subdomain($domain) {
+	public function should_create_www_subdomain($domain): bool {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
/**
* Check if a www subdomain should be created for the given domain.
*
* @since 2.0.0
* @param string $domain The domain to check.
* @return bool True if www subdomain should be created, false otherwise.
*/
public function should_create_www_subdomain($domain) {
// Normalize incoming domain
$domain = trim(strtolower($domain));
// Guard against double-prefixing - return false if already starts with www.
if (strpos($domain, 'www.') === 0) {
return false;
}
$setting = wu_get_setting('auto_create_www_subdomain', 'always');
switch ($setting) {
case 'never':
return false;
case 'main_only':
// Check if this is a main domain (no subdomain parts)
// A main domain has only 2 parts when split by dots (e.g., example.com)
// or 3 parts if it's a known TLD structure (e.g., example.co.uk)
$parts = explode('.', $domain);
// Simple heuristic: if domain has only 2 parts, it's definitely a main domain
if (count($parts) <= 2) {
return true; // e.g., example.com
}
// For 3+ parts, check if it's a main domain with multi-part TLD
$known_multi_part_tlds = apply_filters('wu_multi_part_tlds', ['.co.uk', '.com.au', '.co.nz', '.com.br', '.co.in']);
$last_two_parts = '.' . $parts[ count($parts) - 2 ] . '.' . $parts[ count($parts) - 1 ];
// If it has exactly 3 parts and matches a known multi-part TLD, it's a main domain
if (count($parts) === 3 && in_array($last_two_parts, $known_multi_part_tlds, true)) {
return true; // e.g., example.co.uk
}
// Otherwise, it's a subdomain
return false;
case 'always':
default:
return true;
}
}
/**
* Check if a www subdomain should be created for the given domain.
*
* @since 2.4.4
* @param string $domain The domain to check.
* @return bool True if www subdomain should be created, false otherwise.
*/
public function should_create_www_subdomain($domain) {
// Normalize incoming domain
$domain = strtolower(trim((string) $domain));
// If a URL sneaks in, extract host
if (strpos($domain, '://') !== false) {
$parsed = wp_parse_url($domain);
if (! empty( $parsed['host'] )) {
$domain = $parsed['host'];
}
}
// Drop trailing dot and any port suffix
$domain = rtrim($domain, '.');
$domain = preg_replace('/:\d+$/', '', $domain);
// Guard against double-prefixing - return false if already starts with www.
if (strpos($domain, 'www.') === 0) {
return false;
}
$setting = wu_get_setting('auto_create_www_subdomain', 'always');
switch ($setting) {
case 'never':
return false;
case 'main_only':
// ...existing logic...
🤖 Prompt for AI Agents
In inc/managers/class-domain-manager.php around lines 420 to 470, the domain
normalization can raise a fatal on non-string input and the docblock @since is
inaccurate; cast the incoming $domain to string before using strtolower/trim,
then strip URL scheme (http:// or https://), remove any trailing port (":1234")
and trailing dot to normalize inputs like full URLs or hostnames with ports,
then continue the existing www-prefix check; update the @since tag to the actual
version that introduced this method (replace 2.0.0 with the correct version),
and optionally add a bool return type to the method signature for consistency
(e.g., public function should_create_www_subdomain($domain): bool).


/**
Expand Down
4 changes: 4 additions & 0 deletions readme.txt
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,10 @@ Version [2.4.4] - Released on 2025-08-XX
- Fixed: Saving email templates without stripping html
- New: Option to allow site owners to edit users on their site
- Fixed: Invoices not loading when logo is not set
- Fixed: Verify DNS settings when using a reverse proxy
- Improved: Lazy load limitations for better performance and compatibility
- New: Add Admin Notice if sunrise.php is not setup
- New: Option to not always create www. subdomains with hosting integrations

Version [2.4.3] - Released on 2025-08-15
- Fixed: Bug in Slim SEO plugin
Expand Down
Loading