GitHub - eznix86/laravel-version: Manage your Application Version
https://github.com/eznix86/laravel-version
https://redd.it/1pk34z6
@r_php
https://github.com/eznix86/laravel-version
https://redd.it/1pk34z6
@r_php
GitHub
GitHub - eznix86/laravel-version: Manage your Application Version
Manage your Application Version. Contribute to eznix86/laravel-version development by creating an account on GitHub.
SymfonyCon Amsterdam 2025: Free replay? Where?!
https://symfony.com/blog/symfonycon-amsterdam-2025-free-replay-where?utm_medium=feed&utm_source=Symfony%20Blog%20Feed
https://redd.it/1pk75hd
@r_php
https://symfony.com/blog/symfonycon-amsterdam-2025-free-replay-where?utm_medium=feed&utm_source=Symfony%20Blog%20Feed
https://redd.it/1pk75hd
@r_php
Symfony
SymfonyCon Amsterdam 2025: Free replay? Where?! (Symfony Blog)
Enjoy Christopher Hertel’s talk “Symfony AI in Action”, now available for free! A clear, practical guide to integrating AI into Symfony and PHP projects.
How do you stay competitive / find new opportunities as a remote PHP/Symfony dev (4y exp)
Hey everyone,
I’ve been working fully remotely as a PHP/Symfony developer for about 4+ years now. I’m starting to look around for new opportunities, but the market feels tighter than it used to be.
For those of you doing remote backend work:
Where are you finding the best-quality remote PHP/Symfony roles lately?
Are there specific job boards, agencies, or platforms worth focusing on?
What actually helps you stand out when applying as a remote dev? (Portfolio? OSS contributions? Certifications? Something else?)
Are companies shifting away from fully remote for PHP roles, or is it just more competitive now?
I’m solid with Symfony, API Platform, Docker, CI/CD, and general backend architecture, just trying to figure out the smartest path to land the next good remote gig.
Any advice from people who’ve navigated this recently would be super appreciated!
https://redd.it/1pk7lq5
@r_php
Hey everyone,
I’ve been working fully remotely as a PHP/Symfony developer for about 4+ years now. I’m starting to look around for new opportunities, but the market feels tighter than it used to be.
For those of you doing remote backend work:
Where are you finding the best-quality remote PHP/Symfony roles lately?
Are there specific job boards, agencies, or platforms worth focusing on?
What actually helps you stand out when applying as a remote dev? (Portfolio? OSS contributions? Certifications? Something else?)
Are companies shifting away from fully remote for PHP roles, or is it just more competitive now?
I’m solid with Symfony, API Platform, Docker, CI/CD, and general backend architecture, just trying to figure out the smartest path to land the next good remote gig.
Any advice from people who’ve navigated this recently would be super appreciated!
https://redd.it/1pk7lq5
@r_php
Reddit
From the symfony community on Reddit
Explore this post and more from the symfony community
AI: Coding models benchmarks on PHP?
Hi,
Most coding benchmarks such as the SWE line heavily test coding models on Python.
Are there any benchmarks that evaluate PHP coding capabilities? Vanialia PHP and through frameworks.
Many thanks
https://redd.it/1pk9reg
@r_php
Hi,
Most coding benchmarks such as the SWE line heavily test coding models on Python.
Are there any benchmarks that evaluate PHP coding capabilities? Vanialia PHP and through frameworks.
Many thanks
https://redd.it/1pk9reg
@r_php
Reddit
From the PHP community on Reddit
Explore this post and more from the PHP community
Need laptop suggestions under ₹50,000 – good battery, office work + light gaming
Hey everyone,
I'm looking to buy a laptop under **₹50,000** (can stretch a little if it's really worth it).
My requirements:
* Good **battery life**
* Smooth for **office work** (Google Workspace, MS Office, browsing, multitasking)
* Should handle **light gaming** like Valorant, GTA 5 (medium settings are fine)
* Minimum **8GB RAM** (preferably upgradable)
* **SSD** is must
* Ryzen 5 / Intel i5 level performance
* Decent build quality and not too heavy
If anyone is using a good laptop in this range or has recommendations, please suggest.
Looking for something balanced — not a heavy gaming machine, but also not a weak office laptop.
Thanks in advance! 🙏
[](https://www.reddit.com/submit/?source_id=t3_1pjqqb4)
https://redd.it/1pkjhfd
@r_php
Hey everyone,
I'm looking to buy a laptop under **₹50,000** (can stretch a little if it's really worth it).
My requirements:
* Good **battery life**
* Smooth for **office work** (Google Workspace, MS Office, browsing, multitasking)
* Should handle **light gaming** like Valorant, GTA 5 (medium settings are fine)
* Minimum **8GB RAM** (preferably upgradable)
* **SSD** is must
* Ryzen 5 / Intel i5 level performance
* Decent build quality and not too heavy
If anyone is using a good laptop in this range or has recommendations, please suggest.
Looking for something balanced — not a heavy gaming machine, but also not a weak office laptop.
Thanks in advance! 🙏
[](https://www.reddit.com/submit/?source_id=t3_1pjqqb4)
https://redd.it/1pkjhfd
@r_php
The new clamp() function in PHP 8.6
https://amitmerchant.com/the-clamp-function-in-php-86/
https://redd.it/1pkobw3
@r_php
https://amitmerchant.com/the-clamp-function-in-php-86/
https://redd.it/1pkobw3
@r_php
Amit Merchant
The new clamp() function in PHP 8.6
You know how sometimes you want to ensure that a value stays within a specific range? Maybe you’re working with user input, configuration values, or any scenario where you need to enforce boundaries.
Looking for a Laravel/PHP Equivalent to Bubble or Emergent
Is there anything like Bubble, Lovable, or Emergent that works natively with Laravel/PHP?
Curious what the ecosystem has.
https://redd.it/1pkpshb
@r_php
Is there anything like Bubble, Lovable, or Emergent that works natively with Laravel/PHP?
Curious what the ecosystem has.
https://redd.it/1pkpshb
@r_php
Reddit
From the laravel community on Reddit
Explore this post and more from the laravel community
Building a Production-Ready Webhook System for Laravel
**A deep dive into security, reliability, and extensibility decisions**
When I started building [FilaForms](https://filaforms.app/), a customer-facing form builder for Filament PHP, webhooks seemed straightforward. User submits form, I POST JSON to a URL. Done.
Then I started thinking about edge cases. What if the endpoint is down? What if someone points the webhook at `localhost`? How do consumers verify the request actually came from my system? What happens when I want to add Slack notifications later?
This post documents how I solved these problems. Not just the code, but the reasoning behind each decision.
# Why Webhooks Are Harder Than They Look
Here's what a naive webhook implementation misses:
**Security holes:**
* No protection against Server-Side Request Forgery (SSRF)
* No way for consumers to verify request authenticity
* Potential for replay attacks
**Reliability gaps:**
* No retry mechanism when endpoints fail
* No delivery tracking or audit trail
* Silent failures with no debugging information
**Architectural debt:**
* Tight coupling makes adding new integrations painful
* No standardization across different integration types
I wanted to address all of these from the start.
# The Architecture
The system follows an event-driven, queue-based design:
Form Submission
↓
FormSubmitted Event
↓
TriggerIntegrations Listener (queued)
↓
ProcessIntegrationJob (one per webhook)
↓
WebhookIntegration Handler
↓
IntegrationDelivery Record
Every component serves a purpose:
**Queued listener**: Form submission stays fast. The user sees success immediately while webhook processing happens in the background.
**Separate jobs per integration**: If one webhook fails, others aren't affected. Each has its own retry lifecycle.
**Delivery records**: Complete audit trail. When a user asks "why didn't my webhook fire?", I can show exactly what happened.
# Choosing Standard Webhooks
For request signing, I adopted the [Standard Webhooks](https://github.com/standard-webhooks/standard-webhooks) specification rather than inventing my own scheme.
# The Spec in Brief
Every webhook request includes three headers:
|Header|Purpose|
|:-|:-|
|`webhook-id`|Unique identifier for deduplication|
|`webhook-timestamp`|Unix timestamp to prevent replay attacks|
|`webhook-signature`|HMAC-SHA256 signature for verification|
The signature covers both the message ID and timestamp, not just the payload. This prevents an attacker from capturing a valid request and replaying it later.
# Why I Chose This
**Familiarity**: Stripe, Svix, and others use compatible schemes. Developers integrating with my system likely already know how to verify these signatures.
**Battle-tested**: The spec handles edge cases I would have missed. For example, the signature format (`v1,base64signature`) includes a version prefix, allowing future algorithm upgrades without breaking existing consumers.
**Constant-time comparison**: My verification uses `hash_equals()` to prevent timing attacks. This isn't obvious—using `===` for signature comparison leaks information about which characters match.
# Secret Format
I generate secrets with a `whsec_` prefix followed by 32 bytes of base64-encoded randomness:
whsec_dGhpcyBpcyBhIHNlY3JldCBrZXkgZm9yIHdlYmhvb2tz
The prefix makes secrets instantly recognizable. When someone accidentally commits one to a repository, it's obvious what it is. When reviewing environment variables, there's no confusion about which value is the webhook secret.
# Preventing SSRF Attacks
Server-Side Request Forgery is a critical vulnerability. An attacker could configure a webhook pointing to:
* `http://localhost:6379` — Redis instance accepting commands
* `http://169.254.169.254/latest/meta-data/` — AWS metadata endpoint exposing credentials
* `http://192.168.1.1/admin` — Internal router admin panel
My `WebhookUrlValidator` implements four layers of protection:
# Layer 1: URL Format Validation
Basic
**A deep dive into security, reliability, and extensibility decisions**
When I started building [FilaForms](https://filaforms.app/), a customer-facing form builder for Filament PHP, webhooks seemed straightforward. User submits form, I POST JSON to a URL. Done.
Then I started thinking about edge cases. What if the endpoint is down? What if someone points the webhook at `localhost`? How do consumers verify the request actually came from my system? What happens when I want to add Slack notifications later?
This post documents how I solved these problems. Not just the code, but the reasoning behind each decision.
# Why Webhooks Are Harder Than They Look
Here's what a naive webhook implementation misses:
**Security holes:**
* No protection against Server-Side Request Forgery (SSRF)
* No way for consumers to verify request authenticity
* Potential for replay attacks
**Reliability gaps:**
* No retry mechanism when endpoints fail
* No delivery tracking or audit trail
* Silent failures with no debugging information
**Architectural debt:**
* Tight coupling makes adding new integrations painful
* No standardization across different integration types
I wanted to address all of these from the start.
# The Architecture
The system follows an event-driven, queue-based design:
Form Submission
↓
FormSubmitted Event
↓
TriggerIntegrations Listener (queued)
↓
ProcessIntegrationJob (one per webhook)
↓
WebhookIntegration Handler
↓
IntegrationDelivery Record
Every component serves a purpose:
**Queued listener**: Form submission stays fast. The user sees success immediately while webhook processing happens in the background.
**Separate jobs per integration**: If one webhook fails, others aren't affected. Each has its own retry lifecycle.
**Delivery records**: Complete audit trail. When a user asks "why didn't my webhook fire?", I can show exactly what happened.
# Choosing Standard Webhooks
For request signing, I adopted the [Standard Webhooks](https://github.com/standard-webhooks/standard-webhooks) specification rather than inventing my own scheme.
# The Spec in Brief
Every webhook request includes three headers:
|Header|Purpose|
|:-|:-|
|`webhook-id`|Unique identifier for deduplication|
|`webhook-timestamp`|Unix timestamp to prevent replay attacks|
|`webhook-signature`|HMAC-SHA256 signature for verification|
The signature covers both the message ID and timestamp, not just the payload. This prevents an attacker from capturing a valid request and replaying it later.
# Why I Chose This
**Familiarity**: Stripe, Svix, and others use compatible schemes. Developers integrating with my system likely already know how to verify these signatures.
**Battle-tested**: The spec handles edge cases I would have missed. For example, the signature format (`v1,base64signature`) includes a version prefix, allowing future algorithm upgrades without breaking existing consumers.
**Constant-time comparison**: My verification uses `hash_equals()` to prevent timing attacks. This isn't obvious—using `===` for signature comparison leaks information about which characters match.
# Secret Format
I generate secrets with a `whsec_` prefix followed by 32 bytes of base64-encoded randomness:
whsec_dGhpcyBpcyBhIHNlY3JldCBrZXkgZm9yIHdlYmhvb2tz
The prefix makes secrets instantly recognizable. When someone accidentally commits one to a repository, it's obvious what it is. When reviewing environment variables, there's no confusion about which value is the webhook secret.
# Preventing SSRF Attacks
Server-Side Request Forgery is a critical vulnerability. An attacker could configure a webhook pointing to:
* `http://localhost:6379` — Redis instance accepting commands
* `http://169.254.169.254/latest/meta-data/` — AWS metadata endpoint exposing credentials
* `http://192.168.1.1/admin` — Internal router admin panel
My `WebhookUrlValidator` implements four layers of protection:
# Layer 1: URL Format Validation
Basic
FilaForms
FilaForms - Public Forms for Your Website Visitors | Filament Plugin for Laravel
Create public-facing forms your website visitors fill out. Contact forms, surveys, registrations—FilaForms is the Filament plugin that handles visitor forms with visual builder, submission tracking, and analytics. Not for admin panels—for your actual website…
sanity check using PHP's `filter_var()`. Catches malformed URLs before they cause problems.
# Layer 2: Protocol Enforcement
HTTPS required in production. HTTP only allowed in local/testing environments. This prevents credential interception and blocks most localhost attacks.
# Layer 3: Pattern-Based Blocking
Regex patterns catch obvious private addresses:
* Localhost: `localhost`, `127.*`, `0.0.0.0`
* RFC1918 private: `10.*`, `172.16-31.*`, `192.168.*`
* Link-local: `169.254.*`
* IPv6 private: `::1`, `fe80:*`, `fc*`, `fd*`
# Layer 4: DNS Resolution
Here's where it gets interesting. An attacker could register `webhook.evil.com` pointing to `127.0.0.1`. Pattern matching on the hostname won't catch this.
I resolve the hostname to an IP address using `gethostbyname()`, then validate the resolved IP using PHP's `FILTER_FLAG_NO_PRIV_RANGE` and `FILTER_FLAG_NO_RES_RANGE` flags.
**Critical detail**: I validate both at configuration time AND before each request. This prevents DNS rebinding attacks where an attacker changes DNS records after initial validation.
# The Retry Strategy
Network failures happen. Servers restart. Rate limits trigger. A webhook system without retries isn't production-ready.
I implemented the Standard Webhooks recommended retry schedule:
|Attempt|Delay|Running Total|
|:-|:-|:-|
|1|Immediate|0|
|2|5 seconds|5s|
|3|5 minutes|\~5m|
|4|30 minutes|\~35m|
|5|2 hours|\~2.5h|
|6|5 hours|\~7.5h|
|7|10 hours|\~17.5h|
|8|10 hours|\~27.5h|
# Why This Schedule
**Fast initial retry**: The 5-second delay catches momentary network blips. Many transient failures resolve within seconds.
**Exponential backoff**: If an endpoint is struggling, I don't want to make it worse. Increasing delays give it time to recover.
**\~27 hours total**: Long enough to survive most outages, short enough to not waste resources indefinitely.
# Intelligent Failure Classification
Not all failures deserve retries:
**Retryable** (temporary problems):
* Network errors (connection refused, timeout, DNS failure)
* `5xx` server errors
* `429 Too Many Requests`
* `408 Request Timeout`
**Terminal** (permanent problems):
* `4xx` client errors (bad request, unauthorized, forbidden, not found)
* Successful delivery
**Special case—**`410 Gone`:
When an endpoint returns `410 Gone`, it explicitly signals "this resource no longer exists, don't try again." I automatically disable the integration and log a warning. This prevents wasting resources on endpoints that will never work.
# Delivery Tracking
Every webhook attempt creates an `IntegrationDelivery` record containing:
**Request details:**
* Full JSON payload sent
* All headers including signatures
* Form and submission IDs
**Response details:**
* HTTP status code
* Response body (truncated to prevent storage bloat)
* Response headers
**Timing:**
* When processing started
* When completed (or next retry timestamp)
* Total duration in milliseconds
# The Status Machine
PENDING → PROCESSING → SUCCESS
↓
(failure)
↓
RETRYING → (wait) → PROCESSING
↓
(max retries)
↓
FAILED
This provides complete visibility into every webhook's lifecycle. When debugging, I can see exactly what was sent, what came back, and how long it took.
# Building for Extensibility
Webhooks are just the first integration. Slack notifications, Zapier triggers, Google Sheets exports—these will follow. I needed an architecture that makes adding new integrations trivial.
# The Integration Contract
Every integration implements an `IntegrationInterface`:
**Identity methods:**
* `getKey()`: Unique identifier like `'webhook'` or `'slack'`
* `getName()`: Display name for the UI
* `getDenoscription()`: Help text explaining what it does
* `getIcon()`: Heroicon identifier
* `getCategory()`: Grouping for the admin panel
**Capability methods:**
* `getSupportedEvents()`: Which events trigger this integration
* `getConfigSchema()`: Filament form components for configuration
* `requiresOAuth()`:
# Layer 2: Protocol Enforcement
HTTPS required in production. HTTP only allowed in local/testing environments. This prevents credential interception and blocks most localhost attacks.
# Layer 3: Pattern-Based Blocking
Regex patterns catch obvious private addresses:
* Localhost: `localhost`, `127.*`, `0.0.0.0`
* RFC1918 private: `10.*`, `172.16-31.*`, `192.168.*`
* Link-local: `169.254.*`
* IPv6 private: `::1`, `fe80:*`, `fc*`, `fd*`
# Layer 4: DNS Resolution
Here's where it gets interesting. An attacker could register `webhook.evil.com` pointing to `127.0.0.1`. Pattern matching on the hostname won't catch this.
I resolve the hostname to an IP address using `gethostbyname()`, then validate the resolved IP using PHP's `FILTER_FLAG_NO_PRIV_RANGE` and `FILTER_FLAG_NO_RES_RANGE` flags.
**Critical detail**: I validate both at configuration time AND before each request. This prevents DNS rebinding attacks where an attacker changes DNS records after initial validation.
# The Retry Strategy
Network failures happen. Servers restart. Rate limits trigger. A webhook system without retries isn't production-ready.
I implemented the Standard Webhooks recommended retry schedule:
|Attempt|Delay|Running Total|
|:-|:-|:-|
|1|Immediate|0|
|2|5 seconds|5s|
|3|5 minutes|\~5m|
|4|30 minutes|\~35m|
|5|2 hours|\~2.5h|
|6|5 hours|\~7.5h|
|7|10 hours|\~17.5h|
|8|10 hours|\~27.5h|
# Why This Schedule
**Fast initial retry**: The 5-second delay catches momentary network blips. Many transient failures resolve within seconds.
**Exponential backoff**: If an endpoint is struggling, I don't want to make it worse. Increasing delays give it time to recover.
**\~27 hours total**: Long enough to survive most outages, short enough to not waste resources indefinitely.
# Intelligent Failure Classification
Not all failures deserve retries:
**Retryable** (temporary problems):
* Network errors (connection refused, timeout, DNS failure)
* `5xx` server errors
* `429 Too Many Requests`
* `408 Request Timeout`
**Terminal** (permanent problems):
* `4xx` client errors (bad request, unauthorized, forbidden, not found)
* Successful delivery
**Special case—**`410 Gone`:
When an endpoint returns `410 Gone`, it explicitly signals "this resource no longer exists, don't try again." I automatically disable the integration and log a warning. This prevents wasting resources on endpoints that will never work.
# Delivery Tracking
Every webhook attempt creates an `IntegrationDelivery` record containing:
**Request details:**
* Full JSON payload sent
* All headers including signatures
* Form and submission IDs
**Response details:**
* HTTP status code
* Response body (truncated to prevent storage bloat)
* Response headers
**Timing:**
* When processing started
* When completed (or next retry timestamp)
* Total duration in milliseconds
# The Status Machine
PENDING → PROCESSING → SUCCESS
↓
(failure)
↓
RETRYING → (wait) → PROCESSING
↓
(max retries)
↓
FAILED
This provides complete visibility into every webhook's lifecycle. When debugging, I can see exactly what was sent, what came back, and how long it took.
# Building for Extensibility
Webhooks are just the first integration. Slack notifications, Zapier triggers, Google Sheets exports—these will follow. I needed an architecture that makes adding new integrations trivial.
# The Integration Contract
Every integration implements an `IntegrationInterface`:
**Identity methods:**
* `getKey()`: Unique identifier like `'webhook'` or `'slack'`
* `getName()`: Display name for the UI
* `getDenoscription()`: Help text explaining what it does
* `getIcon()`: Heroicon identifier
* `getCategory()`: Grouping for the admin panel
**Capability methods:**
* `getSupportedEvents()`: Which events trigger this integration
* `getConfigSchema()`: Filament form components for configuration
* `requiresOAuth()`:
Whether OAuth setup is needed
**Execution methods:**
* `handle()`: Process an event and return a result
* `test()`: Verify the integration works
# The Registry
The `IntegrationRegistry` acts as a service locator:
$registry->register(WebhookIntegration::class);
$registry->register(SlackIntegration::class); // Future
$handler = $registry->get('webhook');
$result = $handler->handle($event, $integration);
When I add Slack support, I create one class implementing the interface, register it, and the entire event system, job dispatcher, retry logic, and delivery tracking just works.
# Type Safety with DTOs
I use Spatie Laravel Data for type-safe data transfer throughout the system.
# IntegrationEventData
The payload structure flowing through the pipeline:
class IntegrationEventData extends Data
{
public IntegrationEvent $type;
public string $timestamp;
public string $formId;
public string $formName;
public ?string $formKey;
public array $data;
public ?array $metadata;
public ?string $submissionId;
}
This DTO has transformation methods:
* `toWebhookPayload()`: Nested structure with form/submission/metadata sections
* `toFlatPayload()`: Flat structure for automation platforms like Zapier
* `fromSubmission()`: Factory method to create from a form submission
# IntegrationResultData
What comes back from an integration handler:
class IntegrationResultData extends Data
{
public bool $success;
public ?int $statusCode;
public mixed $response;
public ?array $headers;
public ?string $error;
public ?string $errorCode;
public ?int $duration;
}
Helper methods like `isRetryable()` and `shouldDisableEndpoint()` encapsulate the retry logic decisions.
# Snake Case Mapping
All DTOs use Spatie's `SnakeCaseMapper`. PHP properties use camelCase (`$formId`), but JSON output uses snake\_case (`form_id`). This keeps PHP idiomatic while following JSON conventions.
# The Webhook Payload
The final payload structure:
{
"type": "submission.created",
"timestamp": "2024-01-15T10:30:00+00:00",
"data": {
"form": {
"id": "01HQ5KXJW9YZPX...",
"name": "Contact Form",
"key": "contact-form"
},
"submission": {
"id": "01HQ5L2MN8ABCD...",
"fields": {
"name": "John Doe",
"email": "john@example.com",
"message": "Hello!"
}
},
"metadata": {
"ip": "192.0.2.1",
"user_agent": "Mozilla/5.0...",
"submitted_at": "2024-01-15T10:30:00+00:00"
}
}
}
**Design decisions:**
* **Event type at root**: Easy routing in consumer code
* **ISO8601 timestamps**: Unambiguous, timezone-aware
* **ULIDs for IDs**: Sortable, URL-safe, no sequential exposure
* **Nested structure**: Clear separation of concerns
* **Optional metadata**: Can be disabled for privacy-conscious users
# Lessons Learned
# What Worked Well
**Adopting Standard Webhooks**: Using an established spec saved time and gave consumers familiar patterns. The versioned signature format will age gracefully.
**Queue-first architecture**: Making everything async from day one prevented issues that would have been painful to fix later.
**Multi-layer SSRF protection**: DNS resolution validation catches attacks that pattern matching misses. Worth the extra complexity.
**Complete audit trail**: Delivery records have already paid for themselves in debugging time saved.
# What I'd Add Next
**Rate limiting per endpoint**: A form with 1000 submissions could overwhelm a webhook consumer. I need per-endpoint rate limiting with backpressure.
**Circuit breaker pattern**: After N consecutive failures, stop attempting deliveries for a cooldown period. Protects both my queue workers and the failing endpoint.
**Delivery log viewer**: The records exist but aren't exposed in the admin UI. A panel showing delivery history with filtering and manual retry would
**Execution methods:**
* `handle()`: Process an event and return a result
* `test()`: Verify the integration works
# The Registry
The `IntegrationRegistry` acts as a service locator:
$registry->register(WebhookIntegration::class);
$registry->register(SlackIntegration::class); // Future
$handler = $registry->get('webhook');
$result = $handler->handle($event, $integration);
When I add Slack support, I create one class implementing the interface, register it, and the entire event system, job dispatcher, retry logic, and delivery tracking just works.
# Type Safety with DTOs
I use Spatie Laravel Data for type-safe data transfer throughout the system.
# IntegrationEventData
The payload structure flowing through the pipeline:
class IntegrationEventData extends Data
{
public IntegrationEvent $type;
public string $timestamp;
public string $formId;
public string $formName;
public ?string $formKey;
public array $data;
public ?array $metadata;
public ?string $submissionId;
}
This DTO has transformation methods:
* `toWebhookPayload()`: Nested structure with form/submission/metadata sections
* `toFlatPayload()`: Flat structure for automation platforms like Zapier
* `fromSubmission()`: Factory method to create from a form submission
# IntegrationResultData
What comes back from an integration handler:
class IntegrationResultData extends Data
{
public bool $success;
public ?int $statusCode;
public mixed $response;
public ?array $headers;
public ?string $error;
public ?string $errorCode;
public ?int $duration;
}
Helper methods like `isRetryable()` and `shouldDisableEndpoint()` encapsulate the retry logic decisions.
# Snake Case Mapping
All DTOs use Spatie's `SnakeCaseMapper`. PHP properties use camelCase (`$formId`), but JSON output uses snake\_case (`form_id`). This keeps PHP idiomatic while following JSON conventions.
# The Webhook Payload
The final payload structure:
{
"type": "submission.created",
"timestamp": "2024-01-15T10:30:00+00:00",
"data": {
"form": {
"id": "01HQ5KXJW9YZPX...",
"name": "Contact Form",
"key": "contact-form"
},
"submission": {
"id": "01HQ5L2MN8ABCD...",
"fields": {
"name": "John Doe",
"email": "john@example.com",
"message": "Hello!"
}
},
"metadata": {
"ip": "192.0.2.1",
"user_agent": "Mozilla/5.0...",
"submitted_at": "2024-01-15T10:30:00+00:00"
}
}
}
**Design decisions:**
* **Event type at root**: Easy routing in consumer code
* **ISO8601 timestamps**: Unambiguous, timezone-aware
* **ULIDs for IDs**: Sortable, URL-safe, no sequential exposure
* **Nested structure**: Clear separation of concerns
* **Optional metadata**: Can be disabled for privacy-conscious users
# Lessons Learned
# What Worked Well
**Adopting Standard Webhooks**: Using an established spec saved time and gave consumers familiar patterns. The versioned signature format will age gracefully.
**Queue-first architecture**: Making everything async from day one prevented issues that would have been painful to fix later.
**Multi-layer SSRF protection**: DNS resolution validation catches attacks that pattern matching misses. Worth the extra complexity.
**Complete audit trail**: Delivery records have already paid for themselves in debugging time saved.
# What I'd Add Next
**Rate limiting per endpoint**: A form with 1000 submissions could overwhelm a webhook consumer. I need per-endpoint rate limiting with backpressure.
**Circuit breaker pattern**: After N consecutive failures, stop attempting deliveries for a cooldown period. Protects both my queue workers and the failing endpoint.
**Delivery log viewer**: The records exist but aren't exposed in the admin UI. A panel showing delivery history with filtering and manual retry would
improve the experience.
**Signature verification SDK**: I sign requests, but I could provide verification helpers in common languages to reduce integration friction.
# Security Checklist
For anyone building a similar system:
* \[ \] SSRF protection with DNS resolution validation
* \[ \] HTTPS enforcement in production
* \[ \] Cryptographically secure secret generation (32+ bytes)
* \[ \] HMAC signatures with constant-time comparison
* \[ \] Timestamp validation for replay prevention (5-minute window)
* \[ \] Request timeout to prevent hanging (30 seconds)
* \[ \] No sensitive data in error messages or logs
* \[ \] Complete audit logging for debugging and compliance
* \[ \] Input validation on all user-provided configuration
* \[ \] Automatic endpoint disabling on 410 Gone
# Conclusion
Webhooks seem simple until you think about security, reliability, and maintainability. The naive "POST JSON to URL" approach fails in production.
My key decisions:
1. **Standard Webhooks specification** for interoperability and security
2. **Multi-layer SSRF protection** including DNS resolution validation
3. **Exponential backoff** following industry-standard timing
4. **Registry pattern** for painless extensibility
5. **Type-safe DTOs** for maintainability
6. **Complete delivery tracking** for debugging and compliance
The foundation handles not just webhooks, but any integration type I'll add. Same event system, same job dispatcher, same retry logic, same audit trail—just implement the interface.
Build for production from day one. Your future self will thank you.
https://redd.it/1pkr2ie
@r_php
**Signature verification SDK**: I sign requests, but I could provide verification helpers in common languages to reduce integration friction.
# Security Checklist
For anyone building a similar system:
* \[ \] SSRF protection with DNS resolution validation
* \[ \] HTTPS enforcement in production
* \[ \] Cryptographically secure secret generation (32+ bytes)
* \[ \] HMAC signatures with constant-time comparison
* \[ \] Timestamp validation for replay prevention (5-minute window)
* \[ \] Request timeout to prevent hanging (30 seconds)
* \[ \] No sensitive data in error messages or logs
* \[ \] Complete audit logging for debugging and compliance
* \[ \] Input validation on all user-provided configuration
* \[ \] Automatic endpoint disabling on 410 Gone
# Conclusion
Webhooks seem simple until you think about security, reliability, and maintainability. The naive "POST JSON to URL" approach fails in production.
My key decisions:
1. **Standard Webhooks specification** for interoperability and security
2. **Multi-layer SSRF protection** including DNS resolution validation
3. **Exponential backoff** following industry-standard timing
4. **Registry pattern** for painless extensibility
5. **Type-safe DTOs** for maintainability
6. **Complete delivery tracking** for debugging and compliance
The foundation handles not just webhooks, but any integration type I'll add. Same event system, same job dispatcher, same retry logic, same audit trail—just implement the interface.
Build for production from day one. Your future self will thank you.
https://redd.it/1pkr2ie
@r_php
Reddit
From the PHP community on Reddit: Building a Production-Ready Webhook System for Laravel
Explore this post and more from the PHP community
Bumping Slim framework from 2 to 3
In case you are stuck at slim 2 and want to move to slim 3, maybe it could be helpful for you.
I just wrote an article how you could do to move to slim 3, you can check out here
I hope it could help you with some ideas how to move forward.
https://redd.it/1pkwdr8
@r_php
In case you are stuck at slim 2 and want to move to slim 3, maybe it could be helpful for you.
I just wrote an article how you could do to move to slim 3, you can check out here
I hope it could help you with some ideas how to move forward.
https://redd.it/1pkwdr8
@r_php
blog.erison.work
Upgrade slim framework from 2 to 3 | erison.work
How migrate from slim 2 to 3
Two methods vs one to fetch data
What is your preferred way of handling situations like when you need to fetch multiple records from the database using Doctrine DBAL and already have a method to fetch one. Do you make the one method hybrid,
function getbyID(int|array id) {
#if int add this param
#if array add array param IN()
Less code but mixed.
Or do you make it separated? More code but clearer methods?
function getById(int myid) {...}
function getByIds(array idlist) {...}
Which one you use and why? Following best practices and of course, having compromises sometimes.
https://redd.it/1plpys5
@r_php
What is your preferred way of handling situations like when you need to fetch multiple records from the database using Doctrine DBAL and already have a method to fetch one. Do you make the one method hybrid,
function getbyID(int|array id) {
#if int add this param
#if array add array param IN()
Less code but mixed.
Or do you make it separated? More code but clearer methods?
function getById(int myid) {...}
function getByIds(array idlist) {...}
Which one you use and why? Following best practices and of course, having compromises sometimes.
https://redd.it/1plpys5
@r_php
Reddit
From the symfony community on Reddit
Explore this post and more from the symfony community
What's Coming for PHP in 2026: A Developer's Guide
https://slicker.me/php/php-2026.html
https://redd.it/1plyuiv
@r_php
https://slicker.me/php/php-2026.html
https://redd.it/1plyuiv
@r_php
slicker.me
What's Coming for PHP in 2026: A Developer's Guide
Message Brokers in PHP: From Hundreds of Lines to Just a Few
https://blog.ecotone.tech/message-brokers-in-php-few-lines-integration/
https://redd.it/1pmaci2
@r_php
https://blog.ecotone.tech/message-brokers-in-php-few-lines-integration/
https://redd.it/1pmaci2
@r_php
Ecotone Framework - Resilient and scalable PHP Systems
Message Brokers in PHP: From Hundreds of Lines to Just a Few
Message broker integration in PHP can be wrestling with exchange declarations, queue bindings, consumer configurations, and endless boilerplate. Setting up RabbitMQ, Kafka, or SQS often takes more code than the actual business logic we're trying to run asynchronously.…
Type-safe data flow: Laravel to React with Inertia 2.0
https://laravelmagazine.com/type-safe-data-flow-laravel-to-react-with-inertia-20
https://redd.it/1pmb7mc
@r_php
https://laravelmagazine.com/type-safe-data-flow-laravel-to-react-with-inertia-20
https://redd.it/1pmb7mc
@r_php
Type-safe data flow: Laravel to React with Inertia 2.0
Stop me if you've heard this before: you change a property name in your Laravel model, push to production, and suddenly your React components are trying...
A Week of Symfony #989 (December 8–14, 2025)
https://symfony.com/blog/a-week-of-symfony-989-december-8-14-2025?utm_medium=feed&utm_source=Symfony%20Blog%20Feed
https://redd.it/1pmafef
@r_php
https://symfony.com/blog/a-week-of-symfony-989-december-8-14-2025?utm_medium=feed&utm_source=Symfony%20Blog%20Feed
https://redd.it/1pmafef
@r_php
Symfony
A Week of Symfony #989 (December 8–14, 2025) (Symfony Blog)
This week, Symfony released the maintenance versions 6.4.30 and 7.3.8, as well as the first patch releases of the 7.4 and 8.0 branches: 7.4.1, 8.0.1, 7.4.2, and 8.0.2. In addition, we published a free…
Message Brokers in PHP: From Hundreds of Lines to Just a Few
https://blog.ecotone.tech/message-brokers-in-php-few-lines-integration/
https://redd.it/1pmacr0
@r_php
https://blog.ecotone.tech/message-brokers-in-php-few-lines-integration/
https://redd.it/1pmacr0
@r_php
Ecotone Framework - Resilient and scalable PHP Systems
Message Brokers in PHP: From Hundreds of Lines to Just a Few
Message broker integration in PHP can be wrestling with exchange declarations, queue bindings, consumer configurations, and endless boilerplate. Setting up RabbitMQ, Kafka, or SQS often takes more code than the actual business logic we're trying to run asynchronously.…
Type-safe data flow: Laravel to React with Inertia 2.0
https://laravelmagazine.com/type-safe-data-flow-laravel-to-react-with-inertia-20
https://redd.it/1pmb825
@r_php
https://laravelmagazine.com/type-safe-data-flow-laravel-to-react-with-inertia-20
https://redd.it/1pmb825
@r_php
Type-safe data flow: Laravel to React with Inertia 2.0
Stop me if you've heard this before: you change a property name in your Laravel model, push to production, and suddenly your React components are trying...
Weekly /r/Laravel Help Thread
Ask your Laravel help questions here. To improve your chances of getting an answer from the community, here are some tips:
What steps have you taken so far?
What have you tried from the documentation?
Did you provide any error messages you are getting?
Are you able to provide instructions to replicate the issue?
Did you provide a code example?
Please don't post a screenshot of your code. Use the code block in the Reddit text editor and ensure it's formatted correctly.
For more immediate support, you can ask in the official Laravel Discord.
Thanks and welcome to the r/Laravel community!
https://redd.it/1pmkh1s
@r_php
Ask your Laravel help questions here. To improve your chances of getting an answer from the community, here are some tips:
What steps have you taken so far?
What have you tried from the documentation?
Did you provide any error messages you are getting?
Are you able to provide instructions to replicate the issue?
Did you provide a code example?
Please don't post a screenshot of your code. Use the code block in the Reddit text editor and ensure it's formatted correctly.
For more immediate support, you can ask in the official Laravel Discord.
Thanks and welcome to the r/Laravel community!
https://redd.it/1pmkh1s
@r_php
Laravel
Installation - Laravel 12.x - The PHP Framework For Web Artisans
Laravel is a PHP web application framework with expressive, elegant syntax. We’ve already laid the foundation — freeing you to create without sweating the small things.
What's your go-to approach for structuring large Laravel projects?
Hey fellow Laravel devs! 👋
I’ve been working on some fairly large projects lately and I keep running into the same challenge:
“How do I structure my Laravel apps so that they stay maintainable as they grow?”
Some things I’ve experimented with:
- Modular folder structure for features
- Service Providers for reusable logic
- Domain-driven design patterns in Laravel
I’d love to hear from you:
- How do you organize large Laravel projects?
- Any tricks or best practices for keeping code clean and scalable?
- Packages or tools you swear by for project organization?
Sharing some real-life examples would be amazing!
Let’s make it easier for the community to handle big Laravel apps.
Thanks in advance for your insights! 🙌
https://redd.it/1pmnfxh
@r_php
Hey fellow Laravel devs! 👋
I’ve been working on some fairly large projects lately and I keep running into the same challenge:
“How do I structure my Laravel apps so that they stay maintainable as they grow?”
Some things I’ve experimented with:
- Modular folder structure for features
- Service Providers for reusable logic
- Domain-driven design patterns in Laravel
I’d love to hear from you:
- How do you organize large Laravel projects?
- Any tricks or best practices for keeping code clean and scalable?
- Packages or tools you swear by for project organization?
Sharing some real-life examples would be amazing!
Let’s make it easier for the community to handle big Laravel apps.
Thanks in advance for your insights! 🙌
https://redd.it/1pmnfxh
@r_php
Reddit
From the laravel community on Reddit
Explore this post and more from the laravel community