Skip to content

Conversation

@calebdw
Copy link
Contributor

@calebdw calebdw commented Sep 11, 2025

Hello!

The ArraySpreadInsteadOfArrayMergeRector was unnecessarily skipping valid transformations when it couldn't determine that parameters were explicitly typed as ArrayType or ConstantArrayType. This prevented the rector from transforming many valid array_merge() calls to the more modern spread operator syntax:

class FrameworkParent
{
    /**
     * Get the map of resource methods to ability names.
     *
     * @return array
     */
    protected function resourceAbilityMap()
    {
        return [
            'index' => 'viewAny',
            'show' => 'view',
            'create' => 'create',
            'store' => 'create',
            'edit' => 'update',
            'update' => 'update',
            'destroy' => 'delete',
        ];
    }
}

class Child extends FrameworkParent
{
    #[Override]
    protected function resourceAbilityMap(): array
    {
        return array_merge(parent::resourceAbilityMap(), [
            'cancel'   => 'update',
            'complete' => 'update',
            'hold'     => 'update',
            'ready'    => 'update',
            'start'    => 'update',
        ]);
    }
}

This PR removes overly strict type requirement that prevented these valid transformations---array_merge() itself enforces array types at runtime, it has a strict signature and throws a TypeError if non-arrays are passed.

This is safe for the following reasons:

  • Runtime equivalence: If code currently works with array_merge($var1, $var2), then $var1 and $var2 are guaranteed to be arrays at runtime. Converting to [...$var1, ...$var2] maintains the exact same runtime behavior and type requirements.
  • Same error behavior: Both array_merge($nonArray) and [...$nonArray] throw errors when given non-arrays, so the transformation doesn't change error handling (https://3v4l.org/2YVCi)
  • Advantages: The spread operator has several advantages over array_merge(), there's really no reason to not use it
  • PHP < 8.1 compatibility preserved: The rector's existing PHP version checks remain unchanged. For PHP versions below 8.1, the transformation is still correctly skipped for arrays with string keys or when the type is not known

Thanks!

@calebdw calebdw force-pushed the calebdw/push-zpzpvwkmrkrp branch from 3ab7e36 to 628d85d Compare September 11, 2025 21:36
Copy link
Member

@samsonasik samsonasik left a comment

Choose a reason for hiding this comment

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

since it optional rule and not part of set, I am fine with this 👍

@samsonasik samsonasik merged commit 15eb8c2 into rectorphp:main Sep 12, 2025
49 checks passed
@samsonasik
Copy link
Member

Thank you @calebdw

@calebdw calebdw deleted the calebdw/push-zpzpvwkmrkrp branch September 12, 2025 01:51
@staabm
Copy link
Contributor

staabm commented Sep 12, 2025

it has a strict signature and throws a TypeError if non-arrays are passed.

This is only true for php8+

https://3v4l.org/oSJcb#veol

@samsonasik
Copy link
Member

samsonasik commented Sep 12, 2025

@staabm oops, yes, working with non array at https://3v4l.org/OWtO5#v7.4.33
I think php version < 8.0 is needed with is array check.

I will create PR for it.

@samsonasik
Copy link
Member

@staabm it seems there is already test for php 7.4, see

class SkipSimpleArrayMerge
{
public function run($iter1, $iter2)
{
$values = array_merge($iter1, $iter2);
}
}

so, the issue is probably it should skip @param array $iter which it rely on docblock on php 7.4, should be skipped. I will look into it.

@staabm
Copy link
Contributor

staabm commented Sep 12, 2025

I am not sure whether there is a bug. Just wanted to point out that one of the assumptions does not work for php7

@samsonasik
Copy link
Member

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants