Skip to content

[13.x] Fix Str::unwrap() to only strip when both sides match#59741

Closed
sumaiazaman wants to merge 2 commits intolaravel:13.xfrom
sumaiazaman:fix/str-unwrap-partial-match
Closed

[13.x] Fix Str::unwrap() to only strip when both sides match#59741
sumaiazaman wants to merge 2 commits intolaravel:13.xfrom
sumaiazaman:fix/str-unwrap-partial-match

Conversation

@sumaiazaman
Copy link
Copy Markdown
Contributor

Summary

Str::unwrap() currently strips the $before and $after strings independently. This means it mutates strings where only one side matches, which is unexpected — unwrap should be the strict inverse of wrap.

Context

Input Before After
'"value"' unwrap '"' 'value' 'value'
'"value' unwrap '"' (only start matches) 'value' (partial strip) '"value' (unchanged)
'value"' unwrap '"' (only end matches) 'value' (partial strip) 'value"' (unchanged)
'foo-bar-baz' unwrap 'foo-', '-baz' 'bar' 'bar'
'foo-bar' unwrap 'foo-', '-baz' (only start matches) 'bar' (partial strip) 'foo-bar' (unchanged)

Solution

Require both startsWith($before) and endsWith($after) to be true before stripping either end:

// Before
public static function unwrap($value, $before, $after = null)
{
    if (static::startsWith($value, $before)) {
        $value = static::substr($value, static::length($before));
    }

    if (static::endsWith($value, $after ??= $before)) {
        $value = static::substr($value, 0, -static::length($after));
    }

    return $value;
}

// After
public static function unwrap($value, $before, $after = null)
{
    $after ??= $before;

    if (static::startsWith($value, $before) && static::endsWith($value, $after)) {
        $value = static::substr($value, static::length($before));

        if ($after !== '') {
            $value = static::substr($value, 0, -static::length($after));
        }
    }

    return $value;
}

Safety

All existing fully-wrapped cases continue to work identically. The only behavioral change is for partially-wrapped strings, which now return the original value instead of silently stripping one end.

Changes

  • src/Illuminate/Support/Str.php — fix unwrap() to require both sides to match
  • tests/Support/SupportStrTest.php — update assertions for partial-wrap cases and add new assertions

Test Plan

  • php vendor/bin/phpunit tests/Support/SupportStrTest.php --filter testUnwrap — passes
  • php vendor/bin/phpunit tests/Support/SupportStringableTest.php --filter testUnwrap — passes

@taylorotwell
Copy link
Copy Markdown
Member

Breaking change.

@sumaiazaman
Copy link
Copy Markdown
Contributor Author

Breaking change.

Thanks for the feedback.

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.

2 participants