PHPackages                             glhd/hooks - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. glhd/hooks

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

glhd/hooks
==========

0.7.0(1mo ago)1726.6k—5.7%1MITPHPPHP &gt;=8.1CI passing

Since Jan 21Pushed 1mo ago1 watchersCompare

[ Source](https://github.com/glhd/hooks)[ Packagist](https://packagist.org/packages/glhd/hooks)[ RSS](/packages/glhd-hooks/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (10)Dependencies (12)Versions (13)Used By (0)

 [ ![Build Status](https://github.com/glhd/hooks/workflows/PHPUnit/badge.svg) ](https://github.com/glhd/hooks/actions) [ ![Latest Stable Release](https://camo.githubusercontent.com/2e33547dd7d4601008676d0ee372658d080e30bbf0c3cff4514065b20447ea9c/68747470733a2f2f706f7365722e707567782e6f72672f676c68642f686f6f6b732f762f737461626c65) ](https://packagist.org/packages/glhd/hooks) [ ![MIT Licensed](https://camo.githubusercontent.com/8a9edafd04c7be5a258b2c36a33dadf316958d365edf83b587e515da1cfcec87/68747470733a2f2f706f7365722e707567782e6f72672f676c68642f686f6f6b732f6c6963656e7365) ](./LICENSE) [ ![Follow @cmorrell.com on bsky](https://camo.githubusercontent.com/8790699596340eff27762850c822f5e8e08a379e860085e46bf69359e4d2bc18/68747470733a2f2f696d672e736869656c64732e696f2f626c7565736b792f666f6c6c6f776572732f636d6f7272656c6c2e636f6d) ](https://bsky.app/profile/cmorrell.com)

Hooks
=====

[](#hooks)

Installation
------------

[](#installation)

```
composer require glhd/hooks
```

Usage
-----

[](#usage)

The hooks package provides two types of hooks: hooking into class execution, and hooking into view rendering.

### Within Classes

[](#within-classes)

To make a class "hook-able" you need to use the `Hookable` trait. In your code, you can add `callHook()`calls anywhere that you want to allow outside code to execute. For example, if you were implementing a `Session` class, you might want to allow code to hook into before the session starts, and before the session saves:

```
use Glhd\Hooks\Hookable;

class MySessionClass implements SessionHandlerInterface
{
    use Hookable;

    public function public open(string $path, string $name): bool
    {
        $this->callHook('beforeOpened', $name);
        // ...
    }

    public function write(string $id, string $data): bool
    {
        $this->callHook('beforeWritten');
        // ..
    }
}
```

Now, you can hook into these points from elsewhere in your app:

```
// Get all the available hook points
$hooks = Session::hook();

// Register your custom code to execute at those points
$hooks->beforeOpened(function($name) {
    Log::info("Starting session '$name'");
});

$hooks->beforeWritten(function() {
    Log::info('Writing session to storage');
});
```

Now, whenever `MySessionClass::open` is called, a `"Starting session ''"` message will be logged, and whenever `MySessionClass::write` is called, a `"Writing session to storage"` message will be logged.

### Hook Priority

[](#hook-priority)

You can pass an additional `int` priority to your hooks, to account for multiple hooks attached to the same point. For example:

```
$hooks->beforeOpened(fn($name) => Log::info('Registered First'), 500);
$hooks->beforeOpened(fn($name) => Log::info('Registered Second'), 100);
```

Would cause "Registered Second" to log before "Registered First". If you don't pass a priority, the default of `1000` will be used. All hooks at the same priority will be executed in the order they were registered.

### Stopping Propagation

[](#stopping-propagation)

Hooks can halt further hooks from running with a special `stopPropagation` call (just like JavaScript). All hooks receive a `Context` object as the last argument. Calling `stopPropagation` on this object will halt any future hooks from running:

```
use Glhd\Hooks\Context;

$hooks->beforeOpened(function($name) {
    Log::info('Lower-priority hook');
}, 500);

$hooks->beforeOpened(function($name, Context $context) {
    Log::info('Higher-priority hook');
    $context->stopPropagation();
}, 100);
```

In the above case, the `'Lower-priority hook'` message will never be logged, because a higher-priority hook stopped propagation before it could run.

### Passing data between your code and hooks

[](#passing-data-between-your-code-and-hooks)

There are three different ways that data gets passed in and out of hooks:

1. Passing arguments *into* hooks (one-way)
2. Returning values *from* hooks (one-way)
3. Passing data into hooks that can be mutated by hooks (two-way)

#### One-way data

[](#one-way-data)

Options 1 and 2 are relatively simple. Any positional argument that you pass to `callHook` will be forwarded to the hook as-is. In our example above, the `beforeOpened` call passed `$name` to its hooks, and our hook accepted `$name` as its first argument.

A collection of returned values from our hooks is available to the calling code. For example, if we wanted to allow hooks to add extra recipients to all email sent by our `Mailer` class, we might do something like:

```
use Glhd\Hooks\Hookable;

class Mailer
{
    use Hookable;

    protected function setRecipients() {
        $recipients = $this->callHook('preparingRecipients')
            ->filter()
            ->append($this->to);

        $this->service->setTo($recipients);
    }
}
```

```
// Always add QA to recipient list in staging
if (App::environment('staging')) {
    Mailer::hook()->preparingRecipients(fn() => 'qa@myapp.com');
}
```

It's important to note that you will **always** get a collection of results, though, even if there is only one hook attached to a call, because you never know how many hooks may be registered.

#### Two-way data

[](#two-way-data)

Sometimes you need your calling code and hooks to pass the same data in two directions. A common use-case for this is when you want your hooks to have the option to abort execution, or change some default behavior. You can do this by passing named arguments to the call, which will be added to the `Context` object that is passed as the last argument to your hook.

For example, what if we want hooks to have the ability to *prevent* mail from sending at all? We might do that with something like:

```
use Glhd\Hooks\Hookable;

class Mailer
{
    use Hookable;

    protected function send() {
        $result = $this->callHook('beforeSend', $this->message, shouldSend: true);

        if ($result->shouldSend) {
            $this->service->send();
        }
    }
}
```

```
// Never send mail to mailinator addresses
Mailer::hook()->beforeSend(function($message, $context) {
    if (str_contains($message->to, '@mailinator.com')) {
        $context->shouldSend = false;
    }
});
```

### When to use class hooks

[](#when-to-use-class-hooks)

Class hooks are mostly useful for package code that needs to be extensible without knowing **how** it will exactly be extended. The Laravel framework provides similar extension points, like [`Queue::createPayloadUsing`](https://github.com/laravel/framework/blob/443ec4438c48923c9caa9c2b409a12b84a10033f/src/Illuminate/Queue/Queue.php#L288).

In general, you should avoid using class hooks in your application code unless you are dealing with particularly complex conditional logic that really warrants this approach.

Within Views
------------

[](#within-views)

Sometimes you may want to make certain views "hook-able" as well. For example, suppose you have an ecommerce website that sends out email receipts, and you want to occasionally add promotions or other contextual content to the email message. Rather than constantly adding and removing a bunch of `@if` calls, you can use a hook:

```
{{-- emails/receipt.blade.php --}}
Thank you for shopping at…

Your receipt info…

```

Now you have two spots that you can hook into…

```
// Somewhere in a `PromotionsServiceProvider` class, perhaps…

if ($this->isInCyberMondayPromotionalPeriod()) {
    View::hook('emails.receipt', 'intro', fn() => view('emails.promotions._cyber_monday_intro'));
}

if (Auth::user()->isNewRegistrant()) {
    View::hook('emails.receipt', 'footer', fn() => view('emails.promotions._thank_you_for_first_purchase'));
}
```

The `View::hook` method accepts 4 arguments. The first is the view name that you're hooking into; the second is the name of the hook itself. The third argument can either be a view (or anything that implements the `Htmlable` contract), or a closure that returns anything that Blade can render. Finally, the fourth argument is a `priority` value—the lower the priority, the earlier it will be rendered (if there are multiple things hooking into the same spot). If you do not provide a priority, it will be set the `1000` by default.

### Explicitly Setting View Name

[](#explicitly-setting-view-name)

The `` Blade component can usually infer what view it's being rendered inside. Depending on how your views are rendered, though, you may need to explicitly pass the view name to the component. You can do that by passing an additional `view` prop:

```

```

This is a requirement that we hope to improve in a future release!

### View Hook Attributes

[](#view-hook-attributes)

It's possible to pass component attributes to your hooks, using regular Blade syntax:

```

```

Your hooks will then receive the `status` value (and any other attributes you pass):

```
View::hook('my.view', 'status', function($attributes) {
    assert($attributes['status'] === 'Demoing hooks');
});
```

If you pass the hook a Laravel view, any attributes will automatically be forwarded. This means that you can use the `$status` variable inside your view. For example, given the following views:

```
{{-- my/view.blade.php --}}

{{-- my/hook.blade.php --}}

    Your current status is '{{ $status }}'

```

The following hook code would automatically forward the value `"Demoing hooks"` as the `$status` attribute in your `my.hook` view:

```
View::hook('my.view', 'status', view('my.hook'));
```

###  Health Score

49

—

FairBetter than 95% of packages

Maintenance89

Actively maintained with recent releases

Popularity37

Limited adoption so far

Community11

Small or concentrated contributor base

Maturity47

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 96.7% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~72 days

Recently: every ~185 days

Total

12

Last Release

56d ago

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/21592?v=4)[Chris Morrell](/maintainers/inxilpro)[@inxilpro](https://github.com/inxilpro)

---

Top Contributors

[![inxilpro](https://avatars.githubusercontent.com/u/21592?v=4)](https://github.com/inxilpro "inxilpro (58 commits)")[![actions-user](https://avatars.githubusercontent.com/u/65916846?v=4)](https://github.com/actions-user "actions-user (1 commits)")[![onairmarc](https://avatars.githubusercontent.com/u/50760632?v=4)](https://github.com/onairmarc "onairmarc (1 commits)")

---

Tags

laravel

###  Code Quality

TestsPHPUnit

Code StylePHP CS Fixer

### Embed Badge

![Health badge](/badges/glhd-hooks/health.svg)

```
[![Health](https://phpackages.com/badges/glhd-hooks/health.svg)](https://phpackages.com/packages/glhd-hooks)
```

###  Alternatives

[spatie/laravel-livewire-wizard

Build wizards using Livewire

4061.0M4](/packages/spatie-laravel-livewire-wizard)[nativephp/mobile

NativePHP for Mobile

82724.0k43](/packages/nativephp-mobile)[tonysm/importmap-laravel

Use ESM with importmap to manage modern JavaScript in Laravel without transpiling or bundling.

148399.8k1](/packages/tonysm-importmap-laravel)[bezhansalleh/filament-google-analytics

Google Analytics integration for FilamentPHP

205144.8k5](/packages/bezhansalleh-filament-google-analytics)[laracraft-tech/laravel-useful-additions

A collection of useful Laravel additions!

58109.4k](/packages/laracraft-tech-laravel-useful-additions)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
