Conversation
WalkthroughIntroduces BunnyNet_Host_Provider, a new integration class for BunnyNet CDN/DNS services within WP_Ultimo. Implements domain and subdomain lifecycle hooks for DNS record management, BunnyNet API communication, connection testing, and installation configuration. Includes setup instructions view file. Changes
Sequence DiagramsequenceDiagram
actor Admin
participant WP_Ultimo
participant BunnyNet_Provider
participant BunnyNet_API
participant DNS_System
Admin->>WP_Ultimo: Add new subdomain
WP_Ultimo->>BunnyNet_Provider: on_add_subdomain(subdomain, site_id)
BunnyNet_Provider->>BunnyNet_Provider: Construct domain & API payload
BunnyNet_Provider->>BunnyNet_API: bunnynet_api_call(endpoint, 'POST', dns_data)
BunnyNet_API->>DNS_System: Create DNS records
BunnyNet_API-->>BunnyNet_Provider: Confirm records created
BunnyNet_Provider->>WP_Ultimo: Log success
WP_Ultimo-->>Admin: Subdomain configured
rect rgb(240, 248, 255)
note right of BunnyNet_Provider: Handles www subdomain<br/>automatically if configured
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes
Poem
Pre-merge checks and finishing touches✅ Passed checks (3 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (1)
inc/integrations/host-providers/class-bunnynet-host-provider.php (1)
160-175: Consider adding explicit return for clarity.While
wp_send_json_error()terminates execution internally, adding an explicitreturnafter line 166 would improve code readability and make the control flow clearer.if (! $zone_id) { wp_send_json_error(new \WP_Error('bunnynet-error', __('Zone ID is required.', 'ultimate-multisite'))); + return; }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
assets/img/hosts/bunnynet.svgis excluded by!**/*.svg
📒 Files selected for processing (2)
inc/integrations/host-providers/class-bunnynet-host-provider.php(1 hunks)views/wizards/host-integrations/bunnynet-instructions.php(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
inc/integrations/host-providers/class-bunnynet-host-provider.php (2)
inc/integrations/host-providers/class-base-host-provider.php (1)
supports(337-340)inc/managers/class-domain-manager.php (1)
should_create_www_subdomain(461-484)
🪛 PHPMD (2.15.0)
inc/integrations/host-providers/class-bunnynet-host-provider.php
196-196: Avoid unused parameters such as '$domain'. (undefined)
(UnusedFormalParameter)
196-196: Avoid unused parameters such as '$site_id'. (undefined)
(UnusedFormalParameter)
206-206: Avoid unused parameters such as '$domain'. (undefined)
(UnusedFormalParameter)
206-206: Avoid unused parameters such as '$site_id'. (undefined)
(UnusedFormalParameter)
293-293: Avoid unused parameters such as '$site_id'. (undefined)
(UnusedFormalParameter)
307-307: Avoid unused local variables such as '$original_subdomain'. (undefined)
(UnusedLocalVariable)
🔇 Additional comments (6)
views/wizards/host-integrations/bunnynet-instructions.php (1)
1-102: LGTM!The instructions view is well-structured with proper WordPress i18n escaping using
esc_html_e(). The five-step guide covers the essential BunnyNet setup workflow including security guidance for the API key.inc/integrations/host-providers/class-bunnynet-host-provider.php (5)
21-68: LGTM!Class declaration follows the established pattern from
Base_Host_Provider. The$supportsarray and$constantsdefinitions are correctly configured for BunnyNet integration.
79-119: LGTM!The DNS entries augmentation logic correctly handles the BunnyNet API response, filters records by domain, and properly translates the
@symbol for root domain records.
121-152: LGTM!The
detect()stub andget_fields()implementation are appropriate. The field order shows Zone ID first but the$constantsarray has API Key first - this is fine as field order is for UI while constants are for validation.
196-206: Empty stubs are intentional.The unused parameters flagged by static analysis are expected here - these are interface methods from
Base_Host_Providerthat must maintain the signature even when the implementation doesn't use them.
437-453: LGTM!The
get_explainer_lines()method correctly differentiates between subdomain and subdirectory installs, providing appropriate messaging for each context.
| foreach ($domains_to_send as $subdomain) { | ||
| $server_addr = isset($_SERVER['SERVER_ADDR']) ? sanitize_text_field(wp_unslash($_SERVER['SERVER_ADDR'])) : ''; | ||
|
|
||
| $data = apply_filters( | ||
| 'wu_bunnynet_on_add_domain_data', | ||
| [ | ||
| 'Type' => 0, // A record type | ||
| 'Ttl' => 3600, | ||
| 'Name' => $subdomain, | ||
| 'Value' => $server_addr, | ||
| ], | ||
| $subdomain, | ||
| $site_id | ||
| ); | ||
|
|
||
| $results = $this->bunnynet_api_call("dnszone/$zone_id/records", 'PUT', $data); | ||
|
|
||
| if (is_wp_error($results)) { | ||
| wu_log_add('integration-bunnynet', sprintf('Failed to add subdomain "%s" to BunnyNet. Reason: %s', $subdomain, $results->get_error_message()), LogLevel::ERROR); | ||
|
|
||
| return; | ||
| } |
There was a problem hiding this comment.
Variable shadowing and potential empty IP address.
Two concerns in this loop:
-
Variable shadowing: Reusing
$subdomainin the foreach (line 256) shadows the outer variable, reducing readability. -
Empty IP address:
$_SERVER['SERVER_ADDR']may be empty in CLI contexts, load balancer setups, or CGI environments. This would create DNS records pointing to an empty value.
- foreach ($domains_to_send as $subdomain) {
+ foreach ($domains_to_send as $subdomain_to_add) {
$server_addr = isset($_SERVER['SERVER_ADDR']) ? sanitize_text_field(wp_unslash($_SERVER['SERVER_ADDR'])) : '';
+ if (empty($server_addr)) {
+ wu_log_add('integration-bunnynet', 'SERVER_ADDR is not available. Cannot determine server IP for DNS record.', LogLevel::ERROR);
+ return;
+ }
+
$data = apply_filters(
'wu_bunnynet_on_add_domain_data',
[
'Type' => 0, // A record type
'Ttl' => 3600,
- 'Name' => $subdomain,
+ 'Name' => $subdomain_to_add,
'Value' => $server_addr,
],
- $subdomain,
+ $subdomain_to_add,
$site_id
);
$results = $this->bunnynet_api_call("dnszone/$zone_id/records", 'PUT', $data);
if (is_wp_error($results)) {
- wu_log_add('integration-bunnynet', sprintf('Failed to add subdomain "%s" to BunnyNet. Reason: %s', $subdomain, $results->get_error_message()), LogLevel::ERROR);
+ wu_log_add('integration-bunnynet', sprintf('Failed to add subdomain "%s" to BunnyNet. Reason: %s', $subdomain_to_add, $results->get_error_message()), LogLevel::ERROR);
return;
}
- wu_log_add('integration-bunnynet', sprintf('Added sub-domain "%s" to BunnyNet.', $subdomain));
+ wu_log_add('integration-bunnynet', sprintf('Added sub-domain "%s" to BunnyNet.', $subdomain_to_add));
}📝 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.
| foreach ($domains_to_send as $subdomain) { | |
| $server_addr = isset($_SERVER['SERVER_ADDR']) ? sanitize_text_field(wp_unslash($_SERVER['SERVER_ADDR'])) : ''; | |
| $data = apply_filters( | |
| 'wu_bunnynet_on_add_domain_data', | |
| [ | |
| 'Type' => 0, // A record type | |
| 'Ttl' => 3600, | |
| 'Name' => $subdomain, | |
| 'Value' => $server_addr, | |
| ], | |
| $subdomain, | |
| $site_id | |
| ); | |
| $results = $this->bunnynet_api_call("dnszone/$zone_id/records", 'PUT', $data); | |
| if (is_wp_error($results)) { | |
| wu_log_add('integration-bunnynet', sprintf('Failed to add subdomain "%s" to BunnyNet. Reason: %s', $subdomain, $results->get_error_message()), LogLevel::ERROR); | |
| return; | |
| } | |
| foreach ($domains_to_send as $subdomain_to_add) { | |
| $server_addr = isset($_SERVER['SERVER_ADDR']) ? sanitize_text_field(wp_unslash($_SERVER['SERVER_ADDR'])) : ''; | |
| if (empty($server_addr)) { | |
| wu_log_add('integration-bunnynet', 'SERVER_ADDR is not available. Cannot determine server IP for DNS record.', LogLevel::ERROR); | |
| return; | |
| } | |
| $data = apply_filters( | |
| 'wu_bunnynet_on_add_domain_data', | |
| [ | |
| 'Type' => 0, // A record type | |
| 'Ttl' => 3600, | |
| 'Name' => $subdomain_to_add, | |
| 'Value' => $server_addr, | |
| ], | |
| $subdomain_to_add, | |
| $site_id | |
| ); | |
| $results = $this->bunnynet_api_call("dnszone/$zone_id/records", 'PUT', $data); | |
| if (is_wp_error($results)) { | |
| wu_log_add('integration-bunnynet', sprintf('Failed to add subdomain "%s" to BunnyNet. Reason: %s', $subdomain_to_add, $results->get_error_message()), LogLevel::ERROR); | |
| return; | |
| } |
🤖 Prompt for AI Agents
inc/integrations/host-providers/class-bunnynet-host-provider.php around lines
256-277: the foreach reuses $subdomain (shadowing an outer variable) and reads
$_SERVER['SERVER_ADDR'] which can be empty; rename the loop variable (e.g.
$domain_to_send) to avoid shadowing, move retrieval and sanitization of the
server IP out of the loop so it's computed once, and if the sanitized
SERVER_ADDR is empty attempt fallbacks such as resolving $_SERVER['SERVER_NAME']
or gethostname() with gethostbyname(), and if still empty log an error and skip
adding that DNS record instead of creating a record with an empty value. Ensure
you update variable names used in the data payload and leave the API call and
error handling logic unchanged otherwise.
| $original_subdomain = $subdomain; | ||
|
|
||
| $subdomain = rtrim(str_replace($current_site->domain, '', $subdomain), '.'); | ||
|
|
||
| if (! $subdomain) { | ||
| return; | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
Remove unused variable.
$original_subdomain is assigned but never used. This appears to be leftover from development.
- $original_subdomain = $subdomain;
-
$subdomain = rtrim(str_replace($current_site->domain, '', $subdomain), '.');📝 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.
| $original_subdomain = $subdomain; | |
| $subdomain = rtrim(str_replace($current_site->domain, '', $subdomain), '.'); | |
| if (! $subdomain) { | |
| return; | |
| } | |
| $subdomain = rtrim(str_replace($current_site->domain, '', $subdomain), '.'); | |
| if (! $subdomain) { | |
| return; | |
| } |
🧰 Tools
🪛 PHPMD (2.15.0)
307-307: Avoid unused local variables such as '$original_subdomain'. (undefined)
(UnusedLocalVariable)
🤖 Prompt for AI Agents
In inc/integrations/host-providers/class-bunnynet-host-provider.php around lines
307 to 313, the variable $original_subdomain is assigned but never used; remove
the unused assignment to clean up the code by deleting the line that sets
$original_subdomain = $subdomain so only the remaining logic (normalizing
$subdomain and early return) remains.
| protected function bunnynet_api_call($endpoint = 'dnszone', $method = 'GET', $data = []): object { | ||
|
|
||
| $api_url = 'https://api.bunny.net/'; | ||
|
|
||
| $endpoint_url = $api_url . $endpoint; | ||
|
|
||
| $args = [ | ||
| 'method' => $method, | ||
| 'headers' => [ | ||
| 'AccessKey' => defined('WU_BUNNYNET_API_KEY') ? WU_BUNNYNET_API_KEY : '', | ||
| 'Content-Type' => 'application/json', | ||
| ], | ||
| ]; | ||
|
|
||
| if ('GET' !== $method && ! empty($data)) { | ||
| $args['body'] = wp_json_encode($data); | ||
| } | ||
|
|
||
| $response = wp_remote_request($endpoint_url, $args); | ||
|
|
||
| if (! is_wp_error($response)) { | ||
| $body = wp_remote_retrieve_body($response); | ||
| $code = wp_remote_retrieve_response_code($response); | ||
|
|
||
| if ($code >= 200 && $code < 300) { | ||
| return json_decode($body); | ||
| } else { | ||
| $error_message = wp_remote_retrieve_response_message($response); | ||
|
|
||
| $response = new \WP_Error('bunnynet-error', sprintf('%s: %s', $error_message, $body)); | ||
| } | ||
| } | ||
|
|
||
| return $response; | ||
| } |
There was a problem hiding this comment.
Return type mismatch: method returns \WP_Error but declares object.
The return type declaration object is incorrect since the method can return \WP_Error. Additionally, json_decode() on an empty body returns null, which would violate the object return type.
- protected function bunnynet_api_call($endpoint = 'dnszone', $method = 'GET', $data = []): object {
+ protected function bunnynet_api_call($endpoint = 'dnszone', $method = 'GET', $data = []) {Alternatively, update to a union type if PHP 8.0+ is required:
protected function bunnynet_api_call($endpoint = 'dnszone', $method = 'GET', $data = []): object|\WP_Error {Also consider handling empty responses:
if ($code >= 200 && $code < 300) {
- return json_decode($body);
+ $decoded = json_decode($body);
+ return $decoded ?? (object) [];
} else {🤖 Prompt for AI Agents
In inc/integrations/host-providers/class-bunnynet-host-provider.php around lines
362 to 396, the method declares a return type of object but may return a
\WP_Error or null from json_decode; change the method signature to a union that
allows \WP_Error and null (or remove the return type if running on PHP <8.0),
and add handling for empty or unparseable bodies: after json_decode, if the body
is empty return an empty object (e.g. (object)[]), and if json_decode returns
null while the body is non-empty return a \WP_Error with the json error; ensure
all return paths match the updated signature.
Summary by CodeRabbit
Release Notes
✏️ Tip: You can customize this high-level summary in your review settings.