If I have two extensions that both use, say,
hook_civicrm_post, how can I control the order in which the two extensions fire?
CRM/Utils/Hook.php indirectly calls CRM/Extension/Mapper.php::getActiveModuleFiles which does
SELECT full_name, file FROM civicrm_extension WHERE is_active = 1 AND type = "module"
So there is no well-defined order. You could of course hack that line to add an ORDER BY.
I got some great answers on Mattermost.
Frank Gomez of Ginkgo Street Labs says: We've developed an approach (which you may find heavy handed) to dealing with order of operation problems when it comes to hook_civicrm_post. In our case the problem was not the order in which extensions were firing it but rather that dependent entities might not exist yet. Basically the approach is to defer execution of the code by creating a queue item to deal with it on the next page load, when the dependent entities are expected to have been created.
Here's the repo: https://github.com/ginkgostreet/com.ginkgostreet.listener.
Allen Shaw of Joinery points to Tim Otten's post here, the most notable part is: [You can't] - in fact, I suspect the order is non-deterministic, with a slight bias towards installation-order (ie the extension which was installed first would tend to run first). I came to this belief from tracing CRM_Utils_Hook::commonInvoke() => commonBuildModuleList() => requireCiviModules() ...etc... finally arriving at CRM_Extension_Mapper::getActiveModuleFiles(), which queries the DB and does not have an ORDER BY.
I am not sure if you can, perhaps by making sure the sequence is as you would want in civicrm_system?
Can you explain a little about what you want to do so I can perhaps come up with another alternative (you never know....)
My original answer in 2016 is now out of date. In Civi 4.7.19+, you can control the order of hooks with the Symfony EventDispatcher.