Course outline

PHP / Basic: values and request foundations / Lesson 2

Strings, text boundaries, and encoding

Treat PHP strings as byte sequences while giving human text an explicit encoding policy. Diagnose length, search, concatenation, and serialized output without assuming every byte is a visible character.

About 4 min readContent reviewed
On this page

Lesson goals

  • Distinguish byte length from Unicode text units.
  • Use strict search-result checks.
  • Choose output handling for each context.

A string stores bytes, not an encoding guarantee

PHP strings can contain arbitrary bytes. An application must decide whether a value represents UTF-8 text, binary content, an identifier, or another format. strlen counts bytes, so it is not a general count of user-perceived characters. A Unicode code point and a grapheme cluster are also different units; combining marks can make several code points look like one character. Document the unit for a name-length rule instead of borrowing a database column size. Use appropriate extensions or Unicode-aware operations for the agreed task, and validate malformed input rather than silently assuming all sources share an encoding.

php UTF-8 bytes and code points
<?php
declare(strict_types=1);
$text = "أ";
preg_match_all("/./u", $text, $matches);
echo strlen($text), PHP_EOL;
echo count($matches[0]), PHP_EOL;
Expected output
2
1

This precomposed Arabic letter occupies two UTF-8 bytes and one code point. The example does not count grapheme clusters in arbitrary text.

Build strings with visible boundaries

The dot operator concatenates strings. Single-quoted and double-quoted literals have different interpolation and escape rules, so choose the form that makes intent obvious. Avoid allowing untrusted input to become a format string or executable PHP expression. For a receipt message, keep the validated amount numeric until a dedicated formatting step. Formatting money is not the same as calculating it, and locale-aware presentation needs a specified locale and extension capability. Keep line endings deliberate in generated files and use PHP_EOL only when platform-native console output is intended rather than a protocol demanding particular bytes.

strpos returns a numeric position or false. A match at the beginning is zero, which is falsy, so a loose if check can report a missing prefix that is present. Compare explicitly with false or use str_contains when the question is simply whether a substring exists. Search semantics depend on byte and case rules; a human-language search may need normalization and a locale-aware strategy beyond this primitive. Keep an empty needle’s behavior in mind and define whether empty user searches mean no filter or an error before applying string functions.

php Separate a valid zero from false
<?php
declare(strict_types=1);
$position = strpos("PHP basics", "PHP");
echo ($position === false ? "missing" : "found:" . $position), PHP_EOL;
echo (str_contains("PHP basics", "SQL") ? "yes" : "no"), PHP_EOL;
Expected output
found:0
no

The strict check recognizes the prefix at position zero. The second operation intentionally asks only for membership.

Encoding a representation is not universal escaping

JSON encoding produces a JSON representation, HTML escaping protects specific HTML text or attribute contexts, and URL encoding serves a different grammar. Do not apply one transformation and call the value universally safe. Keep original domain data separate from rendered representations to avoid double escaping when the same title appears in a page and an API response. JSON_THROW_ON_ERROR makes encoding failures explicit instead of treating false as an empty result. A UTF-8 policy should reach storage, transport headers, parsing, and output so a correct local string does not become corrupted at the next boundary.

Inspect bytes and meaning without damaging data

When a name appears corrupted, identify the first boundary where expected bytes change. Compare a small synthetic value through decoding, storage, retrieval, and rendering instead of repeatedly converting encodings until the display looks plausible. Conversion from the wrong assumed source can permanently alter information. Preserve the original input for a controlled investigation and avoid logging personal text unnecessarily. Length checks, case folding, trimming, and normalization are product decisions as well as implementation choices. Test Arabic, combining marks, empty text, and invalid sequences according to the documented contract rather than assuming ASCII examples represent all users.

Common errors and fixes

An Arabic name is rejected by an English byte limit

Why it happens
Byte length is mistaken for the product’s character unit.
How to fix it
Agree the unit and use a suitable Unicode-aware measurement.
Prevent it next time
Include multilingual and combining-mark cases.

A present prefix is reported missing

Why it happens
strpos result zero is checked loosely.
How to fix it
Compare with false strictly or use a membership API.
Prevent it next time
Test matches at start, middle, and nowhere.

Text becomes double escaped

Why it happens
Rendered HTML is stored as if it were original domain data.
How to fix it
Store validated original data and render per output context.
Prevent it next time
Keep serialization functions at named boundaries.

Check your understanding

1. A substring starts at index zero. Which condition proves it exists?

Show worked solution

Use strpos($text, $needle) !== false. Zero is a successful position and must not be discarded by a truthiness check. If position is irrelevant, str_contains expresses the membership question directly. Still define empty-needle and case requirements at the input boundary.

2. Does one Unicode code point always equal one visible character?

Show worked solution

No. Combining marks and other sequences can form a single grapheme cluster from multiple code points. Choose bytes for byte-oriented protocols, code points where that rule is intentional, and grapheme-aware handling for user-perceived length when required. Test the exact policy rather than using strlen by habit.

Key takeaways

  • Choose a text unit and encoding before validating length.
  • Search results and output contexts need explicit interpretation.

Official references