-
Notifications
You must be signed in to change notification settings - Fork 7.8k
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
[RFC] Add is_countable function #3026
Conversation
@@ -4281,6 +4281,19 @@ ZEND_API zend_bool zend_is_iterable(zval *iterable) /* {{{ */ | |||
} | |||
/* }}} */ | |||
|
|||
ZEND_API zend_bool zend_is_countable(zval *countable) /* {{{ */ |
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.
If possible we should make this zval const *
. At a glance it depends if instanceof_function
and related calls are const or not.
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.
By convention, zvals are never passed by const pointer.
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 it should stay as it is?
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 convention likely stems from the days where our zvals directly contained reference counting information. Passing such values by const pointer would prevent making copies on reference counted types, so it wasn't done.
I would prefer it to be const - maybe we should wait for someone else (maybe @dstogov?) to chime in.
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.
Can we please move this discussion elsewhere (i.e. php.internals) and focus on this patch? We currently have two discussions here both unrelated to the patch itself. Thanks. 👍
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.
@Majkl578 You misunderstand. The const discussion is about the patch.
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.
By convention, zvals are never passed by const pointer.
This really sounds like a global issue for all zvals, not just this patch. Trying to mitigate this convention is out of scope 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.
This is my opinion.: it appears instanceof_function
takes its parameters by pointer to const and Z_TYPE_P
should be const correct as well. This function should take its parameter by pointer to const because we have verified here, today, that it is const correct. It is easier to do this now than later. No other functions need to be adjusted for this function to work with const parameters. As such I believe this is in scope for this PR. If other functions would have to be adjust for it to work then it would be out of scope.
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.
@morrisonlevi To avoid this discution on to be const or not to be, should I move this switch
to the function declaration, in type.c
like #2206?
Also, may I ask you something? What are the difference between have the function here in Zend_api.c
or there in type.c
?
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.
Having it in the API means it can be called from other places in the C code. I think it belongs there - I would not move it inline like the other PR.
ext/standard/basic_functions.c
Outdated
@@ -2586,6 +2586,10 @@ ZEND_END_ARG_INFO() | |||
ZEND_BEGIN_ARG_INFO_EX(arginfo_is_iterable, 0, 0, 1) | |||
ZEND_ARG_INFO(0, var) | |||
ZEND_END_ARG_INFO() | |||
|
|||
ZEND_BEGIN_ARG_INFO_EX(arginfo_is_countable, 0, 0, 1) |
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 should have return type information of bool
as well.
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.
It may also return null on zpp failure, at which point the typehint is more harmful than useful until such a time as we make all zpp failures throwing.
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.
Can we please make a push to fix these type-safety related issues in PHP 8? Internal and external function behaviors need to be brought in-line with each other. If we can't even ensure that callable (mixed): bool
will actually return bool
if it happens to be an internal function then we have can't reasonably think about type safety at all.
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 it should be ZEND_BEGIN_ARG_INFO(arginfo_is_countable, 0)
?
I tried to research what are the differences between ZEND_BEGIN_ARG_INFO
and ZEND_BEGIN_ARG_EX
but I didn't found 😞
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.
Agreed with @morrisonlevi here, we should eliminate these inconsistency in the near future.
Internal classes can also be supported by implementing the |
@duncan3dc I’ve searched in both open and close PRs, and, somehow, I didn’t found that one. I’ll include the PS: after that, our code I’ll look almost the same. Should I credit the RFC to you? 😄 |
@carusogabriel No need for that, it's all your own work 👍 |
@duncan3dc Applied @morrisonlevi 44118dc Is this the correct way to inform that this returns |
This function would not be necessary, if warning "trying to count uncountable" since 7.2 was adopted under strict type mode. It brings sometime lots of additional coding when mixing strict behaviours into default weak mode. Actually, in a weak mode there's nothing wrong with |
@lubosdz Thanks for your feedback. Some questions and points:
Do you have this information precisely? AFAIK, the Warning was add in general, as I've seen projects that are even fully optimized for PHP 7 doing this changes 😅
This is actually something that should be discussed with @duncan3dc in #2185, right? |
Zend/zend_vm_def.h
Outdated
@@ -8133,7 +8133,7 @@ ZEND_VM_HANDLER(190, ZEND_COUNT, CONST|TMP|VAR|CV, UNUSED) | |||
} | |||
|
|||
/* if not and the object implements Countable we call its count() method */ | |||
if (instanceof_function(Z_OBJCE_P(op1), zend_ce_countable)) { | |||
if (zend_is_countable(op1)) { |
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 did you modified this line to use your function instead of keep as original? It seems that will do more checks than original one (switch
, IS_ARRAY
, IS_OBJECT
and just on last step instanceof_function
). I don't know if compiler could optimizes it.
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.
@rentalhost I get the same question at #3146 when trying to use zend_is_iterable
. Isn't a good practice to DRY our code with functions? Or this is just for PHP, in C we need to focus on performance? 😬
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 trouble is the code below (line 8139) expects the object to have a count()
method, but zend_is_countable()
will return true if an extension class defines a count_elements
handler.
As this code is specifically calling the count()
method, I think it should check zend_ce_countable
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.
@duncan3dc So, should I revert this changes, or revert the logic and give zend_ce_countable
priority in the switch
statement is also valid?
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.
@duncan3dc That point is a bit moot, since the checks performed in the code above will already have eliminated that possibility.
I do agree that this change should be reverted, though. Trying to reuse the logic in zend_is_countable
isn't overly useful, since we've already duplicated similar checks in the code above.
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.
Reverting this changes as performance was affected :(
case IS_ARRAY: | ||
return 1; | ||
case IS_OBJECT: | ||
if (Z_OBJ_HT_P(countable)->count_elements) { |
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.
In which case it will be 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.
@rentalhost it will be true for extension defined objects that implements their own countable technique
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.
@rentalhost Like @KalleZ said. Also, see @duncan3dc's comment and #2206.
Ref: http://www.phpinternalsbook.com/classes_objects/object_handlers.html#c.count_elements
This should be ready to go, as the RFC was approved 😃 |
Merged as d0ee2a8, thanks! |
This PR was squashed before being merged into the 1.7-dev branch (closes #115). Discussion ---------- Add is_countable under PHP 7.3 As requested by @nicolas-grekas :) Waiting for php/php-src#3026 Commits ------- 803a622 Add is_countable under PHP 7.3
In #2185 a
Warning
was added while trying to count non-countables:and we start to see everyone changing their codes, usually adding the following condition:
Inspired by that, this PR adds
is_countable()
function that returns a boolean:true
, if a value is anarray
or an instance ofCountable
interfacefalse
for other valuesRFC: https://wiki.php.net/rfc/is-countable