jturbide / form-to-email
A lightweight PHP 8.4+ library to process form submissions, validate fields, and send structured email notifications with configurable templates and error codes.
Installs: 3
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Watchers: 0
Forks: 0
Open Issues: 0
pkg:composer/jturbide/form-to-email
Requires
- php: >=8.4
- ext-intl: *
- ext-mbstring: *
- phpmailer/phpmailer: ^7.0.0
Requires (Dev)
- phpunit/phpunit: ^12.4
This package is auto-updated.
Last update: 2025-11-06 16:59:01 UTC
README
A lightweight, extensible PHP 8.4+ library for secure form processing, validation, sanitization, transformation, and structured email delivery. Built for modern PHP projects with strict typing, predictable pipelines, and framework-agnostic design.
π Quick start (tl;dr)
Install the package:
composer require jturbide/form-to-email
Create a form definition, create a mailer, and handle a request:
<?php use FormToEmail\Core\FieldDefinition; use FormToEmail\Core\FormDefinition; use FormToEmail\Enum\FieldRole; use FormToEmail\Filter\HtmlEscapeFilter; use FormToEmail\Filter\RemoveEmojiFilter; use FormToEmail\Filter\RemoveUrlFilter; use FormToEmail\Filter\SanitizeEmailFilter; use FormToEmail\Filter\StripTagsFilter; use FormToEmail\Filter\TrimFilter; use FormToEmail\Http\FormToEmailController; use FormToEmail\Mail\PHPMailerAdapter; use FormToEmail\Rule\EmailRule; use FormToEmail\Rule\RequiredRule; use FormToEmail\Transformer\LowercaseTransformer; require_once __DIR__ . '/../vendor/autoload.php'; $form = new FormDefinition() ->add(new FieldDefinition('name', roles: [FieldRole::SenderName], processors: [ new TrimFilter(), new RequiredRule(), ])) ->add(new FieldDefinition('email', roles: [FieldRole::SenderEmail], processors: [ new SanitizeEmailFilter(), new EmailRule(), new LowercaseTransformer(), ])) ->add(new FieldDefinition('message', roles: [FieldRole::Body], processors: [ new RemoveUrlFilter(), new RemoveEmojiFilter(), new StripTagsFilter(), new RequiredRule(), new HtmlEscapeFilter(), ])); $mailer = new PHPMailerAdapter( useSmtp: true, host: 'mail.example.com', username: 'no-reply@example.com', password: 'secret', fromEmail: 'no-reply@example.com', fromName: 'Website Contact Form' ); new FormToEmailController($form, $mailer, ['contact@example.com'])->handle();
Serve the file
php -s localhost:8000 ./
Send a XHR request
curl -X POST -H "Content-Type: application/json" -d '{ "name": "Julien", "email": "", "message": "Hello world!" }' http://localhost:8000/contact.php
See the result
β¨ Core Features
β Field Definitions & Validation
- Multiple validators per field (
RegexRule,CallbackRule,EmailRule, etc.) - Rich per-field error arrays for enhanced frontend UX
- Enum-based field roles (
SenderEmail,Subject,Body, etc.) - Composable and reusable rules (
RequiredRule,LengthRule,RegexRule, etc.)
β Unified Processor Pipeline (Filters, Transformers, Validators)
- Single
FieldProcessorinterface powering all processing stages - Deterministic execution order: sanitize β validate β transform
- Configurable per-field pipeline with early bailout support
- Fully reusable across forms for consistent data behavior
β Filters & Sanitizers
- First-class sanitization layer with composable filters
- Advanced built-in filters:
SanitizeEmailFilterβ RFC 6531/5321 aware, IDN-safeRemoveEmojiFilterβ removes emoji and pictographic symbolsNormalizeNewlinesFilterβ consistent\nnormalizationHtmlEscapeFilterβ secure HTML output for templatesRemoveUrlFilterβ aggressively removes embedded URLsCallbackFilterβ supports custom callable filters
β Transformers
- Mutate values post-sanitization, pre-validation
- Includes built-ins:
LowercaseTransformer,CallbackTransformer, etc. - Ideal for formatting, slugifying, or normalizing input values
β Email Composition & Delivery
- Dual HTML and plain-text templates (fully customizable)
PHPMailerAdapterby default β pluggable architecture for other adapters- Enum-based structured responses (
ResponseCode::Success,ResponseCode::ValidationError, etc.) - Automatic field-role mapping to email metadata
β Logging & Observability
- Built-in
Loggerfor form submission tracking - Supports multiple formats: raw text, JSON, and syslog-compatible
- Optional toggles for successful or failed submission logging
- Fully tested with configurable verbosity and custom formatters
β Architecture & Quality
- Framework-agnostic β works with plain PHP, Symfony, Laravel, or any custom stack
- 100 % typed and static-analysis-clean (Psalm / PHPStan / Qodana / SonarQube)
- 420+ PHPUnit tests with full coverage (core, filters, transformers, rules, adapters, logger)
- Predictable and deterministic pipeline ensuring consistent validation behavior
π§© Advanced Configuration
Built-in Adapters
MailerAdapter(interface) β minimal contract for sending mail.PHPMailerAdapterβ default adapter backed by PHPMailer.MailPayloadβ immutable value object that carriesto,subject,htmlBody,textBody,replyTo*.
Swap adapters by implementing
MailerAdapterand injecting your implementation intoFormToEmailController.
Built-in Filters
All filters implement Filter and the unified FieldProcessor contract. Use them to sanitize or normalize raw input.
TrimFilterβ trims leading/trailing whitespace.StripTagsFilterβ removes HTML tags.HtmlEscapeFilterβ escapes HTML for safe rendering.SanitizeTextFilterβ general text cleanup (safe subset).SanitizeEmailFilterβ RFC 5321/6531 aware, IDN-safe normalization.SanitizePhoneFilterβ digits-first cleanup for phone inputs.NormalizeNewlinesFilterβ converts mixed newlines to\n.RemoveUrlFilterβ removes URLs aggressively.RemoveEmojiFilterβ strips emoji and pictographs.CallbackFilterβ custom callable filter per field.- ...more to come!
Tip: Prefer filters early to make downstream validation predictable.
Built-in Rules (Validators)
Rules implement Rule (and FieldProcessor) and add ValidationError entries when constraints fail.
RequiredRuleβ value must be present/non-empty.EmailRuleβ email syntax validation (works with IDN-normalized values).RegexRuleβ arbitrary pattern checks.LengthRule/MinLengthRule/MaxLengthRuleβ string length constraints.CallbackRuleβ custom boolean/callback validation.- ...more to come!
Errors use
ErrorDefinitionwith machine-readablecode,message, andcontext.
Built-in Transformers
Transformers modify values after sanitization but generally before rules.
LowercaseTransformerβ lowercases strings.UcFirstTransformerβ capitalizes first letterCallbackTransformerβ custom mapping logic.- ...more to come!
Keep formatting here (slugify, case changes, phone canonicalization) so rules validate the final shape.
Processor Order
Execution is exactly the order you define in each FieldDefinition. A good default convention is:
Filters β Rules (Validators) β Transformers
Explicit order example
$field = new FieldDefinition('username', roles: [], processors: [ new HtmlEscapeFilter(), new TrimFilter(), new LowercaseTransformer(), new RegexRule('/^[a-z0-9_]{3,20}$/'), ]);
Incremental API
$field = new FieldDefinition('username'); $field->addFilter(new HtmlEscapeFilter()); $field->addFilter(new TrimFilter()); $field->addTransformer(new LowercaseTransformer()); $field->addRule(new RegexRule('/^[a-z0-9_]{3,20}$/'));
Chaining
$field = (new FieldDefinition('username')) ->addFilter(new HtmlEscapeFilter()) ->addFilter(new TrimFilter()) ->addTransformer(new LowercaseTransformer()) ->addRule(new RegexRule('/^[a-z0-9_]{3,20}$/'));
The library does not reorder processors for you. Define the pipeline you want. As a rule of thumb: sanitize first, validate, then transform to final form.
Custom Processors (Advanced)
Implement the unified processor contract to add your own behavior:
use FormToEmail\Core\{FieldProcessor, FieldDefinition, FormContext}; final class SlugifyTransformer implements FieldProcessor { public function process(mixed $value, FieldDefinition $field, FormContext $context): mixed { $s = strtolower(trim((string)$value)); $s = preg_replace('/[^a-z0-9]+/', '-', $s) ?? ''; return trim($s, '-'); } }
Register it like any other processor:
$field = (new FieldDefinition('title')) ->addTransformer(new SlugifyTransformer());
You can also stack your own custom ones:
$field->addTransformer(fn($v) => preg_replace('/\s+/', ' ', $v));
π§ Future Roadmap
This library already covers the essentials for form validation, sanitization, transformation, and email delivery. However, thereβs still plenty of room for evolution (as always). While most of the following items arenβt priorities for my own use cases, Iβm open to implementing them if theyβre valuable to you or your project.
β Completed
- Sanitization filters β
- Data transformers β
- Unified processor pipeline β
- Comprehensive unit test coverage β
- Submission logging system β
π§ Planned / Proposed
- reCAPTCHA v3 integration β prevent bot submissions without user friction
- File attachments β safely handle uploaded files via configurable limits
- Sender confirmation & double opt-in β ensure sender authenticity before sending
- Spam protection / honeypot β lightweight anti-spam defense
- Webhook + API notifications β trigger external systems on successful submissions
- Rate limiting & IP throttling β basic abuse protection for public endpoints
- Additional mailer adapters β e.g. Symfony Mailer, AWS SES, Postmark
- SmartEmailRule β enhanced email validation with MX/DNS deliverability checks
π‘ Want a feature sooner?
Open a GitHub issue or start a discussion β contributions and ideas are always welcome!
π§ͺ Quality Assurance
This library is built for reliability, maintainability, and modern PHP ecosystems.
- 100 % strictly typed β every file uses
declare(strict_types=1) - 100 % code coverage β verified through both unit and integration tests
- Modern PHP 8.4 syntax β
readonlyclasses, typed properties, attributes, and enhanced type safety - Continuous Integration β fully validated via GitHub Actions with PHPUnit, Psalm, and PHPStan
- Comprehensive test coverage β unit-tested filters, transformers, rules, and mail adapters
- Deterministic pipeline β predictable processor order with verified behavior across all test cases
πͺͺ License
BSD 3-Clause License Β© Julien Turbide