DEV Community

Jennifer Konikowski
Jennifer Konikowski

Posted on

3 1

Autofixing Variable Case in PHP

I’m currently working on a PHP project that had a mix of camelCase and snake_case for variables and methods. We had recently started using PHP-CS-Fixer on the project and, unfortunately, there was not an existing rule to make this kind of change. So I decided to write a variable case fixer myself! Now: this is inherently risky. Instance variables in PHP are called using $this->variableName, which is really similar to how you call a function. Those could also be defined above the constructor like private $variableName and that would be fixed with a fixer like this, but any call site would not. So there’s some of the risk 😅. There are also predefined variables in PHP that we would not want to update. Ok, let’s get started!

Since I was using an existing project, I did not have to worry about getting each file and was able to trust PHP-CS-Fixer to parse each file and get me the tokens. The hardest part of this was actually figuring out how to pick out the tokens. So all this does is check to see if the token ( smallest block of code) is either of the two variable types and not in the list of predefined variables.

foreach ($tokens as $index => $token) {
  if ((T_VARIABLE === $token->getId()) || (T_STRING_VARNAME === $token->getId())) {
    if (in_array($token->getContent(), $predefinedVariables)) {
      continue;
    }
    $tokens[$index] = new Token([$token->getId(), $this->updateVariableCasing($token->getContent())]);
  }
}
Enter fullscreen mode Exit fullscreen mode

That is actually the bulk of it! updateVariableCasing just takes the configuration and then calls whatever function we need (ie camelCase() if configuration['case'] is equal to camel_case. The functions to change case were found somewhere on StackOverflow. Overall, this works very well! The only places we ran into problems were where private variables were defined at the top, converted there, but then not converted when called in the code (as $this->variable_name). Something to keep in mind if you decide to implement something like this in your project.

I did put up (a PR to add this rule to PHP-CS-Fixer)[https://github.com/FriendsOfPHP/PHP-CS-Fixer/pull/5097], but I think they were reluctant to add it since it’s not safe and I don’t have a ton of extra time to keep trying to get it in.

protected function applyFix(\SplFileInfo $file, Tokens $tokens)
{
$predefinedVariables = [
'$GLOBALS',
'$_SERVER',
'$_GET',
'$_POST',
'$_FILES',
'$_REQUEST',
'$_SESSION',
'$_ENV',
'$_COOKIE',
'$php_errormsg',
'$HTTP_RAW_POST_DATA',
'$http_response_header',
'$argc',
'$argv'
];
foreach ($tokens as $index => $token) {
if ((T_VARIABLE === $token->getId()) || (T_STRING_VARNAME === $token->getId())) {
if (in_array($token->getContent(), $predefinedVariables)) {
continue;
}
$tokens[$index] = new Token([$token->getId(), $this->updateVariableCasing($token->getContent())]);
}
}
}
private function updateVariableCasing($variableName)
{
if (self::CAMEL_CASE === $this->configuration['case']) {
return $this->camelCase($variableName);
}
return $this->snakeCase($variableName);
}
private function camelCase($string)
{
if(strtoupper($string) == $string){
return $string;
}
$string = Preg::replace('/_/i', ' ', $string);
$string = trim($string);
// uppercase the first character of each word
$string = ucwords($string);
$string = str_replace(' ', '', $string);
return lcfirst($string);
}
private function snakeCase($string, $separator = '_')
{
if(strtoupper($string) == $string){
return $string;
}
if(strtolower($string) == $string){
return $string;
}
// insert separator between any letter and the beginning of a numeric chain
$string = Preg::replace('/([a-z]+)([0-9]+)/i', '$1'.$separator.'$2', $string);
// insert separator between any lower-to-upper-case letter chain
$string = Preg::replace('/([a-z]+)([A-Z]+)/', '$1'.$separator.'$2', $string);
// insert separator between the end of a numeric chain and the beginning of an alpha chain
$string = Preg::replace('/([0-9]+)([a-z]+)/i', '$1'.$separator.'$2', $string);
// Lowercase
return strtolower($string);
}

Heroku

This site is built on Heroku

Join the ranks of developers at Salesforce, Airbase, DEV, and more who deploy their mission critical applications on Heroku. Sign up today and launch your first app!

Get Started

Top comments (0)

AWS Security LIVE!

Join us for AWS Security LIVE!

Discover the future of cloud security. Tune in live for trends, tips, and solutions from AWS and AWS Partners.

Learn More

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay