How to Override System Workflow message template variables, single and batch
The short answer is that you can't change that from an extension, but you can change it by patching core. Unfortunately, this requires a change in coding-convention which isn't a trivial one-line patch -- it requires understanding some other layers/concepts and changing multiple files.

The root issue here is that the coding convention has relied too heavily on static functions. For example, if you grep the codebase for CRM_Contribute_Form_Task_Invoice, you'll find snippets like this one (from CRM/Contribute/Form/Task/Invoice.php):

CRM_Contribute_Form_Task_Invoice::printPDF($contributionIDs, $params, $contactId, CRM_Core_DAO::$_nullObject);


If you could change that line to point to your own version of printPDF(), you'd be ready to go. But it's hard-coded -- there's no way for an extension to change it. The good news: there are better patterns which are more flexible, e.g.

// Example: Use a service container
Civi::service('invoice_printer')-&gt;print($contributionIDs, $params, $contactId, CRM_Core_DAO::$_nullObject);

// Example: Use a hook
CRM_Utils_Hook::printInvoicePdf($contributionIDs, $params, $contactId, CRM_Core_DAO::$_nullObject);


Either of those formulations would make it possible for extensions to change the implementation of printPDF(). The bad news: either way, you'd to have to refactor the existing implementation to match a different coding convention.

A few links that might be interesting on the general topic of statics, services, and dependency-injection:


https://www.imarc.com/blog/dependency-injection
http://fabien.potencier.org/what-is-dependency-injection.html
https://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_container
https://civicrm.org/blog/totten/the-static-is-dead-long-live-the-static