PHPackages                             sentinelphp/core - 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. [HTTP &amp; Networking](/categories/http)
4. /
5. sentinelphp/core

ActiveLibrary[HTTP &amp; Networking](/categories/http)

sentinelphp/core
================

SentinelPHP core library - HTTP client wrapper for intercepting and storing API calls with PII redaction and schema generation

v1.0.1(1mo ago)00GPL-3.0-or-laterPHPPHP &gt;=8.2

Since Apr 27Pushed 1mo agoCompare

[ Source](https://github.com/SentinelPHP/Core)[ Packagist](https://packagist.org/packages/sentinelphp/core)[ Docs](https://github.com/SentinelPHP/core)[ RSS](/packages/sentinelphp-core/feed)WikiDiscussions main Synced 1w ago

READMEChangelogDependencies (10)Versions (3)Used By (0)

Sentinel Core
=============

[](#sentinel-core)

[![Latest Version](https://camo.githubusercontent.com/6ea5b35f600dbed345da64ed6bd2b924355144d5acada66acfded13c0267ddd3/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f73656e74696e656c7068702f636f72652e737667)](https://packagist.org/packages/sentinelphp/core)[![License](https://camo.githubusercontent.com/db1d139ab962bb910583b1941398740cd7de98cea8cc5e00cd09c19f07989fc1/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f6c2f73656e74696e656c7068702f636f72652e737667)](https://github.com/SentinelPHP/core/blob/main/LICENSE)

HTTP client wrapper for intercepting and storing API calls with automatic PII redaction and schema generation.

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

[](#installation)

```
composer require sentinelphp/core
```

Features
--------

[](#features)

- **Intercept HTTP calls** via PSR-18 client wrapper or Guzzle middleware
- **Centralized setup** - configure once, intercept all HTTP requests automatically
- **Store API calls** to any backend (logger, database, queue, etc.)
- **Automatic PII redaction** before storage
- **Schema generation** from API responses
- **Pluggable storage** with built-in PSR-3 logger and callback adapters

Quick Start
-----------

[](#quick-start)

### Using PSR-18 Client Wrapper

[](#using-psr-18-client-wrapper)

```
use GuzzleHttp\Client;
use Psr\Log\LoggerInterface;
use SentinelPHP\Core\Client\SentinelClient;
use SentinelPHP\Core\Config\InterceptorConfig;
use SentinelPHP\Core\SentinelInterceptor;
use SentinelPHP\Core\Storage\Psr3LoggerStorage;
use SentinelPHP\Redact\PiiRedactor;

// 1. Create storage (logs to PSR-3 logger)
$storage = new Psr3LoggerStorage($logger);

// 2. Create interceptor with PII redaction
$interceptor = new SentinelInterceptor(
    storage: $storage,
    config: InterceptorConfig::default(),
    redactor: new PiiRedactor(),
);

// 3. Wrap your HTTP client
$client = new SentinelClient(
    inner: new Client(),
    interceptor: $interceptor,
);

// 4. Use normally - all calls are intercepted and stored
$response = $client->sendRequest($request);
```

### Using Guzzle Middleware

[](#using-guzzle-middleware)

```
use GuzzleHttp\Client;
use GuzzleHttp\HandlerStack;
use SentinelPHP\Core\Middleware\GuzzleMiddleware;
use SentinelPHP\Core\SentinelInterceptor;
use SentinelPHP\Core\Storage\CallbackStorage;

// Store to your database
$storage = new CallbackStorage(function (ApiCallRecord $record) use ($db) {
    $db->insert('api_logs', $record->toArray());
});

$interceptor = new SentinelInterceptor($storage);

$stack = HandlerStack::create();
$stack->push(GuzzleMiddleware::create($interceptor));

$client = new Client(['handler' => $stack]);
$client->get('https://api.example.com/users');
```

Storage Options
---------------

[](#storage-options)

### PSR-3 Logger

[](#psr-3-logger)

```
use SentinelPHP\Core\Storage\Psr3LoggerStorage;
use Psr\Log\LogLevel;

$storage = new Psr3LoggerStorage(
    logger: $monolog,
    logLevel: LogLevel::INFO,
    includeBody: true,
);
```

### Custom Callback

[](#custom-callback)

```
use SentinelPHP\Core\Storage\CallbackStorage;

$storage = new CallbackStorage(function (ApiCallRecord $record) {
    // Store to database, queue, file, etc.
    $this->entityManager->persist(ApiLog::fromRecord($record));
});
```

### Chain Multiple Storages

[](#chain-multiple-storages)

```
use SentinelPHP\Core\Storage\ChainStorage;

$storage = new ChainStorage(
    new Psr3LoggerStorage($logger),
    new CallbackStorage($databaseCallback),
);
```

### SentinelPHP Server

[](#sentinelphp-server)

Send intercepted calls to a SentinelPHP server for centralized monitoring, schema learning, and drift detection:

```
use GuzzleHttp\Client;
use GuzzleHttp\Psr7\HttpFactory;
use SentinelPHP\Core\Storage\SentinelHttpStorage;

$httpFactory = new HttpFactory();

$storage = new SentinelHttpStorage(
    httpClient: new Client(),
    requestFactory: $httpFactory,
    streamFactory: $httpFactory,
    baseUrl: 'https://sentinel.example.com',  // Your SentinelPHP server URL
    apiToken: 'your-api-token',                // API token from SentinelPHP dashboard
);
```

**Options:**

- `throwOnError: false` - Silently ignore HTTP errors (fire-and-forget mode)

**How it works:**

1. Your application makes HTTP calls through `SentinelClient` or Guzzle middleware
2. Each call is intercepted and an `ApiCallRecord` is created
3. The record is sent to your SentinelPHP server's `/api/ingest` endpoint
4. SentinelPHP processes the data for logging, schema learning, and drift detection

**Complete Example:**

```
use GuzzleHttp\Client;
use GuzzleHttp\Psr7\HttpFactory;
use SentinelPHP\Core\Client\SentinelClient;
use SentinelPHP\Core\Config\InterceptorConfig;
use SentinelPHP\Core\SentinelInterceptor;
use SentinelPHP\Core\Storage\SentinelHttpStorage;

// Create HTTP storage pointing to your SentinelPHP server
$httpFactory = new HttpFactory();
$storage = new SentinelHttpStorage(
    httpClient: new Client(['timeout' => 5]),
    requestFactory: $httpFactory,
    streamFactory: $httpFactory,
    baseUrl: $_ENV['SENTINEL_SERVER_URL'],
    apiToken: $_ENV['SENTINEL_API_TOKEN'],
    throwOnError: false,  // Don't fail if SentinelPHP is unavailable
);

// Create interceptor with minimal config (SentinelPHP handles redaction)
$interceptor = new SentinelInterceptor(
    storage: $storage,
    config: InterceptorConfig::minimal(),
);

// Wrap your HTTP client
$client = new SentinelClient(
    inner: new Client(['base_uri' => 'https://api.example.com']),
    interceptor: $interceptor,
);

// All API calls are now monitored by SentinelPHP
$response = $client->get('/users');
```

Configuration
-------------

[](#configuration)

```
use SentinelPHP\Core\Config\InterceptorConfig;

// Default: redact PII, capture bodies and headers
$config = InterceptorConfig::default();

// Minimal: no redaction, no body/header capture
$config = InterceptorConfig::minimal();

// Full: redact PII + generate schemas
$config = InterceptorConfig::full();

// Custom
$config = new InterceptorConfig(
    redactPii: true,
    generateSchemas: true,
    captureRequestBody: true,
    captureResponseBody: true,
    captureHeaders: true,
    redactFieldPaths: ['password', 'secret', 'api_key'],
);
```

API Call Record
---------------

[](#api-call-record)

Each intercepted call creates an `ApiCallRecord` with:

FieldTypeDescription`method`stringHTTP method (GET, POST, etc.)`url`stringFull request URL`statusCode`intResponse status code`latencyMs`floatRequest duration in milliseconds`timestamp`DateTimeImmutableWhen the call was made`requestHeaders`arrayRequest headers`requestBody`?stringRequest body (if captured)`responseHeaders`arrayResponse headers`responseBody`?stringResponse body (if captured)`generatedSchema`?arrayJSON Schema (if enabled)`id`?stringUnique identifierCentralized Setup (Intercept All HTTP Requests)
-----------------------------------------------

[](#centralized-setup-intercept-all-http-requests)

You can configure SentinelPHP once in your application's service container to automatically intercept **all** HTTP requests without modifying existing code.

### Symfony

[](#symfony)

Register the `SentinelClient` as your application's HTTP client in `config/services.yaml`:

```
services:
    # Inner HTTP client (not used directly)
    app.http_client.inner:
        class: GuzzleHttp\Client
        arguments:
            - { timeout: 30 }

    # SentinelPHP storage pointing to your server
    SentinelPHP\Core\Storage\SentinelHttpStorage:
        arguments:
            $httpClient: '@app.http_client.inner'
            $requestFactory: '@GuzzleHttp\Psr7\HttpFactory'
            $streamFactory: '@GuzzleHttp\Psr7\HttpFactory'
            $baseUrl: '%env(SENTINEL_SERVER_URL)%'
            $apiToken: '%env(SENTINEL_API_TOKEN)%'
            $throwOnError: false

    # SentinelPHP interceptor
    SentinelPHP\Core\SentinelInterceptor:
        arguments:
            $storage: '@SentinelPHP\Core\Storage\SentinelHttpStorage'
            $config: !service
                class: SentinelPHP\Core\Config\InterceptorConfig
                factory: ['SentinelPHP\Core\Config\InterceptorConfig', 'minimal']

    # Wrapped client - use this as your main HTTP client
    SentinelPHP\Core\Client\SentinelClient:
        arguments:
            $inner: '@app.http_client.inner'
            $interceptor: '@SentinelPHP\Core\SentinelInterceptor'

    # Alias so any service requesting ClientInterface gets the wrapped client
    Psr\Http\Client\ClientInterface: '@SentinelPHP\Core\Client\SentinelClient'
```

Now any service that type-hints `Psr\Http\Client\ClientInterface` will automatically use the SentinelPHP-wrapped client:

```
class PaymentGateway
{
    public function __construct(
        private ClientInterface $httpClient, // Automatically intercepted!
    ) {}

    public function charge(Payment $payment): Response
    {
        // This request is automatically sent to SentinelPHP
        return $this->httpClient->sendRequest($request);
    }
}
```

### Laravel

[](#laravel)

Register in a service provider (`app/Providers/AppServiceProvider.php`):

```
use GuzzleHttp\Client;
use GuzzleHttp\Psr7\HttpFactory;
use Psr\Http\Client\ClientInterface;
use SentinelPHP\Core\Client\SentinelClient;
use SentinelPHP\Core\Config\InterceptorConfig;
use SentinelPHP\Core\SentinelInterceptor;
use SentinelPHP\Core\Storage\SentinelHttpStorage;

public function register(): void
{
    $this->app->singleton(ClientInterface::class, function ($app) {
        $httpFactory = new HttpFactory();

        $storage = new SentinelHttpStorage(
            httpClient: new Client(['timeout' => 5]),
            requestFactory: $httpFactory,
            streamFactory: $httpFactory,
            baseUrl: config('services.sentinel.url'),
            apiToken: config('services.sentinel.token'),
            throwOnError: false,
        );

        $interceptor = new SentinelInterceptor(
            storage: $storage,
            config: InterceptorConfig::minimal(),
        );

        return new SentinelClient(
            inner: new Client(['timeout' => 30]),
            interceptor: $interceptor,
        );
    });
}
```

Then inject `ClientInterface` anywhere in your application:

```
class ExternalApiService
{
    public function __construct(
        private ClientInterface $client,
    ) {}
}
```

### Guzzle-Only Applications

[](#guzzle-only-applications)

If your application uses Guzzle directly, create a factory function that returns a pre-configured client:

```
// src/Http/HttpClientFactory.php
use GuzzleHttp\Client;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Psr7\HttpFactory;
use SentinelPHP\Core\Config\InterceptorConfig;
use SentinelPHP\Core\Middleware\GuzzleMiddleware;
use SentinelPHP\Core\SentinelInterceptor;
use SentinelPHP\Core\Storage\SentinelHttpStorage;

class HttpClientFactory
{
    private static ?Client $instance = null;

    public static function create(): Client
    {
        if (self::$instance !== null) {
            return self::$instance;
        }

        $httpFactory = new HttpFactory();

        $storage = new SentinelHttpStorage(
            httpClient: new Client(['timeout' => 5]),
            requestFactory: $httpFactory,
            streamFactory: $httpFactory,
            baseUrl: $_ENV['SENTINEL_SERVER_URL'],
            apiToken: $_ENV['SENTINEL_API_TOKEN'],
            throwOnError: false,
        );

        $interceptor = new SentinelInterceptor(
            storage: $storage,
            config: InterceptorConfig::minimal(),
        );

        $stack = HandlerStack::create();
        $stack->push(GuzzleMiddleware::create($interceptor));

        self::$instance = new Client([
            'handler' => $stack,
            'timeout' => 30,
        ]);

        return self::$instance;
    }
}
```

Replace your existing `new Client()` calls with `HttpClientFactory::create()`:

```
// Before
$client = new Client();

// After
$client = HttpClientFactory::create();
```

### Environment Variables

[](#environment-variables)

Add these to your `.env` file:

```
SENTINEL_SERVER_URL=https://sentinel.example.com
SENTINEL_API_TOKEN=your-api-token
```

Related Packages
----------------

[](#related-packages)

- **[sentinelphp/redact](../redact)** - PII redaction
- **[sentinelphp/schema](../schema)** - JSON Schema generation
- **[sentinelphp/drift](../drift)** - API drift detection
- **[sentinelphp/encrypt](../encrypt)** - Data encryption
- **[sentinelphp/dto](../dto)** - DTO code generation

License
-------

[](#license)

GPL v3 — see LICENSE for details

###  Health Score

38

—

LowBetter than 83% of packages

Maintenance91

Actively maintained with recent releases

Popularity0

Limited adoption so far

Community8

Small or concentrated contributor base

Maturity47

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 50% 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

2

Last Release

43d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/bff35a1829b34311e361190b585541c13146f2d220d851064a2b402f6c615cd0?d=identicon)[TLyngeJ](/maintainers/TLyngeJ)

---

Top Contributors

[![github-actions[bot]](https://avatars.githubusercontent.com/in/15368?v=4)](https://github.com/github-actions[bot] "github-actions[bot] (1 commits)")[![TLyngeJ](https://avatars.githubusercontent.com/u/3542330?v=4)](https://github.com/TLyngeJ "TLyngeJ (1 commits)")

---

Tags

apiloggingmonitoringschemahttp clientsentinelinterceptordrift

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Type Coverage Yes

### Embed Badge

![Health badge](/badges/sentinelphp-core/health.svg)

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

###  Alternatives

[tempest/framework

The PHP framework that gets out of your way.

2.2k31.1k11](/packages/tempest-framework)[cakephp/cakephp

The CakePHP framework

8.8k19.1M1.7k](/packages/cakephp-cakephp)[guzzlehttp/psr7

PSR-7 message implementation that also provides common utility methods

7.9k1.1B3.7k](/packages/guzzlehttp-psr7)[telnyx/telnyx-php

Official Telnyx PHP SDK — APIs for Voice, SMS, MMS, WhatsApp, Fax, SIP Trunking, Wireless IoT, Call Control, and more. Build global communications on Telnyx's private carrier-grade network.

35729.6k2](/packages/telnyx-telnyx-php)[flow-php/flow

PHP ETL - Extract Transform Load - Data processing framework

84735.1k](/packages/flow-php-flow)[typo3/cms

TYPO3 CMS is a free open source Content Management Framework initially created by Kasper Skaarhoj and licensed under GNU/GPL.

1.2k1.9M122](/packages/typo3-cms)

PHPackages © 2026

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