Troubleshooting
Common error messages and how to fix them, plus the gotchas that don’t produce an error at all: double encoding, vanishing math results, and markup that prints as text. Headings quote the message or symptom so you can find them by search.
Most Common Issues
Section titled “Most Common Issues”Comparisons and if() checks don’t work as expected
Section titled “Comparisons and if() checks don’t work as expected”A SmartString is an object wrapping your value, and PHP comparisons and
checks don’t work on objects - unwrap with value() (or int(), float())
first. Without that, string comparisons see the encoded text, numeric
comparisons treat the object as the number 1, and an object is always
truthy and never empty(), even when the value inside is null:
$status = SmartString::new("it's active");$price = SmartString::new(2000);$missing = SmartString::new(null);
// none of these work the way they readif ($status == "it's active") { } // WRONG - false: compares the encoded textif ((string)$status === "it's active") { } // WRONG - false: the cast encodes tooif ($price > 1000) { } // WRONG - false: the object compares as int 1, not 2000if ($missing === null) { } // WRONG - false: the object itself isn't nullif (empty($missing)) { } // WRONG - false: objects are never emptyif ($missing) { } // WRONG - true: objects are always truthy
// unwrap the value insteadif ($status->value() === "it's active") { } // RIGHTif ($price->int() > 1000) { } // RIGHTif ($missing->isMissing()) { } // RIGHT - true for null or ""if ($missing->isEmpty()) { } // RIGHT - true for null, "", and zerosLoose == is the sneaky one: it passes for plain words like "active"
(nothing to encode) and fails the moment the value contains a quote or
an ampersand, so it can survive testing and break on real data.
HTML tags print as text: the page shows a literal <br>
Section titled “HTML tags print as text: the page shows a literal <br>”Markup passed through a regular (encoding) method gets encoded like any other value:
echo $address->append("<br>"); // WRONG - prints "12 High St<br>" as visible textecho $address->appendHtml("<br>"); // RIGHT - markup stays markupMarkup goes through the HTML-aware methods (appendHtml(), wrapHtml(),
nl2br(), rawHtml()); everything else treats it as text to encode. See
Encoding and HTML.
Output shows & or ' - encoded twice
Section titled “Output shows & or ' - encoded twice”If you see & where an & should be, you are encoding twice.
SmartString already encodes in string context, so a manual
htmlspecialchars() around it encodes the encoded output again:
$name = SmartString::new("Jean O'Brien");
echo htmlspecialchars($name); // WRONG - Jean O&apos;Brien (double encoded)echo $name; // RIGHT - Jean O'BrienThis is the most common mistake when adopting SmartString: delete the
htmlspecialchars() call. If some other encoder genuinely needs the value,
give it the raw one: htmlspecialchars($name->value()).
The other cause is a database that already contains encoded text, usually
from a form handler that encoded values before saving them. Whatever is
stored encoded gets encoded again on output. Save the raw text instead,
then clean up the existing rows with a one-time
htmlspecialchars_decode($value, ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML5) (the flags
matter; the defaults leave ' behind).
or() kept my zero / isEmpty() lost my zero
Section titled “or() kept my zero / isEmpty() lost my zero”Working as designed, in both directions: or() treats zero as a real
value, not a missing one, and isEmpty() follows PHP’s empty(), which
treats 0, "0", and false as empty. The
truth table shows
every combination.
Other Issues
Section titled “Other Issues”“$str->methodName needs brackets() everywhere and {curly braces} in strings”
Section titled ““$str->methodName needs brackets() everywhere and {curly braces} in strings””What happened: A method was written like a field: brackets missing, or a call inside a double-quoted string without curly braces. It’s a PHP warning, not an error: the page keeps running and the expression outputs nothing, so it usually shows up as a blank spot where a chain should be. When output goes blank, check the error log.
Fix: Fields never take brackets and work anywhere, strings included.
Methods always take brackets, plus curly braces around the call inside a
string; chains (more than one ->) need the braces too:
// fields - no brackets, work anywhereecho $user->name;echo "Hello $user->name";
// methods and chains - brackets everywhere, curly braces in stringsecho $name->trim(); // worksecho "Hello {$name->trim()}"; // worksecho "Hello {$user->name->trim()}"; // worksecho $name->trim; // WRONG - written like a field, logs this warningecho "Hello $name->trim()"; // WRONG - prints "Hello ()", logs this warningecho "Hello $user->name->trim()"; // WRONG - prints "Hello Jean->trim()"Braces are always safe: {$...} works around anything in a string, plain
fields included.
Math chain outputs nothing
Section titled “Math chain outputs nothing”Math methods return null when either side is null or not numeric, and null echoes as an empty string. The usual causes, in order of frequency:
echo SmartString::new(null)->add(50); // "" - null inputecho SmartString::new("1,234")->add(50); // "" - the comma makes it non-numeric to PHPecho SmartString::new(100)->divide(0); // "" - division by zeroecho SmartString::new(1234.5)->numberFormat(2)->add(50); // "" - formatting made it non-numeric; format lastFix: Decide what null should mean and say so: ->ifNull(0) before the
math to treat missing as zero, or ->or('n/a') at the end to show a
fallback. For pre-formatted strings like "1,234", store plain numbers and
format on output instead; when the stored data isn’t yours to change, strip
the formatting in the chain first: ->pregReplace('/[^0-9.-]/', '')->add(50).
“Call to a member function methodName() on string”
Section titled ““Call to a member function methodName() on string””What happened: Something was chained after a method that ends the
chain. The encoding methods (htmlEncode(), urlEncode(), jsonEncode(),
nl2br(), appendHtml(), wrapHtml()) return a plain string rather than
another SmartString, so nothing can chain after them. That’s on purpose:
once a value is encoded it’s finished output - no accidental double-encoding
with additional chained methods.
echo $bio->nl2br()->or('No bio'); // throws - nl2br() returned a stringFix: Do the conditional work first, encode last:
echo $bio->or('No bio')->nl2br();ifZero() after percent() never fires
Section titled “ifZero() after percent() never fires”Formatters return display text, and ifZero() only recognizes numeric
zeros, so "0.00%" and "$0.00" never match. Plain numberFormat() output
like "0.00" is still numeric, so ifZero() works after it. Use the
percent() parameter, or match the formatted text with ifEquals():
echo $rate->percent(2, ifZero: 'N/A'); // percent's zero rule is a parameterecho $rate->percent(2)->ifEquals('0.00%', 'N/A'); // or match the formatted textecho $price->numberFormat(2)->prepend('$')->ifEquals('$0.00', 'Free!');See Run Conditionals Before Formatting.
“orRedirect(): headers already sent in file.php on line 12”
Section titled ““orRedirect(): headers already sent in file.php on line 12””What happened: orRedirect() redirects when the value is missing (null
or ""), and redirects only work before any output has been sent - this
page had already sent some. The headers-sent check runs even when the value
is present, so the mistake shows up on the first request instead of waiting
for a missing value.
Fix: Move the guard above any output. The message names the file and
line where output started; the usual culprits are echo statements and
whitespace before <?php.
Debugging
Section titled “Debugging”print_r() covers most “what is this value?” questions:
print_r($name); // shows the raw stored valueThe print_r() output shows the original, unencoded value. To see a value
mid-chain, print_r() any link of it; chains are just objects.