Renaming Parameters in a method
Renaming parameters in a method used to be an innocuous operation: one could do it without impact. Since PHP 8.0, such renaming may break existing calls. With named parameters, not only the method name, but also its parameters are part of the call.
And it is a common challenge to update parameter names, especially in large or old codebases. The problem arose from changing parameter positions, but nowadays, it is also linked to parameter names. And with a method being called many times, it is increasingly difficult to refactor it without impact all across the code base, and a perceived risk.
Ideally, the goal of renaming a parameter is to ensure backward compatibility while encouraging migration to the new parameter name. Reasons to rename a parameter are beyond the scope of this article.
Here are several strategies to refactor a method’s parameter name, without breaking code compatibility:
Parameter Aliasing, And Default Values
The strategy here is to add the new parameter with a default value, and use logic to check if the new parameter is set. If so, use its value; otherwise, use the old parameter.
<?php
class X {
public function exampleMethod(int $oldParam = 3, ?int $newParam = null) {
$value = $newParam ?? $oldParam;
}
}
?>
This approach allows both parameters to coexist, and gives users a sunset period.
sunset period, precedence
One may also decide the precedence: has the old parameter priority, or the new? In fact, over the sunset period, it may actually change, until the old parameter is ultimately removed.
choosing the default value
The default value choice is a bit of a challenge. Here, it is set to null, which is outside the domain of the actual parameter. The detection is then obvious and non-conflictual. The drawback is to expand the type of the new parameter, which may become difficult to reduce later.
Another option would be to use a special value in the int domain, such as -1, or else, but it means such value cannot be used for anything else.
Cluttering the signature
On the other hand, this approach clutters the signature with twice the same parameter, and may make it harder to learn usage. In particular, anyone learning by empirically testing code may loose a lot of time figuring out the situation correctly. Most probably, the result of such experiment will hinder modernisation efforts.
Consider also as any AI training will keep emphasizing the old parameter for a long time, as per existing literature.
Consider a warning
This may be completed with a warning, that helps alert users about the evolution.
<?php
class X {
public function exampleMethod(int $oldParam = 3, ?int $newParam = null) {
if ($newParam === null) {
// non blocking warning
trigger_error("The parameter 'oldParam' is deprecated. Use 'newParam' instead.", E_USER_DEPRECATED);
}
$value = $newParam ?? $oldParam;
}
}
?>
Adapter Pattern
A step above renaming the parameter is renaming the whole method. For this, create an adapter or wrapper method that accepts the old parameter and calls the new method with the new parameter. “`
<?php
class X {
public function exampleMethod($oldParam) {
return $this->exampleMethodNew($oldParam);
}
public function exampleMethodNew($newParam) {
// Use $newParam
// The actual code is now here
}
}
?>
“`
sunset period
This way, the old version of the method is still available.
It is recommended to have the old method call the new one, so that the logic is not duplicated in the code. It also creates a natural penalty to using the old method, even if very small.
more clarity
The new parameter appears in a distinct method, and not as a refactor. The relay function introduce a duplicate method for the same feature, which may be confusing for users. It may also be time to rename that method anyway.
It is also possible to add a warning, like in the previous solution.
Middleware or Proxy Pattern
After renaming the method, comes renaming the whole class. For that, use a middleware or proxy to intercept calls and map old parameter names to new ones.
This moves the refactor from the current class to another one. The correct object may be injected where needed, until the refactor happens.
<?php
class XProxy {
public function exampleMethod($oldParam = null) {
$realObject = new X();
return $realObject->exampleMethod($oldParam);
}
}
class X {
public function exampleMethod($newParam) {
// Use $newParam
}
}
?>
The proxy pattern decouples the old and new logic in a cleaner way, with an explicit intent for refactoring the code. It is also easy to remove the proxy later, when everyone has moved to the new signature.
It also adds some complexity to the code. Phasing out the proxy class is important, and will reduce complexity later.
Documentation and Communication
Finally, the last solution is the breaking change. Go forward and rename the parameter with the new name. No sunseting, just a breaking change. This means doing such a change in a major version of the application.
easing the transition
Update the documentation, mention the evolution in roadmap presentations, changelogs, and release notes to inform users about the change. They won’t be able to migrate until they have their refactor in place, but they might also be aware of the situation, and possible consequences. This gives users time to plan.
sunset period based on version
Here, the sunset period is managed by versioning. Users have to stay on the previous version until they are meeting the new compatibility requirements, and then, they can change. This is valid for a platform with release version, like a released framework or open source tools. It is harder to do when the versions are less obvious, as in a company platform upgrade.
Automated updates
To speed up the process, it is useful to mention automated solutions to handle the renaming:
- Use the IDE
rename parameterfeatures - Use rector to set up a search and replace situation
- Use AI to find and update all calls to the method with the correct new parameter name, when applicable
As usual, renaming things works well with static names. It get harder when the code uses dynamic parameter naming, or method.
<?php
function foo(int $a): int { return $a + 2; }
// static call to foo
foo(a: 3);
// dynamic arguments with foo()
$args = ['a' => 4];
foo(...$args);
// dynamic call to foo()
$function = 'foo';
$function(a: 5);
?>
Just do it
This is the rip the architectural band-aid off approach. It’s simple for you, but your users might not appreciate the surprise errors on Monday morning. In particular, when little knowledge is shared before the even, it is a sign that the refactor was not organized.
The consequences may be a barrage of complaints from users, or, more silently, a delay in the upgrade to the newer versions, or simple quit from using the platform. Consequences don’t always make noise.
Summary Table
| Strategy | Backward Compatible | Encourages Migration | Complexity |
|---|---|---|---|
| Parameter Aliasing | ✅ Yes | ❌ No | Low |
| Adapter Pattern | ✅ Yes | ✅ Yes | Medium |
| Middleware/Proxy | ✅ Yes | ✅ Yes | High |
| Documentation | ❌ Yes | ✅ Yes | Low |
Renaming parameters
Renaming parameters is harder than it looks. Since PHP 8.0, the internal variable names are no longer a secret; they are part of the public API. Named parameters do add an extra layer of attachement, although positional parameters had the same problems with…well… positions.
The situation grows with usage: the more often a method is called, the riskier it becomes to change anything to it: name, type, position, default value, etc. This might be a good starting point to decide what strategy to use. The other one is how much of a sunset period is needed.
It boils down to names, which are a permanent commitment: once a name is set, it is difficult to change it later. This applies to natural language itself, and it is often smoother to promote a new word and leave the previous one adrift, rather than force the replacement.

