Un développeur qui découvre l’Abilities API de WordPress pour la première fois, après avoir lu qu’une ability se déclare avec wp_register_ability(), se pose une question simple : parmi les champs de configuration, lequel exécute réellement l’action que l’agent a demandée ? La réponse est execute_callback, mais la confusion la plus fréquente observée en revue de code ne porte pas sur son existence, plutôt sur son rôle exact face à son voisin immédiat, permission_callback.
Ce que fait execute_callback, précisément
Le champ execute_callback reçoit un callable PHP, appelé avec les arguments fournis par l’agent une fois validés contre le schéma d’entrée de l’ability. C’est cette fonction, et uniquement elle, qui contient la logique métier réelle : créer un article, modifier une commande, envoyer un message.
wp_register_ability( 'wpmoderne/programmer-article', array(
'label' => __( 'Programmer un article', 'wpmoderne' ),
'description' => __( 'Programme la publication d’un article à une date future.', 'wpmoderne' ),
'input_schema' => array(
'type' => 'object',
'properties' => array(
'post_id' => array( 'type' => 'integer' ),
'date' => array( 'type' => 'string' ),
),
'required' => array( 'post_id', 'date' ),
),
'execute_callback' => 'wpmoderne_programmer_article',
'permission_callback' => 'wpmoderne_peut_programmer',
) );
function wpmoderne_programmer_article( array $input ): array {
$result = wp_update_post( array(
'ID' => $input['post_id'],
'post_status' => 'future',
'post_date' => $input['date'],
), true );
return array( 'updated' => ! is_wp_error( $result ) );
}
Cette fonction reçoit un tableau déjà conforme au schéma déclaré ; elle n’a pas à revérifier les types ni la présence des champs requis, ce travail relevant du schéma d’entrée traité en amont, pas de cette fonction elle-même.
Ce que permission_callback fait avant lui

Avant qu’execute_callback ne soit jamais appelé, le registre évalue permission_callback, une fonction distincte qui retourne un booléen ou un objet WP_Error, chargée uniquement de décider si l’appelant a le droit de déclencher cette ability :
function wpmoderne_peut_programmer( array $input ): bool {
return current_user_can( 'edit_post', $input['post_id'] );
}
Si cette fonction renvoie faux, execute_callback n’est jamais invoqué : l’action métier reste entièrement à l’abri d’un appelant non autorisé, sans qu’une seule ligne du code de programmation d’article n’ait à se préoccuper de qui a le droit de faire quoi.
Un execute_callback qui vérifie lui-même les droits de l’appelant a mal compris son rôle : cette vérification appartient à un autre champ, évalué avant lui, qui décide s’il aura même l’occasion de s’exécuter.
L’erreur observée en revue de code
Sur quinze abilities passées en revue, quatre plaçaient un appel à current_user_can() directement au début de la fonction déclarée en execute_callback, sans jamais renseigner de permission_callback distinct, ou en le renseignant avec une fonction renvoyant systématiquement vrai. Le résultat produit un comportement fonctionnellement proche, mais deux défauts concrets : un audit qui interroge le registre pour connaître les permissions déclarées, via wp_get_abilities() par exemple, ne voit rien d’anormal puisque le champ dédié semble correctement rempli ; et un agent qui tente l’appel sans les droits nécessaires reçoit une erreur produite tardivement, depuis l’intérieur de l’action, plutôt qu’un refus propre et anticipé du registre lui-même.
- execute_callback : contient uniquement la logique métier de l’action
- permission_callback : contient uniquement la décision d’autoriser ou non cette action
- Aucune vérification de droits ne doit se dupliquer entre les deux fonctions
Ce que ce champ ne couvre pas
Le champ execute_callback ne valide pas les entrées reçues au-delà de ce que le schéma d’entrée a déjà vérifié en amont, et il ne remplace pas non plus une gestion d’erreur explicite : une fonction qui échoue silencieusement, sans retourner d’indication exploitable, laisse l’agent sans moyen de savoir si l’action a réellement eu lieu. Retourner un tableau clair, ou lever une exception gérée par le registre, reste la responsabilité de cette fonction, distincte de la question des droits traitée ailleurs.
Conclusion
Distinguer clairement execute_callback de permission_callback n’est pas un détail de vocabulaire : c’est ce qui garantit qu’un audit des permissions d’un site, mené en lisant le registre des abilities, reflète effectivement ce qui se passera en production, plutôt qu’une vérification masquée à l’intérieur d’une fonction censée ne faire qu’exécuter l’action déjà autorisée.