Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
8 changes: 7 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,13 @@ assets/ # JS, CSS, images, fonts
- **Ternary**: Short ternary `?:` allowed.
- **Yoda conditions**: Required in production code (`'value' === $var`). Not required in tests.
- **Strings**: Single quotes preferred. Double quotes only when interpolating.
- **Type hints**: Use where present in existing code. Return type `void` on init methods.
- **Type hints**: Use where present in existing code. **NEVER add PHP return type
declarations (`: void`, `: string`, `: bool`, etc.) to public methods on base/abstract
classes or interfaces** — external addons extend these classes and PHP will fatal if
the child class doesn't declare the same return type. Use `@return` PHPDoc tags instead.
This applies to all classes in `inc/gateways/`, `inc/ui/class-base-element.php`,
`inc/models/class-base-model.php`, `inc/integrations/`, and `inc/checkout/signup-fields/`.
Private and final methods may use PHP return types freely.
Comment on lines +89 to +95

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.

⚠️ Potential issue | 🟡 Minor

Document the checkout scope consistently.

This narrows the rule to inc/checkout/signup-fields/, but the repository guideline still applies it to all of inc/checkout/**/*.php. Leaving AGENTS.md narrower than the actual rule will mislead future checkout changes.

As per coding guidelines {inc/gateways/**/*.php,inc/ui/class-base-element.php,inc/models/class-base-model.php,inc/integrations/**/*.php,inc/checkout/**/*.php}: NEVER add PHP return type declarations (...) to public methods on base/abstract classes or interfaces.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@AGENTS.md` around lines 89 - 95, Update the "**Type hints**" bullet in
AGENTS.md so the scope matches the actual repository guideline: change the
narrowed reference to `inc/checkout/signup-fields/` back to the broader
`inc/checkout/**/*.php` and explicitly list the full pattern set
`{inc/gateways/**/*.php, inc/ui/class-base-element.php,
inc/models/class-base-model.php, inc/integrations/**/*.php,
inc/checkout/**/*.php}` (or an equivalent sentence) and reiterate the rule:
NEVER add PHP return type declarations to public methods on base/abstract
classes or interfaces; use `@return` PHPDoc tags instead, while private and final
methods may use return types.

- **PHPDoc**: Required on classes and public methods in `inc/`. Not required in `tests/`.
Every file header: `@package WP_Ultimo`, `@subpackage`, `@since`.
- **Security guard**: Every PHP file starts with `defined('ABSPATH') || exit;`.
Expand Down
7 changes: 6 additions & 1 deletion inc/checkout/signup-fields/class-base-signup-field.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@
/**
* Creates an cart with the parameters of the purchase being placed.
*
* @extensible External addons extend this class to create custom signup fields.
* Do NOT add PHP return type declarations to public methods — it will
* cause a fatal Compile Error in any addon that overrides the method
* without the matching return type. Use @return PHPDoc tags instead.
*
* @package WP_Ultimo
* @subpackage Checkout
* @since 2.0.0
Expand Down Expand Up @@ -244,7 +249,7 @@ public function calculate_style_attr() {
* @param array $attributes Array containing settings for the field.
* @return void
*/
public function set_attributes($attributes): void {
public function set_attributes($attributes) {

$this->attributes = $attributes;
}
Expand Down
29 changes: 17 additions & 12 deletions inc/gateways/class-base-gateway.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@
* For more info on actual implementations,
* check the Gateway_Manual class and the Gateway_Stripe class.
*
* @extensible External addons (e.g. WooCommerce, custom gateways) extend this class.
* Do NOT add PHP return type declarations to public methods — it will
* cause a fatal Compile Error in any addon that overrides the method
* without the matching return type. Use @return PHPDoc tags instead.
*
* @since 2.0.0
*/
abstract class Base_Gateway {
Expand Down Expand Up @@ -165,7 +170,7 @@ public function __construct($order = null) {
* @param \WP_Ultimo\Checkout\Cart $order The order.
* @return void
*/
public function set_order($order): void {
public function set_order($order) {

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.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🌐 Web query:

In PHP 7.4+, if a parent class method is declared without a parameter type, can a child override it with narrower typed parameters such as int $payment_id, string $gateway_payment_id, or Membership $membership? Does this cause a fatal method signature compatibility error?

💡 Result:

No, in PHP 7.4+, a child class cannot override a parent class method that has no parameter type with a narrower typed parameter like int $payment_id, string $gateway_payment_id, or Membership $membership. This causes a fatal error: "Declaration of Child::method(int $param) must be compatible with Parent::method($param)". Untyped parameters in PHP are treated as accepting any type (effectively mixed). Adding a specific type in the child makes the parameter more specific (narrower), which violates contravariance rules for parameters. Child methods must have parameter types that are less specific or equal to the parent's (or untyped). This enforces the Liskov Substitution Principle: code expecting to pass any value to the parent method must still work when using the child class instance. Prior to PHP 7.4, it raised a warning; since PHP 7.4 with full contravariance support, incompatible signatures are fatal errors (strictly enforced in PHP 8+). Workarounds: runtime type checks inside the child method, or redesign with interfaces/abstract methods using the desired types.

Citations:


Restore parameter types on public gateway base methods.

Removing parameter types from parent methods breaks addon gateway overrides. In PHP 7.4+, if a parent method parameter is untyped, child overrides cannot narrow it with specific types (e.g., int $payment_id, Membership $membership). This violates contravariance rules and causes fatal "Declaration must be compatible" errors when addon gateways load. Return-type removal is safe for extensibility, but parameter-type removal recreates the BC problem on the parameter side.

Affects lines: 173, 217, 597, 616, 631, 646, 824, 861, 873, 885, 899

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@inc/gateways/class-base-gateway.php` at line 173, The public gateway base
methods lost their parameter type hints which breaks addon gateway overrides;
restore the original parameter type declarations on public methods (e.g.,
set_order) and the other listed public methods so child classes can declare
narrower types (for example int $payment_id, Membership $membership) while
keeping return types flexible; update the method signatures in
class-base-gateway.php to include the appropriate parameter type hints that
match existing addon expectations and PHP 7.4 contravariance rules, but avoid
adding stricter return types that could break overrides.


