Da última vez que verifiquei, não foi possível preparar uma declaração em que as colunas afetadas eram desconhecidas no momento da preparação - mas essa coisa parece funcionar - talvez seu sistema de banco de dados seja mais tolerante do que aqueles que estou usando (principalmente postgres)
O que está claramente errado é a declaração implode(), já que cada variável deve ser tratada por ela mesma, você também precisa de parênteses ao redor da lista de campos na declaração de inserção.
Para inserir campos definidos pelo usuário, acho que você tem que fazer algo assim (pelo menos assim que eu faço);
$fields=array_keys($a); // here you have to trust your field names!
$values=array_values($a);
$fieldlist=implode(',',$fields);
$qs=str_repeat("?,",count($fields)-1);
$sql="insert into user($fieldlist) values(${qs}?)";
$q=$DBH->prepare($sql);
$q->execute($values);
Se você não pode confiar nos nomes dos campos em $a, você deve fazer algo como
foreach($a as $f=>$v){
if(validfield($f)){
$fields[]=$f;
$values[]=$v;
}
}
Onde validfields é uma função que você escreve que testa cada nome de campo e verifica se é válido (rápido e sujo fazendo um array associativo $valfields=array('name'=>1,'email'=>1, 'phone'=>1 ... e, em seguida, verificando o valor de $valfields[$f], ou (como eu preferiria) buscando os nomes dos campos do servidor)