Skip to content

Regression: str_replace()/str_ireplace() string $search + array $replace returns first element — Zend TypeError (re-#11056, ext/standard/string.c) #22827

Closed

Description

@PurHur

Category

bug · php-src-strict · stdlib string replace (pillar 4)

Problem

After array $search/$replace support landed (#11056), the inverse guard was lost: when $search is a string and $replace is an array, Zend throws TypeError (Argument #2 ($replace) must be of type string when argument #1 ($search) is a string). The VM silently uses $replace[0] and returns a string.

Probed 2026-07-24 on master workspace vs Zend 8.2.32 (php-compiler:22.04-dev).

Repro Zend 8.2.32 VM (2026-07-24)
str_replace('a', ['x','y'], 'a') TypeError 'x'
str_ireplace('A', ['x','y'], 'a') TypeError 'x'
str_replace(['a','b'], ['A','B'], 'ab') 'AB' (ok) 'AB' (ok)

php-src reference

PHP implementation target

  • ext/standard/ string replace implementation (+ JIT lowering if present) — reject is_string($search) && is_array($replace) with Zend's TypeError message before indexing $replace[0]
  • Prefer shared VM/JIT guard; no new C in runtime/

Repro

./script/docker-exec.sh -- bash -lc 'php -r '\''try { var_export(str_replace("a", ["x","y"], "a")); } catch (Throwable $e) { echo get_class($e), ":", $e->getMessage(), "\n"; }'\'''
./script/docker-exec.sh -- bash -lc 'php bin/vm.php -r '\''try { var_export(str_replace("a", ["x","y"], "a")); } catch (Throwable $e) { echo get_class($e), ":", $e->getMessage(), "\n"; }'\'''
# or write the same snippet to a temp file and run php / php bin/vm.php on it

Minimal script:

<?php
try {
    var_export(str_replace('a', ['x', 'y'], 'a'));
    echo "\n";
} catch (Throwable $e) {
    echo get_class($e), ':', $e->getMessage(), "\n";
}
try {
    var_export(str_ireplace('A', ['x', 'y'], 'a'));
    echo "\n";
} catch (Throwable $e) {
    echo get_class($e), ':', $e->getMessage(), "\n";
}

Done when

  • VM matches Zend: both calls throw TypeError with the php-src message (php-src-strict)
  • Array $search + array $replace path still works ('AB')
  • Compliance .phpt under test/compliance/cases/stdlib/ (or test/repro/ + targeted phpunit)
  • No php-compiler-strict shortcut; JIT/AOT agree when in scope

Related

Activity

  1. added
    bugSomething isn't working
    phase-4:stdlibPhase 4 – stdlib for web apps
    area:vmVirtual machine
    implementation-readySpec complete: repro, php-src ref, done-when — safe for workers to claim
    on Jul 24, 2026
  2. PurHur commented on Jul 24, 2026

    @PurHur
    OwnerAuthor

    claim: PHP Compiler STD Lib — starting this run

    Pillar 1 snapshot (pre-claim):

    • php script/bootstrap-inventory.php --check: file-list/doc stale (ArrayUnshiftRuntime warning drift) — not a hard inventory count fail
    • ./script/release-readiness.sh --json: running / will paste outcome with PR
    • make bootstrap-selfhost-link: in progress via docker-exec

    Implementing #22827: TypeError when string $search + array $replace for str_replace/str_ireplace (php-src ext/standard/string.c).

  3. PurHur commented on Jul 24, 2026

    @PurHur
    OwnerAuthor

    Merged via PR #22844.

    Fix: VM rejects string $search + array $replace with Zend's TypeError; JIT/AOT uses JitStrReplaceSearchReplaceGuard (php-src ext/standard/string.c). Array $search/$replace path unchanged (AB).

    Verification: php bin/vm.php test/repro/issue_22827_… matches Zend; ./script/phpunit.sh --filter str_replace_string_search_array_replace OK; AOT fixture abort exit 255 with Uncaught TypeError.

    Pillar 1 note: north-star5-fast still red on master (gen-0 4f-m / #8713) — unrelated to this stdlib fix.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:vmVirtual machinebugSomething isn't workingimplementation-readySpec complete: repro, php-src ref, done-when — safe for workers to claimphase-4:stdlibPhase 4 – stdlib for web appsstdlib

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions