Problem Statement
When serializing objects via JsonConverter::objectToJson(), getters returning null or false are silently skipped. This causes two problems:
- Boolean data loss — a method like
isActive() returning false is a valid value, not a signal to omit the property.
- Inconsistency — public properties with
null values ARE included, but getters returning null are NOT.
Example:
class User {
public ?string $nickname = null; // ✅ Included as "nickname": null
public function getMiddleName(): ?string {
return null; // ❌ Silently excluded
}
public function isActive(): bool {
return false; // ❌ Silently excluded
}
}
$json = new Json();
$user = new User();
$json->addObject('user', $user);
echo $json;
// Current: {"user":{"nickname":null}}
// Expected: {"user":{"MiddleName":null,"Active":false,"nickname":null}}
The root cause is in JsonConverter::objectToJson():
if ($propVal !== false && $propVal !== null) {
$json->add(substr($methods[$y], 3), $propVal);
}
Proposed Solution
Two changes:
Part 1: Include all getter return values by default
Remove the null/false filter. All public getter results are serialized:
$propVal = call_user_func([$obj, $methods[$y]]);
$json->add(substr($methods[$y], 3), $propVal);
This matches the behavior already applied to public properties and aligns with json_encode philosophy (null is a valid JSON value).
Part 2: Add #[JsonIgnore] attribute for opt-out control
Introduce a PHP 8.1 attribute that can be applied to both getters and public properties to exclude them from serialization:
use WebFiori\Json\JsonIgnore;
class User {
#[JsonIgnore]
public string $internalId = 'abc-123'; // excluded
#[JsonIgnore]
public function getDebugInfo(): string { // excluded
return 'internal';
}
public function getName(): string { // included
return 'Ibrahim';
}
public ?string $email = null; // included as null
}
Implementation sketch:
// For getters:
$refMethod = new \ReflectionMethod($obj, $methods[$y]);
if (!empty($refMethod->getAttributes(JsonIgnore::class))) {
continue;
}
// For public properties:
foreach ($publicProps as $prop) {
if (!empty($prop->getAttributes(JsonIgnore::class))) {
continue;
}
$json->add($prop->getName(), $prop->getValue($obj));
}
The attribute class:
namespace WebFiori\Json;
#[\Attribute(\Attribute::TARGET_METHOD | \Attribute::TARGET_PROPERTY)]
class JsonIgnore {}
Alternatives Considered
- Only skip null, include false — fixes booleans but still can't serialize null from getters.
- Configurable null strategy (
nullStrategy: 'include') — adds complexity without solving the opt-out problem.
- Return a sentinel object to signal skip — awkward API, non-standard.
Breaking Change
Yes — objects that previously relied on returning null or false from getters to hide properties will now have those properties appear in output. Developers should migrate to #[JsonIgnore] for explicit exclusion. This is a major version change candidate.
Additional Context
- PHP 8.1 is already the minimum version, so native attributes are available.
#[JsonIgnore] is a well-known pattern in other ecosystems (Jackson in Java, System.Text.Json in .NET, Symfony Serializer).
- This pairs well with future attributes like
#[JsonProperty('custom_name')] and #[JsonType(UserObj::class)].
Problem Statement
When serializing objects via
JsonConverter::objectToJson(), getters returningnullorfalseare silently skipped. This causes two problems:isActive()returningfalseis a valid value, not a signal to omit the property.nullvalues ARE included, but getters returningnullare NOT.Example:
The root cause is in
JsonConverter::objectToJson():Proposed Solution
Two changes:
Part 1: Include all getter return values by default
Remove the
null/falsefilter. All public getter results are serialized:This matches the behavior already applied to public properties and aligns with
json_encodephilosophy (null is a valid JSON value).Part 2: Add
#[JsonIgnore]attribute for opt-out controlIntroduce a PHP 8.1 attribute that can be applied to both getters and public properties to exclude them from serialization:
Implementation sketch:
The attribute class:
Alternatives Considered
nullStrategy: 'include') — adds complexity without solving the opt-out problem.Breaking Change
Yes — objects that previously relied on returning
nullorfalsefrom getters to hide properties will now have those properties appear in output. Developers should migrate to#[JsonIgnore]for explicit exclusion. This is a major version change candidate.Additional Context
#[JsonIgnore]is a well-known pattern in other ecosystems (Jackson in Java, System.Text.Json in .NET, Symfony Serializer).#[JsonProperty('custom_name')]and#[JsonType(UserObj::class)].