{"record":{"id":"86192fa0cc5d5eea","repo":"paragonie/random_compat","slug":"randomcompat-substr-first-argument-should-be-a-string","errorCode":null,"errorMessage":"RandomCompat_substr(): First argument should be a string","messagePattern":"RandomCompat_substr\\(\\): First argument should be a string","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"lib/byte_safe_strings.php","lineNumber":106,"sourceCode":"    ) {\n        /**\n         * substr() implementation that isn't brittle to mbstring.func_overload\n         *\n         * This version uses mb_substr() in '8bit' mode to treat strings as raw\n         * binary rather than UTF-8, ISO-8859-1, etc\n         *\n         * @param string $binary_string\n         * @param int $start\n         * @param int|null $length (optional)\n         *\n         * @throws TypeError\n         *\n         * @return string\n         */\n        function RandomCompat_substr($binary_string, $start, $length = null)\n        {\n            if (!is_string($binary_string)) {\n                throw new TypeError(\n                    'RandomCompat_substr(): First argument should be a string'\n                );\n            }\n\n            if (!is_int($start)) {\n                throw new TypeError(\n                    'RandomCompat_substr(): Second argument should be an integer'\n                );\n            }\n\n            if ($length === null) {\n                /**\n                 * mb_substr($str, 0, NULL, '8bit') returns an empty string on\n                 * PHP 5.3, so we have to find the length ourselves.\n                 */\n                /** @var int $length */\n                $length = RandomCompat_strlen($binary_string) - $start;\n            } elseif (!is_int($length)) {","sourceCodeStart":88,"sourceCodeEnd":124,"githubUrl":"https://github.com/paragonie/random_compat/blob/b5d188cc9d5e02f94d2c41da23093f1ef557c5b1/lib/byte_safe_strings.php#L88-L124","documentation":"RandomCompat_substr() is a binary-safe replacement for substr($str, $start, $length) used by random_compat internals. Its first parameter must be a binary string; when anything else (int, null, bool, array, object) is passed, the function throws TypeError immediately rather than relying on PHP's silent coercion, which would hide bugs in byte-level string slicing.","triggerScenarios":"Calling RandomCompat_substr($value, ...) where $value is not a string — e.g. passing the result of random_bytes() through a function that returned false on error, passing null from an uninitialized variable, or passing an integer that you intended as a length/offset by mistake (wrong argument order).","commonSituations":"Mishandled error returns: calling substr-style helpers on the result of a function that can return false/null (file reads, DB fetches). Swapped arguments: RandomCompat_substr($start, $string). Frameworks/HALs returning objects (e.g. Stringable wrappers) rather than raw strings. JSON decoding yielding numbers where strings were expected.","solutions":["Check is_string($input) before calling, and handle the false/null error case of whatever produced the value.","If the value is a Stringable object, call (string) $obj explicitly.","Verify argument order — first argument is the string, second the int start offset.","Prefer public API random_bytes()/random_int(); RandomCompat_* internals are not part of the supported surface."],"exampleFix":"// before\n$chunk = RandomCompat_substr($fileData, 0, 32); // $fileData = file_get_contents() returning false\n// after\n$fileData = file_get_contents($path);\nif (!is_string($fileData)) {\n    throw new RuntimeException('Could not read file');\n}\n$chunk = RandomCompat_substr($fileData, 0, 32);","handlingStrategy":"type-guard","validationCode":"if (!is_string($binaryString)) {\n    throw new InvalidArgumentException(\n        'RandomCompat_substr first argument must be a string, got ' . gettype($binaryString)\n    );\n}\n$chunk = RandomCompat_substr($binaryString, $start, $length);","typeGuard":"function canSubstring($value, $start, $length = null): bool {\n    return is_string($value) && is_int($start) && ($length === null || is_int($length));\n}\nif (canSubstring($input, 0, 16)) {\n    $chunk = RandomCompat_substr($input, 0, 16);\n}","tryCatchPattern":"try {\n    $chunk = RandomCompat_substr($input, $start, $length);\n} catch (TypeError $e) {\n    throw new InvalidArgumentException('Invalid substr input: ' . $e->getMessage(), 0, $e);\n}","preventionTips":["Handle false/null returns of data-producing functions before slicing.","Check argument order: string first, int offset second, int length third.","Cast Stringable objects to string explicitly.","Declare strict_types=1 to catch coercion mistakes early."],"tags":["php","typeerror","substr","argument-validation"],"backgroundTag":"invalid-argument","analyzedSha":"b5d188cc9d5e02f94d2c41da23093f1ef557c5b1","analyzedAt":"2026-09-13T16:12:09.755Z","contentChangedAt":"2026-09-13T16:12:09.755Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}