Se você desativar a configuração padrão de
PDO::ATTR_EMULATE_PREPARES
, então vai funcionar. Acabei de descobrir que essa configuração está ativada por padrão para o mysql, o que significa que você nunca usa instruções preparadas, o php cria internamente sql dinâmico para você, citando os valores para você e substituindo os espaços reservados. Sim, um grande wtf. $pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$stmt = $pdo->prepare($sql);
$stmt->execute(array(5)); //works!
As preparações são emuladas por padrão por motivos de desempenho.
Veja também PDO MySQL:Usar PDO::ATTR_EMULATE_PREPARES ou não?