PHPackages                             rafalmasiarek/contact-form - 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. rafalmasiarek/contact-form

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

rafalmasiarek/contact-form
==========================

Lightweight, pluggable contact form service with hooks, validators and PSR-7-friendly helpers.

v1.3.0(1w ago)010MITPHPPHP ^8.0

Since Sep 6Pushed 1w agoCompare

[ Source](https://github.com/rafalmasiarek/php-contact-form)[ Packagist](https://packagist.org/packages/rafalmasiarek/contact-form)[ RSS](/packages/rafalmasiarek-contact-form/feed)WikiDiscussions main Synced today

READMEChangelog (4)Dependencies (2)Versions (6)Used By (0)

ContactForm (PSR‑7 friendly)
============================

[](#contactform-psr7-friendly)

Lightweight, framework-agnostic contact form service for PHP. It gives you a tiny, pluggable pipeline (hooks + validators), a simple DTO for requests, and transport-agnostic email sending (PHPMailer SMTP or native mail()), with example wiring for MailHog. Works with Composer or a standalone autoloader.

Highlights
----------

[](#highlights)

- **Small &amp; decoupled**: no hard HTTP coupling; returns a simple array you can turn into any response.
- **Pluggable pipeline**: run validators (as callables) and hooks (before/after validation, after send, on failure).
- **Clean data model**: ContactData DTO + OutboundEmail builder.
- **Transports**: PHPMailer adapter (SMTP) or native mail() sender.
- **i18n-friendly messages**: resolve human texts via codes with ArrayMessageResolver.
- **PSR-7 friendly**: easy to slot into Slim, Laminas, etc.
- **IP/UA resolution**: built-in `DefaultIpResolver` for raw PHP; inject your own `IpResolverInterface` behind a proxy.
- **Batteries included examples**: demo app with MailHog Docker, simple math CAPTCHA (hook + validator), required fields + email validators, IP annotation hook.

> Namespace: `rafalmasiarek\ContactForm` (PSR‑4 autoload).
> PHP: **^8.0**

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

[](#installation)

```
composer require rafalmasiarek/contact-form
# or use a local path repo during development
```

If you use the PHPMailer adapter:

```
composer require phpmailer/phpmailer:^6.9
```

Quickstart
----------

[](#quickstart)

```
git clone https://github.com/rafalmasiarek/php-contact-form.git php-contactform
cd php-contactform/examples
docker compose up -d
# open http://localhost:8080

```

Using with composer
-------------------

[](#using-with-composer)

The repo ships a PSR-4 style autoload.php. Point it at src/ (and optional lib/ folders for hooks/validators) and require it in your front controller.

```
require __DIR__ . '/../autoload.php'; // or examples/lib/contactform/autoload.php

```

Example wiring (simplified)
---------------------------

[](#example-wiring-simplified)

```
$cfg = require __DIR__ . '/../config.php';

$resolver = new ArrayMessageResolver([
  'OK_SENT' => ['message' => 'Message sent', 'http' => 200],
  'ERR_VALIDATION' => ['message' => 'Validation error', 'http' => 422],
  'ERR_SEND_FAILED' => ['message' => 'Message could not be sent.', 'http' => 500],
]);

// optional: register default messages for custom validators
ContactForm\Validators\MathCaptchaValidator::registerDefaultMessages($resolver);

$service = (new ContactFormService())
  ->setMessageResolver($resolver)
  ->setHooks([
    new ContactForm\Hook\AnnotateIpHook(),
    new ContactForm\Hook\MathCaptchaHook(
      field: $cfg['captcha']['field'],
      metaKey: $cfg['captcha']['meta_key']
    ),
  ])
  ->setValidators([
    'required' => ContactForm\Validators\FieldsValidator::required(['name','email','message']),
    'email'    => ContactForm\Validators\FieldsValidator::email('email', 'strict'),
    'captcha'  => ContactForm\Validators\MathCaptchaValidator::validate(
      field: $cfg['captcha']['field'],
      sessionKey: $cfg['captcha']['session_key'],
      oneShot: (bool)$cfg['captcha']['one_shot']
    ),
  ]);

// Pick a sender: PHPMailer if available, otherwise native mail()
$sender = class_exists(\PHPMailer\PHPMailer\PHPMailer::class)
  ? new \rafalmasiarek\ContactForm\Mail\PhpMailerEmailSender($cfg['smtp'])
  : new \rafalmasiarek\ContactForm\Mail\NativeMailSender(
      from: $cfg['smtp']['from'],
      fromName: $cfg['smtp']['from_name'],
      to: $cfg['smtp']['to'],
      replyTo: null
    );
$service->setEmailSender($sender);

// Build data and process
$data = new \rafalmasiarek\ContactForm\Model\ContactData(
  name: $_POST['name'] ?? '',
  email: $_POST['email'] ?? '',
  message: $_POST['message'] ?? '',
  subject: $_POST['subject'] ?? '',
  phone: $_POST['phone'] ?? '',
  meta: []
);

echo json_encode($service->process($data));
```

IP / User-Agent resolution
--------------------------

[](#ip--user-agent-resolution)

The service automatically resolves client IP and User-Agent as a fallback when they are not provided via `withContext()`.

**Raw PHP (no proxy)** — works out of the box, `DefaultIpResolver` reads from `$_SERVER['REMOTE_ADDR']`:

```
$service = new ContactFormService($smtp);
// DefaultIpResolver is used automatically — no config needed
```

**Behind a proxy** (Nginx, Cloudflare, load balancer) — inject your own implementation that validates trusted proxy ranges before trusting forwarded headers:

```
$service = (new ContactFormService($smtp))
    ->withIpResolver(new MyPsr7IpResolver($request));
```

**PSR-7 apps (Slim, Laminas)** — pass the already-resolved IP via `withContext()`; this always takes priority over the resolver:

```
$service = (new ContactFormService($smtp))
    ->withContext([
        'client' => [
            'ip' => $request->getAttribute('client_ip'),
            'ua' => $request->getHeaderLine('User-Agent'),
        ],
    ]);
```

`IpResolverInterface` lives in `Contracts/`; `DefaultIpResolver` in `Support/`.

PSR‑7 / Middlewares
-------------------

[](#psr7--middlewares)

Add a middleware in your app that resolves the real client IP and pass it via `withContext()`.

Hooks
-----

[](#hooks)

Implement `ContactFormHook` with any of these optional methods:

- `onBeforeValidate(ContactDataHook $dto)`
- `onAfterValidate(ContactDataHook $dto, string $validatorLabel)`
- `onBeforeSend(ContactDataHook $dto)`
- `onAfterSend(ContactDataHook $dto, string $transportMessageId)`

All hook errors are swallowed by default to protect the main flow.

Email
-----

[](#email)

Use `EmailSenderInterface` abstraction; provided adapters:

- `PhpMailerEmailSenderInterface` (depends on `phpmailer/phpmailer`)

`OutboundEmail` encapsulates the rendered message.

Messages
--------

[](#messages)

`MessageResolverInterface` decouples symbolic codes from UI strings.
`ArrayMessageResolver` is a simple in‑memory map with optional HTTP codes.

Validation
----------

[](#validation)

Any callable validator is accepted. Library provides helper DTO `ContactDataValidator` to keep the code tidy.

Versioning &amp; BC
-------------------

[](#versioning--bc)

- Namespace is stable: `rafalmasiarek\ContactForm`.
- All classes are PSR‑4 autoloaded from `/src`.
- `DefaultContactTemplate` moved from `Core\` to `Support\` — update your `use` statements if you reference it directly.

License
-------

[](#license)

MIT

###  Health Score

36

—

LowBetter than 79% of packages

Maintenance83

Actively maintained with recent releases

Popularity4

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity44

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% 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 ~95 days

Total

4

Last Release

13d ago

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/36776423?v=4)[Rafał Masiarek](/maintainers/rafalmasiarek)[@rafalmasiarek](https://github.com/rafalmasiarek)

---

Top Contributors

[![rafalmasiarek](https://avatars.githubusercontent.com/u/36776423?v=4)](https://github.com/rafalmasiarek "rafalmasiarek (3 commits)")

### Embed Badge

![Health badge](/badges/rafalmasiarek-contact-form/health.svg)

```
[![Health](https://phpackages.com/badges/rafalmasiarek-contact-form/health.svg)](https://phpackages.com/packages/rafalmasiarek-contact-form)
```

###  Alternatives

[aws/aws-sdk-php

AWS SDK for PHP - Use Amazon Web Services in your PHP project

6.3k543.5M2.6k](/packages/aws-aws-sdk-php)[neuron-core/neuron-ai

The PHP Agentic Framework.

2.0k656.1k38](/packages/neuron-core-neuron-ai)[google/cloud-core

Google Cloud PHP shared dependency, providing functionality useful to all components.

346132.9M112](/packages/google-cloud-core)[spatie/laravel-export

Create a static site bundle from a Laravel app

674146.0k6](/packages/spatie-laravel-export)[tempest/framework

The PHP framework that gets out of your way.

2.2k34.4k15](/packages/tempest-framework)[anthropic-ai/sdk

Anthropic PHP SDK

163583.3k17](/packages/anthropic-ai-sdk)

PHPackages © 2026

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