Think of a Drupal site as a shared office building where every module rents a floor. Core doesn't know, and shouldn't need to know, what any individual tenant module wants to do when a new employee logs in, a page renders, or a form gets submitted. Instead, at each of those key moments, core walks down the hallway and knocks on every module's door with a specific, predictable question — "does anyone want to do something here?" That knock is a hook, and the entire architecture that makes Drupal extensible without ever touching core code depends on it.
What you'll learn in this lesson
- What a hook actually is, and why the naming convention
hook_NAME()exists - The three parts every hook has: a name, an implementation, and a definition
- How Drupal discovers your hook implementations automatically — and the one thing you must remember to do after adding a new one
- A first, working hook implementation you can add to any module right now
A hook is just a function with a predictable name
If your module is machine-named example_module and you want to react to a user logging in, you write a function named exactly example_module_user_login(). That's it — no registration file, no service definition, no annotation. Drupal's own documentation defines the abstract hook as hook_user_login(), and implementing it is simply a matter of replacing the literal word hook with your own module's machine name.
/**
* Implements hook_user_login().
*/
function example_module_user_login(array $edit, \Drupal\user\UserInterface $account) {
\Drupal::logger('example_module')->notice('User %name has logged in.', [
'%name' => $account->getAccountName(),
]);
}
The moment this function exists, in an enabled module, Drupal will call it every single time any user logs in — anywhere on the site, triggered by any code, including core's own login form or a contributed module's SSO integration. You never call this function yourself.
The three parts of a hook
Every hook in Drupal breaks down into exactly three pieces, and keeping them distinct will save you a lot of confusion later:
- The name — the abstract pattern like
hook_user_login, documented (often in a module's.api.phpfile) so other developers know it exists and what arguments it receives. - An implementation — your module's concrete function, named with your module's machine name instead of the literal word
hook. - A definition — the code, somewhere, that actually triggers the hook and collects the results. For core hooks this lives deep in Drupal core; for a hook your own module defines, you write this part yourself using the
module_handlerservice.
Discovery is cached — remember to clear it
Drupal doesn't scan your codebase on every request looking for functions matching a naming pattern — that would be far too slow. Instead, the list of which modules implement which hooks is built once and cached. This means that the very first time you add a brand-new hook implementation to a module, Drupal won't call it until you clear the cache (drush cr, or Configuration → Performance → Clear all caches). Forgetting this step is one of the most common "why isn't my hook running?" moments for developers new to Drupal.
Quick check: you've just added
hooks_example_user_login()to a module and enabled it, but logging in produces no log message. Before assuming your code is wrong, what's the first thing to try? (Answer: clear the cache. Drupal's hook-implementation list is cached and only rebuilds when the cache is cleared or a module is installed/uninstalled — a brand-new implementation on an already-enabled module won't be picked up until then.)
Key takeaways
- A hook implementation is a plain function named
<your_module_name>_<hook_name>()— no registration step beyond the function existing. - Every hook has three distinct parts: the documented name, your concrete implementation, and the definition that actually invokes it.
- Hook discovery is cached — always clear the cache after adding your module's first implementation of a given hook.
- You never call your own hook implementation directly; Drupal (or whichever module defined the hook) calls it for you at the right moment.
Coming up next
Knowing a hook will run is only half the story — sometimes you need to control which order multiple modules' implementations run in, or exclude one entirely. Next up: Creating hook_module_implements_alter, which hands you exactly that control.