PHPackages                             teofanis/hook-press - 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. [Caching](/categories/caching)
4. /
5. teofanis/hook-press

ActiveLibrary[Caching](/categories/caching)

teofanis/hook-press
===================

HookPress is a Laravel package that uses Composer hooks to automatically discover, filter, and cache classes, traits, and interfaces in your application

0.1.6(8mo ago)61.1k↑33.3%MITPHPPHP ^8.0CI passing

Since Aug 31Pushed 2mo agoCompare

[ Source](https://github.com/teofanis/hook-press)[ Packagist](https://packagist.org/packages/teofanis/hook-press)[ Docs](https://github.com/teofanis/hook-press)[ Fund](https://www.buymeacoffee.com/teofanis)[ Fund](https://www.paypal.com/paypalme/teofanis)[ RSS](/packages/teofanis-hook-press/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (7)Dependencies (19)Versions (10)Used By (0)

 [![HookPress logo](assets/logo.png)](assets/logo.png)

HookPress is a Laravel package that uses Composer hooks to automatically discover, filter, and cache classes, traits, and interfaces in your application
========================================================================================================================================================

[](#hookpress-is-a-laravel-package-that-uses-composer-hooks-to-automatically-discover-filter-and-cache-classes-traits-and-interfaces-in-your-application)

 [ ![Latest Version on Packagist](https://camo.githubusercontent.com/36e20fc6862a071311694bb3bf909f37a45ed2ee66769d166d533f4cefdda355/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f74656f66616e69732f686f6f6b2d70726573732e737667) ](https://packagist.org/packages/teofanis/hook-press) [ ![GitHub Tests Action Status](https://camo.githubusercontent.com/7e2600b1f0cb8db37f5ffe09e411ecf135a5d2de101f583b453f67cfeec30a62/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f74656f66616e69732f686f6f6b2d70726573732f72756e2d74657374732e796d6c3f6272616e63683d6d61696e266c6162656c3d746573747326) ](https://github.com/teofanis/hook-press/actions/workflows/run-tests.yml) [ ![GitHub Code Style Action Status](https://camo.githubusercontent.com/6e3f5ea886d65f51cee521015839a6e6d114fa6feb8a42cda09cf6dc6a6101a3/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f74656f66616e69732f686f6f6b2d70726573732f6669782d7068702d636f64652d7374796c652d6973737565732e796d6c3f6272616e63683d6d61696e266c6162656c3d636f64652532307374796c6526) ](https://github.com/teofanis/hook-press/actions/workflows/fix-php-code-style-issues.yml) [ ![Total Downloads](https://camo.githubusercontent.com/1bdbb931d389c56c22784964bf2b137b7955be6efaf65ab824a3412cf99206ef/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f74656f66616e69732f686f6f6b2d70726573732e737667) ](https://packagist.org/packages/teofanis/hook-press)[ ![Licence](https://camo.githubusercontent.com/6c87c243593e5981027c60998f745a4a82e9ef2a25b63ffd4bc4bfac7ebc7847/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f6c2f74656f66616e69732f686f6f6b2d70726573732e737667) ](https://github.com/teofanis/hook-press/blob/main/LICENSE.md)

HookPress builds a static class map during Composer install/update so your app can look up “discoverable” classes in O(1) time at runtime. No boot-time reflection, no recursive directory scans, no config arrays to maintain.

Why?
----

[](#why)

I’ve used this in production for a while to auto-discover things like payment methods and populate lists/registries without hand-wiring every class. It’s been handy for pluggable components (drivers, actions, jobs) where “put the class in the right namespace” should be enough.

Typical uses
------------

[](#typical-uses)

- Collect all implementations of an interface (e.g. PayoutMethod) to build a registry or a UI dropdown.
- Group classes by trait (e.g. everything using Searchable) for indexing or batch operations.
- Discover classes marked by attributes (e.g. #\[Discoverable\]).
- Find invokables or classes with particular methods/properties for handler pipelines.

Requirements
------------

[](#requirements)

- PHP 8.0+
- Laravel 9+

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

[](#installation)

```
composer require teofanis/hook-press
```

Publish the config file with:

```
php artisan vendor:publish --tag="hook-press-config"
```

Wire HookPress to run when composer installs/updates

```
{
  "scripts": {
    "post-install-cmd": [
      "HookPress\\Hooks\\HookRunner::postInstall"
    ],
    "post-update-cmd": [
      "HookPress\\Hooks\\HookRunner::postUpdate"
    ]
  }
}
```

> ***NOTE:*** You can use artisan directly if you prefer

```
"post-install-cmd": ["@php artisan hook-press:build --no-ansi --no-interaction"],
"post-update-cmd":  ["@php artisan hook-press:build --no-ansi --no-interaction"]
```

This is the contents of the published config file:

```
return [
     // Namespaces to consider as your application roots
    'roots' => [
        'App\\',
    ],

    // Optional: trait grouping
    'traits' => [
        'enabled'    => true,
        'namespaces' => ['App\\Traits\\'],
        'group_key'  => 'traits', // where the trait map will be stored
    ],

    // Define your discovery “maps”
    'maps' => [
        'payout_methods' => [
            'namespaces' => ['App\\Classes\\PayoutMethods\\'],
            'conditions' => [
                'isInstantiable',
                'implementsInterface' => 'App\\Interfaces\\PayoutMethod',
            ],
        ],
    ],

    // Exclusions (exact class, namespace prefix, or regex)
    'exclusions' => [
        'classes'    => [],
        'namespaces' => [],
        'regex'      => [],
    ],

    // Where the computed map is stored
    'store' => [
        'driver' => 'file', // 'file' or 'cache'
        'file'   => ['path'  => 'bootstrap/cache/hook-press.php'],
        'cache'  => ['store' => null, 'key' => 'hookpress:map', 'ttl' => null],
    ],

    // Where HookPress reads the Composer classmap from (leave as default in apps)
    'composer' => [
        'classmap_path'   => 'vendor/composer/autoload_classmap.php',
        'artisan_command' => 'hook-press:build',
    ],
];
```

### How it works

[](#how-it-works)

On composer install/update (or php artisan hook-press:build), HookPress scans your Composer classmap, applies your conditions, and writes a single PHP file (or cache entry) with the results.

At runtime you read from that file/cache—no reflection or directory walking.

Usage
-----

[](#usage)

```
use HookPress\Facades\HookPress;
// Entire map (all keys)
$all = HookPress::map();
// Specific map
$methods = HookPress::map('payout_methods');

// Classes that use a trait
$searchables = HookPress::classesUsing(\App\Traits\Searchable::class);

// Rebuild on demand (usually done via Composer hook)
HookPress::refresh();

// Clear the stored map
HookPress::clear();
```

Artisan Commands
----------------

[](#artisan-commands)

```
php artisan hook-press:build        # compute and store the map
php artisan hook-press:show         # print the map
php artisan hook-press:show payout_methods
php artisan hook-press:clear
php artisan hook-press:build --no-traits  # skip trait grouping
```

Conditions &amp; Examples
-------------------------

[](#conditions--examples)

### Available built-in conditions

[](#available-built-in-conditions)

ConditionPurposeArg TypeExample Arg(s)Notes / Behavior`isInstantiable`Include only classes that can be instantiated (not abstract/interfaces).*none**n/a*Default when `conditions` is omitted. Effectively filters out abstract classes and interfaces.`implementsInterface`Require the class to implement a specific interface.`string` (FQCN)`App\\Interfaces\\PayoutMethod::class`Uses `ReflectionClass::implementsInterface()`. Pass a fully-qualified interface name.`usesTrait`Require the class to use a given trait.`string` (FQCN)`App\\Traits\\Searchable::class`Checks `ReflectionClass::getTraitNames()`. Matches exact trait FQCN.`hasAttribute`Require the class to be annotated with a PHP 8 attribute.`string` (FQCN)`App\\Attributes\\Discoverable::class`Uses `ReflectionClass::getAttributes()`. Attribute FQCN must match.`extends`Require the class to extend a given parent/base class.`string` (FQCN)`Illuminate\\Database\\Eloquent\\Model::class`Uses `ReflectionClass::isSubclassOf()`.`isAbstract`Include only abstract classes.*none**n/a*Opposite of `isInstantiable` for abstracts. Useful if you deliberately want base classes.`isFinal`Include only classes declared `final`.*none**n/a*Uses `ReflectionClass::isFinal()`.`hasMethod`Require a method to exist (optionally with constraints).`string` **or** `array``'process'` **or** `['name' => 'process', 'public' => true, 'static' => false, 'returns' => 'bool']`Array keys supported: `name` (required), `public`/`protected`/`private` (bool), `static` (bool), `returns` (`'void'` or FQCN/string of return type).`hasProperty`Require a property to exist (optionally with constraints).`string` **or** `array``'driver'` **or** `['name' => 'driver', 'public' => true, 'static' => false, 'type' => 'string']`Array keys supported: `name` (required), `public`/`protected`/`private` (bool), `static` (bool), `type` (`'string'`, `'int'`, FQCN, etc.).`nameMatches`Match FQCN or short class name using a regex.`string` **or** `array``'/Controller$/'` **or** `['pattern' => '/^Bank/', 'short' => true]`If you pass an array: `pattern` is the regex; `short: true` applies it to the short class name; otherwise it applies to the full-qualified class name (FQCN).**Notes**

- If `conditions` is omitted for a map, HookPress defaults to `['isInstantiable']`.
- Combine multiple conditions in the array to apply **AND** logic.
- All FQCNs must be fully-qualified (escape backslashes in JSON/Markdown as needed).

### Examples

[](#examples)

#### Models (non-abstract Eloquent):

[](#models-non-abstract-eloquent)

```
'models' => [
    'namespaces' => ['App\\Models\\'],
    'conditions' => [
        'extends' => \Illuminate\Database\Eloquent\Model::class,
        'isInstantiable'// removes abstracts if-any
    ],
],
```

#### Invokables (public `__invoke`):

[](#invokables-public-__invoke)

```
'invokables' => [
    'namespaces' => ['App\\Actions\\', 'App\\Jobs\\'],
    'conditions' => [
        'isInstantiable',
        'hasMethod' => ['name' => '__invoke', 'public' => true],
    ],
],
```

#### Controllers by name:

[](#controllers-by-name)

```
'controllers' => [
    'namespaces' => ['App\\Http\\Controllers\\'],
    'conditions' => [
        'isInstantiable',
        'nameMatches' => '/Controller$/',
    ],
],
```

### Extending with your own custom checks

[](#extending-with-your-own-custom-checks)

1. Implement the contract

```
namespace App\HookPress;

use HookPress\Contracts\Condition;
use ReflectionClass;

class ConstructorTakesLogger implements Condition
{
    public function passes(ReflectionClass $ref, mixed $arg = null): bool
    {
        $ctor = $ref->getConstructor();
        if (! $ctor) return false;

        foreach ($ctor->getParameters() as $p) {
            $t = $p->getType();
            if ($t && ltrim((string) $t, '\\') === 'Psr\\Log\\LoggerInterface') {
                return true;
            }
        }
        return false;
    }
}
```

2. Reference it by the FQCN (Fully Qualified Class Name) in your config

```
'services_requiring_logger' => [
    'namespaces' => ['App\\Services\\'],
    'conditions' => [
        \App\HookPress\ConstructorTakesLogger::class => null,
    ],
],
```

HookPress resolves unknown condition keys as classes via the container, so no extra registration is needed.

### Notes

[](#notes)

Build time scales with your classmap size, but it’s a one-off step during Composer or CI.

Runtime lookups are reading from a single array in a PHP file or a cache item.

You can switch to the cache driver if you prefer not to ship a file under bootstrap/cache.

Changelog
---------

[](#changelog)

Please see [CHANGELOG](CHANGELOG.md) for more information on what has changed recently.

Contributing
------------

[](#contributing)

Please see [CONTRIBUTING](CONTRIBUTING.md) for details.

Security Vulnerabilities
------------------------

[](#security-vulnerabilities)

Please review [our security policy](../../security/policy) on how to report security vulnerabilities.

Credits
-------

[](#credits)

- [Teofanis Papadopulos](https://github.com/teofanis)
- [All Contributors](../../contributors)

License
-------

[](#license)

The MIT License (MIT). Please see [License File](LICENSE.md) for more information.

###  Health Score

38

—

LowBetter than 85% of packages

Maintenance75

Regular maintenance activity

Popularity24

Limited adoption so far

Community8

Small or concentrated contributor base

Maturity36

Early-stage or recently created project

 Bus Factor1

Top contributor holds 87.5% 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 ~0 days

Total

7

Last Release

261d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/a52c2fecd11bd797f9c73b93907f12e06c13c932eb492f150adfabde1b0b190a?d=identicon)[teos\_97](/maintainers/teos_97)

---

Top Contributors

[![teofanis](https://avatars.githubusercontent.com/u/47872542?v=4)](https://github.com/teofanis "teofanis (35 commits)")[![dependabot[bot]](https://avatars.githubusercontent.com/in/29110?v=4)](https://github.com/dependabot[bot] "dependabot[bot] (5 commits)")

---

Tags

auto-discoverycomposerlaravellaravel-packagephpclassmapinterfaceslaravelautoloadreflectionartisanlaravel-packagecacheservice discoveryPSR-4traitsregistryphp8-attributespost-install-cmdclass-discoveryTeofanis Papadopuloshook-presshookpresscomposer-hookcomposer-scriptspost-update-cmddriver-discoverybootstrap-cacheplugin-discovery

###  Code Quality

TestsPest

Static AnalysisPHPStan, Rector

Code StyleLaravel Pint

### Embed Badge

![Health badge](/badges/teofanis-hook-press/health.svg)

```
[![Health](https://phpackages.com/badges/teofanis-hook-press/health.svg)](https://phpackages.com/packages/teofanis-hook-press)
```

###  Alternatives

[spatie/laravel-responsecache

Speed up a Laravel application by caching the entire response

2.8k8.2M51](/packages/spatie-laravel-responsecache)[silber/page-cache

Caches responses as static files on disk for lightning fast page loads.

1.3k441.9k6](/packages/silber-page-cache)[ryangjchandler/blade-cache-directive

Cache chunks of your Blade markup with ease.

202200.8k2](/packages/ryangjchandler-blade-cache-directive)[yediyuz/laravel-cloudflare-cache

laravel-cloudflare-cache

28239.2k](/packages/yediyuz-laravel-cloudflare-cache)[vormkracht10/laravel-mails

Laravel Mails can collect everything you might want to track about the mails that has been sent by your Laravel app.

24149.7k](/packages/vormkracht10-laravel-mails)[dragon-code/laravel-cache

An improved interface for working with cache

6844.8k10](/packages/dragon-code-laravel-cache)

PHPackages © 2026

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