Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,26 @@ class TypedParams
function run4($optional = 1, A&B $required)
{
}

function run5($optional = 1, array $required)
{
}

function run6($optional = 1, true $required)
{
}

function run7($optional = 1, false $required)
{
}

function run8($optional = 1, null $required)
{
}

function run9($optional = 1, mixed $required)
{
}
}

?>
Expand All @@ -29,11 +49,11 @@ namespace Rector\Tests\CodeQuality\Rector\ClassMethod\OptionalParametersAfterReq

class TypedParams
{
function run1($optional = 1, ?int $required = null)
function run1($optional = 1, int $required = 0)
{
}

function run2($optional = 1, string|int|null $required = null)
function run2($optional = 1, string|int $required = '')
{
}

Expand All @@ -44,6 +64,26 @@ class TypedParams
function run4($optional = 1, (A&B)|null $required = null)
{
}

function run5($optional = 1, array $required = [])
{
}

function run6($optional = 1, true $required = true)
{
}

function run7($optional = 1, false $required = false)
{
}

function run8($optional = 1, null $required = null)
{
}

function run9($optional = 1, mixed $required = null)
{
}
}

?>
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,13 @@
use PhpParser\Node\Name;
use PhpParser\Node\NullableType;
use PhpParser\Node\Param;
use PhpParser\Node\Scalar\Float_;
use PhpParser\Node\Scalar\Int_;
use PhpParser\Node\Scalar\String_;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\Stmt\Function_;
use PhpParser\Node\UnionType;
use Rector\PhpParser\Node\Value\ValueResolver;
use Rector\Rector\AbstractRector;
use Rector\ValueObject\PhpVersionFeature;
use Rector\VersionBonding\Contract\MinPhpVersionInterface;
Expand All @@ -28,14 +32,19 @@
*/
final class OptionalParametersAfterRequiredRector extends AbstractRector implements MinPhpVersionInterface
{
public function __construct(
private readonly ValueResolver $valueResolver,
) {
}

public function getRuleDefinition(): RuleDefinition
{
return new RuleDefinition('Add null default value when a required parameter follows an optional one', [
return new RuleDefinition('Add reasonable default value when a required parameter follows an optional one', [
new CodeSample(
<<<'CODE_SAMPLE'
class SomeObject
{
public function run($optional = 1, $required)
public function run($optional = 1, int $required)
{
}
}
Expand All @@ -45,7 +54,7 @@ public function run($optional = 1, $required)
<<<'CODE_SAMPLE'
class SomeObject
{
public function run($optional = 1, $required = null)
public function run($optional = 1, int $required = 0)
{
}
}
Expand Down Expand Up @@ -84,47 +93,109 @@ public function refactor(Node $node): ClassMethod|Function_|Closure|null
$previousParam = $node->params[$key - 1] ?? null;
if ($previousParam instanceof Param && $previousParam->default instanceof Expr) {
$hasChanged = true;
$this->processParam($param);
}
}

$param->default = new ConstFetch(new Name('null'));
return $hasChanged ? $node : null;
}

if (! $param->type instanceof Node) {
continue;
}
public function provideMinPhpVersion(): int
{
return PhpVersionFeature::NULLABLE_TYPE;
}

if ($param->type instanceof NullableType) {
continue;
}
/**
* Look first found type reasonable value
*
* @param Node[] $types
*/
private function mapReasonableParamValue(array $types): Expr
{
foreach ($types as $type) {
if ($this->isName($type, 'string')) {
return new String_('');
}

if ($param->type instanceof UnionType) {
foreach ($param->type->types as $unionedType) {
if ($unionedType instanceof Identifier && $this->isName($unionedType, 'null')) {
continue 2;
}
}
if ($this->isName($type, 'int')) {
return new Int_(0);
}

$param->type->types[] = new Identifier('null');
continue;
}
if ($this->isName($type, 'float')) {
return new Float_(0.0);
}

if ($param->type instanceof IntersectionType) {
$param->type = new UnionType([$param->type, new Identifier('null')]);
if ($this->isName($type, 'bool')) {
return $this->nodeFactory->createFalse();
}

continue;
}
if ($this->isName($type, 'array')) {
return $this->nodeFactory->createArray([]);
}

if ($param->type instanceof ComplexType) {
continue;
}
if ($this->isName($type, 'true')) {
return $this->nodeFactory->createTrue();
}

$param->type = new NullableType($param->type);
if ($this->isName($type, 'false')) {
return $this->nodeFactory->createFalse();
}
}

return $hasChanged ? $node : null;
return new ConstFetch(new Name('null'));
}

public function provideMinPhpVersion(): int
private function processParam(Param $param): void
{
return PhpVersionFeature::NULLABLE_TYPE;
if (! $param->type instanceof Node) {
$param->default = new ConstFetch(new Name('null'));
return;
}

if ($param->type instanceof NullableType) {
$param->default = new ConstFetch(new Name('null'));
return;
}

if ($param->type instanceof IntersectionType) {
$param->default = new ConstFetch(new Name('null'));
$param->type = new UnionType([$param->type, new Identifier('null')]);
return;
}

if ($param->type instanceof UnionType) {
foreach ($param->type->types as $unionedType) {
if ($unionedType instanceof Identifier && $this->isName($unionedType, 'null')) {
$param->default = new ConstFetch(new Name('null'));
return;
}
}

$reasonableValue = $this->mapReasonableParamValue($param->type->types);
if ($this->valueResolver->isNull($reasonableValue)) {
$param->default = new ConstFetch(new Name('null'));
$param->type->types[] = new Identifier('null');
return;
}

$param->default = $reasonableValue;
return;
}

if ($param->type instanceof ComplexType) {
return;
}

$reasonableValue = $this->mapReasonableParamValue([$param->type]);
if ($this->valueResolver->isNull($reasonableValue)) {
if (! $param->type instanceof Identifier || ! $this->isNames($param->type, ['null', 'mixed'])) {
$param->type = new NullableType($param->type);
}

$param->default = new ConstFetch(new Name('null'));
return;
}

$param->default = $reasonableValue;
}
}
Loading