Rethinking Do_action(): Events as Objects, Hooks as Class Names

🔗 **Source:** [Rethinking Do_action(): Events as Objects, Hooks as Class Names](https://developer.wordpress.org/news/2026/08/rethinking-do_action-events-as-objects-hooks-as-class-names/)
> [!note]+ Document Notes
> tags: [wordpress-hooks, php-architecture, event-driven-design, plugin-development, software-design]
> author: "[MISSING]"
> source: WordPress Developer Blog
> reference: https://developer.wordpress.org/news/2026/08/rethinking-do_action-events-as-objects-hooks-as-class-names/
> date created: 2026-08-04
>
> > [!info] Generated analysis — not the author's words. Sections below are machine-written
> > from the captured source. Only text inside > [!quote] blocks is verbatim from the author.
> > Do not quote or attribute any other line to the named author.
>
> # 🏷️ Title: Rethinking do_action(): Events as objects, hooks as class names
>
<!-- deep-read:sections start -->
## 🧭 Table of Contents
- [📝 Summary](#-summary)
- [📖 Main Content](#-main-content)
- [🗒️ Notes](#️-notes)
- [📚 References](#-references)
- [🗣️ Paraphrase](#️-paraphrase)
- [📰 Newsletter](#-newsletter)
- [✅ Action Items](#-action-items)
- [🧪 Hypotheses](#-hypotheses)
- [📊 Methodology](#-methodology)
- [❓ Questions](#-questions)
- [🔮 Future Research](#-future-research)
- [💡 Implications](#-implications)
- [🧠 Learning Styles Assessment](#-learning-styles-assessment)
- [📊 Accessibility Assessment](#-accessibility-assessment)
- [🌍 DEIB Assessment](#-deib-assessment)
- [✅ Logical Validity](#-logical-validity)
- [🛡️ DARVO Analysis](#-darvo-analysis)
- [🎯 Conclusion](#-conclusion)
- [🔄 Refactor Appendix](#-refactor-appendix--rethinking-do_action-events-as-objects-hooks-as-class-names)
### 🔗 References
- [Source article](https://developer.wordpress.org/news/2026/08/rethinking-do_action-events-as-objects-hooks-as-class-names/)
- [WordPress do_action() reference](https://developer.wordpress.org/reference/functions/do_action/)
- [WordPress actions handbook](https://developer.wordpress.org/plugins/hooks/actions/)
- [WordPress custom hooks handbook](https://developer.wordpress.org/plugins/hooks/custom-hooks/)
- [WordPress Hooks API](https://developer.wordpress.org/apis/hooks/)
- [PSR-14: Event Dispatcher](https://www.php-fig.org/psr/psr-14/)
- [PHP mixed type](https://www.php.net/manual/en/language.types.mixed.php)
- [PHP variable-length argument lists](https://www.php.net/manual/en/functions.arguments.php#functions.variable-arg-list)
## 📝 Summary
- **What happened:** The article proposes a convention for custom WordPress hooks: pass one typed event object to do_action() and use either the event class name or a stable namespaced string as the hook tag.
- **Why it matters:** This replaces positional, untyped argument lists with a structured payload that editors and static-analysis tools can understand. It also reduces hook-name collisions.
- **Filter-like behavior:** An event may expose immutable context and a deliberately mutable result property. Listeners can change that property, and the dispatcher reads it after the action runs.
- **Compatibility choice:** A class-name hook follows namespace and class refactors. A fixed string remains stable across refactors but gives up automatic symbol renaming.
- **Stakeholders:** Plugin and theme authors, framework maintainers, code reviewers, static-analysis users, documentation teams, and developers maintaining public extension points.
- **Risks:** Shared mutable state can make listener order significant. Class renames can silently break third-party listeners. The pattern resembles PSR-14 but lacks its formal interfaces, propagation control, listener providers, subscriber model, and replaceable dispatcher.
- **Bottom line:** Event objects are a practical payload convention for many new custom hooks. They are not a drop-in replacement for every filter or a complete event-dispatch system.
- **Evidence boundary:** The source demonstrates a design pattern. It does not supply performance measurements, migration data, conflict testing, or evidence that most WordPress hooks benefit from the pattern.
## 📖 Main Content
The article begins with familiar problems in custom WordPress hooks. Multiple loose arguments are positional, hook names occupy one global string namespace, callback payloads are untyped, and filter callbacks can break a chain by forgetting to return the value.
The proposed change leaves WordPress’s Plugin API intact. Code still calls do_action() and add_action(). The difference is the payload: one object represents the event, its context, and any result listeners may alter.
A fully qualified class name can serve as the hook tag because do_action() accepts a string hook name. This gives each event a naturally namespaced identifier. The cost is coupling the public hook name to the class’s namespace and name. Moving that class becomes a backward-compatibility event unless the old hook is deprecated and bridged.
A fixed namespaced string offers the opposite tradeoff. It survives class renames and lets listeners subscribe without importing the event class just to identify the hook. The author’s rule is therefore about stability: use ::class when the hook may follow the class, and a fixed string when the public identifier must remain unchanged.
The event example separates readonly context from mutable policy. A member ID and plan describe what happened. A send-welcome-email property carries a decision that listeners may turn off. The registrar dispatches the event and reads the final property afterward.
This removes the filter return-value trap, but it introduces shared mutable state. When several listeners can modify one result, hook priority and conflict semantics matter. A Boolean that only permits true-to-false movement is easier to reason about than a value listeners may repeatedly overwrite in either direction.
The article distinguishes observer listeners from mutator listeners. Observers read the event and perform side effects. Mutators alter the event so the dispatching code changes its next action. Both receive a typed object, improving completion, static analysis, and discoverability.
The author connects this shape to event-driven software and PSR-14: an event carries information, a listener reacts, and a dispatcher delivers the event. The comparison is conceptual, not contractual. Native WordPress hooks do not implement the PSR interfaces or all the surrounding capabilities.
The closing recommendation is proportional: start with an object payload when it solves the actual hook’s problems. Move to a fuller event system when the project needs propagation control, dynamic listener providers, subscriber registration, or a replaceable dispatcher for testing.
## 🗒️ Notes
- The strongest contribution is the distinction between hook mechanics and payload design. WordPress’s dispatcher can already carry objects.
- A typed object documents the payload in one inspectable class instead of distributing meaning across the firing call, callback signatures, and accepted-argument counts.
- Hook names remain global even when they look namespaced. A fully qualified class name reduces accidental collision but does not create access control.
- Class-name hooks make the PHP symbol part of the public extension API. Automated refactoring within one repository does not update third-party plugins.
- Stable strings preserve the hook contract independently of implementation structure.
- Mutable event properties turn an action into a request-and-response mechanism without apply_filters().
- Mutation should have explicit invariants. A private property with a validating method may be safer than an unrestricted public property.
- Multiple mutators need documented priority and conflict rules. Otherwise the last listener to run wins implicitly.
- Observer and mutator listeners have different side-effect expectations. Naming or documentation should distinguish them.
- Passing one object reduces accepted-argument bookkeeping, but listeners must still expect exactly one payload.
- Event classes can become a navigable catalog of extension points when each event has its own file.
- The PSR-14 analogy helps PHP developers recognize the pattern, while the source correctly says WordPress hooks do not provide the complete standard.
- Missing from the source: exception policy, conflicting mutation policy, deprecation examples, inheritance behavior, interface-based subscription, benchmarks, and migration strategy for an existing public hook.
- The claim that the convention covers most custom hooks is plausible experience-based guidance, not a measured ecosystem result.
- New hooks are the safest place to apply the convention. Converting an existing public hook requires compatibility planning and may need dual dispatch during deprecation.
## 📚 References
- [Rethinking do_action(): Events as objects, hooks as class names](https://developer.wordpress.org/news/2026/08/rethinking-do_action-events-as-objects-hooks-as-class-names/) — primary source.
- [do_action()](https://developer.wordpress.org/reference/functions/do_action/) — authoritative WordPress function reference.
- [Actions](https://developer.wordpress.org/plugins/hooks/actions/) — official Plugin Handbook explanation.
- [Custom Hooks](https://developer.wordpress.org/plugins/hooks/custom-hooks/) — official guidance for plugin-defined extension points.
- [Hooks API](https://developer.wordpress.org/apis/hooks/) — WordPress Hooks API overview and historical context.
- [PSR-14](https://www.php-fig.org/psr/psr-14/) — formal PHP event-dispatcher specification used for comparison.
- [PHP mixed](https://www.php.net/manual/en/language.types.mixed.php) and [variable-length argument lists](https://www.php.net/manual/en/functions.arguments.php#functions.variable-arg-list) — language references for the do_action() signature discussion.
## 🗣️ Paraphrase
1. The author explored PSR-14 and discovered that much of its everyday value can be approximated through a convention on top of WordPress hooks.
2. WordPress’s hooks remain useful, but common payload practices create friction that developers have learned to tolerate.
3. Loose argument lists force subscribers to remember position, count, and types.
4. Conventional prefixed hook strings reduce collisions by convention, not by language-level identity.
5. Filter listeners must return a value, so one missing return can damage the chain.
6. These weaknesses concern how data is represented, not whether WordPress can dispatch extensibility events.
7. One event object can carry context, behavior, validation, and a result that listeners are allowed to change.
8. The object’s class name can identify the hook, creating a unique tag from the PHP namespace.
9. Class-name tags are easy to refactor internally but unstable as public contracts; fixed strings are stable but separate from the class symbol.
10. Readonly properties tell listeners what they may only inspect. Mutable properties define the event’s supported feedback channel.
11. Observer listeners react without changing the event. Mutator listeners change a documented part of it.
12. Typed callback signatures improve editor completion and static analysis.
13. WordPress hooks already resemble the event-listener-dispatcher shape used elsewhere in PHP.
14. The resemblance does not supply the formal contracts or advanced dispatch features of PSR-14.
15. Projects should add a fuller event system when their needs exceed the convention, not before.
## 📰 Newsletter
WordPress’s old hook API still has a few new tricks hiding in plain sight. The Developer Blog proposes sending a typed event object through do_action() instead of passing a small parade of positional arguments. That gives listeners autocomplete, a structured payload, and a naturally namespaced hook tag without adding a framework.
The interesting wrinkle is filter-like mutation: readonly properties carry context, while one intentional mutable property carries the answer back to the dispatcher. It is tidy, but public hook contracts still need care. A ::class hook changes when the class moves, and multiple mutators can make priority part of the business logic.
Read the [article](https://developer.wordpress.org/news/2026/08/rethinking-do_action-events-as-objects-hooks-as-class-names/), compare it with the [do_action() reference](https://developer.wordpress.org/reference/functions/do_action/), and keep [PSR-14](https://www.php-fig.org/psr/psr-14/) nearby for the point where a convention stops being enough.
## ✅ Action Items
#### DevRel
- Build a short before-and-after demo comparing positional hook arguments with an event object.
- Teach the compatibility tradeoff between class-name hooks and fixed public strings.
- Collect developer feedback on mutation, testing, and discoverability.
#### OSPO
- Add public-hook compatibility expectations to extension API guidance.
- Treat event classes and hook strings as supported interfaces when external plugins subscribe.
- Include deprecation procedures in maintainer training.
#### Community
- Invite plugin maintainers to test the pattern in new, noncritical extension points.
- Gather counterexamples where apply_filters() communicates intent more clearly.
- Share migration experiences before recommending ecosystem-wide conventions.
#### WordPress
#### Core
- No immediate Core change is required; evaluate the convention for new internal extension points separately from existing hooks.
#### Core Performance
- Benchmark object allocation and dispatch only if the pattern is proposed for high-frequency hooks.
#### Core AI
- Use typed event payloads as a possible structured boundary for agent-triggered operations, while keeping authorization outside the event convention.
#### Core Program Team
- Track cross-team guidance only if several component teams adopt the pattern.
#### Mobile
- Not directly applicable because the article concerns server-side PHP hooks.
#### Playground
- Publish a runnable plugin example that demonstrates observers, mutators, priorities, and conflicting listeners.
#### Community
- Add the pattern to advanced contributor workshops only after compatibility guidance is clear.
#### Training
- Create an exercise that asks learners to choose between ::class and a stable string.
#### Marketing
- Keep messaging technical and avoid presenting the convention as a replacement for PSR-14.
#### Support
- Document debugging steps for silent class-name hook changes and listener-priority conflicts.
#### Design
- Not directly applicable to interface design; the relevant design work is API ergonomics.
#### Documentation
- Add an example covering deprecation, dual dispatch, mutable-property invariants, and exception behavior.
#### Photos
- Not applicable to this source.
#### TV
- Caption and index any workshop recording by hook names, event objects, and PSR-14.
#### Polyglots
- Add glossary entries for event, listener, dispatcher, payload, readonly, and mutation.
#### Accessibility
- Ensure code samples have meaningful surrounding explanations and do not depend on color alone.
#### Meta
- Not directly applicable unless developer.wordpress.org adds interactive examples.
#### CLI
- Consider scaffolding event classes only if a stable convention gains adoption.
#### Hosting
- No direct operational change; hosts may care if high-frequency plugin hooks introduce measurable overhead.
#### Tide
- Avoid scoring the pattern as inherently good or bad; static checks could instead detect undocumented public hook changes.
#### Openverse
- Not applicable to this source.
#### Themes
- Test the convention for new theme-framework extension points without changing established hook contracts.
#### Plugins
- Prefer new hooks for experiments, publish the event class contract, and document which properties listeners may change.
#### The WPOCC
- Consider a maintainer clinic on stable extension APIs and backward-compatible hook evolution.
### FAIR
- Preserve technical independence by documenting public hook identifiers and payload schemas outside any one implementation.
- Use explicit compatibility policies so governance remains inspectable and portable.
## 🧪 Hypotheses
1. Event-object hooks reduce callback signature errors compared with hooks that pass three or more positional arguments.
2. Static analysis finds more payload-type defects when listeners type-hint one event class.
3. Class-name hook tags create more accidental backward-compatibility breaks during namespace refactors than stable string tags.
4. Mutable event results reduce forgotten-return defects but increase listener-order defects when several callbacks may write the same property.
5. Developers understand observer-only events faster than mixed observer-and-mutator events unless mutability is named and documented.
6. Object allocation has no material effect on ordinary plugin hooks but may matter on hooks fired thousands of times per request.
### 📊 Methodology
- Build paired plugin fixtures implementing the same workflows with positional arguments, filters, event objects, and a PSR-14 dispatcher.
- Give developers maintenance tasks and measure completion time, callback-signature mistakes, IDE assistance, and comprehension.
- Run PHPStan or Psalm across each fixture and compare actionable findings.
- Add controlled class and namespace refactors, then test first-party and external listeners for breakage.
- Register multiple mutators at different priorities and measure contradictory-result frequency.
- Benchmark cold and warm requests across low-, medium-, and high-frequency dispatch loops.
- Publish code, environment details, and raw results so the conclusions can be reproduced.
### ❓ Questions
- Should a mutable event expose public properties, guarded methods, or a dedicated decision object?
- How should two listeners that disagree about a Boolean result resolve the conflict?
- Does using an action for a decision obscure the conventional semantic distinction between actions and filters?
- Should event classes be final, implement marker interfaces, or support inheritance?
- How can a plugin deprecate a class-name hook without firing business logic twice?
- When does testability justify introducing a replaceable dispatcher?
- Would WordPress coding standards benefit from guidance for event-object hooks?
### 🔮 Future Research
- Survey established WordPress plugins already passing objects through hooks.
- Compare class-name hooks with stable namespaced strings in long-lived plugin APIs.
- Test interface- or parent-class listener expectations against WordPress’s exact string matching.
- Develop a conflict policy for events with multiple mutators.
- Document exception propagation and failure isolation across listeners.
- Measure whether event files improve extension-point discovery in large repositories.
- Study compatibility bridges for existing hooks that cannot change their payload.
### 💡 Implications
- **Practice:** New custom hooks can become easier to inspect and type-check without replacing the Plugin API.
- **Policy:** Hook names, event classes, and mutable properties need the same compatibility discipline as public functions.
- **Security:** Typed payloads improve clarity but do not provide authorization, validation, listener isolation, or trustworthy provenance.
- **Education:** The pattern offers a bridge between WordPress hooks and general event-driven design.
- **Testing:** A global do_action() dispatcher remains harder to replace than a PSR-14 dispatcher.
- **Funding:** Maintainer time is better spent on examples, compatibility tests, and migration guidance than on adopting a framework by default.
### 🧠 Learning Styles Assessment
- **Reading-level estimate:** Approximately grade 11–13 by qualitative Flesch-Kincaid inspection; no formula score was computed from the captured text.
#### 👀 Visual learners
- **Present:** Headings, lists, and code-oriented examples structure the argument.
- **Improve:** Add a diagram showing dispatcher, event object, observer, mutator, and returned state.
#### 🔊 Aural learners
- **Present:** No audio version appears in the Reader capture.
- **Improve:** Provide a narrated walkthrough that explains the compatibility tradeoff aloud.
#### 🗣️ Verbal learners
- **Present:** Definitions, analogies, repetition, and a rule of thumb support verbal processing.
- **Improve:** Add a glossary contrasting action, filter, event, payload, and dispatcher.
#### 🧩 Kinesthetic learners
- **Present:** The article uses a concrete member-registration example.
- **Improve:** Supply a downloadable plugin or Playground blueprint with small refactoring tasks.
#### 🧠 Logical learners
- **Present:** The article proceeds from identified defects to one convention and then states its limits.
- **Improve:** Add a decision table for loose arguments, event objects, fixed strings, class names, and PSR-14.
#### 🧑🤝🧑 Social learners
- **Present:** Not applicable to the captured article; no group exercise is supplied.
- **Improve:** Offer peer-review prompts about public API stability and competing mutators.
#### 🧘♀️ Solitary learners
- **Present:** The self-contained example supports independent study.
- **Improve:** Add a checklist and self-test questions.
#### 🌿 Naturalistic learners
- **Present:** Not applicable; the topic has no meaningful environmental analogy.
- **Improve:** Use lifecycle language only where it clarifies API maintenance rather than forcing a metaphor.
### 📊 Accessibility Assessment
#### Screen reader semantics
- **Present:** Reader captured headings, paragraphs, lists, links, emphasis, and inline code.
- **Gap:** The rendered page and its code-block semantics were not independently tested.
- **WCAG:** Review 1.3.1 Info and Relationships and 2.4.6 Headings and Labels.
#### Keyboard navigation
- **Present:** Standard links appear in the capture.
- **Gap:** Focus order, visible focus, skip links, and keyboard access to page controls remain unverified.
- **WCAG:** Test 2.1.1 Keyboard, 2.4.3 Focus Order, and 2.4.7 Focus Visible.
#### Contrast and readability
- **Present:** Short paragraphs and explicit sectioning aid scanning.
- **Gap:** Colors, code contrast, line height, and link distinction were not captured.
- **WCAG:** Test 1.4.3 Contrast Minimum, 1.4.11 Non-text Contrast, and 1.4.12 Text Spacing.
#### Text resizing and reflow
- **Present:** Semantic text should support reflow, but this is an inference.
- **Gap:** No 200% zoom or 320 CSS-pixel test was run.
- **WCAG:** Test 1.4.4 Resize Text and 1.4.10 Reflow.
#### Media and captions
- **Present:** No time-based media appears in the captured article.
- **Gap:** Not applicable unless the live page embeds media outside the article body.
#### Forms and interactions
- **Present:** No forms appear in the article body.
- **Gap:** Site navigation, search, and other page-level controls were not tested.
- **WCAG:** If present, test 3.3.2 Labels or Instructions and 4.1.2 Name, Role, Value.
#### Standards reference
- The source content is structurally readable in Reader. A WCAG 2.2 AA conclusion requires testing the rendered developer.wordpress.org page with keyboard, zoom, contrast tools, and a screen reader.
### 🌍 DEIB Assessment
#### Cultural breadth
- **Present:** The technique applies across the WordPress plugin ecosystem.
- **Improve:** Include examples from multilingual, agency, enterprise, and small-maintainer codebases.
#### Inclusive language
- **Present:** The article uses approachable, gender-neutral language.
- **Improve:** Define idioms such as papercut, prefix roulette, and painting yourself into a corner for global readers.
#### Neurodiversity support
- **Present:** One central convention, repeated summaries, headings, and lists support predictable reading.
- **Improve:** Add a decision table and clearly label which properties may be mutated.
#### Representation
- **Present:** The capture does not identify participants or case-study maintainers.
- **Improve:** Include feedback from developers with different experience levels and project constraints.
#### Psychological safety
- **Present:** The tone treats current hook practices as understandable rather than blaming developers.
- **Improve:** Make clear that existing hooks need not be rewritten and that compatibility comes before stylistic preference.
### ✅ Logical Validity
#### Strong reasoning
- **Problem decomposition:** The article locates positional arguments, naming collisions, missing types, and return discipline in payload design rather than the dispatcher.
- **Mechanism fit:** do_action() already accepts a string plus mixed variadic arguments, so one object is compatible with the documented call shape.
- **Explicit tradeoff:** The class-name versus stable-string discussion identifies a real backward-compatibility choice.
- **Boundary setting:** The source names several PSR-14 capabilities that WordPress hooks do not supply.
- **Proportional recommendation:** It recommends a fuller event system only after the simpler convention reaches a concrete limit.
#### Fallacies
- **Hasty-generalization risk:** The statement that this covers most custom hooks is not backed by a sample or ecosystem study. Fix: scope it as the author’s experience or publish a representative hook survey.
- **Underexamined tradeoff:** Replacing filter return values with mutation removes one failure mode but may add order-dependent shared-state failures. Fix: document conflict semantics and compare both defect classes.
- **No false equivalence finding:** The author explicitly says the WordPress pattern is not PSR-14, so the conceptual comparison is bounded rather than presented as formal equivalence.
- **No forced fallacy label:** The tutorial is an architectural argument, not evidence of deception or categorical invalidity.
#### Tone of voice
- The tone is conversational, confident, instructional, and mildly playful.
- Phrases such as “papercut” and “prefix roulette” make familiar developer friction memorable.
- The closing advice is enthusiastic but includes enough boundary language to avoid presenting the convention as a universal framework replacement.
### 🛡️ DARVO Analysis
- **Deny:** Not applicable. The article does not respond to an accusation or deny harm.
- **Attack:** Not applicable. It critiques a coding convention without attacking a complainant.
- **Reverse victim/offender:** Not applicable. No victim-offender narrative appears.
- **Emotional cues:** Enthusiasm and humor promote the pattern, but they do not constitute DARVO.
- **Conclusion:** DARVO is not an appropriate framework for this technical tutorial.
## 🎯 Conclusion
Passing one event object through do_action() is a credible default for many new custom hooks. It structures the payload, improves tooling, and can make extension points easier to discover. The class name is convenient but becomes part of the public compatibility contract; a stable string is safer when the identifier must survive refactors.
Mutable event state can replace some filter return values, but teams should document invariants, priority behavior, and conflicts. Projects needing propagation control, dynamic listener selection, subscriber registration, or a replaceable dispatcher should evaluate a formal event system instead.
The practical next step is a small controlled plugin example with observer and mutator listeners, backward-compatibility tests, and a decision table for ::class versus stable strings.
## 🔄 Refactor Appendix — Rethinking do_action(): Events as objects, hooks as class names
> Density: 4 Literature atoms and 2 Permanent candidates.
>
> Reading stage: analytical.
>
> Konik tags used: #xref and #articleseed.
### 📚 Literature Atoms
### [H1] Hook friction often begins in payload design
> [!quote] ID:rw_01kz76hncgbv8tqg9dv5ksy413
> None of these are *hook* problems. They’re *payload* problems
**Context & use**:
- Located after the source lists positional arguments, global names, missing types, and filter return discipline.
- Matters because WordPress’s dispatcher can remain unchanged while payload conventions improve.
**Tags**: #xref
**Highlight**: [view highlight](https://read.readwise.io/read/01kz76hncgbv8tqg9dv5ksy413)
<!-- atomicity: 1 idea ✓ -->
### [H2] Event objects replace positional hook arguments
> [!quote] ID:rw_01kz76hmgv9mm6fwtmd04bx0yb
> Instead of passing loose arguments, pass one object that describes what happened.
**Context & use**:
- Located under “The one-line idea.”
- Matters because one typed payload centralizes field names, types, mutability, and helper behavior.
**Tags**: #xref #articleseed
**Highlight**: [view highlight](https://read.readwise.io/read/01kz76hmgv9mm6fwtmd04bx0yb)
<!-- atomicity: 1 idea ✓ -->
### [H3] Class-name hooks create compatibility obligations
> [!quote] ID:rw_01kz76hq0btqnffb67zdtj5fx1
> The catch is exactly that: the tag is the class path
**Context & use**:
- Located in the class-name versus fixed-string comparison.
- Matters because a namespace refactor can silently change a public hook identifier.
**Tags**: #xref
**Highlight**: [view highlight](https://read.readwise.io/read/01kz76hq0btqnffb67zdtj5fx1)
<!-- atomicity: 1 idea ✓ -->
### [H4] WordPress hooks resemble PSR-14 conceptually
> [!quote] ID:rw_01kz76hqt91r9r6m6ynbrjjmtr
> PSR-14 is a formal contract with parts that don’t exist in WordPress.
**Context & use**:
- Located before the source lists the formal event-system features missing from native hooks.
- Matters because the analogy teaches the model without claiming standards compliance.
**Tags**: #xref
**Highlight**: [view highlight](https://read.readwise.io/read/01kz76hqt91r9r6m6ynbrjjmtr)
<!-- atomicity: 1 idea ✓ -->
### 🧠 Permanent Candidates
#### [P1] Public hook names outlive implementation structure
**Claim:** A public extension identifier should remain stable when its implementation moves.
**Evidence pointer:** [[#H3]]
**Scope & conditions:**
- Holds when third-party code subscribes outside the maintainer’s repository.
- Breaks when the hook is private, experimental, or all consumers are changed atomically.
**Links:**
- [[Backward compatibility]] — because extends the stability requirement.
- [[Semantic versioning]] — because applies-in public plugin releases.
- [[WordPress hook deprecation]] — because evidence-for compatibility bridging.
<!-- atomicity: 1 idea ✓ -->
#### [P2] Structured payloads improve extension-point legibility
**Claim:** A typed payload makes an extension point easier to inspect.
**Evidence pointer:** [[#H1]]
**Scope & conditions:**
- Holds when the event class and its mutability rules are documented.
- Breaks when the object becomes an unbounded bag of mutable state.
**Links:**
- [[API ergonomics]] — because extends inspectability.
- [[Positional arguments]] — because contrasts on discoverability.
- [[Static analysis]] — because applies-in typed callback checking.
<!-- atomicity: 1 idea ✓ -->
### ✏️ Fleeting Queue
- [F1] Compare event mutation with filter return values under conflicting listener priorities — controlled plugin fixture #xref
- [F2] Draft a compatibility example for renaming a class-name hook — WordPress developer documentation #articleseed
### → Things 3
- [ ] Build a Playground fixture with observer and mutator listeners — WordPress hook research #things
- [ ] Test dual-dispatch deprecation without duplicate side effects — plugin compatibility #things
Processed: Rethinking do_action(): Events as objects, hooks as class names
- Literature atoms: 4 / 5 cap
- Permanent candidates: 2 / 3 cap
- Fleeting items: 2
- Things 3 actions: 2 / 2 cap
- Konik tags used: #xref, #articleseed
- Triage callouts populated: Cross Reference, Article Ideas
- Quality gates: verbatim quote check ✓; atomicity markers ✓; annotated links ✓; no invented highlight IDs ✓
- Override conditions triggered: second Permanent candidate retained because payload legibility applies beyond WordPress
<!-- deep-read:sections end -->
📄 **Full text:** [[Full Document Contents/Articles/Rethinking Do_action() Events as Objects, Hooks as Class Names.md|Rethinking Do_action(): Events as Objects, Hooks as Class Names by developer.wordpress.org]]
## Highlights
- None of these are *hook* problems. They’re *payload* problems, and they show up whether the hook is an action or a filter. A hook can carry a single object just as easily as it can carry multiple loose arguments. And an object fixes all of it at once. ([View Highlight](https://read.readwise.io/read/01kz76hncgbv8tqg9dv5ksy413))
- Note: The article’s core diagnosis: the rough edges come from unstructured hook payloads, not from the hook mechanism itself.
- Tags: atom/literature/h1, event-driven-design, wordpress-hooks
- Instead of passing loose arguments, pass one object that describes what happened. And instead of inventing a string name, use the object’s own class name as the hook: ([View Highlight](https://read.readwise.io/read/01kz76hmgv9mm6fwtmd04bx0yb))
- Note: The proposed convention combines a typed event object with either its class name or a stable custom string as the hook tag.
- Tags: atom/literature/h2, php-architecture, software-design
- It’s zero ceremony, it’s namespaced for free, and when you rename or move the class, your IDE renames the hook right along with it. The catch is exactly that: the tag is the class path, so the day you move `MemberRegistered` into another namespace or rename it, the hook name changes too. Then, any `add_action()` still pointed at the old name silently stops matching. Of course, solid deprecation procedures in your code should mitigate issues like this, but that’s a topic for another day. ([View Highlight](https://read.readwise.io/read/01kz76hq0btqnffb67zdtj5fx1))
- Note: Class-name hooks reduce collisions but make namespace and class renames part of the hook’s compatibility contract.
- Tags: atom/literature/h3, backward-compatibility, plugin-development
- * `$userId` and `$plan` are `readonly`. This is the context a listener reads to make a decision, not something it should rewrite.
* `$sendWelcomeEmail` is mutable on purpose: it’s the “answer” the plugin reads back after dispatch, and flipping it off is how a listener says “skip the welcome email for this one.” That’s filter-like behavior coming out of a plain action because the property is writable. ([View Highlight](https://read.readwise.io/read/01kz76hp5q81g71n1996c1rjmj))
- Note: The event separates immutable context from the deliberately mutable decision that listeners may change.
- Tags: state-mutation, event-objects, atom/zettel/z1
- I don’t want to oversell the resemblance. PSR-14 is a formal contract with parts that don’t exist in WordPress. I’ll get to what’s missing in a moment. But the core shape (event, listener, dispatcher) is right there in `do_action()`, and it always has been. ([View Highlight](https://read.readwise.io/read/01kz76hqt91r9r6m6ynbrjjmtr))
- Note: The source correctly limits the analogy: WordPress hooks share the event-listener-dispatcher shape but not PSR-14’s formal contracts.
- Tags: psr-14, atom/literature/h4, wordpress-hooks
## Links
- [[3. Resources/Permanent Notes/Public Event Names Outlive Their Implementation Structure]] — evidence-for: class-shaped identifiers risk binding extensions to replaceable internals.
- [[3. Resources/Permanent Notes/Type Compatibility Can Hide Runtime Removal]] — applies-in: a typed event surface still needs runtime compatibility.