if (null === $order) {
return;
Expand Down Expand Up @@ -209,7 +214,7 @@ final public function get_id() {
* @param \WP_Ultimo\Models\Membership $membership The membership.
* @return array{brand: string, last4: string}|null Payment method info, or null.
*/
public function get_payment_method_display($membership): ?array {
public function get_payment_method_display($membership) {
unset($membership);
return null;
}
Expand Down Expand Up @@ -568,7 +573,7 @@ public function process_confirmation() {}
* @since 2.0.0
* @return bool
*/
public function supports_payment_polling(): bool {
public function supports_payment_polling() {

return false;
}
Expand All @@ -589,7 +594,7 @@ public function supports_payment_polling(): bool {
* @param int $payment_id The local payment ID to verify.
* @return array{success: bool, status: string, message: string}
*/
public function verify_and_complete_payment(int $payment_id): array {
public function verify_and_complete_payment($payment_id) {

return [
'success' => false,
Expand All @@ -608,7 +613,7 @@ public function verify_and_complete_payment(int $payment_id): array {
* @param string $gateway_payment_id The gateway payment id.
* @return string
*/
public function get_payment_url_on_gateway($gateway_payment_id): string {
public function get_payment_url_on_gateway($gateway_payment_id) {
unset($gateway_payment_id);
return '';
}
Expand All @@ -623,7 +628,7 @@ public function get_payment_url_on_gateway($gateway_payment_id): string {
* @param string $gateway_subscription_id The gateway subscription id.
* @return string
*/
public function get_subscription_url_on_gateway($gateway_subscription_id): string {
public function get_subscription_url_on_gateway($gateway_subscription_id) {
unset($gateway_subscription_id);
return '';
}
Expand All @@ -638,7 +643,7 @@ public function get_subscription_url_on_gateway($gateway_subscription_id): strin
* @param string $gateway_customer_id The gateway customer id.
* @return string
*/
public function get_customer_url_on_gateway($gateway_customer_id): string {
public function get_customer_url_on_gateway($gateway_customer_id) {
unset($gateway_customer_id);
return '';
}
Expand Down Expand Up @@ -816,7 +821,7 @@ public function get_confirm_url() {
* @param string $message The error message.
* @return void
*/
public function redirect_with_error(string $message): void {
public function redirect_with_error($message) {

$url = remove_query_arg(['wu-confirm', 'payment', 'token', 'PayerID', 'ba_token', 'subscription_id', 'status'], $this->return_url ?: wu_get_current_url());

Expand Down Expand Up @@ -853,7 +858,7 @@ public function get_webhook_listener_url() {
* @param \WP_Ultimo\Models\Payment $payment The payment.
* @return void
*/
public function set_payment($payment): void {
public function set_payment($payment) {

$this->payment = $payment;
}
Expand All @@ -865,7 +870,7 @@ public function set_payment($payment): void {
* @param \WP_Ultimo\Models\Membership $membership The membership.
* @return void
*/
public function set_membership($membership): void {
public function set_membership($membership) {

$this->membership = $membership;
}
Expand All @@ -877,7 +882,7 @@ public function set_membership($membership): void {
* @param \WP_Ultimo\Models\Customer $customer The customer.
* @return void
*/
public function set_customer($customer): void {
public function set_customer($customer) {

$this->customer = $customer;
}
Expand All @@ -891,7 +896,7 @@ public function set_customer($customer): void {
* @param \WP_Ultimo\Models\Membership $membership The membership object.
* @return void
*/
public function trigger_payment_processed($payment, $membership = null): void {
public function trigger_payment_processed($payment, $membership = null) {

if (null === $membership) {
$membership = $payment->get_membership();
Expand Down
4 changes: 2 additions & 2 deletions inc/gateways/class-base-paypal-gateway.php
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ protected function get_subscription_description($cart): string {
* @param string $gateway_payment_id The gateway payment id.
* @return string
*/
public function get_payment_url_on_gateway($gateway_payment_id): string {
public function get_payment_url_on_gateway($gateway_payment_id) {

if (empty($gateway_payment_id)) {
return '';
Expand All @@ -152,7 +152,7 @@ public function get_payment_url_on_gateway($gateway_payment_id): string {
* @param string $gateway_subscription_id The gateway subscription id.
* @return string
*/
public function get_subscription_url_on_gateway($gateway_subscription_id): string {
public function get_subscription_url_on_gateway($gateway_subscription_id) {

if (empty($gateway_subscription_id)) {
return '';
Expand Down
12 changes: 6 additions & 6 deletions inc/gateways/class-base-stripe-gateway.php
Original file line number Diff line number Diff line change
Expand Up @@ -828,7 +828,7 @@ public function get_change_payment_method_url($membership) {
* @param \WP_Ultimo\Models\Membership $membership The membership.
* @return array{brand: string, last4: string}|null Payment method info, or null.
*/
public function get_payment_method_display($membership): ?array {
public function get_payment_method_display($membership) {

try {
$sub_id = $membership->get_gateway_subscription_id();
Expand Down Expand Up @@ -3832,7 +3832,7 @@ public function get_user_saved_payment_methods() {
* @param string $gateway_payment_id The gateway payment id.
* @return string.
*/
public function get_payment_url_on_gateway($gateway_payment_id): string {
public function get_payment_url_on_gateway($gateway_payment_id) {

$route = $this->test_mode ? '/test' : '/';

Expand All @@ -3855,7 +3855,7 @@ public function get_payment_url_on_gateway($gateway_payment_id): string {
* @param string $gateway_subscription_id The gateway subscription id.
* @return string.
*/
public function get_subscription_url_on_gateway($gateway_subscription_id): string {
public function get_subscription_url_on_gateway($gateway_subscription_id) {

$route = $this->test_mode ? '/test' : '/';

Expand All @@ -3872,7 +3872,7 @@ public function get_subscription_url_on_gateway($gateway_subscription_id): strin
* @param string $gateway_customer_id The gateway customer id.
* @return string.
*/
public function get_customer_url_on_gateway($gateway_customer_id): string {
public function get_customer_url_on_gateway($gateway_customer_id) {

$route = $this->test_mode ? '/test' : '/';

Expand All @@ -3883,7 +3883,7 @@ public function get_customer_url_on_gateway($gateway_customer_id): string {
* @inheritdoc
* @since 2.0.0
*/
public function supports_payment_polling(): bool {
public function supports_payment_polling() {

return true;
}
Expand All @@ -3899,7 +3899,7 @@ public function supports_payment_polling(): bool {
* @param int $payment_id The local payment ID to verify.
* @return array{success: bool, message: string, status?: string}
*/
public function verify_and_complete_payment(int $payment_id): array {
public function verify_and_complete_payment($payment_id) {

$payment = wu_get_payment($payment_id);

Expand Down
2 changes: 1 addition & 1 deletion inc/gateways/class-paypal-gateway.php
Original file line number Diff line number Diff line change
Expand Up @@ -1715,7 +1715,7 @@ public function get_checkout_details($token = '') {
* @param string $gateway_payment_id The gateway payment id.
* @return string
*/
public function get_payment_url_on_gateway($gateway_payment_id): string {
public function get_payment_url_on_gateway($gateway_payment_id) {

return '';
}
Expand Down
4 changes: 2 additions & 2 deletions inc/gateways/class-paypal-rest-gateway.php
Original file line number Diff line number Diff line change
Expand Up @@ -2090,7 +2090,7 @@ public function preserve_oauth_settings(array $settings, array $settings_to_save
* @inheritdoc
* @since 2.0.0
*/
public function supports_payment_polling(): bool {
public function supports_payment_polling() {

return true;
}
Expand All @@ -2107,7 +2107,7 @@ public function supports_payment_polling(): bool {
* @param int $payment_id The local payment ID to verify.
* @return array{success: bool, message: string, status?: string}
*/
public function verify_and_complete_payment(int $payment_id): array {
public function verify_and_complete_payment($payment_id) {

$payment = wu_get_payment($payment_id);

Expand Down
19 changes: 12 additions & 7 deletions inc/integrations/class-base-capability-module.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@
/**
* Abstract base class for capability modules.
*
* @extensible External addons extend this class to add capability modules.
* Do NOT add PHP return type declarations to public methods — it will
* cause a fatal Compile Error in any addon that overrides the method
* without the matching return type. Use @return PHPDoc tags instead.
*
* @since 2.5.0
*/
abstract class Base_Capability_Module implements Capability_Module {
Expand Down Expand Up @@ -45,7 +50,7 @@ abstract class Base_Capability_Module implements Capability_Module {
* @param string $feature The feature identifier to check.
* @return bool
*/
public function supports(string $feature): bool {
public function supports($feature) {

return in_array($feature, $this->supported_features, true);
}
Expand All @@ -58,44 +63,44 @@ public function supports(string $feature): bool {
* @param Integration $integration The parent integration instance.
* @return void
*/
public function set_integration(Integration $integration): void {
public function set_integration(Integration $integration) {

$this->integration = $integration;
}

/**
* {@inheritdoc}
*/
public function get_integration(): Integration {
public function get_integration() {

return $this->integration;
}

/**
* {@inheritdoc}
*/
public function get_supported_features(): array {
public function get_supported_features() {

return $this->supported_features;
}

/**
* {@inheritdoc}
*/
public function register_hooks(): void {}
public function register_hooks() {}

/**
* {@inheritdoc}
*/
public function get_fields(): array {
public function get_fields() {

return [];
}

/**
* {@inheritdoc}
*/
public function get_explainer_lines(): array {
public function get_explainer_lines() {

return [
'will' => [],
Expand Down
Loading
Loading