There is one trifle. By default, PDO simply mimics prepared statements.
And although in emulation mode it starts the same old request without actually preparing one operator :)
So, first of all,
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, FALSE);
to include real prepared statements.
it was also often noted that there is a penalty for performance
There is one more trifle. Unfortunately, there is very little real knowledge in the world. And especially in the world of Q&A sites. People tend to repeat the information they read and find it reasonable. Without any proof test or even without their hands. Therefore, the “frequently noted” should not be regarded as a reliable source.
Let's get back to business: although there should be some kind of punishment, it should be insignificant in most cases. If so, you need to set up your system.
In any case, in emulation mode you received it both "fast" and safe.
Update
Well, after running tests on my data, I have to say that something is wrong with your database if you have a difference of 3 times on a large data set.
To request lightning
select title from Board where id = 1
results
emulation on off query 0.07 0.130 prepare 0.075 0.145
but for a rather burdensome request
select title from Board where id > 1
results
emulation on off query 0.96 0.96 prepare 0.96 1.00
So, as we can see, on a large dataset the difference becomes imperceptible.
There is some difference for the lightning request, but since only 0.0003th fraction is required for one request (for one request), I would say that this is a great example for the word "indifference".
For equal results between query () / prepare () - I have only one idea - PDO uses prepare / execute for all queries, even those that don't have bindings.
Now the problem is with the encoding.
Yes, the strange GBK issue affects PDOs for versions prior to 5.3.3. These versions did not have the ability to set the correct encoding and were inevitably vulnerable (in emulation mode). But since 5.3.3 PDO supports the encoding setting in the DSN, and now everything is fine with it.
For mysqli, mysqli_set_charset() must be used for this purpose with the same (impenetrable) result.
In my own mysqli-based class, I use my own placeholder implementation and don't use prepared statements at all. Not for performance reasons, but to increase reliability.