-
-
Notifications
You must be signed in to change notification settings - Fork 20
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
Type check first parameter for Json::stringToObject() #63
base: 2.0-dev
Are you sure you want to change the base?
Conversation
# Conflicts: # composer.json # src/Format/Json.php
This entire pull request is unnecessary and implicitly changes the API contract. You are basically saying that because of the lack of scalar typehints, the API contract is now You are unilaterally deciding that an untyped parameter is now explicitly treated as mixed. Do you intend to introduce runtime type checks and deprecation notices for every parameter without a runtime typehint? |
The API contract is factually that way, because it works that way (in some versions of PHP). People do whatever PHP accepts, not what's written in the documentation. Even the CMS passes One could decide to repair the data by silently cast them to string (as PHP did before version 8), but that's not a suitable strategy for a framework. The way to go is heading towards typehinting everything, but we must give people time to repair their usage of the framework code. Hence the deprecation warning - trying to find a balance between strictness and pragmatism.
That's not me, it's PHP. Without this patch, users get a
We might need to, because with version 3 we want to get as close as possible to complete typehinting of parameters and return values. |
Then there's no point in documenting the API and you might as well nuke all of the PHP's type coercion still exists. But, there are some major differences in engine level C code and userland PHP code, namely how null is handled. Pretty much everything in the engine supports coercion from null (passing null to a string typehinted argument will be coerced to an empty string), however this same behavior does not exist in userland (passing null to a string typehinted argument emits a TypeError); https://3v4l.org/ZDvTd as a demonstration. PHP 8.1 specifically deprecates passing null to engine functions not declared nullable with intent to remove this coercion in PHP 9, bringing feature parity between the engine and userland. Nobody else in the PHP world considers an untyped API explicitly supporting a mixed value when a typehint is omitted, the doc block (when present) is the contract and the only response needed when folks file bug reports trying to use the API in an unsupported manner is to tell them they're Doing It Wrong™ and to update their code. Yes, that is painful to hear, but it is the right response.
Anyone relying on type coercion as an explicit feature is waiting to be bitten by a bug in production code. A framework performing unexpected type coercion of a provided value is just as bad as the engine silently coercing values IMO. The typecast added in this patch has Bad Idea written all over it IMO (not to mention that it's basically an incomplete patch, you aren't handling types that don't convert to string very well (like arrays or resources), and float-to-string conversion runs the risk of data loss depending on the PHP configuration).
Users will only see this message if they are using the API incorrectly. And, your message truthfully does not provide a more actionable message than the PHP deprecation message; the only difference is one is an engine deprecation from a core function called by this class and one is a user deprecation emitted from this class (literally one frame higher up the stack than if the PHP deprecation were to be emitted). Either way, that does not change the fact that you are explicitly changing the API to support a mixed type and creating a runtime behavior that contradicts the documented expectations. Because you are explicitly widening the supported types of the Just let the engine spit out the relevant warning or error if someone is doing it wrong, it's usually going to have a better explanation of the issue (even if it means someone might have to step through a couple of frames of third-party code to understand why it was emitted) compared to an overzealous type cast to avoid an engine deprecation notice because the API is duck typed and runs a real risk of bad data manipulation. |
Got your point. I tend to agree with your view. But then we have to make that clearer in the documentation. I will bring this up for discussion at the next production meeting. Moved this into draft state. Thank you for your detailed explanation! |
Thank you @mbabker for sharing your thoughts on the importance of API documentation and type hints in PHP. I understand that you believe relying too heavily on type coercion or duck typing can be risky and lead to unexpected behavior in production code. You suggest that sticking to documented API contracts and relying on PHP engine's error messages is a safer approach. I appreciate your insights and would like to know more about the best approach for adding type hints to the code. How can we make this change in a gradual manner and ensure that other developers who may be using the code are not caught off guard? Overall, I think your points are well taken, and I look forward to hearing more about how we can implement these ideas in practice. |
My stance is not much different than how the Laravel ecosystem deals with their duck typed APIs. The doc blocks are the contracts, anyone not respecting them is already Doing It Wrong™. Will runtime checks make it more obvious they are doing so? Absolutely. Is type coercion after said runtime check an appropriate solution? Absolutely not. IMO, it is not a library author's responsibility to add runtime type checks (or coerce the input, which can result in data loss) inside a duck-typed function. If the intent is to add types to the signatures with the next major release, just do it, and document the change as part of the upgrade guide. Now, here is the kicker from my experience in the (broken) Joomla development ecosystem. The support, or lack thereof, for null is rarely documented. There are a lot of places where null is legally passed through and handled but the value is not a documented acceptable type (in fact, here's a prime example from Long and short, you don't need runtime checks/deprecations in duck-typed APIs when planning to change to runtime declared types AS LONG AS the runtime signature matches the implied signature from the doc blocks; the doc block already serves as the API contract and no behavioral change for proper use of the API will be introduced by moving that contract from documentation to runtime. You do need runtime checks/deprecations in either duck-typed or fully-typed APIs when intending to narrow the supported types. |
Pull Request for Issue joomla/joomla-cms#37783
Replaces PR #61
Summary of Changes
If the first parameter in a call to
Json::stringToObject()
is not a string, an E_USER_DEPRECATED error is triggered. and the value cast to string.The next major version should typehint the parameter and remove the check.
Testing Instructions
Call the method with
null
as the first parameter. A log entry should be written to the deprecation log, if enabled. The return value should be an emptystdClass
object.Documentation Changes Required
No. The change actually just enforces the documented behaviour.