{"record":{"id":"e5083125373eb3af","repo":"doctrine/inflector","slug":"preg-replace-returned-null-for-value-s","errorCode":null,"errorMessage":"preg_replace returned null for value \"%s\"","messagePattern":"preg_replace returned null for value \"(.+?)\"","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"src/Inflector.php","lineNumber":237,"sourceCode":"\n    /** @var WordInflector */\n    private $pluralizer;\n\n    public function __construct(WordInflector $singularizer, WordInflector $pluralizer)\n    {\n        $this->singularizer = $singularizer;\n        $this->pluralizer   = $pluralizer;\n    }\n\n    /**\n     * Converts a word into the format for a Doctrine table name. Converts 'ModelName' to 'model_name'.\n     */\n    public function tableize(string $word): string\n    {\n        $tableized = preg_replace('~(?<=\\\\w)([A-Z])~u', '_$1', $word);\n\n        if ($tableized === null) {\n            throw new RuntimeException(sprintf(\n                'preg_replace returned null for value \"%s\"',\n                $word\n            ));\n        }\n\n        return mb_strtolower($tableized);\n    }\n\n    /**\n     * Converts a word into the format for a Doctrine class name. Converts 'table_name' to 'TableName'.\n     */\n    public function classify(string $word): string\n    {\n        return str_replace([' ', '_', '-'], '', ucwords($word, ' _-'));\n    }\n\n    /**\n     * Camelizes a word. This uses the classify() method and turns the first character to lowercase.","sourceCodeStart":219,"sourceCodeEnd":255,"githubUrl":"https://github.com/doctrine/inflector/blob/288d99b85cac099b2db56b52a9bf62652ff66f61/src/Inflector.php#L219-L255","documentation":"tableize() converts CamelCase to snake_case via preg_replace with the 'u' (UTF-8) modifier on the pattern '~(?<=\\w)([A-Z])~u' (src/Inflector.php:234). PCRE refuses to process a subject that is not valid UTF-8, makes preg_replace return null, and the library throws RuntimeException instead of returning a broken table name. So this error almost always means the string you passed contains byte sequences that are not valid UTF-8 — legacy single-byte encodings, binary data, or a multibyte character cut in half.","triggerScenarios":"Calling $inflector->tableize($word) where $word is not valid UTF-8: text read from a latin1/Windows-1252 database column or CSV/Excel file, scraped filenames or external payloads, or a UTF-8 string truncated mid-character by substr() instead of mb_substr(). The 'u' modifier makes PCRE raise PREG_BAD_UTF8_ERROR, preg_replace returns null, and the null check at src/Inflector.php:236 throws.","commonSituations":"MySQL connection not set to utf8mb4 so retrieved text arrives as latin1; imports of Windows-1252 files; user-uploaded filenames passed through tableize(); strings sliced with substr() on multibyte content; data pipelines where an upstream system encodes to ISO-8859-1. Often appears after a data migration or a change of data source, and surfaces deep inside ORM/console tooling that tableizes entity or class names.","solutions":["Find where the non-UTF-8 bytes enter (the exception message includes the offending value) and fix the source: database charset, file encoding, or upstream API output.","Sanitize at the boundary before calling tableize(): if (!mb_check_encoding($word, 'UTF-8')) { $word = mb_convert_encoding($word, 'UTF-8', 'Windows-1252'); } using the real source encoding.","Replace substr() with mb_substr() (or mb_strcut()) anywhere multibyte strings can be truncated.","For MySQL, connect with utf8mb4 (charset in the DSN or SET NAMES utf8mb4) so reads are valid UTF-8.","If the cause is unclear, reproduce with the same value and call preg_last_error_msg() right after the failing preg_* call to confirm PREG_BAD_UTF8_ERROR."],"exampleFix":"// before\n$table = $inflector->tableize($rawName); // $rawName is Windows-1252 or truncated UTF-8 -> RuntimeException\n\n// after\nif (! mb_check_encoding($rawName, 'UTF-8')) {\n    $rawName = mb_convert_encoding($rawName, 'UTF-8', 'Windows-1252'); // adjust to the actual source encoding\n}\n$table = $inflector->tableize($rawName);","handlingStrategy":"validation","validationCode":"use Doctrine\\Inflector\\Inflector;\n\nif (! mb_check_encoding($word, 'UTF-8')) {\n    $word = mb_convert_encoding($word, 'UTF-8', 'Windows-1252'); // use the actual source encoding\n}\n\n$table = $inflector->tableize($word);","typeGuard":"function isValidUtf8(string $value): bool\n{\n    return mb_check_encoding($value, 'UTF-8');\n}","tryCatchPattern":"use RuntimeException;\n\ntry {\n    $table = $inflector->tableize($word);\n} catch (RuntimeException $e) {\n    if (str_contains($e->getMessage(), 'preg_replace returned null')) {\n        // input was not valid UTF-8: re-encode once and retry, or reject at this boundary\n        $table = $inflector->tableize(mb_convert_encoding($word, 'UTF-8', 'Windows-1252'));\n    } else {\n        throw $e;\n    }\n}","preventionTips":["Validate encoding at system boundaries (HTTP input, DB reads, file imports) with mb_check_encoding().","Use mb_substr()/mb_strcut() instead of substr() on any string that may contain multibyte characters.","Configure MySQL connections as utf8mb4 and keep source files as UTF-8.","Never pass binary data or raw uploads directly to tableize()."],"tags":["php","inflector","utf-8","encoding","preg-replace"],"backgroundTag":"preg-replace-returns-null","analyzedSha":"288d99b85cac099b2db56b52a9bf62652ff66f61","analyzedAt":"2026-08-21T02:11:51.985Z","schemaVersion":2},"datasetVersion":"2026-08-21T03:17:12.404Z"}