ParagonIE_Sodium_Core_Util::substr( string $str, int $start = 0, int $length = null ): string
- Source
wp-includes/sodium_compat/src/Core/Util.php:836
Extracts a byte-safe substring from $str, starting at byte offset $start and covering $length bytes, guarding against mbstring.func_overload interference. It is an internal helper inside the bundled sodium_compat library that WordPress core uses for cryptographic operations such as key splitting and AEAD decryption. Plugin and theme code almost never calls it directly; reach for PHP's native substr() or mb_substr() instead unless you are working with raw binary crypto data where byte-exact slicing matters.
Compatibility
- WordPress
- core
- PHP
- 7.4–8.6-dev
- 6.7.7
- 6.8.8
- 6.9.7
- 7.0.4
- 7.1.0
Present in every tracked release (6.7.7 to 7.1.0), and compiles on PHP 7.4 through 8.6-dev.
Parameters
$strstring$startintoptional- Default:
0 $lengthintoptional- Default:
null
Return value
string
Code examples
Every example is editable and runs in a real WordPress booted in your browser by WordPress Playground. Press Run, then edit the code: clicking away re-runs it. Nothing is sent anywhere until you do.
Split a random binary key into two byte-exact halves
This mirrors how sodium_compat internally slices raw key material without letting a multibyte-aware substr() corrupt the bytes.
$binary = random_bytes( 32 );
$first_half = ParagonIE_Sodium_Core_Util::substr( $binary, 0, 16 );
$second_half = ParagonIE_Sodium_Core_Util::substr( $binary, 16 );
printf(
'First half: %d bytes, second half: %d bytes',
strlen( $first_half ),
strlen( $second_half )
);Omitting $length, as done for $second_half, returns everything from $start to the end of the string.
See what happens when $str is not a string
The method throws a TypeError before doing any slicing if $str fails an is_string() check, so wrap risky input in a try/catch.
try {
$result = ParagonIE_Sodium_Core_Util::substr( 12345, 0, 3 );
echo esc_html( $result );
} catch ( TypeError $e ) {
echo esc_html( 'Caught TypeError: ' . $e->getMessage() );
}Common problems and fixes · 3
- Why do I get a TypeError instead of a warning when I pass the wrong type?
- Why does passing $length as 0 give me an empty string instead of the rest of the string?
- Will this behave differently on servers with mbstring.func_overload enabled?
Why do I get a TypeError instead of a warning when I pass the wrong type?
Why does passing $length as 0 give me an empty string instead of the rest of the string?
Will this behave differently on servers with mbstring.func_overload enabled?
Alternatives and related functions
substr- When you are slicing a normal ASCII or already byte-safe string outside of a cryptographic context and don't need mbstring.func_overload protection.
mb_substr- When you are deliberately working with multibyte text (like UTF-8 user content) and want character-aware slicing rather than byte-aware slicing.
ParagonIE_Sodium_Core_Util::strlen- When you need the byte length of a string before or after calling substr(), for example to compute a $length argument safely.
Performance profile
How much work a call to ParagonIE_Sodium_Core_Util::substr() does, and what it touches: the algorithmic scaling, the Zend instruction count per call across PHP versions, the hooks it hands control to, and the core code that calls it. Measured from the compiled opcodes, not a stopwatch, so every number is identical on any machine running the same PHP version, and every function in core is ranked by cost.
- Cost class
- Trivial
- Scaling
- Constant
- Instructions
- 8–21
- Plugin surface
- None
- Called by
- 46
Touches nothing outside its own arguments.
No loop in the body: the same number of instructions runs whatever you pass in.
Executed per call on PHP 8.5, depending on the branch taken. The body compiles to 35.
Nothing here hands control to plugin code.
46 places in core call this, so the cost is paid more often than your own code shows.
What one call costs · 3 distinct outcomes
One number would be a lie: the work depends on which branch runs. These are every distinct cost ParagonIE_Sodium_Core_Util::substr() can have, taken from its control-flow graph on PHP 8.5.
| When | Instructions | Calls it makes |
|---|---|---|
| always | 8–9 | none |
is_string($str) && $length !== 0 && !::isMbStringOverride() | 18 | ::isMbStringOverride() |
is_string($str) && $length !== 0 && ::isMbStringOverride() | 21 | ::isMbStringOverride(), mb_substr() |
Across PHP versions
| PHP | Compiled | Executed | Branches | Notes |
|---|---|---|---|---|
| 8.6-dev | 35 | 8–21 | 5 | |
| 8.5 | 35 | 8–21 | 5 | |
| 8.4 | 35 | 8–21 | 5 | 6 fewer instructions than PHP 8.3 |
| 8.3 | 41 | 8–21 | 5 | |
| 8.2 | 41 | 8–21 | 5 | |
| 8.1 | 41 | 8–21 | 5 | |
| 7.4 | 41 | 8–21 | 5 |
An instruction is not a fixed amount of time, so a matching count is not necessarily the same speed; what it rules out is a difference in the work itself.
Uses · 4
- mb_substr()Compat function to mimic mb_substr().
- TypeError::__construct()
- ParagonIE_Sodium_Core_Util::isMbStringOverride()Returns whether or not mbstring.func_overload is in effect.
- ParagonIE_Sodium_Core_Util::strlen()Safe string length
Used by · 46
- ParagonIE_Sodium_Compat::crypto_aead_aegis128l_decrypt()Authenticated Encryption with Associated Data: Decryption
- ParagonIE_Sodium_Compat::crypto_aead_aegis256_decrypt()Authenticated Encryption with Associated Data: Decryption
- ParagonIE_Sodium_Compat::crypto_aead_aes256gcm_decrypt()Authenticated Encryption with Associated Data: Decryption
- ParagonIE_Sodium_Compat::crypto_kx_client_session_keys()
- ParagonIE_Sodium_Compat::crypto_kx_publickey()
- ParagonIE_Sodium_Compat::crypto_kx_secretkey()
- ParagonIE_Sodium_Compat::crypto_kx_server_session_keys()
- ParagonIE_Sodium_Compat::crypto_sign_ed25519_sk_to_curve25519()Convert an Ed25519 secret key to a Curve25519 secret key
- ParagonIE_Sodium_Compat::increment()Increase a string (little endian)
- ParagonIE_Sodium_Compat::unpad()
- ParagonIE_Sodium_Core_AEGIS_State128L::absorb()
- ParagonIE_Sodium_Core_AEGIS_State128L::dec()
Show all 46
- ParagonIE_Sodium_Core_AEGIS_State128L::decPartial()
- ParagonIE_Sodium_Core_AEGIS_State128L::enc()
- ParagonIE_Sodium_Core_AEGIS_State256::decPartial()
- ParagonIE_Sodium_Core_AEGIS_State256::init()
- ParagonIE_Sodium_Core_Base64_Original::decode()decode from base64 into binary
- ParagonIE_Sodium_Core_Base64_Original::doEncode()
- ParagonIE_Sodium_Core_Base64_UrlSafe::decode()decode from base64 into binary
- ParagonIE_Sodium_Core_Base64_UrlSafe::doEncode()
- ParagonIE_Sodium_Core_SecretStream_State::fromString()
- ParagonIE_Sodium_Core_SecretStream_State::getCombinedNonce()
- ParagonIE_Sodium_Core_SecretStream_State::rekey()
- ParagonIE_Sodium_Core_SecretStream_State::xorNonce()
- ParagonIE_Sodium_Core_Util::store_3()Store a 24-bit integer into a string, treating it as big-endian.
- ParagonIE_Sodium_Core_Util::verify_16()Compare a 16-character byte string in constant time.
- ParagonIE_Sodium_Core_Util::verify_32()Compare a 32-character byte string in constant time.
- ParagonIE_Sodium_Crypto::aead_chacha20poly1305_decrypt()AEAD Decryption with ChaCha20-Poly1305
- ParagonIE_Sodium_Crypto::aead_chacha20poly1305_ietf_decrypt()AEAD Decryption with ChaCha20-Poly1305, IETF mode (96-bit nonce)
- ParagonIE_Sodium_Crypto::aead_xchacha20poly1305_ietf_decrypt()AEAD Decryption with ChaCha20-Poly1305, IETF mode (96-bit nonce)
- ParagonIE_Sodium_Crypto::aead_xchacha20poly1305_ietf_encrypt()AEAD Encryption with ChaCha20-Poly1305, IETF mode (96-bit nonce)
- ParagonIE_Sodium_Crypto::auth()HMAC-SHA-512-256 (a.k.a. the leftmost 256 bits of HMAC-SHA-512)
- ParagonIE_Sodium_Crypto::box_keypair_from_secretkey_and_publickey()
- ParagonIE_Sodium_Crypto::box_publickey()
- ParagonIE_Sodium_Crypto::box_seal_open()Opens a message encrypted via box_seal().
- ParagonIE_Sodium_Crypto::box_secretkey()
- ParagonIE_Sodium_Crypto::box_seed_keypair()
- ParagonIE_Sodium_Crypto::secretbox()XSalsa20-Poly1305 authenticated symmetric-key encryption.
- ParagonIE_Sodium_Crypto::secretbox_open()Decrypt a ciphertext generated via secretbox().
- ParagonIE_Sodium_Crypto::secretbox_xchacha20poly1305()XChaCha20-Poly1305 authenticated symmetric-key encryption.
- ParagonIE_Sodium_Crypto::secretbox_xchacha20poly1305_open()Decrypt a ciphertext generated via secretbox_xchacha20poly1305().
- ParagonIE_Sodium_Crypto::secretstream_xchacha20poly1305_init_pull()
- ParagonIE_Sodium_Crypto::secretstream_xchacha20poly1305_init_push()
- ParagonIE_Sodium_Crypto::secretstream_xchacha20poly1305_pull()
- ParagonIE_Sodium_File::secretbox_decrypt()Decrypt a file
- ParagonIE_Sodium_File::secretbox_encrypt()Encrypt a file
Source code
public static function substr($str, $start = 0, $length = null) { /* Type checks: */ if (!is_string($str)) { throw new TypeError('String expected'); } if ($length === 0) { return ''; } if (self::isMbStringOverride()) { if (PHP_VERSION_ID < 50400 && $length === null) { $length = self::strlen($str); } $sub = (string) mb_substr($str, $start, $length, '8bit'); } elseif ($length === null) { $sub = (string) substr($str, $start); } else { $sub = (string) substr($str, $start, $length); } if ($sub !== '') { return $sub; } return ''; }Changelog
Unchanged from 6.7.7 through 7.1.0.
Signature, return type and hooks compared across 5 parsed releases.
About this page
- Parsed data
- Generated from the wordpress-develop 7.1.0 tag, from
src/wp-includes/sodium_compat/src/Core/Util.php, and regenerated for each WordPress release so it tracks the code rather than a snapshot of it. - Corrections
- Something wrong on this page? Report it and it gets fixed in the next regeneration.