AbstractTokenSubscriber and custom token processors stopped being called?
I have been tearing my hair out over this for over a week now...
Sometime between 24 November 2021 and 11 May 2022 (these are the last 'good' and first 'bad' scheduled reminders on file), the token processor (implemented as an AbstractTokenSubscriber
) in our custom CiviCRM extension stopped working, in that the tokens were no longer processed and simply omitted from the resulting emails. I suspect this happened when we upgraded to 5.47.3 on 3 April 2022. The custom token processor code did not change in the meantime.
It seems that the token subscriber methods are simply no longer called from the Symfony system at all. With judicious use of error_log()
and debug_backtrace()
, I can see that the getSubscribedEvents()
method of the token processor object is called when CiviCRM is invoked, but thereafter the event dispatcher never calls any of the methods in the token processor:
- The
registerTokens()
andevaluateTokens()
methods of the token processor are never called. - The dispatcher never fires the
civi.token.list
orcivi.token.eval
events for the subscribed processor (although I can see these events being dispatched to other token processors in core).
In fact the extension has three token processor objects, all affected in the same way.
The token processors are set up in the extension config routine as follows:
function civicses_civicrm_config(&$config) {
_civicses_civix_civicrm_config($config);
if (isset(Civi::$statics[__FUNCTION__])) { return; }
Civi::$statics[__FUNCTION__] = 1;
Civi::dispatcher()->addSubscriber(new CRM_Civicses_TokensSite());
Civi::dispatcher()->addSubscriber(new CRM_Civicses_TokensEvent());
Civi::dispatcher()->addSubscriber(new CRM_Civicses_TokensParticipant());
}
I tried resetting the cache and deleting the cached container multiple times, all to no avail. I also tried disabling and re-enabling the extension.
To make sure I wasn't going mad, I even tried re-implementing some of the simple tokens using ..._civicrm_container()
, ..._register_tokens()
and ..._evaluate_tokens()
as described in https://docs.civicrm.org/dev/en/latest/framework/token/#defining-tokens but the same issue occurred: the methods were never called. Even after purging the cached container.
Aware that various parts of CiviCRM are (or were) still in flux with regards to token processor migration, I tried generating both a mass mailing and a scheduled reminder. Neither worked.
An upgrade to 5.50.4 has not fixed the issue. I think I am going mad after all! I have read and re-read the documentation several times now and can't see what I'm doing wrong (and in any case it worked before), so I suspect this is a genuine issue.
Any and all help gratefully received.
Code from the token processor that worked until earlier this year, if anyone is interested. It provides Joomla routed frontend URLs to the event pages (note deprecated hook included for earlier CiviCRM version).
class CRM_Civicses_TokensEvent extends \Civi\Token\AbstractTokenSubscriber
{
/* Public methods */
/**
* Class constructor.
*/
public function __construct()
{
parent::__construct('event', self::getTokens());
}
/**
* @inheritDoc
*/
public function checkActive(\Civi\Token\TokenProcessor $processor)
{
// Copied from CRM_Event_Tokens
return !empty($processor->context['actionMapping'])
&& $processor->context['actionMapping']->getEntity() === 'civicrm_participant';
}
/**
* @inheritDoc
*/
public function evaluateToken(\Civi\Token\TokenRow $row, $entity, $field, $prefetch = NULL)
{
$info = ($field == 'info_url_frontend');
$registration = ($field == 'registration_url_frontend');
if ($info || $registration)
{
$actionSearchResult = $row->context['actionSearchResult'];
$path = ($registration ? 'civicrm/event/register' : 'civicrm/event/info');
// Default URL using original logic in CRM_Event_Tokens (but with $frontend set to TRUE)
$event = [
'event_id' => $actionSearchResult->event_id,
'url' => \CRM_Utils_System::url($path, 'reset=1&id='.$actionSearchResult->event_id, TRUE, NULL, TRUE),
];
// Use same URL logic as for upcoming event emails, if possible
CRM_Civicses_Events::fixEventURL($event, 0, $registration);
$row->tokens($entity, $field, $event['url']);
}
}
/* CiviCRM hook implementations */
/**
* Implements hook_civicrm_tokens().
*
* @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_tokens
*
* NEEDED TO ENSURE LISTING IN ADMINISTRATOR FORMS (uses method ref'd below not registerTokens event).
* @see CRM_Core_SelectValues::eventTokens()
*/
static public function civicrm_tokens(&$tokens)
{
foreach (self::getTokens() as $key => $value)
{
$tokens['event']['event.'.$key] = $value;
}
}
/* Private methods */
/**
* Get token names.
* @return array
*/
static private function getTokens() : array
{
return [
'info_url_frontend' => ts('Event Info URL (front-end)'),
'registration_url_frontend' => ts('Event Registration URL (front-end)'),
];
}
}