-
Notifications
You must be signed in to change notification settings - Fork 14
Fix $input
type hint in WP_Ability
#61
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## trunk #61 +/- ##
============================================
- Coverage 84.28% 83.33% -0.95%
+ Complexity 97 96 -1
============================================
Files 8 8
Lines 509 516 +7
============================================
+ Hits 429 430 +1
- Misses 80 86 +6
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
840b89b
to
83bf51c
Compare
This still needs some additional test coverage to account for the different scenarios for input values passed to |
@galatanovidiu, I would appreciate a sanity check in relation to the MCP Adapter. Would the changes proposed introduce the need for some additional handling there, as the input type might become a simple type now? The same question applies to the input schema, but that part I'm less afraid of, unless the input in MCP has some specific requirements. |
* @return mixed|\WP_Error The result of the ability execution, or WP_Error on failure. | ||
*/ | ||
protected function do_execute( array $input ) { | ||
protected function do_execute( $input ) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ambiguous handling of null
input in ability execution:
Currently, when $input
is null
in the do_execute()
method, it gets passed to the callback as call_user_func($callback, null)
, meaning the callback receives null as its first argument.
This creates an ambiguity:
Case 1: Developer wants to call a callback with no arguments (e.g., wp_get_current_user()
)
Case 2: Developer wants to pass null
as a meaningful argument to the callback explicitly
Problem scenarios:
Callbacks that expect no parameters (like wp_get_current_user()
) will receive an unexpected null parameter
No way to distinguish between "no arguments" vs "null argument"
Proposed solutions:
Use Reflection API to inspect the callback's parameter signature and make intelligent decisions:
If callback expects 0 parameters and input is null: call without arguments: call_user_func($callback)
Otherwise pass input as argument: call_user_func($callback, $input)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The same problem existed before when this was an array and the default value was []
. I agree it would be nice to remove the ambiguity of the null
value by not using it as the default value.
It would be a nice enhancement to support some built-in functions, as a possible target for execute_callback
. However, it's a more nuanced topic, because do_execute
would have to follow the same type mixed ...$args
like call_user_func
does, while it's currently a single param. It would be convenient for PHP implementers, but this becomes harder to describe with JSON schema and handle through the REST API. What is the approach you had in mind here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not make the decision for whether to pass $input
based on the schema? I think using reflection for this is unnecessary and less efficient.
Case in point: validate_input()
already returns true
without checking anything if the input schema is empty.
Similarly, we could implement this as follows: If the input schema is empty, we don't pass $input
to any callbacks (execute_callback
, permission_callback
).
- Makes sense because if there's no input schema, you don't expect any input.
- If you at the same time pass a callback that expects an input, you're doing it wrong.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The idea shared by @felixarntz that addresses the concerns should be fully implemented in 1935799. I still need to increase test coverage.
5f899d0
to
e117cac
Compare
In e117cac added tests that cover several types of input:
Still to cover:
|
@felixarntz, this is in a shape where it could use some feedback, so we can align if this is going in the right direction. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks good regarding getting rid of the array requirement. I left some thoughts regarding the handling of expecting no input / no parameters.
e117cac
to
1117ceb
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
Fixes type hints for the $input
parameter in the WP_Ability
class and related methods, changing from array
to mixed
to support all JSON schema types including boolean, number, string, array, and object.
- Updated all method signatures and documentation to use
mixed
instead ofarray
for input parameters - Modified test callbacks to match the new signatures where appropriate
- Updated REST API controller to properly handle mixed input types without assuming array format
Reviewed Changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 4 comments.
Show a summary per file
File | Description |
---|---|
includes/abilities-api/class-wp-ability.php |
Core changes to method signatures and documentation for execute, has_permission, and validation methods |
includes/abilities-api/class-wp-abilities-registry.php |
Updated PHPDoc type annotations for callback parameters |
includes/abilities-api.php |
Updated PHPDoc type annotations for wp_register_ability function |
includes/rest-api/endpoints/class-wp-rest-abilities-run-controller.php |
Modified input handling to support mixed types and updated documentation |
tests/unit/abilities-api/wpAbility.php |
Added comprehensive test coverage for different input types (boolean, number, string, array) |
tests/unit/abilities-api/wpRegisterAbility.php |
Updated mock ability class signature |
tests/unit/rest-api/wpRestAbilitiesRunController.php |
Updated test callback signatures to match new mixed input type |
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
b54b402
to
75d89cf
Compare
36cb76c
to
1935799
Compare
The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message.
To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook. |
It's ready for the final review. I addressed the feedback, increased test coverage, and updated existing documentation. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@gziolo The type hint fix here LGTM, but I am realizing that there's IMO a security flaw in how the validation of the input is handled. It's not introduced by this PR, but it would be critical to fix, unless I'm missing something.
cc @justlevine
$input_schema = $this->get_input_schema(); | ||
if ( empty( $input_schema ) ) { | ||
return true; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Giving this another thought, should we really do it like that? I think it's problematic from a security standpoint:
- The
input_schema
is critical in validating that the input is expected, including from a security perspective. - It is already required, which makes sense.
- The only reasonable explanation for providing an empty
$input_schema
is that the ability does not expect any input. (It makes sense that it's required regardless, to make that "opt out" explicit.) But technically, it's still possible to pass$input
to the ability. - Problem: This code interprets the lack of
$input_schema
as "let's not validate" - I think this is a security concern. Instead, it should interpret the lack of$input_schema
as: If there is any input (i.e.$input !== null
), this is invalid and it should return aWP_Error
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm open to hardening this one. I wasn't sure what's the best way to go about it, so all it worth taking into consideration. It means it will become very strict in the sense that the only case when we allow not passing the input schema is when there is no input at all. Otherwise, even when passing null
as value, we would have to validatr with input schema provided.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To me passing null
as $input
is the same as not passing any $input
at all. If the developer provided an input schema that was only allowing null
, it would be pointless to do so anyway because it doesn't have any meaning. And if the value null
is being passed in a situation where passing another value is possible, that would only work in combination with providing a (oneOf) schema anyway.
So to clarify what I meant: If there's no input schema, it's fine as long as $input === null
. Whether it was passed as null
or whether it's just the null
default shouldn't matter.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So to clarify what I meant: If there's no input schema, it's fine as long as
$input === null
. Whether it was passed asnull
or whether it's just thenull
default shouldn't matter.
I agree with the rationale. I committed your proposed changes, and I will update the rest of the codebase accordingly to make it work this way.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
c377af9 should do the trick.
I will look into failing unit tests tomorrow as REST API controllers somehow depend on the old behavior.
Co-authored-by: Felix Arntz <[email protected]>
Co-authored-by: Felix Arntz <[email protected]>
Co-authored-by: Felix Arntz <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This changes the type hint from array to mixed to support all JSON schema types, including null, boolean, number, integer, string, array, and object.
Seeing the diff reinforces my concern about how PHP's mixed
is broader than "all JSON schema types".
( I'm not sure why we want to allow for non-named arguments here, and still think that we should use WP_REST_Controller
as the prior art here where only named inputs ('args': array<string,mixed>|null
) are supported. I cannot think of a justifiable DX we're we (humans, tools, agents, implementations) would prefer to be fully-dependent on also having the WP_Ability::$input_schema
to understand the shape, instead of inferring/juggling an array key. ( e.g. get_post( $input ) is always going to less semantic than get_post( $input['user_id'] )` )
If we are going to allow more than named argments , then we can at least be accurate about the PHP type, and use null|bool|int|float|string|mixed[]|object
, instead of littering the codebase with an avoidable mixed.
Last time I bring all that up I promise 🤐)
Regarding the input schema validation, agree that semantically it should be if ( null === $input_schema ) { return true; }
while if ( empty( $input_schema ) ) { return new \WP_Error()
A few search-replaces ( (nevermind ) LGTM$input=
=> $input
) and
@justlevine, so you suggest we replace
or more precisely
The order might be wrong as it matters here because of Type Juggling. Based on your feedback, we would also need to make the object type more structured. Although I see that it needs to implement |
I added specific types in the REST API schema in a21105f. @justlevine, what else would you like to have included? I may not fully understand your expectations. This looks good enough for me at this point. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@gziolo This looks great to me, thank you for the updates!
Just one minor note that I think would be worth updating before merge.
Co-authored-by: Felix Arntz <[email protected]>
@justlevine, I'm still happy to discuss and iterate when you have a chance to respond. Now, I will land this PR as it addresses a larger issue. |
Closes #58.
Given that
$input
is used with two public methodsexecute
,has_permission
, and other methods internally, the code inWP_Ability
class was adjusted accordingly to use a unified approach. This changes the type hint from array to mixed to support all JSON schema types, includingnull
,boolean
,number
,integer
,string
,array
, andobject
.$input
argument optional inexecute_callback
andpermission_callback
when theinput_schema
is not defined and the value isnull
.Testing
All unit tests should pass: