Skip to content
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

Closed
wants to merge 5 commits into from

Conversation

timhovius
Copy link

@timhovius timhovius commented Jun 20, 2016

Q A
Branch? 2.7, 2.8, 3.0 or 3.1
Bug fix? yes
New feature? no
BC breaks? no
Deprecations? no
Tests pass? yes
Fixed tickets -
License MIT
Doc PR -

The string CamelCaseString was converted to _camel_case_string. The first character must be ignored.

@timhovius timhovius changed the title Ignore first uppercase character [Serializer] Ignore first uppercase character Jun 20, 2016
@@ -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) {
Copy link
Contributor

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 👍

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will do 👍

@sstok
Copy link
Contributor

sstok commented Jun 20, 2016

Please add a test to proof that's its fixed.

Status: needs work

@timhovius
Copy link
Author

timhovius commented Jun 20, 2016

Added a unit test dataProvider value 👍

@fabpot
Copy link
Member

fabpot commented Jun 20, 2016

should be merged in 2.7.

@timhovius
Copy link
Author

timhovius commented Jun 20, 2016

Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'firstCharacterNoUnderscore'
+'FirstCharacterNoUnderscore'

Should I write an another dataProvider, or lowercase the string first?

@timhovius
Copy link
Author

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) {
Copy link
Member

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

Copy link
Contributor

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.

Copy link
Member

@dunglas dunglas Jun 21, 2016

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?

Copy link
Contributor

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().

Copy link
Contributor

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;
}

Copy link
Member

@dunglas dunglas Jun 21, 2016

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;
}

Copy link
Contributor

@GuilhemN GuilhemN Jun 21, 2016

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

Copy link
Contributor

@GuilhemN GuilhemN Jun 21, 2016

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

@dunglas
Copy link
Member

dunglas commented Jul 3, 2016

@timhovius Do you have some time to finish this PR?

@timhovius
Copy link
Author

timhovius commented Jul 3, 2016

@dunglas Yes of course. I will choose one of the solutions above.

@fabpot
Copy link
Member

fabpot commented Jul 28, 2016

@timhovius What's the status of this PR?

@timhovius
Copy link
Author

@fabpot I'm working on it

@timhovius
Copy link
Author

timhovius commented Jul 29, 2016

It's fixed now, I implemented the solution of @dunglas. The solution of @Ener-Getick was failing.

@dunglas
Copy link
Member

dunglas commented Jul 29, 2016

The fix looks good to me. But can you take into account @Ener-Getick's comment for the test? Thanks.

@timhovius
Copy link
Author

Wat do you Mean @dunglas?

@DavidBadura
Copy link
Contributor

@timhovius this: #19112 (comment) ;)

@dunglas
Copy link
Member

dunglas commented Aug 16, 2016

@timhovius any update? Do you want me to finish this PR?

@timhovius
Copy link
Author

I fixed the unit tests

@stof
Copy link
Member

stof commented Aug 23, 2016

your new tests don't seem to be covering this issue. They look like they cover exactly the same cases than existing cases

@timhovius
Copy link
Author

Yes but the new tests are failing. Try it.

@fabpot
Copy link
Member

fabpot commented Sep 14, 2016

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'),
Copy link
Contributor

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'),

@fabpot
Copy link
Member

fabpot commented Sep 26, 2016

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.

@fabpot fabpot closed this Sep 26, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet