PDOStatement::fetchObject is a very practical method when operating a database using PDO in PHP. It can return a record as an object and assign the value of each column to the object's properties. However, when the type of the database column does not match the expected type of the target object property, some confusing problems can arise, such as type mismatch, implicit conversion errors, or behavioral exceptions.
class User {
public int $id;
public string $name;
public bool $is_active;
}
$stmt = $pdo->query('SELECT id, name, is_active FROM users');
$user = $stmt->fetchObject('User');
Suppose that the is_active column in the users table is a TINYINT(1) type, and in the User class we define the bool type. If the value of is_active is 1 or 0 , you may expect it to be automatically converted to true or false . But in reality, PHP will directly assign values of type int , which will cause types inconsistencies and even directly throw errors in strict type mode.
Inside fetchObject , the database field is mapped to object properties through __set() or direct assignment. It does not perform type conversion, but directly assigns the value according to the original value of the database. This means:
The INT value in the database will become PHP int
VARCHAR will become string
Although TINYINT(1) is often used for boolean values, it is actually an integer type
After the type prompt of declare(strict_types=1) or PHP 8.2+ is strengthened, a type mismatch will cause a runtime error.
A safe approach is to manually convert each field to the appropriate type after getting the object. This method is the safest and most readable.
$user = $stmt->fetchObject('User');
if ($user !== false) {
$user->id = (int)$user->id;
$user->name = (string)$user->name;
$user->is_active = (bool)$user->is_active;
}
fetchObject supports passing in constructor parameters. If you design a constructor that accepts initialized data, you can implement type conversion gracefully:
class User {
public function __construct(
public int $id,
public string $name,
public bool $is_active
) {}
}
$stmt = $pdo->query('SELECT id, name, is_active FROM users');
$user = $stmt->fetchObject('User');
But the problem here is: if the database column order is inconsistent or the fields do not match exactly, an exception may be thrown.
If the data volume is large or the object structure is complex, consider using a factory class to handle the transformation logic.
class UserFactory {
public static function fromDb(stdClass $row): User {
return new User(
(int)$row->id,
(string)$row->name,
(bool)$row->is_active
);
}
}
$stmt = $pdo->query('SELECT id, name, is_active FROM users');
$row = $stmt->fetchObject();
if ($row !== false) {
$user = UserFactory::fromDb($row);
}
Although this has more code, the structure is clear and the logic is centralized, making it easy to unit testing and maintenance.
You can also use PDO::FETCH_CLASS | PDO::FETCH_PROPS_LATE mode, but you also need to pay attention to the type conversion problem. PHP does not make type inference internally.
$stmt = $pdo->query('SELECT id, name, is_active FROM users');
$stmt->setFetchMode(PDO::FETCH_CLASS | PDO::FETCH_PROPS_LATE, 'User');
$user = $stmt->fetch();
As mentioned above, although the code is simplified, it still cannot solve the problem of type mandatory.
When using PDOStatement::fetchObject for object extraction, be sure to pay attention to the correspondence between database field types and object attribute types. PHP does not automatically perform intelligent conversions, especially after turning on the type prompt, developers need to explicitly handle the conversion logic. The most recommended way is to decouple data extraction and object construction, such as using factory classes or manual transformations, thereby improving the robustness and maintainability of your code.
For more related examples, please refer to: https://gitbox.net/pdo/fetchobject-type-conversion