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
[Serializer] Ignore first uppercase character #19112
Conversation
@@ -48,7 +48,7 @@ public function normalize($propertyName) | |||
|
|||
$len = strlen($propertyName); | |||
for ($i = 0; $i < $len; ++$i) { | |||
if (ctype_upper($propertyName[$i])) { | |||
if (ctype_upper($propertyName[$i]) && $i != 0) { |
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.
Please use the strict comparison 👍
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 will do 👍
Please add a test to proof that's its fixed. Status: needs work |
Added a unit test dataProvider value 👍 |
should be merged in 2.7. |
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'firstCharacterNoUnderscore'
+'FirstCharacterNoUnderscore' Should I write an another dataProvider, or lowercase the string first? |
It's fixed now 😃 |
@@ -48,7 +48,7 @@ public function normalize($propertyName) | |||
|
|||
$len = strlen($propertyName); | |||
for ($i = 0; $i < $len; ++$i) { | |||
if (ctype_upper($propertyName[$i])) { | |||
if (ctype_upper($propertyName[$i]) && $i !== 0) { |
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.
we use Yoda conditions in the Symfony code base: 0 !== $i
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 could be simplified with something like this, saving one call to ctype_upper()
:
if (0 !== $i && ($lcPropertyName = strtolower($propertyName[$i])) !== $propertyName[$i]) {
$snakeCasedName .= '_'.$lcPropertyName;
}
BTW, if you don't agree with this approach, please move the cheaper check left.
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.
What's the benefit of replacing a call to ctype_upper
by a call to strtolower
?
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.
Now, each iteration calls ctype_upper()
and strtolower()
; with my proposal we can omit the call to ctype_upper()
.
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 complete if/else
structure I'm proposing:
if (($lcPropertyName = strtolower($propertyName[$i])) !== $propertyName[$i] && 0 !== $i) {
$snakeCasedName .= '_'.$lcPropertyName;
} else {
$snakeCasedName .= $lcPropertyName;
}
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.
Ok, got it. Your improvement looks good!
The code can be more readable:
$lcPropertyName = strtolower($propertyName[$i]);
if (0 !== $i && $lcPropertyName !== $propertyName[$i]) {
$snakeCasedName .= '_'.$lcPropertyName;
} else {
$snakeCasedName .= $lcPropertyName;
}
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.
@phansys @dunglas something even more efficient and clearer imo could be:
$len = strlen($propertyName) - 1;
$lowerCasedName = strtolower($propertyName);
// First character taken as is
$snakeCasedName = isset($lowerCasedName[0]) ? $lowerCasedName[0] : '';
for ($i = 1; $i < $len; ++$i) {
// If uppercase
if ($lowerCasedName[0] !== $propertyName[$i]) {
$snakeCasedName .= '_';
}
$snakeCasedName .= $lowerCasedName[$i];
}
Of course the easiest way to decide which structure to use would be to run a benchmark against each implementation.
Edit: missed your comment @dunglas
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 ran a small php benchmarks with 100 000 occurrences and different names:
with Foo
:
test_original : 0.099 sec.
test_energetick : 0.047 sec.
test_dunglas : 0.077 sec.
with aLongNameIsVeryLong
:
test_original : 0.558 sec.
test_energetick : 0.364 sec.
test_dunglas : 0.480 sec.
The results aren't that significant so choose what you think is the more readable :-)
Edit: I used this
@timhovius Do you have some time to finish this PR? |
@dunglas Yes of course. I will choose one of the solutions above. |
@timhovius What's the status of this PR? |
@fabpot I'm working on it |
It's fixed now, I implemented the solution of @dunglas. The solution of @Ener-Getick was failing. |
The fix looks good to me. But can you take into account @Ener-Getick's comment for the test? Thanks. |
Wat do you Mean @dunglas? |
@timhovius this: #19112 (comment) ;) |
@timhovius any update? Do you want me to finish this PR? |
I fixed the unit tests |
your new tests don't seem to be covering this issue. They look like they cover exactly the same cases than existing cases |
Yes but the new tests are failing. Try it. |
I've just tested and the two new tests pass without any changes, which makes sense to me as there are very similar to the existing ones. |
@@ -48,6 +48,8 @@ public function attributeProvider() | |||
array('coop_tilleuls', 'coopTilleuls'), | |||
array('_kevin_dunglas', '_kevinDunglas'), | |||
array('this_is_a_test', 'thisIsATest'), | |||
array('first_character_no_underscore', 'firstCharacterNoUnderscore'), | |||
array('tim_hovius', 'timHovius'), |
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 should be:
array('first_character_no_underscore', 'FirstCharacterNoUnderscore'),
array('tim_hovius', 'TimHovius'),
Closing as there is no feedback and the added tests do not reveal anything to be fixed. Feel free to reopen with unit tests that demonstrate the issue. Thanks. |
The string
CamelCaseString
was converted to_camel_case_string
. The first character must be ignored.